pax_global_header00006660000000000000000000000064135607055320014520gustar00rootroot0000000000000052 comment=6204e9c439481b35dbea3502559c41eefdd0d1d7 dms-1.1.0/000077500000000000000000000000001356070553200123025ustar00rootroot00000000000000dms-1.1.0/.circleci/000077500000000000000000000000001356070553200141355ustar00rootroot00000000000000dms-1.1.0/.circleci/config.yml000066400000000000000000000033121356070553200161240ustar00rootroot00000000000000version: 2 jobs: build: machine: true environment: GO_BRANCH: release-branch.go1.13 steps: - run: echo $CIRCLE_WORKING_DIRECTORY - run: echo $PWD - run: echo $GOPATH - run: echo 'export GOPATH=$HOME/go' >> $BASH_ENV - run: echo 'export PATH="$GOPATH/bin:$PATH"' >> $BASH_ENV - run: echo $GOPATH - run: which go - run: go version - run: | cd /usr/local sudo mkdir go.local sudo chown `whoami` go.local - restore_cache: key: go-local- - run: | cd /usr/local git clone git://github.com/golang/go go.local || true cd go.local git fetch git checkout "$GO_BRANCH" [[ -x bin/go && `git rev-parse HEAD` == `cat anacrolix.built` ]] && exit cd src ./make.bash || exit git rev-parse HEAD > ../anacrolix.built - save_cache: paths: /usr/local/go.local key: go-local-{{ checksum "/usr/local/go.local/anacrolix.built" }} - run: echo 'export PATH="/usr/local/go.local/bin:$PATH"' >> $BASH_ENV - run: go version - checkout - restore_cache: keys: - go-pkg- - restore_cache: keys: - go-cache- - run: go mod download - run: go test -v -race ./... -count 2 -bench . - run: go test -bench . ./... - run: set +e; CGO_ENABLED=0 go test -v ./...; true - run: GOARCH=386 go test ./... -count 2 -bench . - save_cache: key: go-pkg-{{ checksum "go.mod" }} paths: - ~/go/pkg - save_cache: key: go-cache-{{ .Revision }} paths: - ~/.cache/go-build dms-1.1.0/.github/000077500000000000000000000000001356070553200136425ustar00rootroot00000000000000dms-1.1.0/.github/FUNDING.yml000066400000000000000000000013211356070553200154540ustar00rootroot00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: anacrolix # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] dms-1.1.0/LICENSE000066400000000000000000000030071356070553200133070ustar00rootroot00000000000000Copyright (c) 2012, Matt Joiner . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.dms-1.1.0/README.rst000066400000000000000000000027271356070553200140010ustar00rootroot00000000000000dms === .. image:: https://circleci.com/gh/anacrolix/dms.svg?style=svg :target: https://circleci.com/gh/anacrolix/dms dms is a UPnP DLNA Digital Media Server. It runs from the terminal, and serves content directly from the filesystem from the working directory, or the path given. The SSDP component will broadcast and respond to requests on all available network interfaces. dms advertises and serves the raw files, in addition to alternate transcoded streams when it's able, such as mpeg2 PAL-DVD and WebM for the Chromecast. It will also provide thumbnails where possible. dms uses ``ffprobe``/``avprobe`` to get media data such as bitrate and duration, ``ffmpeg``/``avconv`` for video transoding, and ``ffmpegthumbnailer`` for generating thumbnails when browsing. These commands must be in the ``PATH`` given to ``dms`` or the features requiring them will be disabled. .. image:: https://lh3.googleusercontent.com/-z-zh7AzObGo/UEiWni1cQPI/AAAAAAAAASI/DRw9IoMMiNs/w497-h373/2012%2B-%2B1 Installing ========== Assuming ``$GOPATH`` and Go have been configured already:: $ go get github.com/anacrolix/dms Ensure ``ffmpeg``/``avconv`` and/or ``ffmpegthumbnailer`` are in the ``PATH`` if the features depending on them are desired. To run:: $ "$GOPATH"/bin/dms Known Compatible Players and Renderers ====================================== * Probably all Panasonic Viera TVs. * Android's BubbleUPnP and AirWire * Chromecast * VLC * LG Smart TVs, with varying success. dms-1.1.0/TODO000066400000000000000000000006431356070553200127750ustar00rootroot00000000000000* Reintegrate ffprobe error suppression into the ffmpeg.Probe function * Replace panics with proper error handling throughout the codebase. * Move ./dlna/dms somewhere more appropriate. It's moreof a DMS than a DLNADMS now. * Fix seeking for transcodes. Should be broken. * DMS handler path /icon should be /thumbnail, and /deviceIcon->/icon, or something like that. * Work around lack of ffmpegthumbnailer on Windows. dms-1.1.0/bindata.go000066400000000000000000002767571356070553200142630ustar00rootroot00000000000000// Code generated by go-bindata. // sources: // data/VGC Sonic.png // DO NOT EDIT! package main import ( "bytes" "compress/gzip" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "time" ) func bindataRead(data []byte, name string) ([]byte, error) { gz, err := gzip.NewReader(bytes.NewBuffer(data)) if err != nil { return nil, fmt.Errorf("Read %q: %v", name, err) } var buf bytes.Buffer _, err = io.Copy(&buf, gz) clErr := gz.Close() if err != nil { return nil, fmt.Errorf("Read %q: %v", name, err) } if clErr != nil { return nil, err } return buf.Bytes(), nil } type asset struct { bytes []byte info os.FileInfo } type bindataFileInfo struct { name string size int64 mode os.FileMode modTime time.Time } func (fi bindataFileInfo) Name() string { return fi.name } func (fi bindataFileInfo) Size() int64 { return fi.size } func (fi bindataFileInfo) Mode() os.FileMode { return fi.mode } func (fi bindataFileInfo) ModTime() time.Time { return fi.modTime } func (fi bindataFileInfo) IsDir() bool { return false } func (fi bindataFileInfo) Sys() interface{} { return nil } var _dataVgcSonicPng = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x9a\xd3\x5a\x2e\x8e\xc3\x28\xb3\xdd\x9b\xed\x37\xdb\xb6\x6d\xdb\xb5\x5a\xd9\x76\x2b\xdb\xb6\x6d\xdb\xf5\xcb\xb6\x6d\x63\x3f\xff\x83\x7d\x03\xdf\xc1\x3c\x73\x36\x57\x30\x91\x4a\x0a\x92\x88\x70\xb8\x70\x20\x20\x20\x88\xd2\x52\x62\x2a\x20\x20\x20\x01\xff\x03\x06\x0a\x04\x04\x64\x48\xc0\x78\x0a\x04\x84\x18\x44\x5a\x4c\x58\xcd\x23\xfb\xaa\xc7\xbd\xdb\x7c\x55\xfd\xf4\x7d\x7b\x3f\xdb\xf9\xd6\xb7\xc1\x29\xa5\xb2\x98\x86\x76\x0a\x4e\x61\x10\xd4\x52\x77\x8d\xad\x35\xcb\x0a\x1c\x91\x0d\x31\x5d\x71\x51\xde\x34\x93\x10\x2a\xf3\x06\x54\xce\x66\x68\x1a\x84\x9f\x9f\x32\xb6\x00\x76\x11\xdc\xd4\xb4\x1f\x7b\x73\x1c\x53\x2b\x95\x0e\x5a\x3a\xb5\x46\x29\x95\x42\x9d\x5a\x0d\xe9\x76\x59\x96\x33\xdb\xd9\x4d\xeb\x71\x6c\xe7\xb4\x97\x0a\xc2\xc3\xae\xf3\x37\x70\xe3\x63\x49\xf2\x76\xdb\xfd\xb6\xe3\xf5\xaa\xe5\x45\xa7\xef\x59\x77\x7f\x70\xc4\xd5\x51\x5f\x0f\x6b\xf5\xff\xa6\xdf\xbf\xa9\x1f\x9f\xdd\x6f\x2f\x5d\xbf\x0f\xf2\x96\x63\x8f\xbf\xd5\x91\x17\x0f\x83\xd8\x04\x14\x51\x30\xd8\x35\x77\xca\xf3\x01\x2b\xb8\x00\x93\x96\x44\x14\x48\xf9\x62\xb7\xdc\x46\xe0\x7f\x70\x28\x30\x68\xcc\x32\xc1\x60\xa6\x6c\x24\xc3\xe7\x63\xaa\x70\x62\x10\xe4\xbe\x3a\x25\x00\x52\x14\x71\x26\x5c\x31\x21\x88\x92\x16\x36\x70\xf4\x9b\x6d\x56\xd6\xaf\x99\x2c\x07\xaf\x67\xac\x9f\x5e\xbc\xc7\x38\x73\xdf\x75\xb8\xbb\xdc\xb8\x9d\x7b\xdb\xb1\x31\xde\x3e\x64\x48\x9d\x40\x66\x14\x70\x20\x24\xae\x38\xb9\x91\x7d\x24\x4a\x7c\xfd\x3b\x8f\xad\xbc\xdf\x66\x84\x03\xd9\x24\x10\x90\x1f\x4d\xc9\x13\xce\x80\xc0\x0c\x01\x80\x2a\x62\x4b\x28\x46\x90\x86\x06\x53\x57\xfe\x8f\x27\x8f\x08\x1d\x62\xbe\xb2\x0b\x51\xde\xf8\xcb\xaa\x8e\xff\x83\xdf\xc1\xeb\x38\xeb\x67\x97\xf1\x75\xe7\x87\xb9\x86\x51\x16\xf4\x67\x4f\xd4\xff\xa3\xb5\xec\x80\xfd\x77\x4c\x04\x95\x99\x86\x54\xc8\x18\x10\x83\xde\xac\x01\xd0\x10\x86\x60\x0c\x7a\x0d\x62\x3f\x27\x07\xb7\x32\x3a\x02\x83\x0f\x0e\x5a\x91\x44\x49\x01\x48\x51\x83\x89\x44\x07\xa9\x48\x53\x17\xc2\x2c\x62\xc9\x78\xf9\x5c\x5e\xfc\xd0\xe9\xfe\xec\x78\xfc\x83\xb7\xf7\xb5\x7f\xbe\xa3\xed\xfb\x68\x71\xe8\xf8\x9c\xda\x4e\xf7\xa7\x93\xf5\xaf\x60\x6c\x7f\x5e\xd9\xcd\xe2\x5e\xbf\xfa\x69\xf3\x2d\x62\xa6\xcc\x51\x44\xb5\xd2\x2b\x86\x3d\x37\xe3\x61\xf4\x40\x05\x30\x9d\xd9\x26\x6b\xc0\x48\x8f\x83\x04\x37\x82\x48\x97\xe2\x87\x00\x83\xfe\x25\xa0\x24\xe5\xa4\x7d\x19\xc2\x18\xb9\xfa\x05\x77\x8b\x99\x7d\x28\x56\x14\x0f\x70\x5c\x86\x32\xcf\xb7\xa1\x9a\x7f\xf6\x1c\x1e\x5e\x0b\x7e\xbd\xae\xa5\xf9\xb3\x5d\xca\x6a\x04\x2c\xce\xec\xc5\xa7\x67\x4c\xd1\xc5\xa2\x10\x8b\x91\x4c\xc8\x33\xeb\x02\x16\x4f\xa5\x35\x02\x96\x95\xff\xcd\x09\xf4\xb8\x25\x70\xc0\xe1\x0e\x84\x4b\xd5\xcd\xbb\x14\xc6\x34\x7a\x16\xb6\xa8\x00\x4b\x98\x30\x7d\x50\x48\xb3\x2c\x0c\x2e\x9f\xfc\x49\xb4\x61\x40\x45\xfc\xec\xaf\x43\x88\x03\x71\xf0\x2a\x96\xb7\x1e\x77\xbe\x6f\xdc\xd3\x0c\x7f\x56\xbe\x0e\xb5\x0c\x1b\xc1\xd4\x36\xe8\xc5\x06\xb0\x9b\x60\x67\xed\x04\x22\x03\xc9\x5d\x6a\xf2\xeb\x79\x32\xae\xca\xe7\x65\x8c\x63\x0a\x35\xcf\x20\x50\x28\xd0\xc4\xac\xbe\x4b\xb5\x91\x13\xb7\xc2\x23\xc8\x65\x89\xe7\xad\x25\x53\x76\xfb\x37\xec\xd8\x1b\xff\xdd\x25\xd2\x41\x06\x2c\xc3\x25\x6d\x0b\x1f\x92\x01\xb7\x4d\xb1\xb0\x30\xf6\xfe\x55\x33\x6a\x83\xe2\xd6\xd5\xa2\x9a\xff\x27\x83\x5a\xb8\x27\x22\x84\x13\x13\xa7\xad\x43\x93\x73\x82\x92\x48\x0c\xcd\x2f\x6b\x8d\x31\x2b\xee\x13\xed\x81\x6a\x47\xf5\x84\x0d\x4b\x82\xd6\xd4\xc2\x64\xd7\x09\x9b\x82\xda\x8b\x75\x47\xa2\xbf\xe8\x49\x22\xb5\x10\x2d\x62\x0b\x57\x13\x81\xba\x91\x0e\x90\x69\xac\x02\x56\x8b\xd8\xf2\x37\xcd\xaf\x04\xff\x12\x66\x3d\x21\x7b\xfa\x4e\xc0\x31\x99\x8c\x5d\x7a\xab\x0b\x23\x04\xfa\xa9\xa3\x98\x14\xcf\x67\xe6\x2a\x8b\x9d\x33\x00\x71\xfa\x03\xff\x89\xc0\x4c\xb4\xc2\xc3\xd1\xa3\x28\x11\x4f\xb8\xba\x80\x8a\x34\x22\xc0\xc2\x23\x8c\xd1\xc7\xfc\xc3\xae\xaa\x87\x13\x0b\x16\x13\xbe\xb4\x43\x54\x02\xe6\x54\x87\xa8\x18\xa9\x0f\x62\x53\x93\x03\xd8\x02\x35\x26\xed\xd1\xa3\x68\x7f\x3f\xed\x97\x05\xa7\x9f\x29\xa8\xfb\x3f\x05\x9e\x74\x57\xab\xa7\x06\xf1\x10\x73\x89\xf3\xc5\xb4\xb0\xd5\x80\x14\xed\x76\xad\xfa\xc9\x0c\x1c\x92\xd2\x0f\xaf\xe7\x0d\xd5\x80\xf8\x78\x81\x00\x18\x8d\x12\x18\xd2\x51\x33\x66\x1f\x80\x58\xd9\x29\x57\x5e\x80\xf1\xdf\x09\x19\x36\x79\xe2\xe1\x08\xe4\x04\xe9\xac\x1f\x4a\xd2\xf2\x2b\x0e\x13\x0d\x9a\x53\x48\xb5\x81\x7b\xe3\xdf\xcd\x1f\xe8\xab\x75\xc1\xf2\xff\xd5\xa9\xdd\xd7\x15\x01\xc7\x83\x72\xc2\x68\xe3\xc5\xcf\xc3\xd8\x38\x2b\x02\xc6\x20\xd2\x83\xb0\xda\xc4\xf6\x80\x4c\x62\xed\x76\xd4\x1a\xf4\xbf\xf0\xcc\x07\x0f\xbb\xa2\x52\x22\x79\xcd\xb1\xa5\x31\x00\xa0\x57\xe8\x48\xf9\xd1\x54\x11\x8c\x80\x84\x4c\x95\xf6\x46\x3a\x53\x35\xb8\x86\xf5\xa8\x7c\x7f\x68\x04\xb2\x08\xfa\x41\x29\x98\x18\x09\xc4\x5c\x7e\x44\x76\x1c\x74\xef\x2d\xb2\xc0\x93\xe5\xef\x81\xff\xf4\xca\xf7\x90\xd6\x6f\x9f\x47\xc0\x9c\xdf\x8b\x22\x26\x91\x00\xef\xa3\xa9\x24\xd5\xa7\x9f\x59\x2d\x80\x98\x12\x93\x91\xd5\xd3\xee\xe7\xd6\x7a\x9f\xf0\x9d\x6d\x03\xbf\x8c\x9f\xbe\x75\x2e\x5f\xf1\xb8\xbe\x4e\x37\x91\x28\xce\xf1\xb1\x41\xc4\xb6\x79\x65\x69\xd6\x34\x59\x04\x07\x5b\x01\xff\x7b\xa1\x50\x0d\x2c\x43\x3e\x02\x20\x53\xc0\xa6\x8a\x52\x61\x1f\xf9\x38\xc0\x08\xc0\x23\xc2\x03\xe8\x09\x25\xc3\xae\xc3\x51\xdc\x51\x2f\xc2\x2c\x62\x23\x6a\x47\x72\xec\x72\xd7\x60\x27\xe1\xb1\xc4\x5b\x55\xac\x39\x22\xa3\x26\x8f\x9e\x3d\xb5\x87\x44\xa8\xe0\xd6\x14\xb8\x4e\x74\xe0\xd8\x97\x6f\x62\x46\x24\x9b\x0b\xc3\x18\x45\x0f\x86\x0b\x21\x04\x69\xc8\x89\x9e\x97\xc1\x52\xd8\x27\xc0\x91\xe8\xce\xa0\x44\x04\xb0\x53\x1e\x1e\x4a\xc1\x88\x5c\xe3\x7f\x57\x16\xdb\xf3\x33\x43\x2d\x5f\x22\x8e\x3e\x8e\x1c\xfd\x27\x60\xeb\x7f\x47\xef\xf0\x96\x2a\xd9\x97\x6e\xd7\x57\x0e\xe6\x73\x85\xee\xef\x75\x38\x74\x09\xfd\x73\xb5\x55\xec\x6e\x55\x1b\x0d\x10\xd3\xfb\x69\x2e\x51\x6f\x9f\x3f\x17\x16\x79\x0a\x23\x27\x86\x85\x2d\x71\xcf\xb2\x4b\x31\xd9\x92\x98\x14\x31\xae\x19\x7c\x97\x02\x94\xc3\xe5\x70\x84\x02\x66\x13\xc8\xe0\x4c\x55\x60\x60\xa8\x83\xee\x48\x94\xfb\x24\x70\x36\x50\x98\xa0\x60\x60\x45\x01\x50\x7a\xe3\x05\x65\x2a\x8b\xbf\x65\x25\x8e\x89\x80\x99\x70\xfc\x92\x74\x85\xc6\x80\xf8\x21\x39\x94\x14\x90\x32\x75\xea\xc2\xff\x80\x4c\xf9\xa2\x54\xe4\xc2\x55\x21\xea\x22\x50\x4d\xc2\x0b\x02\xc5\xff\xc8\x05\x86\x69\xc4\xc9\xa9\x1b\x5f\x51\x4d\xd9\xe1\xe4\x01\x8a\x08\x72\xfa\x29\xec\x34\xb4\x59\x85\xae\x81\x83\x34\x36\x58\xd4\x35\xc2\x11\x52\x52\x79\x2d\x87\xda\xcd\xc6\x32\x89\xd4\xfd\xde\x05\x31\x09\x93\x36\xd1\x1c\x30\x22\x4f\x09\xf9\x00\x9e\xd6\xc3\xaa\x28\xc9\xbe\x6f\xef\x9d\x87\x1c\xff\x07\x73\xda\xfe\x9e\xd7\x1f\x09\xc7\xd5\x48\x5b\x41\x81\x56\x6f\x43\x2d\x65\x53\x17\x39\xa2\x61\x1a\x1a\x7b\xae\x1e\x16\xb3\x56\x43\x66\x55\x1d\x5c\x22\x60\xb4\x88\x75\xf0\xab\xf6\xa7\x3c\x5a\x7a\x1c\x68\xa8\x85\xbd\x4a\xd3\xfc\x2d\x68\xc2\xca\xd4\xd3\x2d\x33\x04\x9f\x76\x97\xe8\x20\x16\x14\x93\x5b\x12\x11\x71\x29\x74\x0a\x30\x59\x06\x0f\x60\xa2\xd2\x43\xea\x50\x1c\x12\x2b\x05\x52\x85\x10\x18\x7f\x27\xe5\x1e\x15\x30\x89\x8b\x4b\x54\xba\x02\x0c\x2d\x87\xc2\x02\x5b\xc1\xf6\x81\xc1\x16\xc2\x13\x27\x17\x67\x4d\x91\x07\x59\x85\x2e\x60\x92\xf6\xee\x26\x8f\xda\xc4\x9e\x9f\x54\xf2\x8e\x9b\x68\x6e\x7d\x0c\x02\xb5\x4b\xa8\xbd\x14\x9d\xfc\x47\x7c\xfe\xee\x0a\x70\x21\x26\x00\x48\x49\xe6\xb8\x18\x65\x72\xa8\x9c\xb9\x48\xa9\x0c\x57\x59\xeb\xf5\x17\xab\xc1\xce\x8a\xf6\xc9\xfc\xa7\x38\x5d\xa7\x8f\xc7\x8b\x09\x05\x79\x28\x91\x7e\x6e\x0d\x21\x4f\xe0\xd5\xee\xe6\xfa\x7e\x72\x70\x37\xb3\xfb\x51\xf3\x7b\x31\xd1\x27\x3f\xfa\x72\xab\x12\x30\x34\xe6\xdd\x96\x6d\x7b\x6a\xd0\xde\x64\x05\xb3\x73\xd8\xcd\x64\x43\x56\xa2\x19\x11\x33\xfd\x22\x5b\x8d\x9a\x8d\x1c\xbd\x21\xb4\x6b\xa5\xf7\xa4\x55\xe4\x48\x52\x08\x6a\xea\xc3\x52\xa0\xac\xac\x35\x55\xec\xea\x09\x0e\x69\x08\x5d\x2d\x8f\x77\xe8\x3b\x9c\x1e\x81\x26\x0a\xc3\xc0\x1c\x29\xac\xb4\x51\xaf\xa7\xc9\x26\x40\x3d\xf5\xc0\xc4\xc4\x5b\xff\x54\x5d\xd0\x18\x38\x88\x4d\xb3\xa9\x02\x48\x10\x2e\x40\x65\x96\xa3\x44\xaa\xc9\x6f\x4e\x8d\x40\xf1\xc9\xce\xfd\x40\x09\x89\x20\x46\x73\x14\x06\xc6\x14\xde\xe3\x0c\x99\x2d\x4c\xa0\x2a\x09\x17\x49\x91\x94\xc2\x4b\x57\xc4\x84\x39\x1e\x4e\xf0\x04\xac\x84\x4d\x90\xe7\x27\x8f\xfd\x43\x21\x53\xc3\x2c\x94\x29\x4e\x7e\x21\x0c\x1e\x94\x1e\xf6\x2e\xa0\x41\x30\xc5\x42\x0d\x44\x51\x91\xaa\xc6\x48\x96\x40\x25\x6d\x27\x86\x88\x8c\x86\x3c\x81\xa6\xc0\x3b\xc4\x44\x35\x2e\x35\x6c\x60\xf9\x73\x8e\x97\x23\x72\x68\x97\x80\x90\x0f\x2f\xaf\xae\xe3\x13\x0d\xa4\x40\xb8\x8c\x58\x8e\x6a\x8c\x90\x7b\xda\x8a\x35\x9d\x99\x6e\x03\x15\x7c\x35\xa7\xee\x9f\x2c\xf7\x68\x68\xca\x84\x1f\x62\x96\xa1\x30\x23\x0c\x16\x62\xab\x56\x04\x93\x80\x67\x0e\x88\x0b\xc2\x9a\xc3\x45\x11\x43\xa7\x30\x62\xc2\xce\x9f\x30\xc6\xc1\x26\x97\x28\x14\x03\x24\xfa\x9a\x4d\xf7\xd3\xe7\xf0\xdc\x82\xc7\x2c\x66\x57\x62\x69\xe3\xbc\x54\x9c\xa3\xeb\x47\x32\x8b\x90\x44\xa9\x90\x96\x60\xd0\x60\x00\x53\x0a\x69\x68\x2a\x65\x54\x11\x2d\xa8\xe1\xe4\xeb\xac\xa8\xaf\x23\x46\x63\x50\xe8\x81\x35\xf5\x34\x79\x5a\x44\xf3\x94\xef\x66\xb7\xe0\x2b\xb8\xcd\x01\x53\x22\x30\x52\x69\x8e\x32\xa9\x56\x55\xaa\x12\xf2\x35\x23\x1a\x5b\x28\x26\xfd\x56\x8b\xef\x97\x57\xa3\xe4\xe2\x52\xef\x29\x06\x21\x2a\xd4\x3e\x4f\xcd\xca\xe5\x54\xb9\x19\xc2\x90\x9e\x8a\xa5\xdd\x52\xad\x60\x93\xd8\x3a\xa7\xda\x3a\x75\x15\x39\x45\x7f\xc0\x78\x4f\x3f\x80\x90\xb6\x38\xce\xd4\x18\xd4\xd5\x23\x90\xd2\xed\xd0\x77\x42\xea\xe4\xa7\xf9\x29\x22\xec\x48\xca\xa9\x18\xab\xde\xf7\xbb\x14\x87\x96\xc0\xfe\x32\xc2\x6e\xfb\x38\x8d\xef\xa9\x4c\x8e\x56\x77\xfe\x4e\xa7\x1e\x54\x64\x52\x8f\xc0\x08\x14\xd3\x61\xd9\x52\xb7\xcd\x23\x78\x53\x18\xa0\xbd\x12\xcc\xce\xe7\xd5\x27\x59\x22\xba\x88\x44\x08\x07\x4f\xb3\x58\x76\x5c\x32\x0d\x93\x63\xec\x3c\x14\x5b\xe0\xb1\x63\x5c\xf8\x0a\xa3\xb1\x3d\x57\xdc\x4b\x2e\x5a\x01\x5b\xa4\x7c\xb3\x63\xd0\x4a\x22\x7e\x0f\xbb\x18\x74\xc1\xd5\xfe\x3f\xb4\x6b\xdc\x68\xae\x68\x15\x2b\xe8\x06\x9a\x88\x65\x52\xf0\x26\x29\x6d\xa1\xc3\xd5\xb5\xf2\x34\xa5\x75\x87\x43\xe1\xf3\x80\x38\x23\x1f\x30\xc6\x59\x4f\xb8\xa4\x56\x12\x51\x14\x31\x3e\x65\x88\x2a\x0c\x19\xf7\xb5\xfd\x73\x23\x8c\xc4\x36\x47\x66\x25\x9b\x05\xf6\xc0\xfc\x90\x6e\xfe\xc8\x65\x40\x79\xf4\xa0\x34\x0f\x5f\x69\x7d\xb4\x3e\x3c\x89\x39\x67\x0f\xf9\x2b\x0a\x84\x1a\x4c\x39\xfd\xa4\x65\x4c\x81\x26\xec\xa5\x7c\x30\x3b\x26\x05\x6a\xca\xc0\x64\xb1\x3d\xb3\x0e\x7a\xf2\x20\x4d\x68\xe3\x84\x7a\x79\xfa\x56\xb8\x72\x66\x3e\x5e\xe1\xb9\xe1\xc7\x4d\xdf\x5b\x12\xd6\x8b\x17\xff\xf9\xfa\xcb\xd1\xa5\xc1\x4b\x4b\x59\xae\x1b\xf2\x36\xb2\xbf\xcf\x26\xd1\x6d\xfc\xce\x8f\x21\x8c\xd7\x7e\x96\x38\x46\x93\x8e\x75\x75\x8b\x1a\x65\xbe\x12\x31\x35\xd4\xaa\x8c\x29\x62\x08\x08\x70\xc0\x64\x5e\x3c\xfc\x04\xd4\xa5\xed\x1f\xf6\x02\x14\x2a\x69\x8c\x03\x31\x8c\xeb\x22\x4a\xf0\x50\x0b\x62\x9b\x60\xe5\x5b\xce\x1c\xc2\xdf\x43\x33\x4e\xda\xa2\x72\x36\x00\x8d\x92\x56\x3d\x1a\xb3\x79\xe4\xf5\x9c\x49\xc9\x40\x3b\x6c\x2b\x81\xf1\x45\x12\xa0\x79\x4d\x3f\x6d\x71\xd3\x0d\x27\xc5\x4e\x29\x25\xca\xcf\xfc\x2c\x6d\xbe\xe9\x9c\x4d\x2d\xa5\xa0\x3e\x2a\xe7\x91\x5e\x93\x88\x8e\xba\x7a\xd4\x5b\x98\x3c\x00\x2a\x58\x1a\x67\x1c\x04\xa5\x7e\x23\x00\xea\x34\xa4\x09\x8f\xcc\x6d\x56\xcd\xca\x5e\x99\xa6\xc0\xf7\x7c\xbe\x0f\x0e\xc8\x1c\x11\x72\xce\xff\x2f\xc4\x9a\x61\xc8\x46\x34\x82\x38\x2c\x44\x5f\x0a\x54\x34\x19\xc4\x2a\x99\x92\xf7\x2e\x79\xf3\x6e\x20\x58\x01\x53\x95\x0d\xc1\x76\x2c\xba\x25\xbd\x9a\x6b\x00\x12\x4c\xa4\x80\x5e\xa6\x2c\x18\x43\x8c\x69\x72\xcf\x47\x68\x13\xa2\x33\x98\x9c\xe9\x4f\x0a\xe5\x08\x42\xd4\x0a\xd5\xea\x0a\x84\x62\xc1\x57\x7b\x62\x8c\xe0\x48\xde\x4f\x5e\xef\x25\xa2\x03\x37\xab\x7a\xc0\x6b\xdf\xaf\xfb\x6b\xde\xf5\xf6\x1d\xd6\xe3\x63\x97\x43\xbd\x58\x73\xd4\xfb\x90\x48\x05\x24\x16\x06\x49\xab\x18\x51\x04\xb3\x89\x54\xe6\xf7\x63\xdf\x13\x21\x52\x5e\xc6\xa1\xe7\xea\xf5\x09\xb7\xe6\xf5\xc1\xc3\xb7\xb3\x9e\xf7\xb3\x40\xd4\x42\x50\xd8\x19\x78\xaf\x7d\x79\x56\xea\x19\x7f\x4c\x95\xe7\x91\xda\x50\x9d\x7f\xe3\xdc\x87\x0e\x64\x8d\x34\x93\x26\x2c\x66\x01\x4b\x58\x21\x08\x2d\x12\x81\xf0\x9c\xbd\xe4\x72\xd5\xdf\x13\x67\xab\x8c\xfc\x42\x61\x9c\x60\x24\xd0\x26\x1f\xc0\xa5\xa0\xe2\xfb\x64\x5e\x41\xe6\xb6\x78\x9c\x2b\x14\x2b\x45\x30\x29\x93\xa9\xb1\x12\x0e\x14\xf8\x50\x3e\xcb\x93\xac\xbc\x39\x69\x8a\x3d\xb7\x4b\x86\xc2\x9f\xf1\xf4\xe8\x52\x70\x61\x04\x61\x30\x5c\x50\x73\x26\x1d\x54\x0a\x91\x71\x48\xa0\x50\x7c\xc0\xc4\xe4\xb5\x5e\x7e\x39\xc2\x8e\x65\x7e\x56\x11\x18\x91\xd3\x82\x8c\xb8\x0f\x00\x40\xed\x92\x73\x8e\xa5\x6a\xb4\xa6\x0f\xdf\x5f\x6e\xf6\x50\xa4\x08\x5d\x32\x1f\x06\x1c\x5c\x18\x4d\xcf\x81\x07\xd7\xc3\xab\xa7\x2c\xf0\x4d\x19\xea\x13\xa1\xce\x2e\xae\xae\x52\x87\x62\x84\x9d\xb7\x11\xb2\x0c\xd0\x05\x8a\xde\x05\x1e\x7d\x23\x48\xe0\xc5\x32\x3a\x30\xa9\x6a\x43\x7d\x73\xaf\xac\xce\x11\x6c\xf7\xf8\xad\x6a\xfa\x8f\xb9\x7f\x75\x91\x9d\x6c\x13\x7d\xa7\xac\xa7\xfc\x7d\xe7\xb5\x6c\xd3\xff\xc9\x0c\xb4\x8f\x1c\x10\x55\xb8\x88\x80\x99\x85\xec\x76\xb0\xba\x08\xf3\x62\xb1\x60\x21\x64\xc5\x59\xdd\x72\x7e\x3d\xf5\xf8\x35\xf4\x0b\x6c\xbf\xe4\xae\x5e\xbb\x04\x4e\xa1\x51\x84\x39\x53\x31\xd6\xfb\x5c\x90\xab\x00\x0b\x1d\xd6\xb1\x91\x70\x10\x39\xb9\x7a\x9a\x67\x19\xb7\x04\x86\x6a\x90\x31\xd9\x68\x79\x36\x75\xc1\xad\xe0\x10\x43\x87\x43\x30\x4c\xc8\x21\x82\xff\xa5\xbe\x3c\x05\x2f\x49\x0a\x08\x17\x61\x66\x4c\x6f\x0f\x17\xf7\x44\xdf\x6f\xd0\x1d\x12\x68\x32\x65\xf3\x8f\x9f\x25\x7c\xe2\x40\x0d\x59\xdd\x7b\x49\x58\xce\x31\xe3\xb2\xcb\x8d\xc6\x8e\xc2\xce\x72\xfb\xe0\xf5\x53\x0d\xe0\x96\x50\xcb\x4b\xb3\xbe\x00\xd4\xe5\x20\xe9\x70\xd6\x75\x21\x75\x12\x71\x90\x99\x47\x4e\x99\x29\xeb\xd7\xde\x5c\x26\x29\x82\x92\xd2\xb1\xe5\xd1\x16\xb0\x50\xab\x97\xab\x11\x36\xc5\x82\x6d\xc5\x51\x84\xa5\x79\x62\x0e\x39\xe6\x3f\x42\x34\x4d\xe4\x8c\x1d\xe2\xa5\xa2\xcf\xd5\xe0\xe0\x6e\x79\x95\x32\xf5\x02\xff\x25\xf8\x69\x17\x57\x96\x67\x6b\x46\xae\x37\x8a\x49\xa7\x4b\x93\xb2\xd5\x28\x25\x43\x9d\xa2\x97\xbb\x18\xb3\x46\xae\xf8\x4a\x9c\x45\xb4\xf5\x7f\xf5\x9b\xb4\x94\x36\xa3\x36\x6c\xb6\xd6\xb5\xdd\x47\xdd\x7e\x7d\x9c\xfa\xaf\xb5\x1e\xfa\xd6\x25\xfe\xec\xf8\xb3\xd4\x33\xbc\xc7\xd3\xff\x90\x8c\x46\x20\xc7\x96\xd6\xde\x3f\x47\xfc\x67\x25\xa7\xaf\x08\xb3\x73\xbf\x76\x4a\x14\xe0\x86\xb3\xe1\x1c\x7d\x9d\x52\x7d\xae\x7d\xb1\xb0\xe7\x3a\xb0\xd1\x79\xb8\x45\xa0\xa8\x64\xf8\x06\xa6\xd9\x73\x24\x71\xc3\xe7\x1e\xaf\x4b\xb4\x23\xbc\xa6\x15\xe8\x98\xa3\x26\x88\x09\x34\xad\x47\x9b\x8f\xa1\xe2\x51\x64\x82\x6b\xf0\x2d\xc6\x41\x55\xdc\x0e\x86\xf8\x77\xf5\x4e\x89\xed\xed\x71\x26\xea\x73\x9a\xab\xeb\x79\x7d\x1c\xf7\x30\x77\xfb\x25\xec\x71\x73\x29\x64\x2f\xa9\xaf\x25\x99\xb7\xdb\x5f\xbd\x0a\x0b\xf3\x02\x08\x2e\x34\xaf\x45\x86\xf7\x1d\x79\xe1\x9e\x9a\x85\x94\xd5\xa2\x63\x56\x51\xde\x48\xbc\xaf\x0c\xce\xc0\x64\xad\xf7\x00\x8d\x74\xf4\xda\xc5\xcd\x81\xc8\xe0\x5c\xb4\xe1\xaf\xe1\x53\x59\x2f\x74\x67\x73\x23\x0e\x9c\xea\x95\x67\xc2\x2a\x41\x89\xf4\x90\x36\x53\x33\x67\x67\x0d\x55\xa8\x2e\x7b\x4d\x5e\x29\xc5\x85\x12\xaf\x4c\x58\x9b\xcc\x2d\xb0\x32\x9d\xc6\x2c\xde\xfc\xfd\xb2\x6c\x39\xaf\x83\xaf\x12\x06\x08\xe3\xa3\x89\x90\x26\x9a\x80\x75\xdb\xbd\x47\x4f\x0c\x92\xe7\x92\x52\x1b\x32\x4c\x26\x8c\xeb\x9d\x97\x5b\x32\x74\x99\x7b\xfa\x7e\xbb\xba\xf1\x4f\xef\xb0\x59\x56\x41\x96\x98\x65\x3a\x73\x52\x86\x9a\x33\x43\xe8\xac\xb2\xe0\xc4\x61\xd7\xd6\xb1\xe8\xe9\x1a\xf3\xe1\x1e\x64\x85\x85\x13\xb5\x13\x8d\x22\x8d\x96\x19\xb9\x44\xa9\xd2\x86\xaf\x09\x92\x01\xc4\xfb\x57\x02\x99\x5e\xec\xde\xc0\x7d\xee\xfe\x9b\xe2\x66\xde\x90\xaf\x23\x60\x4e\x44\x83\x0b\xe4\x58\x8e\xb5\xbe\x45\xa1\x78\xdb\x8b\x94\x21\x51\x63\xea\x05\xa9\x8a\x1b\x38\x60\xdf\xab\x2b\xe2\x79\x17\xd8\xf1\xfc\x76\x78\x58\x21\xda\x0a\xe0\xec\x46\x69\xed\x0b\xdc\x6d\x7f\x41\xdf\x72\xfd\xb1\x35\x62\x0a\x66\x8b\x45\x21\x61\x43\xc2\x62\x7b\x26\x67\x59\xca\x6c\x21\xd4\x03\x02\xb0\x56\x10\xaa\x6c\x33\x63\x94\xe3\x5a\xdd\x50\xbc\xd3\x47\x08\x2d\xb4\x94\x42\xc2\x4c\x6a\xb3\x65\x48\x2c\x33\xd1\x0f\xd3\x38\x57\x2c\x26\x17\x47\xb8\x6a\x14\x64\xe6\x54\xcf\xf4\xee\xcd\x9a\x4b\xd9\x22\x3b\x5f\x44\xdc\x28\xce\xcf\x0d\x54\xdc\x16\xa6\x2c\x9a\xe7\x51\xd5\x75\xa3\x72\x94\xe0\x44\xe4\xc8\x93\x77\x07\x83\x68\x20\x29\x68\xa8\xd3\xfb\x47\x8b\x4b\x0c\x75\x3e\x42\x49\x48\x55\x39\x97\xe9\xab\x96\xd9\x40\xcb\xe4\x3f\xb6\xa8\x01\x18\x74\x70\xa7\xa1\x95\x4f\x6a\xa1\x15\x1a\x07\x2f\x97\xd5\x11\xec\xa4\xb0\x5a\x56\x39\x3b\x6f\x5f\x17\x21\x1d\x06\x19\xd3\xc6\x9b\x07\x97\x61\xff\x7d\xe0\xd2\x4e\x4d\x21\xac\x6a\x66\x44\x4b\xca\x8d\x92\xdd\xd1\x23\xe4\x85\x1c\x13\xd0\xd5\x50\xe7\x3a\xfd\xf3\x68\x92\x53\x64\xff\xbb\xc7\xcd\xa6\x69\x20\xdb\xa5\x52\x94\xaa\xa0\x1a\x92\x09\x16\xfc\xa8\x2d\x5e\x42\xf9\x91\x2e\x95\x99\x2b\xad\xa6\x87\x1d\x8c\xb1\xe6\x0e\x8f\xef\x9f\xed\xce\xff\xab\xfb\x96\x7b\x8b\x37\xa0\xd9\x8d\x70\x25\xbf\xf7\x0a\xd6\xb4\x75\x17\xf4\xfa\x77\xb5\x8c\x2f\x1d\xce\x05\xf6\x19\x22\x42\xea\x12\x66\x00\x9d\xbd\xbd\x81\x49\x23\x4a\x9d\x3e\xbb\x25\xea\x10\x7c\xa4\x41\x7c\xa8\x00\xbc\x49\xaf\xd5\x21\x75\x9b\xd7\x02\xfb\x09\xde\x62\x28\x64\x40\x04\xb9\x06\x20\x22\x26\x2d\x71\x05\xe3\x4a\xfb\x54\x75\x53\x5c\x49\xfd\x48\x8f\x63\x57\x92\xec\x61\x22\xa1\xaf\xb6\x78\x41\x6e\x4a\x37\xfb\x49\x9d\xf9\x9a\x8b\x7f\xc1\xac\xde\x18\x0b\x12\xa5\x5b\x5b\x13\xc0\x23\x16\x3d\xa9\xae\x9c\x78\x41\xeb\xab\x31\xab\x70\x9f\x2c\x83\x55\xa1\x5f\xd8\x47\x46\x9d\x2c\x7f\xfe\x88\x2f\xac\x25\x8e\x40\xa0\xdb\x91\x11\x14\x0e\x81\x2d\x0e\x61\x9a\xd2\xc0\x7b\xf5\x70\xae\x83\x24\x85\xa0\x5c\xa3\x80\xcf\x9a\x62\xfe\x67\x5e\x14\x3d\x92\xc4\xc9\xcf\x5e\x2a\x3b\x1a\x3b\x2e\xcc\xae\xba\xa6\xbe\xb5\x71\xda\x8c\xcd\xcc\xa2\x75\x6c\xe1\x02\x7b\x45\x11\x0c\x5e\xc9\x2a\x36\xb7\xce\xca\xcd\xfa\x83\xb3\x37\x4a\xc4\x30\x12\x5f\x50\xb6\x87\xb9\xb8\x02\x1c\x0d\x42\x7f\x35\x72\x32\x0a\x14\x46\x6a\x7a\x76\xb5\xe5\xfc\xa6\x25\xd4\x04\xa9\x30\xd0\x2d\x18\xab\xdb\x8e\xa8\xfb\x68\x1d\xa5\x2e\xb4\x20\x34\x03\x8e\x89\x2e\x54\x1c\xc8\x16\x0d\x66\xd7\x45\xc7\x7d\xdb\x0a\x65\xaf\xdc\x09\x02\x3b\xbc\x17\xd9\x30\xaa\xb8\x52\x7e\x5f\x0d\x35\xc6\xcc\x86\x4c\xa0\x7d\x14\x05\xce\xaa\x77\x5f\x9d\xa3\xd7\xe8\x90\x30\x62\x3e\xf3\x98\x9b\x57\x71\xe5\xa3\xd2\xd9\xb8\xbc\x2e\x1c\x85\xbb\x94\x9a\x94\xf7\xc2\x29\xcd\x42\x63\x5c\xf3\x27\xa3\x6a\x52\x0c\xe9\x38\x78\x93\x27\xef\xa6\xad\x4a\x5d\x75\x5b\x96\x9c\x56\xc7\xed\x46\xdb\x66\xbb\x02\xcb\x8c\x35\x2e\x51\x33\x59\xed\xe3\xa4\xc7\x28\x7a\xd0\x26\x54\x62\xb1\x73\x5a\xcb\x02\xeb\xbf\x65\x15\x37\x4c\xca\x7b\xd0\x2c\x06\x59\x7b\x65\x94\x63\x55\x29\x1e\xa5\x2a\x59\x0e\x89\xb6\xa3\x60\x3c\x5a\xdd\x19\x84\x4a\x00\x72\x1a\x8f\xe3\xd7\x8e\x78\xe5\x20\xd3\xd6\xdf\x66\x79\xf9\x45\xb9\xf4\x68\x09\x04\xce\x15\x6a\x39\x8d\x6b\x2f\xf0\x2b\x9a\xf2\x22\x36\xf9\x91\xf3\xa9\x1f\x6a\x73\xbe\x00\xcf\x2b\xec\xd1\x36\x08\x73\x2b\xdd\x01\x6e\xc5\xf2\x2c\x80\xe0\x85\x9c\xe0\x5b\x2a\x00\xc5\x5b\x49\x80\xa4\xa6\x04\xea\x64\x6e\x28\x65\xee\xd0\xf3\x77\xdf\xc9\xcf\xe6\xdd\xcd\xef\x60\x17\x3a\x7d\x3b\x2f\x77\x0a\xaa\xec\x52\x1b\xfb\x69\xff\x0a\xaa\x9a\x1f\x66\xb3\x56\xcc\x15\x9b\x17\x26\x22\xd8\x0b\xc2\x11\xe1\x19\x2e\xc4\xf5\xce\x34\x1e\xf6\xb9\xf1\xf4\xae\x22\x68\x28\xfc\xb4\xe3\xab\x31\x07\x9f\xa7\x9d\xa8\xaf\x71\x60\xa6\xce\xd7\x95\x87\x5b\x37\x84\xa0\xd1\x33\x26\x83\xf8\x22\x59\x4c\x09\x48\x81\x0d\xe8\x82\xe2\xd7\x83\xb2\x98\x18\xa6\xb2\xcb\x2f\x63\x63\xcc\xf2\xcb\x23\x27\xc6\x5a\xa4\xd6\x4c\xac\x15\x74\xa4\xbe\xa8\x84\xe3\x6d\x61\x61\x16\xc8\x29\x6b\xa3\xd4\x17\xa3\x16\xc7\x1b\x7c\xf2\x62\x18\x3d\x8a\x65\x29\x95\xea\x40\x92\x8f\xce\x0a\x59\x6e\x8e\x56\x58\xf8\x56\x2f\x31\x2f\xf9\x33\x98\xaf\x6f\xd8\x2a\x91\x05\xaf\x33\xa8\x24\x36\x03\x58\xd7\x7b\x63\x70\xaf\x53\x59\x23\x61\x3e\xe8\x19\x63\xa7\xb7\xd7\x5d\xe4\x60\xd7\x91\x9e\x7d\x6b\xae\x77\xc4\x85\x6a\xfa\xbb\x6d\x8b\x7d\xdb\x1e\xfb\xb1\x05\xd9\x6c\x5b\x2a\x82\x49\x14\x69\x9f\xe5\xae\x66\xd4\x8d\xc4\x11\x54\x6b\xe4\x0a\x46\x95\x46\xc2\x95\x9e\x15\xbe\xcd\xaf\x65\x08\xea\xcf\x88\xdf\x99\x9b\xe7\x73\xea\xcc\xc5\xc3\x33\xd0\xe5\xe7\x24\x65\xfc\x0a\x76\xfa\x3e\x7c\x08\x6d\x34\x2b\xea\x0b\xa7\xb1\x1b\xb3\xf2\x39\x0f\x9d\x1b\x6e\xc3\x98\x3f\x88\x62\xa8\x18\xef\x46\x70\xe1\x9a\x9e\x0d\xe2\x8e\x54\x32\x46\xc7\x00\x74\x23\xc8\x36\x7e\xdd\xe1\xc7\xef\x74\xa2\xf2\x46\xba\x12\x05\x7f\xdc\x64\xbc\x8c\x26\x9b\xd8\x9d\x6a\xee\xae\xde\x5c\x4a\x27\x23\x33\x9d\xd0\x03\x83\x80\x14\x90\x60\xa3\x29\x7a\x92\x97\x98\xd7\x09\x7d\xcd\x3a\x11\x38\x39\x03\x49\x99\xf6\x72\xef\x24\x31\x0e\x89\x4d\xba\xcc\xb5\x65\x41\xb9\x56\xa8\x9d\x5d\x1b\xfe\x57\x7c\x84\x3e\x7f\x32\x97\x70\x27\x3a\xfa\xd7\xc6\x0b\x50\xda\xac\x6e\x49\x40\x8c\xb5\x5f\x0c\x31\x21\x85\x75\x12\xe4\x3a\x5b\xe3\x71\x19\x4e\xda\x38\x66\xe9\x9c\x74\xd7\xbf\xb4\x60\x2e\x54\x94\xa7\x51\xb8\x4d\xcb\xfa\xa9\xe4\x00\x82\x8a\x99\x07\x9e\xa6\x87\xdc\x40\xd9\x8a\x5e\x58\x6e\x46\x62\xc2\x71\x4f\x2d\x15\x66\x41\x76\x7d\x56\x1e\x10\x7e\x13\xfe\x06\x03\x1f\x4e\x39\x7d\x5b\xce\xf5\xa3\x05\x6f\xbb\x3a\x28\xcf\x18\xb6\x4f\xad\x94\x17\x42\x6b\xd8\x7f\xfb\x7d\x41\xd7\xf7\x2a\x89\x4c\x8c\x9c\xd6\x0f\x32\x7b\x15\x7e\x41\x5d\xae\x98\xaf\x16\x67\xd2\x28\xa3\x6c\x2d\x23\x38\x38\xac\x9e\x6d\x68\x6c\xef\x8a\x88\x93\x27\x92\x72\xca\xed\x11\x7a\xdc\x69\x8b\xdc\x70\xdb\x4f\x71\xb5\xd6\x13\x6e\xcf\xeb\xce\xda\x56\x8f\x48\xfd\x61\x97\x95\x37\xa2\xa0\x68\xc0\x73\xad\xdf\xa8\x54\x45\x7f\x95\x77\x84\x7e\xba\x20\xa1\x28\xda\x46\x4f\xa9\x4b\xcb\xc4\x01\x4a\x7f\x9b\x54\xff\x84\xf9\x50\x0f\x4f\xc5\x5b\xa2\xa4\x4b\xb8\xea\x82\x49\x0e\x2e\x97\x68\xad\x28\x93\x9e\xa1\x95\x92\x6c\xea\xd9\x62\x61\xac\x74\x72\xa0\x5d\xaa\x45\x46\x9d\xb1\xa7\x36\xdb\xce\x93\x71\x10\x7a\xd3\x04\x85\x9a\x0d\x8c\x30\xc6\x2e\xa4\xdb\xf4\x2b\x58\xd1\xdd\x58\x4e\xcf\xe0\x3b\x97\x69\x22\xf3\x48\xb4\xfe\x6f\xa3\xb0\x79\x00\x3b\x78\x05\xaa\x44\x75\xa0\x2c\x85\x92\x64\x65\x44\x96\xd8\xcb\xe3\xea\x4f\xfb\x01\x06\xb7\x95\xdb\xf9\x4a\xb7\x90\x15\xab\x03\x4f\x50\x23\x32\x72\x6e\xc0\xf2\x66\xfb\x87\x78\xa0\xff\xcf\xf5\xea\x43\x65\x9d\x0f\xa6\xce\xd0\xb9\x6a\xdc\x23\x7c\x9f\x0f\x91\xfb\xae\xf9\xfe\x89\xe6\xe0\x05\x56\x7f\x5e\x00\x96\x3e\xd8\xab\xfe\xc8\xf2\xae\x4b\xb4\x2d\x23\xeb\x5d\xf5\x94\xf1\x1b\xb7\x11\xa4\x29\x92\xb2\xc0\xe5\xc8\x74\x3a\x49\xd3\x48\xc8\x8e\x1d\x71\x2a\x7d\x53\xf5\xe2\xd5\x7f\x5b\x1e\xd7\xa0\x6e\xd7\xd9\x25\x9c\xac\x92\x7d\xbb\x65\xcd\x6e\x51\xf3\x9f\x29\xe7\xf5\x82\x22\x95\x16\x0b\xef\xee\x49\x9c\xf2\x21\x08\x8d\x0b\xb7\xe5\x33\x49\x55\x9d\xf2\x79\xa9\xc5\xb0\x19\x45\x13\x8e\xbe\xa7\xe1\xe6\x17\x32\x3c\xbe\xd4\x31\x83\xa1\x7d\x2b\xe0\xff\x69\x67\x01\xb9\x55\x73\xf4\x14\x92\xd5\xbc\xb9\x55\x01\x62\x46\xa5\x86\x0a\x18\x63\xa7\x5e\xff\x0c\x0a\x1c\x85\x1b\x31\x64\xe0\x60\xce\x9b\x75\x73\x1c\x25\xfd\x12\x24\x48\x1a\x08\x52\x6d\x1a\x4d\xa6\xb8\xf0\xa5\xed\x34\x75\xf2\xe4\xe4\xa0\x3d\x92\x57\x92\x47\xe7\xad\x82\x1b\xdc\x3e\x14\xa9\x83\x55\x2a\x56\xfc\xe7\x76\xec\xb3\xf9\x1c\xf2\xdd\x41\xa6\x15\xb0\x6b\xfa\x3f\xd0\xd2\xb9\x95\xd0\x9d\xf2\xb3\x85\x73\x85\x1c\x78\x7d\xbe\x8c\x81\x3f\x78\xb7\x5f\xd7\x69\x8f\xa9\x0e\x3e\xf4\x5d\x27\x79\x90\xd3\x07\x38\xa6\x79\x97\x6b\x7d\xbf\xe0\xdd\x4f\xa9\x71\x28\xb1\x7a\x88\x9a\x37\xc1\x25\xac\x4a\x33\x83\x31\x14\x71\xfc\x4e\xd4\x71\xfc\x34\xf5\x7d\x81\x63\x44\x50\xb9\x42\xf6\xde\xe0\xc7\xb3\xc1\xd3\xcd\xaf\x4d\x35\x76\x5a\xe5\x3a\x70\xdc\x92\x8e\xc0\xfa\xfa\x5b\xb4\xc9\xca\xc2\x6a\x2f\xb5\x1e\x50\x75\x7f\x21\x07\xb7\xd8\xc2\xab\xa2\xe8\x12\x26\xcc\xa2\xe7\xa5\x69\xdd\x11\x97\xa2\x52\x2a\xc3\x92\xb8\x5e\x1c\x58\xf2\x2c\x5e\x6b\x82\x14\xf6\x72\x06\x9e\x78\xbd\xaf\x05\xf6\x01\x60\xe9\x9f\x62\x32\x4b\x1e\x0a\x9a\x68\x0b\xfb\x83\xa4\x4f\x9c\x74\xbb\x49\xd6\x7a\xf2\x04\xb1\x50\x79\x91\x4b\x39\x6f\xf3\x7f\xd4\x83\xb7\x38\xfc\xf2\x90\x58\xe2\x4e\x40\xe6\xa3\xb9\xf4\x6d\x77\x68\x14\xf8\x92\x52\xd7\x34\xe5\x4e\x30\x40\x28\x46\x1f\x0f\x2d\xc2\xb5\x19\x0b\x6e\x85\x76\x9b\x4d\xf3\x8e\xca\x79\x22\x63\xa9\xca\xe4\x22\xb6\xa8\x36\x00\x5b\xd7\x08\x3e\x78\x05\x37\xb4\x30\x81\xf0\x09\xce\x2a\x37\x44\x4b\xc4\x93\x5d\xee\x22\x19\xdd\x55\xa0\x03\xc6\x3c\xd1\x02\xb1\x75\x40\xf2\x26\xdc\xbe\xed\xfe\x08\xdc\xe1\x78\xef\x67\x15\xb4\xe9\x1d\x1f\xb2\x1a\x86\x64\xf6\xae\xf0\xb4\x3b\xd7\xfd\x6b\xc9\xda\xef\xc8\x12\x69\xe8\x0c\x43\x09\xa0\x04\x0d\x04\xbb\xd2\x10\x42\xe6\xa0\x64\xe2\x01\x1b\xb1\x9d\x5f\xf5\x45\x0f\xd5\x31\xb6\x70\xe3\xdd\x9c\x0e\xf1\xfd\x96\xa4\x7e\x25\x34\x62\x3a\x33\xae\x12\x30\x57\x37\xfe\x97\xd8\x58\x09\x9c\x8c\xca\x10\xce\x6b\xe0\x2c\x2f\x24\x2d\x8c\x00\xba\xbc\xc4\x88\xb0\xb4\x84\x6a\xa8\xee\xec\x82\xe0\xd5\x94\x69\x6a\x89\x2d\x4c\x88\xab\xe8\x07\xce\x64\xd1\x6a\x1b\x47\xe9\xb5\x32\xe4\x7c\xae\xd9\xf0\x37\x8a\x88\x33\xb2\x25\x26\x1a\x4d\xa6\x57\x10\xe5\x75\xa2\x7e\xaf\xbe\x05\x7e\x69\x95\x05\x7c\x2f\x30\x59\x8b\x20\xa1\x9b\xc3\xb1\xf8\xb6\xf2\x98\xbd\x3a\x1a\x6f\x2d\xb4\xa5\x12\x27\xac\x67\x04\xed\x25\x89\xb3\xac\xa0\x14\x41\x6d\x2c\x27\x58\x4b\xbc\xd0\x3b\xbc\xbb\x27\xed\x7e\xec\x19\xd6\xbe\x01\x93\x75\x0f\xa4\x4a\x84\x92\x25\xf0\x62\x16\x70\xed\xe9\x75\x6c\x9b\x67\xe1\xf2\xd9\x65\x87\xc0\xba\x0f\x0d\xfb\xcc\x39\xbb\x6a\x7b\x63\xf3\x3e\x1a\x1e\x99\xdc\x41\xa6\x21\x70\x15\x49\x9f\xc0\xa7\xaf\x03\x74\x7f\xa3\xec\x57\xfd\x13\x8b\xf2\x7c\xf0\x36\x2d\x0c\xff\x3b\x27\x5f\x6c\xc4\x57\xcf\x4f\x81\x6e\xbd\x17\x75\xf0\x45\xf4\xf7\xf8\xa4\x6a\xbf\x1d\x20\x41\xb0\xc8\xaf\x75\x76\x95\x02\x11\x94\x36\x7a\xa8\x74\x08\x19\x50\xb8\x43\x1c\x8d\xa1\xc6\x3a\x4d\xdf\xf6\xe6\xdf\x74\x33\x10\xad\x7b\x05\x38\xf7\x44\x30\x78\x9d\x15\xe6\xd3\xc8\x44\xd3\x97\x07\xcf\xe3\x3d\xa9\x16\x8e\x60\x9e\x35\x0a\xd9\x97\xac\x8b\x19\x3b\x7d\xc5\xc0\x08\x40\x0c\x63\x7b\xad\x28\x7c\xdb\x11\xa8\x34\x8a\x57\x85\xf4\x53\x29\x9c\x29\xe2\xe9\x25\x4a\xb7\x49\x98\xe7\x03\x17\x21\xe4\x9d\x78\xa9\xd9\x10\x94\x81\x57\x16\xd6\xbe\x6d\xbb\xfa\x86\x10\xf5\xc7\xf7\x48\x03\xb2\x41\x7a\x21\xf2\x97\xc9\xbb\x8b\x34\xf2\xe8\xaa\x5c\x38\xa6\x93\x6b\x0c\x2f\xf0\xd5\xca\x6c\x06\x05\xce\x7e\x3c\x11\x28\x22\xfd\x55\x30\x24\xd5\xb1\x96\xd7\x40\x61\x45\x43\xc6\x31\x6c\x65\x8d\x22\xa7\x4c\x17\x5b\xbe\x8c\x05\x4d\x9a\xac\x1f\x08\x28\xaa\x18\x45\x26\xae\x95\xab\x35\x6f\x7e\x4e\x43\x6f\x7e\x7c\x1f\xc0\xec\x72\x7d\x0d\x44\x52\xc1\xcc\x97\x5b\xfb\x27\x33\x13\xd1\xc9\x9d\xbd\x60\xbb\xf8\x1b\xc1\xd3\xfd\x22\x6b\xb1\x8b\x19\x9f\x35\xce\x93\xfb\xe3\xc3\xa9\xfd\x61\x2f\x23\x5b\xbe\x0c\x13\x6a\x74\xd8\x86\x83\xc8\xe3\x00\x58\x3b\xac\x95\x62\x04\x78\xf6\x6b\xbb\x16\x2a\xd4\x67\xfa\xa6\x63\xc0\x7a\x65\x89\x1a\x7b\xa9\xd6\x10\x00\xb3\x84\x8d\x95\x07\x96\xc1\x26\x66\x05\x93\x1e\xee\xe1\x3c\x73\xe5\xf9\x3f\x63\xfd\xd0\x16\x0e\x78\x61\x14\x69\xbf\xde\x49\xb0\x7b\x54\x5c\x46\x29\x39\x1c\x81\x22\x15\x49\x17\xaa\x37\x85\xaa\x49\xb0\xa0\x5d\xb1\x47\xc7\x29\xd0\xcb\xbb\x90\xc4\x25\xf1\x6d\x6d\x64\xa9\x79\xd8\xfc\xf1\x0f\xeb\x9a\xc2\xd4\x08\xf7\x20\x76\x82\x08\xb5\x45\xb5\x36\x12\x71\x74\xbc\x24\xa1\x4b\xb0\x2e\x4c\xa8\xea\xd5\x99\x64\xa4\x9b\x80\x12\x28\x04\xb8\x98\xbe\xbb\xd6\x3a\x4f\x5d\x53\xd0\xe8\x54\x73\x92\x13\x1f\x0e\xc5\x55\x46\x69\x5c\xf6\x11\x94\xc4\x3f\xf5\x13\x91\xa9\xd7\x8f\x09\x8d\x7a\xf9\xab\x97\xf7\xd9\x58\xf8\x47\x3f\x9b\x36\x4a\xf0\xe7\xa4\xea\xa0\x55\x70\xb3\x3f\x24\xfe\x3f\x69\x0e\x26\xbb\xb9\xab\xf6\x1d\x4e\x2c\xdf\xba\xec\xdc\x29\x1e\xb6\xf9\x46\xaf\xb5\x34\x03\x36\xdb\xb2\xf8\xc2\x0c\x97\xa4\x1f\x2e\x78\x49\x88\xe5\xb6\x26\xc8\x1b\xae\x63\xe3\x1e\x1a\x96\xf0\x0c\xd5\xf2\xc0\x2a\xed\xee\xa7\xb3\x57\x8b\xfa\x6b\x04\x71\xe7\x40\x7b\xde\xaa\xec\x0a\x0d\x5d\x21\xdd\xc1\xaa\xaa\xa4\x30\xfb\x60\xb8\x35\xb6\x36\xd2\xa0\x24\xb5\xbf\xa0\x59\x41\x59\x47\x02\x30\xc2\x67\x18\xb8\x51\x47\x48\xc1\xc5\x1e\x5b\xe9\x62\x6b\x21\xf1\x51\xf2\x51\xf9\xa5\xae\x51\x15\xfa\x0a\x81\x17\x17\x48\xe8\x86\x75\x18\x15\xbe\xb5\xa2\xbf\xe4\x5f\xf3\x35\xd8\x8b\x13\x37\x58\x99\x6e\xff\x5c\xc8\xc9\x30\xab\xd0\x74\xd7\xbc\x8c\x95\xac\x2a\x45\x0e\xbb\x87\x3a\x84\x96\xa0\x74\x69\xaa\x27\x8e\x1f\x70\xf5\x22\xb4\x30\x52\x0b\x68\xe1\xe3\x1c\x29\xde\x5c\x86\x2a\x9e\x14\x48\x3a\x4e\x04\x42\x02\x86\xf5\x9e\x84\xa5\x9e\xbd\x49\x58\x2a\x21\x87\x1b\xdc\x2d\x89\x4c\x11\x56\x74\x90\xf4\x5b\x58\x1b\x3c\x0c\x12\xdf\x19\x4f\xf7\x7f\x3e\xdf\x6a\x7f\xdf\x51\x1c\x3c\x66\x74\x3d\x2c\xbe\x23\x4b\xff\x35\x26\x85\x32\x43\xa4\x24\x9b\xcc\xd5\x0a\xa8\xd9\x34\x2d\x0c\x02\x81\xf4\x00\x8a\x37\x5c\x1d\xe8\x3f\x72\xd9\xd1\xd6\x94\xa2\x61\x43\x99\xf0\x55\xa6\x0a\x39\x39\x7f\x0f\xbe\x42\x16\x69\x73\x4b\x9e\xfb\x25\x78\x8d\xc8\xd3\xfe\xfe\xa9\x28\x34\x78\x5d\x84\x29\x5d\x3c\x6b\x54\x52\x93\x50\x54\x34\x2f\xbb\xcb\x09\x0a\x59\xc3\xd1\x4b\x51\x44\x6a\x26\x31\xdf\x8b\x56\x91\x5e\x4b\xf4\x8b\xe3\xf6\x3e\xd4\x23\x6f\x57\xdb\xb4\xdc\xb3\xc6\x24\x51\x27\x80\x77\x5d\xa5\x45\x0d\x8d\xd9\x90\x6f\xaa\x6b\x38\x34\xe2\xa1\x23\x25\x10\x85\x39\xdc\x22\xfa\xef\x59\x1f\x93\xbb\xb1\xbc\xeb\x35\x26\x72\x28\x21\x5d\x6f\x4e\xae\x2e\x9f\x26\x51\xa4\x8d\xc1\x17\xea\x34\xe9\x80\xad\x46\x37\x83\x26\x40\xfd\x8f\xbc\xd8\x5f\x81\x1e\xcf\x55\x67\x4b\xf2\x36\x67\xc8\x70\x67\x7a\xda\x4b\x53\x9f\x3b\xa0\xb4\xb5\x0a\x79\xbd\x73\x81\x24\x8c\xb4\x95\x83\xcf\x84\xc0\x19\x35\x76\xcb\xfc\xe5\x40\x24\x46\xfa\x37\x5b\xdf\x77\x9f\x1f\x67\x7d\x96\x65\xed\x1f\x53\xa8\x7e\x90\x82\x99\x41\x58\x23\x99\x88\x0e\x1a\x36\xf0\xa5\x27\x46\x8b\x71\x3c\x62\x1e\x93\xed\xbd\x51\xc4\xa2\x18\x66\x59\x59\x32\x85\xdc\xf5\xbd\x09\x74\xd3\x5c\x9e\xc5\xbf\x4e\x1d\xe6\xd6\x3d\xe6\x6b\x62\xd9\x1b\xa3\x62\x04\x66\xed\xd5\x91\x02\x0a\x38\x6c\xb0\xe4\x0a\xc1\xb3\x62\xa7\x6d\x8e\x09\x36\xb2\x83\x94\xa3\xe3\xe3\xdc\x00\x1a\xaa\x88\x85\x30\x5e\xc6\xb7\xec\x96\x47\x63\x59\x7c\x07\xe7\x3c\x53\x81\x8c\xed\x6d\x0b\x01\x4a\x8a\x6c\x93\x46\xae\x9e\x41\x85\x40\xd3\xc2\xbf\xaa\x6f\xd7\xe6\x8f\xcd\x9c\xcc\x8e\x70\xdb\xbb\x3c\x7b\x65\x8d\x2a\xa3\x6b\xf7\x58\x36\x51\x14\x62\x2a\x2f\x4c\x93\xa9\x43\x39\x65\x36\xc9\xa5\xda\x05\xbc\x89\x24\x4b\x55\x06\x7c\x4d\x2b\x28\x22\x95\x13\xee\x0c\x94\xb4\x28\xa2\x60\x48\xb3\xaa\xb8\x23\x4c\x12\x22\x72\xcd\x14\x32\x4e\x33\x01\x43\xde\x73\x31\x9a\xd3\xfd\xb5\xa4\x0d\xde\xde\x6d\x43\x64\xaf\x77\x0b\x75\xd4\x01\x96\xdf\xf4\x83\x8a\x06\xe7\x09\x73\xfe\x51\xbe\x9f\xaf\xcc\x7d\x7f\xe3\xcf\x14\x41\x13\xf6\xc9\xcd\xee\xd2\xfe\x49\x88\x60\x76\x2e\x44\xf2\xc4\x9a\x46\x40\x45\x22\xd6\xaa\x8b\xa9\x2f\xb2\x98\x05\x99\x8b\xca\x68\xe0\x69\xe1\x68\x56\x33\xc2\x79\x63\x22\x1a\x93\x14\xaa\x0b\x33\xaf\x05\x66\x55\x1b\x57\xc2\x16\x42\xa6\x41\x62\x92\x8f\xec\x68\x50\x80\x8a\x91\x75\xac\xb3\x5f\x87\xaf\x7c\x58\xbf\x4c\x49\xcb\xd4\x17\x47\x1b\x7b\x55\xd7\x47\xa9\x50\x5f\x60\x57\x6c\xf6\xe7\x37\x5a\x05\x1a\x24\x7c\x60\x14\x5e\x72\x05\x93\x6b\xd5\xa0\x8d\x64\x61\x0b\x94\x1e\x21\x16\x6f\x12\xc6\x90\x89\x17\xa8\x1a\x8a\x07\x38\x5e\xa6\x57\xd1\x80\x39\xe2\x72\x97\xc7\x6a\x67\xd3\xd8\x44\x41\x54\x2b\x7c\x67\x0a\x90\xdb\x78\xb1\xd2\x17\xa0\x46\x8a\xdd\x6a\x94\xc1\x19\x35\x6b\x93\x89\x01\x20\x33\x61\x74\x8c\xe9\xd9\x5c\xc5\xfe\xa1\xf8\x10\xbf\x05\x0c\xab\xed\xed\xf9\x16\x6b\x82\xa0\xc1\xee\xb0\x66\xc8\x95\x5f\x66\xe2\xb5\x61\xe9\x7d\x06\x4d\xbd\x0d\xd6\x48\xaa\x14\x96\x7a\xa6\xdf\x7e\xce\x9e\x9e\xa0\xff\x70\x52\x07\x04\xcd\xff\xbd\x7e\x66\x9c\xa0\x52\x4a\xcf\xea\x81\xe0\x9a\x19\x45\xb1\x62\x7f\x22\x2f\x89\x73\x9d\xf6\xbc\x50\xcb\x4d\x89\xb5\x46\x2c\x44\x4d\xaa\x4e\xae\x0b\x6f\x87\xe7\xb7\xb1\x09\x8b\xb1\xf0\x45\xd3\xde\xb4\x5a\xc7\x46\x4e\x5f\xaa\x40\x1b\xb7\x02\x9e\xb4\x29\x43\x89\xaa\x89\x92\xe7\x61\xd6\xb0\xf3\x64\xed\xc4\xe2\x47\x81\x85\xee\xc8\xa1\x6b\x8a\xcb\x20\x91\x64\xcd\x56\x83\xbb\x6f\xdb\x38\x77\xef\x32\xda\xdf\xd8\xc8\x34\xff\x41\xc8\x7a\xdd\xd6\x0e\x04\x0e\xb6\xc7\x2c\xee\x8c\x2a\xbc\x89\x34\xca\x7d\xb3\x2c\xa3\xb5\x57\x73\xa9\x1b\x3b\xa2\x95\xaa\x39\x0d\xca\x6f\x07\x20\xfe\xb5\xde\x19\x75\x81\x94\xa6\x36\xaa\x5a\x98\x8f\x5d\x32\x43\xab\xd0\x55\x54\xac\x62\x7f\x40\x15\x1b\xae\xf9\x37\xbc\xc7\xf3\x98\x37\x13\x0e\x5f\xd1\x4c\x4d\x5c\x81\x4b\x01\xa7\x03\x51\xa3\x9a\x24\x31\x56\x8e\x18\x2e\x2a\x51\x84\x1a\xe7\xff\xa3\xa0\x20\xf0\x66\xd0\xfe\x24\xc3\xe5\x06\x55\xbb\xb6\x87\x67\xf0\xfb\xd5\x27\xfc\xb6\xd8\x76\xb2\xf5\x4e\xc5\xd5\xec\xc0\x0b\xd2\xd6\xf3\xeb\xfe\xb8\x6b\x33\x5b\x2b\xd3\x49\x86\xa7\x7a\xa2\x48\x9e\xe7\x93\x67\x67\xf8\x77\x82\x34\x24\x4c\xed\xed\x84\xd8\xaa\xd0\xe1\xaf\xeb\x94\x55\xd1\xc9\x60\xe5\xe9\x93\xd3\x14\xba\xa5\x4d\x2c\x1f\xfb\xfe\x44\x11\xfb\xe9\x1c\x7b\x34\x55\xae\x12\xbd\xca\xf0\xcf\x52\xdf\xd8\xd5\xa5\x29\x56\xdd\xa9\xef\x99\x17\x81\xa9\x80\xbc\xb2\x8a\xac\xae\xa6\xe3\xe2\xc8\x85\x6b\xc4\x3b\xb7\x63\x26\x32\x4a\x20\x07\xc4\x46\xf6\x79\x52\xf4\xca\x21\xa6\xde\xbe\xaf\x94\xf7\x15\xb9\x3b\x57\x19\x56\x27\xb6\xb5\xa8\x5c\x8a\xa4\xdd\x41\xc4\xec\xad\x7b\x9f\x2c\xc7\x35\x03\xe5\xf4\x3c\x75\x52\x9c\x74\x83\x52\x56\x92\x8e\xc8\xcd\x65\x9b\x4c\x01\x59\x95\x2a\xb5\x96\xe3\x21\xca\x46\x24\xb2\xae\xf5\xb8\x1a\x62\x52\x6e\xf1\x3c\x0d\x78\x89\x1e\x31\xf3\xe2\x62\x3e\x9b\x30\x60\x68\x3f\x7e\xff\xef\xf8\xe7\xef\xdd\xea\xfa\xdb\x74\x97\xd1\x2d\xc5\xdd\x5b\xd2\xd7\x94\x03\xff\x70\xbe\xfb\xe1\x6e\x21\x88\xf5\xf7\xa0\xbf\x17\xb4\x62\x6e\x2f\x48\x7f\xd9\xed\x97\xd9\x82\x9e\x43\xfb\xeb\xdf\x00\x65\xcc\x46\xdb\xa4\xd6\xf7\xf4\xf5\x11\x57\x2b\x1d\xab\xcd\x23\xb8\x0d\x44\x2b\xaa\x48\x9b\xd4\x80\xb7\x54\x6c\xfc\x6b\xb3\xe0\xfd\xd1\x88\x12\x49\x4d\x2e\x3f\x8f\x48\xd6\x21\xc4\x5c\x1d\xc2\x26\x97\x4e\xf3\x2d\xa8\x41\x23\x4e\x56\xf2\xa7\x5d\x73\x2b\x5e\xad\x05\x57\x10\xd7\xf7\xf8\xf4\xd0\xa4\x78\x77\x2f\xa6\xd1\x74\xd3\x6f\x40\x91\x2d\x64\xd2\x4a\xd9\x3a\x74\x87\x8f\x9d\x92\x0e\x5c\x49\x23\x93\x9a\x8d\xfb\x1c\x98\x5c\x3b\xc7\x29\xc9\x74\xb2\x65\x76\x6b\xd9\xf9\x16\x71\xa2\x41\x25\x93\xce\x7f\x62\x19\x84\xdd\xb5\xbe\xb5\x59\xa9\x41\xb3\x13\xf3\xe9\x52\x79\xb5\x9a\x2a\x4d\x38\x23\xc5\x51\xc7\xd8\xd5\x44\x7f\xe4\x30\xf2\x63\x2c\xa2\x13\xa5\xcd\x93\xfa\x12\xf4\x90\x52\x80\x20\x29\x6f\xce\x35\x42\xb6\xcd\x32\xbf\xe4\xb4\xc1\xaf\x22\x8b\x6c\xd1\x63\xa6\x37\xdd\xa7\xc0\xbf\x70\x84\x1f\xf0\x30\x71\xf7\xf6\x0e\x9d\x81\x8c\x0b\x5e\xfd\x49\x71\xfc\xec\xdf\xdc\x27\xa5\x09\x8b\xb6\x81\x45\x92\x9b\x2e\x8d\xf7\x15\x6a\x94\x27\xe9\x35\x38\x96\x36\xda\x14\x6d\xf3\xd1\xaa\x6a\x7b\x97\x10\x48\xa2\x49\x7e\x6e\x88\x68\xb8\x5f\xa7\x9a\xe9\x4b\x64\x7e\x7b\x9f\xaf\xce\x9f\x8c\xdb\xe3\x0e\x5f\xf3\xa4\x53\x8e\x39\x29\x8b\x0c\xec\x29\x1b\x9e\xd5\x72\x19\x0f\x4f\xdc\x8d\x3a\xbe\x84\x46\xff\xff\x05\xc8\x87\xef\xe1\x4e\xa8\xe6\xdd\x5f\x21\x17\x64\xcc\x08\x92\xd5\xeb\x35\x89\x97\xed\xb3\xf0\x16\x47\x93\x1b\xc5\x64\xee\x13\x18\x3f\xa1\xe6\xc0\xb9\x72\x65\x45\x6c\xd6\x50\xf3\x62\xa5\x9a\x6f\xc7\xdf\x39\xae\xcd\xb0\x0e\x41\x09\x55\xa9\xaa\xf8\x84\x61\x92\x87\x74\xdf\x5e\x42\x53\x5d\x4d\x99\xd2\x18\x93\xee\x44\x6e\x44\x2b\x20\x81\x10\x74\x2c\xc9\x94\xc3\x30\xf2\x56\x56\x8c\x28\xc0\x94\x63\xd0\x80\x50\xc6\x31\xac\x1c\x96\xee\x82\x51\x76\xa9\x2c\x5d\x86\x05\xce\xcc\xa5\xca\x6d\x30\x21\x62\xcd\x21\x27\xd5\xd7\xd5\xff\x66\xb8\xf7\xdc\xca\x77\x7f\x85\x1a\xfe\xba\xc3\x11\xf5\xab\xe9\x02\xc8\xc7\x3e\x90\x89\xf2\xfe\xfe\x4c\x60\x42\x25\x42\x5c\xe0\xcf\x97\x62\x8f\xae\xa2\x6c\x63\x6a\xcc\x97\xf8\x47\x25\xbb\x3c\x30\x74\xc1\x43\xbb\xe7\x06\xeb\xdf\xcd\xd7\x30\x9e\xec\xfa\x2f\x62\x04\x98\xb7\x6e\xcc\x30\xfa\x48\xce\x74\x3c\xa3\xe2\x4d\xd1\x2b\x20\x88\xab\xa9\x2e\xd2\x32\xd1\xc3\x04\x7e\x89\x9e\x80\x73\x5c\xa4\x3a\xe2\xb1\x52\x4a\x65\xed\x94\xcc\xf8\xbe\x7c\xbb\x66\x65\x72\x94\x25\x29\x76\xb6\x36\x2b\xbc\x95\x76\xb5\x1e\x99\xcc\xd1\x57\xa6\xf9\xb3\x15\xf3\x64\x94\x3e\xbf\xfb\xf2\x54\xd2\xb5\xed\x26\xd1\x25\x2f\x57\xd0\xd5\x4c\x03\x9b\xbb\x0c\x2c\xd9\x89\x03\x37\xca\x55\x07\xa4\x2f\x16\x56\x80\xf7\x49\xeb\x1a\xa3\xaa\x90\x6f\xd3\x96\x0a\xab\xcc\x1f\x09\x71\x15\x26\xae\x93\x16\x15\x80\x2c\x5a\xd3\x74\x22\x84\xb3\x57\xda\x6a\x66\xab\x24\x96\xa0\xcd\x36\x64\xec\x58\xbc\xc9\xc6\x61\xec\x21\x76\xcd\xe2\xee\x8f\xd7\xde\x7b\x90\xdd\x47\x45\xc3\x36\x57\xbc\xdf\x15\x57\xa3\xd5\xd9\xf3\xb9\x70\x86\x6d\xf8\xfe\xcf\x3f\xfc\xf0\x96\x54\xf3\x0e\x03\x9c\x28\x54\xd4\x37\xc8\xae\x51\x50\x41\xc5\x79\x9b\x50\x7f\xb9\xf7\x97\xc8\xd3\x50\x44\x9c\xa0\x5f\x73\x03\x82\xff\xbe\xcd\x19\xce\x60\x3c\xfa\x8e\x1e\x32\x0f\xed\xc7\xa3\xcc\x32\x9f\xd5\xdc\x8f\x5f\xe0\x52\x29\x46\x6b\xad\xc3\xc8\x0d\x22\x1c\x81\x6a\x32\x97\xfe\x8c\xa1\x38\x0f\x67\xf8\xda\x1d\x92\xb2\xaa\x0b\x27\x0c\x35\xc9\x11\xc8\x29\x9e\xac\x6c\xf9\x19\xab\x56\xa4\x4b\x45\x61\x0e\x2e\xa9\x95\x79\x74\x57\x3d\x5d\x83\x14\x48\xa1\xd9\x71\x72\x16\x5c\x68\x8f\xbb\x66\x90\x2c\xb6\xeb\xfa\xfe\xb0\x0d\xb3\x66\xf2\x88\xad\x48\x54\x48\x17\xcb\xbe\x44\xc0\xd4\xc8\xd2\xbf\x81\x0c\x4c\xb0\x6d\xe3\x07\x0d\x42\x1b\xae\x6e\xfe\x93\x87\xbf\x7b\x69\xeb\xe3\x47\xdb\x96\x98\xfe\xa1\xb0\x8e\x17\x01\x24\xa7\x88\xe3\xc0\xaf\x98\xfc\x43\x33\x57\xb1\xc1\x2e\x39\x4f\x1b\x94\x59\xcb\x5d\xa8\x36\x9f\xa0\x6f\xdb\x02\xeb\x6f\x18\xeb\x4b\x5f\x23\x83\x9d\x18\xd0\xec\xfb\xa9\x5a\x18\x05\xdd\xfd\x68\x31\x04\x85\xf0\x3b\x16\x34\x0c\x98\x20\xcd\xd6\x0f\x59\xec\x32\xb2\xc7\x63\x13\xb5\x1a\x53\x8a\x4b\xaa\x73\x37\xcd\x07\x45\x4f\xb4\x85\x01\x8d\x96\xa8\xc0\x7d\xbe\x53\x5d\x3b\x9d\xfb\x7b\xf1\xb8\x3b\x7e\xcc\x2b\x13\x36\x44\x72\x3a\x97\x77\xfa\xd6\x40\xc5\xe5\xf3\xc6\xf8\x76\xef\x9e\xb3\x17\x2b\xb8\xbe\xb7\x43\xa5\xe1\xf1\x14\x49\x52\xa6\x19\x6a\xe1\x9b\x3e\x48\x55\x35\x4f\x29\x0e\x45\xed\x9c\x0d\x4b\xd6\x35\xb1\x28\x19\x18\xa3\xec\xa1\x03\xcf\x5b\x9c\xc0\x99\xc8\xc9\xd3\x11\x9b\xce\x5a\x2d\x37\x81\x97\x01\xc7\xff\xae\xd5\xf4\x0c\x96\x81\x4b\xa8\x80\x8f\x8e\x55\xc6\x1f\xa1\xcb\x1a\x49\x23\xc8\x1c\x33\x15\xb9\xf6\x95\x4d\x63\x09\xd9\xf5\xe2\x68\xbb\x94\xca\x33\xfc\xc4\x5d\xa3\xbc\x26\xa2\x84\x26\x59\x26\xe8\xdc\x19\x46\x8f\xa4\x43\x82\x07\x27\xbf\x44\xdc\x3b\x54\x5e\xe1\xba\xc9\x2d\x9d\x43\x3b\x5a\x58\x53\x82\x07\xc9\xad\xd7\x13\xa0\xab\x73\xb8\xf6\xdf\x38\xbc\xc1\x1d\x9f\xc1\x43\x94\x2e\xd3\x17\xb6\xa5\x22\xc1\x3b\x29\x5c\x7e\x23\x98\x96\x81\x9b\x6c\x61\xb8\x21\xd4\x9a\x6d\x6f\x6e\xde\xcf\x38\x35\xd7\x23\x80\xb1\xeb\xc8\x20\xd7\xf9\x18\x2a\xa9\xe1\xf8\x35\xd6\x17\xd2\xef\x5a\x42\x8b\x0f\xc9\x11\x42\x1e\x9c\x05\x82\x5d\xc1\xad\x01\x98\x88\x66\xa5\xa4\x58\x53\xe4\x3e\xeb\x74\xb4\xe2\x60\xe4\xe0\xdf\xef\xc1\x67\xc1\x86\xdb\xd0\xed\x13\x7b\x7c\x2c\xd9\xfe\x7c\x23\xf9\x3e\x62\x68\x6d\xec\x16\xb9\x79\xdb\x97\x19\xa3\xe1\xf1\x52\x20\x30\x97\xe9\xc0\xc5\x44\x14\xd9\x40\x0b\x8a\xde\xad\x48\xaa\x1a\x65\x7d\xc2\x28\x0b\x61\x9e\x31\xcb\x91\xf9\xa3\x9a\xb5\x2e\x34\x3e\x92\xaf\xeb\xd6\x5e\x45\x73\x71\x01\x86\xb6\xbd\x55\xc2\x69\x26\xa3\x8a\x15\x76\xee\xdd\xca\xac\x90\x52\x4f\x96\x21\xe4\xf4\x1f\x15\xb6\xfd\xba\x40\x96\x0d\xa8\x08\x0a\xe5\x01\x47\xc5\x9f\xce\x37\x41\x6d\x7a\x58\x91\x25\x38\x58\xbf\xc3\x6b\x07\x7f\x6f\x9f\x4c\xb0\x61\x6c\x8e\x0d\x14\xa6\xf5\x32\x69\x80\xc9\x1f\xdf\x0b\xcd\x4c\x54\xeb\xd8\x38\x89\xfd\xed\xf4\x4f\x41\x4f\xe8\x2e\x77\x86\xbb\x83\xac\x3c\x45\xe6\xb3\xfb\xff\xe2\x7c\xe2\x87\x90\xb7\x66\x18\xbf\x97\xfa\x8e\xba\xfa\x7e\x2d\x3d\xbd\xe2\x7e\x0c\x0d\x17\x11\xd8\x83\x47\x64\x3b\x0e\x39\xfd\x53\xa7\x0c\x1e\x74\x08\xa1\x14\x7e\x3f\x7e\x07\xc7\xfc\x8e\x58\xbb\x0f\x78\x6b\x83\x76\x10\xe8\x72\x4e\xda\x6e\xde\x14\xbf\x9a\x7e\x3f\x68\x7e\xf6\x1a\x1c\xc2\xf8\x2f\x89\x5d\x9d\x10\x2b\xaf\x6d\x9a\xf3\x95\xce\x8e\xc3\x0a\xad\xde\xfa\xfc\x7e\xb3\xb8\xdb\xed\x19\x76\x35\xcd\xd8\x74\xf6\x07\x43\xd1\xb7\x1c\xe7\x44\xbf\x0e\xe6\x76\x93\x3c\x20\x7f\x61\xf6\x13\xc6\xda\xbd\xaf\x97\xa3\x2f\x6c\x1d\xbb\x56\xd6\xbc\xad\xb0\x78\xe0\x2b\xee\x07\xf3\x93\xcf\xa7\xc1\x18\xb3\x98\xfb\xc0\xf4\x0e\x1b\x22\x11\x40\xd7\x59\x8e\xe9\x68\x83\x35\xe0\xcc\x41\x55\x56\xff\x5a\x23\x28\x7c\x2d\x5e\x40\x29\x65\x60\x5f\xd0\x49\x0b\xd4\xe8\x50\xcb\xd8\x5b\x51\xd4\x2b\xf3\x62\x7e\x1e\x37\x33\xe4\x37\x47\x8d\x52\xdd\x4b\x04\xee\xa9\x46\xf6\x38\xe7\x36\x2e\x71\xe5\x79\xe1\x77\xc3\xf0\xd7\x2c\xd2\x41\x9f\xbb\xf1\xb5\x64\x88\xdf\x0f\x29\xa0\x65\x7d\x50\x1b\xaf\xa5\x88\x91\x10\x27\xfa\x48\xdd\x45\x7e\x45\x32\xef\xf9\x48\x39\xfe\x5d\x2c\xc4\xa8\xa6\xab\x73\x3c\x59\x72\x7c\x21\x6a\x8f\xc6\xab\xf3\x47\xf2\xbf\x3f\xbd\x3d\xcc\xc9\xf3\xcf\xa6\x75\x68\xcf\xd0\xdd\x79\xd3\x89\x52\x45\x2e\x38\xc9\x83\xde\x44\xb6\x43\xf5\x93\x78\x81\x51\x02\x07\x37\xf8\xe9\xf9\x4d\x58\x7e\xa6\x8a\xec\x7b\xaf\xf9\x3a\xe8\xb3\xc1\xd9\x7a\x20\xeb\x6f\x0a\x84\x6c\x77\x82\x60\xfc\x00\xf9\x9d\x7b\xee\x9b\x9c\xf0\x87\x77\xe7\xbb\xd7\x4b\xd7\x0f\x33\xc8\x82\x21\x61\x0c\x10\xb5\x1e\x98\x44\x63\xfc\x4c\xad\x3b\xe2\x35\xf0\xdf\x7e\xda\x4e\xc6\x75\x72\x7b\x67\x12\xfc\xb5\xbd\x3d\x4d\x72\x27\x30\x37\xde\xa3\x76\xb7\x89\x46\xcc\xa7\xa2\xcb\xf1\x08\x1a\xb2\xac\x2d\xe2\xc6\xf6\x98\xb1\xad\x95\xd3\xe6\x97\x06\x73\x6a\x10\xdd\xdb\x26\xb4\x0f\x6a\xad\xbc\x40\xf1\x1a\x5b\x75\x58\x39\x76\x9d\xe3\xa4\x3c\x5c\x01\x02\xaf\x27\x58\xe9\xcb\xed\x4c\xe0\xed\x4f\x88\x3a\x9d\x0b\x1f\x77\x21\x0b\x52\xc4\x9e\x18\x5e\xa6\xd8\xdc\xb5\x4b\x38\x9c\xf1\x54\x18\xc8\xcc\xa6\xe2\x93\x59\x4a\x22\xba\x68\x88\x6b\x54\x44\x2b\xe7\x52\xe9\x64\x3a\xdd\xac\xe9\x6b\xac\xa2\xa3\xe6\xd4\x40\xe8\x67\x3e\x96\x56\xe3\xd1\x08\x79\x8a\x3e\xb1\xc1\x6b\x9c\x3d\x85\x54\x8d\x98\xba\xfa\x03\xab\xf2\x66\x32\xca\x08\x4f\xf7\x25\x63\xef\xee\xc8\x24\xdf\xd6\x24\x1d\xca\xc6\xa5\x23\x39\x7d\x0e\x5f\x9d\xc6\x1c\x7c\xe8\x19\xdf\x6f\x38\x4d\x08\xbf\x70\x96\x3a\x56\xfb\x4b\x1c\x82\x32\x40\x33\xb2\x48\xc1\xe8\x99\x70\x78\x07\x2d\xfd\x42\x43\x97\x91\x9b\xde\x10\x0d\x61\x79\x0d\xa1\xed\xa8\x62\x6f\x44\x89\x3a\xd0\x19\xfc\x39\xa6\x7b\x70\x0c\x5a\x26\xc7\x7a\xdf\xdb\x94\x05\x39\xd3\x6c\x10\x52\x67\xc1\x11\xeb\xa1\x5f\xae\x44\x72\x76\xf3\xb7\x7a\x63\x76\xfd\x4f\x57\xe5\x43\x0d\xc3\x29\x08\x79\x7f\xb4\x77\xee\xa7\xfc\x47\xcd\xb8\x4e\x4e\xea\xb8\xc6\x8d\x2c\x62\xdf\x47\xae\xed\x76\x7b\xd2\x72\xbc\x5f\x58\xbb\xec\x79\xc3\x05\x9d\x39\x79\x19\xb8\x93\xe9\xf2\x14\x44\x2b\x74\x88\x54\xde\xb2\x38\x9b\x59\x2f\x13\x23\x89\xbb\x5a\xc6\xc9\xf2\xde\x18\x12\x92\x46\x0f\x83\xa4\xa8\x8c\x84\x23\x4d\x12\xdf\xbd\x26\x42\xe4\x05\x9e\x16\x57\x3b\xb1\xcb\x70\x44\x58\xa4\x10\x32\x18\xeb\x35\x48\x72\xa7\xcf\xa7\x16\xb7\x64\xb3\x2c\x91\xac\x52\x04\xfe\xdb\x68\x17\xb7\x1e\xca\x3f\xbb\xe0\x52\x6d\x98\x1c\xe6\xd5\x63\x03\xf0\xd8\x7d\x21\x62\xf6\xd7\x95\x91\x8a\x0a\x8b\xd7\x03\x6d\x6e\x55\x37\xd5\x22\x22\x5f\x10\x3b\x5c\x74\x43\xae\x79\x5f\x59\x58\x59\xe7\x16\x3a\x92\xb2\x79\x5d\x31\xb1\x74\xb7\x52\x9d\x05\xbc\x6e\xc3\xb1\xfb\xde\x67\x10\xfe\x7c\x09\x4f\x87\x0a\x0a\xf3\x3f\x71\xc4\x43\xa6\xf3\x8b\xe1\x9d\x44\x30\x13\x1b\x07\xaf\xdf\xa3\x46\x4d\x59\xec\x1f\xef\xc2\xeb\xe5\x0a\xa8\x7c\xd1\x7d\xb4\xee\xee\x2f\x79\x0b\x8a\x23\x98\x22\x7d\xa5\xfd\x12\x58\x38\x17\x56\x6c\x05\xa4\x50\x05\x30\xa7\x6d\x7f\xd1\xc4\xfa\xba\x21\x58\x52\x59\xe9\xde\x15\x09\xba\xc3\x7f\x9e\x58\x04\xa2\x52\xcd\xe5\x1c\x34\x21\x6f\x8f\xe9\xe7\xf9\x60\xbb\xc4\x75\x8c\x08\x8c\xea\x7c\x7c\x83\x9a\xf7\xc5\x71\x40\x16\xaa\x30\x1c\x84\xd6\x7d\xd0\x4c\x1a\x10\x51\x0c\xb6\x2b\xa8\xbd\xe5\x03\xad\x4d\x8d\xec\xd5\x9a\xbc\x0b\xe6\xc8\x39\xdd\x4b\x99\xb9\xf2\x1f\x4a\x5b\x19\xd2\xb4\xa5\x87\x69\x72\x1e\xc8\x28\x4f\x39\x79\x87\x91\x16\xb3\x7d\xcb\xc4\x18\x68\x95\xc3\x21\x3b\x4f\x71\x52\x24\x0e\x5d\xcd\xf4\x12\x6a\x1a\x9d\x2b\xd8\xbb\xab\xf8\x56\x3c\x2d\x94\xf1\x48\x0f\x37\x58\x39\x81\x65\x3e\xc2\xea\xe9\x15\x4e\xd2\x37\x56\x0b\x32\xce\x27\x83\xf1\x94\xd1\xeb\xda\x99\x3f\xac\x80\x7e\xcf\x3e\x54\x67\x07\x01\xf2\xd4\x8a\xc5\xe9\x6a\x52\xab\x1a\x55\xed\xf4\x96\x0a\xb5\x69\x1d\x4c\x37\xbd\xc1\x58\xfb\xc7\xe9\x2c\x44\x05\x03\xab\x06\xfb\xa1\x55\x82\x46\x20\xeb\xd1\x57\xdd\x2f\x6e\x57\xef\x6f\x1a\xb6\x9d\x3f\x99\xc5\xaa\x67\x72\x75\xe1\x8a\x26\x70\xac\x0c\x9d\xe7\xdc\x3e\xf8\x5d\x13\x3c\xed\x24\x8e\x05\xcc\x66\xfe\xf1\xae\x65\x77\xec\xcb\x74\x8a\xae\xef\xf5\x8f\xc7\x95\x72\xbb\x0d\x49\x4a\x4e\x0b\x72\x7f\xef\x29\x69\xd9\x0e\x7c\xbf\xaf\xe2\xd6\x14\x3c\xc3\xaf\x71\x32\xcf\x78\x9f\xe7\xa5\xc0\xc5\x0b\xd0\xdd\xf7\x05\x9e\xcf\xf1\x09\x48\x35\x1b\xeb\x79\x21\x37\x55\xdb\x93\x76\x54\x08\x23\xaf\xac\xfe\xf4\x01\x5e\x39\x80\x50\x0a\xfc\x8f\x27\x92\x55\x62\xe0\xf8\x08\x41\x88\x58\x45\x6b\x64\x50\xce\x81\xe7\xa0\xc4\x74\x37\x59\x86\xcc\x2c\xfb\xf6\x85\xa6\x0c\x28\x87\x85\x76\x02\xc2\x55\x24\xda\x12\x6a\x7c\x05\x92\x37\x41\x3c\x27\x6d\x66\xcb\x94\xa0\x73\x11\x2a\xa3\xab\x73\x8f\x60\x3b\x96\x2f\x43\xe6\x11\x23\x95\xc9\x57\x58\xc0\xe6\xc5\x63\x6c\x56\xfa\xba\x2a\xf9\x54\x9a\x01\xc5\xe3\x6f\x25\x7e\x21\xb0\xc3\x2d\x48\x0c\x94\xd5\xe3\xe7\xcc\x41\x65\x52\x0f\x40\xd9\x1a\xb5\x3a\x58\x5b\x4e\x12\x03\xab\x60\xa0\xb7\x46\x31\x6d\x30\x97\xfe\x0f\x4f\xe9\xf9\x83\xed\x1b\xd4\x7e\x13\xcd\x34\x9f\x4a\x04\xe6\x11\xd0\xc4\xae\x69\x4f\x6b\x4c\x10\x5b\x63\xf7\xd7\xd0\x3f\xd7\x63\xd7\x78\x2b\x98\xc8\x68\xb5\xa5\xce\xd8\x62\xd4\xe4\x1a\x74\x7a\x50\x78\xc9\x44\x9c\xc1\xdf\xac\x89\xa9\x78\x76\xb3\x09\x91\x2b\xee\xb3\xc5\x50\x4c\x21\x77\x7f\xaf\xcb\x0f\xe0\x76\x1b\x30\xb7\x3d\x79\x7a\xab\x07\xf5\x27\x13\x8e\xc8\x68\xae\xfb\x70\x41\x53\x6f\x92\xb3\xfb\xe5\x99\xee\xc2\xd6\x0a\xb1\xfb\x45\x5c\x73\xbe\xd7\x28\x38\xef\x7b\xd9\xbd\xc9\x79\x17\x2c\x11\x9d\x5f\x82\x8a\x74\x0a\x93\x42\x7c\xb9\xec\x02\x79\x3e\x54\x5c\x2d\xc8\x8f\xb9\x2c\x42\x9e\x52\x41\x6d\xda\xd7\x8b\xfb\xbb\xea\x5c\xa6\x29\x69\x7d\x5b\x67\xc1\x3c\x26\xf3\x04\xa1\x53\x89\xc9\xdc\x22\x11\x36\x36\xd1\x82\xd6\xc5\x0c\x5d\x28\x49\xf2\xd4\x15\x1a\x29\x5f\x7d\x82\x65\x89\xa4\xe0\xdd\xba\xb0\x6e\x88\x52\x5f\x51\x68\xcd\x05\x4b\x22\x98\xfd\x9c\x55\x1c\x4d\x8b\xb1\x29\xeb\xdc\x8b\x4f\x4e\x0e\x91\x44\x01\x6e\xfe\xe6\x8a\x15\x90\xdf\xec\x56\x74\xb5\x0e\xd0\x3c\x85\x23\x8e\x1b\x34\x05\x56\x12\xf6\xfc\xe0\x37\x39\xd3\xf7\xda\xb3\x32\x44\xc0\x74\x04\x04\xa6\x96\xc4\xe4\x5c\xc1\x94\x51\xab\x9b\x51\x00\x6f\xb8\x0d\x17\xbd\x8c\x0f\x15\x05\xc7\x0b\x38\x6f\x8d\x56\x6b\xed\xa7\xe7\xe6\xe6\x2f\x67\x94\x52\x71\x4d\xe9\x64\xb4\x93\xd1\xf2\xc1\xbc\x84\x88\x52\xff\x50\x7d\xe5\xdd\x0a\xe9\x7b\x82\x3b\xf7\x7d\x6d\x4a\x33\x0b\x3c\x19\xee\xef\x43\x77\xfc\xb4\xc2\xfe\x9e\xf8\x7f\xc6\xa9\xee\xd7\xd5\x0a\x8e\x58\x0a\xb8\x87\x98\x12\x7d\x7e\x9f\x9b\x96\x9c\xdf\x58\xc6\xe6\xf9\x6d\xba\xdf\x7f\x12\x7d\x70\x29\xe6\xe8\x4c\x14\xd9\x70\xfc\xd3\x63\x4e\x6d\xbc\xb0\xfc\x63\xca\x8a\x4b\xc4\x99\x20\x5c\xa1\x95\xc3\x75\xf2\x29\xce\x31\xe5\x89\xa0\x80\x34\xc6\x41\x89\xd8\xd4\x93\x18\x21\x27\xd9\x3d\x9c\xee\x01\x9e\x17\xc9\x66\x0b\x48\x48\xb7\xda\x59\x40\x13\xcd\x2c\x58\x4b\x89\xb0\x5d\x78\xcb\x16\x51\x29\x90\x26\x5e\x7e\xea\x00\x46\x53\x50\x38\x45\x6b\x54\xa8\xf3\xf9\xbf\x84\x60\xa1\xc9\x62\xab\x63\xe6\x4e\xda\x99\x42\x95\x9b\xa7\x71\x63\xdd\x91\xb7\x2d\x7a\x90\xe0\x62\x9f\xa9\x69\xa2\x04\x7b\x53\x42\x81\x84\xe1\xfc\x50\x39\x03\x83\x54\xc2\x2a\x69\x50\xe9\x87\xf7\x54\x09\xb9\x47\x93\xb3\xd3\x59\x54\xd6\xa9\x23\xff\x06\xfa\x5e\xc1\x8d\x31\x87\x9a\xb7\x44\x6e\x75\x01\xb4\xbd\xcf\x10\xa6\xd2\x76\xa6\xcc\x35\xf5\x46\x73\xf9\x79\xd7\x7a\x7d\xa9\xe0\xe1\x7d\xa1\x82\x41\xc5\x13\xb0\xc5\x46\xb8\xb9\xdc\xf1\x09\xfb\x90\x67\xf3\xc6\xbe\xfa\xe3\x06\x67\xb3\x02\xd4\xdf\xd6\xbc\xf7\x06\xba\xde\xaa\xe2\x5e\x03\x08\x7f\x3f\x2e\x6b\xe7\x88\x56\xef\xc7\x1b\x24\x3d\x7e\x10\x19\x27\xfa\xe2\xb4\x88\xe8\x1b\x04\x32\x89\x95\xef\x48\xcc\x37\xdc\xec\xf3\x0e\xb7\xb4\xfe\x9b\x2a\xd5\x3d\x8e\xb2\x10\x56\x7d\xa6\x0b\x92\x06\x8b\x82\x5d\x9c\x7b\x11\x36\xd5\xba\x2a\x4b\xda\x81\x25\x52\xb1\xb2\x03\x4c\xbf\x4c\xda\x4d\xb2\x2e\x67\x1e\x7a\x9d\x3a\xfb\x1a\x2f\x30\x8a\x48\xf5\xc5\xe1\x8d\x3d\x8c\x86\xf4\xfc\x63\xb5\xb6\x70\xba\x9b\x4e\x15\x7f\xd8\x57\x60\xa2\xbc\x3e\x1c\x1e\xca\x6f\xac\xae\x8d\x39\xd3\xa4\x7c\xc5\x2f\xad\xaf\xbb\x44\x8b\x5a\xf8\xef\x09\x27\xc4\x0d\x9b\xec\x44\x5b\xb2\xe0\xd2\xf0\xac\x32\x2e\xd1\x55\xa5\xd8\xfa\x5a\x00\x4a\xe9\x41\x0e\x12\xb8\x64\xc6\xe6\x94\xdb\x28\x87\xd5\x5a\xdc\xc1\x2a\x12\x08\xfe\xd1\xf4\x68\xa6\x18\xc1\x4c\xc9\x49\x6b\x47\x1d\x00\xb8\x6b\xce\x4e\x92\x5d\xd7\x14\x40\x5a\x60\x71\x1a\x04\x57\xac\x65\xfd\x3a\x32\xd6\xd0\x94\xc0\xef\xf6\x41\x4e\x23\xb4\x41\x59\x07\xf7\xb0\xb0\xf4\xc6\x56\xed\xbf\x63\x93\x4b\xdd\x9b\xcc\xda\xa3\xd0\x2c\xbe\xcc\xf9\x64\xc3\xef\xc0\xba\xf9\xe4\xa2\xd3\x36\xf8\xfc\x01\x65\x52\x5c\x97\x2d\x9b\x0f\x57\x9f\xcd\x15\x9c\xf9\xf7\xe7\x8d\x26\x36\xcf\x2b\x40\x99\xdf\xe2\xb3\x75\x4a\x2b\x7b\x3d\x70\x25\x6a\x85\x7b\x96\x91\xc0\xb2\x08\xe9\xe9\x83\xf5\xdc\xc2\xbe\x72\xf9\x1f\x33\x25\xae\x8c\xae\x58\xd9\x98\x0a\x92\x50\x3b\x93\xdd\x7f\x3a\xcc\xbb\x8c\x4a\x19\x48\x98\x2d\xeb\xea\x8b\x18\x14\xc2\xb5\x24\x42\x4e\x48\x06\xc7\xa8\x3c\x91\x5d\xb7\x9d\x4e\x26\x1a\xd9\x19\x83\xa4\x68\x38\x6b\x4f\xb9\xf6\x72\x18\x83\xa1\x32\x83\x88\x1d\x41\x06\x15\xe3\xb3\xa1\x84\x66\x5e\x5c\x11\xf3\x27\x22\xf3\x35\xcf\x07\x1d\x37\x64\xbf\x99\xd4\xbb\x5f\x5a\x17\x50\x0a\xfb\xc5\xcb\x5e\x49\x05\xe4\x88\x84\x57\x8b\xb1\xb9\x05\x20\x94\x2c\x2a\x68\xa0\x59\x3b\xf3\x34\xf1\xe4\x76\x45\x6c\x06\xa7\x93\x46\x39\x99\x22\x22\xd3\xcf\xa0\x04\x13\x08\xe3\x62\x81\x4e\xd9\x31\x9a\x2c\x54\x96\xe7\x55\x18\x85\xee\xf8\x1d\xbf\x8e\x83\x0b\x90\x83\x91\x39\x6b\xcb\x4d\x4f\xe1\xd0\xe5\x2f\x0c\x2a\x33\xaf\x0d\xbe\xff\xce\xd5\x99\xfa\xc8\x5a\x3c\xfd\x5f\xc8\xfe\x5e\x91\x70\xf9\x23\xcc\xea\xed\xc5\x86\x23\x3d\x4c\xba\xdf\x86\xdb\x60\xb1\x9d\xbe\xe7\xbc\xee\x8c\xcd\x7d\xed\x35\x79\xec\xda\xdf\x1e\xfe\x40\x13\x9a\x18\xd7\xf7\x0f\xc8\xd6\x1d\x6c\x4c\xe5\x09\x20\xd8\xce\x87\x8f\xbc\x8b\x4b\xdc\xf3\xc9\x4a\xce\x91\x67\x62\x08\xc1\xeb\xe0\x55\x4b\x45\xe9\x90\x52\x3f\x08\x1f\x4f\x67\x0c\x64\x86\xdc\xc8\x17\x00\x03\xa5\x09\xe9\x57\x11\x07\x93\x6b\x65\xb8\x58\x3d\xa5\x09\x54\x60\xb6\x32\xdc\xaa\x54\xb0\x61\xe6\xeb\xe8\xc4\x29\xff\xae\x48\x90\x61\x5a\x54\x86\xfc\x38\xdf\xe6\xab\x28\x01\xb2\xf8\x7d\x7b\x69\x61\x67\x45\x5a\x3c\xb5\x8a\x27\x3b\x20\x8b\xd6\x86\x89\x44\x08\x39\x12\x92\x9b\xbb\xc2\xd8\xa3\x56\x1c\xfc\xc0\xe1\x03\xfe\xcb\xec\x8c\x19\x46\xc9\xad\x00\xa1\x59\x39\x4c\xde\x8c\x8d\x88\x8d\x38\xcb\x9e\x95\x03\x8a\x6d\x82\x15\x32\x61\x97\x5b\x83\x91\x2b\xc7\xc0\x75\xfe\x80\x9a\x3a\xba\xca\xdf\xd1\x8b\xfa\xc2\x5e\xba\x21\x07\xb7\x0f\xea\x39\x67\x3e\x4e\x59\x9e\xb7\x4d\xd1\x45\xc7\x35\x7c\xbf\xd1\xc1\xff\xf3\xb5\xb0\x64\x37\x71\x9d\x7c\x61\x2f\x5a\x3a\xb8\x26\x65\x39\x77\x1c\x9b\x2c\xe9\x7e\x4e\x62\x3c\x1e\xdf\xbf\xa6\x36\x59\x5b\x0c\x36\x8d\xcd\xfb\x9e\xd8\x7c\x0b\x6c\x9b\x7d\x3c\xaa\x48\xaa\xae\xa9\xbb\xef\x08\x27\x3c\x2a\x61\xf4\x82\xb4\x4c\x23\x32\xe5\x33\xa0\x10\x1b\xc9\x17\x1c\xbb\xba\x02\x7b\x0c\x26\xe0\x7b\x9d\xb4\xe7\x73\xf2\xc9\x29\xb1\x60\x3b\x3e\xab\x12\xd9\xf7\x65\x49\x88\x79\x0d\x28\x90\x69\x93\xd4\x3e\x13\xdb\xe3\x1a\x68\x4b\xbc\xa1\x6d\x72\xa5\xee\x99\x04\x6c\xcb\x5a\xdc\xf0\xac\x2c\x56\x6e\x69\xc9\x48\x12\xba\xe2\x0c\xc6\xb7\xfb\xca\xe3\xd6\xe6\xd0\x9f\x94\x92\x9a\x37\xd5\x60\xfc\x37\xc2\xa7\xc0\x31\x21\x7e\x1a\xa8\x92\x48\x25\x7f\x61\xa7\x3c\x78\xd2\x51\xf4\x07\xbc\x87\xa5\x00\x22\xad\x2b\xd9\x36\x4d\xef\xb9\xdc\x35\x39\x54\x5e\xba\xba\xba\xb2\xb3\xfe\xd0\x12\x28\x24\xa4\x98\x48\x69\x85\xe0\xc5\x93\xeb\x9a\x04\x22\x2d\x93\x1f\xb0\x63\x64\xa1\x03\x5a\x15\x50\xee\x6a\x43\x40\x2b\x93\x79\x1f\x31\xe1\x11\xed\x88\xb8\xc3\x6b\xd3\x97\x84\x88\x09\x54\xff\xa7\x16\x68\x53\xa8\x55\x8d\x01\x2f\xbc\xb6\x23\x63\x83\xb4\x90\xb9\x6f\x96\xa0\x9a\x04\xda\xf7\x78\x06\x76\xfb\xb6\x64\xc0\x85\x1a\x08\x7b\xe5\xaa\x74\x13\x88\x6e\xaa\x04\xbf\xe5\x39\xf6\x7a\x30\x6a\x6a\xe0\xb1\xf8\x78\x5e\x95\x18\x2e\xf0\x0b\x22\xf8\x5b\x75\xbb\x27\xbe\xea\x4f\xf1\xc9\xc0\x3d\x9c\xcf\xd9\x2f\x39\x73\x1a\xc7\xa7\xb0\x2f\x38\x32\x78\x73\x99\xdf\x99\x1c\xc8\x3b\xb8\x9e\x60\x5a\x60\x58\xcf\xe3\x56\xa1\x5a\xd3\xac\xb6\xa2\x70\xab\x5c\x91\x3d\x2f\xdd\x95\x93\xde\xfc\x65\x4f\xa5\x10\xd2\xbb\x32\x81\x78\x44\xf3\x6e\x6d\x68\xd4\x1b\x70\x5b\x5b\xac\x82\x56\x51\x35\xf4\x08\xc6\x9f\x45\x13\xd1\xb5\x98\x13\xa0\x1f\x8b\x52\x88\x62\x42\xe3\x85\x89\x36\x60\x2a\x15\x5c\x69\x22\x55\x3b\x76\x14\x79\xa1\x43\x4d\x4e\x15\xc6\x02\x21\x65\x9e\xb4\x30\x83\x2d\x35\xc9\xeb\x46\xff\x2f\x38\x8a\x85\xb3\x37\x2c\xda\xb2\x59\xcc\x2a\x7c\x4e\x0d\x84\xcd\x35\x62\x47\xbc\x21\x14\x3e\xeb\x7c\xa6\x16\x5c\xdd\x3a\x85\x27\x3c\x5a\x18\x8b\xa2\x67\x80\xae\x75\x3c\x86\x74\xc3\xe4\xdf\x52\x30\xa7\x9e\xe1\x10\x00\xf2\xd9\x03\x21\xc9\x71\x36\x1c\xba\x5b\x6d\xcb\xb9\xf1\x00\x3c\xc9\xbe\xdb\x0d\x0d\x27\x3b\xdb\xf8\x08\x81\x4a\x91\x7b\x65\xd6\xae\x9e\xa3\x1a\xa7\xee\xf8\x89\x41\x74\xad\x8d\xe8\xca\x75\xcf\x52\x68\x56\xb4\xbb\x3f\xcf\xf7\x44\xd7\x63\x5d\x9e\x13\xb4\xde\x7f\x35\xbe\x8a\xb1\x0f\x97\xe4\xe1\xfb\xf7\x20\xfc\xed\xd2\x35\x3c\xed\xba\x27\xea\xd9\xf9\xf5\x61\x7f\x8c\xd4\x1e\xd1\xb9\x67\x9b\x6e\xd5\x52\x88\xca\xa0\xf7\xd3\x02\xad\x88\xd3\x12\x95\x2a\xd7\x7f\x31\x0d\x2f\xc5\xe4\xdb\xa0\xa0\xcc\x5c\x2b\xcb\x7e\xed\xbc\x63\xd1\xe0\xf7\x66\xc9\x92\x00\x86\x74\xf1\xa3\x9b\xe1\xe5\xb6\x70\x67\xe6\xa9\x5e\x4e\xb5\x27\x74\x99\xdb\x00\xab\x12\xff\x10\xda\xa8\x56\x13\x85\x82\xc5\x7a\xc8\x65\x3a\x67\x79\x42\xbf\xa4\x4d\xf6\xa6\xaa\x09\x26\x2a\x39\xd1\xa0\x5e\x5d\x25\x33\x7e\x70\x30\x1e\x47\x86\x88\x4c\xf9\x17\x11\x53\x28\xbc\x34\x01\x18\x53\xfc\x9a\x81\x14\x52\xbb\x94\x3a\xb1\x4d\x43\x62\x6e\x0a\x07\x5c\x84\x3d\x9b\x73\x8d\xd3\x8e\x66\xaf\xf8\xc7\x75\x18\x8a\x4d\xd6\xe5\x25\xaa\xb1\xc2\x6e\xf9\xc5\x61\xd0\xf9\xc8\x67\xe8\x7a\xad\xfc\x32\x8d\x37\x9a\x44\xa8\x58\x3b\xf7\xf0\x43\x05\xd7\xfb\xd7\x0b\x6f\xf8\x52\x55\xfc\xbe\xe9\xc4\xe8\x07\x57\x4b\x27\xe0\x84\x7c\xe9\x74\x2f\x52\x1c\xd3\x80\xa1\xfb\xfc\xfd\x51\x57\x8c\x4f\x54\x1d\xdb\xe0\xee\x2b\x0b\x67\x05\x6f\xfa\x85\x07\xf2\xae\x3a\x8e\x41\x29\x53\xe0\xb1\xcb\xe1\x64\x10\xfc\xd4\x58\x4f\xf5\x2a\xdd\xfc\xd7\x2e\x0e\x63\x57\x41\xf4\x89\xcd\x50\xd1\x29\xd6\xe0\xe1\x4a\xa1\xbc\xa7\xcc\x70\x8d\xcd\x7a\x89\x92\x84\x97\x51\xa9\x98\x34\xc6\x40\xdd\x48\x7a\x06\xdb\xcb\xcb\xaf\xd3\xaa\xa0\x6a\x7f\x2a\x68\xae\x8c\xcf\xf5\x9d\xd2\xf5\x2e\xb1\xca\xc5\x68\xf2\x71\x2c\x1d\x60\x31\x09\x67\xe1\x65\x66\xbf\x25\x77\xfe\x1e\xd2\xb1\x04\xc0\x77\x4b\x11\x9f\x85\x37\x57\xdc\x6f\x75\xa1\x84\xaf\xe7\x70\x9a\x7e\x96\xd1\x1b\x07\xa7\x8c\x09\x5b\x81\x24\xf8\x3e\xc2\x81\x9b\xe7\xda\x16\x73\xac\xa1\x22\x99\x37\x50\xf0\xff\xc8\x76\x61\xa2\xc1\xba\x32\x0b\x82\xd1\x35\x32\x29\x46\x29\xb8\xf0\x16\x91\x52\x6b\x70\xcf\xdd\xc3\x5f\xbd\x5d\xdf\x0b\x7b\xc0\x96\x24\xdc\x94\xb8\xb5\x35\x98\x99\x49\x13\x94\xa9\xab\x4e\x17\x65\xde\xa5\xcd\xd8\x64\x9d\x32\x1a\x43\x10\x6c\xed\x11\x32\xac\xff\x3a\x82\xae\x3b\x29\x73\xf8\xa6\xac\xa7\xe7\xfb\x81\x29\xe5\x99\x1c\xf3\x7b\x25\x9f\xb3\xbf\x31\x7e\x89\x99\x33\xb7\x7f\x06\xf1\xff\xd1\x24\x32\xb3\x78\x31\x36\xf8\xc6\xa8\x83\x6c\x19\x3f\xb8\x2a\x39\xf3\x08\xf9\xfd\x8c\xf3\x4f\xbb\xac\xeb\x89\x95\xc1\x62\xb3\x9f\x92\xeb\x9b\x3b\x68\xb5\xd7\xf3\xdb\x93\xd1\xd0\xd0\x60\x76\x15\x5f\xbe\xfd\x4f\x15\x1c\x57\x53\xd5\x46\x36\x3a\xf7\x8c\x53\x9e\x9b\x9c\xcf\x34\x40\xd8\xf8\x9e\x59\x5c\x44\x9f\x3e\x9f\x99\x9a\x81\x11\x3e\x69\x2b\x64\xc1\x7a\x4d\x5b\xe0\xa0\xe6\x1f\xb0\x8b\x7b\xb2\xa4\xe8\x31\x47\xd1\xf3\x5b\xc4\x7e\xe5\xdd\x9d\xdd\x8b\x52\xf3\xd9\x65\xdf\x8c\x93\x7d\x69\x6c\x66\x00\x99\x54\x84\x80\x9f\x5f\xb5\xf8\x1e\xae\xa6\xa8\x9e\xa3\xad\xae\x4c\x7b\xb4\x64\xd9\x3a\xd3\x7a\x94\x62\x3b\xbf\x5c\x58\xbb\x18\x42\xd5\xd2\x3c\x59\x04\x15\x8f\x6e\x86\x15\x60\x24\xa9\xc8\x29\x5a\x3c\xa3\x11\xf9\xcc\xe1\x5a\xf5\x04\x97\xd5\xa5\x18\x42\xb3\xa8\x81\x63\x19\x2f\x33\x02\x91\x92\x45\xa1\xfe\xf3\xaa\xbe\x9a\x6d\x19\x2e\x20\xff\xfd\xdf\xe2\x1b\x96\x2f\x7f\x5b\xf7\x95\x85\x57\x0f\x5e\x7a\x4e\x0b\x02\x54\x6c\xe3\x13\x20\xb4\x2c\x5f\xbc\x05\x71\x3a\x7b\xaf\x6c\xe7\x8e\xf0\xea\x0c\x42\xf0\x7e\xe0\x36\x6c\x5f\xb8\xf7\x94\xd6\xe1\xb0\x84\xb3\x06\xcc\x77\xe7\x7b\x61\xf7\xa4\x2b\x8b\xc8\xd2\x53\xff\xad\xdf\xde\x7f\xb8\xca\xf0\xad\xe5\xbd\x1a\xf7\xc4\xf2\xa5\xb1\xf5\x47\x59\xe0\x73\xef\x34\x9d\xd6\xb2\xe3\x60\x9e\x1d\xbb\x25\xa7\x97\xb9\x9f\x27\xaf\xfb\x94\x5c\xd3\x21\xaf\x6c\x37\x6b\x6c\x7a\x8e\xb5\x0a\x8c\x36\x63\xf5\xf6\xe2\x61\x02\xec\xaf\xb2\x55\x5b\xfe\x55\x67\x0c\xf7\x7e\xe2\x4d\xab\xeb\xf9\x51\x29\x3a\x36\x6c\x94\xf5\x02\xa5\x8d\xf4\x1a\x35\x56\x6c\x3b\x70\xd1\x41\x3c\x09\x28\xd7\x79\x10\xff\x67\x88\x42\x1c\xde\x7c\xf5\x28\x21\xa5\xc8\x19\x6b\xf1\x3f\x36\x20\x9c\xb9\xb5\x29\x2e\x4c\x89\x2b\xa7\x8c\x86\x7c\x4a\xab\xb3\xb9\xe3\x14\x7b\xe7\x00\x46\x12\xca\xb9\x65\x5a\x62\x6f\xf7\x50\x17\x29\xe6\x35\x2b\x60\x0f\xa2\x88\xe3\x63\xaf\xb9\xd1\xea\xb0\x62\x58\x41\x3e\xd2\xac\x06\x91\x59\x1e\xc4\x5d\x40\xf1\x3d\x16\xc9\x91\x7e\xc6\x2c\x31\x4c\x4a\x61\x94\xcf\xfb\xf3\x10\xb0\x1c\xf1\x8c\xbc\x35\x12\xb5\x3d\x27\x6b\xe8\x03\x62\x1a\x36\x3d\xad\x46\xc7\x80\x39\x3d\xfd\x14\x88\x39\x82\xbb\xd1\x52\x2e\x0d\x3f\xc5\x6f\xf5\xf9\xf5\xf6\xef\x17\x2f\x4f\xa0\x35\x09\x39\xfb\xdb\xed\x0f\xaf\x37\x0c\xe1\x8f\xec\x5a\xdf\x78\x96\x83\x5b\xf7\xbb\x60\x77\x20\x74\xac\x4f\xea\x7e\xa6\xe5\x65\xe7\x4b\xe5\x29\x0b\x6b\xba\x29\x57\x75\x06\x02\x1e\x43\xb5\xde\xde\x84\x33\x15\xd7\x8f\xd9\x9e\x7e\x9b\x7f\x66\x5c\x6f\xdd\xe0\x57\x92\x6c\x86\x36\x13\x40\x1c\xd9\xbc\xc0\x9e\xbf\x49\x7d\x52\x0a\xe9\x32\xbe\x30\x88\xfe\xd5\xda\x09\x54\x5c\xe4\x60\xfb\x15\xf9\x08\x85\x1e\xbc\x2e\xbe\x23\x91\x61\x2c\xcb\x6a\xaf\x56\x90\x89\x80\x77\x82\x4f\x1a\x42\x6d\xab\x78\xad\xab\x57\x88\x80\x37\xc8\x92\x35\xfa\xd2\x09\x66\x32\x00\xb1\x82\xa6\x4a\x16\x5f\x3c\x61\x4b\x2c\x13\x62\x7b\x3f\x6e\xfc\x84\x3c\x71\xd4\xb3\xd2\xc7\xaa\x05\x2c\x46\xee\x14\xb9\xf4\xca\xcc\x85\xa1\xd3\x92\xa6\xff\xd1\xad\xa6\x4b\x31\xfb\xd5\xfe\xa0\xdf\xbc\xd2\x9c\x19\x2e\xa5\x85\x6d\x47\x11\x8f\x40\xc1\x40\xab\xa8\x2e\xa4\xf7\x9e\xcf\xf5\x83\x0f\x5c\x47\xe2\xb2\x0a\x9f\x8e\xd0\xa4\x36\x9d\xe9\xeb\x2e\x67\x8b\xb5\x33\x93\xd6\xd0\x13\x3c\x69\x19\x63\x45\x5e\xb4\x8e\x53\xe8\x52\xbc\x7a\xe6\xd8\x68\x20\x80\x46\x95\x7e\xad\x7f\xf0\xc3\xcd\x2a\x34\xaf\xe7\x64\xee\xad\x3f\xc9\xe1\xe0\xde\xd4\x70\x26\x6f\xf7\x11\xc4\x61\xcb\x31\xd1\xe3\x18\xd1\x0a\x6a\xcb\xe5\x58\x76\x9d\xbd\x2d\x6b\x2c\x4e\x30\xa0\x01\xe3\xd3\x8d\xb0\x90\xee\x64\xbd\xef\x56\x3b\x9b\xcf\x33\xf1\xd1\x92\xe8\x78\xa9\xa2\x08\x9f\xd7\x39\x79\xf7\xbb\x28\x9c\xdb\xc9\x1f\x71\xd3\xe1\x60\x31\xaf\x0d\xe1\x23\xb3\x90\x4e\x6b\x4d\x63\xbe\x0e\x0c\xfd\x94\xda\x49\xba\x95\xc4\xf7\x30\x22\x23\x37\x53\x68\xce\xaa\xf9\xba\xd7\x1f\xa8\x5a\xa5\x7c\x96\x41\x9e\x4c\xf3\x08\xd5\xbb\xc3\xf6\x6e\x0f\x80\x43\x94\xcf\x29\xfe\x6f\x06\xfe\x8c\x49\x72\x1a\xd8\x70\x7f\xa4\x46\xaf\x9a\x89\xc5\xe1\x8d\xda\x09\x97\x3c\x53\x71\x18\x39\x6f\xbd\x39\xdc\x35\x1c\xcd\xc5\x9b\xc6\x9e\xd3\x1d\xae\x84\xe1\xa6\x13\x3e\x4f\xb0\x33\xab\x84\x53\x83\x02\x6b\xbf\x1b\x57\x0a\x37\xdf\xc3\x59\xe7\xb5\xac\x08\x15\xba\xa4\xec\x63\xd2\x26\xb5\xd1\xc4\x61\xad\x4d\xa8\xa0\xf2\x45\x2d\x82\x10\xeb\xeb\x4c\x11\xca\xc5\xb1\xb2\x5a\x31\x18\x12\xd3\x02\xaf\x56\x24\x89\xfd\xa4\x7b\xb5\xae\xea\xe3\x71\x0c\x87\xd4\x84\xa5\x7f\x63\x6e\x7f\xcd\x47\x4b\x2b\x2a\x41\x16\xfe\x4a\x13\xae\x05\x25\x87\x6c\x97\xd1\x8b\xab\x96\xd0\x27\x6d\x1c\x98\x55\x49\xd5\x61\xb2\x5b\x54\xbf\x76\xca\x2f\x59\x17\xd6\xa1\x7f\x26\x79\xbb\x57\x10\xe7\x1f\x0f\xb5\xd6\x76\x4a\x93\xcb\xe7\x4a\x68\x4e\x17\x27\xf0\x8e\x32\xa6\xb0\x3d\x62\xaa\xe8\x1b\xa5\xe8\x7f\x33\x84\xcf\xfb\x08\xf2\xca\xf7\xa6\xbd\xf3\xb3\xe8\x3f\x99\xe0\x7d\x03\xf5\x0b\x83\x20\x38\x33\x51\x14\xfe\xbe\xb4\xff\x88\xcf\xcd\x71\x30\x18\x0a\x05\x11\xa5\xe7\x94\x13\xc6\x45\xe9\x5f\xdd\x9c\x8b\xe4\x6b\x23\x62\x66\x9a\x4d\x9b\xfb\xd8\x96\x49\xc3\x01\x6d\xf5\x3e\x1d\x9a\xce\xe0\x9d\x7e\x79\x4b\x81\xce\x79\x8e\x67\xd5\xa9\x37\xb1\x18\x2e\x04\xcc\x51\x70\xa0\xa1\xde\xa4\xe3\xbf\x6d\xc7\x48\x58\x92\x58\x79\x4b\x77\x0c\x99\x0f\x5b\x22\xbf\x34\xa5\xf9\xce\xf4\x9b\x58\x61\x0b\x33\xff\xe0\xd6\xc1\x62\xd5\x4d\x8f\xb7\x5a\x8d\x46\x4b\x62\x77\x2e\x52\xfe\xcd\x80\xcb\x4f\x46\x13\xaf\x4e\x2a\x2d\x1b\x13\x93\xb5\x79\x68\xc1\x55\x16\x41\x40\x25\x00\xbb\x26\x8f\x89\xc6\x73\xb0\x53\x42\x5e\xc5\xa9\xab\x53\x9e\xf4\x14\xc3\x1f\x1a\x53\x70\x8c\xc2\x69\x9d\x49\x99\x9e\x95\x83\x21\x99\x6f\xa5\xdf\xe9\xfa\x72\xcb\xf9\x0f\x36\xff\x1f\xc2\x48\x53\xae\x4c\xa8\x06\xd9\x8b\xe6\xa9\x11\xd2\x0d\x9f\x3b\x77\x36\xfc\x5f\x23\xc6\xb4\xbe\xdb\x70\xcf\x87\xe1\x22\x3a\x50\xc6\xae\x7b\xa8\x9b\xde\x4f\x37\x28\xaf\xe9\xf2\x96\xe8\x30\x2c\x6c\x64\xf6\xdf\xe3\x55\x81\x61\x49\x81\x0f\x94\x34\x3d\x96\xb9\xd8\xef\x7f\x46\x63\x4c\x9e\xf8\xa9\x37\x07\xe3\x58\x84\x47\x3b\xe1\x69\x1e\x17\x4f\x3c\x39\x65\xd9\x68\xdc\xbf\x86\xde\x96\xb8\x76\x9a\x6e\xcb\xaf\xbb\x3b\x9f\x08\xee\x1e\x4d\xc5\x65\x65\x05\x25\x84\x47\x7f\x5c\x17\xc9\xfe\x2c\xb2\x9d\xbf\x04\xc4\x4c\xa9\x96\x0b\x45\xd0\xa6\x57\x00\xd1\x44\xb0\x45\x50\x35\x99\x29\xd5\x50\xfb\xd5\xd5\x2a\x81\x41\xb4\xa0\x51\xf1\x83\xd4\x78\x51\x13\x03\xe4\x6b\x72\xb0\xc1\x04\x5d\xf6\x57\x84\xf4\x8b\x37\x7f\x5a\x96\xe8\x2b\xf2\x96\x12\x77\x3c\xca\x9c\x67\x09\x1f\x8c\xaf\x7d\x6f\x52\xac\xd8\xc9\x1d\x7b\x51\xae\x2b\x75\x6f\xcf\xcf\xb7\x9d\xe7\x78\xbd\xaf\xd9\xbf\x56\x75\x7e\xc5\x85\xaa\x02\x6c\x71\x5b\xa1\xd0\xb9\xcd\x05\xa4\xf0\xb2\x91\xaf\x90\xb2\x5e\x84\x4b\xb8\xc3\x23\xbd\xa9\x46\x7c\xa3\xa8\x63\x89\x31\x92\x9a\xb3\x72\x71\x52\xd0\xa2\x02\x9e\x40\xcc\x2c\x36\x58\x03\x02\x2b\x51\x77\xc9\x06\xa6\xb1\xcf\xcc\x9c\x00\x90\x29\x25\x23\xbf\x8b\xa7\xb4\xc3\xb0\xd1\xfe\x0a\xd3\x52\x70\xd8\x6c\x36\x76\xbc\x71\xe5\x51\x83\x1c\xe9\x6c\x28\x3e\x2d\xad\xad\x2b\x1b\xff\xd1\x76\x72\x74\x3b\xa3\x13\x1d\x8c\x41\xdc\x71\x30\xad\x4e\x29\xa3\xf1\xef\xe5\x14\x55\xd2\x50\x26\x31\x0f\xed\x07\xdc\x5b\xa5\xfd\x67\x15\x80\xe7\xc0\x52\x8f\x27\x27\x37\xaa\xe1\x90\xd2\x59\xcb\xdd\xec\x9f\x61\x70\x7e\xe6\x12\x55\x4e\x8a\x21\xb3\x0c\x4c\x21\x2b\xd9\x4a\x3a\x13\x19\x63\xf9\xc3\x8b\x09\x7a\x2f\x8a\xd4\x08\x0b\xa5\x60\x4f\x68\xf2\x34\xac\xed\xe5\xe0\xcb\xda\xe3\x7e\x6d\x7c\xc3\x7b\x63\x7c\xfa\xe9\x9f\xfb\x50\xd0\xe5\x71\xb6\x13\xfc\xb5\x41\x94\x7d\xdc\x94\x77\xf5\x70\xd0\xb1\x6b\xae\x77\xbb\x47\x97\x6c\x1e\xee\xe4\x9f\x07\xde\x43\xb6\xd5\xd5\x1c\x4e\x45\xf4\x9e\xa7\xc7\xf7\xc9\x97\xcd\xc9\xf7\xbc\x84\x2b\xeb\x8d\xec\x3c\xc6\x3f\x95\x78\x95\x79\xff\x90\x85\xb2\x86\xaf\x70\x86\x89\xe6\x7e\xda\x51\x3a\xed\x6c\x63\xfb\x9f\x8e\x9d\xdf\xc6\xbc\x1e\x2c\x4e\xc8\xf0\x81\x71\xe7\xe2\xfd\x66\xc3\x23\x18\x98\xb8\x62\x4f\x46\xa0\x5b\x4d\x52\x50\x52\x8d\xf1\xcc\x4e\x47\x6d\xba\x06\xed\x92\x43\x93\x6b\x78\x06\x2a\xe9\xa5\x9a\xc4\x0a\xe5\xf6\xc2\x44\x4c\x47\xa4\x59\x62\x75\x8a\x8a\x5b\x09\x00\x27\x90\x6f\xce\xc4\x2c\x5f\x47\x78\x0e\x91\x6d\x40\x7e\xe7\xfb\xc6\xbd\xed\xb9\x46\x13\x26\x60\xa1\x13\x6e\xa0\xc5\xf5\x46\x86\x1a\x2a\x29\xf3\x4f\x42\x92\x75\xb9\xbd\xa8\xbe\x62\x39\xa5\x69\xe6\x04\x0c\x6f\xc6\xfc\x12\x86\xfc\xf5\x9a\xd7\xbb\x79\xea\x3f\xf3\x20\x6a\x98\x9d\x45\x6a\x0c\x78\x78\x7d\xaf\x47\x48\xcf\x1b\xea\x29\x36\x1b\x73\x84\xe7\x80\x15\x11\xc4\xb1\x7a\xfe\xa0\x01\x0e\x04\x77\xe3\x23\xec\x35\x0f\xc0\xec\x80\xa1\x7d\xdd\xc3\xc6\x32\xfb\xf8\xcd\xf1\x7c\x9f\x5f\xbc\xe5\xce\xef\x22\xfe\xe7\xf3\x58\xc7\xfb\xb8\xf1\x9f\x0d\xcf\xc0\x6f\x68\x46\xa7\x43\xfe\xdd\xd9\x32\xc1\x1f\xbc\xe8\x31\x70\x3b\x7a\x54\x85\xd4\x63\x0f\x64\xe4\xbe\xcb\x4b\xed\xb5\xa6\xc9\x5b\xff\x3b\x51\x3d\xbd\x5f\xb2\xd9\x3c\x5f\xf4\x16\x5e\x3a\xd5\xd9\x98\xd8\x48\x12\xe9\xda\x69\x77\x59\x96\x9a\x3b\x83\x91\x5e\x29\x49\x6b\x36\x67\x82\x8a\x56\x69\x29\x43\x91\xad\xc4\xbb\xe2\xc5\x9c\x44\x56\x4a\x9a\x66\xb1\xf6\xff\x3c\xa4\x91\x15\x69\x14\x19\x35\xed\x43\xc7\x15\xd4\x17\xa7\xe4\xba\x0d\xaa\x7f\xe4\x7b\x45\xe6\x97\x76\x0e\x73\x92\x8b\x4f\xdf\x9b\x9d\xb5\xc4\x89\xf2\xdd\x72\x63\xb6\xa3\x4a\xfc\xc2\xdd\xd2\x93\xa3\xee\xba\x51\x27\x77\x8f\x3b\xfe\x98\xc1\xa2\x22\x9a\xc2\x26\xad\xf3\xaf\x36\xfd\x12\x2f\x25\x05\x24\x58\x88\x52\x11\x29\x9d\x68\xcb\xb9\xc2\x59\xc6\xda\xd3\xb5\x56\x54\x54\x45\x39\xb2\x0f\xe8\xc8\xc6\x18\xdb\x36\x9d\x2c\xe4\x0b\xe3\x48\x2a\x45\x3d\x3f\xa6\x69\xef\x74\xa5\xf1\x7b\x7f\xd0\x31\xbe\x9e\x3d\xb3\x8d\x2e\x2d\x9c\xa3\x57\xef\xfd\x0c\x91\x20\x70\x18\xf9\xe0\x84\x6d\xd6\xb7\x20\x92\x8d\x94\xea\x83\x9a\x16\x84\xc1\x93\xd5\x1f\xca\x57\x8e\x09\x4e\x9f\xb1\xfa\xcf\x7a\x33\x74\x87\xe7\x6c\x53\x84\x61\xb9\x11\xeb\x15\x77\x6d\x59\x3a\x3a\x86\xae\xf6\xfd\x5c\xd3\xdd\x68\x9b\xff\xb1\x46\xde\x7e\x17\x9f\xdc\xc1\xd7\xd6\x2e\x6c\x29\x58\x3c\xbf\xab\xb9\xfd\x16\xd6\x38\x51\x6c\x5c\xc8\x96\x80\x41\xc3\x97\xa1\xbd\xe0\xf2\x56\x62\xae\x99\x01\xf7\x3c\xab\xc0\xec\x73\xcc\xe5\x79\x01\xbd\x30\x5b\x35\x25\x0b\x47\x54\x6d\xf7\x49\xdd\x66\x15\x43\x2c\x40\x85\x59\xe1\x6e\x98\x8a\x41\x5e\x62\x60\x32\x3c\x39\xd2\x69\x13\x18\xb1\x53\x8f\xb7\x98\xbc\xcd\xc2\xb1\xd6\xaa\xc1\xed\xf0\xe8\x3c\x61\x72\xc3\x60\x14\xb3\x48\x02\x94\x14\xab\xf5\xb6\x04\xf2\x3b\x8b\xec\x24\x50\x36\x29\x66\x06\x28\x17\x98\x58\xe8\x17\x80\x56\x97\x4c\xfa\x5c\xfd\x20\xaa\x46\x2c\xbd\x73\xbd\x25\x48\xb1\x98\xd1\xaf\x70\xb0\x4c\xcd\x46\x16\x1d\x69\xfe\x39\xfc\xaa\x98\x6b\xfa\x38\x86\xe6\xda\xe0\xc4\xd7\x31\x28\x5e\x85\xaf\xae\x7e\x1a\x6f\xb6\xac\x4d\x33\x16\xa1\x97\x72\x60\x4d\x85\x98\x37\x03\xe3\xe5\x15\x79\x65\xc9\xee\xff\x9b\x65\xd3\xd8\xdf\xa9\x2b\x7a\x9c\x3a\x7f\xdb\xf0\x33\x82\x1a\x2c\xf8\xcd\x2f\x11\xe7\x70\x4f\xdf\xe7\xc3\x9a\xc7\xeb\x0d\xd5\xfb\x70\xca\xfd\x1d\x59\xc6\x75\x37\xdf\x83\xbf\xe5\x9e\x8d\x3f\xf6\xd5\xcb\xfe\x42\xc8\x35\x6c\x64\x89\xa5\xab\x99\x78\x1f\x97\x22\x78\x86\x90\x7c\x92\xf7\xfb\xd4\x57\x14\x11\x51\x30\x01\x78\x63\x42\x37\xa1\xe0\xad\x25\x5d\x0f\x6a\xd6\x2f\x60\x63\x29\x62\xb4\x58\xeb\x5e\x5b\xf0\xc3\xb1\xc4\x0c\xa3\x7e\xa0\xe4\xd0\x57\x73\x2b\x38\x72\x67\xa5\x5f\xe6\xdd\x81\xfa\x08\x1d\x90\x50\x63\xc2\xc8\x6c\xad\xd0\xc3\x2b\xbd\x51\xea\x8e\x13\x63\x3d\xed\x46\xd1\x7c\x95\xd9\xc6\x8c\x01\x64\xc4\x2b\x1d\x19\xb4\x84\x4a\xe2\x52\xff\x10\x4f\x03\x32\x3b\x4b\xaf\xb1\x53\x81\x7b\xea\x10\xf9\x4a\xae\xec\x2f\x4a\x62\xb7\xcc\x9e\x70\xd4\x48\x0a\xa1\xa8\x1c\xa1\x11\x24\x0b\x15\x1e\x71\x97\x37\xc6\x7f\x1b\xe6\x3a\x2c\x19\xef\x30\xc2\x9d\x63\xd9\x2e\xcf\x26\xec\x9f\xbc\xaa\x5c\x58\x33\x4c\xcc\x7d\xe8\xaf\xb7\x97\x61\x24\xd8\xa3\xdb\x8c\xd9\x92\x17\xfb\x58\x03\xf6\x39\x2c\x29\x25\x3d\x12\x82\xb1\xf1\xdd\xfc\x82\xfd\x3f\xc6\xdc\xef\xd2\xfc\x1f\x60\xfa\x9e\x96\x28\xff\xb2\x3e\xe5\x06\x4b\xe6\x5a\xb8\xff\x00\x15\xf9\x9b\x26\x26\x27\xb9\x31\x79\xa0\xcf\x6f\xb2\x4e\xc7\x0b\xf0\xb8\x74\xff\xec\x5d\xc0\x05\x15\xb5\xc6\xbb\x7b\xb9\x74\x6d\x6b\x3d\xb3\xce\x98\x95\xa2\x4c\x97\x67\x14\x23\x55\x9c\x28\x32\x57\xeb\x23\x1c\x9a\x0d\xb1\xec\xe7\x4b\xd0\x8a\xd7\x65\x53\xc6\xeb\x1c\xe3\x14\x2b\x35\xc3\xa2\x1d\x2c\xc7\xc4\xb5\x0f\x34\x6b\x93\x0f\x5c\x82\x4a\xff\x34\xaf\xf5\x68\x94\xfb\x7c\xac\x53\x0b\xc6\xd7\xc3\x86\x16\xce\xdf\x1b\x6c\xe6\x2b\x84\x8c\x82\x77\x70\x54\x3b\x20\x6b\xe9\x5e\x19\x04\xda\xd1\x94\xfc\x8d\x49\xd1\x10\xc0\x18\x08\x9d\xd7\xd3\x05\x11\x81\x39\xc8\x08\x2d\x6f\xfd\xaa\x9e\x89\x74\x0d\xda\x88\xb7\x26\xd1\x22\x87\xa8\x76\xe5\xa2\xbd\x73\x68\xf9\x1b\x92\x53\x63\x89\xf3\x26\xcb\xa7\x4e\xfb\x92\xed\x93\xa0\xdb\x6b\x97\x62\x90\x9a\xa0\x1d\x4e\x08\x99\xbe\xc4\xb4\x1e\xb6\x0f\x33\x8a\x57\xdf\x04\x8f\x07\xbe\x86\x3b\x58\x7c\x32\xfc\xa6\x31\x39\x40\x3a\xd9\x27\x46\xa0\xa0\x92\xe7\x99\x6a\xb8\xe1\x87\x32\xa9\x4b\x92\x0d\x52\x56\xc2\xd3\xce\x3d\xf4\xb1\x1e\xa4\xd8\xf3\x65\x9e\x3e\xf4\x09\xc3\x2f\x59\xc6\x3b\xb6\x90\x1b\x5d\x5a\x5b\x2f\x1f\xfc\x87\x0e\x33\x46\x1d\x52\xd8\x9a\x67\xa3\x19\x26\x0b\x6c\x34\x86\x82\x35\x92\xcb\x78\xc3\x61\x95\x8b\x71\x4b\xd0\x6f\xa3\x07\xaf\x42\xec\xc8\x53\x8e\x28\x4a\xed\x62\x74\x26\x1a\x89\x3f\x49\x47\x66\x40\x2e\xdd\xf8\x22\x5b\x1a\x01\x95\x53\x1a\x9c\x44\xa4\x9f\xe4\x4f\x7a\xd4\x9c\x7e\xb6\xb0\x31\xc8\x36\xb6\x06\xcb\x22\x93\xd4\x39\xa7\xcc\x1b\xdc\xe5\xd2\x63\x1d\xc1\xb7\x82\xb0\xbd\xe8\xbf\xf1\x52\x60\xc1\x38\xe4\xfc\xa3\xb0\x9c\xc7\xc9\x77\x15\x6c\x40\x5d\x37\x22\x20\xb4\xd4\x4d\x23\x91\x84\xa7\xa8\x55\x83\x30\x6c\x01\x85\x1b\x8c\xff\x81\x1d\x22\xdc\x99\x5d\x5e\xff\x3f\x6a\xc4\xe3\x40\xe8\x35\x44\x0c\xff\x16\x7e\xe3\xbb\x75\x59\x9f\x65\x51\xc6\xc4\xd7\x54\x63\xb6\x82\x06\xc4\xe8\xb0\xf2\xed\xa9\x33\x9b\xe8\x4a\x89\xb3\x7b\x6c\xd7\x81\xc1\x3d\x30\x33\x7e\x3f\x97\x43\xbc\x73\x45\x3e\x6d\x2e\x49\xd7\x1b\x58\x86\x5b\x3e\xa4\xd7\x6f\xdf\xa7\x63\xac\xff\xd2\x46\xb9\x34\x51\x29\x45\xc7\x3f\xf2\x5f\xef\x07\xdf\xd3\xf6\xa7\xf5\xdb\xdf\x21\x91\xc2\x92\xe8\xe8\xeb\x98\x72\x97\xb8\x36\xeb\xcd\x86\x0b\x58\xc5\x85\xcb\x16\x47\x8a\x36\x29\x61\x77\x21\x1a\x45\x46\x37\xa8\xce\xd8\x40\xb2\x86\xa2\x46\x0c\x18\x20\x62\x21\x18\xec\xe3\xb4\x75\x6f\x5a\xa8\xb5\x8c\x8f\xd0\xf0\x18\xa7\x89\x1e\xbc\x3b\x16\x50\xe3\x13\x58\x69\x41\xe4\x23\x12\x06\x52\x5c\x1a\x2b\x2c\xfa\x0b\x94\xc8\x08\x56\x71\x40\x40\x70\x72\xa5\xc5\x84\xd5\x48\xfe\x1f\x00\xad\x19\x52\xe6\x90\x19\xdd\x3a\x99\xa9\x8c\xcb\x7b\x30\x2a\x30\xaa\x5c\x4b\x4a\x69\x32\xb9\x5f\x90\xe1\x93\xe9\x3a\x3b\x63\xa1\xed\x29\xb4\xc6\x33\x73\x8f\x61\x2a\x72\x29\x7c\x34\x84\x7a\x76\xcf\xaf\x8c\x02\x54\xa8\x94\x56\xd4\xd6\xf3\xfc\xfa\x06\x6b\x7b\x97\xe8\xc5\x1c\xb5\x0c\x41\x52\xa2\x4e\x4c\x86\x36\x59\x9b\x08\x49\x8e\x21\x7e\xcc\xd2\xdc\x1e\x6e\x38\x72\x07\x47\xf6\x6a\xfe\xd3\xaf\x7e\x94\xcf\xfc\xc1\x59\xde\xf4\xb6\x2d\xec\x79\x4f\x35\x6e\x70\x75\x85\x1e\x6d\x92\xd5\x15\xb9\xf5\xe4\x03\x4f\x36\xde\x02\x7d\x9e\x9f\x7f\xf9\x7b\x78\x69\xfb\xab\x59\xee\x7c\x81\xce\xe6\x29\xec\xee\x67\x70\xc7\x5e\x87\x59\xd9\x65\x8f\xde\x81\xd5\xc3\x68\x34\x83\xdd\x2d\x2e\x5f\xdc\x65\xb2\x7b\x82\xf0\xe4\x06\x2f\x5c\xde\xa1\x93\xbf\x93\xc5\x9b\xe7\x98\xcf\x3b\x94\xba\x40\x79\x47\x13\x63\xf2\x32\xcb\xc4\xc7\xd6\x9a\xb2\xd0\xd2\x59\x6e\xae\x6c\xbb\x4e\xa0\x4d\x91\x19\x36\x47\x35\x1a\xe8\x97\x39\x7b\x16\xba\xd3\xf4\x2d\x81\x71\xf4\x19\xb2\x98\x1c\x93\x38\x5d\x54\xbe\x94\x96\x08\xa1\x49\xea\xe9\x6b\xfb\x07\xd5\xd4\xde\xa7\x02\xcf\xd4\x48\x56\x3b\x9f\x14\x4a\x6b\x16\x04\x6c\x82\x49\x6d\xe9\x95\x39\xd6\x07\x2e\x6c\x0c\x59\x9b\xef\x4a\x6f\x95\x17\xf0\x2a\x57\x8a\x90\x79\x94\xca\xc9\x95\x61\xec\x6d\xeb\xda\xfb\x58\x72\x9e\xcc\x4c\x23\x35\x06\xd7\xd6\x00\xde\xf9\xca\x7a\x47\xae\xe1\xc6\xb5\x3d\x64\x99\xe2\xc2\xc6\x88\xc5\x7e\x01\x21\x4d\xeb\x8e\xde\x70\x54\x69\x77\x1e\x5b\x61\x7d\x7d\xcc\x89\x4d\xcb\xa8\xb2\xac\x6f\x6e\xd2\x5b\x7c\x9e\x3d\xa6\x4f\xb7\x84\x1f\xf9\xd0\x05\x1e\xb9\x71\x40\xd3\xd9\x60\x70\x29\x67\xbc\xab\xd0\xe3\x9a\xbc\xae\x29\x83\xa2\x0c\x3b\x64\xee\x2c\x7f\xb6\xf1\x66\xde\x7b\xe6\xc7\x99\xe7\x3c\xd9\xc6\x7f\x67\x6e\xfb\x31\xf2\x7d\x8b\x74\x8e\x3d\x88\x79\xe1\x03\xf8\x6e\x1f\x1f\x02\x79\x51\xb0\xbc\x76\x90\xbb\xbe\x62\x2f\xe3\xaa\xe1\x89\x47\x1e\x65\xeb\xfc\xc7\xf8\xe2\xa7\xf6\xf3\xba\x25\x8f\x5f\x5b\xc2\xe4\x10\x82\x47\x39\xf1\xc8\xbc\x81\x80\x0c\xa0\xae\x1a\x70\xc1\x93\x1b\x1d\x87\x3c\x06\x3a\x5a\xb5\x18\xbf\x56\x52\x2f\x90\x1b\x29\xfd\x22\x88\xe9\x4c\x3d\xff\x09\x25\xad\x7d\x90\x22\x59\x20\x44\x76\xb5\xe0\x40\x65\xd3\x96\x32\x8f\xe4\x13\x82\x8b\x50\x6d\xf4\xa5\x8a\x4c\x4d\x29\x62\x95\x08\x85\xf7\xd3\xcf\x4f\x65\x62\x00\x0b\xfd\x4e\x7c\xf6\xb0\xdc\x2b\xdb\x42\xd5\xa0\x68\xeb\xfe\x44\xab\x05\x1a\xe3\xd0\x9e\x36\x39\x24\xce\xa6\xf8\x1b\xe2\xa8\x3a\x82\xb3\xaf\x22\x00\xc1\xc6\xd1\x1d\x32\x56\x15\xaf\x98\xeb\x66\x38\x6b\xb1\xd6\xe2\x9a\x9a\x49\x55\x61\xeb\x9a\xba\xa9\xd9\x63\x36\x39\xb2\x7c\x0b\x37\xad\x2e\xf0\xec\xd3\x9f\x67\xcf\xde\x55\xde\xf5\x35\x5f\xcb\xcd\x37\xed\xa5\xbf\xd0\xe7\xf8\xdd\xb7\xf1\x5b\x7f\xf4\x39\xbe\xe7\xc3\x3b\x7c\xdd\x5a\xe0\x8e\x83\xcf\x31\x1e\x57\x04\xd7\x23\xb7\x9a\x26\x1b\xe0\x2a\xc5\x4f\x3e\xf3\x7d\xfc\xca\x0b\xef\x84\xd1\x5f\x30\xd9\xfc\xaf\xf8\xfe\x80\xf2\xd6\xaf\x60\xf9\xc6\x5b\xc8\x5f\xfe\x18\x76\x79\x0f\xae\x69\xf0\xae\x41\x29\x43\x51\x94\x04\x9d\xb1\x7a\xc3\x21\xbe\xee\xee\x07\x78\xe6\x89\x47\x39\x7d\xea\xa3\xe4\xbd\x77\x48\x11\x64\x90\x5d\xcb\xf3\x80\xce\x55\xfb\xf0\xe5\x41\xfb\xd6\x7b\xf7\xf1\x34\x36\x61\x0a\xf2\x04\x24\x9d\xba\x3b\xac\xf1\xde\xb3\x77\x79\x9e\x4b\xbb\x43\xf0\xb0\x3c\xd7\x95\x99\xc1\xd1\x14\x34\xf1\xc1\xfb\xc8\xc7\x93\x95\x12\x2e\x4b\x1f\x9f\xfc\x4e\xdd\x78\xb6\x76\x47\xac\x2c\xcc\x61\xb4\x78\xe9\x95\xf5\x14\x71\x0c\x6c\x98\x11\x0c\x71\x14\x65\x46\xd0\xa4\x09\x74\x73\xc5\xf6\xce\x90\xa2\xcc\x29\x8b\x9c\xdc\x08\x77\x41\xaa\xf1\x4b\x4e\xa5\x22\xd0\x38\xd9\xcc\x16\xb1\x8c\x9a\xca\x46\xe7\x54\x9b\x8c\xcc\x6b\x7c\x50\xaf\xa6\x01\x42\xe5\x9d\xc7\x06\x8d\xb5\x56\x9c\x46\x2f\x25\xe1\xae\xa9\xa9\x27\x63\xec\x64\x44\x35\x1e\x51\x4d\x26\x5c\x3e\xf9\x45\xfc\xee\x90\xbd\xab\x0b\xbc\xf1\xe1\xfb\x79\xe0\xde\x37\xb1\xb2\xb2\xc2\xbe\x7d\xfb\xd8\xb7\xaf\x64\x6e\x6f\x97\xb7\x6c\x5e\xe2\x37\xff\xdb\xe7\xf8\xa1\x27\x97\x39\xf9\xef\x6f\xe4\xfd\x6f\x7b\x99\xe3\x4b\xe7\xa1\x58\xe0\xe9\x4b\x07\xf8\x85\xd3\x77\xf0\xc4\x17\xd7\x60\xfc\x7e\xe0\x31\x9a\xec\x08\xd5\xd1\x7f\x00\x8b\x47\x98\xcf\x1a\xf4\xfe\xc3\xb8\xa6\xa6\xa9\x27\xd8\xba\x26\x04\x2f\x8d\x1d\x9d\x2e\x3a\x92\x57\xbf\xf6\x4d\x6f\xe1\xa1\x77\x2d\x90\x97\x65\x4b\x9b\x42\x7c\x34\xa9\x2b\x36\x1d\x2b\x13\x6d\xf1\xd0\x79\x8a\x19\x3e\x3d\x50\xe4\x5a\xb5\x88\x5f\xa7\x23\xdc\x3e\xce\x35\x74\xd2\x3c\x01\xef\xda\x64\x4d\x93\x46\x84\x90\xb2\x84\xa1\xed\x79\x4d\x0e\x5e\x91\x19\x86\xd6\xb1\xd0\xef\x0a\xbc\x1c\x9d\xb8\x58\xc6\x1f\x99\xbd\xa6\xf4\x34\x19\x4a\x1a\x4f\x90\xba\x45\x93\x65\xe8\xac\x91\x89\xe3\xd6\x31\x6a\x5c\xcb\xfb\x97\xd2\xbd\xe9\x7e\x32\x1d\xfd\x0d\x35\xbd\x87\xa1\xf3\xc2\x1b\xa0\x14\x93\xc6\xd2\xe8\x8c\x2e\xee\xda\x3e\x40\x6e\x42\x35\x0a\xa0\x83\x13\xa2\x84\x24\x5d\x4d\x23\x9b\x3f\x1e\x30\x1a\xec\x30\x19\x6c\x33\x19\xee\x30\x60\x81\xe7\x4f\x3d\xc7\xe5\xcb\xf3\x1c\x39\x72\x03\xa7\x4e\x9d\xc2\xda\x8a\x2c\x9b\xb0\x31\xf0\x9c\x39\xff\x22\x97\x37\x2a\xde\xf5\x96\xdb\xf9\xc3\x8f\x7f\x91\xe6\xb6\x21\x3f\xfc\xc8\xcd\x74\x9b\x7d\x54\x9b\x67\xd8\xa0\x07\xec\x00\xbf\x2e\x5d\x0b\xe5\xfd\xe4\xcb\x47\xd9\xda\x7c\x9e\x1d\x7d\x91\xb0\x74\x3b\xab\x07\x6f\x62\x5c\x0f\x68\x26\x63\x6c\x2d\xc0\x93\xd6\x9a\xac\xec\x50\x94\x3d\x54\x96\x93\xe7\x02\xaa\x18\x90\xbe\x3c\x3b\x4d\xb1\x2a\x35\xc5\xe5\x9d\xf7\x18\x45\x2c\xf1\x9a\x22\x79\x62\xa3\x43\x54\xeb\x69\x5b\x15\x3a\xcb\xd8\x1c\x8e\x45\x00\xe2\x66\xa5\x4e\x1e\x19\xeb\x37\x05\x81\x52\x11\x46\xda\x14\xa5\xa4\xb2\x48\x6b\x19\x9a\xdd\x58\xa9\x3d\xcf\x25\x4b\x24\xa3\xdc\x98\xa6\x7f\x9d\x0f\xd2\xc8\xa1\xd2\x55\x15\xae\x6e\x28\x8b\x42\xd0\xca\x59\xc8\x91\xc8\x5b\x54\x7b\x8a\x5c\x45\x5a\x59\xd9\xf9\x10\xc7\xc3\x29\xd2\x24\x92\x68\x12\x14\xd2\x27\x18\xec\x15\x48\xe0\x95\x40\x90\xd3\xb5\xf7\x1e\xab\x62\xa2\x24\x8d\x74\xb3\x0d\xbe\xae\xd8\x1d\xec\x32\xde\xd9\x62\xb4\xbb\xc5\x78\xb0\x4b\xa3\x3a\xf8\x6a\x88\xde\xcd\x38\x71\xe2\x19\xb6\xb6\x86\x0c\x87\x43\x76\x07\x4b\x78\x33\x61\x77\x75\x85\xc9\x4d\x47\x08\x3b\x35\xa7\x3e\xff\x49\x94\x31\xec\x3d\x78\x91\xe7\x1e\xff\x32\xd8\xf7\x95\x70\xf1\x77\x58\xb8\x6d\x3f\xf3\xfb\x5f\xc7\xd8\x2d\xb3\x71\x72\x9b\xa5\xec\x2c\x87\x8f\xed\xe1\xa5\x93\x97\x39\xb2\x39\x62\xed\xe8\x1c\xd9\xdc\x2a\xb6\x91\x1c\x84\x72\xd2\x63\xaf\x4d\x46\xd9\xeb\x51\x94\x1d\x3a\x45\x4e\x1d\x14\x99\x07\x1a\x2f\xa5\xd4\x31\xbe\x6f\xb9\x7f\xe3\xc3\x55\x0a\x06\x63\x2f\x2c\x28\xe9\x0f\x57\x30\xae\xe5\xb4\xa4\xa6\x8a\x40\xa0\x6a\x2c\xbd\x22\x6f\x4f\x59\x32\x0f\x75\x74\x18\x3c\x53\xec\xbe\x4e\x5c\xc6\xed\x25\xa7\x14\xef\x75\x23\xf5\xdf\x6a\xba\x7f\xc8\xd0\x48\x8f\xd1\x52\x69\x34\xaa\x1a\x8c\x36\x14\x99\xcc\x2d\x6c\xac\xa5\xc8\x33\x9c\xf3\x24\xce\x80\x04\x58\x41\x4c\x56\x39\x4b\x91\xc9\x10\x09\x93\x81\x8d\x7d\x83\x69\x76\x71\x91\xab\x96\xd3\xc8\x18\x85\x6f\x02\x56\xe5\xb3\x3d\xbc\x57\x45\x01\xd4\x4d\x40\x4d\xfb\xd4\x5b\xc8\xd3\x53\xd9\x06\x5b\x57\x54\x93\x11\xc3\xc1\x80\x6a\xb0\x4b\x35\x1a\x50\xeb\x15\xd4\x78\x83\xb2\xec\x72\xee\xdc\x19\xb6\xb6\x2e\x72\xfa\x85\x05\x16\xef\xb9\x99\xc9\xa7\x3f\xc3\x6b\x3e\xf9\x49\xf2\x66\x85\x6f\x7a\xe0\xb5\x7c\xcd\x77\x7f\x3f\x47\x6e\xe8\xf2\x8f\x3e\xe0\x78\xfa\x99\x23\x74\x1e\xd9\x64\x65\xef\x4b\x18\xce\x50\x97\x81\x8d\x46\xe3\xbc\xe6\xee\xfb\xef\x44\xdb\x47\xb9\xdc\x5b\xe3\x50\xb1\x42\x39\x37\xa2\xe3\x1c\x65\xb7\x42\x79\x87\xf5\xc2\xb5\xd1\x29\x4b\xca\x6e\x97\x60\x72\x69\x97\x8e\x7f\x85\x89\xa8\x98\x84\x6e\x53\x4f\x38\x09\x44\xea\xaf\x4b\xf1\xb4\x8d\x03\x1a\xda\x46\xd8\x64\x93\x67\x92\x2f\x2d\x07\x40\x48\xc4\xa5\x0a\x1b\x3c\x39\x3a\xb6\xb7\xa5\x13\x48\xac\x9d\x9c\x86\x7f\xb9\xd6\x32\x77\x10\xa2\x96\x90\xf0\x2f\x9d\x50\x1f\x02\xbd\x32\xc7\x18\x23\xdc\xcc\x80\x31\xa6\x2d\x0b\xb3\x9e\x96\xe3\x67\x1a\x56\xc2\x4a\xbf\xa0\x89\x23\xe0\x6c\xe4\x21\xc8\xa0\x2d\x58\xc1\xea\x29\x6f\xa0\x15\x67\xb1\x43\x7d\x6d\x01\xc8\x54\xd3\x54\xde\xd3\xcf\x35\xde\x8a\xbd\x6b\xc2\x94\x4e\x55\x29\x22\x85\x9c\x47\x92\x46\x32\x6f\x70\xa3\xe9\x93\x55\x23\x0a\xc6\x5c\xbc\x64\x79\x7e\x73\x9e\x7d\x2f\x5f\xe2\x9e\x3f\xf8\x53\xee\x0b\xb0\xf7\xc1\x1e\xf7\x7e\xe0\x83\xf1\x53\x36\xd8\x5b\xff\x2e\x6b\x47\xee\x61\x6e\xf8\x10\x7d\xf5\x14\xc3\xcd\xe7\xe8\xcd\x1d\xe3\x35\x6b\x67\x09\xfd\x39\x30\x19\x5f\xf9\x8e\xb7\xf3\x67\x9f\xde\x60\x7d\xe1\xad\x1c\x37\x7f\x49\x28\x3c\x59\x59\xa2\x9c\x93\xdc\xb8\x82\xdc\x64\xec\xd4\x81\xcc\x05\x96\xe7\x8c\xd0\xad\x78\x4f\xe3\x12\xa7\x4f\x3c\x6b\xf1\x21\xc4\x1c\x49\x6c\x7b\x53\x34\x56\x28\x61\x42\x90\xcc\x5b\x91\x09\x80\x52\xd9\x40\x27\x97\x16\x8b\x24\x34\x29\xd3\x77\x76\x63\xc8\xde\xb9\x6e\x64\xe7\x54\x6d\x79\x7c\x1e\xe9\x3a\xad\x4d\xf1\x79\x9a\x30\x26\xaa\x3d\x8d\x76\x21\xde\x53\x40\x5a\xbf\x53\x82\x29\x00\x2a\xa6\x6d\x83\x95\xae\x21\xa9\xc2\x8a\xe4\x92\x41\x8a\x71\x66\xe7\xff\x55\xb1\xfb\x28\x99\x3b\x93\x1a\x42\x11\x28\xdf\xba\xe9\x24\x52\x63\x8c\x44\x29\xd8\x2b\x04\xe0\x8a\x64\x90\xf7\x99\x35\x41\xc8\x8d\xeb\x48\x00\xdd\xd1\x99\x54\xce\x68\x83\xca\x0a\x4c\x2e\x93\x37\x4c\x56\x90\x15\x05\x26\x2b\xc8\x8b\x82\x90\xcf\x33\xc9\x56\xb1\xfd\xc3\x28\xf6\x32\x7c\xea\x05\xe6\x0d\x6c\x01\xb7\xfc\xc2\x7f\x6e\x3f\xe3\xf7\xfe\xcb\x6f\x71\xfa\xb7\x3f\xc7\xe2\x2d\xb0\x78\xf4\x18\x2b\xe5\x22\x2b\xb7\x3c\xc0\x8d\x0f\xbc\x93\xbb\x1e\xbe\x8b\x5b\x0e\x75\x62\xb1\xa3\xe3\xab\xee\xed\xf0\xf2\x4b\xeb\xbc\x34\x5e\x25\x98\x1c\x95\x95\x50\x74\xc9\x3b\x3d\x54\xd1\x85\x4c\x06\x41\x74\x4a\xc3\xc4\x4a\x88\x0a\x8a\xdc\xa8\xd8\xe6\x1d\xc3\x38\xa6\x1a\x20\x25\x75\x32\x1d\x84\x9b\x2f\x0a\xb6\xd6\xb4\x4c\x9d\x99\x96\x18\x3a\xa1\x73\x3a\xfa\x0b\x99\x52\xcc\x95\x65\x0b\xcf\x26\x88\x17\xa4\x72\x38\xc1\xbf\xa9\x3c\xcb\x86\xc4\x09\x24\x5a\x23\x2a\x17\xa9\x8a\x48\xa5\xe9\x2e\x15\x92\xc8\xbd\xc9\xf1\x97\x68\xa5\x8e\x02\x61\x62\x5e\x20\xb5\xb0\x26\x2a\xf9\xb6\x39\x37\x84\x56\xe3\xa4\xf2\xf0\xa4\xbb\x6a\x3f\x7d\x06\x10\x70\xe4\xd7\x16\x80\x80\x6b\x40\x1e\x80\x51\x3a\x3a\x26\xa0\xb4\x41\xe7\x25\x79\xa7\x4f\xd9\x9f\x27\xef\xce\xd3\x99\x5b\xa0\xec\xcd\x93\x77\xbb\x64\x45\x47\x9a\x46\xb4\x8e\xd9\x0b\x4b\x76\xf1\x34\x93\x0e\x5c\x04\x9a\xa3\x53\xb6\xd0\x1f\x78\xdf\xa3\x84\xc3\x5f\x8f\xdb\x1a\x51\x1c\x38\x4a\xaf\xb3\xc2\xd1\xa3\x6f\x64\xdf\xe8\x45\x8e\x74\x4e\xb3\x72\xeb\x51\x39\x55\xce\xd3\x5d\x58\xa4\x58\xff\x04\xe7\xb2\xdb\xd1\xce\xa3\x94\x96\x06\x87\x2c\xa3\xcc\x73\x8a\x22\x67\xa9\xdf\xa5\xdb\xc9\x19\x4e\x1c\x93\x46\x60\x1d\x85\x10\x22\xc9\xde\xa8\x36\xa1\xd2\x6e\x5a\x80\x9d\x91\xa5\x72\x1e\x15\x1f\x60\xcb\xd8\x11\xbd\xc0\x44\x12\xed\x7d\xa0\x72\x9e\xcd\x41\x45\xed\x1d\xfd\x8e\xa6\x6a\x7c\x5b\xc4\x99\x9c\xc9\x36\x26\x08\xd3\x94\x6c\xa6\xa7\x38\x5c\x8a\x4c\x7c\x90\x2a\xab\xaa\x6e\xa8\x83\xb0\x8d\xd7\xf1\xde\x52\x3a\x38\x25\xa5\x52\x75\xb0\x0d\x92\x41\x0c\xf1\xfd\x89\x2b\x30\x95\x19\x24\x53\x9d\x60\x6e\xef\x43\x6b\x72\x0c\x31\x99\x17\x3f\xc7\x50\x5d\x5b\x00\x94\x97\x8c\xb3\x4b\x12\x85\xcc\xd4\xc9\xb3\x1c\x55\x74\x28\xbb\x7d\xe6\x17\x96\x98\x5f\xd9\x47\x7f\x69\x2f\x73\x8b\x2b\x74\xe6\x96\x29\x7b\x73\x98\xbc\x20\xb9\xc8\xfa\xf9\xa7\xd0\xb9\x66\x30\x80\x8d\xb5\x2e\xbb\x3b\x3b\x00\xec\xec\x3c\xc3\xe0\xc5\xdf\xa7\xd9\xfa\x1d\xc6\x9f\xfc\x0b\x86\x9d\x03\x34\x07\xee\x26\x9c\xfc\x0c\x7a\x70\x8a\x93\x55\x4e\xd5\x38\x6e\xbb\x71\x05\x42\x83\x6d\x6a\xee\xfb\xf2\x03\x98\x27\x7f\x8b\x13\x1b\x07\x09\x4a\x91\xe7\x39\x26\x32\x95\x5a\x0f\x5b\xbb\x23\x46\x13\x4b\xbf\x93\xd1\xc9\x15\x26\xa6\xb3\xeb\x38\xd2\x55\xb0\x7c\xd9\x94\xaa\xf1\x2d\x2e\x3e\x6e\xac\x9c\xd0\x4c\xe2\xf1\x2a\xf2\xfa\xe9\x38\x55\x24\xd9\xdc\x54\x5c\xb9\xdc\x2f\x5b\x21\xc9\xe3\x11\x2c\xcc\x8c\x9f\x10\x62\x26\x30\xf1\x03\xfa\xc8\x07\xe8\x25\x36\x4b\x6a\xdc\xe8\xe9\xbc\x81\xe0\x05\x28\x4a\x0e\xab\x8b\x7c\xcc\x49\x48\x4b\x1d\x23\x31\x15\x61\x5c\x33\x43\x55\xe7\xa7\xe3\xe8\x53\x4d\x22\x51\xd0\xab\xc6\xc7\x82\x54\x5a\xf3\x67\x83\x93\x36\x74\x6f\xaf\xed\x03\xd8\x90\x3b\xef\x9d\xa4\x3e\x91\xcc\x6d\x6a\x79\xec\x14\x05\x0d\xa0\xb4\xa6\xa7\x0d\x26\xcf\xc9\x8a\x82\x2c\xcb\xd9\xd1\x4a\xbc\x55\xef\x71\xe3\x21\xd9\x70\x1b\x45\xa0\x02\x86\x2a\xc3\x36\x13\x9a\xa6\x61\x61\xe1\x56\x16\x3a\x1b\x7c\xdf\x37\x6f\xf3\x86\xb7\x7a\xbe\xe5\xc3\x15\xe5\x68\x85\xbd\x87\xf6\xf2\x2f\xbe\xe3\x0d\x18\x63\xb8\xf1\xa0\xcc\x15\x38\x77\xe1\x0c\x3f\xf1\xc1\x4f\xa2\x4c\xce\xed\x77\x2f\x73\xf2\xa9\x4f\xf0\x99\x8b\x77\xf2\xba\x3b\x2a\xa9\x8c\x4d\xa7\x3a\x4e\xc2\x28\x73\x15\xf9\x7d\x1d\x95\x97\x18\x3f\x16\xf5\x32\x9e\x48\xfc\x9c\x19\xda\xae\xda\x95\x7e\x29\x1b\x1f\x99\x4e\xd3\xc9\x4d\xce\x5e\xed\x5d\xa4\x6b\x11\x2c\x41\xd4\xac\x12\x0a\x3d\x25\xa7\x72\x32\x33\x07\x60\xb6\x43\x37\x8f\x25\x65\x8a\x69\xe5\x4e\xf2\xde\x6b\x1f\x58\x5b\xec\xd1\x78\x47\x63\x03\xc3\xf1\x84\xb9\x5e\x37\x42\xbb\x53\x04\x30\x85\xa8\xd6\x07\xb6\xb6\x2b\x56\x17\xbb\x34\xd6\xd3\xc4\x4e\xe1\xc6\x09\x43\x68\x08\x81\x71\x93\x1c\xca\x38\x48\xc2\x4c\x7d\x9e\xdc\xc4\x19\xc3\x59\x86\xf2\x81\x26\x14\x57\x94\x55\x5f\xa1\x01\x9c\xf2\x16\xc0\x47\x2c\x3b\x53\x0a\xa3\x04\x48\x28\x8b\x1c\x93\x17\x14\x65\x8f\x4e\x7f\x81\xee\xdc\xa2\x98\x80\xde\x1c\xa6\xe8\x60\x22\x63\x91\xab\xc6\x18\xd7\xd0\x51\x52\xf0\x3b\xb8\xbc\xcb\x38\xdf\x6d\x55\xe0\x7b\x7f\xf2\x27\xd8\x36\x6b\xdc\xf5\x9a\x37\xf3\x6d\x5f\x5d\x72\x61\x6b\x91\x2f\x7c\xfe\x0c\x8d\xdd\xe2\xc8\xc1\x8a\x4f\x3f\xf9\x71\xce\x5d\x78\x9e\x3d\x4b\x4b\xdc\x7f\x6c\x99\xe0\x2d\x0a\xcf\xf1\xd7\xde\x88\xbf\xf8\x04\x27\x1f\x7b\x9c\x71\xf4\x4f\x14\x81\x7e\xb7\x47\x9e\xcb\x18\xb5\x64\x0f\xb3\x18\x9a\x79\x2f\xf6\x38\x65\xf6\x12\xe1\x32\xc8\x03\x74\x21\xc5\xed\x5c\x61\x37\x53\xc8\x98\xb4\xc4\x6c\x9d\x7e\xf2\xf7\x5b\xd6\xef\xe8\x95\x87\x10\x67\x0d\x30\xf5\xd2\x0d\x92\x69\x54\x48\xcd\x6b\xf2\xc6\xb7\x46\x0d\xd6\x46\xb0\xa8\x53\x02\x71\xf3\xa3\x34\x05\x12\x94\x2c\xf7\xbe\xd0\x2f\xda\xfb\x94\xeb\xa4\xb9\x41\x72\xc7\xc6\x4c\x05\x46\x84\x67\x46\x1b\x58\xd1\x36\xb5\xb5\xa2\x8d\xbc\xbf\x42\x00\xae\xd0\x00\x1d\x83\x03\x85\x72\x12\x4b\xb7\x93\x44\x51\x38\x0f\xc6\x88\x9d\xcf\x95\x78\xac\x75\x39\x41\x65\x03\x21\x79\x88\x9e\x28\x75\x85\x46\x36\xbf\x28\x20\xd4\x50\x55\x13\x61\xe5\x00\xbe\xeb\x5b\xdf\xcb\xf3\x2f\x9d\x00\xa0\xf8\xc2\xcf\xb1\xb1\xfa\xcf\xa8\x3f\x57\xf2\x9d\xdf\xf6\x93\xbc\xed\x1b\x6e\xe2\xd8\xc1\x1b\xb9\xe7\xf8\x3d\x8c\xab\x11\xb9\xf6\x54\x8d\xc3\x64\x12\x71\xdc\xff\xf7\x5e\x83\x77\x36\x7a\xdf\x72\xcf\xd6\xca\x1c\x5d\xa3\x84\x54\x51\xfa\xf4\xa6\x60\x4a\x93\x52\xab\x48\x53\x67\x16\xc3\xc0\xed\xa1\xa3\x2c\x14\xa5\x11\x06\xee\x36\x3f\x3f\x83\xa4\x75\xf3\xd4\x1b\xa1\xa6\x1b\xa4\x20\xcf\x0c\xae\x71\x2d\x29\x96\x42\x1a\x3a\x6d\x8b\x05\x44\xff\x21\x7a\x06\x2e\x04\xbc\x25\x92\x4d\xc7\xcd\x89\xce\x9d\x0e\xb1\xb1\xb3\x75\x28\x43\x82\x87\x24\xea\x0b\xc8\x48\x1b\x1d\x84\xbc\x45\x6b\x8c\xd6\x71\x3a\x79\x94\x98\x16\xc5\x9c\x81\x95\x93\xb0\x06\x85\xd6\x62\xda\xbc\x0d\x94\xba\x9e\x1d\xd4\x74\x55\x49\x98\xaf\x2d\x78\x7c\xfc\x69\xe2\xc7\x17\x48\x55\x9e\x8e\x47\xe3\x95\x21\x68\xe1\x0c\xce\xe2\xc6\x8a\x23\xe2\xc1\xd6\xd1\x59\x01\xd3\x93\xd6\xa4\x3f\x7c\xdf\xbf\xe5\xb3\x9b\x4f\xf0\xe8\xe9\x4f\xf0\x27\x9f\xfe\x08\x37\x1f\xbc\x8b\x1f\xff\xa9\xef\xe1\x07\xbf\xeb\x47\x38\xb2\xf3\x61\xdc\xbd\xc7\xb8\xe9\x86\xfd\xdc\x75\xfc\xad\xbc\xfb\x1f\x7e\x13\x79\x9e\xb1\xb3\xbb\xcd\xfd\xc7\x0f\xc5\xe4\x8b\x70\xec\x40\xc0\xe4\x79\x5b\xd0\x91\xc0\x15\x63\x64\xb6\x41\xde\x3a\x79\xe9\x69\xc6\x0a\x5a\x9f\x1c\xb6\xc0\xd8\x09\x9e\x5e\x16\x22\xb0\xe3\xda\x45\x5a\xd5\xa9\xad\xf5\x21\x90\xe5\xb3\x5e\x7e\x44\xee\x02\xa0\x34\x93\x49\xcd\xc4\xba\xd8\x03\x29\x1b\x6b\x6d\x88\x74\x6d\x41\xca\xbf\xe2\xd6\xa0\x68\x4b\xb8\xdb\x01\xcf\x2a\x32\x76\xc4\xdf\xb3\x29\x5c\x8d\x1b\xde\xf6\x07\xc4\x3f\xb3\xf6\xd2\x95\x54\xc7\xca\xac\xe0\xdc\xb4\xa2\x18\x11\xee\xda\x0b\x7b\xcb\xb8\xf6\x54\x6e\x3a\x51\xc4\x46\xf3\x63\x6d\xd4\x74\xaf\xa6\x01\x1a\x6b\x9c\xd5\x9e\x4e\x9a\x81\xa2\x84\xc2\x24\xcf\xa6\xa0\x86\xd4\xb8\xe9\x76\x50\xa1\xd0\xc6\xc4\xd2\x30\x9f\xbe\xf7\x54\x0d\x6c\x0e\x1d\x37\x00\xff\xe3\x57\x3f\xc2\x63\xbf\xf9\x11\x26\x6f\xb9\x97\x67\x1f\x7b\x8a\xc7\x1f\x7d\x9a\x7f\xf3\xaf\xfe\x23\xdf\xfb\x9d\xff\x92\x39\x7d\x89\xde\xde\xd7\x03\xef\x00\xe0\xe2\xce\x69\xea\xb1\xe5\xe8\xc1\x5b\x01\xc7\xd7\x3e\x30\xe2\x63\x8f\x5f\xa2\x30\x2a\xf6\xd7\xc5\x40\xc3\x4d\x93\x22\x89\xd4\x39\xd5\x6c\x04\x24\x2e\xaf\x52\x1c\x1c\x57\x20\xf1\x19\xc8\x93\xae\x9b\xc0\xf6\x70\xc2\xfe\x25\xc9\xf7\x13\xa4\x17\x32\x65\xf5\x12\xf5\x4b\x86\x6a\x5b\xc6\x94\x52\x14\x79\xc6\x70\x3c\x41\xe7\x19\x93\xd8\xb2\xe5\xa3\xa9\x49\x20\x4c\x4f\x2b\x9a\x20\xa6\xa7\xd0\x8a\x89\x0f\xed\xf0\xe8\x44\xf2\x98\x29\x41\xfc\x40\xfa\x14\x94\x9a\x62\x16\xe2\x7c\xc6\x22\x95\x6c\xa6\xad\xdc\x07\x94\x9e\x3a\xea\x59\x0a\xf0\xe2\xfe\x94\x11\x5c\x4a\xa3\x69\xb2\x88\xdd\xe8\x4c\x24\x6d\xe2\xcc\x15\x1a\xe0\xea\x8a\x20\x27\xa8\x52\x2c\x72\x30\x8a\x30\xc3\x66\x9c\x19\xd1\x04\x4d\x48\x55\x40\x96\xba\x6e\x70\x4d\x23\x35\x82\xde\xd1\xd4\x8d\x40\x0d\x0a\xf2\x95\x1e\x07\xef\x3e\xc6\x4f\x3d\xfc\x30\x3b\xeb\x97\x38\xb3\x71\x91\x9f\x3d\xe3\xf8\xed\x5f\xfe\x0f\x7c\xf3\x8f\xfc\x2c\xab\xab\x77\x00\x13\x86\x93\x67\xd8\x19\x36\x58\x0b\xab\x4b\x6b\x94\x0b\x7b\x00\x18\xd9\x8a\xbc\xdf\xc5\xab\x8c\xc6\x82\x37\x21\x0e\xac\xb8\x92\x86\xb6\xb2\x52\xf2\xec\x09\x29\xbb\x4a\xe3\x44\xc5\xa6\x72\xaf\xd9\x30\x29\xd9\xda\x6e\x47\x93\x67\xbd\x76\x0c\x2b\xd1\x94\x04\x33\x0d\x23\x43\x00\xa7\x42\x6b\x54\xaa\xba\x21\xd7\x9a\x5e\xb7\x24\x04\x51\xbd\xc9\x27\xb0\x61\xea\x3f\x34\x61\x1a\x41\x40\xe2\xec\x9d\x9a\x88\x44\x2f\xdf\x29\xb2\x69\xd9\x78\x72\x30\x48\xdc\x83\xd2\x5c\x4a\xa3\xe8\xe6\x1a\x1b\xd3\xcf\x1a\xd1\x58\x5a\xc3\xb0\x92\x43\xb7\xd8\xeb\x30\xae\x1b\x46\x55\xcd\xbe\x85\x3e\xb5\x73\x2d\x24\xae\x94\x64\x9d\x85\xf5\x4d\x5f\x5b\x00\xca\xdc\x59\xa5\x24\xff\x8d\x23\x34\xe2\xc0\xb4\x4f\xba\xb6\x1e\xf0\x32\x4b\x37\x06\x8a\x4d\x4c\x15\x87\x20\x51\x80\xb7\x35\xd9\xfe\x3d\xec\xbb\xef\xcb\xb9\xf5\x8d\x6f\xe6\xae\x07\x1f\xe4\xf8\xdd\x77\xb3\xb4\xb4\x04\xc0\x1d\x27\x3e\xc2\x4d\xb7\xbf\x6d\xe6\x53\x3b\xf4\x3b\xb7\xd2\xef\x70\xc5\x6a\xf0\x3c\x73\xe9\x1c\x3b\xe7\x37\xc8\xe3\xb8\x5a\x94\x0c\x52\x6c\xa2\x9a\xd7\xf1\xe9\x16\x33\xfc\xf8\xb3\x04\x8c\xf2\xa8\xa7\x82\x92\xd8\xbb\x2a\x6b\x5b\x16\xd0\x24\x4c\xd1\xf4\x0b\xac\xea\xa0\x98\xa2\xc5\x2d\x87\x8f\xcc\xcd\x10\x9b\xee\x1a\x08\x58\x8c\xd1\xa2\xe2\x93\xe3\xa7\xa7\x0d\x1f\xa9\x91\xd4\x32\x75\xd0\x82\x0b\x60\xe2\x26\xc6\x08\x26\x21\x8d\x8d\x13\xc2\x28\xad\x54\x1c\x58\x25\x5a\x85\x20\x9d\x44\x01\xb1\xd9\x63\xe7\x63\x58\x19\xff\x76\x32\x02\x4e\xd8\x4e\x82\x44\x42\x32\xed\x21\x48\x7e\x20\x23\xf8\x10\x94\x75\x9e\x22\x6f\xae\x6d\x02\x0a\xea\x89\xc4\x97\x3a\x58\x5c\x48\x9b\x2f\x31\x71\xe4\x9c\xf3\x82\x92\x11\x02\x5a\x09\x83\x28\x21\xb4\x84\xc7\x66\x71\x8e\x85\x43\xb7\xd1\xdf\x7f\x90\xbc\x19\x71\xf1\x89\x4f\xf3\xf1\x4f\xfd\x05\xaa\xc8\x59\xca\x1b\x9e\x33\x3b\x1c\x3e\x75\x9e\xea\x9e\x77\xe0\x6e\xb8\xe1\xaf\x8d\x93\xba\x64\xd7\x39\xf5\xd8\xe7\x38\x7f\xe2\x79\x72\xdd\xe7\x35\xa6\xc3\xcd\xcb\xf3\x7c\x70\x67\x11\x8d\x96\x32\xaa\x18\x86\x25\xa9\x46\x4d\x69\xd4\x8b\xd4\x25\x9b\x6c\xad\x8e\xf0\xb5\x92\x29\x9e\x46\x2b\x94\x92\xa2\xce\xd1\xb8\x89\x04\x11\x79\x7b\x82\x8d\x8e\x70\x46\x88\xaf\x08\x8a\xe9\xa4\x5a\xa2\x54\xa4\xc3\x9a\xa1\xa8\x89\x93\xb9\x62\xc2\xc8\x25\x9b\x1e\xef\xc1\x47\x0a\xd8\x4c\x81\xce\xe4\xe2\x01\x61\xf2\x18\x4e\x6a\x96\xe7\x4b\xac\xa3\xcd\x51\xa4\x0e\xa1\x90\xe0\xe6\x16\x6f\x8c\x9b\x76\x85\xd6\x89\x33\x83\x2c\xf4\x8b\x1c\x5f\x88\xa0\xb7\x0a\xd2\x10\x9c\x0b\x41\xab\x00\xc1\x2b\xe7\xc2\xb5\xb3\x81\x8d\x57\x43\xa5\xbd\x13\xbb\xe4\xbc\x0f\x3a\x0b\x2e\x50\x47\xe0\x43\x86\x2a\x87\xd6\x49\x19\x57\x2e\xa6\x18\xa3\x10\x84\x40\xe8\xf4\x69\xc6\x3b\xac\xff\xe1\xc7\xd8\xfc\xc0\xcb\xec\x01\x8e\x01\xc7\x81\xe2\xd7\x7f\x99\x37\xbc\xe1\x3b\x59\x7e\xe4\x37\xd9\xfa\x86\xdb\xf9\x54\xb8\x81\x97\x1e\xbe\x9b\xc1\x4e\x45\x7d\xf9\x22\x6e\x73\x48\xd8\x1a\xd3\x73\x25\xb7\x1d\x3f\xce\xf1\xdb\x6f\xc7\xdf\x78\x98\xbc\x5f\xf0\x9e\x95\x11\xbf\xb1\x3d\xdf\xa6\x3a\x33\x62\xbb\x95\x62\x66\x8a\x56\x6a\xf3\x96\x47\xec\x08\x74\x94\xc6\x46\x7e\x9d\x10\x68\xbb\x67\x14\xd0\x29\x8b\xf6\x7d\x52\x47\x30\xf5\xc1\x95\x9a\xa9\xd2\x0d\xb4\x0e\x5c\x32\x21\xc9\xb7\x48\xaa\x3c\x8d\x72\x51\x40\xa9\x03\x5e\xe9\x88\x05\xc8\x35\x3a\x4a\x04\xc3\x7b\x4f\x51\xc8\x20\xea\xa6\xb1\xf4\x3a\x39\x8d\xa5\x9d\x18\x0e\x89\x99\x5c\x9c\xd4\x3a\x48\x59\x1b\x2e\xa0\x8c\x8c\x87\x49\x1b\x9f\xaa\x86\xad\x0d\xe4\x89\x2c\x34\xd0\x12\x4d\x88\xab\x13\x94\xd2\xc1\xc5\xa6\xb2\x2c\xa7\x69\x89\xa2\xff\x9a\x00\x94\x5e\xed\xa2\xec\x20\xcf\xf3\x6e\xb0\x9a\x49\x40\x79\x2d\x6a\x77\x56\x95\xa6\xef\x93\x24\xc6\xcf\x95\x87\x6e\x3d\x97\x1f\x79\x8c\xfd\xc0\x7d\xc0\x03\xc0\x57\x2d\x40\x6f\x1f\xf0\xe7\x1f\x82\x7f\xfc\x4f\xe0\xeb\xbe\x97\xb9\x9b\xef\xe4\xc1\x7b\xdf\xc4\xb3\x7f\xf9\x34\x67\xe2\xfb\x8d\x52\x1c\xea\x76\x39\x7e\x7c\x85\xbd\x73\xbb\xb0\x30\x62\x18\xc6\x9c\x1c\x94\xdc\xb4\x00\x1d\xd3\xa7\x41\x42\xb3\x36\x2c\x8b\x47\x23\xa1\x67\x2e\xda\xbc\x5c\x45\x9a\x95\x90\x92\x32\x69\x83\x13\xec\x4a\xeb\xa1\x7b\x54\xcb\x03\x3c\xa5\xc6\x8d\x75\xf4\xc8\x03\x4d\x4e\x95\x42\x34\x82\x8f\x05\x21\xe9\x7a\x04\xa9\xfe\x1d\x54\x1e\x1f\x1c\xdd\x42\x6a\x08\x82\x0d\x92\x24\x8a\x97\xed\x14\x19\x97\x36\x77\xd8\xbb\x34\x4f\xc8\x34\xd6\x7a\x12\xa7\x73\x32\x41\x2e\x6a\x03\xa5\x54\x3b\x25\x0c\x23\xe1\xab\xf3\x91\xa8\x2a\x39\xbc\x01\x9a\x60\xc9\x7c\x16\xfd\x23\x5a\xf5\x94\xaa\x8b\x09\x2a\x28\x82\x0f\xde\x59\xe7\xea\xad\x6b\x0a\x40\xb5\x7d\x7e\x93\xc5\x5b\x77\x9b\x30\xb7\x68\xb4\xf5\x26\xcc\x34\x35\x86\x58\xc9\x1a\x3c\xce\x35\xd8\xa6\xc1\x39\x4b\x70\x56\x42\x13\xef\xe9\xb9\x86\x3b\xd7\xcf\x72\x4f\x06\xb7\xde\xb1\x9f\xb5\x63\x37\x70\xf8\xa1\xb7\x52\xbf\xf8\x34\xf5\x87\x3f\x4c\xf3\x8b\x7f\xc6\xc2\xc5\x3b\xc9\x7e\xe3\x09\xd4\x5d\xaf\x67\xdf\x0b\x4f\xb2\xef\xe8\xdd\x34\xcb\x90\x1f\x99\x87\x60\xd1\x45\x06\x73\x01\x2e\x9f\x87\xd3\x73\x18\xdf\x25\x2c\x68\x5e\x56\xf0\xad\x9d\x4b\xfc\xca\x64\x7f\x54\x6f\xd3\x09\x1a\x57\xb6\x54\x4d\xf9\xf9\xa6\x45\x21\x91\x90\x29\xf6\x6a\x19\x63\xa4\xe2\x09\x85\xc9\x15\x38\x30\xca\xe0\x99\xce\xe4\x0b\x33\x76\x5b\xc5\xbf\xbf\xcd\xac\xc1\xb4\x98\x23\xc6\xfc\x75\xe3\xe9\x15\x86\xb2\x54\x04\x2b\x9e\x73\x26\x92\x48\x08\xd2\xac\x19\x3c\xf4\xca\x9c\x7e\xb7\x43\xe3\x3c\x2e\xc6\xdb\x2a\xe6\xea\xb5\x12\x8f\x5f\xb5\xc1\xe7\xf4\x24\x9b\xd4\xaa\x4e\x9a\x05\x90\x38\x0c\x14\x5d\xd2\xa0\xaa\xd0\x3e\x97\x19\x54\x0b\x47\xc8\x74\xc8\xd0\x7e\x78\x79\xbc\xb5\x7d\x6d\x01\xf8\xe1\x1f\xfe\x99\xdd\xef\xfe\xb9\xbb\x4f\xdb\xde\xc1\xc3\x26\x78\xdd\xcd\x15\x0d\x4e\x5a\x05\x9c\xc7\x2b\x47\xa6\x23\x82\x16\x64\xf3\xbd\xb7\xbc\xf9\xd9\x3f\x65\xcf\xee\x45\x38\x3f\xc6\xbc\xe3\x0d\xf4\xde\xf9\x1e\xb2\xc5\x45\xea\x85\x05\x36\x97\x97\xa9\x1e\x7f\x94\xfa\x12\xa8\x43\x19\xe1\x77\x4e\x70\xe0\xfd\x3f\x46\xf6\x8d\x3f\x80\xea\xec\xa7\xf9\xd8\xaf\x71\xf9\xed\xef\xa1\x9c\x1b\x63\x8a\x02\xc6\x35\x7a\xfb\x12\xf3\x2f\x6d\xd2\xbd\xbc\x83\x1f\x06\x9a\xfd\x03\xd6\x57\xf6\x71\xd7\xe2\x02\xe5\xd2\x0d\x2d\x42\x96\x06\x32\xe1\xe5\x21\x86\x90\xc0\x1e\x5a\x42\x47\x13\x27\x66\x59\x17\xbb\x79\xb5\xc2\x3a\x47\x5e\x14\x82\x14\x6a\x5a\x34\x24\x8b\x8f\x23\x20\x44\x18\x2e\x4c\xeb\x06\x93\x24\xc4\xc3\x48\x02\x09\xd2\x01\x99\xeb\x29\x82\x57\x0c\xab\x9a\xb2\xcc\xe2\x7f\x4b\x02\x4b\xf9\xc0\x7c\x9e\xb5\x94\xb0\xfd\x5e\x0f\x1f\x3c\x3a\xe6\x09\xb4\xd6\xf4\x8a\x69\x14\xa1\x4d\x3c\xb9\xad\xa0\x3b\x42\xec\x4c\x42\x21\xa3\x14\x33\x8d\x8a\xed\xe3\x46\x41\x51\x42\xa6\x35\x55\x6d\x31\xb9\xe4\x43\x0a\xa3\x09\xde\x51\x79\xa5\x2a\xd5\xc7\x57\x67\xcf\xbe\xef\xc7\xde\x77\xe9\x9a\x02\x00\x74\x76\xbe\xf0\xb1\x0f\xf8\xe3\xf3\x77\x2a\xa5\xb3\xe0\x1b\x1d\x9c\xd7\xce\x5a\x13\x82\x57\xc1\x59\x5c\x3d\xa1\x1a\xed\xaa\xf1\x60\x87\xc9\xee\x26\xaf\xfd\xe3\x5f\x34\x97\x6a\xc7\x48\x41\x47\x43\xff\xb9\xe7\x51\x4f\x7e\x0a\x37\x1a\xb2\xbd\xfe\x22\x2f\x7d\xe1\xf3\x98\x4b\x43\xb2\xbe\x46\xbb\x8c\xaa\xb0\x9c\xfe\x81\x9f\x61\xdf\x7d\x6f\x97\xc4\xcb\xfc\x11\x2e\x2a\x70\x67\x2d\x9d\xc3\x99\x18\x69\x0d\x26\x58\xd6\x86\x2f\xd1\xbd\x38\xe2\xdc\x91\x75\xce\x1c\xb8\x89\x83\x47\x0e\x70\x76\x6b\xa5\x76\xe8\x56\x65\xa6\xf0\x4c\x2b\x25\x35\x7b\x70\x85\x1a\xd7\x4c\xa7\x73\x4f\x47\xac\xf9\x30\x13\x45\xb6\x2b\x9a\xec\x56\x73\x08\xf9\x95\xd0\xb5\x05\x3f\xc5\xe9\x83\x32\x53\xbc\x35\xfe\xd0\x45\x41\xb1\xce\x93\x49\x1a\x50\xa1\x74\x1b\xad\x68\xa5\x22\x52\xa8\x5a\x41\x92\x08\x04\x54\xfc\xbd\x36\xb1\xa4\x75\x8c\x3e\x88\xa9\xea\xe9\x48\xb9\x44\x14\x95\x0c\x60\x4b\x30\xd5\x8a\x68\x8c\xfb\x8d\x0e\x68\xed\x44\x9a\x4d\x50\x7a\xc3\xe5\xcf\xfd\xd1\xaf\x02\x5d\x98\x32\xfb\xcf\x3e\x06\x0d\x2c\x00\x07\x81\x43\xf1\xeb\x41\xe0\x30\x70\x00\x61\xf4\x33\x51\x68\x0a\xa0\x78\xb8\xa7\x6f\xf7\x36\x74\x3b\xc0\x82\xd6\xcc\x19\x4d\xaf\x69\xe8\xd4\xed\xdf\x41\x96\x83\xc9\x35\xb9\x11\x1f\x39\x28\x45\xd8\xaa\x39\xf2\xfd\xdf\x8e\xb9\xfb\x21\xb4\xd6\xec\xbe\xef\x47\xd9\x7d\x76\x9d\xee\x6a\x97\x60\x1d\x4d\xf0\xd8\xa0\xe8\xa3\xe8\x05\xcb\x89\x10\x18\x1c\xbe\x8d\x1f\xba\xef\x36\x6e\xfb\xd0\x47\x3e\xfa\xd7\xb7\xee\xff\xbb\x15\x5e\xe1\xfb\x70\xd5\xef\x4c\x90\x6c\xfc\x39\x60\x1d\x78\x29\x7e\x3d\x07\x6c\x12\xf3\x7c\xaf\x34\xd5\x71\x0c\x5c\x9e\xb9\xc8\x56\x7c\x73\x0f\xa1\xf4\xee\x23\xc2\xd0\xff\x6c\x15\x86\xf3\x4a\xdd\xdf\x27\x74\xbb\xde\x33\xe7\x3c\x1d\xa5\xc8\x8b\x28\x21\x4a\x48\x0d\xb5\x85\xac\x91\x19\x78\x06\x2f\x59\xc2\xa7\x4e\xd0\xed\x1f\xc0\x18\xc3\x70\x75\x8d\x93\xcf\xae\xb3\x38\xae\xf0\x4d\xa0\x06\x08\x8a\x9e\xf2\xec\x68\xc5\x39\x17\x78\xe7\xe9\xa7\xf9\x68\x31\x3e\x09\x7c\xe2\xff\xfc\xb9\xfd\x9d\x59\x57\x6f\xf6\x2b\x09\x84\x07\x06\xc0\x06\xb2\xe1\x1b\xf1\xdf\x29\x82\x15\xf7\x70\xe6\x8d\x86\x96\xb3\x9d\x2e\x69\xa6\x3b\xcc\x23\x9b\xde\x03\x96\x10\x42\xff\x15\x60\x71\xe6\xff\x3a\x46\xb1\x00\x14\x85\xa2\x93\x6b\x55\x02\xda\x2b\x3a\x9d\x40\x91\x6b\x72\x01\xd3\x42\x40\xab\x4e\x55\x07\xd3\x3f\xb0\xa2\xe6\x0e\x1f\xa0\x19\x8d\x43\x75\xe1\xa2\x1f\x5d\xda\x75\x45\x4e\x85\xd2\x8d\xd6\x8c\x26\x21\xec\x5a\xb8\xb4\xa2\xd5\xba\xb6\xbc\xec\xf0\x7f\x75\xaa\xe1\x14\xd3\x9e\xd5\xeb\x4b\xd6\xd5\xc2\x30\xfb\x73\x8b\x1c\xe8\xab\x5f\x13\x62\xd9\xe7\xac\x00\x4c\x0d\x8b\x98\x83\xf4\x2a\x11\x81\x58\x42\x36\x7e\x15\xd8\x1b\xbf\x5f\x40\x04\xa3\x44\x12\x80\x19\x71\xf0\x65\xbc\x96\xbe\xea\xba\x57\xdf\xb4\x8f\xdf\xa7\xaf\x2e\xde\xe0\x2e\xa2\x85\xd6\x81\xb3\xc0\xcb\x88\x14\x0f\xb9\x2e\x00\x7f\xdb\x75\xad\x67\x3c\xfb\xba\xc2\x04\x84\xab\xde\x04\x11\x09\x8d\x3f\x37\xc4\x2c\x2f\x2d\x39\x1a\x63\x44\x6b\x64\x4c\x37\x3f\x09\xc0\xec\x2b\x5d\xeb\xea\xcf\xf2\x57\xbd\xd2\xe8\x90\x01\x62\x7a\x2e\x22\xaa\x6b\x2b\xfe\x6c\xcc\x75\x01\xf8\x92\xae\x57\x9f\xec\x2c\x2b\xa9\x92\x0a\xd9\x04\x1d\xff\x3d\x40\x36\x3f\x09\xc4\xd5\xaf\xab\x85\x60\x56\x00\x66\x4f\xbc\x9f\xf9\x9a\x3e\x27\x69\x81\xed\xf8\x9a\x30\x5b\xa0\x74\x7d\x7d\xc9\xd6\xdf\x24\x00\xe9\x94\x3a\x64\x63\x92\x46\x18\x31\x55\xfb\xd7\xda\xf4\x44\x8c\x39\xab\x01\xda\x9c\x0b\xd7\xd6\x00\x49\x08\x26\xf1\x73\x46\xf1\xfb\x84\x7c\x5e\x5f\x5f\xc2\xf5\xbf\xa3\x01\xd2\x89\xad\xb9\x52\xdd\xcf\xda\xfc\xd9\x4d\x7f\xa5\xcd\x9f\xbd\xe6\xac\x10\xcc\x0a\x43\x12\x82\xe6\xaa\xd7\xf5\xd3\xff\xff\x60\xbd\x02\x1c\xf2\xaa\xbf\x37\xbb\xb1\xb3\xf6\x7d\xd6\xd9\xbb\xfa\x67\xaf\xf4\x39\xb3\xb1\xeb\xb5\x84\x21\x99\x85\xf4\x9a\x7d\xdf\xf5\xf5\x25\x5a\x7f\x5b\x01\xf8\x9b\xde\x3b\x7b\xc2\xaf\xf5\xf3\xab\xd7\x2b\x01\x18\x57\x78\xa8\xaf\xf0\xff\xd7\xd7\xf5\x75\x7d\x5d\x5f\xd7\xd7\xf5\x75\x7d\x5d\x5f\xd7\xd7\xf5\x75\x7d\x5d\x5f\xd7\xd7\xff\xd5\xfa\x5f\x4d\xb7\x25\xe4\xb1\xe2\xc7\xa0\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\x01\x00\x00\xff\xff\x48\x81\xdb\x60\xef\x59\x00\x00") func dataVgcSonicPngBytes() ([]byte, error) { return bindataRead( _dataVgcSonicPng, "data/VGC Sonic.png", ) } func dataVgcSonicPng() (*asset, error) { bytes, err := dataVgcSonicPngBytes() if err != nil { return nil, err } info := bindataFileInfo{name: "data/VGC Sonic.png", size: 23023, mode: os.FileMode(436), modTime: time.Unix(1571405971, 0)} a := &asset{bytes: bytes, info: info} return a, nil } // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. func Asset(name string) ([]byte, error) { cannonicalName := strings.Replace(name, "\\", "/", -1) if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) } return a.bytes, nil } return nil, fmt.Errorf("Asset %s not found", name) } // MustAsset is like Asset but panics when Asset would return an error. // It simplifies safe initialization of global variables. func MustAsset(name string) []byte { a, err := Asset(name) if err != nil { panic("asset: Asset(" + name + "): " + err.Error()) } return a } // AssetInfo loads and returns the asset info for the given name. // It returns an error if the asset could not be found or // could not be loaded. func AssetInfo(name string) (os.FileInfo, error) { cannonicalName := strings.Replace(name, "\\", "/", -1) if f, ok := _bindata[cannonicalName]; ok { a, err := f() if err != nil { return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) } return a.info, nil } return nil, fmt.Errorf("AssetInfo %s not found", name) } // AssetNames returns the names of the assets. func AssetNames() []string { names := make([]string, 0, len(_bindata)) for name := range _bindata { names = append(names, name) } return names } // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "data/VGC Sonic.png": dataVgcSonicPng, } // AssetDir returns the file names below a certain // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: // data/ // foo.txt // img/ // a.png // b.png // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error // AssetDir("") will return []string{"data"}. func AssetDir(name string) ([]string, error) { node := _bintree if len(name) != 0 { cannonicalName := strings.Replace(name, "\\", "/", -1) pathList := strings.Split(cannonicalName, "/") for _, p := range pathList { node = node.Children[p] if node == nil { return nil, fmt.Errorf("Asset %s not found", name) } } } if node.Func != nil { return nil, fmt.Errorf("Asset %s not found", name) } rv := make([]string, 0, len(node.Children)) for childName := range node.Children { rv = append(rv, childName) } return rv, nil } type bintree struct { Func func() (*asset, error) Children map[string]*bintree } var _bintree = &bintree{nil, map[string]*bintree{ "data": &bintree{nil, map[string]*bintree{ "VGC Sonic.png": &bintree{dataVgcSonicPng, map[string]*bintree{}}, }}, }} // RestoreAsset restores an asset under the given directory func RestoreAsset(dir, name string) error { data, err := Asset(name) if err != nil { return err } info, err := AssetInfo(name) if err != nil { return err } err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) if err != nil { return err } err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) if err != nil { return err } err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) if err != nil { return err } return nil } // RestoreAssets restores an asset under the given directory recursively func RestoreAssets(dir, name string) error { children, err := AssetDir(name) // File if err != nil { return RestoreAsset(dir, name) } // Dir for _, child := range children { err = RestoreAssets(dir, filepath.Join(name, child)) if err != nil { return err } } return nil } func _filePath(dir, name string) string { cannonicalName := strings.Replace(name, "\\", "/", -1) return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } dms-1.1.0/data/000077500000000000000000000000001356070553200132135ustar00rootroot00000000000000dms-1.1.0/data/VGC Sonic.png000066400000000000000000000547571356070553200154160ustar00rootroot00000000000000‰PNG  IHDR€€Ã>aË IDATxœì½w¼e×UçùÝáœsï}±r•ª¤*+Ë NÂg[Ø4¶›h 4 ˜OÓMcššîLjÃÌ==&Ž¡ ÓccÀÛÈX–,I–­P–%U)TïÕK7œsvXóÇÞç¾'xlºÿÚúÔGïÝwï¹÷ìµöZ¿õ[áÂÅuq]\×Åuq]\×Åuq]\×Åuq]\×Åuq]\×Åuq]\×Åuq]\×Åuq]\×Åuq]\ÿp–úû¼øö»ÿòMgÇóÿ¬‰êòÂ%Š­ðQЀÖbµ’M¤vž²(Ñ 1Jƒc4!ÄéÇS D$}Z¥"E0D@¥µ4îÝ33üÍ›ozõþ¾óe}Ù ðžÞñkÇÇ;¿Z1(E$`m‰°ù:kM~Ûˆo#Ê( Œ&:‡. 1£4‘¤ IUQÑ:ŸЪ» Maüh¯=ú=ozå›þß/÷Þþ1­/KþàCú¶§â5ÿÇB1*"@a´VVA/‚÷‚5é$h`ä ƒ‚ÖG•H)BŒ‚RI)¢ÓJz|ëêþ,[þÞx‹ m}mõð+¿úµo¹ûËݘ,K©/¸õÖß›9Ùìþ·}£1&Oh\¤ éd:/x0èk”VIȃ²I¦„(‚‹‘“—ü_`u~ƒ¼DdúO©¤Á6ë…1зeû½ããí?ü÷Ø—4ëKV€ÓÍà˜™Ë,Ž D!ÉM1[ÓçIV€ÕQ‹Ï>½v‘6 Á‡H¯Ðt¢²y¢µR(¥0|"›f^ëô!YB~mí„ ©ÕÜóÞýîw—_þÖüãX_²TÚ-DÁ³ Îl>‰$t­ °:™ì¨ÐJa¢Wè%Dhý¦Y’܇ˆ$K ÐiG•ßÀÚl5²‹ð’,€Õ “ÝAã#(Ýcà‹¬/Y¯®eÑJ¢àB@YZ*æ’ =KiÎE|Œxl'ô !³µ0lº •¾ð!]Óy!HÒŒBk ­ˆÚ˜J²«(¬Bk=³ÐÖƒÿ›ôy}É 0bÇë{UA ~Ub¤ÐšžQDé.(À‹Bɶ -P ÉutB² Ç-‹«° DƒDAël P(¬„R`UÂ)$4VÊmŠ+ÿûmÕ?Ìõ%)Àû>ô[׬Ë ž  DXT(%¸l¶]”.6GIò÷鱬>€V¥"Æd1|D§ç8Ÿ€apÉJ4M Ĉ‘I›þ&"¨ì6bV*çTÁñaÿÛþìÙ?¨õ%)À)wÙOåÂLAȤõÄÖ>aI Y mš Y¸­p 1âòßCHBŸ´Ž¦(z…ŨäË£>FJ«YÚ˜0¬ViÆMÀ…ˆBâ¦D!Ï ˆœ¾ï>ôgÿâÌÖýÃXÿ¿x€Ï~öO>;ócG'û~d® &/3ylþïiáù4Ú§=-¶Ï Oå°¯[’qó±Bk´ÖÔÎc”BNýÒ¢T™MˆJ¡4S©m‰óÁ/\@” Ù %ð)ÓÓ Y‰6ß9­“2h©Øq”Æèô¸„ˆR­¡uɹm¨Ûˆ”eA`ŒÂ‡@ ‚VLCé§ %m‘ ![͈€5&ããHBíýª¤m=1Õë ÆóÅäÑ>kð-oø–G¿˜l¿¨|ìzãÃëþìܤwh®ŒD\þ´¥U¸ÐéABô†$ ¥w›l•bÜFÊBa­ÂùH¨ 6…Α# cR)‚ð!Q¿FÚ0 C\È¡§RÓÿ§¥q’͇¥˜N²€ÃL•§U)¢Ñ(0 C'$A«„UB³AÒ>¤‹$>Ä*E$)²÷c5 MO L]•5*+›¢uÂ*j)­AˆHHŸµãY´aJ’)À{¡‘ÊjŒ6Bô‘ :¶ã«ŠG¿ý{Þòòe+À½÷þFq׉k?>¶{_XQctLÄ**m8½2d¶_1SZ(ŒBiƒ÷YûM˜…fmR³Ðï‘ÖËôï1)Ëò00;°ô¬¡²Â*ÛR‘A¡1L&­ ´–ˆ|œžú„ˆ qA(¢ñÃdÒÉPA£H!¦ I©†qãÉ:€Ö†É$ ”Ç‹#T¢J¤”öƒÂIÄ{¡* cRH¬”F"¸ ‰Œæ%ãa¦_±2néBãl‘   MUZ|Œ(% ëˆÕвˆLôÜŽcÍÌ·?÷e)Àʨx±³š Ã1J%dƒ@4¬OF 1€‚ÏD%`0 Éa$F¢D’}dÌÀ-:ïÓœªYö©é]‰1B!ŠR"¥*(•¢**ªJS f) M¯h)íˆÆ-(­°*ŸXÐ&ùdïÖ´€ËBH—qJ–h«SFM­ÅVÜbÀhÓùëìÆ$EHù•^„R)|ô´Q¨'ž™AyO¦¼H¶ÊZçYZ¯©£n;çú(ks))¬ÅI¸ð@U$ç •B5Q–×{ñß%ãg(À§¼mVýËš6¶w=ºr½"xˆcÄK¦vÛä c(mPÞûìû#’BF„6:´Dœ”*0qŽÐ4åq‡Ö/am8öž¡4‰M´q‚Ö’½5D¡ìá‹íŒ8ŒRh±*ˆÕ"³HY@ãר¨—PÙoãAé€`|/Îy “¶!CDŠÒ‚†è¾m¨›–è=«yäTï²ÏúZ­I0W ¡2†¢+AÏmGÕ«pæsh™ ¢Aƒ³|ë?OùÊŸ¡šÝEu3%ƒ"0caPà2ôKMe"•m9t™NnȘŒ¦A Ae0Z%BÈ(@€ÉÊí\ ¨ Þg ›£rÒJE|)téS`Ø] À¨dò£O¥Ð†(ÂÒÆ˜ \°&¡}•ÿB)œ¤¬«U/ Ðj@g€[(Cðäý FŽ/o0SYý9Ö×Ïݽ~×WÇwü»#æÝý•Ù•pù;g·]þšm‰ÁCNêˆÎ¼ohê†z2f23×Üs÷çxÿ_~¸ë9¬Øë(Ë%†s'/°|ê$R(¢oÙ 78½´Î/Ü>í4+:Û[h …Ä„b$ƒ‹–öôƒÔG>A£™ÌÝĤ½ŒñÚ,ãW0œ=Èè‘ûÃhñzFgÏ15LÆŽÆ Î9|À'Á¥)MI3öUL,Ù[Z’1Åç1&°ØøtÊ%‚“˜MÒ¬Õ¬ ÛDƒkEAI:ã6Ðú„WbLaoHŒéT ï;Pš\‹‘~Y¤ª¨œW‰Ù²DI˜I"4­P”ç¨ta3‰Ö}F舷ÀüÀbµ¦´±Û¶¯·ñŠïüúçضã}¯’þÞ2°.ù-þ!ƈަ®ñõˆÑhL]OÞñØç€vÚsŒí•¬éYêÒàuÁÚºãÜOP_øW½äFîÞyýoòÖÜ€7¼¶¿ß¸öÜuþk`0ƒ4Ž!44õ$2Ôšµ\(Ö «kšQ¶v{˜ÅfXP„†b®œJ!gšã—7ÖfÊÓÅ8­NJÏSè\ñd´¦4‰ºöBv%éé^RvÒËf´ÐU[ŠP BL$”ß‚*%*)˜ÀƤ¥_Zú…!"S7Õ-«Õ4§MúÜ), ç…ªôT…©Vâö묈5" XhÞøüꄹ^™ÌaÛâë†Ñú+ËË ×W™ŒGLÆ#ð- Ÿ„å,­)žr‹x³LO5pà94W^K»R£–NS„šƒóYE¥&ú˜HšžIT\ƒØw œw=ÿ[øžïÁW¼ ìMà÷³Üð‚÷p×§<˜ t õˆHëÁ5¸±0VŠU-œµŠãűEá³\¶o–Ý;fô fÄÁB­BDIFìu+ô«î¤ª)ó-l¥”xbF]TÓê$c,Îû)š÷žê1U²ŽŠ[®­€:DŒÊUQ’ê+}VÎNñ”J©]¢¿#)”MéóAXE [¹™‚‡Ec•±;ìòéZH Q­N3•enÐC‰!r~mHœŒ†l¬­°¶²Ìd4df¶ÇÒêÖO PhŽž¯hviú7¾ŠB_‰?K½1¤©]׉ʊHÌÎ×µéîµ…b¢vƒ¼l¼äÙ¯…¡…™ 0,…E(4Œl»,9ï¶mQº Äà‰±ÆOÖ¨ñ¬Ç14YäŠ3\ñ¬\²o‘ÅeÍ󞟩ì|Rº²ÕÙAŠÔ–“Ÿö7I2*a­n™«ÊDåƒÛ:Ÿî·«USÝK$+ZvîYjÝ©d3’W”Túæ½`ŒÂj…FÓºÌXfÑÕRv&ñ›.KmQåSH:P«K6F·äƒ+[Í ª—:qüÞEªÂ0Üp´MMÓL˜ŒF 7Ö)LVízì*¨£4MÅéËþ)e<€yìÆ·eh[Á9O¨›?êL?ø–{P>!­¥æÏÕÏãyÿár~jùv~âߘݟžË§ëYôáì¬=Û®Du;ˆƒzq õ „uðc õ²çð©‚Çç¹ü²:¸¿²Áͯ}u:9[„²y™ ê”DP(%»=\fÉÄ'l §4/›~Ÿ$ Ó)W’®?q)êª ;µ21>§·&]D)EÂôz.ÆäŽ2¦H¹MŒ›„Õ´ŒNÒýU¥e¥n ]_¶F› ZÂPDÍÙ\ø.w¯RØ!1â½Ç5-m[Ó65ZIÎø´°q³pÝk︎úÜ´k¦B‰m›wT`¼6‚®`u'—!8˜›‡Ý=X_/ºžŸ|çs8::Á»~rHa÷çùÄã`ÎC} VßÖûО‡j/ Â_Ovêu˜œw¥Ç,.(vœa÷Þ=ºr'{ìCóÛ/ëŒ#ÉßË´¼¬ÛÔ˜##cÉ\>F4*£ôMFÐZ…÷›æ>æè•Ìzwº!a„Ž“ëžk2íN®ÎØÄ‹¤äÐæÏ‘ðCÆpjz¦´Ug á¤ÉHæ‚uέxë‡"²Çgs“ðÀÔÒe@£ŸV¢Ý+3ûPoŸ—\±&¶¢¨d!b6ñ)H†f#í言‡î‡ø ‡&Ìï¨8þX k×ÂM/†á)PŸ„+_Àïýê,¿÷G —ÀÎì‡ê)¨O Tb²JQiJ?ÂÄUœ_amu ŸB•ç9x…åºk±ïÀ%ìØ½‡m;w3·° )úl›ŸÅNO^:í]²P«-ìŸäçÒFOC·-&[+06-ï“°ôò° ';¥P[×t$L’iOJ˜™X•”cyµfaPæâY¦X#)™àTθ:™â…(ˆa¢,Û~¡Ö[ÚÕ˜™<éJ³#x’iÑÚ¢´ÁƒÖ¥SÁ§•&!ÖÅK zxìn¸â9hvéÖ¼@h3o:‚²ž€ÕÛ¸úEþí×òª¯|ZÃéSó ¿|wßeáæWÂêÀŸ€]÷]ÅÕßtŒk/3ð¬Ëaø9`cQ>ëÅ̘!³Å„Þl –-³¬ÓìÑÜxív휥73G¿ß§´vŠÐû•é°?BªfÒùw“7M„ ²Òï¨Í“«ºMŸ–¤ ™£Éq}ç‡eêJ:})Â…¿ÖÑY›(9S\N”T{9SD`¦_NÇçz‹^¡qA²J é´[qG~‘F!±–j²bË8IlWZŸLLx”Ö”ÆÐhƒ±vúO)ƒR©ÖÏÞmœ¹e’f ¶ÁGî‡m·ñäoåàþ׳ù¬!Îð¢ç¼žwüÔi~ägÃèPP(ìV@6&0:ÅkÐ×}…Zafv;ÛÌ„}ýG)÷`0èa«>eUa‹’²ª(ÊŠ™AŸ±7¨¢"¢ˆ(töB2µ…VSÞß­§WXDÒÉER]Í›+YaŠ\¶.ûØj=²BE/4’’`ʘ©Ÿºý×øüX§€ýÒ0iBe 2}¶:d÷qaÜÒ·– ‘Ù™‚à“E2Ö£ÚÕ‘iFö-oùw“ßúà_®ø(”[âH¥@”FÒm-Zk´Î  5Küã_¿e7»®¾ŽŸú±ó0wÌO"ôÌí€[ÿŠm×’£Ÿ|Ûf_Ñé$Ó²U •¶ñCo} xû'¸ïÁ! f ®suH?I`òªo„ÁNð Æ)ç˜Ý{=Ûv^C¯÷s½ˆ.z袠*JŒ-¨ƒÐôS¢Åè²@m¥ÙDarü>ÄzÖNýlR²E«„À'NèO\’I·Fe (ÓMr;)4 Q(ìfi}·ß]_°åñÁK‚\„Më”ßÓ*MS8cÊØ.ö ¬Q4^¡s~:ˆBÀ«!¬Žgí(AO_/ÇQV£rštk<™ÒW*›þ'O08ÖìØ{=7_{=|ÎÕ°­“¡Íüv¸û10æðm?¸EøôñÓ3¤td£RGt'øN«Ê‚ßDóqËëð„’ÔEÝYHÐ  Èúi­¢–ˆw‚5ˆTÓT·!E“@뻬×Í” ¿ð¶¯y[cŒ–e!æ|6O×/I5Å’n°É‰–íó‘²\ ×ëñœäþÛjP é"U« Îþ!þ—obßž×m¹r2ý[59•xk `÷®*ÁjÝKA.ë"út“ ¨kbÚuÇÈxVg .¬M(«Y¼ôè÷f°í Esm;«œ©V[@[««“†mƒ Ø4·"‚“Tp›‚›ä>AèŠäƒ¤xܘDØ„ŠMŠ ¦uŠ©]Nêê_¯©}®Cÿ$üЭÓÉîšv‹t$#«[ØzaPhªBã¼Ã¹@Ô[*¬Éåò\ˆX–¡+ q%øtéôN!¹æ>„€Ä\ôAHõ{!2ªıwgc ÖZ]µ3±x^’ù/çáþûø®ÿùoxÍ[xfý‰¦‹²“…1•”bÏ®>Tj³ÒÂ((-%øZnLœŒi&C†Ãš «cNpâü„Ó+ž¥õÀF;`$—pn©¢^÷Ó¦Óè²PTFOOe§ð‚„Ø\•O´!eàŒRIØ’~9{ã\$¸TÛgài!U u×+…ÚM³¯±ã`:,"CãÜÑõ]9²¨»÷‰Ã‘˜\ÏL¯ *’B·.}ç“â4­[™*€UnMDp>½y×sg$·s‡s-+ëc|ð(IiR$°s¡GIho|É>è)µÐëÁ‰˜ý4¿ý¿~7°›g®ncÀ¡Í `Jˆ¹*4Ôô/fÈ :bÝàÆ £1KK#NžÙàÉcž:Ópr¹ei½eØDœÚÆDd¸¬‰>N4 $NƒÎDç·q‘jKÂ&ÆÈÈ9*'ˆÒ†;/øœµóaKosŸc>™ÝÝf>&%œb²8½ÂNc¢pSøíeó´71q Ýß:à§²RÆØñjŠ%DRö0ʖçj”¦Y¡;’!Ô«^<³ÖBªÉw.&+CÎSÅ0FBL´%JrÍ€Ã{ÏWÌÀ®"±{{·Ã#÷ðÒ¯)€gý-ÂO^é‘ÏŠ}û÷1¿pèO‘á$dð—‹FBºÒ‰:Žcˆ‰ ®UDUÓ2d‚eCJVXW¬/ { a!¢UA1Ø›¬aª ¢—$ÅÞV%D#¹Ù5í‡ÖúiöËIJ°¤Œaz¬°ŠR)ê©ÛÀ ²h^‘ú¢ë8ƒ$X­Å›>¦ä ¤Û#E¡S_E€q똫*q9¨ŽYœ*jЬNýš>$jz3-¡‰DïV§ `´Y#DšZa-4uD‹| ø€ÄTàà}¤W Þ3™L¨ëš·\û‚ËøÂ­GR¢¦y˜ïûÎçæþ7)€‚Ðpíõ/É'P˜›½8ÍÆh5ô$¥uÌyêTž•Ži ¢“¬”¯‡¸ =jj†f}YÛhÙ˜Ô©Èh(æ·QCTC$Ÿ­5:›ÞŽŠ…ÞLWEJ!›Î¬wÝjs¼ß/mîjJ´n ›÷ÝY(¸ÓÞÆNøB²Lý2Õ¸¬8Uaq¦TrÂMÝ€ piÞÆtI)`«ÒÐŽÔd©[OO«5òDÄ­‹Äà:óŸÍ‡ ©´) ©% Z­S“FǨ ‡CF£þNN>ø^¸ôJ8v®Øà^ÿü¿AøÓ·æÜù'8´o;·½ÿwóßjήJº#SæO$Ÿ|Ÿl_pÉ"„†Tøæ h¢opuËh£æÂªçôrËgjŽ<5áÉ£5çÏ5Œ'žP-RÄþÔ¿Çìëc¯ç}èzc>MQRK[WqÓÅêuˆù9qš6ڜ铌Öã\á}H{ì$w8§ºiCL•GlâˆÎïw¿K6í.&ÌÐ)“I±P›“ZBîë·J¡#«S)XqãÚ‰[iÈT “ž¤Ð*¥\ 1ÓÓ 4AÃáápÈûÿð×ÙøÌ»`ï%ðø“üËo=Ä wãߢiýÂzOž¾À§ïüdÒ\o¸÷p€Q²k“¶ù˜ÙÅuhZhÛä Ú h'‰j–€ø–ídƒáƈ¥GW8~x‰3à žZ³tºeÜÂ`73$ôßeh;XÒuuù˜…“wz0Ƙcý£k“Ö§Ù¹|lPZ ¥²0TjwÏL¸6ç!Ì”gœ¶§éÜ ’^ª0ZÑD™¼®}®›–‚ÎÉИHŸ®WR³ŤŠå¬nL fŒ’Ie“¿‘\•(•øs­@k´Jÿ$+ƒ÷BÓ4ŒÇcî¼ç(p ú ñmoº/ÒzÀ“=5ý9榑Ók£GÛt²ñ©T&昭gjY%·ÐŒSTàëC“~v üçW˜Ô#~àû÷sû/ÝÈoüÐ"Ëq•Ó.5•Úy¬L™òô9²Z}` IDAT  ½9‘¬;áÖ$?/™‚K\¾bK¿f{ELÆ1¿>Èæ)6ìu3 ´VTz3'иÞå®™i„…R«©û0MwÝÔSוËVÇIº²¡!?q¥šL`{QOˆ¡L.VQ_ªÄI»Qtª¦Õ (ÍÏ–LÚ0 Ï“°¯°ÿ™w½ïM¼ü…_ÿw ÀT[$ÇúŸpþÉ’êЦÆ×ÝVÐ¥Š”tò.» wžƒ]ðЭ/æßÓ€e¶ÚÍ/|G͹õË-N ETSÓ+0-üCæ6ê–q3&H›Þ¥Í}öýu›š6§“Oº¶´Nø¦»¶äbÏ-ª—4¤:™¦õù}:ª,·ß“,”Õ©,­Ì(´»^g5 Ú¨SÁIêÒÖ(ñ“ÙVÆSøÌgN×1ú‘©buÊf©›+c˜ è÷+¬1h“2‚Û·ŽʲÄÃ5W¤¼ú÷¾ñßóO_·uÿÖu`×Îéχ.Ý Àûn= g'ÐoÁ7')îuŠ‚¤ŸÛp9áÔ$—Br÷ž€ƒpøÏ_Íó}Æã1ÃáÎ1è­ñÈê'OŒXÙL äW1¦þÇsÄ™}lã"3UA‘óLLOæÖÜ~aS©˜HŠÂÔT§x¼«ôí¡Hl77¡êYÈu™!Ó¸[}þ4Ä3š:DÖF©I¤Köˆ­K-øÁÉ4Ý‚Â_×Û‹M ðö·¿=ÝFÌþ%iB”•E Öb‹eRj¥é—ÂJØžÒ¬eÉ-·ÜÀ±Ç¿˜Ø# ÉÁW¿þ«ÒÆßúµ_~å?_€É"œ]„Ó»`m4ChÇP“¯wc ’kŒF©õðSØçÏpêóßÈå;J†Ã!çÏŸçø±'8|ø/øñwàŽ?ÙàÞ'Vxô‰!§W…f}˜Â'«Ð&E)sœ #»ÉÓGQxZ ;¤‘7’7:¹Ž˜3¬LÉ™ =ùX³õ™N§=ˆ[3‰*?1ˉØüœ*g»öqkÔ–:Äô9­QØBPG§?sº†-Z! MÔ ¾Ã¨©uÛ9I+Æ¢­¥9\½€[ZãØÑÈ ^ð<^òŠ[0ügOù"  ²X^vK¢‡_Øk¾žŸþÈ)8ó/»ä^žs哱å÷Ž}~íFX<qM25Nv±(’hPO­£wÎräÖo`oÀxÏšo80‰±+¼O"SŠiæ/Ke™Î6šþS›Ù@ÈÅ [v¸«*êêÝÜ¥7dJS†é{¶1N•°#™@rÑ'mÙ>›jB&â6©nºø?Y- BÔ ~ãío{|šÄ6Ú0Ù§Ibn}êWšiŽFáݘû?y»w.ð⛟O1èñÑ|ÃÜÍ/ýԿ仿ÿgyzþ__Ó 5ƒÅK¹ã7–Ë^òZNÿúÿÂÇ~ä3¼â;®‚Þ ,æ·îøOü³ÿú*þà±o†=ë ur ªíj´ Pè冢hø¿~ÿ›9¸m.ßWd4ZáÂ…ÜqÏCüâÏß“xÿá‘Ǹ{íÕŒêkq»ÖØ{h: ©ñ ”éÌ/›1¿\㱉 Ù2å,+ƒV eR×pi NüÓêP™ê,I>­Z)Jmh%°Q;ûå´. ´ŠÆ'ñFàS‰½sž²Ô8Ÿz¼=­Ó,év<µ™EñÃN›ú)aØ•D! ’|Hð‰ü‰1à|HqpÓ¢¬,¯qæÔqö¸Œ¯ñçï㟜+o¸™[ÞøZŠS¡æŸÛl~FöP^þ½ÿ‘Õõ'‰¿ù­üâ¿jÜò#À³¸r/úÿÏõ¿ÊÉ w<ñ\˜]†^›!/€CiÁÊ/û–¯ä;^ÝôÝ”rvù0?ÿkïç“weaà)wjŒ  ',x‚Ãg®£9xÍk®LY=Ir±CàÝm‚¶ªêzÇ×oÖåuÏ7&ãÖ((íÉ3-¨lb mæºs¢óß¡•Dô.öË4wY¥6sR.#dË”2¥ˆ»* ­RCªÏ–,Jºvi-­hŒŠÏT-ÍúÔ×:£û­sy@S`0»À«ŸÍ™§çáGŽðð#GøØGo§ÈI‰7|ã÷rÇo|-/ùî7büÔ¹×À¥o‚™™›"-0;Âg~……Õ³ø _ ;_l'ŽîC¹.6̽^µÊǾù·Q?7—j –Î °öìBߠܾßç×M…_‡%;þYÞñËÆd8ææ¯8È`fŽùÅílß½—{ö3»m;‡?s7;.9”,ŸNC©ZRø›7´3ë’‰ »µÈ#Ÿø.‘“Æã‚H'`A‰b’ùzÉ„M>ôÓ׺§B2H ™/°V!žÌéËÓÜK›gh•>ÑÚ¸Eiàèê8S±ÊÄ;\4:lü À¯§"CAE°(jïSÛTˆ‰ö ¹t[„í;÷233ÏÒ¹“œ;u[Ü–s>zùÍ nüAÌ…?A=ô6˜=Dæˆ1 aƒÙñŠËfáåß \ž>Rü,ú¶ßáÔ{?E cü—ÿfs¢©Ü€•'€1—Ýü*Ž}v g'h[ð£?w ûæf'Ïâ³ÝÇ]Ÿ|t¹Å>ÆZúýe¿6¢R.â…¯ú*Ê^%¸NTø (ic`mT³{¡Ï$7w¦18 ò¦¶.—YIÄ*ò#&ç7;ÄÓœ}§D.+Q‡ºç•Fãud\‘­²s©5¼«<ÖyfQ"ãÚ37(§|AU˜©rIîhÒJsæÂ&ŒÙYšþ3Ày¿so>$–©Ó̬"h­SY˜µ©TÌZv\ÂŽmóÌ› N^h±mÃײ(3äü¯ÿ»¾ÿ—l#f×y”¬¢ÖW(ǧ:émƒùW—æ2jÐóðµßÇücË,¿÷nxìQ¸j!•µÀ¾ç"§Þ À}OÜË .ÿa”:È¿yë>êö(w}ö Þ9? (K\=70U€ÜÆhâØÕ“D©.-ic±eIÑPõú4ýmÛc 'Bó³5Wm+aŽ­lp_žxßa܃`×µ¯afÆbíÌÂAÔbE.d³0¤ÎÛ³ 8ûµ_DNžáà»~vï€ÏÝÊçï\nš `ϼãÒW\Ê7¼öõ,êkh ¼öEWо`ƒŸýÕw³sß’=F'"Ë%EÕ§êÐ…ET‚~1§ˆM&NTÌ}z9ý«éJ³“ÙÝZÒešæ ºP0ef’ Ž’f+td@“Gçu UÉ2Ô!¡ùi¢¯Ã&]O@ØtFƒ5é3EX/³›ézô_áá¹þ¶ ÿæûSᯮ?Åg>w„cûýéc¥éîgŽŸ~Ûwñûú8OZÉ£j6‹\1–²êgnc37‘A©X8æûE6Ëy NÇ6& ³½’ˆLG¼Ä˜xŸ‰4k‘˜hÞÒC𡨕ˆkÒøœBR¡I Õô¹(Œ%%rŒV©U<ö„KUžÊlc¨e—9ð$·Óx!èTWƒ{&†þ's(‚R†ª*-ÄàS9Æ”œ˜2RÙ)‰p}ì Ç1…eµ‰Ü»Y{è Ë—ÞËeW\Æž=;ؾ}' }ƒE‘DÅ98w¿ΟÇüÀÂΛ€UøØ{àÁ»ø«÷€ÿúë®Ï×ñȱGxþ /É¿X-±>š Qð!eÚvmŸãÜXÑ˦[åŠfASõ,‚IŠ ÓÏöAcXì§“Þ RhlÌöÊlÊ3Õšãzçs}aÒ/BH¿ÇòÆ„½‹3ifrðÌ–EòáN0&íãÄಬ®èW&÷¦°¯ëŠÒÑô„v#æYG¡ë_誒uR¤ií>PòL(Ôd4rž²L¶¤¹3Bú³ódOˆ1&7+l)€𴜓KÙW>Ò2rÂO®3üäaVWF¼|—jÙxfg{SÀð4L–àÊ›à%W¤ëû4¹îû8,Y^ûþ0OÙK§Ð‡UΞ?Í‹nø*ŽŸz€Q=fû¶ËXœÙÖŠÖ9Î/g£ôú3éfmªÕ‹1&J[D§ÇR@¸0lÑZ1ß/P™µÙUÓ%A®!@r^å:‰»ïºrbVœ™Â"ØôžmLÂ…J ¹‚^©ÈÎ…dz8ˆÐæBЭõâ¹î#ÿš)ßüXêNá¤Õz“¡$ ìÓŽž¡&2R›ÞÐ*$l£4^çÊ`šBBÌõÉ ÈDrœW2'gé«¢w¹ÿqÂÒ‰ØÕ³ô‡¨ö.RÍ÷±}‹éU0¿#µyö#pì!8 Î\àŽ‡òÊwï‡j4çù÷ÞÇÏüà³xßmïãþba°ÀÁK¯âÚËŸ> kЇÅùEFñÿ?ÿ«ïàE×%û.9Ä 7ÀGÍçó»oæÂça\Sì˜eÿlßNCô4_OrŽ^òìN¨½§_Ø4iÔ&!;/P¤"^U`IÍzz~ºh¡«á˂ϧ⭋(»9Ê¥£óOyýBmÖùw5z&Wõtád75ÔÇÍÁ"B==S¤ñ ­£°6·¯§YÆ¥ÕišiÆ%Ý ¨AY¤Sge”B,Í3`GO7C¤ÍV‰õ6u«ô ›»O„*ÓVgJÅ•›%Uþ÷Uלg‡>MýÑÓø}=·¼ìfz½˜µ ޲ô…§ Eµ Ìœà§Þðìè?ñÁï†áA¾ç+oã¥7­}ÞýÒßæ»›gy]øÀmÄ«_øµù¬ægö²¶þQ>ûàç˜+g¹âÐ5µ¾1À:Ÿ¼ç$WoŸ§ß›ÇÌÏ3«+™×ïêòÉpQh· ìº9á’î¶uéä¦ ŠiÒ&jIØ)ޏ(ÓoE“(LºânÃ%E e×ä‘•£sÓÑ4( eic¥u7JVM•¶seqË5ºÁ“ég—’¾¼Ã»"í3à£6úà´²hã©ÄNM‰d­ 1Mw>OùŽq-Íd’†HNÆ<{ûãÕˆõÜÅŠÝÏK_|c†ÌÌT,.ÌÌôÅÚµ¨I Ë=hûüø‹ÿŸ>¶“œývn;{þKؿțov¼ù?¼Ž|–ášgëºöªç23˜c8¬™ .¬\àÉs'8þdà]·š¾¯Âü“K™Y0Ee¡m=³UÊH뢂-÷irEBâÝ÷ä-¯¹’.Ç›hà®?0;ÉùÇNÝÅcO}ŠOîÃ;ó÷<øYÞþÓÊ‘{îÿ ?ÍÉ£‡ùÔáó96âÂ…Š\r†8&¬´ž}jBdcœ+žó·š*6hùÌ…˜.{˜ëï%7éhº\ÉÓ‡@(No*)Û,=︑ÔFÖø˜q„ІÔ$ëc*ýš~ÉÖ4†eú9Z3[˜h׌“5s£Qÿ™  ”E¬“¦§ÇDKjòµQB í$ŒolPׯ¯QÊyDÃÇNqŠ¶Í•ÌÎL!ж-Þ»üvÑ  ‹ ųžJ…V#öWç`îÄG>ú—\2ÏŽý‹`Ç0y–îâÈäÞ‡—xêô:§œ9ÿ_{glWvÕ÷ßÞû wx³¤§§¡¥änuÓ#nÓ4éö€ËS¨@ˆ+˜©(BBW1&TÀUTª(‚+ŠÂ)ŠÉÁ$ØL ƒ»mì-ÓînµÔ-©ŸÔ’Þx§sÎòaí}î•h5$q¾íª[ïééÝsÏ;{í5ü×ZÿE@«€k¶9e>Hœì›öA¥9‡‰{'Å}Š)Ž “´›ž•Š€ôdfÚ3€ªß#ÁÌèdr•¥ÅÿÓç+Ä ŒNÌH 4jl;¨ŠÂ6˜4|µ¶¶ÖÚÑ+º6¼sâÇð60×ËQ=࣪*&“1Ãá€ñh—Ñh€o2°LLÆVo•º®9´™^éètЍ"JÕ(•#¥Ü“èBÇ2n;ñC² NmbôÄYzo}3½wíaî;îaçûžò¡»xèÞƒüڜ峟ìòâ¹ße\ïà,”e‡rŸ½#Ü»´‡'ùŸ\<û<œ7<õÔK{sÇ=Ë’ìšñò›ØNèw繦ÌsjkÑZl~ÚÐ\ „ÄâaºÓñÛ±óEO½™v­“‚“­aÍÞ¹®˜V+Ôo³u‡™"ëTFÖ&ÝÒɹ‰d’¬•Rv¥<Ïbd ZAÏ 4ÁMÞýîw·õ­W€Òº ÁµuîJ)“šn‘“i¨mC]O¨&cšÉ;™ÐÔMíØzùe–‹e‚)ÞÓ) ]zóyî)Ë4je õ€ÖB ǰ=‚Á6 waäØxÎÁ_m¯òÚÕ5Èîåп~gÞÿÓûõǹùÈanÚyý/rã=ßΧ?þŒÇl-N–åx¿ëëYسÊïðC\\ÿ#Ο}µ;,SÎŽ‰!I®ÌwK2­ð^žHGi4s©¶IH_BÜ’ð¤Ó’3&*´D¸ÑxIO*O/Wm…ÈNUÓËL¼^¬þM¾BÐÔÞ㔤çù´sXE vžÝŠ¥~‡v˜”Šð¼Êßå¹nd  c “Ù=׳ÿ Ô“€‘fC'PæYLu gŽm,¶©©«Šq5ÁZKak³æÒ AGPŠõó—YÞ»—={ú,/÷èõ4ÆÔÒé¬àþÃ! 6`|†Û°µ #Ŧ]c¡† #°ãMªÇ?Ìè3Î{.¼‡Ý%†v \¤»Ügqq…׿þí,îYeniÝùEÊÞYY¢µ¡Óéð­ïýAVæO²ÒœFJ+ê8{P1­Íyè©¿qž:öêd×c#‡ S¸|–Ý3D ¡Ö9±ËÔ.0_æäZ·>ÅlUfÊï›iX­¹xºæ0v/Df“¥¤T¾µ=sBÞ‘&³Oš€Q¡bf]¡¬¥Ê|ìþ SˆÔù8ø‘H2d-Ö62ËΉ£äWnãü©Ï³úå8Ú7Z^¤× ¬¬ôÈ´g®—aB-Ho3–Ðd;;B÷2œÀp›j²Àº[Cå–Ðï±þŃ?ý=Foñ-¿|3Ÿ;{¾òç9ý‰§8ðнÜwœÇü¾5ö8Ä`g[W’ñ•?è ”æŸÿô¿£ˆˆ¡Éø0b%;®"Ù‚´]¡42£/fؼØÒ~g†)ò&6]Tv¡äw3-Õ;Þú…ÁFT0š¶©\¦îP¼D3ç¼W÷JÝ™Pä^µ…G)þ Ìšº¦í–(àU Pu5öPDï1y )N…6…ˆ!Ò©xëîap±Ï¹g/ð‡sŽ4ëèÉmôz«t3™be|-íÝÕF5Œ·a4”Ó¿3€á6g&Gx‘ƒv~ƒúÇwð—ò¿ôÔ&p3ôžƒGžfwþ(O=³ÉÊÊ9:éî›çÈ¡8[nàê ‚£¶wzt»ÝXõ3Íd¦̨™¤©æO1¬] ãdÃ2á F+E¯œ&Zå¤P´ âÕ¸Á´Y$ÁëIþ´Ø½V¨|úÙ)?° …¢Ðñ^Û N„ƹ6¬oX[ì^l*¥p•V>Á…Ð\[Bâ™…¨¶ü¬Í‰u‚Úi!X$¬u8+ðoµp„­gøKó}üÊ ï„Ñ_0Ùü¯øþ€òÖ¯`ùÆ[È_þvy®ið®A)CQ”±zÃ!¾îîxæ‰G9}ê£ä½wHd]Ëó€ÎUûðåAûÖ{÷ñ46a ò$º;¬ñÞ³wyžK»Cð°<ו™ÁÑ4ñÁûÈÇ“•.KŸüNÝx¶vG¬,Ìa´xé•õq l˜ qeFФ tsÅö΢Ì)‹œÜwAªñKN¥"Ð8Ù̱ŒšÊFçT›ŒÌk|P¯¦BåǵVœF/%ᮩ©'cìdD5QM&\>ùEü ¼ñáûyàÞ7±²²Â¾}ûØ·¯dno—·l^â7ÿÛçø¡'—9ùïoäýo{™ãKç¡XàéKø…ÓwðÄ×`ü~à1šìÕÑ‹G˜Ïôþø¦¦©'غ&/.:’W¿öMoá¡w-—eK›B|4©+6+mñÐyŠ>=PäZµˆ_§#Ü>Î5tÒ<ïÚdM“F„²„¡íyM^‘†Ö±Ðï ¼¸XÆ™½¦ô4JOºE“e謑‰ãÖ1j\Ëû—Ò½é~2ý 5½‡¡ó “ÆÒèŒ.îÚ>@nB5  ƒ¢„$]M#›?0ì0l3î0`çO=ÇåËó9r§NÂÚŠ,›°1ðœ9ÿ"—7*Þõ–ÛùÑæ¶!?üÈÍt›}T›gØ ì¿.] åýäËGÙÚ|ž}‘°t;«ob\h&cl-À“Öš¬ìP”=T–“窾<;M±*5Åå÷E,ñš"yb£CTëi[:ËØŽEâf¥Në7RFÚ¥¤²HkšÝX©=Ï%K$£Ü˜¦ÒÈ¡ÒU®n(‹BÐÊYÈ‘È[T{Š\EZYÙùÇÃ)Ò$’hÒ'ìHà•@Óµ÷«b¢$t³ ¾®Øì2ÞÙb´»Åx°K£:øjˆÞÍ8qâ¶¶† ‡CvKx3awu…ÉMG;5§>ÿI”1ì=x‘çÿ2Ø÷•pñwX¸m?óû_ÇØ-³qr›¥ì,‡íᥓ—9²9bíèÙÜ*¶‘„rÒc¯MFÙëQ”:EN™/¥Ô1¾o¹ãÃU c/,(éW0®å´¤¦Š@ j,½"oOY2utùIòf…ozàµ|Íw?Gnèò>àxú™#tÙdeïKÎP—Fã¼æîûïDÛG¹Ü[ãP±B97¢ãe·By‡õµÑ)KÊn—`ri—Ž…‰¨˜„nSO8 Dê¯Kñ´ÚFØd“g’/-@HÄ¥ <9:¶·¥H¬œ†¹Ö2w¢–ð/P½2Ç#ÜÌ€1¦- ³ž–ãgVÂJ¿ ‰#àlä!È -XÁê)o g±C}mÈTÓTÞÓÏ5ÞŠ½k”NU)"…œG’F2op£é“U# Æ\¼dy~sž}/_âž?øSî °÷Á÷~àƒñS6Ø[ÿ.kGîanø}õÃÍçèÍã5kg ý90_ùŽ·ógŸÞ`}á­7I(n¬œÐLâñ*òúé8U$ÙÜT\¹Ü/[!Éã,ÌŒŸb&0ñúÈè%6KjÜèé¼à(J«‹|ÌIHK#1a\3CUç§ãèSM"QЫÆÇ‚TZógƒ“6to¯íØ;ï¤>‘Ìmjyì  ´¦§ &ÏÉŠ‚,ËÙÑJ¼Uïqã!ÙpE †*Ã6š¦aaáV:|ß7oó†·z¾åÃåh…½‡öò/¾ã c¸ñ Ì8wá ?ñÁO¢LÎíw/sò©Oð™‹wòº;*©ŒM§:NÂ(sù}•—?õ2žHüœÚ®Ú•~)™NÓÉMÎ^í]¤k,AÔ¬ =%§r23`¶C7%eŠiåNòÞkX[ìÑxGcÃñ„¹^7B»S0…¨Ö¶¶+V»4ÖÓÄNáÆ Chq“Ê8HÂL}žÜÄÃY†ò&W”U_¡œòÀG,;S £H(‹“eNîÜ¢˜€Þ¦è`"c‘«Æ×ÐQRð;¸¼Ë8ßmUà{ò'Ø6kÜõš7óm_]rak‘/|þ ÝâÈÁŠO?ùqÎ]xž=KKÜl™à- Ïñ×Þˆ¿ø'{œqôO~·GžËµd³šy/ö8eöá2Èt!Åí\a7SȘ´Äl~ò÷[Öï蕇g 0õÒ ’iTHÍkòÆ·F ÖF°¨Sqó£4”,÷¾Ð/Úû”뤹ArÇÆLF„gFXÑ6µµ¢¼¿B®Ðƒ…rK·“DQ8ƈϕx¬u9Ae!yˆž(u…F6¿( ÔPUaå¾ë[ßËó/ øÂϱ±úϨ?Wòßö“¼ínâØÁ¹çø=Œ«¹öTÃdqÜÿ÷^ƒw6zßrÏÖÊ]£„TQúô¦`J“R«HSgÃÀí¡£,¥î6??ƒ¤uóÔ¡¦¤ Ï ®q-)–B:m‹Dÿ!z.¼%’MÇ͉α±³u(C‚‡$ê ÈH„¼EkŒÖq:y”˜Åœ•“°…ÖbÚ¼ ”ºžÔtUI˜¯-x|üiâÇHUžŽGã•!há ÎâÆŠ#âÁÖÑYÓ“Ö¤?|߿峛OðèéOð'Ÿþ7¼‹ÿ©ïá¿ëG8²óaܽǸé†ýÜuü­¼û~yž±³»ÍýÇÅä‹pì@Àäy[БÀcd¶AÞ:yéiÆ ZŸ¶ÀØ ž^"°ãÚEZÕ©­õ!å³^~Dî 4“IÍĺØ)kmˆtmAÊ¿âÖ hK¸ÛÏ*2vÄß³)\ÞöÄ?³öÒ•TÇʬàÜ´¢îÚ {˸öTn:QÄFócmÔt¯¦kœÕžNš¢„Â$Ϧ †Ô¸évP¡ÐÆÄÒ0Ÿ¾÷T l7ÿãW?Âc¿ù&o¹—g{ŠÇ}šó¯þ#ßûÿ’9}‰ÞÞ×ïàâÎiê±åèÁ[Ç×>0âc_¢0*ö×Å@ÃM“"‰Ô9Õl$.¯RW ñÈ“®›ÀöpÂþ%É÷¤2eõõK†j[Æ”RyÆpÆO=ü0;ë—8³q‘Ÿ=ãøí_þ|óü,««w†“gØ6X «Kk” {ÙŠ¼ßÅ«ŒÆ‚7!¬¸’†¶²Ròì )»JãDŦr¯Ù0)ÙÚnG“g½v +Ñ”3 #C§BkTªº!ך^·$Q½É'°aê?4aA@â욈D/ß)²iÙxr0H܃Ò\J£èæÓÏÑXZð’C·Øë0®FU;…>µs-$®”d…õM_[ÊÜY¥$ÿ#4âÀ´Oº¶ð2K7ŠML‡ Q€·5Ùþ=ì»ï˹õoæ®äøÝw³´´À'>ÂM·¿mæS;ô;·ÒïpÅjðužêžwàn¸á¯“ºd×9õØç8âyrÝç5¦ÃÍËó|pg–2ª†%©FMiÔ‹Ô%›l­Žðµ’)žF+”’¢ÎѸ‰y{‚ŽpFˆ¯Šé¤Z¢T¤Ã𡍉“¹bÂÈ%›ïÁG ØLÎäâaòNj–çK¬£ÍQ¤¡àæoŒ›v…Ö‰3ƒ,ô‹_ˆ · Òœ A«Á+çµ³WC¥½»ä¼: .PGàC†*‡ÖIW.¦£„@èôiÆ;¬ÿáÇØüÀËìŽÇâ×™7¼á;Y~ä7Ùú†ÛùT¸—¾›ÁNE}ù"nsHØÓs%·?ÎñÛoÇßx˜¼_ðž•¿±=ߦ:3b»•bfŠVjó–Gìt”ÆF~h»gÐ)‹ö}RG0õÁ•š©Ò ´\2!É·Hª—T GÈtÈÐ~xy¼µ}møáþ™Ýïþ¹»OÛÞÁÃ&xÝÍ NZœÇ+G¦#‚dó½·¼ùÙ?eÏîE8?Ƽã ôÞù²ÅEê…6——©”ú¨CáwNpàý?Fö?€êì§ùدqùícŠÆ5zûó/mÒ½¼ƒšýÖWöq×âåÒ -B–2áå!†ÀZBG'fY»yµÂ:G^‚jZ4$‹# D.Lë“$ÄÃH Ò™ë)‚W «š²ÌâKKùÀ|žµ”°ý^<:æ ´ÖôŠi¡M<¹­ ;BìLB!£3ŠíãFAQB¦5Um1¹äC £ ÞQy¥*ÕÇWgϾïÇÞwéštv¾ð±øãów*¥³àœ×ÎZ‚WÁY\=¡íªñ`‡Éî&¯ýã_4—jÇHAGCÿ¹çQO~ 7²½þ"/}áó˜KC²¾F»Œª°œþŸaß}o—ÄËü.*pg-Ùi &XÖ†/ѽ8âÜ‘u艃Gpvk¥vèVe¦ðL+%5{p…×L§sOG¬ù0E¶+šìVsù•е?Åéƒ2S¼5þÐEA±Î“IP¡t­h¥"R¨ZA’Tü½6±¤uŒ>ˆ©êéH¹D• `K0ÕŠhŒûhíDšMPzÃåÏýѯ]˜2ûÏ> ,CñëAà0paô3Qh  x¸§o÷6t;À‚ÖÌM¯ièÔíßA–ƒÉ5¹9(Eت9òýߎ¹û!´Öì¾ïGÙ}vîj—`MðØ è£èˉ¾ºï6nûÐG>ú×·îÿ»^áûpÕïLlü9`x)~=ló|¯4Õq \ž¹ÈV|s¡ôî#ÂÐÿl†óJÝß't»Þ3ç<¥È‹(!JH µ…¬‘x/Y§NÐíÀÃpu“Ï®³8®ðM ŠžòìhÅ9xçé§ùh1> |âÿü¹ýYWoö+ „À²áñß)‚÷p憖³.i¦;Ì#›Þ–Bÿ`qæÿ:F±…¢“kUÚ+:@‘krÓB@«NUÓ?°¢æ Cuá¢]ÚuEN…ÒÖŒ&!ìZ¸´¢Õº¶¼ìðuªáÓžÕëKÖÕÂ0ûs‹è«_bÙç¬L ‹˜ƒô*XB6~Ø¿_@£D€qðe¼–¾êºWß´ß§¯.Þà.¢…Ö³Àˈ¹.Ûu­g<ûºÂ„«Þ ?7Ä,/-9cDkdL7? Àì+]ëêÏòW½Òèbz."ªk+þlÌuø’®WŸì,+©’ Ùÿ=@6? ÄÕ¯«…`VfO¼Ÿùš>'iíøš0[ t}}ÉÖß$é”:dc’F1Uû×ÚôDŒ9«Úœ ×ÖI&ñsFñû„|^__Âõ¿£Ò‰­¹RÝÏÚüÙM¥ÍŸ½æ¬Ì C‚æª×õÓÿÿ`½òª¿7»±³ö}ÖÙ»úg¯ô9³±ëµ„!™…ôš}ßõõ%Z[ø›Þ;{¯õó«×+Wx¨¯ðÿ××õu}]_××õu}]_××õu}]_××ÿÕú_M·%ä±âÇ IEND®B`‚dms-1.1.0/dlna/000077500000000000000000000000001356070553200132205ustar00rootroot00000000000000dms-1.1.0/dlna/dlna.go000066400000000000000000000040461356070553200144710ustar00rootroot00000000000000package dlna import ( "fmt" "strings" "time" ) const ( TimeSeekRangeDomain = "TimeSeekRange.dlna.org" ContentFeaturesDomain = "contentFeatures.dlna.org" TransferModeDomain = "transferMode.dlna.org" ) type ContentFeatures struct { ProfileName string SupportTimeSeek bool SupportRange bool // Play speeds, DLNA.ORG_PS would go here if supported. Transcoded bool } func BinaryInt(b bool) uint { if b { return 1 } else { return 0 } } // flags are in hex. trailing 24 zeroes, 26 are after the space // "DLNA.ORG_OP=" time-seek-range-supp bytes-range-header-supp func (cf ContentFeatures) String() (ret string) { //DLNA.ORG_PN=[a-zA-Z0-9_]* params := make([]string, 0, 2) if cf.ProfileName != "" { params = append(params, "DLNA.ORG_PN="+cf.ProfileName) } params = append(params, fmt.Sprintf( "DLNA.ORG_OP=%b%b;DLNA.ORG_CI=%b", BinaryInt(cf.SupportTimeSeek), BinaryInt(cf.SupportRange), BinaryInt(cf.Transcoded))) return strings.Join(params, ";") } func ParseNPTTime(s string) (time.Duration, error) { var h, m, sec, ms time.Duration n, err := fmt.Sscanf(s, "%d:%2d:%2d.%3d", &h, &m, &sec, &ms) if err != nil { return -1, err } if n < 3 { return -1, fmt.Errorf("invalid npt time: %s", s) } ret := time.Duration(h) * time.Hour ret += time.Duration(m) * time.Minute ret += sec * time.Second ret += ms * time.Millisecond return ret, nil } func FormatNPTTime(npt time.Duration) string { npt /= time.Millisecond ms := npt % 1000 npt /= 1000 s := npt % 60 npt /= 60 m := npt % 60 npt /= 60 h := npt return fmt.Sprintf("%02d:%02d:%02d.%03d", h, m, s, ms) } type NPTRange struct { Start, End time.Duration } func ParseNPTRange(s string) (ret NPTRange, err error) { ss := strings.SplitN(s, "-", 2) if ss[0] != "" { ret.Start, err = ParseNPTTime(ss[0]) if err != nil { return } } if ss[1] != "" { ret.End, err = ParseNPTTime(ss[1]) if err != nil { return } } return } func (me NPTRange) String() (ret string) { ret = me.Start.String() + "-" if me.End >= 0 { ret += me.End.String() } return } dms-1.1.0/dlna/dlna_test.go000066400000000000000000000003551356070553200155270ustar00rootroot00000000000000package dlna import ( "testing" ) func TestContentFeaturesString(t *testing.T) { a := ContentFeatures{ Transcoded: true, SupportTimeSeek: true, }.String() e := "DLNA.ORG_OP=10;DLNA.ORG_CI=1" if e != a { t.Fatal(a) } } dms-1.1.0/dlna/dms/000077500000000000000000000000001356070553200140035ustar00rootroot00000000000000dms-1.1.0/dlna/dms/cd-service-desc.go000066400000000000000000000345711356070553200173040ustar00rootroot00000000000000package dms const contentDirectoryServiceDescription = ` 1 0 GetSearchCapabilities SearchCaps out SearchCapabilities GetSortCapabilities SortCaps out SortCapabilities GetSortExtensionCapabilities SortExtensionCaps out SortExtensionCapabilities GetFeatureList FeatureList out FeatureList GetSystemUpdateID Id out SystemUpdateID Browse ObjectID in A_ARG_TYPE_ObjectID BrowseFlag in A_ARG_TYPE_BrowseFlag Filter in A_ARG_TYPE_Filter StartingIndex in A_ARG_TYPE_Index RequestedCount in A_ARG_TYPE_Count SortCriteria in A_ARG_TYPE_SortCriteria Result out A_ARG_TYPE_Result NumberReturned out A_ARG_TYPE_Count TotalMatches out A_ARG_TYPE_Count UpdateID out A_ARG_TYPE_UpdateID Search ContainerID in A_ARG_TYPE_ObjectID SearchCriteria in A_ARG_TYPE_SearchCriteria Filter in A_ARG_TYPE_Filter StartingIndex in A_ARG_TYPE_Index RequestedCount in A_ARG_TYPE_Count SortCriteria in A_ARG_TYPE_SortCriteria Result out A_ARG_TYPE_Result NumberReturned out A_ARG_TYPE_Count TotalMatches out A_ARG_TYPE_Count UpdateID out A_ARG_TYPE_UpdateID CreateObject ContainerID in A_ARG_TYPE_ObjectID Elements in A_ARG_TYPE_Result ObjectID out A_ARG_TYPE_ObjectID Result out A_ARG_TYPE_Result DestroyObject ObjectID in A_ARG_TYPE_ObjectID UpdateObject ObjectID in A_ARG_TYPE_ObjectID CurrentTagValue in A_ARG_TYPE_TagValueList NewTagValue in A_ARG_TYPE_TagValueList MoveObject ObjectID in A_ARG_TYPE_ObjectID NewParentID in A_ARG_TYPE_ObjectID NewObjectID out A_ARG_TYPE_ObjectID ImportResource SourceURI in A_ARG_TYPE_URI DestinationURI in A_ARG_TYPE_URI TransferID out A_ARG_TYPE_TransferID ExportResource SourceURI in A_ARG_TYPE_URI DestinationURI in A_ARG_TYPE_URI TransferID out A_ARG_TYPE_TransferID StopTransferResource TransferID in A_ARG_TYPE_TransferID DeleteResource ResourceURI in A_ARG_TYPE_URI GetTransferProgress TransferID in A_ARG_TYPE_TransferID TransferStatus out A_ARG_TYPE_TransferStatus TransferLength out A_ARG_TYPE_TransferLength TransferTotal out A_ARG_TYPE_TransferTotal CreateReference ContainerID in A_ARG_TYPE_ObjectID ObjectID in A_ARG_TYPE_ObjectID NewID out A_ARG_TYPE_ObjectID SearchCapabilities string SortCapabilities string SortExtensionCapabilities string SystemUpdateID ui4 ContainerUpdateIDs string TransferIDs string FeatureList string A_ARG_TYPE_ObjectID string A_ARG_TYPE_Result string A_ARG_TYPE_SearchCriteria string A_ARG_TYPE_BrowseFlag string BrowseMetadata BrowseDirectChildren A_ARG_TYPE_Filter string A_ARG_TYPE_SortCriteria string A_ARG_TYPE_Index ui4 A_ARG_TYPE_Count ui4 A_ARG_TYPE_UpdateID ui4 A_ARG_TYPE_TransferID ui4 A_ARG_TYPE_TransferStatus string COMPLETED ERROR IN_PROGRESS STOPPED A_ARG_TYPE_TransferLength string A_ARG_TYPE_TransferTotal string A_ARG_TYPE_TagValueList string A_ARG_TYPE_URI uri ` dms-1.1.0/dlna/dms/cds.go000066400000000000000000000232331356070553200151060ustar00rootroot00000000000000package dms import ( "encoding/xml" "fmt" "log" "net/http" "net/url" "os" "path" "path/filepath" "sort" "strings" "github.com/anacrolix/dms/dlna" "github.com/anacrolix/dms/misc" "github.com/anacrolix/dms/upnp" "github.com/anacrolix/dms/upnpav" "github.com/anacrolix/ffprobe" ) type contentDirectoryService struct { *Server upnp.Eventing } func (cds *contentDirectoryService) updateIDString() string { return fmt.Sprintf("%d", uint32(os.Getpid())) } // Turns the given entry and DMS host into a UPnP object. A nil object is // returned if the entry is not of interest. func (me *contentDirectoryService) cdsObjectToUpnpavObject(cdsObject object, fileInfo os.FileInfo, host, userAgent string) (ret interface{}, err error) { entryFilePath := cdsObject.FilePath() ignored, err := me.IgnorePath(entryFilePath) if err != nil { return } if ignored { return } obj := upnpav.Object{ ID: cdsObject.ID(), Restricted: 1, ParentID: cdsObject.ParentID(), } if fileInfo.IsDir() { obj.Class = "object.container.storageFolder" obj.Title = fileInfo.Name() ret = upnpav.Container{Object: obj} return } if !fileInfo.Mode().IsRegular() { log.Printf("%s ignored: non-regular file", cdsObject.FilePath()) return } mimeType, err := MimeTypeByPath(entryFilePath) if err != nil { return } if !mimeType.IsMedia() { log.Printf("%s ignored: non-media file (%s)", cdsObject.FilePath(), mimeType) return } iconURI := (&url.URL{ Scheme: "http", Host: host, Path: iconPath, RawQuery: url.Values{ "path": {cdsObject.Path}, }.Encode(), }).String() obj.Icon = iconURI // TODO(anacrolix): This might not be necessary due to item res image // element. obj.AlbumArtURI = iconURI obj.Class = "object.item." + mimeType.Type() + "Item" var ( ffInfo *ffprobe.Info nativeBitrate uint resDuration string ) if !me.NoProbe { ffInfo, probeErr := me.ffmpegProbe(entryFilePath) switch probeErr { case nil: if ffInfo != nil { nativeBitrate, _ = ffInfo.Bitrate() if d, err := ffInfo.Duration(); err == nil { resDuration = misc.FormatDurationSexagesimal(d) } } case ffprobe.ExeNotFound: default: log.Printf("error probing %s: %s", entryFilePath, probeErr) } } if obj.Title == "" { obj.Title = fileInfo.Name() } resolution := func() string { if ffInfo != nil { for _, strm := range ffInfo.Streams { if strm["codec_type"] != "video" { continue } width := strm["width"] height := strm["height"] return fmt.Sprintf("%.0fx%.0f", width, height) } } return "" }() item := upnpav.Item{ Object: obj, // Capacity: 1 for raw, 1 for icon, plus transcodes. Res: make([]upnpav.Resource, 0, 2+len(transcodes)), } item.Res = append(item.Res, upnpav.Resource{ URL: (&url.URL{ Scheme: "http", Host: host, Path: resPath, RawQuery: url.Values{ "path": {cdsObject.Path}, }.Encode(), }).String(), ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", mimeType, dlna.ContentFeatures{ SupportRange: true, }.String()), Bitrate: nativeBitrate, Duration: resDuration, Size: uint64(fileInfo.Size()), Resolution: resolution, }) if mimeType.IsVideo() { if !me.NoTranscode { item.Res = append(item.Res, transcodeResources(host, cdsObject.Path, resolution, resDuration)...) } } if mimeType.IsVideo() || mimeType.IsImage() { item.Res = append(item.Res, upnpav.Resource{ URL: (&url.URL{ Scheme: "http", Host: host, Path: iconPath, RawQuery: url.Values{ "path": {cdsObject.Path}, "c": {"jpeg"}, }.Encode(), }).String(), ProtocolInfo: "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN", }) } ret = item return } // Returns all the upnpav objects in a directory. func (me *contentDirectoryService) readContainer(o object, host, userAgent string) (ret []interface{}, err error) { sfis := sortableFileInfoSlice{ // TODO(anacrolix): Dig up why this special cast was added. FoldersLast: strings.Contains(userAgent, `AwoX/1.1`), } sfis.fileInfoSlice, err = o.readDir() if err != nil { return } sort.Sort(sfis) for _, fi := range sfis.fileInfoSlice { child := object{path.Join(o.Path, fi.Name()), me.RootObjectPath} obj, err := me.cdsObjectToUpnpavObject(child, fi, host, userAgent) if err != nil { log.Printf("error with %s: %s", child.FilePath(), err) continue } if obj != nil { ret = append(ret, obj) } } return } type browse struct { ObjectID string BrowseFlag string Filter string StartingIndex int RequestedCount int } // ContentDirectory object from ObjectID. func (me *contentDirectoryService) objectFromID(id string) (o object, err error) { o.Path, err = url.QueryUnescape(id) if err != nil { return } if o.Path == "0" { o.Path = "/" } o.Path = path.Clean(o.Path) if !path.IsAbs(o.Path) { err = fmt.Errorf("bad ObjectID %v", o.Path) return } o.RootObjectPath = me.RootObjectPath return } func (me *contentDirectoryService) Handle(action string, argsXML []byte, r *http.Request) (map[string]string, error) { host := r.Host userAgent := r.UserAgent() switch action { case "GetSystemUpdateID": return map[string]string{ "Id": me.updateIDString(), }, nil case "GetSortCapabilities": return map[string]string{ "SortCaps": "dc:title", }, nil case "Browse": var browse browse if err := xml.Unmarshal([]byte(argsXML), &browse); err != nil { return nil, err } obj, err := me.objectFromID(browse.ObjectID) if err != nil { return nil, upnp.Errorf(upnpav.NoSuchObjectErrorCode, err.Error()) } switch browse.BrowseFlag { case "BrowseDirectChildren": objs, err := me.readContainer(obj, host, userAgent) if err != nil { return nil, upnp.Errorf(upnpav.NoSuchObjectErrorCode, err.Error()) } totalMatches := len(objs) objs = objs[func() (low int) { low = browse.StartingIndex if low > len(objs) { low = len(objs) } return }():] if browse.RequestedCount != 0 && int(browse.RequestedCount) < len(objs) { objs = objs[:browse.RequestedCount] } result, err := xml.Marshal(objs) if err != nil { return nil, err } return map[string]string{ "TotalMatches": fmt.Sprint(totalMatches), "NumberReturned": fmt.Sprint(len(objs)), "Result": didl_lite(string(result)), "UpdateID": me.updateIDString(), }, nil case "BrowseMetadata": fileInfo, err := os.Stat(obj.FilePath()) if err != nil { if os.IsNotExist(err) { return nil, &upnp.Error{ Code: upnpav.NoSuchObjectErrorCode, Desc: err.Error(), } } return nil, err } upnp, err := me.cdsObjectToUpnpavObject(obj, fileInfo, host, userAgent) if err != nil { return nil, err } buf, err := xml.Marshal(upnp) if err != nil { return nil, err } return map[string]string{ "TotalMatches": "1", "NumberReturned": "1", "Result": didl_lite(func() string { return string(buf) }()), "UpdateID": me.updateIDString(), }, nil default: return nil, upnp.Errorf(upnp.ArgumentValueInvalidErrorCode, "unhandled browse flag: %v", browse.BrowseFlag) } case "GetSearchCapabilities": return map[string]string{ "SearchCaps": "", }, nil default: return nil, upnp.InvalidActionError } } // Represents a ContentDirectory object. type object struct { Path string // The cleaned, absolute path for the object relative to the server. RootObjectPath string } // Returns the number of children this object has, such as for a container. func (cds *contentDirectoryService) objectChildCount(me object) int { objs, err := cds.readContainer(me, "", "") if err != nil { log.Printf("error reading container: %s", err) } return len(objs) } func (cds *contentDirectoryService) objectHasChildren(obj object) bool { return cds.objectChildCount(obj) != 0 } // Returns the actual local filesystem path for the object. func (o *object) FilePath() string { return filepath.Join(o.RootObjectPath, filepath.FromSlash(o.Path)) } // Returns the ObjectID for the object. This is used in various ContentDirectory actions. func (o object) ID() string { if !path.IsAbs(o.Path) { log.Panicf("Relative object path: %s", o.Path) } if len(o.Path) == 1 { return "0" } return url.QueryEscape(o.Path) } func (o *object) IsRoot() bool { return o.Path == "/" } // Returns the object's parent ObjectID. Fortunately it can be deduced from the // ObjectID (for now). func (o object) ParentID() string { if o.IsRoot() { return "-1" } o.Path = path.Dir(o.Path) return o.ID() } // This function exists rather than just calling os.(*File).Readdir because I // want to stat(), not lstat() each entry. func (o *object) readDir() (fis []os.FileInfo, err error) { dirPath := o.FilePath() dirFile, err := os.Open(dirPath) if err != nil { return } defer dirFile.Close() var dirContent []string dirContent, err = dirFile.Readdirnames(-1) if err != nil { return } fis = make([]os.FileInfo, 0, len(dirContent)) for _, file := range dirContent { fi, err := os.Stat(filepath.Join(dirPath, file)) if err != nil { continue } fis = append(fis, fi) } return } type sortableFileInfoSlice struct { fileInfoSlice []os.FileInfo FoldersLast bool } func (me sortableFileInfoSlice) Len() int { return len(me.fileInfoSlice) } func (me sortableFileInfoSlice) Less(i, j int) bool { if me.fileInfoSlice[i].IsDir() && !me.fileInfoSlice[j].IsDir() { return !me.FoldersLast } if !me.fileInfoSlice[i].IsDir() && me.fileInfoSlice[j].IsDir() { return me.FoldersLast } return strings.ToLower(me.fileInfoSlice[i].Name()) < strings.ToLower(me.fileInfoSlice[j].Name()) } func (me sortableFileInfoSlice) Swap(i, j int) { me.fileInfoSlice[i], me.fileInfoSlice[j] = me.fileInfoSlice[j], me.fileInfoSlice[i] } dms-1.1.0/dlna/dms/cds_test.go000066400000000000000000000007041356070553200161430ustar00rootroot00000000000000package dms import ( "strings" "testing" ) func TestEscapeObjectID(t *testing.T) { o := object{ Path: "/some/file", } id := o.ID() if strings.ContainsAny(id, "/") { t.Skip("may not work with some players: object IDs contain '/'") } } func TestRootObjectID(t *testing.T) { if (object{Path: "/"}).ID() != "0" { t.FailNow() } } func TestRootParentObjectID(t *testing.T) { if (object{Path: "/"}).ParentID() != "-1" { t.FailNow() } } dms-1.1.0/dlna/dms/cm-service-desc.go000066400000000000000000000172761356070553200173200ustar00rootroot00000000000000package dms const connectionManagerServiceDesc = ` 1 0 GetProtocolInfo Source out SourceProtocolInfo Sink out SinkProtocolInfo PrepareForConnection RemoteProtocolInfo in A_ARG_TYPE_ProtocolInfo PeerConnectionManager in A_ARG_TYPE_ConnectionManager PeerConnectionID in A_ARG_TYPE_ConnectionID Direction in A_ARG_TYPE_Direction ConnectionID out A_ARG_TYPE_ConnectionID AVTransportID out A_ARG_TYPE_AVTransportID RcsID out A_ARG_TYPE_RcsID ConnectionComplete ConnectionID in A_ARG_TYPE_ConnectionID GetCurrentConnectionIDs ConnectionIDs out CurrentConnectionIDs GetCurrentConnectionInfo ConnectionID in A_ARG_TYPE_ConnectionID RcsID out A_ARG_TYPE_RcsID AVTransportID out A_ARG_TYPE_AVTransportID ProtocolInfo out A_ARG_TYPE_ProtocolInfo PeerConnectionManager out A_ARG_TYPE_ConnectionManager PeerConnectionID out A_ARG_TYPE_ConnectionID Direction out A_ARG_TYPE_Direction Status out A_ARG_TYPE_ConnectionStatus GetRendererItemInfo ItemInfoFilter in A_ARG_TYPE_ItemInfoFilter ItemMetadataList in A_ARG_TYPE_Result ItemRenderingInfoList out A_ARG_TYPE_RenderingInfoList GetFeatureList FeatureList out FeatureList SourceProtocolInfo string SinkProtocolInfo string CurrentConnectionIDs string A_ARG_TYPE_ConnectionStatus string OK ContentFormatMismatch InsufficientBandwidth UnreliableChannel Unknown A_ARG_TYPE_ConnectionManager string A_ARG_TYPE_Direction string Input Output A_ARG_TYPE_ProtocolInfo string A_ARG_TYPE_ConnectionID i4 A_ARG_TYPE_AVTransportID i4 A_ARG_TYPE_RcsID i4 A_ARG_TYPE_ItemInfoFilter string A_ARG_TYPE_Result string A_ARG_TYPE_RenderingInfoList string ClockUpdateID ui4 DeviceClockInfoUpdates string ` dms-1.1.0/dlna/dms/dms.go000066400000000000000000000645501356070553200151270ustar00rootroot00000000000000package dms import ( "bytes" "crypto/md5" "encoding/xml" "errors" "fmt" "io" "io/ioutil" "log" "net" "net/http" "net/http/pprof" "net/url" "os" "os/exec" "os/user" "path" "path/filepath" "strings" "time" "github.com/anacrolix/dms/dlna" "github.com/anacrolix/dms/soap" "github.com/anacrolix/dms/ssdp" "github.com/anacrolix/dms/transcode" "github.com/anacrolix/dms/upnp" "github.com/anacrolix/dms/upnpav" "github.com/anacrolix/ffprobe" ) const ( serverField = "Linux/3.4 DLNADOC/1.50 UPnP/1.0 DMS/1.0" rootDeviceType = "urn:schemas-upnp-org:device:MediaServer:1" rootDeviceModelName = "dms 1.0" resPath = "/res" iconPath = "/icon" rootDescPath = "/rootDesc.xml" contentDirectorySCPDURL = "/scpd/ContentDirectory.xml" contentDirectoryEventSubURL = "/evt/ContentDirectory" serviceControlURL = "/ctl" deviceIconPath = "/deviceIcon" ) type transcodeSpec struct { mimeType string DLNAProfileName string Transcode func(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) } var transcodes = map[string]transcodeSpec{ "t": { mimeType: "video/mpeg", DLNAProfileName: "MPEG_PS_PAL", Transcode: transcode.Transcode, }, "vp8": {mimeType: "video/webm", Transcode: transcode.VP8Transcode}, "chromecast": {mimeType: "video/mp4", Transcode: transcode.ChromecastTranscode}, } func makeDeviceUuid(unique string) string { h := md5.New() if _, err := io.WriteString(h, unique); err != nil { log.Panicf("makeDeviceUuid write failed: %s", err) } buf := h.Sum(nil) return upnp.FormatUUID(buf) } // Groups the service definition with its XML description. type service struct { upnp.Service SCPD string } // Exposed UPnP AV services. var services = []*service{ { Service: upnp.Service{ ServiceType: "urn:schemas-upnp-org:service:ContentDirectory:1", ServiceId: "urn:upnp-org:serviceId:ContentDirectory", EventSubURL: contentDirectoryEventSubURL, }, SCPD: contentDirectoryServiceDescription, }, // { // Service: upnp.Service{ // ServiceType: "urn:schemas-upnp-org:service:ConnectionManager:3", // ServiceId: "urn:upnp-org:serviceId:ConnectionManager", // }, // SCPD: connectionManagerServiceDesc, // }, } // The control URL for every service is the same. We're able to infer the desired service from the request headers. func init() { for _, s := range services { s.ControlURL = serviceControlURL } } func devices() []string { return []string{ "urn:schemas-upnp-org:device:MediaServer:1", } } func serviceTypes() (ret []string) { for _, s := range services { ret = append(ret, s.ServiceType) } return } func (me *Server) httpPort() int { return me.HTTPConn.Addr().(*net.TCPAddr).Port } func (me *Server) serveHTTP() error { srv := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if me.LogHeaders { fmt.Fprintf(os.Stderr, "%s %s\r\n", r.Method, r.RequestURI) r.Header.Write(os.Stderr) fmt.Fprintln(os.Stderr) } w.Header().Set("Ext", "") w.Header().Set("Server", serverField) me.httpServeMux.ServeHTTP(&mitmRespWriter{ ResponseWriter: w, logHeader: me.LogHeaders, }, r) }), } err := srv.Serve(me.HTTPConn) select { case <-me.closed: return nil default: return err } } // An interface with these flags should be valid for SSDP. const ssdpInterfaceFlags = net.FlagUp | net.FlagMulticast func (me *Server) doSSDP() { active := 0 stopped := make(chan struct{}) for _, if_ := range me.Interfaces { active++ go func(if_ net.Interface) { defer func() { stopped <- struct{}{} }() me.ssdpInterface(if_) }(if_) } for active > 0 { <-stopped active-- } } // Run SSDP server on an interface. func (me *Server) ssdpInterface(if_ net.Interface) { s := ssdp.Server{ Interface: if_, Devices: devices(), Services: serviceTypes(), Location: func(ip net.IP) string { return me.location(ip) }, Server: serverField, UUID: me.rootDeviceUUID, NotifyInterval: me.NotifyInterval, } if err := s.Init(); err != nil { if if_.Flags&ssdpInterfaceFlags != ssdpInterfaceFlags { // Didn't expect it to work anyway. return } if strings.Contains(err.Error(), "listen") { // OSX has a lot of dud interfaces. Failure to create a socket on // the interface are what we're expecting if the interface is no // good. return } log.Printf("error creating ssdp server on %s: %s", if_.Name, err) return } defer s.Close() log.Println("started SSDP on", if_.Name) stopped := make(chan struct{}) go func() { defer close(stopped) if err := s.Serve(); err != nil { log.Printf("%q: %q\n", if_.Name, err) } }() select { case <-me.closed: // Returning will close the server. case <-stopped: } } var ( startTime time.Time ) type Icon struct { Width, Height, Depth int Mimetype string io.ReadSeeker } type Server struct { HTTPConn net.Listener FriendlyName string Interfaces []net.Interface httpServeMux *http.ServeMux RootObjectPath string rootDescXML []byte rootDeviceUUID string FFProbeCache Cache closed chan struct{} ssdpStopped chan struct{} // The service SOAP handler keyed by service URN. services map[string]UPnPService LogHeaders bool // Disable transcoding, and the resource elements implied in the CDS. NoTranscode bool // Force transcoding to certain format of the 'transcodes' map ForceTranscodeTo string // Disable media probing with ffprobe NoProbe bool Icons []Icon // Stall event subscription requests until they drop. A workaround for // some bad clients. StallEventSubscribe bool // Time interval between SSPD announces NotifyInterval time.Duration // Ignore hidden files and directories IgnoreHidden bool // Ingnore unreadable files and directories IgnoreUnreadable bool // White list of clients AllowedIpNets []*net.IPNet } // UPnP SOAP service. type UPnPService interface { Handle(action string, argsXML []byte, r *http.Request) (respArgs map[string]string, err error) Subscribe(callback []*url.URL, timeoutSeconds int) (sid string, actualTimeout int, err error) Unsubscribe(sid string) error } type Cache interface { Set(key interface{}, value interface{}) Get(key interface{}) (value interface{}, ok bool) } type dummyFFProbeCache struct{} func (dummyFFProbeCache) Set(interface{}, interface{}) {} func (dummyFFProbeCache) Get(interface{}) (interface{}, bool) { return nil, false } // Public definition so that external modules can persist cache contents. type FfprobeCacheItem struct { Key ffmpegInfoCacheKey Value *ffprobe.Info } // update the UPnP object fields from ffprobe data // priority is given the format section, and then the streams sequentially func itemExtra(item *upnpav.Object, info *ffprobe.Info) { setFromTags := func(m map[string]interface{}) { for key, val := range m { setIfUnset := func(s *string) { if *s == "" { *s = val.(string) } } switch strings.ToLower(key) { case "tag:artist": setIfUnset(&item.Artist) case "tag:album": setIfUnset(&item.Album) case "tag:genre": setIfUnset(&item.Genre) } } } setFromTags(info.Format) for _, m := range info.Streams { setFromTags(m) } } type ffmpegInfoCacheKey struct { Path string ModTime int64 } func transcodeResources(host, path, resolution, duration string) (ret []upnpav.Resource) { ret = make([]upnpav.Resource, 0, len(transcodes)) for k, v := range transcodes { ret = append(ret, upnpav.Resource{ ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", v.mimeType, dlna.ContentFeatures{ SupportTimeSeek: true, Transcoded: true, ProfileName: v.DLNAProfileName, }.String()), URL: (&url.URL{ Scheme: "http", Host: host, Path: resPath, RawQuery: url.Values{ "path": {path}, "transcode": {k}, }.Encode(), }).String(), Resolution: resolution, Duration: duration, }) } return } func parseDLNARangeHeader(val string) (ret dlna.NPTRange, err error) { if !strings.HasPrefix(val, "npt=") { err = errors.New("bad prefix") return } ret, err = dlna.ParseNPTRange(val[len("npt="):]) if err != nil { return } return } // Determines the time-based range to transcode, and sets the appropriate // headers. Returns !ok if there was an error and the caller should stop // handling the request. func handleDLNARange(w http.ResponseWriter, hs http.Header) (r dlna.NPTRange, partialResponse, ok bool) { if len(hs[http.CanonicalHeaderKey(dlna.TimeSeekRangeDomain)]) == 0 { ok = true return } partialResponse = true h := hs.Get(dlna.TimeSeekRangeDomain) r, err := parseDLNARangeHeader(h) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // Passing an exact NPT duration seems to cause trouble pass the "iono" // (*) duration instead. // // TODO: Check that the request range can't already have /. w.Header().Set(dlna.TimeSeekRangeDomain, h+"/*") ok = true return } func (me *Server) serveDLNATranscode(w http.ResponseWriter, r *http.Request, path_ string, ts transcodeSpec, tsname string) { w.Header().Set(dlna.TransferModeDomain, "Streaming") w.Header().Set("content-type", ts.mimeType) w.Header().Set(dlna.ContentFeaturesDomain, (dlna.ContentFeatures{ Transcoded: true, SupportTimeSeek: true, }).String()) // If a range of any kind is given, we have to respond with 206 if we're // interpreting that range. Since only the DLNA range is handled in this // function, it alone determines if we'll give a partial response. range_, partialResponse, ok := handleDLNARange(w, r.Header) if !ok { return } ffInfo, _ := me.ffmpegProbe(path_) if ffInfo != nil { if duration, err := ffInfo.Duration(); err == nil { s := fmt.Sprintf("%f", duration.Seconds()) w.Header().Set("content-duration", s) w.Header().Set("x-content-duration", s) } } stderrPath := func() string { u, _ := user.Current() return filepath.Join(u.HomeDir, ".dms", "log", tsname, filepath.Base(path_)) }() os.MkdirAll(filepath.Dir(stderrPath), 0750) logFile, err := os.Create(stderrPath) if err != nil { log.Printf("couldn't create transcode log file: %s", err) } else { defer logFile.Close() log.Printf("logging transcode to %q", stderrPath) } p, err := ts.Transcode(path_, range_.Start, range_.End-range_.Start, logFile) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer p.Close() // I recently switched this to returning 200 if no range is specified for // pure UPnP clients. It's possible that DLNA clients will *always* expect // 206. It appears the HTTP standard requires that 206 only be used if a // response is not interpreting any range headers. w.WriteHeader(func() int { if partialResponse { return http.StatusPartialContent } else { return http.StatusOK } }()) io.Copy(w, p) } func init() { startTime = time.Now() } func getDefaultFriendlyName() string { return fmt.Sprintf("%s: %s on %s", rootDeviceModelName, func() string { user, err := user.Current() if err != nil { log.Panicf("getDefaultFriendlyName could not get username: %s", err) } return user.Name }(), func() string { name, err := os.Hostname() if err != nil { log.Panicf("getDefaultFriendlyName could not get hostname: %s", err) } return name }()) } func xmlMarshalOrPanic(value interface{}) []byte { ret, err := xml.MarshalIndent(value, "", " ") if err != nil { log.Panicf("xmlMarshalOrPanic failed to marshal %v: %s", value, err) } return ret } // TODO: Document the use of this for debugging. type mitmRespWriter struct { http.ResponseWriter loggedHeader bool logHeader bool } func (me *mitmRespWriter) WriteHeader(code int) { me.doLogHeader(code) me.ResponseWriter.WriteHeader(code) } func (me *mitmRespWriter) doLogHeader(code int) { if !me.logHeader { return } fmt.Fprintln(os.Stderr, code) for k, v := range me.Header() { fmt.Fprintln(os.Stderr, k, v) } fmt.Fprintln(os.Stderr) me.loggedHeader = true } func (me *mitmRespWriter) Write(b []byte) (int, error) { if !me.loggedHeader { me.doLogHeader(200) } return me.ResponseWriter.Write(b) } func (me *mitmRespWriter) CloseNotify() <-chan bool { return me.ResponseWriter.(http.CloseNotifier).CloseNotify() } // Set the SCPD serve paths. func init() { for _, s := range services { p := path.Join("/scpd", s.ServiceId) s.SCPDURL = p } } // Install handlers to serve SCPD for each UPnP service. func handleSCPDs(mux *http.ServeMux) { for _, s := range services { mux.HandleFunc(s.SCPDURL, func(serviceDesc string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", `text/xml; charset="utf-8"`) http.ServeContent(w, r, ".xml", startTime, bytes.NewReader([]byte(serviceDesc))) } }(s.SCPD)) } } // Marshal SOAP response arguments into a response XML snippet. func marshalSOAPResponse(sa upnp.SoapAction, args map[string]string) []byte { soapArgs := make([]soap.Arg, 0, len(args)) for argName, value := range args { soapArgs = append(soapArgs, soap.Arg{ XMLName: xml.Name{Local: argName}, Value: value, }) } return []byte(fmt.Sprintf(`%[3]s`, sa.Action, sa.ServiceURN.String(), xmlMarshalOrPanic(soapArgs))) } // Handle a SOAP request and return the response arguments or UPnP error. func (me *Server) soapActionResponse(sa upnp.SoapAction, actionRequestXML []byte, r *http.Request) (map[string]string, error) { service, ok := me.services[sa.Type] if !ok { // TODO: What's the invalid service error?! return nil, upnp.Errorf(upnp.InvalidActionErrorCode, "Invalid service: %s", sa.Type) } return service.Handle(sa.Action, actionRequestXML, r) } // Handle a service control HTTP request. func (me *Server) serviceControlHandler(w http.ResponseWriter, r *http.Request) { found := false clientIp, _, _ := net.SplitHostPort(r.RemoteAddr) for _, ipnet := range me.AllowedIpNets { if ipnet.Contains(net.ParseIP(clientIp)) { found = true } } if !found { log.Printf("not allowed client %s, %+v", clientIp, me.AllowedIpNets) http.Error(w, "forbidden", http.StatusForbidden) return } soapActionString := r.Header.Get("SOAPACTION") soapAction, err := upnp.ParseActionHTTPHeader(soapActionString) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } var env soap.Envelope if err := xml.NewDecoder(r.Body).Decode(&env); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } //AwoX/1.1 UPnP/1.0 DLNADOC/1.50 //log.Println(r.UserAgent()) w.Header().Set("Content-Type", `text/xml; charset="utf-8"`) w.Header().Set("Ext", "") w.Header().Set("Server", serverField) soapRespXML, code := func() ([]byte, int) { respArgs, err := me.soapActionResponse(soapAction, env.Body.Action, r) if err != nil { upnpErr := upnp.ConvertError(err) return xmlMarshalOrPanic(soap.NewFault("UPnPError", upnpErr)), 500 } return marshalSOAPResponse(soapAction, respArgs), 200 }() bodyStr := fmt.Sprintf(`%s`, soapRespXML) w.WriteHeader(code) if _, err := w.Write([]byte(bodyStr)); err != nil { log.Print(err) } } func safeFilePath(root, given string) string { return filepath.Join(root, filepath.FromSlash(path.Clean("/" + given))[1:]) } func (s *Server) filePath(_path string) string { return safeFilePath(s.RootObjectPath, _path) } func (me *Server) serveIcon(w http.ResponseWriter, r *http.Request) { filePath := me.filePath(r.URL.Query().Get("path")) c := r.URL.Query().Get("c") if c == "" { c = "png" } cmd := exec.Command("ffmpegthumbnailer", "-i", filePath, "-o", "/dev/stdout", "-c"+c) // cmd.Stderr = os.Stderr body, err := cmd.Output() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } http.ServeContent(w, r, "", time.Now(), bytes.NewReader(body)) } func (server *Server) contentDirectoryInitialEvent(urls []*url.URL, sid string) { body := xmlMarshalOrPanic(upnp.PropertySet{ Properties: []upnp.Property{ upnp.Property{ Variable: upnp.Variable{ XMLName: xml.Name{ Local: "SystemUpdateID", }, Value: "0", }, }, // upnp.Property{ // Variable: upnp.Variable{ // XMLName: xml.Name{ // Local: "ContainerUpdateIDs", // }, // }, // }, // upnp.Property{ // Variable: upnp.Variable{ // XMLName: xml.Name{ // Local: "TransferIDs", // }, // }, // }, }, Space: "urn:schemas-upnp-org:event-1-0", }) body = append([]byte(``+"\n"), body...) eventingLogger.Print(string(body)) for _, _url := range urls { bodyReader := bytes.NewReader(body) req, err := http.NewRequest("NOTIFY", _url.String(), bodyReader) if err != nil { log.Printf("Could not create a request to notify %s: %s", _url.String(), err) continue } req.Header["CONTENT-TYPE"] = []string{`text/xml; charset="utf-8"`} req.Header["NT"] = []string{"upnp:event"} req.Header["NTS"] = []string{"upnp:propchange"} req.Header["SID"] = []string{sid} req.Header["SEQ"] = []string{"0"} // req.Header["TRANSFER-ENCODING"] = []string{"chunked"} // req.ContentLength = int64(bodyReader.Len()) eventingLogger.Print(req.Header) eventingLogger.Print("starting notify") resp, err := http.DefaultClient.Do(req) eventingLogger.Print("finished notify") if err != nil { log.Printf("Could not notify %s: %s", _url.String(), err) continue } eventingLogger.Print(resp) b, _ := ioutil.ReadAll(resp.Body) eventingLogger.Println(string(b)) resp.Body.Close() } } var eventingLogger = log.New(ioutil.Discard, "", 0) func (server *Server) contentDirectoryEventSubHandler(w http.ResponseWriter, r *http.Request) { if server.StallEventSubscribe { // I have an LG TV that doesn't like my eventing implementation. // Returning unimplemented (501?) errors, results in repeat subscribe // attempts which hits some kind of error count limit on the TV // causing it to forcefully disconnect. It also won't work if the CDS // service doesn't include an EventSubURL. The best thing I can do is // cause every attempt to subscribe to timeout on the TV end, which // reduces the error rate enough that the TV continues to operate // without eventing. // // I've not found a reliable way to identify this TV, since it and // others don't seem to include any client-identifying headers on // SUBSCRIBE requests. // // TODO: Get eventing to work with the problematic TV. t := time.Now() <-w.(http.CloseNotifier).CloseNotify() eventingLogger.Printf("stalled subscribe connection went away after %s", time.Since(t)) return } // The following code is a work in progress. It partially implements // the spec on eventing but hasn't been completed as I have nothing to // test it with. eventingLogger.Print(r.Header) service := server.services["ContentDirectory"] eventingLogger.Println(r.RemoteAddr, r.Method, r.Header.Get("SID")) if r.Method == "SUBSCRIBE" && r.Header.Get("SID") == "" { urls := upnp.ParseCallbackURLs(r.Header.Get("CALLBACK")) eventingLogger.Println(urls) var timeout int fmt.Sscanf(r.Header.Get("TIMEOUT"), "Second-%d", &timeout) eventingLogger.Println(timeout, r.Header.Get("TIMEOUT")) sid, timeout, _ := service.Subscribe(urls, timeout) w.Header()["SID"] = []string{sid} w.Header()["TIMEOUT"] = []string{fmt.Sprintf("Second-%d", timeout)} // TODO: Shouldn't have to do this to get headers logged. w.WriteHeader(http.StatusOK) go func() { time.Sleep(100 * time.Millisecond) server.contentDirectoryInitialEvent(urls, sid) }() } else if r.Method == "SUBSCRIBE" { http.Error(w, "meh", http.StatusPreconditionFailed) } else { eventingLogger.Printf("unhandled event method: %s", r.Method) } } func (server *Server) initMux(mux *http.ServeMux) { mux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) { resp.Header().Set("content-type", "text/html") err := rootTmpl.Execute(resp, struct { Readonly bool Path string }{ true, server.RootObjectPath, }) if err != nil { log.Println(err) } }) mux.HandleFunc(contentDirectoryEventSubURL, server.contentDirectoryEventSubHandler) mux.HandleFunc(iconPath, server.serveIcon) mux.HandleFunc(resPath, func(w http.ResponseWriter, r *http.Request) { filePath := server.filePath(r.URL.Query().Get("path")) if ignored, err := server.IgnorePath(filePath); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else if ignored { http.Error(w, "no such object", http.StatusNotFound) return } var k string if server.ForceTranscodeTo != "" { k = server.ForceTranscodeTo } else { k = r.URL.Query().Get("transcode") } if k == "" { mimeType, err := MimeTypeByPath(filePath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", string(mimeType)) http.ServeFile(w, r, filePath) return } if server.NoTranscode { http.Error(w, "transcodes disabled", http.StatusNotFound) return } spec, ok := transcodes[k] if !ok { http.Error(w, fmt.Sprintf("bad transcode spec key: %s", k), http.StatusBadRequest) return } server.serveDLNATranscode(w, r, filePath, spec, k) }) mux.HandleFunc(rootDescPath, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", `text/xml; charset="utf-8"`) w.Header().Set("content-length", fmt.Sprint(len(server.rootDescXML))) w.Header().Set("server", serverField) w.Write(server.rootDescXML) }) handleSCPDs(mux) mux.HandleFunc(serviceControlURL, server.serviceControlHandler) mux.HandleFunc("/debug/pprof/", pprof.Index) for i, di := range server.Icons { mux.HandleFunc(fmt.Sprintf("%s/%d", deviceIconPath, i), func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", di.Mimetype) http.ServeContent(w, r, "", time.Time{}, di.ReadSeeker) }) } } func (s *Server) initServices() (err error) { urn, err := upnp.ParseServiceType(services[0].ServiceType) if err != nil { return } s.services = map[string]UPnPService{ urn.Type: &contentDirectoryService{ Server: s, }, } return } func (srv *Server) Init() (err error) { if err = srv.initServices(); err != nil { return } srv.closed = make(chan struct{}) if srv.FriendlyName == "" { srv.FriendlyName = getDefaultFriendlyName() } if srv.HTTPConn == nil { srv.HTTPConn, err = net.Listen("tcp", "") if err != nil { return } } if srv.Interfaces == nil { ifs, err := net.Interfaces() if err != nil { log.Print(err) } var tmp []net.Interface for _, if_ := range ifs { if if_.Flags&net.FlagUp == 0 || if_.MTU <= 0 { continue } tmp = append(tmp, if_) } srv.Interfaces = tmp } if srv.FFProbeCache == nil { srv.FFProbeCache = dummyFFProbeCache{} } srv.httpServeMux = http.NewServeMux() srv.rootDeviceUUID = makeDeviceUuid(srv.FriendlyName) srv.rootDescXML, err = xml.MarshalIndent( upnp.DeviceDesc{ SpecVersion: upnp.SpecVersion{Major: 1, Minor: 0}, Device: upnp.Device{ DeviceType: rootDeviceType, FriendlyName: srv.FriendlyName, Manufacturer: "Matt Joiner ", ModelName: rootDeviceModelName, UDN: srv.rootDeviceUUID, ServiceList: func() (ss []upnp.Service) { for _, s := range services { ss = append(ss, s.Service) } return }(), IconList: func() (ret []upnp.Icon) { for i, di := range srv.Icons { ret = append(ret, upnp.Icon{ Height: di.Height, Width: di.Width, Depth: di.Depth, Mimetype: di.Mimetype, URL: fmt.Sprintf("%s/%d", deviceIconPath, i), }) } return }(), }, }, " ", " ") if err != nil { return } srv.rootDescXML = append([]byte(``), srv.rootDescXML...) log.Println("HTTP srv on", srv.HTTPConn.Addr()) srv.initMux(srv.httpServeMux) srv.ssdpStopped = make(chan struct{}) return nil } // Deprecated: Use Init and then Run. There's a race calling Close on a Server that's had Serve // called on it. func (srv *Server) Serve() (err error) { err = srv.Init() if err != nil { return } return srv.Run() } func (srv *Server) Run() (err error) { go func() { srv.doSSDP() close(srv.ssdpStopped) }() return srv.serveHTTP() } func (srv *Server) Close() (err error) { close(srv.closed) err = srv.HTTPConn.Close() <-srv.ssdpStopped return } func didl_lite(chardata string) string { return `` + chardata + `` } func (me *Server) location(ip net.IP) string { url := url.URL{ Scheme: "http", Host: (&net.TCPAddr{ IP: ip, Port: me.httpPort(), }).String(), Path: rootDescPath, } return url.String() } // Can return nil info with nil err if an earlier Probe gave an error. func (srv *Server) ffmpegProbe(path string) (info *ffprobe.Info, err error) { // We don't want relative paths in the cache. path, err = filepath.Abs(path) if err != nil { return } fi, err := os.Stat(path) if err != nil { return } key := ffmpegInfoCacheKey{path, fi.ModTime().UnixNano()} value, ok := srv.FFProbeCache.Get(key) if !ok { info, err = ffprobe.Run(path) err = suppressFFmpegProbeDataErrors(err) srv.FFProbeCache.Set(key, info) return } info = value.(*ffprobe.Info) return } // IgnorePath detects if a file/directory should be ignored. func (server *Server) IgnorePath(path string) (bool, error) { if !filepath.IsAbs(path) { return false, fmt.Errorf("Path must be absolute: %s", path) } if server.IgnoreHidden { if hidden, err := isHiddenPath(path); err != nil { return false, err } else if hidden { log.Print(path, " ignored: hidden") return true, nil } } if server.IgnoreUnreadable { if readable, err := isReadablePath(path); err != nil { return false, err } else if !readable { log.Print(path, " ignored: unreadable") return true, nil } } return false, nil } func tryToOpenPath(path string) (bool, error) { // Ugly but portable way to check if we can open a file/directory if fh, err := os.Open(path); err == nil { fh.Close() return true, nil } else if !os.IsPermission(err) { return false, err } return false, nil } dms-1.1.0/dlna/dms/dms_others.go000066400000000000000000000002721356070553200165020ustar00rootroot00000000000000//+build !unix,!windows package dms func isHiddenPath(path string) (bool, error) { return false, nil } func isReadablePath(path string) (bool, error) { return tryToOpenPath(path) } dms-1.1.0/dlna/dms/dms_test.go000066400000000000000000000032041356070553200161530ustar00rootroot00000000000000package dms import ( "bytes" "net/http" "runtime" "testing" ) type safeFilePathTestCase struct { root, given, expected string } func TestSafeFilePath(t *testing.T) { var cases []safeFilePathTestCase if runtime.GOOS == "windows" { cases = []safeFilePathTestCase{ {"c:", "/", "c:."}, {"c:", "/test", "c:test"}, {"c:\\", "/", "c:\\"}, {"c:\\", "/test", "c:\\test"}, {"c:\\hello", "../windows", "c:\\hello\\windows"}, {"c:\\hello", "/../windows", "c:\\hello\\windows"}, {"c:\\hello", "/", "c:\\hello"}, {"c:\\hello", "./world", "c:\\hello\\world"}, {"c:\\hello", "/", "c:\\hello"}, // These two ones are invalid but, as this actually prevents to serve them, it is fine {"c:\\foo", "c:/windows/", "c:\\foo\\c:\\windows"}, {"c:\\foo", "e:/", "c:\\foo\\e:"}, } } else { cases = []safeFilePathTestCase{ {"/", "..", "/"}, {"/hello", "..//", "/hello"}, {"", "/precious", "precious"}, {".", "///precious", "precious"}, } } t.Logf("running %d test cases", len(cases)) for _, _case := range cases { a := safeFilePath(_case.root, _case.given) if a != _case.expected { t.Errorf("expected %q from %q and %q but got %q", _case.expected, _case.root, _case.given, a) } } } func TestRequest(t *testing.T) { resp, err := http.NewRequest("NOTIFY", "/", nil) if err != nil { t.Fatal(err) } buf := bytes.NewBuffer(nil) resp.Write(buf) t.Logf("%q", buf.String()) } func TestResponse(t *testing.T) { var resp http.Response resp.StatusCode = http.StatusOK resp.Header = make(http.Header) resp.Header["SID"] = []string{"uuid:1337"} var buf bytes.Buffer resp.Write(&buf) t.Logf("%q", buf.String()) } dms-1.1.0/dlna/dms/dms_unix.go000066400000000000000000000005651356070553200161660ustar00rootroot00000000000000//+build unix package dms import ( "strings" "golang.org/x/sys/unix" ) func isHiddenPath(path string) (bool, error) { return strings.Contains(path, "/."), nil } func isReadablePath(path string) (bool, error) { err := unix.Access(path, unix.R_OK) switch err { case nil: return true, nil case unix.EACCES: return false, nil default: return false, err } } dms-1.1.0/dlna/dms/dms_unix_test.go000066400000000000000000000010231356070553200172130ustar00rootroot00000000000000//+build unix package dms import "testing" func TestIsHiddenPath(t *testing.T) { var data = map[string]bool{ "/some/path": false, "/some/foo.bar": false, "/some/path/.hidden": true, "/some/.hidden/path": true, "/.hidden/path": true, } for path, expected := range data { if actual, err := isHiddenPath(path); err != nil { t.Errorf("isHiddenPath(%v) returned unexpected error: %s", path, err) ] else if expected != actual { t.Errorf("isHiddenPath(%v), expected %v, got %v", path, expected, actual) } } } dms-1.1.0/dlna/dms/dms_windows.go000066400000000000000000000013131356070553200166650ustar00rootroot00000000000000//+build windows package dms import ( "path/filepath" "golang.org/x/sys/windows" ) const hiddenAttributes = windows.FILE_ATTRIBUTE_HIDDEN | windows.FILE_ATTRIBUTE_SYSTEM func isHiddenPath(path string) (hidden bool, err error) { if path == filepath.VolumeName(path)+"\\" { // Volumes always have the "SYSTEM" flag, so do not even test them return false, nil } winPath, err := windows.UTF16PtrFromString(path) if err != nil { return } attrs, err := windows.GetFileAttributes(winPath) if err != nil { return } if attrs&hiddenAttributes != 0 { hidden = true return } return isHiddenPath(filepath.Dir(path)) } func isReadablePath(path string) (bool, error) { return tryToOpenPath(path) } dms-1.1.0/dlna/dms/ffmpeg.go000066400000000000000000000007041356070553200155770ustar00rootroot00000000000000package dms import ( "os/exec" "runtime" "syscall" ) func suppressFFmpegProbeDataErrors(_err error) (err error) { if _err == nil { return } err = _err exitErr, ok := err.(*exec.ExitError) if !ok { return } waitStat, ok := exitErr.Sys().(syscall.WaitStatus) if !ok { return } code := waitStat.ExitStatus() if runtime.GOOS == "windows" { if code == -1094995529 { err = nil } } else if code == 183 { err = nil } return } dms-1.1.0/dlna/dms/html.go000066400000000000000000000006071356070553200153010ustar00rootroot00000000000000package dms import ( "html/template" ) var ( rootTmpl *template.Template ) func init() { rootTmpl = template.Must(template.New("root").Parse( `
Path:
`)) } dms-1.1.0/dlna/dms/mimetype.go000066400000000000000000000043121356070553200161630ustar00rootroot00000000000000package dms import ( "log" "mime" "net/http" "os" "path" "strings" ) func init() { if err := mime.AddExtensionType(".rmvb", "application/vnd.rn-realmedia-vbr"); err != nil { log.Printf("Could not register application/vnd.rn-realmedia-vbr MIME type: %s", err) } if err := mime.AddExtensionType(".ogv", "video/ogg"); err != nil { log.Printf("Could not register video/ogg MIME type: %s", err) } } // Example: "video/mpeg" type mimeType string // IsMedia returns true for media MIME-types func (mt mimeType) IsMedia() bool { return mt.IsVideo() || mt.IsAudio() || mt.IsImage() } // IsVideo returns true for video MIME-types func (mt mimeType) IsVideo() bool { return strings.HasPrefix(string(mt), "video/") || mt == "application/vnd.rn-realmedia-vbr" } // IsAudio returns true for audio MIME-types func (mt mimeType) IsAudio() bool { return strings.HasPrefix(string(mt), "audio/") } // IsImage returns true for image MIME-types func (mt mimeType) IsImage() bool { return strings.HasPrefix(string(mt), "image/") } // Returns the group "type", the part before the '/'. func (mt mimeType) Type() string { return strings.SplitN(string(mt), "/", 2)[0] } // Returns the string representation of this MIME-type func (mt mimeType) String() string { return string(mt) } // MimeTypeByPath determines the MIME-type of file at the given path func MimeTypeByPath(filePath string) (ret mimeType, err error) { ret = mimeTypeByBaseName(path.Base(filePath)) if ret == "" { ret, err = mimeTypeByContent(filePath) } if ret == "video/x-msvideo" { ret = "video/avi" } else if ret == "" { ret = "application/octet-stream" } return } // Guess MIME-type from the extension, ignoring ".part". func mimeTypeByBaseName(name string) mimeType { name = strings.TrimSuffix(name, ".part") ext := path.Ext(name) if ext != "" { return mimeType(mime.TypeByExtension(ext)) } return mimeType("") } // Guess the MIME-type by analysing the first 512 bytes of the file. func mimeTypeByContent(path string) (ret mimeType, err error) { file, err := os.Open(path) if err != nil { return } defer file.Close() var data [512]byte if n, err := file.Read(data[:]); err == nil { ret = mimeType(http.DetectContentType(data[:n])) } return } dms-1.1.0/go.mod000066400000000000000000000005701356070553200134120ustar00rootroot00000000000000module github.com/anacrolix/dms go 1.12 require ( github.com/anacrolix/ffprobe v1.0.0 github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2 golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd ) dms-1.1.0/go.sum000066400000000000000000000111131356070553200134320ustar00rootroot00000000000000github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v1.0.0 h1:AwZ+mBP4rQ5f7JSsrsN3h7M2xDW/xSE66IPVOqlnuUc= github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/ffprobe v1.0.0 h1:j8fGLBsXejwdXd0pkA9iR3Dt1XwMFv5wjeYWObcue8A= github.com/anacrolix/ffprobe v1.0.0/go.mod h1:BIw+Bjol6CWjm/CRWrVLk2Vy+UYlkgmBZ05vpSYqZPw= github.com/anacrolix/missinggo v1.1.0 h1:0lZbaNa6zTR1bELAIzCNmRGAtkHuLDPJqTiTtXoAIx8= github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c h1:FUUopH4brHNO2kJoNN3pV+OBEYmgraLT/KHZrMM69r0= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2 h1:iC0Y6EDq+rhnAePxGvJs2kzUAYcwESqdcGRPzEUfzTU= golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd h1:MNN7PRW7zYXd8upVO5qfKeOnQG74ivRNv7sz4k4cQMs= golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= dms-1.1.0/main.go000066400000000000000000000207501356070553200135610ustar00rootroot00000000000000package main //go:generate go-bindata data/ import ( "bytes" "encoding/json" "flag" "image" "image/png" "io" "io/ioutil" "log" "net" "os" "os/signal" "os/user" "path/filepath" "runtime" "strings" "sync" "syscall" "time" "github.com/anacrolix/dms/dlna/dms" "github.com/anacrolix/dms/rrcache" "github.com/nfnt/resize" ) type dmsConfig struct { Path string IfName string Http string FriendlyName string DeviceIcon string LogHeaders bool FFprobeCachePath string NoTranscode bool ForceTranscodeTo string NoProbe bool StallEventSubscribe bool NotifyInterval time.Duration IgnoreHidden bool IgnoreUnreadable bool AllowedIpNets []*net.IPNet } func (config *dmsConfig) load(configPath string) { file, err := os.Open(configPath) if err != nil { log.Printf("config error (config file: '%s'): %v\n", configPath, err) return } defer file.Close() decoder := json.NewDecoder(file) err = decoder.Decode(&config) if err != nil { log.Printf("config error: %v\n", err) return } } //default config var config = &dmsConfig{ Path: "", IfName: "", Http: ":1338", FriendlyName: "", DeviceIcon: "", LogHeaders: false, FFprobeCachePath: getDefaultFFprobeCachePath(), ForceTranscodeTo: "", } func getDefaultFFprobeCachePath() (path string) { _user, err := user.Current() if err != nil { log.Print(err) return } path = filepath.Join(_user.HomeDir, ".dms-ffprobe-cache") return } type fFprobeCache struct { c *rrcache.RRCache sync.Mutex } func (fc *fFprobeCache) Get(key interface{}) (value interface{}, ok bool) { fc.Lock() defer fc.Unlock() return fc.c.Get(key) } func (fc *fFprobeCache) Set(key interface{}, value interface{}) { fc.Lock() defer fc.Unlock() var size int64 for _, v := range []interface{}{key, value} { b, err := json.Marshal(v) if err != nil { log.Printf("Could not marshal %v: %s", v, err) continue } size += int64(len(b)) } fc.c.Set(key, value, size) } func main() { log.SetFlags(log.Ltime | log.Lshortfile) path := flag.String("path", config.Path, "browse root path") ifName := flag.String("ifname", config.IfName, "specific SSDP network interface") http := flag.String("http", config.Http, "http server port") friendlyName := flag.String("friendlyName", config.FriendlyName, "server friendly name") deviceIcon := flag.String("deviceIcon", config.DeviceIcon, "device icon") logHeaders := flag.Bool("logHeaders", config.LogHeaders, "log HTTP headers") fFprobeCachePath := flag.String("fFprobeCachePath", config.FFprobeCachePath, "path to FFprobe cache file") configFilePath := flag.String("config", "", "json configuration file") allowedIps := flag.String("allowedIps", "", "allowed ip of clients, separated by comma") forceTranscodeTo := flag.String("forceTranscodeTo", config.ForceTranscodeTo, "force transcoding to certain format, supported: 'chromecast', 'vp8'") flag.BoolVar(&config.NoTranscode, "noTranscode", false, "disable transcoding") flag.BoolVar(&config.NoProbe, "noProbe", false, "disable media probing with ffprobe") flag.BoolVar(&config.StallEventSubscribe, "stallEventSubscribe", false, "workaround for some bad event subscribers") flag.DurationVar(&config.NotifyInterval, "notifyInterval", 30*time.Second, "interval between SSPD announces") flag.BoolVar(&config.IgnoreHidden, "ignoreHidden", false, "ignore hidden files and directories") flag.BoolVar(&config.IgnoreUnreadable, "ignoreUnreadable", false, "ignore unreadable files and directories") flag.Parse() if flag.NArg() != 0 { flag.Usage() log.Fatalf("%s: %s\n", "unexpected positional arguments", flag.Args()) } config.Path = *path config.IfName = *ifName config.Http = *http config.FriendlyName = *friendlyName config.DeviceIcon = *deviceIcon config.LogHeaders = *logHeaders config.FFprobeCachePath = *fFprobeCachePath config.AllowedIpNets = makeIpNets(*allowedIps) config.ForceTranscodeTo = *forceTranscodeTo // if len(config.AllowedIps) > 0 { log.Printf("allowed ip nets are %q", config.AllowedIpNets) // } if len(*configFilePath) > 0 { config.load(*configFilePath) } cache := &fFprobeCache{ c: rrcache.New(64 << 20), } if err := cache.load(config.FFprobeCachePath); err != nil { log.Print(err) } dmsServer := &dms.Server{ Interfaces: func(ifName string) (ifs []net.Interface) { var err error if ifName == "" { ifs, err = net.Interfaces() } else { var if_ *net.Interface if_, err = net.InterfaceByName(ifName) if if_ != nil { ifs = append(ifs, *if_) } } if err != nil { log.Fatal(err) } var tmp []net.Interface for _, if_ := range ifs { if if_.Flags&net.FlagUp == 0 || if_.MTU <= 0 { continue } tmp = append(tmp, if_) } ifs = tmp return }(config.IfName), HTTPConn: func() net.Listener { conn, err := net.Listen("tcp", config.Http) if err != nil { log.Fatal(err) } return conn }(), FriendlyName: config.FriendlyName, RootObjectPath: filepath.Clean(config.Path), FFProbeCache: cache, LogHeaders: config.LogHeaders, NoTranscode: config.NoTranscode, ForceTranscodeTo: config.ForceTranscodeTo, NoProbe: config.NoProbe, Icons: []dms.Icon{ dms.Icon{ Width: 48, Height: 48, Depth: 8, Mimetype: "image/png", ReadSeeker: readIcon(config.DeviceIcon, 48), }, dms.Icon{ Width: 128, Height: 128, Depth: 8, Mimetype: "image/png", ReadSeeker: readIcon(config.DeviceIcon, 128), }, }, StallEventSubscribe: config.StallEventSubscribe, NotifyInterval: config.NotifyInterval, IgnoreHidden: config.IgnoreHidden, IgnoreUnreadable: config.IgnoreUnreadable, AllowedIpNets: config.AllowedIpNets, } if err := dmsServer.Init(); err != nil { log.Fatalf("error initing dms server: %v", err) } go func() { if err := dmsServer.Run(); err != nil { log.Fatal(err) } }() sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) <-sigs err := dmsServer.Close() if err != nil { log.Fatal(err) } if err := cache.save(config.FFprobeCachePath); err != nil { log.Print(err) } } func (cache *fFprobeCache) load(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() dec := json.NewDecoder(f) var items []dms.FfprobeCacheItem err = dec.Decode(&items) if err != nil { return err } for _, item := range items { cache.Set(item.Key, item.Value) } log.Printf("added %d items from cache", len(items)) return nil } func (cache *fFprobeCache) save(path string) error { cache.Lock() items := cache.c.Items() cache.Unlock() f, err := ioutil.TempFile(filepath.Dir(path), filepath.Base(path)) if err != nil { return err } enc := json.NewEncoder(f) err = enc.Encode(items) f.Close() if err != nil { os.Remove(f.Name()) return err } if runtime.GOOS == "windows" { err = os.Remove(path) if err == os.ErrNotExist { err = nil } } if err == nil { err = os.Rename(f.Name(), path) } if err == nil { log.Printf("saved cache with %d items", len(items)) } else { os.Remove(f.Name()) } return err } func getIconReader(path string) (io.ReadCloser, error) { if path == "" { return ioutil.NopCloser(bytes.NewReader(MustAsset("data/VGC Sonic.png"))), nil } return os.Open(path) } func readIcon(path string, size uint) *bytes.Reader { r, err := getIconReader(path) if err != nil { panic(err) } defer r.Close() imageData, _, err := image.Decode(r) if err != nil { panic(err) } return resizeImage(imageData, size) } func resizeImage(imageData image.Image, size uint) *bytes.Reader { img := resize.Resize(size, size, imageData, resize.Lanczos3) var buff bytes.Buffer png.Encode(&buff, img) return bytes.NewReader(buff.Bytes()) } func makeIpNets(s string) []*net.IPNet { var nets []*net.IPNet if len(s) < 1 { _, ipnet, _ := net.ParseCIDR("0.0.0.0/0") nets = append(nets, ipnet) _, ipnet, _ = net.ParseCIDR("0:0:0::/128") nets = append(nets, ipnet) } else { for _, el := range strings.Split(s, ",") { ip := net.ParseIP(el) if ip == nil { _, ipnet, err := net.ParseCIDR(el) if err == nil { nets = append(nets, ipnet) } else { log.Printf("unable to parse expression %q", el) } } else { _, ipnet, err := net.ParseCIDR(el + "/32") if err == nil { nets = append(nets, ipnet) } else { log.Printf("unable to parse ip %q", el) } } } } return nets } dms-1.1.0/misc/000077500000000000000000000000001356070553200132355ustar00rootroot00000000000000dms-1.1.0/misc/dms-win32/000077500000000000000000000000001356070553200147605ustar00rootroot00000000000000dms-1.1.0/misc/dms-win32/NOTES000066400000000000000000000005751356070553200156020ustar00rootroot00000000000000This directory should contain a script to build the "dms-win32" package. It's an archive containing the DMS GUI components and all necessary libraries. The script should get the latest static executable builds of ffmpeg, dms, and GTK+ all-in-one into a single folder, with executable files in bin/, and user-facing utilities from this directory in the root, and compress the lot. dms-1.1.0/misc/dms-win32/README.txt000066400000000000000000000003031356070553200164520ustar00rootroot00000000000000Double click the batch file "dms-gui". Select a media directory to share, and enjoy from a DLNA/UPnP enabled device. Try BubbleUPnP from an Android, or VLC's "Local Network>UPnP" interface.dms-1.1.0/misc/dms-win32/dms-gui.bat000066400000000000000000000000511356070553200170110ustar00rootroot00000000000000set PATH=%~dp0\bin start dms-gtk-gui.exedms-1.1.0/misc/misc.go000066400000000000000000000005241356070553200145200ustar00rootroot00000000000000package misc import ( "fmt" "strings" "time" ) func FormatDurationSexagesimal(d time.Duration) string { ns := d % time.Second d /= time.Second s := d % 60 d /= 60 m := d % 60 d /= 60 h := d ret := fmt.Sprintf("%d:%02d:%02d.%09d", h, m, s, ns) ret = strings.TrimRight(ret, "0") ret = strings.TrimRight(ret, ".") return ret } dms-1.1.0/misc/misc_test.go000066400000000000000000000004231356070553200155550ustar00rootroot00000000000000package misc import ( "testing" "time" ) func TestFormatDurationSexagesimal(t *testing.T) { expected := "0:22:57.628452" actual := FormatDurationSexagesimal(time.Duration(1377628452000)) if actual != expected { t.Fatalf("got %q, expected %q", actual, expected) } } dms-1.1.0/play/000077500000000000000000000000001356070553200132475ustar00rootroot00000000000000dms-1.1.0/play/attrs.go000066400000000000000000000005331356070553200147340ustar00rootroot00000000000000// +build ignore package main import ( "encoding/xml" "fmt" ) type Meh struct { XMLName xml.Name Size //ChildCount *uint `xml:"childCount,attr"` } func main() { size := uint64(137) data, err := xml.Marshal(Meh{ Size: &xml.Attr{ Name: xml.Name{Local: "size"}, Value: fmt.Sprint(size), }, }) fmt.Println(string(data), err) } dms-1.1.0/play/bool.go000066400000000000000000000001361356070553200145310ustar00rootroot00000000000000// +build ignore package main import "fmt" func main() { fmt.Printf("%q%c\n", 3, false) } dms-1.1.0/play/browse.xml000066400000000000000000000012361356070553200152740ustar00rootroot00000000000000 0 BrowseDirectChildren dc:title,dc:date,res,res@protocolInfo,res@size,res@duration,res@resolution,res@dlna:ifoFileURI,res@pv:subtitleFileType,res@pv:subtitleFileUri,upnp:albumArtURI,upnp:album,upnp:artist 0 20 dms-1.1.0/play/closure.go000066400000000000000000000004671356070553200152610ustar00rootroot00000000000000// +build ignore package main import ( "fmt" ) func main() { ch := make(chan struct{}) for i := 0; i < 5; i++ { j := i go func() { fmt.Print(j) ch <- struct{}{} }() } for i := 5; i < 10; i++ { go func() { fmt.Print(i) ch <- struct{}{} }() } for i := 0; i < 10; i++ { <-ch } } dms-1.1.0/play/execbug.go000066400000000000000000000005361356070553200152240ustar00rootroot00000000000000// +build ignore package main import ( "io" "os" "os/exec" "time" ) func main() { cmd := exec.Command("ls") out, err := cmd.StdoutPipe() if err != nil { panic(err) } if err = cmd.Start(); err != nil { panic(err) } go cmd.Wait() time.Sleep(10 * time.Millisecond) _, err = io.Copy(os.Stdout, out) if err != nil { panic(err) } } dms-1.1.0/play/execgood.go000066400000000000000000000007011356070553200153710ustar00rootroot00000000000000// +build ignore package main import ( "io" "log" "os" "os/exec" "time" ) func main() { cmd := exec.Command("ls") out, err := cmd.StdoutPipe() if err != nil { panic(err) } if err := cmd.Start(); err != nil { panic(err) } r, w := io.Pipe() go func() { io.Copy(w, out) out.Close() w.Close() log.Println(cmd.Wait()) }() time.Sleep(10 * time.Millisecond) if _, err := io.Copy(os.Stdout, r); err != nil { panic(err) } } dms-1.1.0/play/ffprobe.go000066400000000000000000000003761356070553200152270ustar00rootroot00000000000000// +build ignore package main import ( "flag" "log" "github.com/anacrolix/ffprobe" ) func main() { log.SetFlags(log.Llongfile) flag.Parse() for _, path := range flag.Args() { i, err := ffprobe.Probe(path) log.Printf("%#v %#v", i, err) } } dms-1.1.0/play/getsortcaps.xml000066400000000000000000000004641356070553200163330ustar00rootroot00000000000000 dms-1.1.0/play/mime.go000066400000000000000000000003141356070553200145230ustar00rootroot00000000000000// +build ignore package main import ( "flag" "fmt" "github.com/anacrolix/dms/dlna/dms" ) func main() { flag.Parse() for _, arg := range flag.Args() { fmt.Println(dms.MimeTypeByPath(arg)) } } dms-1.1.0/play/parse_http_version.go000066400000000000000000000002361356070553200175150ustar00rootroot00000000000000// +build ignore package main import ( "fmt" "net/http" "strings" ) func main() { fmt.Println(http.ParseHTTPVersion(strings.TrimSpace("HTTP/1.1 "))) } dms-1.1.0/play/print-ifs.go000066400000000000000000000007521356070553200155150ustar00rootroot00000000000000// +build ignore package main import ( "fmt" "net" ) func main() { ifs, err := net.Interfaces() if err != nil { panic(err) } for _, if_ := range ifs { fmt.Printf("%#v\n", if_) addrs, err := if_.Addrs() if err != nil { panic(err) } for _, addr := range addrs { fmt.Printf("\t%s %s\n", addr.Network(), addr) } mcastAddrs, err := if_.MulticastAddrs() if err != nil { panic(err) } for _, addr := range mcastAddrs { fmt.Printf("\t%s\n", addr) } } } dms-1.1.0/play/scpd.go000066400000000000000000000013221356070553200145250ustar00rootroot00000000000000// +build ignore package main import ( "encoding/xml" "fmt" "log" "github.com/anacrolix/dms/upnp" ) func main() { scpd := upnp.SCPD{ SpecVersion: upnp.SpecVersion{Major: 1, Minor: 0}, ActionList: []upnp.Action{ { Name: "Browse", Arguments: []upnp.Argument{ {Name: "ObjectID", Direction: "in", RelatedStateVar: "A_ARG_TYPE_ObjectID"}, }, }, }, ServiceStateTable: []upnp.StateVariable{ {SendEvents: "no", Name: "A_ARG_TYPE_ObjectID", DataType: "string", AllowedValues: &[]string{"hi", "there"}}, { SendEvents: "yes", Name: "loltype", }, }, } xml, err := xml.MarshalIndent(scpd, "", " ") if err != nil { log.Fatalln(err) } fmt.Print(string(xml)) } dms-1.1.0/play/soap.go000066400000000000000000000016071356070553200145440ustar00rootroot00000000000000// +build ignore package main import ( "encoding/xml" "fmt" "io/ioutil" "os" "github.com/anacrolix/dms/soap" ) type Browse struct { ObjectID string BrowseFlag string Filter string StartingIndex int RequestedCount int } type GetSortCapabilitiesResponse struct { XMLName xml.Name `xml:"urn:schemas-upnp-org:service:ContentDirectory:1 GetSortCapabilitiesResponse"` SortCaps string } func main() { raw, err := ioutil.ReadAll(os.Stdin) if err != nil { panic(err) } var env soap.Envelope if err := xml.Unmarshal(raw, &env); err != nil { panic(err) } fmt.Println(env) var browse Browse err = xml.Unmarshal([]byte(env.Body.Action), &browse) if err != nil { panic(err) } fmt.Println(browse) raw, err = xml.MarshalIndent( GetSortCapabilitiesResponse{ SortCaps: "dc:title", }, "", " ") if err != nil { panic(err) } fmt.Println(string(raw)) } dms-1.1.0/play/termsig/000077500000000000000000000000001356070553200147215ustar00rootroot00000000000000dms-1.1.0/play/termsig/main.go000066400000000000000000000002401356070553200161700ustar00rootroot00000000000000package main import ( "fmt" "os" "os/signal" ) func main() { c := make(chan os.Signal, 0x100) signal.Notify(c) for i := range c { fmt.Println(i) } } dms-1.1.0/play/transcode.go000066400000000000000000000011311356070553200155540ustar00rootroot00000000000000// +build ignore package main import ( "bufio" "flag" "io" "log" "os" "time" "github.com/anacrolix/dms/misc" ) func main() { ss := flag.String("ss", "", "") t := flag.String("t", "", "") flag.Parse() if flag.NArg() != 1 { log.Fatalln("wrong argument count") } r, err := misc.Transcode(flag.Arg(0), *ss, *t) if err != nil { log.Fatalln(err) } go func() { buf := bufio.NewWriterSize(os.Stdout, 1234) n, err := io.Copy(buf, r) log.Println("copied", n, "bytes") if err != nil { log.Println(err) } }() time.Sleep(time.Second) go r.Close() time.Sleep(time.Second) } dms-1.1.0/play/url.go000066400000000000000000000003111356070553200143730ustar00rootroot00000000000000// +build ignore package main import ( "fmt" "log" "net/url" ) func main() { url_, err := url.Parse("[192:168:26:2::3]:1900") if err != nil { log.Fatalln(err) } fmt.Printf("%#v\n", url_) } dms-1.1.0/rrcache/000077500000000000000000000000001356070553200137115ustar00rootroot00000000000000dms-1.1.0/rrcache/rrcache.go000066400000000000000000000030531356070553200156500ustar00rootroot00000000000000// Package rrcache implements a random replacement cache. Items are set with // an associated size. When the capacity is exceeded, items will be randomly // evicted until it is not. package rrcache import ( "math/rand" ) type RRCache struct { capacity int64 size int64 keys []interface{} table map[interface{}]*entry } type entry struct { size int64 value interface{} } func New(capacity int64) *RRCache { return &RRCache{ capacity: capacity, table: make(map[interface{}]*entry), } } // Returns the sum size of all items currently in the cache. func (c *RRCache) Size() int64 { return c.size } func (c *RRCache) Set(key interface{}, value interface{}, size int64) { if size > c.capacity { return } _entry := c.table[key] if _entry == nil { _entry = new(entry) c.keys = append(c.keys, key) c.table[key] = _entry } sizeDelta := size - _entry.size _entry.value = value _entry.size = size c.size += sizeDelta for c.size > c.capacity { i := rand.Intn(len(c.keys)) key := c.keys[i] c.keys[i] = c.keys[len(c.keys)-1] c.keys = c.keys[:len(c.keys)-1] c.size -= c.table[key].size delete(c.table, key) } } func (c *RRCache) Get(key interface{}) (value interface{}, ok bool) { entry, ok := c.table[key] if !ok { return } value = entry.value return } type Item struct { Key, Value interface{} } // Return all items currently in the cache. This is made available for // serialization purposes. func (c *RRCache) Items() (itens []Item) { for k, e := range c.table { itens = append(itens, Item{k, e.value}) } return } dms-1.1.0/soap/000077500000000000000000000000001356070553200132445ustar00rootroot00000000000000dms-1.1.0/soap/soap.go000066400000000000000000000026521356070553200145420ustar00rootroot00000000000000package soap import ( "encoding/xml" ) const ( EncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/" EnvelopeNS = "http://schemas.xmlsoap.org/soap/envelope/" ) type Arg struct { XMLName xml.Name Value string `xml:",chardata"` } type Action struct { XMLName xml.Name Args []Arg } type Body struct { Action []byte `xml:",innerxml"` } type UPnPError struct { XMLName xml.Name `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"` Code uint `xml:"errorCode"` Desc string `xml:"errorDescription"` } type FaultDetail struct { XMLName xml.Name `xml:"detail"` Data interface{} } type Fault struct { XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"` FaultCode string `xml:"faultcode"` FaultString string `xml:"faultstring"` Detail FaultDetail `xml:"detail"` } func NewFault(s string, detail interface{}) *Fault { return &Fault{ FaultCode: EnvelopeNS + ":Client", FaultString: s, Detail: FaultDetail{ Data: detail, }, } } type Envelope struct { XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` EncodingStyle string `xml:"encodingStyle,attr"` Body Body `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` } /* XML marshalling of nested namespaces is broken. func NewEnvelope(action []byte) Envelope { return Envelope{ EncodingStyle: EncodingStyle, Body: Body{action}, } } */ dms-1.1.0/ssdp/000077500000000000000000000000001356070553200132535ustar00rootroot00000000000000dms-1.1.0/ssdp/ssdp.go000066400000000000000000000156411356070553200145620ustar00rootroot00000000000000package ssdp import ( "bufio" "bytes" "fmt" "io" "log" "math/rand" "net" "net/http" "net/textproto" "strconv" "strings" "time" "golang.org/x/net/ipv4" ) const ( AddrString = "239.255.255.250:1900" rootDevice = "upnp:rootdevice" aliveNTS = "ssdp:alive" byebyeNTS = "ssdp:byebye" ) var ( NetAddr *net.UDPAddr ) func init() { var err error NetAddr, err = net.ResolveUDPAddr("udp4", AddrString) if err != nil { log.Panicf("Could not resolve %s: %s", AddrString, err) } } type badStringError struct { what string str string } func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) } func ReadRequest(b *bufio.Reader) (req *http.Request, err error) { tp := textproto.NewReader(b) var s string if s, err = tp.ReadLine(); err != nil { return nil, err } defer func() { if err == io.EOF { err = io.ErrUnexpectedEOF } }() var f []string // TODO a split that only allows N values? if f = strings.SplitN(s, " ", 3); len(f) < 3 { return nil, &badStringError{"malformed request line", s} } if f[1] != "*" { return nil, &badStringError{"bad URL request", f[1]} } req = &http.Request{ Method: f[0], } var ok bool if req.ProtoMajor, req.ProtoMinor, ok = http.ParseHTTPVersion(strings.TrimSpace(f[2])); !ok { return nil, &badStringError{"malformed HTTP version", f[2]} } mimeHeader, err := tp.ReadMIMEHeader() if err != nil { return nil, err } req.Header = http.Header(mimeHeader) return } type Server struct { conn *net.UDPConn Interface net.Interface Server string Services []string Devices []string Location func(net.IP) string UUID string NotifyInterval time.Duration closed chan struct{} } func makeConn(ifi net.Interface) (ret *net.UDPConn, err error) { ret, err = net.ListenMulticastUDP("udp", &ifi, NetAddr) if err != nil { return } p := ipv4.NewPacketConn(ret) if err := p.SetMulticastTTL(2); err != nil { log.Println(err) } if err := p.SetMulticastLoopback(true); err != nil { log.Println(err) } return } func (me *Server) serve() { for { b := make([]byte, me.Interface.MTU) n, addr, err := me.conn.ReadFromUDP(b) select { case <-me.closed: return default: } if err != nil { log.Printf("error reading from UDP socket: %s", err) break } go me.handle(b[:n], addr) } } func (me *Server) Init() (err error) { me.closed = make(chan struct{}) me.conn, err = makeConn(me.Interface) return } func (me *Server) Close() { close(me.closed) me.sendByeBye() me.conn.Close() } func (me *Server) Serve() (err error) { go me.serve() for { addrs, err := me.Interface.Addrs() if err != nil { return err } for _, addr := range addrs { ip := func() net.IP { switch val := addr.(type) { case *net.IPNet: return val.IP case *net.IPAddr: return val.IP } panic(fmt.Sprint("unexpected addr type:", addr)) }() extraHdrs := [][2]string{ {"CACHE-CONTROL", fmt.Sprintf("max-age=%d", 5*me.NotifyInterval/2/time.Second)}, {"LOCATION", me.Location(ip)}, } me.notifyAll(aliveNTS, extraHdrs) } time.Sleep(me.NotifyInterval) } } func (me *Server) usnFromTarget(target string) string { if target == me.UUID { return target } return me.UUID + "::" + target } func (me *Server) makeNotifyMessage(target, nts string, extraHdrs [][2]string) []byte { lines := [...][2]string{ {"HOST", AddrString}, {"NT", target}, {"NTS", nts}, {"SERVER", me.Server}, {"USN", me.usnFromTarget(target)}, } buf := &bytes.Buffer{} fmt.Fprint(buf, "NOTIFY * HTTP/1.1\r\n") writeHdr := func(keyValue [2]string) { fmt.Fprintf(buf, "%s: %s\r\n", keyValue[0], keyValue[1]) } for _, pair := range lines { writeHdr(pair) } for _, pair := range extraHdrs { writeHdr(pair) } fmt.Fprint(buf, "\r\n") return buf.Bytes() } func (me *Server) send(buf []byte, addr *net.UDPAddr) { if n, err := me.conn.WriteToUDP(buf, addr); err != nil { log.Printf("error writing to UDP socket: %s", err) } else if n != len(buf) { log.Printf("short write: %d/%d bytes", n, len(buf)) } } func (me *Server) delayedSend(delay time.Duration, buf []byte, addr *net.UDPAddr) { go func() { select { case <-time.After(delay): me.send(buf, addr) case <-me.closed: } }() } func (me *Server) log(args ...interface{}) { args = append([]interface{}{me.Interface.Name + ":"}, args...) log.Print(args...) } func (me *Server) sendByeBye() { for _, type_ := range me.allTypes() { buf := me.makeNotifyMessage(type_, byebyeNTS, nil) me.send(buf, NetAddr) } } func (me *Server) notifyAll(nts string, extraHdrs [][2]string) { for _, type_ := range me.allTypes() { buf := me.makeNotifyMessage(type_, nts, extraHdrs) delay := time.Duration(rand.Int63n(int64(100 * time.Millisecond))) me.delayedSend(delay, buf, NetAddr) } } func (me *Server) allTypes() (ret []string) { for _, a := range [][]string{ {rootDevice, me.UUID}, me.Devices, me.Services, } { ret = append(ret, a...) } return } func (me *Server) handle(buf []byte, sender *net.UDPAddr) { req, err := ReadRequest(bufio.NewReader(bytes.NewReader(buf))) if err != nil { log.Println(err) return } if req.Method != "M-SEARCH" || req.Header.Get("man") != `"ssdp:discover"` { return } var mx uint if req.Header.Get("Host") == AddrString { mxHeader := req.Header.Get("mx") i, err := strconv.ParseUint(mxHeader, 0, 0) if err != nil { log.Printf("Invalid mx header %q: %s", mxHeader, err) return } mx = uint(i) } else { mx = 1 } types := func(st string) []string { if st == "ssdp:all" { return me.allTypes() } for _, t := range me.allTypes() { if t == st { return []string{t} } } return nil }(req.Header.Get("st")) for _, ip := range func() (ret []net.IP) { addrs, err := me.Interface.Addrs() if err != nil { panic(err) } for _, addr := range addrs { if ip, ok := func() (net.IP, bool) { switch data := addr.(type) { case *net.IPNet: if data.Contains(sender.IP) { return data.IP, true } return nil, false case *net.IPAddr: return data.IP, true } panic(addr) }(); ok { ret = append(ret, ip) } } return }() { for _, type_ := range types { resp := me.makeResponse(ip, type_, req) delay := time.Duration(rand.Int63n(int64(time.Second) * int64(mx))) me.delayedSend(delay, resp, sender) } } } func (me *Server) makeResponse(ip net.IP, targ string, req *http.Request) (ret []byte) { resp := &http.Response{ StatusCode: 200, ProtoMajor: 1, ProtoMinor: 1, Header: make(http.Header), Request: req, } for _, pair := range [...][2]string{ {"CACHE-CONTROL", fmt.Sprintf("max-age=%d", 5*me.NotifyInterval/2/time.Second)}, {"EXT", ""}, {"LOCATION", me.Location(ip)}, {"SERVER", me.Server}, {"ST", targ}, {"USN", me.usnFromTarget(targ)}, } { resp.Header.Set(pair[0], pair[1]) } buf := &bytes.Buffer{} if err := resp.Write(buf); err != nil { panic(err) } return buf.Bytes() } dms-1.1.0/transcode/000077500000000000000000000000001356070553200142645ustar00rootroot00000000000000dms-1.1.0/transcode/transcode.go000066400000000000000000000067031356070553200166030ustar00rootroot00000000000000// Package transcode implements routines for transcoding to various kinds of // receiver. package transcode import ( "io" "log" "os/exec" "runtime" "strconv" "time" . "github.com/anacrolix/dms/misc" "github.com/anacrolix/ffprobe" ) // Invokes an external command and returns a reader from its stdout. The // command is waited on asynchronously. func transcodePipe(args []string, stderr io.Writer) (r io.ReadCloser, err error) { log.Println("transcode command:", args) cmd := exec.Command(args[0], args[1:]...) cmd.Stderr = stderr r, err = cmd.StdoutPipe() if err != nil { return } err = cmd.Start() if err != nil { return } go func() { err := cmd.Wait() if err != nil { log.Printf("command %s failed: %s", args, err) } }() return } // Return a series of ffmpeg arguments that pick specific codecs for specific // streams. This requires use of the -map flag. func streamArgs(s map[string]interface{}) (ret []string) { defer func() { if len(ret) != 0 { ret = append(ret, []string{ "-map", "0:" + strconv.Itoa(int(s["index"].(float64))), }...) } }() switch s["codec_type"] { case "video": /* if s["codec_name"] == "h264" { if i, _ := strconv.ParseInt(s["is_avc"], 0, 0); i != 0 { return []string{"-vcodec", "copy", "-sameq", "-vbsf", "h264_mp4toannexb"} } } */ return []string{"-target", "pal-dvd"} case "audio": if s["codec_name"] == "dca" { return []string{"-acodec", "ac3", "-ab", "224k", "-ac", "2"} } else { return []string{"-acodec", "copy"} } case "subtitle": return []string{"-scodec", "copy"} } return } // Streams the desired file in the MPEG_PS_PAL DLNA profile. func Transcode(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) { args := []string{ "ffmpeg", "-threads", strconv.FormatInt(int64(runtime.NumCPU()), 10), "-async", "1", "-ss", FormatDurationSexagesimal(start), } if length >= 0 { args = append(args, []string{ "-t", FormatDurationSexagesimal(length), }...) } args = append(args, []string{ "-i", path, }...) info, err := ffprobe.Run(path) if err != nil { return } for _, s := range info.Streams { args = append(args, streamArgs(s)...) } args = append(args, []string{"-f", "mpegts", "pipe:"}...) return transcodePipe(args, stderr) } // Returns a stream of Chromecast supported VP8. func VP8Transcode(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) { args := []string{ "avconv", "-threads", strconv.FormatInt(int64(runtime.NumCPU()), 10), "-async", "1", "-ss", FormatDurationSexagesimal(start), } if length > 0 { args = append(args, []string{ "-t", FormatDurationSexagesimal(length), }...) } args = append(args, []string{ "-i", path, // "-deadline", "good", // "-c:v", "libvpx", "-crf", "10", "-f", "webm", "pipe:"}...) return transcodePipe(args, stderr) } // Returns a stream of Chromecast supported matroska. func ChromecastTranscode(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) { args := []string{ "ffmpeg", "-ss", FormatDurationSexagesimal(start), "-i", path, "-c:v", "libx264", "-preset", "ultrafast", "-profile:v", "high", "-level", "5.0", "-movflags", "+faststart+frag_keyframe+empty_moov", } if length > 0 { args = append(args, []string{ "-t", FormatDurationSexagesimal(length), }...) } args = append(args, []string{ "-f", "mp4", "pipe:"}...) return transcodePipe(args, stderr) } dms-1.1.0/upnp/000077500000000000000000000000001356070553200132645ustar00rootroot00000000000000dms-1.1.0/upnp/eventing.go000066400000000000000000000044511356070553200154360ustar00rootroot00000000000000package upnp import ( "crypto/rand" "encoding/xml" "fmt" "io" "log" "net/url" "regexp" "time" ) // TODO: Why use namespace prefixes in PropertySet et al? Because the spec // uses them, and I believe the Golang standard library XML spec implementers // incorrectly assume that you can get away with just xmlns="". // propertyset is the root element sent in an event callback. type PropertySet struct { XMLName struct{} `xml:"e:propertyset"` Properties []Property // This should be set to `"urn:schemas-upnp-org:event-1-0"`. Space string `xml:"xmlns:e,attr"` } // propertys provide namespacing to the contained variables. type Property struct { XMLName struct{} `xml:"e:property"` Variable Variable } // Represents an evented state variable that has sendEvents="yes" in its // service spec. type Variable struct { XMLName xml.Name Value string `xml:",chardata"` } type subscriber struct { sid string nextSeq uint32 // 0 for initial event, wraps from Uint32Max to 1. urls []*url.URL expiry time.Time } // Intended to eventually be an embeddable implementation for managing // eventing for a service. Not complete. type Eventing struct { subscribers map[string]*subscriber } func (me *Eventing) Subscribe(callback []*url.URL, timeoutSeconds int) (sid string, actualTimeout int, err error) { var uuid [16]byte io.ReadFull(rand.Reader, uuid[:]) sid = FormatUUID(uuid[:]) if _, ok := me.subscribers[sid]; ok { err = fmt.Errorf("already subscribed: %s", sid) return } ssr := &subscriber{ sid: sid, urls: callback, expiry: time.Now().Add(time.Duration(timeoutSeconds) * time.Second), } if me.subscribers == nil { me.subscribers = make(map[string]*subscriber) } me.subscribers[sid] = ssr actualTimeout = int(ssr.expiry.Sub(time.Now()) / time.Second) return } func (me *Eventing) Unsubscribe(sid string) error { return nil } var callbackURLRegexp = regexp.MustCompile("<(.*?)>") // Parse the CALLBACK HTTP header in an event subscription request. See UPnP // Device Architecture 4.1.2. func ParseCallbackURLs(callback string) (ret []*url.URL) { for _, match := range callbackURLRegexp.FindAllStringSubmatch(callback, -1) { _url, err := url.Parse(match[1]) if err != nil { log.Printf("bad callback url: %q", match[1]) continue } ret = append(ret, _url) } return } dms-1.1.0/upnp/eventing_test.go000066400000000000000000000014241356070553200164720ustar00rootroot00000000000000package upnp import ( "encoding/xml" "testing" ) // Visually verify that property sets are marshalled correctly. func TestMarshalPropertySet(t *testing.T) { b, err := xml.MarshalIndent(&PropertySet{ Properties: []Property{ Property{ Variable: Variable{ XMLName: xml.Name{ Local: "SystemUpdateID", }, Value: "0", }, }, Property{ Variable: Variable{ XMLName: xml.Name{ Local: "answerToTheUniverse", }, Value: "42", }, }, }, Space: "urn:schemas-upnp-org:event-1-0", }, "", " ") t.Log("\n" + string(b)) if err != nil { t.Fatal(err) } } func TestParseCallbackURLs(t *testing.T) { urls := ParseCallbackURLs(" ") if len(urls) != 3 { t.Fatal(len(urls)) } } dms-1.1.0/upnp/upnp.go000066400000000000000000000073611356070553200146040ustar00rootroot00000000000000package upnp import ( "encoding/xml" "errors" "fmt" "log" "regexp" "strconv" "strings" ) var serviceURNRegexp *regexp.Regexp = regexp.MustCompile(`^urn:schemas-upnp-org:service:(\w+):(\d+)$`) type ServiceURN struct { Type string Version uint64 } func (me ServiceURN) String() string { return fmt.Sprintf("urn:schemas-upnp-org:service:%s:%d", me.Type, me.Version) } func ParseServiceType(s string) (ret ServiceURN, err error) { matches := serviceURNRegexp.FindStringSubmatch(s) if matches == nil { err = errors.New(s) return } if len(matches) != 3 { log.Panicf("Invalid serviceURNRegexp ?") } ret.Type = matches[1] ret.Version, err = strconv.ParseUint(matches[2], 0, 0) return } type SoapAction struct { ServiceURN Action string } func ParseActionHTTPHeader(s string) (ret SoapAction, err error) { if s[0] != '"' || s[len(s)-1] != '"' { return } s = s[1 : len(s)-1] hashIndex := strings.LastIndex(s, "#") if hashIndex == -1 { return } ret.Action = s[hashIndex+1:] ret.ServiceURN, err = ParseServiceType(s[:hashIndex]) return } type SpecVersion struct { Major int `xml:"major"` Minor int `xml:"minor"` } type Icon struct { Mimetype string `xml:"mimetype"` Width int `xml:"width"` Height int `xml:"height"` Depth int `xml:"depth"` URL string `xml:"url"` } type Service struct { XMLName xml.Name `xml:"service"` ServiceType string `xml:"serviceType"` ServiceId string `xml:"serviceId"` SCPDURL string ControlURL string `xml:"controlURL"` EventSubURL string `xml:"eventSubURL"` } type Device struct { DeviceType string `xml:"deviceType"` FriendlyName string `xml:"friendlyName"` Manufacturer string `xml:"manufacturer"` ModelName string `xml:"modelName"` UDN string IconList []Icon `xml:"iconList>icon"` ServiceList []Service `xml:"serviceList>service"` } type DeviceDesc struct { XMLName xml.Name `xml:"urn:schemas-upnp-org:device-1-0 root"` SpecVersion SpecVersion `xml:"specVersion"` Device Device `xml:"device"` } type Error struct { XMLName xml.Name `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"` Code uint `xml:"errorCode"` Desc string `xml:"errorDescription"` } func (e *Error) Error() string { return fmt.Sprintf("%d %s", e.Code, e.Desc) } const ( InvalidActionErrorCode = 401 ActionFailedErrorCode = 501 ArgumentValueInvalidErrorCode = 600 ) var ( InvalidActionError = Errorf(401, "Invalid Action") ArgumentValueInvalidError = Errorf(600, "The argument value is invalid") ) // Errorf creates an UPNP error from the given code and description func Errorf(code uint, tpl string, args ...interface{}) *Error { return &Error{Code: code, Desc: fmt.Sprintf(tpl, args...)} } // ConvertError converts any error to an UPNP error func ConvertError(err error) *Error { if err == nil { return nil } if e, ok := err.(*Error); ok { return e } return Errorf(ActionFailedErrorCode, err.Error()) } type Action struct { Name string Arguments []Argument } type Argument struct { Name string Direction string RelatedStateVar string } type SCPD struct { XMLName xml.Name `xml:"urn:schemas-upnp-org:service-1-0 scpd"` SpecVersion SpecVersion `xml:"specVersion"` ActionList []Action `xml:"actionList>action"` ServiceStateTable []StateVariable `xml:"serviceStateTable>stateVariable"` } type StateVariable struct { SendEvents string `xml:"sendEvents,attr"` Name string `xml:"name"` DataType string `xml:"dataType"` AllowedValues *[]string `xml:"allowedValueList>allowedValue,omitempty"` } func FormatUUID(buf []byte) string { return fmt.Sprintf("uuid:%x-%x-%x-%x-%x", buf[:4], buf[4:6], buf[6:8], buf[8:10], buf[10:16]) } dms-1.1.0/upnpav/000077500000000000000000000000001356070553200136135ustar00rootroot00000000000000dms-1.1.0/upnpav/upnpav.go000066400000000000000000000022761356070553200154620ustar00rootroot00000000000000package upnpav import ( "encoding/xml" ) const ( NoSuchObjectErrorCode = 701 ) type Resource struct { XMLName xml.Name `xml:"res"` ProtocolInfo string `xml:"protocolInfo,attr"` URL string `xml:",chardata"` Size uint64 `xml:"size,attr,omitempty"` Bitrate uint `xml:"bitrate,attr,omitempty"` Duration string `xml:"duration,attr,omitempty"` Resolution string `xml:"resolution,attr,omitempty"` } type Container struct { Object XMLName xml.Name `xml:"container"` ChildCount int `xml:"childCount,attr"` } type Item struct { Object XMLName xml.Name `xml:"item"` Res []Resource } type Object struct { ID string `xml:"id,attr"` ParentID string `xml:"parentID,attr"` Restricted int `xml:"restricted,attr"` // indicates whether the object is modifiable Class string `xml:"upnp:class"` Icon string `xml:"upnp:icon,omitempty"` Title string `xml:"dc:title"` Artist string `xml:"upnp:artist,omitempty"` Album string `xml:"upnp:album,omitempty"` Genre string `xml:"upnp:genre,omitempty"` AlbumArtURI string `xml:"upnp:albumArtURI,omitempty"` Searchable int `xml:"searchable,attr"` }