.
diff --git a/README.html b/README.html
new file mode 100644
index 0000000..48a88d4
--- /dev/null
+++ b/README.html
@@ -0,0 +1,1875 @@
+UxPlay
+1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix
+(now also runs on Windows).
+Now
+developed at the GitHub site https://github.com/FDH2/UxPlay (where ALL user issues
+should be posted, and latest versions can be found).
+
+- NEW in v1.71: Support for (YouTube) HLS (HTTP
+Live Streaming) video with the new “-hls” option. Click on the
+airplay icon in the YouTube app to stream video. (You may need to wait
+until advertisements have finished or been skipped before clicking the
+YouTube airplay icon.) Please report any issues with this new
+feature of UxPlay.
+
+Highlights:
+
+- GPLv3, open source.
+- Originally supported only AirPlay Mirror protocol, now has added
+support for AirPlay Audio-only (Apple Lossless ALAC) streaming from
+current iOS/iPadOS clients. Now with support for Airplay HLS
+video-streaming (currently only YouTube video).
+- macOS computers (2011 or later, both Intel and “Apple Silicon” M1/M2
+systems) can act either as AirPlay clients, or as the server running
+UxPlay. Using AirPlay, UxPlay can emulate a second display for macOS
+clients.
+- Support for older iOS clients (such as 32-bit iPad 2nd gen., iPod
+Touch 5th gen. and iPhone 4S, when upgraded to iOS 9.3.5, or later
+64-bit devices), plus a Windows AirPlay-client emulator, AirMyPC.
+- Uses GStreamer plugins for audio and video rendering (with options
+to select different hardware-appropriate output “videosinks” and
+“audiosinks”, and a fully-user-configurable video streaming
+pipeline).
+- Support for server behind a firewall.
+- Raspberry Pi support both with and without hardware video
+decoding by the Broadcom GPU. Tested on Raspberry Pi Zero 2
+W, 3 Model B+, 4 Model B, and 5.
+- Support for running on Microsoft Windows (builds with the MinGW-64
+compiler in the unix-like MSYS2 environment).
+
+Note: AirPlay2 multi-room audio streaming is not supported: use shairport-sync
+for that.
+Packaging status
+(Linux and *BSD distributions)
+
.
+
+Install uxplay on Debian-based Linux systems with
+“sudo apt install uxplay
”; on FreeBSD with
+“sudo pkg install uxplay
”. Also available on Arch-based
+systems through AUR. Since v. 1.66, uxplay is now also packaged in RPM
+format by Fedora 38 (“sudo dnf install uxplay
”).
+For other RPM-based distributions which have not yet packaged
+UxPlay, a RPM “specfile” uxplay.spec is now provided
+with recent releases (see their
+“Assets”), and can also be found in the UxPlay source top directory. See
+the section on using this specfile for building an installable RPM
+package.
+
+After installation:
+
+(On Linux and *BSD): if a firewall is active on the server
+hosting UxPlay, make sure the default network port (UDP 5353) for
+mDNS/DNS-SD queries is open (see Troubleshooting below for more details);
+also open three UDP and three TCP ports for Uxplay, and use the “uxplay
+-p ” option (see “man uxplay
” or
+“uxplay -h
”).
+Even if you install your distribution’s pre-compiled uxplay
+binary package, you may need to read the instructions below for running UxPlay to see which of your
+distribution’s GStreamer plugin packages you should
+also install.
+For Audio-only mode (Apple Music, etc.) best quality is obtained
+with the option “uxplay -async”, but there is then a 2 second latency
+imposed by iOS.
+Add any UxPlay options you want to use as defaults to a startup
+file ~/.uxplayrc
(see “man uxplay
” or
+“uxplay -h
” for format and other possible locations). In
+particular, if your system uses PipeWire audio or Wayland video systems,
+you may wish to add “as pipewiresink” or “vs waylandsink” as defaults to
+the file. (Output from terminal commands “ps waux | grep pulse” or
+“pactl info” will contain “pipewire” if your Linux/BSD system uses
+it).
+On Raspberry Pi: models using hardware h264 video decoding by the
+Broadcom GPU (models 4B and earlier) may require the uxplay option
+-bt709. If you use Ubuntu 22.10 or earlier, GStreamer must be patched
+to use hardware video decoding by the Broadcom GPU (also recommended but
+optional for Raspberry Pi OS (Bullseye): the patched GStreamer does not
+need option ” -bt709`“. The need for -bt709 when hardware video decoding
+is used seems to have reappeared starting with GStreamer-1.22.
+
+To (easily) compile the latest UxPlay from source, see the section Getting UxPlay.
+Detailed description of
+UxPlay
+This project is a GPLv3 open source unix AirPlay2 Mirror server for
+Linux, macOS, and *BSD. It was initially developed by antimof using code from
+OpenMAX-based RPiPlay,
+which in turn derives from AirplayServer, shairplay, and playfair. (The
+antimof site is no longer involved in development, but periodically
+posts updates pulled from the new main UxPlay site).
+UxPlay is tested on a number of systems, including (among others)
+Debian (10 “Buster”, 11 “Bullseye”, 12 “Bookworm”), Ubuntu (20.04 LTS,
+22.04 LTS, 23.04 (also Ubuntu derivatives Linux Mint, Pop!_OS), Red Hat
+and clones (Fedora 38, Rocky Linux 9.2), openSUSE Leap 15.5, Mageia 9,
+OpenMandriva “ROME”, PCLinuxOS, Arch Linux, Manjaro, and should run on
+any Linux system. Also tested on macOS Catalina and Ventura (Intel) and
+Sonoma (M2), FreeBSD 14.0, Windows 10 and 11 (64 bit).
+On Raspberry Pi 4 model B, it is tested on Raspberry Pi OS (Bullseye
+and Bookworm) (32- and 64-bit), Ubuntu 22.04 LTS and 23.04, Manjaro RPi4
+23.02, and (without hardware video decoding) on openSUSE 15.5. Also
+tested on Raspberry Pi Zero 2 W, 3 model B+, and now 5.
+Its main use is to act like an AppleTV for screen-mirroring (with
+audio) of iOS/iPadOS/macOS clients (iPhone, iPod Touch, iPad, Mac
+computers) on the server display of a host running Linux, macOS, or
+other unix (and now also Microsoft Windows). UxPlay supports Apple’s
+AirPlay2 protocol using “Legacy Protocol”, but some features are
+missing. (Details of what is publicly known about Apple’s AirPlay 2
+protocol can be found here, here
+and here; see also
+pyatv which
+could be a resource for adding modern protocols.) While there is no
+guarantee that future iOS releases will keep supporting “Legacy
+Protocol”, iOS 17 continues support.
+The UxPlay server and its client must be on the same local area
+network, on which a Bonjour/Zeroconf mDNS/DNS-SD server
+is also running (only DNS-SD “Service Discovery” service is strictly
+necessary, it is not necessary that the local network also be of the
+“.local” mDNS-based type). On Linux and BSD Unix servers, this is
+usually provided by Avahi, through
+the avahi-daemon service, and is included in most Linux distributions
+(this service can also be provided by macOS, iOS or Windows
+servers).
+Connections to the UxPlay server by iOS/MacOS clients can be
+initiated both in AirPlay Mirror mode (which streams
+lossily-compressed AAC audio while mirroring the client screen, or in
+the alternative AirPlay Audio mode which streams Apple
+Lossless (ALAC) audio without screen mirroring. In
+Audio mode, metadata is displayed in the uxplay
+terminal; if UxPlay option -ca <name>
is used, the
+accompanying cover art is also output to a periodically-updated file
+<name>
, and can be viewed with a (reloading) graphics
+viewer of your choice. Switching between
+Mirror and Audio modes
+during an active connection is possible: in Mirror
+mode, stop mirroring (or close the mirror window) and start an
+Audio mode connection, switch back by initiating
+a Mirror mode connection; cover-art display
+stops/restarts as you leave/re-enter Audio
+mode.
+
+Note that Apple video-DRM (as found in “Apple TV app”
+content on the client) cannot be decrypted by UxPlay, and the Apple TV
+app cannot be watched using UxPlay’s AirPlay Mirror mode (only the
+unprotected audio will be streamed, in AAC format).
+With the new “-hls” option, UxPlay now also supports
+non-Mirror AirPlay video streaming (where the client controls a web
+server on the AirPlay server that directly receives HLS content to avoid
+it being decoded and re-encoded by the client). This currently only
+supports streaming of YouTube videos. Without the -hls option, using the
+icon for AirPlay video in apps such as the YouTube app will only send
+audio (in lossless ALAC format) without the accompanying
+video.
+
+Possibility
+for using hardware-accelerated h264/h265 video-decoding, if
+available.
+UxPlay uses GStreamer
+“plugins” for rendering audio and video. This means that video and audio
+are supported “out of the box”, using a choice of plugins. AirPlay
+streams video in h264 format: gstreamer decoding is plugin agnostic, and
+uses accelerated GPU hardware h264 decoders if available; if not,
+software decoding is used.
+
+VAAPI for Intel and AMD integrated graphics, NVIDIA with
+“Nouveau” open-source driver
+With an Intel or AMD GPU, hardware decoding with the open-source
+VAAPI gstreamer plugin is preferable. The open-source “Nouveau” drivers
+for NVIDIA graphics are also in principle supported: see here,
+but this requires VAAPI to be supplemented with firmware extracted from
+the proprietary NVIDIA drivers.
+NVIDIA with proprietary drivers
+The nvh264dec
plugin (included in
+gstreamer1.0-plugins-bad since GStreamer-1.18.0) can be used for
+accelerated video decoding on the NVIDIA GPU after NVIDIA’s CUDA driver
+libcuda.so
is installed. For GStreamer-1.16.3 or earlier,
+the plugin is called nvdec
, and must be built
+by the user.
+Video4Linux2 support for h264 hardware decoding on
+Raspberry Pi (Pi 4B and older)
+Raspberry Pi (RPi) computers (tested on Pi 4 Model B) can now run
+UxPlay using software video decoding, but hardware-accelerated h264/h265
+decoding by firmware in the Pi’s Broadcom 2835 GPU is prefered. UxPlay
+accesses this using the GStreamer-1.22 Video4Linux2 (v4l2) plugin; Uses
+the out-of-mainline Linux kernel module bcm2835-codec maintained by
+Raspberry Pi, so far only included in Raspberry Pi OS, and two other
+distributions (Ubuntu, Manjaro) available with Raspberry Pi Imager.
+(For GStreamer < 1.22, see the UxPlay
+Wiki). Pi model 5 has no support for hardware H264 decoding, as
+its CPU is powerful enough for satisfactory software H264
+decoding
+Support for h265 (HEVC) hardware decoding on Raspberry Pi
+(Pi 4 model B and Pi 5)
+These Raspberry Pi models have a dedicated HEVC decoding block (not
+the GPU), with a driver “rpivid” which is not yet in the mainline Linux
+kernel (but is planned to be there in future). Unfortunately it produces
+decoded video in a non-standard pixel format (NC30 or “SAND”) which will
+not be supported by GStreamer until the driver is in the mainline
+kernel; without this support, UxPlay support for HEVC hardware decoding
+on Raspberry Pi will not work.
+
+Note to packagers:
+UxPlay’s GPLv3 license does not have an added “GPL exception”
+explicitly allowing it to be distributed in compiled form when linked to
+OpenSSL versions prior to v. 3.0.0 (older versions of
+OpenSSL have a license clause incompatible with the GPL unless OpenSSL
+can be regarded as a “System Library”, which it is in *BSD). Many Linux
+distributions treat OpenSSL as a “System Library”, but some
+(e.g. Debian) do not: in this case, the issue is solved by linking with
+OpenSSL-3.0.0 or later.
+Getting UxPlay
+Either download and unzip UxPlay-master.zip,
+or (if git is installed): “git clone https://github.com/FDH2/UxPlay”.
+You can also download a recent or earlier version listed in Releases.
+
+- A recent UxPlay can also be found on the original antimof site; that original
+project is inactive, but is usually kept current or almost-current with
+the active UxPlay github
+site (thank you antimof!).
+
+Building UxPlay on Linux (or
+*BSD):
+Debian-based systems:
+(Adapt these instructions for non-Debian-based Linuxes or *BSD; for
+macOS, see specific instruction below). See Troubleshooting below for help with any
+difficulties.
+You need a C/C++ compiler (e.g. g++) with the standard development
+libraries installed. Debian-based systems provide a package
+“build-essential” for use in compiling software. You also need
+pkg-config: if it is not found by “which pkg-config
”,
+install pkg-config or its work-alike replacement pkgconf. Also make sure
+that cmake>=3.10 is installed: “sudo apt install cmake
”
+(add build-essential
and pkg-config
(or
+pkgconf
) to this if needed).
+Make sure that your distribution provides OpenSSL 1.1.1 or later, and
+libplist 2.0 or later. (This means Debian 10 “Buster” based systems
+(e.g, Ubuntu 18.04) or newer; on Debian 10 systems “libplist” is an
+older version, you need “libplist3”.) If it does not, you may need to
+build and install these from source (see instructions at the end of this
+README).
+If you have a non-standard OpenSSL installation, you may need to set
+the environment variable OPENSSL_ROOT_DIR (e.g. ,
+“export OPENSSL_ROOT_DIR=/usr/local/lib64
” if that is where
+it is installed). Similarly, for non-standard (or multiple) GStreamer
+installations, set the environment variable GSTREAMER_ROOT_DIR to the
+directory that contains the “…/gstreamer-1.0/” directory of the
+gstreamer installation that UxPlay should use (if this is e.g.
+“~/my_gstreamer/lib/gstreamer-1.0/”, set this location with
+“export GSTREAMER_ROOT_DIR=$HOME/my_gstreamer/lib
”).
+
+- Most users will use the GStreamer supplied by their distribution,
+but a few (in particular users of Raspberry Pi OS Lite Legacy (Buster)
+on a Raspberry Pi model 4B who wish to stay on that unsupported Legacy
+OS for compatibility with other apps) should instead build a newer
+Gstreamer from source following these
+instructions . Do this before building
+UxPlay.
+
+In a terminal window, change directories to the source directory of
+the downloaded source code (“UxPlay-*”, “*” = “master” or the release
+tag for zipfile downloads, “UxPlay” for “git clone” downloads), then
+follow the instructions below:
+Note: By default UxPlay will be built with
+optimization for the computer it is built on; when this is not the case,
+as when you are packaging for a distribution, use the cmake option
+-DNO_MARCH_NATIVE=ON
.
+If you use X11 Windows on Linux or *BSD, and wish to toggle in/out of
+fullscreen mode with a keypress (F11 or Alt_L+Enter) UxPlay needs to be
+built with a dependence on X11. Starting with UxPlay-1.59, this will be
+done by default IF the X11 development libraries are
+installed and detected. Install these with
+“sudo apt install libx11-dev
”. If GStreamer < 1.20 is
+detected, a fix needed by screen-sharing apps (e.g., Zoom) will
+also be made.
+
+- If X11 development libraries are present, but you wish to build
+UxPlay without any X11 dependence, use the cmake option
+
-DNO_X11_DEPS=ON
.
+
+
+sudo apt install libssl-dev libplist-dev
“. (unless
+you need to build OpenSSL and libplist from source).
+sudo apt install libavahi-compat-libdnssd-dev
+sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
.
+(*Skip if you built Gstreamer from source)
+cmake .
(For a cleaner build, which is useful if
+you modify the source, replace this by
+“mkdir build; cd build; cmake ..
”: you can then delete
+the contents of the build
directory if needed, without
+affecting the source.) Also add any cmake “-D
” options
+here as needed (e.g, -DNO_X11_DEPS=ON
or
+-DNO_MARCH_NATIVE=ON
).
+make
+sudo make install
(you can afterwards uninstall with
+sudo make uninstall
in the same directory in which this was
+run).
+
+This installs the executable file “uxplay
” to
+/usr/local/bin
, (and installs a manpage to somewhere
+standard like /usr/local/share/man/man1
and README files to
+somewhere like /usr/local/share/doc/uxplay
). (If “man
+uxplay” fails, check if $MANPATH is set: if so, the path to the manpage
+(usually /usr/local/share/man/) needs to be added to $MANPATH .) The
+uxplay executable can also be found in the build directory after the
+build process, if you wish to test before installing (in which case the
+GStreamer plugins must first be installed).
+Building on non-Debian
+Linux and *BSD
+**For those with RPM-based distributions, a RPM spec file uxplay.spec
+is also available: see Building an installable rpm
+package.
+
+Red Hat, or clones like CentOS (now continued as Rocky
+Linux or Alma Linux): (sudo dnf install, or sudo yum install)
+openssl-devel libplist-devel avahi-compat-libdns_sd-devel
+gstreamer1-devel gstreamer1-plugins-base-devel (+libX11-devel for
+fullscreen X11) (some of these may be in the “CodeReady” add-on
+repository, called “PowerTools” by clones)
+Mageia, PCLinuxOS, OpenMandriva: Same as Red
+Hat, except for name changes: (Mageia) “gstreamer1.0-devel”,
+“gstreamer-plugins-base1.0-devel”; (OpenMandriva) “libopenssl-devel”,
+“gstreamer-devel”, “libgst-plugins-base1.0-devel”. PCLinuxOS: same as
+Mageia, but uses synaptic (or apt) as its package manager.
+openSUSE: (sudo zypper install)
+libopenssl-3-devel (formerly libopenssl-devel) libplist-2_0-devel
+(formerly libplist-devel) avahi-compat-mDNSResponder-devel
+gstreamer-devel gstreamer-plugins-base-devel (+ libX11-devel for
+fullscreen X11).
+Arch Linux (Also available as a package in
+AUR): (sudo pacman -Syu) openssl libplist avahi
+gst-plugins-base.
+FreeBSD: (sudo pkg install) libplist gstreamer1.
+Either avahi-libdns or mDNSResponder must also be installed to provide
+the dns_sd library. OpenSSL is already installed as a System
+Library.
+
+Building an installable RPM
+package
+First-time RPM builders should first install the rpm-build and
+rpmdevtools packages, then create the rpmbuild tree with
+“rpmdev-setuptree
”. Then download and copy uxplay.spec into
+~/rpmbuild/SPECS
. In that directory, run
+“rpmdev-spectool -g -R uxplay.spec
” to download the
+corresponding source file uxplay-*.tar.gz
into
+~/rpmbuild/SOURCES
(“rpmdev-spectool” may also be just
+called “spectool”); then run “rpmbuild -ba uxplay.spec
”
+(you will need to install any required dependencies this reports). This
+should create the uxplay RPM package in a subdirectory of
+~/rpmbuild/RPMS
. (uxplay.spec is tested on
+Fedora 38, Rocky Linux 9.2, openSUSE Leap 15.5, Mageia 9, OpenMandriva,
+PCLinuxOS; it can be easily modified to include dependency lists for
+other RPM-based distributions.)
+Running UxPlay
+Installing
+plugins (Debian-based Linux distributions, including Ubuntu and
+Raspberry Pi OS) (skip if you built a complete GStreamer from
+source)
+Next install the GStreamer plugins that are needed with
+sudo apt install gstreamer1.0-<plugin>
. Values of
+<plugin>
required are:
+
+- “plugins-base”
+- “libav” (for sound),
+- “plugins-good” (for v4l2 hardware h264
+decoding)
+- “plugins-bad” (for h264 decoding).
+
+Debian-based distributions split some of the plugin packages
+into smaller pieces: some that may also be needed include
+“gl” for OpenGL support (this provides the “-vs
+glimagesink” videosink, which can be very useful in many systems
+(including Raspberry Pi), and should always be used when using h264/h265
+decoding by a NVIDIA GPU), “gtk3” (which provides the
+“-vs gtksink” videosink), and “x” for X11 support,
+although these may already be installed; “vaapi” is
+needed for hardware-accelerated h264 video decoding by Intel or AMD
+graphics (but not for use with NVIDIA using proprietary drivers). If
+sound is not working,
+“alsa”“,”pulseaudio”, or
+“pipewire” plugins may need to be installed, depending
+on how your audio is set up.
+
+- Also install “gstreamer1.0-tools” to get the
+utility gst-inspect-1.0 for examining the GStreamer installation.
+
+Installing
+plugins (Non-Debian-based Linux or *BSD) (skip if you built a
+complete GStreamer from source)
+In some cases, because of patent issues, the libav plugin feature
+avdec_aac needed for decoding AAC audio in mirror mode
+is not provided in the official distribution: get it from community
+repositories for those distributions.
+
+Red Hat, or clones like CentOS (now continued as Rocky
+Linux or Alma Linux): Install gstreamer1-libav
+gstreamer1-plugins-bad-free (+ gstreamer1-vaapi for Intel/AMD graphics).
+In recent Fedora, gstreamer1-libav is renamed gstreamer1-plugin-libav.
+To get avdec_aac, install packages from rpmfusion.org: (get
+ffmpeg-libs from rpmfusion; on RHEL or clones, but not recent Fedora,
+also get gstreamer1-libav from there).
+Mageia, PCLinuxOS, OpenMandriva: Install
+gstreamer1.0-libav gstreamer1.0-plugins-bad (+ gstreamer1.0-vaapi for
+Intel/AMD graphics). On Mageia, to get avdec_aac, install ffmpeg
+from the “tainted” repository, (which also provides a more
+complete gstreamer1.0-plugins-bad).
+openSUSE: Install gstreamer-plugins-libav
+gstreamer-plugins-bad (+ gstreamer-plugins-vaapi for Intel/AMD
+graphics). To get avdec_aac, install libav* packages for
+openSUSE from Packman
+“Essentials”; recommendation: after adding the Packman
+repository, use the option in YaST Software management to switch all
+system packages for multimedia to Packman).
+Arch Linux Install gst-plugins-good
+gst-plugins-bad gst-libav (+ gstreamer-vaapi for Intel/AMD
+graphics).
+FreeBSD: Install gstreamer1-libav,
+gstreamer1-plugins, gstreamer1-plugins-* (* = core, good, bad, x, gtk,
+gl, vulkan, pulse, v4l2, …), (+ gstreamer1-vaapi for Intel/AMD
+graphics).
+
+Starting and running UxPlay
+Since UxPlay-1.64, UxPlay can be started with options read from a
+configuration file, which will be the first found of (1) a file with a
+path given by environment variable $UXPLAYRC
, (2)
+~/.uxplayrc
in the user’s home directory (“~”), (3)
+~/.config/uxplayrc
. The format is one option per line,
+omitting the initial "-"
of the command-line option. Lines
+in the configuration file beginning with "#"
are treated as
+comments and ignored.
+Run uxplay in a terminal window. On some systems,
+you can specify fullscreen mode with the -fs
option, or
+toggle into and out of fullscreen mode with F11 or (held-down left
+Alt)+Enter keys. Use Ctrl-C (or close the window) to terminate it when
+done. If the UxPlay server is not seen by the iOS client’s drop-down
+“Screen Mirroring” panel, check that your DNS-SD server (usually
+avahi-daemon) is running: do this in a terminal window with
+systemctl status avahi-daemon
. If this shows the
+avahi-daemon is not running, control it with
+sudo systemctl [start,stop,enable,disable] avahi-daemon
(on
+non-systemd systems, such as *BSD, use
+sudo service avahi-daemon [status, start, stop, restart, ...]
).
+If UxPlay is seen, but the client fails to connect when it is selected,
+there may be a firewall on the server that prevents UxPlay from
+receiving client connection requests unless some network ports are
+opened: if a firewall is active, also open UDP port 5353 (for
+mDNS queries) needed by Avahi. See Troubleshooting below for help with this or
+other problems.
+
+Unlike an Apple TV, the UxPlay server does not by default require
+clients to initially “pair” with it using a pin code displayed by the
+server (after which the client “trusts” the server, and does not need to
+repeat this). Since v1.67, Uxplay offers such “pin-authentication” as an
+option: see “-pin
” and “-reg
” in Usage for details, if you wish to use it. Some
+clients with MDM (Mobile Device Management, often present on
+employer-owned devices) are required to use pin-authentication: UxPlay
+will provide this even when running without the pin
+option.
+By default, UxPlay is locked to its current client until that
+client drops the connection; since UxPlay-1.58, the option
+-nohold
modifies this behavior so that when a new client
+requests a connection, it removes the current client and takes over.
+UxPlay 1.66 introduces a mechanism ( -restrict
,
+-allow <id>
, -block <id>
) to
+control which clients are allowed to connect, using their “deviceID”
+(which in Apple devices appears to be immutable).
+In Mirror mode, GStreamer has a choice of two
+methods to play video with its accompanying audio: prior to UxPlay-1.64,
+the video and audio streams were both played as soon as possible after
+they arrived (the GStreamer “sync=false” method), with a
+GStreamer internal clock used to try to keep them synchronized.
+Starting with UxPlay-1.64, the other method (GStreamer’s
+“sync=true” mode), which uses timestamps in the audio and video
+streams sent by the client, is the new default. On
+low-decoding-power UxPlay hosts (such as Raspberry Pi Zero W or 3 B+
+models) this will drop video frames that cannot be decoded in time to
+play with the audio, making the video jerky, but still
+synchronized.
+
+The older method which does not drop late video frames worked well on
+more powerful systems, and is still available with the UxPlay option
+“-vsync no
”; this method is adapted to “live streaming”,
+and may be better when using UxPlay as a second monitor for a Mac
+computer, for example, while the new default timestamp-based method is
+best for watching a video, to keep lip movements and voices
+synchronized. (Without use of timestamps, video will eventually lag
+behind audio if it cannot be decoded fast enough: hardware-accelerated
+video-decoding helped to prevent this previously when timestamps were
+not being used.)
+
+- In Audio-only mode the GStreamer “sync=false” mode (not using
+timestamps) is still the default, but if you want to keep the audio
+playing on the server synchronized with the video showing on the client,
+use the
-async
timestamp-based option. (An example might be
+if you want to follow the Apple Music lyrics on the client while
+listening to superior sound on the UxPlay server). This delays the video
+on the client to match audio on the server, so leads to a slight delay
+before a pause or track-change initiated on the client takes effect on
+the audio played by the server.
+
+AirPlay volume-control attenuates volume (gain) by up to -30dB: the
+decibel range -30:0 can be rescaled from Low:0, or
+Low:High, using the option -db
(“-db
+Low” or “-db Low:High”), Low must be
+negative. Rescaling is linear in decibels. Note that GStreamer’s audio
+format will “clip” any audio gain above +20db, so keep High
+below that level. The option -taper
provides a “tapered”
+AirPlay volume-control profile some users may prefer.
+The -vsync and -async options also allow an optional positive (or
+negative) audio-delay adjustment in milliseconds for
+fine-tuning : -vsync 20.5
delays audio relative to video by
+0.0205 secs; a negative value advances it.)
+
+you may find video is improved by the setting -fps 60 that allows
+some video to be played at 60 frames per second. (You can see what
+framerate is actually streaming by using -vs fpsdisplaysink, and/or
+-FPSdata.) When using this, you should use the default timestamp-based
+synchronization option -vsync
.
+Since UxPlay-1.54, you can display the accompanying “Cover Art”
+from sources like Apple Music in Audio-Only (ALAC) mode: run
+“uxplay -ca <name> &
” in the background, then run
+a image viewer with an autoreload feature: an example is “feh”: run
+“feh -R 1 <name>
” in the foreground; terminate feh
+and then Uxplay with “ctrl-C fg ctrl-C
”.
+
+By default, GStreamer uses an algorithm to search for the best
+“videosink” (GStreamer’s term for a graphics driver to display images)
+to use. You can overide this with the uxplay option
+-vs <videosink>
. Which videosinks are available
+depends on your operating system and graphics hardware: use
+“gst-inspect-1.0 | grep sink | grep -e video -e Video -e image
”
+to see what is available. Some possibilites on Linux/*BSD are:
+
+glimagesink (OpenGL),
+waylandsink
+xvimagesink, ximagesink
+(X11)
+kmssink, fbdevsink (console
+graphics without X11)
+vaapisink (for Intel/AMD hardware-accelerated
+graphics); for NVIDIA hardware graphics (with CUDA) use
+glimagesink combined with “-vd nvh264dec
”
+(or “nvh264sldec”, a new variant which will become “nvh264dec” in
+GStreamer-1.24).
+If the server is “headless” (no attached monitor, renders audio
+only) use -vs 0
.
+
+Note that videosink options can set using quoted arguments to -vs:
+e.g., -vs "xvimagesink display=:0"
: ximagesink and
+xvimagesink allow an X11 display name to be specified, and waylandsink
+has a similar option. Videosink options (“properties”) can be found in
+their GStreamer description pages,such as
+https://gstreamer.freedesktop.org/documentation/xvimagesink .
+GStreamer also searches for the best “audiosink”; override its choice
+with -as <audiosink>
. Choices on Linux include
+pulsesink, alsasink, pipewiresink, oss4sink; see what is available with
+gst-inspect-1.0 | grep sink | grep -e audio -e Audio
.
+One common problem involves GStreamer attempting to use
+incorrectly-configured or absent accelerated hardware h264 video
+decoding (e.g., VAAPI). Try “uxplay -avdec
” to force
+software video decoding; if this works you can then try to fix
+accelerated hardware video decoding if you need it, or just uninstall
+the GStreamer vaapi plugin.
+See Usage for more run-time options.
+Special
+instructions for Raspberry Pi (tested on Raspberry Pi Zero 2 W, 3 Model
+B+, 4 Model B, and 5 only):
+
+For Framebuffer video (for Raspberry Pi OS “Lite” and other
+non-X11 distributions) use the KMS videosink “-vs kmssink” (the DirectFB
+framebuffer videosink “dfbvideosink” is broken on the Pi, and
+segfaults). In this case you should explicitly use the “-vs kmssink”
+option, as without it, autovideosink does not find the correct
+videosink.
+Raspberry Pi 5 does not provide hardware H264 decoding (and does
+not need it).
+Pi Zero 2 W, 3 Model B+ and 4 Model B should use hardware H264
+decoding by the Broadcom GPU, but it requires an out-of-mainstream
+kernel module bcm2835_codec maintained in the Raspberry Pi kernel
+tree; distributions that are known to supply it include Raspberry Pi
+OS, Ubuntu, and Manjaro-RPi4. Use software decoding (option -avdec) if
+this module is not available.
+Uxplay uses the Video4Linux2 (v4l2) plugin from GStreamer-1.22
+and later to access the GPU, if hardware H264 decoding is used. This
+should happen automatically. The option -v4l2 can be used, but it is
+usually best to just let GStreamer find the best video pipeline by
+itself.
+On older distributions (GStreamer < 1.22), the v4l2 plugin
+needs a patch: see the UxPlay
+Wiki. Legacy Raspberry Pi OS (Bullseye) has a partially-patched
+GStreamer-1.18.4 which needs the uxplay option -bt709 (and don’t use
+-v4l2); it is still better to apply the full patch from the UxPlay Wiki
+in this case.
+It appears that when hardware h264 video decoding is
+used, the option -bt709 became needed again in GStreamer-1.22 and
+later.
+For “double-legacy” Raspberry Pi OS (Buster), there is no patch
+for GStreamer-1.14. Instead, first build a complete newer
+GStreamer-1.18.6 from source using these
+instructions before building UxPlay.
+Raspberry Pi 3 Model B+ running a 32 bit OS can also access the
+GPU with the GStreamer OMX plugin (use option
+“-vd omxh264dec
”), but this is broken by Pi 4 Model B
+firmware. OMX support was removed from Raspberry Pi OS (Bullseye), but
+is present in Buster.
+H265 (4K) video is potentially supported by
+hardware decoding on Raspberry Pi 5 models, as well as on Raspberry Pi 4
+model B, using a dedicated HEVC decoding block, but the “rpivid” kernel
+driver for this is not yet supported by GStreamer (this driver decodes
+video into a non-standard format that cannot be supported by GStreamer
+until the driver is in the mainline Linux kernel). Raspberry Pi provides
+a version of ffmpeg that can use that format, but at present UxPlay
+cannot use this. The best solution would be for the driver to be
+“upstreamed” to the kernel, allowing GStreamer support. (Software HEVC
+decoding works, but does not seem to give satisfactory results on the
+Pi).
+
+Even with GPU video decoding, some frames may be dropped by the
+lower-power models to keep audio and video synchronized using
+timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config
+“Performance Options” allows specifying how much memory to allocate to
+the GPU, but this setting appears to be absent in Bookworm (but it can
+still be set to e.g. 128MB by adding a line “gpu_mem=128” in
+/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well
+when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to
+the GPU (default seems to be 64MB).
+The basic uxplay options for R Pi are
+uxplay [-vs <videosink>]
. The choice
+<videosink>
= glimagesink
is sometimes
+useful. With the Wayland video compositor, use
+<videosink>
= waylandsink
. With
+framebuffer video, use <videosink>
=
+kmssink
.
+
+- Tip: to start UxPlay on a remote host (such as a Raspberry Pi) using
+ssh:
+
+
+ ssh user@remote_host
+ export DISPLAY=:0
+ nohup uxplay [options] > FILE &
+Sound and video will play on the remote host; “nohup” will keep
+uxplay running if the ssh session is closed. Terminal output is saved to
+FILE (which can be /dev/null to discard it)
+Building
+UxPlay on macOS: (Intel X86_64 and “Apple Silicon” M1/M2
+Macs)
+Note: A native AirPlay Server feature is included in macOS 12
+Monterey, but is restricted to recent hardware. UxPlay can run on older
+macOS systems that will not be able to run Monterey, or can run Monterey
+but not AirPlay.
+These instructions for macOS assume that the Xcode command-line
+developer tools are installed (if Xcode is installed, open the Terminal,
+type “sudo xcode-select –install” and accept the conditions).
+It is also assumed that CMake >= 3.13 is installed: this can be
+done with package managers MacPorts
+(sudo port install cmake
), Homebrew (brew install cmake
), or
+by a download from https://cmake.org/download/. Also install
+git
if you will use it to fetch UxPlay.
+Next install libplist and openssl-3.x. Note that static versions of
+these libraries will be used in the macOS builds, so they can be
+uninstalled after building uxplay, if you wish.
+
+Otherwise, build libplist and openssl from source: see instructions
+near the end of this README; requires development tools (autoconf,
+automake, libtool, etc.) to be installed.
+Next get the latest macOS release of GStreamer-1.0.
+Using “Official” GStreamer (Recommended for both MacPorts and
+Homebrew users): install the GStreamer release for macOS from
+https://gstreamer.freedesktop.org/download/. (This
+release contains its own pkg-config, so you don’t have to install one.)
+Install both the gstreamer-1.0 and gstreamer-1.0-devel packages. After
+downloading, Shift-Click on them to install (they install to
+/Library/FrameWorks/GStreamer.framework). Homebrew or MacPorts users
+should not install (or should uninstall) the GStreamer
+supplied by their package manager, if they use the “official”
+release.
+
+- Since GStreamer v1.22, the “Official” (gstreamer.freedesktop.org)
+macOS binaries require a wrapper “gst_macos_main” around the actual main
+program (uxplay). This should have been applied during the UxPlay
+compilation process, and the initial UxPlay terminal message should
+confirm it is being used. (UxPlay can also be built using “Official”
+GStreamer v.1.20.7 binaries, which work without the wrapper.)
+
+Using Homebrew’s GStreamer: pkg-config is needed:
+(“brew install pkg-config gstreamer”). This causes a large number of
+extra packages to be installed by Homebrew as dependencies. The Homebrew
+gstreamer installation has recently been reworked into a single
+“formula” named gstreamer
, which now works without needing
+GST_PLUGIN_PATH to be set in the enviroment. Homebrew installs gstreamer
+to HOMEBREW_PREFIX/lib/gstreamer-1.0
where by default
+HOMEBREW_PREFIX/*
is /opt/homebrew/*
on Apple
+Silicon Macs, and /usr/local/*
on Intel Macs; do not put
+any extra non-Homebrew plugins (that you build yourself) there, and
+instead set GST_PLUGIN_PATH to point to their location (Homebrew does
+not supply a complete GStreamer, but seems to have everything needed for
+UxPlay). New: the UxPlay build script will now also detect
+Homebrew installations in non-standard locations indicated by the
+environment variable $HOMEBREW_PREFIX
.
+Using GStreamer installed from MacPorts: this is
+not recommended, as currently the MacPorts GStreamer is
+old (v1.16.2), unmaintained, and built to use X11:
+
+(If you really wish to use the MacPorts GStreamer-1.16.2, install
+pkgconf (“sudo port install pkgconf”), then “sudo port install
+gstreamer1-gst-plugins-base gstreamer1-gst-plugins-good
+gstreamer1-gst-plugins-bad gstreamer1-gst-libav”. For X11 support on
+macOS, compile UxPlay using a special cmake option
+-DUSE_X11=ON
, and run it from an XQuartz terminal with -vs
+ximagesink; older non-retina macs require a lower resolution when using
+X11: uxplay -s 800x600
.)
+After installing GStreamer, build and install uxplay: open a terminal
+and change into the UxPlay source directory (“UxPlay-master” for zipfile
+downloads, “UxPlay” for “git clone” downloads) and build/install with
+“cmake . ; make ; sudo make install” (same as for Linux).
+
+Running UxPlay while checking for GStreamer warnings (do this
+with “export GST_DEBUG=2” before runnng UxPlay) reveals that with the
+default (since UxPlay 1.64) use of timestamps for video synchonization,
+many video frames are being dropped (only on macOS), perhaps due to
+another error (about videometa) that shows up in the GStreamer warnings.
+Recommendation: use the new UxPlay “no timestamp” option
+“-vsync no
” (you can add a line “vsync no” in the
+uxplayrc configuration file).
+On macOS with this installation of GStreamer, the only videosinks
+available seem to be glimagesink (default choice made by autovideosink)
+and osxvideosink. The window title does not show the Airplay server
+name, but the window is visible to screen-sharing apps (e.g., Zoom). The
+only available audiosink seems to be osxaudiosink.
+The option -nc is always used, whether or not it is selected.
+This is a workaround for a problem with GStreamer videosinks on macOS:
+if the GStreamer pipeline is destroyed while the mirror window is still
+open, a segfault occurs.
+In the case of glimagesink, the resolution settings “-s wxh” do
+not affect the (small) initial OpenGL mirror window size, but the window
+can be expanded using the mouse or trackpad. In contrast, a window
+created with “-vs osxvideosink” is initially big, but has the wrong
+aspect ratio (stretched image); in this case the aspect ratio changes
+when the window width is changed by dragging its side; the option
+-vs "osxvideosink force-aspect-ratio=true"
can be used to
+make the window have the correct aspect ratio when it first
+opens.
+
+Building
+UxPlay on Microsoft Windows, using MSYS2 with the MinGW-64
+compiler.
+
+- tested on Windows 10 and 11, 64-bit.
+
+
+Download and install Bonjour SDK for Windows
+v3.0. You can download the SDK without any registration at softpedia.com,
+or get it from the official Apple site https://developer.apple.com/download
+(Apple makes you register as a developer to access it from their site).
+This should install the Bonjour SDK as
+C:\Program Files\Bonjour SDK
.
+(This is for 64-bit Windows; a build for 32-bit Windows should be
+possible, but is not tested.) The unix-like MSYS2 build environment will
+be used: download and install MSYS2 from the official site https://www.msys2.org/. Accept the
+default installation location C:\mysys64
.
+MSYS2 packages
+are installed with a variant of the “pacman” package manager used by
+Arch Linux. Open a “MSYS2 MINGW64” terminal from the MSYS2 tab in the
+Windows Start menu, and update the new MSYS2 installation with “pacman
+-Syu”. Then install the MinGW-64 compiler and
+cmake
+pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc
+The compiler with all required dependencies will be installed in the
+msys64 directory, with default path C:/msys64/mingw64
. Here
+we will simply build UxPlay from the command line in the MSYS2
+environment (this uses “ninja
” in place of
+“make
” for the build system).
+Download the latest UxPlay from github (to use
+git
, install it with pacman -S git
, then
+“git clone https://github.com/FDH2/UxPlay
”), then
+install UxPlay dependencies (openssl is already installed with
+MSYS2):
+pacman -S mingw-w64-x86_64-libplist mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base
+If you are trying a different Windows build system, MSVC versions of
+GStreamer for Windows are available from the official GStreamer
+site, but only the MinGW 64-bit build on MSYS2 has been
+tested.
+cd to the UxPlay source directory, then
+“mkdir build
” and “cd build
”. The build
+process assumes that the Bonjour SDK is installed at
+C:\Program Files\Bonjour SDK
. If it is somewhere else, set
+the enviroment variable BONJOUR_SDK_HOME to point to its location. Then
+build UxPlay with
+cmake ..
+ninja
+Assuming no error in either of these, you will have built the
+uxplay executable uxplay.exe in the current (“build”)
+directory. The “sudo make install” and “sudo make uninstall” features
+offered in the other builds are not available on Windows; instead, the
+MSYS2 environment has /mingw64/...
available, and you can
+install the uxplay.exe executable in C:/msys64/mingw64/bin
+(plus manpage and documentation in
+C:/msys64/mingw64/share/...
) with
+cmake --install . --prefix /mingw64
+To be able to view the manpage, you need to install the manpage
+viewer with “pacman -S man
”.
+
+To run uxplay.exe you need to install some gstreamer
+plugin packages with
+pacman -S mingw-w64-x86_64-gst-<plugin>
, where the
+required ones have <plugin>
given by
+
+- libav
+- plugins-good
+- plugins-bad
+
+Other possible MSYS2 gstreamer plugin packages you might use are
+listed in MSYS2
+packages.
+You also will need to grant permission to the uxplay executable
+uxplay.exe to access data through the Windows firewall. You may
+automatically be offered the choice to do this when you first run
+uxplay, or you may need to do it using Windows
+Settings->Update and Security->Windows Security->Firewall &
+network protection -> allow an app through firewall. If your
+virus protection flags uxplay.exe as “suspicious” (but without a true
+malware signature) you may need to give it an exception.
+Now test by running “uxplay
” (in a MSYS2 terminal
+window). If you need to specify the audiosink, there are two main
+choices on Windows: the older DirectSound plugin
+“-as directsoundsink
”, and the more modern Windows Audio
+Session API (wasapi) plugin “-as wasapisink
”, which
+supports additional
+options such as
+uxplay -as 'wasapisink device=\"<guid>\"'
+where <guid>
specifies an available audio device
+by its GUID, which can be found using
+“gst-device-monitor-1.0 Audio
”: <guid>
+has a form like
+\{0.0.0.00000000\}.\{98e35b2b-8eba-412e-b840-fd2c2492cf44\}
.
+If “device
” is not specified, the default audio device is
+used.
+If you wish to specify the videosink using the
+-vs <videosink>
option, some choices for
+<videosink>
are d3d11videosink
,
+d3dvideosink
, glimagesink
,
+gtksink
.
+
+- With Direct3D 11.0 or greater, you can either always be in
+fullscreen mode using option
+
-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true"
,
+or get the ability to toggle into and out of fullscreen mode using the
+Alt-Enter key combination with option
+-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"
. For
+convenience, these options will be added if just
+-vs d3d11videosink
with or without the fullscreen option
+“-fs” is used. (Windows users may wish to add
+“vs d3d11videosink
” (no initial “-
”) to the
+UxPlay startup options file; see “man uxplay” or “uxplay -h”.)
+
+The executable uxplay.exe can also be run without the MSYS2
+environment, in the Windows Terminal, with
+C:\msys64\mingw64\bin\uxplay
.
+Usage
+Options:
+
+- These can also be written (one option per line, without the initial
+“
-
” character) in the UxPlay startup file (either given by
+environment variable $UXPLAYRC
, or ~/.uxplayrc
+or ~/.config/uxplayrc
); lines begining with
+“#
” are treated as comments, and ignored. Command line
+options supersede options in the startup file.
+
+-n server_name (Default: UxPlay);
+server_name@_hostname_ will be the name that appears offering AirPlay
+services to your iPad, iPhone etc, where hostname is the name
+of the server running uxplay. This will also now be the name shown above
+the mirror display (X11) window.
+-nh Do not append “@_hostname_” at the end of the AirPlay
+server name.
+-h265 Activate “ScreenMultiCodec” support (AirPlay
+“Features” bit 42) for accepting h265 (4K/HEVC) video in addition to
+h264 video (1080p) in screen-mirror mode. When this option is used, two
+“video pipelines” (one for h264, one for h265) are created. If any
+GStreamer plugins in the pipeline are specific for h264 or h265, the
+correct version will be used in each pipeline. A wired Client-Server
+ethernet connection is preferred over Wifi for 4K video, and might be
+required by the client. Only recent Apple devices (M1/M2 Macs or iPads,
+and some iPhones) can send h265 video if a resolution “-s wxh” with h
+> 1080 is requested. The “-h265” option changes the default
+resolution (“-s” option) from 1920x1080 to 3840x2160, and leaves default
+maximum framerate (“-fps” option) at 30fps.
+-hls Activate HTTP Live Streaming support. With this
+option YouTube videos can be streamed directly from YouTube servers to
+UxPlay (without passing through the client) by clicking on the AirPlay
+icon in the YouTube app.
+-pin [nnnn]: (since v1.67) use Apple-style
+(one-time) “pin” authentication when a new client connects for the first
+time: a four-digit pin code is displayed on the terminal, and the client
+screen shows a login prompt for this to be entered. When “-pin” is used
+by itself, a new random pin code is chosen for each authentication; if
+“-pin nnnn” (e.g., “-pin 3939”) is used, this will set an unchanging
+fixed code. Authentication adds the server to the client’s list of
+“trusted servers” and the client will not need to reauthenticate
+provided that the client and server public keys remain unchanged. (By
+default since v1.68, the server public key is generated from the MAC
+address, which can be changed with the -m option; see the -key option
+for an alternative method of key generation). (Add a line “pin” in
+the UxPlay startup file if you wish the UxPlay server to use the pin
+authentication protocol).
+-reg [filename]: (since v1.68). If “-pin”
+is used, this option maintains a register of pin-authenticated “trusted
+clients” in $HOME/.uxplay.register (or optionally, in
+filename). Without this option, returning clients that skip
+pin-authentication are trusted and not checked. This option may be
+useful if UxPlay is used in a more public environment, to record client
+details; the register is text, one line per client, with client’s public
+key (base-64 format), Device ID, and Device name; commenting out (with
+“#”) or deleting a line deregisters the corresponding client (see
+options -restrict, -block, -allow for more ways to control client
+access). (Add a line “reg” in the startup file if you wish to use
+this feature.)
+-vsync [x] (In Mirror mode:) this option
+(now the default) uses timestamps to synchronize audio
+with video on the server, with an optional audio delay in (decimal)
+milliseconds (x = “20.5” means 0.0205 seconds delay: positive
+or negative delays less than a second are allowed.) It is needed on
+low-power systems such as Raspberry Pi without hardware video
+decoding.
+-vsync no (In Mirror mode:) this switches off
+timestamp-based audio-video synchronization, restoring the default
+behavior prior to UxPlay-1.64. Standard desktop systems seem to work
+well without use of timestamps: this mode is appropriate for “live
+streaming” such as using UxPlay as a second monitor for a mac computer,
+or monitoring a webcam; with it, no video frames are dropped.
+-async [x] (In Audio-Only (ALAC) mode:) this option
+uses timestamps to synchronize audio on the server with video on the
+client, with an optional audio delay in (decimal) milliseconds
+(x = “20.5” means 0.0205 seconds delay: positive or negative
+delays less than a second are allowed.) Because the client adds a video
+delay to account for latency, the server in -async mode adds an
+equivalent audio delay, which means that audio changes such as a pause
+or a track-change will not take effect immediately. This might in
+principle be mitigated by using the -al
audio latency
+setting to change the latency (default 0.25 secs) that the server
+reports to the client, but at present changing this does not seem to
+have any effect.
+-async no. This is the still the default behavior in
+Audio-only mode, but this option may be useful as a command-line option
+to switch off a -async
option set in a “uxplayrc”
+configuration file.
+-db low[:high] Rescales the
+AirPlay volume-control attenuation (gain) from -30dB:0dB to
+low:0dB or low:high. The lower limit
+low must be negative (attenuation); the upper limit
+high can be either sign. (GStreamer restricts
+volume-augmentation by high so that it cannot exceed +20dB).
+The rescaling is “flat”, so that for -db -50:10, a change in Airplay
+attenuation by -7dB is translated to a -7 x (60/30) = -14dB attenuation,
+and the maximum volume (AirPlay 0dB) is a 10dB augmentation, and Airplay
+-30dB would become -50dB. Note that the minimum AirPlay value (-30dB
+exactly) is translated to “mute”.
+-taper Provides a “tapered” Airplay volume-control
+profile (matching the one called “dasl-tapering” in shairport-sync):
+each time the length of the volume slider (or the number of steps above
+mute, where 16 steps = full volume) is reduced by 50%, the perceived
+volume is halved (a 10dB attenuation). (This is modified at low volumes,
+to use the “untapered” volume if it is louder.)
+-s wxh e.g. -s 1920x1080 (= “1080p”), the default
+width and height resolutions in pixels for h264 video. (The default
+becomes 3840x2160 (= “4K”) when the -h265 option is used.) This is just
+a request made to the AirPlay client, and perhaps will not be the final
+resolution you get. w and h are whole numbers with four digits or less.
+Note that the height pixel size is the controlling one
+used by the client for determining the streaming format; the width is
+dynamically adjusted to the shape of the image (portrait or landscape
+format, depending on how an iPad is held, for example).
+-s wxh@r As above, but also informs the AirPlay
+client about the screen refresh rate of the display. Default is r=60 (60
+Hz); r must be a whole number less than 256.
+-o turns on an “overscanned” option for the display
+window. This reduces the image resolution by using some of the pixels
+requested by option -s wxh (or their default values 1920x1080) by adding
+an empty boundary frame of unused pixels (which would be lost in a
+full-screen display that overscans, and is not displayed by gstreamer).
+Recommendation: don’t use this option unless there is
+some special reason to use it.
+-fs uses fullscreen mode, but only works with X11,
+Wayland, VAAPI, and D3D11 (Windows).
+-p allows you to select the network ports used by
+UxPlay (these need to be opened if the server is behind a firewall). By
+itself, -p sets “legacy” ports TCP 7100, 7000, 7001, UDP 6000, 6001,
+7011. -p n (e.g. -p 35000) sets TCP and UDP ports n, n+1, n+2. -p
+n1,n2,n3 (comma-separated values) sets each port separately; -p n1,n2
+sets ports n1,n2,n2+1. -p tcp n or -p udp n sets just the TCP or UDP
+ports. Ports must be in the range [1024-65535].
+If the -p option is not used, the ports are chosen dynamically
+(randomly), which will not work if a firewall is running.
+-avdec forces use of software h264 decoding using
+Gstreamer element avdec_h264 (libav h264 decoder). This option should
+prevent autovideosink choosing a hardware-accelerated videosink plugin
+such as vaapisink.
+-vp parser choses the GStreamer pipeline’s
+h264 parser element, default is h264parse. Using quotes “…” allows
+options to be added.
+-vd decoder chooses the GStreamer
+pipeline’s h264 decoder element, instead of the default value
+“decodebin” which chooses it for you. Software decoding is done by
+avdec_h264; various hardware decoders include: vaapih264dec, nvdec,
+nvh264dec, v4l2h264dec (these require that the appropriate hardware is
+available). Using quotes “…” allows some parameters to be included with
+the decoder name.
+-vc converter chooses the GStreamer
+pipeline’s videoconverter element, instead of the default value
+“videoconvert”. When using Video4Linux2 hardware-decoding by a
+GPU,-vc v4l2convert
will also use the GPU for video
+conversion. Using quotes “…” allows some parameters to be included with
+the converter name.
+-vs videosink chooses the GStreamer
+videosink, instead of the default value “autovideosink” which chooses it
+for you. Some videosink choices are: ximagesink, xvimagesink, vaapisink
+(for intel graphics), gtksink, glimagesink, waylandsink, osxvideosink
+(for macOS), kmssink (for systems without X11, like Raspberry Pi OS
+lite) or fpsdisplaysink (which shows the streaming framerate in fps).
+Using quotes “…” allows some parameters to be included with the
+videosink name. For example, fullscreen mode is
+supported by the vaapisink plugin, and is obtained using
+-vs "vaapisink fullscreen=true"
; this also works with
+waylandsink
. The syntax of such options is specific to a
+given plugin (see GStreamer documentation), and some choices of
+videosink might not work on your system.
+-vs 0 suppresses display of streamed video. In
+mirror mode, the client’s screen is still mirrored at a reduced rate of
+1 frame per second, but is not rendered or displayed. This option should
+always be used if the server is “headless” (with no attached screen to
+display video), and only used to render audio, which will be AAC
+lossily-compressed audio in mirror mode with unrendered video, and
+superior-quality ALAC Apple Lossless audio in Airplay audio-only
+mode.
+-v4l2 Video settings for hardware h264 video
+decoding in the GPU by Video4Linux2. Equivalent to
+-vd v4l2h264dec -vc v4l2convert
.
+-bt709 A workaround for the failure of the older
+Video4Linux2 plugin to recognize Apple’s use of an uncommon (but
+permitted) “full-range color” variant of the bt709 color standard for
+digital TV. This is no longer needed by GStreamer-1.20.4 and backports
+from it.
+-rpi Equivalent to “-v4l2” (Not valid for Raspberry
+Pi model 5, and removed in UxPlay 1.67)
+-rpigl Equivalent to “-rpi -vs glimagesink”.
+(Removed since UxPlay 1.67)
+-rpifb Equivalent to “-rpi -vs kmssink” (Removed
+since UxPlay 1.67)
+-rpiwl Equivalent to “-rpi -vs waylandsink”.
+(Removed since UxPlay 1.67)
+-as audiosink chooses the GStreamer
+audiosink, instead of letting autoaudiosink pick it for you. Some
+audiosink choices are: pulsesink, alsasink, pipewiresink, osssink,
+oss4sink, jackaudiosink, osxaudiosink (for macOS), wasapisink,
+directsoundsink (for Windows). Using quotes “…” might allow some
+optional parameters (e.g. -as "alsasink device=..."
to
+specify a non-default output device). The syntax of such options is
+specific to a given plugin (see GStreamer documentation), and some
+choices of audiosink might not work on your system.
+-as 0 (or just -a) suppresses
+playing of streamed audio, but displays streamed video.
+-al x specifies an audio latency x
+in (decimal) seconds in Audio-only (ALAC), that is reported to the
+client. Values in the range [0.0, 10.0] seconds are allowed, and will be
+converted to a whole number of microseconds. Default is 0.25 sec (250000
+usec). (However, the client appears to ignore this reported latency,
+so this option seems non-functional.)
+-ca filename provides a file (where
+filename can include a full path) used for output of “cover
+art” (from Apple Music, etc.,) in audio-only ALAC mode. This
+file is overwritten with the latest cover art as it arrives. Cover art
+(jpeg format) is discarded if this option is not used. Use with a image
+viewer that reloads the image if it changes, or regularly (e.g.
+once per second.). To achieve this, run
+“uxplay -ca [path/to/]filename &
” in the background,
+then run the the image viewer in the foreground. Example, using
+feh
as the viewer: run
+“feh -R 1 [path/to/]filename
” (in the same terminal window
+in which uxplay was put into the background). To quit, use
+ctrl-C fg ctrl-C
to terminate the image viewer, bring
+uxplay
into the foreground, and terminate it too.
+-reset n sets a limit of n consecutive
+timeout failures of the client to respond to ntp requests from the
+server (these are sent every 3 seconds to check if the client is still
+present, and synchronize with it). After n failures, the client
+will be presumed to be offline, and the connection will be reset to
+allow a new connection. The default value of n is 5; the value
+n = 0 means “no limit” on timeouts.
+-nofreeze closes the video window after a reset due
+to ntp timeout (default is to leave window open to allow a smoother
+reconection to the same client). This option may be useful in fullscreen
+mode.
+-nc maintains previous UxPlay < 1.45 behavior
+that does not close the video window when the the
+client sends the “Stop Mirroring” signal. This option is currently
+used by default in macOS, as the window created in macOS by GStreamer
+does not terminate correctly (it causes a segfault) if it is still open
+when the GStreamer pipeline is closed.
+-nohold Drops the current connection when a new
+client attempts to connect. Without this option, the current client
+maintains exclusive ownership of UxPlay until it disconnects.
+-restrict Restrict clients allowed to connect to
+those specified by -allow <deviceID>
. The deviceID
+has the form of a MAC address which is displayed by UxPlay when the
+client attempts to connect, and appears to be immutable. It has the
+format XX:XX:XX:XX:XX:XX
, X = 0-9,A-F, and is possibly the
+“true” hardware MAC address of the device. Note that iOS clients
+generally expose different random “private Wi_Fi addresses” (“fake” MAC
+addresses) to different networks (for privacy reasons, to prevent
+tracking), which may change, and do not correpond to the deviceID.
+-restrict no Remove restrictions (default). This is
+useful as a command-line argument to overide restrictions set in the
+Startup file.
+-allow id Adds the deviceID = id
+to the list of allowed clients when client restrictions are being
+enforced. Usually this will be an entry in the uxplayrc startup
+file.
+-block id Always block clients with
+deviceID = id, even when client restrictions are not being
+enforced generally. Usually this will be an entry in the uxplayrc
+startup file.
+-FPSdata Turns on monitoring of regular reports
+about video streaming performance that are sent by the client. These
+will be displayed in the terminal window if this option is used. The
+data is updated by the client at 1 second intervals.
+-fps n sets a maximum frame rate (in frames per
+second) for the AirPlay client to stream video; n must be a whole number
+less than 256. (The client may choose to serve video at any frame rate
+lower than this; default is 30 fps.) A setting of 60 fps may give you
+improved video but is not recommended on Raspberry Pi. A setting below
+30 fps might be useful to reduce latency if you are running more than
+one instance of uxplay at the same time. This setting is only an
+advisory to the client device, so setting a high value will not force a
+high framerate. (You can test using “-vs fpsdisplaysink” to see
+what framerate is being received, or use the option -FPSdata which
+displays video-stream performance data continuously sent by the client
+during video-streaming.)
+-f {H|V|I} implements “videoflip” image transforms:
+H = horizontal flip (right-left flip, or mirror image); V = vertical
+flip ; I = 180 degree rotation or inversion (which is the combination of
+H with V).
+-r {R|L} 90 degree Right (clockwise) or Left
+(counter-clockwise) rotations; these image transforms are carried out
+after any -f transforms.
+-m [mac] changes the MAC address (Device ID) used by
+UxPlay (default is to use the true hardware MAC address reported by the
+host computer’s network card). (Different server_name, MAC addresses,
+and network ports are needed for each running uxplay if you attempt to
+run more than one instance of uxplay on the same computer.) If [mac] (in
+form xx:xx:xx:xx:xx:xx, 6 hex octets) is not given, a random MAC address
+is generated. If UxPlay fails to find the true MAC address of a network
+card, (more specifically, the MAC address used by the first active
+network interface detected) a random MAC address will be used even if
+option -m was not specified. (Note that a random MAC
+address will be different each time UxPlay is started).
+-key [filename]: This (more secure) option
+for generating and storing a persistant public key (needed for the -pin
+option) has been replaced by default with a (less secure) method which
+generates a key from the server’s “device ID” (MAC address, which can be
+changed with the -m option, conveniently as a startup file option). When
+the -key option is used, a securely generated keypair is generated and
+stored in $HOME/.uxplay.pem
, if that file does not exist,
+or read from it, if it exists. (Optionally, the key can be stored in
+filename.) This method is more secure than the new default
+method, (because the Device ID is broadcast in the DNS_SD announcement)
+but still leaves the private key exposed to anyone who can access the
+pem file. This option should be set in the UxPlay startup file as a line
+“key” or “key filename” (no initial “-”), where
+filename is a full path which should be enclosed in quotes
+("...."
) if it contains any blank spaces. Because
+the default method is simpler, and security of client access to uxplay
+is unlikely to be an important issue, the -key option is no longer
+recommended.
+-dacp [filename]: Export current client
+DACP-ID and Active-Remote key to file: default is $HOME/.uxplay.dacp.
+(optionally can be changed to filename). Can be used by remote
+control applications. File is transient: only exists while client is
+connected.
+-vdmp Dumps h264 video to file videodump.h264. -vdmp
+n dumps not more than n NAL units to videodump.x.h264; x= 1,2,…
+increases each time a SPS/PPS NAL unit arrives. To change the name
+videodump, use -vdmp [n] filename.
+-admp Dumps audio to file audiodump.x.aac (AAC-ELD
+format audio), audiodump.x.alac (ALAC format audio) or audiodump.x.aud
+(other-format audio), where x = 1,2,3… increases each time the audio
+format changes. -admp n restricts the number of packets dumped
+to a file to n or less. To change the name audiodump,
+use -admp [n] filename. Note that (unlike dumped video) the
+dumped audio is currently only useful for debugging, as it is not
+containerized to make it playable with standard audio players.
+-d Enable debug output. Note: this does not show
+GStreamer error or debug messages. To see GStreamer error and warning
+messages, set the environment variable GST_DEBUG with “export
+GST_DEBUG=2” before running uxplay. To see GStreamer information
+messages, set GST_DEBUG=4; for DEBUG messages, GST_DEBUG=5; increase
+this to see even more of the GStreamer inner workings.
+Troubleshooting
+Note: uxplay
is run from a terminal command line, and
+informational messages are written to the terminal.
+0. Problems in compiling
+UxPlay.
+One user (on Ubuntu) found compilation failed with messages about
+linking to “usr/local/lib/libcrypto.a” and “zlib”. This was because (in
+addition to the standard ubuntu installation of libssl-dev), the user
+was unaware that a second installation with libcrypto in /usr/local was
+present. Solution: when more than one installation of OpenSSL is
+present, set the environment variable OPEN_SSL_ROOT_DIR to point to the
+correct one; on 64-bit Ubuntu, this is done by running
+export OPENSSL_ROOT_DIR=/usr/lib/X86_64-linux-gnu/
before
+running cmake.
+1. Avahi/DNS_SD
+Bonjour/Zeroconf issues
+The DNS_SD Service-Discovery (“Bonjour” or “Zeroconf”) service is
+required for UxPlay to work. On Linux, it will be usually provided by
+Avahi, and to troubleshoot this, you should use the tool
+avahi-browse
. (You may need to install a separate package
+with a name like avahi-utils
to get this.)
+On Linux, make sure Avahi is installed, and start the avahi-daemon
+service on the system running uxplay (your distribution will document
+how to do this, for example:
+sudo systemctl <cmd> avahi-daemon
or
+sudo service avahi-daemon <cmd>
, with
+<cmd>
one of enable, disable, start, stop, status.
+You might need to edit the avahi-daemon.conf file (it is typically in
+/etc/avahi/, find it with
+“sudo find /etc -name avahi-daemon.conf
”): make sure that
+“disable-publishing” is not a selected option). Some
+systems may instead use the mdnsd daemon as an alternative to provide
+DNS-SD service. (FreeBSD offers both alternatives, but only Avahi was
+tested; see here.)
+
+- uxplay starts, but either stalls or stops after “Initialized
+server socket(s)” appears (without the server name showing on the
+client).
+
+If UxPlay stops with the “No DNS-SD Server found” message, this means
+that your network does not have a running Bonjour/zeroconf
+DNS-SD server. Before v1.60, UxPlay used to stall silently if
+DNS-SD service registration failed, but now stops with an error message
+returned by the DNSServiceRegister function: kDNSServiceErr_Unknown if
+no DNS-SD server was found: (A NixOS user found that in NixOS, this
+error can also occur if avahi-daemon service IS running with publishing
+enabled, but reports “the error disappeared on NixOS by setting
+services.avahi.openFirewall to true”.) Other mDNS error codes are
+in the range FFFE FF00 (-65792) to FFFE FFFF (-65537), and are listed in
+the dnssd.h file. An older version of this (the one used by avahi) is
+found here.
+A few additional error codes are defined in a later version from Apple.
+If UxPlay stalls without an error message and without
+the server name showing on the client, this is a network
+problem (if your UxPlay version is older than 1.60, it is also
+the behavior when no DNS-SD server is found.)
+A useful tool for examining such network problems from the client end
+is the (free) Discovery DNS-SD browser available
+in the Apple App Store for both iOS (works on iPadOS too) and
+macOS.
+
+- Some users using dual-band (2.4GHz/5GHz) routers have reported that
+clients using the 5GHz band (sometimes) “fail to see UxPlay” (i.e., do
+not get a response to their mDNS queries), but the 2.4GHz band works.
+Other projects using Bonjour/mDNS have had similar reports; the issue
+seems to be router-specific, perhaps related to “auto” rather than fixed
+channel selection (5GHz has many more channels to switch between), or
+channel width selections; one speculation is that since mDNS uses UDP
+protocol (where “lost” messages are not resent), a mDNS query might get
+lost if channel switching occurs during the query.
+
+If your router has this problem, a reported “fix” is to (at least on
+5GHz) use fixed channel and/or fixed (not dynamic) channel width.
+
+- Avahi works at first, but new clients do not see UxPlay, or
+clients that initially saw it stop doing so after they
+disconnect.
+
+This is usually because Avahi is only using the “loopback” network
+interface, and is not receiving mDNS queries from new clients that were
+not listening when UxPlay started.
+To check this, after starting uxplay, use the utility
+avahi-browse -a -t
in a different terminal
+window on the server to verify that the UxPlay AirTunes and
+AirPlay services are correctly registered (only the AirTunes service is
+used in the “Legacy” AirPlay Mirror mode used by UxPlay, but the AirPlay
+service is used for the initial contact).
+The results returned by avahi-browse should show entries for uxplay
+like
++ eno1 IPv6 UxPlay AirPlay Remote Video local
++ eno1 IPv4 UxPlay AirPlay Remote Video local
++ lo IPv4 UxPlay AirPlay Remote Video local
++ eno1 IPv6 863EA27598FE@UxPlay AirTunes Remote Audio local
++ eno1 IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local
++ lo IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local
+If only the loopback (“lo”) entries are shown, a firewall on the
+UxPlay host is probably blocking full DNS-SD service, and you need to
+open the default UDP port 5353 for mDNS requests, as loopback-based
+DNS-SD service is unreliable.
+If the UxPlay services are listed by avahi-browse as above, but are
+not seen by the client, the problem is likely to be a problem with the
+local network.
+2.
+uxplay starts, but stalls after “Initialized server socket(s)” appears,
+with the server name showing on the client (but the client
+fails to connect when the UxPlay server is selected).
+This shows that a DNS-SD service is working, clients hear
+UxPlay is available, but the UxPlay server is not receiving the response
+from the client. This is usually because a firewall on the server is
+blocking the connection request from the client. (One user who insisted
+that the firewall had been turned off turned out to have had
+two active firewalls (firewalld and ufw)
+both running on the server!) If possible, either turn off the
+firewall to see if that is the problem, or get three consecutive network
+ports, starting at port n, all three in the range 1024-65535, opened for
+both tcp and udp, and use “uxplay -p n” (or open UDP 7011,6001,6000 TCP
+7100,7000,7001 and use “uxplay -p”).
+If you are really sure there is no firewall, you may need to
+investigate your network transmissions with a tool like netstat, but
+almost always this is a firewall issue.
+3.
+Problems after the client-server connection has been made:
+If you do not see the message
+raop_rtp_mirror starting mirroring
, something went wrong
+before the client-server negotiations were finished. For such problems,
+use “uxplay -d” (debug log option) to see what is happening: it will
+show how far the connection process gets before the failure occurs. You
+can compare your debug output to that from a successful start of UxPlay
+in the UxPlay
+Wiki.
+If UxPlay reports that mirroring started, but you get no
+video or audio, the problem is probably from a GStreamer plugin that
+doesn’t work on your system (by default, GStreamer uses the
+“autovideosink” and “autoaudiosink” algorithms to guess what are the
+“best” plugins to use on your system). A different reason for no audio
+occurred when a user with a firewall only opened two udp network ports:
+three are required (the third one receives the audio
+data).
+Raspberry Pi devices (Pi 4B+ and earlier: this
+does not apply to the Pi 5, which does not provide hardware h264
+decoding, and does not need it) work best with hardware GPU h264
+video decoding if the Video4Linux2 plugin in GStreamer v1.20.x or
+earlier has been patched (see the UxPlay Wiki
+for patches). This is fixed in GStreamer-1.22, and by backport patches
+from this in distributions such as Raspberry Pi OS (Bullseye):
+use option -bt709
with the GStreamer-1.18.4 from
+Raspberry Pi OS. This also needs the bcm2835-codec kernel
+module that is not in the standard Linux kernel (it is available in
+Raspberry Pi OS, Ubuntu and Manjaro).
+
+- If this kernel module is not available in your Raspberry Pi
+operating system, or if GStreamer < 1.22 is not patched, use option
+
-avdec
for software h264-decoding.
+
+Sometimes “autovideosink” may select the OpenGL renderer
+“glimagesink” which may not work correctly on your system. Try the
+options “-vs ximagesink” or “-vs xvimagesink” to see if using one of
+these fixes the problem.
+Other reported problems are connected to the GStreamer VAAPI plugin
+(for hardware-accelerated Intel graphics, but not NVIDIA graphics). Use
+the option “-avdec” to force software h264 video decoding: this should
+prevent autovideosink from selecting the vaapisink videosink.
+Alternatively, find out if the gstreamer1.0-vaapi plugin is installed,
+and if so, uninstall it. (If this does not fix the problem, you can
+reinstall it.)
+There are some reports of other GStreamer problems with
+hardware-accelerated Intel HD graphics. One user (on Debian) solved this
+with “sudo apt install intel-media-va-driver-non-free”. This is a driver
+for 8’th (or later) generation “*-lake” Intel chips, that seems to be
+related to VAAPI accelerated graphics.
+If you do have Intel HD graphics, and have installed the
+vaapi plugin, but -vs vaapisink
does not work, check that
+vaapi is not “blacklisted” in your GStreamer installation: run
+gst-inspect-1.0 vaapi
, if this reports
+0 features
, you need to
+export GST_VAAPI_ALL_DRIVERS=1
before running uxplay, or
+set this in the default environment.
+You can try to fix audio or video problems by using the
+“-as <audiosink>
” or
+“-vs <videosink>
” options to choose the GStreamer
+audiosink or videosink , rather than letting GStreamer choose one for
+you. (See above, in Starting and
+running UxPlay for choices of <audiosink>
or
+<videosink>
.)
+The “OpenGL renderer” window created on Linux by “-vs glimagesink”
+sometimes does not close properly when its “close” button is clicked.
+(this is a GStreamer issue). You may need to terminate uxplay with
+Ctrl-C to close a “zombie” OpenGl window. If similar problems happen
+when the client sends the “Stop Mirroring” signal, try the no-close
+option “-nc” that leaves the video window open.
+4. GStreamer issues
+(missing plugins, etc.):
+
+- clearing the user’s GStreamer cache with
+
rm -rf ~/.cache/gstreamer-1.0/*
may be the solution to
+problems where gst-inspect-1.0 does not show a plugin that you believe
+is installed. The cache will be regenerated next time GStreamer is
+started. This is the solution to puzzling problems that turn out
+to come from corruption of the cache, and should be tried
+first.
+
+If UxPlay fails to start, with a message that a required GStreamer
+plugin (such as “libav”) was not found, first check with the GStreamer
+tool gst-inspect-1.0 to see what GStreamer knows is available. (You may
+need to install some additional GStreamer “tools” package to get
+gst-inspect-1.0). For, e.g. a libav problem, check with
+“gst-inspect-1.0 libav
”. If it is not shown as available to
+GStreamer, but your package manager shows the relevant package as
+installed (as one user found), try entirely removing and reinstalling
+the package. That user found that a solution to a “Required
+gstreamer plugin ‘libav’ not found” message that kept recurring
+was to clear the user’s gstreamer cache.
+If it fails to start with an error like
+‘no element "avdec_aac"
’ this is because even though
+gstreamer-libav is installed. it is incomplete because some plugin
+features are missing: “gst-inspect-1.0 | grep avdec_aac
”
+will show if avdec_aac is available. Unlike other GStreamer plugins, the
+libav plugin is a front end to FFmpeg codecs which provide avdec_*.
+
+Some distributions (RedHat, SUSE, etc) provide incomplete
+versions of FFmpeg because of patent issues with codecs used by certain
+plugins. In those cases there will be some “extra package” provider like
+RPM fusion (RedHat), packman (SUSE) where you can
+get complete packages (your distribution will usually provide
+instructions for this, Mageia puts them in an optional “tainted” repo).
+The packages needed may be “ffmpeg*” or “libav*” packages: the GStreamer
+libav plugin package does not contain any codecs itself, it just
+provides a way for GStreamer to use ffmpeg/libav codec libraries which
+must be installed separately. For similar reasons, distributions may
+ship incomplete packages of GStreamer “plugins-bad”. Use user on Fedora
+thought they had installed from rpmfusion, but the system had not
+obeyed: “Adding –allowerasing to the dnf command fixed it after a
+restart”.
+starting with release UxPlay-1.65.3, UxPlay will continue to
+function, but without audio in mirror mode, if avdec_aac is
+missing.
+
+To troubleshoot GStreamer execute “export GST_DEBUG=2” to set the
+GStreamer debug-level environment-variable in the terminal where you
+will run uxplay, so that you see warning and error messages; see GStreamer
+debugging tools for how to see much more of what is happening inside
+GStreamer. Run “gst-inspect-1.0” to see which GStreamer plugins are
+installed on your system.
+Some extra GStreamer packages for special plugins may need to be
+installed (or reinstalled: a user using a Wayland display system as an
+alternative to X11 reported that after reinstalling Lubuntu 18.4, UxPlay
+would not work until gstreamer1.0-x was installed, presumably for
+Wayland’s X11-compatibility mode). Different distributions may break up
+GStreamer 1.x into packages in different ways; the packages listed above
+in the build instructions should bring in other required GStreamer
+packages as dependencies, but will not install all possible plugins.
+The GStreamer video pipeline, which is shown in the initial output
+from uxplay -d
, has the default form
+appsrc name=video_source ! queue ! h264parse ! decodebin ! videoconvert ! autovideosink name=video_sink sync=false
+The pipeline is fully configurable: default elements “h264parse”,
+“decodebin”, “videoconvert”, and “autovideosink” can respectively be
+replaced by using uxplay options -vp
, -vd
,
+-vc
, and -vs
, if there is any need to modify
+it (entries can be given in quotes “…” to include options).
+5. Mirror screen
+freezes (a network problem):
+This can happen if the TCP video stream from the client stops
+arriving at the server, probably because of network problems (the UDP
+audio stream may continue to arrive). At 3-second intervals, UxPlay
+checks that the client is still connected by sending it a request for a
+NTP time signal. If a reply is not received from the client within a 0.3
+sec time-window, an “ntp timeout” is registered. If a certain number
+(currently 5) of consecutive ntp timeouts occur, UxPlay assumes that the
+client is “dead”, and resets the connection, becoming available for
+connection to a new client, or reconnection to the previous one.
+Sometimes the connection may recover before the timeout limit is
+reached, and if the default limit is not right for your network, it can
+be modified using the option “-reset n”, where n is
+the desired timeout-limit value (n = 0 means “no limit”). If
+the connection starts to recover after ntp timeouts, a corrupt video
+packet from before the timeout may trigger a “connection reset by peer”
+error, which also causes UxPlay to reset the connection.
+
+- When the connection is reset, the “frozen” mirror screen of the
+previous connection is left in place, but does not
+block new connections, and will be taken over by a new client connection
+when it is made.
+
+6.
+Protocol issues (with decryption of the encrypted audio and video
+streams sent by the client).
+A protocol failure may trigger an unending stream of error messages,
+and means that the audio decryption key (also used in video decryption)
+was not correctly extracted from data sent by the client.
+The protocol was modifed in UxPlay-1.65 after it was discovered that
+the client-server “pairing” step could be avoided (leading to a much
+quicker connection setup, without a 5 second delay) by disabling
+“Supports Legacy Pairing” (bit 27) in the “features” code UxPlay
+advertises on DNS-SD Service Discovery. Most clients will then not
+attempt the setup of a “shared secret key” when pairing, which is used
+by AppleTV for simultaneous handling of multiple clients (UxPlay only
+supports one client at a time). This change is now well-tested,
+but in case it causes any protocol failures, UxPlay can be reverted to
+the previous behavior by uncommenting the previous “FEATURES_1” setting
+(and commenting out the new one) in lib/dnssdint.h, and then rebuilding
+UxPlay. (“Pairing” is re-enabled when the new Apple-style
+one-time “pin” authentication is activated by running UxPlay with the
+“-pin” option introduced in UxPlay 1.67.)
+Protocol failure should not happen for iOS 9.3 or later clients.
+However, if a client uses the same older version of the protocol that is
+used by the Windows-based AirPlay client emulator AirMyPC, the
+protocol can be switched to the older version by the setting
+OLD_PROTOCOL_CLIENT_USER_AGENT_LIST
in
+UxPlay/lib/global.h
. UxPlay reports the client’s “User
+Agent” string when it connects. If some other client also fails to
+decrypt all audio and video, try adding its “User Agent” string in place
+of “xxx” in the entry “AirMyPC/2.0;xxx” in global.h and rebuild
+uxplay.
+Note that for DNS-SD Service Discovery, Uxplay declares itself to be
+an AppleTV3,2 (a 32 bit device) with a sourceVersion 220.68; this can
+also be changed in global.h. UxPlay also works if it declares itself as
+an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen,
+introduced 2017, running tvOS 12.2.1), so it does not seem to matter
+what version UxPlay claims to be.
+Changelog
+1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
+only for YouTube movies. Fix issue with NTP timeout on Windows.
+1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x
+2160). Fix issue with GStreamer >= 1.24 when client sleeps, then
+wakes.
+1.69 2024-08-09 Internal improvements (e.g. in -nohold option,
+identifying GStreamer videosink selected by autovideosink, finding X11
+display) in anticipation of future HLS video support. New -nofreeze
+option to not leave frozen video in place when a network connection is
+reset. Fixes for GStreamer-1.24.x changes.
+1.68 2023-12-31 New simpler (default) method for generating a
+persistent public key from the server MAC address (which can now be set
+with the -m option). (The previous method is still available with -key
+option). New option -reg to maintain a register of pin-authenticated
+clients. Corrected volume-control: now interprets AirPlay volume range
+-30dB:0dB as decibel gain attenuation, with new option -db low[:high]
+for “flat” rescaling of the dB range. Add -taper option for a “tapered”
+AirPlay volume-control profile.
+1.67 2023-11-30 Add support for Apple-style one-time pin
+authentication of clients with option “-pin”: (uses SRP6a authentication
+protocol and public key persistence). Detection with error message of
+(currently) unsupported H265 video when requesting high resolution over
+wired ethernet. Removed rpi* options (which are not valid with new
+Raspberry Pi model 5, and can be replaced by combinations of other
+options). Added optional argument “mac” to “-m” option, to specify a
+replacement MAC address/Device ID. Update llhttp to v. 9.1.3. Add -dacp
+option for exporting current client DACP info (for remotes).
+1.66 2023-09-05 Fix IPV6 support. Add option to restrict clients to
+those on a list of allowed deviceIDs, or to block connections from
+clients on a list of blocked deviceIDs. Fix for #207 from @thiccaxe (screen lag in
+vsync mode after client wakes from sleep).
+1.65.3 2023-07-23 Add RPM spec file; add warning if required
+gstreamer libav feature “avdec_aac” is missing: (this occurs in
+RPM-based distributions that ship an incomplete FFmpeg for Patent or
+License reasons, and rely on users installing an externally-supplied
+complete FFmpeg). Mirror-mode airplay will now work without audio if
+avdec_aac is missing.
+1.65 2023-06-03 Eliminate pair_setup part of connection protocol to
+allow faster connections with clients (thanks to @shuax #176 for this discovery); to revert,
+uncomment a line in lib/dnssdint.h. Disconnect from audio device when
+connection closes, to not block its use by other apps if uxplay is
+running but not connected. Fix for AirMyPC client (broken since 1.60),
+so its older non-NTP timestamp protocol works with -vsync. Corrected
+parsing of configuration file entries that were in quotes.
+1.64 2023-04-23 Timestamp-based synchronization of audio and video is
+now the default in Mirror mode. (Use “-vsync no” to restore previous
+behavior.) A configuration file can now be used for startup options.
+Also some internal cleanups and a minor bugfix that fixes #192.
+1.63 2023-02-12 Reworked audio-video synchronization, with new
+options -vsync (for Mirror mode) and -async (for Audio-Only mode, to
+sync with client video). Option -vsync makes software h264 decoding of
+streamed videos with option -avdec viable on some recent Raspberry Pi
+models. Internal change: all times are now processed in nanoseconds
+units. Removed -ao option introduced in 1.62.
+1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user
+synchronization of ALAC audio playing on the server with video, song
+lyrics, etc. playing on the client. x = 5.0 appears to be optimal in
+many cases. Quality fixes: cleanup in volume changes, timestamps, some
+bugfixes.
+1.61 2022-12-30 Removed -t option (workaround for an Avahi issue,
+correctly solved by opening network port UDP 5353 in firewall). Remove
+-g debug flag from CMAKE_CFLAGS. Postpend (instead of prepend) build
+environment CFLAGS to CMAKE_CFLAGS. Refactor parts of uxplay.cpp
+1.60 2022-12-15 Added exit with error message if DNSServiceRegister
+fails (instead of just stalling). Test for Client’s attempt to using
+unsupported AirPlay 2 “REMOTE CONTROL” protocol (with no timing
+channel), and exit if this occurs. Reworked metadata processing to
+correctly parse DMAP header (previous version worked with DMAP messages
+currently received, but was not correct).
+1.59 2022-12-12 remove “ZOOMFIX” compile option and make compilation
+with X11-dependence the default if X11 development libraries are
+detected (this now also provides fullscreen mode with a F11 or Alt+Enter
+key toggle); ZOOMFIX is now automatically applied for GStreamer <
+1.20. New cmake option -DNO_X11_DEPS compiles uxplay without X11
+dependence. Reworked internal metadata handling. Fix segfault with “-vs
+0”.
+1.58 2022-10-29 Add option “-nohold” that will drop existing
+connections when a new client connects. Update llhttp to v8.1.0.
+1.57 2022-10-09 Minor fixes: (fix coredump on AUR on “stop
+mirroring”, occurs when compiled with AUR CFLAGS -DFORTIFY_SOURCE);
+graceful exit when required plugins are missing; improved support for
+builds on Windows. Include audioresample in GStreamer audio
+pipeline.
+1.56 2022-09-01 Added support for building and running UxPlay-1.56 on
+Windows (no changes to Unix (Linux, *BSD, macOS) codebase.)
+1.56 2022-07-30 Remove -bt709 from -rpi, -rpiwl, -rpifb as GStreamer
+is now fixed.
+1.55 2022-07-04 Remove the bt709 fix from -v4l2 and create a new
+-bt709 option (previous “-v4l2” is now “-v4l2 -bt709”). This allows the
+currently-required -bt709 option to be used on its own on RPi without
+-v4l2 (sometimes this give better results).
+1.54 2022-06-25 Add support for “Cover Art” display in Audio-only
+(ALAC) mode. Reverted a change that caused VAAPI to crash with AMD
+POLARIS graphics cards. Minor internal changes to plist code and uxplay
+option parsing.
+1.53 2022-06-13 Internal changes to audio sync code, revised
+documentation, Minor bugfix (fix assertion crash when resent audio
+packets are empty).
+1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted
+streaming debug output (readable aligned timestamps with decimal points
+in seconds). Eliminate memory leaks (found by valgrind). Support for
+display of ALAC (audio-only) metadata (soundtrack artist names, titles
+etc.) in the uxplay terminal.
+1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option
+-v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2,
+-v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from
+1.48 that broke reconnection after “Stop Mirroring” is sent by
+client.
+1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI
+plugins only), Changed -rpi to be for framebuffer (“lite”) RPi systems
+and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop
+systems. Also modified timestamps from “DTS” to “PTS” for latency
+improvement, plus internal cleanups.
+1.49 2022-03-28 Addded options for dumping video and/or audio to
+file, for debugging, etc. h264 PPS/SPS NALU’s are shown with -d. Fixed
+video-not-working for M1 Mac clients.
+1.48 2022-03-11 Made the GStreamer video pipeline fully configurable,
+for use with hardware h264 decoding. Support for Raspberry Pi.
+1.47 2022-02-05 Added -FPSdata option to display (in the terminal)
+regular reports sent by the client about video streaming performance.
+Internal cleanups of processing of video packets received from the
+client. Added -reset n option to reset the connection after n ntp
+timeouts (also reset after “connection reset by peer” error in video
+stream).
+1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken
+hardware acceleration): once again use decodebin in the video pipeline;
+introduce new option “-avdec” to force software h264 decoding by libav
+h264, if needed (to prevent selection of vaapisink by autovideosink).
+Update llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2.
+Restrict connections to one client at a time (second client must now
+wait for first client to disconnect).
+1.45 2022-01-10 New behavior: close video window when client requests
+“stop mirroring”. (A new “no close” option “-nc” is added for users who
+wish to retain previous behavior that does not close the video
+window).
+1.44 2021-12-13 Omit hash of aeskey with ecdh_secret for an AirMyPC
+client; make an internal rearrangement of where this hash is done. Fully
+report all initial communications between client and server in -d debug
+mode. Replace decodebin in GStreamer video pipeline by h264-specific
+elements.
+1.43 2021-12-07 Various internal changes, such as tests for
+successful decryption, uniform treatment of informational/debug
+messages, etc., updated README.
+1.42 2021-11-20 Fix MAC detection to work with modern Linux interface
+naming practices, MacOS and *BSD.
+1.41 2021-11-11 Further cleanups of multiple audio format support
+(internal changes, separated RAOP and GStreamer audio/video startup)
+1.40 2021-11-09 Cleanup segfault in ALAC support, manpage location
+fix, show request Plists in debug mode.
+1.39 2021-11-06 Added support for Apple Lossless (ALAC) audio
+streams.
+1.38 2021-10-8 Add -as audiosink option to allow user to
+choose the GStreamer audiosink.
+1.37 2021-09-29 Append “@hostname” to AirPlay Server name, where
+“hostname” is the name of the server running uxplay (reworked change in
+1.36).
+1.36 2021-09-29 Implemented suggestion (by @mrbesen and @PetrusZ) to use hostname of machine runing
+uxplay as the default server name
+1.35.1 2021-09-28 Added the -vs 0 option for streaming audio, but not
+displaying video.
+1.35 2021-09-10 now uses a GLib MainLoop, and builds on macOS (tested
+on Intel Mac, 10.15 ). New option -t timeout for relaunching
+server if no connections were active in previous timeout
+seconds (to renew Bonjour registration).
+1.341 2021-09-04 fixed: render logger was not being destroyed by
+stop_server()
+1.34 2021-08-27 Fixed “ZOOMFIX”: the X11 window name fix was only
+being made the first time the GStreamer window was created by uxplay,
+and not if the server was relaunched after the GStreamer window was
+closed, with uxplay still running. Corrected in v. 1.34
+Building OpenSSL >=
+1.1.1 from source.
+If you need to do this, note that you may be able to use a newer
+version (OpenSSL-3.0.1 is known to work). You will need the standard
+development toolset (autoconf, automake, libtool). Download the source
+code from https://www.openssl.org/source/. Install the downloaded
+openssl by opening a terminal in your Downloads directory, and unpacking
+the source distribution: (“tar -xvzf openssl-3.0.1.tar.gz ; cd
+openssl-3.0.1”). Then build/install with “./config ; make ; sudo make
+install_dev”. This will typically install the needed library
+libcrypto.*
, either in /usr/local/lib or
+/usr/local/lib64.
+(Ignore the following for builds on MacOS:) On some systems
+like Debian or Ubuntu, you may also need to add a missing entry
+/usr/local/lib64
in /etc/ld.so.conf (or place a file
+containing “/usr/local/lib64/libcrypto.so” in /etc/ld.so.conf.d) and
+then run “sudo ldconfig”.
+Building libplist >=
+2.0.0 from source.
+(Note: on Debian 9 “Stretch” or Ubuntu 16.04 LTS editions, you
+can avoid this step by installing libplist-dev and libplist3 from Debian
+10 or Ubuntu 18.04.) As well as the usual build tools (autoconf,
+automake, libtool), you may need to also install some libpython*-dev
+package. Download the latest source with git from https://github.com/libimobiledevice/libplist, or get the
+source from the Releases section (use the *.tar.bz2 release,
+not the *.zip or *.tar.gz versions): download libplist-2.3.0,
+then unpack it (“tar -xvjf libplist-2.3.0.tar.bz2 ; cd libplist-2.3.0”),
+and build/install it: (“./configure ; make ; sudo make install”). This
+will probably install libplist-2.0.* in /usr/local/lib. The new
+libplist-2.3.0 release should be compatible with UxPlay; libplist-2.2.0
+is also available if there are any issues.
+(Ignore the following for builds on MacOS:) On some systems
+like Debian or Ubuntu, you may also need to add a missing entry
+/usr/local/lib
in /etc/ld.so.conf (or place a file
+containing “/usr/local/lib/libplist-2.0.so” in /etc/ld.so.conf.d) and
+then run “sudo ldconfig”.
+Disclaimer
+All the resources in this repository are written using only freely
+available information from the internet. The code and related resources
+are meant for educational purposes only. It is the responsibility of the
+user to make sure all local laws are adhered to.
+This project makes use of a third-party GPL library for handling
+FairPlay. The legal status of that library is unclear. Should you be a
+representative of Apple and have any objections against the legality of
+the library and its use in this project, please contact the developers
+and the appropriate steps will be taken.
+Given the large number of third-party AirPlay receivers (mostly
+closed-source) available for purchase, it is our understanding that an
+open source implementation of the same functionality wouldn’t violate
+any of Apple’s rights either.
+UxPlay authors
+[adapted from fdraschbacher’s notes on RPiPlay
+antecedents]
+The code in this repository accumulated from various sources over
+time. Here is an attempt at listing the various authors and the
+components they created:
+UxPlay was initially created by antimof from
+RPiPlay, by replacing its Raspberry-Pi-adapted OpenMAX video and audio
+rendering system with GStreamer rendering for desktop Linux systems; the
+antimof work on code in renderers/
was later backported to
+RPiPlay, and the antimof project became dormant, but was later revived
+at the current GitHub site
+to serve a wider community of users.
+The previous authors of code included in UxPlay by inheritance from
+RPiPlay include:
+
+- EstebanKubata: Created a FairPlay library called PlayFair. Located
+in the
lib/playfair
folder. License: GNU GPL
+- Juho Vähä-Herttua and contributors: Created an
+AirPlay audio server called ShairPlay, including
+support for Fairplay based on PlayFair. Most of the code in
+
lib/
originally stems from this project. License: GNU
+LGPLv2.1+
+- dsafa22: Created an AirPlay 2 mirroring server AirplayServer (seems
+gone now), for Android based on ShairPlay. Code is preserved here, and see here for the
+description of the analysis of the AirPlay 2 mirror protocol that made
+RPiPlay possible, by the AirplayServer author. All code in
+
lib/
concerning mirroring is dsafa22’s work. License: GNU
+LGPLv2.1+
+- Florian Draschbacher (FD-) and contributors:
+adapted dsafa22’s Android project for the Raspberry Pi, with extensive
+cleanups, debugging and improvements. The project RPiPlay is basically a port of
+dsafa22’s code to the Raspberry Pi, utilizing OpenMAX and OpenSSL for
+better performance on the Pi. License GPL v3. FD- has written an
+interesting note on the history of Airplay
+protocol versions, available at the RPiPlay github repository.
+
+Independent of UxPlay, but used by it and bundled with it:
+
+- Fedor Indutny (of Node.js, and formerly Joyent,
+Inc) and contributors: Created an http parsing library called llhttp. Located at
+
lib/llhttp/
. License: MIT
+
diff --git a/README.md b/README.md
index 403b638..016adc7 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,1954 @@
-# uxplayer
+# UxPlay 1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
+### **Now developed at the GitHub site (where ALL user issues should be posted, and latest versions can be found).**
+
+- **\*NEW in v1.71**: Support for (YouTube) HLS (HTTP Live Streaming)
+ video with the new "-hls" option.\* Click on the airplay icon in the
+ YouTube app to stream video. (You may need to wait until
+ advertisements have finished or been skipped before clicking the
+ YouTube airplay icon.) **Please report any issues with this new
+ feature of UxPlay**.
+
+## Highlights:
+
+- GPLv3, open source.
+- Originally supported only AirPlay Mirror protocol, now has added
+ support for AirPlay Audio-only (Apple Lossless ALAC) streaming from
+ current iOS/iPadOS clients. **Now with support for Airplay HLS
+ video-streaming (currently only YouTube video).**
+- macOS computers (2011 or later, both Intel and "Apple Silicon" M1/M2
+ systems) can act either as AirPlay clients, or as the server running
+ UxPlay. Using AirPlay, UxPlay can emulate a second display for macOS
+ clients.
+- Support for older iOS clients (such as 32-bit iPad 2nd gen., iPod
+ Touch 5th gen. and iPhone 4S, when upgraded to iOS 9.3.5, or later
+ 64-bit devices), plus a Windows AirPlay-client emulator, AirMyPC.
+- Uses GStreamer plugins for audio and video rendering (with options
+ to select different hardware-appropriate output "videosinks" and
+ "audiosinks", and a fully-user-configurable video streaming
+ pipeline).
+- Support for server behind a firewall.
+- Raspberry Pi support **both with and without hardware video
+ decoding** by the Broadcom GPU. _Tested on Raspberry Pi Zero 2 W, 3
+ Model B+, 4 Model B, and 5._
+- Support for running on Microsoft Windows (builds with the MinGW-64
+ compiler in the unix-like MSYS2 environment).
+
+Note: AirPlay2 multi-room audio streaming is not supported: use
+[shairport-sync](https://github.com/mikebrady/shairport-sync) for that.
+
+## Packaging status (Linux and \*BSD distributions)
+
+[](https://repology.org/project/uxplay/versions).
+
+- Install uxplay on Debian-based Linux systems with
+ "`sudo apt install uxplay`"; on FreeBSD with
+ "`sudo pkg install uxplay`". Also available on Arch-based systems
+ through AUR. Since v. 1.66, uxplay is now also packaged in RPM
+ format by Fedora 38 ("`sudo dnf install uxplay`").
+
+- For other RPM-based distributions which have not yet packaged
+ UxPlay, a RPM "specfile" **uxplay.spec** is now provided with recent
+ [releases](https://github.com/FDH2/UxPlay/releases) (see their
+ "Assets"), and can also be found in the UxPlay source top directory.
+ See the section on using this specfile for [building an installable
+ RPM package](#building-an-installable-rpm-package).
+
+After installation:
+
+- (On Linux and \*BSD): if a firewall is active on the server hosting
+ UxPlay, make sure the default network port (UDP 5353) for
+ mDNS/DNS-SD queries is open (see [Troubleshooting](#troubleshooting)
+ below for more details); also open three UDP and three TCP ports for
+ Uxplay, and use the "uxplay -p ``{=html}" option (see
+ "`man uxplay`" or "`uxplay -h`").
+
+- Even if you install your distribution's pre-compiled uxplay binary
+ package, you may need to read the instructions below for [running
+ UxPlay](#running-uxplay) to see which of your distribution's
+ **GStreamer plugin packages** you should also install.
+
+- For Audio-only mode (Apple Music, etc.) best quality is obtained
+ with the option "uxplay -async", but there is then a 2 second
+ latency imposed by iOS.
+
+- Add any UxPlay options you want to use as defaults to a startup file
+ `~/.uxplayrc` (see "`man uxplay`" or "`uxplay -h`" for format and
+ other possible locations). In particular, if your system uses
+ PipeWire audio or Wayland video systems, you may wish to add "as
+ pipewiresink" or "vs waylandsink" as defaults to the file. _(Output
+ from terminal commands "ps waux \| grep pulse" or "pactl info" will
+ contain "pipewire" if your Linux/BSD system uses it)._
+
+- On Raspberry Pi: models using hardware h264 video decoding by the
+ Broadcom GPU (models 4B and earlier) may require the uxplay option -bt709.
+ If you use Ubuntu 22.10 or earlier, GStreamer must
+ be [patched](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches)
+ to use hardware video decoding by the Broadcom GPU (also recommended
+ but optional for Raspberry Pi OS (Bullseye): the patched GStreamer
+ does not need option " -bt709`". The need for -bt709 when hardware video
+ decoding is used seems to have reappeared
+ starting with GStreamer-1.22.
+
+To (easily) compile the latest UxPlay from source, see the section
+[Getting UxPlay](#getting-uxplay).
+
+# Detailed description of UxPlay
+
+This project is a GPLv3 open source unix AirPlay2 Mirror server for
+Linux, macOS, and \*BSD. It was initially developed by
+[antimof](http://github.com/antimof/Uxplay) using code from
+OpenMAX-based [RPiPlay](https://github.com/FD-/RPiPlay), which in turn
+derives from [AirplayServer](https://github.com/KqsMea8/AirplayServer),
+[shairplay](https://github.com/juhovh/shairplay), and
+[playfair](https://github.com/EstebanKubata/playfair). (The antimof site
+is no longer involved in development, but periodically posts updates
+pulled from the new main [UxPlay site](https://github.com/FDH2/UxPlay)).
+
+UxPlay is tested on a number of systems, including (among others) Debian
+(10 "Buster", 11 "Bullseye", 12 "Bookworm"), Ubuntu (20.04 LTS, 22.04
+LTS, 23.04 (also Ubuntu derivatives Linux Mint, Pop!\_OS), Red Hat and
+clones (Fedora 38, Rocky Linux 9.2), openSUSE Leap 15.5, Mageia 9,
+OpenMandriva "ROME", PCLinuxOS, Arch Linux, Manjaro, and should run on
+any Linux system. Also tested on macOS Catalina and Ventura (Intel) and
+Sonoma (M2), FreeBSD 14.0, Windows 10 and 11 (64 bit).
+
+On Raspberry Pi 4 model B, it is tested on Raspberry Pi OS (Bullseye and
+Bookworm) (32- and 64-bit), Ubuntu 22.04 LTS and 23.04, Manjaro RPi4
+23.02, and (without hardware video decoding) on openSUSE 15.5. Also
+tested on Raspberry Pi Zero 2 W, 3 model B+, and now 5.
+
+Its main use is to act like an AppleTV for screen-mirroring (with audio)
+of iOS/iPadOS/macOS clients (iPhone, iPod Touch, iPad, Mac computers) on
+the server display of a host running Linux, macOS, or other unix (and
+now also Microsoft Windows). UxPlay supports Apple's AirPlay2 protocol
+using "Legacy Protocol", but some features are missing. (Details of what
+is publicly known about Apple's AirPlay 2 protocol can be found
+[here](https://openairplay.github.io/airplay-spec/),
+[here](https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol)
+and [here](https://emanuelecozzi.net/docs/airplay2); see also
+[pyatv](https://pyatv.dev/documentation/protocols) which could be a
+resource for adding modern protocols.) While there is no guarantee that
+future iOS releases will keep supporting "Legacy Protocol", iOS 17
+continues support.
+
+The UxPlay server and its client must be on the same local area network,
+on which a **Bonjour/Zeroconf mDNS/DNS-SD server** is also running (only
+DNS-SD "Service Discovery" service is strictly necessary, it is not
+necessary that the local network also be of the ".local" mDNS-based
+type). On Linux and BSD Unix servers, this is usually provided by
+[Avahi](https://www.avahi.org), through the avahi-daemon service, and is
+included in most Linux distributions (this service can also be provided
+by macOS, iOS or Windows servers).
+
+Connections to the UxPlay server by iOS/MacOS clients can be initiated
+both in **AirPlay Mirror** mode (which streams lossily-compressed AAC
+audio while mirroring the client screen, or in the alternative **AirPlay
+Audio** mode which streams Apple Lossless (ALAC) audio without screen
+mirroring. In **Audio** mode, metadata is displayed in the uxplay
+terminal; if UxPlay option `-ca ` is used, the accompanying cover
+art is also output to a periodically-updated file ``, and can be
+viewed with a (reloading) graphics viewer of your choice. _Switching
+between_ **Mirror** _and_ **Audio** _modes during an active connection
+is possible: in_ **Mirror** _mode, stop mirroring (or close the mirror
+window) and start an_ **Audio** _mode connection, switch back by
+initiating a_ **Mirror** _mode connection; cover-art display
+stops/restarts as you leave/re-enter_ **Audio** _mode._
+
+- **Note that Apple video-DRM (as found in "Apple TV app" content on
+ the client) cannot be decrypted by UxPlay, and the Apple TV app
+ cannot be watched using UxPlay's AirPlay Mirror mode (only the
+ unprotected audio will be streamed, in AAC format).**
+
+- **With the new "-hls" option, UxPlay now also supports non-Mirror
+ AirPlay video streaming (where the client controls a web server on
+ the AirPlay server that directly receives HLS content to avoid it
+ being decoded and re-encoded by the client). This currently only
+ supports streaming of YouTube videos. Without the -hls option, using
+ the icon for AirPlay video in apps such as the YouTube app will only
+ send audio (in lossless ALAC format) without the accompanying
+ video.**
+
+### Possibility for using hardware-accelerated h264/h265 video-decoding, if available.
+
+UxPlay uses [GStreamer](https://gstreamer.freedesktop.org) "plugins" for
+rendering audio and video. This means that video and audio are supported
+"out of the box", using a choice of plugins. AirPlay streams video in
+h264 format: gstreamer decoding is plugin agnostic, and uses accelerated
+GPU hardware h264 decoders if available; if not, software decoding is
+used.
+
+- **VAAPI for Intel and AMD integrated graphics, NVIDIA with "Nouveau"
+ open-source driver**
+
+ With an Intel or AMD GPU, hardware decoding with the open-source
+ VAAPI gstreamer plugin is preferable. The open-source "Nouveau"
+ drivers for NVIDIA graphics are also in principle supported: see
+ [here](https://nouveau.freedesktop.org/VideoAcceleration.html), but
+ this requires VAAPI to be supplemented with firmware extracted from
+ the proprietary NVIDIA drivers.
+
+- **NVIDIA with proprietary drivers**
+
+ The `nvh264dec` plugin (included in gstreamer1.0-plugins-bad since
+ GStreamer-1.18.0) can be used for accelerated video decoding on the
+ NVIDIA GPU after NVIDIA's CUDA driver `libcuda.so` is installed. For
+ GStreamer-1.16.3 or earlier, the plugin is called `nvdec`, and must
+ be [built by the
+ user](https://github.com/FDH2/UxPlay/wiki/NVIDIA-nvdec-and-nvenc-plugins).
+
+- **Video4Linux2 support for h264 hardware decoding on Raspberry Pi
+ (Pi 4B and older)**
+
+ Raspberry Pi (RPi) computers (tested on Pi 4 Model B) can now run
+ UxPlay using software video decoding, but hardware-accelerated
+ h264/h265 decoding by firmware in the Pi's Broadcom 2835 GPU is
+ prefered. UxPlay accesses this using the GStreamer-1.22 Video4Linux2
+ (v4l2) plugin; Uses the out-of-mainline Linux kernel module
+ bcm2835-codec maintained by Raspberry Pi, so far only included in
+ Raspberry Pi OS, and two other distributions (Ubuntu, Manjaro)
+ available with Raspberry Pi Imager. _(For GStreamer \< 1.22, see the
+ [UxPlay
+ Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches))_.
+ Pi model 5 has no support for hardware H264 decoding, as its CPU is
+ powerful enough for satisfactory software H264 decoding
+
+- **Support for h265 (HEVC) hardware decoding on Raspberry Pi (Pi 4
+ model B and Pi 5)**
+
+ These Raspberry Pi models have a dedicated HEVC decoding block (not
+ the GPU), with a driver "rpivid" which is not yet in the mainline
+ Linux kernel (but is planned to be there in future). Unfortunately
+ it produces decoded video in a non-standard pixel format (NC30 or
+ "SAND") which will not be supported by GStreamer until the driver is
+ in the mainline kernel; without this support, UxPlay support for
+ HEVC hardware decoding on Raspberry Pi will not work.
+
+### Note to packagers:
+
+UxPlay's GPLv3 license does not have an added "GPL exception" explicitly
+allowing it to be distributed in compiled form when linked to OpenSSL
+versions **prior to v. 3.0.0** (older versions of OpenSSL have a license
+clause incompatible with the GPL unless OpenSSL can be regarded as a
+"System Library", which it is in \*BSD). Many Linux distributions treat
+OpenSSL as a "System Library", but some (e.g. Debian) do not: in this
+case, the issue is solved by linking with OpenSSL-3.0.0 or later.
+
+# Getting UxPlay
+
+Either download and unzip
+[UxPlay-master.zip](https://github.com/FDH2/UxPlay/archive/refs/heads/master.zip),
+or (if git is installed): "git clone https://github.com/FDH2/UxPlay".
+You can also download a recent or earlier version listed in
+[Releases](https://github.com/FDH2/UxPlay/releases).
+
+- A recent UxPlay can also be found on the original [antimof
+ site](https://github.com/antimof/UxPlay); that original project is
+ inactive, but is usually kept current or almost-current with the
+ [active UxPlay github site](https://github.com/FDH2/UxPlay) (thank
+ you antimof!).
+
+## Building UxPlay on Linux (or \*BSD):
+
+### Debian-based systems:
+
+(Adapt these instructions for non-Debian-based Linuxes or \*BSD; for
+macOS, see specific instruction below). See
+[Troubleshooting](#troubleshooting) below for help with any
+difficulties.
+
+You need a C/C++ compiler (e.g. g++) with the standard development
+libraries installed. Debian-based systems provide a package
+"build-essential" for use in compiling software. You also need
+pkg-config: if it is not found by "`which pkg-config`", install
+pkg-config or its work-alike replacement pkgconf. Also make sure that
+cmake\>=3.10 is installed: "`sudo apt install cmake`" (add
+`build-essential` and `pkg-config` (or `pkgconf`) to this if needed).
+
+Make sure that your distribution provides OpenSSL 1.1.1 or later, and
+libplist 2.0 or later. (This means Debian 10 "Buster" based systems
+(e.g, Ubuntu 18.04) or newer; on Debian 10 systems "libplist" is an
+older version, you need "libplist3".) If it does not, you may need to
+build and install these from source (see instructions at the end of this
+README).
+
+If you have a non-standard OpenSSL installation, you may need to set the
+environment variable OPENSSL_ROOT_DIR (_e.g._ ,
+"`export OPENSSL_ROOT_DIR=/usr/local/lib64`" if that is where it is
+installed). Similarly, for non-standard (or multiple) GStreamer
+installations, set the environment variable GSTREAMER_ROOT_DIR to the
+directory that contains the ".../gstreamer-1.0/" directory of the
+gstreamer installation that UxPlay should use (if this is _e.g._
+"\~/my_gstreamer/lib/gstreamer-1.0/", set this location with
+"`export GSTREAMER_ROOT_DIR=$HOME/my_gstreamer/lib`").
+
+- Most users will use the GStreamer supplied by their distribution,
+ but a few (in particular users of Raspberry Pi OS Lite Legacy
+ (Buster) on a Raspberry Pi model 4B who wish to stay on that
+ unsupported Legacy OS for compatibility with other apps) should
+ instead build a newer Gstreamer from source following [these
+ instructions]()
+ . **Do this _before_ building UxPlay**.
+
+In a terminal window, change directories to the source directory of the
+downloaded source code ("UxPlay-\*", "\*" = "master" or the release tag
+for zipfile downloads, "UxPlay" for "git clone" downloads), then follow
+the instructions below:
+
+**Note:** By default UxPlay will be built with optimization for the
+computer it is built on; when this is not the case, as when you are
+packaging for a distribution, use the cmake option
+`-DNO_MARCH_NATIVE=ON`.
+
+If you use X11 Windows on Linux or \*BSD, and wish to toggle in/out of
+fullscreen mode with a keypress (F11 or Alt_L+Enter) UxPlay needs to be
+built with a dependence on X11. Starting with UxPlay-1.59, this will be
+done by default **IF** the X11 development libraries are installed and
+detected. Install these with "`sudo apt install libx11-dev`". If
+GStreamer \< 1.20 is detected, a fix needed by screen-sharing apps
+(_e.g._, Zoom) will also be made.
+
+- If X11 development libraries are present, but you wish to build
+ UxPlay _without_ any X11 dependence, use the cmake option
+ `-DNO_X11_DEPS=ON`.
+
+1. `sudo apt install libssl-dev libplist-dev`". (_unless you need to
+ build OpenSSL and libplist from source_).
+2. `sudo apt install libavahi-compat-libdnssd-dev`
+3. `sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev`.
+ (\*_Skip if you built Gstreamer from source_)
+4. `cmake .` (_For a cleaner build, which is useful if you modify the
+ source, replace this by_ "`mkdir build; cd build; cmake ..`": _you
+ can then delete the contents of the `build` directory if needed,
+ without affecting the source._) Also add any cmake "`-D`" options
+ here as needed (e.g, `-DNO_X11_DEPS=ON` or `-DNO_MARCH_NATIVE=ON`).
+5. `make`
+6. `sudo make install` (you can afterwards uninstall with
+ `sudo make uninstall` in the same directory in which this was run).
+
+This installs the executable file "`uxplay`" to `/usr/local/bin`, (and
+installs a manpage to somewhere standard like
+`/usr/local/share/man/man1` and README files to somewhere like
+`/usr/local/share/doc/uxplay`). (If "man uxplay" fails, check if
+\$MANPATH is set: if so, the path to the manpage (usually
+/usr/local/share/man/) needs to be added to \$MANPATH .) The uxplay
+executable can also be found in the build directory after the build
+process, if you wish to test before installing (in which case the
+GStreamer plugins must first be installed).
+
+### Building on non-Debian Linux and \*BSD
+
+\*\*For those with RPM-based distributions, a RPM spec file uxplay.spec
+is also available: see [Building an installable rpm
+package](#building-an-installable-rpm-package).
+
+- **Red Hat, or clones like CentOS (now continued as Rocky Linux or
+ Alma Linux):** (sudo dnf install, or sudo yum install) openssl-devel
+ libplist-devel avahi-compat-libdns_sd-devel gstreamer1-devel
+ gstreamer1-plugins-base-devel (+libX11-devel for fullscreen X11)
+ _(some of these may be in the "CodeReady" add-on repository, called
+ "PowerTools" by clones)_
+
+- **Mageia, PCLinuxOS, OpenMandriva:** Same as Red Hat, except for
+ name changes: (Mageia) "gstreamer1.0-devel",
+ "gstreamer-plugins-base1.0-devel"; (OpenMandriva)
+ "libopenssl-devel", "gstreamer-devel",
+ "libgst-plugins-base1.0-devel". PCLinuxOS: same as Mageia, but uses
+ synaptic (or apt) as its package manager.
+
+- **openSUSE:** (sudo zypper install) libopenssl-3-devel (formerly
+ libopenssl-devel) libplist-2_0-devel (formerly libplist-devel)
+ avahi-compat-mDNSResponder-devel gstreamer-devel
+ gstreamer-plugins-base-devel (+ libX11-devel for fullscreen X11).
+
+- **Arch Linux** (_Also available as a package in AUR_): (sudo pacman
+ -Syu) openssl libplist avahi gst-plugins-base.
+
+- **FreeBSD:** (sudo pkg install) libplist gstreamer1. Either
+ avahi-libdns or mDNSResponder must also be installed to provide the
+ dns_sd library. OpenSSL is already installed as a System Library.
+
+#### Building an installable RPM package
+
+First-time RPM builders should first install the rpm-build and
+rpmdevtools packages, then create the rpmbuild tree with
+"`rpmdev-setuptree`". Then download and copy uxplay.spec into
+`~/rpmbuild/SPECS`. In that directory, run
+"`rpmdev-spectool -g -R uxplay.spec`" to download the corresponding
+source file `uxplay-*.tar.gz` into `~/rpmbuild/SOURCES`
+("rpmdev-spectool" may also be just called "spectool"); then run
+"`rpmbuild -ba uxplay.spec`" (you will need to install any required
+dependencies this reports). This should create the uxplay RPM package in
+a subdirectory of `~/rpmbuild/RPMS`. (**uxplay.spec** is tested on
+Fedora 38, Rocky Linux 9.2, openSUSE Leap 15.5, Mageia 9, OpenMandriva,
+PCLinuxOS; it can be easily modified to include dependency lists for
+other RPM-based distributions.)
+
+## Running UxPlay
+
+### Installing plugins (Debian-based Linux distributions, including Ubuntu and Raspberry Pi OS) (_skip if you built a complete GStreamer from source_)
+
+Next install the GStreamer plugins that are needed with
+`sudo apt install gstreamer1.0-`. Values of `` required
+are:
+
+1. "**plugins-base**"
+2. "**libav**" (for sound),
+3. "**plugins-good**" (for v4l2 hardware h264 decoding)
+4. "**plugins-bad**" (for h264 decoding).
+
+**Debian-based distributions split some of the plugin packages into
+smaller pieces:** some that may also be needed include "**gl**" for
+OpenGL support (this provides the "-vs glimagesink" videosink, which can
+be very useful in many systems (including Raspberry Pi), and should
+always be used when using h264/h265 decoding by a NVIDIA GPU),
+"**gtk3**" (which provides the "-vs gtksink" videosink), and "**x**" for
+X11 support, although these may already be installed; "**vaapi**" is
+needed for hardware-accelerated h264 video decoding by Intel or AMD
+graphics (but not for use with NVIDIA using proprietary drivers). If
+sound is not working, "**alsa**"","**pulseaudio**", or "**pipewire**"
+plugins may need to be installed, depending on how your audio is set up.
+
+- Also install "**gstreamer1.0-tools**" to get the utility
+ gst-inspect-1.0 for examining the GStreamer installation.
+
+### Installing plugins (Non-Debian-based Linux or \*BSD) (_skip if you built a complete GStreamer from source_)
+
+In some cases, because of patent issues, the libav plugin feature
+**avdec_aac** needed for decoding AAC audio in mirror mode is not
+provided in the official distribution: get it from community
+repositories for those distributions.
+
+- **Red Hat, or clones like CentOS (now continued as Rocky Linux or
+ Alma Linux):** Install gstreamer1-libav gstreamer1-plugins-bad-free
+ (+ gstreamer1-vaapi for Intel/AMD graphics). In recent Fedora,
+ gstreamer1-libav is renamed gstreamer1-plugin-libav. **To get
+ avdec_aac, install packages from
+ [rpmfusion.org](https://rpmfusion.org)**: (get ffmpeg-libs from
+ rpmfusion; on RHEL or clones, but not recent Fedora, also get
+ gstreamer1-libav from there).
+
+- **Mageia, PCLinuxOS, OpenMandriva:** Install gstreamer1.0-libav
+ gstreamer1.0-plugins-bad (+ gstreamer1.0-vaapi for Intel/AMD
+ graphics). **On Mageia, to get avdec_aac, install ffmpeg from the
+ "tainted" repository**, (which also provides a more complete
+ gstreamer1.0-plugins-bad).
+
+- **openSUSE:** Install gstreamer-plugins-libav gstreamer-plugins-bad
+ (+ gstreamer-plugins-vaapi for Intel/AMD graphics). **To get
+ avdec_aac, install libav\* packages for openSUSE from
+ [Packman](https://ftp.gwdg.de/pub/linux/misc/packman/suse/)
+ "Essentials"**; recommendation: after adding the Packman repository,
+ use the option in YaST Software management to switch all system
+ packages for multimedia to Packman).
+
+- **Arch Linux** Install gst-plugins-good gst-plugins-bad gst-libav (+
+ gstreamer-vaapi for Intel/AMD graphics).
+
+- **FreeBSD:** Install gstreamer1-libav, gstreamer1-plugins,
+ gstreamer1-plugins-\* (\* = core, good, bad, x, gtk, gl, vulkan,
+ pulse, v4l2, ...), (+ gstreamer1-vaapi for Intel/AMD graphics).
+
+### Starting and running UxPlay
+
+Since UxPlay-1.64, UxPlay can be started with options read from a
+configuration file, which will be the first found of (1) a file with a
+path given by environment variable `$UXPLAYRC`, (2) `~/.uxplayrc` in the
+user's home directory ("\~"), (3) `~/.config/uxplayrc`. The format is
+one option per line, omitting the initial `"-"` of the command-line
+option. Lines in the configuration file beginning with `"#"` are treated
+as comments and ignored.
+
+**Run uxplay in a terminal window**. On some systems, you can specify
+fullscreen mode with the `-fs` option, or toggle into and out of
+fullscreen mode with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C
+(or close the window) to terminate it when done. If the UxPlay server is
+not seen by the iOS client's drop-down "Screen Mirroring" panel, check
+that your DNS-SD server (usually avahi-daemon) is running: do this in a
+terminal window with `systemctl status avahi-daemon`. If this shows the
+avahi-daemon is not running, control it with
+`sudo systemctl [start,stop,enable,disable] avahi-daemon` (on
+non-systemd systems, such as \*BSD, use
+`sudo service avahi-daemon [status, start, stop, restart, ...]`). If
+UxPlay is seen, but the client fails to connect when it is selected,
+there may be a firewall on the server that prevents UxPlay from
+receiving client connection requests unless some network ports are
+opened: **if a firewall is active, also open UDP port 5353 (for mDNS
+queries) needed by Avahi**. See [Troubleshooting](#troubleshooting)
+below for help with this or other problems.
+
+- Unlike an Apple TV, the UxPlay server does not by default require
+ clients to initially "pair" with it using a pin code displayed by
+ the server (after which the client "trusts" the server, and does not
+ need to repeat this). Since v1.67, Uxplay offers such
+ "pin-authentication" as an option: see "`-pin`" and "`-reg`" in
+ [Usage](#usage) for details, if you wish to use it. _Some clients
+ with MDM (Mobile Device Management, often present on employer-owned
+ devices) are required to use pin-authentication: UxPlay will provide
+ this even when running without the pin option._
+
+- By default, UxPlay is locked to its current client until that client
+ drops the connection; since UxPlay-1.58, the option `-nohold`
+ modifies this behavior so that when a new client requests a
+ connection, it removes the current client and takes over. UxPlay
+ 1.66 introduces a mechanism ( `-restrict`, `-allow `,
+ `-block `) to control which clients are allowed to connect,
+ using their "deviceID" (which in Apple devices appears to be
+ immutable).
+
+- In Mirror mode, GStreamer has a choice of **two** methods to play
+ video with its accompanying audio: prior to UxPlay-1.64, the video
+ and audio streams were both played as soon as possible after they
+ arrived (the GStreamer "_sync=false_" method), with a GStreamer
+ internal clock used to try to keep them synchronized. **Starting
+ with UxPlay-1.64, the other method (GStreamer's "_sync=true_" mode),
+ which uses timestamps in the audio and video streams sent by the
+ client, is the new default**. On low-decoding-power UxPlay hosts
+ (such as Raspberry Pi Zero W or 3 B+ models) this will drop video
+ frames that cannot be decoded in time to play with the audio, making
+ the video jerky, but still synchronized.
+
+The older method which does not drop late video frames worked well on
+more powerful systems, and is still available with the UxPlay option
+"`-vsync no`"; this method is adapted to "live streaming", and may be
+better when using UxPlay as a second monitor for a Mac computer, for
+example, while the new default timestamp-based method is best for
+watching a video, to keep lip movements and voices synchronized.
+(Without use of timestamps, video will eventually lag behind audio if it
+cannot be decoded fast enough: hardware-accelerated video-decoding
+helped to prevent this previously when timestamps were not being used.)
+
+- In Audio-only mode the GStreamer "sync=false" mode (not using
+ timestamps) is still the default, but if you want to keep the audio
+ playing on the server synchronized with the video showing on the
+ client, use the `-async` timestamp-based option. (An example might
+ be if you want to follow the Apple Music lyrics on the client while
+ listening to superior sound on the UxPlay server). This delays the
+ video on the client to match audio on the server, so leads to a
+ slight delay before a pause or track-change initiated on the client
+ takes effect on the audio played by the server.
+
+AirPlay volume-control attenuates volume (gain) by up to -30dB: the
+decibel range -30:0 can be rescaled from _Low_:0, or _Low_:_High_, using
+the option `-db` ("-db _Low_" or "-db _Low_:_High_"), _Low_ must be
+negative. Rescaling is linear in decibels. Note that GStreamer's audio
+format will "clip" any audio gain above +20db, so keep _High_ below that
+level. The option `-taper` provides a "tapered" AirPlay volume-control
+profile some users may prefer.
+
+The -vsync and -async options also allow an optional positive (or
+negative) audio-delay adjustment in _milliseconds_ for fine-tuning :
+`-vsync 20.5` delays audio relative to video by 0.0205 secs; a negative
+value advances it.)
+
+- you may find video is improved by the setting -fps 60 that allows
+ some video to be played at 60 frames per second. (You can see what
+ framerate is actually streaming by using -vs fpsdisplaysink, and/or
+ -FPSdata.) When using this, you should use the default
+ timestamp-based synchronization option `-vsync`.
+
+- Since UxPlay-1.54, you can display the accompanying "Cover Art" from
+ sources like Apple Music in Audio-Only (ALAC) mode: run
+ "`uxplay -ca &`" in the background, then run a image viewer
+ with an autoreload feature: an example is "feh": run
+ "`feh -R 1 `" in the foreground; terminate feh and then Uxplay
+ with "`ctrl-C fg ctrl-C`".
+
+By default, GStreamer uses an algorithm to search for the best
+"videosink" (GStreamer's term for a graphics driver to display images)
+to use. You can overide this with the uxplay option `-vs `.
+Which videosinks are available depends on your operating system and
+graphics hardware: use
+"`gst-inspect-1.0 | grep sink | grep -e video -e Video -e image`" to see
+what is available. Some possibilites on Linux/\*BSD are:
+
+- **glimagesink** (OpenGL), **waylandsink**
+
+- **xvimagesink**, **ximagesink** (X11)
+
+- **kmssink**, **fbdevsink** (console graphics without X11)
+
+- **vaapisink** (for Intel/AMD hardware-accelerated graphics); for
+ NVIDIA hardware graphics (with CUDA) use **glimagesink** combined
+ with "`-vd nvh264dec`" (or "nvh264sldec", a new variant which will
+ become "nvh264dec" in GStreamer-1.24).
+
+- If the server is "headless" (no attached monitor, renders audio
+ only) use `-vs 0`.
+
+Note that videosink options can set using quoted arguments to -vs:
+_e.g._, `-vs "xvimagesink display=:0"`: ximagesink and xvimagesink allow
+an X11 display name to be specified, and waylandsink has a similar option.
+Videosink options ("properties") can be found in their GStreamer description pages,such as
+https://gstreamer.freedesktop.org/documentation/xvimagesink .
+
+GStreamer also searches for the best "audiosink"; override its choice
+with `-as `. Choices on Linux include pulsesink, alsasink,
+pipewiresink, oss4sink; see what is available with
+`gst-inspect-1.0 | grep sink | grep -e audio -e Audio`.
+
+**One common problem involves GStreamer attempting to use
+incorrectly-configured or absent accelerated hardware h264 video
+decoding (e.g., VAAPI). Try "`uxplay -avdec`" to force software video
+decoding; if this works you can then try to fix accelerated hardware
+video decoding if you need it, or just uninstall the GStreamer vaapi
+plugin.**
+
+See [Usage](#usage) for more run-time options.
+
+### **Special instructions for Raspberry Pi (tested on Raspberry Pi Zero 2 W, 3 Model B+, 4 Model B, and 5 only)**:
+
+- For Framebuffer video (for Raspberry Pi OS "Lite" and other non-X11
+ distributions) use the KMS videosink "-vs kmssink" (the DirectFB
+ framebuffer videosink "dfbvideosink" is broken on the Pi, and
+ segfaults). _In this case you should explicitly use the "-vs
+ kmssink" option, as without it, autovideosink does not find the
+ correct videosink._
+
+- Raspberry Pi 5 does not provide hardware H264 decoding (and does not
+ need it).
+
+- Pi Zero 2 W, 3 Model B+ and 4 Model B should use hardware H264
+ decoding by the Broadcom GPU, but it requires an out-of-mainstream
+ kernel module bcm2835_codec maintained in the [Raspberry Pi kernel
+ tree](https://github.com/raspberrypi/linux); distributions that are
+ known to supply it include Raspberry Pi OS, Ubuntu, and
+ Manjaro-RPi4. Use software decoding (option -avdec) if this module
+ is not available.
+
+- Uxplay uses the Video4Linux2 (v4l2) plugin from GStreamer-1.22 and
+ later to access the GPU, if hardware H264 decoding is used. This
+ should happen automatically. The option -v4l2 can be used, but it is
+ usually best to just let GStreamer find the best video pipeline by
+ itself.
+
+- On older distributions (GStreamer \< 1.22), the v4l2 plugin needs a
+ patch: see the [UxPlay
+ Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches).
+ Legacy Raspberry Pi OS (Bullseye) has a partially-patched
+ GStreamer-1.18.4 which needs the uxplay option -bt709 (and don't use
+ -v4l2); it is still better to apply the full patch from the UxPlay
+ Wiki in this case.
+
+- **It appears that when hardware h264 video decoding is used, the option
+ -bt709 became needed again in GStreamer-1.22 and later.**
+
+- For "double-legacy" Raspberry Pi OS (Buster), there is no patch for
+ GStreamer-1.14. Instead, first build a complete newer
+ GStreamer-1.18.6 from source using [these
+ instructions]()
+ before building UxPlay.
+
+- Raspberry Pi 3 Model B+ running a 32 bit OS can also access the GPU
+ with the GStreamer OMX plugin (use option "`-vd omxh264dec`"), but
+ this is broken by Pi 4 Model B firmware. OMX support was removed
+ from Raspberry Pi OS (Bullseye), but is present in Buster.
+
+- **H265 (4K)** video is potentially supported by hardware decoding on
+ Raspberry Pi 5 models, as well as on Raspberry Pi 4 model B, using a
+ dedicated HEVC decoding block, but the "rpivid" kernel driver for
+ this is not yet supported by GStreamer (this driver decodes video
+ into a non-standard format that cannot be supported by GStreamer
+ until the driver is in the mainline Linux kernel). Raspberry Pi
+ provides a version of ffmpeg that can use that format, but at
+ present UxPlay cannot use this. The best solution would be for the
+ driver to be "upstreamed" to the kernel, allowing GStreamer support.
+ (Software HEVC decoding works, but does not seem to give
+ satisfactory results on the Pi).
+
+Even with GPU video decoding, some frames may be dropped by the
+lower-power models to keep audio and video synchronized using
+timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config
+"Performance Options" allows specifying how much memory to allocate to
+the GPU, but this setting appears to be absent in Bookworm (but it can
+still be set to e.g. 128MB by adding a line "gpu_mem=128" in
+/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well
+when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to
+the GPU (default seems to be 64MB).
+
+The basic uxplay options for R Pi are `uxplay [-vs ]`. The
+choice `` = `glimagesink` is sometimes useful. With the
+Wayland video compositor, use `` = `waylandsink`. With
+framebuffer video, use `` = `kmssink`.
+
+- Tip: to start UxPlay on a remote host (such as a Raspberry Pi) using
+ ssh:
+
+```{=html}
+
+```
+
+ ssh user@remote_host
+ export DISPLAY=:0
+ nohup uxplay [options] > FILE &
+
+Sound and video will play on the remote host; "nohup" will keep uxplay
+running if the ssh session is closed. Terminal output is saved to FILE
+(which can be /dev/null to discard it)
+
+## Building UxPlay on macOS: **(Intel X86_64 and "Apple Silicon" M1/M2 Macs)**
+
+_Note: A native AirPlay Server feature is included in macOS 12 Monterey,
+but is restricted to recent hardware. UxPlay can run on older macOS
+systems that will not be able to run Monterey, or can run Monterey but
+not AirPlay._
+
+These instructions for macOS assume that the Xcode command-line
+developer tools are installed (if Xcode is installed, open the Terminal,
+type "sudo xcode-select --install" and accept the conditions).
+
+It is also assumed that CMake \>= 3.13 is installed: this can be done
+with package managers [MacPorts](http://www.macports.org)
+(`sudo port install cmake`), [Homebrew](http://brew.sh)
+(`brew install cmake`), or by a download from
+. Also install `git` if you will use it to
+fetch UxPlay.
+
+Next install libplist and openssl-3.x. Note that static versions of
+these libraries will be used in the macOS builds, so they can be
+uninstalled after building uxplay, if you wish.
+
+- If you use Homebrew: `brew install libplist openssl@3`
+
+- if you use MacPorts: `sudo port install libplist-devel openssl3`
+
+Otherwise, build libplist and openssl from source: see instructions near
+the end of this README; requires development tools (autoconf, automake,
+libtool, _etc._) to be installed.
+
+Next get the latest macOS release of GStreamer-1.0.
+
+**Using "Official" GStreamer (Recommended for both MacPorts and Homebrew
+users)**: install the GStreamer release for macOS from
+. (This release contains
+its own pkg-config, so you don't have to install one.) Install both the
+gstreamer-1.0 and gstreamer-1.0-devel packages. After downloading,
+Shift-Click on them to install (they install to
+/Library/FrameWorks/GStreamer.framework). Homebrew or MacPorts users
+should **not** install (or should uninstall) the GStreamer supplied by
+their package manager, if they use the "official" release.
+
+- Since GStreamer v1.22, the "Official" (gstreamer.freedesktop.org)
+ macOS binaries require a wrapper "gst_macos_main" around the actual
+ main program (uxplay). This should have been applied during the
+ UxPlay compilation process, and the initial UxPlay terminal message
+ should confirm it is being used. (UxPlay can also be built using
+ "Official" GStreamer v.1.20.7 binaries, which work without the
+ wrapper.)
+
+**Using Homebrew's GStreamer**: pkg-config is needed: ("brew install
+pkg-config gstreamer"). This causes a large number of extra packages to
+be installed by Homebrew as dependencies. The [Homebrew gstreamer
+installation](https://formulae.brew.sh/formula/gstreamer#default) has
+recently been reworked into a single "formula" named `gstreamer`, which
+now works without needing GST_PLUGIN_PATH to be set in the enviroment.
+Homebrew installs gstreamer to `HOMEBREW_PREFIX/lib/gstreamer-1.0` where
+by default `HOMEBREW_PREFIX/*` is `/opt/homebrew/*` on Apple Silicon
+Macs, and `/usr/local/*` on Intel Macs; do not put any extra
+non-Homebrew plugins (that you build yourself) there, and instead set
+GST_PLUGIN_PATH to point to their location (Homebrew does not supply a
+complete GStreamer, but seems to have everything needed for UxPlay).
+**New: the UxPlay build script will now also detect Homebrew
+installations in non-standard locations indicated by the environment
+variable `$HOMEBREW_PREFIX`.**
+
+**Using GStreamer installed from MacPorts**: this is **not**
+recommended, as currently the MacPorts GStreamer is old (v1.16.2),
+unmaintained, and built to use X11:
+
+- Instead [build gstreamer
+ yourself](https://github.com/FDH2/UxPlay/wiki/Building-GStreamer-from-Source-on-macOS-with-MacPorts)
+ if you use MacPorts and do not want to use the "Official" Gstreamer
+ binaries.
+
+_(If you really wish to use the MacPorts GStreamer-1.16.2, install
+pkgconf ("sudo port install pkgconf"), then "sudo port install
+gstreamer1-gst-plugins-base gstreamer1-gst-plugins-good
+gstreamer1-gst-plugins-bad gstreamer1-gst-libav". For X11 support on
+macOS, compile UxPlay using a special cmake option `-DUSE_X11=ON`, and
+run it from an XQuartz terminal with -vs ximagesink; older non-retina
+macs require a lower resolution when using X11: `uxplay -s 800x600`.)_
+
+After installing GStreamer, build and install uxplay: open a terminal
+and change into the UxPlay source directory ("UxPlay-master" for zipfile
+downloads, "UxPlay" for "git clone" downloads) and build/install with
+"cmake . ; make ; sudo make install" (same as for Linux).
+
+- Running UxPlay while checking for GStreamer warnings (do this with
+ "export GST_DEBUG=2" before runnng UxPlay) reveals that with the
+ default (since UxPlay 1.64) use of timestamps for video
+ synchonization, many video frames are being dropped (only on macOS),
+ perhaps due to another error (about videometa) that shows up in the
+ GStreamer warnings. **Recommendation: use the new UxPlay "no
+ timestamp" option "`-vsync no`"** (you can add a line "vsync no" in
+ the uxplayrc configuration file).
+
+- On macOS with this installation of GStreamer, the only videosinks
+ available seem to be glimagesink (default choice made by
+ autovideosink) and osxvideosink. The window title does not show the
+ Airplay server name, but the window is visible to screen-sharing
+ apps (e.g., Zoom). The only available audiosink seems to be
+ osxaudiosink.
+
+- The option -nc is always used, whether or not it is selected. This
+ is a workaround for a problem with GStreamer videosinks on macOS: if
+ the GStreamer pipeline is destroyed while the mirror window is still
+ open, a segfault occurs.
+
+- In the case of glimagesink, the resolution settings "-s wxh" do not
+ affect the (small) initial OpenGL mirror window size, but the window
+ can be expanded using the mouse or trackpad. In contrast, a window
+ created with "-vs osxvideosink" is initially big, but has the wrong
+ aspect ratio (stretched image); in this case the aspect ratio
+ changes when the window width is changed by dragging its side; the
+ option `-vs "osxvideosink force-aspect-ratio=true"` can be used to
+ make the window have the correct aspect ratio when it first opens.
+
+## Building UxPlay on Microsoft Windows, using MSYS2 with the MinGW-64 compiler.
+
+- tested on Windows 10 and 11, 64-bit.
+
+1. Download and install **Bonjour SDK for Windows v3.0**. You can
+ download the SDK without any registration at
+ [softpedia.com](https://www.softpedia.com/get/Programming/SDK-DDK/Bonjour-SDK.shtml),
+ or get it from the official Apple site
+ [https://developer.apple.com/download](https://developer.apple.com/download/all/?q=Bonjour%20SDK%20for%20Windows)
+ (Apple makes you register as a developer to access it from their
+ site). This should install the Bonjour SDK as
+ `C:\Program Files\Bonjour SDK`.
+
+2. (This is for 64-bit Windows; a build for 32-bit Windows should be
+ possible, but is not tested.) The unix-like MSYS2 build environment
+ will be used: download and install MSYS2 from the official site
+ [https://www.msys2.org/](https://www.msys2.org). Accept the default
+ installation location `C:\mysys64`.
+
+3. [MSYS2 packages](https://packages.msys2.org/package/) are installed
+ with a variant of the "pacman" package manager used by Arch Linux.
+ Open a "MSYS2 MINGW64" terminal from the MSYS2 tab in the Windows
+ Start menu, and update the new MSYS2 installation with "pacman
+ -Syu". Then install the **MinGW-64** compiler and **cmake**
+
+ pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc
+
+ The compiler with all required dependencies will be installed in the
+ msys64 directory, with default path `C:/msys64/mingw64`. Here we
+ will simply build UxPlay from the command line in the MSYS2
+ environment (this uses "`ninja`" in place of "`make`" for the build
+ system).
+
+4. Download the latest UxPlay from github **(to use `git`, install it
+ with `pacman -S git`, then
+ "`git clone https://github.com/FDH2/UxPlay`")**, then install UxPlay
+ dependencies (openssl is already installed with MSYS2):
+
+ `pacman -S mingw-w64-x86_64-libplist mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base`
+
+ If you are trying a different Windows build system, MSVC versions of
+ GStreamer for Windows are available from the [official GStreamer
+ site](https://gstreamer.freedesktop.org/download/), but only the
+ MinGW 64-bit build on MSYS2 has been tested.
+
+5. cd to the UxPlay source directory, then "`mkdir build`" and
+ "`cd build`". The build process assumes that the Bonjour SDK is
+ installed at `C:\Program Files\Bonjour SDK`. If it is somewhere
+ else, set the enviroment variable BONJOUR_SDK_HOME to point to its
+ location. Then build UxPlay with
+
+ `cmake ..`
+
+ `ninja`
+
+6. Assuming no error in either of these, you will have built the uxplay
+ executable **uxplay.exe** in the current ("build") directory. The
+ "sudo make install" and "sudo make uninstall" features offered in
+ the other builds are not available on Windows; instead, the MSYS2
+ environment has `/mingw64/...` available, and you can install the
+ uxplay.exe executable in `C:/msys64/mingw64/bin` (plus manpage and
+ documentation in `C:/msys64/mingw64/share/...`) with
+
+ `cmake --install . --prefix /mingw64`
+
+ To be able to view the manpage, you need to install the manpage
+ viewer with "`pacman -S man`".
+
+To run **uxplay.exe** you need to install some gstreamer plugin packages
+with `pacman -S mingw-w64-x86_64-gst-`, where the required ones
+have `` given by
+
+1. **libav**
+2. **plugins-good**
+3. **plugins-bad**
+
+Other possible MSYS2 gstreamer plugin packages you might use are listed
+in [MSYS2 packages](https://packages.msys2.org/package/).
+
+You also will need to grant permission to the uxplay executable
+uxplay.exe to access data through the Windows firewall. You may
+automatically be offered the choice to do this when you first run
+uxplay, or you may need to do it using **Windows Settings-\>Update and
+Security-\>Windows Security-\>Firewall & network protection -\> allow an
+app through firewall**. If your virus protection flags uxplay.exe as
+"suspicious" (but without a true malware signature) you may need to give
+it an exception.
+
+Now test by running "`uxplay`" (in a MSYS2 terminal window). If you need
+to specify the audiosink, there are two main choices on Windows: the
+older DirectSound plugin "`-as directsoundsink`", and the more modern
+Windows Audio Session API (wasapi) plugin "`-as wasapisink`", which
+supports [additional
+options](https://gstreamer.freedesktop.org/documentation/wasapi/wasapisink.html)
+such as
+
+ uxplay -as 'wasapisink device=\"\"'
+
+where `` specifies an available audio device by its GUID, which
+can be found using "`gst-device-monitor-1.0 Audio`": `` has a form
+like `\{0.0.0.00000000\}.\{98e35b2b-8eba-412e-b840-fd2c2492cf44\}`. If
+"`device`" is not specified, the default audio device is used.
+
+If you wish to specify the videosink using the `-vs ` option,
+some choices for `` are `d3d11videosink`, `d3dvideosink`,
+`glimagesink`, `gtksink`.
+
+- With Direct3D 11.0 or greater, you can either always be in
+ fullscreen mode using option
+ `-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true"`,
+ or get the ability to toggle into and out of fullscreen mode using
+ the Alt-Enter key combination with option
+ `-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`. For
+ convenience, these options will be added if just
+ `-vs d3d11videosink` with or without the fullscreen option "-fs" is
+ used. _(Windows users may wish to add "`vs d3d11videosink`" (no
+ initial "`-`") to the UxPlay startup options file; see "man uxplay"
+ or "uxplay -h".)_
+
+The executable uxplay.exe can also be run without the MSYS2 environment,
+in the Windows Terminal, with `C:\msys64\mingw64\bin\uxplay`.
+
+# Usage
+
+Options:
+
+- These can also be written (one option per line, without the initial
+ "`-`" character) in the UxPlay startup file (either given by
+ environment variable `$UXPLAYRC`, or `~/.uxplayrc` or
+ `~/.config/uxplayrc`); lines begining with "`#`" are treated as
+ comments, and ignored. Command line options supersede options in the
+ startup file.
+
+**-n server_name** (Default: UxPlay); server_name@\_hostname\_ will be
+the name that appears offering AirPlay services to your iPad, iPhone
+etc, where _hostname_ is the name of the server running uxplay. This
+will also now be the name shown above the mirror display (X11) window.
+
+**-nh** Do not append "@_hostname_" at the end of the AirPlay server
+name.
+
+**-h265** Activate "ScreenMultiCodec" support (AirPlay "Features" bit 42) for accepting h265 (4K/HEVC) video in addition to h264 video (1080p)
+in screen-mirror mode. When this option is used, two "video pipelines"
+(one for h264, one for h265) are created. If any GStreamer plugins in
+the pipeline are specific for h264 or h265, the correct version will be
+used in each pipeline. A wired Client-Server ethernet connection is
+preferred over Wifi for 4K video, and might be required by the client.
+Only recent Apple devices (M1/M2 Macs or iPads, and some iPhones) can
+send h265 video if a resolution "-s wxh" with h \> 1080 is requested.
+The "-h265" option changes the default resolution ("-s" option) from
+1920x1080 to 3840x2160, and leaves default maximum framerate ("-fps"
+option) at 30fps.
+
+**-hls** Activate HTTP Live Streaming support. With this option YouTube
+videos can be streamed directly from YouTube servers to UxPlay (without
+passing through the client) by clicking on the AirPlay icon in the
+YouTube app.
+
+**-pin \[nnnn\]**: (since v1.67) use Apple-style (one-time) "pin"
+authentication when a new client connects for the first time: a
+four-digit pin code is displayed on the terminal, and the client screen
+shows a login prompt for this to be entered. When "-pin" is used by
+itself, a new random pin code is chosen for each authentication; if
+"-pin nnnn" (e.g., "-pin 3939") is used, this will set an unchanging
+fixed code. Authentication adds the server to the client's list of
+"trusted servers" and the client will not need to reauthenticate
+provided that the client and server public keys remain unchanged. (By
+default since v1.68, the server public key is generated from the MAC
+address, which can be changed with the -m option; see the -key option
+for an alternative method of key generation). _(Add a line "pin" in the
+UxPlay startup file if you wish the UxPlay server to use the pin
+authentication protocol)._
+
+**-reg \[_filename_\]**: (since v1.68). If "-pin" is used, this option
+maintains a register of pin-authenticated "trusted clients" in
+\$HOME/.uxplay.register (or optionally, in _filename_). Without this
+option, returning clients that skip pin-authentication are trusted and
+not checked. This option may be useful if UxPlay is used in a more
+public environment, to record client details; the register is text, one
+line per client, with client's public key (base-64 format), Device ID,
+and Device name; commenting out (with "\#") or deleting a line
+deregisters the corresponding client (see options -restrict, -block,
+-allow for more ways to control client access). _(Add a line "reg" in
+the startup file if you wish to use this feature.)_
+
+**-vsync \[x\]** (In Mirror mode:) this option (**now the default**)
+uses timestamps to synchronize audio with video on the server, with an
+optional audio delay in (decimal) milliseconds (_x_ = "20.5" means
+0.0205 seconds delay: positive or negative delays less than a second are
+allowed.) It is needed on low-power systems such as Raspberry Pi without
+hardware video decoding.
+
+**-vsync no** (In Mirror mode:) this switches off timestamp-based
+audio-video synchronization, restoring the default behavior prior to
+UxPlay-1.64. Standard desktop systems seem to work well without use of
+timestamps: this mode is appropriate for "live streaming" such as using
+UxPlay as a second monitor for a mac computer, or monitoring a webcam;
+with it, no video frames are dropped.
+
+**-async \[x\]** (In Audio-Only (ALAC) mode:) this option uses
+timestamps to synchronize audio on the server with video on the client,
+with an optional audio delay in (decimal) milliseconds (_x_ = "20.5"
+means 0.0205 seconds delay: positive or negative delays less than a
+second are allowed.) Because the client adds a video delay to account
+for latency, the server in -async mode adds an equivalent audio delay,
+which means that audio changes such as a pause or a track-change will
+not take effect immediately. _This might in principle be mitigated by
+using the `-al` audio latency setting to change the latency (default
+0.25 secs) that the server reports to the client, but at present
+changing this does not seem to have any effect_.
+
+**-async no**. This is the still the default behavior in Audio-only
+mode, but this option may be useful as a command-line option to switch
+off a `-async` option set in a "uxplayrc" configuration file.
+
+**-db _low_\[:_high_\]** Rescales the AirPlay volume-control attenuation
+(gain) from -30dB:0dB to _low_:0dB or _low_:_high_. The lower limit
+_low_ must be negative (attenuation); the upper limit _high_ can be
+either sign. (GStreamer restricts volume-augmentation by _high_ so that
+it cannot exceed +20dB). The rescaling is "flat", so that for -db
+-50:10, a change in Airplay attenuation by -7dB is translated to a -7 x
+(60/30) = -14dB attenuation, and the maximum volume (AirPlay 0dB) is a
+10dB augmentation, and Airplay -30dB would become -50dB. Note that the
+minimum AirPlay value (-30dB exactly) is translated to "mute".
+
+**-taper** Provides a "tapered" Airplay volume-control profile (matching
+the one called "dasl-tapering" in
+[shairport-sync](https://github.com/mikebrady/shairport-sync)): each
+time the length of the volume slider (or the number of steps above mute,
+where 16 steps = full volume) is reduced by 50%, the perceived volume is
+halved (a 10dB attenuation). (This is modified at low volumes, to use
+the "untapered" volume if it is louder.)
+
+**-s wxh** e.g. -s 1920x1080 (= "1080p"), the default width and height
+resolutions in pixels for h264 video. (The default becomes 3840x2160 (=
+"4K") when the -h265 option is used.) This is just a request made to the
+AirPlay client, and perhaps will not be the final resolution you get. w
+and h are whole numbers with four digits or less. Note that the
+**height** pixel size is the controlling one used by the client for
+determining the streaming format; the width is dynamically adjusted to
+the shape of the image (portrait or landscape format, depending on how
+an iPad is held, for example).
+
+**-s wxh@r** As above, but also informs the AirPlay client about the
+screen refresh rate of the display. Default is r=60 (60 Hz); r must be a
+whole number less than 256.
+
+**-o** turns on an "overscanned" option for the display window. This
+reduces the image resolution by using some of the pixels requested by
+option -s wxh (or their default values 1920x1080) by adding an empty
+boundary frame of unused pixels (which would be lost in a full-screen
+display that overscans, and is not displayed by gstreamer).
+Recommendation: **don't use this option** unless there is some special
+reason to use it.
+
+**-fs** uses fullscreen mode, but only works with X11, Wayland, VAAPI,
+and D3D11 (Windows).
+
+**-p** allows you to select the network ports used by UxPlay (these need
+to be opened if the server is behind a firewall). By itself, -p sets
+"legacy" ports TCP 7100, 7000, 7001, UDP 6000, 6001, 7011. -p n (e.g. -p 35000) sets TCP and UDP ports n, n+1, n+2. -p n1,n2,n3 (comma-separated
+values) sets each port separately; -p n1,n2 sets ports n1,n2,n2+1. -p
+tcp n or -p udp n sets just the TCP or UDP ports. Ports must be in the
+range \[1024-65535\].
+
+If the -p option is not used, the ports are chosen dynamically
+(randomly), which will not work if a firewall is running.
+
+**-avdec** forces use of software h264 decoding using Gstreamer element
+avdec_h264 (libav h264 decoder). This option should prevent
+autovideosink choosing a hardware-accelerated videosink plugin such as
+vaapisink.
+
+**-vp _parser_** choses the GStreamer pipeline's h264 parser element,
+default is h264parse. Using quotes "..." allows options to be added.
+
+**-vd _decoder_** chooses the GStreamer pipeline's h264 decoder element,
+instead of the default value "decodebin" which chooses it for you.
+Software decoding is done by avdec_h264; various hardware decoders
+include: vaapih264dec, nvdec, nvh264dec, v4l2h264dec (these require that
+the appropriate hardware is available). Using quotes "..." allows some
+parameters to be included with the decoder name.
+
+**-vc _converter_** chooses the GStreamer pipeline's videoconverter
+element, instead of the default value "videoconvert". When using
+Video4Linux2 hardware-decoding by a GPU,`-vc v4l2convert` will also use
+the GPU for video conversion. Using quotes "..." allows some parameters
+to be included with the converter name.
+
+**-vs _videosink_** chooses the GStreamer videosink, instead of the
+default value "autovideosink" which chooses it for you. Some videosink
+choices are: ximagesink, xvimagesink, vaapisink (for intel graphics),
+gtksink, glimagesink, waylandsink, osxvideosink (for macOS), kmssink
+(for systems without X11, like Raspberry Pi OS lite) or fpsdisplaysink
+(which shows the streaming framerate in fps). Using quotes "..." allows
+some parameters to be included with the videosink name. For example,
+**fullscreen** mode is supported by the vaapisink plugin, and is
+obtained using `-vs "vaapisink fullscreen=true"`; this also works with
+`waylandsink`. The syntax of such options is specific to a given plugin
+(see GStreamer documentation), and some choices of videosink might not
+work on your system.
+
+**-vs 0** suppresses display of streamed video. In mirror mode, the
+client's screen is still mirrored at a reduced rate of 1 frame per
+second, but is not rendered or displayed. This option should always be
+used if the server is "headless" (with no attached screen to display
+video), and only used to render audio, which will be AAC
+lossily-compressed audio in mirror mode with unrendered video, and
+superior-quality ALAC Apple Lossless audio in Airplay audio-only mode.
+
+**-v4l2** Video settings for hardware h264 video decoding in the GPU by
+Video4Linux2. Equivalent to `-vd v4l2h264dec -vc v4l2convert`.
+
+**-bt709** A workaround for the failure of the older Video4Linux2 plugin
+to recognize Apple's use of an uncommon (but permitted) "full-range
+color" variant of the bt709 color standard for digital TV. This is no
+longer needed by GStreamer-1.20.4 and backports from it.
+
+**-rpi** Equivalent to "-v4l2" (Not valid for Raspberry Pi model 5, and
+removed in UxPlay 1.67)
+
+**-rpigl** Equivalent to "-rpi -vs glimagesink". (Removed since UxPlay
+1.67)
+
+**-rpifb** Equivalent to "-rpi -vs kmssink" (Removed since UxPlay 1.67)
+
+**-rpiwl** Equivalent to "-rpi -vs waylandsink". (Removed since UxPlay
+1.67)
+
+**-as _audiosink_** chooses the GStreamer audiosink, instead of letting
+autoaudiosink pick it for you. Some audiosink choices are: pulsesink,
+alsasink, pipewiresink, osssink, oss4sink, jackaudiosink, osxaudiosink
+(for macOS), wasapisink, directsoundsink (for Windows). Using quotes
+"..." might allow some optional parameters
+(e.g. `-as "alsasink device=..."` to specify a non-default output
+device). The syntax of such options is specific to a given plugin (see
+GStreamer documentation), and some choices of audiosink might not work
+on your system.
+
+**-as 0** (or just **-a**) suppresses playing of streamed audio, but
+displays streamed video.
+
+**-al _x_** specifies an audio latency _x_ in (decimal) seconds in
+Audio-only (ALAC), that is reported to the client. Values in the range
+\[0.0, 10.0\] seconds are allowed, and will be converted to a whole
+number of microseconds. Default is 0.25 sec (250000 usec). _(However,
+the client appears to ignore this reported latency, so this option seems
+non-functional.)_
+
+**-ca _filename_** provides a file (where _filename_ can include a full
+path) used for output of "cover art" (from Apple Music, _etc._,) in
+audio-only ALAC mode. This file is overwritten with the latest cover art
+as it arrives. Cover art (jpeg format) is discarded if this option is
+not used. Use with a image viewer that reloads the image if it changes,
+or regularly (_e.g._ once per second.). To achieve this, run
+"`uxplay -ca [path/to/]filename &`" in the background, then run the the
+image viewer in the foreground. Example, using `feh` as the viewer: run
+"`feh -R 1 [path/to/]filename`" (in the same terminal window in which
+uxplay was put into the background). To quit, use `ctrl-C fg ctrl-C` to
+terminate the image viewer, bring `uxplay` into the foreground, and
+terminate it too.
+
+**-reset n** sets a limit of _n_ consecutive timeout failures of the
+client to respond to ntp requests from the server (these are sent every
+3 seconds to check if the client is still present, and synchronize with
+it). After _n_ failures, the client will be presumed to be offline, and
+the connection will be reset to allow a new connection. The default
+value of _n_ is 5; the value _n_ = 0 means "no limit" on timeouts.
+
+**-nofreeze** closes the video window after a reset due to ntp timeout
+(default is to leave window open to allow a smoother reconection to the
+same client). This option may be useful in fullscreen mode.
+
+**-nc** maintains previous UxPlay \< 1.45 behavior that does **not
+close** the video window when the the client sends the "Stop Mirroring"
+signal. _This option is currently used by default in macOS, as the
+window created in macOS by GStreamer does not terminate correctly (it
+causes a segfault) if it is still open when the GStreamer pipeline is
+closed._
+
+**-nohold** Drops the current connection when a new client attempts to
+connect. Without this option, the current client maintains exclusive
+ownership of UxPlay until it disconnects.
+
+**-restrict** Restrict clients allowed to connect to those specified by
+`-allow `. The deviceID has the form of a MAC address which is
+displayed by UxPlay when the client attempts to connect, and appears to
+be immutable. It has the format `XX:XX:XX:XX:XX:XX`, X = 0-9,A-F, and is
+possibly the "true" hardware MAC address of the device. Note that iOS
+clients generally expose different random "private Wi_Fi addresses"
+("fake" MAC addresses) to different networks (for privacy reasons, to
+prevent tracking), which may change, and do not correpond to the
+deviceID.
+
+**-restrict no** Remove restrictions (default). This is useful as a
+command-line argument to overide restrictions set in the Startup file.
+
+**-allow _id_** Adds the deviceID = _id_ to the list of allowed clients
+when client restrictions are being enforced. Usually this will be an
+entry in the uxplayrc startup file.
+
+**-block _id_** Always block clients with deviceID = _id_, even when
+client restrictions are not being enforced generally. Usually this will
+be an entry in the uxplayrc startup file.
+
+**-FPSdata** Turns on monitoring of regular reports about video
+streaming performance that are sent by the client. These will be
+displayed in the terminal window if this option is used. The data is
+updated by the client at 1 second intervals.
+
+**-fps n** sets a maximum frame rate (in frames per second) for the
+AirPlay client to stream video; n must be a whole number less than 256.
+(The client may choose to serve video at any frame rate lower than this;
+default is 30 fps.) A setting of 60 fps may give you improved video but
+is not recommended on Raspberry Pi. A setting below 30 fps might be
+useful to reduce latency if you are running more than one instance of
+uxplay at the same time. _This setting is only an advisory to the client
+device, so setting a high value will not force a high framerate._ (You
+can test using "-vs fpsdisplaysink" to see what framerate is being
+received, or use the option -FPSdata which displays video-stream
+performance data continuously sent by the client during
+video-streaming.)
+
+**-f {H\|V\|I}** implements "videoflip" image transforms: H = horizontal
+flip (right-left flip, or mirror image); V = vertical flip ; I = 180
+degree rotation or inversion (which is the combination of H with V).
+
+**-r {R\|L}** 90 degree Right (clockwise) or Left (counter-clockwise)
+rotations; these image transforms are carried out after any **-f**
+transforms.
+
+**-m \[mac\]** changes the MAC address (Device ID) used by UxPlay
+(default is to use the true hardware MAC address reported by the host
+computer's network card). (Different server_name, MAC addresses, and
+network ports are needed for each running uxplay if you attempt to run
+more than one instance of uxplay on the same computer.) If \[mac\] (in
+form xx:xx:xx:xx:xx:xx, 6 hex octets) is not given, a random MAC address
+is generated. If UxPlay fails to find the true MAC address of a network
+card, (more specifically, the MAC address used by the first active
+network interface detected) a random MAC address will be used even if
+option **-m** was not specified. (Note that a random MAC address will be
+different each time UxPlay is started).
+
+**-key \[_filename_\]**: This (more secure) option for generating and
+storing a persistant public key (needed for the -pin option) has been
+replaced by default with a (less secure) method which generates a key
+from the server's "device ID" (MAC address, which can be changed with
+the -m option, conveniently as a startup file option). When the -key
+option is used, a securely generated keypair is generated and stored in
+`$HOME/.uxplay.pem`, if that file does not exist, or read from it, if it
+exists. (Optionally, the key can be stored in _filename_.) This method
+is more secure than the new default method, (because the Device ID is
+broadcast in the DNS_SD announcement) but still leaves the private key
+exposed to anyone who can access the pem file. This option should be set
+in the UxPlay startup file as a line "key" or "key _filename_" (no
+initial "-"), where _filename_ is a full path which should be enclosed
+in quotes (`"...."`) if it contains any blank spaces. **Because the
+default method is simpler, and security of client access to uxplay is
+unlikely to be an important issue, the -key option is no longer
+recommended**.
+
+**-dacp \[_filename_\]**: Export current client DACP-ID and
+Active-Remote key to file: default is \$HOME/.uxplay.dacp. (optionally
+can be changed to _filename_). Can be used by remote control
+applications. File is transient: only exists while client is connected.
+
+**-vdmp** Dumps h264 video to file videodump.h264. -vdmp n dumps not
+more than n NAL units to videodump.x.h264; x= 1,2,... increases each
+time a SPS/PPS NAL unit arrives. To change the name _videodump_, use
+-vdmp \[n\] _filename_.
+
+**-admp** Dumps audio to file audiodump.x.aac (AAC-ELD format audio),
+audiodump.x.alac (ALAC format audio) or audiodump.x.aud (other-format
+audio), where x = 1,2,3... increases each time the audio format changes.
+-admp _n_ restricts the number of packets dumped to a file to _n_ or
+less. To change the name _audiodump_, use -admp \[n\] _filename_. _Note
+that (unlike dumped video) the dumped audio is currently only useful for
+debugging, as it is not containerized to make it playable with standard
+audio players._
+
+**-d** Enable debug output. Note: this does not show GStreamer error or
+debug messages. To see GStreamer error and warning messages, set the
+environment variable GST_DEBUG with "export GST_DEBUG=2" before running
+uxplay. To see GStreamer information messages, set GST_DEBUG=4; for
+DEBUG messages, GST_DEBUG=5; increase this to see even more of the
+GStreamer inner workings.
+
+# Troubleshooting
+
+Note: `uxplay` is run from a terminal command line, and informational
+messages are written to the terminal.
+
+### 0. Problems in compiling UxPlay.
+
+One user (on Ubuntu) found compilation failed with messages about
+linking to "usr/local/lib/libcrypto.a" and "zlib". This was because (in
+addition to the standard ubuntu installation of libssl-dev), the user
+was unaware that a second installation with libcrypto in /usr/local was
+present. Solution: when more than one installation of OpenSSL is
+present, set the environment variable OPEN_SSL_ROOT_DIR to point to the
+correct one; on 64-bit Ubuntu, this is done by running
+`export OPENSSL_ROOT_DIR=/usr/lib/X86_64-linux-gnu/` before running
+cmake.
+
+### 1. **Avahi/DNS_SD Bonjour/Zeroconf issues**
+
+The DNS_SD Service-Discovery ("Bonjour" or "Zeroconf") service is
+required for UxPlay to work. On Linux, it will be usually provided by
+Avahi, and to troubleshoot this, you should use the tool `avahi-browse`.
+(You may need to install a separate package with a name like
+`avahi-utils` to get this.)
+
+On Linux, make sure Avahi is installed, and start the avahi-daemon
+service on the system running uxplay (your distribution will document
+how to do this, for example: `sudo systemctl avahi-daemon` or
+`sudo service avahi-daemon `, with `` one of enable, disable,
+start, stop, status. You might need to edit the avahi-daemon.conf file
+(it is typically in /etc/avahi/, find it with
+"`sudo find /etc -name avahi-daemon.conf`"): make sure that
+"disable-publishing" is **not** a selected option). Some systems may
+instead use the mdnsd daemon as an alternative to provide DNS-SD
+service. (FreeBSD offers both alternatives, but only Avahi was tested;
+see [here](https://gist.github.com/reidransom/6033227).)
+
+- **uxplay starts, but either stalls or stops after "Initialized
+ server socket(s)" appears (_without the server name showing on the
+ client_)**.
+
+If UxPlay stops with the "No DNS-SD Server found" message, this means
+that your network **does not have a running Bonjour/zeroconf DNS-SD
+server.** Before v1.60, UxPlay used to stall silently if DNS-SD service
+registration failed, but now stops with an error message returned by the
+DNSServiceRegister function: kDNSServiceErr_Unknown if no DNS-SD server
+was found: _(A NixOS user found that in NixOS, this error can also occur
+if avahi-daemon service IS running with publishing enabled, but reports
+"the error disappeared on NixOS by setting services.avahi.openFirewall
+to true".)_ Other mDNS error codes are in the range FFFE FF00 (-65792)
+to FFFE FFFF (-65537), and are listed in the dnssd.h file. An older
+version of this (the one used by avahi) is found
+[here](https://github.com/lathiat/avahi/blob/master/avahi-compat-libdns_sd/dns_sd.h).
+A few additional error codes are defined in a later version from
+[Apple](https://opensource.apple.com/source/mDNSResponder/mDNSResponder-544/mDNSShared/dns_sd.h.auto.html).
+
+If UxPlay stalls _without an error message_ and _without the server name
+showing on the client_, **this is a network problem** (if your UxPlay
+version is older than 1.60, it is also the behavior when no DNS-SD
+server is found.)
+
+A useful tool for examining such network problems from the client end is
+the (free) Discovery DNS-SD browser [available in the Apple App
+Store](https://apps.apple.com/us/developer/lily-ballard/id305441020) for
+both iOS (works on iPadOS too) and macOS.
+
+- Some users using dual-band (2.4GHz/5GHz) routers have reported that
+ clients using the 5GHz band (sometimes) "fail to see UxPlay" (i.e.,
+ do not get a response to their mDNS queries), but the 2.4GHz band
+ works. Other projects using Bonjour/mDNS have had similar reports;
+ the issue seems to be router-specific, perhaps related to "auto"
+ rather than fixed channel selection (5GHz has many more channels to
+ switch between), or channel width selections; one speculation is
+ that since mDNS uses UDP protocol (where "lost" messages are not
+ resent), a mDNS query might get lost if channel switching occurs
+ during the query.
+
+If your router has this problem, a reported "fix" is to (at least on
+5GHz) use fixed channel and/or fixed (not dynamic) channel width.
+
+- **Avahi works at first, but new clients do not see UxPlay, or
+ clients that initially saw it stop doing so after they disconnect**.
+
+This is usually because Avahi is only using the "loopback" network
+interface, and is not receiving mDNS queries from new clients that were
+not listening when UxPlay started.
+
+To check this, after starting uxplay, use the utility
+`avahi-browse -a -t` **in a different terminal window** on the server to
+verify that the UxPlay AirTunes and AirPlay services are correctly
+registered (only the AirTunes service is used in the "Legacy" AirPlay
+Mirror mode used by UxPlay, but the AirPlay service is used for the
+initial contact).
+
+The results returned by avahi-browse should show entries for uxplay like
+
+ + eno1 IPv6 UxPlay AirPlay Remote Video local
+ + eno1 IPv4 UxPlay AirPlay Remote Video local
+ + lo IPv4 UxPlay AirPlay Remote Video local
+ + eno1 IPv6 863EA27598FE@UxPlay AirTunes Remote Audio local
+ + eno1 IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local
+ + lo IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local
+
+If only the loopback ("lo") entries are shown, a firewall on the UxPlay
+host is probably blocking full DNS-SD service, and you need to open the
+default UDP port 5353 for mDNS requests, as loopback-based DNS-SD
+service is unreliable.
+
+If the UxPlay services are listed by avahi-browse as above, but are not
+seen by the client, the problem is likely to be a problem with the local
+network.
+
+### 2. uxplay starts, but stalls after "Initialized server socket(s)" appears, _with the server name showing on the client_ (but the client fails to connect when the UxPlay server is selected).
+
+This shows that a _DNS-SD_ service is working, clients hear UxPlay is
+available, but the UxPlay server is not receiving the response from the
+client. This is usually because a firewall on the server is blocking the
+connection request from the client. (One user who insisted that the
+firewall had been turned off turned out to have had _two_ active
+firewalls (_firewalld_ and _ufw_) _both_ running on the server!) If
+possible, either turn off the firewall to see if that is the problem, or
+get three consecutive network ports, starting at port n, all three in
+the range 1024-65535, opened for both tcp and udp, and use "uxplay -p n"
+(or open UDP 7011,6001,6000 TCP 7100,7000,7001 and use "uxplay -p").
+
+If you are _really_ sure there is no firewall, you may need to
+investigate your network transmissions with a tool like netstat, but
+almost always this is a firewall issue.
+
+### 3. Problems _after_ the client-server connection has been made:
+
+If you do _not_ see the message `raop_rtp_mirror starting mirroring`,
+something went wrong before the client-server negotiations were
+finished. For such problems, use "uxplay -d" (debug log option) to see
+what is happening: it will show how far the connection process gets
+before the failure occurs. You can compare your debug output to that
+from a successful start of UxPlay in the [UxPlay
+Wiki](https://github.com/FDH2/UxPlay/wiki).
+
+**If UxPlay reports that mirroring started, but you get no video or
+audio, the problem is probably from a GStreamer plugin that doesn't work
+on your system** (by default, GStreamer uses the "autovideosink" and
+"autoaudiosink" algorithms to guess what are the "best" plugins to use
+on your system). A different reason for no audio occurred when a user
+with a firewall only opened two udp network ports: **three** are
+required (the third one receives the audio data).
+
+**Raspberry Pi** devices (_Pi 4B+ and earlier: this does not apply to
+the Pi 5, which does not provide hardware h264 decoding, and does not
+need it_) work best with hardware GPU h264 video decoding if the
+Video4Linux2 plugin in GStreamer v1.20.x or earlier has been patched
+(see the UxPlay
+[Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches)
+for patches). This is fixed in GStreamer-1.22, and by backport patches
+from this in distributions such as Raspberry Pi OS (Bullseye): **use
+option `-bt709` with the GStreamer-1.18.4 from Raspberry Pi OS**. This
+also needs the bcm2835-codec kernel module that is not in the standard
+Linux kernel (it is available in Raspberry Pi OS, Ubuntu and Manjaro).
+
+- **If this kernel module is not available in your Raspberry Pi
+ operating system, or if GStreamer \< 1.22 is not patched, use option
+ `-avdec` for software h264-decoding.**
+
+Sometimes "autovideosink" may select the OpenGL renderer "glimagesink"
+which may not work correctly on your system. Try the options "-vs
+ximagesink" or "-vs xvimagesink" to see if using one of these fixes the
+problem.
+
+Other reported problems are connected to the GStreamer VAAPI plugin (for
+hardware-accelerated Intel graphics, but not NVIDIA graphics). Use the
+option "-avdec" to force software h264 video decoding: this should
+prevent autovideosink from selecting the vaapisink videosink.
+Alternatively, find out if the gstreamer1.0-vaapi plugin is installed,
+and if so, uninstall it. (If this does not fix the problem, you can
+reinstall it.)
+
+There are some reports of other GStreamer problems with
+hardware-accelerated Intel HD graphics. One user (on Debian) solved this
+with "sudo apt install intel-media-va-driver-non-free". This is a driver
+for 8'th (or later) generation "\*-lake" Intel chips, that seems to be
+related to VAAPI accelerated graphics.
+
+If you _do_ have Intel HD graphics, and have installed the vaapi plugin,
+but `-vs vaapisink` does not work, check that vaapi is not "blacklisted"
+in your GStreamer installation: run `gst-inspect-1.0 vaapi`, if this
+reports `0 features`, you need to `export GST_VAAPI_ALL_DRIVERS=1`
+before running uxplay, or set this in the default environment.
+
+You can try to fix audio or video problems by using the
+"`-as `" or "`-vs `" options to choose the
+GStreamer audiosink or videosink , rather than letting GStreamer choose
+one for you. (See above, in [Starting and running
+UxPlay](#starting-and-running-uxplay) for choices of `` or
+``.)
+
+The "OpenGL renderer" window created on Linux by "-vs glimagesink"
+sometimes does not close properly when its "close" button is clicked.
+(this is a GStreamer issue). You may need to terminate uxplay with
+Ctrl-C to close a "zombie" OpenGl window. If similar problems happen
+when the client sends the "Stop Mirroring" signal, try the no-close
+option "-nc" that leaves the video window open.
+
+### 4. GStreamer issues (missing plugins, etc.):
+
+- clearing the user's GStreamer cache with
+ `rm -rf ~/.cache/gstreamer-1.0/*` may be the solution to problems
+ where gst-inspect-1.0 does not show a plugin that you believe is
+ installed. The cache will be regenerated next time GStreamer is
+ started. **This is the solution to puzzling problems that turn out
+ to come from corruption of the cache, and should be tried first.**
+
+If UxPlay fails to start, with a message that a required GStreamer
+plugin (such as "libav") was not found, first check with the GStreamer
+tool gst-inspect-1.0 to see what GStreamer knows is available. (You may
+need to install some additional GStreamer "tools" package to get
+gst-inspect-1.0). For, _e.g._ a libav problem, check with
+"`gst-inspect-1.0 libav`". If it is not shown as available to GStreamer,
+but your package manager shows the relevant package as installed (as one
+user found), try entirely removing and reinstalling the package. That
+user found that a solution to a "**Required gstreamer plugin 'libav' not
+found**" message that kept recurring was to clear the user's gstreamer
+cache.
+
+If it fails to start with an error like '`no element "avdec_aac"`' this
+is because even though gstreamer-libav is installed. it is incomplete
+because some plugin features are missing:
+"`gst-inspect-1.0 | grep avdec_aac`" will show if avdec_aac is
+available. Unlike other GStreamer plugins, the libav plugin is a front
+end to FFmpeg codecs which provide avdec\_\*.
+
+- Some distributions (RedHat, SUSE, etc) provide incomplete versions
+ of FFmpeg because of patent issues with codecs used by certain
+ plugins. In those cases there will be some "extra package" provider
+ like [RPM fusion](https://rpmfusion.org) (RedHat),
+ [packman](http://packman.links2linux.org/) (SUSE) where you can get
+ complete packages (your distribution will usually provide
+ instructions for this, Mageia puts them in an optional "tainted"
+ repo). The packages needed may be "ffmpeg\*" or "libav\*" packages:
+ the GStreamer libav plugin package does not contain any codecs
+ itself, it just provides a way for GStreamer to use ffmpeg/libav
+ codec libraries which must be installed separately. For similar
+ reasons, distributions may ship incomplete packages of GStreamer
+ "plugins-bad". Use user on Fedora thought they had installed from
+ rpmfusion, but the system had not obeyed: _"Adding --allowerasing to
+ the dnf command fixed it after a restart"_.
+
+- starting with release UxPlay-1.65.3, UxPlay will continue to
+ function, but without audio in mirror mode, if avdec_aac is missing.
+
+To troubleshoot GStreamer execute "export GST_DEBUG=2" to set the
+GStreamer debug-level environment-variable in the terminal where you
+will run uxplay, so that you see warning and error messages; see
+[GStreamer debugging
+tools](https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html)
+for how to see much more of what is happening inside GStreamer. Run
+"gst-inspect-1.0" to see which GStreamer plugins are installed on your
+system.
+
+Some extra GStreamer packages for special plugins may need to be
+installed (or reinstalled: a user using a Wayland display system as an
+alternative to X11 reported that after reinstalling Lubuntu 18.4, UxPlay
+would not work until gstreamer1.0-x was installed, presumably for
+Wayland's X11-compatibility mode). Different distributions may break up
+GStreamer 1.x into packages in different ways; the packages listed above
+in the build instructions should bring in other required GStreamer
+packages as dependencies, but will not install all possible plugins.
+
+The GStreamer video pipeline, which is shown in the initial output from
+`uxplay -d`, has the default form
+
+ appsrc name=video_source ! queue ! h264parse ! decodebin ! videoconvert ! autovideosink name=video_sink sync=false
+
+The pipeline is fully configurable: default elements "h264parse",
+"decodebin", "videoconvert", and "autovideosink" can respectively be
+replaced by using uxplay options `-vp`, `-vd`, `-vc`, and `-vs`, if
+there is any need to modify it (entries can be given in quotes "..." to
+include options).
+
+### 5. Mirror screen freezes (a network problem):
+
+This can happen if the TCP video stream from the client stops arriving
+at the server, probably because of network problems (the UDP audio
+stream may continue to arrive). At 3-second intervals, UxPlay checks
+that the client is still connected by sending it a request for a NTP
+time signal. If a reply is not received from the client within a 0.3 sec
+time-window, an "ntp timeout" is registered. If a certain number
+(currently 5) of consecutive ntp timeouts occur, UxPlay assumes that the
+client is "dead", and resets the connection, becoming available for
+connection to a new client, or reconnection to the previous one.
+Sometimes the connection may recover before the timeout limit is
+reached, and if the default limit is not right for your network, it can
+be modified using the option "-reset _n_", where _n_ is the desired
+timeout-limit value (_n_ = 0 means "no limit"). If the connection starts
+to recover after ntp timeouts, a corrupt video packet from before the
+timeout may trigger a "connection reset by peer" error, which also
+causes UxPlay to reset the connection.
+
+- When the connection is reset, the "frozen" mirror screen of the
+ previous connection is left in place, but does **not** block new
+ connections, and will be taken over by a new client connection when
+ it is made.
+
+### 6. Protocol issues (with decryption of the encrypted audio and video streams sent by the client).
+
+A protocol failure may trigger an unending stream of error messages, and
+means that the audio decryption key (also used in video decryption) was
+not correctly extracted from data sent by the client.
+
+The protocol was modifed in UxPlay-1.65 after it was discovered that the
+client-server "pairing" step could be avoided (leading to a much quicker
+connection setup, without a 5 second delay) by disabling "Supports
+Legacy Pairing" (bit 27) in the "features" code UxPlay advertises on
+DNS-SD Service Discovery. Most clients will then not attempt the setup
+of a "shared secret key" when pairing, which is used by AppleTV for
+simultaneous handling of multiple clients (UxPlay only supports one
+client at a time). **This change is now well-tested, but in case it
+causes any protocol failures, UxPlay can be reverted to the previous
+behavior by uncommenting the previous "FEATURES_1" setting (and
+commenting out the new one) in lib/dnssdint.h, and then rebuilding
+UxPlay.** ("Pairing" is re-enabled when the new Apple-style one-time
+"pin" authentication is activated by running UxPlay with the "-pin"
+option introduced in UxPlay 1.67.)
+
+Protocol failure should not happen for iOS 9.3 or later clients.
+However, if a client uses the same older version of the protocol that is
+used by the Windows-based AirPlay client emulator _AirMyPC_, the
+protocol can be switched to the older version by the setting
+`OLD_PROTOCOL_CLIENT_USER_AGENT_LIST` in `UxPlay/lib/global.h`. UxPlay
+reports the client's "User Agent" string when it connects. If some other
+client also fails to decrypt all audio and video, try adding its "User
+Agent" string in place of "xxx" in the entry "AirMyPC/2.0;xxx" in
+global.h and rebuild uxplay.
+
+Note that for DNS-SD Service Discovery, Uxplay declares itself to be an
+AppleTV3,2 (a 32 bit device) with a sourceVersion 220.68; this can also
+be changed in global.h. UxPlay also works if it declares itself as an
+AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen,
+introduced 2017, running tvOS 12.2.1), so it does not seem to matter
+what version UxPlay claims to be.
+
+# Changelog
+
+1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
+only for YouTube movies. Fix issue with NTP timeout on Windows.
+
+1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x
+2160). Fix issue with GStreamer \>= 1.24 when client sleeps, then wakes.
+
+1.69 2024-08-09 Internal improvements (e.g. in -nohold option,
+identifying GStreamer videosink selected by autovideosink, finding X11
+display) in anticipation of future HLS video support. New -nofreeze
+option to not leave frozen video in place when a network connection is
+reset. Fixes for GStreamer-1.24.x changes.
+
+1.68 2023-12-31 New simpler (default) method for generating a persistent
+public key from the server MAC address (which can now be set with the -m
+option). (The previous method is still available with -key option). New
+option -reg to maintain a register of pin-authenticated clients.
+Corrected volume-control: now interprets AirPlay volume range -30dB:0dB
+as decibel gain attenuation, with new option -db low\[:high\] for "flat"
+rescaling of the dB range. Add -taper option for a "tapered" AirPlay
+volume-control profile.
+
+1.67 2023-11-30 Add support for Apple-style one-time pin authentication
+of clients with option "-pin": (uses SRP6a authentication protocol and
+public key persistence). Detection with error message of (currently)
+unsupported H265 video when requesting high resolution over wired
+ethernet. Removed rpi\* options (which are not valid with new Raspberry
+Pi model 5, and can be replaced by combinations of other options). Added
+optional argument "mac" to "-m" option, to specify a replacement MAC
+address/Device ID. Update llhttp to v. 9.1.3. Add -dacp option for
+exporting current client DACP info (for remotes).
+
+1.66 2023-09-05 Fix IPV6 support. Add option to restrict clients to
+those on a list of allowed deviceIDs, or to block connections from
+clients on a list of blocked deviceIDs. Fix for #207 from @thiccaxe
+(screen lag in vsync mode after client wakes from sleep).
+
+1.65.3 2023-07-23 Add RPM spec file; add warning if required gstreamer
+libav feature "avdec_aac" is missing: (this occurs in RPM-based
+distributions that ship an incomplete FFmpeg for Patent or License
+reasons, and rely on users installing an externally-supplied complete
+FFmpeg). Mirror-mode airplay will now work without audio if avdec_aac is
+missing.
+
+1.65 2023-06-03 Eliminate pair_setup part of connection protocol to
+allow faster connections with clients (thanks to @shuax #176 for this
+discovery); to revert, uncomment a line in lib/dnssdint.h. Disconnect
+from audio device when connection closes, to not block its use by other
+apps if uxplay is running but not connected. Fix for AirMyPC client
+(broken since 1.60), so its older non-NTP timestamp protocol works with
+-vsync. Corrected parsing of configuration file entries that were in
+quotes.
+
+1.64 2023-04-23 Timestamp-based synchronization of audio and video is
+now the default in Mirror mode. (Use "-vsync no" to restore previous
+behavior.) A configuration file can now be used for startup options.
+Also some internal cleanups and a minor bugfix that fixes #192.
+
+1.63 2023-02-12 Reworked audio-video synchronization, with new options
+-vsync (for Mirror mode) and -async (for Audio-Only mode, to sync with
+client video). Option -vsync makes software h264 decoding of streamed
+videos with option -avdec viable on some recent Raspberry Pi models.
+Internal change: all times are now processed in nanoseconds units.
+Removed -ao option introduced in 1.62.
+
+1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user
+synchronization of ALAC audio playing on the server with video, song
+lyrics, etc. playing on the client. x = 5.0 appears to be optimal in
+many cases. Quality fixes: cleanup in volume changes, timestamps, some
+bugfixes.
+
+1.61 2022-12-30 Removed -t option (workaround for an Avahi issue,
+correctly solved by opening network port UDP 5353 in firewall). Remove
+-g debug flag from CMAKE_CFLAGS. Postpend (instead of prepend) build
+environment CFLAGS to CMAKE_CFLAGS. Refactor parts of uxplay.cpp
+
+1.60 2022-12-15 Added exit with error message if DNSServiceRegister
+fails (instead of just stalling). Test for Client's attempt to using
+unsupported AirPlay 2 "REMOTE CONTROL" protocol (with no timing
+channel), and exit if this occurs. Reworked metadata processing to
+correctly parse DMAP header (previous version worked with DMAP messages
+currently received, but was not correct).
+
+1.59 2022-12-12 remove "ZOOMFIX" compile option and make compilation
+with X11-dependence the default if X11 development libraries are
+detected (this now also provides fullscreen mode with a F11 or Alt+Enter
+key toggle); ZOOMFIX is now automatically applied for GStreamer \< 1.20.
+New cmake option -DNO_X11_DEPS compiles uxplay without X11 dependence.
+Reworked internal metadata handling. Fix segfault with "-vs 0".
+
+1.58 2022-10-29 Add option "-nohold" that will drop existing connections
+when a new client connects. Update llhttp to v8.1.0.
+
+1.57 2022-10-09 Minor fixes: (fix coredump on AUR on "stop mirroring",
+occurs when compiled with AUR CFLAGS -DFORTIFY_SOURCE); graceful exit
+when required plugins are missing; improved support for builds on
+Windows. Include audioresample in GStreamer audio pipeline.
+
+1.56 2022-09-01 Added support for building and running UxPlay-1.56 on
+Windows (no changes to Unix (Linux, \*BSD, macOS) codebase.)
+
+1.56 2022-07-30 Remove -bt709 from -rpi, -rpiwl, -rpifb as GStreamer is
+now fixed.
+
+1.55 2022-07-04 Remove the bt709 fix from -v4l2 and create a new -bt709
+option (previous "-v4l2" is now "-v4l2 -bt709"). This allows the
+currently-required -bt709 option to be used on its own on RPi without
+-v4l2 (sometimes this give better results).
+
+1.54 2022-06-25 Add support for "Cover Art" display in Audio-only (ALAC)
+mode. Reverted a change that caused VAAPI to crash with AMD POLARIS
+graphics cards. Minor internal changes to plist code and uxplay option
+parsing.
+
+1.53 2022-06-13 Internal changes to audio sync code, revised
+documentation, Minor bugfix (fix assertion crash when resent audio
+packets are empty).
+
+1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted
+streaming debug output (readable aligned timestamps with decimal points
+in seconds). Eliminate memory leaks (found by valgrind). Support for
+display of ALAC (audio-only) metadata (soundtrack artist names, titles
+etc.) in the uxplay terminal.
+
+1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option
+-v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2,
+-v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from
+1.48 that broke reconnection after "Stop Mirroring" is sent by client.
+
+1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI
+plugins only), Changed -rpi to be for framebuffer ("lite") RPi systems
+and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop
+systems. Also modified timestamps from "DTS" to "PTS" for latency
+improvement, plus internal cleanups.
+
+1.49 2022-03-28 Addded options for dumping video and/or audio to file,
+for debugging, etc. h264 PPS/SPS NALU's are shown with -d. Fixed
+video-not-working for M1 Mac clients.
+
+1.48 2022-03-11 Made the GStreamer video pipeline fully configurable,
+for use with hardware h264 decoding. Support for Raspberry Pi.
+
+1.47 2022-02-05 Added -FPSdata option to display (in the terminal)
+regular reports sent by the client about video streaming performance.
+Internal cleanups of processing of video packets received from the
+client. Added -reset n option to reset the connection after n ntp
+timeouts (also reset after "connection reset by peer" error in video
+stream).
+
+1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken hardware
+acceleration): once again use decodebin in the video pipeline; introduce
+new option "-avdec" to force software h264 decoding by libav h264, if
+needed (to prevent selection of vaapisink by autovideosink). Update
+llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2. Restrict
+connections to one client at a time (second client must now wait for
+first client to disconnect).
+
+1.45 2022-01-10 New behavior: close video window when client requests
+"stop mirroring". (A new "no close" option "-nc" is added for users who
+wish to retain previous behavior that does not close the video window).
+
+1.44 2021-12-13 Omit hash of aeskey with ecdh_secret for an AirMyPC
+client; make an internal rearrangement of where this hash is done. Fully
+report all initial communications between client and server in -d debug
+mode. Replace decodebin in GStreamer video pipeline by h264-specific
+elements.
+
+1.43 2021-12-07 Various internal changes, such as tests for successful
+decryption, uniform treatment of informational/debug messages, etc.,
+updated README.
+
+1.42 2021-11-20 Fix MAC detection to work with modern Linux interface
+naming practices, MacOS and \*BSD.
+
+1.41 2021-11-11 Further cleanups of multiple audio format support
+(internal changes, separated RAOP and GStreamer audio/video startup)
+
+1.40 2021-11-09 Cleanup segfault in ALAC support, manpage location fix,
+show request Plists in debug mode.
+
+1.39 2021-11-06 Added support for Apple Lossless (ALAC) audio streams.
+
+1.38 2021-10-8 Add -as _audiosink_ option to allow user to choose the
+GStreamer audiosink.
+
+1.37 2021-09-29 Append "@hostname" to AirPlay Server name, where
+"hostname" is the name of the server running uxplay (reworked change in
+1.36).
+
+1.36 2021-09-29 Implemented suggestion (by @mrbesen and @PetrusZ) to use
+hostname of machine runing uxplay as the default server name
+
+1.35.1 2021-09-28 Added the -vs 0 option for streaming audio, but not
+displaying video.
+
+1.35 2021-09-10 now uses a GLib MainLoop, and builds on macOS (tested on
+Intel Mac, 10.15 ). New option -t _timeout_ for relaunching server if no
+connections were active in previous _timeout_ seconds (to renew Bonjour
+registration).
+
+1.341 2021-09-04 fixed: render logger was not being destroyed by
+stop_server()
+
+1.34 2021-08-27 Fixed "ZOOMFIX": the X11 window name fix was only being
+made the first time the GStreamer window was created by uxplay, and not
+if the server was relaunched after the GStreamer window was closed, with
+uxplay still running. Corrected in v. 1.34
+
+### Building OpenSSL \>= 1.1.1 from source.
+
+If you need to do this, note that you may be able to use a newer version
+(OpenSSL-3.0.1 is known to work). You will need the standard development
+toolset (autoconf, automake, libtool). Download the source code from
+. Install the downloaded openssl by
+opening a terminal in your Downloads directory, and unpacking the source
+distribution: ("tar -xvzf openssl-3.0.1.tar.gz ; cd openssl-3.0.1").
+Then build/install with "./config ; make ; sudo make install_dev". This
+will typically install the needed library `libcrypto.*`, either in
+/usr/local/lib or /usr/local/lib64.
+
+_(Ignore the following for builds on MacOS:)_ On some systems like
+Debian or Ubuntu, you may also need to add a missing entry
+`/usr/local/lib64` in /etc/ld.so.conf (or place a file containing
+"/usr/local/lib64/libcrypto.so" in /etc/ld.so.conf.d) and then run "sudo
+ldconfig".
+
+### Building libplist \>= 2.0.0 from source.
+
+_(Note: on Debian 9 "Stretch" or Ubuntu 16.04 LTS editions, you can
+avoid this step by installing libplist-dev and libplist3 from Debian 10
+or Ubuntu 18.04.)_ As well as the usual build tools (autoconf, automake,
+libtool), you may need to also install some libpython\*-dev package.
+Download the latest source with git from
+, or get the source from
+the Releases section (use the \*.tar.bz2 release, **not** the \*.zip or
+\*.tar.gz versions): download
+[libplist-2.3.0](https://github.com/libimobiledevice/libplist/releases/download/2.3.0/libplist-2.3.0.tar.bz2),
+then unpack it ("tar -xvjf libplist-2.3.0.tar.bz2 ; cd libplist-2.3.0"),
+and build/install it: ("./configure ; make ; sudo make install"). This
+will probably install libplist-2.0.\* in /usr/local/lib. The new
+libplist-2.3.0 release should be compatible with UxPlay;
+[libplist-2.2.0](https://github.com/libimobiledevice/libplist/releases/download/2.2.0/libplist-2.2.0.tar.bz2)
+is also available if there are any issues.
+
+_(Ignore the following for builds on MacOS:)_ On some systems like
+Debian or Ubuntu, you may also need to add a missing entry
+`/usr/local/lib` in /etc/ld.so.conf (or place a file containing
+"/usr/local/lib/libplist-2.0.so" in /etc/ld.so.conf.d) and then run
+"sudo ldconfig".
+
+# Disclaimer
+
+All the resources in this repository are written using only freely
+available information from the internet. The code and related resources
+are meant for educational purposes only. It is the responsibility of the
+user to make sure all local laws are adhered to.
+
+This project makes use of a third-party GPL library for handling
+FairPlay. The legal status of that library is unclear. Should you be a
+representative of Apple and have any objections against the legality of
+the library and its use in this project, please contact the developers
+and the appropriate steps will be taken.
+
+Given the large number of third-party AirPlay receivers (mostly
+closed-source) available for purchase, it is our understanding that an
+open source implementation of the same functionality wouldn't violate
+any of Apple's rights either.
+
+# UxPlay authors
+
+_\[adapted from fdraschbacher's notes on RPiPlay antecedents\]_
+
+The code in this repository accumulated from various sources over time.
+Here is an attempt at listing the various authors and the components
+they created:
+
+UxPlay was initially created by **antimof** from RPiPlay, by replacing
+its Raspberry-Pi-adapted OpenMAX video and audio rendering system with
+GStreamer rendering for desktop Linux systems; the antimof work on code
+in `renderers/` was later backported to RPiPlay, and the antimof project
+became dormant, but was later revived at the [current GitHub
+site](http://github.com/FDH2/UxPlay) to serve a wider community of
+users.
+
+The previous authors of code included in UxPlay by inheritance from
+RPiPlay include:
+
+- **EstebanKubata**: Created a FairPlay library called
+ [PlayFair](https://github.com/EstebanKubata/playfair). Located in
+ the `lib/playfair` folder. License: GNU GPL
+- **Juho Vähä-Herttua** and contributors: Created an AirPlay audio
+ server called [ShairPlay](https://github.com/juhovh/shairplay),
+ including support for Fairplay based on PlayFair. Most of the code
+ in `lib/` originally stems from this project. License: GNU LGPLv2.1+
+- **dsafa22**: Created an AirPlay 2 mirroring server
+ [AirplayServer](https://github.com/dsafa22/AirplayServer) (seems
+ gone now), for Android based on ShairPlay. Code is preserved
+ [here](https://github.com/jiangban/AirplayServer), and [see
+ here](https://github.com/FDH2/UxPlay/wiki/AirPlay2) for the
+ description of the analysis of the AirPlay 2 mirror protocol that
+ made RPiPlay possible, by the AirplayServer author. All code in
+ `lib/` concerning mirroring is dsafa22's work. License: GNU
+ LGPLv2.1+
+- **Florian Draschbacher** (FD-) and contributors: adapted dsafa22's
+ Android project for the Raspberry Pi, with extensive cleanups,
+ debugging and improvements. The project
+ [RPiPlay](https://github.com/FD-/RPiPlay) is basically a port of
+ dsafa22's code to the Raspberry Pi, utilizing OpenMAX and OpenSSL
+ for better performance on the Pi. License GPL v3. FD- has written an
+ interesting note on the history of [Airplay protocol
+ versions](http://github.com/FD-/RPiPlay#airplay-protocol-versions),
+ available at the RPiPlay github repository.
+
+Independent of UxPlay, but used by it and bundled with it:
+
+- **Fedor Indutny** (of Node.js, and formerly Joyent, Inc) and
+ contributors: Created an http parsing library called
+ [llhttp](https://github.com/nodejs/llhttp). Located at
+ `lib/llhttp/`. License: MIT
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..0ee38e9
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,1956 @@
+# UxPlay 1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows).
+
+### **Now developed at the GitHub site (where ALL user issues should be posted, and latest versions can be found).**
+
+- ***NEW in v1.71**: Support for (YouTube) HLS (HTTP Live Streaming)
+ video with the new "-hls" option.* Click on the airplay icon in the
+ YouTube app to stream video. (You may need to wait until
+ advertisements have finished or been skipped before clicking the
+ YouTube airplay icon.) **Please report any issues with this new
+ feature of UxPlay**.
+
+## Highlights:
+
+- GPLv3, open source.
+- Originally supported only AirPlay Mirror protocol, now has added
+ support for AirPlay Audio-only (Apple Lossless ALAC) streaming from
+ current iOS/iPadOS clients. **Now with support for Airplay HLS
+ video-streaming (currently only YouTube video).**
+- macOS computers (2011 or later, both Intel and "Apple Silicon" M1/M2
+ systems) can act either as AirPlay clients, or as the server running
+ UxPlay. Using AirPlay, UxPlay can emulate a second display for macOS
+ clients.
+- Support for older iOS clients (such as 32-bit iPad 2nd gen., iPod
+ Touch 5th gen. and iPhone 4S, when upgraded to iOS 9.3.5, or later
+ 64-bit devices), plus a Windows AirPlay-client emulator, AirMyPC.
+- Uses GStreamer plugins for audio and video rendering (with options
+ to select different hardware-appropriate output "videosinks" and
+ "audiosinks", and a fully-user-configurable video streaming
+ pipeline).
+- Support for server behind a firewall.
+- Raspberry Pi support **both with and without hardware video
+ decoding** by the Broadcom GPU. *Tested on Raspberry Pi Zero 2 W, 3
+ Model B+, 4 Model B, and 5.*
+- Support for running on Microsoft Windows (builds with the MinGW-64
+ compiler in the unix-like MSYS2 environment).
+
+Note: AirPlay2 multi-room audio streaming is not supported: use
+[shairport-sync](https://github.com/mikebrady/shairport-sync) for that.
+
+## Packaging status (Linux and \*BSD distributions)
+
+[](https://repology.org/project/uxplay/versions).
+
+- Install uxplay on Debian-based Linux systems with
+ "`sudo apt install uxplay`"; on FreeBSD with
+ "`sudo pkg install uxplay`". Also available on Arch-based systems
+ through AUR. Since v. 1.66, uxplay is now also packaged in RPM
+ format by Fedora 38 ("`sudo dnf install uxplay`").
+
+- For other RPM-based distributions which have not yet packaged
+ UxPlay, a RPM "specfile" **uxplay.spec** is now provided with recent
+ [releases](https://github.com/FDH2/UxPlay/releases) (see their
+ "Assets"), and can also be found in the UxPlay source top directory.
+ See the section on using this specfile for [building an installable
+ RPM package](#building-an-installable-rpm-package).
+
+After installation:
+
+- (On Linux and \*BSD): if a firewall is active on the server hosting
+ UxPlay, make sure the default network port (UDP 5353) for
+ mDNS/DNS-SD queries is open (see [Troubleshooting](#troubleshooting)
+ below for more details); also open three UDP and three TCP ports for
+ Uxplay, and use the "uxplay -p ``{=html}" option (see
+ "`man uxplay`" or "`uxplay -h`").
+
+- Even if you install your distribution's pre-compiled uxplay binary
+ package, you may need to read the instructions below for [running
+ UxPlay](#running-uxplay) to see which of your distribution's
+ **GStreamer plugin packages** you should also install.
+
+- For Audio-only mode (Apple Music, etc.) best quality is obtained
+ with the option "uxplay -async", but there is then a 2 second
+ latency imposed by iOS.
+
+- Add any UxPlay options you want to use as defaults to a startup file
+ `~/.uxplayrc` (see "`man uxplay`" or "`uxplay -h`" for format and
+ other possible locations). In particular, if your system uses
+ PipeWire audio or Wayland video systems, you may wish to add "as
+ pipewiresink" or "vs waylandsink" as defaults to the file. *(Output
+ from terminal commands "ps waux \| grep pulse" or "pactl info" will
+ contain "pipewire" if your Linux/BSD system uses it).*
+
+- On Raspberry Pi: models using hardware h264 video decoding by the
+ Broadcom GPU (models 4B and earlier) may require the uxplay option
+ -bt709. If you use Ubuntu 22.10 or earlier, GStreamer must be
+ [patched](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches)
+ to use hardware video decoding by the Broadcom GPU (also recommended
+ but optional for Raspberry Pi OS (Bullseye): the patched GStreamer
+ does not need option " -bt709\`". The need for -bt709 when hardware
+ video decoding is used seems to have reappeared starting with
+ GStreamer-1.22.
+
+To (easily) compile the latest UxPlay from source, see the section
+[Getting UxPlay](#getting-uxplay).
+
+# Detailed description of UxPlay
+
+This project is a GPLv3 open source unix AirPlay2 Mirror server for
+Linux, macOS, and \*BSD. It was initially developed by
+[antimof](http://github.com/antimof/Uxplay) using code from
+OpenMAX-based [RPiPlay](https://github.com/FD-/RPiPlay), which in turn
+derives from [AirplayServer](https://github.com/KqsMea8/AirplayServer),
+[shairplay](https://github.com/juhovh/shairplay), and
+[playfair](https://github.com/EstebanKubata/playfair). (The antimof site
+is no longer involved in development, but periodically posts updates
+pulled from the new main [UxPlay site](https://github.com/FDH2/UxPlay)).
+
+UxPlay is tested on a number of systems, including (among others) Debian
+(10 "Buster", 11 "Bullseye", 12 "Bookworm"), Ubuntu (20.04 LTS, 22.04
+LTS, 23.04 (also Ubuntu derivatives Linux Mint, Pop!\_OS), Red Hat and
+clones (Fedora 38, Rocky Linux 9.2), openSUSE Leap 15.5, Mageia 9,
+OpenMandriva "ROME", PCLinuxOS, Arch Linux, Manjaro, and should run on
+any Linux system. Also tested on macOS Catalina and Ventura (Intel) and
+Sonoma (M2), FreeBSD 14.0, Windows 10 and 11 (64 bit).
+
+On Raspberry Pi 4 model B, it is tested on Raspberry Pi OS (Bullseye and
+Bookworm) (32- and 64-bit), Ubuntu 22.04 LTS and 23.04, Manjaro RPi4
+23.02, and (without hardware video decoding) on openSUSE 15.5. Also
+tested on Raspberry Pi Zero 2 W, 3 model B+, and now 5.
+
+Its main use is to act like an AppleTV for screen-mirroring (with audio)
+of iOS/iPadOS/macOS clients (iPhone, iPod Touch, iPad, Mac computers) on
+the server display of a host running Linux, macOS, or other unix (and
+now also Microsoft Windows). UxPlay supports Apple's AirPlay2 protocol
+using "Legacy Protocol", but some features are missing. (Details of what
+is publicly known about Apple's AirPlay 2 protocol can be found
+[here](https://openairplay.github.io/airplay-spec/),
+[here](https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol)
+and [here](https://emanuelecozzi.net/docs/airplay2); see also
+[pyatv](https://pyatv.dev/documentation/protocols) which could be a
+resource for adding modern protocols.) While there is no guarantee that
+future iOS releases will keep supporting "Legacy Protocol", iOS 17
+continues support.
+
+The UxPlay server and its client must be on the same local area network,
+on which a **Bonjour/Zeroconf mDNS/DNS-SD server** is also running (only
+DNS-SD "Service Discovery" service is strictly necessary, it is not
+necessary that the local network also be of the ".local" mDNS-based
+type). On Linux and BSD Unix servers, this is usually provided by
+[Avahi](https://www.avahi.org), through the avahi-daemon service, and is
+included in most Linux distributions (this service can also be provided
+by macOS, iOS or Windows servers).
+
+Connections to the UxPlay server by iOS/MacOS clients can be initiated
+both in **AirPlay Mirror** mode (which streams lossily-compressed AAC
+audio while mirroring the client screen, or in the alternative **AirPlay
+Audio** mode which streams Apple Lossless (ALAC) audio without screen
+mirroring. In **Audio** mode, metadata is displayed in the uxplay
+terminal; if UxPlay option `-ca ` is used, the accompanying cover
+art is also output to a periodically-updated file ``, and can be
+viewed with a (reloading) graphics viewer of your choice. *Switching
+between* **Mirror** *and* **Audio** *modes during an active connection
+is possible: in* **Mirror** *mode, stop mirroring (or close the mirror
+window) and start an* **Audio** *mode connection, switch back by
+initiating a* **Mirror** *mode connection; cover-art display
+stops/restarts as you leave/re-enter* **Audio** *mode.*
+
+- **Note that Apple video-DRM (as found in "Apple TV app" content on
+ the client) cannot be decrypted by UxPlay, and the Apple TV app
+ cannot be watched using UxPlay's AirPlay Mirror mode (only the
+ unprotected audio will be streamed, in AAC format).**
+
+- **With the new "-hls" option, UxPlay now also supports non-Mirror
+ AirPlay video streaming (where the client controls a web server on
+ the AirPlay server that directly receives HLS content to avoid it
+ being decoded and re-encoded by the client). This currently only
+ supports streaming of YouTube videos. Without the -hls option, using
+ the icon for AirPlay video in apps such as the YouTube app will only
+ send audio (in lossless ALAC format) without the accompanying
+ video.**
+
+### Possibility for using hardware-accelerated h264/h265 video-decoding, if available.
+
+UxPlay uses [GStreamer](https://gstreamer.freedesktop.org) "plugins" for
+rendering audio and video. This means that video and audio are supported
+"out of the box", using a choice of plugins. AirPlay streams video in
+h264 format: gstreamer decoding is plugin agnostic, and uses accelerated
+GPU hardware h264 decoders if available; if not, software decoding is
+used.
+
+- **VAAPI for Intel and AMD integrated graphics, NVIDIA with "Nouveau"
+ open-source driver**
+
+ With an Intel or AMD GPU, hardware decoding with the open-source
+ VAAPI gstreamer plugin is preferable. The open-source "Nouveau"
+ drivers for NVIDIA graphics are also in principle supported: see
+ [here](https://nouveau.freedesktop.org/VideoAcceleration.html), but
+ this requires VAAPI to be supplemented with firmware extracted from
+ the proprietary NVIDIA drivers.
+
+- **NVIDIA with proprietary drivers**
+
+ The `nvh264dec` plugin (included in gstreamer1.0-plugins-bad since
+ GStreamer-1.18.0) can be used for accelerated video decoding on the
+ NVIDIA GPU after NVIDIA's CUDA driver `libcuda.so` is installed. For
+ GStreamer-1.16.3 or earlier, the plugin is called `nvdec`, and must
+ be [built by the
+ user](https://github.com/FDH2/UxPlay/wiki/NVIDIA-nvdec-and-nvenc-plugins).
+
+- **Video4Linux2 support for h264 hardware decoding on Raspberry Pi
+ (Pi 4B and older)**
+
+ Raspberry Pi (RPi) computers (tested on Pi 4 Model B) can now run
+ UxPlay using software video decoding, but hardware-accelerated
+ h264/h265 decoding by firmware in the Pi's Broadcom 2835 GPU is
+ prefered. UxPlay accesses this using the GStreamer-1.22 Video4Linux2
+ (v4l2) plugin; Uses the out-of-mainline Linux kernel module
+ bcm2835-codec maintained by Raspberry Pi, so far only included in
+ Raspberry Pi OS, and two other distributions (Ubuntu, Manjaro)
+ available with Raspberry Pi Imager. *(For GStreamer \< 1.22, see the
+ [UxPlay
+ Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches))*.
+ Pi model 5 has no support for hardware H264 decoding, as its CPU is
+ powerful enough for satisfactory software H264 decoding
+
+- **Support for h265 (HEVC) hardware decoding on Raspberry Pi (Pi 4
+ model B and Pi 5)**
+
+ These Raspberry Pi models have a dedicated HEVC decoding block (not
+ the GPU), with a driver "rpivid" which is not yet in the mainline
+ Linux kernel (but is planned to be there in future). Unfortunately
+ it produces decoded video in a non-standard pixel format (NC30 or
+ "SAND") which will not be supported by GStreamer until the driver is
+ in the mainline kernel; without this support, UxPlay support for
+ HEVC hardware decoding on Raspberry Pi will not work.
+
+### Note to packagers:
+
+UxPlay's GPLv3 license does not have an added "GPL exception" explicitly
+allowing it to be distributed in compiled form when linked to OpenSSL
+versions **prior to v. 3.0.0** (older versions of OpenSSL have a license
+clause incompatible with the GPL unless OpenSSL can be regarded as a
+"System Library", which it is in \*BSD). Many Linux distributions treat
+OpenSSL as a "System Library", but some (e.g. Debian) do not: in this
+case, the issue is solved by linking with OpenSSL-3.0.0 or later.
+
+# Getting UxPlay
+
+Either download and unzip
+[UxPlay-master.zip](https://github.com/FDH2/UxPlay/archive/refs/heads/master.zip),
+or (if git is installed): "git clone https://github.com/FDH2/UxPlay".
+You can also download a recent or earlier version listed in
+[Releases](https://github.com/FDH2/UxPlay/releases).
+
+- A recent UxPlay can also be found on the original [antimof
+ site](https://github.com/antimof/UxPlay); that original project is
+ inactive, but is usually kept current or almost-current with the
+ [active UxPlay github site](https://github.com/FDH2/UxPlay) (thank
+ you antimof!).
+
+## Building UxPlay on Linux (or \*BSD):
+
+### Debian-based systems:
+
+(Adapt these instructions for non-Debian-based Linuxes or \*BSD; for
+macOS, see specific instruction below). See
+[Troubleshooting](#troubleshooting) below for help with any
+difficulties.
+
+You need a C/C++ compiler (e.g. g++) with the standard development
+libraries installed. Debian-based systems provide a package
+"build-essential" for use in compiling software. You also need
+pkg-config: if it is not found by "`which pkg-config`", install
+pkg-config or its work-alike replacement pkgconf. Also make sure that
+cmake\>=3.10 is installed: "`sudo apt install cmake`" (add
+`build-essential` and `pkg-config` (or `pkgconf`) to this if needed).
+
+Make sure that your distribution provides OpenSSL 1.1.1 or later, and
+libplist 2.0 or later. (This means Debian 10 "Buster" based systems
+(e.g, Ubuntu 18.04) or newer; on Debian 10 systems "libplist" is an
+older version, you need "libplist3".) If it does not, you may need to
+build and install these from source (see instructions at the end of this
+README).
+
+If you have a non-standard OpenSSL installation, you may need to set the
+environment variable OPENSSL_ROOT_DIR (*e.g.* ,
+"`export OPENSSL_ROOT_DIR=/usr/local/lib64`" if that is where it is
+installed). Similarly, for non-standard (or multiple) GStreamer
+installations, set the environment variable GSTREAMER_ROOT_DIR to the
+directory that contains the ".../gstreamer-1.0/" directory of the
+gstreamer installation that UxPlay should use (if this is *e.g.*
+"\~/my_gstreamer/lib/gstreamer-1.0/", set this location with
+"`export GSTREAMER_ROOT_DIR=$HOME/my_gstreamer/lib`").
+
+- Most users will use the GStreamer supplied by their distribution,
+ but a few (in particular users of Raspberry Pi OS Lite Legacy
+ (Buster) on a Raspberry Pi model 4B who wish to stay on that
+ unsupported Legacy OS for compatibility with other apps) should
+ instead build a newer Gstreamer from source following [these
+ instructions](https://github.com/FDH2/UxPlay/wiki/Building-latest-GStreamer-from-source-on-distributions-with-older-GStreamer-(e.g.-Raspberry-Pi-OS-).)
+ . **Do this *before* building UxPlay**.
+
+In a terminal window, change directories to the source directory of the
+downloaded source code ("UxPlay-\*", "\*" = "master" or the release tag
+for zipfile downloads, "UxPlay" for "git clone" downloads), then follow
+the instructions below:
+
+**Note:** By default UxPlay will be built with optimization for the
+computer it is built on; when this is not the case, as when you are
+packaging for a distribution, use the cmake option
+`-DNO_MARCH_NATIVE=ON`.
+
+If you use X11 Windows on Linux or \*BSD, and wish to toggle in/out of
+fullscreen mode with a keypress (F11 or Alt_L+Enter) UxPlay needs to be
+built with a dependence on X11. Starting with UxPlay-1.59, this will be
+done by default **IF** the X11 development libraries are installed and
+detected. Install these with "`sudo apt install libx11-dev`". If
+GStreamer \< 1.20 is detected, a fix needed by screen-sharing apps
+(*e.g.*, Zoom) will also be made.
+
+- If X11 development libraries are present, but you wish to build
+ UxPlay *without* any X11 dependence, use the cmake option
+ `-DNO_X11_DEPS=ON`.
+
+1. `sudo apt install libssl-dev libplist-dev`". (*unless you need to
+ build OpenSSL and libplist from source*).
+2. `sudo apt install libavahi-compat-libdnssd-dev`
+3. `sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev`.
+ (\**Skip if you built Gstreamer from source*)
+4. `cmake .` (*For a cleaner build, which is useful if you modify the
+ source, replace this by* "`mkdir build; cd build; cmake ..`": *you
+ can then delete the contents of the `build` directory if needed,
+ without affecting the source.*) Also add any cmake "`-D`" options
+ here as needed (e.g, `-DNO_X11_DEPS=ON` or `-DNO_MARCH_NATIVE=ON`).
+5. `make`
+6. `sudo make install` (you can afterwards uninstall with
+ `sudo make uninstall` in the same directory in which this was run).
+
+This installs the executable file "`uxplay`" to `/usr/local/bin`, (and
+installs a manpage to somewhere standard like
+`/usr/local/share/man/man1` and README files to somewhere like
+`/usr/local/share/doc/uxplay`). (If "man uxplay" fails, check if
+\$MANPATH is set: if so, the path to the manpage (usually
+/usr/local/share/man/) needs to be added to \$MANPATH .) The uxplay
+executable can also be found in the build directory after the build
+process, if you wish to test before installing (in which case the
+GStreamer plugins must first be installed).
+
+### Building on non-Debian Linux and \*BSD
+
+\*\*For those with RPM-based distributions, a RPM spec file uxplay.spec
+is also available: see [Building an installable rpm
+package](#building-an-installable-rpm-package).
+
+- **Red Hat, or clones like CentOS (now continued as Rocky Linux or
+ Alma Linux):** (sudo dnf install, or sudo yum install) openssl-devel
+ libplist-devel avahi-compat-libdns_sd-devel gstreamer1-devel
+ gstreamer1-plugins-base-devel (+libX11-devel for fullscreen X11)
+ *(some of these may be in the "CodeReady" add-on repository, called
+ "PowerTools" by clones)*
+
+- **Mageia, PCLinuxOS, OpenMandriva:** Same as Red Hat, except for
+ name changes: (Mageia) "gstreamer1.0-devel",
+ "gstreamer-plugins-base1.0-devel"; (OpenMandriva)
+ "libopenssl-devel", "gstreamer-devel",
+ "libgst-plugins-base1.0-devel". PCLinuxOS: same as Mageia, but uses
+ synaptic (or apt) as its package manager.
+
+- **openSUSE:** (sudo zypper install) libopenssl-3-devel (formerly
+ libopenssl-devel) libplist-2_0-devel (formerly libplist-devel)
+ avahi-compat-mDNSResponder-devel gstreamer-devel
+ gstreamer-plugins-base-devel (+ libX11-devel for fullscreen X11).
+
+- **Arch Linux** (*Also available as a package in AUR*): (sudo pacman
+ -Syu) openssl libplist avahi gst-plugins-base.
+
+- **FreeBSD:** (sudo pkg install) libplist gstreamer1. Either
+ avahi-libdns or mDNSResponder must also be installed to provide the
+ dns_sd library. OpenSSL is already installed as a System Library.
+
+#### Building an installable RPM package
+
+First-time RPM builders should first install the rpm-build and
+rpmdevtools packages, then create the rpmbuild tree with
+"`rpmdev-setuptree`". Then download and copy uxplay.spec into
+`~/rpmbuild/SPECS`. In that directory, run
+"`rpmdev-spectool -g -R uxplay.spec`" to download the corresponding
+source file `uxplay-*.tar.gz` into `~/rpmbuild/SOURCES`
+("rpmdev-spectool" may also be just called "spectool"); then run
+"`rpmbuild -ba uxplay.spec`" (you will need to install any required
+dependencies this reports). This should create the uxplay RPM package in
+a subdirectory of `~/rpmbuild/RPMS`. (**uxplay.spec** is tested on
+Fedora 38, Rocky Linux 9.2, openSUSE Leap 15.5, Mageia 9, OpenMandriva,
+PCLinuxOS; it can be easily modified to include dependency lists for
+other RPM-based distributions.)
+
+## Running UxPlay
+
+### Installing plugins (Debian-based Linux distributions, including Ubuntu and Raspberry Pi OS) (*skip if you built a complete GStreamer from source*)
+
+Next install the GStreamer plugins that are needed with
+`sudo apt install gstreamer1.0-`. Values of `` required
+are:
+
+1. "**plugins-base**"
+2. "**libav**" (for sound),
+3. "**plugins-good**" (for v4l2 hardware h264 decoding)
+4. "**plugins-bad**" (for h264 decoding).
+
+**Debian-based distributions split some of the plugin packages into
+smaller pieces:** some that may also be needed include "**gl**" for
+OpenGL support (this provides the "-vs glimagesink" videosink, which can
+be very useful in many systems (including Raspberry Pi), and should
+always be used when using h264/h265 decoding by a NVIDIA GPU),
+"**gtk3**" (which provides the "-vs gtksink" videosink), and "**x**" for
+X11 support, although these may already be installed; "**vaapi**" is
+needed for hardware-accelerated h264 video decoding by Intel or AMD
+graphics (but not for use with NVIDIA using proprietary drivers). If
+sound is not working, "**alsa**"","**pulseaudio**", or "**pipewire**"
+plugins may need to be installed, depending on how your audio is set up.
+
+- Also install "**gstreamer1.0-tools**" to get the utility
+ gst-inspect-1.0 for examining the GStreamer installation.
+
+### Installing plugins (Non-Debian-based Linux or \*BSD) (*skip if you built a complete GStreamer from source*)
+
+In some cases, because of patent issues, the libav plugin feature
+**avdec_aac** needed for decoding AAC audio in mirror mode is not
+provided in the official distribution: get it from community
+repositories for those distributions.
+
+- **Red Hat, or clones like CentOS (now continued as Rocky Linux or
+ Alma Linux):** Install gstreamer1-libav gstreamer1-plugins-bad-free
+ (+ gstreamer1-vaapi for Intel/AMD graphics). In recent Fedora,
+ gstreamer1-libav is renamed gstreamer1-plugin-libav. **To get
+ avdec_aac, install packages from
+ [rpmfusion.org](https://rpmfusion.org)**: (get ffmpeg-libs from
+ rpmfusion; on RHEL or clones, but not recent Fedora, also get
+ gstreamer1-libav from there).
+
+- **Mageia, PCLinuxOS, OpenMandriva:** Install gstreamer1.0-libav
+ gstreamer1.0-plugins-bad (+ gstreamer1.0-vaapi for Intel/AMD
+ graphics). **On Mageia, to get avdec_aac, install ffmpeg from the
+ "tainted" repository**, (which also provides a more complete
+ gstreamer1.0-plugins-bad).
+
+- **openSUSE:** Install gstreamer-plugins-libav gstreamer-plugins-bad
+ (+ gstreamer-plugins-vaapi for Intel/AMD graphics). **To get
+ avdec_aac, install libav\* packages for openSUSE from
+ [Packman](https://ftp.gwdg.de/pub/linux/misc/packman/suse/)
+ "Essentials"**; recommendation: after adding the Packman repository,
+ use the option in YaST Software management to switch all system
+ packages for multimedia to Packman).
+
+- **Arch Linux** Install gst-plugins-good gst-plugins-bad gst-libav (+
+ gstreamer-vaapi for Intel/AMD graphics).
+
+- **FreeBSD:** Install gstreamer1-libav, gstreamer1-plugins,
+ gstreamer1-plugins-\* (\* = core, good, bad, x, gtk, gl, vulkan,
+ pulse, v4l2, ...), (+ gstreamer1-vaapi for Intel/AMD graphics).
+
+### Starting and running UxPlay
+
+Since UxPlay-1.64, UxPlay can be started with options read from a
+configuration file, which will be the first found of (1) a file with a
+path given by environment variable `$UXPLAYRC`, (2) `~/.uxplayrc` in the
+user's home directory ("\~"), (3) `~/.config/uxplayrc`. The format is
+one option per line, omitting the initial `"-"` of the command-line
+option. Lines in the configuration file beginning with `"#"` are treated
+as comments and ignored.
+
+**Run uxplay in a terminal window**. On some systems, you can specify
+fullscreen mode with the `-fs` option, or toggle into and out of
+fullscreen mode with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C
+(or close the window) to terminate it when done. If the UxPlay server is
+not seen by the iOS client's drop-down "Screen Mirroring" panel, check
+that your DNS-SD server (usually avahi-daemon) is running: do this in a
+terminal window with `systemctl status avahi-daemon`. If this shows the
+avahi-daemon is not running, control it with
+`sudo systemctl [start,stop,enable,disable] avahi-daemon` (on
+non-systemd systems, such as \*BSD, use
+`sudo service avahi-daemon [status, start, stop, restart, ...]`). If
+UxPlay is seen, but the client fails to connect when it is selected,
+there may be a firewall on the server that prevents UxPlay from
+receiving client connection requests unless some network ports are
+opened: **if a firewall is active, also open UDP port 5353 (for mDNS
+queries) needed by Avahi**. See [Troubleshooting](#troubleshooting)
+below for help with this or other problems.
+
+- Unlike an Apple TV, the UxPlay server does not by default require
+ clients to initially "pair" with it using a pin code displayed by
+ the server (after which the client "trusts" the server, and does not
+ need to repeat this). Since v1.67, Uxplay offers such
+ "pin-authentication" as an option: see "`-pin`" and "`-reg`" in
+ [Usage](#usage) for details, if you wish to use it. *Some clients
+ with MDM (Mobile Device Management, often present on employer-owned
+ devices) are required to use pin-authentication: UxPlay will provide
+ this even when running without the pin option.*
+
+- By default, UxPlay is locked to its current client until that client
+ drops the connection; since UxPlay-1.58, the option `-nohold`
+ modifies this behavior so that when a new client requests a
+ connection, it removes the current client and takes over. UxPlay
+ 1.66 introduces a mechanism ( `-restrict`, `-allow `,
+ `-block `) to control which clients are allowed to connect,
+ using their "deviceID" (which in Apple devices appears to be
+ immutable).
+
+- In Mirror mode, GStreamer has a choice of **two** methods to play
+ video with its accompanying audio: prior to UxPlay-1.64, the video
+ and audio streams were both played as soon as possible after they
+ arrived (the GStreamer "*sync=false*" method), with a GStreamer
+ internal clock used to try to keep them synchronized. **Starting
+ with UxPlay-1.64, the other method (GStreamer's "*sync=true*" mode),
+ which uses timestamps in the audio and video streams sent by the
+ client, is the new default**. On low-decoding-power UxPlay hosts
+ (such as Raspberry Pi Zero W or 3 B+ models) this will drop video
+ frames that cannot be decoded in time to play with the audio, making
+ the video jerky, but still synchronized.
+
+The older method which does not drop late video frames worked well on
+more powerful systems, and is still available with the UxPlay option
+"`-vsync no`"; this method is adapted to "live streaming", and may be
+better when using UxPlay as a second monitor for a Mac computer, for
+example, while the new default timestamp-based method is best for
+watching a video, to keep lip movements and voices synchronized.
+(Without use of timestamps, video will eventually lag behind audio if it
+cannot be decoded fast enough: hardware-accelerated video-decoding
+helped to prevent this previously when timestamps were not being used.)
+
+- In Audio-only mode the GStreamer "sync=false" mode (not using
+ timestamps) is still the default, but if you want to keep the audio
+ playing on the server synchronized with the video showing on the
+ client, use the `-async` timestamp-based option. (An example might
+ be if you want to follow the Apple Music lyrics on the client while
+ listening to superior sound on the UxPlay server). This delays the
+ video on the client to match audio on the server, so leads to a
+ slight delay before a pause or track-change initiated on the client
+ takes effect on the audio played by the server.
+
+AirPlay volume-control attenuates volume (gain) by up to -30dB: the
+decibel range -30:0 can be rescaled from *Low*:0, or *Low*:*High*, using
+the option `-db` ("-db *Low*" or "-db *Low*:*High*"), *Low* must be
+negative. Rescaling is linear in decibels. Note that GStreamer's audio
+format will "clip" any audio gain above +20db, so keep *High* below that
+level. The option `-taper` provides a "tapered" AirPlay volume-control
+profile some users may prefer.
+
+The -vsync and -async options also allow an optional positive (or
+negative) audio-delay adjustment in *milliseconds* for fine-tuning :
+`-vsync 20.5` delays audio relative to video by 0.0205 secs; a negative
+value advances it.)
+
+- you may find video is improved by the setting -fps 60 that allows
+ some video to be played at 60 frames per second. (You can see what
+ framerate is actually streaming by using -vs fpsdisplaysink, and/or
+ -FPSdata.) When using this, you should use the default
+ timestamp-based synchronization option `-vsync`.
+
+- Since UxPlay-1.54, you can display the accompanying "Cover Art" from
+ sources like Apple Music in Audio-Only (ALAC) mode: run
+ "`uxplay -ca &`" in the background, then run a image viewer
+ with an autoreload feature: an example is "feh": run
+ "`feh -R 1 `" in the foreground; terminate feh and then Uxplay
+ with "`ctrl-C fg ctrl-C`".
+
+By default, GStreamer uses an algorithm to search for the best
+"videosink" (GStreamer's term for a graphics driver to display images)
+to use. You can overide this with the uxplay option `-vs `.
+Which videosinks are available depends on your operating system and
+graphics hardware: use
+"`gst-inspect-1.0 | grep sink | grep -e video -e Video -e image`" to see
+what is available. Some possibilites on Linux/\*BSD are:
+
+- **glimagesink** (OpenGL), **waylandsink**
+
+- **xvimagesink**, **ximagesink** (X11)
+
+- **kmssink**, **fbdevsink** (console graphics without X11)
+
+- **vaapisink** (for Intel/AMD hardware-accelerated graphics); for
+ NVIDIA hardware graphics (with CUDA) use **glimagesink** combined
+ with "`-vd nvh264dec`" (or "nvh264sldec", a new variant which will
+ become "nvh264dec" in GStreamer-1.24).
+
+- If the server is "headless" (no attached monitor, renders audio
+ only) use `-vs 0`.
+
+Note that videosink options can set using quoted arguments to -vs:
+*e.g.*, `-vs "xvimagesink display=:0"`: ximagesink and xvimagesink allow
+an X11 display name to be specified, and waylandsink has a similar
+option. Videosink options ("properties") can be found in their GStreamer
+description pages,such as
+https://gstreamer.freedesktop.org/documentation/xvimagesink .
+
+GStreamer also searches for the best "audiosink"; override its choice
+with `-as `. Choices on Linux include pulsesink, alsasink,
+pipewiresink, oss4sink; see what is available with
+`gst-inspect-1.0 | grep sink | grep -e audio -e Audio`.
+
+**One common problem involves GStreamer attempting to use
+incorrectly-configured or absent accelerated hardware h264 video
+decoding (e.g., VAAPI). Try "`uxplay -avdec`" to force software video
+decoding; if this works you can then try to fix accelerated hardware
+video decoding if you need it, or just uninstall the GStreamer vaapi
+plugin.**
+
+See [Usage](#usage) for more run-time options.
+
+### **Special instructions for Raspberry Pi (tested on Raspberry Pi Zero 2 W, 3 Model B+, 4 Model B, and 5 only)**:
+
+- For Framebuffer video (for Raspberry Pi OS "Lite" and other non-X11
+ distributions) use the KMS videosink "-vs kmssink" (the DirectFB
+ framebuffer videosink "dfbvideosink" is broken on the Pi, and
+ segfaults). *In this case you should explicitly use the "-vs
+ kmssink" option, as without it, autovideosink does not find the
+ correct videosink.*
+
+- Raspberry Pi 5 does not provide hardware H264 decoding (and does not
+ need it).
+
+- Pi Zero 2 W, 3 Model B+ and 4 Model B should use hardware H264
+ decoding by the Broadcom GPU, but it requires an out-of-mainstream
+ kernel module bcm2835_codec maintained in the [Raspberry Pi kernel
+ tree](https://github.com/raspberrypi/linux); distributions that are
+ known to supply it include Raspberry Pi OS, Ubuntu, and
+ Manjaro-RPi4. Use software decoding (option -avdec) if this module
+ is not available.
+
+- Uxplay uses the Video4Linux2 (v4l2) plugin from GStreamer-1.22 and
+ later to access the GPU, if hardware H264 decoding is used. This
+ should happen automatically. The option -v4l2 can be used, but it is
+ usually best to just let GStreamer find the best video pipeline by
+ itself.
+
+- On older distributions (GStreamer \< 1.22), the v4l2 plugin needs a
+ patch: see the [UxPlay
+ Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches).
+ Legacy Raspberry Pi OS (Bullseye) has a partially-patched
+ GStreamer-1.18.4 which needs the uxplay option -bt709 (and don't use
+ -v4l2); it is still better to apply the full patch from the UxPlay
+ Wiki in this case.
+
+- **It appears that when hardware h264 video decoding is used, the
+ option -bt709 became needed again in GStreamer-1.22 and later.**
+
+- For "double-legacy" Raspberry Pi OS (Buster), there is no patch for
+ GStreamer-1.14. Instead, first build a complete newer
+ GStreamer-1.18.6 from source using [these
+ instructions](https://github.com/FDH2/UxPlay/wiki/Building-latest-GStreamer-from-source-on-distributions-with-older-GStreamer-(e.g.-Raspberry-Pi-OS-).)
+ before building UxPlay.
+
+- Raspberry Pi 3 Model B+ running a 32 bit OS can also access the GPU
+ with the GStreamer OMX plugin (use option "`-vd omxh264dec`"), but
+ this is broken by Pi 4 Model B firmware. OMX support was removed
+ from Raspberry Pi OS (Bullseye), but is present in Buster.
+
+- **H265 (4K)** video is potentially supported by hardware decoding on
+ Raspberry Pi 5 models, as well as on Raspberry Pi 4 model B, using a
+ dedicated HEVC decoding block, but the "rpivid" kernel driver for
+ this is not yet supported by GStreamer (this driver decodes video
+ into a non-standard format that cannot be supported by GStreamer
+ until the driver is in the mainline Linux kernel). Raspberry Pi
+ provides a version of ffmpeg that can use that format, but at
+ present UxPlay cannot use this. The best solution would be for the
+ driver to be "upstreamed" to the kernel, allowing GStreamer support.
+ (Software HEVC decoding works, but does not seem to give
+ satisfactory results on the Pi).
+
+Even with GPU video decoding, some frames may be dropped by the
+lower-power models to keep audio and video synchronized using
+timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config
+"Performance Options" allows specifying how much memory to allocate to
+the GPU, but this setting appears to be absent in Bookworm (but it can
+still be set to e.g. 128MB by adding a line "gpu_mem=128" in
+/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well
+when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to
+the GPU (default seems to be 64MB).
+
+The basic uxplay options for R Pi are `uxplay [-vs ]`. The
+choice `` = `glimagesink` is sometimes useful. With the
+Wayland video compositor, use `` = `waylandsink`. With
+framebuffer video, use `` = `kmssink`.
+
+- Tip: to start UxPlay on a remote host (such as a Raspberry Pi) using
+ ssh:
+
+```{=html}
+
+```
+ ssh user@remote_host
+ export DISPLAY=:0
+ nohup uxplay [options] > FILE &
+
+Sound and video will play on the remote host; "nohup" will keep uxplay
+running if the ssh session is closed. Terminal output is saved to FILE
+(which can be /dev/null to discard it)
+
+## Building UxPlay on macOS: **(Intel X86_64 and "Apple Silicon" M1/M2 Macs)**
+
+*Note: A native AirPlay Server feature is included in macOS 12 Monterey,
+but is restricted to recent hardware. UxPlay can run on older macOS
+systems that will not be able to run Monterey, or can run Monterey but
+not AirPlay.*
+
+These instructions for macOS assume that the Xcode command-line
+developer tools are installed (if Xcode is installed, open the Terminal,
+type "sudo xcode-select --install" and accept the conditions).
+
+It is also assumed that CMake \>= 3.13 is installed: this can be done
+with package managers [MacPorts](http://www.macports.org)
+(`sudo port install cmake`), [Homebrew](http://brew.sh)
+(`brew install cmake`), or by a download from
+. Also install `git` if you will use it to
+fetch UxPlay.
+
+Next install libplist and openssl-3.x. Note that static versions of
+these libraries will be used in the macOS builds, so they can be
+uninstalled after building uxplay, if you wish.
+
+- If you use Homebrew: `brew install libplist openssl@3`
+
+- if you use MacPorts: `sudo port install libplist-devel openssl3`
+
+Otherwise, build libplist and openssl from source: see instructions near
+the end of this README; requires development tools (autoconf, automake,
+libtool, *etc.*) to be installed.
+
+Next get the latest macOS release of GStreamer-1.0.
+
+**Using "Official" GStreamer (Recommended for both MacPorts and Homebrew
+users)**: install the GStreamer release for macOS from
+. (This release contains
+its own pkg-config, so you don't have to install one.) Install both the
+gstreamer-1.0 and gstreamer-1.0-devel packages. After downloading,
+Shift-Click on them to install (they install to
+/Library/FrameWorks/GStreamer.framework). Homebrew or MacPorts users
+should **not** install (or should uninstall) the GStreamer supplied by
+their package manager, if they use the "official" release.
+
+- Since GStreamer v1.22, the "Official" (gstreamer.freedesktop.org)
+ macOS binaries require a wrapper "gst_macos_main" around the actual
+ main program (uxplay). This should have been applied during the
+ UxPlay compilation process, and the initial UxPlay terminal message
+ should confirm it is being used. (UxPlay can also be built using
+ "Official" GStreamer v.1.20.7 binaries, which work without the
+ wrapper.)
+
+**Using Homebrew's GStreamer**: pkg-config is needed: ("brew install
+pkg-config gstreamer"). This causes a large number of extra packages to
+be installed by Homebrew as dependencies. The [Homebrew gstreamer
+installation](https://formulae.brew.sh/formula/gstreamer#default) has
+recently been reworked into a single "formula" named `gstreamer`, which
+now works without needing GST_PLUGIN_PATH to be set in the enviroment.
+Homebrew installs gstreamer to `HOMEBREW_PREFIX/lib/gstreamer-1.0` where
+by default `HOMEBREW_PREFIX/*` is `/opt/homebrew/*` on Apple Silicon
+Macs, and `/usr/local/*` on Intel Macs; do not put any extra
+non-Homebrew plugins (that you build yourself) there, and instead set
+GST_PLUGIN_PATH to point to their location (Homebrew does not supply a
+complete GStreamer, but seems to have everything needed for UxPlay).
+**New: the UxPlay build script will now also detect Homebrew
+installations in non-standard locations indicated by the environment
+variable `$HOMEBREW_PREFIX`.**
+
+**Using GStreamer installed from MacPorts**: this is **not**
+recommended, as currently the MacPorts GStreamer is old (v1.16.2),
+unmaintained, and built to use X11:
+
+- Instead [build gstreamer
+ yourself](https://github.com/FDH2/UxPlay/wiki/Building-GStreamer-from-Source-on-macOS-with-MacPorts)
+ if you use MacPorts and do not want to use the "Official" Gstreamer
+ binaries.
+
+*(If you really wish to use the MacPorts GStreamer-1.16.2, install
+pkgconf ("sudo port install pkgconf"), then "sudo port install
+gstreamer1-gst-plugins-base gstreamer1-gst-plugins-good
+gstreamer1-gst-plugins-bad gstreamer1-gst-libav". For X11 support on
+macOS, compile UxPlay using a special cmake option `-DUSE_X11=ON`, and
+run it from an XQuartz terminal with -vs ximagesink; older non-retina
+macs require a lower resolution when using X11: `uxplay -s 800x600`.)*
+
+After installing GStreamer, build and install uxplay: open a terminal
+and change into the UxPlay source directory ("UxPlay-master" for zipfile
+downloads, "UxPlay" for "git clone" downloads) and build/install with
+"cmake . ; make ; sudo make install" (same as for Linux).
+
+- Running UxPlay while checking for GStreamer warnings (do this with
+ "export GST_DEBUG=2" before runnng UxPlay) reveals that with the
+ default (since UxPlay 1.64) use of timestamps for video
+ synchonization, many video frames are being dropped (only on macOS),
+ perhaps due to another error (about videometa) that shows up in the
+ GStreamer warnings. **Recommendation: use the new UxPlay "no
+ timestamp" option "`-vsync no`"** (you can add a line "vsync no" in
+ the uxplayrc configuration file).
+
+- On macOS with this installation of GStreamer, the only videosinks
+ available seem to be glimagesink (default choice made by
+ autovideosink) and osxvideosink. The window title does not show the
+ Airplay server name, but the window is visible to screen-sharing
+ apps (e.g., Zoom). The only available audiosink seems to be
+ osxaudiosink.
+
+- The option -nc is always used, whether or not it is selected. This
+ is a workaround for a problem with GStreamer videosinks on macOS: if
+ the GStreamer pipeline is destroyed while the mirror window is still
+ open, a segfault occurs.
+
+- In the case of glimagesink, the resolution settings "-s wxh" do not
+ affect the (small) initial OpenGL mirror window size, but the window
+ can be expanded using the mouse or trackpad. In contrast, a window
+ created with "-vs osxvideosink" is initially big, but has the wrong
+ aspect ratio (stretched image); in this case the aspect ratio
+ changes when the window width is changed by dragging its side; the
+ option `-vs "osxvideosink force-aspect-ratio=true"` can be used to
+ make the window have the correct aspect ratio when it first opens.
+
+## Building UxPlay on Microsoft Windows, using MSYS2 with the MinGW-64 compiler.
+
+- tested on Windows 10 and 11, 64-bit.
+
+1. Download and install **Bonjour SDK for Windows v3.0**. You can
+ download the SDK without any registration at
+ [softpedia.com](https://www.softpedia.com/get/Programming/SDK-DDK/Bonjour-SDK.shtml),
+ or get it from the official Apple site
+ [https://developer.apple.com/download](https://developer.apple.com/download/all/?q=Bonjour%20SDK%20for%20Windows)
+ (Apple makes you register as a developer to access it from their
+ site). This should install the Bonjour SDK as
+ `C:\Program Files\Bonjour SDK`.
+
+2. (This is for 64-bit Windows; a build for 32-bit Windows should be
+ possible, but is not tested.) The unix-like MSYS2 build environment
+ will be used: download and install MSYS2 from the official site
+ [https://www.msys2.org/](https://www.msys2.org). Accept the default
+ installation location `C:\mysys64`.
+
+3. [MSYS2 packages](https://packages.msys2.org/package/) are installed
+ with a variant of the "pacman" package manager used by Arch Linux.
+ Open a "MSYS2 MINGW64" terminal from the MSYS2 tab in the Windows
+ Start menu, and update the new MSYS2 installation with "pacman
+ -Syu". Then install the **MinGW-64** compiler and **cmake**
+
+ pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc
+
+ The compiler with all required dependencies will be installed in the
+ msys64 directory, with default path `C:/msys64/mingw64`. Here we
+ will simply build UxPlay from the command line in the MSYS2
+ environment (this uses "`ninja`" in place of "`make`" for the build
+ system).
+
+4. Download the latest UxPlay from github **(to use `git`, install it
+ with `pacman -S git`, then
+ "`git clone https://github.com/FDH2/UxPlay`")**, then install UxPlay
+ dependencies (openssl is already installed with MSYS2):
+
+ `pacman -S mingw-w64-x86_64-libplist mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base`
+
+ If you are trying a different Windows build system, MSVC versions of
+ GStreamer for Windows are available from the [official GStreamer
+ site](https://gstreamer.freedesktop.org/download/), but only the
+ MinGW 64-bit build on MSYS2 has been tested.
+
+5. cd to the UxPlay source directory, then "`mkdir build`" and
+ "`cd build`". The build process assumes that the Bonjour SDK is
+ installed at `C:\Program Files\Bonjour SDK`. If it is somewhere
+ else, set the enviroment variable BONJOUR_SDK_HOME to point to its
+ location. Then build UxPlay with
+
+ `cmake ..`
+
+ `ninja`
+
+6. Assuming no error in either of these, you will have built the uxplay
+ executable **uxplay.exe** in the current ("build") directory. The
+ "sudo make install" and "sudo make uninstall" features offered in
+ the other builds are not available on Windows; instead, the MSYS2
+ environment has `/mingw64/...` available, and you can install the
+ uxplay.exe executable in `C:/msys64/mingw64/bin` (plus manpage and
+ documentation in `C:/msys64/mingw64/share/...`) with
+
+ `cmake --install . --prefix /mingw64`
+
+ To be able to view the manpage, you need to install the manpage
+ viewer with "`pacman -S man`".
+
+To run **uxplay.exe** you need to install some gstreamer plugin packages
+with `pacman -S mingw-w64-x86_64-gst-`, where the required ones
+have `` given by
+
+1. **libav**
+2. **plugins-good**
+3. **plugins-bad**
+
+Other possible MSYS2 gstreamer plugin packages you might use are listed
+in [MSYS2 packages](https://packages.msys2.org/package/).
+
+You also will need to grant permission to the uxplay executable
+uxplay.exe to access data through the Windows firewall. You may
+automatically be offered the choice to do this when you first run
+uxplay, or you may need to do it using **Windows Settings-\>Update and
+Security-\>Windows Security-\>Firewall & network protection -\> allow an
+app through firewall**. If your virus protection flags uxplay.exe as
+"suspicious" (but without a true malware signature) you may need to give
+it an exception.
+
+Now test by running "`uxplay`" (in a MSYS2 terminal window). If you need
+to specify the audiosink, there are two main choices on Windows: the
+older DirectSound plugin "`-as directsoundsink`", and the more modern
+Windows Audio Session API (wasapi) plugin "`-as wasapisink`", which
+supports [additional
+options](https://gstreamer.freedesktop.org/documentation/wasapi/wasapisink.html)
+such as
+
+ uxplay -as 'wasapisink device=\"\"'
+
+where `` specifies an available audio device by its GUID, which
+can be found using "`gst-device-monitor-1.0 Audio`": `` has a form
+like `\{0.0.0.00000000\}.\{98e35b2b-8eba-412e-b840-fd2c2492cf44\}`. If
+"`device`" is not specified, the default audio device is used.
+
+If you wish to specify the videosink using the `-vs ` option,
+some choices for `` are `d3d11videosink`, `d3dvideosink`,
+`glimagesink`, `gtksink`.
+
+- With Direct3D 11.0 or greater, you can either always be in
+ fullscreen mode using option
+ `-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true"`,
+ or get the ability to toggle into and out of fullscreen mode using
+ the Alt-Enter key combination with option
+ `-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`. For
+ convenience, these options will be added if just
+ `-vs d3d11videosink` with or without the fullscreen option "-fs" is
+ used. *(Windows users may wish to add "`vs d3d11videosink`" (no
+ initial "`-`") to the UxPlay startup options file; see "man uxplay"
+ or "uxplay -h".)*
+
+The executable uxplay.exe can also be run without the MSYS2 environment,
+in the Windows Terminal, with `C:\msys64\mingw64\bin\uxplay`.
+
+# Usage
+
+Options:
+
+- These can also be written (one option per line, without the initial
+ "`-`" character) in the UxPlay startup file (either given by
+ environment variable `$UXPLAYRC`, or `~/.uxplayrc` or
+ `~/.config/uxplayrc`); lines begining with "`#`" are treated as
+ comments, and ignored. Command line options supersede options in the
+ startup file.
+
+**-n server_name** (Default: UxPlay); server_name@\_hostname\_ will be
+the name that appears offering AirPlay services to your iPad, iPhone
+etc, where *hostname* is the name of the server running uxplay. This
+will also now be the name shown above the mirror display (X11) window.
+
+**-nh** Do not append "@_hostname_" at the end of the AirPlay server
+name.
+
+**-h265** Activate "ScreenMultiCodec" support (AirPlay "Features" bit
+42) for accepting h265 (4K/HEVC) video in addition to h264 video (1080p)
+in screen-mirror mode. When this option is used, two "video pipelines"
+(one for h264, one for h265) are created. If any GStreamer plugins in
+the pipeline are specific for h264 or h265, the correct version will be
+used in each pipeline. A wired Client-Server ethernet connection is
+preferred over Wifi for 4K video, and might be required by the client.
+Only recent Apple devices (M1/M2 Macs or iPads, and some iPhones) can
+send h265 video if a resolution "-s wxh" with h \> 1080 is requested.
+The "-h265" option changes the default resolution ("-s" option) from
+1920x1080 to 3840x2160, and leaves default maximum framerate ("-fps"
+option) at 30fps.
+
+**-hls** Activate HTTP Live Streaming support. With this option YouTube
+videos can be streamed directly from YouTube servers to UxPlay (without
+passing through the client) by clicking on the AirPlay icon in the
+YouTube app.
+
+**-pin \[nnnn\]**: (since v1.67) use Apple-style (one-time) "pin"
+authentication when a new client connects for the first time: a
+four-digit pin code is displayed on the terminal, and the client screen
+shows a login prompt for this to be entered. When "-pin" is used by
+itself, a new random pin code is chosen for each authentication; if
+"-pin nnnn" (e.g., "-pin 3939") is used, this will set an unchanging
+fixed code. Authentication adds the server to the client's list of
+"trusted servers" and the client will not need to reauthenticate
+provided that the client and server public keys remain unchanged. (By
+default since v1.68, the server public key is generated from the MAC
+address, which can be changed with the -m option; see the -key option
+for an alternative method of key generation). *(Add a line "pin" in the
+UxPlay startup file if you wish the UxPlay server to use the pin
+authentication protocol).*
+
+**-reg \[*filename*\]**: (since v1.68). If "-pin" is used, this option
+maintains a register of pin-authenticated "trusted clients" in
+\$HOME/.uxplay.register (or optionally, in *filename*). Without this
+option, returning clients that skip pin-authentication are trusted and
+not checked. This option may be useful if UxPlay is used in a more
+public environment, to record client details; the register is text, one
+line per client, with client's public key (base-64 format), Device ID,
+and Device name; commenting out (with "\#") or deleting a line
+deregisters the corresponding client (see options -restrict, -block,
+-allow for more ways to control client access). *(Add a line "reg" in
+the startup file if you wish to use this feature.)*
+
+**-vsync \[x\]** (In Mirror mode:) this option (**now the default**)
+uses timestamps to synchronize audio with video on the server, with an
+optional audio delay in (decimal) milliseconds (*x* = "20.5" means
+0.0205 seconds delay: positive or negative delays less than a second are
+allowed.) It is needed on low-power systems such as Raspberry Pi without
+hardware video decoding.
+
+**-vsync no** (In Mirror mode:) this switches off timestamp-based
+audio-video synchronization, restoring the default behavior prior to
+UxPlay-1.64. Standard desktop systems seem to work well without use of
+timestamps: this mode is appropriate for "live streaming" such as using
+UxPlay as a second monitor for a mac computer, or monitoring a webcam;
+with it, no video frames are dropped.
+
+**-async \[x\]** (In Audio-Only (ALAC) mode:) this option uses
+timestamps to synchronize audio on the server with video on the client,
+with an optional audio delay in (decimal) milliseconds (*x* = "20.5"
+means 0.0205 seconds delay: positive or negative delays less than a
+second are allowed.) Because the client adds a video delay to account
+for latency, the server in -async mode adds an equivalent audio delay,
+which means that audio changes such as a pause or a track-change will
+not take effect immediately. *This might in principle be mitigated by
+using the `-al` audio latency setting to change the latency (default
+0.25 secs) that the server reports to the client, but at present
+changing this does not seem to have any effect*.
+
+**-async no**. This is the still the default behavior in Audio-only
+mode, but this option may be useful as a command-line option to switch
+off a `-async` option set in a "uxplayrc" configuration file.
+
+**-db *low*\[:*high*\]** Rescales the AirPlay volume-control attenuation
+(gain) from -30dB:0dB to *low*:0dB or *low*:*high*. The lower limit
+*low* must be negative (attenuation); the upper limit *high* can be
+either sign. (GStreamer restricts volume-augmentation by *high* so that
+it cannot exceed +20dB). The rescaling is "flat", so that for -db
+-50:10, a change in Airplay attenuation by -7dB is translated to a -7 x
+(60/30) = -14dB attenuation, and the maximum volume (AirPlay 0dB) is a
+10dB augmentation, and Airplay -30dB would become -50dB. Note that the
+minimum AirPlay value (-30dB exactly) is translated to "mute".
+
+**-taper** Provides a "tapered" Airplay volume-control profile (matching
+the one called "dasl-tapering" in
+[shairport-sync](https://github.com/mikebrady/shairport-sync)): each
+time the length of the volume slider (or the number of steps above mute,
+where 16 steps = full volume) is reduced by 50%, the perceived volume is
+halved (a 10dB attenuation). (This is modified at low volumes, to use
+the "untapered" volume if it is louder.)
+
+**-s wxh** e.g. -s 1920x1080 (= "1080p"), the default width and height
+resolutions in pixels for h264 video. (The default becomes 3840x2160 (=
+"4K") when the -h265 option is used.) This is just a request made to the
+AirPlay client, and perhaps will not be the final resolution you get. w
+and h are whole numbers with four digits or less. Note that the
+**height** pixel size is the controlling one used by the client for
+determining the streaming format; the width is dynamically adjusted to
+the shape of the image (portrait or landscape format, depending on how
+an iPad is held, for example).
+
+**-s wxh@r** As above, but also informs the AirPlay client about the
+screen refresh rate of the display. Default is r=60 (60 Hz); r must be a
+whole number less than 256.
+
+**-o** turns on an "overscanned" option for the display window. This
+reduces the image resolution by using some of the pixels requested by
+option -s wxh (or their default values 1920x1080) by adding an empty
+boundary frame of unused pixels (which would be lost in a full-screen
+display that overscans, and is not displayed by gstreamer).
+Recommendation: **don't use this option** unless there is some special
+reason to use it.
+
+**-fs** uses fullscreen mode, but only works with X11, Wayland, VAAPI,
+and D3D11 (Windows).
+
+**-p** allows you to select the network ports used by UxPlay (these need
+to be opened if the server is behind a firewall). By itself, -p sets
+"legacy" ports TCP 7100, 7000, 7001, UDP 6000, 6001, 7011. -p n (e.g. -p
+35000) sets TCP and UDP ports n, n+1, n+2. -p n1,n2,n3 (comma-separated
+values) sets each port separately; -p n1,n2 sets ports n1,n2,n2+1. -p
+tcp n or -p udp n sets just the TCP or UDP ports. Ports must be in the
+range \[1024-65535\].
+
+If the -p option is not used, the ports are chosen dynamically
+(randomly), which will not work if a firewall is running.
+
+**-avdec** forces use of software h264 decoding using Gstreamer element
+avdec_h264 (libav h264 decoder). This option should prevent
+autovideosink choosing a hardware-accelerated videosink plugin such as
+vaapisink.
+
+**-vp *parser*** choses the GStreamer pipeline's h264 parser element,
+default is h264parse. Using quotes "..." allows options to be added.
+
+**-vd *decoder*** chooses the GStreamer pipeline's h264 decoder element,
+instead of the default value "decodebin" which chooses it for you.
+Software decoding is done by avdec_h264; various hardware decoders
+include: vaapih264dec, nvdec, nvh264dec, v4l2h264dec (these require that
+the appropriate hardware is available). Using quotes "..." allows some
+parameters to be included with the decoder name.
+
+**-vc *converter*** chooses the GStreamer pipeline's videoconverter
+element, instead of the default value "videoconvert". When using
+Video4Linux2 hardware-decoding by a GPU,`-vc v4l2convert` will also use
+the GPU for video conversion. Using quotes "..." allows some parameters
+to be included with the converter name.
+
+**-vs *videosink*** chooses the GStreamer videosink, instead of the
+default value "autovideosink" which chooses it for you. Some videosink
+choices are: ximagesink, xvimagesink, vaapisink (for intel graphics),
+gtksink, glimagesink, waylandsink, osxvideosink (for macOS), kmssink
+(for systems without X11, like Raspberry Pi OS lite) or fpsdisplaysink
+(which shows the streaming framerate in fps). Using quotes "..." allows
+some parameters to be included with the videosink name. For example,
+**fullscreen** mode is supported by the vaapisink plugin, and is
+obtained using `-vs "vaapisink fullscreen=true"`; this also works with
+`waylandsink`. The syntax of such options is specific to a given plugin
+(see GStreamer documentation), and some choices of videosink might not
+work on your system.
+
+**-vs 0** suppresses display of streamed video. In mirror mode, the
+client's screen is still mirrored at a reduced rate of 1 frame per
+second, but is not rendered or displayed. This option should always be
+used if the server is "headless" (with no attached screen to display
+video), and only used to render audio, which will be AAC
+lossily-compressed audio in mirror mode with unrendered video, and
+superior-quality ALAC Apple Lossless audio in Airplay audio-only mode.
+
+**-v4l2** Video settings for hardware h264 video decoding in the GPU by
+Video4Linux2. Equivalent to `-vd v4l2h264dec -vc v4l2convert`.
+
+**-bt709** A workaround for the failure of the older Video4Linux2 plugin
+to recognize Apple's use of an uncommon (but permitted) "full-range
+color" variant of the bt709 color standard for digital TV. This is no
+longer needed by GStreamer-1.20.4 and backports from it.
+
+**-rpi** Equivalent to "-v4l2" (Not valid for Raspberry Pi model 5, and
+removed in UxPlay 1.67)
+
+**-rpigl** Equivalent to "-rpi -vs glimagesink". (Removed since UxPlay
+1.67)
+
+**-rpifb** Equivalent to "-rpi -vs kmssink" (Removed since UxPlay 1.67)
+
+**-rpiwl** Equivalent to "-rpi -vs waylandsink". (Removed since UxPlay
+1.67)
+
+**-as *audiosink*** chooses the GStreamer audiosink, instead of letting
+autoaudiosink pick it for you. Some audiosink choices are: pulsesink,
+alsasink, pipewiresink, osssink, oss4sink, jackaudiosink, osxaudiosink
+(for macOS), wasapisink, directsoundsink (for Windows). Using quotes
+"..." might allow some optional parameters
+(e.g. `-as "alsasink device=..."` to specify a non-default output
+device). The syntax of such options is specific to a given plugin (see
+GStreamer documentation), and some choices of audiosink might not work
+on your system.
+
+**-as 0** (or just **-a**) suppresses playing of streamed audio, but
+displays streamed video.
+
+**-al *x*** specifies an audio latency *x* in (decimal) seconds in
+Audio-only (ALAC), that is reported to the client. Values in the range
+\[0.0, 10.0\] seconds are allowed, and will be converted to a whole
+number of microseconds. Default is 0.25 sec (250000 usec). *(However,
+the client appears to ignore this reported latency, so this option seems
+non-functional.)*
+
+**-ca *filename*** provides a file (where *filename* can include a full
+path) used for output of "cover art" (from Apple Music, *etc.*,) in
+audio-only ALAC mode. This file is overwritten with the latest cover art
+as it arrives. Cover art (jpeg format) is discarded if this option is
+not used. Use with a image viewer that reloads the image if it changes,
+or regularly (*e.g.* once per second.). To achieve this, run
+"`uxplay -ca [path/to/]filename &`" in the background, then run the the
+image viewer in the foreground. Example, using `feh` as the viewer: run
+"`feh -R 1 [path/to/]filename`" (in the same terminal window in which
+uxplay was put into the background). To quit, use `ctrl-C fg ctrl-C` to
+terminate the image viewer, bring `uxplay` into the foreground, and
+terminate it too.
+
+**-reset n** sets a limit of *n* consecutive timeout failures of the
+client to respond to ntp requests from the server (these are sent every
+3 seconds to check if the client is still present, and synchronize with
+it). After *n* failures, the client will be presumed to be offline, and
+the connection will be reset to allow a new connection. The default
+value of *n* is 5; the value *n* = 0 means "no limit" on timeouts.
+
+**-nofreeze** closes the video window after a reset due to ntp timeout
+(default is to leave window open to allow a smoother reconection to the
+same client). This option may be useful in fullscreen mode.
+
+**-nc** maintains previous UxPlay \< 1.45 behavior that does **not
+close** the video window when the the client sends the "Stop Mirroring"
+signal. *This option is currently used by default in macOS, as the
+window created in macOS by GStreamer does not terminate correctly (it
+causes a segfault) if it is still open when the GStreamer pipeline is
+closed.*
+
+**-nohold** Drops the current connection when a new client attempts to
+connect. Without this option, the current client maintains exclusive
+ownership of UxPlay until it disconnects.
+
+**-restrict** Restrict clients allowed to connect to those specified by
+`-allow `. The deviceID has the form of a MAC address which is
+displayed by UxPlay when the client attempts to connect, and appears to
+be immutable. It has the format `XX:XX:XX:XX:XX:XX`, X = 0-9,A-F, and is
+possibly the "true" hardware MAC address of the device. Note that iOS
+clients generally expose different random "private Wi_Fi addresses"
+("fake" MAC addresses) to different networks (for privacy reasons, to
+prevent tracking), which may change, and do not correpond to the
+deviceID.
+
+**-restrict no** Remove restrictions (default). This is useful as a
+command-line argument to overide restrictions set in the Startup file.
+
+**-allow *id*** Adds the deviceID = *id* to the list of allowed clients
+when client restrictions are being enforced. Usually this will be an
+entry in the uxplayrc startup file.
+
+**-block *id*** Always block clients with deviceID = *id*, even when
+client restrictions are not being enforced generally. Usually this will
+be an entry in the uxplayrc startup file.
+
+**-FPSdata** Turns on monitoring of regular reports about video
+streaming performance that are sent by the client. These will be
+displayed in the terminal window if this option is used. The data is
+updated by the client at 1 second intervals.
+
+**-fps n** sets a maximum frame rate (in frames per second) for the
+AirPlay client to stream video; n must be a whole number less than 256.
+(The client may choose to serve video at any frame rate lower than this;
+default is 30 fps.) A setting of 60 fps may give you improved video but
+is not recommended on Raspberry Pi. A setting below 30 fps might be
+useful to reduce latency if you are running more than one instance of
+uxplay at the same time. *This setting is only an advisory to the client
+device, so setting a high value will not force a high framerate.* (You
+can test using "-vs fpsdisplaysink" to see what framerate is being
+received, or use the option -FPSdata which displays video-stream
+performance data continuously sent by the client during
+video-streaming.)
+
+**-f {H\|V\|I}** implements "videoflip" image transforms: H = horizontal
+flip (right-left flip, or mirror image); V = vertical flip ; I = 180
+degree rotation or inversion (which is the combination of H with V).
+
+**-r {R\|L}** 90 degree Right (clockwise) or Left (counter-clockwise)
+rotations; these image transforms are carried out after any **-f**
+transforms.
+
+**-m \[mac\]** changes the MAC address (Device ID) used by UxPlay
+(default is to use the true hardware MAC address reported by the host
+computer's network card). (Different server_name, MAC addresses, and
+network ports are needed for each running uxplay if you attempt to run
+more than one instance of uxplay on the same computer.) If \[mac\] (in
+form xx:xx:xx:xx:xx:xx, 6 hex octets) is not given, a random MAC address
+is generated. If UxPlay fails to find the true MAC address of a network
+card, (more specifically, the MAC address used by the first active
+network interface detected) a random MAC address will be used even if
+option **-m** was not specified. (Note that a random MAC address will be
+different each time UxPlay is started).
+
+**-key \[*filename*\]**: This (more secure) option for generating and
+storing a persistant public key (needed for the -pin option) has been
+replaced by default with a (less secure) method which generates a key
+from the server's "device ID" (MAC address, which can be changed with
+the -m option, conveniently as a startup file option). When the -key
+option is used, a securely generated keypair is generated and stored in
+`$HOME/.uxplay.pem`, if that file does not exist, or read from it, if it
+exists. (Optionally, the key can be stored in *filename*.) This method
+is more secure than the new default method, (because the Device ID is
+broadcast in the DNS_SD announcement) but still leaves the private key
+exposed to anyone who can access the pem file. This option should be set
+in the UxPlay startup file as a line "key" or "key *filename*" (no
+initial "-"), where *filename* is a full path which should be enclosed
+in quotes (`"...."`) if it contains any blank spaces. **Because the
+default method is simpler, and security of client access to uxplay is
+unlikely to be an important issue, the -key option is no longer
+recommended**.
+
+**-dacp \[*filename*\]**: Export current client DACP-ID and
+Active-Remote key to file: default is \$HOME/.uxplay.dacp. (optionally
+can be changed to *filename*). Can be used by remote control
+applications. File is transient: only exists while client is connected.
+
+**-vdmp** Dumps h264 video to file videodump.h264. -vdmp n dumps not
+more than n NAL units to videodump.x.h264; x= 1,2,... increases each
+time a SPS/PPS NAL unit arrives. To change the name *videodump*, use
+-vdmp \[n\] *filename*.
+
+**-admp** Dumps audio to file audiodump.x.aac (AAC-ELD format audio),
+audiodump.x.alac (ALAC format audio) or audiodump.x.aud (other-format
+audio), where x = 1,2,3... increases each time the audio format changes.
+-admp *n* restricts the number of packets dumped to a file to *n* or
+less. To change the name *audiodump*, use -admp \[n\] *filename*. *Note
+that (unlike dumped video) the dumped audio is currently only useful for
+debugging, as it is not containerized to make it playable with standard
+audio players.*
+
+**-d** Enable debug output. Note: this does not show GStreamer error or
+debug messages. To see GStreamer error and warning messages, set the
+environment variable GST_DEBUG with "export GST_DEBUG=2" before running
+uxplay. To see GStreamer information messages, set GST_DEBUG=4; for
+DEBUG messages, GST_DEBUG=5; increase this to see even more of the
+GStreamer inner workings.
+
+# Troubleshooting
+
+Note: `uxplay` is run from a terminal command line, and informational
+messages are written to the terminal.
+
+### 0. Problems in compiling UxPlay.
+
+One user (on Ubuntu) found compilation failed with messages about
+linking to "usr/local/lib/libcrypto.a" and "zlib". This was because (in
+addition to the standard ubuntu installation of libssl-dev), the user
+was unaware that a second installation with libcrypto in /usr/local was
+present. Solution: when more than one installation of OpenSSL is
+present, set the environment variable OPEN_SSL_ROOT_DIR to point to the
+correct one; on 64-bit Ubuntu, this is done by running
+`export OPENSSL_ROOT_DIR=/usr/lib/X86_64-linux-gnu/` before running
+cmake.
+
+### 1. **Avahi/DNS_SD Bonjour/Zeroconf issues**
+
+The DNS_SD Service-Discovery ("Bonjour" or "Zeroconf") service is
+required for UxPlay to work. On Linux, it will be usually provided by
+Avahi, and to troubleshoot this, you should use the tool `avahi-browse`.
+(You may need to install a separate package with a name like
+`avahi-utils` to get this.)
+
+On Linux, make sure Avahi is installed, and start the avahi-daemon
+service on the system running uxplay (your distribution will document
+how to do this, for example: `sudo systemctl avahi-daemon` or
+`sudo service avahi-daemon `, with `` one of enable, disable,
+start, stop, status. You might need to edit the avahi-daemon.conf file
+(it is typically in /etc/avahi/, find it with
+"`sudo find /etc -name avahi-daemon.conf`"): make sure that
+"disable-publishing" is **not** a selected option). Some systems may
+instead use the mdnsd daemon as an alternative to provide DNS-SD
+service. (FreeBSD offers both alternatives, but only Avahi was tested;
+see [here](https://gist.github.com/reidransom/6033227).)
+
+- **uxplay starts, but either stalls or stops after "Initialized
+ server socket(s)" appears (*without the server name showing on the
+ client*)**.
+
+If UxPlay stops with the "No DNS-SD Server found" message, this means
+that your network **does not have a running Bonjour/zeroconf DNS-SD
+server.** Before v1.60, UxPlay used to stall silently if DNS-SD service
+registration failed, but now stops with an error message returned by the
+DNSServiceRegister function: kDNSServiceErr_Unknown if no DNS-SD server
+was found: *(A NixOS user found that in NixOS, this error can also occur
+if avahi-daemon service IS running with publishing enabled, but reports
+"the error disappeared on NixOS by setting services.avahi.openFirewall
+to true".)* Other mDNS error codes are in the range FFFE FF00 (-65792)
+to FFFE FFFF (-65537), and are listed in the dnssd.h file. An older
+version of this (the one used by avahi) is found
+[here](https://github.com/lathiat/avahi/blob/master/avahi-compat-libdns_sd/dns_sd.h).
+A few additional error codes are defined in a later version from
+[Apple](https://opensource.apple.com/source/mDNSResponder/mDNSResponder-544/mDNSShared/dns_sd.h.auto.html).
+
+If UxPlay stalls *without an error message* and *without the server name
+showing on the client*, **this is a network problem** (if your UxPlay
+version is older than 1.60, it is also the behavior when no DNS-SD
+server is found.)
+
+A useful tool for examining such network problems from the client end is
+the (free) Discovery DNS-SD browser [available in the Apple App
+Store](https://apps.apple.com/us/developer/lily-ballard/id305441020) for
+both iOS (works on iPadOS too) and macOS.
+
+- Some users using dual-band (2.4GHz/5GHz) routers have reported that
+ clients using the 5GHz band (sometimes) "fail to see UxPlay" (i.e.,
+ do not get a response to their mDNS queries), but the 2.4GHz band
+ works. Other projects using Bonjour/mDNS have had similar reports;
+ the issue seems to be router-specific, perhaps related to "auto"
+ rather than fixed channel selection (5GHz has many more channels to
+ switch between), or channel width selections; one speculation is
+ that since mDNS uses UDP protocol (where "lost" messages are not
+ resent), a mDNS query might get lost if channel switching occurs
+ during the query.
+
+If your router has this problem, a reported "fix" is to (at least on
+5GHz) use fixed channel and/or fixed (not dynamic) channel width.
+
+- **Avahi works at first, but new clients do not see UxPlay, or
+ clients that initially saw it stop doing so after they disconnect**.
+
+This is usually because Avahi is only using the "loopback" network
+interface, and is not receiving mDNS queries from new clients that were
+not listening when UxPlay started.
+
+To check this, after starting uxplay, use the utility
+`avahi-browse -a -t` **in a different terminal window** on the server to
+verify that the UxPlay AirTunes and AirPlay services are correctly
+registered (only the AirTunes service is used in the "Legacy" AirPlay
+Mirror mode used by UxPlay, but the AirPlay service is used for the
+initial contact).
+
+The results returned by avahi-browse should show entries for uxplay like
+
+ + eno1 IPv6 UxPlay AirPlay Remote Video local
+ + eno1 IPv4 UxPlay AirPlay Remote Video local
+ + lo IPv4 UxPlay AirPlay Remote Video local
+ + eno1 IPv6 863EA27598FE@UxPlay AirTunes Remote Audio local
+ + eno1 IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local
+ + lo IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local
+
+If only the loopback ("lo") entries are shown, a firewall on the UxPlay
+host is probably blocking full DNS-SD service, and you need to open the
+default UDP port 5353 for mDNS requests, as loopback-based DNS-SD
+service is unreliable.
+
+If the UxPlay services are listed by avahi-browse as above, but are not
+seen by the client, the problem is likely to be a problem with the local
+network.
+
+### 2. uxplay starts, but stalls after "Initialized server socket(s)" appears, *with the server name showing on the client* (but the client fails to connect when the UxPlay server is selected).
+
+This shows that a *DNS-SD* service is working, clients hear UxPlay is
+available, but the UxPlay server is not receiving the response from the
+client. This is usually because a firewall on the server is blocking the
+connection request from the client. (One user who insisted that the
+firewall had been turned off turned out to have had *two* active
+firewalls (*firewalld* and *ufw*) *both* running on the server!) If
+possible, either turn off the firewall to see if that is the problem, or
+get three consecutive network ports, starting at port n, all three in
+the range 1024-65535, opened for both tcp and udp, and use "uxplay -p n"
+(or open UDP 7011,6001,6000 TCP 7100,7000,7001 and use "uxplay -p").
+
+If you are *really* sure there is no firewall, you may need to
+investigate your network transmissions with a tool like netstat, but
+almost always this is a firewall issue.
+
+### 3. Problems *after* the client-server connection has been made:
+
+If you do *not* see the message `raop_rtp_mirror starting mirroring`,
+something went wrong before the client-server negotiations were
+finished. For such problems, use "uxplay -d" (debug log option) to see
+what is happening: it will show how far the connection process gets
+before the failure occurs. You can compare your debug output to that
+from a successful start of UxPlay in the [UxPlay
+Wiki](https://github.com/FDH2/UxPlay/wiki).
+
+**If UxPlay reports that mirroring started, but you get no video or
+audio, the problem is probably from a GStreamer plugin that doesn't work
+on your system** (by default, GStreamer uses the "autovideosink" and
+"autoaudiosink" algorithms to guess what are the "best" plugins to use
+on your system). A different reason for no audio occurred when a user
+with a firewall only opened two udp network ports: **three** are
+required (the third one receives the audio data).
+
+**Raspberry Pi** devices (*Pi 4B+ and earlier: this does not apply to
+the Pi 5, which does not provide hardware h264 decoding, and does not
+need it*) work best with hardware GPU h264 video decoding if the
+Video4Linux2 plugin in GStreamer v1.20.x or earlier has been patched
+(see the UxPlay
+[Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches)
+for patches). This is fixed in GStreamer-1.22, and by backport patches
+from this in distributions such as Raspberry Pi OS (Bullseye): **use
+option `-bt709` with the GStreamer-1.18.4 from Raspberry Pi OS**. This
+also needs the bcm2835-codec kernel module that is not in the standard
+Linux kernel (it is available in Raspberry Pi OS, Ubuntu and Manjaro).
+
+- **If this kernel module is not available in your Raspberry Pi
+ operating system, or if GStreamer \< 1.22 is not patched, use option
+ `-avdec` for software h264-decoding.**
+
+Sometimes "autovideosink" may select the OpenGL renderer "glimagesink"
+which may not work correctly on your system. Try the options "-vs
+ximagesink" or "-vs xvimagesink" to see if using one of these fixes the
+problem.
+
+Other reported problems are connected to the GStreamer VAAPI plugin (for
+hardware-accelerated Intel graphics, but not NVIDIA graphics). Use the
+option "-avdec" to force software h264 video decoding: this should
+prevent autovideosink from selecting the vaapisink videosink.
+Alternatively, find out if the gstreamer1.0-vaapi plugin is installed,
+and if so, uninstall it. (If this does not fix the problem, you can
+reinstall it.)
+
+There are some reports of other GStreamer problems with
+hardware-accelerated Intel HD graphics. One user (on Debian) solved this
+with "sudo apt install intel-media-va-driver-non-free". This is a driver
+for 8'th (or later) generation "\*-lake" Intel chips, that seems to be
+related to VAAPI accelerated graphics.
+
+If you *do* have Intel HD graphics, and have installed the vaapi plugin,
+but `-vs vaapisink` does not work, check that vaapi is not "blacklisted"
+in your GStreamer installation: run `gst-inspect-1.0 vaapi`, if this
+reports `0 features`, you need to `export GST_VAAPI_ALL_DRIVERS=1`
+before running uxplay, or set this in the default environment.
+
+You can try to fix audio or video problems by using the
+"`-as `" or "`-vs `" options to choose the
+GStreamer audiosink or videosink , rather than letting GStreamer choose
+one for you. (See above, in [Starting and running
+UxPlay](#starting-and-running-uxplay) for choices of `` or
+``.)
+
+The "OpenGL renderer" window created on Linux by "-vs glimagesink"
+sometimes does not close properly when its "close" button is clicked.
+(this is a GStreamer issue). You may need to terminate uxplay with
+Ctrl-C to close a "zombie" OpenGl window. If similar problems happen
+when the client sends the "Stop Mirroring" signal, try the no-close
+option "-nc" that leaves the video window open.
+
+### 4. GStreamer issues (missing plugins, etc.):
+
+- clearing the user's GStreamer cache with
+ `rm -rf ~/.cache/gstreamer-1.0/*` may be the solution to problems
+ where gst-inspect-1.0 does not show a plugin that you believe is
+ installed. The cache will be regenerated next time GStreamer is
+ started. **This is the solution to puzzling problems that turn out
+ to come from corruption of the cache, and should be tried first.**
+
+If UxPlay fails to start, with a message that a required GStreamer
+plugin (such as "libav") was not found, first check with the GStreamer
+tool gst-inspect-1.0 to see what GStreamer knows is available. (You may
+need to install some additional GStreamer "tools" package to get
+gst-inspect-1.0). For, *e.g.* a libav problem, check with
+"`gst-inspect-1.0 libav`". If it is not shown as available to GStreamer,
+but your package manager shows the relevant package as installed (as one
+user found), try entirely removing and reinstalling the package. That
+user found that a solution to a "**Required gstreamer plugin 'libav' not
+found**" message that kept recurring was to clear the user's gstreamer
+cache.
+
+If it fails to start with an error like '`no element "avdec_aac"`' this
+is because even though gstreamer-libav is installed. it is incomplete
+because some plugin features are missing:
+"`gst-inspect-1.0 | grep avdec_aac`" will show if avdec_aac is
+available. Unlike other GStreamer plugins, the libav plugin is a front
+end to FFmpeg codecs which provide avdec\_\*.
+
+- Some distributions (RedHat, SUSE, etc) provide incomplete versions
+ of FFmpeg because of patent issues with codecs used by certain
+ plugins. In those cases there will be some "extra package" provider
+ like [RPM fusion](https://rpmfusion.org) (RedHat),
+ [packman](http://packman.links2linux.org/) (SUSE) where you can get
+ complete packages (your distribution will usually provide
+ instructions for this, Mageia puts them in an optional "tainted"
+ repo). The packages needed may be "ffmpeg\*" or "libav\*" packages:
+ the GStreamer libav plugin package does not contain any codecs
+ itself, it just provides a way for GStreamer to use ffmpeg/libav
+ codec libraries which must be installed separately. For similar
+ reasons, distributions may ship incomplete packages of GStreamer
+ "plugins-bad". Use user on Fedora thought they had installed from
+ rpmfusion, but the system had not obeyed: *"Adding --allowerasing to
+ the dnf command fixed it after a restart"*.
+
+- starting with release UxPlay-1.65.3, UxPlay will continue to
+ function, but without audio in mirror mode, if avdec_aac is missing.
+
+To troubleshoot GStreamer execute "export GST_DEBUG=2" to set the
+GStreamer debug-level environment-variable in the terminal where you
+will run uxplay, so that you see warning and error messages; see
+[GStreamer debugging
+tools](https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html)
+for how to see much more of what is happening inside GStreamer. Run
+"gst-inspect-1.0" to see which GStreamer plugins are installed on your
+system.
+
+Some extra GStreamer packages for special plugins may need to be
+installed (or reinstalled: a user using a Wayland display system as an
+alternative to X11 reported that after reinstalling Lubuntu 18.4, UxPlay
+would not work until gstreamer1.0-x was installed, presumably for
+Wayland's X11-compatibility mode). Different distributions may break up
+GStreamer 1.x into packages in different ways; the packages listed above
+in the build instructions should bring in other required GStreamer
+packages as dependencies, but will not install all possible plugins.
+
+The GStreamer video pipeline, which is shown in the initial output from
+`uxplay -d`, has the default form
+
+ appsrc name=video_source ! queue ! h264parse ! decodebin ! videoconvert ! autovideosink name=video_sink sync=false
+
+The pipeline is fully configurable: default elements "h264parse",
+"decodebin", "videoconvert", and "autovideosink" can respectively be
+replaced by using uxplay options `-vp`, `-vd`, `-vc`, and `-vs`, if
+there is any need to modify it (entries can be given in quotes "..." to
+include options).
+
+### 5. Mirror screen freezes (a network problem):
+
+This can happen if the TCP video stream from the client stops arriving
+at the server, probably because of network problems (the UDP audio
+stream may continue to arrive). At 3-second intervals, UxPlay checks
+that the client is still connected by sending it a request for a NTP
+time signal. If a reply is not received from the client within a 0.3 sec
+time-window, an "ntp timeout" is registered. If a certain number
+(currently 5) of consecutive ntp timeouts occur, UxPlay assumes that the
+client is "dead", and resets the connection, becoming available for
+connection to a new client, or reconnection to the previous one.
+Sometimes the connection may recover before the timeout limit is
+reached, and if the default limit is not right for your network, it can
+be modified using the option "-reset *n*", where *n* is the desired
+timeout-limit value (*n* = 0 means "no limit"). If the connection starts
+to recover after ntp timeouts, a corrupt video packet from before the
+timeout may trigger a "connection reset by peer" error, which also
+causes UxPlay to reset the connection.
+
+- When the connection is reset, the "frozen" mirror screen of the
+ previous connection is left in place, but does **not** block new
+ connections, and will be taken over by a new client connection when
+ it is made.
+
+### 6. Protocol issues (with decryption of the encrypted audio and video streams sent by the client).
+
+A protocol failure may trigger an unending stream of error messages, and
+means that the audio decryption key (also used in video decryption) was
+not correctly extracted from data sent by the client.
+
+The protocol was modifed in UxPlay-1.65 after it was discovered that the
+client-server "pairing" step could be avoided (leading to a much quicker
+connection setup, without a 5 second delay) by disabling "Supports
+Legacy Pairing" (bit 27) in the "features" code UxPlay advertises on
+DNS-SD Service Discovery. Most clients will then not attempt the setup
+of a "shared secret key" when pairing, which is used by AppleTV for
+simultaneous handling of multiple clients (UxPlay only supports one
+client at a time). **This change is now well-tested, but in case it
+causes any protocol failures, UxPlay can be reverted to the previous
+behavior by uncommenting the previous "FEATURES_1" setting (and
+commenting out the new one) in lib/dnssdint.h, and then rebuilding
+UxPlay.** ("Pairing" is re-enabled when the new Apple-style one-time
+"pin" authentication is activated by running UxPlay with the "-pin"
+option introduced in UxPlay 1.67.)
+
+Protocol failure should not happen for iOS 9.3 or later clients.
+However, if a client uses the same older version of the protocol that is
+used by the Windows-based AirPlay client emulator *AirMyPC*, the
+protocol can be switched to the older version by the setting
+`OLD_PROTOCOL_CLIENT_USER_AGENT_LIST` in `UxPlay/lib/global.h`. UxPlay
+reports the client's "User Agent" string when it connects. If some other
+client also fails to decrypt all audio and video, try adding its "User
+Agent" string in place of "xxx" in the entry "AirMyPC/2.0;xxx" in
+global.h and rebuild uxplay.
+
+Note that for DNS-SD Service Discovery, Uxplay declares itself to be an
+AppleTV3,2 (a 32 bit device) with a sourceVersion 220.68; this can also
+be changed in global.h. UxPlay also works if it declares itself as an
+AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen,
+introduced 2017, running tvOS 12.2.1), so it does not seem to matter
+what version UxPlay claims to be.
+
+# Changelog
+
+1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially
+only for YouTube movies. Fix issue with NTP timeout on Windows.
+
+1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x
+2160). Fix issue with GStreamer \>= 1.24 when client sleeps, then wakes.
+
+1.69 2024-08-09 Internal improvements (e.g. in -nohold option,
+identifying GStreamer videosink selected by autovideosink, finding X11
+display) in anticipation of future HLS video support. New -nofreeze
+option to not leave frozen video in place when a network connection is
+reset. Fixes for GStreamer-1.24.x changes.
+
+1.68 2023-12-31 New simpler (default) method for generating a persistent
+public key from the server MAC address (which can now be set with the -m
+option). (The previous method is still available with -key option). New
+option -reg to maintain a register of pin-authenticated clients.
+Corrected volume-control: now interprets AirPlay volume range -30dB:0dB
+as decibel gain attenuation, with new option -db low\[:high\] for "flat"
+rescaling of the dB range. Add -taper option for a "tapered" AirPlay
+volume-control profile.
+
+1.67 2023-11-30 Add support for Apple-style one-time pin authentication
+of clients with option "-pin": (uses SRP6a authentication protocol and
+public key persistence). Detection with error message of (currently)
+unsupported H265 video when requesting high resolution over wired
+ethernet. Removed rpi\* options (which are not valid with new Raspberry
+Pi model 5, and can be replaced by combinations of other options). Added
+optional argument "mac" to "-m" option, to specify a replacement MAC
+address/Device ID. Update llhttp to v. 9.1.3. Add -dacp option for
+exporting current client DACP info (for remotes).
+
+1.66 2023-09-05 Fix IPV6 support. Add option to restrict clients to
+those on a list of allowed deviceIDs, or to block connections from
+clients on a list of blocked deviceIDs. Fix for #207 from @thiccaxe
+(screen lag in vsync mode after client wakes from sleep).
+
+1.65.3 2023-07-23 Add RPM spec file; add warning if required gstreamer
+libav feature "avdec_aac" is missing: (this occurs in RPM-based
+distributions that ship an incomplete FFmpeg for Patent or License
+reasons, and rely on users installing an externally-supplied complete
+FFmpeg). Mirror-mode airplay will now work without audio if avdec_aac is
+missing.
+
+1.65 2023-06-03 Eliminate pair_setup part of connection protocol to
+allow faster connections with clients (thanks to @shuax #176 for this
+discovery); to revert, uncomment a line in lib/dnssdint.h. Disconnect
+from audio device when connection closes, to not block its use by other
+apps if uxplay is running but not connected. Fix for AirMyPC client
+(broken since 1.60), so its older non-NTP timestamp protocol works with
+-vsync. Corrected parsing of configuration file entries that were in
+quotes.
+
+1.64 2023-04-23 Timestamp-based synchronization of audio and video is
+now the default in Mirror mode. (Use "-vsync no" to restore previous
+behavior.) A configuration file can now be used for startup options.
+Also some internal cleanups and a minor bugfix that fixes #192.
+
+1.63 2023-02-12 Reworked audio-video synchronization, with new options
+-vsync (for Mirror mode) and -async (for Audio-Only mode, to sync with
+client video). Option -vsync makes software h264 decoding of streamed
+videos with option -avdec viable on some recent Raspberry Pi models.
+Internal change: all times are now processed in nanoseconds units.
+Removed -ao option introduced in 1.62.
+
+1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user
+synchronization of ALAC audio playing on the server with video, song
+lyrics, etc. playing on the client. x = 5.0 appears to be optimal in
+many cases. Quality fixes: cleanup in volume changes, timestamps, some
+bugfixes.
+
+1.61 2022-12-30 Removed -t option (workaround for an Avahi issue,
+correctly solved by opening network port UDP 5353 in firewall). Remove
+-g debug flag from CMAKE_CFLAGS. Postpend (instead of prepend) build
+environment CFLAGS to CMAKE_CFLAGS. Refactor parts of uxplay.cpp
+
+1.60 2022-12-15 Added exit with error message if DNSServiceRegister
+fails (instead of just stalling). Test for Client's attempt to using
+unsupported AirPlay 2 "REMOTE CONTROL" protocol (with no timing
+channel), and exit if this occurs. Reworked metadata processing to
+correctly parse DMAP header (previous version worked with DMAP messages
+currently received, but was not correct).
+
+1.59 2022-12-12 remove "ZOOMFIX" compile option and make compilation
+with X11-dependence the default if X11 development libraries are
+detected (this now also provides fullscreen mode with a F11 or Alt+Enter
+key toggle); ZOOMFIX is now automatically applied for GStreamer \< 1.20.
+New cmake option -DNO_X11_DEPS compiles uxplay without X11 dependence.
+Reworked internal metadata handling. Fix segfault with "-vs 0".
+
+1.58 2022-10-29 Add option "-nohold" that will drop existing connections
+when a new client connects. Update llhttp to v8.1.0.
+
+1.57 2022-10-09 Minor fixes: (fix coredump on AUR on "stop mirroring",
+occurs when compiled with AUR CFLAGS -DFORTIFY_SOURCE); graceful exit
+when required plugins are missing; improved support for builds on
+Windows. Include audioresample in GStreamer audio pipeline.
+
+1.56 2022-09-01 Added support for building and running UxPlay-1.56 on
+Windows (no changes to Unix (Linux, \*BSD, macOS) codebase.)
+
+1.56 2022-07-30 Remove -bt709 from -rpi, -rpiwl, -rpifb as GStreamer is
+now fixed.
+
+1.55 2022-07-04 Remove the bt709 fix from -v4l2 and create a new -bt709
+option (previous "-v4l2" is now "-v4l2 -bt709"). This allows the
+currently-required -bt709 option to be used on its own on RPi without
+-v4l2 (sometimes this give better results).
+
+1.54 2022-06-25 Add support for "Cover Art" display in Audio-only (ALAC)
+mode. Reverted a change that caused VAAPI to crash with AMD POLARIS
+graphics cards. Minor internal changes to plist code and uxplay option
+parsing.
+
+1.53 2022-06-13 Internal changes to audio sync code, revised
+documentation, Minor bugfix (fix assertion crash when resent audio
+packets are empty).
+
+1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted
+streaming debug output (readable aligned timestamps with decimal points
+in seconds). Eliminate memory leaks (found by valgrind). Support for
+display of ALAC (audio-only) metadata (soundtrack artist names, titles
+etc.) in the uxplay terminal.
+
+1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option
+-v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2,
+-v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from
+1.48 that broke reconnection after "Stop Mirroring" is sent by client.
+
+1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI
+plugins only), Changed -rpi to be for framebuffer ("lite") RPi systems
+and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop
+systems. Also modified timestamps from "DTS" to "PTS" for latency
+improvement, plus internal cleanups.
+
+1.49 2022-03-28 Addded options for dumping video and/or audio to file,
+for debugging, etc. h264 PPS/SPS NALU's are shown with -d. Fixed
+video-not-working for M1 Mac clients.
+
+1.48 2022-03-11 Made the GStreamer video pipeline fully configurable,
+for use with hardware h264 decoding. Support for Raspberry Pi.
+
+1.47 2022-02-05 Added -FPSdata option to display (in the terminal)
+regular reports sent by the client about video streaming performance.
+Internal cleanups of processing of video packets received from the
+client. Added -reset n option to reset the connection after n ntp
+timeouts (also reset after "connection reset by peer" error in video
+stream).
+
+1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken hardware
+acceleration): once again use decodebin in the video pipeline; introduce
+new option "-avdec" to force software h264 decoding by libav h264, if
+needed (to prevent selection of vaapisink by autovideosink). Update
+llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2. Restrict
+connections to one client at a time (second client must now wait for
+first client to disconnect).
+
+1.45 2022-01-10 New behavior: close video window when client requests
+"stop mirroring". (A new "no close" option "-nc" is added for users who
+wish to retain previous behavior that does not close the video window).
+
+1.44 2021-12-13 Omit hash of aeskey with ecdh_secret for an AirMyPC
+client; make an internal rearrangement of where this hash is done. Fully
+report all initial communications between client and server in -d debug
+mode. Replace decodebin in GStreamer video pipeline by h264-specific
+elements.
+
+1.43 2021-12-07 Various internal changes, such as tests for successful
+decryption, uniform treatment of informational/debug messages, etc.,
+updated README.
+
+1.42 2021-11-20 Fix MAC detection to work with modern Linux interface
+naming practices, MacOS and \*BSD.
+
+1.41 2021-11-11 Further cleanups of multiple audio format support
+(internal changes, separated RAOP and GStreamer audio/video startup)
+
+1.40 2021-11-09 Cleanup segfault in ALAC support, manpage location fix,
+show request Plists in debug mode.
+
+1.39 2021-11-06 Added support for Apple Lossless (ALAC) audio streams.
+
+1.38 2021-10-8 Add -as *audiosink* option to allow user to choose the
+GStreamer audiosink.
+
+1.37 2021-09-29 Append "@hostname" to AirPlay Server name, where
+"hostname" is the name of the server running uxplay (reworked change in
+1.36).
+
+1.36 2021-09-29 Implemented suggestion (by @mrbesen and @PetrusZ) to use
+hostname of machine runing uxplay as the default server name
+
+1.35.1 2021-09-28 Added the -vs 0 option for streaming audio, but not
+displaying video.
+
+1.35 2021-09-10 now uses a GLib MainLoop, and builds on macOS (tested on
+Intel Mac, 10.15 ). New option -t *timeout* for relaunching server if no
+connections were active in previous *timeout* seconds (to renew Bonjour
+registration).
+
+1.341 2021-09-04 fixed: render logger was not being destroyed by
+stop_server()
+
+1.34 2021-08-27 Fixed "ZOOMFIX": the X11 window name fix was only being
+made the first time the GStreamer window was created by uxplay, and not
+if the server was relaunched after the GStreamer window was closed, with
+uxplay still running. Corrected in v. 1.34
+
+### Building OpenSSL \>= 1.1.1 from source.
+
+If you need to do this, note that you may be able to use a newer version
+(OpenSSL-3.0.1 is known to work). You will need the standard development
+toolset (autoconf, automake, libtool). Download the source code from
+. Install the downloaded openssl by
+opening a terminal in your Downloads directory, and unpacking the source
+distribution: ("tar -xvzf openssl-3.0.1.tar.gz ; cd openssl-3.0.1").
+Then build/install with "./config ; make ; sudo make install_dev". This
+will typically install the needed library `libcrypto.*`, either in
+/usr/local/lib or /usr/local/lib64.
+
+*(Ignore the following for builds on MacOS:)* On some systems like
+Debian or Ubuntu, you may also need to add a missing entry
+`/usr/local/lib64` in /etc/ld.so.conf (or place a file containing
+"/usr/local/lib64/libcrypto.so" in /etc/ld.so.conf.d) and then run "sudo
+ldconfig".
+
+### Building libplist \>= 2.0.0 from source.
+
+*(Note: on Debian 9 "Stretch" or Ubuntu 16.04 LTS editions, you can
+avoid this step by installing libplist-dev and libplist3 from Debian 10
+or Ubuntu 18.04.)* As well as the usual build tools (autoconf, automake,
+libtool), you may need to also install some libpython\*-dev package.
+Download the latest source with git from
+, or get the source from
+the Releases section (use the \*.tar.bz2 release, **not** the \*.zip or
+\*.tar.gz versions): download
+[libplist-2.3.0](https://github.com/libimobiledevice/libplist/releases/download/2.3.0/libplist-2.3.0.tar.bz2),
+then unpack it ("tar -xvjf libplist-2.3.0.tar.bz2 ; cd libplist-2.3.0"),
+and build/install it: ("./configure ; make ; sudo make install"). This
+will probably install libplist-2.0.\* in /usr/local/lib. The new
+libplist-2.3.0 release should be compatible with UxPlay;
+[libplist-2.2.0](https://github.com/libimobiledevice/libplist/releases/download/2.2.0/libplist-2.2.0.tar.bz2)
+is also available if there are any issues.
+
+*(Ignore the following for builds on MacOS:)* On some systems like
+Debian or Ubuntu, you may also need to add a missing entry
+`/usr/local/lib` in /etc/ld.so.conf (or place a file containing
+"/usr/local/lib/libplist-2.0.so" in /etc/ld.so.conf.d) and then run
+"sudo ldconfig".
+
+# Disclaimer
+
+All the resources in this repository are written using only freely
+available information from the internet. The code and related resources
+are meant for educational purposes only. It is the responsibility of the
+user to make sure all local laws are adhered to.
+
+This project makes use of a third-party GPL library for handling
+FairPlay. The legal status of that library is unclear. Should you be a
+representative of Apple and have any objections against the legality of
+the library and its use in this project, please contact the developers
+and the appropriate steps will be taken.
+
+Given the large number of third-party AirPlay receivers (mostly
+closed-source) available for purchase, it is our understanding that an
+open source implementation of the same functionality wouldn't violate
+any of Apple's rights either.
+
+# UxPlay authors
+
+*\[adapted from fdraschbacher's notes on RPiPlay antecedents\]*
+
+The code in this repository accumulated from various sources over time.
+Here is an attempt at listing the various authors and the components
+they created:
+
+UxPlay was initially created by **antimof** from RPiPlay, by replacing
+its Raspberry-Pi-adapted OpenMAX video and audio rendering system with
+GStreamer rendering for desktop Linux systems; the antimof work on code
+in `renderers/` was later backported to RPiPlay, and the antimof project
+became dormant, but was later revived at the [current GitHub
+site](http://github.com/FDH2/UxPlay) to serve a wider community of
+users.
+
+The previous authors of code included in UxPlay by inheritance from
+RPiPlay include:
+
+- **EstebanKubata**: Created a FairPlay library called
+ [PlayFair](https://github.com/EstebanKubata/playfair). Located in
+ the `lib/playfair` folder. License: GNU GPL
+- **Juho Vähä-Herttua** and contributors: Created an AirPlay audio
+ server called [ShairPlay](https://github.com/juhovh/shairplay),
+ including support for Fairplay based on PlayFair. Most of the code
+ in `lib/` originally stems from this project. License: GNU LGPLv2.1+
+- **dsafa22**: Created an AirPlay 2 mirroring server
+ [AirplayServer](https://github.com/dsafa22/AirplayServer) (seems
+ gone now), for Android based on ShairPlay. Code is preserved
+ [here](https://github.com/jiangban/AirplayServer), and [see
+ here](https://github.com/FDH2/UxPlay/wiki/AirPlay2) for the
+ description of the analysis of the AirPlay 2 mirror protocol that
+ made RPiPlay possible, by the AirplayServer author. All code in
+ `lib/` concerning mirroring is dsafa22's work. License: GNU
+ LGPLv2.1+
+- **Florian Draschbacher** (FD-) and contributors: adapted dsafa22's
+ Android project for the Raspberry Pi, with extensive cleanups,
+ debugging and improvements. The project
+ [RPiPlay](https://github.com/FD-/RPiPlay) is basically a port of
+ dsafa22's code to the Raspberry Pi, utilizing OpenMAX and OpenSSL
+ for better performance on the Pi. License GPL v3. FD- has written an
+ interesting note on the history of [Airplay protocol
+ versions](http://github.com/FD-/RPiPlay#airplay-protocol-versions),
+ available at the RPiPlay github repository.
+
+Independent of UxPlay, but used by it and bundled with it:
+
+- **Fedor Indutny** (of Node.js, and formerly Joyent, Inc) and
+ contributors: Created an http parsing library called
+ [llhttp](https://github.com/nodejs/llhttp). Located at
+ `lib/llhttp/`. License: MIT
diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in
new file mode 100644
index 0000000..7db7869
--- /dev/null
+++ b/cmake_uninstall.cmake.in
@@ -0,0 +1,21 @@
+if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+ message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
+endif()
+
+file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+ message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+ if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+ execute_process(
+ COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}"
+ OUTPUT_VARIABLE rm_out
+ RESULT_VARIABLE rm_retval
+ )
+ if(NOT "${rm_retval}" STREQUAL 0)
+ message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+ endif()
+ else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+ message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+ endif()
+endforeach()
diff --git a/compile_commands.json b/compile_commands.json
new file mode 120000
index 0000000..95eca4c
--- /dev/null
+++ b/compile_commands.json
@@ -0,0 +1 @@
+./compile_commands.json
\ No newline at end of file
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..520c5bb
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,173 @@
+cmake_minimum_required(VERSION 3.5)
+include_directories( playfair llhttp )
+
+message( STATUS "*** CFLAGS \"" ${CMAKE_C_FLAGS} "\" from build environment will be postpended to CMAKE_CFLAGS" )
+
+# Common x86/x86_64 cflags
+if( NOT NO_MARCH_NATIVE AND CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)" )
+ set( CMAKE_C_FLAGS "-Ofast -march=native ${CMAKE_C_FLAGS}" )
+ message( STATUS "Using CFLAGS with -march=native" )
+ message( STATUS "*** ONLY USE THIS WHEN COMPILING ON THE MACHINE THAT WILL RUN UXPLAY" )
+ message( STATUS " run \"cmake -DNO_MARCH_NATIVE=ON\" to switch off this compiler option" )
+else()
+ message( STATUS "Not using -march=native" )
+ set( CMAKE_C_FLAGS "-O2 ${CMAKE_C_FLAGS}" )
+endif()
+
+# Common Linux cflags
+if ( UNIX AND NOT APPLE )
+ set( CMAKE_C_FLAGS "-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall ${CMAKE_C_FLAGS}" )
+endif()
+
+if ( WIN32 )
+ message( STATUS "Building for Windows " )
+ set( CMAKE_C_FLAGS "-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_WIN32 -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall ${CMAKE_C_FLAGS}" )
+endif()
+
+message( STATUS "using CMAKE_CFLAGS: " ${CMAKE_C_FLAGS} )
+
+#activate the NOHOLD feature to drop existing connections if a third connection is made
+add_definitions( -DNOHOLD )
+
+INCLUDE (CheckIncludeFiles)
+if( WIN32 )
+CHECK_INCLUDE_FILES ("winsock2.h" WINSOCK2 )
+else()
+# for BSD Unix (e.g. FreeBSD)
+CHECK_INCLUDE_FILES ("sys/endian.h" BSD )
+if ( BSD )
+ add_definitions( -DSYS_ENDIAN_H )
+endif ( BSD )
+endif()
+
+if( APPLE )
+ set( ENV{PKG_CONFIG_PATH} "/usr/local/lib/pkgconfig" ) # standard location, and Brew
+ set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/homebrew/lib/pkgconfig" ) # Brew for M1 macs
+ set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:$ENV{HOMEBREW_PREFIX}/lib/pkgconfig" ) # Brew using prefix
+ set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/local/lib/pkgconfig/" ) # MacPorts
+ set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/openssl@3/lib/pkgconfig" ) # Brew openssl
+ set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/homebrew/opt/openssl@3/lib/pkgconfig" ) # Brew M1 openssl
+ set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:$ENV{HOMEBREW_PREFIX}/opt/openssl@3/lib/pkgconfig" ) # Brew using prefix openssl
+ message( "PKG_CONFIG_PATH (Apple, lib) = " $ENV{PKG_CONFIG_PATH} )
+ find_program( PKG_CONFIG_EXECUTABLE pkg-config PATHS /Library/FrameWorks/GStreamer.framework/Commands )
+ message( "PKG_CONFIG_EXECUTABLE " ${PKG_CONFIG_EXECUTABLE} )
+endif()
+find_package(PkgConfig REQUIRED)
+
+aux_source_directory(. play_src)
+set(DIR_SRCS ${play_src})
+
+add_library( airplay STATIC
+ ${DIR_SRCS}
+ )
+
+if ( APPLE )
+ target_link_libraries( airplay
+ pthread
+ playfair
+ llhttp )
+elseif( WIN32 )
+ target_link_libraries( airplay
+ pthread
+ playfair
+ llhttp
+ wsock32
+ iphlpapi
+ ws2_32 )
+else()
+ target_link_libraries( airplay PUBLIC
+ pthread
+ playfair
+ llhttp )
+endif()
+
+# libplist
+
+if( APPLE )
+ # use static linking
+ pkg_search_module(PLIST REQUIRED libplist-2.0)
+ find_library( LIBPLIST libplist-2.0.a REQUIRED )
+ message( STATUS "(Static linking) LIBPLIST " ${LIBPLIST} )
+ target_link_libraries ( airplay ${LIBPLIST} )
+elseif( WIN32)
+pkg_search_module(PLIST REQUIRED libplist-2.0)
+ find_library( LIBPLIST ${PLIST_LIBRARIES} PATH ${PLIST_LIBDIR} )
+ target_link_libraries ( airplay ${LIBPLIST} )
+else ()
+ pkg_search_module(PLIST libplist>=2.0)
+ if(NOT PLIST_FOUND)
+ pkg_search_module(PLIST REQUIRED libplist-2.0)
+ endif()
+ find_library( LIBPLIST ${PLIST_LIBRARIES} PATH ${PLIST_LIBDIR} )
+ target_link_libraries ( airplay PUBLIC ${LIBPLIST} )
+endif()
+if ( PLIST_FOUND )
+ message( STATUS "found libplist-${PLIST_VERSION}" )
+endif()
+target_include_directories( airplay PRIVATE ${PLIST_INCLUDE_DIRS} )
+
+#libcrypto
+if( APPLE )
+ # use static linking
+ # can either compile Openssl 1.1.1 or 3.0.0 from source (install_dev to /usr/local) or use Macports or Brew
+ # MacPorts needs zlib with it. Brew has a "keg-only" installation in /usr/local/opt/openssl@3
+ # Brew on M1 macs puts this in /opt/homebrew/opt/openssl@3
+ pkg_check_modules( OPENSSL REQUIRED openssl>=1.1.1)
+ message( "OPENSSL_LIBRARY_DIRS " ${OPENSSL_LIBRARY_DIRS} )
+ message( "OPENSSL_INCLUDE_DIRS " ${OPENSSL_INCLUDE_DIRS} )
+ find_library( LIBCRYPTO libcrypto.a PATHS ${OPENSSL_LIBRARY_DIRS} REQUIRED )
+ message( "(Static linking) LIBCRYPTO " ${LIBCRYPTO} )
+ target_link_libraries( airplay ${LIBCRYPTO} )
+ if( LIBCRYPTO MATCHES "/opt/local/lib/libcrypto.a" ) #MacPorts openssl
+ find_library( LIBZ libz.a) # needed by MacPorts openssl
+ message("(MacPorts) LIBZ= " ${LIBZ} )
+ target_link_libraries( airplay ${LIBZ} )
+ endif()
+ target_include_directories( airplay PRIVATE ${OPENSSL_INCLUDE_DIRS} )
+elseif( WIN32 )
+ find_package(OpenSSL 1.1.1 REQUIRED)
+ target_compile_definitions( airplay PUBLIC OPENSSL_API_COMPAT=0x10101000L )
+ target_link_libraries( airplay OpenSSL::Crypto )
+else()
+ find_package(OpenSSL 1.1.1 REQUIRED)
+ target_compile_definitions( airplay PUBLIC OPENSSL_API_COMPAT=0x10101000L )
+ target_link_libraries( airplay PUBLIC OpenSSL::Crypto )
+endif()
+
+#dns_sd
+if ( NOT APPLE )
+ pkg_search_module(AVAHI_DNSSD avahi-compat-libdns_sd)
+ if (AVAHI_DNSSD_FOUND)
+ target_include_directories( airplay PRIVATE ${AVAHI_DNSSD_INCLUDE_DIRS} )
+ find_library( DNSSD ${AVAHI_DNSSD_LIBRARIES} PATH ${AVAHI_DNSSD_LIBDIR})
+ target_link_libraries(airplay PUBLIC ${DNSSD} )
+ else() # can also build if mDNSResponder or another implementation of dns_sd is present instead of Avahi
+ if ( WIN32 )
+ if (DEFINED ENV{BONJOUR_SDK_HOME})
+ set(BONJOUR_SDK "$ENV{BONJOUR_SDK_HOME}" )
+ else()
+ set(BONJOUR_SDK "C:\\Program Files\\Bonjour SDK")
+ endif()
+ message( STATUS "BONJOUR_SDK_HOME " ${BONJOUR_SDK} )
+ set(DNSSD "${BONJOUR_SDK}/Lib/x64/dnssd.lib")
+ target_link_libraries(airplay ${DNSSD} )
+ message( STATUS "dns_sd: using " ${DNSSD} )
+ find_path(DNSSD_INCLUDE_DIR dns_sd.h HINTS ${BONJOUR_SDK}/Include )
+ else()
+ find_library( DNSSD dns_sd )
+ if( NOT DNSSD )
+ message( FATAL_ERROR "libdns_sd missing; can be provided by avahi_compat-libdns_sd or mDNSResponder." )
+ else()
+ message( STATUS "dns_sd: found" ${DNSSD} )
+ endif()
+ target_link_libraries(airplay PUBLIC ${DNSSD} )
+ find_path(DNSSD_INCLUDE_DIR dns_sd.h HINTS ${CMAKE_INSTALL_INCLUDEDIR} )
+ endif()
+ if ( NOT DNSSD_INCLUDE_DIR )
+ message( FATAL_ERROR " dns_sd.h not found ")
+ else()
+ message( STATUS "found dns_sd.h in " ${DNSSD_INCLUDE_DIR} )
+ endif()
+ target_include_directories( airplay PRIVATE ${DNSSD_INCLUDE_DIR} )
+ endif()
+endif()
diff --git a/lib/airplay_video.c b/lib/airplay_video.c
new file mode 100644
index 0000000..5749057
--- /dev/null
+++ b/lib/airplay_video.c
@@ -0,0 +1,322 @@
+/**
+ * Copyright (c) 2024 fduncanh
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+// it should only start and stop the media_data_store that handles all HLS transactions, without
+// otherwise participating in them.
+
+#include
+#include
+#include
+#include
+#include
+
+#include "raop.h"
+#include "airplay_video.h"
+
+struct media_item_s {
+ char *uri;
+ char *playlist;
+ int access;
+};
+
+struct airplay_video_s {
+ raop_t *raop;
+ char apple_session_id[37];
+ char playback_uuid[37];
+ char *uri_prefix;
+ char local_uri_prefix[23];
+ int next_uri;
+ int FCUP_RequestID;
+ float start_position_seconds;
+ playback_info_t *playback_info;
+ // The local port of the airplay server on the AirPlay server
+ unsigned short airplay_port;
+ char *master_uri;
+ char *master_playlist;
+ media_item_t *media_data_store;
+ int num_uri;
+};
+
+// initialize airplay_video service.
+int airplay_video_service_init(raop_t *raop, unsigned short http_port,
+ const char *session_id) {
+ char uri[] = "http://localhost:xxxxx";
+ assert(raop);
+
+ airplay_video_t *airplay_video = deregister_airplay_video(raop);
+ if (airplay_video) {
+ airplay_video_service_destroy(airplay_video);
+ }
+
+ airplay_video = (airplay_video_t *) calloc(1, sizeof(airplay_video_t));
+ if (!airplay_video) {
+ return -1;
+ }
+
+ /* create local_uri_prefix string */
+ strncpy(airplay_video->local_uri_prefix, uri, sizeof(airplay_video->local_uri_prefix));
+ char *ptr = strstr(airplay_video->local_uri_prefix, "xxxxx");
+ snprintf(ptr, 6, "%-5u", http_port);
+ ptr = strstr(airplay_video->local_uri_prefix, " ");
+ if (ptr) {
+ *ptr = '\0';
+ }
+
+ if (!register_airplay_video(raop, airplay_video)) {
+ return -2;
+ }
+
+ //printf(" %p %p\n", airplay_video, get_airplay_video(raop));
+
+ airplay_video->raop = raop;
+
+
+ airplay_video->FCUP_RequestID = 0;
+
+
+ size_t len = strlen(session_id);
+ assert(len == 36);
+ strncpy(airplay_video->apple_session_id, session_id, len);
+ (airplay_video->apple_session_id)[len] = '\0';
+
+ airplay_video->start_position_seconds = 0.0f;
+
+ airplay_video->master_uri = NULL;
+ airplay_video->media_data_store = NULL;
+ airplay_video->master_playlist = NULL;
+ airplay_video->num_uri = 0;
+ airplay_video->next_uri = 0;
+ return 0;
+}
+
+// destroy the airplay_video service
+void
+airplay_video_service_destroy(airplay_video_t *airplay_video)
+{
+
+ if (airplay_video->uri_prefix) {
+ free(airplay_video->uri_prefix);
+ }
+ if (airplay_video->master_uri) {
+ free (airplay_video->master_uri);
+ }
+ if (airplay_video->media_data_store) {
+ destroy_media_data_store(airplay_video);
+ }
+ if (airplay_video->master_playlist) {
+ free (airplay_video->master_playlist);
+ }
+
+
+ free (airplay_video);
+}
+
+const char *get_apple_session_id(airplay_video_t *airplay_video) {
+ return airplay_video->apple_session_id;
+}
+
+float get_start_position_seconds(airplay_video_t *airplay_video) {
+ return airplay_video->start_position_seconds;
+}
+
+void set_start_position_seconds(airplay_video_t *airplay_video, float start_position_seconds) {
+ airplay_video->start_position_seconds = start_position_seconds;
+}
+
+void set_playback_uuid(airplay_video_t *airplay_video, const char *playback_uuid) {
+ size_t len = strlen(playback_uuid);
+ assert(len == 36);
+ memcpy(airplay_video->playback_uuid, playback_uuid, len);
+ (airplay_video->playback_uuid)[len] = '\0';
+}
+
+void set_uri_prefix(airplay_video_t *airplay_video, char *uri_prefix, int uri_prefix_len) {
+ if (airplay_video->uri_prefix) {
+ free (airplay_video->uri_prefix);
+ }
+ airplay_video->uri_prefix = (char *) calloc(uri_prefix_len + 1, sizeof(char));
+ memcpy(airplay_video->uri_prefix, uri_prefix, uri_prefix_len);
+}
+
+char *get_uri_prefix(airplay_video_t *airplay_video) {
+ return airplay_video->uri_prefix;
+}
+
+char *get_uri_local_prefix(airplay_video_t *airplay_video) {
+ return airplay_video->local_uri_prefix;
+}
+
+
+char *get_master_uri(airplay_video_t *airplay_video) {
+ return airplay_video->master_uri;
+}
+
+
+int get_next_FCUP_RequestID(airplay_video_t *airplay_video) {
+ return ++(airplay_video->FCUP_RequestID);
+}
+
+void set_next_media_uri_id(airplay_video_t *airplay_video, int num) {
+ airplay_video->next_uri = num;
+}
+
+int get_next_media_uri_id(airplay_video_t *airplay_video) {
+ return airplay_video->next_uri;
+}
+
+
+/* master playlist */
+
+void store_master_playlist(airplay_video_t *airplay_video, char *master_playlist) {
+ if (airplay_video->master_playlist) {
+ free (airplay_video->master_playlist);
+ }
+ airplay_video->master_playlist = master_playlist;
+}
+
+char *get_master_playlist(airplay_video_t *airplay_video) {
+ return airplay_video->master_playlist;
+}
+
+/* media_data_store */
+
+int get_num_media_uri(airplay_video_t *airplay_video) {
+ return airplay_video->num_uri;
+}
+
+void destroy_media_data_store(airplay_video_t *airplay_video) {
+ media_item_t *media_data_store = airplay_video->media_data_store;
+ if (media_data_store) {
+ for (int i = 0; i < airplay_video->num_uri ; i ++ ) {
+ if (media_data_store[i].uri) {
+ free (media_data_store[i].uri);
+ }
+ if (media_data_store[i].playlist) {
+ free (media_data_store[i].playlist);
+ }
+ }
+ }
+ free (media_data_store);
+ airplay_video->num_uri = 0;
+}
+
+void create_media_data_store(airplay_video_t * airplay_video, char ** uri_list, int num_uri) {
+ destroy_media_data_store(airplay_video);
+ media_item_t *media_data_store = calloc(num_uri, sizeof(media_item_t));
+ for (int i = 0; i < num_uri; i++) {
+ media_data_store[i].uri = uri_list[i];
+ media_data_store[i].playlist = NULL;
+ media_data_store[i].access = 0;
+ }
+ airplay_video->media_data_store = media_data_store;
+ airplay_video->num_uri = num_uri;
+}
+
+int store_media_data_playlist_by_num(airplay_video_t *airplay_video, char * media_playlist, int num) {
+ media_item_t *media_data_store = airplay_video->media_data_store;
+ if ( num < 0 || num >= airplay_video->num_uri) {
+ return -1;
+ } else if (media_data_store[num].playlist) {
+ return -2;
+ }
+ media_data_store[num].playlist = media_playlist;
+ return 0;
+}
+
+char * get_media_playlist_by_num(airplay_video_t *airplay_video, int num) {
+ media_item_t *media_data_store = airplay_video->media_data_store;
+ if (media_data_store == NULL) {
+ return NULL;
+ }
+ if (num >= 0 && num num_uri) {
+ return media_data_store[num].playlist;
+ }
+ return NULL;
+}
+
+int get_media_playlist_by_uri(airplay_video_t *airplay_video, const char *uri) {
+ /* Problem: there can be more than one StreamInf playlist with the same uri:
+ * they differ by choice of partner Media (audio, subtitles) playlists
+ * If the same uri is requested again, one of the other ones will be returned
+ * (the least-previously-requested one will be served up)
+ */
+ // modified to return the position of the media playlist in the master playlist
+ media_item_t *media_data_store = airplay_video->media_data_store;
+ if (media_data_store == NULL) {
+ return -2;
+ }
+ int found = 0;;
+ int num = -1;
+ int access = -1;
+ for (int i = 0; i < airplay_video->num_uri; i++) {
+ if (strstr(media_data_store[i].uri, uri)) {
+ if (!found) {
+ found = 1;
+ num = i;
+ access = media_data_store[i].access;
+ } else {
+ /* change > below to >= to reverse the order of choice */
+ if (access > media_data_store[i].access) {
+ access = media_data_store[i].access;
+ num = i;
+ }
+ }
+ }
+ }
+ if (found) {
+ //printf("found %s\n", media_data_store[num].uri);
+ ++media_data_store[num].access;
+ return num;
+ }
+ return -1;
+}
+
+char * get_media_uri_by_num(airplay_video_t *airplay_video, int num) {
+ media_item_t * media_data_store = airplay_video->media_data_store;
+ if (media_data_store == NULL) {
+ return NULL;
+ }
+ if (num >= 0 && num < airplay_video->num_uri) {
+ return media_data_store[num].uri;
+ }
+ return NULL;
+}
+
+int get_media_uri_num(airplay_video_t *airplay_video, char * uri) {
+ media_item_t *media_data_store = airplay_video->media_data_store;
+ for (int i = 0; i < airplay_video->num_uri ; i++) {
+ if (strstr(media_data_store[i].uri, uri)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int analyze_media_playlist(char *playlist, float *duration) {
+ float next;
+ int count = 0;
+ char *ptr = strstr(playlist, "#EXTINF:");
+ *duration = 0.0f;
+ while (ptr != NULL) {
+ char *end;
+ ptr += strlen("#EXTINF:");
+ next = strtof(ptr, &end);
+ *duration += next;
+ count++;
+ ptr = strstr(end, "#EXTINF:");
+ }
+ return count;
+}
diff --git a/lib/airplay_video.h b/lib/airplay_video.h
new file mode 100644
index 0000000..81fb589
--- /dev/null
+++ b/lib/airplay_video.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024 fduncanh, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ */
+
+#ifndef AIRPLAY_VIDEO_H
+#define AIRPLAY_VIDEO_H
+
+
+#include
+#include
+#include "raop.h"
+#include "logger.h"
+
+typedef struct airplay_video_s airplay_video_t;
+typedef struct media_item_s media_item_t;
+
+const char *get_apple_session_id(airplay_video_t *airplay_video);
+void set_start_position_seconds(airplay_video_t *airplay_video, float start_position_seconds);
+float get_start_position_seconds(airplay_video_t *airplay_video);
+void set_playback_uuid(airplay_video_t *airplay_video, const char *playback_uuid);
+void set_uri_prefix(airplay_video_t *airplay_video, char *uri_prefix, int uri_prefix_len);
+char *get_uri_prefix(airplay_video_t *airplay_video);
+char *get_uri_local_prefix(airplay_video_t *airplay_video);
+int get_next_FCUP_RequestID(airplay_video_t *airplay_video);
+void set_next_media_uri_id(airplay_video_t *airplay_video, int id);
+int get_next_media_uri_id(airplay_video_t *airplay_video);
+int get_media_playlist_by_uri(airplay_video_t *airplay_video, const char *uri);
+void store_master_playlist(airplay_video_t *airplay_video, char *master_playlist);
+char *get_master_playlist(airplay_video_t *airplay_video);
+int get_num_media_uri(airplay_video_t *airplay_video);
+void destroy_media_data_store(airplay_video_t *airplay_video);
+void create_media_data_store(airplay_video_t * airplay_video, char ** media_data_store, int num_uri);
+int store_media_data_playlist_by_num(airplay_video_t *airplay_video, char * media_playlist, int num);
+char *get_media_playlist_by_num(airplay_video_t *airplay_video, int num);
+char *get_media_uri_by_num(airplay_video_t *airplay_video, int num);
+int get_media_uri_num(airplay_video_t *airplay_video, char * uri);
+int analyze_media_playlist(char *playlist, float *duration);
+
+void airplay_video_service_destroy(airplay_video_t *airplay_video);
+
+// C wrappers for c++ class MediaDataStore
+//create the media_data_store, return a pointer to it.
+void* media_data_store_create(void *conn_opaque, uint16_t port);
+
+//delete the media_data_store
+void media_data_store_destroy(void *media_data_store);
+
+// called by the POST /action handler:
+char *process_media_data(void *media_data_store, const char *url, const char *data, int datalen);
+
+//called by the POST /play handler
+bool request_media_data(void *media_data_store, const char *primary_url, const char * session_id);
+
+//called by airplay_video_media_http_connection::get_handler: &path = req.uri)
+char *query_media_data(void *media_data_store, const char *url, int *len);
+
+//called by the post_stop_handler:
+void media_data_store_reset(void *media_data_store);
+
+const char *adjust_primary_uri(void *media_data_store, const char *url);
+
+#endif //AIRPLAY_VIDEO_H
diff --git a/lib/byteutils.c b/lib/byteutils.c
new file mode 100644
index 0000000..1687fb5
--- /dev/null
+++ b/lib/byteutils.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2019 dsafa22, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2021-23
+ */
+
+
+#define SECOND_IN_NSECS 1000000000UL
+
+#include
+#ifdef _WIN32
+# include
+#else
+# include
+#endif
+
+#include "byteutils.h"
+
+#ifdef _WIN32
+# ifndef ntonll
+# define ntohll(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32)))
+# endif
+#else
+# ifndef htonll
+# ifdef SYS_ENDIAN_H
+# include
+# else
+# include
+# endif
+# define htonll(x) htobe64(x)
+# define ntohll(x) be64toh(x)
+# endif
+#endif
+
+// The functions in this file assume a little endian cpu architecture!
+
+/**
+ * Reads a little endian unsigned 16 bit integer from the buffer at position offset
+ */
+uint16_t byteutils_get_short(unsigned char* b, int offset) {
+ return *((uint16_t*)(b + offset));
+}
+
+/**
+ * Reads a little endian unsigned 32 bit integer from the buffer at position offset
+ */
+uint32_t byteutils_get_int(unsigned char* b, int offset) {
+ return *((uint32_t*)(b + offset));
+}
+
+/**
+ * Reads a little endian unsigned 64 bit integer from the buffer at position offset
+ */
+uint64_t byteutils_get_long(unsigned char* b, int offset) {
+ return *((uint64_t*)(b + offset));
+}
+
+/**
+ * Reads a big endian unsigned 16 bit integer from the buffer at position offset
+ */
+uint16_t byteutils_get_short_be(unsigned char* b, int offset) {
+ return ntohs(byteutils_get_short(b, offset));
+}
+
+/**
+ * Reads a big endian unsigned 32 bit integer from the buffer at position offset
+ */
+uint32_t byteutils_get_int_be(unsigned char* b, int offset) {
+ return ntohl(byteutils_get_int(b, offset));
+}
+
+/**
+ * Reads a big endian unsigned 64 bit integer from the buffer at position offset
+ */
+uint64_t byteutils_get_long_be(unsigned char* b, int offset) {
+ return ntohll(byteutils_get_long(b, offset));
+}
+
+/**
+ * Reads a float from the buffer at position offset
+ */
+float byteutils_get_float(unsigned char* b, int offset) {
+ return *((float*)(b + offset));
+}
+
+/**
+ * Writes a little endian unsigned 32 bit integer to the buffer at position offset
+ */
+void byteutils_put_int(unsigned char* b, int offset, uint32_t value) {
+ *((uint32_t*)(b + offset)) = value;
+}
+
+/**
+ * Reads an ntp timestamp and returns it as nano seconds since the Unix epoch
+ */
+uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset) {
+ uint64_t seconds = ntohl(((unsigned int) byteutils_get_int(b, offset))) - SECONDS_FROM_1900_TO_1970;
+ uint64_t fraction = ntohl((unsigned int) byteutils_get_int(b, offset + 4));
+ return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32);
+}
+
+/**
+ * Writes a time given as nano seconds since the Unix time epoch as an ntp timestamp
+ * into the buffer at position offset
+ */
+void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t ns_since_1970) {
+ uint64_t seconds = ns_since_1970 / SECOND_IN_NSECS;
+ uint64_t nanoseconds = ns_since_1970 % SECOND_IN_NSECS;
+ seconds += SECONDS_FROM_1900_TO_1970;
+ uint64_t fraction = (nanoseconds << 32) / SECOND_IN_NSECS;
+
+ // Write in big endian!
+ byteutils_put_int(b, offset, htonl(seconds));
+ byteutils_put_int(b, offset + 4, htonl(fraction));
+}
+
diff --git a/lib/byteutils.h b/lib/byteutils.h
new file mode 100644
index 0000000..9f8a4c5
--- /dev/null
+++ b/lib/byteutils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 dsafa22, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef AIRPLAYSERVER_BYTEUTILS_H
+#define AIRPLAYSERVER_BYTEUTILS_H
+#include
+
+uint16_t byteutils_get_short(unsigned char* b, int offset);
+uint32_t byteutils_get_int(unsigned char* b, int offset);
+uint64_t byteutils_get_long(unsigned char* b, int offset);
+uint16_t byteutils_get_short_be(unsigned char* b, int offset);
+uint32_t byteutils_get_int_be(unsigned char* b, int offset);
+uint64_t byteutils_get_long_be(unsigned char* b, int offset);
+float byteutils_get_float(unsigned char* b, int offset);
+
+#define SECONDS_FROM_1900_TO_1970 2208988800ULL
+
+uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset);
+void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970);
+
+#endif //AIRPLAYSERVER_BYTEUTILS_H
diff --git a/lib/compat.c b/lib/compat.c
new file mode 100644
index 0000000..cba80fc
--- /dev/null
+++ b/lib/compat.c
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright (c) 2024 F. Duncanh, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ */
+
+#ifdef _WIN32
+#include
+#include
+#include "compat.h"
+
+#define MAX_SOCKET_ERROR_MESSAGE_LENGTH 256
+
+/* Windows (winsock2) socket error message text */
+char *wsa_strerror(int errnum) {
+ static char message[MAX_SOCKET_ERROR_MESSAGE_LENGTH] = { 0 };
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, errnum, 0, message, sizeof(message), 0);
+ char *nl = strchr(message, '\n');
+ if (nl) {
+ *nl = 0; /* remove any trailing newline, or truncate to one line */
+ }
+ return message;
+}
+#endif
diff --git a/lib/compat.h b/lib/compat.h
new file mode 100644
index 0000000..4ccfb02
--- /dev/null
+++ b/lib/compat.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef COMPAT_H
+#define COMPAT_H
+
+#if defined(WIN32)
+#include
+#include
+#ifndef snprintf
+#define snprintf _snprintf
+#endif
+#else
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#include "sockets.h"
+#include "threads.h"
+
+#endif
diff --git a/lib/crypto.c b/lib/crypto.c
new file mode 100644
index 0000000..d530d97
--- /dev/null
+++ b/lib/crypto.c
@@ -0,0 +1,581 @@
+/**
+ * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi
+ * Copyright (C) 2019 Florian Draschbacher
+ * Copyright (C) 2020 Jaslo Ziska
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *===================================================================
+ * modified by fduncanh 2021-2022
+ */
+
+#include "crypto.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "utils.h"
+
+#define SALT_PK "UxPlay-Persistent-Not-Secure-Public-Key"
+
+struct aes_ctx_s {
+ EVP_CIPHER_CTX *cipher_ctx;
+ uint8_t key[AES_128_BLOCK_SIZE];
+ uint8_t iv[AES_128_BLOCK_SIZE];
+ aes_direction_t direction;
+ uint8_t block_offset;
+};
+
+uint8_t waste[AES_128_BLOCK_SIZE];
+
+// Common AES utilities
+
+void handle_error(const char* location) {
+ long error = ERR_get_error();
+ const char* error_str = ERR_error_string(error, NULL);
+ fprintf(stderr, "Crypto error at %s: %s\n", location, error_str);
+ exit(EXIT_FAILURE);
+}
+
+aes_ctx_t *aes_init(const uint8_t *key, const uint8_t *iv, const EVP_CIPHER *type, aes_direction_t direction) {
+ aes_ctx_t *ctx = malloc(sizeof(aes_ctx_t));
+ assert(ctx != NULL);
+ ctx->cipher_ctx = EVP_CIPHER_CTX_new();
+ assert(ctx->cipher_ctx != NULL);
+
+ ctx->block_offset = 0;
+ ctx->direction = direction;
+
+ if (direction == AES_ENCRYPT) {
+ if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
+ handle_error(__func__);
+ }
+ } else {
+ if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
+ handle_error(__func__);
+ }
+ }
+
+ memcpy(ctx->key, key, AES_128_BLOCK_SIZE);
+ memcpy(ctx->iv, iv, AES_128_BLOCK_SIZE);
+ EVP_CIPHER_CTX_set_padding(ctx->cipher_ctx, 0);
+ return ctx;
+}
+
+void aes_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) {
+ int out_len_e = 0;
+ if (!EVP_EncryptUpdate(ctx->cipher_ctx, out, &out_len_e, in, in_len)) {
+ handle_error(__func__);
+ }
+ int out_len_f = in_len - out_len_e;
+ if (!EVP_EncryptFinal_ex(ctx->cipher_ctx, out + out_len_e, &out_len_f)) {
+ handle_error(__func__);
+ }
+ assert(out_len_e + out_len_f <= in_len);
+}
+
+void aes_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) {
+ int out_len_d = 0;
+ if (!EVP_DecryptUpdate(ctx->cipher_ctx, out, &out_len_d, in, in_len)) {
+ handle_error(__func__);
+ }
+ int out_len_f = in_len - out_len_d;
+ if (!EVP_DecryptFinal_ex(ctx->cipher_ctx, out + out_len_d, &out_len_f)) {
+ handle_error(__func__);
+ }
+ assert(out_len_f + out_len_d <= in_len);
+}
+
+void aes_destroy(aes_ctx_t *ctx) {
+ if (ctx) {
+ EVP_CIPHER_CTX_free(ctx->cipher_ctx);
+ free(ctx);
+ }
+}
+
+void aes_reset(aes_ctx_t *ctx, const EVP_CIPHER *type, aes_direction_t direction) {
+ uint8_t key[AES_128_BLOCK_SIZE], iv[AES_128_BLOCK_SIZE];
+ memcpy(key, ctx->key, AES_128_BLOCK_SIZE);
+ memcpy(iv, ctx->iv, AES_128_BLOCK_SIZE);
+
+ if (!EVP_CIPHER_CTX_reset(ctx->cipher_ctx)) {
+ handle_error(__func__);
+ }
+
+ if (direction == AES_ENCRYPT) {
+ if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
+ handle_error(__func__);
+ }
+ } else {
+ if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
+ handle_error(__func__);
+ }
+ }
+
+ memcpy(ctx->key, key, AES_128_BLOCK_SIZE);
+ memcpy(ctx->iv, iv, AES_128_BLOCK_SIZE);
+ EVP_CIPHER_CTX_set_padding(ctx->cipher_ctx, 0);
+}
+
+// AES CTR
+
+aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv) {
+ return aes_init(key, iv, EVP_aes_128_ctr(), AES_ENCRYPT);
+}
+
+void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
+ aes_encrypt(ctx, in, out, len);
+ ctx->block_offset = (ctx->block_offset + len) % AES_128_BLOCK_SIZE;
+}
+
+void aes_ctr_start_fresh_block(aes_ctx_t *ctx) {
+ // Is there a better way to do this?
+ if (ctx->block_offset == 0) return;
+ aes_ctr_encrypt(ctx, waste, waste, AES_128_BLOCK_SIZE - ctx->block_offset);
+}
+
+void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
+ aes_encrypt(ctx, in, out, len);
+}
+
+void aes_ctr_reset(aes_ctx_t *ctx) {
+ aes_reset(ctx, EVP_aes_128_ctr(), AES_ENCRYPT);
+}
+
+void aes_ctr_destroy(aes_ctx_t *ctx) {
+ aes_destroy(ctx);
+}
+
+// AES CBC
+
+aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction) {
+ return aes_init(key, iv, EVP_aes_128_cbc(), direction);
+}
+
+void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
+ assert(ctx->direction == AES_ENCRYPT);
+ aes_encrypt(ctx, in, out, len);
+}
+
+void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
+ assert(ctx->direction == AES_DECRYPT);
+ aes_decrypt(ctx, in, out, len);
+}
+
+void aes_cbc_reset(aes_ctx_t *ctx) {
+ aes_reset(ctx, EVP_aes_128_cbc(), ctx->direction);
+}
+
+void aes_cbc_destroy(aes_ctx_t *ctx) {
+ aes_destroy(ctx);
+}
+
+// X25519
+
+struct x25519_key_s {
+ EVP_PKEY *pkey;
+};
+
+x25519_key_t *x25519_key_generate(void) {
+ x25519_key_t *key;
+ EVP_PKEY_CTX *pctx;
+
+ key = calloc(1, sizeof(x25519_key_t));
+ assert(key);
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
+ if (!pctx) {
+ handle_error(__func__);
+ }
+ if (!EVP_PKEY_keygen_init(pctx)) {
+ handle_error(__func__);
+ }
+ if (!EVP_PKEY_keygen(pctx, &key->pkey)) {
+ handle_error(__func__);
+ }
+ EVP_PKEY_CTX_free(pctx);
+
+ return key;
+}
+
+x25519_key_t *x25519_key_from_raw(const unsigned char data[X25519_KEY_SIZE]) {
+ x25519_key_t *key;
+
+ key = malloc(sizeof(x25519_key_t));
+ assert(key);
+
+ key->pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, data, X25519_KEY_SIZE);
+ if (!key->pkey) {
+ handle_error(__func__);
+ }
+
+ return key;
+}
+
+void x25519_key_get_raw(unsigned char data[X25519_KEY_SIZE], const x25519_key_t *key) {
+ assert(key);
+ if (!EVP_PKEY_get_raw_public_key(key->pkey, data, &(size_t) {X25519_KEY_SIZE})) {
+ handle_error(__func__);
+ }
+}
+
+void x25519_key_destroy(x25519_key_t *key) {
+ if (key) {
+ EVP_PKEY_free(key->pkey);
+ free(key);
+ }
+}
+
+void x25519_derive_secret(unsigned char secret[X25519_KEY_SIZE], const x25519_key_t *ours, const x25519_key_t *theirs) {
+ EVP_PKEY_CTX *pctx;
+
+ assert(ours);
+ assert(theirs);
+
+ pctx = EVP_PKEY_CTX_new(ours->pkey, NULL);
+ if (!pctx) {
+ handle_error(__func__);
+ }
+ if (!EVP_PKEY_derive_init(pctx)) {
+ handle_error(__func__);
+ }
+ if (!EVP_PKEY_derive_set_peer(pctx, theirs->pkey)) {
+ handle_error(__func__);
+ }
+ if (!EVP_PKEY_derive(pctx, secret, &(size_t) {X25519_KEY_SIZE})) {
+ handle_error(__func__);
+ }
+ EVP_PKEY_CTX_free(pctx);
+}
+
+// GCM AES 128
+
+int gcm_encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *ciphertext,
+ unsigned char *key, unsigned char *iv, unsigned char *tag)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ int len;
+
+ int ciphertext_len;
+
+ if(!(ctx = EVP_CIPHER_CTX_new()))
+ handle_error(__func__);
+
+ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL))
+ handle_error(__func__);
+
+ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
+ handle_error(__func__);
+
+ if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
+ handle_error(__func__);
+
+ if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
+ handle_error(__func__);
+ ciphertext_len = len;
+
+ if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
+ handle_error(__func__);
+ ciphertext_len += len;
+
+ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
+ handle_error(__func__);
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ return ciphertext_len;
+}
+
+int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *plaintext,
+ unsigned char *key, unsigned char *iv, unsigned char *tag)
+{
+ EVP_CIPHER_CTX *ctx;
+ int len;
+ int plaintext_len;
+ int ret;
+
+ if(!(ctx = EVP_CIPHER_CTX_new()))
+ handle_error(__func__);
+
+ if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL))
+ handle_error(__func__);
+
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
+ handle_error(__func__);
+
+ if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
+ handle_error(__func__);
+
+ if(!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
+ handle_error(__func__);
+ plaintext_len = len;
+
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag))
+ handle_error(__func__);
+
+ ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ if(ret > 0) {
+ /* Success */
+ plaintext_len += len;
+ return plaintext_len;
+ } else {
+ /* Verify failed */
+ return -1;
+ }
+}
+
+// ED25519
+
+struct ed25519_key_s {
+ EVP_PKEY *pkey;
+};
+
+ed25519_key_t *ed25519_key_generate(const char *device_id, const char *keyfile, int *result) {
+ ed25519_key_t *key;
+ EVP_PKEY_CTX *pctx;
+ BIO *bp;
+ FILE *file;
+ bool new_pk = false;
+ bool use_keyfile = strlen(keyfile);
+
+ *result = 0;
+
+ key = calloc(1, sizeof(ed25519_key_t));
+ assert(key);
+
+ if (use_keyfile) {
+ file = fopen(keyfile, "r");
+ if (file) {
+ bp = BIO_new_fp(file, BIO_NOCLOSE);
+ key->pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
+ BIO_free(bp);
+ fclose(file);
+ if (!key->pkey) {
+ new_pk = true;
+ }
+ } else {
+ new_pk = true;
+ }
+ } else {
+ /* generate (insecure) persistent keypair using device_id */
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ char salt[] = SALT_PK;
+ sha_ctx_t *ctx = sha_init();
+ sha_update(ctx, (const unsigned char *) salt, (unsigned int) strlen(salt));
+ sha_update(ctx, (const unsigned char *) device_id, (unsigned int) strlen(device_id));
+ sha_final(ctx, hash, NULL);
+ sha_destroy(ctx);
+ key->pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, (const unsigned char *) hash, ED25519_KEY_SIZE);
+ }
+
+ if (new_pk) {
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL);
+ if (!pctx) {
+ handle_error(__func__);
+ }
+ if (!EVP_PKEY_keygen_init(pctx)) {
+ handle_error(__func__);
+ }
+ if (!EVP_PKEY_keygen(pctx, &key->pkey)) {
+ handle_error(__func__);
+ }
+ EVP_PKEY_CTX_free(pctx);
+ if (use_keyfile) {
+ file = fopen(keyfile, "w");
+ if (file) {
+ bp = BIO_new_fp(file, BIO_NOCLOSE);
+ PEM_write_bio_PrivateKey(bp, key->pkey, NULL, NULL, 0, NULL, NULL);
+ BIO_free(bp);
+ fclose(file);
+ *result = 1;
+ }
+ }
+ }
+ return key;
+}
+
+ed25519_key_t *ed25519_key_from_raw(const unsigned char data[ED25519_KEY_SIZE]) {
+ ed25519_key_t *key;
+
+ key = malloc(sizeof(ed25519_key_t));
+ assert(key);
+
+ key->pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, data, ED25519_KEY_SIZE);
+ if (!key->pkey) {
+ handle_error(__func__);
+ }
+
+ return key;
+}
+
+void ed25519_key_get_raw(unsigned char data[ED25519_KEY_SIZE], const ed25519_key_t *key) {
+ assert(key);
+ if (!EVP_PKEY_get_raw_public_key(key->pkey, data, &(size_t) {ED25519_KEY_SIZE})) {
+ handle_error(__func__);
+ }
+}
+
+ed25519_key_t *ed25519_key_copy(const ed25519_key_t *key) {
+ ed25519_key_t *new_key;
+
+ assert(key);
+
+ new_key = malloc(sizeof(ed25519_key_t));
+ assert(new_key);
+
+ new_key->pkey = key->pkey;
+ if (!EVP_PKEY_up_ref(key->pkey)) {
+ handle_error(__func__);
+ }
+
+ return new_key;
+}
+
+void ed25519_sign(unsigned char *signature, size_t signature_len,
+ const unsigned char *data, size_t data_len,
+ const ed25519_key_t *key)
+{
+ EVP_MD_CTX *mctx;
+
+ mctx = EVP_MD_CTX_new();
+ if (!mctx) {
+ handle_error(__func__);
+ }
+
+ if (!EVP_DigestSignInit(mctx, NULL, NULL, NULL, key->pkey)) {
+ handle_error(__func__);
+ }
+ if (!EVP_DigestSign(mctx, signature, &signature_len, data, data_len)) {
+ handle_error(__func__);
+ }
+
+ EVP_MD_CTX_free(mctx);
+}
+
+int ed25519_verify(const unsigned char *signature, size_t signature_len,
+ const unsigned char *data, size_t data_len,
+ const ed25519_key_t *key)
+{
+ EVP_MD_CTX *mctx;
+
+ mctx = EVP_MD_CTX_new();
+ if (!mctx) {
+ handle_error(__func__);
+ }
+
+ if (!EVP_DigestVerifyInit(mctx, NULL, NULL, NULL, key->pkey)) {
+ handle_error(__func__);
+ }
+
+ int ret = EVP_DigestVerify(mctx, signature, signature_len, data, data_len);
+ if (ret < 0) {
+ handle_error(__func__);
+ }
+
+ EVP_MD_CTX_free(mctx);
+
+ return ret;
+}
+
+void ed25519_key_destroy(ed25519_key_t *key) {
+ if (key) {
+ EVP_PKEY_free(key->pkey);
+ free(key);
+ }
+}
+
+// SHA 512
+
+struct sha_ctx_s {
+ EVP_MD_CTX *digest_ctx;
+};
+
+sha_ctx_t *sha_init() {
+ sha_ctx_t *ctx = malloc(sizeof(sha_ctx_t));
+ assert(ctx != NULL);
+ ctx->digest_ctx = EVP_MD_CTX_new();
+ assert(ctx->digest_ctx != NULL);
+
+ if (!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
+ handle_error(__func__);
+ }
+ return ctx;
+}
+
+void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len) {
+ if (!EVP_DigestUpdate(ctx->digest_ctx, in, len)) {
+ handle_error(__func__);
+ }
+}
+
+void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len) {
+ if (!EVP_DigestFinal_ex(ctx->digest_ctx, out, len)) {
+ handle_error(__func__);
+ }
+}
+
+void sha_reset(sha_ctx_t *ctx) {
+ if (!EVP_MD_CTX_reset(ctx->digest_ctx) ||
+ !EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
+
+ handle_error(__func__);
+ }
+}
+
+void sha_destroy(sha_ctx_t *ctx) {
+ if (ctx) {
+ EVP_MD_CTX_free(ctx->digest_ctx);
+ free(ctx);
+ }
+}
+
+int get_random_bytes(unsigned char *buf, int num) {
+ return RAND_bytes(buf, num);
+}
+#include
+void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len) {
+ memset(pk_base64, 0, len);
+ int len64 = (4 * (pk_len /3)) + (pk_len % 3 ? 4 : 0);
+
+ assert (len > len64);
+
+ BIO *b64 = BIO_new(BIO_f_base64());
+ BIO *bio = BIO_new(BIO_s_mem());
+ BUF_MEM *bufferPtr;
+
+
+ bio = BIO_push(b64, bio);
+
+ BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
+ BIO_write(bio, pk, pk_len);
+ BIO_flush(bio);
+
+ BIO_get_mem_ptr(bio, &bufferPtr);
+ BIO_set_close(bio, BIO_NOCLOSE);
+ BIO_free_all(bio);
+ memcpy(pk_base64,(*bufferPtr).data, len64);
+}
+
diff --git a/lib/crypto.h b/lib/crypto.h
new file mode 100644
index 0000000..7bd2d5e
--- /dev/null
+++ b/lib/crypto.h
@@ -0,0 +1,115 @@
+/**
+ * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi
+ * Copyright (C) 2019 Florian Draschbacher
+ * Copyright (C) 2020 Jaslo Ziska
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * modified by fduncanh 2023
+ */
+
+/*
+ * Helper methods for various crypto operations.
+ * Uses OpenSSL behind the scenes.
+*/
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 128bit AES in CTR mode
+
+#define AES_128_BLOCK_SIZE 16
+
+typedef enum aes_direction_e { AES_DECRYPT, AES_ENCRYPT } aes_direction_t;
+
+typedef struct aes_ctx_s aes_ctx_t;
+
+aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv);
+void aes_ctr_reset(aes_ctx_t *ctx);
+void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
+void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
+void aes_ctr_start_fresh_block(aes_ctx_t *ctx);
+void aes_ctr_destroy(aes_ctx_t *ctx);
+
+aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction);
+void aes_cbc_reset(aes_ctx_t *ctx);
+void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
+void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
+void aes_cbc_destroy(aes_ctx_t *ctx);
+
+// X25519
+
+#define X25519_KEY_SIZE 32
+
+typedef struct x25519_key_s x25519_key_t;
+
+x25519_key_t *x25519_key_generate(void);
+x25519_key_t *x25519_key_from_raw(const unsigned char data[X25519_KEY_SIZE]);
+void x25519_key_get_raw(unsigned char data[X25519_KEY_SIZE], const x25519_key_t *key);
+void x25519_key_destroy(x25519_key_t *key);
+int get_random_bytes(unsigned char *buf, int num);
+void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len);
+
+void x25519_derive_secret(unsigned char secret[X25519_KEY_SIZE], const x25519_key_t *ours, const x25519_key_t *theirs);
+
+// GCM AES 128
+
+int gcm_encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *ciphertext,
+ unsigned char *key, unsigned char *iv, unsigned char *tag);
+int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *plaintext,
+ unsigned char *key, unsigned char *iv, unsigned char *tag);
+// ED25519
+
+#define ED25519_KEY_SIZE 32
+
+typedef struct ed25519_key_s ed25519_key_t;
+
+ed25519_key_t *ed25519_key_generate(const char *device_id, const char * keyfile, int * result);
+ed25519_key_t *ed25519_key_from_raw(const unsigned char data[ED25519_KEY_SIZE]);
+void ed25519_key_get_raw(unsigned char data[ED25519_KEY_SIZE], const ed25519_key_t *key);
+/*
+ * Note that this function does *not copy* the OpenSSL key but only the wrapper. The internal OpenSSL key is still the
+ * same. Only the reference count is increased so destroying both the original and the copy is allowed.
+ */
+ed25519_key_t *ed25519_key_copy(const ed25519_key_t *key);
+void ed25519_key_destroy(ed25519_key_t *key);
+
+void ed25519_sign(unsigned char *signature, size_t signature_len,
+ const unsigned char *data, size_t data_len,
+ const ed25519_key_t *key);
+int ed25519_verify(const unsigned char *signature, size_t signature_len,
+ const unsigned char *data, size_t data_len,
+ const ed25519_key_t *key);
+
+// SHA512
+
+typedef struct sha_ctx_s sha_ctx_t;
+sha_ctx_t *sha_init();
+void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len);
+void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len);
+void sha_reset(sha_ctx_t *ctx);
+void sha_destroy(sha_ctx_t *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/dnssd.c b/lib/dnssd.c
new file mode 100644
index 0000000..0725937
--- /dev/null
+++ b/lib/dnssd.c
@@ -0,0 +1,482 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2022
+ */
+
+/* These defines allow us to compile on iOS */
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+#ifndef __has_extension
+# define __has_extension __has_feature
+#endif
+
+#include
+#include
+#include
+#include
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dnssdint.h"
+#include "dnssd.h"
+#include "global.h"
+#include "compat.h"
+#include "utils.h"
+
+#include
+
+#define MAX_DEVICEID 18
+#define MAX_SERVNAME 256
+
+#if defined(HAVE_LIBDL) && !defined(__APPLE__)
+# define USE_LIBDL 1
+#else
+# define USE_LIBDL 0
+#endif
+
+#if defined(_WIN32) || USE_LIBDL
+# ifdef _WIN32
+# include
+# if !defined(EFI32) && !defined(EFI64)
+# define DNSSD_STDCALL __stdcall
+# else
+# define DNSSD_STDCALL
+# endif
+# else
+# include
+# define DNSSD_STDCALL
+# endif
+
+typedef struct _DNSServiceRef_t *DNSServiceRef;
+#ifndef _WIN32
+typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
+#endif
+typedef uint32_t DNSServiceFlags;
+typedef int32_t DNSServiceErrorType;
+
+typedef void (DNSSD_STDCALL *DNSServiceRegisterReply)
+ (
+ DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ DNSServiceErrorType errorCode,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ void *context
+ );
+
+#else
+//# include
+# define DNSSD_STDCALL
+#endif
+
+typedef DNSServiceErrorType (DNSSD_STDCALL *DNSServiceRegister_t)
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *name,
+ const char *regtype,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ uint16_t txtLen,
+ const void *txtRecord,
+ DNSServiceRegisterReply callBack,
+ void *context
+ );
+typedef void (DNSSD_STDCALL *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef);
+typedef void (DNSSD_STDCALL *TXTRecordCreate_t)
+ (
+ TXTRecordRef *txtRecord,
+ uint16_t bufferLen,
+ void *buffer
+ );
+typedef void (DNSSD_STDCALL *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord);
+typedef DNSServiceErrorType (DNSSD_STDCALL *TXTRecordSetValue_t)
+ (
+ TXTRecordRef *txtRecord,
+ const char *key,
+ uint8_t valueSize,
+ const void *value
+ );
+typedef uint16_t (DNSSD_STDCALL *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord);
+typedef const void * (DNSSD_STDCALL *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord);
+
+
+struct dnssd_s {
+#ifdef WIN32
+ HMODULE module;
+#elif USE_LIBDL
+ void *module;
+#endif
+
+ DNSServiceRegister_t DNSServiceRegister;
+ DNSServiceRefDeallocate_t DNSServiceRefDeallocate;
+ TXTRecordCreate_t TXTRecordCreate;
+ TXTRecordSetValue_t TXTRecordSetValue;
+ TXTRecordGetLength_t TXTRecordGetLength;
+ TXTRecordGetBytesPtr_t TXTRecordGetBytesPtr;
+ TXTRecordDeallocate_t TXTRecordDeallocate;
+
+ TXTRecordRef raop_record;
+ TXTRecordRef airplay_record;
+
+ DNSServiceRef raop_service;
+ DNSServiceRef airplay_service;
+
+ char *name;
+ int name_len;
+
+ char *hw_addr;
+ int hw_addr_len;
+
+ char *pk;
+
+ uint32_t features1;
+ uint32_t features2;
+
+ unsigned char require_pw;
+};
+
+
+
+dnssd_t *
+dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len, int *error, int require_pw)
+{
+ dnssd_t *dnssd;
+ char *end;
+ unsigned long features;
+
+ if (error) *error = DNSSD_ERROR_NOERROR;
+
+ dnssd = calloc(1, sizeof(dnssd_t));
+ if (!dnssd) {
+ if (error) *error = DNSSD_ERROR_OUTOFMEM;
+ return NULL;
+ }
+
+ dnssd->require_pw = (unsigned char) require_pw;
+
+ features = strtoul(FEATURES_1, &end, 16);
+ if (!end || (features & 0xFFFFFFFF) != features) {
+ free (dnssd);
+ if (error) *error = DNSSD_ERROR_BADFEATURES;
+ return NULL;
+ }
+ dnssd->features1 = (uint32_t) features;
+
+ features = strtoul(FEATURES_2, &end, 16);
+ if (!end || (features & 0xFFFFFFFF) != features) {
+ free (dnssd);
+ if (error) *error = DNSSD_ERROR_BADFEATURES;
+ return NULL;
+ }
+ dnssd->features2 = (uint32_t) features;
+
+#ifdef WIN32
+ dnssd->module = LoadLibraryA("dnssd.dll");
+ if (!dnssd->module) {
+ if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
+ free(dnssd);
+ return NULL;
+ }
+ dnssd->DNSServiceRegister = (DNSServiceRegister_t)GetProcAddress(dnssd->module, "DNSServiceRegister");
+ dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)GetProcAddress(dnssd->module, "DNSServiceRefDeallocate");
+ dnssd->TXTRecordCreate = (TXTRecordCreate_t)GetProcAddress(dnssd->module, "TXTRecordCreate");
+ dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)GetProcAddress(dnssd->module, "TXTRecordSetValue");
+ dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)GetProcAddress(dnssd->module, "TXTRecordGetLength");
+ dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)GetProcAddress(dnssd->module, "TXTRecordGetBytesPtr");
+ dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)GetProcAddress(dnssd->module, "TXTRecordDeallocate");
+
+ if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
+ !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
+ !dnssd->TXTRecordDeallocate) {
+ if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
+ FreeLibrary(dnssd->module);
+ free(dnssd);
+ return NULL;
+ }
+#elif USE_LIBDL
+ dnssd->module = dlopen("libdns_sd.so", RTLD_LAZY);
+ if (!dnssd->module) {
+ if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
+ free(dnssd);
+ return NULL;
+ }
+ dnssd->DNSServiceRegister = (DNSServiceRegister_t)dlsym(dnssd->module, "DNSServiceRegister");
+ dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)dlsym(dnssd->module, "DNSServiceRefDeallocate");
+ dnssd->TXTRecordCreate = (TXTRecordCreate_t)dlsym(dnssd->module, "TXTRecordCreate");
+ dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)dlsym(dnssd->module, "TXTRecordSetValue");
+ dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)dlsym(dnssd->module, "TXTRecordGetLength");
+ dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)dlsym(dnssd->module, "TXTRecordGetBytesPtr");
+ dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)dlsym(dnssd->module, "TXTRecordDeallocate");
+
+ if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
+ !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
+ !dnssd->TXTRecordDeallocate) {
+ if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
+ dlclose(dnssd->module);
+ free(dnssd);
+ return NULL;
+ }
+#else
+ dnssd->DNSServiceRegister = &DNSServiceRegister;
+ dnssd->DNSServiceRefDeallocate = &DNSServiceRefDeallocate;
+ dnssd->TXTRecordCreate = &TXTRecordCreate;
+ dnssd->TXTRecordSetValue = &TXTRecordSetValue;
+ dnssd->TXTRecordGetLength = &TXTRecordGetLength;
+ dnssd->TXTRecordGetBytesPtr = &TXTRecordGetBytesPtr;
+ dnssd->TXTRecordDeallocate = &TXTRecordDeallocate;
+#endif
+
+ dnssd->name_len = name_len;
+ dnssd->name = calloc(1, name_len + 1);
+ if (!dnssd->name) {
+ free(dnssd);
+ if (error) *error = DNSSD_ERROR_OUTOFMEM;
+ return NULL;
+ }
+ memcpy(dnssd->name, name, name_len);
+
+ dnssd->hw_addr_len = hw_addr_len;
+ dnssd->hw_addr = calloc(1, dnssd->hw_addr_len);
+ if (!dnssd->hw_addr) {
+ free(dnssd->name);
+ free(dnssd);
+ if (error) *error = DNSSD_ERROR_OUTOFMEM;
+ return NULL;
+ }
+
+ memcpy(dnssd->hw_addr, hw_addr, hw_addr_len);
+
+ return dnssd;
+}
+
+void
+dnssd_destroy(dnssd_t *dnssd)
+{
+ if (dnssd) {
+#ifdef WIN32
+ FreeLibrary(dnssd->module);
+#elif USE_LIBDL
+ dlclose(dnssd->module);
+#endif
+ free(dnssd);
+ }
+}
+
+int
+dnssd_register_raop(dnssd_t *dnssd, unsigned short port)
+{
+ char servname[MAX_SERVNAME];
+ DNSServiceErrorType retval;
+ char features[22];
+
+ assert(dnssd);
+
+ snprintf(features, sizeof(features), "0x%X,0x%X", dnssd->features1, dnssd->features2);
+
+ dnssd->TXTRecordCreate(&dnssd->raop_record, 0, NULL);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "ch", strlen(RAOP_CH), RAOP_CH);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "cn", strlen(RAOP_CN), RAOP_CN);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "da", strlen(RAOP_DA), RAOP_DA);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "et", strlen(RAOP_ET), RAOP_ET);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "vv", strlen(RAOP_VV), RAOP_VV);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "ft", strlen(features), features);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "am", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "md", strlen(RAOP_MD), RAOP_MD);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "rhd", strlen(RAOP_RHD), RAOP_RHD);
+ if (dnssd->require_pw) {
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("true"), "true");
+ } else {
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("false"), "false");
+ }
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "sr", strlen(RAOP_SR), RAOP_SR);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "ss", strlen(RAOP_SS), RAOP_SS);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "sv", strlen(RAOP_SV), RAOP_SV);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "tp", strlen(RAOP_TP), RAOP_TP);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "txtvers", strlen(RAOP_TXTVERS), RAOP_TXTVERS);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", strlen(RAOP_SF), RAOP_SF);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "vs", strlen(RAOP_VS), RAOP_VS);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "vn", strlen(RAOP_VN), RAOP_VN);
+ dnssd->TXTRecordSetValue(&dnssd->raop_record, "pk", strlen(dnssd->pk), dnssd->pk);
+
+ /* Convert hardware address to string */
+ if (utils_hwaddr_raop(servname, sizeof(servname), dnssd->hw_addr, dnssd->hw_addr_len) < 0) {
+ /* FIXME: handle better */
+ return -1;
+ }
+
+ /* Check that we have bytes for 'hw@name' format */
+ if (sizeof(servname) < strlen(servname) + 1 + dnssd->name_len + 1) {
+ /* FIXME: handle better */
+ return -2;
+ }
+
+ strncat(servname, "@", sizeof(servname)-strlen(servname)-1);
+ strncat(servname, dnssd->name, sizeof(servname)-strlen(servname)-1);
+
+ /* Register the service */
+ retval = dnssd->DNSServiceRegister(&dnssd->raop_service, 0, 0,
+ servname, "_raop._tcp",
+ NULL, NULL,
+ htons(port),
+ dnssd->TXTRecordGetLength(&dnssd->raop_record),
+ dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record),
+ NULL, NULL);
+
+ return (int) retval; /* error codes are listed in Apple's dns_sd.h */
+}
+
+int
+dnssd_register_airplay(dnssd_t *dnssd, unsigned short port)
+{
+ char device_id[3 * MAX_HWADDR_LEN];
+ DNSServiceErrorType retval;
+ char features[22];
+
+ assert(dnssd);
+
+ snprintf(features, sizeof(features), "0x%X,0x%X", dnssd->features1, dnssd->features2);
+
+ /* Convert hardware address to string */
+ if (utils_hwaddr_airplay(device_id, sizeof(device_id), dnssd->hw_addr, dnssd->hw_addr_len) < 0) {
+ /* FIXME: handle better */
+ return -1;
+ }
+
+
+ dnssd->TXTRecordCreate(&dnssd->airplay_record, 0, NULL);
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "deviceid", strlen(device_id), device_id);
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "features", strlen(features), features);
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", strlen(AIRPLAY_FLAGS), AIRPLAY_FLAGS);
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pk", strlen(dnssd->pk), dnssd->pk);
+ if (dnssd->require_pw) {
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("true"), "true");
+ } else {
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("false"), "false");
+ }
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pi", strlen(AIRPLAY_PI), AIRPLAY_PI);
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS);
+ dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV);
+
+ /* Register the service */
+ retval = dnssd->DNSServiceRegister(&dnssd->airplay_service, 0, 0,
+ dnssd->name, "_airplay._tcp",
+ NULL, NULL,
+ htons(port),
+ dnssd->TXTRecordGetLength(&dnssd->airplay_record),
+ dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record),
+ NULL, NULL);
+
+ return (int) retval; /* error codes are listed in Apple's dns_sd.h */
+}
+
+const char *
+dnssd_get_airplay_txt(dnssd_t *dnssd, int *length)
+{
+ *length = dnssd->TXTRecordGetLength(&dnssd->airplay_record);
+ return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record);
+}
+
+const char *
+dnssd_get_name(dnssd_t *dnssd, int *length)
+{
+ *length = dnssd->name_len;
+ return dnssd->name;
+}
+
+const char *
+dnssd_get_hw_addr(dnssd_t *dnssd, int *length)
+{
+ *length = dnssd->hw_addr_len;
+ return dnssd->hw_addr;
+}
+
+void
+dnssd_unregister_raop(dnssd_t *dnssd)
+{
+ assert(dnssd);
+
+ if (!dnssd->raop_service) {
+ return;
+ }
+
+ /* Deallocate TXT record */
+ dnssd->TXTRecordDeallocate(&dnssd->raop_record);
+
+ dnssd->DNSServiceRefDeallocate(dnssd->raop_service);
+ dnssd->raop_service = NULL;
+
+ if (dnssd->airplay_service == NULL) {
+ free(dnssd->name);
+ free(dnssd->hw_addr);
+ }
+}
+
+void
+dnssd_unregister_airplay(dnssd_t *dnssd)
+{
+ assert(dnssd);
+
+ if (!dnssd->airplay_service) {
+ return;
+ }
+
+ /* Deallocate TXT record */
+ dnssd->TXTRecordDeallocate(&dnssd->airplay_record);
+
+ dnssd->DNSServiceRefDeallocate(dnssd->airplay_service);
+ dnssd->airplay_service = NULL;
+
+ if (dnssd->raop_service == NULL) {
+ free(dnssd->name);
+ free(dnssd->hw_addr);
+ }
+}
+
+uint64_t dnssd_get_airplay_features(dnssd_t *dnssd) {
+ uint64_t features = ((uint64_t) dnssd->features2) << 32;
+ features += (uint64_t) dnssd->features1;
+ return features;
+}
+
+void dnssd_set_pk(dnssd_t *dnssd, char * pk_str) {
+ dnssd->pk = pk_str;
+}
+
+void dnssd_set_airplay_features(dnssd_t *dnssd, int bit, int val) {
+ uint32_t mask;
+ uint32_t *features;
+ if (bit < 0 || bit > 63) return;
+ if (val < 0 || val > 1) return;
+ if (bit >= 32) {
+ mask = 0x1 << (bit - 32);
+ features = &(dnssd->features2);
+ } else {
+ mask = 0x1 << bit;
+ features = &(dnssd->features1);
+ }
+ if (val) {
+ *features = *features | mask;
+ } else {
+ *features = *features & ~mask;
+ }
+}
diff --git a/lib/dnssd.h b/lib/dnssd.h
new file mode 100644
index 0000000..1f83210
--- /dev/null
+++ b/lib/dnssd.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef DNSSD_H
+#define DNSSD_H
+#include
+
+#if defined(WIN32) && defined(DLL_EXPORT)
+# define DNSSD_API __declspec(dllexport)
+#else
+# define DNSSD_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DNSSD_ERROR_NOERROR 0
+#define DNSSD_ERROR_HWADDRLEN 1
+#define DNSSD_ERROR_OUTOFMEM 2
+#define DNSSD_ERROR_LIBNOTFOUND 3
+#define DNSSD_ERROR_PROCNOTFOUND 4
+#define DNSSD_ERROR_BADFEATURES 5
+
+typedef struct dnssd_s dnssd_t;
+
+ DNSSD_API dnssd_t *dnssd_init(const char *name, int name_len, const char *hw_addr, int hw_addr_len, int *error, int require_pw);
+
+DNSSD_API int dnssd_register_raop(dnssd_t *dnssd, unsigned short port);
+DNSSD_API int dnssd_register_airplay(dnssd_t *dnssd, unsigned short port);
+
+DNSSD_API void dnssd_unregister_raop(dnssd_t *dnssd);
+DNSSD_API void dnssd_unregister_airplay(dnssd_t *dnssd);
+
+DNSSD_API const char *dnssd_get_airplay_txt(dnssd_t *dnssd, int *length);
+DNSSD_API const char *dnssd_get_name(dnssd_t *dnssd, int *length);
+DNSSD_API const char *dnssd_get_hw_addr(dnssd_t *dnssd, int *length);
+DNSSD_API void dnssd_set_airplay_features(dnssd_t *dnssd, int bit, int val);
+DNSSD_API uint64_t dnssd_get_airplay_features(dnssd_t *dnssd);
+DNSSD_API void dnssd_set_pk(dnssd_t *dnssd, char * pk_str);
+
+DNSSD_API void dnssd_destroy(dnssd_t *dnssd);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/dnssdint.h b/lib/dnssdint.h
new file mode 100644
index 0000000..6a457d0
--- /dev/null
+++ b/lib/dnssdint.h
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2022
+ */
+
+#ifndef DNSSDINT_H
+#define DNSSDINT_H
+
+#include "global.h"
+
+/* the previous behavior of announcing UxPlay with a fixed public key PK
+ * can be restored by uncommenting the following line */
+//#define PK "b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7"
+
+#define RAOP_TXTVERS "1"
+#define RAOP_CH "2" /* Audio channels: 2 */
+#define RAOP_CN "0,1,2,3" /* Audio codec: PCM, ALAC, AAC, AAC ELD */
+#define RAOP_ET "0,3,5" /* Encryption type: None, FairPlay, FairPlay SAPv2.5 */
+#define RAOP_VV "2"
+#define FEATURES_1 "0x5A7FFEE6" /* first 32 bits of features, with bit 27 ("supports legacy pairing") ON */
+//#define FEATURES_1 "0x527FFEE6" /* first 32 bits of features, with bit 27 ("supports legacy pairing") OFF */
+#define FEATURES_2 "0x0" /* second 32 bits of features */
+#define RAOP_RHD "5.6.0.0"
+#define RAOP_SF "0x4"
+#define RAOP_SV "false"
+#define RAOP_DA "true"
+#define RAOP_SR "44100" /* Sample rate: 44100 */
+#define RAOP_SS "16" /* Sample size: 16 */
+#define RAOP_VS GLOBAL_VERSION /* defined in global.h */
+#define RAOP_TP "UDP" /* Transport protocol. Possible values: UDP or TCP or TCP,UDP */
+#define RAOP_MD "0,1,2" /* Metadata: text, artwork, progress */
+#define RAOP_VN "65537"
+
+#define AIRPLAY_SRCVERS GLOBAL_VERSION /*defined in global.h */
+#define AIRPLAY_FLAGS "0x4"
+#define AIRPLAY_VV "2"
+#define AIRPLAY_PI "2e388006-13ba-4041-9a67-25dd4a43d536"
+
+#endif
diff --git a/lib/fairplay.h b/lib/fairplay.h
new file mode 100644
index 0000000..2b7c30f
--- /dev/null
+++ b/lib/fairplay.h
@@ -0,0 +1,28 @@
+/**
+ * Copyright (C) 2018 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef FAIRPLAY_H
+#define FAIRPLAY_H
+
+#include "logger.h"
+
+typedef struct fairplay_s fairplay_t;
+
+fairplay_t *fairplay_init(logger_t *logger);
+int fairplay_setup(fairplay_t *fp, const unsigned char req[16], unsigned char res[142]);
+int fairplay_handshake(fairplay_t *fp, const unsigned char req[164], unsigned char res[32]);
+int fairplay_decrypt(fairplay_t *fp, const unsigned char input[72], unsigned char output[16]);
+void fairplay_destroy(fairplay_t *fp);
+
+#endif
diff --git a/lib/fairplay_playfair.c b/lib/fairplay_playfair.c
new file mode 100644
index 0000000..8308c77
--- /dev/null
+++ b/lib/fairplay_playfair.c
@@ -0,0 +1,101 @@
+/**
+ * Copyright (C) 2018 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include
+#include
+#include
+
+#include "fairplay.h"
+#include "playfair/playfair.h"
+
+char reply_message[4][142] = {{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x00,0x0f,0x9f,0x3f,0x9e,0x0a,0x25,0x21,0xdb,0xdf,0x31,0x2a,0xb2,0xbf,0xb2,0x9e,0x8d,0x23,0x2b,0x63,0x76,0xa8,0xc8,0x18,0x70,0x1d,0x22,0xae,0x93,0xd8,0x27,0x37,0xfe,0xaf,0x9d,0xb4,0xfd,0xf4,0x1c,0x2d,0xba,0x9d,0x1f,0x49,0xca,0xaa,0xbf,0x65,0x91,0xac,0x1f,0x7b,0xc6,0xf7,0xe0,0x66,0x3d,0x21,0xaf,0xe0,0x15,0x65,0x95,0x3e,0xab,0x81,0xf4,0x18,0xce,0xed,0x09,0x5a,0xdb,0x7c,0x3d,0x0e,0x25,0x49,0x09,0xa7,0x98,0x31,0xd4,0x9c,0x39,0x82,0x97,0x34,0x34,0xfa,0xcb,0x42,0xc6,0x3a,0x1c,0xd9,0x11,0xa6,0xfe,0x94,0x1a,0x8a,0x6d,0x4a,0x74,0x3b,0x46,0xc3,0xa7,0x64,0x9e,0x44,0xc7,0x89,0x55,0xe4,0x9d,0x81,0x55,0x00,0x95,0x49,0xc4,0xe2,0xf7,0xa3,0xf6,0xd5,0xba},
+ {0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x01,0xcf,0x32,0xa2,0x57,0x14,0xb2,0x52,0x4f,0x8a,0xa0,0xad,0x7a,0xf1,0x64,0xe3,0x7b,0xcf,0x44,0x24,0xe2,0x00,0x04,0x7e,0xfc,0x0a,0xd6,0x7a,0xfc,0xd9,0x5d,0xed,0x1c,0x27,0x30,0xbb,0x59,0x1b,0x96,0x2e,0xd6,0x3a,0x9c,0x4d,0xed,0x88,0xba,0x8f,0xc7,0x8d,0xe6,0x4d,0x91,0xcc,0xfd,0x5c,0x7b,0x56,0xda,0x88,0xe3,0x1f,0x5c,0xce,0xaf,0xc7,0x43,0x19,0x95,0xa0,0x16,0x65,0xa5,0x4e,0x19,0x39,0xd2,0x5b,0x94,0xdb,0x64,0xb9,0xe4,0x5d,0x8d,0x06,0x3e,0x1e,0x6a,0xf0,0x7e,0x96,0x56,0x16,0x2b,0x0e,0xfa,0x40,0x42,0x75,0xea,0x5a,0x44,0xd9,0x59,0x1c,0x72,0x56,0xb9,0xfb,0xe6,0x51,0x38,0x98,0xb8,0x02,0x27,0x72,0x19,0x88,0x57,0x16,0x50,0x94,0x2a,0xd9,0x46,0x68,0x8a},
+ {0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x02,0xc1,0x69,0xa3,0x52,0xee,0xed,0x35,0xb1,0x8c,0xdd,0x9c,0x58,0xd6,0x4f,0x16,0xc1,0x51,0x9a,0x89,0xeb,0x53,0x17,0xbd,0x0d,0x43,0x36,0xcd,0x68,0xf6,0x38,0xff,0x9d,0x01,0x6a,0x5b,0x52,0xb7,0xfa,0x92,0x16,0xb2,0xb6,0x54,0x82,0xc7,0x84,0x44,0x11,0x81,0x21,0xa2,0xc7,0xfe,0xd8,0x3d,0xb7,0x11,0x9e,0x91,0x82,0xaa,0xd7,0xd1,0x8c,0x70,0x63,0xe2,0xa4,0x57,0x55,0x59,0x10,0xaf,0x9e,0x0e,0xfc,0x76,0x34,0x7d,0x16,0x40,0x43,0x80,0x7f,0x58,0x1e,0xe4,0xfb,0xe4,0x2c,0xa9,0xde,0xdc,0x1b,0x5e,0xb2,0xa3,0xaa,0x3d,0x2e,0xcd,0x59,0xe7,0xee,0xe7,0x0b,0x36,0x29,0xf2,0x2a,0xfd,0x16,0x1d,0x87,0x73,0x53,0xdd,0xb9,0x9a,0xdc,0x8e,0x07,0x00,0x6e,0x56,0xf8,0x50,0xce},
+ {0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x03,0x90,0x01,0xe1,0x72,0x7e,0x0f,0x57,0xf9,0xf5,0x88,0x0d,0xb1,0x04,0xa6,0x25,0x7a,0x23,0xf5,0xcf,0xff,0x1a,0xbb,0xe1,0xe9,0x30,0x45,0x25,0x1a,0xfb,0x97,0xeb,0x9f,0xc0,0x01,0x1e,0xbe,0x0f,0x3a,0x81,0xdf,0x5b,0x69,0x1d,0x76,0xac,0xb2,0xf7,0xa5,0xc7,0x08,0xe3,0xd3,0x28,0xf5,0x6b,0xb3,0x9d,0xbd,0xe5,0xf2,0x9c,0x8a,0x17,0xf4,0x81,0x48,0x7e,0x3a,0xe8,0x63,0xc6,0x78,0x32,0x54,0x22,0xe6,0xf7,0x8e,0x16,0x6d,0x18,0xaa,0x7f,0xd6,0x36,0x25,0x8b,0xce,0x28,0x72,0x6f,0x66,0x1f,0x73,0x88,0x93,0xce,0x44,0x31,0x1e,0x4b,0xe6,0xc0,0x53,0x51,0x93,0xe5,0xef,0x72,0xe8,0x68,0x62,0x33,0x72,0x9c,0x22,0x7d,0x82,0x0c,0x99,0x94,0x45,0xd8,0x92,0x46,0xc8,0xc3,0x59}};
+
+char fp_header[] = {0x46, 0x50, 0x4c, 0x59, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14};
+
+struct fairplay_s {
+ logger_t *logger;
+
+ unsigned char keymsg[164];
+ unsigned int keymsglen;
+};
+
+fairplay_t *
+fairplay_init(logger_t *logger)
+{
+ fairplay_t *fp;
+
+ fp = calloc(1, sizeof(fairplay_t));
+ if (!fp) {
+ return NULL;
+ }
+ fp->logger = logger;
+
+ return fp;
+}
+
+int
+fairplay_setup(fairplay_t *fp, const unsigned char req[16], unsigned char res[142])
+{
+ int mode;
+
+ assert(fp);
+
+ if (req[4] != 0x03) {
+ /* Unsupported fairplay version */
+ return -1;
+ }
+
+ mode = req[14];
+ memcpy(res, reply_message[mode], 142);
+ fp->keymsglen = 0;
+ return 0;
+}
+
+int
+fairplay_handshake(fairplay_t *fp, const unsigned char req[164], unsigned char res[32])
+{
+ assert(fp);
+
+ if (req[4] != 0x03) {
+ /* Unsupported fairplay version */
+ return -1;
+ }
+
+ memcpy(fp->keymsg, req, 164);
+ fp->keymsglen = 164;
+
+ memcpy(res, fp_header, 12);
+ memcpy(res + 12, req + 144, 20);
+ return 0;
+}
+
+int
+fairplay_decrypt(fairplay_t *fp, const unsigned char input[72], unsigned char output[16])
+{
+ if (fp->keymsglen != 164) {
+ return -1;
+ }
+
+ playfair_decrypt(fp->keymsg, (unsigned char *) input, output);
+ return 0;
+}
+
+void
+fairplay_destroy(fairplay_t *fp)
+{
+ free(fp);
+}
diff --git a/lib/fcup_request.h b/lib/fcup_request.h
new file mode 100644
index 0000000..166ffa9
--- /dev/null
+++ b/lib/fcup_request.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2022 fduncanh
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+/* this file is part of raop.c via http_handlers.h and should not be included in any other file */
+
+
+//produces the fcup request plist in xml format as a null-terminated string
+char *create_fcup_request(const char *url, int request_id, const char *client_session_id, int *datalen) {
+ char *plist_xml = NULL;
+ /* values taken from apsdk-public; */
+ /* these seem to be arbitrary choices */
+ const int sessionID = 1;
+ const int FCUP_Response_ClientInfo = 1;
+ const int FCUP_Response_ClientRef = 40030004;
+
+ /* taken from a working AppleTV? */
+ const char User_Agent[] = "AppleCoreMedia/1.0.0.11B554a (Apple TV; U; CPU OS 7_0_4 like Mac OS X; en_us";
+
+ plist_t req_root_node = plist_new_dict();
+
+ plist_t session_id_node = plist_new_uint((int64_t) sessionID);
+ plist_dict_set_item(req_root_node, "sessionID", session_id_node);
+ plist_t type_node = plist_new_string("unhandledURLRequest");
+ plist_dict_set_item(req_root_node, "type", type_node);
+
+ plist_t fcup_request_node = plist_new_dict();
+
+ plist_t client_info_node = plist_new_uint(FCUP_Response_ClientInfo);
+ plist_dict_set_item(fcup_request_node, "FCUP_Response_ClientInfo", client_info_node);
+ plist_t client_ref_node = plist_new_uint((int64_t) FCUP_Response_ClientRef);
+ plist_dict_set_item(fcup_request_node, "FCUP_Response_ClientRef", client_ref_node);
+ plist_t request_id_node = plist_new_uint((int64_t) request_id);
+ plist_dict_set_item(fcup_request_node, "FCUP_Response_RequestID", request_id_node);
+ plist_t url_node = plist_new_string(url);
+ plist_dict_set_item(fcup_request_node, "FCUP_Response_URL", url_node);
+ plist_t session_id1_node = plist_new_uint((int64_t) sessionID);
+ plist_dict_set_item(fcup_request_node, "sessionID", session_id1_node);
+
+ plist_t fcup_response_header_node = plist_new_dict();
+ plist_t playback_session_id_node = plist_new_string(client_session_id);
+ plist_dict_set_item(fcup_response_header_node, "X-Playback-Session-Id", playback_session_id_node);
+ plist_t user_agent_node = plist_new_string(User_Agent);
+ plist_dict_set_item(fcup_response_header_node, "User-Agent", user_agent_node);
+
+ plist_dict_set_item(fcup_request_node, "FCUP_Response_Headers", fcup_response_header_node);
+ plist_dict_set_item(req_root_node, "request", fcup_request_node);
+
+ uint32_t uint_val;
+
+ plist_to_xml(req_root_node, &plist_xml, &uint_val);
+ *datalen = (int) uint_val;
+ plist_free(req_root_node);
+ assert(plist_xml[*datalen] == '\0');
+ return plist_xml; //needs to be freed after use
+}
+
+int fcup_request(void *conn_opaque, const char *media_url, const char *client_session_id, int request_id) {
+
+ raop_conn_t *conn = (raop_conn_t *) conn_opaque;
+ int datalen = 0;
+ int requestlen;
+
+ int socket_fd = httpd_get_connection_socket_by_type(conn->raop->httpd, CONNECTION_TYPE_PTTH, 1);
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "fcup_request send socket = %d", socket_fd);
+
+ /* create xml plist request data */
+ char *plist_xml = create_fcup_request(media_url, request_id, client_session_id, &datalen);
+
+ /* use http_response tools for creating the reverse http request */
+ http_response_t *request = http_response_create();
+ http_response_reverse_request_init(request, "POST", "/event", "HTTP/1.1");
+ http_response_add_header(request, "X-Apple-Session-ID", client_session_id);
+ http_response_add_header(request, "Content-Type", "text/x-apple-plist+xml");
+ http_response_finish(request, plist_xml, datalen);
+
+ free(plist_xml);
+
+ const char *http_request = http_response_get_data(request, &requestlen);
+ int send_len = send(socket_fd, http_request, requestlen, 0);
+ if (send_len < 0) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(conn->raop->logger, LOGGER_ERR, "fcup_request: send error %d:%s\n",
+ sock_err, strerror(sock_err));
+ http_response_destroy(request);
+ /* shut down connection? */
+ return -1;
+ }
+
+ if (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG) {
+ char *request_str = utils_data_to_text(http_request, requestlen);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s", request_str);
+ free (request_str);
+ }
+ http_response_destroy(request);
+ logger_log(conn->raop->logger, LOGGER_DEBUG,"fcup_request: send sent Request of %d bytes from socket %d\n",
+ send_len, socket_fd);
+ return 0;
+}
diff --git a/lib/global.h b/lib/global.h
new file mode 100644
index 0000000..03f2dcb
--- /dev/null
+++ b/lib/global.h
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) 2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2021-2022
+ */
+
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#define GLOBAL_MODEL "AppleTV3,2"
+#define GLOBAL_VERSION "220.68"
+
+/* use old protocol for audio AES key if client's User-Agent string is contained in these strings */
+/* replace xxx by any new User-Agent string as needed */
+#define OLD_PROTOCOL_CLIENT_USER_AGENT_LIST "AirMyPC/2.0;xxx"
+
+#define DECRYPTION_TEST 0 /* set to 1 or 2 to examine audio decryption */
+
+#define MAX_HWADDR_LEN 6
+
+#endif
diff --git a/lib/http_handlers.h b/lib/http_handlers.h
new file mode 100644
index 0000000..a47f482
--- /dev/null
+++ b/lib/http_handlers.h
@@ -0,0 +1,997 @@
+/**
+ * Copyright (c) 2024 fduncanh
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+/* this file is part of raop.c and should not be included in any other file */
+
+#include "airplay_video.h"
+#include "fcup_request.h"
+
+static void
+http_handler_server_info(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+
+ assert(conn->raop->dnssd);
+ int hw_addr_raw_len = 0;
+ const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len);
+
+ char *hw_addr = calloc(1, 3 * hw_addr_raw_len);
+ //int hw_addr_len =
+ utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len);
+
+ plist_t r_node = plist_new_dict();
+
+ /* first 12 AirPlay features bits (R to L): 0x27F = 0010 0111 1111
+ * Only bits 0-6 and bit 9 are set:
+ * 0. video supported
+ * 1. photo supported
+ * 2. video protected wirh FairPlay DRM
+ * 3. volume control supported for video
+ * 4. HLS supported
+ * 5. slideshow supported
+ * 6. (unknown)
+ * 9. audio supported.
+ */
+ plist_t features_node = plist_new_uint(0x27F);
+ plist_dict_set_item(r_node, "features", features_node);
+
+ plist_t mac_address_node = plist_new_string(hw_addr);
+ plist_dict_set_item(r_node, "macAddress", mac_address_node);
+
+ plist_t model_node = plist_new_string(GLOBAL_MODEL);
+ plist_dict_set_item(r_node, "model", model_node);
+
+ plist_t os_build_node = plist_new_string("12B435");
+ plist_dict_set_item(r_node, "osBuildVersion", os_build_node);
+
+ plist_t protovers_node = plist_new_string("1.0");
+ plist_dict_set_item(r_node, "protovers", protovers_node);
+
+ plist_t source_version_node = plist_new_string(GLOBAL_VERSION);
+ plist_dict_set_item(r_node, "srcvers", source_version_node);
+
+ plist_t vv_node = plist_new_uint(strtol(AIRPLAY_VV, NULL, 10));
+ plist_dict_set_item(r_node, "vv", vv_node);
+
+ plist_t device_id_node = plist_new_string(hw_addr);
+ plist_dict_set_item(r_node, "deviceid", device_id_node);
+
+ plist_to_xml(r_node, response_data, (uint32_t *) response_datalen);
+
+ //assert(*response_datalen == strlen(*response_data));
+
+ /* last character (at *response_data[response_datalen - 1]) is 0x0a = '\n'
+ * (*response_data[response_datalen] is '\0').
+ * apsdk removes the last "\n" by overwriting it with '\0', and reducing response_datalen by 1.
+ * TODO: check if this is necessary */
+
+ plist_free(r_node);
+ http_response_add_header(response, "Content-Type", "text/x-apple-plist+xml");
+ free(hw_addr);
+
+ /* initialize the airplay video service */
+ const char *session_id = http_request_get_header(request, "X-Apple-Session-ID");
+
+ airplay_video_service_init(conn->raop, conn->raop->port, session_id);
+
+}
+
+static void
+http_handler_scrub(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+ const char *url = http_request_get_url(request);
+ const char *data = strstr(url, "?");
+ float scrub_position = 0.0f;
+ if (data) {
+ data++;
+ const char *position = strstr(data, "=") + 1;
+ char *end;
+ double value = strtod(position, &end);
+ if (end && end != position) {
+ scrub_position = (float) value;
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_scrub: got position = %.6f",
+ scrub_position);
+ }
+ }
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "**********************SCRUB %f ***********************",scrub_position);
+ conn->raop->callbacks.on_video_scrub(conn->raop->callbacks.cls, scrub_position);
+}
+
+static void
+http_handler_rate(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+
+ const char *url = http_request_get_url(request);
+ const char *data = strstr(url, "?");
+ float rate_value = 0.0f;
+ if (data) {
+ data++;
+ const char *rate = strstr(data, "=") + 1;
+ char *end;
+ float value = strtof(rate, &end);
+ if (end && end != rate) {
+ rate_value = value;
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_rate: got rate = %.6f", rate_value);
+ }
+ }
+ conn->raop->callbacks.on_video_rate(conn->raop->callbacks.cls, rate_value);
+}
+
+static void
+http_handler_stop(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "client HTTP request POST stop");
+
+ conn->raop->callbacks.on_video_stop(conn->raop->callbacks.cls);
+}
+
+/* handles PUT /setProperty http requests from Client to Server */
+
+static void
+http_handler_set_property(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+
+ const char *url = http_request_get_url(request);
+ const char *property = url + strlen("/setProperty?");
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_set_property: %s", property);
+
+ /* actionAtItemEnd: values:
+ 0: advance (advance to next item, if there is one)
+ 1: pause (pause playing)
+ 2: none (do nothing)
+
+ reverseEndTime (only used when rate < 0) time at which reverse playback ends
+ forwardEndTime (only used when rate > 0) time at which reverse playback ends
+ */
+
+ if (!strcmp(property, "reverseEndTime") ||
+ !strcmp(property, "forwardEndTime") ||
+ !strcmp(property, "actionAtItemEnd")) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "property %s is known but unhandled", property);
+
+ plist_t errResponse = plist_new_dict();
+ plist_t errCode = plist_new_uint(0);
+ plist_dict_set_item(errResponse, "errorCode", errCode);
+ plist_to_xml(errResponse, response_data, (uint32_t *) response_datalen);
+ plist_free(errResponse);
+ http_response_add_header(response, "Content-Type", "text/x-apple-plist+xml");
+ } else {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "property %s is unknown, unhandled", property);
+ http_response_add_header(response, "Content-Length", "0");
+ }
+}
+
+/* handles GET /getProperty http requests from Client to Server. (not implemented) */
+
+static void
+http_handler_get_property(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+ const char *url = http_request_get_url(request);
+ const char *property = url + strlen("getProperty?");
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_get_property: %s (unhandled)", property);
+}
+
+/* this request (for a variant FairPlay decryption) cannot be handled by UxPlay */
+static void
+http_handler_fpsetup2(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+ logger_log(conn->raop->logger, LOGGER_WARNING, "client HTTP request POST fp-setup2 is unhandled");
+ http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
+ int req_datalen;
+ const unsigned char *req_data = (unsigned char *) http_request_get_data(request, &req_datalen);
+ logger_log(conn->raop->logger, LOGGER_ERR, "only FairPlay version 0x03 is implemented, version is 0x%2.2x",
+ req_data[4]);
+ http_response_init(response, "HTTP/1.1", 421, "Misdirected Request");
+}
+
+// called by http_handler_playback_info while preparing response to a GET /playback_info request from the client.
+
+typedef struct time_range_s {
+ double start;
+ double duration;
+} time_range_t;
+
+void time_range_to_plist(void *time_ranges, const int n_time_ranges,
+ plist_t time_ranges_node) {
+ time_range_t *tr = (time_range_t *) time_ranges;
+ for (int i = 0 ; i < n_time_ranges; i++) {
+ plist_t time_range_node = plist_new_dict();
+ plist_t duration_node = plist_new_real(tr[i].duration);
+ plist_dict_set_item(time_range_node, "duration", duration_node);
+ plist_t start_node = plist_new_real(tr[i].start);
+ plist_dict_set_item(time_range_node, "start", start_node);
+ plist_array_append_item(time_ranges_node, time_range_node);
+ }
+}
+
+// called by http_handler_playback_info while preparing response to a GET /playback_info request from the client.
+
+int create_playback_info_plist_xml(playback_info_t *playback_info, char **plist_xml) {
+
+ plist_t res_root_node = plist_new_dict();
+
+ plist_t duration_node = plist_new_real(playback_info->duration);
+ plist_dict_set_item(res_root_node, "duration", duration_node);
+
+ plist_t position_node = plist_new_real(playback_info->position);
+ plist_dict_set_item(res_root_node, "position", position_node);
+
+ plist_t rate_node = plist_new_real(playback_info->rate);
+ plist_dict_set_item(res_root_node, "rate", rate_node);
+
+ /* should these be int or bool? */
+ plist_t ready_to_play_node = plist_new_uint(playback_info->ready_to_play);
+ plist_dict_set_item(res_root_node, "readyToPlay", ready_to_play_node);
+
+ plist_t playback_buffer_empty_node = plist_new_uint(playback_info->playback_buffer_empty);
+ plist_dict_set_item(res_root_node, "playbackBufferEmpty", playback_buffer_empty_node);
+
+ plist_t playback_buffer_full_node = plist_new_uint(playback_info->playback_buffer_full);
+ plist_dict_set_item(res_root_node, "playbackBufferFull", playback_buffer_full_node);
+
+ plist_t playback_likely_to_keep_up_node = plist_new_uint(playback_info->playback_likely_to_keep_up);
+ plist_dict_set_item(res_root_node, "playbackLikelyToKeepUp", playback_likely_to_keep_up_node);
+
+ plist_t loaded_time_ranges_node = plist_new_array();
+ time_range_to_plist(playback_info->loadedTimeRanges, playback_info->num_loaded_time_ranges,
+ loaded_time_ranges_node);
+ plist_dict_set_item(res_root_node, "loadedTimeRanges", loaded_time_ranges_node);
+
+ plist_t seekable_time_ranges_node = plist_new_array();
+ time_range_to_plist(playback_info->seekableTimeRanges, playback_info->num_seekable_time_ranges,
+ seekable_time_ranges_node);
+ plist_dict_set_item(res_root_node, "seekableTimeRanges", seekable_time_ranges_node);
+
+ int len;
+ plist_to_xml(res_root_node, plist_xml, (uint32_t *) &len);
+ /* plist_xml is null-terminated, last character is '/n' */
+
+ plist_free(res_root_node);
+
+ return len;
+}
+
+
+/* this handles requests from the Client for "Playback information" while the Media is playing on the
+ Media Player. (The Server gets this information by monitoring the Media Player). The Client could use
+ the information to e.g. update the slider it shows with progress to the player (0%-100%).
+ It does not affect playing of the Media*/
+
+static void
+http_handler_playback_info(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_playback_info");
+ //const char *session_id = http_request_get_header(request, "X-Apple-Session-ID");
+ playback_info_t playback_info;
+
+ playback_info.stallcount = 0;
+ playback_info.ready_to_play = true; // ???;
+ playback_info.playback_buffer_empty = false; // maybe need to get this from playbin
+ playback_info.playback_buffer_full = true;
+ playback_info.playback_likely_to_keep_up = true;
+
+ conn->raop->callbacks.on_video_acquire_playback_info(conn->raop->callbacks.cls, &playback_info);
+ if (playback_info.duration == -1.0) {
+ /* video has finished, reset */
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "playback_info not available (finishing)");
+ //httpd_remove_known_connections(conn->raop->httpd);
+ http_response_set_disconnect(response,1);
+ conn->raop->callbacks.video_reset(conn->raop->callbacks.cls);
+ return;
+ } else if (playback_info.position == -1.0) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "playback_info not available");
+ return;
+ }
+
+ playback_info.num_loaded_time_ranges = 1;
+ time_range_t time_ranges_loaded[1];
+ time_ranges_loaded[0].start = playback_info.position;
+ time_ranges_loaded[0].duration = playback_info.duration - playback_info.position;
+ playback_info.loadedTimeRanges = (void *) &time_ranges_loaded;
+
+ playback_info.num_seekable_time_ranges = 1;
+ time_range_t time_ranges_seekable[1];
+ time_ranges_seekable[0].start = 0.0;
+ time_ranges_seekable[0].duration = playback_info.position;
+ playback_info.seekableTimeRanges = (void *) &time_ranges_seekable;
+
+ *response_datalen = create_playback_info_plist_xml(&playback_info, response_data);
+ http_response_add_header(response, "Content-Type", "text/x-apple-plist+xml");
+}
+
+/* this handles the POST /reverse request from Client to Server on a AirPlay http channel to "Upgrade"
+ to "PTTH/1.0" Reverse HTTP protocol proposed in 2009 Internet-Draft
+
+ https://datatracker.ietf.org/doc/id/draft-lentczner-rhttp-00.txt .
+
+ After the Upgrade the channel becomes a reverse http "AirPlay (reversed)" channel for
+ http requests from Server to Client.
+ */
+
+static void
+http_handler_reverse(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+
+ /* get http socket for send */
+ int socket_fd = httpd_get_connection_socket (conn->raop->httpd, (void *) conn);
+ if (socket_fd < 0) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "fcup_request failed to retrieve socket_fd from httpd");
+ /* shut down connection? */
+ }
+
+ const char *purpose = http_request_get_header(request, "X-Apple-Purpose");
+ const char *connection = http_request_get_header(request, "Connection");
+ const char *upgrade = http_request_get_header(request, "Upgrade");
+ logger_log(conn->raop->logger, LOGGER_INFO, "client requested reverse connection: %s; purpose: %s \"%s\"",
+ connection, upgrade, purpose);
+
+ httpd_set_connection_type(conn->raop->httpd, (void *) conn, CONNECTION_TYPE_PTTH);
+ int type_PTTH = httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_PTTH);
+
+ if (type_PTTH == 1) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "will use socket %d for %s connections", socket_fd, purpose);
+ http_response_init(response, "HTTP/1.1", 101, "Switching Protocols");
+ http_response_add_header(response, "Connection", "Upgrade");
+ http_response_add_header(response, "Upgrade", "PTTH/1.0");
+
+ } else {
+ logger_log(conn->raop->logger, LOGGER_ERR, "multiple TPPH connections (%d) are forbidden", type_PTTH );
+ }
+}
+
+/* this copies a Media Playlist into a null-terminated string. If it has the "#YT-EXT-CONDENSED-URI"
+ header, it is also expanded into the full Media Playlist format */
+
+char *adjust_yt_condensed_playlist(const char *media_playlist) {
+ /* expands a YT-EXT_CONDENSED-URL media playlist into a full media playlist
+ * returns a pointer to the expanded playlist, WHICH MUST BE FREED AFTER USE */
+
+ const char *base_uri_begin;
+ const char *params_begin;
+ const char *prefix_begin;
+ size_t base_uri_len;
+ size_t params_len;
+ size_t prefix_len;
+ const char* ptr = strstr(media_playlist, "#EXTM3U\n");
+
+ ptr += strlen("#EXTM3U\n");
+ assert(ptr);
+ if (strncmp(ptr, "#YT-EXT-CONDENSED-URL", strlen("#YT-EXT-CONDENSED-URL"))) {
+ size_t len = strlen(media_playlist);
+ char * playlist_copy = (char *) malloc(len + 1);
+ memcpy(playlist_copy, media_playlist, len);
+ playlist_copy[len] = '\0';
+ return playlist_copy;
+ }
+ ptr = strstr(ptr, "BASE-URI=");
+ base_uri_begin = strchr(ptr, '"');
+ base_uri_begin++;
+ ptr = strchr(base_uri_begin, '"');
+ base_uri_len = ptr - base_uri_begin;
+ char *base_uri = (char *) calloc(base_uri_len + 1, sizeof(char));
+ assert(base_uri);
+ memcpy(base_uri, base_uri_begin, base_uri_len); //must free
+
+ ptr = strstr(ptr, "PARAMS=");
+ params_begin = strchr(ptr, '"');
+ params_begin++;
+ ptr = strchr(params_begin,'"');
+ params_len = ptr - params_begin;
+ char *params = (char *) calloc(params_len + 1, sizeof(char));
+ assert(params);
+ memcpy(params, params_begin, params_len); //must free
+
+ ptr = strstr(ptr, "PREFIX=");
+ prefix_begin = strchr(ptr, '"');
+ prefix_begin++;
+ ptr = strchr(prefix_begin,'"');
+ prefix_len = ptr - prefix_begin;
+ char *prefix = (char *) calloc(prefix_len + 1, sizeof(char));
+ assert(prefix);
+ memcpy(prefix, prefix_begin, prefix_len); //must free
+
+ /* expand params */
+ int nparams = 0;
+ int *params_size = NULL;
+ const char **params_start = NULL;
+ if (strlen(params)) {
+ nparams = 1;
+ char * comma = strchr(params, ',');
+ while (comma) {
+ nparams++;
+ comma++;
+ comma = strchr(comma, ',');
+ }
+ params_start = (const char **) calloc(nparams, sizeof(char *)); //must free
+ params_size = (int *) calloc(nparams, sizeof(int)); //must free
+ ptr = params;
+ for (int i = 0; i < nparams; i++) {
+ comma = strchr(ptr, ',');
+ params_start[i] = ptr;
+ if (comma) {
+ params_size[i] = (int) (comma - ptr);
+ ptr = comma;
+ ptr++;
+ } else {
+ params_size[i] = (int) (params + params_len - ptr);
+ break;
+ }
+ }
+ }
+
+ int count = 0;
+ ptr = strstr(media_playlist, "#EXTINF");
+ while (ptr) {
+ count++;
+ ptr = strstr(++ptr, "#EXTINF");
+ }
+
+ size_t old_size = strlen(media_playlist);
+ size_t new_size = old_size;
+ new_size += count * (base_uri_len + params_len);
+
+ char * new_playlist = (char *) calloc( new_size + 100, sizeof(char));
+ const char *old_pos = media_playlist;
+ char *new_pos = new_playlist;
+ ptr = old_pos;
+ ptr = strstr(old_pos, "#EXTINF:");
+ size_t len = ptr - old_pos;
+ /* copy header section before chunks */
+ memcpy(new_pos, old_pos, len);
+ old_pos += len;
+ new_pos += len;
+ int counter = 0;
+ while (ptr) {
+ counter++;
+ /* for each chunk */
+ const char *end = NULL;
+ char *start = strstr(ptr, prefix);
+ len = start - ptr;
+ /* copy first line of chunk entry */
+ memcpy(new_pos, old_pos, len);
+ old_pos += len;
+ new_pos += len;
+
+ /* copy base uri to replace prefix*/
+ memcpy(new_pos, base_uri, base_uri_len);
+ new_pos += base_uri_len;
+ old_pos += prefix_len;
+ ptr = strstr(old_pos, "#EXTINF:");
+
+ /* insert the PARAMS separators on the slices line */
+ end = old_pos;
+ int last = nparams - 1;
+ for (int i = 0; i < nparams; i++) {
+ if (i != last) {
+ end = strchr(end, '/');
+ } else {
+ end = strstr(end, "#EXT"); /* the next line starts with either #EXTINF (usually) or #EXT-X-ENDLIST (at last chunk)*/
+ }
+ *new_pos = '/';
+ new_pos++;
+ memcpy(new_pos, params_start[i], params_size[i]);
+ new_pos += params_size[i];
+ *new_pos = '/';
+ new_pos++;
+
+ len = end - old_pos;
+ end++;
+
+ memcpy (new_pos, old_pos, len);
+ new_pos += len;
+ old_pos += len;
+ if (i != last) {
+ old_pos++; /* last entry is not followed by "/" separator */
+ }
+ }
+ }
+ /* copy tail */
+
+ len = media_playlist + strlen(media_playlist) - old_pos;
+ memcpy(new_pos, old_pos, len);
+ new_pos += len;
+ old_pos += len;
+
+ new_playlist[new_size] = '\0';
+
+ free (prefix);
+ free (base_uri);
+ free (params);
+ if (params_size) {
+ free (params_size);
+ }
+ if (params_start) {
+ free (params_start);
+ }
+
+ return new_playlist;
+}
+
+/* this adjusts the uri prefixes in the Master Playlist, for sending to the Media Player running on the Server Host */
+
+char *adjust_master_playlist (char *fcup_response_data, int fcup_response_datalen, char *uri_prefix, char *uri_local_prefix) {
+
+ size_t uri_prefix_len = strlen(uri_prefix);
+ size_t uri_local_prefix_len = strlen(uri_local_prefix);
+ int counter = 0;
+ char *ptr = strstr(fcup_response_data, uri_prefix);
+ while (ptr != NULL) {
+ counter++;
+ ptr++;
+ ptr = strstr(ptr, uri_prefix);
+ }
+
+ size_t len = uri_local_prefix_len - uri_prefix_len;
+ len *= counter;
+ len += fcup_response_datalen;
+ char *new_master = (char *) malloc(len + 1);
+ *(new_master + len) = '\0';
+ char *first = fcup_response_data;
+ char *new = new_master;
+ char *last = strstr(first, uri_prefix);
+ counter = 0;
+ while (last != NULL) {
+ counter++;
+ len = last - first;
+ memcpy(new, first, len);
+ first = last + uri_prefix_len;
+ new += len;
+ memcpy(new, uri_local_prefix, uri_local_prefix_len);
+ new += uri_local_prefix_len;
+ last = strstr(last + uri_prefix_len, uri_prefix);
+ if (last == NULL) {
+ len = fcup_response_data + fcup_response_datalen - first;
+ memcpy(new, first, len);
+ break;
+ }
+ }
+ return new_master;
+}
+
+/* this parses the Master Playlist to make a table of the Media Playlist uri's that it lists */
+
+int create_media_uri_table(const char *url_prefix, const char *master_playlist_data, int datalen,
+ char ***media_uri_table, int *num_uri) {
+ char *ptr = strstr(master_playlist_data, url_prefix);
+ char ** table = NULL;
+ if (ptr == NULL) {
+ return -1;
+ }
+ int count = 0;
+ while (ptr != NULL) {
+ char *end = strstr(ptr, "m3u8");
+ if (end == NULL) {
+ return 1;
+ }
+ end += sizeof("m3u8");
+ count++;
+ ptr = strstr(end, url_prefix);
+ }
+ table = (char **) calloc(count, sizeof(char *));
+ if (!table) {
+ return -1;
+ }
+ for (int i = 0; i < count; i++) {
+ table[i] = NULL;
+ }
+ ptr = strstr(master_playlist_data, url_prefix);
+ count = 0;
+ while (ptr != NULL) {
+ char *end = strstr(ptr, "m3u8");
+ char *uri;
+ if (end == NULL) {
+ return 0;
+ }
+ end += sizeof("m3u8");
+ size_t len = end - ptr - 1;
+ uri = (char *) calloc(len + 1, sizeof(char));
+ memcpy(uri , ptr, len);
+ table[count] = uri;
+ uri = NULL;
+ count ++;
+ ptr = strstr(end, url_prefix);
+ }
+ *num_uri = count;
+
+ *media_uri_table = table;
+ return 0;
+}
+
+/* the POST /action request from Client to Server on the AirPlay http channel follows a POST /event "FCUP Request"
+ from Server to Client on the reverse http channel, for a HLS playlist (first the Master Playlist, then the Media Playlists
+ listed in the Master Playlist. The POST /action request contains the playlist requested by the Server in
+ the preceding "FCUP Request". The FCUP Request sequence continues until all Media Playlists have been obtained by the Server */
+
+static void
+http_handler_action(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+
+ bool data_is_plist = false;
+ plist_t req_root_node = NULL;
+ uint64_t uint_val;
+ int request_id = 0;
+ int fcup_response_statuscode = 0;
+ bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
+
+
+ const char* session_id = http_request_get_header(request, "X-Apple-Session-ID");
+ if (!session_id) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Play request had no X-Apple-Session-ID");
+ goto post_action_error;
+ }
+ const char *apple_session_id = get_apple_session_id(conn->raop->airplay_video);
+ if (strcmp(session_id, apple_session_id)){
+ logger_log(conn->raop->logger, LOGGER_ERR, "X-Apple-Session-ID has changed:\n was:\"%s\"\n now:\"%s\"",
+ apple_session_id, session_id);
+ goto post_action_error;
+ }
+
+ /* verify that this request contains a binary plist*/
+ char *header_str = NULL;
+ http_request_get_header_string(request, &header_str);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "request header: %s", header_str);
+ data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
+ free(header_str);
+ if (!data_is_plist) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "POST /action: did not receive expected plist from client");
+ goto post_action_error;
+ }
+
+ /* extract the root_node plist */
+ int request_datalen = 0;
+ const char *request_data = http_request_get_data(request, &request_datalen);
+ if (request_datalen == 0) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "POST /action: did not receive expected plist from client");
+ goto post_action_error;
+ }
+ plist_from_bin(request_data, request_datalen, &req_root_node);
+
+ /* determine type of data */
+ plist_t req_type_node = plist_dict_get_item(req_root_node, "type");
+ if (!PLIST_IS_STRING(req_type_node)) {
+ goto post_action_error;
+ }
+
+ /* three possible types are known */
+ char *type = NULL;
+ int action_type = 0;
+ plist_get_string_val(req_type_node, &type);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "action type is %s", type);
+ if (strstr(type, "unhandledURLResponse")) {
+ action_type = 1;
+ } else if (strstr(type, "playlistInsert")) {
+ action_type = 2;
+ } else if (strstr(type, "playlistRemove")) {
+ action_type = 3;
+ }
+ free (type);
+
+ plist_t req_params_node = NULL;
+ switch (action_type) {
+ case 1:
+ goto unhandledURLResponse;
+ case 2:
+ logger_log(conn->raop->logger, LOGGER_INFO, "unhandled action type playlistInsert (add new playback)");
+ goto finish;
+ case 3:
+ logger_log(conn->raop->logger, LOGGER_INFO, "unhandled action type playlistRemove (stop playback)");
+ goto finish;
+ default:
+ logger_log(conn->raop->logger, LOGGER_INFO, "unknown action type (unhandled)");
+ goto finish;
+ }
+
+ unhandledURLResponse:;
+
+ req_params_node = plist_dict_get_item(req_root_node, "params");
+ if (!PLIST_IS_DICT (req_params_node)) {
+ goto post_action_error;
+ }
+
+ /* handling type "unhandledURLResponse" (case 1)*/
+ uint_val = 0;
+ int fcup_response_datalen = 0;
+
+ if (logger_debug) {
+ plist_t plist_fcup_response_statuscode_node = plist_dict_get_item(req_params_node,
+ "FCUP_Response_StatusCode");
+ if (plist_fcup_response_statuscode_node) {
+ plist_get_uint_val(plist_fcup_response_statuscode_node, &uint_val);
+ fcup_response_statuscode = (int) uint_val;
+ uint_val = 0;
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response_StatusCode = %d",
+ fcup_response_statuscode);
+ }
+
+ plist_t plist_fcup_response_requestid_node = plist_dict_get_item(req_params_node,
+ "FCUP_Response_RequestID");
+ if (plist_fcup_response_requestid_node) {
+ plist_get_uint_val(plist_fcup_response_requestid_node, &uint_val);
+ request_id = (int) uint_val;
+ uint_val = 0;
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response_RequestID = %d", request_id);
+ }
+ }
+
+ plist_t plist_fcup_response_url_node = plist_dict_get_item(req_params_node, "FCUP_Response_URL");
+ if (!PLIST_IS_STRING(plist_fcup_response_url_node)) {
+ goto post_action_error;
+ }
+ char *fcup_response_url = NULL;
+ plist_get_string_val(plist_fcup_response_url_node, &fcup_response_url);
+ if (!fcup_response_url) {
+ goto post_action_error;
+ }
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response_URL = %s", fcup_response_url);
+
+ plist_t plist_fcup_response_data_node = plist_dict_get_item(req_params_node, "FCUP_Response_Data");
+ if (!PLIST_IS_DATA(plist_fcup_response_data_node)){
+ goto post_action_error;
+ }
+
+ uint_val = 0;
+ char *fcup_response_data = NULL;
+ plist_get_data_val(plist_fcup_response_data_node, &fcup_response_data, &uint_val);
+ fcup_response_datalen = (int) uint_val;
+
+ if (!fcup_response_data) {
+ free (fcup_response_url);
+ goto post_action_error;
+ }
+
+ if (logger_debug) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response datalen = %d", fcup_response_datalen);
+ char *data = malloc(fcup_response_datalen + 1);
+ memcpy(data, fcup_response_data, fcup_response_datalen);
+ data[fcup_response_datalen] = '\0';
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "begin FCUP Response data:\n%s\nend FCUP Response data",data);
+ free (data);
+ }
+
+
+ char *ptr = strstr(fcup_response_url, "/master.m3u8");
+ if (ptr) {
+ /* this is a master playlist */
+ char *uri_prefix = get_uri_prefix(conn->raop->airplay_video);
+ char ** media_data_store = NULL;
+ int num_uri = 0;
+
+ char *uri_local_prefix = get_uri_local_prefix(conn->raop->airplay_video);
+ char *new_master = adjust_master_playlist (fcup_response_data, fcup_response_datalen, uri_prefix, uri_local_prefix);
+ store_master_playlist(conn->raop->airplay_video, new_master);
+ create_media_uri_table(uri_prefix, fcup_response_data, fcup_response_datalen, &media_data_store, &num_uri);
+ create_media_data_store(conn->raop->airplay_video, media_data_store, num_uri);
+ num_uri = get_num_media_uri(conn->raop->airplay_video);
+ set_next_media_uri_id(conn->raop->airplay_video, 0);
+ } else {
+ /* this is a media playlist */
+ assert(fcup_response_data);
+ char *playlist = (char *) calloc(fcup_response_datalen + 1, sizeof(char));
+ memcpy(playlist, fcup_response_data, fcup_response_datalen);
+ int uri_num = get_next_media_uri_id(conn->raop->airplay_video);
+ --uri_num; // (next num is current num + 1)
+ store_media_data_playlist_by_num(conn->raop->airplay_video, playlist, uri_num);
+ float duration = 0.0f;
+ int count = analyze_media_playlist(playlist, &duration);
+ if (count) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG,
+ "\n%s:\nreceived media playlist has %5d chunks, total duration %9.3f secs\n",
+ fcup_response_url, count, duration);
+ }
+ }
+
+ if (fcup_response_data) {
+ free (fcup_response_data);
+ }
+ if (fcup_response_url) {
+ free (fcup_response_url);
+ }
+
+ int num_uri = get_num_media_uri(conn->raop->airplay_video);
+ int uri_num = get_next_media_uri_id(conn->raop->airplay_video);
+ if (uri_num < num_uri) {
+ fcup_request((void *) conn, get_media_uri_by_num(conn->raop->airplay_video, uri_num),
+ apple_session_id,
+ get_next_FCUP_RequestID(conn->raop->airplay_video));
+ set_next_media_uri_id(conn->raop->airplay_video, ++uri_num);
+ } else {
+ char * uri_local_prefix = get_uri_local_prefix(conn->raop->airplay_video);
+ conn->raop->callbacks.on_video_play(conn->raop->callbacks.cls,
+ strcat(uri_local_prefix, "/master.m3u8"),
+ get_start_position_seconds(conn->raop->airplay_video));
+ }
+
+ finish:
+ plist_free(req_root_node);
+ return;
+
+ post_action_error:;
+ http_response_init(response, "HTTP/1.1", 400, "Bad Request");
+
+ if (req_root_node) {
+ plist_free(req_root_node);
+ }
+
+}
+
+/* The POST /play request from the Client to Server on the AirPlay http channel contains (among other information)
+ the "Content Location" that specifies the HLS Playlists for the video to be streamed, as well as the video
+ "start position in seconds". Once this request is received by the Sever, the Server sends a POST /event
+ "FCUP Request" request to the Client on the reverse http channel, to request the HLS Master Playlist */
+
+static void
+http_handler_play(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+
+ char* playback_location = NULL;
+ plist_t req_root_node = NULL;
+ float start_position_seconds = 0.0f;
+ bool data_is_binary_plist = false;
+ bool data_is_text = false;
+ bool data_is_octet = false;
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_play");
+
+ const char* session_id = http_request_get_header(request, "X-Apple-Session-ID");
+ if (!session_id) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Play request had no X-Apple-Session-ID");
+ goto play_error;
+ }
+ const char *apple_session_id = get_apple_session_id(conn->raop->airplay_video);
+ if (strcmp(session_id, apple_session_id)){
+ logger_log(conn->raop->logger, LOGGER_ERR, "X-Apple-Session-ID has changed:\n was:\"%s\"\n now:\"%s\"",
+ apple_session_id, session_id);
+ goto play_error;
+ }
+
+ int request_datalen = -1;
+ const char *request_data = http_request_get_data(request, &request_datalen);
+
+ if (request_datalen > 0) {
+ char *header_str = NULL;
+ http_request_get_header_string(request, &header_str);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "request header:\n%s", header_str);
+ data_is_binary_plist = (strstr(header_str, "x-apple-binary-plist") != NULL);
+ data_is_text = (strstr(header_str, "text/parameters") != NULL);
+ data_is_octet = (strstr(header_str, "octet-stream") != NULL);
+ free (header_str);
+ }
+ if (!data_is_text && !data_is_octet && !data_is_binary_plist) {
+ goto play_error;
+ }
+
+ if (data_is_text) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Play request Content is text (unsupported)");
+ goto play_error;
+ }
+
+ if (data_is_octet) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Play request Content is octet-stream (unsupported)");
+ goto play_error;
+ }
+
+ if (data_is_binary_plist) {
+ plist_from_bin(request_data, request_datalen, &req_root_node);
+
+ plist_t req_uuid_node = plist_dict_get_item(req_root_node, "uuid");
+ if (!req_uuid_node) {
+ goto play_error;
+ } else {
+ char* playback_uuid = NULL;
+ plist_get_string_val(req_uuid_node, &playback_uuid);
+ set_playback_uuid(conn->raop->airplay_video, playback_uuid);
+ free (playback_uuid);
+ }
+
+ plist_t req_content_location_node = plist_dict_get_item(req_root_node, "Content-Location");
+ if (!req_content_location_node) {
+ goto play_error;
+ } else {
+ plist_get_string_val(req_content_location_node, &playback_location);
+ }
+
+ plist_t req_start_position_seconds_node = plist_dict_get_item(req_root_node, "Start-Position-Seconds");
+ if (!req_start_position_seconds_node) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "No Start-Position-Seconds in Play request");
+ } else {
+ double start_position = 0.0;
+ plist_get_real_val(req_start_position_seconds_node, &start_position);
+ start_position_seconds = (float) start_position;
+ }
+ set_start_position_seconds(conn->raop->airplay_video, (float) start_position_seconds);
+ }
+
+ char *ptr = strstr(playback_location, "/master.m3u8");
+ int prefix_len = (int) (ptr - playback_location);
+ set_uri_prefix(conn->raop->airplay_video, playback_location, prefix_len);
+ set_next_media_uri_id(conn->raop->airplay_video, 0);
+ fcup_request((void *) conn, playback_location, apple_session_id, get_next_FCUP_RequestID(conn->raop->airplay_video));
+
+ if (playback_location) {
+ free (playback_location);
+ }
+
+ if (req_root_node) {
+ plist_free(req_root_node);
+ }
+ return;
+
+ play_error:;
+ if (req_root_node) {
+ plist_free(req_root_node);
+ }
+ logger_log(conn->raop->logger, LOGGER_ERR, "Could not find valid Plist Data for /play, Unhandled");
+ http_response_init(response, "HTTP/1.1", 400, "Bad Request");
+}
+
+/* the HLS handler handles http requests GET /[uri] on the HLS channel from the media player to the Server, asking for
+ (adjusted) copies of Playlists: first the Master Playlist (adjusted to change the uri prefix to
+ "http://localhost:[port]/.......m3u8"), then the Media Playlists that the media player wishes to use.
+ If the client supplied Media playlists with the "YT-EXT-CONDENSED-URI" header, these must be adjusted into
+ the standard uncondensed form before sending with the response. The uri in the request is the uri for the
+ Media Playlist, taken from the Master Playlist, with the uri prefix removed.
+*/
+
+static void
+http_handler_hls(raop_conn_t *conn, http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+ const char *method = http_request_get_method(request);
+ assert (!strcmp(method, "GET"));
+ const char *url = http_request_get_url(request);
+ const char* upgrade = http_request_get_header(request, "Upgrade");
+ if (upgrade) {
+ //don't accept Upgrade: h2c request ?
+ return;
+ }
+
+ if (!strcmp(url, "/master.m3u8")){
+ char * master_playlist = get_master_playlist(conn->raop->airplay_video);
+ size_t len = strlen(master_playlist);
+ char * data = (char *) malloc(len + 1);
+ memcpy(data, master_playlist, len);
+ data[len] = '\0';
+ *response_data = data;
+ *response_datalen = (int ) len;
+ } else {
+ int num = get_media_playlist_by_uri(conn->raop->airplay_video, url);
+ if (num < 0) {
+ logger_log(conn->raop->logger, LOGGER_ERR,"Requested playlist %s not found", url);
+ assert(0);
+ } else {
+ char *media_playlist = get_media_playlist_by_num(conn->raop->airplay_video, num);
+ assert(media_playlist);
+ char *data = adjust_yt_condensed_playlist(media_playlist);
+ *response_data = data;
+ *response_datalen = strlen(data);
+ float duration = 0.0f;
+ int chunks = analyze_media_playlist(data, &duration);
+ logger_log(conn->raop->logger, LOGGER_INFO,
+ "Requested media_playlist %s has %5d chunks, total duration %9.3f secs", url, chunks, duration);
+ }
+ }
+
+ http_response_add_header(response, "Access-Control-Allow-Headers", "Content-type");
+ http_response_add_header(response, "Access-Control-Allow-Origin", "*");
+ const char *date;
+ date = gmt_time_string();
+ http_response_add_header(response, "Date", date);
+ if (*response_datalen > 0) {
+ http_response_add_header(response, "Content-Type", "application/x-mpegURL; charset=utf-8");
+ } else if (*response_datalen == 0) {
+ http_response_init(response, "HTTP/1.1", 404, "Not Found");
+ }
+}
diff --git a/lib/http_request.c b/lib/http_request.c
new file mode 100644
index 0000000..76bece6
--- /dev/null
+++ b/lib/http_request.c
@@ -0,0 +1,344 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2021
+ */
+
+#include
+#include
+#include
+#include
+
+#include "http_request.h"
+#include "llhttp/llhttp.h"
+
+struct http_request_s {
+ llhttp_t parser;
+ llhttp_settings_t parser_settings;
+
+ bool is_reverse; // if true, this is a reverse-response from client
+ const char *method;
+ char *url;
+ char protocol[9];
+
+ char **headers;
+ int headers_size;
+ int headers_index;
+
+ char *data;
+ int datalen;
+
+ int complete;
+};
+
+static int
+on_url(llhttp_t *parser, const char *at, size_t length)
+{
+ http_request_t *request = parser->data;
+ int urllen = request->url ? strlen(request->url) : 0;
+
+ request->url = realloc(request->url, urllen+length+1);
+ assert(request->url);
+
+ request->url[urllen] = '\0';
+ strncat(request->url, at, length);
+
+ strncpy(request->protocol, at + length + 1, 8);
+
+ return 0;
+}
+
+static int
+on_header_field(llhttp_t *parser, const char *at, size_t length)
+{
+ http_request_t *request = parser->data;
+
+ /* Check if our index is a value */
+ if (request->headers_index%2 == 1) {
+ request->headers_index++;
+ }
+
+ /* Allocate space for new field-value pair */
+ if (request->headers_index == request->headers_size) {
+ request->headers_size += 2;
+ request->headers = realloc(request->headers,
+ request->headers_size*sizeof(char*));
+ assert(request->headers);
+ request->headers[request->headers_index] = NULL;
+ request->headers[request->headers_index+1] = NULL;
+ }
+
+ /* Allocate space in the current header string */
+ if (request->headers[request->headers_index] == NULL) {
+ request->headers[request->headers_index] = calloc(1, length+1);
+ } else {
+ request->headers[request->headers_index] = realloc(
+ request->headers[request->headers_index],
+ strlen(request->headers[request->headers_index])+length+1
+ );
+ }
+ assert(request->headers[request->headers_index]);
+
+ strncat(request->headers[request->headers_index], at, length);
+ return 0;
+}
+
+static int
+on_header_value(llhttp_t *parser, const char *at, size_t length)
+{
+ http_request_t *request = parser->data;
+
+ /* Check if our index is a field */
+ if (request->headers_index%2 == 0) {
+ request->headers_index++;
+ }
+
+ /* Allocate space in the current header string */
+ if (request->headers[request->headers_index] == NULL) {
+ request->headers[request->headers_index] = calloc(1, length+1);
+ } else {
+ request->headers[request->headers_index] = realloc(
+ request->headers[request->headers_index],
+ strlen(request->headers[request->headers_index])+length+1
+ );
+ }
+ assert(request->headers[request->headers_index]);
+
+ strncat(request->headers[request->headers_index], at, length);
+ return 0;
+}
+
+static int
+on_body(llhttp_t *parser, const char *at, size_t length)
+{
+ http_request_t *request = parser->data;
+
+ request->data = realloc(request->data, request->datalen+length);
+ assert(request->data);
+
+ memcpy(request->data+request->datalen, at, length);
+ request->datalen += length;
+ return 0;
+}
+
+static int
+on_message_complete(llhttp_t *parser)
+{
+ http_request_t *request = parser->data;
+
+ request->method = llhttp_method_name(request->parser.method);
+ request->complete = 1;
+ return 0;
+}
+
+http_request_t *
+http_request_init(void)
+{
+ http_request_t *request;
+
+ request = calloc(1, sizeof(http_request_t));
+ if (!request) {
+ return NULL;
+ }
+
+ llhttp_settings_init(&request->parser_settings);
+ request->parser_settings.on_url = &on_url;
+ request->parser_settings.on_header_field = &on_header_field;
+ request->parser_settings.on_header_value = &on_header_value;
+ request->parser_settings.on_body = &on_body;
+ request->parser_settings.on_message_complete = &on_message_complete;
+
+ llhttp_init(&request->parser, HTTP_REQUEST, &request->parser_settings);
+ request->parser.data = request;
+ request->is_reverse = false;
+ return request;
+}
+
+void
+http_request_destroy(http_request_t *request)
+{
+ int i;
+
+ if (request) {
+ free(request->url);
+ for (i=0; iheaders_size; i++) {
+ free(request->headers[i]);
+ }
+ free(request->headers);
+ free(request->data);
+ free(request);
+ }
+}
+
+int
+http_request_add_data(http_request_t *request, const char *data, int datalen)
+{
+ int ret;
+
+ assert(request);
+
+ ret = llhttp_execute(&request->parser, data, datalen);
+
+ /* support for "Upgrade" to reverse http ("PTTH/1.0") protocol */
+ llhttp_resume_after_upgrade(&request->parser);
+
+ return ret;
+}
+
+int
+http_request_is_complete(http_request_t *request)
+{
+ assert(request);
+ return request->complete;
+}
+
+int
+http_request_has_error(http_request_t *request)
+{
+ assert(request);
+ if (request->is_reverse) {
+ return 0;
+ }
+ return (llhttp_get_errno(&request->parser) != HPE_OK);
+}
+
+const char *
+http_request_get_error_name(http_request_t *request)
+{
+ assert(request);
+ if (request->is_reverse) {
+ return NULL;
+ }
+ return llhttp_errno_name(llhttp_get_errno(&request->parser));
+}
+
+const char *
+http_request_get_error_description(http_request_t *request)
+{
+ assert(request);
+ if (request->is_reverse) {
+ return NULL;
+ }
+ return llhttp_get_error_reason(&request->parser);
+}
+
+const char *
+http_request_get_method(http_request_t *request)
+{
+ assert(request);
+ if (request->is_reverse) {
+ return NULL;
+ }
+ return request->method;
+}
+
+const char *
+http_request_get_url(http_request_t *request)
+{
+ assert(request);
+ if (request->is_reverse) {
+ return NULL;
+ }
+ return request->url;
+}
+
+const char *
+http_request_get_protocol(http_request_t *request)
+{
+ assert(request);
+ if (request->is_reverse) {
+ return NULL;
+ }
+ return request->protocol;
+}
+
+const char *
+http_request_get_header(http_request_t *request, const char *name)
+{
+ int i;
+
+ assert(request);
+ if (request->is_reverse) {
+ return NULL;
+ }
+
+ for (i=0; iheaders_size; i+=2) {
+ if (!strcmp(request->headers[i], name)) {
+ return request->headers[i+1];
+ }
+ }
+ return NULL;
+}
+
+const char *
+http_request_get_data(http_request_t *request, int *datalen)
+{
+ assert(request);
+ if (datalen) {
+ *datalen = request->datalen;
+ }
+ return request->data;
+}
+
+int
+http_request_get_header_string(http_request_t *request, char **header_str)
+{
+ if(!request || request->headers_size == 0) {
+ *header_str = NULL;
+ return 0;
+ }
+ if (request->is_reverse) {
+ *header_str = NULL;
+ return 0;
+ }
+ int len = 0;
+ for (int i = 0; i < request->headers_size; i++) {
+ len += strlen(request->headers[i]);
+ if (i%2 == 0) {
+ len += 2;
+ } else {
+ len++;
+ }
+ }
+ char *str = calloc(len+1, sizeof(char));
+ assert(str);
+ *header_str = str;
+ char *p = str;
+ int n = len + 1;
+ for (int i = 0; i < request->headers_size; i++) {
+ int hlen = strlen(request->headers[i]);
+ snprintf(p, n, "%s", request->headers[i]);
+ n -= hlen;
+ p += hlen;
+ if (i%2 == 0) {
+ snprintf(p, n, ": ");
+ n -= 2;
+ p += 2;
+ } else {
+ snprintf(p, n, "\n");
+ n--;
+ p++;
+ }
+ }
+ assert(p == &(str[len]));
+ return len;
+}
+
+bool http_request_is_reverse(http_request_t *request) {
+ return request->is_reverse;
+}
+
+void http_request_set_reverse(http_request_t *request) {
+ request->is_reverse = true;
+}
diff --git a/lib/http_request.h b/lib/http_request.h
new file mode 100644
index 0000000..dd13680
--- /dev/null
+++ b/lib/http_request.h
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef HTTP_REQUEST_H
+#define HTTP_REQUEST_H
+
+#include
+
+typedef struct http_request_s http_request_t;
+
+http_request_t *http_request_init(void);
+
+int http_request_add_data(http_request_t *request, const char *data, int datalen);
+int http_request_is_complete(http_request_t *request);
+int http_request_has_error(http_request_t *request);
+
+const char *http_request_get_error_name(http_request_t *request);
+const char *http_request_get_error_description(http_request_t *request);
+const char *http_request_get_method(http_request_t *request);
+const char *http_request_get_url(http_request_t *request);
+const char *http_request_get_protocol(http_request_t *request);
+const char *http_request_get_header(http_request_t *request, const char *name);
+const char *http_request_get_data(http_request_t *request, int *datalen);
+int http_request_get_header_string(http_request_t *request, char **header_str);
+bool http_request_is_reverse(http_request_t *request);
+void http_request_set_reverse(http_request_t *request);
+
+void http_request_destroy(http_request_t *request);
+
+#endif
diff --git a/lib/http_response.c b/lib/http_response.c
new file mode 100644
index 0000000..a194338
--- /dev/null
+++ b/lib/http_response.c
@@ -0,0 +1,198 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "http_response.h"
+#include "compat.h"
+
+struct http_response_s {
+ int complete;
+ int disconnect;
+
+ char *data;
+ int data_size;
+ int data_length;
+};
+
+
+static void
+http_response_add_data(http_response_t *response, const char *data, int datalen)
+{
+ int newdatasize;
+
+ assert(response);
+ assert(data);
+ assert(datalen > 0);
+
+ newdatasize = response->data_size;
+ while (response->data_size+datalen > newdatasize) {
+ newdatasize *= 2;
+ }
+ if (newdatasize != response->data_size) {
+ response->data = realloc(response->data, newdatasize);
+ assert(response->data);
+ }
+ memcpy(response->data+response->data_length, data, datalen);
+ response->data_length += datalen;
+}
+
+
+http_response_t *
+http_response_create()
+{
+ http_response_t *response = (http_response_t *) calloc(1, sizeof(http_response_t));
+ if (!response) {
+ return NULL;
+ }
+ /* Allocate response data */
+ response->data_size = 1024;
+ response->data = (char *) malloc(response->data_size);
+ if (!response->data) {
+ free(response);
+ return NULL;
+ }
+ return response;
+}
+
+void
+http_response_init(http_response_t *response, const char *protocol, int code, const char *message)
+{
+ assert(response);
+ response->data_length = 0; /* can be used to reinitialize a previously-initialized response */
+ char codestr[4];
+
+ assert(code >= 100 && code < 1000);
+
+ /* Convert code into string */
+ memset(codestr, 0, sizeof(codestr));
+ snprintf(codestr, sizeof(codestr), "%u", code);
+
+ /* Add first line of response to the data array */
+ http_response_add_data(response, protocol, strlen(protocol));
+ http_response_add_data(response, " ", 1);
+ http_response_add_data(response, codestr, strlen(codestr));
+ http_response_add_data(response, " ", 1);
+ http_response_add_data(response, message, strlen(message));
+ http_response_add_data(response, "\r\n", 2);
+}
+
+void
+http_response_reverse_request_init(http_response_t *request, const char *method, const char *url, const char *protocol)
+{
+ assert(request);
+ request->data_length = 0; /* reinitialize a previously-initialized response as a reverse-HTTP (PTTH/1.0) request */
+
+ /* Add first line of response to the data array */
+ http_response_add_data(request, method, strlen(method));
+ http_response_add_data(request, " ", 1);
+ http_response_add_data(request, url, strlen(url));
+ http_response_add_data(request, " ", 1);
+ http_response_add_data(request, protocol, strlen(protocol));
+ http_response_add_data(request, "\r\n", 2);
+}
+
+void
+http_response_destroy(http_response_t *response)
+{
+ if (response) {
+ free(response->data);
+ free(response);
+ }
+}
+
+void
+http_response_add_header(http_response_t *response, const char *name, const char *value)
+{
+ assert(response);
+ assert(name);
+ assert(value);
+
+ http_response_add_data(response, name, strlen(name));
+ http_response_add_data(response, ": ", 2);
+ http_response_add_data(response, value, strlen(value));
+ http_response_add_data(response, "\r\n", 2);
+}
+
+void
+http_response_finish(http_response_t *response, const char *data, int datalen)
+{
+ assert(response);
+ assert(datalen==0 || (data && datalen > 0));
+
+ if (data && datalen > 0) {
+ const char *hdrname = "Content-Length";
+ char hdrvalue[16];
+
+ memset(hdrvalue, 0, sizeof(hdrvalue));
+ snprintf(hdrvalue, sizeof(hdrvalue)-1, "%d", datalen);
+
+ /* Add Content-Length header first */
+ http_response_add_data(response, hdrname, strlen(hdrname));
+ http_response_add_data(response, ": ", 2);
+ http_response_add_data(response, hdrvalue, strlen(hdrvalue));
+ http_response_add_data(response, "\r\n\r\n", 4);
+
+ /* Add data to the end of response */
+ http_response_add_data(response, data, datalen);
+ } else {
+ /* check for "Content-Type" header, with datalen = 0 */
+ const char *item = "Content-Type";
+ int item_len = strlen(item);
+ const char *part = response->data;
+ int end = response->data_length - item_len;
+ for (int i = 0; i < end; i++) {
+ if (memcmp (part, item, item_len) == 0) {
+ const char *hdrname = "Content-Length: 0";
+ http_response_add_data(response, hdrname, strlen(hdrname));
+ http_response_add_data(response, "\r\n", 2);
+ break;
+ }
+ part++;
+ }
+ /* Add extra end of line after headers */
+ http_response_add_data(response, "\r\n", 2);
+ }
+ response->complete = 1;
+}
+
+void
+http_response_set_disconnect(http_response_t *response, int disconnect)
+{
+ assert(response);
+
+ response->disconnect = !!disconnect;
+}
+
+int
+http_response_get_disconnect(http_response_t *response)
+{
+ assert(response);
+
+ return response->disconnect;
+}
+
+const char *
+http_response_get_data(http_response_t *response, int *datalen)
+{
+ assert(response);
+ assert(datalen);
+ assert(response->complete);
+
+ *datalen = response->data_length;
+ return response->data;
+}
diff --git a/lib/http_response.h b/lib/http_response.h
new file mode 100644
index 0000000..e34ba0c
--- /dev/null
+++ b/lib/http_response.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==============================================================
+ * modified fduncanh 2024
+ */
+
+#ifndef HTTP_RESPONSE_H
+#define HTTP_RESPONSE_H
+
+typedef struct http_response_s http_response_t;
+
+http_response_t *http_response_create();
+void http_response_init(http_response_t *response, const char *protocol, int code, const char *message);
+void http_response_reverse_request_init(http_response_t *request, const char *method, const char *url,
+ const char *protocol);
+
+void http_response_add_header(http_response_t *response, const char *name, const char *value);
+void http_response_finish(http_response_t *response, const char *data, int datalen);
+
+void http_response_set_disconnect(http_response_t *response, int disconnect);
+int http_response_get_disconnect(http_response_t *response);
+
+const char *http_response_get_data(http_response_t *response, int *datalen);
+
+void http_response_destroy(http_response_t *response);
+
+#endif
diff --git a/lib/httpd.c b/lib/httpd.c
new file mode 100644
index 0000000..28f3d3c
--- /dev/null
+++ b/lib/httpd.c
@@ -0,0 +1,663 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *===================================================================
+ * modified by fduncanh 2022
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "httpd.h"
+#include "netutils.h"
+#include "http_request.h"
+#include "compat.h"
+#include "logger.h"
+#include "utils.h"
+
+static const char *typename[] = {
+ [CONNECTION_TYPE_UNKNOWN] = "Unknown",
+ [CONNECTION_TYPE_RAOP] = "RAOP",
+ [CONNECTION_TYPE_AIRPLAY] = "AirPlay",
+ [CONNECTION_TYPE_PTTH] = "AirPlay (reversed)",
+ [CONNECTION_TYPE_HLS] = "HLS"
+};
+
+struct http_connection_s {
+ int connected;
+
+ int socket_fd;
+ void *user_data;
+ connection_type_t type;
+ http_request_t *request;
+};
+typedef struct http_connection_s http_connection_t;
+
+struct httpd_s {
+ logger_t *logger;
+ httpd_callbacks_t callbacks;
+
+ int max_connections;
+ int open_connections;
+ http_connection_t *connections;
+ char nohold;
+
+ /* These variables only edited mutex locked */
+ int running;
+ int joined;
+ thread_handle_t thread;
+ mutex_handle_t run_mutex;
+
+ /* Server fds for accepting connections */
+ int server_fd4;
+ int server_fd6;
+};
+
+const char *
+httpd_get_connection_typename (connection_type_t type) {
+ return typename[type];
+}
+
+int
+httpd_get_connection_socket (httpd_t *httpd, void *user_data) {
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ if (connection->user_data == user_data) {
+ return connection->socket_fd;
+ }
+ }
+ return -1;
+}
+
+int
+httpd_set_connection_type (httpd_t *httpd, void *user_data, connection_type_t type) {
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ if (connection->user_data == user_data) {
+ connection->type = type;
+ return i;
+ }
+ }
+ return -1;
+}
+
+int
+httpd_count_connection_type (httpd_t *httpd, connection_type_t type) {
+ int count = 0;
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ if (connection->type == type) {
+ count++;
+ }
+ }
+ return count;
+}
+
+int
+httpd_get_connection_socket_by_type (httpd_t *httpd, connection_type_t type, int instance){
+ int count = 0;
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ if (connection->type == type) {
+ count++;
+ if (count == instance) {
+ return connection->socket_fd;
+ }
+ }
+ }
+ return 0;
+}
+
+void *
+httpd_get_connection_by_type (httpd_t *httpd, connection_type_t type, int instance){
+ int count = 0;
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ if (connection->type == type) {
+ count++;
+ if (count == instance) {
+ return connection->user_data;
+ }
+ }
+ }
+ return NULL;
+}
+
+#define MAX_CONNECTIONS 12 /* value used in AppleTV 3*/
+httpd_t *
+httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold)
+{
+ httpd_t *httpd;
+ assert(logger);
+ assert(callbacks);
+
+ /* Allocate the httpd_t structure */
+ httpd = calloc(1, sizeof(httpd_t));
+ if (!httpd) {
+ return NULL;
+ }
+
+ httpd->nohold = (nohold ? 1 : 0);
+ httpd->max_connections = MAX_CONNECTIONS;
+ httpd->connections = calloc(httpd->max_connections, sizeof(http_connection_t));
+ if (!httpd->connections) {
+ free(httpd);
+ return NULL;
+ }
+
+ /* Use the logger provided */
+ httpd->logger = logger;
+
+ /* Save callback pointers */
+ memcpy(&httpd->callbacks, callbacks, sizeof(httpd_callbacks_t));
+
+ /* Initial status joined */
+ httpd->running = 0;
+ httpd->joined = 1;
+
+ return httpd;
+}
+
+void
+httpd_destroy(httpd_t *httpd)
+{
+ if (httpd) {
+ httpd_stop(httpd);
+
+ free(httpd->connections);
+ free(httpd);
+ }
+}
+
+static void
+httpd_remove_connection(httpd_t *httpd, http_connection_t *connection)
+{
+ if (connection->request) {
+ http_request_destroy(connection->request);
+ connection->request = NULL;
+ }
+ httpd->callbacks.conn_destroy(connection->user_data);
+ shutdown(connection->socket_fd, SHUT_WR);
+ closesocket(connection->socket_fd);
+ connection->connected = 0;
+ connection->user_data = NULL;
+ connection->type = CONNECTION_TYPE_UNKNOWN;
+ httpd->open_connections--;
+}
+
+static int
+httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len, unsigned char *remote,
+ int remote_len, unsigned int zone_id)
+{
+ void *user_data;
+ int i;
+
+ for (i=0; imax_connections; i++) {
+ if (!httpd->connections[i].connected) {
+ break;
+ }
+ }
+ if (i == httpd->max_connections) {
+ /* This code should never be reached, we do not select server_fds when full */
+ logger_log(httpd->logger, LOGGER_INFO, "Max connections reached");
+ return -1;
+ }
+ user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len, zone_id);
+ if (!user_data) {
+ logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler");
+ return -1;
+ }
+
+ httpd->open_connections++;
+ httpd->connections[i].socket_fd = fd;
+ httpd->connections[i].connected = 1;
+ httpd->connections[i].user_data = user_data;
+ httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN; //should not be necessary ...
+ return 0;
+}
+
+static int
+httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
+{
+ struct sockaddr_storage remote_saddr;
+ socklen_t remote_saddrlen;
+ struct sockaddr_storage local_saddr;
+ socklen_t local_saddrlen;
+ unsigned char *local, *remote;
+ unsigned int local_zone_id, remote_zone_id;
+ int local_len, remote_len;
+ int ret, fd;
+
+ remote_saddrlen = sizeof(remote_saddr);
+ fd = accept(server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen);
+ if (fd == -1) {
+ /* FIXME: Error happened */
+ return -1;
+ }
+
+ local_saddrlen = sizeof(local_saddr);
+ ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen);
+ if (ret == -1) {
+ shutdown(fd, SHUT_RDWR);
+ closesocket(fd);
+ return 0;
+ }
+
+ logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d",
+ (is_ipv6 ? "IPv6" : "IPv4"), fd);
+ local = netutils_get_address(&local_saddr, &local_len, &local_zone_id);
+ remote = netutils_get_address(&remote_saddr, &remote_len, &remote_zone_id);
+ assert (local_zone_id == remote_zone_id);
+
+ ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len, local_zone_id);
+ if (ret == -1) {
+ shutdown(fd, SHUT_RDWR);
+ closesocket(fd);
+ return 0;
+ }
+ return 1;
+}
+
+bool
+httpd_nohold(httpd_t *httpd) {
+ return (httpd->nohold ? true: false);
+}
+
+void
+httpd_remove_known_connections(httpd_t *httpd) {
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected || connection->type == CONNECTION_TYPE_UNKNOWN) {
+ continue;
+ }
+ httpd_remove_connection(httpd, connection);
+ }
+}
+
+static THREAD_RETVAL
+httpd_thread(void *arg)
+{
+ httpd_t *httpd = arg;
+ char http[] = "HTTP/1.1";
+ char buffer[1024];
+ int i;
+
+ bool logger_debug = (logger_get_level(httpd->logger) >= LOGGER_DEBUG);
+ assert(httpd);
+
+ while (1) {
+ fd_set rfds;
+ struct timeval tv;
+ int nfds=0;
+ int ret;
+ int new_request;
+
+ MUTEX_LOCK(httpd->run_mutex);
+ if (!httpd->running) {
+ MUTEX_UNLOCK(httpd->run_mutex);
+ break;
+ }
+ MUTEX_UNLOCK(httpd->run_mutex);
+
+ /* Set timeout value to 5ms */
+ tv.tv_sec = 1;
+ tv.tv_usec = 5000;
+
+ /* Get the correct nfds value and set rfds */
+ FD_ZERO(&rfds);
+ if (httpd->open_connections < httpd->max_connections) {
+ if (httpd->server_fd4 != -1) {
+ FD_SET(httpd->server_fd4, &rfds);
+ if (nfds <= httpd->server_fd4) {
+ nfds = httpd->server_fd4+1;
+ }
+ }
+ if (httpd->server_fd6 != -1) {
+ FD_SET(httpd->server_fd6, &rfds);
+ if (nfds <= httpd->server_fd6) {
+ nfds = httpd->server_fd6+1;
+ }
+ }
+ }
+ for (i=0; imax_connections; i++) {
+ int socket_fd;
+ if (!httpd->connections[i].connected) {
+ continue;
+ }
+ socket_fd = httpd->connections[i].socket_fd;
+ FD_SET(socket_fd, &rfds);
+ if (nfds <= socket_fd) {
+ nfds = socket_fd+1;
+ }
+ }
+
+ ret = select(nfds, &rfds, NULL, NULL, &tv);
+ if (ret == 0) {
+ /* Timeout happened */
+ continue;
+ } else if (ret == -1) {
+ logger_log(httpd->logger, LOGGER_ERR, "httpd error in select: %d %s", errno, strerror(errno));
+ break;
+ }
+
+ if (httpd->open_connections < httpd->max_connections &&
+ httpd->server_fd4 != -1 && FD_ISSET(httpd->server_fd4, &rfds)) {
+ ret = httpd_accept_connection(httpd, httpd->server_fd4, 0);
+ if (ret == -1) {
+ logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv4");
+ break;
+ } else if (ret == 0) {
+ continue;
+ }
+ }
+ if (httpd->open_connections < httpd->max_connections &&
+ httpd->server_fd6 != -1 && FD_ISSET(httpd->server_fd6, &rfds)) {
+ ret = httpd_accept_connection(httpd, httpd->server_fd6, 1);
+ if (ret == -1) {
+ logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv6");
+ break;
+ } else if (ret == 0) {
+ continue;
+ }
+ }
+ for (i=0; imax_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+
+ if (!connection->connected) {
+ continue;
+ }
+ if (!FD_ISSET(connection->socket_fd, &rfds)) {
+ continue;
+ }
+
+ /* If not in the middle of request, allocate one */
+ if (!connection->request) {
+ connection->request = http_request_init();
+ assert(connection->request);
+ new_request = 1;
+ if (connection->type == CONNECTION_TYPE_PTTH) {
+ http_request_is_reverse(connection->request);
+ }
+ logger_log(httpd->logger, LOGGER_DEBUG, "new request, connection %d, socket %d type %s",
+ i, connection->socket_fd, typename [connection->type]);
+ } else {
+ new_request = 0;
+ }
+
+ logger_log(httpd->logger, LOGGER_DEBUG, "httpd receiving on socket %d, connection %d",
+ connection->socket_fd, i);
+ if (logger_debug) {
+ logger_log(httpd->logger, LOGGER_DEBUG,"\nhttpd: current connections:");
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if(!connection->connected) {
+ continue;
+ }
+ if (!FD_ISSET(connection->socket_fd, &rfds)) {
+ logger_log(httpd->logger, LOGGER_DEBUG, "connection %d type %d socket %d conn %p %s", i,
+ connection->type, connection->socket_fd,
+ connection->user_data, typename [connection->type]);
+ } else {
+ logger_log(httpd->logger, LOGGER_DEBUG, "connection %d type %d socket %d conn %p %s ACTIVE CONNECTION",
+ i, connection->type, connection->socket_fd, connection->user_data, typename [connection->type]);
+ }
+ }
+ logger_log(httpd->logger, LOGGER_DEBUG, " ");
+ }
+ /* reverse-http responses from the client must not be sent to the llhttp parser:
+ * such messages start with "HTTP/1.1" */
+ if (new_request) {
+ int readstart = 0;
+ new_request = 0;
+ while (readstart < 8) {
+ ret = recv(connection->socket_fd, buffer + readstart, sizeof(buffer) - 1 - readstart, 0);
+ if (ret == 0) {
+ logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d",
+ connection->socket_fd);
+ break;
+ } else if (ret == -1) {
+ if (errno == EAGAIN) {
+ continue;
+ } else {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(httpd->logger, LOGGER_ERR, "httpd: recv socket error %d:%s",
+ sock_err, strerror(sock_err));
+ break;
+ }
+ } else {
+ readstart += ret;
+ ret = readstart;
+ }
+ }
+ if (!memcmp(buffer, http, 8)) {
+ http_request_set_reverse(connection->request);
+ }
+ } else {
+ ret = recv(connection->socket_fd, buffer, sizeof(buffer) - 1, 0);
+ if (ret == 0) {
+ logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d",
+ connection->socket_fd);
+ httpd_remove_connection(httpd, connection);
+ continue;
+ }
+ }
+ if (http_request_is_reverse(connection->request)) {
+ /* this is a response from the client to a
+ * GET /event reverse HTTP request from the server */
+ if (ret && logger_debug) {
+ buffer[ret] = '\0';
+ logger_log(httpd->logger, LOGGER_INFO, "<<<< received response from client"
+ " (reversed HTTP = \"PTTH/1.0\") connection"
+ " on socket %d:\n%s\n", connection->socket_fd, buffer);
+ }
+ if (ret == 0) {
+ httpd_remove_connection(httpd, connection);
+ }
+ continue;
+ }
+
+ /* Parse HTTP request from data read from connection */
+ http_request_add_data(connection->request, buffer, ret);
+ if (http_request_has_error(connection->request)) {
+ logger_log(httpd->logger, LOGGER_ERR, "httpd error in parsing: %s",
+ http_request_get_error_name(connection->request));
+ httpd_remove_connection(httpd, connection);
+ continue;
+ }
+
+ /* If request is finished, process and deallocate */
+ if (http_request_is_complete(connection->request)) {
+ http_response_t *response = NULL;
+ // Callback the received data to raop
+ if (logger_debug) {
+ const char *method = http_request_get_method(connection->request);
+ const char *url = http_request_get_url(connection->request);
+ const char *protocol = http_request_get_protocol(connection->request);
+ logger_log(httpd->logger, LOGGER_INFO, "httpd request received on socket %d, "
+ "connection %d, method = %s, url = %s, protocol = %s",
+ connection->socket_fd, i, method, url, protocol);
+ }
+ httpd->callbacks.conn_request(connection->user_data, connection->request, &response);
+ http_request_destroy(connection->request);
+ connection->request = NULL;
+
+ if (response) {
+ const char *data;
+ int datalen;
+ int written;
+ int ret;
+
+ /* Get response data and datalen */
+ data = http_response_get_data(response, &datalen);
+
+ written = 0;
+ while (written < datalen) {
+ ret = send(connection->socket_fd, data+written, datalen-written, 0);
+ if (ret == -1) {
+ logger_log(httpd->logger, LOGGER_ERR, "httpd error in sending data");
+ break;
+ }
+ written += ret;
+ }
+
+ if (http_response_get_disconnect(response)) {
+ logger_log(httpd->logger, LOGGER_INFO, "Disconnecting on software request");
+ httpd_remove_connection(httpd, connection);
+ }
+ } else {
+ logger_log(httpd->logger, LOGGER_WARNING, "httpd didn't get response");
+ }
+ http_response_destroy(response);
+ } else {
+ logger_log(httpd->logger, LOGGER_DEBUG, "Request not complete, waiting for more data...");
+ }
+ }
+ }
+
+ /* Remove all connections that are still connected */
+ for (i=0; imax_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+
+ if (!connection->connected) {
+ continue;
+ }
+ logger_log(httpd->logger, LOGGER_INFO, "Removing connection for socket %d", connection->socket_fd);
+ httpd_remove_connection(httpd, connection);
+ }
+
+ /* Close server sockets since they are not used any more */
+ if (httpd->server_fd4 != -1) {
+ shutdown(httpd->server_fd4, SHUT_RDWR);
+ closesocket(httpd->server_fd4);
+ httpd->server_fd4 = -1;
+ }
+ if (httpd->server_fd6 != -1) {
+ shutdown(httpd->server_fd6, SHUT_RDWR);
+ closesocket(httpd->server_fd6);
+ httpd->server_fd6 = -1;
+ }
+
+ // Ensure running reflects the actual state
+ MUTEX_LOCK(httpd->run_mutex);
+ httpd->running = 0;
+ MUTEX_UNLOCK(httpd->run_mutex);
+
+ logger_log(httpd->logger, LOGGER_DEBUG, "Exiting HTTP thread");
+
+ return 0;
+}
+
+int
+httpd_start(httpd_t *httpd, unsigned short *port)
+{
+ /* How many connection attempts are kept in queue */
+ int backlog = 5;
+
+ assert(httpd);
+ assert(port);
+
+ MUTEX_LOCK(httpd->run_mutex);
+ if (httpd->running || !httpd->joined) {
+ MUTEX_UNLOCK(httpd->run_mutex);
+ return 0;
+ }
+
+ httpd->server_fd4 = netutils_init_socket(port, 0, 0);
+ if (httpd->server_fd4 == -1) {
+ logger_log(httpd->logger, LOGGER_ERR, "Error initialising socket %d", SOCKET_GET_ERROR());
+ MUTEX_UNLOCK(httpd->run_mutex);
+ return -1;
+ }
+ httpd->server_fd6 = netutils_init_socket(port, 1, 0);
+ if (httpd->server_fd6 == -1) {
+ logger_log(httpd->logger, LOGGER_WARNING, "Error initialising IPv6 socket %d", SOCKET_GET_ERROR());
+ logger_log(httpd->logger, LOGGER_WARNING, "Continuing without IPv6 support");
+ }
+
+ if (httpd->server_fd4 != -1 && listen(httpd->server_fd4, backlog) == -1) {
+ logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv4 socket");
+ closesocket(httpd->server_fd4);
+ closesocket(httpd->server_fd6);
+ MUTEX_UNLOCK(httpd->run_mutex);
+ return -2;
+ }
+ if (httpd->server_fd6 != -1 && listen(httpd->server_fd6, backlog) == -1) {
+ logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv6 socket");
+ closesocket(httpd->server_fd4);
+ closesocket(httpd->server_fd6);
+ MUTEX_UNLOCK(httpd->run_mutex);
+ return -2;
+ }
+ logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket(s)");
+
+ /* Set values correctly and create new thread */
+ httpd->running = 1;
+ httpd->joined = 0;
+ THREAD_CREATE(httpd->thread, httpd_thread, httpd);
+ MUTEX_UNLOCK(httpd->run_mutex);
+
+ return 1;
+}
+
+int
+httpd_is_running(httpd_t *httpd)
+{
+ int running;
+
+ assert(httpd);
+
+ MUTEX_LOCK(httpd->run_mutex);
+ running = httpd->running || !httpd->joined;
+ MUTEX_UNLOCK(httpd->run_mutex);
+
+ return running;
+}
+
+void
+httpd_stop(httpd_t *httpd)
+{
+ assert(httpd);
+
+ MUTEX_LOCK(httpd->run_mutex);
+ if (!httpd->running || httpd->joined) {
+ MUTEX_UNLOCK(httpd->run_mutex);
+ return;
+ }
+ httpd->running = 0;
+ MUTEX_UNLOCK(httpd->run_mutex);
+
+ THREAD_JOIN(httpd->thread);
+
+ MUTEX_LOCK(httpd->run_mutex);
+ httpd->joined = 1;
+ MUTEX_UNLOCK(httpd->run_mutex);
+}
diff --git a/lib/httpd.h b/lib/httpd.h
new file mode 100644
index 0000000..1df0e6b
--- /dev/null
+++ b/lib/httpd.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef HTTPD_H
+#define HTTPD_H
+
+#include "logger.h"
+#include "http_request.h"
+#include "http_response.h"
+
+typedef struct httpd_s httpd_t;
+
+typedef enum connectype_type_e {
+ CONNECTION_TYPE_UNKNOWN,
+ CONNECTION_TYPE_RAOP,
+ CONNECTION_TYPE_AIRPLAY,
+ CONNECTION_TYPE_PTTH,
+ CONNECTION_TYPE_HLS
+} connection_type_t;
+
+struct httpd_callbacks_s {
+ void* opaque;
+ void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote,
+ int remotelen, unsigned int zone_id);
+ void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response);
+ void (*conn_destroy)(void *ptr);
+};
+typedef struct httpd_callbacks_s httpd_callbacks_t;
+bool httpd_nohold(httpd_t *httpd);
+void httpd_remove_known_connections(httpd_t *httpd);
+
+int httpd_set_connection_type (httpd_t *http, void *user_data, connection_type_t type);
+int httpd_count_connection_type (httpd_t *http, connection_type_t type);
+int httpd_get_connection_socket (httpd_t *httpd, void *user_data);
+int httpd_get_connection_socket_by_type (httpd_t *httpd, connection_type_t type, int instance);
+const char *httpd_get_connection_typename (connection_type_t type);
+void *httpd_get_connection_by_type (httpd_t *httpd, connection_type_t type, int instance);
+httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold);
+
+int httpd_is_running(httpd_t *httpd);
+
+int httpd_start(httpd_t *httpd, unsigned short *port);
+void httpd_stop(httpd_t *httpd);
+
+void httpd_destroy(httpd_t *httpd);
+
+
+#endif
diff --git a/lib/llhttp/CMakeLists.txt b/lib/llhttp/CMakeLists.txt
new file mode 100644
index 0000000..3895909
--- /dev/null
+++ b/lib/llhttp/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.5)
+aux_source_directory(. llhttp_src)
+set(DIR_SRCS ${llhttp_src})
+include_directories(.)
+add_library( llhttp
+ STATIC
+ ${DIR_SRCS})
diff --git a/lib/llhttp/LICENSE-MIT b/lib/llhttp/LICENSE-MIT
new file mode 100644
index 0000000..6c1512d
--- /dev/null
+++ b/lib/llhttp/LICENSE-MIT
@@ -0,0 +1,22 @@
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2018.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/llhttp/api.c b/lib/llhttp/api.c
new file mode 100644
index 0000000..8c2ce3d
--- /dev/null
+++ b/lib/llhttp/api.c
@@ -0,0 +1,510 @@
+#include
+#include
+#include
+
+#include "llhttp.h"
+
+#define CALLBACK_MAYBE(PARSER, NAME) \
+ do { \
+ const llhttp_settings_t* settings; \
+ settings = (const llhttp_settings_t*) (PARSER)->settings; \
+ if (settings == NULL || settings->NAME == NULL) { \
+ err = 0; \
+ break; \
+ } \
+ err = settings->NAME((PARSER)); \
+ } while (0)
+
+#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
+ do { \
+ const llhttp_settings_t* settings; \
+ settings = (const llhttp_settings_t*) (PARSER)->settings; \
+ if (settings == NULL || settings->NAME == NULL) { \
+ err = 0; \
+ break; \
+ } \
+ err = settings->NAME((PARSER), (START), (LEN)); \
+ if (err == -1) { \
+ err = HPE_USER; \
+ llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
+ } \
+ } while (0)
+
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+ const llhttp_settings_t* settings) {
+ llhttp__internal_init(parser);
+
+ parser->type = type;
+ parser->settings = (void*) settings;
+}
+
+
+#if defined(__wasm__)
+
+extern int wasm_on_message_begin(llhttp_t * p);
+extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
+ uint8_t upgrade, int should_keep_alive);
+extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
+extern int wasm_on_message_complete(llhttp_t * p);
+
+static int wasm_on_headers_complete_wrap(llhttp_t* p) {
+ return wasm_on_headers_complete(p, p->status_code, p->upgrade,
+ llhttp_should_keep_alive(p));
+}
+
+const llhttp_settings_t wasm_settings = {
+ wasm_on_message_begin,
+ wasm_on_url,
+ wasm_on_status,
+ NULL,
+ NULL,
+ wasm_on_header_field,
+ wasm_on_header_value,
+ NULL,
+ NULL,
+ wasm_on_headers_complete_wrap,
+ wasm_on_body,
+ wasm_on_message_complete,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+llhttp_t* llhttp_alloc(llhttp_type_t type) {
+ llhttp_t* parser = malloc(sizeof(llhttp_t));
+ llhttp_init(parser, type, &wasm_settings);
+ return parser;
+}
+
+void llhttp_free(llhttp_t* parser) {
+ free(parser);
+}
+
+#endif // defined(__wasm__)
+
+/* Some getters required to get stuff from the parser */
+
+uint8_t llhttp_get_type(llhttp_t* parser) {
+ return parser->type;
+}
+
+uint8_t llhttp_get_http_major(llhttp_t* parser) {
+ return parser->http_major;
+}
+
+uint8_t llhttp_get_http_minor(llhttp_t* parser) {
+ return parser->http_minor;
+}
+
+uint8_t llhttp_get_method(llhttp_t* parser) {
+ return parser->method;
+}
+
+int llhttp_get_status_code(llhttp_t* parser) {
+ return parser->status_code;
+}
+
+uint8_t llhttp_get_upgrade(llhttp_t* parser) {
+ return parser->upgrade;
+}
+
+
+void llhttp_reset(llhttp_t* parser) {
+ llhttp_type_t type = parser->type;
+ const llhttp_settings_t* settings = parser->settings;
+ void* data = parser->data;
+ uint16_t lenient_flags = parser->lenient_flags;
+
+ llhttp__internal_init(parser);
+
+ parser->type = type;
+ parser->settings = (void*) settings;
+ parser->data = data;
+ parser->lenient_flags = lenient_flags;
+}
+
+
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
+ return llhttp__internal_execute(parser, data, data + len);
+}
+
+
+void llhttp_settings_init(llhttp_settings_t* settings) {
+ memset(settings, 0, sizeof(*settings));
+}
+
+
+llhttp_errno_t llhttp_finish(llhttp_t* parser) {
+ int err;
+
+ /* We're in an error state. Don't bother doing anything. */
+ if (parser->error != 0) {
+ return 0;
+ }
+
+ switch (parser->finish) {
+ case HTTP_FINISH_SAFE_WITH_CB:
+ CALLBACK_MAYBE(parser, on_message_complete);
+ if (err != HPE_OK) return err;
+
+ /* FALLTHROUGH */
+ case HTTP_FINISH_SAFE:
+ return HPE_OK;
+ case HTTP_FINISH_UNSAFE:
+ parser->reason = "Invalid EOF state";
+ return HPE_INVALID_EOF_STATE;
+ default:
+ abort();
+ }
+}
+
+
+void llhttp_pause(llhttp_t* parser) {
+ if (parser->error != HPE_OK) {
+ return;
+ }
+
+ parser->error = HPE_PAUSED;
+ parser->reason = "Paused";
+}
+
+
+void llhttp_resume(llhttp_t* parser) {
+ if (parser->error != HPE_PAUSED) {
+ return;
+ }
+
+ parser->error = 0;
+}
+
+
+void llhttp_resume_after_upgrade(llhttp_t* parser) {
+ if (parser->error != HPE_PAUSED_UPGRADE) {
+ return;
+ }
+
+ parser->error = 0;
+}
+
+
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
+ return parser->error;
+}
+
+
+const char* llhttp_get_error_reason(const llhttp_t* parser) {
+ return parser->reason;
+}
+
+
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
+ parser->reason = reason;
+}
+
+
+const char* llhttp_get_error_pos(const llhttp_t* parser) {
+ return parser->error_pos;
+}
+
+
+const char* llhttp_errno_name(llhttp_errno_t err) {
+#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
+ switch (err) {
+ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+ default: abort();
+ }
+#undef HTTP_ERRNO_GEN
+}
+
+
+const char* llhttp_method_name(llhttp_method_t method) {
+#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
+ switch (method) {
+ HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
+ default: abort();
+ }
+#undef HTTP_METHOD_GEN
+}
+
+const char* llhttp_status_name(llhttp_status_t status) {
+#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING;
+ switch (status) {
+ HTTP_STATUS_MAP(HTTP_STATUS_GEN)
+ default: abort();
+ }
+#undef HTTP_STATUS_GEN
+}
+
+
+void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_HEADERS;
+ } else {
+ parser->lenient_flags &= ~LENIENT_HEADERS;
+ }
+}
+
+
+void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
+ } else {
+ parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
+ }
+}
+
+
+void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_KEEP_ALIVE;
+ } else {
+ parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
+ }
+}
+
+void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
+ } else {
+ parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
+ }
+}
+
+void llhttp_set_lenient_version(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_VERSION;
+ } else {
+ parser->lenient_flags &= ~LENIENT_VERSION;
+ }
+}
+
+void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
+ } else {
+ parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
+ }
+}
+
+void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
+ } else {
+ parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
+ }
+}
+
+void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
+ } else {
+ parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
+ }
+}
+
+void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;
+ } else {
+ parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;
+ }
+}
+
+void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {
+ if (enabled) {
+ parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
+ } else {
+ parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
+ }
+}
+
+/* Callbacks */
+
+
+int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_message_begin);
+ return err;
+}
+
+
+int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_url_complete);
+ return err;
+}
+
+
+int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_status_complete);
+ return err;
+}
+
+
+int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_method_complete);
+ return err;
+}
+
+
+int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_version_complete);
+ return err;
+}
+
+
+int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_header_field_complete);
+ return err;
+}
+
+
+int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_header_value_complete);
+ return err;
+}
+
+
+int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_headers_complete);
+ return err;
+}
+
+
+int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_message_complete);
+ return err;
+}
+
+
+int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_header);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_extension_name_complete);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);
+ return err;
+}
+
+
+int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_extension_value_complete);
+ return err;
+}
+
+
+int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_chunk_complete);
+ return err;
+}
+
+
+int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {
+ int err;
+ CALLBACK_MAYBE(s, on_reset);
+ return err;
+}
+
+
+/* Private */
+
+
+void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
+ const char* msg) {
+ if (p == endp) {
+ fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
+ s->flags, msg);
+ } else {
+ fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
+ s->type, s->flags, *p, msg);
+ }
+}
diff --git a/lib/llhttp/http.c b/lib/llhttp/http.c
new file mode 100644
index 0000000..1ab91a5
--- /dev/null
+++ b/lib/llhttp/http.c
@@ -0,0 +1,170 @@
+#include
+#ifndef LLHTTP__TEST
+# include "llhttp.h"
+#else
+# define llhttp_t llparse_t
+#endif /* */
+
+int llhttp_message_needs_eof(const llhttp_t* parser);
+int llhttp_should_keep_alive(const llhttp_t* parser);
+
+int llhttp__before_headers_complete(llhttp_t* parser, const char* p,
+ const char* endp) {
+ /* Set this here so that on_headers_complete() callbacks can see it */
+ if ((parser->flags & F_UPGRADE) &&
+ (parser->flags & F_CONNECTION_UPGRADE)) {
+ /* For responses, "Upgrade: foo" and "Connection: upgrade" are
+ * mandatory only when it is a 101 Switching Protocols response,
+ * otherwise it is purely informational, to announce support.
+ */
+ parser->upgrade =
+ (parser->type == HTTP_REQUEST || parser->status_code == 101);
+ } else {
+ parser->upgrade = (parser->method == HTTP_CONNECT);
+ }
+ return 0;
+}
+
+
+/* Return values:
+ * 0 - No body, `restart`, message_complete
+ * 1 - CONNECT request, `restart`, message_complete, and pause
+ * 2 - chunk_size_start
+ * 3 - body_identity
+ * 4 - body_identity_eof
+ * 5 - invalid transfer-encoding for request
+ */
+int llhttp__after_headers_complete(llhttp_t* parser, const char* p,
+ const char* endp) {
+ int hasBody;
+
+ hasBody = parser->flags & F_CHUNKED || parser->content_length > 0;
+ if (
+ (parser->upgrade && (parser->method == HTTP_CONNECT ||
+ (parser->flags & F_SKIPBODY) || !hasBody)) ||
+ /* See RFC 2616 section 4.4 - 1xx e.g. Continue */
+ (parser->type == HTTP_RESPONSE && parser->status_code == 101)
+ ) {
+ /* Exit, the rest of the message is in a different protocol. */
+ return 1;
+ }
+
+ if (parser->type == HTTP_RESPONSE && parser->status_code == 100) {
+ /* No body, restart as the message is complete */
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (
+ parser->flags & F_SKIPBODY || /* response to a HEAD request */
+ (
+ parser->type == HTTP_RESPONSE && (
+ parser->status_code == 102 || /* Processing */
+ parser->status_code == 103 || /* Early Hints */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 /* Not Modified */
+ )
+ )
+ ) {
+ return 0;
+ } else if (parser->flags & F_CHUNKED) {
+ /* chunked encoding - ignore Content-Length header, prepare for a chunk */
+ return 2;
+ } else if (parser->flags & F_TRANSFER_ENCODING) {
+ if (parser->type == HTTP_REQUEST &&
+ (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 &&
+ (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) {
+ /* RFC 7230 3.3.3 */
+
+ /* If a Transfer-Encoding header field
+ * is present in a request and the chunked transfer coding is not
+ * the final encoding, the message body length cannot be determined
+ * reliably; the server MUST respond with the 400 (Bad Request)
+ * status code and then close the connection.
+ */
+ return 5;
+ } else {
+ /* RFC 7230 3.3.3 */
+
+ /* If a Transfer-Encoding header field is present in a response and
+ * the chunked transfer coding is not the final encoding, the
+ * message body length is determined by reading the connection until
+ * it is closed by the server.
+ */
+ return 4;
+ }
+ } else {
+ if (!(parser->flags & F_CONTENT_LENGTH)) {
+ if (!llhttp_message_needs_eof(parser)) {
+ /* Assume content-length 0 - read the next */
+ return 0;
+ } else {
+ /* Read body until EOF */
+ return 4;
+ }
+ } else if (parser->content_length == 0) {
+ /* Content-Length header given but zero: Content-Length: 0\r\n */
+ return 0;
+ } else {
+ /* Content-Length header given and non-zero */
+ return 3;
+ }
+ }
+}
+
+
+int llhttp__after_message_complete(llhttp_t* parser, const char* p,
+ const char* endp) {
+ int should_keep_alive;
+
+ should_keep_alive = llhttp_should_keep_alive(parser);
+ parser->finish = HTTP_FINISH_SAFE;
+ parser->flags = 0;
+
+ /* NOTE: this is ignored in loose parsing mode */
+ return should_keep_alive;
+}
+
+
+int llhttp_message_needs_eof(const llhttp_t* parser) {
+ if (parser->type == HTTP_REQUEST) {
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 || /* Not Modified */
+ (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */
+ return 0;
+ }
+
+ /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */
+ if ((parser->flags & F_TRANSFER_ENCODING) &&
+ (parser->flags & F_CHUNKED) == 0) {
+ return 1;
+ }
+
+ if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int llhttp_should_keep_alive(const llhttp_t* parser) {
+ if (parser->http_major > 0 && parser->http_minor > 0) {
+ /* HTTP/1.1 */
+ if (parser->flags & F_CONNECTION_CLOSE) {
+ return 0;
+ }
+ } else {
+ /* HTTP/1.0 or earlier */
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+ return 0;
+ }
+ }
+
+ return !llhttp_message_needs_eof(parser);
+}
diff --git a/lib/llhttp/llhttp.c b/lib/llhttp/llhttp.c
new file mode 100644
index 0000000..3ef3b81
--- /dev/null
+++ b/lib/llhttp/llhttp.c
@@ -0,0 +1,10168 @@
+#include
+#include
+#include
+
+#ifdef __SSE4_2__
+ #ifdef _MSC_VER
+ #include
+ #else /* !_MSC_VER */
+ #include
+ #endif /* _MSC_VER */
+#endif /* __SSE4_2__ */
+
+#ifdef _MSC_VER
+ #define ALIGN(n) _declspec(align(n))
+#else /* !_MSC_VER */
+ #define ALIGN(n) __attribute__((aligned(n)))
+#endif /* _MSC_VER */
+
+#include "llhttp.h"
+
+typedef int (*llhttp__internal__span_cb)(
+ llhttp__internal_t*, const char*, const char*);
+
+static const unsigned char llparse_blob0[] = {
+ 'o', 'n'
+};
+static const unsigned char llparse_blob1[] = {
+ 'e', 'c', 't', 'i', 'o', 'n'
+};
+static const unsigned char llparse_blob2[] = {
+ 'l', 'o', 's', 'e'
+};
+static const unsigned char llparse_blob3[] = {
+ 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e'
+};
+static const unsigned char llparse_blob4[] = {
+ 'p', 'g', 'r', 'a', 'd', 'e'
+};
+static const unsigned char llparse_blob5[] = {
+ 'c', 'h', 'u', 'n', 'k', 'e', 'd'
+};
+#ifdef __SSE4_2__
+static const unsigned char ALIGN(16) llparse_blob6[] = {
+ 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0
+};
+#endif /* __SSE4_2__ */
+#ifdef __SSE4_2__
+static const unsigned char ALIGN(16) llparse_blob7[] = {
+ '!', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A',
+ 'Z', '^', 'z', '|', '|'
+};
+#endif /* __SSE4_2__ */
+#ifdef __SSE4_2__
+static const unsigned char ALIGN(16) llparse_blob8[] = {
+ '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0
+};
+#endif /* __SSE4_2__ */
+static const unsigned char llparse_blob9[] = {
+ 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h'
+};
+static const unsigned char llparse_blob10[] = {
+ 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c',
+ 't', 'i', 'o', 'n'
+};
+static const unsigned char llparse_blob11[] = {
+ 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c',
+ 'o', 'd', 'i', 'n', 'g'
+};
+static const unsigned char llparse_blob12[] = {
+ 'p', 'g', 'r', 'a', 'd', 'e'
+};
+static const unsigned char llparse_blob13[] = {
+ 'T', 'T', 'P', '/'
+};
+static const unsigned char llparse_blob14[] = {
+ 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa
+};
+static const unsigned char llparse_blob15[] = {
+ 'C', 'E', '/'
+};
+static const unsigned char llparse_blob16[] = {
+ 'T', 'S', 'P', '/'
+};
+static const unsigned char llparse_blob17[] = {
+ 'N', 'O', 'U', 'N', 'C', 'E'
+};
+static const unsigned char llparse_blob18[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob19[] = {
+ 'E', 'C', 'K', 'O', 'U', 'T'
+};
+static const unsigned char llparse_blob20[] = {
+ 'N', 'E', 'C', 'T'
+};
+static const unsigned char llparse_blob21[] = {
+ 'E', 'T', 'E'
+};
+static const unsigned char llparse_blob22[] = {
+ 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob23[] = {
+ 'L', 'U', 'S', 'H'
+};
+static const unsigned char llparse_blob24[] = {
+ 'E', 'T'
+};
+static const unsigned char llparse_blob25[] = {
+ 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'
+};
+static const unsigned char llparse_blob26[] = {
+ 'E', 'A', 'D'
+};
+static const unsigned char llparse_blob27[] = {
+ 'N', 'K'
+};
+static const unsigned char llparse_blob28[] = {
+ 'C', 'K'
+};
+static const unsigned char llparse_blob29[] = {
+ 'S', 'E', 'A', 'R', 'C', 'H'
+};
+static const unsigned char llparse_blob30[] = {
+ 'R', 'G', 'E'
+};
+static const unsigned char llparse_blob31[] = {
+ 'C', 'T', 'I', 'V', 'I', 'T', 'Y'
+};
+static const unsigned char llparse_blob32[] = {
+ 'L', 'E', 'N', 'D', 'A', 'R'
+};
+static const unsigned char llparse_blob33[] = {
+ 'V', 'E'
+};
+static const unsigned char llparse_blob34[] = {
+ 'O', 'T', 'I', 'F', 'Y'
+};
+static const unsigned char llparse_blob35[] = {
+ 'P', 'T', 'I', 'O', 'N', 'S'
+};
+static const unsigned char llparse_blob36[] = {
+ 'C', 'H'
+};
+static const unsigned char llparse_blob37[] = {
+ 'S', 'E'
+};
+static const unsigned char llparse_blob38[] = {
+ 'A', 'Y'
+};
+static const unsigned char llparse_blob39[] = {
+ 'S', 'T'
+};
+static const unsigned char llparse_blob40[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob41[] = {
+ 'A', 'T', 'C', 'H'
+};
+static const unsigned char llparse_blob42[] = {
+ 'G', 'E'
+};
+static const unsigned char llparse_blob43[] = {
+ 'U', 'E', 'R', 'Y'
+};
+static const unsigned char llparse_blob44[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob45[] = {
+ 'O', 'R', 'D'
+};
+static const unsigned char llparse_blob46[] = {
+ 'I', 'R', 'E', 'C', 'T'
+};
+static const unsigned char llparse_blob47[] = {
+ 'O', 'R', 'T'
+};
+static const unsigned char llparse_blob48[] = {
+ 'R', 'C', 'H'
+};
+static const unsigned char llparse_blob49[] = {
+ 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R'
+};
+static const unsigned char llparse_blob50[] = {
+ 'U', 'R', 'C', 'E'
+};
+static const unsigned char llparse_blob51[] = {
+ 'B', 'S', 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob52[] = {
+ 'A', 'R', 'D', 'O', 'W', 'N'
+};
+static const unsigned char llparse_blob53[] = {
+ 'A', 'C', 'E'
+};
+static const unsigned char llparse_blob54[] = {
+ 'I', 'N', 'D'
+};
+static const unsigned char llparse_blob55[] = {
+ 'N', 'K'
+};
+static const unsigned char llparse_blob56[] = {
+ 'C', 'K'
+};
+static const unsigned char llparse_blob57[] = {
+ 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E'
+};
+static const unsigned char llparse_blob58[] = {
+ 'H', 'T', 'T', 'P', '/'
+};
+static const unsigned char llparse_blob59[] = {
+ 'A', 'D'
+};
+static const unsigned char llparse_blob60[] = {
+ 'T', 'P', '/'
+};
+
+enum llparse_match_status_e {
+ kMatchComplete,
+ kMatchPause,
+ kMatchMismatch
+};
+typedef enum llparse_match_status_e llparse_match_status_t;
+
+struct llparse_match_s {
+ llparse_match_status_t status;
+ const unsigned char* current;
+};
+typedef struct llparse_match_s llparse_match_t;
+
+static llparse_match_t llparse__match_sequence_to_lower(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp,
+ const unsigned char* seq, uint32_t seq_len) {
+ uint32_t index;
+ llparse_match_t res;
+
+ index = s->_index;
+ for (; p != endp; p++) {
+ unsigned char current;
+
+ current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p));
+ if (current == seq[index]) {
+ if (++index == seq_len) {
+ res.status = kMatchComplete;
+ goto reset;
+ }
+ } else {
+ res.status = kMatchMismatch;
+ goto reset;
+ }
+ }
+ s->_index = index;
+ res.status = kMatchPause;
+ res.current = p;
+ return res;
+reset:
+ s->_index = 0;
+ res.current = p;
+ return res;
+}
+
+static llparse_match_t llparse__match_sequence_to_lower_unsafe(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp,
+ const unsigned char* seq, uint32_t seq_len) {
+ uint32_t index;
+ llparse_match_t res;
+
+ index = s->_index;
+ for (; p != endp; p++) {
+ unsigned char current;
+
+ current = ((*p) | 0x20);
+ if (current == seq[index]) {
+ if (++index == seq_len) {
+ res.status = kMatchComplete;
+ goto reset;
+ }
+ } else {
+ res.status = kMatchMismatch;
+ goto reset;
+ }
+ }
+ s->_index = index;
+ res.status = kMatchPause;
+ res.current = p;
+ return res;
+reset:
+ s->_index = 0;
+ res.current = p;
+ return res;
+}
+
+static llparse_match_t llparse__match_sequence_id(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp,
+ const unsigned char* seq, uint32_t seq_len) {
+ uint32_t index;
+ llparse_match_t res;
+
+ index = s->_index;
+ for (; p != endp; p++) {
+ unsigned char current;
+
+ current = *p;
+ if (current == seq[index]) {
+ if (++index == seq_len) {
+ res.status = kMatchComplete;
+ goto reset;
+ }
+ } else {
+ res.status = kMatchMismatch;
+ goto reset;
+ }
+ }
+ s->_index = index;
+ res.status = kMatchPause;
+ res.current = p;
+ return res;
+reset:
+ s->_index = 0;
+ res.current = p;
+ return res;
+}
+
+enum llparse_state_e {
+ s_error,
+ s_n_llhttp__internal__n_closed,
+ s_n_llhttp__internal__n_invoke_llhttp__after_message_complete,
+ s_n_llhttp__internal__n_pause_1,
+ s_n_llhttp__internal__n_invoke_is_equal_upgrade,
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2,
+ s_n_llhttp__internal__n_chunk_data_almost_done_1,
+ s_n_llhttp__internal__n_chunk_data_almost_done,
+ s_n_llhttp__internal__n_consume_content_length,
+ s_n_llhttp__internal__n_span_start_llhttp__on_body,
+ s_n_llhttp__internal__n_invoke_is_equal_content_length,
+ s_n_llhttp__internal__n_chunk_size_almost_done,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_9,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_10,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1,
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_done,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2,
+ s_n_llhttp__internal__n_error_30,
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair,
+ s_n_llhttp__internal__n_error_31,
+ s_n_llhttp__internal__n_chunk_extension_quoted_value,
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3,
+ s_n_llhttp__internal__n_error_33,
+ s_n_llhttp__internal__n_chunk_extension_value,
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value,
+ s_n_llhttp__internal__n_error_34,
+ s_n_llhttp__internal__n_chunk_extension_name,
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name,
+ s_n_llhttp__internal__n_chunk_extensions,
+ s_n_llhttp__internal__n_chunk_size_otherwise,
+ s_n_llhttp__internal__n_chunk_size,
+ s_n_llhttp__internal__n_chunk_size_digit,
+ s_n_llhttp__internal__n_invoke_update_content_length_1,
+ s_n_llhttp__internal__n_consume_content_length_1,
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_1,
+ s_n_llhttp__internal__n_eof,
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_2,
+ s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete,
+ s_n_llhttp__internal__n_error_5,
+ s_n_llhttp__internal__n_headers_almost_done,
+ s_n_llhttp__internal__n_header_field_colon_discard_ws,
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete,
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value,
+ s_n_llhttp__internal__n_header_value_discard_lws,
+ s_n_llhttp__internal__n_header_value_discard_ws_almost_done,
+ s_n_llhttp__internal__n_header_value_lws,
+ s_n_llhttp__internal__n_header_value_almost_done,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_17,
+ s_n_llhttp__internal__n_header_value_lenient,
+ s_n_llhttp__internal__n_error_54,
+ s_n_llhttp__internal__n_header_value_otherwise,
+ s_n_llhttp__internal__n_header_value_connection_token,
+ s_n_llhttp__internal__n_header_value_connection_ws,
+ s_n_llhttp__internal__n_header_value_connection_1,
+ s_n_llhttp__internal__n_header_value_connection_2,
+ s_n_llhttp__internal__n_header_value_connection_3,
+ s_n_llhttp__internal__n_header_value_connection,
+ s_n_llhttp__internal__n_error_56,
+ s_n_llhttp__internal__n_error_57,
+ s_n_llhttp__internal__n_header_value_content_length_ws,
+ s_n_llhttp__internal__n_header_value_content_length,
+ s_n_llhttp__internal__n_error_59,
+ s_n_llhttp__internal__n_error_58,
+ s_n_llhttp__internal__n_header_value_te_token_ows,
+ s_n_llhttp__internal__n_header_value,
+ s_n_llhttp__internal__n_header_value_te_token,
+ s_n_llhttp__internal__n_header_value_te_chunked_last,
+ s_n_llhttp__internal__n_header_value_te_chunked,
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1,
+ s_n_llhttp__internal__n_header_value_discard_ws,
+ s_n_llhttp__internal__n_invoke_load_header_state,
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete,
+ s_n_llhttp__internal__n_header_field_general_otherwise,
+ s_n_llhttp__internal__n_header_field_general,
+ s_n_llhttp__internal__n_header_field_colon,
+ s_n_llhttp__internal__n_header_field_3,
+ s_n_llhttp__internal__n_header_field_4,
+ s_n_llhttp__internal__n_header_field_2,
+ s_n_llhttp__internal__n_header_field_1,
+ s_n_llhttp__internal__n_header_field_5,
+ s_n_llhttp__internal__n_header_field_6,
+ s_n_llhttp__internal__n_header_field_7,
+ s_n_llhttp__internal__n_header_field,
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_field,
+ s_n_llhttp__internal__n_header_field_start,
+ s_n_llhttp__internal__n_headers_start,
+ s_n_llhttp__internal__n_url_to_http_09,
+ s_n_llhttp__internal__n_url_skip_to_http09,
+ s_n_llhttp__internal__n_url_skip_lf_to_http09_1,
+ s_n_llhttp__internal__n_url_skip_lf_to_http09,
+ s_n_llhttp__internal__n_req_pri_upgrade,
+ s_n_llhttp__internal__n_req_http_complete_crlf,
+ s_n_llhttp__internal__n_req_http_complete,
+ s_n_llhttp__internal__n_invoke_load_method_1,
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete,
+ s_n_llhttp__internal__n_error_66,
+ s_n_llhttp__internal__n_error_73,
+ s_n_llhttp__internal__n_req_http_minor,
+ s_n_llhttp__internal__n_error_74,
+ s_n_llhttp__internal__n_req_http_dot,
+ s_n_llhttp__internal__n_error_75,
+ s_n_llhttp__internal__n_req_http_major,
+ s_n_llhttp__internal__n_span_start_llhttp__on_version,
+ s_n_llhttp__internal__n_req_http_start_1,
+ s_n_llhttp__internal__n_req_http_start_2,
+ s_n_llhttp__internal__n_req_http_start_3,
+ s_n_llhttp__internal__n_req_http_start,
+ s_n_llhttp__internal__n_url_to_http,
+ s_n_llhttp__internal__n_url_skip_to_http,
+ s_n_llhttp__internal__n_url_fragment,
+ s_n_llhttp__internal__n_span_end_stub_query_3,
+ s_n_llhttp__internal__n_url_query,
+ s_n_llhttp__internal__n_url_query_or_fragment,
+ s_n_llhttp__internal__n_url_path,
+ s_n_llhttp__internal__n_span_start_stub_path_2,
+ s_n_llhttp__internal__n_span_start_stub_path,
+ s_n_llhttp__internal__n_span_start_stub_path_1,
+ s_n_llhttp__internal__n_url_server_with_at,
+ s_n_llhttp__internal__n_url_server,
+ s_n_llhttp__internal__n_url_schema_delim_1,
+ s_n_llhttp__internal__n_url_schema_delim,
+ s_n_llhttp__internal__n_span_end_stub_schema,
+ s_n_llhttp__internal__n_url_schema,
+ s_n_llhttp__internal__n_url_start,
+ s_n_llhttp__internal__n_span_start_llhttp__on_url_1,
+ s_n_llhttp__internal__n_url_entry_normal,
+ s_n_llhttp__internal__n_span_start_llhttp__on_url,
+ s_n_llhttp__internal__n_url_entry_connect,
+ s_n_llhttp__internal__n_req_spaces_before_url,
+ s_n_llhttp__internal__n_req_first_space_before_url,
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1,
+ s_n_llhttp__internal__n_after_start_req_2,
+ s_n_llhttp__internal__n_after_start_req_3,
+ s_n_llhttp__internal__n_after_start_req_1,
+ s_n_llhttp__internal__n_after_start_req_4,
+ s_n_llhttp__internal__n_after_start_req_6,
+ s_n_llhttp__internal__n_after_start_req_8,
+ s_n_llhttp__internal__n_after_start_req_9,
+ s_n_llhttp__internal__n_after_start_req_7,
+ s_n_llhttp__internal__n_after_start_req_5,
+ s_n_llhttp__internal__n_after_start_req_12,
+ s_n_llhttp__internal__n_after_start_req_13,
+ s_n_llhttp__internal__n_after_start_req_11,
+ s_n_llhttp__internal__n_after_start_req_10,
+ s_n_llhttp__internal__n_after_start_req_14,
+ s_n_llhttp__internal__n_after_start_req_17,
+ s_n_llhttp__internal__n_after_start_req_16,
+ s_n_llhttp__internal__n_after_start_req_15,
+ s_n_llhttp__internal__n_after_start_req_18,
+ s_n_llhttp__internal__n_after_start_req_20,
+ s_n_llhttp__internal__n_after_start_req_21,
+ s_n_llhttp__internal__n_after_start_req_19,
+ s_n_llhttp__internal__n_after_start_req_23,
+ s_n_llhttp__internal__n_after_start_req_24,
+ s_n_llhttp__internal__n_after_start_req_26,
+ s_n_llhttp__internal__n_after_start_req_28,
+ s_n_llhttp__internal__n_after_start_req_29,
+ s_n_llhttp__internal__n_after_start_req_27,
+ s_n_llhttp__internal__n_after_start_req_25,
+ s_n_llhttp__internal__n_after_start_req_30,
+ s_n_llhttp__internal__n_after_start_req_22,
+ s_n_llhttp__internal__n_after_start_req_31,
+ s_n_llhttp__internal__n_after_start_req_32,
+ s_n_llhttp__internal__n_after_start_req_35,
+ s_n_llhttp__internal__n_after_start_req_36,
+ s_n_llhttp__internal__n_after_start_req_34,
+ s_n_llhttp__internal__n_after_start_req_37,
+ s_n_llhttp__internal__n_after_start_req_38,
+ s_n_llhttp__internal__n_after_start_req_42,
+ s_n_llhttp__internal__n_after_start_req_43,
+ s_n_llhttp__internal__n_after_start_req_41,
+ s_n_llhttp__internal__n_after_start_req_40,
+ s_n_llhttp__internal__n_after_start_req_39,
+ s_n_llhttp__internal__n_after_start_req_45,
+ s_n_llhttp__internal__n_after_start_req_44,
+ s_n_llhttp__internal__n_after_start_req_33,
+ s_n_llhttp__internal__n_after_start_req_46,
+ s_n_llhttp__internal__n_after_start_req_49,
+ s_n_llhttp__internal__n_after_start_req_50,
+ s_n_llhttp__internal__n_after_start_req_51,
+ s_n_llhttp__internal__n_after_start_req_52,
+ s_n_llhttp__internal__n_after_start_req_48,
+ s_n_llhttp__internal__n_after_start_req_47,
+ s_n_llhttp__internal__n_after_start_req_55,
+ s_n_llhttp__internal__n_after_start_req_57,
+ s_n_llhttp__internal__n_after_start_req_58,
+ s_n_llhttp__internal__n_after_start_req_56,
+ s_n_llhttp__internal__n_after_start_req_54,
+ s_n_llhttp__internal__n_after_start_req_59,
+ s_n_llhttp__internal__n_after_start_req_60,
+ s_n_llhttp__internal__n_after_start_req_53,
+ s_n_llhttp__internal__n_after_start_req_62,
+ s_n_llhttp__internal__n_after_start_req_63,
+ s_n_llhttp__internal__n_after_start_req_61,
+ s_n_llhttp__internal__n_after_start_req_66,
+ s_n_llhttp__internal__n_after_start_req_68,
+ s_n_llhttp__internal__n_after_start_req_69,
+ s_n_llhttp__internal__n_after_start_req_67,
+ s_n_llhttp__internal__n_after_start_req_70,
+ s_n_llhttp__internal__n_after_start_req_65,
+ s_n_llhttp__internal__n_after_start_req_64,
+ s_n_llhttp__internal__n_after_start_req,
+ s_n_llhttp__internal__n_span_start_llhttp__on_method_1,
+ s_n_llhttp__internal__n_res_line_almost_done,
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_30,
+ s_n_llhttp__internal__n_res_status,
+ s_n_llhttp__internal__n_span_start_llhttp__on_status,
+ s_n_llhttp__internal__n_res_status_code_otherwise,
+ s_n_llhttp__internal__n_res_status_code_digit_3,
+ s_n_llhttp__internal__n_res_status_code_digit_2,
+ s_n_llhttp__internal__n_res_status_code_digit_1,
+ s_n_llhttp__internal__n_res_after_version,
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1,
+ s_n_llhttp__internal__n_error_89,
+ s_n_llhttp__internal__n_error_103,
+ s_n_llhttp__internal__n_res_http_minor,
+ s_n_llhttp__internal__n_error_104,
+ s_n_llhttp__internal__n_res_http_dot,
+ s_n_llhttp__internal__n_error_105,
+ s_n_llhttp__internal__n_res_http_major,
+ s_n_llhttp__internal__n_span_start_llhttp__on_version_1,
+ s_n_llhttp__internal__n_start_res,
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete,
+ s_n_llhttp__internal__n_req_or_res_method_2,
+ s_n_llhttp__internal__n_invoke_update_type_1,
+ s_n_llhttp__internal__n_req_or_res_method_3,
+ s_n_llhttp__internal__n_req_or_res_method_1,
+ s_n_llhttp__internal__n_req_or_res_method,
+ s_n_llhttp__internal__n_span_start_llhttp__on_method,
+ s_n_llhttp__internal__n_start_req_or_res,
+ s_n_llhttp__internal__n_invoke_load_type,
+ s_n_llhttp__internal__n_invoke_update_finish,
+ s_n_llhttp__internal__n_start,
+};
+typedef enum llparse_state_e llparse_state_t;
+
+int llhttp__on_method(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_url(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_version(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_header_field(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_header_value(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_body(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_chunk_extension_name(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_chunk_extension_value(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_status(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_initial_message_completed(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->initial_message_completed;
+}
+
+int llhttp__on_reset(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_finish(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->finish = 2;
+ return 0;
+}
+
+int llhttp__on_message_begin(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_type(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->type;
+}
+
+int llhttp__internal__c_store_method(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->method = match;
+ return 0;
+}
+
+int llhttp__on_method_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_method(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->method == 5;
+}
+
+int llhttp__internal__c_update_http_major(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->http_major = 0;
+ return 0;
+}
+
+int llhttp__internal__c_update_http_minor(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->http_minor = 9;
+ return 0;
+}
+
+int llhttp__on_url_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_test_lenient_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 1) == 1;
+}
+
+int llhttp__internal__c_test_lenient_flags_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 256) == 256;
+}
+
+int llhttp__internal__c_test_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 128) == 128;
+}
+
+int llhttp__on_chunk_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_message_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_upgrade(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->upgrade == 1;
+}
+
+int llhttp__after_message_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_content_length(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->content_length = 0;
+ return 0;
+}
+
+int llhttp__internal__c_update_initial_message_completed(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->initial_message_completed = 1;
+ return 0;
+}
+
+int llhttp__internal__c_update_finish_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->finish = 0;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_2(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 4) == 4;
+}
+
+int llhttp__internal__c_test_lenient_flags_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 32) == 32;
+}
+
+int llhttp__before_headers_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_headers_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__after_headers_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_mul_add_content_length(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ /* Multiplication overflow */
+ if (state->content_length > 0xffffffffffffffffULL / 16) {
+ return 1;
+ }
+
+ state->content_length *= 16;
+
+ /* Addition overflow */
+ if (match >= 0) {
+ if (state->content_length > 0xffffffffffffffffULL - match) {
+ return 1;
+ }
+ } else {
+ if (state->content_length < 0ULL - match) {
+ return 1;
+ }
+ }
+ state->content_length += match;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_4(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 512) == 512;
+}
+
+int llhttp__on_chunk_header(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_is_equal_content_length(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->content_length == 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_7(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 128) == 128;
+}
+
+int llhttp__internal__c_or_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 128;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_8(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 64) == 64;
+}
+
+int llhttp__on_chunk_extension_name_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__on_chunk_extension_value_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_finish_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->finish = 1;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 64;
+ return 0;
+}
+
+int llhttp__internal__c_update_upgrade(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->upgrade = 1;
+ return 0;
+}
+
+int llhttp__internal__c_store_header_state(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->header_state = match;
+ return 0;
+}
+
+int llhttp__on_header_field_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_header_state(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->header_state;
+}
+
+int llhttp__internal__c_test_flags_4(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 512) == 512;
+}
+
+int llhttp__internal__c_test_lenient_flags_22(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 2) == 2;
+}
+
+int llhttp__internal__c_or_flags_5(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 1;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 1;
+ return 0;
+}
+
+int llhttp__on_header_value_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_or_flags_6(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 2;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_7(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 4;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_8(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 8;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 6;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 0;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_6(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 5;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_7(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 7;
+ return 0;
+}
+
+int llhttp__internal__c_test_flags_2(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 32) == 32;
+}
+
+int llhttp__internal__c_mul_add_content_length_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ /* Multiplication overflow */
+ if (state->content_length > 0xffffffffffffffffULL / 10) {
+ return 1;
+ }
+
+ state->content_length *= 10;
+
+ /* Addition overflow */
+ if (match >= 0) {
+ if (state->content_length > 0xffffffffffffffffULL - match) {
+ return 1;
+ }
+ } else {
+ if (state->content_length < 0ULL - match) {
+ return 1;
+ }
+ }
+ state->content_length += match;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_17(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 32;
+ return 0;
+}
+
+int llhttp__internal__c_test_flags_3(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->flags & 8) == 8;
+}
+
+int llhttp__internal__c_test_lenient_flags_20(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 8) == 8;
+}
+
+int llhttp__internal__c_or_flags_18(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 512;
+ return 0;
+}
+
+int llhttp__internal__c_and_flags(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags &= -9;
+ return 0;
+}
+
+int llhttp__internal__c_update_header_state_8(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->header_state = 8;
+ return 0;
+}
+
+int llhttp__internal__c_or_flags_20(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->flags |= 16;
+ return 0;
+}
+
+int llhttp__internal__c_load_method(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->method;
+}
+
+int llhttp__internal__c_store_http_major(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->http_major = match;
+ return 0;
+}
+
+int llhttp__internal__c_store_http_minor(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ state->http_minor = match;
+ return 0;
+}
+
+int llhttp__internal__c_test_lenient_flags_24(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return (state->lenient_flags & 16) == 16;
+}
+
+int llhttp__on_version_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_load_http_major(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->http_major;
+}
+
+int llhttp__internal__c_load_http_minor(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ return state->http_minor;
+}
+
+int llhttp__internal__c_update_status_code(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->status_code = 0;
+ return 0;
+}
+
+int llhttp__internal__c_mul_add_status_code(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp,
+ int match) {
+ /* Multiplication overflow */
+ if (state->status_code > 0xffff / 10) {
+ return 1;
+ }
+
+ state->status_code *= 10;
+
+ /* Addition overflow */
+ if (match >= 0) {
+ if (state->status_code > 0xffff - match) {
+ return 1;
+ }
+ } else {
+ if (state->status_code < 0 - match) {
+ return 1;
+ }
+ }
+ state->status_code += match;
+ return 0;
+}
+
+int llhttp__on_status_complete(
+ llhttp__internal_t* s, const unsigned char* p,
+ const unsigned char* endp);
+
+int llhttp__internal__c_update_type(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->type = 1;
+ return 0;
+}
+
+int llhttp__internal__c_update_type_1(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ state->type = 2;
+ return 0;
+}
+
+int llhttp__internal_init(llhttp__internal_t* state) {
+ memset(state, 0, sizeof(*state));
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start;
+ return 0;
+}
+
+static llparse_state_t llhttp__internal__run(
+ llhttp__internal_t* state,
+ const unsigned char* p,
+ const unsigned char* endp) {
+ int match;
+ switch ((llparse_state_t) (intptr_t) state->_current) {
+ case s_n_llhttp__internal__n_closed:
+ s_n_llhttp__internal__n_closed: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_closed;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_closed;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_closed;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: {
+ switch (llhttp__after_message_complete(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_update_content_length;
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_finish_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_pause_1:
+ s_n_llhttp__internal__n_pause_1: {
+ state->error = 0x16;
+ state->reason = "Pause on CONNECT/Upgrade";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_is_equal_upgrade:
+ s_n_llhttp__internal__n_invoke_is_equal_upgrade: {
+ switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ default:
+ goto s_n_llhttp__internal__n_pause_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2:
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: {
+ switch (llhttp__on_message_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_is_equal_upgrade;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_13;
+ default:
+ goto s_n_llhttp__internal__n_error_38;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_data_almost_done_1:
+ s_n_llhttp__internal__n_chunk_data_almost_done_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_data_almost_done_1;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_data_almost_done:
+ s_n_llhttp__internal__n_chunk_data_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_data_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_data_almost_done_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_consume_content_length:
+ s_n_llhttp__internal__n_consume_content_length: {
+ size_t avail;
+ uint64_t need;
+
+ avail = endp - p;
+ need = state->content_length;
+ if (avail >= need) {
+ p += need;
+ state->content_length = 0;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_body;
+ }
+
+ state->content_length -= avail;
+ return s_n_llhttp__internal__n_consume_content_length;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_body:
+ s_n_llhttp__internal__n_span_start_llhttp__on_body: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_body;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_body;
+ goto s_n_llhttp__internal__n_consume_content_length;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_is_equal_content_length:
+ s_n_llhttp__internal__n_invoke_is_equal_content_length: {
+ switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_body;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size_almost_done:
+ s_n_llhttp__internal__n_chunk_size_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_9:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_9: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_20;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_5;
+ default:
+ goto s_n_llhttp__internal__n_error_19;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_6;
+ default:
+ goto s_n_llhttp__internal__n_error_21;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_7;
+ default:
+ goto s_n_llhttp__internal__n_error_22;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_10:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_10: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_25;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_8;
+ default:
+ goto s_n_llhttp__internal__n_error_24;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_9;
+ default:
+ goto s_n_llhttp__internal__n_error_26;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_quoted_value_done:
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_quoted_value_done;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+ case ';': {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_29;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_10;
+ default:
+ goto s_n_llhttp__internal__n_error_27;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_30:
+ s_n_llhttp__internal__n_error_30: {
+ state->error = 0x2;
+ state->reason = "Invalid quoted-pair in chunk extensions quoted value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair:
+ s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_31:
+ s_n_llhttp__internal__n_error_31: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions quoted value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_quoted_value:
+ s_n_llhttp__internal__n_chunk_extension_quoted_value: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3:
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: {
+ switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_11;
+ default:
+ goto s_n_llhttp__internal__n_error_32;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_33:
+ s_n_llhttp__internal__n_error_33: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_value:
+ s_n_llhttp__internal__n_chunk_extension_value: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_value;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_value;
+ }
+ case 4: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_quoted_value;
+ }
+ case 5: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value:
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_chunk_extension_value;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_34:
+ s_n_llhttp__internal__n_error_34: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions name";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extension_name:
+ s_n_llhttp__internal__n_chunk_extension_name: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extension_name;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extension_name;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2;
+ }
+ case 5: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name:
+ s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_chunk_extension_name;
+ goto s_n_llhttp__internal__n_chunk_extension_name;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_extensions:
+ s_n_llhttp__internal__n_chunk_extensions: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_extensions;
+ }
+ switch (*p) {
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_error_17;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_error_18;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size_otherwise:
+ s_n_llhttp__internal__n_chunk_size_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size_otherwise;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;
+ }
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4;
+ }
+ case ';': {
+ p++;
+ goto s_n_llhttp__internal__n_chunk_extensions;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_35;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size:
+ s_n_llhttp__internal__n_chunk_size: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'A': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'B': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'C': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'D': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'E': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'F': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'a': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'b': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'c': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'd': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'e': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'f': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_chunk_size_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_chunk_size_digit:
+ s_n_llhttp__internal__n_chunk_size_digit: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_chunk_size_digit;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'A': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'B': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'C': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'D': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'E': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'F': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'a': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'b': {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'c': {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'd': {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'e': {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ case 'f': {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_37;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_update_content_length_1:
+ s_n_llhttp__internal__n_invoke_update_content_length_1: {
+ switch (llhttp__internal__c_update_content_length(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_chunk_size_digit;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_consume_content_length_1:
+ s_n_llhttp__internal__n_consume_content_length_1: {
+ size_t avail;
+ uint64_t need;
+
+ avail = endp - p;
+ need = state->content_length;
+ if (avail >= need) {
+ p += need;
+ state->content_length = 0;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1;
+ }
+
+ state->content_length -= avail;
+ return s_n_llhttp__internal__n_consume_content_length_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_body_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_body_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_body;
+ goto s_n_llhttp__internal__n_consume_content_length_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_eof:
+ s_n_llhttp__internal__n_eof: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_eof;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_eof;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_body_2:
+ s_n_llhttp__internal__n_span_start_llhttp__on_body_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_body_2;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_body;
+ goto s_n_llhttp__internal__n_eof;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: {
+ switch (llhttp__after_headers_complete(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_update_content_length_1;
+ case 3:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1;
+ case 4:
+ goto s_n_llhttp__internal__n_invoke_update_finish_3;
+ case 5:
+ goto s_n_llhttp__internal__n_error_39;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_5:
+ s_n_llhttp__internal__n_error_5: {
+ state->error = 0xa;
+ state->reason = "Invalid header field char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_headers_almost_done:
+ s_n_llhttp__internal__n_headers_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_headers_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_flags_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_colon_discard_ws:
+ s_n_llhttp__internal__n_header_field_colon_discard_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_colon_discard_ws;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_colon_discard_ws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_field_colon;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: {
+ switch (llhttp__on_header_value_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_header_field_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_18;
+ default:
+ goto s_n_llhttp__internal__n_error_48;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_header_value:
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_header_value;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_discard_lws:
+ s_n_llhttp__internal__n_header_value_discard_lws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_discard_lws;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_load_header_state_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_discard_ws_almost_done:
+ s_n_llhttp__internal__n_header_value_discard_ws_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_discard_ws_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_lws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_lws:
+ s_n_llhttp__internal__n_header_value_lws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_lws;
+ }
+ switch (*p) {
+ case 9: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;
+ }
+ case ' ': {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_load_header_state_5;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_almost_done:
+ s_n_llhttp__internal__n_header_value_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_lws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_53;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_17:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_17: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_51;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_lenient:
+ s_n_llhttp__internal__n_header_value_lenient: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_lenient;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_lenient;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_54:
+ s_n_llhttp__internal__n_error_54: {
+ state->error = 0xa;
+ state->reason = "Invalid header value char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_otherwise:
+ s_n_llhttp__internal__n_header_value_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_token:
+ s_n_llhttp__internal__n_header_value_connection_token: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_ws:
+ s_n_llhttp__internal__n_header_value_connection_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ case ',': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_header_state_6;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_5;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_1:
+ s_n_llhttp__internal__n_header_value_connection_1: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_1;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_header_state_3;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_connection_1;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_2:
+ s_n_llhttp__internal__n_header_value_connection_2: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_2;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_header_state_6;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_connection_2;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection_3:
+ s_n_llhttp__internal__n_header_value_connection_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection_3;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_header_state_7;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_connection_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_connection:
+ s_n_llhttp__internal__n_header_value_connection: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_connection;
+ }
+ switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ case 'c': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_1;
+ }
+ case 'k': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_2;
+ }
+ case 'u': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_connection_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_56:
+ s_n_llhttp__internal__n_error_56: {
+ state->error = 0xb;
+ state->reason = "Content-Length overflow";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_57:
+ s_n_llhttp__internal__n_error_57: {
+ state->error = 0xb;
+ state->reason = "Invalid character in Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_content_length_ws:
+ s_n_llhttp__internal__n_header_value_content_length_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_content_length_ws;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_invoke_or_flags_17;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_invoke_or_flags_17;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_content_length_ws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_content_length:
+ s_n_llhttp__internal__n_header_value_content_length: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_content_length;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_content_length_ws;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_59:
+ s_n_llhttp__internal__n_error_59: {
+ state->error = 0xf;
+ state->reason = "Invalid `Transfer-Encoding` header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_58:
+ s_n_llhttp__internal__n_error_58: {
+ state->error = 0xf;
+ state->reason = "Invalid `Transfer-Encoding` header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_token_ows:
+ s_n_llhttp__internal__n_header_value_te_token_ows: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value:
+ s_n_llhttp__internal__n_header_value: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value;
+ }
+ #ifdef __SSE4_2__
+ if (endp - p >= 16) {
+ __m128i ranges;
+ __m128i input;
+ int avail;
+ int match_len;
+
+ /* Load input */
+ input = _mm_loadu_si128((__m128i const*) p);
+ ranges = _mm_loadu_si128((__m128i const*) llparse_blob6);
+
+ /* Find first character that does not match `ranges` */
+ match_len = _mm_cmpestri(ranges, 6,
+ input, 16,
+ _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
+ _SIDD_NEGATIVE_POLARITY);
+
+ if (match_len != 0) {
+ p += match_len;
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ #endif /* __SSE4_2__ */
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_token:
+ s_n_llhttp__internal__n_header_value_te_token: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_token;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_token_ows;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_9;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_chunked_last:
+ s_n_llhttp__internal__n_header_value_te_chunked_last: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_chunked_last;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_8;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_8;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_chunked_last;
+ }
+ case ',': {
+ goto s_n_llhttp__internal__n_invoke_load_type_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_value_te_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_te_chunked:
+ s_n_llhttp__internal__n_header_value_te_chunked: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_te_chunked_last;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_header_value_te_token;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_header_value;
+ goto s_n_llhttp__internal__n_invoke_load_header_state_3;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_value_discard_ws:
+ s_n_llhttp__internal__n_header_value_discard_ws: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_load_header_state:
+ s_n_llhttp__internal__n_invoke_load_header_state: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_test_flags_4;
+ case 3:
+ goto s_n_llhttp__internal__n_invoke_test_flags_5;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: {
+ switch (llhttp__on_header_field_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_header_state;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_19;
+ default:
+ goto s_n_llhttp__internal__n_error_45;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_general_otherwise:
+ s_n_llhttp__internal__n_header_field_general_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_general_otherwise;
+ }
+ switch (*p) {
+ case ':': {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_62;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_general:
+ s_n_llhttp__internal__n_header_field_general: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_general;
+ }
+ #ifdef __SSE4_2__
+ if (endp - p >= 16) {
+ __m128i ranges;
+ __m128i input;
+ int avail;
+ int match_len;
+
+ /* Load input */
+ input = _mm_loadu_si128((__m128i const*) p);
+ ranges = _mm_loadu_si128((__m128i const*) llparse_blob7);
+
+ /* Find first character that does not match `ranges` */
+ match_len = _mm_cmpestri(ranges, 16,
+ input, 16,
+ _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
+ _SIDD_NEGATIVE_POLARITY);
+
+ if (match_len != 0) {
+ p += match_len;
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ ranges = _mm_loadu_si128((__m128i const*) llparse_blob8);
+
+ /* Find first character that does not match `ranges` */
+ match_len = _mm_cmpestri(ranges, 2,
+ input, 16,
+ _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
+ _SIDD_NEGATIVE_POLARITY);
+
+ if (match_len != 0) {
+ p += match_len;
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ goto s_n_llhttp__internal__n_header_field_general_otherwise;
+ }
+ #endif /* __SSE4_2__ */
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_field_general_otherwise;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_colon:
+ s_n_llhttp__internal__n_header_field_colon: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_colon;
+ }
+ switch (*p) {
+ case ' ': {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13;
+ }
+ case ':': {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_10;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_3:
+ s_n_llhttp__internal__n_header_field_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_3;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_4:
+ s_n_llhttp__internal__n_header_field_4: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_4;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_4;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_2:
+ s_n_llhttp__internal__n_header_field_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_2;
+ }
+ switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {
+ case 'n': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_3;
+ }
+ case 't': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_4;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_1:
+ s_n_llhttp__internal__n_header_field_1: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_1;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_2;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_1;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_5:
+ s_n_llhttp__internal__n_header_field_5: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_5;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_5;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_6:
+ s_n_llhttp__internal__n_header_field_6: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_6;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_6;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_7:
+ s_n_llhttp__internal__n_header_field_7: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_7;
+ }
+ match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_header_state;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_header_field_7;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field:
+ s_n_llhttp__internal__n_header_field: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field;
+ }
+ switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) {
+ case 'c': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_1;
+ }
+ case 'p': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_5;
+ }
+ case 't': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_6;
+ }
+ case 'u': {
+ p++;
+ goto s_n_llhttp__internal__n_header_field_7;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_header_state_11;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_header_field:
+ s_n_llhttp__internal__n_span_start_llhttp__on_header_field: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_header_field;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_header_field;
+ goto s_n_llhttp__internal__n_header_field;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_header_field_start:
+ s_n_llhttp__internal__n_header_field_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_header_field_start;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_headers_almost_done;
+ }
+ case ':': {
+ goto s_n_llhttp__internal__n_error_44;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_headers_start:
+ s_n_llhttp__internal__n_headers_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_headers_start;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_header_field_start;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_to_http_09:
+ s_n_llhttp__internal__n_url_to_http_09: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_to_http_09;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_http_major;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_to_http09:
+ s_n_llhttp__internal__n_url_skip_to_http09: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_to_http09;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_url_to_http_09;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_lf_to_http09_1:
+ s_n_llhttp__internal__n_url_skip_lf_to_http09_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_lf_to_http09_1;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_url_to_http_09;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_63;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_lf_to_http09:
+ s_n_llhttp__internal__n_url_skip_lf_to_http09: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_63;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_pri_upgrade:
+ s_n_llhttp__internal__n_req_pri_upgrade: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_pri_upgrade;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_error_71;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_pri_upgrade;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_72;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_complete_crlf:
+ s_n_llhttp__internal__n_req_http_complete_crlf: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_complete_crlf;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_headers_start;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_complete:
+ s_n_llhttp__internal__n_req_http_complete: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_complete;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_complete_crlf;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_70;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_load_method_1:
+ s_n_llhttp__internal__n_invoke_load_method_1: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 34:
+ goto s_n_llhttp__internal__n_req_pri_upgrade;
+ default:
+ goto s_n_llhttp__internal__n_req_http_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: {
+ switch (llhttp__on_version_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_method_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_21;
+ default:
+ goto s_n_llhttp__internal__n_error_67;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_66:
+ s_n_llhttp__internal__n_error_66: {
+ state->error = 0x9;
+ state->reason = "Invalid HTTP version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_73:
+ s_n_llhttp__internal__n_error_73: {
+ state->error = 0x9;
+ state->reason = "Invalid minor version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_minor:
+ s_n_llhttp__internal__n_req_http_minor: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_minor;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_74:
+ s_n_llhttp__internal__n_error_74: {
+ state->error = 0x9;
+ state->reason = "Expected dot";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_dot:
+ s_n_llhttp__internal__n_req_http_dot: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_dot;
+ }
+ switch (*p) {
+ case '.': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_minor;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_75:
+ s_n_llhttp__internal__n_error_75: {
+ state->error = 0x9;
+ state->reason = "Invalid major version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_major:
+ s_n_llhttp__internal__n_req_http_major: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_major;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_major;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_version:
+ s_n_llhttp__internal__n_span_start_llhttp__on_version: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_version;
+ goto s_n_llhttp__internal__n_req_http_major;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start_1:
+ s_n_llhttp__internal__n_req_http_start_1: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start_1;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_method;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_http_start_1;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start_2:
+ s_n_llhttp__internal__n_req_http_start_2: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start_2;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_method_2;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_http_start_2;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start_3:
+ s_n_llhttp__internal__n_req_http_start_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start_3;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_load_method_3;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_http_start_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_http_start:
+ s_n_llhttp__internal__n_req_http_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_http_start;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start;
+ }
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start_1;
+ }
+ case 'I': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start_2;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_req_http_start_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_78;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_to_http:
+ s_n_llhttp__internal__n_url_to_http: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_to_http;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_skip_to_http:
+ s_n_llhttp__internal__n_url_skip_to_http: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_skip_to_http;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_url_to_http;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_fragment:
+ s_n_llhttp__internal__n_url_fragment: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_fragment;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_fragment;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_79;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_end_stub_query_3:
+ s_n_llhttp__internal__n_span_end_stub_query_3: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_end_stub_query_3;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_fragment;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_query:
+ s_n_llhttp__internal__n_url_query: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_query;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ case 6: {
+ goto s_n_llhttp__internal__n_span_end_stub_query_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_80;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_query_or_fragment:
+ s_n_llhttp__internal__n_url_query_or_fragment: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_query_or_fragment;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4;
+ }
+ case ' ': {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5;
+ }
+ case '#': {
+ p++;
+ goto s_n_llhttp__internal__n_url_fragment;
+ }
+ case '?': {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_81;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_path:
+ s_n_llhttp__internal__n_url_path: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_path;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_url_query_or_fragment;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_stub_path_2:
+ s_n_llhttp__internal__n_span_start_stub_path_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_stub_path_2;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_stub_path:
+ s_n_llhttp__internal__n_span_start_stub_path: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_stub_path;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_stub_path_1:
+ s_n_llhttp__internal__n_span_start_stub_path_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_stub_path_1;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_path;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_server_with_at:
+ s_n_llhttp__internal__n_url_server_with_at: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,
+ 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_server_with_at;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_server;
+ }
+ case 6: {
+ goto s_n_llhttp__internal__n_span_start_stub_path_1;
+ }
+ case 7: {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ case 8: {
+ p++;
+ goto s_n_llhttp__internal__n_error_82;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_83;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_server:
+ s_n_llhttp__internal__n_url_server: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7,
+ 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_server;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1;
+ }
+ case 4: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2;
+ }
+ case 5: {
+ p++;
+ goto s_n_llhttp__internal__n_url_server;
+ }
+ case 6: {
+ goto s_n_llhttp__internal__n_span_start_stub_path;
+ }
+ case 7: {
+ p++;
+ goto s_n_llhttp__internal__n_url_query;
+ }
+ case 8: {
+ p++;
+ goto s_n_llhttp__internal__n_url_server_with_at;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_84;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_schema_delim_1:
+ s_n_llhttp__internal__n_url_schema_delim_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_schema_delim_1;
+ }
+ switch (*p) {
+ case '/': {
+ p++;
+ goto s_n_llhttp__internal__n_url_server;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_85;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_schema_delim:
+ s_n_llhttp__internal__n_url_schema_delim: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_schema_delim;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case '/': {
+ p++;
+ goto s_n_llhttp__internal__n_url_schema_delim_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_85;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_end_stub_schema:
+ s_n_llhttp__internal__n_span_end_stub_schema: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_end_stub_schema;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_url_schema_delim;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_schema:
+ s_n_llhttp__internal__n_url_schema: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_schema;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_end_stub_schema;
+ }
+ case 3: {
+ p++;
+ goto s_n_llhttp__internal__n_url_schema;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_86;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_start:
+ s_n_llhttp__internal__n_url_start: {
+ static uint8_t lookup_table[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_start;
+ }
+ switch (lookup_table[(uint8_t) *p]) {
+ case 1: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 2: {
+ goto s_n_llhttp__internal__n_span_start_stub_path_2;
+ }
+ case 3: {
+ goto s_n_llhttp__internal__n_url_schema;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_87;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_url_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_url_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_url_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_url;
+ goto s_n_llhttp__internal__n_url_start;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_entry_normal:
+ s_n_llhttp__internal__n_url_entry_normal: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_entry_normal;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_url:
+ s_n_llhttp__internal__n_span_start_llhttp__on_url: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_url;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_url;
+ goto s_n_llhttp__internal__n_url_server;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_url_entry_connect:
+ s_n_llhttp__internal__n_url_entry_connect: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_url_entry_connect;
+ }
+ switch (*p) {
+ case 9: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ case 12: {
+ p++;
+ goto s_n_llhttp__internal__n_error_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_url;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_spaces_before_url:
+ s_n_llhttp__internal__n_req_spaces_before_url: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_spaces_before_url;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_req_spaces_before_url;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_is_equal_method;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_first_space_before_url:
+ s_n_llhttp__internal__n_req_first_space_before_url: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_first_space_before_url;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_req_spaces_before_url;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_88;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: {
+ switch (llhttp__on_method_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_req_first_space_before_url;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_26;
+ default:
+ goto s_n_llhttp__internal__n_error_107;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_2:
+ s_n_llhttp__internal__n_after_start_req_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_2;
+ }
+ switch (*p) {
+ case 'L': {
+ p++;
+ match = 19;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_3:
+ s_n_llhttp__internal__n_after_start_req_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_3;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 36;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_1:
+ s_n_llhttp__internal__n_after_start_req_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_1;
+ }
+ switch (*p) {
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_2;
+ }
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_4:
+ s_n_llhttp__internal__n_after_start_req_4: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_4;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 16;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_4;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_6:
+ s_n_llhttp__internal__n_after_start_req_6: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_6;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 22;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_6;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_8:
+ s_n_llhttp__internal__n_after_start_req_8: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_8;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_8;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_9:
+ s_n_llhttp__internal__n_after_start_req_9: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_9;
+ }
+ switch (*p) {
+ case 'Y': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_7:
+ s_n_llhttp__internal__n_after_start_req_7: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_7;
+ }
+ switch (*p) {
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_8;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_9;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_5:
+ s_n_llhttp__internal__n_after_start_req_5: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_5;
+ }
+ switch (*p) {
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_6;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_7;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_12:
+ s_n_llhttp__internal__n_after_start_req_12: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_12;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_12;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_13:
+ s_n_llhttp__internal__n_after_start_req_13: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_13;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 35;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_13;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_11:
+ s_n_llhttp__internal__n_after_start_req_11: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_11;
+ }
+ switch (*p) {
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_12;
+ }
+ case 'S': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_13;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_10:
+ s_n_llhttp__internal__n_after_start_req_10: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_10;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_11;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_14:
+ s_n_llhttp__internal__n_after_start_req_14: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_14;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 45;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_14;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_17:
+ s_n_llhttp__internal__n_after_start_req_17: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_17;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 41;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_17;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_16:
+ s_n_llhttp__internal__n_after_start_req_16: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_16;
+ }
+ switch (*p) {
+ case '_': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_17;
+ }
+ default: {
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_15:
+ s_n_llhttp__internal__n_after_start_req_15: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_15;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_16;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_15;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_18:
+ s_n_llhttp__internal__n_after_start_req_18: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_18;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_18;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_20:
+ s_n_llhttp__internal__n_after_start_req_20: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_20;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 31;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_20;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_21:
+ s_n_llhttp__internal__n_after_start_req_21: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_21;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_21;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_19:
+ s_n_llhttp__internal__n_after_start_req_19: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_19;
+ }
+ switch (*p) {
+ case 'I': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_20;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_21;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_23:
+ s_n_llhttp__internal__n_after_start_req_23: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_23;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 24;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_23;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_24:
+ s_n_llhttp__internal__n_after_start_req_24: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_24;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 23;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_24;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_26:
+ s_n_llhttp__internal__n_after_start_req_26: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_26;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 21;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_26;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_28:
+ s_n_llhttp__internal__n_after_start_req_28: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_28;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 30;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_28;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_29:
+ s_n_llhttp__internal__n_after_start_req_29: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_29;
+ }
+ switch (*p) {
+ case 'L': {
+ p++;
+ match = 10;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_27:
+ s_n_llhttp__internal__n_after_start_req_27: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_27;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_28;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_29;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_25:
+ s_n_llhttp__internal__n_after_start_req_25: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_25;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_26;
+ }
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_27;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_30:
+ s_n_llhttp__internal__n_after_start_req_30: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_30;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 11;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_30;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_22:
+ s_n_llhttp__internal__n_after_start_req_22: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_22;
+ }
+ switch (*p) {
+ case '-': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_23;
+ }
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_24;
+ }
+ case 'K': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_25;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_30;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_31:
+ s_n_llhttp__internal__n_after_start_req_31: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_31;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 25;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_31;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_32:
+ s_n_llhttp__internal__n_after_start_req_32: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_32;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_32;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_35:
+ s_n_llhttp__internal__n_after_start_req_35: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_35;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 28;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_35;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_36:
+ s_n_llhttp__internal__n_after_start_req_36: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_36;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 39;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_36;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_34:
+ s_n_llhttp__internal__n_after_start_req_34: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_34;
+ }
+ switch (*p) {
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_35;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_36;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_37:
+ s_n_llhttp__internal__n_after_start_req_37: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_37;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 38;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_37;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_38:
+ s_n_llhttp__internal__n_after_start_req_38: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_38;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_38;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_42:
+ s_n_llhttp__internal__n_after_start_req_42: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_42;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 12;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_42;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_43:
+ s_n_llhttp__internal__n_after_start_req_43: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_43;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 13;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_43;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_41:
+ s_n_llhttp__internal__n_after_start_req_41: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_41;
+ }
+ switch (*p) {
+ case 'F': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_42;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_43;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_40:
+ s_n_llhttp__internal__n_after_start_req_40: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_40;
+ }
+ switch (*p) {
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_41;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_39:
+ s_n_llhttp__internal__n_after_start_req_39: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_39;
+ }
+ switch (*p) {
+ case 'I': {
+ p++;
+ match = 34;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_40;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_45:
+ s_n_llhttp__internal__n_after_start_req_45: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_45;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 29;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_45;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_44:
+ s_n_llhttp__internal__n_after_start_req_44: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_44;
+ }
+ switch (*p) {
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_45;
+ }
+ case 'T': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_33:
+ s_n_llhttp__internal__n_after_start_req_33: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_33;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_34;
+ }
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_37;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_38;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_39;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_44;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_46:
+ s_n_llhttp__internal__n_after_start_req_46: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_46;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 46;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_46;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_49:
+ s_n_llhttp__internal__n_after_start_req_49: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_49;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 17;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_49;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_50:
+ s_n_llhttp__internal__n_after_start_req_50: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_50;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 44;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_50;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_51:
+ s_n_llhttp__internal__n_after_start_req_51: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_51;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 43;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_51;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_52:
+ s_n_llhttp__internal__n_after_start_req_52: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_52;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 20;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_52;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_48:
+ s_n_llhttp__internal__n_after_start_req_48: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_48;
+ }
+ switch (*p) {
+ case 'B': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_49;
+ }
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_50;
+ }
+ case 'D': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_51;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_52;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_47:
+ s_n_llhttp__internal__n_after_start_req_47: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_47;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_48;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_55:
+ s_n_llhttp__internal__n_after_start_req_55: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_55;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 14;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_55;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_57:
+ s_n_llhttp__internal__n_after_start_req_57: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_57;
+ }
+ switch (*p) {
+ case 'P': {
+ p++;
+ match = 37;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_58:
+ s_n_llhttp__internal__n_after_start_req_58: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_58;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 42;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_58;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_56:
+ s_n_llhttp__internal__n_after_start_req_56: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_56;
+ }
+ switch (*p) {
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_57;
+ }
+ case '_': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_58;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_54:
+ s_n_llhttp__internal__n_after_start_req_54: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_54;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_55;
+ }
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_56;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_59:
+ s_n_llhttp__internal__n_after_start_req_59: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_59;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 33;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_59;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_60:
+ s_n_llhttp__internal__n_after_start_req_60: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_60;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 26;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_60;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_53:
+ s_n_llhttp__internal__n_after_start_req_53: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_53;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_54;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_59;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_60;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_62:
+ s_n_llhttp__internal__n_after_start_req_62: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_62;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 40;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_62;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_63:
+ s_n_llhttp__internal__n_after_start_req_63: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_63;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_63;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_61:
+ s_n_llhttp__internal__n_after_start_req_61: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_61;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_62;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_63;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_66:
+ s_n_llhttp__internal__n_after_start_req_66: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_66;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 18;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_66;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_68:
+ s_n_llhttp__internal__n_after_start_req_68: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_68;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 32;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_68;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_69:
+ s_n_llhttp__internal__n_after_start_req_69: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_69;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 15;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_69;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_67:
+ s_n_llhttp__internal__n_after_start_req_67: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_67;
+ }
+ switch (*p) {
+ case 'I': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_68;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_69;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_70:
+ s_n_llhttp__internal__n_after_start_req_70: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_70;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 27;
+ goto s_n_llhttp__internal__n_invoke_store_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_after_start_req_70;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_65:
+ s_n_llhttp__internal__n_after_start_req_65: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_65;
+ }
+ switch (*p) {
+ case 'B': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_66;
+ }
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_67;
+ }
+ case 'S': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_70;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req_64:
+ s_n_llhttp__internal__n_after_start_req_64: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req_64;
+ }
+ switch (*p) {
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_65;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_after_start_req:
+ s_n_llhttp__internal__n_after_start_req: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_after_start_req;
+ }
+ switch (*p) {
+ case 'A': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_1;
+ }
+ case 'B': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_4;
+ }
+ case 'C': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_5;
+ }
+ case 'D': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_10;
+ }
+ case 'F': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_14;
+ }
+ case 'G': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_15;
+ }
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_18;
+ }
+ case 'L': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_19;
+ }
+ case 'M': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_22;
+ }
+ case 'N': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_31;
+ }
+ case 'O': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_32;
+ }
+ case 'P': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_33;
+ }
+ case 'Q': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_46;
+ }
+ case 'R': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_47;
+ }
+ case 'S': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_53;
+ }
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_61;
+ }
+ case 'U': {
+ p++;
+ goto s_n_llhttp__internal__n_after_start_req_64;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_108;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_method_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_method_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_method_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_method;
+ goto s_n_llhttp__internal__n_after_start_req;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_line_almost_done:
+ s_n_llhttp__internal__n_res_line_almost_done: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_line_almost_done;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_test_lenient_flags_30:
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_30: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_94;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status:
+ s_n_llhttp__internal__n_res_status: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status;
+ }
+ switch (*p) {
+ case 10: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_status;
+ }
+ case 13: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1;
+ }
+ default: {
+ p++;
+ goto s_n_llhttp__internal__n_res_status;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_status:
+ s_n_llhttp__internal__n_span_start_llhttp__on_status: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_status;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_status;
+ goto s_n_llhttp__internal__n_res_status;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_otherwise:
+ s_n_llhttp__internal__n_res_status_code_otherwise: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_otherwise;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_res_line_almost_done;
+ }
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_status;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_95;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_digit_3:
+ s_n_llhttp__internal__n_res_status_code_digit_3: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_digit_3;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_97;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_digit_2:
+ s_n_llhttp__internal__n_res_status_code_digit_2: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_digit_2;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_99;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_status_code_digit_1:
+ s_n_llhttp__internal__n_res_status_code_digit_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_status_code_digit_1;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_mul_add_status_code;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_101;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_after_version:
+ s_n_llhttp__internal__n_res_after_version: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_after_version;
+ }
+ switch (*p) {
+ case ' ': {
+ p++;
+ goto s_n_llhttp__internal__n_invoke_update_status_code;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_102;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1:
+ s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: {
+ switch (llhttp__on_version_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_res_after_version;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_25;
+ default:
+ goto s_n_llhttp__internal__n_error_90;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_89:
+ s_n_llhttp__internal__n_error_89: {
+ state->error = 0x9;
+ state->reason = "Invalid HTTP version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_103:
+ s_n_llhttp__internal__n_error_103: {
+ state->error = 0x9;
+ state->reason = "Invalid minor version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_http_minor:
+ s_n_llhttp__internal__n_res_http_minor: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_http_minor;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_minor_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_104:
+ s_n_llhttp__internal__n_error_104: {
+ state->error = 0x9;
+ state->reason = "Expected dot";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_http_dot:
+ s_n_llhttp__internal__n_res_http_dot: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_http_dot;
+ }
+ switch (*p) {
+ case '.': {
+ p++;
+ goto s_n_llhttp__internal__n_res_http_minor;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_error_105:
+ s_n_llhttp__internal__n_error_105: {
+ state->error = 0x9;
+ state->reason = "Invalid major version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_res_http_major:
+ s_n_llhttp__internal__n_res_http_major: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_res_http_major;
+ }
+ switch (*p) {
+ case '0': {
+ p++;
+ match = 0;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '1': {
+ p++;
+ match = 1;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '2': {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '3': {
+ p++;
+ match = 3;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '4': {
+ p++;
+ match = 4;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '5': {
+ p++;
+ match = 5;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '6': {
+ p++;
+ match = 6;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '7': {
+ p++;
+ match = 7;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '8': {
+ p++;
+ match = 8;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ case '9': {
+ p++;
+ match = 9;
+ goto s_n_llhttp__internal__n_invoke_store_http_major_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_version_1:
+ s_n_llhttp__internal__n_span_start_llhttp__on_version_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_version_1;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_version;
+ goto s_n_llhttp__internal__n_res_http_major;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_start_res:
+ s_n_llhttp__internal__n_start_res: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_start_res;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 5);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_start_res;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_109;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete:
+ s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: {
+ switch (llhttp__on_method_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_req_first_space_before_url;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_23;
+ default:
+ goto s_n_llhttp__internal__n_error_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method_2:
+ s_n_llhttp__internal__n_req_or_res_method_2: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method_2;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ match = 2;
+ goto s_n_llhttp__internal__n_invoke_store_method;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_or_res_method_2;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_update_type_1:
+ s_n_llhttp__internal__n_invoke_update_type_1: {
+ switch (llhttp__internal__c_update_type_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method_3:
+ s_n_llhttp__internal__n_req_or_res_method_3: {
+ llparse_match_t match_seq;
+
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method_3;
+ }
+ match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3);
+ p = match_seq.current;
+ switch (match_seq.status) {
+ case kMatchComplete: {
+ p++;
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1;
+ }
+ case kMatchPause: {
+ return s_n_llhttp__internal__n_req_or_res_method_3;
+ }
+ case kMatchMismatch: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method_1:
+ s_n_llhttp__internal__n_req_or_res_method_1: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method_1;
+ }
+ switch (*p) {
+ case 'E': {
+ p++;
+ goto s_n_llhttp__internal__n_req_or_res_method_2;
+ }
+ case 'T': {
+ p++;
+ goto s_n_llhttp__internal__n_req_or_res_method_3;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_req_or_res_method:
+ s_n_llhttp__internal__n_req_or_res_method: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_req_or_res_method;
+ }
+ switch (*p) {
+ case 'H': {
+ p++;
+ goto s_n_llhttp__internal__n_req_or_res_method_1;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_error_106;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_span_start_llhttp__on_method:
+ s_n_llhttp__internal__n_span_start_llhttp__on_method: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_span_start_llhttp__on_method;
+ }
+ state->_span_pos0 = (void*) p;
+ state->_span_cb0 = llhttp__on_method;
+ goto s_n_llhttp__internal__n_req_or_res_method;
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_start_req_or_res:
+ s_n_llhttp__internal__n_start_req_or_res: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_start_req_or_res;
+ }
+ switch (*p) {
+ case 'H': {
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_method;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_update_type_2;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_load_type:
+ s_n_llhttp__internal__n_invoke_load_type: {
+ switch (llhttp__internal__c_load_type(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;
+ case 2:
+ goto s_n_llhttp__internal__n_start_res;
+ default:
+ goto s_n_llhttp__internal__n_start_req_or_res;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_invoke_update_finish:
+ s_n_llhttp__internal__n_invoke_update_finish: {
+ switch (llhttp__internal__c_update_finish(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ case s_n_llhttp__internal__n_start:
+ s_n_llhttp__internal__n_start: {
+ if (p == endp) {
+ return s_n_llhttp__internal__n_start;
+ }
+ switch (*p) {
+ case 10: {
+ p++;
+ goto s_n_llhttp__internal__n_start;
+ }
+ case 13: {
+ p++;
+ goto s_n_llhttp__internal__n_start;
+ }
+ default: {
+ goto s_n_llhttp__internal__n_invoke_load_initial_message_completed;
+ }
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ default:
+ /* UNREACHABLE */
+ abort();
+ }
+ s_n_llhttp__internal__n_error_2: {
+ state->error = 0x7;
+ state->reason = "Invalid characters in url";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_finish_2: {
+ switch (llhttp__internal__c_update_finish_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_start;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_initial_message_completed: {
+ switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_finish_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_content_length: {
+ switch (llhttp__internal__c_update_content_length(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_8: {
+ state->error = 0x5;
+ state->reason = "Data after `Connection: close`";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_3: {
+ switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_closed;
+ default:
+ goto s_n_llhttp__internal__n_error_8;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_2: {
+ switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_update_initial_message_completed;
+ default:
+ goto s_n_llhttp__internal__n_closed;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_finish_1: {
+ switch (llhttp__internal__c_update_finish_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_13: {
+ state->error = 0x15;
+ state->reason = "on_message_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_38: {
+ state->error = 0x12;
+ state->reason = "`on_message_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_15: {
+ state->error = 0x15;
+ state->reason = "on_chunk_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_40: {
+ state->error = 0x14;
+ state->reason = "`on_chunk_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: {
+ switch (llhttp__on_chunk_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_15;
+ default:
+ goto s_n_llhttp__internal__n_error_40;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_2: {
+ state->error = 0x15;
+ state->reason = "on_message_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_9: {
+ state->error = 0x12;
+ state->reason = "`on_message_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: {
+ switch (llhttp__on_message_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_pause_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_2;
+ default:
+ goto s_n_llhttp__internal__n_error_9;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_36: {
+ state->error = 0xc;
+ state->reason = "Chunk size overflow";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_10: {
+ state->error = 0xc;
+ state->reason = "Invalid character in chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_4: {
+ switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_otherwise;
+ default:
+ goto s_n_llhttp__internal__n_error_10;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_3: {
+ state->error = 0x15;
+ state->reason = "on_chunk_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_14: {
+ state->error = 0x14;
+ state->reason = "`on_chunk_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: {
+ switch (llhttp__on_chunk_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_update_content_length_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_3;
+ default:
+ goto s_n_llhttp__internal__n_error_14;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_13: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk data";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_6: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_13;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_15: {
+ state->error = 0x2;
+ state->reason = "Expected LF after chunk data";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_7: {
+ switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_15;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_body: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_body(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_chunk_data_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags: {
+ switch (llhttp__internal__c_or_flags(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_start;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_4: {
+ state->error = 0x15;
+ state->reason = "on_chunk_header pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_12: {
+ state->error = 0x13;
+ state->reason = "`on_chunk_header` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: {
+ switch (llhttp__on_chunk_header(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_is_equal_content_length;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_4;
+ default:
+ goto s_n_llhttp__internal__n_error_12;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_16: {
+ state->error = 0x2;
+ state->reason = "Expected LF after chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_8: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header;
+ default:
+ goto s_n_llhttp__internal__n_error_16;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_11: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_5: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_11;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_17: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_18: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_20: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk extension name";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_5: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_19: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_6: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_21: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_7: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_22: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_25: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk extension value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_8: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_24: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_9: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_26: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_28: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after chunk extension value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_11: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_chunk_size_almost_done;
+ default:
+ goto s_n_llhttp__internal__n_error_28;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_29: {
+ state->error = 0x2;
+ state->reason = "Invalid character in chunk extensions quote value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_10: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_27: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_30;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_31;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_11: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_value pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_32: {
+ state->error = 0x23;
+ state->reason = "`on_chunk_extension_value` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_33;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_12: {
+ state->error = 0x15;
+ state->reason = "on_chunk_extension_name pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_23: {
+ state->error = 0x22;
+ state->reason = "`on_chunk_extension_name` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: {
+ switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_chunk_extension_value;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_12;
+ default:
+ goto s_n_llhttp__internal__n_error_23;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_chunk_extension_name(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_34;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_35: {
+ state->error = 0xc;
+ state->reason = "Invalid character in chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_content_length: {
+ switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_36;
+ default:
+ goto s_n_llhttp__internal__n_chunk_size;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_37: {
+ state->error = 0xc;
+ state->reason = "Invalid character in chunk size";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_body_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_body(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_finish_3: {
+ switch (llhttp__internal__c_update_finish_3(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_39: {
+ state->error = 0xf;
+ state->reason = "Request has invalid `Transfer-Encoding`";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause: {
+ state->error = 0x15;
+ state->reason = "on_message_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_7: {
+ state->error = 0x12;
+ state->reason = "`on_message_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: {
+ switch (llhttp__on_message_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete;
+ case 21:
+ goto s_n_llhttp__internal__n_pause;
+ default:
+ goto s_n_llhttp__internal__n_error_7;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_1: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_2: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_upgrade: {
+ switch (llhttp__internal__c_update_upgrade(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_14: {
+ state->error = 0x15;
+ state->reason = "Paused by on_headers_complete";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_6: {
+ state->error = 0x11;
+ state->reason = "User callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: {
+ switch (llhttp__on_headers_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_or_flags_1;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_update_upgrade;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_14;
+ default:
+ goto s_n_llhttp__internal__n_error_6;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: {
+ switch (llhttp__before_headers_complete(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags: {
+ switch (llhttp__internal__c_test_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_1: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_flags;
+ default:
+ goto s_n_llhttp__internal__n_error_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_17: {
+ state->error = 0x15;
+ state->reason = "on_chunk_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_42: {
+ state->error = 0x14;
+ state->reason = "`on_chunk_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: {
+ switch (llhttp__on_chunk_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_17;
+ default:
+ goto s_n_llhttp__internal__n_error_42;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_3: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_4: {
+ switch (llhttp__internal__c_or_flags_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_upgrade_1: {
+ switch (llhttp__internal__c_update_upgrade(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_16: {
+ state->error = 0x15;
+ state->reason = "Paused by on_headers_complete";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_41: {
+ state->error = 0x11;
+ state->reason = "User callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: {
+ switch (llhttp__on_headers_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_or_flags_3;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_update_upgrade_1;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_16;
+ default:
+ goto s_n_llhttp__internal__n_error_41;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: {
+ switch (llhttp__before_headers_complete(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_1: {
+ switch (llhttp__internal__c_test_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_43: {
+ state->error = 0x2;
+ state->reason = "Expected LF after headers";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_12: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_flags_1;
+ default:
+ goto s_n_llhttp__internal__n_error_43;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_44: {
+ state->error = 0xa;
+ state->reason = "Invalid header token";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_field: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_field(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_5;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_13: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_field_colon_discard_ws;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_60: {
+ state->error = 0xb;
+ state->reason = "Content-Length can't be present with Transfer-Encoding";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_47: {
+ state->error = 0xa;
+ state->reason = "Invalid header value char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_15: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ default:
+ goto s_n_llhttp__internal__n_error_47;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_49: {
+ state->error = 0xb;
+ state->reason = "Empty Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_18: {
+ state->error = 0x15;
+ state->reason = "on_header_value_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_48: {
+ state->error = 0x1d;
+ state->reason = "`on_header_value_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state: {
+ switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_5: {
+ switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_6: {
+ switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_7: {
+ switch (llhttp__internal__c_or_flags_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_8: {
+ switch (llhttp__internal__c_or_flags_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_2: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 5:
+ goto s_n_llhttp__internal__n_invoke_or_flags_5;
+ case 6:
+ goto s_n_llhttp__internal__n_invoke_or_flags_6;
+ case 7:
+ goto s_n_llhttp__internal__n_invoke_or_flags_7;
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_or_flags_8;
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_1: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 2:
+ goto s_n_llhttp__internal__n_error_49;
+ default:
+ goto s_n_llhttp__internal__n_invoke_load_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_46: {
+ state->error = 0xa;
+ state->reason = "Invalid header value char";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_14: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_discard_lws;
+ default:
+ goto s_n_llhttp__internal__n_error_46;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_50: {
+ state->error = 0x2;
+ state->reason = "Expected LF after CR";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_16: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_discard_lws;
+ default:
+ goto s_n_llhttp__internal__n_error_50;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_1: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_4: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_1;
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_52: {
+ state->error = 0xa;
+ state->reason = "Unexpected whitespace after header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_18: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_header_state_4;
+ default:
+ goto s_n_llhttp__internal__n_error_52;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_2: {
+ switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_9: {
+ switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_10: {
+ switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_11: {
+ switch (llhttp__internal__c_or_flags_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_12: {
+ switch (llhttp__internal__c_or_flags_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_5: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 5:
+ goto s_n_llhttp__internal__n_invoke_or_flags_9;
+ case 6:
+ goto s_n_llhttp__internal__n_invoke_or_flags_10;
+ case 7:
+ goto s_n_llhttp__internal__n_invoke_or_flags_11;
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_or_flags_12;
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_53: {
+ state->error = 0x3;
+ state->reason = "Missing expected LF after header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_51: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after header value";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_header_value_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_54;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_19: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_lenient;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_4: {
+ switch (llhttp__internal__c_update_header_state(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_13: {
+ switch (llhttp__internal__c_or_flags_5(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_14: {
+ switch (llhttp__internal__c_or_flags_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_15: {
+ switch (llhttp__internal__c_or_flags_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_16: {
+ switch (llhttp__internal__c_or_flags_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_6: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 5:
+ goto s_n_llhttp__internal__n_invoke_or_flags_13;
+ case 6:
+ goto s_n_llhttp__internal__n_invoke_or_flags_14;
+ case 7:
+ goto s_n_llhttp__internal__n_invoke_or_flags_15;
+ case 8:
+ goto s_n_llhttp__internal__n_invoke_or_flags_16;
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_5: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_token;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_3: {
+ switch (llhttp__internal__c_update_header_state_3(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_6: {
+ switch (llhttp__internal__c_update_header_state_6(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_7: {
+ switch (llhttp__internal__c_update_header_state_7(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_connection_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_56;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_content_length_1: {
+ switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6;
+ default:
+ goto s_n_llhttp__internal__n_header_value_content_length;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_17: {
+ switch (llhttp__internal__c_or_flags_17(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_57;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_55: {
+ state->error = 0x4;
+ state->reason = "Duplicate Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_2: {
+ switch (llhttp__internal__c_test_flags_2(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_header_value_content_length;
+ default:
+ goto s_n_llhttp__internal__n_error_55;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_59;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_8: {
+ switch (llhttp__internal__c_update_header_state_8(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_otherwise;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_value(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_error_58;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_20: {
+ switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8;
+ default:
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_type_1: {
+ switch (llhttp__internal__c_load_type(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20;
+ default:
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_9: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_and_flags: {
+ switch (llhttp__internal__c_and_flags(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_value_te_chunked;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_19: {
+ switch (llhttp__internal__c_or_flags_18(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_and_flags;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_21: {
+ switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_19;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_type_2: {
+ switch (llhttp__internal__c_load_type(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_19;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_18: {
+ switch (llhttp__internal__c_or_flags_18(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_and_flags;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_3: {
+ switch (llhttp__internal__c_test_flags_3(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_type_2;
+ default:
+ goto s_n_llhttp__internal__n_invoke_or_flags_18;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_or_flags_20: {
+ switch (llhttp__internal__c_or_flags_20(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_header_state_9;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_header_state_3: {
+ switch (llhttp__internal__c_load_header_state(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_value_connection;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_test_flags_2;
+ case 3:
+ goto s_n_llhttp__internal__n_invoke_test_flags_3;
+ case 4:
+ goto s_n_llhttp__internal__n_invoke_or_flags_20;
+ default:
+ goto s_n_llhttp__internal__n_header_value;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_22: {
+ switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_error_60;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_4: {
+ switch (llhttp__internal__c_test_flags_4(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_61: {
+ state->error = 0xf;
+ state->reason = "Transfer-Encoding can't be present with Content-Length";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_23: {
+ switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_error_61;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_flags_5: {
+ switch (llhttp__internal__c_test_flags_2(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23;
+ default:
+ goto s_n_llhttp__internal__n_header_value_discard_ws;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_19: {
+ state->error = 0x15;
+ state->reason = "on_header_field_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_45: {
+ state->error = 0x1c;
+ state->reason = "`on_header_field_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_field(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_header_field(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_62: {
+ state->error = 0xa;
+ state->reason = "Invalid header token";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_10: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_header_state: {
+ switch (llhttp__internal__c_store_header_state(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_colon;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_header_state_11: {
+ switch (llhttp__internal__c_update_header_state_1(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_header_field_general;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_4: {
+ state->error = 0x1e;
+ state->reason = "Unexpected space after start line";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags: {
+ switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_header_field_start;
+ default:
+ goto s_n_llhttp__internal__n_error_4;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_20: {
+ state->error = 0x15;
+ state->reason = "on_url_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_3: {
+ state->error = 0x1a;
+ state->reason = "`on_url_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: {
+ switch (llhttp__on_url_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_headers_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_20;
+ default:
+ goto s_n_llhttp__internal__n_error_3;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_http_minor: {
+ switch (llhttp__internal__c_update_http_minor(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_http_major: {
+ switch (llhttp__internal__c_update_http_major(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_http_minor;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_63: {
+ state->error = 0x7;
+ state->reason = "Expected CRLF";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_71: {
+ state->error = 0x17;
+ state->reason = "Pause on PRI/Upgrade";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_72: {
+ state->error = 0x9;
+ state->reason = "Expected HTTP/2 Connection Preface";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_69: {
+ state->error = 0x2;
+ state->reason = "Expected CRLF after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_26: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_headers_start;
+ default:
+ goto s_n_llhttp__internal__n_error_69;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_68: {
+ state->error = 0x9;
+ state->reason = "Expected CRLF after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_25: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_req_http_complete_crlf;
+ default:
+ goto s_n_llhttp__internal__n_error_68;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_70: {
+ state->error = 0x9;
+ state->reason = "Expected CRLF after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_21: {
+ state->error = 0x15;
+ state->reason = "on_version_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_67: {
+ state->error = 0x21;
+ state->reason = "`on_version_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_66;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_66;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 9:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_1: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_2: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_major: {
+ switch (llhttp__internal__c_load_http_major(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_1;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_2;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_24: {
+ switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1;
+ default:
+ goto s_n_llhttp__internal__n_invoke_load_http_major;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_minor: {
+ switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_73;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_73;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_3: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_74;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_major: {
+ switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_req_http_dot;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_4: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_75;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_65: {
+ state->error = 0x8;
+ state->reason = "Invalid method for HTTP/x.x request";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_method: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 1:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 2:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 3:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 4:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 5:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 6:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 7:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 8:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 9:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 10:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 11:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 12:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 13:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 14:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 15:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 16:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 17:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 18:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 19:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 20:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 21:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 22:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 23:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 24:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 25:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 26:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 27:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 28:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 29:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 30:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 31:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 32:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 33:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 34:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 46:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ default:
+ goto s_n_llhttp__internal__n_error_65;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_78: {
+ state->error = 0x8;
+ state->reason = "Expected HTTP/";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_76: {
+ state->error = 0x8;
+ state->reason = "Expected SOURCE method for ICE/x.x request";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_method_2: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 33:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ default:
+ goto s_n_llhttp__internal__n_error_76;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_77: {
+ state->error = 0x8;
+ state->reason = "Invalid method for RTSP/x.x request";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_method_3: {
+ switch (llhttp__internal__c_load_method(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 3:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 6:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 35:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 36:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 37:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 38:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 39:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 40:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 41:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 42:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 43:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 44:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ case 45:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_version;
+ default:
+ goto s_n_llhttp__internal__n_error_77;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_22: {
+ state->error = 0x15;
+ state->reason = "on_url_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_64: {
+ state->error = 0x1a;
+ state->reason = "`on_url_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: {
+ switch (llhttp__on_url_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_req_http_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_22;
+ default:
+ goto s_n_llhttp__internal__n_error_64;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_7: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_8: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_79: {
+ state->error = 0x7;
+ state->reason = "Invalid char in url fragment start";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_9: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_10: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_11: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_80: {
+ state->error = 0x7;
+ state->reason = "Invalid char in url query";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_81: {
+ state->error = 0x7;
+ state->reason = "Invalid char in url path";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_12: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_13: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_lf_to_http09;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_url_14: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_url(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_url_skip_to_http;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_82: {
+ state->error = 0x7;
+ state->reason = "Double @ in url";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_83: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url server";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_84: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url server";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_85: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url schema";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_86: {
+ state->error = 0x7;
+ state->reason = "Unexpected char in url schema";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_87: {
+ state->error = 0x7;
+ state->reason = "Unexpected start char in url";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_is_equal_method: {
+ switch (llhttp__internal__c_is_equal_method(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_url_entry_normal;
+ default:
+ goto s_n_llhttp__internal__n_url_entry_connect;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_88: {
+ state->error = 0x6;
+ state->reason = "Expected space after method";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_26: {
+ state->error = 0x15;
+ state->reason = "on_method_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_107: {
+ state->error = 0x20;
+ state->reason = "`on_method_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_method_2: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_method(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_method_1: {
+ switch (llhttp__internal__c_store_method(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_108: {
+ state->error = 0x6;
+ state->reason = "Invalid method encountered";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_100: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_98: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_96: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_24: {
+ state->error = 0x15;
+ state->reason = "on_status_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_92: {
+ state->error = 0x1b;
+ state->reason = "`on_status_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: {
+ switch (llhttp__on_status_complete(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_headers_start;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_24;
+ default:
+ goto s_n_llhttp__internal__n_error_92;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_91: {
+ state->error = 0xd;
+ state->reason = "Invalid response status";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_28: {
+ switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_91;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_93: {
+ state->error = 0x2;
+ state->reason = "Expected LF after CR";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_29: {
+ switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete;
+ default:
+ goto s_n_llhttp__internal__n_error_93;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_94: {
+ state->error = 0x19;
+ state->reason = "Missing expected CR after response line";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_status: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_status(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_status_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_status(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) (p + 1);
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done;
+ return s_error;
+ }
+ p++;
+ goto s_n_llhttp__internal__n_res_line_almost_done;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_95: {
+ state->error = 0xd;
+ state->reason = "Invalid response status";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_status_code_2: {
+ switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_96;
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_otherwise;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_97: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_status_code_1: {
+ switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_98;
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_digit_3;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_99: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_mul_add_status_code: {
+ switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) {
+ case 1:
+ goto s_n_llhttp__internal__n_error_100;
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_digit_2;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_101: {
+ state->error = 0xd;
+ state->reason = "Invalid status code";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_status_code: {
+ switch (llhttp__internal__c_update_status_code(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_res_status_code_digit_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_102: {
+ state->error = 0x9;
+ state->reason = "Expected space after version";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_25: {
+ state->error = 0x15;
+ state->reason = "on_version_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_90: {
+ state->error = 0x21;
+ state->reason = "`on_version_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_6: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_5: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_89;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_89;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_3: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 9:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_4: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_minor_5: {
+ switch (llhttp__internal__c_load_http_minor(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_http_major_1: {
+ switch (llhttp__internal__c_load_http_major(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_3;
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_4;
+ case 2:
+ goto s_n_llhttp__internal__n_invoke_load_http_minor_5;
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_test_lenient_flags_27: {
+ switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6;
+ default:
+ goto s_n_llhttp__internal__n_invoke_load_http_major_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_minor_1: {
+ switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_7: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_103;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_103;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_8: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_104;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_104;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_http_major_1: {
+ switch (llhttp__internal__c_store_http_major(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_res_http_dot;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_version_9: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_version(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_105;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_error_105;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_109: {
+ state->error = 0x8;
+ state->reason = "Expected HTTP/";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_23: {
+ state->error = 0x15;
+ state->reason = "on_method_complete pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_1: {
+ state->error = 0x20;
+ state->reason = "`on_method_complete` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_method: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_method(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_type: {
+ switch (llhttp__internal__c_update_type(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_end_llhttp__on_method;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_store_method: {
+ switch (llhttp__internal__c_store_method(state, p, endp, match)) {
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_type;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_106: {
+ state->error = 0x8;
+ state->reason = "Invalid word encountered";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_span_end_llhttp__on_method_1: {
+ const unsigned char* start;
+ int err;
+
+ start = state->_span_pos0;
+ state->_span_pos0 = NULL;
+ err = llhttp__on_method(state, start, p);
+ if (err != 0) {
+ state->error = err;
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1;
+ return s_error;
+ }
+ goto s_n_llhttp__internal__n_invoke_update_type_1;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_update_type_2: {
+ switch (llhttp__internal__c_update_type(state, p, endp)) {
+ default:
+ goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_27: {
+ state->error = 0x15;
+ state->reason = "on_message_begin pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error: {
+ state->error = 0x10;
+ state->reason = "`on_message_begin` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: {
+ switch (llhttp__on_message_begin(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_load_type;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_27;
+ default:
+ goto s_n_llhttp__internal__n_error;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_pause_28: {
+ state->error = 0x15;
+ state->reason = "on_reset pause";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_error_110: {
+ state->error = 0x1f;
+ state->reason = "`on_reset` callback error";
+ state->error_pos = (const char*) p;
+ state->_current = (void*) (intptr_t) s_error;
+ return s_error;
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_llhttp__on_reset: {
+ switch (llhttp__on_reset(state, p, endp)) {
+ case 0:
+ goto s_n_llhttp__internal__n_invoke_update_finish;
+ case 21:
+ goto s_n_llhttp__internal__n_pause_28;
+ default:
+ goto s_n_llhttp__internal__n_error_110;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+ s_n_llhttp__internal__n_invoke_load_initial_message_completed: {
+ switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) {
+ case 1:
+ goto s_n_llhttp__internal__n_invoke_llhttp__on_reset;
+ default:
+ goto s_n_llhttp__internal__n_invoke_update_finish;
+ }
+ /* UNREACHABLE */;
+ abort();
+ }
+}
+
+int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) {
+ llparse_state_t next;
+
+ /* check lingering errors */
+ if (state->error != 0) {
+ return state->error;
+ }
+
+ /* restart spans */
+ if (state->_span_pos0 != NULL) {
+ state->_span_pos0 = (void*) p;
+ }
+
+ next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp);
+ if (next == s_error) {
+ return state->error;
+ }
+ state->_current = (void*) (intptr_t) next;
+
+ /* execute spans */
+ if (state->_span_pos0 != NULL) {
+ int error;
+
+ error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp);
+ if (error != 0) {
+ state->error = error;
+ state->error_pos = endp;
+ return error;
+ }
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/lib/llhttp/llhttp.h b/lib/llhttp/llhttp.h
new file mode 100644
index 0000000..37b7934
--- /dev/null
+++ b/lib/llhttp/llhttp.h
@@ -0,0 +1,903 @@
+
+#ifndef INCLUDE_LLHTTP_H_
+#define INCLUDE_LLHTTP_H_
+
+#define LLHTTP_VERSION_MAJOR 9
+#define LLHTTP_VERSION_MINOR 2
+#define LLHTTP_VERSION_PATCH 1
+
+#ifndef INCLUDE_LLHTTP_ITSELF_H_
+#define INCLUDE_LLHTTP_ITSELF_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+typedef struct llhttp__internal_s llhttp__internal_t;
+struct llhttp__internal_s {
+ int32_t _index;
+ void* _span_pos0;
+ void* _span_cb0;
+ int32_t error;
+ const char* reason;
+ const char* error_pos;
+ void* data;
+ void* _current;
+ uint64_t content_length;
+ uint8_t type;
+ uint8_t method;
+ uint8_t http_major;
+ uint8_t http_minor;
+ uint8_t header_state;
+ uint16_t lenient_flags;
+ uint8_t upgrade;
+ uint8_t finish;
+ uint16_t flags;
+ uint16_t status_code;
+ uint8_t initial_message_completed;
+ void* settings;
+};
+
+int llhttp__internal_init(llhttp__internal_t* s);
+int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
+
+
+#ifndef LLLLHTTP_C_HEADERS_
+#define LLLLHTTP_C_HEADERS_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum llhttp_errno {
+ HPE_OK = 0,
+ HPE_INTERNAL = 1,
+ HPE_STRICT = 2,
+ HPE_CR_EXPECTED = 25,
+ HPE_LF_EXPECTED = 3,
+ HPE_UNEXPECTED_CONTENT_LENGTH = 4,
+ HPE_UNEXPECTED_SPACE = 30,
+ HPE_CLOSED_CONNECTION = 5,
+ HPE_INVALID_METHOD = 6,
+ HPE_INVALID_URL = 7,
+ HPE_INVALID_CONSTANT = 8,
+ HPE_INVALID_VERSION = 9,
+ HPE_INVALID_HEADER_TOKEN = 10,
+ HPE_INVALID_CONTENT_LENGTH = 11,
+ HPE_INVALID_CHUNK_SIZE = 12,
+ HPE_INVALID_STATUS = 13,
+ HPE_INVALID_EOF_STATE = 14,
+ HPE_INVALID_TRANSFER_ENCODING = 15,
+ HPE_CB_MESSAGE_BEGIN = 16,
+ HPE_CB_HEADERS_COMPLETE = 17,
+ HPE_CB_MESSAGE_COMPLETE = 18,
+ HPE_CB_CHUNK_HEADER = 19,
+ HPE_CB_CHUNK_COMPLETE = 20,
+ HPE_PAUSED = 21,
+ HPE_PAUSED_UPGRADE = 22,
+ HPE_PAUSED_H2_UPGRADE = 23,
+ HPE_USER = 24,
+ HPE_CB_URL_COMPLETE = 26,
+ HPE_CB_STATUS_COMPLETE = 27,
+ HPE_CB_METHOD_COMPLETE = 32,
+ HPE_CB_VERSION_COMPLETE = 33,
+ HPE_CB_HEADER_FIELD_COMPLETE = 28,
+ HPE_CB_HEADER_VALUE_COMPLETE = 29,
+ HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,
+ HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,
+ HPE_CB_RESET = 31
+};
+typedef enum llhttp_errno llhttp_errno_t;
+
+enum llhttp_flags {
+ F_CONNECTION_KEEP_ALIVE = 0x1,
+ F_CONNECTION_CLOSE = 0x2,
+ F_CONNECTION_UPGRADE = 0x4,
+ F_CHUNKED = 0x8,
+ F_UPGRADE = 0x10,
+ F_CONTENT_LENGTH = 0x20,
+ F_SKIPBODY = 0x40,
+ F_TRAILING = 0x80,
+ F_TRANSFER_ENCODING = 0x200
+};
+typedef enum llhttp_flags llhttp_flags_t;
+
+enum llhttp_lenient_flags {
+ LENIENT_HEADERS = 0x1,
+ LENIENT_CHUNKED_LENGTH = 0x2,
+ LENIENT_KEEP_ALIVE = 0x4,
+ LENIENT_TRANSFER_ENCODING = 0x8,
+ LENIENT_VERSION = 0x10,
+ LENIENT_DATA_AFTER_CLOSE = 0x20,
+ LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
+ LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,
+ LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,
+ LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200
+};
+typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
+
+enum llhttp_type {
+ HTTP_BOTH = 0,
+ HTTP_REQUEST = 1,
+ HTTP_RESPONSE = 2
+};
+typedef enum llhttp_type llhttp_type_t;
+
+enum llhttp_finish {
+ HTTP_FINISH_SAFE = 0,
+ HTTP_FINISH_SAFE_WITH_CB = 1,
+ HTTP_FINISH_UNSAFE = 2
+};
+typedef enum llhttp_finish llhttp_finish_t;
+
+enum llhttp_method {
+ HTTP_DELETE = 0,
+ HTTP_GET = 1,
+ HTTP_HEAD = 2,
+ HTTP_POST = 3,
+ HTTP_PUT = 4,
+ HTTP_CONNECT = 5,
+ HTTP_OPTIONS = 6,
+ HTTP_TRACE = 7,
+ HTTP_COPY = 8,
+ HTTP_LOCK = 9,
+ HTTP_MKCOL = 10,
+ HTTP_MOVE = 11,
+ HTTP_PROPFIND = 12,
+ HTTP_PROPPATCH = 13,
+ HTTP_SEARCH = 14,
+ HTTP_UNLOCK = 15,
+ HTTP_BIND = 16,
+ HTTP_REBIND = 17,
+ HTTP_UNBIND = 18,
+ HTTP_ACL = 19,
+ HTTP_REPORT = 20,
+ HTTP_MKACTIVITY = 21,
+ HTTP_CHECKOUT = 22,
+ HTTP_MERGE = 23,
+ HTTP_MSEARCH = 24,
+ HTTP_NOTIFY = 25,
+ HTTP_SUBSCRIBE = 26,
+ HTTP_UNSUBSCRIBE = 27,
+ HTTP_PATCH = 28,
+ HTTP_PURGE = 29,
+ HTTP_MKCALENDAR = 30,
+ HTTP_LINK = 31,
+ HTTP_UNLINK = 32,
+ HTTP_SOURCE = 33,
+ HTTP_PRI = 34,
+ HTTP_DESCRIBE = 35,
+ HTTP_ANNOUNCE = 36,
+ HTTP_SETUP = 37,
+ HTTP_PLAY = 38,
+ HTTP_PAUSE = 39,
+ HTTP_TEARDOWN = 40,
+ HTTP_GET_PARAMETER = 41,
+ HTTP_SET_PARAMETER = 42,
+ HTTP_REDIRECT = 43,
+ HTTP_RECORD = 44,
+ HTTP_FLUSH = 45,
+ HTTP_QUERY = 46
+};
+typedef enum llhttp_method llhttp_method_t;
+
+enum llhttp_status {
+ HTTP_STATUS_CONTINUE = 100,
+ HTTP_STATUS_SWITCHING_PROTOCOLS = 101,
+ HTTP_STATUS_PROCESSING = 102,
+ HTTP_STATUS_EARLY_HINTS = 103,
+ HTTP_STATUS_RESPONSE_IS_STALE = 110,
+ HTTP_STATUS_REVALIDATION_FAILED = 111,
+ HTTP_STATUS_DISCONNECTED_OPERATION = 112,
+ HTTP_STATUS_HEURISTIC_EXPIRATION = 113,
+ HTTP_STATUS_MISCELLANEOUS_WARNING = 199,
+ HTTP_STATUS_OK = 200,
+ HTTP_STATUS_CREATED = 201,
+ HTTP_STATUS_ACCEPTED = 202,
+ HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
+ HTTP_STATUS_NO_CONTENT = 204,
+ HTTP_STATUS_RESET_CONTENT = 205,
+ HTTP_STATUS_PARTIAL_CONTENT = 206,
+ HTTP_STATUS_MULTI_STATUS = 207,
+ HTTP_STATUS_ALREADY_REPORTED = 208,
+ HTTP_STATUS_TRANSFORMATION_APPLIED = 214,
+ HTTP_STATUS_IM_USED = 226,
+ HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,
+ HTTP_STATUS_MULTIPLE_CHOICES = 300,
+ HTTP_STATUS_MOVED_PERMANENTLY = 301,
+ HTTP_STATUS_FOUND = 302,
+ HTTP_STATUS_SEE_OTHER = 303,
+ HTTP_STATUS_NOT_MODIFIED = 304,
+ HTTP_STATUS_USE_PROXY = 305,
+ HTTP_STATUS_SWITCH_PROXY = 306,
+ HTTP_STATUS_TEMPORARY_REDIRECT = 307,
+ HTTP_STATUS_PERMANENT_REDIRECT = 308,
+ HTTP_STATUS_BAD_REQUEST = 400,
+ HTTP_STATUS_UNAUTHORIZED = 401,
+ HTTP_STATUS_PAYMENT_REQUIRED = 402,
+ HTTP_STATUS_FORBIDDEN = 403,
+ HTTP_STATUS_NOT_FOUND = 404,
+ HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
+ HTTP_STATUS_NOT_ACCEPTABLE = 406,
+ HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
+ HTTP_STATUS_REQUEST_TIMEOUT = 408,
+ HTTP_STATUS_CONFLICT = 409,
+ HTTP_STATUS_GONE = 410,
+ HTTP_STATUS_LENGTH_REQUIRED = 411,
+ HTTP_STATUS_PRECONDITION_FAILED = 412,
+ HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
+ HTTP_STATUS_URI_TOO_LONG = 414,
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
+ HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
+ HTTP_STATUS_EXPECTATION_FAILED = 417,
+ HTTP_STATUS_IM_A_TEAPOT = 418,
+ HTTP_STATUS_PAGE_EXPIRED = 419,
+ HTTP_STATUS_ENHANCE_YOUR_CALM = 420,
+ HTTP_STATUS_MISDIRECTED_REQUEST = 421,
+ HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
+ HTTP_STATUS_LOCKED = 423,
+ HTTP_STATUS_FAILED_DEPENDENCY = 424,
+ HTTP_STATUS_TOO_EARLY = 425,
+ HTTP_STATUS_UPGRADE_REQUIRED = 426,
+ HTTP_STATUS_PRECONDITION_REQUIRED = 428,
+ HTTP_STATUS_TOO_MANY_REQUESTS = 429,
+ HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,
+ HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
+ HTTP_STATUS_LOGIN_TIMEOUT = 440,
+ HTTP_STATUS_NO_RESPONSE = 444,
+ HTTP_STATUS_RETRY_WITH = 449,
+ HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,
+ HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
+ HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,
+ HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,
+ HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,
+ HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,
+ HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,
+ HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,
+ HTTP_STATUS_INVALID_TOKEN = 498,
+ HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,
+ HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
+ HTTP_STATUS_NOT_IMPLEMENTED = 501,
+ HTTP_STATUS_BAD_GATEWAY = 502,
+ HTTP_STATUS_SERVICE_UNAVAILABLE = 503,
+ HTTP_STATUS_GATEWAY_TIMEOUT = 504,
+ HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
+ HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
+ HTTP_STATUS_INSUFFICIENT_STORAGE = 507,
+ HTTP_STATUS_LOOP_DETECTED = 508,
+ HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,
+ HTTP_STATUS_NOT_EXTENDED = 510,
+ HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,
+ HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,
+ HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,
+ HTTP_STATUS_CONNECTION_TIMEOUT = 522,
+ HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,
+ HTTP_STATUS_TIMEOUT_OCCURED = 524,
+ HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,
+ HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,
+ HTTP_STATUS_RAILGUN_ERROR = 527,
+ HTTP_STATUS_SITE_IS_OVERLOADED = 529,
+ HTTP_STATUS_SITE_IS_FROZEN = 530,
+ HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,
+ HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,
+ HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599
+};
+typedef enum llhttp_status llhttp_status_t;
+
+#define HTTP_ERRNO_MAP(XX) \
+ XX(0, OK, OK) \
+ XX(1, INTERNAL, INTERNAL) \
+ XX(2, STRICT, STRICT) \
+ XX(25, CR_EXPECTED, CR_EXPECTED) \
+ XX(3, LF_EXPECTED, LF_EXPECTED) \
+ XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
+ XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \
+ XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
+ XX(6, INVALID_METHOD, INVALID_METHOD) \
+ XX(7, INVALID_URL, INVALID_URL) \
+ XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
+ XX(9, INVALID_VERSION, INVALID_VERSION) \
+ XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
+ XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
+ XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
+ XX(13, INVALID_STATUS, INVALID_STATUS) \
+ XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
+ XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
+ XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
+ XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
+ XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
+ XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
+ XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
+ XX(21, PAUSED, PAUSED) \
+ XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
+ XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
+ XX(24, USER, USER) \
+ XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \
+ XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \
+ XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \
+ XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \
+ XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \
+ XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \
+ XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \
+ XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \
+ XX(31, CB_RESET, CB_RESET) \
+
+
+#define HTTP_METHOD_MAP(XX) \
+ XX(0, DELETE, DELETE) \
+ XX(1, GET, GET) \
+ XX(2, HEAD, HEAD) \
+ XX(3, POST, POST) \
+ XX(4, PUT, PUT) \
+ XX(5, CONNECT, CONNECT) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(7, TRACE, TRACE) \
+ XX(8, COPY, COPY) \
+ XX(9, LOCK, LOCK) \
+ XX(10, MKCOL, MKCOL) \
+ XX(11, MOVE, MOVE) \
+ XX(12, PROPFIND, PROPFIND) \
+ XX(13, PROPPATCH, PROPPATCH) \
+ XX(14, SEARCH, SEARCH) \
+ XX(15, UNLOCK, UNLOCK) \
+ XX(16, BIND, BIND) \
+ XX(17, REBIND, REBIND) \
+ XX(18, UNBIND, UNBIND) \
+ XX(19, ACL, ACL) \
+ XX(20, REPORT, REPORT) \
+ XX(21, MKACTIVITY, MKACTIVITY) \
+ XX(22, CHECKOUT, CHECKOUT) \
+ XX(23, MERGE, MERGE) \
+ XX(24, MSEARCH, M-SEARCH) \
+ XX(25, NOTIFY, NOTIFY) \
+ XX(26, SUBSCRIBE, SUBSCRIBE) \
+ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+ XX(28, PATCH, PATCH) \
+ XX(29, PURGE, PURGE) \
+ XX(30, MKCALENDAR, MKCALENDAR) \
+ XX(31, LINK, LINK) \
+ XX(32, UNLINK, UNLINK) \
+ XX(33, SOURCE, SOURCE) \
+ XX(46, QUERY, QUERY) \
+
+
+#define RTSP_METHOD_MAP(XX) \
+ XX(1, GET, GET) \
+ XX(3, POST, POST) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(35, DESCRIBE, DESCRIBE) \
+ XX(36, ANNOUNCE, ANNOUNCE) \
+ XX(37, SETUP, SETUP) \
+ XX(38, PLAY, PLAY) \
+ XX(39, PAUSE, PAUSE) \
+ XX(40, TEARDOWN, TEARDOWN) \
+ XX(41, GET_PARAMETER, GET_PARAMETER) \
+ XX(42, SET_PARAMETER, SET_PARAMETER) \
+ XX(43, REDIRECT, REDIRECT) \
+ XX(44, RECORD, RECORD) \
+ XX(45, FLUSH, FLUSH) \
+
+
+#define HTTP_ALL_METHOD_MAP(XX) \
+ XX(0, DELETE, DELETE) \
+ XX(1, GET, GET) \
+ XX(2, HEAD, HEAD) \
+ XX(3, POST, POST) \
+ XX(4, PUT, PUT) \
+ XX(5, CONNECT, CONNECT) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(7, TRACE, TRACE) \
+ XX(8, COPY, COPY) \
+ XX(9, LOCK, LOCK) \
+ XX(10, MKCOL, MKCOL) \
+ XX(11, MOVE, MOVE) \
+ XX(12, PROPFIND, PROPFIND) \
+ XX(13, PROPPATCH, PROPPATCH) \
+ XX(14, SEARCH, SEARCH) \
+ XX(15, UNLOCK, UNLOCK) \
+ XX(16, BIND, BIND) \
+ XX(17, REBIND, REBIND) \
+ XX(18, UNBIND, UNBIND) \
+ XX(19, ACL, ACL) \
+ XX(20, REPORT, REPORT) \
+ XX(21, MKACTIVITY, MKACTIVITY) \
+ XX(22, CHECKOUT, CHECKOUT) \
+ XX(23, MERGE, MERGE) \
+ XX(24, MSEARCH, M-SEARCH) \
+ XX(25, NOTIFY, NOTIFY) \
+ XX(26, SUBSCRIBE, SUBSCRIBE) \
+ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+ XX(28, PATCH, PATCH) \
+ XX(29, PURGE, PURGE) \
+ XX(30, MKCALENDAR, MKCALENDAR) \
+ XX(31, LINK, LINK) \
+ XX(32, UNLINK, UNLINK) \
+ XX(33, SOURCE, SOURCE) \
+ XX(34, PRI, PRI) \
+ XX(35, DESCRIBE, DESCRIBE) \
+ XX(36, ANNOUNCE, ANNOUNCE) \
+ XX(37, SETUP, SETUP) \
+ XX(38, PLAY, PLAY) \
+ XX(39, PAUSE, PAUSE) \
+ XX(40, TEARDOWN, TEARDOWN) \
+ XX(41, GET_PARAMETER, GET_PARAMETER) \
+ XX(42, SET_PARAMETER, SET_PARAMETER) \
+ XX(43, REDIRECT, REDIRECT) \
+ XX(44, RECORD, RECORD) \
+ XX(45, FLUSH, FLUSH) \
+ XX(46, QUERY, QUERY) \
+
+
+#define HTTP_STATUS_MAP(XX) \
+ XX(100, CONTINUE, CONTINUE) \
+ XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \
+ XX(102, PROCESSING, PROCESSING) \
+ XX(103, EARLY_HINTS, EARLY_HINTS) \
+ XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \
+ XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \
+ XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \
+ XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \
+ XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \
+ XX(200, OK, OK) \
+ XX(201, CREATED, CREATED) \
+ XX(202, ACCEPTED, ACCEPTED) \
+ XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \
+ XX(204, NO_CONTENT, NO_CONTENT) \
+ XX(205, RESET_CONTENT, RESET_CONTENT) \
+ XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \
+ XX(207, MULTI_STATUS, MULTI_STATUS) \
+ XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \
+ XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \
+ XX(226, IM_USED, IM_USED) \
+ XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \
+ XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \
+ XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \
+ XX(302, FOUND, FOUND) \
+ XX(303, SEE_OTHER, SEE_OTHER) \
+ XX(304, NOT_MODIFIED, NOT_MODIFIED) \
+ XX(305, USE_PROXY, USE_PROXY) \
+ XX(306, SWITCH_PROXY, SWITCH_PROXY) \
+ XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \
+ XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \
+ XX(400, BAD_REQUEST, BAD_REQUEST) \
+ XX(401, UNAUTHORIZED, UNAUTHORIZED) \
+ XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \
+ XX(403, FORBIDDEN, FORBIDDEN) \
+ XX(404, NOT_FOUND, NOT_FOUND) \
+ XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \
+ XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \
+ XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \
+ XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \
+ XX(409, CONFLICT, CONFLICT) \
+ XX(410, GONE, GONE) \
+ XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \
+ XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \
+ XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \
+ XX(414, URI_TOO_LONG, URI_TOO_LONG) \
+ XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \
+ XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \
+ XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \
+ XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \
+ XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \
+ XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \
+ XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \
+ XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \
+ XX(423, LOCKED, LOCKED) \
+ XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \
+ XX(425, TOO_EARLY, TOO_EARLY) \
+ XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \
+ XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \
+ XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \
+ XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \
+ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \
+ XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \
+ XX(444, NO_RESPONSE, NO_RESPONSE) \
+ XX(449, RETRY_WITH, RETRY_WITH) \
+ XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \
+ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \
+ XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \
+ XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \
+ XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \
+ XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \
+ XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \
+ XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \
+ XX(498, INVALID_TOKEN, INVALID_TOKEN) \
+ XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \
+ XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \
+ XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \
+ XX(502, BAD_GATEWAY, BAD_GATEWAY) \
+ XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \
+ XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \
+ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \
+ XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \
+ XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \
+ XX(508, LOOP_DETECTED, LOOP_DETECTED) \
+ XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \
+ XX(510, NOT_EXTENDED, NOT_EXTENDED) \
+ XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \
+ XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \
+ XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \
+ XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \
+ XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \
+ XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \
+ XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \
+ XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \
+ XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \
+ XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \
+ XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \
+ XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \
+ XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \
+ XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif /* LLLLHTTP_C_HEADERS_ */
+
+
+#ifndef INCLUDE_LLHTTP_API_H_
+#define INCLUDE_LLHTTP_API_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include
+
+#if defined(__wasm__)
+#define LLHTTP_EXPORT __attribute__((visibility("default")))
+#elif defined(_WIN32)
+#define LLHTTP_EXPORT __declspec(dllexport)
+#else
+#define LLHTTP_EXPORT
+#endif
+
+typedef llhttp__internal_t llhttp_t;
+typedef struct llhttp_settings_s llhttp_settings_t;
+
+typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
+typedef int (*llhttp_cb)(llhttp_t*);
+
+struct llhttp_settings_s {
+ /* Possible return values 0, -1, `HPE_PAUSED` */
+ llhttp_cb on_message_begin;
+
+ /* Possible return values 0, -1, HPE_USER */
+ llhttp_data_cb on_url;
+ llhttp_data_cb on_status;
+ llhttp_data_cb on_method;
+ llhttp_data_cb on_version;
+ llhttp_data_cb on_header_field;
+ llhttp_data_cb on_header_value;
+ llhttp_data_cb on_chunk_extension_name;
+ llhttp_data_cb on_chunk_extension_value;
+
+ /* Possible return values:
+ * 0 - Proceed normally
+ * 1 - Assume that request/response has no body, and proceed to parsing the
+ * next message
+ * 2 - Assume absence of body (as above) and make `llhttp_execute()` return
+ * `HPE_PAUSED_UPGRADE`
+ * -1 - Error
+ * `HPE_PAUSED`
+ */
+ llhttp_cb on_headers_complete;
+
+ /* Possible return values 0, -1, HPE_USER */
+ llhttp_data_cb on_body;
+
+ /* Possible return values 0, -1, `HPE_PAUSED` */
+ llhttp_cb on_message_complete;
+ llhttp_cb on_url_complete;
+ llhttp_cb on_status_complete;
+ llhttp_cb on_method_complete;
+ llhttp_cb on_version_complete;
+ llhttp_cb on_header_field_complete;
+ llhttp_cb on_header_value_complete;
+ llhttp_cb on_chunk_extension_name_complete;
+ llhttp_cb on_chunk_extension_value_complete;
+
+ /* When on_chunk_header is called, the current chunk length is stored
+ * in parser->content_length.
+ * Possible return values 0, -1, `HPE_PAUSED`
+ */
+ llhttp_cb on_chunk_header;
+ llhttp_cb on_chunk_complete;
+ llhttp_cb on_reset;
+};
+
+/* Initialize the parser with specific type and user settings.
+ *
+ * NOTE: lifetime of `settings` has to be at least the same as the lifetime of
+ * the `parser` here. In practice, `settings` has to be either a static
+ * variable or be allocated with `malloc`, `new`, etc.
+ */
+LLHTTP_EXPORT
+void llhttp_init(llhttp_t* parser, llhttp_type_t type,
+ const llhttp_settings_t* settings);
+
+LLHTTP_EXPORT
+llhttp_t* llhttp_alloc(llhttp_type_t type);
+
+LLHTTP_EXPORT
+void llhttp_free(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_type(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_http_major(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_http_minor(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_method(llhttp_t* parser);
+
+LLHTTP_EXPORT
+int llhttp_get_status_code(llhttp_t* parser);
+
+LLHTTP_EXPORT
+uint8_t llhttp_get_upgrade(llhttp_t* parser);
+
+/* Reset an already initialized parser back to the start state, preserving the
+ * existing parser type, callback settings, user data, and lenient flags.
+ */
+LLHTTP_EXPORT
+void llhttp_reset(llhttp_t* parser);
+
+/* Initialize the settings object */
+LLHTTP_EXPORT
+void llhttp_settings_init(llhttp_settings_t* settings);
+
+/* Parse full or partial request/response, invoking user callbacks along the
+ * way.
+ *
+ * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
+ * interrupts, and such errno is returned from `llhttp_execute()`. If
+ * `HPE_PAUSED` was used as a errno, the execution can be resumed with
+ * `llhttp_resume()` call.
+ *
+ * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
+ * is returned after fully parsing the request/response. If the user wishes to
+ * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
+ *
+ * NOTE: if this function ever returns a non-pause type error, it will continue
+ * to return the same error upon each successive call up until `llhttp_init()`
+ * is called.
+ */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
+
+/* This method should be called when the other side has no further bytes to
+ * send (e.g. shutdown of readable side of the TCP connection.)
+ *
+ * Requests without `Content-Length` and other messages might require treating
+ * all incoming bytes as the part of the body, up to the last byte of the
+ * connection. This method will invoke `on_message_complete()` callback if the
+ * request was terminated safely. Otherwise a error code would be returned.
+ */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_finish(llhttp_t* parser);
+
+/* Returns `1` if the incoming message is parsed until the last byte, and has
+ * to be completed by calling `llhttp_finish()` on EOF
+ */
+LLHTTP_EXPORT
+int llhttp_message_needs_eof(const llhttp_t* parser);
+
+/* Returns `1` if there might be any other messages following the last that was
+ * successfully parsed.
+ */
+LLHTTP_EXPORT
+int llhttp_should_keep_alive(const llhttp_t* parser);
+
+/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
+ * appropriate error reason.
+ *
+ * Important: do not call this from user callbacks! User callbacks must return
+ * `HPE_PAUSED` if pausing is required.
+ */
+LLHTTP_EXPORT
+void llhttp_pause(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
+ */
+LLHTTP_EXPORT
+void llhttp_resume(llhttp_t* parser);
+
+/* Might be called to resume the execution after the pause in user's callback.
+ * See `llhttp_execute()` above for details.
+ *
+ * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
+ */
+LLHTTP_EXPORT
+void llhttp_resume_after_upgrade(llhttp_t* parser);
+
+/* Returns the latest return error */
+LLHTTP_EXPORT
+llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
+
+/* Returns the verbal explanation of the latest returned error.
+ *
+ * Note: User callback should set error reason when returning the error. See
+ * `llhttp_set_error_reason()` for details.
+ */
+LLHTTP_EXPORT
+const char* llhttp_get_error_reason(const llhttp_t* parser);
+
+/* Assign verbal description to the returned error. Must be called in user
+ * callbacks right before returning the errno.
+ *
+ * Note: `HPE_USER` error code might be useful in user callbacks.
+ */
+LLHTTP_EXPORT
+void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
+
+/* Returns the pointer to the last parsed byte before the returned error. The
+ * pointer is relative to the `data` argument of `llhttp_execute()`.
+ *
+ * Note: this method might be useful for counting the number of parsed bytes.
+ */
+LLHTTP_EXPORT
+const char* llhttp_get_error_pos(const llhttp_t* parser);
+
+/* Returns textual name of error code */
+LLHTTP_EXPORT
+const char* llhttp_errno_name(llhttp_errno_t err);
+
+/* Returns textual name of HTTP method */
+LLHTTP_EXPORT
+const char* llhttp_method_name(llhttp_method_t method);
+
+/* Returns textual name of HTTP status */
+LLHTTP_EXPORT
+const char* llhttp_status_name(llhttp_status_t status);
+
+/* Enables/disables lenient header value parsing (disabled by default).
+ *
+ * Lenient parsing disables header value token checks, extending llhttp's
+ * protocol support to highly non-compliant clients/server. No
+ * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
+ * lenient parsing is "on".
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
+
+
+/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
+ * `Content-Length` headers (disabled by default).
+ *
+ * Normally `llhttp` would error when `Transfer-Encoding` is present in
+ * conjunction with `Content-Length`. This error is important to prevent HTTP
+ * request smuggling, but may be less desirable for small number of cases
+ * involving legacy servers.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
+
+
+/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
+ * requests responses.
+ *
+ * Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
+ * the HTTP request/response after the request/response with `Connection: close`
+ * and `Content-Length`. This is important to prevent cache poisoning attacks,
+ * but might interact badly with outdated and insecure clients. With this flag
+ * the extra request/response will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * poisoning attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of `Transfer-Encoding` header.
+ *
+ * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
+ * and another value after it (either in a single header or in multiple
+ * headers whose value are internally joined using `, `).
+ * This is mandated by the spec to reliably determine request body size and thus
+ * avoid request smuggling.
+ * With this flag the extra value will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of HTTP version.
+ *
+ * Normally `llhttp` would error when the HTTP version in the request or status line
+ * is not `0.9`, `1.0`, `1.1` or `2.0`.
+ * With this flag the invalid value will be parsed normally.
+ *
+ * **Enabling this flag can pose a security issue since you will allow unsupported
+ * HTTP versions. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_version(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of additional data received after a message ends
+ * and keep-alive is disabled.
+ *
+ * Normally `llhttp` would error when additional unexpected data is received if the message
+ * contains the `Connection` header with `close` value.
+ * With this flag the extra data will discarded without throwing an error.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * poisoning attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of incomplete CRLF sequences.
+ *
+ * Normally `llhttp` would error when a CR is not followed by LF when terminating the
+ * request line, the status line, the headers or a chunk header.
+ * With this flag only a CR is required to terminate such sections.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);
+
+/*
+ * Enables/disables lenient handling of line separators.
+ *
+ * Normally `llhttp` would error when a LF is not preceded by CR when terminating the
+ * request line, the status line, the headers, a chunk header or a chunk data.
+ * With this flag only a LF is required to terminate such sections.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of chunks not separated via CRLF.
+ *
+ * Normally `llhttp` would error when after a chunk data a CRLF is missing before
+ * starting a new chunk.
+ * With this flag the new chunk can start immediately after the previous one.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);
+
+/* Enables/disables lenient handling of spaces after chunk size.
+ *
+ * Normally `llhttp` would error when after a chunk size is followed by one or more
+ * spaces are present instead of a CRLF or `;`.
+ * With this flag this check is disabled.
+ *
+ * **Enabling this flag can pose a security issue since you will be exposed to
+ * request smuggling attacks. USE WITH CAUTION!**
+ */
+LLHTTP_EXPORT
+void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif /* INCLUDE_LLHTTP_API_H_ */
+
+
+#endif /* INCLUDE_LLHTTP_H_ */
diff --git a/lib/logger.c b/lib/logger.c
new file mode 100644
index 0000000..fb3f560
--- /dev/null
+++ b/lib/logger.c
@@ -0,0 +1,155 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *===============================================================
+ * modified by fduncanh 2023
+ */
+
+#include
+#include
+#include
+#include
+
+#include "logger.h"
+#include "compat.h"
+
+struct logger_s {
+ mutex_handle_t lvl_mutex;
+ mutex_handle_t cb_mutex;
+
+ int level;
+ void *cls;
+ logger_callback_t callback;
+};
+
+logger_t *
+logger_init()
+{
+ logger_t *logger = calloc(1, sizeof(logger_t));
+ assert(logger);
+
+ MUTEX_CREATE(logger->lvl_mutex);
+ MUTEX_CREATE(logger->cb_mutex);
+
+ logger->level = LOGGER_WARNING;
+ logger->callback = NULL;
+ return logger;
+}
+
+void
+logger_destroy(logger_t *logger)
+{
+ MUTEX_DESTROY(logger->lvl_mutex);
+ MUTEX_DESTROY(logger->cb_mutex);
+ free(logger);
+}
+
+void
+logger_set_level(logger_t *logger, int level)
+{
+ assert(logger);
+
+ MUTEX_LOCK(logger->lvl_mutex);
+ logger->level = level;
+ MUTEX_UNLOCK(logger->lvl_mutex);
+}
+
+int
+logger_get_level(logger_t *logger)
+{
+ int level;
+ assert(logger);
+
+ MUTEX_LOCK(logger->lvl_mutex);
+ level = logger->level;
+ MUTEX_UNLOCK(logger->lvl_mutex);
+
+ return level;
+}
+
+void
+logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls)
+{
+ assert(logger);
+
+ MUTEX_LOCK(logger->cb_mutex);
+ logger->cls = cls;
+ logger->callback = callback;
+ MUTEX_UNLOCK(logger->cb_mutex);
+}
+
+static char *
+logger_utf8_to_local(const char *str)
+{
+ char *ret = NULL;
+
+/* FIXME: This is only implemented on Windows for now */
+#if defined(_WIN32) || defined(_WIN64)
+ int wclen, mblen;
+ WCHAR *wcstr;
+ BOOL failed;
+
+ wclen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
+ wcstr = malloc(sizeof(WCHAR) * wclen);
+ MultiByteToWideChar(CP_UTF8, 0, str, -1, wcstr, wclen);
+
+ mblen = WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, NULL, 0, NULL, &failed);
+ if (failed) {
+ /* Invalid characters in input, conversion failed */
+ free(wcstr);
+ return NULL;
+ }
+
+ ret = malloc(sizeof(CHAR) * mblen);
+ WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, ret, mblen, NULL, NULL);
+ free(wcstr);
+#endif
+
+ return ret;
+}
+
+void
+logger_log(logger_t *logger, int level, const char *fmt, ...)
+{
+ char buffer[4096];
+ va_list ap;
+
+ MUTEX_LOCK(logger->lvl_mutex);
+ if (level > logger->level) {
+ MUTEX_UNLOCK(logger->lvl_mutex);
+ return;
+ }
+ MUTEX_UNLOCK(logger->lvl_mutex);
+
+ buffer[sizeof(buffer)-1] = '\0';
+ va_start(ap, fmt);
+ vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
+ va_end(ap);
+
+ MUTEX_LOCK(logger->cb_mutex);
+ if (logger->callback) {
+ logger->callback(logger->cls, level, buffer);
+ MUTEX_UNLOCK(logger->cb_mutex);
+ } else {
+ char *local;
+ MUTEX_UNLOCK(logger->cb_mutex);
+ local = logger_utf8_to_local(buffer);
+ if (local) {
+ fprintf(stderr, "%s\n", local);
+ free(local);
+ } else {
+ fprintf(stderr, "%s\n", buffer);
+ }
+ }
+}
+
diff --git a/lib/logger.h b/lib/logger.h
new file mode 100644
index 0000000..970344f
--- /dev/null
+++ b/lib/logger.h
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2023
+ */
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Define syslog style log levels */
+#define LOGGER_EMERG 0 /* system is unusable */
+#define LOGGER_ALERT 1 /* action must be taken immediately */
+#define LOGGER_CRIT 2 /* critical conditions */
+#define LOGGER_ERR 3 /* error conditions */
+#define LOGGER_WARNING 4 /* warning conditions */
+#define LOGGER_NOTICE 5 /* normal but significant condition */
+#define LOGGER_INFO 6 /* informational */
+#define LOGGER_DEBUG 7 /* debug-level messages */
+
+typedef void (*logger_callback_t)(void *cls, int level, const char *msg);
+
+typedef struct logger_s logger_t;
+
+logger_t *logger_init();
+void logger_destroy(logger_t *logger);
+
+void logger_set_level(logger_t *logger, int level);
+int logger_get_level(logger_t *logger);
+void logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls);
+
+void logger_log(logger_t *logger, int level, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/mirror_buffer.c b/lib/mirror_buffer.c
new file mode 100644
index 0000000..b88fe24
--- /dev/null
+++ b/lib/mirror_buffer.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2019 dsafa22, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *================================================================
+ * modified by fduncanh 2022
+ */
+
+#include "mirror_buffer.h"
+#include "raop_rtp.h"
+#include "raop_rtp.h"
+#include
+#include "crypto.h"
+#include "compat.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct mirror_buffer_s {
+ logger_t *logger;
+ aes_ctx_t *aes_ctx;
+ int nextDecryptCount;
+ uint8_t og[16];
+ /* audio aes key is used in a hash for the video aes key and iv */
+ unsigned char aeskey_audio[RAOP_AESKEY_LEN];
+};
+
+void
+mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, const uint64_t *streamConnectionID)
+{
+ unsigned char aeskey_video[64];
+ unsigned char aesiv_video[64];
+
+ assert(mirror_buffer);
+ assert(streamConnectionID);
+
+ /* AES key and IV */
+ // Need secondary processing to use
+
+ snprintf((char*) aeskey_video, sizeof(aeskey_video), "AirPlayStreamKey%" PRIu64, *streamConnectionID);
+ snprintf((char*) aesiv_video, sizeof(aesiv_video), "AirPlayStreamIV%" PRIu64, *streamConnectionID);
+
+ sha_ctx_t *ctx = sha_init();
+ sha_update(ctx, aeskey_video, strlen((char*) aeskey_video));
+ sha_update(ctx, mirror_buffer->aeskey_audio, RAOP_AESKEY_LEN);
+ sha_final(ctx, aeskey_video, NULL);
+
+ sha_reset(ctx);
+ sha_update(ctx, aesiv_video, strlen((char*) aesiv_video));
+ sha_update(ctx, mirror_buffer->aeskey_audio, RAOP_AESKEY_LEN);
+ sha_final(ctx, aesiv_video, NULL);
+ sha_destroy(ctx);
+
+ // Need to be initialized externally
+ mirror_buffer->aes_ctx = aes_ctr_init(aeskey_video, aesiv_video);
+}
+
+mirror_buffer_t *
+mirror_buffer_init(logger_t *logger, const unsigned char *aeskey)
+{
+ mirror_buffer_t *mirror_buffer;
+ assert(aeskey);
+ mirror_buffer = calloc(1, sizeof(mirror_buffer_t));
+ if (!mirror_buffer) {
+ return NULL;
+ }
+ memcpy(mirror_buffer->aeskey_audio, aeskey, RAOP_AESKEY_LEN);
+ mirror_buffer->logger = logger;
+ mirror_buffer->nextDecryptCount = 0;
+ return mirror_buffer;
+}
+
+void mirror_buffer_decrypt(mirror_buffer_t *mirror_buffer, unsigned char* input, unsigned char* output, int inputLen) {
+ // Start decrypting
+ if (mirror_buffer->nextDecryptCount > 0) {//mirror_buffer->nextDecryptCount = 10
+ for (int i = 0; i < mirror_buffer->nextDecryptCount; i++) {
+ output[i] = (input[i] ^ mirror_buffer->og[(16 - mirror_buffer->nextDecryptCount) + i]);
+ }
+ }
+ // Handling encrypted bytes
+ int encryptlen = ((inputLen - mirror_buffer->nextDecryptCount) / 16) * 16;
+ // Aes decryption
+ aes_ctr_start_fresh_block(mirror_buffer->aes_ctx);
+ aes_ctr_decrypt(mirror_buffer->aes_ctx, input + mirror_buffer->nextDecryptCount,
+ input + mirror_buffer->nextDecryptCount, encryptlen);
+ // Copy to output
+ memcpy(output + mirror_buffer->nextDecryptCount, input + mirror_buffer->nextDecryptCount, encryptlen);
+ // int outputlength = mirror_buffer->nextDecryptCount + encryptlen;
+ // Processing remaining length
+ int restlen = (inputLen - mirror_buffer->nextDecryptCount) % 16;
+ int reststart = inputLen - restlen;
+ mirror_buffer->nextDecryptCount = 0;
+ if (restlen > 0) {
+ memset(mirror_buffer->og, 0, 16);
+ memcpy(mirror_buffer->og, input + reststart, restlen);
+ aes_ctr_decrypt(mirror_buffer->aes_ctx, mirror_buffer->og, mirror_buffer->og, 16);
+ for (int j = 0; j < restlen; j++) {
+ output[reststart + j] = mirror_buffer->og[j];
+ }
+ //outputlength += restlen;
+ mirror_buffer->nextDecryptCount = 16 - restlen;// Difference 16-6=10 bytes
+ }
+}
+
+void
+mirror_buffer_destroy(mirror_buffer_t *mirror_buffer)
+{
+ if (mirror_buffer) {
+ aes_ctr_destroy(mirror_buffer->aes_ctx);
+ free(mirror_buffer);
+ }
+}
diff --git a/lib/mirror_buffer.h b/lib/mirror_buffer.h
new file mode 100644
index 0000000..2b65ab0
--- /dev/null
+++ b/lib/mirror_buffer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 dsafa22, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2022
+ */
+
+#ifndef MIRROR_BUFFER_H
+#define MIRROR_BUFFER_H
+
+#include
+#include "logger.h"
+
+typedef struct mirror_buffer_s mirror_buffer_t;
+
+
+mirror_buffer_t *mirror_buffer_init( logger_t *logger, const unsigned char *aeskey);
+void mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, const uint64_t *streamConnectionID);
+void mirror_buffer_decrypt(mirror_buffer_t *raop_mirror, unsigned char* input, unsigned char* output, int datalen);
+void mirror_buffer_destroy(mirror_buffer_t *mirror_buffer);
+#endif //MIRROR_BUFFER_H
diff --git a/lib/netutils.c b/lib/netutils.c
new file mode 100644
index 0000000..642efba
--- /dev/null
+++ b/lib/netutils.c
@@ -0,0 +1,212 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2022
+ */
+
+#include
+#include
+#include
+
+#include "compat.h"
+
+int
+netutils_init()
+{
+#ifdef WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int ret;
+
+ wVersionRequested = MAKEWORD(2, 2);
+ ret = WSAStartup(wVersionRequested, &wsaData);
+ if (ret) {
+ return -1;
+ }
+
+ if (LOBYTE(wsaData.wVersion) != 2 ||
+ HIBYTE(wsaData.wVersion) != 2) {
+ /* Version mismatch, requested version not found */
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+void
+netutils_cleanup()
+{
+#ifdef WIN32
+ WSACleanup();
+#endif
+}
+
+unsigned char *
+netutils_get_address(void *sockaddr, int *length, unsigned int *zone_id)
+{
+ unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
+ struct sockaddr *address = sockaddr;
+
+ assert(address);
+ assert(length);
+ assert(zone_id);
+ if (address->sa_family == AF_INET) {
+ struct sockaddr_in *sin;
+ *zone_id = 0;
+ sin = (struct sockaddr_in *)address;
+ *length = sizeof(sin->sin_addr.s_addr);
+ return (unsigned char *)&sin->sin_addr.s_addr;
+ } else if (address->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)address;
+ if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
+ /* Actually an embedded IPv4 address */
+ *zone_id = 0;
+ *length = sizeof(sin6->sin6_addr.s6_addr)-12;
+ return (sin6->sin6_addr.s6_addr+12);
+ }
+ *zone_id = (unsigned int) sin6->sin6_scope_id;
+ *length = sizeof(sin6->sin6_addr.s6_addr);
+ return sin6->sin6_addr.s6_addr;
+ }
+
+ *length = 0;
+ return NULL;
+}
+
+int
+netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
+{
+ int family = use_ipv6 ? AF_INET6 : AF_INET;
+ int type = use_udp ? SOCK_DGRAM : SOCK_STREAM;
+ int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP;
+
+ struct sockaddr_storage saddr;
+ socklen_t socklen;
+ int server_fd;
+ int ret;
+#ifndef _WIN32
+ int reuseaddr = 1;
+#else
+ const char reuseaddr = 1;
+#endif
+
+ assert(port);
+
+ server_fd = socket(family, type, proto);
+ if (server_fd == -1) {
+ goto cleanup;
+ }
+
+ ret = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof (reuseaddr));
+ if (ret == -1) {
+ goto cleanup;
+ }
+
+ memset(&saddr, 0, sizeof(saddr));
+ if (use_ipv6) {
+ struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
+
+ /* Initialize sockaddr for bind */
+ sin6ptr->sin6_family = family;
+ sin6ptr->sin6_addr = in6addr_any;
+ sin6ptr->sin6_port = htons(*port);
+
+#ifndef _WIN32
+ int v6only = 1;
+ /* Make sure we only listen to IPv6 addresses */
+ setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *) &v6only, sizeof(v6only));
+#endif
+
+ socklen = sizeof(*sin6ptr);
+ ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
+ if (ret == -1) {
+ goto cleanup;
+ }
+
+ ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
+ if (ret == -1) {
+ goto cleanup;
+ }
+ *port = ntohs(sin6ptr->sin6_port);
+ } else {
+ struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
+
+ /* Initialize sockaddr for bind */
+ sinptr->sin_family = family;
+ sinptr->sin_addr.s_addr = INADDR_ANY;
+ sinptr->sin_port = htons(*port);
+
+ socklen = sizeof(*sinptr);
+ ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
+ if (ret == -1) {
+ goto cleanup;
+ }
+
+ ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
+ if (ret == -1) {
+ goto cleanup;
+ }
+ *port = ntohs(sinptr->sin_port);
+ }
+ return server_fd;
+
+ cleanup:
+ ret = SOCKET_GET_ERROR();
+ if (server_fd != -1) {
+ closesocket(server_fd);
+ }
+ SOCKET_SET_ERROR(ret);
+ return -1;
+}
+
+// Src is the ip address
+int
+netutils_parse_address(int family, const char *src, void *dst, int dstlen)
+{
+ struct addrinfo *result;
+ struct addrinfo *ptr;
+ struct addrinfo hints;
+ int length;
+ int ret;
+
+ if (family != AF_INET && family != AF_INET6) {
+ return -1;
+ }
+ if (!src || !dst) {
+ return -1;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+
+ ret = getaddrinfo(src, NULL, &hints, &result);
+ if (ret != 0) {
+ return -1;
+ }
+
+ length = -1;
+ for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) {
+ if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
+ memcpy(dst, ptr->ai_addr, ptr->ai_addrlen);
+ length = ptr->ai_addrlen;
+ break;
+ }
+ }
+ freeaddrinfo(result);
+ return length;
+}
diff --git a/lib/netutils.h b/lib/netutils.h
new file mode 100644
index 0000000..e64c638
--- /dev/null
+++ b/lib/netutils.h
@@ -0,0 +1,25 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef NETUTILS_H
+#define NETUTILS_H
+
+int netutils_init();
+void netutils_cleanup();
+
+int netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp);
+unsigned char *netutils_get_address(void *sockaddr, int *length, unsigned int *zone_id);
+int netutils_parse_address(int family, const char *src, void *dst, int dstlen);
+
+#endif
diff --git a/lib/pairing.c b/lib/pairing.c
new file mode 100644
index 0000000..e60273c
--- /dev/null
+++ b/lib/pairing.c
@@ -0,0 +1,466 @@
+/**
+ * Copyright (C) 2018 Juho Vähä-Herttua
+ * Copyright (C) 2020 Jaslo Ziska
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *==================================================================
+ * modified by fduncanh 2021, 2023
+ */
+
+#include
+#include
+#include
+#include
+#include // for SHA512_DIGEST_LENGTH
+
+#include "pairing.h"
+#include "crypto.h"
+#include "srp.h"
+
+#define SALT_KEY "Pair-Verify-AES-Key"
+#define SALT_IV "Pair-Verify-AES-IV"
+
+typedef struct srp_s {
+ unsigned char salt[SRP_SALT_SIZE];
+ unsigned char verifier[SRP_VERIFIER_SIZE];
+ unsigned char session_key[SRP_SESSION_KEY_SIZE];
+ unsigned char private_key[SRP_PRIVATE_KEY_SIZE];
+} srp_t;
+
+struct pairing_s {
+ ed25519_key_t *ed;
+};
+
+typedef enum {
+ STATUS_INITIAL,
+ STATUS_SETUP,
+ STATUS_HANDSHAKE,
+ STATUS_FINISHED
+} status_t;
+
+struct pairing_session_s {
+ status_t status;
+
+ ed25519_key_t *ed_ours;
+ ed25519_key_t *ed_theirs;
+
+ x25519_key_t *ecdh_ours;
+ x25519_key_t *ecdh_theirs;
+ unsigned char ecdh_secret[X25519_KEY_SIZE];
+
+ char username[SRP_USERNAME_SIZE + 1];
+ unsigned char client_pk[ED25519_KEY_SIZE];
+ bool pair_setup;
+
+ /* srp items */
+ srp_t *srp;
+};
+
+static int
+derive_key_internal(pairing_session_t *session, const unsigned char *salt, unsigned int saltlen, unsigned char *key, unsigned int keylen)
+{
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+
+ if (keylen > sizeof(hash)) {
+ return -1;
+ }
+
+ sha_ctx_t *ctx = sha_init();
+ sha_update(ctx, salt, saltlen);
+ sha_update(ctx, session->ecdh_secret, X25519_KEY_SIZE);
+ sha_final(ctx, hash, NULL);
+ sha_destroy(ctx);
+
+ memcpy(key, hash, keylen);
+ return 0;
+}
+
+pairing_t *
+pairing_init_generate(const char *device_id, const char *keyfile, int *result)
+{
+ pairing_t *pairing;
+ *result = 0;
+ pairing = calloc(1, sizeof(pairing_t));
+ if (!pairing) {
+ return NULL;
+ }
+
+ pairing->ed = ed25519_key_generate(device_id, keyfile, result);
+
+ return pairing;
+}
+
+void
+pairing_get_public_key(pairing_t *pairing, unsigned char public_key[ED25519_KEY_SIZE])
+{
+ assert(pairing);
+ ed25519_key_get_raw(public_key, pairing->ed);
+}
+
+int
+pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE])
+{
+ assert(session);
+ switch (session->status) {
+ case STATUS_INITIAL:
+ return 0;
+ default:
+ memcpy(ecdh_secret, session->ecdh_secret, X25519_KEY_SIZE);
+ return 1;
+ }
+}
+
+pairing_session_t *
+pairing_session_init(pairing_t *pairing)
+{
+ pairing_session_t *session;
+
+ if (!pairing) {
+ return NULL;
+ }
+
+ session = calloc(1, sizeof(pairing_session_t));
+ if (!session) {
+ return NULL;
+ }
+
+ session->ed_ours = ed25519_key_copy(pairing->ed);
+
+ session->status = STATUS_INITIAL;
+ session->srp = NULL;
+ session->pair_setup = false;
+ return session;
+}
+
+void
+pairing_session_set_setup_status(pairing_session_t *session)
+{
+ assert(session);
+ session->status = STATUS_SETUP;
+}
+
+int
+pairing_session_check_handshake_status(pairing_session_t *session)
+{
+ assert(session);
+ switch (session->status) {
+ case STATUS_SETUP:
+ case STATUS_HANDSHAKE:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+int
+pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[X25519_KEY_SIZE],
+ const unsigned char ed_key[ED25519_KEY_SIZE])
+{
+ assert(session);
+
+ if (session->status == STATUS_FINISHED) {
+ return -1;
+ }
+
+ session->ecdh_theirs = x25519_key_from_raw(ecdh_key);
+ session->ed_theirs = ed25519_key_from_raw(ed_key);
+
+ session->ecdh_ours = x25519_key_generate();
+
+ x25519_derive_secret(session->ecdh_secret, session->ecdh_ours, session->ecdh_theirs);
+
+ session->status = STATUS_HANDSHAKE;
+ return 0;
+}
+
+int
+pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[X25519_KEY_SIZE])
+{
+ assert(session);
+
+ if (session->status != STATUS_HANDSHAKE) {
+ return -1;
+ }
+
+ x25519_key_get_raw(ecdh_key, session->ecdh_ours);
+
+ return 0;
+}
+
+int
+pairing_session_get_signature(pairing_session_t *session, unsigned char signature[PAIRING_SIG_SIZE])
+{
+ unsigned char sig_msg[PAIRING_SIG_SIZE];
+ unsigned char key[AES_128_BLOCK_SIZE];
+ unsigned char iv[AES_128_BLOCK_SIZE];
+ aes_ctx_t *aes_ctx;
+
+ assert(session);
+
+ if (session->status != STATUS_HANDSHAKE) {
+ return -1;
+ }
+
+ /* First sign the public ECDH keys of both parties */
+ x25519_key_get_raw(sig_msg, session->ecdh_ours);
+ x25519_key_get_raw(sig_msg + X25519_KEY_SIZE, session->ecdh_theirs);
+
+ ed25519_sign(signature, PAIRING_SIG_SIZE, sig_msg, PAIRING_SIG_SIZE, session->ed_ours);
+
+ /* Then encrypt the result with keys derived from the shared secret */
+ derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key));
+ derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(iv));
+
+ aes_ctx = aes_ctr_init(key, iv);
+ aes_ctr_encrypt(aes_ctx, signature, signature, PAIRING_SIG_SIZE);
+ aes_ctr_destroy(aes_ctx);
+
+ return 0;
+}
+
+int
+pairing_session_finish(pairing_session_t *session, const unsigned char signature[PAIRING_SIG_SIZE])
+{
+ unsigned char sig_buffer[PAIRING_SIG_SIZE];
+ unsigned char sig_msg[PAIRING_SIG_SIZE];
+ unsigned char key[AES_128_BLOCK_SIZE];
+ unsigned char iv[AES_128_BLOCK_SIZE];
+ aes_ctx_t *aes_ctx;
+
+ assert(session);
+
+ if (session->status != STATUS_HANDSHAKE) {
+ return -1;
+ }
+
+ /* First decrypt the signature with keys derived from the shared secret */
+ derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key));
+ derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(iv));
+
+ aes_ctx = aes_ctr_init(key, iv);
+ /* One fake round for the initial handshake encryption */
+ aes_ctr_encrypt(aes_ctx, sig_buffer, sig_buffer, PAIRING_SIG_SIZE);
+ aes_ctr_encrypt(aes_ctx, signature, sig_buffer, PAIRING_SIG_SIZE);
+ aes_ctr_destroy(aes_ctx);
+
+ /* Then verify the signature with public ECDH keys of both parties */
+ x25519_key_get_raw(sig_msg, session->ecdh_theirs);
+ x25519_key_get_raw(sig_msg + X25519_KEY_SIZE, session->ecdh_ours);
+
+ if (!ed25519_verify(sig_buffer, PAIRING_SIG_SIZE, sig_msg, PAIRING_SIG_SIZE, session->ed_theirs)) {
+ return -2;
+ }
+
+ session->status = STATUS_FINISHED;
+ return 0;
+}
+
+void
+pairing_session_destroy(pairing_session_t *session)
+{
+ if (session) {
+ ed25519_key_destroy(session->ed_ours);
+ ed25519_key_destroy(session->ed_theirs);
+
+ x25519_key_destroy(session->ecdh_ours);
+ x25519_key_destroy(session->ecdh_theirs);
+ if (session->srp) {
+ free(session->srp);
+ session->srp = NULL;
+ }
+ free(session);
+ }
+}
+
+void
+pairing_destroy(pairing_t *pairing)
+{
+ if (pairing) {
+ ed25519_key_destroy(pairing->ed);
+ free(pairing);
+ }
+}
+
+int
+random_pin() {
+ unsigned char random_bytes[2] = { 0 };
+ unsigned short random_short = 0;
+ int ret;
+ /* create a random unsigned short in range 1-9999 */
+ while (!random_short) {
+ if ((ret = get_random_bytes(random_bytes, sizeof(random_bytes)) < 1)) {
+ return -1;
+ }
+ memcpy(&random_short, random_bytes, sizeof(random_bytes));
+ random_short = random_short % 10000;
+ }
+ return (int) random_short;
+}
+
+int
+srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin,
+ const char **salt, int *len_salt, const char **pk, int *len_pk) {
+ if (strlen(device_id) > SRP_USERNAME_SIZE) {
+ return -1;
+ }
+
+ strncpy(session->username, device_id, SRP_USERNAME_SIZE);
+
+ if (session->srp) {
+ free (session->srp);
+ session->srp = NULL;
+ }
+ session->srp = (srp_t *) calloc(1, sizeof(srp_t));
+ if (!session->srp) {
+ return -2;
+ }
+
+ get_random_bytes(session->srp->private_key, SRP_PRIVATE_KEY_SIZE);
+
+ const unsigned char *srp_b = session->srp->private_key;
+ unsigned char * srp_B;
+ unsigned char * srp_s;
+ unsigned char * srp_v;
+ int len_b = SRP_PRIVATE_KEY_SIZE;
+ int len_B;
+ int len_s;
+ int len_v;
+ srp_create_salted_verification_key(SRP_SHA, SRP_NG, device_id,
+ (const unsigned char *) pin, strlen (pin),
+ (const unsigned char **) &srp_s, &len_s,
+ (const unsigned char **) &srp_v, &len_v,
+ NULL, NULL);
+ if (len_s != SRP_SALT_SIZE || len_v != SRP_VERIFIER_SIZE) {
+ return -3;
+ }
+
+ memcpy(session->srp->salt, srp_s, SRP_SALT_SIZE);
+ memcpy(session->srp->verifier, srp_v, SRP_VERIFIER_SIZE);
+
+ *salt = (char *) session->srp->salt;
+ *len_salt = len_s;
+
+ srp_create_server_ephemeral_key(SRP_SHA, SRP_NG,
+ srp_v, len_v,
+ srp_b, len_b,
+ (const unsigned char **) &srp_B, &len_B,
+ NULL, NULL, 1);
+
+ *pk = (char *) srp_B;
+ *len_pk = len_B;
+
+ return 0;
+}
+
+int
+srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A,
+ int len_A, unsigned char *proof, int client_proof_len, int proof_len) {
+ int authenticated = 0;
+ const unsigned char *B = NULL;
+ const unsigned char *b = session->srp->private_key;
+ int len_b = SRP_PRIVATE_KEY_SIZE;
+ int len_B = 0;
+ int len_K = 0;
+ const unsigned char *session_key = NULL;
+ const unsigned char *M2 = NULL;
+
+ struct SRPVerifier *verifier = srp_verifier_new(SRP_SHA, SRP_NG, (const char *) session->username,
+ (const unsigned char *) session->srp->salt, SRP_SALT_SIZE,
+ (const unsigned char *) session->srp->verifier, SRP_VERIFIER_SIZE,
+ A, len_A,
+ b, len_b,
+ &B, &len_B, NULL, NULL, 1);
+
+ srp_verifier_verify_session(verifier, proof, &M2);
+ authenticated = srp_verifier_is_authenticated(verifier);
+ if (authenticated == 0) {
+ /* HTTP 470 should be sent to client if not verified.*/
+ srp_verifier_delete(verifier);
+ free (session->srp);
+ session->srp = NULL;
+ return -1;
+ }
+ session_key = srp_verifier_get_session_key(verifier, &len_K);
+ if (len_K != SRP_SESSION_KEY_SIZE) {
+ return -2;
+ }
+ memcpy(session->srp->session_key, session_key, len_K);
+ memcpy(proof, M2, proof_len);
+ srp_verifier_delete(verifier);
+ return 0;
+}
+int
+srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing,
+ unsigned char *epk, unsigned char *auth_tag) {
+ unsigned char aesKey[16], aesIV[16];
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ unsigned char pk[ED25519_KEY_SIZE];
+ int pk_len_client, epk_len;
+ /* decrypt client epk to get client pk, authenticate with auth_tag*/
+
+ const char *salt = "Pair-Setup-AES-Key";
+ sha_ctx_t *ctx = sha_init();
+ sha_update(ctx, (const unsigned char *) salt, strlen(salt));
+ sha_update(ctx, session->srp->session_key, SRP_SESSION_KEY_SIZE);
+ sha_final(ctx, hash, NULL);
+ sha_destroy(ctx);
+ memcpy(aesKey, hash, 16);
+
+ salt = "Pair-Setup-AES-IV";
+ ctx = sha_init();
+ sha_update(ctx, (const unsigned char *) salt, strlen(salt));
+ sha_update(ctx, session->srp->session_key, SRP_SESSION_KEY_SIZE);
+ sha_final(ctx, hash, NULL);
+ sha_destroy(ctx);
+ memcpy(aesIV, hash, 16);
+ aesIV[15]++;
+
+ /* SRP6a data is no longer needed */
+ free(session->srp);
+ session->srp = NULL;
+
+ /* decrypt client epk to authenticate client using auth_tag */
+ pk_len_client = gcm_decrypt(epk, ED25519_KEY_SIZE, pk, aesKey, aesIV, auth_tag);
+ if (pk_len_client <= 0) {
+ /* authentication failed */
+ return pk_len_client;
+ }
+
+ /* success, from server viewpoint */
+ memcpy(session->client_pk, pk, ED25519_KEY_SIZE);
+ session->pair_setup = true;
+
+ /* encrypt server epk so client can also authenticate server using auth_tag */
+ pairing_get_public_key(pairing, pk);
+
+ /* encryption needs this previously undocumented additional "nonce" */
+ aesIV[15]++;
+ epk_len = gcm_encrypt(pk, ED25519_KEY_SIZE, epk, aesKey, aesIV, auth_tag);
+ return epk_len;
+}
+
+void access_client_session_data(pairing_session_t *session, char **username, char **client_pk64, bool *setup) {
+ int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1;
+ setup = &(session->pair_setup);
+ *username = session->username;
+ if (setup) {
+ *client_pk64 = (char *) malloc(len64);
+ pk_to_base64(session->client_pk, ED25519_KEY_SIZE, *client_pk64, len64);
+ } else {
+ *client_pk64 = NULL;
+ }
+}
+
+void ed25519_pk_to_base64(const unsigned char *pk, char **pk64) {
+ int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1;
+ *pk64 = (char *) malloc(len64);
+ pk_to_base64(pk, ED25519_KEY_SIZE, *pk64, len64);
+}
diff --git a/lib/pairing.h b/lib/pairing.h
new file mode 100644
index 0000000..54db62b
--- /dev/null
+++ b/lib/pairing.h
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2018 Juho Vähä-Herttua
+ * Copyright (C) 2020 Jaslo Ziska
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include "crypto.h"
+
+#ifndef PAIRING_H
+#define PAIRING_H
+
+#define PAIRING_SIG_SIZE (2 * X25519_KEY_SIZE)
+
+
+#define SRP_USERNAME_SIZE 24 /* accomodates up to an 8-octet MAC address */
+#define SRP_SESSION_KEY_SIZE 40
+#define SRP_VERIFIER_SIZE 256
+#define SRP_SALT_SIZE 16
+#define SRP_PK_SIZE 256
+#define SRP_SHA SRP_SHA1
+#define SRP_NG SRP_NG_2048
+#define SRP_M2_SIZE 64
+#define SRP_PRIVATE_KEY_SIZE 32
+#define GCM_AUTHTAG_SIZE 16
+#define SHA512_KEY_LENGTH 64
+
+typedef struct pairing_s pairing_t;
+typedef struct pairing_session_s pairing_session_t;
+
+pairing_t *pairing_init_generate(const char *device_id, const char *keyfile, int *result);
+void pairing_get_public_key(pairing_t *pairing, unsigned char public_key[ED25519_KEY_SIZE]);
+
+pairing_session_t *pairing_session_init(pairing_t *pairing);
+void pairing_session_set_setup_status(pairing_session_t *session);
+int pairing_session_check_handshake_status(pairing_session_t *session);
+int pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[X25519_KEY_SIZE],
+ const unsigned char ed_key[ED25519_KEY_SIZE]);
+int pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[X25519_KEY_SIZE]);
+int random_pin();
+int pairing_session_get_signature(pairing_session_t *session, unsigned char signature[PAIRING_SIG_SIZE]);
+int pairing_session_finish(pairing_session_t *session, const unsigned char signature[PAIRING_SIG_SIZE]);
+void pairing_session_destroy(pairing_session_t *session);
+
+void pairing_destroy(pairing_t *pairing);
+
+int pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE]);
+
+int srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin,
+ const char **salt, int *len_salt, const char **pk, int *len_pk);
+int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A,
+ int len_A, unsigned char *proof, int client_proof_len, int proof_len);
+int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsigned char *epk,
+ unsigned char *auth_tag);
+void access_client_session_data(pairing_session_t *session, char **username, char **client_pk, bool *setup);
+void ed25519_pk_to_base64(const unsigned char *pk, char **pk64);
+#endif
diff --git a/lib/playfair/CMakeLists.txt b/lib/playfair/CMakeLists.txt
new file mode 100644
index 0000000..163f9aa
--- /dev/null
+++ b/lib/playfair/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.5)
+aux_source_directory(. playfair_src)
+set(DIR_SRCS ${playfair_src})
+include_directories(.)
+add_library( playfair
+ STATIC
+ ${DIR_SRCS})
diff --git a/lib/playfair/LICENSE.md b/lib/playfair/LICENSE.md
new file mode 100644
index 0000000..09f984d
--- /dev/null
+++ b/lib/playfair/LICENSE.md
@@ -0,0 +1,637 @@
+
+# GNU GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+
+Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/)
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+## Preamble
+
+The GNU General Public License is a free, copyleft license for software and
+other kinds of works.
+
+The licenses for most software and other practical works are designed to take
+away your freedom to share and change the works. By contrast, the GNU General
+Public License is intended to guarantee your freedom to share and change all
+versions of a program--to make sure it remains free software for all its users.
+We, the Free Software Foundation, use the GNU General Public License for most
+of our software; it applies also to any other work released this way by its
+authors. You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for them if you wish), that you
+receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs, and that you know you can do
+these things.
+
+To protect your rights, we need to prevent others from denying you these rights
+or asking you to surrender the rights. Therefore, you have certain
+responsibilities if you distribute copies of the software, or if you modify it:
+responsibilities to respect the freedom of others.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must pass on to the recipients the same freedoms that you received.
+You must make sure that they, too, receive or can get the source code. And you
+must show them these terms so they know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps:
+
+ 1. assert copyright on the software, and
+ 2. offer you this License giving you legal permission to copy, distribute
+ and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains that
+there is no warranty for this free software. For both users' and authors' sake,
+the GPL requires that modified versions be marked as changed, so that their
+problems will not be attributed erroneously to authors of previous versions.
+
+Some devices are designed to deny users access to install or run modified
+versions of the software inside them, although the manufacturer can do so. This
+is fundamentally incompatible with the aim of protecting users' freedom to
+change the software. The systematic pattern of such abuse occurs in the area of
+products for individuals to use, which is precisely where it is most
+unacceptable. Therefore, we have designed this version of the GPL to prohibit
+the practice for those products. If such problems arise substantially in other
+domains, we stand ready to extend this provision to those domains in future
+versions of the GPL, as needed to protect the freedom of users.
+
+Finally, every program is threatened constantly by software patents. States
+should not allow patents to restrict development and use of software on
+general-purpose computers, but in those that do, we wish to avoid the special
+danger that patents applied to a free program could make it effectively
+proprietary. To prevent this, the GPL assures that patents cannot be used to
+render the program non-free.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+## TERMS AND CONDITIONS
+
+### 0. Definitions.
+
+*This License* refers to version 3 of the GNU General Public License.
+
+*Copyright* also means copyright-like laws that apply to other kinds of works,
+such as semiconductor masks.
+
+*The Program* refers to any copyrightable work licensed under this License.
+Each licensee is addressed as *you*. *Licensees* and *recipients* may be
+individuals or organizations.
+
+To *modify* a work means to copy from or adapt all or part of the work in a
+fashion requiring copyright permission, other than the making of an exact copy.
+The resulting work is called a *modified version* of the earlier work or a work
+*based on* the earlier work.
+
+A *covered work* means either the unmodified Program or a work based on the
+Program.
+
+To *propagate* a work means to do anything with it that, without permission,
+would make you directly or secondarily liable for infringement under applicable
+copyright law, except executing it on a computer or modifying a private copy.
+Propagation includes copying, distribution (with or without modification),
+making available to the public, and in some countries other activities as well.
+
+To *convey* a work means any kind of propagation that enables other parties to
+make or receive copies. Mere interaction with a user through a computer
+network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays *Appropriate Legal Notices* to the
+extent that it includes a convenient and prominently visible feature that
+
+ 1. displays an appropriate copyright notice, and
+ 2. tells the user that there is no warranty for the work (except to the
+ extent that warranties are provided), that licensees may convey the work
+ under this License, and how to view a copy of this License.
+
+If the interface presents a list of user commands or options, such as a menu, a
+prominent item in the list meets this criterion.
+
+### 1. Source Code.
+
+The *source code* for a work means the preferred form of the work for making
+modifications to it. *Object code* means any non-source form of a work.
+
+A *Standard Interface* means an interface that either is an official standard
+defined by a recognized standards body, or, in the case of interfaces specified
+for a particular programming language, one that is widely used among developers
+working in that language.
+
+The *System Libraries* of an executable work include anything, other than the
+work as a whole, that (a) is included in the normal form of packaging a Major
+Component, but which is not part of that Major Component, and (b) serves only
+to enable use of the work with that Major Component, or to implement a Standard
+Interface for which an implementation is available to the public in source code
+form. A *Major Component*, in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system (if any) on
+which the executable work runs, or a compiler used to produce the work, or an
+object code interpreter used to run it.
+
+The *Corresponding Source* for a work in object code form means all the source
+code needed to generate, install, and (for an executable work) run the object
+code and to modify the work, including scripts to control those activities.
+However, it does not include the work's System Libraries, or general-purpose
+tools or generally available free programs which are used unmodified in
+performing those activities but which are not part of the work. For example,
+Corresponding Source includes interface definition files associated with source
+files for the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require, such as
+by intimate data communication or control flow between those subprograms and
+other parts of the work.
+
+The Corresponding Source need not include anything that users can regenerate
+automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same work.
+
+### 2. Basic Permissions.
+
+All rights granted under this License are granted for the term of copyright on
+the Program, and are irrevocable provided the stated conditions are met. This
+License explicitly affirms your unlimited permission to run the unmodified
+Program. The output from running a covered work is covered by this License only
+if the output, given its content, constitutes a covered work. This License
+acknowledges your rights of fair use or other equivalent, as provided by
+copyright law.
+
+You may make, run and propagate covered works that you do not convey, without
+conditions so long as your license otherwise remains in force. You may convey
+covered works to others for the sole purpose of having them make modifications
+exclusively for you, or provide you with facilities for running those works,
+provided that you comply with the terms of this License in conveying all
+material for which you do not control copyright. Those thus making or running
+the covered works for you must do so exclusively on your behalf, under your
+direction and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the
+conditions stated below. Sublicensing is not allowed; section 10 makes it
+unnecessary.
+
+### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological measure
+under any applicable law fulfilling obligations under article 11 of the WIPO
+copyright treaty adopted on 20 December 1996, or similar laws prohibiting or
+restricting circumvention of such measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention is
+effected by exercising rights under this License with respect to the covered
+work, and you disclaim any intention to limit operation or modification of the
+work as a means of enforcing, against the work's users, your or third parties'
+legal rights to forbid circumvention of technological measures.
+
+### 4. Conveying Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you receive it,
+in any medium, provided that you conspicuously and appropriately publish on
+each copy an appropriate copyright notice; keep intact all notices stating that
+this License and any non-permissive terms added in accord with section 7 apply
+to the code; keep intact all notices of the absence of any warranty; and give
+all recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey, and you may
+offer support or warranty protection for a fee.
+
+### 5. Conveying Modified Source Versions.
+
+You may convey a work based on the Program, or the modifications to produce it
+from the Program, in the form of source code under the terms of section 4,
+provided that you also meet all of these conditions:
+
+ - a) The work must carry prominent notices stating that you modified it, and
+ giving a relevant date.
+ - b) The work must carry prominent notices stating that it is released under
+ this License and any conditions added under section 7. This requirement
+ modifies the requirement in section 4 to *keep intact all notices*.
+ - c) You must license the entire work, as a whole, under this License to
+ anyone who comes into possession of a copy. This License will therefore
+ apply, along with any applicable section 7 additional terms, to the whole
+ of the work, and all its parts, regardless of how they are packaged. This
+ License gives no permission to license the work in any other way, but it
+ does not invalidate such permission if you have separately received it.
+ - d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your work need
+ not make them do so.
+
+A compilation of a covered work with other separate and independent works,
+which are not by their nature extensions of the covered work, and which are not
+combined with it such as to form a larger program, in or on a volume of a
+storage or distribution medium, is called an *aggregate* if the compilation and
+its resulting copyright are not used to limit the access or legal rights of the
+compilation's users beyond what the individual works permit. Inclusion of a
+covered work in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+### 6. Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of sections 4
+and 5, provided that you also convey the machine-readable Corresponding Source
+under the terms of this License, in one of these ways:
+
+ - a) Convey the object code in, or embodied in, a physical product (including
+ a physical distribution medium), accompanied by the Corresponding Source
+ fixed on a durable physical medium customarily used for software
+ interchange.
+ - b) Convey the object code in, or embodied in, a physical product (including
+ a physical distribution medium), accompanied by a written offer, valid for
+ at least three years and valid for as long as you offer spare parts or
+ customer support for that product model, to give anyone who possesses the
+ object code either
+ 1. a copy of the Corresponding Source for all the software in the product
+ that is covered by this License, on a durable physical medium
+ customarily used for software interchange, for a price no more than your
+ reasonable cost of physically performing this conveying of source, or
+ 2. access to copy the Corresponding Source from a network server at no
+ charge.
+ - c) Convey individual copies of the object code with a copy of the written
+ offer to provide the Corresponding Source. This alternative is allowed only
+ occasionally and noncommercially, and only if you received the object code
+ with such an offer, in accord with subsection 6b.
+ - d) Convey the object code by offering access from a designated place
+ (gratis or for a charge), and offer equivalent access to the Corresponding
+ Source in the same way through the same place at no further charge. You
+ need not require recipients to copy the Corresponding Source along with the
+ object code. If the place to copy the object code is a network server, the
+ Corresponding Source may be on a different server operated by you or a
+ third party) that supports equivalent copying facilities, provided you
+ maintain clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the Corresponding
+ Source, you remain obligated to ensure that it is available for as long as
+ needed to satisfy these requirements.
+ - e) Convey the object code using peer-to-peer transmission, provided you
+ inform other peers where the object code and Corresponding Source of the
+ work are being offered to the general public at no charge under subsection
+ 6d.
+
+A separable portion of the object code, whose source code is excluded from the
+Corresponding Source as a System Library, need not be included in conveying the
+object code work.
+
+A *User Product* is either
+
+ 1. a *consumer product*, which means any tangible personal property which is
+ normally used for personal, family, or household purposes, or
+ 2. anything designed or sold for incorporation into a dwelling.
+
+In determining whether a product is a consumer product, doubtful cases shall be
+resolved in favor of coverage. For a particular product received by a
+particular user, *normally used* refers to a typical or common use of that
+class of product, regardless of the status of the particular user or of the way
+in which the particular user actually uses, or expects or is expected to use,
+the product. A product is a consumer product regardless of whether the product
+has substantial commercial, industrial or non-consumer uses, unless such uses
+represent the only significant mode of use of the product.
+
+*Installation Information* for a User Product means any methods, procedures,
+authorization keys, or other information required to install and execute
+modified versions of a covered work in that User Product from a modified
+version of its Corresponding Source. The information must suffice to ensure
+that the continued functioning of the modified object code is in no case
+prevented or interfered with solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as part of a
+transaction in which the right of possession and use of the User Product is
+transferred to the recipient in perpetuity or for a fixed term (regardless of
+how the transaction is characterized), the Corresponding Source conveyed under
+this section must be accompanied by the Installation Information. But this
+requirement does not apply if neither you nor any third party retains the
+ability to install modified object code on the User Product (for example, the
+work has been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates for a
+work that has been modified or installed by the recipient, or for the User
+Product in which it has been modified or installed. Access to a network may be
+denied when the modification itself materially and adversely affects the
+operation of the network or violates the rules and protocols for communication
+across the network.
+
+Corresponding Source conveyed, and Installation Information provided, in accord
+with this section must be in a format that is publicly documented (and with an
+implementation available to the public in source code form), and must require
+no special password or key for unpacking, reading or copying.
+
+### 7. Additional Terms.
+
+*Additional permissions* are terms that supplement the terms of this License by
+making exceptions from one or more of its conditions. Additional permissions
+that are applicable to the entire Program shall be treated as though they were
+included in this License, to the extent that they are valid under applicable
+law. If additional permissions apply only to part of the Program, that part may
+be used separately under those permissions, but the entire Program remains
+governed by this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option remove any
+additional permissions from that copy, or from any part of it. (Additional
+permissions may be written to require their own removal in certain cases when
+you modify the work.) You may place additional permissions on material, added
+by you to a covered work, for which you have or can give appropriate copyright
+permission.
+
+Notwithstanding any other provision of this License, for material you add to a
+covered work, you may (if authorized by the copyright holders of that material)
+supplement the terms of this License with terms:
+
+ - a) Disclaiming warranty or limiting liability differently from the terms of
+ sections 15 and 16 of this License; or
+ - b) Requiring preservation of specified reasonable legal notices or author
+ attributions in that material or in the Appropriate Legal Notices displayed
+ by works containing it; or
+ - c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in reasonable
+ ways as different from the original version; or
+ - d) Limiting the use for publicity purposes of names of licensors or authors
+ of the material; or
+ - e) Declining to grant rights under trademark law for use of some trade
+ names, trademarks, or service marks; or
+ - f) Requiring indemnification of licensors and authors of that material by
+ anyone who conveys the material (or modified versions of it) with
+ contractual assumptions of liability to the recipient, for any liability
+ that these contractual assumptions directly impose on those licensors and
+ authors.
+
+All other non-permissive additional terms are considered *further restrictions*
+within the meaning of section 10. If the Program as you received it, or any
+part of it, contains a notice stating that it is governed by this License along
+with a term that is a further restriction, you may remove that term. If a
+license document contains a further restriction but permits relicensing or
+conveying under this License, you may add to a covered work material governed
+by the terms of that license document, provided that the further restriction
+does not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you must place,
+in the relevant source files, a statement of the additional terms that apply to
+those files, or a notice indicating where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the form of a
+separately written license, or stated as exceptions; the above requirements
+apply either way.
+
+### 8. Termination.
+
+You may not propagate or modify a covered work except as expressly provided
+under this License. Any attempt otherwise to propagate or modify it is void,
+and will automatically terminate your rights under this License (including any
+patent licenses granted under the third paragraph of section 11).
+
+However, if you cease all violation of this License, then your license from a
+particular copyright holder is reinstated
+
+ - a) provisionally, unless and until the copyright holder explicitly and
+ finally terminates your license, and
+ - b) permanently, if the copyright holder fails to notify you of the
+ violation by some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated
+permanently if the copyright holder notifies you of the violation by some
+reasonable means, this is the first time you have received notice of violation
+of this License (for any work) from that copyright holder, and you cure the
+violation prior to 30 days after your receipt of the notice.
+
+Termination of your rights under this section does not terminate the licenses
+of parties who have received copies or rights from you under this License. If
+your rights have been terminated and not permanently reinstated, you do not
+qualify to receive new licenses for the same material under section 10.
+
+### 9. Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run a copy
+of the Program. Ancillary propagation of a covered work occurring solely as a
+consequence of using peer-to-peer transmission to receive a copy likewise does
+not require acceptance. However, nothing other than this License grants you
+permission to propagate or modify any covered work. These actions infringe
+copyright if you do not accept this License. Therefore, by modifying or
+propagating a covered work, you indicate your acceptance of this License to do
+so.
+
+### 10. Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically receives a
+license from the original licensors, to run, modify and propagate that work,
+subject to this License. You are not responsible for enforcing compliance by
+third parties with this License.
+
+An *entity transaction* is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered work
+results from an entity transaction, each party to that transaction who receives
+a copy of the work also receives whatever licenses to the work the party's
+predecessor in interest had or could give under the previous paragraph, plus a
+right to possession of the Corresponding Source of the work from the
+predecessor in interest, if the predecessor has it or can get it with
+reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the rights
+granted or affirmed under this License. For example, you may not impose a
+license fee, royalty, or other charge for exercise of rights granted under this
+License, and you may not initiate litigation (including a cross-claim or
+counterclaim in a lawsuit) alleging that any patent claim is infringed by
+making, using, selling, offering for sale, or importing the Program or any
+portion of it.
+
+### 11. Patents.
+
+A *contributor* is a copyright holder who authorizes use under this License of
+the Program or a work on which the Program is based. The work thus licensed is
+called the contributor's *contributor version*.
+
+A contributor's *essential patent claims* are all patent claims owned or
+controlled by the contributor, whether already acquired or hereafter acquired,
+that would be infringed by some manner, permitted by this License, of making,
+using, or selling its contributor version, but do not include claims that would
+be infringed only as a consequence of further modification of the contributor
+version. For purposes of this definition, *control* includes the right to grant
+patent sublicenses in a manner consistent with the requirements of this
+License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free patent
+license under the contributor's essential patent claims, to make, use, sell,
+offer for sale, import and otherwise run, modify and propagate the contents of
+its contributor version.
+
+In the following three paragraphs, a *patent license* is any express agreement
+or commitment, however denominated, not to enforce a patent (such as an express
+permission to practice a patent or covenant not to sue for patent
+infringement). To *grant* such a patent license to a party means to make such
+an agreement or commitment not to enforce a patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license, and the
+Corresponding Source of the work is not available for anyone to copy, free of
+charge and under the terms of this License, through a publicly available
+network server or other readily accessible means, then you must either
+
+ 1. cause the Corresponding Source to be so available, or
+ 2. arrange to deprive yourself of the benefit of the patent license for this
+ particular work, or
+ 3. arrange, in a manner consistent with the requirements of this License, to
+ extend the patent license to downstream recipients.
+
+*Knowingly relying* means you have actual knowledge that, but for the patent
+license, your conveying the covered work in a country, or your recipient's use
+of the covered work in a country, would infringe one or more identifiable
+patents in that country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or arrangement, you
+convey, or propagate by procuring conveyance of, a covered work, and grant a
+patent license to some of the parties receiving the covered work authorizing
+them to use, propagate, modify or convey a specific copy of the covered work,
+then the patent license you grant is automatically extended to all recipients
+of the covered work and works based on it.
+
+A patent license is *discriminatory* if it does not include within the scope of
+its coverage, prohibits the exercise of, or is conditioned on the non-exercise
+of one or more of the rights that are specifically granted under this License.
+You may not convey a covered work if you are a party to an arrangement with a
+third party that is in the business of distributing software, under which you
+make payment to the third party based on the extent of your activity of
+conveying the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory patent
+license
+
+ - a) in connection with copies of the covered work conveyed by you (or copies
+ made from those copies), or
+ - b) primarily for and in connection with specific products or compilations
+ that contain the covered work, unless you entered into that arrangement, or
+ that patent license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting any implied
+license or other defenses to infringement that may otherwise be available to
+you under applicable patent law.
+
+### 12. No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not excuse
+you from the conditions of this License. If you cannot convey a covered work so
+as to satisfy simultaneously your obligations under this License and any other
+pertinent obligations, then as a consequence you may not convey it at all. For
+example, if you agree to terms that obligate you to collect a royalty for
+further conveying from those to whom you convey the Program, the only way you
+could satisfy both those terms and this License would be to refrain entirely
+from conveying the Program.
+
+### 13. Use with the GNU Affero General Public License.
+
+Notwithstanding any other provision of this License, you have permission to
+link or combine any covered work with a work licensed under version 3 of the
+GNU Affero General Public License into a single combined work, and to convey
+the resulting work. The terms of this License will continue to apply to the
+part which is the covered work, but the special requirements of the GNU Affero
+General Public License, section 13, concerning interaction through a network
+will apply to the combination as such.
+
+### 14. Revised Versions of this License.
+
+The Free Software Foundation may publish revised and/or new versions of the GNU
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems
+or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+that a certain numbered version of the GNU General Public License *or any later
+version* applies to it, you have the option of following the terms and
+conditions either of that numbered version or of any later version published by
+the Free Software Foundation. If the Program does not specify a version number
+of the GNU General Public License, you may choose any version ever published by
+the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions of the
+GNU General Public License can be used, that proxy's public statement of
+acceptance of a version permanently authorizes you to choose that version for
+the Program.
+
+Later license versions may give you additional or different permissions.
+However, no additional obligations are imposed on any author or copyright
+holder as a result of your choosing to follow a later version.
+
+### 15. Disclaimer of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
+LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
+PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
+QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+### 16. Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
+COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
+INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
+HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+### 17. Interpretation of Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided above cannot
+be given local legal effect according to their terms, reviewing courts shall
+apply local law that most closely approximates an absolute waiver of all civil
+liability in connection with the Program, unless a warranty or assumption of
+liability accompanies a copy of the Program in return for a fee.
+
+## END OF TERMS AND CONDITIONS ###
+
+### How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach
+them to the start of each source file to most effectively state the exclusion
+of warranty; and each file should have at least the *copyright* line and a
+pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program does terminal interaction, make it output a short notice like
+this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w` and `show c` should show the appropriate
+parts of the General Public License. Of course, your program's commands might
+be different; for a GUI interface, you would use an *about box*.
+
+You should also get your employer (if you work as a programmer) or school, if
+any, to sign a *copyright disclaimer* for the program, if necessary. For more
+information on this, and how to apply and follow the GNU GPL, see
+[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).
+
+The GNU General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may consider
+it more useful to permit linking proprietary applications with the library. If
+this is what you want to do, use the GNU Lesser General Public License instead
+of this License. But first, please read
+[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html).
diff --git a/lib/playfair/hand_garble.c b/lib/playfair/hand_garble.c
new file mode 100644
index 0000000..8e8361b
--- /dev/null
+++ b/lib/playfair/hand_garble.c
@@ -0,0 +1,443 @@
+#include
+#include
+
+#define printf(...) (void)0;
+
+uint8_t rol8(uint8_t x, int y);
+uint32_t rol8x(uint8_t x, int y);
+
+uint32_t weird_ror8(uint8_t input, int count)
+{
+ if (count == 0)
+ return 0;
+ return ((input >> count) & 0xff) | (input & 0xff) << (8-count);
+
+}
+
+uint32_t weird_rol8(uint8_t input, int count)
+{
+ if (count == 0)
+ return 0;
+ return ((input << count) & 0xff) | (input & 0xff) >> (8-count);
+}
+
+uint32_t weird_rol32(uint8_t input, int count)
+{
+ if (count == 0)
+ return 0;
+ return (input << count) ^ (input >> (8 - count));
+}
+
+// I do not know why it is doing all of this, and there is still a possibility for a gremlin or two to be lurking in the background
+// I DO know it is not trivial. It could be purely random garbage, of course.
+void garble(unsigned char* buffer0, unsigned char* buffer1, unsigned char* buffer2, unsigned char* buffer3, unsigned char* buffer4)
+{
+ unsigned int tmp, tmp2, tmp3;
+ unsigned int A, B, C, D, E, M, J, G, F, H, K, R, S, T, U, V, W, X, Y, Z;
+ // buffer1[64] = A
+ // (buffer1[99] / 3) = B
+ // 0ABAAABB
+ // Then we AND with a complex expression, and add 20 just for good measure
+ buffer2[12] = 0x14 + (((buffer1[64] & 92) | ((buffer1[99] / 3) & 35)) & buffer4[rol8x(buffer4[(buffer1[206] % 21)],4) % 21]);
+ printf("buffer2[12] = %02x\n", buffer2[12]);
+
+ // This is a bit simpler: 2*B*B/25
+ buffer1[4] = (buffer1[99] / 5) * (buffer1[99] / 5) * 2;
+ printf("buffer1[4] = %02x\n", buffer1[4]);
+
+ // Simpler still!
+ buffer2[34] = 0xb8;
+ printf("buffer2[34] = %02x\n", buffer2[34]);
+
+ // ...
+ buffer1[153] ^= (buffer2[buffer1[203] % 35] * buffer2[buffer1[203] % 35] * buffer1[190]);
+ printf("buffer1[153] = %02x\n", buffer1[153]);
+
+ // This one looks simple, but wow was it not :(
+ buffer0[3] -= (((buffer4[buffer1[205] % 21]>>1) & 80) | 0xe6440);
+ printf("buffer0[3] = %02x\n", buffer0[3]);
+
+ // This is always 0x93
+ buffer0[16] = 0x93;
+ printf("buffer0[16] = %02x\n", buffer0[16]);
+
+ // This is always 0x62
+ buffer0[13] = 0x62;
+ printf("buffer0[13] = %02x\n", buffer0[13]);
+
+ buffer1[33] -= (buffer4[buffer1[36] % 21] & 0xf6);
+ printf("buffer1[33] = %02x\n", buffer1[33]);
+
+ // This is always 7
+ tmp2 = buffer2[buffer1[67] % 35];
+ buffer2[12] = 0x07;
+ printf("buffer2[12] = %02x\n", buffer2[12]);
+
+ // This is pretty easy!
+ tmp = buffer0[buffer1[181] % 20];
+ buffer1[2] -= 3136;
+ printf("buffer1[2] = %02x\n", buffer1[2]);
+
+ buffer0[19] = buffer4[buffer1[58] % 21];
+ printf("buffer0[19] = %02x\n", buffer0[19]);
+
+ buffer3[0] = 92 - buffer2[buffer1[32] % 35];
+ printf("buffer3[0] = %02x\n", buffer3[0]);
+
+ buffer3[4] = buffer2[buffer1[15] % 35] + 0x9e;
+ printf("buffer3[4] = %02x\n", buffer3[4]);
+
+ buffer1[34] += (buffer4[((buffer2[buffer1[15] % 35] + 0x9e) & 0xff) % 21] / 5);
+ printf("buffer1[34] = %02x\n", buffer1[34]);
+
+ buffer0[19] += 0xfffffee6 - ((buffer0[buffer3[4] % 20]>>1) & 102);
+ printf("buffer0[19] = %02x\n", buffer0[19]);
+
+ // This LOOKS like it should be a rol8x, but it just doesnt work out because if the shift amount is 0, then the output is 0 too :(
+ // FIXME: Switch to weird_ror8
+ buffer1[15] = (3*(((buffer1[72] >> (buffer4[buffer1[190] % 21] & 7)) ^ (buffer1[72] << ((7 - (buffer4[buffer1[190] % 21]-1)&7)))) - (3*buffer4[buffer1[126] % 21]))) ^ buffer1[15];
+ printf("buffer1[15] = %02x\n", buffer1[15]);
+
+ buffer0[15] ^= buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35];
+ printf("buffer0[15] = %02x\n", buffer0[15]);
+
+ buffer2[4] ^= buffer1[202]/3;
+ printf("buffer2[4] = %02x\n", buffer2[4]);
+
+ // This could probably be quite a bit simpler.
+ A = 92 - buffer0[buffer3[0] % 20];
+ E = (A & 0xc6) | (~buffer1[105] & 0xc6) | (A & (~buffer1[105]));
+ buffer2[1] += (E*E*E);
+ printf("buffer2[1] = %02x\n", buffer2[1]);
+
+ buffer0[19] ^= ((224 | (buffer4[buffer1[92] % 21] & 27)) * buffer2[buffer1[41] % 35]) / 3;
+ printf("buffer0[19] = %02x\n", buffer0[19]);
+
+ buffer1[140] += weird_ror8(92, buffer1[5] & 7);
+ printf("buffer1[140] = %02x\n", buffer1[140]);
+
+ // Is this as simple as it could be?
+ buffer2[12] += ((((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) | buffer1[182]) & 192) | (((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) & buffer1[182]);
+ printf("buffer2[12] = %02x\n", buffer2[12]);
+
+ buffer1[36] += 125;
+ printf("buffer1[36] = %02x\n", buffer1[36]);
+
+ buffer1[124] = rol8x((((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15])) & buffer0[buffer1[43] % 20]) | (((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15]) | buffer0[buffer1[43] % 20]) & 95), 4);
+ printf("buffer1[124] = %02x\n", buffer1[124]);
+
+ buffer3[8] = ((((buffer0[buffer3[4] % 20] & 95)) & ((buffer4[buffer1[68] % 21] & 46) << 1)) | 16) ^ 92;
+ printf("buffer3[8] = %02x\n", buffer3[8]);
+
+ A = buffer1[177] + buffer4[buffer1[79] % 21];
+ D = (((A >> 1) | ((3 * buffer1[148]) / 5)) & buffer2[1]) | ((A >> 1) & ((3 * buffer1[148])/5));
+ buffer3[12] = ((-34 - D));
+ printf("buffer3[12] = %02x\n", buffer3[12]);
+
+ A = 8 - ((buffer2[22] & 7)); // FIXME: buffer2[22] = 74, so A is always 6 and B^C is just ror8(buffer1[33], 6)
+ B = (buffer1[33] >> (A & 7));
+ C = buffer1[33] << (buffer2[22] & 7);
+ buffer2[16] += ((buffer2[buffer3[0] % 35] & 159) | buffer0[buffer3[4] % 20] | 8) - ((B^C) | 128);
+ printf("buffer2[16] = %02x\n", buffer2[16]);
+
+ // This one was very easy so I just skipped ahead and did it
+ buffer0[14] ^= buffer2[buffer3[12] % 35];
+ printf("buffer0[14] = %02x\n", buffer0[14]);
+
+ // Monster goes here
+ A = weird_rol8(buffer4[buffer0[buffer1[201] % 20] % 21], ((buffer2[buffer1[112] % 35] << 1) & 7));
+ D = (buffer0[buffer1[208] % 20] & 131) | (buffer0[buffer1[164] % 20] & 124);
+ buffer1[19] += (A & (D/5)) | ((A | (D/5)) & 37);
+ printf("buffer1[19] = %02x\n", buffer1[19]);
+
+ buffer2[8] = weird_ror8(140, ((buffer4[buffer1[45] % 21] + 92) * (buffer4[buffer1[45] % 21] + 92)) & 7);
+ printf("buffer2[8] = %02x\n", buffer2[8]);
+
+ buffer1[190] = 56;
+ printf("buffer1[190] = %02x\n", buffer1[190]);
+
+ buffer2[8] ^= buffer3[0];
+ printf("buffer2[8] = %02x\n", buffer2[8]);
+
+ buffer1[53] = ~((buffer0[buffer1[83] % 20] | 204)/5);
+ printf("buffer1[53] = %02x\n", buffer1[53]);
+
+ buffer0[13] += buffer0[buffer1[41] % 20];
+ printf("buffer0[13] = %02x\n", buffer0[13]);
+
+ buffer0[10] = ((buffer2[buffer3[0] % 35] & buffer1[2]) | ((buffer2[buffer3[0] % 35] | buffer1[2]) & buffer3[12])) / 15;
+ printf("buffer0[10] = %02x\n", buffer0[10]);
+
+ A = (((56 | (buffer4[buffer1[2] % 21] & 68)) | buffer2[buffer3[8] % 35]) & 42) | (((buffer4[buffer1[2] % 21] & 68) | 56) & buffer2[buffer3[8] % 35]);
+ buffer3[16] = (A*A) + 110;
+ printf("buffer3[16] = %02x\n", buffer3[16]);
+
+ buffer3[20] = 202 - buffer3[16];
+ printf("buffer3[20] = %02x\n", buffer3[20]);
+
+ buffer3[24] = buffer1[151];
+ printf("buffer3[24] = %02x\n", buffer3[24]);
+
+ buffer2[13] ^= buffer4[buffer3[0] % 21];
+ printf("buffer2[13] = %02x\n", buffer2[13]);
+
+ B = ((buffer2[buffer1[179] % 35] - 38) & 177) | (buffer3[12] & 177);
+ C = ((buffer2[buffer1[179] % 35] - 38)) & buffer3[12];
+ buffer3[28] = 30 + ((B | C) * (B | C));
+ printf("buffer3[28] = %02x\n", buffer3[28]);
+
+ buffer3[32] = buffer3[28] + 62;
+ printf("buffer3[32] = %02x\n", buffer3[32]);
+
+ // eek
+ A = ((buffer3[20] + (buffer3[0] & 74)) | ~buffer4[buffer3[0] % 21]) & 121;
+ B = ((buffer3[20] + (buffer3[0] & 74)) & ~buffer4[buffer3[0] % 21]);
+ tmp3 = (A|B);
+ C = ((((A|B) ^ 0xffffffa6) | buffer3[0]) & 4) | (((A|B) ^ 0xffffffa6) & buffer3[0]);
+ buffer1[47] = (buffer2[buffer1[89] % 35] + C) ^ buffer1[47];
+ printf("buffer1[47] = %02x\n", buffer1[47]);
+
+ buffer3[36] = ((rol8((tmp & 179) + 68, 2) & buffer0[3]) | (tmp2 & ~buffer0[3])) - 15;
+ printf("buffer3[36] = %02x\n", buffer3[36]);
+
+ buffer1[123] ^= 221;
+ printf("buffer1[123] = %02x\n", buffer1[123]);
+
+ A = ((buffer4[buffer3[0] % 21]) / 3) - buffer2[buffer3[4] % 35];
+ C = (((buffer3[0] & 163) + 92) & 246) | (buffer3[0] & 92);
+ E = ((C | buffer3[24]) & 54) | (C & buffer3[24]);
+ buffer3[40] = A - E;
+ printf("buffer3[40] = %02x\n", buffer3[40]);
+
+ buffer3[44] = tmp3 ^ 81 ^ (((buffer3[0] >> 1) & 101) + 26);
+ printf("buffer3[44] = %02x\n", buffer3[44]);
+
+ buffer3[48] = buffer2[buffer3[4] % 35] & 27;
+ printf("buffer3[48] = %02x\n", buffer3[48]);
+ buffer3[52] = 27;
+ printf("buffer3[52] = %02x\n", buffer3[52]);
+ buffer3[56] = 199;
+ printf("buffer3[56] = %02x\n", buffer3[56]);
+
+ // caffeine
+ buffer3[64] = buffer3[4] + (((((((buffer3[40] | buffer3[24]) & 177) | (buffer3[40] & buffer3[24])) & ((((buffer4[buffer3[0] % 20] & 177) | 176)) | ((buffer4[buffer3[0] % 21]) & ~3))) | ((((buffer3[40] & buffer3[24]) | ((buffer3[40] | buffer3[24]) & 177)) & 199) | ((((buffer4[buffer3[0] % 21] & 1) + 176) | (buffer4[buffer3[0] % 21] & ~3)) & buffer3[56]))) & (~buffer3[52])) | buffer3[48]);
+ printf("buffer3[64] = %02x (want E7)\n", buffer3[64]);
+
+ buffer2[33] ^= buffer1[26];
+ printf("buffer2[33] = %02x\n", buffer2[33]);
+
+ buffer1[106] ^= buffer3[20] ^ 133;
+ printf("buffer1[106] = %02x\n", buffer1[106]);
+
+ buffer2[30] = ((buffer3[64] / 3) - (275 | (buffer3[0] & 247))) ^ buffer0[buffer1[122] % 20];
+ printf("buffer2[130] = %02x\n", buffer2[30]);
+
+ buffer1[22] = (buffer2[buffer1[90] % 35] & 95) | 68;
+ printf("buffer1[22] = %02x\n", buffer1[22]);
+
+ A = (buffer4[buffer3[36] % 21] & 184) | (buffer2[buffer3[44] % 35] & ~184);
+ buffer2[18] += ((A*A*A) >> 1);
+ printf("buffer2[18] = %02x\n", buffer2[18]);
+
+ buffer2[5] -= buffer4[buffer1[92] % 21];
+ printf("buffer2[5] = %02x\n", buffer2[5]);
+
+ A = (((buffer1[41] & ~24)|(buffer2[buffer1[183] % 35] & 24)) & (buffer3[16] + 53)) | (buffer3[20] & buffer2[buffer3[20] % 35]);
+ B = (buffer1[17] & (~buffer3[44])) | (buffer0[buffer1[59] % 20] & buffer3[44]);
+ buffer2[18] ^= (A*B);
+ printf("buffer2[18] = %02x\n", buffer2[18]);
+
+
+ A = weird_ror8(buffer1[11], buffer2[buffer1[28] % 35] & 7) & 7;
+ B = (((buffer0[buffer1[93] % 20] & ~buffer0[14]) | (buffer0[14] & 150)) & ~28) | (buffer1[7] & 28);
+ buffer2[22] = (((((B | weird_rol8(buffer2[buffer3[0] % 35], A)) & buffer2[33]) | (B & weird_rol8(buffer2[buffer3[0] % 35], A))) + 74) & 0xff);
+ printf("buffer2[22] = %02x\n", buffer2[22]);
+
+ A = buffer4[(buffer0[buffer1[39] % 20] ^ 217) % 21]; // X5
+ buffer0[15] -= ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0])) & A) | ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0]) | A) & buffer3[32]);
+ printf("buffer0[15] = %02x\n", buffer0[15]);
+
+ // We need to save T here, and boy is it complicated to calculate!
+ B = (((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer0[buffer3[64] % 20] | buffer2[buffer1[57] % 35]) & 95) | (buffer3[64] & 45) | 82) & 32);
+ C = ((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer2[buffer1[57] % 35] | buffer0[buffer3[64] % 20]) & 95)) & ((buffer3[64] & 45) | 82);
+ D = ((((buffer3[0]/3) - (buffer3[64]|buffer1[22]))) ^ (buffer3[28] + 62) ^ ((B|C)));
+ T = buffer0[(D & 0xff) % 20];
+
+ buffer3[68] = (buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20]) | buffer2[buffer3[64] % 35];
+ printf("buffer3[68] = %02x\n", buffer3[68]);
+
+ U = buffer0[buffer1[50] % 20]; // this is also v100
+ W = buffer2[buffer1[138] % 35];
+ X = buffer4[buffer1[39] % 21];
+ Y = buffer0[buffer1[4] % 20]; // this is also v120
+ Z = buffer4[buffer1[202] % 21]; // also v124
+ V = buffer0[buffer1[151] % 20];
+ S = buffer2[buffer1[14] % 35];
+ R = buffer0[buffer1[145] % 20];
+
+ A = (buffer2[buffer3[68] % 35] & buffer0[buffer1[209] % 20]) | ((buffer2[buffer3[68] % 35] | buffer0[buffer1[209] % 20]) & 24);
+ B = weird_rol8(buffer4[buffer1[127] % 21], buffer2[buffer3[68] % 35] & 7);
+ C = (A & buffer0[10]) | (B & ~buffer0[10]);
+ D = 7 ^ (buffer4[buffer2[buffer3[36] % 35] % 21] << 1);
+ buffer3[72] = (C & 71) | (D & ~71);
+ printf("buffer3[72] = %02x\n", buffer3[72]);
+
+ buffer2[2] += (((buffer0[buffer3[20] % 20] << 1) & 159) | (buffer4[buffer1[190] % 21] & ~159)) & ((((buffer4[buffer3[64] % 21] & 110) | (buffer0[buffer1[25] % 20] & ~110)) & ~150) | (buffer1[25] & 150));
+ printf("buffer2[2] = %02x\n", buffer2[2]);
+
+ buffer2[14] -= ((buffer2[buffer3[20] % 35] & (buffer3[72] ^ buffer2[buffer1[100] % 35])) & ~34) | (buffer1[97] & 34);
+ printf("buffer2[14] = %02x\n", buffer2[14]);
+
+ buffer0[17] = 115;
+ printf("buffer0[17] = %02x\n", buffer0[17]);
+
+ buffer1[23] ^= ((((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20])) & (buffer1[50]/3)) |
+ ((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20]) | (buffer1[50] / 3)) & 246)) << 1);
+ printf("buffer1[23] = %02x\n", buffer1[23]);
+
+ buffer0[13] = ((((((buffer0[buffer3[40] % 20] | buffer1[10]) & 82) | (buffer0[buffer3[40] % 20] & buffer1[10])) & 209) |
+ ((buffer0[buffer1[39] % 20] << 1) & 46)) >> 1);
+ printf("buffer0[13] = %02x\n", buffer0[13]);
+
+ buffer2[33] -= buffer1[113] & 9;
+ printf("buffer2[33] = %02x\n", buffer2[33]);
+
+ buffer2[28] -= ((((2 | (buffer1[110] & 222)) >> 1) & ~223) | (buffer3[20] & 223));
+ printf("buffer2[28] = %02x\n", buffer2[28]);
+
+ J = weird_rol8((V | Z), (U & 7)); // OK
+ A = (buffer2[16] & T) | (W & (~buffer2[16]));
+ B = (buffer1[33] & 17) | (X & ~17);
+ E = ((Y | ((A+B) / 5)) & 147) |
+ (Y & ((A+B) / 5)); // OK
+ M = (buffer3[40] & buffer4[((buffer3[8] + J + E) & 0xff) % 21]) |
+ ((buffer3[40] | buffer4[((buffer3[8] + J + E) & 0xff) % 21]) & buffer2[23]);
+
+ buffer0[15] = (((buffer4[buffer3[20] % 21] - 48) & (~buffer1[184])) | ((buffer4[buffer3[20] % 21] - 48) & 189) | (189 & ~buffer1[184])) & (M*M*M);
+ printf("buffer0[15] = %02x\n", buffer0[15]);
+
+ buffer2[22] += buffer1[183];
+ printf("buffer2[22] = %02x\n", buffer2[22]);
+
+ buffer3[76] = (3 * buffer4[buffer1[1] % 21]) ^ buffer3[0];
+ printf("buffer3[76] = %02x\n", buffer3[76]);
+
+ A = buffer2[((buffer3[8] + (J + E)) & 0xff) % 35];
+ F = (((buffer4[buffer1[178] % 21] & A) | ((buffer4[buffer1[178] % 21] | A) & 209)) * buffer0[buffer1[13] % 20]) * (buffer4[buffer1[26] % 21] >> 1);
+ G = (F + 0x733ffff9) * 198 - (((F + 0x733ffff9) * 396 + 212) & 212) + 85;
+ buffer3[80] = buffer3[36] + (G ^ 148) + ((G ^ 107) << 1) - 127;
+ printf("buffer3[80] = %02x\n", buffer3[80]);
+
+ buffer3[84] = ((buffer2[buffer3[64] % 35]) & 245) | (buffer2[buffer3[20] % 35] & 10);
+ printf("buffer3[84] = %02x\n", buffer3[84]);
+
+ A = buffer0[buffer3[68] % 20] | 81;
+ buffer2[18] -= ((A*A*A) & ~buffer0[15]) | ((buffer3[80] / 15) & buffer0[15]);
+ printf("buffer2[18] = %02x\n", buffer2[18]);
+
+ buffer3[88] = buffer3[8] + J + E - buffer0[buffer1[160] % 20] + (buffer4[buffer0[((buffer3[8] + J + E) & 255) % 20] % 21] / 3);
+ printf("buffer3[88] = %02x\n", buffer3[88]);
+
+ B = ((R ^ buffer3[72]) & ~198) | ((S * S) & 198);
+ F = (buffer4[buffer1[69] % 21] & buffer1[172]) | ((buffer4[buffer1[69] % 21] | buffer1[172] ) & ((buffer3[12] - B) + 77));
+ buffer0[16] = 147 - ((buffer3[72] & ((F & 251) | 1)) | (((F & 250) | buffer3[72]) & 198));
+ printf("buffer0[16] = %02x\n", buffer0[16]);
+
+ C = (buffer4[buffer1[168] % 21] & buffer0[buffer1[29] % 20] & 7) | ((buffer4[buffer1[168] % 21] | buffer0[buffer1[29] % 20]) & 6);
+ F = (buffer4[buffer1[155] % 21] & buffer1[105]) | ((buffer4[buffer1[155] % 21] | buffer1[105]) & 141);
+ buffer0[3] -= buffer4[weird_rol32(F, C) % 21];
+ printf("buffer0[3] = %02x\n", buffer0[3]);
+
+ buffer1[5] = weird_ror8(buffer0[12], ((buffer0[buffer1[61] % 20] / 5) & 7)) ^ (((~buffer2[buffer3[84] % 35]) & 0xffffffff) / 5);
+ printf("buffer1[5] = %02x\n", buffer1[5]);
+
+ buffer1[198] += buffer1[3];
+ printf("buffer1[198] = %02x\n", buffer1[198]);
+
+ A = (162 | buffer2[buffer3[64] % 35]);
+ buffer1[164] += ((A*A)/5);
+ printf("buffer1[164] = %02x\n", buffer1[164]);
+
+ G = weird_ror8(139, (buffer3[80] & 7));
+ C = ((buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21]) & 95) | (buffer0[buffer3[40] % 20] & ~95);
+ buffer3[92] = (G & 12) | (buffer0[buffer3[20] % 20] & 12) | (G & buffer0[buffer3[20] % 20]) | C;
+ printf("buffer3[92] = %02x\n", buffer3[92]);
+
+ buffer2[12] += ((buffer1[103] & 32) | (buffer3[92] & ((buffer1[103] | 60))) | 16)/3;
+ printf("buffer2[12] = %02x\n", buffer2[12]);
+
+ buffer3[96] = buffer1[143];
+ printf("buffer3[96] = %02x\n", buffer3[96]);
+
+ buffer3[100] = 27;
+ printf("buffer3[100] = %02x\n", buffer3[100]);
+
+ buffer3[104] = (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) ^ 119;
+ printf("buffer3[104] = %02x\n", buffer3[104]);
+
+ buffer3[108] = 238 & ((((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) << 1);
+ printf("buffer3[108] = %02x\n", buffer3[108]);
+
+ buffer3[112] = (~buffer3[64] & (buffer3[84] / 3)) ^ 49;
+ printf("buffer3[112] = %02x\n", buffer3[112]);
+
+ buffer3[116] = 98 & ((~buffer3[64] & (buffer3[84] / 3)) << 1);
+ printf("buffer3[116] = %02x\n", buffer3[116]);
+
+ // finale
+ A = (buffer1[35] & buffer2[8]) | (buffer3[40] & ~buffer2[8]);
+ B = (A & buffer3[64]) | (((buffer3[84] / 3) & ~buffer3[64]));
+ buffer1[143] = buffer3[96] - ((B & (86 + ((buffer1[172] & 64) >> 1))) | (((((buffer1[172] & 65) >> 1) ^ 86) | ((~buffer3[64] & (buffer3[84] / 3)) | (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]))) & buffer3[100]));
+ printf("buffer1[143] = %02x\n", buffer1[143]);
+
+ buffer2[29] = 162;
+ printf("buffer2[29] = %02x\n", buffer2[29]);
+
+ A = ((((buffer4[buffer3[88] % 21]) & 160) | (buffer0[buffer1[125] % 20] & 95)) >> 1);
+ B = buffer2[buffer1[149] % 35] ^ (buffer1[43] * buffer1[43]);
+ buffer0[15] += (B&A) | ((A|B) & 115);
+ printf("buffer0[15] = %02x\n", buffer0[15]);
+
+ buffer3[120] = buffer3[64] - buffer0[buffer3[40] % 20];
+ printf("buffer3[120] = %02x\n", buffer3[120]);
+
+ buffer1[95] = buffer4[buffer3[20] % 21];
+ printf("buffer1[95] = %02x\n", buffer1[95]);
+
+ A = weird_ror8(buffer2[buffer3[80] % 35], (buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35]) & 7);
+ buffer0[7] -= (A*A);
+ printf("buffer0[7] = %02x\n", buffer0[7]);
+
+ buffer2[8] = buffer2[8] - buffer1[184] + (buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21]);
+ printf("buffer2[8] = %02x\n", buffer2[8]);
+
+ buffer0[16] = (buffer2[buffer1[102] % 35] << 1) & 132;
+ printf("buffer0[16] = %02x\n", buffer0[16]);
+
+ buffer3[124] = (buffer4[buffer3[40] % 21] >> 1) ^ buffer3[68];
+ printf("buffer3[124] = %02x\n", buffer3[124]);
+
+ buffer0[7] -= (buffer0[buffer1[191] % 20] - (((buffer4[buffer1[80] % 21] << 1) & ~177) | (buffer4[buffer4[buffer3[88] % 21] % 21] & 177)));
+ printf("buffer0[7] = %02x\n", buffer0[7]);
+
+ buffer0[6] = buffer0[buffer1[119] % 20];
+ printf("buffer0[6] = %02x\n", buffer0[6]);
+
+ A = (buffer4[buffer1[190] % 21] & ~209) | (buffer1[118] & 209);
+ B = buffer0[buffer3[120] % 20] * buffer0[buffer3[120] % 20];
+ buffer0[12] = (buffer0[buffer3[84] % 20] ^ (buffer2[buffer1[71] % 35] + buffer2[buffer1[15] % 35])) & ((A & B) | ((A | B) & 27));
+ printf("buffer0[12] = %02x\n", buffer0[12]);
+
+ B = (buffer1[32] & buffer2[buffer3[88] % 35]) | ((buffer1[32] | buffer2[buffer3[88] % 35]) & 23);
+ D = (((buffer4[buffer1[57] % 21] * 231) & 169) | (B & 86));
+ F = (((buffer0[buffer1[82] % 20] & ~29) | (buffer4[buffer3[124] % 21] & 29)) & 190) | (buffer4[(D/5) % 21] & ~190);
+ H = buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20];
+ K = (H & buffer1[82]) | (H & 92) | (buffer1[82] & 92);
+ buffer3[128] = ((F & K) | ((F | K) & 192)) ^ (D/5);
+ printf("buffer3[128] = %02x\n", buffer3[128]);
+
+ buffer2[25] ^= ((buffer0[buffer3[120] % 20] << 1) * buffer1[5]) - (weird_rol8(buffer3[76], (buffer4[buffer3[124] % 21] & 7)) & (buffer3[20] + 110));
+ printf("buffer2[25] = %02x\n", buffer2[25]);
+ //exit(0);
+
+}
diff --git a/lib/playfair/modified_md5.c b/lib/playfair/modified_md5.c
new file mode 100644
index 0000000..3758a33
--- /dev/null
+++ b/lib/playfair/modified_md5.c
@@ -0,0 +1,119 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#define printf(...) (void)0;
+
+int shift[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+ 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+ 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+ 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
+
+uint32_t F(uint32_t B, uint32_t C, uint32_t D)
+{
+ return (B & C) | (~B & D);
+}
+
+uint32_t G(uint32_t B, uint32_t C, uint32_t D)
+{
+ return (B & D) | (C & ~D);
+}
+
+uint32_t H(uint32_t B, uint32_t C, uint32_t D)
+{
+ return B ^ C ^ D;
+}
+
+uint32_t I(uint32_t B, uint32_t C, uint32_t D)
+{
+ return C ^ (B | ~D);
+}
+
+
+uint32_t rol(uint32_t input, int count)
+{
+ return ((input << count) & 0xffffffff) | (input & 0xffffffff) >> (32-count);
+}
+
+void swap(uint32_t* a, uint32_t* b)
+{
+ printf("%08x <-> %08x\n", *a, *b);
+ uint32_t c = *a;
+ *a = *b;
+ *b = c;
+}
+
+void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut)
+{
+ unsigned char blockIn[64];
+ uint32_t* block_words = (uint32_t*)blockIn;
+ uint32_t* key_words = (uint32_t*)keyIn;
+ uint32_t* out_words = (uint32_t*)keyOut;
+ uint32_t A, B, C, D, Z, tmp;
+ int i;
+
+ memcpy(blockIn, originalblockIn, 64);
+
+ // Each cycle does something like this:
+ A = key_words[0];
+ B = key_words[1];
+ C = key_words[2];
+ D = key_words[3];
+ for (i = 0; i < 64; i++)
+ {
+ uint32_t input;
+ int j;
+ if (i < 16)
+ j = i;
+ else if (i < 32)
+ j = (5*i + 1) % 16;
+ else if (i < 48)
+ j = (3*i + 5) % 16;
+ else if (i < 64)
+ j = 7*i % 16;
+
+ input = blockIn[4*j] << 24 | blockIn[4*j+1] << 16 | blockIn[4*j+2] << 8 | blockIn[4*j+3];
+ printf("Key = %08x\n", A);
+ Z = A + input + (int)(long long)((1LL << 32) * fabs(sin(i + 1)));
+ if (i < 16)
+ Z = rol(Z + F(B,C,D), shift[i]);
+ else if (i < 32)
+ Z = rol(Z + G(B,C,D), shift[i]);
+ else if (i < 48)
+ Z = rol(Z + H(B,C,D), shift[i]);
+ else if (i < 64)
+ Z = rol(Z + I(B,C,D), shift[i]);
+ if (i == 63)
+ printf("Ror is %08x\n", Z);
+ printf("Output of round %d: %08X + %08X = %08X (shift %d, constant %08X)\n", i, Z, B, Z+B, shift[i], (int)(long long)((1LL << 32) * fabs(sin(i + 1))));
+ Z = Z + B;
+ tmp = D;
+ D = C;
+ C = B;
+ B = Z;
+ A = tmp;
+ if (i == 31)
+ {
+ // swapsies
+ swap(&block_words[A & 15], &block_words[B & 15]);
+ swap(&block_words[C & 15], &block_words[D & 15]);
+ swap(&block_words[(A & (15<<4))>>4], &block_words[(B & (15<<4))>>4]);
+ swap(&block_words[(A & (15<<8))>>8], &block_words[(B & (15<<8))>>8]);
+ swap(&block_words[(A & (15<<12))>>12], &block_words[(B & (15<<12))>>12]);
+ }
+ }
+ printf("%08X %08X %08X %08X\n", A, B, C, D);
+ // Now we can actually compute the output
+ printf("Out:\n");
+ printf("%08x + %08x = %08x\n", key_words[0], A, key_words[0] + A);
+ printf("%08x + %08x = %08x\n", key_words[1], B, key_words[1] + B);
+ printf("%08x + %08x = %08x\n", key_words[2], C, key_words[2] + C);
+ printf("%08x + %08x = %08x\n", key_words[3], D, key_words[3] + D);
+ out_words[0] = key_words[0] + A;
+ out_words[1] = key_words[1] + B;
+ out_words[2] = key_words[2] + C;
+ out_words[3] = key_words[3] + D;
+
+}
diff --git a/lib/playfair/omg_hax.c b/lib/playfair/omg_hax.c
new file mode 100644
index 0000000..96f79a9
--- /dev/null
+++ b/lib/playfair/omg_hax.c
@@ -0,0 +1,540 @@
+void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut);
+void sap_hash(unsigned char* blockIn, unsigned char* keyOut);
+
+#include
+#include
+#include
+#include
+#include "omg_hax.h"
+
+#define printf(...) (void)0;
+
+void xor_blocks(unsigned char* a, unsigned char* b, unsigned char* out)
+{
+ for (int i = 0; i < 16; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+
+void z_xor(unsigned char* in, unsigned char* out, int blocks)
+{
+ for (int j = 0; j < blocks; j++)
+ for (int i = 0; i < 16; i++)
+ out[j*16+i] = in[j*16+i] ^ z_key[i];
+}
+
+void x_xor(unsigned char* in, unsigned char* out, int blocks)
+{
+ for (int j = 0; j < blocks; j++)
+ for (int i = 0; i < 16; i++)
+ out[j*16+i] = in[j*16+i] ^ x_key[i];
+}
+
+
+void t_xor(unsigned char* in, unsigned char* out)
+{
+ for (int i = 0; i < 16; i++)
+ out[i] = in[i] ^ t_key[i];
+}
+
+unsigned char sap_iv[] = {0x2B,0x84,0xFB,0x79,0xDA,0x75,0xB9,0x04,0x6C,0x24,0x73,0xF7,0xD1,0xC4,0xAB,0x0E,0x2B,0x84,0xFB,0x79,0x75,0xB9,0x04,0x6C,0x24,0x73};
+
+unsigned char sap_key_material[] = {0xA1, 0x1A, 0x4A, 0x83,
+ 0xF2, 0x7A, 0x75, 0xEE,
+ 0xA2, 0x1A, 0x7D, 0xB8,
+ 0x8D, 0x77, 0x92, 0xAB};
+
+unsigned char index_mangle[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C};
+
+unsigned char* table_index(int i)
+{
+ return &table_s1[((31*i) % 0x28) << 8];
+}
+
+unsigned char* message_table_index(int i)
+{
+ return &table_s2[(97*i % 144) << 8];
+}
+
+void print_block(char* msg, unsigned char* dword)
+{
+ printf("%s", msg);
+ for (int i = 0; i < 16; i++)
+ printf("%02X ", dword[i]);
+ printf("\n");
+}
+
+void permute_block_1(unsigned char* block)
+{
+ block[0] = table_s3[block[0]];
+ block[4] = table_s3[0x400+block[4]];
+ block[8] = table_s3[0x800+block[8]];
+ block[12] = table_s3[0xc00+block[12]];
+
+ unsigned char tmp = block[13];
+ block[13] = table_s3[0x100+block[9]];
+ block[9] = table_s3[0xd00+block[5]];
+ block[5] = table_s3[0x900+block[1]];
+ block[1] = table_s3[0x500+tmp];
+
+ tmp = block[2];
+ block[2] = table_s3[0xa00+block[10]];
+ block[10] = table_s3[0x200+tmp];
+ tmp = block[6];
+ block[6] = table_s3[0xe00+block[14]];
+ block[14] = table_s3[0x600+tmp];
+
+ tmp = block[3];
+ block[3] = table_s3[0xf00+block[7]];
+ block[7] = table_s3[0x300+block[11]];
+ block[11] = table_s3[0x700+block[15]];
+ block[15] = table_s3[0xb00+tmp];
+ print_block("Permutation complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel
+}
+
+unsigned char* permute_table_2(unsigned int i)
+{
+ return &table_s4[((71 * i) % 144) << 8];
+}
+
+void permute_block_2(unsigned char* block, int round)
+{
+ // round is 0..8?
+ printf("Permuting via table2, round %d... (block[0] = %02X)\n", round, block[0]);
+ block[0] = permute_table_2(round*16+0)[block[0]];
+ block[4] = permute_table_2(round*16+4)[block[4]];
+ block[8] = permute_table_2(round*16+8)[block[8]];
+ block[12] = permute_table_2(round*16+12)[block[12]];
+
+ unsigned char tmp = block[13];
+ block[13] = permute_table_2(round*16+13)[block[9]];
+ block[9] = permute_table_2(round*16+9)[block[5]];
+ block[5] = permute_table_2(round*16+5)[block[1]];
+ block[1] = permute_table_2(round*16+1)[tmp];
+
+ tmp = block[2];
+ block[2] = permute_table_2(round*16+2)[block[10]];
+ block[10] = permute_table_2(round*16+10)[tmp];
+ tmp = block[6];
+ block[6] = permute_table_2(round*16+6)[block[14]];
+ block[14] = permute_table_2(round*16+14)[tmp];
+
+ tmp = block[3];
+ block[3] = permute_table_2(round*16+3)[block[7]];
+ block[7] = permute_table_2(round*16+7)[block[11]];
+ block[11] = permute_table_2(round*16+11)[block[15]];
+ block[15] = permute_table_2(round*16+15)[tmp];
+ print_block("Permutation (2) complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel
+}
+
+// This COULD just be Rijndael key expansion, but with a different set of S-boxes
+void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4])
+{
+ uint32_t key_data[4];
+ int i;
+ for (i = 0; i < 11; i++)
+ {
+ key_schedule[i][0] = 0xdeadbeef;
+ key_schedule[i][1] = 0xdeadbeef;
+ key_schedule[i][2] = 0xdeadbeef;
+ key_schedule[i][3] = 0xdeadbeef;
+ }
+ unsigned char* buffer = (unsigned char*)key_data;
+ int ti = 0;
+ printf("Generating key schedule\n");
+ // G
+ print_block("Raw key material: ", key_material);
+ t_xor(key_material, buffer);
+ print_block("G has produced: ", buffer);
+ for (int round = 0; round < 11; round++)
+ {
+ printf("Starting round %d\n", round);
+ // H
+ key_schedule[round][0] = key_data[0];
+ printf("H has set chunk 1 of round %d %08X\n", round, key_schedule[round][0]);
+ printf("H complete\n");
+ // I
+ unsigned char* table1 = table_index(ti);
+ unsigned char* table2 = table_index(ti+1);
+ unsigned char* table3 = table_index(ti+2);
+ unsigned char* table4 = table_index(ti+3);
+ ti += 4;
+ //buffer[0] = (buffer[0] - (4 & (buffer[0] << 1)) + 2) ^ 2 ^ index_mangle[round] ^ table1[buffer[0x0d]];
+ printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0d], table1[buffer[0x0d]]);
+ printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0e], table2[buffer[0x0e]]);
+ printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0f], table3[buffer[0x0f]]);
+ printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0c], table4[buffer[0x0c]]);
+ buffer[0] ^= table1[buffer[0x0d]] ^ index_mangle[round];
+ buffer[1] ^= table2[buffer[0x0e]];
+ buffer[2] ^= table3[buffer[0x0f]];
+ buffer[3] ^= table4[buffer[0x0c]];
+ print_block("After I, buffer is now: ", buffer);
+ printf("I complete\n");
+ // H
+ key_schedule[round][1] = key_data[1];
+ printf("H has set chunk 2 to %08X\n", key_schedule[round][1]);
+
+ printf("H complete\n");
+ // J
+ key_data[1] ^= key_data[0];
+ printf("J complete\n");
+ print_block("Buffer is now ", buffer);
+ // H
+ key_schedule[round][2] = key_data[2];
+ printf("H has set chunk3 to %08X\n", key_schedule[round][2]);
+ printf("H complete\n");
+
+ // J
+ key_data[2] ^= key_data[1];
+ printf("J complete\n");
+ // K and L
+ // Implement K and L to fill in other bits of the key schedule
+ key_schedule[round][3] = key_data[3];
+ // J again
+ key_data[3] ^= key_data[2];
+ printf("J complete\n");
+ }
+ for (i = 0; i < 11; i++)
+ print_block("Schedule: ", (unsigned char*)key_schedule[i]);
+}
+
+// This MIGHT just be AES, or some variant thereof.
+void cycle(unsigned char* block, uint32_t key_schedule[11][4])
+{
+ uint32_t ptr1 = 0;
+ uint32_t ptr2 = 0;
+ uint32_t ptr3 = 0;
+ uint32_t ptr4 = 0;
+ uint32_t ab;
+ unsigned char* buffer = (unsigned char*)&ab;
+ uint32_t* bWords = (uint32_t*)block;
+ bWords[0] ^= key_schedule[10][0];
+ bWords[1] ^= key_schedule[10][1];
+ bWords[2] ^= key_schedule[10][2];
+ bWords[3] ^= key_schedule[10][3];
+ // First, these are permuted
+ permute_block_1(block);
+
+ for (int round = 0; round < 9; round++)
+ {
+ // E
+ // Note that table_s5 is a table of 4-byte words. Therefore we do not need to <<2 these indices
+ // TODO: Are these just T-tables?
+ unsigned char* key0 = (unsigned char*)&key_schedule[9-round][0];
+ ptr1 = table_s5[block[3] ^ key0[3]];
+ ptr2 = table_s6[block[2] ^ key0[2]];
+ ptr3 = table_s8[block[0] ^ key0[0]];
+ ptr4 = table_s7[block[1] ^ key0[1]];
+
+ // A B
+ ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4;
+ printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab);
+ // C
+ ((uint32_t*)block)[0] = ab;
+ printf("f7 = %02X\n", block[7]);
+ unsigned char* key1 = (unsigned char*)&key_schedule[9-round][1];
+ ptr2 = table_s5[block[7] ^ key1[3]];
+ ptr1 = table_s6[block[6] ^ key1[2]];
+ ptr4 = table_s7[block[5] ^ key1[1]];
+ ptr3 = table_s8[block[4] ^ key1[0]];
+ // A B again
+ ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4;
+ printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab);
+ // D is a bit of a nightmare, but it is really not as complicated as you might think
+ unsigned char* key2 = (unsigned char*)&key_schedule[9-round][2];
+ unsigned char* key3 = (unsigned char*)&key_schedule[9-round][3];
+ ((uint32_t*)block)[1] = ab;
+ ((uint32_t*)block)[2] = table_s5[block[11] ^ key2[3]] ^
+ table_s6[block[10] ^ key2[2]] ^
+ table_s7[block[9] ^ key2[1]] ^
+ table_s8[block[8] ^ key2[0]];
+
+ ((uint32_t*)block)[3] = table_s5[block[15] ^ key3[3]] ^
+ table_s6[block[14] ^ key3[2]] ^
+ table_s7[block[13] ^ key3[1]] ^
+ table_s8[block[12] ^ key3[0]];
+ printf("Set block2 = %08X, block3 = %08X\n", ((uint32_t*)block)[2], ((uint32_t*)block)[3]);
+ // In the last round, instead of the permute, we do F
+ permute_block_2(block, 8-round);
+ }
+ printf("Using last bit of key up: %08X xor %08X -> %08X\n", ((uint32_t*)block)[0], key_schedule[0][0], ((uint32_t*)block)[0] ^ key_schedule[0][0]);
+ ((uint32_t*)block)[0] ^= key_schedule[0][0];
+ ((uint32_t*)block)[1] ^= key_schedule[0][1];
+ ((uint32_t*)block)[2] ^= key_schedule[0][2];
+ ((uint32_t*)block)[3] ^= key_schedule[0][3];
+}
+
+
+void decrypt_sap(unsigned char* sapIn, unsigned char* sapOut)
+{
+ uint32_t key_schedule[11][4];
+ unsigned char* iv;
+ print_block("Base sap: ", &sapIn[0xf0]);
+ z_xor(sapIn, sapOut, 16);
+ generate_key_schedule(sap_key_material, key_schedule);
+ print_block("lastSap before cycle: ", &sapOut[0xf0]);
+ for (int i = 0xf0; i >= 0x00; i-=0x10)
+ {
+ printf("Ready to cycle %02X\n", i);
+ cycle(&sapOut[i], key_schedule);
+ print_block("After cycling, block is: ", &sapOut[i]);
+ if (i > 0)
+ { // xor with previous block
+ iv = &sapOut[i-0x10];
+ }
+ else
+ { // xor with sap IV
+ iv = sap_iv;
+ }
+ for (int j = 0; j < 16; j++)
+ {
+ printf("%02X ^ %02X -> %02X\n", sapOut[i+j], iv[j], sapOut[i+j] ^ iv[j]);
+ sapOut[i+j] = sapOut[i+j] ^ iv[j];
+ }
+ printf("Decrypted SAP %02X-%02X:\n", i, i+0xf);
+ print_block("", &sapOut[i]);
+ }
+ // Lastly grind the whole thing through x_key. This is the last time we modify sap
+ x_xor(sapOut, sapOut, 16);
+ printf("Sap is decrypted to\n");
+ for (int i = 0xf0; i >= 0x00; i-=0x10)
+ {
+ printf("Final SAP %02X-%02X: ", i, i+0xf);
+ print_block("", &sapOut[i]);
+ }
+}
+
+unsigned char initial_session_key[] = {0xDC, 0xDC, 0xF3, 0xB9, 0x0B, 0x74, 0xDC, 0xFB, 0x86, 0x7F, 0xF7, 0x60, 0x16, 0x72, 0x90, 0x51};
+
+
+void decrypt_key(unsigned char* decryptedSap, unsigned char* keyIn, unsigned char* iv, unsigned char* keyOut)
+{
+ unsigned char blockIn[16];
+ uint32_t key_schedule[11][4];
+ uint32_t mode_key_schedule[11][4];
+ generate_key_schedule(&decryptedSap[8], key_schedule);
+ printf("Generating mode key:\n");
+ generate_key_schedule(initial_session_key, mode_key_schedule);
+ z_xor(keyIn, blockIn, 1);
+ print_block("Input to cycle is: ", blockIn);
+ cycle(blockIn, key_schedule);
+ for (int j = 0; j < 16; j++)
+ keyOut[j] = blockIn[j] ^ iv[j];
+ print_block("Output from cycle is: ", keyOut);
+ x_xor(keyOut, keyOut, 1);
+}
+
+
+void decryptMessage(unsigned char* messageIn, unsigned char* decryptedMessage)
+{
+ unsigned char buffer[16];
+ int i, j;
+ unsigned char tmp;
+ uint32_t key_schedule[11][4];
+ int mode = messageIn[12]; // 0,1,2,3
+ printf("mode = %02x\n", mode);
+ generate_key_schedule(initial_session_key, key_schedule);
+
+ // For M0-M6 we follow the same pattern
+ for (i = 0; i < 8; i++)
+ {
+ // First, copy in the nth block (we must start with the last one)
+ for (j = 0; j < 16; j++)
+ {
+ if (mode == 3)
+ buffer[j] = messageIn[(0x80-0x10*i)+j];
+ else if (mode == 2 || mode == 1 || mode == 0)
+ buffer[j] = messageIn[(0x10*(i+1))+j];
+ }
+ // do this permutation and update 9 times. Could this be cycle(), or the reverse of cycle()?
+ for (j = 0; j < 9; j++)
+ {
+ int base = 0x80 - 0x10*j;
+ //print_block("About to cycle. Buffer is currently: ", buffer);
+ buffer[0x0] = message_table_index(base+0x0)[buffer[0x0]] ^ message_key[mode][base+0x0];
+ buffer[0x4] = message_table_index(base+0x4)[buffer[0x4]] ^ message_key[mode][base+0x4];
+ buffer[0x8] = message_table_index(base+0x8)[buffer[0x8]] ^ message_key[mode][base+0x8];
+ buffer[0xc] = message_table_index(base+0xc)[buffer[0xc]] ^ message_key[mode][base+0xc];
+
+ tmp = buffer[0x0d];
+ buffer[0xd] = message_table_index(base+0xd)[buffer[0x9]] ^ message_key[mode][base+0xd];
+ buffer[0x9] = message_table_index(base+0x9)[buffer[0x5]] ^ message_key[mode][base+0x9];
+ buffer[0x5] = message_table_index(base+0x5)[buffer[0x1]] ^ message_key[mode][base+0x5];
+ buffer[0x1] = message_table_index(base+0x1)[tmp] ^ message_key[mode][base+0x1];
+
+ tmp = buffer[0x02];
+ buffer[0x2] = message_table_index(base+0x2)[buffer[0xa]] ^ message_key[mode][base+0x2];
+ buffer[0xa] = message_table_index(base+0xa)[tmp] ^ message_key[mode][base+0xa];
+ tmp = buffer[0x06];
+ buffer[0x6] = message_table_index(base+0x6)[buffer[0xe]] ^ message_key[mode][base+0x6];
+ buffer[0xe] = message_table_index(base+0xe)[tmp] ^ message_key[mode][base+0xe];
+
+ tmp = buffer[0x3];
+ buffer[0x3] = message_table_index(base+0x3)[buffer[0x7]] ^ message_key[mode][base+0x3];
+ buffer[0x7] = message_table_index(base+0x7)[buffer[0xb]] ^ message_key[mode][base+0x7];
+ buffer[0xb] = message_table_index(base+0xb)[buffer[0xf]] ^ message_key[mode][base+0xb];
+ buffer[0xf] = message_table_index(base+0xf)[tmp] ^ message_key[mode][base+0xf];
+
+ // Now we must replace the entire buffer with 4 words that we read and xor together
+ uint32_t word;
+ uint32_t* block = (uint32_t*)buffer;
+
+ block[0] = table_s9[0x000 + buffer[0x0]] ^
+ table_s9[0x100 + buffer[0x1]] ^
+ table_s9[0x200 + buffer[0x2]] ^
+ table_s9[0x300 + buffer[0x3]];
+ block[1] = table_s9[0x000 + buffer[0x4]] ^
+ table_s9[0x100 + buffer[0x5]] ^
+ table_s9[0x200 + buffer[0x6]] ^
+ table_s9[0x300 + buffer[0x7]];
+ block[2] = table_s9[0x000 + buffer[0x8]] ^
+ table_s9[0x100 + buffer[0x9]] ^
+ table_s9[0x200 + buffer[0xa]] ^
+ table_s9[0x300 + buffer[0xb]];
+ block[3] = table_s9[0x000 + buffer[0xc]] ^
+ table_s9[0x100 + buffer[0xd]] ^
+ table_s9[0x200 + buffer[0xe]] ^
+ table_s9[0x300 + buffer[0xf]];
+ }
+ // Next, another permute with a different table
+ buffer[0x0] = table_s10[(0x0 << 8) + buffer[0x0]];
+ buffer[0x4] = table_s10[(0x4 << 8) + buffer[0x4]];
+ buffer[0x8] = table_s10[(0x8 << 8) + buffer[0x8]];
+ buffer[0xc] = table_s10[(0xc << 8) + buffer[0xc]];
+
+ tmp = buffer[0x0d];
+ buffer[0xd] = table_s10[(0xd << 8) + buffer[0x9]];
+ buffer[0x9] = table_s10[(0x9 << 8) + buffer[0x5]];
+ buffer[0x5] = table_s10[(0x5 << 8) + buffer[0x1]];
+ buffer[0x1] = table_s10[(0x1 << 8) + tmp];
+
+ tmp = buffer[0x02];
+ buffer[0x2] = table_s10[(0x2 << 8) + buffer[0xa]];
+ buffer[0xa] = table_s10[(0xa << 8) + tmp];
+ tmp = buffer[0x06];
+ buffer[0x6] = table_s10[(0x6 << 8) + buffer[0xe]];
+ buffer[0xe] = table_s10[(0xe << 8) + tmp];
+
+ tmp = buffer[0x3];
+ buffer[0x3] = table_s10[(0x3 << 8) + buffer[0x7]];
+ buffer[0x7] = table_s10[(0x7 << 8) + buffer[0xb]];
+ buffer[0xb] = table_s10[(0xb << 8) + buffer[0xf]];
+ buffer[0xf] = table_s10[(0xf << 8) + tmp];
+
+ // And finally xor with the previous block of the message, except in mode-2 where we do this in reverse
+ if (mode == 2 || mode == 1 || mode == 0)
+ {
+ if (i > 0)
+ {
+ xor_blocks(buffer, &messageIn[0x10*i], &decryptedMessage[0x10*i]); // remember that the first 0x10 bytes are the header
+ }
+ else
+ xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x10*i]);
+ print_block(" ", &decryptedMessage[0x10*i]);
+ }
+ else
+ {
+ if (i < 7)
+ xor_blocks(buffer, &messageIn[0x70 - 0x10*i], &decryptedMessage[0x70 - 0x10*i]);
+ else
+ xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x70 - 0x10*i]);
+ printf("Decrypted message block %02X-%02X:", 0x70 - 0x10*i, 0x70 - 0x10*i+0xf);
+ print_block(" ", &decryptedMessage[0x70 - 0x10*i]);
+ }
+ }
+}
+
+unsigned char static_source_1[] = {0xFA, 0x9C, 0xAD, 0x4D, 0x4B, 0x68, 0x26, 0x8C, 0x7F, 0xF3, 0x88, 0x99, 0xDE, 0x92, 0x2E, 0x95,
+ 0x1E};
+unsigned char static_source_2[] = {0xEC, 0x4E, 0x27, 0x5E, 0xFD, 0xF2, 0xE8, 0x30, 0x97, 0xAE, 0x70, 0xFB, 0xE0, 0x00, 0x3F, 0x1C,
+ 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x09, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00};
+
+void swap_bytes(unsigned char* a, unsigned char *b)
+{
+ unsigned char c = *a;
+ *a = *b;
+ *b = c;
+}
+
+void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey)
+{
+ unsigned char decryptedMessage[128];
+ unsigned char newSap[320];
+ unsigned char Q[210];
+ int i;
+ int round;
+ unsigned char md5[16];
+ unsigned char otherHash[16];
+
+ decryptMessage(messageIn, decryptedMessage);
+ // Now that we have the decrypted message, we can combine it with our initial sap to form the 5 blocks needed to generate the 5 words which, when added together, give
+ // the session key.
+ memcpy(&newSap[0x000], static_source_1, 0x11);
+ memcpy(&newSap[0x011], decryptedMessage, 0x80);
+ memcpy(&newSap[0x091], &oldSap[0x80], 0x80);
+ memcpy(&newSap[0x111], static_source_2, 0x2f);
+ memcpy(sessionKey, initial_session_key, 16);
+
+ for (round = 0; round < 5; round++)
+ {
+ unsigned char* base = &newSap[round * 64];
+ print_block("Input block: ", &base[0]);
+ print_block("Input block: ", &base[0x10]);
+ print_block("Input block: ", &base[0x20]);
+ print_block("Input block: ", &base[0x30]);
+ modified_md5(base, sessionKey, md5);
+ printf("MD5 OK\n");
+ sap_hash(base, sessionKey);
+ printf("OtherHash OK\n");
+
+ printf("MD5 = ");
+ for (i = 0; i < 4; i++)
+ printf("%08x ", ((uint32_t*)md5)[i]);
+ printf("\nOtherHash = ");
+ for (i = 0; i < 4; i++)
+ printf("%08x ", ((uint32_t*)sessionKey)[i]);
+ printf("\n");
+
+ uint32_t* sessionKeyWords = (uint32_t*)sessionKey;
+ uint32_t* md5Words = (uint32_t*)md5;
+ for (i = 0; i < 4; i++)
+ {
+ sessionKeyWords[i] = (sessionKeyWords[i] + md5Words[i]) & 0xffffffff;
+ }
+ printf("Current key: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", sessionKey[i]);
+ printf("\n");
+ }
+ for (i = 0; i < 16; i+=4)
+ {
+ swap_bytes(&sessionKey[i], &sessionKey[i+3]);
+ swap_bytes(&sessionKey[i+1], &sessionKey[i+2]);
+ }
+
+ // Finally the whole thing is XORd with 121:
+ for (i = 0; i < 16; i++)
+ sessionKey[i] ^= 121;
+ print_block("Session key computed as: ", sessionKey);
+}
+
+unsigned char default_sap[] =
+ { 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+ 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+ 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+ 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+ 0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x53,
+ 0x00, 0x01, 0xcc, 0x34, 0x2a, 0x5e, 0x5b, 0x1a, 0x67, 0x73, 0xc2, 0x0e, 0x21, 0xb8, 0x22, 0x4d,
+ 0xf8, 0x62, 0x48, 0x18, 0x64, 0xef, 0x81, 0x0a, 0xae, 0x2e, 0x37, 0x03, 0xc8, 0x81, 0x9c, 0x23,
+ 0x53, 0x9d, 0xe5, 0xf5, 0xd7, 0x49, 0xbc, 0x5b, 0x7a, 0x26, 0x6c, 0x49, 0x62, 0x83, 0xce, 0x7f,
+ 0x03, 0x93, 0x7a, 0xe1, 0xf6, 0x16, 0xde, 0x0c, 0x15, 0xff, 0x33, 0x8c, 0xca, 0xff, 0xb0, 0x9e,
+ 0xaa, 0xbb, 0xe4, 0x0f, 0x5d, 0x5f, 0x55, 0x8f, 0xb9, 0x7f, 0x17, 0x31, 0xf8, 0xf7, 0xda, 0x60,
+ 0xa0, 0xec, 0x65, 0x79, 0xc3, 0x3e, 0xa9, 0x83, 0x12, 0xc3, 0xb6, 0x71, 0x35, 0xa6, 0x69, 0x4f,
+ 0xf8, 0x23, 0x05, 0xd9, 0xba, 0x5c, 0x61, 0x5f, 0xa2, 0x54, 0xd2, 0xb1, 0x83, 0x45, 0x83, 0xce,
+ 0xe4, 0x2d, 0x44, 0x26, 0xc8, 0x35, 0xa7, 0xa5, 0xf6, 0xc8, 0x42, 0x1c, 0x0d, 0xa3, 0xf1, 0xc7,
+ 0x00, 0x50, 0xf2, 0xe5, 0x17, 0xf8, 0xd0, 0xfa, 0x77, 0x8d, 0xfb, 0x82, 0x8d, 0x40, 0xc7, 0x8e,
+ 0x94, 0x1e, 0x1e, 0x1e};
diff --git a/lib/playfair/omg_hax.h b/lib/playfair/omg_hax.h
new file mode 100644
index 0000000..d6f19d3
--- /dev/null
+++ b/lib/playfair/omg_hax.h
@@ -0,0 +1,32 @@
+unsigned char message_key[4][144] = {{0x1D, 0x24, 0x03, 0x40, 0xDC, 0xAE, 0xC7, 0xA8, 0x26, 0x7C, 0x20, 0x99, 0x5D, 0x7E, 0x89, 0x2E, 0xA2, 0x58, 0xAF, 0xBE, 0xB8, 0x07, 0x9A, 0x2F, 0x87, 0x77, 0xD3, 0xCE, 0x37, 0x3E, 0x1B, 0x16, 0x41, 0x4F, 0x4E, 0xBE, 0x62, 0x5A, 0x00, 0x77, 0xC6, 0xEB, 0xDA, 0x4B, 0x97, 0x1A, 0x61, 0x8D, 0x31, 0x32, 0x1C, 0xA2, 0x78, 0x9B, 0x66, 0x72, 0x60, 0x94, 0x44, 0x86, 0xCB, 0x09, 0xBD, 0x3A, 0x77, 0x57, 0xC1, 0x72, 0x61, 0x1D, 0x32, 0xC7, 0x85, 0xD1, 0xEF, 0xE5, 0x4D, 0x95, 0x0B, 0xF0, 0xD8, 0x18, 0xE7, 0x4A, 0xDC, 0x77, 0xCA, 0x55, 0x28, 0x32, 0x93, 0x2A, 0x7B, 0x3E, 0x3A, 0xD4, 0x97, 0xFD, 0x7D, 0x6D, 0x95, 0x71, 0x27, 0x9C, 0x77, 0x6A, 0x7C, 0xD5, 0xBF, 0x9D, 0x0E, 0xF2, 0x0F, 0x55, 0x91, 0x29, 0xCF, 0xAA, 0x58, 0x1C, 0x7A, 0xE7, 0xCB, 0x8B, 0x20, 0x07, 0x53, 0xAA, 0x59, 0x40, 0x3B, 0x03, 0xBE, 0x33, 0x47, 0x47, 0x5A, 0x4F, 0x86, 0x31, 0x8D, 0x30, 0xF9, 0x1C},
+ {0xF1, 0xA2, 0x04, 0x7A, 0xAE, 0xE9, 0xD8, 0xBF, 0xD4, 0xC0, 0x6B, 0x77, 0xC1, 0x05, 0x8C, 0x99, 0xA9, 0xFD, 0x3D, 0x44, 0xEE, 0x7B, 0x6C, 0x28, 0x42, 0x31, 0x63, 0x87, 0x6D, 0xD2, 0x6D, 0x48, 0xCC, 0x4E, 0x93, 0x31, 0x7B, 0x27, 0x14, 0xFC, 0x2D, 0x71, 0x5D, 0xE4, 0xB0, 0xF9, 0x4B, 0x82, 0x76, 0x52, 0xD5, 0x02, 0x6C, 0xB6, 0xCF, 0x57, 0xFE, 0xB2, 0xBF, 0xB7, 0x30, 0x56, 0x7B, 0x9B, 0x3E, 0x3E, 0xB0, 0x47, 0x10, 0x63, 0xE8, 0x72, 0x1C, 0x38, 0x2D, 0x79, 0xC4, 0x77, 0x3C, 0xD1, 0xED, 0x02, 0x43, 0x03, 0x5C, 0xBC, 0x57, 0x9E, 0x43, 0x02, 0x67, 0xA1, 0x9B, 0x8C, 0xF3, 0x54, 0xE4, 0x46, 0xE1, 0x1C, 0x4F, 0xDC, 0xF7, 0x9F, 0xF4, 0x49, 0x76, 0x4F, 0x13, 0x96, 0x86, 0xCF, 0xF1, 0x7A, 0x01, 0xAC, 0xE4, 0xD5, 0x32, 0x5B, 0x5D, 0x7D, 0xEE, 0xCA, 0xBF, 0x76, 0xFB, 0x50, 0xD7, 0xEC, 0x9C, 0xA3, 0xF6, 0x2E, 0xBE, 0x9B, 0xC7, 0xC8, 0x0F, 0xF2, 0xB7, 0x3B, 0xDE, 0x8A},
+ {0x18, 0x6E, 0xD3, 0x73, 0x5E, 0xE9, 0x5A, 0x8F, 0x66, 0x3F, 0xF1, 0xB8, 0x4A, 0x62, 0xD9, 0xC0, 0xD2, 0x08, 0x13, 0x61, 0xCB, 0xF3, 0xAD, 0xA6, 0x26, 0x4D, 0x3A, 0x7B, 0x06, 0xB5, 0x51, 0x56, 0xFE, 0x66, 0x0A, 0xD8, 0x3A, 0xAA, 0x47, 0x49, 0xD3, 0x7C, 0xC3, 0x68, 0x70, 0xD0, 0x96, 0x80, 0x6A, 0x05, 0x90, 0xEF, 0xAF, 0x43, 0x42, 0xC4, 0x2E, 0x50, 0x4C, 0x96, 0x13, 0xB5, 0x2E, 0x4C, 0x80, 0xA2, 0x8D, 0x23, 0xEE, 0xE2, 0x5E, 0x78, 0xF4, 0x3D, 0x65, 0xCA, 0x71, 0x4F, 0x68, 0x9E, 0x4B, 0x43, 0x58, 0x7B, 0x47, 0x96, 0x40, 0x81, 0x8A, 0x98, 0x6C, 0x04, 0x33, 0x0F, 0x2F, 0x1C, 0x33, 0x8E, 0xEA, 0xA1, 0x4F, 0xA8, 0x37, 0x93, 0x17, 0x1D, 0x8D, 0x18, 0xA9, 0x6A, 0x1B, 0x07, 0x7C, 0xB6, 0x08, 0x58, 0x1F, 0x12, 0x00, 0xFA, 0x37, 0x4D, 0x7F, 0xBA, 0xA5, 0x00, 0x6B, 0x72, 0x78, 0x9C, 0x33, 0xE8, 0x41, 0x07, 0xB7, 0xC1, 0x67, 0x9B, 0x76, 0xBB, 0xDD, 0x91, 0x3D, 0x3D},
+ {0x47, 0x69, 0x9F, 0x08, 0xB8, 0x82, 0xFB, 0xA1, 0x95, 0xE5, 0x6F, 0x41, 0x79, 0x1E, 0x0C, 0xB6, 0xA1, 0xCA, 0x11, 0x0A, 0xE2, 0x87, 0x2C, 0x7E, 0x39, 0xBC, 0x98, 0xA5, 0x1E, 0xB2, 0xFA, 0x1F, 0xEE, 0x73, 0x42, 0xD7, 0xA9, 0x09, 0x42, 0xC0, 0xEF, 0xC4, 0x44, 0x0C, 0x0F, 0x6F, 0x97, 0x09, 0x08, 0xBC, 0x66, 0x31, 0x33, 0xFF, 0xCA, 0x7E, 0xB5, 0xE9, 0x7D, 0x77, 0x98, 0xC0, 0xD2, 0x6A, 0xFD, 0x2F, 0x0B, 0x6C, 0x9D, 0xAB, 0xAA, 0x78, 0x4C, 0x76, 0xDE, 0x21, 0xBF, 0xF4, 0x3A, 0x28, 0x2A, 0xC4, 0x74, 0xB4, 0xA9, 0x1B, 0x9A, 0x38, 0x21, 0x4C, 0xEB, 0xBD, 0x72, 0x51, 0xA6, 0x15, 0xD4, 0x9E, 0x17, 0xF3, 0x94, 0x26, 0x6D, 0x07, 0x5F, 0x92, 0xAA, 0xA4, 0x4E, 0xF2, 0xCD, 0x3F, 0x02, 0x4F, 0x05, 0x35, 0xE3, 0x58, 0xDF, 0x82, 0x7E, 0x6A, 0x17, 0xF0, 0x5F, 0x6B, 0xDC, 0xE9, 0x3A, 0xCF, 0x04, 0xB3, 0x01, 0x44, 0x87, 0xD7, 0xBC, 0xAD, 0x3D, 0x74, 0x96, 0x74, 0xA3, 0x99}};
+unsigned char message_iv[4][16] = {{0x57, 0x52, 0xF1, 0xB7, 0x54, 0x9D, 0x8F, 0x87, 0x0C, 0x10, 0x48, 0x5A, 0x60, 0x88, 0xCA, 0xDB},
+ {0xDF, 0x7B, 0x15, 0x63, 0xF0, 0x05, 0x58, 0x77, 0x52, 0xA9, 0x04, 0x02, 0xB9, 0xA3, 0x92, 0x95},
+ {0x68, 0xB5, 0x46, 0x11, 0xFB, 0x04, 0xDE, 0x67, 0x6C, 0x96, 0x8E, 0xFB, 0x8C, 0x9D, 0xB0, 0xC9},
+ {0x27, 0x07, 0x8B, 0x21, 0x23, 0x36, 0x1E, 0x7A, 0xDC, 0x9D, 0x0B, 0x11, 0x53, 0x54, 0x69, 0x0D}};
+
+unsigned char z_key[] = {0x1a, 0x64, 0xf9, 0x60, 0x6c, 0xe3, 0x01, 0xa9, 0x54, 0x48, 0x1b, 0xd4, 0xab, 0x81, 0xfc, 0xc6};
+unsigned char x_key[] = {0x8e, 0xba, 0x07, 0xcc, 0xb6, 0x5a, 0xf6, 0x20, 0x33, 0xcf, 0xf8, 0x42, 0xe5, 0xd5, 0x5a, 0x7d};
+unsigned char t_key[] = {0xd0, 0x04, 0xa9, 0x61, 0x6b, 0xa4, 0x00, 0x87, 0x68, 0x8b, 0x5f, 0x15, 0x15, 0x35, 0xd9, 0xa9};
+
+unsigned long table_s5[] = {0x21aa8423, 0x2fa1892a, 0x3dbc9e31, 0x33b79338, 0x1986b007, 0x178dbd0e, 0x590aa15, 0xb9ba71c, 0x51f2ec6b, 0x5ff9e162, 0x4de4f679, 0x43effb70, 0x69ded84f, 0x67d5d546, 0x75c8c25d, 0x7bc3cf54, 0xc11a54b3, 0xcf1159ba, 0xdd0c4ea1, 0xd30743a8, 0xf9366097, 0xf73d6d9e, 0xe5207a85, 0xeb2b778c, 0xb1423cfb, 0xbf4931f2, 0xad5426e9, 0xa35f2be0, 0x896e08df, 0x876505d6, 0x957812cd, 0x9b731fc4, 0xfad13f18, 0xf4da3211, 0xe6c7250a, 0xe8cc2803, 0xc2fd0b3c, 0xccf60635, 0xdeeb112e, 0xd0e01c27, 0x8a895750, 0x84825a59, 0x969f4d42, 0x9894404b, 0xb2a56374, 0xbcae6e7d, 0xaeb37966, 0xa0b8746f, 0x1a61ef88, 0x146ae281, 0x677f59a, 0x87cf893, 0x224ddbac, 0x2c46d6a5, 0x3e5bc1be, 0x3050ccb7, 0x6a3987c0, 0x64328ac9, 0x762f9dd2, 0x782490db, 0x5215b3e4, 0x5c1ebeed, 0x4e03a9f6, 0x4008a4ff, 0x8c5ce955, 0x8257e45c, 0x904af347, 0x9e41fe4e, 0xb470dd71, 0xba7bd078, 0xa866c763, 0xa66dca6a, 0xfc04811d, 0xf20f8c14, 0xe0129b0f, 0xee199606, 0xc428b539, 0xca23b830, 0xd83eaf2b, 0xd635a222, 0x6cec39c5, 0x62e734cc, 0x70fa23d7, 0x7ef12ede, 0x54c00de1, 0x5acb00e8, 0x48d617f3, 0x46dd1afa, 0x1cb4518d, 0x12bf5c84, 0xa24b9f, 0xea94696, 0x249865a9, 0x2a9368a0, 0x388e7fbb, 0x368572b2, 0x5727526e, 0x592c5f67, 0x4b31487c, 0x453a4575, 0x6f0b664a, 0x61006b43, 0x731d7c58, 0x7d167151, 0x277f3a26, 0x2974372f, 0x3b692034, 0x35622d3d, 0x1f530e02, 0x1158030b, 0x3451410, 0xd4e1919, 0xb79782fe, 0xb99c8ff7, 0xab8198ec, 0xa58a95e5, 0x8fbbb6da, 0x81b0bbd3, 0x93adacc8, 0x9da6a1c1, 0xc7cfeab6, 0xc9c4e7bf, 0xdbd9f0a4, 0xd5d2fdad, 0xffe3de92, 0xf1e8d39b, 0xe3f5c480, 0xedfec989, 0x605d5ecf, 0x6e5653c6, 0x7c4b44dd, 0x724049d4, 0x58716aeb, 0x567a67e2, 0x446770f9, 0x4a6c7df0, 0x10053687, 0x1e0e3b8e, 0xc132c95, 0x218219c, 0x282902a3, 0x26220faa, 0x343f18b1, 0x3a3415b8, 0x80ed8e5f, 0x8ee68356, 0x9cfb944d, 0x92f09944, 0xb8c1ba7b, 0xb6cab772, 0xa4d7a069, 0xaadcad60, 0xf0b5e617, 0xfebeeb1e, 0xeca3fc05, 0xe2a8f10c, 0xc899d233, 0xc692df3a, 0xd48fc821, 0xda84c528, 0xbb26e5f4, 0xb52de8fd, 0xa730ffe6, 0xa93bf2ef, 0x830ad1d0, 0x8d01dcd9, 0x9f1ccbc2, 0x9117c6cb, 0xcb7e8dbc, 0xc57580b5, 0xd76897ae, 0xd9639aa7, 0xf352b998, 0xfd59b491, 0xef44a38a, 0xe14fae83, 0x5b963564, 0x559d386d, 0x47802f76, 0x498b227f, 0x63ba0140, 0x6db10c49, 0x7fac1b52, 0x71a7165b, 0x2bce5d2c, 0x25c55025, 0x37d8473e, 0x39d34a37, 0x13e26908, 0x1de96401, 0xff4731a, 0x1ff7e13, 0xcdab33b9, 0xc3a03eb0, 0xd1bd29ab, 0xdfb624a2, 0xf587079d, 0xfb8c0a94, 0xe9911d8f, 0xe79a1086, 0xbdf35bf1, 0xb3f856f8, 0xa1e541e3, 0xafee4cea, 0x85df6fd5, 0x8bd462dc, 0x99c975c7, 0x97c278ce, 0x2d1be329, 0x2310ee20, 0x310df93b, 0x3f06f432, 0x1537d70d, 0x1b3cda04, 0x921cd1f, 0x72ac016, 0x5d438b61, 0x53488668, 0x41559173, 0x4f5e9c7a, 0x656fbf45, 0x6b64b24c, 0x7979a557, 0x7772a85e, 0x16d08882, 0x18db858b, 0xac69290, 0x4cd9f99, 0x2efcbca6, 0x20f7b1af, 0x32eaa6b4, 0x3ce1abbd, 0x6688e0ca, 0x6883edc3, 0x7a9efad8, 0x7495f7d1, 0x5ea4d4ee, 0x50afd9e7, 0x42b2cefc, 0x4cb9c3f5, 0xf6605812, 0xf86b551b, 0xea764200, 0xe47d4f09, 0xce4c6c36, 0xc047613f, 0xd25a7624, 0xdc517b2d, 0x8638305a, 0x88333d53, 0x9a2e2a48, 0x94252741, 0xbe14047e, 0xb01f0977, 0xa2021e6c, 0xac091365};
+
+unsigned long table_s6[] = {0x5ee7493, 0xce07f9e, 0x17f26289, 0x1efc6984, 0x21d658a7, 0x28d853aa, 0x33ca4ebd, 0x3ac445b0, 0x4d9e2cfb, 0x449027f6, 0x5f823ae1, 0x568c31ec, 0x69a600cf, 0x60a80bc2, 0x7bba16d5, 0x72b41dd8, 0x950ec443, 0x9c00cf4e, 0x8712d259, 0x8e1cd954, 0xb136e877, 0xb838e37a, 0xa32afe6d, 0xaa24f560, 0xdd7e9c2b, 0xd4709726, 0xcf628a31, 0xc66c813c, 0xf946b01f, 0xf048bb12, 0xeb5aa605, 0xe254ad08, 0x3e350f28, 0x373b0425, 0x2c291932, 0x2527123f, 0x1a0d231c, 0x13032811, 0x8113506, 0x11f3e0b, 0x76455740, 0x7f4b5c4d, 0x6459415a, 0x6d574a57, 0x527d7b74, 0x5b737079, 0x40616d6e, 0x496f6663, 0xaed5bff8, 0xa7dbb4f5, 0xbcc9a9e2, 0xb5c7a2ef, 0x8aed93cc, 0x83e398c1, 0x98f185d6, 0x91ff8edb, 0xe6a5e790, 0xefabec9d, 0xf4b9f18a, 0xfdb7fa87, 0xc29dcba4, 0xcb93c0a9, 0xd081ddbe, 0xd98fd6b3, 0x734382fe, 0x7a4d89f3, 0x615f94e4, 0x68519fe9, 0x577baeca, 0x5e75a5c7, 0x4567b8d0, 0x4c69b3dd, 0x3b33da96, 0x323dd19b, 0x292fcc8c, 0x2021c781, 0x1f0bf6a2, 0x1605fdaf, 0xd17e0b8, 0x419ebb5, 0xe3a3322e, 0xeaad3923, 0xf1bf2434, 0xf8b12f39, 0xc79b1e1a, 0xce951517, 0xd5870800, 0xdc89030d, 0xabd36a46, 0xa2dd614b, 0xb9cf7c5c, 0xb0c17751, 0x8feb4672, 0x86e54d7f, 0x9df75068, 0x94f95b65, 0x4898f945, 0x4196f248, 0x5a84ef5f, 0x538ae452, 0x6ca0d571, 0x65aede7c, 0x7ebcc36b, 0x77b2c866, 0xe8a12d, 0x9e6aa20, 0x12f4b737, 0x1bfabc3a, 0x24d08d19, 0x2dde8614, 0x36cc9b03, 0x3fc2900e, 0xd8784995, 0xd1764298, 0xca645f8f, 0xc36a5482, 0xfc4065a1, 0xf54e6eac, 0xee5c73bb, 0xe75278b6, 0x900811fd, 0x99061af0, 0x821407e7, 0x8b1a0cea, 0xb4303dc9, 0xbd3e36c4, 0xa62c2bd3, 0xaf2220de, 0xe9af8349, 0xe0a18844, 0xfbb39553, 0xf2bd9e5e, 0xcd97af7d, 0xc499a470, 0xdf8bb967, 0xd685b26a, 0xa1dfdb21, 0xa8d1d02c, 0xb3c3cd3b, 0xbacdc636, 0x85e7f715, 0x8ce9fc18, 0x97fbe10f, 0x9ef5ea02, 0x794f3399, 0x70413894, 0x6b532583, 0x625d2e8e, 0x5d771fad, 0x547914a0, 0x4f6b09b7, 0x466502ba, 0x313f6bf1, 0x383160fc, 0x23237deb, 0x2a2d76e6, 0x150747c5, 0x1c094cc8, 0x71b51df, 0xe155ad2, 0xd274f8f2, 0xdb7af3ff, 0xc068eee8, 0xc966e5e5, 0xf64cd4c6, 0xff42dfcb, 0xe450c2dc, 0xed5ec9d1, 0x9a04a09a, 0x930aab97, 0x8818b680, 0x8116bd8d, 0xbe3c8cae, 0xb73287a3, 0xac209ab4, 0xa52e91b9, 0x42944822, 0x4b9a432f, 0x50885e38, 0x59865535, 0x66ac6416, 0x6fa26f1b, 0x74b0720c, 0x7dbe7901, 0xae4104a, 0x3ea1b47, 0x18f80650, 0x11f60d5d, 0x2edc3c7e, 0x27d23773, 0x3cc02a64, 0x35ce2169, 0x9f027524, 0x960c7e29, 0x8d1e633e, 0x84106833, 0xbb3a5910, 0xb234521d, 0xa9264f0a, 0xa0284407, 0xd7722d4c, 0xde7c2641, 0xc56e3b56, 0xcc60305b, 0xf34a0178, 0xfa440a75, 0xe1561762, 0xe8581c6f, 0xfe2c5f4, 0x6eccef9, 0x1dfed3ee, 0x14f0d8e3, 0x2bdae9c0, 0x22d4e2cd, 0x39c6ffda, 0x30c8f4d7, 0x47929d9c, 0x4e9c9691, 0x558e8b86, 0x5c80808b, 0x63aab1a8, 0x6aa4baa5, 0x71b6a7b2, 0x78b8acbf, 0xa4d90e9f, 0xadd70592, 0xb6c51885, 0xbfcb1388, 0x80e122ab, 0x89ef29a6, 0x92fd34b1, 0x9bf33fbc, 0xeca956f7, 0xe5a75dfa, 0xfeb540ed, 0xf7bb4be0, 0xc8917ac3, 0xc19f71ce, 0xda8d6cd9, 0xd38367d4, 0x3439be4f, 0x3d37b542, 0x2625a855, 0x2f2ba358, 0x1001927b, 0x190f9976, 0x21d8461, 0xb138f6c, 0x7c49e627, 0x7547ed2a, 0x6e55f03d, 0x675bfb30, 0x5871ca13, 0x517fc11e, 0x4a6ddc09, 0x4363d704};
+
+unsigned long table_s7[] = {0xb33a6e73, 0xbe336078, 0xa9287265, 0xa4217c6e, 0x871e565f, 0x8a175854, 0x9d0c4a49, 0x90054442, 0xdb721e2b, 0xd67b1020, 0xc160023d, 0xcc690c36, 0xef562607, 0xe25f280c, 0xf5443a11, 0xf84d341a, 0x63aa8ec3, 0x6ea380c8, 0x79b892d5, 0x74b19cde, 0x578eb6ef, 0x5a87b8e4, 0x4d9caaf9, 0x4095a4f2, 0xbe2fe9b, 0x6ebf090, 0x11f0e28d, 0x1cf9ec86, 0x3fc6c6b7, 0x32cfc8bc, 0x25d4daa1, 0x28ddd4aa, 0x801b508, 0x508bb03, 0x1213a91e, 0x1f1aa715, 0x3c258d24, 0x312c832f, 0x26379132, 0x2b3e9f39, 0x6049c550, 0x6d40cb5b, 0x7a5bd946, 0x7752d74d, 0x546dfd7c, 0x5964f377, 0x4e7fe16a, 0x4376ef61, 0xd89155b8, 0xd5985bb3, 0xc28349ae, 0xcf8a47a5, 0xecb56d94, 0xe1bc639f, 0xf6a77182, 0xfbae7f89, 0xb0d925e0, 0xbdd02beb, 0xaacb39f6, 0xa7c237fd, 0x84fd1dcc, 0x89f413c7, 0x9eef01da, 0x93e60fd1, 0xde4cc385, 0xd345cd8e, 0xc45edf93, 0xc957d198, 0xea68fba9, 0xe761f5a2, 0xf07ae7bf, 0xfd73e9b4, 0xb604b3dd, 0xbb0dbdd6, 0xac16afcb, 0xa11fa1c0, 0x82208bf1, 0x8f2985fa, 0x983297e7, 0x953b99ec, 0xedc2335, 0x3d52d3e, 0x14ce3f23, 0x19c73128, 0x3af81b19, 0x37f11512, 0x20ea070f, 0x2de30904, 0x6694536d, 0x6b9d5d66, 0x7c864f7b, 0x718f4170, 0x52b06b41, 0x5fb9654a, 0x48a27757, 0x45ab795c, 0x657718fe, 0x687e16f5, 0x7f6504e8, 0x726c0ae3, 0x515320d2, 0x5c5a2ed9, 0x4b413cc4, 0x464832cf, 0xd3f68a6, 0x3666ad, 0x172d74b0, 0x1a247abb, 0x391b508a, 0x34125e81, 0x23094c9c, 0x2e004297, 0xb5e7f84e, 0xb8eef645, 0xaff5e458, 0xa2fcea53, 0x81c3c062, 0x8ccace69, 0x9bd1dc74, 0x96d8d27f, 0xddaf8816, 0xd0a6861d, 0xc7bd9400, 0xcab49a0b, 0xe98bb03a, 0xe482be31, 0xf399ac2c, 0xfe90a227, 0x69d62f84, 0x64df218f, 0x73c43392, 0x7ecd3d99, 0x5df217a8, 0x50fb19a3, 0x47e00bbe, 0x4ae905b5, 0x19e5fdc, 0xc9751d7, 0x1b8c43ca, 0x16854dc1, 0x35ba67f0, 0x38b369fb, 0x2fa87be6, 0x22a175ed, 0xb946cf34, 0xb44fc13f, 0xa354d322, 0xae5ddd29, 0x8d62f718, 0x806bf913, 0x9770eb0e, 0x9a79e505, 0xd10ebf6c, 0xdc07b167, 0xcb1ca37a, 0xc615ad71, 0xe52a8740, 0xe823894b, 0xff389b56, 0xf231955d, 0xd2edf4ff, 0xdfe4faf4, 0xc8ffe8e9, 0xc5f6e6e2, 0xe6c9ccd3, 0xebc0c2d8, 0xfcdbd0c5, 0xf1d2dece, 0xbaa584a7, 0xb7ac8aac, 0xa0b798b1, 0xadbe96ba, 0x8e81bc8b, 0x8388b280, 0x9493a09d, 0x999aae96, 0x27d144f, 0xf741a44, 0x186f0859, 0x15660652, 0x36592c63, 0x3b502268, 0x2c4b3075, 0x21423e7e, 0x6a356417, 0x673c6a1c, 0x70277801, 0x7d2e760a, 0x5e115c3b, 0x53185230, 0x4403402d, 0x490a4e26, 0x4a08272, 0x9a98c79, 0x1eb29e64, 0x13bb906f, 0x3084ba5e, 0x3d8db455, 0x2a96a648, 0x279fa843, 0x6ce8f22a, 0x61e1fc21, 0x76faee3c, 0x7bf3e037, 0x58ccca06, 0x55c5c40d, 0x42ded610, 0x4fd7d81b, 0xd43062c2, 0xd9396cc9, 0xce227ed4, 0xc32b70df, 0xe0145aee, 0xed1d54e5, 0xfa0646f8, 0xf70f48f3, 0xbc78129a, 0xb1711c91, 0xa66a0e8c, 0xab630087, 0x885c2ab6, 0x855524bd, 0x924e36a0, 0x9f4738ab, 0xbf9b5909, 0xb2925702, 0xa589451f, 0xa8804b14, 0x8bbf6125, 0x86b66f2e, 0x91ad7d33, 0x9ca47338, 0xd7d32951, 0xdada275a, 0xcdc13547, 0xc0c83b4c, 0xe3f7117d, 0xeefe1f76, 0xf9e50d6b, 0xf4ec0360, 0x6f0bb9b9, 0x6202b7b2, 0x7519a5af, 0x7810aba4, 0x5b2f8195, 0x56268f9e, 0x413d9d83, 0x4c349388, 0x743c9e1, 0xa4ac7ea, 0x1d51d5f7, 0x1058dbfc, 0x3367f1cd, 0x3e6effc6, 0x2975eddb, 0x247ce3d0};
+
+unsigned long table_s8[] = {0xb4469bf0, 0xbf4b92fe, 0xa25c89ec, 0xa95180e2, 0x9872bfc8, 0x937fb6c6, 0x8e68add4, 0x8565a4da, 0xec2ed380, 0xe723da8e, 0xfa34c19c, 0xf139c892, 0xc01af7b8, 0xcb17feb6, 0xd600e5a4, 0xdd0decaa, 0x4960b10, 0xf9b021e, 0x128c190c, 0x19811002, 0x28a22f28, 0x23af2626, 0x3eb83d34, 0x35b5343a, 0x5cfe4360, 0x57f34a6e, 0x4ae4517c, 0x41e95872, 0x70ca6758, 0x7bc76e56, 0x66d07544, 0x6ddd7c4a, 0xcffda02b, 0xc4f0a925, 0xd9e7b237, 0xd2eabb39, 0xe3c98413, 0xe8c48d1d, 0xf5d3960f, 0xfede9f01, 0x9795e85b, 0x9c98e155, 0x818ffa47, 0x8a82f349, 0xbba1cc63, 0xb0acc56d, 0xadbbde7f, 0xa6b6d771, 0x7f2d30cb, 0x742039c5, 0x693722d7, 0x623a2bd9, 0x531914f3, 0x58141dfd, 0x450306ef, 0x4e0e0fe1, 0x274578bb, 0x2c4871b5, 0x315f6aa7, 0x3a5263a9, 0xb715c83, 0x7c558d, 0x1d6b4e9f, 0x16664791, 0x422bed5d, 0x4926e453, 0x5431ff41, 0x5f3cf64f, 0x6e1fc965, 0x6512c06b, 0x7805db79, 0x7308d277, 0x1a43a52d, 0x114eac23, 0xc59b731, 0x754be3f, 0x36778115, 0x3d7a881b, 0x206d9309, 0x2b609a07, 0xf2fb7dbd, 0xf9f674b3, 0xe4e16fa1, 0xefec66af, 0xdecf5985, 0xd5c2508b, 0xc8d54b99, 0xc3d84297, 0xaa9335cd, 0xa19e3cc3, 0xbc8927d1, 0xb7842edf, 0x86a711f5, 0x8daa18fb, 0x90bd03e9, 0x9bb00ae7, 0x3990d686, 0x329ddf88, 0x2f8ac49a, 0x2487cd94, 0x15a4f2be, 0x1ea9fbb0, 0x3bee0a2, 0x8b3e9ac, 0x61f89ef6, 0x6af597f8, 0x77e28cea, 0x7cef85e4, 0x4dccbace, 0x46c1b3c0, 0x5bd6a8d2, 0x50dba1dc, 0x89404666, 0x824d4f68, 0x9f5a547a, 0x94575d74, 0xa574625e, 0xae796b50, 0xb36e7042, 0xb863794c, 0xd1280e16, 0xda250718, 0xc7321c0a, 0xcc3f1504, 0xfd1c2a2e, 0xf6112320, 0xeb063832, 0xe00b313c, 0x439c77b1, 0x48917ebf, 0x558665ad, 0x5e8b6ca3, 0x6fa85389, 0x64a55a87, 0x79b24195, 0x72bf489b, 0x1bf43fc1, 0x10f936cf, 0xdee2ddd, 0x6e324d3, 0x37c01bf9, 0x3ccd12f7, 0x21da09e5, 0x2ad700eb, 0xf34ce751, 0xf841ee5f, 0xe556f54d, 0xee5bfc43, 0xdf78c369, 0xd475ca67, 0xc962d175, 0xc26fd87b, 0xab24af21, 0xa029a62f, 0xbd3ebd3d, 0xb633b433, 0x87108b19, 0x8c1d8217, 0x910a9905, 0x9a07900b, 0x38274c6a, 0x332a4564, 0x2e3d5e76, 0x25305778, 0x14136852, 0x1f1e615c, 0x2097a4e, 0x9047340, 0x604f041a, 0x6b420d14, 0x76551606, 0x7d581f08, 0x4c7b2022, 0x4776292c, 0x5a61323e, 0x516c3b30, 0x88f7dc8a, 0x83fad584, 0x9eedce96, 0x95e0c798, 0xa4c3f8b2, 0xafcef1bc, 0xb2d9eaae, 0xb9d4e3a0, 0xd09f94fa, 0xdb929df4, 0xc68586e6, 0xcd888fe8, 0xfcabb0c2, 0xf7a6b9cc, 0xeab1a2de, 0xe1bcabd0, 0xb5f1011c, 0xbefc0812, 0xa3eb1300, 0xa8e61a0e, 0x99c52524, 0x92c82c2a, 0x8fdf3738, 0x84d23e36, 0xed99496c, 0xe6944062, 0xfb835b70, 0xf08e527e, 0xc1ad6d54, 0xcaa0645a, 0xd7b77f48, 0xdcba7646, 0x52191fc, 0xe2c98f2, 0x133b83e0, 0x18368aee, 0x2915b5c4, 0x2218bcca, 0x3f0fa7d8, 0x3402aed6, 0x5d49d98c, 0x5644d082, 0x4b53cb90, 0x405ec29e, 0x717dfdb4, 0x7a70f4ba, 0x6767efa8, 0x6c6ae6a6, 0xce4a3ac7, 0xc54733c9, 0xd85028db, 0xd35d21d5, 0xe27e1eff, 0xe97317f1, 0xf4640ce3, 0xff6905ed, 0x962272b7, 0x9d2f7bb9, 0x803860ab, 0x8b3569a5, 0xba16568f, 0xb11b5f81, 0xac0c4493, 0xa7014d9d, 0x7e9aaa27, 0x7597a329, 0x6880b83b, 0x638db135, 0x52ae8e1f, 0x59a38711, 0x44b49c03, 0x4fb9950d, 0x26f2e257, 0x2dffeb59, 0x30e8f04b, 0x3be5f945, 0xac6c66f, 0x1cbcf61, 0x1cdcd473, 0x17d1dd7d};
+
+unsigned char table_s1[] = {0x66,0xac,0x60,0xcb,0x1d,0x74,0x99,0xfc,0x54,0x21,0x15,0xef,0x46,0xcc,0x63,0x55,0x5,0x3,0x17,0x80,0x25,0x84,0xf1,0x76,0x6f,0xb3,0xfe,0x14,0x1c,0x9e,0x4f,0xa2,0x1f,0x73,0xd0,0xe7,0xa,0xb6,0x4d,0x30,0xc,0x2,0x7c,0xca,0x9f,0xa0,0xe5,0xb4,0x5f,0xb0,0xbc,0x9d,0xf3,0xf9,0x95,0x6e,0xdd,0xd2,0x77,0xba,0x1e,0xec,0xf,0xc0,0xb1,0x98,0xe4,0x39,0x7f,0x4e,0x28,0x64,0xbd,0x24,0x20,0x8a,0x2c,0x33,0x38,0x34,0xd3,0xeb,0x3d,0x8f,0xe2,0x9b,0xed,0xe0,0xb5,0x16,0x8,0xbf,0x85,0xcd,0x86,0x32,0x3e,0x97,0x7e,0x5a,0x7b,0xea,0xaa,0xbe,0x79,0x70,0xb8,0x83,0xf8,0xb2,0xdc,0x69,0xa4,0x68,0xfd,0x3a,0x48,0x5d,0xcf,0xad,0x57,0xd9,0x4a,0xd5,0x4b,0x88,0x6c,0x8c,0x4,0xf2,0xc4,0xc5,0xa7,0x92,0x3b,0x50,0x53,0xe9,0xfb,0x89,0xf5,0x37,0x6a,0x61,0xc9,0x8e,0x52,0xd1,0x2e,0x7a,0x18,0xf6,0x7,0x4c,0xb9,0x41,0x3f,0x71,0xfa,0x29,0x81,0x1a,0x67,0x90,0xd4,0x51,0xc8,0xa6,0x26,0x96,0xc1,0xdb,0xae,0xb7,0xd7,0x5e,0xff,0x1b,0xf4,0x59,0xe,0xd6,0x62,0x40,0xf0,0xa9,0xd,0x27,0xc3,0xee,0xc6,0x42,0x2b,0x12,0x56,0x3c,0x8b,0xe8,0x31,0x72,0x10,0xd8,0xb,0x58,0x82,0x43,0x5c,0xa3,0x91,0x11,0x44,0x94,0x9,0xa1,0xf7,0x5b,0x6d,0x65,0xdf,0xc7,0x2f,0xce,0x0,0x93,0xde,0xda,0xab,0x36,0x8d,0x9c,0xe3,0x2d,0x6,0x49,0x6b,0x13,0xaf,0x7d,0x75,0x45,0x2a,0x35,0xe1,0x47,0x23,0x19,0xbb,0xa5,0xc2,0x9a,0x1,0xe6,0xa8,0x87,0x78,0x22,0xc7,0xa9,0x30,0xb5,0xf1,0x6,0x7b,0xe0,0x3f,0xb6,0xd6,0xcf,0xba,0xa0,0xf7,0x47,0x21,0x3,0xb7,0x6f,0x38,0x95,0x7a,0x9e,0x23,0xa7,0x8f,0xa2,0x46,0x6c,0xc8,0x91,0x31,0x5a,0xf3,0xc6,0xa4,0xa5,0x93,0x65,0x0,0xb,0x56,0x94,0xe8,0x9a,0x88,0x32,0x97,0x79,0x1b,0x4f,0xb0,0x33,0xef,0xa8,0x48,0x9b,0x10,0x5e,0x20,0xd8,0x2d,0x66,0x4c,0x82,0xfd,0xec,0x57,0xca,0xbb,0xbf,0x24,0x14,0x1c,0xce,0x72,0xa,0x28,0x67,0xc4,0xda,0x78,0x42,0x26,0x80,0x54,0x4b,0x43,0x19,0xe6,0xc9,0x87,0x60,0xfb,0xa3,0x13,0x50,0x89,0xea,0x5d,0x37,0x73,0x4a,0xc2,0x3d,0x22,0xe3,0x39,0x6a,0xb9,0x71,0x3a,0x96,0xc0,0x68,0xf5,0x25,0x70,0xf0,0xf2,0x61,0xaf,0x4e,0xa6,0xbe,0x4,0xc,0x51,0x2c,0xd7,0x6b,0x86,0xb1,0x12,0x7e,0xd5,0x84,0xc1,0xfe,0xab,0x1d,0x63,0x6d,0xf,0xf4,0x98,0x92,0xfc,0xdd,0xd1,0x3e,0xa1,0x6e,0x8d,0x7f,0xdb,0x16,0xb3,0xbc,0x9d,0xf8,0x15,0x7c,0xaa,0x1,0xcd,0x7,0x34,0x2,0xad,0x27,0x8e,0x74,0x40,0x35,0x17,0x90,0xe5,0x44,0xe1,0x76,0x62,0x64,0xc3,0x2e,0xff,0x7d,0x75,0x9f,0xd2,0xe,0xdf,0xcb,0x8b,0x1a,0x3b,0x1f,0xf6,0x5f,0x8,0xbd,0xd3,0x99,0xe2,0xd9,0x11,0x18,0xcc,0xae,0x3c,0x29,0x5b,0x9c,0x9,0xc5,0xed,0xd,0xe9,0x2a,0xb4,0x2b,0xb8,0x36,0x5,0x49,0x2f,0x1e,0x58,0x85,0xf9,0xd0,0x55,0x59,0x52,0x4d,0xeb,0x41,0x45,0xdc,0x81,0x8c,0xfa,0x83,0xee,0x5c,0x8a,0xb2,0x53,0xe7,0xac,0xe4,0xde,0x69,0x77,0xd4,0x17,0xa7,0xf0,0xea,0x9f,0x86,0xe6,0x6f,0xb0,0x2b,0x56,0xa1,0xe5,0x60,0xf9,0x97,0xc1,0x98,0x3c,0x16,0xf2,0xdf,0xf7,0x73,0xce,0x2a,0xc5,0x68,0x3f,0xe7,0x53,0x71,0x62,0xd8,0xca,0xb8,0xc4,0x6,0x5b,0x50,0x35,0xc3,0xf5,0xf4,0x96,0xa3,0xa,0x61,0x36,0x7d,0x88,0x70,0xe,0x40,0xcb,0x18,0xf8,0xbf,0x63,0xe0,0x1f,0x4b,0x29,0xc7,0x37,0x78,0x5a,0x22,0x9e,0x4c,0x44,0x74,0xef,0xeb,0x9a,0x7,0xbc,0xad,0xd2,0x1c,0xf3,0xab,0x30,0xd7,0x99,0xb6,0x49,0x13,0x1b,0x4,0xd0,0x76,0x12,0x28,0x8a,0x94,0x21,0xe9,0x3a,0x69,0xb3,0x72,0x6d,0x92,0x1a,0x23,0x67,0xd,0xba,0xd9,0x0,0x43,0x5c,0x54,0xee,0xf6,0x1e,0xff,0x31,0xa2,0xa0,0x20,0x75,0xa5,0x38,0x90,0xc6,0x6a,0x3d,0x33,0x4d,0xfb,0xae,0x91,0xd4,0x85,0x2e,0x42,0xe1,0xd6,0x3b,0x87,0x7c,0x1,0xec,0xe3,0x46,0x8b,0x2f,0xdd,0x3e,0xf1,0x6e,0x81,0x8d,0xac,0xc2,0xc8,0xa4,0x5f,0x65,0x10,0x24,0xde,0x77,0xfd,0x52,0x64,0x57,0x9d,0x51,0xfa,0x2c,0x45,0xa8,0xcd,0x5e,0x82,0xcf,0x25,0x2d,0xaf,0x7e,0x93,0x34,0x32,0x26,0xb1,0x14,0xb5,0xc0,0x47,0x48,0x41,0x89,0xb2,0xc9,0x83,0xed,0x58,0xf,0xa6,0x4f,0x6b,0x4a,0xdb,0x9b,0x8f,0x66,0xe8,0x7b,0xe4,0x7a,0xb9,0x5d,0xbd,0x95,0x59,0xcc,0xb,0x79,0x6c,0xfe,0x9c,0x8c,0x15,0x11,0xbb,0x1d,0x2,0x9,0x5,0x80,0xa9,0xd5,0x8,0x4e,0x7f,0x19,0x55,0x84,0x27,0x39,0x8e,0xb4,0xfc,0xb7,0x3,0xe2,0xda,0xc,0xbe,0xd3,0xaa,0xdc,0xd1,0x1c,0xf8,0x5e,0xf3,0xd1,0x9,0x47,0x65,0xae,0xf7,0x20,0xa,0xe9,0xc4,0x45,0xc1,0x1d,0x86,0x97,0x60,0x56,0xd3,0xa1,0xcf,0x91,0x21,0xdc,0xc6,0xb0,0xa9,0x59,0xd0,0x89,0xce,0xd6,0x55,0x7d,0x29,0xf1,0x1f,0x4b,0x0,0x46,0xbe,0x76,0x38,0x2e,0xfd,0xf5,0x3,0xc2,0xc3,0x95,0xa0,0x57,0x3c,0xee,0x54,0x8e,0xfc,0x30,0xf2,0x66,0x6d,0x32,0x2d,0x40,0xe6,0x1e,0x24,0xa2,0xbc,0x9d,0xc5,0xe1,0x6,0x80,0xaf,0x25,0x7f,0xdd,0xd9,0x31,0xac,0x9b,0x8a,0x2a,0xe4,0x4e,0x1,0x14,0x6c,0x7a,0xa8,0x42,0x72,0x16,0x96,0x93,0x43,0xa6,0xe,0x5c,0xf0,0x62,0x6a,0xc0,0xd8,0xc9,0x28,0x94,0x7,0x15,0x2c,0x3b,0x51,0xef,0x8c,0x75,0x36,0xdf,0x17,0x5f,0xc,0x44,0x85,0xa4,0x5b,0xb7,0x58,0x9a,0xbb,0xfe,0xf4,0x69,0x92,0xd5,0xda,0xbd,0x70,0xeb,0x19,0xc7,0x8,0x74,0x18,0xe0,0xd7,0xb1,0xd,0x37,0x4a,0x5,0xb,0xcd,0x7b,0xa7,0x98,0xb3,0xe2,0x4,0x2,0x87,0x10,0x83,0x22,0x71,0xf6,0xb4,0x68,0x13,0xf9,0x99,0x1b,0xa5,0x48,0xab,0x61,0xcc,0x67,0x73,0x1a,0xfb,0x9e,0x26,0x53,0xe8,0x12,0xcb,0x41,0x52,0x64,0x6f,0xa3,0x3d,0xfa,0x5a,0x4f,0xaa,0xc8,0xde,0x50,0xd2,0x4d,0x8f,0x4c,0x8b,0x6b,0x90,0x39,0x5d,0x79,0xed,0x7c,0xb9,0xad,0x77,0x7e,0x84,0xbf,0xb5,0xff,0x6e,0xdb,0xec,0xd4,0x88,0x3a,0x9c,0xe5,0xe7,0xea,0x11,0xb2,0xb8,0xf,0xca,0x82,0x35,0x81,0x9f,0xb6,0x3e,0xe3,0x49,0x78,0x63,0x2f,0x23,0xba,0x8d,0x27,0x34,0x2b,0x33,0x3f,0x3c,0x23,0xf7,0x51,0x35,0xf,0xad,0xb3,0xd4,0x8c,0x17,0xf0,0xbe,0x91,0x6e,0x34,0xc8,0xcc,0xbd,0x20,0x9b,0x8a,0xf5,0x3b,0x10,0x5f,0x7d,0x5,0xb9,0x6b,0x63,0x53,0x87,0x7,0x52,0x82,0x1f,0xb7,0xe1,0x4d,0x7b,0x73,0xc9,0xd1,0x39,0xd8,0x16,0x85,0x3d,0x4,0x40,0x2a,0x9d,0xfe,0x27,0x64,0x6,0xce,0x1d,0x4e,0x94,0x55,0x4a,0xb5,0xe9,0xd,0xe2,0x4f,0x18,0xc0,0x74,0x56,0xe6,0xbf,0x1b,0x31,0xd5,0xf8,0xd0,0x54,0x97,0xc,0x71,0x86,0xc2,0x47,0xde,0xb0,0x30,0x80,0xd7,0xcd,0xb8,0xa1,0xc1,0x48,0xdf,0x98,0x44,0xc7,0x38,0x6c,0xe,0xe0,0x11,0x5a,0xaf,0x57,0x29,0x67,0xec,0x3f,0x12,0xe4,0xd2,0xd3,0xb1,0x84,0x2d,0x46,0x45,0xff,0xed,0x9f,0xe3,0x21,0x7c,0x77,0xb2,0x7e,0xeb,0x2c,0x5e,0x4b,0xd9,0xbb,0x41,0xcf,0x5c,0xc3,0x5d,0x9e,0x7a,0x9a,0x28,0x81,0x68,0x4c,0x6d,0xfc,0xbc,0xa8,0x6f,0x66,0xae,0x95,0xee,0xa4,0xca,0x7f,0xc5,0xfd,0x2b,0x99,0xf4,0x8d,0xfb,0xf6,0xa3,0x0,0x1e,0xa9,0x93,0xdb,0x90,0x24,0xa7,0x8e,0xf2,0x2f,0x69,0x58,0x3e,0x72,0xab,0x32,0x36,0x9c,0x3a,0x25,0x2e,0x22,0x49,0xa6,0xaa,0x8b,0xe5,0xef,0x83,0x78,0xcb,0xc4,0x61,0xac,0x8,0xfa,0x19,0xd6,0x9,0x65,0xc6,0xf1,0x1c,0xa0,0x5b,0x26,0x1a,0x14,0x6a,0xdc,0x89,0xb6,0xf3,0xa2,0x13,0x15,0x1,0x96,0x33,0x92,0xe7,0x60,0x79,0xa5,0xe8,0x2,0xa,0x88,0x59,0xb4,0x70,0xba,0x76,0xdd,0xb,0x62,0x8f,0xea,0x42,0x37,0x3,0xf9,0x50,0xda,0x75,0x43,0x37,0xac,0xbd,0x4a,0x7c,0xf9,0x8b,0xe5,0xbb,0xb,0xf6,0xec,0x9a,0x83,0x73,0xfa,0x36,0xd2,0x74,0xd9,0xfb,0x23,0x6d,0x4f,0x84,0xdd,0xa,0x20,0xc3,0xee,0x6f,0xeb,0xdf,0x29,0xe8,0xe9,0xbf,0x8a,0x7d,0x16,0xc4,0x7e,0xa4,0xd6,0x1a,0xd8,0x4c,0x47,0xa3,0xe4,0xfc,0x7f,0x57,0x3,0xdb,0x35,0x61,0x2a,0x6c,0x94,0x5c,0x12,0x4,0xd7,0xf7,0xf3,0x1b,0x86,0xb1,0xa0,0x0,0xce,0x64,0x2b,0x3e,0x46,0x50,0x82,0x68,0x58,0x18,0x7,0x6a,0xcc,0x34,0xe,0x88,0x96,0xb7,0xef,0xcb,0x2c,0xaa,0x85,0xf,0x55,0x3f,0x6,0x11,0x7b,0xc5,0xa6,0x5f,0x1c,0xf5,0x3d,0x75,0x26,0x6e,0xaf,0x8e,0x71,0x3c,0xbc,0xb9,0x69,0x8c,0x24,0x76,0xda,0x48,0x40,0xea,0xf2,0xe3,0x2,0xbe,0x2d,0x5e,0x32,0xca,0xfd,0x9b,0x27,0x1d,0x60,0x2f,0x21,0xe7,0x51,0x8d,0xb2,0x99,0xc8,0x9d,0x72,0xb0,0x91,0xd4,0xde,0x43,0xb8,0xff,0xf0,0x97,0x5a,0xc1,0x33,0xed,0x22,0x81,0x4b,0xe6,0x4d,0x59,0x30,0xd1,0xb4,0xc,0x79,0xc2,0x38,0xe1,0x6b,0x78,0x4e,0x2e,0x28,0xad,0x3a,0xa9,0x8,0x5b,0xdc,0x9e,0x42,0x39,0xd3,0xb3,0x31,0x8f,0x62,0xba,0x13,0x77,0x53,0xc7,0x56,0x93,0x87,0x5d,0x54,0xae,0x95,0x9f,0xd5,0x44,0xf1,0x45,0x89,0x17,0xd0,0x70,0x65,0x80,0xe2,0xf4,0x7a,0xf8,0x67,0xa5,0x66,0xa1,0x41,0xb5,0x9c,0x14,0xc9,0x63,0x52,0x49,0x5,0x9,0x90,0xa7,0xd,0x1e,0x1,0x19,0x15,0xc6,0xfe,0xa2,0x10,0xb6,0xcf,0xcd,0xc0,0x3b,0x98,0x92,0x25,0xe0,0xa8,0x1f,0xab,0xb3,0x52,0xee,0x7d,0x18,0x10,0xba,0xa2,0xdc,0x74,0x26,0x8a,0x6c,0xec,0xe9,0x39,0x3e,0xff,0xde,0x21,0xa5,0x6d,0x25,0x76,0x95,0xf6,0xf,0x4c,0x6f,0x56,0x41,0x2b,0xfa,0xd5,0x5f,0x5,0xe7,0xbf,0x9b,0x7c,0x64,0x5e,0xd8,0xc6,0x48,0x57,0x3a,0x9c,0x0,0xd2,0x38,0x8,0x34,0x7b,0x6e,0x16,0xe1,0xf0,0x50,0x9e,0xa7,0xa3,0x4b,0xd6,0xc,0x42,0x54,0x87,0x31,0x7a,0x3c,0xc4,0x7,0x53,0x8b,0x65,0xf3,0xb4,0xac,0x2f,0x4a,0x88,0x1c,0x17,0x94,0x2e,0xf4,0x86,0xef,0xda,0x2d,0x46,0x8f,0x79,0xb8,0xb9,0x93,0xbe,0x3f,0xbb,0xd4,0x8d,0x5a,0x70,0xab,0x73,0x3d,0x1f,0x66,0x82,0x24,0x89,0xca,0xd3,0x23,0xaa,0xeb,0x5b,0xa6,0xbc,0x2c,0xa9,0xdb,0xb5,0x67,0xfc,0xed,0x1a,0xb0,0xf8,0x4f,0xfb,0x6b,0xc8,0xc2,0x75,0xe6,0x9f,0x9d,0x90,0x96,0xae,0xf2,0x40,0x4e,0x51,0x49,0x45,0x59,0xc0,0xf7,0x5d,0x33,0x2,0x19,0x55,0xe5,0xcc,0x44,0x99,0xf5,0x36,0xf1,0x11,0xa4,0x2a,0xa8,0x37,0x20,0x35,0xd0,0xb2,0x15,0xd9,0x47,0x80,0xcf,0x85,0x14,0xa1,0xd,0x4,0xfe,0xc5,0x97,0x6,0xc3,0xd7,0xea,0x43,0x27,0x3,0xe3,0x61,0xdf,0x32,0xce,0x12,0x69,0x83,0xf9,0x58,0xb,0x8c,0x7e,0x78,0xfd,0x6a,0xb1,0x3b,0x28,0x1e,0x5c,0x29,0x92,0x68,0x9,0x60,0x81,0xe4,0xd1,0x1b,0xb6,0x1d,0x91,0x63,0xbd,0x72,0xaf,0xa0,0xc7,0xa,0x84,0x8e,0x13,0xe8,0xcd,0x22,0xe0,0xc1,0xdd,0xe2,0xc9,0x98,0x7f,0x71,0xb7,0x1,0xcb,0x77,0x4d,0x30,0xe,0x62,0x9a,0xad,0x9a,0x2a,0xd7,0xcd,0xbb,0xa2,0x52,0xdb,0x16,0x8d,0x9c,0x6b,0x5d,0xd8,0xaa,0xc4,0xa5,0xfc,0x2b,0x1,0xe2,0xcf,0x4e,0xca,0x17,0xf3,0x55,0xf8,0xda,0x2,0x4c,0x6e,0xe5,0x5f,0x85,0xf7,0x3b,0xf9,0x6d,0x66,0xfe,0x8,0xc9,0xc8,0x9e,0xab,0x5c,0x37,0x40,0xb,0x4d,0xb5,0x7d,0x33,0x25,0xf6,0x82,0xc5,0xdd,0x5e,0x76,0x22,0xfa,0x14,0x45,0xa,0x1f,0x67,0x71,0xa3,0x49,0x79,0xd6,0xd2,0x3a,0xa7,0x90,0x81,0x21,0xef,0x96,0xce,0xea,0xd,0x8b,0xa4,0x2e,0x74,0x39,0x26,0x4b,0xed,0x15,0x2f,0xa9,0xb7,0xd4,0x1c,0x54,0x7,0x4f,0x8e,0xaf,0x50,0x1e,0x27,0x30,0x5a,0xe4,0x87,0x7e,0x3d,0x69,0x61,0xcb,0xd3,0xc2,0x23,0x9f,0xc,0x1d,0x9d,0x98,0x48,0xad,0x5,0x57,0xfb,0xe,0x0,0xc6,0x70,0xac,0x93,0xb8,0xe9,0x7f,0x13,0xeb,0xdc,0xba,0x6,0x3c,0x41,0xde,0xd1,0xb6,0x7b,0xe0,0x12,0xcc,0x3,0xbc,0x53,0x91,0xb0,0xf5,0xff,0x62,0x99,0x2d,0x58,0xe3,0x19,0xc0,0x4a,0x59,0x6f,0xa0,0x6a,0xc7,0x6c,0x78,0x11,0xf0,0x95,0xbf,0x63,0x18,0xf2,0x92,0x10,0xae,0x43,0xf,0x9,0x8c,0x1b,0x88,0x29,0x7a,0xfd,0x7c,0x75,0x8f,0xb4,0xbe,0xf4,0x65,0xd0,0x9b,0x32,0x56,0x72,0xe6,0x77,0xb2,0xa6,0xd5,0x5b,0xd9,0x46,0x84,0x47,0x80,0x60,0x64,0xa8,0x36,0xf1,0x51,0x44,0xa1,0xc3,0x28,0xb1,0x86,0x2c,0x3f,0x20,0x38,0x34,0x94,0xbd,0x35,0xe8,0x42,0x73,0x68,0x24,0x1a,0xb9,0xb3,0x4,0xc1,0x89,0x3e,0x8a,0xe7,0xdf,0x83,0x31,0x97,0xee,0xec,0xe1,0x44,0x2e,0x39,0x0,0x23,0x60,0x99,0xfa,0x19,0x4a,0x2,0xca,0x4e,0xb1,0x90,0x51,0x56,0x86,0x83,0x3,0xe5,0x49,0x1b,0xb3,0xcd,0xd5,0x7f,0x77,0x12,0x81,0x3d,0xdc,0xb9,0x24,0xcc,0xc8,0xf1,0x3f,0x9f,0x8e,0x79,0x1,0x14,0x5b,0x67,0x57,0xbd,0x6f,0xf3,0x55,0x38,0x27,0xa9,0xb7,0x31,0xb,0x13,0xf4,0xd0,0x88,0x6a,0x30,0xba,0x95,0xd6,0xd7,0x16,0xe0,0x29,0x42,0xb5,0x80,0xe9,0x9b,0x41,0xfb,0x78,0x73,0xe7,0x25,0x40,0xc3,0xdb,0x9c,0xa,0xe4,0x3c,0x68,0xab,0x53,0x15,0x5e,0xe8,0x3b,0x2d,0x63,0x75,0x82,0x93,0x8,0xda,0xb4,0xc6,0x43,0xd3,0xc9,0x34,0x84,0xc5,0x4c,0xbc,0xa5,0xe6,0x4b,0xed,0x9,0x70,0x52,0x1c,0xc4,0x1f,0x35,0xe2,0xbb,0xd4,0x50,0xd1,0xfc,0xf6,0x2b,0xa3,0x8a,0x3a,0x76,0x6d,0x5c,0x32,0x98,0xaf,0x36,0x2a,0x26,0x3e,0x21,0x2f,0x9d,0xc1,0xf9,0xff,0xf2,0xf0,0x89,0x1a,0xad,0xa7,0x4,0x94,0x20,0x97,0xdf,0x6c,0x48,0x2c,0x85,0xb8,0xac,0x69,0xf8,0xaa,0x91,0x6b,0x62,0xce,0x7b,0xea,0xa0,0xef,0x28,0xb6,0x7a,0xdd,0xbf,0x5a,0x4f,0x58,0xc7,0x45,0xcb,0x7e,0x9e,0x59,0x9a,0x72,0xd9,0x74,0xbe,0x8b,0xee,0xf,0x66,0x7,0xfd,0x46,0x33,0x71,0x47,0x54,0xde,0x5,0x92,0x17,0x11,0xe3,0x64,0x37,0x96,0xec,0x6,0x7d,0xa1,0x5d,0xb0,0xe,0x8c,0xc2,0xf5,0xd,0x61,0x5f,0x22,0x18,0xa4,0x6e,0xd8,0x1e,0x10,0xf7,0xa6,0x8d,0xb2,0xae,0x8f,0x4d,0xa2,0x87,0x7c,0xe1,0xeb,0x65,0xa8,0xcf,0xc0,0x1d,0xd2,0xc,0xfe,0x96,0x4a,0x31,0xdb,0xbb,0x39,0x87,0x6a,0x26,0x20,0xa5,0x32,0xa1,0x0,0x53,0xd4,0x4,0x71,0xca,0x30,0xe9,0x63,0x70,0x46,0x89,0x43,0xee,0x45,0x51,0x38,0xd9,0xbc,0xf7,0xf8,0x9f,0x52,0xc9,0x3b,0xe5,0x2a,0x95,0x7a,0xb8,0x99,0xdc,0xd6,0x4b,0xb0,0x27,0x29,0xef,0x59,0x85,0xba,0x91,0xc0,0x56,0x3a,0xc2,0xf5,0x93,0x2f,0x15,0x68,0x33,0x90,0x9a,0x2d,0xe8,0xa0,0x17,0xa3,0xce,0xf6,0xaa,0x18,0xbe,0xc7,0xc5,0xc8,0x1,0x98,0xaf,0x5,0x16,0x9,0x11,0x1d,0xbd,0x94,0x1c,0xc1,0x6b,0x5a,0x41,0xd,0xfc,0x72,0xf0,0x6f,0xad,0x6e,0xa9,0x49,0x4d,0x81,0x1f,0xd8,0x78,0x6d,0x88,0xea,0x55,0x5c,0xa6,0x9d,0x97,0xdd,0x4c,0xf9,0xb2,0x1b,0x7f,0x5b,0xcf,0x5e,0x9b,0x8f,0x69,0x22,0x64,0x9c,0x54,0x1a,0xc,0xdf,0xab,0xec,0xf4,0x77,0x5f,0xb,0xd3,0x3d,0xcc,0x76,0xac,0xde,0x12,0xd0,0x44,0x4f,0xd7,0x21,0xe0,0xe1,0xb7,0x82,0x75,0x1e,0x8c,0xd5,0x2,0x28,0xcb,0xe6,0x67,0xe3,0x3e,0xda,0x7c,0xd1,0xf3,0x2b,0x65,0x47,0xb3,0x3,0xfe,0xe4,0x92,0x8b,0x7b,0xf2,0x3f,0xa4,0xb5,0x42,0x74,0xf1,0x83,0xed,0x40,0x48,0xe2,0xfa,0xeb,0xa,0xb6,0x25,0x34,0xb4,0xb1,0x61,0x84,0x2c,0x7e,0xd2,0xfd,0x35,0x7d,0x2e,0x66,0xa7,0x86,0x79,0x37,0xe,0x19,0x73,0xcd,0xae,0x57,0x14,0xbf,0xe7,0xc3,0x24,0xa2,0x8d,0x7,0x5d,0x10,0xf,0x62,0xc4,0x3c,0x6,0x80,0x9e,0x6c,0x23,0x36,0x4e,0x58,0x8a,0x60,0x50,0xff,0xfb,0x13,0x8e,0xb9,0xa8,0x8,0xc6,0xa9,0x80,0xfc,0x21,0x67,0x56,0x30,0x7c,0xa5,0x3c,0x38,0x92,0x34,0x2b,0x20,0x2c,0xcb,0xf3,0x25,0x97,0xfa,0x83,0xf5,0xf8,0xad,0xe,0x10,0xa7,0x9d,0xd5,0x9e,0x2a,0x26,0x8f,0x66,0x42,0x63,0xf2,0xb2,0xa6,0x61,0x68,0xa0,0x9b,0xe0,0xaa,0xc4,0x71,0xbc,0x70,0xe5,0x22,0x50,0x45,0xd7,0xb5,0x4f,0xc1,0x52,0xcd,0x53,0x90,0x74,0x94,0x7e,0xb4,0x78,0xd3,0x5,0x6c,0x81,0xe4,0x4c,0x39,0xd,0xf7,0x5e,0xd4,0x7b,0x4d,0x1d,0x1b,0xf,0x98,0x3d,0x9c,0xe9,0x6e,0x77,0xab,0xe6,0xc,0x4,0x86,0x57,0xba,0x7,0x6b,0xc8,0xff,0x12,0xae,0x55,0x28,0x14,0x1a,0x64,0xd2,0x87,0xb8,0xfd,0xac,0x47,0xa8,0xa4,0x85,0xeb,0xe1,0x8d,0x76,0xc5,0xca,0x6f,0xa2,0x6,0xf4,0x17,0xd8,0x33,0xa,0x4e,0x24,0x93,0xf0,0x29,0x6a,0x8,0xc0,0x13,0x40,0x9a,0x5b,0x44,0xbb,0x89,0x9,0x5c,0x8c,0x11,0xb9,0xef,0x43,0x75,0x7d,0xc7,0xdf,0x37,0xd6,0x18,0x8b,0xc6,0xc2,0xb3,0x2e,0x95,0x84,0xfb,0x35,0x1e,0x51,0x73,0xb,0xb7,0x65,0x6d,0x5d,0x32,0x2d,0xf9,0x5f,0x3b,0x1,0xa3,0xbd,0xda,0x82,0x19,0xfe,0xb0,0x9f,0x60,0x3a,0x1c,0xea,0xdc,0xdd,0xbf,0x8a,0x23,0x48,0x4b,0xf1,0xe3,0x91,0xed,0x2f,0x72,0x79,0xd1,0x96,0x4a,0xc9,0x36,0x62,0x0,0xee,0x1f,0x54,0xa1,0x59,0x27,0x69,0xe2,0x31,0x99,0x2,0x7f,0x88,0xcc,0x49,0xd0,0xbe,0x3e,0x8e,0xd9,0xc3,0xb6,0xaf,0xcf,0x46,0xe7,0x3,0xec,0x41,0x16,0xce,0x7a,0x58,0xe8,0xb1,0x15,0x3f,0xdb,0xf6,0xde,0x5a,0x7d,0x31,0x2a,0x1b,0xb1,0x6c,0xe4,0xcd,0x6d,0x61,0x79,0x66,0x75,0xdf,0xe8,0x71,0xb8,0xb5,0xb7,0xce,0x68,0xda,0x86,0xbe,0xd3,0x67,0xd0,0x98,0x5d,0xea,0xe0,0x43,0xff,0xeb,0x2e,0xbf,0x2b,0xf,0x6b,0xc2,0x89,0x3c,0xad,0xe7,0xed,0xd6,0x2c,0x25,0x9a,0xf8,0x1d,0x8,0xa8,0x6f,0xf1,0x3d,0x39,0xd9,0x1e,0xdd,0x1f,0x80,0x2,0x8c,0xcc,0xa9,0x48,0x21,0x35,0x9e,0x33,0xf9,0x36,0x0,0x13,0x99,0x40,0xba,0x1,0x74,0xa4,0x23,0x70,0xd1,0x42,0xd5,0x50,0x56,0x1a,0xf7,0x49,0xcb,0xab,0x41,0x3a,0xe6,0x18,0x65,0x5f,0xe3,0x85,0xb2,0x4a,0x26,0xb0,0xe1,0xca,0xf5,0x29,0x9f,0x59,0x57,0xc0,0x3b,0xa6,0xac,0xe9,0xc8,0xa,0xe5,0x5a,0x95,0x4b,0xb9,0x22,0xef,0x88,0x87,0x64,0x27,0xde,0xbd,0x3,0x69,0x7e,0x47,0x9,0xf6,0xd7,0x16,0x5e,0xd,0x45,0x8d,0xa2,0xe,0x5c,0xf4,0x11,0xc1,0xc4,0x44,0x55,0xc6,0x7a,0x9b,0x8a,0x92,0x38,0x30,0xb6,0x78,0xd8,0xc9,0xfe,0x63,0x8b,0x8f,0x20,0x10,0xfa,0x28,0x3e,0x46,0x53,0x1c,0xee,0xf0,0x76,0x4c,0xb4,0x12,0x7f,0x60,0x2d,0x77,0xfd,0xd2,0x54,0xb3,0x97,0xcf,0x6e,0x5,0xf2,0xc7,0x91,0x90,0x51,0xa7,0x3f,0x34,0xa0,0x62,0xae,0xdc,0x6,0xbc,0x4d,0xa3,0x7b,0x2f,0x7,0x84,0x9c,0xdb,0xaf,0x7c,0x6a,0x24,0xec,0x14,0x52,0x19,0x9d,0xf3,0x81,0x4,0x32,0xc5,0xd4,0x4f,0x82,0xb,0xfb,0xe2,0x94,0x8e,0x73,0xc3,0x37,0x15,0x5b,0x83,0xa1,0xc,0xaa,0x4e,0x93,0x17,0x96,0xbb,0x58,0x72,0xa5,0xfc,0x5b,0x97,0x9,0xce,0x6e,0x7b,0x9e,0xfc,0xea,0x64,0xe6,0x79,0xbb,0x78,0xbf,0x5f,0xa4,0xd,0x69,0x4d,0xd9,0x48,0x8d,0x99,0x43,0x4a,0xb0,0x8b,0x81,0xcb,0x5a,0xef,0xd8,0xe0,0xbc,0xe,0xa8,0xd1,0xd3,0xde,0x25,0x86,0x8c,0x3b,0xfe,0xb6,0x1,0xb5,0xab,0x82,0xa,0xd7,0x7d,0x4c,0x57,0x1b,0x17,0x8e,0xb9,0x13,0x0,0x1f,0x7,0xb,0x83,0x6c,0xae,0x8f,0xca,0xc0,0x5d,0xa6,0xe1,0xee,0x89,0x44,0xdf,0x2d,0xf3,0x3c,0x40,0x2c,0xd4,0xe3,0x85,0x39,0x3,0x7e,0x31,0x3f,0xf9,0x4f,0x93,0xac,0x87,0xd6,0x30,0x36,0xb3,0x24,0xb7,0x16,0x45,0xc2,0x80,0x5c,0x27,0xcd,0xad,0x2f,0x91,0x7c,0x9f,0x55,0xf8,0x53,0x47,0x2e,0xcf,0xaa,0x12,0x67,0xdc,0x26,0xff,0x75,0x66,0x50,0x6,0x19,0x74,0xd2,0x2a,0x10,0x96,0x88,0xa9,0xf1,0xd5,0x32,0xb4,0x9b,0x11,0x4b,0xe9,0xed,0x5,0x98,0xaf,0xbe,0x1e,0xd0,0x7a,0x35,0x20,0x58,0x4e,0x9c,0x76,0x46,0x22,0xa2,0xa7,0x77,0x92,0x3a,0x68,0xc4,0x56,0x5e,0xf4,0xec,0xfd,0x1c,0xa0,0x33,0x21,0x18,0xf,0x65,0xdb,0xb8,0x41,0x2,0xeb,0x23,0x6b,0x38,0x70,0xb1,0x90,0x6f,0x28,0xcc,0x6a,0xc7,0xe5,0x3d,0x73,0x51,0x9a,0xc3,0x14,0x3e,0xdd,0xf0,0x71,0xf5,0x29,0xb2,0xa3,0x54,0x62,0xe7,0x95,0xfb,0xa5,0x15,0xe8,0xf2,0x84,0x9d,0x6d,0xe4,0xbd,0xfa,0xe2,0x61,0x49,0x1d,0xc5,0x2b,0x7f,0x34,0x72,0x8a,0x42,0xc,0x1a,0xc9,0xc1,0x37,0xf6,0xf7,0xa1,0x94,0x63,0x8,0xda,0x60,0xba,0xc8,0x4,0xc6,0x52,0x59,0x42,0x2e,0x8d,0xba,0x57,0xeb,0x10,0x6d,0x51,0x5f,0x21,0x97,0xc2,0xfd,0xb8,0xe9,0x2,0xed,0xe1,0xc0,0xae,0xa4,0xc8,0x33,0x80,0x8f,0x2a,0xe7,0x43,0xb1,0x52,0x9d,0x3b,0xf1,0x3d,0x96,0x40,0x29,0xc4,0xa1,0x9,0x7c,0x48,0xb2,0x1b,0x91,0x3e,0x8,0x58,0x5e,0x4a,0xdd,0x78,0xd9,0xac,0x2b,0x32,0xee,0xa3,0x49,0x41,0xc3,0x12,0xff,0x63,0xca,0x23,0x7,0x26,0xb7,0xf7,0xe3,0x24,0x2d,0xe5,0xde,0xa5,0xef,0x81,0x34,0xf9,0x35,0xa0,0x67,0x15,0x0,0x92,0xf0,0xa,0x84,0x17,0x88,0x16,0xd5,0x31,0xd1,0xec,0xc5,0xb9,0x64,0x22,0x13,0x75,0x39,0xe0,0x79,0x7d,0xd7,0x71,0x6e,0x65,0x69,0x8e,0xb6,0x60,0xd2,0xbf,0xc6,0xb0,0xbd,0xe8,0x4b,0x55,0xe2,0xd8,0x90,0xdb,0x6f,0xdc,0x47,0x3a,0xcd,0x89,0xc,0x95,0xfb,0x7b,0xcb,0x9c,0x86,0xf3,0xea,0x8a,0x3,0xa2,0x46,0xa9,0x4,0x53,0x8b,0x3f,0x1d,0xad,0xf4,0x50,0x7a,0x9e,0xb3,0x9b,0x1f,0x59,0xaf,0x99,0x98,0xfa,0xcf,0x66,0xd,0xe,0xb4,0xa6,0xd4,0xa8,0x6a,0x37,0x3c,0x94,0xd3,0xf,0x8c,0x73,0x27,0x45,0xab,0x5a,0x11,0xe4,0x1c,0x62,0x2c,0xa7,0x74,0x83,0x87,0xf6,0x6b,0xd0,0xc1,0xbe,0x70,0x5b,0x14,0x36,0x4e,0xf2,0x20,0x28,0x18,0x77,0x68,0xbc,0x1a,0x7e,0x44,0xe6,0xf8,0x9f,0xc7,0x5c,0xbb,0xf5,0xda,0x25,0x7f,0x76,0x4f,0xb,0x61,0xd6,0xb5,0x6c,0x2f,0x4d,0x85,0x56,0x5,0xdf,0x1e,0x1,0xfe,0xcc,0x4c,0x19,0xc9,0x54,0xfc,0xaa,0x6,0x30,0x38,0x82,0x9a,0x72,0x93,0x5d,0xce,0x43,0x72,0x14,0x58,0x8d,0xa4,0xd8,0x5,0x10,0xf,0x4,0x8,0x81,0x18,0x1c,0xb6,0xde,0xa7,0xd1,0xdc,0xef,0xd7,0x1,0xb3,0xb9,0xf1,0xba,0xe,0x89,0x2a,0x34,0x83,0x47,0xd6,0x96,0x82,0x2,0xab,0x42,0x66,0xc4,0x8e,0xe0,0x55,0x45,0x4c,0x84,0xbf,0x74,0x61,0xf3,0x91,0x98,0x54,0xc1,0x6,0x77,0xb4,0x50,0xb0,0x6b,0xe5,0x76,0xe9,0x21,0x48,0xa5,0xc0,0x5a,0x90,0x5c,0xf7,0x7a,0xf0,0x5f,0x69,0x68,0x1d,0x29,0xd3,0x19,0xb8,0xcd,0x4a,0x39,0x3f,0x2b,0xbc,0x20,0xa2,0x73,0x9e,0x53,0x8f,0xc2,0x28,0x36,0x8a,0x71,0xc,0x23,0x4f,0xec,0xdb,0xa3,0x9c,0xd9,0x88,0x30,0x3e,0x40,0xf6,0xcf,0xc5,0xa9,0x52,0x63,0x8c,0x80,0xa1,0x22,0xd0,0x33,0xfc,0xe1,0xee,0x4b,0x86,0xb7,0xd4,0xd,0x4e,0x17,0x2e,0x6a,0x0,0xbe,0x7f,0x60,0x9f,0x2c,0xe4,0x37,0x64,0x35,0x9d,0xcb,0x67,0xad,0x2d,0x78,0xa8,0x13,0xf2,0x3c,0xaf,0x51,0x59,0xe3,0xfb,0xb1,0xa0,0xdf,0x11,0xe2,0xe6,0x97,0xa,0x93,0x41,0x49,0x79,0x3a,0x75,0x57,0x2f,0x1f,0x25,0x87,0x99,0x16,0x9,0xdd,0x7b,0x94,0xbb,0x44,0x1e,0xfe,0xa6,0x3d,0xda,0x9b,0xae,0x7,0x6c,0x38,0xce,0xf8,0xf9,0xc9,0xb,0x56,0x5d,0x6f,0xd5,0xc7,0xb5,0x12,0x46,0x24,0xca,0xf5,0xb2,0x6e,0xed,0x3,0x4d,0xc6,0x15,0x3b,0x70,0x85,0x7d,0xe8,0x6d,0xf4,0x9a,0xbd,0x26,0x5b,0xac,0x92,0x8b,0xeb,0x62,0x1a,0xaa,0xfd,0xe7,0x32,0xea,0x5e,0x7c,0xc3,0x27,0xc8,0x65,0xff,0xd2,0xfa,0x7e,0xcc,0x95,0x31,0x1b,0x88,0x17,0x84,0xa,0xd1,0x31,0xd5,0x16,0x67,0xa0,0x35,0xf9,0xf0,0x92,0x0,0x15,0xde,0xe5,0x2d,0x24,0x34,0x81,0xef,0xa5,0x7,0x23,0xca,0x63,0xe3,0xf7,0xb7,0x26,0xe2,0x55,0x4b,0xe8,0x6f,0xdb,0x90,0xd8,0xd2,0x60,0xb6,0x8e,0xbd,0xb0,0xc6,0xbf,0xd7,0x7d,0x79,0xe0,0x69,0x65,0x6e,0x71,0x64,0xb9,0xc5,0xec,0x39,0x75,0x13,0x22,0xe7,0x2a,0x8f,0x80,0x9d,0x52,0xb1,0x43,0xc0,0xe1,0xed,0x2,0x33,0xc8,0xa4,0xae,0x97,0x21,0x5f,0x51,0xe9,0xb8,0xfd,0xc2,0xba,0x8d,0x2e,0x42,0x6d,0x10,0xeb,0x57,0x49,0xa3,0xee,0x32,0xff,0x12,0xc3,0x41,0xdd,0x4a,0x5e,0x58,0x2b,0xac,0xd9,0x78,0xb2,0x48,0x7c,0x9,0x8,0x3e,0x91,0x1b,0x96,0x3d,0xf1,0x3b,0xa1,0xc4,0x29,0x40,0xbb,0x5c,0xc7,0x9f,0x7f,0x25,0xda,0xf5,0x1a,0xbc,0x68,0x77,0xf8,0xe6,0x44,0x7e,0x4e,0x36,0x14,0x5b,0x18,0x28,0x20,0xf2,0x6b,0xf6,0x87,0x83,0x70,0xbe,0xc1,0xd0,0x9a,0x82,0x38,0x30,0xce,0x5d,0x93,0x72,0xc9,0x19,0x4c,0xcc,0x6,0xaa,0xfc,0x54,0x5,0x56,0x85,0x4d,0xfe,0x1,0x1e,0xdf,0x61,0xb,0x4f,0x76,0x2f,0x6c,0xb5,0xd6,0x7a,0x50,0xf4,0xad,0x1f,0x9b,0xb3,0x9e,0x4,0xa9,0x46,0xa2,0x1d,0x3f,0x8b,0x53,0x86,0x9c,0xcb,0x7b,0x3,0x8a,0xea,0xf3,0xcd,0x3a,0x47,0xdc,0xfb,0x95,0xc,0x89,0x1c,0xe4,0x11,0x5a,0x74,0xa7,0x2c,0x62,0x8c,0xf,0xd3,0x94,0xab,0x45,0x27,0x73,0xd4,0xa6,0xb4,0xe,0x3c,0x37,0x6a,0xa8,0x98,0x99,0xaf,0x59,0xd,0x66,0xcf,0xfa,0xce,0x57,0x53,0xf9,0x5f,0x40,0x4b,0x47,0xc2,0xeb,0x97,0x4a,0xc,0x3d,0x5b,0x17,0xc6,0x65,0x7b,0xcc,0xf6,0xbe,0xf5,0x41,0xa0,0x98,0x4e,0xfc,0x91,0xe8,0x9e,0x93,0xa,0x3,0xcb,0xf0,0x8b,0xc1,0xaf,0x1a,0x4d,0xe4,0xd,0x29,0x8,0x99,0xd9,0xcd,0x24,0xaa,0x39,0xa6,0x38,0xfb,0x1f,0xff,0xd7,0x1b,0x8e,0x49,0x3b,0x2e,0xbc,0xde,0x27,0x52,0x66,0x9c,0x35,0xbf,0x10,0x26,0x15,0xdf,0x13,0xb8,0x6e,0x7,0xea,0x8f,0x1c,0xc0,0x8d,0x67,0x6f,0xed,0x3c,0xd1,0x76,0x70,0x64,0xf3,0x56,0xf7,0x82,0x5,0x7f,0x71,0xf,0xb9,0xec,0xd3,0x96,0xc7,0x6c,0x0,0xa3,0x94,0x79,0xc5,0x3e,0x43,0xae,0xa1,0x4,0xc9,0x6d,0x9f,0x7c,0xb3,0x2c,0xc3,0xcf,0xee,0x80,0x8a,0xe6,0x1d,0x63,0xab,0x78,0x2b,0xf1,0x30,0x2f,0xd0,0x58,0x61,0x25,0x4f,0xf8,0x9b,0x42,0x1,0x1e,0x16,0xac,0xb4,0x5c,0xbd,0x73,0xe0,0xe2,0x62,0x37,0xe7,0x7a,0xd2,0x84,0x28,0x75,0x3a,0x18,0x60,0xdc,0xe,0x6,0x36,0xad,0xa9,0xd8,0x45,0xfe,0xef,0x90,0x5e,0xb1,0xe9,0x72,0x95,0xdb,0xf4,0xb,0x51,0x59,0x46,0x92,0x34,0x50,0x6a,0xc8,0xd6,0x20,0x9a,0x88,0xfa,0x86,0x44,0x19,0x12,0x77,0x81,0xb7,0xb6,0xd4,0xe1,0x48,0x23,0x74,0x3f,0xca,0x32,0x4c,0x2,0x89,0x5a,0xba,0xfd,0x21,0xa2,0x5d,0x9,0x6b,0x85,0x55,0xe5,0xb2,0xa8,0xdd,0xc4,0xa4,0x2d,0xf2,0x69,0x14,0xe3,0xa7,0x22,0xbb,0xd5,0x83,0xda,0x7e,0x54,0xb0,0x9d,0xb5,0x31,0x8c,0x68,0x87,0x2a,0x7d,0xa5,0x11,0x33,0xc,0x6f,0x96,0xd5,0xf6,0xcf,0xd8,0xb2,0xa7,0x66,0x47,0xb8,0x3c,0xf4,0xbc,0xef,0x45,0xed,0xbf,0x13,0xf5,0x75,0x70,0xa0,0x2a,0xcb,0x77,0xe4,0x81,0x89,0x23,0x3b,0x78,0x69,0xc9,0x7,0x3e,0x3a,0xd2,0x4f,0x99,0x4b,0xa1,0x91,0xad,0xe2,0xf7,0x8f,0xfd,0xc7,0x41,0x5f,0xd1,0xce,0xa3,0x5,0x63,0x4c,0xc6,0x9c,0x7e,0x26,0x2,0xe5,0x76,0x43,0xb4,0xdf,0x16,0xe0,0x21,0x20,0xd3,0x11,0x85,0x8e,0xd,0xb7,0x6d,0x1f,0x9e,0xca,0x12,0xfc,0x6a,0x2d,0x35,0xb6,0x95,0xdb,0xcd,0x1e,0xa8,0xe3,0xa5,0x5d,0xb5,0x30,0x42,0x2c,0xfe,0x65,0x74,0x83,0x53,0x4a,0xba,0x33,0x72,0xc2,0x3f,0x25,0x32,0xea,0xa4,0x86,0xff,0x1b,0xbd,0x10,0xa,0x27,0xa6,0x22,0x4d,0x14,0xc3,0xe9,0xaa,0x9b,0x80,0xcc,0x7c,0x55,0xdd,0x0,0xd7,0xc8,0xd0,0xdc,0xc0,0x59,0x6e,0xc4,0x7f,0x6,0x4,0x9,0xf,0x37,0x6b,0xd9,0x29,0x61,0xd6,0x62,0xf2,0x51,0x5b,0xec,0xe,0x9f,0x5a,0x4e,0x73,0xda,0xbe,0x9a,0x56,0x1c,0x8d,0x38,0x94,0x9d,0x67,0x5c,0xb9,0xac,0x49,0x2b,0x8c,0x40,0xde,0x19,0x6c,0xaf,0x68,0x88,0x3d,0xb3,0x31,0xae,0x90,0xf9,0x18,0x7d,0x48,0x82,0x2f,0x84,0x28,0xa2,0xb1,0x87,0xc5,0xb0,0xb,0xf1,0x60,0xc1,0x92,0x15,0xe7,0xe1,0x64,0xf3,0x7a,0xf8,0x46,0xab,0x57,0x8b,0xf0,0x1a,0x52,0xee,0xd4,0xa9,0x97,0xfb,0x3,0x34,0x44,0x7b,0x50,0x1,0xe6,0xe8,0x2e,0x98,0x1d,0x17,0x8a,0x71,0x54,0xbb,0x79,0x58,0x8,0xfa,0x24,0xeb,0x36,0x39,0x5e,0x93,0x1,0x52,0x1a,0xd2,0x56,0xa9,0x88,0x49,0x5c,0x36,0x21,0x18,0x3b,0x78,0x81,0xe2,0xd5,0xcd,0x67,0x6f,0xa,0x99,0x25,0xc4,0x4e,0x9e,0x9b,0x1b,0xfd,0x51,0x3,0xab,0x61,0x19,0xc,0x43,0x7f,0x4f,0xa5,0x77,0xa1,0x3c,0xd4,0xd0,0xe9,0x27,0x87,0x96,0xb,0xec,0xc8,0x90,0x72,0x28,0xa2,0x8d,0xeb,0x4d,0x20,0x3f,0xb1,0xaf,0x29,0x13,0xf1,0x83,0x59,0xe3,0x60,0x6b,0xff,0x3d,0xce,0xcf,0xe,0xf8,0x31,0x5a,0xad,0x98,0xb3,0x4b,0xd,0x46,0xf0,0x23,0x35,0x7b,0x58,0xdb,0xc3,0x84,0x12,0xfc,0x24,0x70,0xcb,0xd1,0x2c,0x9c,0xdd,0x54,0xa4,0xbd,0x6d,0x9a,0x8b,0x10,0xc2,0xac,0xde,0x5b,0x7,0x2d,0xfa,0xa3,0xcc,0x48,0xc9,0xe4,0xfe,0x53,0xf5,0x11,0x68,0x4a,0x4,0xdc,0x2a,0x80,0xb7,0x2e,0x32,0x3e,0x26,0x39,0xee,0x33,0xbb,0x92,0x22,0x6e,0x75,0x44,0x2,0xb5,0xbf,0x1c,0x8c,0x38,0x8f,0xc7,0x37,0x85,0xd9,0xe1,0xe7,0xea,0xe8,0x91,0xb2,0x89,0x73,0x7a,0xd6,0x63,0xf2,0xb8,0x74,0x50,0x34,0x9d,0xa0,0xb4,0x71,0xe0,0x40,0xdf,0x5d,0xd3,0x66,0x86,0x41,0x82,0xf7,0x30,0xae,0x62,0xc5,0xa7,0x42,0x57,0x1f,0xe5,0x5e,0x2b,0x69,0x5f,0x4c,0xc6,0x6a,0xc1,0x6c,0xa6,0x93,0xf6,0x17,0x7e,0xf4,0x1e,0x65,0xb9,0x45,0xa8,0x16,0x94,0x1d,0x8a,0xf,0x9,0xfb,0x7c,0x2f,0x8e,0x76,0xc0,0x6,0x8,0xef,0xbe,0x95,0xaa,0xda,0xed,0x15,0x79,0x47,0x3a,0x0,0xbc,0x7d,0xb0,0xd7,0xd8,0x5,0xca,0x14,0xe6,0xb6,0x97,0x55,0xba,0x9f,0x64,0xf9,0xf3,0x52,0x53,0x65,0x93,0xc7,0xac,0x5,0x30,0x1e,0x6c,0x7e,0xc4,0xf6,0xfd,0xa0,0x62,0x46,0xc5,0x19,0x5e,0x61,0x8f,0xed,0xb9,0xd6,0x2e,0xdb,0x90,0xbe,0x6d,0xe6,0xa8,0x7,0xf0,0x8d,0x16,0x31,0x5f,0xc6,0x43,0x4c,0x56,0x1,0xb1,0xc9,0x40,0x20,0x39,0xce,0x63,0x8c,0x68,0xd7,0xf5,0x41,0x99,0xb0,0x9a,0x3e,0x67,0xd5,0x51,0x79,0x54,0xab,0xc1,0x85,0xbc,0xe5,0xa6,0x7f,0x1c,0xcf,0x9c,0x4f,0x87,0x34,0xcb,0xd4,0x15,0x3,0xd3,0x86,0x6,0xcc,0x60,0x36,0x9e,0x50,0x48,0xf2,0xfa,0x4,0x97,0x59,0xb8,0xa1,0x3c,0x4d,0x49,0xba,0x74,0xb,0x1a,0x84,0xfc,0xde,0x91,0xd2,0xe2,0xea,0x38,0xd0,0x76,0xa2,0xbd,0x32,0x2c,0x8e,0xb4,0x71,0x96,0xd,0x55,0xb5,0xef,0x10,0x3f,0x5c,0xf7,0x3b,0xf1,0x6b,0xe,0xe3,0x8a,0x78,0x82,0xb6,0xc3,0xc2,0xf4,0x5b,0xd1,0x17,0x80,0x94,0x92,0xe1,0x66,0x13,0xb2,0x83,0x69,0x24,0xf8,0x35,0xd8,0x9,0x8b,0x70,0x47,0xe4,0x88,0xa7,0xda,0x21,0x9d,0x5d,0xeb,0x95,0x9b,0x23,0x72,0x37,0x8,0xa,0x2b,0x27,0xc8,0xf9,0x2,0x6e,0x64,0x2d,0xe0,0x45,0x4a,0x57,0x98,0x7b,0x89,0xae,0x73,0xf,0x26,0xf3,0xbf,0xd9,0xe8,0x1d,0xb7,0xb3,0x2a,0xa3,0xaf,0xa4,0xbb,0x18,0xaa,0x7c,0x44,0x77,0x7a,0xc,0x75,0x28,0x9f,0x81,0x22,0xa5,0x11,0x5a,0x12,0xcd,0xe9,0x0,0xa9,0x29,0x3d,0x7d,0xec,0x14,0x2f,0xe7,0xee,0xfe,0x4b,0x25,0x6f,0xad,0x6a,0xff,0x33,0x3a,0x58,0xca,0xdf,0x42,0xdd,0x4e,0xc0,0x1b,0xfb,0x1f,0xdc,0xab,0x6,0xe9,0xd,0xb2,0x90,0x24,0xfc,0xd5,0xff,0x5b,0x2,0xb0,0x34,0x1c,0x31,0x62,0x95,0xe8,0x73,0x54,0x3a,0xa3,0x26,0x29,0x33,0x64,0xd4,0xac,0x25,0x45,0x5c,0x23,0xa0,0x7c,0x3b,0x4,0xea,0x88,0xdc,0xb3,0x4b,0xbe,0xf5,0xdb,0x8,0x83,0xcd,0x37,0x36,0x0,0xf6,0xa2,0xc9,0x60,0x55,0x7b,0x9,0x1b,0xa1,0x93,0x98,0xc5,0x7,0xb5,0x13,0xc7,0xd8,0x57,0x49,0xeb,0xd1,0x14,0xf3,0x68,0x30,0xd0,0x8a,0x75,0x5a,0xc4,0x59,0x28,0x2c,0xdf,0x11,0x6e,0x7f,0xe1,0x99,0xbb,0xf4,0xb7,0x87,0x8f,0x5d,0x66,0xb6,0xe3,0x63,0xa9,0x5,0x53,0xfb,0x35,0x2d,0x97,0x9f,0x61,0xf2,0x3c,0xdd,0xce,0xa4,0xe0,0xd9,0x80,0xc3,0x1a,0x79,0xaa,0xf9,0x2a,0xe2,0x51,0xae,0xb1,0x70,0x6f,0x4e,0x42,0xad,0x9c,0x67,0xb,0x1,0x48,0x85,0x20,0x2f,0x32,0xfd,0x1e,0xec,0x15,0x22,0x81,0xed,0xc2,0xbf,0x44,0xf8,0x38,0x8e,0xf0,0xfe,0x46,0x17,0x52,0x6d,0x72,0xe5,0xf1,0xf7,0x84,0x3,0x76,0xd7,0xe6,0xc,0x41,0x9d,0x50,0xbd,0x6c,0xee,0x39,0x92,0x5e,0x94,0xe,0x6b,0x86,0xef,0x1d,0xe7,0xd3,0xa6,0xa7,0x91,0x3e,0xb4,0xc8,0xf,0x9a,0x56,0x5f,0x3d,0xaf,0xba,0x27,0xb8,0x2b,0xa5,0x7e,0x9e,0x7a,0xb9,0xa8,0x8c,0x65,0xcc,0x4c,0x58,0x18,0x89,0x71,0x4a,0x82,0x8b,0x9b,0x2e,0x40,0xa,0x7d,0xcf,0x19,0x21,0x12,0x1f,0x69,0x10,0x4d,0xfa,0xe4,0x47,0xc0,0x74,0x3f,0x77,0xcb,0x16,0x6a,0x43,0x96,0xda,0xbc,0x8d,0x78,0xd2,0xd6,0x4f,0xc6,0xca,0xc1,0xde,0xe8,0xd3,0x1b,0x12,0x2,0xb7,0xd9,0x93,0x31,0x15,0xfc,0x55,0xd5,0xc1,0x81,0x10,0xbe,0x21,0xb2,0x3c,0xe7,0x7,0xe3,0x20,0x51,0x96,0x3,0xcf,0xc6,0xa4,0x36,0x23,0xe1,0x4b,0x4f,0xd6,0x5f,0x53,0x58,0x47,0x52,0x8f,0xf3,0xda,0xf,0x43,0x25,0x14,0xd4,0x63,0x7d,0xde,0x59,0xed,0xa6,0xee,0xe4,0x56,0x80,0xb8,0x8b,0x86,0xf0,0x89,0xa1,0x17,0x69,0x67,0xdf,0x8e,0xcb,0xf4,0x8c,0xbb,0x18,0x74,0x5b,0x26,0xdd,0x61,0xd1,0x1c,0xb9,0xb6,0xab,0x64,0x87,0x75,0xf6,0xd7,0xdb,0x34,0x5,0xfe,0x92,0x98,0x84,0x7e,0x4a,0x3f,0x3e,0x8,0xa7,0x2d,0xa0,0xb,0xc7,0xd,0x97,0xf2,0x1f,0x76,0x7f,0x95,0xd8,0x4,0xc9,0x24,0xf5,0x77,0xeb,0x7c,0x68,0x6e,0x1d,0x9a,0xef,0x4e,0x78,0x0,0x22,0x6d,0x2e,0x1e,0x16,0xc4,0x5d,0xc0,0xb1,0xb5,0x46,0x88,0xf7,0xe6,0x8d,0x6a,0xf1,0xa9,0x49,0x13,0xec,0xc3,0x2c,0x8a,0x5e,0x41,0xce,0xd0,0x72,0x48,0x33,0x60,0xb3,0x7b,0xc8,0x37,0x28,0xe9,0x57,0x3d,0x79,0x40,0x19,0x5a,0x83,0xe0,0xac,0xb4,0xe,0x6,0xf8,0x6b,0xa5,0x44,0xff,0x2f,0x7a,0xfa,0x30,0x9c,0xca,0x62,0xb0,0xaa,0xfd,0x4d,0x35,0xbc,0xdc,0xc5,0xfb,0xc,0x71,0xea,0xcd,0xa3,0x3a,0xbf,0x4c,0x66,0xc2,0x9b,0x29,0xad,0x85,0xa8,0x32,0x9f,0x70,0x94,0x2b,0x9,0xbd,0x65,0xe2,0x90,0x82,0x38,0xa,0x1,0x5c,0x9e,0xae,0xaf,0x99,0x6f,0x3b,0x50,0xf9,0xcc,0x2a,0xd2,0x27,0x6c,0x42,0x91,0x1a,0x54,0xba,0x39,0xe5,0xa2,0x9d,0x73,0x11,0x45,0xd8,0x96,0x1d,0xce,0xe0,0xab,0x5e,0xa6,0xc9,0x9d,0xff,0x11,0x2e,0x69,0xb5,0x36,0x12,0xd0,0x8d,0x86,0xb4,0xe,0x1c,0x6e,0x40,0x75,0xdc,0xb7,0xe3,0x15,0x23,0x22,0x24,0x9,0x21,0xa5,0x17,0x4e,0xea,0xc0,0xe9,0x31,0x85,0xa7,0x18,0xfc,0x13,0xbe,0x49,0x50,0x30,0xb9,0xc1,0x71,0x26,0x3c,0x33,0xb6,0x2f,0x41,0x66,0xfd,0x80,0x77,0xc8,0x29,0xe7,0x74,0x8a,0x82,0x38,0x20,0xee,0x46,0x10,0xbc,0x76,0xf6,0xa3,0x73,0x65,0xa4,0xbb,0x44,0xf7,0x3f,0xec,0xbf,0x6c,0xf,0xd6,0x95,0xcc,0xf5,0xb1,0xdb,0x4f,0x60,0x9f,0xc5,0x25,0x7d,0xe6,0x1,0xc4,0xfe,0x5c,0x42,0xcd,0xd2,0x6,0xa0,0x48,0x9a,0x92,0xa2,0xe1,0xae,0x8c,0xf4,0x6a,0x7b,0x4,0xca,0x39,0x3d,0x4c,0xd1,0xfb,0x79,0xa8,0x45,0x88,0x54,0x19,0xf3,0xc2,0x63,0x16,0x91,0xe2,0xe4,0xf0,0x67,0xa1,0x2b,0x84,0xb2,0xb3,0xc6,0xf2,0x8,0xfa,0x93,0x7e,0x1b,0x81,0x4b,0x87,0x2c,0xf9,0xb,0xe8,0x27,0x3a,0x35,0x90,0x5d,0x14,0x1e,0x72,0x89,0xb8,0x57,0x5b,0x7a,0x78,0x47,0x2,0x53,0xeb,0xe5,0x9b,0x2d,0xed,0x51,0xaa,0xd7,0xf8,0x94,0x37,0x0,0x62,0x2a,0x61,0xd5,0x52,0xf1,0xef,0x58,0x5,0x7c,0xa,0x7,0x34,0xc,0xda,0x68,0xcb,0xd4,0xdf,0xd3,0x5a,0xc3,0xc7,0x6d,0x98,0xa9,0xcf,0x83,0x56,0x7f,0x3,0xde,0xac,0x6f,0x8b,0x6b,0xb0,0x3e,0xad,0x32,0xaf,0xba,0x28,0x4a,0x43,0x8f,0x1a,0xdd,0x1f,0x55,0x3b,0x8e,0x9e,0x97,0x5f,0x64,0x9c,0xd,0x4d,0x59,0xd9,0x70,0x99,0xbd,0x26,0x7c,0xf6,0xd9,0x5f,0xb8,0x9c,0xc4,0xe5,0xfb,0x7d,0x47,0xbf,0x19,0x74,0x6b,0x2b,0x1b,0xf1,0x23,0x35,0x4d,0x58,0x17,0xbd,0x73,0xd3,0xc2,0xf5,0x68,0x80,0x84,0x5e,0xcd,0x71,0x90,0x81,0x99,0x33,0x3b,0xa9,0x5,0x57,0xff,0x1a,0xca,0xcf,0x4f,0x2,0xfd,0xdc,0x1d,0x55,0x6,0x4e,0x86,0x6f,0x2c,0xd5,0xb6,0x8,0x62,0x75,0x4c,0x98,0x1c,0x9d,0xb0,0x53,0x79,0xae,0xf7,0x3c,0x1e,0x50,0x88,0xaa,0x7,0xa1,0x45,0x89,0x0,0xf0,0xe9,0x9f,0x85,0x78,0xc8,0x96,0xf8,0x8a,0xf,0x39,0xce,0xdf,0x44,0xa4,0x77,0x61,0x2f,0xe7,0x1f,0x59,0x12,0x46,0xa8,0x70,0x24,0xc,0x8f,0x97,0xd0,0x34,0x3f,0xab,0x69,0xa5,0xd7,0xd,0xb7,0x65,0xe,0xf9,0xcc,0x9a,0x9b,0x5a,0xac,0x32,0xd2,0x15,0xd6,0x14,0x8b,0x9,0x87,0x91,0xf3,0x16,0x3,0xa3,0x64,0xfa,0x36,0x82,0x37,0xa6,0xec,0xe6,0xdd,0x27,0x2e,0xf4,0xe0,0x25,0xb4,0x20,0x4,0x60,0xc9,0xd8,0x6c,0xdb,0x93,0x56,0xe1,0xeb,0x48,0xb3,0xbe,0xbc,0xc5,0x63,0xd1,0x8d,0xb5,0x66,0x6a,0x72,0x6d,0x7e,0xd4,0xe3,0x7a,0x76,0x3a,0x21,0x10,0xba,0x67,0xef,0xc6,0x51,0x9e,0x40,0xb2,0x29,0xe4,0x83,0x8c,0xcb,0x30,0xad,0xa7,0xe2,0xc3,0x1,0xee,0xbb,0xea,0xc1,0xfe,0x22,0x94,0x52,0x5c,0x13,0x6e,0x54,0xe8,0x8e,0xb9,0x41,0x2d,0x11,0xfc,0x42,0xc0,0xa0,0x4a,0x31,0xed,0xaf,0x28,0x7b,0xda,0x49,0xde,0x5b,0x5d,0x3d,0xb,0x18,0x92,0x4b,0xb1,0xa,0x7f,0xc7,0xa2,0x43,0x2a,0x3e,0x95,0x38,0xf2,0xe9,0xe1,0x5b,0x43,0xab,0x4a,0x84,0x17,0x15,0x95,0xc0,0x10,0x8d,0x25,0x73,0xdf,0x94,0x5c,0x8f,0xdc,0x6,0xc7,0xd8,0x27,0xaf,0x96,0xd2,0xb8,0xf,0x6c,0xb5,0xf6,0x46,0x1e,0x85,0x62,0x2c,0x3,0xfc,0xa6,0xae,0xb1,0x65,0xc3,0xa7,0x9d,0x3f,0x21,0x82,0xcd,0xef,0x97,0x2b,0xf9,0xf1,0xc1,0x5a,0x5e,0x2f,0xb2,0x9,0x18,0x67,0xa9,0x83,0xc8,0x3d,0xc5,0xbb,0xf5,0x7e,0xad,0x4d,0xa,0xd6,0x55,0xaa,0xfe,0x9c,0x72,0xd7,0x6d,0x7f,0xd,0x71,0xb3,0xee,0xe5,0x80,0x76,0x40,0x41,0x23,0x16,0xbf,0xd4,0x74,0x2d,0x89,0xa3,0x47,0x6a,0x42,0xc6,0x7b,0x9f,0x70,0xdd,0x8a,0x52,0xe6,0xc4,0xa2,0x12,0x45,0x5f,0x2a,0x33,0x53,0xda,0x5,0x9e,0xe3,0x14,0x50,0xd5,0x4c,0x22,0x31,0x92,0x8c,0x3b,0x1,0x49,0x2,0xb6,0x57,0x6f,0xb9,0xb,0x66,0x1f,0x69,0x64,0x39,0xa0,0xa4,0xe,0xa8,0xb7,0xbc,0xb0,0x35,0x1c,0x60,0xbd,0xfb,0xca,0xac,0xe0,0xd3,0x5d,0xce,0x51,0xcf,0xc,0xe8,0x8,0x20,0xec,0x79,0xbe,0xcc,0xd9,0x4b,0x29,0xfd,0xf4,0x3c,0x7,0x7c,0x36,0x58,0xed,0xba,0x13,0xfa,0xde,0xff,0x6e,0x2e,0x3a,0xeb,0x37,0x7a,0x90,0x98,0x1a,0xcb,0x26,0x81,0x87,0x93,0x4,0xa1,0x0,0x75,0xf2,0xd0,0xa5,0x91,0x6b,0xc2,0x48,0xe7,0xd1,0xe2,0x28,0xe4,0x4f,0x99,0xf0,0x1d,0x78,0x59,0x56,0xf3,0x3e,0x9a,0x68,0x8b,0x44,0xdb,0x34,0x38,0x19,0x77,0x7d,0x11,0xea,0x88,0x86,0xf8,0x4e,0x1b,0x24,0x61,0x30,0x9b,0xf7,0x54,0x63,0x8e,0x32,0xc9,0xb4,0x41,0xb,0x65,0xd0,0xc0,0xc9,0x1,0x3a,0xc2,0x53,0x13,0x7,0x87,0x2e,0xc7,0xe3,0xf2,0x31,0xd5,0x35,0xee,0x60,0xf3,0x6c,0xf1,0xe4,0x76,0x14,0x1d,0xd1,0x44,0x83,0x95,0x8a,0x81,0x8d,0x4,0x9d,0x99,0x33,0xc6,0xf7,0x91,0xdd,0x8,0x21,0x5d,0x80,0x3c,0x74,0x3f,0x8b,0xc,0xaf,0xb1,0x6,0x5b,0x22,0x54,0x59,0x6a,0x52,0x84,0x36,0x26,0x19,0x5c,0xd,0xb5,0xbb,0xc5,0x73,0xb3,0xf,0xf4,0x89,0xa6,0xca,0x69,0x5e,0xa7,0x55,0xb6,0x79,0x64,0x6b,0xce,0x3,0x4a,0x40,0x2c,0xd7,0xe6,0x9,0x5,0x24,0xff,0x75,0xda,0xec,0xed,0x98,0xac,0x56,0xa4,0xcd,0x20,0x45,0xdf,0x15,0xd9,0x72,0xa5,0x27,0xf6,0x1b,0xd6,0xa,0x47,0xad,0x9c,0x3d,0x48,0xcf,0xbc,0xba,0xae,0x39,0x16,0xc4,0xcc,0xfc,0xbf,0xf0,0xd2,0xaa,0x34,0x25,0x5a,0x94,0x67,0x63,0x12,0x8f,0x11,0x3e,0xc1,0x9b,0x7b,0x23,0xb8,0x5f,0x9a,0xa0,0x2,0x1c,0x93,0x8c,0x58,0xfe,0x3b,0xfa,0xe5,0x1a,0xa9,0x61,0xb2,0xe1,0x32,0x51,0x88,0xcb,0x92,0xab,0xef,0x85,0x96,0x77,0xb9,0x2a,0xd4,0xdc,0x66,0x7e,0xb0,0x18,0x4e,0xe2,0x28,0xa8,0xfd,0x2d,0x17,0xe,0x6e,0xe7,0x9f,0x2f,0x78,0x62,0x6d,0xe8,0x71,0x1f,0x38,0xa3,0xde,0x29,0x7a,0x57,0x7f,0xfb,0x49,0x10,0xb4,0x9e,0xb7,0x6f,0xdb,0xf9,0x46,0xa2,0x4d,0xe0,0x4c,0x8e,0xd3,0xd8,0xea,0x50,0x42,0x30,0x1e,0x2b,0x82,0xe9,0xbd,0x4b,0x7d,0x7c,0x86,0xc8,0x43,0x90,0xbe,0xf5,0x0,0xf8,0x97,0xc3,0xa1,0x4f,0x70,0x37,0xeb,0x68,0xc2,0xab,0x46,0x23,0xb9,0x73,0xbf,0x14,0x99,0x13,0xbc,0x8a,0x8b,0xfe,0xca,0x30,0xfa,0x5b,0x2e,0xa9,0xda,0xdc,0xc8,0x5f,0xc3,0x41,0x90,0x7d,0xb0,0x6c,0x21,0xcb,0xd5,0x69,0x92,0xef,0xc0,0xac,0xf,0x38,0x40,0x7f,0x3a,0x6b,0xd3,0xdd,0xa3,0x15,0x2c,0x26,0x4a,0xb1,0x80,0x6f,0x63,0x42,0xc1,0x33,0xd0,0x1f,0x2,0xd,0xa8,0x65,0xa0,0x91,0xf7,0xbb,0x6e,0x47,0x3b,0xe6,0xf3,0xec,0xe7,0xeb,0x62,0xfb,0xff,0x55,0x3d,0x44,0x32,0x3f,0xc,0x34,0xe2,0x50,0x5a,0x12,0x59,0xed,0x6a,0xc9,0xd7,0x60,0xa4,0x35,0x75,0x61,0xe1,0x48,0xa1,0x85,0x27,0x6d,0x3,0xb6,0xa6,0xaf,0x67,0x5c,0x97,0x82,0x10,0x72,0x7b,0xb7,0x22,0xe5,0x94,0x57,0xb3,0x53,0x88,0x6,0x95,0xa,0x78,0x4d,0xe4,0x8f,0xdb,0x2d,0x1b,0x1a,0x2a,0xe8,0xb5,0xbe,0x8c,0x36,0x24,0x56,0xf1,0xa5,0xc7,0x29,0x16,0x51,0x8d,0xe,0xe0,0xae,0x25,0xf6,0xd8,0x93,0x66,0x9e,0xb,0x8e,0x17,0x79,0x5e,0xc5,0xb8,0x4f,0x71,0x68,0x8,0x81,0xf9,0x49,0x1e,0x4,0xd1,0x9,0xbd,0x9f,0x20,0xc4,0x2b,0x86,0x1c,0x31,0x19,0x9d,0x2f,0x76,0xd2,0xf8,0x54,0x37,0xee,0xad,0xf4,0xcd,0x89,0xe3,0x5d,0x9c,0x83,0x7c,0xcf,0x7,0xd4,0x87,0xd6,0x7e,0x28,0x84,0x4e,0xce,0x9b,0x4b,0xf0,0x11,0xdf,0x4c,0xb2,0xba,0x0,0x18,0x52,0x43,0x3c,0xf2,0x1,0x5,0x74,0xe9,0x70,0xa2,0xaa,0x9a,0xd9,0x96,0xb4,0xcc,0xfc,0xc6,0x64,0x7a,0xf5,0xea,0x3e,0x98,0x77,0x58,0xa7,0xfd,0x1d,0x45,0xde,0x39,0xa3,0xa9,0x34,0xcf,0xea,0x5,0xc7,0xe6,0xb6,0x44,0x9a,0x55,0x88,0x87,0xe0,0x2d,0xec,0x50,0x6a,0x17,0x29,0x45,0xbd,0x8a,0xfa,0xc5,0xee,0xbf,0x58,0x56,0x90,0x26,0xde,0x7f,0x2c,0xab,0x59,0x5f,0xda,0x4d,0xc4,0x46,0xf8,0x15,0xe9,0x35,0x4e,0xa4,0x2e,0x47,0xa6,0xc3,0xf6,0x3c,0x91,0x3a,0x96,0x1c,0xf,0x39,0x7b,0xe,0xb5,0x4f,0x7,0x12,0xf7,0x95,0x32,0xfe,0x60,0xa7,0xd2,0x11,0xd6,0x36,0x83,0xd,0x8f,0x10,0xb0,0x21,0xe4,0xf0,0xcd,0x64,0x0,0x24,0xe8,0xa2,0x33,0x86,0x2a,0x23,0xd9,0xe2,0xc1,0xb8,0xba,0xb7,0xb1,0x89,0xd5,0x67,0x97,0xdf,0x68,0xdc,0x4c,0xef,0xe5,0x52,0x14,0x25,0x3e,0x72,0xc2,0xeb,0x63,0xbe,0x69,0x76,0x6e,0x62,0x7e,0xe7,0xd0,0x7a,0x8c,0x54,0x1a,0x38,0x41,0xa5,0x3,0xae,0xb4,0x99,0x18,0x9c,0xf3,0xaa,0x7d,0x57,0xb,0x8e,0xfc,0x92,0x40,0xdb,0xca,0x3d,0xed,0xf4,0x4,0x8d,0xcc,0x7c,0x81,0x9b,0x20,0x74,0xac,0x42,0xd4,0x93,0x8b,0x8,0x2b,0x65,0x73,0xa0,0x16,0x5d,0x1b,0xe3,0xc8,0xfd,0xa,0x61,0xa8,0x5e,0x9f,0x9e,0x6d,0xaf,0x3b,0x30,0xb3,0x9,0xd3,0xa1,0x43,0x79,0xff,0xe1,0x6f,0x70,0x1d,0xbb,0xdd,0xf2,0x78,0x22,0xc0,0x98,0xbc,0x5b,0xc6,0xd7,0x77,0xb9,0x80,0x84,0x6c,0xf1,0x27,0xf5,0x1f,0x2f,0x13,0x5c,0x49,0x31,0xfb,0x53,0x1,0xad,0x4b,0xcb,0xce,0x1e,0x94,0x75,0xc9,0x5a,0x3f,0x37,0x9d,0x85,0xb2,0xd1,0x28,0x6b,0x48,0x71,0x66,0xc,0x19,0xd8,0xf9,0x6,0x82,0x4a,0x2,0x51,0xe8,0xcc,0x25,0x8c,0xc,0x18,0x58,0xc9,0x31,0xa,0xc2,0xcb,0xdb,0x6e,0x0,0x4a,0x88,0x4f,0xda,0x16,0x1f,0x7d,0xef,0xfa,0x67,0xf8,0x6b,0xe5,0x3e,0xde,0x3a,0xf9,0x8b,0x56,0x2a,0x3,0xd6,0x9a,0xfc,0xcd,0x38,0x92,0x96,0xf,0x86,0x8a,0x81,0x9e,0x3d,0x8f,0x59,0x61,0x52,0x5f,0x29,0x50,0xd,0xba,0xa4,0x7,0x80,0x34,0x7f,0x37,0x55,0x62,0xc1,0xad,0x82,0xff,0x4,0xb8,0x78,0xce,0xb0,0xbe,0x6,0x57,0x12,0x2d,0x2f,0xe,0x2,0xed,0xdc,0x27,0x4b,0x41,0x8,0xc5,0x60,0x6f,0x72,0xbd,0x5e,0xac,0x79,0xd2,0x1e,0xd4,0x4e,0x2b,0xc6,0xaf,0x5d,0xa7,0x93,0xe6,0xe7,0xd1,0x7e,0xf4,0x32,0xa5,0xb1,0xb7,0xc4,0x43,0x36,0x97,0xa6,0x4c,0x1,0xdd,0x10,0xfd,0x2c,0xae,0x84,0x19,0x68,0x6c,0x9f,0x51,0x2e,0x3f,0xa1,0xd9,0xfb,0xb4,0xf7,0xc7,0xcf,0x1d,0xf5,0x53,0x87,0x98,0x17,0x9,0xab,0x91,0x54,0xb3,0x28,0x70,0x90,0xca,0x35,0x1a,0x8e,0xe4,0xa0,0x99,0xc0,0x83,0x5a,0x39,0xea,0xb9,0x6a,0xa2,0x11,0xee,0xf1,0x30,0x26,0xf6,0xa3,0x23,0xe9,0x45,0x13,0xbb,0x75,0x6d,0xd7,0xdf,0x21,0xb2,0x7c,0x9d,0x22,0xd5,0xa8,0x33,0x14,0x7a,0xe3,0x66,0x69,0x73,0x24,0x94,0xec,0x65,0x5,0x1c,0xeb,0x46,0xa9,0x4d,0xf2,0xd0,0x64,0xbc,0x95,0xbf,0x1b,0x42,0xf0,0x74,0x5c,0x71,0x77,0x76,0x40,0xb6,0xe2,0x89,0x20,0x15,0x3b,0x49,0x5b,0xe1,0xd3,0xd8,0x85,0x47,0x63,0xe0,0x3c,0x7b,0x44,0xaa,0xc8,0x9c,0xf3,0xb,0xfe,0xb5,0x9b,0x48,0xc3,0x8d,0x66,0x2e,0x65,0xd1,0x56,0xf5,0xeb,0x5c,0x1,0x78,0xe,0x3,0x30,0x8,0xde,0x6c,0xcf,0xd0,0xdb,0xd7,0x5e,0xc7,0xc3,0x69,0x9c,0xad,0xcb,0x87,0x52,0x7b,0x7,0xda,0xa8,0x6b,0x8f,0x6f,0xb4,0x3a,0xa9,0x36,0xab,0xbe,0x2c,0x4e,0x47,0x8b,0x1e,0xd9,0x1b,0x51,0x3f,0x8a,0x9a,0x93,0x5b,0x60,0x98,0x9,0x49,0x5d,0xdd,0x74,0x9d,0xb9,0xff,0x7d,0xac,0x41,0x8c,0x50,0x1d,0xf7,0xc6,0x67,0x12,0x95,0xe6,0xe0,0xf4,0x63,0xa5,0x2f,0x80,0xb6,0xb7,0xc2,0xf6,0xc,0xfe,0x97,0x7a,0x1f,0x85,0x4f,0x83,0x28,0xfd,0xf,0xec,0x23,0x3e,0x31,0x94,0x59,0x10,0x1a,0x76,0x8d,0xbc,0x53,0x5f,0x7e,0x7c,0x43,0x6,0x57,0xef,0xe1,0x9f,0x29,0xe9,0x55,0xae,0xd3,0xfc,0x90,0x33,0x4,0xcc,0x2d,0xe3,0x70,0x8e,0x86,0x3c,0x24,0xea,0x42,0x14,0xb8,0x72,0xf2,0xa7,0x77,0x61,0xa0,0xbf,0x40,0xf3,0x3b,0xe8,0xbb,0x68,0xb,0xd2,0x91,0xc8,0xf1,0xb5,0xdf,0x4b,0x64,0x9b,0xc1,0x21,0x79,0xe2,0x5,0xc0,0xfa,0x58,0x46,0xc9,0xd6,0x2,0xa4,0x4c,0x9e,0x96,0xa6,0xe5,0xaa,0x88,0xf0,0x6e,0x7f,0x0,0xce,0x3d,0x39,0x48,0xd5,0xdc,0x92,0x19,0xca,0xe4,0xaf,0x5a,0xa2,0xcd,0x99,0xfb,0x15,0x2a,0x6d,0xb1,0x32,0x16,0xd4,0x89,0x82,0xb0,0xa,0x18,0x6a,0x44,0x71,0xd8,0xb3,0xe7,0x11,0x27,0x26,0x20,0xd,0x25,0xa1,0x13,0x4a,0xee,0xc4,0xed,0x35,0x81,0xa3,0x1c,0xf8,0x17,0xba,0x4d,0x54,0x34,0xbd,0xc5,0x75,0x22,0x38,0x37,0xb2,0x2b,0x45,0x62,0xf9,0x84,0x73,0xec,0x24,0x6c,0x3f,0x77,0xb6,0x97,0x68,0x26,0x1f,0x8,0x62,0xdc,0xbf,0x46,0x5,0x51,0x59,0xf3,0xeb,0xfa,0x1b,0xa7,0x34,0x25,0xa5,0xa0,0x70,0x95,0x3d,0x6f,0xc3,0x7d,0x32,0x27,0x5f,0x49,0x9b,0x71,0x41,0xee,0xea,0x2,0x9f,0xa8,0xb9,0x19,0xd7,0xae,0xf6,0xd2,0x35,0xb3,0x9c,0x16,0x4c,0x1,0x1e,0x73,0xd5,0x2d,0x17,0x91,0x8f,0xdd,0x67,0xbd,0xcf,0x3,0xc1,0x55,0x5e,0xc6,0x30,0xf1,0xf0,0xa6,0x93,0x64,0xf,0x78,0x33,0x75,0x8d,0x45,0xb,0x1d,0xce,0xba,0xfd,0xe5,0x66,0x4e,0x1a,0xc2,0x2c,0xa2,0x12,0xef,0xf5,0x83,0x9a,0x6a,0xe3,0x2e,0xb5,0xa4,0x53,0x65,0xe0,0x92,0xfc,0x9d,0xc4,0x13,0x39,0xda,0xf7,0x76,0xf2,0x2f,0xcb,0x6d,0xc0,0xe2,0x3a,0x74,0x56,0x10,0x89,0xbe,0x14,0x7,0x18,0x0,0xc,0xac,0x85,0xd,0xd0,0x7a,0x4b,0x50,0x1c,0x22,0x81,0x8b,0x3c,0xf9,0xb1,0x6,0xb2,0xdf,0xe7,0xbb,0x9,0xaf,0xd6,0xd4,0xd9,0x44,0x4d,0xb7,0x8c,0x86,0xcc,0x5d,0xe8,0xa3,0xa,0x6e,0x4a,0xde,0x4f,0x8a,0x9e,0xed,0x63,0xe1,0x7e,0xbc,0x7f,0xb8,0x58,0x5c,0x90,0xe,0xc9,0x69,0x7c,0x99,0xfb,0x15,0x60,0xdb,0x21,0xf8,0x72,0x61,0x57,0x98,0x52,0xff,0x54,0x40,0x29,0xc8,0xad,0x87,0x5b,0x20,0xca,0xaa,0x28,0x96,0x7b,0x37,0x31,0xb4,0x23,0xb0,0x11,0x42,0xc5,0x36,0x38,0xfe,0x48,0x94,0xab,0x80,0xd1,0x47,0x2b,0xd3,0xe4,0x82,0x3e,0x4,0x79,0xe6,0xe9,0x8e,0x43,0xd8,0x2a,0xf4,0x3b,0x84,0x6b,0xa9,0x88,0xcd,0xc7,0x5a,0xa1,0x42,0x90,0x98,0xa8,0xeb,0xa4,0x86,0xfe,0x60,0x71,0xe,0xc0,0x33,0x37,0x46,0xdb,0x45,0x6a,0x95,0xcf,0x2f,0x77,0xec,0xb,0xce,0xf4,0x56,0x48,0xc7,0xd8,0xc,0xaa,0x6f,0xae,0xb1,0x4e,0xfd,0x35,0xe6,0xb5,0x66,0x5,0xdc,0x9f,0xc6,0xff,0xbb,0xd1,0xc2,0x23,0xed,0x7e,0x80,0x88,0x32,0x2a,0xe4,0x4c,0x1a,0xb6,0x7c,0xfc,0xa9,0x79,0x43,0x5a,0x3a,0xb3,0xcb,0x7b,0x2c,0x36,0x39,0xbc,0x25,0x4b,0x6c,0xf7,0x8a,0x7d,0x2e,0x3,0x2b,0xaf,0x1d,0x44,0xe0,0xca,0xe3,0x3b,0x8f,0xad,0x12,0xf6,0x19,0xb4,0x18,0xda,0x87,0x8c,0xbe,0x4,0x16,0x64,0x4a,0x7f,0xd6,0xbd,0xe9,0x1f,0x29,0x28,0xd2,0x9c,0x17,0xc4,0xea,0xa1,0x54,0xac,0xc3,0x97,0xf5,0x1b,0x24,0x63,0xbf,0x3c,0x15,0x5f,0x31,0x84,0x94,0x9d,0x55,0x6e,0x96,0x7,0x47,0x53,0xd3,0x7a,0x93,0xb7,0xa6,0x65,0x81,0x61,0xba,0x34,0xa7,0x38,0xa5,0xb0,0x22,0x40,0x49,0x85,0x10,0xd7,0xc1,0xde,0xd5,0xd9,0x50,0xc9,0xcd,0x67,0x92,0xa3,0xc5,0x89,0x5c,0x75,0x9,0xd4,0x68,0x20,0x6b,0xdf,0x58,0xfb,0xe5,0x52,0xf,0x76,0x0,0xd,0x3e,0x6,0xd0,0x62,0x72,0x4d,0x8,0x59,0xe1,0xef,0x91,0x27,0xe7,0x5b,0xa0,0xdd,0xf2,0x9e,0x3d,0xa,0xf3,0x1,0xe2,0x2d,0x30,0x3f,0x9a,0x57,0x1e,0x14,0x78,0x83,0xb2,0x5d,0x51,0x70,0xab,0x21,0x8e,0xb8,0xb9,0xcc,0xf8,0x2,0xf0,0x99,0x74,0x11,0x8b,0x41,0x8d,0x26,0xf1,0x73,0xa2,0x4f,0x82,0x5e,0x13,0xf9,0xc8,0x69,0x1c,0x9b,0xe8,0xee,0xfa,0x6d,0x66,0x21,0xfd,0x7e,0x81,0xd5,0xb7,0x59,0xa8,0xe3,0x16,0xee,0x90,0xde,0x55,0x86,0xab,0x5d,0x6b,0x6a,0x8,0x3d,0x94,0xff,0xfc,0x46,0x54,0x26,0x5a,0x98,0xc5,0xce,0x50,0xb4,0x5b,0xf6,0xa1,0x79,0xcd,0xef,0x5f,0x6,0xa2,0x88,0x6c,0x41,0x69,0xed,0x2e,0xb5,0xc8,0x3f,0x7b,0xfe,0x67,0x9,0x89,0x39,0x6e,0x74,0x1,0x18,0x78,0xf1,0x3e,0xbe,0xeb,0x3b,0xa6,0xe,0x58,0xf4,0xc2,0xca,0x70,0x68,0x80,0x61,0xaf,0x3c,0x84,0xbd,0xf9,0x93,0x24,0x47,0x9e,0xdd,0xbf,0x77,0xa4,0xf7,0x2d,0xec,0xf3,0xc,0x85,0x9a,0x4e,0xe8,0x8c,0xb6,0x14,0xa,0x6d,0x35,0xae,0x49,0x7,0x28,0xd7,0x8d,0x71,0x75,0x4,0x99,0x22,0x33,0x4c,0x82,0xa9,0xe6,0xc4,0xbc,0x0,0xd2,0xda,0xea,0xaa,0xac,0xb8,0x2f,0x8a,0x2b,0x5e,0xd9,0xc0,0x1c,0x51,0xbb,0xb3,0x31,0xe0,0xd,0xc9,0x3,0xcf,0x64,0xb2,0xdb,0x36,0x53,0xfb,0x8e,0xba,0x40,0xe9,0x63,0xcc,0xfa,0xf0,0x1f,0x13,0x32,0x5c,0x56,0x3a,0xc1,0x72,0x7d,0xd8,0x15,0xb1,0x43,0xa0,0x6f,0xb0,0xdc,0x7f,0x48,0xa5,0x19,0xe2,0x9f,0xa3,0xad,0xd3,0x65,0x30,0xf,0x4a,0x1b,0x7c,0x44,0x92,0x20,0x4d,0x34,0x42,0x4f,0x1a,0xb9,0xa7,0x10,0x2a,0x62,0x29,0x9d,0x1e,0x37,0x4b,0x96,0xd0,0xe1,0x87,0xcb,0x12,0x8b,0x8f,0x25,0x83,0x9c,0x97,0x9b,0xb,0xc7,0x52,0x95,0xe7,0xf2,0x60,0x2,0xf8,0x76,0xe5,0x7a,0xe4,0x27,0xc3,0x23,0x91,0x38,0xd1,0xf5,0xd4,0x45,0x5,0x11,0xd6,0xdf,0x17,0x2c,0x57,0x1d,0x73,0xc6,0xc1,0x99,0xbd,0x5a,0xdc,0xf3,0x79,0x23,0x6e,0x71,0x1c,0xba,0x42,0x78,0xfe,0xe0,0x12,0x5d,0x48,0x30,0x26,0xf4,0x1e,0x2e,0x81,0x85,0x6d,0xf0,0xc7,0xd6,0x76,0xb8,0x3e,0x36,0x9c,0x84,0x95,0x74,0xc8,0x5b,0x4a,0xca,0xcf,0x1f,0xfa,0x52,0x0,0xac,0x83,0x4b,0x3,0x50,0x18,0xd9,0xf8,0x7,0x49,0x70,0x67,0xd,0xb3,0xd0,0x29,0x6a,0xf2,0xab,0x7c,0x56,0xb5,0x98,0x19,0x9d,0x40,0xa4,0x2,0xaf,0x8d,0x55,0x1b,0x39,0xcd,0x7d,0x80,0x9a,0xec,0xf5,0x5,0x8c,0x41,0xda,0xcb,0x3c,0xa,0x8f,0xfd,0x93,0x17,0x5c,0x1a,0xe2,0x2a,0x64,0x72,0xa1,0xd5,0x92,0x8a,0x9,0x21,0x75,0xad,0x43,0xb2,0x8,0xd2,0xa0,0x6c,0xae,0x3a,0x31,0xa9,0x5f,0x9e,0x9f,0xc9,0xfc,0xb,0x60,0x82,0xc,0x8e,0x11,0xd3,0x10,0xd7,0x37,0x33,0xff,0x61,0xa6,0x6,0x13,0xf6,0x94,0x2b,0x22,0xd8,0xe3,0xe9,0xa3,0x32,0x87,0xcc,0x65,0x1,0x25,0xb1,0x20,0xe5,0xf1,0x4d,0xee,0xe4,0x53,0x96,0xde,0x69,0xdd,0xb0,0x88,0xd4,0x66,0xc0,0xb9,0xbb,0xb6,0x7f,0xe6,0xd1,0x7b,0x68,0x77,0x6f,0x63,0xc3,0xea,0x62,0xbf,0x15,0x24,0x3f,0x73,0x89,0x86,0xe1,0x2c,0xb7,0x45,0x9b,0x54,0xeb,0x4,0xc6,0xe7,0xa2,0xa8,0x35,0xce,0x59,0x57,0x91,0x27,0xfb,0xc4,0xef,0xbe,0x28,0x44,0xbc,0x8b,0xed,0x51,0x6b,0x16,0xe8,0x34,0x4f,0xa5,0xc5,0x47,0xf9,0x14,0x58,0x5e,0xdb,0x4c,0xdf,0x7e,0x2d,0xaa,0x7a,0xf,0xb4,0x4e,0x97,0x1d,0xe,0x38,0xf7,0x3d,0x90,0x3b,0x2f,0x46,0xa7,0xc2,0x3,0xab,0xfd,0x51,0x9b,0x1b,0x4e,0x9e,0x25,0xc4,0xa,0x99,0x67,0x6f,0xd5,0xcd,0x81,0xe2,0x3b,0x78,0x21,0x18,0x5c,0x36,0x88,0x49,0x56,0xa9,0x1a,0xd2,0x1,0x52,0x29,0x13,0xb1,0xaf,0x20,0x3f,0xeb,0x4d,0xa2,0x8d,0x72,0x28,0xc8,0x90,0xb,0xec,0x87,0x96,0xe9,0x27,0xd4,0xd0,0xa1,0x3c,0xa5,0x77,0x7f,0x4f,0xc,0x43,0x61,0x19,0x24,0x70,0x12,0xfc,0xc3,0x84,0x58,0xdb,0x35,0x7b,0xf0,0x23,0xd,0x46,0xb3,0x4b,0xad,0x98,0x31,0x5a,0xe,0xf8,0xce,0xcf,0xff,0x3d,0x60,0x6b,0x59,0xe3,0xf1,0x83,0x4,0xdc,0x68,0x4a,0xf5,0x11,0xfe,0x53,0xc9,0xe4,0xcc,0x48,0xfa,0xa3,0x7,0x2d,0xde,0x5b,0xc2,0xac,0x8b,0x10,0x6d,0x9a,0xa4,0xbd,0xdd,0x54,0x2c,0x9c,0xcb,0xd1,0xe8,0x91,0xe7,0xea,0xd9,0xe1,0x37,0x85,0x8f,0xc7,0x8c,0x38,0xbf,0x1c,0x2,0xb5,0x75,0x44,0x22,0x6e,0xbb,0x92,0xee,0x33,0x26,0x39,0x32,0x3e,0xb7,0x2e,0x2a,0x80,0x42,0x57,0xc5,0xa7,0xae,0x62,0xf7,0x30,0x41,0x82,0x66,0x86,0x5d,0xd3,0x40,0xdf,0x71,0xe0,0xa0,0xb4,0x34,0x9d,0x74,0x50,0xf2,0xb8,0xd6,0x63,0x73,0x7a,0xb2,0x89,0x2f,0x8e,0xfb,0x7c,0xf,0x9,0x1d,0x8a,0x16,0x94,0x45,0xa8,0x65,0xb9,0xf4,0x1e,0x17,0x7e,0x93,0xf6,0x6c,0xa6,0x6a,0xc1,0x4c,0xc6,0x69,0x5f,0x5e,0x2b,0x1f,0xe5,0xf9,0xf3,0x9f,0x64,0x55,0xba,0xb6,0x97,0x14,0xe6,0x5,0xca,0xd7,0xd8,0x7d,0xb0,0x0,0xbc,0x47,0x3a,0x15,0x79,0xda,0xed,0x95,0xaa,0xef,0xbe,0x6,0x8,0x76,0xc0,0x8,0x5b,0x88,0x40,0xf3,0xc,0x13,0xd2,0x6c,0x6,0x42,0x7b,0x22,0x61,0xb8,0xdb,0x97,0x8f,0x35,0x3d,0xc3,0x50,0x9e,0x7f,0xc4,0x14,0x41,0xc1,0xb,0xa7,0xf1,0x59,0x43,0x3b,0x19,0x56,0x15,0x25,0x2d,0xff,0x66,0xfb,0x8a,0x8e,0x7d,0xb3,0xcc,0xdd,0xb6,0x51,0xca,0x92,0x72,0x28,0xd7,0xf8,0x17,0xb1,0x65,0x7a,0xf5,0xeb,0x49,0x73,0xd9,0xab,0xb9,0x3,0x31,0x3a,0x67,0xa5,0x95,0x94,0xa2,0x54,0x0,0x6b,0xc2,0xf7,0x11,0xe9,0x1c,0x57,0x79,0xaa,0x21,0x6f,0x81,0x2,0xde,0x99,0xa6,0x48,0x2a,0x7e,0x8b,0x91,0xc6,0x76,0xe,0x87,0xe7,0xfe,0xc0,0x37,0x4a,0xd1,0xf6,0x98,0x1,0x84,0x77,0x5d,0xf9,0xa0,0x12,0x96,0xbe,0x93,0x9,0xa4,0x4b,0xaf,0x10,0x32,0x86,0x5e,0xda,0x70,0x74,0xed,0x64,0x68,0x63,0x7c,0x69,0xb4,0xc8,0xe1,0x34,0x78,0x1e,0x2f,0xef,0x58,0x46,0xe5,0x62,0xd6,0x9d,0xd5,0xdf,0x6d,0xbb,0x83,0xb0,0xbd,0xcb,0xb2,0xd3,0xe8,0x20,0x29,0x39,0x8c,0xe2,0xa8,0xa,0x2e,0xc7,0x6e,0xee,0xfa,0xba,0x2b,0x85,0x1a,0x89,0x7,0xdc,0x3c,0xd8,0x1b,0x6a,0xad,0x38,0xf4,0xfd,0x9f,0xd,0x18,0xbf,0x45,0x71,0x4,0x5,0x33,0x9c,0x16,0x9b,0x30,0xfc,0x36,0xac,0xc9,0x24,0x4d,0x44,0xae,0xe3,0x3f,0xf2,0x1f,0xce,0x4c,0xd0,0x47,0x53,0x55,0x26,0xa1,0xd4,0x75,0x9a,0x2c,0x52,0x5c,0xe4,0xb5,0xf0,0xcf,0xb7,0x80,0x23,0x4f,0x60,0x1d,0xe6,0x5a,0xea,0x27,0x82,0x8d,0x90,0x5f,0xbc,0x4e,0xcd,0xec,0xe0,0xf,0x3e,0xc5,0xa9,0xa3,0x1,0xf6,0xe7,0x7c,0xae,0xc0,0xb2,0x37,0xa7,0xbd,0x40,0xf0,0xb1,0x38,0xc8,0xd1,0x92,0x3f,0x99,0x7d,0x4,0x26,0x68,0xb0,0x6b,0x41,0x96,0xcf,0xa0,0x24,0xa5,0x88,0xa2,0xa3,0x62,0x94,0x5d,0x36,0xc1,0xf4,0x9d,0xef,0x35,0x8f,0xc,0x7,0x93,0x51,0x34,0xb7,0xaf,0xe8,0x7e,0x90,0x48,0x1c,0xdf,0x27,0x61,0x2a,0x9c,0x4f,0x59,0x17,0xcd,0x50,0xb8,0xbc,0x85,0x4b,0xeb,0xfa,0xd,0x75,0x60,0x2f,0x13,0x23,0xc9,0x1b,0x87,0x21,0x4c,0x53,0xdd,0xc3,0x45,0x7f,0x67,0x80,0xa4,0xfc,0x1e,0x44,0xce,0xe1,0x30,0x5a,0x4d,0x74,0x57,0x14,0xed,0x8e,0x6d,0x3e,0x76,0xbe,0x3a,0xc5,0xe4,0x25,0x22,0xf2,0xf7,0x77,0x91,0x3d,0x6f,0xc7,0xb9,0xa1,0xb,0x3,0x66,0xf5,0x49,0xa8,0xb6,0x81,0x79,0x15,0x2b,0x56,0x6c,0xd0,0x1a,0xac,0x6a,0x64,0x83,0xd2,0xf9,0xc6,0xda,0xfb,0x39,0xd6,0xf3,0x8,0x95,0x9f,0x11,0xdc,0xbb,0xb4,0x69,0xa6,0x78,0x8a,0x6,0xad,0x0,0xca,0xff,0x9a,0x7b,0x12,0x73,0x89,0x32,0x47,0x5,0x33,0x20,0xaa,0x71,0xe6,0x63,0x65,0x97,0x10,0x43,0xe2,0x98,0x72,0x9,0xd5,0x29,0xc4,0x7a,0xf8,0x18,0x3c,0x58,0xf1,0xcc,0xd8,0x1d,0x8c,0xde,0xe5,0x1f,0x16,0xba,0xf,0x9e,0xd4,0x9b,0x5c,0xc2,0xe,0xa9,0xcb,0x2e,0x3b,0x2c,0xb3,0x31,0xbf,0xa,0xea,0x2d,0xee,0x82,0x5f,0xd7,0xfe,0x4e,0x2,0x19,0x28,0x46,0xec,0xdb,0x42,0x5e,0x52,0x4a,0x55,0x5b,0xe9,0xb5,0x8d,0x8b,0x86,0x84,0xfd,0x6e,0xd9,0xd3,0x70,0xe0,0x54,0xe3,0xab,0x2c,0x80,0xd2,0x7a,0x9f,0x4f,0x4a,0xca,0xdb,0x48,0xf4,0x15,0x4,0x1c,0xb6,0xbe,0xea,0xa9,0x50,0x33,0x8d,0xe7,0xf0,0xc9,0x87,0x78,0x59,0x98,0xd0,0x83,0xcb,0x3,0x60,0x7e,0xf8,0xc2,0x3a,0x9c,0xf1,0xee,0xa3,0xf9,0x73,0x5c,0xda,0x3d,0x19,0x41,0x38,0xf6,0x56,0x47,0x70,0xed,0x5,0x1,0xae,0x9e,0x74,0xa6,0xb0,0xc8,0xdd,0x92,0xc3,0x2d,0xf5,0xa1,0x89,0xa,0x12,0x55,0x21,0xf2,0xe4,0xaa,0x62,0x9a,0xdc,0x97,0xe0,0x8b,0x7c,0x49,0x1f,0x1e,0xdf,0x29,0xb1,0xba,0x2e,0xec,0x20,0x52,0x88,0x32,0xb9,0x9b,0xd5,0xd,0x2f,0x82,0x24,0xc0,0x1d,0x99,0x18,0x35,0xd6,0xfc,0x2b,0x72,0x13,0x7d,0xf,0x8a,0xbc,0x4b,0x5a,0xc1,0xc,0x85,0x75,0x6c,0x1a,0x0,0xfd,0x4d,0x36,0x3b,0x39,0x40,0xe6,0x54,0x8,0x30,0x5d,0xe9,0x5e,0x16,0xd3,0x64,0x6e,0xcd,0xf3,0xbf,0xa4,0x95,0x3f,0xe2,0x6a,0x43,0xe3,0xef,0xf7,0xe8,0xfb,0x51,0x66,0xff,0x14,0x76,0x93,0x86,0x26,0xe1,0x7f,0xb3,0xb7,0x57,0x90,0x53,0x91,0xe,0x8c,0x2,0x71,0x65,0xa0,0x31,0xa5,0x81,0xe5,0x4c,0x7,0xb2,0x23,0x69,0x63,0x58,0xa2,0xab,0x2a,0xad,0xfe,0x5f,0xcc,0x5b,0xde,0xd8,0x94,0x79,0xc7,0x45,0x25,0xcf,0xb4,0x68,0x42,0x27,0xc6,0xaf,0xbb,0x10,0xbd,0x77,0xb8,0x8e,0x9d,0x17,0xce,0x34,0x8f,0xfa,0x4e,0xb5,0x28,0x22,0x67,0x46,0x84,0x6b,0xd4,0x1b,0xc5,0x37,0xac,0x61,0x6,0x9,0x96,0xeb,0xd1,0x6d,0xb,0x3c,0xc4,0xa8,0x3e,0x6f,0x44,0x7b,0xa7,0x11,0xd7,0xd9,0x7a,0x41,0xbb,0xb2,0x1e,0xab,0x3a,0x70,0xbc,0x98,0xfc,0x55,0x68,0x7c,0xb9,0x28,0x88,0x17,0x95,0x1b,0xae,0x4e,0x89,0x4a,0x3f,0xf8,0x66,0xaa,0xd,0x6f,0x8a,0x9f,0xe2,0x48,0x7f,0xe6,0xfa,0xf6,0xee,0xf1,0x26,0xfb,0x73,0x5a,0xea,0xa6,0xbd,0x8c,0xca,0x7d,0x77,0xd4,0x44,0xf0,0x47,0xf,0xff,0x4d,0x11,0x29,0x2f,0x22,0x20,0x59,0xbe,0x8,0xce,0xc0,0x27,0x76,0x5d,0x62,0x12,0x25,0xdd,0xb1,0x8f,0xf2,0xc8,0x74,0xb5,0x78,0x1f,0x10,0xcd,0x2,0xdc,0x2e,0x7e,0x5f,0x9d,0x72,0x57,0xac,0x31,0x3b,0xd7,0x2d,0x96,0xe3,0xa1,0x97,0x84,0xe,0xa2,0x9,0xa4,0x6e,0x5b,0x3e,0xdf,0xb6,0x3c,0xd6,0xad,0x71,0x8d,0x60,0xde,0x5c,0xd5,0x42,0xc7,0xc1,0x33,0xb4,0xe7,0x46,0xa9,0xd1,0xc4,0x8b,0xb7,0x87,0x6d,0xbf,0x69,0xf4,0x1c,0x18,0x21,0xef,0x4f,0x5e,0xc3,0x24,0x0,0x58,0xba,0xe0,0x6a,0x45,0x23,0x85,0xe8,0xf7,0x79,0x67,0xe1,0xdb,0xc9,0x9a,0xd2,0x1a,0x9e,0x61,0x40,0x81,0x94,0xfe,0xe9,0xd0,0xf3,0xb0,0x49,0x2a,0x1d,0x5,0xaf,0xa7,0xc2,0x51,0xed,0xc,0x86,0x56,0x53,0xd3,0x35,0x99,0xcb,0x63,0x3,0x19,0xe4,0x54,0x15,0x9c,0x6c,0x75,0xa5,0x52,0x43,0xd8,0xa,0x64,0x16,0x93,0xcf,0xe5,0x32,0x6b,0x4,0x80,0x1,0x2c,0x36,0x9b,0x3d,0xd9,0xa0,0x82,0xcc,0x14,0x39,0x4b,0x91,0x2b,0xa8,0xa3,0x37,0xf5,0x6,0x7,0xc6,0x30,0xf9,0x92,0x65,0x50,0x7b,0x83,0xc5,0x8e,0x38,0xeb,0xfd,0xb3,0x90,0x13,0xb,0x4c,0xda,0x34,0xec,0xb8,0xb8,0xd1,0x30,0x55,0x60,0xaa,0x7,0xac,0x0,0x8a,0x99,0xaf,0xed,0x98,0x23,0xd9,0x48,0xe9,0xba,0x3d,0xcf,0xc9,0x4c,0xdb,0x52,0xd0,0x6e,0x83,0x7f,0xa3,0xd8,0x32,0x7a,0xc6,0xfc,0x81,0xbf,0xd3,0x2b,0x1c,0x6c,0x53,0x78,0x29,0xce,0xc0,0x6,0xb0,0x35,0x3f,0xa2,0x59,0x7c,0x93,0x51,0x70,0x20,0xd2,0xc,0xc3,0x1e,0x11,0x76,0xbb,0x82,0xb3,0xa8,0xe4,0x54,0x7d,0xf5,0x28,0xff,0xe0,0xf8,0xf4,0xe8,0x71,0x46,0xec,0x57,0x2e,0x2c,0x21,0x27,0x1f,0x43,0xf1,0x1,0x49,0xfe,0x4a,0xda,0x79,0x73,0xc4,0x26,0xb7,0x72,0x66,0x5b,0xf2,0x96,0xb2,0x7e,0x34,0xa5,0x10,0xbc,0xb5,0x4f,0x74,0x91,0x84,0x61,0x3,0xa4,0x68,0xf6,0x31,0x44,0x87,0x40,0xa0,0x15,0x9b,0x19,0x86,0x5e,0x6b,0x9c,0xf7,0x3e,0xc8,0x9,0x8,0xfb,0x39,0xad,0xa6,0x25,0x9f,0x45,0x37,0xb6,0xe2,0x3a,0xd4,0x42,0x5,0x1d,0x9e,0xbd,0xf3,0xe5,0x36,0x80,0xcb,0x8d,0x75,0x9d,0x18,0x6a,0x4,0xd6,0x4d,0x5c,0xab,0x7b,0x62,0x92,0x1b,0x5a,0xea,0x17,0xd,0x1a,0xc2,0x8c,0xae,0xd7,0x33,0x95,0x38,0x22,0xf,0x8e,0xa,0x65,0x3c,0xeb,0xc1,0x24,0x47,0xbe,0xfd,0xde,0xe7,0xf0,0x9a,0x8f,0x4e,0x6f,0x90,0x14,0xdc,0x94,0xc7,0x6d,0xc5,0x97,0x3b,0xdd,0x5d,0x58,0x88,0x2,0xe3,0x5f,0xcc,0xa9,0xa1,0xb,0x13,0x50,0x41,0xe1,0x2f,0x16,0x12,0xfa,0x67,0xb1,0x63,0x89,0xb9,0x85,0xca,0xdf,0xa7,0xd5,0xef,0x69,0x77,0xf9,0xe6,0x8b,0x2d,0x4b,0x64,0xee,0xb4,0x56,0xe,0x2a,0xcd};
+
+unsigned char table_s3[] = {0xdd,0xaa,0x28,0xa0,0xc5,0xb4,0x9c,0xe8,0xcd,0xb7,0xde,0xe2,0x1f,0xef,0x73,0xbe,0x72,0xf5,0x1,0x22,0x50,0xa8,0x4a,0x9a,0xf2,0x84,0x69,0x29,0x3c,0x79,0x67,0xe3,0xb3,0x56,0xe4,0x2c,0x55,0x26,0x5a,0x0,0xb6,0x60,0xa9,0x98,0x83,0xc4,0xd0,0x7c,0xd9,0x90,0x78,0xdb,0x25,0x96,0xee,0x49,0x61,0xfa,0xd6,0x14,0xe,0xf8,0x41,0xce,0x9,0x37,0x53,0x12,0x93,0x3d,0xb1,0x4,0xf7,0x82,0x35,0x9f,0xb0,0xe9,0xf,0x1b,0xab,0xc7,0xa6,0x7e,0x77,0xd2,0x63,0xd1,0xd3,0xb8,0x8e,0x38,0xc,0x40,0xd4,0xe0,0xfe,0x21,0x2b,0x30,0x16,0xa7,0xd5,0xbc,0xbd,0xeb,0x5f,0x65,0xfc,0x4c,0x2e,0x64,0x6,0x7,0x5e,0x3b,0x2f,0xba,0x39,0x7d,0xd8,0x88,0xf3,0x58,0x15,0x23,0x86,0xae,0x74,0xca,0x8,0x66,0x43,0xa2,0xc8,0xda,0xd7,0x46,0x19,0xe5,0xc6,0xcb,0x3,0xf6,0x91,0xcc,0x3e,0x2d,0x8c,0xcf,0x71,0x7a,0x62,0xc9,0x59,0x11,0x1a,0xc3,0x45,0x75,0x8f,0x9e,0x97,0xdc,0x54,0x4d,0x6e,0x44,0x81,0x99,0xa5,0xb9,0x70,0x13,0x34,0x24,0x95,0x5,0x1d,0x6d,0x7f,0x5b,0x94,0xac,0x3f,0xad,0xbb,0x31,0x51,0xdf,0x4f,0xa1,0x6b,0x80,0xbf,0x92,0x18,0xec,0xa4,0x42,0x68,0xaf,0xc1,0xe7,0xed,0x7b,0xe1,0x10,0x5c,0xc2,0x27,0x85,0xa,0x87,0x8b,0x33,0x5d,0xfb,0x9d,0xb2,0xea,0xf4,0x6f,0xb,0x8a,0x8d,0xfd,0x47,0x20,0x2,0xd,0x17,0xf0,0x4b,0xb5,0x2a,0x36,0x4e,0x52,0xe6,0x6a,0x57,0x76,0x89,0x1e,0x32,0x48,0x3a,0xa3,0x1c,0x9b,0xc0,0x6c,0xf1,0xf9,0xff,0xd3,0xcd,0x2c,0x48,0xdc,0x7a,0x95,0xba,0xa0,0x2d,0x14,0xac,0xe5,0x7b,0xa2,0x0,0x5c,0xca,0x37,0xc6,0x88,0x4f,0xc0,0xe6,0xcb,0x3f,0x65,0x83,0xa7,0x4c,0xb5,0x98,0xd6,0x4b,0xd8,0xde,0x3b,0x84,0xe7,0xbc,0x15,0x39,0x1d,0x6f,0x70,0x4d,0xae,0x51,0x69,0x11,0xc1,0x75,0x6c,0xd7,0xd,0x92,0x25,0x7,0x30,0x2a,0xaa,0xad,0x60,0xda,0xe4,0x3d,0x52,0x62,0xee,0x45,0x36,0x7e,0xe8,0xab,0x5d,0x56,0xeb,0xb6,0xa,0x19,0xec,0xe1,0xd1,0x24,0x61,0xf0,0xc2,0x3e,0x85,0x64,0xfd,0xef,0xed,0x53,0x41,0x2f,0xf8,0x76,0x86,0x68,0x8a,0x18,0x16,0x9c,0x7c,0x58,0x8b,0xb3,0x22,0xb2,0x4a,0x3a,0x34,0x57,0x3,0x13,0xbe,0xa6,0x9e,0x82,0x6a,0x73,0x63,0x49,0xb9,0xa8,0xfb,0xb0,0x67,0x2b,0xc7,0xf3,0x9f,0xf4,0x1f,0xa9,0xf5,0x50,0xf6,0x44,0xe0,0x8c,0x59,0x81,0xce,0x97,0x3c,0x28,0xa5,0xd0,0xb8,0x12,0x1a,0xb4,0x23,0x96,0x10,0x2e,0x35,0x74,0x4,0x32,0x89,0xa1,0xaf,0xff,0x7f,0xd4,0x9d,0x8,0x5a,0x1e,0x20,0x21,0x1c,0x79,0x6b,0xdb,0x43,0x9,0xcc,0x9a,0x42,0x78,0x80,0x31,0x9b,0xf2,0x6,0xd9,0x17,0xc,0x5e,0x1b,0xc4,0x40,0xa3,0xd5,0xe,0x4e,0x8f,0x77,0xbd,0x6d,0xd2,0x55,0x5,0x26,0xc8,0x38,0x99,0x54,0x90,0xea,0xc5,0xf9,0x93,0xe2,0xcf,0xbb,0x8d,0xfa,0x87,0xf,0xdf,0x29,0xe9,0x66,0xdd,0x46,0x33,0xf1,0xb1,0x2,0x6e,0xc9,0xb7,0xfe,0xfc,0x5f,0xe3,0xa4,0x5b,0xf7,0x47,0x91,0xbf,0x8e,0x1,0x72,0x27,0x7d,0x71,0x94,0xb,0xc3,0xb8,0x2d,0x7f,0x3b,0x5,0x4,0x39,0x5c,0x21,0x17,0xac,0x84,0x8a,0xda,0x5a,0xf1,0xa5,0x14,0xbe,0xd7,0x23,0xfc,0x32,0x29,0x4e,0xfe,0x66,0x2c,0xe9,0xbf,0x67,0x5d,0xd0,0x75,0xd3,0x61,0xc5,0xa9,0x7c,0xa4,0x42,0xe,0xe2,0xd6,0xba,0xd1,0x3a,0x8c,0x3f,0x91,0x6,0xb3,0x35,0xb,0x10,0x51,0xeb,0xb2,0x19,0xd,0x80,0xf5,0x9d,0x37,0x94,0x27,0x4b,0xec,0x92,0xdb,0xd9,0x7a,0xfa,0xc,0xcc,0x43,0xf8,0x63,0x16,0xd4,0x24,0x57,0x2,0x58,0x54,0xb1,0x2e,0xe6,0xc6,0x81,0x7e,0xd2,0x62,0xb4,0x9a,0xab,0xaa,0x52,0x98,0x48,0xf7,0x70,0x20,0x3,0x7b,0x3e,0xe1,0x65,0x86,0xf0,0x2b,0x6b,0xb6,0xc7,0xea,0x9e,0xa8,0xdf,0xa2,0x2a,0xed,0x1d,0xbc,0x71,0xb5,0xcf,0xe0,0xdc,0x30,0x1c,0x38,0x4a,0x55,0x68,0x8b,0x74,0xf3,0x6e,0xfd,0xfb,0x1e,0xa1,0xc2,0x99,0x0,0x22,0x15,0xf,0x8f,0x88,0x45,0xff,0x4c,0x34,0xe4,0x50,0x49,0xf2,0x28,0xb7,0x85,0x8,0x31,0x89,0xc0,0x5e,0x87,0x25,0xf6,0xe8,0x9,0x6d,0xf9,0x5f,0xb0,0x9f,0xee,0x1a,0x40,0xa6,0x82,0x69,0x90,0xbd,0x79,0xef,0x12,0xe3,0xad,0x6a,0xe5,0xc3,0x59,0x7d,0xae,0x96,0x7,0x97,0x6f,0x1f,0xdd,0x53,0xa3,0x4d,0xaf,0x3d,0x33,0xb9,0x4f,0x56,0x46,0x6c,0x9c,0x8d,0xde,0x95,0x11,0x72,0x26,0x36,0x9b,0x83,0xbb,0xa7,0xcd,0x8e,0x78,0x73,0xce,0x93,0x2f,0x3c,0xc1,0x18,0x77,0x47,0xcb,0x60,0x13,0x5b,0xa0,0x41,0xd8,0xca,0xc8,0x76,0x64,0xa,0xc9,0xc4,0xf4,0x1,0x44,0xd5,0xe7,0x1b,0x3d,0xe5,0x84,0xe8,0x92,0x20,0x91,0x34,0x7b,0xcd,0xfb,0x90,0xa3,0x97,0x3,0x4f,0x51,0x10,0x74,0x4a,0x47,0xf2,0x7e,0xd0,0xdc,0x76,0xc1,0xb4,0x58,0x4c,0xaa,0xf3,0x78,0x1d,0x44,0x45,0x3e,0x7a,0xf9,0x6c,0x1b,0xb0,0xcb,0x9b,0xed,0xc5,0x60,0x56,0x73,0x68,0x62,0xbd,0xff,0x96,0xe4,0x55,0x26,0x1c,0xa8,0xfe,0x27,0x6d,0xf,0xbf,0x61,0x42,0xb6,0x31,0xd9,0x9,0xeb,0x13,0x6a,0x2a,0xc7,0xb1,0xa0,0x24,0x3a,0x7f,0xe3,0x6b,0xe9,0x9e,0xab,0xdf,0xf7,0x86,0xa1,0x9d,0xf4,0x8e,0xfd,0x30,0xac,0x5c,0x98,0x3b,0xd3,0x9a,0xa,0xad,0xd5,0x66,0x57,0x95,0xb9,0x22,0x8d,0x2,0xbb,0x4d,0x6f,0xa7,0x15,0xf0,0x43,0x19,0x65,0x16,0xdb,0xea,0x23,0xf5,0x3f,0x93,0x87,0xc0,0xc6,0x64,0x81,0x1f,0x70,0xc8,0xc4,0x49,0xf1,0xde,0xb8,0x1e,0x48,0x2c,0xb7,0xa9,0xd1,0xfc,0xc3,0x28,0x1,0xe7,0xaf,0x5b,0xa4,0x82,0xec,0x2b,0x53,0xa2,0x38,0xae,0xca,0x35,0x14,0x29,0x79,0xb,0x71,0x5d,0x83,0xd8,0x5f,0xe0,0xbc,0xba,0xb2,0x2f,0x4,0xbe,0xce,0xc9,0x54,0x4e,0x41,0x63,0x69,0xf6,0x8,0xb3,0xa5,0x11,0xd,0x75,0x6e,0x7d,0x8f,0xd2,0x39,0x32,0x8c,0xcf,0x52,0x1a,0x8a,0x21,0x36,0x6,0x80,0x59,0x25,0x4b,0x89,0x37,0x99,0x8b,0xe1,0x0,0xa6,0x5a,0x5,0x94,0xb5,0x40,0x88,0x85,0x2e,0x5e,0x46,0xd6,0xef,0xd7,0x18,0x3c,0x72,0xf8,0xee,0x7c,0xe2,0xc,0x9c,0x12,0x9f,0xd4,0xdd,0xcc,0x7,0x2d,0xe,0x17,0xfa,0xe6,0xda,0xc2,0x67,0x77,0x50,0x33,0x1f,0xd8,0xb6,0x90,0x9a,0xc,0x96,0x67,0x1c,0xf7,0xc8,0xe5,0x6f,0x9b,0xd3,0x35,0x2a,0x8c,0xea,0xc5,0x9d,0x83,0x18,0x7c,0x2b,0xb5,0x50,0xf2,0x7d,0xf0,0xfc,0x44,0x87,0x3c,0xc2,0x5d,0x41,0x39,0x25,0x91,0xfd,0xfa,0x8a,0x30,0x57,0x75,0x7a,0x60,0xd4,0x6b,0xec,0xb7,0x1b,0x86,0x8e,0x88,0x1d,0x20,0x1,0xfe,0x69,0x45,0x3f,0x4d,0xa0,0x31,0x6e,0x92,0xb1,0xbc,0x74,0x81,0x3,0xbd,0x7f,0x11,0x34,0xd5,0xbf,0xad,0x15,0xbe,0x2e,0x66,0x6d,0xb4,0x32,0x2,0xe6,0xbb,0x49,0x5a,0xfb,0xb8,0x6,0xd,0xf6,0xee,0xd2,0xce,0x7,0x64,0x43,0x53,0xf8,0xe9,0xe0,0xab,0x23,0x3a,0x19,0x33,0x48,0xda,0xcc,0x46,0x26,0xa8,0x38,0xd6,0xe2,0x72,0x6a,0x1a,0x8,0x2c,0xe3,0xdb,0x80,0xf5,0x42,0xe8,0xc7,0x9e,0x78,0x6c,0x7e,0x40,0x24,0x65,0xe4,0x4a,0xc6,0x73,0xa4,0xcf,0xf9,0x4f,0x7b,0x37,0xa3,0x97,0xdc,0xb0,0xd1,0x9,0x0,0xa5,0x14,0xa6,0xca,0x9c,0x28,0x12,0x8b,0x3b,0x59,0x13,0x89,0x56,0x5c,0x47,0x61,0xd0,0xa2,0xcb,0xaf,0xff,0x84,0x2f,0x62,0x54,0xf1,0xd9,0x71,0x70,0x29,0x4c,0x58,0xcd,0x4e,0xa,0xba,0xc0,0xa9,0x95,0x68,0x98,0x4,0xc9,0xaa,0xdd,0x5f,0xd7,0xb2,0xc3,0xeb,0x9f,0x85,0xf3,0x1e,0x5e,0x4b,0xe,0x10,0x94,0x5,0x82,0x76,0x55,0x27,0xdf,0x3d,0xed,0xc1,0x17,0xde,0xef,0xf4,0xb3,0xa7,0xb,0xc4,0x21,0x93,0x5b,0x22,0x51,0x2d,0x77,0x16,0x8d,0xa1,0x63,0x79,0x8f,0x36,0xb9,0xae,0xe7,0xf,0xac,0x52,0xe1,0x99,0x3e,0xdb,0x80,0xe3,0x5c,0xb9,0xbf,0x2c,0xb1,0x36,0xc9,0x2a,0x17,0x8,0x7a,0x5e,0x72,0xf5,0x6a,0xb0,0xb,0x12,0xa6,0x76,0xe,0xbd,0x7,0xca,0xcd,0x4d,0x57,0x60,0x42,0xdd,0xf2,0x1d,0xbb,0x2f,0x4b,0xaa,0xb4,0x67,0xc5,0x1c,0x82,0xcb,0x73,0x4a,0xc7,0x81,0xa7,0x28,0xef,0xa1,0x50,0xad,0x3b,0xff,0xd2,0x2b,0xc0,0xe4,0x2,0x58,0xac,0xfb,0x71,0x7f,0xed,0xf,0xe1,0x11,0x9f,0x5d,0x2d,0xd5,0x45,0xd4,0xec,0x3f,0x1b,0xe5,0xf9,0xc1,0xd9,0x74,0x64,0x30,0x53,0xd7,0x9c,0xcf,0xde,0x2e,0x4,0x14,0xd,0x19,0x51,0x22,0x89,0x5,0x35,0x5a,0x83,0x7e,0x6d,0xd1,0x8c,0x31,0x3a,0xcc,0x8f,0x59,0xa5,0x97,0x6,0x43,0xb6,0x86,0x8b,0x48,0x26,0x34,0x8a,0x88,0x9a,0x3,0xe2,0xb3,0x18,0x98,0xc8,0xc6,0xee,0x55,0x63,0x1e,0x7b,0x46,0x47,0x79,0x3d,0x6f,0xfa,0x1f,0x25,0xfd,0xab,0x6e,0x24,0xbc,0xc,0x6b,0x70,0xbe,0x61,0x95,0xfc,0x56,0xe7,0xce,0x78,0x93,0xf8,0x94,0xa0,0x4c,0x0,0xe6,0x3e,0xeb,0x87,0x23,0x91,0x37,0x92,0x75,0xdf,0xb7,0xc2,0x4f,0x5b,0xf0,0xa9,0x13,0x52,0x49,0x77,0xf1,0x44,0xd3,0x7d,0x96,0x54,0x21,0xba,0x1,0x8e,0x4e,0xb8,0x38,0x9b,0x99,0xd0,0xae,0x9,0x65,0xd6,0xe9,0xd8,0xf6,0x20,0x90,0x3c,0xc3,0x84,0xa4,0x6c,0xf3,0x16,0x1a,0x40,0x15,0x66,0x29,0x69,0xb2,0xc4,0x27,0xa3,0x7c,0x39,0x41,0x62,0x32,0xb5,0xa,0xda,0x10,0xe8,0x9e,0xa2,0x8d,0xf7,0x33,0xfe,0x5f,0xaf,0x68,0xe0,0x9d,0xea,0xdc,0xa8,0x85,0xf4,0xa4,0x66,0x4a,0xd1,0x7e,0xf1,0x48,0xbe,0x6b,0xc8,0x20,0x69,0xf9,0x5e,0x26,0x95,0x28,0x19,0xd0,0x6,0xcc,0x60,0x74,0x33,0x9c,0x54,0xe6,0x3,0xb0,0xea,0x96,0xe5,0x99,0xd9,0x34,0x42,0x53,0xd7,0xc9,0x8c,0x92,0xb1,0x45,0xc2,0x2a,0xfa,0x18,0xe0,0x52,0x6e,0x7,0x7d,0xe,0xc3,0x5f,0xaf,0x10,0x98,0x1a,0x6d,0x58,0x2c,0x4,0x75,0xe8,0x43,0x38,0x68,0x1e,0x36,0x93,0xa5,0x8b,0xee,0xb7,0xb6,0xcd,0x89,0xa,0x9f,0xd5,0xef,0x5b,0xd,0xd4,0x9e,0xfc,0x4c,0x80,0x9b,0x91,0x4e,0xc,0x65,0x17,0xa6,0x88,0x3e,0x8,0x63,0x50,0x64,0xf0,0xbc,0xce,0x16,0x77,0x1b,0x61,0xd3,0x62,0xc7,0x2f,0x85,0x32,0x47,0xab,0xbf,0x59,0x0,0xa2,0xe3,0x87,0xb9,0xb4,0x1,0x8d,0x23,0x81,0xb,0x1d,0x8f,0x11,0xff,0x6f,0xe1,0xdd,0xad,0xb5,0x25,0x1c,0x24,0xeb,0xcf,0x9,0x15,0x29,0x31,0x94,0x84,0xa3,0xc0,0x6c,0x27,0x2e,0x3f,0xf4,0xde,0xfd,0xe4,0xa1,0xe9,0x79,0xd2,0xc5,0xf5,0x73,0xaa,0x9d,0x8e,0x7c,0x21,0xca,0xc1,0x7f,0x3c,0x55,0xa9,0xf6,0x67,0x46,0xb3,0x7b,0x76,0xd6,0xb8,0x7a,0xc4,0x6a,0x78,0x12,0xf3,0x70,0x2b,0xac,0x13,0x4f,0x49,0x41,0xdc,0x39,0xc6,0xe7,0xda,0x8a,0xf8,0x82,0xae,0x9a,0x5,0xfb,0x40,0x56,0xe2,0xfe,0x86,0xf7,0x4d,0x3d,0x3a,0xa7,0xbd,0xb2,0x90,0x2,0x2d,0x4b,0xed,0xbb,0xdf,0x44,0x5a,0x35,0x97,0x72,0xec,0x83,0x3b,0x37,0xba,0x57,0x71,0x1f,0xd8,0xa0,0x51,0xcb,0x5d,0x22,0xf,0x30,0xdb,0xf2,0x14,0x5c,0xa8,0x5,0xe3,0xab,0x5f,0xd5,0xf8,0xc7,0x2c,0x57,0xa6,0x3c,0xaa,0xa0,0x86,0xe8,0x2f,0x74,0xcc,0xc0,0x4d,0xc2,0x60,0x85,0x1b,0x4c,0x28,0xb3,0xad,0xf5,0xda,0xbc,0x1a,0x50,0x4a,0x45,0x67,0x0,0xba,0xca,0xcd,0xa1,0x15,0x9,0x71,0x6d,0xf2,0xc,0xb7,0x7d,0xf,0x75,0x59,0xce,0x31,0x10,0x2d,0xb8,0xbe,0xb6,0x2b,0x87,0xdc,0x5b,0xe4,0x9d,0x8f,0xe5,0x4,0x21,0x4f,0x8d,0x33,0xb1,0x44,0x8c,0x81,0xa2,0x5e,0x1,0x90,0x3d,0x36,0x88,0xcb,0x6a,0x79,0x8b,0xd6,0x32,0x2,0x84,0x5d,0x56,0x1e,0x8e,0x25,0x3,0x29,0xa,0x13,0x9b,0xd0,0xd9,0xc8,0x63,0x73,0x54,0x37,0xfe,0xe2,0xde,0xc6,0xeb,0xd3,0x1c,0x38,0x2a,0x5a,0x42,0xd2,0xe6,0x8,0x98,0x16,0x76,0xfc,0xea,0x78,0x43,0xf6,0x7a,0xd4,0x55,0x14,0x70,0x4e,0x5c,0x48,0xae,0xf7,0xd8,0x72,0xc5,0xb0,0x96,0x24,0x95,0x30,0x39,0xe1,0x80,0xec,0xa7,0x93,0x7,0x4b,0x7f,0xc9,0xff,0x94,0xfb,0x92,0xe0,0x51,0x77,0x6c,0x66,0xb9,0x23,0x69,0xb,0xbb,0x22,0x18,0xac,0xfa,0x3a,0x7e,0xfd,0x68,0x7c,0x19,0x40,0x41,0xe9,0xc1,0x64,0x52,0x1f,0xb4,0xcf,0x9f,0xaf,0xdb,0xf3,0x82,0xe7,0x6f,0xed,0x9a,0xf9,0x34,0xa8,0x58,0xa5,0x99,0xf0,0x8a,0xdd,0xd,0xef,0x17,0x65,0x46,0xb2,0x35,0xa4,0x20,0x3e,0x7b,0x6e,0x2e,0xc3,0xb5,0x47,0x1d,0x61,0x12,0x6b,0xa3,0x11,0xf4,0x3b,0x97,0x83,0xc4,0xdf,0xee,0x27,0xf1,0xe,0xa9,0xd1,0x62,0x9c,0x3f,0xd7,0x9e,0x89,0x6,0xbf,0x49,0x53,0x91,0xbd,0x26,0x86,0xf3,0x44,0xee,0xc1,0x98,0x7e,0x6a,0x78,0x46,0x22,0x63,0xe2,0x4c,0xc0,0x75,0xa2,0xc9,0xff,0x49,0x7d,0x31,0xa5,0x91,0xda,0xb6,0xd7,0xf,0x6,0xa3,0x12,0xa0,0xcc,0x9a,0x2e,0x14,0x8d,0x3d,0x5f,0x15,0x8f,0x50,0x5a,0x41,0x67,0xd6,0xa4,0xcd,0xa9,0xf9,0x82,0x29,0x64,0x52,0xf7,0xdf,0x77,0x76,0x2f,0x4a,0x5e,0xcb,0x48,0xc,0xbc,0xc6,0xaf,0x93,0x6e,0x9e,0x2,0xcf,0xac,0xdb,0x59,0xd1,0xb4,0xc5,0xed,0x99,0x83,0xf5,0x18,0x58,0x4d,0x8,0x16,0x92,0x3,0x84,0x70,0x53,0x21,0xd9,0x3b,0xeb,0xc7,0x11,0xd8,0xe9,0xf2,0xb5,0xa1,0xd,0xc2,0x27,0x95,0x5d,0x24,0x57,0x2b,0x71,0x10,0x8b,0xa7,0x65,0x7f,0x89,0x30,0xbf,0xa8,0xe1,0x9,0xaa,0x54,0xe7,0x9f,0x38,0x19,0xde,0xb0,0x96,0x9c,0xa,0x90,0x61,0x1a,0xf1,0xce,0xe3,0x69,0x9d,0xd5,0x33,0x2c,0x8a,0xec,0xc3,0x9b,0x85,0x1e,0x7a,0x2d,0xb3,0x56,0xf4,0x7b,0xf6,0xfa,0x42,0x81,0x3a,0xc4,0x5b,0x47,0x3f,0x23,0x97,0xfb,0xfc,0x8c,0x36,0x51,0x73,0x7c,0x66,0xd2,0x6d,0xea,0xb1,0x1d,0x80,0x88,0x8e,0x1b,0x26,0x7,0xf8,0x6f,0x43,0x39,0x4b,0xa6,0x37,0x68,0x94,0xb7,0xba,0x72,0x87,0x5,0xbb,0x79,0x17,0x32,0xd3,0xb9,0xab,0x13,0xb8,0x28,0x60,0x6b,0xb2,0x34,0x4,0xe0,0xbd,0x4f,0x5c,0xfd,0xbe,0x0,0xb,0xf0,0xe8,0xd4,0xc8,0x1,0x62,0x45,0x55,0xfe,0xef,0xe6,0xad,0x25,0x3c,0x1f,0x35,0x4e,0xdc,0xca,0x40,0x20,0xae,0x3e,0xd0,0xe4,0x74,0x6c,0x1c,0xe,0x2a,0xe5,0xdd,0x20,0xa8,0xd5,0xa2,0x94,0xe0,0xcd,0xbc,0xd6,0xea,0xc5,0xbf,0x7b,0xb6,0x17,0xe7,0x9,0x2a,0x7a,0xfd,0x42,0x92,0x58,0xa0,0x61,0x21,0xfa,0x8c,0x6f,0xeb,0x34,0x71,0xec,0x24,0xbb,0x5e,0x52,0x8,0x5d,0x2e,0xa1,0x90,0xbe,0x68,0xd8,0x74,0x8b,0xcc,0x70,0xd3,0xd1,0x98,0xe6,0x41,0x2d,0x9e,0xde,0x1c,0x69,0xf2,0x49,0xc6,0x6,0xf0,0x5b,0x1a,0x1,0x3f,0xb9,0xc,0x9b,0x35,0x3d,0x97,0xff,0x8a,0x7,0x13,0xb8,0xe1,0xae,0x76,0xa3,0xcf,0x6b,0xd9,0x7f,0xda,0x86,0x30,0xdb,0xb0,0xdc,0xe8,0x4,0x48,0x23,0x38,0xf6,0x29,0xdd,0xb4,0x1e,0xaf,0x57,0x6d,0xb5,0xe3,0x26,0x6c,0xf4,0x44,0x56,0x33,0xe,0xf,0x31,0x75,0x27,0xb2,0xfb,0x50,0xd0,0x80,0x8e,0xa6,0x1d,0x2b,0x0,0x6e,0x7c,0xc2,0xc0,0xd2,0x4b,0xaa,0x11,0xed,0xdf,0x4e,0xb,0xfe,0xce,0xc3,0x36,0x25,0x99,0xc4,0x79,0x72,0x84,0xc7,0x51,0x19,0x6a,0xc1,0x4d,0x7d,0x12,0xcb,0x9f,0xd4,0x87,0x96,0x66,0x4c,0x5c,0x45,0xad,0xb1,0x89,0x91,0x3c,0x2c,0x78,0x1b,0x15,0x65,0x9d,0xd,0x9c,0xa4,0x77,0x53,0xb3,0x39,0x37,0xa5,0x47,0xa9,0x59,0xd7,0xb7,0x9a,0x63,0x88,0xac,0x4a,0x10,0xe4,0xc9,0xef,0x60,0xa7,0xe9,0x18,0xe5,0x73,0x2f,0x8d,0x54,0xca,0x83,0x3b,0x2,0x8f,0x95,0xba,0x55,0xf3,0x67,0x3,0xe2,0xfc,0xf5,0x4f,0x82,0x85,0x5,0x1f,0x28,0xa,0xbd,0x22,0xf8,0x43,0x5a,0xee,0x3e,0x46,0x7e,0x81,0x62,0x5f,0x40,0x32,0x16,0x3a,0x93,0xc8,0xab,0x14,0xf1,0xf7,0x64,0xf9,0x4f,0xd0,0xa,0xb1,0xa8,0x1c,0xcc,0xb4,0x7,0xbd,0x70,0x77,0xf7,0xed,0xda,0xf8,0x61,0x3a,0x59,0xe6,0x3,0x5,0x96,0xb,0x8c,0x73,0x90,0xad,0xb2,0xc0,0xe4,0xc8,0x3b,0x1d,0x92,0x55,0x1b,0xea,0x17,0x81,0x45,0x68,0x91,0x7a,0x5e,0xb8,0xe2,0x16,0x67,0x48,0xa7,0x1,0x95,0xf1,0x10,0xe,0xdd,0x7f,0xa6,0x38,0x71,0xc9,0xf0,0x7d,0x5f,0x43,0x7b,0x63,0xce,0xde,0x8a,0xe9,0x6d,0x26,0x75,0x64,0x94,0xbe,0xae,0xb7,0x41,0xcb,0xc5,0x57,0xb5,0x5b,0xab,0x25,0xe7,0x97,0x6f,0xff,0x6e,0x56,0x85,0xa1,0xe3,0x1f,0x2d,0xbc,0xf9,0xc,0x3c,0x31,0xf2,0x9c,0x8e,0x30,0x32,0x20,0xb9,0x58,0xa3,0xeb,0x98,0x33,0xbf,0x8f,0xe0,0x39,0xc4,0xd7,0x6b,0x36,0x8b,0x80,0x76,0x35,0xa5,0x9f,0x47,0x11,0xd4,0x9e,0x6,0xb6,0xd1,0xca,0x4,0xdb,0x2f,0x46,0xec,0x5d,0x9,0xa2,0x22,0x72,0x7c,0x54,0xef,0xd9,0xa4,0xc1,0xfc,0xfd,0xc3,0x87,0xd5,0x40,0xcf,0x65,0xd,0x78,0xf5,0xe1,0x4a,0x13,0xa9,0xe8,0xf3,0xcd,0x4b,0xfe,0x69,0xc7,0x74,0xc2,0x29,0x42,0x2e,0x1a,0xf6,0xba,0x5c,0x84,0x51,0x3d,0x99,0x2b,0x8d,0x28,0x53,0x62,0x4c,0x9a,0x2a,0x86,0x79,0x3e,0x1e,0xd6,0x49,0xac,0xa0,0xfa,0xaf,0xdc,0x2c,0xee,0x9b,0x0,0xbb,0x34,0xf4,0x2,0x82,0x21,0x23,0x6a,0x14,0xb3,0xdf,0x6c,0x24,0x18,0x37,0x4d,0x89,0x44,0xe5,0x15,0xd2,0x5a,0x27,0x50,0x66,0x12,0x3f,0x4e,0x93,0xd3,0x8,0x7e,0x9d,0x19,0xc6,0x83,0xfb,0xd8,0x88,0xf,0xb0,0x60,0xaa,0x52,0x8,0xb2,0xc2,0xc5,0x58,0x42,0x4d,0x6f,0x65,0xfa,0x4,0xbf,0xa9,0x1d,0x1,0x79,0xc6,0x39,0x18,0x25,0x75,0x7,0x7d,0x51,0x8f,0xd4,0x53,0xec,0xb0,0xb6,0xbe,0x23,0xdd,0xf0,0xcf,0x24,0xd,0xeb,0xa3,0x57,0xa8,0x8e,0xe0,0x27,0x5f,0xae,0x34,0xa2,0xca,0x68,0x8d,0x13,0x7c,0xc4,0xc8,0x45,0xfd,0xd2,0xb4,0x12,0x44,0x20,0xbb,0xa5,0x93,0xd8,0xd1,0xc0,0xb,0x21,0x2,0x1b,0xf6,0xea,0xd6,0xce,0x6b,0x7b,0x5c,0x3f,0x22,0x52,0x4a,0xda,0xe3,0xdb,0x14,0x30,0x7e,0xf4,0xe2,0x70,0xee,0x0,0x90,0x1e,0x29,0x47,0x85,0x3b,0x95,0x87,0xed,0xc,0xaa,0x56,0x9,0x98,0xb9,0x4c,0x84,0x89,0x62,0x71,0x83,0xde,0x35,0x3e,0x80,0xc3,0x5e,0x16,0x86,0x2d,0x3a,0xa,0x8c,0x55,0x7f,0x64,0x6e,0xb1,0xf3,0x9a,0xe8,0x59,0x2a,0x10,0xa4,0xf2,0x2b,0x61,0x3,0xb3,0x74,0x11,0x48,0x49,0x32,0x76,0xf5,0x60,0x17,0xbc,0xc7,0x97,0xe1,0xc9,0x6c,0x5a,0x5d,0x1c,0x78,0x46,0x4b,0xfe,0x72,0xdc,0xd0,0x7a,0xcd,0xb8,0x54,0x40,0xa6,0xff,0x31,0xe9,0x88,0xe4,0x9e,0x2c,0x9d,0x38,0x77,0xc1,0xf7,0x9c,0xaf,0x9b,0xf,0x43,0x63,0xab,0x19,0xfc,0x4f,0x15,0x69,0x1a,0xd7,0xe6,0x2f,0xf9,0x33,0x9f,0x8b,0xcc,0x94,0x37,0xdf,0x96,0x6,0xa1,0xd9,0x6a,0x5b,0x99,0xb5,0x2e,0x81,0xe,0xb7,0x41,0xef,0x67,0xe5,0x92,0xa7,0xd3,0xfb,0x8a,0xad,0x91,0xf8,0x82,0xf1,0x3c,0xa0,0x50,0x6d,0x4e,0xba,0x3d,0xd5,0x5,0xe7,0x1f,0x66,0x26,0xcb,0xbd,0xac,0x28,0x36,0x73,0x61,0xdb,0x16,0x11,0x91,0x8b,0xbc,0x9e,0x29,0xb6,0x6c,0xd7,0xce,0x7a,0xaa,0xd2,0xea,0x15,0xf6,0xcb,0xd4,0xa6,0x82,0xae,0x7,0x5c,0x3f,0x80,0x65,0x63,0xf0,0x6d,0x23,0xe,0xf7,0x1c,0x38,0xde,0x84,0x70,0x5d,0x7b,0xf4,0x33,0x7d,0x8c,0x71,0xe7,0xbb,0x19,0xc0,0x5e,0x17,0xaf,0x96,0x1b,0x1,0x2e,0xc1,0x67,0xf3,0x97,0x76,0x68,0xb,0x40,0x13,0x2,0xf2,0xd8,0xc8,0xd1,0x39,0x25,0x1d,0x5,0xa8,0xb8,0xec,0x8f,0x81,0xf1,0x9,0x99,0x8,0x30,0xe3,0xc7,0x27,0xad,0xa3,0x31,0xd3,0x3d,0xcd,0x43,0x94,0xfa,0xe8,0x56,0x54,0x46,0xdf,0x3e,0x85,0x79,0x4b,0xda,0x9f,0x6a,0x5a,0x57,0xa2,0xb1,0xd,0x50,0xed,0xe6,0x10,0x53,0xc5,0x8d,0xfe,0x55,0xd9,0xe9,0x86,0x5f,0xb7,0xac,0x62,0xbd,0x49,0x20,0x8a,0x3b,0xc3,0xf9,0x21,0x77,0xb2,0xf8,0x60,0xd0,0xc2,0xa7,0x9a,0x9b,0xa5,0xe1,0xb3,0x26,0x6f,0xc4,0x44,0x14,0x1a,0x32,0x89,0xbf,0xcf,0x8e,0x95,0xab,0x2d,0x98,0xf,0xa1,0xa9,0x3,0x6b,0x1e,0x93,0x87,0x2c,0x75,0x3a,0xe2,0x37,0x5b,0xff,0x4d,0xeb,0x4e,0x12,0xa4,0x4f,0x24,0x48,0x7c,0x90,0xdc,0x78,0xb0,0x2f,0xca,0xc6,0x9c,0xc9,0xba,0x35,0x4,0x2a,0xfc,0x4c,0xe0,0x1f,0x58,0xe4,0x47,0x45,0xc,0x72,0xd5,0xb9,0xa,0x4a,0x88,0xfd,0x66,0xdd,0x52,0x92,0x64,0xb4,0x3c,0x41,0x36,0x0,0x74,0x59,0x28,0x42,0x7e,0x51,0x2b,0xef,0x22,0x83,0x73,0x9d,0xbe,0xee,0x69,0xd6,0x6,0xcc,0x34,0xf5,0xb5,0x6e,0x18,0xfb,0x7f,0xa0,0xe5,0x68,0xc6,0x4a,0xff,0xf2,0xcc,0xa8,0xe9,0x4b,0x12,0xf4,0xe0,0xc,0x79,0xce,0x64,0x8c,0x29,0x98,0x2a,0x50,0x3c,0x5d,0x85,0xf7,0xbb,0x2f,0x1b,0x28,0x43,0x75,0xc3,0xed,0x5c,0x2e,0x47,0x5,0xda,0xd0,0xcb,0x7,0xb7,0xd5,0x9f,0x46,0x10,0xa4,0x9e,0xd4,0x41,0xc2,0x86,0xfd,0xfc,0xa5,0xc0,0xee,0xd8,0x7d,0x55,0x23,0x73,0x8,0xa3,0x3e,0x4f,0x67,0x13,0x26,0x51,0xd3,0x5b,0xe4,0x14,0x88,0x45,0x36,0x4c,0x25,0x19,0xab,0x53,0xb1,0x61,0x89,0xe,0xfa,0xd9,0xc7,0x82,0x9c,0x18,0x9,0x7f,0x92,0xd2,0xae,0xdd,0xa1,0xfb,0x48,0xad,0x1f,0xd7,0x78,0x3f,0x2b,0x87,0x4d,0x9b,0x52,0x63,0xde,0x6d,0x15,0xb2,0x22,0x6b,0x83,0x20,0xf5,0x3,0xba,0x35,0x9a,0x1,0x2d,0xef,0xe3,0x17,0x5f,0xb9,0x90,0x7b,0x44,0x69,0x16,0x80,0x1a,0xeb,0x93,0x54,0x3a,0x1c,0xf1,0x7c,0x70,0xc8,0xa7,0x39,0xdc,0x7e,0x11,0xf,0x94,0xf0,0xa6,0x0,0x66,0x49,0xdb,0xf9,0xf6,0xec,0x71,0x76,0x6,0xbc,0xcd,0xb5,0xa9,0x1d,0xb,0xb0,0x4e,0xd1,0xe5,0xc9,0xb3,0xc1,0x91,0xac,0x8d,0x72,0x97,0xa,0x2,0x4,0x58,0xe7,0x60,0x3b,0xb8,0x59,0x33,0x21,0x8f,0x31,0xf3,0x9d,0x3d,0x30,0xf8,0xd,0x2c,0xbd,0xe2,0x1e,0x77,0x34,0x8a,0x81,0x6a,0x37,0xc5,0xd6,0xe1,0x38,0xbe,0x8e,0x99,0x32,0xa2,0xea,0xaf,0xb6,0x95,0xbf,0x74,0x65,0x6c,0x27,0x8b,0xe8,0xcf,0xdf,0x7a,0x62,0x5e,0x42,0x84,0xa0,0x6f,0x57,0x6e,0xfe,0xe6,0x96,0xaa,0x24,0xb4,0x5a,0xc4,0x56,0x40,0xca,0x50,0xc5,0x46,0x2,0x79,0x78,0x21,0x44,0x6a,0x5c,0xf9,0xd1,0xa7,0xf7,0x8c,0x27,0x69,0xd8,0xaa,0xc3,0x81,0x5e,0x54,0x4f,0x83,0x33,0x51,0x1b,0xc2,0x94,0x20,0x1a,0x8,0xad,0x1c,0xae,0xd4,0xb8,0xd9,0x1,0x73,0x3f,0xab,0x9f,0xac,0xc7,0xf1,0x47,0xec,0x42,0xce,0x7b,0x76,0x48,0x2c,0x6d,0xcf,0x96,0x70,0x64,0x88,0xfd,0x4a,0xe0,0x5a,0xe9,0x91,0x36,0xa6,0xef,0x7,0xa4,0x71,0x87,0x3e,0xb1,0x1e,0x85,0xa9,0x6b,0x2a,0x59,0x25,0x7f,0xcc,0x29,0x9b,0x53,0xfc,0xbb,0xaf,0x3,0xc9,0x1f,0xd6,0xe7,0x2f,0xd7,0x35,0xe5,0xd,0x8a,0x7e,0x5d,0x43,0x6,0x18,0x9c,0x8d,0xfb,0x16,0x56,0xba,0xcb,0xe3,0x97,0xa2,0xd5,0x57,0xdf,0x60,0x90,0xc,0xc1,0xb2,0xc8,0xa1,0x9d,0x61,0x4d,0x37,0x45,0x15,0x28,0x9,0xf6,0x13,0x8e,0x86,0x80,0xdc,0x63,0xe4,0xbf,0x5f,0x7d,0x72,0x68,0xf5,0xf2,0x82,0x38,0x49,0x31,0x2d,0x99,0x8f,0x34,0xca,0x55,0x75,0xf8,0xf4,0x4c,0x23,0xbd,0x58,0xfa,0x95,0x8b,0x10,0x74,0x22,0x84,0xe2,0xcd,0x67,0x93,0xdb,0x3d,0x14,0xff,0xc0,0xed,0x92,0x4,0x9e,0x6f,0x17,0xd0,0xbe,0x98,0x0,0x24,0xeb,0xd3,0xea,0x7a,0x62,0x12,0x2e,0xa0,0x30,0xde,0x40,0xd2,0xc4,0x4e,0x2b,0x32,0x11,0x3b,0xf0,0xe1,0xe8,0xa3,0xf,0x6c,0x4b,0x5b,0xfe,0xe6,0xda,0xc6,0xf3,0xb0,0xe,0x5,0xee,0xb3,0x41,0x52,0x65,0xbc,0x3a,0xa,0x1d,0xb6,0x26,0x6e,0x3c,0xdd,0xb7,0xa5,0xb,0xb5,0x77,0x19,0xb9,0xb4,0x7c,0x89,0xa8,0x39,0x66,0x9a,0x55,0x44,0x4d,0x6,0x8e,0x97,0xb4,0x9e,0x5b,0x43,0x7f,0x63,0xaa,0xc9,0xee,0xfe,0x4f,0xdf,0xc7,0xb7,0xa5,0x81,0x4e,0x76,0xe5,0x77,0x61,0xeb,0x8b,0x5,0x95,0x7b,0xae,0x10,0xd2,0xbc,0x99,0x78,0x12,0x0,0xd,0x9c,0xc3,0x3f,0x1c,0x11,0xd9,0x2c,0x4b,0x16,0xe4,0xf7,0x56,0x15,0xab,0xa0,0xb8,0x13,0x83,0xcb,0xc0,0x19,0x9f,0xaf,0x50,0x57,0x27,0x9d,0xfa,0xd8,0xd7,0xcd,0x2a,0x91,0x6f,0xf0,0xec,0x94,0x88,0x3c,0xb0,0x8d,0xac,0x53,0xc4,0xe8,0x92,0xe0,0x79,0xc6,0x41,0x1a,0xb6,0x2b,0x23,0x25,0xb1,0x5a,0x65,0x48,0xc2,0x36,0x7e,0x98,0xb2,0x75,0x1b,0x3d,0x37,0xa1,0x3b,0xca,0x86,0x18,0xfd,0x5f,0xd0,0x5d,0x51,0xe9,0x87,0x21,0x47,0x68,0x30,0x2e,0xb5,0xd1,0x69,0x8c,0x3e,0xf6,0x8f,0xfc,0x80,0xda,0x6c,0xba,0x73,0x42,0x59,0x1e,0xa,0xa6,0x3,0x4a,0xa2,0x1,0xff,0x4c,0x34,0x93,0xbb,0x20,0xc,0xce,0xd4,0x22,0x9b,0x14,0x7,0x70,0xf2,0x7a,0x1f,0x6e,0x46,0x32,0x17,0x6d,0x4,0x38,0xc5,0x35,0xa9,0x64,0xa8,0x2f,0xdb,0xf8,0x8a,0x72,0x90,0x40,0x28,0x5e,0xb3,0xf3,0xe6,0xa3,0xbd,0x39,0x24,0xfb,0xf1,0xea,0xcc,0x7d,0xf,0x66,0x67,0x31,0x85,0xbf,0x26,0x96,0xf4,0xbe,0xdc,0xdd,0x84,0xe1,0xf5,0x60,0xe3,0xa7,0x2,0x52,0x29,0x82,0xcf,0xf9,0x5c,0x74,0xd3,0xed,0x89,0xc8,0x49,0xe7,0x6b,0xde,0x2d,0x58,0xef,0x45,0x6a,0x33,0xd5,0xc1,0x71,0x1d,0x7c,0xa4,0xad,0x8,0xb9,0xb,0x9,0x62,0x54,0xe2,0xd6,0x9a,0xe,0x3a};
+
+unsigned char table_s4[] = {0x5b,0x9c,0x13,0x35,0x8f,0x19,0xe4,0x15,0x74,0x9f,0x66,0x4b,0x18,0xec,0xb6,0x50,0xf,0xa9,0x46,0x69,0x0,0x1e,0xff,0x9b,0x36,0xa8,0x71,0xd3,0x73,0xfe,0xc7,0x7f,0xbf,0x4,0xde,0x41,0xba,0xc2,0x12,0xa6,0x79,0x7e,0xb3,0x9,0xf6,0xd4,0xe3,0xf9,0xe8,0x57,0x34,0x6f,0x5,0x98,0xb,0xd,0xa3,0x9e,0x7d,0x82,0xc6,0xea,0xce,0xbc,0xb2,0x23,0x11,0xed,0x3f,0x32,0x2,0xf7,0x3e,0x80,0x92,0xfc,0x56,0xb7,0x2e,0x3c,0x3d,0x96,0xe5,0xad,0x37,0xee,0x81,0xb1,0x38,0x65,0xd9,0xca,0x3b,0x78,0x8e,0x85,0x6d,0x75,0x4d,0x51,0xe7,0x84,0xd0,0xc0,0x6a,0x7b,0x28,0x63,0xb9,0xa0,0xb0,0x9a,0x59,0xcb,0xc5,0x4f,0x2b,0xa5,0x55,0xbb,0xf1,0x61,0x99,0xe9,0xaf,0x8b,0x58,0x60,0x76,0x3,0x6b,0xc1,0x1d,0x44,0xef,0xfb,0xc3,0xfd,0xe6,0xa7,0xc9,0x67,0xf0,0x45,0x4c,0x27,0xcc,0x7a,0xb4,0xf8,0x14,0x20,0x33,0x5f,0x8a,0x52,0x26,0x83,0x25,0x97,0x1f,0x49,0x91,0xab,0xb8,0x8,0x90,0xda,0xd5,0xa,0xc4,0xdf,0x53,0xe2,0x48,0x21,0x7c,0x2c,0xac,0x7,0xd7,0xe1,0x5a,0x72,0xf3,0xf2,0xcf,0xaa,0x4e,0xdb,0x89,0xcd,0x43,0x39,0x16,0x2a,0x1b,0xeb,0x4a,0x87,0x5e,0x29,0x54,0xdc,0x40,0x31,0x1c,0x68,0x70,0x6,0xdd,0x9d,0x8d,0xc8,0x17,0x93,0x1,0x86,0xd6,0xf5,0x5c,0xa4,0x6e,0xbe,0x94,0x42,0x6c,0x5d,0x30,0x77,0x88,0x24,0xa2,0x47,0xd8,0x10,0xd2,0xa1,0xf4,0xae,0xe,0x95,0xe0,0x22,0xc,0xfa,0x3a,0xb5,0x64,0x2d,0x2f,0x8c,0x62,0xd1,0xbd,0x1a,0x91,0x37,0xd8,0xf7,0x9e,0x80,0x61,0x5,0xa8,0x36,0xef,0x4d,0xed,0x60,0x59,0xe1,0xc5,0x2,0x8d,0xab,0x11,0x87,0x7a,0x8b,0xea,0x1,0xf8,0xd5,0x86,0x72,0x28,0xce,0x76,0xc9,0xaa,0xf1,0x9b,0x6,0x95,0x93,0x3d,0x0,0xe3,0x1c,0x58,0x74,0x50,0x22,0x21,0x9a,0x40,0xdf,0x24,0x5c,0x8c,0x38,0xe7,0xe0,0x2d,0x97,0x68,0x4a,0x7d,0x67,0xa3,0x8,0x7b,0x33,0xa9,0x70,0x1f,0x2f,0xa6,0xfb,0x47,0x54,0xa5,0xe6,0x10,0x1b,0x2c,0xbd,0x8f,0x73,0xa1,0xac,0x9c,0x69,0xa0,0x1e,0xc,0x62,0xc8,0x29,0xb0,0xa2,0xc7,0x55,0x5b,0xd1,0xb5,0x3b,0xcb,0x25,0x6f,0xff,0x7,0x77,0x31,0x15,0xc6,0xfe,0xf3,0xeb,0xd3,0xcf,0x79,0x1a,0x4e,0x5e,0xf4,0xe5,0xb6,0xfd,0x27,0x3e,0x2e,0x4,0xd2,0xb9,0x52,0xe4,0x2a,0x66,0x8a,0xbe,0xad,0xc1,0x14,0xcc,0xb8,0x1d,0xbb,0x9,0xe8,0x9d,0xf5,0x5f,0x83,0xda,0x71,0x65,0x5d,0x63,0x78,0x39,0x57,0xf9,0x6e,0xdb,0xe2,0xb2,0x32,0x99,0x49,0x7f,0xc4,0xec,0x6d,0x6c,0x51,0x34,0xd0,0x45,0x17,0x53,0x81,0xd7,0xf,0x35,0x26,0x96,0xe,0x44,0x4b,0x94,0x5a,0x41,0xcd,0x7c,0xd6,0xbf,0xee,0x98,0x43,0x3,0x13,0x56,0x89,0xd,0x9f,0x18,0x48,0x6b,0xc2,0x3a,0xf0,0x20,0xdd,0xa7,0x88,0xb4,0x85,0x75,0xd4,0x19,0xc0,0xb7,0xca,0x42,0xde,0xaf,0x82,0xf6,0x90,0xb,0x7e,0xbc,0x92,0x64,0xa4,0x2b,0xfa,0xb3,0xb1,0x12,0xfc,0x4f,0x23,0x84,0xa,0xdc,0xf2,0xc3,0xae,0xe9,0x16,0xba,0x3c,0xd9,0x46,0x8e,0x4c,0x3f,0x6a,0x30,0xc4,0xe9,0x10,0xfb,0xdf,0x39,0x63,0x97,0xba,0x9c,0x13,0xd4,0x9a,0x6b,0x96,0x0,0x5c,0xfe,0x27,0xb9,0xf0,0x48,0x71,0xfc,0xe6,0xc9,0x26,0x80,0x14,0x70,0x91,0x8f,0x86,0x3c,0xf1,0xf6,0x76,0x6c,0x5b,0x79,0xce,0x51,0x8b,0x30,0x29,0x9d,0x4d,0x35,0xd,0xf2,0x11,0x2c,0x33,0x41,0x65,0x49,0xe0,0xbb,0xd8,0x67,0x82,0x84,0x17,0x8a,0x73,0x1d,0xf,0xb1,0xb3,0xa1,0x38,0xd9,0x62,0x9e,0xac,0x3d,0x78,0x8d,0xbd,0xb0,0x45,0x56,0xea,0xb7,0xa,0x1,0xf7,0xb4,0x22,0x6a,0x19,0xb2,0x3e,0xe,0x61,0xb8,0xec,0xa7,0xf4,0xe5,0x15,0x3f,0x2f,0x36,0xde,0xc2,0xfa,0xe2,0x4f,0x5f,0xb,0x68,0x66,0x16,0xee,0x7e,0xef,0xd7,0x4,0x20,0xc0,0x4a,0x44,0xd6,0x34,0xda,0x2a,0xa4,0x28,0x69,0x72,0x4c,0xca,0x7f,0xe8,0x46,0x4e,0xe4,0x8c,0xf9,0x74,0x60,0xcb,0x92,0xdd,0x5,0xd0,0xbc,0x18,0xaa,0xc,0xa9,0xf5,0x43,0xa8,0xc3,0xaf,0x9b,0x77,0x3b,0x50,0x4b,0x85,0x5a,0xae,0xc7,0x6d,0xdc,0x24,0x1e,0xc6,0x90,0x55,0x1f,0x87,0x37,0x25,0x40,0x7d,0x7c,0x42,0x6,0x54,0xc1,0x88,0x23,0xa3,0xf3,0xfd,0xd5,0x6e,0x58,0x53,0xdb,0xa6,0xd1,0xe7,0x93,0xbe,0xcf,0xa5,0x99,0xb6,0xcc,0x8,0xc5,0x64,0x94,0x7a,0x59,0x9,0x8e,0x31,0xe1,0x2b,0xd3,0x12,0x52,0x89,0xff,0x1c,0x98,0x47,0x2,0x9f,0x57,0xc8,0x2d,0x21,0x7b,0x2e,0x5d,0xd2,0xe3,0xcd,0x1b,0xab,0x7,0xf8,0xbf,0x3,0xa0,0xa2,0xeb,0x95,0x32,0x5e,0xed,0xad,0x6f,0x1a,0x81,0x3a,0xb5,0x75,0x83,0xbc,0xba,0xb2,0x2f,0x83,0xd8,0x5f,0xe0,0x79,0xb,0x71,0x5d,0xca,0x35,0x14,0x29,0xa5,0x11,0xd,0x75,0x69,0xf6,0x8,0xb3,0x54,0x4e,0x41,0x63,0x4,0xbe,0xce,0xc9,0x48,0x2c,0xb7,0xa9,0xf1,0xde,0xb8,0x1e,0x70,0xc8,0xc4,0x49,0xc6,0x64,0x81,0x1f,0x53,0xa2,0x38,0xae,0xa4,0x82,0xec,0x2b,0x1,0xe7,0xaf,0x5b,0xd1,0xfc,0xc3,0x28,0xe2,0xc,0x9c,0x12,0x72,0xf8,0xee,0x7c,0xef,0xd7,0x18,0x3c,0x2e,0x5e,0x46,0xd6,0x67,0x77,0x50,0x33,0xfa,0xe6,0xda,0xc2,0x7,0x2d,0xe,0x17,0x9f,0xd4,0xdd,0xcc,0x36,0x6,0x80,0x59,0x52,0x1a,0x8a,0x21,0x39,0x32,0x8c,0xcf,0x6e,0x7d,0x8f,0xd2,0xb5,0x40,0x88,0x85,0xa6,0x5a,0x5,0x94,0x99,0x8b,0xe1,0x0,0x25,0x4b,0x89,0x37,0xed,0xc5,0x60,0x56,0x1b,0xb0,0xcb,0x9b,0x3e,0x7a,0xf9,0x6c,0x78,0x1d,0x44,0x45,0x27,0x6d,0xf,0xbf,0x26,0x1c,0xa8,0xfe,0xff,0x96,0xe4,0x55,0x73,0x68,0x62,0xbd,0xa3,0x97,0x3,0x4f,0x7b,0xcd,0xfb,0x90,0x92,0x20,0x91,0x34,0x3d,0xe5,0x84,0xe8,0x58,0x4c,0xaa,0xf3,0xdc,0x76,0xc1,0xb4,0x47,0xf2,0x7e,0xd0,0x51,0x10,0x74,0x4a,0x8d,0x2,0xbb,0x4d,0x57,0x95,0xb9,0x22,0xa,0xad,0xd5,0x66,0x98,0x3b,0xd3,0x9a,0x3f,0x93,0x87,0xc0,0xdb,0xea,0x23,0xf5,0x43,0x19,0x65,0x16,0x6f,0xa7,0x15,0xf0,0xa0,0x24,0x3a,0x7f,0x6a,0x2a,0xc7,0xb1,0xd9,0x9,0xeb,0x13,0x61,0x42,0xb6,0x31,0xfd,0x30,0xac,0x5c,0xa1,0x9d,0xf4,0x8e,0xab,0xdf,0xf7,0x86,0xe3,0x6b,0xe9,0x9e,0x8b,0x4c,0xc3,0xe5,0x5f,0xc9,0x34,0xc5,0xa4,0x4f,0xb6,0x9b,0xc8,0x3c,0x66,0x80,0xdf,0x79,0x96,0xb9,0xd0,0xce,0x2f,0x4b,0xe6,0x78,0xa1,0x3,0xa3,0x2e,0x17,0xaf,0x6f,0xd4,0xe,0x91,0x6a,0x12,0xc2,0x76,0xa9,0xae,0x63,0xd9,0x26,0x4,0x33,0x29,0x38,0x87,0xe4,0xbf,0xd5,0x48,0xdb,0xdd,0x73,0x4e,0xad,0x52,0x16,0x3a,0x1e,0x6c,0x62,0xf3,0xc1,0x3d,0xef,0xe2,0xd2,0x27,0xee,0x50,0x42,0x2c,0x86,0x67,0xfe,0xec,0xed,0x46,0x35,0x7d,0xe7,0x3e,0x51,0x61,0xe8,0xb5,0x9,0x1a,0xeb,0xa8,0x5e,0x55,0xbd,0xa5,0x9d,0x81,0x37,0x54,0x0,0x10,0xba,0xab,0xf8,0xb3,0x69,0x70,0x60,0x4a,0x89,0x1b,0x15,0x9f,0xfb,0x75,0x85,0x6b,0x21,0xb1,0x49,0x39,0x7f,0x5b,0x88,0xb0,0xa6,0xd3,0xbb,0x11,0xcd,0x94,0x3f,0x2b,0x13,0x2d,0x36,0x77,0x19,0xb7,0x20,0x95,0x9c,0xf7,0x1c,0xaa,0x64,0x28,0xc4,0xf0,0xe3,0x8f,0x5a,0x82,0xf6,0x53,0xf5,0x47,0xcf,0x99,0x41,0x7b,0x68,0xd8,0x40,0xa,0x5,0xda,0x14,0xf,0x83,0x32,0x98,0xf1,0xac,0xfc,0x7c,0xd7,0x7,0x31,0x8a,0xa2,0x23,0x22,0x1f,0x7a,0x9e,0xb,0x59,0x1d,0x93,0xe9,0xc6,0xfa,0xcb,0x3b,0x9a,0x57,0x8e,0xf9,0x84,0xc,0x90,0xe1,0xcc,0xb8,0xa0,0xd6,0xd,0x4d,0x5d,0x18,0xc7,0x43,0xd1,0x56,0x6,0x25,0x8c,0x74,0xbe,0x6e,0x44,0x92,0xbc,0x8d,0xe0,0xa7,0x58,0xf4,0x72,0x97,0x8,0xc0,0x2,0x71,0x24,0x7e,0xde,0x45,0x30,0xf2,0xdc,0x2a,0xea,0x65,0xb4,0xfd,0xff,0x5c,0xb2,0x1,0x6d,0xca,0x41,0xe7,0x8,0x27,0x4e,0x50,0xb1,0xd5,0x78,0xe6,0x3f,0x9d,0x3d,0xb0,0x89,0x31,0x15,0xd2,0x5d,0x7b,0xc1,0x57,0xaa,0x5b,0x3a,0xd1,0x28,0x5,0x56,0xa2,0xf8,0x1e,0xa6,0x19,0x7a,0x21,0x4b,0xd6,0x45,0x43,0xed,0xd0,0x33,0xcc,0x88,0xa4,0x80,0xf2,0xf1,0x4a,0x90,0xf,0xf4,0x8c,0x5c,0xe8,0x37,0x30,0xfd,0x47,0xb8,0x9a,0xad,0xb7,0x73,0xd8,0xab,0xe3,0x79,0xa0,0xcf,0xff,0x76,0x2b,0x97,0x84,0x75,0x36,0xc0,0xcb,0xfc,0x6d,0x5f,0xa3,0x71,0x7c,0x4c,0xb9,0x70,0xce,0xdc,0xb2,0x18,0xf9,0x60,0x72,0x17,0x85,0x8b,0x1,0x65,0xeb,0x1b,0xf5,0xbf,0x2f,0xd7,0xa7,0xe1,0xc5,0x16,0x2e,0x23,0x3b,0x3,0x1f,0xa9,0xca,0x9e,0x8e,0x24,0x35,0x66,0x2d,0xf7,0xee,0xfe,0xd4,0x2,0x69,0x82,0x34,0xfa,0xb6,0x5a,0x6e,0x7d,0x11,0xc4,0x1c,0x68,0xcd,0x6b,0xd9,0x38,0x4d,0x25,0x8f,0x53,0xa,0xa1,0xb5,0x8d,0xb3,0xa8,0xe9,0x87,0x29,0xbe,0xb,0x32,0x62,0xe2,0x49,0x99,0xaf,0x14,0x3c,0xbd,0xbc,0x81,0xe4,0x0,0x95,0xc7,0x83,0x51,0x7,0xdf,0xe5,0xf6,0x46,0xde,0x94,0x9b,0x44,0x8a,0x91,0x1d,0xac,0x6,0x6f,0x3e,0x48,0x93,0xd3,0xc3,0x86,0x59,0xdd,0x4f,0xc8,0x98,0xbb,0x12,0xea,0x20,0xf0,0xd,0x77,0x58,0x64,0x55,0xa5,0x4,0xc9,0x10,0x67,0x1a,0x92,0xe,0x7f,0x52,0x26,0x40,0xdb,0xae,0x6c,0x42,0xb4,0x74,0xfb,0x2a,0x63,0x61,0xc2,0x2c,0x9f,0xf3,0x54,0xda,0xc,0x22,0x13,0x7e,0x39,0xc6,0x6a,0xec,0x9,0x96,0x5e,0x9c,0xef,0xba,0xe0,0xb,0x26,0xdf,0x34,0x10,0xf6,0xac,0x58,0x75,0x53,0xdc,0x1b,0x55,0xa4,0x59,0xcf,0x93,0x31,0xe8,0x76,0x3f,0x87,0xbe,0x33,0x29,0x6,0xe9,0x4f,0xdb,0xbf,0x5e,0x40,0x49,0xf3,0x3e,0x39,0xb9,0xa3,0x94,0xb6,0x1,0x9e,0x44,0xff,0xe6,0x52,0x82,0xfa,0xc2,0x3d,0xde,0xe3,0xfc,0x8e,0xaa,0x86,0x2f,0x74,0x17,0xa8,0x4d,0x4b,0xd8,0x45,0xbc,0xd2,0xc0,0x7e,0x7c,0x6e,0xf7,0x16,0xad,0x51,0x63,0xf2,0xb7,0x42,0x72,0x7f,0x8a,0x99,0x25,0x78,0xc5,0xce,0x38,0x7b,0xed,0xa5,0xd6,0x7d,0xf1,0xc1,0xae,0x77,0x23,0x68,0x3b,0x2a,0xda,0xf0,0xe0,0xf9,0x11,0xd,0x35,0x2d,0x80,0x90,0xc4,0xa7,0xa9,0xd9,0x21,0xb1,0x20,0x18,0xcb,0xef,0xf,0x85,0x8b,0x19,0xfb,0x15,0xe5,0x6b,0xe7,0xa6,0xbd,0x83,0x5,0xb0,0x27,0x89,0x81,0x2b,0x43,0x36,0xbb,0xaf,0x4,0x5d,0x12,0xca,0x1f,0x73,0xd7,0x65,0xc3,0x66,0x3a,0x8c,0x67,0xc,0x60,0x54,0xb8,0xf4,0x9f,0x84,0x4a,0x95,0x61,0x8,0xa2,0x13,0xeb,0xd1,0x9,0x5f,0x9a,0xd0,0x48,0xf8,0xea,0x8f,0xb2,0xb3,0x8d,0xc9,0x9b,0xe,0x47,0xec,0x6c,0x3c,0x32,0x1a,0xa1,0x97,0x9c,0x14,0x69,0x1e,0x28,0x5c,0x71,0x0,0x6a,0x56,0x79,0x3,0xc7,0xa,0xab,0x5b,0xb5,0x96,0xc6,0x41,0xfe,0x2e,0xe4,0x1c,0xdd,0x9d,0x46,0x30,0xd3,0x57,0x88,0xcd,0x50,0x98,0x7,0xe2,0xee,0xb4,0xe1,0x92,0x1d,0x2c,0x2,0xd4,0x64,0xc8,0x37,0x70,0xcc,0x6f,0x6d,0x24,0x5a,0xfd,0x91,0x22,0x62,0xa0,0xd5,0x4e,0xf5,0x7a,0xba,0x4c,0x3d,0x3b,0x33,0xae,0x2,0x59,0xde,0x61,0xf8,0x8a,0xf0,0xdc,0x4b,0xb4,0x95,0xa8,0x24,0x90,0x8c,0xf4,0xe8,0x77,0x89,0x32,0xd5,0xcf,0xc0,0xe2,0x85,0x3f,0x4f,0x48,0xc9,0xad,0x36,0x28,0x70,0x5f,0x39,0x9f,0xf1,0x49,0x45,0xc8,0x47,0xe5,0x0,0x9e,0xd2,0x23,0xb9,0x2f,0x25,0x3,0x6d,0xaa,0x80,0x66,0x2e,0xda,0x50,0x7d,0x42,0xa9,0x63,0x8d,0x1d,0x93,0xf3,0x79,0x6f,0xfd,0x6e,0x56,0x99,0xbd,0xaf,0xdf,0xc7,0x57,0xe6,0xf6,0xd1,0xb2,0x7b,0x67,0x5b,0x43,0x86,0xac,0x8f,0x96,0x1e,0x55,0x5c,0x4d,0xb7,0x87,0x1,0xd8,0xd3,0x9b,0xb,0xa0,0xb8,0xb3,0xd,0x4e,0xef,0xfc,0xe,0x53,0x34,0xc1,0x9,0x4,0x27,0xdb,0x84,0x15,0x18,0xa,0x60,0x81,0xa4,0xca,0x8,0xb6,0x6c,0x44,0xe1,0xd7,0x9a,0x31,0x4a,0x1a,0xbf,0xfb,0x78,0xed,0xf9,0x9c,0xc5,0xc4,0xa6,0xec,0x8e,0x3e,0xa7,0x9d,0x29,0x7f,0x7e,0x17,0x65,0xd4,0xf2,0xe9,0xe3,0x3c,0x22,0x16,0x82,0xce,0xfa,0x4c,0x7a,0x11,0x13,0xa1,0x10,0xb5,0xbc,0x64,0x5,0x69,0xd9,0xcd,0x2b,0x72,0x5d,0xf7,0x40,0x35,0xc6,0x73,0xff,0x51,0xd0,0x91,0xf5,0xcb,0xc,0x83,0x3a,0xcc,0xd6,0x14,0x38,0xa3,0x8b,0x2c,0x54,0xe7,0x19,0xba,0x52,0x1b,0xbe,0x12,0x6,0x41,0x5a,0x6b,0xa2,0x74,0xc2,0x98,0xe4,0x97,0xee,0x26,0x94,0x71,0x21,0xa5,0xbb,0xfe,0xeb,0xab,0x46,0x30,0x58,0x88,0x6a,0x92,0xe0,0xc3,0x37,0xb0,0x7c,0xb1,0x2d,0xdd,0x20,0x1c,0x75,0xf,0x2a,0x5e,0x76,0x7,0x62,0xea,0x68,0x1f,0x60,0xa7,0x28,0xe,0xb4,0x22,0xdf,0x2e,0x4f,0xa4,0x5d,0x70,0x23,0xd7,0x8d,0x6b,0x34,0x92,0x7d,0x52,0x3b,0x25,0xc4,0xa0,0xd,0x93,0x4a,0xe8,0x48,0xc5,0xfc,0x44,0x84,0x3f,0xe5,0x7a,0x81,0xf9,0x29,0x9d,0x42,0x45,0x88,0x32,0xcd,0xef,0xd8,0xc2,0xd3,0x6c,0xf,0x54,0x3e,0xa3,0x30,0x36,0x98,0xa5,0x46,0xb9,0xfd,0xd1,0xf5,0x87,0x89,0x18,0x2a,0xd6,0x4,0x9,0x39,0xcc,0x5,0xbb,0xa9,0xc7,0x6d,0x8c,0x15,0x7,0x6,0xad,0xde,0x96,0xc,0xd5,0xba,0x8a,0x3,0x5e,0xe2,0xf1,0x0,0x43,0xb5,0xbe,0x56,0x4e,0x76,0x6a,0xdc,0xbf,0xeb,0xfb,0x51,0x40,0x13,0x58,0x82,0x9b,0x8b,0xa1,0x62,0xf0,0xfe,0x74,0x10,0x9e,0x6e,0x80,0xca,0x5a,0xa2,0xd2,0x94,0xb0,0x63,0x5b,0x4d,0x38,0x50,0xfa,0x26,0x7f,0xd4,0xc0,0xf8,0xc6,0xdd,0x9c,0xf2,0x5c,0xcb,0x7e,0x77,0x1c,0xf7,0x41,0x8f,0xc3,0x2f,0x1b,0x8,0x64,0xb1,0x69,0x1d,0xb8,0x1e,0xac,0x24,0x72,0xaa,0x90,0x83,0x33,0xab,0xe1,0xee,0x31,0xff,0xe4,0x68,0xd9,0x73,0x1a,0x47,0x17,0x97,0x3c,0xec,0xda,0x61,0x49,0xc8,0xc9,0xf4,0x91,0x75,0xe0,0xb2,0xf6,0x78,0x2,0x2d,0x11,0x20,0xd0,0x71,0xbc,0x65,0x12,0x6f,0xe7,0x7b,0xa,0x27,0x53,0x4b,0x3d,0xe6,0xa6,0xb6,0xf3,0x2c,0xa8,0x3a,0xbd,0xed,0xce,0x67,0x9f,0x55,0x85,0xaf,0x79,0x57,0x66,0xb,0x4c,0xb3,0x1f,0x99,0x7c,0xe3,0x2b,0xe9,0x9a,0xcf,0x95,0x35,0xae,0xdb,0x19,0x37,0xc1,0x1,0x8e,0x5f,0x16,0x14,0xb7,0x59,0xea,0x86,0x21,0xaa,0xc,0xe3,0xcc,0xa5,0xbb,0x5a,0x3e,0x93,0xd,0xd4,0x76,0xd6,0x5b,0x62,0xda,0xfe,0x39,0xb6,0x90,0x2a,0xbc,0x41,0xb0,0xd1,0x3a,0xc3,0xee,0xbd,0x49,0x13,0xf5,0x4d,0xf2,0x91,0xca,0xa0,0x3d,0xae,0xa8,0x6,0x3b,0xd8,0x27,0x63,0x4f,0x6b,0x19,0x1a,0xa1,0x7b,0xe4,0x1f,0x67,0xb7,0x3,0xdc,0xdb,0x16,0xac,0x53,0x71,0x46,0x5c,0x98,0x33,0x40,0x8,0x92,0x4b,0x24,0x14,0x9d,0xc0,0x7c,0x6f,0x9e,0xdd,0x2b,0x20,0x17,0x86,0xb4,0x48,0x9a,0x97,0xa7,0x52,0x9b,0x25,0x37,0x59,0xf3,0x12,0x8b,0x99,0xfc,0x6e,0x60,0xea,0x8e,0x0,0xf0,0x1e,0x54,0xc4,0x3c,0x4c,0xa,0x2e,0xfd,0xc5,0xc8,0xd0,0xe8,0xf4,0x42,0x21,0x75,0x65,0xcf,0xde,0x8d,0xc6,0x1c,0x5,0x15,0x3f,0xe9,0x82,0x69,0xdf,0x11,0x5d,0xb1,0x85,0x96,0xfa,0x2f,0xf7,0x83,0x26,0x80,0x32,0xd3,0xa6,0xce,0x64,0xb8,0xe1,0x4a,0x5e,0x66,0x58,0x43,0x2,0x6c,0xc2,0x55,0xe0,0xd9,0x89,0x9,0xa2,0x72,0x44,0xff,0xd7,0x56,0x57,0x6a,0xf,0xeb,0x7e,0x2c,0x68,0xba,0xec,0x34,0xe,0x1d,0xad,0x35,0x7f,0x70,0xaf,0x61,0x7a,0xf6,0x47,0xed,0x84,0xd5,0xa3,0x78,0x38,0x28,0x6d,0xb2,0x36,0xa4,0x23,0x73,0x50,0xf9,0x1,0xcb,0x1b,0xe6,0x9c,0xb3,0x8f,0xbe,0x4e,0xef,0x22,0xfb,0x8c,0xf1,0x79,0xe5,0x94,0xb9,0xcd,0xab,0x30,0x45,0x87,0xa9,0x5f,0x9f,0x10,0xc1,0x88,0x8a,0x29,0xc7,0x74,0x18,0xbf,0x31,0xe7,0xc9,0xf8,0x95,0xd2,0x2d,0x81,0x7,0xe2,0x7d,0xb5,0x77,0x4,0x51,0xb,0xf3,0xde,0x27,0xcc,0xe8,0xe,0x54,0xa0,0x8d,0xab,0x24,0xe3,0xad,0x5c,0xa1,0x37,0x6b,0xc9,0x10,0x8e,0xc7,0x7f,0x46,0xcb,0xd1,0xfe,0x11,0xb7,0x23,0x47,0xa6,0xb8,0xb1,0xb,0xc6,0xc1,0x41,0x5b,0x6c,0x4e,0xf9,0x66,0xbc,0x7,0x1e,0xaa,0x7a,0x2,0x3a,0xc5,0x26,0x1b,0x4,0x76,0x52,0x7e,0xd7,0x8c,0xef,0x50,0xb5,0xb3,0x20,0xbd,0x44,0x2a,0x38,0x86,0x84,0x96,0xf,0xee,0x55,0xa9,0x9b,0xa,0x4f,0xba,0x8a,0x87,0x72,0x61,0xdd,0x80,0x3d,0x36,0xc0,0x83,0x15,0x5d,0x2e,0x85,0x9,0x39,0x56,0x8f,0xdb,0x90,0xc3,0xd2,0x22,0x8,0x18,0x1,0xe9,0xf5,0xcd,0xd5,0x78,0x68,0x3c,0x5f,0x51,0x21,0xd9,0x49,0xd8,0xe0,0x33,0x17,0xf7,0x7d,0x73,0xe1,0x3,0xed,0x1d,0x93,0x1f,0x5e,0x45,0x7b,0xfd,0x48,0xdf,0x71,0x79,0xd3,0xbb,0xce,0x43,0x57,0xfc,0xa5,0xea,0x32,0xe7,0x8b,0x2f,0x9d,0x3b,0x9e,0xc2,0x74,0x9f,0xf4,0x98,0xac,0x40,0xc,0x67,0x7c,0xb2,0x6d,0x99,0xf0,0x5a,0xeb,0x13,0x29,0xf1,0xa7,0x62,0x28,0xb0,0x0,0x12,0x77,0x4a,0x4b,0x75,0x31,0x63,0xf6,0xbf,0x14,0x94,0xc4,0xca,0xe2,0x59,0x6f,0x64,0xec,0x91,0xe6,0xd0,0xa4,0x89,0xf8,0x92,0xae,0x81,0xfb,0x3f,0xf2,0x53,0xa3,0x4d,0x6e,0x3e,0xb9,0x6,0xd6,0x1c,0xe4,0x25,0x65,0xbe,0xc8,0x2b,0xaf,0x70,0x35,0xa8,0x60,0xff,0x1a,0x16,0x4c,0x19,0x6a,0xe5,0xd4,0xfa,0x2c,0x9c,0x30,0xcf,0x88,0x34,0x97,0x95,0xdc,0xa2,0x5,0x69,0xda,0x9a,0x58,0x2d,0xb6,0xd,0x82,0x42,0xb4,0xb,0xd,0x5,0x98,0x34,0x6f,0xe8,0x57,0xce,0xbc,0xc6,0xea,0x7d,0x82,0xa3,0x9e,0x12,0xa6,0xba,0xc2,0xde,0x41,0xbf,0x4,0xe3,0xf9,0xf6,0xd4,0xb3,0x9,0x79,0x7e,0xff,0x9b,0x0,0x1e,0x46,0x69,0xf,0xa9,0xc7,0x7f,0x73,0xfe,0x71,0xd3,0x36,0xa8,0xe4,0x15,0x8f,0x19,0x13,0x35,0x5b,0x9c,0xb6,0x50,0x18,0xec,0x66,0x4b,0x74,0x9f,0x55,0xbb,0x2b,0xa5,0xc5,0x4f,0x59,0xcb,0x58,0x60,0xaf,0x8b,0x99,0xe9,0xf1,0x61,0xd0,0xc0,0xe7,0x84,0x4d,0x51,0x6d,0x75,0xb0,0x9a,0xb9,0xa0,0x28,0x63,0x6a,0x7b,0x81,0xb1,0x37,0xee,0xe5,0xad,0x3d,0x96,0x8e,0x85,0x3b,0x78,0xd9,0xca,0x38,0x65,0x2,0xf7,0x3f,0x32,0x11,0xed,0xb2,0x23,0x2e,0x3c,0x56,0xb7,0x92,0xfc,0x3e,0x80,0x5a,0x72,0xd7,0xe1,0xac,0x7,0x7c,0x2c,0x89,0xcd,0x4e,0xdb,0xcf,0xaa,0xf3,0xf2,0x90,0xda,0xb8,0x8,0x91,0xab,0x1f,0x49,0x48,0x21,0x53,0xe2,0xc4,0xdf,0xd5,0xa,0x14,0x20,0xb4,0xf8,0xcc,0x7a,0x4c,0x27,0x25,0x97,0x26,0x83,0x8a,0x52,0x33,0x5f,0xef,0xfb,0x1d,0x44,0x6b,0xc1,0x76,0x3,0xf0,0x45,0xc9,0x67,0xe6,0xa7,0xc3,0xfd,0x3a,0xb5,0xc,0xfa,0xe0,0x22,0xe,0x95,0xbd,0x1a,0x62,0xd1,0x2f,0x8c,0x64,0x2d,0x88,0x24,0x30,0x77,0x6c,0x5d,0x94,0x42,0xf4,0xae,0xd2,0xa1,0xd8,0x10,0xa2,0x47,0x17,0x93,0x8d,0xc8,0xdd,0x9d,0x70,0x6,0x6e,0xbe,0x5c,0xa4,0xd6,0xf5,0x1,0x86,0x4a,0x87,0x1b,0xeb,0x16,0x2a,0x43,0x39,0x1c,0x68,0x40,0x31,0x54,0xdc,0x5e,0x29,0x11,0xd6,0x59,0x7f,0xc5,0x53,0xae,0x5f,0x3e,0xd5,0x2c,0x1,0x52,0xa6,0xfc,0x1a,0x45,0xe3,0xc,0x23,0x4a,0x54,0xb5,0xd1,0x7c,0xe2,0x3b,0x99,0x39,0xb4,0x8d,0x35,0xf5,0x4e,0x94,0xb,0xf0,0x88,0x58,0xec,0x33,0x34,0xf9,0x43,0xbc,0x9e,0xa9,0xb3,0xa2,0x1d,0x7e,0x25,0x4f,0xd2,0x41,0x47,0xe9,0xd4,0x37,0xc8,0x8c,0xa0,0x84,0xf6,0xf8,0x69,0x5b,0xa7,0x75,0x78,0x48,0xbd,0x74,0xca,0xd8,0xb6,0x1c,0xfd,0x64,0x76,0x77,0xdc,0xaf,0xe7,0x7d,0xa4,0xcb,0xfb,0x72,0x2f,0x93,0x80,0x71,0x32,0xc4,0xcf,0x27,0x3f,0x7,0x1b,0xad,0xce,0x9a,0x8a,0x20,0x31,0x62,0x29,0xf3,0xea,0xfa,0xd0,0x13,0x81,0x8f,0x5,0x61,0xef,0x1f,0xf1,0xbb,0x2b,0xd3,0xa3,0xe5,0xc1,0x12,0x2a,0x3c,0x49,0x21,0x8b,0x57,0xe,0xa5,0xb1,0x89,0xb7,0xac,0xed,0x83,0x2d,0xba,0xf,0x6,0x6d,0x86,0x30,0xfe,0xb2,0x5e,0x6a,0x79,0x15,0xc0,0x18,0x6c,0xc9,0x6f,0xdd,0x55,0x3,0xdb,0xe1,0xf2,0x42,0xda,0x90,0x9f,0x40,0x8e,0x95,0x19,0xa8,0x2,0x6b,0x36,0x66,0xe6,0x4d,0x9d,0xab,0x10,0x38,0xb9,0xb8,0x85,0xe0,0x4,0x91,0xc3,0x87,0x9,0x73,0x5c,0x60,0x51,0xa1,0x0,0xcd,0x14,0x63,0x1e,0x96,0xa,0x7b,0x56,0x22,0x3a,0x4c,0x97,0xd7,0xc7,0x82,0x5d,0xd9,0x4b,0xcc,0x9c,0xbf,0x16,0xee,0x24,0xf4,0xde,0x8,0x26,0x17,0x7a,0x3d,0xc2,0x6e,0xe8,0xd,0x92,0x5a,0x98,0xeb,0xbe,0xe4,0x44,0xdf,0xaa,0x68,0x46,0xb0,0x70,0xff,0x2e,0x67,0x65,0xc6,0x28,0x9b,0xf7,0x50,0x2f,0x89,0x66,0x49,0x20,0x3e,0xdf,0xbb,0x16,0x88,0x51,0xf3,0x53,0xde,0xe7,0x5f,0x7b,0xbc,0x33,0x15,0xaf,0x39,0xc4,0x35,0x54,0xbf,0x46,0x6b,0x38,0xcc,0x96,0x70,0xc8,0x77,0x14,0x4f,0x25,0xb8,0x2b,0x2d,0x83,0xbe,0x5d,0xa2,0xe6,0xca,0xee,0x9c,0x9f,0x24,0xfe,0x61,0x9a,0xe2,0x32,0x86,0x59,0x5e,0x93,0x29,0xd6,0xf4,0xc3,0xd9,0x1d,0xb6,0xc5,0x8d,0x17,0xce,0xa1,0x91,0x18,0x45,0xf9,0xea,0x1b,0x58,0xae,0xa5,0x92,0x3,0x31,0xcd,0x1f,0x12,0x22,0xd7,0x1e,0xa0,0xb2,0xdc,0x76,0x97,0xe,0x1c,0x79,0xeb,0xe5,0x6f,0xb,0x85,0x75,0x9b,0xd1,0x41,0xb9,0xc9,0x8f,0xab,0x78,0x40,0x4d,0x55,0x6d,0x71,0xc7,0xa4,0xf0,0xe0,0x4a,0x5b,0x8,0x43,0x99,0x80,0x90,0xba,0x6c,0x7,0xec,0x5a,0x94,0xd8,0x34,0x0,0x13,0x7f,0xaa,0x72,0x6,0xa3,0x5,0xb7,0x56,0x23,0x4b,0xe1,0x3d,0x64,0xcf,0xdb,0xe3,0xdd,0xc6,0x87,0xe9,0x47,0xd0,0x65,0x5c,0xc,0x8c,0x27,0xf7,0xc1,0x7a,0x52,0xd3,0xd2,0xef,0x8a,0x6e,0xfb,0xa9,0xed,0x3f,0x69,0xb1,0x8b,0x98,0x28,0xb0,0xfa,0xf5,0x2a,0xe4,0xff,0x73,0xc2,0x68,0x1,0x50,0x26,0xfd,0xbd,0xad,0xe8,0x37,0xb3,0x21,0xa6,0xf6,0xd5,0x7c,0x84,0x4e,0x9e,0x63,0x19,0x36,0xa,0x3b,0xcb,0x6a,0xa7,0x7e,0x9,0x74,0xfc,0x60,0x11,0x3c,0x48,0x2e,0xb5,0xc0,0x2,0x2c,0xda,0x1a,0x95,0x44,0xd,0xf,0xac,0x42,0xf1,0x9d,0x3a,0xb4,0x62,0x4c,0x7d,0x10,0x57,0xa8,0x4,0x82,0x67,0xf8,0x30,0xf2,0x81,0xd4,0x8e,0x36,0x1b,0xe2,0x9,0x2d,0xcb,0x91,0x65,0x48,0x6e,0xe1,0x26,0x68,0x99,0x64,0xf2,0xae,0xc,0xd5,0x4b,0x2,0xba,0x83,0xe,0x14,0x3b,0xd4,0x72,0xe6,0x82,0x63,0x7d,0x74,0xce,0x3,0x4,0x84,0x9e,0xa9,0x8b,0x3c,0xa3,0x79,0xc2,0xdb,0x6f,0xbf,0xc7,0xff,0x0,0xe3,0xde,0xc1,0xb3,0x97,0xbb,0x12,0x49,0x2a,0x95,0x70,0x76,0xe5,0x78,0x81,0xef,0xfd,0x43,0x41,0x53,0xca,0x2b,0x90,0x6c,0x5e,0xcf,0x8a,0x7f,0x4f,0x42,0xb7,0xa4,0x18,0x45,0xf8,0xf3,0x5,0x46,0xd0,0x98,0xeb,0x40,0xcc,0xfc,0x93,0x4a,0x1e,0x55,0x6,0x17,0xe7,0xcd,0xdd,0xc4,0x2c,0x30,0x8,0x10,0xbd,0xad,0xf9,0x9a,0x94,0xe4,0x1c,0x8c,0x1d,0x25,0xf6,0xd2,0x32,0xb8,0xb6,0x24,0xc6,0x28,0xd8,0x56,0xda,0x9b,0x80,0xbe,0x38,0x8d,0x1a,0xb4,0xbc,0x16,0x7e,0xb,0x86,0x92,0x39,0x60,0x2f,0xf7,0x22,0x4e,0xea,0x58,0xfe,0x5b,0x7,0xb1,0x5a,0x31,0x5d,0x69,0x85,0xc9,0xa2,0xb9,0x77,0xa8,0x5c,0x35,0x9f,0x2e,0xd6,0xec,0x34,0x62,0xa7,0xed,0x75,0xc5,0xd7,0xb2,0x8f,0x8e,0xb0,0xf4,0xa6,0x33,0x7a,0xd1,0x51,0x1,0xf,0x27,0x9c,0xaa,0xa1,0x29,0x54,0x23,0x15,0x61,0x4c,0x3d,0x57,0x6b,0x44,0x3e,0xfa,0x37,0x96,0x66,0x88,0xab,0xfb,0x7c,0xc3,0x13,0xd9,0x21,0xe0,0xa0,0x7b,0xd,0xee,0x6a,0xb5,0xf0,0x6d,0xa5,0x3a,0xdf,0xd3,0x89,0xdc,0xaf,0x20,0x11,0x3f,0xe9,0x59,0xf5,0xa,0x4d,0xf1,0x52,0x50,0x19,0x67,0xc0,0xac,0x1f,0x5f,0x9d,0xe8,0x73,0xc8,0x47,0x87,0x71,0x85,0x83,0x8b,0x16,0xba,0xe1,0x66,0xd9,0x40,0x32,0x48,0x64,0xf3,0xc,0x2d,0x10,0x9c,0x28,0x34,0x4c,0x50,0xcf,0x31,0x8a,0x6d,0x77,0x78,0x5a,0x3d,0x87,0xf7,0xf0,0x71,0x15,0x8e,0x90,0xc8,0xe7,0x81,0x27,0x49,0xf1,0xfd,0x70,0xff,0x5d,0xb8,0x26,0x6a,0x9b,0x1,0x97,0x9d,0xbb,0xd5,0x12,0x38,0xde,0x96,0x62,0xe8,0xc5,0xfa,0x11,0xdb,0x35,0xa5,0x2b,0x4b,0xc1,0xd7,0x45,0xd6,0xee,0x21,0x5,0x17,0x67,0x7f,0xef,0x5e,0x4e,0x69,0xa,0xc3,0xdf,0xe3,0xfb,0x3e,0x14,0x37,0x2e,0xa6,0xed,0xe4,0xf5,0xf,0x3f,0xb9,0x60,0x6b,0x23,0xb3,0x18,0x0,0xb,0xb5,0xf6,0x57,0x44,0xb6,0xeb,0x8c,0x79,0xb1,0xbc,0x9f,0x63,0x3c,0xad,0xa0,0xb2,0xd8,0x39,0x1c,0x72,0xb0,0xe,0xd4,0xfc,0x59,0x6f,0x22,0x89,0xf2,0xa2,0x7,0x43,0xc0,0x55,0x41,0x24,0x7d,0x7c,0x1e,0x54,0x36,0x86,0x1f,0x25,0x91,0xc7,0xc6,0xaf,0xdd,0x6c,0x4a,0x51,0x5b,0x84,0x9a,0xae,0x3a,0x76,0x42,0xf4,0xc2,0xa9,0xab,0x19,0xa8,0xd,0x4,0xdc,0xbd,0xd1,0x61,0x75,0x93,0xca,0xe5,0x4f,0xf8,0x8d,0x7e,0xcb,0x47,0xe9,0x68,0x29,0x4d,0x73,0xb4,0x3b,0x82,0x74,0x6e,0xac,0x80,0x1b,0x33,0x94,0xec,0x5f,0xa1,0x2,0xea,0xa3,0x6,0xaa,0xbe,0xf9,0xe2,0xd3,0x1a,0xcc,0x7a,0x20,0x5c,0x2f,0x56,0x9e,0x2c,0xc9,0x99,0x1d,0x3,0x46,0x53,0x13,0xfe,0x88,0xe0,0x30,0xd2,0x2a,0x58,0x7b,0x8f,0x8,0xc4,0x9,0x95,0x65,0x98,0xa4,0xcd,0xb7,0x92,0xe6,0xce,0xbf,0xda,0x52,0xd0,0xa7,0x4f,0x88,0x7,0x21,0x9b,0xd,0xf0,0x1,0x60,0x8b,0x72,0x5f,0xc,0xf8,0xa2,0x44,0x1b,0xbd,0x52,0x7d,0x14,0xa,0xeb,0x8f,0x22,0xbc,0x65,0xc7,0x67,0xea,0xd3,0x6b,0xab,0x10,0xca,0x55,0xae,0xd6,0x6,0xb2,0x6d,0x6a,0xa7,0x1d,0xe2,0xc0,0xf7,0xed,0xfc,0x43,0x20,0x7b,0x11,0x8c,0x1f,0x19,0xb7,0x8a,0x69,0x96,0xd2,0xfe,0xda,0xa8,0xa6,0x37,0x5,0xf9,0x2b,0x26,0x16,0xe3,0x2a,0x94,0x86,0xe8,0x42,0xa3,0x3a,0x28,0x29,0x82,0xf1,0xb9,0x23,0xfa,0x95,0xa5,0x2c,0x71,0xcd,0xde,0x2f,0x6c,0x9a,0x91,0x79,0x61,0x59,0x45,0xf3,0x90,0xc4,0xd4,0x7e,0x6f,0x3c,0x77,0xad,0xb4,0xa4,0x8e,0x4d,0xdf,0xd1,0x5b,0x3f,0xb1,0x41,0xaf,0xe5,0x75,0x8d,0xfd,0xbb,0x9f,0x4c,0x74,0x62,0x17,0x7f,0xd5,0x9,0x50,0xfb,0xef,0xd7,0xe9,0xf2,0xb3,0xdd,0x73,0xe4,0x51,0x58,0x33,0xd8,0x6e,0xa0,0xec,0x0,0x34,0x27,0x4b,0x9e,0x46,0x32,0x97,0x31,0x83,0xb,0x5d,0x85,0xbf,0xac,0x1c,0x84,0xce,0xc1,0x1e,0xd0,0xcb,0x47,0xf6,0x5c,0x35,0x68,0x38,0xb8,0x13,0xc3,0xf5,0x4e,0x66,0xe7,0xe6,0xdb,0xbe,0x5a,0xcf,0x9d,0xd9,0x57,0x2d,0x2,0x3e,0xf,0xff,0x5e,0x93,0x4a,0x3d,0x40,0xc8,0x54,0x25,0x8,0x7c,0x64,0x12,0xc9,0x89,0x99,0xdc,0x3,0x87,0x15,0x92,0xc2,0xe1,0x48,0xb0,0x7a,0xaa,0x80,0x56,0x78,0x49,0x24,0x63,0x9c,0x30,0xb6,0x53,0xcc,0x4,0xc6,0xb5,0xe0,0xba,0x1a,0x81,0xf4,0x36,0x18,0xee,0x2e,0xa1,0x70,0x39,0x3b,0x98,0x76,0xc5,0xa9,0xe,0xe2,0x44,0xab,0x84,0xed,0xf3,0x12,0x76,0xdb,0x45,0x9c,0x3e,0x9e,0x13,0x2a,0x92,0xb6,0x71,0xfe,0xd8,0x62,0xf4,0x9,0xf8,0x99,0x72,0x8b,0xa6,0xf5,0x1,0x5b,0xbd,0x5,0xba,0xd9,0x82,0xe8,0x75,0xe6,0xe0,0x4e,0x73,0x90,0x6f,0x2b,0x7,0x23,0x51,0x52,0xe9,0x33,0xac,0x57,0x2f,0xff,0x4b,0x94,0x93,0x5e,0xe4,0x1b,0x39,0xe,0x14,0xd0,0x7b,0x8,0x40,0xda,0x3,0x6c,0x5c,0xd5,0x88,0x34,0x27,0xd6,0x95,0x63,0x68,0x5f,0xce,0xfc,0x0,0xd2,0xdf,0xef,0x1a,0xd3,0x6d,0x7f,0x11,0xbb,0x5a,0xc3,0xd1,0xb4,0x26,0x28,0xa2,0xc6,0x48,0xb8,0x56,0x1c,0x8c,0x74,0x4,0x42,0x66,0xb5,0x8d,0x80,0x98,0xa0,0xbc,0xa,0x69,0x3d,0x2d,0x87,0x96,0xc5,0x8e,0x54,0x4d,0x5d,0x77,0xa1,0xca,0x21,0x97,0x59,0x15,0xf9,0xcd,0xde,0xb2,0x67,0xbf,0xcb,0x6e,0xc8,0x7a,0x9b,0xee,0x86,0x2c,0xf0,0xa9,0x2,0x16,0x2e,0x10,0xb,0x4a,0x24,0x8a,0x1d,0xa8,0x91,0xc1,0x41,0xea,0x3a,0xc,0xb7,0x9f,0x1e,0x1f,0x22,0x47,0xa3,0x36,0x64,0x20,0xf2,0xa4,0x7c,0x46,0x55,0xe5,0x7d,0x37,0x38,0xe7,0x29,0x32,0xbe,0xf,0xa5,0xcc,0x9d,0xeb,0x30,0x70,0x60,0x25,0xfa,0x7e,0xec,0x6b,0x3b,0x18,0xb1,0x49,0x83,0x53,0xae,0xd4,0xfb,0xc7,0xf6,0x6,0xa7,0x6a,0xb3,0xc4,0xb9,0x31,0xad,0xdc,0xf1,0x85,0xe3,0x78,0xd,0xcf,0xe1,0x17,0xd7,0x58,0x89,0xc0,0xc2,0x61,0x8f,0x3c,0x50,0xf7,0x79,0xaf,0x81,0xb0,0xdd,0x9a,0x65,0xc9,0x4f,0xaa,0x35,0xfd,0x3f,0x4c,0x19,0x43,0xb9,0x94,0x6d,0x86,0xa2,0x44,0x1e,0xea,0xc7,0xe1,0x6e,0xa9,0xe7,0x16,0xeb,0x7d,0x21,0x83,0x5a,0xc4,0x8d,0x35,0xc,0x81,0x9b,0xb4,0x5b,0xfd,0x69,0xd,0xec,0xf2,0xfb,0x41,0x8c,0x8b,0xb,0x11,0x26,0x4,0xb3,0x2c,0xf6,0x4d,0x54,0xe0,0x30,0x48,0x70,0x8f,0x6c,0x51,0x4e,0x3c,0x18,0x34,0x9d,0xc6,0xa5,0x1a,0xff,0xf9,0x6a,0xf7,0xe,0x60,0x72,0xcc,0xce,0xdc,0x45,0xa4,0x1f,0xe3,0xd1,0x40,0x5,0xf0,0xc0,0xcd,0x38,0x2b,0x97,0xca,0x77,0x7c,0x8a,0xc9,0x5f,0x17,0x64,0xcf,0x43,0x73,0x1c,0xc5,0x91,0xda,0x89,0x98,0x68,0x42,0x52,0x4b,0xa3,0xbf,0x87,0x9f,0x32,0x22,0x76,0x15,0x1b,0x6b,0x93,0x3,0x92,0xaa,0x79,0x5d,0xbd,0x37,0x39,0xab,0x49,0xa7,0x57,0xd9,0x55,0x14,0xf,0x31,0xb7,0x2,0x95,0x3b,0x33,0x99,0xf1,0x84,0x9,0x1d,0xb6,0xef,0xa0,0x78,0xad,0xc1,0x65,0xd7,0x71,0xd4,0x88,0x3e,0xd5,0xbe,0xd2,0xe6,0xa,0x46,0x2d,0x36,0xf8,0x27,0xd3,0xba,0x10,0xa1,0x59,0x63,0xbb,0xed,0x28,0x62,0xfa,0x4a,0x58,0x3d,0x0,0x1,0x3f,0x7b,0x29,0xbc,0xf5,0x5e,0xde,0x8e,0x80,0xa8,0x13,0x25,0x2e,0xa6,0xdb,0xac,0x9a,0xee,0xc3,0xb2,0xd8,0xe4,0xcb,0xb1,0x75,0xb8,0x19,0xe9,0x7,0x24,0x74,0xf3,0x4c,0x9c,0x56,0xae,0x6f,0x2f,0xf4,0x82,0x61,0xe5,0x3a,0x7f,0xe2,0x2a,0xb5,0x50,0x5c,0x6,0x53,0x20,0xaf,0x9e,0xb0,0x66,0xd6,0x7a,0x85,0xc2,0x7e,0xdd,0xdf,0x96,0xe8,0x4f,0x23,0x90,0xd0,0x12,0x67,0xfc,0x47,0xc8,0x8,0xfe,0xb7,0xb1,0xb9,0x24,0x88,0xd3,0x54,0xeb,0x72,0x0,0x7a,0x56,0xc1,0x3e,0x1f,0x22,0xae,0x1a,0x6,0x7e,0x62,0xfd,0x3,0xb8,0x5f,0x45,0x4a,0x68,0xf,0xb5,0xc5,0xc2,0x43,0x27,0xbc,0xa2,0xfa,0xd5,0xb3,0x15,0x7b,0xc3,0xcf,0x42,0xcd,0x6f,0x8a,0x14,0x58,0xa9,0x33,0xa5,0xaf,0x89,0xe7,0x20,0xa,0xec,0xa4,0x50,0xda,0xf7,0xc8,0x23,0xe9,0x7,0x97,0x19,0x79,0xf3,0xe5,0x77,0xe4,0xdc,0x13,0x37,0x25,0x55,0x4d,0xdd,0x6c,0x7c,0x5b,0x38,0xf1,0xed,0xd1,0xc9,0xc,0x26,0x5,0x1c,0x94,0xdf,0xd6,0xc7,0x3d,0xd,0x8b,0x52,0x59,0x11,0x81,0x2a,0x32,0x39,0x87,0xc4,0x65,0x76,0x84,0xd9,0xbe,0x4b,0x83,0x8e,0xad,0x51,0xe,0x9f,0x92,0x80,0xea,0xb,0x2e,0x40,0x82,0x3c,0xe6,0xce,0x6b,0x5d,0x10,0xbb,0xc0,0x90,0x35,0x71,0xf2,0x67,0x73,0x16,0x4f,0x4e,0x2c,0x66,0x4,0xb4,0x2d,0x17,0xa3,0xf5,0xf4,0x9d,0xef,0x5e,0x78,0x63,0x69,0xb6,0xa8,0x9c,0x8,0x44,0x70,0xc6,0xf0,0x9b,0x99,0x2b,0x9a,0x3f,0x36,0xee,0x8f,0xe3,0x53,0x47,0xa1,0xf8,0xd7,0x7d,0xca,0xbf,0x4c,0xf9,0x75,0xdb,0x5a,0x1b,0x7f,0x41,0x86,0x9,0xb0,0x46,0x5c,0x9e,0xb2,0x29,0x1,0xa6,0xde,0x6d,0x93,0x30,0xd8,0x91,0x34,0x98,0x8c,0xcb,0xd0,0xe1,0x28,0xfe,0x48,0x12,0x6e,0x1d,0x64,0xac,0x1e,0xfb,0xab,0x2f,0x31,0x74,0x61,0x21,0xcc,0xba,0xd2,0x2,0xe0,0x18,0x6a,0x49,0xbd,0x3a,0xf6,0x3b,0xa7,0x57,0xaa,0x96,0xff,0x85,0xa0,0xd4,0xfc,0x8d,0xe8,0x60,0xe2,0x95,0x8d,0x4a,0xc5,0xe3,0x59,0xcf,0x32,0xc3,0xa2,0x49,0xb0,0x9d,0xce,0x3a,0x60,0x86,0xd9,0x7f,0x90,0xbf,0xd6,0xc8,0x29,0x4d,0xe0,0x7e,0xa7,0x5,0xa5,0x28,0x11,0xa9,0x69,0xd2,0x8,0x97,0x6c,0x14,0xc4,0x70,0xaf,0xa8,0x65,0xdf,0x20,0x2,0x35,0x2f,0x3e,0x81,0xe2,0xb9,0xd3,0x4e,0xdd,0xdb,0x75,0x48,0xab,0x54,0x10,0x3c,0x18,0x6a,0x64,0xf5,0xc7,0x3b,0xe9,0xe4,0xd4,0x21,0xe8,0x56,0x44,0x2a,0x80,0x61,0xf8,0xea,0xeb,0x40,0x33,0x7b,0xe1,0x38,0x57,0x67,0xee,0xb3,0xf,0x1c,0xed,0xae,0x58,0x53,0xbb,0xa3,0x9b,0x87,0x31,0x52,0x6,0x16,0xbc,0xad,0xfe,0xb5,0x6f,0x76,0x66,0x4c,0x8f,0x1d,0x13,0x99,0xfd,0x73,0x83,0x6d,0x27,0xb7,0x4f,0x3f,0x79,0x5d,0x8e,0xb6,0xa0,0xd5,0xbd,0x17,0xcb,0x92,0x39,0x2d,0x15,0x2b,0x30,0x71,0x1f,0xb1,0x26,0x93,0x9a,0xf1,0x1a,0xac,0x62,0x2e,0xc2,0xf6,0xe5,0x89,0x5c,0x84,0xf0,0x55,0xf3,0x41,0xc9,0x9f,0x47,0x7d,0x6e,0xde,0x46,0xc,0x3,0xdc,0x12,0x9,0x85,0x34,0x9e,0xf7,0xaa,0xfa,0x7a,0xd1,0x1,0x37,0x8c,0xa4,0x25,0x24,0x19,0x7c,0x98,0xd,0x5f,0x1b,0x95,0xef,0xc0,0xfc,0xcd,0x3d,0x9c,0x51,0x88,0xff,0x82,0xa,0x96,0xe7,0xca,0xbe,0xa6,0xd0,0xb,0x4b,0x5b,0x1e,0xc1,0x45,0xd7,0x50,0x0,0x23,0x8a,0x72,0xb8,0x68,0x42,0x94,0xba,0x8b,0xe6,0xa1,0x5e,0xf2,0x74,0x91,0xe,0xc6,0x4,0x77,0x22,0x78,0xd8,0x43,0x36,0xf4,0xda,0x2c,0xec,0x63,0xb2,0xfb,0xf9,0x5a,0xb4,0x7,0x6b,0xcc,0xb7,0x11,0xfe,0xd1,0xb8,0xa6,0x47,0x23,0x8e,0x10,0xc9,0x6b,0xcb,0x46,0x7f,0xc7,0xe3,0x24,0xab,0x8d,0x37,0xa1,0x5c,0xad,0xcc,0x27,0xde,0xf3,0xa0,0x54,0xe,0xe8,0x50,0xef,0x8c,0xd7,0xbd,0x20,0xb3,0xb5,0x1b,0x26,0xc5,0x3a,0x7e,0x52,0x76,0x4,0x7,0xbc,0x66,0xf9,0x2,0x7a,0xaa,0x1e,0xc1,0xc6,0xb,0xb1,0x4e,0x6c,0x5b,0x41,0x85,0x2e,0x5d,0x15,0x8f,0x56,0x39,0x9,0x80,0xdd,0x61,0x72,0x83,0xc0,0x36,0x3d,0xa,0x9b,0xa9,0x55,0x87,0x8a,0xba,0x4f,0x86,0x38,0x2a,0x44,0xee,0xf,0x96,0x84,0xe1,0x73,0x7d,0xf7,0x93,0x1d,0xed,0x3,0x49,0xd9,0x21,0x51,0x17,0x33,0xe0,0xd8,0xd5,0xcd,0xf5,0xe9,0x5f,0x3c,0x68,0x78,0xd2,0xc3,0x90,0xdb,0x1,0x18,0x8,0x22,0xf4,0x9f,0x74,0xc2,0xc,0x40,0xac,0x98,0x8b,0xe7,0x32,0xea,0x9e,0x3b,0x9d,0x2f,0xce,0xbb,0xd3,0x79,0xa5,0xfc,0x57,0x43,0x7b,0x45,0x5e,0x1f,0x71,0xdf,0x48,0xfd,0xc4,0x94,0x14,0xbf,0x6f,0x59,0xe2,0xca,0x4b,0x4a,0x77,0x12,0xf6,0x63,0x31,0x75,0xa7,0xf1,0x29,0x13,0x0,0xb0,0x28,0x62,0x6d,0xb2,0x7c,0x67,0xeb,0x5a,0xf0,0x99,0xc8,0xbe,0x65,0x25,0x35,0x70,0xaf,0x2b,0xb9,0x3e,0x6e,0x4d,0xe4,0x1c,0xd6,0x6,0xfb,0x81,0xae,0x92,0xa3,0x53,0xf2,0x3f,0xe6,0x91,0xec,0x64,0xf8,0x89,0xa4,0xd0,0xb6,0x2d,0x58,0x9a,0xb4,0x42,0x82,0xd,0xdc,0x95,0x97,0x34,0xda,0x69,0x5,0xa2,0x2c,0xfa,0xd4,0xe5,0x88,0xcf,0x30,0x9c,0x1a,0xff,0x60,0xa8,0x6a,0x19,0x4c,0x16,0x8e,0xa3,0x5a,0xb1,0x95,0x73,0x29,0xdd,0xf0,0xd6,0x59,0x9e,0xd0,0x21,0xdc,0x4a,0x16,0xb4,0x6d,0xf3,0xba,0x2,0x3b,0xb6,0xac,0x83,0x6c,0xca,0x5e,0x3a,0xdb,0xc5,0xcc,0x76,0xbb,0xbc,0x3c,0x26,0x11,0x33,0x84,0x1b,0xc1,0x7a,0x63,0xd7,0x7,0x7f,0x47,0xb8,0x5b,0x66,0x79,0xb,0x2f,0x3,0xaa,0xf1,0x92,0x2d,0xc8,0xce,0x5d,0xc0,0x39,0x57,0x45,0xfb,0xf9,0xeb,0x72,0x93,0x28,0xd4,0xe6,0x77,0x32,0xc7,0xf7,0xfa,0xf,0x1c,0xa0,0xfd,0x40,0x4b,0xbd,0xfe,0x68,0x20,0x53,0xf8,0x74,0x44,0x2b,0xf2,0xa6,0xed,0xbe,0xaf,0x5f,0x75,0x65,0x7c,0x94,0x88,0xb0,0xa8,0x5,0x15,0x41,0x22,0x2c,0x5c,0xa4,0x34,0xa5,0x9d,0x4e,0x6a,0x8a,0x0,0xe,0x9c,0x7e,0x90,0x60,0xee,0x62,0x23,0x38,0x6,0x80,0x35,0xa2,0xc,0x4,0xae,0xc6,0xb3,0x3e,0x2a,0x81,0xd8,0x97,0x4f,0x9a,0xf6,0x52,0xe0,0x46,0xe3,0xbf,0x9,0xe2,0x89,0xe5,0xd1,0x3d,0x71,0x1a,0x1,0xcf,0x10,0xe4,0x8d,0x27,0x96,0x6e,0x54,0x8c,0xda,0x1f,0x55,0xcd,0x7d,0x6f,0xa,0x37,0x36,0x8,0x4c,0x1e,0x8b,0xc2,0x69,0xe9,0xb9,0xb7,0x9f,0x24,0x12,0x19,0x91,0xec,0x9b,0xad,0xd9,0xf4,0x85,0xef,0xd3,0xfc,0x86,0x42,0x8f,0x2e,0xde,0x30,0x13,0x43,0xc4,0x7b,0xab,0x61,0x99,0x58,0x18,0xc3,0xb5,0x56,0xd2,0xd,0x48,0xd5,0x1d,0x82,0x67,0x6b,0x31,0x64,0x17,0x98,0xa9,0x87,0x51,0xe1,0x4d,0xb2,0xf5,0x49,0xea,0xe8,0xa1,0xdf,0x78,0x14,0xa7,0xe7,0x25,0x50,0xcb,0x70,0xff,0x3f,0xc9,0x75,0x73,0x7b,0xe6,0x4a,0x11,0x96,0x29,0xb0,0xc2,0xb8,0x94,0x3,0xfc,0xdd,0xe0,0x6c,0xd8,0xc4,0xbc,0xa0,0x3f,0xc1,0x7a,0x9d,0x87,0x88,0xaa,0xcd,0x77,0x7,0x0,0x81,0xe5,0x7e,0x60,0x38,0x17,0x71,0xd7,0xb9,0x1,0xd,0x80,0xf,0xad,0x48,0xd6,0x9a,0x6b,0xf1,0x67,0x6d,0x4b,0x25,0xe2,0xc8,0x2e,0x66,0x92,0x18,0x35,0xa,0xe1,0x2b,0xc5,0x55,0xdb,0xbb,0x31,0x27,0xb5,0x26,0x1e,0xd1,0xf5,0xe7,0x97,0x8f,0x1f,0xae,0xbe,0x99,0xfa,0x33,0x2f,0x13,0xb,0xce,0xe4,0xc7,0xde,0x56,0x1d,0x14,0x5,0xff,0xcf,0x49,0x90,0x9b,0xd3,0x43,0xe8,0xf0,0xfb,0x45,0x6,0xa7,0xb4,0x46,0x1b,0x7c,0x89,0x41,0x4c,0x6f,0x93,0xcc,0x5d,0x50,0x42,0x28,0xc9,0xec,0x82,0x40,0xfe,0x24,0xc,0xa9,0x9f,0xd2,0x79,0x2,0x52,0xf7,0xb3,0x30,0xa5,0xb1,0xd4,0x8d,0x8c,0xee,0xa4,0xc6,0x76,0xef,0xd5,0x61,0x37,0x36,0x5f,0x2d,0x9c,0xba,0xa1,0xab,0x74,0x6a,0x5e,0xca,0x86,0xb2,0x4,0x32,0x59,0x5b,0xe9,0x58,0xfd,0xf4,0x2c,0x4d,0x21,0x91,0x85,0x63,0x3a,0x15,0xbf,0x8,0x7d,0x8e,0x3b,0xb7,0x19,0x98,0xd9,0xbd,0x83,0x44,0xcb,0x72,0x84,0x9e,0x5c,0x70,0xeb,0xc3,0x64,0x1c,0xaf,0x51,0xf2,0x1a,0x53,0xf6,0x5a,0x4e,0x9,0x12,0x23,0xea,0x3c,0x8a,0xd0,0xac,0xdf,0xa6,0x6e,0xdc,0x39,0x69,0xed,0xf3,0xb6,0xa3,0xe3,0xe,0x78,0x10,0xc0,0x22,0xda,0xa8,0x8b,0x7f,0xf8,0x34,0xf9,0x65,0x95,0x68,0x54,0x3d,0x47,0x62,0x16,0x3e,0x4f,0x2a,0xa2,0x20,0x57,0x17,0xd0,0x5f,0x79,0xc3,0x55,0xa8,0x59,0x38,0xd3,0x2a,0x7,0x54,0xa0,0xfa,0x1c,0x43,0xe5,0xa,0x25,0x4c,0x52,0xb3,0xd7,0x7a,0xe4,0x3d,0x9f,0x3f,0xb2,0x8b,0x33,0xf3,0x48,0x92,0xd,0xf6,0x8e,0x5e,0xea,0x35,0x32,0xff,0x45,0xba,0x98,0xaf,0xb5,0xa4,0x1b,0x78,0x23,0x49,0xd4,0x47,0x41,0xef,0xd2,0x31,0xce,0x8a,0xa6,0x82,0xf0,0xfe,0x6f,0x5d,0xa1,0x73,0x7e,0x4e,0xbb,0x72,0xcc,0xde,0xb0,0x1a,0xfb,0x62,0x70,0x71,0xda,0xa9,0xe1,0x7b,0xa2,0xcd,0xfd,0x74,0x29,0x95,0x86,0x77,0x34,0xc2,0xc9,0x21,0x39,0x1,0x1d,0xab,0xc8,0x9c,0x8c,0x26,0x37,0x64,0x2f,0xf5,0xec,0xfc,0xd6,0x15,0x87,0x89,0x3,0x67,0xe9,0x19,0xf7,0xbd,0x2d,0xd5,0xa5,0xe3,0xc7,0x14,0x2c,0x3a,0x4f,0x27,0x8d,0x51,0x8,0xa3,0xb7,0x8f,0xb1,0xaa,0xeb,0x85,0x2b,0xbc,0x9,0x0,0x6b,0x80,0x36,0xf8,0xb4,0x58,0x6c,0x7f,0x13,0xc6,0x1e,0x6a,0xcf,0x69,0xdb,0x53,0x5,0xdd,0xe7,0xf4,0x44,0xdc,0x96,0x99,0x46,0x88,0x93,0x1f,0xae,0x4,0x6d,0x30,0x60,0xe0,0x4b,0x9b,0xad,0x16,0x3e,0xbf,0xbe,0x83,0xe6,0x2,0x97,0xc5,0x81,0xf,0x75,0x5a,0x66,0x57,0xa7,0x6,0xcb,0x12,0x65,0x18,0x90,0xc,0x7d,0x50,0x24,0x3c,0x4a,0x91,0xd1,0xc1,0x84,0x5b,0xdf,0x4d,0xca,0x9a,0xb9,0x10,0xe8,0x22,0xf2,0xd8,0xe,0x20,0x11,0x7c,0x3b,0xc4,0x68,0xee,0xb,0x94,0x5c,0x9e,0xed,0xb8,0xe2,0x42,0xd9,0xac,0x6e,0x40,0xb6,0x76,0xf9,0x28,0x61,0x63,0xc0,0x2e,0x9d,0xf1,0x56,0xb7,0x11,0xfe,0xd1,0xb8,0xa6,0x47,0x23,0x8e,0x10,0xc9,0x6b,0xcb,0x46,0x7f,0xc7,0xe3,0x24,0xab,0x8d,0x37,0xa1,0x5c,0xad,0xcc,0x27,0xde,0xf3,0xa0,0x54,0xe,0xe8,0x50,0xef,0x8c,0xd7,0xbd,0x20,0xb3,0xb5,0x1b,0x26,0xc5,0x3a,0x7e,0x52,0x76,0x4,0x7,0xbc,0x66,0xf9,0x2,0x7a,0xaa,0x1e,0xc1,0xc6,0xb,0xb1,0x4e,0x6c,0x5b,0x41,0x85,0x2e,0x5d,0x15,0x8f,0x56,0x39,0x9,0x80,0xdd,0x61,0x72,0x83,0xc0,0x36,0x3d,0xa,0x9b,0xa9,0x55,0x87,0x8a,0xba,0x4f,0x86,0x38,0x2a,0x44,0xee,0xf,0x96,0x84,0xe1,0x73,0x7d,0xf7,0x93,0x1d,0xed,0x3,0x49,0xd9,0x21,0x51,0x17,0x33,0xe0,0xd8,0xd5,0xcd,0xf5,0xe9,0x5f,0x3c,0x68,0x78,0xd2,0xc3,0x90,0xdb,0x1,0x18,0x8,0x22,0xf4,0x9f,0x74,0xc2,0xc,0x40,0xac,0x98,0x8b,0xe7,0x32,0xea,0x9e,0x3b,0x9d,0x2f,0xce,0xbb,0xd3,0x79,0xa5,0xfc,0x57,0x43,0x7b,0x45,0x5e,0x1f,0x71,0xdf,0x48,0xfd,0xc4,0x94,0x14,0xbf,0x6f,0x59,0xe2,0xca,0x4b,0x4a,0x77,0x12,0xf6,0x63,0x31,0x75,0xa7,0xf1,0x29,0x13,0x0,0xb0,0x28,0x62,0x6d,0xb2,0x7c,0x67,0xeb,0x5a,0xf0,0x99,0xc8,0xbe,0x65,0x25,0x35,0x70,0xaf,0x2b,0xb9,0x3e,0x6e,0x4d,0xe4,0x1c,0xd6,0x6,0xfb,0x81,0xae,0x92,0xa3,0x53,0xf2,0x3f,0xe6,0x91,0xec,0x64,0xf8,0x89,0xa4,0xd0,0xb6,0x2d,0x58,0x9a,0xb4,0x42,0x82,0xd,0xdc,0x95,0x97,0x34,0xda,0x69,0x5,0xa2,0x2c,0xfa,0xd4,0xe5,0x88,0xcf,0x30,0x9c,0x1a,0xff,0x60,0xa8,0x6a,0x19,0x4c,0x16,0xb3,0x9e,0x67,0x8c,0xa8,0x4e,0x14,0xe0,0xcd,0xeb,0x64,0xa3,0xed,0x1c,0xe1,0x77,0x2b,0x89,0x50,0xce,0x87,0x3f,0x6,0x8b,0x91,0xbe,0x51,0xf7,0x63,0x7,0xe6,0xf8,0xf1,0x4b,0x86,0x81,0x1,0x1b,0x2c,0xe,0xb9,0x26,0xfc,0x47,0x5e,0xea,0x3a,0x42,0x7a,0x85,0x66,0x5b,0x44,0x36,0x12,0x3e,0x97,0xcc,0xaf,0x10,0xf5,0xf3,0x60,0xfd,0x4,0x6a,0x78,0xc6,0xc4,0xd6,0x4f,0xae,0x15,0xe9,0xdb,0x4a,0xf,0xfa,0xca,0xc7,0x32,0x21,0x9d,0xc0,0x7d,0x76,0x80,0xc3,0x55,0x1d,0x6e,0xc5,0x49,0x79,0x16,0xcf,0x9b,0xd0,0x83,0x92,0x62,0x48,0x58,0x41,0xa9,0xb5,0x8d,0x95,0x38,0x28,0x7c,0x1f,0x11,0x61,0x99,0x9,0x98,0xa0,0x73,0x57,0xb7,0x3d,0x33,0xa1,0x43,0xad,0x5d,0xd3,0x5f,0x1e,0x5,0x3b,0xbd,0x8,0x9f,0x31,0x39,0x93,0xfb,0x8e,0x3,0x17,0xbc,0xe5,0xaa,0x72,0xa7,0xcb,0x6f,0xdd,0x7b,0xde,0x82,0x34,0xdf,0xb4,0xd8,0xec,0x0,0x4c,0x27,0x3c,0xf2,0x2d,0xd9,0xb0,0x1a,0xab,0x53,0x69,0xb1,0xe7,0x22,0x68,0xf0,0x40,0x52,0x37,0xa,0xb,0x35,0x71,0x23,0xb6,0xff,0x54,0xd4,0x84,0x8a,0xa2,0x19,0x2f,0x24,0xac,0xd1,0xa6,0x90,0xe4,0xc9,0xb8,0xd2,0xee,0xc1,0xbb,0x7f,0xb2,0x13,0xe3,0xd,0x2e,0x7e,0xf9,0x46,0x96,0x5c,0xa4,0x65,0x25,0xfe,0x88,0x6b,0xef,0x30,0x75,0xe8,0x20,0xbf,0x5a,0x56,0xc,0x59,0x2a,0xa5,0x94,0xba,0x6c,0xdc,0x70,0x8f,0xc8,0x74,0xd7,0xd5,0x9c,0xe2,0x45,0x29,0x9a,0xda,0x18,0x6d,0xf6,0x4d,0xc2,0x2,0xf4,0xb8,0xbe,0xb6,0x2b,0x87,0xdc,0x5b,0xe4,0x7d,0xf,0x75,0x59,0xce,0x31,0x10,0x2d,0xa1,0x15,0x9,0x71,0x6d,0xf2,0xc,0xb7,0x50,0x4a,0x45,0x67,0x0,0xba,0xca,0xcd,0x4c,0x28,0xb3,0xad,0xf5,0xda,0xbc,0x1a,0x74,0xcc,0xc0,0x4d,0xc2,0x60,0x85,0x1b,0x57,0xa6,0x3c,0xaa,0xa0,0x86,0xe8,0x2f,0x5,0xe3,0xab,0x5f,0xd5,0xf8,0xc7,0x2c,0xe6,0x8,0x98,0x16,0x76,0xfc,0xea,0x78,0xeb,0xd3,0x1c,0x38,0x2a,0x5a,0x42,0xd2,0x63,0x73,0x54,0x37,0xfe,0xe2,0xde,0xc6,0x3,0x29,0xa,0x13,0x9b,0xd0,0xd9,0xc8,0x32,0x2,0x84,0x5d,0x56,0x1e,0x8e,0x25,0x3d,0x36,0x88,0xcb,0x6a,0x79,0x8b,0xd6,0xb1,0x44,0x8c,0x81,0xa2,0x5e,0x1,0x90,0x9d,0x8f,0xe5,0x4,0x21,0x4f,0x8d,0x33,0xe9,0xc1,0x64,0x52,0x1f,0xb4,0xcf,0x9f,0x3a,0x7e,0xfd,0x68,0x7c,0x19,0x40,0x41,0x23,0x69,0xb,0xbb,0x22,0x18,0xac,0xfa,0xfb,0x92,0xe0,0x51,0x77,0x6c,0x66,0xb9,0xa7,0x93,0x7,0x4b,0x7f,0xc9,0xff,0x94,0x96,0x24,0x95,0x30,0x39,0xe1,0x80,0xec,0x5c,0x48,0xae,0xf7,0xd8,0x72,0xc5,0xb0,0x43,0xf6,0x7a,0xd4,0x55,0x14,0x70,0x4e,0x89,0x6,0xbf,0x49,0x53,0x91,0xbd,0x26,0xe,0xa9,0xd1,0x62,0x9c,0x3f,0xd7,0x9e,0x3b,0x97,0x83,0xc4,0xdf,0xee,0x27,0xf1,0x47,0x1d,0x61,0x12,0x6b,0xa3,0x11,0xf4,0xa4,0x20,0x3e,0x7b,0x6e,0x2e,0xc3,0xb5,0xdd,0xd,0xef,0x17,0x65,0x46,0xb2,0x35,0xf9,0x34,0xa8,0x58,0xa5,0x99,0xf0,0x8a,0xaf,0xdb,0xf3,0x82,0xe7,0x6f,0xed,0x9a,0x38,0xff,0x70,0x56,0xec,0x7a,0x87,0x76,0x17,0xfc,0x5,0x28,0x7b,0x8f,0xd5,0x33,0x6c,0xca,0x25,0xa,0x63,0x7d,0x9c,0xf8,0x55,0xcb,0x12,0xb0,0x10,0x9d,0xa4,0x1c,0xdc,0x67,0xbd,0x22,0xd9,0xa1,0x71,0xc5,0x1a,0x1d,0xd0,0x6a,0x95,0xb7,0x80,0x9a,0x8b,0x34,0x57,0xc,0x66,0xfb,0x68,0x6e,0xc0,0xfd,0x1e,0xe1,0xa5,0x89,0xad,0xdf,0xd1,0x40,0x72,0x8e,0x5c,0x51,0x61,0x94,0x5d,0xe3,0xf1,0x9f,0x35,0xd4,0x4d,0x5f,0x5e,0xf5,0x86,0xce,0x54,0x8d,0xe2,0xd2,0x5b,0x6,0xba,0xa9,0x58,0x1b,0xed,0xe6,0xe,0x16,0x2e,0x32,0x84,0xe7,0xb3,0xa3,0x9,0x18,0x4b,0x0,0xda,0xc3,0xd3,0xf9,0x3a,0xa8,0xa6,0x2c,0x48,0xc6,0x36,0xd8,0x92,0x2,0xfa,0x8a,0xcc,0xe8,0x3b,0x3,0x15,0x60,0x8,0xa2,0x7e,0x27,0x8c,0x98,0xa0,0x9e,0x85,0xc4,0xaa,0x4,0x93,0x26,0x2f,0x44,0xaf,0x19,0xd7,0x9b,0x77,0x43,0x50,0x3c,0xe9,0x31,0x45,0xe0,0x46,0xf4,0x7c,0x2a,0xf2,0xc8,0xdb,0x6b,0xf3,0xb9,0xb6,0x69,0xa7,0xbc,0x30,0x81,0x2b,0x42,0x1f,0x4f,0xcf,0x64,0xb4,0x82,0x39,0x11,0x90,0x91,0xac,0xc9,0x2d,0xb8,0xea,0xae,0x20,0x5a,0x75,0x49,0x78,0x88,0x29,0xe4,0x3d,0x4a,0x37,0xbf,0x23,0x52,0x7f,0xb,0x13,0x65,0xbe,0xfe,0xee,0xab,0x74,0xf0,0x62,0xe5,0xb5,0x96,0x3f,0xc7,0xd,0xdd,0xf7,0x21,0xf,0x3e,0x53,0x14,0xeb,0x47,0xc1,0x24,0xbb,0x73,0xb1,0xc2,0x97,0xcd,0x6d,0xf6,0x83,0x41,0x6f,0x99,0x59,0xd6,0x7,0x4e,0x4c,0xef,0x1,0xb2,0xde,0x79,0xff,0x59,0xb6,0x99,0xf0,0xee,0xf,0x6b,0xc6,0x58,0x81,0x23,0x83,0xe,0x37,0x8f,0xab,0x6c,0xe3,0xc5,0x7f,0xe9,0x14,0xe5,0x84,0x6f,0x96,0xbb,0xe8,0x1c,0x46,0xa0,0x18,0xa7,0xc4,0x9f,0xf5,0x68,0xfb,0xfd,0x53,0x6e,0x8d,0x72,0x36,0x1a,0x3e,0x4c,0x4f,0xf4,0x2e,0xb1,0x4a,0x32,0xe2,0x56,0x89,0x8e,0x43,0xf9,0x6,0x24,0x13,0x9,0xcd,0x66,0x15,0x5d,0xc7,0x1e,0x71,0x41,0xc8,0x95,0x29,0x3a,0xcb,0x88,0x7e,0x75,0x42,0xd3,0xe1,0x1d,0xcf,0xc2,0xf2,0x7,0xce,0x70,0x62,0xc,0xa6,0x47,0xde,0xcc,0xa9,0x3b,0x35,0xbf,0xdb,0x55,0xa5,0x4b,0x1,0x91,0x69,0x19,0x5f,0x7b,0xa8,0x90,0x9d,0x85,0xbd,0xa1,0x17,0x74,0x20,0x30,0x9a,0x8b,0xd8,0x93,0x49,0x50,0x40,0x6a,0xbc,0xd7,0x3c,0x8a,0x44,0x8,0xe4,0xd0,0xc3,0xaf,0x7a,0xa2,0xd6,0x73,0xd5,0x67,0x86,0xf3,0x9b,0x31,0xed,0xb4,0x1f,0xb,0x33,0xd,0x16,0x57,0x39,0x97,0x0,0xb5,0x8c,0xdc,0x5c,0xf7,0x27,0x11,0xaa,0x82,0x3,0x2,0x3f,0x5a,0xbe,0x2b,0x79,0x3d,0xef,0xb9,0x61,0x5b,0x48,0xf8,0x60,0x2a,0x25,0xfa,0x34,0x2f,0xa3,0x12,0xb8,0xd1,0x80,0xf6,0x2d,0x6d,0x7d,0x38,0xe7,0x63,0xf1,0x76,0x26,0x5,0xac,0x54,0x9e,0x4e,0xb3,0xc9,0xe6,0xda,0xeb,0x1b,0xba,0x77,0xae,0xd9,0xa4,0x2c,0xb0,0xc1,0xec,0x98,0xfe,0x65,0x10,0xd2,0xfc,0xa,0xca,0x45,0x94,0xdd,0xdf,0x7c,0x92,0x21,0x4d,0xea,0x64,0xb2,0x9c,0xad,0xc0,0x87,0x78,0xd4,0x52,0xb7,0x28,0xe0,0x22,0x51,0x4,0x5e,0x61,0x4c,0xb5,0x5e,0x7a,0x9c,0xc6,0x32,0x1f,0x39,0xb6,0x71,0x3f,0xce,0x33,0xa5,0xf9,0x5b,0x82,0x1c,0x55,0xed,0xd4,0x59,0x43,0x6c,0x83,0x25,0xb1,0xd5,0x34,0x2a,0x23,0x99,0x54,0x53,0xd3,0xc9,0xfe,0xdc,0x6b,0xf4,0x2e,0x95,0x8c,0x38,0xe8,0x90,0xa8,0x57,0xb4,0x89,0x96,0xe4,0xc0,0xec,0x45,0x1e,0x7d,0xc2,0x27,0x21,0xb2,0x2f,0xd6,0xb8,0xaa,0x14,0x16,0x4,0x9d,0x7c,0xc7,0x3b,0x9,0x98,0xdd,0x28,0x18,0x15,0xe0,0xf3,0x4f,0x12,0xaf,0xa4,0x52,0x11,0x87,0xcf,0xbc,0x17,0x9b,0xab,0xc4,0x1d,0x49,0x2,0x51,0x40,0xb0,0x9a,0x8a,0x93,0x7b,0x67,0x5f,0x47,0xea,0xfa,0xae,0xcd,0xc3,0xb3,0x4b,0xdb,0x4a,0x72,0xa1,0x85,0x65,0xef,0xe1,0x73,0x91,0x7f,0x8f,0x1,0x8d,0xcc,0xd7,0xe9,0x6f,0xda,0x4d,0xe3,0xeb,0x41,0x29,0x5c,0xd1,0xc5,0x6e,0x37,0x78,0xa0,0x75,0x19,0xbd,0xf,0xa9,0xc,0x50,0xe6,0xd,0x66,0xa,0x3e,0xd2,0x9e,0xf5,0xee,0x20,0xff,0xb,0x62,0xc8,0x79,0x81,0xbb,0x63,0x35,0xf0,0xba,0x22,0x92,0x80,0xe5,0xd8,0xd9,0xe7,0xa3,0xf1,0x64,0x2d,0x86,0x6,0x56,0x58,0x70,0xcb,0xfd,0xf6,0x7e,0x3,0x74,0x42,0x36,0x1b,0x6a,0x0,0x3c,0x13,0x69,0xad,0x60,0xc1,0x31,0xdf,0xfc,0xac,0x2b,0x94,0x44,0x8e,0x76,0xb7,0xf7,0x2c,0x5a,0xb9,0x3d,0xe2,0xa7,0x3a,0xf2,0x6d,0x88,0x84,0xde,0x8b,0xf8,0x77,0x46,0x68,0xbe,0xe,0xa2,0x5d,0x1a,0xa6,0x5,0x7,0x4e,0x30,0x97,0xfb,0x48,0x8,0xca,0xbf,0x24,0x9f,0x10,0xd0,0x26,0x4,0x2,0xa,0x97,0x3b,0x60,0xe7,0x58,0xc1,0xb3,0xc9,0xe5,0x72,0x8d,0xac,0x91,0x1d,0xa9,0xb5,0xcd,0xd1,0x4e,0xb0,0xb,0xec,0xf6,0xf9,0xdb,0xbc,0x6,0x76,0x71,0xf0,0x94,0xf,0x11,0x49,0x66,0x0,0xa6,0xc8,0x70,0x7c,0xf1,0x7e,0xdc,0x39,0xa7,0xeb,0x1a,0x80,0x16,0x1c,0x3a,0x54,0x93,0xb9,0x5f,0x17,0xe3,0x69,0x44,0x7b,0x90,0x5a,0xb4,0x24,0xaa,0xca,0x40,0x56,0xc4,0x57,0x6f,0xa0,0x84,0x96,0xe6,0xfe,0x6e,0xdf,0xcf,0xe8,0x8b,0x42,0x5e,0x62,0x7a,0xbf,0x95,0xb6,0xaf,0x27,0x6c,0x65,0x74,0x8e,0xbe,0x38,0xe1,0xea,0xa2,0x32,0x99,0x81,0x8a,0x34,0x77,0xd6,0xc5,0x37,0x6a,0xd,0xf8,0x30,0x3d,0x1e,0xe2,0xbd,0x2c,0x21,0x33,0x59,0xb8,0x9d,0xf3,0x31,0x8f,0x55,0x7d,0xd8,0xee,0xa3,0x8,0x73,0x23,0x86,0xc2,0x41,0xd4,0xc0,0xa5,0xfc,0xfd,0x9f,0xd5,0xb7,0x7,0x9e,0xa4,0x10,0x46,0x47,0x2e,0x5c,0xed,0xcb,0xd0,0xda,0x5,0x1b,0x2f,0xbb,0xf7,0xc3,0x75,0x43,0x28,0x2a,0x98,0x29,0x8c,0x85,0x5d,0x3c,0x50,0xe0,0xf4,0x12,0x4b,0x64,0xce,0x79,0xc,0xff,0x4a,0xc6,0x68,0xe9,0xa8,0xcc,0xf2,0x35,0xba,0x3,0xf5,0xef,0x2d,0x1,0x9a,0xb2,0x15,0x6d,0xde,0x20,0x83,0x6b,0x22,0x87,0x2b,0x3f,0x78,0x63,0x52,0x9b,0x4d,0xfb,0xa1,0xdd,0xae,0xd7,0x1f,0xad,0x48,0x18,0x9c,0x82,0xc7,0xd2,0x92,0x7f,0x9,0x61,0xb1,0x53,0xab,0xd9,0xfa,0xe,0x89,0x45,0x88,0x14,0xe4,0x19,0x25,0x4c,0x36,0x13,0x67,0x4f,0x3e,0x5b,0xd3,0x51,0x26,0xb2,0x75,0xfa,0xdc,0x66,0xf0,0xd,0xfc,0x9d,0x76,0x8f,0xa2,0xf1,0x5,0x5f,0xb9,0xe6,0x40,0xaf,0x80,0xe9,0xf7,0x16,0x72,0xdf,0x41,0x98,0x3a,0x9a,0x17,0x2e,0x96,0x56,0xed,0x37,0xa8,0x53,0x2b,0xfb,0x4f,0x90,0x97,0x5a,0xe0,0x1f,0x3d,0xa,0x10,0x1,0xbe,0xdd,0x86,0xec,0x71,0xe2,0xe4,0x4a,0x77,0x94,0x6b,0x2f,0x3,0x27,0x55,0x5b,0xca,0xf8,0x4,0xd6,0xdb,0xeb,0x1e,0xd7,0x69,0x7b,0x15,0xbf,0x5e,0xc7,0xd5,0xd4,0x7f,0xc,0x44,0xde,0x7,0x68,0x58,0xd1,0x8c,0x30,0x23,0xd2,0x91,0x67,0x6c,0x84,0x9c,0xa4,0xb8,0xe,0x6d,0x39,0x29,0x83,0x92,0xc1,0x8a,0x50,0x49,0x59,0x73,0xb0,0x22,0x2c,0xa6,0xc2,0x4c,0xbc,0x52,0x18,0x88,0x70,0x0,0x46,0x62,0xb1,0x89,0x9f,0xea,0x82,0x28,0xf4,0xad,0x6,0x12,0x2a,0x14,0xf,0x4e,0x20,0x8e,0x19,0xac,0xa5,0xce,0x25,0x93,0x5d,0x11,0xfd,0xc9,0xda,0xb6,0x63,0xbb,0xcf,0x6a,0xcc,0x7e,0xf6,0xa0,0x78,0x42,0x51,0xe1,0x79,0x33,0x3c,0xe3,0x2d,0x36,0xba,0xb,0xa1,0xc8,0x95,0xc5,0x45,0xee,0x3e,0x8,0xb3,0x9b,0x1a,0x1b,0x26,0x43,0xa7,0x32,0x60,0x24,0xaa,0xd0,0xff,0xc3,0xf2,0x2,0xa3,0x6e,0xb7,0xc0,0xbd,0x35,0xa9,0xd8,0xf5,0x81,0x99,0xef,0x34,0x74,0x64,0x21,0xfe,0x7a,0xe8,0x6f,0x3f,0x1c,0xb5,0x4d,0x87,0x57,0x7d,0xab,0x85,0xb4,0xd9,0x9e,0x61,0xcd,0x4b,0xae,0x31,0xf9,0x3b,0x48,0x1d,0x47,0xe7,0x7c,0x9,0xcb,0xe5,0x13,0xd3,0x5c,0x8d,0xc4,0xc6,0x65,0x8b,0x38,0x54,0xf3,0x67,0xc1,0x2e,0x1,0x68,0x76,0x97,0xf3,0x5e,0xc0,0x19,0xbb,0x1b,0x96,0xaf,0x17,0x33,0xf4,0x7b,0x5d,0xe7,0x71,0x8c,0x7d,0x1c,0xf7,0xe,0x23,0x70,0x84,0xde,0x38,0x80,0x3f,0x5c,0x7,0x6d,0xf0,0x63,0x65,0xcb,0xf6,0x15,0xea,0xae,0x82,0xa6,0xd4,0xd7,0x6c,0xb6,0x29,0xd2,0xaa,0x7a,0xce,0x11,0x16,0xdb,0x61,0x9e,0xbc,0x8b,0x91,0x55,0xfe,0x8d,0xc5,0x5f,0x86,0xe9,0xd9,0x50,0xd,0xb1,0xa2,0x53,0x10,0xe6,0xed,0xda,0x4b,0x79,0x85,0x57,0x5a,0x6a,0x9f,0x56,0xe8,0xfa,0x94,0x3e,0xdf,0x46,0x54,0x31,0xa3,0xad,0x27,0x43,0xcd,0x3d,0xd3,0x99,0x9,0xf1,0x81,0xc7,0xe3,0x30,0x8,0x5,0x1d,0x25,0x39,0x8f,0xec,0xb8,0xa8,0x2,0x13,0x40,0xb,0xd1,0xc8,0xd8,0xf2,0x24,0x4f,0xa4,0x12,0xdc,0x90,0x7c,0x48,0x5b,0x37,0xe2,0x3a,0x4e,0xeb,0x4d,0xff,0x1e,0x6b,0x3,0xa9,0x75,0x2c,0x87,0x93,0xab,0x95,0x8e,0xcf,0xa1,0xf,0x98,0x2d,0x14,0x44,0xc4,0x6f,0xbf,0x89,0x32,0x1a,0x9b,0x9a,0xa7,0xc2,0x26,0xb3,0xe1,0xa5,0x77,0x21,0xf9,0xc3,0xd0,0x60,0xf8,0xb2,0xbd,0x62,0xac,0xb7,0x3b,0x8a,0x20,0x49,0x18,0x6e,0xb5,0xf5,0xe5,0xa0,0x7f,0xfb,0x69,0xee,0xbe,0x9d,0x34,0xcc,0x6,0xd6,0x2b,0x51,0x7e,0x42,0x73,0x83,0x22,0xef,0x36,0x41,0x3c,0xb4,0x28,0x59,0x74,0x0,0x66,0xfd,0x88,0x4a,0x64,0x92,0x52,0xdd,0xc,0x45,0x47,0xe4,0xa,0xb9,0xd5,0x72,0xfc,0x2a,0x4,0x35,0x58,0x1f,0xe0,0x4c,0xca,0x2f,0xb0,0x78,0xba,0xc9,0x9c,0xc6,0x41,0x6c,0x95,0x7e,0x5a,0xbc,0xe6,0x12,0x3f,0x19,0x96,0x51,0x1f,0xee,0x13,0x85,0xd9,0x7b,0xa2,0x3c,0x75,0xcd,0xf4,0x79,0x63,0x4c,0xa3,0x5,0x91,0xf5,0x14,0xa,0x3,0xb9,0x74,0x73,0xf3,0xe9,0xde,0xfc,0x4b,0xd4,0xe,0xb5,0xac,0x18,0xc8,0xb0,0x88,0x77,0x94,0xa9,0xb6,0xc4,0xe0,0xcc,0x65,0x3e,0x5d,0xe2,0x7,0x1,0x92,0xf,0xf6,0x98,0x8a,0x34,0x36,0x24,0xbd,0x5c,0xe7,0x1b,0x29,0xb8,0xfd,0x8,0x38,0x35,0xc0,0xd3,0x6f,0x32,0x8f,0x84,0x72,0x31,0xa7,0xef,0x9c,0x37,0xbb,0x8b,0xe4,0x3d,0x69,0x22,0x71,0x60,0x90,0xba,0xaa,0xb3,0x5b,0x47,0x7f,0x67,0xca,0xda,0x8e,0xed,0xe3,0x93,0x6b,0xfb,0x6a,0x52,0x81,0xa5,0x45,0xcf,0xc1,0x53,0xb1,0x5f,0xaf,0x21,0xad,0xec,0xf7,0xc9,0x4f,0xfa,0x6d,0xc3,0xcb,0x61,0x9,0x7c,0xf1,0xe5,0x4e,0x17,0x58,0x80,0x55,0x39,0x9d,0x2f,0x89,0x2c,0x70,0xc6,0x2d,0x46,0x2a,0x1e,0xf2,0xbe,0xd5,0xce,0x0,0xdf,0x2b,0x42,0xe8,0x59,0xa1,0x9b,0x43,0x15,0xd0,0x9a,0x2,0xb2,0xa0,0xc5,0xf8,0xf9,0xc7,0x83,0xd1,0x44,0xd,0xa6,0x26,0x76,0x78,0x50,0xeb,0xdd,0xd6,0x5e,0x23,0x54,0x62,0x16,0x3b,0x4a,0x20,0x1c,0x33,0x49,0x8d,0x40,0xe1,0x11,0xff,0xdc,0x8c,0xb,0xb4,0x64,0xae,0x56,0x97,0xd7,0xc,0x7a,0x99,0x1d,0xc2,0x87,0x1a,0xd2,0x4d,0xa8,0xa4,0xfe,0xab,0xd8,0x57,0x66,0x48,0x9e,0x2e,0x82,0x7d,0x3a,0x86,0x25,0x27,0x6e,0x10,0xb7,0xdb,0x68,0x28,0xea,0x9f,0x4,0xbf,0x30,0xf0,0x6,0xf4,0xf2,0xfa,0x67,0xcb,0x90,0x17,0xa8,0x31,0x43,0x39,0x15,0x82,0x7d,0x5c,0x61,0xed,0x59,0x45,0x3d,0x21,0xbe,0x40,0xfb,0x1c,0x6,0x9,0x2b,0x4c,0xf6,0x86,0x81,0x0,0x64,0xff,0xe1,0xb9,0x96,0xf0,0x56,0x38,0x80,0x8c,0x1,0x8e,0x2c,0xc9,0x57,0x1b,0xea,0x70,0xe6,0xec,0xca,0xa4,0x63,0x49,0xaf,0xe7,0x13,0x99,0xb4,0x8b,0x60,0xaa,0x44,0xd4,0x5a,0x3a,0xb0,0xa6,0x34,0xa7,0x9f,0x50,0x74,0x66,0x16,0xe,0x9e,0x2f,0x3f,0x18,0x7b,0xb2,0xae,0x92,0x8a,0x4f,0x65,0x46,0x5f,0xd7,0x9c,0x95,0x84,0x7e,0x4e,0xc8,0x11,0x1a,0x52,0xc2,0x69,0x71,0x7a,0xc4,0x87,0x26,0x35,0xc7,0x9a,0xfd,0x8,0xc0,0xcd,0xee,0x12,0x4d,0xdc,0xd1,0xc3,0xa9,0x48,0x6d,0x3,0xc1,0x7f,0xa5,0x8d,0x28,0x1e,0x53,0xf8,0x83,0xd3,0x76,0x32,0xb1,0x24,0x30,0x55,0xc,0xd,0x6f,0x25,0x47,0xf7,0x6e,0x54,0xe0,0xb6,0xb7,0xde,0xac,0x1d,0x3b,0x20,0x2a,0xf5,0xeb,0xdf,0x4b,0x7,0x33,0x85,0xb3,0xd8,0xda,0x68,0xd9,0x7c,0x75,0xad,0xcc,0xa0,0x10,0x4,0xe2,0xbb,0x94,0x3e,0x89,0xfc,0xf,0xba,0x36,0x98,0x19,0x58,0x3c,0x2,0xc5,0x4a,0xf3,0x5,0x1f,0xdd,0xf1,0x6a,0x42,0xe5,0x9d,0x2e,0xd0,0x73,0x9b,0xd2,0x77,0xdb,0xcf,0x88,0x93,0xa2,0x6b,0xbd,0xb,0x51,0x2d,0x5e,0x27,0xef,0x5d,0xb8,0xe8,0x6c,0x72,0x37,0x22,0x62,0x8f,0xf9,0x91,0x41,0xa3,0x5b,0x29,0xa,0xfe,0x79,0xb5,0x78,0xe4,0x14,0xe9,0xd5,0xbc,0xc6,0xe3,0x97,0xbf,0xce,0xab,0x23,0xa1,0xd6,0xfc,0x3b,0xb4,0x92,0x28,0xbe,0x43,0xb2,0xd3,0x38,0xc1,0xec,0xbf,0x4b,0x11,0xf7,0xa8,0xe,0xe1,0xce,0xa7,0xb9,0x58,0x3c,0x91,0xf,0xd6,0x74,0xd4,0x59,0x60,0xd8,0x18,0xa3,0x79,0xe6,0x1d,0x65,0xb5,0x1,0xde,0xd9,0x14,0xae,0x51,0x73,0x44,0x5e,0x4f,0xf0,0x93,0xc8,0xa2,0x3f,0xac,0xaa,0x4,0x39,0xda,0x25,0x61,0x4d,0x69,0x1b,0x15,0x84,0xb6,0x4a,0x98,0x95,0xa5,0x50,0x99,0x27,0x35,0x5b,0xf1,0x10,0x89,0x9b,0x9a,0x31,0x42,0xa,0x90,0x49,0x26,0x16,0x9f,0xc2,0x7e,0x6d,0x9c,0xdf,0x29,0x22,0xca,0xd2,0xea,0xf6,0x40,0x23,0x77,0x67,0xcd,0xdc,0x8f,0xc4,0x1e,0x7,0x17,0x3d,0xfe,0x6c,0x62,0xe8,0x8c,0x2,0xf2,0x1c,0x56,0xc6,0x3e,0x4e,0x8,0x2c,0xff,0xc7,0xd1,0xa4,0xcc,0x66,0xba,0xe3,0x48,0x5c,0x64,0x5a,0x41,0x0,0x6e,0xc0,0x57,0xe2,0xeb,0x80,0x6b,0xdd,0x13,0x5f,0xb3,0x87,0x94,0xf8,0x2d,0xf5,0x81,0x24,0x82,0x30,0xb8,0xee,0x36,0xc,0x1f,0xaf,0x37,0x7d,0x72,0xad,0x63,0x78,0xf4,0x45,0xef,0x86,0xdb,0x8b,0xb,0xa0,0x70,0x46,0xfd,0xd5,0x54,0x55,0x68,0xd,0xe9,0x7c,0x2e,0x6a,0xe4,0x9e,0xb1,0x8d,0xbc,0x4c,0xed,0x20,0xf9,0x8e,0xf3,0x7b,0xe7,0x96,0xbb,0xcf,0xd7,0xa1,0x7a,0x3a,0x2a,0x6f,0xb0,0x34,0xa6,0x21,0x71,0x52,0xfb,0x3,0xc9,0x19,0x33,0xe5,0xcb,0xfa,0x97,0xd0,0x2f,0x83,0x5,0xe0,0x7f,0xb7,0x75,0x6,0x53,0x9,0xa9,0x32,0x47,0x85,0xab,0x5d,0x9d,0x12,0xc3,0x8a,0x88,0x2b,0xc5,0x76,0x1a,0xbd,0x93,0x35,0xda,0xf5,0x9c,0x82,0x63,0x7,0xaa,0x34,0xed,0x4f,0xef,0x62,0x5b,0xe3,0xc7,0x0,0x8f,0xa9,0x13,0x85,0x78,0x89,0xe8,0x3,0xfa,0xd7,0x84,0x70,0x2a,0xcc,0x74,0xcb,0xa8,0xf3,0x99,0x4,0x97,0x91,0x3f,0x2,0xe1,0x1e,0x5a,0x76,0x52,0x20,0x23,0x98,0x42,0xdd,0x26,0x5e,0x8e,0x3a,0xe5,0xe2,0x2f,0x95,0x6a,0x48,0x7f,0x65,0xa1,0xa,0x79,0x31,0xab,0x72,0x1d,0x2d,0xa4,0xf9,0x45,0x56,0xa7,0xe4,0x12,0x19,0x2e,0xbf,0x8d,0x71,0xa3,0xae,0x9e,0x6b,0xa2,0x1c,0xe,0x60,0xca,0x2b,0xb2,0xa0,0xc5,0x57,0x59,0xd3,0xb7,0x39,0xc9,0x27,0x6d,0xfd,0x5,0x75,0x33,0x17,0xc4,0xfc,0xf1,0xe9,0xd1,0xcd,0x7b,0x18,0x4c,0x5c,0xf6,0xe7,0xb4,0xff,0x25,0x3c,0x2c,0x6,0xd0,0xbb,0x50,0xe6,0x28,0x64,0x88,0xbc,0xaf,0xc3,0x16,0xce,0xba,0x1f,0xb9,0xb,0xea,0x9f,0xf7,0x5d,0x81,0xd8,0x73,0x67,0x5f,0x61,0x7a,0x3b,0x55,0xfb,0x6c,0xd9,0xe0,0xb0,0x30,0x9b,0x4b,0x7d,0xc6,0xee,0x6f,0x6e,0x53,0x36,0xd2,0x47,0x15,0x51,0x83,0xd5,0xd,0x37,0x24,0x94,0xc,0x46,0x49,0x96,0x58,0x43,0xcf,0x7e,0xd4,0xbd,0xec,0x9a,0x41,0x1,0x11,0x54,0x8b,0xf,0x9d,0x1a,0x4a,0x69,0xc0,0x38,0xf2,0x22,0xdf,0xa5,0x8a,0xb6,0x87,0x77,0xd6,0x1b,0xc2,0xb5,0xc8,0x40,0xdc,0xad,0x80,0xf4,0x92,0x9,0x7c,0xbe,0x90,0x66,0xa6,0x29,0xf8,0xb1,0xb3,0x10,0xfe,0x4d,0x21,0x86,0x8,0xde,0xf0,0xc1,0xac,0xeb,0x14,0xb8,0x3e,0xdb,0x44,0x8c,0x4e,0x3d,0x68,0x32,0x4b,0x66,0x9f,0x74,0x50,0xb6,0xec,0x18,0x35,0x13,0x9c,0x5b,0x15,0xe4,0x19,0x8f,0xd3,0x71,0xa8,0x36,0x7f,0xc7,0xfe,0x73,0x69,0x46,0xa9,0xf,0x9b,0xff,0x1e,0x0,0x9,0xb3,0x7e,0x79,0xf9,0xe3,0xd4,0xf6,0x41,0xde,0x4,0xbf,0xa6,0x12,0xc2,0xba,0x82,0x7d,0x9e,0xa3,0xbc,0xce,0xea,0xc6,0x6f,0x34,0x57,0xe8,0xd,0xb,0x98,0x5,0xfc,0x92,0x80,0x3e,0x3c,0x2e,0xb7,0x56,0xed,0x11,0x23,0xb2,0xf7,0x2,0x32,0x3f,0xca,0xd9,0x65,0x38,0x85,0x8e,0x78,0x3b,0xad,0xe5,0x96,0x3d,0xb1,0x81,0xee,0x37,0x63,0x28,0x7b,0x6a,0x9a,0xb0,0xa0,0xb9,0x51,0x4d,0x75,0x6d,0xc0,0xd0,0x84,0xe7,0xe9,0x99,0x61,0xf1,0x60,0x58,0x8b,0xaf,0x4f,0xc5,0xcb,0x59,0xbb,0x55,0xa5,0x2b,0xa7,0xe6,0xfd,0xc3,0x45,0xf0,0x67,0xc9,0xc1,0x6b,0x3,0x76,0xfb,0xef,0x44,0x1d,0x52,0x8a,0x5f,0x33,0x97,0x25,0x83,0x26,0x7a,0xcc,0x27,0x4c,0x20,0x14,0xf8,0xb4,0xdf,0xc4,0xa,0xd5,0x21,0x48,0xe2,0x53,0xab,0x91,0x49,0x1f,0xda,0x90,0x8,0xb8,0xaa,0xcf,0xf2,0xf3,0xcd,0x89,0xdb,0x4e,0x7,0xac,0x2c,0x7c,0x72,0x5a,0xe1,0xd7,0xdc,0x54,0x29,0x5e,0x68,0x1c,0x31,0x40,0x2a,0x16,0x39,0x43,0x87,0x4a,0xeb,0x1b,0xf5,0xd6,0x86,0x1,0xbe,0x6e,0xa4,0x5c,0x9d,0xdd,0x6,0x70,0x93,0x17,0xc8,0x8d,0x10,0xd8,0x47,0xa2,0xae,0xf4,0xa1,0xd2,0x5d,0x6c,0x42,0x94,0x24,0x88,0x77,0x30,0x8c,0x2f,0x2d,0x64,0x1a,0xbd,0xd1,0x62,0x22,0xe0,0x95,0xe,0xb5,0x3a,0xfa,0xc,0xf6,0xf0,0xf8,0x65,0xc9,0x92,0x15,0xaa,0x33,0x41,0x3b,0x17,0x80,0x7f,0x5e,0x63,0xef,0x5b,0x47,0x3f,0x23,0xbc,0x42,0xf9,0x1e,0x4,0xb,0x29,0x4e,0xf4,0x84,0x83,0x2,0x66,0xfd,0xe3,0xbb,0x94,0xf2,0x54,0x3a,0x82,0x8e,0x3,0x8c,0x2e,0xcb,0x55,0x19,0xe8,0x72,0xe4,0xee,0xc8,0xa6,0x61,0x4b,0xad,0xe5,0x11,0x9b,0xb6,0x89,0x62,0xa8,0x46,0xd6,0x58,0x38,0xb2,0xa4,0x36,0xa5,0x9d,0x52,0x76,0x64,0x14,0xc,0x9c,0x2d,0x3d,0x1a,0x79,0xb0,0xac,0x90,0x88,0x4d,0x67,0x44,0x5d,0xd5,0x9e,0x97,0x86,0x7c,0x4c,0xca,0x13,0x18,0x50,0xc0,0x6b,0x73,0x78,0xc6,0x85,0x24,0x37,0xc5,0x98,0xff,0xa,0xc2,0xcf,0xec,0x10,0x4f,0xde,0xd3,0xc1,0xab,0x4a,0x6f,0x1,0xc3,0x7d,0xa7,0x8f,0x2a,0x1c,0x51,0xfa,0x81,0xd1,0x74,0x30,0xb3,0x26,0x32,0x57,0xe,0xf,0x6d,0x27,0x45,0xf5,0x6c,0x56,0xe2,0xb4,0xb5,0xdc,0xae,0x1f,0x39,0x22,0x28,0xf7,0xe9,0xdd,0x49,0x5,0x31,0x87,0xb1,0xda,0xd8,0x6a,0xdb,0x7e,0x77,0xaf,0xce,0xa2,0x12,0x6,0xe0,0xb9,0x96,0x3c,0x8b,0xfe,0xd,0xb8,0x34,0x9a,0x1b,0x5a,0x3e,0x0,0xc7,0x48,0xf1,0x7,0x1d,0xdf,0xf3,0x68,0x40,0xe7,0x9f,0x2c,0xd2,0x71,0x99,0xd0,0x75,0xd9,0xcd,0x8a,0x91,0xa0,0x69,0xbf,0x9,0x53,0x2f,0x5c,0x25,0xed,0x5f,0xba,0xea,0x6e,0x70,0x35,0x20,0x60,0x8d,0xfb,0x93,0x43,0xa1,0x59,0x2b,0x8,0xfc,0x7b,0xb7,0x7a,0xe6,0x16,0xeb,0xd7,0xbe,0xc4,0xe1,0x95,0xbd,0xcc,0xa9,0x21,0xa3,0xd4,0x49,0x8e,0x1,0x27,0x9d,0xb,0xf6,0x7,0x66,0x8d,0x74,0x59,0xa,0xfe,0xa4,0x42,0x1d,0xbb,0x54,0x7b,0x12,0xc,0xed,0x89,0x24,0xba,0x63,0xc1,0x61,0xec,0xd5,0x6d,0xad,0x16,0xcc,0x53,0xa8,0xd0,0x0,0xb4,0x6b,0x6c,0xa1,0x1b,0xe4,0xc6,0xf1,0xeb,0xfa,0x45,0x26,0x7d,0x17,0x8a,0x19,0x1f,0xb1,0x8c,0x6f,0x90,0xd4,0xf8,0xdc,0xae,0xa0,0x31,0x3,0xff,0x2d,0x20,0x10,0xe5,0x2c,0x92,0x80,0xee,0x44,0xa5,0x3c,0x2e,0x2f,0x84,0xf7,0xbf,0x25,0xfc,0x93,0xa3,0x2a,0x77,0xcb,0xd8,0x29,0x6a,0x9c,0x97,0x7f,0x67,0x5f,0x43,0xf5,0x96,0xc2,0xd2,0x78,0x69,0x3a,0x71,0xab,0xb2,0xa2,0x88,0x4b,0xd9,0xd7,0x5d,0x39,0xb7,0x47,0xa9,0xe3,0x73,0x8b,0xfb,0xbd,0x99,0x4a,0x72,0x64,0x11,0x79,0xd3,0xf,0x56,0xfd,0xe9,0xd1,0xef,0xf4,0xb5,0xdb,0x75,0xe2,0x57,0x5e,0x35,0xde,0x68,0xa6,0xea,0x6,0x32,0x21,0x4d,0x98,0x40,0x34,0x91,0x37,0x85,0xd,0x5b,0x83,0xb9,0xaa,0x1a,0x82,0xc8,0xc7,0x18,0xd6,0xcd,0x41,0xf0,0x5a,0x33,0x6e,0x3e,0xbe,0x15,0xc5,0xf3,0x48,0x60,0xe1,0xe0,0xdd,0xb8,0x5c,0xc9,0x9b,0xdf,0x51,0x2b,0x4,0x38,0x9,0xf9,0x58,0x95,0x4c,0x3b,0x46,0xce,0x52,0x23,0xe,0x7a,0x62,0x14,0xcf,0x8f,0x9f,0xda,0x5,0x81,0x13,0x94,0xc4,0xe7,0x4e,0xb6,0x7c,0xac,0x86,0x50,0x7e,0x4f,0x22,0x65,0x9a,0x36,0xb0,0x55,0xca,0x2,0xc0,0xb3,0xe6,0xbc,0x1c,0x87,0xf2,0x30,0x1e,0xe8,0x28,0xa7,0x76,0x3f,0x3d,0x9e,0x70,0xc3,0xaf,0x8,0x7a,0xdc,0x33,0x1c,0x75,0x6b,0x8a,0xee,0x43,0xdd,0x4,0xa6,0x6,0x8b,0xb2,0xa,0x2e,0xe9,0x66,0x40,0xfa,0x6c,0x91,0x60,0x1,0xea,0x13,0x3e,0x6d,0x99,0xc3,0x25,0x9d,0x22,0x41,0x1a,0x70,0xed,0x7e,0x78,0xd6,0xeb,0x8,0xf7,0xb3,0x9f,0xbb,0xc9,0xca,0x71,0xab,0x34,0xcf,0xb7,0x67,0xd3,0xc,0xb,0xc6,0x7c,0x83,0xa1,0x96,0x8c,0x48,0xe3,0x90,0xd8,0x42,0x9b,0xf4,0xc4,0x4d,0x10,0xac,0xbf,0x4e,0xd,0xfb,0xf0,0xc7,0x56,0x64,0x98,0x4a,0x47,0x77,0x82,0x4b,0xf5,0xe7,0x89,0x23,0xc2,0x5b,0x49,0x2c,0xbe,0xb0,0x3a,0x5e,0xd0,0x20,0xce,0x84,0x14,0xec,0x9c,0xda,0xfe,0x2d,0x15,0x18,0x0,0x38,0x24,0x92,0xf1,0xa5,0xb5,0x1f,0xe,0x5d,0x16,0xcc,0xd5,0xc5,0xef,0x39,0x52,0xb9,0xf,0xc1,0x8d,0x61,0x55,0x46,0x2a,0xff,0x27,0x53,0xf6,0x50,0xe2,0x3,0x76,0x1e,0xb4,0x68,0x31,0x9a,0x8e,0xb6,0x88,0x93,0xd2,0xbc,0x12,0x85,0x30,0x9,0x59,0xd9,0x72,0xa2,0x94,0x2f,0x7,0x86,0x87,0xba,0xdf,0x3b,0xae,0xfc,0xb8,0x6a,0x3c,0xe4,0xde,0xcd,0x7d,0xe5,0xaf,0xa0,0x7f,0xb1,0xaa,0x26,0x97,0x3d,0x54,0x5,0x73,0xa8,0xe8,0xf8,0xbd,0x62,0xe6,0x74,0xf3,0xa3,0x80,0x29,0xd1,0x1b,0xcb,0x36,0x4c,0x63,0x5f,0x6e,0x9e,0x3f,0xf2,0x2b,0x5c,0x21,0xa9,0x35,0x44,0x69,0x1d,0x7b,0xe0,0x95,0x57,0x79,0x8f,0x4f,0xc0,0x11,0x58,0x5a,0xf9,0x17,0xa4,0xc8,0x6f,0xe1,0x37,0x19,0x28,0x45,0x2,0xfd,0x51,0xd7,0x32,0xad,0x65,0xa7,0xd4,0x81,0xdb,0xa4,0x89,0x70,0x9b,0xbf,0x59,0x3,0xf7,0xda,0xfc,0x73,0xb4,0xfa,0xb,0xf6,0x60,0x3c,0x9e,0x47,0xd9,0x90,0x28,0x11,0x9c,0x86,0xa9,0x46,0xe0,0x74,0x10,0xf1,0xef,0xe6,0x5c,0x91,0x96,0x16,0xc,0x3b,0x19,0xae,0x31,0xeb,0x50,0x49,0xfd,0x2d,0x55,0x6d,0x92,0x71,0x4c,0x53,0x21,0x5,0x29,0x80,0xdb,0xb8,0x7,0xe2,0xe4,0x77,0xea,0x13,0x7d,0x6f,0xd1,0xd3,0xc1,0x58,0xb9,0x2,0xfe,0xcc,0x5d,0x18,0xed,0xdd,0xd0,0x25,0x36,0x8a,0xd7,0x6a,0x61,0x97,0xd4,0x42,0xa,0x79,0xd2,0x5e,0x6e,0x1,0xd8,0x8c,0xc7,0x94,0x85,0x75,0x5f,0x4f,0x56,0xbe,0xa2,0x9a,0x82,0x2f,0x3f,0x6b,0x8,0x6,0x76,0x8e,0x1e,0x8f,0xb7,0x64,0x40,0xa0,0x2a,0x24,0xb6,0x54,0xba,0x4a,0xc4,0x48,0x9,0x12,0x2c,0xaa,0x1f,0x88,0x26,0x2e,0x84,0xec,0x99,0x14,0x0,0xab,0xf2,0xbd,0x65,0xb0,0xdc,0x78,0xca,0x6c,0xc9,0x95,0x23,0xc8,0xa3,0xcf,0xfb,0x17,0x5b,0x30,0x2b,0xe5,0x3a,0xce,0xa7,0xd,0xbc,0x44,0x7e,0xa6,0xf0,0x35,0x7f,0xe7,0x57,0x45,0x20,0x1d,0x1c,0x22,0x66,0x34,0xa1,0xe8,0x43,0xc3,0x93,0x9d,0xb5,0xe,0x38,0x33,0xbb,0xc6,0xb1,0x87,0xf3,0xde,0xaf,0xc5,0xf9,0xd6,0xac,0x68,0xa5,0x4,0xf4,0x1a,0x39,0x69,0xee,0x51,0x81,0x4b,0xb3,0x72,0x32,0xe9,0x9f,0x7c,0xf8,0x27,0x62,0xff,0x37,0xa8,0x4d,0x41,0x1b,0x4e,0x3d,0xb2,0x83,0xad,0x7b,0xcb,0x67,0x98,0xdf,0x63,0xc0,0xc2,0x8b,0xf5,0x52,0x3e,0x8d,0xcd,0xf,0x7a,0xe1,0x5a,0xd5,0x15,0xe3,0x8a,0x8c,0x84,0x19,0xb5,0xee,0x69,0xd6,0x4f,0x3d,0x47,0x6b,0xfc,0x3,0x22,0x1f,0x93,0x27,0x3b,0x43,0x5f,0xc0,0x3e,0x85,0x62,0x78,0x77,0x55,0x32,0x88,0xf8,0xff,0x7e,0x1a,0x81,0x9f,0xc7,0xe8,0x8e,0x28,0x46,0xfe,0xf2,0x7f,0xf0,0x52,0xb7,0x29,0x65,0x94,0xe,0x98,0x92,0xb4,0xda,0x1d,0x37,0xd1,0x99,0x6d,0xe7,0xca,0xf5,0x1e,0xd4,0x3a,0xaa,0x24,0x44,0xce,0xd8,0x4a,0xd9,0xe1,0x2e,0xa,0x18,0x68,0x70,0xe0,0x51,0x41,0x66,0x5,0xcc,0xd0,0xec,0xf4,0x31,0x1b,0x38,0x21,0xa9,0xe2,0xeb,0xfa,0x0,0x30,0xb6,0x6f,0x64,0x2c,0xbc,0x17,0xf,0x4,0xba,0xf9,0x58,0x4b,0xb9,0xe4,0x83,0x76,0xbe,0xb3,0x90,0x6c,0x33,0xa2,0xaf,0xbd,0xd7,0x36,0x13,0x7d,0xbf,0x1,0xdb,0xf3,0x56,0x60,0x2d,0x86,0xfd,0xad,0x8,0x4c,0xcf,0x5a,0x4e,0x2b,0x72,0x73,0x11,0x5b,0x39,0x89,0x10,0x2a,0x9e,0xc8,0xc9,0xa0,0xd2,0x63,0x45,0x5e,0x54,0x8b,0x95,0xa1,0x35,0x79,0x4d,0xfb,0xcd,0xa6,0xa4,0x16,0xa7,0x2,0xb,0xd3,0xb2,0xde,0x6e,0x7a,0x9c,0xc5,0xea,0x40,0xf7,0x82,0x71,0xc4,0x48,0xe6,0x67,0x26,0x42,0x7c,0xbb,0x34,0x8d,0x7b,0x61,0xa3,0x8f,0x14,0x3c,0x9b,0xe3,0x50,0xae,0xd,0xe5,0xac,0x9,0xa5,0xb1,0xf6,0xed,0xdc,0x15,0xc3,0x75,0x2f,0x53,0x20,0x59,0x91,0x23,0xc6,0x96,0x12,0xc,0x49,0x5c,0x1c,0xf1,0x87,0xef,0x3f,0xdd,0x25,0x57,0x74,0x80,0x7,0xcb,0x6,0x9a,0x6a,0x97,0xab,0xc2,0xb8,0x9d,0xe9,0xc1,0xb0,0xd5,0x5d,0xdf,0xa8,0xec,0x2b,0xa4,0x82,0x38,0xae,0x53,0xa2,0xc3,0x28,0xd1,0xfc,0xaf,0x5b,0x1,0xe7,0xb8,0x1e,0xf1,0xde,0xb7,0xa9,0x48,0x2c,0x81,0x1f,0xc6,0x64,0xc4,0x49,0x70,0xc8,0x8,0xb3,0x69,0xf6,0xd,0x75,0xa5,0x11,0xce,0xc9,0x4,0xbe,0x41,0x63,0x54,0x4e,0x5f,0xe0,0x83,0xd8,0xb2,0x2f,0xbc,0xba,0x14,0x29,0xca,0x35,0x71,0x5d,0x79,0xb,0x5,0x94,0xa6,0x5a,0x88,0x85,0xb5,0x40,0x89,0x37,0x25,0x4b,0xe1,0x0,0x99,0x8b,0x8a,0x21,0x52,0x1a,0x80,0x59,0x36,0x6,0x8f,0xd2,0x6e,0x7d,0x8c,0xcf,0x39,0x32,0xda,0xc2,0xfa,0xe6,0x50,0x33,0x67,0x77,0xdd,0xcc,0x9f,0xd4,0xe,0x17,0x7,0x2d,0xee,0x7c,0x72,0xf8,0x9c,0x12,0xe2,0xc,0x46,0xd6,0x2e,0x5e,0x18,0x3c,0xef,0xd7,0xc1,0xb4,0xdc,0x76,0xaa,0xf3,0x58,0x4c,0x74,0x4a,0x51,0x10,0x7e,0xd0,0x47,0xf2,0xfb,0x90,0x7b,0xcd,0x3,0x4f,0xa3,0x97,0x84,0xe8,0x3d,0xe5,0x91,0x34,0x92,0x20,0xa8,0xfe,0x26,0x1c,0xf,0xbf,0x27,0x6d,0x62,0xbd,0x73,0x68,0xe4,0x55,0xff,0x96,0xcb,0x9b,0x1b,0xb0,0x60,0x56,0xed,0xc5,0x44,0x45,0x78,0x1d,0xf9,0x6c,0x3e,0x7a,0xf4,0x8e,0xa1,0x9d,0xac,0x5c,0xfd,0x30,0xe9,0x9e,0xe3,0x6b,0xf7,0x86,0xab,0xdf,0xc7,0xb1,0x6a,0x2a,0x3a,0x7f,0xa0,0x24,0xb6,0x31,0x61,0x42,0xeb,0x13,0xd9,0x9,0x23,0xf5,0xdb,0xea,0x87,0xc0,0x3f,0x93,0x15,0xf0,0x6f,0xa7,0x65,0x16,0x43,0x19,0xb9,0x22,0x57,0x95,0xbb,0x4d,0x8d,0x2,0xd3,0x9a,0x98,0x3b,0xd5,0x66,0xa,0xad,0xaa,0xc,0xe3,0xcc,0xa5,0xbb,0x5a,0x3e,0x93,0xd,0xd4,0x76,0xd6,0x5b,0x62,0xda,0xfe,0x39,0xb6,0x90,0x2a,0xbc,0x41,0xb0,0xd1,0x3a,0xc3,0xee,0xbd,0x49,0x13,0xf5,0x4d,0xf2,0x91,0xca,0xa0,0x3d,0xae,0xa8,0x6,0x3b,0xd8,0x27,0x63,0x4f,0x6b,0x19,0x1a,0xa1,0x7b,0xe4,0x1f,0x67,0xb7,0x3,0xdc,0xdb,0x16,0xac,0x53,0x71,0x46,0x5c,0x98,0x33,0x40,0x8,0x92,0x4b,0x24,0x14,0x9d,0xc0,0x7c,0x6f,0x9e,0xdd,0x2b,0x20,0x17,0x86,0xb4,0x48,0x9a,0x97,0xa7,0x52,0x9b,0x25,0x37,0x59,0xf3,0x12,0x8b,0x99,0xfc,0x6e,0x60,0xea,0x8e,0x0,0xf0,0x1e,0x54,0xc4,0x3c,0x4c,0xa,0x2e,0xfd,0xc5,0xc8,0xd0,0xe8,0xf4,0x42,0x21,0x75,0x65,0xcf,0xde,0x8d,0xc6,0x1c,0x5,0x15,0x3f,0xe9,0x82,0x69,0xdf,0x11,0x5d,0xb1,0x85,0x96,0xfa,0x2f,0xf7,0x83,0x26,0x80,0x32,0xd3,0xa6,0xce,0x64,0xb8,0xe1,0x4a,0x5e,0x66,0x58,0x43,0x2,0x6c,0xc2,0x55,0xe0,0xd9,0x89,0x9,0xa2,0x72,0x44,0xff,0xd7,0x56,0x57,0x6a,0xf,0xeb,0x7e,0x2c,0x68,0xba,0xec,0x34,0xe,0x1d,0xad,0x35,0x7f,0x70,0xaf,0x61,0x7a,0xf6,0x47,0xed,0x84,0xd5,0xa3,0x78,0x38,0x28,0x6d,0xb2,0x36,0xa4,0x23,0x73,0x50,0xf9,0x1,0xcb,0x1b,0xe6,0x9c,0xb3,0x8f,0xbe,0x4e,0xef,0x22,0xfb,0x8c,0xf1,0x79,0xe5,0x94,0xb9,0xcd,0xab,0x30,0x45,0x87,0xa9,0x5f,0x9f,0x10,0xc1,0x88,0x8a,0x29,0xc7,0x74,0x18,0xbf,0x31,0xe7,0xc9,0xf8,0x95,0xd2,0x2d,0x81,0x7,0xe2,0x7d,0xb5,0x77,0x4,0x51,0xb,0x53,0x7e,0x87,0x6c,0x48,0xae,0xf4,0x0,0x2d,0xb,0x84,0x43,0xd,0xfc,0x1,0x97,0xcb,0x69,0xb0,0x2e,0x67,0xdf,0xe6,0x6b,0x71,0x5e,0xb1,0x17,0x83,0xe7,0x6,0x18,0x11,0xab,0x66,0x61,0xe1,0xfb,0xcc,0xee,0x59,0xc6,0x1c,0xa7,0xbe,0xa,0xda,0xa2,0x9a,0x65,0x86,0xbb,0xa4,0xd6,0xf2,0xde,0x77,0x2c,0x4f,0xf0,0x15,0x13,0x80,0x1d,0xe4,0x8a,0x98,0x26,0x24,0x36,0xaf,0x4e,0xf5,0x9,0x3b,0xaa,0xef,0x1a,0x2a,0x27,0xd2,0xc1,0x7d,0x20,0x9d,0x96,0x60,0x23,0xb5,0xfd,0x8e,0x25,0xa9,0x99,0xf6,0x2f,0x7b,0x30,0x63,0x72,0x82,0xa8,0xb8,0xa1,0x49,0x55,0x6d,0x75,0xd8,0xc8,0x9c,0xff,0xf1,0x81,0x79,0xe9,0x78,0x40,0x93,0xb7,0x57,0xdd,0xd3,0x41,0xa3,0x4d,0xbd,0x33,0xbf,0xfe,0xe5,0xdb,0x5d,0xe8,0x7f,0xd1,0xd9,0x73,0x1b,0x6e,0xe3,0xf7,0x5c,0x5,0x4a,0x92,0x47,0x2b,0x8f,0x3d,0x9b,0x3e,0x62,0xd4,0x3f,0x54,0x38,0xc,0xe0,0xac,0xc7,0xdc,0x12,0xcd,0x39,0x50,0xfa,0x4b,0xb3,0x89,0x51,0x7,0xc2,0x88,0x10,0xa0,0xb2,0xd7,0xea,0xeb,0xd5,0x91,0xc3,0x56,0x1f,0xb4,0x34,0x64,0x6a,0x42,0xf9,0xcf,0xc4,0x4c,0x31,0x46,0x70,0x4,0x29,0x58,0x32,0xe,0x21,0x5b,0x9f,0x52,0xf3,0x3,0xed,0xce,0x9e,0x19,0xa6,0x76,0xbc,0x44,0x85,0xc5,0x1e,0x68,0x8b,0xf,0xd0,0x95,0x8,0xc0,0x5f,0xba,0xb6,0xec,0xb9,0xca,0x45,0x74,0x5a,0x8c,0x3c,0x90,0x6f,0x28,0x94,0x37,0x35,0x7c,0x2,0xa5,0xc9,0x7a,0x3a,0xf8,0x8d,0x16,0xad,0x22,0xe2,0x14,0xc6,0xc0,0xc8,0x55,0xf9,0xa2,0x25,0x9a,0x3,0x71,0xb,0x27,0xb0,0x4f,0x6e,0x53,0xdf,0x6b,0x77,0xf,0x13,0x8c,0x72,0xc9,0x2e,0x34,0x3b,0x19,0x7e,0xc4,0xb4,0xb3,0x32,0x56,0xcd,0xd3,0x8b,0xa4,0xc2,0x64,0xa,0xb2,0xbe,0x33,0xbc,0x1e,0xfb,0x65,0x29,0xd8,0x42,0xd4,0xde,0xf8,0x96,0x51,0x7b,0x9d,0xd5,0x21,0xab,0x86,0xb9,0x52,0x98,0x76,0xe6,0x68,0x8,0x82,0x94,0x6,0x95,0xad,0x62,0x46,0x54,0x24,0x3c,0xac,0x1d,0xd,0x2a,0x49,0x80,0x9c,0xa0,0xb8,0x7d,0x57,0x74,0x6d,0xe5,0xae,0xa7,0xb6,0x4c,0x7c,0xfa,0x23,0x28,0x60,0xf0,0x5b,0x43,0x48,0xf6,0xb5,0x14,0x7,0xf5,0xa8,0xcf,0x3a,0xf2,0xff,0xdc,0x20,0x7f,0xee,0xe3,0xf1,0x9b,0x7a,0x5f,0x31,0xf3,0x4d,0x97,0xbf,0x1a,0x2c,0x61,0xca,0xb1,0xe1,0x44,0x0,0x83,0x16,0x2,0x67,0x3e,0x3f,0x5d,0x17,0x75,0xc5,0x5c,0x66,0xd2,0x84,0x85,0xec,0x9e,0x2f,0x9,0x12,0x18,0xc7,0xd9,0xed,0x79,0x35,0x1,0xb7,0x81,0xea,0xe8,0x5a,0xeb,0x4e,0x47,0x9f,0xfe,0x92,0x22,0x36,0xd0,0x89,0xa6,0xc,0xbb,0xce,0x3d,0x88,0x4,0xaa,0x2b,0x6a,0xe,0x30,0xf7,0x78,0xc1,0x37,0x2d,0xef,0xc3,0x58,0x70,0xd7,0xaf,0x1c,0xe2,0x41,0xa9,0xe0,0x45,0xe9,0xfd,0xba,0xa1,0x90,0x59,0x8f,0x39,0x63,0x1f,0x6c,0x15,0xdd,0x6f,0x8a,0xda,0x5e,0x40,0x5,0x10,0x50,0xbd,0xcb,0xa3,0x73,0x91,0x69,0x1b,0x38,0xcc,0x4b,0x87,0x4a,0xd6,0x26,0xdb,0xe7,0x8e,0xf4,0xd1,0xa5,0x8d,0xfc,0x99,0x11,0x93,0xe4,0x79,0xbe,0x31,0x17,0xad,0x3b,0xc6,0x37,0x56,0xbd,0x44,0x69,0x3a,0xce,0x94,0x72,0x2d,0x8b,0x64,0x4b,0x22,0x3c,0xdd,0xb9,0x14,0x8a,0x53,0xf1,0x51,0xdc,0xe5,0x5d,0x9d,0x26,0xfc,0x63,0x98,0xe0,0x30,0x84,0x5b,0x5c,0x91,0x2b,0xd4,0xf6,0xc1,0xdb,0xca,0x75,0x16,0x4d,0x27,0xba,0x29,0x2f,0x81,0xbc,0x5f,0xa0,0xe4,0xc8,0xec,0x9e,0x90,0x1,0x33,0xcf,0x1d,0x10,0x20,0xd5,0x1c,0xa2,0xb0,0xde,0x74,0x95,0xc,0x1e,0x1f,0xb4,0xc7,0x8f,0x15,0xcc,0xa3,0x93,0x1a,0x47,0xfb,0xe8,0x19,0x5a,0xac,0xa7,0x4f,0x57,0x6f,0x73,0xc5,0xa6,0xf2,0xe2,0x48,0x59,0xa,0x41,0x9b,0x82,0x92,0xb8,0x7b,0xe9,0xe7,0x6d,0x9,0x87,0x77,0x99,0xd3,0x43,0xbb,0xcb,0x8d,0xa9,0x7a,0x42,0x54,0x21,0x49,0xe3,0x3f,0x66,0xcd,0xd9,0xe1,0xdf,0xc4,0x85,0xeb,0x45,0xd2,0x67,0x6e,0x5,0xee,0x58,0x96,0xda,0x36,0x2,0x11,0x7d,0xa8,0x70,0x4,0xa1,0x7,0xb5,0x3d,0x6b,0xb3,0x89,0x9a,0x2a,0xb2,0xf8,0xf7,0x28,0xe6,0xfd,0x71,0xc0,0x6a,0x3,0x5e,0xe,0x8e,0x25,0xf5,0xc3,0x78,0x50,0xd1,0xd0,0xed,0x88,0x6c,0xf9,0xab,0xef,0x61,0x1b,0x34,0x8,0x39,0xc9,0x68,0xa5,0x7c,0xb,0x76,0xfe,0x62,0x13,0x3e,0x4a,0x52,0x24,0xff,0xbf,0xaf,0xea,0x35,0xb1,0x23,0xa4,0xf4,0xd7,0x7e,0x86,0x4c,0x9c,0xb6,0x60,0x4e,0x7f,0x12,0x55,0xaa,0x6,0x80,0x65,0xfa,0x32,0xf0,0x83,0xd6,0x8c,0x2c,0xb7,0xc2,0x0,0x2e,0xd8,0x18,0x97,0x46,0xf,0xd,0xae,0x40,0xf3,0x9f,0x38,0xc6,0x60,0x8f,0xa0,0xc9,0xd7,0x36,0x52,0xff,0x61,0xb8,0x1a,0xba,0x37,0xe,0xb6,0x92,0x55,0xda,0xfc,0x46,0xd0,0x2d,0xdc,0xbd,0x56,0xaf,0x82,0xd1,0x25,0x7f,0x99,0x21,0x9e,0xfd,0xa6,0xcc,0x51,0xc2,0xc4,0x6a,0x57,0xb4,0x4b,0xf,0x23,0x7,0x75,0x76,0xcd,0x17,0x88,0x73,0xb,0xdb,0x6f,0xb0,0xb7,0x7a,0xc0,0x3f,0x1d,0x2a,0x30,0xf4,0x5f,0x2c,0x64,0xfe,0x27,0x48,0x78,0xf1,0xac,0x10,0x3,0xf2,0xb1,0x47,0x4c,0x7b,0xea,0xd8,0x24,0xf6,0xfb,0xcb,0x3e,0xf7,0x49,0x5b,0x35,0x9f,0x7e,0xe7,0xf5,0x90,0x2,0xc,0x86,0xe2,0x6c,0x9c,0x72,0x38,0xa8,0x50,0x20,0x66,0x42,0x91,0xa9,0xa4,0xbc,0x84,0x98,0x2e,0x4d,0x19,0x9,0xa3,0xb2,0xe1,0xaa,0x70,0x69,0x79,0x53,0x85,0xee,0x5,0xb3,0x7d,0x31,0xdd,0xe9,0xfa,0x96,0x43,0x9b,0xef,0x4a,0xec,0x5e,0xbf,0xca,0xa2,0x8,0xd4,0x8d,0x26,0x32,0xa,0x34,0x2f,0x6e,0x0,0xae,0x39,0x8c,0xb5,0xe5,0x65,0xce,0x1e,0x28,0x93,0xbb,0x3a,0x3b,0x6,0x63,0x87,0x12,0x40,0x4,0xd6,0x80,0x58,0x62,0x71,0xc1,0x59,0x13,0x1c,0xc3,0xd,0x16,0x9a,0x2b,0x81,0xe8,0xb9,0xcf,0x14,0x54,0x44,0x1,0xde,0x5a,0xc8,0x4f,0x1f,0x3c,0x95,0x6d,0xa7,0x77,0x8a,0xf0,0xdf,0xe3,0xd2,0x22,0x83,0x4e,0x97,0xe0,0x9d,0x15,0x89,0xf8,0xd5,0xa1,0xc7,0x5c,0x29,0xeb,0xc5,0x33,0xf3,0x7c,0xad,0xe4,0xe6,0x45,0xab,0x18,0x74,0xd3,0x5d,0x8b,0xa5,0x94,0xf9,0xbe,0x41,0xed,0x6b,0x8e,0x11,0xd9,0x1b,0x68,0x3d,0x67,0x7c,0x51,0xa8,0x43,0x67,0x81,0xdb,0x2f,0x2,0x24,0xab,0x6c,0x22,0xd3,0x2e,0xb8,0xe4,0x46,0x9f,0x1,0x48,0xf0,0xc9,0x44,0x5e,0x71,0x9e,0x38,0xac,0xc8,0x29,0x37,0x3e,0x84,0x49,0x4e,0xce,0xd4,0xe3,0xc1,0x76,0xe9,0x33,0x88,0x91,0x25,0xf5,0x8d,0xb5,0x4a,0xa9,0x94,0x8b,0xf9,0xdd,0xf1,0x58,0x3,0x60,0xdf,0x3a,0x3c,0xaf,0x32,0xcb,0xa5,0xb7,0x9,0xb,0x19,0x80,0x61,0xda,0x26,0x14,0x85,0xc0,0x35,0x5,0x8,0xfd,0xee,0x52,0xf,0xb2,0xb9,0x4f,0xc,0x9a,0xd2,0xa1,0xa,0x86,0xb6,0xd9,0x0,0x54,0x1f,0x4c,0x5d,0xad,0x87,0x97,0x8e,0x66,0x7a,0x42,0x5a,0xf7,0xe7,0xb3,0xd0,0xde,0xae,0x56,0xc6,0x57,0x6f,0xbc,0x98,0x78,0xf2,0xfc,0x6e,0x8c,0x62,0x92,0x1c,0x90,0xd1,0xca,0xf4,0x72,0xc7,0x50,0xfe,0xf6,0x5c,0x34,0x41,0xcc,0xd8,0x73,0x2a,0x65,0xbd,0x68,0x4,0xa0,0x12,0xb4,0x11,0x4d,0xfb,0x10,0x7b,0x17,0x23,0xcf,0x83,0xe8,0xf3,0x3d,0xe2,0x16,0x7f,0xd5,0x64,0x9c,0xa6,0x7e,0x28,0xed,0xa7,0x3f,0x8f,0x9d,0xf8,0xc5,0xc4,0xfa,0xbe,0xec,0x79,0x30,0x9b,0x1b,0x4b,0x45,0x6d,0xd6,0xe0,0xeb,0x63,0x1e,0x69,0x5f,0x2b,0x6,0x77,0x1d,0x21,0xe,0x74,0xb0,0x7d,0xdc,0x2c,0xc2,0xe1,0xb1,0x36,0x89,0x59,0x93,0x6b,0xaa,0xea,0x31,0x47,0xa4,0x20,0xff,0xba,0x27,0xef,0x70,0x95,0x99,0xc3,0x96,0xe5,0x6a,0x5b,0x75,0xa3,0x13,0xbf,0x40,0x7,0xbb,0x18,0x1a,0x53,0x2d,0x8a,0xe6,0x55,0x15,0xd7,0xa2,0x39,0x82,0xd,0xcd,0x3b,0x34,0x32,0x3a,0xa7,0xb,0x50,0xd7,0x68,0xf1,0x83,0xf9,0xd5,0x42,0xbd,0x9c,0xa1,0x2d,0x99,0x85,0xfd,0xe1,0x7e,0x80,0x3b,0xdc,0xc6,0xc9,0xeb,0x8c,0x36,0x46,0x41,0xc0,0xa4,0x3f,0x21,0x79,0x56,0x30,0x96,0xf8,0x40,0x4c,0xc1,0x4e,0xec,0x9,0x97,0xdb,0x2a,0xb0,0x26,0x2c,0xa,0x64,0xa3,0x89,0x6f,0x27,0xd3,0x59,0x74,0x4b,0xa0,0x6a,0x84,0x14,0x9a,0xfa,0x70,0x66,0xf4,0x67,0x5f,0x90,0xb4,0xa6,0xd6,0xce,0x5e,0xef,0xff,0xd8,0xbb,0x72,0x6e,0x52,0x4a,0x8f,0xa5,0x86,0x9f,0x17,0x5c,0x55,0x44,0xbe,0x8e,0x8,0xd1,0xda,0x92,0x2,0xa9,0xb1,0xba,0x4,0x47,0xe6,0xf5,0x7,0x5a,0x3d,0xc8,0x0,0xd,0x2e,0xd2,0x8d,0x1c,0x11,0x3,0x69,0x88,0xad,0xc3,0x1,0xbf,0x65,0x4d,0xe8,0xde,0x93,0x38,0x43,0x13,0xb6,0xf2,0x71,0xe4,0xf0,0x95,0xcc,0xcd,0xaf,0xe5,0x87,0x37,0xae,0x94,0x20,0x76,0x77,0x1e,0x6c,0xdd,0xfb,0xe0,0xea,0x35,0x2b,0x1f,0x8b,0xc7,0xf3,0x45,0x73,0x18,0x1a,0xa8,0x19,0xbc,0xb5,0x6d,0xc,0x60,0xd0,0xc4,0x22,0x7b,0x54,0xfe,0x49,0x3c,0xcf,0x7a,0xf6,0x58,0xd9,0x98,0xfc,0xc2,0x5,0x8a,0x33,0xc5,0xdf,0x1d,0x31,0xaa,0x82,0x25,0x5d,0xee,0x10,0xb3,0x5b,0x12,0xb7,0x1b,0xf,0x48,0x53,0x62,0xab,0x7d,0xcb,0x91,0xed,0x9e,0xe7,0x2f,0x9d,0x78,0x28,0xac,0xb2,0xf7,0xe2,0xa2,0x4f,0x39,0x51,0x81,0x63,0x9b,0xe9,0xca,0x3e,0xb9,0x75,0xb8,0x24,0xd4,0x29,0x15,0x7c,0x6,0x23,0x57,0x7f,0xe,0x6b,0xe3,0x61,0x16,0xd3,0x14,0x9b,0xbd,0x7,0x91,0x6c,0x9d,0xfc,0x17,0xee,0xc3,0x90,0x64,0x3e,0xd8,0x87,0x21,0xce,0xe1,0x88,0x96,0x77,0x13,0xbe,0x20,0xf9,0x5b,0xfb,0x76,0x4f,0xf7,0x37,0x8c,0x56,0xc9,0x32,0x4a,0x9a,0x2e,0xf1,0xf6,0x3b,0x81,0x7e,0x5c,0x6b,0x71,0x60,0xdf,0xbc,0xe7,0x8d,0x10,0x83,0x85,0x2b,0x16,0xf5,0xa,0x4e,0x62,0x46,0x34,0x3a,0xab,0x99,0x65,0xb7,0xba,0x8a,0x7f,0xb6,0x8,0x1a,0x74,0xde,0x3f,0xa6,0xb4,0xb5,0x1e,0x6d,0x25,0xbf,0x66,0x9,0x39,0xb0,0xed,0x51,0x42,0xb3,0xf0,0x6,0xd,0xe5,0xfd,0xc5,0xd9,0x6f,0xc,0x58,0x48,0xe2,0xf3,0xa0,0xeb,0x31,0x28,0x38,0x12,0xd1,0x43,0x4d,0xc7,0xa3,0x2d,0xdd,0x33,0x79,0xe9,0x11,0x61,0x27,0x3,0xd0,0xe8,0xfe,0x8b,0xe3,0x49,0x95,0xcc,0x67,0x73,0x4b,0x75,0x6e,0x2f,0x41,0xef,0x78,0xcd,0xc4,0xaf,0x44,0xf2,0x3c,0x70,0x9c,0xa8,0xbb,0xd7,0x2,0xda,0xae,0xb,0xad,0x1f,0x97,0xc1,0x19,0x23,0x30,0x80,0x18,0x52,0x5d,0x82,0x4c,0x57,0xdb,0x6a,0xc0,0xa9,0xf4,0xa4,0x24,0x8f,0x5f,0x69,0xd2,0xfa,0x7b,0x7a,0x47,0x22,0xc6,0x53,0x1,0x45,0xcb,0xb1,0x9e,0xa2,0x93,0x63,0xc2,0xf,0xd6,0xa1,0xdc,0x54,0xc8,0xb9,0x94,0xe0,0xf8,0x8e,0x55,0x15,0x5,0x40,0x9f,0x1b,0x89,0xe,0x5e,0x7d,0xd4,0x2c,0xe6,0x36,0x1c,0xca,0xe4,0xd5,0xb8,0xff,0x0,0xac,0x2a,0xcf,0x50,0x98,0x5a,0x29,0x7c,0x26,0x86,0x1d,0x68,0xaa,0x84,0x72,0xb2,0x3d,0xec,0xa5,0xa7,0x4,0xea,0x59,0x35,0x92,0x38,0x9e,0x71,0x5e,0x37,0x29,0xc8,0xac,0x1,0x9f,0x46,0xe4,0x44,0xc9,0xf0,0x48,0x6c,0xab,0x24,0x2,0xb8,0x2e,0xd3,0x22,0x43,0xa8,0x51,0x7c,0x2f,0xdb,0x81,0x67,0xdf,0x60,0x3,0x58,0x32,0xaf,0x3c,0x3a,0x94,0xa9,0x4a,0xb5,0xf1,0xdd,0xf9,0x8b,0x88,0x33,0xe9,0x76,0x8d,0xf5,0x25,0x91,0x4e,0x49,0x84,0x3e,0xc1,0xe3,0xd4,0xce,0xa,0xa1,0xd2,0x9a,0x0,0xd9,0xb6,0x86,0xf,0x52,0xee,0xfd,0xc,0x4f,0xb9,0xb2,0x85,0x14,0x26,0xda,0x8,0x5,0x35,0xc0,0x9,0xb7,0xa5,0xcb,0x61,0x80,0x19,0xb,0x6e,0xfc,0xf2,0x78,0x1c,0x92,0x62,0x8c,0xc6,0x56,0xae,0xde,0x98,0xbc,0x6f,0x57,0x5a,0x42,0x7a,0x66,0xd0,0xb3,0xe7,0xf7,0x5d,0x4c,0x1f,0x54,0x8e,0x97,0x87,0xad,0x7b,0x10,0xfb,0x4d,0x83,0xcf,0x23,0x17,0x4,0x68,0xbd,0x65,0x11,0xb4,0x12,0xa0,0x41,0x34,0x5c,0xf6,0x2a,0x73,0xd8,0xcc,0xf4,0xca,0xd1,0x90,0xfe,0x50,0xc7,0x72,0x4b,0x1b,0x9b,0x30,0xe0,0xd6,0x6d,0x45,0xc4,0xc5,0xf8,0x9d,0x79,0xec,0xbe,0xfa,0x28,0x7e,0xa6,0x9c,0x8f,0x3f,0xa7,0xed,0xe2,0x3d,0xf3,0xe8,0x64,0xd5,0x7f,0x16,0x47,0x31,0xea,0xaa,0xba,0xff,0x20,0xa4,0x36,0xb1,0xe1,0xc2,0x6b,0x93,0x59,0x89,0x74,0xe,0x21,0x1d,0x2c,0xdc,0x7d,0xb0,0x69,0x1e,0x63,0xeb,0x77,0x6,0x2b,0x5f,0x39,0xa2,0xd7,0x15,0x3b,0xcd,0xd,0x82,0x53,0x1a,0x18,0xbb,0x55,0xe6,0x8a,0x2d,0xa3,0x75,0x5b,0x6a,0x7,0x40,0xbf,0x13,0x95,0x70,0xef,0x27,0xe5,0x96,0xc3,0x99,0x99,0xb4,0x4d,0xa6,0x82,0x64,0x3e,0xca,0xe7,0xc1,0x4e,0x89,0xc7,0x36,0xcb,0x5d,0x1,0xa3,0x7a,0xe4,0xad,0x15,0x2c,0xa1,0xbb,0x94,0x7b,0xdd,0x49,0x2d,0xcc,0xd2,0xdb,0x61,0xac,0xab,0x2b,0x31,0x6,0x24,0x93,0xc,0xd6,0x6d,0x74,0xc0,0x10,0x68,0x50,0xaf,0x4c,0x71,0x6e,0x1c,0x38,0x14,0xbd,0xe6,0x85,0x3a,0xdf,0xd9,0x4a,0xd7,0x2e,0x40,0x52,0xec,0xee,0xfc,0x65,0x84,0x3f,0xc3,0xf1,0x60,0x25,0xd0,0xe0,0xed,0x18,0xb,0xb7,0xea,0x57,0x5c,0xaa,0xe9,0x7f,0x37,0x44,0xef,0x63,0x53,0x3c,0xe5,0xb1,0xfa,0xa9,0xb8,0x48,0x62,0x72,0x6b,0x83,0x9f,0xa7,0xbf,0x12,0x2,0x56,0x35,0x3b,0x4b,0xb3,0x23,0xb2,0x8a,0x59,0x7d,0x9d,0x17,0x19,0x8b,0x69,0x87,0x77,0xf9,0x75,0x34,0x2f,0x11,0x97,0x22,0xb5,0x1b,0x13,0xb9,0xd1,0xa4,0x29,0x3d,0x96,0xcf,0x80,0x58,0x8d,0xe1,0x45,0xf7,0x51,0xf4,0xa8,0x1e,0xf5,0x9e,0xf2,0xc6,0x2a,0x66,0xd,0x16,0xd8,0x7,0xf3,0x9a,0x30,0x81,0x79,0x43,0x9b,0xcd,0x8,0x42,0xda,0x6a,0x78,0x1d,0x20,0x21,0x1f,0x5b,0x9,0x9c,0xd5,0x7e,0xfe,0xae,0xa0,0x88,0x33,0x5,0xe,0x86,0xfb,0x8c,0xba,0xce,0xe3,0x92,0xf8,0xc4,0xeb,0x91,0x55,0x98,0x39,0xc9,0x27,0x4,0x54,0xd3,0x6c,0xbc,0x76,0x8e,0x4f,0xf,0xd4,0xa2,0x41,0xc5,0x1a,0x5f,0xc2,0xa,0x95,0x70,0x7c,0x26,0x73,0x0,0x8f,0xbe,0x90,0x46,0xf6,0x5a,0xa5,0xe2,0x5e,0xfd,0xff,0xb6,0xc8,0x6f,0x3,0xb0,0xf0,0x32,0x47,0xdc,0x67,0xe8,0x28,0xde,0xe9,0xef,0xe7,0x7a,0xd6,0x8d,0xa,0xb5,0x2c,0x5e,0x24,0x8,0x9f,0x60,0x41,0x7c,0xf0,0x44,0x58,0x20,0x3c,0xa3,0x5d,0xe6,0x1,0x1b,0x14,0x36,0x51,0xeb,0x9b,0x9c,0x1d,0x79,0xe2,0xfc,0xa4,0x8b,0xed,0x4b,0x25,0x9d,0x91,0x1c,0x93,0x31,0xd4,0x4a,0x6,0xf7,0x6d,0xfb,0xf1,0xd7,0xb9,0x7e,0x54,0xb2,0xfa,0xe,0x84,0xa9,0x96,0x7d,0xb7,0x59,0xc9,0x47,0x27,0xad,0xbb,0x29,0xba,0x82,0x4d,0x69,0x7b,0xb,0x13,0x83,0x32,0x22,0x5,0x66,0xaf,0xb3,0x8f,0x97,0x52,0x78,0x5b,0x42,0xca,0x81,0x88,0x99,0x63,0x53,0xd5,0xc,0x7,0x4f,0xdf,0x74,0x6c,0x67,0xd9,0x9a,0x3b,0x28,0xda,0x87,0xe0,0x15,0xdd,0xd0,0xf3,0xf,0x50,0xc1,0xcc,0xde,0xb4,0x55,0x70,0x1e,0xdc,0x62,0xb8,0x90,0x35,0x3,0x4e,0xe5,0x9e,0xce,0x6b,0x2f,0xac,0x39,0x2d,0x48,0x11,0x10,0x72,0x38,0x5a,0xea,0x73,0x49,0xfd,0xab,0xaa,0xc3,0xb1,0x0,0x26,0x3d,0x37,0xe8,0xf6,0xc2,0x56,0x1a,0x2e,0x98,0xae,0xc5,0xc7,0x75,0xc4,0x61,0x68,0xb0,0xd1,0xbd,0xd,0x19,0xff,0xa6,0x89,0x23,0x94,0xe1,0x12,0xa7,0x2b,0x85,0x4,0x45,0x21,0x1f,0xd8,0x57,0xee,0x18,0x2,0xc0,0xec,0x77,0x5f,0xf8,0x80,0x33,0xcd,0x6e,0x86,0xcf,0x6a,0xc6,0xd2,0x95,0x8e,0xbf,0x76,0xa0,0x16,0x4c,0x30,0x43,0x3a,0xf2,0x40,0xa5,0xf5,0x71,0x6f,0x2a,0x3f,0x7f,0x92,0xe4,0x8c,0x5c,0xbe,0x46,0x34,0x17,0xe3,0x64,0xa8,0x65,0xf9,0x9,0xf4,0xc8,0xa1,0xdb,0xfe,0x8a,0xa2,0xd3,0xb6,0x3e,0xbc,0xcb,0xc3,0x4,0x8b,0xad,0x17,0x81,0x7c,0x8d,0xec,0x7,0xfe,0xd3,0x80,0x74,0x2e,0xc8,0x97,0x31,0xde,0xf1,0x98,0x86,0x67,0x3,0xae,0x30,0xe9,0x4b,0xeb,0x66,0x5f,0xe7,0x27,0x9c,0x46,0xd9,0x22,0x5a,0x8a,0x3e,0xe1,0xe6,0x2b,0x91,0x6e,0x4c,0x7b,0x61,0x70,0xcf,0xac,0xf7,0x9d,0x0,0x93,0x95,0x3b,0x6,0xe5,0x1a,0x5e,0x72,0x56,0x24,0x2a,0xbb,0x89,0x75,0xa7,0xaa,0x9a,0x6f,0xa6,0x18,0xa,0x64,0xce,0x2f,0xb6,0xa4,0xa5,0xe,0x7d,0x35,0xaf,0x76,0x19,0x29,0xa0,0xfd,0x41,0x52,0xa3,0xe0,0x16,0x1d,0xf5,0xed,0xd5,0xc9,0x7f,0x1c,0x48,0x58,0xf2,0xe3,0xb0,0xfb,0x21,0x38,0x28,0x2,0xc1,0x53,0x5d,0xd7,0xb3,0x3d,0xcd,0x23,0x69,0xf9,0x1,0x71,0x37,0x13,0xc0,0xf8,0xee,0x9b,0xf3,0x59,0x85,0xdc,0x77,0x63,0x5b,0x65,0x7e,0x3f,0x51,0xff,0x68,0xdd,0xd4,0xbf,0x54,0xe2,0x2c,0x60,0x8c,0xb8,0xab,0xc7,0x12,0xca,0xbe,0x1b,0xbd,0xf,0x87,0xd1,0x9,0x33,0x20,0x90,0x8,0x42,0x4d,0x92,0x5c,0x47,0xcb,0x7a,0xd0,0xb9,0xe4,0xb4,0x34,0x9f,0x4f,0x79,0xc2,0xea,0x6b,0x6a,0x57,0x32,0xd6,0x43,0x11,0x55,0xdb,0xa1,0x8e,0xb2,0x83,0x73,0xd2,0x1f,0xc6,0xb1,0xcc,0x44,0xd8,0xa9,0x84,0xf0,0xe8,0x9e,0x45,0x5,0x15,0x50,0x8f,0xb,0x99,0x1e,0x4e,0x6d,0xc4,0x3c,0xf6,0x26,0xc,0xda,0xf4,0xc5,0xa8,0xef,0x10,0xbc,0x3a,0xdf,0x40,0x88,0x4a,0x39,0x6c,0x36,0x96,0xd,0x78,0xba,0x94,0x62,0xa2,0x2d,0xfc,0xb5,0xb7,0x14,0xfa,0x49,0x25,0x82,0x59,0xff,0x10,0x3f,0x56,0x48,0xa9,0xcd,0x60,0xfe,0x27,0x85,0x25,0xa8,0x91,0x29,0xd,0xca,0x45,0x63,0xd9,0x4f,0xb2,0x43,0x22,0xc9,0x30,0x1d,0x4e,0xba,0xe0,0x6,0xbe,0x1,0x62,0x39,0x53,0xce,0x5d,0x5b,0xf5,0xc8,0x2b,0xd4,0x90,0xbc,0x98,0xea,0xe9,0x52,0x88,0x17,0xec,0x94,0x44,0xf0,0x2f,0x28,0xe5,0x5f,0xa0,0x82,0xb5,0xaf,0x6b,0xc0,0xb3,0xfb,0x61,0xb8,0xd7,0xe7,0x6e,0x33,0x8f,0x9c,0x6d,0x2e,0xd8,0xd3,0xe4,0x75,0x47,0xbb,0x69,0x64,0x54,0xa1,0x68,0xd6,0xc4,0xaa,0x0,0xe1,0x78,0x6a,0xf,0x9d,0x93,0x19,0x7d,0xf3,0x3,0xed,0xa7,0x37,0xcf,0xbf,0xf9,0xdd,0xe,0x36,0x3b,0x23,0x1b,0x7,0xb1,0xd2,0x86,0x96,0x3c,0x2d,0x7e,0x35,0xef,0xf6,0xe6,0xcc,0x1a,0x71,0x9a,0x2c,0xe2,0xae,0x42,0x76,0x65,0x9,0xdc,0x4,0x70,0xd5,0x73,0xc1,0x20,0x55,0x3d,0x97,0x4b,0x12,0xb9,0xad,0x95,0xab,0xb0,0xf1,0x9f,0x31,0xa6,0x13,0x2a,0x7a,0xfa,0x51,0x81,0xb7,0xc,0x24,0xa5,0xa4,0x99,0xfc,0x18,0x8d,0xdf,0x9b,0x49,0x1f,0xc7,0xfd,0xee,0x5e,0xc6,0x8c,0x83,0x5c,0x92,0x89,0x5,0xb4,0x1e,0x77,0x26,0x50,0x8b,0xcb,0xdb,0x9e,0x41,0xc5,0x57,0xd0,0x80,0xa3,0xa,0xf2,0x38,0xe8,0x15,0x6f,0x40,0x7c,0x4d,0xbd,0x1c,0xd1,0x8,0x7f,0x2,0x8a,0x16,0x67,0x4a,0x3e,0x58,0xc3,0xb6,0x74,0x5a,0xac,0x6c,0xe3,0x32,0x7b,0x79,0xda,0x34,0x87,0xeb,0x4c,0xc2,0x14,0x3a,0xb,0x66,0x21,0xde,0x72,0xf4,0x11,0x8e,0x46,0x84,0xf7,0xa2,0xf8,0x81,0xac,0x55,0xbe,0x9a,0x7c,0x26,0xd2,0xff,0xd9,0x56,0x91,0xdf,0x2e,0xd3,0x45,0x19,0xbb,0x62,0xfc,0xb5,0xd,0x34,0xb9,0xa3,0x8c,0x63,0xc5,0x51,0x35,0xd4,0xca,0xc3,0x79,0xb4,0xb3,0x33,0x29,0x1e,0x3c,0x8b,0x14,0xce,0x75,0x6c,0xd8,0x8,0x70,0x48,0xb7,0x54,0x69,0x76,0x4,0x20,0xc,0xa5,0xfe,0x9d,0x22,0xc7,0xc1,0x52,0xcf,0x36,0x58,0x4a,0xf4,0xf6,0xe4,0x7d,0x9c,0x27,0xdb,0xe9,0x78,0x3d,0xc8,0xf8,0xf5,0x0,0x13,0xaf,0xf2,0x4f,0x44,0xb2,0xf1,0x67,0x2f,0x5c,0xf7,0x7b,0x4b,0x24,0xfd,0xa9,0xe2,0xb1,0xa0,0x50,0x7a,0x6a,0x73,0x9b,0x87,0xbf,0xa7,0xa,0x1a,0x4e,0x2d,0x23,0x53,0xab,0x3b,0xaa,0x92,0x41,0x65,0x85,0xf,0x1,0x93,0x71,0x9f,0x6f,0xe1,0x6d,0x2c,0x37,0x9,0x8f,0x3a,0xad,0x3,0xb,0xa1,0xc9,0xbc,0x31,0x25,0x8e,0xd7,0x98,0x40,0x95,0xf9,0x5d,0xef,0x49,0xec,0xb0,0x6,0xed,0x86,0xea,0xde,0x32,0x7e,0x15,0xe,0xc0,0x1f,0xeb,0x82,0x28,0x99,0x61,0x5b,0x83,0xd5,0x10,0x5a,0xc2,0x72,0x60,0x5,0x38,0x39,0x7,0x43,0x11,0x84,0xcd,0x66,0xe6,0xb6,0xb8,0x90,0x2b,0x1d,0x16,0x9e,0xe3,0x94,0xa2,0xd6,0xfb,0x8a,0xe0,0xdc,0xf3,0x89,0x4d,0x80,0x21,0xd1,0x3f,0x1c,0x4c,0xcb,0x74,0xa4,0x6e,0x96,0x57,0x17,0xcc,0xba,0x59,0xdd,0x2,0x47,0xda,0x12,0x8d,0x68,0x64,0x3e,0x6b,0x18,0x97,0xa6,0x88,0x5e,0xee,0x42,0xbd,0xfa,0x46,0xe5,0xe7,0xae,0xd0,0x77,0x1b,0xa8,0xe8,0x2a,0x5f,0xc4,0x7f,0xf0,0x30,0xc6,0x2e,0x28,0x20,0xbd,0x11,0x4a,0xcd,0x72,0xeb,0x99,0xe3,0xcf,0x58,0xa7,0x86,0xbb,0x37,0x83,0x9f,0xe7,0xfb,0x64,0x9a,0x21,0xc6,0xdc,0xd3,0xf1,0x96,0x2c,0x5c,0x5b,0xda,0xbe,0x25,0x3b,0x63,0x4c,0x2a,0x8c,0xe2,0x5a,0x56,0xdb,0x54,0xf6,0x13,0x8d,0xc1,0x30,0xaa,0x3c,0x36,0x10,0x7e,0xb9,0x93,0x75,0x3d,0xc9,0x43,0x6e,0x51,0xba,0x70,0x9e,0xe,0x80,0xe0,0x6a,0x7c,0xee,0x7d,0x45,0x8a,0xae,0xbc,0xcc,0xd4,0x44,0xf5,0xe5,0xc2,0xa1,0x68,0x74,0x48,0x50,0x95,0xbf,0x9c,0x85,0xd,0x46,0x4f,0x5e,0xa4,0x94,0x12,0xcb,0xc0,0x88,0x18,0xb3,0xab,0xa0,0x1e,0x5d,0xfc,0xef,0x1d,0x40,0x27,0xd2,0x1a,0x17,0x34,0xc8,0x97,0x6,0xb,0x19,0x73,0x92,0xb7,0xd9,0x1b,0xa5,0x7f,0x57,0xf2,0xc4,0x89,0x22,0x59,0x9,0xac,0xe8,0x6b,0xfe,0xea,0x8f,0xd6,0xd7,0xb5,0xff,0x9d,0x2d,0xb4,0x8e,0x3a,0x6c,0x6d,0x4,0x76,0xc7,0xe1,0xfa,0xf0,0x2f,0x31,0x5,0x91,0xdd,0xe9,0x5f,0x69,0x2,0x0,0xb2,0x3,0xa6,0xaf,0x77,0x16,0x7a,0xca,0xde,0x38,0x61,0x4e,0xe4,0x53,0x26,0xd5,0x60,0xec,0x42,0xc3,0x82,0xe6,0xd8,0x1f,0x90,0x29,0xdf,0xc5,0x7,0x2b,0xb0,0x98,0x3f,0x47,0xf4,0xa,0xa9,0x41,0x8,0xad,0x1,0x15,0x52,0x49,0x78,0xb1,0x67,0xd1,0x8b,0xf7,0x84,0xfd,0x35,0x87,0x62,0x32,0xb6,0xa8,0xed,0xf8,0xb8,0x55,0x23,0x4b,0x9b,0x79,0x81,0xf3,0xd0,0x24,0xa3,0x6f,0xa2,0x3e,0xce,0x33,0xf,0x66,0x1c,0x39,0x4d,0x65,0x14,0x71,0xf9,0x7b,0xc,0xf3,0x34,0xbb,0x9d,0x27,0xb1,0x4c,0xbd,0xdc,0x37,0xce,0xe3,0xb0,0x44,0x1e,0xf8,0xa7,0x1,0xee,0xc1,0xa8,0xb6,0x57,0x33,0x9e,0x0,0xd9,0x7b,0xdb,0x56,0x6f,0xd7,0x17,0xac,0x76,0xe9,0x12,0x6a,0xba,0xe,0xd1,0xd6,0x1b,0xa1,0x5e,0x7c,0x4b,0x51,0x40,0xff,0x9c,0xc7,0xad,0x30,0xa3,0xa5,0xb,0x36,0xd5,0x2a,0x6e,0x42,0x66,0x14,0x1a,0x8b,0xb9,0x45,0x97,0x9a,0xaa,0x5f,0x96,0x28,0x3a,0x54,0xfe,0x1f,0x86,0x94,0x95,0x3e,0x4d,0x5,0x9f,0x46,0x29,0x19,0x90,0xcd,0x71,0x62,0x93,0xd0,0x26,0x2d,0xc5,0xdd,0xe5,0xf9,0x4f,0x2c,0x78,0x68,0xc2,0xd3,0x80,0xcb,0x11,0x8,0x18,0x32,0xf1,0x63,0x6d,0xe7,0x83,0xd,0xfd,0x13,0x59,0xc9,0x31,0x41,0x7,0x23,0xf0,0xc8,0xde,0xab,0xc3,0x69,0xb5,0xec,0x47,0x53,0x6b,0x55,0x4e,0xf,0x61,0xcf,0x58,0xed,0xe4,0x8f,0x64,0xd2,0x1c,0x50,0xbc,0x88,0x9b,0xf7,0x22,0xfa,0x8e,0x2b,0x8d,0x3f,0xb7,0xe1,0x39,0x3,0x10,0xa0,0x38,0x72,0x7d,0xa2,0x6c,0x77,0xfb,0x4a,0xe0,0x89,0xd4,0x84,0x4,0xaf,0x7f,0x49,0xf2,0xda,0x5b,0x5a,0x67,0x2,0xe6,0x73,0x21,0x65,0xeb,0x91,0xbe,0x82,0xb3,0x43,0xe2,0x2f,0xf6,0x81,0xfc,0x74,0xe8,0x99,0xb4,0xc0,0xd8,0xae,0x75,0x35,0x25,0x60,0xbf,0x3b,0xa9,0x2e,0x7e,0x5d,0xf4,0xc,0xc6,0x16,0x3c,0xea,0xc4,0xf5,0x98,0xdf,0x20,0x8c,0xa,0xef,0x70,0xb8,0x7a,0x9,0x5c,0x6,0xa6,0x3d,0x48,0x8a,0xa4,0x52,0x92,0x1d,0xcc,0x85,0x87,0x24,0xca,0x79,0x15,0xb2,0xc8,0x6e,0x81,0xae,0xc7,0xd9,0x38,0x5c,0xf1,0x6f,0xb6,0x14,0xb4,0x39,0x0,0xb8,0x9c,0x5b,0xd4,0xf2,0x48,0xde,0x23,0xd2,0xb3,0x58,0xa1,0x8c,0xdf,0x2b,0x71,0x97,0x2f,0x90,0xf3,0xa8,0xc2,0x5f,0xcc,0xca,0x64,0x59,0xba,0x45,0x1,0x2d,0x9,0x7b,0x78,0xc3,0x19,0x86,0x7d,0x5,0xd5,0x61,0xbe,0xb9,0x74,0xce,0x31,0x13,0x24,0x3e,0xfa,0x51,0x22,0x6a,0xf0,0x29,0x46,0x76,0xff,0xa2,0x1e,0xd,0xfc,0xbf,0x49,0x42,0x75,0xe4,0xd6,0x2a,0xf8,0xf5,0xc5,0x30,0xf9,0x47,0x55,0x3b,0x91,0x70,0xe9,0xfb,0x9e,0xc,0x2,0x88,0xec,0x62,0x92,0x7c,0x36,0xa6,0x5e,0x2e,0x68,0x4c,0x9f,0xa7,0xaa,0xb2,0x8a,0x96,0x20,0x43,0x17,0x7,0xad,0xbc,0xef,0xa4,0x7e,0x67,0x77,0x5d,0x8b,0xe0,0xb,0xbd,0x73,0x3f,0xd3,0xe7,0xf4,0x98,0x4d,0x95,0xe1,0x44,0xe2,0x50,0xb1,0xc4,0xac,0x6,0xda,0x83,0x28,0x3c,0x4,0x3a,0x21,0x60,0xe,0xa0,0x37,0x82,0xbb,0xeb,0x6b,0xc0,0x10,0x26,0x9d,0xb5,0x34,0x35,0x8,0x6d,0x89,0x1c,0x4e,0xa,0xd8,0x8e,0x56,0x6c,0x7f,0xcf,0x57,0x1d,0x12,0xcd,0x3,0x18,0x94,0x25,0x8f,0xe6,0xb7,0xc1,0x1a,0x5a,0x4a,0xf,0xd0,0x54,0xc6,0x41,0x11,0x32,0x9b,0x63,0xa9,0x79,0x84,0xfe,0xd1,0xed,0xdc,0x2c,0x8d,0x40,0x99,0xee,0x93,0x1b,0x87,0xf6,0xdb,0xaf,0xc9,0x52,0x27,0xe5,0xcb,0x3d,0xfd,0x72,0xa3,0xea,0xe8,0x4b,0xa5,0x16,0x7a,0xdd,0x53,0x85,0xab,0x9a,0xf7,0xb0,0x4f,0xe3,0x65,0x80,0x1f,0xd7,0x15,0x66,0x33,0x69,0x2d,0x0,0xf9,0x12,0x36,0xd0,0x8a,0x7e,0x53,0x75,0xfa,0x3d,0x73,0x82,0x7f,0xe9,0xb5,0x17,0xce,0x50,0x19,0xa1,0x98,0x15,0xf,0x20,0xcf,0x69,0xfd,0x99,0x78,0x66,0x6f,0xd5,0x18,0x1f,0x9f,0x85,0xb2,0x90,0x27,0xb8,0x62,0xd9,0xc0,0x74,0xa4,0xdc,0xe4,0x1b,0xf8,0xc5,0xda,0xa8,0x8c,0xa0,0x9,0x52,0x31,0x8e,0x6b,0x6d,0xfe,0x63,0x9a,0xf4,0xe6,0x58,0x5a,0x48,0xd1,0x30,0x8b,0x77,0x45,0xd4,0x91,0x64,0x54,0x59,0xac,0xbf,0x3,0x5e,0xe3,0xe8,0x1e,0x5d,0xcb,0x83,0xf0,0x5b,0xd7,0xe7,0x88,0x51,0x5,0x4e,0x1d,0xc,0xfc,0xd6,0xc6,0xdf,0x37,0x2b,0x13,0xb,0xa6,0xb6,0xe2,0x81,0x8f,0xff,0x7,0x97,0x6,0x3e,0xed,0xc9,0x29,0xa3,0xad,0x3f,0xdd,0x33,0xc3,0x4d,0xc1,0x80,0x9b,0xa5,0x23,0x96,0x1,0xaf,0xa7,0xd,0x65,0x10,0x9d,0x89,0x22,0x7b,0x34,0xec,0x39,0x55,0xf1,0x43,0xe5,0x40,0x1c,0xaa,0x41,0x2a,0x46,0x72,0x9e,0xd2,0xb9,0xa2,0x6c,0xb3,0x47,0x2e,0x84,0x35,0xcd,0xf7,0x2f,0x79,0xbc,0xf6,0x6e,0xde,0xcc,0xa9,0x94,0x95,0xab,0xef,0xbd,0x28,0x61,0xca,0x4a,0x1a,0x14,0x3c,0x87,0xb1,0xba,0x32,0x4f,0x38,0xe,0x7a,0x57,0x26,0x4c,0x70,0x5f,0x25,0xe1,0x2c,0x8d,0x7d,0x93,0xb0,0xe0,0x67,0xd8,0x8,0xc2,0x3a,0xfb,0xbb,0x60,0x16,0xf5,0x71,0xae,0xeb,0x76,0xbe,0x21,0xc4,0xc8,0x92,0xc7,0xb4,0x3b,0xa,0x24,0xf2,0x42,0xee,0x11,0x56,0xea,0x49,0x4b,0x2,0x7c,0xdb,0xb7,0x4,0x44,0x86,0xf3,0x68,0xd3,0x5c,0x9c,0x6a,0x43,0x45,0x4d,0xd0,0x7c,0x27,0xa0,0x1f,0x86,0xf4,0x8e,0xa2,0x35,0xca,0xeb,0xd6,0x5a,0xee,0xf2,0x8a,0x96,0x9,0xf7,0x4c,0xab,0xb1,0xbe,0x9c,0xfb,0x41,0x31,0x36,0xb7,0xd3,0x48,0x56,0xe,0x21,0x47,0xe1,0x8f,0x37,0x3b,0xb6,0x39,0x9b,0x7e,0xe0,0xac,0x5d,0xc7,0x51,0x5b,0x7d,0x13,0xd4,0xfe,0x18,0x50,0xa4,0x2e,0x3,0x3c,0xd7,0x1d,0xf3,0x63,0xed,0x8d,0x7,0x11,0x83,0x10,0x28,0xe7,0xc3,0xd1,0xa1,0xb9,0x29,0x98,0x88,0xaf,0xcc,0x5,0x19,0x25,0x3d,0xf8,0xd2,0xf1,0xe8,0x60,0x2b,0x22,0x33,0xc9,0xf9,0x7f,0xa6,0xad,0xe5,0x75,0xde,0xc6,0xcd,0x73,0x30,0x91,0x82,0x70,0x2d,0x4a,0xbf,0x77,0x7a,0x59,0xa5,0xfa,0x6b,0x66,0x74,0x1e,0xff,0xda,0xb4,0x76,0xc8,0x12,0x3a,0x9f,0xa9,0xe4,0x4f,0x34,0x64,0xc1,0x85,0x6,0x93,0x87,0xe2,0xbb,0xba,0xd8,0x92,0xf0,0x40,0xd9,0xe3,0x57,0x1,0x0,0x69,0x1b,0xaa,0x8c,0x97,0x9d,0x42,0x5c,0x68,0xfc,0xb0,0x84,0x32,0x4,0x6f,0x6d,0xdf,0x6e,0xcb,0xc2,0x1a,0x7b,0x17,0xa7,0xb3,0x55,0xc,0x23,0x89,0x3e,0x4b,0xb8,0xd,0x81,0x2f,0xae,0xef,0x8b,0xb5,0x72,0xfd,0x44,0xb2,0xa8,0x6a,0x46,0xdd,0xf5,0x52,0x2a,0x99,0x67,0xc4,0x2c,0x65,0xc0,0x6c,0x78,0x3f,0x24,0x15,0xdc,0xa,0xbc,0xe6,0x9a,0xe9,0x90,0x58,0xea,0xf,0x5f,0xdb,0xc5,0x80,0x95,0xd5,0x38,0x4e,0x26,0xf6,0x14,0xec,0x9e,0xbd,0x49,0xce,0x2,0xcf,0x53,0xa3,0x5e,0x62,0xb,0x71,0x54,0x20,0x8,0x79,0x1c,0x94,0x16,0x61,0xc4,0x3,0x8c,0xaa,0x10,0x86,0x7b,0x8a,0xeb,0x0,0xf9,0xd4,0x87,0x73,0x29,0xcf,0x90,0x36,0xd9,0xf6,0x9f,0x81,0x60,0x4,0xa9,0x37,0xee,0x4c,0xec,0x61,0x58,0xe0,0x20,0x9b,0x41,0xde,0x25,0x5d,0x8d,0x39,0xe6,0xe1,0x2c,0x96,0x69,0x4b,0x7c,0x66,0x77,0xc8,0xab,0xf0,0x9a,0x7,0x94,0x92,0x3c,0x1,0xe2,0x1d,0x59,0x75,0x51,0x23,0x2d,0xbc,0x8e,0x72,0xa0,0xad,0x9d,0x68,0xa1,0x1f,0xd,0x63,0xc9,0x28,0xb1,0xa3,0xa2,0x9,0x7a,0x32,0xa8,0x71,0x1e,0x2e,0xa7,0xfa,0x46,0x55,0xa4,0xe7,0x11,0x1a,0xf2,0xea,0xd2,0xce,0x78,0x1b,0x4f,0x5f,0xf5,0xe4,0xb7,0xfc,0x26,0x3f,0x2f,0x5,0xc6,0x54,0x5a,0xd0,0xb4,0x3a,0xca,0x24,0x6e,0xfe,0x6,0x76,0x30,0x14,0xc7,0xff,0xe9,0x9c,0xf4,0x5e,0x82,0xdb,0x70,0x64,0x5c,0x62,0x79,0x38,0x56,0xf8,0x6f,0xda,0xd3,0xb8,0x53,0xe5,0x2b,0x67,0x8b,0xbf,0xac,0xc0,0x15,0xcd,0xb9,0x1c,0xba,0x8,0x80,0xd6,0xe,0x34,0x27,0x97,0xf,0x45,0x4a,0x95,0x5b,0x40,0xcc,0x7d,0xd7,0xbe,0xe3,0xb3,0x33,0x98,0x48,0x7e,0xc5,0xed,0x6c,0x6d,0x50,0x35,0xd1,0x44,0x16,0x52,0xdc,0xa6,0x89,0xb5,0x84,0x74,0xd5,0x18,0xc1,0xb6,0xcb,0x43,0xdf,0xae,0x83,0xf7,0xef,0x99,0x42,0x2,0x12,0x57,0x88,0xc,0x9e,0x19,0x49,0x6a,0xc3,0x3b,0xf1,0x21,0xb,0xdd,0xf3,0xc2,0xaf,0xe8,0x17,0xbb,0x3d,0xd8,0x47,0x8f,0x4d,0x3e,0x6b,0x31,0x91,0xa,0x7f,0xbd,0x93,0x65,0xa5,0x2a,0xfb,0xb2,0xb0,0x13,0xfd,0x4e,0x22,0x85,0x45,0xe3,0xc,0x23,0x4a,0x54,0xb5,0xd1,0x7c,0xe2,0x3b,0x99,0x39,0xb4,0x8d,0x35,0x11,0xd6,0x59,0x7f,0xc5,0x53,0xae,0x5f,0x3e,0xd5,0x2c,0x1,0x52,0xa6,0xfc,0x1a,0xa2,0x1d,0x7e,0x25,0x4f,0xd2,0x41,0x47,0xe9,0xd4,0x37,0xc8,0x8c,0xa0,0x84,0xf6,0xf5,0x4e,0x94,0xb,0xf0,0x88,0x58,0xec,0x33,0x34,0xf9,0x43,0xbc,0x9e,0xa9,0xb3,0x77,0xdc,0xaf,0xe7,0x7d,0xa4,0xcb,0xfb,0x72,0x2f,0x93,0x80,0x71,0x32,0xc4,0xcf,0xf8,0x69,0x5b,0xa7,0x75,0x78,0x48,0xbd,0x74,0xca,0xd8,0xb6,0x1c,0xfd,0x64,0x76,0x13,0x81,0x8f,0x5,0x61,0xef,0x1f,0xf1,0xbb,0x2b,0xd3,0xa3,0xe5,0xc1,0x12,0x2a,0x27,0x3f,0x7,0x1b,0xad,0xce,0x9a,0x8a,0x20,0x31,0x62,0x29,0xf3,0xea,0xfa,0xd0,0x6,0x6d,0x86,0x30,0xfe,0xb2,0x5e,0x6a,0x79,0x15,0xc0,0x18,0x6c,0xc9,0x6f,0xdd,0x3c,0x49,0x21,0x8b,0x57,0xe,0xa5,0xb1,0x89,0xb7,0xac,0xed,0x83,0x2d,0xba,0xf,0x36,0x66,0xe6,0x4d,0x9d,0xab,0x10,0x38,0xb9,0xb8,0x85,0xe0,0x4,0x91,0xc3,0x87,0x55,0x3,0xdb,0xe1,0xf2,0x42,0xda,0x90,0x9f,0x40,0x8e,0x95,0x19,0xa8,0x2,0x6b,0x3a,0x4c,0x97,0xd7,0xc7,0x82,0x5d,0xd9,0x4b,0xcc,0x9c,0xbf,0x16,0xee,0x24,0xf4,0x9,0x73,0x5c,0x60,0x51,0xa1,0x0,0xcd,0x14,0x63,0x1e,0x96,0xa,0x7b,0x56,0x22,0x44,0xdf,0xaa,0x68,0x46,0xb0,0x70,0xff,0x2e,0x67,0x65,0xc6,0x28,0x9b,0xf7,0x50,0xde,0x8,0x26,0x17,0x7a,0x3d,0xc2,0x6e,0xe8,0xd,0x92,0x5a,0x98,0xeb,0xbe,0xe4,0x93,0xbe,0x47,0xac,0x88,0x6e,0x34,0xc0,0xed,0xcb,0x44,0x83,0xcd,0x3c,0xc1,0x57,0xb,0xa9,0x70,0xee,0xa7,0x1f,0x26,0xab,0xb1,0x9e,0x71,0xd7,0x43,0x27,0xc6,0xd8,0xd1,0x6b,0xa6,0xa1,0x21,0x3b,0xc,0x2e,0x99,0x6,0xdc,0x67,0x7e,0xca,0x1a,0x62,0x5a,0xa5,0x46,0x7b,0x64,0x16,0x32,0x1e,0xb7,0xec,0x8f,0x30,0xd5,0xd3,0x40,0xdd,0x24,0x4a,0x58,0xe6,0xe4,0xf6,0x6f,0x8e,0x35,0xc9,0xfb,0x6a,0x2f,0xda,0xea,0xe7,0x12,0x1,0xbd,0xe0,0x5d,0x56,0xa0,0xe3,0x75,0x3d,0x4e,0xe5,0x69,0x59,0x36,0xef,0xbb,0xf0,0xa3,0xb2,0x42,0x68,0x78,0x61,0x89,0x95,0xad,0xb5,0x18,0x8,0x5c,0x3f,0x31,0x41,0xb9,0x29,0xb8,0x80,0x53,0x77,0x97,0x1d,0x13,0x81,0x63,0x8d,0x7d,0xf3,0x7f,0x3e,0x25,0x1b,0x9d,0x28,0xbf,0x11,0x19,0xb3,0xdb,0xae,0x23,0x37,0x9c,0xc5,0x8a,0x52,0x87,0xeb,0x4f,0xfd,0x5b,0xfe,0xa2,0x14,0xff,0x94,0xf8,0xcc,0x20,0x6c,0x7,0x1c,0xd2,0xd,0xf9,0x90,0x3a,0x8b,0x73,0x49,0x91,0xc7,0x2,0x48,0xd0,0x60,0x72,0x17,0x2a,0x2b,0x15,0x51,0x3,0x96,0xdf,0x74,0xf4,0xa4,0xaa,0x82,0x39,0xf,0x4,0x8c,0xf1,0x86,0xb0,0xc4,0xe9,0x98,0xf2,0xce,0xe1,0x9b,0x5f,0x92,0x33,0xc3,0x2d,0xe,0x5e,0xd9,0x66,0xb6,0x7c,0x84,0x45,0x5,0xde,0xa8,0x4b,0xcf,0x10,0x55,0xc8,0x0,0x9f,0x7a,0x76,0x2c,0x79,0xa,0x85,0xb4,0x9a,0x4c,0xfc,0x50,0xaf,0xe8,0x54,0xf7,0xf5,0xbc,0xc2,0x65,0x9,0xba,0xfa,0x38,0x4d,0xd6,0x6d,0xe2,0x22,0xd4,0x52,0x54,0x5c,0xc1,0x6d,0x36,0xb1,0xe,0x97,0xe5,0x9f,0xb3,0x24,0xdb,0xfa,0xc7,0x4b,0xff,0xe3,0x9b,0x87,0x18,0xe6,0x5d,0xba,0xa0,0xaf,0x8d,0xea,0x50,0x20,0x27,0xa6,0xc2,0x59,0x47,0x1f,0x30,0x56,0xf0,0x9e,0x26,0x2a,0xa7,0x28,0x8a,0x6f,0xf1,0xbd,0x4c,0xd6,0x40,0x4a,0x6c,0x2,0xc5,0xef,0x9,0x41,0xb5,0x3f,0x12,0x2d,0xc6,0xc,0xe2,0x72,0xfc,0x9c,0x16,0x0,0x92,0x1,0x39,0xf6,0xd2,0xc0,0xb0,0xa8,0x38,0x89,0x99,0xbe,0xdd,0x14,0x8,0x34,0x2c,0xe9,0xc3,0xe0,0xf9,0x71,0x3a,0x33,0x22,0xd8,0xe8,0x6e,0xb7,0xbc,0xf4,0x64,0xcf,0xd7,0xdc,0x62,0x21,0x80,0x93,0x61,0x3c,0x5b,0xae,0x66,0x6b,0x48,0xb4,0xeb,0x7a,0x77,0x65,0xf,0xee,0xcb,0xa5,0x67,0xd9,0x3,0x2b,0x8e,0xb8,0xf5,0x5e,0x25,0x75,0xd0,0x94,0x17,0x82,0x96,0xf3,0xaa,0xab,0xc9,0x83,0xe1,0x51,0xc8,0xf2,0x46,0x10,0x11,0x78,0xa,0xbb,0x9d,0x86,0x8c,0x53,0x4d,0x79,0xed,0xa1,0x95,0x23,0x15,0x7e,0x7c,0xce,0x7f,0xda,0xd3,0xb,0x6a,0x6,0xb6,0xa2,0x44,0x1d,0x32,0x98,0x2f,0x5a,0xa9,0x1c,0x90,0x3e,0xbf,0xfe,0x9a,0xa4,0x63,0xec,0x55,0xa3,0xb9,0x7b,0x57,0xcc,0xe4,0x43,0x3b,0x88,0x76,0xd5,0x3d,0x74,0xd1,0x7d,0x69,0x2e,0x35,0x4,0xcd,0x1b,0xad,0xf7,0x8b,0xf8,0x81,0x49,0xfb,0x1e,0x4e,0xca,0xd4,0x91,0x84,0xc4,0x29,0x5f,0x37,0xe7,0x5,0xfd,0x8f,0xac,0x58,0xdf,0x13,0xde,0x42,0xb2,0x4f,0x73,0x1a,0x60,0x45,0x31,0x19,0x68,0xd,0x85,0x7,0x70,0x76,0xb1,0x3e,0x18,0xa2,0x34,0xc9,0x38,0x59,0xb2,0x4b,0x66,0x35,0xc1,0x9b,0x7d,0x22,0x84,0x6b,0x44,0x2d,0x33,0xd2,0xb6,0x1b,0x85,0x5c,0xfe,0x5e,0xd3,0xea,0x52,0x92,0x29,0xf3,0x6c,0x97,0xef,0x3f,0x8b,0x54,0x53,0x9e,0x24,0xdb,0xf9,0xce,0xd4,0xc5,0x7a,0x19,0x42,0x28,0xb5,0x26,0x20,0x8e,0xb3,0x50,0xaf,0xeb,0xc7,0xe3,0x91,0x9f,0xe,0x3c,0xc0,0x12,0x1f,0x2f,0xda,0x13,0xad,0xbf,0xd1,0x7b,0x9a,0x3,0x11,0x10,0xbb,0xc8,0x80,0x1a,0xc3,0xac,0x9c,0x15,0x48,0xf4,0xe7,0x16,0x55,0xa3,0xa8,0x40,0x58,0x60,0x7c,0xca,0xa9,0xfd,0xed,0x47,0x56,0x5,0x4e,0x94,0x8d,0x9d,0xb7,0x74,0xe6,0xe8,0x62,0x6,0x88,0x78,0x96,0xdc,0x4c,0xb4,0xc4,0x82,0xa6,0x75,0x4d,0x5b,0x2e,0x46,0xec,0x30,0x69,0xc2,0xd6,0xee,0xd0,0xcb,0x8a,0xe4,0x4a,0xdd,0x68,0x61,0xa,0xe1,0x57,0x99,0xd5,0x39,0xd,0x1e,0x72,0xa7,0x7f,0xb,0xae,0x8,0xba,0x32,0x64,0xbc,0x86,0x95,0x25,0xbd,0xf7,0xf8,0x27,0xe9,0xf2,0x7e,0xcf,0x65,0xc,0x51,0x1,0x81,0x2a,0xfa,0xcc,0x77,0x5f,0xde,0xdf,0xe2,0x87,0x63,0xf6,0xa4,0xe0,0x6e,0x14,0x3b,0x7,0x36,0xc6,0x67,0xaa,0x73,0x4,0x79,0xf1,0x6d,0x1c,0x31,0x45,0x5d,0x2b,0xf0,0xb0,0xa0,0xe5,0x3a,0xbe,0x2c,0xab,0xfb,0xd8,0x71,0x89,0x43,0x93,0xb9,0x6f,0x41,0x70,0x1d,0x5a,0xa5,0x9,0x8f,0x6a,0xf5,0x3d,0xff,0x8c,0xd9,0x83,0x23,0xb8,0xcd,0xf,0x21,0xd7,0x17,0x98,0x49,0x0,0x2,0xa1,0x4f,0xfc,0x90,0x37,0x86,0x20,0xcf,0xe0,0x89,0x97,0x76,0x12,0xbf,0x21,0xf8,0x5a,0xfa,0x77,0x4e,0xf6,0xd2,0x15,0x9a,0xbc,0x6,0x90,0x6d,0x9c,0xfd,0x16,0xef,0xc2,0x91,0x65,0x3f,0xd9,0x61,0xde,0xbd,0xe6,0x8c,0x11,0x82,0x84,0x2a,0x17,0xf4,0xb,0x4f,0x63,0x47,0x35,0x36,0x8d,0x57,0xc8,0x33,0x4b,0x9b,0x2f,0xf0,0xf7,0x3a,0x80,0x7f,0x5d,0x6a,0x70,0xb4,0x1f,0x6c,0x24,0xbe,0x67,0x8,0x38,0xb1,0xec,0x50,0x43,0xb2,0xf1,0x7,0xc,0x3b,0xaa,0x98,0x64,0xb6,0xbb,0x8b,0x7e,0xb7,0x9,0x1b,0x75,0xdf,0x3e,0xa7,0xb5,0xd0,0x42,0x4c,0xc6,0xa2,0x2c,0xdc,0x32,0x78,0xe8,0x10,0x60,0x26,0x2,0xd1,0xe9,0xe4,0xfc,0xc4,0xd8,0x6e,0xd,0x59,0x49,0xe3,0xf2,0xa1,0xea,0x30,0x29,0x39,0x13,0xc5,0xae,0x45,0xf3,0x3d,0x71,0x9d,0xa9,0xba,0xd6,0x3,0xdb,0xaf,0xa,0xac,0x1e,0xff,0x8a,0xe2,0x48,0x94,0xcd,0x66,0x72,0x4a,0x74,0x6f,0x2e,0x40,0xee,0x79,0xcc,0xf5,0xa5,0x25,0x8e,0x5e,0x68,0xd3,0xfb,0x7a,0x7b,0x46,0x23,0xc7,0x52,0x0,0x44,0x96,0xc0,0x18,0x22,0x31,0x81,0x19,0x53,0x5c,0x83,0x4d,0x56,0xda,0x6b,0xc1,0xa8,0xf9,0x8f,0x54,0x14,0x4,0x41,0x9e,0x1a,0x88,0xf,0x5f,0x7c,0xd5,0x2d,0xe7,0x37,0xca,0xb0,0x9f,0xa3,0x92,0x62,0xc3,0xe,0xd7,0xa0,0xdd,0x55,0xc9,0xb8,0x95,0xe1,0x87,0x1c,0x69,0xab,0x85,0x73,0xb3,0x3c,0xed,0xa4,0xa6,0x5,0xeb,0x58,0x34,0x93,0x1d,0xcb,0xe5,0xd4,0xb9,0xfe,0x1,0xad,0x2b,0xce,0x51,0x99,0x5b,0x28,0x7d,0x27,0x6e,0x43,0xba,0x51,0x75,0x93,0xc9,0x3d,0x10,0x36,0xb9,0x7e,0x30,0xc1,0x3c,0xaa,0xf6,0x54,0x8d,0x13,0x5a,0xe2,0xdb,0x56,0x4c,0x63,0x8c,0x2a,0xbe,0xda,0x3b,0x25,0x2c,0x96,0x5b,0x5c,0xdc,0xc6,0xf1,0xd3,0x64,0xfb,0x21,0x9a,0x83,0x37,0xe7,0x9f,0xa7,0x58,0xbb,0x86,0x99,0xeb,0xcf,0xe3,0x4a,0x11,0x72,0xcd,0x28,0x2e,0xbd,0x20,0xd9,0xb7,0xa5,0x1b,0x19,0xb,0x92,0x73,0xc8,0x34,0x6,0x97,0xd2,0x27,0x17,0x1a,0xef,0xfc,0x40,0x1d,0xa0,0xab,0x5d,0x1e,0x88,0xc0,0xb3,0x18,0x94,0xa4,0xcb,0x12,0x46,0xd,0x5e,0x4f,0xbf,0x95,0x85,0x9c,0x74,0x68,0x50,0x48,0xe5,0xf5,0xa1,0xc2,0xcc,0xbc,0x44,0xd4,0x45,0x7d,0xae,0x8a,0x6a,0xe0,0xee,0x7c,0x9e,0x70,0x80,0xe,0x82,0xc3,0xd8,0xe6,0x60,0xd5,0x42,0xec,0xe4,0x4e,0x26,0x53,0xde,0xca,0x61,0x38,0x77,0xaf,0x7a,0x16,0xb2,0x0,0xa6,0x3,0x5f,0xe9,0x2,0x69,0x5,0x31,0xdd,0x91,0xfa,0xe1,0x2f,0xf0,0x4,0x6d,0xc7,0x76,0x8e,0xb4,0x6c,0x3a,0xff,0xb5,0x2d,0x9d,0x8f,0xea,0xd7,0xd6,0xe8,0xac,0xfe,0x6b,0x22,0x89,0x9,0x59,0x57,0x7f,0xc4,0xf2,0xf9,0x71,0xc,0x7b,0x4d,0x39,0x14,0x65,0xf,0x33,0x1c,0x66,0xa2,0x6f,0xce,0x3e,0xd0,0xf3,0xa3,0x24,0x9b,0x4b,0x81,0x79,0xb8,0xf8,0x23,0x55,0xb6,0x32,0xed,0xa8,0x35,0xfd,0x62,0x87,0x8b,0xd1,0x84,0xf7,0x78,0x49,0x67,0xb1,0x1,0xad,0x52,0x15,0xa9,0xa,0x8,0x41,0x3f,0x98,0xf4,0x47,0x7,0xc5,0xb0,0x2b,0x90,0x1f,0xdf,0x29,0x95,0x93,0x9b,0x6,0xaa,0xf1,0x76,0xc9,0x50,0x22,0x58,0x74,0xe3,0x1c,0x3d,0x0,0x8c,0x38,0x24,0x5c,0x40,0xdf,0x21,0x9a,0x7d,0x67,0x68,0x4a,0x2d,0x97,0xe7,0xe0,0x61,0x5,0x9e,0x80,0xd8,0xf7,0x91,0x37,0x59,0xe1,0xed,0x60,0xef,0x4d,0xa8,0x36,0x7a,0x8b,0x11,0x87,0x8d,0xab,0xc5,0x2,0x28,0xce,0x86,0x72,0xf8,0xd5,0xea,0x1,0xcb,0x25,0xb5,0x3b,0x5b,0xd1,0xc7,0x55,0xc6,0xfe,0x31,0x15,0x7,0x77,0x6f,0xff,0x4e,0x5e,0x79,0x1a,0xd3,0xcf,0xf3,0xeb,0x2e,0x4,0x27,0x3e,0xb6,0xfd,0xf4,0xe5,0x1f,0x2f,0xa9,0x70,0x7b,0x33,0xa3,0x8,0x10,0x1b,0xa5,0xe6,0x47,0x54,0xa6,0xfb,0x9c,0x69,0xa1,0xac,0x8f,0x73,0x2c,0xbd,0xb0,0xa2,0xc8,0x29,0xc,0x62,0xa0,0x1e,0xc4,0xec,0x49,0x7f,0x32,0x99,0xe2,0xb2,0x17,0x53,0xd0,0x45,0x51,0x34,0x6d,0x6c,0xe,0x44,0x26,0x96,0xf,0x35,0x81,0xd7,0xd6,0xbf,0xcd,0x7c,0x5a,0x41,0x4b,0x94,0x8a,0xbe,0x2a,0x66,0x52,0xe4,0xd2,0xb9,0xbb,0x9,0xb8,0x1d,0x14,0xcc,0xad,0xc1,0x71,0x65,0x83,0xda,0xf5,0x5f,0xe8,0x9d,0x6e,0xdb,0x57,0xf9,0x78,0x39,0x5d,0x63,0xa4,0x2b,0x92,0x64,0x7e,0xbc,0x90,0xb,0x23,0x84,0xfc,0x4f,0xb1,0x12,0xfa,0xb3,0x16,0xba,0xae,0xe9,0xf2,0xc3,0xa,0xdc,0x6a,0x30,0x4c,0x3f,0x46,0x8e,0x3c,0xd9,0x89,0xd,0x13,0x56,0x43,0x3,0xee,0x98,0xf0,0x20,0xc2,0x3a,0x48,0x6b,0x9f,0x18,0xd4,0x19,0x85,0x75,0x88,0xb4,0xdd,0xa7,0x82,0xf6,0xde,0xaf,0xca,0x42,0xc0,0xb7,0x56,0x91,0x1e,0x38,0x82,0x14,0xe9,0x18,0x79,0x92,0x6b,0x46,0x15,0xe1,0xbb,0x5d,0x2,0xa4,0x4b,0x64,0xd,0x13,0xf2,0x96,0x3b,0xa5,0x7c,0xde,0x7e,0xf3,0xca,0x72,0xb2,0x9,0xd3,0x4c,0xb7,0xcf,0x1f,0xab,0x74,0x73,0xbe,0x4,0xfb,0xd9,0xee,0xf4,0xe5,0x5a,0x39,0x62,0x8,0x95,0x6,0x0,0xae,0x93,0x70,0x8f,0xcb,0xe7,0xc3,0xb1,0xbf,0x2e,0x1c,0xe0,0x32,0x3f,0xf,0xfa,0x33,0x8d,0x9f,0xf1,0x5b,0xba,0x23,0x31,0x30,0x9b,0xe8,0xa0,0x3a,0xe3,0x8c,0xbc,0x35,0x68,0xd4,0xc7,0x36,0x75,0x83,0x88,0x60,0x78,0x40,0x5c,0xea,0x89,0xdd,0xcd,0x67,0x76,0x25,0x6e,0xb4,0xad,0xbd,0x97,0x54,0xc6,0xc8,0x42,0x26,0xa8,0x58,0xb6,0xfc,0x6c,0x94,0xe4,0xa2,0x86,0x55,0x6d,0x7b,0xe,0x66,0xcc,0x10,0x49,0xe2,0xf6,0xce,0xf0,0xeb,0xaa,0xc4,0x6a,0xfd,0x48,0x41,0x2a,0xc1,0x77,0xb9,0xf5,0x19,0x2d,0x3e,0x52,0x87,0x5f,0x2b,0x8e,0x28,0x9a,0x12,0x44,0x9c,0xa6,0xb5,0x5,0x9d,0xd7,0xd8,0x7,0xc9,0xd2,0x5e,0xef,0x45,0x2c,0x71,0x21,0xa1,0xa,0xda,0xec,0x57,0x7f,0xfe,0xff,0xc2,0xa7,0x43,0xd6,0x84,0xc0,0x4e,0x34,0x1b,0x27,0x16,0xe6,0x47,0x8a,0x53,0x24,0x59,0xd1,0x4d,0x3c,0x11,0x65,0x7d,0xb,0xd0,0x90,0x80,0xc5,0x1a,0x9e,0xc,0x8b,0xdb,0xf8,0x51,0xa9,0x63,0xb3,0x99,0x4f,0x61,0x50,0x3d,0x7a,0x85,0x29,0xaf,0x4a,0xd5,0x1d,0xdf,0xac,0xf9,0xa3,0x3,0x98,0xed,0x2f,0x1,0xf7,0x37,0xb8,0x69,0x20,0x22,0x81,0x6f,0xdc,0xb0,0x17,0x3a,0x9c,0x73,0x5c,0x35,0x2b,0xca,0xae,0x3,0x9d,0x44,0xe6,0x46,0xcb,0xf2,0x4a,0x6e,0xa9,0x26,0x0,0xba,0x2c,0xd1,0x20,0x41,0xaa,0x53,0x7e,0x2d,0xd9,0x83,0x65,0xdd,0x62,0x1,0x5a,0x30,0xad,0x3e,0x38,0x96,0xab,0x48,0xb7,0xf3,0xdf,0xfb,0x89,0x8a,0x31,0xeb,0x74,0x8f,0xf7,0x27,0x93,0x4c,0x4b,0x86,0x3c,0xc3,0xe1,0xd6,0xcc,0x8,0xa3,0xd0,0x98,0x2,0xdb,0xb4,0x84,0xd,0x50,0xec,0xff,0xe,0x4d,0xbb,0xb0,0x87,0x16,0x24,0xd8,0xa,0x7,0x37,0xc2,0xb,0xb5,0xa7,0xc9,0x63,0x82,0x1b,0x9,0x6c,0xfe,0xf0,0x7a,0x1e,0x90,0x60,0x8e,0xc4,0x54,0xac,0xdc,0x9a,0xbe,0x6d,0x55,0x58,0x40,0x78,0x64,0xd2,0xb1,0xe5,0xf5,0x5f,0x4e,0x1d,0x56,0x8c,0x95,0x85,0xaf,0x79,0x12,0xf9,0x4f,0x81,0xcd,0x21,0x15,0x6,0x6a,0xbf,0x67,0x13,0xb6,0x10,0xa2,0x43,0x36,0x5e,0xf4,0x28,0x71,0xda,0xce,0xf6,0xc8,0xd3,0x92,0xfc,0x52,0xc5,0x70,0x49,0x19,0x99,0x32,0xe2,0xd4,0x6f,0x47,0xc6,0xc7,0xfa,0x9f,0x7b,0xee,0xbc,0xf8,0x2a,0x7c,0xa4,0x9e,0x8d,0x3d,0xa5,0xef,0xe0,0x3f,0xf1,0xea,0x66,0xd7,0x7d,0x14,0x45,0x33,0xe8,0xa8,0xb8,0xfd,0x22,0xa6,0x34,0xb3,0xe3,0xc0,0x69,0x91,0x5b,0x8b,0x76,0xc,0x23,0x1f,0x2e,0xde,0x7f,0xb2,0x6b,0x1c,0x61,0xe9,0x75,0x4,0x29,0x5d,0x3b,0xa0,0xd5,0x17,0x39,0xcf,0xf,0x80,0x51,0x18,0x1a,0xb9,0x57,0xe4,0x88,0x2f,0xa1,0x77,0x59,0x68,0x5,0x42,0xbd,0x11,0x97,0x72,0xed,0x25,0xe7,0x94,0xc1,0x9b,0xda,0xf7,0xe,0xe5,0xc1,0x27,0x7d,0x89,0xa4,0x82,0xd,0xca,0x84,0x75,0x88,0x1e,0x42,0xe0,0x39,0xa7,0xee,0x56,0x6f,0xe2,0xf8,0xd7,0x38,0x9e,0xa,0x6e,0x8f,0x91,0x98,0x22,0xef,0xe8,0x68,0x72,0x45,0x67,0xd0,0x4f,0x95,0x2e,0x37,0x83,0x53,0x2b,0x13,0xec,0xf,0x32,0x2d,0x5f,0x7b,0x57,0xfe,0xa5,0xc6,0x79,0x9c,0x9a,0x9,0x94,0x6d,0x3,0x11,0xaf,0xad,0xbf,0x26,0xc7,0x7c,0x80,0xb2,0x23,0x66,0x93,0xa3,0xae,0x5b,0x48,0xf4,0xa9,0x14,0x1f,0xe9,0xaa,0x3c,0x74,0x7,0xac,0x20,0x10,0x7f,0xa6,0xf2,0xb9,0xea,0xfb,0xb,0x21,0x31,0x28,0xc0,0xdc,0xe4,0xfc,0x51,0x41,0x15,0x76,0x78,0x8,0xf0,0x60,0xf1,0xc9,0x1a,0x3e,0xde,0x54,0x5a,0xc8,0x2a,0xc4,0x34,0xba,0x36,0x77,0x6c,0x52,0xd4,0x61,0xf6,0x58,0x50,0xfa,0x92,0xe7,0x6a,0x7e,0xd5,0x8c,0xc3,0x1b,0xce,0xa2,0x6,0xb4,0x12,0xb7,0xeb,0x5d,0xb6,0xdd,0xb1,0x85,0x69,0x25,0x4e,0x55,0x9b,0x44,0xb0,0xd9,0x73,0xc2,0x3a,0x0,0xd8,0x8e,0x4b,0x1,0x99,0x29,0x3b,0x5e,0x63,0x62,0x5c,0x18,0x4a,0xdf,0x96,0x3d,0xbd,0xed,0xe3,0xcb,0x70,0x46,0x4d,0xc5,0xb8,0xcf,0xf9,0x8d,0xa0,0xd1,0xbb,0x87,0xa8,0xd2,0x16,0xdb,0x7a,0x8a,0x64,0x47,0x17,0x90,0x2f,0xff,0x35,0xcd,0xc,0x4c,0x97,0xe1,0x2,0x86,0x59,0x1c,0x81,0x49,0xd6,0x33,0x3f,0x65,0x30,0x43,0xcc,0xfd,0xd3,0x5,0xb5,0x19,0xe6,0xa1,0x1d,0xbe,0xbc,0xf5,0x8b,0x2c,0x40,0xf3,0xb3,0x71,0x4,0x9f,0x24,0xab,0x6b,0x9d,0xea,0xec,0xe4,0x79,0xd5,0x8e,0x9,0xb6,0x2f,0x5d,0x27,0xb,0x9c,0x63,0x42,0x7f,0xf3,0x47,0x5b,0x23,0x3f,0xa0,0x5e,0xe5,0x2,0x18,0x17,0x35,0x52,0xe8,0x98,0x9f,0x1e,0x7a,0xe1,0xff,0xa7,0x88,0xee,0x48,0x26,0x9e,0x92,0x1f,0x90,0x32,0xd7,0x49,0x5,0xf4,0x6e,0xf8,0xf2,0xd4,0xba,0x7d,0x57,0xb1,0xf9,0xd,0x87,0xaa,0x95,0x7e,0xb4,0x5a,0xca,0x44,0x24,0xae,0xb8,0x2a,0xb9,0x81,0x4e,0x6a,0x78,0x8,0x10,0x80,0x31,0x21,0x6,0x65,0xac,0xb0,0x8c,0x94,0x51,0x7b,0x58,0x41,0xc9,0x82,0x8b,0x9a,0x60,0x50,0xd6,0xf,0x4,0x4c,0xdc,0x77,0x6f,0x64,0xda,0x99,0x38,0x2b,0xd9,0x84,0xe3,0x16,0xde,0xd3,0xf0,0xc,0x53,0xc2,0xcf,0xdd,0xb7,0x56,0x73,0x1d,0xdf,0x61,0xbb,0x93,0x36,0x0,0x4d,0xe6,0x9d,0xcd,0x68,0x2c,0xaf,0x3a,0x2e,0x4b,0x12,0x13,0x71,0x3b,0x59,0xe9,0x70,0x4a,0xfe,0xa8,0xa9,0xc0,0xb2,0x3,0x25,0x3e,0x34,0xeb,0xf5,0xc1,0x55,0x19,0x2d,0x9b,0xad,0xc6,0xc4,0x76,0xc7,0x62,0x6b,0xb3,0xd2,0xbe,0xe,0x1a,0xfc,0xa5,0x8a,0x20,0x97,0xe2,0x11,0xa4,0x28,0x86,0x7,0x46,0x22,0x1c,0xdb,0x54,0xed,0x1b,0x1,0xc3,0xef,0x74,0x5c,0xfb,0x83,0x30,0xce,0x6d,0x85,0xcc,0x69,0xc5,0xd1,0x96,0x8d,0xbc,0x75,0xa3,0x15,0x4f,0x33,0x40,0x39,0xf1,0x43,0xa6,0xf6,0x72,0x6c,0x29,0x3c,0x7c,0x91,0xe7,0x8f,0x5f,0xbd,0x45,0x37,0x14,0xe0,0x67,0xab,0x66,0xfa,0xa,0xf7,0xcb,0xa2,0xd8,0xfd,0x89,0xa1,0xd0,0xb5,0x3d,0xbf,0xc8,0x51,0x96,0x19,0x3f,0x85,0x13,0xee,0x1f,0x7e,0x95,0x6c,0x41,0x12,0xe6,0xbc,0x5a,0x5,0xa3,0x4c,0x63,0xa,0x14,0xf5,0x91,0x3c,0xa2,0x7b,0xd9,0x79,0xf4,0xcd,0x75,0xb5,0xe,0xd4,0x4b,0xb0,0xc8,0x18,0xac,0x73,0x74,0xb9,0x3,0xfc,0xde,0xe9,0xf3,0xe2,0x5d,0x3e,0x65,0xf,0x92,0x1,0x7,0xa9,0x94,0x77,0x88,0xcc,0xe0,0xc4,0xb6,0xb8,0x29,0x1b,0xe7,0x35,0x38,0x8,0xfd,0x34,0x8a,0x98,0xf6,0x5c,0xbd,0x24,0x36,0x37,0x9c,0xef,0xa7,0x3d,0xe4,0x8b,0xbb,0x32,0x6f,0xd3,0xc0,0x31,0x72,0x84,0x8f,0x67,0x7f,0x47,0x5b,0xed,0x8e,0xda,0xca,0x60,0x71,0x22,0x69,0xb3,0xaa,0xba,0x90,0x53,0xc1,0xcf,0x45,0x21,0xaf,0x5f,0xb1,0xfb,0x6b,0x93,0xe3,0xa5,0x81,0x52,0x6a,0x7c,0x9,0x61,0xcb,0x17,0x4e,0xe5,0xf1,0xc9,0xf7,0xec,0xad,0xc3,0x6d,0xfa,0x4f,0x46,0x2d,0xc6,0x70,0xbe,0xf2,0x1e,0x2a,0x39,0x55,0x80,0x58,0x2c,0x89,0x2f,0x9d,0x15,0x43,0x9b,0xa1,0xb2,0x2,0x9a,0xd0,0xdf,0x0,0xce,0xd5,0x59,0xe8,0x42,0x2b,0x76,0x26,0xa6,0xd,0xdd,0xeb,0x50,0x78,0xf9,0xf8,0xc5,0xa0,0x44,0xd1,0x83,0xc7,0x49,0x33,0x1c,0x20,0x11,0xe1,0x40,0x8d,0x54,0x23,0x5e,0xd6,0x4a,0x3b,0x16,0x62,0x7a,0xc,0xd7,0x97,0x87,0xc2,0x1d,0x99,0xb,0x8c,0xdc,0xff,0x56,0xae,0x64,0xb4,0x9e,0x48,0x66,0x57,0x3a,0x7d,0x82,0x2e,0xa8,0x4d,0xd2,0x1a,0xd8,0xab,0xfe,0xa4,0x4,0x9f,0xea,0x28,0x6,0xf0,0x30,0xbf,0x6e,0x27,0x25,0x86,0x68,0xdb,0xb7,0x10,0x36,0x90,0x7f,0x50,0x39,0x27,0xc6,0xa2,0xf,0x91,0x48,0xea,0x4a,0xc7,0xfe,0x46,0x62,0xa5,0x2a,0xc,0xb6,0x20,0xdd,0x2c,0x4d,0xa6,0x5f,0x72,0x21,0xd5,0x8f,0x69,0xd1,0x6e,0xd,0x56,0x3c,0xa1,0x32,0x34,0x9a,0xa7,0x44,0xbb,0xff,0xd3,0xf7,0x85,0x86,0x3d,0xe7,0x78,0x83,0xfb,0x2b,0x9f,0x40,0x47,0x8a,0x30,0xcf,0xed,0xda,0xc0,0x4,0xaf,0xdc,0x94,0xe,0xd7,0xb8,0x88,0x1,0x5c,0xe0,0xf3,0x2,0x41,0xb7,0xbc,0x8b,0x1a,0x28,0xd4,0x6,0xb,0x3b,0xce,0x7,0xb9,0xab,0xc5,0x6f,0x8e,0x17,0x5,0x60,0xf2,0xfc,0x76,0x12,0x9c,0x6c,0x82,0xc8,0x58,0xa0,0xd0,0x96,0xb2,0x61,0x59,0x54,0x4c,0x74,0x68,0xde,0xbd,0xe9,0xf9,0x53,0x42,0x11,0x5a,0x80,0x99,0x89,0xa3,0x75,0x1e,0xf5,0x43,0x8d,0xc1,0x2d,0x19,0xa,0x66,0xb3,0x6b,0x1f,0xba,0x1c,0xae,0x4f,0x3a,0x52,0xf8,0x24,0x7d,0xd6,0xc2,0xfa,0xc4,0xdf,0x9e,0xf0,0x5e,0xc9,0x7c,0x45,0x15,0x95,0x3e,0xee,0xd8,0x63,0x4b,0xca,0xcb,0xf6,0x93,0x77,0xe2,0xb0,0xf4,0x26,0x70,0xa8,0x92,0x81,0x31,0xa9,0xe3,0xec,0x33,0xfd,0xe6,0x6a,0xdb,0x71,0x18,0x49,0x3f,0xe4,0xa4,0xb4,0xf1,0x2e,0xaa,0x38,0xbf,0xef,0xcc,0x65,0x9d,0x57,0x87,0x7a,0x0,0x2f,0x13,0x22,0xd2,0x73,0xbe,0x67,0x10,0x6d,0xe5,0x79,0x8,0x25,0x51,0x37,0xac,0xd9,0x1b,0x35,0xc3,0x3,0x8c,0x5d,0x14,0x16,0xb5,0x5b,0xe8,0x84,0x23,0xad,0x7b,0x55,0x64,0x9,0x4e,0xb1,0x1d,0x9b,0x7e,0xe1,0x29,0xeb,0x98,0xcd,0x97,0xa0,0x8d,0x74,0x9f,0xbb,0x5d,0x7,0xf3,0xde,0xf8,0x77,0xb0,0xfe,0xf,0xf2,0x64,0x38,0x9a,0x43,0xdd,0x94,0x2c,0x15,0x98,0x82,0xad,0x42,0xe4,0x70,0x14,0xf5,0xeb,0xe2,0x58,0x95,0x92,0x12,0x8,0x3f,0x1d,0xaa,0x35,0xef,0x54,0x4d,0xf9,0x29,0x51,0x69,0x96,0x75,0x48,0x57,0x25,0x1,0x2d,0x84,0xdf,0xbc,0x3,0xe6,0xe0,0x73,0xee,0x17,0x79,0x6b,0xd5,0xd7,0xc5,0x5c,0xbd,0x6,0xfa,0xc8,0x59,0x1c,0xe9,0xd9,0xd4,0x21,0x32,0x8e,0xd3,0x6e,0x65,0x93,0xd0,0x46,0xe,0x7d,0xd6,0x5a,0x6a,0x5,0xdc,0x88,0xc3,0x90,0x81,0x71,0x5b,0x4b,0x52,0xba,0xa6,0x9e,0x86,0x2b,0x3b,0x6f,0xc,0x2,0x72,0x8a,0x1a,0x8b,0xb3,0x60,0x44,0xa4,0x2e,0x20,0xb2,0x50,0xbe,0x4e,0xc0,0x4c,0xd,0x16,0x28,0xae,0x1b,0x8c,0x22,0x2a,0x80,0xe8,0x9d,0x10,0x4,0xaf,0xf6,0xb9,0x61,0xb4,0xd8,0x7c,0xce,0x68,0xcd,0x91,0x27,0xcc,0xa7,0xcb,0xff,0x13,0x5f,0x34,0x2f,0xe1,0x3e,0xca,0xa3,0x9,0xb8,0x40,0x7a,0xa2,0xf4,0x31,0x7b,0xe3,0x53,0x41,0x24,0x19,0x18,0x26,0x62,0x30,0xa5,0xec,0x47,0xc7,0x97,0x99,0xb1,0xa,0x3c,0x37,0xbf,0xc2,0xb5,0x83,0xf7,0xda,0xab,0xc1,0xfd,0xd2,0xa8,0x6c,0xa1,0x0,0xf0,0x1e,0x3d,0x6d,0xea,0x55,0x85,0x4f,0xb7,0x76,0x36,0xed,0x9b,0x78,0xfc,0x23,0x66,0xfb,0x33,0xac,0x49,0x45,0x1f,0x4a,0x39,0xb6,0x87,0xa9,0x7f,0xcf,0x63,0x9c,0xdb,0x67,0xc4,0xc6,0x8f,0xf1,0x56,0x3a,0x89,0xc9,0xb,0x7e,0xe5,0x5e,0xd1,0x11,0xe7,0xc5,0xc3,0xcb,0x56,0xfa,0xa1,0x26,0x99,0x0,0x72,0x8,0x24,0xb3,0x4c,0x6d,0x50,0xdc,0x68,0x74,0xc,0x10,0x8f,0x71,0xca,0x2d,0x37,0x38,0x1a,0x7d,0xc7,0xb7,0xb0,0x31,0x55,0xce,0xd0,0x88,0xa7,0xc1,0x67,0x9,0xb1,0xbd,0x30,0xbf,0x1d,0xf8,0x66,0x2a,0xdb,0x41,0xd7,0xdd,0xfb,0x95,0x52,0x78,0x9e,0xd6,0x22,0xa8,0x85,0xba,0x51,0x9b,0x75,0xe5,0x6b,0xb,0x81,0x97,0x5,0x96,0xae,0x61,0x45,0x57,0x27,0x3f,0xaf,0x1e,0xe,0x29,0x4a,0x83,0x9f,0xa3,0xbb,0x7e,0x54,0x77,0x6e,0xe6,0xad,0xa4,0xb5,0x4f,0x7f,0xf9,0x20,0x2b,0x63,0xf3,0x58,0x40,0x4b,0xf5,0xb6,0x17,0x4,0xf6,0xab,0xcc,0x39,0xf1,0xfc,0xdf,0x23,0x7c,0xed,0xe0,0xf2,0x98,0x79,0x5c,0x32,0xf0,0x4e,0x94,0xbc,0x19,0x2f,0x62,0xc9,0xb2,0xe2,0x47,0x3,0x80,0x15,0x1,0x64,0x3d,0x3c,0x5e,0x14,0x76,0xc6,0x5f,0x65,0xd1,0x87,0x86,0xef,0x9d,0x2c,0xa,0x11,0x1b,0xc4,0xda,0xee,0x7a,0x36,0x2,0xb4,0x82,0xe9,0xeb,0x59,0xe8,0x4d,0x44,0x9c,0xfd,0x91,0x21,0x35,0xd3,0x8a,0xa5,0xf,0xb8,0xcd,0x3e,0x8b,0x7,0xa9,0x28,0x69,0xd,0x33,0xf4,0x7b,0xc2,0x34,0x2e,0xec,0xc0,0x5b,0x73,0xd4,0xac,0x1f,0xe1,0x42,0xaa,0xe3,0x46,0xea,0xfe,0xb9,0xa2,0x93,0x5a,0x8c,0x3a,0x60,0x1c,0x6f,0x16,0xde,0x6c,0x89,0xd9,0x5d,0x43,0x6,0x13,0x53,0xbe,0xc8,0xa0,0x70,0x92,0x6a,0x18,0x3b,0xcf,0x48,0x84,0x49,0xd5,0x25,0xd8,0xe4,0x8d,0xf7,0xd2,0xa6,0x8e,0xff,0x9a,0x12,0x90,0xe7,0xf8,0x3f,0xb0,0x96,0x2c,0xba,0x47,0xb6,0xd7,0x3c,0xc5,0xe8,0xbb,0x4f,0x15,0xf3,0xac,0xa,0xe5,0xca,0xa3,0xbd,0x5c,0x38,0x95,0xb,0xd2,0x70,0xd0,0x5d,0x64,0xdc,0x1c,0xa7,0x7d,0xe2,0x19,0x61,0xb1,0x5,0xda,0xdd,0x10,0xaa,0x55,0x77,0x40,0x5a,0x4b,0xf4,0x97,0xcc,0xa6,0x3b,0xa8,0xae,0x0,0x3d,0xde,0x21,0x65,0x49,0x6d,0x1f,0x11,0x80,0xb2,0x4e,0x9c,0x91,0xa1,0x54,0x9d,0x23,0x31,0x5f,0xf5,0x14,0x8d,0x9f,0x9e,0x35,0x46,0xe,0x94,0x4d,0x22,0x12,0x9b,0xc6,0x7a,0x69,0x98,0xdb,0x2d,0x26,0xce,0xd6,0xee,0xf2,0x44,0x27,0x73,0x63,0xc9,0xd8,0x8b,0xc0,0x1a,0x3,0x13,0x39,0xfa,0x68,0x66,0xec,0x88,0x6,0xf6,0x18,0x52,0xc2,0x3a,0x4a,0xc,0x28,0xfb,0xc3,0xd5,0xa0,0xc8,0x62,0xbe,0xe7,0x4c,0x58,0x60,0x5e,0x45,0x4,0x6a,0xc4,0x53,0xe6,0xef,0x84,0x6f,0xd9,0x17,0x5b,0xb7,0x83,0x90,0xfc,0x29,0xf1,0x85,0x20,0x86,0x34,0xbc,0xea,0x32,0x8,0x1b,0xab,0x33,0x79,0x76,0xa9,0x67,0x7c,0xf0,0x41,0xeb,0x82,0xdf,0x8f,0xf,0xa4,0x74,0x42,0xf9,0xd1,0x50,0x51,0x6c,0x9,0xed,0x78,0x2a,0x6e,0xe0,0x9a,0xb5,0x89,0xb8,0x48,0xe9,0x24,0xfd,0x8a,0xf7,0x7f,0xe3,0x92,0xbf,0xcb,0xd3,0xa5,0x7e,0x3e,0x2e,0x6b,0xb4,0x30,0xa2,0x25,0x75,0x56,0xff,0x7,0xcd,0x1d,0x37,0xe1,0xcf,0xfe,0x93,0xd4,0x2b,0x87,0x1,0xe4,0x7b,0xb3,0x71,0x2,0x57,0xd,0xad,0x36,0x43,0x81,0xaf,0x59,0x99,0x16,0xc7,0x8e,0x8c,0x2f,0xc1,0x72,0x1e,0xb9,0xab,0xd,0xe2,0xcd,0xa4,0xba,0x5b,0x3f,0x92,0xc,0xd5,0x77,0xd7,0x5a,0x63,0xdb,0xff,0x38,0xb7,0x91,0x2b,0xbd,0x40,0xb1,0xd0,0x3b,0xc2,0xef,0xbc,0x48,0x12,0xf4,0x4c,0xf3,0x90,0xcb,0xa1,0x3c,0xaf,0xa9,0x7,0x3a,0xd9,0x26,0x62,0x4e,0x6a,0x18,0x1b,0xa0,0x7a,0xe5,0x1e,0x66,0xb6,0x2,0xdd,0xda,0x17,0xad,0x52,0x70,0x47,0x5d,0x99,0x32,0x41,0x9,0x93,0x4a,0x25,0x15,0x9c,0xc1,0x7d,0x6e,0x9f,0xdc,0x2a,0x21,0x16,0x87,0xb5,0x49,0x9b,0x96,0xa6,0x53,0x9a,0x24,0x36,0x58,0xf2,0x13,0x8a,0x98,0xfd,0x6f,0x61,0xeb,0x8f,0x1,0xf1,0x1f,0x55,0xc5,0x3d,0x4d,0xb,0x2f,0xfc,0xc4,0xc9,0xd1,0xe9,0xf5,0x43,0x20,0x74,0x64,0xce,0xdf,0x8c,0xc7,0x1d,0x4,0x14,0x3e,0xe8,0x83,0x68,0xde,0x10,0x5c,0xb0,0x84,0x97,0xfb,0x2e,0xf6,0x82,0x27,0x81,0x33,0xd2,0xa7,0xcf,0x65,0xb9,0xe0,0x4b,0x5f,0x67,0x59,0x42,0x3,0x6d,0xc3,0x54,0xe1,0xd8,0x88,0x8,0xa3,0x73,0x45,0xfe,0xd6,0x57,0x56,0x6b,0xe,0xea,0x7f,0x2d,0x69,0xbb,0xed,0x35,0xf,0x1c,0xac,0x34,0x7e,0x71,0xae,0x60,0x7b,0xf7,0x46,0xec,0x85,0xd4,0xa2,0x79,0x39,0x29,0x6c,0xb3,0x37,0xa5,0x22,0x72,0x51,0xf8,0x0,0xca,0x1a,0xe7,0x9d,0xb2,0x8e,0xbf,0x4f,0xee,0x23,0xfa,0x8d,0xf0,0x78,0xe4,0x95,0xb8,0xcc,0xaa,0x31,0x44,0x86,0xa8,0x5e,0x9e,0x11,0xc0,0x89,0x8b,0x28,0xc6,0x75,0x19,0xbe,0x30,0xe6,0xc8,0xf9,0x94,0xd3,0x2c,0x80,0x6,0xe3,0x7c,0xb4,0x76,0x5,0x50,0xa,0x8b,0xa6,0x5f,0xb4,0x90,0x76,0x2c,0xd8,0xf5,0xd3,0x5c,0x9b,0xd5,0x24,0xd9,0x4f,0x13,0xb1,0x68,0xf6,0xbf,0x7,0x3e,0xb3,0xa9,0x86,0x69,0xcf,0x5b,0x3f,0xde,0xc0,0xc9,0x73,0xbe,0xb9,0x39,0x23,0x14,0x36,0x81,0x1e,0xc4,0x7f,0x66,0xd2,0x2,0x7a,0x42,0xbd,0x5e,0x63,0x7c,0xe,0x2a,0x6,0xaf,0xf4,0x97,0x28,0xcd,0xcb,0x58,0xc5,0x3c,0x52,0x40,0xfe,0xfc,0xee,0x77,0x96,0x2d,0xd1,0xe3,0x72,0x37,0xc2,0xf2,0xff,0xa,0x19,0xa5,0xf8,0x45,0x4e,0xb8,0xfb,0x6d,0x25,0x56,0xfd,0x71,0x41,0x2e,0xf7,0xa3,0xe8,0xbb,0xaa,0x5a,0x70,0x60,0x79,0x91,0x8d,0xb5,0xad,0x0,0x10,0x44,0x27,0x29,0x59,0xa1,0x31,0xa0,0x98,0x4b,0x6f,0x8f,0x5,0xb,0x99,0x7b,0x95,0x65,0xeb,0x67,0x26,0x3d,0x3,0x85,0x30,0xa7,0x9,0x1,0xab,0xc3,0xb6,0x3b,0x2f,0x84,0xdd,0x92,0x4a,0x9f,0xf3,0x57,0xe5,0x43,0xe6,0xba,0xc,0xe7,0x8c,0xe0,0xd4,0x38,0x74,0x1f,0x4,0xca,0x15,0xe1,0x88,0x22,0x93,0x6b,0x51,0x89,0xdf,0x1a,0x50,0xc8,0x78,0x6a,0xf,0x32,0x33,0xd,0x49,0x1b,0x8e,0xc7,0x6c,0xec,0xbc,0xb2,0x9a,0x21,0x17,0x1c,0x94,0xe9,0x9e,0xa8,0xdc,0xf1,0x80,0xea,0xd6,0xf9,0x83,0x47,0x8a,0x2b,0xdb,0x35,0x16,0x46,0xc1,0x7e,0xae,0x64,0x9c,0x5d,0x1d,0xc6,0xb0,0x53,0xd7,0x8,0x4d,0xd0,0x18,0x87,0x62,0x6e,0x34,0x61,0x12,0x9d,0xac,0x82,0x54,0xe4,0x48,0xb7,0xf0,0x4c,0xef,0xed,0xa4,0xda,0x7d,0x11,0xa2,0xe2,0x20,0x55,0xce,0x75,0xfa,0x3a,0xcc,0x7e,0x78,0x70,0xed,0x41,0x1a,0x9d,0x22,0xbb,0xc9,0xb3,0x9f,0x8,0xf7,0xd6,0xeb,0x67,0xd3,0xcf,0xb7,0xab,0x34,0xca,0x71,0x96,0x8c,0x83,0xa1,0xc6,0x7c,0xc,0xb,0x8a,0xee,0x75,0x6b,0x33,0x1c,0x7a,0xdc,0xb2,0xa,0x6,0x8b,0x4,0xa6,0x43,0xdd,0x91,0x60,0xfa,0x6c,0x66,0x40,0x2e,0xe9,0xc3,0x25,0x6d,0x99,0x13,0x3e,0x1,0xea,0x20,0xce,0x5e,0xd0,0xb0,0x3a,0x2c,0xbe,0x2d,0x15,0xda,0xfe,0xec,0x9c,0x84,0x14,0xa5,0xb5,0x92,0xf1,0x38,0x24,0x18,0x0,0xc5,0xef,0xcc,0xd5,0x5d,0x16,0x1f,0xe,0xf4,0xc4,0x42,0x9b,0x90,0xd8,0x48,0xe3,0xfb,0xf0,0x4e,0xd,0xac,0xbf,0x4d,0x10,0x77,0x82,0x4a,0x47,0x64,0x98,0xc7,0x56,0x5b,0x49,0x23,0xc2,0xe7,0x89,0x4b,0xf5,0x2f,0x7,0xa2,0x94,0xd9,0x72,0x9,0x59,0xfc,0xb8,0x3b,0xae,0xba,0xdf,0x86,0x87,0xe5,0xaf,0xcd,0x7d,0xe4,0xde,0x6a,0x3c,0x3d,0x54,0x26,0x97,0xb1,0xaa,0xa0,0x7f,0x61,0x55,0xc1,0x8d,0xb9,0xf,0x39,0x52,0x50,0xe2,0x53,0xf6,0xff,0x27,0x46,0x2a,0x9a,0x8e,0x68,0x31,0x1e,0xb4,0x3,0x76,0x85,0x30,0xbc,0x12,0x93,0xd2,0xb6,0x88,0x4f,0xc0,0x79,0x8f,0x95,0x57,0x7b,0xe0,0xc8,0x6f,0x17,0xa4,0x5a,0xf9,0x11,0x58,0xfd,0x51,0x45,0x2,0x19,0x28,0xe1,0x37,0x81,0xdb,0xa7,0xd4,0xad,0x65,0xd7,0x32,0x62,0xe6,0xf8,0xbd,0xa8,0xe8,0x5,0x73,0x1b,0xcb,0x29,0xd1,0xa3,0x80,0x74,0xf3,0x3f,0xf2,0x6e,0x9e,0x63,0x5f,0x36,0x4c,0x69,0x1d,0x35,0x44,0x21,0xa9,0x2b,0x5c,0x46,0x81,0xe,0x28,0x92,0x4,0xf9,0x8,0x69,0x82,0x7b,0x56,0x5,0xf1,0xab,0x4d,0x12,0xb4,0x5b,0x74,0x1d,0x3,0xe2,0x86,0x2b,0xb5,0x6c,0xce,0x6e,0xe3,0xda,0x62,0xa2,0x19,0xc3,0x5c,0xa7,0xdf,0xf,0xbb,0x64,0x63,0xae,0x14,0xeb,0xc9,0xfe,0xe4,0xf5,0x4a,0x29,0x72,0x18,0x85,0x16,0x10,0xbe,0x83,0x60,0x9f,0xdb,0xf7,0xd3,0xa1,0xaf,0x3e,0xc,0xf0,0x22,0x2f,0x1f,0xea,0x23,0x9d,0x8f,0xe1,0x4b,0xaa,0x33,0x21,0x20,0x8b,0xf8,0xb0,0x2a,0xf3,0x9c,0xac,0x25,0x78,0xc4,0xd7,0x26,0x65,0x93,0x98,0x70,0x68,0x50,0x4c,0xfa,0x99,0xcd,0xdd,0x77,0x66,0x35,0x7e,0xa4,0xbd,0xad,0x87,0x44,0xd6,0xd8,0x52,0x36,0xb8,0x48,0xa6,0xec,0x7c,0x84,0xf4,0xb2,0x96,0x45,0x7d,0x6b,0x1e,0x76,0xdc,0x0,0x59,0xf2,0xe6,0xde,0xe0,0xfb,0xba,0xd4,0x7a,0xed,0x58,0x51,0x3a,0xd1,0x67,0xa9,0xe5,0x9,0x3d,0x2e,0x42,0x97,0x4f,0x3b,0x9e,0x38,0x8a,0x2,0x54,0x8c,0xb6,0xa5,0x15,0x8d,0xc7,0xc8,0x17,0xd9,0xc2,0x4e,0xff,0x55,0x3c,0x61,0x31,0xb1,0x1a,0xca,0xfc,0x47,0x6f,0xee,0xef,0xd2,0xb7,0x53,0xc6,0x94,0xd0,0x5e,0x24,0xb,0x37,0x6,0xf6,0x57,0x9a,0x43,0x34,0x49,0xc1,0x5d,0x2c,0x1,0x75,0x6d,0x1b,0xc0,0x80,0x90,0xd5,0xa,0x8e,0x1c,0x9b,0xcb,0xe8,0x41,0xb9,0x73,0xa3,0x89,0x5f,0x71,0x40,0x2d,0x6a,0x95,0x39,0xbf,0x5a,0xc5,0xd,0xcf,0xbc,0xe9,0xb3,0x13,0x88,0xfd,0x3f,0x11,0xe7,0x27,0xa8,0x79,0x30,0x32,0x91,0x7f,0xcc,0xa0,0x7,0x43,0xe5,0xa,0x25,0x4c,0x52,0xb3,0xd7,0x7a,0xe4,0x3d,0x9f,0x3f,0xb2,0x8b,0x33,0x17,0xd0,0x5f,0x79,0xc3,0x55,0xa8,0x59,0x38,0xd3,0x2a,0x7,0x54,0xa0,0xfa,0x1c,0xa4,0x1b,0x78,0x23,0x49,0xd4,0x47,0x41,0xef,0xd2,0x31,0xce,0x8a,0xa6,0x82,0xf0,0xf3,0x48,0x92,0xd,0xf6,0x8e,0x5e,0xea,0x35,0x32,0xff,0x45,0xba,0x98,0xaf,0xb5,0x71,0xda,0xa9,0xe1,0x7b,0xa2,0xcd,0xfd,0x74,0x29,0x95,0x86,0x77,0x34,0xc2,0xc9,0xfe,0x6f,0x5d,0xa1,0x73,0x7e,0x4e,0xbb,0x72,0xcc,0xde,0xb0,0x1a,0xfb,0x62,0x70,0x15,0x87,0x89,0x3,0x67,0xe9,0x19,0xf7,0xbd,0x2d,0xd5,0xa5,0xe3,0xc7,0x14,0x2c,0x21,0x39,0x1,0x1d,0xab,0xc8,0x9c,0x8c,0x26,0x37,0x64,0x2f,0xf5,0xec,0xfc,0xd6,0x0,0x6b,0x80,0x36,0xf8,0xb4,0x58,0x6c,0x7f,0x13,0xc6,0x1e,0x6a,0xcf,0x69,0xdb,0x3a,0x4f,0x27,0x8d,0x51,0x8,0xa3,0xb7,0x8f,0xb1,0xaa,0xeb,0x85,0x2b,0xbc,0x9,0x30,0x60,0xe0,0x4b,0x9b,0xad,0x16,0x3e,0xbf,0xbe,0x83,0xe6,0x2,0x97,0xc5,0x81,0x53,0x5,0xdd,0xe7,0xf4,0x44,0xdc,0x96,0x99,0x46,0x88,0x93,0x1f,0xae,0x4,0x6d,0x3c,0x4a,0x91,0xd1,0xc1,0x84,0x5b,0xdf,0x4d,0xca,0x9a,0xb9,0x10,0xe8,0x22,0xf2,0xf,0x75,0x5a,0x66,0x57,0xa7,0x6,0xcb,0x12,0x65,0x18,0x90,0xc,0x7d,0x50,0x24,0x42,0xd9,0xac,0x6e,0x40,0xb6,0x76,0xf9,0x28,0x61,0x63,0xc0,0x2e,0x9d,0xf1,0x56,0xd8,0xe,0x20,0x11,0x7c,0x3b,0xc4,0x68,0xee,0xb,0x94,0x5c,0x9e,0xed,0xb8,0xe2,0xc2,0xef,0x16,0xfd,0xd9,0x3f,0x65,0x91,0xbc,0x9a,0x15,0xd2,0x9c,0x6d,0x90,0x6,0x5a,0xf8,0x21,0xbf,0xf6,0x4e,0x77,0xfa,0xe0,0xcf,0x20,0x86,0x12,0x76,0x97,0x89,0x80,0x3a,0xf7,0xf0,0x70,0x6a,0x5d,0x7f,0xc8,0x57,0x8d,0x36,0x2f,0x9b,0x4b,0x33,0xb,0xf4,0x17,0x2a,0x35,0x47,0x63,0x4f,0xe6,0xbd,0xde,0x61,0x84,0x82,0x11,0x8c,0x75,0x1b,0x9,0xb7,0xb5,0xa7,0x3e,0xdf,0x64,0x98,0xaa,0x3b,0x7e,0x8b,0xbb,0xb6,0x43,0x50,0xec,0xb1,0xc,0x7,0xf1,0xb2,0x24,0x6c,0x1f,0xb4,0x38,0x8,0x67,0xbe,0xea,0xa1,0xf2,0xe3,0x13,0x39,0x29,0x30,0xd8,0xc4,0xfc,0xe4,0x49,0x59,0xd,0x6e,0x60,0x10,0xe8,0x78,0xe9,0xd1,0x2,0x26,0xc6,0x4c,0x42,0xd0,0x32,0xdc,0x2c,0xa2,0x2e,0x6f,0x74,0x4a,0xcc,0x79,0xee,0x40,0x48,0xe2,0x8a,0xff,0x72,0x66,0xcd,0x94,0xdb,0x3,0xd6,0xba,0x1e,0xac,0xa,0xaf,0xf3,0x45,0xae,0xc5,0xa9,0x9d,0x71,0x3d,0x56,0x4d,0x83,0x5c,0xa8,0xc1,0x6b,0xda,0x22,0x18,0xc0,0x96,0x53,0x19,0x81,0x31,0x23,0x46,0x7b,0x7a,0x44,0x0,0x52,0xc7,0x8e,0x25,0xa5,0xf5,0xfb,0xd3,0x68,0x5e,0x55,0xdd,0xa0,0xd7,0xe1,0x95,0xb8,0xc9,0xa3,0x9f,0xb0,0xca,0xe,0xc3,0x62,0x92,0x7c,0x5f,0xf,0x88,0x37,0xe7,0x2d,0xd5,0x14,0x54,0x8f,0xf9,0x1a,0x9e,0x41,0x4,0x99,0x51,0xce,0x2b,0x27,0x7d,0x28,0x5b,0xd4,0xe5,0xcb,0x1d,0xad,0x1,0xfe,0xb9,0x5,0xa6,0xa4,0xed,0x93,0x34,0x58,0xeb,0xab,0x69,0x1c,0x87,0x3c,0xb3,0x73,0x85,0xae,0xa8,0xa0,0x3d,0x91,0xca,0x4d,0xf2,0x6b,0x19,0x63,0x4f,0xd8,0x27,0x6,0x3b,0xb7,0x3,0x1f,0x67,0x7b,0xe4,0x1a,0xa1,0x46,0x5c,0x53,0x71,0x16,0xac,0xdc,0xdb,0x5a,0x3e,0xa5,0xbb,0xe3,0xcc,0xaa,0xc,0x62,0xda,0xd6,0x5b,0xd4,0x76,0x93,0xd,0x41,0xb0,0x2a,0xbc,0xb6,0x90,0xfe,0x39,0x13,0xf5,0xbd,0x49,0xc3,0xee,0xd1,0x3a,0xf0,0x1e,0x8e,0x0,0x60,0xea,0xfc,0x6e,0xfd,0xc5,0xa,0x2e,0x3c,0x4c,0x54,0xc4,0x75,0x65,0x42,0x21,0xe8,0xf4,0xc8,0xd0,0x15,0x3f,0x1c,0x5,0x8d,0xc6,0xcf,0xde,0x24,0x14,0x92,0x4b,0x40,0x8,0x98,0x33,0x2b,0x20,0x9e,0xdd,0x7c,0x6f,0x9d,0xc0,0xa7,0x52,0x9a,0x97,0xb4,0x48,0x17,0x86,0x8b,0x99,0xf3,0x12,0x37,0x59,0x9b,0x25,0xff,0xd7,0x72,0x44,0x9,0xa2,0xd9,0x89,0x2c,0x68,0xeb,0x7e,0x6a,0xf,0x56,0x57,0x35,0x7f,0x1d,0xad,0x34,0xe,0xba,0xec,0xed,0x84,0xf6,0x47,0x61,0x7a,0x70,0xaf,0xb1,0x85,0x11,0x5d,0x69,0xdf,0xe9,0x82,0x80,0x32,0x83,0x26,0x2f,0xf7,0x96,0xfa,0x4a,0x5e,0xb8,0xe1,0xce,0x64,0xd3,0xa6,0x55,0xe0,0x6c,0xc2,0x43,0x2,0x66,0x58,0x9f,0x10,0xa9,0x5f,0x45,0x87,0xab,0x30,0x18,0xbf,0xc7,0x74,0x8a,0x29,0xc1,0x88,0x2d,0x81,0x95,0xd2,0xc9,0xf8,0x31,0xe7,0x51,0xb,0x77,0x4,0x7d,0xb5,0x7,0xe2,0xb2,0x36,0x28,0x6d,0x78,0x38,0xd5,0xa3,0xcb,0x1b,0xf9,0x1,0x73,0x50,0xa4,0x23,0xef,0x22,0xbe,0x4e,0xb3,0x8f,0xe6,0x9c,0xb9,0xcd,0xe5,0x94,0xf1,0x79,0xfb,0x8c,0x61,0xa6,0x29,0xf,0xb5,0x23,0xde,0x2f,0x4e,0xa5,0x5c,0x71,0x22,0xd6,0x8c,0x6a,0x35,0x93,0x7c,0x53,0x3a,0x24,0xc5,0xa1,0xc,0x92,0x4b,0xe9,0x49,0xc4,0xfd,0x45,0x85,0x3e,0xe4,0x7b,0x80,0xf8,0x28,0x9c,0x43,0x44,0x89,0x33,0xcc,0xee,0xd9,0xc3,0xd2,0x6d,0xe,0x55,0x3f,0xa2,0x31,0x37,0x99,0xa4,0x47,0xb8,0xfc,0xd0,0xf4,0x86,0x88,0x19,0x2b,0xd7,0x5,0x8,0x38,0xcd,0x4,0xba,0xa8,0xc6,0x6c,0x8d,0x14,0x6,0x7,0xac,0xdf,0x97,0xd,0xd4,0xbb,0x8b,0x2,0x5f,0xe3,0xf0,0x1,0x42,0xb4,0xbf,0x57,0x4f,0x77,0x6b,0xdd,0xbe,0xea,0xfa,0x50,0x41,0x12,0x59,0x83,0x9a,0x8a,0xa0,0x63,0xf1,0xff,0x75,0x11,0x9f,0x6f,0x81,0xcb,0x5b,0xa3,0xd3,0x95,0xb1,0x62,0x5a,0x4c,0x39,0x51,0xfb,0x27,0x7e,0xd5,0xc1,0xf9,0xc7,0xdc,0x9d,0xf3,0x5d,0xca,0x7f,0x76,0x1d,0xf6,0x40,0x8e,0xc2,0x2e,0x1a,0x9,0x65,0xb0,0x68,0x1c,0xb9,0x1f,0xad,0x25,0x73,0xab,0x91,0x82,0x32,0xaa,0xe0,0xef,0x30,0xfe,0xe5,0x69,0xd8,0x72,0x1b,0x46,0x16,0x96,0x3d,0xed,0xdb,0x60,0x48,0xc9,0xc8,0xf5,0x90,0x74,0xe1,0xb3,0xf7,0x79,0x3,0x2c,0x10,0x21,0xd1,0x70,0xbd,0x64,0x13,0x6e,0xe6,0x7a,0xb,0x26,0x52,0x4a,0x3c,0xe7,0xa7,0xb7,0xf2,0x2d,0xa9,0x3b,0xbc,0xec,0xcf,0x66,0x9e,0x54,0x84,0xae,0x78,0x56,0x67,0xa,0x4d,0xb2,0x1e,0x98,0x7d,0xe2,0x2a,0xe8,0x9b,0xce,0x94,0x34,0xaf,0xda,0x18,0x36,0xc0,0x0,0x8f,0x5e,0x17,0x15,0xb6,0x58,0xeb,0x87,0x20,0x3e,0x98,0x77,0x58,0x31,0x2f,0xce,0xaa,0x7,0x99,0x40,0xe2,0x42,0xcf,0xf6,0x4e,0x6a,0xad,0x22,0x4,0xbe,0x28,0xd5,0x24,0x45,0xae,0x57,0x7a,0x29,0xdd,0x87,0x61,0xd9,0x66,0x5,0x5e,0x34,0xa9,0x3a,0x3c,0x92,0xaf,0x4c,0xb3,0xf7,0xdb,0xff,0x8d,0x8e,0x35,0xef,0x70,0x8b,0xf3,0x23,0x97,0x48,0x4f,0x82,0x38,0xc7,0xe5,0xd2,0xc8,0xc,0xa7,0xd4,0x9c,0x6,0xdf,0xb0,0x80,0x9,0x54,0xe8,0xfb,0xa,0x49,0xbf,0xb4,0x83,0x12,0x20,0xdc,0xe,0x3,0x33,0xc6,0xf,0xb1,0xa3,0xcd,0x67,0x86,0x1f,0xd,0x68,0xfa,0xf4,0x7e,0x1a,0x94,0x64,0x8a,0xc0,0x50,0xa8,0xd8,0x9e,0xba,0x69,0x51,0x5c,0x44,0x7c,0x60,0xd6,0xb5,0xe1,0xf1,0x5b,0x4a,0x19,0x52,0x88,0x91,0x81,0xab,0x7d,0x16,0xfd,0x4b,0x85,0xc9,0x25,0x11,0x2,0x6e,0xbb,0x63,0x17,0xb2,0x14,0xa6,0x47,0x32,0x5a,0xf0,0x2c,0x75,0xde,0xca,0xf2,0xcc,0xd7,0x96,0xf8,0x56,0xc1,0x74,0x4d,0x1d,0x9d,0x36,0xe6,0xd0,0x6b,0x43,0xc2,0xc3,0xfe,0x9b,0x7f,0xea,0xb8,0xfc,0x2e,0x78,0xa0,0x9a,0x89,0x39,0xa1,0xeb,0xe4,0x3b,0xf5,0xee,0x62,0xd3,0x79,0x10,0x41,0x37,0xec,0xac,0xbc,0xf9,0x26,0xa2,0x30,0xb7,0xe7,0xc4,0x6d,0x95,0x5f,0x8f,0x72,0x8,0x27,0x1b,0x2a,0xda,0x7b,0xb6,0x6f,0x18,0x65,0xed,0x71,0x0,0x2d,0x59,0x3f,0xa4,0xd1,0x13,0x3d,0xcb,0xb,0x84,0x55,0x1c,0x1e,0xbd,0x53,0xe0,0x8c,0x2b,0xa5,0x73,0x5d,0x6c,0x1,0x46,0xb9,0x15,0x93,0x76,0xe9,0x21,0xe3,0x90,0xc5,0x9f,0xc,0x21,0xd8,0x33,0x17,0xf1,0xab,0x5f,0x72,0x54,0xdb,0x1c,0x52,0xa3,0x5e,0xc8,0x94,0x36,0xef,0x71,0x38,0x80,0xb9,0x34,0x2e,0x1,0xee,0x48,0xdc,0xb8,0x59,0x47,0x4e,0xf4,0x39,0x3e,0xbe,0xa4,0x93,0xb1,0x6,0x99,0x43,0xf8,0xe1,0x55,0x85,0xfd,0xc5,0x3a,0xd9,0xe4,0xfb,0x89,0xad,0x81,0x28,0x73,0x10,0xaf,0x4a,0x4c,0xdf,0x42,0xbb,0xd5,0xc7,0x79,0x7b,0x69,0xf0,0x11,0xaa,0x56,0x64,0xf5,0xb0,0x45,0x75,0x78,0x8d,0x9e,0x22,0x7f,0xc2,0xc9,0x3f,0x7c,0xea,0xa2,0xd1,0x7a,0xf6,0xc6,0xa9,0x70,0x24,0x6f,0x3c,0x2d,0xdd,0xf7,0xe7,0xfe,0x16,0xa,0x32,0x2a,0x87,0x97,0xc3,0xa0,0xae,0xde,0x26,0xb6,0x27,0x1f,0xcc,0xe8,0x8,0x82,0x8c,0x1e,0xfc,0x12,0xe2,0x6c,0xe0,0xa1,0xba,0x84,0x2,0xb7,0x20,0x8e,0x86,0x2c,0x44,0x31,0xbc,0xa8,0x3,0x5a,0x15,0xcd,0x18,0x74,0xd0,0x62,0xc4,0x61,0x3d,0x8b,0x60,0xb,0x67,0x53,0xbf,0xf3,0x98,0x83,0x4d,0x92,0x66,0xf,0xa5,0x14,0xec,0xd6,0xe,0x58,0x9d,0xd7,0x4f,0xff,0xed,0x88,0xb5,0xb4,0x8a,0xce,0x9c,0x9,0x40,0xeb,0x6b,0x3b,0x35,0x1d,0xa6,0x90,0x9b,0x13,0x6e,0x19,0x2f,0x5b,0x76,0x7,0x6d,0x51,0x7e,0x4,0xc0,0xd,0xac,0x5c,0xb2,0x91,0xc1,0x46,0xf9,0x29,0xe3,0x1b,0xda,0x9a,0x41,0x37,0xd4,0x50,0x8f,0xca,0x57,0x9f,0x0,0xe5,0xe9,0xb3,0xe6,0x95,0x1a,0x2b,0x5,0xd3,0x63,0xcf,0x30,0x77,0xcb,0x68,0x6a,0x23,0x5d,0xfa,0x96,0x25,0x65,0xa7,0xd2,0x49,0xf2,0x7d,0xbd,0x4b,0x19,0x1f,0x17,0x8a,0x26,0x7d,0xfa,0x45,0xdc,0xae,0xd4,0xf8,0x6f,0x90,0xb1,0x8c,0x0,0xb4,0xa8,0xd0,0xcc,0x53,0xad,0x16,0xf1,0xeb,0xe4,0xc6,0xa1,0x1b,0x6b,0x6c,0xed,0x89,0x12,0xc,0x54,0x7b,0x1d,0xbb,0xd5,0x6d,0x61,0xec,0x63,0xc1,0x24,0xba,0xf6,0x7,0x9d,0xb,0x1,0x27,0x49,0x8e,0xa4,0x42,0xa,0xfe,0x74,0x59,0x66,0x8d,0x47,0xa9,0x39,0xb7,0xd7,0x5d,0x4b,0xd9,0x4a,0x72,0xbd,0x99,0x8b,0xfb,0xe3,0x73,0xc2,0xd2,0xf5,0x96,0x5f,0x43,0x7f,0x67,0xa2,0x88,0xab,0xb2,0x3a,0x71,0x78,0x69,0x93,0xa3,0x25,0xfc,0xf7,0xbf,0x2f,0x84,0x9c,0x97,0x29,0x6a,0xcb,0xd8,0x2a,0x77,0x10,0xe5,0x2d,0x20,0x3,0xff,0xa0,0x31,0x3c,0x2e,0x44,0xa5,0x80,0xee,0x2c,0x92,0x48,0x60,0xc5,0xf3,0xbe,0x15,0x6e,0x3e,0x9b,0xdf,0x5c,0xc9,0xdd,0xb8,0xe1,0xe0,0x82,0xc8,0xaa,0x1a,0x83,0xb9,0xd,0x5b,0x5a,0x33,0x41,0xf0,0xd6,0xcd,0xc7,0x18,0x6,0x32,0xa6,0xea,0xde,0x68,0x5e,0x35,0x37,0x85,0x34,0x91,0x98,0x40,0x21,0x4d,0xfd,0xe9,0xf,0x56,0x79,0xd3,0x64,0x11,0xe2,0x57,0xdb,0x75,0xf4,0xb5,0xd1,0xef,0x28,0xa7,0x1e,0xe8,0xf2,0x30,0x1c,0x87,0xaf,0x8,0x70,0xc3,0x3d,0x9e,0x76,0x3f,0x9a,0x36,0x22,0x65,0x7e,0x4f,0x86,0x50,0xe6,0xbc,0xc0,0xb3,0xca,0x2,0xb0,0x55,0x5,0x81,0x9f,0xda,0xcf,0x8f,0x62,0x14,0x7c,0xac,0x4e,0xb6,0xc4,0xe7,0x13,0x94,0x58,0x95,0x9,0xf9,0x4,0x38,0x51,0x2b,0xe,0x7a,0x52,0x23,0x46,0xce,0x4c,0x3b,0xcf,0x8,0x87,0xa1,0x1b,0x8d,0x70,0x81,0xe0,0xb,0xf2,0xdf,0x8c,0x78,0x22,0xc4,0x9b,0x3d,0xd2,0xfd,0x94,0x8a,0x6b,0xf,0xa2,0x3c,0xe5,0x47,0xe7,0x6a,0x53,0xeb,0x2b,0x90,0x4a,0xd5,0x2e,0x56,0x86,0x32,0xed,0xea,0x27,0x9d,0x62,0x40,0x77,0x6d,0x7c,0xc3,0xa0,0xfb,0x91,0xc,0x9f,0x99,0x37,0xa,0xe9,0x16,0x52,0x7e,0x5a,0x28,0x26,0xb7,0x85,0x79,0xab,0xa6,0x96,0x63,0xaa,0x14,0x6,0x68,0xc2,0x23,0xba,0xa8,0xa9,0x2,0x71,0x39,0xa3,0x7a,0x15,0x25,0xac,0xf1,0x4d,0x5e,0xaf,0xec,0x1a,0x11,0xf9,0xe1,0xd9,0xc5,0x73,0x10,0x44,0x54,0xfe,0xef,0xbc,0xf7,0x2d,0x34,0x24,0xe,0xcd,0x5f,0x51,0xdb,0xbf,0x31,0xc1,0x2f,0x65,0xf5,0xd,0x7d,0x3b,0x1f,0xcc,0xf4,0xe2,0x97,0xff,0x55,0x89,0xd0,0x7b,0x6f,0x57,0x69,0x72,0x33,0x5d,0xf3,0x64,0xd1,0xd8,0xb3,0x58,0xee,0x20,0x6c,0x80,0xb4,0xa7,0xcb,0x1e,0xc6,0xb2,0x17,0xb1,0x3,0x8b,0xdd,0x5,0x3f,0x2c,0x9c,0x4,0x4e,0x41,0x9e,0x50,0x4b,0xc7,0x76,0xdc,0xb5,0xe8,0xb8,0x38,0x93,0x43,0x75,0xce,0xe6,0x67,0x66,0x5b,0x3e,0xda,0x4f,0x1d,0x59,0xd7,0xad,0x82,0xbe,0x8f,0x7f,0xde,0x13,0xca,0xbd,0xc0,0x48,0xd4,0xa5,0x88,0xfc,0xe4,0x92,0x49,0x9,0x19,0x5c,0x83,0x7,0x95,0x12,0x42,0x61,0xc8,0x30,0xfa,0x2a,0x0,0xd6,0xf8,0xc9,0xa4,0xe3,0x1c,0xb0,0x36,0xd3,0x4c,0x84,0x46,0x35,0x60,0x3a,0x9a,0x1,0x74,0xb6,0x98,0x6e,0xae,0x21,0xf0,0xb9,0xbb,0x18,0xf6,0x45,0x29,0x8e,0xaf,0x9,0xe6,0xc9,0xa0,0xbe,0x5f,0x3b,0x96,0x8,0xd1,0x73,0xd3,0x5e,0x67,0xdf,0xfb,0x3c,0xb3,0x95,0x2f,0xb9,0x44,0xb5,0xd4,0x3f,0xc6,0xeb,0xb8,0x4c,0x16,0xf0,0x48,0xf7,0x94,0xcf,0xa5,0x38,0xab,0xad,0x3,0x3e,0xdd,0x22,0x66,0x4a,0x6e,0x1c,0x1f,0xa4,0x7e,0xe1,0x1a,0x62,0xb2,0x6,0xd9,0xde,0x13,0xa9,0x56,0x74,0x43,0x59,0x9d,0x36,0x45,0xd,0x97,0x4e,0x21,0x11,0x98,0xc5,0x79,0x6a,0x9b,0xd8,0x2e,0x25,0x12,0x83,0xb1,0x4d,0x9f,0x92,0xa2,0x57,0x9e,0x20,0x32,0x5c,0xf6,0x17,0x8e,0x9c,0xf9,0x6b,0x65,0xef,0x8b,0x5,0xf5,0x1b,0x51,0xc1,0x39,0x49,0xf,0x2b,0xf8,0xc0,0xcd,0xd5,0xed,0xf1,0x47,0x24,0x70,0x60,0xca,0xdb,0x88,0xc3,0x19,0x0,0x10,0x3a,0xec,0x87,0x6c,0xda,0x14,0x58,0xb4,0x80,0x93,0xff,0x2a,0xf2,0x86,0x23,0x85,0x37,0xd6,0xa3,0xcb,0x61,0xbd,0xe4,0x4f,0x5b,0x63,0x5d,0x46,0x7,0x69,0xc7,0x50,0xe5,0xdc,0x8c,0xc,0xa7,0x77,0x41,0xfa,0xd2,0x53,0x52,0x6f,0xa,0xee,0x7b,0x29,0x6d,0xbf,0xe9,0x31,0xb,0x18,0xa8,0x30,0x7a,0x75,0xaa,0x64,0x7f,0xf3,0x42,0xe8,0x81,0xd0,0xa6,0x7d,0x3d,0x2d,0x68,0xb7,0x33,0xa1,0x26,0x76,0x55,0xfc,0x4,0xce,0x1e,0xe3,0x99,0xb6,0x8a,0xbb,0x4b,0xea,0x27,0xfe,0x89,0xf4,0x7c,0xe0,0x91,0xbc,0xc8,0xae,0x35,0x40,0x82,0xac,0x5a,0x9a,0x15,0xc4,0x8d,0x8f,0x2c,0xc2,0x71,0x1d,0xba,0x34,0xe2,0xcc,0xfd,0x90,0xd7,0x28,0x84,0x2,0xe7,0x78,0xb0,0x72,0x1,0x54,0xe,0xe5,0xc8,0x31,0xda,0xfe,0x18,0x42,0xb6,0x9b,0xbd,0x32,0xf5,0xbb,0x4a,0xb7,0x21,0x7d,0xdf,0x6,0x98,0xd1,0x69,0x50,0xdd,0xc7,0xe8,0x7,0xa1,0x35,0x51,0xb0,0xae,0xa7,0x1d,0xd0,0xd7,0x57,0x4d,0x7a,0x58,0xef,0x70,0xaa,0x11,0x8,0xbc,0x6c,0x14,0x2c,0xd3,0x30,0xd,0x12,0x60,0x44,0x68,0xc1,0x9a,0xf9,0x46,0xa3,0xa5,0x36,0xab,0x52,0x3c,0x2e,0x90,0x92,0x80,0x19,0xf8,0x43,0xbf,0x8d,0x1c,0x59,0xac,0x9c,0x91,0x64,0x77,0xcb,0x96,0x2b,0x20,0xd6,0x95,0x3,0x4b,0x38,0x93,0x1f,0x2f,0x40,0x99,0xcd,0x86,0xd5,0xc4,0x34,0x1e,0xe,0x17,0xff,0xe3,0xdb,0xc3,0x6e,0x7e,0x2a,0x49,0x47,0x37,0xcf,0x5f,0xce,0xf6,0x25,0x1,0xe1,0x6b,0x65,0xf7,0x15,0xfb,0xb,0x85,0x9,0x48,0x53,0x6d,0xeb,0x5e,0xc9,0x67,0x6f,0xc5,0xad,0xd8,0x55,0x41,0xea,0xb3,0xfc,0x24,0xf1,0x9d,0x39,0x8b,0x2d,0x88,0xd4,0x62,0x89,0xe2,0x8e,0xba,0x56,0x1a,0x71,0x6a,0xa4,0x7b,0x8f,0xe6,0x4c,0xfd,0x5,0x3f,0xe7,0xb1,0x74,0x3e,0xa6,0x16,0x4,0x61,0x5c,0x5d,0x63,0x27,0x75,0xe0,0xa9,0x2,0x82,0xd2,0xdc,0xf4,0x4f,0x79,0x72,0xfa,0x87,0xf0,0xc6,0xb2,0x9f,0xee,0x84,0xb8,0x97,0xed,0x29,0xe4,0x45,0xb5,0x5b,0x78,0x28,0xaf,0x10,0xc0,0xa,0xf2,0x33,0x73,0xa8,0xde,0x3d,0xb9,0x66,0x23,0xbe,0x76,0xe9,0xc,0x0,0x5a,0xf,0x7c,0xf3,0xc2,0xec,0x3a,0x8a,0x26,0xd9,0x9e,0x22,0x81,0x83,0xca,0xb4,0x13,0x7f,0xcc,0x8c,0x4e,0x3b,0xa0,0x1b,0x94,0x54,0xa2,0x8d,0x8b,0x83,0x1e,0xb2,0xe9,0x6e,0xd1,0x48,0x3a,0x40,0x6c,0xfb,0x4,0x25,0x18,0x94,0x20,0x3c,0x44,0x58,0xc7,0x39,0x82,0x65,0x7f,0x70,0x52,0x35,0x8f,0xff,0xf8,0x79,0x1d,0x86,0x98,0xc0,0xef,0x89,0x2f,0x41,0xf9,0xf5,0x78,0xf7,0x55,0xb0,0x2e,0x62,0x93,0x9,0x9f,0x95,0xb3,0xdd,0x1a,0x30,0xd6,0x9e,0x6a,0xe0,0xcd,0xf2,0x19,0xd3,0x3d,0xad,0x23,0x43,0xc9,0xdf,0x4d,0xde,0xe6,0x29,0xd,0x1f,0x6f,0x77,0xe7,0x56,0x46,0x61,0x2,0xcb,0xd7,0xeb,0xf3,0x36,0x1c,0x3f,0x26,0xae,0xe5,0xec,0xfd,0x7,0x37,0xb1,0x68,0x63,0x2b,0xbb,0x10,0x8,0x3,0xbd,0xfe,0x5f,0x4c,0xbe,0xe3,0x84,0x71,0xb9,0xb4,0x97,0x6b,0x34,0xa5,0xa8,0xba,0xd0,0x31,0x14,0x7a,0xb8,0x6,0xdc,0xf4,0x51,0x67,0x2a,0x81,0xfa,0xaa,0xf,0x4b,0xc8,0x5d,0x49,0x2c,0x75,0x74,0x16,0x5c,0x3e,0x8e,0x17,0x2d,0x99,0xcf,0xce,0xa7,0xd5,0x64,0x42,0x59,0x53,0x8c,0x92,0xa6,0x32,0x7e,0x4a,0xfc,0xca,0xa1,0xa3,0x11,0xa0,0x5,0xc,0xd4,0xb5,0xd9,0x69,0x7d,0x9b,0xc2,0xed,0x47,0xf0,0x85,0x76,0xc3,0x4f,0xe1,0x60,0x21,0x45,0x7b,0xbc,0x33,0x8a,0x7c,0x66,0xa4,0x88,0x13,0x3b,0x9c,0xe4,0x57,0xa9,0xa,0xe2,0xab,0xe,0xa2,0xb6,0xf1,0xea,0xdb,0x12,0xc4,0x72,0x28,0x54,0x27,0x5e,0x96,0x24,0xc1,0x91,0x15,0xb,0x4e,0x5b,0x1b,0xf6,0x80,0xe8,0x38,0xda,0x22,0x50,0x73,0x87,0x0,0xcc,0x1,0x9d,0x6d,0x90,0xac,0xc5,0xbf,0x9a,0xee,0xc6,0xb7,0xd2,0x5a,0xd8,0xaf,0x84,0x43,0xcc,0xea,0x50,0xc6,0x3b,0xca,0xab,0x40,0xb9,0x94,0xc7,0x33,0x69,0x8f,0xd0,0x76,0x99,0xb6,0xdf,0xc1,0x20,0x44,0xe9,0x77,0xae,0xc,0xac,0x21,0x18,0xa0,0x60,0xdb,0x1,0x9e,0x65,0x1d,0xcd,0x79,0xa6,0xa1,0x6c,0xd6,0x29,0xb,0x3c,0x26,0x37,0x88,0xeb,0xb0,0xda,0x47,0xd4,0xd2,0x7c,0x41,0xa2,0x5d,0x19,0x35,0x11,0x63,0x6d,0xfc,0xce,0x32,0xe0,0xed,0xdd,0x28,0xe1,0x5f,0x4d,0x23,0x89,0x68,0xf1,0xe3,0xe2,0x49,0x3a,0x72,0xe8,0x31,0x5e,0x6e,0xe7,0xba,0x6,0x15,0xe4,0xa7,0x51,0x5a,0xb2,0xaa,0x92,0x8e,0x38,0x5b,0xf,0x1f,0xb5,0xa4,0xf7,0xbc,0x66,0x7f,0x6f,0x45,0x86,0x14,0x1a,0x90,0xf4,0x7a,0x8a,0x64,0x2e,0xbe,0x46,0x36,0x70,0x54,0x87,0xbf,0xa9,0xdc,0xb4,0x1e,0xc2,0x9b,0x30,0x24,0x1c,0x22,0x39,0x78,0x16,0xb8,0x2f,0x9a,0x93,0xf8,0x13,0xa5,0x6b,0x27,0xcb,0xff,0xec,0x80,0x55,0x8d,0xf9,0x5c,0xfa,0x48,0xc0,0x96,0x4e,0x74,0x67,0xd7,0x4f,0x5,0xa,0xd5,0x1b,0x0,0x8c,0x3d,0x97,0xfe,0xa3,0xf3,0x73,0xd8,0x8,0x3e,0x85,0xad,0x2c,0x2d,0x10,0x75,0x91,0x4,0x56,0x12,0x9c,0xe6,0xc9,0xf5,0xc4,0x34,0x95,0x58,0x81,0xf6,0x8b,0x3,0x9f,0xee,0xc3,0xb7,0xaf,0xd9,0x2,0x42,0x52,0x17,0xc8,0x4c,0xde,0x59,0x9,0x2a,0x83,0x7b,0xb1,0x61,0x4b,0x9d,0xb3,0x82,0xef,0xa8,0x57,0xfb,0x7d,0x98,0x7,0xcf,0xd,0x7e,0x2b,0x71,0xd1,0x4a,0x3f,0xfd,0xd3,0x25,0xe5,0x6a,0xbb,0xf2,0xf0,0x53,0xbd,0xe,0x62,0xc5,0xda,0x7c,0x93,0xbc,0xd5,0xcb,0x2a,0x4e,0xe3,0x7d,0xa4,0x6,0xa6,0x2b,0x12,0xaa,0x8e,0x49,0xc6,0xe0,0x5a,0xcc,0x31,0xc0,0xa1,0x4a,0xb3,0x9e,0xcd,0x39,0x63,0x85,0x3d,0x82,0xe1,0xba,0xd0,0x4d,0xde,0xd8,0x76,0x4b,0xa8,0x57,0x13,0x3f,0x1b,0x69,0x6a,0xd1,0xb,0x94,0x6f,0x17,0xc7,0x73,0xac,0xab,0x66,0xdc,0x23,0x1,0x36,0x2c,0xe8,0x43,0x30,0x78,0xe2,0x3b,0x54,0x64,0xed,0xb0,0xc,0x1f,0xee,0xad,0x5b,0x50,0x67,0xf6,0xc4,0x38,0xea,0xe7,0xd7,0x22,0xeb,0x55,0x47,0x29,0x83,0x62,0xfb,0xe9,0x8c,0x1e,0x10,0x9a,0xfe,0x70,0x80,0x6e,0x24,0xb4,0x4c,0x3c,0x7a,0x5e,0x8d,0xb5,0xb8,0xa0,0x98,0x84,0x32,0x51,0x5,0x15,0xbf,0xae,0xfd,0xb6,0x6c,0x75,0x65,0x4f,0x99,0xf2,0x19,0xaf,0x61,0x2d,0xc1,0xf5,0xe6,0x8a,0x5f,0x87,0xf3,0x56,0xf0,0x42,0xa3,0xd6,0xbe,0x14,0xc8,0x91,0x3a,0x2e,0x16,0x28,0x33,0x72,0x1c,0xb2,0x25,0x90,0xa9,0xf9,0x79,0xd2,0x2,0x34,0x8f,0xa7,0x26,0x27,0x1a,0x7f,0x9b,0xe,0x5c,0x18,0xca,0x9c,0x44,0x7e,0x6d,0xdd,0x45,0xf,0x0,0xdf,0x11,0xa,0x86,0x37,0x9d,0xf4,0xa5,0xd3,0x8,0x48,0x58,0x1d,0xc2,0x46,0xd4,0x53,0x3,0x20,0x89,0x71,0xbb,0x6b,0x96,0xec,0xc3,0xff,0xce,0x3e,0x9f,0x52,0x8b,0xfc,0x81,0x9,0x95,0xe4,0xc9,0xbd,0xdb,0x40,0x35,0xf7,0xd9,0x2f,0xef,0x60,0xb1,0xf8,0xfa,0x59,0xb7,0x4,0x68,0xcf,0x41,0x97,0xb9,0x88,0xe5,0xa2,0x5d,0xf1,0x77,0x92,0xd,0xc5,0x7,0x74,0x21,0x7b,0x3f,0x12,0xeb,0x0,0x24,0xc2,0x98,0x6c,0x41,0x67,0xe8,0x2f,0x61,0x90,0x6d,0xfb,0xa7,0x5,0xdc,0x42,0xb,0xb3,0x8a,0x7,0x1d,0x32,0xdd,0x7b,0xef,0x8b,0x6a,0x74,0x7d,0xc7,0xa,0xd,0x8d,0x97,0xa0,0x82,0x35,0xaa,0x70,0xcb,0xd2,0x66,0xb6,0xce,0xf6,0x9,0xea,0xd7,0xc8,0xba,0x9e,0xb2,0x1b,0x40,0x23,0x9c,0x79,0x7f,0xec,0x71,0x88,0xe6,0xf4,0x4a,0x48,0x5a,0xc3,0x22,0x99,0x65,0x57,0xc6,0x83,0x76,0x46,0x4b,0xbe,0xad,0x11,0x4c,0xf1,0xfa,0xc,0x4f,0xd9,0x91,0xe2,0x49,0xc5,0xf5,0x9a,0x43,0x17,0x5c,0xf,0x1e,0xee,0xc4,0xd4,0xcd,0x25,0x39,0x1,0x19,0xb4,0xa4,0xf0,0x93,0x9d,0xed,0x15,0x85,0x14,0x2c,0xff,0xdb,0x3b,0xb1,0xbf,0x2d,0xcf,0x21,0xd1,0x5f,0xd3,0x92,0x89,0xb7,0x31,0x84,0x13,0xbd,0xb5,0x1f,0x77,0x2,0x8f,0x9b,0x30,0x69,0x26,0xfe,0x2b,0x47,0xe3,0x51,0xf7,0x52,0xe,0xb8,0x53,0x38,0x54,0x60,0x8c,0xc0,0xab,0xb0,0x7e,0xa1,0x55,0x3c,0x96,0x27,0xdf,0xe5,0x3d,0x6b,0xae,0xe4,0x7c,0xcc,0xde,0xbb,0x86,0x87,0xb9,0xfd,0xaf,0x3a,0x73,0xd8,0x58,0x8,0x6,0x2e,0x95,0xa3,0xa8,0x20,0x5d,0x2a,0x1c,0x68,0x45,0x34,0x5e,0x62,0x4d,0x37,0xf3,0x3e,0x9f,0x6f,0x81,0xa2,0xf2,0x75,0xca,0x1a,0xd0,0x28,0xe9,0xa9,0x72,0x4,0xe7,0x63,0xbc,0xf9,0x64,0xac,0x33,0xd6,0xda,0x80,0xd5,0xa6,0x29,0x18,0x36,0xe0,0x50,0xfc,0x3,0x44,0xf8,0x5b,0x59,0x10,0x6e,0xc9,0xa5,0x16,0x56,0x94,0xe1,0x7a,0xc1,0x4e,0x8e,0x78,0xe6,0xe0,0xe8,0x75,0xd9,0x82,0x5,0xba,0x23,0x51,0x2b,0x7,0x90,0x6f,0x4e,0x73,0xff,0x4b,0x57,0x2f,0x33,0xac,0x52,0xe9,0xe,0x14,0x1b,0x39,0x5e,0xe4,0x94,0x93,0x12,0x76,0xed,0xf3,0xab,0x84,0xe2,0x44,0x2a,0x92,0x9e,0x13,0x9c,0x3e,0xdb,0x45,0x9,0xf8,0x62,0xf4,0xfe,0xd8,0xb6,0x71,0x5b,0xbd,0xf5,0x1,0x8b,0xa6,0x99,0x72,0xb8,0x56,0xc6,0x48,0x28,0xa2,0xb4,0x26,0xb5,0x8d,0x42,0x66,0x74,0x4,0x1c,0x8c,0x3d,0x2d,0xa,0x69,0xa0,0xbc,0x80,0x98,0x5d,0x77,0x54,0x4d,0xc5,0x8e,0x87,0x96,0x6c,0x5c,0xda,0x3,0x8,0x40,0xd0,0x7b,0x63,0x68,0xd6,0x95,0x34,0x27,0xd5,0x88,0xef,0x1a,0xd2,0xdf,0xfc,0x0,0x5f,0xce,0xc3,0xd1,0xbb,0x5a,0x7f,0x11,0xd3,0x6d,0xb7,0x9f,0x3a,0xc,0x41,0xea,0x91,0xc1,0x64,0x20,0xa3,0x36,0x22,0x47,0x1e,0x1f,0x7d,0x37,0x55,0xe5,0x7c,0x46,0xf2,0xa4,0xa5,0xcc,0xbe,0xf,0x29,0x32,0x38,0xe7,0xf9,0xcd,0x59,0x15,0x21,0x97,0xa1,0xca,0xc8,0x7a,0xcb,0x6e,0x67,0xbf,0xde,0xb2,0x2,0x16,0xf0,0xa9,0x86,0x2c,0x9b,0xee,0x1d,0xa8,0x24,0x8a,0xb,0x4a,0x2e,0x10,0xd7,0x58,0xe1,0x17,0xd,0xcf,0xe3,0x78,0x50,0xf7,0x8f,0x3c,0xc2,0x61,0x89,0xc0,0x65,0xc9,0xdd,0x9a,0x81,0xb0,0x79,0xaf,0x19,0x43,0x3f,0x4c,0x35,0xfd,0x4f,0xaa,0xfa,0x7e,0x60,0x25,0x30,0x70,0x9d,0xeb,0x83,0x53,0xb1,0x49,0x3b,0x18,0xec,0x6b,0xa7,0x6a,0xf6,0x6,0xfb,0xc7,0xae,0xd4,0xf1,0x85,0xad,0xdc,0xb9,0x31,0xb3,0xc4,0x41,0x86,0x9,0x2f,0x95,0x3,0xfe,0xf,0x6e,0x85,0x7c,0x51,0x2,0xf6,0xac,0x4a,0x15,0xb3,0x5c,0x73,0x1a,0x4,0xe5,0x81,0x2c,0xb2,0x6b,0xc9,0x69,0xe4,0xdd,0x65,0xa5,0x1e,0xc4,0x5b,0xa0,0xd8,0x8,0xbc,0x63,0x64,0xa9,0x13,0xec,0xce,0xf9,0xe3,0xf2,0x4d,0x2e,0x75,0x1f,0x82,0x11,0x17,0xb9,0x84,0x67,0x98,0xdc,0xf0,0xd4,0xa6,0xa8,0x39,0xb,0xf7,0x25,0x28,0x18,0xed,0x24,0x9a,0x88,0xe6,0x4c,0xad,0x34,0x26,0x27,0x8c,0xff,0xb7,0x2d,0xf4,0x9b,0xab,0x22,0x7f,0xc3,0xd0,0x21,0x62,0x94,0x9f,0x77,0x6f,0x57,0x4b,0xfd,0x9e,0xca,0xda,0x70,0x61,0x32,0x79,0xa3,0xba,0xaa,0x80,0x43,0xd1,0xdf,0x55,0x31,0xbf,0x4f,0xa1,0xeb,0x7b,0x83,0xf3,0xb5,0x91,0x42,0x7a,0x6c,0x19,0x71,0xdb,0x7,0x5e,0xf5,0xe1,0xd9,0xe7,0xfc,0xbd,0xd3,0x7d,0xea,0x5f,0x56,0x3d,0xd6,0x60,0xae,0xe2,0xe,0x3a,0x29,0x45,0x90,0x48,0x3c,0x99,0x3f,0x8d,0x5,0x53,0x8b,0xb1,0xa2,0x12,0x8a,0xc0,0xcf,0x10,0xde,0xc5,0x49,0xf8,0x52,0x3b,0x66,0x36,0xb6,0x1d,0xcd,0xfb,0x40,0x68,0xe9,0xe8,0xd5,0xb0,0x54,0xc1,0x93,0xd7,0x59,0x23,0xc,0x30,0x1,0xf1,0x50,0x9d,0x44,0x33,0x4e,0xc6,0x5a,0x2b,0x6,0x72,0x6a,0x1c,0xc7,0x87,0x97,0xd2,0xd,0x89,0x1b,0x9c,0xcc,0xef,0x46,0xbe,0x74,0xa4,0x8e,0x58,0x76,0x47,0x2a,0x6d,0x92,0x3e,0xb8,0x5d,0xc2,0xa,0xc8,0xbb,0xee,0xb4,0x14,0x8f,0xfa,0x38,0x16,0xe0,0x20,0xaf,0x7e,0x37,0x35,0x96,0x78,0xcb,0xa7,0x0,0x6d,0xcb,0x24,0xb,0x62,0x7c,0x9d,0xf9,0x54,0xca,0x13,0xb1,0x11,0x9c,0xa5,0x1d,0x39,0xfe,0x71,0x57,0xed,0x7b,0x86,0x77,0x16,0xfd,0x4,0x29,0x7a,0x8e,0xd4,0x32,0x8a,0x35,0x56,0xd,0x67,0xfa,0x69,0x6f,0xc1,0xfc,0x1f,0xe0,0xa4,0x88,0xac,0xde,0xdd,0x66,0xbc,0x23,0xd8,0xa0,0x70,0xc4,0x1b,0x1c,0xd1,0x6b,0x94,0xb6,0x81,0x9b,0x5f,0xf4,0x87,0xcf,0x55,0x8c,0xe3,0xd3,0x5a,0x7,0xbb,0xa8,0x59,0x1a,0xec,0xe7,0xd0,0x41,0x73,0x8f,0x5d,0x50,0x60,0x95,0x5c,0xe2,0xf0,0x9e,0x34,0xd5,0x4c,0x5e,0x3b,0xa9,0xa7,0x2d,0x49,0xc7,0x37,0xd9,0x93,0x3,0xfb,0x8b,0xcd,0xe9,0x3a,0x2,0xf,0x17,0x2f,0x33,0x85,0xe6,0xb2,0xa2,0x8,0x19,0x4a,0x1,0xdb,0xc2,0xd2,0xf8,0x2e,0x45,0xae,0x18,0xd6,0x9a,0x76,0x42,0x51,0x3d,0xe8,0x30,0x44,0xe1,0x47,0xf5,0x14,0x61,0x9,0xa3,0x7f,0x26,0x8d,0x99,0xa1,0x9f,0x84,0xc5,0xab,0x5,0x92,0x27,0x1e,0x4e,0xce,0x65,0xb5,0x83,0x38,0x10,0x91,0x90,0xad,0xc8,0x2c,0xb9,0xeb,0xaf,0x7d,0x2b,0xf3,0xc9,0xda,0x6a,0xf2,0xb8,0xb7,0x68,0xa6,0xbd,0x31,0x80,0x2a,0x43,0x12,0x64,0xbf,0xff,0xef,0xaa,0x75,0xf1,0x63,0xe4,0xb4,0x97,0x3e,0xc6,0xc,0xdc,0x21,0x5b,0x74,0x48,0x79,0x89,0x28,0xe5,0x3c,0x4b,0x36,0xbe,0x22,0x53,0x7e,0xa,0x6c,0xf7,0x82,0x40,0x6e,0x98,0x58,0xd7,0x6,0x4f,0x4d,0xee,0x0,0xb3,0xdf,0x78,0xf6,0x20,0xe,0x3f,0x52,0x15,0xea,0x46,0xc0,0x25,0xba,0x72,0xb0,0xc3,0x96,0xcc,0xb8,0x95,0x6c,0x87,0xa3,0x45,0x1f,0xeb,0xc6,0xe0,0x6f,0xa8,0xe6,0x17,0xea,0x7c,0x20,0x82,0x5b,0xc5,0x8c,0x34,0xd,0x80,0x9a,0xb5,0x5a,0xfc,0x68,0xc,0xed,0xf3,0xfa,0x40,0x8d,0x8a,0xa,0x10,0x27,0x5,0xb2,0x2d,0xf7,0x4c,0x55,0xe1,0x31,0x49,0x71,0x8e,0x6d,0x50,0x4f,0x3d,0x19,0x35,0x9c,0xc7,0xa4,0x1b,0xfe,0xf8,0x6b,0xf6,0xf,0x61,0x73,0xcd,0xcf,0xdd,0x44,0xa5,0x1e,0xe2,0xd0,0x41,0x4,0xf1,0xc1,0xcc,0x39,0x2a,0x96,0xcb,0x76,0x7d,0x8b,0xc8,0x5e,0x16,0x65,0xce,0x42,0x72,0x1d,0xc4,0x90,0xdb,0x88,0x99,0x69,0x43,0x53,0x4a,0xa2,0xbe,0x86,0x9e,0x33,0x23,0x77,0x14,0x1a,0x6a,0x92,0x2,0x93,0xab,0x78,0x5c,0xbc,0x36,0x38,0xaa,0x48,0xa6,0x56,0xd8,0x54,0x15,0xe,0x30,0xb6,0x3,0x94,0x3a,0x32,0x98,0xf0,0x85,0x8,0x1c,0xb7,0xee,0xa1,0x79,0xac,0xc0,0x64,0xd6,0x70,0xd5,0x89,0x3f,0xd4,0xbf,0xd3,0xe7,0xb,0x47,0x2c,0x37,0xf9,0x26,0xd2,0xbb,0x11,0xa0,0x58,0x62,0xba,0xec,0x29,0x63,0xfb,0x4b,0x59,0x3c,0x1,0x0,0x3e,0x7a,0x28,0xbd,0xf4,0x5f,0xdf,0x8f,0x81,0xa9,0x12,0x24,0x2f,0xa7,0xda,0xad,0x9b,0xef,0xc2,0xb3,0xd9,0xe5,0xca,0xb0,0x74,0xb9,0x18,0xe8,0x6,0x25,0x75,0xf2,0x4d,0x9d,0x57,0xaf,0x6e,0x2e,0xf5,0x83,0x60,0xe4,0x3b,0x7e,0xe3,0x2b,0xb4,0x51,0x5d,0x7,0x52,0x21,0xae,0x9f,0xb1,0x67,0xd7,0x7b,0x84,0xc3,0x7f,0xdc,0xde,0x97,0xe9,0x4e,0x22,0x91,0xd1,0x13,0x66,0xfd,0x46,0xc9,0x9,0xff,0x76,0x70,0x78,0xe5,0x49,0x12,0x95,0x2a,0xb3,0xc1,0xbb,0x97,0x0,0xff,0xde,0xe3,0x6f,0xdb,0xc7,0xbf,0xa3,0x3c,0xc2,0x79,0x9e,0x84,0x8b,0xa9,0xce,0x74,0x4,0x3,0x82,0xe6,0x7d,0x63,0x3b,0x14,0x72,0xd4,0xba,0x2,0xe,0x83,0xc,0xae,0x4b,0xd5,0x99,0x68,0xf2,0x64,0x6e,0x48,0x26,0xe1,0xcb,0x2d,0x65,0x91,0x1b,0x36,0x9,0xe2,0x28,0xc6,0x56,0xd8,0xb8,0x32,0x24,0xb6,0x25,0x1d,0xd2,0xf6,0xe4,0x94,0x8c,0x1c,0xad,0xbd,0x9a,0xf9,0x30,0x2c,0x10,0x8,0xcd,0xe7,0xc4,0xdd,0x55,0x1e,0x17,0x6,0xfc,0xcc,0x4a,0x93,0x98,0xd0,0x40,0xeb,0xf3,0xf8,0x46,0x5,0xa4,0xb7,0x45,0x18,0x7f,0x8a,0x42,0x4f,0x6c,0x90,0xcf,0x5e,0x53,0x41,0x2b,0xca,0xef,0x81,0x43,0xfd,0x27,0xf,0xaa,0x9c,0xd1,0x7a,0x1,0x51,0xf4,0xb0,0x33,0xa6,0xb2,0xd7,0x8e,0x8f,0xed,0xa7,0xc5,0x75,0xec,0xd6,0x62,0x34,0x35,0x5c,0x2e,0x9f,0xb9,0xa2,0xa8,0x77,0x69,0x5d,0xc9,0x85,0xb1,0x7,0x31,0x5a,0x58,0xea,0x5b,0xfe,0xf7,0x2f,0x4e,0x22,0x92,0x86,0x60,0x39,0x16,0xbc,0xb,0x7e,0x8d,0x38,0xb4,0x1a,0x9b,0xda,0xbe,0x80,0x47,0xc8,0x71,0x87,0x9d,0x5f,0x73,0xe8,0xc0,0x67,0x1f,0xac,0x52,0xf1,0x19,0x50,0xf5,0x59,0x4d,0xa,0x11,0x20,0xe9,0x3f,0x89,0xd3,0xaf,0xdc,0xa5,0x6d,0xdf,0x3a,0x6a,0xee,0xf0,0xb5,0xa0,0xe0,0xd,0x7b,0x13,0xc3,0x21,0xd9,0xab,0x88,0x7c,0xfb,0x37,0xfa,0x66,0x96,0x6b,0x57,0x3e,0x44,0x61,0x15,0x3d,0x4c,0x29,0xa1,0x23,0x54,0xc8,0xf,0x80,0xa6,0x1c,0x8a,0x77,0x86,0xe7,0xc,0xf5,0xd8,0x8b,0x7f,0x25,0xc3,0x9c,0x3a,0xd5,0xfa,0x93,0x8d,0x6c,0x8,0xa5,0x3b,0xe2,0x40,0xe0,0x6d,0x54,0xec,0x2c,0x97,0x4d,0xd2,0x29,0x51,0x81,0x35,0xea,0xed,0x20,0x9a,0x65,0x47,0x70,0x6a,0x7b,0xc4,0xa7,0xfc,0x96,0xb,0x98,0x9e,0x30,0xd,0xee,0x11,0x55,0x79,0x5d,0x2f,0x21,0xb0,0x82,0x7e,0xac,0xa1,0x91,0x64,0xad,0x13,0x1,0x6f,0xc5,0x24,0xbd,0xaf,0xae,0x5,0x76,0x3e,0xa4,0x7d,0x12,0x22,0xab,0xf6,0x4a,0x59,0xa8,0xeb,0x1d,0x16,0xfe,0xe6,0xde,0xc2,0x74,0x17,0x43,0x53,0xf9,0xe8,0xbb,0xf0,0x2a,0x33,0x23,0x9,0xca,0x58,0x56,0xdc,0xb8,0x36,0xc6,0x28,0x62,0xf2,0xa,0x7a,0x3c,0x18,0xcb,0xf3,0xe5,0x90,0xf8,0x52,0x8e,0xd7,0x7c,0x68,0x50,0x6e,0x75,0x34,0x5a,0xf4,0x63,0xd6,0xdf,0xb4,0x5f,0xe9,0x27,0x6b,0x87,0xb3,0xa0,0xcc,0x19,0xc1,0xb5,0x10,0xb6,0x4,0x8c,0xda,0x2,0x38,0x2b,0x9b,0x3,0x49,0x46,0x99,0x57,0x4c,0xc0,0x71,0xdb,0xb2,0xef,0xbf,0x3f,0x94,0x44,0x72,0xc9,0xe1,0x60,0x61,0x5c,0x39,0xdd,0x48,0x1a,0x5e,0xd0,0xaa,0x85,0xb9,0x88,0x78,0xd9,0x14,0xcd,0xba,0xc7,0x4f,0xd3,0xa2,0x8f,0xfb,0xe3,0x95,0x4e,0xe,0x1e,0x5b,0x84,0x0,0x92,0x15,0x45,0x66,0xcf,0x37,0xfd,0x2d,0x7,0xd1,0xff,0xce,0xa3,0xe4,0x1b,0xb7,0x31,0xd4,0x4b,0x83,0x41,0x32,0x67,0x3d,0x9d,0x6,0x73,0xb1,0x9f,0x69,0xa9,0x26,0xf7,0xbe,0xbc,0x1f,0xf1,0x42,0x2e,0x89,0xa3,0x5,0xea,0xc5,0xac,0xb2,0x53,0x37,0x9a,0x4,0xdd,0x7f,0xdf,0x52,0x6b,0xd3,0xf7,0x30,0xbf,0x99,0x23,0xb5,0x48,0xb9,0xd8,0x33,0xca,0xe7,0xb4,0x40,0x1a,0xfc,0x44,0xfb,0x98,0xc3,0xa9,0x34,0xa7,0xa1,0xf,0x32,0xd1,0x2e,0x6a,0x46,0x62,0x10,0x13,0xa8,0x72,0xed,0x16,0x6e,0xbe,0xa,0xd5,0xd2,0x1f,0xa5,0x5a,0x78,0x4f,0x55,0x91,0x3a,0x49,0x1,0x9b,0x42,0x2d,0x1d,0x94,0xc9,0x75,0x66,0x97,0xd4,0x22,0x29,0x1e,0x8f,0xbd,0x41,0x93,0x9e,0xae,0x5b,0x92,0x2c,0x3e,0x50,0xfa,0x1b,0x82,0x90,0xf5,0x67,0x69,0xe3,0x87,0x9,0xf9,0x17,0x5d,0xcd,0x35,0x45,0x3,0x27,0xf4,0xcc,0xc1,0xd9,0xe1,0xfd,0x4b,0x28,0x7c,0x6c,0xc6,0xd7,0x84,0xcf,0x15,0xc,0x1c,0x36,0xe0,0x8b,0x60,0xd6,0x18,0x54,0xb8,0x8c,0x9f,0xf3,0x26,0xfe,0x8a,0x2f,0x89,0x3b,0xda,0xaf,0xc7,0x6d,0xb1,0xe8,0x43,0x57,0x6f,0x51,0x4a,0xb,0x65,0xcb,0x5c,0xe9,0xd0,0x80,0x0,0xab,0x7b,0x4d,0xf6,0xde,0x5f,0x5e,0x63,0x6,0xe2,0x77,0x25,0x61,0xb3,0xe5,0x3d,0x7,0x14,0xa4,0x3c,0x76,0x79,0xa6,0x68,0x73,0xff,0x4e,0xe4,0x8d,0xdc,0xaa,0x71,0x31,0x21,0x64,0xbb,0x3f,0xad,0x2a,0x7a,0x59,0xf0,0x8,0xc2,0x12,0xef,0x95,0xba,0x86,0xb7,0x47,0xe6,0x2b,0xf2,0x85,0xf8,0x70,0xec,0x9d,0xb0,0xc4,0xa2,0x39,0x4c,0x8e,0xa0,0x56,0x96,0x19,0xc8,0x81,0x83,0x20,0xce,0x7d,0x11,0xb6,0x38,0xee,0xc0,0xf1,0x9c,0xdb,0x24,0x88,0xe,0xeb,0x74,0xbc,0x7e,0xd,0x58,0x2,0x9f,0xb2,0x4b,0xa0,0x84,0x62,0x38,0xcc,0xe1,0xc7,0x48,0x8f,0xc1,0x30,0xcd,0x5b,0x7,0xa5,0x7c,0xe2,0xab,0x13,0x2a,0xa7,0xbd,0x92,0x7d,0xdb,0x4f,0x2b,0xca,0xd4,0xdd,0x67,0xaa,0xad,0x2d,0x37,0x0,0x22,0x95,0xa,0xd0,0x6b,0x72,0xc6,0x16,0x6e,0x56,0xa9,0x4a,0x77,0x68,0x1a,0x3e,0x12,0xbb,0xe0,0x83,0x3c,0xd9,0xdf,0x4c,0xd1,0x28,0x46,0x54,0xea,0xe8,0xfa,0x63,0x82,0x39,0xc5,0xf7,0x66,0x23,0xd6,0xe6,0xeb,0x1e,0xd,0xb1,0xec,0x51,0x5a,0xac,0xef,0x79,0x31,0x42,0xe9,0x65,0x55,0x3a,0xe3,0xb7,0xfc,0xaf,0xbe,0x4e,0x64,0x74,0x6d,0x85,0x99,0xa1,0xb9,0x14,0x4,0x50,0x33,0x3d,0x4d,0xb5,0x25,0xb4,0x8c,0x5f,0x7b,0x9b,0x11,0x1f,0x8d,0x6f,0x81,0x71,0xff,0x73,0x32,0x29,0x17,0x91,0x24,0xb3,0x1d,0x15,0xbf,0xd7,0xa2,0x2f,0x3b,0x90,0xc9,0x86,0x5e,0x8b,0xe7,0x43,0xf1,0x57,0xf2,0xae,0x18,0xf3,0x98,0xf4,0xc0,0x2c,0x60,0xb,0x10,0xde,0x1,0xf5,0x9c,0x36,0x87,0x7f,0x45,0x9d,0xcb,0xe,0x44,0xdc,0x6c,0x7e,0x1b,0x26,0x27,0x19,0x5d,0xf,0x9a,0xd3,0x78,0xf8,0xa8,0xa6,0x8e,0x35,0x3,0x8,0x80,0xfd,0x8a,0xbc,0xc8,0xe5,0x94,0xfe,0xc2,0xed,0x97,0x53,0x9e,0x3f,0xcf,0x21,0x2,0x52,0xd5,0x6a,0xba,0x70,0x88,0x49,0x9,0xd2,0xa4,0x47,0xc3,0x1c,0x59,0xc4,0xc,0x93,0x76,0x7a,0x20,0x75,0x6,0x89,0xb8,0x96,0x40,0xf0,0x5c,0xa3,0xe4,0x58,0xfb,0xf9,0xb0,0xce,0x69,0x5,0xb6,0xf6,0x34,0x41,0xda,0x61,0xee,0x2e,0xd8,0xa2,0xa4,0xac,0x31,0x9d,0xc6,0x41,0xfe,0x67,0x15,0x6f,0x43,0xd4,0x2b,0xa,0x37,0xbb,0xf,0x13,0x6b,0x77,0xe8,0x16,0xad,0x4a,0x50,0x5f,0x7d,0x1a,0xa0,0xd0,0xd7,0x56,0x32,0xa9,0xb7,0xef,0xc0,0xa6,0x0,0x6e,0xd6,0xda,0x57,0xd8,0x7a,0x9f,0x1,0x4d,0xbc,0x26,0xb0,0xba,0x9c,0xf2,0x35,0x1f,0xf9,0xb1,0x45,0xcf,0xe2,0xdd,0x36,0xfc,0x12,0x82,0xc,0x6c,0xe6,0xf0,0x62,0xf1,0xc9,0x6,0x22,0x30,0x40,0x58,0xc8,0x79,0x69,0x4e,0x2d,0xe4,0xf8,0xc4,0xdc,0x19,0x33,0x10,0x9,0x81,0xca,0xc3,0xd2,0x28,0x18,0x9e,0x47,0x4c,0x4,0x94,0x3f,0x27,0x2c,0x92,0xd1,0x70,0x63,0x91,0xcc,0xab,0x5e,0x96,0x9b,0xb8,0x44,0x1b,0x8a,0x87,0x95,0xff,0x1e,0x3b,0x55,0x97,0x29,0xf3,0xdb,0x7e,0x48,0x5,0xae,0xd5,0x85,0x20,0x64,0xe7,0x72,0x66,0x3,0x5a,0x5b,0x39,0x73,0x11,0xa1,0x38,0x2,0xb6,0xe0,0xe1,0x88,0xfa,0x4b,0x6d,0x76,0x7c,0xa3,0xbd,0x89,0x1d,0x51,0x65,0xd3,0xe5,0x8e,0x8c,0x3e,0x8f,0x2a,0x23,0xfb,0x9a,0xf6,0x46,0x52,0xb4,0xed,0xc2,0x68,0xdf,0xaa,0x59,0xec,0x60,0xce,0x4f,0xe,0x6a,0x54,0x93,0x1c,0xa5,0x53,0x49,0x8b,0xa7,0x3c,0x14,0xb3,0xcb,0x78,0x86,0x25,0xcd,0x84,0x21,0x8d,0x99,0xde,0xc5,0xf4,0x3d,0xeb,0x5d,0x7,0x7b,0x8,0x71,0xb9,0xb,0xee,0xbe,0x3a,0x24,0x61,0x74,0x34,0xd9,0xaf,0xc7,0x17,0xf5,0xd,0x7f,0x5c,0xa8,0x2f,0xe3,0x2e,0xb2,0x42,0xbf,0x83,0xea,0x90,0xb5,0xc1,0xe9,0x98,0xfd,0x75,0xf7,0x80,0x2d,0xea,0x65,0x43,0xf9,0x6f,0x92,0x63,0x2,0xe9,0x10,0x3d,0x6e,0x9a,0xc0,0x26,0x79,0xdf,0x30,0x1f,0x76,0x68,0x89,0xed,0x40,0xde,0x7,0xa5,0x5,0x88,0xb1,0x9,0xc9,0x72,0xa8,0x37,0xcc,0xb4,0x64,0xd0,0xf,0x8,0xc5,0x7f,0x80,0xa2,0x95,0x8f,0x9e,0x21,0x42,0x19,0x73,0xee,0x7d,0x7b,0xd5,0xe8,0xb,0xf4,0xb0,0x9c,0xb8,0xca,0xc4,0x55,0x67,0x9b,0x49,0x44,0x74,0x81,0x48,0xf6,0xe4,0x8a,0x20,0xc1,0x58,0x4a,0x4b,0xe0,0x93,0xdb,0x41,0x98,0xf7,0xc7,0x4e,0x13,0xaf,0xbc,0x4d,0xe,0xf8,0xf3,0x1b,0x3,0x3b,0x27,0x91,0xf2,0xa6,0xb6,0x1c,0xd,0x5e,0x15,0xcf,0xd6,0xc6,0xec,0x2f,0xbd,0xb3,0x39,0x5d,0xd3,0x23,0xcd,0x87,0x17,0xef,0x9f,0xd9,0xfd,0x2e,0x16,0x0,0x75,0x1d,0xb7,0x6b,0x32,0x99,0x8d,0xb5,0x8b,0x90,0xd1,0xbf,0x11,0x86,0x33,0x3a,0x51,0xba,0xc,0xc2,0x8e,0x62,0x56,0x45,0x29,0xfc,0x24,0x50,0xf5,0x53,0xe1,0x69,0x3f,0xe7,0xdd,0xce,0x7e,0xe6,0xac,0xa3,0x7c,0xb2,0xa9,0x25,0x94,0x3e,0x57,0xa,0x5a,0xda,0x71,0xa1,0x97,0x2c,0x4,0x85,0x84,0xb9,0xdc,0x38,0xad,0xff,0xbb,0x35,0x4f,0x60,0x5c,0x6d,0x9d,0x3c,0xf1,0x28,0x5f,0x22,0xaa,0x36,0x47,0x6a,0x1e,0x6,0x70,0xab,0xeb,0xfb,0xbe,0x61,0xe5,0x77,0xf0,0xa0,0x83,0x2a,0xd2,0x18,0xc8,0xe2,0x34,0x1a,0x2b,0x46,0x1,0xfe,0x52,0xd4,0x31,0xae,0x66,0xa4,0xd7,0x82,0xd8,0x78,0xe3,0x96,0x54,0x7a,0x8c,0x4c,0xc3,0x12,0x5b,0x59,0xfa,0x14,0xa7,0xcb,0x6c,0x47,0xe1,0xe,0x21,0x48,0x56,0xb7,0xd3,0x7e,0xe0,0x39,0x9b,0x3b,0xb6,0x8f,0x37,0x13,0xd4,0x5b,0x7d,0xc7,0x51,0xac,0x5d,0x3c,0xd7,0x2e,0x3,0x50,0xa4,0xfe,0x18,0xa0,0x1f,0x7c,0x27,0x4d,0xd0,0x43,0x45,0xeb,0xd6,0x35,0xca,0x8e,0xa2,0x86,0xf4,0xf7,0x4c,0x96,0x9,0xf2,0x8a,0x5a,0xee,0x31,0x36,0xfb,0x41,0xbe,0x9c,0xab,0xb1,0x75,0xde,0xad,0xe5,0x7f,0xa6,0xc9,0xf9,0x70,0x2d,0x91,0x82,0x73,0x30,0xc6,0xcd,0xfa,0x6b,0x59,0xa5,0x77,0x7a,0x4a,0xbf,0x76,0xc8,0xda,0xb4,0x1e,0xff,0x66,0x74,0x11,0x83,0x8d,0x7,0x63,0xed,0x1d,0xf3,0xb9,0x29,0xd1,0xa1,0xe7,0xc3,0x10,0x28,0x25,0x3d,0x5,0x19,0xaf,0xcc,0x98,0x88,0x22,0x33,0x60,0x2b,0xf1,0xe8,0xf8,0xd2,0x4,0x6f,0x84,0x32,0xfc,0xb0,0x5c,0x68,0x7b,0x17,0xc2,0x1a,0x6e,0xcb,0x6d,0xdf,0x3e,0x4b,0x23,0x89,0x55,0xc,0xa7,0xb3,0x8b,0xb5,0xae,0xef,0x81,0x2f,0xb8,0xd,0x34,0x64,0xe4,0x4f,0x9f,0xa9,0x12,0x3a,0xbb,0xba,0x87,0xe2,0x6,0x93,0xc1,0x85,0x57,0x1,0xd9,0xe3,0xf0,0x40,0xd8,0x92,0x9d,0x42,0x8c,0x97,0x1b,0xaa,0x0,0x69,0x38,0x4e,0x95,0xd5,0xc5,0x80,0x5f,0xdb,0x49,0xce,0x9e,0xbd,0x14,0xec,0x26,0xf6,0xb,0x71,0x5e,0x62,0x53,0xa3,0x2,0xcf,0x16,0x61,0x1c,0x94,0x8,0x79,0x54,0x20,0x46,0xdd,0xa8,0x6a,0x44,0xb2,0x72,0xfd,0x2c,0x65,0x67,0xc4,0x2a,0x99,0xf5,0x52,0xdc,0xa,0x24,0x15,0x78,0x3f,0xc0,0x6c,0xea,0xf,0x90,0x58,0x9a,0xe9,0xbc,0xe6,0x9e,0xb3,0x4a,0xa1,0x85,0x63,0x39,0xcd,0xe0,0xc6,0x49,0x8e,0xc0,0x31,0xcc,0x5a,0x6,0xa4,0x7d,0xe3,0xaa,0x12,0x2b,0xa6,0xbc,0x93,0x7c,0xda,0x4e,0x2a,0xcb,0xd5,0xdc,0x66,0xab,0xac,0x2c,0x36,0x1,0x23,0x94,0xb,0xd1,0x6a,0x73,0xc7,0x17,0x6f,0x57,0xa8,0x4b,0x76,0x69,0x1b,0x3f,0x13,0xba,0xe1,0x82,0x3d,0xd8,0xde,0x4d,0xd0,0x29,0x47,0x55,0xeb,0xe9,0xfb,0x62,0x83,0x38,0xc4,0xf6,0x67,0x22,0xd7,0xe7,0xea,0x1f,0xc,0xb0,0xed,0x50,0x5b,0xad,0xee,0x78,0x30,0x43,0xe8,0x64,0x54,0x3b,0xe2,0xb6,0xfd,0xae,0xbf,0x4f,0x65,0x75,0x6c,0x84,0x98,0xa0,0xb8,0x15,0x5,0x51,0x32,0x3c,0x4c,0xb4,0x24,0xb5,0x8d,0x5e,0x7a,0x9a,0x10,0x1e,0x8c,0x6e,0x80,0x70,0xfe,0x72,0x33,0x28,0x16,0x90,0x25,0xb2,0x1c,0x14,0xbe,0xd6,0xa3,0x2e,0x3a,0x91,0xc8,0x87,0x5f,0x8a,0xe6,0x42,0xf0,0x56,0xf3,0xaf,0x19,0xf2,0x99,0xf5,0xc1,0x2d,0x61,0xa,0x11,0xdf,0x0,0xf4,0x9d,0x37,0x86,0x7e,0x44,0x9c,0xca,0xf,0x45,0xdd,0x6d,0x7f,0x1a,0x27,0x26,0x18,0x5c,0xe,0x9b,0xd2,0x79,0xf9,0xa9,0xa7,0x8f,0x34,0x2,0x9,0x81,0xfc,0x8b,0xbd,0xc9,0xe4,0x95,0xff,0xc3,0xec,0x96,0x52,0x9f,0x3e,0xce,0x20,0x3,0x53,0xd4,0x6b,0xbb,0x71,0x89,0x48,0x8,0xd3,0xa5,0x46,0xc2,0x1d,0x58,0xc5,0xd,0x92,0x77,0x7b,0x21,0x74,0x7,0x88,0xb9,0x97,0x41,0xf1,0x5d,0xa2,0xe5,0x59,0xfa,0xf8,0xb1,0xcf,0x68,0x4,0xb7,0xf7,0x35,0x40,0xdb,0x60,0xef,0x2f,0xd9,0x5d,0x5b,0x53,0xce,0x62,0x39,0xbe,0x1,0x98,0xea,0x90,0xbc,0x2b,0xd4,0xf5,0xc8,0x44,0xf0,0xec,0x94,0x88,0x17,0xe9,0x52,0xb5,0xaf,0xa0,0x82,0xe5,0x5f,0x2f,0x28,0xa9,0xcd,0x56,0x48,0x10,0x3f,0x59,0xff,0x91,0x29,0x25,0xa8,0x27,0x85,0x60,0xfe,0xb2,0x43,0xd9,0x4f,0x45,0x63,0xd,0xca,0xe0,0x6,0x4e,0xba,0x30,0x1d,0x22,0xc9,0x3,0xed,0x7d,0xf3,0x93,0x19,0xf,0x9d,0xe,0x36,0xf9,0xdd,0xcf,0xbf,0xa7,0x37,0x86,0x96,0xb1,0xd2,0x1b,0x7,0x3b,0x23,0xe6,0xcc,0xef,0xf6,0x7e,0x35,0x3c,0x2d,0xd7,0xe7,0x61,0xb8,0xb3,0xfb,0x6b,0xc0,0xd8,0xd3,0x6d,0x2e,0x8f,0x9c,0x6e,0x33,0x54,0xa1,0x69,0x64,0x47,0xbb,0xe4,0x75,0x78,0x6a,0x0,0xe1,0xc4,0xaa,0x68,0xd6,0xc,0x24,0x81,0xb7,0xfa,0x51,0x2a,0x7a,0xdf,0x9b,0x18,0x8d,0x99,0xfc,0xa5,0xa4,0xc6,0x8c,0xee,0x5e,0xc7,0xfd,0x49,0x1f,0x1e,0x77,0x5,0xb4,0x92,0x89,0x83,0x5c,0x42,0x76,0xe2,0xae,0x9a,0x2c,0x1a,0x71,0x73,0xc1,0x70,0xd5,0xdc,0x4,0x65,0x9,0xb9,0xad,0x4b,0x12,0x3d,0x97,0x20,0x55,0xa6,0x13,0x9f,0x31,0xb0,0xf1,0x95,0xab,0x6c,0xe3,0x5a,0xac,0xb6,0x74,0x58,0xc3,0xeb,0x4c,0x34,0x87,0x79,0xda,0x32,0x7b,0xde,0x72,0x66,0x21,0x3a,0xb,0xc2,0x14,0xa2,0xf8,0x84,0xf7,0x8e,0x46,0xf4,0x11,0x41,0xc5,0xdb,0x9e,0x8b,0xcb,0x26,0x50,0x38,0xe8,0xa,0xf2,0x80,0xa3,0x57,0xd0,0x1c,0xd1,0x4d,0xbd,0x40,0x7c,0x15,0x6f,0x4a,0x3e,0x16,0x67,0x2,0x8a,0x8,0x7f,0xd9,0x1e,0x91,0xb7,0xd,0x9b,0x66,0x97,0xf6,0x1d,0xe4,0xc9,0x9a,0x6e,0x34,0xd2,0x8d,0x2b,0xc4,0xeb,0x82,0x9c,0x7d,0x19,0xb4,0x2a,0xf3,0x51,0xf1,0x7c,0x45,0xfd,0x3d,0x86,0x5c,0xc3,0x38,0x40,0x90,0x24,0xfb,0xfc,0x31,0x8b,0x74,0x56,0x61,0x7b,0x6a,0xd5,0xb6,0xed,0x87,0x1a,0x89,0x8f,0x21,0x1c,0xff,0x0,0x44,0x68,0x4c,0x3e,0x30,0xa1,0x93,0x6f,0xbd,0xb0,0x80,0x75,0xbc,0x2,0x10,0x7e,0xd4,0x35,0xac,0xbe,0xbf,0x14,0x67,0x2f,0xb5,0x6c,0x3,0x33,0xba,0xe7,0x5b,0x48,0xb9,0xfa,0xc,0x7,0xef,0xf7,0xcf,0xd3,0x65,0x6,0x52,0x42,0xe8,0xf9,0xaa,0xe1,0x3b,0x22,0x32,0x18,0xdb,0x49,0x47,0xcd,0xa9,0x27,0xd7,0x39,0x73,0xe3,0x1b,0x6b,0x2d,0x9,0xda,0xe2,0xf4,0x81,0xe9,0x43,0x9f,0xc6,0x6d,0x79,0x41,0x7f,0x64,0x25,0x4b,0xe5,0x72,0xc7,0xce,0xa5,0x4e,0xf8,0x36,0x7a,0x96,0xa2,0xb1,0xdd,0x8,0xd0,0xa4,0x1,0xa7,0x15,0x9d,0xcb,0x13,0x29,0x3a,0x8a,0x12,0x58,0x57,0x88,0x46,0x5d,0xd1,0x60,0xca,0xa3,0xfe,0xae,0x2e,0x85,0x55,0x63,0xd8,0xf0,0x71,0x70,0x4d,0x28,0xcc,0x59,0xb,0x4f,0xc1,0xbb,0x94,0xa8,0x99,0x69,0xc8,0x5,0xdc,0xab,0xd6,0x5e,0xc2,0xb3,0x9e,0xea,0xf2,0x84,0x5f,0x1f,0xf,0x4a,0x95,0x11,0x83,0x4,0x54,0x77,0xde,0x26,0xec,0x3c,0x16,0xc0,0xee,0xdf,0xb2,0xf5,0xa,0xa6,0x20,0xc5,0x5a,0x92,0x50,0x23,0x76,0x2c,0x8c,0x17,0x62,0xa0,0x8e,0x78,0xb8,0x37,0xe6,0xaf,0xad,0xe,0xe0,0x53,0x3f,0x98,0x85,0x23,0xcc,0xe3,0x8a,0x94,0x75,0x11,0xbc,0x22,0xfb,0x59,0xf9,0x74,0x4d,0xf5,0xd1,0x16,0x99,0xbf,0x5,0x93,0x6e,0x9f,0xfe,0x15,0xec,0xc1,0x92,0x66,0x3c,0xda,0x62,0xdd,0xbe,0xe5,0x8f,0x12,0x81,0x87,0x29,0x14,0xf7,0x8,0x4c,0x60,0x44,0x36,0x35,0x8e,0x54,0xcb,0x30,0x48,0x98,0x2c,0xf3,0xf4,0x39,0x83,0x7c,0x5e,0x69,0x73,0xb7,0x1c,0x6f,0x27,0xbd,0x64,0xb,0x3b,0xb2,0xef,0x53,0x40,0xb1,0xf2,0x4,0xf,0x38,0xa9,0x9b,0x67,0xb5,0xb8,0x88,0x7d,0xb4,0xa,0x18,0x76,0xdc,0x3d,0xa4,0xb6,0xd3,0x41,0x4f,0xc5,0xa1,0x2f,0xdf,0x31,0x7b,0xeb,0x13,0x63,0x25,0x1,0xd2,0xea,0xe7,0xff,0xc7,0xdb,0x6d,0xe,0x5a,0x4a,0xe0,0xf1,0xa2,0xe9,0x33,0x2a,0x3a,0x10,0xc6,0xad,0x46,0xf0,0x3e,0x72,0x9e,0xaa,0xb9,0xd5,0x0,0xd8,0xac,0x9,0xaf,0x1d,0xfc,0x89,0xe1,0x4b,0x97,0xce,0x65,0x71,0x49,0x77,0x6c,0x2d,0x43,0xed,0x7a,0xcf,0xf6,0xa6,0x26,0x8d,0x5d,0x6b,0xd0,0xf8,0x79,0x78,0x45,0x20,0xc4,0x51,0x3,0x47,0x95,0xc3,0x1b,0x21,0x32,0x82,0x1a,0x50,0x5f,0x80,0x4e,0x55,0xd9,0x68,0xc2,0xab,0xfa,0x8c,0x57,0x17,0x7,0x42,0x9d,0x19,0x8b,0xc,0x5c,0x7f,0xd6,0x2e,0xe4,0x34,0xc9,0xb3,0x9c,0xa0,0x91,0x61,0xc0,0xd,0xd4,0xa3,0xde,0x56,0xca,0xbb,0x96,0xe2,0x84,0x1f,0x6a,0xa8,0x86,0x70,0xb0,0x3f,0xee,0xa7,0xa5,0x6,0xe8,0x5b,0x37,0x90,0x1e,0xc8,0xe6,0xd7,0xba,0xfd,0x2,0xae,0x28,0xcd,0x52,0x9a,0x58,0x2b,0x7e,0x24,0xf1,0xdc,0x25,0xce,0xea,0xc,0x56,0xa2,0x8f,0xa9,0x26,0xe1,0xaf,0x5e,0xa3,0x35,0x69,0xcb,0x12,0x8c,0xc5,0x7d,0x44,0xc9,0xd3,0xfc,0x13,0xb5,0x21,0x45,0xa4,0xba,0xb3,0x9,0xc4,0xc3,0x43,0x59,0x6e,0x4c,0xfb,0x64,0xbe,0x5,0x1c,0xa8,0x78,0x0,0x38,0xc7,0x24,0x19,0x6,0x74,0x50,0x7c,0xd5,0x8e,0xed,0x52,0xb7,0xb1,0x22,0xbf,0x46,0x28,0x3a,0x84,0x86,0x94,0xd,0xec,0x57,0xab,0x99,0x8,0x4d,0xb8,0x88,0x85,0x70,0x63,0xdf,0x82,0x3f,0x34,0xc2,0x81,0x17,0x5f,0x2c,0x87,0xb,0x3b,0x54,0x8d,0xd9,0x92,0xc1,0xd0,0x20,0xa,0x1a,0x3,0xeb,0xf7,0xcf,0xd7,0x7a,0x6a,0x3e,0x5d,0x53,0x23,0xdb,0x4b,0xda,0xe2,0x31,0x15,0xf5,0x7f,0x71,0xe3,0x1,0xef,0x1f,0x91,0x1d,0x5c,0x47,0x79,0xff,0x4a,0xdd,0x73,0x7b,0xd1,0xb9,0xcc,0x41,0x55,0xfe,0xa7,0xe8,0x30,0xe5,0x89,0x2d,0x9f,0x39,0x9c,0xc0,0x76,0x9d,0xf6,0x9a,0xae,0x42,0xe,0x65,0x7e,0xb0,0x6f,0x9b,0xf2,0x58,0xe9,0x11,0x2b,0xf3,0xa5,0x60,0x2a,0xb2,0x2,0x10,0x75,0x48,0x49,0x77,0x33,0x61,0xf4,0xbd,0x16,0x96,0xc6,0xc8,0xe0,0x5b,0x6d,0x66,0xee,0x93,0xe4,0xd2,0xa6,0x8b,0xfa,0x90,0xac,0x83,0xf9,0x3d,0xf0,0x51,0xa1,0x4f,0x6c,0x3c,0xbb,0x4,0xd4,0x1e,0xe6,0x27,0x67,0xbc,0xca,0x29,0xad,0x72,0x37,0xaa,0x62,0xfd,0x18,0x14,0x4e,0x1b,0x68,0xe7,0xd6,0xf8,0x2e,0x9e,0x32,0xcd,0x8a,0x36,0x95,0x97,0xde,0xa0,0x7,0x6b,0xd8,0x98,0x5a,0x2f,0xb4,0xf,0x80,0x40,0xb6,0xa6,0xa0,0xa8,0x35,0x99,0xc2,0x45,0xfa,0x63,0x11,0x6b,0x47,0xd0,0x2f,0xe,0x33,0xbf,0xb,0x17,0x6f,0x73,0xec,0x12,0xa9,0x4e,0x54,0x5b,0x79,0x1e,0xa4,0xd4,0xd3,0x52,0x36,0xad,0xb3,0xeb,0xc4,0xa2,0x4,0x6a,0xd2,0xde,0x53,0xdc,0x7e,0x9b,0x5,0x49,0xb8,0x22,0xb4,0xbe,0x98,0xf6,0x31,0x1b,0xfd,0xb5,0x41,0xcb,0xe6,0xd9,0x32,0xf8,0x16,0x86,0x8,0x68,0xe2,0xf4,0x66,0xf5,0xcd,0x2,0x26,0x34,0x44,0x5c,0xcc,0x7d,0x6d,0x4a,0x29,0xe0,0xfc,0xc0,0xd8,0x1d,0x37,0x14,0xd,0x85,0xce,0xc7,0xd6,0x2c,0x1c,0x9a,0x43,0x48,0x0,0x90,0x3b,0x23,0x28,0x96,0xd5,0x74,0x67,0x95,0xc8,0xaf,0x5a,0x92,0x9f,0xbc,0x40,0x1f,0x8e,0x83,0x91,0xfb,0x1a,0x3f,0x51,0x93,0x2d,0xf7,0xdf,0x7a,0x4c,0x1,0xaa,0xd1,0x81,0x24,0x60,0xe3,0x76,0x62,0x7,0x5e,0x5f,0x3d,0x77,0x15,0xa5,0x3c,0x6,0xb2,0xe4,0xe5,0x8c,0xfe,0x4f,0x69,0x72,0x78,0xa7,0xb9,0x8d,0x19,0x55,0x61,0xd7,0xe1,0x8a,0x88,0x3a,0x8b,0x2e,0x27,0xff,0x9e,0xf2,0x42,0x56,0xb0,0xe9,0xc6,0x6c,0xdb,0xae,0x5d,0xe8,0x64,0xca,0x4b,0xa,0x6e,0x50,0x97,0x18,0xa1,0x57,0x4d,0x8f,0xa3,0x38,0x10,0xb7,0xcf,0x7c,0x82,0x21,0xc9,0x80,0x25,0x89,0x9d,0xda,0xc1,0xf0,0x39,0xef,0x59,0x3,0x7f,0xc,0x75,0xbd,0xf,0xea,0xba,0x3e,0x20,0x65,0x70,0x30,0xdd,0xab,0xc3,0x13,0xf1,0x9,0x7b,0x58,0xac,0x2b,0xe7,0x2a,0xb6,0x46,0xbb,0x87,0xee,0x94,0xb1,0xc5,0xed,0x9c,0xf9,0x71,0xf3,0x84,0xef,0x28,0xa7,0x81,0x3b,0xad,0x50,0xa1,0xc0,0x2b,0xd2,0xff,0xac,0x58,0x2,0xe4,0xbb,0x1d,0xf2,0xdd,0xb4,0xaa,0x4b,0x2f,0x82,0x1c,0xc5,0x67,0xc7,0x4a,0x73,0xcb,0xb,0xb0,0x6a,0xf5,0xe,0x76,0xa6,0x12,0xcd,0xca,0x7,0xbd,0x42,0x60,0x57,0x4d,0x5c,0xe3,0x80,0xdb,0xb1,0x2c,0xbf,0xb9,0x17,0x2a,0xc9,0x36,0x72,0x5e,0x7a,0x8,0x6,0x97,0xa5,0x59,0x8b,0x86,0xb6,0x43,0x8a,0x34,0x26,0x48,0xe2,0x3,0x9a,0x88,0x89,0x22,0x51,0x19,0x83,0x5a,0x35,0x5,0x8c,0xd1,0x6d,0x7e,0x8f,0xcc,0x3a,0x31,0xd9,0xc1,0xf9,0xe5,0x53,0x30,0x64,0x74,0xde,0xcf,0x9c,0xd7,0xd,0x14,0x4,0x2e,0xed,0x7f,0x71,0xfb,0x9f,0x11,0xe1,0xf,0x45,0xd5,0x2d,0x5d,0x1b,0x3f,0xec,0xd4,0xc2,0xb7,0xdf,0x75,0xa9,0xf0,0x5b,0x4f,0x77,0x49,0x52,0x13,0x7d,0xd3,0x44,0xf1,0xf8,0x93,0x78,0xce,0x0,0x4c,0xa0,0x94,0x87,0xeb,0x3e,0xe6,0x92,0x37,0x91,0x23,0xab,0xfd,0x25,0x1f,0xc,0xbc,0x24,0x6e,0x61,0xbe,0x70,0x6b,0xe7,0x56,0xfc,0x95,0xc8,0x98,0x18,0xb3,0x63,0x55,0xee,0xc6,0x47,0x46,0x7b,0x1e,0xfa,0x6f,0x3d,0x79,0xf7,0x8d,0xa2,0x9e,0xaf,0x5f,0xfe,0x33,0xea,0x9d,0xe0,0x68,0xf4,0x85,0xa8,0xdc,0xc4,0xb2,0x69,0x29,0x39,0x7c,0xa3,0x27,0xb5,0x32,0x62,0x41,0xe8,0x10,0xda,0xa,0x20,0xf6,0xd8,0xe9,0x84,0xc3,0x3c,0x90,0x16,0xf3,0x6c,0xa4,0x66,0x15,0x40,0x1a,0xba,0x21,0x54,0x96,0xb8,0x4e,0x8e,0x1,0xd0,0x99,0x9b,0x38,0xd6,0x65,0x9,0xae,0xf9,0x5f,0xb0,0x9f,0xf6,0xe8,0x9,0x6d,0xc0,0x5e,0x87,0x25,0x85,0x8,0x31,0x89,0xad,0x6a,0xe5,0xc3,0x79,0xef,0x12,0xe3,0x82,0x69,0x90,0xbd,0xee,0x1a,0x40,0xa6,0x1e,0xa1,0xc2,0x99,0xf3,0x6e,0xfd,0xfb,0x55,0x68,0x8b,0x74,0x30,0x1c,0x38,0x4a,0x49,0xf2,0x28,0xb7,0x4c,0x34,0xe4,0x50,0x8f,0x88,0x45,0xff,0x0,0x22,0x15,0xf,0xcb,0x60,0x13,0x5b,0xc1,0x18,0x77,0x47,0xce,0x93,0x2f,0x3c,0xcd,0x8e,0x78,0x73,0x44,0xd5,0xe7,0x1b,0xc9,0xc4,0xf4,0x1,0xc8,0x76,0x64,0xa,0xa0,0x41,0xd8,0xca,0xaf,0x3d,0x33,0xb9,0xdd,0x53,0xa3,0x4d,0x7,0x97,0x6f,0x1f,0x59,0x7d,0xae,0x96,0x9b,0x83,0xbb,0xa7,0x11,0x72,0x26,0x36,0x9c,0x8d,0xde,0x95,0x4f,0x56,0x46,0x6c,0xba,0xd1,0x3a,0x8c,0x42,0xe,0xe2,0xd6,0xc5,0xa9,0x7c,0xa4,0xd0,0x75,0xd3,0x61,0x80,0xf5,0x9d,0x37,0xeb,0xb2,0x19,0xd,0x35,0xb,0x10,0x51,0x3f,0x91,0x6,0xb3,0x8a,0xda,0x5a,0xf1,0x21,0x17,0xac,0x84,0x5,0x4,0x39,0x5c,0xb8,0x2d,0x7f,0x3b,0xe9,0xbf,0x67,0x5d,0x4e,0xfe,0x66,0x2c,0x23,0xfc,0x32,0x29,0xa5,0x14,0xbe,0xd7,0x86,0xf0,0x2b,0x6b,0x7b,0x3e,0xe1,0x65,0xf7,0x70,0x20,0x3,0xaa,0x52,0x98,0x48,0xb5,0xcf,0xe0,0xdc,0xed,0x1d,0xbc,0x71,0xa8,0xdf,0xa2,0x2a,0xb6,0xc7,0xea,0x9e,0xf8,0x63,0x16,0xd4,0xfa,0xc,0xcc,0x43,0x92,0xdb,0xd9,0x7a,0x94,0x27,0x4b,0xec,0x62,0xb4,0x9a,0xab,0xc6,0x81,0x7e,0xd2,0x54,0xb1,0x2e,0xe6,0x24,0x57,0x2,0x58,0x51,0x7c,0x85,0x6e,0x4a,0xac,0xf6,0x2,0x2f,0x9,0x86,0x41,0xf,0xfe,0x3,0x95,0xc9,0x6b,0xb2,0x2c,0x65,0xdd,0xe4,0x69,0x73,0x5c,0xb3,0x15,0x81,0xe5,0x4,0x1a,0x13,0xa9,0x64,0x63,0xe3,0xf9,0xce,0xec,0x5b,0xc4,0x1e,0xa5,0xbc,0x8,0xd8,0xa0,0x98,0x67,0x84,0xb9,0xa6,0xd4,0xf0,0xdc,0x75,0x2e,0x4d,0xf2,0x17,0x11,0x82,0x1f,0xe6,0x88,0x9a,0x24,0x26,0x34,0xad,0x4c,0xf7,0xb,0x39,0xa8,0xed,0x18,0x28,0x25,0xd0,0xc3,0x7f,0x22,0x9f,0x94,0x62,0x21,0xb7,0xff,0x8c,0x27,0xab,0x9b,0xf4,0x2d,0x79,0x32,0x61,0x70,0x80,0xaa,0xba,0xa3,0x4b,0x57,0x6f,0x77,0xda,0xca,0x9e,0xfd,0xf3,0x83,0x7b,0xeb,0x7a,0x42,0x91,0xb5,0x55,0xdf,0xd1,0x43,0xa1,0x4f,0xbf,0x31,0xbd,0xfc,0xe7,0xd9,0x5f,0xea,0x7d,0xd3,0xdb,0x71,0x19,0x6c,0xe1,0xf5,0x5e,0x7,0x48,0x90,0x45,0x29,0x8d,0x3f,0x99,0x3c,0x60,0xd6,0x3d,0x56,0x3a,0xe,0xe2,0xae,0xc5,0xde,0x10,0xcf,0x3b,0x52,0xf8,0x49,0xb1,0x8b,0x53,0x5,0xc0,0x8a,0x12,0xa2,0xb0,0xd5,0xe8,0xe9,0xd7,0x93,0xc1,0x54,0x1d,0xb6,0x36,0x66,0x68,0x40,0xfb,0xcd,0xc6,0x4e,0x33,0x44,0x72,0x6,0x2b,0x5a,0x30,0xc,0x23,0x59,0x9d,0x50,0xf1,0x1,0xef,0xcc,0x9c,0x1b,0xa4,0x74,0xbe,0x46,0x87,0xc7,0x1c,0x6a,0x89,0xd,0xd2,0x97,0xa,0xc2,0x5d,0xb8,0xb4,0xee,0xbb,0xc8,0x47,0x76,0x58,0x8e,0x3e,0x92,0x6d,0x2a,0x96,0x35,0x37,0x7e,0x0,0xa7,0xcb,0x78,0x38,0xfa,0x8f,0x14,0xaf,0x20,0xe0,0x16,0x64,0x62,0x6a,0xf7,0x5b,0x0,0x87,0x38,0xa1,0xd3,0xa9,0x85,0x12,0xed,0xcc,0xf1,0x7d,0xc9,0xd5,0xad,0xb1,0x2e,0xd0,0x6b,0x8c,0x96,0x99,0xbb,0xdc,0x66,0x16,0x11,0x90,0xf4,0x6f,0x71,0x29,0x6,0x60,0xc6,0xa8,0x10,0x1c,0x91,0x1e,0xbc,0x59,0xc7,0x8b,0x7a,0xe0,0x76,0x7c,0x5a,0x34,0xf3,0xd9,0x3f,0x77,0x83,0x9,0x24,0x1b,0xf0,0x3a,0xd4,0x44,0xca,0xaa,0x20,0x36,0xa4,0x37,0xf,0xc0,0xe4,0xf6,0x86,0x9e,0xe,0xbf,0xaf,0x88,0xeb,0x22,0x3e,0x2,0x1a,0xdf,0xf5,0xd6,0xcf,0x47,0xc,0x5,0x14,0xee,0xde,0x58,0x81,0x8a,0xc2,0x52,0xf9,0xe1,0xea,0x54,0x17,0xb6,0xa5,0x57,0xa,0x6d,0x98,0x50,0x5d,0x7e,0x82,0xdd,0x4c,0x41,0x53,0x39,0xd8,0xfd,0x93,0x51,0xef,0x35,0x1d,0xb8,0x8e,0xc3,0x68,0x13,0x43,0xe6,0xa2,0x21,0xb4,0xa0,0xc5,0x9c,0x9d,0xff,0xb5,0xd7,0x67,0xfe,0xc4,0x70,0x26,0x27,0x4e,0x3c,0x8d,0xab,0xb0,0xba,0x65,0x7b,0x4f,0xdb,0x97,0xa3,0x15,0x23,0x48,0x4a,0xf8,0x49,0xec,0xe5,0x3d,0x5c,0x30,0x80,0x94,0x72,0x2b,0x4,0xae,0x19,0x6c,0x9f,0x2a,0xa6,0x8,0x89,0xc8,0xac,0x92,0x55,0xda,0x63,0x95,0x8f,0x4d,0x61,0xfa,0xd2,0x75,0xd,0xbe,0x40,0xe3,0xb,0x42,0xe7,0x4b,0x5f,0x18,0x3,0x32,0xfb,0x2d,0x9b,0xc1,0xbd,0xce,0xb7,0x7f,0xcd,0x28,0x78,0xfc,0xe2,0xa7,0xb2,0xf2,0x1f,0x69,0x1,0xd1,0x33,0xcb,0xb9,0x9a,0x6e,0xe9,0x25,0xe8,0x74,0x84,0x79,0x45,0x2c,0x56,0x73,0x7,0x2f,0x5e,0x3b,0xb3,0x31,0x46,0x83,0x44,0xcb,0xed,0x57,0xc1,0x3c,0xcd,0xac,0x47,0xbe,0x93,0xc0,0x34,0x6e,0x88,0xd7,0x71,0x9e,0xb1,0xd8,0xc6,0x27,0x43,0xee,0x70,0xa9,0xb,0xab,0x26,0x1f,0xa7,0x67,0xdc,0x6,0x99,0x62,0x1a,0xca,0x7e,0xa1,0xa6,0x6b,0xd1,0x2e,0xc,0x3b,0x21,0x30,0x8f,0xec,0xb7,0xdd,0x40,0xd3,0xd5,0x7b,0x46,0xa5,0x5a,0x1e,0x32,0x16,0x64,0x6a,0xfb,0xc9,0x35,0xe7,0xea,0xda,0x2f,0xe6,0x58,0x4a,0x24,0x8e,0x6f,0xf6,0xe4,0xe5,0x4e,0x3d,0x75,0xef,0x36,0x59,0x69,0xe0,0xbd,0x1,0x12,0xe3,0xa0,0x56,0x5d,0xb5,0xad,0x95,0x89,0x3f,0x5c,0x8,0x18,0xb2,0xa3,0xf0,0xbb,0x61,0x78,0x68,0x42,0x81,0x13,0x1d,0x97,0xf3,0x7d,0x8d,0x63,0x29,0xb9,0x41,0x31,0x77,0x53,0x80,0xb8,0xae,0xdb,0xb3,0x19,0xc5,0x9c,0x37,0x23,0x1b,0x25,0x3e,0x7f,0x11,0xbf,0x28,0x9d,0x94,0xff,0x14,0xa2,0x6c,0x20,0xcc,0xf8,0xeb,0x87,0x52,0x8a,0xfe,0x5b,0xfd,0x4f,0xc7,0x91,0x49,0x73,0x60,0xd0,0x48,0x2,0xd,0xd2,0x1c,0x7,0x8b,0x3a,0x90,0xf9,0xa4,0xf4,0x74,0xdf,0xf,0x39,0x82,0xaa,0x2b,0x2a,0x17,0x72,0x96,0x3,0x51,0x15,0x9b,0xe1,0xce,0xf2,0xc3,0x33,0x92,0x5f,0x86,0xf1,0x8c,0x4,0x98,0xe9,0xc4,0xb0,0xa8,0xde,0x5,0x45,0x55,0x10,0xcf,0x4b,0xd9,0x5e,0xe,0x2d,0x84,0x7c,0xb6,0x66,0x4c,0x9a,0xb4,0x85,0xe8,0xaf,0x50,0xfc,0x7a,0x9f,0x0,0xc8,0xa,0x79,0x2c,0x76,0xd6,0x4d,0x38,0xfa,0xd4,0x22,0xe2,0x6d,0xbc,0xf5,0xf7,0x54,0xba,0x9,0x65,0xc2,0xd6,0x70,0x9f,0xb0,0xd9,0xc7,0x26,0x42,0xef,0x71,0xa8,0xa,0xaa,0x27,0x1e,0xa6,0x82,0x45,0xca,0xec,0x56,0xc0,0x3d,0xcc,0xad,0x46,0xbf,0x92,0xc1,0x35,0x6f,0x89,0x31,0x8e,0xed,0xb6,0xdc,0x41,0xd2,0xd4,0x7a,0x47,0xa4,0x5b,0x1f,0x33,0x17,0x65,0x66,0xdd,0x7,0x98,0x63,0x1b,0xcb,0x7f,0xa0,0xa7,0x6a,0xd0,0x2f,0xd,0x3a,0x20,0xe4,0x4f,0x3c,0x74,0xee,0x37,0x58,0x68,0xe1,0xbc,0x0,0x13,0xe2,0xa1,0x57,0x5c,0x6b,0xfa,0xc8,0x34,0xe6,0xeb,0xdb,0x2e,0xe7,0x59,0x4b,0x25,0x8f,0x6e,0xf7,0xe5,0x80,0x12,0x1c,0x96,0xf2,0x7c,0x8c,0x62,0x28,0xb8,0x40,0x30,0x76,0x52,0x81,0xb9,0xb4,0xac,0x94,0x88,0x3e,0x5d,0x9,0x19,0xb3,0xa2,0xf1,0xba,0x60,0x79,0x69,0x43,0x95,0xfe,0x15,0xa3,0x6d,0x21,0xcd,0xf9,0xea,0x86,0x53,0x8b,0xff,0x5a,0xfc,0x4e,0xaf,0xda,0xb2,0x18,0xc4,0x9d,0x36,0x22,0x1a,0x24,0x3f,0x7e,0x10,0xbe,0x29,0x9c,0xa5,0xf5,0x75,0xde,0xe,0x38,0x83,0xab,0x2a,0x2b,0x16,0x73,0x97,0x2,0x50,0x14,0xc6,0x90,0x48,0x72,0x61,0xd1,0x49,0x3,0xc,0xd3,0x1d,0x6,0x8a,0x3b,0x91,0xf8,0xa9,0xdf,0x4,0x44,0x54,0x11,0xce,0x4a,0xd8,0x5f,0xf,0x2c,0x85,0x7d,0xb7,0x67,0x9a,0xe0,0xcf,0xf3,0xc2,0x32,0x93,0x5e,0x87,0xf0,0x8d,0x5,0x99,0xe8,0xc5,0xb1,0xd7,0x4c,0x39,0xfb,0xd5,0x23,0xe3,0x6c,0xbd,0xf4,0xf6,0x55,0xbb,0x8,0x64,0xc3,0x4d,0x9b,0xb5,0x84,0xe9,0xae,0x51,0xfd,0x7b,0x9e,0x1,0xc9,0xb,0x78,0x2d,0x77,0x77,0x5a,0xa3,0x48,0x6c,0x8a,0xd0,0x24,0x9,0x2f,0xa0,0x67,0x29,0xd8,0x25,0xb3,0xef,0x4d,0x94,0xa,0x43,0xfb,0xc2,0x4f,0x55,0x7a,0x95,0x33,0xa7,0xc3,0x22,0x3c,0x35,0x8f,0x42,0x45,0xc5,0xdf,0xe8,0xca,0x7d,0xe2,0x38,0x83,0x9a,0x2e,0xfe,0x86,0xbe,0x41,0xa2,0x9f,0x80,0xf2,0xd6,0xfa,0x53,0x8,0x6b,0xd4,0x31,0x37,0xa4,0x39,0xc0,0xae,0xbc,0x2,0x0,0x12,0x8b,0x6a,0xd1,0x2d,0x1f,0x8e,0xcb,0x3e,0xe,0x3,0xf6,0xe5,0x59,0x4,0xb9,0xb2,0x44,0x7,0x91,0xd9,0xaa,0x1,0x8d,0xbd,0xd2,0xb,0x5f,0x14,0x47,0x56,0xa6,0x8c,0x9c,0x85,0x6d,0x71,0x49,0x51,0xfc,0xec,0xb8,0xdb,0xd5,0xa5,0x5d,0xcd,0x5c,0x64,0xb7,0x93,0x73,0xf9,0xf7,0x65,0x87,0x69,0x99,0x17,0x9b,0xda,0xc1,0xff,0x79,0xcc,0x5b,0xf5,0xfd,0x57,0x3f,0x4a,0xc7,0xd3,0x78,0x21,0x6e,0xb6,0x63,0xf,0xab,0x19,0xbf,0x1a,0x46,0xf0,0x1b,0x70,0x1c,0x28,0xc4,0x88,0xe3,0xf8,0x36,0xe9,0x1d,0x74,0xde,0x6f,0x97,0xad,0x75,0x23,0xe6,0xac,0x34,0x84,0x96,0xf3,0xce,0xcf,0xf1,0xb5,0xe7,0x72,0x3b,0x90,0x10,0x40,0x4e,0x66,0xdd,0xeb,0xe0,0x68,0x15,0x62,0x54,0x20,0xd,0x7c,0x16,0x2a,0x5,0x7f,0xbb,0x76,0xd7,0x27,0xc9,0xea,0xba,0x3d,0x82,0x52,0x98,0x60,0xa1,0xe1,0x3a,0x4c,0xaf,0x2b,0xf4,0xb1,0x2c,0xe4,0x7b,0x9e,0x92,0xc8,0x9d,0xee,0x61,0x50,0x7e,0xa8,0x18,0xb4,0x4b,0xc,0xb0,0x13,0x11,0x58,0x26,0x81,0xed,0x5e,0x1e,0xdc,0xa9,0x32,0x89,0x6,0xc6,0x30,0xc9,0xcf,0xc7,0x5a,0xf6,0xad,0x2a,0x95,0xc,0x7e,0x4,0x28,0xbf,0x40,0x61,0x5c,0xd0,0x64,0x78,0x0,0x1c,0x83,0x7d,0xc6,0x21,0x3b,0x34,0x16,0x71,0xcb,0xbb,0xbc,0x3d,0x59,0xc2,0xdc,0x84,0xab,0xcd,0x6b,0x5,0xbd,0xb1,0x3c,0xb3,0x11,0xf4,0x6a,0x26,0xd7,0x4d,0xdb,0xd1,0xf7,0x99,0x5e,0x74,0x92,0xda,0x2e,0xa4,0x89,0xb6,0x5d,0x97,0x79,0xe9,0x67,0x7,0x8d,0x9b,0x9,0x9a,0xa2,0x6d,0x49,0x5b,0x2b,0x33,0xa3,0x12,0x2,0x25,0x46,0x8f,0x93,0xaf,0xb7,0x72,0x58,0x7b,0x62,0xea,0xa1,0xa8,0xb9,0x43,0x73,0xf5,0x2c,0x27,0x6f,0xff,0x54,0x4c,0x47,0xf9,0xba,0x1b,0x8,0xfa,0xa7,0xc0,0x35,0xfd,0xf0,0xd3,0x2f,0x70,0xe1,0xec,0xfe,0x94,0x75,0x50,0x3e,0xfc,0x42,0x98,0xb0,0x15,0x23,0x6e,0xc5,0xbe,0xee,0x4b,0xf,0x8c,0x19,0xd,0x68,0x31,0x30,0x52,0x18,0x7a,0xca,0x53,0x69,0xdd,0x8b,0x8a,0xe3,0x91,0x20,0x6,0x1d,0x17,0xc8,0xd6,0xe2,0x76,0x3a,0xe,0xb8,0x8e,0xe5,0xe7,0x55,0xe4,0x41,0x48,0x90,0xf1,0x9d,0x2d,0x39,0xdf,0x86,0xa9,0x3,0xb4,0xc1,0x32,0x87,0xb,0xa5,0x24,0x65,0x1,0x3f,0xf8,0x77,0xce,0x38,0x22,0xe0,0xcc,0x57,0x7f,0xd8,0xa0,0x13,0xed,0x4e,0xa6,0xef,0x4a,0xe6,0xf2,0xb5,0xae,0x9f,0x56,0x80,0x36,0x6c,0x10,0x63,0x1a,0xd2,0x60,0x85,0xd5,0x51,0x4f,0xa,0x1f,0x5f,0xb2,0xc4,0xac,0x7c,0x9e,0x66,0x14,0x37,0xc3,0x44,0x88,0x45,0xd9,0x29,0xd4,0xe8,0x81,0xfb,0xde,0xaa,0x82,0xf3,0x96,0x1e,0x9c,0xeb,0x92,0x55,0xda,0xfc,0x46,0xd0,0x2d,0xdc,0xbd,0x56,0xaf,0x82,0xd1,0x25,0x7f,0x99,0xc6,0x60,0x8f,0xa0,0xc9,0xd7,0x36,0x52,0xff,0x61,0xb8,0x1a,0xba,0x37,0xe,0xb6,0x76,0xcd,0x17,0x88,0x73,0xb,0xdb,0x6f,0xb0,0xb7,0x7a,0xc0,0x3f,0x1d,0x2a,0x30,0x21,0x9e,0xfd,0xa6,0xcc,0x51,0xc2,0xc4,0x6a,0x57,0xb4,0x4b,0xf,0x23,0x7,0x75,0x7b,0xea,0xd8,0x24,0xf6,0xfb,0xcb,0x3e,0xf7,0x49,0x5b,0x35,0x9f,0x7e,0xe7,0xf5,0xf4,0x5f,0x2c,0x64,0xfe,0x27,0x48,0x78,0xf1,0xac,0x10,0x3,0xf2,0xb1,0x47,0x4c,0xa4,0xbc,0x84,0x98,0x2e,0x4d,0x19,0x9,0xa3,0xb2,0xe1,0xaa,0x70,0x69,0x79,0x53,0x90,0x2,0xc,0x86,0xe2,0x6c,0x9c,0x72,0x38,0xa8,0x50,0x20,0x66,0x42,0x91,0xa9,0xbf,0xca,0xa2,0x8,0xd4,0x8d,0x26,0x32,0xa,0x34,0x2f,0x6e,0x0,0xae,0x39,0x8c,0x85,0xee,0x5,0xb3,0x7d,0x31,0xdd,0xe9,0xfa,0x96,0x43,0x9b,0xef,0x4a,0xec,0x5e,0xd6,0x80,0x58,0x62,0x71,0xc1,0x59,0x13,0x1c,0xc3,0xd,0x16,0x9a,0x2b,0x81,0xe8,0xb5,0xe5,0x65,0xce,0x1e,0x28,0x93,0xbb,0x3a,0x3b,0x6,0x63,0x87,0x12,0x40,0x4,0x8a,0xf0,0xdf,0xe3,0xd2,0x22,0x83,0x4e,0x97,0xe0,0x9d,0x15,0x89,0xf8,0xd5,0xa1,0xb9,0xcf,0x14,0x54,0x44,0x1,0xde,0x5a,0xc8,0x4f,0x1f,0x3c,0x95,0x6d,0xa7,0x77,0x5d,0x8b,0xa5,0x94,0xf9,0xbe,0x41,0xed,0x6b,0x8e,0x11,0xd9,0x1b,0x68,0x3d,0x67,0xc7,0x5c,0x29,0xeb,0xc5,0x33,0xf3,0x7c,0xad,0xe4,0xe6,0x45,0xab,0x18,0x74,0xd3,0xf0,0x56,0xb9,0x96,0xff,0xe1,0x0,0x64,0xc9,0x57,0x8e,0x2c,0x8c,0x1,0x38,0x80,0xa4,0x63,0xec,0xca,0x70,0xe6,0x1b,0xea,0x8b,0x60,0x99,0xb4,0xe7,0x13,0x49,0xaf,0x17,0xa8,0xcb,0x90,0xfa,0x67,0xf4,0xf2,0x5c,0x61,0x82,0x7d,0x39,0x15,0x31,0x43,0x40,0xfb,0x21,0xbe,0x45,0x3d,0xed,0x59,0x86,0x81,0x4c,0xf6,0x9,0x2b,0x1c,0x6,0xc2,0x69,0x1a,0x52,0xc8,0x11,0x7e,0x4e,0xc7,0x9a,0x26,0x35,0xc4,0x87,0x71,0x7a,0x4d,0xdc,0xee,0x12,0xc0,0xcd,0xfd,0x8,0xc1,0x7f,0x6d,0x3,0xa9,0x48,0xd1,0xc3,0xa6,0x34,0x3a,0xb0,0xd4,0x5a,0xaa,0x44,0xe,0x9e,0x66,0x16,0x50,0x74,0xa7,0x9f,0x92,0x8a,0xb2,0xae,0x18,0x7b,0x2f,0x3f,0x95,0x84,0xd7,0x9c,0x46,0x5f,0x4f,0x65,0xb3,0xd8,0x33,0x85,0x4b,0x7,0xeb,0xdf,0xcc,0xa0,0x75,0xad,0xd9,0x7c,0xda,0x68,0x89,0xfc,0x94,0x3e,0xe2,0xbb,0x10,0x4,0x3c,0x2,0x19,0x58,0x36,0x98,0xf,0xba,0x83,0xd3,0x53,0xf8,0x28,0x1e,0xa5,0x8d,0xc,0xd,0x30,0x55,0xb1,0x24,0x76,0x32,0xe0,0xb6,0x6e,0x54,0x47,0xf7,0x6f,0x25,0x2a,0xf5,0x3b,0x20,0xac,0x1d,0xb7,0xde,0x8f,0xf9,0x22,0x62,0x72,0x37,0xe8,0x6c,0xfe,0x79,0x29,0xa,0xa3,0x5b,0x91,0x41,0xbc,0xc6,0xe9,0xd5,0xe4,0x14,0xb5,0x78,0xa1,0xd6,0xab,0x23,0xbf,0xce,0xe3,0x97,0xf1,0x6a,0x1f,0xdd,0xf3,0x5,0xc5,0x4a,0x9b,0xd2,0xd0,0x73,0x9d,0x2e,0x42,0xe5,0x6b,0xbd,0x93,0xa2,0xcf,0x88,0x77,0xdb,0x5d,0xb8,0x27,0xef,0x2d,0x5e,0xb,0x51,0xe,0x23,0xda,0x31,0x15,0xf3,0xa9,0x5d,0x70,0x56,0xd9,0x1e,0x50,0xa1,0x5c,0xca,0x96,0x34,0xed,0x73,0x3a,0x82,0xbb,0x36,0x2c,0x3,0xec,0x4a,0xde,0xba,0x5b,0x45,0x4c,0xf6,0x3b,0x3c,0xbc,0xa6,0x91,0xb3,0x4,0x9b,0x41,0xfa,0xe3,0x57,0x87,0xff,0xc7,0x38,0xdb,0xe6,0xf9,0x8b,0xaf,0x83,0x2a,0x71,0x12,0xad,0x48,0x4e,0xdd,0x40,0xb9,0xd7,0xc5,0x7b,0x79,0x6b,0xf2,0x13,0xa8,0x54,0x66,0xf7,0xb2,0x47,0x77,0x7a,0x8f,0x9c,0x20,0x7d,0xc0,0xcb,0x3d,0x7e,0xe8,0xa0,0xd3,0x78,0xf4,0xc4,0xab,0x72,0x26,0x6d,0x3e,0x2f,0xdf,0xf5,0xe5,0xfc,0x14,0x8,0x30,0x28,0x85,0x95,0xc1,0xa2,0xac,0xdc,0x24,0xb4,0x25,0x1d,0xce,0xea,0xa,0x80,0x8e,0x1c,0xfe,0x10,0xe0,0x6e,0xe2,0xa3,0xb8,0x86,0x0,0xb5,0x22,0x8c,0x84,0x2e,0x46,0x33,0xbe,0xaa,0x1,0x58,0x17,0xcf,0x1a,0x76,0xd2,0x60,0xc6,0x63,0x3f,0x89,0x62,0x9,0x65,0x51,0xbd,0xf1,0x9a,0x81,0x4f,0x90,0x64,0xd,0xa7,0x16,0xee,0xd4,0xc,0x5a,0x9f,0xd5,0x4d,0xfd,0xef,0x8a,0xb7,0xb6,0x88,0xcc,0x9e,0xb,0x42,0xe9,0x69,0x39,0x37,0x1f,0xa4,0x92,0x99,0x11,0x6c,0x1b,0x2d,0x59,0x74,0x5,0x6f,0x53,0x7c,0x6,0xc2,0xf,0xae,0x5e,0xb0,0x93,0xc3,0x44,0xfb,0x2b,0xe1,0x19,0xd8,0x98,0x43,0x35,0xd6,0x52,0x8d,0xc8,0x55,0x9d,0x2,0xe7,0xeb,0xb1,0xe4,0x97,0x18,0x29,0x7,0xd1,0x61,0xcd,0x32,0x75,0xc9,0x6a,0x68,0x21,0x5f,0xf8,0x94,0x27,0x67,0xa5,0xd0,0x4b,0xf0,0x7f,0xbf,0x49,0xcd,0xcb,0xc3,0x5e,0xf2,0xa9,0x2e,0x91,0x8,0x7a,0x0,0x2c,0xbb,0x44,0x65,0x58,0xd4,0x60,0x7c,0x4,0x18,0x87,0x79,0xc2,0x25,0x3f,0x30,0x12,0x75,0xcf,0xbf,0xb8,0x39,0x5d,0xc6,0xd8,0x80,0xaf,0xc9,0x6f,0x1,0xb9,0xb5,0x38,0xb7,0x15,0xf0,0x6e,0x22,0xd3,0x49,0xdf,0xd5,0xf3,0x9d,0x5a,0x70,0x96,0xde,0x2a,0xa0,0x8d,0xb2,0x59,0x93,0x7d,0xed,0x63,0x3,0x89,0x9f,0xd,0x9e,0xa6,0x69,0x4d,0x5f,0x2f,0x37,0xa7,0x16,0x6,0x21,0x42,0x8b,0x97,0xab,0xb3,0x76,0x5c,0x7f,0x66,0xee,0xa5,0xac,0xbd,0x47,0x77,0xf1,0x28,0x23,0x6b,0xfb,0x50,0x48,0x43,0xfd,0xbe,0x1f,0xc,0xfe,0xa3,0xc4,0x31,0xf9,0xf4,0xd7,0x2b,0x74,0xe5,0xe8,0xfa,0x90,0x71,0x54,0x3a,0xf8,0x46,0x9c,0xb4,0x11,0x27,0x6a,0xc1,0xba,0xea,0x4f,0xb,0x88,0x1d,0x9,0x6c,0x35,0x34,0x56,0x1c,0x7e,0xce,0x57,0x6d,0xd9,0x8f,0x8e,0xe7,0x95,0x24,0x2,0x19,0x13,0xcc,0xd2,0xe6,0x72,0x3e,0xa,0xbc,0x8a,0xe1,0xe3,0x51,0xe0,0x45,0x4c,0x94,0xf5,0x99,0x29,0x3d,0xdb,0x82,0xad,0x7,0xb0,0xc5,0x36,0x83,0xf,0xa1,0x20,0x61,0x5,0x3b,0xfc,0x73,0xca,0x3c,0x26,0xe4,0xc8,0x53,0x7b,0xdc,0xa4,0x17,0xe9,0x4a,0xa2,0xeb,0x4e,0xe2,0xf6,0xb1,0xaa,0x9b,0x52,0x84,0x32,0x68,0x14,0x67,0x1e,0xd6,0x64,0x81,0xd1,0x55,0x4b,0xe,0x1b,0x5b,0xb6,0xc0,0xa8,0x78,0x9a,0x62,0x10,0x33,0xc7,0x40,0x8c,0x41,0xdd,0x2d,0xd0,0xec,0x85,0xff,0xda,0xae,0x86,0xf7,0x92,0x1a,0x98,0xef,0x80,0x47,0xc8,0xee,0x54,0xc2,0x3f,0xce,0xaf,0x44,0xbd,0x90,0xc3,0x37,0x6d,0x8b,0xd4,0x72,0x9d,0xb2,0xdb,0xc5,0x24,0x40,0xed,0x73,0xaa,0x8,0xa8,0x25,0x1c,0xa4,0x64,0xdf,0x5,0x9a,0x61,0x19,0xc9,0x7d,0xa2,0xa5,0x68,0xd2,0x2d,0xf,0x38,0x22,0x33,0x8c,0xef,0xb4,0xde,0x43,0xd0,0xd6,0x78,0x45,0xa6,0x59,0x1d,0x31,0x15,0x67,0x69,0xf8,0xca,0x36,0xe4,0xe9,0xd9,0x2c,0xe5,0x5b,0x49,0x27,0x8d,0x6c,0xf5,0xe7,0xe6,0x4d,0x3e,0x76,0xec,0x35,0x5a,0x6a,0xe3,0xbe,0x2,0x11,0xe0,0xa3,0x55,0x5e,0xb6,0xae,0x96,0x8a,0x3c,0x5f,0xb,0x1b,0xb1,0xa0,0xf3,0xb8,0x62,0x7b,0x6b,0x41,0x82,0x10,0x1e,0x94,0xf0,0x7e,0x8e,0x60,0x2a,0xba,0x42,0x32,0x74,0x50,0x83,0xbb,0xad,0xd8,0xb0,0x1a,0xc6,0x9f,0x34,0x20,0x18,0x26,0x3d,0x7c,0x12,0xbc,0x2b,0x9e,0x97,0xfc,0x17,0xa1,0x6f,0x23,0xcf,0xfb,0xe8,0x84,0x51,0x89,0xfd,0x58,0xfe,0x4c,0xc4,0x92,0x4a,0x70,0x63,0xd3,0x4b,0x1,0xe,0xd1,0x1f,0x4,0x88,0x39,0x93,0xfa,0xa7,0xf7,0x77,0xdc,0xc,0x3a,0x81,0xa9,0x28,0x29,0x14,0x71,0x95,0x0,0x52,0x16,0x98,0xe2,0xcd,0xf1,0xc0,0x30,0x91,0x5c,0x85,0xf2,0x8f,0x7,0x9b,0xea,0xc7,0xb3,0xab,0xdd,0x6,0x46,0x56,0x13,0xcc,0x48,0xda,0x5d,0xd,0x2e,0x87,0x7f,0xb5,0x65,0x4f,0x99,0xb7,0x86,0xeb,0xac,0x53,0xff,0x79,0x9c,0x3,0xcb,0x9,0x7a,0x2f,0x75,0xd5,0x4e,0x3b,0xf9,0xd7,0x21,0xe1,0x6e,0xbf,0xf6,0xf4,0x57,0xb9,0xa,0x66,0xc1,0x4e,0xe8,0x7,0x28,0x41,0x5f,0xbe,0xda,0x77,0xe9,0x30,0x92,0x32,0xbf,0x86,0x3e,0x1a,0xdd,0x52,0x74,0xce,0x58,0xa5,0x54,0x35,0xde,0x27,0xa,0x59,0xad,0xf7,0x11,0xa9,0x16,0x75,0x2e,0x44,0xd9,0x4a,0x4c,0xe2,0xdf,0x3c,0xc3,0x87,0xab,0x8f,0xfd,0xfe,0x45,0x9f,0x0,0xfb,0x83,0x53,0xe7,0x38,0x3f,0xf2,0x48,0xb7,0x95,0xa2,0xb8,0x7c,0xd7,0xa4,0xec,0x76,0xaf,0xc0,0xf0,0x79,0x24,0x98,0x8b,0x7a,0x39,0xcf,0xc4,0xf3,0x62,0x50,0xac,0x7e,0x73,0x43,0xb6,0x7f,0xc1,0xd3,0xbd,0x17,0xf6,0x6f,0x7d,0x18,0x8a,0x84,0xe,0x6a,0xe4,0x14,0xfa,0xb0,0x20,0xd8,0xa8,0xee,0xca,0x19,0x21,0x2c,0x34,0xc,0x10,0xa6,0xc5,0x91,0x81,0x2b,0x3a,0x69,0x22,0xf8,0xe1,0xf1,0xdb,0xd,0x66,0x8d,0x3b,0xf5,0xb9,0x55,0x61,0x72,0x1e,0xcb,0x13,0x67,0xc2,0x64,0xd6,0x37,0x42,0x2a,0x80,0x5c,0x5,0xae,0xba,0x82,0xbc,0xa7,0xe6,0x88,0x26,0xb1,0x4,0x3d,0x6d,0xed,0x46,0x96,0xa0,0x1b,0x33,0xb2,0xb3,0x8e,0xeb,0xf,0x9a,0xc8,0x8c,0x5e,0x8,0xd0,0xea,0xf9,0x49,0xd1,0x9b,0x94,0x4b,0x85,0x9e,0x12,0xa3,0x9,0x60,0x31,0x47,0x9c,0xdc,0xcc,0x89,0x56,0xd2,0x40,0xc7,0x97,0xb4,0x1d,0xe5,0x2f,0xff,0x2,0x78,0x57,0x6b,0x5a,0xaa,0xb,0xc6,0x1f,0x68,0x15,0x9d,0x1,0x70,0x5d,0x29,0x4f,0xd4,0xa1,0x63,0x4d,0xbb,0x7b,0xf4,0x25,0x6c,0x6e,0xcd,0x23,0x90,0xfc,0x5b,0xd5,0x3,0x2d,0x1c,0x71,0x36,0xc9,0x65,0xe3,0x6,0x99,0x51,0x93,0xe0,0xb5,0xef,0x70,0x5d,0xa4,0x4f,0x6b,0x8d,0xd7,0x23,0xe,0x28,0xa7,0x60,0x2e,0xdf,0x22,0xb4,0xe8,0x4a,0x93,0xd,0x44,0xfc,0xc5,0x48,0x52,0x7d,0x92,0x34,0xa0,0xc4,0x25,0x3b,0x32,0x88,0x45,0x42,0xc2,0xd8,0xef,0xcd,0x7a,0xe5,0x3f,0x84,0x9d,0x29,0xf9,0x81,0xb9,0x46,0xa5,0x98,0x87,0xf5,0xd1,0xfd,0x54,0xf,0x6c,0xd3,0x36,0x30,0xa3,0x3e,0xc7,0xa9,0xbb,0x5,0x7,0x15,0x8c,0x6d,0xd6,0x2a,0x18,0x89,0xcc,0x39,0x9,0x4,0xf1,0xe2,0x5e,0x3,0xbe,0xb5,0x43,0x0,0x96,0xde,0xad,0x6,0x8a,0xba,0xd5,0xc,0x58,0x13,0x40,0x51,0xa1,0x8b,0x9b,0x82,0x6a,0x76,0x4e,0x56,0xfb,0xeb,0xbf,0xdc,0xd2,0xa2,0x5a,0xca,0x5b,0x63,0xb0,0x94,0x74,0xfe,0xf0,0x62,0x80,0x6e,0x9e,0x10,0x9c,0xdd,0xc6,0xf8,0x7e,0xcb,0x5c,0xf2,0xfa,0x50,0x38,0x4d,0xc0,0xd4,0x7f,0x26,0x69,0xb1,0x64,0x8,0xac,0x1e,0xb8,0x1d,0x41,0xf7,0x1c,0x77,0x1b,0x2f,0xc3,0x8f,0xe4,0xff,0x31,0xee,0x1a,0x73,0xd9,0x68,0x90,0xaa,0x72,0x24,0xe1,0xab,0x33,0x83,0x91,0xf4,0xc9,0xc8,0xf6,0xb2,0xe0,0x75,0x3c,0x97,0x17,0x47,0x49,0x61,0xda,0xec,0xe7,0x6f,0x12,0x65,0x53,0x27,0xa,0x7b,0x11,0x2d,0x2,0x78,0xbc,0x71,0xd0,0x20,0xce,0xed,0xbd,0x3a,0x85,0x55,0x9f,0x67,0xa6,0xe6,0x3d,0x4b,0xa8,0x2c,0xf3,0xb6,0x2b,0xe3,0x7c,0x99,0x95,0xcf,0x9a,0xe9,0x66,0x57,0x79,0xaf,0x1f,0xb3,0x4c,0xb,0xb7,0x14,0x16,0x5f,0x21,0x86,0xea,0x59,0x19,0xdb,0xae,0x35,0x8e,0x1,0xc1,0x37,0xf4,0xf2,0xfa,0x67,0xcb,0x90,0x17,0xa8,0x31,0x43,0x39,0x15,0x82,0x7d,0x5c,0x61,0xed,0x59,0x45,0x3d,0x21,0xbe,0x40,0xfb,0x1c,0x6,0x9,0x2b,0x4c,0xf6,0x86,0x81,0x0,0x64,0xff,0xe1,0xb9,0x96,0xf0,0x56,0x38,0x80,0x8c,0x1,0x8e,0x2c,0xc9,0x57,0x1b,0xea,0x70,0xe6,0xec,0xca,0xa4,0x63,0x49,0xaf,0xe7,0x13,0x99,0xb4,0x8b,0x60,0xaa,0x44,0xd4,0x5a,0x3a,0xb0,0xa6,0x34,0xa7,0x9f,0x50,0x74,0x66,0x16,0xe,0x9e,0x2f,0x3f,0x18,0x7b,0xb2,0xae,0x92,0x8a,0x4f,0x65,0x46,0x5f,0xd7,0x9c,0x95,0x84,0x7e,0x4e,0xc8,0x11,0x1a,0x52,0xc2,0x69,0x71,0x7a,0xc4,0x87,0x26,0x35,0xc7,0x9a,0xfd,0x8,0xc0,0xcd,0xee,0x12,0x4d,0xdc,0xd1,0xc3,0xa9,0x48,0x6d,0x3,0xc1,0x7f,0xa5,0x8d,0x28,0x1e,0x53,0xf8,0x83,0xd3,0x76,0x32,0xb1,0x24,0x30,0x55,0xc,0xd,0x6f,0x25,0x47,0xf7,0x6e,0x54,0xe0,0xb6,0xb7,0xde,0xac,0x1d,0x3b,0x20,0x2a,0xf5,0xeb,0xdf,0x4b,0x7,0x33,0x85,0xb3,0xd8,0xda,0x68,0xd9,0x7c,0x75,0xad,0xcc,0xa0,0x10,0x4,0xe2,0xbb,0x94,0x3e,0x89,0xfc,0xf,0xba,0x36,0x98,0x19,0x58,0x3c,0x2,0xc5,0x4a,0xf3,0x5,0x1f,0xdd,0xf1,0x6a,0x42,0xe5,0x9d,0x2e,0xd0,0x73,0x9b,0xd2,0x77,0xdb,0xcf,0x88,0x93,0xa2,0x6b,0xbd,0xb,0x51,0x2d,0x5e,0x27,0xef,0x5d,0xb8,0xe8,0x6c,0x72,0x37,0x22,0x62,0x8f,0xf9,0x91,0x41,0xa3,0x5b,0x29,0xa,0xfe,0x79,0xb5,0x78,0xe4,0x14,0xe9,0xd5,0xbc,0xc6,0xe3,0x97,0xbf,0xce,0xab,0x23,0xa1,0xd6,0x44,0x83,0xc,0x2a,0x90,0x6,0xfb,0xa,0x6b,0x80,0x79,0x54,0x7,0xf3,0xa9,0x4f,0x10,0xb6,0x59,0x76,0x1f,0x1,0xe0,0x84,0x29,0xb7,0x6e,0xcc,0x6c,0xe1,0xd8,0x60,0xa0,0x1b,0xc1,0x5e,0xa5,0xdd,0xd,0xb9,0x66,0x61,0xac,0x16,0xe9,0xcb,0xfc,0xe6,0xf7,0x48,0x2b,0x70,0x1a,0x87,0x14,0x12,0xbc,0x81,0x62,0x9d,0xd9,0xf5,0xd1,0xa3,0xad,0x3c,0xe,0xf2,0x20,0x2d,0x1d,0xe8,0x21,0x9f,0x8d,0xe3,0x49,0xa8,0x31,0x23,0x22,0x89,0xfa,0xb2,0x28,0xf1,0x9e,0xae,0x27,0x7a,0xc6,0xd5,0x24,0x67,0x91,0x9a,0x72,0x6a,0x52,0x4e,0xf8,0x9b,0xcf,0xdf,0x75,0x64,0x37,0x7c,0xa6,0xbf,0xaf,0x85,0x46,0xd4,0xda,0x50,0x34,0xba,0x4a,0xa4,0xee,0x7e,0x86,0xf6,0xb0,0x94,0x47,0x7f,0x69,0x1c,0x74,0xde,0x2,0x5b,0xf0,0xe4,0xdc,0xe2,0xf9,0xb8,0xd6,0x78,0xef,0x5a,0x53,0x38,0xd3,0x65,0xab,0xe7,0xb,0x3f,0x2c,0x40,0x95,0x4d,0x39,0x9c,0x3a,0x88,0x0,0x56,0x8e,0xb4,0xa7,0x17,0x8f,0xc5,0xca,0x15,0xdb,0xc0,0x4c,0xfd,0x57,0x3e,0x63,0x33,0xb3,0x18,0xc8,0xfe,0x45,0x6d,0xec,0xed,0xd0,0xb5,0x51,0xc4,0x96,0xd2,0x5c,0x26,0x9,0x35,0x4,0xf4,0x55,0x98,0x41,0x36,0x4b,0xc3,0x5f,0x2e,0x3,0x77,0x6f,0x19,0xc2,0x82,0x92,0xd7,0x8,0x8c,0x1e,0x99,0xc9,0xea,0x43,0xbb,0x71,0xa1,0x8b,0x5d,0x73,0x42,0x2f,0x68,0x97,0x3b,0xbd,0x58,0xc7,0xf,0xcd,0xbe,0xeb,0xb1,0x11,0x8a,0xff,0x3d,0x13,0xe5,0x25,0xaa,0x7b,0x32,0x30,0x93,0x7d,0xce,0xa2,0x5,0xed,0x4b,0xa4,0x8b,0xe2,0xfc,0x1d,0x79,0xd4,0x4a,0x93,0x31,0x91,0x1c,0x25,0x9d,0xb9,0x7e,0xf1,0xd7,0x6d,0xfb,0x6,0xf7,0x96,0x7d,0x84,0xa9,0xfa,0xe,0x54,0xb2,0xa,0xb5,0xd6,0x8d,0xe7,0x7a,0xe9,0xef,0x41,0x7c,0x9f,0x60,0x24,0x8,0x2c,0x5e,0x5d,0xe6,0x3c,0xa3,0x58,0x20,0xf0,0x44,0x9b,0x9c,0x51,0xeb,0x14,0x36,0x1,0x1b,0xdf,0x74,0x7,0x4f,0xd5,0xc,0x63,0x53,0xda,0x87,0x3b,0x28,0xd9,0x9a,0x6c,0x67,0x50,0xc1,0xf3,0xf,0xdd,0xd0,0xe0,0x15,0xdc,0x62,0x70,0x1e,0xb4,0x55,0xcc,0xde,0xbb,0x29,0x27,0xad,0xc9,0x47,0xb7,0x59,0x13,0x83,0x7b,0xb,0x4d,0x69,0xba,0x82,0x8f,0x97,0xaf,0xb3,0x5,0x66,0x32,0x22,0x88,0x99,0xca,0x81,0x5b,0x42,0x52,0x78,0xae,0xc5,0x2e,0x98,0x56,0x1a,0xf6,0xc2,0xd1,0xbd,0x68,0xb0,0xc4,0x61,0xc7,0x75,0x94,0xe1,0x89,0x23,0xff,0xa6,0xd,0x19,0x21,0x1f,0x4,0x45,0x2b,0x85,0x12,0xa7,0x9e,0xce,0x4e,0xe5,0x35,0x3,0xb8,0x90,0x11,0x10,0x2d,0x48,0xac,0x39,0x6b,0x2f,0xfd,0xab,0x73,0x49,0x5a,0xea,0x72,0x38,0x37,0xe8,0x26,0x3d,0xb1,0x0,0xaa,0xc3,0x92,0xe4,0x3f,0x7f,0x6f,0x2a,0xf5,0x71,0xe3,0x64,0x34,0x17,0xbe,0x46,0x8c,0x5c,0xa1,0xdb,0xf4,0xc8,0xf9,0x9,0xa8,0x65,0xbc,0xcb,0xb6,0x3e,0xa2,0xd3,0xfe,0x8a,0xec,0x77,0x2,0xc0,0xee,0x18,0xd8,0x57,0x86,0xcf,0xcd,0x6e,0x80,0x33,0x5f,0xf8,0x76,0xa0,0x8e,0xbf,0xd2,0x95,0x6a,0xc6,0x40,0xa5,0x3a,0xf2,0x30,0x43,0x16,0x4c,0xb3,0x9e,0x67,0x8c,0xa8,0x4e,0x14,0xe0,0xcd,0xeb,0x64,0xa3,0xed,0x1c,0xe1,0x77,0x2b,0x89,0x50,0xce,0x87,0x3f,0x6,0x8b,0x91,0xbe,0x51,0xf7,0x63,0x7,0xe6,0xf8,0xf1,0x4b,0x86,0x81,0x1,0x1b,0x2c,0xe,0xb9,0x26,0xfc,0x47,0x5e,0xea,0x3a,0x42,0x7a,0x85,0x66,0x5b,0x44,0x36,0x12,0x3e,0x97,0xcc,0xaf,0x10,0xf5,0xf3,0x60,0xfd,0x4,0x6a,0x78,0xc6,0xc4,0xd6,0x4f,0xae,0x15,0xe9,0xdb,0x4a,0xf,0xfa,0xca,0xc7,0x32,0x21,0x9d,0xc0,0x7d,0x76,0x80,0xc3,0x55,0x1d,0x6e,0xc5,0x49,0x79,0x16,0xcf,0x9b,0xd0,0x83,0x92,0x62,0x48,0x58,0x41,0xa9,0xb5,0x8d,0x95,0x38,0x28,0x7c,0x1f,0x11,0x61,0x99,0x9,0x98,0xa0,0x73,0x57,0xb7,0x3d,0x33,0xa1,0x43,0xad,0x5d,0xd3,0x5f,0x1e,0x5,0x3b,0xbd,0x8,0x9f,0x31,0x39,0x93,0xfb,0x8e,0x3,0x17,0xbc,0xe5,0xaa,0x72,0xa7,0xcb,0x6f,0xdd,0x7b,0xde,0x82,0x34,0xdf,0xb4,0xd8,0xec,0x0,0x4c,0x27,0x3c,0xf2,0x2d,0xd9,0xb0,0x1a,0xab,0x53,0x69,0xb1,0xe7,0x22,0x68,0xf0,0x40,0x52,0x37,0xa,0xb,0x35,0x71,0x23,0xb6,0xff,0x54,0xd4,0x84,0x8a,0xa2,0x19,0x2f,0x24,0xac,0xd1,0xa6,0x90,0xe4,0xc9,0xb8,0xd2,0xee,0xc1,0xbb,0x7f,0xb2,0x13,0xe3,0xd,0x2e,0x7e,0xf9,0x46,0x96,0x5c,0xa4,0x65,0x25,0xfe,0x88,0x6b,0xef,0x30,0x75,0xe8,0x20,0xbf,0x5a,0x56,0xc,0x59,0x2a,0xa5,0x94,0xba,0x6c,0xdc,0x70,0x8f,0xc8,0x74,0xd7,0xd5,0x9c,0xe2,0x45,0x29,0x9a,0xda,0x18,0x6d,0xf6,0x4d,0xc2,0x2,0xf4,0x7e,0x78,0x70,0xed,0x41,0x1a,0x9d,0x22,0xbb,0xc9,0xb3,0x9f,0x8,0xf7,0xd6,0xeb,0x67,0xd3,0xcf,0xb7,0xab,0x34,0xca,0x71,0x96,0x8c,0x83,0xa1,0xc6,0x7c,0xc,0xb,0x8a,0xee,0x75,0x6b,0x33,0x1c,0x7a,0xdc,0xb2,0xa,0x6,0x8b,0x4,0xa6,0x43,0xdd,0x91,0x60,0xfa,0x6c,0x66,0x40,0x2e,0xe9,0xc3,0x25,0x6d,0x99,0x13,0x3e,0x1,0xea,0x20,0xce,0x5e,0xd0,0xb0,0x3a,0x2c,0xbe,0x2d,0x15,0xda,0xfe,0xec,0x9c,0x84,0x14,0xa5,0xb5,0x92,0xf1,0x38,0x24,0x18,0x0,0xc5,0xef,0xcc,0xd5,0x5d,0x16,0x1f,0xe,0xf4,0xc4,0x42,0x9b,0x90,0xd8,0x48,0xe3,0xfb,0xf0,0x4e,0xd,0xac,0xbf,0x4d,0x10,0x77,0x82,0x4a,0x47,0x64,0x98,0xc7,0x56,0x5b,0x49,0x23,0xc2,0xe7,0x89,0x4b,0xf5,0x2f,0x7,0xa2,0x94,0xd9,0x72,0x9,0x59,0xfc,0xb8,0x3b,0xae,0xba,0xdf,0x86,0x87,0xe5,0xaf,0xcd,0x7d,0xe4,0xde,0x6a,0x3c,0x3d,0x54,0x26,0x97,0xb1,0xaa,0xa0,0x7f,0x61,0x55,0xc1,0x8d,0xb9,0xf,0x39,0x52,0x50,0xe2,0x53,0xf6,0xff,0x27,0x46,0x2a,0x9a,0x8e,0x68,0x31,0x1e,0xb4,0x3,0x76,0x85,0x30,0xbc,0x12,0x93,0xd2,0xb6,0x88,0x4f,0xc0,0x79,0x8f,0x95,0x57,0x7b,0xe0,0xc8,0x6f,0x17,0xa4,0x5a,0xf9,0x11,0x58,0xfd,0x51,0x45,0x2,0x19,0x28,0xe1,0x37,0x81,0xdb,0xa7,0xd4,0xad,0x65,0xd7,0x32,0x62,0xe6,0xf8,0xbd,0xa8,0xe8,0x5,0x73,0x1b,0xcb,0x29,0xd1,0xa3,0x80,0x74,0xf3,0x3f,0xf2,0x6e,0x9e,0x63,0x5f,0x36,0x4c,0x69,0x1d,0x35,0x44,0x21,0xa9,0x2b,0x5c,0x10,0xd7,0x58,0x7e,0xc4,0x52,0xaf,0x5e,0x3f,0xd4,0x2d,0x0,0x53,0xa7,0xfd,0x1b,0x44,0xe2,0xd,0x22,0x4b,0x55,0xb4,0xd0,0x7d,0xe3,0x3a,0x98,0x38,0xb5,0x8c,0x34,0xf4,0x4f,0x95,0xa,0xf1,0x89,0x59,0xed,0x32,0x35,0xf8,0x42,0xbd,0x9f,0xa8,0xb2,0xa3,0x1c,0x7f,0x24,0x4e,0xd3,0x40,0x46,0xe8,0xd5,0x36,0xc9,0x8d,0xa1,0x85,0xf7,0xf9,0x68,0x5a,0xa6,0x74,0x79,0x49,0xbc,0x75,0xcb,0xd9,0xb7,0x1d,0xfc,0x65,0x77,0x76,0xdd,0xae,0xe6,0x7c,0xa5,0xca,0xfa,0x73,0x2e,0x92,0x81,0x70,0x33,0xc5,0xce,0x26,0x3e,0x6,0x1a,0xac,0xcf,0x9b,0x8b,0x21,0x30,0x63,0x28,0xf2,0xeb,0xfb,0xd1,0x12,0x80,0x8e,0x4,0x60,0xee,0x1e,0xf0,0xba,0x2a,0xd2,0xa2,0xe4,0xc0,0x13,0x2b,0x3d,0x48,0x20,0x8a,0x56,0xf,0xa4,0xb0,0x88,0xb6,0xad,0xec,0x82,0x2c,0xbb,0xe,0x7,0x6c,0x87,0x31,0xff,0xb3,0x5f,0x6b,0x78,0x14,0xc1,0x19,0x6d,0xc8,0x6e,0xdc,0x54,0x2,0xda,0xe0,0xf3,0x43,0xdb,0x91,0x9e,0x41,0x8f,0x94,0x18,0xa9,0x3,0x6a,0x37,0x67,0xe7,0x4c,0x9c,0xaa,0x11,0x39,0xb8,0xb9,0x84,0xe1,0x5,0x90,0xc2,0x86,0x8,0x72,0x5d,0x61,0x50,0xa0,0x1,0xcc,0x15,0x62,0x1f,0x97,0xb,0x7a,0x57,0x23,0x3b,0x4d,0x96,0xd6,0xc6,0x83,0x5c,0xd8,0x4a,0xcd,0x9d,0xbe,0x17,0xef,0x25,0xf5,0xdf,0x9,0x27,0x16,0x7b,0x3c,0xc3,0x6f,0xe9,0xc,0x93,0x5b,0x99,0xea,0xbf,0xe5,0x45,0xde,0xab,0x69,0x47,0xb1,0x71,0xfe,0x2f,0x66,0x64,0xc7,0x29,0x9a,0xf6,0x51,0x14,0xb2,0x5d,0x72,0x1b,0x5,0xe4,0x80,0x2d,0xb3,0x6a,0xc8,0x68,0xe5,0xdc,0x64,0x40,0x87,0x8,0x2e,0x94,0x2,0xff,0xe,0x6f,0x84,0x7d,0x50,0x3,0xf7,0xad,0x4b,0xf3,0x4c,0x2f,0x74,0x1e,0x83,0x10,0x16,0xb8,0x85,0x66,0x99,0xdd,0xf1,0xd5,0xa7,0xa4,0x1f,0xc5,0x5a,0xa1,0xd9,0x9,0xbd,0x62,0x65,0xa8,0x12,0xed,0xcf,0xf8,0xe2,0x26,0x8d,0xfe,0xb6,0x2c,0xf5,0x9a,0xaa,0x23,0x7e,0xc2,0xd1,0x20,0x63,0x95,0x9e,0xa9,0x38,0xa,0xf6,0x24,0x29,0x19,0xec,0x25,0x9b,0x89,0xe7,0x4d,0xac,0x35,0x27,0x42,0xd0,0xde,0x54,0x30,0xbe,0x4e,0xa0,0xea,0x7a,0x82,0xf2,0xb4,0x90,0x43,0x7b,0x76,0x6e,0x56,0x4a,0xfc,0x9f,0xcb,0xdb,0x71,0x60,0x33,0x78,0xa2,0xbb,0xab,0x81,0x57,0x3c,0xd7,0x61,0xaf,0xe3,0xf,0x3b,0x28,0x44,0x91,0x49,0x3d,0x98,0x3e,0x8c,0x6d,0x18,0x70,0xda,0x6,0x5f,0xf4,0xe0,0xd8,0xe6,0xfd,0xbc,0xd2,0x7c,0xeb,0x5e,0x67,0x37,0xb7,0x1c,0xcc,0xfa,0x41,0x69,0xe8,0xe9,0xd4,0xb1,0x55,0xc0,0x92,0xd6,0x4,0x52,0x8a,0xb0,0xa3,0x13,0x8b,0xc1,0xce,0x11,0xdf,0xc4,0x48,0xf9,0x53,0x3a,0x6b,0x1d,0xc6,0x86,0x96,0xd3,0xc,0x88,0x1a,0x9d,0xcd,0xee,0x47,0xbf,0x75,0xa5,0x58,0x22,0xd,0x31,0x0,0xf0,0x51,0x9c,0x45,0x32,0x4f,0xc7,0x5b,0x2a,0x7,0x73,0x15,0x8e,0xfb,0x39,0x17,0xe1,0x21,0xae,0x7f,0x36,0x34,0x97,0x79,0xca,0xa6,0x1,0x8f,0x59,0x77,0x46,0x2b,0x6c,0x93,0x3f,0xb9,0x5c,0xc3,0xb,0xc9,0xba,0xef,0xb5,0xf8,0xd5,0x2c,0xc7,0xe3,0x5,0x5f,0xab,0x86,0xa0,0x2f,0xe8,0xa6,0x57,0xaa,0x3c,0x60,0xc2,0x1b,0x85,0xcc,0x74,0x4d,0xc0,0xda,0xf5,0x1a,0xbc,0x28,0x4c,0xad,0xb3,0xba,0x0,0xcd,0xca,0x4a,0x50,0x67,0x45,0xf2,0x6d,0xb7,0xc,0x15,0xa1,0x71,0x9,0x31,0xce,0x2d,0x10,0xf,0x7d,0x59,0x75,0xdc,0x87,0xe4,0x5b,0xbe,0xb8,0x2b,0xb6,0x4f,0x21,0x33,0x8d,0x8f,0x9d,0x4,0xe5,0x5e,0xa2,0x90,0x1,0x44,0xb1,0x81,0x8c,0x79,0x6a,0xd6,0x8b,0x36,0x3d,0xcb,0x88,0x1e,0x56,0x25,0x8e,0x2,0x32,0x5d,0x84,0xd0,0x9b,0xc8,0xd9,0x29,0x3,0x13,0xa,0xe2,0xfe,0xc6,0xde,0x73,0x63,0x37,0x54,0x5a,0x2a,0xd2,0x42,0xd3,0xeb,0x38,0x1c,0xfc,0x76,0x78,0xea,0x8,0xe6,0x16,0x98,0x14,0x55,0x4e,0x70,0xf6,0x43,0xd4,0x7a,0x72,0xd8,0xb0,0xc5,0x48,0x5c,0xf7,0xae,0xe1,0x39,0xec,0x80,0x24,0x96,0x30,0x95,0xc9,0x7f,0x94,0xff,0x93,0xa7,0x4b,0x7,0x6c,0x77,0xb9,0x66,0x92,0xfb,0x51,0xe0,0x18,0x22,0xfa,0xac,0x69,0x23,0xbb,0xb,0x19,0x7c,0x41,0x40,0x7e,0x3a,0x68,0xfd,0xb4,0x1f,0x9f,0xcf,0xc1,0xe9,0x52,0x64,0x6f,0xe7,0x9a,0xed,0xdb,0xaf,0x82,0xf3,0x99,0xa5,0x8a,0xf0,0x34,0xf9,0x58,0xa8,0x46,0x65,0x35,0xb2,0xd,0xdd,0x17,0xef,0x2e,0x6e,0xb5,0xc3,0x20,0xa4,0x7b,0x3e,0xa3,0x6b,0xf4,0x11,0x1d,0x47,0x12,0x61,0xee,0xdf,0xf1,0x27,0x97,0x3b,0xc4,0x83,0x3f,0x9c,0x9e,0xd7,0xa9,0xe,0x62,0xd1,0x91,0x53,0x26,0xbd,0x6,0x89,0x49,0xbf,0x32,0x34,0x3c,0xa1,0xd,0x56,0xd1,0x6e,0xf7,0x85,0xff,0xd3,0x44,0xbb,0x9a,0xa7,0x2b,0x9f,0x83,0xfb,0xe7,0x78,0x86,0x3d,0xda,0xc0,0xcf,0xed,0x8a,0x30,0x40,0x47,0xc6,0xa2,0x39,0x27,0x7f,0x50,0x36,0x90,0xfe,0x46,0x4a,0xc7,0x48,0xea,0xf,0x91,0xdd,0x2c,0xb6,0x20,0x2a,0xc,0x62,0xa5,0x8f,0x69,0x21,0xd5,0x5f,0x72,0x4d,0xa6,0x6c,0x82,0x12,0x9c,0xfc,0x76,0x60,0xf2,0x61,0x59,0x96,0xb2,0xa0,0xd0,0xc8,0x58,0xe9,0xf9,0xde,0xbd,0x74,0x68,0x54,0x4c,0x89,0xa3,0x80,0x99,0x11,0x5a,0x53,0x42,0xb8,0x88,0xe,0xd7,0xdc,0x94,0x4,0xaf,0xb7,0xbc,0x2,0x41,0xe0,0xf3,0x1,0x5c,0x3b,0xce,0x6,0xb,0x28,0xd4,0x8b,0x1a,0x17,0x5,0x6f,0x8e,0xab,0xc5,0x7,0xb9,0x63,0x4b,0xee,0xd8,0x95,0x3e,0x45,0x15,0xb0,0xf4,0x77,0xe2,0xf6,0x93,0xca,0xcb,0xa9,0xe3,0x81,0x31,0xa8,0x92,0x26,0x70,0x71,0x18,0x6a,0xdb,0xfd,0xe6,0xec,0x33,0x2d,0x19,0x8d,0xc1,0xf5,0x43,0x75,0x1e,0x1c,0xae,0x1f,0xba,0xb3,0x6b,0xa,0x66,0xd6,0xc2,0x24,0x7d,0x52,0xf8,0x4f,0x3a,0xc9,0x7c,0xf0,0x5e,0xdf,0x9e,0xfa,0xc4,0x3,0x8c,0x35,0xc3,0xd9,0x1b,0x37,0xac,0x84,0x23,0x5b,0xe8,0x16,0xb5,0x5d,0x14,0xb1,0x1d,0x9,0x4e,0x55,0x64,0xad,0x7b,0xcd,0x97,0xeb,0x98,0xe1,0x29,0x9b,0x7e,0x2e,0xaa,0xb4,0xf1,0xe4,0xa4,0x49,0x3f,0x57,0x87,0x65,0x9d,0xef,0xcc,0x38,0xbf,0x73,0xbe,0x22,0xd2,0x2f,0x13,0x7a,0x0,0x25,0x51,0x79,0x8,0x6d,0xe5,0x67,0x10,0xae,0x69,0xe6,0xc0,0x7a,0xec,0x11,0xe0,0x81,0x6a,0x93,0xbe,0xed,0x19,0x43,0xa5,0xfa,0x5c,0xb3,0x9c,0xf5,0xeb,0xa,0x6e,0xc3,0x5d,0x84,0x26,0x86,0xb,0x32,0x8a,0x4a,0xf1,0x2b,0xb4,0x4f,0x37,0xe7,0x53,0x8c,0x8b,0x46,0xfc,0x3,0x21,0x16,0xc,0x1d,0xa2,0xc1,0x9a,0xf0,0x6d,0xfe,0xf8,0x56,0x6b,0x88,0x77,0x33,0x1f,0x3b,0x49,0x47,0xd6,0xe4,0x18,0xca,0xc7,0xf7,0x2,0xcb,0x75,0x67,0x9,0xa3,0x42,0xdb,0xc9,0xc8,0x63,0x10,0x58,0xc2,0x1b,0x74,0x44,0xcd,0x90,0x2c,0x3f,0xce,0x8d,0x7b,0x70,0x98,0x80,0xb8,0xa4,0x12,0x71,0x25,0x35,0x9f,0x8e,0xdd,0x96,0x4c,0x55,0x45,0x6f,0xac,0x3e,0x30,0xba,0xde,0x50,0xa0,0x4e,0x4,0x94,0x6c,0x1c,0x5a,0x7e,0xad,0x95,0x83,0xf6,0x9e,0x34,0xe8,0xb1,0x1a,0xe,0x36,0x8,0x13,0x52,0x3c,0x92,0x5,0xb0,0xb9,0xd2,0x39,0x8f,0x41,0xd,0xe1,0xd5,0xc6,0xaa,0x7f,0xa7,0xd3,0x76,0xd0,0x62,0xea,0xbc,0x64,0x5e,0x4d,0xfd,0x65,0x2f,0x20,0xff,0x31,0x2a,0xa6,0x17,0xbd,0xd4,0x89,0xd9,0x59,0xf2,0x22,0x14,0xaf,0x87,0x6,0x7,0x3a,0x5f,0xbb,0x2e,0x7c,0x38,0xb6,0xcc,0xe3,0xdf,0xee,0x1e,0xbf,0x72,0xab,0xdc,0xa1,0x29,0xb5,0xc4,0xe9,0x9d,0x85,0xf3,0x28,0x68,0x78,0x3d,0xe2,0x66,0xf4,0x73,0x23,0x0,0xa9,0x51,0x9b,0x4b,0x61,0xb7,0x99,0xa8,0xc5,0x82,0x7d,0xd1,0x57,0xb2,0x2d,0xe5,0x27,0x54,0x1,0x5b,0xfb,0x60,0x15,0xd7,0xf9,0xf,0xcf,0x40,0x91,0xd8,0xda,0x79,0x97,0x24,0x48,0xef,0x8c,0x2a,0xc5,0xea,0x83,0x9d,0x7c,0x18,0xb5,0x2b,0xf2,0x50,0xf0,0x7d,0x44,0xfc,0xd8,0x1f,0x90,0xb6,0xc,0x9a,0x67,0x96,0xf7,0x1c,0xe5,0xc8,0x9b,0x6f,0x35,0xd3,0x6b,0xd4,0xb7,0xec,0x86,0x1b,0x88,0x8e,0x20,0x1d,0xfe,0x1,0x45,0x69,0x4d,0x3f,0x3c,0x87,0x5d,0xc2,0x39,0x41,0x91,0x25,0xfa,0xfd,0x30,0x8a,0x75,0x57,0x60,0x7a,0xbe,0x15,0x66,0x2e,0xb4,0x6d,0x2,0x32,0xbb,0xe6,0x5a,0x49,0xb8,0xfb,0xd,0x6,0x31,0xa0,0x92,0x6e,0xbc,0xb1,0x81,0x74,0xbd,0x3,0x11,0x7f,0xd5,0x34,0xad,0xbf,0xda,0x48,0x46,0xcc,0xa8,0x26,0xd6,0x38,0x72,0xe2,0x1a,0x6a,0x2c,0x8,0xdb,0xe3,0xee,0xf6,0xce,0xd2,0x64,0x7,0x53,0x43,0xe9,0xf8,0xab,0xe0,0x3a,0x23,0x33,0x19,0xcf,0xa4,0x4f,0xf9,0x37,0x7b,0x97,0xa3,0xb0,0xdc,0x9,0xd1,0xa5,0x0,0xa6,0x14,0xf5,0x80,0xe8,0x42,0x9e,0xc7,0x6c,0x78,0x40,0x7e,0x65,0x24,0x4a,0xe4,0x73,0xc6,0xff,0xaf,0x2f,0x84,0x54,0x62,0xd9,0xf1,0x70,0x71,0x4c,0x29,0xcd,0x58,0xa,0x4e,0x9c,0xca,0x12,0x28,0x3b,0x8b,0x13,0x59,0x56,0x89,0x47,0x5c,0xd0,0x61,0xcb,0xa2,0xf3,0x85,0x5e,0x1e,0xe,0x4b,0x94,0x10,0x82,0x5,0x55,0x76,0xdf,0x27,0xed,0x3d,0xc0,0xba,0x95,0xa9,0x98,0x68,0xc9,0x4,0xdd,0xaa,0xd7,0x5f,0xc3,0xb2,0x9f,0xeb,0x8d,0x16,0x63,0xa1,0x8f,0x79,0xb9,0x36,0xe7,0xae,0xac,0xf,0xe1,0x52,0x3e,0x99,0x17,0xc1,0xef,0xde,0xb3,0xf4,0xb,0xa7,0x21,0xc4,0x5b,0x93,0x51,0x22,0x77,0x2d,0x58,0x75,0x8c,0x67,0x43,0xa5,0xff,0xb,0x26,0x0,0x8f,0x48,0x6,0xf7,0xa,0x9c,0xc0,0x62,0xbb,0x25,0x6c,0xd4,0xed,0x60,0x7a,0x55,0xba,0x1c,0x88,0xec,0xd,0x13,0x1a,0xa0,0x6d,0x6a,0xea,0xf0,0xc7,0xe5,0x52,0xcd,0x17,0xac,0xb5,0x1,0xd1,0xa9,0x91,0x6e,0x8d,0xb0,0xaf,0xdd,0xf9,0xd5,0x7c,0x27,0x44,0xfb,0x1e,0x18,0x8b,0x16,0xef,0x81,0x93,0x2d,0x2f,0x3d,0xa4,0x45,0xfe,0x2,0x30,0xa1,0xe4,0x11,0x21,0x2c,0xd9,0xca,0x76,0x2b,0x96,0x9d,0x6b,0x28,0xbe,0xf6,0x85,0x2e,0xa2,0x92,0xfd,0x24,0x70,0x3b,0x68,0x79,0x89,0xa3,0xb3,0xaa,0x42,0x5e,0x66,0x7e,0xd3,0xc3,0x97,0xf4,0xfa,0x8a,0x72,0xe2,0x73,0x4b,0x98,0xbc,0x5c,0xd6,0xd8,0x4a,0xa8,0x46,0xb6,0x38,0xb4,0xf5,0xee,0xd0,0x56,0xe3,0x74,0xda,0xd2,0x78,0x10,0x65,0xe8,0xfc,0x57,0xe,0x41,0x99,0x4c,0x20,0x84,0x36,0x90,0x35,0x69,0xdf,0x34,0x5f,0x33,0x7,0xeb,0xa7,0xcc,0xd7,0x19,0xc6,0x32,0x5b,0xf1,0x40,0xb8,0x82,0x5a,0xc,0xc9,0x83,0x1b,0xab,0xb9,0xdc,0xe1,0xe0,0xde,0x9a,0xc8,0x5d,0x14,0xbf,0x3f,0x6f,0x61,0x49,0xf2,0xc4,0xcf,0x47,0x3a,0x4d,0x7b,0xf,0x22,0x53,0x39,0x5,0x2a,0x50,0x94,0x59,0xf8,0x8,0xe6,0xc5,0x95,0x12,0xad,0x7d,0xb7,0x4f,0x8e,0xce,0x15,0x63,0x80,0x4,0xdb,0x9e,0x3,0xcb,0x54,0xb1,0xbd,0xe7,0xb2,0xc1,0x4e,0x7f,0x51,0x87,0x37,0x9b,0x64,0x23,0x9f,0x3c,0x3e,0x77,0x9,0xae,0xc2,0x71,0x31,0xf3,0x86,0x1d,0xa6,0x29,0xe9,0x1f,0xf,0x9,0x1,0x9c,0x30,0x6b,0xec,0x53,0xca,0xb8,0xc2,0xee,0x79,0x86,0xa7,0x9a,0x16,0xa2,0xbe,0xc6,0xda,0x45,0xbb,0x0,0xe7,0xfd,0xf2,0xd0,0xb7,0xd,0x7d,0x7a,0xfb,0x9f,0x4,0x1a,0x42,0x6d,0xb,0xad,0xc3,0x7b,0x77,0xfa,0x75,0xd7,0x32,0xac,0xe0,0x11,0x8b,0x1d,0x17,0x31,0x5f,0x98,0xb2,0x54,0x1c,0xe8,0x62,0x4f,0x70,0x9b,0x51,0xbf,0x2f,0xa1,0xc1,0x4b,0x5d,0xcf,0x5c,0x64,0xab,0x8f,0x9d,0xed,0xf5,0x65,0xd4,0xc4,0xe3,0x80,0x49,0x55,0x69,0x71,0xb4,0x9e,0xbd,0xa4,0x2c,0x67,0x6e,0x7f,0x85,0xb5,0x33,0xea,0xe1,0xa9,0x39,0x92,0x8a,0x81,0x3f,0x7c,0xdd,0xce,0x3c,0x61,0x6,0xf3,0x3b,0x36,0x15,0xe9,0xb6,0x27,0x2a,0x38,0x52,0xb3,0x96,0xf8,0x3a,0x84,0x5e,0x76,0xd3,0xe5,0xa8,0x3,0x78,0x28,0x8d,0xc9,0x4a,0xdf,0xcb,0xae,0xf7,0xf6,0x94,0xde,0xbc,0xc,0x95,0xaf,0x1b,0x4d,0x4c,0x25,0x57,0xe6,0xc0,0xdb,0xd1,0xe,0x10,0x24,0xb0,0xfc,0xc8,0x7e,0x48,0x23,0x21,0x93,0x22,0x87,0x8e,0x56,0x37,0x5b,0xeb,0xff,0x19,0x40,0x6f,0xc5,0x72,0x7,0xf4,0x41,0xcd,0x63,0xe2,0xa3,0xc7,0xf9,0x3e,0xb1,0x8,0xfe,0xe4,0x26,0xa,0x91,0xb9,0x1e,0x66,0xd5,0x2b,0x88,0x60,0x29,0x8c,0x20,0x34,0x73,0x68,0x59,0x90,0x46,0xf0,0xaa,0xd6,0xa5,0xdc,0x14,0xa6,0x43,0x13,0x97,0x89,0xcc,0xd9,0x99,0x74,0x2,0x6a,0xba,0x58,0xa0,0xd2,0xf1,0x5,0x82,0x4e,0x83,0x1f,0xef,0x12,0x2e,0x47,0x3d,0x18,0x6c,0x44,0x35,0x50,0xd8,0x5a,0x2d};
+
+unsigned char table_s2[] = {0xab,0xd,0xb5,0xc8,0x9d,0xed,0x11,0x50,0x63,0x8b,0x83,0x79,0x12,0x7c,0x8d,0x23,0xa1,0xeb,0x4c,0xe6,0xd2,0x53,0x7b,0x62,0xf0,0x14,0x28,0xae,0x9e,0xaf,0xb6,0x8c,0xc6,0xb3,0x85,0x40,0x94,0x6d,0x32,0x58,0xf7,0xf8,0x86,0xb1,0xdd,0xdb,0xfc,0x37,0x5b,0xa4,0xee,0x46,0x27,0x1a,0x41,0x93,0x89,0xdf,0x3,0x5a,0x59,0x87,0x60,0xd5,0xe5,0xb7,0xc1,0x3c,0xce,0x1f,0xc2,0x6f,0x36,0x9,0x5f,0xe0,0x10,0x52,0x4a,0x61,0x4b,0xe7,0x42,0xbe,0xa0,0xde,0x2d,0xa9,0x91,0x21,0xfe,0x39,0xb9,0x26,0x68,0xbb,0x25,0xec,0x4f,0x97,0xa,0x4e,0xfb,0x81,0x49,0x5c,0x67,0xc7,0x1c,0x17,0xb8,0xa7,0x16,0xad,0xf9,0x69,0xe4,0x3e,0x0,0xd9,0xac,0x3d,0x2e,0x47,0xd1,0x74,0x1d,0x54,0x7,0x9b,0xd3,0x56,0x29,0xdc,0xa6,0xc9,0x33,0x9c,0x2a,0xfd,0x72,0xf5,0xb,0x7d,0x22,0x80,0x96,0x4,0x3f,0xe9,0x2f,0x34,0x65,0x84,0x38,0xa3,0x2b,0xe1,0x24,0xc4,0x7f,0x57,0xc0,0xb4,0xea,0xca,0x45,0x99,0x90,0x75,0xbc,0x71,0xb0,0xf6,0x1b,0xcb,0x9f,0x6,0xff,0x98,0x20,0xd4,0xfa,0x9a,0xd0,0x8a,0x18,0x70,0xb2,0x5e,0x13,0x3a,0xe3,0x35,0x5d,0x1e,0xd8,0xd6,0xda,0x51,0x2,0x66,0x8,0xd7,0xcf,0xf1,0x30,0x2c,0x6b,0x95,0xaa,0xf2,0xba,0xc3,0xef,0x78,0xc,0x8f,0xa2,0x4d,0x44,0x43,0x7a,0x77,0x15,0xa8,0xc5,0xf3,0x5,0xf4,0xe,0xf,0x55,0x48,0xbf,0xe2,0xbd,0x92,0x7e,0x6c,0xcd,0x82,0x31,0x3b,0xcc,0x19,0x6e,0x6a,0x1,0x76,0x73,0x64,0xe8,0x8e,0xa5,0x88,0x28,0xef,0x30,0x80,0xaa,0x79,0x37,0xa8,0xaf,0x53,0xf6,0x5a,0xb8,0x3c,0xcf,0xb1,0xf1,0x4e,0x18,0x27,0x70,0x5b,0x43,0x1,0x2d,0xd0,0xa6,0xf4,0x7e,0xd3,0xe,0xdf,0x56,0x3f,0x2c,0xbd,0x45,0xc,0x65,0xc0,0x78,0xe8,0xbc,0x7,0xc8,0x11,0x2f,0xf5,0xd6,0x76,0x4d,0x58,0xb6,0xa9,0x6,0xd,0x86,0x5e,0xfd,0x34,0x90,0xea,0x5f,0x1b,0xbf,0x39,0x5,0xe1,0x9d,0xa7,0xbe,0x8f,0xf7,0x5d,0xfa,0xb0,0x73,0x6a,0x42,0xc3,0x68,0x92,0x9a,0x72,0x32,0x9c,0x6d,0x3,0xd9,0xa4,0x1c,0xba,0x41,0x0,0xfc,0x8c,0x4b,0x12,0xce,0x98,0xc4,0x71,0x96,0x48,0x57,0xff,0xb5,0x4a,0x82,0x50,0xb,0x36,0xa0,0x97,0xe9,0xe6,0x26,0xed,0xca,0xcc,0x51,0x94,0xa2,0xd7,0x49,0x23,0x7c,0x85,0x5c,0xb3,0x9e,0x1d,0x66,0x6b,0x52,0x55,0xe3,0xbb,0x84,0x7a,0x69,0xfe,0xd2,0xab,0xc6,0x19,0x77,0x13,0x3d,0x21,0xe0,0xde,0xf,0x4c,0x24,0xf2,0x40,0xcb,0xc7,0xc9,0x75,0x62,0x67,0x10,0x99,0xb4,0x9f,0xf9,0x2a,0x20,0x93,0xdc,0x7b,0x7f,0x8,0xdd,0xf3,0xae,0x59,0x44,0x7d,0x6f,0x83,0xac,0xe2,0xd4,0xb9,0x4,0x1e,0x1f,0xe5,0x14,0xb2,0x29,0x95,0x74,0xd5,0x35,0xf0,0x3a,0x15,0x87,0x91,0x33,0x25,0x3e,0xf8,0x2e,0xec,0x3b,0x8d,0x22,0x6c,0x1a,0xe4,0x63,0x47,0xc2,0x8a,0x16,0xd8,0xb7,0xcd,0x38,0x61,0x9,0x9b,0xc1,0x2b,0x2,0x4f,0xa3,0x89,0xee,0x17,0x8e,0x8b,0xeb,0xc5,0x31,0x60,0xad,0x64,0x81,0xda,0xa,0xe7,0xa1,0xa5,0xd1,0x46,0x6e,0x88,0x54,0xdb,0xfb,0x83,0x21,0x7,0x95,0xea,0x3c,0x37,0x2c,0x87,0x66,0xa0,0x3b,0xe2,0x28,0xc7,0x27,0x98,0x4,0x55,0xd0,0xdf,0x2a,0xca,0xa5,0x9f,0x30,0xfe,0x29,0xf6,0x71,0x7e,0x8,0x5,0x9c,0x9b,0xfc,0xd7,0x23,0x99,0xf9,0x89,0xd3,0x73,0x1b,0x5d,0xb1,0x39,0x10,0x54,0x7c,0xb7,0xc3,0xc9,0xe9,0x9a,0x46,0x76,0x93,0x72,0xbf,0xf5,0xb3,0xc8,0x18,0x96,0x68,0xf1,0xa9,0xc0,0xb9,0x7b,0xec,0x8c,0xf,0x4e,0xa1,0x40,0x47,0x74,0x79,0x36,0xe0,0x1d,0x5e,0xd5,0xdb,0x52,0xd9,0x65,0x1,0xd4,0xb,0xf2,0xcc,0x2f,0x33,0x81,0xce,0x38,0x32,0x1a,0xcf,0x69,0x6d,0x75,0x2,0x67,0x70,0x8d,0xeb,0x8b,0xa6,0xab,0x16,0xf0,0xc6,0xf7,0x6,0xc,0xd,0x4b,0x56,0xe1,0xbc,0x91,0xbe,0x6f,0x7d,0xe8,0xa2,0xe5,0x4f,0x50,0xd1,0x61,0x78,0x17,0xf3,0xad,0x2b,0xac,0x9d,0x8f,0xb5,0xe,0xa8,0xcb,0xb6,0xee,0x9e,0x53,0x12,0x88,0x60,0x7a,0x80,0x7f,0x11,0x20,0x8e,0xa7,0x58,0x45,0xed,0x19,0x24,0x90,0x42,0xdc,0x8a,0x59,0x0,0x84,0x5a,0xd6,0x63,0xb0,0xc5,0x43,0x86,0x6e,0x97,0x5b,0x31,0xfb,0xf4,0xb2,0x85,0xd8,0xde,0x34,0xff,0xe4,0x48,0xbd,0x41,0xdd,0xa3,0xaa,0x2e,0x22,0x92,0x3a,0xfd,0x25,0xba,0xb8,0x6b,0xb4,0xe6,0x3f,0xc2,0x1c,0xcd,0x6c,0xc1,0xa,0x35,0xe3,0x5c,0x51,0x13,0x62,0x49,0xae,0x15,0x6a,0xfa,0x3d,0xe7,0xda,0x3,0x3e,0xaf,0x44,0x2d,0x77,0xd2,0x57,0x1e,0xef,0x26,0x94,0x4c,0x4d,0x9,0x82,0xf8,0x5f,0x4a,0xc4,0x64,0x14,0x1f,0xa4,0xbb,0x47,0xbb,0x4e,0xe2,0x28,0xac,0xa5,0xdb,0xfb,0x3c,0x94,0x24,0x6d,0xbe,0xbc,0x23,0xc4,0x39,0xe0,0xb2,0xc7,0x6a,0xcb,0x1a,0x5a,0xe5,0x33,0xc,0x4f,0x64,0x15,0x57,0xfc,0x6c,0x13,0xa8,0x5,0xdc,0xe1,0x3b,0x2b,0x42,0xa9,0x38,0x18,0x51,0xd4,0x71,0x4a,0x92,0x20,0xe9,0xfe,0x84,0xf,0x4b,0x62,0xc2,0x4c,0x59,0xbd,0xa2,0x19,0x12,0x49,0xe3,0xa4,0xee,0x7e,0x67,0xd7,0x56,0x2d,0xab,0xf5,0x11,0xb3,0x89,0x9b,0xaa,0xb0,0xcd,0xae,0x8,0x14,0x55,0x98,0xe8,0x86,0x7c,0x66,0x8e,0x88,0x26,0x17,0x79,0xeb,0x43,0x5e,0xa1,0x44,0x96,0x22,0x1f,0x6,0x5f,0x8c,0xda,0x65,0xd0,0x5c,0x82,0x80,0x45,0xc3,0xb6,0x37,0x5d,0x91,0x68,0x83,0xb4,0xf2,0xfd,0xf9,0x32,0xd8,0xde,0xaf,0xf7,0x6e,0x90,0xea,0x7d,0xbf,0xc6,0xa7,0x48,0x9,0x8a,0x7f,0x72,0x41,0x46,0x58,0x1b,0xe6,0x30,0xdf,0x54,0xdd,0xd3,0xd,0xd2,0x7,0x63,0x35,0x29,0xca,0xf4,0x34,0x3e,0xc8,0x87,0x6b,0x6f,0xc9,0x1c,0x76,0x61,0x4,0x73,0xa0,0x8d,0xed,0x8b,0xc0,0xf6,0x10,0xad,0xb,0xa,0x0,0xf1,0xba,0xe7,0x50,0x4d,0x7b,0x69,0xb8,0x97,0x93,0x1,0x27,0x85,0x2a,0x31,0x3a,0xec,0x3d,0xa6,0x60,0x81,0x21,0xc1,0x2e,0xe4,0xd6,0x53,0x2,0x9e,0xa3,0xcc,0x2c,0xd9,0x2f,0xf8,0x36,0x99,0xe,0x78,0x77,0xf0,0xfa,0x9d,0x9a,0x3,0xff,0x9f,0x25,0xd1,0x1d,0x75,0xd5,0x8f,0x16,0x3f,0xb7,0x5b,0xc5,0xb1,0x7a,0x52,0x40,0x9c,0xef,0xcf,0xb9,0x74,0x95,0x70,0x1e,0xce,0xb5,0xf3,0x6c,0xca,0x72,0xf,0x5a,0x2a,0xd6,0x97,0xa4,0x4c,0x44,0xbe,0xd5,0xbb,0x4a,0xe4,0x66,0x2c,0x8b,0x21,0x15,0x94,0xbc,0xa5,0x37,0xd3,0xef,0x69,0x59,0x68,0x71,0x4b,0x1,0x74,0x42,0x87,0x53,0xaa,0xf5,0x9f,0x30,0x3f,0x41,0x76,0x1a,0x1c,0x3b,0xf0,0x9c,0x63,0x29,0x81,0xe0,0xdd,0x86,0x54,0x4e,0x18,0xc4,0x9d,0x9e,0x40,0xa7,0x12,0x22,0x70,0x6,0xfb,0x9,0xd8,0x5,0xa8,0xf1,0xce,0x98,0x27,0xd7,0x95,0x8d,0xa6,0x8c,0x20,0x85,0x79,0x67,0x19,0xea,0x6e,0x56,0xe6,0x39,0xfe,0x7e,0xe1,0xaf,0x7c,0xe2,0x2b,0x88,0x50,0xcd,0x89,0x3c,0x46,0x8e,0x9b,0xa0,0x0,0xdb,0xd0,0x7f,0x60,0xd1,0x6a,0x3e,0xae,0x23,0xf9,0xc7,0x1e,0x6b,0xfa,0xe9,0x80,0x16,0xb3,0xda,0x93,0xc0,0x5c,0x14,0x91,0xee,0x1b,0x61,0xe,0xf4,0x5b,0xed,0x3a,0xb5,0x32,0xcc,0xba,0xe5,0x47,0x51,0xc3,0xf8,0x2e,0xe8,0xf3,0xa2,0x43,0xff,0x64,0xec,0x26,0xe3,0x3,0xb8,0x90,0x7,0x73,0x2d,0xd,0x82,0x5e,0x57,0xb2,0x7b,0xb6,0x77,0x31,0xdc,0xc,0x58,0xc1,0x38,0x5f,0xe7,0x13,0x3d,0x5d,0x17,0x4d,0xdf,0xb7,0x75,0x99,0xd4,0xfd,0x24,0xf2,0x9a,0xd9,0x1f,0x11,0x1d,0x96,0xc5,0xa1,0xcf,0x10,0x8,0x36,0xf7,0xeb,0xac,0x52,0x6d,0x35,0x7d,0x4,0x28,0xbf,0xcb,0x48,0x65,0x8a,0x83,0x84,0xbd,0xb0,0xd2,0x6f,0x2,0x34,0xc2,0x33,0xc9,0xc8,0x92,0x8f,0x78,0x25,0x7a,0x55,0xb9,0xab,0xa,0x45,0xf6,0xfc,0xb,0xde,0xa9,0xad,0xc6,0xb1,0xb4,0xa3,0x2f,0x49,0x62,0x4f,0xad,0x6a,0xb5,0x5,0x2f,0xfc,0xb2,0x2d,0x2a,0xd6,0x73,0xdf,0x3d,0xb9,0x4a,0x34,0x74,0xcb,0x9d,0xa2,0xf5,0xde,0xc6,0x84,0xa8,0x55,0x23,0x71,0xfb,0x56,0x8b,0x5a,0xd3,0xba,0xa9,0x38,0xc0,0x89,0xe0,0x45,0xfd,0x6d,0x39,0x82,0x4d,0x94,0xaa,0x70,0x53,0xf3,0xc8,0xdd,0x33,0x2c,0x83,0x88,0x3,0xdb,0x78,0xb1,0x15,0x6f,0xda,0x9e,0x3a,0xbc,0x80,0x64,0x18,0x22,0x3b,0xa,0x72,0xd8,0x7f,0x35,0xf6,0xef,0xc7,0x46,0xed,0x17,0x1f,0xf7,0xb7,0x19,0xe8,0x86,0x5c,0x21,0x99,0x3f,0xc4,0x85,0x79,0x9,0xce,0x97,0x4b,0x1d,0x41,0xf4,0x13,0xcd,0xd2,0x7a,0x30,0xcf,0x7,0xd5,0x8e,0xb3,0x25,0x12,0x6c,0x63,0xa3,0x68,0x4f,0x49,0xd4,0x11,0x27,0x52,0xcc,0xa6,0xf9,0x0,0xd9,0x36,0x1b,0x98,0xe3,0xee,0xd7,0xd0,0x66,0x3e,0x1,0xff,0xec,0x7b,0x57,0x2e,0x43,0x9c,0xf2,0x96,0xb8,0xa4,0x65,0x5b,0x8a,0xc9,0xa1,0x77,0xc5,0x4e,0x42,0x4c,0xf0,0xe7,0xe2,0x95,0x1c,0x31,0x1a,0x7c,0xaf,0xa5,0x16,0x59,0xfe,0xfa,0x8d,0x58,0x76,0x2b,0xdc,0xc1,0xf8,0xea,0x6,0x29,0x67,0x51,0x3c,0x81,0x9b,0x9a,0x60,0x91,0x37,0xac,0x10,0xf1,0x50,0xb0,0x75,0xbf,0x90,0x2,0x14,0xb6,0xa0,0xbb,0x7d,0xab,0x69,0xbe,0x8,0xa7,0xe9,0x9f,0x61,0xe6,0xc2,0x47,0xf,0x93,0x5d,0x32,0x48,0xbd,0xe4,0x8c,0x1e,0x44,0xae,0x87,0xca,0x26,0xc,0x6b,0x92,0xb,0xe,0x6e,0x40,0xb4,0xe5,0x28,0xe1,0x4,0x5f,0x8f,0x62,0x24,0x20,0x54,0xc3,0xeb,0xd,0xd1,0x5e,0x7e,0x94,0x36,0x10,0x82,0xfd,0x2b,0x20,0x3b,0x90,0x71,0xb7,0x2c,0xf5,0x3f,0xd0,0x30,0x8f,0x13,0x42,0xc7,0xc8,0x3d,0xdd,0xb2,0x88,0x27,0xe9,0x3e,0xe1,0x66,0x69,0x1f,0x12,0x8b,0x8c,0xeb,0xc0,0x34,0x8e,0xee,0x9e,0xc4,0x64,0xc,0x4a,0xa6,0x2e,0x7,0x43,0x6b,0xa0,0xd4,0xde,0xfe,0x8d,0x51,0x61,0x84,0x65,0xa8,0xe2,0xa4,0xdf,0xf,0x81,0x7f,0xe6,0xbe,0xd7,0xae,0x6c,0xfb,0x9b,0x18,0x59,0xb6,0x57,0x50,0x63,0x6e,0x21,0xf7,0xa,0x49,0xc2,0xcc,0x45,0xce,0x72,0x16,0xc3,0x1c,0xe5,0xdb,0x38,0x24,0x96,0xd9,0x2f,0x25,0xd,0xd8,0x7e,0x7a,0x62,0x15,0x70,0x67,0x9a,0xfc,0x9c,0xb1,0xbc,0x1,0xe7,0xd1,0xe0,0x11,0x1b,0x1a,0x5c,0x41,0xf6,0xab,0x86,0xa9,0x78,0x6a,0xff,0xb5,0xf2,0x58,0x47,0xc6,0x76,0x6f,0x0,0xe4,0xba,0x3c,0xbb,0x8a,0x98,0xa2,0x19,0xbf,0xdc,0xa1,0xf9,0x89,0x44,0x5,0x9f,0x77,0x6d,0x97,0x68,0x6,0x37,0x99,0xb0,0x4f,0x52,0xfa,0xe,0x33,0x87,0x55,0xcb,0x9d,0x4e,0x17,0x93,0x4d,0xc1,0x74,0xa7,0xd2,0x54,0x91,0x79,0x80,0x4c,0x26,0xec,0xe3,0xa5,0x92,0xcf,0xc9,0x23,0xe8,0xf3,0x5f,0xaa,0x56,0xca,0xb4,0xbd,0x39,0x35,0x85,0x2d,0xea,0x32,0xad,0xaf,0x7c,0xa3,0xf1,0x28,0xd5,0xb,0xda,0x7b,0xd6,0x1d,0x22,0xf4,0x4b,0x46,0x4,0x75,0x5e,0xb9,0x2,0x7d,0xed,0x2a,0xf0,0xcd,0x14,0x29,0xb8,0x53,0x3a,0x60,0xc5,0x40,0x9,0xf8,0x31,0x83,0x5b,0x5a,0x1e,0x95,0xef,0x48,0x5d,0xd3,0x73,0x3,0x8,0xb3,0xac,0x8c,0x70,0x85,0x29,0xe3,0x67,0x6e,0x10,0x30,0xf7,0x5f,0xef,0xa6,0x75,0x77,0xe8,0xf,0xf2,0x2b,0x79,0xc,0xa1,0x0,0xd1,0x91,0x2e,0xf8,0xc7,0x84,0xaf,0xde,0x9c,0x37,0xa7,0xd8,0x63,0xce,0x17,0x2a,0xf0,0xe0,0x89,0x62,0xf3,0xd3,0x9a,0x1f,0xba,0x81,0x59,0xeb,0x22,0x35,0x4f,0xc4,0x80,0xa9,0x9,0x87,0x92,0x76,0x69,0xd2,0xd9,0x82,0x28,0x6f,0x25,0xb5,0xac,0x1c,0x9d,0xe6,0x60,0x3e,0xda,0x78,0x42,0x50,0x61,0x7b,0x6,0x65,0xc3,0xdf,0x9e,0x53,0x23,0x4d,0xb7,0xad,0x45,0x43,0xed,0xdc,0xb2,0x20,0x88,0x95,0x6a,0x8f,0x5d,0xe9,0xd4,0xcd,0x94,0x47,0x11,0xae,0x1b,0x97,0x49,0x4b,0x8e,0x8,0x7d,0xfc,0x96,0x5a,0xa3,0x48,0x7f,0x39,0x36,0x32,0xf9,0x13,0x15,0x64,0x3c,0xa5,0x5b,0x21,0xb6,0x74,0xd,0x6c,0x83,0xc2,0x41,0xb4,0xb9,0x8a,0x8d,0x93,0xd0,0x2d,0xfb,0x14,0x9f,0x16,0x18,0xc6,0x19,0xcc,0xa8,0xfe,0xe2,0x1,0x3f,0xff,0xf5,0x3,0x4c,0xa0,0xa4,0x2,0xd7,0xbd,0xaa,0xcf,0xb8,0x6b,0x46,0x26,0x40,0xb,0x3d,0xdb,0x66,0xc0,0xc1,0xcb,0x3a,0x71,0x2c,0x9b,0x86,0xb0,0xa2,0x73,0x5c,0x58,0xca,0xec,0x4e,0xe1,0xfa,0xf1,0x27,0xf6,0x6d,0xab,0x4a,0xea,0xa,0xe5,0x2f,0x1d,0x98,0xc9,0x55,0x68,0x7,0xe7,0x12,0xe4,0x33,0xfd,0x52,0xc5,0xb3,0xbc,0x3b,0x31,0x56,0x51,0xc8,0x34,0x54,0xee,0x1a,0xd6,0xbe,0x1e,0x44,0xdd,0xf4,0x7c,0x90,0xe,0x7a,0xb1,0x99,0x8b,0x57,0x24,0x4,0x72,0xbf,0x5e,0xbb,0xd5,0x5,0x7e,0x38,0x1,0xa7,0x1f,0x62,0x37,0x47,0xbb,0xfa,0xc9,0x21,0x29,0xd3,0xb8,0xd6,0x27,0x89,0xb,0x41,0xe6,0x4c,0x78,0xf9,0xd1,0xc8,0x5a,0xbe,0x82,0x4,0x34,0x5,0x1c,0x26,0x6c,0x19,0x2f,0xea,0x3e,0xc7,0x98,0xf2,0x5d,0x52,0x2c,0x1b,0x77,0x71,0x56,0x9d,0xf1,0xe,0x44,0xec,0x8d,0xb0,0xeb,0x39,0x23,0x75,0xa9,0xf0,0xf3,0x2d,0xca,0x7f,0x4f,0x1d,0x6b,0x96,0x64,0xb5,0x68,0xc5,0x9c,0xa3,0xf5,0x4a,0xba,0xf8,0xe0,0xcb,0xe1,0x4d,0xe8,0x14,0xa,0x74,0x87,0x3,0x3b,0x8b,0x54,0x93,0x13,0x8c,0xc2,0x11,0x8f,0x46,0xe5,0x3d,0xa0,0xe4,0x51,0x2b,0xe3,0xf6,0xcd,0x6d,0xb6,0xbd,0x12,0xd,0xbc,0x7,0x53,0xc3,0x4e,0x94,0xaa,0x73,0x6,0x97,0x84,0xed,0x7b,0xde,0xb7,0xfe,0xad,0x31,0x79,0xfc,0x83,0x76,0xc,0x63,0x99,0x36,0x80,0x57,0xd8,0x5f,0xa1,0xd7,0x88,0x2a,0x3c,0xae,0x95,0x43,0x85,0x9e,0xcf,0x2e,0x92,0x9,0x81,0x4b,0x8e,0x6e,0xd5,0xfd,0x6a,0x1e,0x40,0x60,0xef,0x33,0x3a,0xdf,0x16,0xdb,0x1a,0x5c,0xb1,0x61,0x35,0xac,0x55,0x32,0x8a,0x7e,0x50,0x30,0x7a,0x20,0xb2,0xda,0x18,0xf4,0xb9,0x90,0x49,0x9f,0xf7,0xb4,0x72,0x7c,0x70,0xfb,0xa8,0xcc,0xa2,0x7d,0x65,0x5b,0x9a,0x86,0xc1,0x3f,0x0,0x58,0x10,0x69,0x45,0xd2,0xa6,0x25,0x8,0xe7,0xee,0xe9,0xd0,0xdd,0xbf,0x2,0x6f,0x59,0xaf,0x5e,0xa4,0xa5,0xff,0xe2,0x15,0x48,0x17,0x38,0xd4,0xc6,0x67,0x28,0x9b,0x91,0x66,0xb3,0xc4,0xc0,0xab,0xdc,0xd9,0xce,0x42,0x24,0xf,0x22,0xf4,0x33,0xec,0x5c,0x76,0xa5,0xeb,0x74,0x73,0x8f,0x2a,0x86,0x64,0xe0,0x13,0x6d,0x2d,0x92,0xc4,0xfb,0xac,0x87,0x9f,0xdd,0xf1,0xc,0x7a,0x28,0xa2,0xf,0xd2,0x3,0x8a,0xe3,0xf0,0x61,0x99,0xd0,0xb9,0x1c,0xa4,0x34,0x60,0xdb,0x14,0xcd,0xf3,0x29,0xa,0xaa,0x91,0x84,0x6a,0x75,0xda,0xd1,0x5a,0x82,0x21,0xe8,0x4c,0x36,0x83,0xc7,0x63,0xe5,0xd9,0x3d,0x41,0x7b,0x62,0x53,0x2b,0x81,0x26,0x6c,0xaf,0xb6,0x9e,0x1f,0xb4,0x4e,0x46,0xae,0xee,0x40,0xb1,0xdf,0x5,0x78,0xc0,0x66,0x9d,0xdc,0x20,0x50,0x97,0xce,0x12,0x44,0x18,0xad,0x4a,0x94,0x8b,0x23,0x69,0x96,0x5e,0x8c,0xd7,0xea,0x7c,0x4b,0x35,0x3a,0xfa,0x31,0x16,0x10,0x8d,0x48,0x7e,0xb,0x95,0xff,0xa0,0x59,0x80,0x6f,0x42,0xc1,0xba,0xb7,0x8e,0x89,0x3f,0x67,0x58,0xa6,0xb5,0x22,0xe,0x77,0x1a,0xc5,0xab,0xcf,0xe1,0xfd,0x3c,0x2,0xd3,0x90,0xf8,0x2e,0x9c,0x17,0x1b,0x15,0xa9,0xbe,0xbb,0xcc,0x45,0x68,0x43,0x25,0xf6,0xfc,0x4f,0x0,0xa7,0xa3,0xd4,0x1,0x2f,0x72,0x85,0x98,0xa1,0xb3,0x5f,0x70,0x3e,0x8,0x65,0xd8,0xc2,0xc3,0x39,0xc8,0x6e,0xf5,0x49,0xa8,0x9,0xe9,0x2c,0xe6,0xc9,0x5b,0x4d,0xef,0xf9,0xe2,0x24,0xf2,0x30,0xe7,0x51,0xfe,0xb0,0xc6,0x38,0xbf,0x9b,0x1e,0x56,0xca,0x4,0x6b,0x11,0xe4,0xbd,0xd5,0x47,0x1d,0xf7,0xde,0x93,0x7f,0x55,0x32,0xcb,0x52,0x57,0x37,0x19,0xed,0xbc,0x71,0xb8,0x5d,0x6,0xd6,0x3b,0x7d,0x79,0xd,0x9a,0xb2,0x54,0x88,0x7,0x27,0x31,0x93,0xb5,0x27,0x58,0x8e,0x85,0x9e,0x35,0xd4,0x12,0x89,0x50,0x9a,0x75,0x95,0x2a,0xb6,0xe7,0x62,0x6d,0x98,0x78,0x17,0x2d,0x82,0x4c,0x9b,0x44,0xc3,0xcc,0xba,0xb7,0x2e,0x29,0x4e,0x65,0x91,0x2b,0x4b,0x3b,0x61,0xc1,0xa9,0xef,0x3,0x8b,0xa2,0xe6,0xce,0x5,0x71,0x7b,0x5b,0x28,0xf4,0xc4,0x21,0xc0,0xd,0x47,0x1,0x7a,0xaa,0x24,0xda,0x43,0x1b,0x72,0xb,0xc9,0x5e,0x3e,0xbd,0xfc,0x13,0xf2,0xf5,0xc6,0xcb,0x84,0x52,0xaf,0xec,0x67,0x69,0xe0,0x6b,0xd7,0xb3,0x66,0xb9,0x40,0x7e,0x9d,0x81,0x33,0x7c,0x8a,0x80,0xa8,0x7d,0xdb,0xdf,0xc7,0xb0,0xd5,0xc2,0x3f,0x59,0x39,0x14,0x19,0xa4,0x42,0x74,0x45,0xb4,0xbe,0xbf,0xf9,0xe4,0x53,0xe,0x23,0xc,0xdd,0xcf,0x5a,0x10,0x57,0xfd,0xe2,0x63,0xd3,0xca,0xa5,0x41,0x1f,0x99,0x1e,0x2f,0x3d,0x7,0xbc,0x1a,0x79,0x4,0x5c,0x2c,0xe1,0xa0,0x3a,0xd2,0xc8,0x32,0xcd,0xa3,0x92,0x3c,0x15,0xea,0xf7,0x5f,0xab,0x96,0x22,0xf0,0x6e,0x38,0xeb,0xb2,0x36,0xe8,0x64,0xd1,0x2,0x77,0xf1,0x34,0xdc,0x25,0xe9,0x83,0x49,0x46,0x0,0x37,0x6a,0x6c,0x86,0x4d,0x56,0xfa,0xf,0xf3,0x6f,0x11,0x18,0x9c,0x90,0x20,0x88,0x4f,0x97,0x8,0xa,0xd9,0x6,0x54,0x8d,0x70,0xae,0x7f,0xde,0x73,0xb8,0x87,0x51,0xee,0xe3,0xa1,0xd0,0xfb,0x1c,0xa7,0xd8,0x48,0x8f,0x55,0x68,0xb1,0x8c,0x1d,0xf6,0x9f,0xc5,0x60,0xe5,0xac,0x5d,0x94,0x26,0xfe,0xff,0xbb,0x30,0x4a,0xed,0xf8,0x76,0xd6,0xa6,0xad,0x16,0x9,0x66,0x9a,0x6f,0xc3,0x9,0x8d,0x84,0xfa,0xda,0x1d,0xb5,0x5,0x4c,0x9f,0x9d,0x2,0xe5,0x18,0xc1,0x93,0xe6,0x4b,0xea,0x3b,0x7b,0xc4,0x12,0x2d,0x6e,0x45,0x34,0x76,0xdd,0x4d,0x32,0x89,0x24,0xfd,0xc0,0x1a,0xa,0x63,0x88,0x19,0x39,0x70,0xf5,0x50,0x6b,0xb3,0x1,0xc8,0xdf,0xa5,0x2e,0x6a,0x43,0xe3,0x6d,0x78,0x9c,0x83,0x38,0x33,0x68,0xc2,0x85,0xcf,0x5f,0x46,0xf6,0x77,0xc,0x8a,0xd4,0x30,0x92,0xa8,0xba,0x8b,0x91,0xec,0x8f,0x29,0x35,0x74,0xb9,0xc9,0xa7,0x5d,0x47,0xaf,0xa9,0x7,0x36,0x58,0xca,0x62,0x7f,0x80,0x65,0xb7,0x3,0x3e,0x27,0x7e,0xad,0xfb,0x44,0xf1,0x7d,0xa3,0xa1,0x64,0xe2,0x97,0x16,0x7c,0xb0,0x49,0xa2,0x95,0xd3,0xdc,0xd8,0x13,0xf9,0xff,0x8e,0xd6,0x4f,0xb1,0xcb,0x5c,0x9e,0xe7,0x86,0x69,0x28,0xab,0x5e,0x53,0x60,0x67,0x79,0x3a,0xc7,0x11,0xfe,0x75,0xfc,0xf2,0x2c,0xf3,0x26,0x42,0x14,0x8,0xeb,0xd5,0x15,0x1f,0xe9,0xa6,0x4a,0x4e,0xe8,0x3d,0x57,0x40,0x25,0x52,0x81,0xac,0xcc,0xaa,0xe1,0xd7,0x31,0x8c,0x2a,0x2b,0x21,0xd0,0x9b,0xc6,0x71,0x6c,0x5a,0x48,0x99,0xb6,0xb2,0x20,0x6,0xa4,0xb,0x10,0x1b,0xcd,0x1c,0x87,0x41,0xa0,0x0,0xe0,0xf,0xc5,0xf7,0x72,0x23,0xbf,0x82,0xed,0xd,0xf8,0xe,0xd9,0x17,0xb8,0x2f,0x59,0x56,0xd1,0xdb,0xbc,0xbb,0x22,0xde,0xbe,0x4,0xf0,0x3c,0x54,0xf4,0xae,0x37,0x1e,0x96,0x7a,0xe4,0x90,0x5b,0x73,0x61,0xbd,0xce,0xee,0x98,0x55,0xb4,0x51,0x3f,0xef,0x94,0xd2,0x2b,0x8d,0x35,0x48,0x1d,0x6d,0x91,0xd0,0xe3,0xb,0x3,0xf9,0x92,0xfc,0xd,0xa3,0x21,0x6b,0xcc,0x66,0x52,0xd3,0xfb,0xe2,0x70,0x94,0xa8,0x2e,0x1e,0x2f,0x36,0xc,0x46,0x33,0x5,0xc0,0x14,0xed,0xb2,0xd8,0x77,0x78,0x6,0x31,0x5d,0x5b,0x7c,0xb7,0xdb,0x24,0x6e,0xc6,0xa7,0x9a,0xc1,0x13,0x9,0x5f,0x83,0xda,0xd9,0x7,0xe0,0x55,0x65,0x37,0x41,0xbc,0x4e,0x9f,0x42,0xef,0xb6,0x89,0xdf,0x60,0x90,0xd2,0xca,0xe1,0xcb,0x67,0xc2,0x3e,0x20,0x5e,0xad,0x29,0x11,0xa1,0x7e,0xb9,0x39,0xa6,0xe8,0x3b,0xa5,0x6c,0xcf,0x17,0x8a,0xce,0x7b,0x1,0xc9,0xdc,0xe7,0x47,0x9c,0x97,0x38,0x27,0x96,0x2d,0x79,0xe9,0x64,0xbe,0x80,0x59,0x2c,0xbd,0xae,0xc7,0x51,0xf4,0x9d,0xd4,0x87,0x1b,0x53,0xd6,0xa9,0x5c,0x26,0x49,0xb3,0x1c,0xaa,0x7d,0xf2,0x75,0x8b,0xfd,0xa2,0x0,0x16,0x84,0xbf,0x69,0xaf,0xb4,0xe5,0x4,0xb8,0x23,0xab,0x61,0xa4,0x44,0xff,0xd7,0x40,0x34,0x6a,0x4a,0xc5,0x19,0x10,0xf5,0x3c,0xf1,0x30,0x76,0x9b,0x4b,0x1f,0x86,0x7f,0x18,0xa0,0x54,0x7a,0x1a,0x50,0xa,0x98,0xf0,0x32,0xde,0x93,0xba,0x63,0xb5,0xdd,0x9e,0x58,0x56,0x5a,0xd1,0x82,0xe6,0x88,0x57,0x4f,0x71,0xb0,0xac,0xeb,0x15,0x2a,0x72,0x3a,0x43,0x6f,0xf8,0x8c,0xf,0x22,0xcd,0xc4,0xc3,0xfa,0xf7,0x95,0x28,0x45,0x73,0x85,0x74,0x8e,0x8f,0xd5,0xc8,0x3f,0x62,0x3d,0x12,0xfe,0xec,0x4d,0x2,0xb1,0xbb,0x4c,0x99,0xee,0xea,0x81,0xf6,0xf3,0xe4,0x68,0xe,0x25,0x8,0xfa,0x3d,0xe2,0x52,0x78,0xab,0xe5,0x7a,0x7d,0x81,0x24,0x88,0x6a,0xee,0x1d,0x63,0x23,0x9c,0xca,0xf5,0xa2,0x89,0x91,0xd3,0xff,0x2,0x74,0x26,0xac,0x1,0xdc,0xd,0x84,0xed,0xfe,0x6f,0x97,0xde,0xb7,0x12,0xaa,0x3a,0x6e,0xd5,0x1a,0xc3,0xfd,0x27,0x4,0xa4,0x9f,0x8a,0x64,0x7b,0xd4,0xdf,0x54,0x8c,0x2f,0xe6,0x42,0x38,0x8d,0xc9,0x6d,0xeb,0xd7,0x33,0x4f,0x75,0x6c,0x5d,0x25,0x8f,0x28,0x62,0xa1,0xb8,0x90,0x11,0xba,0x40,0x48,0xa0,0xe0,0x4e,0xbf,0xd1,0xb,0x76,0xce,0x68,0x93,0xd2,0x2e,0x5e,0x99,0xc0,0x1c,0x4a,0x16,0xa3,0x44,0x9a,0x85,0x2d,0x67,0x98,0x50,0x82,0xd9,0xe4,0x72,0x45,0x3b,0x34,0xf4,0x3f,0x18,0x1e,0x83,0x46,0x70,0x5,0x9b,0xf1,0xae,0x57,0x8e,0x61,0x4c,0xcf,0xb4,0xb9,0x80,0x87,0x31,0x69,0x56,0xa8,0xbb,0x2c,0x0,0x79,0x14,0xcb,0xa5,0xc1,0xef,0xf3,0x32,0xc,0xdd,0x9e,0xf6,0x20,0x92,0x19,0x15,0x1b,0xa7,0xb0,0xb5,0xc2,0x4b,0x66,0x4d,0x2b,0xf8,0xf2,0x41,0xe,0xa9,0xad,0xda,0xf,0x21,0x7c,0x8b,0x96,0xaf,0xbd,0x51,0x7e,0x30,0x6,0x6b,0xd6,0xcc,0xcd,0x37,0xc6,0x60,0xfb,0x47,0xa6,0x7,0xe7,0x22,0xe8,0xc7,0x55,0x43,0xe1,0xf7,0xec,0x2a,0xfc,0x3e,0xe9,0x5f,0xf0,0xbe,0xc8,0x36,0xb1,0x95,0x10,0x58,0xc4,0xa,0x65,0x1f,0xea,0xb3,0xdb,0x49,0x13,0xf9,0xd0,0x9d,0x71,0x5b,0x3c,0xc5,0x5c,0x59,0x39,0x17,0xe3,0xb2,0x7f,0xb6,0x53,0x8,0xd8,0x35,0x73,0x77,0x3,0x94,0xbc,0x5a,0x86,0x9,0x29,0x3f,0x9d,0xbb,0x29,0x56,0x80,0x8b,0x90,0x3b,0xda,0x1c,0x87,0x5e,0x94,0x7b,0x9b,0x24,0xb8,0xe9,0x6c,0x63,0x96,0x76,0x19,0x23,0x8c,0x42,0x95,0x4a,0xcd,0xc2,0xb4,0xb9,0x20,0x27,0x40,0x6b,0x9f,0x25,0x45,0x35,0x6f,0xcf,0xa7,0xe1,0xd,0x85,0xac,0xe8,0xc0,0xb,0x7f,0x75,0x55,0x26,0xfa,0xca,0x2f,0xce,0x3,0x49,0xf,0x74,0xa4,0x2a,0xd4,0x4d,0x15,0x7c,0x5,0xc7,0x50,0x30,0xb3,0xf2,0x1d,0xfc,0xfb,0xc8,0xc5,0x8a,0x5c,0xa1,0xe2,0x69,0x67,0xee,0x65,0xd9,0xbd,0x68,0xb7,0x4e,0x70,0x93,0x8f,0x3d,0x72,0x84,0x8e,0xa6,0x73,0xd5,0xd1,0xc9,0xbe,0xdb,0xcc,0x31,0x57,0x37,0x1a,0x17,0xaa,0x4c,0x7a,0x4b,0xba,0xb0,0xb1,0xf7,0xea,0x5d,0x0,0x2d,0x2,0xd3,0xc1,0x54,0x1e,0x59,0xf3,0xec,0x6d,0xdd,0xc4,0xab,0x4f,0x11,0x97,0x10,0x21,0x33,0x9,0xb2,0x14,0x77,0xa,0x52,0x22,0xef,0xae,0x34,0xdc,0xc6,0x3c,0xc3,0xad,0x9c,0x32,0x1b,0xe4,0xf9,0x51,0xa5,0x98,0x2c,0xfe,0x60,0x36,0xe5,0xbc,0x38,0xe6,0x6a,0xdf,0xc,0x79,0xff,0x3a,0xd2,0x2b,0xe7,0x8d,0x47,0x48,0xe,0x39,0x64,0x62,0x88,0x43,0x58,0xf4,0x1,0xfd,0x61,0x1f,0x16,0x92,0x9e,0x2e,0x86,0x41,0x99,0x6,0x4,0xd7,0x8,0x5a,0x83,0x7e,0xa0,0x71,0xd0,0x7d,0xb6,0x89,0x5f,0xe0,0xed,0xaf,0xde,0xf5,0x12,0xa9,0xd6,0x46,0x81,0x5b,0x66,0xbf,0x82,0x13,0xf8,0x91,0xcb,0x6e,0xeb,0xa2,0x53,0x9a,0x28,0xf0,0xf1,0xb5,0x3e,0x44,0xe3,0xf6,0x78,0xd8,0xa8,0xa3,0x18,0x7,0xef,0x13,0xe6,0x4a,0x80,0x4,0xd,0x73,0x53,0x94,0x3c,0x8c,0xc5,0x16,0x14,0x8b,0x6c,0x91,0x48,0x1a,0x6f,0xc2,0x63,0xb2,0xf2,0x4d,0x9b,0xa4,0xe7,0xcc,0xbd,0xff,0x54,0xc4,0xbb,0x0,0xad,0x74,0x49,0x93,0x83,0xea,0x1,0x90,0xb0,0xf9,0x7c,0xd9,0xe2,0x3a,0x88,0x41,0x56,0x2c,0xa7,0xe3,0xca,0x6a,0xe4,0xf1,0x15,0xa,0xb1,0xba,0xe1,0x4b,0xc,0x46,0xd6,0xcf,0x7f,0xfe,0x85,0x3,0x5d,0xb9,0x1b,0x21,0x33,0x2,0x18,0x65,0x6,0xa0,0xbc,0xfd,0x30,0x40,0x2e,0xd4,0xce,0x26,0x20,0x8e,0xbf,0xd1,0x43,0xeb,0xf6,0x9,0xec,0x3e,0x8a,0xb7,0xae,0xf7,0x24,0x72,0xcd,0x78,0xf4,0x2a,0x28,0xed,0x6b,0x1e,0x9f,0xf5,0x39,0xc0,0x2b,0x1c,0x5a,0x55,0x51,0x9a,0x70,0x76,0x7,0x5f,0xc6,0x38,0x42,0xd5,0x17,0x6e,0xf,0xe0,0xa1,0x22,0xd7,0xda,0xe9,0xee,0xf0,0xb3,0x4e,0x98,0x77,0xfc,0x75,0x7b,0xa5,0x7a,0xaf,0xcb,0x9d,0x81,0x62,0x5c,0x9c,0x96,0x60,0x2f,0xc3,0xc7,0x61,0xb4,0xde,0xc9,0xac,0xdb,0x8,0x25,0x45,0x23,0x68,0x5e,0xb8,0x5,0xa3,0xa2,0xa8,0x59,0x12,0x4f,0xf8,0xe5,0xd3,0xc1,0x10,0x3f,0x3b,0xa9,0x8f,0x2d,0x82,0x99,0x92,0x44,0x95,0xe,0xc8,0x29,0x89,0x69,0x86,0x4c,0x7e,0xfb,0xaa,0x36,0xb,0x64,0x84,0x71,0x87,0x50,0x9e,0x31,0xa6,0xd0,0xdf,0x58,0x52,0x35,0x32,0xab,0x57,0x37,0x8d,0x79,0xb5,0xdd,0x7d,0x27,0xbe,0x97,0x1f,0xf3,0x6d,0x19,0xd2,0xfa,0xe8,0x34,0x47,0x67,0x11,0xdc,0x3d,0xd8,0xb6,0x66,0x1d,0x5b,0x89,0x2f,0x97,0xea,0xbf,0xcf,0x33,0x72,0x41,0xa9,0xa1,0x5b,0x30,0x5e,0xaf,0x1,0x83,0xc9,0x6e,0xc4,0xf0,0x71,0x59,0x40,0xd2,0x36,0xa,0x8c,0xbc,0x8d,0x94,0xae,0xe4,0x91,0xa7,0x62,0xb6,0x4f,0x10,0x7a,0xd5,0xda,0xa4,0x93,0xff,0xf9,0xde,0x15,0x79,0x86,0xcc,0x64,0x5,0x38,0x63,0xb1,0xab,0xfd,0x21,0x78,0x7b,0xa5,0x42,0xf7,0xc7,0x95,0xe3,0x1e,0xec,0x3d,0xe0,0x4d,0x14,0x2b,0x7d,0xc2,0x32,0x70,0x68,0x43,0x69,0xc5,0x60,0x9c,0x82,0xfc,0xf,0x8b,0xb3,0x3,0xdc,0x1b,0x9b,0x4,0x4a,0x99,0x7,0xce,0x6d,0xb5,0x28,0x6c,0xd9,0xa3,0x6b,0x7e,0x45,0xe5,0x3e,0x35,0x9a,0x85,0x34,0x8f,0xdb,0x4b,0xc6,0x1c,0x22,0xfb,0x8e,0x1f,0xc,0x65,0xf3,0x56,0x3f,0x76,0x25,0xb9,0xf1,0x74,0xb,0xfe,0x84,0xeb,0x11,0xbe,0x8,0xdf,0x50,0xd7,0x29,0x5f,0x0,0xa2,0xb4,0x26,0x1d,0xcb,0xd,0x16,0x47,0xa6,0x1a,0x81,0x9,0xc3,0x6,0xe6,0x5d,0x75,0xe2,0x96,0xc8,0xe8,0x67,0xbb,0xb2,0x57,0x9e,0x53,0x92,0xd4,0x39,0xe9,0xbd,0x24,0xdd,0xba,0x2,0xf6,0xd8,0xb8,0xf2,0xa8,0x3a,0x52,0x90,0x7c,0x31,0x18,0xc1,0x17,0x7f,0x3c,0xfa,0xf4,0xf8,0x73,0x20,0x44,0x2a,0xf5,0xed,0xd3,0x12,0xe,0x49,0xb7,0x88,0xd0,0x98,0xe1,0xcd,0x5a,0x2e,0xad,0x80,0x6f,0x66,0x61,0x58,0x55,0x37,0x8a,0xe7,0xd1,0x27,0xd6,0x2c,0x2d,0x77,0x6a,0x9d,0xc0,0x9f,0xb0,0x5c,0x4e,0xef,0xa0,0x13,0x19,0xee,0x3b,0x4c,0x48,0x23,0x54,0x51,0x46,0xca,0xac,0x87,0xaa,0x76,0xb1,0x6e,0xde,0xf4,0x27,0x69,0xf6,0xf1,0xd,0xa8,0x4,0xe6,0x62,0x91,0xef,0xaf,0x10,0x46,0x79,0x2e,0x5,0x1d,0x5f,0x73,0x8e,0xf8,0xaa,0x20,0x8d,0x50,0x81,0x8,0x61,0x72,0xe3,0x1b,0x52,0x3b,0x9e,0x26,0xb6,0xe2,0x59,0x96,0x4f,0x71,0xab,0x88,0x28,0x13,0x6,0xe8,0xf7,0x58,0x53,0xd8,0x0,0xa3,0x6a,0xce,0xb4,0x1,0x45,0xe1,0x67,0x5b,0xbf,0xc3,0xf9,0xe0,0xd1,0xa9,0x3,0xa4,0xee,0x2d,0x34,0x1c,0x9d,0x36,0xcc,0xc4,0x2c,0x6c,0xc2,0x33,0x5d,0x87,0xfa,0x42,0xe4,0x1f,0x5e,0xa2,0xd2,0x15,0x4c,0x90,0xc6,0x9a,0x2f,0xc8,0x16,0x9,0xa1,0xeb,0x14,0xdc,0xe,0x55,0x68,0xfe,0xc9,0xb7,0xb8,0x78,0xb3,0x94,0x92,0xf,0xca,0xfc,0x89,0x17,0x7d,0x22,0xdb,0x2,0xed,0xc0,0x43,0x38,0x35,0xc,0xb,0xbd,0xe5,0xda,0x24,0x37,0xa0,0x8c,0xf5,0x98,0x47,0x29,0x4d,0x63,0x7f,0xbe,0x80,0x51,0x12,0x7a,0xac,0x1e,0x95,0x99,0x97,0x2b,0x3c,0x39,0x4e,0xc7,0xea,0xc1,0xa7,0x74,0x7e,0xcd,0x82,0x25,0x21,0x56,0x83,0xad,0xf0,0x7,0x1a,0x23,0x31,0xdd,0xf2,0xbc,0x8a,0xe7,0x5a,0x40,0x41,0xbb,0x4a,0xec,0x77,0xcb,0x2a,0x8b,0x6b,0xae,0x64,0x4b,0xd9,0xcf,0x6d,0x7b,0x60,0xa6,0x70,0xb2,0x65,0xd3,0x7c,0x32,0x44,0xba,0x3d,0x19,0x9c,0xd4,0x48,0x86,0xe9,0x93,0x66,0x3f,0x57,0xc5,0x9f,0x75,0x5c,0x11,0xfd,0xd7,0xb0,0x49,0xd0,0xd5,0xb5,0x9b,0x6f,0x3e,0xf3,0x3a,0xdf,0x84,0x54,0xb9,0xff,0xfb,0x8f,0x18,0x30,0xd6,0xa,0x85,0xa5,0x17,0xb5,0x93,0x1,0x7e,0xa8,0xa3,0xb8,0x13,0xf2,0x34,0xaf,0x76,0xbc,0x53,0xb3,0xc,0x90,0xc1,0x44,0x4b,0xbe,0x5e,0x31,0xb,0xa4,0x6a,0xbd,0x62,0xe5,0xea,0x9c,0x91,0x8,0xf,0x68,0x43,0xb7,0xd,0x6d,0x1d,0x47,0xe7,0x8f,0xc9,0x25,0xad,0x84,0xc0,0xe8,0x23,0x57,0x5d,0x7d,0xe,0xd2,0xe2,0x7,0xe6,0x2b,0x61,0x27,0x5c,0x8c,0x2,0xfc,0x65,0x3d,0x54,0x2d,0xef,0x78,0x18,0x9b,0xda,0x35,0xd4,0xd3,0xe0,0xed,0xa2,0x74,0x89,0xca,0x41,0x4f,0xc6,0x4d,0xf1,0x95,0x40,0x9f,0x66,0x58,0xbb,0xa7,0x15,0x5a,0xac,0xa6,0x8e,0x5b,0xfd,0xf9,0xe1,0x96,0xf3,0xe4,0x19,0x7f,0x1f,0x32,0x3f,0x82,0x64,0x52,0x63,0x92,0x98,0x99,0xdf,0xc2,0x75,0x28,0x5,0x2a,0xfb,0xe9,0x7c,0x36,0x71,0xdb,0xc4,0x45,0xf5,0xec,0x83,0x67,0x39,0xbf,0x38,0x9,0x1b,0x21,0x9a,0x3c,0x5f,0x22,0x7a,0xa,0xc7,0x86,0x1c,0xf4,0xee,0x14,0xeb,0x85,0xb4,0x1a,0x33,0xcc,0xd1,0x79,0x8d,0xb0,0x4,0xd6,0x48,0x1e,0xcd,0x94,0x10,0xce,0x42,0xf7,0x24,0x51,0xd7,0x12,0xfa,0x3,0xcf,0xa5,0x6f,0x60,0x26,0x11,0x4c,0x4a,0xa0,0x6b,0x70,0xdc,0x29,0xd5,0x49,0x37,0x3e,0xba,0xb6,0x6,0xae,0x69,0xb1,0x2e,0x2c,0xff,0x20,0x72,0xab,0x56,0x88,0x59,0xf8,0x55,0x9e,0xa1,0x77,0xc8,0xc5,0x87,0xf6,0xdd,0x3a,0x81,0xfe,0x6e,0xa9,0x73,0x4e,0x97,0xaa,0x3b,0xd0,0xb9,0xe3,0x46,0xc3,0x8a,0x7b,0xb2,0x0,0xd8,0xd9,0x9d,0x16,0x6c,0xcb,0xde,0x50,0xf0,0x80,0x8b,0x30,0x2f,0x34,0xc8,0x3d,0x91,0x5b,0xdf,0xd6,0xa8,0x88,0x4f,0xe7,0x57,0x1e,0xcd,0xcf,0x50,0xb7,0x4a,0x93,0xc1,0xb4,0x19,0xb8,0x69,0x29,0x96,0x40,0x7f,0x3c,0x17,0x66,0x24,0x8f,0x1f,0x60,0xdb,0x76,0xaf,0x92,0x48,0x58,0x31,0xda,0x4b,0x6b,0x22,0xa7,0x2,0x39,0xe1,0x53,0x9a,0x8d,0xf7,0x7c,0x38,0x11,0xb1,0x3f,0x2a,0xce,0xd1,0x6a,0x61,0x3a,0x90,0xd7,0x9d,0xd,0x14,0xa4,0x25,0x5e,0xd8,0x86,0x62,0xc0,0xfa,0xe8,0xd9,0xc3,0xbe,0xdd,0x7b,0x67,0x26,0xeb,0x9b,0xf5,0xf,0x15,0xfd,0xfb,0x55,0x64,0xa,0x98,0x30,0x2d,0xd2,0x37,0xe5,0x51,0x6c,0x75,0x2c,0xff,0xa9,0x16,0xa3,0x2f,0xf1,0xf3,0x36,0xb0,0xc5,0x44,0x2e,0xe2,0x1b,0xf0,0xc7,0x81,0x8e,0x8a,0x41,0xab,0xad,0xdc,0x84,0x1d,0xe3,0x99,0xe,0xcc,0xb5,0xd4,0x3b,0x7a,0xf9,0xc,0x1,0x32,0x35,0x2b,0x68,0x95,0x43,0xac,0x27,0xae,0xa0,0x7e,0xa1,0x74,0x10,0x46,0x5a,0xb9,0x87,0x47,0x4d,0xbb,0xf4,0x18,0x1c,0xba,0x6f,0x5,0x12,0x77,0x0,0xd3,0xfe,0x9e,0xf8,0xb3,0x85,0x63,0xde,0x78,0x79,0x73,0x82,0xc9,0x94,0x23,0x3e,0x8,0x1a,0xcb,0xe4,0xe0,0x72,0x54,0xf6,0x59,0x42,0x49,0x9f,0x4e,0xd5,0x13,0xf2,0x52,0xb2,0x5d,0x97,0xa5,0x20,0x71,0xed,0xd0,0xbf,0x5f,0xaa,0x5c,0x8b,0x45,0xea,0x7d,0xb,0x4,0x83,0x89,0xee,0xe9,0x70,0x8c,0xec,0x56,0xa2,0x6e,0x6,0xa6,0xfc,0x65,0x4c,0xc4,0x28,0xb6,0xc2,0x9,0x21,0x33,0xef,0x9c,0xbc,0xca,0x7,0xe6,0x3,0x6d,0xbd,0xc6,0x80,0x5b,0xfd,0x45,0x38,0x6d,0x1d,0xe1,0xa0,0x93,0x7b,0x73,0x89,0xe2,0x8c,0x7d,0xd3,0x51,0x1b,0xbc,0x16,0x22,0xa3,0x8b,0x92,0x0,0xe4,0xd8,0x5e,0x6e,0x5f,0x46,0x7c,0x36,0x43,0x75,0xb0,0x64,0x9d,0xc2,0xa8,0x7,0x8,0x76,0x41,0x2d,0x2b,0xc,0xc7,0xab,0x54,0x1e,0xb6,0xd7,0xea,0xb1,0x63,0x79,0x2f,0xf3,0xaa,0xa9,0x77,0x90,0x25,0x15,0x47,0x31,0xcc,0x3e,0xef,0x32,0x9f,0xc6,0xf9,0xaf,0x10,0xe0,0xa2,0xba,0x91,0xbb,0x17,0xb2,0x4e,0x50,0x2e,0xdd,0x59,0x61,0xd1,0xe,0xc9,0x49,0xd6,0x98,0x4b,0xd5,0x1c,0xbf,0x67,0xfa,0xbe,0xb,0x71,0xb9,0xac,0x97,0x37,0xec,0xe7,0x48,0x57,0xe6,0x5d,0x9,0x99,0x14,0xce,0xf0,0x29,0x5c,0xcd,0xde,0xb7,0x21,0x84,0xed,0xa4,0xf7,0x6b,0x23,0xa6,0xd9,0x2c,0x56,0x39,0xc3,0x6c,0xda,0xd,0x82,0x5,0xfb,0x8d,0xd2,0x70,0x66,0xf4,0xcf,0x19,0xdf,0xc4,0x95,0x74,0xc8,0x53,0xdb,0x11,0xd4,0x34,0x8f,0xa7,0x30,0x44,0x1a,0x3a,0xb5,0x69,0x60,0x85,0x4c,0x81,0x40,0x6,0xeb,0x3b,0x6f,0xf6,0xf,0x68,0xd0,0x24,0xa,0x6a,0x20,0x7a,0xe8,0x80,0x42,0xae,0xe3,0xca,0x13,0xc5,0xad,0xee,0x28,0x26,0x2a,0xa1,0xf2,0x96,0xf8,0x27,0x3f,0x1,0xc0,0xdc,0x9b,0x65,0x5a,0x2,0x4a,0x33,0x1f,0x88,0xfc,0x7f,0x52,0xbd,0xb4,0xb3,0x8a,0x87,0xe5,0x58,0x35,0x3,0xf5,0x4,0xfe,0xff,0xa5,0xb8,0x4f,0x12,0x4d,0x62,0x8e,0x9c,0x3d,0x72,0xc1,0xcb,0x3c,0xe9,0x9e,0x9a,0xf1,0x86,0x83,0x94,0x18,0x7e,0x55,0x78,0xc0,0x7,0xd8,0x68,0x42,0x91,0xdf,0x40,0x47,0xbb,0x1e,0xb2,0x50,0xd4,0x27,0x59,0x19,0xa6,0xf0,0xcf,0x98,0xb3,0xab,0xe9,0xc5,0x38,0x4e,0x1c,0x96,0x3b,0xe6,0x37,0xbe,0xd7,0xc4,0x55,0xad,0xe4,0x8d,0x28,0x90,0x0,0x54,0xef,0x20,0xf9,0xc7,0x1d,0x3e,0x9e,0xa5,0xb0,0x5e,0x41,0xee,0xe5,0x6e,0xb6,0x15,0xdc,0x78,0x2,0xb7,0xf3,0x57,0xd1,0xed,0x9,0x75,0x4f,0x56,0x67,0x1f,0xb5,0x12,0x58,0x9b,0x82,0xaa,0x2b,0x80,0x7a,0x72,0x9a,0xda,0x74,0x85,0xeb,0x31,0x4c,0xf4,0x52,0xa9,0xe8,0x14,0x64,0xa3,0xfa,0x26,0x70,0x2c,0x99,0x7e,0xa0,0xbf,0x17,0x5d,0xa2,0x6a,0xb8,0xe3,0xde,0x48,0x7f,0x1,0xe,0xce,0x5,0x22,0x24,0xb9,0x7c,0x4a,0x3f,0xa1,0xcb,0x94,0x6d,0xb4,0x5b,0x76,0xf5,0x8e,0x83,0xba,0xbd,0xb,0x53,0x6c,0x92,0x81,0x16,0x3a,0x43,0x2e,0xf1,0x9f,0xfb,0xd5,0xc9,0x8,0x36,0xe7,0xa4,0xcc,0x1a,0xa8,0x23,0x2f,0x21,0x9d,0x8a,0x8f,0xf8,0x71,0x5c,0x77,0x11,0xc2,0xc8,0x7b,0x34,0x93,0x97,0xe0,0x35,0x1b,0x46,0xb1,0xac,0x95,0x87,0x6b,0x44,0xa,0x3c,0x51,0xec,0xf6,0xf7,0xd,0xfc,0x5a,0xc1,0x7d,0x9c,0x3d,0xdd,0x18,0xd2,0xfd,0x6f,0x79,0xdb,0xcd,0xd6,0x10,0xc6,0x4,0xd3,0x65,0xca,0x84,0xf2,0xc,0x8b,0xaf,0x2a,0x62,0xfe,0x30,0x5f,0x25,0xd0,0x89,0xe1,0x73,0x29,0xc3,0xea,0xa7,0x4b,0x61,0x6,0xff,0x66,0x63,0x3,0x2d,0xd9,0x88,0x45,0x8c,0x69,0x32,0xe2,0xf,0x49,0x4d,0x39,0xae,0x86,0x60,0xbc,0x33,0x13,0xc8,0x6a,0x4c,0xde,0xa1,0x77,0x7c,0x67,0xcc,0x2d,0xeb,0x70,0xa9,0x63,0x8c,0x6c,0xd3,0x4f,0x1e,0x9b,0x94,0x61,0x81,0xee,0xd4,0x7b,0xb5,0x62,0xbd,0x3a,0x35,0x43,0x4e,0xd7,0xd0,0xb7,0x9c,0x68,0xd2,0xb2,0xc2,0x98,0x38,0x50,0x16,0xfa,0x72,0x5b,0x1f,0x37,0xfc,0x88,0x82,0xa2,0xd1,0xd,0x3d,0xd8,0x39,0xf4,0xbe,0xf8,0x83,0x53,0xdd,0x23,0xba,0xe2,0x8b,0xf2,0x30,0xa7,0xc7,0x44,0x5,0xea,0xb,0xc,0x3f,0x32,0x7d,0xab,0x56,0x15,0x9e,0x90,0x19,0x92,0x2e,0x4a,0x9f,0x40,0xb9,0x87,0x64,0x78,0xca,0x85,0x73,0x79,0x51,0x84,0x22,0x26,0x3e,0x49,0x2c,0x3b,0xc6,0xa0,0xc0,0xed,0xe0,0x5d,0xbb,0x8d,0xbc,0x4d,0x47,0x46,0x0,0x1d,0xaa,0xf7,0xda,0xf5,0x24,0x36,0xa3,0xe9,0xae,0x4,0x1b,0x9a,0x2a,0x33,0x5c,0xb8,0xe6,0x60,0xe7,0xd6,0xc4,0xfe,0x45,0xe3,0x80,0xfd,0xa5,0xd5,0x18,0x59,0xc3,0x2b,0x31,0xcb,0x34,0x5a,0x6b,0xc5,0xec,0x13,0xe,0xa6,0x52,0x6f,0xdb,0x9,0x97,0xc1,0x12,0x4b,0xcf,0x11,0x9d,0x28,0xfb,0x8e,0x8,0xcd,0x25,0xdc,0x10,0x7a,0xb0,0xbf,0xf9,0xce,0x93,0x95,0x7f,0xb4,0xaf,0x3,0xf6,0xa,0x96,0xe8,0xe1,0x65,0x69,0xd9,0x71,0xb6,0x6e,0xf1,0xf3,0x20,0xff,0xad,0x74,0x89,0x57,0x86,0x27,0x8a,0x41,0x7e,0xa8,0x17,0x1a,0x58,0x29,0x2,0xe5,0x5e,0x21,0xb1,0x76,0xac,0x91,0x48,0x75,0xe4,0xf,0x66,0x3c,0x99,0x1c,0x55,0xa4,0x6d,0xdf,0x7,0x6,0x42,0xc9,0xb3,0x14,0x1,0x8f,0x2f,0x5f,0x54,0xef,0xf0,0xee,0x12,0xe7,0x4b,0x81,0x5,0xc,0x72,0x52,0x95,0x3d,0x8d,0xc4,0x17,0x15,0x8a,0x6d,0x90,0x49,0x1b,0x6e,0xc3,0x62,0xb3,0xf3,0x4c,0x9a,0xa5,0xe6,0xcd,0xbc,0xfe,0x55,0xc5,0xba,0x1,0xac,0x75,0x48,0x92,0x82,0xeb,0x0,0x91,0xb1,0xf8,0x7d,0xd8,0xe3,0x3b,0x89,0x40,0x57,0x2d,0xa6,0xe2,0xcb,0x6b,0xe5,0xf0,0x14,0xb,0xb0,0xbb,0xe0,0x4a,0xd,0x47,0xd7,0xce,0x7e,0xff,0x84,0x2,0x5c,0xb8,0x1a,0x20,0x32,0x3,0x19,0x64,0x7,0xa1,0xbd,0xfc,0x31,0x41,0x2f,0xd5,0xcf,0x27,0x21,0x8f,0xbe,0xd0,0x42,0xea,0xf7,0x8,0xed,0x3f,0x8b,0xb6,0xaf,0xf6,0x25,0x73,0xcc,0x79,0xf5,0x2b,0x29,0xec,0x6a,0x1f,0x9e,0xf4,0x38,0xc1,0x2a,0x1d,0x5b,0x54,0x50,0x9b,0x71,0x77,0x6,0x5e,0xc7,0x39,0x43,0xd4,0x16,0x6f,0xe,0xe1,0xa0,0x23,0xd6,0xdb,0xe8,0xef,0xf1,0xb2,0x4f,0x99,0x76,0xfd,0x74,0x7a,0xa4,0x7b,0xae,0xca,0x9c,0x80,0x63,0x5d,0x9d,0x97,0x61,0x2e,0xc2,0xc6,0x60,0xb5,0xdf,0xc8,0xad,0xda,0x9,0x24,0x44,0x22,0x69,0x5f,0xb9,0x4,0xa2,0xa3,0xa9,0x58,0x13,0x4e,0xf9,0xe4,0xd2,0xc0,0x11,0x3e,0x3a,0xa8,0x8e,0x2c,0x83,0x98,0x93,0x45,0x94,0xf,0xc9,0x28,0x88,0x68,0x87,0x4d,0x7f,0xfa,0xab,0x37,0xa,0x65,0x85,0x70,0x86,0x51,0x9f,0x30,0xa7,0xd1,0xde,0x59,0x53,0x34,0x33,0xaa,0x56,0x36,0x8c,0x78,0xb4,0xdc,0x7c,0x26,0xbf,0x96,0x1e,0xf2,0x6c,0x18,0xd3,0xfb,0xe9,0x35,0x46,0x66,0x10,0xdd,0x3c,0xd9,0xb7,0x67,0x1c,0x5a,0xa7,0x1,0xb9,0xc4,0x91,0xe1,0x1d,0x5c,0x6f,0x87,0x8f,0x75,0x1e,0x70,0x81,0x2f,0xad,0xe7,0x40,0xea,0xde,0x5f,0x77,0x6e,0xfc,0x18,0x24,0xa2,0x92,0xa3,0xba,0x80,0xca,0xbf,0x89,0x4c,0x98,0x61,0x3e,0x54,0xfb,0xf4,0x8a,0xbd,0xd1,0xd7,0xf0,0x3b,0x57,0xa8,0xe2,0x4a,0x2b,0x16,0x4d,0x9f,0x85,0xd3,0xf,0x56,0x55,0x8b,0x6c,0xd9,0xe9,0xbb,0xcd,0x30,0xc2,0x13,0xce,0x63,0x3a,0x5,0x53,0xec,0x1c,0x5e,0x46,0x6d,0x47,0xeb,0x4e,0xb2,0xac,0xd2,0x21,0xa5,0x9d,0x2d,0xf2,0x35,0xb5,0x2a,0x64,0xb7,0x29,0xe0,0x43,0x9b,0x6,0x42,0xf7,0x8d,0x45,0x50,0x6b,0xcb,0x10,0x1b,0xb4,0xab,0x1a,0xa1,0xf5,0x65,0xe8,0x32,0xc,0xd5,0xa0,0x31,0x22,0x4b,0xdd,0x78,0x11,0x58,0xb,0x97,0xdf,0x5a,0x25,0xd0,0xaa,0xc5,0x3f,0x90,0x26,0xf1,0x7e,0xf9,0x7,0x71,0x2e,0x8c,0x9a,0x8,0x33,0xe5,0x23,0x38,0x69,0x88,0x34,0xaf,0x27,0xed,0x28,0xc8,0x73,0x5b,0xcc,0xb8,0xe6,0xc6,0x49,0x95,0x9c,0x79,0xb0,0x7d,0xbc,0xfa,0x17,0xc7,0x93,0xa,0xf3,0x94,0x2c,0xd8,0xf6,0x96,0xdc,0x86,0x14,0x7c,0xbe,0x52,0x1f,0x36,0xef,0x39,0x51,0x12,0xd4,0xda,0xd6,0x5d,0xe,0x6a,0x4,0xdb,0xc3,0xfd,0x3c,0x20,0x67,0x99,0xa6,0xfe,0xb6,0xcf,0xe3,0x74,0x0,0x83,0xae,0x41,0x48,0x4f,0x76,0x7b,0x19,0xa4,0xc9,0xff,0x9,0xf8,0x2,0x3,0x59,0x44,0xb3,0xee,0xb1,0x9e,0x72,0x60,0xc1,0x8e,0x3d,0x37,0xc0,0x15,0x62,0x66,0xd,0x7a,0x7f,0x68,0xe4,0x82,0xa9,0x84,0x28,0xef,0x30,0x80,0xaa,0x79,0x37,0xa8,0xaf,0x53,0xf6,0x5a,0xb8,0x3c,0xcf,0xb1,0xf1,0x4e,0x18,0x27,0x70,0x5b,0x43,0x1,0x2d,0xd0,0xa6,0xf4,0x7e,0xd3,0xe,0xdf,0x56,0x3f,0x2c,0xbd,0x45,0xc,0x65,0xc0,0x78,0xe8,0xbc,0x7,0xc8,0x11,0x2f,0xf5,0xd6,0x76,0x4d,0x58,0xb6,0xa9,0x6,0xd,0x86,0x5e,0xfd,0x34,0x90,0xea,0x5f,0x1b,0xbf,0x39,0x5,0xe1,0x9d,0xa7,0xbe,0x8f,0xf7,0x5d,0xfa,0xb0,0x73,0x6a,0x42,0xc3,0x68,0x92,0x9a,0x72,0x32,0x9c,0x6d,0x3,0xd9,0xa4,0x1c,0xba,0x41,0x0,0xfc,0x8c,0x4b,0x12,0xce,0x98,0xc4,0x71,0x96,0x48,0x57,0xff,0xb5,0x4a,0x82,0x50,0xb,0x36,0xa0,0x97,0xe9,0xe6,0x26,0xed,0xca,0xcc,0x51,0x94,0xa2,0xd7,0x49,0x23,0x7c,0x85,0x5c,0xb3,0x9e,0x1d,0x66,0x6b,0x52,0x55,0xe3,0xbb,0x84,0x7a,0x69,0xfe,0xd2,0xab,0xc6,0x19,0x77,0x13,0x3d,0x21,0xe0,0xde,0xf,0x4c,0x24,0xf2,0x40,0xcb,0xc7,0xc9,0x75,0x62,0x67,0x10,0x99,0xb4,0x9f,0xf9,0x2a,0x20,0x93,0xdc,0x7b,0x7f,0x8,0xdd,0xf3,0xae,0x59,0x44,0x7d,0x6f,0x83,0xac,0xe2,0xd4,0xb9,0x4,0x1e,0x1f,0xe5,0x14,0xb2,0x29,0x95,0x74,0xd5,0x35,0xf0,0x3a,0x15,0x87,0x91,0x33,0x25,0x3e,0xf8,0x2e,0xec,0x3b,0x8d,0x22,0x6c,0x1a,0xe4,0x63,0x47,0xc2,0x8a,0x16,0xd8,0xb7,0xcd,0x38,0x61,0x9,0x9b,0xc1,0x2b,0x2,0x4f,0xa3,0x89,0xee,0x17,0x8e,0x8b,0xeb,0xc5,0x31,0x60,0xad,0x64,0x81,0xda,0xa,0xe7,0xa1,0xa5,0xd1,0x46,0x6e,0x88,0x54,0xdb,0xfb,0x6c,0xce,0xe8,0x7a,0x5,0xd3,0xd8,0xc3,0x68,0x89,0x4f,0xd4,0xd,0xc7,0x28,0xc8,0x77,0xeb,0xba,0x3f,0x30,0xc5,0x25,0x4a,0x70,0xdf,0x11,0xc6,0x19,0x9e,0x91,0xe7,0xea,0x73,0x74,0x13,0x38,0xcc,0x76,0x16,0x66,0x3c,0x9c,0xf4,0xb2,0x5e,0xd6,0xff,0xbb,0x93,0x58,0x2c,0x26,0x6,0x75,0xa9,0x99,0x7c,0x9d,0x50,0x1a,0x5c,0x27,0xf7,0x79,0x87,0x1e,0x46,0x2f,0x56,0x94,0x3,0x63,0xe0,0xa1,0x4e,0xaf,0xa8,0x9b,0x96,0xd9,0xf,0xf2,0xb1,0x3a,0x34,0xbd,0x36,0x8a,0xee,0x3b,0xe4,0x1d,0x23,0xc0,0xdc,0x6e,0x21,0xd7,0xdd,0xf5,0x20,0x86,0x82,0x9a,0xed,0x88,0x9f,0x62,0x4,0x64,0x49,0x44,0xf9,0x1f,0x29,0x18,0xe9,0xe3,0xe2,0xa4,0xb9,0xe,0x53,0x7e,0x51,0x80,0x92,0x7,0x4d,0xa,0xa0,0xbf,0x3e,0x8e,0x97,0xf8,0x1c,0x42,0xc4,0x43,0x72,0x60,0x5a,0xe1,0x47,0x24,0x59,0x1,0x71,0xbc,0xfd,0x67,0x8f,0x95,0x6f,0x90,0xfe,0xcf,0x61,0x48,0xb7,0xaa,0x2,0xf6,0xcb,0x7f,0xad,0x33,0x65,0xb6,0xef,0x6b,0xb5,0x39,0x8c,0x5f,0x2a,0xac,0x69,0x81,0x78,0xb4,0xde,0x14,0x1b,0x5d,0x6a,0x37,0x31,0xdb,0x10,0xb,0xa7,0x52,0xae,0x32,0x4c,0x45,0xc1,0xcd,0x7d,0xd5,0x12,0xca,0x55,0x57,0x84,0x5b,0x9,0xd0,0x2d,0xf3,0x22,0x83,0x2e,0xe5,0xda,0xc,0xb3,0xbe,0xfc,0x8d,0xa6,0x41,0xfa,0x85,0x15,0xd2,0x8,0x35,0xec,0xd1,0x40,0xab,0xc2,0x98,0x3d,0xb8,0xf1,0x0,0xc9,0x7b,0xa3,0xa2,0xe6,0x6d,0x17,0xb0,0xa5,0x2b,0x8b,0xfb,0xf0,0x4b,0x54,0x29,0xd5,0x20,0x8c,0x46,0xc2,0xcb,0xb5,0x95,0x52,0xfa,0x4a,0x3,0xd0,0xd2,0x4d,0xaa,0x57,0x8e,0xdc,0xa9,0x4,0xa5,0x74,0x34,0x8b,0x5d,0x62,0x21,0xa,0x7b,0x39,0x92,0x2,0x7d,0xc6,0x6b,0xb2,0x8f,0x55,0x45,0x2c,0xc7,0x56,0x76,0x3f,0xba,0x1f,0x24,0xfc,0x4e,0x87,0x90,0xea,0x61,0x25,0xc,0xac,0x22,0x37,0xd3,0xcc,0x77,0x7c,0x27,0x8d,0xca,0x80,0x10,0x9,0xb9,0x38,0x43,0xc5,0x9b,0x7f,0xdd,0xe7,0xf5,0xc4,0xde,0xa3,0xc0,0x66,0x7a,0x3b,0xf6,0x86,0xe8,0x12,0x8,0xe0,0xe6,0x48,0x79,0x17,0x85,0x2d,0x30,0xcf,0x2a,0xf8,0x4c,0x71,0x68,0x31,0xe2,0xb4,0xb,0xbe,0x32,0xec,0xee,0x2b,0xad,0xd8,0x59,0x33,0xff,0x6,0xed,0xda,0x9c,0x93,0x97,0x5c,0xb6,0xb0,0xc1,0x99,0x0,0xfe,0x84,0x13,0xd1,0xa8,0xc9,0x26,0x67,0xe4,0x11,0x1c,0x2f,0x28,0x36,0x75,0x88,0x5e,0xb1,0x3a,0xb3,0xbd,0x63,0xbc,0x69,0xd,0x5b,0x47,0xa4,0x9a,0x5a,0x50,0xa6,0xe9,0x5,0x1,0xa7,0x72,0x18,0xf,0x6a,0x1d,0xce,0xe3,0x83,0xe5,0xae,0x98,0x7e,0xc3,0x65,0x64,0x6e,0x9f,0xd4,0x89,0x3e,0x23,0x15,0x7,0xd6,0xf9,0xfd,0x6f,0x49,0xeb,0x44,0x5f,0x54,0x82,0x53,0xc8,0xe,0xef,0x4f,0xaf,0x40,0x8a,0xb8,0x3d,0x6c,0xf0,0xcd,0xa2,0x42,0xb7,0x41,0x96,0x58,0xf7,0x60,0x16,0x19,0x9e,0x94,0xf3,0xf4,0x6d,0x91,0xf1,0x4b,0xbf,0x73,0x1b,0xbb,0xe1,0x78,0x51,0xd9,0x35,0xab,0xdf,0x14,0x3c,0x2e,0xf2,0x81,0xa1,0xd7,0x1a,0xfb,0x1e,0x70,0xa0,0xdb,0x9d,0xc8,0x6e,0xd6,0xab,0xfe,0x8e,0x72,0x33,0x0,0xe8,0xe0,0x1a,0x71,0x1f,0xee,0x40,0xc2,0x88,0x2f,0x85,0xb1,0x30,0x18,0x1,0x93,0x77,0x4b,0xcd,0xfd,0xcc,0xd5,0xef,0xa5,0xd0,0xe6,0x23,0xf7,0xe,0x51,0x3b,0x94,0x9b,0xe5,0xd2,0xbe,0xb8,0x9f,0x54,0x38,0xc7,0x8d,0x25,0x44,0x79,0x22,0xf0,0xea,0xbc,0x60,0x39,0x3a,0xe4,0x3,0xb6,0x86,0xd4,0xa2,0x5f,0xad,0x7c,0xa1,0xc,0x55,0x6a,0x3c,0x83,0x73,0x31,0x29,0x2,0x28,0x84,0x21,0xdd,0xc3,0xbd,0x4e,0xca,0xf2,0x42,0x9d,0x5a,0xda,0x45,0xb,0xd8,0x46,0x8f,0x2c,0xf4,0x69,0x2d,0x98,0xe2,0x2a,0x3f,0x4,0xa4,0x7f,0x74,0xdb,0xc4,0x75,0xce,0x9a,0xa,0x87,0x5d,0x63,0xba,0xcf,0x5e,0x4d,0x24,0xb2,0x17,0x7e,0x37,0x64,0xf8,0xb0,0x35,0x4a,0xbf,0xc5,0xaa,0x50,0xff,0x49,0x9e,0x11,0x96,0x68,0x1e,0x41,0xe3,0xf5,0x67,0x5c,0x8a,0x4c,0x57,0x6,0xe7,0x5b,0xc0,0x48,0x82,0x47,0xa7,0x1c,0x34,0xa3,0xd7,0x89,0xa9,0x26,0xfa,0xf3,0x16,0xdf,0x12,0xd3,0x95,0x78,0xa8,0xfc,0x65,0x9c,0xfb,0x43,0xb7,0x99,0xf9,0xb3,0xe9,0x7b,0x13,0xd1,0x3d,0x70,0x59,0x80,0x56,0x3e,0x7d,0xbb,0xb5,0xb9,0x32,0x61,0x5,0x6b,0xb4,0xac,0x92,0x53,0x4f,0x8,0xf6,0xc9,0x91,0xd9,0xa0,0x8c,0x1b,0x6f,0xec,0xc1,0x2e,0x27,0x20,0x19,0x14,0x76,0xcb,0xa6,0x90,0x66,0x97,0x6d,0x6c,0x36,0x2b,0xdc,0x81,0xde,0xf1,0x1d,0xf,0xae,0xe1,0x52,0x58,0xaf,0x7a,0xd,0x9,0x62,0x15,0x10,0x7,0x8b,0xed,0xc6,0xeb,0x21,0xe6,0x39,0x89,0xa3,0x70,0x3e,0xa1,0xa6,0x5a,0xff,0x53,0xb1,0x35,0xc6,0xb8,0xf8,0x47,0x11,0x2e,0x79,0x52,0x4a,0x8,0x24,0xd9,0xaf,0xfd,0x77,0xda,0x7,0xd6,0x5f,0x36,0x25,0xb4,0x4c,0x5,0x6c,0xc9,0x71,0xe1,0xb5,0xe,0xc1,0x18,0x26,0xfc,0xdf,0x7f,0x44,0x51,0xbf,0xa0,0xf,0x4,0x8f,0x57,0xf4,0x3d,0x99,0xe3,0x56,0x12,0xb6,0x30,0xc,0xe8,0x94,0xae,0xb7,0x86,0xfe,0x54,0xf3,0xb9,0x7a,0x63,0x4b,0xca,0x61,0x9b,0x93,0x7b,0x3b,0x95,0x64,0xa,0xd0,0xad,0x15,0xb3,0x48,0x9,0xf5,0x85,0x42,0x1b,0xc7,0x91,0xcd,0x78,0x9f,0x41,0x5e,0xf6,0xbc,0x43,0x8b,0x59,0x2,0x3f,0xa9,0x9e,0xe0,0xef,0x2f,0xe4,0xc3,0xc5,0x58,0x9d,0xab,0xde,0x40,0x2a,0x75,0x8c,0x55,0xba,0x97,0x14,0x6f,0x62,0x5b,0x5c,0xea,0xb2,0x8d,0x73,0x60,0xf7,0xdb,0xa2,0xcf,0x10,0x7e,0x1a,0x34,0x28,0xe9,0xd7,0x6,0x45,0x2d,0xfb,0x49,0xc2,0xce,0xc0,0x7c,0x6b,0x6e,0x19,0x90,0xbd,0x96,0xf0,0x23,0x29,0x9a,0xd5,0x72,0x76,0x1,0xd4,0xfa,0xa7,0x50,0x4d,0x74,0x66,0x8a,0xa5,0xeb,0xdd,0xb0,0xd,0x17,0x16,0xec,0x1d,0xbb,0x20,0x9c,0x7d,0xdc,0x3c,0xf9,0x33,0x1c,0x8e,0x98,0x3a,0x2c,0x37,0xf1,0x27,0xe5,0x32,0x84,0x2b,0x65,0x13,0xed,0x6a,0x4e,0xcb,0x83,0x1f,0xd1,0xbe,0xc4,0x31,0x68,0x0,0x92,0xc8,0x22,0xb,0x46,0xaa,0x80,0xe7,0x1e,0x87,0x82,0xe2,0xcc,0x38,0x69,0xa4,0x6d,0x88,0xd3,0x3,0xee,0xa8,0xac,0xd8,0x4f,0x67,0x81,0x5d,0xd2,0xf2,0xe9,0x4b,0x6d,0xff,0x80,0x56,0x5d,0x46,0xed,0xc,0xca,0x51,0x88,0x42,0xad,0x4d,0xf2,0x6e,0x3f,0xba,0xb5,0x40,0xa0,0xcf,0xf5,0x5a,0x94,0x43,0x9c,0x1b,0x14,0x62,0x6f,0xf6,0xf1,0x96,0xbd,0x49,0xf3,0x93,0xe3,0xb9,0x19,0x71,0x37,0xdb,0x53,0x7a,0x3e,0x16,0xdd,0xa9,0xa3,0x83,0xf0,0x2c,0x1c,0xf9,0x18,0xd5,0x9f,0xd9,0xa2,0x72,0xfc,0x2,0x9b,0xc3,0xaa,0xd3,0x11,0x86,0xe6,0x65,0x24,0xcb,0x2a,0x2d,0x1e,0x13,0x5c,0x8a,0x77,0x34,0xbf,0xb1,0x38,0xb3,0xf,0x6b,0xbe,0x61,0x98,0xa6,0x45,0x59,0xeb,0xa4,0x52,0x58,0x70,0xa5,0x3,0x7,0x1f,0x68,0xd,0x1a,0xe7,0x81,0xe1,0xcc,0xc1,0x7c,0x9a,0xac,0x9d,0x6c,0x66,0x67,0x21,0x3c,0x8b,0xd6,0xfb,0xd4,0x5,0x17,0x82,0xc8,0x8f,0x25,0x3a,0xbb,0xb,0x12,0x7d,0x99,0xc7,0x41,0xc6,0xf7,0xe5,0xdf,0x64,0xc2,0xa1,0xdc,0x84,0xf4,0x39,0x78,0xe2,0xa,0x10,0xea,0x15,0x7b,0x4a,0xe4,0xcd,0x32,0x2f,0x87,0x73,0x4e,0xfa,0x28,0xb6,0xe0,0x33,0x6a,0xee,0x30,0xbc,0x9,0xda,0xaf,0x29,0xec,0x4,0xfd,0x31,0x5b,0x91,0x9e,0xd8,0xef,0xb2,0xb4,0x5e,0x95,0x8e,0x22,0xd7,0x2b,0xb7,0xc9,0xc0,0x44,0x48,0xf8,0x50,0x97,0x4f,0xd0,0xd2,0x1,0xde,0x8c,0x55,0xa8,0x76,0xa7,0x6,0xab,0x60,0x5f,0x89,0x36,0x3b,0x79,0x8,0x23,0xc4,0x7f,0x0,0x90,0x57,0x8d,0xb0,0x69,0x54,0xc5,0x2e,0x47,0x1d,0xb8,0x3d,0x74,0x85,0x4c,0xfe,0x26,0x27,0x63,0xe8,0x92,0x35,0x20,0xae,0xe,0x7e,0x75,0xce,0xd1,0x8e,0x72,0x87,0x2b,0xe1,0x65,0x6c,0x12,0x32,0xf5,0x5d,0xed,0xa4,0x77,0x75,0xea,0xd,0xf0,0x29,0x7b,0xe,0xa3,0x2,0xd3,0x93,0x2c,0xfa,0xc5,0x86,0xad,0xdc,0x9e,0x35,0xa5,0xda,0x61,0xcc,0x15,0x28,0xf2,0xe2,0x8b,0x60,0xf1,0xd1,0x98,0x1d,0xb8,0x83,0x5b,0xe9,0x20,0x37,0x4d,0xc6,0x82,0xab,0xb,0x85,0x90,0x74,0x6b,0xd0,0xdb,0x80,0x2a,0x6d,0x27,0xb7,0xae,0x1e,0x9f,0xe4,0x62,0x3c,0xd8,0x7a,0x40,0x52,0x63,0x79,0x4,0x67,0xc1,0xdd,0x9c,0x51,0x21,0x4f,0xb5,0xaf,0x47,0x41,0xef,0xde,0xb0,0x22,0x8a,0x97,0x68,0x8d,0x5f,0xeb,0xd6,0xcf,0x96,0x45,0x13,0xac,0x19,0x95,0x4b,0x49,0x8c,0xa,0x7f,0xfe,0x94,0x58,0xa1,0x4a,0x7d,0x3b,0x34,0x30,0xfb,0x11,0x17,0x66,0x3e,0xa7,0x59,0x23,0xb4,0x76,0xf,0x6e,0x81,0xc0,0x43,0xb6,0xbb,0x88,0x8f,0x91,0xd2,0x2f,0xf9,0x16,0x9d,0x14,0x1a,0xc4,0x1b,0xce,0xaa,0xfc,0xe0,0x3,0x3d,0xfd,0xf7,0x1,0x4e,0xa2,0xa6,0x0,0xd5,0xbf,0xa8,0xcd,0xba,0x69,0x44,0x24,0x42,0x9,0x3f,0xd9,0x64,0xc2,0xc3,0xc9,0x38,0x73,0x2e,0x99,0x84,0xb2,0xa0,0x71,0x5e,0x5a,0xc8,0xee,0x4c,0xe3,0xf8,0xf3,0x25,0xf4,0x6f,0xa9,0x48,0xe8,0x8,0xe7,0x2d,0x1f,0x9a,0xcb,0x57,0x6a,0x5,0xe5,0x10,0xe6,0x31,0xff,0x50,0xc7,0xb1,0xbe,0x39,0x33,0x54,0x53,0xca,0x36,0x56,0xec,0x18,0xd4,0xbc,0x1c,0x46,0xdf,0xf6,0x7e,0x92,0xc,0x78,0xb3,0x9b,0x89,0x55,0x26,0x6,0x70,0xbd,0x5c,0xb9,0xd7,0x7,0x7c,0x3a,0xfa,0x3b,0x87,0x55,0xc6,0x13,0xdc,0x89,0xb8,0xc7,0x67,0xc9,0xbc,0x82,0x28,0x44,0xaf,0xdf,0x77,0xd4,0x5,0x49,0xf6,0x7d,0x7,0xd9,0xca,0xf9,0xe1,0x8,0x29,0x4,0xae,0xd8,0x65,0xad,0x63,0xc2,0x23,0x61,0x7f,0x1f,0x7e,0x2,0x7c,0x79,0xcf,0x15,0x60,0xf5,0x4b,0xa7,0x58,0x11,0x2a,0x9d,0x39,0xc,0x56,0xa3,0x12,0xbf,0x69,0x9a,0x6a,0x7b,0x2d,0x27,0x34,0xcb,0x99,0xf4,0x3e,0x80,0xe,0xd1,0x90,0x78,0xe6,0xe4,0x76,0xa0,0x6e,0xf3,0x2b,0x33,0x41,0x3c,0x3d,0x4a,0x83,0x73,0x50,0xa6,0x42,0x1b,0xb4,0xb,0x96,0x18,0x48,0x3,0xe8,0x47,0x5d,0xcd,0xaa,0x86,0x16,0xbd,0x62,0xfb,0xbb,0x32,0x6f,0xfe,0xe7,0x8f,0x1d,0xff,0xb7,0xa4,0xd,0x5f,0xd7,0x52,0x94,0x6c,0x71,0xc8,0xb2,0x25,0x6d,0xec,0xd6,0xa5,0x35,0xa1,0xd2,0xc0,0x46,0x40,0x1c,0x20,0xbe,0x4f,0xee,0xb9,0xf0,0xea,0xba,0xd0,0x38,0x3f,0x8a,0x2e,0xf,0x21,0x8c,0xb6,0x5c,0x43,0xd5,0xf2,0x68,0x4d,0xf7,0xb1,0x57,0xd3,0xfc,0xda,0xc5,0xfd,0x8e,0x66,0xab,0xe3,0x17,0x88,0x72,0x92,0xac,0x3a,0x1,0x8d,0xc1,0x85,0x24,0xf8,0xf1,0xef,0x59,0x97,0x64,0xce,0x8b,0x75,0x9b,0x5b,0x51,0xa,0x37,0x0,0xde,0xc3,0x81,0x7a,0x9e,0xeb,0xe0,0x2c,0x9c,0x1e,0x9f,0xa9,0x6,0xe2,0xcc,0x70,0x5e,0x26,0xb5,0x5a,0x9,0xc4,0xed,0xdd,0xa2,0x1a,0x91,0x84,0xb0,0x22,0x4e,0x2f,0x10,0x98,0x45,0xe9,0xe5,0x4c,0x74,0x95,0x93,0x54,0xdb,0x36,0xa8,0x30,0x31,0x19,0x6b,0xb3,0x53,0x14,0x43,0x84,0x5b,0xeb,0xc1,0x12,0x5c,0xc3,0xc4,0x38,0x9d,0x31,0xd3,0x57,0xa4,0xda,0x9a,0x25,0x73,0x4c,0x1b,0x30,0x28,0x6a,0x46,0xbb,0xcd,0x9f,0x15,0xb8,0x65,0xb4,0x3d,0x54,0x47,0xd6,0x2e,0x67,0xe,0xab,0x13,0x83,0xd7,0x6c,0xa3,0x7a,0x44,0x9e,0xbd,0x1d,0x26,0x33,0xdd,0xc2,0x6d,0x66,0xed,0x35,0x96,0x5f,0xfb,0x81,0x34,0x70,0xd4,0x52,0x6e,0x8a,0xf6,0xcc,0xd5,0xe4,0x9c,0x36,0x91,0xdb,0x18,0x1,0x29,0xa8,0x3,0xf9,0xf1,0x19,0x59,0xf7,0x6,0x68,0xb2,0xcf,0x77,0xd1,0x2a,0x6b,0x97,0xe7,0x20,0x79,0xa5,0xf3,0xaf,0x1a,0xfd,0x23,0x3c,0x94,0xde,0x21,0xe9,0x3b,0x60,0x5d,0xcb,0xfc,0x82,0x8d,0x4d,0x86,0xa1,0xa7,0x3a,0xff,0xc9,0xbc,0x22,0x48,0x17,0xee,0x37,0xd8,0xf5,0x76,0xd,0x0,0x39,0x3e,0x88,0xd0,0xef,0x11,0x2,0x95,0xb9,0xc0,0xad,0x72,0x1c,0x78,0x56,0x4a,0x8b,0xb5,0x64,0x27,0x4f,0x99,0x2b,0xa0,0xac,0xa2,0x1e,0x9,0xc,0x7b,0xf2,0xdf,0xf4,0x92,0x41,0x4b,0xf8,0xb7,0x10,0x14,0x63,0xb6,0x98,0xc5,0x32,0x2f,0x16,0x4,0xe8,0xc7,0x89,0xbf,0xd2,0x6f,0x75,0x74,0x8e,0x7f,0xd9,0x42,0xfe,0x1f,0xbe,0x5e,0x9b,0x51,0x7e,0xec,0xfa,0x58,0x4e,0x55,0x93,0x45,0x87,0x50,0xe6,0x49,0x7,0x71,0x8f,0x8,0x2c,0xa9,0xe1,0x7d,0xb3,0xdc,0xa6,0x53,0xa,0x62,0xf0,0xaa,0x40,0x69,0x24,0xc8,0xe2,0x85,0x7c,0xe5,0xe0,0x80,0xae,0x5a,0xb,0xc6,0xf,0xea,0xb1,0x61,0x8c,0xca,0xce,0xba,0x2d,0x5,0xe3,0x3f,0xb0,0x90,0xc2,0x60,0x46,0xd4,0xab,0x7d,0x76,0x6d,0xc6,0x27,0xe1,0x7a,0xa3,0x69,0x86,0x66,0xd9,0x45,0x14,0x91,0x9e,0x6b,0x8b,0xe4,0xde,0x71,0xbf,0x68,0xb7,0x30,0x3f,0x49,0x44,0xdd,0xda,0xbd,0x96,0x62,0xd8,0xb8,0xc8,0x92,0x32,0x5a,0x1c,0xf0,0x78,0x51,0x15,0x3d,0xf6,0x82,0x88,0xa8,0xdb,0x7,0x37,0xd2,0x33,0xfe,0xb4,0xf2,0x89,0x59,0xd7,0x29,0xb0,0xe8,0x81,0xf8,0x3a,0xad,0xcd,0x4e,0xf,0xe0,0x1,0x6,0x35,0x38,0x77,0xa1,0x5c,0x1f,0x94,0x9a,0x13,0x98,0x24,0x40,0x95,0x4a,0xb3,0x8d,0x6e,0x72,0xc0,0x8f,0x79,0x73,0x5b,0x8e,0x28,0x2c,0x34,0x43,0x26,0x31,0xcc,0xaa,0xca,0xe7,0xea,0x57,0xb1,0x87,0xb6,0x47,0x4d,0x4c,0xa,0x17,0xa0,0xfd,0xd0,0xff,0x2e,0x3c,0xa9,0xe3,0xa4,0xe,0x11,0x90,0x20,0x39,0x56,0xb2,0xec,0x6a,0xed,0xdc,0xce,0xf4,0x4f,0xe9,0x8a,0xf7,0xaf,0xdf,0x12,0x53,0xc9,0x21,0x3b,0xc1,0x3e,0x50,0x61,0xcf,0xe6,0x19,0x4,0xac,0x58,0x65,0xd1,0x3,0x9d,0xcb,0x18,0x41,0xc5,0x1b,0x97,0x22,0xf1,0x84,0x2,0xc7,0x2f,0xd6,0x1a,0x70,0xba,0xb5,0xf3,0xc4,0x99,0x9f,0x75,0xbe,0xa5,0x9,0xfc,0x0,0x9c,0xe2,0xeb,0x6f,0x63,0xd3,0x7b,0xbc,0x64,0xfb,0xf9,0x2a,0xf5,0xa7,0x7e,0x83,0x5d,0x8c,0x2d,0x80,0x4b,0x74,0xa2,0x1d,0x10,0x52,0x23,0x8,0xef,0x54,0x2b,0xbb,0x7c,0xa6,0x9b,0x42,0x7f,0xee,0x5,0x6c,0x36,0x93,0x16,0x5f,0xae,0x67,0xd5,0xd,0xc,0x48,0xc3,0xb9,0x1e,0xb,0x85,0x25,0x55,0x5e,0xe5,0xfa,0xd5,0xa8,0xbf,0xa7,0xfa,0x67,0xe2,0x34,0xd6,0x8f,0xc4,0x32,0x17,0xe7,0xa9,0xde,0xd,0x60,0xa0,0x5f,0xb9,0xb3,0xfe,0xef,0x72,0x70,0x4,0xec,0x9a,0x45,0xaa,0x14,0x89,0x6b,0x73,0x1b,0xfb,0x6a,0x2f,0xa6,0x0,0xf8,0x43,0xc6,0x99,0xcb,0x23,0x30,0x7c,0xd3,0xdc,0x97,0x2,0x8c,0x20,0x9f,0xf6,0x6f,0x82,0x29,0x3e,0x12,0xc9,0x59,0x62,0xe9,0x91,0xdd,0xe3,0x40,0x3b,0x4b,0xbd,0x90,0x75,0x9c,0x5e,0x6d,0x93,0x4d,0x48,0x1d,0x52,0x87,0x13,0xc1,0x6e,0xaf,0xbc,0xd0,0x28,0x16,0xf3,0x5d,0x2c,0x53,0xbe,0x9,0xcc,0x85,0xdf,0x33,0xf4,0x61,0xfd,0xe,0x86,0x2b,0xc2,0x37,0xad,0x98,0xb7,0xf5,0xf7,0x56,0xf1,0x39,0x3a,0x4c,0x5b,0x81,0xe8,0xed,0xea,0x96,0xeb,0x8b,0xb,0x3d,0x8,0x8a,0x74,0xb8,0xa,0x7f,0x21,0xce,0xca,0xb2,0x58,0xe4,0x92,0x76,0xf,0xcf,0x1f,0xe1,0xf0,0x5a,0xcd,0x3,0x15,0xee,0x4a,0x57,0xa3,0x94,0xc5,0x9e,0x4f,0xa2,0x7,0xc0,0xe0,0x1,0x71,0xd8,0xc7,0x80,0xff,0x27,0xa5,0x8d,0x3c,0xa4,0x5,0x10,0x36,0x8e,0x79,0x49,0x9d,0x50,0xd1,0x7d,0x84,0xc,0xda,0xbb,0x24,0xb6,0x2e,0x44,0x64,0x7e,0x7a,0x2d,0x2a,0xdb,0x18,0x22,0x9b,0xb5,0x1e,0xba,0xac,0xab,0x42,0x31,0xf9,0x78,0x26,0xb1,0xe5,0x5c,0x88,0xb4,0xd2,0xd4,0x46,0x54,0xa1,0x35,0x38,0xae,0xe6,0x6,0x83,0x1c,0x3f,0x77,0x65,0x7b,0xb0,0x6c,0x55,0x11,0x95,0x19,0x63,0x25,0xfc,0xd9,0x41,0x66,0xc8,0xd7,0x1a,0xf2,0x51,0x69,0x68,0x4e,0xc3,0x47,0x44,0xe2,0x5a,0x27,0x72,0x2,0xfe,0xbf,0x8c,0x64,0x6c,0x96,0xfd,0x93,0x62,0xcc,0x4e,0x4,0xa3,0x9,0x3d,0xbc,0x94,0x8d,0x1f,0xfb,0xc7,0x41,0x71,0x40,0x59,0x63,0x29,0x5c,0x6a,0xaf,0x7b,0x82,0xdd,0xb7,0x18,0x17,0x69,0x5e,0x32,0x34,0x13,0xd8,0xb4,0x4b,0x1,0xa9,0xc8,0xf5,0xae,0x7c,0x66,0x30,0xec,0xb5,0xb6,0x68,0x8f,0x3a,0xa,0x58,0x2e,0xd3,0x21,0xf0,0x2d,0x80,0xd9,0xe6,0xb0,0xf,0xff,0xbd,0xa5,0x8e,0xa4,0x8,0xad,0x51,0x4f,0x31,0xc2,0x46,0x7e,0xce,0x11,0xd6,0x56,0xc9,0x87,0x54,0xca,0x3,0xa0,0x78,0xe5,0xa1,0x14,0x6e,0xa6,0xb3,0x88,0x28,0xf3,0xf8,0x57,0x48,0xf9,0x42,0x16,0x86,0xb,0xd1,0xef,0x36,0x43,0xd2,0xc1,0xa8,0x3e,0x9b,0xf2,0xbb,0xe8,0x74,0x3c,0xb9,0xc6,0x33,0x49,0x26,0xdc,0x73,0xc5,0x12,0x9d,0x1a,0xe4,0x92,0xcd,0x6f,0x79,0xeb,0xd0,0x6,0xc0,0xdb,0x8a,0x6b,0xd7,0x4c,0xc4,0xe,0xcb,0x2b,0x90,0xb8,0x2f,0x5b,0x5,0x25,0xaa,0x76,0x7f,0x9a,0x53,0x9e,0x5f,0x19,0xf4,0x24,0x70,0xe9,0x10,0x77,0xcf,0x3b,0x15,0x75,0x3f,0x65,0xf7,0x9f,0x5d,0xb1,0xfc,0xd5,0xc,0xda,0xb2,0xf1,0x37,0x39,0x35,0xbe,0xed,0x89,0xe7,0x38,0x20,0x1e,0xdf,0xc3,0x84,0x7a,0x45,0x1d,0x55,0x2c,0x0,0x97,0xe3,0x60,0x4d,0xa2,0xab,0xac,0x95,0x98,0xfa,0x47,0x2a,0x1c,0xea,0x1b,0xe1,0xe0,0xba,0xa7,0x50,0xd,0x52,0x7d,0x91,0x83,0x22,0x6d,0xde,0xd4,0x23,0xf6,0x81,0x85,0xee,0x99,0x9c,0x8b,0x7,0x61,0x4a,0x67,0xd0,0x17,0xc8,0x78,0x52,0x81,0xcf,0x50,0x57,0xab,0xe,0xa2,0x40,0xc4,0x37,0x49,0x9,0xb6,0xe0,0xdf,0x88,0xa3,0xbb,0xf9,0xd5,0x28,0x5e,0xc,0x86,0x2b,0xf6,0x27,0xae,0xc7,0xd4,0x45,0xbd,0xf4,0x9d,0x38,0x80,0x10,0x44,0xff,0x30,0xe9,0xd7,0xd,0x2e,0x8e,0xb5,0xa0,0x4e,0x51,0xfe,0xf5,0x7e,0xa6,0x5,0xcc,0x68,0x12,0xa7,0xe3,0x47,0xc1,0xfd,0x19,0x65,0x5f,0x46,0x77,0xf,0xa5,0x2,0x48,0x8b,0x92,0xba,0x3b,0x90,0x6a,0x62,0x8a,0xca,0x64,0x95,0xfb,0x21,0x5c,0xe4,0x42,0xb9,0xf8,0x4,0x74,0xb3,0xea,0x36,0x60,0x3c,0x89,0x6e,0xb0,0xaf,0x7,0x4d,0xb2,0x7a,0xa8,0xf3,0xce,0x58,0x6f,0x11,0x1e,0xde,0x15,0x32,0x34,0xa9,0x6c,0x5a,0x2f,0xb1,0xdb,0x84,0x7d,0xa4,0x4b,0x66,0xe5,0x9e,0x93,0xaa,0xad,0x1b,0x43,0x7c,0x82,0x91,0x6,0x2a,0x53,0x3e,0xe1,0x8f,0xeb,0xc5,0xd9,0x18,0x26,0xf7,0xb4,0xdc,0xa,0xb8,0x33,0x3f,0x31,0x8d,0x9a,0x9f,0xe8,0x61,0x4c,0x67,0x1,0xd2,0xd8,0x6b,0x24,0x83,0x87,0xf0,0x25,0xb,0x56,0xa1,0xbc,0x85,0x97,0x7b,0x54,0x1a,0x2c,0x41,0xfc,0xe6,0xe7,0x1d,0xec,0x4a,0xd1,0x6d,0x8c,0x2d,0xcd,0x8,0xc2,0xed,0x7f,0x69,0xcb,0xdd,0xc6,0x0,0xd6,0x14,0xc3,0x75,0xda,0x94,0xe2,0x1c,0x9b,0xbf,0x3a,0x72,0xee,0x20,0x4f,0x35,0xc0,0x99,0xf1,0x63,0x39,0xd3,0xfa,0xb7,0x5b,0x71,0x16,0xef,0x76,0x73,0x13,0x3d,0xc9,0x98,0x55,0x9c,0x79,0x22,0xf2,0x1f,0x59,0x5d,0x29,0xbe,0x96,0x70,0xac,0x23,0x3,0x69,0x2b,0x29,0x88,0x2f,0xe7,0xe4,0x92,0x85,0x5f,0x36,0x33,0x34,0x48,0x35,0x55,0x60,0xd7,0x12,0x5b,0x1,0xed,0x2a,0xbf,0x23,0xd0,0x58,0xf5,0x1c,0xe9,0x73,0x46,0x96,0xc3,0x8c,0x59,0xcd,0x1f,0xb0,0x71,0x62,0xe,0xf6,0xc8,0x2d,0x83,0xf2,0x8d,0xbc,0x37,0x4f,0x3,0x3d,0x9e,0xe5,0x95,0x63,0x4e,0xab,0x42,0x80,0xb3,0x4d,0x93,0xa2,0xd,0x2,0x49,0xdc,0x52,0xfe,0x41,0x28,0xb1,0x5c,0xf7,0xe0,0xcc,0x17,0x87,0x57,0xb5,0xad,0xc5,0x25,0xb4,0xf1,0x78,0xde,0x26,0x9d,0x18,0x47,0x15,0xfd,0xee,0xd3,0xbe,0x7e,0x81,0x67,0x6d,0x20,0x31,0xac,0xae,0xda,0x32,0x44,0x9b,0x74,0xca,0xb,0x76,0x61,0x79,0x24,0xb9,0x3c,0xea,0x8,0x51,0x1a,0xec,0xc9,0x39,0x77,0x0,0xbd,0xfb,0x22,0x7,0x9f,0xb8,0x16,0x9,0xc4,0x2c,0x8f,0xb7,0xb6,0x90,0x1d,0x99,0xe6,0x70,0x38,0xd8,0x5d,0xc2,0xe1,0xa9,0xbb,0xa5,0x6e,0xb2,0x8b,0xcf,0x4b,0xc7,0x9c,0xef,0x27,0xa6,0xf8,0x6f,0x3b,0x82,0x56,0x6a,0xc,0xa,0x98,0x8a,0x7f,0xeb,0xf0,0x9a,0xba,0xa0,0xa4,0xf3,0xf4,0x5,0xc6,0xfc,0x45,0x6b,0xc0,0x64,0x72,0x75,0xdb,0xce,0xe8,0x50,0xa7,0x97,0x43,0x8e,0xf,0xa3,0x5a,0xd2,0x4,0x65,0xfa,0x68,0x91,0x7c,0xd9,0x1e,0x3e,0xdf,0xaf,0x6,0x19,0x5e,0x21,0xf9,0x7b,0x53,0xe2,0x7a,0xd1,0x11,0xc1,0x3f,0x2e,0x84,0x13,0xdd,0xcb,0x30,0x94,0x89,0x7d,0x4a,0x1b,0x40,0xd5,0xe3,0xd6,0x54,0xaa,0x66,0xd4,0xa1,0xff,0x10,0x14,0x6c,0x86,0x3a,0x4c,0xa8,0x72,0x8e,0x7b,0xd7,0x1d,0x99,0x90,0xee,0xce,0x9,0xa1,0x11,0x58,0x8b,0x89,0x16,0xf1,0xc,0xd5,0x87,0xf2,0x5f,0xfe,0x2f,0x6f,0xd0,0x6,0x39,0x7a,0x51,0x20,0x62,0xc9,0x59,0x26,0x9d,0x30,0xe9,0xd4,0xe,0x1e,0x77,0x9c,0xd,0x2d,0x64,0xe1,0x44,0x7f,0xa7,0x15,0xdc,0xcb,0xb1,0x3a,0x7e,0x57,0xf7,0x79,0x6c,0x88,0x97,0x2c,0x27,0x7c,0xd6,0x91,0xdb,0x4b,0x52,0xe2,0x63,0x18,0x9e,0xc0,0x24,0x86,0xbc,0xae,0x9f,0x85,0xf8,0x9b,0x3d,0x21,0x60,0xad,0xdd,0xb3,0x49,0x53,0xbb,0xbd,0x13,0x22,0x4c,0xde,0x76,0x6b,0x94,0x71,0xa3,0x17,0x2a,0x33,0x6a,0xb9,0xef,0x50,0xe5,0x69,0xb7,0xb5,0x70,0xf6,0x83,0x2,0x68,0xa4,0x5d,0xb6,0x81,0xc7,0xc8,0xcc,0x7,0xed,0xeb,0x9a,0xc2,0x5b,0xa5,0xdf,0x48,0x8a,0xf3,0x92,0x7d,0x3c,0xbf,0x4a,0x47,0x74,0x73,0x6d,0x2e,0xd3,0x5,0xea,0x61,0xe8,0xe6,0x38,0xe7,0x32,0x56,0x0,0x1c,0xff,0xc1,0x1,0xb,0xfd,0xb2,0x5e,0x5a,0xfc,0x29,0x43,0x54,0x31,0x46,0x95,0xb8,0xd8,0xbe,0xf5,0xc3,0x25,0x98,0x3e,0x3f,0x35,0xc4,0x8f,0xd2,0x65,0x78,0x4e,0x5c,0x8d,0xa2,0xa6,0x34,0x12,0xb0,0x1f,0x4,0xf,0xd9,0x8,0x93,0x55,0xb4,0x14,0xf4,0x1b,0xd1,0xe3,0x66,0x37,0xab,0x96,0xf9,0x19,0xec,0x1a,0xcd,0x3,0xac,0x3b,0x4d,0x42,0xc5,0xcf,0xa8,0xaf,0x36,0xca,0xaa,0x10,0xe4,0x28,0x40,0xe0,0xba,0x23,0xa,0x82,0x6e,0xf0,0x84,0x4f,0x67,0x75,0xa9,0xda,0xfa,0x8c,0x41,0xa0,0x45,0x2b,0xfb,0x80,0xc6,0x5,0xa3,0x1b,0x66,0x33,0x43,0xbf,0xfe,0xcd,0x25,0x2d,0xd7,0xbc,0xd2,0x23,0x8d,0xf,0x45,0xe2,0x48,0x7c,0xfd,0xd5,0xcc,0x5e,0xba,0x86,0x0,0x30,0x1,0x18,0x22,0x68,0x1d,0x2b,0xee,0x3a,0xc3,0x9c,0xf6,0x59,0x56,0x28,0x1f,0x73,0x75,0x52,0x99,0xf5,0xa,0x40,0xe8,0x89,0xb4,0xef,0x3d,0x27,0x71,0xad,0xf4,0xf7,0x29,0xce,0x7b,0x4b,0x19,0x6f,0x92,0x60,0xb1,0x6c,0xc1,0x98,0xa7,0xf1,0x4e,0xbe,0xfc,0xe4,0xcf,0xe5,0x49,0xec,0x10,0xe,0x70,0x83,0x7,0x3f,0x8f,0x50,0x97,0x17,0x88,0xc6,0x15,0x8b,0x42,0xe1,0x39,0xa4,0xe0,0x55,0x2f,0xe7,0xf2,0xc9,0x69,0xb2,0xb9,0x16,0x9,0xb8,0x3,0x57,0xc7,0x4a,0x90,0xae,0x77,0x2,0x93,0x80,0xe9,0x7f,0xda,0xb3,0xfa,0xa9,0x35,0x7d,0xf8,0x87,0x72,0x8,0x67,0x9d,0x32,0x84,0x53,0xdc,0x5b,0xa5,0xd3,0x8c,0x2e,0x38,0xaa,0x91,0x47,0x81,0x9a,0xcb,0x2a,0x96,0xd,0x85,0x4f,0x8a,0x6a,0xd1,0xf9,0x6e,0x1a,0x44,0x64,0xeb,0x37,0x3e,0xdb,0x12,0xdf,0x1e,0x58,0xb5,0x65,0x31,0xa8,0x51,0x36,0x8e,0x7a,0x54,0x34,0x7e,0x24,0xb6,0xde,0x1c,0xf0,0xbd,0x94,0x4d,0x9b,0xf3,0xb0,0x76,0x78,0x74,0xff,0xac,0xc8,0xa6,0x79,0x61,0x5f,0x9e,0x82,0xc5,0x3b,0x4,0x5c,0x14,0x6d,0x41,0xd6,0xa2,0x21,0xc,0xe3,0xea,0xed,0xd4,0xd9,0xbb,0x6,0x6b,0x5d,0xab,0x5a,0xa0,0xa1,0xfb,0xe6,0x11,0x4c,0x13,0x3c,0xd0,0xc2,0x63,0x2c,0x9f,0x95,0x62,0xb7,0xc0,0xc4,0xaf,0xd8,0xdd,0xca,0x46,0x20,0xb,0x26,0xde,0x50,0xfc,0x43,0xa0,0xf,0x0,0x4b,0xe2,0xce,0x15,0x85,0x2a,0xb3,0x5e,0xf5,0x27,0xb6,0xf3,0x7a,0x55,0xb7,0xaf,0xc7,0x45,0x17,0xff,0xec,0xdc,0x24,0x9f,0x1a,0x65,0x6f,0x22,0x33,0xd1,0xbc,0x7c,0x83,0x46,0x99,0x76,0xc8,0xae,0xac,0xd8,0x30,0x26,0xbb,0x3e,0xe8,0x9,0x74,0x63,0x7b,0xcb,0x3b,0x75,0x2,0xa,0x53,0x18,0xee,0x2d,0xe5,0xe6,0x90,0x6b,0x29,0x2b,0x8a,0x36,0x4a,0x37,0x57,0x87,0x5d,0x34,0x31,0x3,0xef,0x28,0xbd,0x62,0xd5,0x10,0x59,0x1e,0xeb,0x71,0x44,0x21,0xd2,0x5a,0xf7,0xcf,0x1d,0xb2,0x73,0x94,0xc1,0x8e,0x5b,0x2f,0x81,0xf0,0x8f,0x60,0xc,0xf4,0xca,0x3f,0x9c,0xe7,0x97,0xbe,0x35,0x4d,0x1,0x82,0xb1,0x4f,0x91,0x61,0x4c,0xa9,0x40,0xa5,0x95,0x41,0x8c,0xd9,0xcc,0xea,0x52,0x6,0x67,0xf8,0x6a,0xd,0xa1,0x58,0xd0,0x3c,0xdd,0xad,0x4,0x93,0x7e,0xdb,0x1c,0x79,0x51,0xe0,0x78,0x1b,0x5c,0x23,0xfb,0x2c,0x86,0x11,0xdf,0xd3,0x13,0xc3,0x3d,0x7f,0x48,0x19,0x42,0xc9,0x32,0x96,0x8b,0xa8,0x64,0xd6,0xa3,0xd7,0xe1,0xd4,0x56,0x84,0x38,0x4e,0xaa,0xfd,0x12,0x16,0x6e,0x9d,0xba,0x14,0xb,0xbf,0xf9,0x20,0x5,0xb4,0x92,0x1f,0x9b,0xc6,0x2e,0x8d,0xb5,0x5f,0xc0,0xe3,0xab,0xe4,0x72,0x3a,0xda,0x89,0xcd,0x49,0xc5,0xb9,0xa7,0x6c,0xb0,0xfa,0x6d,0x39,0x80,0x9e,0xed,0x25,0xa4,0x9a,0x88,0x7d,0xe9,0x54,0x68,0xe,0x8,0xa6,0xf1,0xf6,0x7,0xf2,0x98,0xb8,0xa2,0xc2,0x66,0x70,0x77,0xc4,0xfe,0x47,0x69,0xfe,0x5c,0x7a,0xe8,0x97,0x41,0x4a,0x51,0xfa,0x1b,0xdd,0x46,0x9f,0x55,0xba,0x5a,0xe5,0x79,0x28,0xad,0xa2,0x57,0xb7,0xd8,0xe2,0x4d,0x83,0x54,0x8b,0xc,0x3,0x75,0x78,0xe1,0xe6,0x81,0xaa,0x5e,0xe4,0x84,0xf4,0xae,0xe,0x66,0x20,0xcc,0x44,0x6d,0x29,0x1,0xca,0xbe,0xb4,0x94,0xe7,0x3b,0xb,0xee,0xf,0xc2,0x88,0xce,0xb5,0x65,0xeb,0x15,0x8c,0xd4,0xbd,0xc4,0x6,0x91,0xf1,0x72,0x33,0xdc,0x3d,0x3a,0x9,0x4,0x4b,0x9d,0x60,0x23,0xa8,0xa6,0x2f,0xa4,0x18,0x7c,0xa9,0x76,0x8f,0xb1,0x52,0x4e,0xfc,0xb3,0x45,0x4f,0x67,0xb2,0x14,0x10,0x8,0x7f,0x1a,0xd,0xf0,0x96,0xf6,0xdb,0xd6,0x6b,0x8d,0xbb,0x8a,0x7b,0x71,0x70,0x36,0x2b,0x9c,0xc1,0xec,0xc3,0x12,0x0,0x95,0xdf,0x98,0x32,0x2d,0xac,0x1c,0x5,0x6a,0x8e,0xd0,0x56,0xd1,0xe0,0xf2,0xc8,0x73,0xd5,0xb6,0xcb,0x93,0xe3,0x2e,0x6f,0xf5,0x1d,0x7,0xfd,0x2,0x6c,0x5d,0xf3,0xda,0x25,0x38,0x90,0x64,0x59,0xed,0x3f,0xa1,0xf7,0x24,0x7d,0xf9,0x27,0xab,0x1e,0xcd,0xb8,0x3e,0xfb,0x13,0xea,0x26,0x4c,0x86,0x89,0xcf,0xf8,0xa5,0xa3,0x49,0x82,0x99,0x35,0xc0,0x3c,0xa0,0xde,0xd7,0x53,0x5f,0xef,0x47,0x80,0x58,0xc7,0xc5,0x16,0xc9,0x9b,0x42,0xbf,0x61,0xb0,0x11,0xbc,0x77,0x48,0x9e,0x21,0x2c,0x6e,0x1f,0x34,0xd3,0x68,0x17,0x87,0x40,0x9a,0xa7,0x7e,0x43,0xd2,0x39,0x50,0xa,0xaf,0x2a,0x63,0x92,0x5b,0xe9,0x31,0x30,0x74,0xff,0x85,0x22,0x37,0xb9,0x19,0x69,0x62,0xd9,0xc6,0x71,0x8d,0x78,0xd4,0x1e,0x9a,0x93,0xed,0xcd,0xa,0xa2,0x12,0x5b,0x88,0x8a,0x15,0xf2,0xf,0xd6,0x84,0xf1,0x5c,0xfd,0x2c,0x6c,0xd3,0x5,0x3a,0x79,0x52,0x23,0x61,0xca,0x5a,0x25,0x9e,0x33,0xea,0xd7,0xd,0x1d,0x74,0x9f,0xe,0x2e,0x67,0xe2,0x47,0x7c,0xa4,0x16,0xdf,0xc8,0xb2,0x39,0x7d,0x54,0xf4,0x7a,0x6f,0x8b,0x94,0x2f,0x24,0x7f,0xd5,0x92,0xd8,0x48,0x51,0xe1,0x60,0x1b,0x9d,0xc3,0x27,0x85,0xbf,0xad,0x9c,0x86,0xfb,0x98,0x3e,0x22,0x63,0xae,0xde,0xb0,0x4a,0x50,0xb8,0xbe,0x10,0x21,0x4f,0xdd,0x75,0x68,0x97,0x72,0xa0,0x14,0x29,0x30,0x69,0xba,0xec,0x53,0xe6,0x6a,0xb4,0xb6,0x73,0xf5,0x80,0x1,0x6b,0xa7,0x5e,0xb5,0x82,0xc4,0xcb,0xcf,0x4,0xee,0xe8,0x99,0xc1,0x58,0xa6,0xdc,0x4b,0x89,0xf0,0x91,0x7e,0x3f,0xbc,0x49,0x44,0x77,0x70,0x6e,0x2d,0xd0,0x6,0xe9,0x62,0xeb,0xe5,0x3b,0xe4,0x31,0x55,0x3,0x1f,0xfc,0xc2,0x2,0x8,0xfe,0xb1,0x5d,0x59,0xff,0x2a,0x40,0x57,0x32,0x45,0x96,0xbb,0xdb,0xbd,0xf6,0xc0,0x26,0x9b,0x3d,0x3c,0x36,0xc7,0x8c,0xd1,0x66,0x7b,0x4d,0x5f,0x8e,0xa1,0xa5,0x37,0x11,0xb3,0x1c,0x7,0xc,0xda,0xb,0x90,0x56,0xb7,0x17,0xf7,0x18,0xd2,0xe0,0x65,0x34,0xa8,0x95,0xfa,0x1a,0xef,0x19,0xce,0x0,0xaf,0x38,0x4e,0x41,0xc6,0xcc,0xab,0xac,0x35,0xc9,0xa9,0x13,0xe7,0x2b,0x43,0xe3,0xb9,0x20,0x9,0x81,0x6d,0xf3,0x87,0x4c,0x64,0x76,0xaa,0xd9,0xf9,0x8f,0x42,0xa3,0x46,0x28,0xf8,0x83,0xc5,0x9f,0x76,0x93,0xbe,0x4e,0x90,0x6e,0x5d,0xde,0x92,0xea,0x61,0x48,0x38,0x43,0xe0,0x15,0x2b,0xd3,0xbf,0x50,0x2f,0x5e,0xf0,0x84,0x51,0x1e,0x4b,0xac,0x6d,0xc2,0x10,0x28,0x85,0xd,0xfe,0x9b,0xae,0x34,0xc1,0x86,0xcf,0xa,0xbd,0x62,0xf7,0x30,0xdc,0xee,0xeb,0x82,0x58,0x88,0xe8,0x95,0xe9,0x55,0xf4,0xf6,0xb4,0x4f,0x39,0x3a,0xf2,0x31,0xc7,0x8c,0xd5,0xdd,0xaa,0xe4,0x14,0xa4,0xbc,0xab,0xd6,0x37,0xe1,0x64,0xf9,0xef,0x7,0x73,0x71,0x17,0xa9,0x46,0x99,0x5c,0xa3,0x63,0xe,0xec,0xfd,0xb0,0xba,0xc5,0x40,0xfb,0x3,0x33,0x20,0xc8,0x9a,0x18,0x70,0x68,0x8a,0xa5,0x2c,0x69,0xf8,0x2a,0x81,0x6c,0xf5,0x5a,0xca,0x11,0x3d,0x94,0xdf,0xd0,0x7f,0x9c,0x23,0x8f,0x1,0xb6,0x98,0x21,0x1b,0xa8,0xaf,0xb9,0x1d,0x7d,0x67,0x47,0x2d,0xd8,0x29,0x2e,0x79,0xd7,0xd1,0xb7,0x8b,0x36,0xa2,0x57,0x45,0x7b,0xfa,0x32,0x41,0x5f,0xe6,0xb2,0x25,0x6f,0xb3,0x78,0x66,0x1a,0x96,0x12,0x56,0x5,0xe5,0xad,0x3b,0x74,0x3c,0x1f,0x80,0x6a,0x52,0xf1,0x19,0x44,0xc0,0x4d,0x6b,0xda,0xff,0x26,0x60,0xd4,0xcb,0x65,0x42,0xb1,0xc9,0xcd,0x22,0x75,0x91,0xe7,0x5b,0x89,0xb,0x3e,0x8,0x7c,0x9,0xbb,0x77,0x54,0x49,0xed,0x16,0x9d,0xc6,0x97,0xa0,0xe2,0x1c,0xcc,0xc,0x0,0xce,0x59,0xf3,0x24,0xfc,0x83,0xc4,0xa7,0x3f,0x8e,0xa6,0xc3,0x4,0xa1,0x4c,0xdb,0x72,0x2,0xe3,0xf,0x87,0x7e,0xd2,0xb5,0x27,0xb8,0xd9,0x8d,0x35,0x13,0x6,0x53,0x9e,0x4a,0x7a,0x36,0xf1,0x2e,0x9e,0xb4,0x67,0x29,0xb6,0xb1,0x4d,0xe8,0x44,0xa6,0x22,0xd1,0xaf,0xef,0x50,0x6,0x39,0x6e,0x45,0x5d,0x1f,0x33,0xce,0xb8,0xea,0x60,0xcd,0x10,0xc1,0x48,0x21,0x32,0xa3,0x5b,0x12,0x7b,0xde,0x66,0xf6,0xa2,0x19,0xd6,0xf,0x31,0xeb,0xc8,0x68,0x53,0x46,0xa8,0xb7,0x18,0x13,0x98,0x40,0xe3,0x2a,0x8e,0xf4,0x41,0x5,0xa1,0x27,0x1b,0xff,0x83,0xb9,0xa0,0x91,0xe9,0x43,0xe4,0xae,0x6d,0x74,0x5c,0xdd,0x76,0x8c,0x84,0x6c,0x2c,0x82,0x73,0x1d,0xc7,0xba,0x2,0xa4,0x5f,0x1e,0xe2,0x92,0x55,0xc,0xd0,0x86,0xda,0x6f,0x88,0x56,0x49,0xe1,0xab,0x54,0x9c,0x4e,0x15,0x28,0xbe,0x89,0xf7,0xf8,0x38,0xf3,0xd4,0xd2,0x4f,0x8a,0xbc,0xc9,0x57,0x3d,0x62,0x9b,0x42,0xad,0x80,0x3,0x78,0x75,0x4c,0x4b,0xfd,0xa5,0x9a,0x64,0x77,0xe0,0xcc,0xb5,0xd8,0x7,0x69,0xd,0x23,0x3f,0xfe,0xc0,0x11,0x52,0x3a,0xec,0x5e,0xd5,0xd9,0xd7,0x6b,0x7c,0x79,0xe,0x87,0xaa,0x81,0xe7,0x34,0x3e,0x8d,0xc2,0x65,0x61,0x16,0xc3,0xed,0xb0,0x47,0x5a,0x63,0x71,0x9d,0xb2,0xfc,0xca,0xa7,0x1a,0x0,0x1,0xfb,0xa,0xac,0x37,0x8b,0x6a,0xcb,0x2b,0xee,0x24,0xb,0x99,0x8f,0x2d,0x3b,0x20,0xe6,0x30,0xf2,0x25,0x93,0x3c,0x72,0x4,0xfa,0x7d,0x59,0xdc,0x94,0x8,0xc6,0xa9,0xd3,0x26,0x7f,0x17,0x85,0xdf,0x35,0x1c,0x51,0xbd,0x97,0xf0,0x9,0x90,0x95,0xf5,0xdb,0x2f,0x7e,0xb3,0x7a,0x9f,0xc4,0x14,0xf9,0xbf,0xbb,0xcf,0x58,0x70,0x96,0x4a,0xc5,0xe5,0x2c,0x8e,0xa8,0x3a,0x45,0x93,0x98,0x83,0x28,0xc9,0xf,0x94,0x4d,0x87,0x68,0x88,0x37,0xab,0xfa,0x7f,0x70,0x85,0x65,0xa,0x30,0x9f,0x51,0x86,0x59,0xde,0xd1,0xa7,0xaa,0x33,0x34,0x53,0x78,0x8c,0x36,0x56,0x26,0x7c,0xdc,0xb4,0xf2,0x1e,0x96,0xbf,0xfb,0xd3,0x18,0x6c,0x66,0x46,0x35,0xe9,0xd9,0x3c,0xdd,0x10,0x5a,0x1c,0x67,0xb7,0x39,0xc7,0x5e,0x6,0x6f,0x16,0xd4,0x43,0x23,0xa0,0xe1,0xe,0xef,0xe8,0xdb,0xd6,0x99,0x4f,0xb2,0xf1,0x7a,0x74,0xfd,0x76,0xca,0xae,0x7b,0xa4,0x5d,0x63,0x80,0x9c,0x2e,0x61,0x97,0x9d,0xb5,0x60,0xc6,0xc2,0xda,0xad,0xc8,0xdf,0x22,0x44,0x24,0x9,0x4,0xb9,0x5f,0x69,0x58,0xa9,0xa3,0xa2,0xe4,0xf9,0x4e,0x13,0x3e,0x11,0xc0,0xd2,0x47,0xd,0x4a,0xe0,0xff,0x7e,0xce,0xd7,0xb8,0x5c,0x2,0x84,0x3,0x32,0x20,0x1a,0xa1,0x7,0x64,0x19,0x41,0x31,0xfc,0xbd,0x27,0xcf,0xd5,0x2f,0xd0,0xbe,0x8f,0x21,0x8,0xf7,0xea,0x42,0xb6,0x8b,0x3f,0xed,0x73,0x25,0xf6,0xaf,0x2b,0xf5,0x79,0xcc,0x1f,0x6a,0xec,0x29,0xc1,0x38,0xf4,0x9e,0x54,0x5b,0x1d,0x2a,0x77,0x71,0x9b,0x50,0x4b,0xe7,0x12,0xee,0x72,0xc,0x5,0x81,0x8d,0x3d,0x95,0x52,0x8a,0x15,0x17,0xc4,0x1b,0x49,0x90,0x6d,0xb3,0x62,0xc3,0x6e,0xa5,0x9a,0x4c,0xf3,0xfe,0xbc,0xcd,0xe6,0x1,0xba,0xc5,0x55,0x92,0x48,0x75,0xac,0x91,0x0,0xeb,0x82,0xd8,0x7d,0xf8,0xb1,0x40,0x89,0x3b,0xe3,0xe2,0xa6,0x2d,0x57,0xf0,0xe5,0x6b,0xcb,0xbb,0xb0,0xb,0x14,0x31,0x23,0xd6,0x42,0xff,0xc3,0xa5,0xa3,0x51,0xc6,0x92,0x2b,0x35,0x46,0x8e,0xf,0x69,0xcd,0xdb,0xdc,0x6f,0x55,0xec,0xc2,0xd,0x5a,0x5d,0xac,0x59,0x33,0x13,0x9,0x1f,0x39,0xb4,0x30,0x6d,0x85,0x26,0x1e,0x36,0x11,0xbf,0xa0,0x14,0x52,0x8b,0xae,0x22,0x66,0xe2,0x6e,0x12,0xc,0xc7,0x1b,0xf4,0x6b,0x48,0x0,0x4f,0xd9,0x91,0x71,0xd4,0xe3,0xb2,0xe9,0x62,0x99,0x3d,0x20,0x87,0x2d,0xba,0x74,0x78,0xb8,0x68,0x96,0x2f,0x93,0xe5,0x1,0x56,0xb9,0xbd,0xc5,0x3,0xcf,0x7d,0x8,0x7c,0x4a,0x7f,0xfd,0xad,0xcc,0x53,0xc1,0xa6,0xa,0xf3,0x7b,0xe,0x3e,0xea,0x27,0x72,0x67,0x41,0xf9,0xd2,0xfa,0x4b,0xd3,0xb0,0xf7,0x88,0x50,0x97,0x76,0x6,0xaf,0x38,0xd5,0x70,0xb7,0x84,0x2a,0x5b,0x24,0xcb,0xa7,0x5f,0x61,0x64,0xb6,0x19,0xd8,0x3f,0x6a,0x25,0xf0,0x29,0x1a,0xe4,0x3a,0xca,0xe7,0x2,0xeb,0x94,0x37,0x4c,0x3c,0x15,0x9e,0xe6,0xaa,0x9d,0xe1,0x9c,0xfc,0x2c,0xf6,0x9f,0x9a,0x86,0x4e,0x4d,0x3b,0xc0,0x82,0x80,0x21,0xb5,0x40,0xda,0xef,0x8a,0x79,0xf1,0x5c,0xa8,0x44,0x83,0x16,0xc9,0x7e,0xbb,0xf2,0xed,0x32,0xdd,0x63,0x5,0x7,0x73,0x9b,0xce,0xc4,0x89,0x98,0x7a,0x17,0xd7,0x28,0x60,0x90,0xde,0xa9,0xa1,0xf8,0xb3,0x45,0x8d,0x10,0x95,0x43,0xa2,0xdf,0xc8,0xd0,0x49,0x65,0xbe,0x2e,0x81,0x18,0xf5,0x5e,0x75,0xfb,0x57,0xe8,0xb,0xa4,0xab,0xe0,0xee,0xbc,0x54,0x47,0x77,0x8f,0x34,0xb1,0x8c,0x1d,0x58,0xd1,0xfe,0x1c,0x4,0x6c,0x6a,0xcc,0x74,0x9,0x5c,0x2c,0xd0,0x91,0xa2,0x4a,0x42,0xb8,0xd3,0xbd,0x4c,0xe2,0x60,0x2a,0x8d,0x27,0x13,0x92,0xba,0xa3,0x31,0xd5,0xe9,0x6f,0x5f,0x6e,0x77,0x4d,0x7,0x72,0x44,0x81,0x55,0xac,0xf3,0x99,0x36,0x39,0x47,0x70,0x1c,0x1a,0x3d,0xf6,0x9a,0x65,0x2f,0x87,0xe6,0xdb,0x80,0x52,0x48,0x1e,0xc2,0x9b,0x98,0x46,0xa1,0x14,0x24,0x76,0x0,0xfd,0xf,0xde,0x3,0xae,0xf7,0xc8,0x9e,0x21,0xd1,0x93,0x8b,0xa0,0x8a,0x26,0x83,0x7f,0x61,0x1f,0xec,0x68,0x50,0xe0,0x3f,0xf8,0x78,0xe7,0xa9,0x7a,0xe4,0x2d,0x8e,0x56,0xcb,0x8f,0x3a,0x40,0x88,0x9d,0xa6,0x6,0xdd,0xd6,0x79,0x66,0xd7,0x6c,0x38,0xa8,0x25,0xff,0xc1,0x18,0x6d,0xfc,0xef,0x86,0x10,0xb5,0xdc,0x95,0xc6,0x5a,0x12,0x97,0xe8,0x1d,0x67,0x8,0xf2,0x5d,0xeb,0x3c,0xb3,0x34,0xca,0xbc,0xe3,0x41,0x57,0xc5,0xfe,0x28,0xee,0xf5,0xa4,0x45,0xf9,0x62,0xea,0x20,0xe5,0x5,0xbe,0x96,0x1,0x75,0x2b,0xb,0x84,0x58,0x51,0xb4,0x7d,0xb0,0x71,0x37,0xda,0xa,0x5e,0xc7,0x3e,0x59,0xe1,0x15,0x3b,0x5b,0x11,0x4b,0xd9,0xb1,0x73,0x9f,0xd2,0xfb,0x22,0xf4,0x9c,0xdf,0x19,0x17,0x1b,0x90,0xc3,0xa7,0xc9,0x16,0xe,0x30,0xf1,0xed,0xaa,0x54,0x6b,0x33,0x7b,0x2,0x2e,0xb9,0xcd,0x4e,0x63,0x8c,0x85,0x82,0xbb,0xb6,0xd4,0x69,0x4,0x32,0xc4,0x35,0xcf,0xce,0x94,0x89,0x7e,0x23,0x7c,0x53,0xbf,0xad,0xc,0x43,0xf0,0xfa,0xd,0xd8,0xaf,0xab,0xc0,0xb7,0xb2,0xa5,0x29,0x4f,0x64,0x49,0xad,0x6a,0xb5,0x5,0x2f,0xfc,0xb2,0x2d,0x2a,0xd6,0x73,0xdf,0x3d,0xb9,0x4a,0x34,0x74,0xcb,0x9d,0xa2,0xf5,0xde,0xc6,0x84,0xa8,0x55,0x23,0x71,0xfb,0x56,0x8b,0x5a,0xd3,0xba,0xa9,0x38,0xc0,0x89,0xe0,0x45,0xfd,0x6d,0x39,0x82,0x4d,0x94,0xaa,0x70,0x53,0xf3,0xc8,0xdd,0x33,0x2c,0x83,0x88,0x3,0xdb,0x78,0xb1,0x15,0x6f,0xda,0x9e,0x3a,0xbc,0x80,0x64,0x18,0x22,0x3b,0xa,0x72,0xd8,0x7f,0x35,0xf6,0xef,0xc7,0x46,0xed,0x17,0x1f,0xf7,0xb7,0x19,0xe8,0x86,0x5c,0x21,0x99,0x3f,0xc4,0x85,0x79,0x9,0xce,0x97,0x4b,0x1d,0x41,0xf4,0x13,0xcd,0xd2,0x7a,0x30,0xcf,0x7,0xd5,0x8e,0xb3,0x25,0x12,0x6c,0x63,0xa3,0x68,0x4f,0x49,0xd4,0x11,0x27,0x52,0xcc,0xa6,0xf9,0x0,0xd9,0x36,0x1b,0x98,0xe3,0xee,0xd7,0xd0,0x66,0x3e,0x1,0xff,0xec,0x7b,0x57,0x2e,0x43,0x9c,0xf2,0x96,0xb8,0xa4,0x65,0x5b,0x8a,0xc9,0xa1,0x77,0xc5,0x4e,0x42,0x4c,0xf0,0xe7,0xe2,0x95,0x1c,0x31,0x1a,0x7c,0xaf,0xa5,0x16,0x59,0xfe,0xfa,0x8d,0x58,0x76,0x2b,0xdc,0xc1,0xf8,0xea,0x6,0x29,0x67,0x51,0x3c,0x81,0x9b,0x9a,0x60,0x91,0x37,0xac,0x10,0xf1,0x50,0xb0,0x75,0xbf,0x90,0x2,0x14,0xb6,0xa0,0xbb,0x7d,0xab,0x69,0xbe,0x8,0xa7,0xe9,0x9f,0x61,0xe6,0xc2,0x47,0xf,0x93,0x5d,0x32,0x48,0xbd,0xe4,0x8c,0x1e,0x44,0xae,0x87,0xca,0x26,0xc,0x6b,0x92,0xb,0xe,0x6e,0x40,0xb4,0xe5,0x28,0xe1,0x4,0x5f,0x8f,0x62,0x24,0x20,0x54,0xc3,0xeb,0xd,0xd1,0x5e,0x7e,0x57,0xf5,0xd3,0x41,0x3e,0xe8,0xe3,0xf8,0x53,0xb2,0x74,0xef,0x36,0xfc,0x13,0xf3,0x4c,0xd0,0x81,0x4,0xb,0xfe,0x1e,0x71,0x4b,0xe4,0x2a,0xfd,0x22,0xa5,0xaa,0xdc,0xd1,0x48,0x4f,0x28,0x3,0xf7,0x4d,0x2d,0x5d,0x7,0xa7,0xcf,0x89,0x65,0xed,0xc4,0x80,0xa8,0x63,0x17,0x1d,0x3d,0x4e,0x92,0xa2,0x47,0xa6,0x6b,0x21,0x67,0x1c,0xcc,0x42,0xbc,0x25,0x7d,0x14,0x6d,0xaf,0x38,0x58,0xdb,0x9a,0x75,0x94,0x93,0xa0,0xad,0xe2,0x34,0xc9,0x8a,0x1,0xf,0x86,0xd,0xb1,0xd5,0x0,0xdf,0x26,0x18,0xfb,0xe7,0x55,0x1a,0xec,0xe6,0xce,0x1b,0xbd,0xb9,0xa1,0xd6,0xb3,0xa4,0x59,0x3f,0x5f,0x72,0x7f,0xc2,0x24,0x12,0x23,0xd2,0xd8,0xd9,0x9f,0x82,0x35,0x68,0x45,0x6a,0xbb,0xa9,0x3c,0x76,0x31,0x9b,0x84,0x5,0xb5,0xac,0xc3,0x27,0x79,0xff,0x78,0x49,0x5b,0x61,0xda,0x7c,0x1f,0x62,0x3a,0x4a,0x87,0xc6,0x5c,0xb4,0xae,0x54,0xab,0xc5,0xf4,0x5a,0x73,0x8c,0x91,0x39,0xcd,0xf0,0x44,0x96,0x8,0x5e,0x8d,0xd4,0x50,0x8e,0x2,0xb7,0x64,0x11,0x97,0x52,0xba,0x43,0x8f,0xe5,0x2f,0x20,0x66,0x51,0xc,0xa,0xe0,0x2b,0x30,0x9c,0x69,0x95,0x9,0x77,0x7e,0xfa,0xf6,0x46,0xee,0x29,0xf1,0x6e,0x6c,0xbf,0x60,0x32,0xeb,0x16,0xc8,0x19,0xb8,0x15,0xde,0xe1,0x37,0x88,0x85,0xc7,0xb6,0x9d,0x7a,0xc1,0xbe,0x2e,0xe9,0x33,0xe,0xd7,0xea,0x7b,0x90,0xf9,0xa3,0x6,0x83,0xca,0x3b,0xf2,0x40,0x98,0x99,0xdd,0x56,0x2c,0x8b,0x9e,0x10,0xb0,0xc0,0xcb,0x70,0x6f,0x55,0xa9,0x5c,0xf0,0x3a,0xbe,0xb7,0xc9,0xe9,0x2e,0x86,0x36,0x7f,0xac,0xae,0x31,0xd6,0x2b,0xf2,0xa0,0xd5,0x78,0xd9,0x8,0x48,0xf7,0x21,0x1e,0x5d,0x76,0x7,0x45,0xee,0x7e,0x1,0xba,0x17,0xce,0xf3,0x29,0x39,0x50,0xbb,0x2a,0xa,0x43,0xc6,0x63,0x58,0x80,0x32,0xfb,0xec,0x96,0x1d,0x59,0x70,0xd0,0x5e,0x4b,0xaf,0xb0,0xb,0x0,0x5b,0xf1,0xb6,0xfc,0x6c,0x75,0xc5,0x44,0x3f,0xb9,0xe7,0x3,0xa1,0x9b,0x89,0xb8,0xa2,0xdf,0xbc,0x1a,0x6,0x47,0x8a,0xfa,0x94,0x6e,0x74,0x9c,0x9a,0x34,0x5,0x6b,0xf9,0x51,0x4c,0xb3,0x56,0x84,0x30,0xd,0x14,0x4d,0x9e,0xc8,0x77,0xc2,0x4e,0x90,0x92,0x57,0xd1,0xa4,0x25,0x4f,0x83,0x7a,0x91,0xa6,0xe0,0xef,0xeb,0x20,0xca,0xcc,0xbd,0xe5,0x7c,0x82,0xf8,0x6f,0xad,0xd4,0xb5,0x5a,0x1b,0x98,0x6d,0x60,0x53,0x54,0x4a,0x9,0xf4,0x22,0xcd,0x46,0xcf,0xc1,0x1f,0xc0,0x15,0x71,0x27,0x3b,0xd8,0xe6,0x26,0x2c,0xda,0x95,0x79,0x7d,0xdb,0xe,0x64,0x73,0x16,0x61,0xb2,0x9f,0xff,0x99,0xd2,0xe4,0x2,0xbf,0x19,0x18,0x12,0xe3,0xa8,0xf5,0x42,0x5f,0x69,0x7b,0xaa,0x85,0x81,0x13,0x35,0x97,0x38,0x23,0x28,0xfe,0x2f,0xb4,0x72,0x93,0x33,0xd3,0x3c,0xf6,0xc4,0x41,0x10,0x8c,0xb1,0xde,0x3e,0xcb,0x3d,0xea,0x24,0x8b,0x1c,0x6a,0x65,0xe2,0xe8,0x8f,0x88,0x11,0xed,0x8d,0x37,0xc3,0xf,0x67,0xc7,0x9d,0x4,0x2d,0xa5,0x49,0xd7,0xa3,0x68,0x40,0x52,0x8e,0xfd,0xdd,0xab,0x66,0x87,0x62,0xc,0xdc,0xa7,0xe1,0x46,0xe0,0x58,0x25,0x70,0x0,0xfc,0xbd,0x8e,0x66,0x6e,0x94,0xff,0x91,0x60,0xce,0x4c,0x6,0xa1,0xb,0x3f,0xbe,0x96,0x8f,0x1d,0xf9,0xc5,0x43,0x73,0x42,0x5b,0x61,0x2b,0x5e,0x68,0xad,0x79,0x80,0xdf,0xb5,0x1a,0x15,0x6b,0x5c,0x30,0x36,0x11,0xda,0xb6,0x49,0x3,0xab,0xca,0xf7,0xac,0x7e,0x64,0x32,0xee,0xb7,0xb4,0x6a,0x8d,0x38,0x8,0x5a,0x2c,0xd1,0x23,0xf2,0x2f,0x82,0xdb,0xe4,0xb2,0xd,0xfd,0xbf,0xa7,0x8c,0xa6,0xa,0xaf,0x53,0x4d,0x33,0xc0,0x44,0x7c,0xcc,0x13,0xd4,0x54,0xcb,0x85,0x56,0xc8,0x1,0xa2,0x7a,0xe7,0xa3,0x16,0x6c,0xa4,0xb1,0x8a,0x2a,0xf1,0xfa,0x55,0x4a,0xfb,0x40,0x14,0x84,0x9,0xd3,0xed,0x34,0x41,0xd0,0xc3,0xaa,0x3c,0x99,0xf0,0xb9,0xea,0x76,0x3e,0xbb,0xc4,0x31,0x4b,0x24,0xde,0x71,0xc7,0x10,0x9f,0x18,0xe6,0x90,0xcf,0x6d,0x7b,0xe9,0xd2,0x4,0xc2,0xd9,0x88,0x69,0xd5,0x4e,0xc6,0xc,0xc9,0x29,0x92,0xba,0x2d,0x59,0x7,0x27,0xa8,0x74,0x7d,0x98,0x51,0x9c,0x5d,0x1b,0xf6,0x26,0x72,0xeb,0x12,0x75,0xcd,0x39,0x17,0x77,0x3d,0x67,0xf5,0x9d,0x5f,0xb3,0xfe,0xd7,0xe,0xd8,0xb0,0xf3,0x35,0x3b,0x37,0xbc,0xef,0x8b,0xe5,0x3a,0x22,0x1c,0xdd,0xc1,0x86,0x78,0x47,0x1f,0x57,0x2e,0x2,0x95,0xe1,0x62,0x4f,0xa0,0xa9,0xae,0x97,0x9a,0xf8,0x45,0x28,0x1e,0xe8,0x19,0xe3,0xe2,0xb8,0xa5,0x52,0xf,0x50,0x7f,0x93,0x81,0x20,0x6f,0xdc,0xd6,0x21,0xf4,0x83,0x87,0xec,0x9b,0x9e,0x89,0x5,0x63,0x48,0x65,0x11,0xd6,0x9,0xb9,0x93,0x40,0xe,0x91,0x96,0x6a,0xcf,0x63,0x81,0x5,0xf6,0x88,0xc8,0x77,0x21,0x1e,0x49,0x62,0x7a,0x38,0x14,0xe9,0x9f,0xcd,0x47,0xea,0x37,0xe6,0x6f,0x6,0x15,0x84,0x7c,0x35,0x5c,0xf9,0x41,0xd1,0x85,0x3e,0xf1,0x28,0x16,0xcc,0xef,0x4f,0x74,0x61,0x8f,0x90,0x3f,0x34,0xbf,0x67,0xc4,0xd,0xa9,0xd3,0x66,0x22,0x86,0x0,0x3c,0xd8,0xa4,0x9e,0x87,0xb6,0xce,0x64,0xc3,0x89,0x4a,0x53,0x7b,0xfa,0x51,0xab,0xa3,0x4b,0xb,0xa5,0x54,0x3a,0xe0,0x9d,0x25,0x83,0x78,0x39,0xc5,0xb5,0x72,0x2b,0xf7,0xa1,0xfd,0x48,0xaf,0x71,0x6e,0xc6,0x8c,0x73,0xbb,0x69,0x32,0xf,0x99,0xae,0xd0,0xdf,0x1f,0xd4,0xf3,0xf5,0x68,0xad,0x9b,0xee,0x70,0x1a,0x45,0xbc,0x65,0x8a,0xa7,0x24,0x5f,0x52,0x6b,0x6c,0xda,0x82,0xbd,0x43,0x50,0xc7,0xeb,0x92,0xff,0x20,0x4e,0x2a,0x4,0x18,0xd9,0xe7,0x36,0x75,0x1d,0xcb,0x79,0xf2,0xfe,0xf0,0x4c,0x5b,0x5e,0x29,0xa0,0x8d,0xa6,0xc0,0x13,0x19,0xaa,0xe5,0x42,0x46,0x31,0xe4,0xca,0x97,0x60,0x7d,0x44,0x56,0xba,0x95,0xdb,0xed,0x80,0x3d,0x27,0x26,0xdc,0x2d,0x8b,0x10,0xac,0x4d,0xec,0xc,0xc9,0x3,0x2c,0xbe,0xa8,0xa,0x1c,0x7,0xc1,0x17,0xd5,0x2,0xb4,0x1b,0x55,0x23,0xdd,0x5a,0x7e,0xfb,0xb3,0x2f,0xe1,0x8e,0xf4,0x1,0x58,0x30,0xa2,0xf8,0x12,0x3b,0x76,0x9a,0xb0,0xd7,0x2e,0xb7,0xb2,0xd2,0xfc,0x8,0x59,0x94,0x5d,0xb8,0xe3,0x33,0xde,0x98,0x9c,0xe8,0x7f,0x57,0xb1,0x6d,0xe2,0xc2,0xa6,0x4,0x22,0xb0,0xcf,0x19,0x12,0x9,0xa2,0x43,0x85,0x1e,0xc7,0xd,0xe2,0x2,0xbd,0x21,0x70,0xf5,0xfa,0xf,0xef,0x80,0xba,0x15,0xdb,0xc,0xd3,0x54,0x5b,0x2d,0x20,0xb9,0xbe,0xd9,0xf2,0x6,0xbc,0xdc,0xac,0xf6,0x56,0x3e,0x78,0x94,0x1c,0x35,0x71,0x59,0x92,0xe6,0xec,0xcc,0xbf,0x63,0x53,0xb6,0x57,0x9a,0xd0,0x96,0xed,0x3d,0xb3,0x4d,0xd4,0x8c,0xe5,0x9c,0x5e,0xc9,0xa9,0x2a,0x6b,0x84,0x65,0x62,0x51,0x5c,0x13,0xc5,0x38,0x7b,0xf0,0xfe,0x77,0xfc,0x40,0x24,0xf1,0x2e,0xd7,0xe9,0xa,0x16,0xa4,0xeb,0x1d,0x17,0x3f,0xea,0x4c,0x48,0x50,0x27,0x42,0x55,0xa8,0xce,0xae,0x83,0x8e,0x33,0xd5,0xe3,0xd2,0x23,0x29,0x28,0x6e,0x73,0xc4,0x99,0xb4,0x9b,0x4a,0x58,0xcd,0x87,0xc0,0x6a,0x75,0xf4,0x44,0x5d,0x32,0xd6,0x88,0xe,0x89,0xb8,0xaa,0x90,0x2b,0x8d,0xee,0x93,0xcb,0xbb,0x76,0x37,0xad,0x45,0x5f,0xa5,0x5a,0x34,0x5,0xab,0x82,0x7d,0x60,0xc8,0x3c,0x1,0xb5,0x67,0xf9,0xaf,0x7c,0x25,0xa1,0x7f,0xf3,0x46,0x95,0xe0,0x66,0xa3,0x4b,0xb2,0x7e,0x14,0xde,0xd1,0x97,0xa0,0xfd,0xfb,0x11,0xda,0xc1,0x6d,0x98,0x64,0xf8,0x86,0x8f,0xb,0x7,0xb7,0x1f,0xd8,0x0,0x9f,0x9d,0x4e,0x91,0xc3,0x1a,0xe7,0x39,0xe8,0x49,0xe4,0x2f,0x10,0xc6,0x79,0x74,0x36,0x47,0x6c,0x8b,0x30,0x4f,0xdf,0x18,0xc2,0xff,0x26,0x1b,0x8a,0x61,0x8,0x52,0xf7,0x72,0x3b,0xca,0x3,0xb1,0x69,0x68,0x2c,0xa7,0xdd,0x7a,0x6f,0xe1,0x41,0x31,0x3a,0x81,0x9e,0xbc,0x40,0xb5,0x19,0xd3,0x57,0x5e,0x20,0x0,0xc7,0x6f,0xdf,0x96,0x45,0x47,0xd8,0x3f,0xc2,0x1b,0x49,0x3c,0x91,0x30,0xe1,0xa1,0x1e,0xc8,0xf7,0xb4,0x9f,0xee,0xac,0x7,0x97,0xe8,0x53,0xfe,0x27,0x1a,0xc0,0xd0,0xb9,0x52,0xc3,0xe3,0xaa,0x2f,0x8a,0xb1,0x69,0xdb,0x12,0x5,0x7f,0xf4,0xb0,0x99,0x39,0xb7,0xa2,0x46,0x59,0xe2,0xe9,0xb2,0x18,0x5f,0x15,0x85,0x9c,0x2c,0xad,0xd6,0x50,0xe,0xea,0x48,0x72,0x60,0x51,0x4b,0x36,0x55,0xf3,0xef,0xae,0x63,0x13,0x7d,0x87,0x9d,0x75,0x73,0xdd,0xec,0x82,0x10,0xb8,0xa5,0x5a,0xbf,0x6d,0xd9,0xe4,0xfd,0xa4,0x77,0x21,0x9e,0x2b,0xa7,0x79,0x7b,0xbe,0x38,0x4d,0xcc,0xa6,0x6a,0x93,0x78,0x4f,0x9,0x6,0x2,0xc9,0x23,0x25,0x54,0xc,0x95,0x6b,0x11,0x86,0x44,0x3d,0x5c,0xb3,0xf2,0x71,0x84,0x89,0xba,0xbd,0xa3,0xe0,0x1d,0xcb,0x24,0xaf,0x26,0x28,0xf6,0x29,0xfc,0x98,0xce,0xd2,0x31,0xf,0xcf,0xc5,0x33,0x7c,0x90,0x94,0x32,0xe7,0x8d,0x9a,0xff,0x88,0x5b,0x76,0x16,0x70,0x3b,0xd,0xeb,0x56,0xf0,0xf1,0xfb,0xa,0x41,0x1c,0xab,0xb6,0x80,0x92,0x43,0x6c,0x68,0xfa,0xdc,0x7e,0xd1,0xca,0xc1,0x17,0xc6,0x5d,0x9b,0x7a,0xda,0x3a,0xd5,0x1f,0x2d,0xa8,0xf9,0x65,0x58,0x37,0xd7,0x22,0xd4,0x3,0xcd,0x62,0xf5,0x83,0x8c,0xb,0x1,0x66,0x61,0xf8,0x4,0x64,0xde,0x2a,0xe6,0x8e,0x2e,0x74,0xed,0xc4,0x4c,0xa0,0x3e,0x4a,0x81,0xa9,0xbb,0x67,0x14,0x34,0x42,0x8f,0x6e,0x8b,0xe5,0x35,0x4e,0x8,0x62,0xc4,0x7c,0x1,0x54,0x24,0xd8,0x99,0xaa,0x42,0x4a,0xb0,0xdb,0xb5,0x44,0xea,0x68,0x22,0x85,0x2f,0x1b,0x9a,0xb2,0xab,0x39,0xdd,0xe1,0x67,0x57,0x66,0x7f,0x45,0xf,0x7a,0x4c,0x89,0x5d,0xa4,0xfb,0x91,0x3e,0x31,0x4f,0x78,0x14,0x12,0x35,0xfe,0x92,0x6d,0x27,0x8f,0xee,0xd3,0x88,0x5a,0x40,0x16,0xca,0x93,0x90,0x4e,0xa9,0x1c,0x2c,0x7e,0x8,0xf5,0x7,0xd6,0xb,0xa6,0xff,0xc0,0x96,0x29,0xd9,0x9b,0x83,0xa8,0x82,0x2e,0x8b,0x77,0x69,0x17,0xe4,0x60,0x58,0xe8,0x37,0xf0,0x70,0xef,0xa1,0x72,0xec,0x25,0x86,0x5e,0xc3,0x87,0x32,0x48,0x80,0x95,0xae,0xe,0xd5,0xde,0x71,0x6e,0xdf,0x64,0x30,0xa0,0x2d,0xf7,0xc9,0x10,0x65,0xf4,0xe7,0x8e,0x18,0xbd,0xd4,0x9d,0xce,0x52,0x1a,0x9f,0xe0,0x15,0x6f,0x0,0xfa,0x55,0xe3,0x34,0xbb,0x3c,0xc2,0xb4,0xeb,0x49,0x5f,0xcd,0xf6,0x20,0xe6,0xfd,0xac,0x4d,0xf1,0x6a,0xe2,0x28,0xed,0xd,0xb6,0x9e,0x9,0x7d,0x23,0x3,0x8c,0x50,0x59,0xbc,0x75,0xb8,0x79,0x3f,0xd2,0x2,0x56,0xcf,0x36,0x51,0xe9,0x1d,0x33,0x53,0x19,0x43,0xd1,0xb9,0x7b,0x97,0xda,0xf3,0x2a,0xfc,0x94,0xd7,0x11,0x1f,0x13,0x98,0xcb,0xaf,0xc1,0x1e,0x6,0x38,0xf9,0xe5,0xa2,0x5c,0x63,0x3b,0x73,0xa,0x26,0xb1,0xc5,0x46,0x6b,0x84,0x8d,0x8a,0xb3,0xbe,0xdc,0x61,0xc,0x3a,0xcc,0x3d,0xc7,0xc6,0x9c,0x81,0x76,0x2b,0x74,0x5b,0xb7,0xa5,0x4,0x4b,0xf8,0xf2,0x5,0xd0,0xa7,0xa3,0xc8,0xbf,0xba,0xad,0x21,0x47,0x6c,0x41,0x1c,0xdb,0x4,0xb4,0x9e,0x4d,0x3,0x9c,0x9b,0x67,0xc2,0x6e,0x8c,0x8,0xfb,0x85,0xc5,0x7a,0x2c,0x13,0x44,0x6f,0x77,0x35,0x19,0xe4,0x92,0xc0,0x4a,0xe7,0x3a,0xeb,0x62,0xb,0x18,0x89,0x71,0x38,0x51,0xf4,0x4c,0xdc,0x88,0x33,0xfc,0x25,0x1b,0xc1,0xe2,0x42,0x79,0x6c,0x82,0x9d,0x32,0x39,0xb2,0x6a,0xc9,0x0,0xa4,0xde,0x6b,0x2f,0x8b,0xd,0x31,0xd5,0xa9,0x93,0x8a,0xbb,0xc3,0x69,0xce,0x84,0x47,0x5e,0x76,0xf7,0x5c,0xa6,0xae,0x46,0x6,0xa8,0x59,0x37,0xed,0x90,0x28,0x8e,0x75,0x34,0xc8,0xb8,0x7f,0x26,0xfa,0xac,0xf0,0x45,0xa2,0x7c,0x63,0xcb,0x81,0x7e,0xb6,0x64,0x3f,0x2,0x94,0xa3,0xdd,0xd2,0x12,0xd9,0xfe,0xf8,0x65,0xa0,0x96,0xe3,0x7d,0x17,0x48,0xb1,0x68,0x87,0xaa,0x29,0x52,0x5f,0x66,0x61,0xd7,0x8f,0xb0,0x4e,0x5d,0xca,0xe6,0x9f,0xf2,0x2d,0x43,0x27,0x9,0x15,0xd4,0xea,0x3b,0x78,0x10,0xc6,0x74,0xff,0xf3,0xfd,0x41,0x56,0x53,0x24,0xad,0x80,0xab,0xcd,0x1e,0x14,0xa7,0xe8,0x4f,0x4b,0x3c,0xe9,0xc7,0x9a,0x6d,0x70,0x49,0x5b,0xb7,0x98,0xd6,0xe0,0x8d,0x30,0x2a,0x2b,0xd1,0x20,0x86,0x1d,0xa1,0x40,0xe1,0x1,0xc4,0xe,0x21,0xb3,0xa5,0x7,0x11,0xa,0xcc,0x1a,0xd8,0xf,0xb9,0x16,0x58,0x2e,0xd0,0x57,0x73,0xf6,0xbe,0x22,0xec,0x83,0xf9,0xc,0x55,0x3d,0xaf,0xf5,0x1f,0x36,0x7b,0x97,0xbd,0xda,0x23,0xba,0xbf,0xdf,0xf1,0x5,0x54,0x99,0x50,0xb5,0xee,0x3e,0xd3,0x95,0x91,0xe5,0x72,0x5a,0xbc,0x60,0xef,0xcf,0x7a,0xd8,0xfe,0x6c,0x13,0xc5,0xce,0xd5,0x7e,0x9f,0x59,0xc2,0x1b,0xd1,0x3e,0xde,0x61,0xfd,0xac,0x29,0x26,0xd3,0x33,0x5c,0x66,0xc9,0x7,0xd0,0xf,0x88,0x87,0xf1,0xfc,0x65,0x62,0x5,0x2e,0xda,0x60,0x0,0x70,0x2a,0x8a,0xe2,0xa4,0x48,0xc0,0xe9,0xad,0x85,0x4e,0x3a,0x30,0x10,0x63,0xbf,0x8f,0x6a,0x8b,0x46,0xc,0x4a,0x31,0xe1,0x6f,0x91,0x8,0x50,0x39,0x40,0x82,0x15,0x75,0xf6,0xb7,0x58,0xb9,0xbe,0x8d,0x80,0xcf,0x19,0xe4,0xa7,0x2c,0x22,0xab,0x20,0x9c,0xf8,0x2d,0xf2,0xb,0x35,0xd6,0xca,0x78,0x37,0xc1,0xcb,0xe3,0x36,0x90,0x94,0x8c,0xfb,0x9e,0x89,0x74,0x12,0x72,0x5f,0x52,0xef,0x9,0x3f,0xe,0xff,0xf5,0xf4,0xb2,0xaf,0x18,0x45,0x68,0x47,0x96,0x84,0x11,0x5b,0x1c,0xb6,0xa9,0x28,0x98,0x81,0xee,0xa,0x54,0xd2,0x55,0x64,0x76,0x4c,0xf7,0x51,0x32,0x4f,0x17,0x67,0xaa,0xeb,0x71,0x99,0x83,0x79,0x86,0xe8,0xd9,0x77,0x5e,0xa1,0xbc,0x14,0xe0,0xdd,0x69,0xbb,0x25,0x73,0xa0,0xf9,0x7d,0xa3,0x2f,0x9a,0x49,0x3c,0xba,0x7f,0x97,0x6e,0xa2,0xc8,0x2,0xd,0x4b,0x7c,0x21,0x27,0xcd,0x6,0x1d,0xb1,0x44,0xb8,0x24,0x5a,0x53,0xd7,0xdb,0x6b,0xc3,0x4,0xdc,0x43,0x41,0x92,0x4d,0x1f,0xc6,0x3b,0xe5,0x34,0x95,0x38,0xf3,0xcc,0x1a,0xa5,0xa8,0xea,0x9b,0xb0,0x57,0xec,0x93,0x3,0xc4,0x1e,0x23,0xfa,0xc7,0x56,0xbd,0xd4,0x8e,0x2b,0xae,0xe7,0x16,0xdf,0x6d,0xb5,0xb4,0xf0,0x7b,0x1,0xa6,0xb3,0x3d,0x9d,0xed,0xe6,0x5d,0x42,0x15,0xe9,0x1c,0xb0,0x7a,0xfe,0xf7,0x89,0xa9,0x6e,0xc6,0x76,0x3f,0xec,0xee,0x71,0x96,0x6b,0xb2,0xe0,0x95,0x38,0x99,0x48,0x8,0xb7,0x61,0x5e,0x1d,0x36,0x47,0x5,0xae,0x3e,0x41,0xfa,0x57,0x8e,0xb3,0x69,0x79,0x10,0xfb,0x6a,0x4a,0x3,0x86,0x23,0x18,0xc0,0x72,0xbb,0xac,0xd6,0x5d,0x19,0x30,0x90,0x1e,0xb,0xef,0xf0,0x4b,0x40,0x1b,0xb1,0xf6,0xbc,0x2c,0x35,0x85,0x4,0x7f,0xf9,0xa7,0x43,0xe1,0xdb,0xc9,0xf8,0xe2,0x9f,0xfc,0x5a,0x46,0x7,0xca,0xba,0xd4,0x2e,0x34,0xdc,0xda,0x74,0x45,0x2b,0xb9,0x11,0xc,0xf3,0x16,0xc4,0x70,0x4d,0x54,0xd,0xde,0x88,0x37,0x82,0xe,0xd0,0xd2,0x17,0x91,0xe4,0x65,0xf,0xc3,0x3a,0xd1,0xe6,0xa0,0xaf,0xab,0x60,0x8a,0x8c,0xfd,0xa5,0x3c,0xc2,0xb8,0x2f,0xed,0x94,0xf5,0x1a,0x5b,0xd8,0x2d,0x20,0x13,0x14,0xa,0x49,0xb4,0x62,0x8d,0x6,0x8f,0x81,0x5f,0x80,0x55,0x31,0x67,0x7b,0x98,0xa6,0x66,0x6c,0x9a,0xd5,0x39,0x3d,0x9b,0x4e,0x24,0x33,0x56,0x21,0xf2,0xdf,0xbf,0xd9,0x92,0xa4,0x42,0xff,0x59,0x58,0x52,0xa3,0xe8,0xb5,0x2,0x1f,0x29,0x3b,0xea,0xc5,0xc1,0x53,0x75,0xd7,0x78,0x63,0x68,0xbe,0x6f,0xf4,0x32,0xd3,0x73,0x93,0x7c,0xb6,0x84,0x1,0x50,0xcc,0xf1,0x9e,0x7e,0x8b,0x7d,0xaa,0x64,0xcb,0x5c,0x2a,0x25,0xa2,0xa8,0xcf,0xc8,0x51,0xad,0xcd,0x77,0x83,0x4f,0x27,0x87,0xdd,0x44,0x6d,0xe5,0x9,0x97,0xe3,0x28,0x0,0x12,0xce,0xbd,0x9d,0xeb,0x26,0xc7,0x22,0x4c,0x9c,0xe7,0xa1,0xe0,0x46,0xfe,0x83,0xd6,0xa6,0x5a,0x1b,0x28,0xc0,0xc8,0x32,0x59,0x37,0xc6,0x68,0xea,0xa0,0x7,0xad,0x99,0x18,0x30,0x29,0xbb,0x5f,0x63,0xe5,0xd5,0xe4,0xfd,0xc7,0x8d,0xf8,0xce,0xb,0xdf,0x26,0x79,0x13,0xbc,0xb3,0xcd,0xfa,0x96,0x90,0xb7,0x7c,0x10,0xef,0xa5,0xd,0x6c,0x51,0xa,0xd8,0xc2,0x94,0x48,0x11,0x12,0xcc,0x2b,0x9e,0xae,0xfc,0x8a,0x77,0x85,0x54,0x89,0x24,0x7d,0x42,0x14,0xab,0x5b,0x19,0x1,0x2a,0x0,0xac,0x9,0xf5,0xeb,0x95,0x66,0xe2,0xda,0x6a,0xb5,0x72,0xf2,0x6d,0x23,0xf0,0x6e,0xa7,0x4,0xdc,0x41,0x5,0xb0,0xca,0x2,0x17,0x2c,0x8c,0x57,0x5c,0xf3,0xec,0x5d,0xe6,0xb2,0x22,0xaf,0x75,0x4b,0x92,0xe7,0x76,0x65,0xc,0x9a,0x3f,0x56,0x1f,0x4c,0xd0,0x98,0x1d,0x62,0x97,0xed,0x82,0x78,0xd7,0x61,0xb6,0x39,0xbe,0x40,0x36,0x69,0xcb,0xdd,0x4f,0x74,0xa2,0x64,0x7f,0x2e,0xcf,0x73,0xe8,0x60,0xaa,0x6f,0x8f,0x34,0x1c,0x8b,0xff,0xa1,0x81,0xe,0xd2,0xdb,0x3e,0xf7,0x3a,0xfb,0xbd,0x50,0x80,0xd4,0x4d,0xb4,0xd3,0x6b,0x9f,0xb1,0xd1,0x9b,0xc1,0x53,0x3b,0xf9,0x15,0x58,0x71,0xa8,0x7e,0x16,0x55,0x93,0x9d,0x91,0x1a,0x49,0x2d,0x43,0x9c,0x84,0xba,0x7b,0x67,0x20,0xde,0xe1,0xb9,0xf1,0x88,0xa4,0x33,0x47,0xc4,0xe9,0x6,0xf,0x8,0x31,0x3c,0x5e,0xe3,0x8e,0xb8,0x4e,0xbf,0x45,0x44,0x1e,0x3,0xf4,0xa9,0xf6,0xd9,0x35,0x27,0x86,0xc9,0x7a,0x70,0x87,0x52,0x25,0x21,0x4a,0x3d,0x38,0x2f,0xa3,0xc5,0xee,0xc3,0x7f,0xb8,0x67,0xd7,0xfd,0x2e,0x60,0xff,0xf8,0x4,0xa1,0xd,0xef,0x6b,0x98,0xe6,0xa6,0x19,0x4f,0x70,0x27,0xc,0x14,0x56,0x7a,0x87,0xf1,0xa3,0x29,0x84,0x59,0x88,0x1,0x68,0x7b,0xea,0x12,0x5b,0x32,0x97,0x2f,0xbf,0xeb,0x50,0x9f,0x46,0x78,0xa2,0x81,0x21,0x1a,0xf,0xe1,0xfe,0x51,0x5a,0xd1,0x9,0xaa,0x63,0xc7,0xbd,0x8,0x4c,0xe8,0x6e,0x52,0xb6,0xca,0xf0,0xe9,0xd8,0xa0,0xa,0xad,0xe7,0x24,0x3d,0x15,0x94,0x3f,0xc5,0xcd,0x25,0x65,0xcb,0x3a,0x54,0x8e,0xf3,0x4b,0xed,0x16,0x57,0xab,0xdb,0x1c,0x45,0x99,0xcf,0x93,0x26,0xc1,0x1f,0x0,0xa8,0xe2,0x1d,0xd5,0x7,0x5c,0x61,0xf7,0xc0,0xbe,0xb1,0x71,0xba,0x9d,0x9b,0x6,0xc3,0xf5,0x80,0x1e,0x74,0x2b,0xd2,0xb,0xe4,0xc9,0x4a,0x31,0x3c,0x5,0x2,0xb4,0xec,0xd3,0x2d,0x3e,0xa9,0x85,0xfc,0x91,0x4e,0x20,0x44,0x6a,0x76,0xb7,0x89,0x58,0x1b,0x73,0xa5,0x17,0x9c,0x90,0x9e,0x22,0x35,0x30,0x47,0xce,0xe3,0xc8,0xae,0x7d,0x77,0xc4,0x8b,0x2c,0x28,0x5f,0x8a,0xa4,0xf9,0xe,0x13,0x2a,0x38,0xd4,0xfb,0xb5,0x83,0xee,0x53,0x49,0x48,0xb2,0x43,0xe5,0x7e,0xc2,0x23,0x82,0x62,0xa7,0x6d,0x42,0xd0,0xc6,0x64,0x72,0x69,0xaf,0x79,0xbb,0x6c,0xda,0x75,0x3b,0x4d,0xb3,0x34,0x10,0x95,0xdd,0x41,0x8f,0xe0,0x9a,0x6f,0x36,0x5e,0xcc,0x96,0x7c,0x55,0x18,0xf4,0xde,0xb9,0x40,0xd9,0xdc,0xbc,0x92,0x66,0x37,0xfa,0x33,0xd6,0x8d,0x5d,0xb0,0xf6,0xf2,0x86,0x11,0x39,0xdf,0x3,0x8c,0xac,0x24,0x86,0xa0,0x32,0x4d,0x9b,0x90,0x8b,0x20,0xc1,0x7,0x9c,0x45,0x8f,0x60,0x80,0x3f,0xa3,0xf2,0x77,0x78,0x8d,0x6d,0x2,0x38,0x97,0x59,0x8e,0x51,0xd6,0xd9,0xaf,0xa2,0x3b,0x3c,0x5b,0x70,0x84,0x3e,0x5e,0x2e,0x74,0xd4,0xbc,0xfa,0x16,0x9e,0xb7,0xf3,0xdb,0x10,0x64,0x6e,0x4e,0x3d,0xe1,0xd1,0x34,0xd5,0x18,0x52,0x14,0x6f,0xbf,0x31,0xcf,0x56,0xe,0x67,0x1e,0xdc,0x4b,0x2b,0xa8,0xe9,0x6,0xe7,0xe0,0xd3,0xde,0x91,0x47,0xba,0xf9,0x72,0x7c,0xf5,0x7e,0xc2,0xa6,0x73,0xac,0x55,0x6b,0x88,0x94,0x26,0x69,0x9f,0x95,0xbd,0x68,0xce,0xca,0xd2,0xa5,0xc0,0xd7,0x2a,0x4c,0x2c,0x1,0xc,0xb1,0x57,0x61,0x50,0xa1,0xab,0xaa,0xec,0xf1,0x46,0x1b,0x36,0x19,0xc8,0xda,0x4f,0x5,0x42,0xe8,0xf7,0x76,0xc6,0xdf,0xb0,0x54,0xa,0x8c,0xb,0x3a,0x28,0x12,0xa9,0xf,0x6c,0x11,0x49,0x39,0xf4,0xb5,0x2f,0xc7,0xdd,0x27,0xd8,0xb6,0x87,0x29,0x0,0xff,0xe2,0x4a,0xbe,0x83,0x37,0xe5,0x7b,0x2d,0xfe,0xa7,0x23,0xfd,0x71,0xc4,0x17,0x62,0xe4,0x21,0xc9,0x30,0xfc,0x96,0x5c,0x53,0x15,0x22,0x7f,0x79,0x93,0x58,0x43,0xef,0x1a,0xe6,0x7a,0x4,0xd,0x89,0x85,0x35,0x9d,0x5a,0x82,0x1d,0x1f,0xcc,0x13,0x41,0x98,0x65,0xbb,0x6a,0xcb,0x66,0xad,0x92,0x44,0xfb,0xf6,0xb4,0xc5,0xee,0x9,0xb2,0xcd,0x5d,0x9a,0x40,0x7d,0xa4,0x99,0x8,0xe3,0x8a,0xd0,0x75,0xf0,0xb9,0x48,0x81,0x33,0xeb,0xea,0xae,0x25,0x5f,0xf8,0xed,0x63,0xc3,0xb3,0xb8,0x3,0x1c,0x4a,0xb6,0x43,0xef,0x25,0xa1,0xa8,0xd6,0xf6,0x31,0x99,0x29,0x60,0xb3,0xb1,0x2e,0xc9,0x34,0xed,0xbf,0xca,0x67,0xc6,0x17,0x57,0xe8,0x3e,0x1,0x42,0x69,0x18,0x5a,0xf1,0x61,0x1e,0xa5,0x8,0xd1,0xec,0x36,0x26,0x4f,0xa4,0x35,0x15,0x5c,0xd9,0x7c,0x47,0x9f,0x2d,0xe4,0xf3,0x89,0x2,0x46,0x6f,0xcf,0x41,0x54,0xb0,0xaf,0x14,0x1f,0x44,0xee,0xa9,0xe3,0x73,0x6a,0xda,0x5b,0x20,0xa6,0xf8,0x1c,0xbe,0x84,0x96,0xa7,0xbd,0xc0,0xa3,0x5,0x19,0x58,0x95,0xe5,0x8b,0x71,0x6b,0x83,0x85,0x2b,0x1a,0x74,0xe6,0x4e,0x53,0xac,0x49,0x9b,0x2f,0x12,0xb,0x52,0x81,0xd7,0x68,0xdd,0x51,0x8f,0x8d,0x48,0xce,0xbb,0x3a,0x50,0x9c,0x65,0x8e,0xb9,0xff,0xf0,0xf4,0x3f,0xd5,0xd3,0xa2,0xfa,0x63,0x9d,0xe7,0x70,0xb2,0xcb,0xaa,0x45,0x4,0x87,0x72,0x7f,0x4c,0x4b,0x55,0x16,0xeb,0x3d,0xd2,0x59,0xd0,0xde,0x0,0xdf,0xa,0x6e,0x38,0x24,0xc7,0xf9,0x39,0x33,0xc5,0x8a,0x66,0x62,0xc4,0x11,0x7b,0x6c,0x9,0x7e,0xad,0x80,0xe0,0x86,0xcd,0xfb,0x1d,0xa0,0x6,0x7,0xd,0xfc,0xb7,0xea,0x5d,0x40,0x76,0x64,0xb5,0x9a,0x9e,0xc,0x2a,0x88,0x27,0x3c,0x37,0xe1,0x30,0xab,0x6d,0x8c,0x2c,0xcc,0x23,0xe9,0xdb,0x5e,0xf,0x93,0xae,0xc1,0x21,0xd4,0x22,0xf5,0x3b,0x94,0x3,0x75,0x7a,0xfd,0xf7,0x90,0x97,0xe,0xf2,0x92,0x28,0xdc,0x10,0x78,0xd8,0x82,0x1b,0x32,0xba,0x56,0xc8,0xbc,0x77,0x5f,0x4d,0x91,0xe2,0xc2,0xb4,0x79,0x98,0x7d,0x13,0xc3,0xb8,0xfe,0x5f,0xf9,0x41,0x3c,0x69,0x19,0xe5,0xa4,0x97,0x7f,0x77,0x8d,0xe6,0x88,0x79,0xd7,0x55,0x1f,0xb8,0x12,0x26,0xa7,0x8f,0x96,0x4,0xe0,0xdc,0x5a,0x6a,0x5b,0x42,0x78,0x32,0x47,0x71,0xb4,0x60,0x99,0xc6,0xac,0x3,0xc,0x72,0x45,0x29,0x2f,0x8,0xc3,0xaf,0x50,0x1a,0xb2,0xd3,0xee,0xb5,0x67,0x7d,0x2b,0xf7,0xae,0xad,0x73,0x94,0x21,0x11,0x43,0x35,0xc8,0x3a,0xeb,0x36,0x9b,0xc2,0xfd,0xab,0x14,0xe4,0xa6,0xbe,0x95,0xbf,0x13,0xb6,0x4a,0x54,0x2a,0xd9,0x5d,0x65,0xd5,0xa,0xcd,0x4d,0xd2,0x9c,0x4f,0xd1,0x18,0xbb,0x63,0xfe,0xba,0xf,0x75,0xbd,0xa8,0x93,0x33,0xe8,0xe3,0x4c,0x53,0xe2,0x59,0xd,0x9d,0x10,0xca,0xf4,0x2d,0x58,0xc9,0xda,0xb3,0x25,0x80,0xe9,0xa0,0xf3,0x6f,0x27,0xa2,0xdd,0x28,0x52,0x3d,0xc7,0x68,0xde,0x9,0x86,0x1,0xff,0x89,0xd6,0x74,0x62,0xf0,0xcb,0x1d,0xdb,0xc0,0x91,0x70,0xcc,0x57,0xdf,0x15,0xd0,0x30,0x8b,0xa3,0x34,0x40,0x1e,0x3e,0xb1,0x6d,0x64,0x81,0x48,0x85,0x44,0x2,0xef,0x3f,0x6b,0xf2,0xb,0x6c,0xd4,0x20,0xe,0x6e,0x24,0x7e,0xec,0x84,0x46,0xaa,0xe7,0xce,0x17,0xc1,0xa9,0xea,0x2c,0x22,0x2e,0xa5,0xf6,0x92,0xfc,0x23,0x3b,0x5,0xc4,0xd8,0x9f,0x61,0x5e,0x6,0x4e,0x37,0x1b,0x8c,0xf8,0x7b,0x56,0xb9,0xb0,0xb7,0x8e,0x83,0xe1,0x5c,0x31,0x7,0xf1,0x0,0xfa,0xfb,0xa1,0xbc,0x4b,0x16,0x49,0x66,0x8a,0x98,0x39,0x76,0xc5,0xcf,0x38,0xed,0x9a,0x9e,0xf5,0x82,0x87,0x90,0x1c,0x7a,0x51,0x7c,0x1,0xc6,0x19,0xa9,0x83,0x50,0x1e,0x81,0x86,0x7a,0xdf,0x73,0x91,0x15,0xe6,0x98,0xd8,0x67,0x31,0xe,0x59,0x72,0x6a,0x28,0x4,0xf9,0x8f,0xdd,0x57,0xfa,0x27,0xf6,0x7f,0x16,0x5,0x94,0x6c,0x25,0x4c,0xe9,0x51,0xc1,0x95,0x2e,0xe1,0x38,0x6,0xdc,0xff,0x5f,0x64,0x71,0x9f,0x80,0x2f,0x24,0xaf,0x77,0xd4,0x1d,0xb9,0xc3,0x76,0x32,0x96,0x10,0x2c,0xc8,0xb4,0x8e,0x97,0xa6,0xde,0x74,0xd3,0x99,0x5a,0x43,0x6b,0xea,0x41,0xbb,0xb3,0x5b,0x1b,0xb5,0x44,0x2a,0xf0,0x8d,0x35,0x93,0x68,0x29,0xd5,0xa5,0x62,0x3b,0xe7,0xb1,0xed,0x58,0xbf,0x61,0x7e,0xd6,0x9c,0x63,0xab,0x79,0x22,0x1f,0x89,0xbe,0xc0,0xcf,0xf,0xc4,0xe3,0xe5,0x78,0xbd,0x8b,0xfe,0x60,0xa,0x55,0xac,0x75,0x9a,0xb7,0x34,0x4f,0x42,0x7b,0x7c,0xca,0x92,0xad,0x53,0x40,0xd7,0xfb,0x82,0xef,0x30,0x5e,0x3a,0x14,0x8,0xc9,0xf7,0x26,0x65,0xd,0xdb,0x69,0xe2,0xee,0xe0,0x5c,0x4b,0x4e,0x39,0xb0,0x9d,0xb6,0xd0,0x3,0x9,0xba,0xf5,0x52,0x56,0x21,0xf4,0xda,0x87,0x70,0x6d,0x54,0x46,0xaa,0x85,0xcb,0xfd,0x90,0x2d,0x37,0x36,0xcc,0x3d,0x9b,0x0,0xbc,0x5d,0xfc,0x1c,0xd9,0x13,0x3c,0xae,0xb8,0x1a,0xc,0x17,0xd1,0x7,0xc5,0x12,0xa4,0xb,0x45,0x33,0xcd,0x4a,0x6e,0xeb,0xa3,0x3f,0xf1,0x9e,0xe4,0x11,0x48,0x20,0xb2,0xe8,0x2,0x2b,0x66,0x8a,0xa0,0xc7,0x3e,0xa7,0xa2,0xc2,0xec,0x18,0x49,0x84,0x4d,0xa8,0xf3,0x23,0xce,0x88,0x8c,0xf8,0x6f,0x47,0xa1,0x7d,0xf2,0xd2,0x7,0xa5,0x83,0x11,0x6e,0xb8,0xb3,0xa8,0x3,0xe2,0x24,0xbf,0x66,0xac,0x43,0xa3,0x1c,0x80,0xd1,0x54,0x5b,0xae,0x4e,0x21,0x1b,0xb4,0x7a,0xad,0x72,0xf5,0xfa,0x8c,0x81,0x18,0x1f,0x78,0x53,0xa7,0x1d,0x7d,0xd,0x57,0xf7,0x9f,0xd9,0x35,0xbd,0x94,0xd0,0xf8,0x33,0x47,0x4d,0x6d,0x1e,0xc2,0xf2,0x17,0xf6,0x3b,0x71,0x37,0x4c,0x9c,0x12,0xec,0x75,0x2d,0x44,0x3d,0xff,0x68,0x8,0x8b,0xca,0x25,0xc4,0xc3,0xf0,0xfd,0xb2,0x64,0x99,0xda,0x51,0x5f,0xd6,0x5d,0xe1,0x85,0x50,0x8f,0x76,0x48,0xab,0xb7,0x5,0x4a,0xbc,0xb6,0x9e,0x4b,0xed,0xe9,0xf1,0x86,0xe3,0xf4,0x9,0x6f,0xf,0x22,0x2f,0x92,0x74,0x42,0x73,0x82,0x88,0x89,0xcf,0xd2,0x65,0x38,0x15,0x3a,0xeb,0xf9,0x6c,0x26,0x61,0xcb,0xd4,0x55,0xe5,0xfc,0x93,0x77,0x29,0xaf,0x28,0x19,0xb,0x31,0x8a,0x2c,0x4f,0x32,0x6a,0x1a,0xd7,0x96,0xc,0xe4,0xfe,0x4,0xfb,0x95,0xa4,0xa,0x23,0xdc,0xc1,0x69,0x9d,0xa0,0x14,0xc6,0x58,0xe,0xdd,0x84,0x0,0xde,0x52,0xe7,0x34,0x41,0xc7,0x2,0xea,0x13,0xdf,0xb5,0x7f,0x70,0x36,0x1,0x5c,0x5a,0xb0,0x7b,0x60,0xcc,0x39,0xc5,0x59,0x27,0x2e,0xaa,0xa6,0x16,0xbe,0x79,0xa1,0x3e,0x3c,0xef,0x30,0x62,0xbb,0x46,0x98,0x49,0xe8,0x45,0x8e,0xb1,0x67,0xd8,0xd5,0x97,0xe6,0xcd,0x2a,0x91,0xee,0x7e,0xb9,0x63,0x5e,0x87,0xba,0x2b,0xc0,0xa9,0xf3,0x56,0xd3,0x9a,0x6b,0xa2,0x10,0xc8,0xc9,0x8d,0x6,0x7c,0xdb,0xce,0x40,0xe0,0x90,0x9b,0x20,0x3f,0xf9,0x5,0xf0,0x5c,0x96,0x12,0x1b,0x65,0x45,0x82,0x2a,0x9a,0xd3,0x0,0x2,0x9d,0x7a,0x87,0x5e,0xc,0x79,0xd4,0x75,0xa4,0xe4,0x5b,0x8d,0xb2,0xf1,0xda,0xab,0xe9,0x42,0xd2,0xad,0x16,0xbb,0x62,0x5f,0x85,0x95,0xfc,0x17,0x86,0xa6,0xef,0x6a,0xcf,0xf4,0x2c,0x9e,0x57,0x40,0x3a,0xb1,0xf5,0xdc,0x7c,0xf2,0xe7,0x3,0x1c,0xa7,0xac,0xf7,0x5d,0x1a,0x50,0xc0,0xd9,0x69,0xe8,0x93,0x15,0x4b,0xaf,0xd,0x37,0x25,0x14,0xe,0x73,0x10,0xb6,0xaa,0xeb,0x26,0x56,0x38,0xc2,0xd8,0x30,0x36,0x98,0xa9,0xc7,0x55,0xfd,0xe0,0x1f,0xfa,0x28,0x9c,0xa1,0xb8,0xe1,0x32,0x64,0xdb,0x6e,0xe2,0x3c,0x3e,0xfb,0x7d,0x8,0x89,0xe3,0x2f,0xd6,0x3d,0xa,0x4c,0x43,0x47,0x8c,0x66,0x60,0x11,0x49,0xd0,0x2e,0x54,0xc3,0x1,0x78,0x19,0xf6,0xb7,0x34,0xc1,0xcc,0xff,0xf8,0xe6,0xa5,0x58,0x8e,0x61,0xea,0x63,0x6d,0xb3,0x6c,0xb9,0xdd,0x8b,0x97,0x74,0x4a,0x8a,0x80,0x76,0x39,0xd5,0xd1,0x77,0xa2,0xc8,0xdf,0xba,0xcd,0x1e,0x33,0x53,0x35,0x7e,0x48,0xae,0x13,0xb5,0xb4,0xbe,0x4f,0x4,0x59,0xee,0xf3,0xc5,0xd7,0x6,0x29,0x2d,0xbf,0x99,0x3b,0x94,0x8f,0x84,0x52,0x83,0x18,0xde,0x3f,0x9f,0x7f,0x90,0x5a,0x68,0xed,0xbc,0x20,0x1d,0x72,0x92,0x67,0x91,0x46,0x88,0x27,0xb0,0xc6,0xc9,0x4e,0x44,0x23,0x24,0xbd,0x41,0x21,0x9b,0x6f,0xa3,0xcb,0x6b,0x31,0xa8,0x81,0x9,0xe5,0x7b,0xf,0xc4,0xec,0xfe,0x22,0x51,0x71,0x7,0xca,0x2b,0xce,0xa0,0x70,0xb,0x4d,0x87,0x21,0x99,0xe4,0xb1,0xc1,0x3d,0x7c,0x4f,0xa7,0xaf,0x55,0x3e,0x50,0xa1,0xf,0x8d,0xc7,0x60,0xca,0xfe,0x7f,0x57,0x4e,0xdc,0x38,0x4,0x82,0xb2,0x83,0x9a,0xa0,0xea,0x9f,0xa9,0x6c,0xb8,0x41,0x1e,0x74,0xdb,0xd4,0xaa,0x9d,0xf1,0xf7,0xd0,0x1b,0x77,0x88,0xc2,0x6a,0xb,0x36,0x6d,0xbf,0xa5,0xf3,0x2f,0x76,0x75,0xab,0x4c,0xf9,0xc9,0x9b,0xed,0x10,0xe2,0x33,0xee,0x43,0x1a,0x25,0x73,0xcc,0x3c,0x7e,0x66,0x4d,0x67,0xcb,0x6e,0x92,0x8c,0xf2,0x1,0x85,0xbd,0xd,0xd2,0x15,0x95,0xa,0x44,0x97,0x9,0xc0,0x63,0xbb,0x26,0x62,0xd7,0xad,0x65,0x70,0x4b,0xeb,0x30,0x3b,0x94,0x8b,0x3a,0x81,0xd5,0x45,0xc8,0x12,0x2c,0xf5,0x80,0x11,0x2,0x6b,0xfd,0x58,0x31,0x78,0x2b,0xb7,0xff,0x7a,0x5,0xf0,0x8a,0xe5,0x1f,0xb0,0x6,0xd1,0x5e,0xd9,0x27,0x51,0xe,0xac,0xba,0x28,0x13,0xc5,0x3,0x18,0x49,0xa8,0x14,0x8f,0x7,0xcd,0x8,0xe8,0x53,0x7b,0xec,0x98,0xc6,0xe6,0x69,0xb5,0xbc,0x59,0x90,0x5d,0x9c,0xda,0x37,0xe7,0xb3,0x2a,0xd3,0xb4,0xc,0xf8,0xd6,0xb6,0xfc,0xa6,0x34,0x5c,0x9e,0x72,0x3f,0x16,0xcf,0x19,0x71,0x32,0xf4,0xfa,0xf6,0x7d,0x2e,0x4a,0x24,0xfb,0xe3,0xdd,0x1c,0x0,0x47,0xb9,0x86,0xde,0x96,0xef,0xc3,0x54,0x20,0xa3,0x8e,0x61,0x68,0x6f,0x56,0x5b,0x39,0x84,0xe9,0xdf,0x29,0xd8,0x22,0x23,0x79,0x64,0x93,0xce,0x91,0xbe,0x52,0x40,0xe1,0xae,0x1d,0x17,0xe0,0x35,0x42,0x46,0x2d,0x5a,0x5f,0x48,0xc4,0xa2,0x89,0xa4,0xe4,0x23,0xfc,0x4c,0x66,0xb5,0xfb,0x64,0x63,0x9f,0x3a,0x96,0x74,0xf0,0x3,0x7d,0x3d,0x82,0xd4,0xeb,0xbc,0x97,0x8f,0xcd,0xe1,0x1c,0x6a,0x38,0xb2,0x1f,0xc2,0x13,0x9a,0xf3,0xe0,0x71,0x89,0xc0,0xa9,0xc,0xb4,0x24,0x70,0xcb,0x4,0xdd,0xe3,0x39,0x1a,0xba,0x81,0x94,0x7a,0x65,0xca,0xc1,0x4a,0x92,0x31,0xf8,0x5c,0x26,0x93,0xd7,0x73,0xf5,0xc9,0x2d,0x51,0x6b,0x72,0x43,0x3b,0x91,0x36,0x7c,0xbf,0xa6,0x8e,0xf,0xa4,0x5e,0x56,0xbe,0xfe,0x50,0xa1,0xcf,0x15,0x68,0xd0,0x76,0x8d,0xcc,0x30,0x40,0x87,0xde,0x2,0x54,0x8,0xbd,0x5a,0x84,0x9b,0x33,0x79,0x86,0x4e,0x9c,0xc7,0xfa,0x6c,0x5b,0x25,0x2a,0xea,0x21,0x6,0x0,0x9d,0x58,0x6e,0x1b,0x85,0xef,0xb0,0x49,0x90,0x7f,0x52,0xd1,0xaa,0xa7,0x9e,0x99,0x2f,0x77,0x48,0xb6,0xa5,0x32,0x1e,0x67,0xa,0xd5,0xbb,0xdf,0xf1,0xed,0x2c,0x12,0xc3,0x80,0xe8,0x3e,0x8c,0x7,0xb,0x5,0xb9,0xae,0xab,0xdc,0x55,0x78,0x53,0x35,0xe6,0xec,0x5f,0x10,0xb7,0xb3,0xc4,0x11,0x3f,0x62,0x95,0x88,0xb1,0xa3,0x4f,0x60,0x2e,0x18,0x75,0xc8,0xd2,0xd3,0x29,0xd8,0x7e,0xe5,0x59,0xb8,0x19,0xf9,0x3c,0xf6,0xd9,0x4b,0x5d,0xff,0xe9,0xf2,0x34,0xe2,0x20,0xf7,0x41,0xee,0xa0,0xd6,0x28,0xaf,0x8b,0xe,0x46,0xda,0x14,0x7b,0x1,0xf4,0xad,0xc5,0x57,0xd,0xe7,0xce,0x83,0x6f,0x45,0x22,0xdb,0x42,0x47,0x27,0x9,0xfd,0xac,0x61,0xa8,0x4d,0x16,0xc6,0x2b,0x6d,0x69,0x1d,0x8a,0xa2,0x44,0x98,0x17,0x37,0x7f,0xdd,0xfb,0x69,0x16,0xc0,0xcb,0xd0,0x7b,0x9a,0x5c,0xc7,0x1e,0xd4,0x3b,0xdb,0x64,0xf8,0xa9,0x2c,0x23,0xd6,0x36,0x59,0x63,0xcc,0x2,0xd5,0xa,0x8d,0x82,0xf4,0xf9,0x60,0x67,0x0,0x2b,0xdf,0x65,0x5,0x75,0x2f,0x8f,0xe7,0xa1,0x4d,0xc5,0xec,0xa8,0x80,0x4b,0x3f,0x35,0x15,0x66,0xba,0x8a,0x6f,0x8e,0x43,0x9,0x4f,0x34,0xe4,0x6a,0x94,0xd,0x55,0x3c,0x45,0x87,0x10,0x70,0xf3,0xb2,0x5d,0xbc,0xbb,0x88,0x85,0xca,0x1c,0xe1,0xa2,0x29,0x27,0xae,0x25,0x99,0xfd,0x28,0xf7,0xe,0x30,0xd3,0xcf,0x7d,0x32,0xc4,0xce,0xe6,0x33,0x95,0x91,0x89,0xfe,0x9b,0x8c,0x71,0x17,0x77,0x5a,0x57,0xea,0xc,0x3a,0xb,0xfa,0xf0,0xf1,0xb7,0xaa,0x1d,0x40,0x6d,0x42,0x93,0x81,0x14,0x5e,0x19,0xb3,0xac,0x2d,0x9d,0x84,0xeb,0xf,0x51,0xd7,0x50,0x61,0x73,0x49,0xf2,0x54,0x37,0x4a,0x12,0x62,0xaf,0xee,0x74,0x9c,0x86,0x7c,0x83,0xed,0xdc,0x72,0x5b,0xa4,0xb9,0x11,0xe5,0xd8,0x6c,0xbe,0x20,0x76,0xa5,0xfc,0x78,0xa6,0x2a,0x9f,0x4c,0x39,0xbf,0x7a,0x92,0x6b,0xa7,0xcd,0x7,0x8,0x4e,0x79,0x24,0x22,0xc8,0x3,0x18,0xb4,0x41,0xbd,0x21,0x5f,0x56,0xd2,0xde,0x6e,0xc6,0x1,0xd9,0x46,0x44,0x97,0x48,0x1a,0xc3,0x3e,0xe0,0x31,0x90,0x3d,0xf6,0xc9,0x1f,0xa0,0xad,0xef,0x9e,0xb5,0x52,0xe9,0x96,0x6,0xc1,0x1b,0x26,0xff,0xc2,0x53,0xb8,0xd1,0x8b,0x2e,0xab,0xe2,0x13,0xda,0x68,0xb0,0xb1,0xf5,0x7e,0x4,0xa3,0xb6,0x38,0x98,0xe8,0xe3,0x58,0x47,0x97,0x6b,0x9e,0x32,0xf8,0x7c,0x75,0xb,0x2b,0xec,0x44,0xf4,0xbd,0x6e,0x6c,0xf3,0x14,0xe9,0x30,0x62,0x17,0xba,0x1b,0xca,0x8a,0x35,0xe3,0xdc,0x9f,0xb4,0xc5,0x87,0x2c,0xbc,0xc3,0x78,0xd5,0xc,0x31,0xeb,0xfb,0x92,0x79,0xe8,0xc8,0x81,0x4,0xa1,0x9a,0x42,0xf0,0x39,0x2e,0x54,0xdf,0x9b,0xb2,0x12,0x9c,0x89,0x6d,0x72,0xc9,0xc2,0x99,0x33,0x74,0x3e,0xae,0xb7,0x7,0x86,0xfd,0x7b,0x25,0xc1,0x63,0x59,0x4b,0x7a,0x60,0x1d,0x7e,0xd8,0xc4,0x85,0x48,0x38,0x56,0xac,0xb6,0x5e,0x58,0xf6,0xc7,0xa9,0x3b,0x93,0x8e,0x71,0x94,0x46,0xf2,0xcf,0xd6,0x8f,0x5c,0xa,0xb5,0x0,0x8c,0x52,0x50,0x95,0x13,0x66,0xe7,0x8d,0x41,0xb8,0x53,0x64,0x22,0x2d,0x29,0xe2,0x8,0xe,0x7f,0x27,0xbe,0x40,0x3a,0xad,0x6f,0x16,0x77,0x98,0xd9,0x5a,0xaf,0xa2,0x91,0x96,0x88,0xcb,0x36,0xe0,0xf,0x84,0xd,0x3,0xdd,0x2,0xd7,0xb3,0xe5,0xf9,0x1a,0x24,0xe4,0xee,0x18,0x57,0xbb,0xbf,0x19,0xcc,0xa6,0xb1,0xd4,0xa3,0x70,0x5d,0x3d,0x5b,0x10,0x26,0xc0,0x7d,0xdb,0xda,0xd0,0x21,0x6a,0x37,0x80,0x9d,0xab,0xb9,0x68,0x47,0x43,0xd1,0xf7,0x55,0xfa,0xe1,0xea,0x3c,0xed,0x76,0xb0,0x51,0xf1,0x11,0xfe,0x34,0x6,0x83,0xd2,0x4e,0x73,0x1c,0xfc,0x9,0xff,0x28,0xe6,0x49,0xde,0xa8,0xa7,0x20,0x2a,0x4d,0x4a,0xd3,0x2f,0x4f,0xf5,0x1,0xcd,0xa5,0x5,0x5f,0xc6,0xef,0x67,0x8b,0x15,0x61,0xaa,0x82,0x90,0x4c,0x3f,0x1f,0x69,0xa4,0x45,0xa0,0xce,0x1e,0x65,0x23,0x6a,0xcc,0x74,0x9,0x5c,0x2c,0xd0,0x91,0xa2,0x4a,0x42,0xb8,0xd3,0xbd,0x4c,0xe2,0x60,0x2a,0x8d,0x27,0x13,0x92,0xba,0xa3,0x31,0xd5,0xe9,0x6f,0x5f,0x6e,0x77,0x4d,0x7,0x72,0x44,0x81,0x55,0xac,0xf3,0x99,0x36,0x39,0x47,0x70,0x1c,0x1a,0x3d,0xf6,0x9a,0x65,0x2f,0x87,0xe6,0xdb,0x80,0x52,0x48,0x1e,0xc2,0x9b,0x98,0x46,0xa1,0x14,0x24,0x76,0x0,0xfd,0xf,0xde,0x3,0xae,0xf7,0xc8,0x9e,0x21,0xd1,0x93,0x8b,0xa0,0x8a,0x26,0x83,0x7f,0x61,0x1f,0xec,0x68,0x50,0xe0,0x3f,0xf8,0x78,0xe7,0xa9,0x7a,0xe4,0x2d,0x8e,0x56,0xcb,0x8f,0x3a,0x40,0x88,0x9d,0xa6,0x6,0xdd,0xd6,0x79,0x66,0xd7,0x6c,0x38,0xa8,0x25,0xff,0xc1,0x18,0x6d,0xfc,0xef,0x86,0x10,0xb5,0xdc,0x95,0xc6,0x5a,0x12,0x97,0xe8,0x1d,0x67,0x8,0xf2,0x5d,0xeb,0x3c,0xb3,0x34,0xca,0xbc,0xe3,0x41,0x57,0xc5,0xfe,0x28,0xee,0xf5,0xa4,0x45,0xf9,0x62,0xea,0x20,0xe5,0x5,0xbe,0x96,0x1,0x75,0x2b,0xb,0x84,0x58,0x51,0xb4,0x7d,0xb0,0x71,0x37,0xda,0xa,0x5e,0xc7,0x3e,0x59,0xe1,0x15,0x3b,0x5b,0x11,0x4b,0xd9,0xb1,0x73,0x9f,0xd2,0xfb,0x22,0xf4,0x9c,0xdf,0x19,0x17,0x1b,0x90,0xc3,0xa7,0xc9,0x16,0xe,0x30,0xf1,0xed,0xaa,0x54,0x6b,0x33,0x7b,0x2,0x2e,0xb9,0xcd,0x4e,0x63,0x8c,0x85,0x82,0xbb,0xb6,0xd4,0x69,0x4,0x32,0xc4,0x35,0xcf,0xce,0x94,0x89,0x7e,0x23,0x7c,0x53,0xbf,0xad,0xc,0x43,0xf0,0xfa,0xd,0xd8,0xaf,0xab,0xc0,0xb7,0xb2,0xa5,0x29,0x4f,0x64,0x49,0x8e,0x49,0x96,0x26,0xc,0xdf,0x91,0xe,0x9,0xf5,0x50,0xfc,0x1e,0x9a,0x69,0x17,0x57,0xe8,0xbe,0x81,0xd6,0xfd,0xe5,0xa7,0x8b,0x76,0x0,0x52,0xd8,0x75,0xa8,0x79,0xf0,0x99,0x8a,0x1b,0xe3,0xaa,0xc3,0x66,0xde,0x4e,0x1a,0xa1,0x6e,0xb7,0x89,0x53,0x70,0xd0,0xeb,0xfe,0x10,0xf,0xa0,0xab,0x20,0xf8,0x5b,0x92,0x36,0x4c,0xf9,0xbd,0x19,0x9f,0xa3,0x47,0x3b,0x1,0x18,0x29,0x51,0xfb,0x5c,0x16,0xd5,0xcc,0xe4,0x65,0xce,0x34,0x3c,0xd4,0x94,0x3a,0xcb,0xa5,0x7f,0x2,0xba,0x1c,0xe7,0xa6,0x5a,0x2a,0xed,0xb4,0x68,0x3e,0x62,0xd7,0x30,0xee,0xf1,0x59,0x13,0xec,0x24,0xf6,0xad,0x90,0x6,0x31,0x4f,0x40,0x80,0x4b,0x6c,0x6a,0xf7,0x32,0x4,0x71,0xef,0x85,0xda,0x23,0xfa,0x15,0x38,0xbb,0xc0,0xcd,0xf4,0xf3,0x45,0x1d,0x22,0xdc,0xcf,0x58,0x74,0xd,0x60,0xbf,0xd1,0xb5,0x9b,0x87,0x46,0x78,0xa9,0xea,0x82,0x54,0xe6,0x6d,0x61,0x6f,0xd3,0xc4,0xc1,0xb6,0x3f,0x12,0x39,0x5f,0x8c,0x86,0x35,0x7a,0xdd,0xd9,0xae,0x7b,0x55,0x8,0xff,0xe2,0xdb,0xc9,0x25,0xa,0x44,0x72,0x1f,0xa2,0xb8,0xb9,0x43,0xb2,0x14,0x8f,0x33,0xd2,0x73,0x93,0x56,0x9c,0xb3,0x21,0x37,0x95,0x83,0x98,0x5e,0x88,0x4a,0x9d,0x2b,0x84,0xca,0xbc,0x42,0xc5,0xe1,0x64,0x2c,0xb0,0x7e,0x11,0x6b,0x9e,0xc7,0xaf,0x3d,0x67,0x8d,0xa4,0xe9,0x5,0x2f,0x48,0xb1,0x28,0x2d,0x4d,0x63,0x97,0xc6,0xb,0xc2,0x27,0x7c,0xac,0x41,0x7,0x3,0x77,0xe0,0xc8,0x2e,0xf2,0x7d,0x5d,0xa4,0x6,0x20,0xb2,0xcd,0x1b,0x10,0xb,0xa0,0x41,0x87,0x1c,0xc5,0xf,0xe0,0x0,0xbf,0x23,0x72,0xf7,0xf8,0xd,0xed,0x82,0xb8,0x17,0xd9,0xe,0xd1,0x56,0x59,0x2f,0x22,0xbb,0xbc,0xdb,0xf0,0x4,0xbe,0xde,0xae,0xf4,0x54,0x3c,0x7a,0x96,0x1e,0x37,0x73,0x5b,0x90,0xe4,0xee,0xce,0xbd,0x61,0x51,0xb4,0x55,0x98,0xd2,0x94,0xef,0x3f,0xb1,0x4f,0xd6,0x8e,0xe7,0x9e,0x5c,0xcb,0xab,0x28,0x69,0x86,0x67,0x60,0x53,0x5e,0x11,0xc7,0x3a,0x79,0xf2,0xfc,0x75,0xfe,0x42,0x26,0xf3,0x2c,0xd5,0xeb,0x8,0x14,0xa6,0xe9,0x1f,0x15,0x3d,0xe8,0x4e,0x4a,0x52,0x25,0x40,0x57,0xaa,0xcc,0xac,0x81,0x8c,0x31,0xd7,0xe1,0xd0,0x21,0x2b,0x2a,0x6c,0x71,0xc6,0x9b,0xb6,0x99,0x48,0x5a,0xcf,0x85,0xc2,0x68,0x77,0xf6,0x46,0x5f,0x30,0xd4,0x8a,0xc,0x8b,0xba,0xa8,0x92,0x29,0x8f,0xec,0x91,0xc9,0xb9,0x74,0x35,0xaf,0x47,0x5d,0xa7,0x58,0x36,0x7,0xa9,0x80,0x7f,0x62,0xca,0x3e,0x3,0xb7,0x65,0xfb,0xad,0x7e,0x27,0xa3,0x7d,0xf1,0x44,0x97,0xe2,0x64,0xa1,0x49,0xb0,0x7c,0x16,0xdc,0xd3,0x95,0xa2,0xff,0xf9,0x13,0xd8,0xc3,0x6f,0x9a,0x66,0xfa,0x84,0x8d,0x9,0x5,0xb5,0x1d,0xda,0x2,0x9d,0x9f,0x4c,0x93,0xc1,0x18,0xe5,0x3b,0xea,0x4b,0xe6,0x2d,0x12,0xc4,0x7b,0x76,0x34,0x45,0x6e,0x89,0x32,0x4d,0xdd,0x1a,0xc0,0xfd,0x24,0x19,0x88,0x63,0xa,0x50,0xf5,0x70,0x39,0xc8,0x1,0xb3,0x6b,0x6a,0x2e,0xa5,0xdf,0x78,0x6d,0xe3,0x43,0x33,0x38,0x83,0x9c,0x6f,0x93,0x66,0xca,0x0,0x84,0x8d,0xf3,0xd3,0x14,0xbc,0xc,0x45,0x96,0x94,0xb,0xec,0x11,0xc8,0x9a,0xef,0x42,0xe3,0x32,0x72,0xcd,0x1b,0x24,0x67,0x4c,0x3d,0x7f,0xd4,0x44,0x3b,0x80,0x2d,0xf4,0xc9,0x13,0x3,0x6a,0x81,0x10,0x30,0x79,0xfc,0x59,0x62,0xba,0x8,0xc1,0xd6,0xac,0x27,0x63,0x4a,0xea,0x64,0x71,0x95,0x8a,0x31,0x3a,0x61,0xcb,0x8c,0xc6,0x56,0x4f,0xff,0x7e,0x5,0x83,0xdd,0x39,0x9b,0xa1,0xb3,0x82,0x98,0xe5,0x86,0x20,0x3c,0x7d,0xb0,0xc0,0xae,0x54,0x4e,0xa6,0xa0,0xe,0x3f,0x51,0xc3,0x6b,0x76,0x89,0x6c,0xbe,0xa,0x37,0x2e,0x77,0xa4,0xf2,0x4d,0xf8,0x74,0xaa,0xa8,0x6d,0xeb,0x9e,0x1f,0x75,0xb9,0x40,0xab,0x9c,0xda,0xd5,0xd1,0x1a,0xf0,0xf6,0x87,0xdf,0x46,0xb8,0xc2,0x55,0x97,0xee,0x8f,0x60,0x21,0xa2,0x57,0x5a,0x69,0x6e,0x70,0x33,0xce,0x18,0xf7,0x7c,0xf5,0xfb,0x25,0xfa,0x2f,0x4b,0x1d,0x1,0xe2,0xdc,0x1c,0x16,0xe0,0xaf,0x43,0x47,0xe1,0x34,0x5e,0x49,0x2c,0x5b,0x88,0xa5,0xc5,0xa3,0xe8,0xde,0x38,0x85,0x23,0x22,0x28,0xd9,0x92,0xcf,0x78,0x65,0x53,0x41,0x90,0xbf,0xbb,0x29,0xf,0xad,0x2,0x19,0x12,0xc4,0x15,0x8e,0x48,0xa9,0x9,0xe9,0x6,0xcc,0xfe,0x7b,0x2a,0xb6,0x8b,0xe4,0x4,0xf1,0x7,0xd0,0x1e,0xb1,0x26,0x50,0x5f,0xd8,0xd2,0xb5,0xb2,0x2b,0xd7,0xb7,0xd,0xf9,0x35,0x5d,0xfd,0xa7,0x3e,0x17,0x9f,0x73,0xed,0x99,0x52,0x7a,0x68,0xb4,0xc7,0xe7,0x91,0x5c,0xbd,0x58,0x36,0xe6,0x9d,0xdb,0x96,0x30,0x88,0xf5,0xa0,0xd0,0x2c,0x6d,0x5e,0xb6,0xbe,0x44,0x2f,0x41,0xb0,0x1e,0x9c,0xd6,0x71,0xdb,0xef,0x6e,0x46,0x5f,0xcd,0x29,0x15,0x93,0xa3,0x92,0x8b,0xb1,0xfb,0x8e,0xb8,0x7d,0xa9,0x50,0xf,0x65,0xca,0xc5,0xbb,0x8c,0xe0,0xe6,0xc1,0xa,0x66,0x99,0xd3,0x7b,0x1a,0x27,0x7c,0xae,0xb4,0xe2,0x3e,0x67,0x64,0xba,0x5d,0xe8,0xd8,0x8a,0xfc,0x1,0xf3,0x22,0xff,0x52,0xb,0x34,0x62,0xdd,0x2d,0x6f,0x77,0x5c,0x76,0xda,0x7f,0x83,0x9d,0xe3,0x10,0x94,0xac,0x1c,0xc3,0x4,0x84,0x1b,0x55,0x86,0x18,0xd1,0x72,0xaa,0x37,0x73,0xc6,0xbc,0x74,0x61,0x5a,0xfa,0x21,0x2a,0x85,0x9a,0x2b,0x90,0xc4,0x54,0xd9,0x3,0x3d,0xe4,0x91,0x0,0x13,0x7a,0xec,0x49,0x20,0x69,0x3a,0xa6,0xee,0x6b,0x14,0xe1,0x9b,0xf4,0xe,0xa1,0x17,0xc0,0x4f,0xc8,0x36,0x40,0x1f,0xbd,0xab,0x39,0x2,0xd4,0x12,0x9,0x58,0xb9,0x5,0x9e,0x16,0xdc,0x19,0xf9,0x42,0x6a,0xfd,0x89,0xd7,0xf7,0x78,0xa4,0xad,0x48,0x81,0x4c,0x8d,0xcb,0x26,0xf6,0xa2,0x3b,0xc2,0xa5,0x1d,0xe9,0xc7,0xa7,0xed,0xb7,0x25,0x4d,0x8f,0x63,0x2e,0x7,0xde,0x8,0x60,0x23,0xe5,0xeb,0xe7,0x6c,0x3f,0x5b,0x35,0xea,0xf2,0xcc,0xd,0x11,0x56,0xa8,0x97,0xcf,0x87,0xfe,0xd2,0x45,0x31,0xb2,0x9f,0x70,0x79,0x7e,0x47,0x4a,0x28,0x95,0xf8,0xce,0x38,0xc9,0x33,0x32,0x68,0x75,0x82,0xdf,0x80,0xaf,0x43,0x51,0xf0,0xbf,0xc,0x6,0xf1,0x24,0x53,0x57,0x3c,0x4b,0x4e,0x59,0xd5,0xb3,0x98,0xb5,0x81,0x46,0x99,0x29,0x3,0xd0,0x9e,0x1,0x6,0xfa,0x5f,0xf3,0x11,0x95,0x66,0x18,0x58,0xe7,0xb1,0x8e,0xd9,0xf2,0xea,0xa8,0x84,0x79,0xf,0x5d,0xd7,0x7a,0xa7,0x76,0xff,0x96,0x85,0x14,0xec,0xa5,0xcc,0x69,0xd1,0x41,0x15,0xae,0x61,0xb8,0x86,0x5c,0x7f,0xdf,0xe4,0xf1,0x1f,0x0,0xaf,0xa4,0x2f,0xf7,0x54,0x9d,0x39,0x43,0xf6,0xb2,0x16,0x90,0xac,0x48,0x34,0xe,0x17,0x26,0x5e,0xf4,0x53,0x19,0xda,0xc3,0xeb,0x6a,0xc1,0x3b,0x33,0xdb,0x9b,0x35,0xc4,0xaa,0x70,0xd,0xb5,0x13,0xe8,0xa9,0x55,0x25,0xe2,0xbb,0x67,0x31,0x6d,0xd8,0x3f,0xe1,0xfe,0x56,0x1c,0xe3,0x2b,0xf9,0xa2,0x9f,0x9,0x3e,0x40,0x4f,0x8f,0x44,0x63,0x65,0xf8,0x3d,0xb,0x7e,0xe0,0x8a,0xd5,0x2c,0xf5,0x1a,0x37,0xb4,0xcf,0xc2,0xfb,0xfc,0x4a,0x12,0x2d,0xd3,0xc0,0x57,0x7b,0x2,0x6f,0xb0,0xde,0xba,0x94,0x88,0x49,0x77,0xa6,0xe5,0x8d,0x5b,0xe9,0x62,0x6e,0x60,0xdc,0xcb,0xce,0xb9,0x30,0x1d,0x36,0x50,0x83,0x89,0x3a,0x75,0xd2,0xd6,0xa1,0x74,0x5a,0x7,0xf0,0xed,0xd4,0xc6,0x2a,0x5,0x4b,0x7d,0x10,0xad,0xb7,0xb6,0x4c,0xbd,0x1b,0x80,0x3c,0xdd,0x7c,0x9c,0x59,0x93,0xbc,0x2e,0x38,0x9a,0x8c,0x97,0x51,0x87,0x45,0x92,0x24,0x8b,0xc5,0xb3,0x4d,0xca,0xee,0x6b,0x23,0xbf,0x71,0x1e,0x64,0x91,0xc8,0xa0,0x32,0x68,0x82,0xab,0xe6,0xa,0x20,0x47,0xbe,0x27,0x22,0x42,0x6c,0x98,0xc9,0x4,0xcd,0x28,0x73,0xa3,0x4e,0x8,0xc,0x78,0xef,0xc7,0x21,0xfd,0x72,0x52,0xdf,0x7d,0x5b,0xc9,0xb6,0x60,0x6b,0x70,0xdb,0x3a,0xfc,0x67,0xbe,0x74,0x9b,0x7b,0xc4,0x58,0x9,0x8c,0x83,0x76,0x96,0xf9,0xc3,0x6c,0xa2,0x75,0xaa,0x2d,0x22,0x54,0x59,0xc0,0xc7,0xa0,0x8b,0x7f,0xc5,0xa5,0xd5,0x8f,0x2f,0x47,0x1,0xed,0x65,0x4c,0x8,0x20,0xeb,0x9f,0x95,0xb5,0xc6,0x1a,0x2a,0xcf,0x2e,0xe3,0xa9,0xef,0x94,0x44,0xca,0x34,0xad,0xf5,0x9c,0xe5,0x27,0xb0,0xd0,0x53,0x12,0xfd,0x1c,0x1b,0x28,0x25,0x6a,0xbc,0x41,0x2,0x89,0x87,0xe,0x85,0x39,0x5d,0x88,0x57,0xae,0x90,0x73,0x6f,0xdd,0x92,0x64,0x6e,0x46,0x93,0x35,0x31,0x29,0x5e,0x3b,0x2c,0xd1,0xb7,0xd7,0xfa,0xf7,0x4a,0xac,0x9a,0xab,0x5a,0x50,0x51,0x17,0xa,0xbd,0xe0,0xcd,0xe2,0x33,0x21,0xb4,0xfe,0xb9,0x13,0xc,0x8d,0x3d,0x24,0x4b,0xaf,0xf1,0x77,0xf0,0xc1,0xd3,0xe9,0x52,0xf4,0x97,0xea,0xb2,0xc2,0xf,0x4e,0xd4,0x3c,0x26,0xdc,0x23,0x4d,0x7c,0xd2,0xfb,0x4,0x19,0xb1,0x45,0x78,0xcc,0x1e,0x80,0xd6,0x5,0x5c,0xd8,0x6,0x8a,0x3f,0xec,0x99,0x1f,0xda,0x32,0xcb,0x7,0x6d,0xa7,0xa8,0xee,0xd9,0x84,0x82,0x68,0xa3,0xb8,0x14,0xe1,0x1d,0x81,0xff,0xf6,0x72,0x7e,0xce,0x66,0xa1,0x79,0xe6,0xe4,0x37,0xe8,0xba,0x63,0x9e,0x40,0x91,0x30,0x9d,0x56,0x69,0xbf,0x0,0xd,0x4f,0x3e,0x15,0xf2,0x49,0x36,0xa6,0x61,0xbb,0x86,0x5f,0x62,0xf3,0x18,0x71,0x2b,0x8e,0xb,0x42,0xb3,0x7a,0xc8,0x10,0x11,0x55,0xde,0xa4,0x3,0x16,0x98,0x38,0x48,0x43,0xf8,0xe7,0x11,0xed,0x18,0xb4,0x7e,0xfa,0xf3,0x8d,0xad,0x6a,0xc2,0x72,0x3b,0xe8,0xea,0x75,0x92,0x6f,0xb6,0xe4,0x91,0x3c,0x9d,0x4c,0xc,0xb3,0x65,0x5a,0x19,0x32,0x43,0x1,0xaa,0x3a,0x45,0xfe,0x53,0x8a,0xb7,0x6d,0x7d,0x14,0xff,0x6e,0x4e,0x7,0x82,0x27,0x1c,0xc4,0x76,0xbf,0xa8,0xd2,0x59,0x1d,0x34,0x94,0x1a,0xf,0xeb,0xf4,0x4f,0x44,0x1f,0xb5,0xf2,0xb8,0x28,0x31,0x81,0x0,0x7b,0xfd,0xa3,0x47,0xe5,0xdf,0xcd,0xfc,0xe6,0x9b,0xf8,0x5e,0x42,0x3,0xce,0xbe,0xd0,0x2a,0x30,0xd8,0xde,0x70,0x41,0x2f,0xbd,0x15,0x8,0xf7,0x12,0xc0,0x74,0x49,0x50,0x9,0xda,0x8c,0x33,0x86,0xa,0xd4,0xd6,0x13,0x95,0xe0,0x61,0xb,0xc7,0x3e,0xd5,0xe2,0xa4,0xab,0xaf,0x64,0x8e,0x88,0xf9,0xa1,0x38,0xc6,0xbc,0x2b,0xe9,0x90,0xf1,0x1e,0x5f,0xdc,0x29,0x24,0x17,0x10,0xe,0x4d,0xb0,0x66,0x89,0x2,0x8b,0x85,0x5b,0x84,0x51,0x35,0x63,0x7f,0x9c,0xa2,0x62,0x68,0x9e,0xd1,0x3d,0x39,0x9f,0x4a,0x20,0x37,0x52,0x25,0xf6,0xdb,0xbb,0xdd,0x96,0xa0,0x46,0xfb,0x5d,0x5c,0x56,0xa7,0xec,0xb1,0x6,0x1b,0x2d,0x3f,0xee,0xc1,0xc5,0x57,0x71,0xd3,0x7c,0x67,0x6c,0xba,0x6b,0xf0,0x36,0xd7,0x77,0x97,0x78,0xb2,0x80,0x5,0x54,0xc8,0xf5,0x9a,0x7a,0x8f,0x79,0xae,0x60,0xcf,0x58,0x2e,0x21,0xa6,0xac,0xcb,0xcc,0x55,0xa9,0xc9,0x73,0x87,0x4b,0x23,0x83,0xd9,0x40,0x69,0xe1,0xd,0x93,0xe7,0x2c,0x4,0x16,0xca,0xb9,0x99,0xef,0x22,0xc3,0x26,0x48,0x98,0xe3,0xa5,0x81,0x27,0x9f,0xe2,0xb7,0xc7,0x3b,0x7a,0x49,0xa1,0xa9,0x53,0x38,0x56,0xa7,0x9,0x8b,0xc1,0x66,0xcc,0xf8,0x79,0x51,0x48,0xda,0x3e,0x2,0x84,0xb4,0x85,0x9c,0xa6,0xec,0x99,0xaf,0x6a,0xbe,0x47,0x18,0x72,0xdd,0xd2,0xac,0x9b,0xf7,0xf1,0xd6,0x1d,0x71,0x8e,0xc4,0x6c,0xd,0x30,0x6b,0xb9,0xa3,0xf5,0x29,0x70,0x73,0xad,0x4a,0xff,0xcf,0x9d,0xeb,0x16,0xe4,0x35,0xe8,0x45,0x1c,0x23,0x75,0xca,0x3a,0x78,0x60,0x4b,0x61,0xcd,0x68,0x94,0x8a,0xf4,0x7,0x83,0xbb,0xb,0xd4,0x13,0x93,0xc,0x42,0x91,0xf,0xc6,0x65,0xbd,0x20,0x64,0xd1,0xab,0x63,0x76,0x4d,0xed,0x36,0x3d,0x92,0x8d,0x3c,0x87,0xd3,0x43,0xce,0x14,0x2a,0xf3,0x86,0x17,0x4,0x6d,0xfb,0x5e,0x37,0x7e,0x2d,0xb1,0xf9,0x7c,0x3,0xf6,0x8c,0xe3,0x19,0xb6,0x0,0xd7,0x58,0xdf,0x21,0x57,0x8,0xaa,0xbc,0x2e,0x15,0xc3,0x5,0x1e,0x4f,0xae,0x12,0x89,0x1,0xcb,0xe,0xee,0x55,0x7d,0xea,0x9e,0xc0,0xe0,0x6f,0xb3,0xba,0x5f,0x96,0x5b,0x9a,0xdc,0x31,0xe1,0xb5,0x2c,0xd5,0xb2,0xa,0xfe,0xd0,0xb0,0xfa,0xa0,0x32,0x5a,0x98,0x74,0x39,0x10,0xc9,0x1f,0x77,0x34,0xf2,0xfc,0xf0,0x7b,0x28,0x4c,0x22,0xfd,0xe5,0xdb,0x1a,0x6,0x41,0xbf,0x80,0xd8,0x90,0xe9,0xc5,0x52,0x26,0xa5,0x88,0x67,0x6e,0x69,0x50,0x5d,0x3f,0x82,0xef,0xd9,0x2f,0xde,0x24,0x25,0x7f,0x62,0x95,0xc8,0x97,0xb8,0x54,0x46,0xe7,0xa8,0x1b,0x11,0xe6,0x33,0x44,0x40,0x2b,0x5c,0x59,0x4e,0xc2,0xa4,0x8f,0xa2,0xe4,0x23,0xfc,0x4c,0x66,0xb5,0xfb,0x64,0x63,0x9f,0x3a,0x96,0x74,0xf0,0x3,0x7d,0x3d,0x82,0xd4,0xeb,0xbc,0x97,0x8f,0xcd,0xe1,0x1c,0x6a,0x38,0xb2,0x1f,0xc2,0x13,0x9a,0xf3,0xe0,0x71,0x89,0xc0,0xa9,0xc,0xb4,0x24,0x70,0xcb,0x4,0xdd,0xe3,0x39,0x1a,0xba,0x81,0x94,0x7a,0x65,0xca,0xc1,0x4a,0x92,0x31,0xf8,0x5c,0x26,0x93,0xd7,0x73,0xf5,0xc9,0x2d,0x51,0x6b,0x72,0x43,0x3b,0x91,0x36,0x7c,0xbf,0xa6,0x8e,0xf,0xa4,0x5e,0x56,0xbe,0xfe,0x50,0xa1,0xcf,0x15,0x68,0xd0,0x76,0x8d,0xcc,0x30,0x40,0x87,0xde,0x2,0x54,0x8,0xbd,0x5a,0x84,0x9b,0x33,0x79,0x86,0x4e,0x9c,0xc7,0xfa,0x6c,0x5b,0x25,0x2a,0xea,0x21,0x6,0x0,0x9d,0x58,0x6e,0x1b,0x85,0xef,0xb0,0x49,0x90,0x7f,0x52,0xd1,0xaa,0xa7,0x9e,0x99,0x2f,0x77,0x48,0xb6,0xa5,0x32,0x1e,0x67,0xa,0xd5,0xbb,0xdf,0xf1,0xed,0x2c,0x12,0xc3,0x80,0xe8,0x3e,0x8c,0x7,0xb,0x5,0xb9,0xae,0xab,0xdc,0x55,0x78,0x53,0x35,0xe6,0xec,0x5f,0x10,0xb7,0xb3,0xc4,0x11,0x3f,0x62,0x95,0x88,0xb1,0xa3,0x4f,0x60,0x2e,0x18,0x75,0xc8,0xd2,0xd3,0x29,0xd8,0x7e,0xe5,0x59,0xb8,0x19,0xf9,0x3c,0xf6,0xd9,0x4b,0x5d,0xff,0xe9,0xf2,0x34,0xe2,0x20,0xf7,0x41,0xee,0xa0,0xd6,0x28,0xaf,0x8b,0xe,0x46,0xda,0x14,0x7b,0x1,0xf4,0xad,0xc5,0x57,0xd,0xe7,0xce,0x83,0x6f,0x45,0x22,0xdb,0x42,0x47,0x27,0x9,0xfd,0xac,0x61,0xa8,0x4d,0x16,0xc6,0x2b,0x6d,0x69,0x1d,0x8a,0xa2,0x44,0x98,0x17,0x37,0x71,0x7b,0x36,0x27,0xc5,0xa8,0x68,0x97,0x52,0x8d,0x62,0xdc,0xba,0xb8,0xcc,0x24,0x32,0xaf,0x2a,0xfc,0x1d,0x60,0x77,0x6f,0xdf,0x2f,0x61,0x16,0x1e,0x47,0xc,0xfa,0xca,0x44,0xe8,0x57,0xb4,0x1b,0x14,0x5f,0xf6,0xda,0x1,0x91,0x3e,0xa7,0x4a,0xe1,0x33,0xa2,0xe7,0x6e,0x41,0xa3,0xbb,0xd3,0x51,0x3,0xeb,0xf8,0xc8,0x30,0x8b,0xe,0xdb,0x9,0xa6,0x67,0x80,0xd5,0x9a,0x4f,0x3b,0x95,0xe4,0x9b,0x74,0x18,0xe0,0xde,0x2b,0x88,0xf3,0x83,0xaa,0x21,0x59,0x15,0x96,0xa5,0x5b,0x85,0x75,0x58,0xbd,0x54,0x39,0xf1,0xf2,0x84,0x7f,0x3d,0x3f,0x9e,0x22,0x5e,0x23,0x43,0x93,0x49,0x20,0x25,0x17,0xfb,0x3c,0xa9,0x76,0xc1,0x4,0x4d,0xa,0xff,0x65,0x50,0x35,0xc6,0x4e,0xe3,0x38,0x92,0x5,0xcb,0xc7,0x7,0xd7,0x29,0x6b,0x5c,0xd,0x56,0xdd,0x26,0x82,0x9f,0xbc,0x70,0xc2,0xb7,0xc3,0xf5,0xc0,0x42,0x90,0x2c,0x5a,0xbe,0xe9,0x6,0x2,0x7a,0xb1,0x81,0x55,0x98,0xcd,0xd8,0xfe,0x46,0x12,0x73,0xec,0x7e,0x19,0xb5,0x4c,0xc4,0x28,0xc9,0xb9,0x10,0x87,0x6a,0xcf,0x8,0x6d,0x45,0xf4,0x6c,0xf,0x48,0x37,0xef,0xee,0x79,0x2d,0x94,0x8a,0xf9,0x31,0xb0,0x8e,0x9c,0x69,0xfd,0x40,0x7c,0x1a,0x1c,0xb2,0xe5,0xe2,0x13,0xe6,0x8c,0xac,0xb6,0xd6,0x72,0x64,0x63,0xd0,0xea,0x53,0x7d,0x89,0xae,0x0,0x1f,0xab,0xed,0x34,0x11,0xa0,0x86,0xb,0x8f,0xd2,0x3a,0x99,0xa1,0x4b,0xd4,0xf7,0xbf,0xf0,0x66,0x2e,0xce,0x9d,0xd9,0x5d,0xd1,0xad,0xb3,0x78,0xa4,0x4e,0xb2,0x47,0xeb,0x21,0xa5,0xac,0xd2,0xf2,0x35,0x9d,0x2d,0x64,0xb7,0xb5,0x2a,0xcd,0x30,0xe9,0xbb,0xce,0x63,0xc2,0x13,0x53,0xec,0x3a,0x5,0x46,0x6d,0x1c,0x5e,0xf5,0x65,0x1a,0xa1,0xc,0xd5,0xe8,0x32,0x22,0x4b,0xa0,0x31,0x11,0x58,0xdd,0x78,0x43,0x9b,0x29,0xe0,0xf7,0x8d,0x6,0x42,0x6b,0xcb,0x45,0x50,0xb4,0xab,0x10,0x1b,0x40,0xea,0xad,0xe7,0x77,0x6e,0xde,0x5f,0x24,0xa2,0xfc,0x18,0xba,0x80,0x92,0xa3,0xb9,0xc4,0xa7,0x1,0x1d,0x5c,0x91,0xe1,0x8f,0x75,0x6f,0x87,0x81,0x2f,0x1e,0x70,0xe2,0x4a,0x57,0xa8,0x4d,0x9f,0x2b,0x16,0xf,0x56,0x85,0xd3,0x6c,0xd9,0x55,0x8b,0x89,0x4c,0xca,0xbf,0x3e,0x54,0x98,0x61,0x8a,0xbd,0xfb,0xf4,0xf0,0x3b,0xd1,0xd7,0xa6,0xfe,0x67,0x99,0xe3,0x74,0xb6,0xcf,0xae,0x41,0x0,0x83,0x76,0x7b,0x48,0x4f,0x51,0x12,0xef,0x39,0xd6,0x5d,0xd4,0xda,0x4,0xdb,0xe,0x6a,0x3c,0x20,0xc3,0xfd,0x3d,0x37,0xc1,0x8e,0x62,0x66,0xc0,0x15,0x7f,0x68,0xd,0x7a,0xa9,0x84,0xe4,0x82,0xc9,0xff,0x19,0xa4,0x2,0x3,0x9,0xf8,0xb3,0xee,0x59,0x44,0x72,0x60,0xb1,0x9e,0x9a,0x8,0x2e,0x8c,0x23,0x38,0x33,0xe5,0x34,0xaf,0x69,0x88,0x28,0xc8,0x27,0xed,0xdf,0x5a,0xb,0x97,0xaa,0xc5,0x25,0xd0,0x26,0xf1,0x3f,0x90,0x7,0x71,0x7e,0xf9,0xf3,0x94,0x93,0xa,0xf6,0x96,0x2c,0xd8,0x14,0x7c,0xdc,0x86,0x1f,0x36,0xbe,0x52,0xcc,0xb8,0x73,0x5b,0x49,0x95,0xe6,0xc6,0xb0,0x7d,0x9c,0x79,0x17,0xc7,0xbc,0xfa,0x2d,0x8b,0x33,0x4e,0x1b,0x6b,0x97,0xd6,0xe5,0xd,0x5,0xff,0x94,0xfa,0xb,0xa5,0x27,0x6d,0xca,0x60,0x54,0xd5,0xfd,0xe4,0x76,0x92,0xae,0x28,0x18,0x29,0x30,0xa,0x40,0x35,0x3,0xc6,0x12,0xeb,0xb4,0xde,0x71,0x7e,0x0,0x37,0x5b,0x5d,0x7a,0xb1,0xdd,0x22,0x68,0xc0,0xa1,0x9c,0xc7,0x15,0xf,0x59,0x85,0xdc,0xdf,0x1,0xe6,0x53,0x63,0x31,0x47,0xba,0x48,0x99,0x44,0xe9,0xb0,0x8f,0xd9,0x66,0x96,0xd4,0xcc,0xe7,0xcd,0x61,0xc4,0x38,0x26,0x58,0xab,0x2f,0x17,0xa7,0x78,0xbf,0x3f,0xa0,0xee,0x3d,0xa3,0x6a,0xc9,0x11,0x8c,0xc8,0x7d,0x7,0xcf,0xda,0xe1,0x41,0x9a,0x91,0x3e,0x21,0x90,0x2b,0x7f,0xef,0x62,0xb8,0x86,0x5f,0x2a,0xbb,0xa8,0xc1,0x57,0xf2,0x9b,0xd2,0x81,0x1d,0x55,0xd0,0xaf,0x5a,0x20,0x4f,0xb5,0x1a,0xac,0x7b,0xf4,0x73,0x8d,0xfb,0xa4,0x6,0x10,0x82,0xb9,0x6f,0xa9,0xb2,0xe3,0x2,0xbe,0x25,0xad,0x67,0xa2,0x42,0xf9,0xd1,0x46,0x32,0x6c,0x4c,0xc3,0x1f,0x16,0xf3,0x3a,0xf7,0x36,0x70,0x9d,0x4d,0x19,0x80,0x79,0x1e,0xa6,0x52,0x7c,0x1c,0x56,0xc,0x9e,0xf6,0x34,0xd8,0x95,0xbc,0x65,0xb3,0xdb,0x98,0x5e,0x50,0x5c,0xd7,0x84,0xe0,0x8e,0x51,0x49,0x77,0xb6,0xaa,0xed,0x13,0x2c,0x74,0x3c,0x45,0x69,0xfe,0x8a,0x9,0x24,0xcb,0xc2,0xc5,0xfc,0xf1,0x93,0x2e,0x43,0x75,0x83,0x72,0x88,0x89,0xd3,0xce,0x39,0x64,0x3b,0x14,0xf8,0xea,0x4b,0x4,0xb7,0xbd,0x4a,0x9f,0xe8,0xec,0x87,0xf0,0xf5,0xe2,0x6e,0x8,0x23,0xe,0xaf,0xf8,0x59,0xa8,0xc6,0xac,0xfc,0xe6,0x38,0x9c,0x29,0x2e,0xa0,0x9a,0x37,0x19,0x33,0xa4,0xde,0x67,0xb3,0xc0,0xfa,0x7b,0xd6,0xc4,0xb7,0x23,0x36,0xa,0x56,0x50,0x9e,0x1,0xf5,0xbd,0x2c,0xba,0x84,0x64,0x93,0xd7,0x9b,0x17,0xf9,0xe7,0xee,0x32,0xe4,0xc3,0x55,0x4a,0xa7,0xe1,0x5b,0x7e,0xcc,0xea,0xc5,0x41,0x70,0x98,0xeb,0xd3,0x3a,0xf6,0xfd,0x88,0xbf,0x89,0x8,0x8a,0x66,0xda,0xf4,0x10,0x4c,0xa3,0x30,0x48,0xd8,0x72,0x81,0x4f,0x4d,0x8d,0x63,0x9d,0x16,0x21,0x1c,0x47,0x6c,0x97,0xd5,0xc8,0x83,0x62,0x5a,0xf3,0x20,0xcd,0x42,0x85,0xf,0x27,0x26,0xbe,0x2,0x45,0xa5,0x7d,0xcb,0xfb,0xd2,0x1f,0x92,0x87,0xc,0xb4,0x39,0x58,0x34,0xa6,0xff,0x53,0x8e,0x6,0xc2,0x61,0xc9,0xb9,0x6b,0xe0,0x5f,0x13,0xef,0xdc,0xcf,0x11,0x12,0x3f,0x1e,0xf7,0x43,0x91,0x2d,0xec,0x9f,0xca,0x5,0xd0,0xdf,0x71,0xd1,0xae,0x52,0x3e,0x94,0xaa,0xb1,0x5d,0xe3,0x76,0x8b,0x3c,0x7,0x4e,0xb5,0x40,0x1a,0x2f,0x8c,0x7f,0xa9,0x4,0xbb,0x73,0xce,0xb8,0x77,0x35,0xd4,0x75,0x14,0x68,0x9,0x69,0x3,0xd9,0x6f,0x6a,0xe5,0x78,0xb6,0x60,0x2a,0x57,0x25,0x3d,0x65,0x95,0x5c,0x2b,0xd,0x54,0xb0,0x46,0x31,0x3b,0x6d,0x7c,0xe2,0x8f,0xdd,0x22,0xc7,0x18,0x96,0x28,0xf2,0xf0,0x6e,0x86,0xe8,0x79,0x24,0xad,0xe9,0xb,0x99,0xf1,0x49,0x1b,0xb2,0xa1,0x7a,0x82,0x44,0xc1,0xe,0x80,0x1d,0xa2,0x51,0xfe,0x15,0x5e,0x90,0xbc,0xdb,0x4b,0xed,0x74,0xab,0x0,0x33,0x91,0xb7,0x25,0x5a,0x8c,0x87,0x9c,0x37,0xd6,0x10,0x8b,0x52,0x98,0x77,0x97,0x28,0xb4,0xe5,0x60,0x6f,0x9a,0x7a,0x15,0x2f,0x80,0x4e,0x99,0x46,0xc1,0xce,0xb8,0xb5,0x2c,0x2b,0x4c,0x67,0x93,0x29,0x49,0x39,0x63,0xc3,0xab,0xed,0x1,0x89,0xa0,0xe4,0xcc,0x7,0x73,0x79,0x59,0x2a,0xf6,0xc6,0x23,0xc2,0xf,0x45,0x3,0x78,0xa8,0x26,0xd8,0x41,0x19,0x70,0x9,0xcb,0x5c,0x3c,0xbf,0xfe,0x11,0xf0,0xf7,0xc4,0xc9,0x86,0x50,0xad,0xee,0x65,0x6b,0xe2,0x69,0xd5,0xb1,0x64,0xbb,0x42,0x7c,0x9f,0x83,0x31,0x7e,0x88,0x82,0xaa,0x7f,0xd9,0xdd,0xc5,0xb2,0xd7,0xc0,0x3d,0x5b,0x3b,0x16,0x1b,0xa6,0x40,0x76,0x47,0xb6,0xbc,0xbd,0xfb,0xe6,0x51,0xc,0x21,0xe,0xdf,0xcd,0x58,0x12,0x55,0xff,0xe0,0x61,0xd1,0xc8,0xa7,0x43,0x1d,0x9b,0x1c,0x2d,0x3f,0x5,0xbe,0x18,0x7b,0x6,0x5e,0x2e,0xe3,0xa2,0x38,0xd0,0xca,0x30,0xcf,0xa1,0x90,0x3e,0x17,0xe8,0xf5,0x5d,0xa9,0x94,0x20,0xf2,0x6c,0x3a,0xe9,0xb0,0x34,0xea,0x66,0xd3,0x0,0x75,0xf3,0x36,0xde,0x27,0xeb,0x81,0x4b,0x44,0x2,0x35,0x68,0x6e,0x84,0x4f,0x54,0xf8,0xd,0xf1,0x6d,0x13,0x1a,0x9e,0x92,0x22,0x8a,0x4d,0x95,0xa,0x8,0xdb,0x4,0x56,0x8f,0x72,0xac,0x7d,0xdc,0x71,0xba,0x85,0x53,0xec,0xe1,0xa3,0xd2,0xf9,0x1e,0xa5,0xda,0x4a,0x8d,0x57,0x6a,0xb3,0x8e,0x1f,0xf4,0x9d,0xc7,0x62,0xe7,0xae,0x5f,0x96,0x24,0xfc,0xfd,0xb9,0x32,0x48,0xef,0xfa,0x74,0xd4,0xa4,0xaf,0x14,0xb,0xb5,0x49,0xbc,0x10,0xda,0x5e,0x57,0x29,0x9,0xce,0x66,0xd6,0x9f,0x4c,0x4e,0xd1,0x36,0xcb,0x12,0x40,0x35,0x98,0x39,0xe8,0xa8,0x17,0xc1,0xfe,0xbd,0x96,0xe7,0xa5,0xe,0x9e,0xe1,0x5a,0xf7,0x2e,0x13,0xc9,0xd9,0xb0,0x5b,0xca,0xea,0xa3,0x26,0x83,0xb8,0x60,0xd2,0x1b,0xc,0x76,0xfd,0xb9,0x90,0x30,0xbe,0xab,0x4f,0x50,0xeb,0xe0,0xbb,0x11,0x56,0x1c,0x8c,0x95,0x25,0xa4,0xdf,0x59,0x7,0xe3,0x41,0x7b,0x69,0x58,0x42,0x3f,0x5c,0xfa,0xe6,0xa7,0x6a,0x1a,0x74,0x8e,0x94,0x7c,0x7a,0xd4,0xe5,0x8b,0x19,0xb1,0xac,0x53,0xb6,0x64,0xd0,0xed,0xf4,0xad,0x7e,0x28,0x97,0x22,0xae,0x70,0x72,0xb7,0x31,0x44,0xc5,0xaf,0x63,0x9a,0x71,0x46,0x0,0xf,0xb,0xc0,0x2a,0x2c,0x5d,0x5,0x9c,0x62,0x18,0x8f,0x4d,0x34,0x55,0xba,0xfb,0x78,0x8d,0x80,0xb3,0xb4,0xaa,0xe9,0x14,0xc2,0x2d,0xa6,0x2f,0x21,0xff,0x20,0xf5,0x91,0xc7,0xdb,0x38,0x6,0xc6,0xcc,0x3a,0x75,0x99,0x9d,0x3b,0xee,0x84,0x93,0xf6,0x81,0x52,0x7f,0x1f,0x79,0x32,0x4,0xe2,0x5f,0xf9,0xf8,0xf2,0x3,0x48,0x15,0xa2,0xbf,0x89,0x9b,0x4a,0x65,0x61,0xf3,0xd5,0x77,0xd8,0xc3,0xc8,0x1e,0xcf,0x54,0x92,0x73,0xd3,0x33,0xdc,0x16,0x24,0xa1,0xf0,0x6c,0x51,0x3e,0xde,0x2b,0xdd,0xa,0xc4,0x6b,0xfc,0x8a,0x85,0x2,0x8,0x6f,0x68,0xf1,0xd,0x6d,0xd7,0x23,0xef,0x87,0x27,0x7d,0xe4,0xcd,0x45,0xa9,0x37,0x43,0x88,0xa0,0xb2,0x6e,0x1d,0x3d,0x4b,0x86,0x67,0x82,0xec,0x3c,0x47,0x1,0x4c,0x62,0xcf,0xf5,0x7b,0x7c,0xc9,0x6d,0xb3,0xa9,0xf9,0x93,0xfd,0xc,0xad,0xfa,0x5,0x3,0x5f,0x63,0x76,0xe2,0x91,0x83,0x2e,0xaf,0x95,0xe6,0x32,0x8b,0xf1,0x66,0x67,0xbb,0xb2,0xac,0x42,0xce,0x82,0xc6,0x31,0xd1,0xef,0x79,0xe8,0xa0,0x54,0xcb,0x86,0xbe,0xcd,0x25,0x14,0x90,0xbf,0x99,0x2b,0xe,0xb4,0xf2,0x1f,0x0,0x96,0xb1,0x1d,0x65,0xf6,0x19,0x45,0xa1,0x8f,0x33,0xdf,0x5d,0xdc,0xea,0xdd,0xa8,0xa3,0x6f,0x9d,0x80,0xc2,0x39,0x12,0x49,0x74,0x43,0xc8,0x36,0xd8,0x18,0x1a,0xd4,0x27,0x8d,0x28,0xf0,0x10,0x57,0xeb,0x73,0x72,0x5a,0xd0,0x17,0x98,0x75,0xa6,0xf,0x37,0xd6,0x53,0xdb,0x6,0xaa,0xf3,0x61,0xd,0x6c,0xe1,0x59,0xd2,0xc7,0x4a,0x87,0xae,0x9e,0xa2,0x4b,0x6a,0x47,0x44,0x9a,0x89,0xba,0x46,0xa,0xb5,0x3e,0xec,0x9c,0x34,0x97,0xff,0xc1,0x6b,0x7,0xfb,0x84,0x24,0x8a,0x85,0x50,0x9f,0xca,0xb9,0x78,0xc4,0x16,0x51,0xfc,0x2a,0xd9,0x7a,0x4f,0x15,0xe0,0x1b,0x52,0x69,0xde,0x23,0xb6,0x8,0xe4,0x3f,0x3a,0x8c,0x56,0x3c,0x5c,0x3d,0x41,0x20,0x81,0x60,0x22,0xed,0x9b,0x26,0xee,0x13,0xe5,0x1,0x58,0x7e,0x9,0xc0,0x30,0x68,0x70,0x2,0x7f,0x35,0xe3,0x2d,0xb0,0xd3,0x3b,0xa5,0xa7,0x7d,0xc3,0x4d,0x92,0x77,0x88,0xda,0xb7,0x29,0x38,0x6e,0x64,0x94,0x11,0xd7,0x2f,0xf4,0xe7,0x4e,0x1c,0xa4,0xcc,0x5e,0xbc,0xf8,0x71,0x2c,0xbd,0x55,0xfe,0x21,0xb8,0x1e,0x8e,0xe9,0xc5,0xb,0x40,0xab,0x4,0xf7,0x48,0xd5,0x5b,0x67,0xa0,0x7f,0xcf,0xe5,0x36,0x78,0xe7,0xe0,0x1c,0xb9,0x15,0xf7,0x73,0x80,0xfe,0xbe,0x1,0x57,0x68,0x3f,0x14,0xc,0x4e,0x62,0x9f,0xe9,0xbb,0x31,0x9c,0x41,0x90,0x19,0x70,0x63,0xf2,0xa,0x43,0x2a,0x8f,0x37,0xa7,0xf3,0x48,0x87,0x5e,0x60,0xba,0x99,0x39,0x2,0x17,0xf9,0xe6,0x49,0x42,0xc9,0x11,0xb2,0x7b,0xdf,0xa5,0x10,0x54,0xf0,0x76,0x4a,0xae,0xd2,0xe8,0xf1,0xc0,0xb8,0x12,0xb5,0xff,0x3c,0x25,0xd,0x8c,0x27,0xdd,0xd5,0x3d,0x7d,0xd3,0x22,0x4c,0x96,0xeb,0x53,0xf5,0xe,0x4f,0xb3,0xc3,0x4,0x5d,0x81,0xd7,0x8b,0x3e,0xd9,0x7,0x18,0xb0,0xfa,0x5,0xcd,0x1f,0x44,0x79,0xef,0xd8,0xa6,0xa9,0x69,0xa2,0x85,0x83,0x1e,0xdb,0xed,0x98,0x6,0x6c,0x33,0xca,0x13,0xfc,0xd1,0x52,0x29,0x24,0x1d,0x1a,0xac,0xf4,0xcb,0x35,0x26,0xb1,0x9d,0xe4,0x89,0x56,0x38,0x5c,0x72,0x6e,0xaf,0x91,0x40,0x3,0x6b,0xbd,0xf,0x84,0x88,0x86,0x3a,0x2d,0x28,0x5f,0xd6,0xfb,0xd0,0xb6,0x65,0x6f,0xdc,0x93,0x34,0x30,0x47,0x92,0xbc,0xe1,0x16,0xb,0x32,0x20,0xcc,0xe3,0xad,0x9b,0xf6,0x4b,0x51,0x50,0xaa,0x5b,0xfd,0x66,0xda,0x3b,0x9a,0x7a,0xbf,0x75,0x5a,0xc8,0xde,0x7c,0x6a,0x71,0xb7,0x61,0xa3,0x74,0xc2,0x6d,0x23,0x55,0xab,0x2c,0x8,0x8d,0xc5,0x59,0x97,0xf8,0x82,0x77,0x2e,0x46,0xd4,0x8e,0x64,0x4d,0x0,0xec,0xc6,0xa1,0x58,0xc1,0xc4,0xa4,0x8a,0x7e,0x2f,0xe2,0x2b,0xce,0x95,0x45,0xa8,0xee,0xea,0x9e,0x9,0x21,0xc7,0x1b,0x94,0xb4,0x9a,0x38,0x1e,0x8c,0xf3,0x25,0x2e,0x35,0x9e,0x7f,0xb9,0x22,0xfb,0x31,0xde,0x3e,0x81,0x1d,0x4c,0xc9,0xc6,0x33,0xd3,0xbc,0x86,0x29,0xe7,0x30,0xef,0x68,0x67,0x11,0x1c,0x85,0x82,0xe5,0xce,0x3a,0x80,0xe0,0x90,0xca,0x6a,0x2,0x44,0xa8,0x20,0x9,0x4d,0x65,0xae,0xda,0xd0,0xf0,0x83,0x5f,0x6f,0x8a,0x6b,0xa6,0xec,0xaa,0xd1,0x1,0x8f,0x71,0xe8,0xb0,0xd9,0xa0,0x62,0xf5,0x95,0x16,0x57,0xb8,0x59,0x5e,0x6d,0x60,0x2f,0xf9,0x4,0x47,0xcc,0xc2,0x4b,0xc0,0x7c,0x18,0xcd,0x12,0xeb,0xd5,0x36,0x2a,0x98,0xd7,0x21,0x2b,0x3,0xd6,0x70,0x74,0x6c,0x1b,0x7e,0x69,0x94,0xf2,0x92,0xbf,0xb2,0xf,0xe9,0xdf,0xee,0x1f,0x15,0x14,0x52,0x4f,0xf8,0xa5,0x88,0xa7,0x76,0x64,0xf1,0xbb,0xfc,0x56,0x49,0xc8,0x78,0x61,0xe,0xea,0xb4,0x32,0xb5,0x84,0x96,0xac,0x17,0xb1,0xd2,0xaf,0xf7,0x87,0x4a,0xb,0x91,0x79,0x63,0x99,0x66,0x8,0x39,0x97,0xbe,0x41,0x5c,0xf4,0x0,0x3d,0x89,0x5b,0xc5,0x93,0x40,0x19,0x9d,0x43,0xcf,0x7a,0xa9,0xdc,0x5a,0x9f,0x77,0x8e,0x42,0x28,0xe2,0xed,0xab,0x9c,0xc1,0xc7,0x2d,0xe6,0xfd,0x51,0xa4,0x58,0xc4,0xba,0xb3,0x37,0x3b,0x8b,0x23,0xe4,0x3c,0xa3,0xa1,0x72,0xad,0xff,0x26,0xdb,0x5,0xd4,0x75,0xd8,0x13,0x2c,0xfa,0x45,0x48,0xa,0x7b,0x50,0xb7,0xc,0x73,0xe3,0x24,0xfe,0xc3,0x1a,0x27,0xb6,0x5d,0x34,0x6e,0xcb,0x4e,0x7,0xf6,0x3f,0x8d,0x55,0x54,0x10,0x9b,0xe1,0x46,0x53,0xdd,0x7d,0xd,0x6,0xbd,0xa2,0x31,0x14,0xae,0xe8,0x5,0x1a,0x8c,0xab,0x9c,0xa4,0xd7,0x3f,0xe,0x8a,0xa5,0x83,0x2b,0xcb,0xf5,0x63,0xf2,0xba,0x4e,0xd1,0x7d,0xa1,0xa8,0xb6,0x58,0xd4,0x98,0xdc,0x34,0xb5,0x8f,0xfc,0x28,0x91,0xeb,0x7c,0x1f,0x19,0x45,0x79,0x6c,0xf8,0x8b,0x99,0xa9,0xb3,0xe3,0x89,0xe7,0x16,0xb7,0xe0,0x56,0x78,0xd5,0xef,0x61,0x66,0xd3,0x77,0xfb,0x43,0xc8,0xdd,0x50,0x9d,0xb4,0x84,0x49,0xc1,0x1c,0xb0,0xe9,0x7b,0x17,0x76,0xca,0xd,0x82,0x6f,0xbc,0x15,0x2d,0xcc,0x32,0xea,0xa,0x4d,0xf1,0x69,0x68,0x40,0xd2,0x2c,0xc2,0x2,0x0,0xce,0x3d,0x97,0x87,0x9a,0xd8,0x23,0x8,0x53,0x6e,0x59,0xc5,0x47,0xc6,0xf0,0xc7,0xb2,0xb9,0x75,0x7,0x7f,0xec,0x3,0x5f,0xbb,0x95,0x29,0x3a,0x9b,0x7a,0x38,0xf7,0x81,0x3c,0xf4,0x25,0x20,0x96,0x4c,0x26,0x46,0x27,0x5b,0x1,0x48,0x73,0xc4,0x39,0xac,0x12,0xfe,0x4b,0xe6,0x30,0xc3,0x60,0x55,0xf,0xfa,0x9f,0x4a,0x85,0xd0,0xa3,0x62,0xde,0xc,0xe5,0xdb,0x71,0x1d,0xe1,0x9e,0x3e,0x90,0x5c,0x10,0xaf,0x24,0xf6,0x86,0x2e,0x8d,0xb8,0x51,0x70,0x5d,0x5e,0x80,0x93,0xa0,0x11,0x5a,0xb1,0x1e,0xed,0x52,0xcf,0x41,0x4f,0xe4,0x3b,0xa2,0x4,0x94,0xf3,0xdf,0xbe,0xd6,0x44,0xa6,0xe2,0x6b,0x36,0xa7,0x8e,0xb,0xcd,0x35,0xee,0xfd,0x54,0x6,0x6d,0x92,0xc0,0xad,0x33,0x22,0x74,0x7e,0xc9,0x21,0xbf,0xbd,0x67,0xd9,0x57,0x88,0x72,0x6a,0x18,0x65,0x2f,0xf9,0x37,0xaa,0x9,0xff,0x1b,0x42,0x64,0x13,0xda,0x2a,0x2d,0x8b,0x33,0x4e,0x1b,0x6b,0x97,0xd6,0xe5,0xd,0x5,0xff,0x94,0xfa,0xb,0xa5,0x27,0x6d,0xca,0x60,0x54,0xd5,0xfd,0xe4,0x76,0x92,0xae,0x28,0x18,0x29,0x30,0xa,0x40,0x35,0x3,0xc6,0x12,0xeb,0xb4,0xde,0x71,0x7e,0x0,0x37,0x5b,0x5d,0x7a,0xb1,0xdd,0x22,0x68,0xc0,0xa1,0x9c,0xc7,0x15,0xf,0x59,0x85,0xdc,0xdf,0x1,0xe6,0x53,0x63,0x31,0x47,0xba,0x48,0x99,0x44,0xe9,0xb0,0x8f,0xd9,0x66,0x96,0xd4,0xcc,0xe7,0xcd,0x61,0xc4,0x38,0x26,0x58,0xab,0x2f,0x17,0xa7,0x78,0xbf,0x3f,0xa0,0xee,0x3d,0xa3,0x6a,0xc9,0x11,0x8c,0xc8,0x7d,0x7,0xcf,0xda,0xe1,0x41,0x9a,0x91,0x3e,0x21,0x90,0x2b,0x7f,0xef,0x62,0xb8,0x86,0x5f,0x2a,0xbb,0xa8,0xc1,0x57,0xf2,0x9b,0xd2,0x81,0x1d,0x55,0xd0,0xaf,0x5a,0x20,0x4f,0xb5,0x1a,0xac,0x7b,0xf4,0x73,0x8d,0xfb,0xa4,0x6,0x10,0x82,0xb9,0x6f,0xa9,0xb2,0xe3,0x2,0xbe,0x25,0xad,0x67,0xa2,0x42,0xf9,0xd1,0x46,0x32,0x6c,0x4c,0xc3,0x1f,0x16,0xf3,0x3a,0xf7,0x36,0x70,0x9d,0x4d,0x19,0x80,0x79,0x1e,0xa6,0x52,0x7c,0x1c,0x56,0xc,0x9e,0xf6,0x34,0xd8,0x95,0xbc,0x65,0xb3,0xdb,0x98,0x5e,0x50,0x5c,0xd7,0x84,0xe0,0x8e,0x51,0x49,0x77,0xb6,0xaa,0xed,0x13,0x2c,0x74,0x3c,0x45,0x69,0xfe,0x8a,0x9,0x24,0xcb,0xc2,0xc5,0xfc,0xf1,0x93,0x2e,0x43,0x75,0x83,0x72,0x88,0x89,0xd3,0xce,0x39,0x64,0x3b,0x14,0xf8,0xea,0x4b,0x4,0xb7,0xbd,0x4a,0x9f,0xe8,0xec,0x87,0xf0,0xf5,0xe2,0x6e,0x8,0x23,0xe,0xfa,0x3d,0xe2,0x52,0x78,0xab,0xe5,0x7a,0x7d,0x81,0x24,0x88,0x6a,0xee,0x1d,0x63,0x23,0x9c,0xca,0xf5,0xa2,0x89,0x91,0xd3,0xff,0x2,0x74,0x26,0xac,0x1,0xdc,0xd,0x84,0xed,0xfe,0x6f,0x97,0xde,0xb7,0x12,0xaa,0x3a,0x6e,0xd5,0x1a,0xc3,0xfd,0x27,0x4,0xa4,0x9f,0x8a,0x64,0x7b,0xd4,0xdf,0x54,0x8c,0x2f,0xe6,0x42,0x38,0x8d,0xc9,0x6d,0xeb,0xd7,0x33,0x4f,0x75,0x6c,0x5d,0x25,0x8f,0x28,0x62,0xa1,0xb8,0x90,0x11,0xba,0x40,0x48,0xa0,0xe0,0x4e,0xbf,0xd1,0xb,0x76,0xce,0x68,0x93,0xd2,0x2e,0x5e,0x99,0xc0,0x1c,0x4a,0x16,0xa3,0x44,0x9a,0x85,0x2d,0x67,0x98,0x50,0x82,0xd9,0xe4,0x72,0x45,0x3b,0x34,0xf4,0x3f,0x18,0x1e,0x83,0x46,0x70,0x5,0x9b,0xf1,0xae,0x57,0x8e,0x61,0x4c,0xcf,0xb4,0xb9,0x80,0x87,0x31,0x69,0x56,0xa8,0xbb,0x2c,0x0,0x79,0x14,0xcb,0xa5,0xc1,0xef,0xf3,0x32,0xc,0xdd,0x9e,0xf6,0x20,0x92,0x19,0x15,0x1b,0xa7,0xb0,0xb5,0xc2,0x4b,0x66,0x4d,0x2b,0xf8,0xf2,0x41,0xe,0xa9,0xad,0xda,0xf,0x21,0x7c,0x8b,0x96,0xaf,0xbd,0x51,0x7e,0x30,0x6,0x6b,0xd6,0xcc,0xcd,0x37,0xc6,0x60,0xfb,0x47,0xa6,0x7,0xe7,0x22,0xe8,0xc7,0x55,0x43,0xe1,0xf7,0xec,0x2a,0xfc,0x3e,0xe9,0x5f,0xf0,0xbe,0xc8,0x36,0xb1,0x95,0x10,0x58,0xc4,0xa,0x65,0x1f,0xea,0xb3,0xdb,0x49,0x13,0xf9,0xd0,0x9d,0x71,0x5b,0x3c,0xc5,0x5c,0x59,0x39,0x17,0xe3,0xb2,0x7f,0xb6,0x53,0x8,0xd8,0x35,0x73,0x77,0x3,0x94,0xbc,0x5a,0x86,0x9,0x29,0x6,0xbe,0x98,0x8d,0xd8,0x15,0xc1,0xf1,0x84,0xc,0xf5,0x59,0x3e,0xac,0x33,0x52,0x48,0x8f,0x2a,0xc7,0x50,0xf9,0x89,0x68,0xaf,0x77,0x8,0x4f,0x2c,0xb4,0x5,0x2d,0x69,0x97,0x47,0x87,0x8b,0x45,0xd2,0x78,0xdf,0xc2,0x66,0x9d,0x16,0x4d,0x1c,0x2b,0x2,0x80,0xb5,0x83,0xf7,0x82,0x30,0xfc,0x3a,0x42,0x46,0xa9,0xfe,0x1a,0x6c,0xd0,0x51,0x74,0xad,0xeb,0x5f,0x40,0xee,0xc9,0xe1,0xd9,0x7a,0x92,0xcf,0x4b,0xc6,0xe0,0x8e,0x6e,0x26,0xb0,0xff,0xb7,0x94,0xb,0xe4,0x38,0xf3,0xed,0x91,0x1d,0x99,0xdd,0xf0,0x71,0xb9,0xca,0xd4,0x6d,0x39,0xae,0x5c,0x5a,0x3c,0x0,0xbd,0x29,0xdc,0xce,0xf6,0xec,0xcc,0xa6,0x53,0xa2,0xa5,0xf2,0x3d,0x13,0xaa,0x90,0x23,0x24,0x32,0x96,0x1f,0x54,0x5b,0xf4,0x17,0xa8,0x4,0x8a,0xa1,0xa,0xe7,0x7e,0xd1,0x41,0x9a,0xb6,0x93,0xfb,0xe3,0x1,0x2e,0xa7,0xe2,0x73,0x4e,0xcb,0x70,0x88,0xb8,0xab,0x43,0x11,0xd7,0x28,0xe8,0x85,0x67,0x76,0x3b,0x31,0x64,0x8c,0xf8,0xfa,0x9c,0x22,0xcd,0x12,0x2f,0x37,0x20,0x5d,0xbc,0x6a,0xef,0x72,0xba,0x4c,0x7,0x5e,0x56,0x21,0x6f,0x9f,0xde,0x7f,0x7d,0x3f,0xc4,0xb2,0xb1,0x79,0x65,0x60,0x9,0xd3,0x3,0x63,0x1e,0x62,0xd,0x44,0x81,0x36,0xe9,0x7c,0xbb,0x57,0xa3,0xe,0x86,0x75,0x10,0x25,0xbf,0x4a,0xf,0xda,0x95,0xc0,0x27,0xe6,0x49,0x9b,0x9e,0xa0,0x58,0x34,0xdb,0xa4,0xd5,0x7b,0x55,0x19,0x61,0xea,0xc3,0xb3,0xc8,0x6b,0x14,0xfd,0x18,0x35,0xc5,0x1b,0xe5,0xd6,0x36,0xca,0x3f,0x93,0x59,0xdd,0xd4,0xaa,0x8a,0x4d,0xe5,0x55,0x1c,0xcf,0xcd,0x52,0xb5,0x48,0x91,0xc3,0xb6,0x1b,0xba,0x6b,0x2b,0x94,0x42,0x7d,0x3e,0x15,0x64,0x26,0x8d,0x1d,0x62,0xd9,0x74,0xad,0x90,0x4a,0x5a,0x33,0xd8,0x49,0x69,0x20,0xa5,0x0,0x3b,0xe3,0x51,0x98,0x8f,0xf5,0x7e,0x3a,0x13,0xb3,0x3d,0x28,0xcc,0xd3,0x68,0x63,0x38,0x92,0xd5,0x9f,0xf,0x16,0xa6,0x27,0x5c,0xda,0x84,0x60,0xc2,0xf8,0xea,0xdb,0xc1,0xbc,0xdf,0x79,0x65,0x24,0xe9,0x99,0xf7,0xd,0x17,0xff,0xf9,0x57,0x66,0x8,0x9a,0x32,0x2f,0xd0,0x35,0xe7,0x53,0x6e,0x77,0x2e,0xfd,0xab,0x14,0xa1,0x2d,0xf3,0xf1,0x34,0xb2,0xc7,0x46,0x2c,0xe0,0x19,0xf2,0xc5,0x83,0x8c,0x88,0x43,0xa9,0xaf,0xde,0x86,0x1f,0xe1,0x9b,0xc,0xce,0xb7,0xd6,0x39,0x78,0xfb,0xe,0x3,0x30,0x37,0x29,0x6a,0x97,0x41,0xae,0x25,0xac,0xa2,0x7c,0xa3,0x76,0x12,0x44,0x58,0xbb,0x85,0x45,0x4f,0xb9,0xf6,0x1a,0x1e,0xb8,0x6d,0x7,0x10,0x75,0x2,0xd1,0xfc,0x9c,0xfa,0xb1,0x87,0x61,0xdc,0x7a,0x7b,0x71,0x80,0xcb,0x96,0x21,0x3c,0xa,0x18,0xc9,0xe6,0xe2,0x70,0x56,0xf4,0x5b,0x40,0x4b,0x9d,0x4c,0xd7,0x11,0xf0,0x50,0xb0,0x5f,0x95,0xa7,0x22,0x73,0xef,0xd2,0xbd,0x5d,0xa8,0x5e,0x89,0x47,0xe8,0x7f,0x9,0x6,0x81,0x8b,0xec,0xeb,0x72,0x8e,0xee,0x54,0xa0,0x6c,0x4,0xa4,0xfe,0x67,0x4e,0xc6,0x2a,0xb4,0xc0,0xb,0x23,0x31,0xed,0x9e,0xbe,0xc8,0x5,0xe4,0x1,0x6f,0xbf,0xc4,0x82,0x29,0x8f,0x37,0x4a,0x1f,0x6f,0x93,0xd2,0xe1,0x9,0x1,0xfb,0x90,0xfe,0xf,0xa1,0x23,0x69,0xce,0x64,0x50,0xd1,0xf9,0xe0,0x72,0x96,0xaa,0x2c,0x1c,0x2d,0x34,0xe,0x44,0x31,0x7,0xc2,0x16,0xef,0xb0,0xda,0x75,0x7a,0x4,0x33,0x5f,0x59,0x7e,0xb5,0xd9,0x26,0x6c,0xc4,0xa5,0x98,0xc3,0x11,0xb,0x5d,0x81,0xd8,0xdb,0x5,0xe2,0x57,0x67,0x35,0x43,0xbe,0x4c,0x9d,0x40,0xed,0xb4,0x8b,0xdd,0x62,0x92,0xd0,0xc8,0xe3,0xc9,0x65,0xc0,0x3c,0x22,0x5c,0xaf,0x2b,0x13,0xa3,0x7c,0xbb,0x3b,0xa4,0xea,0x39,0xa7,0x6e,0xcd,0x15,0x88,0xcc,0x79,0x3,0xcb,0xde,0xe5,0x45,0x9e,0x95,0x3a,0x25,0x94,0x2f,0x7b,0xeb,0x66,0xbc,0x82,0x5b,0x2e,0xbf,0xac,0xc5,0x53,0xf6,0x9f,0xd6,0x85,0x19,0x51,0xd4,0xab,0x5e,0x24,0x4b,0xb1,0x1e,0xa8,0x7f,0xf0,0x77,0x89,0xff,0xa0,0x2,0x14,0x86,0xbd,0x6b,0xad,0xb6,0xe7,0x6,0xba,0x21,0xa9,0x63,0xa6,0x46,0xfd,0xd5,0x42,0x36,0x68,0x48,0xc7,0x1b,0x12,0xf7,0x3e,0xf3,0x32,0x74,0x99,0x49,0x1d,0x84,0x7d,0x1a,0xa2,0x56,0x78,0x18,0x52,0x8,0x9a,0xf2,0x30,0xdc,0x91,0xb8,0x61,0xb7,0xdf,0x9c,0x5a,0x54,0x58,0xd3,0x80,0xe4,0x8a,0x55,0x4d,0x73,0xb2,0xae,0xe9,0x17,0x28,0x70,0x38,0x41,0x6d,0xfa,0x8e,0xd,0x20,0xcf,0xc6,0xc1,0xf8,0xf5,0x97,0x2a,0x47,0x71,0x87,0x76,0x8c,0x8d,0xd7,0xca,0x3d,0x60,0x3f,0x10,0xfc,0xee,0x4f,0x0,0xb3,0xb9,0x4e,0x9b,0xec,0xe8,0x83,0xf4,0xf1,0xe6,0x6a,0xc,0x27,0xa,0x1,0xc6,0x19,0xa9,0x83,0x50,0x1e,0x81,0x86,0x7a,0xdf,0x73,0x91,0x15,0xe6,0x98,0xd8,0x67,0x31,0xe,0x59,0x72,0x6a,0x28,0x4,0xf9,0x8f,0xdd,0x57,0xfa,0x27,0xf6,0x7f,0x16,0x5,0x94,0x6c,0x25,0x4c,0xe9,0x51,0xc1,0x95,0x2e,0xe1,0x38,0x6,0xdc,0xff,0x5f,0x64,0x71,0x9f,0x80,0x2f,0x24,0xaf,0x77,0xd4,0x1d,0xb9,0xc3,0x76,0x32,0x96,0x10,0x2c,0xc8,0xb4,0x8e,0x97,0xa6,0xde,0x74,0xd3,0x99,0x5a,0x43,0x6b,0xea,0x41,0xbb,0xb3,0x5b,0x1b,0xb5,0x44,0x2a,0xf0,0x8d,0x35,0x93,0x68,0x29,0xd5,0xa5,0x62,0x3b,0xe7,0xb1,0xed,0x58,0xbf,0x61,0x7e,0xd6,0x9c,0x63,0xab,0x79,0x22,0x1f,0x89,0xbe,0xc0,0xcf,0xf,0xc4,0xe3,0xe5,0x78,0xbd,0x8b,0xfe,0x60,0xa,0x55,0xac,0x75,0x9a,0xb7,0x34,0x4f,0x42,0x7b,0x7c,0xca,0x92,0xad,0x53,0x40,0xd7,0xfb,0x82,0xef,0x30,0x5e,0x3a,0x14,0x8,0xc9,0xf7,0x26,0x65,0xd,0xdb,0x69,0xe2,0xee,0xe0,0x5c,0x4b,0x4e,0x39,0xb0,0x9d,0xb6,0xd0,0x3,0x9,0xba,0xf5,0x52,0x56,0x21,0xf4,0xda,0x87,0x70,0x6d,0x54,0x46,0xaa,0x85,0xcb,0xfd,0x90,0x2d,0x37,0x36,0xcc,0x3d,0x9b,0x0,0xbc,0x5d,0xfc,0x1c,0xd9,0x13,0x3c,0xae,0xb8,0x1a,0xc,0x17,0xd1,0x7,0xc5,0x12,0xa4,0xb,0x45,0x33,0xcd,0x4a,0x6e,0xeb,0xa3,0x3f,0xf1,0x9e,0xe4,0x11,0x48,0x20,0xb2,0xe8,0x2,0x2b,0x66,0x8a,0xa0,0xc7,0x3e,0xa7,0xa2,0xc2,0xec,0x18,0x49,0x84,0x4d,0xa8,0xf3,0x23,0xce,0x88,0x8c,0xf8,0x6f,0x47,0xa1,0x7d,0xf2,0xd2,0xfc,0x5e,0x78,0xea,0x95,0x43,0x48,0x53,0xf8,0x19,0xdf,0x44,0x9d,0x57,0xb8,0x58,0xe7,0x7b,0x2a,0xaf,0xa0,0x55,0xb5,0xda,0xe0,0x4f,0x81,0x56,0x89,0xe,0x1,0x77,0x7a,0xe3,0xe4,0x83,0xa8,0x5c,0xe6,0x86,0xf6,0xac,0xc,0x64,0x22,0xce,0x46,0x6f,0x2b,0x3,0xc8,0xbc,0xb6,0x96,0xe5,0x39,0x9,0xec,0xd,0xc0,0x8a,0xcc,0xb7,0x67,0xe9,0x17,0x8e,0xd6,0xbf,0xc6,0x4,0x93,0xf3,0x70,0x31,0xde,0x3f,0x38,0xb,0x6,0x49,0x9f,0x62,0x21,0xaa,0xa4,0x2d,0xa6,0x1a,0x7e,0xab,0x74,0x8d,0xb3,0x50,0x4c,0xfe,0xb1,0x47,0x4d,0x65,0xb0,0x16,0x12,0xa,0x7d,0x18,0xf,0xf2,0x94,0xf4,0xd9,0xd4,0x69,0x8f,0xb9,0x88,0x79,0x73,0x72,0x34,0x29,0x9e,0xc3,0xee,0xc1,0x10,0x2,0x97,0xdd,0x9a,0x30,0x2f,0xae,0x1e,0x7,0x68,0x8c,0xd2,0x54,0xd3,0xe2,0xf0,0xca,0x71,0xd7,0xb4,0xc9,0x91,0xe1,0x2c,0x6d,0xf7,0x1f,0x5,0xff,0x0,0x6e,0x5f,0xf1,0xd8,0x27,0x3a,0x92,0x66,0x5b,0xef,0x3d,0xa3,0xf5,0x26,0x7f,0xfb,0x25,0xa9,0x1c,0xcf,0xba,0x3c,0xf9,0x11,0xe8,0x24,0x4e,0x84,0x8b,0xcd,0xfa,0xa7,0xa1,0x4b,0x80,0x9b,0x37,0xc2,0x3e,0xa2,0xdc,0xd5,0x51,0x5d,0xed,0x45,0x82,0x5a,0xc5,0xc7,0x14,0xcb,0x99,0x40,0xbd,0x63,0xb2,0x13,0xbe,0x75,0x4a,0x9c,0x23,0x2e,0x6c,0x1d,0x36,0xd1,0x6a,0x15,0x85,0x42,0x98,0xa5,0x7c,0x41,0xd0,0x3b,0x52,0x8,0xad,0x28,0x61,0x90,0x59,0xeb,0x33,0x32,0x76,0xfd,0x87,0x20,0x35,0xbb,0x1b,0x6b,0x60,0xdb,0xc4,0xa2,0x5e,0xab,0x7,0xcd,0x49,0x40,0x3e,0x1e,0xd9,0x71,0xc1,0x88,0x5b,0x59,0xc6,0x21,0xdc,0x5,0x57,0x22,0x8f,0x2e,0xff,0xbf,0x0,0xd6,0xe9,0xaa,0x81,0xf0,0xb2,0x19,0x89,0xf6,0x4d,0xe0,0x39,0x4,0xde,0xce,0xa7,0x4c,0xdd,0xfd,0xb4,0x31,0x94,0xaf,0x77,0xc5,0xc,0x1b,0x61,0xea,0xae,0x87,0x27,0xa9,0xbc,0x58,0x47,0xfc,0xf7,0xac,0x6,0x41,0xb,0x9b,0x82,0x32,0xb3,0xc8,0x4e,0x10,0xf4,0x56,0x6c,0x7e,0x4f,0x55,0x28,0x4b,0xed,0xf1,0xb0,0x7d,0xd,0x63,0x99,0x83,0x6b,0x6d,0xc3,0xf2,0x9c,0xe,0xa6,0xbb,0x44,0xa1,0x73,0xc7,0xfa,0xe3,0xba,0x69,0x3f,0x80,0x35,0xb9,0x67,0x65,0xa0,0x26,0x53,0xd2,0xb8,0x74,0x8d,0x66,0x51,0x17,0x18,0x1c,0xd7,0x3d,0x3b,0x4a,0x12,0x8b,0x75,0xf,0x98,0x5a,0x23,0x42,0xad,0xec,0x6f,0x9a,0x97,0xa4,0xa3,0xbd,0xfe,0x3,0xd5,0x3a,0xb1,0x38,0x36,0xe8,0x37,0xe2,0x86,0xd0,0xcc,0x2f,0x11,0xd1,0xdb,0x2d,0x62,0x8e,0x8a,0x2c,0xf9,0x93,0x84,0xe1,0x96,0x45,0x68,0x8,0x6e,0x25,0x13,0xf5,0x48,0xee,0xef,0xe5,0x14,0x5f,0x2,0xb5,0xa8,0x9e,0x8c,0x5d,0x72,0x76,0xe4,0xc2,0x60,0xcf,0xd4,0xdf,0x9,0xd8,0x43,0x85,0x64,0xc4,0x24,0xcb,0x1,0x33,0xb6,0xe7,0x7b,0x46,0x29,0xc9,0x3c,0xca,0x1d,0xd3,0x7c,0xeb,0x9d,0x92,0x15,0x1f,0x78,0x7f,0xe6,0x1a,0x7a,0xc0,0x34,0xf8,0x90,0x30,0x6a,0xf3,0xda,0x52,0xbe,0x20,0x54,0x9f,0xb7,0xa5,0x79,0xa,0x2a,0x5c,0x91,0x70,0x95,0xfb,0x2b,0x50,0x16,0xf,0xa9,0x11,0x6c,0x39,0x49,0xb5,0xf4,0xc7,0x2f,0x27,0xdd,0xb6,0xd8,0x29,0x87,0x5,0x4f,0xe8,0x42,0x76,0xf7,0xdf,0xc6,0x54,0xb0,0x8c,0xa,0x3a,0xb,0x12,0x28,0x62,0x17,0x21,0xe4,0x30,0xc9,0x96,0xfc,0x53,0x5c,0x22,0x15,0x79,0x7f,0x58,0x93,0xff,0x0,0x4a,0xe2,0x83,0xbe,0xe5,0x37,0x2d,0x7b,0xa7,0xfe,0xfd,0x23,0xc4,0x71,0x41,0x13,0x65,0x98,0x6a,0xbb,0x66,0xcb,0x92,0xad,0xfb,0x44,0xb4,0xf6,0xee,0xc5,0xef,0x43,0xe6,0x1a,0x4,0x7a,0x89,0xd,0x35,0x85,0x5a,0x9d,0x1d,0x82,0xcc,0x1f,0x81,0x48,0xeb,0x33,0xae,0xea,0x5f,0x25,0xed,0xf8,0xc3,0x63,0xb8,0xb3,0x1c,0x3,0xb2,0x9,0x5d,0xcd,0x40,0x9a,0xa4,0x7d,0x8,0x99,0x8a,0xe3,0x75,0xd0,0xb9,0xf0,0xa3,0x3f,0x77,0xf2,0x8d,0x78,0x2,0x6d,0x97,0x38,0x8e,0x59,0xd6,0x51,0xaf,0xd9,0x86,0x24,0x32,0xa0,0x9b,0x4d,0x8b,0x90,0xc1,0x20,0x9c,0x7,0x8f,0x45,0x80,0x60,0xdb,0xf3,0x64,0x10,0x4e,0x6e,0xe1,0x3d,0x34,0xd1,0x18,0xd5,0x14,0x52,0xbf,0x6f,0x3b,0xa2,0x5b,0x3c,0x84,0x70,0x5e,0x3e,0x74,0x2e,0xbc,0xd4,0x16,0xfa,0xb7,0x9e,0x47,0x91,0xf9,0xba,0x7c,0x72,0x7e,0xf5,0xa6,0xc2,0xac,0x73,0x6b,0x55,0x94,0x88,0xcf,0x31,0xe,0x56,0x1e,0x67,0x4b,0xdc,0xa8,0x2b,0x6,0xe9,0xe0,0xe7,0xde,0xd3,0xb1,0xc,0x61,0x57,0xa1,0x50,0xaa,0xab,0xf1,0xec,0x1b,0x46,0x19,0x36,0xda,0xc8,0x69,0x26,0x95,0x9f,0x68,0xbd,0xca,0xce,0xa5,0xd2,0xd7,0xc0,0x4c,0x2a,0x1,0x2c,0xab,0x6c,0xb3,0x3,0x29,0xfa,0xb4,0x2b,0x2c,0xd0,0x75,0xd9,0x3b,0xbf,0x4c,0x32,0x72,0xcd,0x9b,0xa4,0xf3,0xd8,0xc0,0x82,0xae,0x53,0x25,0x77,0xfd,0x50,0x8d,0x5c,0xd5,0xbc,0xaf,0x3e,0xc6,0x8f,0xe6,0x43,0xfb,0x6b,0x3f,0x84,0x4b,0x92,0xac,0x76,0x55,0xf5,0xce,0xdb,0x35,0x2a,0x85,0x8e,0x5,0xdd,0x7e,0xb7,0x13,0x69,0xdc,0x98,0x3c,0xba,0x86,0x62,0x1e,0x24,0x3d,0xc,0x74,0xde,0x79,0x33,0xf0,0xe9,0xc1,0x40,0xeb,0x11,0x19,0xf1,0xb1,0x1f,0xee,0x80,0x5a,0x27,0x9f,0x39,0xc2,0x83,0x7f,0xf,0xc8,0x91,0x4d,0x1b,0x47,0xf2,0x15,0xcb,0xd4,0x7c,0x36,0xc9,0x1,0xd3,0x88,0xb5,0x23,0x14,0x6a,0x65,0xa5,0x6e,0x49,0x4f,0xd2,0x17,0x21,0x54,0xca,0xa0,0xff,0x6,0xdf,0x30,0x1d,0x9e,0xe5,0xe8,0xd1,0xd6,0x60,0x38,0x7,0xf9,0xea,0x7d,0x51,0x28,0x45,0x9a,0xf4,0x90,0xbe,0xa2,0x63,0x5d,0x8c,0xcf,0xa7,0x71,0xc3,0x48,0x44,0x4a,0xf6,0xe1,0xe4,0x93,0x1a,0x37,0x1c,0x7a,0xa9,0xa3,0x10,0x5f,0xf8,0xfc,0x8b,0x5e,0x70,0x2d,0xda,0xc7,0xfe,0xec,0x0,0x2f,0x61,0x57,0x3a,0x87,0x9d,0x9c,0x66,0x97,0x31,0xaa,0x16,0xf7,0x56,0xb6,0x73,0xb9,0x96,0x4,0x12,0xb0,0xa6,0xbd,0x7b,0xad,0x6f,0xb8,0xe,0xa1,0xef,0x99,0x67,0xe0,0xc4,0x41,0x9,0x95,0x5b,0x34,0x4e,0xbb,0xe2,0x8a,0x18,0x42,0xa8,0x81,0xcc,0x20,0xa,0x6d,0x94,0xd,0x8,0x68,0x46,0xb2,0xe3,0x2e,0xe7,0x2,0x59,0x89,0x64,0x22,0x26,0x52,0xc5,0xed,0xb,0xd7,0x58,0x78,0x89,0x2b,0xd,0x9f,0xe0,0x36,0x3d,0x26,0x8d,0x6c,0xaa,0x31,0xe8,0x22,0xcd,0x2d,0x92,0xe,0x5f,0xda,0xd5,0x20,0xc0,0xaf,0x95,0x3a,0xf4,0x23,0xfc,0x7b,0x74,0x2,0xf,0x96,0x91,0xf6,0xdd,0x29,0x93,0xf3,0x83,0xd9,0x79,0x11,0x57,0xbb,0x33,0x1a,0x5e,0x76,0xbd,0xc9,0xc3,0xe3,0x90,0x4c,0x7c,0x99,0x78,0xb5,0xff,0xb9,0xc2,0x12,0x9c,0x62,0xfb,0xa3,0xca,0xb3,0x71,0xe6,0x86,0x5,0x44,0xab,0x4a,0x4d,0x7e,0x73,0x3c,0xea,0x17,0x54,0xdf,0xd1,0x58,0xd3,0x6f,0xb,0xde,0x1,0xf8,0xc6,0x25,0x39,0x8b,0xc4,0x32,0x38,0x10,0xc5,0x63,0x67,0x7f,0x8,0x6d,0x7a,0x87,0xe1,0x81,0xac,0xa1,0x1c,0xfa,0xcc,0xfd,0xc,0x6,0x7,0x41,0x5c,0xeb,0xb6,0x9b,0xb4,0x65,0x77,0xe2,0xa8,0xef,0x45,0x5a,0xdb,0x6b,0x72,0x1d,0xf9,0xa7,0x21,0xa6,0x97,0x85,0xbf,0x4,0xa2,0xc1,0xbc,0xe4,0x94,0x59,0x18,0x82,0x6a,0x70,0x8a,0x75,0x1b,0x2a,0x84,0xad,0x52,0x4f,0xe7,0x13,0x2e,0x9a,0x48,0xd6,0x80,0x53,0xa,0x8e,0x50,0xdc,0x69,0xba,0xcf,0x49,0x8c,0x64,0x9d,0x51,0x3b,0xf1,0xfe,0xb8,0x8f,0xd2,0xd4,0x3e,0xf5,0xee,0x42,0xb7,0x4b,0xd7,0xa9,0xa0,0x24,0x28,0x98,0x30,0xf7,0x2f,0xb0,0xb2,0x61,0xbe,0xec,0x35,0xc8,0x16,0xc7,0x66,0xcb,0x0,0x3f,0xe9,0x56,0x5b,0x19,0x68,0x43,0xa4,0x1f,0x60,0xf0,0x37,0xed,0xd0,0x9,0x34,0xa5,0x4e,0x27,0x7d,0xd8,0x5d,0x14,0xe5,0x2c,0x9e,0x46,0x47,0x3,0x88,0xf2,0x55,0x40,0xce,0x6e,0x1e,0x15,0xae,0xb1,0xdb,0x27,0xd2,0x7e,0xb4,0x30,0x39,0x47,0x67,0xa0,0x8,0xb8,0xf1,0x22,0x20,0xbf,0x58,0xa5,0x7c,0x2e,0x5b,0xf6,0x57,0x86,0xc6,0x79,0xaf,0x90,0xd3,0xf8,0x89,0xcb,0x60,0xf0,0x8f,0x34,0x99,0x40,0x7d,0xa7,0xb7,0xde,0x35,0xa4,0x84,0xcd,0x48,0xed,0xd6,0xe,0xbc,0x75,0x62,0x18,0x93,0xd7,0xfe,0x5e,0xd0,0xc5,0x21,0x3e,0x85,0x8e,0xd5,0x7f,0x38,0x72,0xe2,0xfb,0x4b,0xca,0xb1,0x37,0x69,0x8d,0x2f,0x15,0x7,0x36,0x2c,0x51,0x32,0x94,0x88,0xc9,0x4,0x74,0x1a,0xe0,0xfa,0x12,0x14,0xba,0x8b,0xe5,0x77,0xdf,0xc2,0x3d,0xd8,0xa,0xbe,0x83,0x9a,0xc3,0x10,0x46,0xf9,0x4c,0xc0,0x1e,0x1c,0xd9,0x5f,0x2a,0xab,0xc1,0xd,0xf4,0x1f,0x28,0x6e,0x61,0x65,0xae,0x44,0x42,0x33,0x6b,0xf2,0xc,0x76,0xe1,0x23,0x5a,0x3b,0xd4,0x95,0x16,0xe3,0xee,0xdd,0xda,0xc4,0x87,0x7a,0xac,0x43,0xc8,0x41,0x4f,0x91,0x4e,0x9b,0xff,0xa9,0xb5,0x56,0x68,0xa8,0xa2,0x54,0x1b,0xf7,0xf3,0x55,0x80,0xea,0xfd,0x98,0xef,0x3c,0x11,0x71,0x17,0x5c,0x6a,0x8c,0x31,0x97,0x96,0x9c,0x6d,0x26,0x7b,0xcc,0xd1,0xe7,0xf5,0x24,0xb,0xf,0x9d,0xbb,0x19,0xb6,0xad,0xa6,0x70,0xa1,0x3a,0xfc,0x1d,0xbd,0x5d,0xb2,0x78,0x4a,0xcf,0x9e,0x2,0x3f,0x50,0xb0,0x45,0xb3,0x64,0xaa,0x5,0x92,0xe4,0xeb,0x6c,0x66,0x1,0x6,0x9f,0x63,0x3,0xb9,0x4d,0x81,0xe9,0x49,0x13,0x8a,0xa3,0x2b,0xc7,0x59,0x2d,0xe6,0xce,0xdc,0x0,0x73,0x53,0x25,0xe8,0x9,0xec,0x82,0x52,0x29,0x6f,0x8f,0x29,0x91,0xec,0xb9,0xc9,0x35,0x74,0x47,0xaf,0xa7,0x5d,0x36,0x58,0xa9,0x7,0x85,0xcf,0x68,0xc2,0xf6,0x77,0x5f,0x46,0xd4,0x30,0xc,0x8a,0xba,0x8b,0x92,0xa8,0xe2,0x97,0xa1,0x64,0xb0,0x49,0x16,0x7c,0xd3,0xdc,0xa2,0x95,0xf9,0xff,0xd8,0x13,0x7f,0x80,0xca,0x62,0x3,0x3e,0x65,0xb7,0xad,0xfb,0x27,0x7e,0x7d,0xa3,0x44,0xf1,0xc1,0x93,0xe5,0x18,0xea,0x3b,0xe6,0x4b,0x12,0x2d,0x7b,0xc4,0x34,0x76,0x6e,0x45,0x6f,0xc3,0x66,0x9a,0x84,0xfa,0x9,0x8d,0xb5,0x5,0xda,0x1d,0x9d,0x2,0x4c,0x9f,0x1,0xc8,0x6b,0xb3,0x2e,0x6a,0xdf,0xa5,0x6d,0x78,0x43,0xe3,0x38,0x33,0x9c,0x83,0x32,0x89,0xdd,0x4d,0xc0,0x1a,0x24,0xfd,0x88,0x19,0xa,0x63,0xf5,0x50,0x39,0x70,0x23,0xbf,0xf7,0x72,0xd,0xf8,0x82,0xed,0x17,0xb8,0xe,0xd9,0x56,0xd1,0x2f,0x59,0x6,0xa4,0xb2,0x20,0x1b,0xcd,0xb,0x10,0x41,0xa0,0x1c,0x87,0xf,0xc5,0x0,0xe0,0x5b,0x73,0xe4,0x90,0xce,0xee,0x61,0xbd,0xb4,0x51,0x98,0x55,0x94,0xd2,0x3f,0xef,0xbb,0x22,0xdb,0xbc,0x4,0xf0,0xde,0xbe,0xf4,0xae,0x3c,0x54,0x96,0x7a,0x37,0x1e,0xc7,0x11,0x79,0x3a,0xfc,0xf2,0xfe,0x75,0x26,0x42,0x2c,0xf3,0xeb,0xd5,0x14,0x8,0x4f,0xb1,0x8e,0xd6,0x9e,0xe7,0xcb,0x5c,0x28,0xab,0x86,0x69,0x60,0x67,0x5e,0x53,0x31,0x8c,0xe1,0xd7,0x21,0xd0,0x2a,0x2b,0x71,0x6c,0x9b,0xc6,0x99,0xb6,0x5a,0x48,0xe9,0xa6,0x15,0x1f,0xe8,0x3d,0x4a,0x4e,0x25,0x52,0x57,0x40,0xcc,0xaa,0x81,0xac,0x55,0x92,0x4d,0xfd,0xd7,0x4,0x4a,0xd5,0xd2,0x2e,0x8b,0x27,0xc5,0x41,0xb2,0xcc,0x8c,0x33,0x65,0x5a,0xd,0x26,0x3e,0x7c,0x50,0xad,0xdb,0x89,0x3,0xae,0x73,0xa2,0x2b,0x42,0x51,0xc0,0x38,0x71,0x18,0xbd,0x5,0x95,0xc1,0x7a,0xb5,0x6c,0x52,0x88,0xab,0xb,0x30,0x25,0xcb,0xd4,0x7b,0x70,0xfb,0x23,0x80,0x49,0xed,0x97,0x22,0x66,0xc2,0x44,0x78,0x9c,0xe0,0xda,0xc3,0xf2,0x8a,0x20,0x87,0xcd,0xe,0x17,0x3f,0xbe,0x15,0xef,0xe7,0xf,0x4f,0xe1,0x10,0x7e,0xa4,0xd9,0x61,0xc7,0x3c,0x7d,0x81,0xf1,0x36,0x6f,0xb3,0xe5,0xb9,0xc,0xeb,0x35,0x2a,0x82,0xc8,0x37,0xff,0x2d,0x76,0x4b,0xdd,0xea,0x94,0x9b,0x5b,0x90,0xb7,0xb1,0x2c,0xe9,0xdf,0xaa,0x34,0x5e,0x1,0xf8,0x21,0xce,0xe3,0x60,0x1b,0x16,0x2f,0x28,0x9e,0xc6,0xf9,0x7,0x14,0x83,0xaf,0xd6,0xbb,0x64,0xa,0x6e,0x40,0x5c,0x9d,0xa3,0x72,0x31,0x59,0x8f,0x3d,0xb6,0xba,0xb4,0x8,0x1f,0x1a,0x6d,0xe4,0xc9,0xe2,0x84,0x57,0x5d,0xee,0xa1,0x6,0x2,0x75,0xa0,0x8e,0xd3,0x24,0x39,0x0,0x12,0xfe,0xd1,0x9f,0xa9,0xc4,0x79,0x63,0x62,0x98,0x69,0xcf,0x54,0xe8,0x9,0xa8,0x48,0x8d,0x47,0x68,0xfa,0xec,0x4e,0x58,0x43,0x85,0x53,0x91,0x46,0xf0,0x5f,0x11,0x67,0x99,0x1e,0x3a,0xbf,0xf7,0x6b,0xa5,0xca,0xb0,0x45,0x1c,0x74,0xe6,0xbc,0x56,0x7f,0x32,0xde,0xf4,0x93,0x6a,0xf3,0xf6,0x96,0xb8,0x4c,0x1d,0xd0,0x19,0xfc,0xa7,0x77,0x9a,0xdc,0xd8,0xac,0x3b,0x13,0xf5,0x29,0xa6,0x86,0x27,0x85,0xa3,0x31,0x4e,0x98,0x93,0x88,0x23,0xc2,0x4,0x9f,0x46,0x8c,0x63,0x83,0x3c,0xa0,0xf1,0x74,0x7b,0x8e,0x6e,0x1,0x3b,0x94,0x5a,0x8d,0x52,0xd5,0xda,0xac,0xa1,0x38,0x3f,0x58,0x73,0x87,0x3d,0x5d,0x2d,0x77,0xd7,0xbf,0xf9,0x15,0x9d,0xb4,0xf0,0xd8,0x13,0x67,0x6d,0x4d,0x3e,0xe2,0xd2,0x37,0xd6,0x1b,0x51,0x17,0x6c,0xbc,0x32,0xcc,0x55,0xd,0x64,0x1d,0xdf,0x48,0x28,0xab,0xea,0x5,0xe4,0xe3,0xd0,0xdd,0x92,0x44,0xb9,0xfa,0x71,0x7f,0xf6,0x7d,0xc1,0xa5,0x70,0xaf,0x56,0x68,0x8b,0x97,0x25,0x6a,0x9c,0x96,0xbe,0x6b,0xcd,0xc9,0xd1,0xa6,0xc3,0xd4,0x29,0x4f,0x2f,0x2,0xf,0xb2,0x54,0x62,0x53,0xa2,0xa8,0xa9,0xef,0xf2,0x45,0x18,0x35,0x1a,0xcb,0xd9,0x4c,0x6,0x41,0xeb,0xf4,0x75,0xc5,0xdc,0xb3,0x57,0x9,0x8f,0x8,0x39,0x2b,0x11,0xaa,0xc,0x6f,0x12,0x4a,0x3a,0xf7,0xb6,0x2c,0xc4,0xde,0x24,0xdb,0xb5,0x84,0x2a,0x3,0xfc,0xe1,0x49,0xbd,0x80,0x34,0xe6,0x78,0x2e,0xfd,0xa4,0x20,0xfe,0x72,0xc7,0x14,0x61,0xe7,0x22,0xca,0x33,0xff,0x95,0x5f,0x50,0x16,0x21,0x7c,0x7a,0x90,0x5b,0x40,0xec,0x19,0xe5,0x79,0x7,0xe,0x8a,0x86,0x36,0x9e,0x59,0x81,0x1e,0x1c,0xcf,0x10,0x42,0x9b,0x66,0xb8,0x69,0xc8,0x65,0xae,0x91,0x47,0xf8,0xf5,0xb7,0xc6,0xed,0xa,0xb1,0xce,0x5e,0x99,0x43,0x7e,0xa7,0x9a,0xb,0xe0,0x89,0xd3,0x76,0xf3,0xba,0x4b,0x82,0x30,0xe8,0xe9,0xad,0x26,0x5c,0xfb,0xee,0x60,0xc0,0xb0,0xbb,0x0,0x1f,0xd7,0x2b,0xde,0x72,0xb8,0x3c,0x35,0x4b,0x6b,0xac,0x4,0xb4,0xfd,0x2e,0x2c,0xb3,0x54,0xa9,0x70,0x22,0x57,0xfa,0x5b,0x8a,0xca,0x75,0xa3,0x9c,0xdf,0xf4,0x85,0xc7,0x6c,0xfc,0x83,0x38,0x95,0x4c,0x71,0xab,0xbb,0xd2,0x39,0xa8,0x88,0xc1,0x44,0xe1,0xda,0x2,0xb0,0x79,0x6e,0x14,0x9f,0xdb,0xf2,0x52,0xdc,0xc9,0x2d,0x32,0x89,0x82,0xd9,0x73,0x34,0x7e,0xee,0xf7,0x47,0xc6,0xbd,0x3b,0x65,0x81,0x23,0x19,0xb,0x3a,0x20,0x5d,0x3e,0x98,0x84,0xc5,0x8,0x78,0x16,0xec,0xf6,0x1e,0x18,0xb6,0x87,0xe9,0x7b,0xd3,0xce,0x31,0xd4,0x6,0xb2,0x8f,0x96,0xcf,0x1c,0x4a,0xf5,0x40,0xcc,0x12,0x10,0xd5,0x53,0x26,0xa7,0xcd,0x1,0xf8,0x13,0x24,0x62,0x6d,0x69,0xa2,0x48,0x4e,0x3f,0x67,0xfe,0x0,0x7a,0xed,0x2f,0x56,0x37,0xd8,0x99,0x1a,0xef,0xe2,0xd1,0xd6,0xc8,0x8b,0x76,0xa0,0x4f,0xc4,0x4d,0x43,0x9d,0x42,0x97,0xf3,0xa5,0xb9,0x5a,0x64,0xa4,0xae,0x58,0x17,0xfb,0xff,0x59,0x8c,0xe6,0xf1,0x94,0xe3,0x30,0x1d,0x7d,0x1b,0x50,0x66,0x80,0x3d,0x9b,0x9a,0x90,0x61,0x2a,0x77,0xc0,0xdd,0xeb,0xf9,0x28,0x7,0x3,0x91,0xb7,0x15,0xba,0xa1,0xaa,0x7c,0xad,0x36,0xf0,0x11,0xb1,0x51,0xbe,0x74,0x46,0xc3,0x92,0xe,0x33,0x5c,0xbc,0x49,0xbf,0x68,0xa6,0x9,0x9e,0xe8,0xe7,0x60,0x6a,0xd,0xa,0x93,0x6f,0xf,0xb5,0x41,0x8d,0xe5,0x45,0x1f,0x86,0xaf,0x27,0xcb,0x55,0x21,0xea,0xc2,0xd0,0xc,0x7f,0x5f,0x29,0xe4,0x5,0xe0,0x8e,0x5e,0x25,0x63,0xcc,0x6a,0xd2,0xaf,0xfa,0x8a,0x76,0x37,0x4,0xec,0xe4,0x1e,0x75,0x1b,0xea,0x44,0xc6,0x8c,0x2b,0x81,0xb5,0x34,0x1c,0x5,0x97,0x73,0x4f,0xc9,0xf9,0xc8,0xd1,0xeb,0xa1,0xd4,0xe2,0x27,0xf3,0xa,0x55,0x3f,0x90,0x9f,0xe1,0xd6,0xba,0xbc,0x9b,0x50,0x3c,0xc3,0x89,0x21,0x40,0x7d,0x26,0xf4,0xee,0xb8,0x64,0x3d,0x3e,0xe0,0x7,0xb2,0x82,0xd0,0xa6,0x5b,0xa9,0x78,0xa5,0x8,0x51,0x6e,0x38,0x87,0x77,0x35,0x2d,0x6,0x2c,0x80,0x25,0xd9,0xc7,0xb9,0x4a,0xce,0xf6,0x46,0x99,0x5e,0xde,0x41,0xf,0xdc,0x42,0x8b,0x28,0xf0,0x6d,0x29,0x9c,0xe6,0x2e,0x3b,0x0,0xa0,0x7b,0x70,0xdf,0xc0,0x71,0xca,0x9e,0xe,0x83,0x59,0x67,0xbe,0xcb,0x5a,0x49,0x20,0xb6,0x13,0x7a,0x33,0x60,0xfc,0xb4,0x31,0x4e,0xbb,0xc1,0xae,0x54,0xfb,0x4d,0x9a,0x15,0x92,0x6c,0x1a,0x45,0xe7,0xf1,0x63,0x58,0x8e,0x48,0x53,0x2,0xe3,0x5f,0xc4,0x4c,0x86,0x43,0xa3,0x18,0x30,0xa7,0xd3,0x8d,0xad,0x22,0xfe,0xf7,0x12,0xdb,0x16,0xd7,0x91,0x7c,0xac,0xf8,0x61,0x98,0xff,0x47,0xb3,0x9d,0xfd,0xb7,0xed,0x7f,0x17,0xd5,0x39,0x74,0x5d,0x84,0x52,0x3a,0x79,0xbf,0xb1,0xbd,0x36,0x65,0x1,0x6f,0xb0,0xa8,0x96,0x57,0x4b,0xc,0xf2,0xcd,0x95,0xdd,0xa4,0x88,0x1f,0x6b,0xe8,0xc5,0x2a,0x23,0x24,0x1d,0x10,0x72,0xcf,0xa2,0x94,0x62,0x93,0x69,0x68,0x32,0x2f,0xd8,0x85,0xda,0xf5,0x19,0xb,0xaa,0xe5,0x56,0x5c,0xab,0x7e,0x9,0xd,0x66,0x11,0x14,0x3,0x8f,0xe9,0xc2,0xef,0xb5,0x72,0xad,0x1d,0x37,0xe4,0xaa,0x35,0x32,0xce,0x6b,0xc7,0x25,0xa1,0x52,0x2c,0x6c,0xd3,0x85,0xba,0xed,0xc6,0xde,0x9c,0xb0,0x4d,0x3b,0x69,0xe3,0x4e,0x93,0x42,0xcb,0xa2,0xb1,0x20,0xd8,0x91,0xf8,0x5d,0xe5,0x75,0x21,0x9a,0x55,0x8c,0xb2,0x68,0x4b,0xeb,0xd0,0xc5,0x2b,0x34,0x9b,0x90,0x1b,0xc3,0x60,0xa9,0xd,0x77,0xc2,0x86,0x22,0xa4,0x98,0x7c,0x0,0x3a,0x23,0x12,0x6a,0xc0,0x67,0x2d,0xee,0xf7,0xdf,0x5e,0xf5,0xf,0x7,0xef,0xaf,0x1,0xf0,0x9e,0x44,0x39,0x81,0x27,0xdc,0x9d,0x61,0x11,0xd6,0x8f,0x53,0x5,0x59,0xec,0xb,0xd5,0xca,0x62,0x28,0xd7,0x1f,0xcd,0x96,0xab,0x3d,0xa,0x74,0x7b,0xbb,0x70,0x57,0x51,0xcc,0x9,0x3f,0x4a,0xd4,0xbe,0xe1,0x18,0xc1,0x2e,0x3,0x80,0xfb,0xf6,0xcf,0xc8,0x7e,0x26,0x19,0xe7,0xf4,0x63,0x4f,0x36,0x5b,0x84,0xea,0x8e,0xa0,0xbc,0x7d,0x43,0x92,0xd1,0xb9,0x6f,0xdd,0x56,0x5a,0x54,0xe8,0xff,0xfa,0x8d,0x4,0x29,0x2,0x64,0xb7,0xbd,0xe,0x41,0xe6,0xe2,0x95,0x40,0x6e,0x33,0xc4,0xd9,0xe0,0xf2,0x1e,0x31,0x7f,0x49,0x24,0x99,0x83,0x82,0x78,0x89,0x2f,0xb4,0x8,0xe9,0x48,0xa8,0x6d,0xa7,0x88,0x1a,0xc,0xae,0xb8,0xa3,0x65,0xb3,0x71,0xa6,0x10,0xbf,0xf1,0x87,0x79,0xfe,0xda,0x5f,0x17,0x8b,0x45,0x2a,0x50,0xa5,0xfc,0x94,0x6,0x5c,0xb6,0x9f,0xd2,0x3e,0x14,0x73,0x8a,0x13,0x16,0x76,0x58,0xac,0xfd,0x30,0xf9,0x1c,0x47,0x97,0x7a,0x3c,0x38,0x4c,0xdb,0xf3,0x15,0xc9,0x46,0x66,0x26,0x84,0xa2,0x30,0x4f,0x99,0x92,0x89,0x22,0xc3,0x5,0x9e,0x47,0x8d,0x62,0x82,0x3d,0xa1,0xf0,0x75,0x7a,0x8f,0x6f,0x0,0x3a,0x95,0x5b,0x8c,0x53,0xd4,0xdb,0xad,0xa0,0x39,0x3e,0x59,0x72,0x86,0x3c,0x5c,0x2c,0x76,0xd6,0xbe,0xf8,0x14,0x9c,0xb5,0xf1,0xd9,0x12,0x66,0x6c,0x4c,0x3f,0xe3,0xd3,0x36,0xd7,0x1a,0x50,0x16,0x6d,0xbd,0x33,0xcd,0x54,0xc,0x65,0x1c,0xde,0x49,0x29,0xaa,0xeb,0x4,0xe5,0xe2,0xd1,0xdc,0x93,0x45,0xb8,0xfb,0x70,0x7e,0xf7,0x7c,0xc0,0xa4,0x71,0xae,0x57,0x69,0x8a,0x96,0x24,0x6b,0x9d,0x97,0xbf,0x6a,0xcc,0xc8,0xd0,0xa7,0xc2,0xd5,0x28,0x4e,0x2e,0x3,0xe,0xb3,0x55,0x63,0x52,0xa3,0xa9,0xa8,0xee,0xf3,0x44,0x19,0x34,0x1b,0xca,0xd8,0x4d,0x7,0x40,0xea,0xf5,0x74,0xc4,0xdd,0xb2,0x56,0x8,0x8e,0x9,0x38,0x2a,0x10,0xab,0xd,0x6e,0x13,0x4b,0x3b,0xf6,0xb7,0x2d,0xc5,0xdf,0x25,0xda,0xb4,0x85,0x2b,0x2,0xfd,0xe0,0x48,0xbc,0x81,0x35,0xe7,0x79,0x2f,0xfc,0xa5,0x21,0xff,0x73,0xc6,0x15,0x60,0xe6,0x23,0xcb,0x32,0xfe,0x94,0x5e,0x51,0x17,0x20,0x7d,0x7b,0x91,0x5a,0x41,0xed,0x18,0xe4,0x78,0x6,0xf,0x8b,0x87,0x37,0x9f,0x58,0x80,0x1f,0x1d,0xce,0x11,0x43,0x9a,0x67,0xb9,0x68,0xc9,0x64,0xaf,0x90,0x46,0xf9,0xf4,0xb6,0xc7,0xec,0xb,0xb0,0xcf,0x5f,0x98,0x42,0x7f,0xa6,0x9b,0xa,0xe1,0x88,0xd2,0x77,0xf2,0xbb,0x4a,0x83,0x31,0xe9,0xe8,0xac,0x27,0x5d,0xfa,0xef,0x61,0xc1,0xb1,0xba,0x1,0x1e,0x99,0x65,0x90,0x3c,0xf6,0x72,0x7b,0x5,0x25,0xe2,0x4a,0xfa,0xb3,0x60,0x62,0xfd,0x1a,0xe7,0x3e,0x6c,0x19,0xb4,0x15,0xc4,0x84,0x3b,0xed,0xd2,0x91,0xba,0xcb,0x89,0x22,0xb2,0xcd,0x76,0xdb,0x2,0x3f,0xe5,0xf5,0x9c,0x77,0xe6,0xc6,0x8f,0xa,0xaf,0x94,0x4c,0xfe,0x37,0x20,0x5a,0xd1,0x95,0xbc,0x1c,0x92,0x87,0x63,0x7c,0xc7,0xcc,0x97,0x3d,0x7a,0x30,0xa0,0xb9,0x9,0x88,0xf3,0x75,0x2b,0xcf,0x6d,0x57,0x45,0x74,0x6e,0x13,0x70,0xd6,0xca,0x8b,0x46,0x36,0x58,0xa2,0xb8,0x50,0x56,0xf8,0xc9,0xa7,0x35,0x9d,0x80,0x7f,0x9a,0x48,0xfc,0xc1,0xd8,0x81,0x52,0x4,0xbb,0xe,0x82,0x5c,0x5e,0x9b,0x1d,0x68,0xe9,0x83,0x4f,0xb6,0x5d,0x6a,0x2c,0x23,0x27,0xec,0x6,0x0,0x71,0x29,0xb0,0x4e,0x34,0xa3,0x61,0x18,0x79,0x96,0xd7,0x54,0xa1,0xac,0x9f,0x98,0x86,0xc5,0x38,0xee,0x1,0x8a,0x3,0xd,0xd3,0xc,0xd9,0xbd,0xeb,0xf7,0x14,0x2a,0xea,0xe0,0x16,0x59,0xb5,0xb1,0x17,0xc2,0xa8,0xbf,0xda,0xad,0x7e,0x53,0x33,0x55,0x1e,0x28,0xce,0x73,0xd5,0xd4,0xde,0x2f,0x64,0x39,0x8e,0x93,0xa5,0xb7,0x66,0x49,0x4d,0xdf,0xf9,0x5b,0xf4,0xef,0xe4,0x32,0xe3,0x78,0xbe,0x5f,0xff,0x1f,0xf0,0x3a,0x8,0x8d,0xdc,0x40,0x7d,0x12,0xf2,0x7,0xf1,0x26,0xe8,0x47,0xd0,0xa6,0xa9,0x2e,0x24,0x43,0x44,0xdd,0x21,0x41,0xfb,0xf,0xc3,0xab,0xb,0x51,0xc8,0xe1,0x69,0x85,0x1b,0x6f,0xa4,0x8c,0x9e,0x42,0x31,0x11,0x67,0xaa,0x4b,0xae,0xc0,0x10,0x6b,0x2d,0xca,0x6c,0xd4,0xa9,0xfc,0x8c,0x70,0x31,0x2,0xea,0xe2,0x18,0x73,0x1d,0xec,0x42,0xc0,0x8a,0x2d,0x87,0xb3,0x32,0x1a,0x3,0x91,0x75,0x49,0xcf,0xff,0xce,0xd7,0xed,0xa7,0xd2,0xe4,0x21,0xf5,0xc,0x53,0x39,0x96,0x99,0xe7,0xd0,0xbc,0xba,0x9d,0x56,0x3a,0xc5,0x8f,0x27,0x46,0x7b,0x20,0xf2,0xe8,0xbe,0x62,0x3b,0x38,0xe6,0x1,0xb4,0x84,0xd6,0xa0,0x5d,0xaf,0x7e,0xa3,0xe,0x57,0x68,0x3e,0x81,0x71,0x33,0x2b,0x0,0x2a,0x86,0x23,0xdf,0xc1,0xbf,0x4c,0xc8,0xf0,0x40,0x9f,0x58,0xd8,0x47,0x9,0xda,0x44,0x8d,0x2e,0xf6,0x6b,0x2f,0x9a,0xe0,0x28,0x3d,0x6,0xa6,0x7d,0x76,0xd9,0xc6,0x77,0xcc,0x98,0x8,0x85,0x5f,0x61,0xb8,0xcd,0x5c,0x4f,0x26,0xb0,0x15,0x7c,0x35,0x66,0xfa,0xb2,0x37,0x48,0xbd,0xc7,0xa8,0x52,0xfd,0x4b,0x9c,0x13,0x94,0x6a,0x1c,0x43,0xe1,0xf7,0x65,0x5e,0x88,0x4e,0x55,0x4,0xe5,0x59,0xc2,0x4a,0x80,0x45,0xa5,0x1e,0x36,0xa1,0xd5,0x8b,0xab,0x24,0xf8,0xf1,0x14,0xdd,0x10,0xd1,0x97,0x7a,0xaa,0xfe,0x67,0x9e,0xf9,0x41,0xb5,0x9b,0xfb,0xb1,0xeb,0x79,0x11,0xd3,0x3f,0x72,0x5b,0x82,0x54,0x3c,0x7f,0xb9,0xb7,0xbb,0x30,0x63,0x7,0x69,0xb6,0xae,0x90,0x51,0x4d,0xa,0xf4,0xcb,0x93,0xdb,0xa2,0x8e,0x19,0x6d,0xee,0xc3,0x2c,0x25,0x22,0x1b,0x16,0x74,0xc9,0xa4,0x92,0x64,0x95,0x6f,0x6e,0x34,0x29,0xde,0x83,0xdc,0xf3,0x1f,0xd,0xac,0xe3,0x50,0x5a,0xad,0x78,0xf,0xb,0x60,0x17,0x12,0x5,0x89,0xef,0xc4,0xe9,0x9f,0x58,0x87,0x37,0x1d,0xce,0x80,0x1f,0x18,0xe4,0x41,0xed,0xf,0x8b,0x78,0x6,0x46,0xf9,0xaf,0x90,0xc7,0xec,0xf4,0xb6,0x9a,0x67,0x11,0x43,0xc9,0x64,0xb9,0x68,0xe1,0x88,0x9b,0xa,0xf2,0xbb,0xd2,0x77,0xcf,0x5f,0xb,0xb0,0x7f,0xa6,0x98,0x42,0x61,0xc1,0xfa,0xef,0x1,0x1e,0xb1,0xba,0x31,0xe9,0x4a,0x83,0x27,0x5d,0xe8,0xac,0x8,0x8e,0xb2,0x56,0x2a,0x10,0x9,0x38,0x40,0xea,0x4d,0x7,0xc4,0xdd,0xf5,0x74,0xdf,0x25,0x2d,0xc5,0x85,0x2b,0xda,0xb4,0x6e,0x13,0xab,0xd,0xf6,0xb7,0x4b,0x3b,0xfc,0xa5,0x79,0x2f,0x73,0xc6,0x21,0xff,0xe0,0x48,0x2,0xfd,0x35,0xe7,0xbc,0x81,0x17,0x20,0x5e,0x51,0x91,0x5a,0x7d,0x7b,0xe6,0x23,0x15,0x60,0xfe,0x94,0xcb,0x32,0xeb,0x4,0x29,0xaa,0xd1,0xdc,0xe5,0xe2,0x54,0xc,0x33,0xcd,0xde,0x49,0x65,0x1c,0x71,0xae,0xc0,0xa4,0x8a,0x96,0x57,0x69,0xb8,0xfb,0x93,0x45,0xf7,0x7c,0x70,0x7e,0xc2,0xd5,0xd0,0xa7,0x2e,0x3,0x28,0x4e,0x9d,0x97,0x24,0x6b,0xcc,0xc8,0xbf,0x6a,0x44,0x19,0xee,0xf3,0xca,0xd8,0x34,0x1b,0x55,0x63,0xe,0xb3,0xa9,0xa8,0x52,0xa3,0x5,0x9e,0x22,0xc3,0x62,0x82,0x47,0x8d,0xa2,0x30,0x26,0x84,0x92,0x89,0x4f,0x99,0x5b,0x8c,0x3a,0x95,0xdb,0xad,0x53,0xd4,0xf0,0x75,0x3d,0xa1,0x6f,0x0,0x7a,0x8f,0xd6,0xbe,0x2c,0x76,0x9c,0xb5,0xf8,0x14,0x3e,0x59,0xa0,0x39,0x3c,0x5c,0x72,0x86,0xd7,0x1a,0xd3,0x36,0x6d,0xbd,0x50,0x16,0x12,0x66,0xf1,0xd9,0x3f,0xe3,0x6c,0x4c,0x9f,0x3d,0x1b,0x89,0xf6,0x20,0x2b,0x30,0x9b,0x7a,0xbc,0x27,0xfe,0x34,0xdb,0x3b,0x84,0x18,0x49,0xcc,0xc3,0x36,0xd6,0xb9,0x83,0x2c,0xe2,0x35,0xea,0x6d,0x62,0x14,0x19,0x80,0x87,0xe0,0xcb,0x3f,0x85,0xe5,0x95,0xcf,0x6f,0x7,0x41,0xad,0x25,0xc,0x48,0x60,0xab,0xdf,0xd5,0xf5,0x86,0x5a,0x6a,0x8f,0x6e,0xa3,0xe9,0xaf,0xd4,0x4,0x8a,0x74,0xed,0xb5,0xdc,0xa5,0x67,0xf0,0x90,0x13,0x52,0xbd,0x5c,0x5b,0x68,0x65,0x2a,0xfc,0x1,0x42,0xc9,0xc7,0x4e,0xc5,0x79,0x1d,0xc8,0x17,0xee,0xd0,0x33,0x2f,0x9d,0xd2,0x24,0x2e,0x6,0xd3,0x75,0x71,0x69,0x1e,0x7b,0x6c,0x91,0xf7,0x97,0xba,0xb7,0xa,0xec,0xda,0xeb,0x1a,0x10,0x11,0x57,0x4a,0xfd,0xa0,0x8d,0xa2,0x73,0x61,0xf4,0xbe,0xf9,0x53,0x4c,0xcd,0x7d,0x64,0xb,0xef,0xb1,0x37,0xb0,0x81,0x93,0xa9,0x12,0xb4,0xd7,0xaa,0xf2,0x82,0x4f,0xe,0x94,0x7c,0x66,0x9c,0x63,0xd,0x3c,0x92,0xbb,0x44,0x59,0xf1,0x5,0x38,0x8c,0x5e,0xc0,0x96,0x45,0x1c,0x98,0x46,0xca,0x7f,0xac,0xd9,0x5f,0x9a,0x72,0x8b,0x47,0x2d,0xe7,0xe8,0xae,0x99,0xc4,0xc2,0x28,0xe3,0xf8,0x54,0xa1,0x5d,0xc1,0xbf,0xb6,0x32,0x3e,0x8e,0x26,0xe1,0x39,0xa6,0xa4,0x77,0xa8,0xfa,0x23,0xde,0x0,0xd1,0x70,0xdd,0x16,0x29,0xff,0x40,0x4d,0xf,0x7e,0x55,0xb2,0x9,0x76,0xe6,0x21,0xfb,0xc6,0x1f,0x22,0xb3,0x58,0x31,0x6b,0xce,0x4b,0x2,0xf3,0x3a,0x88,0x50,0x51,0x15,0x9e,0xe4,0x43,0x56,0xd8,0x78,0x8,0x3,0xb8,0xa7,0xe7,0x1b,0xee,0x42,0x88,0xc,0x5,0x7b,0x5b,0x9c,0x34,0x84,0xcd,0x1e,0x1c,0x83,0x64,0x99,0x40,0x12,0x67,0xca,0x6b,0xba,0xfa,0x45,0x93,0xac,0xef,0xc4,0xb5,0xf7,0x5c,0xcc,0xb3,0x8,0xa5,0x7c,0x41,0x9b,0x8b,0xe2,0x9,0x98,0xb8,0xf1,0x74,0xd1,0xea,0x32,0x80,0x49,0x5e,0x24,0xaf,0xeb,0xc2,0x62,0xec,0xf9,0x1d,0x2,0xb9,0xb2,0xe9,0x43,0x4,0x4e,0xde,0xc7,0x77,0xf6,0x8d,0xb,0x55,0xb1,0x13,0x29,0x3b,0xa,0x10,0x6d,0xe,0xa8,0xb4,0xf5,0x38,0x48,0x26,0xdc,0xc6,0x2e,0x28,0x86,0xb7,0xd9,0x4b,0xe3,0xfe,0x1,0xe4,0x36,0x82,0xbf,0xa6,0xff,0x2c,0x7a,0xc5,0x70,0xfc,0x22,0x20,0xe5,0x63,0x16,0x97,0xfd,0x31,0xc8,0x23,0x14,0x52,0x5d,0x59,0x92,0x78,0x7e,0xf,0x57,0xce,0x30,0x4a,0xdd,0x1f,0x66,0x7,0xe8,0xa9,0x2a,0xdf,0xd2,0xe1,0xe6,0xf8,0xbb,0x46,0x90,0x7f,0xf4,0x7d,0x73,0xad,0x72,0xa7,0xc3,0x95,0x89,0x6a,0x54,0x94,0x9e,0x68,0x27,0xcb,0xcf,0x69,0xbc,0xd6,0xc1,0xa4,0xd3,0x0,0x2d,0x4d,0x2b,0x60,0x56,0xb0,0xd,0xab,0xaa,0xa0,0x51,0x1a,0x47,0xf0,0xed,0xdb,0xc9,0x18,0x37,0x33,0xa1,0x87,0x25,0x8a,0x91,0x9a,0x4c,0x9d,0x6,0xc0,0x21,0x81,0x61,0x8e,0x44,0x76,0xf3,0xa2,0x3e,0x3,0x6c,0x8c,0x79,0x8f,0x58,0x96,0x39,0xae,0xd8,0xd7,0x50,0x5a,0x3d,0x3a,0xa3,0x5f,0x3f,0x85,0x71,0xbd,0xd5,0x75,0x2f,0xb6,0x9f,0x17,0xfb,0x65,0x11,0xda,0xf2,0xe0,0x3c,0x4f,0x6f,0x19,0xd4,0x35,0xd0,0xbe,0x6e,0x15,0x53,0xa7,0x1,0xb9,0xc4,0x91,0xe1,0x1d,0x5c,0x6f,0x87,0x8f,0x75,0x1e,0x70,0x81,0x2f,0xad,0xe7,0x40,0xea,0xde,0x5f,0x77,0x6e,0xfc,0x18,0x24,0xa2,0x92,0xa3,0xba,0x80,0xca,0xbf,0x89,0x4c,0x98,0x61,0x3e,0x54,0xfb,0xf4,0x8a,0xbd,0xd1,0xd7,0xf0,0x3b,0x57,0xa8,0xe2,0x4a,0x2b,0x16,0x4d,0x9f,0x85,0xd3,0xf,0x56,0x55,0x8b,0x6c,0xd9,0xe9,0xbb,0xcd,0x30,0xc2,0x13,0xce,0x63,0x3a,0x5,0x53,0xec,0x1c,0x5e,0x46,0x6d,0x47,0xeb,0x4e,0xb2,0xac,0xd2,0x21,0xa5,0x9d,0x2d,0xf2,0x35,0xb5,0x2a,0x64,0xb7,0x29,0xe0,0x43,0x9b,0x6,0x42,0xf7,0x8d,0x45,0x50,0x6b,0xcb,0x10,0x1b,0xb4,0xab,0x1a,0xa1,0xf5,0x65,0xe8,0x32,0xc,0xd5,0xa0,0x31,0x22,0x4b,0xdd,0x78,0x11,0x58,0xb,0x97,0xdf,0x5a,0x25,0xd0,0xaa,0xc5,0x3f,0x90,0x26,0xf1,0x7e,0xf9,0x7,0x71,0x2e,0x8c,0x9a,0x8,0x33,0xe5,0x23,0x38,0x69,0x88,0x34,0xaf,0x27,0xed,0x28,0xc8,0x73,0x5b,0xcc,0xb8,0xe6,0xc6,0x49,0x95,0x9c,0x79,0xb0,0x7d,0xbc,0xfa,0x17,0xc7,0x93,0xa,0xf3,0x94,0x2c,0xd8,0xf6,0x96,0xdc,0x86,0x14,0x7c,0xbe,0x52,0x1f,0x36,0xef,0x39,0x51,0x12,0xd4,0xda,0xd6,0x5d,0xe,0x6a,0x4,0xdb,0xc3,0xfd,0x3c,0x20,0x67,0x99,0xa6,0xfe,0xb6,0xcf,0xe3,0x74,0x0,0x83,0xae,0x41,0x48,0x4f,0x76,0x7b,0x19,0xa4,0xc9,0xff,0x9,0xf8,0x2,0x3,0x59,0x44,0xb3,0xee,0xb1,0x9e,0x72,0x60,0xc1,0x8e,0x3d,0x37,0xc0,0x15,0x62,0x66,0xd,0x7a,0x7f,0x68,0xe4,0x82,0xa9,0x84,0xb,0xcc,0x13,0xa3,0x89,0x5a,0x14,0x8b,0x8c,0x70,0xd5,0x79,0x9b,0x1f,0xec,0x92,0xd2,0x6d,0x3b,0x4,0x53,0x78,0x60,0x22,0xe,0xf3,0x85,0xd7,0x5d,0xf0,0x2d,0xfc,0x75,0x1c,0xf,0x9e,0x66,0x2f,0x46,0xe3,0x5b,0xcb,0x9f,0x24,0xeb,0x32,0xc,0xd6,0xf5,0x55,0x6e,0x7b,0x95,0x8a,0x25,0x2e,0xa5,0x7d,0xde,0x17,0xb3,0xc9,0x7c,0x38,0x9c,0x1a,0x26,0xc2,0xbe,0x84,0x9d,0xac,0xd4,0x7e,0xd9,0x93,0x50,0x49,0x61,0xe0,0x4b,0xb1,0xb9,0x51,0x11,0xbf,0x4e,0x20,0xfa,0x87,0x3f,0x99,0x62,0x23,0xdf,0xaf,0x68,0x31,0xed,0xbb,0xe7,0x52,0xb5,0x6b,0x74,0xdc,0x96,0x69,0xa1,0x73,0x28,0x15,0x83,0xb4,0xca,0xc5,0x5,0xce,0xe9,0xef,0x72,0xb7,0x81,0xf4,0x6a,0x0,0x5f,0xa6,0x7f,0x90,0xbd,0x3e,0x45,0x48,0x71,0x76,0xc0,0x98,0xa7,0x59,0x4a,0xdd,0xf1,0x88,0xe5,0x3a,0x54,0x30,0x1e,0x2,0xc3,0xfd,0x2c,0x6f,0x7,0xd1,0x63,0xe8,0xe4,0xea,0x56,0x41,0x44,0x33,0xba,0x97,0xbc,0xda,0x9,0x3,0xb0,0xff,0x58,0x5c,0x2b,0xfe,0xd0,0x8d,0x7a,0x67,0x5e,0x4c,0xa0,0x8f,0xc1,0xf7,0x9a,0x27,0x3d,0x3c,0xc6,0x37,0x91,0xa,0xb6,0x57,0xf6,0x16,0xd3,0x19,0x36,0xa4,0xb2,0x10,0x6,0x1d,0xdb,0xd,0xcf,0x18,0xae,0x1,0x4f,0x39,0xc7,0x40,0x64,0xe1,0xa9,0x35,0xfb,0x94,0xee,0x1b,0x42,0x2a,0xb8,0xe2,0x8,0x21,0x6c,0x80,0xaa,0xcd,0x34,0xad,0xa8,0xc8,0xe6,0x12,0x43,0x8e,0x47,0xa2,0xf9,0x29,0xc4,0x82,0x86,0xf2,0x65,0x4d,0xab,0x77,0xf8,0xd8,0xea,0x48,0x6e,0xfc,0x83,0x55,0x5e,0x45,0xee,0xf,0xc9,0x52,0x8b,0x41,0xae,0x4e,0xf1,0x6d,0x3c,0xb9,0xb6,0x43,0xa3,0xcc,0xf6,0x59,0x97,0x40,0x9f,0x18,0x17,0x61,0x6c,0xf5,0xf2,0x95,0xbe,0x4a,0xf0,0x90,0xe0,0xba,0x1a,0x72,0x34,0xd8,0x50,0x79,0x3d,0x15,0xde,0xaa,0xa0,0x80,0xf3,0x2f,0x1f,0xfa,0x1b,0xd6,0x9c,0xda,0xa1,0x71,0xff,0x1,0x98,0xc0,0xa9,0xd0,0x12,0x85,0xe5,0x66,0x27,0xc8,0x29,0x2e,0x1d,0x10,0x5f,0x89,0x74,0x37,0xbc,0xb2,0x3b,0xb0,0xc,0x68,0xbd,0x62,0x9b,0xa5,0x46,0x5a,0xe8,0xa7,0x51,0x5b,0x73,0xa6,0x0,0x4,0x1c,0x6b,0xe,0x19,0xe4,0x82,0xe2,0xcf,0xc2,0x7f,0x99,0xaf,0x9e,0x6f,0x65,0x64,0x22,0x3f,0x88,0xd5,0xf8,0xd7,0x6,0x14,0x81,0xcb,0x8c,0x26,0x39,0xb8,0x8,0x11,0x7e,0x9a,0xc4,0x42,0xc5,0xf4,0xe6,0xdc,0x67,0xc1,0xa2,0xdf,0x87,0xf7,0x3a,0x7b,0xe1,0x9,0x13,0xe9,0x16,0x78,0x49,0xe7,0xce,0x31,0x2c,0x84,0x70,0x4d,0xf9,0x2b,0xb5,0xe3,0x30,0x69,0xed,0x33,0xbf,0xa,0xd9,0xac,0x2a,0xef,0x7,0xfe,0x32,0x58,0x92,0x9d,0xdb,0xec,0xb1,0xb7,0x5d,0x96,0x8d,0x21,0xd4,0x28,0xb4,0xca,0xc3,0x47,0x4b,0xfb,0x53,0x94,0x4c,0xd3,0xd1,0x2,0xdd,0x8f,0x56,0xab,0x75,0xa4,0x5,0xa8,0x63,0x5c,0x8a,0x35,0x38,0x7a,0xb,0x20,0xc7,0x7c,0x3,0x93,0x54,0x8e,0xb3,0x6a,0x57,0xc6,0x2d,0x44,0x1e,0xbb,0x3e,0x77,0x86,0x4f,0xfd,0x25,0x24,0x60,0xeb,0x91,0x36,0x23,0xad,0xd,0x7d,0x76,0xcd,0xd2,0x13,0xef,0x1a,0xb6,0x7c,0xf8,0xf1,0x8f,0xaf,0x68,0xc0,0x70,0x39,0xea,0xe8,0x77,0x90,0x6d,0xb4,0xe6,0x93,0x3e,0x9f,0x4e,0xe,0xb1,0x67,0x58,0x1b,0x30,0x41,0x3,0xa8,0x38,0x47,0xfc,0x51,0x88,0xb5,0x6f,0x7f,0x16,0xfd,0x6c,0x4c,0x5,0x80,0x25,0x1e,0xc6,0x74,0xbd,0xaa,0xd0,0x5b,0x1f,0x36,0x96,0x18,0xd,0xe9,0xf6,0x4d,0x46,0x1d,0xb7,0xf0,0xba,0x2a,0x33,0x83,0x2,0x79,0xff,0xa1,0x45,0xe7,0xdd,0xcf,0xfe,0xe4,0x99,0xfa,0x5c,0x40,0x1,0xcc,0xbc,0xd2,0x28,0x32,0xda,0xdc,0x72,0x43,0x2d,0xbf,0x17,0xa,0xf5,0x10,0xc2,0x76,0x4b,0x52,0xb,0xd8,0x8e,0x31,0x84,0x8,0xd6,0xd4,0x11,0x97,0xe2,0x63,0x9,0xc5,0x3c,0xd7,0xe0,0xa6,0xa9,0xad,0x66,0x8c,0x8a,0xfb,0xa3,0x3a,0xc4,0xbe,0x29,0xeb,0x92,0xf3,0x1c,0x5d,0xde,0x2b,0x26,0x15,0x12,0xc,0x4f,0xb2,0x64,0x8b,0x0,0x89,0x87,0x59,0x86,0x53,0x37,0x61,0x7d,0x9e,0xa0,0x60,0x6a,0x9c,0xd3,0x3f,0x3b,0x9d,0x48,0x22,0x35,0x50,0x27,0xf4,0xd9,0xb9,0xdf,0x94,0xa2,0x44,0xf9,0x5f,0x5e,0x54,0xa5,0xee,0xb3,0x4,0x19,0x2f,0x3d,0xec,0xc3,0xc7,0x55,0x73,0xd1,0x7e,0x65,0x6e,0xb8,0x69,0xf2,0x34,0xd5,0x75,0x95,0x7a,0xb0,0x82,0x7,0x56,0xca,0xf7,0x98,0x78,0x8d,0x7b,0xac,0x62,0xcd,0x5a,0x2c,0x23,0xa4,0xae,0xc9,0xce,0x57,0xab,0xcb,0x71,0x85,0x49,0x21,0x81,0xdb,0x42,0x6b,0xe3,0xf,0x91,0xe5,0x2e,0x6,0x14,0xc8,0xbb,0x9b,0xed,0x20,0xc1,0x24,0x4a,0x9a,0xe1,0xa7,0x18,0xbe,0x6,0x7b,0x2e,0x5e,0xa2,0xe3,0xd0,0x38,0x30,0xca,0xa1,0xcf,0x3e,0x90,0x12,0x58,0xff,0x55,0x61,0xe0,0xc8,0xd1,0x43,0xa7,0x9b,0x1d,0x2d,0x1c,0x5,0x3f,0x75,0x0,0x36,0xf3,0x27,0xde,0x81,0xeb,0x44,0x4b,0x35,0x2,0x6e,0x68,0x4f,0x84,0xe8,0x17,0x5d,0xf5,0x94,0xa9,0xf2,0x20,0x3a,0x6c,0xb0,0xe9,0xea,0x34,0xd3,0x66,0x56,0x4,0x72,0x8f,0x7d,0xac,0x71,0xdc,0x85,0xba,0xec,0x53,0xa3,0xe1,0xf9,0xd2,0xf8,0x54,0xf1,0xd,0x13,0x6d,0x9e,0x1a,0x22,0x92,0x4d,0x8a,0xa,0x95,0xdb,0x8,0x96,0x5f,0xfc,0x24,0xb9,0xfd,0x48,0x32,0xfa,0xef,0xd4,0x74,0xaf,0xa4,0xb,0x14,0xa5,0x1e,0x4a,0xda,0x57,0x8d,0xb3,0x6a,0x1f,0x8e,0x9d,0xf4,0x62,0xc7,0xae,0xe7,0xb4,0x28,0x60,0xe5,0x9a,0x6f,0x15,0x7a,0x80,0x2f,0x99,0x4e,0xc1,0x46,0xb8,0xce,0x91,0x33,0x25,0xb7,0x8c,0x5a,0x9c,0x87,0xd6,0x37,0x8b,0x10,0x98,0x52,0x97,0x77,0xcc,0xe4,0x73,0x7,0x59,0x79,0xf6,0x2a,0x23,0xc6,0xf,0xc2,0x3,0x45,0xa8,0x78,0x2c,0xb5,0x4c,0x2b,0x93,0x67,0x49,0x29,0x63,0x39,0xab,0xc3,0x1,0xed,0xa0,0x89,0x50,0x86,0xee,0xad,0x6b,0x65,0x69,0xe2,0xb1,0xd5,0xbb,0x64,0x7c,0x42,0x83,0x9f,0xd8,0x26,0x19,0x41,0x9,0x70,0x5c,0xcb,0xbf,0x3c,0x11,0xfe,0xf7,0xf0,0xc9,0xc4,0xa6,0x1b,0x76,0x40,0xb6,0x47,0xbd,0xbc,0xe6,0xfb,0xc,0x51,0xe,0x21,0xcd,0xdf,0x7e,0x31,0x82,0x88,0x7f,0xaa,0xdd,0xd9,0xb2,0xc5,0xc0,0xd7,0x5b,0x3d,0x16,0x3b,0xe4,0x23,0xfc,0x4c,0x66,0xb5,0xfb,0x64,0x63,0x9f,0x3a,0x96,0x74,0xf0,0x3,0x7d,0x3d,0x82,0xd4,0xeb,0xbc,0x97,0x8f,0xcd,0xe1,0x1c,0x6a,0x38,0xb2,0x1f,0xc2,0x13,0x9a,0xf3,0xe0,0x71,0x89,0xc0,0xa9,0xc,0xb4,0x24,0x70,0xcb,0x4,0xdd,0xe3,0x39,0x1a,0xba,0x81,0x94,0x7a,0x65,0xca,0xc1,0x4a,0x92,0x31,0xf8,0x5c,0x26,0x93,0xd7,0x73,0xf5,0xc9,0x2d,0x51,0x6b,0x72,0x43,0x3b,0x91,0x36,0x7c,0xbf,0xa6,0x8e,0xf,0xa4,0x5e,0x56,0xbe,0xfe,0x50,0xa1,0xcf,0x15,0x68,0xd0,0x76,0x8d,0xcc,0x30,0x40,0x87,0xde,0x2,0x54,0x8,0xbd,0x5a,0x84,0x9b,0x33,0x79,0x86,0x4e,0x9c,0xc7,0xfa,0x6c,0x5b,0x25,0x2a,0xea,0x21,0x6,0x0,0x9d,0x58,0x6e,0x1b,0x85,0xef,0xb0,0x49,0x90,0x7f,0x52,0xd1,0xaa,0xa7,0x9e,0x99,0x2f,0x77,0x48,0xb6,0xa5,0x32,0x1e,0x67,0xa,0xd5,0xbb,0xdf,0xf1,0xed,0x2c,0x12,0xc3,0x80,0xe8,0x3e,0x8c,0x7,0xb,0x5,0xb9,0xae,0xab,0xdc,0x55,0x78,0x53,0x35,0xe6,0xec,0x5f,0x10,0xb7,0xb3,0xc4,0x11,0x3f,0x62,0x95,0x88,0xb1,0xa3,0x4f,0x60,0x2e,0x18,0x75,0xc8,0xd2,0xd3,0x29,0xd8,0x7e,0xe5,0x59,0xb8,0x19,0xf9,0x3c,0xf6,0xd9,0x4b,0x5d,0xff,0xe9,0xf2,0x34,0xe2,0x20,0xf7,0x41,0xee,0xa0,0xd6,0x28,0xaf,0x8b,0xe,0x46,0xda,0x14,0x7b,0x1,0xf4,0xad,0xc5,0x57,0xd,0xe7,0xce,0x83,0x6f,0x45,0x22,0xdb,0x42,0x47,0x27,0x9,0xfd,0xac,0x61,0xa8,0x4d,0x16,0xc6,0x2b,0x6d,0x69,0x1d,0x8a,0xa2,0x44,0x98,0x17,0x37,0x90,0x32,0x14,0x86,0xf9,0x2f,0x24,0x3f,0x94,0x75,0xb3,0x28,0xf1,0x3b,0xd4,0x34,0x8b,0x17,0x46,0xc3,0xcc,0x39,0xd9,0xb6,0x8c,0x23,0xed,0x3a,0xe5,0x62,0x6d,0x1b,0x16,0x8f,0x88,0xef,0xc4,0x30,0x8a,0xea,0x9a,0xc0,0x60,0x8,0x4e,0xa2,0x2a,0x3,0x47,0x6f,0xa4,0xd0,0xda,0xfa,0x89,0x55,0x65,0x80,0x61,0xac,0xe6,0xa0,0xdb,0xb,0x85,0x7b,0xe2,0xba,0xd3,0xaa,0x68,0xff,0x9f,0x1c,0x5d,0xb2,0x53,0x54,0x67,0x6a,0x25,0xf3,0xe,0x4d,0xc6,0xc8,0x41,0xca,0x76,0x12,0xc7,0x18,0xe1,0xdf,0x3c,0x20,0x92,0xdd,0x2b,0x21,0x9,0xdc,0x7a,0x7e,0x66,0x11,0x74,0x63,0x9e,0xf8,0x98,0xb5,0xb8,0x5,0xe3,0xd5,0xe4,0x15,0x1f,0x1e,0x58,0x45,0xf2,0xaf,0x82,0xad,0x7c,0x6e,0xfb,0xb1,0xf6,0x5c,0x43,0xc2,0x72,0x6b,0x4,0xe0,0xbe,0x38,0xbf,0x8e,0x9c,0xa6,0x1d,0xbb,0xd8,0xa5,0xfd,0x8d,0x40,0x1,0x9b,0x73,0x69,0x93,0x6c,0x2,0x33,0x9d,0xb4,0x4b,0x56,0xfe,0xa,0x37,0x83,0x51,0xcf,0x99,0x4a,0x13,0x97,0x49,0xc5,0x70,0xa3,0xd6,0x50,0x95,0x7d,0x84,0x48,0x22,0xe8,0xe7,0xa1,0x96,0xcb,0xcd,0x27,0xec,0xf7,0x5b,0xae,0x52,0xce,0xb0,0xb9,0x3d,0x31,0x81,0x29,0xee,0x36,0xa9,0xab,0x78,0xa7,0xf5,0x2c,0xd1,0xf,0xde,0x7f,0xd2,0x19,0x26,0xf0,0x4f,0x42,0x0,0x71,0x5a,0xbd,0x6,0x79,0xe9,0x2e,0xf4,0xc9,0x10,0x2d,0xbc,0x57,0x3e,0x64,0xc1,0x44,0xd,0xfc,0x35,0x87,0x5f,0x5e,0x1a,0x91,0xeb,0x4c,0x59,0xd7,0x77,0x7,0xc,0xb7,0xa8,0x23,0xdf,0x2a,0x86,0x4c,0xc8,0xc1,0xbf,0x9f,0x58,0xf0,0x40,0x9,0xda,0xd8,0x47,0xa0,0x5d,0x84,0xd6,0xa3,0xe,0xaf,0x7e,0x3e,0x81,0x57,0x68,0x2b,0x0,0x71,0x33,0x98,0x8,0x77,0xcc,0x61,0xb8,0x85,0x5f,0x4f,0x26,0xcd,0x5c,0x7c,0x35,0xb0,0x15,0x2e,0xf6,0x44,0x8d,0x9a,0xe0,0x6b,0x2f,0x6,0xa6,0x28,0x3d,0xd9,0xc6,0x7d,0x76,0x2d,0x87,0xc0,0x8a,0x1a,0x3,0xb3,0x32,0x49,0xcf,0x91,0x75,0xd7,0xed,0xff,0xce,0xd4,0xa9,0xca,0x6c,0x70,0x31,0xfc,0x8c,0xe2,0x18,0x2,0xea,0xec,0x42,0x73,0x1d,0x8f,0x27,0x3a,0xc5,0x20,0xf2,0x46,0x7b,0x62,0x3b,0xe8,0xbe,0x1,0xb4,0x38,0xe6,0xe4,0x21,0xa7,0xd2,0x53,0x39,0xf5,0xc,0xe7,0xd0,0x96,0x99,0x9d,0x56,0xbc,0xba,0xcb,0x93,0xa,0xf4,0x8e,0x19,0xdb,0xa2,0xc3,0x2c,0x6d,0xee,0x1b,0x16,0x25,0x22,0x3c,0x7f,0x82,0x54,0xbb,0x30,0xb9,0xb7,0x69,0xb6,0x63,0x7,0x51,0x4d,0xae,0x90,0x50,0x5a,0xac,0xe3,0xf,0xb,0xad,0x78,0x12,0x5,0x60,0x17,0xc4,0xe9,0x89,0xef,0xa4,0x92,0x74,0xc9,0x6f,0x6e,0x64,0x95,0xde,0x83,0x34,0x29,0x1f,0xd,0xdc,0xf3,0xf7,0x65,0x43,0xe1,0x4e,0x55,0x5e,0x88,0x59,0xc2,0x4,0xe5,0x45,0xa5,0x4a,0x80,0xb2,0x37,0x66,0xfa,0xc7,0xa8,0x48,0xbd,0x4b,0x9c,0x52,0xfd,0x6a,0x1c,0x13,0x94,0x9e,0xf9,0xfe,0x67,0x9b,0xfb,0x41,0xb5,0x79,0x11,0xb1,0xeb,0x72,0x5b,0xd3,0x3f,0xa1,0xd5,0x1e,0x36,0x24,0xf8,0x8b,0xab,0xdd,0x10,0xf1,0x14,0x7a,0xaa,0xd1,0x97,0xce,0x68,0xd0,0xad,0xf8,0x88,0x74,0x35,0x6,0xee,0xe6,0x1c,0x77,0x19,0xe8,0x46,0xc4,0x8e,0x29,0x83,0xb7,0x36,0x1e,0x7,0x95,0x71,0x4d,0xcb,0xfb,0xca,0xd3,0xe9,0xa3,0xd6,0xe0,0x25,0xf1,0x8,0x57,0x3d,0x92,0x9d,0xe3,0xd4,0xb8,0xbe,0x99,0x52,0x3e,0xc1,0x8b,0x23,0x42,0x7f,0x24,0xf6,0xec,0xba,0x66,0x3f,0x3c,0xe2,0x5,0xb0,0x80,0xd2,0xa4,0x59,0xab,0x7a,0xa7,0xa,0x53,0x6c,0x3a,0x85,0x75,0x37,0x2f,0x4,0x2e,0x82,0x27,0xdb,0xc5,0xbb,0x48,0xcc,0xf4,0x44,0x9b,0x5c,0xdc,0x43,0xd,0xde,0x40,0x89,0x2a,0xf2,0x6f,0x2b,0x9e,0xe4,0x2c,0x39,0x2,0xa2,0x79,0x72,0xdd,0xc2,0x73,0xc8,0x9c,0xc,0x81,0x5b,0x65,0xbc,0xc9,0x58,0x4b,0x22,0xb4,0x11,0x78,0x31,0x62,0xfe,0xb6,0x33,0x4c,0xb9,0xc3,0xac,0x56,0xf9,0x4f,0x98,0x17,0x90,0x6e,0x18,0x47,0xe5,0xf3,0x61,0x5a,0x8c,0x4a,0x51,0x0,0xe1,0x5d,0xc6,0x4e,0x84,0x41,0xa1,0x1a,0x32,0xa5,0xd1,0x8f,0xaf,0x20,0xfc,0xf5,0x10,0xd9,0x14,0xd5,0x93,0x7e,0xae,0xfa,0x63,0x9a,0xfd,0x45,0xb1,0x9f,0xff,0xb5,0xef,0x7d,0x15,0xd7,0x3b,0x76,0x5f,0x86,0x50,0x38,0x7b,0xbd,0xb3,0xbf,0x34,0x67,0x3,0x6d,0xb2,0xaa,0x94,0x55,0x49,0xe,0xf0,0xcf,0x97,0xdf,0xa6,0x8a,0x1d,0x69,0xea,0xc7,0x28,0x21,0x26,0x1f,0x12,0x70,0xcd,0xa0,0x96,0x60,0x91,0x6b,0x6a,0x30,0x2d,0xda,0x87,0xd8,0xf7,0x1b,0x9,0xa8,0xe7,0x54,0x5e,0xa9,0x7c,0xb,0xf,0x64,0x13,0x16,0x1,0x8d,0xeb,0xc0,0xed,0x2,0xc5,0x1a,0xaa,0x80,0x53,0x1d,0x82,0x85,0x79,0xdc,0x70,0x92,0x16,0xe5,0x9b,0xdb,0x64,0x32,0xd,0x5a,0x71,0x69,0x2b,0x7,0xfa,0x8c,0xde,0x54,0xf9,0x24,0xf5,0x7c,0x15,0x6,0x97,0x6f,0x26,0x4f,0xea,0x52,0xc2,0x96,0x2d,0xe2,0x3b,0x5,0xdf,0xfc,0x5c,0x67,0x72,0x9c,0x83,0x2c,0x27,0xac,0x74,0xd7,0x1e,0xba,0xc0,0x75,0x31,0x95,0x13,0x2f,0xcb,0xb7,0x8d,0x94,0xa5,0xdd,0x77,0xd0,0x9a,0x59,0x40,0x68,0xe9,0x42,0xb8,0xb0,0x58,0x18,0xb6,0x47,0x29,0xf3,0x8e,0x36,0x90,0x6b,0x2a,0xd6,0xa6,0x61,0x38,0xe4,0xb2,0xee,0x5b,0xbc,0x62,0x7d,0xd5,0x9f,0x60,0xa8,0x7a,0x21,0x1c,0x8a,0xbd,0xc3,0xcc,0xc,0xc7,0xe0,0xe6,0x7b,0xbe,0x88,0xfd,0x63,0x9,0x56,0xaf,0x76,0x99,0xb4,0x37,0x4c,0x41,0x78,0x7f,0xc9,0x91,0xae,0x50,0x43,0xd4,0xf8,0x81,0xec,0x33,0x5d,0x39,0x17,0xb,0xca,0xf4,0x25,0x66,0xe,0xd8,0x6a,0xe1,0xed,0xe3,0x5f,0x48,0x4d,0x3a,0xb3,0x9e,0xb5,0xd3,0x0,0xa,0xb9,0xf6,0x51,0x55,0x22,0xf7,0xd9,0x84,0x73,0x6e,0x57,0x45,0xa9,0x86,0xc8,0xfe,0x93,0x2e,0x34,0x35,0xcf,0x3e,0x98,0x3,0xbf,0x5e,0xff,0x1f,0xda,0x10,0x3f,0xad,0xbb,0x19,0xf,0x14,0xd2,0x4,0xc6,0x11,0xa7,0x8,0x46,0x30,0xce,0x49,0x6d,0xe8,0xa0,0x3c,0xf2,0x9d,0xe7,0x12,0x4b,0x23,0xb1,0xeb,0x1,0x28,0x65,0x89,0xa3,0xc4,0x3d,0xa4,0xa1,0xc1,0xef,0x1b,0x4a,0x87,0x4e,0xab,0xf0,0x20,0xcd,0x8b,0x8f,0xfb,0x6c,0x44,0xa2,0x7e,0xf1,0xd1,0x3a,0x98,0xbe,0x2c,0x53,0x85,0x8e,0x95,0x3e,0xdf,0x19,0x82,0x5b,0x91,0x7e,0x9e,0x21,0xbd,0xec,0x69,0x66,0x93,0x73,0x1c,0x26,0x89,0x47,0x90,0x4f,0xc8,0xc7,0xb1,0xbc,0x25,0x22,0x45,0x6e,0x9a,0x20,0x40,0x30,0x6a,0xca,0xa2,0xe4,0x8,0x80,0xa9,0xed,0xc5,0xe,0x7a,0x70,0x50,0x23,0xff,0xcf,0x2a,0xcb,0x6,0x4c,0xa,0x71,0xa1,0x2f,0xd1,0x48,0x10,0x79,0x0,0xc2,0x55,0x35,0xb6,0xf7,0x18,0xf9,0xfe,0xcd,0xc0,0x8f,0x59,0xa4,0xe7,0x6c,0x62,0xeb,0x60,0xdc,0xb8,0x6d,0xb2,0x4b,0x75,0x96,0x8a,0x38,0x77,0x81,0x8b,0xa3,0x76,0xd0,0xd4,0xcc,0xbb,0xde,0xc9,0x34,0x52,0x32,0x1f,0x12,0xaf,0x49,0x7f,0x4e,0xbf,0xb5,0xb4,0xf2,0xef,0x58,0x5,0x28,0x7,0xd6,0xc4,0x51,0x1b,0x5c,0xf6,0xe9,0x68,0xd8,0xc1,0xae,0x4a,0x14,0x92,0x15,0x24,0x36,0xc,0xb7,0x11,0x72,0xf,0x57,0x27,0xea,0xab,0x31,0xd9,0xc3,0x39,0xc6,0xa8,0x99,0x37,0x1e,0xe1,0xfc,0x54,0xa0,0x9d,0x29,0xfb,0x65,0x33,0xe0,0xb9,0x3d,0xe3,0x6f,0xda,0x9,0x7c,0xfa,0x3f,0xd7,0x2e,0xe2,0x88,0x42,0x4d,0xb,0x3c,0x61,0x67,0x8d,0x46,0x5d,0xf1,0x4,0xf8,0x64,0x1a,0x13,0x97,0x9b,0x2b,0x83,0x44,0x9c,0x3,0x1,0xd2,0xd,0x5f,0x86,0x7b,0xa5,0x74,0xd5,0x78,0xb3,0x8c,0x5a,0xe5,0xe8,0xaa,0xdb,0xf0,0x17,0xac,0xd3,0x43,0x84,0x5e,0x63,0xba,0x87,0x16,0xfd,0x94,0xce,0x6b,0xee,0xa7,0x56,0x9f,0x2d,0xf5,0xf4,0xb0,0x3b,0x41,0xe6,0xf3,0x7d,0xdd,0xad,0xa6,0x1d,0x2,0x6d,0x91,0x64,0xc8,0x2,0x86,0x8f,0xf1,0xd1,0x16,0xbe,0xe,0x47,0x94,0x96,0x9,0xee,0x13,0xca,0x98,0xed,0x40,0xe1,0x30,0x70,0xcf,0x19,0x26,0x65,0x4e,0x3f,0x7d,0xd6,0x46,0x39,0x82,0x2f,0xf6,0xcb,0x11,0x1,0x68,0x83,0x12,0x32,0x7b,0xfe,0x5b,0x60,0xb8,0xa,0xc3,0xd4,0xae,0x25,0x61,0x48,0xe8,0x66,0x73,0x97,0x88,0x33,0x38,0x63,0xc9,0x8e,0xc4,0x54,0x4d,0xfd,0x7c,0x7,0x81,0xdf,0x3b,0x99,0xa3,0xb1,0x80,0x9a,0xe7,0x84,0x22,0x3e,0x7f,0xb2,0xc2,0xac,0x56,0x4c,0xa4,0xa2,0xc,0x3d,0x53,0xc1,0x69,0x74,0x8b,0x6e,0xbc,0x8,0x35,0x2c,0x75,0xa6,0xf0,0x4f,0xfa,0x76,0xa8,0xaa,0x6f,0xe9,0x9c,0x1d,0x77,0xbb,0x42,0xa9,0x9e,0xd8,0xd7,0xd3,0x18,0xf2,0xf4,0x85,0xdd,0x44,0xba,0xc0,0x57,0x95,0xec,0x8d,0x62,0x23,0xa0,0x55,0x58,0x6b,0x6c,0x72,0x31,0xcc,0x1a,0xf5,0x7e,0xf7,0xf9,0x27,0xf8,0x2d,0x49,0x1f,0x3,0xe0,0xde,0x1e,0x14,0xe2,0xad,0x41,0x45,0xe3,0x36,0x5c,0x4b,0x2e,0x59,0x8a,0xa7,0xc7,0xa1,0xea,0xdc,0x3a,0x87,0x21,0x20,0x2a,0xdb,0x90,0xcd,0x7a,0x67,0x51,0x43,0x92,0xbd,0xb9,0x2b,0xd,0xaf,0x0,0x1b,0x10,0xc6,0x17,0x8c,0x4a,0xab,0xb,0xeb,0x4,0xce,0xfc,0x79,0x28,0xb4,0x89,0xe6,0x6,0xf3,0x5,0xd2,0x1c,0xb3,0x24,0x52,0x5d,0xda,0xd0,0xb7,0xb0,0x29,0xd5,0xb5,0xf,0xfb,0x37,0x5f,0xff,0xa5,0x3c,0x15,0x9d,0x71,0xef,0x9b,0x50,0x78,0x6a,0xb6,0xc5,0xe5,0x93,0x5e,0xbf,0x5a,0x34,0xe4,0x9f,0xd9,0xc8,0x6e,0xd6,0xab,0xfe,0x8e,0x72,0x33,0x0,0xe8,0xe0,0x1a,0x71,0x1f,0xee,0x40,0xc2,0x88,0x2f,0x85,0xb1,0x30,0x18,0x1,0x93,0x77,0x4b,0xcd,0xfd,0xcc,0xd5,0xef,0xa5,0xd0,0xe6,0x23,0xf7,0xe,0x51,0x3b,0x94,0x9b,0xe5,0xd2,0xbe,0xb8,0x9f,0x54,0x38,0xc7,0x8d,0x25,0x44,0x79,0x22,0xf0,0xea,0xbc,0x60,0x39,0x3a,0xe4,0x3,0xb6,0x86,0xd4,0xa2,0x5f,0xad,0x7c,0xa1,0xc,0x55,0x6a,0x3c,0x83,0x73,0x31,0x29,0x2,0x28,0x84,0x21,0xdd,0xc3,0xbd,0x4e,0xca,0xf2,0x42,0x9d,0x5a,0xda,0x45,0xb,0xd8,0x46,0x8f,0x2c,0xf4,0x69,0x2d,0x98,0xe2,0x2a,0x3f,0x4,0xa4,0x7f,0x74,0xdb,0xc4,0x75,0xce,0x9a,0xa,0x87,0x5d,0x63,0xba,0xcf,0x5e,0x4d,0x24,0xb2,0x17,0x7e,0x37,0x64,0xf8,0xb0,0x35,0x4a,0xbf,0xc5,0xaa,0x50,0xff,0x49,0x9e,0x11,0x96,0x68,0x1e,0x41,0xe3,0xf5,0x67,0x5c,0x8a,0x4c,0x57,0x6,0xe7,0x5b,0xc0,0x48,0x82,0x47,0xa7,0x1c,0x34,0xa3,0xd7,0x89,0xa9,0x26,0xfa,0xf3,0x16,0xdf,0x12,0xd3,0x95,0x78,0xa8,0xfc,0x65,0x9c,0xfb,0x43,0xb7,0x99,0xf9,0xb3,0xe9,0x7b,0x13,0xd1,0x3d,0x70,0x59,0x80,0x56,0x3e,0x7d,0xbb,0xb5,0xb9,0x32,0x61,0x5,0x6b,0xb4,0xac,0x92,0x53,0x4f,0x8,0xf6,0xc9,0x91,0xd9,0xa0,0x8c,0x1b,0x6f,0xec,0xc1,0x2e,0x27,0x20,0x19,0x14,0x76,0xcb,0xa6,0x90,0x66,0x97,0x6d,0x6c,0x36,0x2b,0xdc,0x81,0xde,0xf1,0x1d,0xf,0xae,0xe1,0x52,0x58,0xaf,0x7a,0xd,0x9,0x62,0x15,0x10,0x7,0x8b,0xed,0xc6,0xeb,0xf3,0x4d,0xc3,0x1c,0x5d,0xb5,0x2b,0x29,0xa7,0xb6,0xe0,0xea,0xf9,0x6,0x54,0x39,0xf0,0x87,0x4e,0xbe,0x9d,0x6b,0x8f,0xd6,0xbb,0x6d,0xa3,0x3e,0xe6,0xfe,0x8c,0xf1,0x90,0x0,0x67,0x4b,0xdb,0x70,0xaf,0x36,0x79,0xc6,0x5b,0xd5,0x85,0xce,0x25,0x8a,0x7a,0x69,0xc0,0x92,0x1a,0x9f,0x59,0xa1,0x76,0xff,0xa2,0x33,0x2a,0x42,0xd0,0x32,0x75,0xa,0xaa,0x4,0x71,0x4f,0xe5,0x89,0x37,0xf6,0x4a,0x98,0xb,0xde,0x11,0x44,0xca,0x14,0x7,0x34,0x2c,0xc5,0xe4,0xc9,0x62,0x12,0xba,0x19,0xc8,0x84,0x3b,0xb0,0xb2,0xd2,0xb3,0xcf,0xb1,0xb4,0x2,0xd8,0x63,0x15,0xa8,0x60,0xae,0xf,0xee,0xac,0xf4,0xc1,0x9b,0x6e,0xdf,0x72,0xa4,0x57,0xad,0x38,0x86,0x6a,0x95,0xdc,0xe7,0x50,0x9c,0xc7,0xfa,0xcd,0x13,0xe,0x4c,0xb7,0x94,0x5a,0xa9,0x3,0x46,0xb8,0x56,0x96,0xcb,0x2f,0x1,0xbd,0x93,0xeb,0x78,0x97,0x53,0x26,0x2d,0xe1,0x51,0xd3,0x52,0x64,0x7d,0xef,0x83,0xe2,0xdd,0x55,0x88,0x24,0xc4,0x9,0x20,0x10,0x6f,0xd7,0x5c,0x49,0x65,0xfd,0xfc,0xd4,0xa6,0x7e,0x9e,0xd9,0x28,0x81,0xb9,0x58,0x5e,0x99,0x16,0xfb,0xf8,0x6c,0x1f,0xd,0x8b,0x8d,0xd1,0xed,0xbc,0x5,0x7f,0xe8,0xa0,0x21,0x1b,0x68,0xf5,0xf2,0x47,0xe3,0xc2,0xec,0x41,0x7b,0x73,0x82,0x23,0x74,0x3d,0x27,0x77,0x1d,0x9a,0x1e,0x31,0x17,0x8,0x30,0x43,0xab,0x91,0x8e,0x18,0x3f,0xa5,0x80,0x3a,0x7c,0xcc,0x40,0xc,0x48,0xe9,0x35,0x3c,0x22,0x66,0x2e,0xda,0x45,0xbf,0x5f,0x61,0xf7,0xb0,0x12,0x34,0xa6,0xd9,0xf,0x4,0x1f,0xb4,0x55,0x93,0x8,0xd1,0x1b,0xf4,0x14,0xab,0x37,0x66,0xe3,0xec,0x19,0xf9,0x96,0xac,0x3,0xcd,0x1a,0xc5,0x42,0x4d,0x3b,0x36,0xaf,0xa8,0xcf,0xe4,0x10,0xaa,0xca,0xba,0xe0,0x40,0x28,0x6e,0x82,0xa,0x23,0x67,0x4f,0x84,0xf0,0xfa,0xda,0xa9,0x75,0x45,0xa0,0x41,0x8c,0xc6,0x80,0xfb,0x2b,0xa5,0x5b,0xc2,0x9a,0xf3,0x8a,0x48,0xdf,0xbf,0x3c,0x7d,0x92,0x73,0x74,0x47,0x4a,0x5,0xd3,0x2e,0x6d,0xe6,0xe8,0x61,0xea,0x56,0x32,0xe7,0x38,0xc1,0xff,0x1c,0x0,0xb2,0xfd,0xb,0x1,0x29,0xfc,0x5a,0x5e,0x46,0x31,0x54,0x43,0xbe,0xd8,0xb8,0x95,0x98,0x25,0xc3,0xf5,0xc4,0x35,0x3f,0x3e,0x78,0x65,0xd2,0x8f,0xa2,0x8d,0x5c,0x4e,0xdb,0x91,0xd6,0x7c,0x63,0xe2,0x52,0x4b,0x24,0xc0,0x9e,0x18,0x9f,0xae,0xbc,0x86,0x3d,0x9b,0xf8,0x85,0xdd,0xad,0x60,0x21,0xbb,0x53,0x49,0xb3,0x4c,0x22,0x13,0xbd,0x94,0x6b,0x76,0xde,0x2a,0x17,0xa3,0x71,0xef,0xb9,0x6a,0x33,0xb7,0x69,0xe5,0x50,0x83,0xf6,0x70,0xb5,0x5d,0xa4,0x68,0x2,0xc8,0xc7,0x81,0xb6,0xeb,0xed,0x7,0xcc,0xd7,0x7b,0x8e,0x72,0xee,0x90,0x99,0x1d,0x11,0xa1,0x9,0xce,0x16,0x89,0x8b,0x58,0x87,0xd5,0xc,0xf1,0x2f,0xfe,0x5f,0xf2,0x39,0x6,0xd0,0x6f,0x62,0x20,0x51,0x7a,0x9d,0x26,0x59,0xc9,0xe,0xd4,0xe9,0x30,0xd,0x9c,0x77,0x1e,0x44,0xe1,0x64,0x2d,0xdc,0x15,0xa7,0x7f,0x7e,0x3a,0xb1,0xcb,0x6c,0x79,0xf7,0x57,0x27,0x2c,0x97,0x88,0xd,0xf1,0x4,0xa8,0x62,0xe6,0xef,0x91,0xb1,0x76,0xde,0x6e,0x27,0xf4,0xf6,0x69,0x8e,0x73,0xaa,0xf8,0x8d,0x20,0x81,0x50,0x10,0xaf,0x79,0x46,0x5,0x2e,0x5f,0x1d,0xb6,0x26,0x59,0xe2,0x4f,0x96,0xab,0x71,0x61,0x8,0xe3,0x72,0x52,0x1b,0x9e,0x3b,0x0,0xd8,0x6a,0xa3,0xb4,0xce,0x45,0x1,0x28,0x88,0x6,0x13,0xf7,0xe8,0x53,0x58,0x3,0xa9,0xee,0xa4,0x34,0x2d,0x9d,0x1c,0x67,0xe1,0xbf,0x5b,0xf9,0xc3,0xd1,0xe0,0xfa,0x87,0xe4,0x42,0x5e,0x1f,0xd2,0xa2,0xcc,0x36,0x2c,0xc4,0xc2,0x6c,0x5d,0x33,0xa1,0x9,0x14,0xeb,0xe,0xdc,0x68,0x55,0x4c,0x15,0xc6,0x90,0x2f,0x9a,0x16,0xc8,0xca,0xf,0x89,0xfc,0x7d,0x17,0xdb,0x22,0xc9,0xfe,0xb8,0xb7,0xb3,0x78,0x92,0x94,0xe5,0xbd,0x24,0xda,0xa0,0x37,0xf5,0x8c,0xed,0x2,0x43,0xc0,0x35,0x38,0xb,0xc,0x12,0x51,0xac,0x7a,0x95,0x1e,0x97,0x99,0x47,0x98,0x4d,0x29,0x7f,0x63,0x80,0xbe,0x7e,0x74,0x82,0xcd,0x21,0x25,0x83,0x56,0x3c,0x2b,0x4e,0x39,0xea,0xc7,0xa7,0xc1,0x8a,0xbc,0x5a,0xe7,0x41,0x40,0x4a,0xbb,0xf0,0xad,0x1a,0x7,0x31,0x23,0xf2,0xdd,0xd9,0x4b,0x6d,0xcf,0x60,0x7b,0x70,0xa6,0x77,0xec,0x2a,0xcb,0x6b,0x8b,0x64,0xae,0x9c,0x19,0x48,0xd4,0xe9,0x86,0x66,0x93,0x65,0xb2,0x7c,0xd3,0x44,0x32,0x3d,0xba,0xb0,0xd7,0xd0,0x49,0xb5,0xd5,0x6f,0x9b,0x57,0x3f,0x9f,0xc5,0x5c,0x75,0xfd,0x11,0x8f,0xfb,0x30,0x18,0xa,0xd6,0xa5,0x85,0xf3,0x3e,0xdf,0x3a,0x54,0x84,0xff,0xb9,0x7a,0xdb,0x3a,0x78,0xb7,0xc1,0x7c,0xb4,0x65,0x60,0xd6,0xc,0x66,0x6,0x67,0x1b,0x41,0x8,0x33,0x84,0x79,0xec,0x52,0xbe,0xb,0xa6,0x70,0x83,0x20,0x15,0x4f,0xba,0xdf,0xa,0xc5,0x90,0xe3,0x22,0x9e,0x4c,0xa5,0x9b,0x31,0x5d,0xa1,0xde,0x7e,0xd0,0x1c,0x50,0xef,0x64,0xb6,0xc6,0x6e,0xcd,0xf8,0x11,0x30,0x1d,0x1e,0xc0,0xd3,0xe0,0x51,0x1a,0xf1,0x5e,0xad,0x12,0x8f,0x1,0xf,0xa4,0x7b,0xe2,0x44,0xd4,0xb3,0x9f,0xfe,0x96,0x4,0xe6,0xa2,0x2b,0x76,0xe7,0xce,0x4b,0x8d,0x75,0xae,0xbd,0x14,0x46,0x2d,0xd2,0x80,0xed,0x73,0x62,0x34,0x3e,0x89,0x61,0xff,0xfd,0x27,0x99,0x17,0xc8,0x32,0x2a,0x58,0x25,0x6f,0xb9,0x77,0xea,0x49,0xbf,0x5b,0x2,0x24,0x53,0x9a,0x6a,0x71,0x54,0xee,0xa8,0x45,0x5a,0xcc,0xeb,0xdc,0xe4,0x97,0x7f,0x4e,0xca,0xe5,0xc3,0x6b,0x8b,0xb5,0x23,0xb2,0xfa,0xe,0x91,0x3d,0xe1,0xe8,0xf6,0x18,0x94,0xd8,0x9c,0x74,0xf5,0xcf,0xbc,0x68,0xd1,0xab,0x3c,0x5f,0x59,0x5,0x39,0x2c,0xb8,0xcb,0xd9,0xe9,0xf3,0xa3,0xc9,0xa7,0x56,0xf7,0xa0,0x16,0x38,0x95,0xaf,0x21,0x26,0x93,0x37,0xbb,0x3,0x88,0x9d,0x10,0xdd,0xf4,0xc4,0x9,0x81,0x5c,0xf0,0xa9,0x3b,0x57,0x36,0x8a,0x4d,0xc2,0x2f,0xfc,0x55,0x6d,0x8c,0x72,0xaa,0x4a,0xd,0xb1,0x29,0x28,0x0,0x92,0x6c,0x82,0x42,0x40,0x8e,0x7d,0xd7,0xc7,0xda,0x98,0x63,0x48,0x13,0x2e,0x19,0x85,0x7,0x86,0xb0,0x87,0xf2,0xf9,0x35,0x47,0x3f,0xac,0x43,0x1f,0xfb,0xd5,0x69,0x53,0x94,0x4b,0xfb,0xd1,0x2,0x4c,0xd3,0xd4,0x28,0x8d,0x21,0xc3,0x47,0xb4,0xca,0x8a,0x35,0x63,0x5c,0xb,0x20,0x38,0x7a,0x56,0xab,0xdd,0x8f,0x5,0xa8,0x75,0xa4,0x2d,0x44,0x57,0xc6,0x3e,0x77,0x1e,0xbb,0x3,0x93,0xc7,0x7c,0xb3,0x6a,0x54,0x8e,0xad,0xd,0x36,0x23,0xcd,0xd2,0x7d,0x76,0xfd,0x25,0x86,0x4f,0xeb,0x91,0x24,0x60,0xc4,0x42,0x7e,0x9a,0xe6,0xdc,0xc5,0xf4,0x8c,0x26,0x81,0xcb,0x8,0x11,0x39,0xb8,0x13,0xe9,0xe1,0x9,0x49,0xe7,0x16,0x78,0xa2,0xdf,0x67,0xc1,0x3a,0x7b,0x87,0xf7,0x30,0x69,0xb5,0xe3,0xbf,0xa,0xed,0x33,0x2c,0x84,0xce,0x31,0xf9,0x2b,0x70,0x4d,0xdb,0xec,0x92,0x9d,0x5d,0x96,0xb1,0xb7,0x2a,0xef,0xd9,0xac,0x32,0x58,0x7,0xfe,0x27,0xc8,0xe5,0x66,0x1d,0x10,0x29,0x2e,0x98,0xc0,0xff,0x1,0x12,0x85,0xa9,0xd0,0xbd,0x62,0xc,0x68,0x46,0x5a,0x9b,0xa5,0x74,0x37,0x5f,0x89,0x3b,0xb0,0xbc,0xb2,0xe,0x19,0x1c,0x6b,0xe2,0xcf,0xe4,0x82,0x51,0x5b,0xe8,0xa7,0x0,0x4,0x73,0xa6,0x88,0xd5,0x22,0x3f,0x6,0x14,0xf8,0xd7,0x99,0xaf,0xc2,0x7f,0x65,0x64,0x9e,0x6f,0xc9,0x52,0xee,0xf,0xae,0x4e,0x8b,0x41,0x6e,0xfc,0xea,0x48,0x5e,0x45,0x83,0x55,0x97,0x40,0xf6,0x59,0x17,0x61,0x9f,0x18,0x3c,0xb9,0xf1,0x6d,0xa3,0xcc,0xb6,0x43,0x1a,0x72,0xe0,0xba,0x50,0x79,0x34,0xd8,0xf2,0x95,0x6c,0xf5,0xf0,0x90,0xbe,0x4a,0x1b,0xd6,0x1f,0xfa,0xa1,0x71,0x9c,0xda,0xde,0xaa,0x3d,0x15,0xf3,0x2f,0xa0,0x80,0x63,0xc1,0xe7,0x75,0xa,0xdc,0xd7,0xcc,0x67,0x86,0x40,0xdb,0x2,0xc8,0x27,0xc7,0x78,0xe4,0xb5,0x30,0x3f,0xca,0x2a,0x45,0x7f,0xd0,0x1e,0xc9,0x16,0x91,0x9e,0xe8,0xe5,0x7c,0x7b,0x1c,0x37,0xc3,0x79,0x19,0x69,0x33,0x93,0xfb,0xbd,0x51,0xd9,0xf0,0xb4,0x9c,0x57,0x23,0x29,0x9,0x7a,0xa6,0x96,0x73,0x92,0x5f,0x15,0x53,0x28,0xf8,0x76,0x88,0x11,0x49,0x20,0x59,0x9b,0xc,0x6c,0xef,0xae,0x41,0xa0,0xa7,0x94,0x99,0xd6,0x0,0xfd,0xbe,0x35,0x3b,0xb2,0x39,0x85,0xe1,0x34,0xeb,0x12,0x2c,0xcf,0xd3,0x61,0x2e,0xd8,0xd2,0xfa,0x2f,0x89,0x8d,0x95,0xe2,0x87,0x90,0x6d,0xb,0x6b,0x46,0x4b,0xf6,0x10,0x26,0x17,0xe6,0xec,0xed,0xab,0xb6,0x1,0x5c,0x71,0x5e,0x8f,0x9d,0x8,0x42,0x5,0xaf,0xb0,0x31,0x81,0x98,0xf7,0x13,0x4d,0xcb,0x4c,0x7d,0x6f,0x55,0xee,0x48,0x2b,0x56,0xe,0x7e,0xb3,0xf2,0x68,0x80,0x9a,0x60,0x9f,0xf1,0xc0,0x6e,0x47,0xb8,0xa5,0xd,0xf9,0xc4,0x70,0xa2,0x3c,0x6a,0xb9,0xe0,0x64,0xba,0x36,0x83,0x50,0x25,0xa3,0x66,0x8e,0x77,0xbb,0xd1,0x1b,0x14,0x52,0x65,0x38,0x3e,0xd4,0x1f,0x4,0xa8,0x5d,0xa1,0x3d,0x43,0x4a,0xce,0xc2,0x72,0xda,0x1d,0xc5,0x5a,0x58,0x8b,0x54,0x6,0xdf,0x22,0xfc,0x2d,0x8c,0x21,0xea,0xd5,0x3,0xbc,0xb1,0xf3,0x82,0xa9,0x4e,0xf5,0x8a,0x1a,0xdd,0x7,0x3a,0xe3,0xde,0x4f,0xa4,0xcd,0x97,0x32,0xb7,0xfe,0xf,0xc6,0x74,0xac,0xad,0xe9,0x62,0x18,0xbf,0xaa,0x24,0x84,0xf4,0xff,0x44,0x5b,0x9c,0xb9,0x60,0x26,0x92,0x8d,0x23,0x4,0x2c,0x14,0xb7,0x5f,0x2,0x86,0xb,0x2d,0x43,0xa3,0xeb,0x7d,0x32,0x7a,0x59,0xc6,0x29,0xf5,0x3e,0x20,0x5c,0xd0,0x54,0x10,0x3d,0xbc,0x74,0x7,0x19,0xa0,0xf4,0x63,0x91,0x97,0xf1,0xcd,0x70,0xe4,0x11,0x3,0x3b,0x21,0x1,0x6b,0x9e,0x6f,0x68,0x3f,0xf0,0xde,0x67,0x5d,0xee,0xe9,0xff,0x5b,0xcb,0x73,0x55,0x40,0x15,0xd8,0xc,0x3c,0x49,0xc1,0x38,0x94,0xf3,0x61,0xfe,0x9f,0x85,0x42,0xe7,0xa,0x9d,0x34,0x44,0xa5,0x62,0xba,0xc5,0x82,0xe1,0x79,0xc8,0xe0,0xa4,0x5a,0x8a,0x4a,0x46,0x88,0x1f,0xb5,0x12,0xf,0xab,0x50,0xdb,0x80,0xd1,0xe6,0xcf,0x4d,0x78,0x4e,0x3a,0x4f,0xfd,0x31,0xf7,0x8f,0x8b,0x64,0x33,0xd7,0xa1,0x1d,0x13,0xb2,0xb0,0xf2,0x9,0x7f,0x7c,0xb4,0xa8,0xad,0xc4,0x1e,0xce,0xae,0xd3,0xaf,0xc0,0x89,0x4c,0xfb,0x24,0xb1,0x76,0x9a,0x6e,0xc3,0x4b,0xb8,0xdd,0xe8,0x72,0x87,0xc2,0x17,0x58,0xd,0xea,0x2b,0x84,0x56,0x53,0x6d,0x95,0xf9,0x16,0x69,0x18,0xb6,0x98,0xd4,0xac,0x27,0xe,0x7e,0x5,0xa6,0xd9,0x30,0xd5,0xf8,0x8,0xd6,0x28,0x1b,0xd2,0x99,0x96,0x39,0xda,0x65,0xc9,0x47,0x6c,0xc7,0x2a,0xb3,0x1c,0x8c,0x57,0x7b,0x5e,0x36,0x2e,0xcc,0xe3,0x6a,0x2f,0xbe,0x83,0x6,0xbd,0x45,0x75,0x66,0x8e,0xdc,0x1a,0xe5,0x25,0x48,0xaa,0xbb,0xf6,0xfc,0xa9,0x41,0x35,0x37,0x51,0xef,0x0,0xdf,0xe2,0xfa,0xed,0x90,0x71,0xa7,0x22,0xbf,0x77,0x81,0xca,0x93,0x9b,0xec,0xa2,0x52,0xa1,0x7,0xbf,0xc2,0x97,0xe7,0x1b,0x5a,0x69,0x81,0x89,0x73,0x18,0x76,0x87,0x29,0xab,0xe1,0x46,0xec,0xd8,0x59,0x71,0x68,0xfa,0x1e,0x22,0xa4,0x94,0xa5,0xbc,0x86,0xcc,0xb9,0x8f,0x4a,0x9e,0x67,0x38,0x52,0xfd,0xf2,0x8c,0xbb,0xd7,0xd1,0xf6,0x3d,0x51,0xae,0xe4,0x4c,0x2d,0x10,0x4b,0x99,0x83,0xd5,0x9,0x50,0x53,0x8d,0x6a,0xdf,0xef,0xbd,0xcb,0x36,0xc4,0x15,0xc8,0x65,0x3c,0x3,0x55,0xea,0x1a,0x58,0x40,0x6b,0x41,0xed,0x48,0xb4,0xaa,0xd4,0x27,0xa3,0x9b,0x2b,0xf4,0x33,0xb3,0x2c,0x62,0xb1,0x2f,0xe6,0x45,0x9d,0x0,0x44,0xf1,0x8b,0x43,0x56,0x6d,0xcd,0x16,0x1d,0xb2,0xad,0x1c,0xa7,0xf3,0x63,0xee,0x34,0xa,0xd3,0xa6,0x37,0x24,0x4d,0xdb,0x7e,0x17,0x5e,0xd,0x91,0xd9,0x5c,0x23,0xd6,0xac,0xc3,0x39,0x96,0x20,0xf7,0x78,0xff,0x1,0x77,0x28,0x8a,0x9c,0xe,0x35,0xe3,0x25,0x3e,0x6f,0x8e,0x32,0xa9,0x21,0xeb,0x2e,0xce,0x75,0x5d,0xca,0xbe,0xe0,0xc0,0x4f,0x93,0x9a,0x7f,0xb6,0x7b,0xba,0xfc,0x11,0xc1,0x95,0xc,0xf5,0x92,0x2a,0xde,0xf0,0x90,0xda,0x80,0x12,0x7a,0xb8,0x54,0x19,0x30,0xe9,0x3f,0x57,0x14,0xd2,0xdc,0xd0,0x5b,0x8,0x6c,0x2,0xdd,0xc5,0xfb,0x3a,0x26,0x61,0x9f,0xa0,0xf8,0xb0,0xc9,0xe5,0x72,0x6,0x85,0xa8,0x47,0x4e,0x49,0x70,0x7d,0x1f,0xa2,0xcf,0xf9,0xf,0xfe,0x4,0x5,0x5f,0x42,0xb5,0xe8,0xb7,0x98,0x74,0x66,0xc7,0x88,0x3b,0x31,0xc6,0x13,0x64,0x60,0xb,0x7c,0x79,0x6e,0xe2,0x84,0xaf,0x82,0x28,0xef,0x30,0x80,0xaa,0x79,0x37,0xa8,0xaf,0x53,0xf6,0x5a,0xb8,0x3c,0xcf,0xb1,0xf1,0x4e,0x18,0x27,0x70,0x5b,0x43,0x1,0x2d,0xd0,0xa6,0xf4,0x7e,0xd3,0xe,0xdf,0x56,0x3f,0x2c,0xbd,0x45,0xc,0x65,0xc0,0x78,0xe8,0xbc,0x7,0xc8,0x11,0x2f,0xf5,0xd6,0x76,0x4d,0x58,0xb6,0xa9,0x6,0xd,0x86,0x5e,0xfd,0x34,0x90,0xea,0x5f,0x1b,0xbf,0x39,0x5,0xe1,0x9d,0xa7,0xbe,0x8f,0xf7,0x5d,0xfa,0xb0,0x73,0x6a,0x42,0xc3,0x68,0x92,0x9a,0x72,0x32,0x9c,0x6d,0x3,0xd9,0xa4,0x1c,0xba,0x41,0x0,0xfc,0x8c,0x4b,0x12,0xce,0x98,0xc4,0x71,0x96,0x48,0x57,0xff,0xb5,0x4a,0x82,0x50,0xb,0x36,0xa0,0x97,0xe9,0xe6,0x26,0xed,0xca,0xcc,0x51,0x94,0xa2,0xd7,0x49,0x23,0x7c,0x85,0x5c,0xb3,0x9e,0x1d,0x66,0x6b,0x52,0x55,0xe3,0xbb,0x84,0x7a,0x69,0xfe,0xd2,0xab,0xc6,0x19,0x77,0x13,0x3d,0x21,0xe0,0xde,0xf,0x4c,0x24,0xf2,0x40,0xcb,0xc7,0xc9,0x75,0x62,0x67,0x10,0x99,0xb4,0x9f,0xf9,0x2a,0x20,0x93,0xdc,0x7b,0x7f,0x8,0xdd,0xf3,0xae,0x59,0x44,0x7d,0x6f,0x83,0xac,0xe2,0xd4,0xb9,0x4,0x1e,0x1f,0xe5,0x14,0xb2,0x29,0x95,0x74,0xd5,0x35,0xf0,0x3a,0x15,0x87,0x91,0x33,0x25,0x3e,0xf8,0x2e,0xec,0x3b,0x8d,0x22,0x6c,0x1a,0xe4,0x63,0x47,0xc2,0x8a,0x16,0xd8,0xb7,0xcd,0x38,0x61,0x9,0x9b,0xc1,0x2b,0x2,0x4f,0xa3,0x89,0xee,0x17,0x8e,0x8b,0xeb,0xc5,0x31,0x60,0xad,0x64,0x81,0xda,0xa,0xe7,0xa1,0xa5,0xd1,0x46,0x6e,0x88,0x54,0xdb,0xfb,0xc8,0x1a,0xb5,0x74,0x93,0xc6,0x89,0x5c,0x28,0x86,0xf7,0x88,0x67,0xb,0xf3,0xcd,0x38,0x9b,0xe0,0x90,0xb9,0x32,0x4a,0x6,0x85,0xb6,0x48,0x96,0x66,0x4b,0xae,0x47,0x2a,0xe2,0xe1,0x97,0x6c,0x2e,0x2c,0x8d,0x31,0x4d,0x30,0x50,0x80,0x5a,0x33,0x36,0x4,0xe8,0x2f,0xba,0x65,0xd2,0x17,0x5e,0x19,0xec,0x76,0x43,0x26,0xd5,0x5d,0xf0,0x62,0x68,0x25,0x34,0xd6,0xbb,0x7b,0x84,0x41,0x9e,0x71,0xcf,0xa9,0xab,0xdf,0x37,0x21,0xbc,0x39,0xef,0xe,0x73,0x64,0x7c,0xcc,0x3c,0x72,0x5,0xd,0x54,0x1f,0xe9,0xd9,0x57,0xfb,0x44,0xa7,0x8,0x7,0x4c,0xe5,0xc9,0x12,0x82,0x2d,0xb4,0x59,0xf2,0x20,0xb1,0xf4,0x7d,0x52,0xb0,0xa8,0xc0,0x42,0x10,0xf8,0xeb,0xdb,0x23,0x98,0x1d,0xfd,0x6a,0x3e,0x87,0x99,0xea,0x22,0xa3,0x9d,0x8f,0x7a,0xee,0x53,0x6f,0x9,0xf,0xa1,0xf6,0xf1,0x0,0xf5,0x9f,0xbf,0xa5,0xc5,0x61,0x77,0x70,0xc3,0xf9,0x40,0x6e,0x9a,0xbd,0x13,0xc,0xb8,0xfe,0x27,0x2,0xb3,0x95,0x18,0x9c,0xc1,0x29,0x8a,0xb2,0x58,0xc7,0xe4,0xac,0xe3,0x75,0x3d,0xdd,0x8e,0xca,0x4e,0xc2,0xbe,0xa0,0x6b,0xb7,0x2b,0x81,0x16,0xd8,0xd4,0x14,0xc4,0x3a,0x78,0x4f,0x1e,0x45,0xce,0x35,0x91,0x8c,0xaf,0x63,0xd1,0xa4,0xd0,0xe6,0xd3,0x51,0x83,0x3f,0x49,0xad,0xfa,0x15,0x11,0x69,0xa2,0x92,0x46,0x8b,0xde,0xcb,0xed,0x55,0x1,0x60,0xff,0x6d,0xa,0xa6,0x5f,0xd7,0x3b,0xda,0xaa,0x3,0x94,0x79,0xdc,0x1b,0x7e,0x56,0xe7,0x7f,0x1c,0x5b,0x24,0xfc,0xf0,0xc,0xf9,0x55,0x9f,0x1b,0x12,0x6c,0x4c,0x8b,0x23,0x93,0xda,0x9,0xb,0x94,0x73,0x8e,0x57,0x5,0x70,0xdd,0x7c,0xad,0xed,0x52,0x84,0xbb,0xf8,0xd3,0xa2,0xe0,0x4b,0xdb,0xa4,0x1f,0xb2,0x6b,0x56,0x8c,0x9c,0xf5,0x1e,0x8f,0xaf,0xe6,0x63,0xc6,0xfd,0x25,0x97,0x5e,0x49,0x33,0xb8,0xfc,0xd5,0x75,0xfb,0xee,0xa,0x15,0xae,0xa5,0xfe,0x54,0x13,0x59,0xc9,0xd0,0x60,0xe1,0x9a,0x1c,0x42,0xa6,0x4,0x3e,0x2c,0x1d,0x7,0x7a,0x19,0xbf,0xa3,0xe2,0x2f,0x5f,0x31,0xcb,0xd1,0x39,0x3f,0x91,0xa0,0xce,0x5c,0xf4,0xe9,0x16,0xf3,0x21,0x95,0xa8,0xb1,0xe8,0x3b,0x6d,0xd2,0x67,0xeb,0x35,0x37,0xf2,0x74,0x1,0x80,0xea,0x26,0xdf,0x34,0x3,0x45,0x4a,0x4e,0x85,0x6f,0x69,0x18,0x40,0xd9,0x27,0x5d,0xca,0x8,0x71,0x10,0xff,0xbe,0x3d,0xc8,0xc5,0xf6,0xf1,0xef,0xac,0x51,0x87,0x68,0xe3,0x6a,0x64,0xba,0x65,0xb0,0xd4,0x82,0x9e,0x7d,0x43,0x83,0x89,0x7f,0x30,0xdc,0xd8,0x7e,0xab,0xc1,0xd6,0xb3,0xc4,0x17,0x3a,0x5a,0x3c,0x77,0x41,0xa7,0x1a,0xbc,0xbd,0xb7,0x46,0xd,0x50,0xe7,0xfa,0xcc,0xde,0xf,0x20,0x24,0xb6,0x90,0x32,0x9d,0x86,0x8d,0x5b,0x8a,0x11,0xd7,0x36,0x96,0x76,0x99,0x53,0x61,0xe4,0xb5,0x29,0x14,0x7b,0x9b,0x6e,0x98,0x4f,0x81,0x2e,0xb9,0xcf,0xc0,0x47,0x4d,0x2a,0x2d,0xb4,0x48,0x28,0x92,0x66,0xaa,0xc2,0x62,0x38,0xa1,0x88,0x0,0xec,0x72,0x6,0xcd,0xe5,0xf7,0x2b,0x58,0x78,0xe,0xc3,0x22,0xc7,0xa9,0x79,0x2,0x44,0x8f,0x29,0x91,0xec,0xb9,0xc9,0x35,0x74,0x47,0xaf,0xa7,0x5d,0x36,0x58,0xa9,0x7,0x85,0xcf,0x68,0xc2,0xf6,0x77,0x5f,0x46,0xd4,0x30,0xc,0x8a,0xba,0x8b,0x92,0xa8,0xe2,0x97,0xa1,0x64,0xb0,0x49,0x16,0x7c,0xd3,0xdc,0xa2,0x95,0xf9,0xff,0xd8,0x13,0x7f,0x80,0xca,0x62,0x3,0x3e,0x65,0xb7,0xad,0xfb,0x27,0x7e,0x7d,0xa3,0x44,0xf1,0xc1,0x93,0xe5,0x18,0xea,0x3b,0xe6,0x4b,0x12,0x2d,0x7b,0xc4,0x34,0x76,0x6e,0x45,0x6f,0xc3,0x66,0x9a,0x84,0xfa,0x9,0x8d,0xb5,0x5,0xda,0x1d,0x9d,0x2,0x4c,0x9f,0x1,0xc8,0x6b,0xb3,0x2e,0x6a,0xdf,0xa5,0x6d,0x78,0x43,0xe3,0x38,0x33,0x9c,0x83,0x32,0x89,0xdd,0x4d,0xc0,0x1a,0x24,0xfd,0x88,0x19,0xa,0x63,0xf5,0x50,0x39,0x70,0x23,0xbf,0xf7,0x72,0xd,0xf8,0x82,0xed,0x17,0xb8,0xe,0xd9,0x56,0xd1,0x2f,0x59,0x6,0xa4,0xb2,0x20,0x1b,0xcd,0xb,0x10,0x41,0xa0,0x1c,0x87,0xf,0xc5,0x0,0xe0,0x5b,0x73,0xe4,0x90,0xce,0xee,0x61,0xbd,0xb4,0x51,0x98,0x55,0x94,0xd2,0x3f,0xef,0xbb,0x22,0xdb,0xbc,0x4,0xf0,0xde,0xbe,0xf4,0xae,0x3c,0x54,0x96,0x7a,0x37,0x1e,0xc7,0x11,0x79,0x3a,0xfc,0xf2,0xfe,0x75,0x26,0x42,0x2c,0xf3,0xeb,0xd5,0x14,0x8,0x4f,0xb1,0x8e,0xd6,0x9e,0xe7,0xcb,0x5c,0x28,0xab,0x86,0x69,0x60,0x67,0x5e,0x53,0x31,0x8c,0xe1,0xd7,0x21,0xd0,0x2a,0x2b,0x71,0x6c,0x9b,0xc6,0x99,0xb6,0x5a,0x48,0xe9,0xa6,0x15,0x1f,0xe8,0x3d,0x4a,0x4e,0x25,0x52,0x57,0x40,0xcc,0xaa,0x81,0xac,0xa7,0x25,0x10,0x26,0x52,0x27,0x95,0x59,0x9f,0xe7,0xe3,0xc,0x5b,0xbf,0xc9,0x75,0xcc,0x32,0xe2,0x22,0x2e,0xe0,0x77,0xdd,0x7a,0x67,0xc3,0x38,0xb3,0xe8,0xb9,0x8e,0xed,0x2a,0x8f,0x62,0xf5,0x5c,0x2c,0xcd,0xa,0xd2,0xad,0xea,0x89,0x11,0xa0,0x88,0xa3,0x1b,0x3d,0x28,0x7d,0xb0,0x64,0x54,0x21,0xa9,0x50,0xfc,0x9b,0x9,0x96,0xf7,0x53,0x49,0x69,0x3,0xf6,0x7,0x0,0x57,0x98,0xb6,0xf,0x35,0x86,0x81,0x97,0x33,0x55,0xd4,0x1c,0x6f,0x71,0xc8,0x9c,0xb,0xf9,0xff,0x99,0xa5,0x18,0x8c,0x79,0x6b,0x2b,0xcb,0x83,0x15,0x5a,0x12,0x31,0xae,0x41,0x9d,0x56,0x48,0x34,0xb8,0x3c,0x78,0xf4,0xd1,0x8,0x4e,0xfa,0xe5,0x4b,0x6c,0x44,0x7c,0xdf,0x37,0x6a,0xee,0x63,0x45,0x8a,0x92,0x85,0xf8,0x19,0xcf,0x4a,0xd7,0x1f,0xe9,0xa2,0xfb,0xf3,0x84,0xca,0x3a,0x72,0x8d,0x4d,0x20,0xc2,0xd3,0x9e,0x94,0xc1,0x29,0x5d,0x5f,0x39,0x87,0x68,0xb7,0x36,0x5e,0x46,0xa4,0x8b,0x2,0x47,0xd6,0xeb,0x6e,0xd5,0x2d,0x1d,0xe,0xe6,0xb4,0xba,0xf1,0xfe,0x51,0xb2,0xd,0xa1,0x2f,0x4,0xaf,0x42,0xdb,0x74,0xe4,0x3f,0x13,0xf0,0xbc,0xc4,0x4f,0x66,0x16,0x6d,0xce,0xb1,0x58,0xbd,0x90,0x60,0xbe,0x40,0x73,0xaa,0x7f,0x30,0x65,0x82,0x43,0xec,0x3e,0x3b,0x5,0xfd,0x91,0x7e,0x1,0x70,0xde,0xa8,0xe1,0x24,0x93,0x4c,0xd9,0x1e,0xf2,0x6,0xab,0x23,0xd0,0xb5,0x80,0x1a,0xef,0x7b,0xda,0xd8,0x9a,0x61,0x17,0x14,0xdc,0xc0,0xc5,0xac,0x76,0xa6,0xc6,0xbb,0xc7,0x7e,0xdc,0xfa,0x68,0x17,0xc1,0xca,0xd1,0x7a,0x9b,0x5d,0xc6,0x1f,0xd5,0x3a,0xda,0x65,0xf9,0xa8,0x2d,0x22,0xd7,0x37,0x58,0x62,0xcd,0x3,0xd4,0xb,0x8c,0x83,0xf5,0xf8,0x61,0x66,0x1,0x2a,0xde,0x64,0x4,0x74,0x2e,0x8e,0xe6,0xa0,0x4c,0xc4,0xed,0xa9,0x81,0x4a,0x3e,0x34,0x14,0x67,0xbb,0x8b,0x6e,0x8f,0x42,0x8,0x4e,0x35,0xe5,0x6b,0x95,0xc,0x54,0x3d,0x44,0x86,0x11,0x71,0xf2,0xb3,0x5c,0xbd,0xba,0x89,0x84,0xcb,0x1d,0xe0,0xa3,0x28,0x26,0xaf,0x24,0x98,0xfc,0x29,0xf6,0xf,0x31,0xd2,0xce,0x7c,0x33,0xc5,0xcf,0xe7,0x32,0x94,0x90,0x88,0xff,0x9a,0x8d,0x70,0x16,0x76,0x5b,0x56,0xeb,0xd,0x3b,0xa,0xfb,0xf1,0xf0,0xb6,0xab,0x1c,0x41,0x6c,0x43,0x92,0x80,0x15,0x5f,0x18,0xb2,0xad,0x2c,0x9c,0x85,0xea,0xe,0x50,0xd6,0x51,0x60,0x72,0x48,0xf3,0x55,0x36,0x4b,0x13,0x63,0xae,0xef,0x75,0x9d,0x87,0x7d,0x82,0xec,0xdd,0x73,0x5a,0xa5,0xb8,0x10,0xe4,0xd9,0x6d,0xbf,0x21,0x77,0xa4,0xfd,0x79,0xa7,0x2b,0x9e,0x4d,0x38,0xbe,0x7b,0x93,0x6a,0xa6,0xcc,0x6,0x9,0x4f,0x78,0x25,0x23,0xc9,0x2,0x19,0xb5,0x40,0xbc,0x20,0x5e,0x57,0xd3,0xdf,0x6f,0xc7,0x0,0xd8,0x47,0x45,0x96,0x49,0x1b,0xc2,0x3f,0xe1,0x30,0x91,0x3c,0xf7,0xc8,0x1e,0xa1,0xac,0xee,0x9f,0xb4,0x53,0xe8,0x97,0x7,0xc0,0x1a,0x27,0xfe,0xc3,0x52,0xb9,0xd0,0x8a,0x2f,0xaa,0xe3,0x12,0xdb,0x69,0xb1,0xb0,0xf4,0x7f,0x5,0xa2,0xb7,0x39,0x99,0xe9,0xe2,0x59,0x46,0x54,0xa8,0x5d,0xf1,0x3b,0xbf,0xb6,0xc8,0xe8,0x2f,0x87,0x37,0x7e,0xad,0xaf,0x30,0xd7,0x2a,0xf3,0xa1,0xd4,0x79,0xd8,0x9,0x49,0xf6,0x20,0x1f,0x5c,0x77,0x6,0x44,0xef,0x7f,0x0,0xbb,0x16,0xcf,0xf2,0x28,0x38,0x51,0xba,0x2b,0xb,0x42,0xc7,0x62,0x59,0x81,0x33,0xfa,0xed,0x97,0x1c,0x58,0x71,0xd1,0x5f,0x4a,0xae,0xb1,0xa,0x1,0x5a,0xf0,0xb7,0xfd,0x6d,0x74,0xc4,0x45,0x3e,0xb8,0xe6,0x2,0xa0,0x9a,0x88,0xb9,0xa3,0xde,0xbd,0x1b,0x7,0x46,0x8b,0xfb,0x95,0x6f,0x75,0x9d,0x9b,0x35,0x4,0x6a,0xf8,0x50,0x4d,0xb2,0x57,0x85,0x31,0xc,0x15,0x4c,0x9f,0xc9,0x76,0xc3,0x4f,0x91,0x93,0x56,0xd0,0xa5,0x24,0x4e,0x82,0x7b,0x90,0xa7,0xe1,0xee,0xea,0x21,0xcb,0xcd,0xbc,0xe4,0x7d,0x83,0xf9,0x6e,0xac,0xd5,0xb4,0x5b,0x1a,0x99,0x6c,0x61,0x52,0x55,0x4b,0x8,0xf5,0x23,0xcc,0x47,0xce,0xc0,0x1e,0xc1,0x14,0x70,0x26,0x3a,0xd9,0xe7,0x27,0x2d,0xdb,0x94,0x78,0x7c,0xda,0xf,0x65,0x72,0x17,0x60,0xb3,0x9e,0xfe,0x98,0xd3,0xe5,0x3,0xbe,0x18,0x19,0x13,0xe2,0xa9,0xf4,0x43,0x5e,0x68,0x7a,0xab,0x84,0x80,0x12,0x34,0x96,0x39,0x22,0x29,0xff,0x2e,0xb5,0x73,0x92,0x32,0xd2,0x3d,0xf7,0xc5,0x40,0x11,0x8d,0xb0,0xdf,0x3f,0xca,0x3c,0xeb,0x25,0x8a,0x1d,0x6b,0x64,0xe3,0xe9,0x8e,0x89,0x10,0xec,0x8c,0x36,0xc2,0xe,0x66,0xc6,0x9c,0x5,0x2c,0xa4,0x48,0xd6,0xa2,0x69,0x41,0x53,0x8f,0xfc,0xdc,0xaa,0x67,0x86,0x63,0xd,0xdd,0xa6,0xe0};
+
+uint32_t table_s9[] = {0x7cba6f01,0x2c06b734,0x220ec640,0x72b21e75,0x82c46d87,0xd278b5b2,0xdc70c4c6,0x8ccc1cf3,0xa4a8a905,0xf4147130,0xfa1c0044,0xaaa0d871,0x5ad6ab83,0xa6a73b6,0x46202c2,0x54dedaf7,0x2854fa3e,0x78e8220b,0x76e0537f,0x265c8b4a,0xd62af8b8,0x8696208d,0x889e51f9,0xd82289cc,0xf0463c3a,0xa0fae40f,0xaef2957b,0xfe4e4d4e,0xe383ebc,0x5e84e689,0x508c97fd,0x304fc8,0x613fde08,0x3183063d,0x3f8b7749,0x6f37af7c,0x9f41dc8e,0xcffd04bb,0xc1f575cf,0x9149adfa,0xb92d180c,0xe991c039,0xe799b14d,0xb7256978,0x47531a8a,0x17efc2bf,0x19e7b3cb,0x495b6bfe,0x35d14b37,0x656d9302,0x6b65e276,0x3bd93a43,0xcbaf49b1,0x9b139184,0x951be0f0,0xc5a738c5,0xedc38d33,0xbd7f5506,0xb3772472,0xe3cbfc47,0x13bd8fb5,0x43015780,0x4d0926f4,0x1db5fec1,0x8ab92d9,0x58174aec,0x561f3b98,0x6a3e3ad,0xf6d5905f,0xa669486a,0xa861391e,0xf8dde12b,0xd0b954dd,0x80058ce8,0x8e0dfd9c,0xdeb125a9,0x2ec7565b,0x7e7b8e6e,0x7073ff1a,0x20cf272f,0x5c4507e6,0xcf9dfd3,0x2f1aea7,0x524d7692,0xa23b0560,0xf287dd55,0xfc8fac21,0xac337414,0x8457c1e2,0xd4eb19d7,0xdae368a3,0x8a5fb096,0x7a29c364,0x2a951b51,0x249d6a25,0x7421b210,0x152e23d0,0x4592fbe5,0x4b9a8a91,0x1b2652a4,0xeb502156,0xbbecf963,0xb5e48817,0xe5585022,0xcd3ce5d4,0x9d803de1,0x93884c95,0xc33494a0,0x3342e752,0x63fe3f67,0x6df64e13,0x3d4a9626,0x41c0b6ef,0x117c6eda,0x1f741fae,0x4fc8c79b,0xbfbeb469,0xef026c5c,0xe10a1d28,0xb1b6c51d,0x99d270eb,0xc96ea8de,0xc766d9aa,0x97da019f,0x67ac726d,0x3710aa58,0x3918db2c,0x69a40319,0xec5d851c,0xbce15d29,0xb2e92c5d,0xe255f468,0x1223879a,0x429f5faf,0x4c972edb,0x1c2bf6ee,0x344f4318,0x64f39b2d,0x6afbea59,0x3a47326c,0xca31419e,0x9a8d99ab,0x9485e8df,0xc43930ea,0xb8b31023,0xe80fc816,0xe607b962,0xb6bb6157,0x46cd12a5,0x1671ca90,0x1879bbe4,0x48c563d1,0x60a1d627,0x301d0e12,0x3e157f66,0x6ea9a753,0x9edfd4a1,0xce630c94,0xc06b7de0,0x90d7a5d5,0xf1d83415,0xa164ec20,0xaf6c9d54,0xffd04561,0xfa63693,0x5f1aeea6,0x51129fd2,0x1ae47e7,0x29caf211,0x79762a24,0x777e5b50,0x27c28365,0xd7b4f097,0x870828a2,0x890059d6,0xd9bc81e3,0xa536a12a,0xf58a791f,0xfb82086b,0xab3ed05e,0x5b48a3ac,0xbf47b99,0x5fc0aed,0x5540d2d8,0x7d24672e,0x2d98bf1b,0x2390ce6f,0x732c165a,0x835a65a8,0xd3e6bd9d,0xddeecce9,0x8d5214dc,0x984c78c4,0xc8f0a0f1,0xc6f8d185,0x964409b0,0x66327a42,0x368ea277,0x3886d303,0x683a0b36,0x405ebec0,0x10e266f5,0x1eea1781,0x4e56cfb4,0xbe20bc46,0xee9c6473,0xe0941507,0xb028cd32,0xcca2edfb,0x9c1e35ce,0x921644ba,0xc2aa9c8f,0x32dcef7d,0x62603748,0x6c68463c,0x3cd49e09,0x14b02bff,0x440cf3ca,0x4a0482be,0x1ab85a8b,0xeace2979,0xba72f14c,0xb47a8038,0xe4c6580d,0x85c9c9cd,0xd57511f8,0xdb7d608c,0x8bc1b8b9,0x7bb7cb4b,0x2b0b137e,0x2503620a,0x75bfba3f,0x5ddb0fc9,0xd67d7fc,0x36fa688,0x53d37ebd,0xa3a50d4f,0xf319d57a,0xfd11a40e,0xadad7c3b,0xd1275cf2,0x819b84c7,0x8f93f5b3,0xdf2f2d86,0x2f595e74,0x7fe58641,0x71edf735,0x21512f00,0x9359af6,0x598942c3,0x578133b7,0x73deb82,0xf74b9870,0xa7f74045,0xa9ff3131,0xf943e904,0xcdbd827d,0x7165b72d,0x7914c323,0xc5ccf673,0xb3bf0483,0xf6731d3,0x71645dd,0xbbce708d,0xdf7b86a5,0x63a3b3f5,0x6bd2c7fb,0xd70af2ab,0xa179005b,0x1da1350b,0x15d04105,0xa9087455,0x2328bd29,0x9ff08879,0x9781fc77,0x2b59c927,0x5d2a3bd7,0xe1f20e87,0xe9837a89,0x555b4fd9,0x31eeb9f1,0x8d368ca1,0x8547f8af,0x399fcdff,0x4fec3f0f,0xf3340a5f,0xfb457e51,0x479d4b01,0x480c8b60,0xf4d4be30,0xfca5ca3e,0x407dff6e,0x360e0d9e,0x8ad638ce,0x82a74cc0,0x3e7f7990,0x5aca8fb8,0xe612bae8,0xee63cee6,0x52bbfbb6,0x24c80946,0x98103c16,0x90614818,0x2cb97d48,0xa699b434,0x1a418164,0x1230f56a,0xaee8c03a,0xd89b32ca,0x6443079a,0x6c327394,0xd0ea46c4,0xb45fb0ec,0x88785bc,0xf6f1b2,0xbc2ec4e2,0xca5d3612,0x76850342,0x7ef4774c,0xc22c421c,0xdc405a09,0x60986f59,0x68e91b57,0xd4312e07,0xa242dcf7,0x1e9ae9a7,0x16eb9da9,0xaa33a8f9,0xce865ed1,0x725e6b81,0x7a2f1f8f,0xc6f72adf,0xb084d82f,0xc5ced7f,0x42d9971,0xb8f5ac21,0x32d5655d,0x8e0d500d,0x867c2403,0x3aa41153,0x4cd7e3a3,0xf00fd6f3,0xf87ea2fd,0x44a697ad,0x20136185,0x9ccb54d5,0x94ba20db,0x2862158b,0x5e11e77b,0xe2c9d22b,0xeab8a625,0x56609375,0x59f15314,0xe5296644,0xed58124a,0x5180271a,0x27f3d5ea,0x9b2be0ba,0x935a94b4,0x2f82a1e4,0x4b3757cc,0xf7ef629c,0xff9e1692,0x434623c2,0x3535d132,0x89ede462,0x819c906c,0x3d44a53c,0xb7646c40,0xbbc5910,0x3cd2d1e,0xbf15184e,0xc966eabe,0x75bedfee,0x7dcfabe0,0xc1179eb0,0xa5a26898,0x197a5dc8,0x110b29c6,0xadd31c96,0xdba0ee66,0x6778db36,0x6f09af38,0xd3d19a68,0x2a579fed,0x968faabd,0x9efedeb3,0x2226ebe3,0x54551913,0xe88d2c43,0xe0fc584d,0x5c246d1d,0x38919b35,0x8449ae65,0x8c38da6b,0x30e0ef3b,0x46931dcb,0xfa4b289b,0xf23a5c95,0x4ee269c5,0xc4c2a0b9,0x781a95e9,0x706be1e7,0xccb3d4b7,0xbac02647,0x6181317,0xe696719,0xb2b15249,0xd604a461,0x6adc9131,0x62ade53f,0xde75d06f,0xa806229f,0x14de17cf,0x1caf63c1,0xa0775691,0xafe696f0,0x133ea3a0,0x1b4fd7ae,0xa797e2fe,0xd1e4100e,0x6d3c255e,0x654d5150,0xd9956400,0xbd209228,0x1f8a778,0x989d376,0xb551e626,0xc32214d6,0x7ffa2186,0x778b5588,0xcb5360d8,0x4173a9a4,0xfdab9cf4,0xf5dae8fa,0x4902ddaa,0x3f712f5a,0x83a91a0a,0x8bd86e04,0x37005b54,0x53b5ad7c,0xef6d982c,0xe71cec22,0x5bc4d972,0x2db72b82,0x916f1ed2,0x991e6adc,0x25c65f8c,0x3baa4799,0x877272c9,0x8f0306c7,0x33db3397,0x45a8c167,0xf970f437,0xf1018039,0x4dd9b569,0x296c4341,0x95b47611,0x9dc5021f,0x211d374f,0x576ec5bf,0xebb6f0ef,0xe3c784e1,0x5f1fb1b1,0xd53f78cd,0x69e74d9d,0x61963993,0xdd4e0cc3,0xab3dfe33,0x17e5cb63,0x1f94bf6d,0xa34c8a3d,0xc7f97c15,0x7b214945,0x73503d4b,0xcf88081b,0xb9fbfaeb,0x523cfbb,0xd52bbb5,0xb18a8ee5,0xbe1b4e84,0x2c37bd4,0xab20fda,0xb66a3a8a,0xc019c87a,0x7cc1fd2a,0x74b08924,0xc868bc74,0xacdd4a5c,0x10057f0c,0x18740b02,0xa4ac3e52,0xd2dfcca2,0x6e07f9f2,0x66768dfc,0xdaaeb8ac,0x508e71d0,0xec564480,0xe427308e,0x58ff05de,0x2e8cf72e,0x9254c27e,0x9a25b670,0x26fd8320,0x42487508,0xfe904058,0xf6e13456,0x4a390106,0x3c4af3f6,0x8092c6a6,0x88e3b2a8,0x343b87f8,0xd78d63dd,0xfb83361,0x7ecc3d69,0xa6f96dd5,0xd50b9da3,0xd3ecd1f,0x7c4ac317,0xa47f93ab,0x1189bbcf,0xc9bceb73,0xb8c8e57b,0x60fdb5c7,0x130f45b1,0xcb3a150d,0xba4e1b05,0x627b4bb9,0x42b23733,0x9a87678f,0xebf36987,0x33c6393b,0x4034c94d,0x980199f1,0xe97597f9,0x3140c745,0x84b6ef21,0x5c83bf9d,0x2df7b195,0xf5c2e129,0x8630115f,0x5e0541e3,0x2f714feb,0xf7441f57,0x66847e58,0xbeb12ee4,0xcfc520ec,0x17f07050,0x64028026,0xbc37d09a,0xcd43de92,0x15768e2e,0xa080a64a,0x78b5f6f6,0x9c1f8fe,0xd1f4a842,0xa2065834,0x7a330888,0xb470680,0xd372563c,0xf3bb2ab6,0x2b8e7a0a,0x5afa7402,0x82cf24be,0xf13dd4c8,0x29088474,0x587c8a7c,0x8049dac0,0x35bff2a4,0xed8aa218,0x9cfeac10,0x44cbfcac,0x37390cda,0xef0c5c66,0x9e78526e,0x464d02d2,0x2a5517cc,0xf2604770,0x83144978,0x5b2119c4,0x28d3e9b2,0xf0e6b90e,0x8192b706,0x59a7e7ba,0xec51cfde,0x34649f62,0x4510916a,0x9d25c1d6,0xeed731a0,0x36e2611c,0x47966f14,0x9fa33fa8,0xbf6a4322,0x675f139e,0x162b1d96,0xce1e4d2a,0xbdecbd5c,0x65d9ede0,0x14ade3e8,0xcc98b354,0x796e9b30,0xa15bcb8c,0xd02fc584,0x81a9538,0x7be8654e,0xa3dd35f2,0xd2a93bfa,0xa9c6b46,0x9b5c0a49,0x43695af5,0x321d54fd,0xea280441,0x99daf437,0x41efa48b,0x309baa83,0xe8aefa3f,0x5d58d25b,0x856d82e7,0xf4198cef,0x2c2cdc53,0x5fde2c25,0x87eb7c99,0xf69f7291,0x2eaa222d,0xe635ea7,0xd6560e1b,0xa7220013,0x7f1750af,0xce5a0d9,0xd4d0f065,0xa5a4fe6d,0x7d91aed1,0xc86786b5,0x1052d609,0x6126d801,0xb91388bd,0xcae178cb,0x12d42877,0x63a0267f,0xbb9576c3,0x3d90f33a,0xe5a5a386,0x94d1ad8e,0x4ce4fd32,0x3f160d44,0xe7235df8,0x965753f0,0x4e62034c,0xfb942b28,0x23a17b94,0x52d5759c,0x8ae02520,0xf912d556,0x212785ea,0x50538be2,0x8866db5e,0xa8afa7d4,0x709af768,0x1eef960,0xd9dba9dc,0xaa2959aa,0x721c0916,0x368071e,0xdb5d57a2,0x6eab7fc6,0xb69e2f7a,0xc7ea2172,0x1fdf71ce,0x6c2d81b8,0xb418d104,0xc56cdf0c,0x1d598fb0,0x8c99eebf,0x54acbe03,0x25d8b00b,0xfdede0b7,0x8e1f10c1,0x562a407d,0x275e4e75,0xff6b1ec9,0x4a9d36ad,0x92a86611,0xe3dc6819,0x3be938a5,0x481bc8d3,0x902e986f,0xe15a9667,0x396fc6db,0x19a6ba51,0xc193eaed,0xb0e7e4e5,0x68d2b459,0x1b20442f,0xc3151493,0xb2611a9b,0x6a544a27,0xdfa26243,0x79732ff,0x76e33cf7,0xaed66c4b,0xdd249c3d,0x511cc81,0x7465c289,0xac509235,0xc048872b,0x187dd797,0x6909d99f,0xb13c8923,0xc2ce7955,0x1afb29e9,0x6b8f27e1,0xb3ba775d,0x64c5f39,0xde790f85,0xaf0d018d,0x77385131,0x4caa147,0xdcfff1fb,0xad8bfff3,0x75beaf4f,0x5577d3c5,0x8d428379,0xfc368d71,0x2403ddcd,0x57f12dbb,0x8fc47d07,0xfeb0730f,0x268523b3,0x93730bd7,0x4b465b6b,0x3a325563,0xe20705df,0x91f5f5a9,0x49c0a515,0x38b4ab1d,0xe081fba1,0x71419aae,0xa974ca12,0xd800c41a,0x3594a6,0x73c764d0,0xabf2346c,0xda863a64,0x2b36ad8,0xb74542bc,0x6f701200,0x1e041c08,0xc6314cb4,0xb5c3bcc2,0x6df6ec7e,0x1c82e276,0xc4b7b2ca,0xe47ece40,0x3c4b9efc,0x4d3f90f4,0x950ac048,0xe6f8303e,0x3ecd6082,0x4fb96e8a,0x978c3e36,0x227a1652,0xfa4f46ee,0x8b3b48e6,0x530e185a,0x20fce82c,0xf8c9b890,0x89bdb698,0x5188e624,0x52ae490,0x307a5848,0x44745039,0x7124ece1,0x83d49a92,0xb684264a,0xc28a2e3b,0xf7da92e3,0x1f2f656,0x34a24a8e,0x40ac42ff,0x75fcfe27,0x870c8854,0xb25c348c,0xc6523cfd,0xf3028025,0x3a7e0a05,0xf2eb6dd,0x7b20beac,0x4e700274,0xbc807407,0x89d0c8df,0xfddec0ae,0xc88e7c76,0x3ea618c3,0xbf6a41b,0x7ff8ac6a,0x4aa810b2,0xb85866c1,0x8d08da19,0xf906d268,0xcc566eb0,0xc376121,0x3967ddf9,0x4d69d588,0x78396950,0x8ac91f23,0xbf99a3fb,0xcb97ab8a,0xfec71752,0x8ef73e7,0x3dbfcf3f,0x49b1c74e,0x7ce17b96,0x8e110de5,0xbb41b13d,0xcf4fb94c,0xfa1f0594,0x33638fb4,0x633336c,0x723d3b1d,0x476d87c5,0xb59df1b6,0x80cd4d6e,0xf4c3451f,0xc193f9c7,0x37bb9d72,0x2eb21aa,0x76e529db,0x43b59503,0xb145e370,0x84155fa8,0xf01b57d9,0xc54beb01,0xdd5ef56d,0xe80e49b5,0x9c0041c4,0xa950fd1c,0x5ba08b6f,0x6ef037b7,0x1afe3fc6,0x2fae831e,0xd986e7ab,0xecd65b73,0x98d85302,0xad88efda,0x5f7899a9,0x6a282571,0x1e262d00,0x2b7691d8,0xe20a1bf8,0xd75aa720,0xa354af51,0x96041389,0x64f465fa,0x51a4d922,0x25aad153,0x10fa6d8b,0xe6d2093e,0xd382b5e6,0xa78cbd97,0x92dc014f,0x602c773c,0x557ccbe4,0x2172c395,0x14227f4d,0xd44370dc,0xe113cc04,0x951dc475,0xa04d78ad,0x52bd0ede,0x67edb206,0x13e3ba77,0x26b306af,0xd09b621a,0xe5cbdec2,0x91c5d6b3,0xa4956a6b,0x56651c18,0x6335a0c0,0x173ba8b1,0x226b1469,0xeb179e49,0xde472291,0xaa492ae0,0x9f199638,0x6de9e04b,0x58b95c93,0x2cb754e2,0x19e7e83a,0xefcf8c8f,0xda9f3057,0xae913826,0x9bc184fe,0x6931f28d,0x5c614e55,0x286f4624,0x1d3ffafc,0x18ba037a,0x2deabfa2,0x59e4b7d3,0x6cb40b0b,0x9e447d78,0xab14c1a0,0xdf1ac9d1,0xea4a7509,0x1c6211bc,0x2932ad64,0x5d3ca515,0x686c19cd,0x9a9c6fbe,0xafccd366,0xdbc2db17,0xee9267cf,0x27eeedef,0x12be5137,0x66b05946,0x53e0e59e,0xa11093ed,0x94402f35,0xe04e2744,0xd51e9b9c,0x2336ff29,0x166643f1,0x62684b80,0x5738f758,0xa5c8812b,0x90983df3,0xe4963582,0xd1c6895a,0x11a786cb,0x24f73a13,0x50f93262,0x65a98eba,0x9759f8c9,0xa2094411,0xd6074c60,0xe357f0b8,0x157f940d,0x202f28d5,0x542120a4,0x61719c7c,0x9381ea0f,0xa6d156d7,0xd2df5ea6,0xe78fe27e,0x2ef3685e,0x1ba3d486,0x6faddcf7,0x5afd602f,0xa80d165c,0x9d5daa84,0xe953a2f5,0xdc031e2d,0x2a2b7a98,0x1f7bc640,0x6b75ce31,0x5e2572e9,0xacd5049a,0x9985b842,0xed8bb033,0xd8db0ceb,0xc0ce1287,0xf59eae5f,0x8190a62e,0xb4c01af6,0x46306c85,0x7360d05d,0x76ed82c,0x323e64f4,0xc4160041,0xf146bc99,0x8548b4e8,0xb0180830,0x42e87e43,0x77b8c29b,0x3b6caea,0x36e67632,0xff9afc12,0xcaca40ca,0xbec448bb,0x8b94f463,0x79648210,0x4c343ec8,0x383a36b9,0xd6a8a61,0xfb42eed4,0xce12520c,0xba1c5a7d,0x8f4ce6a5,0x7dbc90d6,0x48ec2c0e,0x3ce2247f,0x9b298a7,0xc9d39736,0xfc832bee,0x888d239f,0xbddd9f47,0x4f2de934,0x7a7d55ec,0xe735d9d,0x3b23e145,0xcd0b85f0,0xf85b3928,0x8c553159,0xb9058d81,0x4bf5fbf2,0x7ea5472a,0xaab4f5b,0x3ffbf383,0xf68779a3,0xc3d7c57b,0xb7d9cd0a,0x828971d2,0x707907a1,0x4529bb79,0x3127b308,0x4770fd0,0xf25f6b65,0xc70fd7bd,0xb301dfcc,0x86516314,0x74a11567,0x41f1a9bf,0x35ffa1ce,0xaf1d16};
+
+unsigned char table_s10[] = {0x54,0x67,0xc5,0x1a,0x53,0x9,0xa6,0xeb,0xac,0x7,0xc2,0xe7,0x6c,0xa7,0x8d,0x7b,0xc1,0x83,0x2a,0x6,0x51,0x21,0xb7,0x36,0x52,0xe6,0x74,0x14,0x99,0x8e,0xf,0x17,0x55,0x4f,0x48,0x35,0xc,0xe3,0x3f,0x2e,0x42,0x97,0x82,0x1f,0x84,0xe,0x4d,0x7f,0xe4,0x81,0xc3,0xbf,0xa1,0xa9,0xaf,0x1c,0x57,0xd4,0x28,0x7e,0xb4,0x18,0x66,0x41,0xcc,0x95,0x45,0x70,0x90,0xe9,0x8f,0xb3,0xe5,0xbd,0x3e,0x8c,0x3c,0xbb,0xa0,0xfc,0x3a,0x9c,0x65,0xca,0x5b,0x4e,0x34,0x4,0x4c,0x2b,0x1d,0x30,0xda,0x3b,0xa3,0x8a,0xf1,0x93,0xe0,0xc6,0x77,0x7a,0x5d,0x92,0x6a,0xf4,0x76,0xcf,0x23,0x2c,0x40,0x4b,0xb6,0xde,0xd,0x39,0x56,0x20,0xe2,0x5e,0x44,0xea,0xfe,0x25,0x9b,0x62,0xb9,0x31,0xf2,0xd9,0xcb,0x61,0xee,0x1e,0xd1,0x80,0xa5,0xc9,0x24,0xd7,0xe8,0x12,0xed,0x3d,0xe1,0x8,0x5c,0x38,0xba,0xd3,0x64,0xb5,0x26,0xd2,0xaa,0x91,0xbe,0x16,0x6b,0x5,0x6d,0xfb,0xdf,0x5f,0x19,0x4a,0x75,0x89,0xd6,0xf8,0x9a,0x22,0x85,0xd8,0x33,0xd0,0x3,0x68,0x87,0x13,0xb1,0xdb,0xc7,0x43,0x1,0x9d,0xf9,0xb8,0xd5,0xa4,0xf6,0xfa,0x46,0x2f,0x6e,0x73,0xc4,0x8b,0x94,0x71,0xb2,0xec,0x27,0x11,0xa,0xc8,0x6f,0xae,0x69,0x96,0xce,0x2,0x10,0x15,0x59,0x7d,0xfd,0xdd,0xb,0xb0,0xef,0xff,0x2d,0xa8,0x7c,0x9e,0x9f,0x98,0xa2,0x88,0xad,0x37,0xab,0xf0,0x50,0xdc,0x0,0x86,0xf7,0x79,0x5a,0x58,0xf5,0x60,0xc0,0x63,0x29,0xf3,0x78,0x32,0x72,0xbc,0x49,0x47,0x1b,0xcd,0xee,0xc3,0xf5,0x92,0x54,0x7d,0xe5,0x4,0x14,0xbb,0x42,0xe4,0xda,0xea,0x90,0x85,0x52,0xe0,0x63,0x3b,0x22,0x7e,0x65,0xe2,0xae,0x9b,0x4b,0x12,0x6d,0x51,0x37,0x4e,0xfb,0x20,0x34,0x9a,0xef,0x67,0xbc,0x45,0xe7,0xd3,0x0,0x68,0x80,0x3c,0xfe,0x88,0x11,0xa8,0x2a,0xb4,0x95,0x9e,0xf2,0xfd,0x18,0x3e,0x4d,0x2f,0x4c,0x83,0xa4,0xa9,0xca,0xaa,0x38,0x8c,0xc9,0xd1,0x50,0x47,0xd8,0xf4,0x5d,0x1f,0xe8,0x69,0xff,0x8f,0x39,0x1c,0xd9,0x72,0xa5,0x53,0x79,0xb2,0xc4,0x1b,0xb9,0x8a,0x35,0x78,0xd7,0x8d,0xa0,0xf6,0xa,0x89,0x9f,0xb8,0xc6,0x6a,0x61,0x1d,0x5f,0x3a,0xc2,0x71,0x77,0x7f,0xc1,0x5c,0x49,0x9c,0xa1,0x93,0xd0,0x5a,0xeb,0x96,0x91,0x8b,0xf0,0xe1,0x3d,0xd2,0x6e,0xd5,0x3,0x23,0x76,0xf3,0x21,0x31,0xdc,0x10,0x48,0xb7,0xa3,0x87,0xcb,0xce,0xcf,0xf9,0x32,0x6c,0x70,0xb1,0x16,0xd4,0xad,0xb0,0xf1,0x98,0xaf,0x4a,0x55,0x1a,0x62,0xac,0xec,0xa6,0x13,0xc5,0x99,0x97,0xbe,0x2b,0x86,0x84,0x2d,0xf7,0xbd,0x1e,0x2,0x8e,0x2e,0x75,0xa7,0x29,0x58,0xde,0x46,0x41,0x40,0xa2,0xe9,0x73,0x56,0x7c,0x4f,0x74,0xc,0xf8,0xdb,0xb5,0xc8,0x60,0xe6,0x82,0xd6,0x3f,0x6b,0xba,0xd,0x64,0x9,0xfa,0x17,0x7b,0xe3,0x33,0xcc,0x36,0xbf,0x15,0x7,0x2c,0x5e,0xf,0xc0,0x30,0x66,0x27,0x43,0xdf,0x24,0x28,0x7a,0xb,0xcd,0x59,0xb6,0xdd,0x9d,0x19,0x5,0x6f,0xfc,0x44,0x26,0x8,0xe,0xed,0x6,0x5b,0x81,0x1,0x25,0xb3,0x57,0xab,0x94,0xc7,0x26,0xcf,0x16,0x72,0xfd,0x94,0x9b,0x4a,0xfc,0x8,0xbf,0x84,0x38,0x90,0x2b,0x45,0xf7,0xdc,0x4f,0xe5,0x30,0xc0,0xae,0xff,0xe7,0x8b,0xf9,0xa,0x3c,0xc6,0x13,0xc3,0x46,0x2d,0x3d,0xa9,0xf5,0x9f,0x6d,0xe9,0xb3,0x2f,0x96,0xd7,0x8a,0xfb,0xd4,0xd8,0xd5,0x43,0x71,0xf1,0x64,0x37,0xa7,0x5b,0xd6,0xf8,0xc,0xb4,0xf6,0xab,0xfe,0x1d,0xb8,0x47,0x2c,0xe0,0x3b,0x3e,0x53,0x77,0xf3,0xd3,0x9e,0x25,0xd1,0xc1,0x86,0x3,0x1,0x68,0x5d,0x40,0xa5,0xea,0x5f,0xba,0xc2,0x9c,0x3f,0x9,0xe6,0x24,0x80,0x41,0x76,0x74,0x4e,0xdb,0x4d,0xee,0xdd,0x7,0x1c,0x56,0x92,0x5c,0x69,0x67,0xe3,0x35,0xb0,0x52,0xb6,0xb1,0xa6,0x8c,0x19,0x83,0xde,0x85,0xf2,0x7e,0xa8,0x2e,0x57,0xd9,0xad,0xef,0x28,0x4,0xf,0x7f,0x18,0x99,0xc8,0x7c,0x3a,0x5a,0xa0,0xb7,0x39,0x21,0x49,0x7a,0x34,0xeb,0x27,0x7d,0xc5,0x88,0x29,0x82,0xc9,0xec,0x89,0x42,0x55,0xa3,0xaf,0xca,0x91,0xed,0x87,0x8f,0x32,0x81,0xfa,0x79,0x50,0x6,0x36,0x9a,0x6f,0x48,0x61,0x7b,0x1b,0x66,0xcd,0x22,0x0,0x11,0xb9,0x6c,0x31,0xac,0x20,0xaa,0x51,0x63,0xb2,0x14,0xe4,0x4b,0x60,0x75,0x2a,0x1a,0x5,0x62,0x1e,0x33,0x15,0xf4,0xa4,0x8d,0xbb,0xe2,0x5e,0x6b,0xc7,0xbe,0x9d,0xa1,0x93,0xcb,0xa2,0x10,0x95,0x12,0xd2,0x8e,0xf0,0x98,0x17,0x23,0xe,0x78,0x70,0xcc,0xc4,0x6a,0xb,0xd0,0x4c,0xb5,0x1f,0x97,0xbd,0xdf,0xe8,0xce,0x54,0x59,0xbc,0x73,0xda,0x44,0xe1,0x58,0x2,0xd,0x65,0x6e,0x3,0xac,0x5c,0xfa,0x52,0x62,0x3d,0x28,0x7b,0x56,0x2a,0x4d,0xc5,0xec,0xbc,0x5d,0x23,0x16,0xaa,0xf3,0xe9,0xd5,0xf6,0x8f,0x58,0xea,0x83,0xdb,0xc6,0x9a,0x5a,0xdd,0x6b,0x5f,0xd0,0xb8,0x84,0x38,0x30,0x46,0x98,0x43,0x22,0x8c,0xdf,0x57,0xfd,0x4,0x86,0xa0,0x97,0xf5,0x3b,0xf4,0x11,0x1c,0x10,0xa9,0xc,0x92,0x26,0x2d,0x45,0x4a,0x4c,0x60,0xa7,0xe5,0xd1,0x50,0x37,0x47,0x12,0x72,0x34,0x80,0x69,0x71,0xff,0xe8,0xa3,0x7c,0x32,0x1,0xc0,0x8d,0x35,0x6f,0xa4,0x81,0xca,0x61,0xeb,0x1d,0xa,0xc1,0xa5,0xd9,0x82,0xe7,0xc9,0x7a,0xc7,0xcf,0x4e,0x18,0x31,0xb2,0x0,0x27,0xd2,0x7e,0x2e,0x53,0x33,0x29,0x59,0x48,0x6a,0x85,0xe4,0x79,0x24,0xf1,0x2b,0x19,0xe2,0x68,0xa8,0x64,0xf,0xf0,0x3f,0x1b,0x76,0x73,0x6d,0xd6,0x9b,0xbb,0x4b,0xce,0x89,0x99,0x8,0x15,0x20,0x49,0xf2,0x17,0xa2,0xed,0x41,0x77,0xd4,0x8a,0x9,0xc8,0x6c,0xae,0x93,0x6,0x3c,0x3e,0x4f,0x95,0xa6,0x5,0x14,0xda,0x1e,0x54,0x7d,0xab,0x2f,0x21,0xf9,0xfe,0x1a,0xf8,0xcb,0x51,0xc4,0xee,0x36,0xba,0xcd,0x96,0x91,0x1f,0x66,0xe0,0x3a,0x5e,0x87,0x6e,0x2,0xd3,0xdc,0xb5,0xcc,0xf7,0x40,0xb4,0xd,0x63,0xd8,0x70,0xad,0x7,0x94,0xbf,0xb7,0xe6,0x88,0x78,0x42,0xb1,0xc3,0xaf,0x8b,0x5b,0x8e,0x74,0xe1,0x75,0x65,0xe,0xa1,0x25,0xd7,0xbd,0x9f,0xde,0x67,0xfb,0x90,0x9c,0xb3,0xc2,0xb9,0x39,0xb,0x9d,0x13,0xef,0x7f,0x2c,0xfc,0x44,0xb0,0x9e,0x55,0xb6,0xe3,0xbe,0xe4,0xd7,0x75,0xaa,0xe3,0xb9,0x16,0x5b,0x1c,0xb7,0x72,0x57,0xdc,0x17,0x3d,0xcb,0x71,0x33,0x9a,0xb6,0xe1,0x91,0x7,0x86,0xe2,0x56,0xc4,0xa4,0x29,0x3e,0xbf,0xa7,0xe5,0xff,0xf8,0x85,0xbc,0x53,0x8f,0x9e,0xf2,0x27,0x32,0xaf,0x34,0xbe,0xfd,0xcf,0x54,0x31,0x73,0xf,0x11,0x19,0x1f,0xac,0xe7,0x64,0x98,0xce,0x4,0xa8,0xd6,0xf1,0x7c,0x25,0xf5,0xc0,0x20,0x59,0x3f,0x3,0x55,0xd,0x8e,0x3c,0x8c,0xb,0x10,0x4c,0x8a,0x2c,0xd5,0x7a,0xeb,0xfe,0x84,0xb4,0xfc,0x9b,0xad,0x80,0x6a,0x8b,0x13,0x3a,0x41,0x23,0x50,0x76,0xc7,0xca,0xed,0x22,0xda,0x44,0xc6,0x7f,0x93,0x9c,0xf0,0xfb,0x6,0x6e,0xbd,0x89,0xe6,0x90,0x52,0xee,0xf4,0x5a,0x4e,0x95,0x2b,0xd2,0x9,0x81,0x42,0x69,0x7b,0xd1,0x5e,0xae,0x61,0x30,0x15,0x79,0x94,0x67,0x58,0xa2,0x5d,0x8d,0x51,0xb8,0xec,0x88,0xa,0x63,0xd4,0x5,0x96,0x62,0x1a,0x21,0xe,0xa6,0xdb,0xb5,0xdd,0x4b,0x6f,0xef,0xa9,0xfa,0xc5,0x39,0x66,0x48,0x2a,0x92,0x35,0x68,0x83,0x60,0xb3,0xd8,0x37,0xa3,0x1,0x6b,0x77,0xf3,0xb1,0x2d,0x49,0x8,0x65,0x14,0x46,0x4a,0xf6,0x9f,0xde,0xc3,0x74,0x3b,0x24,0xc1,0x2,0x5c,0x97,0xa1,0xba,0x78,0xdf,0x1e,0xd9,0x26,0x7e,0xb2,0xa0,0xa5,0xe9,0xcd,0x4d,0x6d,0xbb,0x0,0x5f,0x4f,0x9d,0x18,0xcc,0x2e,0x2f,0x28,0x12,0x38,0x1d,0x87,0x1b,0x40,0xe0,0x6c,0xb0,0x36,0x47,0xc9,0xea,0xe8,0x45,0xd0,0x70,0xd3,0x99,0x43,0xc8,0x82,0xc2,0xc,0xf9,0xf7,0xab,0x7d,0x3d,0x10,0x26,0x41,0x87,0xae,0x36,0xd7,0xc7,0x68,0x91,0x37,0x9,0x39,0x43,0x56,0x81,0x33,0xb0,0xe8,0xf1,0xad,0xb6,0x31,0x7d,0x48,0x98,0xc1,0xbe,0x82,0xe4,0x9d,0x28,0xf3,0xe7,0x49,0x3c,0xb4,0x6f,0x96,0x34,0x0,0xd3,0xbb,0x53,0xef,0x2d,0x5b,0xc2,0x7b,0xf9,0x67,0x46,0x4d,0x21,0x2e,0xcb,0xed,0x9e,0xfc,0x9f,0x50,0x77,0x7a,0x19,0x79,0xeb,0x5f,0x1a,0x2,0x83,0x94,0xb,0x27,0x8e,0xcc,0x3b,0xba,0x2c,0x5c,0xea,0xcf,0xa,0xa1,0x76,0x80,0xaa,0x61,0x17,0xc8,0x6a,0x59,0xe6,0xab,0x4,0x5e,0x73,0x25,0xd9,0x5a,0x4c,0x6b,0x15,0xb9,0xb2,0xce,0x8c,0xe9,0x11,0xa2,0xa4,0xac,0x12,0x8f,0x9a,0x4f,0x72,0x40,0x3,0x89,0x38,0x45,0x42,0x58,0x23,0x32,0xee,0x1,0xbd,0x6,0xd0,0xf0,0xa5,0x20,0xf2,0xe2,0xf,0xc3,0x9b,0x64,0x70,0x54,0x18,0x1d,0x1c,0x2a,0xe1,0xbf,0xa3,0x62,0xc5,0x7,0x7e,0x63,0x22,0x4b,0x7c,0x99,0x86,0xc9,0xb1,0x7f,0x3f,0x75,0xc0,0x16,0x4a,0x44,0x6d,0xf8,0x55,0x57,0xfe,0x24,0x6e,0xcd,0xd1,0x5d,0xfd,0xa6,0x74,0xfa,0x8b,0xd,0x95,0x92,0x93,0x71,0x3a,0xa0,0x85,0xaf,0x9c,0xa7,0xdf,0x2b,0x8,0x66,0x1b,0xb3,0x35,0x51,0x5,0xec,0xb8,0x69,0xde,0xb7,0xda,0x29,0xc4,0xa8,0x30,0xe0,0x1f,0xe5,0x6c,0xc6,0xd4,0xff,0x8d,0xdc,0x13,0xe3,0xb5,0xf4,0x90,0xc,0xf7,0xfb,0xa9,0xd8,0x1e,0x8a,0x65,0xe,0x4e,0xca,0xd6,0xbc,0x2f,0x97,0xf5,0xdb,0xdd,0x3e,0xd5,0x88,0x52,0xd2,0xf6,0x60,0x84,0x78,0x47,0x14,0x2c,0xc5,0x1c,0x78,0xf7,0x9e,0x91,0x40,0xf6,0x2,0xb5,0x8e,0x32,0x9a,0x21,0x4f,0xfd,0xd6,0x45,0xef,0x3a,0xca,0xa4,0xf5,0xed,0x81,0xf3,0x0,0x36,0xcc,0x19,0xc9,0x4c,0x27,0x37,0xa3,0xff,0x95,0x67,0xe3,0xb9,0x25,0x9c,0xdd,0x80,0xf1,0xde,0xd2,0xdf,0x49,0x7b,0xfb,0x6e,0x3d,0xad,0x51,0xdc,0xf2,0x6,0xbe,0xfc,0xa1,0xf4,0x17,0xb2,0x4d,0x26,0xea,0x31,0x34,0x59,0x7d,0xf9,0xd9,0x94,0x2f,0xdb,0xcb,0x8c,0x9,0xb,0x62,0x57,0x4a,0xaf,0xe0,0x55,0xb0,0xc8,0x96,0x35,0x3,0xec,0x2e,0x8a,0x4b,0x7c,0x7e,0x44,0xd1,0x47,0xe4,0xd7,0xd,0x16,0x5c,0x98,0x56,0x63,0x6d,0xe9,0x3f,0xba,0x58,0xbc,0xbb,0xac,0x86,0x13,0x89,0xd4,0x8f,0xf8,0x74,0xa2,0x24,0x5d,0xd3,0xa7,0xe5,0x22,0xe,0x5,0x75,0x12,0x93,0xc2,0x76,0x30,0x50,0xaa,0xbd,0x33,0x2b,0x43,0x70,0x3e,0xe1,0x2d,0x77,0xcf,0x82,0x23,0x88,0xc3,0xe6,0x83,0x48,0x5f,0xa9,0xa5,0xc0,0x9b,0xe7,0x8d,0x85,0x38,0x8b,0xf0,0x73,0x5a,0xc,0x3c,0x90,0x65,0x42,0x6b,0x71,0x11,0x6c,0xc7,0x28,0xa,0x1b,0xb3,0x66,0x3b,0xa6,0x2a,0xa0,0x5b,0x69,0xb8,0x1e,0xee,0x41,0x6a,0x7f,0x20,0x10,0xf,0x68,0x14,0x39,0x1f,0xfe,0xae,0x87,0xb1,0xe8,0x54,0x61,0xcd,0xb4,0x97,0xab,0x99,0xc1,0xa8,0x1a,0x9f,0x18,0xd8,0x84,0xfa,0x92,0x1d,0x29,0x4,0x72,0x7a,0xc6,0xce,0x60,0x1,0xda,0x46,0xbf,0x15,0x9d,0xb7,0xd5,0xe2,0xc4,0x5e,0x53,0xb6,0x79,0xd0,0x4e,0xeb,0x52,0x8,0x7,0x6f,0x64,0x9d,0x32,0xc2,0x64,0xcc,0xfc,0xa3,0xb6,0xe5,0xc8,0xb4,0xd3,0x5b,0x72,0x22,0xc3,0xbd,0x88,0x34,0x6d,0x77,0x4b,0x68,0x11,0xc6,0x74,0x1d,0x45,0x58,0x4,0xc4,0x43,0xf5,0xc1,0x4e,0x26,0x1a,0xa6,0xae,0xd8,0x6,0xdd,0xbc,0x12,0x41,0xc9,0x63,0x9a,0x18,0x3e,0x9,0x6b,0xa5,0x6a,0x8f,0x82,0x8e,0x37,0x92,0xc,0xb8,0xb3,0xdb,0xd4,0xd2,0xfe,0x39,0x7b,0x4f,0xce,0xa9,0xd9,0x8c,0xec,0xaa,0x1e,0xf7,0xef,0x61,0x76,0x3d,0xe2,0xac,0x9f,0x5e,0x13,0xab,0xf1,0x3a,0x1f,0x54,0xff,0x75,0x83,0x94,0x5f,0x3b,0x47,0x1c,0x79,0x57,0xe4,0x59,0x51,0xd0,0x86,0xaf,0x2c,0x9e,0xb9,0x4c,0xe0,0xb0,0xcd,0xad,0xb7,0xc7,0xd6,0xf4,0x1b,0x7a,0xe7,0xba,0x6f,0xb5,0x87,0x7c,0xf6,0x36,0xfa,0x91,0x6e,0xa1,0x85,0xe8,0xed,0xf3,0x48,0x5,0x25,0xd5,0x50,0x17,0x7,0x96,0x8b,0xbe,0xd7,0x6c,0x89,0x3c,0x73,0xdf,0xe9,0x4a,0x14,0x97,0x56,0xf2,0x30,0xd,0x98,0xa2,0xa0,0xd1,0xb,0x38,0x9b,0x8a,0x44,0x80,0xca,0xe3,0x35,0xb1,0xbf,0x67,0x60,0x84,0x66,0x55,0xcf,0x5a,0x70,0xa8,0x24,0x53,0x8,0xf,0x81,0xf8,0x7e,0xa4,0xc0,0x19,0xf0,0x9c,0x4d,0x42,0x2b,0x52,0x69,0xde,0x2a,0x93,0xfd,0x46,0xee,0x33,0x99,0xa,0x21,0x29,0x78,0x16,0xe6,0xdc,0x2f,0x5d,0x31,0x15,0xc5,0x10,0xea,0x7f,0xeb,0xfb,0x90,0x3f,0xbb,0x49,0x23,0x1,0x40,0xf9,0x65,0xe,0x2,0x2d,0x5c,0x27,0xa7,0x95,0x3,0x8d,0x71,0xe1,0xb2,0x62,0xda,0x2e,0x0,0xcb,0x28,0x7d,0x20,0x86,0xb5,0x17,0xc8,0x81,0xdb,0x74,0x39,0x7e,0xd5,0x10,0x35,0xbe,0x75,0x5f,0xa9,0x13,0x51,0xf8,0xd4,0x83,0xf3,0x65,0xe4,0x80,0x34,0xa6,0xc6,0x4b,0x5c,0xdd,0xc5,0x87,0x9d,0x9a,0xe7,0xde,0x31,0xed,0xfc,0x90,0x45,0x50,0xcd,0x56,0xdc,0x9f,0xad,0x36,0x53,0x11,0x6d,0x73,0x7b,0x7d,0xce,0x85,0x6,0xfa,0xac,0x66,0xca,0xb4,0x93,0x1e,0x47,0x97,0xa2,0x42,0x3b,0x5d,0x61,0x37,0x6f,0xec,0x5e,0xee,0x69,0x72,0x2e,0xe8,0x4e,0xb7,0x18,0x89,0x9c,0xe6,0xd6,0x9e,0xf9,0xcf,0xe2,0x8,0xe9,0x71,0x58,0x23,0x41,0x32,0x14,0xa5,0xa8,0x8f,0x40,0xb8,0x26,0xa4,0x1d,0xf1,0xfe,0x92,0x99,0x64,0xc,0xdf,0xeb,0x84,0xf2,0x30,0x8c,0x96,0x38,0x2c,0xf7,0x49,0xb0,0x6b,0xe3,0x20,0xb,0x19,0xb3,0x3c,0xcc,0x3,0x52,0x77,0x1b,0xf6,0x5,0x3a,0xc0,0x3f,0xef,0x33,0xda,0x8e,0xea,0x68,0x1,0xb6,0x67,0xf4,0x0,0x78,0x43,0x6c,0xc4,0xb9,0xd7,0xbf,0x29,0xd,0x8d,0xcb,0x98,0xa7,0x5b,0x4,0x2a,0x48,0xf0,0x57,0xa,0xe1,0x2,0xd1,0xba,0x55,0xc1,0x63,0x9,0x15,0x91,0xd3,0x4f,0x2b,0x6a,0x7,0x76,0x24,0x28,0x94,0xfd,0xbc,0xa1,0x16,0x59,0x46,0xa3,0x60,0x3e,0xf5,0xc3,0xd8,0x1a,0xbd,0x7c,0xbb,0x44,0x1c,0xd0,0xc2,0xc7,0x8b,0xaf,0x2f,0xf,0xd9,0x62,0x3d,0x2d,0xff,0x7a,0xae,0x4c,0x4d,0x4a,0x70,0x5a,0x7f,0xe5,0x79,0x22,0x82,0xe,0xd2,0x54,0x25,0xab,0x88,0x8a,0x27,0xb2,0x12,0xb1,0xfb,0x21,0xaa,0xe0,0xa0,0x6e,0x9b,0x95,0xc9,0x1f,0xcd,0xe0,0xd6,0xb1,0x77,0x5e,0xc6,0x27,0x37,0x98,0x61,0xc7,0xf9,0xc9,0xb3,0xa6,0x71,0xc3,0x40,0x18,0x1,0x5d,0x46,0xc1,0x8d,0xb8,0x68,0x31,0x4e,0x72,0x14,0x6d,0xd8,0x3,0x17,0xb9,0xcc,0x44,0x9f,0x66,0xc4,0xf0,0x23,0x4b,0xa3,0x1f,0xdd,0xab,0x32,0x8b,0x9,0x97,0xb6,0xbd,0xd1,0xde,0x3b,0x1d,0x6e,0xc,0x6f,0xa0,0x87,0x8a,0xe9,0x89,0x1b,0xaf,0xea,0xf2,0x73,0x64,0xfb,0xd7,0x7e,0x3c,0xcb,0x4a,0xdc,0xac,0x1a,0x3f,0xfa,0x51,0x86,0x70,0x5a,0x91,0xe7,0x38,0x9a,0xa9,0x16,0x5b,0xf4,0xae,0x83,0xd5,0x29,0xaa,0xbc,0x9b,0xe5,0x49,0x42,0x3e,0x7c,0x19,0xe1,0x52,0x54,0x5c,0xe2,0x7f,0x6a,0xbf,0x82,0xb0,0xf3,0x79,0xc8,0xb5,0xb2,0xa8,0xd3,0xc2,0x1e,0xf1,0x4d,0xf6,0x20,0x0,0x55,0xd0,0x2,0x12,0xff,0x33,0x6b,0x94,0x80,0xa4,0xe8,0xed,0xec,0xda,0x11,0x4f,0x53,0x92,0x35,0xf7,0x8e,0x93,0xd2,0xbb,0x8c,0x69,0x76,0x39,0x41,0x8f,0xcf,0x85,0x30,0xe6,0xba,0xb4,0x9d,0x8,0xa5,0xa7,0xe,0xd4,0x9e,0x3d,0x21,0xad,0xd,0x56,0x84,0xa,0x7b,0xfd,0x65,0x62,0x63,0x81,0xca,0x50,0x75,0x5f,0x6c,0x57,0x2f,0xdb,0xf8,0x96,0xeb,0x43,0xc5,0xa1,0xf5,0x1c,0x48,0x99,0x2e,0x47,0x2a,0xd9,0x34,0x58,0xc0,0x10,0xef,0x15,0x9c,0x36,0x24,0xf,0x7d,0x2c,0xe3,0x13,0x45,0x4,0x60,0xfc,0x7,0xb,0x59,0x28,0xee,0x7a,0x95,0xfe,0xbe,0x3a,0x26,0x4c,0xdf,0x67,0x5,0x2b,0x2d,0xce,0x25,0x78,0xa2,0x22,0x6,0x90,0x74,0x88,0xb7,0xe4,0x76,0x9f,0x46,0x22,0xad,0xc4,0xcb,0x1a,0xac,0x58,0xef,0xd4,0x68,0xc0,0x7b,0x15,0xa7,0x8c,0x1f,0xb5,0x60,0x90,0xfe,0xaf,0xb7,0xdb,0xa9,0x5a,0x6c,0x96,0x43,0x93,0x16,0x7d,0x6d,0xf9,0xa5,0xcf,0x3d,0xb9,0xe3,0x7f,0xc6,0x87,0xda,0xab,0x84,0x88,0x85,0x13,0x21,0xa1,0x34,0x67,0xf7,0xb,0x86,0xa8,0x5c,0xe4,0xa6,0xfb,0xae,0x4d,0xe8,0x17,0x7c,0xb0,0x6b,0x6e,0x3,0x27,0xa3,0x83,0xce,0x75,0x81,0x91,0xd6,0x53,0x51,0x38,0xd,0x10,0xf5,0xba,0xf,0xea,0x92,0xcc,0x6f,0x59,0xb6,0x74,0xd0,0x11,0x26,0x24,0x1e,0x8b,0x1d,0xbe,0x8d,0x57,0x4c,0x6,0xc2,0xc,0x39,0x37,0xb3,0x65,0xe0,0x2,0xe6,0xe1,0xf6,0xdc,0x49,0xd3,0x8e,0xd5,0xa2,0x2e,0xf8,0x7e,0x7,0x89,0xfd,0xbf,0x78,0x54,0x5f,0x2f,0x48,0xc9,0x98,0x2c,0x6a,0xa,0xf0,0xe7,0x69,0x71,0x19,0x2a,0x64,0xbb,0x77,0x2d,0x95,0xd8,0x79,0xd2,0x99,0xbc,0xd9,0x12,0x5,0xf3,0xff,0x9a,0xc1,0xbd,0xd7,0xdf,0x62,0xd1,0xaa,0x29,0x0,0x56,0x66,0xca,0x3f,0x18,0x31,0x2b,0x4b,0x36,0x9d,0x72,0x50,0x41,0xe9,0x3c,0x61,0xfc,0x70,0xfa,0x1,0x33,0xe2,0x44,0xb4,0x1b,0x30,0x25,0x7a,0x4a,0x55,0x32,0x4e,0x63,0x45,0xa4,0xf4,0xdd,0xeb,0xb2,0xe,0x3b,0x97,0xee,0xcd,0xf1,0xc3,0x9b,0xf2,0x40,0xc5,0x42,0x82,0xde,0xa0,0xc8,0x47,0x73,0x5e,0x28,0x20,0x9c,0x94,0x3a,0x5b,0x80,0x1c,0xe5,0x4f,0xc7,0xed,0x8f,0xb8,0x9e,0x4,0x9,0xec,0x23,0x8a,0x14,0xb1,0x8,0x52,0x5d,0x35,0x3e,0xc4,0x6b,0x9b,0x3d,0x95,0xa5,0xfa,0xef,0xbc,0x91,0xed,0x8a,0x2,0x2b,0x7b,0x9a,0xe4,0xd1,0x6d,0x34,0x2e,0x12,0x31,0x48,0x9f,0x2d,0x44,0x1c,0x1,0x5d,0x9d,0x1a,0xac,0x98,0x17,0x7f,0x43,0xff,0xf7,0x81,0x5f,0x84,0xe5,0x4b,0x18,0x90,0x3a,0xc3,0x41,0x67,0x50,0x32,0xfc,0x33,0xd6,0xdb,0xd7,0x6e,0xcb,0x55,0xe1,0xea,0x82,0x8d,0x8b,0xa7,0x60,0x22,0x16,0x97,0xf0,0x80,0xd5,0xb5,0xf3,0x47,0xae,0xb6,0x38,0x2f,0x64,0xbb,0xf5,0xc6,0x7,0x4a,0xf2,0xa8,0x63,0x46,0xd,0xa6,0x2c,0xda,0xcd,0x6,0x62,0x1e,0x45,0x20,0xe,0xbd,0x0,0x8,0x89,0xdf,0xf6,0x75,0xc7,0xe0,0x15,0xb9,0xe9,0x94,0xf4,0xee,0x9e,0x8f,0xad,0x42,0x23,0xbe,0xe3,0x36,0xec,0xde,0x25,0xaf,0x6f,0xa3,0xc8,0x37,0xf8,0xdc,0xb1,0xb4,0xaa,0x11,0x5c,0x7c,0x8c,0x9,0x4e,0x5e,0xcf,0xd2,0xe7,0x8e,0x35,0xd0,0x65,0x2a,0x86,0xb0,0x13,0x4d,0xce,0xf,0xab,0x69,0x54,0xc1,0xfb,0xf9,0x88,0x52,0x61,0xc2,0xd3,0x1d,0xd9,0x93,0xba,0x6c,0xe8,0xe6,0x3e,0x39,0xdd,0x3f,0xc,0x96,0x3,0x29,0xf1,0x7d,0xa,0x51,0x56,0xd8,0xa1,0x27,0xfd,0x99,0x40,0xa9,0xc5,0x14,0x1b,0x72,0xb,0x30,0x87,0x73,0xca,0xa4,0x1f,0xb7,0x6a,0xc0,0x53,0x78,0x70,0x21,0x4f,0xbf,0x85,0x76,0x4,0x68,0x4c,0x9c,0x49,0xb3,0x26,0xb2,0xa2,0xc9,0x66,0xe2,0x10,0x7a,0x58,0x19,0xa0,0x3c,0x57,0x5b,0x74,0x5,0x7e,0xfe,0xcc,0x5a,0xd4,0x28,0xb8,0xeb,0x3b,0x83,0x77,0x59,0x92,0x71,0x24,0x79,0xf,0x3c,0x9e,0x41,0x8,0x52,0xfd,0xb0,0xf7,0x5c,0x99,0xbc,0x37,0xfc,0xd6,0x20,0x9a,0xd8,0x71,0x5d,0xa,0x7a,0xec,0x6d,0x9,0xbd,0x2f,0x4f,0xc2,0xd5,0x54,0x4c,0xe,0x14,0x13,0x6e,0x57,0xb8,0x64,0x75,0x19,0xcc,0xd9,0x44,0xdf,0x55,0x16,0x24,0xbf,0xda,0x98,0xe4,0xfa,0xf2,0xf4,0x47,0xc,0x8f,0x73,0x25,0xef,0x43,0x3d,0x1a,0x97,0xce,0x1e,0x2b,0xcb,0xb2,0xd4,0xe8,0xbe,0xe6,0x65,0xd7,0x67,0xe0,0xfb,0xa7,0x61,0xc7,0x3e,0x91,0x0,0x15,0x6f,0x5f,0x17,0x70,0x46,0x6b,0x81,0x60,0xf8,0xd1,0xaa,0xc8,0xbb,0x9d,0x2c,0x21,0x6,0xc9,0x31,0xaf,0x2d,0x94,0x78,0x77,0x1b,0x10,0xed,0x85,0x56,0x62,0xd,0x7b,0xb9,0x5,0x1f,0xb1,0xa5,0x7e,0xc0,0x39,0xe2,0x6a,0xa9,0x82,0x90,0x3a,0xb5,0x45,0x8a,0xdb,0xfe,0x92,0x7f,0x8c,0xb3,0x49,0xb6,0x66,0xba,0x53,0x7,0x63,0xe1,0x88,0x3f,0xee,0x7d,0x89,0xf1,0xca,0xe5,0x4d,0x30,0x5e,0x36,0xa0,0x84,0x4,0x42,0x11,0x2e,0xd2,0x8d,0xa3,0xc1,0x79,0xde,0x83,0x68,0x8b,0x58,0x33,0xdc,0x48,0xea,0x80,0x9c,0x18,0x5a,0xc6,0xa2,0xe3,0x8e,0xff,0xad,0xa1,0x1d,0x74,0x35,0x28,0x9f,0xd0,0xcf,0x2a,0xe9,0xb7,0x7c,0x4a,0x51,0x93,0x34,0xf5,0x32,0xcd,0x95,0x59,0x4b,0x4e,0x2,0x26,0xa6,0x86,0x50,0xeb,0xb4,0xa4,0x76,0xf3,0x27,0xc5,0xc4,0xc3,0xf9,0xd3,0xf6,0x6c,0xf0,0xab,0xb,0x87,0x5b,0xdd,0xac,0x22,0x1,0x3,0xae,0x3b,0x9b,0x38,0x72,0xa8,0x23,0x69,0x29,0xe7,0x12,0x1c,0x40,0x96,0xae,0x83,0xb5,0xd2,0x14,0x3d,0xa5,0x44,0x54,0xfb,0x2,0xa4,0x9a,0xaa,0xd0,0xc5,0x12,0xa0,0x23,0x7b,0x62,0x3e,0x25,0xa2,0xee,0xdb,0xb,0x52,0x2d,0x11,0x77,0xe,0xbb,0x60,0x74,0xda,0xaf,0x27,0xfc,0x5,0xa7,0x93,0x40,0x28,0xc0,0x7c,0xbe,0xc8,0x51,0xe8,0x6a,0xf4,0xd5,0xde,0xb2,0xbd,0x58,0x7e,0xd,0x6f,0xc,0xc3,0xe4,0xe9,0x8a,0xea,0x78,0xcc,0x89,0x91,0x10,0x7,0x98,0xb4,0x1d,0x5f,0xa8,0x29,0xbf,0xcf,0x79,0x5c,0x99,0x32,0xe5,0x13,0x39,0xf2,0x84,0x5b,0xf9,0xca,0x75,0x38,0x97,0xcd,0xe0,0xb6,0x4a,0xc9,0xdf,0xf8,0x86,0x2a,0x21,0x5d,0x1f,0x7a,0x82,0x31,0x37,0x3f,0x81,0x1c,0x9,0xdc,0xe1,0xd3,0x90,0x1a,0xab,0xd6,0xd1,0xcb,0xb0,0xa1,0x7d,0x92,0x2e,0x95,0x43,0x63,0x36,0xb3,0x61,0x71,0x9c,0x50,0x8,0xf7,0xe3,0xc7,0x8b,0x8e,0x8f,0xb9,0x72,0x2c,0x30,0xf1,0x56,0x94,0xed,0xf0,0xb1,0xd8,0xef,0xa,0x15,0x5a,0x22,0xec,0xac,0xe6,0x53,0x85,0xd9,0xd7,0xfe,0x6b,0xc6,0xc4,0x6d,0xb7,0xfd,0x5e,0x42,0xce,0x6e,0x35,0xe7,0x69,0x18,0x9e,0x6,0x1,0x0,0xe2,0xa9,0x33,0x16,0x3c,0xf,0x34,0x4c,0xb8,0x9b,0xf5,0x88,0x20,0xa6,0xc2,0x96,0x7f,0x2b,0xfa,0x4d,0x24,0x49,0xba,0x57,0x3b,0xa3,0x73,0x8c,0x76,0xff,0x55,0x47,0x6c,0x1e,0x4f,0x80,0x70,0x26,0x67,0x3,0x9f,0x64,0x68,0x3a,0x4b,0x8d,0x19,0xf6,0x9d,0xdd,0x59,0x45,0x2f,0xbc,0x4,0x66,0x48,0x4e,0xad,0x46,0x1b,0xc1,0x41,0x65,0xf3,0x17,0xeb,0xd4,0x87,0x11,0xf8,0x21,0x45,0xca,0xa3,0xac,0x7d,0xcb,0x3f,0x88,0xb3,0xf,0xa7,0x1c,0x72,0xc0,0xeb,0x78,0xd2,0x7,0xf7,0x99,0xc8,0xd0,0xbc,0xce,0x3d,0xb,0xf1,0x24,0xf4,0x71,0x1a,0xa,0x9e,0xc2,0xa8,0x5a,0xde,0x84,0x18,0xa1,0xe0,0xbd,0xcc,0xe3,0xef,0xe2,0x74,0x46,0xc6,0x53,0x0,0x90,0x6c,0xe1,0xcf,0x3b,0x83,0xc1,0x9c,0xc9,0x2a,0x8f,0x70,0x1b,0xd7,0xc,0x9,0x64,0x40,0xc4,0xe4,0xa9,0x12,0xe6,0xf6,0xb1,0x34,0x36,0x5f,0x6a,0x77,0x92,0xdd,0x68,0x8d,0xf5,0xab,0x8,0x3e,0xd1,0x13,0xb7,0x76,0x41,0x43,0x79,0xec,0x7a,0xd9,0xea,0x30,0x2b,0x61,0xa5,0x6b,0x5e,0x50,0xd4,0x2,0x87,0x65,0x81,0x86,0x91,0xbb,0x2e,0xb4,0xe9,0xb2,0xc5,0x49,0x9f,0x19,0x60,0xee,0x9a,0xd8,0x1f,0x33,0x38,0x48,0x2f,0xae,0xff,0x4b,0xd,0x6d,0x97,0x80,0xe,0x16,0x7e,0x4d,0x3,0xdc,0x10,0x4a,0xf2,0xbf,0x1e,0xb5,0xfe,0xdb,0xbe,0x75,0x62,0x94,0x98,0xfd,0xa6,0xda,0xb0,0xb8,0x5,0xb6,0xcd,0x4e,0x67,0x31,0x1,0xad,0x58,0x7f,0x56,0x4c,0x2c,0x51,0xfa,0x15,0x37,0x26,0x8e,0x5b,0x6,0x9b,0x17,0x9d,0x66,0x54,0x85,0x23,0xd3,0x7c,0x57,0x42,0x1d,0x2d,0x32,0x55,0x29,0x4,0x22,0xc3,0x93,0xba,0x8c,0xd5,0x69,0x5c,0xf0,0x89,0xaa,0x96,0xa4,0xfc,0x95,0x27,0xa2,0x25,0xe5,0xb9,0xc7,0xaf,0x20,0x14,0x39,0x4f,0x47,0xfb,0xf3,0x5d,0x3c,0xe7,0x7b,0x82,0x28,0xa0,0x8a,0xe8,0xdf,0xf9,0x63,0x6e,0x8b,0x44,0xed,0x73,0xd6,0x6f,0x35,0x3a,0x52,0x59,0x93,0x3c,0xcc,0x6a,0xc2,0xf2,0xad,0xb8,0xeb,0xc6,0xba,0xdd,0x55,0x7c,0x2c,0xcd,0xb3,0x86,0x3a,0x63,0x79,0x45,0x66,0x1f,0xc8,0x7a,0x13,0x4b,0x56,0xa,0xca,0x4d,0xfb,0xcf,0x40,0x28,0x14,0xa8,0xa0,0xd6,0x8,0xd3,0xb2,0x1c,0x4f,0xc7,0x6d,0x94,0x16,0x30,0x7,0x65,0xab,0x64,0x81,0x8c,0x80,0x39,0x9c,0x2,0xb6,0xbd,0xd5,0xda,0xdc,0xf0,0x37,0x75,0x41,0xc0,0xa7,0xd7,0x82,0xe2,0xa4,0x10,0xf9,0xe1,0x6f,0x78,0x33,0xec,0xa2,0x91,0x50,0x1d,0xa5,0xff,0x34,0x11,0x5a,0xf1,0x7b,0x8d,0x9a,0x51,0x35,0x49,0x12,0x77,0x59,0xea,0x57,0x5f,0xde,0x88,0xa1,0x22,0x90,0xb7,0x42,0xee,0xbe,0xc3,0xa3,0xb9,0xc9,0xd8,0xfa,0x15,0x74,0xe9,0xb4,0x61,0xbb,0x89,0x72,0xf8,0x38,0xf4,0x9f,0x60,0xaf,0x8b,0xe6,0xe3,0xfd,0x46,0xb,0x2b,0xdb,0x5e,0x19,0x9,0x98,0x85,0xb0,0xd9,0x62,0x87,0x32,0x7d,0xd1,0xe7,0x44,0x1a,0x99,0x58,0xfc,0x3e,0x3,0x96,0xac,0xae,0xdf,0x5,0x36,0x95,0x84,0x4a,0x8e,0xc4,0xed,0x3b,0xbf,0xb1,0x69,0x6e,0x8a,0x68,0x5b,0xc1,0x54,0x7e,0xa6,0x2a,0x5d,0x6,0x1,0x8f,0xf6,0x70,0xaa,0xce,0x17,0xfe,0x92,0x43,0x4c,0x25,0x5c,0x67,0xd0,0x24,0x9d,0xf3,0x48,0xe0,0x3d,0x97,0x4,0x2f,0x27,0x76,0x18,0xe8,0xd2,0x21,0x53,0x3f,0x1b,0xcb,0x1e,0xe4,0x71,0xe5,0xf5,0x9e,0x31,0xb5,0x47,0x2d,0xf,0x4e,0xf7,0x6b,0x0,0xc,0x23,0x52,0x29,0xa9,0x9b,0xd,0x83,0x7f,0xef,0xbc,0x6c,0xd4,0x20,0xe,0xc5,0x26,0x73,0x2e};
diff --git a/lib/playfair/playfair.c b/lib/playfair/playfair.c
new file mode 100644
index 0000000..782c17e
--- /dev/null
+++ b/lib/playfair/playfair.c
@@ -0,0 +1,31 @@
+#include
+
+#include "playfair.h"
+
+void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4]);
+void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey);
+void cycle(unsigned char* block, uint32_t key_schedule[11][4]);
+void z_xor(unsigned char* in, unsigned char* out, int blocks);
+void x_xor(unsigned char* in, unsigned char* out, int blocks);
+
+extern unsigned char default_sap[];
+
+void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut)
+{
+ unsigned char* chunk1 = &cipherText[16];
+ unsigned char* chunk2 = &cipherText[56];
+ int i;
+ unsigned char blockIn[16];
+ unsigned char sapKey[16];
+ uint32_t key_schedule[11][4];
+ generate_session_key(default_sap, message3, sapKey);
+ generate_key_schedule(sapKey, key_schedule);
+ z_xor(chunk2, blockIn, 1);
+ cycle(blockIn, key_schedule);
+ for (i = 0; i < 16; i++) {
+ keyOut[i] = blockIn[i] ^ chunk1[i];
+ }
+ x_xor(keyOut, keyOut, 1);
+ z_xor(keyOut, keyOut, 1);
+}
+
diff --git a/lib/playfair/playfair.h b/lib/playfair/playfair.h
new file mode 100644
index 0000000..5909a99
--- /dev/null
+++ b/lib/playfair/playfair.h
@@ -0,0 +1,6 @@
+#ifndef PLAYFAIR_H
+#define PLAYFAIR_H
+
+void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut);
+
+#endif
diff --git a/lib/playfair/sap_hash.c b/lib/playfair/sap_hash.c
new file mode 100644
index 0000000..9e7ae85
--- /dev/null
+++ b/lib/playfair/sap_hash.c
@@ -0,0 +1,96 @@
+#include
+#include
+#include
+
+#define printf(...) (void)0;
+
+void garble(unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned char*);
+
+unsigned char rol8(unsigned char input, int count)
+{
+ return ((input << count) & 0xff) | (input & 0xff) >> (8-count);
+}
+
+uint32_t rol8x(unsigned char input, int count)
+{
+ return ((input << count)) | (input) >> (8-count);
+}
+
+
+void sap_hash(unsigned char* blockIn, unsigned char* keyOut)
+{
+ uint32_t* block_words = (uint32_t*)blockIn;
+ uint32_t* out_words = (uint32_t*)keyOut;
+ unsigned char buffer0[20] = {0x96, 0x5F, 0xC6, 0x53, 0xF8, 0x46, 0xCC, 0x18, 0xDF, 0xBE, 0xB2, 0xF8, 0x38, 0xD7, 0xEC, 0x22, 0x03, 0xD1, 0x20, 0x8F};
+ unsigned char buffer1[210];
+ unsigned char buffer2[35] = {0x43, 0x54, 0x62, 0x7A, 0x18, 0xC3, 0xD6, 0xB3, 0x9A, 0x56, 0xF6, 0x1C, 0x14, 0x3F, 0x0C, 0x1D, 0x3B, 0x36, 0x83, 0xB1, 0x39, 0x51, 0x4A, 0xAA, 0x09, 0x3E, 0xFE, 0x44, 0xAF, 0xDE, 0xC3, 0x20, 0x9D, 0x42, 0x3A};
+ unsigned char buffer3[132];
+ unsigned char buffer4[21] = {0xED, 0x25, 0xD1, 0xBB, 0xBC, 0x27, 0x9F, 0x02, 0xA2, 0xA9, 0x11, 0x00, 0x0C, 0xB3, 0x52, 0xC0, 0xBD, 0xE3, 0x1B, 0x49, 0xC7};
+ int i0_index[11] = {18, 22, 23, 0, 5, 19, 32, 31, 10, 21, 30};
+ uint8_t w,x,y,z;
+ int i, j;
+
+ // Load the input into the buffer
+ for (i = 0; i < 210; i++)
+ {
+ // We need to swap the byte order around so it is the right endianness
+ uint32_t in_word = block_words[((i % 64)>>2)];
+ uint32_t in_byte = (in_word >> ((3-(i % 4)) << 3)) & 0xff;
+ buffer1[i] = in_byte;
+ }
+ // Next a scrambling
+ for (i = 0; i < 840; i++)
+ {
+ // We have to do unsigned, 32-bit modulo, or we get the wrong indices
+ x = buffer1[((i-155) & 0xffffffff) % 210];
+ y = buffer1[((i-57) & 0xffffffff) % 210];
+ z = buffer1[((i-13) & 0xffffffff) % 210];
+ w = buffer1[(i & 0xffffffff) % 210];
+ buffer1[i % 210] = (rol8(y, 5) + (rol8(z, 3) ^ w) - rol8(x,7)) & 0xff;
+ }
+ printf("Garbling...\n");
+ // I have no idea what this is doing (yet), but it gives the right output
+ garble(buffer0, buffer1, buffer2, buffer3, buffer4);
+
+ // Fill the output with 0xE1
+ for (i = 0; i < 16; i++)
+ keyOut[i] = 0xE1;
+
+ // Now we use all the buffers we have calculated to grind out the output. First buffer3
+ for (i = 0; i < 11; i++)
+ {
+ // Note that this is addition (mod 255) and not XOR
+ // Also note that we only use certain indices
+ // And that index 3 is hard-coded to be 0x3d (Maybe we can hack this up by changing buffer3[0] to be 0xdc?
+ if (i == 3)
+ keyOut[i] = 0x3d;
+ else
+ keyOut[i] = ((keyOut[i] + buffer3[i0_index[i] * 4]) & 0xff);
+ }
+
+ // Then buffer0
+ for (i = 0; i < 20; i++)
+ keyOut[i % 16] ^= buffer0[i];
+
+ // Then buffer2
+ for (i = 0; i < 35; i++)
+ keyOut[i % 16] ^= buffer2[i];
+
+ // Do buffer1
+ for (i = 0; i < 210; i++)
+ keyOut[(i % 16)] ^= buffer1[i];
+
+
+ // Now we do a kind of reverse-scramble
+ for (j = 0; j < 16; j++)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ x = keyOut[((i-7) & 0xffffffff) % 16];
+ y = keyOut[i % 16];
+ z = keyOut[((i-37) & 0xffffffff) % 16];
+ w = keyOut[((i-177) & 0xffffffff) % 16];
+ keyOut[i] = rol8(x, 1) ^ y ^ rol8(z, 6) ^ rol8(w, 5);
+ }
+ }
+}
diff --git a/lib/raop.c b/lib/raop.c
new file mode 100644
index 0000000..c275dc1
--- /dev/null
+++ b/lib/raop.c
@@ -0,0 +1,772 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *===================================================================
+ * modified by fduncanh 2021-23
+ */
+
+#include
+#include
+#include
+#include
+
+#include "raop.h"
+#include "raop_rtp.h"
+#include "pairing.h"
+#include "httpd.h"
+
+#include "global.h"
+#include "fairplay.h"
+#include "netutils.h"
+#include "logger.h"
+#include "compat.h"
+#include "raop_rtp_mirror.h"
+#include "raop_ntp.h"
+
+struct raop_s {
+ /* Callbacks for audio and video */
+ raop_callbacks_t callbacks;
+
+ /* Logger instance */
+ logger_t *logger;
+
+ /* Pairing, HTTP daemon and RSA key */
+ pairing_t *pairing;
+ httpd_t *httpd;
+
+ dnssd_t *dnssd;
+
+ /* local network ports */
+ unsigned short port;
+ unsigned short timing_lport;
+ unsigned short control_lport;
+ unsigned short data_lport;
+ unsigned short mirror_data_lport;
+
+ /* configurable plist items: width, height, refreshRate, maxFPS, overscanned *
+ * also clientFPSdata, which controls whether video stream info received *
+ * from the client is shown on terminal monitor. */
+ uint16_t width;
+ uint16_t height;
+ uint8_t refreshRate;
+ uint8_t maxFPS;
+ uint8_t overscanned;
+ uint8_t clientFPSdata;
+
+ int audio_delay_micros;
+ int max_ntp_timeouts;
+
+ /* for temporary storage of pin during pair-pin start */
+ unsigned short pin;
+ bool use_pin;
+
+ /* public key as string */
+ char pk_str[2*ED25519_KEY_SIZE + 1];
+
+ /* place to store media_data_store */
+ airplay_video_t *airplay_video;
+
+ /* activate support for HLS live streaming */
+ bool hls_support;
+};
+
+struct raop_conn_s {
+ raop_t *raop;
+ raop_ntp_t *raop_ntp;
+ raop_rtp_t *raop_rtp;
+ raop_rtp_mirror_t *raop_rtp_mirror;
+ fairplay_t *fairplay;
+ pairing_session_t *session;
+ airplay_video_t *airplay_video;
+
+ unsigned char *local;
+ int locallen;
+
+ unsigned char *remote;
+ int remotelen;
+
+ unsigned int zone_id;
+
+ connection_type_t connection_type;
+
+ char *client_session_id;
+
+ bool have_active_remote;
+};
+typedef struct raop_conn_s raop_conn_t;
+
+#include "raop_handlers.h"
+#include "http_handlers.h"
+
+static void *
+conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen, unsigned int zone_id) {
+ raop_t *raop = opaque;
+ raop_conn_t *conn;
+ char ip_address[40];
+ assert(raop);
+
+ conn = calloc(1, sizeof(raop_conn_t));
+ if (!conn) {
+ return NULL;
+ }
+ conn->raop = raop;
+ conn->raop_rtp = NULL;
+ conn->raop_rtp_mirror = NULL;
+ conn->raop_ntp = NULL;
+ conn->fairplay = fairplay_init(raop->logger);
+
+ if (!conn->fairplay) {
+ free(conn);
+ return NULL;
+ }
+ conn->session = pairing_session_init(raop->pairing);
+ if (!conn->session) {
+ fairplay_destroy(conn->fairplay);
+ free(conn);
+ return NULL;
+ }
+
+
+ utils_ipaddress_to_string(locallen, local, zone_id, ip_address, (int) sizeof(ip_address));
+ logger_log(conn->raop->logger, LOGGER_INFO, "Local : %s", ip_address);
+
+ utils_ipaddress_to_string(remotelen, remote, zone_id, ip_address, (int) sizeof(ip_address));
+ logger_log(conn->raop->logger, LOGGER_INFO, "Remote: %s", ip_address);
+
+ conn->local = malloc(locallen);
+ assert(conn->local);
+ memcpy(conn->local, local, locallen);
+
+ conn->remote = malloc(remotelen);
+ assert(conn->remote);
+ memcpy(conn->remote, remote, remotelen);
+
+ conn->zone_id = zone_id;
+
+ conn->locallen = locallen;
+ conn->remotelen = remotelen;
+
+ conn->connection_type = CONNECTION_TYPE_UNKNOWN;
+ conn->client_session_id = NULL;
+ conn->airplay_video = NULL;
+
+
+ conn->have_active_remote = false;
+
+ if (raop->callbacks.conn_init) {
+ raop->callbacks.conn_init(raop->callbacks.cls);
+ }
+
+ return conn;
+}
+
+static void
+conn_request(void *ptr, http_request_t *request, http_response_t **response) {
+ char *response_data = NULL;
+ int response_datalen = 0;
+ raop_conn_t *conn = ptr;
+ bool hls_request = false;
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
+ bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
+
+ /*
+ All requests arriving here have been parsed by llhttp to obtain
+ method | url | protocol (RTSP/1.0 or HTTP/1.1)
+
+ There are three types of connections supplying these requests:
+ Connections from the AirPlay client:
+ (1) type RAOP connections with CSeq seqence header, and no X-Apple-Session-ID header
+ (2) type AIRPLAY connection with an X-Apple-Sequence-ID header and no Cseq header
+ Connections from localhost:
+ (3) type HLS internal connections from the local HLS server (gstreamer) at localhost with neither
+ of these headers, but a Host: localhost:[port] header. method = GET.
+ */
+
+ const char *method = http_request_get_method(request);
+
+ if (!method) {
+ return;
+ }
+
+/* this rejects messages from _airplay._tcp for video streaming protocol unless bool raop->hls_support is true*/
+ const char *cseq = http_request_get_header(request, "CSeq");
+ const char *protocol = http_request_get_protocol(request);
+ if (!cseq && !conn->raop->hls_support) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "ignoring AirPlay video streaming request (use option -hls to activate HLS support)");
+ return;
+ }
+
+ const char *url = http_request_get_url(request);
+ const char *client_session_id = http_request_get_header(request, "X-Apple-Session-ID");
+ const char *host = http_request_get_header(request, "Host");
+ hls_request = (host && !cseq && !client_session_id);
+
+ if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) {
+ if (cseq) {
+ if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) {
+ char ipaddr[40];
+ utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr)));
+ if (httpd_nohold(conn->raop->httpd)) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "\"nohold\" feature: switch to new connection request from %s", ipaddr);
+ if (conn->raop->callbacks.video_reset) {
+ conn->raop->callbacks.video_reset(conn->raop->callbacks.cls);
+ }
+ httpd_remove_known_connections(conn->raop->httpd);
+ } else {
+ logger_log(conn->raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr);
+ *response = http_response_create();
+ http_response_init(*response, protocol, 409, "Conflict: Server is connected to another client");
+ goto finish;
+ }
+ }
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type RAOP", ptr);
+ httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_RAOP);
+ conn->connection_type = CONNECTION_TYPE_RAOP;
+ } else if (client_session_id) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type AirPlay", ptr);
+ httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_AIRPLAY);
+ conn->connection_type = CONNECTION_TYPE_AIRPLAY;
+ size_t len = strlen(client_session_id) + 1;
+ conn->client_session_id = (char *) malloc(len);
+ strncpy(conn->client_session_id, client_session_id, len);
+ /* airplay video has been requested: shut down any running RAOP udp services */
+ raop_conn_t *raop_conn = (raop_conn_t *) httpd_get_connection_by_type(conn->raop->httpd, CONNECTION_TYPE_RAOP, 1);
+ if (raop_conn) {
+ raop_rtp_mirror_t *raop_rtp_mirror = raop_conn->raop_rtp_mirror;
+ if (raop_rtp_mirror) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP mirror"
+ " service on RAOP connection %p", raop_conn);
+ raop_rtp_mirror_stop(raop_rtp_mirror);
+ }
+
+ raop_rtp_t *raop_rtp = raop_conn->raop_rtp;
+ if (raop_rtp) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP audio"
+ " service on RAOP connection %p", raop_conn);
+ raop_rtp_stop(raop_rtp);
+ }
+
+ raop_ntp_t *raop_ntp = raop_conn->raop_ntp;
+ if (raop_rtp) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping NTP time"
+ " service on RAOP connection %p", raop_conn);
+ raop_ntp_stop(raop_ntp);
+ }
+ }
+ } else if (host) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type HLS", ptr);
+ httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_HLS);
+ conn->connection_type = CONNECTION_TYPE_HLS;
+ } else {
+ logger_log(conn->raop->logger, LOGGER_WARNING, "connection from unknown connection type");
+ }
+ }
+
+ /* this response code and message will be modified by the handler if necessary */
+ *response = http_response_create();
+ http_response_init(*response, protocol, 200, "OK");
+
+ /* is this really necessary? or is it obsolete? (added for all RTSP requests EXCEPT "RECORD") */
+ if (cseq && strcmp(method, "RECORD")) {
+ http_response_add_header(*response, "Audio-Jack-Status", "connected; type=digital");
+ }
+
+ if (!conn->have_active_remote) {
+ const char *active_remote = http_request_get_header(request, "Active-Remote");
+ if (active_remote) {
+ conn->have_active_remote = true;
+ if (conn->raop->callbacks.export_dacp) {
+ const char *dacp_id = http_request_get_header(request, "DACP-ID");
+ conn->raop->callbacks.export_dacp(conn->raop->callbacks.cls, active_remote, dacp_id);
+ }
+ }
+ }
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s %s", method, url, protocol);
+ char *header_str= NULL;
+ http_request_get_header_string(request, &header_str);
+ if (header_str) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", header_str);
+ bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
+ bool data_is_text = (strstr(header_str,"text/") != NULL);
+ free(header_str);
+ int request_datalen;
+ const char *request_data = http_request_get_data(request, &request_datalen);
+ if (request_data && logger_debug) {
+ if (request_datalen > 0) {
+ /* logger has a buffer limit of 4096 */
+ if (data_is_plist) {
+ plist_t req_root_node = NULL;
+ plist_from_bin(request_data, request_datalen, &req_root_node);
+ char * plist_xml;
+ uint32_t plist_len;
+ plist_to_xml(req_root_node, &plist_xml, &plist_len);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml);
+ free(plist_xml);
+ plist_free(req_root_node);
+ } else if (data_is_text) {
+ char *data_str = utils_data_to_text((char *) request_data, request_datalen);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
+ free(data_str);
+ } else {
+ char *data_str = utils_data_to_string((unsigned char *) request_data, request_datalen, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
+ free(data_str);
+ }
+ }
+ }
+ }
+
+ if (client_session_id) {
+ assert(!strcmp(client_session_id, conn->client_session_id));
+ }
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url);
+ raop_handler_t handler = NULL;
+ if (!hls_request && !strcmp(protocol, "RTSP/1.0")) {
+ if (!strcmp(method, "POST")) {
+ if (!strcmp(url, "/feedback")) {
+ handler = &raop_handler_feedback;
+ } else if (!strcmp(url, "/pair-pin-start")) {
+ handler = &raop_handler_pairpinstart;
+ } else if (!strcmp(url, "/pair-setup-pin")) {
+ handler = &raop_handler_pairsetup_pin;
+ } else if (!strcmp(url, "/pair-setup")) {
+ handler = &raop_handler_pairsetup;
+ } else if (!strcmp(url, "/pair-verify")) {
+ handler = &raop_handler_pairverify;
+ } else if (!strcmp(url, "/fp-setup")) {
+ handler = &raop_handler_fpsetup;
+ } else if (!strcmp(url, "/getProperty")) {
+ handler = &http_handler_get_property;
+ } else if (!strcmp(url, "/audioMode")) {
+ //handler = &http_handler_audioMode;
+ }
+ } else if (!strcmp(method, "GET")) {
+ if (!strcmp(url, "/info")) {
+ handler = &raop_handler_info;
+ }
+ } else if (!strcmp(method, "OPTIONS")) {
+ handler = &raop_handler_options;
+ } else if (!strcmp(method, "SETUP")) {
+ handler = &raop_handler_setup;
+ } else if (!strcmp(method, "GET_PARAMETER")) {
+ handler = &raop_handler_get_parameter;
+ } else if (!strcmp(method, "SET_PARAMETER")) {
+ handler = &raop_handler_set_parameter;
+ } else if (!strcmp(method, "RECORD")) {
+ handler = &raop_handler_record;
+ } else if (!strcmp(method, "FLUSH")) {
+ handler = &raop_handler_flush;
+ } else if (!strcmp(method, "TEARDOWN")) {
+ handler = &raop_handler_teardown;
+ }
+ } else if (!hls_request && !strcmp(protocol, "HTTP/1.1")) {
+ if (!strcmp(method, "POST")) {
+ if (!strcmp(url, "/reverse")) {
+ handler = &http_handler_reverse;
+ } else if (!strcmp(url, "/play")) {
+ handler = &http_handler_play;
+ } else if (!strncmp (url, "/getProperty?", strlen("/getProperty?"))) {
+ handler = &http_handler_get_property;
+ } else if (!strncmp(url, "/scrub?", strlen("/scrub?"))) {
+ handler = &http_handler_scrub;
+ } else if (!strncmp(url, "/rate?", strlen("/rate?"))) {
+ handler = &http_handler_rate;
+ } else if (!strcmp(url, "/stop")) {
+ handler = &http_handler_stop;
+ } else if (!strcmp(url, "/action")) {
+ handler = &http_handler_action;
+ } else if (!strcmp(url, "/fp-setup2")) {
+ handler = &http_handler_fpsetup2;
+ }
+ } else if (!strcmp(method, "GET")) {
+ if (!strcmp(url, "/server-info")) {
+ handler = &http_handler_server_info;
+ } else if (!strcmp(url, "/playback-info")) {
+ handler = &http_handler_playback_info;
+ }
+ } else if (!strcmp(method, "PUT")) {
+ if (!strncmp (url, "/setProperty?", strlen("/setProperty?"))) {
+ handler = &http_handler_set_property;
+ } else {
+ }
+ }
+ } else if (hls_request) {
+ handler = &http_handler_hls;
+ }
+
+ if (handler != NULL) {
+ handler(conn, request, *response, &response_data, &response_datalen);
+ } else {
+ logger_log(conn->raop->logger, LOGGER_INFO,
+ "Unhandled Client Request: %s %s %s", method, url, protocol);
+ }
+
+ finish:;
+ if (!hls_request) {
+ http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION);
+ if (cseq) {
+ http_response_add_header(*response, "CSeq", cseq);
+ }
+ }
+ http_response_finish(*response, response_data, response_datalen);
+
+ int len;
+ const char *data = http_response_get_data(*response, &len);
+ if (response_data && response_datalen > 0) {
+ len -= response_datalen;
+ } else {
+ len -= 2;
+ }
+ header_str = utils_data_to_text(data, len);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s", header_str);
+
+ bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
+ bool data_is_text = (strstr(header_str,"text/") != NULL ||
+ strstr(header_str, "x-mpegURL") != NULL);
+ free(header_str);
+ if (response_data) {
+ if (response_datalen > 0 && logger_debug) {
+ /* logger has a buffer limit of 4096 */
+ if (data_is_plist) {
+ plist_t res_root_node = NULL;
+ plist_from_bin(response_data, response_datalen, &res_root_node);
+ char * plist_xml;
+ uint32_t plist_len;
+ plist_to_xml(res_root_node, &plist_xml, &plist_len);
+ plist_free(res_root_node);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml);
+ free(plist_xml);
+ } else if (data_is_text) {
+ char *data_str = utils_data_to_text((char*) response_data, response_datalen);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
+ free(data_str);
+ } else {
+ char *data_str = utils_data_to_string((unsigned char *) response_data, response_datalen, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
+ free(data_str);
+ }
+ }
+ if (response_data) {
+ free(response_data);
+ }
+ }
+}
+
+static void
+conn_destroy(void *ptr) {
+ raop_conn_t *conn = ptr;
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Destroying connection");
+
+ if (conn->raop->callbacks.conn_destroy) {
+ conn->raop->callbacks.conn_destroy(conn->raop->callbacks.cls);
+ }
+
+ if (conn->raop_rtp) {
+ /* This is done in case TEARDOWN was not called */
+ raop_rtp_destroy(conn->raop_rtp);
+ }
+ if (conn->raop_rtp_mirror) {
+ /* This is done in case TEARDOWN was not called */
+ raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
+ }
+ if (conn->raop_ntp) {
+ raop_ntp_destroy(conn->raop_ntp);
+ }
+
+ if (conn->raop->callbacks.video_flush) {
+ conn->raop->callbacks.video_flush(conn->raop->callbacks.cls);
+ }
+
+ free(conn->local);
+ free(conn->remote);
+ pairing_session_destroy(conn->session);
+ fairplay_destroy(conn->fairplay);
+ if (conn->client_session_id) {
+ free(conn->client_session_id);
+ }
+ if (conn->airplay_video) {
+ airplay_video_service_destroy(conn->airplay_video);
+ }
+
+ free(conn);
+}
+
+raop_t *
+raop_init(raop_callbacks_t *callbacks) {
+ raop_t *raop;
+
+ assert(callbacks);
+
+ /* Initialize the network */
+ if (netutils_init() < 0) {
+ return NULL;
+ }
+
+ /* Validate the callbacks structure */
+ if (!callbacks->audio_process ||
+ !callbacks->video_process) {
+ return NULL;
+ }
+
+ /* Allocate the raop_t structure */
+ raop = calloc(1, sizeof(raop_t));
+ if (!raop) {
+ return NULL;
+ }
+
+ /* Initialize the logger */
+ raop->logger = logger_init();
+
+ /* Copy callbacks structure */
+ memcpy(&raop->callbacks, callbacks, sizeof(raop_callbacks_t));
+
+ /* initialize network port list */
+ raop->port = 0;
+ raop->timing_lport = 0;
+ raop->control_lport = 0;
+ raop->data_lport = 0;
+ raop->mirror_data_lport = 0;
+
+ /* initialize configurable plist parameters */
+ raop->width = 1920;
+ raop->height = 1080;
+ raop->refreshRate = 60;
+ raop->maxFPS = 30;
+ raop->overscanned = 0;
+
+ /* initialise stored pin */
+ raop->pin = 0;
+ raop->use_pin = false;
+
+ /* initialize switch for display of client's streaming data records */
+ raop->clientFPSdata = 0;
+
+ raop->max_ntp_timeouts = 0;
+ raop->audio_delay_micros = 250000;
+
+ raop->hls_support = false;
+
+ return raop;
+}
+
+int
+raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile) {
+ pairing_t *pairing;
+ httpd_t *httpd;
+ httpd_callbacks_t httpd_cbs;
+
+ /* create a new public key for pairing */
+ int new_key;
+ pairing = pairing_init_generate(device_id, keyfile, &new_key);
+ if (!pairing) {
+ logger_log(raop->logger, LOGGER_ERR, "failed to create new public key for pairing");
+ return -1;
+ }
+ /* store PK as a string in raop->pk_str */
+ memset(raop->pk_str, 0, sizeof(raop->pk_str));
+#ifdef PK
+ strncpy(raop->pk_str, PK, 2*ED25519_KEY_SIZE);
+#else
+ unsigned char public_key[ED25519_KEY_SIZE];
+ pairing_get_public_key(pairing, public_key);
+ char *pk_str = utils_pk_to_string(public_key, ED25519_KEY_SIZE);
+ strncpy(raop->pk_str, (const char *) pk_str, 2*ED25519_KEY_SIZE);
+ free(pk_str);
+#endif
+ if (new_key) {
+ logger_log(raop->logger, LOGGER_INFO,"*** A new Public Key has been created and stored in %s", keyfile);
+ }
+
+ /* Set HTTP callbacks to our handlers */
+ memset(&httpd_cbs, 0, sizeof(httpd_cbs));
+ httpd_cbs.opaque = raop;
+ httpd_cbs.conn_init = &conn_init;
+ httpd_cbs.conn_request = &conn_request;
+ httpd_cbs.conn_destroy = &conn_destroy;
+
+ /* Initialize the http daemon */
+ httpd = httpd_init(raop->logger, &httpd_cbs, nohold);
+ if (!httpd) {
+ logger_log(raop->logger, LOGGER_ERR, "failed to initialize http daemon");
+ pairing_destroy(pairing);
+ return -1;
+ }
+
+ raop->pairing = pairing;
+ raop->httpd = httpd;
+ return 0;
+}
+
+void
+raop_destroy(raop_t *raop) {
+ if (raop) {
+ raop_destroy_airplay_video(raop);
+ raop_stop(raop);
+ pairing_destroy(raop->pairing);
+ httpd_destroy(raop->httpd);
+ logger_destroy(raop->logger);
+ free(raop);
+
+ /* Cleanup the network */
+ netutils_cleanup();
+ }
+}
+
+int
+raop_is_running(raop_t *raop) {
+ assert(raop);
+
+ return httpd_is_running(raop->httpd);
+}
+
+void
+raop_set_log_level(raop_t *raop, int level) {
+ assert(raop);
+
+ logger_set_level(raop->logger, level);
+}
+
+int raop_set_plist(raop_t *raop, const char *plist_item, const int value) {
+ int retval = 0;
+ assert(raop);
+ assert(plist_item);
+
+ if (strcmp(plist_item, "width") == 0) {
+ raop->width = (uint16_t) value;
+ if ((int) raop->width != value) retval = 1;
+ } else if (strcmp(plist_item, "height") == 0) {
+ raop->height = (uint16_t) value;
+ if ((int) raop->height != value) retval = 1;
+ } else if (strcmp(plist_item, "refreshRate") == 0) {
+ raop->refreshRate = (uint8_t) value;
+ if ((int) raop->refreshRate != value) retval = 1;
+ } else if (strcmp(plist_item, "maxFPS") == 0) {
+ raop->maxFPS = (uint8_t) value;
+ if ((int) raop->maxFPS != value) retval = 1;
+ } else if (strcmp(plist_item, "overscanned") == 0) {
+ raop->overscanned = (uint8_t) (value ? 1 : 0);
+ if ((int) raop->overscanned != value) retval = 1;
+ } else if (strcmp(plist_item, "clientFPSdata") == 0) {
+ raop->clientFPSdata = (value ? 1 : 0);
+ if ((int) raop->clientFPSdata != value) retval = 1;
+ } else if (strcmp(plist_item, "max_ntp_timeouts") == 0) {
+ raop->max_ntp_timeouts = (value > 0 ? value : 0);
+ if (raop->max_ntp_timeouts != value) retval = 1;
+ } else if (strcmp(plist_item, "audio_delay_micros") == 0) {
+ if (value >= 0 && value <= 10 * SECOND_IN_USECS) {
+ raop->audio_delay_micros = value;
+ }
+ if (raop->audio_delay_micros != value) retval = 1;
+ } else if (strcmp(plist_item, "pin") == 0) {
+ raop->pin = value;
+ raop->use_pin = true;
+ } else if (strcmp(plist_item, "hls") == 0) {
+ raop->hls_support = (value > 0 ? true : false);
+ } else {
+ retval = -1;
+ }
+ return retval;
+}
+
+void
+raop_set_port(raop_t *raop, unsigned short port) {
+ assert(raop);
+ raop->port = port;
+}
+
+void
+raop_set_udp_ports(raop_t *raop, unsigned short udp[3]) {
+ assert(raop);
+ raop->timing_lport = udp[0];
+ raop->control_lport = udp[1];
+ raop->data_lport = udp[2];
+}
+
+void
+raop_set_tcp_ports(raop_t *raop, unsigned short tcp[2]) {
+ assert(raop);
+ raop->mirror_data_lport = tcp[0];
+ raop->port = tcp[1];
+}
+
+unsigned short
+raop_get_port(raop_t *raop) {
+ assert(raop);
+ return raop->port;
+}
+
+void *
+raop_get_callback_cls(raop_t *raop) {
+ assert(raop);
+ return raop->callbacks.cls;
+}
+
+void
+raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls) {
+ assert(raop);
+
+ logger_set_callback(raop->logger, callback, cls);
+}
+
+void
+raop_set_dnssd(raop_t *raop, dnssd_t *dnssd) {
+ assert(dnssd);
+ dnssd_set_pk(dnssd, raop->pk_str);
+ raop->dnssd = dnssd;
+}
+
+
+int
+raop_start(raop_t *raop, unsigned short *port) {
+ assert(raop);
+ assert(port);
+ return httpd_start(raop->httpd, port);
+}
+
+void
+raop_stop(raop_t *raop) {
+ assert(raop);
+ httpd_stop(raop->httpd);
+}
+
+void raop_remove_known_connections(raop_t * raop) {
+ httpd_remove_known_connections(raop->httpd);
+}
+
+airplay_video_t *deregister_airplay_video(raop_t *raop) {
+ airplay_video_t *airplay_video = raop->airplay_video;
+ raop->airplay_video = NULL;
+ return airplay_video;
+}
+
+bool register_airplay_video(raop_t *raop, airplay_video_t *airplay_video) {
+ if (raop->airplay_video) {
+ return false;
+ }
+ raop->airplay_video = airplay_video;
+ return true;
+}
+
+airplay_video_t * get_airplay_video(raop_t *raop) {
+ return raop->airplay_video;
+}
+
+void raop_destroy_airplay_video(raop_t *raop) {
+ if (raop->airplay_video) {
+ airplay_video_service_destroy(raop->airplay_video);
+ raop->airplay_video = NULL;
+ }
+}
diff --git a/lib/raop.h b/lib/raop.h
new file mode 100644
index 0000000..e0a2062
--- /dev/null
+++ b/lib/raop.h
@@ -0,0 +1,132 @@
+/**
+ * Copyright (C) 2012-2015 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *===================================================================
+ * modified by fduncanh 2021-23
+ */
+
+#ifndef RAOP_H
+#define RAOP_H
+
+#include "dnssd.h"
+#include "stream.h"
+#include "raop_ntp.h"
+#include "airplay_video.h"
+
+#if defined (WIN32) && defined(DLL_EXPORT)
+# define RAOP_API __declspec(dllexport)
+#else
+# define RAOP_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct raop_s raop_t;
+
+typedef void (*raop_log_callback_t)(void *cls, int level, const char *msg);
+
+
+typedef struct playback_info_s {
+ //char * uuid;
+ uint32_t stallcount;
+ double duration;
+ double position;
+ float rate;
+ bool ready_to_play;
+ bool playback_buffer_empty;
+ bool playback_buffer_full;
+ bool playback_likely_to_keep_up;
+ int num_loaded_time_ranges;
+ int num_seekable_time_ranges;
+ void *loadedTimeRanges;
+ void *seekableTimeRanges;
+} playback_info_t;
+
+typedef enum video_codec_e {
+ VIDEO_CODEC_UNKNOWN,
+ VIDEO_CODEC_H264,
+ VIDEO_CODEC_H265
+} video_codec_t;
+
+struct raop_callbacks_s {
+ void* cls;
+
+ void (*audio_process)(void *cls, raop_ntp_t *ntp, audio_decode_struct *data);
+ void (*video_process)(void *cls, raop_ntp_t *ntp, video_decode_struct *data);
+ void (*video_pause)(void *cls);
+ void (*video_resume)(void *cls);
+
+ /* Optional but recommended callback functions */
+ void (*conn_init)(void *cls);
+ void (*conn_destroy)(void *cls);
+ void (*conn_reset) (void *cls, int timeouts, bool reset_video);
+ void (*conn_teardown)(void *cls, bool *teardown_96, bool *teardown_110 );
+ void (*audio_flush)(void *cls);
+ void (*video_flush)(void *cls);
+ void (*audio_set_volume)(void *cls, float volume);
+ void (*audio_set_metadata)(void *cls, const void *buffer, int buflen);
+ void (*audio_set_coverart)(void *cls, const void *buffer, int buflen);
+ void (*audio_remote_control_id)(void *cls, const char *dacp_id, const char *active_remote_header);
+ void (*audio_set_progress)(void *cls, unsigned int start, unsigned int curr, unsigned int end);
+ void (*audio_get_format)(void *cls, unsigned char *ct, unsigned short *spf, bool *usingScreen, bool *isMedia, uint64_t *audioFormat);
+ void (*video_report_size)(void *cls, float *width_source, float *height_source, float *width, float *height);
+ void (*report_client_request) (void *cls, char *deviceid, char *model, char *name, bool *admit);
+ void (*display_pin) (void *cls, char * pin);
+ void (*register_client) (void *cls, const char *device_id, const char *pk_str, const char *name);
+ bool (*check_register) (void *cls, const char *pk_str);
+ void (*export_dacp) (void *cls, const char *active_remote, const char *dacp_id);
+ void (*video_reset) (void *cls);
+ void (*video_set_codec)(void *cls, video_codec_t codec);
+ /* for HLS video player controls */
+ void (*on_video_play) (void *cls, const char *location, const float start_position);
+ void (*on_video_scrub) (void *cls, const float position);
+ void (*on_video_rate) (void *cls, const float rate);
+ void (*on_video_stop) (void *cls);
+ void (*on_video_acquire_playback_info) (void *cls, playback_info_t *playback_video);
+
+};
+typedef struct raop_callbacks_s raop_callbacks_t;
+raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote,
+ int remote_addr_len, unsigned short timing_rport,
+ timing_protocol_t *time_protocol);
+
+int airplay_video_service_init(raop_t *raop, unsigned short port, const char *session_id);
+
+bool register_airplay_video(raop_t *raop, airplay_video_t *airplay_video);
+airplay_video_t *get_airplay_video(raop_t *raop);
+airplay_video_t *deregister_airplay_video(raop_t *raop);
+
+RAOP_API raop_t *raop_init(raop_callbacks_t *callbacks);
+RAOP_API int raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile);
+RAOP_API void raop_set_log_level(raop_t *raop, int level);
+RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls);
+RAOP_API int raop_set_plist(raop_t *raop, const char *plist_item, const int value);
+RAOP_API void raop_set_port(raop_t *raop, unsigned short port);
+RAOP_API void raop_set_udp_ports(raop_t *raop, unsigned short port[3]);
+RAOP_API void raop_set_tcp_ports(raop_t *raop, unsigned short port[2]);
+RAOP_API unsigned short raop_get_port(raop_t *raop);
+RAOP_API void *raop_get_callback_cls(raop_t *raop);
+RAOP_API int raop_start(raop_t *raop, unsigned short *port);
+RAOP_API int raop_is_running(raop_t *raop);
+RAOP_API void raop_stop(raop_t *raop);
+RAOP_API void raop_set_dnssd(raop_t *raop, dnssd_t *dnssd);
+RAOP_API void raop_destroy(raop_t *raop);
+RAOP_API void raop_remove_known_connections(raop_t * raop);
+RAOP_API void raop_destroy_airplay_video(raop_t *raop);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/raop_buffer.c b/lib/raop_buffer.c
new file mode 100644
index 0000000..4fc8d76
--- /dev/null
+++ b/lib/raop_buffer.c
@@ -0,0 +1,311 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2021-2023
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "raop_buffer.h"
+#include "raop_rtp.h"
+
+#include "crypto.h"
+#include "compat.h"
+#include "stream.h"
+#include "global.h"
+#include "utils.h"
+#include "byteutils.h"
+
+#define RAOP_BUFFER_LENGTH 32
+
+typedef struct {
+ /* Data available */
+ int filled;
+
+ /* RTP header */
+ unsigned short seqnum;
+ uint64_t rtp_timestamp;
+ uint64_t ntp_timestamp;
+
+ /* Payload data */
+ unsigned int payload_size;
+ void *payload_data;
+} raop_buffer_entry_t;
+
+struct raop_buffer_s {
+ logger_t *logger;
+ /* AES CTX used for decryption */
+ aes_ctx_t *aes_ctx;
+
+ /* First and last seqnum */
+ int is_empty;
+ unsigned short first_seqnum;
+ unsigned short last_seqnum;
+
+ /* RTP buffer entries */
+ raop_buffer_entry_t entries[RAOP_BUFFER_LENGTH];
+};
+
+raop_buffer_t *
+raop_buffer_init(logger_t *logger,
+ const unsigned char *aeskey,
+ const unsigned char *aesiv)
+{
+ raop_buffer_t *raop_buffer;
+ assert(aeskey);
+ assert(aesiv);
+ raop_buffer = calloc(1, sizeof(raop_buffer_t));
+ if (!raop_buffer) {
+ return NULL;
+ }
+ raop_buffer->logger = logger;
+ // Need to be initialized internally
+ raop_buffer->aes_ctx = aes_cbc_init(aeskey, aesiv, AES_DECRYPT);
+
+ for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
+ raop_buffer_entry_t *entry = &raop_buffer->entries[i];
+ entry->payload_data = NULL;
+ entry->payload_size = 0;
+ }
+
+ raop_buffer->is_empty = 1;
+
+ return raop_buffer;
+}
+
+void
+raop_buffer_destroy(raop_buffer_t *raop_buffer)
+{
+ for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
+ raop_buffer_entry_t *entry = &raop_buffer->entries[i];
+ if (entry->payload_data != NULL) {
+ free(entry->payload_data);
+ }
+ }
+
+ if (raop_buffer) {
+ aes_cbc_destroy(raop_buffer->aes_ctx);
+ free(raop_buffer);
+ }
+
+}
+
+static short
+seqnum_cmp(unsigned short s1, unsigned short s2)
+{
+ return (s1 - s2);
+}
+
+int
+raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output, unsigned int payload_size, unsigned int *outputlen)
+{
+ assert(raop_buffer);
+ int encryptedlen;
+ if (DECRYPTION_TEST) {
+ char *str = utils_data_to_string(data,12,12);
+ logger_log(raop_buffer->logger, LOGGER_INFO, "encrypted 12 byte header %s", str);
+ free(str);
+ if (payload_size) {
+ str = utils_data_to_string(&data[12],16,16);
+ logger_log(raop_buffer->logger, LOGGER_INFO, "len %d before decryption:\n%s", payload_size, str);
+ free(str);
+ }
+ }
+ encryptedlen = payload_size / 16*16;
+ memset(output, 0, payload_size);
+
+ aes_cbc_decrypt(raop_buffer->aes_ctx, &data[12], output, encryptedlen);
+ aes_cbc_reset(raop_buffer->aes_ctx);
+
+ memcpy(output + encryptedlen, &data[12 + encryptedlen], payload_size - encryptedlen);
+ *outputlen = payload_size;
+ if (payload_size && DECRYPTION_TEST){
+ switch (output[0]) {
+ case 0x8c:
+ case 0x8d:
+ case 0x8e:
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x20:
+ break;
+ default:
+ logger_log(raop_buffer->logger, LOGGER_INFO, "***ERROR AUDIO FRAME IS NOT AAC_ELD OR ALAC");
+ break;
+ }
+ if (DECRYPTION_TEST == 2) {
+ logger_log(raop_buffer->logger, LOGGER_INFO, "decrypted audio frame, len = %d", *outputlen);
+ char *str = utils_data_to_string(output,payload_size,16);
+ logger_log(raop_buffer->logger, LOGGER_INFO,"%s",str);
+ free(str);
+ } else {
+ char *str = utils_data_to_string(output,16,16);
+ logger_log(raop_buffer->logger, LOGGER_INFO, "%d after \n%s", payload_size, str);
+ free(str);
+ }
+ }
+ return 1;
+}
+
+int
+raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, int use_seqnum) {
+ unsigned char empty_packet_marker[] = { 0x00, 0x68, 0x34, 0x00 };
+ assert(raop_buffer);
+
+ /* Check packet data length is valid */
+ if (datalen < 12 || datalen > RAOP_PACKET_LEN) {
+ return -1;
+ }
+ /* before time is synchronized, some empty data packets are sent */
+ if (datalen == 16 && !memcmp(&data[12], empty_packet_marker, 4)) {
+ return 0;
+ }
+ int payload_size = datalen - 12;
+
+ /* Get correct seqnum for the packet */
+ unsigned short seqnum;
+ if (use_seqnum) {
+ seqnum = byteutils_get_short_be(data, 2);
+ } else {
+ seqnum = raop_buffer->first_seqnum;
+ }
+
+ /* If this packet is too late, just skip it */
+ if (!raop_buffer->is_empty && seqnum_cmp(seqnum, raop_buffer->first_seqnum) < 0) {
+ return 0;
+ }
+
+ /* Check that there is always space in the buffer, otherwise flush */
+ if (seqnum_cmp(seqnum, raop_buffer->first_seqnum + RAOP_BUFFER_LENGTH) >= 0) {
+ raop_buffer_flush(raop_buffer, seqnum);
+ }
+
+ /* Get entry corresponding our seqnum */
+ raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH];
+ if (entry->filled && seqnum_cmp(entry->seqnum, seqnum) == 0) {
+ /* Packet resend, we can safely ignore */
+ return 0;
+ }
+
+ /* Update the raop_buffer entry header */
+ entry->seqnum = seqnum;
+ entry->rtp_timestamp = *rtp_timestamp;
+ entry->ntp_timestamp = *ntp_timestamp;
+ entry->filled = 1;
+
+ entry->payload_data = malloc(payload_size);
+ int decrypt_ret = raop_buffer_decrypt(raop_buffer, data, entry->payload_data, payload_size, &entry->payload_size);
+ assert(decrypt_ret >= 0);
+ assert(entry->payload_size <= payload_size);
+
+ /* Update the raop_buffer seqnums */
+ if (raop_buffer->is_empty) {
+ raop_buffer->first_seqnum = seqnum;
+ raop_buffer->last_seqnum = seqnum;
+ raop_buffer->is_empty = 0;
+ }
+ if (seqnum_cmp(seqnum, raop_buffer->last_seqnum) > 0) {
+ raop_buffer->last_seqnum = seqnum;
+ }
+ return 1;
+}
+
+void *
+raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, unsigned short *seqnum, int no_resend) {
+ assert(raop_buffer);
+
+ /* Calculate number of entries in the current buffer */
+ short entry_count = seqnum_cmp(raop_buffer->last_seqnum, raop_buffer->first_seqnum)+1;
+
+ /* Cannot dequeue from empty buffer */
+ if (raop_buffer->is_empty || entry_count <= 0) {
+ return NULL;
+ }
+
+ /* Get the first buffer entry for inspection */
+ raop_buffer_entry_t *entry = &raop_buffer->entries[raop_buffer->first_seqnum % RAOP_BUFFER_LENGTH];
+ if (no_resend) {
+ /* If we do no resends, always return the first entry */
+ } else if (!entry->filled) {
+ /* Check how much we have space left in the buffer */
+ if (entry_count < RAOP_BUFFER_LENGTH) {
+ /* Return nothing and hope resend gets on time */
+ return NULL;
+ }
+ /* Risk of buffer overrun, return empty buffer */
+ }
+
+ /* Update buffer and validate entry */
+ raop_buffer->first_seqnum += 1;
+ if (!entry->filled) {
+ return NULL;
+ }
+ entry->filled = 0;
+
+ /* Return entry payload buffer */
+ *rtp_timestamp = entry->rtp_timestamp;
+ *ntp_timestamp = entry->ntp_timestamp;
+ *seqnum = entry->seqnum;
+ *length = entry->payload_size;
+ entry->payload_size = 0;
+ void* data = entry->payload_data;
+ entry->payload_data = NULL;
+ return data;
+}
+
+void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque) {
+ assert(raop_buffer);
+ assert(resend_cb);
+
+ if (seqnum_cmp(raop_buffer->first_seqnum, raop_buffer->last_seqnum) < 0) {
+ unsigned short seqnum, count = 0;
+ logger_log(raop_buffer->logger, LOGGER_DEBUG, "raop_buffer_handle_resends first_seqnum=%u last seqnum=%u",
+ raop_buffer->first_seqnum, raop_buffer->last_seqnum);
+ for (seqnum = raop_buffer->first_seqnum; seqnum_cmp(seqnum, raop_buffer->last_seqnum) < 0; seqnum++) {
+ raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH];
+ if (entry->filled) {
+ break;
+ }
+ count++;
+ }
+ if (count){
+ resend_cb(opaque, raop_buffer->first_seqnum, count);
+ }
+ }
+}
+
+void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq) {
+ assert(raop_buffer);
+
+ for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
+ if (raop_buffer->entries[i].payload_data) {
+ free(raop_buffer->entries[i].payload_data);
+ raop_buffer->entries[i].payload_data = NULL;
+ raop_buffer->entries[i].payload_size = 0;
+ }
+ raop_buffer->entries[i].filled = 0;
+ }
+ if (next_seq < 0 || next_seq > 0xffff) {
+ raop_buffer->is_empty = 1;
+ } else {
+ raop_buffer->first_seqnum = next_seq;
+ raop_buffer->last_seqnum = next_seq - 1;
+ }
+}
diff --git a/lib/raop_buffer.h b/lib/raop_buffer.h
new file mode 100644
index 0000000..1093188
--- /dev/null
+++ b/lib/raop_buffer.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2021-23
+ */
+
+#ifndef RAOP_BUFFER_H
+#define RAOP_BUFFER_H
+
+#include "logger.h"
+#include "raop_rtp.h"
+
+typedef struct raop_buffer_s raop_buffer_t;
+
+typedef int (*raop_resend_cb_t)(void *opaque, unsigned short seqno, unsigned short count);
+
+raop_buffer_t *raop_buffer_init(logger_t *logger,
+ const unsigned char *aeskey,
+ const unsigned char *aesiv);
+int raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, int use_seqnum);
+void *raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, unsigned short *seqnum, int no_resend);
+void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque);
+void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq);
+
+int raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output,
+ unsigned int datalen, unsigned int *outputlen);
+void raop_buffer_destroy(raop_buffer_t *raop_buffer);
+
+#endif
diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h
new file mode 100644
index 0000000..3b2c838
--- /dev/null
+++ b/lib/raop_handlers.h
@@ -0,0 +1,1085 @@
+/**
+ * Copyright (C) 2018 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *===================================================================
+ * modfied by fduncanh 2021-2023
+ */
+
+/* This file should be only included from raop.c as it defines static handler
+ * functions and depends on raop internals */
+
+#include "dnssdint.h"
+#include "utils.h"
+#include
+#include
+#include
+#define AUDIO_SAMPLE_RATE 44100 /* all supported AirPlay audio format use this sample rate */
+#define SECOND_IN_USECS 1000000
+
+typedef void (*raop_handler_t)(raop_conn_t *, http_request_t *,
+ http_response_t *, char **, int *);
+
+static void
+raop_handler_info(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ assert(conn->raop->dnssd);
+
+ plist_t res_node = plist_new_dict();
+
+ /* deviceID is the physical hardware address, and will not change */
+ int hw_addr_raw_len = 0;
+ const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len);
+ char *hw_addr = calloc(1, 3 * hw_addr_raw_len);
+ //int hw_addr_len =
+ utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len);
+ plist_t device_id_node = plist_new_string(hw_addr);
+ plist_dict_set_item(res_node, "deviceID", device_id_node);
+
+ /* Persistent Public Key */
+ int pk_len = 0;
+ char *pk = utils_parse_hex(conn->raop->pk_str, strlen(conn->raop->pk_str), &pk_len);
+ plist_t pk_node = plist_new_data(pk, pk_len);
+ plist_dict_set_item(res_node, "pk", pk_node);
+
+ /* airplay_txt is from the _airplay._tcp dnssd announuncement, may not be necessary */
+ int airplay_txt_len = 0;
+ const char *airplay_txt = dnssd_get_airplay_txt(conn->raop->dnssd, &airplay_txt_len);
+ plist_t txt_airplay_node = plist_new_data(airplay_txt, airplay_txt_len);
+ plist_dict_set_item(res_node, "txtAirPlay", txt_airplay_node);
+
+ uint64_t features = dnssd_get_airplay_features(conn->raop->dnssd);
+ plist_t features_node = plist_new_uint(features);
+ plist_dict_set_item(res_node, "features", features_node);
+
+ int name_len = 0;
+ const char *name = dnssd_get_name(conn->raop->dnssd, &name_len);
+ plist_t name_node = plist_new_string(name);
+ plist_dict_set_item(res_node, "name", name_node);
+
+ plist_t audio_latencies_node = plist_new_array();
+ plist_t audio_latencies_0_node = plist_new_dict();
+ plist_t audio_latencies_0_output_latency_micros_node = plist_new_bool(0);
+ plist_t audio_latencies_0_type_node = plist_new_uint(100);
+ plist_t audio_latencies_0_audio_type_node = plist_new_string("default");
+ plist_t audio_latencies_0_input_latency_micros_node = plist_new_uint(0);
+ plist_dict_set_item(audio_latencies_0_node, "type", audio_latencies_0_type_node);
+ plist_dict_set_item(audio_latencies_0_node, "inputLatencyMicros", audio_latencies_0_input_latency_micros_node);
+ plist_dict_set_item(audio_latencies_0_node, "audioType", audio_latencies_0_audio_type_node);
+ plist_dict_set_item(audio_latencies_0_node, "outputLatencyMicros", audio_latencies_0_output_latency_micros_node);
+ plist_array_append_item(audio_latencies_node, audio_latencies_0_node);
+
+ plist_t audio_latencies_1_node = plist_new_dict();
+ plist_t audio_latencies_1_output_latency_micros_node = plist_new_bool(0);
+ plist_t audio_latencies_1_type_node = plist_new_uint(101);
+ plist_t audio_latencies_1_audio_type_node = plist_new_string("default");
+ plist_t audio_latencies_1_input_latency_micros_node = plist_new_uint(0);
+ plist_dict_set_item(audio_latencies_1_node, "type", audio_latencies_1_type_node);
+ plist_dict_set_item(audio_latencies_1_node, "audioType", audio_latencies_1_audio_type_node);
+ plist_dict_set_item(audio_latencies_1_node, "inputLatencyMicros", audio_latencies_1_input_latency_micros_node);
+ plist_dict_set_item(audio_latencies_1_node, "outputLatencyMicros", audio_latencies_1_output_latency_micros_node);
+ plist_array_append_item(audio_latencies_node, audio_latencies_1_node);
+ plist_dict_set_item(res_node, "audioLatencies", audio_latencies_node);
+
+ plist_t audio_formats_node = plist_new_array();
+ plist_t audio_format_0_node = plist_new_dict();
+ plist_t audio_format_0_type_node = plist_new_uint(100);
+ plist_t audio_format_0_audio_input_formats_node = plist_new_uint(0x3fffffc);
+ plist_t audio_format_0_audio_output_formats_node = plist_new_uint(0x3fffffc);
+ plist_dict_set_item(audio_format_0_node, "audioOutputFormats", audio_format_0_audio_output_formats_node);
+ plist_dict_set_item(audio_format_0_node, "type", audio_format_0_type_node);
+ plist_dict_set_item(audio_format_0_node, "audioInputFormats", audio_format_0_audio_input_formats_node);
+ plist_array_append_item(audio_formats_node, audio_format_0_node);
+
+ plist_t audio_format_1_node = plist_new_dict();
+ plist_t audio_format_1_type_node = plist_new_uint(101);
+ plist_t audio_format_1_audio_input_formats_node = plist_new_uint(0x3fffffc);
+ plist_t audio_format_1_audio_output_formats_node = plist_new_uint(0x3fffffc);
+ plist_dict_set_item(audio_format_1_node, "audioOutputFormats", audio_format_1_audio_output_formats_node);
+ plist_dict_set_item(audio_format_1_node, "type", audio_format_1_type_node);
+ plist_dict_set_item(audio_format_1_node, "audioInputFormats", audio_format_1_audio_input_formats_node);
+ plist_array_append_item(audio_formats_node, audio_format_1_node);
+ plist_dict_set_item(res_node, "audioFormats", audio_formats_node);
+
+ plist_t pi_node = plist_new_string(AIRPLAY_PI);
+ plist_dict_set_item(res_node, "pi", pi_node);
+
+ plist_t vv_node = plist_new_uint(strtol(AIRPLAY_VV, NULL, 10));
+ plist_dict_set_item(res_node, "vv", vv_node);
+
+ plist_t status_flags_node = plist_new_uint(68);
+ plist_dict_set_item(res_node, "statusFlags", status_flags_node);
+
+ plist_t keep_alive_low_power_node = plist_new_uint(1);
+ plist_dict_set_item(res_node, "keepAliveLowPower", keep_alive_low_power_node);
+
+ plist_t source_version_node = plist_new_string(GLOBAL_VERSION);
+ plist_dict_set_item(res_node, "sourceVersion", source_version_node);
+
+ plist_t keep_alive_send_stats_as_body_node = plist_new_bool(1);
+ plist_dict_set_item(res_node, "keepAliveSendStatsAsBody", keep_alive_send_stats_as_body_node);
+
+ plist_t model_node = plist_new_string(GLOBAL_MODEL);
+ plist_dict_set_item(res_node, "model", model_node);
+
+ plist_t mac_address_node = plist_new_string(hw_addr);
+ plist_dict_set_item(res_node, "macAddress", mac_address_node);
+
+ plist_t displays_node = plist_new_array();
+ plist_t displays_0_node = plist_new_dict();
+ plist_t displays_0_width_physical_node = plist_new_uint(0);
+ plist_t displays_0_height_physical_node = plist_new_uint(0);
+ plist_t displays_0_uuid_node = plist_new_string("e0ff8a27-6738-3d56-8a16-cc53aacee925");
+ plist_t displays_0_width_node = plist_new_uint(conn->raop->width);
+ plist_t displays_0_height_node = plist_new_uint(conn->raop->height);
+ plist_t displays_0_width_pixels_node = plist_new_uint(conn->raop->width);
+ plist_t displays_0_height_pixels_node = plist_new_uint(conn->raop->height);
+ plist_t displays_0_rotation_node = plist_new_bool(0); /* set to true in AppleTV gen 3 (which has features bit 8 set */
+ plist_t displays_0_refresh_rate_node = plist_new_real((double) 1.0 / conn->raop->refreshRate); /* set as real 0.166666 = 60hz in AppleTV gen 3 */
+ plist_t displays_0_max_fps_node = plist_new_uint(conn->raop->maxFPS);
+ plist_t displays_0_overscanned_node = plist_new_bool(conn->raop->overscanned);
+ plist_t displays_0_features = plist_new_uint(14);
+
+ plist_dict_set_item(displays_0_node, "uuid", displays_0_uuid_node);
+ plist_dict_set_item(displays_0_node, "widthPhysical", displays_0_width_physical_node);
+ plist_dict_set_item(displays_0_node, "heightPhysical", displays_0_height_physical_node);
+ plist_dict_set_item(displays_0_node, "width", displays_0_width_node);
+ plist_dict_set_item(displays_0_node, "height", displays_0_height_node);
+ plist_dict_set_item(displays_0_node, "widthPixels", displays_0_width_pixels_node);
+ plist_dict_set_item(displays_0_node, "heightPixels", displays_0_height_pixels_node);
+ plist_dict_set_item(displays_0_node, "rotation", displays_0_rotation_node);
+ plist_dict_set_item(displays_0_node, "refreshRate", displays_0_refresh_rate_node);
+ plist_dict_set_item(displays_0_node, "maxFPS", displays_0_max_fps_node);
+ plist_dict_set_item(displays_0_node, "overscanned", displays_0_overscanned_node);
+ plist_dict_set_item(displays_0_node, "features", displays_0_features);
+ plist_array_append_item(displays_node, displays_0_node);
+ plist_dict_set_item(res_node, "displays", displays_node);
+
+ plist_to_bin(res_node, response_data, (uint32_t *) response_datalen);
+ plist_free(res_node);
+ http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
+ free(pk);
+ free(hw_addr);
+}
+
+static void
+raop_handler_pairpinstart(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "client sent PAIR-PIN-START request");
+ int pin_4;
+ if (conn->raop->pin > 9999) {
+ pin_4 = conn->raop->pin % 10000;
+ } else {
+ pin_4 = random_pin();
+ if (pin_4 < 0) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Failed to generate random pin");
+ } else {
+ conn->raop->pin = (unsigned short) pin_4 % 10000;
+ }
+ }
+ char pin[6];
+ snprintf(pin, 5, "%04u", pin_4);
+ if (conn->raop->callbacks.display_pin) {
+ conn->raop->callbacks.display_pin(conn->raop->callbacks.cls, pin);
+ }
+ logger_log(conn->raop->logger, LOGGER_INFO, "*** CLIENT MUST NOW ENTER PIN = \"%s\" AS AIRPLAY PASSWORD", pin);
+ *response_data = NULL;
+ response_datalen = 0;
+}
+
+static void
+raop_handler_pairsetup_pin(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen) {
+
+ const char *request_data = NULL;;
+ int request_datalen = 0;
+ bool data_is_plist = false;
+ bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
+ request_data = http_request_get_data(request, &request_datalen);
+ logger_log(conn->raop->logger, LOGGER_INFO, "client requested pair-setup-pin, datalen = %d", request_datalen);
+ if (request_datalen > 0) {
+ char *header_str= NULL;
+ http_request_get_header_string(request, &header_str);
+ logger_log(conn->raop->logger, LOGGER_INFO, "request header: %s", header_str);
+ data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
+ free(header_str);
+ }
+ if (!data_is_plist) {
+ logger_log(conn->raop->logger, LOGGER_INFO, "did not receive expected plist from client, request_datalen = %d",
+ request_datalen);
+ goto authentication_failed;
+ }
+
+ /* process the pair-setup-pin request */
+ plist_t req_root_node = NULL;
+ plist_from_bin(request_data, request_datalen, &req_root_node);
+ plist_t req_method_node = plist_dict_get_item(req_root_node, "method");
+ plist_t req_user_node = plist_dict_get_item(req_root_node, "user");
+ plist_t req_pk_node = plist_dict_get_item(req_root_node, "pk");
+ plist_t req_proof_node = plist_dict_get_item(req_root_node, "proof");
+ plist_t req_epk_node = plist_dict_get_item(req_root_node, "epk");
+ plist_t req_authtag_node = plist_dict_get_item(req_root_node, "authTag");
+
+ if (PLIST_IS_STRING(req_method_node) && PLIST_IS_STRING(req_user_node)) {
+ /* this is the initial pair-setup-pin request */
+ const char *salt;
+ char pin[6];
+ const char *pk;
+ int len_pk, len_salt;
+ char *method = NULL;
+ char *user = NULL;
+ plist_get_string_val(req_method_node, &method);
+ if (strncmp(method, "pin", strlen (method))) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "error, required method is \"pin\", client requested \"%s\"", method);
+ *response_data = NULL;
+ response_datalen = 0;
+ free (method);
+ plist_free (req_root_node);
+ return;
+ }
+ free (method);
+ plist_get_string_val(req_user_node, &user);
+ logger_log(conn->raop->logger, LOGGER_INFO, "pair-setup-pin: device_id = %s", user);
+ snprintf(pin, 6, "%04u", conn->raop->pin % 10000);
+ if (conn->raop->pin < 10000) {
+ conn->raop->pin = 0;
+ }
+ int ret = srp_new_user(conn->session, conn->raop->pairing, (const char *) user,
+ (const char *) pin, &salt, &len_salt, &pk, &len_pk);
+ free(user);
+ plist_free(req_root_node);
+ if (ret < 0) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "failed to create user, err = %d", ret);
+ goto authentication_failed;
+ }
+ plist_t res_root_node = plist_new_dict();
+ plist_t res_salt_node = plist_new_data(salt, len_salt);
+ plist_t res_pk_node = plist_new_data(pk, len_pk);
+ plist_dict_set_item(res_root_node, "pk", res_pk_node);
+ plist_dict_set_item(res_root_node, "salt", res_salt_node);
+ plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
+ plist_free(res_root_node);
+ http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
+ return;
+ } else if (PLIST_IS_DATA(req_pk_node) && PLIST_IS_DATA(req_proof_node)) {
+ /* this is the second part of pair-setup-pin request */
+ char *client_pk = NULL;
+ char *client_proof = NULL;
+ unsigned char proof[64];
+ memset(proof, 0, sizeof(proof));
+ uint64_t client_pk_len;
+ uint64_t client_proof_len;
+ plist_get_data_val(req_pk_node, &client_pk, &client_pk_len);
+ plist_get_data_val(req_proof_node, &client_proof, &client_proof_len);
+ if (logger_debug) {
+ char *str = utils_data_to_string((const unsigned char *) client_proof, client_proof_len, 20);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "client SRP6a proof :\n%s", str);
+ free (str);
+ }
+ memcpy(proof, client_proof, (int) client_proof_len);
+ free (client_proof);
+ int ret = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk,
+ (int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof));
+ free (client_pk);
+ plist_free(req_root_node);
+ if (ret < 0) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Client Authentication Failure (client proof not validated)");
+ goto authentication_failed;
+ }
+ if (logger_debug) {
+ char *str = utils_data_to_string((const unsigned char *) proof, sizeof(proof), 20);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "server SRP6a proof :\n%s", str);
+ free (str);
+ }
+ plist_t res_root_node = plist_new_dict();
+ plist_t res_proof_node = plist_new_data((const char *) proof, 20);
+ plist_dict_set_item(res_root_node, "proof", res_proof_node);
+ plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
+ plist_free(res_root_node);
+ http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
+ return;
+ } else if (PLIST_IS_DATA(req_epk_node) && PLIST_IS_DATA(req_authtag_node)) {
+ /* this is the third part of pair-setup-pin request */
+ char *client_epk = NULL;
+ char *client_authtag = NULL;
+ uint64_t client_epk_len;
+ uint64_t client_authtag_len;
+ unsigned char epk[ED25519_KEY_SIZE];
+ unsigned char authtag[GCM_AUTHTAG_SIZE];
+ int ret;
+ plist_get_data_val(req_epk_node, &client_epk, &client_epk_len);
+ plist_get_data_val(req_authtag_node, &client_authtag, &client_authtag_len);
+
+ if (logger_debug) {
+ char *str = utils_data_to_string((const unsigned char *) client_epk, client_epk_len, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "client_epk %d:\n%s\n", (int) client_epk_len, str);
+ str = utils_data_to_string((const unsigned char *) client_authtag, client_authtag_len, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "client_authtag %d:\n%s\n", (int) client_authtag_len, str);
+ free (str);
+ }
+
+ memcpy(epk, client_epk, ED25519_KEY_SIZE);
+ memcpy(authtag, client_authtag, GCM_AUTHTAG_SIZE);
+ free (client_authtag);
+ free (client_epk);
+ plist_free(req_root_node);
+ ret = srp_confirm_pair_setup(conn->session, conn->raop->pairing, epk, authtag);
+ if (ret < 0) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "pair-pin-setup (step 3): client authentication failed\n");
+ goto authentication_failed;
+ } else {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-pin-setup success\n");
+ }
+ pairing_session_set_setup_status(conn->session);
+ plist_t res_root_node = plist_new_dict();
+ plist_t res_epk_node = plist_new_data((const char *) epk, 32);
+ plist_t res_authtag_node = plist_new_data((const char *) authtag, 16);
+ plist_dict_set_item(res_root_node, "epk", res_epk_node);
+ plist_dict_set_item(res_root_node, "authTag", res_authtag_node);
+ plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
+ plist_free(res_root_node);
+ http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
+ return;
+ }
+ authentication_failed:;
+ http_response_init(response, "RTSP/1.0", 470, "Client Authentication Failure");
+}
+
+static void
+raop_handler_pairsetup(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ unsigned char public_key[ED25519_KEY_SIZE];
+ //const char *data;
+ int datalen;
+
+ //data =
+ http_request_get_data(request, &datalen);
+ if (datalen != 32) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data");
+ return;
+ }
+
+ pairing_get_public_key(conn->raop->pairing, public_key);
+ pairing_session_set_setup_status(conn->session);
+
+ *response_data = malloc(sizeof(public_key));
+ if (*response_data) {
+ http_response_add_header(response, "Content-Type", "application/octet-stream");
+ memcpy(*response_data, public_key, sizeof(public_key));
+ *response_datalen = sizeof(public_key);
+ }
+}
+
+static void
+raop_handler_pairverify(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ bool register_check = false;
+ if (pairing_session_check_handshake_status(conn->session)) {
+ if (conn->raop->use_pin) {
+ pairing_session_set_setup_status(conn->session);
+ register_check = true;
+ } else {
+ return;
+ }
+ }
+ unsigned char public_key[X25519_KEY_SIZE];
+ unsigned char signature[PAIRING_SIG_SIZE];
+ const unsigned char *data;
+ int datalen;
+
+ data = (unsigned char *) http_request_get_data(request, &datalen);
+ if (datalen < 4) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
+ return;
+ }
+ switch (data[0]) {
+ case 1:
+ if (datalen != 4 + X25519_KEY_SIZE + ED25519_KEY_SIZE) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
+ return;
+ }
+ /* We can fall through these errors, the result will just be garbage... */
+ if (pairing_session_handshake(conn->session, data + 4, data + 4 + X25519_KEY_SIZE)) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake");
+ }
+ if (pairing_session_get_public_key(conn->session, public_key)) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ECDH public key");
+ }
+ if (pairing_session_get_signature(conn->session, signature)) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature");
+ }
+ if (register_check) {
+ bool registered_client = true;
+ if (conn->raop->callbacks.check_register) {
+ const unsigned char *pk = data + 4 + X25519_KEY_SIZE;
+ char *pk64;
+ ed25519_pk_to_base64(pk, &pk64);
+ registered_client = conn->raop->callbacks.check_register(conn->raop->callbacks.cls, pk64);
+ free (pk64);
+ }
+
+ if (!registered_client) {
+ return;
+ }
+ }
+ *response_data = malloc(sizeof(public_key) + sizeof(signature));
+ if (*response_data) {
+ http_response_add_header(response, "Content-Type", "application/octet-stream");
+ memcpy(*response_data, public_key, sizeof(public_key));
+ memcpy(*response_data + sizeof(public_key), signature, sizeof(signature));
+ *response_datalen = sizeof(public_key) + sizeof(signature);
+ }
+ break;
+ case 0:
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "2nd pair-verify step: checking signature");
+ if (datalen != 4 + PAIRING_SIG_SIZE) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
+ return;
+ }
+
+ if (pairing_session_finish(conn->session, data + 4)) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Incorrect pair-verify signature");
+ http_response_set_disconnect(response, 1);
+ return;
+ }
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-verify: signature is verified");
+ http_response_add_header(response, "Content-Type", "application/octet-stream");
+ break;
+ }
+}
+
+static void
+raop_handler_fpsetup(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ const unsigned char *data;
+ int datalen;
+
+ data = (unsigned char *) http_request_get_data(request, &datalen);
+ if (datalen == 16) {
+ *response_data = malloc(142);
+ if (*response_data) {
+ http_response_add_header(response, "Content-Type", "application/octet-stream");
+ if (!fairplay_setup(conn->fairplay, data, (unsigned char *) *response_data)) {
+ *response_datalen = 142;
+ } else {
+ // Handle error?
+ free(*response_data);
+ *response_data = NULL;
+ }
+ }
+ } else if (datalen == 164) {
+ *response_data = malloc(32);
+ if (*response_data) {
+ http_response_add_header(response, "Content-Type", "application/octet-stream");
+ if (!fairplay_handshake(conn->fairplay, data, (unsigned char *) *response_data)) {
+ *response_datalen = 32;
+ } else {
+ // Handle error?
+ free(*response_data);
+ *response_data = NULL;
+ }
+ }
+ } else {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Invalid fp-setup data length");
+ return;
+ }
+}
+
+static void
+raop_handler_options(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ http_response_add_header(response, "Public", "SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
+}
+
+static void
+raop_handler_setup(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ const char *dacp_id;
+ const char *active_remote_header;
+ bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
+
+ const char *data;
+ int data_len;
+ data = http_request_get_data(request, &data_len);
+
+ dacp_id = http_request_get_header(request, "DACP-ID");
+ active_remote_header = http_request_get_header(request, "Active-Remote");
+
+ if (dacp_id && active_remote_header) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header);
+ if (conn->raop_rtp) {
+ raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header);
+ }
+ }
+
+ // Parsing bplist
+ plist_t req_root_node = NULL;
+ plist_from_bin(data, data_len, &req_root_node);
+ plist_t req_ekey_node = plist_dict_get_item(req_root_node, "ekey");
+ plist_t req_eiv_node = plist_dict_get_item(req_root_node, "eiv");
+
+ // For the response
+ plist_t res_root_node = plist_new_dict();
+
+ if (PLIST_IS_DATA(req_eiv_node) && PLIST_IS_DATA(req_ekey_node)) {
+ // The first SETUP call that initializes keys and timing
+
+ unsigned char aesiv[16];
+ unsigned char aeskey[16];
+ unsigned char eaeskey[72];
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1");
+
+ // First setup
+ char* eiv = NULL;
+ uint64_t eiv_len = 0;
+
+ char *deviceID = NULL;
+ char *model = NULL;
+ char *name = NULL;
+ bool admit_client = true;
+ plist_t req_deviceid_node = plist_dict_get_item(req_root_node, "deviceID");
+ plist_get_string_val(req_deviceid_node, &deviceID);
+ plist_t req_model_node = plist_dict_get_item(req_root_node, "model");
+ plist_get_string_val(req_model_node, &model);
+ plist_t req_name_node = plist_dict_get_item(req_root_node, "name");
+ plist_get_string_val(req_name_node, &name);
+ if (conn->raop->callbacks.report_client_request) {
+ conn->raop->callbacks.report_client_request(conn->raop->callbacks.cls, deviceID, model, name, &admit_client);
+ }
+ if (admit_client && deviceID && name && conn->raop->callbacks.register_client) {
+ bool pending_registration;
+ char *client_device_id;
+ char *client_pk; /* encoded as null-terminated base64 string*/
+ access_client_session_data(conn->session, &client_device_id, &client_pk, &pending_registration);
+ if (pending_registration) {
+ if (client_pk && !strcmp(deviceID, client_device_id)) {
+ conn->raop->callbacks.register_client(conn->raop->callbacks.cls, client_device_id, client_pk, name);
+ }
+ }
+ if (client_pk) {
+ free (client_pk);
+ }
+ }
+ if (deviceID) {
+ free (deviceID);
+ deviceID = NULL;
+ }
+ if (model) {
+ free (model);
+ model = NULL;
+ }
+ if (name) {
+ free (name);
+ name = NULL;
+ }
+ if (admit_client == false) {
+ /* client is not authorized to connect */
+ plist_free(res_root_node);
+ plist_free(req_root_node);
+ return;
+ }
+
+ plist_get_data_val(req_eiv_node, &eiv, &eiv_len);
+ memcpy(aesiv, eiv, 16);
+ free(eiv);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len);
+ if (logger_debug) {
+ char* str = utils_data_to_string(aesiv, 16, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str);
+ free(str);
+ }
+
+ char* ekey = NULL;
+ uint64_t ekey_len = 0;
+ plist_get_data_val(req_ekey_node, &ekey, &ekey_len);
+ memcpy(eaeskey,ekey,72);
+ free(ekey);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len);
+ // eaeskey is 72 bytes, aeskey is 16 bytes
+ if (logger_debug) {
+ char *str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str);
+ free (str);
+ }
+ int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) eaeskey, aeskey);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret);
+ if (logger_debug) {
+ char *str = utils_data_to_string(aeskey, 16, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str);
+ free(str);
+ }
+
+ const char *user_agent = http_request_get_header(request, "User-Agent");
+ logger_log(conn->raop->logger, LOGGER_INFO, "Client identified as User-Agent: %s", user_agent);
+
+ bool old_protocol = false;
+#ifdef OLD_PROTOCOL_CLIENT_USER_AGENT_LIST /* set in global.h */
+ if (strstr(OLD_PROTOCOL_CLIENT_USER_AGENT_LIST, user_agent)) old_protocol = true;
+#endif
+ if (old_protocol) { /* some windows AirPlay-client emulators use old AirPlay 1 protocol with unhashed AES key */
+ logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)");
+ } else {
+ unsigned char ecdh_secret[X25519_KEY_SIZE];
+ if (pairing_get_ecdh_secret_key(conn->session, ecdh_secret)) {
+ /* In this case (legacy) pairing with client was successfully set up and created the shared ecdh_secret:
+ * aeskey must now be hashed with it
+ *
+ * If byte 27 of features ("supports legacy pairing") is turned off, the client does not request pairsetup
+ * and does NOT set up pairing (this eliminates a 5 second delay in connecting with no apparent bad effects).
+ * In this case, ecdh_secret does not exist, so aeskey should NOT be hashed with it.
+
+ * UxPlay may be able to function with byte 27 turned off because it currently does not support connections
+ * with more than one client at a time. AppleTV supports up to 12 clients, uses pairing to give each a distinct
+ * SessionID and ecdh_secret.
+
+ * The "old protocol" Windows AirPlay client AirMyPC seems not to respect the byte 27 setting, and always sets
+ * up the ecdh_secret, but decryption fails if aeskey is hashed.*/
+
+ if (logger_debug) {
+ char *str = utils_data_to_string(ecdh_secret, X25519_KEY_SIZE, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "32 byte shared ecdh_secret:\n%s", str);
+ free(str);
+ }
+ memcpy(eaeskey, aeskey, 16);
+ sha_ctx_t *ctx = sha_init();
+ sha_update(ctx, eaeskey, 16);
+ sha_update(ctx, ecdh_secret, 32);
+ sha_final(ctx, eaeskey, NULL);
+ sha_destroy(ctx);
+ memcpy(aeskey, eaeskey, 16);
+ if (logger_debug) {
+ char *str = utils_data_to_string(aeskey, 16, 16);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey after sha-256 hash with ecdh_secret:\n%s", str);
+ free(str);
+ }
+ }
+ }
+
+ // Time port
+ plist_t req_is_remote_control_only_node = plist_dict_get_item(req_root_node, "isRemoteControlOnly");
+ if (req_is_remote_control_only_node) {
+ uint8_t bool_val = 0;
+ plist_get_bool_val(req_is_remote_control_only_node, &bool_val);
+ if (bool_val) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Client specified AirPlay2 \"Remote Control\" protocol\n"
+ " Only AirPlay v1 protocol (using NTP and timing port) is supported");
+ }
+ }
+ char *timing_protocol = NULL;
+ timing_protocol_t time_protocol;
+ plist_t req_timing_protocol_node = plist_dict_get_item(req_root_node, "timingProtocol");
+ plist_get_string_val(req_timing_protocol_node, &timing_protocol);
+ if (timing_protocol) {
+ int string_len = strlen(timing_protocol);
+ if (strncmp(timing_protocol, "NTP", string_len) == 0) {
+ time_protocol = NTP;
+ } else if (strncmp(timing_protocol, "None", string_len) == 0) {
+ time_protocol = TP_NONE;
+ } else {
+ time_protocol = TP_OTHER;
+ }
+ if (time_protocol != NTP) {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s,"
+ " but timingProtocol= NTP is required here", timing_protocol);
+ }
+ free (timing_protocol);
+ timing_protocol = NULL;
+ } else {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Client did not specify timingProtocol,"
+ " old protocol without offset will be used");
+ time_protocol = TP_UNSPECIFIED;
+ }
+ uint64_t timing_rport = 0;
+ plist_t req_timing_port_node = plist_dict_get_item(req_root_node, "timingPort");
+ if (req_timing_port_node) {
+ plist_get_uint_val(req_timing_port_node, &timing_rport);
+ }
+ if (timing_rport) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport);
+ } else {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Client did not supply timing_rport,"
+ " may be using unsupported AirPlay2 \"Remote Control\" protocol");
+ }
+ unsigned short timing_lport = conn->raop->timing_lport;
+
+ conn->raop_ntp = NULL;
+ conn->raop_rtp = NULL;
+ conn->raop_rtp_mirror = NULL;
+ char remote[40];
+ int len = utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, remote, (int) sizeof(remote));
+ if (!len || len > sizeof(remote)) {
+ char *str = utils_data_to_string(conn->remote, conn->remotelen, 16);
+ logger_log(conn->raop->logger, LOGGER_ERR, "failed to extract valid client ip address:\n"
+ "*** UxPlay will be unable to send communications to client.\n"
+ "*** address length %d, zone_id %u address data:\n%sparser returned \"%s\"\n",
+ conn->remotelen, conn->zone_id, str, remote);
+ free(str);
+ }
+ conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, remote,
+ conn->remotelen, (unsigned short) timing_rport, &time_protocol);
+ raop_ntp_start(conn->raop_ntp, &timing_lport, conn->raop->max_ntp_timeouts);
+ conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp,
+ remote, conn->remotelen, aeskey, aesiv);
+ conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks,
+ conn->raop_ntp, remote, conn->remotelen, aeskey);
+
+ /* the event port is not used in mirror mode or audio mode */
+ unsigned short event_port = 0;
+ plist_t res_event_port_node = plist_new_uint(event_port);
+ plist_t res_timing_port_node = plist_new_uint(timing_lport);
+ plist_dict_set_item(res_root_node, "timingPort", res_timing_port_node);
+ plist_dict_set_item(res_root_node, "eventPort", res_event_port_node);
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "eport = %d, tport = %d", event_port, timing_lport);
+ }
+
+ // Process stream setup requests
+ plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams");
+ if (PLIST_IS_ARRAY(req_streams_node)) {
+ plist_t res_streams_node = plist_new_array();
+
+ int count = plist_array_get_size(req_streams_node);
+ for (int i = 0; i < count; i++) {
+ plist_t req_stream_node = plist_array_get_item(req_streams_node, i);
+ plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type");
+ uint64_t type;
+ plist_get_uint_val(req_stream_type_node, &type);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "type = %llu", type);
+
+ switch (type) {
+ case 110: {
+ // Mirroring
+ unsigned short dport = conn->raop->mirror_data_lport;
+ plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
+ uint64_t stream_connection_id;
+ plist_get_uint_val(stream_id_node, &stream_connection_id);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption"
+ " key and iv): %llu", stream_connection_id);
+
+ if (conn->raop_rtp_mirror) {
+ raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id);
+ raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully");
+ } else {
+ logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!");
+ http_response_set_disconnect(response, 1);
+ }
+
+ plist_t res_stream_node = plist_new_dict();
+ plist_t res_stream_data_port_node = plist_new_uint(dport);
+ plist_t res_stream_type_node = plist_new_uint(110);
+ plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
+ plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
+ plist_array_append_item(res_streams_node, res_stream_node);
+
+ break;
+ } case 96: {
+ // Audio
+ unsigned short cport = conn->raop->control_lport, dport = conn->raop->data_lport;
+ unsigned short remote_cport = 0;
+ unsigned char ct;
+ unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */
+
+ uint64_t uint_val = 0;
+ plist_t req_stream_control_port_node = plist_dict_get_item(req_stream_node, "controlPort");
+ plist_get_uint_val(req_stream_control_port_node, &uint_val);
+ remote_cport = (unsigned short) uint_val; /* must != 0 to activate audio resend requests */
+
+ plist_t req_stream_ct_node = plist_dict_get_item(req_stream_node, "ct");
+ plist_get_uint_val(req_stream_ct_node, &uint_val);
+ ct = (unsigned char) uint_val;
+
+ if (conn->raop->callbacks.audio_get_format) {
+ /* get additional audio format parameters */
+ uint64_t audioFormat;
+ unsigned short spf;
+ bool isMedia;
+ bool usingScreen;
+ uint8_t bool_val = 0;
+
+ plist_t req_stream_spf_node = plist_dict_get_item(req_stream_node, "spf");
+ plist_get_uint_val(req_stream_spf_node, &uint_val);
+ spf = (unsigned short) uint_val;
+
+ plist_t req_stream_audio_format_node = plist_dict_get_item(req_stream_node, "audioFormat");
+ plist_get_uint_val(req_stream_audio_format_node, &audioFormat);
+
+ plist_t req_stream_is_media_node = plist_dict_get_item(req_stream_node, "isMedia");
+ if (req_stream_is_media_node) {
+ plist_get_bool_val(req_stream_is_media_node, &bool_val);
+ isMedia = (bool) bool_val;
+ } else {
+ isMedia = false;
+ }
+
+ plist_t req_stream_using_screen_node = plist_dict_get_item(req_stream_node, "usingScreen");
+ if (req_stream_using_screen_node) {
+ plist_get_bool_val(req_stream_using_screen_node, &bool_val);
+ usingScreen = (bool) bool_val;
+ } else {
+ usingScreen = false;
+ }
+
+ conn->raop->callbacks.audio_get_format(conn->raop->callbacks.cls, &ct, &spf, &usingScreen, &isMedia, &audioFormat);
+ }
+
+ if (conn->raop_rtp) {
+ raop_rtp_start_audio(conn->raop_rtp, &remote_cport, &cport, &dport, &ct, &sr);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success");
+ } else {
+ logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
+ http_response_set_disconnect(response, 1);
+ }
+
+ plist_t res_stream_node = plist_new_dict();
+ plist_t res_stream_data_port_node = plist_new_uint(dport);
+ plist_t res_stream_control_port_node = plist_new_uint(cport);
+ plist_t res_stream_type_node = plist_new_uint(96);
+ plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
+ plist_dict_set_item(res_stream_node, "controlPort", res_stream_control_port_node);
+ plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
+ plist_array_append_item(res_streams_node, res_stream_node);
+
+ break;
+ }
+
+ default:
+ logger_log(conn->raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type);
+ http_response_set_disconnect(response, 1);
+ break;
+ }
+ }
+
+ plist_dict_set_item(res_root_node, "streams", res_streams_node);
+ }
+
+ plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
+ plist_free(res_root_node);
+ plist_free(req_root_node);
+ http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
+}
+
+static void
+raop_handler_get_parameter(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ const char *content_type;
+ const char *data;
+ int datalen;
+
+ content_type = http_request_get_header(request, "Content-Type");
+ data = http_request_get_data(request, &datalen);
+ if (!strcmp(content_type, "text/parameters")) {
+ const char *current = data;
+
+ while (current && (datalen - (current - data) > 0)) {
+ const char *next;
+
+ /* This is a bit ugly, but seems to be how airport works too */
+ if ((datalen - (current - data) >= 8) && !strncmp(current, "volume\r\n", 8)) {
+ const char volume[] = "volume: 0.0\r\n";
+
+ http_response_add_header(response, "Content-Type", "text/parameters");
+ *response_data = strdup(volume);
+ if (*response_data) {
+ *response_datalen = strlen(*response_data);
+ }
+ return;
+ }
+
+ for (next = current ; (datalen - (next - data) > 0) ; ++next)
+ if (*next == '\r')
+ break;
+
+ if ((datalen - (next - data) >= 2) && !strncmp(next, "\r\n", 2)) {
+ if ((next - current) > 0) {
+ logger_log(conn->raop->logger, LOGGER_WARNING,
+ "Found an unknown parameter: %.*s", (next - current), current);
+ }
+ current = next + 2;
+ } else {
+ current = NULL;
+ }
+ }
+ }
+}
+
+static void
+raop_handler_set_parameter(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ const char *content_type;
+ const char *data;
+ int datalen;
+
+ content_type = http_request_get_header(request, "Content-Type");
+ data = http_request_get_data(request, &datalen);
+ if (!strcmp(content_type, "text/parameters")) {
+ char *datastr;
+ datastr = calloc(1, datalen+1);
+ if (data && datastr && conn->raop_rtp) {
+ memcpy(datastr, data, datalen);
+ if ((datalen >= 8) && !strncmp(datastr, "volume: ", 8)) {
+ float vol = 0.0;
+ sscanf(datastr+8, "%f", &vol);
+ raop_rtp_set_volume(conn->raop_rtp, vol);
+ } else if ((datalen >= 10) && !strncmp(datastr, "progress: ", 10)) {
+ unsigned int start, curr, end;
+ sscanf(datastr+10, "%u/%u/%u", &start, &curr, &end);
+ raop_rtp_set_progress(conn->raop_rtp, start, curr, end);
+ }
+ } else if (!conn->raop_rtp) {
+ logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER");
+ }
+ free(datastr);
+ } else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Got image data of %d bytes", datalen);
+ if (conn->raop_rtp) {
+ raop_rtp_set_coverart(conn->raop_rtp, data, datalen);
+ } else {
+ logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart");
+ }
+ } else if (!strcmp(content_type, "application/x-dmap-tagged")) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Got metadata of %d bytes", datalen);
+ if (conn->raop_rtp) {
+ raop_rtp_set_metadata(conn->raop_rtp, data, datalen);
+ } else {
+ logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata");
+ }
+ }
+}
+
+
+static void
+raop_handler_feedback(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_feedback");
+}
+
+static void
+raop_handler_record(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ char audio_latency[12];
+ unsigned int ad = (unsigned int) (((uint64_t) conn->raop->audio_delay_micros) * AUDIO_SAMPLE_RATE / SECOND_IN_USECS);
+ snprintf(audio_latency, sizeof(audio_latency), "%u", ad);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_record");
+ http_response_add_header(response, "Audio-Latency", audio_latency);
+ http_response_add_header(response, "Audio-Jack-Status", "connected; type=analog");
+}
+
+static void
+raop_handler_flush(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ const char *rtpinfo;
+ int next_seq = -1;
+
+ rtpinfo = http_request_get_header(request, "RTP-Info");
+ if (rtpinfo) {
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "Flush with RTP-Info: %s", rtpinfo);
+ if (!strncmp(rtpinfo, "seq=", 4)) {
+ next_seq = strtol(rtpinfo + 4, NULL, 10);
+ }
+ }
+ if (conn->raop_rtp) {
+ raop_rtp_flush(conn->raop_rtp, next_seq);
+ } else {
+ logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH");
+ }
+}
+
+static void
+raop_handler_teardown(raop_conn_t *conn,
+ http_request_t *request, http_response_t *response,
+ char **response_data, int *response_datalen)
+{
+ /* get the teardown request type(s): (type 96, 110, or none) */
+ const char *data;
+ int data_len;
+ bool teardown_96 = false, teardown_110 = false;
+ data = http_request_get_data(request, &data_len);
+ plist_t req_root_node = NULL;
+ plist_from_bin(data, data_len, &req_root_node);
+ char * plist_xml;
+ uint32_t plist_len;
+ plist_to_xml(req_root_node, &plist_xml, &plist_len);
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml);
+ free(plist_xml);
+ plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams");
+ /* Process stream teardown requests */
+ if (PLIST_IS_ARRAY(req_streams_node)) {
+ uint64_t val;
+ int count = plist_array_get_size(req_streams_node);
+ for (int i = 0; i < count; i++) {
+ plist_t req_stream_node = plist_array_get_item(req_streams_node,0);
+ plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type");
+ plist_get_uint_val(req_stream_type_node, &val);
+ if (val == 96) {
+ teardown_96 = true;
+ } else if (val == 110) {
+ teardown_110 = true;
+ }
+ }
+ }
+ plist_free(req_root_node);
+ if (conn->raop->callbacks.conn_teardown) {
+ conn->raop->callbacks.conn_teardown(conn->raop->callbacks.cls, &teardown_96, &teardown_110);
+ }
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "TEARDOWN request, 96=%d, 110=%d", teardown_96, teardown_110);
+
+ http_response_add_header(response, "Connection", "close");
+
+ if (teardown_96) {
+ if (conn->raop_rtp) {
+ /* Stop our audio RTP session */
+ raop_rtp_stop(conn->raop_rtp);
+ }
+ } else if (teardown_110) {
+ if (conn->raop_rtp_mirror) {
+ /* Stop our video RTP session */
+ raop_rtp_mirror_stop(conn->raop_rtp_mirror);
+ }
+ } else {
+ /* Destroy our sessions */
+ if (conn->raop_rtp) {
+ raop_rtp_destroy(conn->raop_rtp);
+ conn->raop_rtp = NULL;
+ }
+ if (conn->raop_rtp_mirror) {
+ raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
+ conn->raop_rtp_mirror = NULL;
+ }
+ }
+}
diff --git a/lib/raop_ntp.c b/lib/raop_ntp.c
new file mode 100644
index 0000000..1a932e4
--- /dev/null
+++ b/lib/raop_ntp.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 2019 dsafa22 and 2014 Joakim Plate, modified by Florian Draschbacher,
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2021-23
+ */
+
+// Some of the code in here comes from https://github.com/juhovh/shairplay/pull/25/files
+
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#define CAST (char *)
+#else
+#define CAST
+#endif
+
+#include "raop.h"
+#include "threads.h"
+#include "compat.h"
+#include "netutils.h"
+#include "byteutils.h"
+#include "utils.h"
+
+#define SECOND_IN_NSECS 1000000000UL
+#define RAOP_NTP_DATA_COUNT 8
+#define RAOP_NTP_PHI_PPM 15ull // PPM
+#define RAOP_NTP_R_RHO ((1ull << 32) / 1000u) // packet precision
+#define RAOP_NTP_S_RHO ((1ull << 32) / 1000u) // system clock precision
+#define RAOP_NTP_MAX_DIST ((1500ull << 32) / 1000u) // maximum allowed distance
+#define RAOP_NTP_MAX_DISP ((16ull << 32)) // maximum dispersion
+
+#define RAOP_NTP_CLOCK_BASE (2208988800ull << 32)
+
+typedef struct raop_ntp_data_s {
+ uint64_t time; // The local wall clock time at time of ntp packet arrival
+ uint64_t dispersion;
+ int64_t delay; // The round trip delay
+ int64_t offset; // The difference between remote and local wall clock time
+} raop_ntp_data_t;
+
+struct raop_ntp_s {
+ logger_t *logger;
+ raop_callbacks_t callbacks;
+
+ int max_ntp_timeouts;
+
+ thread_handle_t thread;
+ mutex_handle_t run_mutex;
+
+ mutex_handle_t wait_mutex;
+ cond_handle_t wait_cond;
+
+ raop_ntp_data_t data[RAOP_NTP_DATA_COUNT];
+ int data_index;
+
+ // The clock sync params are periodically updated to the AirPlay client's NTP clock
+ mutex_handle_t sync_params_mutex;
+ int64_t sync_offset;
+ int64_t sync_dispersion;
+ int64_t sync_delay;
+
+ // Socket address of the AirPlay client
+ struct sockaddr_storage remote_saddr;
+ socklen_t remote_saddr_len;
+
+ // The remote port of the NTP server on the AirPlay client
+ unsigned short timing_rport;
+
+ // The local port of the NTP client on the AirPlay server
+ unsigned short timing_lport;
+
+ /* MUTEX LOCKED VARIABLES START */
+ /* These variables only edited mutex locked */
+ int running;
+ int joined;
+
+ // UDP socket
+ int tsock;
+
+ timing_protocol_t time_protocol;
+};
+
+
+/*
+ * Used for sorting the data array by delay
+ */
+static int
+raop_ntp_compare(const void* av, const void* bv)
+{
+ const raop_ntp_data_t* a = (const raop_ntp_data_t*)av;
+ const raop_ntp_data_t* b = (const raop_ntp_data_t*)bv;
+ if (a->delay < b->delay) {
+ return -1;
+ } else if(a->delay > b->delay) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int
+raop_ntp_parse_remote(raop_ntp_t *raop_ntp, const char *remote, int remote_addr_len)
+{
+ int family;
+ int ret;
+ assert(raop_ntp);
+ if (remote_addr_len == 4) {
+ family = AF_INET;
+ } else if (remote_addr_len == 16) {
+ family = AF_INET6;
+ } else {
+ return -1;
+ }
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp parse remote ip = %s", remote);
+ ret = netutils_parse_address(family, remote,
+ &raop_ntp->remote_saddr,
+ sizeof(raop_ntp->remote_saddr));
+ if (ret < 0) {
+ return -1;
+ }
+ raop_ntp->remote_saddr_len = ret;
+ return 0;
+}
+
+raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote,
+ int remote_addr_len, unsigned short timing_rport, timing_protocol_t *time_protocol) {
+ raop_ntp_t *raop_ntp;
+
+ assert(logger);
+ assert(callbacks);
+
+ raop_ntp = calloc(1, sizeof(raop_ntp_t));
+ if (!raop_ntp) {
+ return NULL;
+ }
+ raop_ntp->time_protocol = *time_protocol;
+ raop_ntp->logger = logger;
+ memcpy(&raop_ntp->callbacks, callbacks, sizeof(raop_callbacks_t));
+ raop_ntp->timing_rport = timing_rport;
+
+ if (raop_ntp_parse_remote(raop_ntp, remote, remote_addr_len) < 0) {
+ free(raop_ntp);
+ return NULL;
+ }
+
+ // Set port on the remote address struct
+ ((struct sockaddr_in *) &raop_ntp->remote_saddr)->sin_port = htons(timing_rport);
+
+ raop_ntp->running = 0;
+ raop_ntp->joined = 1;
+
+ uint64_t time = raop_ntp_get_local_time(raop_ntp);
+
+ for (int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) {
+ raop_ntp->data[i].offset = 0ll;
+ raop_ntp->data[i].delay = RAOP_NTP_MAX_DISP;
+ raop_ntp->data[i].dispersion = RAOP_NTP_MAX_DISP;
+ raop_ntp->data[i].time = time;
+ }
+
+ raop_ntp->sync_delay = 0;
+ raop_ntp->sync_dispersion = 0;
+ raop_ntp->sync_offset = 0;
+
+ MUTEX_CREATE(raop_ntp->run_mutex);
+ MUTEX_CREATE(raop_ntp->wait_mutex);
+ COND_CREATE(raop_ntp->wait_cond);
+ MUTEX_CREATE(raop_ntp->sync_params_mutex);
+ return raop_ntp;
+}
+
+void
+raop_ntp_destroy(raop_ntp_t *raop_ntp)
+{
+ if (raop_ntp) {
+ raop_ntp_stop(raop_ntp);
+ MUTEX_DESTROY(raop_ntp->run_mutex);
+ MUTEX_DESTROY(raop_ntp->wait_mutex);
+ COND_DESTROY(raop_ntp->wait_cond);
+ MUTEX_DESTROY(raop_ntp->sync_params_mutex);
+ free(raop_ntp);
+ }
+}
+
+unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp) {
+ return raop_ntp->timing_lport;
+}
+
+static int
+raop_ntp_init_socket(raop_ntp_t *raop_ntp, int use_ipv6)
+{
+ assert(raop_ntp);
+ unsigned short tport = raop_ntp->timing_lport;
+ int tsock = netutils_init_socket(&tport, use_ipv6, 1);
+
+ if (tsock == -1) {
+ goto sockets_cleanup;
+ }
+
+ // We're calling recvfrom without knowing whether there is any data, so we need a timeout
+ uint32_t recv_timeout_msec = 300;
+#ifdef _WIN32
+ DWORD tv = recv_timeout_msec;
+#else
+ struct timeval tv;
+ tv.tv_sec = recv_timeout_msec / (uint32_t) 1000;
+ tv.tv_usec = ((uint32_t) 1000) * (recv_timeout_msec % (uint32_t) 1000);
+#endif
+ if (setsockopt(tsock, SOL_SOCKET, SO_RCVTIMEO, CAST &tv, sizeof(tv)) < 0) {
+ goto sockets_cleanup;
+ }
+
+ /* Set socket descriptors */
+ raop_ntp->tsock = tsock;
+
+ /* Set port values */
+ raop_ntp->timing_lport = tport;
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp local timing port socket %d port UDP %d", tsock, tport);
+ return 0;
+
+ sockets_cleanup:
+ if (tsock != -1) closesocket(tsock);
+ return -1;
+}
+
+static void
+raop_ntp_flush_socket(int fd)
+{
+#ifdef _WIN32
+#define IOCTL ioctlsocket
+ u_long bytes_available = 0;
+#else
+#define IOCTL ioctl
+ int bytes_available = 0;
+#endif
+ while (IOCTL(fd, FIONREAD, &bytes_available) == 0 && bytes_available > 0)
+ {
+ // We are guaranteed that we won't block, because bytes are available.
+ // Read 1 byte. Extra bytes in the datagram will be discarded.
+ char c;
+ int result = recvfrom(fd, &c, sizeof(c), 0, NULL, NULL);
+ if (result < 0)
+ {
+ break;
+ }
+ }
+}
+
+static THREAD_RETVAL
+raop_ntp_thread(void *arg)
+{
+ raop_ntp_t *raop_ntp = arg;
+ assert(raop_ntp);
+ unsigned char response[128];
+ int response_len;
+ unsigned char request[32] = {0x80, 0xd2, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ raop_ntp_data_t data_sorted[RAOP_NTP_DATA_COUNT];
+ const unsigned two_pow_n[RAOP_NTP_DATA_COUNT] = {2, 4, 8, 16, 32, 64, 128, 256};
+ int timeout_counter = 0;
+ bool conn_reset = false;
+ bool logger_debug = (logger_get_level(raop_ntp->logger) >= LOGGER_DEBUG);
+
+ while (1) {
+ MUTEX_LOCK(raop_ntp->run_mutex);
+ if (!raop_ntp->running) {
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+ break;
+ }
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+
+ // Flush the socket in case a super delayed response arrived or something
+ raop_ntp_flush_socket(raop_ntp->tsock);
+
+ // Send request
+ uint64_t send_time = raop_ntp_get_local_time(raop_ntp);
+ byteutils_put_ntp_timestamp(request, 24, send_time);
+ int send_len = sendto(raop_ntp->tsock, (char *)request, sizeof(request), 0,
+ (struct sockaddr *) &raop_ntp->remote_saddr, raop_ntp->remote_saddr_len);
+ if (logger_debug) {
+ char *str = utils_data_to_string(request, sizeof(request), 16);
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "\nraop_ntp send time type_t=%d packetlen = %d, now = %8.6f\n%s",
+ request[1] &~0x80, sizeof(request), (double) send_time / SECOND_IN_NSECS, str);
+ free(str);
+ }
+ if (send_len < 0) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp error sending request. Error %d:%s",
+ sock_err, SOCKET_ERROR_STRING(sock_err));
+ } else {
+ // Read response
+ response_len = recvfrom(raop_ntp->tsock, (char *)response, sizeof(response), 0, NULL, NULL);
+ if (response_len < 0) {
+ timeout_counter++;
+ char time[30];
+ int level = (timeout_counter == 1 ? LOGGER_DEBUG : LOGGER_ERR);
+ ntp_timestamp_to_time(send_time, time, sizeof(time));
+ logger_log(raop_ntp->logger, level, "raop_ntp receive timeout %d (limit %d) (request sent %s)",
+ timeout_counter, raop_ntp->max_ntp_timeouts, time);
+ if (timeout_counter == raop_ntp->max_ntp_timeouts) {
+ conn_reset = true; /* client is no longer responding */
+ break;
+ }
+ } else {
+ //local time of the server when the NTP response packet returns
+ int64_t t3 = (int64_t) raop_ntp_get_local_time(raop_ntp);
+ timeout_counter = 0;
+
+ // Local time of the server when the NTP request packet leaves the server
+ int64_t t0 = (int64_t) byteutils_get_ntp_timestamp(response, 8);
+
+ // Local time of the client when the NTP request packet arrives at the client
+ int64_t t1 = (int64_t) raop_remote_timestamp_to_nano_seconds(raop_ntp, byteutils_get_long_be(response, 16));
+
+ // Local time of the client when the response message leaves the client
+ int64_t t2 = (int64_t) raop_remote_timestamp_to_nano_seconds(raop_ntp, byteutils_get_long_be(response, 24));
+
+ if (logger_debug) {
+ char *str = utils_data_to_string(response, response_len, 16);
+ logger_log(raop_ntp->logger, LOGGER_DEBUG,
+ "raop_ntp receive time type_t=%d packetlen = %d, now = %8.6f t1 = %8.6f, t2 = %8.6f\n%s",
+ response[1] &~0x80, response_len, (double) t3 / SECOND_IN_NSECS, (double) t1 / SECOND_IN_NSECS,
+ (double) t2 / SECOND_IN_NSECS, str);
+ free(str);
+ }
+ // The iOS client device sends its time in seconds relative to an arbitrary Epoch (the last boot).
+ // For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970.
+ // This means we have to expect some rather huge offset, but its growth or shrink over time should be small.
+
+ raop_ntp->data_index = (raop_ntp->data_index + 1) % RAOP_NTP_DATA_COUNT;
+ raop_ntp->data[raop_ntp->data_index].time = t3;
+ raop_ntp->data[raop_ntp->data_index].offset = ((t1 - t0) + (t2 - t3)) / 2;
+ raop_ntp->data[raop_ntp->data_index].delay = ((t3 - t0) - (t2 - t1));
+ raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS;
+
+ // Sort by delay
+ memcpy(data_sorted, raop_ntp->data, sizeof(data_sorted));
+ qsort(data_sorted, RAOP_NTP_DATA_COUNT, sizeof(data_sorted[0]), raop_ntp_compare);
+
+ uint64_t dispersion = 0ull;
+ int64_t offset = data_sorted[0].offset;
+ int64_t delay = data_sorted[RAOP_NTP_DATA_COUNT - 1].delay;
+
+ // Calculate dispersion
+ for(int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) {
+ unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS;
+ dispersion += disp / two_pow_n[i];
+ }
+
+ MUTEX_LOCK(raop_ntp->sync_params_mutex);
+
+ int64_t correction = offset - raop_ntp->sync_offset;
+ raop_ntp->sync_offset = offset;
+ raop_ntp->sync_dispersion = dispersion;
+ raop_ntp->sync_delay = delay;
+ MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
+
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp sync correction = %lld", correction);
+ }
+ }
+
+ // Sleep for 3 seconds
+ struct timespec wait_time;
+ MUTEX_LOCK(raop_ntp->wait_mutex);
+ clock_gettime(CLOCK_REALTIME, &wait_time);
+ wait_time.tv_sec += 3;
+ pthread_cond_timedwait(&raop_ntp->wait_cond, &raop_ntp->wait_mutex, &wait_time);
+ MUTEX_UNLOCK(raop_ntp->wait_mutex);
+ }
+
+ // Ensure running reflects the actual state
+ MUTEX_LOCK(raop_ntp->run_mutex);
+ raop_ntp->running = false;
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp exiting thread");
+ if (conn_reset && raop_ntp->callbacks.conn_reset) {
+ const bool video_reset = false; /* leave "frozen video" in place */
+ raop_ntp->callbacks.conn_reset(raop_ntp->callbacks.cls, timeout_counter, video_reset);
+ }
+ return 0;
+}
+
+void
+raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_timeouts)
+{
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp starting time");
+ int use_ipv6 = 0;
+
+ assert(raop_ntp);
+ assert(timing_lport);
+
+ raop_ntp->max_ntp_timeouts = max_ntp_timeouts;
+ raop_ntp->timing_lport = *timing_lport;
+
+ MUTEX_LOCK(raop_ntp->run_mutex);
+ if (raop_ntp->running || !raop_ntp->joined) {
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+ return;
+ }
+
+ /* Initialize ports and sockets */
+ if (raop_ntp->remote_saddr.ss_family == AF_INET6) {
+ use_ipv6 = 1;
+ }
+ //use_ipv6 = 0;
+ if (raop_ntp_init_socket(raop_ntp, use_ipv6) < 0) {
+ logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp initializing timing socket failed");
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+ return;
+ }
+ *timing_lport = raop_ntp->timing_lport;
+
+ /* Create the thread and initialize running values */
+ raop_ntp->running = 1;
+ raop_ntp->joined = 0;
+
+ THREAD_CREATE(raop_ntp->thread, raop_ntp_thread, raop_ntp);
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+}
+
+void
+raop_ntp_stop(raop_ntp_t *raop_ntp)
+{
+ assert(raop_ntp);
+
+ /* Check that we are running and thread is not
+ * joined (should never be while still running) */
+ MUTEX_LOCK(raop_ntp->run_mutex);
+ if (!raop_ntp->running || raop_ntp->joined) {
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+ return;
+ }
+ raop_ntp->running = 0;
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopping time thread");
+
+ MUTEX_LOCK(raop_ntp->wait_mutex);
+ COND_SIGNAL(raop_ntp->wait_cond);
+ MUTEX_UNLOCK(raop_ntp->wait_mutex);
+
+ if (raop_ntp->tsock != -1) {
+ closesocket(raop_ntp->tsock);
+ raop_ntp->tsock = -1;
+ }
+
+ THREAD_JOIN(raop_ntp->thread);
+
+ logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopped time thread");
+
+ /* Mark thread as joined */
+ MUTEX_LOCK(raop_ntp->run_mutex);
+ raop_ntp->joined = 1;
+ MUTEX_UNLOCK(raop_ntp->run_mutex);
+}
+
+/**
+ * Converts from a little endian ntp timestamp to nano seconds since the Unix epoch.
+ * Does the same thing as byteutils_get_ntp_timestamp, except its input is an uint64_t
+ * and expected to already be in little endian.
+ * Please note this just converts to a different representation, the clock remains the
+ * same.
+ */
+uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) {
+ uint64_t seconds = ((ntp_timestamp >> 32) & 0xffffffff) - (account_for_epoch_diff ? SECONDS_FROM_1900_TO_1970 : 0);
+ uint64_t fraction = (ntp_timestamp & 0xffffffff);
+ return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32);
+}
+
+uint64_t raop_remote_timestamp_to_nano_seconds(raop_ntp_t *raop_ntp, uint64_t timestamp) {
+ uint64_t seconds = ((timestamp >> 32) & 0xffffffff);
+ if (raop_ntp->time_protocol == NTP) seconds -= SECONDS_FROM_1900_TO_1970;
+ uint64_t fraction = (timestamp & 0xffffffff);
+ return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32);
+}
+/**
+ * Returns the current time in nano seconds according to the local wall clock.
+ * The system Unix time is used as the local wall clock.
+ */
+uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp) {
+ struct timespec time;
+ clock_gettime(CLOCK_REALTIME, &time);
+ return ((uint64_t) time.tv_nsec) + (uint64_t) time.tv_sec * SECOND_IN_NSECS;
+}
+
+/**
+ * Returns the current time in nano seconds according to the remote wall clock.
+ */
+uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp) {
+ MUTEX_LOCK(raop_ntp->sync_params_mutex);
+ int64_t offset = raop_ntp->sync_offset;
+ MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
+ return (uint64_t) ((int64_t) raop_ntp_get_local_time(raop_ntp) + offset);
+}
+
+/**
+ * Returns the local wall clock time in nano seconds for the given point in remote clock time
+ */
+uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time) {
+ MUTEX_LOCK(raop_ntp->sync_params_mutex);
+ int64_t offset = raop_ntp->sync_offset;
+ MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
+ return (uint64_t) ((int64_t) remote_time - offset);
+}
+
+/**
+ * Returns the remote wall clock time in nano seconds for the given point in local clock time
+ */
+uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time) {
+ MUTEX_LOCK(raop_ntp->sync_params_mutex);
+ int64_t offset = raop_ntp->sync_offset;
+ MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
+ return (uint64_t) ((int64_t) local_time + offset);
+}
diff --git a/lib/raop_ntp.h b/lib/raop_ntp.h
new file mode 100644
index 0000000..bf2f5bb
--- /dev/null
+++ b/lib/raop_ntp.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 dsafa22, modified by Florian Draschbacher,
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2021-2023
+ */
+
+#ifndef RAOP_NTP_H
+#define RAOP_NTP_H
+
+#include
+#include
+#include "logger.h"
+
+typedef struct raop_ntp_s raop_ntp_t;
+
+typedef enum timing_protocol_e { NTP, TP_NONE, TP_OTHER, TP_UNSPECIFIED } timing_protocol_t;
+
+void raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_timeouts);
+
+void raop_ntp_stop(raop_ntp_t *raop_ntp);
+
+unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp);
+
+void raop_ntp_destroy(raop_ntp_t *raop_rtp);
+
+uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff);
+uint64_t raop_remote_timestamp_to_nano_seconds(raop_ntp_t *raop_ntp, uint64_t timestamp);
+
+uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp);
+uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp);
+uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time);
+uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time);
+
+#endif //RAOP_NTP_H
diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c
new file mode 100644
index 0000000..62543f7
--- /dev/null
+++ b/lib/raop_rtp.c
@@ -0,0 +1,920 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2021-2023
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "raop_rtp.h"
+#include "raop.h"
+#include "raop_buffer.h"
+#include "netutils.h"
+#include "compat.h"
+#include "logger.h"
+#include "byteutils.h"
+#include "mirror_buffer.h"
+#include "stream.h"
+#include "utils.h"
+
+#define NO_FLUSH (-42)
+
+#define SECOND_IN_NSECS 1000000000
+#define RAOP_RTP_SYNC_DATA_COUNT 8
+#define SEC SECOND_IN_NSECS
+
+#define DELAY_AAC 0.275 //empirical, matches audio latency of about -0.25 sec after first clock sync event
+
+/* note: it is unclear what will happen in the unlikely event that this code is running at the time of the unix-time
+ * epoch event on 2038-01-19 at 3:14:08 UTC ! (but Apple will surely have removed AirPlay "legacy pairing" by then!) */
+
+typedef struct raop_rtp_sync_data_s {
+ uint64_t ntp_time; // The local wall clock time (unix time in usec) at the time of rtp_time
+ uint64_t rtp_time; // The remote rtp clock time corresponding to ntp_time
+} raop_rtp_sync_data_t;
+
+struct raop_rtp_s {
+ logger_t *logger;
+ raop_callbacks_t callbacks;
+
+ // Time and sync
+ raop_ntp_t *ntp;
+ double rtp_clock_rate;
+ int64_t rtp_sync_offset;
+ raop_rtp_sync_data_t sync_data[RAOP_RTP_SYNC_DATA_COUNT];
+ int sync_data_index;
+ uint64_t ntp_start_time;
+ uint64_t rtp_start_time;
+ uint64_t rtp_time;
+ bool rtp_clock_started;
+
+ // Transmission Stats, could be used if a playout buffer is needed
+ // float interarrival_jitter; // As defined by RTP RFC 3550, Section 6.4.1
+ // unsigned int last_packet_transit_time;
+ //int transit = (packet_receive_time - packet_send_time);
+ // int d = transit - last_packet_transit_time;
+ // if (d < 0) d = -d;
+ // interarrival_jitter = (1.f / 16.f) * ((double) d - interarrival_jitter);
+
+ /* Buffer to handle all resends */
+ raop_buffer_t *buffer;
+
+ /* Remote address as sockaddr */
+ struct sockaddr_storage remote_saddr;
+ socklen_t remote_saddr_len;
+
+ /* MUTEX LOCKED VARIABLES START */
+ /* These variables only edited mutex locked */
+ int running;
+ int joined;
+
+ float volume;
+ int volume_changed;
+ unsigned char *metadata;
+ int metadata_len;
+ unsigned char *coverart;
+ int coverart_len;
+ char *dacp_id;
+ char *active_remote_header;
+ unsigned int progress_start;
+ unsigned int progress_curr;
+ unsigned int progress_end;
+ int progress_changed;
+
+ int flush;
+ thread_handle_t thread;
+ mutex_handle_t run_mutex;
+ /* MUTEX LOCKED VARIABLES END */
+
+ /* Remote control and timing ports */
+ unsigned short control_rport;
+
+ /* Sockets for control and data */
+ int csock, dsock;
+
+ /* Local control, timing and data ports */
+ unsigned short control_lport;
+ unsigned short data_lport;
+
+ /* Initialized after the first control packet */
+ struct sockaddr_storage control_saddr;
+ socklen_t control_saddr_len;
+ unsigned short control_seqnum;
+
+ /* audio compression type: ct = 2 (ALAC), ct = 8 (AAC_ELD) (ct = 4 would be AAC-MAIN) */
+ unsigned char ct;
+};
+
+static int
+raop_rtp_parse_remote(raop_rtp_t *raop_rtp, const char *remote, int remotelen)
+{
+ int family;
+ int ret;
+ assert(raop_rtp);
+ if (remotelen == 4) {
+ family = AF_INET;
+ } else if (remotelen == 16) {
+ family = AF_INET6;
+ } else {
+ return -1;
+ }
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp parse remote ip = %s", remote);
+ ret = netutils_parse_address(family, remote,
+ &raop_rtp->remote_saddr,
+ sizeof(raop_rtp->remote_saddr));
+ if (ret < 0) {
+ return -1;
+ }
+ raop_rtp->remote_saddr_len = ret;
+ return 0;
+}
+
+raop_rtp_t *
+raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const char *remote,
+ int remotelen, const unsigned char *aeskey, const unsigned char *aesiv)
+{
+ raop_rtp_t *raop_rtp;
+
+ assert(logger);
+ assert(callbacks);
+
+ raop_rtp = calloc(1, sizeof(raop_rtp_t));
+ if (!raop_rtp) {
+ return NULL;
+ }
+ raop_rtp->logger = logger;
+ raop_rtp->ntp = ntp;
+
+ raop_rtp->rtp_sync_offset = 0;
+ raop_rtp->sync_data_index = 0;
+ for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; ++i) {
+ raop_rtp->sync_data[i].ntp_time = 0;
+ raop_rtp->sync_data[i].rtp_time = 0;
+ }
+ raop_rtp->ntp_start_time = 0;
+ raop_rtp->rtp_start_time = 0;
+ raop_rtp->rtp_clock_started = false;
+
+
+ raop_rtp->dacp_id = NULL;
+ raop_rtp->active_remote_header = NULL;
+ raop_rtp->metadata = NULL;
+ raop_rtp->coverart = NULL;
+
+ memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t));
+ raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv);
+ if (!raop_rtp->buffer) {
+ free(raop_rtp);
+ return NULL;
+ }
+ if (raop_rtp_parse_remote(raop_rtp, remote, remotelen) < 0) {
+ free(raop_rtp);
+ return NULL;
+ }
+
+ raop_rtp->running = 0;
+ raop_rtp->joined = 1;
+ raop_rtp->flush = NO_FLUSH;
+
+ MUTEX_CREATE(raop_rtp->run_mutex);
+ return raop_rtp;
+}
+
+
+void
+raop_rtp_destroy(raop_rtp_t *raop_rtp)
+{
+ if (raop_rtp) {
+ raop_rtp_stop(raop_rtp);
+ MUTEX_DESTROY(raop_rtp->run_mutex);
+ raop_buffer_destroy(raop_rtp->buffer);
+ free(raop_rtp->metadata);
+ free(raop_rtp->coverart);
+ free(raop_rtp->dacp_id);
+ free(raop_rtp->active_remote_header);
+ free(raop_rtp);
+ }
+}
+
+static int
+raop_rtp_resend_callback(void *opaque, unsigned short seqnum, unsigned short count)
+{
+ raop_rtp_t *raop_rtp = opaque;
+ unsigned char packet[8];
+ unsigned short ourseqnum;
+ struct sockaddr *addr;
+ socklen_t addrlen;
+ int ret;
+
+ addr = (struct sockaddr *)&raop_rtp->control_saddr;
+ addrlen = raop_rtp->control_saddr_len;
+
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp got resend request %d %d", seqnum, count);
+ ourseqnum = raop_rtp->control_seqnum++;
+
+ /* Fill the request buffer */
+ packet[0] = 0x80;
+ packet[1] = 0x55|0x80;
+ packet[2] = (ourseqnum >> 8);
+ packet[3] = ourseqnum;
+ packet[4] = (seqnum >> 8);
+ packet[5] = seqnum;
+ packet[6] = (count >> 8);
+ packet[7] = count;
+
+ ret = sendto(raop_rtp->csock, (const char *)packet, sizeof(packet), 0, addr, addrlen);
+ if (ret == -1) {
+ logger_log(raop_rtp->logger, LOGGER_WARNING, "raop_rtp resend failed: %d", SOCKET_GET_ERROR());
+ }
+
+ return 0;
+}
+
+static int
+raop_rtp_init_sockets(raop_rtp_t *raop_rtp, int use_ipv6)
+{
+ assert(raop_rtp);
+
+ unsigned short cport = raop_rtp->control_lport;
+ unsigned short dport = raop_rtp->data_lport;
+ int csock = netutils_init_socket(&cport, use_ipv6, 1);
+ int dsock = netutils_init_socket(&dport, use_ipv6, 1);
+
+ if (csock == -1 || dsock == -1) {
+ goto sockets_cleanup;
+ }
+
+ /* Set socket descriptors */
+ raop_rtp->csock = csock;
+ raop_rtp->dsock = dsock;
+
+ /* Set port values */
+ raop_rtp->control_lport = cport;
+ raop_rtp->data_lport = dport;
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp local control port socket %d port UDP %d", csock, cport);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp local data port socket %d port UDP %d", dsock, dport);
+ return 0;
+
+ sockets_cleanup:
+ if (csock != -1) closesocket(csock);
+ if (dsock != -1) closesocket(dsock);
+ return -1;
+}
+
+static int
+raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data)
+{
+ int flush;
+ float volume;
+ int volume_changed;
+ unsigned char *metadata;
+ int metadata_len;
+ unsigned char *coverart;
+ int coverart_len;
+ char *dacp_id;
+ char *active_remote_header;
+ unsigned int progress_start;
+ unsigned int progress_curr;
+ unsigned int progress_end;
+ int progress_changed;
+
+ assert(raop_rtp);
+
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ if (!raop_rtp->running) {
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+ return 1;
+ }
+
+ /* Read the volume level */
+ volume = raop_rtp->volume;
+ volume_changed = raop_rtp->volume_changed;
+ raop_rtp->volume_changed = 0;
+
+ /* Read the flush value */
+ flush = raop_rtp->flush;
+ raop_rtp->flush = NO_FLUSH;
+
+ /* Read the metadata */
+ metadata = raop_rtp->metadata;
+ metadata_len = raop_rtp->metadata_len;
+ raop_rtp->metadata = NULL;
+ raop_rtp->metadata_len = 0;
+
+ /* Read the coverart */
+ coverart = raop_rtp->coverart;
+ coverart_len = raop_rtp->coverart_len;
+ raop_rtp->coverart = NULL;
+ raop_rtp->coverart_len = 0;
+
+ /* Read DACP remote control data */
+ dacp_id = raop_rtp->dacp_id;
+ active_remote_header = raop_rtp->active_remote_header;
+ raop_rtp->dacp_id = NULL;
+ raop_rtp->active_remote_header = NULL;
+
+ /* Read the progress values */
+ progress_start = raop_rtp->progress_start;
+ progress_curr = raop_rtp->progress_curr;
+ progress_end = raop_rtp->progress_end;
+ progress_changed = raop_rtp->progress_changed;
+ raop_rtp->progress_changed = 0;
+
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+
+ /* Call set_volume callback if changed */
+ if (volume_changed) {
+ //raop_buffer_flush(raop_rtp->buffer, flush); /* seems to be unnecessary, may cause audio artefacts */
+ if (raop_rtp->callbacks.audio_set_volume) {
+ raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, volume);
+ }
+ }
+
+ /* Handle flush if requested */
+ if (flush != NO_FLUSH) {
+ if (raop_rtp->callbacks.audio_flush) {
+ raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls);
+ }
+ }
+
+ if (metadata != NULL) {
+ if (raop_rtp->callbacks.audio_set_metadata) {
+ raop_rtp->callbacks.audio_set_metadata(raop_rtp->callbacks.cls, metadata, metadata_len);
+ }
+ free(metadata);
+ metadata = NULL;
+ }
+
+ if (coverart != NULL) {
+ if (raop_rtp->callbacks.audio_set_coverart) {
+ raop_rtp->callbacks.audio_set_coverart(raop_rtp->callbacks.cls, coverart, coverart_len);
+ }
+ free(coverart);
+ coverart = NULL;
+ }
+ if (dacp_id && active_remote_header) {
+ if (raop_rtp->callbacks.audio_remote_control_id) {
+ raop_rtp->callbacks.audio_remote_control_id(raop_rtp->callbacks.cls, dacp_id, active_remote_header);
+ }
+ free(dacp_id);
+ free(active_remote_header);
+ dacp_id = NULL;
+ active_remote_header = NULL;
+ }
+
+ if (progress_changed) {
+ if (raop_rtp->callbacks.audio_set_progress) {
+ raop_rtp->callbacks.audio_set_progress(raop_rtp->callbacks.cls, progress_start, progress_curr, progress_end);
+ }
+ }
+ return 0;
+}
+
+void raop_rtp_sync_clock(raop_rtp_t *raop_rtp, uint64_t *ntp_time, uint64_t *rtp_time) {
+ /* ntp_time = (uint64_t)(((int64_t)(raop_rtp->rtp_clock_rate * rtp_time)) + raop_rtp->rtp_sync_offset) */
+ int latest, valid_data_count = 0;
+ uint64_t ntp_sum = 0, rtp_sum = 0;
+ double offset = ((double) *ntp_time) - raop_rtp->rtp_clock_rate * *rtp_time;
+ int64_t correction = 0;
+
+ raop_rtp->sync_data_index = (raop_rtp->sync_data_index + 1) % RAOP_RTP_SYNC_DATA_COUNT;
+ latest = raop_rtp->sync_data_index;
+ raop_rtp->sync_data[latest].rtp_time = *rtp_time;
+ raop_rtp->sync_data[latest].ntp_time = *ntp_time;
+
+ for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; i++) {
+ if (raop_rtp->sync_data[i].ntp_time == 0) continue;
+ valid_data_count++;
+ if (i == latest) continue;
+ ntp_sum += *ntp_time - raop_rtp->sync_data[i].ntp_time;
+ rtp_sum += *rtp_time - raop_rtp->sync_data[i].rtp_time;
+ }
+
+ if (valid_data_count > 1) {
+ correction -= raop_rtp->rtp_sync_offset;
+ offset += (((double) ntp_sum) - raop_rtp->rtp_clock_rate * rtp_sum) / valid_data_count;
+ }
+ raop_rtp->rtp_sync_offset = (int64_t) offset;
+ correction += raop_rtp->rtp_sync_offset;
+
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "dataset %d raop_rtp sync correction=%lld, rtp_sync_offset = %lld ",
+ valid_data_count, correction, raop_rtp->rtp_sync_offset);
+}
+
+uint64_t rtp64_time (raop_rtp_t *raop_rtp, const uint32_t *rtp32) {
+ /* convert from 32-bit to 64-bit rtp time:
+ * the rtp_time 32-bit epoch at 44.1kHz has length of about 27 hours
+ * using 64-bit rtp time avoids any epoch issues.
+ * initial call sets epoch to 1; subsequent calls maintain consistent epoch.
+ * (assumes successive calls are close in time) */
+
+ if (raop_rtp->rtp_clock_started) {
+ uint32_t diff1 = *rtp32 - ((uint32_t) raop_rtp->rtp_time);
+ uint32_t diff2 = ((uint32_t) raop_rtp->rtp_time) - *rtp32;
+ if (diff1 <= diff2) {
+ raop_rtp->rtp_time += (uint64_t) diff1;
+ } else {
+ raop_rtp->rtp_time -= (uint64_t) diff2;
+ }
+ } else {
+ raop_rtp->rtp_time = (0x01ULL << 32 ) + (uint64_t) *rtp32;
+ raop_rtp->rtp_start_time = raop_rtp->rtp_time;
+ raop_rtp->rtp_clock_started = true;
+ }
+ //assert(*rtp32 == (uint32_t) raop_rtp->rtp_time);
+ return raop_rtp->rtp_time;
+}
+
+static THREAD_RETVAL
+raop_rtp_thread_udp(void *arg)
+{
+ raop_rtp_t *raop_rtp = arg;
+ unsigned char packet[RAOP_PACKET_LEN];
+ unsigned int packetlen;
+ struct sockaddr_storage saddr;
+ socklen_t saddrlen;
+ bool got_remote_control_saddr = false;
+
+ /* for initial rtp to ntp conversions */
+ bool have_synced = false;
+ bool no_data_yet = true;
+ unsigned char no_data_marker[] = {0x00, 0x68, 0x34, 0x00 };
+ int rtp_count = 0;
+ double sync_adjustment = 0;
+ unsigned short seqnum1 = 0, seqnum2 = 0;
+
+ assert(raop_rtp);
+ bool logger_debug = (logger_get_level(raop_rtp->logger) >= LOGGER_DEBUG);
+ raop_rtp->ntp_start_time = raop_ntp_get_local_time(raop_rtp->ntp);
+ raop_rtp->rtp_clock_started = false;
+ for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; i++) {
+ raop_rtp->sync_data[i].ntp_time = 0;
+ }
+
+ int no_resend = (raop_rtp->control_rport == 0); /* true when control_rport is not set */
+
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp start_time = %8.6f (raop_rtp audio)",
+ ((double) raop_rtp->ntp_start_time) / SEC);
+
+ while(1) {
+ fd_set rfds;
+ struct timeval tv;
+ int nfds, ret;
+ /* Check if we are still running and process callbacks */
+ if (raop_rtp_process_events(raop_rtp, NULL)) {
+ break;
+ }
+
+ /* Set timeout value to 5ms */
+ tv.tv_sec = 0;
+ tv.tv_usec = 5000;
+
+ /* Get the correct nfds value */
+ nfds = raop_rtp->csock+1;
+ if (raop_rtp->dsock >= nfds)
+ nfds = raop_rtp->dsock+1;
+
+ /* Set rfds and call select */
+ FD_ZERO(&rfds);
+ FD_SET(raop_rtp->csock, &rfds);
+ FD_SET(raop_rtp->dsock, &rfds);
+
+ ret = select(nfds, &rfds, NULL, NULL, &tv);
+ if (ret == 0) {
+ /* Timeout happened */
+ continue;
+ } else if (ret == -1) {
+ logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp error in select");
+ break;
+ }
+
+ if (FD_ISSET(raop_rtp->csock, &rfds)) {
+ if (got_remote_control_saddr== false) {
+ saddrlen = sizeof(saddr);
+ packetlen = recvfrom(raop_rtp->csock, (char *)packet, sizeof(packet), 0,
+ (struct sockaddr *)&saddr, &saddrlen);
+ if (packetlen > 0) {
+ memcpy(&raop_rtp->control_saddr, &saddr, saddrlen);
+ raop_rtp->control_saddr_len = saddrlen;
+ got_remote_control_saddr = true;
+ }
+ } else {
+ packetlen = recvfrom(raop_rtp->csock, (char *)packet, sizeof(packet), 0, NULL, NULL);
+ }
+ int type_c = packet[1] & ~0x80;
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "\nraop_rtp type_c 0x%02x, packetlen = %d", type_c, packetlen);
+
+ if (type_c == 0x56 && packetlen >= 8) {
+ /* Handle resent data packet, which begins at offset 4 of these packets */
+ unsigned char *resent_packet = &packet[4];
+ unsigned int resent_packetlen = packetlen - 4;
+ unsigned short seqnum = byteutils_get_short_be(resent_packet, 2);
+ if (resent_packetlen >= 12) {
+ uint32_t timestamp = byteutils_get_int_be(resent_packet, 4);
+ uint64_t rtp_time = rtp64_time(raop_rtp, ×tamp);
+ uint64_t ntp_time = 0;
+ if (have_synced) {
+ ntp_time = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp_time));
+ }
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp resent audio packet: seqnum=%u", seqnum);
+ int result = raop_buffer_enqueue(raop_rtp->buffer, resent_packet, resent_packetlen, &ntp_time, &rtp_time, 1);
+ assert(result >= 0);
+ } else if (logger_debug) {
+ /* type_c = 0x56 packets with length 8 have been reported */
+ char *str = utils_data_to_string(packet, packetlen, 16);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "Received empty resent audio packet length %d, seqnum=%u:\n%s",
+ packetlen, seqnum, str);
+ free (str);
+ }
+ } else if (type_c == 0x54 && packetlen >= 20) {
+ /* packet[0] = 0x90 (first sync ?) or 0x80 (subsequent ones)
+ * packet[1] = 0xd4, (0xd4 && ~0x80 = type 0x54)
+ * packet[2:3] = 0x00 0x04
+ * packet[4:7] : sync_rtp (big-endian uint32_t)
+ * packet[8:15]: remote ntp timestamp (big-endian uint64_t)
+ * packet[16:20]: next_rtp (big-endian uint32_t)
+ * next_rtp = sync_rtp + 7497 = 441 * 17 (0.17 sec) for AAC-ELD
+ * next_rtp = sync_rtp + 77175 = 441 * 175 (1.75 sec) for ALAC */
+
+ // The unit for the rtp clock is 1 / sample rate = 1 / 44100
+ uint32_t sync_rtp = byteutils_get_int_be(packet, 4);
+ uint64_t sync_rtp64 = rtp64_time(raop_rtp, &sync_rtp);
+ if (have_synced == false) {
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "first audio rtp sync");
+ have_synced = true;
+ }
+ uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
+ uint64_t sync_ntp_remote = raop_remote_timestamp_to_nano_seconds(raop_rtp->ntp, sync_ntp_raw);
+ if (logger_debug) {
+ uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote);
+ char *str = utils_data_to_string(packet, packetlen, 20);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG,
+ "raop_rtp sync: client ntp=%8.6f, ntp = %8.6f, ntp_start_time %8.6f\nts_client = %8.6f sync_rtp=%u\n%s",
+ (double) sync_ntp_remote / SEC, (double) sync_ntp_local / SEC,
+ (double) raop_rtp->ntp_start_time / SEC, (double) sync_ntp_remote / SEC, sync_rtp, str);
+ free(str);
+ }
+ raop_rtp_sync_clock(raop_rtp, &sync_ntp_remote, &sync_rtp64);
+ } else if (logger_debug) {
+ char *str = utils_data_to_string(packet, packetlen, 16);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp unknown udp control packet\n%s", str);
+ free(str);
+ }
+ }
+
+ /* rtp audio data packets:
+ * packet[0] 0x80
+ * packet[1] 0x60 = 96
+ * packet[2:3] seqnum (big-endian unsigned short)
+ * packet[4:7] rtp timestamp (big-endian unsigned int)
+ * packet[8:11] 0x00 0x00 0x00 0x00
+ * packet[12:packetlen - 1] encrypted audio payload
+ * For (AAC-ELD only), the payload of initial packets at the start of
+ * the stream may be replaced by a 4-byte "no_data_marker" 0x00 0x68 0x34 0x00 */
+
+ /* consecutive AAC-ELD rtp timestamps differ by spf = 480
+ * consecutive ALAC rtp timestamps differ by spf = 352
+ * both have PCM uncompressed sampling rate = 441000 Hz */
+
+ /* clock time in microseconds advances at (rtp_timestamp * 1000000)/44100 between frames */
+
+ /* every AAC-ELD packet is sent three times: 0 0 1 0 1 2 1 2 3 2 3 4 ...
+ * (after decoding AAC-ELD into PCM, the sound frame is three times bigger)
+ * ALAC packets are sent once only 0 1 2 3 4 5 ... */
+
+ /* When the AAC-ELD audio stream starts, the initial packets are length-16 packets with
+ * a four-byte "no_data_marker" 0x00 0x68 0x34 0x00 replacing the payload.
+ * The 12-byte packetheader contains a secnum and rtp_timestamp, and each packets is sent
+ * three times; the secnum and rtp_timestamp increment according to the same pattern as
+ * AAC-ELD packets with audio content.*/
+
+ /* When the ALAC audio stream starts, the initial packets are length-44 packets with
+ * the same 32-byte encrypted payload which after decryption is the beginning of a
+ * 32-byte ALAC packet, presumably with format information, but not actual audio data.
+ * The secnum and rtp_timestamp in the packet header increment according to the same
+ * pattern as ALAC packets with audio content */
+
+ /* The first ALAC packet with data seems to be decoded just before the first sync event
+ * so its dequeuing should be delayed until the first rtp sync has occurred */
+
+
+ if (FD_ISSET(raop_rtp->dsock, &rfds)) {
+ //logger_log(raop_rtp->logger, LOGGER_INFO, "Would have data packet in queue");
+ // Receiving audio data here
+ saddrlen = sizeof(saddr);
+ packetlen = recvfrom(raop_rtp->dsock, (char *)packet, sizeof(packet), 0, NULL, NULL);
+ // rtp payload type
+ //int type_d = packet[1] & ~0x80;
+ //logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp_thread_udp type_d 0x%02x, packetlen = %d", type_d, packetlen);
+
+ if (packetlen < 12) {
+ if (logger_debug) {
+ char *str = utils_data_to_string(packet, packetlen, 16);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "Received short type_d = 0x%2x packet with length %d:\n%s",
+ packet[1] & ~0x80, packetlen, str);
+ free (str);
+ }
+ continue;
+ }
+
+ uint32_t rtp_timestamp = byteutils_get_int_be(packet, 4);
+ uint64_t rtp_time = rtp64_time(raop_rtp, &rtp_timestamp);
+ uint64_t ntp_time = 0;
+
+ if (raop_rtp->ct == 2 && packetlen == 44) continue; /* ignore the ALAC packets with format information only. */
+
+ if (have_synced) {
+ ntp_time = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp_time));
+ } else if (packetlen == 16 && memcmp(packet + 12, no_data_marker, 4) == 0) {
+ /* use the special "no_data" packet to help determine an initial offset before the first rtp sync.
+ * until the first rtp sync occurs, we don't know the exact client ntp timestamp that matches the client rtp timestamp */
+ if (no_data_yet) {
+ int64_t sync_ntp = ((int64_t) raop_ntp_get_local_time(raop_rtp->ntp)) - ((int64_t) raop_rtp->ntp_start_time) ;
+ int64_t sync_rtp = ((int64_t) rtp_time) - ((int64_t) raop_rtp->rtp_start_time);
+ unsigned short seqnum = byteutils_get_short_be(packet, 2);
+ if (rtp_count == 0) {
+ sync_adjustment = ((double) sync_ntp);
+ rtp_count = 1;
+ seqnum1 = seqnum;
+ seqnum2 = seqnum;
+ }
+ if (seqnum2 != seqnum) { /* for AAC-ELD only use copy 1 of the 3 copies of each frame */
+ rtp_count++;
+ sync_adjustment += (((double) sync_ntp) - raop_rtp->rtp_clock_rate * sync_rtp - sync_adjustment) / rtp_count;
+ }
+ seqnum2 = seqnum1;
+ seqnum1 = seqnum;
+ }
+ continue;
+ } else {
+ no_data_yet = false;
+ }
+ int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, &ntp_time, &rtp_time, 1);
+ assert(result >= 0);
+
+ if (raop_rtp->ct == 2 && !have_synced) {
+ /* in ALAC Audio-only mode wait until the first sync before dequeing */
+ continue;
+ } else {
+ // Render continuous buffer entries
+ void *payload = NULL;
+ unsigned int payload_size;
+ unsigned short seqnum;
+ uint64_t rtp64_timestamp;
+ uint64_t ntp_timestamp;
+
+ while ((payload = raop_buffer_dequeue(raop_rtp->buffer, &payload_size, &ntp_timestamp, &rtp64_timestamp, &seqnum, no_resend))) {
+ audio_decode_struct audio_data;
+ audio_data.rtp_time = rtp64_timestamp;
+ audio_data.seqnum = seqnum;
+ audio_data.data_len = payload_size;
+ audio_data.data = payload;
+ audio_data.ct = raop_rtp->ct;
+ if (have_synced) {
+ if (ntp_timestamp == 0) {
+ ntp_timestamp = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp64_timestamp));
+ }
+ audio_data.ntp_time_remote = ntp_timestamp;
+ audio_data.ntp_time_local = raop_ntp_convert_remote_time(raop_rtp->ntp, audio_data.ntp_time_remote);
+ audio_data.sync_status = 1;
+ } else {
+ double elapsed_time = raop_rtp->rtp_clock_rate * (rtp64_timestamp - raop_rtp->rtp_start_time) + sync_adjustment
+ + DELAY_AAC * SECOND_IN_NSECS;
+ audio_data.ntp_time_local = raop_rtp->ntp_start_time + (uint64_t) elapsed_time;
+ audio_data.ntp_time_remote = raop_ntp_convert_local_time(raop_rtp->ntp, audio_data.ntp_time_local);
+ audio_data.sync_status = 0;
+ }
+ raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, raop_rtp->ntp, &audio_data);
+ free(payload);
+ if (logger_debug) {
+ uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp);
+ int64_t latency = ((int64_t) ntp_now) - ((int64_t) audio_data.ntp_time_local);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG,
+ "raop_rtp audio: now = %8.6f, ntp = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u",
+ (double) ntp_now / SEC, (double) audio_data.ntp_time_local / SEC, (double) latency / SEC,
+ (uint32_t) rtp64_timestamp, seqnum);
+ }
+ }
+
+ /* Handle possible resend requests */
+ if (!no_resend) {
+ raop_buffer_handle_resends(raop_rtp->buffer, raop_rtp_resend_callback, raop_rtp);
+ }
+ }
+ }
+ }
+
+ // Ensure running reflects the actual state
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->running = false;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp exiting thread");
+
+ return 0;
+}
+
+// Start rtp service, using two udp ports
+void
+raop_rtp_start_audio(raop_rtp_t *raop_rtp, unsigned short *control_rport, unsigned short *control_lport,
+ unsigned short *data_lport, unsigned char *ct, unsigned int *sr)
+{
+ logger_log(raop_rtp->logger, LOGGER_INFO, "raop_rtp starting audio");
+ int use_ipv6 = 0;
+
+ assert(raop_rtp);
+
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ if (raop_rtp->running || !raop_rtp->joined) {
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+ return;
+ }
+
+ raop_rtp->ct = *ct;
+ raop_rtp->rtp_clock_rate = SECOND_IN_NSECS / *sr;
+
+ /* Initialize ports and sockets */
+ raop_rtp->control_lport = *control_lport;
+ raop_rtp->data_lport = *data_lport;
+ raop_rtp->control_rport = *control_rport;
+ if (raop_rtp->remote_saddr.ss_family == AF_INET6) {
+ use_ipv6 = 1;
+ }
+ //use_ipv6 = 0;
+ if (raop_rtp_init_sockets(raop_rtp, use_ipv6) < 0) {
+ logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp initializing sockets failed");
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+ return;
+ }
+ *control_lport = raop_rtp->control_lport;
+ *data_lport = raop_rtp->data_lport;
+ /* Create the thread and initialize running values */
+ raop_rtp->running = 1;
+ raop_rtp->joined = 0;
+
+ THREAD_CREATE(raop_rtp->thread, raop_rtp_thread_udp, raop_rtp);
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume)
+{
+ assert(raop_rtp);
+
+ if (volume > 0.0f) {
+ volume = 0.0f;
+ } else if (volume < -144.0f) {
+ volume = -144.0f;
+ }
+
+ /* Set volume in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->volume = volume;
+ raop_rtp->volume_changed = 1;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen)
+{
+ unsigned char *metadata;
+
+ assert(raop_rtp);
+
+ if (datalen <= 0) {
+ return;
+ }
+ metadata = malloc(datalen);
+ assert(metadata);
+ memcpy(metadata, data, datalen);
+
+ /* Set metadata in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->metadata = metadata;
+ raop_rtp->metadata_len = datalen;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen)
+{
+ unsigned char *coverart;
+
+ assert(raop_rtp);
+
+ if (datalen <= 0) {
+ return;
+ }
+ coverart = malloc(datalen);
+ assert(coverart);
+ memcpy(coverart, data, datalen);
+
+ /* Set coverart in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->coverart = coverart;
+ raop_rtp->coverart_len = datalen;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header)
+{
+ assert(raop_rtp);
+
+ if (!dacp_id || !active_remote_header) {
+ return;
+ }
+
+ /* Set dacp stuff in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ if (raop_rtp->dacp_id) {
+ free(raop_rtp->dacp_id);
+ }
+ raop_rtp->dacp_id = strdup(dacp_id);
+ if (raop_rtp->active_remote_header) {
+ free(raop_rtp->active_remote_header);
+ }
+ raop_rtp->active_remote_header = strdup(active_remote_header);
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end)
+{
+ assert(raop_rtp);
+
+ /* Set progress in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->progress_start = start;
+ raop_rtp->progress_curr = curr;
+ raop_rtp->progress_end = end;
+ raop_rtp->progress_changed = 1;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq)
+{
+ assert(raop_rtp);
+
+ /* Call flush in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->flush = next_seq;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_stop(raop_rtp_t *raop_rtp)
+{
+ assert(raop_rtp);
+
+ /* Check that we are running and thread is not
+ * joined (should never be while still running) */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ if (!raop_rtp->running || raop_rtp->joined) {
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+ return;
+ }
+ raop_rtp->running = 0;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+
+ /* Join the thread */
+ THREAD_JOIN(raop_rtp->thread);
+
+ if (raop_rtp->csock != -1) closesocket(raop_rtp->csock);
+ if (raop_rtp->dsock != -1) closesocket(raop_rtp->dsock);
+
+ /* Flush buffer into initial state */
+ raop_buffer_flush(raop_rtp->buffer, -1);
+
+ /* Mark thread as joined */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->joined = 1;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+int
+raop_rtp_is_running(raop_rtp_t *raop_rtp)
+{
+ assert(raop_rtp);
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ int running = raop_rtp->running;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+ return running;
+}
diff --git a/lib/raop_rtp.h b/lib/raop_rtp.h
new file mode 100644
index 0000000..e1276bd
--- /dev/null
+++ b/lib/raop_rtp.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2021-2023
+ */
+
+#ifndef RAOP_RTP_H
+#define RAOP_RTP_H
+
+/* For raop_callbacks_t */
+#include "raop.h"
+#include "logger.h"
+#include "raop_ntp.h"
+
+#define RAOP_AESIV_LEN 16
+#define RAOP_AESKEY_LEN 16
+#define RAOP_PACKET_LEN 32768
+
+typedef struct raop_rtp_s raop_rtp_t;
+
+raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const char *remote,
+ int remotelen, const unsigned char *aeskey, const unsigned char *aesiv);
+
+void raop_rtp_start_audio(raop_rtp_t *raop_rtp, unsigned short *control_rport, unsigned short *control_lport,
+ unsigned short *data_lport, unsigned char *ct, unsigned int *sr);
+
+void raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume);
+void raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen);
+void raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen);
+void raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header);
+void raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end);
+void raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq);
+void raop_rtp_stop(raop_rtp_t *raop_rtp);
+int raop_rtp_is_running(raop_rtp_t *raop_rtp);
+void raop_rtp_destroy(raop_rtp_t *raop_rtp);
+
+#endif
diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c
new file mode 100644
index 0000000..bc88472
--- /dev/null
+++ b/lib/raop_rtp_mirror.c
@@ -0,0 +1,923 @@
+/*
+ * Copyright (c) 2019 dsafa22, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *==================================================================
+ * modified by fduncanh 2021-2023
+ */
+
+#include "raop_rtp_mirror.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#else
+#include
+#endif
+
+#include "raop.h"
+#include "netutils.h"
+#include "compat.h"
+#include "logger.h"
+#include "byteutils.h"
+#include "mirror_buffer.h"
+#include "stream.h"
+#include "utils.h"
+#include "plist/plist.h"
+
+#ifdef _WIN32
+#define CAST (char *)
+/* are these keepalive settings for WIN32 correct? */
+/* (taken from https://github.com/wegank/ludimus) */
+#define TCP_KEEPALIVE 3
+#define TCP_KEEPCNT 16
+#define TCP_KEEPIDLE TCP_KEEPALIVE
+#define TCP_KEEPINTVL 17
+#else
+#define CAST
+#endif
+
+#define SECOND_IN_NSECS 1000000000UL
+#define SEC SECOND_IN_NSECS
+
+/* for MacOS, where SOL_TCP and TCP_KEEPIDLE are not defined */
+#if !defined(SOL_TCP) && defined(IPPROTO_TCP)
+#define SOL_TCP IPPROTO_TCP
+#endif
+#if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE)
+#define TCP_KEEPIDLE TCP_KEEPALIVE
+#endif
+
+//struct h264codec_s {
+// unsigned char compatibility;
+// short pps_size;
+// short sps_size;
+// unsigned char level;
+// unsigned char number_of_pps;
+// unsigned char* picture_parameter_set;
+// unsigned char profile_high;
+// unsigned char reserved_3_and_sps;
+// unsigned char reserved_6_and_nal;
+// unsigned char* sequence_parameter_set;
+// unsigned char version;
+//};
+
+struct raop_rtp_mirror_s {
+ logger_t *logger;
+ raop_callbacks_t callbacks;
+ raop_ntp_t *ntp;
+
+ /* mirror buffer for decryption */
+ mirror_buffer_t *buffer;
+
+ /* Remote address as sockaddr */
+ struct sockaddr_storage remote_saddr;
+ socklen_t remote_saddr_len;
+
+ /* MUTEX LOCKED VARIABLES START */
+ /* These variables only edited mutex locked */
+ int running;
+ int joined;
+
+ int flush;
+ thread_handle_t thread_mirror;
+ mutex_handle_t run_mutex;
+
+ /* MUTEX LOCKED VARIABLES END */
+ int mirror_data_sock;
+
+ unsigned short mirror_data_lport;
+
+ /* switch for displaying client FPS data */
+ uint8_t show_client_FPS_data;
+};
+
+static int
+raop_rtp_mirror_parse_remote(raop_rtp_mirror_t *raop_rtp_mirror, const char *remote, int remotelen)
+{
+ int family;
+ int ret;
+ assert(raop_rtp_mirror);
+ if (remotelen == 4) {
+ family = AF_INET;
+ } else if (remotelen == 16) {
+ family = AF_INET6;
+ } else {
+ return -1;
+ }
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror parse remote ip = %s", remote);
+ ret = netutils_parse_address(family, remote,
+ &raop_rtp_mirror->remote_saddr,
+ sizeof(raop_rtp_mirror->remote_saddr));
+ if (ret < 0) {
+ return -1;
+ }
+ raop_rtp_mirror->remote_saddr_len = ret;
+ return 0;
+}
+
+#define NO_FLUSH (-42)
+raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
+ const char *remote, int remotelen, const unsigned char *aeskey)
+{
+ raop_rtp_mirror_t *raop_rtp_mirror;
+
+ assert(logger);
+ assert(callbacks);
+
+ raop_rtp_mirror = calloc(1, sizeof(raop_rtp_mirror_t));
+ if (!raop_rtp_mirror) {
+ return NULL;
+ }
+ raop_rtp_mirror->logger = logger;
+ raop_rtp_mirror->ntp = ntp;
+
+ memcpy(&raop_rtp_mirror->callbacks, callbacks, sizeof(raop_callbacks_t));
+ raop_rtp_mirror->buffer = mirror_buffer_init(logger, aeskey);
+ if (!raop_rtp_mirror->buffer) {
+ free(raop_rtp_mirror);
+ return NULL;
+ }
+ if (raop_rtp_mirror_parse_remote(raop_rtp_mirror, remote, remotelen) < 0) {
+ free(raop_rtp_mirror);
+ return NULL;
+ }
+ raop_rtp_mirror->running = 0;
+ raop_rtp_mirror->joined = 1;
+ raop_rtp_mirror->flush = NO_FLUSH;
+
+ MUTEX_CREATE(raop_rtp_mirror->run_mutex);
+ return raop_rtp_mirror;
+}
+
+void
+raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID)
+{
+ mirror_buffer_init_aes(raop_rtp_mirror->buffer, streamConnectionID);
+}
+
+#define RAOP_PACKET_LEN 32768
+/**
+ * Mirror
+ */
+static THREAD_RETVAL
+raop_rtp_mirror_thread(void *arg)
+{
+ raop_rtp_mirror_t *raop_rtp_mirror = arg;
+ assert(raop_rtp_mirror);
+
+ int stream_fd = -1;
+ unsigned char packet[128];
+ memset(packet, 0 , 128);
+ unsigned char* sps_pps = NULL;
+ bool prepend_sps_pps = false;
+ int sps_pps_len = 0;
+ unsigned char* payload = NULL;
+ unsigned int readstart = 0;
+ bool conn_reset = false;
+ uint64_t ntp_timestamp_nal = 0;
+ uint64_t ntp_timestamp_raw = 0;
+ uint64_t ntp_timestamp_remote = 0;
+ uint64_t ntp_timestamp_local = 0;
+ unsigned char nal_start_code[4] = { 0x00, 0x00, 0x00, 0x01 };
+ bool logger_debug = (logger_get_level(raop_rtp_mirror->logger) >= LOGGER_DEBUG);
+ bool h265_video = false;
+ video_codec_t codec;
+ const char h264[] = "h264";
+ const char h265[] = "h265";
+ bool unsupported_codec = false;
+ bool video_stream_suspended = false;
+
+ while (1) {
+ fd_set rfds;
+ struct timeval tv;
+ int nfds, ret;
+ MUTEX_LOCK(raop_rtp_mirror->run_mutex);
+ if (!raop_rtp_mirror->running) {
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+ logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror->running is no longer true");
+ break;
+ }
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+
+ /* Set timeout valu to 5ms */
+ tv.tv_sec = 0;
+ tv.tv_usec = 5000;
+
+ /* Get the correct nfds value and set rfds */
+ FD_ZERO(&rfds);
+ if (stream_fd == -1) {
+ FD_SET(raop_rtp_mirror->mirror_data_sock, &rfds);
+ nfds = raop_rtp_mirror->mirror_data_sock+1;
+ } else {
+ FD_SET(stream_fd, &rfds);
+ nfds = stream_fd+1;
+ }
+ ret = select(nfds, &rfds, NULL, NULL, &tv);
+ if (ret == 0) {
+ /* Timeout happened */
+ continue;
+ } else if (ret == -1) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in select");
+ break;
+ }
+
+ if (stream_fd == -1 &&
+ (raop_rtp_mirror && raop_rtp_mirror->mirror_data_sock >= 0) &&
+ FD_ISSET(raop_rtp_mirror->mirror_data_sock, &rfds)) {
+ struct sockaddr_storage saddr;
+ socklen_t saddrlen;
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror accepting client");
+ saddrlen = sizeof(saddr);
+ stream_fd = accept(raop_rtp_mirror->mirror_data_sock, (struct sockaddr *)&saddr, &saddrlen);
+ if (stream_fd == -1) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR,
+ "raop_rtp_mirror error in accept %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ break;
+ }
+
+ // We're calling recv for a certain amount of data, so we need a timeout
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 5000;
+ if (setsockopt(stream_fd, SOL_SOCKET, SO_RCVTIMEO, CAST &tv, sizeof(tv)) < 0) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR,
+ "raop_rtp_mirror could not set stream socket timeout %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ break;
+ }
+
+ int option;
+ option = 1;
+ if (setsockopt(stream_fd, SOL_SOCKET, SO_KEEPALIVE, CAST &option, sizeof(option)) < 0) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(raop_rtp_mirror->logger, LOGGER_WARNING,
+ "raop_rtp_mirror could not set stream socket keepalive %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ }
+ option = 60;
+ if (setsockopt(stream_fd, SOL_TCP, TCP_KEEPIDLE, CAST &option, sizeof(option)) < 0) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(raop_rtp_mirror->logger, LOGGER_WARNING,
+ "raop_rtp_mirror could not set stream socket keepalive time %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ }
+ option = 10;
+ if (setsockopt(stream_fd, SOL_TCP, TCP_KEEPINTVL, CAST &option, sizeof(option)) < 0) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(raop_rtp_mirror->logger, LOGGER_WARNING,
+ "raop_rtp_mirror could not set stream socket keepalive interval %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ }
+ option = 6;
+ if (setsockopt(stream_fd, SOL_TCP, TCP_KEEPCNT, CAST &option, sizeof(option)) < 0) {
+ int sock_err = SOCKET_GET_ERROR();
+ logger_log(raop_rtp_mirror->logger, LOGGER_WARNING,
+ "raop_rtp_mirror could not set stream socket keepalive probes %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ }
+ readstart = 0;
+ }
+
+ if (stream_fd != -1 && FD_ISSET(stream_fd, &rfds)) {
+
+ // The first 128 bytes are some kind of header for the payload that follows
+ while (payload == NULL && readstart < 128) {
+ unsigned char* pos = packet + readstart;
+ ret = recv(stream_fd, CAST pos, 128 - readstart, 0);
+ if (ret <= 0) break;
+ readstart = readstart + ret;
+ }
+
+ if (payload == NULL && ret == 0) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
+ "raop_rtp_mirror tcp socket was closed by client (recv returned 0); got %d bytes of 128 byte header",readstart);
+ FD_CLR(stream_fd, &rfds);
+ stream_fd = -1;
+ continue;
+ } else if (payload == NULL && ret == -1) {
+ int sock_err = SOCKET_GET_ERROR();
+ if (sock_err == SOCKET_ERRORNAME(EAGAIN) || sock_err == SOCKET_ERRORNAME(EWOULDBLOCK)) continue; // Timeouts can happen even if the connection is fine
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR,
+ "raop_rtp_mirror error in header recv: %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ if (sock_err == SOCKET_ERRORNAME(ECONNRESET)) conn_reset = true;;
+ break;
+ }
+
+ /*packet[0:3] contains the payload size */
+ int payload_size = byteutils_get_int(packet, 0);
+ char packet_description[13] = {0};
+ char *p = packet_description;
+ int n = sizeof(packet_description);
+ for (int i = 4; i < 8; i++) {
+ snprintf(p, n, "%2.2x ", (unsigned int) packet[i]);
+ n -= 3;
+ p += 3;
+ }
+ ntp_timestamp_raw = byteutils_get_long(packet, 8);
+ ntp_timestamp_remote = raop_ntp_timestamp_to_nano_seconds(ntp_timestamp_raw, false);
+
+ /* packet[4] + packet[5] identify the payload type: values seen are: *
+ * 0x00 0x00: encrypted packet containing a non-IDR type 1 VCL NAL unit *
+ * 0x00 0x10: encrypted packet containing an IDR type 5 VCL NAL unit *
+ * 0x01 0x00: unencrypted packet containing a type 7 SPS NAL + a type 8 PPS NAL unit *
+ * 0x02 0x00: unencrypted packet (old protocol) no payload, sent once every second *
+ * 0x05 0x00 unencrypted packet with a "streaming report", sent once per second. */
+
+ /* packet[6] + packet[7] may list a payload "option": values seen are: *
+ * 0x00 0x00 : encrypted and "streaming report" packets *
+ * 0x1e 0x00 : old protocol (seen in AirMyPC) no-payload once-per-second packets *
+ * 0x16 0x01 : seen in most unencrypted h264 SPS+PPS packets *
+ * 0x56 0x01 : unencrypted h264 SPS+PPS packets (video stream stops, client sleeps) *
+ * 0x1e 0x01 : unencrypted h265/HEVC SPS+PPS packets
+ * 0x5e 0x01 : unencrypted h265 SPS+PPS packets (video stream stops, client sleeps) */
+
+ /* unencrypted packets with a SPS and a PPS NAL are sent initially, and also when a *
+ * change in video format (e.g. width, height) subsequently occurs. They seem always *
+ * to be followed by a packet with a type 5 encrypted IDR VCL NAL, with an identical *
+ * timestamp. On M1/M2 Mac clients, this type 5 NAL is prepended with a type 6 SEI *
+ * NAL unit. Here we prepend the SPS+PPS NALs to the next encrypted packet, which *
+ * always has the same timestamp, and is (almost?) always an IDR NAL unit. */
+
+ /* Unencrypted SPS/PPS packets also have image-size data in (parts of) packet[16:127] */
+
+ /* "streaming report" packets have no timestamp in packet[8:15] */
+
+ if (payload == NULL) {
+ payload = malloc(payload_size);
+ readstart = 0;
+ }
+
+ while (readstart < payload_size) {
+ // Payload data
+ unsigned char *pos = payload + readstart;
+ ret = recv(stream_fd, CAST pos, payload_size - readstart, 0);
+ if (ret <= 0) break;
+ readstart = readstart + ret;
+ }
+
+ if (ret == 0) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket was closed by client (recv returned 0)");
+ break;
+ } else if (ret == -1) {
+ int sock_err = SOCKET_GET_ERROR();
+ if (sock_err == SOCKET_ERRORNAME(EAGAIN) || sock_err == SOCKET_ERRORNAME(EWOULDBLOCK)) continue; // Timeouts can happen even if the connection is fine
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in recv: %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
+ if (errno == SOCKET_ERRORNAME(ECONNRESET)) conn_reset = true;
+ break;
+ }
+
+ switch (packet[4]) {
+ case 0x00:
+ // Normal video data (VCL NAL)
+
+ // Conveniently, the video data is already stamped with the remote wall clock time,
+ // so no additional clock syncing needed. The only thing odd here is that the video
+ // ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just
+ // counting nano seconds since last boot.
+
+ ntp_timestamp_local = raop_ntp_convert_remote_time(raop_rtp_mirror->ntp, ntp_timestamp_remote);
+ if (logger_debug) {
+ uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp_mirror->ntp);
+ int64_t latency = ((int64_t) ntp_now) - ((int64_t) ntp_timestamp_local);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
+ "raop_rtp video: now = %8.6f, ntp = %8.6f, latency = %8.6f, ts = %8.6f, %s %s",
+ (double) ntp_now / SEC, (double) ntp_timestamp_local / SEC, (double) latency / SEC,
+ (double) ntp_timestamp_remote / SEC, packet_description, h265_video ? h265 : h264);
+ }
+
+ unsigned char* payload_out;
+ unsigned char* payload_decrypted;
+ /*
+ * nal_types:1 Coded non-partitioned slice of a non-IDR picture
+ * 5 Coded non-partitioned slice of an IDR picture
+ * 6 Supplemental enhancement information (SEI)
+ * 7 Sequence parameter set (SPS)
+ * 8 Picture parameter set (PPS)
+ *
+ * if a previous unencrypted packet contains an SPS (type 7) and PPS (type 8) NAL which has not
+ * yet been sent, it should be prepended to the current NAL. The M1 Macs have increased the h264 level,
+ * and now the first encrypted packet after the unencrypted SPS+PPS packet may also contain a SEI (type 6) NAL
+ * prepended to its VCL NAL.
+ *
+ * The flag prepend_sps_pps = true will signal that the previous packet contained a SPS NAL + a PPS NAL,
+ * that has not yet been sent. This will trigger prepending it to the current NAL, and the prepend_sps_pps
+ * flag will be set to false after it has been prepended. */
+
+ if (prepend_sps_pps & (ntp_timestamp_raw != ntp_timestamp_nal)) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
+ "raop_rtp_mirror: prepended sps_pps timestamp does not match timestamp of "
+ "video payload\n%llu\n%llu , discarding", ntp_timestamp_raw, ntp_timestamp_nal);
+ free (sps_pps);
+ sps_pps = NULL;
+ prepend_sps_pps = false;
+ }
+
+ if (prepend_sps_pps) {
+ assert(sps_pps);
+ payload_out = (unsigned char*) malloc(payload_size + sps_pps_len);
+ payload_decrypted = payload_out + sps_pps_len;
+ memcpy(payload_out, sps_pps, sps_pps_len);
+ free (sps_pps);
+ sps_pps = NULL;
+ } else {
+ payload_out = (unsigned char*) malloc(payload_size);
+ payload_decrypted = payload_out;
+ }
+ // Decrypt data
+ mirror_buffer_decrypt(raop_rtp_mirror->buffer, payload, payload_decrypted, payload_size);
+
+ // It seems the AirPlay protocol prepends NALs with their size, which we're replacing with the 4-byte
+ // start code for the NAL Byte-Stream Format.
+ bool valid_data = true;
+ int nalu_size = 0;
+ int nalus_count = 0;
+ while (nalu_size < payload_size) {
+ int nc_len = byteutils_get_int_be(payload_decrypted, nalu_size);
+ if (nc_len < 0 || nalu_size + 4 > payload_size) {
+ valid_data = false;
+ break;
+ }
+ memcpy(payload_decrypted + nalu_size, nal_start_code, 4);
+ nalu_size += 4;
+ nalus_count++;
+ /* first bit of h264 nalu MUST be 0 ("forbidden_zero_bit") */
+ if (payload_decrypted[nalu_size] & 0x80) {
+ valid_data = false;
+ break;
+ }
+ int nalu_type;
+ if (h265_video) {
+ nalu_type = payload_decrypted[nalu_size] & 0x7e >> 1;;
+ //logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG," h265 video, NALU type %d, size %d", nalu_type, nc_len);
+ } else {
+ nalu_type = payload_decrypted[nalu_size] & 0x1f;
+ int ref_idc = (payload_decrypted[nalu_size] >> 5);
+ switch (nalu_type) {
+ case 14: /* Prefix NALu , seen before all VCL Nalu's in AirMyPc */
+ case 5: /*IDR, slice_layer_without_partitioning */
+ case 1: /*non-IDR, slice_layer_without_partitioning */
+ break;
+ case 2: /* slice data partition A */
+ case 3: /* slice data partition B */
+ case 4: /* slice data partition C */
+ logger_log(raop_rtp_mirror->logger, LOGGER_INFO,
+ "unexpected partitioned VCL NAL unit: nalu_type = %d, ref_idc = %d, nalu_size = %d,"
+ "processed bytes %d, payloadsize = %d nalus_count = %d",
+ nalu_type, ref_idc, nc_len, nalu_size, payload_size, nalus_count);
+ break;
+ case 6:
+ if (logger_debug) {
+ char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror SEI NAL size = %d", nc_len);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
+ "raop_rtp_mirror h264 Supplemental Enhancement Information:\n%s", str);
+ free(str);
+ }
+ break;
+ case 7:
+ if (logger_debug) {
+ char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror SPS NAL size = %d", nc_len);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
+ "raop_rtp_mirror h264 Sequence Parameter Set:\n%s", str);
+ free(str);
+ }
+ break;
+ case 8:
+ if (logger_debug) {
+ char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror PPS NAL size = %d", nc_len);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
+ "raop_rtp_mirror h264 Picture Parameter Set :\n%s", str);
+ free(str);
+ }
+ break;
+ default:
+ logger_log(raop_rtp_mirror->logger, LOGGER_INFO,
+ "unexpected non-VCL NAL unit: nalu_type = %d, ref_idc = %d, nalu_size = %d,"
+ "processed bytes %d, payloadsize = %d nalus_count = %d",
+ nalu_type, ref_idc, nc_len, nalu_size, payload_size, nalus_count);
+ break;
+ }
+ }
+ nalu_size += nc_len;
+ }
+ if (nalu_size != payload_size) valid_data = false;
+ if(!valid_data) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "nalu marked as invalid");
+ payload_out[0] = 1; /* mark video data as invalid h264 (failed decryption) */
+ }
+
+
+ payload_decrypted = NULL;
+ video_decode_struct video_data;
+ video_data.is_h265 = h265_video;
+ video_data.ntp_time_local = ntp_timestamp_local;
+ video_data.ntp_time_remote = ntp_timestamp_remote;
+ video_data.nal_count = nalus_count; /*nal_count will be the number of nal units in the packet */
+ video_data.data_len = payload_size;
+ video_data.data = payload_out;
+ if (prepend_sps_pps) {
+ video_data.data_len += sps_pps_len;
+ video_data.nal_count += 2;
+ if (h265_video) {
+ video_data.nal_count++;
+ }
+ prepend_sps_pps = false;
+ }
+
+ raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &video_data);
+ free(payload_out);
+ break;
+ case 0x01:
+ /* 128-byte observed packet header structure
+ bytes 0-15: length + timestamp
+ bytes 16-19 float width_source (value is x.0000, x = unsigned short)
+ bytes 20-23 float height_source (value is x.0000, x = unsigned short)
+ bytes 24-39 all 0x0
+ bytes 40-43 float width_source (value is x.0000, x = unsigned short)
+ bytes 44-47 float height_source (value is x.0000, x = unsigned short)
+ bytes 48-51 ??? float "other_w" (value seems to be x.0000, x = unsigned short)
+ bytes 48-51 ??? float "other_h" (value seems to be x.0000, x = unsigned short)
+ bytes 56-59 width
+ bytes 60-63 height
+ bytes 64-127 all 0x0
+ */
+
+ // The information in the payload contains an SPS and a PPS NAL
+ // The sps_pps is not encrypted
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived unencrypted codec packet from client:"
+ " payload_size %d header %s ts_client = %8.6f",
+ payload_size, packet_description, (double) ntp_timestamp_remote / SEC);
+
+ if (!video_stream_suspended && (packet[6] == 0x56 || packet[6] == 0x5e)) {
+ video_stream_suspended = true;
+ raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls);
+ } else if (video_stream_suspended && (packet[6] == 0x16 || packet[6] == 0x1e)) {
+ raop_rtp_mirror->callbacks.video_resume(raop_rtp_mirror->callbacks.cls);
+ video_stream_suspended = false;
+ }
+
+ codec = VIDEO_CODEC_UNKNOWN;
+ assert (raop_rtp_mirror->callbacks.video_set_codec);
+ ntp_timestamp_nal = ntp_timestamp_raw;
+
+ /* these "floats" are in fact integers that fit into unsigned shorts */
+ float width_0 = byteutils_get_float(packet, 16);
+ float height_0 = byteutils_get_float(packet, 20);
+ float width_source = byteutils_get_float(packet, 40); // duplication of width_0
+ float height_source = byteutils_get_float(packet, 44); // duplication of height_0
+ float unknown_w = byteutils_get_float(packet, 48);
+ float unknown_h = byteutils_get_float(packet, 52);
+ float width = byteutils_get_float(packet, 56);
+ float height = byteutils_get_float(packet, 60);
+
+ if (width != width_0 || height != height_0) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror: Unexpected : data %f,"
+ " %f != width_source = %f, height_source = %f", width_0, height_0, width_source, height_source);
+ }
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror: unidentified extra header data %f, %f", unknown_w, unknown_h);
+ if (raop_rtp_mirror->callbacks.video_report_size) {
+ raop_rtp_mirror->callbacks.video_report_size(raop_rtp_mirror->callbacks.cls, &width_source, &height_source, &width, &height);
+ }
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror width_source = %f height_source = %f width = %f height = %f",
+ width_source, height_source, width, height);
+
+ if (payload_size == 0) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror: received type 0x01 packet with no payload:\n"
+ "this indicates non-h264 video but Airplay features bit 42 (SupportsScreenMultiCodec) is not set\n"
+ "use startup option \"-h265\" to set this bit and support h265 (4K) video");
+ unsupported_codec = true;
+ break;
+ }
+ if (sps_pps) {
+ free(sps_pps);
+ sps_pps = NULL;
+ }
+ /* test for a H265 VPS/SPS/PPS */
+ unsigned char hvc1[] = { 0x68, 0x76, 0x63, 0x31 };
+
+ if (!memcmp(payload + 4, hvc1, 4)) {
+ /* hvc1 HECV detected */
+ codec = VIDEO_CODEC_H265;
+ h265_video = true;
+ raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec);
+ unsigned char vps_start_code[] = { 0xa0, 0x00, 0x01, 0x00 };
+ unsigned char sps_start_code[] = { 0xa1, 0x00, 0x01, 0x00 };
+ unsigned char pps_start_code[] = { 0xa2, 0x00, 0x01, 0x00 };
+ unsigned char *vps;
+ short vps_size;
+ unsigned char *sps;
+ short sps_size;
+ unsigned char *pps;
+ short pps_size;
+
+ unsigned char * ptr = payload + 0x75;
+
+ if (memcmp(ptr, vps_start_code, 4)) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "non-conforming HEVC VPS/SPS/PPS payload (VPS)");
+ raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls);
+ break;
+ }
+ vps_size = byteutils_get_short_be(ptr, 3);
+ ptr += 5;
+ vps = ptr;
+ if (logger_debug) {
+ char *str = utils_data_to_string(vps, vps_size, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "h265 vps size %d\n%s",vps_size, str);
+ free(str);
+ }
+ ptr += vps_size;
+ if (memcmp(ptr, sps_start_code, 4)) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "non-conforming HEVC VPS/SPS/PPS payload (SPS)");
+ raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls);
+ break;
+ }
+ sps_size = byteutils_get_short_be(ptr, 3);
+ ptr += 5;
+ sps = ptr;
+ if (logger_debug) {
+ char *str = utils_data_to_string(sps, sps_size, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "h265 sps size %d\n%s",vps_size, str);
+ free(str);
+ }
+ ptr += sps_size;
+ if (memcmp(ptr, pps_start_code, 4)) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "non-conforming HEVC VPS/SPS/PPS payload (PPS)");
+ raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls);
+ break;
+ }
+ pps_size = byteutils_get_short_be(ptr, 3);
+ ptr += 5;
+ pps = ptr;
+ if (logger_debug) {
+ char *str = utils_data_to_string(pps, pps_size, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "h265 pps size %d\n%s",pps_size, str);
+ free(str);
+ }
+
+ sps_pps_len = vps_size + sps_size + pps_size + 12;
+ sps_pps = (unsigned char*) malloc(sps_pps_len);
+ assert(sps_pps);
+ ptr = sps_pps;
+ memcpy(ptr, nal_start_code, 4);
+ ptr += 4;
+ memcpy(ptr, vps, vps_size);
+ ptr += vps_size;
+ memcpy(ptr, nal_start_code, 4);
+ ptr += 4;
+ memcpy(ptr, sps, sps_size);
+ ptr += sps_size;
+ memcpy(ptr, nal_start_code, 4);
+ ptr += 4;
+ memcpy(ptr, pps, pps_size);
+ } else {
+ codec = VIDEO_CODEC_H264;
+ h265_video = false;
+ raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec);
+ short sps_size = byteutils_get_short_be(payload,6);
+ unsigned char *sequence_parameter_set = payload + 8;
+ short pps_size = byteutils_get_short_be(payload, sps_size + 9);
+ unsigned char *picture_parameter_set = payload + sps_size + 11;
+ int data_size = 6;
+ if (logger_debug) {
+ char *str = utils_data_to_string(payload, data_size, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror: SPS+PPS header size = %d", data_size);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror h264 SPS+PPS header:\n%s", str);
+ free(str);
+ str = utils_data_to_string(sequence_parameter_set, sps_size,16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror SPS NAL size = %d", sps_size);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror h264 Sequence Parameter Set:\n%s", str);
+ free(str);
+ str = utils_data_to_string(picture_parameter_set, pps_size, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror PPS NAL size = %d", pps_size);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror h264 Picture Parameter Set:\n%s", str);
+ free(str);
+ }
+ data_size = payload_size - sps_size - pps_size - 11;
+ if (data_size > 0 && logger_debug) {
+ char *str = utils_data_to_string (picture_parameter_set + pps_size, data_size, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "remainder size = %d", data_size);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "remainder of SPS+PPS packet:\n%s", str);
+ free(str);
+ } else if (data_size < 0) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, " pps_sps error: packet remainder size = %d < 0", data_size);
+ }
+
+ // Copy the sps and pps into a buffer to prepend to the next NAL unit.
+ sps_pps_len = sps_size + pps_size + 8;
+ sps_pps = (unsigned char*) malloc(sps_pps_len);
+ assert(sps_pps);
+ memcpy(sps_pps, nal_start_code, 4);
+ memcpy(sps_pps + 4, sequence_parameter_set, sps_size);
+ memcpy(sps_pps + sps_size + 4, nal_start_code, 4);
+ memcpy(sps_pps + sps_size + 8, payload + sps_size + 11, pps_size);
+ }
+ prepend_sps_pps = true;
+ // h264codec_t h264;
+ // h264.version = payload[0];
+ // h264.profile_high = payload[1];
+ // h264.compatibility = payload[2];
+ // h264.level = payload[3];
+ // h264.reserved_6_and_nal = payload[4];
+ // h264.reserved_3_and_sps = payload[5];
+ // h264.sps_size = sps_size;
+ // h264.sequence_parameter_set = malloc(h264.sps_size);
+ // memcpy(h264.sequence_parameter_set, sequence_parameter_set, sps_size);
+ // h264.number_of_pps = payload[h264.sps_size + 8];
+ // h264.pps_size = pps_size;
+ // h264.picture_parameter_set = malloc(h264.pps_size);
+ // memcpy(h264.picture_parameter_set, picture_parameter_set, pps_size);
+ break;
+ case 0x02:
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived old-protocol once-per-second packet from client:"
+ " payload_size %d header %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw);
+ /* "old protocol" (used by AirMyPC), rest of 128-byte packet is empty */
+ case 0x05:
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived video streaming performance info packet from client:"
+ " payload_size %d header %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw);
+ /* payloads with packet[4] = 0x05 have no timestamp, and carry video info from the client as a binary plist *
+ * Sometimes (e.g, when the client has a locked screen), there is a 25kB trailer attached to the packet. *
+ * This 25000 Byte trailer with unidentified content seems to be the same data each time it is sent. */
+
+ if (payload_size && raop_rtp_mirror->show_client_FPS_data) {
+ //char *str = utils_data_to_string(packet, 128, 16);
+ //logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "type 5 video packet header:\n%s", str);
+ //free (str);
+
+ int plist_size = payload_size;
+ if (payload_size > 25000) {
+ plist_size = payload_size - 25000;
+ if (logger_debug) {
+ char *str = utils_data_to_string(payload + plist_size, 16, 16);
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
+ "video_info packet had 25kB trailer; first 16 bytes are:\n%s", str);
+ free(str);
+ }
+ }
+ if (plist_size) {
+ char *plist_xml;
+ uint32_t plist_len;
+ plist_t root_node = NULL;
+ plist_from_bin((char *) payload, plist_size, &root_node);
+ plist_to_xml(root_node, &plist_xml, &plist_len);
+ logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "%s", plist_xml);
+ free(plist_xml);
+ }
+ }
+ break;
+ default:
+ logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "\nReceived unexpected TCP packet from client, "
+ "size %d, %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw);
+ break;
+ }
+
+ free(payload);
+ payload = NULL;
+ memset(packet, 0, 128);
+ readstart = 0;
+ if (unsupported_codec) {
+ break;
+ }
+ }
+ }
+ /* Close the stream file descriptor */
+ if (stream_fd != -1) {
+ closesocket(stream_fd);
+ }
+
+ // Ensure running reflects the actual state
+ MUTEX_LOCK(raop_rtp_mirror->run_mutex);
+ raop_rtp_mirror->running = false;
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror exiting TCP thread");
+ if (conn_reset && raop_rtp_mirror->callbacks.conn_reset) {
+ const bool video_reset = false; /* leave "frozen video" showing */
+ raop_rtp_mirror->callbacks.conn_reset(raop_rtp_mirror->callbacks.cls, 0, video_reset);
+ }
+
+ if (unsupported_codec) {
+ closesocket(raop_rtp_mirror->mirror_data_sock);
+ raop_rtp_mirror_stop(raop_rtp_mirror);
+ raop_rtp_mirror->callbacks.video_reset(raop_rtp_mirror->callbacks.cls);
+ }
+
+ return 0;
+}
+
+static int
+raop_rtp_mirror_init_socket(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6)
+{
+ assert(raop_rtp_mirror);
+
+ unsigned short dport = raop_rtp_mirror->mirror_data_lport;
+ int dsock = netutils_init_socket(&dport, use_ipv6, 0);
+ if (dsock == -1) {
+ goto sockets_cleanup;
+ }
+
+ /* Listen to the data socket if using TCP */
+ if (listen(dsock, 1) < 0) {
+ goto sockets_cleanup;
+ }
+
+ /* Set socket descriptors */
+ raop_rtp_mirror->mirror_data_sock = dsock;
+
+ /* Set port values */
+ raop_rtp_mirror->mirror_data_lport = dport;
+ logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror local data port socket %d port TCP %d",
+ dsock, dport);
+ return 0;
+
+ sockets_cleanup:
+ if (dsock != -1) closesocket(dsock);
+ return -1;
+}
+
+void
+raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport,
+ uint8_t show_client_FPS_data)
+{
+ logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror starting mirroring");
+ int use_ipv6 = 0;
+
+ assert(raop_rtp_mirror);
+ assert(mirror_data_lport);
+ raop_rtp_mirror->show_client_FPS_data = show_client_FPS_data;
+
+ MUTEX_LOCK(raop_rtp_mirror->run_mutex);
+ if (raop_rtp_mirror->running || !raop_rtp_mirror->joined) {
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+ return;
+ }
+
+ if (raop_rtp_mirror->remote_saddr.ss_family == AF_INET6) {
+ use_ipv6 = 1;
+ }
+ //use_ipv6 = 0;
+
+ raop_rtp_mirror->mirror_data_lport = *mirror_data_lport;
+ if (raop_rtp_mirror_init_socket(raop_rtp_mirror, use_ipv6) < 0) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror initializing socket failed");
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+ return;
+ }
+ *mirror_data_lport = raop_rtp_mirror->mirror_data_lport;
+
+ /* Create the thread and initialize running values */
+ raop_rtp_mirror->running = 1;
+ raop_rtp_mirror->joined = 0;
+
+ THREAD_CREATE(raop_rtp_mirror->thread_mirror, raop_rtp_mirror_thread, raop_rtp_mirror);
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+}
+
+void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror) {
+ assert(raop_rtp_mirror);
+
+ /* Check that we are running and thread is not
+ * joined (should never be while still running) */
+ MUTEX_LOCK(raop_rtp_mirror->run_mutex);
+ if (!raop_rtp_mirror->running || raop_rtp_mirror->joined) {
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+ return;
+ }
+ raop_rtp_mirror->running = 0;
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+
+ if (raop_rtp_mirror->mirror_data_sock != -1) {
+ closesocket(raop_rtp_mirror->mirror_data_sock);
+ raop_rtp_mirror->mirror_data_sock = -1;
+ }
+
+ /* Join the thread */
+ THREAD_JOIN(raop_rtp_mirror->thread_mirror);
+
+ /* Mark thread as joined */
+ MUTEX_LOCK(raop_rtp_mirror->run_mutex);
+ raop_rtp_mirror->joined = 1;
+ MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
+}
+
+void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror) {
+ if (raop_rtp_mirror) {
+ raop_rtp_mirror_stop(raop_rtp_mirror);
+ MUTEX_DESTROY(raop_rtp_mirror->run_mutex);
+ mirror_buffer_destroy(raop_rtp_mirror->buffer);
+ free(raop_rtp_mirror);
+ }
+}
diff --git a/lib/raop_rtp_mirror.h b/lib/raop_rtp_mirror.h
new file mode 100644
index 0000000..2e7914d
--- /dev/null
+++ b/lib/raop_rtp_mirror.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 dsafa22, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2021-2023
+ */
+
+#ifndef RAOP_RTP_MIRROR_H
+#define RAOP_RTP_MIRROR_H
+
+#include
+#include "raop.h"
+#include "logger.h"
+
+typedef struct raop_rtp_mirror_s raop_rtp_mirror_t;
+typedef struct h264codec_s h264codec_t;
+
+raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
+ const char *remote, int remotelen, const unsigned char *aeskey);
+void raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID);
+void raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport, uint8_t show_client_FPS_data);
+void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror);
+void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror);
+#endif //RAOP_RTP_MIRROR_H
diff --git a/lib/sockets.h b/lib/sockets.h
new file mode 100644
index 0000000..7fb2585
--- /dev/null
+++ b/lib/sockets.h
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#ifndef SOCKETS_H
+#define SOCKETS_H
+
+#if defined(WIN32)
+
+char *wsa_strerror(int errnum);
+
+typedef int socklen_t;
+
+#ifndef SHUT_RD
+# define SHUT_RD SD_RECEIVE
+#endif
+#ifndef SHUT_WR
+# define SHUT_WR SD_SEND
+#endif
+#ifndef SHUT_RDWR
+# define SHUT_RDWR SD_BOTH
+#endif
+
+#define SOCKET_GET_ERROR() WSAGetLastError()
+#define SOCKET_SET_ERROR(value) WSASetLastError(value)
+#define SOCKET_ERRORNAME(name) WSA##name
+#define SOCKET_ERROR_STRING(errnum) wsa_strerror(errnum)
+
+#define WSAEAGAIN WSAEWOULDBLOCK
+#define WSAENOMEM WSA_NOT_ENOUGH_MEMORY
+
+#else
+
+#define closesocket close
+#define ioctlsocket ioctl
+
+#define SOCKET_GET_ERROR() (errno)
+#define SOCKET_SET_ERROR(value) (errno = (value))
+#define SOCKET_ERRORNAME(name) name
+#define SOCKET_ERROR_STRING(errnum) strerror(errnum)
+#endif
+
+#endif
diff --git a/lib/srp.c b/lib/srp.c
new file mode 100644
index 0000000..d909cbf
--- /dev/null
+++ b/lib/srp.c
@@ -0,0 +1,1180 @@
+/*
+ * Secure Remote Password 6a implementation
+ * Copyright (c) 2010 Tom Cocagne. All rights reserved.
+ * https://github.com/cocagne/csrp
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013 Tom Cocagne
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *===========================================================================
+ * updated (2023) by fduncanh to replace deprecated openssl SHA* hash functions
+ * modified (2023) by fduncanh for use with Apple's pair-setup-pin protocol
+ */
+#define APPLE_VARIANT
+#ifdef WIN32
+# include
+# include
+#else
+# include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "srp.h"
+
+static int g_initialized = 0;
+
+typedef struct
+{
+ BIGNUM * N;
+ BIGNUM * g;
+} NGConstant;
+
+struct NGHex
+{
+ const char * n_hex;
+ const char * g_hex;
+};
+
+/* All constants here were pulled from Appendix A of RFC 5054 */
+static struct NGHex global_Ng_constants[] = {
+#if 0 /* begin removed section 1 */
+ { /* 1024 */
+ "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496"
+ "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E"
+ "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA"
+ "9AFD5138FE8376435B9FC61D2FC0EB06E3",
+ "2"
+ },
+ { /* 1536 */
+ "9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA961"
+ "4B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F843"
+ "80B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0B"
+ "E3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF5"
+ "6EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734A"
+ "F7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E"
+ "8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB",
+ "2"
+ },
+#endif /* end removed section 1 */
+ { /* 2048 */
+ "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4"
+ "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60"
+ "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF"
+ "747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907"
+ "8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861"
+ "60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB"
+ "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73",
+ "2"
+ },
+#if 0 /* begin removed section 2 */
+ { /* 3072 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B"
+ "139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485"
+ "B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1F"
+ "E649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23"
+ "DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32"
+ "905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558"
+ "17183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521"
+ "ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D7"
+ "1E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B1817"
+ "7B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82"
+ "D120A93AD2CAFFFFFFFFFFFFFFFF",
+ "5"
+ },
+ { /* 4096 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ "FFFFFFFFFFFFFFFF",
+ "5"
+ },
+ { /* 6144 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ "6DCC4024FFFFFFFFFFFFFFFF",
+ "5"
+ },
+ { /* 8192 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
+ "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
+ "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
+ "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
+ "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
+ "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
+ "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
+ "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
+ "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+ "13"
+ },
+ #endif /* end removed section 2 */
+ {0,0} /* null sentinel */
+};
+
+
+static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * g_hex )
+{
+ NGConstant * ng = (NGConstant *) malloc( sizeof(NGConstant) );
+ ng->N = BN_new();
+ ng->g = BN_new();
+
+ if( !ng || !ng->N || !ng->g )
+ return 0;
+
+ if ( ng_type != SRP_NG_CUSTOM )
+ {
+ int idx = ng_type;
+ if ( ng_type > SRP_NG_CUSTOM )
+ idx -= 1;
+ n_hex = global_Ng_constants[ idx ].n_hex;
+ g_hex = global_Ng_constants[ idx ].g_hex;
+ }
+ BN_hex2bn( &ng->N, n_hex );
+ BN_hex2bn( &ng->g, g_hex );
+
+ return ng;
+}
+
+static void delete_ng( NGConstant * ng )
+{
+ if (ng)
+ {
+ BN_free( ng->N );
+ BN_free( ng->g );
+ ng->N = 0;
+ ng->g = 0;
+ free(ng);
+ }
+}
+
+typedef struct HashCTX_s {
+ EVP_MD_CTX *digest_ctx;
+} HashCTX_t;
+
+
+struct SRPVerifier
+{
+ SRP_HashAlgorithm hash_alg;
+ NGConstant *ng;
+ const char * username;
+ const unsigned char * bytes_B;
+ int authenticated;
+ int rfc5054;
+ unsigned char M [SHA512_DIGEST_LENGTH];
+ unsigned char H_AMK [SHA512_DIGEST_LENGTH];
+#ifdef APPLE_VARIANT
+ unsigned char session_key [2 * SHA512_DIGEST_LENGTH];
+#else
+ unsigned char session_key [SHA512_DIGEST_LENGTH];
+#endif
+};
+
+#if 0 /*begin removed section 3*/
+struct SRPUser
+{
+ SRP_HashAlgorithm hash_alg;
+ NGConstant *ng;
+
+ BIGNUM *a;
+ BIGNUM *A;
+ BIGNUM *S;
+
+ const unsigned char * bytes_A;
+ int authenticated;
+ int rfc5054;
+
+ const char * username;
+ const unsigned char * password;
+ int password_len;
+
+ unsigned char M [SHA512_DIGEST_LENGTH];
+ unsigned char H_AMK [SHA512_DIGEST_LENGTH];
+#ifdef APPLE_VARIANT
+ unsigned char session_key [2 * SHA512_DIGEST_LENGTH];
+#else
+ unsigned char session_key [SHA512_DIGEST_LENGTH];
+#endif
+
+};
+#endif /*end removed section 3*/
+static void handle_error(const char* location) {
+ long error = ERR_get_error();
+ const char* error_str = ERR_error_string(error, NULL);
+ fprintf(stderr, "SRP error at %s: %s\n", location, error_str);
+ exit(EXIT_FAILURE);
+}
+
+static void hash_destroy( HashCTX_t *ctx) {
+ if (ctx) {
+ EVP_MD_CTX_free(ctx->digest_ctx);
+ free(ctx);
+ }
+}
+
+static HashCTX_t *hash_create()
+{
+ HashCTX_t *ctx = (HashCTX_t *) malloc(sizeof(HashCTX_t));
+ assert(ctx != NULL);
+ ctx->digest_ctx = EVP_MD_CTX_new();
+ assert(ctx->digest_ctx != NULL);
+ return ctx;
+}
+
+int hash_reset( HashCTX_t *ctx)
+{
+ return EVP_MD_CTX_reset( ctx->digest_ctx);
+}
+
+static int hash_init( SRP_HashAlgorithm alg, HashCTX_t *ctx)
+{
+ int ret;
+ switch (alg)
+ {
+ case SRP_SHA1 :
+ ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha1(), NULL);
+ break;
+ case SRP_SHA224 :
+ ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha224(), NULL);
+ break;
+ case SRP_SHA256 :
+ ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha256(), NULL);
+ break;
+ case SRP_SHA384 :
+ ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha384(), NULL);
+ break;
+ case SRP_SHA512 :
+ ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL);
+ break;
+ default:
+ return -1;
+ }
+ return ret;
+}
+
+static int hash_update( SRP_HashAlgorithm alg, HashCTX_t *ctx, const void *data, size_t len )
+{
+ int ret = EVP_DigestUpdate(ctx->digest_ctx, data, len);
+ if (!ret) {
+ handle_error(__func__);
+ }
+ return ret;
+}
+
+static int hash_final( SRP_HashAlgorithm alg, HashCTX_t *ctx, unsigned char *md, unsigned int *md_len )
+{
+ int ret = EVP_DigestFinal_ex(ctx->digest_ctx, md, md_len);
+ if (!ret) {
+ handle_error(__func__);
+ }
+ return ret;
+}
+
+static unsigned char * hash( SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md )
+{
+ switch (alg)
+ {
+ case SRP_SHA1 : return SHA1( d, n, md );
+ case SRP_SHA224: return SHA224( d, n, md );
+ case SRP_SHA256: return SHA256( d, n, md );
+ case SRP_SHA384: return SHA384( d, n, md );
+ case SRP_SHA512: return SHA512( d, n, md );
+ default:
+ return 0;
+ };
+}
+
+static int hash_length( SRP_HashAlgorithm alg )
+{
+ switch (alg)
+ {
+ case SRP_SHA1 : return SHA_DIGEST_LENGTH;
+ case SRP_SHA224: return SHA224_DIGEST_LENGTH;
+ case SRP_SHA256: return SHA256_DIGEST_LENGTH;
+ case SRP_SHA384: return SHA384_DIGEST_LENGTH;
+ case SRP_SHA512: return SHA512_DIGEST_LENGTH;
+ default:
+ return -1;
+ };
+}
+
+static BIGNUM * H_nn_orig( SRP_HashAlgorithm alg, const BIGNUM * n1, const BIGNUM * n2 )
+{
+ unsigned char buff[ SHA512_DIGEST_LENGTH ];
+ int len_n1 = BN_num_bytes(n1);
+ int len_n2 = BN_num_bytes(n2);
+ int nbytes = len_n1 + len_n2;
+ unsigned char * bin = (unsigned char *) malloc( nbytes );
+ if (!bin)
+ return 0;
+ BN_bn2bin(n1, bin);
+ BN_bn2bin(n2, bin + len_n1);
+ hash( alg, bin, nbytes, buff );
+ free(bin);
+ return BN_bin2bn(buff, hash_length(alg), NULL);
+}
+
+static BIGNUM * H_nn_rfc5054( SRP_HashAlgorithm alg, const BIGNUM * N, const BIGNUM * n1, const BIGNUM * n2 )
+{
+ unsigned char buff[ SHA512_DIGEST_LENGTH ];
+ int len_N = BN_num_bytes(N);
+ int len_n1 = BN_num_bytes(n1);
+ int len_n2 = BN_num_bytes(n2);
+ int nbytes = len_N * 2;
+ unsigned char * bin = (unsigned char *) malloc( nbytes );
+ if (!bin)
+ return 0;
+
+ if (len_n1 > len_N || len_n2 > len_N)
+ return 0;
+
+ memset(bin, 0, nbytes);
+ BN_bn2bin(n1, bin + (len_N - len_n1));
+ BN_bn2bin(n2, bin + (len_N + len_N - len_n2));
+ hash( alg, bin, nbytes, buff );
+ free(bin);
+ return BN_bin2bn(buff, hash_length(alg), NULL);
+}
+
+static BIGNUM * H_ns( SRP_HashAlgorithm alg, const BIGNUM * n, const unsigned char * bytes, int len_bytes )
+{
+ unsigned char buff[ SHA512_DIGEST_LENGTH ];
+ int len_n = BN_num_bytes(n);
+ int nbytes = len_n + len_bytes;
+ unsigned char * bin = (unsigned char *) malloc( nbytes );
+ if (!bin)
+ return 0;
+ BN_bn2bin(n, bin);
+ memcpy( bin + len_n, bytes, len_bytes );
+ hash( alg, bin, nbytes, buff );
+ free(bin);
+ return BN_bin2bn(buff, hash_length(alg), NULL);
+}
+
+static BIGNUM * calculate_x( SRP_HashAlgorithm alg, const BIGNUM * salt, const char * username, const unsigned char * password, int password_len )
+{
+ unsigned char ucp_hash[SHA512_DIGEST_LENGTH];
+ unsigned int ucp_hash_len;
+ HashCTX_t *ctx = hash_create();
+
+ hash_init( alg, ctx);
+ hash_update( alg, ctx, username, strlen(username) );
+ hash_update( alg, ctx, ":", 1 );
+ hash_update( alg, ctx, password, password_len );
+ hash_final( alg, ctx, ucp_hash, &ucp_hash_len );
+ hash_destroy ( ctx);
+
+ return H_ns( alg, salt, ucp_hash, hash_length(alg) );
+}
+
+static void update_hash_n( SRP_HashAlgorithm alg, HashCTX_t *ctx, const BIGNUM * n )
+{
+ unsigned long len = BN_num_bytes(n);
+ unsigned char * n_bytes = (unsigned char *) malloc( len );
+ if (!n_bytes)
+ return;
+ BN_bn2bin(n, n_bytes);
+ hash_update(alg, ctx, n_bytes, len);
+ free(n_bytes);
+}
+
+static void hash_num( SRP_HashAlgorithm alg, const BIGNUM * n, unsigned char * dest )
+{
+ int nbytes = BN_num_bytes(n);
+ unsigned char * bin = (unsigned char *) malloc( nbytes );
+ if(!bin)
+ return;
+ BN_bn2bin(n, bin);
+ hash( alg, bin, nbytes, dest );
+ free(bin);
+}
+
+#ifdef APPLE_VARIANT
+/* added for compatibility with Apple's modified srp
+ * see https://htmlpreview.github.io/?https://github.com/philippe44/RAOP-Player/blob/master/doc/auth_protocol.html
+ */
+
+static int hash_session_key( SRP_HashAlgorithm alg, const BIGNUM * n, unsigned char * dest )
+{
+ HashCTX_t *ctx;
+ int nbytes = BN_num_bytes(n);
+ unsigned char * bin = (unsigned char *) malloc( nbytes );
+ unsigned char fourbytes[4] = { 0 }; //Apple's modified SRP protocol
+ unsigned int len1, len2;
+ if(!bin)
+ return -1;
+ BN_bn2bin(n, bin);
+
+ ctx = hash_create();
+ hash_init(alg, ctx);
+ hash_update( alg, ctx, bin, nbytes);
+ hash_update( alg, ctx, fourbytes, sizeof(fourbytes));
+ hash_final( alg, ctx, dest, &len1);
+ hash_reset( ctx);
+
+ fourbytes[3] = 1;
+ hash_init(alg, ctx);
+ hash_update( alg, ctx, bin, nbytes);
+ hash_update( alg, ctx, fourbytes, sizeof(fourbytes));
+ hash_final( alg, ctx, dest + len1, &len2);
+ hash_destroy( ctx);
+
+ free(bin);
+ return len1 + len2;
+}
+#endif
+
+static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * dest, const char * I, const BIGNUM * s,
+ const BIGNUM * A, const BIGNUM * B, const unsigned char * K )
+{
+ unsigned char H_N[ SHA512_DIGEST_LENGTH ];
+ unsigned char H_g[ SHA512_DIGEST_LENGTH ];
+ unsigned char H_I[ SHA512_DIGEST_LENGTH ];
+ unsigned char H_xor[ SHA512_DIGEST_LENGTH ];
+ unsigned int dest_len;
+ HashCTX_t *ctx;
+ int i = 0;
+ int hash_len = hash_length(alg);
+
+ hash_num( alg, ng->N, H_N );
+ hash_num( alg, ng->g, H_g );
+ hash(alg, (const unsigned char *)I, strlen(I), H_I);
+
+ for (i=0; i < hash_len; i++ )
+ H_xor[i] = H_N[i] ^ H_g[i];
+
+ ctx = hash_create();
+ hash_init( alg, ctx);
+ hash_update( alg, ctx, H_xor, hash_len );
+ hash_update( alg, ctx, H_I, hash_len );
+ update_hash_n( alg, ctx, s );
+ update_hash_n( alg, ctx, A );
+ update_hash_n( alg, ctx, B );
+#ifdef APPLE_VARIANT /* Apple's SRP session key length is 2 x hash_len */
+ hash_update( alg, ctx, K, 2 * hash_len );
+#else
+ hash_update( alg, ctx, K, hash_len );
+#endif
+ hash_final( alg, ctx, dest, &dest_len );
+ hash_destroy ( ctx);
+
+}
+
+static void calculate_H_AMK( SRP_HashAlgorithm alg, unsigned char *dest, const BIGNUM * A, const unsigned char * M, const unsigned char * K )
+{
+ HashCTX_t *ctx;
+ unsigned int dest_len;
+
+ ctx = hash_create();
+ hash_init( alg, ctx);
+ update_hash_n( alg, ctx, A );
+ hash_update( alg, ctx, M, hash_length(alg) );
+#ifdef APPLE_VARIANT
+ hash_update( alg, ctx, K, 2 * hash_length(alg) );
+#else
+ hash_update( alg, ctx, K, hash_length(alg) );
+#endif
+ hash_final( alg, ctx, dest, &dest_len );
+ hash_destroy ( ctx);
+}
+
+
+
+static void init_random()
+{
+ if (g_initialized)
+ return;
+
+#ifdef WIN32
+ HCRYPTPROV wctx;
+#else
+ FILE *fp = 0;
+#endif
+ unsigned char buff[64];
+
+#ifdef WIN32
+ CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+ CryptGenRandom(wctx, sizeof(buff), (BYTE*) buff);
+ CryptReleaseContext(wctx, 0);
+ g_initialized = 1;
+#else
+ fp = fopen("/dev/urandom", "r");
+ if (fp)
+ {
+ size_t count = fread(buff, sizeof(buff), 1, fp);
+ fclose(fp);
+ if (count == 1) {
+ g_initialized = 1;
+ }
+ }
+#endif
+ if (g_initialized)
+ RAND_seed( buff, sizeof(buff) );
+}
+
+
+/***********************************************************************************************************
+ *
+ * Exported Functions
+ *
+ ***********************************************************************************************************/
+
+void srp_random_seed( const unsigned char * random_data, int data_length )
+{
+ g_initialized = 1;
+
+ if (random_data)
+ RAND_seed( random_data, data_length );
+}
+
+
+void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
+ SRP_NGType ng_type, const char * username,
+ const unsigned char * password, int len_password,
+ const unsigned char ** bytes_s, int * len_s,
+ const unsigned char ** bytes_v, int * len_v,
+ const char * n_hex, const char * g_hex )
+{
+ BIGNUM * s = BN_new();
+ BIGNUM * v = BN_new();
+ BIGNUM * x = 0;
+ BN_CTX * ctx = BN_CTX_new();
+ NGConstant * ng = new_ng( ng_type, n_hex, g_hex );
+
+ if( !s || !v || !ctx || !ng )
+ goto cleanup_and_exit;
+
+ init_random(); /* Only happens once */
+
+#ifdef APPLE_VARIANT //use a 16 byte salt
+ BN_rand(s, 128, -1, 0);
+#else
+ BN_rand(s, 32, -1, 0);
+#endif
+ x = calculate_x( alg, s, username, password, len_password );
+
+ if( !x )
+ goto cleanup_and_exit;
+
+ BN_mod_exp(v, ng->g, x, ng->N, ctx);
+
+ *len_s = BN_num_bytes(s);
+ *len_v = BN_num_bytes(v);
+
+ *bytes_s = (const unsigned char *) malloc( *len_s );
+ *bytes_v = (const unsigned char *) malloc( *len_v );
+
+ if (!bytes_s || !bytes_v)
+ goto cleanup_and_exit;
+
+ BN_bn2bin(s, (unsigned char *) *bytes_s);
+ BN_bn2bin(v, (unsigned char *) *bytes_v);
+
+ cleanup_and_exit:
+ delete_ng( ng );
+ BN_free(s);
+ BN_free(v);
+ BN_free(x);
+ BN_CTX_free(ctx);
+}
+#ifdef APPLE_VARIANT
+
+/* Out: bytes_B, len_B, bytes_b, len_b
+ * On failure, bytes_B and bytes_b will be set to NULL
+ * len_B and len_will be set to 0
+ */
+void srp_create_server_ephemeral_key( SRP_HashAlgorithm alg, SRP_NGType ng_type,
+ const unsigned char * bytes_v, int len_v,
+ const unsigned char * bytes_b, int len_b,
+ const unsigned char ** bytes_B, int * len_B,
+ const char * n_hex, const char * g_hex,
+ int rfc5054_compat ) {
+ BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL);
+ BIGNUM *tmp1 = BN_new();
+ BIGNUM *tmp2 = BN_new();
+ BIGNUM *B = BN_new();
+ BIGNUM *b = BN_new();
+ BIGNUM *k = 0;
+ BN_CTX *ctx = BN_CTX_new();
+ NGConstant *ng = new_ng( ng_type, n_hex, g_hex );
+
+ *len_B = 0;
+ *bytes_B = 0;
+
+ if( !v || !B || !b || !tmp1 || !tmp2 || !ctx || !ng )
+ goto cleanup_and_exit;
+
+ b = BN_bin2bn(bytes_b, len_b, NULL);
+
+ if (rfc5054_compat)
+ k = H_nn_rfc5054(alg, ng->N, ng->N, ng->g);
+ else
+ k = H_nn_orig(alg, ng->N, ng->g);
+
+ if(!k)
+ goto cleanup_and_exit;
+
+ /* B = kv + g^b */
+ if (rfc5054_compat)
+ {
+ BN_mod_mul(tmp1, k, v, ng->N, ctx);
+ BN_mod_exp(tmp2, ng->g, b, ng->N, ctx);
+ BN_mod_add(B, tmp1, tmp2, ng->N, ctx);
+ }
+ else
+ {
+ BN_mul(tmp1, k, v, ctx);
+ BN_mod_exp(tmp2, ng->g, b, ng->N, ctx);
+ BN_add(B, tmp1, tmp2);
+ }
+
+ *len_B = BN_num_bytes(B);
+ *bytes_B = (const unsigned char *)malloc( *len_B );
+ BN_bn2bin( B, (unsigned char *) *bytes_B );
+
+ cleanup_and_exit:
+ BN_free(v);
+ if (k) BN_free(k);
+ BN_free(B);
+ BN_free(b);
+ BN_free(tmp1);
+ BN_free(tmp2);
+ BN_CTX_free(ctx);
+}
+#endif
+
+/* Out: bytes_B, len_B.
+ *
+ * On failure, bytes_B will be set to NULL and len_B will be set to 0
+ */
+struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
+ const unsigned char * bytes_s, int len_s,
+ const unsigned char * bytes_v, int len_v,
+ const unsigned char * bytes_A, int len_A,
+#ifdef APPLE_VARIANT
+ const unsigned char * bytes_b, int len_b,
+#endif
+ const unsigned char ** bytes_B, int * len_B,
+ const char * n_hex, const char * g_hex,
+ int rfc5054_compat )
+{
+ BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL);
+ BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL);
+ BIGNUM *A = BN_bin2bn(bytes_A, len_A, NULL);
+ BIGNUM *u = 0;
+ BIGNUM *B = BN_new();
+ BIGNUM *S = BN_new();
+ BIGNUM *b = BN_new();
+ BIGNUM *k = 0;
+ BIGNUM *tmp1 = BN_new();
+ BIGNUM *tmp2 = BN_new();
+ BN_CTX *ctx = BN_CTX_new();
+ int ulen = strlen(username) + 1;
+ NGConstant *ng = new_ng( ng_type, n_hex, g_hex );
+ struct SRPVerifier *ver = 0;
+
+ *len_B = 0;
+ *bytes_B = 0;
+
+ if( !s || !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !ctx || !ng )
+ goto cleanup_and_exit;
+
+ ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) );
+
+ if (!ver)
+ goto cleanup_and_exit;
+
+ init_random(); /* Only happens once */
+
+ ver->username = (char *) malloc( ulen );
+ ver->hash_alg = alg;
+ ver->ng = ng;
+
+ if (!ver->username)
+ {
+ free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
+ }
+
+ memcpy( (char*)ver->username, username, ulen );
+
+ ver->authenticated = 0;
+ ver->rfc5054 = rfc5054_compat;
+
+ /* SRP-6a safety check */
+ BN_mod(tmp1, A, ng->N, ctx);
+ if ( !BN_is_zero(tmp1) )
+ {
+#ifdef APPLE_VARIANT
+ if ( !len_b || !bytes_b) {
+#endif
+ BN_rand(b, 256, -1, 0);
+#ifdef APPLE_VARIANT
+ } else {
+ b = BN_bin2bn(bytes_b, len_b, NULL);
+ }
+#endif
+
+ if (rfc5054_compat)
+ k = H_nn_rfc5054(alg, ng->N, ng->N, ng->g);
+ else
+ k = H_nn_orig(alg, ng->N, ng->g);
+
+ if(!k)
+ {
+ free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
+ }
+
+ /* B = kv + g^b */
+ if (rfc5054_compat)
+ {
+ BN_mod_mul(tmp1, k, v, ng->N, ctx);
+ BN_mod_exp(tmp2, ng->g, b, ng->N, ctx);
+ BN_mod_add(B, tmp1, tmp2, ng->N, ctx);
+ }
+ else
+ {
+ BN_mul(tmp1, k, v, ctx);
+ BN_mod_exp(tmp2, ng->g, b, ng->N, ctx);
+ BN_add(B, tmp1, tmp2);
+ }
+
+ if (rfc5054_compat)
+ u = H_nn_rfc5054(alg, ng->N, A, B);
+ else
+ u = H_nn_orig(alg, A, B);
+
+ if(!u)
+ {
+ free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
+ }
+
+ /* S = (A *(v^u)) ^ b */
+ BN_mod_exp(tmp1, v, u, ng->N, ctx);
+ BN_mul(tmp2, A, tmp1, ctx);
+ BN_mod_exp(S, tmp2, b, ng->N, ctx);
+
+#ifdef APPLE_VARIANT
+ hash_session_key(alg, S, ver->session_key);
+#else
+ hash_num(alg, S, ver->session_key);
+#endif
+ calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key );
+ calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key );
+
+ *len_B = BN_num_bytes(B);
+ *bytes_B = (const unsigned char *)malloc( *len_B );
+
+ if( !((const unsigned char *)*bytes_B) )
+ {
+ free( (void*) ver->username );
+ free( ver );
+ ver = 0;
+ *len_B = 0;
+ goto cleanup_and_exit;
+ }
+
+ BN_bn2bin( B, (unsigned char *) *bytes_B );
+
+ ver->bytes_B = *bytes_B;
+ } else {
+ free(ver);
+ ver = 0;
+ }
+
+ cleanup_and_exit:
+ BN_free(s);
+ BN_free(v);
+ BN_free(A);
+ if (u) BN_free(u);
+ if (k) BN_free(k);
+ BN_free(B);
+ BN_free(S);
+ BN_free(b);
+ BN_free(tmp1);
+ BN_free(tmp2);
+ BN_CTX_free(ctx);
+
+ return ver;
+}
+
+void srp_verifier_delete( struct SRPVerifier * ver )
+{
+ if (ver)
+ {
+ delete_ng( ver->ng );
+ free( (char *) ver->username );
+ free( (unsigned char *) ver->bytes_B );
+ memset(ver, 0, sizeof(*ver));
+ free( ver );
+ }
+}
+
+int srp_verifier_is_authenticated( struct SRPVerifier * ver )
+{
+ return ver->authenticated;
+}
+
+const char * srp_verifier_get_username( struct SRPVerifier * ver )
+{
+ return ver->username;
+}
+
+const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length )
+{
+ if (key_length)
+#ifdef APPLE_VARIANT
+ *key_length = 2 * hash_length( ver->hash_alg );
+#else
+ *key_length = hash_length( ver->hash_alg );
+#endif
+ return ver->session_key;
+}
+
+int srp_verifier_get_session_key_length( struct SRPVerifier * ver )
+{
+#ifdef APPLE_VARIANT
+ return 2 * hash_length( ver->hash_alg );
+#else
+ return hash_length( ver->hash_alg );
+#endif
+}
+
+/* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */
+void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK )
+{
+ if ( memcmp( ver->M, user_M, hash_length(ver->hash_alg) ) == 0 )
+ {
+ ver->authenticated = 1;
+ *bytes_HAMK = ver->H_AMK;
+ }
+ else
+ *bytes_HAMK = NULL;
+}
+
+/*******************************************************************************/
+#if 0 /*begin removed section 4 */
+struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
+ const unsigned char * bytes_password, int len_password,
+ const char * n_hex, const char * g_hex,
+ int rfc5054_compat )
+{
+ struct SRPUser *usr = (struct SRPUser *) malloc( sizeof(struct SRPUser) );
+ int ulen = strlen(username) + 1;
+
+ if (!usr)
+ goto err_exit;
+
+ init_random(); /* Only happens once */
+ usr->hash_alg = alg;
+ usr->ng = new_ng( ng_type, n_hex, g_hex );
+ usr->a = BN_new();
+ usr->A = BN_new();
+ usr->S = BN_new();
+
+ if (!usr->ng || !usr->a || !usr->A || !usr->S)
+ goto err_exit;
+
+ usr->username = (const char *) malloc(ulen);
+ usr->password = (const unsigned char *) malloc(len_password);
+ usr->password_len = len_password;
+
+ if (!usr->username || !usr->password)
+ goto err_exit;
+
+ memcpy((char *)usr->username, username, ulen);
+ memcpy((char *)usr->password, bytes_password, len_password);
+
+ usr->authenticated = 0;
+ usr->rfc5054 = rfc5054_compat;
+
+ usr->bytes_A = 0;
+
+ return usr;
+
+ err_exit:
+ if (usr)
+ {
+ BN_free(usr->a);
+ BN_free(usr->A);
+ BN_free(usr->S);
+ if (usr->username)
+ free((void*)usr->username);
+ if (usr->password)
+ {
+ memset((void*)usr->password, 0, usr->password_len);
+ free((void*)usr->password);
+ }
+ free(usr);
+ }
+
+ return 0;
+}
+
+void srp_user_delete( struct SRPUser * usr )
+{
+ if( usr )
+ {
+ BN_free( usr->a );
+ BN_free( usr->A );
+ BN_free( usr->S );
+ delete_ng( usr->ng );
+
+ memset((void*)usr->password, 0, usr->password_len);
+ free((char *)usr->username);
+ free((char *)usr->password);
+ if (usr->bytes_A)
+ free( (char *)usr->bytes_A );
+
+ memset(usr, 0, sizeof(*usr));
+ free( usr );
+ }
+}
+
+int srp_user_is_authenticated( struct SRPUser * usr)
+{
+ return usr->authenticated;
+}
+
+const char * srp_user_get_username( struct SRPUser * usr )
+{
+ return usr->username;
+}
+
+const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length )
+{
+ if (key_length)
+#ifdef APPLE_VARIANT
+ *key_length = 2 * hash_length( usr->hash_alg );
+#else
+ *key_length = hash_length( usr->hash_alg );
+#endif
+ return usr->session_key;
+}
+
+int srp_user_get_session_key_length( struct SRPUser * usr )
+{
+#ifdef APPLE_VARIANT
+ return 2 * hash_length( usr->hash_alg );
+#else
+ return hash_length( usr->hash_alg );
+#endif
+}
+
+/* Output: username, bytes_A, len_A */
+void srp_user_start_authentication( struct SRPUser * usr, const char ** username,
+ const unsigned char ** bytes_A, int * len_A )
+{
+ BN_CTX *ctx = BN_CTX_new();
+ BN_rand(usr->a, 256, -1, 0);
+ BN_mod_exp(usr->A, usr->ng->g, usr->a, usr->ng->N, ctx);
+ BN_CTX_free(ctx);
+
+ *len_A = BN_num_bytes(usr->A);
+ *bytes_A = (const unsigned char *)malloc( *len_A );
+
+ if (!*bytes_A)
+ {
+ *len_A = 0;
+ *bytes_A = 0;
+ *username = 0;
+ return;
+ }
+
+ BN_bn2bin( usr->A, (unsigned char *) *bytes_A );
+ usr->bytes_A = *bytes_A;
+ *username = usr->username;
+}
+
+/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */
+void srp_user_process_challenge( struct SRPUser * usr,
+ const unsigned char * bytes_s, int len_s,
+ const unsigned char * bytes_B, int len_B,
+ const unsigned char ** bytes_M, int * len_M)
+{
+ BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL);
+ BIGNUM *B = BN_bin2bn(bytes_B, len_B, NULL);
+ BIGNUM *u = 0;
+ BIGNUM *x = 0;
+ BIGNUM *k = 0;
+ BIGNUM *v = BN_new();
+ BIGNUM *tmp1 = BN_new();
+ BIGNUM *tmp2 = BN_new();
+ BIGNUM *tmp3 = BN_new();
+ BN_CTX *ctx = BN_CTX_new();
+
+ *len_M = 0;
+ *bytes_M = 0;
+
+ if( !s || !B || !v || !tmp1 || !tmp2 || !tmp3 || !ctx )
+ goto cleanup_and_exit;
+
+ if (usr->rfc5054)
+ u = H_nn_rfc5054(usr->hash_alg, usr->ng->N, usr->A, B);
+ else
+ u = H_nn_orig(usr->hash_alg, usr->A, B);
+
+ if (!u)
+ goto cleanup_and_exit;
+
+ x = calculate_x( usr->hash_alg, s, usr->username, usr->password, usr->password_len );
+
+ if (!x)
+ goto cleanup_and_exit;
+
+ if (usr->rfc5054)
+ k = H_nn_rfc5054(usr->hash_alg, usr->ng->N, usr->ng->N, usr->ng->g);
+ else
+ k = H_nn_orig(usr->hash_alg, usr->ng->N, usr->ng->g);
+
+ if (!k)
+ goto cleanup_and_exit;
+
+ /* SRP-6a safety check */
+ if ( !BN_is_zero(B) && !BN_is_zero(u) )
+ {
+ BN_mod_exp(v, usr->ng->g, x, usr->ng->N, ctx);
+
+ /* S = (B - k*(g^x)) ^ (a + ux) */
+ BN_mul(tmp1, u, x, ctx);
+ BN_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */
+ BN_mod_exp(tmp1, usr->ng->g, x, usr->ng->N, ctx);
+ BN_mul(tmp3, k, tmp1, ctx); /* tmp3 = k*(g^x) */
+ BN_sub(tmp1, B, tmp3); /* tmp1 = (B - K*(g^x)) */
+ BN_mod_exp(usr->S, tmp1, tmp2, usr->ng->N, ctx);
+
+#ifdef APPLE_VARIANT
+ hash_session_key(usr->hash_alg, usr->S, usr->session_key);
+#else
+ hash_num(usr->hash_alg, usr->S, usr->session_key);
+#endif
+ calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key );
+ calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key );
+ *bytes_M = usr->M;
+ if (len_M)
+ *len_M = hash_length( usr->hash_alg );
+ }
+ else
+ {
+ *bytes_M = NULL;
+ if (len_M)
+ *len_M = 0;
+ }
+
+ cleanup_and_exit:
+ BN_free(s);
+ BN_free(B);
+ BN_free(u);
+ BN_free(x);
+ BN_free(k);
+ BN_free(v);
+ BN_free(tmp1);
+ BN_free(tmp2);
+ BN_free(tmp3);
+ BN_CTX_free(ctx);
+}
+
+void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK )
+{
+ if ( memcmp( usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg) ) == 0 )
+ usr->authenticated = 1;
+}
+#endif /*end removed section 4 */
diff --git a/lib/srp.h b/lib/srp.h
new file mode 100644
index 0000000..6af96a7
--- /dev/null
+++ b/lib/srp.h
@@ -0,0 +1,236 @@
+/*
+ * Secure Remote Password 6a implementation
+ * Copyright (c) 2010 Tom Cocagne. All rights reserved.
+ * https://github.com/cocagne/csrp
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Tom Cocagne
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *===========================================================================
+ * updated (2023) by fduncanh to replace deprecated openssl SHA* hash functions
+ * modified (2023) by fduncanh for use with Apple's pair-setup-pin protocol
+ */
+
+/*
+ *
+ * Purpose: This is a direct implementation of the Secure Remote Password
+ * Protocol version 6a as described by
+ * http://srp.stanford.edu/design.html
+ *
+ * Author: tom.cocagne@gmail.com (Tom Cocagne)
+ *
+ * Dependencies: OpenSSL (and Advapi32.lib on Windows)
+ *
+ * Usage: Refer to test_srp.c for a demonstration
+ *
+ * Notes:
+ * This library allows multiple combinations of hashing algorithms and
+ * prime number constants. For authentication to succeed, the hash and
+ * prime number constants must match between
+ * srp_create_salted_verification_key(), srp_user_new(),
+ * and srp_verifier_new(). A recommended approach is to determine the
+ * desired level of security for an application and globally define the
+ * hash and prime number constants to the predetermined values.
+ *
+ * As one might suspect, more bits means more security. As one might also
+ * suspect, more bits also means more processing time. The test_srp.c
+ * program can be easily modified to profile various combinations of
+ * hash & prime number pairings.
+ */
+
+#ifndef SRP_H
+#define SRP_H
+#define APPLE_VARIANT
+
+struct SRPVerifier;
+#if 0 /*begin removed section 1*/
+struct SRPUser;
+#endif /*end removed section 1*/
+typedef enum
+{
+#if 0 /* begin removed section 2*/
+ SRP_NG_1024,
+ SRP_NG_1536,
+#endif /* end removed section 2*/
+ SRP_NG_2048,
+#if 0 /* begin removed section 3*/
+ SRP_NG_3072,
+ SRP_NG_4096,
+ SRP_NG_6144,
+ SRP_NG_8192,
+#endif /* end removed section 3*/
+ SRP_NG_CUSTOM
+} SRP_NGType;
+
+typedef enum
+{
+ SRP_SHA1,
+ SRP_SHA224,
+ SRP_SHA256,
+ SRP_SHA384,
+ SRP_SHA512
+} SRP_HashAlgorithm;
+
+
+/* This library will automatically seed the OpenSSL random number generator
+ * using cryptographically sound random data on Windows & Linux. If this is
+ * undesirable behavior or the host OS does not provide a /dev/urandom file,
+ * this function may be called to seed the random number generator with
+ * alternate data.
+ *
+ * The random data should include at least as many bits of entropy as the
+ * largest hash function used by the application. So, for example, if a
+ * 512-bit hash function is used, the random data requies at least 512
+ * bits of entropy.
+ *
+ * Passing a null pointer to this function will cause this library to skip
+ * seeding the random number generator. This is only legitimate if it is
+ * absolutely known that the OpenSSL random number generator has already
+ * been sufficiently seeded within the running application.
+ *
+ * Notes:
+ * * This function is optional on Windows & Linux and mandatory on all
+ * other platforms.
+ */
+void srp_random_seed( const unsigned char * random_data, int data_length );
+
+
+/* Out: bytes_s, len_s, bytes_v, len_v
+ *
+ * The caller is responsible for freeing the memory allocated for bytes_s and bytes_v
+ *
+ * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type.
+ * If provided, they must contain ASCII text of the hexidecimal notation.
+ *
+ */
+void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
+ SRP_NGType ng_type, const char * username,
+ const unsigned char * password, int len_password,
+ const unsigned char ** bytes_s, int * len_s,
+ const unsigned char ** bytes_v, int * len_v,
+ const char * n_hex, const char * g_hex );
+
+
+#ifdef APPLE_VARIANT
+/* Out: bytes_B, len_B
+ * On failure, bytes_B will be set to NULL and len_B will be set to 0
+ *
+ * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type
+ *
+ * bytes_b should be a pointer to a cryptographically secure random array of length
+ * len_b bytes (for example, produced with OpenSSL's RAND_bytes(bytes_b, len_b)).
+ */
+void srp_create_server_ephemeral_key( SRP_HashAlgorithm alg, SRP_NGType ng_type,
+ const unsigned char * bytes_v, int len_v,
+ const unsigned char * bytes_b, int len_b,
+ const unsigned char ** bytes_B, int * len_B,
+ const char * n_hex, const char * g_hex,
+ int rfc5054_compat );
+#endif
+
+/* Out: bytes_B, len_B.
+ *
+ * On failure, bytes_B will be set to NULL and len_B will be set to 0
+ *
+ * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type
+ *
+ * If rfc5054_compat is non-zero the resulting verifier will be RFC 5054 compaliant. This
+ * breaks compatibility with previous versions of the csrp library but is recommended
+ * for new code.
+ */
+struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
+ const unsigned char * bytes_s, int len_s,
+ const unsigned char * bytes_v, int len_v,
+ const unsigned char * bytes_A, int len_A,
+#ifdef APPLE_VARIANT
+ const unsigned char * bytes_b, int len_b,
+#endif
+ const unsigned char ** bytes_B, int * len_B,
+ const char * n_hex, const char * g_hex,
+ int rfc5054_compat );
+
+
+void srp_verifier_delete( struct SRPVerifier * ver );
+
+
+int srp_verifier_is_authenticated( struct SRPVerifier * ver );
+
+
+const char * srp_verifier_get_username( struct SRPVerifier * ver );
+
+/* key_length may be null */
+const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length );
+
+
+int srp_verifier_get_session_key_length( struct SRPVerifier * ver );
+
+
+/* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */
+/* (in APPLE_VARIANT case, session_key_length is DOUBLE the length of user_M) */
+void srp_verifier_verify_session( struct SRPVerifier * ver,
+ const unsigned char * user_M,
+ const unsigned char ** bytes_HAMK );
+
+/*******************************************************************************/
+
+/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type
+ *
+ * If rfc5054_compat is non-zero the resulting verifier will be RFC 5054 compaliant. This
+ * breaks compatibility with previous versions of the csrp library but is recommended
+ * for new code.
+ */
+#if 0 /*begin removed section 4 */
+struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
+ const unsigned char * bytes_password, int len_password,
+ const char * n_hex, const char * g_hex,
+ int rfc5054_compat );
+
+void srp_user_delete( struct SRPUser * usr );
+
+int srp_user_is_authenticated( struct SRPUser * usr);
+
+
+const char * srp_user_get_username( struct SRPUser * usr );
+
+/* key_length may be null */
+const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length );
+
+int srp_user_get_session_key_length( struct SRPUser * usr );
+
+/* Output: username, bytes_A, len_A */
+void srp_user_start_authentication( struct SRPUser * usr, const char ** username,
+ const unsigned char ** bytes_A, int * len_A );
+
+/* Output: bytes_M, len_M (len_M may be null and will always be
+ * srp_user_get_session_key_length() bytes in size) */
+/* (in APPLE_VARIANT case, session_key_length is DOUBLE the length of bytes_M) */
+void srp_user_process_challenge( struct SRPUser * usr,
+ const unsigned char * bytes_s, int len_s,
+ const unsigned char * bytes_B, int len_B,
+ const unsigned char ** bytes_M, int * len_M );
+
+/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */
+/* (in APPLE_VARIANT case, session_key_length is DOUBLE the length of bytes_HAMK) */
+void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK );
+
+#endif /*end removed section 4*/
+#endif /* Include Guard */
diff --git a/lib/stream.h b/lib/stream.h
new file mode 100644
index 0000000..6905dcd
--- /dev/null
+++ b/lib/stream.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 dsafa22, All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2022-2023
+ */
+
+#ifndef AIRPLAYSERVER_STREAM_H
+#define AIRPLAYSERVER_STREAM_H
+
+#include
+#include
+
+typedef struct {
+ bool is_h265;
+ int nal_count;
+ unsigned char *data;
+ int data_len;
+ uint64_t ntp_time_local;
+ uint64_t ntp_time_remote;
+} video_decode_struct;
+
+typedef struct {
+ unsigned char *data;
+ unsigned char ct;
+ int data_len;
+ int sync_status;
+ uint64_t ntp_time_local;
+ uint64_t ntp_time_remote;
+ uint64_t rtp_time;
+ unsigned short seqnum;
+} audio_decode_struct;
+
+#endif //AIRPLAYSERVER_STREAM_H
diff --git a/lib/threads.h b/lib/threads.h
new file mode 100644
index 0000000..d9881da
--- /dev/null
+++ b/lib/threads.h
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2022
+ */
+
+#ifndef THREADS_H
+#define THREADS_H
+
+/* Always use pthread library */
+
+#include
+#include
+
+#define sleepms(x) usleep((x)*1000)
+
+typedef pthread_t thread_handle_t;
+
+#define THREAD_RETVAL void *
+#define THREAD_CREATE(handle, func, arg) \
+ if (pthread_create(&(handle), NULL, func, arg)) handle = 0
+#define THREAD_JOIN(handle) pthread_join(handle, NULL)
+
+typedef pthread_mutex_t mutex_handle_t;
+
+typedef pthread_cond_t cond_handle_t;
+
+#define MUTEX_CREATE(handle) pthread_mutex_init(&(handle), NULL)
+#define MUTEX_LOCK(handle) pthread_mutex_lock(&(handle))
+#define MUTEX_UNLOCK(handle) pthread_mutex_unlock(&(handle))
+#define MUTEX_DESTROY(handle) pthread_mutex_destroy(&(handle))
+
+#define COND_CREATE(handle) pthread_cond_init(&(handle), NULL)
+#define COND_SIGNAL(handle) pthread_cond_signal(&(handle))
+#define COND_DESTROY(handle) pthread_cond_destroy(&(handle))
+
+#endif /* THREADS_H */
diff --git a/lib/utils.c b/lib/utils.c
new file mode 100644
index 0000000..8f82186
--- /dev/null
+++ b/lib/utils.c
@@ -0,0 +1,295 @@
+/**
+ * Copyright (C) 2011-2012 Juho Vähä-Herttua
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ *=================================================================
+ * modified by fduncanh 2021-2022
+ */
+
+#include