diff --git a/.cache/clangd/index/FILE.h.79AEFC497861DFC5.idx b/.cache/clangd/index/FILE.h.79AEFC497861DFC5.idx new file mode 100644 index 0000000..9d496f7 Binary files /dev/null and b/.cache/clangd/index/FILE.h.79AEFC497861DFC5.idx differ diff --git a/.cache/clangd/index/__FILE.h.3324F85E382E036A.idx b/.cache/clangd/index/__FILE.h.3324F85E382E036A.idx new file mode 100644 index 0000000..892b411 Binary files /dev/null and b/.cache/clangd/index/__FILE.h.3324F85E382E036A.idx differ diff --git a/.cache/clangd/index/__fpos64_t.h.23C43F442076149A.idx b/.cache/clangd/index/__fpos64_t.h.23C43F442076149A.idx new file mode 100644 index 0000000..2c2dfba Binary files /dev/null and b/.cache/clangd/index/__fpos64_t.h.23C43F442076149A.idx differ diff --git a/.cache/clangd/index/__fpos_t.h.ECFBE9EEDAAB6648.idx b/.cache/clangd/index/__fpos_t.h.ECFBE9EEDAAB6648.idx new file mode 100644 index 0000000..799b102 Binary files /dev/null and b/.cache/clangd/index/__fpos_t.h.ECFBE9EEDAAB6648.idx differ diff --git a/.cache/clangd/index/__locale_t.h.40EA38A2DCA58618.idx b/.cache/clangd/index/__locale_t.h.40EA38A2DCA58618.idx new file mode 100644 index 0000000..f77e6d5 Binary files /dev/null and b/.cache/clangd/index/__locale_t.h.40EA38A2DCA58618.idx differ diff --git a/.cache/clangd/index/__mbstate_t.h.6C32CF66236653D7.idx b/.cache/clangd/index/__mbstate_t.h.6C32CF66236653D7.idx new file mode 100644 index 0000000..f46650e Binary files /dev/null and b/.cache/clangd/index/__mbstate_t.h.6C32CF66236653D7.idx differ diff --git a/.cache/clangd/index/__sigset_t.h.72131E8056C7E284.idx b/.cache/clangd/index/__sigset_t.h.72131E8056C7E284.idx new file mode 100644 index 0000000..5048503 Binary files /dev/null and b/.cache/clangd/index/__sigset_t.h.72131E8056C7E284.idx differ diff --git a/.cache/clangd/index/__sigval_t.h.666FFB429519E69C.idx b/.cache/clangd/index/__sigval_t.h.666FFB429519E69C.idx new file mode 100644 index 0000000..0b34c00 Binary files /dev/null and b/.cache/clangd/index/__sigval_t.h.666FFB429519E69C.idx differ diff --git a/.cache/clangd/index/__stdarg___gnuc_va_list.h.957EE2B4DB6A5247.idx b/.cache/clangd/index/__stdarg___gnuc_va_list.h.957EE2B4DB6A5247.idx new file mode 100644 index 0000000..422cc8f Binary files /dev/null and b/.cache/clangd/index/__stdarg___gnuc_va_list.h.957EE2B4DB6A5247.idx differ diff --git a/.cache/clangd/index/__stdarg___va_copy.h.11ED534E7AF4A856.idx b/.cache/clangd/index/__stdarg___va_copy.h.11ED534E7AF4A856.idx new file mode 100644 index 0000000..223304d Binary files /dev/null and b/.cache/clangd/index/__stdarg___va_copy.h.11ED534E7AF4A856.idx differ diff --git a/.cache/clangd/index/__stdarg_va_arg.h.C38675C0D160CE4C.idx b/.cache/clangd/index/__stdarg_va_arg.h.C38675C0D160CE4C.idx new file mode 100644 index 0000000..4c057f1 Binary files /dev/null and b/.cache/clangd/index/__stdarg_va_arg.h.C38675C0D160CE4C.idx differ diff --git a/.cache/clangd/index/__stdarg_va_copy.h.5812F997B3F780A9.idx b/.cache/clangd/index/__stdarg_va_copy.h.5812F997B3F780A9.idx new file mode 100644 index 0000000..bee0079 Binary files /dev/null and b/.cache/clangd/index/__stdarg_va_copy.h.5812F997B3F780A9.idx differ diff --git a/.cache/clangd/index/__stdarg_va_list.h.A20C0FFDA235CCCC.idx b/.cache/clangd/index/__stdarg_va_list.h.A20C0FFDA235CCCC.idx new file mode 100644 index 0000000..3c39fae Binary files /dev/null and b/.cache/clangd/index/__stdarg_va_list.h.A20C0FFDA235CCCC.idx differ diff --git a/.cache/clangd/index/__stddef_max_align_t.h.9A696B9ACA5AF69C.idx b/.cache/clangd/index/__stddef_max_align_t.h.9A696B9ACA5AF69C.idx new file mode 100644 index 0000000..17f9e8d Binary files /dev/null and b/.cache/clangd/index/__stddef_max_align_t.h.9A696B9ACA5AF69C.idx differ diff --git a/.cache/clangd/index/__stddef_null.h.46DACB2AE1FCBB57.idx b/.cache/clangd/index/__stddef_null.h.46DACB2AE1FCBB57.idx new file mode 100644 index 0000000..cc160c5 Binary files /dev/null and b/.cache/clangd/index/__stddef_null.h.46DACB2AE1FCBB57.idx differ diff --git a/.cache/clangd/index/__stddef_nullptr_t.h.BB76DF7F0265238C.idx b/.cache/clangd/index/__stddef_nullptr_t.h.BB76DF7F0265238C.idx new file mode 100644 index 0000000..7f1e60d Binary files /dev/null and b/.cache/clangd/index/__stddef_nullptr_t.h.BB76DF7F0265238C.idx differ diff --git a/.cache/clangd/index/__stddef_offsetof.h.F032B8137362A473.idx b/.cache/clangd/index/__stddef_offsetof.h.F032B8137362A473.idx new file mode 100644 index 0000000..345432b Binary files /dev/null and b/.cache/clangd/index/__stddef_offsetof.h.F032B8137362A473.idx differ diff --git a/.cache/clangd/index/__stddef_ptrdiff_t.h.244B2A4B8AE5E0B4.idx b/.cache/clangd/index/__stddef_ptrdiff_t.h.244B2A4B8AE5E0B4.idx new file mode 100644 index 0000000..114645e Binary files /dev/null and b/.cache/clangd/index/__stddef_ptrdiff_t.h.244B2A4B8AE5E0B4.idx differ diff --git a/.cache/clangd/index/__stddef_size_t.h.747ED4A2DA41774B.idx b/.cache/clangd/index/__stddef_size_t.h.747ED4A2DA41774B.idx new file mode 100644 index 0000000..135407f Binary files /dev/null and b/.cache/clangd/index/__stddef_size_t.h.747ED4A2DA41774B.idx differ diff --git a/.cache/clangd/index/__stddef_wchar_t.h.E89E65F6312D18C8.idx b/.cache/clangd/index/__stddef_wchar_t.h.E89E65F6312D18C8.idx new file mode 100644 index 0000000..e8a633f Binary files /dev/null and b/.cache/clangd/index/__stddef_wchar_t.h.E89E65F6312D18C8.idx differ diff --git a/.cache/clangd/index/airplay_video.c.82EB1007E990FA8D.idx b/.cache/clangd/index/airplay_video.c.82EB1007E990FA8D.idx new file mode 100644 index 0000000..e8789ba Binary files /dev/null and b/.cache/clangd/index/airplay_video.c.82EB1007E990FA8D.idx differ diff --git a/.cache/clangd/index/airplay_video.h.318C9A9F1E6D5C72.idx b/.cache/clangd/index/airplay_video.h.318C9A9F1E6D5C72.idx new file mode 100644 index 0000000..01af78e Binary files /dev/null and b/.cache/clangd/index/airplay_video.h.318C9A9F1E6D5C72.idx differ diff --git a/.cache/clangd/index/algorithm.498CA574ACDEDD3F.idx b/.cache/clangd/index/algorithm.498CA574ACDEDD3F.idx new file mode 100644 index 0000000..7e07ad2 Binary files /dev/null and b/.cache/clangd/index/algorithm.498CA574ACDEDD3F.idx differ diff --git a/.cache/clangd/index/algorithmfwd.h.0C27EFECF199CABC.idx b/.cache/clangd/index/algorithmfwd.h.0C27EFECF199CABC.idx new file mode 100644 index 0000000..758a640 Binary files /dev/null and b/.cache/clangd/index/algorithmfwd.h.0C27EFECF199CABC.idx differ diff --git a/.cache/clangd/index/alloc_traits.h.AD04F06EB9131043.idx b/.cache/clangd/index/alloc_traits.h.AD04F06EB9131043.idx new file mode 100644 index 0000000..ffa5fdb Binary files /dev/null and b/.cache/clangd/index/alloc_traits.h.AD04F06EB9131043.idx differ diff --git a/.cache/clangd/index/alloc_traits.h.C5FA76BF47B97A2C.idx b/.cache/clangd/index/alloc_traits.h.C5FA76BF47B97A2C.idx new file mode 100644 index 0000000..720b694 Binary files /dev/null and b/.cache/clangd/index/alloc_traits.h.C5FA76BF47B97A2C.idx differ diff --git a/.cache/clangd/index/alloca.h.793370BF9878CFB6.idx b/.cache/clangd/index/alloca.h.793370BF9878CFB6.idx new file mode 100644 index 0000000..a5cadc7 Binary files /dev/null and b/.cache/clangd/index/alloca.h.793370BF9878CFB6.idx differ diff --git a/.cache/clangd/index/allocator.h.0E446C7C21C98155.idx b/.cache/clangd/index/allocator.h.0E446C7C21C98155.idx new file mode 100644 index 0000000..2213cb9 Binary files /dev/null and b/.cache/clangd/index/allocator.h.0E446C7C21C98155.idx differ diff --git a/.cache/clangd/index/api.c.FDB8F81E526D75C2.idx b/.cache/clangd/index/api.c.FDB8F81E526D75C2.idx new file mode 100644 index 0000000..85535f0 Binary files /dev/null and b/.cache/clangd/index/api.c.FDB8F81E526D75C2.idx differ diff --git a/.cache/clangd/index/app-enumtypes.h.07236DB829758BAF.idx b/.cache/clangd/index/app-enumtypes.h.07236DB829758BAF.idx new file mode 100644 index 0000000..9a9d399 Binary files /dev/null and b/.cache/clangd/index/app-enumtypes.h.07236DB829758BAF.idx differ diff --git a/.cache/clangd/index/app-prelude.h.649AA1B473689AEF.idx b/.cache/clangd/index/app-prelude.h.649AA1B473689AEF.idx new file mode 100644 index 0000000..48aaaa3 Binary files /dev/null and b/.cache/clangd/index/app-prelude.h.649AA1B473689AEF.idx differ diff --git a/.cache/clangd/index/asn1.h.5D851880E14A50F6.idx b/.cache/clangd/index/asn1.h.5D851880E14A50F6.idx new file mode 100644 index 0000000..78ecb05 Binary files /dev/null and b/.cache/clangd/index/asn1.h.5D851880E14A50F6.idx differ diff --git a/.cache/clangd/index/asn1err.h.DB8DF3439C219E5A.idx b/.cache/clangd/index/asn1err.h.DB8DF3439C219E5A.idx new file mode 100644 index 0000000..830ab17 Binary files /dev/null and b/.cache/clangd/index/asn1err.h.DB8DF3439C219E5A.idx differ diff --git a/.cache/clangd/index/assert.h.9895EEC17E1D2A2B.idx b/.cache/clangd/index/assert.h.9895EEC17E1D2A2B.idx new file mode 100644 index 0000000..3c9288b Binary files /dev/null and b/.cache/clangd/index/assert.h.9895EEC17E1D2A2B.idx differ diff --git a/.cache/clangd/index/assertions.h.A07D4EE107562455.idx b/.cache/clangd/index/assertions.h.A07D4EE107562455.idx new file mode 100644 index 0000000..a358700 Binary files /dev/null and b/.cache/clangd/index/assertions.h.A07D4EE107562455.idx differ diff --git a/.cache/clangd/index/atomic_word.h.40B5AFEC9E219BA7.idx b/.cache/clangd/index/atomic_word.h.40B5AFEC9E219BA7.idx new file mode 100644 index 0000000..ff0f0de Binary files /dev/null and b/.cache/clangd/index/atomic_word.h.40B5AFEC9E219BA7.idx differ diff --git a/.cache/clangd/index/atomicity.h.D39336495F8767D7.idx b/.cache/clangd/index/atomicity.h.D39336495F8767D7.idx new file mode 100644 index 0000000..93814ee Binary files /dev/null and b/.cache/clangd/index/atomicity.h.D39336495F8767D7.idx differ diff --git a/.cache/clangd/index/audio_renderer.c.010EE65A32D8DC93.idx b/.cache/clangd/index/audio_renderer.c.010EE65A32D8DC93.idx new file mode 100644 index 0000000..b45a2f9 Binary files /dev/null and b/.cache/clangd/index/audio_renderer.c.010EE65A32D8DC93.idx differ diff --git a/.cache/clangd/index/audio_renderer.h.CF88CA741EED9E5A.idx b/.cache/clangd/index/audio_renderer.h.CF88CA741EED9E5A.idx new file mode 100644 index 0000000..2afb761 Binary files /dev/null and b/.cache/clangd/index/audio_renderer.h.CF88CA741EED9E5A.idx differ diff --git a/.cache/clangd/index/base-prelude.h.A2092358E3EC0EBB.idx b/.cache/clangd/index/base-prelude.h.A2092358E3EC0EBB.idx new file mode 100644 index 0000000..aef424f Binary files /dev/null and b/.cache/clangd/index/base-prelude.h.A2092358E3EC0EBB.idx differ diff --git a/.cache/clangd/index/basic_file.h.CC566D4D8FF2A217.idx b/.cache/clangd/index/basic_file.h.CC566D4D8FF2A217.idx new file mode 100644 index 0000000..96cea1f Binary files /dev/null and b/.cache/clangd/index/basic_file.h.CC566D4D8FF2A217.idx differ diff --git a/.cache/clangd/index/basic_ios.h.7EBACFC02421E207.idx b/.cache/clangd/index/basic_ios.h.7EBACFC02421E207.idx new file mode 100644 index 0000000..6175a84 Binary files /dev/null and b/.cache/clangd/index/basic_ios.h.7EBACFC02421E207.idx differ diff --git a/.cache/clangd/index/basic_ios.tcc.C0FD31BF223D90C1.idx b/.cache/clangd/index/basic_ios.tcc.C0FD31BF223D90C1.idx new file mode 100644 index 0000000..d97d9be Binary files /dev/null and b/.cache/clangd/index/basic_ios.tcc.C0FD31BF223D90C1.idx differ diff --git a/.cache/clangd/index/basic_string.h.387287D336746F49.idx b/.cache/clangd/index/basic_string.h.387287D336746F49.idx new file mode 100644 index 0000000..fa3f8e8 Binary files /dev/null and b/.cache/clangd/index/basic_string.h.387287D336746F49.idx differ diff --git a/.cache/clangd/index/basic_string.tcc.99B713A6DD7FA1FC.idx b/.cache/clangd/index/basic_string.tcc.99B713A6DD7FA1FC.idx new file mode 100644 index 0000000..4363311 Binary files /dev/null and b/.cache/clangd/index/basic_string.tcc.99B713A6DD7FA1FC.idx differ diff --git a/.cache/clangd/index/binders.h.D754A978B7F005D6.idx b/.cache/clangd/index/binders.h.D754A978B7F005D6.idx new file mode 100644 index 0000000..8c34acd Binary files /dev/null and b/.cache/clangd/index/binders.h.D754A978B7F005D6.idx differ diff --git a/.cache/clangd/index/bio.h.C62CDB2383121335.idx b/.cache/clangd/index/bio.h.C62CDB2383121335.idx new file mode 100644 index 0000000..c3ff850 Binary files /dev/null and b/.cache/clangd/index/bio.h.C62CDB2383121335.idx differ diff --git a/.cache/clangd/index/bioerr.h.59AC516D9E17B74B.idx b/.cache/clangd/index/bioerr.h.59AC516D9E17B74B.idx new file mode 100644 index 0000000..d98ca9e Binary files /dev/null and b/.cache/clangd/index/bioerr.h.59AC516D9E17B74B.idx differ diff --git a/.cache/clangd/index/bitsperlong.h.6E74E6215C6106E0.idx b/.cache/clangd/index/bitsperlong.h.6E74E6215C6106E0.idx new file mode 100644 index 0000000..ad0c91e Binary files /dev/null and b/.cache/clangd/index/bitsperlong.h.6E74E6215C6106E0.idx differ diff --git a/.cache/clangd/index/bitsperlong.h.B267C684FE143FB5.idx b/.cache/clangd/index/bitsperlong.h.B267C684FE143FB5.idx new file mode 100644 index 0000000..d24a594 Binary files /dev/null and b/.cache/clangd/index/bitsperlong.h.B267C684FE143FB5.idx differ diff --git a/.cache/clangd/index/bn.h.E7B705097921AA70.idx b/.cache/clangd/index/bn.h.E7B705097921AA70.idx new file mode 100644 index 0000000..a465507 Binary files /dev/null and b/.cache/clangd/index/bn.h.E7B705097921AA70.idx differ diff --git a/.cache/clangd/index/bnerr.h.121E86FCD0269A61.idx b/.cache/clangd/index/bnerr.h.121E86FCD0269A61.idx new file mode 100644 index 0000000..a4fa376 Binary files /dev/null and b/.cache/clangd/index/bnerr.h.121E86FCD0269A61.idx differ diff --git a/.cache/clangd/index/buffer.h.05AB9F50CF1818F9.idx b/.cache/clangd/index/buffer.h.05AB9F50CF1818F9.idx new file mode 100644 index 0000000..f9fa444 Binary files /dev/null and b/.cache/clangd/index/buffer.h.05AB9F50CF1818F9.idx differ diff --git a/.cache/clangd/index/buffererr.h.08F67D0ED7350988.idx b/.cache/clangd/index/buffererr.h.08F67D0ED7350988.idx new file mode 100644 index 0000000..0be8337 Binary files /dev/null and b/.cache/clangd/index/buffererr.h.08F67D0ED7350988.idx differ diff --git a/.cache/clangd/index/byteswap.h.E57FF553A16DFAFC.idx b/.cache/clangd/index/byteswap.h.E57FF553A16DFAFC.idx new file mode 100644 index 0000000..1f7b76d Binary files /dev/null and b/.cache/clangd/index/byteswap.h.E57FF553A16DFAFC.idx differ diff --git a/.cache/clangd/index/byteutils.c.3A859582377DB470.idx b/.cache/clangd/index/byteutils.c.3A859582377DB470.idx new file mode 100644 index 0000000..1673020 Binary files /dev/null and b/.cache/clangd/index/byteutils.c.3A859582377DB470.idx differ diff --git a/.cache/clangd/index/byteutils.h.F7E50FB9A1848C8D.idx b/.cache/clangd/index/byteutils.h.F7E50FB9A1848C8D.idx new file mode 100644 index 0000000..d0ccf39 Binary files /dev/null and b/.cache/clangd/index/byteutils.h.F7E50FB9A1848C8D.idx differ diff --git a/.cache/clangd/index/c++allocator.h.69825EBEC9FD7F9F.idx b/.cache/clangd/index/c++allocator.h.69825EBEC9FD7F9F.idx new file mode 100644 index 0000000..c276906 Binary files /dev/null and b/.cache/clangd/index/c++allocator.h.69825EBEC9FD7F9F.idx differ diff --git a/.cache/clangd/index/c++config.h.902B70E4E5B61FAE.idx b/.cache/clangd/index/c++config.h.902B70E4E5B61FAE.idx new file mode 100644 index 0000000..be95cdb Binary files /dev/null and b/.cache/clangd/index/c++config.h.902B70E4E5B61FAE.idx differ diff --git a/.cache/clangd/index/c++io.h.732B379E226D97E6.idx b/.cache/clangd/index/c++io.h.732B379E226D97E6.idx new file mode 100644 index 0000000..685e159 Binary files /dev/null and b/.cache/clangd/index/c++io.h.732B379E226D97E6.idx differ diff --git a/.cache/clangd/index/c++locale.h.31950B47B0DBEE99.idx b/.cache/clangd/index/c++locale.h.31950B47B0DBEE99.idx new file mode 100644 index 0000000..9824975 Binary files /dev/null and b/.cache/clangd/index/c++locale.h.31950B47B0DBEE99.idx differ diff --git a/.cache/clangd/index/cctype.165F27C4304B3C9B.idx b/.cache/clangd/index/cctype.165F27C4304B3C9B.idx new file mode 100644 index 0000000..2a68291 Binary files /dev/null and b/.cache/clangd/index/cctype.165F27C4304B3C9B.idx differ diff --git a/.cache/clangd/index/cdefs.h.E4D450DF98C1338F.idx b/.cache/clangd/index/cdefs.h.E4D450DF98C1338F.idx new file mode 100644 index 0000000..1415744 Binary files /dev/null and b/.cache/clangd/index/cdefs.h.E4D450DF98C1338F.idx differ diff --git a/.cache/clangd/index/cerrno.3BCF3D08C4058E27.idx b/.cache/clangd/index/cerrno.3BCF3D08C4058E27.idx new file mode 100644 index 0000000..71e5617 Binary files /dev/null and b/.cache/clangd/index/cerrno.3BCF3D08C4058E27.idx differ diff --git a/.cache/clangd/index/char_traits.h.89A432428E303FAE.idx b/.cache/clangd/index/char_traits.h.89A432428E303FAE.idx new file mode 100644 index 0000000..4e6bef9 Binary files /dev/null and b/.cache/clangd/index/char_traits.h.89A432428E303FAE.idx differ diff --git a/.cache/clangd/index/charconv.h.7CCDB5A25C09C917.idx b/.cache/clangd/index/charconv.h.7CCDB5A25C09C917.idx new file mode 100644 index 0000000..cff882a Binary files /dev/null and b/.cache/clangd/index/charconv.h.7CCDB5A25C09C917.idx differ diff --git a/.cache/clangd/index/clocale.7A8A8F11E6CFA90C.idx b/.cache/clangd/index/clocale.7A8A8F11E6CFA90C.idx new file mode 100644 index 0000000..1629c8b Binary files /dev/null and b/.cache/clangd/index/clocale.7A8A8F11E6CFA90C.idx differ diff --git a/.cache/clangd/index/clock_t.h.65AFE5BA7329BCD4.idx b/.cache/clangd/index/clock_t.h.65AFE5BA7329BCD4.idx new file mode 100644 index 0000000..2215c83 Binary files /dev/null and b/.cache/clangd/index/clock_t.h.65AFE5BA7329BCD4.idx differ diff --git a/.cache/clangd/index/clockid_t.h.5545878511E5BAF1.idx b/.cache/clangd/index/clockid_t.h.5545878511E5BAF1.idx new file mode 100644 index 0000000..7f0ebde Binary files /dev/null and b/.cache/clangd/index/clockid_t.h.5545878511E5BAF1.idx differ diff --git a/.cache/clangd/index/cmath.2ADD1F7B4D4C3652.idx b/.cache/clangd/index/cmath.2ADD1F7B4D4C3652.idx new file mode 100644 index 0000000..934c58a Binary files /dev/null and b/.cache/clangd/index/cmath.2ADD1F7B4D4C3652.idx differ diff --git a/.cache/clangd/index/codecvt.h.76F700EC784B2068.idx b/.cache/clangd/index/codecvt.h.76F700EC784B2068.idx new file mode 100644 index 0000000..b18c62e Binary files /dev/null and b/.cache/clangd/index/codecvt.h.76F700EC784B2068.idx differ diff --git a/.cache/clangd/index/compat.c.9F7625F1ED161716.idx b/.cache/clangd/index/compat.c.9F7625F1ED161716.idx new file mode 100644 index 0000000..b404ecf Binary files /dev/null and b/.cache/clangd/index/compat.c.9F7625F1ED161716.idx differ diff --git a/.cache/clangd/index/compat.h.5EA5E978B46E185D.idx b/.cache/clangd/index/compat.h.5EA5E978B46E185D.idx new file mode 100644 index 0000000..06f9b4a Binary files /dev/null and b/.cache/clangd/index/compat.h.5EA5E978B46E185D.idx differ diff --git a/.cache/clangd/index/concept_check.h.D48F98228EE00BC5.idx b/.cache/clangd/index/concept_check.h.D48F98228EE00BC5.idx new file mode 100644 index 0000000..048d163 Binary files /dev/null and b/.cache/clangd/index/concept_check.h.D48F98228EE00BC5.idx differ diff --git a/.cache/clangd/index/concepts.79FFDC37FA75088F.idx b/.cache/clangd/index/concepts.79FFDC37FA75088F.idx new file mode 100644 index 0000000..cdb5ac1 Binary files /dev/null and b/.cache/clangd/index/concepts.79FFDC37FA75088F.idx differ diff --git a/.cache/clangd/index/confname.h.ACD47B58EFD02DD6.idx b/.cache/clangd/index/confname.h.ACD47B58EFD02DD6.idx new file mode 100644 index 0000000..0f48ae6 Binary files /dev/null and b/.cache/clangd/index/confname.h.ACD47B58EFD02DD6.idx differ diff --git a/.cache/clangd/index/cookie_io_functions_t.h.5A08066DC7F05542.idx b/.cache/clangd/index/cookie_io_functions_t.h.5A08066DC7F05542.idx new file mode 100644 index 0000000..9fc5af7 Binary files /dev/null and b/.cache/clangd/index/cookie_io_functions_t.h.5A08066DC7F05542.idx differ diff --git a/.cache/clangd/index/cpp_type_traits.h.3C474D79576F3A80.idx b/.cache/clangd/index/cpp_type_traits.h.3C474D79576F3A80.idx new file mode 100644 index 0000000..03abc38 Binary files /dev/null and b/.cache/clangd/index/cpp_type_traits.h.3C474D79576F3A80.idx differ diff --git a/.cache/clangd/index/cpu-set.h.B40F3E2869BE4FF9.idx b/.cache/clangd/index/cpu-set.h.B40F3E2869BE4FF9.idx new file mode 100644 index 0000000..92cc22a Binary files /dev/null and b/.cache/clangd/index/cpu-set.h.B40F3E2869BE4FF9.idx differ diff --git a/.cache/clangd/index/cpu_defines.h.6B021CCF8EDC5311.idx b/.cache/clangd/index/cpu_defines.h.6B021CCF8EDC5311.idx new file mode 100644 index 0000000..86b0729 Binary files /dev/null and b/.cache/clangd/index/cpu_defines.h.6B021CCF8EDC5311.idx differ diff --git a/.cache/clangd/index/crypto.c.0FC59A82D615A64D.idx b/.cache/clangd/index/crypto.c.0FC59A82D615A64D.idx new file mode 100644 index 0000000..fe1f75e Binary files /dev/null and b/.cache/clangd/index/crypto.c.0FC59A82D615A64D.idx differ diff --git a/.cache/clangd/index/crypto.h.467636892840B7BC.idx b/.cache/clangd/index/crypto.h.467636892840B7BC.idx new file mode 100644 index 0000000..96f9abf Binary files /dev/null and b/.cache/clangd/index/crypto.h.467636892840B7BC.idx differ diff --git a/.cache/clangd/index/crypto.h.7A67DE9DA228F672.idx b/.cache/clangd/index/crypto.h.7A67DE9DA228F672.idx new file mode 100644 index 0000000..89b9b62 Binary files /dev/null and b/.cache/clangd/index/crypto.h.7A67DE9DA228F672.idx differ diff --git a/.cache/clangd/index/cryptoerr.h.BBCE68DD00883CF1.idx b/.cache/clangd/index/cryptoerr.h.BBCE68DD00883CF1.idx new file mode 100644 index 0000000..e5e9874 Binary files /dev/null and b/.cache/clangd/index/cryptoerr.h.BBCE68DD00883CF1.idx differ diff --git a/.cache/clangd/index/cstdint.FE66805F056AE174.idx b/.cache/clangd/index/cstdint.FE66805F056AE174.idx new file mode 100644 index 0000000..a954d9a Binary files /dev/null and b/.cache/clangd/index/cstdint.FE66805F056AE174.idx differ diff --git a/.cache/clangd/index/cstdio.7C35738EEA255D63.idx b/.cache/clangd/index/cstdio.7C35738EEA255D63.idx new file mode 100644 index 0000000..25c19e3 Binary files /dev/null and b/.cache/clangd/index/cstdio.7C35738EEA255D63.idx differ diff --git a/.cache/clangd/index/cstdlib.0864F928DDD6E563.idx b/.cache/clangd/index/cstdlib.0864F928DDD6E563.idx new file mode 100644 index 0000000..999bc68 Binary files /dev/null and b/.cache/clangd/index/cstdlib.0864F928DDD6E563.idx differ diff --git a/.cache/clangd/index/cstring.0D2337749B0CD808.idx b/.cache/clangd/index/cstring.0D2337749B0CD808.idx new file mode 100644 index 0000000..dbfd976 Binary files /dev/null and b/.cache/clangd/index/cstring.0D2337749B0CD808.idx differ diff --git a/.cache/clangd/index/ctype.h.B268CEA0CECCFC59.idx b/.cache/clangd/index/ctype.h.B268CEA0CECCFC59.idx new file mode 100644 index 0000000..bee2990 Binary files /dev/null and b/.cache/clangd/index/ctype.h.B268CEA0CECCFC59.idx differ diff --git a/.cache/clangd/index/ctype_base.h.762A789D21F18363.idx b/.cache/clangd/index/ctype_base.h.762A789D21F18363.idx new file mode 100644 index 0000000..974101b Binary files /dev/null and b/.cache/clangd/index/ctype_base.h.762A789D21F18363.idx differ diff --git a/.cache/clangd/index/ctype_inline.h.432526632DB890D0.idx b/.cache/clangd/index/ctype_inline.h.432526632DB890D0.idx new file mode 100644 index 0000000..9ac7b3d Binary files /dev/null and b/.cache/clangd/index/ctype_inline.h.432526632DB890D0.idx differ diff --git a/.cache/clangd/index/cwchar.D8027C71D4F77244.idx b/.cache/clangd/index/cwchar.D8027C71D4F77244.idx new file mode 100644 index 0000000..7b0c100 Binary files /dev/null and b/.cache/clangd/index/cwchar.D8027C71D4F77244.idx differ diff --git a/.cache/clangd/index/cwctype.3DF3E41CD43B404C.idx b/.cache/clangd/index/cwctype.3DF3E41CD43B404C.idx new file mode 100644 index 0000000..911cbbe Binary files /dev/null and b/.cache/clangd/index/cwctype.3DF3E41CD43B404C.idx differ diff --git a/.cache/clangd/index/cxxabi_forced.h.E353F8DE38ADAE72.idx b/.cache/clangd/index/cxxabi_forced.h.E353F8DE38ADAE72.idx new file mode 100644 index 0000000..6b16229 Binary files /dev/null and b/.cache/clangd/index/cxxabi_forced.h.E353F8DE38ADAE72.idx differ diff --git a/.cache/clangd/index/cxxabi_init_exception.h.18D754CF3F3A9E7A.idx b/.cache/clangd/index/cxxabi_init_exception.h.18D754CF3F3A9E7A.idx new file mode 100644 index 0000000..c62f5fb Binary files /dev/null and b/.cache/clangd/index/cxxabi_init_exception.h.18D754CF3F3A9E7A.idx differ diff --git a/.cache/clangd/index/debug.h.48A8EF96BB3CB5D7.idx b/.cache/clangd/index/debug.h.48A8EF96BB3CB5D7.idx new file mode 100644 index 0000000..e7c6dbc Binary files /dev/null and b/.cache/clangd/index/debug.h.48A8EF96BB3CB5D7.idx differ diff --git a/.cache/clangd/index/dirent.h.5B5B107C92265F7A.idx b/.cache/clangd/index/dirent.h.5B5B107C92265F7A.idx new file mode 100644 index 0000000..d96cc4b Binary files /dev/null and b/.cache/clangd/index/dirent.h.5B5B107C92265F7A.idx differ diff --git a/.cache/clangd/index/dirent.h.D89913C90A5A044D.idx b/.cache/clangd/index/dirent.h.D89913C90A5A044D.idx new file mode 100644 index 0000000..94c31a5 Binary files /dev/null and b/.cache/clangd/index/dirent.h.D89913C90A5A044D.idx differ diff --git a/.cache/clangd/index/dirent_ext.h.370E1355AD336D8C.idx b/.cache/clangd/index/dirent_ext.h.370E1355AD336D8C.idx new file mode 100644 index 0000000..fd16053 Binary files /dev/null and b/.cache/clangd/index/dirent_ext.h.370E1355AD336D8C.idx differ diff --git a/.cache/clangd/index/dns_sd.h.3058A224AEB54F50.idx b/.cache/clangd/index/dns_sd.h.3058A224AEB54F50.idx new file mode 100644 index 0000000..130dbd1 Binary files /dev/null and b/.cache/clangd/index/dns_sd.h.3058A224AEB54F50.idx differ diff --git a/.cache/clangd/index/dnssd.c.E40A91C3D003F441.idx b/.cache/clangd/index/dnssd.c.E40A91C3D003F441.idx new file mode 100644 index 0000000..4bbdec6 Binary files /dev/null and b/.cache/clangd/index/dnssd.c.E40A91C3D003F441.idx differ diff --git a/.cache/clangd/index/dnssd.h.102E43B473F8BF69.idx b/.cache/clangd/index/dnssd.h.102E43B473F8BF69.idx new file mode 100644 index 0000000..1311211 Binary files /dev/null and b/.cache/clangd/index/dnssd.h.102E43B473F8BF69.idx differ diff --git a/.cache/clangd/index/dnssdint.h.601E326C5229D015.idx b/.cache/clangd/index/dnssdint.h.601E326C5229D015.idx new file mode 100644 index 0000000..da9d1da Binary files /dev/null and b/.cache/clangd/index/dnssdint.h.601E326C5229D015.idx differ diff --git a/.cache/clangd/index/e_os2.h.5F21AF74C1FE4B77.idx b/.cache/clangd/index/e_os2.h.5F21AF74C1FE4B77.idx new file mode 100644 index 0000000..3a1d2a2 Binary files /dev/null and b/.cache/clangd/index/e_os2.h.5F21AF74C1FE4B77.idx differ diff --git a/.cache/clangd/index/ec.h.AF46841256795646.idx b/.cache/clangd/index/ec.h.AF46841256795646.idx new file mode 100644 index 0000000..49e4099 Binary files /dev/null and b/.cache/clangd/index/ec.h.AF46841256795646.idx differ diff --git a/.cache/clangd/index/ecerr.h.50EABE85D1EC767C.idx b/.cache/clangd/index/ecerr.h.50EABE85D1EC767C.idx new file mode 100644 index 0000000..ff525ce Binary files /dev/null and b/.cache/clangd/index/ecerr.h.50EABE85D1EC767C.idx differ diff --git a/.cache/clangd/index/endian.h.1FC6A4D6D2846204.idx b/.cache/clangd/index/endian.h.1FC6A4D6D2846204.idx new file mode 100644 index 0000000..9c8dc89 Binary files /dev/null and b/.cache/clangd/index/endian.h.1FC6A4D6D2846204.idx differ diff --git a/.cache/clangd/index/endian.h.20F8E286A486EE09.idx b/.cache/clangd/index/endian.h.20F8E286A486EE09.idx new file mode 100644 index 0000000..c5d3935 Binary files /dev/null and b/.cache/clangd/index/endian.h.20F8E286A486EE09.idx differ diff --git a/.cache/clangd/index/endianness.h.F2D2CC6251A4EAFA.idx b/.cache/clangd/index/endianness.h.F2D2CC6251A4EAFA.idx new file mode 100644 index 0000000..8b68010 Binary files /dev/null and b/.cache/clangd/index/endianness.h.F2D2CC6251A4EAFA.idx differ diff --git a/.cache/clangd/index/environments.h.AA09E46153D7F6D9.idx b/.cache/clangd/index/environments.h.AA09E46153D7F6D9.idx new file mode 100644 index 0000000..35737ed Binary files /dev/null and b/.cache/clangd/index/environments.h.AA09E46153D7F6D9.idx differ diff --git a/.cache/clangd/index/err.h.984E1B7D861C1F69.idx b/.cache/clangd/index/err.h.984E1B7D861C1F69.idx new file mode 100644 index 0000000..ad00aae Binary files /dev/null and b/.cache/clangd/index/err.h.984E1B7D861C1F69.idx differ diff --git a/.cache/clangd/index/errno-base.h.23FC55A99C73A727.idx b/.cache/clangd/index/errno-base.h.23FC55A99C73A727.idx new file mode 100644 index 0000000..06e3d50 Binary files /dev/null and b/.cache/clangd/index/errno-base.h.23FC55A99C73A727.idx differ diff --git a/.cache/clangd/index/errno.h.063064A523A47758.idx b/.cache/clangd/index/errno.h.063064A523A47758.idx new file mode 100644 index 0000000..602ae6c Binary files /dev/null and b/.cache/clangd/index/errno.h.063064A523A47758.idx differ diff --git a/.cache/clangd/index/errno.h.10013861918E601A.idx b/.cache/clangd/index/errno.h.10013861918E601A.idx new file mode 100644 index 0000000..bc7b404 Binary files /dev/null and b/.cache/clangd/index/errno.h.10013861918E601A.idx differ diff --git a/.cache/clangd/index/errno.h.19BCCCD58DA5623E.idx b/.cache/clangd/index/errno.h.19BCCCD58DA5623E.idx new file mode 100644 index 0000000..91a86ac Binary files /dev/null and b/.cache/clangd/index/errno.h.19BCCCD58DA5623E.idx differ diff --git a/.cache/clangd/index/errno.h.33F6986A3C61A3BF.idx b/.cache/clangd/index/errno.h.33F6986A3C61A3BF.idx new file mode 100644 index 0000000..37312f7 Binary files /dev/null and b/.cache/clangd/index/errno.h.33F6986A3C61A3BF.idx differ diff --git a/.cache/clangd/index/errno.h.3F8ECD84FB880B64.idx b/.cache/clangd/index/errno.h.3F8ECD84FB880B64.idx new file mode 100644 index 0000000..5f43f38 Binary files /dev/null and b/.cache/clangd/index/errno.h.3F8ECD84FB880B64.idx differ diff --git a/.cache/clangd/index/error_constants.h.C7C4F947F406AC13.idx b/.cache/clangd/index/error_constants.h.C7C4F947F406AC13.idx new file mode 100644 index 0000000..5721910 Binary files /dev/null and b/.cache/clangd/index/error_constants.h.C7C4F947F406AC13.idx differ diff --git a/.cache/clangd/index/error_t.h.58D3895A591B592D.idx b/.cache/clangd/index/error_t.h.58D3895A591B592D.idx new file mode 100644 index 0000000..461c1e4 Binary files /dev/null and b/.cache/clangd/index/error_t.h.58D3895A591B592D.idx differ diff --git a/.cache/clangd/index/evp.h.4C5AB98261A4D6CD.idx b/.cache/clangd/index/evp.h.4C5AB98261A4D6CD.idx new file mode 100644 index 0000000..4b112b7 Binary files /dev/null and b/.cache/clangd/index/evp.h.4C5AB98261A4D6CD.idx differ diff --git a/.cache/clangd/index/evperr.h.02F8C82BB87A8536.idx b/.cache/clangd/index/evperr.h.02F8C82BB87A8536.idx new file mode 100644 index 0000000..1a91530 Binary files /dev/null and b/.cache/clangd/index/evperr.h.02F8C82BB87A8536.idx differ diff --git a/.cache/clangd/index/exception.DCA4493F80FC6DAF.idx b/.cache/clangd/index/exception.DCA4493F80FC6DAF.idx new file mode 100644 index 0000000..632550a Binary files /dev/null and b/.cache/clangd/index/exception.DCA4493F80FC6DAF.idx differ diff --git a/.cache/clangd/index/exception.h.3A5914EB9F22EA73.idx b/.cache/clangd/index/exception.h.3A5914EB9F22EA73.idx new file mode 100644 index 0000000..83aa3e3 Binary files /dev/null and b/.cache/clangd/index/exception.h.3A5914EB9F22EA73.idx differ diff --git a/.cache/clangd/index/exception_defines.h.A386DCE292C5326F.idx b/.cache/clangd/index/exception_defines.h.A386DCE292C5326F.idx new file mode 100644 index 0000000..28ae60a Binary files /dev/null and b/.cache/clangd/index/exception_defines.h.A386DCE292C5326F.idx differ diff --git a/.cache/clangd/index/exception_ptr.h.8A4FD83D16E0DA49.idx b/.cache/clangd/index/exception_ptr.h.8A4FD83D16E0DA49.idx new file mode 100644 index 0000000..9928ed4 Binary files /dev/null and b/.cache/clangd/index/exception_ptr.h.8A4FD83D16E0DA49.idx differ diff --git a/.cache/clangd/index/fairplay.h.3D114D8E294D83E6.idx b/.cache/clangd/index/fairplay.h.3D114D8E294D83E6.idx new file mode 100644 index 0000000..9bfc426 Binary files /dev/null and b/.cache/clangd/index/fairplay.h.3D114D8E294D83E6.idx differ diff --git a/.cache/clangd/index/fairplay_playfair.c.3A41E16A3A9A443B.idx b/.cache/clangd/index/fairplay_playfair.c.3A41E16A3A9A443B.idx new file mode 100644 index 0000000..391747e Binary files /dev/null and b/.cache/clangd/index/fairplay_playfair.c.3A41E16A3A9A443B.idx differ diff --git a/.cache/clangd/index/falloc.h.5C586ED1D55FF40E.idx b/.cache/clangd/index/falloc.h.5C586ED1D55FF40E.idx new file mode 100644 index 0000000..737e93b Binary files /dev/null and b/.cache/clangd/index/falloc.h.5C586ED1D55FF40E.idx differ diff --git a/.cache/clangd/index/fcntl-linux.h.B59A22F1AA8971F5.idx b/.cache/clangd/index/fcntl-linux.h.B59A22F1AA8971F5.idx new file mode 100644 index 0000000..6c784e9 Binary files /dev/null and b/.cache/clangd/index/fcntl-linux.h.B59A22F1AA8971F5.idx differ diff --git a/.cache/clangd/index/fcntl.h.B275FA9E57415C0F.idx b/.cache/clangd/index/fcntl.h.B275FA9E57415C0F.idx new file mode 100644 index 0000000..28ff2a3 Binary files /dev/null and b/.cache/clangd/index/fcntl.h.B275FA9E57415C0F.idx differ diff --git a/.cache/clangd/index/fcntl.h.FAA28F3AAA9C8758.idx b/.cache/clangd/index/fcntl.h.FAA28F3AAA9C8758.idx new file mode 100644 index 0000000..ad3399e Binary files /dev/null and b/.cache/clangd/index/fcntl.h.FAA28F3AAA9C8758.idx differ diff --git a/.cache/clangd/index/fcup_request.h.D421000EC43A51BA.idx b/.cache/clangd/index/fcup_request.h.D421000EC43A51BA.idx new file mode 100644 index 0000000..5b598fc Binary files /dev/null and b/.cache/clangd/index/fcup_request.h.D421000EC43A51BA.idx differ diff --git a/.cache/clangd/index/features.h.5A4ED7C80201A723.idx b/.cache/clangd/index/features.h.5A4ED7C80201A723.idx new file mode 100644 index 0000000..3fad115 Binary files /dev/null and b/.cache/clangd/index/features.h.5A4ED7C80201A723.idx differ diff --git a/.cache/clangd/index/float.h.E398714F70D64DD2.idx b/.cache/clangd/index/float.h.E398714F70D64DD2.idx new file mode 100644 index 0000000..e9d2f63 Binary files /dev/null and b/.cache/clangd/index/float.h.E398714F70D64DD2.idx differ diff --git a/.cache/clangd/index/floatn-common.h.0FE8C4DDCB84E088.idx b/.cache/clangd/index/floatn-common.h.0FE8C4DDCB84E088.idx new file mode 100644 index 0000000..a2deefc Binary files /dev/null and b/.cache/clangd/index/floatn-common.h.0FE8C4DDCB84E088.idx differ diff --git a/.cache/clangd/index/floatn.h.DA21C738CC1B6ABF.idx b/.cache/clangd/index/floatn.h.DA21C738CC1B6ABF.idx new file mode 100644 index 0000000..3b6606c Binary files /dev/null and b/.cache/clangd/index/floatn.h.DA21C738CC1B6ABF.idx differ diff --git a/.cache/clangd/index/flt-eval-method.h.8058951BDB657B4B.idx b/.cache/clangd/index/flt-eval-method.h.8058951BDB657B4B.idx new file mode 100644 index 0000000..998cda4 Binary files /dev/null and b/.cache/clangd/index/flt-eval-method.h.8058951BDB657B4B.idx differ diff --git a/.cache/clangd/index/fp-fast.h.72CDF40E7648EF43.idx b/.cache/clangd/index/fp-fast.h.72CDF40E7648EF43.idx new file mode 100644 index 0000000..a325d12 Binary files /dev/null and b/.cache/clangd/index/fp-fast.h.72CDF40E7648EF43.idx differ diff --git a/.cache/clangd/index/fp-logb.h.EA7AC5545F14BC26.idx b/.cache/clangd/index/fp-logb.h.EA7AC5545F14BC26.idx new file mode 100644 index 0000000..90fdc5e Binary files /dev/null and b/.cache/clangd/index/fp-logb.h.EA7AC5545F14BC26.idx differ diff --git a/.cache/clangd/index/fstream.65E7135B049F16EB.idx b/.cache/clangd/index/fstream.65E7135B049F16EB.idx new file mode 100644 index 0000000..2a64a5e Binary files /dev/null and b/.cache/clangd/index/fstream.65E7135B049F16EB.idx differ diff --git a/.cache/clangd/index/fstream.tcc.E755F3CDA18DE129.idx b/.cache/clangd/index/fstream.tcc.E755F3CDA18DE129.idx new file mode 100644 index 0000000..a6bc097 Binary files /dev/null and b/.cache/clangd/index/fstream.tcc.E755F3CDA18DE129.idx differ diff --git a/.cache/clangd/index/functexcept.h.C9A0D011C987C0AC.idx b/.cache/clangd/index/functexcept.h.C9A0D011C987C0AC.idx new file mode 100644 index 0000000..57e6c0e Binary files /dev/null and b/.cache/clangd/index/functexcept.h.C9A0D011C987C0AC.idx differ diff --git a/.cache/clangd/index/functional_hash.h.DB61F04D680B1C94.idx b/.cache/clangd/index/functional_hash.h.DB61F04D680B1C94.idx new file mode 100644 index 0000000..d6cf4f9 Binary files /dev/null and b/.cache/clangd/index/functional_hash.h.DB61F04D680B1C94.idx differ diff --git a/.cache/clangd/index/galloca.h.54B042F70E453C31.idx b/.cache/clangd/index/galloca.h.54B042F70E453C31.idx new file mode 100644 index 0000000..92ed6dd Binary files /dev/null and b/.cache/clangd/index/galloca.h.54B042F70E453C31.idx differ diff --git a/.cache/clangd/index/gallocator.h.4151C99B96B8350A.idx b/.cache/clangd/index/gallocator.h.4151C99B96B8350A.idx new file mode 100644 index 0000000..ef6e71b Binary files /dev/null and b/.cache/clangd/index/gallocator.h.4151C99B96B8350A.idx differ diff --git a/.cache/clangd/index/garray.h.09B0D1F5BF90092A.idx b/.cache/clangd/index/garray.h.09B0D1F5BF90092A.idx new file mode 100644 index 0000000..98af600 Binary files /dev/null and b/.cache/clangd/index/garray.h.09B0D1F5BF90092A.idx differ diff --git a/.cache/clangd/index/gasyncqueue.h.68FE2FC70E2BE824.idx b/.cache/clangd/index/gasyncqueue.h.68FE2FC70E2BE824.idx new file mode 100644 index 0000000..57fdf4d Binary files /dev/null and b/.cache/clangd/index/gasyncqueue.h.68FE2FC70E2BE824.idx differ diff --git a/.cache/clangd/index/gatomic.h.84551B453A7FD962.idx b/.cache/clangd/index/gatomic.h.84551B453A7FD962.idx new file mode 100644 index 0000000..ee945e9 Binary files /dev/null and b/.cache/clangd/index/gatomic.h.84551B453A7FD962.idx differ diff --git a/.cache/clangd/index/gbacktrace.h.097A018C2BEEB459.idx b/.cache/clangd/index/gbacktrace.h.097A018C2BEEB459.idx new file mode 100644 index 0000000..865dc3a Binary files /dev/null and b/.cache/clangd/index/gbacktrace.h.097A018C2BEEB459.idx differ diff --git a/.cache/clangd/index/gbase64.h.3451DB79A9A9FE81.idx b/.cache/clangd/index/gbase64.h.3451DB79A9A9FE81.idx new file mode 100644 index 0000000..29b4966 Binary files /dev/null and b/.cache/clangd/index/gbase64.h.3451DB79A9A9FE81.idx differ diff --git a/.cache/clangd/index/gbinding.h.7E1B4C283EDA1666.idx b/.cache/clangd/index/gbinding.h.7E1B4C283EDA1666.idx new file mode 100644 index 0000000..e926ce2 Binary files /dev/null and b/.cache/clangd/index/gbinding.h.7E1B4C283EDA1666.idx differ diff --git a/.cache/clangd/index/gbitlock.h.7D130B5F87795B07.idx b/.cache/clangd/index/gbitlock.h.7D130B5F87795B07.idx new file mode 100644 index 0000000..6326115 Binary files /dev/null and b/.cache/clangd/index/gbitlock.h.7D130B5F87795B07.idx differ diff --git a/.cache/clangd/index/gbookmarkfile.h.2097E85161667F2A.idx b/.cache/clangd/index/gbookmarkfile.h.2097E85161667F2A.idx new file mode 100644 index 0000000..1bd5028 Binary files /dev/null and b/.cache/clangd/index/gbookmarkfile.h.2097E85161667F2A.idx differ diff --git a/.cache/clangd/index/gboxed.h.884357187D928174.idx b/.cache/clangd/index/gboxed.h.884357187D928174.idx new file mode 100644 index 0000000..086f15d Binary files /dev/null and b/.cache/clangd/index/gboxed.h.884357187D928174.idx differ diff --git a/.cache/clangd/index/gbytes.h.034519EA4647F40B.idx b/.cache/clangd/index/gbytes.h.034519EA4647F40B.idx new file mode 100644 index 0000000..7b58492 Binary files /dev/null and b/.cache/clangd/index/gbytes.h.034519EA4647F40B.idx differ diff --git a/.cache/clangd/index/gcache.h.3BDA13ED083CB035.idx b/.cache/clangd/index/gcache.h.3BDA13ED083CB035.idx new file mode 100644 index 0000000..6447fb4 Binary files /dev/null and b/.cache/clangd/index/gcache.h.3BDA13ED083CB035.idx differ diff --git a/.cache/clangd/index/gcharset.h.BA4251C28E72F5A0.idx b/.cache/clangd/index/gcharset.h.BA4251C28E72F5A0.idx new file mode 100644 index 0000000..3efe924 Binary files /dev/null and b/.cache/clangd/index/gcharset.h.BA4251C28E72F5A0.idx differ diff --git a/.cache/clangd/index/gchecksum.h.F9287F3B87184D6A.idx b/.cache/clangd/index/gchecksum.h.F9287F3B87184D6A.idx new file mode 100644 index 0000000..b8b4149 Binary files /dev/null and b/.cache/clangd/index/gchecksum.h.F9287F3B87184D6A.idx differ diff --git a/.cache/clangd/index/gclosure.h.F317D4582C612010.idx b/.cache/clangd/index/gclosure.h.F317D4582C612010.idx new file mode 100644 index 0000000..7a1fe21 Binary files /dev/null and b/.cache/clangd/index/gclosure.h.F317D4582C612010.idx differ diff --git a/.cache/clangd/index/gcompletion.h.E46D6407D23D8D89.idx b/.cache/clangd/index/gcompletion.h.E46D6407D23D8D89.idx new file mode 100644 index 0000000..d159549 Binary files /dev/null and b/.cache/clangd/index/gcompletion.h.E46D6407D23D8D89.idx differ diff --git a/.cache/clangd/index/gconvert.h.52A8B61739AB9DD6.idx b/.cache/clangd/index/gconvert.h.52A8B61739AB9DD6.idx new file mode 100644 index 0000000..46d8671 Binary files /dev/null and b/.cache/clangd/index/gconvert.h.52A8B61739AB9DD6.idx differ diff --git a/.cache/clangd/index/gdataset.h.26505C2B4F32DA35.idx b/.cache/clangd/index/gdataset.h.26505C2B4F32DA35.idx new file mode 100644 index 0000000..723d5af Binary files /dev/null and b/.cache/clangd/index/gdataset.h.26505C2B4F32DA35.idx differ diff --git a/.cache/clangd/index/gdate.h.631E1B63318FC02C.idx b/.cache/clangd/index/gdate.h.631E1B63318FC02C.idx new file mode 100644 index 0000000..39241a6 Binary files /dev/null and b/.cache/clangd/index/gdate.h.631E1B63318FC02C.idx differ diff --git a/.cache/clangd/index/gdatetime.h.C33E04E02FCF3D2D.idx b/.cache/clangd/index/gdatetime.h.C33E04E02FCF3D2D.idx new file mode 100644 index 0000000..4f52819 Binary files /dev/null and b/.cache/clangd/index/gdatetime.h.C33E04E02FCF3D2D.idx differ diff --git a/.cache/clangd/index/gdir.h.7D335F595344E85E.idx b/.cache/clangd/index/gdir.h.7D335F595344E85E.idx new file mode 100644 index 0000000..0b512d0 Binary files /dev/null and b/.cache/clangd/index/gdir.h.7D335F595344E85E.idx differ diff --git a/.cache/clangd/index/genums.h.9CA17D783649FE2A.idx b/.cache/clangd/index/genums.h.9CA17D783649FE2A.idx new file mode 100644 index 0000000..c5bd431 Binary files /dev/null and b/.cache/clangd/index/genums.h.9CA17D783649FE2A.idx differ diff --git a/.cache/clangd/index/genviron.h.97B2F184158CE759.idx b/.cache/clangd/index/genviron.h.97B2F184158CE759.idx new file mode 100644 index 0000000..6bd6d83 Binary files /dev/null and b/.cache/clangd/index/genviron.h.97B2F184158CE759.idx differ diff --git a/.cache/clangd/index/gerror.h.8188390F08D1EB1C.idx b/.cache/clangd/index/gerror.h.8188390F08D1EB1C.idx new file mode 100644 index 0000000..b26cf4a Binary files /dev/null and b/.cache/clangd/index/gerror.h.8188390F08D1EB1C.idx differ diff --git a/.cache/clangd/index/getopt_core.h.D7307CF3828B57B6.idx b/.cache/clangd/index/getopt_core.h.D7307CF3828B57B6.idx new file mode 100644 index 0000000..925dfd8 Binary files /dev/null and b/.cache/clangd/index/getopt_core.h.D7307CF3828B57B6.idx differ diff --git a/.cache/clangd/index/getopt_posix.h.EAB1D9CAA7EB87EE.idx b/.cache/clangd/index/getopt_posix.h.EAB1D9CAA7EB87EE.idx new file mode 100644 index 0000000..8602eb2 Binary files /dev/null and b/.cache/clangd/index/getopt_posix.h.EAB1D9CAA7EB87EE.idx differ diff --git a/.cache/clangd/index/gfileutils.h.EAD7E8C17ABA3E7F.idx b/.cache/clangd/index/gfileutils.h.EAD7E8C17ABA3E7F.idx new file mode 100644 index 0000000..39a017f Binary files /dev/null and b/.cache/clangd/index/gfileutils.h.EAD7E8C17ABA3E7F.idx differ diff --git a/.cache/clangd/index/ggettext.h.0CB3D9AB8FC13B6A.idx b/.cache/clangd/index/ggettext.h.0CB3D9AB8FC13B6A.idx new file mode 100644 index 0000000..360cdef Binary files /dev/null and b/.cache/clangd/index/ggettext.h.0CB3D9AB8FC13B6A.idx differ diff --git a/.cache/clangd/index/ghash.h.FB45E3140496C602.idx b/.cache/clangd/index/ghash.h.FB45E3140496C602.idx new file mode 100644 index 0000000..5af64e3 Binary files /dev/null and b/.cache/clangd/index/ghash.h.FB45E3140496C602.idx differ diff --git a/.cache/clangd/index/ghmac.h.3919D61BA922E931.idx b/.cache/clangd/index/ghmac.h.3919D61BA922E931.idx new file mode 100644 index 0000000..a8cbb0a Binary files /dev/null and b/.cache/clangd/index/ghmac.h.3919D61BA922E931.idx differ diff --git a/.cache/clangd/index/ghook.h.EE2CFD0E8A88DD5F.idx b/.cache/clangd/index/ghook.h.EE2CFD0E8A88DD5F.idx new file mode 100644 index 0000000..4a53521 Binary files /dev/null and b/.cache/clangd/index/ghook.h.EE2CFD0E8A88DD5F.idx differ diff --git a/.cache/clangd/index/ghostutils.h.0D72BD9673680AAB.idx b/.cache/clangd/index/ghostutils.h.0D72BD9673680AAB.idx new file mode 100644 index 0000000..a021652 Binary files /dev/null and b/.cache/clangd/index/ghostutils.h.0D72BD9673680AAB.idx differ diff --git a/.cache/clangd/index/giochannel.h.2A8F2991A3FD1EEA.idx b/.cache/clangd/index/giochannel.h.2A8F2991A3FD1EEA.idx new file mode 100644 index 0000000..e353ab6 Binary files /dev/null and b/.cache/clangd/index/giochannel.h.2A8F2991A3FD1EEA.idx differ diff --git a/.cache/clangd/index/gkeyfile.h.8210EDFC3DEB4682.idx b/.cache/clangd/index/gkeyfile.h.8210EDFC3DEB4682.idx new file mode 100644 index 0000000..6c24395 Binary files /dev/null and b/.cache/clangd/index/gkeyfile.h.8210EDFC3DEB4682.idx differ diff --git a/.cache/clangd/index/glib-autocleanups.h.1A2A77E7C624042B.idx b/.cache/clangd/index/glib-autocleanups.h.1A2A77E7C624042B.idx new file mode 100644 index 0000000..64b3bba Binary files /dev/null and b/.cache/clangd/index/glib-autocleanups.h.1A2A77E7C624042B.idx differ diff --git a/.cache/clangd/index/glib-compat.h.43F18B0FE436A027.idx b/.cache/clangd/index/glib-compat.h.43F18B0FE436A027.idx new file mode 100644 index 0000000..195a685 Binary files /dev/null and b/.cache/clangd/index/glib-compat.h.43F18B0FE436A027.idx differ diff --git a/.cache/clangd/index/glib-enumtypes.h.DC8F085FA3DAAC96.idx b/.cache/clangd/index/glib-enumtypes.h.DC8F085FA3DAAC96.idx new file mode 100644 index 0000000..dc70bc2 Binary files /dev/null and b/.cache/clangd/index/glib-enumtypes.h.DC8F085FA3DAAC96.idx differ diff --git a/.cache/clangd/index/glib-object.h.BEF6A6E2590792F3.idx b/.cache/clangd/index/glib-object.h.BEF6A6E2590792F3.idx new file mode 100644 index 0000000..963dd1b Binary files /dev/null and b/.cache/clangd/index/glib-object.h.BEF6A6E2590792F3.idx differ diff --git a/.cache/clangd/index/glib-types.h.29E5B1368CD22D48.idx b/.cache/clangd/index/glib-types.h.29E5B1368CD22D48.idx new file mode 100644 index 0000000..c88485a Binary files /dev/null and b/.cache/clangd/index/glib-types.h.29E5B1368CD22D48.idx differ diff --git a/.cache/clangd/index/glib-unix.h.0FD2EB648D737790.idx b/.cache/clangd/index/glib-unix.h.0FD2EB648D737790.idx new file mode 100644 index 0000000..959d67f Binary files /dev/null and b/.cache/clangd/index/glib-unix.h.0FD2EB648D737790.idx differ diff --git a/.cache/clangd/index/glib.h.ECE25ABDBC8750A8.idx b/.cache/clangd/index/glib.h.ECE25ABDBC8750A8.idx new file mode 100644 index 0000000..994c494 Binary files /dev/null and b/.cache/clangd/index/glib.h.ECE25ABDBC8750A8.idx differ diff --git a/.cache/clangd/index/glibconfig.h.C2349237D9BC9DD2.idx b/.cache/clangd/index/glibconfig.h.C2349237D9BC9DD2.idx new file mode 100644 index 0000000..ba3461a Binary files /dev/null and b/.cache/clangd/index/glibconfig.h.C2349237D9BC9DD2.idx differ diff --git a/.cache/clangd/index/glist.h.89BC8E2DFD8CC693.idx b/.cache/clangd/index/glist.h.89BC8E2DFD8CC693.idx new file mode 100644 index 0000000..84f510a Binary files /dev/null and b/.cache/clangd/index/glist.h.89BC8E2DFD8CC693.idx differ diff --git a/.cache/clangd/index/global.h.1531EF5239DECDA9.idx b/.cache/clangd/index/global.h.1531EF5239DECDA9.idx new file mode 100644 index 0000000..8385689 Binary files /dev/null and b/.cache/clangd/index/global.h.1531EF5239DECDA9.idx differ diff --git a/.cache/clangd/index/gmacros.h.523DF9E12ACFEEB0.idx b/.cache/clangd/index/gmacros.h.523DF9E12ACFEEB0.idx new file mode 100644 index 0000000..22ec553 Binary files /dev/null and b/.cache/clangd/index/gmacros.h.523DF9E12ACFEEB0.idx differ diff --git a/.cache/clangd/index/gmain.h.5082CA6C77440896.idx b/.cache/clangd/index/gmain.h.5082CA6C77440896.idx new file mode 100644 index 0000000..5ac328e Binary files /dev/null and b/.cache/clangd/index/gmain.h.5082CA6C77440896.idx differ diff --git a/.cache/clangd/index/gmain.h.553193725C120F49.idx b/.cache/clangd/index/gmain.h.553193725C120F49.idx new file mode 100644 index 0000000..8d65a50 Binary files /dev/null and b/.cache/clangd/index/gmain.h.553193725C120F49.idx differ diff --git a/.cache/clangd/index/gmappedfile.h.D2EBEA1F4BEF26CC.idx b/.cache/clangd/index/gmappedfile.h.D2EBEA1F4BEF26CC.idx new file mode 100644 index 0000000..9ede4f1 Binary files /dev/null and b/.cache/clangd/index/gmappedfile.h.D2EBEA1F4BEF26CC.idx differ diff --git a/.cache/clangd/index/gmarkup.h.CA8835DF7FF1CB85.idx b/.cache/clangd/index/gmarkup.h.CA8835DF7FF1CB85.idx new file mode 100644 index 0000000..5f0b575 Binary files /dev/null and b/.cache/clangd/index/gmarkup.h.CA8835DF7FF1CB85.idx differ diff --git a/.cache/clangd/index/gmarshal.h.6E63FBE717F7E343.idx b/.cache/clangd/index/gmarshal.h.6E63FBE717F7E343.idx new file mode 100644 index 0000000..8944ee0 Binary files /dev/null and b/.cache/clangd/index/gmarshal.h.6E63FBE717F7E343.idx differ diff --git a/.cache/clangd/index/gmem.h.66363CA6EBCE8AB1.idx b/.cache/clangd/index/gmem.h.66363CA6EBCE8AB1.idx new file mode 100644 index 0000000..4bf0126 Binary files /dev/null and b/.cache/clangd/index/gmem.h.66363CA6EBCE8AB1.idx differ diff --git a/.cache/clangd/index/gmessages.h.122E0915CDAE4401.idx b/.cache/clangd/index/gmessages.h.122E0915CDAE4401.idx new file mode 100644 index 0000000..15a1dd2 Binary files /dev/null and b/.cache/clangd/index/gmessages.h.122E0915CDAE4401.idx differ diff --git a/.cache/clangd/index/gnode.h.DA5FB7FBFB17FCD2.idx b/.cache/clangd/index/gnode.h.DA5FB7FBFB17FCD2.idx new file mode 100644 index 0000000..7a5b415 Binary files /dev/null and b/.cache/clangd/index/gnode.h.DA5FB7FBFB17FCD2.idx differ diff --git a/.cache/clangd/index/gobject-autocleanups.h.2FC7EB05E1071B45.idx b/.cache/clangd/index/gobject-autocleanups.h.2FC7EB05E1071B45.idx new file mode 100644 index 0000000..8122558 Binary files /dev/null and b/.cache/clangd/index/gobject-autocleanups.h.2FC7EB05E1071B45.idx differ diff --git a/.cache/clangd/index/gobject.h.9B7896429D76A7E8.idx b/.cache/clangd/index/gobject.h.9B7896429D76A7E8.idx new file mode 100644 index 0000000..157c41a Binary files /dev/null and b/.cache/clangd/index/gobject.h.9B7896429D76A7E8.idx differ diff --git a/.cache/clangd/index/goption.h.273A58029F14D024.idx b/.cache/clangd/index/goption.h.273A58029F14D024.idx new file mode 100644 index 0000000..92a4a25 Binary files /dev/null and b/.cache/clangd/index/goption.h.273A58029F14D024.idx differ diff --git a/.cache/clangd/index/gparam.h.31AAB6509E3522A8.idx b/.cache/clangd/index/gparam.h.31AAB6509E3522A8.idx new file mode 100644 index 0000000..5181af5 Binary files /dev/null and b/.cache/clangd/index/gparam.h.31AAB6509E3522A8.idx differ diff --git a/.cache/clangd/index/gparamspecs.h.3F7611AFE4D3144D.idx b/.cache/clangd/index/gparamspecs.h.3F7611AFE4D3144D.idx new file mode 100644 index 0000000..66152dd Binary files /dev/null and b/.cache/clangd/index/gparamspecs.h.3F7611AFE4D3144D.idx differ diff --git a/.cache/clangd/index/gpattern.h.60084FFE0F819495.idx b/.cache/clangd/index/gpattern.h.60084FFE0F819495.idx new file mode 100644 index 0000000..5e2dc7f Binary files /dev/null and b/.cache/clangd/index/gpattern.h.60084FFE0F819495.idx differ diff --git a/.cache/clangd/index/gpoll.h.0C1A15A3CDC43C8C.idx b/.cache/clangd/index/gpoll.h.0C1A15A3CDC43C8C.idx new file mode 100644 index 0000000..e2a08c6 Binary files /dev/null and b/.cache/clangd/index/gpoll.h.0C1A15A3CDC43C8C.idx differ diff --git a/.cache/clangd/index/gprimes.h.D33971443DBB901F.idx b/.cache/clangd/index/gprimes.h.D33971443DBB901F.idx new file mode 100644 index 0000000..d8c61b8 Binary files /dev/null and b/.cache/clangd/index/gprimes.h.D33971443DBB901F.idx differ diff --git a/.cache/clangd/index/gqsort.h.6C47F209ED4E8EAB.idx b/.cache/clangd/index/gqsort.h.6C47F209ED4E8EAB.idx new file mode 100644 index 0000000..5aeb796 Binary files /dev/null and b/.cache/clangd/index/gqsort.h.6C47F209ED4E8EAB.idx differ diff --git a/.cache/clangd/index/gquark.h.68680E19F5DCDAF2.idx b/.cache/clangd/index/gquark.h.68680E19F5DCDAF2.idx new file mode 100644 index 0000000..a3ea4d5 Binary files /dev/null and b/.cache/clangd/index/gquark.h.68680E19F5DCDAF2.idx differ diff --git a/.cache/clangd/index/gqueue.h.88944AAE287C8BB3.idx b/.cache/clangd/index/gqueue.h.88944AAE287C8BB3.idx new file mode 100644 index 0000000..c8ff64a Binary files /dev/null and b/.cache/clangd/index/gqueue.h.88944AAE287C8BB3.idx differ diff --git a/.cache/clangd/index/grand.h.F97A061F5FBEDBEA.idx b/.cache/clangd/index/grand.h.F97A061F5FBEDBEA.idx new file mode 100644 index 0000000..ac80088 Binary files /dev/null and b/.cache/clangd/index/grand.h.F97A061F5FBEDBEA.idx differ diff --git a/.cache/clangd/index/grcbox.h.7F73AC2E2B8546FD.idx b/.cache/clangd/index/grcbox.h.7F73AC2E2B8546FD.idx new file mode 100644 index 0000000..6a154d7 Binary files /dev/null and b/.cache/clangd/index/grcbox.h.7F73AC2E2B8546FD.idx differ diff --git a/.cache/clangd/index/grefcount.h.3A6D45B7651B26A2.idx b/.cache/clangd/index/grefcount.h.3A6D45B7651B26A2.idx new file mode 100644 index 0000000..20d7a82 Binary files /dev/null and b/.cache/clangd/index/grefcount.h.3A6D45B7651B26A2.idx differ diff --git a/.cache/clangd/index/grefstring.h.A51D8B1A8AE23328.idx b/.cache/clangd/index/grefstring.h.A51D8B1A8AE23328.idx new file mode 100644 index 0000000..2a80fa2 Binary files /dev/null and b/.cache/clangd/index/grefstring.h.A51D8B1A8AE23328.idx differ diff --git a/.cache/clangd/index/gregex.h.AABCC1616D296E4A.idx b/.cache/clangd/index/gregex.h.AABCC1616D296E4A.idx new file mode 100644 index 0000000..dbb4b0d Binary files /dev/null and b/.cache/clangd/index/gregex.h.AABCC1616D296E4A.idx differ diff --git a/.cache/clangd/index/grel.h.347D3880BE9F5FCE.idx b/.cache/clangd/index/grel.h.347D3880BE9F5FCE.idx new file mode 100644 index 0000000..f5e43ff Binary files /dev/null and b/.cache/clangd/index/grel.h.347D3880BE9F5FCE.idx differ diff --git a/.cache/clangd/index/gscanner.h.478455E9AFF68EA2.idx b/.cache/clangd/index/gscanner.h.478455E9AFF68EA2.idx new file mode 100644 index 0000000..1156588 Binary files /dev/null and b/.cache/clangd/index/gscanner.h.478455E9AFF68EA2.idx differ diff --git a/.cache/clangd/index/gsequence.h.0844E2366A1518A4.idx b/.cache/clangd/index/gsequence.h.0844E2366A1518A4.idx new file mode 100644 index 0000000..db7d70c Binary files /dev/null and b/.cache/clangd/index/gsequence.h.0844E2366A1518A4.idx differ diff --git a/.cache/clangd/index/gshell.h.89B3CB11533C1DB8.idx b/.cache/clangd/index/gshell.h.89B3CB11533C1DB8.idx new file mode 100644 index 0000000..ba112b5 Binary files /dev/null and b/.cache/clangd/index/gshell.h.89B3CB11533C1DB8.idx differ diff --git a/.cache/clangd/index/gsignal.h.4F67FDB621ECD379.idx b/.cache/clangd/index/gsignal.h.4F67FDB621ECD379.idx new file mode 100644 index 0000000..7639d93 Binary files /dev/null and b/.cache/clangd/index/gsignal.h.4F67FDB621ECD379.idx differ diff --git a/.cache/clangd/index/gslice.h.03535A2A2D0AC6F9.idx b/.cache/clangd/index/gslice.h.03535A2A2D0AC6F9.idx new file mode 100644 index 0000000..59d4601 Binary files /dev/null and b/.cache/clangd/index/gslice.h.03535A2A2D0AC6F9.idx differ diff --git a/.cache/clangd/index/gslist.h.72EC02C10AD69603.idx b/.cache/clangd/index/gslist.h.72EC02C10AD69603.idx new file mode 100644 index 0000000..f156cd1 Binary files /dev/null and b/.cache/clangd/index/gslist.h.72EC02C10AD69603.idx differ diff --git a/.cache/clangd/index/gsourceclosure.h.7B43B08EC4694448.idx b/.cache/clangd/index/gsourceclosure.h.7B43B08EC4694448.idx new file mode 100644 index 0000000..1107ddc Binary files /dev/null and b/.cache/clangd/index/gsourceclosure.h.7B43B08EC4694448.idx differ diff --git a/.cache/clangd/index/gspawn.h.23CB506F29AC3C7E.idx b/.cache/clangd/index/gspawn.h.23CB506F29AC3C7E.idx new file mode 100644 index 0000000..d84280d Binary files /dev/null and b/.cache/clangd/index/gspawn.h.23CB506F29AC3C7E.idx differ diff --git a/.cache/clangd/index/gst.h.C60E095BCC84C10B.idx b/.cache/clangd/index/gst.h.C60E095BCC84C10B.idx new file mode 100644 index 0000000..33905a7 Binary files /dev/null and b/.cache/clangd/index/gst.h.C60E095BCC84C10B.idx differ diff --git a/.cache/clangd/index/gstallocator.h.2BF0F08696F9C278.idx b/.cache/clangd/index/gstallocator.h.2BF0F08696F9C278.idx new file mode 100644 index 0000000..242cfcb Binary files /dev/null and b/.cache/clangd/index/gstallocator.h.2BF0F08696F9C278.idx differ diff --git a/.cache/clangd/index/gstappsrc.h.254F751A833F7B96.idx b/.cache/clangd/index/gstappsrc.h.254F751A833F7B96.idx new file mode 100644 index 0000000..b5161cb Binary files /dev/null and b/.cache/clangd/index/gstappsrc.h.254F751A833F7B96.idx differ diff --git a/.cache/clangd/index/gstatomicqueue.h.B251CCE1DE159E3C.idx b/.cache/clangd/index/gstatomicqueue.h.B251CCE1DE159E3C.idx new file mode 100644 index 0000000..c190bc7 Binary files /dev/null and b/.cache/clangd/index/gstatomicqueue.h.B251CCE1DE159E3C.idx differ diff --git a/.cache/clangd/index/gstbasesrc.h.B384C626E42794F9.idx b/.cache/clangd/index/gstbasesrc.h.B384C626E42794F9.idx new file mode 100644 index 0000000..05bc5da Binary files /dev/null and b/.cache/clangd/index/gstbasesrc.h.B384C626E42794F9.idx differ diff --git a/.cache/clangd/index/gstbin.h.D4FB506FD5A8FDA4.idx b/.cache/clangd/index/gstbin.h.D4FB506FD5A8FDA4.idx new file mode 100644 index 0000000..f57021a Binary files /dev/null and b/.cache/clangd/index/gstbin.h.D4FB506FD5A8FDA4.idx differ diff --git a/.cache/clangd/index/gstbuffer.h.2C4CD941F5C2A169.idx b/.cache/clangd/index/gstbuffer.h.2C4CD941F5C2A169.idx new file mode 100644 index 0000000..b537798 Binary files /dev/null and b/.cache/clangd/index/gstbuffer.h.2C4CD941F5C2A169.idx differ diff --git a/.cache/clangd/index/gstbufferlist.h.2F1EB919D8BA244F.idx b/.cache/clangd/index/gstbufferlist.h.2F1EB919D8BA244F.idx new file mode 100644 index 0000000..6879d9c Binary files /dev/null and b/.cache/clangd/index/gstbufferlist.h.2F1EB919D8BA244F.idx differ diff --git a/.cache/clangd/index/gstbufferpool.h.A93DAE49746AF0CD.idx b/.cache/clangd/index/gstbufferpool.h.A93DAE49746AF0CD.idx new file mode 100644 index 0000000..deda68f Binary files /dev/null and b/.cache/clangd/index/gstbufferpool.h.A93DAE49746AF0CD.idx differ diff --git a/.cache/clangd/index/gstbus.h.4847FE7D79B45A4C.idx b/.cache/clangd/index/gstbus.h.4847FE7D79B45A4C.idx new file mode 100644 index 0000000..40631d5 Binary files /dev/null and b/.cache/clangd/index/gstbus.h.4847FE7D79B45A4C.idx differ diff --git a/.cache/clangd/index/gstcaps.h.14E701E704ECB10A.idx b/.cache/clangd/index/gstcaps.h.14E701E704ECB10A.idx new file mode 100644 index 0000000..1c76a02 Binary files /dev/null and b/.cache/clangd/index/gstcaps.h.14E701E704ECB10A.idx differ diff --git a/.cache/clangd/index/gstcapsfeatures.h.2E77B02FF1C0E497.idx b/.cache/clangd/index/gstcapsfeatures.h.2E77B02FF1C0E497.idx new file mode 100644 index 0000000..233ca3c Binary files /dev/null and b/.cache/clangd/index/gstcapsfeatures.h.2E77B02FF1C0E497.idx differ diff --git a/.cache/clangd/index/gstchildproxy.h.02BFC5CCC6462636.idx b/.cache/clangd/index/gstchildproxy.h.02BFC5CCC6462636.idx new file mode 100644 index 0000000..e0b4236 Binary files /dev/null and b/.cache/clangd/index/gstchildproxy.h.02BFC5CCC6462636.idx differ diff --git a/.cache/clangd/index/gstclock.h.9A82CA5242BAB058.idx b/.cache/clangd/index/gstclock.h.9A82CA5242BAB058.idx new file mode 100644 index 0000000..b39eca6 Binary files /dev/null and b/.cache/clangd/index/gstclock.h.9A82CA5242BAB058.idx differ diff --git a/.cache/clangd/index/gstcompat.h.2B636F6235155E6D.idx b/.cache/clangd/index/gstcompat.h.2B636F6235155E6D.idx new file mode 100644 index 0000000..dc5b59b Binary files /dev/null and b/.cache/clangd/index/gstcompat.h.2B636F6235155E6D.idx differ diff --git a/.cache/clangd/index/gstconfig.h.B220405D1DD649EE.idx b/.cache/clangd/index/gstconfig.h.B220405D1DD649EE.idx new file mode 100644 index 0000000..9f4bcc4 Binary files /dev/null and b/.cache/clangd/index/gstconfig.h.B220405D1DD649EE.idx differ diff --git a/.cache/clangd/index/gstcontext.h.3A6645C811EEF3E2.idx b/.cache/clangd/index/gstcontext.h.3A6645C811EEF3E2.idx new file mode 100644 index 0000000..46e46b5 Binary files /dev/null and b/.cache/clangd/index/gstcontext.h.3A6645C811EEF3E2.idx differ diff --git a/.cache/clangd/index/gstcontrolbinding.h.085150912EE7FEB3.idx b/.cache/clangd/index/gstcontrolbinding.h.085150912EE7FEB3.idx new file mode 100644 index 0000000..425a0c5 Binary files /dev/null and b/.cache/clangd/index/gstcontrolbinding.h.085150912EE7FEB3.idx differ diff --git a/.cache/clangd/index/gstcontrolsource.h.4C70CFFA53C8522F.idx b/.cache/clangd/index/gstcontrolsource.h.4C70CFFA53C8522F.idx new file mode 100644 index 0000000..b08915a Binary files /dev/null and b/.cache/clangd/index/gstcontrolsource.h.4C70CFFA53C8522F.idx differ diff --git a/.cache/clangd/index/gstdatetime.h.DE5E284219631592.idx b/.cache/clangd/index/gstdatetime.h.DE5E284219631592.idx new file mode 100644 index 0000000..9ea47ae Binary files /dev/null and b/.cache/clangd/index/gstdatetime.h.DE5E284219631592.idx differ diff --git a/.cache/clangd/index/gstdebugutils.h.78C5C658CBAF231D.idx b/.cache/clangd/index/gstdebugutils.h.78C5C658CBAF231D.idx new file mode 100644 index 0000000..9650d63 Binary files /dev/null and b/.cache/clangd/index/gstdebugutils.h.78C5C658CBAF231D.idx differ diff --git a/.cache/clangd/index/gstdevice.h.9A484F41BC23080B.idx b/.cache/clangd/index/gstdevice.h.9A484F41BC23080B.idx new file mode 100644 index 0000000..fff6ad8 Binary files /dev/null and b/.cache/clangd/index/gstdevice.h.9A484F41BC23080B.idx differ diff --git a/.cache/clangd/index/gstdevicemonitor.h.7BBF07C538FB834F.idx b/.cache/clangd/index/gstdevicemonitor.h.7BBF07C538FB834F.idx new file mode 100644 index 0000000..7712d2d Binary files /dev/null and b/.cache/clangd/index/gstdevicemonitor.h.7BBF07C538FB834F.idx differ diff --git a/.cache/clangd/index/gstdeviceprovider.h.243A1245A3C4380E.idx b/.cache/clangd/index/gstdeviceprovider.h.243A1245A3C4380E.idx new file mode 100644 index 0000000..f5a1aea Binary files /dev/null and b/.cache/clangd/index/gstdeviceprovider.h.243A1245A3C4380E.idx differ diff --git a/.cache/clangd/index/gstdeviceproviderfactory.h.824CAF07306FD6EF.idx b/.cache/clangd/index/gstdeviceproviderfactory.h.824CAF07306FD6EF.idx new file mode 100644 index 0000000..3002d32 Binary files /dev/null and b/.cache/clangd/index/gstdeviceproviderfactory.h.824CAF07306FD6EF.idx differ diff --git a/.cache/clangd/index/gstdynamictypefactory.h.B411F26587A6E757.idx b/.cache/clangd/index/gstdynamictypefactory.h.B411F26587A6E757.idx new file mode 100644 index 0000000..c448056 Binary files /dev/null and b/.cache/clangd/index/gstdynamictypefactory.h.B411F26587A6E757.idx differ diff --git a/.cache/clangd/index/gstelement.h.8828A97F775B9699.idx b/.cache/clangd/index/gstelement.h.8828A97F775B9699.idx new file mode 100644 index 0000000..dab4b29 Binary files /dev/null and b/.cache/clangd/index/gstelement.h.8828A97F775B9699.idx differ diff --git a/.cache/clangd/index/gstelementfactory.h.36C4244C5636DB0D.idx b/.cache/clangd/index/gstelementfactory.h.36C4244C5636DB0D.idx new file mode 100644 index 0000000..641a16b Binary files /dev/null and b/.cache/clangd/index/gstelementfactory.h.36C4244C5636DB0D.idx differ diff --git a/.cache/clangd/index/gstelementmetadata.h.986872FD1A463DC8.idx b/.cache/clangd/index/gstelementmetadata.h.986872FD1A463DC8.idx new file mode 100644 index 0000000..9e423e4 Binary files /dev/null and b/.cache/clangd/index/gstelementmetadata.h.986872FD1A463DC8.idx differ diff --git a/.cache/clangd/index/gstenumtypes.h.B624E0E7228298E5.idx b/.cache/clangd/index/gstenumtypes.h.B624E0E7228298E5.idx new file mode 100644 index 0000000..fac6c8c Binary files /dev/null and b/.cache/clangd/index/gstenumtypes.h.B624E0E7228298E5.idx differ diff --git a/.cache/clangd/index/gsterror.h.E1ECFCC8CC6F495F.idx b/.cache/clangd/index/gsterror.h.E1ECFCC8CC6F495F.idx new file mode 100644 index 0000000..940f077 Binary files /dev/null and b/.cache/clangd/index/gsterror.h.E1ECFCC8CC6F495F.idx differ diff --git a/.cache/clangd/index/gstevent.h.0EBCB28A24A966A8.idx b/.cache/clangd/index/gstevent.h.0EBCB28A24A966A8.idx new file mode 100644 index 0000000..4c371da Binary files /dev/null and b/.cache/clangd/index/gstevent.h.0EBCB28A24A966A8.idx differ diff --git a/.cache/clangd/index/gstformat.h.F5DCD369A79463CB.idx b/.cache/clangd/index/gstformat.h.F5DCD369A79463CB.idx new file mode 100644 index 0000000..7c9048a Binary files /dev/null and b/.cache/clangd/index/gstformat.h.F5DCD369A79463CB.idx differ diff --git a/.cache/clangd/index/gstghostpad.h.8E21F4FF3D9FFC02.idx b/.cache/clangd/index/gstghostpad.h.8E21F4FF3D9FFC02.idx new file mode 100644 index 0000000..23eb302 Binary files /dev/null and b/.cache/clangd/index/gstghostpad.h.8E21F4FF3D9FFC02.idx differ diff --git a/.cache/clangd/index/gstinfo.h.F09C1F1E30368016.idx b/.cache/clangd/index/gstinfo.h.F09C1F1E30368016.idx new file mode 100644 index 0000000..04b9542 Binary files /dev/null and b/.cache/clangd/index/gstinfo.h.F09C1F1E30368016.idx differ diff --git a/.cache/clangd/index/gstiterator.h.DFF9224D960E54B0.idx b/.cache/clangd/index/gstiterator.h.DFF9224D960E54B0.idx new file mode 100644 index 0000000..576b2e6 Binary files /dev/null and b/.cache/clangd/index/gstiterator.h.DFF9224D960E54B0.idx differ diff --git a/.cache/clangd/index/gstmacros.h.7DBF65B555A66C74.idx b/.cache/clangd/index/gstmacros.h.7DBF65B555A66C74.idx new file mode 100644 index 0000000..9b0486e Binary files /dev/null and b/.cache/clangd/index/gstmacros.h.7DBF65B555A66C74.idx differ diff --git a/.cache/clangd/index/gstmemory.h.39F2CEEDD4058099.idx b/.cache/clangd/index/gstmemory.h.39F2CEEDD4058099.idx new file mode 100644 index 0000000..6dfe119 Binary files /dev/null and b/.cache/clangd/index/gstmemory.h.39F2CEEDD4058099.idx differ diff --git a/.cache/clangd/index/gstmessage.h.D895CD4FF4EDC76F.idx b/.cache/clangd/index/gstmessage.h.D895CD4FF4EDC76F.idx new file mode 100644 index 0000000..e059a9e Binary files /dev/null and b/.cache/clangd/index/gstmessage.h.D895CD4FF4EDC76F.idx differ diff --git a/.cache/clangd/index/gstmeta.h.5FD7B26C6622C385.idx b/.cache/clangd/index/gstmeta.h.5FD7B26C6622C385.idx new file mode 100644 index 0000000..188cb4b Binary files /dev/null and b/.cache/clangd/index/gstmeta.h.5FD7B26C6622C385.idx differ diff --git a/.cache/clangd/index/gstminiobject.h.2D7996D420C64DC9.idx b/.cache/clangd/index/gstminiobject.h.2D7996D420C64DC9.idx new file mode 100644 index 0000000..ffbd75a Binary files /dev/null and b/.cache/clangd/index/gstminiobject.h.2D7996D420C64DC9.idx differ diff --git a/.cache/clangd/index/gstobject.h.C3D98D3CC5632134.idx b/.cache/clangd/index/gstobject.h.C3D98D3CC5632134.idx new file mode 100644 index 0000000..0307cd9 Binary files /dev/null and b/.cache/clangd/index/gstobject.h.C3D98D3CC5632134.idx differ diff --git a/.cache/clangd/index/gstpad.h.F833F7AD16329183.idx b/.cache/clangd/index/gstpad.h.F833F7AD16329183.idx new file mode 100644 index 0000000..501be1d Binary files /dev/null and b/.cache/clangd/index/gstpad.h.F833F7AD16329183.idx differ diff --git a/.cache/clangd/index/gstpadtemplate.h.C6A7B3CEA2467631.idx b/.cache/clangd/index/gstpadtemplate.h.C6A7B3CEA2467631.idx new file mode 100644 index 0000000..c6c9e2e Binary files /dev/null and b/.cache/clangd/index/gstpadtemplate.h.C6A7B3CEA2467631.idx differ diff --git a/.cache/clangd/index/gstparamspecs.h.54272429E32CA4BF.idx b/.cache/clangd/index/gstparamspecs.h.54272429E32CA4BF.idx new file mode 100644 index 0000000..b52fefa Binary files /dev/null and b/.cache/clangd/index/gstparamspecs.h.54272429E32CA4BF.idx differ diff --git a/.cache/clangd/index/gstparse.h.3EE69D0958A1F453.idx b/.cache/clangd/index/gstparse.h.3EE69D0958A1F453.idx new file mode 100644 index 0000000..fbba451 Binary files /dev/null and b/.cache/clangd/index/gstparse.h.3EE69D0958A1F453.idx differ diff --git a/.cache/clangd/index/gstpipeline.h.0514F593E44C97E3.idx b/.cache/clangd/index/gstpipeline.h.0514F593E44C97E3.idx new file mode 100644 index 0000000..83ce777 Binary files /dev/null and b/.cache/clangd/index/gstpipeline.h.0514F593E44C97E3.idx differ diff --git a/.cache/clangd/index/gstplugin.h.BE1E7594B44817F5.idx b/.cache/clangd/index/gstplugin.h.BE1E7594B44817F5.idx new file mode 100644 index 0000000..225a9f0 Binary files /dev/null and b/.cache/clangd/index/gstplugin.h.BE1E7594B44817F5.idx differ diff --git a/.cache/clangd/index/gstpluginfeature.h.1B651DE36A369379.idx b/.cache/clangd/index/gstpluginfeature.h.1B651DE36A369379.idx new file mode 100644 index 0000000..7a069f4 Binary files /dev/null and b/.cache/clangd/index/gstpluginfeature.h.1B651DE36A369379.idx differ diff --git a/.cache/clangd/index/gstpoll.h.73B5A8B5081F0996.idx b/.cache/clangd/index/gstpoll.h.73B5A8B5081F0996.idx new file mode 100644 index 0000000..214df2c Binary files /dev/null and b/.cache/clangd/index/gstpoll.h.73B5A8B5081F0996.idx differ diff --git a/.cache/clangd/index/gstpreset.h.19B513CD0F707F75.idx b/.cache/clangd/index/gstpreset.h.19B513CD0F707F75.idx new file mode 100644 index 0000000..f17411a Binary files /dev/null and b/.cache/clangd/index/gstpreset.h.19B513CD0F707F75.idx differ diff --git a/.cache/clangd/index/gstpromise.h.B8B2121572B3DB3D.idx b/.cache/clangd/index/gstpromise.h.B8B2121572B3DB3D.idx new file mode 100644 index 0000000..023e038 Binary files /dev/null and b/.cache/clangd/index/gstpromise.h.B8B2121572B3DB3D.idx differ diff --git a/.cache/clangd/index/gstprotection.h.752D414476ED9A34.idx b/.cache/clangd/index/gstprotection.h.752D414476ED9A34.idx new file mode 100644 index 0000000..8902fbc Binary files /dev/null and b/.cache/clangd/index/gstprotection.h.752D414476ED9A34.idx differ diff --git a/.cache/clangd/index/gstpushsrc.h.1C314D3C4D6725FD.idx b/.cache/clangd/index/gstpushsrc.h.1C314D3C4D6725FD.idx new file mode 100644 index 0000000..19c25af Binary files /dev/null and b/.cache/clangd/index/gstpushsrc.h.1C314D3C4D6725FD.idx differ diff --git a/.cache/clangd/index/gstquery.h.0F2750186B3600BB.idx b/.cache/clangd/index/gstquery.h.0F2750186B3600BB.idx new file mode 100644 index 0000000..67fbad1 Binary files /dev/null and b/.cache/clangd/index/gstquery.h.0F2750186B3600BB.idx differ diff --git a/.cache/clangd/index/gstregistry.h.3AC1A652B2DF4F67.idx b/.cache/clangd/index/gstregistry.h.3AC1A652B2DF4F67.idx new file mode 100644 index 0000000..11d23dc Binary files /dev/null and b/.cache/clangd/index/gstregistry.h.3AC1A652B2DF4F67.idx differ diff --git a/.cache/clangd/index/gstrfuncs.h.3F5919DE19CBD85F.idx b/.cache/clangd/index/gstrfuncs.h.3F5919DE19CBD85F.idx new file mode 100644 index 0000000..5a379e9 Binary files /dev/null and b/.cache/clangd/index/gstrfuncs.h.3F5919DE19CBD85F.idx differ diff --git a/.cache/clangd/index/gstring.h.5517DD5E0101828E.idx b/.cache/clangd/index/gstring.h.5517DD5E0101828E.idx new file mode 100644 index 0000000..c051e74 Binary files /dev/null and b/.cache/clangd/index/gstring.h.5517DD5E0101828E.idx differ diff --git a/.cache/clangd/index/gstringchunk.h.AE41B81B48BE56D6.idx b/.cache/clangd/index/gstringchunk.h.AE41B81B48BE56D6.idx new file mode 100644 index 0000000..f65ab41 Binary files /dev/null and b/.cache/clangd/index/gstringchunk.h.AE41B81B48BE56D6.idx differ diff --git a/.cache/clangd/index/gstsample.h.A0C2CE1E00A06CC7.idx b/.cache/clangd/index/gstsample.h.A0C2CE1E00A06CC7.idx new file mode 100644 index 0000000..db59434 Binary files /dev/null and b/.cache/clangd/index/gstsample.h.A0C2CE1E00A06CC7.idx differ diff --git a/.cache/clangd/index/gstsegment.h.422747C6309ECFBD.idx b/.cache/clangd/index/gstsegment.h.422747C6309ECFBD.idx new file mode 100644 index 0000000..671febf Binary files /dev/null and b/.cache/clangd/index/gstsegment.h.422747C6309ECFBD.idx differ diff --git a/.cache/clangd/index/gststreamcollection.h.464BDA00B706949A.idx b/.cache/clangd/index/gststreamcollection.h.464BDA00B706949A.idx new file mode 100644 index 0000000..5c2367e Binary files /dev/null and b/.cache/clangd/index/gststreamcollection.h.464BDA00B706949A.idx differ diff --git a/.cache/clangd/index/gststreams.h.AE23173EC86005BE.idx b/.cache/clangd/index/gststreams.h.AE23173EC86005BE.idx new file mode 100644 index 0000000..9009527 Binary files /dev/null and b/.cache/clangd/index/gststreams.h.AE23173EC86005BE.idx differ diff --git a/.cache/clangd/index/gststructure.h.3EDF37FBF99A7972.idx b/.cache/clangd/index/gststructure.h.3EDF37FBF99A7972.idx new file mode 100644 index 0000000..98a92ad Binary files /dev/null and b/.cache/clangd/index/gststructure.h.3EDF37FBF99A7972.idx differ diff --git a/.cache/clangd/index/gstsystemclock.h.EF9CD75CEBB78552.idx b/.cache/clangd/index/gstsystemclock.h.EF9CD75CEBB78552.idx new file mode 100644 index 0000000..5819f97 Binary files /dev/null and b/.cache/clangd/index/gstsystemclock.h.EF9CD75CEBB78552.idx differ diff --git a/.cache/clangd/index/gsttaglist.h.348AAF486166548E.idx b/.cache/clangd/index/gsttaglist.h.348AAF486166548E.idx new file mode 100644 index 0000000..5b48990 Binary files /dev/null and b/.cache/clangd/index/gsttaglist.h.348AAF486166548E.idx differ diff --git a/.cache/clangd/index/gsttagsetter.h.931ECC772FEB5370.idx b/.cache/clangd/index/gsttagsetter.h.931ECC772FEB5370.idx new file mode 100644 index 0000000..c23eb81 Binary files /dev/null and b/.cache/clangd/index/gsttagsetter.h.931ECC772FEB5370.idx differ diff --git a/.cache/clangd/index/gsttask.h.651406E7F9128152.idx b/.cache/clangd/index/gsttask.h.651406E7F9128152.idx new file mode 100644 index 0000000..c68c927 Binary files /dev/null and b/.cache/clangd/index/gsttask.h.651406E7F9128152.idx differ diff --git a/.cache/clangd/index/gsttaskpool.h.1255DF1747FC8C43.idx b/.cache/clangd/index/gsttaskpool.h.1255DF1747FC8C43.idx new file mode 100644 index 0000000..cc33b9d Binary files /dev/null and b/.cache/clangd/index/gsttaskpool.h.1255DF1747FC8C43.idx differ diff --git a/.cache/clangd/index/gsttoc.h.66EB2B97737FC45F.idx b/.cache/clangd/index/gsttoc.h.66EB2B97737FC45F.idx new file mode 100644 index 0000000..0314da4 Binary files /dev/null and b/.cache/clangd/index/gsttoc.h.66EB2B97737FC45F.idx differ diff --git a/.cache/clangd/index/gsttocsetter.h.3348A1B19E7F3E9B.idx b/.cache/clangd/index/gsttocsetter.h.3348A1B19E7F3E9B.idx new file mode 100644 index 0000000..0385ea3 Binary files /dev/null and b/.cache/clangd/index/gsttocsetter.h.3348A1B19E7F3E9B.idx differ diff --git a/.cache/clangd/index/gsttracer.h.A7862082791D3D55.idx b/.cache/clangd/index/gsttracer.h.A7862082791D3D55.idx new file mode 100644 index 0000000..89ce9d2 Binary files /dev/null and b/.cache/clangd/index/gsttracer.h.A7862082791D3D55.idx differ diff --git a/.cache/clangd/index/gsttracerfactory.h.212D6B1DF31FFEDC.idx b/.cache/clangd/index/gsttracerfactory.h.212D6B1DF31FFEDC.idx new file mode 100644 index 0000000..c8f7e44 Binary files /dev/null and b/.cache/clangd/index/gsttracerfactory.h.212D6B1DF31FFEDC.idx differ diff --git a/.cache/clangd/index/gsttracerrecord.h.CF2B950B2FB8A79E.idx b/.cache/clangd/index/gsttracerrecord.h.CF2B950B2FB8A79E.idx new file mode 100644 index 0000000..1393f6b Binary files /dev/null and b/.cache/clangd/index/gsttracerrecord.h.CF2B950B2FB8A79E.idx differ diff --git a/.cache/clangd/index/gsttypefind.h.B2ABE626CA545B4C.idx b/.cache/clangd/index/gsttypefind.h.B2ABE626CA545B4C.idx new file mode 100644 index 0000000..d90a449 Binary files /dev/null and b/.cache/clangd/index/gsttypefind.h.B2ABE626CA545B4C.idx differ diff --git a/.cache/clangd/index/gsttypefindfactory.h.60067C6288386921.idx b/.cache/clangd/index/gsttypefindfactory.h.60067C6288386921.idx new file mode 100644 index 0000000..b827310 Binary files /dev/null and b/.cache/clangd/index/gsttypefindfactory.h.60067C6288386921.idx differ diff --git a/.cache/clangd/index/gsturi.h.76EE88F90A1F7B6F.idx b/.cache/clangd/index/gsturi.h.76EE88F90A1F7B6F.idx new file mode 100644 index 0000000..9536da0 Binary files /dev/null and b/.cache/clangd/index/gsturi.h.76EE88F90A1F7B6F.idx differ diff --git a/.cache/clangd/index/gstutils.h.95A250F64B682D7F.idx b/.cache/clangd/index/gstutils.h.95A250F64B682D7F.idx new file mode 100644 index 0000000..7c15a32 Binary files /dev/null and b/.cache/clangd/index/gstutils.h.95A250F64B682D7F.idx differ diff --git a/.cache/clangd/index/gstvalue.h.3C4CEA23A9951DD4.idx b/.cache/clangd/index/gstvalue.h.3C4CEA23A9951DD4.idx new file mode 100644 index 0000000..0289981 Binary files /dev/null and b/.cache/clangd/index/gstvalue.h.3C4CEA23A9951DD4.idx differ diff --git a/.cache/clangd/index/gstversion.h.282324436D2211F7.idx b/.cache/clangd/index/gstversion.h.282324436D2211F7.idx new file mode 100644 index 0000000..cda0456 Binary files /dev/null and b/.cache/clangd/index/gstversion.h.282324436D2211F7.idx differ diff --git a/.cache/clangd/index/gtestutils.h.9DEF786DD86BD12F.idx b/.cache/clangd/index/gtestutils.h.9DEF786DD86BD12F.idx new file mode 100644 index 0000000..82d33ae Binary files /dev/null and b/.cache/clangd/index/gtestutils.h.9DEF786DD86BD12F.idx differ diff --git a/.cache/clangd/index/gthr-default.h.DAFD1652A53947C9.idx b/.cache/clangd/index/gthr-default.h.DAFD1652A53947C9.idx new file mode 100644 index 0000000..c4797cc Binary files /dev/null and b/.cache/clangd/index/gthr-default.h.DAFD1652A53947C9.idx differ diff --git a/.cache/clangd/index/gthr.h.987DFB27620C6279.idx b/.cache/clangd/index/gthr.h.987DFB27620C6279.idx new file mode 100644 index 0000000..2ba063a Binary files /dev/null and b/.cache/clangd/index/gthr.h.987DFB27620C6279.idx differ diff --git a/.cache/clangd/index/gthread.h.4BBFAF740C27AA2E.idx b/.cache/clangd/index/gthread.h.4BBFAF740C27AA2E.idx new file mode 100644 index 0000000..d24dfbc Binary files /dev/null and b/.cache/clangd/index/gthread.h.4BBFAF740C27AA2E.idx differ diff --git a/.cache/clangd/index/gthread.h.67F1A07D2A9BA845.idx b/.cache/clangd/index/gthread.h.67F1A07D2A9BA845.idx new file mode 100644 index 0000000..73b6bdc Binary files /dev/null and b/.cache/clangd/index/gthread.h.67F1A07D2A9BA845.idx differ diff --git a/.cache/clangd/index/gthreadpool.h.2B5FB894D57F6E29.idx b/.cache/clangd/index/gthreadpool.h.2B5FB894D57F6E29.idx new file mode 100644 index 0000000..f158a99 Binary files /dev/null and b/.cache/clangd/index/gthreadpool.h.2B5FB894D57F6E29.idx differ diff --git a/.cache/clangd/index/gtimer.h.E5186239BDF7803E.idx b/.cache/clangd/index/gtimer.h.E5186239BDF7803E.idx new file mode 100644 index 0000000..ede0e3e Binary files /dev/null and b/.cache/clangd/index/gtimer.h.E5186239BDF7803E.idx differ diff --git a/.cache/clangd/index/gtimezone.h.7C409D05DC8D6A7F.idx b/.cache/clangd/index/gtimezone.h.7C409D05DC8D6A7F.idx new file mode 100644 index 0000000..a89b3b8 Binary files /dev/null and b/.cache/clangd/index/gtimezone.h.7C409D05DC8D6A7F.idx differ diff --git a/.cache/clangd/index/gtrashstack.h.908B09842E22036F.idx b/.cache/clangd/index/gtrashstack.h.908B09842E22036F.idx new file mode 100644 index 0000000..3b0876a Binary files /dev/null and b/.cache/clangd/index/gtrashstack.h.908B09842E22036F.idx differ diff --git a/.cache/clangd/index/gtree.h.44A47E622CBC7692.idx b/.cache/clangd/index/gtree.h.44A47E622CBC7692.idx new file mode 100644 index 0000000..cef1f47 Binary files /dev/null and b/.cache/clangd/index/gtree.h.44A47E622CBC7692.idx differ diff --git a/.cache/clangd/index/gtype.h.39B0EA4CD3E36253.idx b/.cache/clangd/index/gtype.h.39B0EA4CD3E36253.idx new file mode 100644 index 0000000..de8d343 Binary files /dev/null and b/.cache/clangd/index/gtype.h.39B0EA4CD3E36253.idx differ diff --git a/.cache/clangd/index/gtypemodule.h.26CE3010EEACFD49.idx b/.cache/clangd/index/gtypemodule.h.26CE3010EEACFD49.idx new file mode 100644 index 0000000..4c740bc Binary files /dev/null and b/.cache/clangd/index/gtypemodule.h.26CE3010EEACFD49.idx differ diff --git a/.cache/clangd/index/gtypeplugin.h.D07CCFEC93364078.idx b/.cache/clangd/index/gtypeplugin.h.D07CCFEC93364078.idx new file mode 100644 index 0000000..1ca51eb Binary files /dev/null and b/.cache/clangd/index/gtypeplugin.h.D07CCFEC93364078.idx differ diff --git a/.cache/clangd/index/gtypes.h.01D38CEBA345F420.idx b/.cache/clangd/index/gtypes.h.01D38CEBA345F420.idx new file mode 100644 index 0000000..5d18cfc Binary files /dev/null and b/.cache/clangd/index/gtypes.h.01D38CEBA345F420.idx differ diff --git a/.cache/clangd/index/gunicode.h.AC226D7266191B1D.idx b/.cache/clangd/index/gunicode.h.AC226D7266191B1D.idx new file mode 100644 index 0000000..d18f4ec Binary files /dev/null and b/.cache/clangd/index/gunicode.h.AC226D7266191B1D.idx differ diff --git a/.cache/clangd/index/gurifuncs.h.20FC197B85ECEB7C.idx b/.cache/clangd/index/gurifuncs.h.20FC197B85ECEB7C.idx new file mode 100644 index 0000000..ced7897 Binary files /dev/null and b/.cache/clangd/index/gurifuncs.h.20FC197B85ECEB7C.idx differ diff --git a/.cache/clangd/index/gutils.h.E0295B1D4F251629.idx b/.cache/clangd/index/gutils.h.E0295B1D4F251629.idx new file mode 100644 index 0000000..e30f16b Binary files /dev/null and b/.cache/clangd/index/gutils.h.E0295B1D4F251629.idx differ diff --git a/.cache/clangd/index/guuid.h.EB0D5DB39E454D38.idx b/.cache/clangd/index/guuid.h.EB0D5DB39E454D38.idx new file mode 100644 index 0000000..b78cddd Binary files /dev/null and b/.cache/clangd/index/guuid.h.EB0D5DB39E454D38.idx differ diff --git a/.cache/clangd/index/gvalue.h.1A3B3896FAA9877F.idx b/.cache/clangd/index/gvalue.h.1A3B3896FAA9877F.idx new file mode 100644 index 0000000..061bd61 Binary files /dev/null and b/.cache/clangd/index/gvalue.h.1A3B3896FAA9877F.idx differ diff --git a/.cache/clangd/index/gvaluearray.h.3D6A8809707885CE.idx b/.cache/clangd/index/gvaluearray.h.3D6A8809707885CE.idx new file mode 100644 index 0000000..1e227e8 Binary files /dev/null and b/.cache/clangd/index/gvaluearray.h.3D6A8809707885CE.idx differ diff --git a/.cache/clangd/index/gvaluetypes.h.50FD3D6D7F628069.idx b/.cache/clangd/index/gvaluetypes.h.50FD3D6D7F628069.idx new file mode 100644 index 0000000..b159b39 Binary files /dev/null and b/.cache/clangd/index/gvaluetypes.h.50FD3D6D7F628069.idx differ diff --git a/.cache/clangd/index/gvariant.h.4F62DE0D9CF802FC.idx b/.cache/clangd/index/gvariant.h.4F62DE0D9CF802FC.idx new file mode 100644 index 0000000..f6708fb Binary files /dev/null and b/.cache/clangd/index/gvariant.h.4F62DE0D9CF802FC.idx differ diff --git a/.cache/clangd/index/gvarianttype.h.F36CF67C6FF8C256.idx b/.cache/clangd/index/gvarianttype.h.F36CF67C6FF8C256.idx new file mode 100644 index 0000000..0578af8 Binary files /dev/null and b/.cache/clangd/index/gvarianttype.h.F36CF67C6FF8C256.idx differ diff --git a/.cache/clangd/index/gversion.h.EC0AA5263D436D3D.idx b/.cache/clangd/index/gversion.h.EC0AA5263D436D3D.idx new file mode 100644 index 0000000..51a6bf7 Binary files /dev/null and b/.cache/clangd/index/gversion.h.EC0AA5263D436D3D.idx differ diff --git a/.cache/clangd/index/gversionmacros.h.CE1E6FF512849547.idx b/.cache/clangd/index/gversionmacros.h.CE1E6FF512849547.idx new file mode 100644 index 0000000..ba2ba64 Binary files /dev/null and b/.cache/clangd/index/gversionmacros.h.CE1E6FF512849547.idx differ diff --git a/.cache/clangd/index/hand_garble.c.291FED9E4E45CD90.idx b/.cache/clangd/index/hand_garble.c.291FED9E4E45CD90.idx new file mode 100644 index 0000000..36e031f Binary files /dev/null and b/.cache/clangd/index/hand_garble.c.291FED9E4E45CD90.idx differ diff --git a/.cache/clangd/index/hash_bytes.h.BA97C11D6623237D.idx b/.cache/clangd/index/hash_bytes.h.BA97C11D6623237D.idx new file mode 100644 index 0000000..d217f5a Binary files /dev/null and b/.cache/clangd/index/hash_bytes.h.BA97C11D6623237D.idx differ diff --git a/.cache/clangd/index/http.c.086E80DFA1725405.idx b/.cache/clangd/index/http.c.086E80DFA1725405.idx new file mode 100644 index 0000000..5c4329b Binary files /dev/null and b/.cache/clangd/index/http.c.086E80DFA1725405.idx differ diff --git a/.cache/clangd/index/http_handlers.h.7352DE4F22D9B773.idx b/.cache/clangd/index/http_handlers.h.7352DE4F22D9B773.idx new file mode 100644 index 0000000..1d59cc4 Binary files /dev/null and b/.cache/clangd/index/http_handlers.h.7352DE4F22D9B773.idx differ diff --git a/.cache/clangd/index/http_request.c.DB8A424BBA2CAC23.idx b/.cache/clangd/index/http_request.c.DB8A424BBA2CAC23.idx new file mode 100644 index 0000000..563aed6 Binary files /dev/null and b/.cache/clangd/index/http_request.c.DB8A424BBA2CAC23.idx differ diff --git a/.cache/clangd/index/http_request.h.BFDF2EB74D33C8A6.idx b/.cache/clangd/index/http_request.h.BFDF2EB74D33C8A6.idx new file mode 100644 index 0000000..07d7abd Binary files /dev/null and b/.cache/clangd/index/http_request.h.BFDF2EB74D33C8A6.idx differ diff --git a/.cache/clangd/index/http_response.c.C5F63CC1BAB7AD7A.idx b/.cache/clangd/index/http_response.c.C5F63CC1BAB7AD7A.idx new file mode 100644 index 0000000..2afc200 Binary files /dev/null and b/.cache/clangd/index/http_response.c.C5F63CC1BAB7AD7A.idx differ diff --git a/.cache/clangd/index/http_response.h.BA11BF69B07EED5D.idx b/.cache/clangd/index/http_response.h.BA11BF69B07EED5D.idx new file mode 100644 index 0000000..ed8b02a Binary files /dev/null and b/.cache/clangd/index/http_response.h.BA11BF69B07EED5D.idx differ diff --git a/.cache/clangd/index/httpd.c.451E40AA0EC4A966.idx b/.cache/clangd/index/httpd.c.451E40AA0EC4A966.idx new file mode 100644 index 0000000..96fe653 Binary files /dev/null and b/.cache/clangd/index/httpd.c.451E40AA0EC4A966.idx differ diff --git a/.cache/clangd/index/httpd.h.35DAA7F060C750E6.idx b/.cache/clangd/index/httpd.h.35DAA7F060C750E6.idx new file mode 100644 index 0000000..906400c Binary files /dev/null and b/.cache/clangd/index/httpd.h.35DAA7F060C750E6.idx differ diff --git a/.cache/clangd/index/ifaddrs.h.974368CC37155438.idx b/.cache/clangd/index/ifaddrs.h.974368CC37155438.idx new file mode 100644 index 0000000..543d73d Binary files /dev/null and b/.cache/clangd/index/ifaddrs.h.974368CC37155438.idx differ diff --git a/.cache/clangd/index/in.h.DD8EEBCAE3CEB61A.idx b/.cache/clangd/index/in.h.DD8EEBCAE3CEB61A.idx new file mode 100644 index 0000000..b0b92c4 Binary files /dev/null and b/.cache/clangd/index/in.h.DD8EEBCAE3CEB61A.idx differ diff --git a/.cache/clangd/index/in.h.ECEBD552F5E1F102.idx b/.cache/clangd/index/in.h.ECEBD552F5E1F102.idx new file mode 100644 index 0000000..e4f7585 Binary files /dev/null and b/.cache/clangd/index/in.h.ECEBD552F5E1F102.idx differ diff --git a/.cache/clangd/index/inet.h.E8FF95FEFD8277CB.idx b/.cache/clangd/index/inet.h.E8FF95FEFD8277CB.idx new file mode 100644 index 0000000..33b2211 Binary files /dev/null and b/.cache/clangd/index/inet.h.E8FF95FEFD8277CB.idx differ diff --git a/.cache/clangd/index/initializer_list.6F5E404CA102F6C4.idx b/.cache/clangd/index/initializer_list.6F5E404CA102F6C4.idx new file mode 100644 index 0000000..f1f6c11 Binary files /dev/null and b/.cache/clangd/index/initializer_list.6F5E404CA102F6C4.idx differ diff --git a/.cache/clangd/index/int-ll64.h.AE74C618A4956C59.idx b/.cache/clangd/index/int-ll64.h.AE74C618A4956C59.idx new file mode 100644 index 0000000..04b33c8 Binary files /dev/null and b/.cache/clangd/index/int-ll64.h.AE74C618A4956C59.idx differ diff --git a/.cache/clangd/index/inttypes.h.495A386E5CA5247A.idx b/.cache/clangd/index/inttypes.h.495A386E5CA5247A.idx new file mode 100644 index 0000000..8f74858 Binary files /dev/null and b/.cache/clangd/index/inttypes.h.495A386E5CA5247A.idx differ diff --git a/.cache/clangd/index/inttypes.h.CA4EF3825D5F845B.idx b/.cache/clangd/index/inttypes.h.CA4EF3825D5F845B.idx new file mode 100644 index 0000000..a4a7f05 Binary files /dev/null and b/.cache/clangd/index/inttypes.h.CA4EF3825D5F845B.idx differ diff --git a/.cache/clangd/index/ioctl-types.h.04B0132B5C5C7C09.idx b/.cache/clangd/index/ioctl-types.h.04B0132B5C5C7C09.idx new file mode 100644 index 0000000..4b5f89c Binary files /dev/null and b/.cache/clangd/index/ioctl-types.h.04B0132B5C5C7C09.idx differ diff --git a/.cache/clangd/index/ioctl.h.018EAD0DE54125FA.idx b/.cache/clangd/index/ioctl.h.018EAD0DE54125FA.idx new file mode 100644 index 0000000..13e9fc4 Binary files /dev/null and b/.cache/clangd/index/ioctl.h.018EAD0DE54125FA.idx differ diff --git a/.cache/clangd/index/ioctl.h.0B7115D7DA31CBD3.idx b/.cache/clangd/index/ioctl.h.0B7115D7DA31CBD3.idx new file mode 100644 index 0000000..8aad3c9 Binary files /dev/null and b/.cache/clangd/index/ioctl.h.0B7115D7DA31CBD3.idx differ diff --git a/.cache/clangd/index/ioctl.h.7D5EF3AA1DA6F7B9.idx b/.cache/clangd/index/ioctl.h.7D5EF3AA1DA6F7B9.idx new file mode 100644 index 0000000..d6df147 Binary files /dev/null and b/.cache/clangd/index/ioctl.h.7D5EF3AA1DA6F7B9.idx differ diff --git a/.cache/clangd/index/ioctl.h.D0A6527909EC3849.idx b/.cache/clangd/index/ioctl.h.D0A6527909EC3849.idx new file mode 100644 index 0000000..89ce56d Binary files /dev/null and b/.cache/clangd/index/ioctl.h.D0A6527909EC3849.idx differ diff --git a/.cache/clangd/index/ioctls.h.00D5BC043D99E35F.idx b/.cache/clangd/index/ioctls.h.00D5BC043D99E35F.idx new file mode 100644 index 0000000..1c14732 Binary files /dev/null and b/.cache/clangd/index/ioctls.h.00D5BC043D99E35F.idx differ diff --git a/.cache/clangd/index/ioctls.h.76A8BB579AD89BE6.idx b/.cache/clangd/index/ioctls.h.76A8BB579AD89BE6.idx new file mode 100644 index 0000000..8fe9679 Binary files /dev/null and b/.cache/clangd/index/ioctls.h.76A8BB579AD89BE6.idx differ diff --git a/.cache/clangd/index/ioctls.h.BBC711A752724C88.idx b/.cache/clangd/index/ioctls.h.BBC711A752724C88.idx new file mode 100644 index 0000000..7a29e57 Binary files /dev/null and b/.cache/clangd/index/ioctls.h.BBC711A752724C88.idx differ diff --git a/.cache/clangd/index/ios.A257BEDA86460D27.idx b/.cache/clangd/index/ios.A257BEDA86460D27.idx new file mode 100644 index 0000000..5fddd4a Binary files /dev/null and b/.cache/clangd/index/ios.A257BEDA86460D27.idx differ diff --git a/.cache/clangd/index/ios_base.h.44DA5C97E4905A0D.idx b/.cache/clangd/index/ios_base.h.44DA5C97E4905A0D.idx new file mode 100644 index 0000000..a20fb8e Binary files /dev/null and b/.cache/clangd/index/ios_base.h.44DA5C97E4905A0D.idx differ diff --git a/.cache/clangd/index/iosfwd.CB5CD6E794C74A74.idx b/.cache/clangd/index/iosfwd.CB5CD6E794C74A74.idx new file mode 100644 index 0000000..3f14d8f Binary files /dev/null and b/.cache/clangd/index/iosfwd.CB5CD6E794C74A74.idx differ diff --git a/.cache/clangd/index/iscanonical.h.C53E44E13856FCEA.idx b/.cache/clangd/index/iscanonical.h.C53E44E13856FCEA.idx new file mode 100644 index 0000000..f8e22be Binary files /dev/null and b/.cache/clangd/index/iscanonical.h.C53E44E13856FCEA.idx differ diff --git a/.cache/clangd/index/istream.B26D668688F52904.idx b/.cache/clangd/index/istream.B26D668688F52904.idx new file mode 100644 index 0000000..7eb9222 Binary files /dev/null and b/.cache/clangd/index/istream.B26D668688F52904.idx differ diff --git a/.cache/clangd/index/istream.tcc.8D24E498BA6A8D5D.idx b/.cache/clangd/index/istream.tcc.8D24E498BA6A8D5D.idx new file mode 100644 index 0000000..41f3426 Binary files /dev/null and b/.cache/clangd/index/istream.tcc.8D24E498BA6A8D5D.idx differ diff --git a/.cache/clangd/index/iterator.4CFADAA2793E40A6.idx b/.cache/clangd/index/iterator.4CFADAA2793E40A6.idx new file mode 100644 index 0000000..5f1a132 Binary files /dev/null and b/.cache/clangd/index/iterator.4CFADAA2793E40A6.idx differ diff --git a/.cache/clangd/index/iterator_concepts.h.A4E023A56C936E5B.idx b/.cache/clangd/index/iterator_concepts.h.A4E023A56C936E5B.idx new file mode 100644 index 0000000..8dea84f Binary files /dev/null and b/.cache/clangd/index/iterator_concepts.h.A4E023A56C936E5B.idx differ diff --git a/.cache/clangd/index/lhash.h.8D689AF264B9BF93.idx b/.cache/clangd/index/lhash.h.8D689AF264B9BF93.idx new file mode 100644 index 0000000..b197530 Binary files /dev/null and b/.cache/clangd/index/lhash.h.8D689AF264B9BF93.idx differ diff --git a/.cache/clangd/index/libc-header-start.h.E88219B53B0B0BF5.idx b/.cache/clangd/index/libc-header-start.h.E88219B53B0B0BF5.idx new file mode 100644 index 0000000..2a1b518 Binary files /dev/null and b/.cache/clangd/index/libc-header-start.h.E88219B53B0B0BF5.idx differ diff --git a/.cache/clangd/index/libm-simd-decl-stubs.h.06B70A57602EA3C8.idx b/.cache/clangd/index/libm-simd-decl-stubs.h.06B70A57602EA3C8.idx new file mode 100644 index 0000000..8d3d7fd Binary files /dev/null and b/.cache/clangd/index/libm-simd-decl-stubs.h.06B70A57602EA3C8.idx differ diff --git a/.cache/clangd/index/limits.12ED17C307A7ABD1.idx b/.cache/clangd/index/limits.12ED17C307A7ABD1.idx new file mode 100644 index 0000000..9b6b669 Binary files /dev/null and b/.cache/clangd/index/limits.12ED17C307A7ABD1.idx differ diff --git a/.cache/clangd/index/limits.h.1DFE7D0FE6434B13.idx b/.cache/clangd/index/limits.h.1DFE7D0FE6434B13.idx new file mode 100644 index 0000000..167da57 Binary files /dev/null and b/.cache/clangd/index/limits.h.1DFE7D0FE6434B13.idx differ diff --git a/.cache/clangd/index/limits.h.1E7D7771EC751FFB.idx b/.cache/clangd/index/limits.h.1E7D7771EC751FFB.idx new file mode 100644 index 0000000..9c2538f Binary files /dev/null and b/.cache/clangd/index/limits.h.1E7D7771EC751FFB.idx differ diff --git a/.cache/clangd/index/limits.h.C293B8934AC926BB.idx b/.cache/clangd/index/limits.h.C293B8934AC926BB.idx new file mode 100644 index 0000000..2839b1a Binary files /dev/null and b/.cache/clangd/index/limits.h.C293B8934AC926BB.idx differ diff --git a/.cache/clangd/index/llhttp.c.19749A7070AC9017.idx b/.cache/clangd/index/llhttp.c.19749A7070AC9017.idx new file mode 100644 index 0000000..5b77843 Binary files /dev/null and b/.cache/clangd/index/llhttp.c.19749A7070AC9017.idx differ diff --git a/.cache/clangd/index/llhttp.h.3C7AC85810E658A2.idx b/.cache/clangd/index/llhttp.h.3C7AC85810E658A2.idx new file mode 100644 index 0000000..76cb444 Binary files /dev/null and b/.cache/clangd/index/llhttp.h.3C7AC85810E658A2.idx differ diff --git a/.cache/clangd/index/local_lim.h.2CFD657F256B117E.idx b/.cache/clangd/index/local_lim.h.2CFD657F256B117E.idx new file mode 100644 index 0000000..d68e2be Binary files /dev/null and b/.cache/clangd/index/local_lim.h.2CFD657F256B117E.idx differ diff --git a/.cache/clangd/index/locale.h.E3C63D05A4E78516.idx b/.cache/clangd/index/locale.h.E3C63D05A4E78516.idx new file mode 100644 index 0000000..4882ada Binary files /dev/null and b/.cache/clangd/index/locale.h.E3C63D05A4E78516.idx differ diff --git a/.cache/clangd/index/locale.h.F1B51844858762D2.idx b/.cache/clangd/index/locale.h.F1B51844858762D2.idx new file mode 100644 index 0000000..f0bd5aa Binary files /dev/null and b/.cache/clangd/index/locale.h.F1B51844858762D2.idx differ diff --git a/.cache/clangd/index/locale_classes.h.25AC23AAAB9ED394.idx b/.cache/clangd/index/locale_classes.h.25AC23AAAB9ED394.idx new file mode 100644 index 0000000..676ed49 Binary files /dev/null and b/.cache/clangd/index/locale_classes.h.25AC23AAAB9ED394.idx differ diff --git a/.cache/clangd/index/locale_classes.tcc.F8AC5FFE828DBECF.idx b/.cache/clangd/index/locale_classes.tcc.F8AC5FFE828DBECF.idx new file mode 100644 index 0000000..400b40a Binary files /dev/null and b/.cache/clangd/index/locale_classes.tcc.F8AC5FFE828DBECF.idx differ diff --git a/.cache/clangd/index/locale_facets.h.A1D85E601565D744.idx b/.cache/clangd/index/locale_facets.h.A1D85E601565D744.idx new file mode 100644 index 0000000..4088c28 Binary files /dev/null and b/.cache/clangd/index/locale_facets.h.A1D85E601565D744.idx differ diff --git a/.cache/clangd/index/locale_facets.tcc.1E81899BCA4D0DA5.idx b/.cache/clangd/index/locale_facets.tcc.1E81899BCA4D0DA5.idx new file mode 100644 index 0000000..f435dd3 Binary files /dev/null and b/.cache/clangd/index/locale_facets.tcc.1E81899BCA4D0DA5.idx differ diff --git a/.cache/clangd/index/locale_t.h.557516A6B361170F.idx b/.cache/clangd/index/locale_t.h.557516A6B361170F.idx new file mode 100644 index 0000000..9287cad Binary files /dev/null and b/.cache/clangd/index/locale_t.h.557516A6B361170F.idx differ diff --git a/.cache/clangd/index/localefwd.h.679701229356324D.idx b/.cache/clangd/index/localefwd.h.679701229356324D.idx new file mode 100644 index 0000000..1108885 Binary files /dev/null and b/.cache/clangd/index/localefwd.h.679701229356324D.idx differ diff --git a/.cache/clangd/index/logger.c.DAEB4BC722A8C47E.idx b/.cache/clangd/index/logger.c.DAEB4BC722A8C47E.idx new file mode 100644 index 0000000..e11b8ce Binary files /dev/null and b/.cache/clangd/index/logger.c.DAEB4BC722A8C47E.idx differ diff --git a/.cache/clangd/index/logger.h.C02F19179C56A940.idx b/.cache/clangd/index/logger.h.C02F19179C56A940.idx new file mode 100644 index 0000000..45fe6b6 Binary files /dev/null and b/.cache/clangd/index/logger.h.C02F19179C56A940.idx differ diff --git a/.cache/clangd/index/long-double.h.ACD735A1862B6BF8.idx b/.cache/clangd/index/long-double.h.ACD735A1862B6BF8.idx new file mode 100644 index 0000000..750d852 Binary files /dev/null and b/.cache/clangd/index/long-double.h.ACD735A1862B6BF8.idx differ diff --git a/.cache/clangd/index/math-vector.h.B3EAC58DA1700969.idx b/.cache/clangd/index/math-vector.h.B3EAC58DA1700969.idx new file mode 100644 index 0000000..7240355 Binary files /dev/null and b/.cache/clangd/index/math-vector.h.B3EAC58DA1700969.idx differ diff --git a/.cache/clangd/index/math.h.C7C010203B7D0A27.idx b/.cache/clangd/index/math.h.C7C010203B7D0A27.idx new file mode 100644 index 0000000..55da6e7 Binary files /dev/null and b/.cache/clangd/index/math.h.C7C010203B7D0A27.idx differ diff --git a/.cache/clangd/index/math.h.DE4D39AAF5AC2C76.idx b/.cache/clangd/index/math.h.DE4D39AAF5AC2C76.idx new file mode 100644 index 0000000..6a594b2 Binary files /dev/null and b/.cache/clangd/index/math.h.DE4D39AAF5AC2C76.idx differ diff --git a/.cache/clangd/index/mathcalls-helper-functions.h.934C8C52064E404C.idx b/.cache/clangd/index/mathcalls-helper-functions.h.934C8C52064E404C.idx new file mode 100644 index 0000000..fa9dab6 Binary files /dev/null and b/.cache/clangd/index/mathcalls-helper-functions.h.934C8C52064E404C.idx differ diff --git a/.cache/clangd/index/mathcalls-narrow.h.EF4B0E9378BD4EE1.idx b/.cache/clangd/index/mathcalls-narrow.h.EF4B0E9378BD4EE1.idx new file mode 100644 index 0000000..a521295 Binary files /dev/null and b/.cache/clangd/index/mathcalls-narrow.h.EF4B0E9378BD4EE1.idx differ diff --git a/.cache/clangd/index/mathcalls.h.655A2220FF0677F8.idx b/.cache/clangd/index/mathcalls.h.655A2220FF0677F8.idx new file mode 100644 index 0000000..7b07557 Binary files /dev/null and b/.cache/clangd/index/mathcalls.h.655A2220FF0677F8.idx differ diff --git a/.cache/clangd/index/mathinline.h.AC15A182A28178AE.idx b/.cache/clangd/index/mathinline.h.AC15A182A28178AE.idx new file mode 100644 index 0000000..3adfcd2 Binary files /dev/null and b/.cache/clangd/index/mathinline.h.AC15A182A28178AE.idx differ diff --git a/.cache/clangd/index/mbstate_t.h.F9237FB65FF84D5A.idx b/.cache/clangd/index/mbstate_t.h.F9237FB65FF84D5A.idx new file mode 100644 index 0000000..436c554 Binary files /dev/null and b/.cache/clangd/index/mbstate_t.h.F9237FB65FF84D5A.idx differ diff --git a/.cache/clangd/index/memoryfwd.h.3AFA07D49A2F6427.idx b/.cache/clangd/index/memoryfwd.h.3AFA07D49A2F6427.idx new file mode 100644 index 0000000..a44edb8 Binary files /dev/null and b/.cache/clangd/index/memoryfwd.h.3AFA07D49A2F6427.idx differ diff --git a/.cache/clangd/index/mirror_buffer.c.D770ACC028225A86.idx b/.cache/clangd/index/mirror_buffer.c.D770ACC028225A86.idx new file mode 100644 index 0000000..37c78bd Binary files /dev/null and b/.cache/clangd/index/mirror_buffer.c.D770ACC028225A86.idx differ diff --git a/.cache/clangd/index/mirror_buffer.h.7BDECFBAFFFE0B22.idx b/.cache/clangd/index/mirror_buffer.h.7BDECFBAFFFE0B22.idx new file mode 100644 index 0000000..2c36129 Binary files /dev/null and b/.cache/clangd/index/mirror_buffer.h.7BDECFBAFFFE0B22.idx differ diff --git a/.cache/clangd/index/modified_md5.c.D50179EB3F348B01.idx b/.cache/clangd/index/modified_md5.c.D50179EB3F348B01.idx new file mode 100644 index 0000000..bb2a9ff Binary files /dev/null and b/.cache/clangd/index/modified_md5.c.D50179EB3F348B01.idx differ diff --git a/.cache/clangd/index/move.h.66002BD7D4D9657F.idx b/.cache/clangd/index/move.h.66002BD7D4D9657F.idx new file mode 100644 index 0000000..c7a73ba Binary files /dev/null and b/.cache/clangd/index/move.h.66002BD7D4D9657F.idx differ diff --git a/.cache/clangd/index/nested_exception.h.2B2F6CFEF2238F17.idx b/.cache/clangd/index/nested_exception.h.2B2F6CFEF2238F17.idx new file mode 100644 index 0000000..57220d9 Binary files /dev/null and b/.cache/clangd/index/nested_exception.h.2B2F6CFEF2238F17.idx differ diff --git a/.cache/clangd/index/netdb.h.8E1DCA58D61938DD.idx b/.cache/clangd/index/netdb.h.8E1DCA58D61938DD.idx new file mode 100644 index 0000000..e300d7a Binary files /dev/null and b/.cache/clangd/index/netdb.h.8E1DCA58D61938DD.idx differ diff --git a/.cache/clangd/index/netdb.h.9F63923995DF9ADF.idx b/.cache/clangd/index/netdb.h.9F63923995DF9ADF.idx new file mode 100644 index 0000000..2940a21 Binary files /dev/null and b/.cache/clangd/index/netdb.h.9F63923995DF9ADF.idx differ diff --git a/.cache/clangd/index/netdb.h.EBD13206C79B4A1C.idx b/.cache/clangd/index/netdb.h.EBD13206C79B4A1C.idx new file mode 100644 index 0000000..46431ab Binary files /dev/null and b/.cache/clangd/index/netdb.h.EBD13206C79B4A1C.idx differ diff --git a/.cache/clangd/index/netutils.c.D72FE88BBBAC616B.idx b/.cache/clangd/index/netutils.c.D72FE88BBBAC616B.idx new file mode 100644 index 0000000..e50f7f8 Binary files /dev/null and b/.cache/clangd/index/netutils.c.D72FE88BBBAC616B.idx differ diff --git a/.cache/clangd/index/netutils.h.0D90EFA1DF55BF1E.idx b/.cache/clangd/index/netutils.h.0D90EFA1DF55BF1E.idx new file mode 100644 index 0000000..6b01ce5 Binary files /dev/null and b/.cache/clangd/index/netutils.h.0D90EFA1DF55BF1E.idx differ diff --git a/.cache/clangd/index/new.05A56BDB26BFB891.idx b/.cache/clangd/index/new.05A56BDB26BFB891.idx new file mode 100644 index 0000000..fab39d6 Binary files /dev/null and b/.cache/clangd/index/new.05A56BDB26BFB891.idx differ diff --git a/.cache/clangd/index/new_allocator.h.9159B59DFF76E157.idx b/.cache/clangd/index/new_allocator.h.9159B59DFF76E157.idx new file mode 100644 index 0000000..72c5dd3 Binary files /dev/null and b/.cache/clangd/index/new_allocator.h.9159B59DFF76E157.idx differ diff --git a/.cache/clangd/index/numeric_traits.h.34CECE852C3E540C.idx b/.cache/clangd/index/numeric_traits.h.34CECE852C3E540C.idx new file mode 100644 index 0000000..0775429 Binary files /dev/null and b/.cache/clangd/index/numeric_traits.h.34CECE852C3E540C.idx differ diff --git a/.cache/clangd/index/obj_mac.h.A52CAA6380B17282.idx b/.cache/clangd/index/obj_mac.h.A52CAA6380B17282.idx new file mode 100644 index 0000000..08cd13b Binary files /dev/null and b/.cache/clangd/index/obj_mac.h.A52CAA6380B17282.idx differ diff --git a/.cache/clangd/index/objects.h.DD5383CAC735B14B.idx b/.cache/clangd/index/objects.h.DD5383CAC735B14B.idx new file mode 100644 index 0000000..615471b Binary files /dev/null and b/.cache/clangd/index/objects.h.DD5383CAC735B14B.idx differ diff --git a/.cache/clangd/index/objectserr.h.3AD87738F0D640DE.idx b/.cache/clangd/index/objectserr.h.3AD87738F0D640DE.idx new file mode 100644 index 0000000..ae2fddf Binary files /dev/null and b/.cache/clangd/index/objectserr.h.3AD87738F0D640DE.idx differ diff --git a/.cache/clangd/index/omg_hax.c.223C086349D24E11.idx b/.cache/clangd/index/omg_hax.c.223C086349D24E11.idx new file mode 100644 index 0000000..dc56699 Binary files /dev/null and b/.cache/clangd/index/omg_hax.c.223C086349D24E11.idx differ diff --git a/.cache/clangd/index/omg_hax.h.4FF7ABFDACFDBDF6.idx b/.cache/clangd/index/omg_hax.h.4FF7ABFDACFDBDF6.idx new file mode 100644 index 0000000..0628916 Binary files /dev/null and b/.cache/clangd/index/omg_hax.h.4FF7ABFDACFDBDF6.idx differ diff --git a/.cache/clangd/index/opensslconf.h.E80A598B9DEA76DA.idx b/.cache/clangd/index/opensslconf.h.E80A598B9DEA76DA.idx new file mode 100644 index 0000000..2993e1c Binary files /dev/null and b/.cache/clangd/index/opensslconf.h.E80A598B9DEA76DA.idx differ diff --git a/.cache/clangd/index/opensslv.h.095BCA30014AF354.idx b/.cache/clangd/index/opensslv.h.095BCA30014AF354.idx new file mode 100644 index 0000000..d7fbbdc Binary files /dev/null and b/.cache/clangd/index/opensslv.h.095BCA30014AF354.idx differ diff --git a/.cache/clangd/index/os_defines.h.3339A82D644F70AE.idx b/.cache/clangd/index/os_defines.h.3339A82D644F70AE.idx new file mode 100644 index 0000000..3670faa Binary files /dev/null and b/.cache/clangd/index/os_defines.h.3339A82D644F70AE.idx differ diff --git a/.cache/clangd/index/ossl_typ.h.7BCF0BBA07EA9A13.idx b/.cache/clangd/index/ossl_typ.h.7BCF0BBA07EA9A13.idx new file mode 100644 index 0000000..868688d Binary files /dev/null and b/.cache/clangd/index/ossl_typ.h.7BCF0BBA07EA9A13.idx differ diff --git a/.cache/clangd/index/ostream.57C45D01A3C6A56D.idx b/.cache/clangd/index/ostream.57C45D01A3C6A56D.idx new file mode 100644 index 0000000..cb95f4b Binary files /dev/null and b/.cache/clangd/index/ostream.57C45D01A3C6A56D.idx differ diff --git a/.cache/clangd/index/ostream.tcc.4ADAE98D83FEAAE5.idx b/.cache/clangd/index/ostream.tcc.4ADAE98D83FEAAE5.idx new file mode 100644 index 0000000..eccf792 Binary files /dev/null and b/.cache/clangd/index/ostream.tcc.4ADAE98D83FEAAE5.idx differ diff --git a/.cache/clangd/index/ostream_insert.h.51ECE361EEBB9C13.idx b/.cache/clangd/index/ostream_insert.h.51ECE361EEBB9C13.idx new file mode 100644 index 0000000..ebb16f2 Binary files /dev/null and b/.cache/clangd/index/ostream_insert.h.51ECE361EEBB9C13.idx differ diff --git a/.cache/clangd/index/packet.h.79110192E8A2E04F.idx b/.cache/clangd/index/packet.h.79110192E8A2E04F.idx new file mode 100644 index 0000000..974f8df Binary files /dev/null and b/.cache/clangd/index/packet.h.79110192E8A2E04F.idx differ diff --git a/.cache/clangd/index/pairing.c.794ADB00F9AB179D.idx b/.cache/clangd/index/pairing.c.794ADB00F9AB179D.idx new file mode 100644 index 0000000..48bab45 Binary files /dev/null and b/.cache/clangd/index/pairing.c.794ADB00F9AB179D.idx differ diff --git a/.cache/clangd/index/pairing.h.BFA48264E757438E.idx b/.cache/clangd/index/pairing.h.BFA48264E757438E.idx new file mode 100644 index 0000000..56d50a5 Binary files /dev/null and b/.cache/clangd/index/pairing.h.BFA48264E757438E.idx differ diff --git a/.cache/clangd/index/pem.h.7B6D842EA5CBE213.idx b/.cache/clangd/index/pem.h.7B6D842EA5CBE213.idx new file mode 100644 index 0000000..03ee230 Binary files /dev/null and b/.cache/clangd/index/pem.h.7B6D842EA5CBE213.idx differ diff --git a/.cache/clangd/index/pemerr.h.45A2BC2B263F5DF9.idx b/.cache/clangd/index/pemerr.h.45A2BC2B263F5DF9.idx new file mode 100644 index 0000000..a024217 Binary files /dev/null and b/.cache/clangd/index/pemerr.h.45A2BC2B263F5DF9.idx differ diff --git a/.cache/clangd/index/pkcs7.h.9232F8FB47E7A33E.idx b/.cache/clangd/index/pkcs7.h.9232F8FB47E7A33E.idx new file mode 100644 index 0000000..cb7ee08 Binary files /dev/null and b/.cache/clangd/index/pkcs7.h.9232F8FB47E7A33E.idx differ diff --git a/.cache/clangd/index/pkcs7err.h.E57D386DD65E6CB2.idx b/.cache/clangd/index/pkcs7err.h.E57D386DD65E6CB2.idx new file mode 100644 index 0000000..6858bee Binary files /dev/null and b/.cache/clangd/index/pkcs7err.h.E57D386DD65E6CB2.idx differ diff --git a/.cache/clangd/index/playfair.c.603194332B80439F.idx b/.cache/clangd/index/playfair.c.603194332B80439F.idx new file mode 100644 index 0000000..26aeb57 Binary files /dev/null and b/.cache/clangd/index/playfair.c.603194332B80439F.idx differ diff --git a/.cache/clangd/index/playfair.h.0C33210D1EB5270A.idx b/.cache/clangd/index/playfair.h.0C33210D1EB5270A.idx new file mode 100644 index 0000000..72c5415 Binary files /dev/null and b/.cache/clangd/index/playfair.h.0C33210D1EB5270A.idx differ diff --git a/.cache/clangd/index/plist.h.450175006F5E2C29.idx b/.cache/clangd/index/plist.h.450175006F5E2C29.idx new file mode 100644 index 0000000..a5b54ce Binary files /dev/null and b/.cache/clangd/index/plist.h.450175006F5E2C29.idx differ diff --git a/.cache/clangd/index/posix1_lim.h.D94E4687FC9359FF.idx b/.cache/clangd/index/posix1_lim.h.D94E4687FC9359FF.idx new file mode 100644 index 0000000..93a6b26 Binary files /dev/null and b/.cache/clangd/index/posix1_lim.h.D94E4687FC9359FF.idx differ diff --git a/.cache/clangd/index/posix2_lim.h.430DA38A1F301F29.idx b/.cache/clangd/index/posix2_lim.h.430DA38A1F301F29.idx new file mode 100644 index 0000000..e35b04c Binary files /dev/null and b/.cache/clangd/index/posix2_lim.h.430DA38A1F301F29.idx differ diff --git a/.cache/clangd/index/posix_opt.h.1A6B676984A189EC.idx b/.cache/clangd/index/posix_opt.h.1A6B676984A189EC.idx new file mode 100644 index 0000000..252a8c1 Binary files /dev/null and b/.cache/clangd/index/posix_opt.h.1A6B676984A189EC.idx differ diff --git a/.cache/clangd/index/posix_types.h.109D032764DE7736.idx b/.cache/clangd/index/posix_types.h.109D032764DE7736.idx new file mode 100644 index 0000000..6505a6e Binary files /dev/null and b/.cache/clangd/index/posix_types.h.109D032764DE7736.idx differ diff --git a/.cache/clangd/index/posix_types.h.825B0F552923DCCC.idx b/.cache/clangd/index/posix_types.h.825B0F552923DCCC.idx new file mode 100644 index 0000000..6c6ade6 Binary files /dev/null and b/.cache/clangd/index/posix_types.h.825B0F552923DCCC.idx differ diff --git a/.cache/clangd/index/posix_types.h.8EBBF1FC83D6AE77.idx b/.cache/clangd/index/posix_types.h.8EBBF1FC83D6AE77.idx new file mode 100644 index 0000000..73080fc Binary files /dev/null and b/.cache/clangd/index/posix_types.h.8EBBF1FC83D6AE77.idx differ diff --git a/.cache/clangd/index/posix_types_64.h.A12FDCD6ECB1FBD9.idx b/.cache/clangd/index/posix_types_64.h.A12FDCD6ECB1FBD9.idx new file mode 100644 index 0000000..451576f Binary files /dev/null and b/.cache/clangd/index/posix_types_64.h.A12FDCD6ECB1FBD9.idx differ diff --git a/.cache/clangd/index/postypes.h.6B4D7040E5D6D59E.idx b/.cache/clangd/index/postypes.h.6B4D7040E5D6D59E.idx new file mode 100644 index 0000000..f69f66a Binary files /dev/null and b/.cache/clangd/index/postypes.h.6B4D7040E5D6D59E.idx differ diff --git a/.cache/clangd/index/predefined_ops.h.6FAE156C8E7E5978.idx b/.cache/clangd/index/predefined_ops.h.6FAE156C8E7E5978.idx new file mode 100644 index 0000000..12a71ae Binary files /dev/null and b/.cache/clangd/index/predefined_ops.h.6FAE156C8E7E5978.idx differ diff --git a/.cache/clangd/index/pthread.h.022320E0CE01A46D.idx b/.cache/clangd/index/pthread.h.022320E0CE01A46D.idx new file mode 100644 index 0000000..644b1ed Binary files /dev/null and b/.cache/clangd/index/pthread.h.022320E0CE01A46D.idx differ diff --git a/.cache/clangd/index/pthreadtypes-arch.h.9E138C411BE448A0.idx b/.cache/clangd/index/pthreadtypes-arch.h.9E138C411BE448A0.idx new file mode 100644 index 0000000..08070b8 Binary files /dev/null and b/.cache/clangd/index/pthreadtypes-arch.h.9E138C411BE448A0.idx differ diff --git a/.cache/clangd/index/pthreadtypes.h.BAA8217B62BB6E86.idx b/.cache/clangd/index/pthreadtypes.h.BAA8217B62BB6E86.idx new file mode 100644 index 0000000..401637f Binary files /dev/null and b/.cache/clangd/index/pthreadtypes.h.BAA8217B62BB6E86.idx differ diff --git a/.cache/clangd/index/ptr_traits.h.E1B9070FD388B721.idx b/.cache/clangd/index/ptr_traits.h.E1B9070FD388B721.idx new file mode 100644 index 0000000..10504c7 Binary files /dev/null and b/.cache/clangd/index/ptr_traits.h.E1B9070FD388B721.idx differ diff --git a/.cache/clangd/index/pwd.h.ECBEFF303233900E.idx b/.cache/clangd/index/pwd.h.ECBEFF303233900E.idx new file mode 100644 index 0000000..7db05ed Binary files /dev/null and b/.cache/clangd/index/pwd.h.ECBEFF303233900E.idx differ diff --git a/.cache/clangd/index/rand.h.998788C4B3E77C44.idx b/.cache/clangd/index/rand.h.998788C4B3E77C44.idx new file mode 100644 index 0000000..73de9ba Binary files /dev/null and b/.cache/clangd/index/rand.h.998788C4B3E77C44.idx differ diff --git a/.cache/clangd/index/randerr.h.3AE856BE6F79215A.idx b/.cache/clangd/index/randerr.h.3AE856BE6F79215A.idx new file mode 100644 index 0000000..916bb81 Binary files /dev/null and b/.cache/clangd/index/randerr.h.3AE856BE6F79215A.idx differ diff --git a/.cache/clangd/index/range_access.h.8640220878640514.idx b/.cache/clangd/index/range_access.h.8640220878640514.idx new file mode 100644 index 0000000..d57460c Binary files /dev/null and b/.cache/clangd/index/range_access.h.8640220878640514.idx differ diff --git a/.cache/clangd/index/range_cmp.h.064D464B55F72D98.idx b/.cache/clangd/index/range_cmp.h.064D464B55F72D98.idx new file mode 100644 index 0000000..53e90fe Binary files /dev/null and b/.cache/clangd/index/range_cmp.h.064D464B55F72D98.idx differ diff --git a/.cache/clangd/index/raop.c.4DC8061129CB5764.idx b/.cache/clangd/index/raop.c.4DC8061129CB5764.idx new file mode 100644 index 0000000..376a7ce Binary files /dev/null and b/.cache/clangd/index/raop.c.4DC8061129CB5764.idx differ diff --git a/.cache/clangd/index/raop.h.3B4269DA053EB113.idx b/.cache/clangd/index/raop.h.3B4269DA053EB113.idx new file mode 100644 index 0000000..9390b63 Binary files /dev/null and b/.cache/clangd/index/raop.h.3B4269DA053EB113.idx differ diff --git a/.cache/clangd/index/raop_buffer.c.C290CDF062945F25.idx b/.cache/clangd/index/raop_buffer.c.C290CDF062945F25.idx new file mode 100644 index 0000000..e9b6fb9 Binary files /dev/null and b/.cache/clangd/index/raop_buffer.c.C290CDF062945F25.idx differ diff --git a/.cache/clangd/index/raop_buffer.h.7ED6A640BC71909B.idx b/.cache/clangd/index/raop_buffer.h.7ED6A640BC71909B.idx new file mode 100644 index 0000000..dcb7c47 Binary files /dev/null and b/.cache/clangd/index/raop_buffer.h.7ED6A640BC71909B.idx differ diff --git a/.cache/clangd/index/raop_handlers.h.570D63FD009177DD.idx b/.cache/clangd/index/raop_handlers.h.570D63FD009177DD.idx new file mode 100644 index 0000000..e5d99ed Binary files /dev/null and b/.cache/clangd/index/raop_handlers.h.570D63FD009177DD.idx differ diff --git a/.cache/clangd/index/raop_ntp.c.B43D5E7CFB2607D8.idx b/.cache/clangd/index/raop_ntp.c.B43D5E7CFB2607D8.idx new file mode 100644 index 0000000..98b460b Binary files /dev/null and b/.cache/clangd/index/raop_ntp.c.B43D5E7CFB2607D8.idx differ diff --git a/.cache/clangd/index/raop_ntp.h.39EA34DFA1197625.idx b/.cache/clangd/index/raop_ntp.h.39EA34DFA1197625.idx new file mode 100644 index 0000000..5df9874 Binary files /dev/null and b/.cache/clangd/index/raop_ntp.h.39EA34DFA1197625.idx differ diff --git a/.cache/clangd/index/raop_rtp.c.ADBF9AC530F64EF3.idx b/.cache/clangd/index/raop_rtp.c.ADBF9AC530F64EF3.idx new file mode 100644 index 0000000..822bd76 Binary files /dev/null and b/.cache/clangd/index/raop_rtp.c.ADBF9AC530F64EF3.idx differ diff --git a/.cache/clangd/index/raop_rtp.h.A338DE867CB89F21.idx b/.cache/clangd/index/raop_rtp.h.A338DE867CB89F21.idx new file mode 100644 index 0000000..51757b9 Binary files /dev/null and b/.cache/clangd/index/raop_rtp.h.A338DE867CB89F21.idx differ diff --git a/.cache/clangd/index/raop_rtp_mirror.c.1746A63CD8F36CA4.idx b/.cache/clangd/index/raop_rtp_mirror.c.1746A63CD8F36CA4.idx new file mode 100644 index 0000000..a69f5ab Binary files /dev/null and b/.cache/clangd/index/raop_rtp_mirror.c.1746A63CD8F36CA4.idx differ diff --git a/.cache/clangd/index/raop_rtp_mirror.h.EF615F6D8ED1B38A.idx b/.cache/clangd/index/raop_rtp_mirror.h.EF615F6D8ED1B38A.idx new file mode 100644 index 0000000..3db9df1 Binary files /dev/null and b/.cache/clangd/index/raop_rtp_mirror.h.EF615F6D8ED1B38A.idx differ diff --git a/.cache/clangd/index/safestack.h.B4FBD6368CB616A9.idx b/.cache/clangd/index/safestack.h.B4FBD6368CB616A9.idx new file mode 100644 index 0000000..08fb51c Binary files /dev/null and b/.cache/clangd/index/safestack.h.B4FBD6368CB616A9.idx differ diff --git a/.cache/clangd/index/sap_hash.c.23D768A7A42DD27A.idx b/.cache/clangd/index/sap_hash.c.23D768A7A42DD27A.idx new file mode 100644 index 0000000..ffa06fb Binary files /dev/null and b/.cache/clangd/index/sap_hash.c.23D768A7A42DD27A.idx differ diff --git a/.cache/clangd/index/sched.h.A585A48500495348.idx b/.cache/clangd/index/sched.h.A585A48500495348.idx new file mode 100644 index 0000000..6c9e000 Binary files /dev/null and b/.cache/clangd/index/sched.h.A585A48500495348.idx differ diff --git a/.cache/clangd/index/sched.h.E8530EF262253892.idx b/.cache/clangd/index/sched.h.E8530EF262253892.idx new file mode 100644 index 0000000..2b676d6 Binary files /dev/null and b/.cache/clangd/index/sched.h.E8530EF262253892.idx differ diff --git a/.cache/clangd/index/select.h.BA57472D13519767.idx b/.cache/clangd/index/select.h.BA57472D13519767.idx new file mode 100644 index 0000000..40930a1 Binary files /dev/null and b/.cache/clangd/index/select.h.BA57472D13519767.idx differ diff --git a/.cache/clangd/index/select.h.E19325D0BE3592A0.idx b/.cache/clangd/index/select.h.E19325D0BE3592A0.idx new file mode 100644 index 0000000..94dc155 Binary files /dev/null and b/.cache/clangd/index/select.h.E19325D0BE3592A0.idx differ diff --git a/.cache/clangd/index/setjmp.h.71DEFB3DCC800794.idx b/.cache/clangd/index/setjmp.h.71DEFB3DCC800794.idx new file mode 100644 index 0000000..0776618 Binary files /dev/null and b/.cache/clangd/index/setjmp.h.71DEFB3DCC800794.idx differ diff --git a/.cache/clangd/index/sha.h.53F4CE410EAD9A48.idx b/.cache/clangd/index/sha.h.53F4CE410EAD9A48.idx new file mode 100644 index 0000000..944d10f Binary files /dev/null and b/.cache/clangd/index/sha.h.53F4CE410EAD9A48.idx differ diff --git a/.cache/clangd/index/sig_atomic_t.h.3B536F8E741B9623.idx b/.cache/clangd/index/sig_atomic_t.h.3B536F8E741B9623.idx new file mode 100644 index 0000000..e90fc5f Binary files /dev/null and b/.cache/clangd/index/sig_atomic_t.h.3B536F8E741B9623.idx differ diff --git a/.cache/clangd/index/sigaction.h.66B137993ED41FA6.idx b/.cache/clangd/index/sigaction.h.66B137993ED41FA6.idx new file mode 100644 index 0000000..5e745dd Binary files /dev/null and b/.cache/clangd/index/sigaction.h.66B137993ED41FA6.idx differ diff --git a/.cache/clangd/index/sigcontext.h.7600F5C8B10DFACF.idx b/.cache/clangd/index/sigcontext.h.7600F5C8B10DFACF.idx new file mode 100644 index 0000000..c5ca516 Binary files /dev/null and b/.cache/clangd/index/sigcontext.h.7600F5C8B10DFACF.idx differ diff --git a/.cache/clangd/index/sigevent-consts.h.164FB5F64CB196D3.idx b/.cache/clangd/index/sigevent-consts.h.164FB5F64CB196D3.idx new file mode 100644 index 0000000..f88042e Binary files /dev/null and b/.cache/clangd/index/sigevent-consts.h.164FB5F64CB196D3.idx differ diff --git a/.cache/clangd/index/sigevent_t.h.63456B1714A8FD74.idx b/.cache/clangd/index/sigevent_t.h.63456B1714A8FD74.idx new file mode 100644 index 0000000..4ea137e Binary files /dev/null and b/.cache/clangd/index/sigevent_t.h.63456B1714A8FD74.idx differ diff --git a/.cache/clangd/index/siginfo-arch.h.DDEDA2246C6214DB.idx b/.cache/clangd/index/siginfo-arch.h.DDEDA2246C6214DB.idx new file mode 100644 index 0000000..15cbb15 Binary files /dev/null and b/.cache/clangd/index/siginfo-arch.h.DDEDA2246C6214DB.idx differ diff --git a/.cache/clangd/index/siginfo-consts-arch.h.DC5C572A2A163B04.idx b/.cache/clangd/index/siginfo-consts-arch.h.DC5C572A2A163B04.idx new file mode 100644 index 0000000..a4ec410 Binary files /dev/null and b/.cache/clangd/index/siginfo-consts-arch.h.DC5C572A2A163B04.idx differ diff --git a/.cache/clangd/index/siginfo-consts.h.086583452C13A7E3.idx b/.cache/clangd/index/siginfo-consts.h.086583452C13A7E3.idx new file mode 100644 index 0000000..311b05a Binary files /dev/null and b/.cache/clangd/index/siginfo-consts.h.086583452C13A7E3.idx differ diff --git a/.cache/clangd/index/siginfo_t.h.34DBB5C2C8344FC2.idx b/.cache/clangd/index/siginfo_t.h.34DBB5C2C8344FC2.idx new file mode 100644 index 0000000..0a3100e Binary files /dev/null and b/.cache/clangd/index/siginfo_t.h.34DBB5C2C8344FC2.idx differ diff --git a/.cache/clangd/index/signal.h.9A173143B440B5DA.idx b/.cache/clangd/index/signal.h.9A173143B440B5DA.idx new file mode 100644 index 0000000..3cfbae7 Binary files /dev/null and b/.cache/clangd/index/signal.h.9A173143B440B5DA.idx differ diff --git a/.cache/clangd/index/signal_ext.h.ED0CC89A982A1E6A.idx b/.cache/clangd/index/signal_ext.h.ED0CC89A982A1E6A.idx new file mode 100644 index 0000000..72e3999 Binary files /dev/null and b/.cache/clangd/index/signal_ext.h.ED0CC89A982A1E6A.idx differ diff --git a/.cache/clangd/index/signum-generic.h.712CE9FB2F7CAED7.idx b/.cache/clangd/index/signum-generic.h.712CE9FB2F7CAED7.idx new file mode 100644 index 0000000..83d51ed Binary files /dev/null and b/.cache/clangd/index/signum-generic.h.712CE9FB2F7CAED7.idx differ diff --git a/.cache/clangd/index/signum.h.FFC0F3B8B7FDDA0B.idx b/.cache/clangd/index/signum.h.FFC0F3B8B7FDDA0B.idx new file mode 100644 index 0000000..9f83529 Binary files /dev/null and b/.cache/clangd/index/signum.h.FFC0F3B8B7FDDA0B.idx differ diff --git a/.cache/clangd/index/sigset_t.h.32273C537A103809.idx b/.cache/clangd/index/sigset_t.h.32273C537A103809.idx new file mode 100644 index 0000000..261f145 Binary files /dev/null and b/.cache/clangd/index/sigset_t.h.32273C537A103809.idx differ diff --git a/.cache/clangd/index/sigstack.h.C067B2382ACC7FB2.idx b/.cache/clangd/index/sigstack.h.C067B2382ACC7FB2.idx new file mode 100644 index 0000000..8c4695e Binary files /dev/null and b/.cache/clangd/index/sigstack.h.C067B2382ACC7FB2.idx differ diff --git a/.cache/clangd/index/sigthread.h.B1F62700EC3B3575.idx b/.cache/clangd/index/sigthread.h.B1F62700EC3B3575.idx new file mode 100644 index 0000000..23a6be5 Binary files /dev/null and b/.cache/clangd/index/sigthread.h.B1F62700EC3B3575.idx differ diff --git a/.cache/clangd/index/sigval_t.h.5D6D66DDD393104C.idx b/.cache/clangd/index/sigval_t.h.5D6D66DDD393104C.idx new file mode 100644 index 0000000..04ced39 Binary files /dev/null and b/.cache/clangd/index/sigval_t.h.5D6D66DDD393104C.idx differ diff --git a/.cache/clangd/index/sockaddr.h.9CA21C5F89358D89.idx b/.cache/clangd/index/sockaddr.h.9CA21C5F89358D89.idx new file mode 100644 index 0000000..958fb92 Binary files /dev/null and b/.cache/clangd/index/sockaddr.h.9CA21C5F89358D89.idx differ diff --git a/.cache/clangd/index/socket.h.04EB1A43278AEF57.idx b/.cache/clangd/index/socket.h.04EB1A43278AEF57.idx new file mode 100644 index 0000000..9bcc375 Binary files /dev/null and b/.cache/clangd/index/socket.h.04EB1A43278AEF57.idx differ diff --git a/.cache/clangd/index/socket.h.AEE20B1C32168942.idx b/.cache/clangd/index/socket.h.AEE20B1C32168942.idx new file mode 100644 index 0000000..9d0dd60 Binary files /dev/null and b/.cache/clangd/index/socket.h.AEE20B1C32168942.idx differ diff --git a/.cache/clangd/index/socket.h.EC1958727F5628B5.idx b/.cache/clangd/index/socket.h.EC1958727F5628B5.idx new file mode 100644 index 0000000..3d2eb65 Binary files /dev/null and b/.cache/clangd/index/socket.h.EC1958727F5628B5.idx differ diff --git a/.cache/clangd/index/socket.h.F690A131FE4E6720.idx b/.cache/clangd/index/socket.h.F690A131FE4E6720.idx new file mode 100644 index 0000000..4d4f1c1 Binary files /dev/null and b/.cache/clangd/index/socket.h.F690A131FE4E6720.idx differ diff --git a/.cache/clangd/index/socket_type.h.F297E4C9AB857715.idx b/.cache/clangd/index/socket_type.h.F297E4C9AB857715.idx new file mode 100644 index 0000000..260199d Binary files /dev/null and b/.cache/clangd/index/socket_type.h.F297E4C9AB857715.idx differ diff --git a/.cache/clangd/index/sockets.h.4927F49DEF6C37DC.idx b/.cache/clangd/index/sockets.h.4927F49DEF6C37DC.idx new file mode 100644 index 0000000..ff4b81e Binary files /dev/null and b/.cache/clangd/index/sockets.h.4927F49DEF6C37DC.idx differ diff --git a/.cache/clangd/index/sockios.h.51FEA1F9C917CA52.idx b/.cache/clangd/index/sockios.h.51FEA1F9C917CA52.idx new file mode 100644 index 0000000..a400aa4 Binary files /dev/null and b/.cache/clangd/index/sockios.h.51FEA1F9C917CA52.idx differ diff --git a/.cache/clangd/index/sockios.h.A634DAD93F5F4A89.idx b/.cache/clangd/index/sockios.h.A634DAD93F5F4A89.idx new file mode 100644 index 0000000..9a00f3e Binary files /dev/null and b/.cache/clangd/index/sockios.h.A634DAD93F5F4A89.idx differ diff --git a/.cache/clangd/index/srp.c.9072E84F8D9F39F1.idx b/.cache/clangd/index/srp.c.9072E84F8D9F39F1.idx new file mode 100644 index 0000000..6507386 Binary files /dev/null and b/.cache/clangd/index/srp.c.9072E84F8D9F39F1.idx differ diff --git a/.cache/clangd/index/srp.h.AD52200677A43A6C.idx b/.cache/clangd/index/srp.h.AD52200677A43A6C.idx new file mode 100644 index 0000000..181f7f1 Binary files /dev/null and b/.cache/clangd/index/srp.h.AD52200677A43A6C.idx differ diff --git a/.cache/clangd/index/ss_flags.h.CA0DB82E0B94B6A5.idx b/.cache/clangd/index/ss_flags.h.CA0DB82E0B94B6A5.idx new file mode 100644 index 0000000..8cb92d5 Binary files /dev/null and b/.cache/clangd/index/ss_flags.h.CA0DB82E0B94B6A5.idx differ diff --git a/.cache/clangd/index/sstream.AEAD25600C8A0327.idx b/.cache/clangd/index/sstream.AEAD25600C8A0327.idx new file mode 100644 index 0000000..9458d48 Binary files /dev/null and b/.cache/clangd/index/sstream.AEAD25600C8A0327.idx differ diff --git a/.cache/clangd/index/sstream.tcc.03A9DD503F657346.idx b/.cache/clangd/index/sstream.tcc.03A9DD503F657346.idx new file mode 100644 index 0000000..480ddd2 Binary files /dev/null and b/.cache/clangd/index/sstream.tcc.03A9DD503F657346.idx differ diff --git a/.cache/clangd/index/stack.h.8D7621E6D65D9CB8.idx b/.cache/clangd/index/stack.h.8D7621E6D65D9CB8.idx new file mode 100644 index 0000000..9d8e4de Binary files /dev/null and b/.cache/clangd/index/stack.h.8D7621E6D65D9CB8.idx differ diff --git a/.cache/clangd/index/stack_t.h.F69D87D663935BB6.idx b/.cache/clangd/index/stack_t.h.F69D87D663935BB6.idx new file mode 100644 index 0000000..6055b08 Binary files /dev/null and b/.cache/clangd/index/stack_t.h.F69D87D663935BB6.idx differ diff --git a/.cache/clangd/index/stat.h.091FA1871C7E9189.idx b/.cache/clangd/index/stat.h.091FA1871C7E9189.idx new file mode 100644 index 0000000..caf3b30 Binary files /dev/null and b/.cache/clangd/index/stat.h.091FA1871C7E9189.idx differ diff --git a/.cache/clangd/index/stat.h.86C3C850450F2AF6.idx b/.cache/clangd/index/stat.h.86C3C850450F2AF6.idx new file mode 100644 index 0000000..b3ae192 Binary files /dev/null and b/.cache/clangd/index/stat.h.86C3C850450F2AF6.idx differ diff --git a/.cache/clangd/index/stat.h.9B75F06B412D4302.idx b/.cache/clangd/index/stat.h.9B75F06B412D4302.idx new file mode 100644 index 0000000..69c8aae Binary files /dev/null and b/.cache/clangd/index/stat.h.9B75F06B412D4302.idx differ diff --git a/.cache/clangd/index/statx-generic.h.801B7CF5F64698C0.idx b/.cache/clangd/index/statx-generic.h.801B7CF5F64698C0.idx new file mode 100644 index 0000000..9a7ee14 Binary files /dev/null and b/.cache/clangd/index/statx-generic.h.801B7CF5F64698C0.idx differ diff --git a/.cache/clangd/index/statx.h.1A5DDCB2373AC022.idx b/.cache/clangd/index/statx.h.1A5DDCB2373AC022.idx new file mode 100644 index 0000000..ca1b4bf Binary files /dev/null and b/.cache/clangd/index/statx.h.1A5DDCB2373AC022.idx differ diff --git a/.cache/clangd/index/std_abs.h.C5FD01A6799379AA.idx b/.cache/clangd/index/std_abs.h.C5FD01A6799379AA.idx new file mode 100644 index 0000000..aaaff1f Binary files /dev/null and b/.cache/clangd/index/std_abs.h.C5FD01A6799379AA.idx differ diff --git a/.cache/clangd/index/stdarg.h.32EA7B95DAFCCF99.idx b/.cache/clangd/index/stdarg.h.32EA7B95DAFCCF99.idx new file mode 100644 index 0000000..e8fafcb Binary files /dev/null and b/.cache/clangd/index/stdarg.h.32EA7B95DAFCCF99.idx differ diff --git a/.cache/clangd/index/stdbool.h.CEE9A09AF9E79F0A.idx b/.cache/clangd/index/stdbool.h.CEE9A09AF9E79F0A.idx new file mode 100644 index 0000000..4d8e843 Binary files /dev/null and b/.cache/clangd/index/stdbool.h.CEE9A09AF9E79F0A.idx differ diff --git a/.cache/clangd/index/stdc-predef.h.9EA93D70716BF84B.idx b/.cache/clangd/index/stdc-predef.h.9EA93D70716BF84B.idx new file mode 100644 index 0000000..85a4113 Binary files /dev/null and b/.cache/clangd/index/stdc-predef.h.9EA93D70716BF84B.idx differ diff --git a/.cache/clangd/index/stddef.h.573E230AE7BF71F3.idx b/.cache/clangd/index/stddef.h.573E230AE7BF71F3.idx new file mode 100644 index 0000000..52bb250 Binary files /dev/null and b/.cache/clangd/index/stddef.h.573E230AE7BF71F3.idx differ diff --git a/.cache/clangd/index/stddef.h.B10142B5B6E10391.idx b/.cache/clangd/index/stddef.h.B10142B5B6E10391.idx new file mode 100644 index 0000000..3c9cfcd Binary files /dev/null and b/.cache/clangd/index/stddef.h.B10142B5B6E10391.idx differ diff --git a/.cache/clangd/index/stdexcept.4716110D4F978B64.idx b/.cache/clangd/index/stdexcept.4716110D4F978B64.idx new file mode 100644 index 0000000..93fdf53 Binary files /dev/null and b/.cache/clangd/index/stdexcept.4716110D4F978B64.idx differ diff --git a/.cache/clangd/index/stdint-intn.h.EE0939E700184847.idx b/.cache/clangd/index/stdint-intn.h.EE0939E700184847.idx new file mode 100644 index 0000000..50766ed Binary files /dev/null and b/.cache/clangd/index/stdint-intn.h.EE0939E700184847.idx differ diff --git a/.cache/clangd/index/stdint-uintn.h.B3EA113168048B57.idx b/.cache/clangd/index/stdint-uintn.h.B3EA113168048B57.idx new file mode 100644 index 0000000..61aebea Binary files /dev/null and b/.cache/clangd/index/stdint-uintn.h.B3EA113168048B57.idx differ diff --git a/.cache/clangd/index/stdint.h.08597F86630FDE5E.idx b/.cache/clangd/index/stdint.h.08597F86630FDE5E.idx new file mode 100644 index 0000000..0bed224 Binary files /dev/null and b/.cache/clangd/index/stdint.h.08597F86630FDE5E.idx differ diff --git a/.cache/clangd/index/stdint.h.22B451C129B23D79.idx b/.cache/clangd/index/stdint.h.22B451C129B23D79.idx new file mode 100644 index 0000000..778a4ca Binary files /dev/null and b/.cache/clangd/index/stdint.h.22B451C129B23D79.idx differ diff --git a/.cache/clangd/index/stdio.h.6919C07CB89D47AD.idx b/.cache/clangd/index/stdio.h.6919C07CB89D47AD.idx new file mode 100644 index 0000000..a0b0506 Binary files /dev/null and b/.cache/clangd/index/stdio.h.6919C07CB89D47AD.idx differ diff --git a/.cache/clangd/index/stdio.h.7A48852493806B8A.idx b/.cache/clangd/index/stdio.h.7A48852493806B8A.idx new file mode 100644 index 0000000..80330ed Binary files /dev/null and b/.cache/clangd/index/stdio.h.7A48852493806B8A.idx differ diff --git a/.cache/clangd/index/stdio_lim.h.09E79B5FC5C8DF86.idx b/.cache/clangd/index/stdio_lim.h.09E79B5FC5C8DF86.idx new file mode 100644 index 0000000..8a81a13 Binary files /dev/null and b/.cache/clangd/index/stdio_lim.h.09E79B5FC5C8DF86.idx differ diff --git a/.cache/clangd/index/stdlib-bsearch.h.DB13D78536AD07E7.idx b/.cache/clangd/index/stdlib-bsearch.h.DB13D78536AD07E7.idx new file mode 100644 index 0000000..de1e1d3 Binary files /dev/null and b/.cache/clangd/index/stdlib-bsearch.h.DB13D78536AD07E7.idx differ diff --git a/.cache/clangd/index/stdlib-float.h.126E8959FAE2A511.idx b/.cache/clangd/index/stdlib-float.h.126E8959FAE2A511.idx new file mode 100644 index 0000000..bcfa227 Binary files /dev/null and b/.cache/clangd/index/stdlib-float.h.126E8959FAE2A511.idx differ diff --git a/.cache/clangd/index/stdlib.h.39B90B9BAAF6E5B6.idx b/.cache/clangd/index/stdlib.h.39B90B9BAAF6E5B6.idx new file mode 100644 index 0000000..be3c6ab Binary files /dev/null and b/.cache/clangd/index/stdlib.h.39B90B9BAAF6E5B6.idx differ diff --git a/.cache/clangd/index/stdlib.h.61AA7B9EED10C430.idx b/.cache/clangd/index/stdlib.h.61AA7B9EED10C430.idx new file mode 100644 index 0000000..fd69b06 Binary files /dev/null and b/.cache/clangd/index/stdlib.h.61AA7B9EED10C430.idx differ diff --git a/.cache/clangd/index/stl_algo.h.DB262F706A603AED.idx b/.cache/clangd/index/stl_algo.h.DB262F706A603AED.idx new file mode 100644 index 0000000..bd4ebea Binary files /dev/null and b/.cache/clangd/index/stl_algo.h.DB262F706A603AED.idx differ diff --git a/.cache/clangd/index/stl_algobase.h.6FE4CA840D38F5DF.idx b/.cache/clangd/index/stl_algobase.h.6FE4CA840D38F5DF.idx new file mode 100644 index 0000000..b1c10a4 Binary files /dev/null and b/.cache/clangd/index/stl_algobase.h.6FE4CA840D38F5DF.idx differ diff --git a/.cache/clangd/index/stl_bvector.h.A9A8B31AD6B3F68F.idx b/.cache/clangd/index/stl_bvector.h.A9A8B31AD6B3F68F.idx new file mode 100644 index 0000000..04eb7cf Binary files /dev/null and b/.cache/clangd/index/stl_bvector.h.A9A8B31AD6B3F68F.idx differ diff --git a/.cache/clangd/index/stl_construct.h.EE9D315D5FF3515E.idx b/.cache/clangd/index/stl_construct.h.EE9D315D5FF3515E.idx new file mode 100644 index 0000000..8a505b2 Binary files /dev/null and b/.cache/clangd/index/stl_construct.h.EE9D315D5FF3515E.idx differ diff --git a/.cache/clangd/index/stl_function.h.B80B3FDFDD7580C8.idx b/.cache/clangd/index/stl_function.h.B80B3FDFDD7580C8.idx new file mode 100644 index 0000000..f55ce59 Binary files /dev/null and b/.cache/clangd/index/stl_function.h.B80B3FDFDD7580C8.idx differ diff --git a/.cache/clangd/index/stl_heap.h.4815C4DEF9C35140.idx b/.cache/clangd/index/stl_heap.h.4815C4DEF9C35140.idx new file mode 100644 index 0000000..848c486 Binary files /dev/null and b/.cache/clangd/index/stl_heap.h.4815C4DEF9C35140.idx differ diff --git a/.cache/clangd/index/stl_iterator.h.F9207AA4F649CADD.idx b/.cache/clangd/index/stl_iterator.h.F9207AA4F649CADD.idx new file mode 100644 index 0000000..0671446 Binary files /dev/null and b/.cache/clangd/index/stl_iterator.h.F9207AA4F649CADD.idx differ diff --git a/.cache/clangd/index/stl_iterator_base_funcs.h.1C1C67E1C799B046.idx b/.cache/clangd/index/stl_iterator_base_funcs.h.1C1C67E1C799B046.idx new file mode 100644 index 0000000..7167805 Binary files /dev/null and b/.cache/clangd/index/stl_iterator_base_funcs.h.1C1C67E1C799B046.idx differ diff --git a/.cache/clangd/index/stl_iterator_base_types.h.F3516407D28BEC1C.idx b/.cache/clangd/index/stl_iterator_base_types.h.F3516407D28BEC1C.idx new file mode 100644 index 0000000..b65deaa Binary files /dev/null and b/.cache/clangd/index/stl_iterator_base_types.h.F3516407D28BEC1C.idx differ diff --git a/.cache/clangd/index/stl_pair.h.D3AE94A94F2E2ABF.idx b/.cache/clangd/index/stl_pair.h.D3AE94A94F2E2ABF.idx new file mode 100644 index 0000000..c63f9af Binary files /dev/null and b/.cache/clangd/index/stl_pair.h.D3AE94A94F2E2ABF.idx differ diff --git a/.cache/clangd/index/stl_relops.h.4EDE923E28099422.idx b/.cache/clangd/index/stl_relops.h.4EDE923E28099422.idx new file mode 100644 index 0000000..c828523 Binary files /dev/null and b/.cache/clangd/index/stl_relops.h.4EDE923E28099422.idx differ diff --git a/.cache/clangd/index/stl_tempbuf.h.B2C1604FBA3C3207.idx b/.cache/clangd/index/stl_tempbuf.h.B2C1604FBA3C3207.idx new file mode 100644 index 0000000..cbc6915 Binary files /dev/null and b/.cache/clangd/index/stl_tempbuf.h.B2C1604FBA3C3207.idx differ diff --git a/.cache/clangd/index/stl_uninitialized.h.A4A4B029E0E852D7.idx b/.cache/clangd/index/stl_uninitialized.h.A4A4B029E0E852D7.idx new file mode 100644 index 0000000..7bbad2a Binary files /dev/null and b/.cache/clangd/index/stl_uninitialized.h.A4A4B029E0E852D7.idx differ diff --git a/.cache/clangd/index/stl_vector.h.4EC9031238D69F4D.idx b/.cache/clangd/index/stl_vector.h.4EC9031238D69F4D.idx new file mode 100644 index 0000000..a05db4a Binary files /dev/null and b/.cache/clangd/index/stl_vector.h.4EC9031238D69F4D.idx differ diff --git a/.cache/clangd/index/stream.h.BE9D85F504BD8828.idx b/.cache/clangd/index/stream.h.BE9D85F504BD8828.idx new file mode 100644 index 0000000..4a60ba9 Binary files /dev/null and b/.cache/clangd/index/stream.h.BE9D85F504BD8828.idx differ diff --git a/.cache/clangd/index/stream_iterator.h.9BD05123E59ED540.idx b/.cache/clangd/index/stream_iterator.h.9BD05123E59ED540.idx new file mode 100644 index 0000000..2c961e5 Binary files /dev/null and b/.cache/clangd/index/stream_iterator.h.9BD05123E59ED540.idx differ diff --git a/.cache/clangd/index/streambuf.796E555F88DC7EA2.idx b/.cache/clangd/index/streambuf.796E555F88DC7EA2.idx new file mode 100644 index 0000000..2f40dd4 Binary files /dev/null and b/.cache/clangd/index/streambuf.796E555F88DC7EA2.idx differ diff --git a/.cache/clangd/index/streambuf.tcc.56E9E72F1AEA0C1B.idx b/.cache/clangd/index/streambuf.tcc.56E9E72F1AEA0C1B.idx new file mode 100644 index 0000000..8b72149 Binary files /dev/null and b/.cache/clangd/index/streambuf.tcc.56E9E72F1AEA0C1B.idx differ diff --git a/.cache/clangd/index/streambuf_iterator.h.D777A3E7EF441F40.idx b/.cache/clangd/index/streambuf_iterator.h.D777A3E7EF441F40.idx new file mode 100644 index 0000000..94683f2 Binary files /dev/null and b/.cache/clangd/index/streambuf_iterator.h.D777A3E7EF441F40.idx differ diff --git a/.cache/clangd/index/string.493A6CA6DE0A9FC0.idx b/.cache/clangd/index/string.493A6CA6DE0A9FC0.idx new file mode 100644 index 0000000..c587ff1 Binary files /dev/null and b/.cache/clangd/index/string.493A6CA6DE0A9FC0.idx differ diff --git a/.cache/clangd/index/string.h.979B97B48AB27554.idx b/.cache/clangd/index/string.h.979B97B48AB27554.idx new file mode 100644 index 0000000..029ae99 Binary files /dev/null and b/.cache/clangd/index/string.h.979B97B48AB27554.idx differ diff --git a/.cache/clangd/index/string_conversions.h.9834562197A60437.idx b/.cache/clangd/index/string_conversions.h.9834562197A60437.idx new file mode 100644 index 0000000..78a8859 Binary files /dev/null and b/.cache/clangd/index/string_conversions.h.9834562197A60437.idx differ diff --git a/.cache/clangd/index/stringfwd.h.1533271C8F2E419B.idx b/.cache/clangd/index/stringfwd.h.1533271C8F2E419B.idx new file mode 100644 index 0000000..0f28551 Binary files /dev/null and b/.cache/clangd/index/stringfwd.h.1533271C8F2E419B.idx differ diff --git a/.cache/clangd/index/strings.h.9EB46A34EB89BB5D.idx b/.cache/clangd/index/strings.h.9EB46A34EB89BB5D.idx new file mode 100644 index 0000000..f9b1d37 Binary files /dev/null and b/.cache/clangd/index/strings.h.9EB46A34EB89BB5D.idx differ diff --git a/.cache/clangd/index/struct_FILE.h.2B6C435DE91D3DF8.idx b/.cache/clangd/index/struct_FILE.h.2B6C435DE91D3DF8.idx new file mode 100644 index 0000000..86c7653 Binary files /dev/null and b/.cache/clangd/index/struct_FILE.h.2B6C435DE91D3DF8.idx differ diff --git a/.cache/clangd/index/struct_iovec.h.2D76C8EFEB1B5ED0.idx b/.cache/clangd/index/struct_iovec.h.2D76C8EFEB1B5ED0.idx new file mode 100644 index 0000000..7eaecac Binary files /dev/null and b/.cache/clangd/index/struct_iovec.h.2D76C8EFEB1B5ED0.idx differ diff --git a/.cache/clangd/index/struct_itimerspec.h.7F2AA6FE7224B30F.idx b/.cache/clangd/index/struct_itimerspec.h.7F2AA6FE7224B30F.idx new file mode 100644 index 0000000..1c8422d Binary files /dev/null and b/.cache/clangd/index/struct_itimerspec.h.7F2AA6FE7224B30F.idx differ diff --git a/.cache/clangd/index/struct_mutex.h.A62EF32C01DB3505.idx b/.cache/clangd/index/struct_mutex.h.A62EF32C01DB3505.idx new file mode 100644 index 0000000..98de81c Binary files /dev/null and b/.cache/clangd/index/struct_mutex.h.A62EF32C01DB3505.idx differ diff --git a/.cache/clangd/index/struct_osockaddr.h.C6204468B73866A2.idx b/.cache/clangd/index/struct_osockaddr.h.C6204468B73866A2.idx new file mode 100644 index 0000000..cd5f3a9 Binary files /dev/null and b/.cache/clangd/index/struct_osockaddr.h.C6204468B73866A2.idx differ diff --git a/.cache/clangd/index/struct_rwlock.h.76DF445C3AA414C9.idx b/.cache/clangd/index/struct_rwlock.h.76DF445C3AA414C9.idx new file mode 100644 index 0000000..00f63d0 Binary files /dev/null and b/.cache/clangd/index/struct_rwlock.h.76DF445C3AA414C9.idx differ diff --git a/.cache/clangd/index/struct_sched_param.h.B3F662015F7067C8.idx b/.cache/clangd/index/struct_sched_param.h.B3F662015F7067C8.idx new file mode 100644 index 0000000..e49fb0a Binary files /dev/null and b/.cache/clangd/index/struct_sched_param.h.B3F662015F7067C8.idx differ diff --git a/.cache/clangd/index/struct_sigstack.h.D78FCFB82EAA8868.idx b/.cache/clangd/index/struct_sigstack.h.D78FCFB82EAA8868.idx new file mode 100644 index 0000000..aed7f4d Binary files /dev/null and b/.cache/clangd/index/struct_sigstack.h.D78FCFB82EAA8868.idx differ diff --git a/.cache/clangd/index/struct_statx.h.AE853018DD65C8CE.idx b/.cache/clangd/index/struct_statx.h.AE853018DD65C8CE.idx new file mode 100644 index 0000000..a761301 Binary files /dev/null and b/.cache/clangd/index/struct_statx.h.AE853018DD65C8CE.idx differ diff --git a/.cache/clangd/index/struct_statx_timestamp.h.FE8DDCE5CF08343F.idx b/.cache/clangd/index/struct_statx_timestamp.h.FE8DDCE5CF08343F.idx new file mode 100644 index 0000000..20dde56 Binary files /dev/null and b/.cache/clangd/index/struct_statx_timestamp.h.FE8DDCE5CF08343F.idx differ diff --git a/.cache/clangd/index/struct_timespec.h.0E09B6677A8D6A08.idx b/.cache/clangd/index/struct_timespec.h.0E09B6677A8D6A08.idx new file mode 100644 index 0000000..cd7684e Binary files /dev/null and b/.cache/clangd/index/struct_timespec.h.0E09B6677A8D6A08.idx differ diff --git a/.cache/clangd/index/struct_timeval.h.4A17C4CCB9EE5883.idx b/.cache/clangd/index/struct_timeval.h.4A17C4CCB9EE5883.idx new file mode 100644 index 0000000..e661e03 Binary files /dev/null and b/.cache/clangd/index/struct_timeval.h.4A17C4CCB9EE5883.idx differ diff --git a/.cache/clangd/index/struct_tm.h.472DDF26D5924DB0.idx b/.cache/clangd/index/struct_tm.h.472DDF26D5924DB0.idx new file mode 100644 index 0000000..bf1eef3 Binary files /dev/null and b/.cache/clangd/index/struct_tm.h.472DDF26D5924DB0.idx differ diff --git a/.cache/clangd/index/stubs-64.h.C743FD695F310B00.idx b/.cache/clangd/index/stubs-64.h.C743FD695F310B00.idx new file mode 100644 index 0000000..5e7f7bd Binary files /dev/null and b/.cache/clangd/index/stubs-64.h.C743FD695F310B00.idx differ diff --git a/.cache/clangd/index/stubs.h.07D09A29AEBA877C.idx b/.cache/clangd/index/stubs.h.07D09A29AEBA877C.idx new file mode 100644 index 0000000..ca6ae11 Binary files /dev/null and b/.cache/clangd/index/stubs.h.07D09A29AEBA877C.idx differ diff --git a/.cache/clangd/index/symhacks.h.2159789663E8F86A.idx b/.cache/clangd/index/symhacks.h.2159789663E8F86A.idx new file mode 100644 index 0000000..a8d1687 Binary files /dev/null and b/.cache/clangd/index/symhacks.h.2159789663E8F86A.idx differ diff --git a/.cache/clangd/index/sys_errlist.h.D7C55CF42F24A7DC.idx b/.cache/clangd/index/sys_errlist.h.D7C55CF42F24A7DC.idx new file mode 100644 index 0000000..60286f4 Binary files /dev/null and b/.cache/clangd/index/sys_errlist.h.D7C55CF42F24A7DC.idx differ diff --git a/.cache/clangd/index/system_error.2EB671682CAAC040.idx b/.cache/clangd/index/system_error.2EB671682CAAC040.idx new file mode 100644 index 0000000..02fc03a Binary files /dev/null and b/.cache/clangd/index/system_error.2EB671682CAAC040.idx differ diff --git a/.cache/clangd/index/tcp.h.A1670E7CD2FF515F.idx b/.cache/clangd/index/tcp.h.A1670E7CD2FF515F.idx new file mode 100644 index 0000000..4ec9d3d Binary files /dev/null and b/.cache/clangd/index/tcp.h.A1670E7CD2FF515F.idx differ diff --git a/.cache/clangd/index/thread-shared-types.h.D988ADBA7666A8DD.idx b/.cache/clangd/index/thread-shared-types.h.D988ADBA7666A8DD.idx new file mode 100644 index 0000000..d426907 Binary files /dev/null and b/.cache/clangd/index/thread-shared-types.h.D988ADBA7666A8DD.idx differ diff --git a/.cache/clangd/index/threads.h.D9399FC99CF54D69.idx b/.cache/clangd/index/threads.h.D9399FC99CF54D69.idx new file mode 100644 index 0000000..7c4e4c8 Binary files /dev/null and b/.cache/clangd/index/threads.h.D9399FC99CF54D69.idx differ diff --git a/.cache/clangd/index/time.h.1407763DE90304EB.idx b/.cache/clangd/index/time.h.1407763DE90304EB.idx new file mode 100644 index 0000000..378e57d Binary files /dev/null and b/.cache/clangd/index/time.h.1407763DE90304EB.idx differ diff --git a/.cache/clangd/index/time.h.E3714B647728C2FE.idx b/.cache/clangd/index/time.h.E3714B647728C2FE.idx new file mode 100644 index 0000000..f52b684 Binary files /dev/null and b/.cache/clangd/index/time.h.E3714B647728C2FE.idx differ diff --git a/.cache/clangd/index/time.h.EDC06F0AFB6EC7EC.idx b/.cache/clangd/index/time.h.EDC06F0AFB6EC7EC.idx new file mode 100644 index 0000000..533ec26 Binary files /dev/null and b/.cache/clangd/index/time.h.EDC06F0AFB6EC7EC.idx differ diff --git a/.cache/clangd/index/time64.h.1807D6F5124100A2.idx b/.cache/clangd/index/time64.h.1807D6F5124100A2.idx new file mode 100644 index 0000000..e385dbf Binary files /dev/null and b/.cache/clangd/index/time64.h.1807D6F5124100A2.idx differ diff --git a/.cache/clangd/index/time_t.h.CAA645D29A7A0EF9.idx b/.cache/clangd/index/time_t.h.CAA645D29A7A0EF9.idx new file mode 100644 index 0000000..1803d12 Binary files /dev/null and b/.cache/clangd/index/time_t.h.CAA645D29A7A0EF9.idx differ diff --git a/.cache/clangd/index/timer_t.h.3EFB88CBA2B5B801.idx b/.cache/clangd/index/timer_t.h.3EFB88CBA2B5B801.idx new file mode 100644 index 0000000..5de8fe7 Binary files /dev/null and b/.cache/clangd/index/timer_t.h.3EFB88CBA2B5B801.idx differ diff --git a/.cache/clangd/index/timesize.h.44719E86E09400E4.idx b/.cache/clangd/index/timesize.h.44719E86E09400E4.idx new file mode 100644 index 0000000..84be13f Binary files /dev/null and b/.cache/clangd/index/timesize.h.44719E86E09400E4.idx differ diff --git a/.cache/clangd/index/timex.h.F2D36E17192C32F0.idx b/.cache/clangd/index/timex.h.F2D36E17192C32F0.idx new file mode 100644 index 0000000..12d3416 Binary files /dev/null and b/.cache/clangd/index/timex.h.F2D36E17192C32F0.idx differ diff --git a/.cache/clangd/index/ttydefaults.h.C98530E4C2DE508E.idx b/.cache/clangd/index/ttydefaults.h.C98530E4C2DE508E.idx new file mode 100644 index 0000000..7aa4a1e Binary files /dev/null and b/.cache/clangd/index/ttydefaults.h.C98530E4C2DE508E.idx differ diff --git a/.cache/clangd/index/type_traits.4787C5B2A7C5E7F9.idx b/.cache/clangd/index/type_traits.4787C5B2A7C5E7F9.idx new file mode 100644 index 0000000..f9794bf Binary files /dev/null and b/.cache/clangd/index/type_traits.4787C5B2A7C5E7F9.idx differ diff --git a/.cache/clangd/index/type_traits.h.1F5E7C37817A1B77.idx b/.cache/clangd/index/type_traits.h.1F5E7C37817A1B77.idx new file mode 100644 index 0000000..6836aed Binary files /dev/null and b/.cache/clangd/index/type_traits.h.1F5E7C37817A1B77.idx differ diff --git a/.cache/clangd/index/typeinfo.7CB690AB21E9DB5C.idx b/.cache/clangd/index/typeinfo.7CB690AB21E9DB5C.idx new file mode 100644 index 0000000..817fd69 Binary files /dev/null and b/.cache/clangd/index/typeinfo.7CB690AB21E9DB5C.idx differ diff --git a/.cache/clangd/index/types.h.14B4CD5C32014264.idx b/.cache/clangd/index/types.h.14B4CD5C32014264.idx new file mode 100644 index 0000000..7f2be4e Binary files /dev/null and b/.cache/clangd/index/types.h.14B4CD5C32014264.idx differ diff --git a/.cache/clangd/index/types.h.BB07396DFADEEB89.idx b/.cache/clangd/index/types.h.BB07396DFADEEB89.idx new file mode 100644 index 0000000..4804baf Binary files /dev/null and b/.cache/clangd/index/types.h.BB07396DFADEEB89.idx differ diff --git a/.cache/clangd/index/types.h.CAF4DC91124A326D.idx b/.cache/clangd/index/types.h.CAF4DC91124A326D.idx new file mode 100644 index 0000000..c323fc2 Binary files /dev/null and b/.cache/clangd/index/types.h.CAF4DC91124A326D.idx differ diff --git a/.cache/clangd/index/types.h.E5A59583DBE28918.idx b/.cache/clangd/index/types.h.E5A59583DBE28918.idx new file mode 100644 index 0000000..f9a8cc6 Binary files /dev/null and b/.cache/clangd/index/types.h.E5A59583DBE28918.idx differ diff --git a/.cache/clangd/index/types.h.E5E7FE6DA0F78AF5.idx b/.cache/clangd/index/types.h.E5E7FE6DA0F78AF5.idx new file mode 100644 index 0000000..d3500ed Binary files /dev/null and b/.cache/clangd/index/types.h.E5E7FE6DA0F78AF5.idx differ diff --git a/.cache/clangd/index/typesizes.h.A6E116B64E63B90B.idx b/.cache/clangd/index/typesizes.h.A6E116B64E63B90B.idx new file mode 100644 index 0000000..8bffbfc Binary files /dev/null and b/.cache/clangd/index/typesizes.h.A6E116B64E63B90B.idx differ diff --git a/.cache/clangd/index/ucontext.h.8AAC2548CD8DD4A8.idx b/.cache/clangd/index/ucontext.h.8AAC2548CD8DD4A8.idx new file mode 100644 index 0000000..0afe1cc Binary files /dev/null and b/.cache/clangd/index/ucontext.h.8AAC2548CD8DD4A8.idx differ diff --git a/.cache/clangd/index/uintn-identity.h.CB86545C6FF94BB3.idx b/.cache/clangd/index/uintn-identity.h.CB86545C6FF94BB3.idx new file mode 100644 index 0000000..f8df791 Binary files /dev/null and b/.cache/clangd/index/uintn-identity.h.CB86545C6FF94BB3.idx differ diff --git a/.cache/clangd/index/uio_lim.h.DF584C6AE54BEA07.idx b/.cache/clangd/index/uio_lim.h.DF584C6AE54BEA07.idx new file mode 100644 index 0000000..9cb6622 Binary files /dev/null and b/.cache/clangd/index/uio_lim.h.DF584C6AE54BEA07.idx differ diff --git a/.cache/clangd/index/uniform_int_dist.h.2D8C49070F411E2E.idx b/.cache/clangd/index/uniform_int_dist.h.2D8C49070F411E2E.idx new file mode 100644 index 0000000..2be0645 Binary files /dev/null and b/.cache/clangd/index/uniform_int_dist.h.2D8C49070F411E2E.idx differ diff --git a/.cache/clangd/index/unistd.h.4957E2CBEF0B97CE.idx b/.cache/clangd/index/unistd.h.4957E2CBEF0B97CE.idx new file mode 100644 index 0000000..71eefc9 Binary files /dev/null and b/.cache/clangd/index/unistd.h.4957E2CBEF0B97CE.idx differ diff --git a/.cache/clangd/index/unistd_ext.h.19E02BEC0B72F7D2.idx b/.cache/clangd/index/unistd_ext.h.19E02BEC0B72F7D2.idx new file mode 100644 index 0000000..ac863f0 Binary files /dev/null and b/.cache/clangd/index/unistd_ext.h.19E02BEC0B72F7D2.idx differ diff --git a/.cache/clangd/index/utility.2DAD4C3AC7570013.idx b/.cache/clangd/index/utility.2DAD4C3AC7570013.idx new file mode 100644 index 0000000..6c85e32 Binary files /dev/null and b/.cache/clangd/index/utility.2DAD4C3AC7570013.idx differ diff --git a/.cache/clangd/index/utils.c.E62DF16C7C0F9F4A.idx b/.cache/clangd/index/utils.c.E62DF16C7C0F9F4A.idx new file mode 100644 index 0000000..0d1f575 Binary files /dev/null and b/.cache/clangd/index/utils.c.E62DF16C7C0F9F4A.idx differ diff --git a/.cache/clangd/index/utils.h.6C2EB8E813832A47.idx b/.cache/clangd/index/utils.h.6C2EB8E813832A47.idx new file mode 100644 index 0000000..d5de1d8 Binary files /dev/null and b/.cache/clangd/index/utils.h.6C2EB8E813832A47.idx differ diff --git a/.cache/clangd/index/utsname.h.266197958A4223DA.idx b/.cache/clangd/index/utsname.h.266197958A4223DA.idx new file mode 100644 index 0000000..4e4e011 Binary files /dev/null and b/.cache/clangd/index/utsname.h.266197958A4223DA.idx differ diff --git a/.cache/clangd/index/utsname.h.5A3B7E41D51C93B0.idx b/.cache/clangd/index/utsname.h.5A3B7E41D51C93B0.idx new file mode 100644 index 0000000..cdbbdfd Binary files /dev/null and b/.cache/clangd/index/utsname.h.5A3B7E41D51C93B0.idx differ diff --git a/.cache/clangd/index/uxplay.cpp.11F7D92933280A4E.idx b/.cache/clangd/index/uxplay.cpp.11F7D92933280A4E.idx new file mode 100644 index 0000000..488a3a2 Binary files /dev/null and b/.cache/clangd/index/uxplay.cpp.11F7D92933280A4E.idx differ diff --git a/.cache/clangd/index/vector.E76A692663F90BF2.idx b/.cache/clangd/index/vector.E76A692663F90BF2.idx new file mode 100644 index 0000000..6aa5a0b Binary files /dev/null and b/.cache/clangd/index/vector.E76A692663F90BF2.idx differ diff --git a/.cache/clangd/index/vector.tcc.7230A8E4487B3DDF.idx b/.cache/clangd/index/vector.tcc.7230A8E4487B3DDF.idx new file mode 100644 index 0000000..d3aaed9 Binary files /dev/null and b/.cache/clangd/index/vector.tcc.7230A8E4487B3DDF.idx differ diff --git a/.cache/clangd/index/video_renderer.c.1BFDA4181DB5BC75.idx b/.cache/clangd/index/video_renderer.c.1BFDA4181DB5BC75.idx new file mode 100644 index 0000000..be608a3 Binary files /dev/null and b/.cache/clangd/index/video_renderer.c.1BFDA4181DB5BC75.idx differ diff --git a/.cache/clangd/index/video_renderer.h.ABDBE8D7CC606FF3.idx b/.cache/clangd/index/video_renderer.h.ABDBE8D7CC606FF3.idx new file mode 100644 index 0000000..503046a Binary files /dev/null and b/.cache/clangd/index/video_renderer.h.ABDBE8D7CC606FF3.idx differ diff --git a/.cache/clangd/index/wait.h.BD157266BD1E3B6E.idx b/.cache/clangd/index/wait.h.BD157266BD1E3B6E.idx new file mode 100644 index 0000000..cd7c9b3 Binary files /dev/null and b/.cache/clangd/index/wait.h.BD157266BD1E3B6E.idx differ diff --git a/.cache/clangd/index/waitflags.h.8046C5530CD87302.idx b/.cache/clangd/index/waitflags.h.8046C5530CD87302.idx new file mode 100644 index 0000000..20f469e Binary files /dev/null and b/.cache/clangd/index/waitflags.h.8046C5530CD87302.idx differ diff --git a/.cache/clangd/index/waitstatus.h.D7C4F06852D51050.idx b/.cache/clangd/index/waitstatus.h.D7C4F06852D51050.idx new file mode 100644 index 0000000..2e70a90 Binary files /dev/null and b/.cache/clangd/index/waitstatus.h.D7C4F06852D51050.idx differ diff --git a/.cache/clangd/index/wchar.h.27C0562D93BBAA12.idx b/.cache/clangd/index/wchar.h.27C0562D93BBAA12.idx new file mode 100644 index 0000000..aabc49c Binary files /dev/null and b/.cache/clangd/index/wchar.h.27C0562D93BBAA12.idx differ diff --git a/.cache/clangd/index/wchar.h.83BD995621576773.idx b/.cache/clangd/index/wchar.h.83BD995621576773.idx new file mode 100644 index 0000000..4d87622 Binary files /dev/null and b/.cache/clangd/index/wchar.h.83BD995621576773.idx differ diff --git a/.cache/clangd/index/wctype-wchar.h.D8CFC0DAB89A1342.idx b/.cache/clangd/index/wctype-wchar.h.D8CFC0DAB89A1342.idx new file mode 100644 index 0000000..eb9666c Binary files /dev/null and b/.cache/clangd/index/wctype-wchar.h.D8CFC0DAB89A1342.idx differ diff --git a/.cache/clangd/index/wctype.h.CC12045E6059335C.idx b/.cache/clangd/index/wctype.h.CC12045E6059335C.idx new file mode 100644 index 0000000..9b9d84a Binary files /dev/null and b/.cache/clangd/index/wctype.h.CC12045E6059335C.idx differ diff --git a/.cache/clangd/index/wint_t.h.DAC5928CF0BA7895.idx b/.cache/clangd/index/wint_t.h.DAC5928CF0BA7895.idx new file mode 100644 index 0000000..c0eb7f0 Binary files /dev/null and b/.cache/clangd/index/wint_t.h.DAC5928CF0BA7895.idx differ diff --git a/.cache/clangd/index/wordsize.h.23D8030BEC81E6F1.idx b/.cache/clangd/index/wordsize.h.23D8030BEC81E6F1.idx new file mode 100644 index 0000000..7adf2a5 Binary files /dev/null and b/.cache/clangd/index/wordsize.h.23D8030BEC81E6F1.idx differ diff --git a/.cache/clangd/index/x509.h.2C2780FC83DC5826.idx b/.cache/clangd/index/x509.h.2C2780FC83DC5826.idx new file mode 100644 index 0000000..1fdc5e2 Binary files /dev/null and b/.cache/clangd/index/x509.h.2C2780FC83DC5826.idx differ diff --git a/.cache/clangd/index/x509_vfy.h.64C86E854CAB8C8D.idx b/.cache/clangd/index/x509_vfy.h.64C86E854CAB8C8D.idx new file mode 100644 index 0000000..1c2a3fc Binary files /dev/null and b/.cache/clangd/index/x509_vfy.h.64C86E854CAB8C8D.idx differ diff --git a/.cache/clangd/index/x509err.h.BEACD87877508E5C.idx b/.cache/clangd/index/x509err.h.BEACD87877508E5C.idx new file mode 100644 index 0000000..89171c3 Binary files /dev/null and b/.cache/clangd/index/x509err.h.BEACD87877508E5C.idx differ diff --git a/.cache/clangd/index/xopen_lim.h.E4E4BC8C11167D14.idx b/.cache/clangd/index/xopen_lim.h.E4E4BC8C11167D14.idx new file mode 100644 index 0000000..9a05b97 Binary files /dev/null and b/.cache/clangd/index/xopen_lim.h.E4E4BC8C11167D14.idx differ diff --git a/.gitignore b/.gitignore index e257658..34b8173 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ *.out *.app +build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..03cd02d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +if ( APPLE ) + cmake_minimum_required( VERSION 3.13 ) +else () + cmake_minimum_required( VERSION 3.10 ) +endif () + +project( uxplay ) + +message( STATUS "Project name: " ${PROJECT_NAME} ) + +include(GNUInstallDirs) + +set ( CMAKE_CXX_STANDARD 11 ) + +if (ZOOMFIX ) + message (STATUS "cmake option ZOOMFIX is no longer used (if needed, ZOOMFIX is automatically applied if X11 libraries are present)" ) +endif() + +if ( ( UNIX AND NOT APPLE ) OR USE_X11 ) + if ( NOT NO_X11_DEPS ) + find_package( X11 ) + if ( X11_FOUND ) + message (STATUS "Will compile using X11 Libraries (use cmake option -DNO_X11_DEPS=ON if X11 dependence is not wanted)" ) + link_libraries( ${X11_LIBRARIES} ) + include_directories( ${X11_INCLUDE_DIR} ) + else () + message (STATUS "X11 libraries not found, will compile without X11 dependence" ) + endif () + else() + message (STATUS "will compile without X11 dependence" ) + endif() +endif() + +if( UNIX AND NOT APPLE ) + add_definitions( -DSUPPRESS_AVAHI_COMPAT_WARNING ) +else() + set( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE ) +endif() + +add_subdirectory( lib/llhttp ) +add_subdirectory( lib/playfair ) +add_subdirectory( lib ) +add_subdirectory( renderers ) + +if ( GST_MACOS ) + add_definitions( -DGST_MACOS ) + message ( STATUS "define GST_MACOS" ) +endif() + +add_executable( uxplay uxplay.cpp ) +target_link_libraries( uxplay + renderers + airplay + ) + +install( TARGETS uxplay RUNTIME DESTINATION bin ) +install( FILES uxplay.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 ) +install( FILES README.md README.txt README.html LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR} ) +install( FILES lib/llhttp/LICENSE-MIT DESTINATION ${CMAKE_INSTALL_DOCDIR}/llhttp ) + +# uninstall target +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.html b/README.html new file mode 100644 index 0000000..48a88d4 --- /dev/null +++ b/README.html @@ -0,0 +1,1875 @@ +

UxPlay +1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix +(now also runs on Windows).

+

Now +developed at the GitHub site https://github.com/FDH2/UxPlay (where ALL user issues +should be posted, and latest versions can be found).

+
    +
  • NEW in v1.71: Support for (YouTube) HLS (HTTP +Live Streaming) video with the new “-hls” option. Click on the +airplay icon in the YouTube app to stream video. (You may need to wait +until advertisements have finished or been skipped before clicking the +YouTube airplay icon.) Please report any issues with this new +feature of UxPlay.
  • +
+

Highlights:

+
    +
  • GPLv3, open source.
  • +
  • Originally supported only AirPlay Mirror protocol, now has added +support for AirPlay Audio-only (Apple Lossless ALAC) streaming from +current iOS/iPadOS clients. Now with support for Airplay HLS +video-streaming (currently only YouTube video).
  • +
  • macOS computers (2011 or later, both Intel and “Apple Silicon” M1/M2 +systems) can act either as AirPlay clients, or as the server running +UxPlay. Using AirPlay, UxPlay can emulate a second display for macOS +clients.
  • +
  • Support for older iOS clients (such as 32-bit iPad 2nd gen., iPod +Touch 5th gen. and iPhone 4S, when upgraded to iOS 9.3.5, or later +64-bit devices), plus a Windows AirPlay-client emulator, AirMyPC.
  • +
  • Uses GStreamer plugins for audio and video rendering (with options +to select different hardware-appropriate output “videosinks” and +“audiosinks”, and a fully-user-configurable video streaming +pipeline).
  • +
  • Support for server behind a firewall.
  • +
  • Raspberry Pi support both with and without hardware video +decoding by the Broadcom GPU. Tested on Raspberry Pi Zero 2 +W, 3 Model B+, 4 Model B, and 5.
  • +
  • Support for running on Microsoft Windows (builds with the MinGW-64 +compiler in the unix-like MSYS2 environment).
  • +
+

Note: AirPlay2 multi-room audio streaming is not supported: use shairport-sync +for that.

+

Packaging status +(Linux and *BSD distributions)

+

.

+
    +
  • Install uxplay on Debian-based Linux systems with +“sudo apt install uxplay”; on FreeBSD with +“sudo pkg install uxplay”. Also available on Arch-based +systems through AUR. Since v. 1.66, uxplay is now also packaged in RPM +format by Fedora 38 (“sudo dnf install uxplay”).

  • +
  • For other RPM-based distributions which have not yet packaged +UxPlay, a RPM “specfile” uxplay.spec is now provided +with recent releases (see their +“Assets”), and can also be found in the UxPlay source top directory. See +the section on using this specfile for building an installable RPM +package.

  • +
+

After installation:

+
    +
  • (On Linux and *BSD): if a firewall is active on the server +hosting UxPlay, make sure the default network port (UDP 5353) for +mDNS/DNS-SD queries is open (see Troubleshooting below for more details); +also open three UDP and three TCP ports for Uxplay, and use the “uxplay +-p ” option (see “man uxplay” or +“uxplay -h”).

  • +
  • Even if you install your distribution’s pre-compiled uxplay +binary package, you may need to read the instructions below for running UxPlay to see which of your +distribution’s GStreamer plugin packages you should +also install.

  • +
  • For Audio-only mode (Apple Music, etc.) best quality is obtained +with the option “uxplay -async”, but there is then a 2 second latency +imposed by iOS.

  • +
  • Add any UxPlay options you want to use as defaults to a startup +file ~/.uxplayrc (see “man uxplay” or +“uxplay -h” for format and other possible locations). In +particular, if your system uses PipeWire audio or Wayland video systems, +you may wish to add “as pipewiresink” or “vs waylandsink” as defaults to +the file. (Output from terminal commands “ps waux | grep pulse” or +“pactl info” will contain “pipewire” if your Linux/BSD system uses +it).

  • +
  • On Raspberry Pi: models using hardware h264 video decoding by the +Broadcom GPU (models 4B and earlier) may require the uxplay option +-bt709. If you use Ubuntu 22.10 or earlier, GStreamer must be patched +to use hardware video decoding by the Broadcom GPU (also recommended but +optional for Raspberry Pi OS (Bullseye): the patched GStreamer does not +need option ” -bt709`“. The need for -bt709 when hardware video decoding +is used seems to have reappeared starting with GStreamer-1.22.

  • +
+

To (easily) compile the latest UxPlay from source, see the section Getting UxPlay.

+

Detailed description of +UxPlay

+

This project is a GPLv3 open source unix AirPlay2 Mirror server for +Linux, macOS, and *BSD. It was initially developed by antimof using code from +OpenMAX-based RPiPlay, +which in turn derives from AirplayServer, shairplay, and playfair. (The +antimof site is no longer involved in development, but periodically +posts updates pulled from the new main UxPlay site).

+

UxPlay is tested on a number of systems, including (among others) +Debian (10 “Buster”, 11 “Bullseye”, 12 “Bookworm”), Ubuntu (20.04 LTS, +22.04 LTS, 23.04 (also Ubuntu derivatives Linux Mint, Pop!_OS), Red Hat +and clones (Fedora 38, Rocky Linux 9.2), openSUSE Leap 15.5, Mageia 9, +OpenMandriva “ROME”, PCLinuxOS, Arch Linux, Manjaro, and should run on +any Linux system. Also tested on macOS Catalina and Ventura (Intel) and +Sonoma (M2), FreeBSD 14.0, Windows 10 and 11 (64 bit).

+

On Raspberry Pi 4 model B, it is tested on Raspberry Pi OS (Bullseye +and Bookworm) (32- and 64-bit), Ubuntu 22.04 LTS and 23.04, Manjaro RPi4 +23.02, and (without hardware video decoding) on openSUSE 15.5. Also +tested on Raspberry Pi Zero 2 W, 3 model B+, and now 5.

+

Its main use is to act like an AppleTV for screen-mirroring (with +audio) of iOS/iPadOS/macOS clients (iPhone, iPod Touch, iPad, Mac +computers) on the server display of a host running Linux, macOS, or +other unix (and now also Microsoft Windows). UxPlay supports Apple’s +AirPlay2 protocol using “Legacy Protocol”, but some features are +missing. (Details of what is publicly known about Apple’s AirPlay 2 +protocol can be found here, here +and here; see also +pyatv which +could be a resource for adding modern protocols.) While there is no +guarantee that future iOS releases will keep supporting “Legacy +Protocol”, iOS 17 continues support.

+

The UxPlay server and its client must be on the same local area +network, on which a Bonjour/Zeroconf mDNS/DNS-SD server +is also running (only DNS-SD “Service Discovery” service is strictly +necessary, it is not necessary that the local network also be of the +“.local” mDNS-based type). On Linux and BSD Unix servers, this is +usually provided by Avahi, through +the avahi-daemon service, and is included in most Linux distributions +(this service can also be provided by macOS, iOS or Windows +servers).

+

Connections to the UxPlay server by iOS/MacOS clients can be +initiated both in AirPlay Mirror mode (which streams +lossily-compressed AAC audio while mirroring the client screen, or in +the alternative AirPlay Audio mode which streams Apple +Lossless (ALAC) audio without screen mirroring. In +Audio mode, metadata is displayed in the uxplay +terminal; if UxPlay option -ca <name> is used, the +accompanying cover art is also output to a periodically-updated file +<name>, and can be viewed with a (reloading) graphics +viewer of your choice. Switching between +Mirror and Audio modes +during an active connection is possible: in Mirror +mode, stop mirroring (or close the mirror window) and start an +Audio mode connection, switch back by initiating +a Mirror mode connection; cover-art display +stops/restarts as you leave/re-enter Audio +mode.

+
    +
  • Note that Apple video-DRM (as found in “Apple TV app” +content on the client) cannot be decrypted by UxPlay, and the Apple TV +app cannot be watched using UxPlay’s AirPlay Mirror mode (only the +unprotected audio will be streamed, in AAC format).

  • +
  • With the new “-hls” option, UxPlay now also supports +non-Mirror AirPlay video streaming (where the client controls a web +server on the AirPlay server that directly receives HLS content to avoid +it being decoded and re-encoded by the client). This currently only +supports streaming of YouTube videos. Without the -hls option, using the +icon for AirPlay video in apps such as the YouTube app will only send +audio (in lossless ALAC format) without the accompanying +video.

  • +
+

Possibility +for using hardware-accelerated h264/h265 video-decoding, if +available.

+

UxPlay uses GStreamer +“plugins” for rendering audio and video. This means that video and audio +are supported “out of the box”, using a choice of plugins. AirPlay +streams video in h264 format: gstreamer decoding is plugin agnostic, and +uses accelerated GPU hardware h264 decoders if available; if not, +software decoding is used.

+
    +
  • VAAPI for Intel and AMD integrated graphics, NVIDIA with +“Nouveau” open-source driver

    +

    With an Intel or AMD GPU, hardware decoding with the open-source +VAAPI gstreamer plugin is preferable. The open-source “Nouveau” drivers +for NVIDIA graphics are also in principle supported: see here, +but this requires VAAPI to be supplemented with firmware extracted from +the proprietary NVIDIA drivers.

  • +
  • NVIDIA with proprietary drivers

    +

    The nvh264dec plugin (included in +gstreamer1.0-plugins-bad since GStreamer-1.18.0) can be used for +accelerated video decoding on the NVIDIA GPU after NVIDIA’s CUDA driver +libcuda.so is installed. For GStreamer-1.16.3 or earlier, +the plugin is called nvdec, and must be built +by the user.

  • +
  • Video4Linux2 support for h264 hardware decoding on +Raspberry Pi (Pi 4B and older)

    +

    Raspberry Pi (RPi) computers (tested on Pi 4 Model B) can now run +UxPlay using software video decoding, but hardware-accelerated h264/h265 +decoding by firmware in the Pi’s Broadcom 2835 GPU is prefered. UxPlay +accesses this using the GStreamer-1.22 Video4Linux2 (v4l2) plugin; Uses +the out-of-mainline Linux kernel module bcm2835-codec maintained by +Raspberry Pi, so far only included in Raspberry Pi OS, and two other +distributions (Ubuntu, Manjaro) available with Raspberry Pi Imager. +(For GStreamer < 1.22, see the UxPlay +Wiki). Pi model 5 has no support for hardware H264 decoding, as +its CPU is powerful enough for satisfactory software H264 +decoding

  • +
  • Support for h265 (HEVC) hardware decoding on Raspberry Pi +(Pi 4 model B and Pi 5)

    +

    These Raspberry Pi models have a dedicated HEVC decoding block (not +the GPU), with a driver “rpivid” which is not yet in the mainline Linux +kernel (but is planned to be there in future). Unfortunately it produces +decoded video in a non-standard pixel format (NC30 or “SAND”) which will +not be supported by GStreamer until the driver is in the mainline +kernel; without this support, UxPlay support for HEVC hardware decoding +on Raspberry Pi will not work.

  • +
+

Note to packagers:

+

UxPlay’s GPLv3 license does not have an added “GPL exception” +explicitly allowing it to be distributed in compiled form when linked to +OpenSSL versions prior to v. 3.0.0 (older versions of +OpenSSL have a license clause incompatible with the GPL unless OpenSSL +can be regarded as a “System Library”, which it is in *BSD). Many Linux +distributions treat OpenSSL as a “System Library”, but some +(e.g. Debian) do not: in this case, the issue is solved by linking with +OpenSSL-3.0.0 or later.

+

Getting UxPlay

+

Either download and unzip UxPlay-master.zip, +or (if git is installed): “git clone https://github.com/FDH2/UxPlay”. +You can also download a recent or earlier version listed in Releases.

+
    +
  • A recent UxPlay can also be found on the original antimof site; that original +project is inactive, but is usually kept current or almost-current with +the active UxPlay github +site (thank you antimof!).
  • +
+

Building UxPlay on Linux (or +*BSD):

+

Debian-based systems:

+

(Adapt these instructions for non-Debian-based Linuxes or *BSD; for +macOS, see specific instruction below). See Troubleshooting below for help with any +difficulties.

+

You need a C/C++ compiler (e.g. g++) with the standard development +libraries installed. Debian-based systems provide a package +“build-essential” for use in compiling software. You also need +pkg-config: if it is not found by “which pkg-config”, +install pkg-config or its work-alike replacement pkgconf. Also make sure +that cmake>=3.10 is installed: “sudo apt install cmake” +(add build-essential and pkg-config (or +pkgconf) to this if needed).

+

Make sure that your distribution provides OpenSSL 1.1.1 or later, and +libplist 2.0 or later. (This means Debian 10 “Buster” based systems +(e.g, Ubuntu 18.04) or newer; on Debian 10 systems “libplist” is an +older version, you need “libplist3”.) If it does not, you may need to +build and install these from source (see instructions at the end of this +README).

+

If you have a non-standard OpenSSL installation, you may need to set +the environment variable OPENSSL_ROOT_DIR (e.g. , +“export OPENSSL_ROOT_DIR=/usr/local/lib64” if that is where +it is installed). Similarly, for non-standard (or multiple) GStreamer +installations, set the environment variable GSTREAMER_ROOT_DIR to the +directory that contains the “…/gstreamer-1.0/” directory of the +gstreamer installation that UxPlay should use (if this is e.g. +“~/my_gstreamer/lib/gstreamer-1.0/”, set this location with +“export GSTREAMER_ROOT_DIR=$HOME/my_gstreamer/lib”).

+
    +
  • Most users will use the GStreamer supplied by their distribution, +but a few (in particular users of Raspberry Pi OS Lite Legacy (Buster) +on a Raspberry Pi model 4B who wish to stay on that unsupported Legacy +OS for compatibility with other apps) should instead build a newer +Gstreamer from source following these +instructions . Do this before building +UxPlay.
  • +
+

In a terminal window, change directories to the source directory of +the downloaded source code (“UxPlay-*”, “*” = “master” or the release +tag for zipfile downloads, “UxPlay” for “git clone” downloads), then +follow the instructions below:

+

Note: By default UxPlay will be built with +optimization for the computer it is built on; when this is not the case, +as when you are packaging for a distribution, use the cmake option +-DNO_MARCH_NATIVE=ON.

+

If you use X11 Windows on Linux or *BSD, and wish to toggle in/out of +fullscreen mode with a keypress (F11 or Alt_L+Enter) UxPlay needs to be +built with a dependence on X11. Starting with UxPlay-1.59, this will be +done by default IF the X11 development libraries are +installed and detected. Install these with +“sudo apt install libx11-dev”. If GStreamer < 1.20 is +detected, a fix needed by screen-sharing apps (e.g., Zoom) will +also be made.

+
    +
  • If X11 development libraries are present, but you wish to build +UxPlay without any X11 dependence, use the cmake option +-DNO_X11_DEPS=ON.
  • +
+
    +
  1. sudo apt install libssl-dev libplist-dev“. (unless +you need to build OpenSSL and libplist from source).
  2. +
  3. sudo apt install libavahi-compat-libdnssd-dev
  4. +
  5. sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev. +(*Skip if you built Gstreamer from source)
  6. +
  7. cmake . (For a cleaner build, which is useful if +you modify the source, replace this by +“mkdir build; cd build; cmake ..”: you can then delete +the contents of the build directory if needed, without +affecting the source.) Also add any cmake “-D” options +here as needed (e.g, -DNO_X11_DEPS=ON or +-DNO_MARCH_NATIVE=ON).
  8. +
  9. make
  10. +
  11. sudo make install (you can afterwards uninstall with +sudo make uninstall in the same directory in which this was +run).
  12. +
+

This installs the executable file “uxplay” to +/usr/local/bin, (and installs a manpage to somewhere +standard like /usr/local/share/man/man1 and README files to +somewhere like /usr/local/share/doc/uxplay). (If “man +uxplay” fails, check if $MANPATH is set: if so, the path to the manpage +(usually /usr/local/share/man/) needs to be added to $MANPATH .) The +uxplay executable can also be found in the build directory after the +build process, if you wish to test before installing (in which case the +GStreamer plugins must first be installed).

+

Building on non-Debian +Linux and *BSD

+

**For those with RPM-based distributions, a RPM spec file uxplay.spec +is also available: see Building an installable rpm +package.

+
    +
  • Red Hat, or clones like CentOS (now continued as Rocky +Linux or Alma Linux): (sudo dnf install, or sudo yum install) +openssl-devel libplist-devel avahi-compat-libdns_sd-devel +gstreamer1-devel gstreamer1-plugins-base-devel (+libX11-devel for +fullscreen X11) (some of these may be in the “CodeReady” add-on +repository, called “PowerTools” by clones)

  • +
  • Mageia, PCLinuxOS, OpenMandriva: Same as Red +Hat, except for name changes: (Mageia) “gstreamer1.0-devel”, +“gstreamer-plugins-base1.0-devel”; (OpenMandriva) “libopenssl-devel”, +“gstreamer-devel”, “libgst-plugins-base1.0-devel”. PCLinuxOS: same as +Mageia, but uses synaptic (or apt) as its package manager.

  • +
  • openSUSE: (sudo zypper install) +libopenssl-3-devel (formerly libopenssl-devel) libplist-2_0-devel +(formerly libplist-devel) avahi-compat-mDNSResponder-devel +gstreamer-devel gstreamer-plugins-base-devel (+ libX11-devel for +fullscreen X11).

  • +
  • Arch Linux (Also available as a package in +AUR): (sudo pacman -Syu) openssl libplist avahi +gst-plugins-base.

  • +
  • FreeBSD: (sudo pkg install) libplist gstreamer1. +Either avahi-libdns or mDNSResponder must also be installed to provide +the dns_sd library. OpenSSL is already installed as a System +Library.

  • +
+

Building an installable RPM +package

+

First-time RPM builders should first install the rpm-build and +rpmdevtools packages, then create the rpmbuild tree with +“rpmdev-setuptree”. Then download and copy uxplay.spec into +~/rpmbuild/SPECS. In that directory, run +“rpmdev-spectool -g -R uxplay.spec” to download the +corresponding source file uxplay-*.tar.gz into +~/rpmbuild/SOURCES (“rpmdev-spectool” may also be just +called “spectool”); then run “rpmbuild -ba uxplay.spec” +(you will need to install any required dependencies this reports). This +should create the uxplay RPM package in a subdirectory of +~/rpmbuild/RPMS. (uxplay.spec is tested on +Fedora 38, Rocky Linux 9.2, openSUSE Leap 15.5, Mageia 9, OpenMandriva, +PCLinuxOS; it can be easily modified to include dependency lists for +other RPM-based distributions.)

+

Running UxPlay

+

Installing +plugins (Debian-based Linux distributions, including Ubuntu and +Raspberry Pi OS) (skip if you built a complete GStreamer from +source)

+

Next install the GStreamer plugins that are needed with +sudo apt install gstreamer1.0-<plugin>. Values of +<plugin> required are:

+
    +
  1. plugins-base
  2. +
  3. libav” (for sound),
  4. +
  5. plugins-good” (for v4l2 hardware h264 +decoding)
  6. +
  7. plugins-bad” (for h264 decoding).
  8. +
+

Debian-based distributions split some of the plugin packages +into smaller pieces: some that may also be needed include +“gl” for OpenGL support (this provides the “-vs +glimagesink” videosink, which can be very useful in many systems +(including Raspberry Pi), and should always be used when using h264/h265 +decoding by a NVIDIA GPU), “gtk3” (which provides the +“-vs gtksink” videosink), and “x” for X11 support, +although these may already be installed; “vaapi” is +needed for hardware-accelerated h264 video decoding by Intel or AMD +graphics (but not for use with NVIDIA using proprietary drivers). If +sound is not working, +“alsa”“,”pulseaudio”, or +“pipewire” plugins may need to be installed, depending +on how your audio is set up.

+
    +
  • Also install “gstreamer1.0-tools” to get the +utility gst-inspect-1.0 for examining the GStreamer installation.
  • +
+

Installing +plugins (Non-Debian-based Linux or *BSD) (skip if you built a +complete GStreamer from source)

+

In some cases, because of patent issues, the libav plugin feature +avdec_aac needed for decoding AAC audio in mirror mode +is not provided in the official distribution: get it from community +repositories for those distributions.

+
    +
  • Red Hat, or clones like CentOS (now continued as Rocky +Linux or Alma Linux): Install gstreamer1-libav +gstreamer1-plugins-bad-free (+ gstreamer1-vaapi for Intel/AMD graphics). +In recent Fedora, gstreamer1-libav is renamed gstreamer1-plugin-libav. +To get avdec_aac, install packages from rpmfusion.org: (get +ffmpeg-libs from rpmfusion; on RHEL or clones, but not recent Fedora, +also get gstreamer1-libav from there).

  • +
  • Mageia, PCLinuxOS, OpenMandriva: Install +gstreamer1.0-libav gstreamer1.0-plugins-bad (+ gstreamer1.0-vaapi for +Intel/AMD graphics). On Mageia, to get avdec_aac, install ffmpeg +from the “tainted” repository, (which also provides a more +complete gstreamer1.0-plugins-bad).

  • +
  • openSUSE: Install gstreamer-plugins-libav +gstreamer-plugins-bad (+ gstreamer-plugins-vaapi for Intel/AMD +graphics). To get avdec_aac, install libav* packages for +openSUSE from Packman +“Essentials”; recommendation: after adding the Packman +repository, use the option in YaST Software management to switch all +system packages for multimedia to Packman).

  • +
  • Arch Linux Install gst-plugins-good +gst-plugins-bad gst-libav (+ gstreamer-vaapi for Intel/AMD +graphics).

  • +
  • FreeBSD: Install gstreamer1-libav, +gstreamer1-plugins, gstreamer1-plugins-* (* = core, good, bad, x, gtk, +gl, vulkan, pulse, v4l2, …), (+ gstreamer1-vaapi for Intel/AMD +graphics).

  • +
+

Starting and running UxPlay

+

Since UxPlay-1.64, UxPlay can be started with options read from a +configuration file, which will be the first found of (1) a file with a +path given by environment variable $UXPLAYRC, (2) +~/.uxplayrc in the user’s home directory (“~”), (3) +~/.config/uxplayrc. The format is one option per line, +omitting the initial "-" of the command-line option. Lines +in the configuration file beginning with "#" are treated as +comments and ignored.

+

Run uxplay in a terminal window. On some systems, +you can specify fullscreen mode with the -fs option, or +toggle into and out of fullscreen mode with F11 or (held-down left +Alt)+Enter keys. Use Ctrl-C (or close the window) to terminate it when +done. If the UxPlay server is not seen by the iOS client’s drop-down +“Screen Mirroring” panel, check that your DNS-SD server (usually +avahi-daemon) is running: do this in a terminal window with +systemctl status avahi-daemon. If this shows the +avahi-daemon is not running, control it with +sudo systemctl [start,stop,enable,disable] avahi-daemon (on +non-systemd systems, such as *BSD, use +sudo service avahi-daemon [status, start, stop, restart, ...]). +If UxPlay is seen, but the client fails to connect when it is selected, +there may be a firewall on the server that prevents UxPlay from +receiving client connection requests unless some network ports are +opened: if a firewall is active, also open UDP port 5353 (for +mDNS queries) needed by Avahi. See Troubleshooting below for help with this or +other problems.

+
    +
  • Unlike an Apple TV, the UxPlay server does not by default require +clients to initially “pair” with it using a pin code displayed by the +server (after which the client “trusts” the server, and does not need to +repeat this). Since v1.67, Uxplay offers such “pin-authentication” as an +option: see “-pin” and “-reg” in Usage for details, if you wish to use it. Some +clients with MDM (Mobile Device Management, often present on +employer-owned devices) are required to use pin-authentication: UxPlay +will provide this even when running without the pin +option.

  • +
  • By default, UxPlay is locked to its current client until that +client drops the connection; since UxPlay-1.58, the option +-nohold modifies this behavior so that when a new client +requests a connection, it removes the current client and takes over. +UxPlay 1.66 introduces a mechanism ( -restrict, +-allow <id>, -block <id>) to +control which clients are allowed to connect, using their “deviceID” +(which in Apple devices appears to be immutable).

  • +
  • In Mirror mode, GStreamer has a choice of two +methods to play video with its accompanying audio: prior to UxPlay-1.64, +the video and audio streams were both played as soon as possible after +they arrived (the GStreamer “sync=false” method), with a +GStreamer internal clock used to try to keep them synchronized. +Starting with UxPlay-1.64, the other method (GStreamer’s +“sync=true” mode), which uses timestamps in the audio and video +streams sent by the client, is the new default. On +low-decoding-power UxPlay hosts (such as Raspberry Pi Zero W or 3 B+ +models) this will drop video frames that cannot be decoded in time to +play with the audio, making the video jerky, but still +synchronized.

  • +
+

The older method which does not drop late video frames worked well on +more powerful systems, and is still available with the UxPlay option +“-vsync no”; this method is adapted to “live streaming”, +and may be better when using UxPlay as a second monitor for a Mac +computer, for example, while the new default timestamp-based method is +best for watching a video, to keep lip movements and voices +synchronized. (Without use of timestamps, video will eventually lag +behind audio if it cannot be decoded fast enough: hardware-accelerated +video-decoding helped to prevent this previously when timestamps were +not being used.)

+
    +
  • In Audio-only mode the GStreamer “sync=false” mode (not using +timestamps) is still the default, but if you want to keep the audio +playing on the server synchronized with the video showing on the client, +use the -async timestamp-based option. (An example might be +if you want to follow the Apple Music lyrics on the client while +listening to superior sound on the UxPlay server). This delays the video +on the client to match audio on the server, so leads to a slight delay +before a pause or track-change initiated on the client takes effect on +the audio played by the server.
  • +
+

AirPlay volume-control attenuates volume (gain) by up to -30dB: the +decibel range -30:0 can be rescaled from Low:0, or +Low:High, using the option -db (“-db +Low” or “-db Low:High”), Low must be +negative. Rescaling is linear in decibels. Note that GStreamer’s audio +format will “clip” any audio gain above +20db, so keep High +below that level. The option -taper provides a “tapered” +AirPlay volume-control profile some users may prefer.

+

The -vsync and -async options also allow an optional positive (or +negative) audio-delay adjustment in milliseconds for +fine-tuning : -vsync 20.5 delays audio relative to video by +0.0205 secs; a negative value advances it.)

+
    +
  • you may find video is improved by the setting -fps 60 that allows +some video to be played at 60 frames per second. (You can see what +framerate is actually streaming by using -vs fpsdisplaysink, and/or +-FPSdata.) When using this, you should use the default timestamp-based +synchronization option -vsync.

  • +
  • Since UxPlay-1.54, you can display the accompanying “Cover Art” +from sources like Apple Music in Audio-Only (ALAC) mode: run +“uxplay -ca <name> &” in the background, then run +a image viewer with an autoreload feature: an example is “feh”: run +“feh -R 1 <name>” in the foreground; terminate feh +and then Uxplay with “ctrl-C fg ctrl-C”.

  • +
+

By default, GStreamer uses an algorithm to search for the best +“videosink” (GStreamer’s term for a graphics driver to display images) +to use. You can overide this with the uxplay option +-vs <videosink>. Which videosinks are available +depends on your operating system and graphics hardware: use +“gst-inspect-1.0 | grep sink | grep -e video -e Video -e image” +to see what is available. Some possibilites on Linux/*BSD are:

+
    +
  • glimagesink (OpenGL), +waylandsink

  • +
  • xvimagesink, ximagesink +(X11)

  • +
  • kmssink, fbdevsink (console +graphics without X11)

  • +
  • vaapisink (for Intel/AMD hardware-accelerated +graphics); for NVIDIA hardware graphics (with CUDA) use +glimagesink combined with “-vd nvh264dec” +(or “nvh264sldec”, a new variant which will become “nvh264dec” in +GStreamer-1.24).

  • +
  • If the server is “headless” (no attached monitor, renders audio +only) use -vs 0.

  • +
+

Note that videosink options can set using quoted arguments to -vs: +e.g., -vs "xvimagesink display=:0": ximagesink and +xvimagesink allow an X11 display name to be specified, and waylandsink +has a similar option. Videosink options (“properties”) can be found in +their GStreamer description pages,such as +https://gstreamer.freedesktop.org/documentation/xvimagesink .

+

GStreamer also searches for the best “audiosink”; override its choice +with -as <audiosink>. Choices on Linux include +pulsesink, alsasink, pipewiresink, oss4sink; see what is available with +gst-inspect-1.0 | grep sink | grep -e audio -e Audio.

+

One common problem involves GStreamer attempting to use +incorrectly-configured or absent accelerated hardware h264 video +decoding (e.g., VAAPI). Try “uxplay -avdec” to force +software video decoding; if this works you can then try to fix +accelerated hardware video decoding if you need it, or just uninstall +the GStreamer vaapi plugin.

+

See Usage for more run-time options.

+

Special +instructions for Raspberry Pi (tested on Raspberry Pi Zero 2 W, 3 Model +B+, 4 Model B, and 5 only):

+
    +
  • For Framebuffer video (for Raspberry Pi OS “Lite” and other +non-X11 distributions) use the KMS videosink “-vs kmssink” (the DirectFB +framebuffer videosink “dfbvideosink” is broken on the Pi, and +segfaults). In this case you should explicitly use the “-vs kmssink” +option, as without it, autovideosink does not find the correct +videosink.

  • +
  • Raspberry Pi 5 does not provide hardware H264 decoding (and does +not need it).

  • +
  • Pi Zero 2 W, 3 Model B+ and 4 Model B should use hardware H264 +decoding by the Broadcom GPU, but it requires an out-of-mainstream +kernel module bcm2835_codec maintained in the Raspberry Pi kernel +tree; distributions that are known to supply it include Raspberry Pi +OS, Ubuntu, and Manjaro-RPi4. Use software decoding (option -avdec) if +this module is not available.

  • +
  • Uxplay uses the Video4Linux2 (v4l2) plugin from GStreamer-1.22 +and later to access the GPU, if hardware H264 decoding is used. This +should happen automatically. The option -v4l2 can be used, but it is +usually best to just let GStreamer find the best video pipeline by +itself.

  • +
  • On older distributions (GStreamer < 1.22), the v4l2 plugin +needs a patch: see the UxPlay +Wiki. Legacy Raspberry Pi OS (Bullseye) has a partially-patched +GStreamer-1.18.4 which needs the uxplay option -bt709 (and don’t use +-v4l2); it is still better to apply the full patch from the UxPlay Wiki +in this case.

  • +
  • It appears that when hardware h264 video decoding is +used, the option -bt709 became needed again in GStreamer-1.22 and +later.

  • +
  • For “double-legacy” Raspberry Pi OS (Buster), there is no patch +for GStreamer-1.14. Instead, first build a complete newer +GStreamer-1.18.6 from source using these +instructions before building UxPlay.

  • +
  • Raspberry Pi 3 Model B+ running a 32 bit OS can also access the +GPU with the GStreamer OMX plugin (use option +“-vd omxh264dec”), but this is broken by Pi 4 Model B +firmware. OMX support was removed from Raspberry Pi OS (Bullseye), but +is present in Buster.

  • +
  • H265 (4K) video is potentially supported by +hardware decoding on Raspberry Pi 5 models, as well as on Raspberry Pi 4 +model B, using a dedicated HEVC decoding block, but the “rpivid” kernel +driver for this is not yet supported by GStreamer (this driver decodes +video into a non-standard format that cannot be supported by GStreamer +until the driver is in the mainline Linux kernel). Raspberry Pi provides +a version of ffmpeg that can use that format, but at present UxPlay +cannot use this. The best solution would be for the driver to be +“upstreamed” to the kernel, allowing GStreamer support. (Software HEVC +decoding works, but does not seem to give satisfactory results on the +Pi).

  • +
+

Even with GPU video decoding, some frames may be dropped by the +lower-power models to keep audio and video synchronized using +timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config +“Performance Options” allows specifying how much memory to allocate to +the GPU, but this setting appears to be absent in Bookworm (but it can +still be set to e.g. 128MB by adding a line “gpu_mem=128” in +/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well +when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to +the GPU (default seems to be 64MB).

+

The basic uxplay options for R Pi are +uxplay [-vs <videosink>]. The choice +<videosink> = glimagesink is sometimes +useful. With the Wayland video compositor, use +<videosink> = waylandsink. With +framebuffer video, use <videosink> = +kmssink.

+
    +
  • Tip: to start UxPlay on a remote host (such as a Raspberry Pi) using +ssh:
  • +
+ +
   ssh user@remote_host
+   export DISPLAY=:0
+   nohup uxplay [options] > FILE &
+

Sound and video will play on the remote host; “nohup” will keep +uxplay running if the ssh session is closed. Terminal output is saved to +FILE (which can be /dev/null to discard it)

+

Building +UxPlay on macOS: (Intel X86_64 and “Apple Silicon” M1/M2 +Macs)

+

Note: A native AirPlay Server feature is included in macOS 12 +Monterey, but is restricted to recent hardware. UxPlay can run on older +macOS systems that will not be able to run Monterey, or can run Monterey +but not AirPlay.

+

These instructions for macOS assume that the Xcode command-line +developer tools are installed (if Xcode is installed, open the Terminal, +type “sudo xcode-select –install” and accept the conditions).

+

It is also assumed that CMake >= 3.13 is installed: this can be +done with package managers MacPorts +(sudo port install cmake), Homebrew (brew install cmake), or +by a download from https://cmake.org/download/. Also install +git if you will use it to fetch UxPlay.

+

Next install libplist and openssl-3.x. Note that static versions of +these libraries will be used in the macOS builds, so they can be +uninstalled after building uxplay, if you wish.

+
    +
  • If you use Homebrew: +brew install libplist openssl@3

  • +
  • if you use MacPorts: +sudo port install libplist-devel openssl3

  • +
+

Otherwise, build libplist and openssl from source: see instructions +near the end of this README; requires development tools (autoconf, +automake, libtool, etc.) to be installed.

+

Next get the latest macOS release of GStreamer-1.0.

+

Using “Official” GStreamer (Recommended for both MacPorts and +Homebrew users): install the GStreamer release for macOS from +https://gstreamer.freedesktop.org/download/. (This +release contains its own pkg-config, so you don’t have to install one.) +Install both the gstreamer-1.0 and gstreamer-1.0-devel packages. After +downloading, Shift-Click on them to install (they install to +/Library/FrameWorks/GStreamer.framework). Homebrew or MacPorts users +should not install (or should uninstall) the GStreamer +supplied by their package manager, if they use the “official” +release.

+
    +
  • Since GStreamer v1.22, the “Official” (gstreamer.freedesktop.org) +macOS binaries require a wrapper “gst_macos_main” around the actual main +program (uxplay). This should have been applied during the UxPlay +compilation process, and the initial UxPlay terminal message should +confirm it is being used. (UxPlay can also be built using “Official” +GStreamer v.1.20.7 binaries, which work without the wrapper.)
  • +
+

Using Homebrew’s GStreamer: pkg-config is needed: +(“brew install pkg-config gstreamer”). This causes a large number of +extra packages to be installed by Homebrew as dependencies. The Homebrew +gstreamer installation has recently been reworked into a single +“formula” named gstreamer, which now works without needing +GST_PLUGIN_PATH to be set in the enviroment. Homebrew installs gstreamer +to HOMEBREW_PREFIX/lib/gstreamer-1.0 where by default +HOMEBREW_PREFIX/* is /opt/homebrew/* on Apple +Silicon Macs, and /usr/local/* on Intel Macs; do not put +any extra non-Homebrew plugins (that you build yourself) there, and +instead set GST_PLUGIN_PATH to point to their location (Homebrew does +not supply a complete GStreamer, but seems to have everything needed for +UxPlay). New: the UxPlay build script will now also detect +Homebrew installations in non-standard locations indicated by the +environment variable $HOMEBREW_PREFIX.

+

Using GStreamer installed from MacPorts: this is +not recommended, as currently the MacPorts GStreamer is +old (v1.16.2), unmaintained, and built to use X11:

+ +

(If you really wish to use the MacPorts GStreamer-1.16.2, install +pkgconf (“sudo port install pkgconf”), then “sudo port install +gstreamer1-gst-plugins-base gstreamer1-gst-plugins-good +gstreamer1-gst-plugins-bad gstreamer1-gst-libav”. For X11 support on +macOS, compile UxPlay using a special cmake option +-DUSE_X11=ON, and run it from an XQuartz terminal with -vs +ximagesink; older non-retina macs require a lower resolution when using +X11: uxplay -s 800x600.)

+

After installing GStreamer, build and install uxplay: open a terminal +and change into the UxPlay source directory (“UxPlay-master” for zipfile +downloads, “UxPlay” for “git clone” downloads) and build/install with +“cmake . ; make ; sudo make install” (same as for Linux).

+
    +
  • Running UxPlay while checking for GStreamer warnings (do this +with “export GST_DEBUG=2” before runnng UxPlay) reveals that with the +default (since UxPlay 1.64) use of timestamps for video synchonization, +many video frames are being dropped (only on macOS), perhaps due to +another error (about videometa) that shows up in the GStreamer warnings. +Recommendation: use the new UxPlay “no timestamp” option +“-vsync no (you can add a line “vsync no” in the +uxplayrc configuration file).

  • +
  • On macOS with this installation of GStreamer, the only videosinks +available seem to be glimagesink (default choice made by autovideosink) +and osxvideosink. The window title does not show the Airplay server +name, but the window is visible to screen-sharing apps (e.g., Zoom). The +only available audiosink seems to be osxaudiosink.

  • +
  • The option -nc is always used, whether or not it is selected. +This is a workaround for a problem with GStreamer videosinks on macOS: +if the GStreamer pipeline is destroyed while the mirror window is still +open, a segfault occurs.

  • +
  • In the case of glimagesink, the resolution settings “-s wxh” do +not affect the (small) initial OpenGL mirror window size, but the window +can be expanded using the mouse or trackpad. In contrast, a window +created with “-vs osxvideosink” is initially big, but has the wrong +aspect ratio (stretched image); in this case the aspect ratio changes +when the window width is changed by dragging its side; the option +-vs "osxvideosink force-aspect-ratio=true" can be used to +make the window have the correct aspect ratio when it first +opens.

  • +
+

Building +UxPlay on Microsoft Windows, using MSYS2 with the MinGW-64 +compiler.

+
    +
  • tested on Windows 10 and 11, 64-bit.
  • +
+
    +
  1. Download and install Bonjour SDK for Windows +v3.0. You can download the SDK without any registration at softpedia.com, +or get it from the official Apple site https://developer.apple.com/download +(Apple makes you register as a developer to access it from their site). +This should install the Bonjour SDK as +C:\Program Files\Bonjour SDK.

  2. +
  3. (This is for 64-bit Windows; a build for 32-bit Windows should be +possible, but is not tested.) The unix-like MSYS2 build environment will +be used: download and install MSYS2 from the official site https://www.msys2.org/. Accept the +default installation location C:\mysys64.

  4. +
  5. MSYS2 packages +are installed with a variant of the “pacman” package manager used by +Arch Linux. Open a “MSYS2 MINGW64” terminal from the MSYS2 tab in the +Windows Start menu, and update the new MSYS2 installation with “pacman +-Syu”. Then install the MinGW-64 compiler and +cmake

    +
    pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc
    +

    The compiler with all required dependencies will be installed in the +msys64 directory, with default path C:/msys64/mingw64. Here +we will simply build UxPlay from the command line in the MSYS2 +environment (this uses “ninja” in place of +“make” for the build system).

  6. +
  7. Download the latest UxPlay from github (to use +git, install it with pacman -S git, then +“git clone https://github.com/FDH2/UxPlay”), then +install UxPlay dependencies (openssl is already installed with +MSYS2):

    +

    pacman -S mingw-w64-x86_64-libplist mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base

    +

    If you are trying a different Windows build system, MSVC versions of +GStreamer for Windows are available from the official GStreamer +site, but only the MinGW 64-bit build on MSYS2 has been +tested.

  8. +
  9. cd to the UxPlay source directory, then +“mkdir build” and “cd build”. The build +process assumes that the Bonjour SDK is installed at +C:\Program Files\Bonjour SDK. If it is somewhere else, set +the enviroment variable BONJOUR_SDK_HOME to point to its location. Then +build UxPlay with

    +

    cmake ..

    +

    ninja

  10. +
  11. Assuming no error in either of these, you will have built the +uxplay executable uxplay.exe in the current (“build”) +directory. The “sudo make install” and “sudo make uninstall” features +offered in the other builds are not available on Windows; instead, the +MSYS2 environment has /mingw64/... available, and you can +install the uxplay.exe executable in C:/msys64/mingw64/bin +(plus manpage and documentation in +C:/msys64/mingw64/share/...) with

    +

    cmake --install . --prefix /mingw64

    +

    To be able to view the manpage, you need to install the manpage +viewer with “pacman -S man”.

  12. +
+

To run uxplay.exe you need to install some gstreamer +plugin packages with +pacman -S mingw-w64-x86_64-gst-<plugin>, where the +required ones have <plugin> given by

+
    +
  1. libav
  2. +
  3. plugins-good
  4. +
  5. plugins-bad
  6. +
+

Other possible MSYS2 gstreamer plugin packages you might use are +listed in MSYS2 +packages.

+

You also will need to grant permission to the uxplay executable +uxplay.exe to access data through the Windows firewall. You may +automatically be offered the choice to do this when you first run +uxplay, or you may need to do it using Windows +Settings->Update and Security->Windows Security->Firewall & +network protection -> allow an app through firewall. If your +virus protection flags uxplay.exe as “suspicious” (but without a true +malware signature) you may need to give it an exception.

+

Now test by running “uxplay” (in a MSYS2 terminal +window). If you need to specify the audiosink, there are two main +choices on Windows: the older DirectSound plugin +“-as directsoundsink”, and the more modern Windows Audio +Session API (wasapi) plugin “-as wasapisink”, which +supports additional +options such as

+
uxplay -as 'wasapisink device=\"<guid>\"' 
+

where <guid> specifies an available audio device +by its GUID, which can be found using +“gst-device-monitor-1.0 Audio”: <guid> +has a form like +\{0.0.0.00000000\}.\{98e35b2b-8eba-412e-b840-fd2c2492cf44\}. +If “device” is not specified, the default audio device is +used.

+

If you wish to specify the videosink using the +-vs <videosink> option, some choices for +<videosink> are d3d11videosink, +d3dvideosink, glimagesink, +gtksink.

+
    +
  • With Direct3D 11.0 or greater, you can either always be in +fullscreen mode using option +-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true", +or get the ability to toggle into and out of fullscreen mode using the +Alt-Enter key combination with option +-vs "d3d11videosink fullscreen-toggle-mode=alt-enter". For +convenience, these options will be added if just +-vs d3d11videosink with or without the fullscreen option +“-fs” is used. (Windows users may wish to add +“vs d3d11videosink” (no initial “-”) to the +UxPlay startup options file; see “man uxplay” or “uxplay -h”.)
  • +
+

The executable uxplay.exe can also be run without the MSYS2 +environment, in the Windows Terminal, with +C:\msys64\mingw64\bin\uxplay.

+

Usage

+

Options:

+
    +
  • These can also be written (one option per line, without the initial +“-” character) in the UxPlay startup file (either given by +environment variable $UXPLAYRC, or ~/.uxplayrc +or ~/.config/uxplayrc); lines begining with +“#” are treated as comments, and ignored. Command line +options supersede options in the startup file.
  • +
+

-n server_name (Default: UxPlay); +server_name@_hostname_ will be the name that appears offering AirPlay +services to your iPad, iPhone etc, where hostname is the name +of the server running uxplay. This will also now be the name shown above +the mirror display (X11) window.

+

-nh Do not append “@_hostname_” at the end of the AirPlay +server name.

+

-h265 Activate “ScreenMultiCodec” support (AirPlay +“Features” bit 42) for accepting h265 (4K/HEVC) video in addition to +h264 video (1080p) in screen-mirror mode. When this option is used, two +“video pipelines” (one for h264, one for h265) are created. If any +GStreamer plugins in the pipeline are specific for h264 or h265, the +correct version will be used in each pipeline. A wired Client-Server +ethernet connection is preferred over Wifi for 4K video, and might be +required by the client. Only recent Apple devices (M1/M2 Macs or iPads, +and some iPhones) can send h265 video if a resolution “-s wxh” with h +> 1080 is requested. The “-h265” option changes the default +resolution (“-s” option) from 1920x1080 to 3840x2160, and leaves default +maximum framerate (“-fps” option) at 30fps.

+

-hls Activate HTTP Live Streaming support. With this +option YouTube videos can be streamed directly from YouTube servers to +UxPlay (without passing through the client) by clicking on the AirPlay +icon in the YouTube app.

+

-pin [nnnn]: (since v1.67) use Apple-style +(one-time) “pin” authentication when a new client connects for the first +time: a four-digit pin code is displayed on the terminal, and the client +screen shows a login prompt for this to be entered. When “-pin” is used +by itself, a new random pin code is chosen for each authentication; if +“-pin nnnn” (e.g., “-pin 3939”) is used, this will set an unchanging +fixed code. Authentication adds the server to the client’s list of +“trusted servers” and the client will not need to reauthenticate +provided that the client and server public keys remain unchanged. (By +default since v1.68, the server public key is generated from the MAC +address, which can be changed with the -m option; see the -key option +for an alternative method of key generation). (Add a line “pin” in +the UxPlay startup file if you wish the UxPlay server to use the pin +authentication protocol).

+

-reg [filename]: (since v1.68). If “-pin” +is used, this option maintains a register of pin-authenticated “trusted +clients” in $HOME/.uxplay.register (or optionally, in +filename). Without this option, returning clients that skip +pin-authentication are trusted and not checked. This option may be +useful if UxPlay is used in a more public environment, to record client +details; the register is text, one line per client, with client’s public +key (base-64 format), Device ID, and Device name; commenting out (with +“#”) or deleting a line deregisters the corresponding client (see +options -restrict, -block, -allow for more ways to control client +access). (Add a line “reg” in the startup file if you wish to use +this feature.)

+

-vsync [x] (In Mirror mode:) this option +(now the default) uses timestamps to synchronize audio +with video on the server, with an optional audio delay in (decimal) +milliseconds (x = “20.5” means 0.0205 seconds delay: positive +or negative delays less than a second are allowed.) It is needed on +low-power systems such as Raspberry Pi without hardware video +decoding.

+

-vsync no (In Mirror mode:) this switches off +timestamp-based audio-video synchronization, restoring the default +behavior prior to UxPlay-1.64. Standard desktop systems seem to work +well without use of timestamps: this mode is appropriate for “live +streaming” such as using UxPlay as a second monitor for a mac computer, +or monitoring a webcam; with it, no video frames are dropped.

+

-async [x] (In Audio-Only (ALAC) mode:) this option +uses timestamps to synchronize audio on the server with video on the +client, with an optional audio delay in (decimal) milliseconds +(x = “20.5” means 0.0205 seconds delay: positive or negative +delays less than a second are allowed.) Because the client adds a video +delay to account for latency, the server in -async mode adds an +equivalent audio delay, which means that audio changes such as a pause +or a track-change will not take effect immediately. This might in +principle be mitigated by using the -al audio latency +setting to change the latency (default 0.25 secs) that the server +reports to the client, but at present changing this does not seem to +have any effect.

+

-async no. This is the still the default behavior in +Audio-only mode, but this option may be useful as a command-line option +to switch off a -async option set in a “uxplayrc” +configuration file.

+

-db low[:high] Rescales the +AirPlay volume-control attenuation (gain) from -30dB:0dB to +low:0dB or low:high. The lower limit +low must be negative (attenuation); the upper limit +high can be either sign. (GStreamer restricts +volume-augmentation by high so that it cannot exceed +20dB). +The rescaling is “flat”, so that for -db -50:10, a change in Airplay +attenuation by -7dB is translated to a -7 x (60/30) = -14dB attenuation, +and the maximum volume (AirPlay 0dB) is a 10dB augmentation, and Airplay +-30dB would become -50dB. Note that the minimum AirPlay value (-30dB +exactly) is translated to “mute”.

+

-taper Provides a “tapered” Airplay volume-control +profile (matching the one called “dasl-tapering” in shairport-sync): +each time the length of the volume slider (or the number of steps above +mute, where 16 steps = full volume) is reduced by 50%, the perceived +volume is halved (a 10dB attenuation). (This is modified at low volumes, +to use the “untapered” volume if it is louder.)

+

-s wxh e.g. -s 1920x1080 (= “1080p”), the default +width and height resolutions in pixels for h264 video. (The default +becomes 3840x2160 (= “4K”) when the -h265 option is used.) This is just +a request made to the AirPlay client, and perhaps will not be the final +resolution you get. w and h are whole numbers with four digits or less. +Note that the height pixel size is the controlling one +used by the client for determining the streaming format; the width is +dynamically adjusted to the shape of the image (portrait or landscape +format, depending on how an iPad is held, for example).

+

-s wxh@r As above, but also informs the AirPlay +client about the screen refresh rate of the display. Default is r=60 (60 +Hz); r must be a whole number less than 256.

+

-o turns on an “overscanned” option for the display +window. This reduces the image resolution by using some of the pixels +requested by option -s wxh (or their default values 1920x1080) by adding +an empty boundary frame of unused pixels (which would be lost in a +full-screen display that overscans, and is not displayed by gstreamer). +Recommendation: don’t use this option unless there is +some special reason to use it.

+

-fs uses fullscreen mode, but only works with X11, +Wayland, VAAPI, and D3D11 (Windows).

+

-p allows you to select the network ports used by +UxPlay (these need to be opened if the server is behind a firewall). By +itself, -p sets “legacy” ports TCP 7100, 7000, 7001, UDP 6000, 6001, +7011. -p n (e.g. -p 35000) sets TCP and UDP ports n, n+1, n+2. -p +n1,n2,n3 (comma-separated values) sets each port separately; -p n1,n2 +sets ports n1,n2,n2+1. -p tcp n or -p udp n sets just the TCP or UDP +ports. Ports must be in the range [1024-65535].

+

If the -p option is not used, the ports are chosen dynamically +(randomly), which will not work if a firewall is running.

+

-avdec forces use of software h264 decoding using +Gstreamer element avdec_h264 (libav h264 decoder). This option should +prevent autovideosink choosing a hardware-accelerated videosink plugin +such as vaapisink.

+

-vp parser choses the GStreamer pipeline’s +h264 parser element, default is h264parse. Using quotes “…” allows +options to be added.

+

-vd decoder chooses the GStreamer +pipeline’s h264 decoder element, instead of the default value +“decodebin” which chooses it for you. Software decoding is done by +avdec_h264; various hardware decoders include: vaapih264dec, nvdec, +nvh264dec, v4l2h264dec (these require that the appropriate hardware is +available). Using quotes “…” allows some parameters to be included with +the decoder name.

+

-vc converter chooses the GStreamer +pipeline’s videoconverter element, instead of the default value +“videoconvert”. When using Video4Linux2 hardware-decoding by a +GPU,-vc v4l2convert will also use the GPU for video +conversion. Using quotes “…” allows some parameters to be included with +the converter name.

+

-vs videosink chooses the GStreamer +videosink, instead of the default value “autovideosink” which chooses it +for you. Some videosink choices are: ximagesink, xvimagesink, vaapisink +(for intel graphics), gtksink, glimagesink, waylandsink, osxvideosink +(for macOS), kmssink (for systems without X11, like Raspberry Pi OS +lite) or fpsdisplaysink (which shows the streaming framerate in fps). +Using quotes “…” allows some parameters to be included with the +videosink name. For example, fullscreen mode is +supported by the vaapisink plugin, and is obtained using +-vs "vaapisink fullscreen=true"; this also works with +waylandsink. The syntax of such options is specific to a +given plugin (see GStreamer documentation), and some choices of +videosink might not work on your system.

+

-vs 0 suppresses display of streamed video. In +mirror mode, the client’s screen is still mirrored at a reduced rate of +1 frame per second, but is not rendered or displayed. This option should +always be used if the server is “headless” (with no attached screen to +display video), and only used to render audio, which will be AAC +lossily-compressed audio in mirror mode with unrendered video, and +superior-quality ALAC Apple Lossless audio in Airplay audio-only +mode.

+

-v4l2 Video settings for hardware h264 video +decoding in the GPU by Video4Linux2. Equivalent to +-vd v4l2h264dec -vc v4l2convert.

+

-bt709 A workaround for the failure of the older +Video4Linux2 plugin to recognize Apple’s use of an uncommon (but +permitted) “full-range color” variant of the bt709 color standard for +digital TV. This is no longer needed by GStreamer-1.20.4 and backports +from it.

+

-rpi Equivalent to “-v4l2” (Not valid for Raspberry +Pi model 5, and removed in UxPlay 1.67)

+

-rpigl Equivalent to “-rpi -vs glimagesink”. +(Removed since UxPlay 1.67)

+

-rpifb Equivalent to “-rpi -vs kmssink” (Removed +since UxPlay 1.67)

+

-rpiwl Equivalent to “-rpi -vs waylandsink”. +(Removed since UxPlay 1.67)

+

-as audiosink chooses the GStreamer +audiosink, instead of letting autoaudiosink pick it for you. Some +audiosink choices are: pulsesink, alsasink, pipewiresink, osssink, +oss4sink, jackaudiosink, osxaudiosink (for macOS), wasapisink, +directsoundsink (for Windows). Using quotes “…” might allow some +optional parameters (e.g. -as "alsasink device=..." to +specify a non-default output device). The syntax of such options is +specific to a given plugin (see GStreamer documentation), and some +choices of audiosink might not work on your system.

+

-as 0 (or just -a) suppresses +playing of streamed audio, but displays streamed video.

+

-al x specifies an audio latency x +in (decimal) seconds in Audio-only (ALAC), that is reported to the +client. Values in the range [0.0, 10.0] seconds are allowed, and will be +converted to a whole number of microseconds. Default is 0.25 sec (250000 +usec). (However, the client appears to ignore this reported latency, +so this option seems non-functional.)

+

-ca filename provides a file (where +filename can include a full path) used for output of “cover +art” (from Apple Music, etc.,) in audio-only ALAC mode. This +file is overwritten with the latest cover art as it arrives. Cover art +(jpeg format) is discarded if this option is not used. Use with a image +viewer that reloads the image if it changes, or regularly (e.g. +once per second.). To achieve this, run +“uxplay -ca [path/to/]filename &” in the background, +then run the the image viewer in the foreground. Example, using +feh as the viewer: run +“feh -R 1 [path/to/]filename” (in the same terminal window +in which uxplay was put into the background). To quit, use +ctrl-C fg ctrl-C to terminate the image viewer, bring +uxplay into the foreground, and terminate it too.

+

-reset n sets a limit of n consecutive +timeout failures of the client to respond to ntp requests from the +server (these are sent every 3 seconds to check if the client is still +present, and synchronize with it). After n failures, the client +will be presumed to be offline, and the connection will be reset to +allow a new connection. The default value of n is 5; the value +n = 0 means “no limit” on timeouts.

+

-nofreeze closes the video window after a reset due +to ntp timeout (default is to leave window open to allow a smoother +reconection to the same client). This option may be useful in fullscreen +mode.

+

-nc maintains previous UxPlay < 1.45 behavior +that does not close the video window when the the +client sends the “Stop Mirroring” signal. This option is currently +used by default in macOS, as the window created in macOS by GStreamer +does not terminate correctly (it causes a segfault) if it is still open +when the GStreamer pipeline is closed.

+

-nohold Drops the current connection when a new +client attempts to connect. Without this option, the current client +maintains exclusive ownership of UxPlay until it disconnects.

+

-restrict Restrict clients allowed to connect to +those specified by -allow <deviceID>. The deviceID +has the form of a MAC address which is displayed by UxPlay when the +client attempts to connect, and appears to be immutable. It has the +format XX:XX:XX:XX:XX:XX, X = 0-9,A-F, and is possibly the +“true” hardware MAC address of the device. Note that iOS clients +generally expose different random “private Wi_Fi addresses” (“fake” MAC +addresses) to different networks (for privacy reasons, to prevent +tracking), which may change, and do not correpond to the deviceID.

+

-restrict no Remove restrictions (default). This is +useful as a command-line argument to overide restrictions set in the +Startup file.

+

-allow id Adds the deviceID = id +to the list of allowed clients when client restrictions are being +enforced. Usually this will be an entry in the uxplayrc startup +file.

+

-block id Always block clients with +deviceID = id, even when client restrictions are not being +enforced generally. Usually this will be an entry in the uxplayrc +startup file.

+

-FPSdata Turns on monitoring of regular reports +about video streaming performance that are sent by the client. These +will be displayed in the terminal window if this option is used. The +data is updated by the client at 1 second intervals.

+

-fps n sets a maximum frame rate (in frames per +second) for the AirPlay client to stream video; n must be a whole number +less than 256. (The client may choose to serve video at any frame rate +lower than this; default is 30 fps.) A setting of 60 fps may give you +improved video but is not recommended on Raspberry Pi. A setting below +30 fps might be useful to reduce latency if you are running more than +one instance of uxplay at the same time. This setting is only an +advisory to the client device, so setting a high value will not force a +high framerate. (You can test using “-vs fpsdisplaysink” to see +what framerate is being received, or use the option -FPSdata which +displays video-stream performance data continuously sent by the client +during video-streaming.)

+

-f {H|V|I} implements “videoflip” image transforms: +H = horizontal flip (right-left flip, or mirror image); V = vertical +flip ; I = 180 degree rotation or inversion (which is the combination of +H with V).

+

-r {R|L} 90 degree Right (clockwise) or Left +(counter-clockwise) rotations; these image transforms are carried out +after any -f transforms.

+

-m [mac] changes the MAC address (Device ID) used by +UxPlay (default is to use the true hardware MAC address reported by the +host computer’s network card). (Different server_name, MAC addresses, +and network ports are needed for each running uxplay if you attempt to +run more than one instance of uxplay on the same computer.) If [mac] (in +form xx:xx:xx:xx:xx:xx, 6 hex octets) is not given, a random MAC address +is generated. If UxPlay fails to find the true MAC address of a network +card, (more specifically, the MAC address used by the first active +network interface detected) a random MAC address will be used even if +option -m was not specified. (Note that a random MAC +address will be different each time UxPlay is started).

+

-key [filename]: This (more secure) option +for generating and storing a persistant public key (needed for the -pin +option) has been replaced by default with a (less secure) method which +generates a key from the server’s “device ID” (MAC address, which can be +changed with the -m option, conveniently as a startup file option). When +the -key option is used, a securely generated keypair is generated and +stored in $HOME/.uxplay.pem, if that file does not exist, +or read from it, if it exists. (Optionally, the key can be stored in +filename.) This method is more secure than the new default +method, (because the Device ID is broadcast in the DNS_SD announcement) +but still leaves the private key exposed to anyone who can access the +pem file. This option should be set in the UxPlay startup file as a line +“key” or “key filename” (no initial “-”), where +filename is a full path which should be enclosed in quotes +("....") if it contains any blank spaces. Because +the default method is simpler, and security of client access to uxplay +is unlikely to be an important issue, the -key option is no longer +recommended.

+

-dacp [filename]: Export current client +DACP-ID and Active-Remote key to file: default is $HOME/.uxplay.dacp. +(optionally can be changed to filename). Can be used by remote +control applications. File is transient: only exists while client is +connected.

+

-vdmp Dumps h264 video to file videodump.h264. -vdmp +n dumps not more than n NAL units to videodump.x.h264; x= 1,2,… +increases each time a SPS/PPS NAL unit arrives. To change the name +videodump, use -vdmp [n] filename.

+

-admp Dumps audio to file audiodump.x.aac (AAC-ELD +format audio), audiodump.x.alac (ALAC format audio) or audiodump.x.aud +(other-format audio), where x = 1,2,3… increases each time the audio +format changes. -admp n restricts the number of packets dumped +to a file to n or less. To change the name audiodump, +use -admp [n] filename. Note that (unlike dumped video) the +dumped audio is currently only useful for debugging, as it is not +containerized to make it playable with standard audio players.

+

-d Enable debug output. Note: this does not show +GStreamer error or debug messages. To see GStreamer error and warning +messages, set the environment variable GST_DEBUG with “export +GST_DEBUG=2” before running uxplay. To see GStreamer information +messages, set GST_DEBUG=4; for DEBUG messages, GST_DEBUG=5; increase +this to see even more of the GStreamer inner workings.

+

Troubleshooting

+

Note: uxplay is run from a terminal command line, and +informational messages are written to the terminal.

+

0. Problems in compiling +UxPlay.

+

One user (on Ubuntu) found compilation failed with messages about +linking to “usr/local/lib/libcrypto.a” and “zlib”. This was because (in +addition to the standard ubuntu installation of libssl-dev), the user +was unaware that a second installation with libcrypto in /usr/local was +present. Solution: when more than one installation of OpenSSL is +present, set the environment variable OPEN_SSL_ROOT_DIR to point to the +correct one; on 64-bit Ubuntu, this is done by running +export OPENSSL_ROOT_DIR=/usr/lib/X86_64-linux-gnu/ before +running cmake.

+

1. Avahi/DNS_SD +Bonjour/Zeroconf issues

+

The DNS_SD Service-Discovery (“Bonjour” or “Zeroconf”) service is +required for UxPlay to work. On Linux, it will be usually provided by +Avahi, and to troubleshoot this, you should use the tool +avahi-browse. (You may need to install a separate package +with a name like avahi-utils to get this.)

+

On Linux, make sure Avahi is installed, and start the avahi-daemon +service on the system running uxplay (your distribution will document +how to do this, for example: +sudo systemctl <cmd> avahi-daemon or +sudo service avahi-daemon <cmd>, with +<cmd> one of enable, disable, start, stop, status. +You might need to edit the avahi-daemon.conf file (it is typically in +/etc/avahi/, find it with +“sudo find /etc -name avahi-daemon.conf”): make sure that +“disable-publishing” is not a selected option). Some +systems may instead use the mdnsd daemon as an alternative to provide +DNS-SD service. (FreeBSD offers both alternatives, but only Avahi was +tested; see here.)

+
    +
  • uxplay starts, but either stalls or stops after “Initialized +server socket(s)” appears (without the server name showing on the +client).
  • +
+

If UxPlay stops with the “No DNS-SD Server found” message, this means +that your network does not have a running Bonjour/zeroconf +DNS-SD server. Before v1.60, UxPlay used to stall silently if +DNS-SD service registration failed, but now stops with an error message +returned by the DNSServiceRegister function: kDNSServiceErr_Unknown if +no DNS-SD server was found: (A NixOS user found that in NixOS, this +error can also occur if avahi-daemon service IS running with publishing +enabled, but reports “the error disappeared on NixOS by setting +services.avahi.openFirewall to true”.) Other mDNS error codes are +in the range FFFE FF00 (-65792) to FFFE FFFF (-65537), and are listed in +the dnssd.h file. An older version of this (the one used by avahi) is +found here. +A few additional error codes are defined in a later version from Apple.

+

If UxPlay stalls without an error message and without +the server name showing on the client, this is a network +problem (if your UxPlay version is older than 1.60, it is also +the behavior when no DNS-SD server is found.)

+

A useful tool for examining such network problems from the client end +is the (free) Discovery DNS-SD browser available +in the Apple App Store for both iOS (works on iPadOS too) and +macOS.

+
    +
  • Some users using dual-band (2.4GHz/5GHz) routers have reported that +clients using the 5GHz band (sometimes) “fail to see UxPlay” (i.e., do +not get a response to their mDNS queries), but the 2.4GHz band works. +Other projects using Bonjour/mDNS have had similar reports; the issue +seems to be router-specific, perhaps related to “auto” rather than fixed +channel selection (5GHz has many more channels to switch between), or +channel width selections; one speculation is that since mDNS uses UDP +protocol (where “lost” messages are not resent), a mDNS query might get +lost if channel switching occurs during the query.
  • +
+

If your router has this problem, a reported “fix” is to (at least on +5GHz) use fixed channel and/or fixed (not dynamic) channel width.

+
    +
  • Avahi works at first, but new clients do not see UxPlay, or +clients that initially saw it stop doing so after they +disconnect.
  • +
+

This is usually because Avahi is only using the “loopback” network +interface, and is not receiving mDNS queries from new clients that were +not listening when UxPlay started.

+

To check this, after starting uxplay, use the utility +avahi-browse -a -t in a different terminal +window on the server to verify that the UxPlay AirTunes and +AirPlay services are correctly registered (only the AirTunes service is +used in the “Legacy” AirPlay Mirror mode used by UxPlay, but the AirPlay +service is used for the initial contact).

+

The results returned by avahi-browse should show entries for uxplay +like

+
+   eno1 IPv6 UxPlay                                        AirPlay Remote Video local
++   eno1 IPv4 UxPlay                                        AirPlay Remote Video local
++     lo IPv4 UxPlay                                        AirPlay Remote Video local
++   eno1 IPv6 863EA27598FE@UxPlay                           AirTunes Remote Audio local
++   eno1 IPv4 863EA27598FE@UxPlay                           AirTunes Remote Audio local
++     lo IPv4 863EA27598FE@UxPlay                           AirTunes Remote Audio local
+

If only the loopback (“lo”) entries are shown, a firewall on the +UxPlay host is probably blocking full DNS-SD service, and you need to +open the default UDP port 5353 for mDNS requests, as loopback-based +DNS-SD service is unreliable.

+

If the UxPlay services are listed by avahi-browse as above, but are +not seen by the client, the problem is likely to be a problem with the +local network.

+

2. +uxplay starts, but stalls after “Initialized server socket(s)” appears, +with the server name showing on the client (but the client +fails to connect when the UxPlay server is selected).

+

This shows that a DNS-SD service is working, clients hear +UxPlay is available, but the UxPlay server is not receiving the response +from the client. This is usually because a firewall on the server is +blocking the connection request from the client. (One user who insisted +that the firewall had been turned off turned out to have had +two active firewalls (firewalld and ufw) +both running on the server!) If possible, either turn off the +firewall to see if that is the problem, or get three consecutive network +ports, starting at port n, all three in the range 1024-65535, opened for +both tcp and udp, and use “uxplay -p n” (or open UDP 7011,6001,6000 TCP +7100,7000,7001 and use “uxplay -p”).

+

If you are really sure there is no firewall, you may need to +investigate your network transmissions with a tool like netstat, but +almost always this is a firewall issue.

+

3. +Problems after the client-server connection has been made:

+

If you do not see the message +raop_rtp_mirror starting mirroring, something went wrong +before the client-server negotiations were finished. For such problems, +use “uxplay -d” (debug log option) to see what is happening: it will +show how far the connection process gets before the failure occurs. You +can compare your debug output to that from a successful start of UxPlay +in the UxPlay +Wiki.

+

If UxPlay reports that mirroring started, but you get no +video or audio, the problem is probably from a GStreamer plugin that +doesn’t work on your system (by default, GStreamer uses the +“autovideosink” and “autoaudiosink” algorithms to guess what are the +“best” plugins to use on your system). A different reason for no audio +occurred when a user with a firewall only opened two udp network ports: +three are required (the third one receives the audio +data).

+

Raspberry Pi devices (Pi 4B+ and earlier: this +does not apply to the Pi 5, which does not provide hardware h264 +decoding, and does not need it) work best with hardware GPU h264 +video decoding if the Video4Linux2 plugin in GStreamer v1.20.x or +earlier has been patched (see the UxPlay Wiki +for patches). This is fixed in GStreamer-1.22, and by backport patches +from this in distributions such as Raspberry Pi OS (Bullseye): +use option -bt709 with the GStreamer-1.18.4 from +Raspberry Pi OS. This also needs the bcm2835-codec kernel +module that is not in the standard Linux kernel (it is available in +Raspberry Pi OS, Ubuntu and Manjaro).

+
    +
  • If this kernel module is not available in your Raspberry Pi +operating system, or if GStreamer < 1.22 is not patched, use option +-avdec for software h264-decoding.
  • +
+

Sometimes “autovideosink” may select the OpenGL renderer +“glimagesink” which may not work correctly on your system. Try the +options “-vs ximagesink” or “-vs xvimagesink” to see if using one of +these fixes the problem.

+

Other reported problems are connected to the GStreamer VAAPI plugin +(for hardware-accelerated Intel graphics, but not NVIDIA graphics). Use +the option “-avdec” to force software h264 video decoding: this should +prevent autovideosink from selecting the vaapisink videosink. +Alternatively, find out if the gstreamer1.0-vaapi plugin is installed, +and if so, uninstall it. (If this does not fix the problem, you can +reinstall it.)

+

There are some reports of other GStreamer problems with +hardware-accelerated Intel HD graphics. One user (on Debian) solved this +with “sudo apt install intel-media-va-driver-non-free”. This is a driver +for 8’th (or later) generation “*-lake” Intel chips, that seems to be +related to VAAPI accelerated graphics.

+

If you do have Intel HD graphics, and have installed the +vaapi plugin, but -vs vaapisink does not work, check that +vaapi is not “blacklisted” in your GStreamer installation: run +gst-inspect-1.0 vaapi, if this reports +0 features, you need to +export GST_VAAPI_ALL_DRIVERS=1 before running uxplay, or +set this in the default environment.

+

You can try to fix audio or video problems by using the +“-as <audiosink>” or +“-vs <videosink>” options to choose the GStreamer +audiosink or videosink , rather than letting GStreamer choose one for +you. (See above, in Starting and +running UxPlay for choices of <audiosink> or +<videosink>.)

+

The “OpenGL renderer” window created on Linux by “-vs glimagesink” +sometimes does not close properly when its “close” button is clicked. +(this is a GStreamer issue). You may need to terminate uxplay with +Ctrl-C to close a “zombie” OpenGl window. If similar problems happen +when the client sends the “Stop Mirroring” signal, try the no-close +option “-nc” that leaves the video window open.

+

4. GStreamer issues +(missing plugins, etc.):

+
    +
  • clearing the user’s GStreamer cache with +rm -rf ~/.cache/gstreamer-1.0/* may be the solution to +problems where gst-inspect-1.0 does not show a plugin that you believe +is installed. The cache will be regenerated next time GStreamer is +started. This is the solution to puzzling problems that turn out +to come from corruption of the cache, and should be tried +first.
  • +
+

If UxPlay fails to start, with a message that a required GStreamer +plugin (such as “libav”) was not found, first check with the GStreamer +tool gst-inspect-1.0 to see what GStreamer knows is available. (You may +need to install some additional GStreamer “tools” package to get +gst-inspect-1.0). For, e.g. a libav problem, check with +“gst-inspect-1.0 libav”. If it is not shown as available to +GStreamer, but your package manager shows the relevant package as +installed (as one user found), try entirely removing and reinstalling +the package. That user found that a solution to a “Required +gstreamer plugin ‘libav’ not found” message that kept recurring +was to clear the user’s gstreamer cache.

+

If it fails to start with an error like +‘no element "avdec_aac"’ this is because even though +gstreamer-libav is installed. it is incomplete because some plugin +features are missing: “gst-inspect-1.0 | grep avdec_aac” +will show if avdec_aac is available. Unlike other GStreamer plugins, the +libav plugin is a front end to FFmpeg codecs which provide avdec_*.

+
    +
  • Some distributions (RedHat, SUSE, etc) provide incomplete +versions of FFmpeg because of patent issues with codecs used by certain +plugins. In those cases there will be some “extra package” provider like +RPM fusion (RedHat), packman (SUSE) where you can +get complete packages (your distribution will usually provide +instructions for this, Mageia puts them in an optional “tainted” repo). +The packages needed may be “ffmpeg*” or “libav*” packages: the GStreamer +libav plugin package does not contain any codecs itself, it just +provides a way for GStreamer to use ffmpeg/libav codec libraries which +must be installed separately. For similar reasons, distributions may +ship incomplete packages of GStreamer “plugins-bad”. Use user on Fedora +thought they had installed from rpmfusion, but the system had not +obeyed: “Adding –allowerasing to the dnf command fixed it after a +restart”.

  • +
  • starting with release UxPlay-1.65.3, UxPlay will continue to +function, but without audio in mirror mode, if avdec_aac is +missing.

  • +
+

To troubleshoot GStreamer execute “export GST_DEBUG=2” to set the +GStreamer debug-level environment-variable in the terminal where you +will run uxplay, so that you see warning and error messages; see GStreamer +debugging tools for how to see much more of what is happening inside +GStreamer. Run “gst-inspect-1.0” to see which GStreamer plugins are +installed on your system.

+

Some extra GStreamer packages for special plugins may need to be +installed (or reinstalled: a user using a Wayland display system as an +alternative to X11 reported that after reinstalling Lubuntu 18.4, UxPlay +would not work until gstreamer1.0-x was installed, presumably for +Wayland’s X11-compatibility mode). Different distributions may break up +GStreamer 1.x into packages in different ways; the packages listed above +in the build instructions should bring in other required GStreamer +packages as dependencies, but will not install all possible plugins.

+

The GStreamer video pipeline, which is shown in the initial output +from uxplay -d, has the default form

+
appsrc name=video_source ! queue ! h264parse ! decodebin ! videoconvert ! autovideosink name=video_sink sync=false
+

The pipeline is fully configurable: default elements “h264parse”, +“decodebin”, “videoconvert”, and “autovideosink” can respectively be +replaced by using uxplay options -vp, -vd, +-vc, and -vs, if there is any need to modify +it (entries can be given in quotes “…” to include options).

+

5. Mirror screen +freezes (a network problem):

+

This can happen if the TCP video stream from the client stops +arriving at the server, probably because of network problems (the UDP +audio stream may continue to arrive). At 3-second intervals, UxPlay +checks that the client is still connected by sending it a request for a +NTP time signal. If a reply is not received from the client within a 0.3 +sec time-window, an “ntp timeout” is registered. If a certain number +(currently 5) of consecutive ntp timeouts occur, UxPlay assumes that the +client is “dead”, and resets the connection, becoming available for +connection to a new client, or reconnection to the previous one. +Sometimes the connection may recover before the timeout limit is +reached, and if the default limit is not right for your network, it can +be modified using the option “-reset n”, where n is +the desired timeout-limit value (n = 0 means “no limit”). If +the connection starts to recover after ntp timeouts, a corrupt video +packet from before the timeout may trigger a “connection reset by peer” +error, which also causes UxPlay to reset the connection.

+
    +
  • When the connection is reset, the “frozen” mirror screen of the +previous connection is left in place, but does not +block new connections, and will be taken over by a new client connection +when it is made.
  • +
+

6. +Protocol issues (with decryption of the encrypted audio and video +streams sent by the client).

+

A protocol failure may trigger an unending stream of error messages, +and means that the audio decryption key (also used in video decryption) +was not correctly extracted from data sent by the client.

+

The protocol was modifed in UxPlay-1.65 after it was discovered that +the client-server “pairing” step could be avoided (leading to a much +quicker connection setup, without a 5 second delay) by disabling +“Supports Legacy Pairing” (bit 27) in the “features” code UxPlay +advertises on DNS-SD Service Discovery. Most clients will then not +attempt the setup of a “shared secret key” when pairing, which is used +by AppleTV for simultaneous handling of multiple clients (UxPlay only +supports one client at a time). This change is now well-tested, +but in case it causes any protocol failures, UxPlay can be reverted to +the previous behavior by uncommenting the previous “FEATURES_1” setting +(and commenting out the new one) in lib/dnssdint.h, and then rebuilding +UxPlay. (“Pairing” is re-enabled when the new Apple-style +one-time “pin” authentication is activated by running UxPlay with the +“-pin” option introduced in UxPlay 1.67.)

+

Protocol failure should not happen for iOS 9.3 or later clients. +However, if a client uses the same older version of the protocol that is +used by the Windows-based AirPlay client emulator AirMyPC, the +protocol can be switched to the older version by the setting +OLD_PROTOCOL_CLIENT_USER_AGENT_LIST in +UxPlay/lib/global.h. UxPlay reports the client’s “User +Agent” string when it connects. If some other client also fails to +decrypt all audio and video, try adding its “User Agent” string in place +of “xxx” in the entry “AirMyPC/2.0;xxx” in global.h and rebuild +uxplay.

+

Note that for DNS-SD Service Discovery, Uxplay declares itself to be +an AppleTV3,2 (a 32 bit device) with a sourceVersion 220.68; this can +also be changed in global.h. UxPlay also works if it declares itself as +an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen, +introduced 2017, running tvOS 12.2.1), so it does not seem to matter +what version UxPlay claims to be.

+

Changelog

+

1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially +only for YouTube movies. Fix issue with NTP timeout on Windows.

+

1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x +2160). Fix issue with GStreamer >= 1.24 when client sleeps, then +wakes.

+

1.69 2024-08-09 Internal improvements (e.g. in -nohold option, +identifying GStreamer videosink selected by autovideosink, finding X11 +display) in anticipation of future HLS video support. New -nofreeze +option to not leave frozen video in place when a network connection is +reset. Fixes for GStreamer-1.24.x changes.

+

1.68 2023-12-31 New simpler (default) method for generating a +persistent public key from the server MAC address (which can now be set +with the -m option). (The previous method is still available with -key +option). New option -reg to maintain a register of pin-authenticated +clients. Corrected volume-control: now interprets AirPlay volume range +-30dB:0dB as decibel gain attenuation, with new option -db low[:high] +for “flat” rescaling of the dB range. Add -taper option for a “tapered” +AirPlay volume-control profile.

+

1.67 2023-11-30 Add support for Apple-style one-time pin +authentication of clients with option “-pin”: (uses SRP6a authentication +protocol and public key persistence). Detection with error message of +(currently) unsupported H265 video when requesting high resolution over +wired ethernet. Removed rpi* options (which are not valid with new +Raspberry Pi model 5, and can be replaced by combinations of other +options). Added optional argument “mac” to “-m” option, to specify a +replacement MAC address/Device ID. Update llhttp to v. 9.1.3. Add -dacp +option for exporting current client DACP info (for remotes).

+

1.66 2023-09-05 Fix IPV6 support. Add option to restrict clients to +those on a list of allowed deviceIDs, or to block connections from +clients on a list of blocked deviceIDs. Fix for #207 from @thiccaxe (screen lag in +vsync mode after client wakes from sleep).

+

1.65.3 2023-07-23 Add RPM spec file; add warning if required +gstreamer libav feature “avdec_aac” is missing: (this occurs in +RPM-based distributions that ship an incomplete FFmpeg for Patent or +License reasons, and rely on users installing an externally-supplied +complete FFmpeg). Mirror-mode airplay will now work without audio if +avdec_aac is missing.

+

1.65 2023-06-03 Eliminate pair_setup part of connection protocol to +allow faster connections with clients (thanks to @shuax #176 for this discovery); to revert, +uncomment a line in lib/dnssdint.h. Disconnect from audio device when +connection closes, to not block its use by other apps if uxplay is +running but not connected. Fix for AirMyPC client (broken since 1.60), +so its older non-NTP timestamp protocol works with -vsync. Corrected +parsing of configuration file entries that were in quotes.

+

1.64 2023-04-23 Timestamp-based synchronization of audio and video is +now the default in Mirror mode. (Use “-vsync no” to restore previous +behavior.) A configuration file can now be used for startup options. +Also some internal cleanups and a minor bugfix that fixes #192.

+

1.63 2023-02-12 Reworked audio-video synchronization, with new +options -vsync (for Mirror mode) and -async (for Audio-Only mode, to +sync with client video). Option -vsync makes software h264 decoding of +streamed videos with option -avdec viable on some recent Raspberry Pi +models. Internal change: all times are now processed in nanoseconds +units. Removed -ao option introduced in 1.62.

+

1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user +synchronization of ALAC audio playing on the server with video, song +lyrics, etc. playing on the client. x = 5.0 appears to be optimal in +many cases. Quality fixes: cleanup in volume changes, timestamps, some +bugfixes.

+

1.61 2022-12-30 Removed -t option (workaround for an Avahi issue, +correctly solved by opening network port UDP 5353 in firewall). Remove +-g debug flag from CMAKE_CFLAGS. Postpend (instead of prepend) build +environment CFLAGS to CMAKE_CFLAGS. Refactor parts of uxplay.cpp

+

1.60 2022-12-15 Added exit with error message if DNSServiceRegister +fails (instead of just stalling). Test for Client’s attempt to using +unsupported AirPlay 2 “REMOTE CONTROL” protocol (with no timing +channel), and exit if this occurs. Reworked metadata processing to +correctly parse DMAP header (previous version worked with DMAP messages +currently received, but was not correct).

+

1.59 2022-12-12 remove “ZOOMFIX” compile option and make compilation +with X11-dependence the default if X11 development libraries are +detected (this now also provides fullscreen mode with a F11 or Alt+Enter +key toggle); ZOOMFIX is now automatically applied for GStreamer < +1.20. New cmake option -DNO_X11_DEPS compiles uxplay without X11 +dependence. Reworked internal metadata handling. Fix segfault with “-vs +0”.

+

1.58 2022-10-29 Add option “-nohold” that will drop existing +connections when a new client connects. Update llhttp to v8.1.0.

+

1.57 2022-10-09 Minor fixes: (fix coredump on AUR on “stop +mirroring”, occurs when compiled with AUR CFLAGS -DFORTIFY_SOURCE); +graceful exit when required plugins are missing; improved support for +builds on Windows. Include audioresample in GStreamer audio +pipeline.

+

1.56 2022-09-01 Added support for building and running UxPlay-1.56 on +Windows (no changes to Unix (Linux, *BSD, macOS) codebase.)

+

1.56 2022-07-30 Remove -bt709 from -rpi, -rpiwl, -rpifb as GStreamer +is now fixed.

+

1.55 2022-07-04 Remove the bt709 fix from -v4l2 and create a new +-bt709 option (previous “-v4l2” is now “-v4l2 -bt709”). This allows the +currently-required -bt709 option to be used on its own on RPi without +-v4l2 (sometimes this give better results).

+

1.54 2022-06-25 Add support for “Cover Art” display in Audio-only +(ALAC) mode. Reverted a change that caused VAAPI to crash with AMD +POLARIS graphics cards. Minor internal changes to plist code and uxplay +option parsing.

+

1.53 2022-06-13 Internal changes to audio sync code, revised +documentation, Minor bugfix (fix assertion crash when resent audio +packets are empty).

+

1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted +streaming debug output (readable aligned timestamps with decimal points +in seconds). Eliminate memory leaks (found by valgrind). Support for +display of ALAC (audio-only) metadata (soundtrack artist names, titles +etc.) in the uxplay terminal.

+

1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option +-v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, +-v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from +1.48 that broke reconnection after “Stop Mirroring” is sent by +client.

+

1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI +plugins only), Changed -rpi to be for framebuffer (“lite”) RPi systems +and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop +systems. Also modified timestamps from “DTS” to “PTS” for latency +improvement, plus internal cleanups.

+

1.49 2022-03-28 Addded options for dumping video and/or audio to +file, for debugging, etc. h264 PPS/SPS NALU’s are shown with -d. Fixed +video-not-working for M1 Mac clients.

+

1.48 2022-03-11 Made the GStreamer video pipeline fully configurable, +for use with hardware h264 decoding. Support for Raspberry Pi.

+

1.47 2022-02-05 Added -FPSdata option to display (in the terminal) +regular reports sent by the client about video streaming performance. +Internal cleanups of processing of video packets received from the +client. Added -reset n option to reset the connection after n ntp +timeouts (also reset after “connection reset by peer” error in video +stream).

+

1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken +hardware acceleration): once again use decodebin in the video pipeline; +introduce new option “-avdec” to force software h264 decoding by libav +h264, if needed (to prevent selection of vaapisink by autovideosink). +Update llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2. +Restrict connections to one client at a time (second client must now +wait for first client to disconnect).

+

1.45 2022-01-10 New behavior: close video window when client requests +“stop mirroring”. (A new “no close” option “-nc” is added for users who +wish to retain previous behavior that does not close the video +window).

+

1.44 2021-12-13 Omit hash of aeskey with ecdh_secret for an AirMyPC +client; make an internal rearrangement of where this hash is done. Fully +report all initial communications between client and server in -d debug +mode. Replace decodebin in GStreamer video pipeline by h264-specific +elements.

+

1.43 2021-12-07 Various internal changes, such as tests for +successful decryption, uniform treatment of informational/debug +messages, etc., updated README.

+

1.42 2021-11-20 Fix MAC detection to work with modern Linux interface +naming practices, MacOS and *BSD.

+

1.41 2021-11-11 Further cleanups of multiple audio format support +(internal changes, separated RAOP and GStreamer audio/video startup)

+

1.40 2021-11-09 Cleanup segfault in ALAC support, manpage location +fix, show request Plists in debug mode.

+

1.39 2021-11-06 Added support for Apple Lossless (ALAC) audio +streams.

+

1.38 2021-10-8 Add -as audiosink option to allow user to +choose the GStreamer audiosink.

+

1.37 2021-09-29 Append “@hostname” to AirPlay Server name, where +“hostname” is the name of the server running uxplay (reworked change in +1.36).

+

1.36 2021-09-29 Implemented suggestion (by @mrbesen and @PetrusZ) to use hostname of machine runing +uxplay as the default server name

+

1.35.1 2021-09-28 Added the -vs 0 option for streaming audio, but not +displaying video.

+

1.35 2021-09-10 now uses a GLib MainLoop, and builds on macOS (tested +on Intel Mac, 10.15 ). New option -t timeout for relaunching +server if no connections were active in previous timeout +seconds (to renew Bonjour registration).

+

1.341 2021-09-04 fixed: render logger was not being destroyed by +stop_server()

+

1.34 2021-08-27 Fixed “ZOOMFIX”: the X11 window name fix was only +being made the first time the GStreamer window was created by uxplay, +and not if the server was relaunched after the GStreamer window was +closed, with uxplay still running. Corrected in v. 1.34

+

Building OpenSSL >= +1.1.1 from source.

+

If you need to do this, note that you may be able to use a newer +version (OpenSSL-3.0.1 is known to work). You will need the standard +development toolset (autoconf, automake, libtool). Download the source +code from https://www.openssl.org/source/. Install the downloaded +openssl by opening a terminal in your Downloads directory, and unpacking +the source distribution: (“tar -xvzf openssl-3.0.1.tar.gz ; cd +openssl-3.0.1”). Then build/install with “./config ; make ; sudo make +install_dev”. This will typically install the needed library +libcrypto.*, either in /usr/local/lib or +/usr/local/lib64.

+

(Ignore the following for builds on MacOS:) On some systems +like Debian or Ubuntu, you may also need to add a missing entry +/usr/local/lib64 in /etc/ld.so.conf (or place a file +containing “/usr/local/lib64/libcrypto.so” in /etc/ld.so.conf.d) and +then run “sudo ldconfig”.

+

Building libplist >= +2.0.0 from source.

+

(Note: on Debian 9 “Stretch” or Ubuntu 16.04 LTS editions, you +can avoid this step by installing libplist-dev and libplist3 from Debian +10 or Ubuntu 18.04.) As well as the usual build tools (autoconf, +automake, libtool), you may need to also install some libpython*-dev +package. Download the latest source with git from https://github.com/libimobiledevice/libplist, or get the +source from the Releases section (use the *.tar.bz2 release, +not the *.zip or *.tar.gz versions): download libplist-2.3.0, +then unpack it (“tar -xvjf libplist-2.3.0.tar.bz2 ; cd libplist-2.3.0”), +and build/install it: (“./configure ; make ; sudo make install”). This +will probably install libplist-2.0.* in /usr/local/lib. The new +libplist-2.3.0 release should be compatible with UxPlay; libplist-2.2.0 +is also available if there are any issues.

+

(Ignore the following for builds on MacOS:) On some systems +like Debian or Ubuntu, you may also need to add a missing entry +/usr/local/lib in /etc/ld.so.conf (or place a file +containing “/usr/local/lib/libplist-2.0.so” in /etc/ld.so.conf.d) and +then run “sudo ldconfig”.

+

Disclaimer

+

All the resources in this repository are written using only freely +available information from the internet. The code and related resources +are meant for educational purposes only. It is the responsibility of the +user to make sure all local laws are adhered to.

+

This project makes use of a third-party GPL library for handling +FairPlay. The legal status of that library is unclear. Should you be a +representative of Apple and have any objections against the legality of +the library and its use in this project, please contact the developers +and the appropriate steps will be taken.

+

Given the large number of third-party AirPlay receivers (mostly +closed-source) available for purchase, it is our understanding that an +open source implementation of the same functionality wouldn’t violate +any of Apple’s rights either.

+

UxPlay authors

+

[adapted from fdraschbacher’s notes on RPiPlay +antecedents]

+

The code in this repository accumulated from various sources over +time. Here is an attempt at listing the various authors and the +components they created:

+

UxPlay was initially created by antimof from +RPiPlay, by replacing its Raspberry-Pi-adapted OpenMAX video and audio +rendering system with GStreamer rendering for desktop Linux systems; the +antimof work on code in renderers/ was later backported to +RPiPlay, and the antimof project became dormant, but was later revived +at the current GitHub site +to serve a wider community of users.

+

The previous authors of code included in UxPlay by inheritance from +RPiPlay include:

+
    +
  • EstebanKubata: Created a FairPlay library called PlayFair. Located +in the lib/playfair folder. License: GNU GPL
  • +
  • Juho Vähä-Herttua and contributors: Created an +AirPlay audio server called ShairPlay, including +support for Fairplay based on PlayFair. Most of the code in +lib/ originally stems from this project. License: GNU +LGPLv2.1+
  • +
  • dsafa22: Created an AirPlay 2 mirroring server AirplayServer (seems +gone now), for Android based on ShairPlay. Code is preserved here, and see here for the +description of the analysis of the AirPlay 2 mirror protocol that made +RPiPlay possible, by the AirplayServer author. All code in +lib/ concerning mirroring is dsafa22’s work. License: GNU +LGPLv2.1+
  • +
  • Florian Draschbacher (FD-) and contributors: +adapted dsafa22’s Android project for the Raspberry Pi, with extensive +cleanups, debugging and improvements. The project RPiPlay is basically a port of +dsafa22’s code to the Raspberry Pi, utilizing OpenMAX and OpenSSL for +better performance on the Pi. License GPL v3. FD- has written an +interesting note on the history of Airplay +protocol versions, available at the RPiPlay github repository.
  • +
+

Independent of UxPlay, but used by it and bundled with it:

+
    +
  • Fedor Indutny (of Node.js, and formerly Joyent, +Inc) and contributors: Created an http parsing library called llhttp. Located at +lib/llhttp/. License: MIT
  • +
diff --git a/README.md b/README.md index 403b638..016adc7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,1954 @@ -# uxplayer +# UxPlay 1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows). +### **Now developed at the GitHub site (where ALL user issues should be posted, and latest versions can be found).** + +- **\*NEW in v1.71**: Support for (YouTube) HLS (HTTP Live Streaming) + video with the new "-hls" option.\* Click on the airplay icon in the + YouTube app to stream video. (You may need to wait until + advertisements have finished or been skipped before clicking the + YouTube airplay icon.) **Please report any issues with this new + feature of UxPlay**. + +## Highlights: + +- GPLv3, open source. +- Originally supported only AirPlay Mirror protocol, now has added + support for AirPlay Audio-only (Apple Lossless ALAC) streaming from + current iOS/iPadOS clients. **Now with support for Airplay HLS + video-streaming (currently only YouTube video).** +- macOS computers (2011 or later, both Intel and "Apple Silicon" M1/M2 + systems) can act either as AirPlay clients, or as the server running + UxPlay. Using AirPlay, UxPlay can emulate a second display for macOS + clients. +- Support for older iOS clients (such as 32-bit iPad 2nd gen., iPod + Touch 5th gen. and iPhone 4S, when upgraded to iOS 9.3.5, or later + 64-bit devices), plus a Windows AirPlay-client emulator, AirMyPC. +- Uses GStreamer plugins for audio and video rendering (with options + to select different hardware-appropriate output "videosinks" and + "audiosinks", and a fully-user-configurable video streaming + pipeline). +- Support for server behind a firewall. +- Raspberry Pi support **both with and without hardware video + decoding** by the Broadcom GPU. _Tested on Raspberry Pi Zero 2 W, 3 + Model B+, 4 Model B, and 5._ +- Support for running on Microsoft Windows (builds with the MinGW-64 + compiler in the unix-like MSYS2 environment). + +Note: AirPlay2 multi-room audio streaming is not supported: use +[shairport-sync](https://github.com/mikebrady/shairport-sync) for that. + +## Packaging status (Linux and \*BSD distributions) + +[![Current Packaging +status](https://repology.org/badge/vertical-allrepos/uxplay.svg)](https://repology.org/project/uxplay/versions). + +- Install uxplay on Debian-based Linux systems with + "`sudo apt install uxplay`"; on FreeBSD with + "`sudo pkg install uxplay`". Also available on Arch-based systems + through AUR. Since v. 1.66, uxplay is now also packaged in RPM + format by Fedora 38 ("`sudo dnf install uxplay`"). + +- For other RPM-based distributions which have not yet packaged + UxPlay, a RPM "specfile" **uxplay.spec** is now provided with recent + [releases](https://github.com/FDH2/UxPlay/releases) (see their + "Assets"), and can also be found in the UxPlay source top directory. + See the section on using this specfile for [building an installable + RPM package](#building-an-installable-rpm-package). + +After installation: + +- (On Linux and \*BSD): if a firewall is active on the server hosting + UxPlay, make sure the default network port (UDP 5353) for + mDNS/DNS-SD queries is open (see [Troubleshooting](#troubleshooting) + below for more details); also open three UDP and three TCP ports for + Uxplay, and use the "uxplay -p ``{=html}" option (see + "`man uxplay`" or "`uxplay -h`"). + +- Even if you install your distribution's pre-compiled uxplay binary + package, you may need to read the instructions below for [running + UxPlay](#running-uxplay) to see which of your distribution's + **GStreamer plugin packages** you should also install. + +- For Audio-only mode (Apple Music, etc.) best quality is obtained + with the option "uxplay -async", but there is then a 2 second + latency imposed by iOS. + +- Add any UxPlay options you want to use as defaults to a startup file + `~/.uxplayrc` (see "`man uxplay`" or "`uxplay -h`" for format and + other possible locations). In particular, if your system uses + PipeWire audio or Wayland video systems, you may wish to add "as + pipewiresink" or "vs waylandsink" as defaults to the file. _(Output + from terminal commands "ps waux \| grep pulse" or "pactl info" will + contain "pipewire" if your Linux/BSD system uses it)._ + +- On Raspberry Pi: models using hardware h264 video decoding by the + Broadcom GPU (models 4B and earlier) may require the uxplay option -bt709. + If you use Ubuntu 22.10 or earlier, GStreamer must + be [patched](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches) + to use hardware video decoding by the Broadcom GPU (also recommended + but optional for Raspberry Pi OS (Bullseye): the patched GStreamer + does not need option " -bt709`". The need for -bt709 when hardware video + decoding is used seems to have reappeared + starting with GStreamer-1.22. + +To (easily) compile the latest UxPlay from source, see the section +[Getting UxPlay](#getting-uxplay). + +# Detailed description of UxPlay + +This project is a GPLv3 open source unix AirPlay2 Mirror server for +Linux, macOS, and \*BSD. It was initially developed by +[antimof](http://github.com/antimof/Uxplay) using code from +OpenMAX-based [RPiPlay](https://github.com/FD-/RPiPlay), which in turn +derives from [AirplayServer](https://github.com/KqsMea8/AirplayServer), +[shairplay](https://github.com/juhovh/shairplay), and +[playfair](https://github.com/EstebanKubata/playfair). (The antimof site +is no longer involved in development, but periodically posts updates +pulled from the new main [UxPlay site](https://github.com/FDH2/UxPlay)). + +UxPlay is tested on a number of systems, including (among others) Debian +(10 "Buster", 11 "Bullseye", 12 "Bookworm"), Ubuntu (20.04 LTS, 22.04 +LTS, 23.04 (also Ubuntu derivatives Linux Mint, Pop!\_OS), Red Hat and +clones (Fedora 38, Rocky Linux 9.2), openSUSE Leap 15.5, Mageia 9, +OpenMandriva "ROME", PCLinuxOS, Arch Linux, Manjaro, and should run on +any Linux system. Also tested on macOS Catalina and Ventura (Intel) and +Sonoma (M2), FreeBSD 14.0, Windows 10 and 11 (64 bit). + +On Raspberry Pi 4 model B, it is tested on Raspberry Pi OS (Bullseye and +Bookworm) (32- and 64-bit), Ubuntu 22.04 LTS and 23.04, Manjaro RPi4 +23.02, and (without hardware video decoding) on openSUSE 15.5. Also +tested on Raspberry Pi Zero 2 W, 3 model B+, and now 5. + +Its main use is to act like an AppleTV for screen-mirroring (with audio) +of iOS/iPadOS/macOS clients (iPhone, iPod Touch, iPad, Mac computers) on +the server display of a host running Linux, macOS, or other unix (and +now also Microsoft Windows). UxPlay supports Apple's AirPlay2 protocol +using "Legacy Protocol", but some features are missing. (Details of what +is publicly known about Apple's AirPlay 2 protocol can be found +[here](https://openairplay.github.io/airplay-spec/), +[here](https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol) +and [here](https://emanuelecozzi.net/docs/airplay2); see also +[pyatv](https://pyatv.dev/documentation/protocols) which could be a +resource for adding modern protocols.) While there is no guarantee that +future iOS releases will keep supporting "Legacy Protocol", iOS 17 +continues support. + +The UxPlay server and its client must be on the same local area network, +on which a **Bonjour/Zeroconf mDNS/DNS-SD server** is also running (only +DNS-SD "Service Discovery" service is strictly necessary, it is not +necessary that the local network also be of the ".local" mDNS-based +type). On Linux and BSD Unix servers, this is usually provided by +[Avahi](https://www.avahi.org), through the avahi-daemon service, and is +included in most Linux distributions (this service can also be provided +by macOS, iOS or Windows servers). + +Connections to the UxPlay server by iOS/MacOS clients can be initiated +both in **AirPlay Mirror** mode (which streams lossily-compressed AAC +audio while mirroring the client screen, or in the alternative **AirPlay +Audio** mode which streams Apple Lossless (ALAC) audio without screen +mirroring. In **Audio** mode, metadata is displayed in the uxplay +terminal; if UxPlay option `-ca ` is used, the accompanying cover +art is also output to a periodically-updated file ``, and can be +viewed with a (reloading) graphics viewer of your choice. _Switching +between_ **Mirror** _and_ **Audio** _modes during an active connection +is possible: in_ **Mirror** _mode, stop mirroring (or close the mirror +window) and start an_ **Audio** _mode connection, switch back by +initiating a_ **Mirror** _mode connection; cover-art display +stops/restarts as you leave/re-enter_ **Audio** _mode._ + +- **Note that Apple video-DRM (as found in "Apple TV app" content on + the client) cannot be decrypted by UxPlay, and the Apple TV app + cannot be watched using UxPlay's AirPlay Mirror mode (only the + unprotected audio will be streamed, in AAC format).** + +- **With the new "-hls" option, UxPlay now also supports non-Mirror + AirPlay video streaming (where the client controls a web server on + the AirPlay server that directly receives HLS content to avoid it + being decoded and re-encoded by the client). This currently only + supports streaming of YouTube videos. Without the -hls option, using + the icon for AirPlay video in apps such as the YouTube app will only + send audio (in lossless ALAC format) without the accompanying + video.** + +### Possibility for using hardware-accelerated h264/h265 video-decoding, if available. + +UxPlay uses [GStreamer](https://gstreamer.freedesktop.org) "plugins" for +rendering audio and video. This means that video and audio are supported +"out of the box", using a choice of plugins. AirPlay streams video in +h264 format: gstreamer decoding is plugin agnostic, and uses accelerated +GPU hardware h264 decoders if available; if not, software decoding is +used. + +- **VAAPI for Intel and AMD integrated graphics, NVIDIA with "Nouveau" + open-source driver** + + With an Intel or AMD GPU, hardware decoding with the open-source + VAAPI gstreamer plugin is preferable. The open-source "Nouveau" + drivers for NVIDIA graphics are also in principle supported: see + [here](https://nouveau.freedesktop.org/VideoAcceleration.html), but + this requires VAAPI to be supplemented with firmware extracted from + the proprietary NVIDIA drivers. + +- **NVIDIA with proprietary drivers** + + The `nvh264dec` plugin (included in gstreamer1.0-plugins-bad since + GStreamer-1.18.0) can be used for accelerated video decoding on the + NVIDIA GPU after NVIDIA's CUDA driver `libcuda.so` is installed. For + GStreamer-1.16.3 or earlier, the plugin is called `nvdec`, and must + be [built by the + user](https://github.com/FDH2/UxPlay/wiki/NVIDIA-nvdec-and-nvenc-plugins). + +- **Video4Linux2 support for h264 hardware decoding on Raspberry Pi + (Pi 4B and older)** + + Raspberry Pi (RPi) computers (tested on Pi 4 Model B) can now run + UxPlay using software video decoding, but hardware-accelerated + h264/h265 decoding by firmware in the Pi's Broadcom 2835 GPU is + prefered. UxPlay accesses this using the GStreamer-1.22 Video4Linux2 + (v4l2) plugin; Uses the out-of-mainline Linux kernel module + bcm2835-codec maintained by Raspberry Pi, so far only included in + Raspberry Pi OS, and two other distributions (Ubuntu, Manjaro) + available with Raspberry Pi Imager. _(For GStreamer \< 1.22, see the + [UxPlay + Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches))_. + Pi model 5 has no support for hardware H264 decoding, as its CPU is + powerful enough for satisfactory software H264 decoding + +- **Support for h265 (HEVC) hardware decoding on Raspberry Pi (Pi 4 + model B and Pi 5)** + + These Raspberry Pi models have a dedicated HEVC decoding block (not + the GPU), with a driver "rpivid" which is not yet in the mainline + Linux kernel (but is planned to be there in future). Unfortunately + it produces decoded video in a non-standard pixel format (NC30 or + "SAND") which will not be supported by GStreamer until the driver is + in the mainline kernel; without this support, UxPlay support for + HEVC hardware decoding on Raspberry Pi will not work. + +### Note to packagers: + +UxPlay's GPLv3 license does not have an added "GPL exception" explicitly +allowing it to be distributed in compiled form when linked to OpenSSL +versions **prior to v. 3.0.0** (older versions of OpenSSL have a license +clause incompatible with the GPL unless OpenSSL can be regarded as a +"System Library", which it is in \*BSD). Many Linux distributions treat +OpenSSL as a "System Library", but some (e.g. Debian) do not: in this +case, the issue is solved by linking with OpenSSL-3.0.0 or later. + +# Getting UxPlay + +Either download and unzip +[UxPlay-master.zip](https://github.com/FDH2/UxPlay/archive/refs/heads/master.zip), +or (if git is installed): "git clone https://github.com/FDH2/UxPlay". +You can also download a recent or earlier version listed in +[Releases](https://github.com/FDH2/UxPlay/releases). + +- A recent UxPlay can also be found on the original [antimof + site](https://github.com/antimof/UxPlay); that original project is + inactive, but is usually kept current or almost-current with the + [active UxPlay github site](https://github.com/FDH2/UxPlay) (thank + you antimof!). + +## Building UxPlay on Linux (or \*BSD): + +### Debian-based systems: + +(Adapt these instructions for non-Debian-based Linuxes or \*BSD; for +macOS, see specific instruction below). See +[Troubleshooting](#troubleshooting) below for help with any +difficulties. + +You need a C/C++ compiler (e.g. g++) with the standard development +libraries installed. Debian-based systems provide a package +"build-essential" for use in compiling software. You also need +pkg-config: if it is not found by "`which pkg-config`", install +pkg-config or its work-alike replacement pkgconf. Also make sure that +cmake\>=3.10 is installed: "`sudo apt install cmake`" (add +`build-essential` and `pkg-config` (or `pkgconf`) to this if needed). + +Make sure that your distribution provides OpenSSL 1.1.1 or later, and +libplist 2.0 or later. (This means Debian 10 "Buster" based systems +(e.g, Ubuntu 18.04) or newer; on Debian 10 systems "libplist" is an +older version, you need "libplist3".) If it does not, you may need to +build and install these from source (see instructions at the end of this +README). + +If you have a non-standard OpenSSL installation, you may need to set the +environment variable OPENSSL_ROOT_DIR (_e.g._ , +"`export OPENSSL_ROOT_DIR=/usr/local/lib64`" if that is where it is +installed). Similarly, for non-standard (or multiple) GStreamer +installations, set the environment variable GSTREAMER_ROOT_DIR to the +directory that contains the ".../gstreamer-1.0/" directory of the +gstreamer installation that UxPlay should use (if this is _e.g._ +"\~/my_gstreamer/lib/gstreamer-1.0/", set this location with +"`export GSTREAMER_ROOT_DIR=$HOME/my_gstreamer/lib`"). + +- Most users will use the GStreamer supplied by their distribution, + but a few (in particular users of Raspberry Pi OS Lite Legacy + (Buster) on a Raspberry Pi model 4B who wish to stay on that + unsupported Legacy OS for compatibility with other apps) should + instead build a newer Gstreamer from source following [these + instructions]() + . **Do this _before_ building UxPlay**. + +In a terminal window, change directories to the source directory of the +downloaded source code ("UxPlay-\*", "\*" = "master" or the release tag +for zipfile downloads, "UxPlay" for "git clone" downloads), then follow +the instructions below: + +**Note:** By default UxPlay will be built with optimization for the +computer it is built on; when this is not the case, as when you are +packaging for a distribution, use the cmake option +`-DNO_MARCH_NATIVE=ON`. + +If you use X11 Windows on Linux or \*BSD, and wish to toggle in/out of +fullscreen mode with a keypress (F11 or Alt_L+Enter) UxPlay needs to be +built with a dependence on X11. Starting with UxPlay-1.59, this will be +done by default **IF** the X11 development libraries are installed and +detected. Install these with "`sudo apt install libx11-dev`". If +GStreamer \< 1.20 is detected, a fix needed by screen-sharing apps +(_e.g._, Zoom) will also be made. + +- If X11 development libraries are present, but you wish to build + UxPlay _without_ any X11 dependence, use the cmake option + `-DNO_X11_DEPS=ON`. + +1. `sudo apt install libssl-dev libplist-dev`". (_unless you need to + build OpenSSL and libplist from source_). +2. `sudo apt install libavahi-compat-libdnssd-dev` +3. `sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev`. + (\*_Skip if you built Gstreamer from source_) +4. `cmake .` (_For a cleaner build, which is useful if you modify the + source, replace this by_ "`mkdir build; cd build; cmake ..`": _you + can then delete the contents of the `build` directory if needed, + without affecting the source._) Also add any cmake "`-D`" options + here as needed (e.g, `-DNO_X11_DEPS=ON` or `-DNO_MARCH_NATIVE=ON`). +5. `make` +6. `sudo make install` (you can afterwards uninstall with + `sudo make uninstall` in the same directory in which this was run). + +This installs the executable file "`uxplay`" to `/usr/local/bin`, (and +installs a manpage to somewhere standard like +`/usr/local/share/man/man1` and README files to somewhere like +`/usr/local/share/doc/uxplay`). (If "man uxplay" fails, check if +\$MANPATH is set: if so, the path to the manpage (usually +/usr/local/share/man/) needs to be added to \$MANPATH .) The uxplay +executable can also be found in the build directory after the build +process, if you wish to test before installing (in which case the +GStreamer plugins must first be installed). + +### Building on non-Debian Linux and \*BSD + +\*\*For those with RPM-based distributions, a RPM spec file uxplay.spec +is also available: see [Building an installable rpm +package](#building-an-installable-rpm-package). + +- **Red Hat, or clones like CentOS (now continued as Rocky Linux or + Alma Linux):** (sudo dnf install, or sudo yum install) openssl-devel + libplist-devel avahi-compat-libdns_sd-devel gstreamer1-devel + gstreamer1-plugins-base-devel (+libX11-devel for fullscreen X11) + _(some of these may be in the "CodeReady" add-on repository, called + "PowerTools" by clones)_ + +- **Mageia, PCLinuxOS, OpenMandriva:** Same as Red Hat, except for + name changes: (Mageia) "gstreamer1.0-devel", + "gstreamer-plugins-base1.0-devel"; (OpenMandriva) + "libopenssl-devel", "gstreamer-devel", + "libgst-plugins-base1.0-devel". PCLinuxOS: same as Mageia, but uses + synaptic (or apt) as its package manager. + +- **openSUSE:** (sudo zypper install) libopenssl-3-devel (formerly + libopenssl-devel) libplist-2_0-devel (formerly libplist-devel) + avahi-compat-mDNSResponder-devel gstreamer-devel + gstreamer-plugins-base-devel (+ libX11-devel for fullscreen X11). + +- **Arch Linux** (_Also available as a package in AUR_): (sudo pacman + -Syu) openssl libplist avahi gst-plugins-base. + +- **FreeBSD:** (sudo pkg install) libplist gstreamer1. Either + avahi-libdns or mDNSResponder must also be installed to provide the + dns_sd library. OpenSSL is already installed as a System Library. + +#### Building an installable RPM package + +First-time RPM builders should first install the rpm-build and +rpmdevtools packages, then create the rpmbuild tree with +"`rpmdev-setuptree`". Then download and copy uxplay.spec into +`~/rpmbuild/SPECS`. In that directory, run +"`rpmdev-spectool -g -R uxplay.spec`" to download the corresponding +source file `uxplay-*.tar.gz` into `~/rpmbuild/SOURCES` +("rpmdev-spectool" may also be just called "spectool"); then run +"`rpmbuild -ba uxplay.spec`" (you will need to install any required +dependencies this reports). This should create the uxplay RPM package in +a subdirectory of `~/rpmbuild/RPMS`. (**uxplay.spec** is tested on +Fedora 38, Rocky Linux 9.2, openSUSE Leap 15.5, Mageia 9, OpenMandriva, +PCLinuxOS; it can be easily modified to include dependency lists for +other RPM-based distributions.) + +## Running UxPlay + +### Installing plugins (Debian-based Linux distributions, including Ubuntu and Raspberry Pi OS) (_skip if you built a complete GStreamer from source_) + +Next install the GStreamer plugins that are needed with +`sudo apt install gstreamer1.0-`. Values of `` required +are: + +1. "**plugins-base**" +2. "**libav**" (for sound), +3. "**plugins-good**" (for v4l2 hardware h264 decoding) +4. "**plugins-bad**" (for h264 decoding). + +**Debian-based distributions split some of the plugin packages into +smaller pieces:** some that may also be needed include "**gl**" for +OpenGL support (this provides the "-vs glimagesink" videosink, which can +be very useful in many systems (including Raspberry Pi), and should +always be used when using h264/h265 decoding by a NVIDIA GPU), +"**gtk3**" (which provides the "-vs gtksink" videosink), and "**x**" for +X11 support, although these may already be installed; "**vaapi**" is +needed for hardware-accelerated h264 video decoding by Intel or AMD +graphics (but not for use with NVIDIA using proprietary drivers). If +sound is not working, "**alsa**"","**pulseaudio**", or "**pipewire**" +plugins may need to be installed, depending on how your audio is set up. + +- Also install "**gstreamer1.0-tools**" to get the utility + gst-inspect-1.0 for examining the GStreamer installation. + +### Installing plugins (Non-Debian-based Linux or \*BSD) (_skip if you built a complete GStreamer from source_) + +In some cases, because of patent issues, the libav plugin feature +**avdec_aac** needed for decoding AAC audio in mirror mode is not +provided in the official distribution: get it from community +repositories for those distributions. + +- **Red Hat, or clones like CentOS (now continued as Rocky Linux or + Alma Linux):** Install gstreamer1-libav gstreamer1-plugins-bad-free + (+ gstreamer1-vaapi for Intel/AMD graphics). In recent Fedora, + gstreamer1-libav is renamed gstreamer1-plugin-libav. **To get + avdec_aac, install packages from + [rpmfusion.org](https://rpmfusion.org)**: (get ffmpeg-libs from + rpmfusion; on RHEL or clones, but not recent Fedora, also get + gstreamer1-libav from there). + +- **Mageia, PCLinuxOS, OpenMandriva:** Install gstreamer1.0-libav + gstreamer1.0-plugins-bad (+ gstreamer1.0-vaapi for Intel/AMD + graphics). **On Mageia, to get avdec_aac, install ffmpeg from the + "tainted" repository**, (which also provides a more complete + gstreamer1.0-plugins-bad). + +- **openSUSE:** Install gstreamer-plugins-libav gstreamer-plugins-bad + (+ gstreamer-plugins-vaapi for Intel/AMD graphics). **To get + avdec_aac, install libav\* packages for openSUSE from + [Packman](https://ftp.gwdg.de/pub/linux/misc/packman/suse/) + "Essentials"**; recommendation: after adding the Packman repository, + use the option in YaST Software management to switch all system + packages for multimedia to Packman). + +- **Arch Linux** Install gst-plugins-good gst-plugins-bad gst-libav (+ + gstreamer-vaapi for Intel/AMD graphics). + +- **FreeBSD:** Install gstreamer1-libav, gstreamer1-plugins, + gstreamer1-plugins-\* (\* = core, good, bad, x, gtk, gl, vulkan, + pulse, v4l2, ...), (+ gstreamer1-vaapi for Intel/AMD graphics). + +### Starting and running UxPlay + +Since UxPlay-1.64, UxPlay can be started with options read from a +configuration file, which will be the first found of (1) a file with a +path given by environment variable `$UXPLAYRC`, (2) `~/.uxplayrc` in the +user's home directory ("\~"), (3) `~/.config/uxplayrc`. The format is +one option per line, omitting the initial `"-"` of the command-line +option. Lines in the configuration file beginning with `"#"` are treated +as comments and ignored. + +**Run uxplay in a terminal window**. On some systems, you can specify +fullscreen mode with the `-fs` option, or toggle into and out of +fullscreen mode with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C +(or close the window) to terminate it when done. If the UxPlay server is +not seen by the iOS client's drop-down "Screen Mirroring" panel, check +that your DNS-SD server (usually avahi-daemon) is running: do this in a +terminal window with `systemctl status avahi-daemon`. If this shows the +avahi-daemon is not running, control it with +`sudo systemctl [start,stop,enable,disable] avahi-daemon` (on +non-systemd systems, such as \*BSD, use +`sudo service avahi-daemon [status, start, stop, restart, ...]`). If +UxPlay is seen, but the client fails to connect when it is selected, +there may be a firewall on the server that prevents UxPlay from +receiving client connection requests unless some network ports are +opened: **if a firewall is active, also open UDP port 5353 (for mDNS +queries) needed by Avahi**. See [Troubleshooting](#troubleshooting) +below for help with this or other problems. + +- Unlike an Apple TV, the UxPlay server does not by default require + clients to initially "pair" with it using a pin code displayed by + the server (after which the client "trusts" the server, and does not + need to repeat this). Since v1.67, Uxplay offers such + "pin-authentication" as an option: see "`-pin`" and "`-reg`" in + [Usage](#usage) for details, if you wish to use it. _Some clients + with MDM (Mobile Device Management, often present on employer-owned + devices) are required to use pin-authentication: UxPlay will provide + this even when running without the pin option._ + +- By default, UxPlay is locked to its current client until that client + drops the connection; since UxPlay-1.58, the option `-nohold` + modifies this behavior so that when a new client requests a + connection, it removes the current client and takes over. UxPlay + 1.66 introduces a mechanism ( `-restrict`, `-allow `, + `-block `) to control which clients are allowed to connect, + using their "deviceID" (which in Apple devices appears to be + immutable). + +- In Mirror mode, GStreamer has a choice of **two** methods to play + video with its accompanying audio: prior to UxPlay-1.64, the video + and audio streams were both played as soon as possible after they + arrived (the GStreamer "_sync=false_" method), with a GStreamer + internal clock used to try to keep them synchronized. **Starting + with UxPlay-1.64, the other method (GStreamer's "_sync=true_" mode), + which uses timestamps in the audio and video streams sent by the + client, is the new default**. On low-decoding-power UxPlay hosts + (such as Raspberry Pi Zero W or 3 B+ models) this will drop video + frames that cannot be decoded in time to play with the audio, making + the video jerky, but still synchronized. + +The older method which does not drop late video frames worked well on +more powerful systems, and is still available with the UxPlay option +"`-vsync no`"; this method is adapted to "live streaming", and may be +better when using UxPlay as a second monitor for a Mac computer, for +example, while the new default timestamp-based method is best for +watching a video, to keep lip movements and voices synchronized. +(Without use of timestamps, video will eventually lag behind audio if it +cannot be decoded fast enough: hardware-accelerated video-decoding +helped to prevent this previously when timestamps were not being used.) + +- In Audio-only mode the GStreamer "sync=false" mode (not using + timestamps) is still the default, but if you want to keep the audio + playing on the server synchronized with the video showing on the + client, use the `-async` timestamp-based option. (An example might + be if you want to follow the Apple Music lyrics on the client while + listening to superior sound on the UxPlay server). This delays the + video on the client to match audio on the server, so leads to a + slight delay before a pause or track-change initiated on the client + takes effect on the audio played by the server. + +AirPlay volume-control attenuates volume (gain) by up to -30dB: the +decibel range -30:0 can be rescaled from _Low_:0, or _Low_:_High_, using +the option `-db` ("-db _Low_" or "-db _Low_:_High_"), _Low_ must be +negative. Rescaling is linear in decibels. Note that GStreamer's audio +format will "clip" any audio gain above +20db, so keep _High_ below that +level. The option `-taper` provides a "tapered" AirPlay volume-control +profile some users may prefer. + +The -vsync and -async options also allow an optional positive (or +negative) audio-delay adjustment in _milliseconds_ for fine-tuning : +`-vsync 20.5` delays audio relative to video by 0.0205 secs; a negative +value advances it.) + +- you may find video is improved by the setting -fps 60 that allows + some video to be played at 60 frames per second. (You can see what + framerate is actually streaming by using -vs fpsdisplaysink, and/or + -FPSdata.) When using this, you should use the default + timestamp-based synchronization option `-vsync`. + +- Since UxPlay-1.54, you can display the accompanying "Cover Art" from + sources like Apple Music in Audio-Only (ALAC) mode: run + "`uxplay -ca &`" in the background, then run a image viewer + with an autoreload feature: an example is "feh": run + "`feh -R 1 `" in the foreground; terminate feh and then Uxplay + with "`ctrl-C fg ctrl-C`". + +By default, GStreamer uses an algorithm to search for the best +"videosink" (GStreamer's term for a graphics driver to display images) +to use. You can overide this with the uxplay option `-vs `. +Which videosinks are available depends on your operating system and +graphics hardware: use +"`gst-inspect-1.0 | grep sink | grep -e video -e Video -e image`" to see +what is available. Some possibilites on Linux/\*BSD are: + +- **glimagesink** (OpenGL), **waylandsink** + +- **xvimagesink**, **ximagesink** (X11) + +- **kmssink**, **fbdevsink** (console graphics without X11) + +- **vaapisink** (for Intel/AMD hardware-accelerated graphics); for + NVIDIA hardware graphics (with CUDA) use **glimagesink** combined + with "`-vd nvh264dec`" (or "nvh264sldec", a new variant which will + become "nvh264dec" in GStreamer-1.24). + +- If the server is "headless" (no attached monitor, renders audio + only) use `-vs 0`. + +Note that videosink options can set using quoted arguments to -vs: +_e.g._, `-vs "xvimagesink display=:0"`: ximagesink and xvimagesink allow +an X11 display name to be specified, and waylandsink has a similar option. +Videosink options ("properties") can be found in their GStreamer description pages,such as +https://gstreamer.freedesktop.org/documentation/xvimagesink . + +GStreamer also searches for the best "audiosink"; override its choice +with `-as `. Choices on Linux include pulsesink, alsasink, +pipewiresink, oss4sink; see what is available with +`gst-inspect-1.0 | grep sink | grep -e audio -e Audio`. + +**One common problem involves GStreamer attempting to use +incorrectly-configured or absent accelerated hardware h264 video +decoding (e.g., VAAPI). Try "`uxplay -avdec`" to force software video +decoding; if this works you can then try to fix accelerated hardware +video decoding if you need it, or just uninstall the GStreamer vaapi +plugin.** + +See [Usage](#usage) for more run-time options. + +### **Special instructions for Raspberry Pi (tested on Raspberry Pi Zero 2 W, 3 Model B+, 4 Model B, and 5 only)**: + +- For Framebuffer video (for Raspberry Pi OS "Lite" and other non-X11 + distributions) use the KMS videosink "-vs kmssink" (the DirectFB + framebuffer videosink "dfbvideosink" is broken on the Pi, and + segfaults). _In this case you should explicitly use the "-vs + kmssink" option, as without it, autovideosink does not find the + correct videosink._ + +- Raspberry Pi 5 does not provide hardware H264 decoding (and does not + need it). + +- Pi Zero 2 W, 3 Model B+ and 4 Model B should use hardware H264 + decoding by the Broadcom GPU, but it requires an out-of-mainstream + kernel module bcm2835_codec maintained in the [Raspberry Pi kernel + tree](https://github.com/raspberrypi/linux); distributions that are + known to supply it include Raspberry Pi OS, Ubuntu, and + Manjaro-RPi4. Use software decoding (option -avdec) if this module + is not available. + +- Uxplay uses the Video4Linux2 (v4l2) plugin from GStreamer-1.22 and + later to access the GPU, if hardware H264 decoding is used. This + should happen automatically. The option -v4l2 can be used, but it is + usually best to just let GStreamer find the best video pipeline by + itself. + +- On older distributions (GStreamer \< 1.22), the v4l2 plugin needs a + patch: see the [UxPlay + Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches). + Legacy Raspberry Pi OS (Bullseye) has a partially-patched + GStreamer-1.18.4 which needs the uxplay option -bt709 (and don't use + -v4l2); it is still better to apply the full patch from the UxPlay + Wiki in this case. + +- **It appears that when hardware h264 video decoding is used, the option + -bt709 became needed again in GStreamer-1.22 and later.** + +- For "double-legacy" Raspberry Pi OS (Buster), there is no patch for + GStreamer-1.14. Instead, first build a complete newer + GStreamer-1.18.6 from source using [these + instructions]() + before building UxPlay. + +- Raspberry Pi 3 Model B+ running a 32 bit OS can also access the GPU + with the GStreamer OMX plugin (use option "`-vd omxh264dec`"), but + this is broken by Pi 4 Model B firmware. OMX support was removed + from Raspberry Pi OS (Bullseye), but is present in Buster. + +- **H265 (4K)** video is potentially supported by hardware decoding on + Raspberry Pi 5 models, as well as on Raspberry Pi 4 model B, using a + dedicated HEVC decoding block, but the "rpivid" kernel driver for + this is not yet supported by GStreamer (this driver decodes video + into a non-standard format that cannot be supported by GStreamer + until the driver is in the mainline Linux kernel). Raspberry Pi + provides a version of ffmpeg that can use that format, but at + present UxPlay cannot use this. The best solution would be for the + driver to be "upstreamed" to the kernel, allowing GStreamer support. + (Software HEVC decoding works, but does not seem to give + satisfactory results on the Pi). + +Even with GPU video decoding, some frames may be dropped by the +lower-power models to keep audio and video synchronized using +timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config +"Performance Options" allows specifying how much memory to allocate to +the GPU, but this setting appears to be absent in Bookworm (but it can +still be set to e.g. 128MB by adding a line "gpu_mem=128" in +/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well +when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to +the GPU (default seems to be 64MB). + +The basic uxplay options for R Pi are `uxplay [-vs ]`. The +choice `` = `glimagesink` is sometimes useful. With the +Wayland video compositor, use `` = `waylandsink`. With +framebuffer video, use `` = `kmssink`. + +- Tip: to start UxPlay on a remote host (such as a Raspberry Pi) using + ssh: + +```{=html} + +``` + + ssh user@remote_host + export DISPLAY=:0 + nohup uxplay [options] > FILE & + +Sound and video will play on the remote host; "nohup" will keep uxplay +running if the ssh session is closed. Terminal output is saved to FILE +(which can be /dev/null to discard it) + +## Building UxPlay on macOS: **(Intel X86_64 and "Apple Silicon" M1/M2 Macs)** + +_Note: A native AirPlay Server feature is included in macOS 12 Monterey, +but is restricted to recent hardware. UxPlay can run on older macOS +systems that will not be able to run Monterey, or can run Monterey but +not AirPlay._ + +These instructions for macOS assume that the Xcode command-line +developer tools are installed (if Xcode is installed, open the Terminal, +type "sudo xcode-select --install" and accept the conditions). + +It is also assumed that CMake \>= 3.13 is installed: this can be done +with package managers [MacPorts](http://www.macports.org) +(`sudo port install cmake`), [Homebrew](http://brew.sh) +(`brew install cmake`), or by a download from +. Also install `git` if you will use it to +fetch UxPlay. + +Next install libplist and openssl-3.x. Note that static versions of +these libraries will be used in the macOS builds, so they can be +uninstalled after building uxplay, if you wish. + +- If you use Homebrew: `brew install libplist openssl@3` + +- if you use MacPorts: `sudo port install libplist-devel openssl3` + +Otherwise, build libplist and openssl from source: see instructions near +the end of this README; requires development tools (autoconf, automake, +libtool, _etc._) to be installed. + +Next get the latest macOS release of GStreamer-1.0. + +**Using "Official" GStreamer (Recommended for both MacPorts and Homebrew +users)**: install the GStreamer release for macOS from +. (This release contains +its own pkg-config, so you don't have to install one.) Install both the +gstreamer-1.0 and gstreamer-1.0-devel packages. After downloading, +Shift-Click on them to install (they install to +/Library/FrameWorks/GStreamer.framework). Homebrew or MacPorts users +should **not** install (or should uninstall) the GStreamer supplied by +their package manager, if they use the "official" release. + +- Since GStreamer v1.22, the "Official" (gstreamer.freedesktop.org) + macOS binaries require a wrapper "gst_macos_main" around the actual + main program (uxplay). This should have been applied during the + UxPlay compilation process, and the initial UxPlay terminal message + should confirm it is being used. (UxPlay can also be built using + "Official" GStreamer v.1.20.7 binaries, which work without the + wrapper.) + +**Using Homebrew's GStreamer**: pkg-config is needed: ("brew install +pkg-config gstreamer"). This causes a large number of extra packages to +be installed by Homebrew as dependencies. The [Homebrew gstreamer +installation](https://formulae.brew.sh/formula/gstreamer#default) has +recently been reworked into a single "formula" named `gstreamer`, which +now works without needing GST_PLUGIN_PATH to be set in the enviroment. +Homebrew installs gstreamer to `HOMEBREW_PREFIX/lib/gstreamer-1.0` where +by default `HOMEBREW_PREFIX/*` is `/opt/homebrew/*` on Apple Silicon +Macs, and `/usr/local/*` on Intel Macs; do not put any extra +non-Homebrew plugins (that you build yourself) there, and instead set +GST_PLUGIN_PATH to point to their location (Homebrew does not supply a +complete GStreamer, but seems to have everything needed for UxPlay). +**New: the UxPlay build script will now also detect Homebrew +installations in non-standard locations indicated by the environment +variable `$HOMEBREW_PREFIX`.** + +**Using GStreamer installed from MacPorts**: this is **not** +recommended, as currently the MacPorts GStreamer is old (v1.16.2), +unmaintained, and built to use X11: + +- Instead [build gstreamer + yourself](https://github.com/FDH2/UxPlay/wiki/Building-GStreamer-from-Source-on-macOS-with-MacPorts) + if you use MacPorts and do not want to use the "Official" Gstreamer + binaries. + +_(If you really wish to use the MacPorts GStreamer-1.16.2, install +pkgconf ("sudo port install pkgconf"), then "sudo port install +gstreamer1-gst-plugins-base gstreamer1-gst-plugins-good +gstreamer1-gst-plugins-bad gstreamer1-gst-libav". For X11 support on +macOS, compile UxPlay using a special cmake option `-DUSE_X11=ON`, and +run it from an XQuartz terminal with -vs ximagesink; older non-retina +macs require a lower resolution when using X11: `uxplay -s 800x600`.)_ + +After installing GStreamer, build and install uxplay: open a terminal +and change into the UxPlay source directory ("UxPlay-master" for zipfile +downloads, "UxPlay" for "git clone" downloads) and build/install with +"cmake . ; make ; sudo make install" (same as for Linux). + +- Running UxPlay while checking for GStreamer warnings (do this with + "export GST_DEBUG=2" before runnng UxPlay) reveals that with the + default (since UxPlay 1.64) use of timestamps for video + synchonization, many video frames are being dropped (only on macOS), + perhaps due to another error (about videometa) that shows up in the + GStreamer warnings. **Recommendation: use the new UxPlay "no + timestamp" option "`-vsync no`"** (you can add a line "vsync no" in + the uxplayrc configuration file). + +- On macOS with this installation of GStreamer, the only videosinks + available seem to be glimagesink (default choice made by + autovideosink) and osxvideosink. The window title does not show the + Airplay server name, but the window is visible to screen-sharing + apps (e.g., Zoom). The only available audiosink seems to be + osxaudiosink. + +- The option -nc is always used, whether or not it is selected. This + is a workaround for a problem with GStreamer videosinks on macOS: if + the GStreamer pipeline is destroyed while the mirror window is still + open, a segfault occurs. + +- In the case of glimagesink, the resolution settings "-s wxh" do not + affect the (small) initial OpenGL mirror window size, but the window + can be expanded using the mouse or trackpad. In contrast, a window + created with "-vs osxvideosink" is initially big, but has the wrong + aspect ratio (stretched image); in this case the aspect ratio + changes when the window width is changed by dragging its side; the + option `-vs "osxvideosink force-aspect-ratio=true"` can be used to + make the window have the correct aspect ratio when it first opens. + +## Building UxPlay on Microsoft Windows, using MSYS2 with the MinGW-64 compiler. + +- tested on Windows 10 and 11, 64-bit. + +1. Download and install **Bonjour SDK for Windows v3.0**. You can + download the SDK without any registration at + [softpedia.com](https://www.softpedia.com/get/Programming/SDK-DDK/Bonjour-SDK.shtml), + or get it from the official Apple site + [https://developer.apple.com/download](https://developer.apple.com/download/all/?q=Bonjour%20SDK%20for%20Windows) + (Apple makes you register as a developer to access it from their + site). This should install the Bonjour SDK as + `C:\Program Files\Bonjour SDK`. + +2. (This is for 64-bit Windows; a build for 32-bit Windows should be + possible, but is not tested.) The unix-like MSYS2 build environment + will be used: download and install MSYS2 from the official site + [https://www.msys2.org/](https://www.msys2.org). Accept the default + installation location `C:\mysys64`. + +3. [MSYS2 packages](https://packages.msys2.org/package/) are installed + with a variant of the "pacman" package manager used by Arch Linux. + Open a "MSYS2 MINGW64" terminal from the MSYS2 tab in the Windows + Start menu, and update the new MSYS2 installation with "pacman + -Syu". Then install the **MinGW-64** compiler and **cmake** + + pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc + + The compiler with all required dependencies will be installed in the + msys64 directory, with default path `C:/msys64/mingw64`. Here we + will simply build UxPlay from the command line in the MSYS2 + environment (this uses "`ninja`" in place of "`make`" for the build + system). + +4. Download the latest UxPlay from github **(to use `git`, install it + with `pacman -S git`, then + "`git clone https://github.com/FDH2/UxPlay`")**, then install UxPlay + dependencies (openssl is already installed with MSYS2): + + `pacman -S mingw-w64-x86_64-libplist mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base` + + If you are trying a different Windows build system, MSVC versions of + GStreamer for Windows are available from the [official GStreamer + site](https://gstreamer.freedesktop.org/download/), but only the + MinGW 64-bit build on MSYS2 has been tested. + +5. cd to the UxPlay source directory, then "`mkdir build`" and + "`cd build`". The build process assumes that the Bonjour SDK is + installed at `C:\Program Files\Bonjour SDK`. If it is somewhere + else, set the enviroment variable BONJOUR_SDK_HOME to point to its + location. Then build UxPlay with + + `cmake ..` + + `ninja` + +6. Assuming no error in either of these, you will have built the uxplay + executable **uxplay.exe** in the current ("build") directory. The + "sudo make install" and "sudo make uninstall" features offered in + the other builds are not available on Windows; instead, the MSYS2 + environment has `/mingw64/...` available, and you can install the + uxplay.exe executable in `C:/msys64/mingw64/bin` (plus manpage and + documentation in `C:/msys64/mingw64/share/...`) with + + `cmake --install . --prefix /mingw64` + + To be able to view the manpage, you need to install the manpage + viewer with "`pacman -S man`". + +To run **uxplay.exe** you need to install some gstreamer plugin packages +with `pacman -S mingw-w64-x86_64-gst-`, where the required ones +have `` given by + +1. **libav** +2. **plugins-good** +3. **plugins-bad** + +Other possible MSYS2 gstreamer plugin packages you might use are listed +in [MSYS2 packages](https://packages.msys2.org/package/). + +You also will need to grant permission to the uxplay executable +uxplay.exe to access data through the Windows firewall. You may +automatically be offered the choice to do this when you first run +uxplay, or you may need to do it using **Windows Settings-\>Update and +Security-\>Windows Security-\>Firewall & network protection -\> allow an +app through firewall**. If your virus protection flags uxplay.exe as +"suspicious" (but without a true malware signature) you may need to give +it an exception. + +Now test by running "`uxplay`" (in a MSYS2 terminal window). If you need +to specify the audiosink, there are two main choices on Windows: the +older DirectSound plugin "`-as directsoundsink`", and the more modern +Windows Audio Session API (wasapi) plugin "`-as wasapisink`", which +supports [additional +options](https://gstreamer.freedesktop.org/documentation/wasapi/wasapisink.html) +such as + + uxplay -as 'wasapisink device=\"\"' + +where `` specifies an available audio device by its GUID, which +can be found using "`gst-device-monitor-1.0 Audio`": `` has a form +like `\{0.0.0.00000000\}.\{98e35b2b-8eba-412e-b840-fd2c2492cf44\}`. If +"`device`" is not specified, the default audio device is used. + +If you wish to specify the videosink using the `-vs ` option, +some choices for `` are `d3d11videosink`, `d3dvideosink`, +`glimagesink`, `gtksink`. + +- With Direct3D 11.0 or greater, you can either always be in + fullscreen mode using option + `-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true"`, + or get the ability to toggle into and out of fullscreen mode using + the Alt-Enter key combination with option + `-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`. For + convenience, these options will be added if just + `-vs d3d11videosink` with or without the fullscreen option "-fs" is + used. _(Windows users may wish to add "`vs d3d11videosink`" (no + initial "`-`") to the UxPlay startup options file; see "man uxplay" + or "uxplay -h".)_ + +The executable uxplay.exe can also be run without the MSYS2 environment, +in the Windows Terminal, with `C:\msys64\mingw64\bin\uxplay`. + +# Usage + +Options: + +- These can also be written (one option per line, without the initial + "`-`" character) in the UxPlay startup file (either given by + environment variable `$UXPLAYRC`, or `~/.uxplayrc` or + `~/.config/uxplayrc`); lines begining with "`#`" are treated as + comments, and ignored. Command line options supersede options in the + startup file. + +**-n server_name** (Default: UxPlay); server_name@\_hostname\_ will be +the name that appears offering AirPlay services to your iPad, iPhone +etc, where _hostname_ is the name of the server running uxplay. This +will also now be the name shown above the mirror display (X11) window. + +**-nh** Do not append "@_hostname_" at the end of the AirPlay server +name. + +**-h265** Activate "ScreenMultiCodec" support (AirPlay "Features" bit 42) for accepting h265 (4K/HEVC) video in addition to h264 video (1080p) +in screen-mirror mode. When this option is used, two "video pipelines" +(one for h264, one for h265) are created. If any GStreamer plugins in +the pipeline are specific for h264 or h265, the correct version will be +used in each pipeline. A wired Client-Server ethernet connection is +preferred over Wifi for 4K video, and might be required by the client. +Only recent Apple devices (M1/M2 Macs or iPads, and some iPhones) can +send h265 video if a resolution "-s wxh" with h \> 1080 is requested. +The "-h265" option changes the default resolution ("-s" option) from +1920x1080 to 3840x2160, and leaves default maximum framerate ("-fps" +option) at 30fps. + +**-hls** Activate HTTP Live Streaming support. With this option YouTube +videos can be streamed directly from YouTube servers to UxPlay (without +passing through the client) by clicking on the AirPlay icon in the +YouTube app. + +**-pin \[nnnn\]**: (since v1.67) use Apple-style (one-time) "pin" +authentication when a new client connects for the first time: a +four-digit pin code is displayed on the terminal, and the client screen +shows a login prompt for this to be entered. When "-pin" is used by +itself, a new random pin code is chosen for each authentication; if +"-pin nnnn" (e.g., "-pin 3939") is used, this will set an unchanging +fixed code. Authentication adds the server to the client's list of +"trusted servers" and the client will not need to reauthenticate +provided that the client and server public keys remain unchanged. (By +default since v1.68, the server public key is generated from the MAC +address, which can be changed with the -m option; see the -key option +for an alternative method of key generation). _(Add a line "pin" in the +UxPlay startup file if you wish the UxPlay server to use the pin +authentication protocol)._ + +**-reg \[_filename_\]**: (since v1.68). If "-pin" is used, this option +maintains a register of pin-authenticated "trusted clients" in +\$HOME/.uxplay.register (or optionally, in _filename_). Without this +option, returning clients that skip pin-authentication are trusted and +not checked. This option may be useful if UxPlay is used in a more +public environment, to record client details; the register is text, one +line per client, with client's public key (base-64 format), Device ID, +and Device name; commenting out (with "\#") or deleting a line +deregisters the corresponding client (see options -restrict, -block, +-allow for more ways to control client access). _(Add a line "reg" in +the startup file if you wish to use this feature.)_ + +**-vsync \[x\]** (In Mirror mode:) this option (**now the default**) +uses timestamps to synchronize audio with video on the server, with an +optional audio delay in (decimal) milliseconds (_x_ = "20.5" means +0.0205 seconds delay: positive or negative delays less than a second are +allowed.) It is needed on low-power systems such as Raspberry Pi without +hardware video decoding. + +**-vsync no** (In Mirror mode:) this switches off timestamp-based +audio-video synchronization, restoring the default behavior prior to +UxPlay-1.64. Standard desktop systems seem to work well without use of +timestamps: this mode is appropriate for "live streaming" such as using +UxPlay as a second monitor for a mac computer, or monitoring a webcam; +with it, no video frames are dropped. + +**-async \[x\]** (In Audio-Only (ALAC) mode:) this option uses +timestamps to synchronize audio on the server with video on the client, +with an optional audio delay in (decimal) milliseconds (_x_ = "20.5" +means 0.0205 seconds delay: positive or negative delays less than a +second are allowed.) Because the client adds a video delay to account +for latency, the server in -async mode adds an equivalent audio delay, +which means that audio changes such as a pause or a track-change will +not take effect immediately. _This might in principle be mitigated by +using the `-al` audio latency setting to change the latency (default +0.25 secs) that the server reports to the client, but at present +changing this does not seem to have any effect_. + +**-async no**. This is the still the default behavior in Audio-only +mode, but this option may be useful as a command-line option to switch +off a `-async` option set in a "uxplayrc" configuration file. + +**-db _low_\[:_high_\]** Rescales the AirPlay volume-control attenuation +(gain) from -30dB:0dB to _low_:0dB or _low_:_high_. The lower limit +_low_ must be negative (attenuation); the upper limit _high_ can be +either sign. (GStreamer restricts volume-augmentation by _high_ so that +it cannot exceed +20dB). The rescaling is "flat", so that for -db +-50:10, a change in Airplay attenuation by -7dB is translated to a -7 x +(60/30) = -14dB attenuation, and the maximum volume (AirPlay 0dB) is a +10dB augmentation, and Airplay -30dB would become -50dB. Note that the +minimum AirPlay value (-30dB exactly) is translated to "mute". + +**-taper** Provides a "tapered" Airplay volume-control profile (matching +the one called "dasl-tapering" in +[shairport-sync](https://github.com/mikebrady/shairport-sync)): each +time the length of the volume slider (or the number of steps above mute, +where 16 steps = full volume) is reduced by 50%, the perceived volume is +halved (a 10dB attenuation). (This is modified at low volumes, to use +the "untapered" volume if it is louder.) + +**-s wxh** e.g. -s 1920x1080 (= "1080p"), the default width and height +resolutions in pixels for h264 video. (The default becomes 3840x2160 (= +"4K") when the -h265 option is used.) This is just a request made to the +AirPlay client, and perhaps will not be the final resolution you get. w +and h are whole numbers with four digits or less. Note that the +**height** pixel size is the controlling one used by the client for +determining the streaming format; the width is dynamically adjusted to +the shape of the image (portrait or landscape format, depending on how +an iPad is held, for example). + +**-s wxh@r** As above, but also informs the AirPlay client about the +screen refresh rate of the display. Default is r=60 (60 Hz); r must be a +whole number less than 256. + +**-o** turns on an "overscanned" option for the display window. This +reduces the image resolution by using some of the pixels requested by +option -s wxh (or their default values 1920x1080) by adding an empty +boundary frame of unused pixels (which would be lost in a full-screen +display that overscans, and is not displayed by gstreamer). +Recommendation: **don't use this option** unless there is some special +reason to use it. + +**-fs** uses fullscreen mode, but only works with X11, Wayland, VAAPI, +and D3D11 (Windows). + +**-p** allows you to select the network ports used by UxPlay (these need +to be opened if the server is behind a firewall). By itself, -p sets +"legacy" ports TCP 7100, 7000, 7001, UDP 6000, 6001, 7011. -p n (e.g. -p 35000) sets TCP and UDP ports n, n+1, n+2. -p n1,n2,n3 (comma-separated +values) sets each port separately; -p n1,n2 sets ports n1,n2,n2+1. -p +tcp n or -p udp n sets just the TCP or UDP ports. Ports must be in the +range \[1024-65535\]. + +If the -p option is not used, the ports are chosen dynamically +(randomly), which will not work if a firewall is running. + +**-avdec** forces use of software h264 decoding using Gstreamer element +avdec_h264 (libav h264 decoder). This option should prevent +autovideosink choosing a hardware-accelerated videosink plugin such as +vaapisink. + +**-vp _parser_** choses the GStreamer pipeline's h264 parser element, +default is h264parse. Using quotes "..." allows options to be added. + +**-vd _decoder_** chooses the GStreamer pipeline's h264 decoder element, +instead of the default value "decodebin" which chooses it for you. +Software decoding is done by avdec_h264; various hardware decoders +include: vaapih264dec, nvdec, nvh264dec, v4l2h264dec (these require that +the appropriate hardware is available). Using quotes "..." allows some +parameters to be included with the decoder name. + +**-vc _converter_** chooses the GStreamer pipeline's videoconverter +element, instead of the default value "videoconvert". When using +Video4Linux2 hardware-decoding by a GPU,`-vc v4l2convert` will also use +the GPU for video conversion. Using quotes "..." allows some parameters +to be included with the converter name. + +**-vs _videosink_** chooses the GStreamer videosink, instead of the +default value "autovideosink" which chooses it for you. Some videosink +choices are: ximagesink, xvimagesink, vaapisink (for intel graphics), +gtksink, glimagesink, waylandsink, osxvideosink (for macOS), kmssink +(for systems without X11, like Raspberry Pi OS lite) or fpsdisplaysink +(which shows the streaming framerate in fps). Using quotes "..." allows +some parameters to be included with the videosink name. For example, +**fullscreen** mode is supported by the vaapisink plugin, and is +obtained using `-vs "vaapisink fullscreen=true"`; this also works with +`waylandsink`. The syntax of such options is specific to a given plugin +(see GStreamer documentation), and some choices of videosink might not +work on your system. + +**-vs 0** suppresses display of streamed video. In mirror mode, the +client's screen is still mirrored at a reduced rate of 1 frame per +second, but is not rendered or displayed. This option should always be +used if the server is "headless" (with no attached screen to display +video), and only used to render audio, which will be AAC +lossily-compressed audio in mirror mode with unrendered video, and +superior-quality ALAC Apple Lossless audio in Airplay audio-only mode. + +**-v4l2** Video settings for hardware h264 video decoding in the GPU by +Video4Linux2. Equivalent to `-vd v4l2h264dec -vc v4l2convert`. + +**-bt709** A workaround for the failure of the older Video4Linux2 plugin +to recognize Apple's use of an uncommon (but permitted) "full-range +color" variant of the bt709 color standard for digital TV. This is no +longer needed by GStreamer-1.20.4 and backports from it. + +**-rpi** Equivalent to "-v4l2" (Not valid for Raspberry Pi model 5, and +removed in UxPlay 1.67) + +**-rpigl** Equivalent to "-rpi -vs glimagesink". (Removed since UxPlay +1.67) + +**-rpifb** Equivalent to "-rpi -vs kmssink" (Removed since UxPlay 1.67) + +**-rpiwl** Equivalent to "-rpi -vs waylandsink". (Removed since UxPlay +1.67) + +**-as _audiosink_** chooses the GStreamer audiosink, instead of letting +autoaudiosink pick it for you. Some audiosink choices are: pulsesink, +alsasink, pipewiresink, osssink, oss4sink, jackaudiosink, osxaudiosink +(for macOS), wasapisink, directsoundsink (for Windows). Using quotes +"..." might allow some optional parameters +(e.g. `-as "alsasink device=..."` to specify a non-default output +device). The syntax of such options is specific to a given plugin (see +GStreamer documentation), and some choices of audiosink might not work +on your system. + +**-as 0** (or just **-a**) suppresses playing of streamed audio, but +displays streamed video. + +**-al _x_** specifies an audio latency _x_ in (decimal) seconds in +Audio-only (ALAC), that is reported to the client. Values in the range +\[0.0, 10.0\] seconds are allowed, and will be converted to a whole +number of microseconds. Default is 0.25 sec (250000 usec). _(However, +the client appears to ignore this reported latency, so this option seems +non-functional.)_ + +**-ca _filename_** provides a file (where _filename_ can include a full +path) used for output of "cover art" (from Apple Music, _etc._,) in +audio-only ALAC mode. This file is overwritten with the latest cover art +as it arrives. Cover art (jpeg format) is discarded if this option is +not used. Use with a image viewer that reloads the image if it changes, +or regularly (_e.g._ once per second.). To achieve this, run +"`uxplay -ca [path/to/]filename &`" in the background, then run the the +image viewer in the foreground. Example, using `feh` as the viewer: run +"`feh -R 1 [path/to/]filename`" (in the same terminal window in which +uxplay was put into the background). To quit, use `ctrl-C fg ctrl-C` to +terminate the image viewer, bring `uxplay` into the foreground, and +terminate it too. + +**-reset n** sets a limit of _n_ consecutive timeout failures of the +client to respond to ntp requests from the server (these are sent every +3 seconds to check if the client is still present, and synchronize with +it). After _n_ failures, the client will be presumed to be offline, and +the connection will be reset to allow a new connection. The default +value of _n_ is 5; the value _n_ = 0 means "no limit" on timeouts. + +**-nofreeze** closes the video window after a reset due to ntp timeout +(default is to leave window open to allow a smoother reconection to the +same client). This option may be useful in fullscreen mode. + +**-nc** maintains previous UxPlay \< 1.45 behavior that does **not +close** the video window when the the client sends the "Stop Mirroring" +signal. _This option is currently used by default in macOS, as the +window created in macOS by GStreamer does not terminate correctly (it +causes a segfault) if it is still open when the GStreamer pipeline is +closed._ + +**-nohold** Drops the current connection when a new client attempts to +connect. Without this option, the current client maintains exclusive +ownership of UxPlay until it disconnects. + +**-restrict** Restrict clients allowed to connect to those specified by +`-allow `. The deviceID has the form of a MAC address which is +displayed by UxPlay when the client attempts to connect, and appears to +be immutable. It has the format `XX:XX:XX:XX:XX:XX`, X = 0-9,A-F, and is +possibly the "true" hardware MAC address of the device. Note that iOS +clients generally expose different random "private Wi_Fi addresses" +("fake" MAC addresses) to different networks (for privacy reasons, to +prevent tracking), which may change, and do not correpond to the +deviceID. + +**-restrict no** Remove restrictions (default). This is useful as a +command-line argument to overide restrictions set in the Startup file. + +**-allow _id_** Adds the deviceID = _id_ to the list of allowed clients +when client restrictions are being enforced. Usually this will be an +entry in the uxplayrc startup file. + +**-block _id_** Always block clients with deviceID = _id_, even when +client restrictions are not being enforced generally. Usually this will +be an entry in the uxplayrc startup file. + +**-FPSdata** Turns on monitoring of regular reports about video +streaming performance that are sent by the client. These will be +displayed in the terminal window if this option is used. The data is +updated by the client at 1 second intervals. + +**-fps n** sets a maximum frame rate (in frames per second) for the +AirPlay client to stream video; n must be a whole number less than 256. +(The client may choose to serve video at any frame rate lower than this; +default is 30 fps.) A setting of 60 fps may give you improved video but +is not recommended on Raspberry Pi. A setting below 30 fps might be +useful to reduce latency if you are running more than one instance of +uxplay at the same time. _This setting is only an advisory to the client +device, so setting a high value will not force a high framerate._ (You +can test using "-vs fpsdisplaysink" to see what framerate is being +received, or use the option -FPSdata which displays video-stream +performance data continuously sent by the client during +video-streaming.) + +**-f {H\|V\|I}** implements "videoflip" image transforms: H = horizontal +flip (right-left flip, or mirror image); V = vertical flip ; I = 180 +degree rotation or inversion (which is the combination of H with V). + +**-r {R\|L}** 90 degree Right (clockwise) or Left (counter-clockwise) +rotations; these image transforms are carried out after any **-f** +transforms. + +**-m \[mac\]** changes the MAC address (Device ID) used by UxPlay +(default is to use the true hardware MAC address reported by the host +computer's network card). (Different server_name, MAC addresses, and +network ports are needed for each running uxplay if you attempt to run +more than one instance of uxplay on the same computer.) If \[mac\] (in +form xx:xx:xx:xx:xx:xx, 6 hex octets) is not given, a random MAC address +is generated. If UxPlay fails to find the true MAC address of a network +card, (more specifically, the MAC address used by the first active +network interface detected) a random MAC address will be used even if +option **-m** was not specified. (Note that a random MAC address will be +different each time UxPlay is started). + +**-key \[_filename_\]**: This (more secure) option for generating and +storing a persistant public key (needed for the -pin option) has been +replaced by default with a (less secure) method which generates a key +from the server's "device ID" (MAC address, which can be changed with +the -m option, conveniently as a startup file option). When the -key +option is used, a securely generated keypair is generated and stored in +`$HOME/.uxplay.pem`, if that file does not exist, or read from it, if it +exists. (Optionally, the key can be stored in _filename_.) This method +is more secure than the new default method, (because the Device ID is +broadcast in the DNS_SD announcement) but still leaves the private key +exposed to anyone who can access the pem file. This option should be set +in the UxPlay startup file as a line "key" or "key _filename_" (no +initial "-"), where _filename_ is a full path which should be enclosed +in quotes (`"...."`) if it contains any blank spaces. **Because the +default method is simpler, and security of client access to uxplay is +unlikely to be an important issue, the -key option is no longer +recommended**. + +**-dacp \[_filename_\]**: Export current client DACP-ID and +Active-Remote key to file: default is \$HOME/.uxplay.dacp. (optionally +can be changed to _filename_). Can be used by remote control +applications. File is transient: only exists while client is connected. + +**-vdmp** Dumps h264 video to file videodump.h264. -vdmp n dumps not +more than n NAL units to videodump.x.h264; x= 1,2,... increases each +time a SPS/PPS NAL unit arrives. To change the name _videodump_, use +-vdmp \[n\] _filename_. + +**-admp** Dumps audio to file audiodump.x.aac (AAC-ELD format audio), +audiodump.x.alac (ALAC format audio) or audiodump.x.aud (other-format +audio), where x = 1,2,3... increases each time the audio format changes. +-admp _n_ restricts the number of packets dumped to a file to _n_ or +less. To change the name _audiodump_, use -admp \[n\] _filename_. _Note +that (unlike dumped video) the dumped audio is currently only useful for +debugging, as it is not containerized to make it playable with standard +audio players._ + +**-d** Enable debug output. Note: this does not show GStreamer error or +debug messages. To see GStreamer error and warning messages, set the +environment variable GST_DEBUG with "export GST_DEBUG=2" before running +uxplay. To see GStreamer information messages, set GST_DEBUG=4; for +DEBUG messages, GST_DEBUG=5; increase this to see even more of the +GStreamer inner workings. + +# Troubleshooting + +Note: `uxplay` is run from a terminal command line, and informational +messages are written to the terminal. + +### 0. Problems in compiling UxPlay. + +One user (on Ubuntu) found compilation failed with messages about +linking to "usr/local/lib/libcrypto.a" and "zlib". This was because (in +addition to the standard ubuntu installation of libssl-dev), the user +was unaware that a second installation with libcrypto in /usr/local was +present. Solution: when more than one installation of OpenSSL is +present, set the environment variable OPEN_SSL_ROOT_DIR to point to the +correct one; on 64-bit Ubuntu, this is done by running +`export OPENSSL_ROOT_DIR=/usr/lib/X86_64-linux-gnu/` before running +cmake. + +### 1. **Avahi/DNS_SD Bonjour/Zeroconf issues** + +The DNS_SD Service-Discovery ("Bonjour" or "Zeroconf") service is +required for UxPlay to work. On Linux, it will be usually provided by +Avahi, and to troubleshoot this, you should use the tool `avahi-browse`. +(You may need to install a separate package with a name like +`avahi-utils` to get this.) + +On Linux, make sure Avahi is installed, and start the avahi-daemon +service on the system running uxplay (your distribution will document +how to do this, for example: `sudo systemctl avahi-daemon` or +`sudo service avahi-daemon `, with `` one of enable, disable, +start, stop, status. You might need to edit the avahi-daemon.conf file +(it is typically in /etc/avahi/, find it with +"`sudo find /etc -name avahi-daemon.conf`"): make sure that +"disable-publishing" is **not** a selected option). Some systems may +instead use the mdnsd daemon as an alternative to provide DNS-SD +service. (FreeBSD offers both alternatives, but only Avahi was tested; +see [here](https://gist.github.com/reidransom/6033227).) + +- **uxplay starts, but either stalls or stops after "Initialized + server socket(s)" appears (_without the server name showing on the + client_)**. + +If UxPlay stops with the "No DNS-SD Server found" message, this means +that your network **does not have a running Bonjour/zeroconf DNS-SD +server.** Before v1.60, UxPlay used to stall silently if DNS-SD service +registration failed, but now stops with an error message returned by the +DNSServiceRegister function: kDNSServiceErr_Unknown if no DNS-SD server +was found: _(A NixOS user found that in NixOS, this error can also occur +if avahi-daemon service IS running with publishing enabled, but reports +"the error disappeared on NixOS by setting services.avahi.openFirewall +to true".)_ Other mDNS error codes are in the range FFFE FF00 (-65792) +to FFFE FFFF (-65537), and are listed in the dnssd.h file. An older +version of this (the one used by avahi) is found +[here](https://github.com/lathiat/avahi/blob/master/avahi-compat-libdns_sd/dns_sd.h). +A few additional error codes are defined in a later version from +[Apple](https://opensource.apple.com/source/mDNSResponder/mDNSResponder-544/mDNSShared/dns_sd.h.auto.html). + +If UxPlay stalls _without an error message_ and _without the server name +showing on the client_, **this is a network problem** (if your UxPlay +version is older than 1.60, it is also the behavior when no DNS-SD +server is found.) + +A useful tool for examining such network problems from the client end is +the (free) Discovery DNS-SD browser [available in the Apple App +Store](https://apps.apple.com/us/developer/lily-ballard/id305441020) for +both iOS (works on iPadOS too) and macOS. + +- Some users using dual-band (2.4GHz/5GHz) routers have reported that + clients using the 5GHz band (sometimes) "fail to see UxPlay" (i.e., + do not get a response to their mDNS queries), but the 2.4GHz band + works. Other projects using Bonjour/mDNS have had similar reports; + the issue seems to be router-specific, perhaps related to "auto" + rather than fixed channel selection (5GHz has many more channels to + switch between), or channel width selections; one speculation is + that since mDNS uses UDP protocol (where "lost" messages are not + resent), a mDNS query might get lost if channel switching occurs + during the query. + +If your router has this problem, a reported "fix" is to (at least on +5GHz) use fixed channel and/or fixed (not dynamic) channel width. + +- **Avahi works at first, but new clients do not see UxPlay, or + clients that initially saw it stop doing so after they disconnect**. + +This is usually because Avahi is only using the "loopback" network +interface, and is not receiving mDNS queries from new clients that were +not listening when UxPlay started. + +To check this, after starting uxplay, use the utility +`avahi-browse -a -t` **in a different terminal window** on the server to +verify that the UxPlay AirTunes and AirPlay services are correctly +registered (only the AirTunes service is used in the "Legacy" AirPlay +Mirror mode used by UxPlay, but the AirPlay service is used for the +initial contact). + +The results returned by avahi-browse should show entries for uxplay like + + + eno1 IPv6 UxPlay AirPlay Remote Video local + + eno1 IPv4 UxPlay AirPlay Remote Video local + + lo IPv4 UxPlay AirPlay Remote Video local + + eno1 IPv6 863EA27598FE@UxPlay AirTunes Remote Audio local + + eno1 IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local + + lo IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local + +If only the loopback ("lo") entries are shown, a firewall on the UxPlay +host is probably blocking full DNS-SD service, and you need to open the +default UDP port 5353 for mDNS requests, as loopback-based DNS-SD +service is unreliable. + +If the UxPlay services are listed by avahi-browse as above, but are not +seen by the client, the problem is likely to be a problem with the local +network. + +### 2. uxplay starts, but stalls after "Initialized server socket(s)" appears, _with the server name showing on the client_ (but the client fails to connect when the UxPlay server is selected). + +This shows that a _DNS-SD_ service is working, clients hear UxPlay is +available, but the UxPlay server is not receiving the response from the +client. This is usually because a firewall on the server is blocking the +connection request from the client. (One user who insisted that the +firewall had been turned off turned out to have had _two_ active +firewalls (_firewalld_ and _ufw_) _both_ running on the server!) If +possible, either turn off the firewall to see if that is the problem, or +get three consecutive network ports, starting at port n, all three in +the range 1024-65535, opened for both tcp and udp, and use "uxplay -p n" +(or open UDP 7011,6001,6000 TCP 7100,7000,7001 and use "uxplay -p"). + +If you are _really_ sure there is no firewall, you may need to +investigate your network transmissions with a tool like netstat, but +almost always this is a firewall issue. + +### 3. Problems _after_ the client-server connection has been made: + +If you do _not_ see the message `raop_rtp_mirror starting mirroring`, +something went wrong before the client-server negotiations were +finished. For such problems, use "uxplay -d" (debug log option) to see +what is happening: it will show how far the connection process gets +before the failure occurs. You can compare your debug output to that +from a successful start of UxPlay in the [UxPlay +Wiki](https://github.com/FDH2/UxPlay/wiki). + +**If UxPlay reports that mirroring started, but you get no video or +audio, the problem is probably from a GStreamer plugin that doesn't work +on your system** (by default, GStreamer uses the "autovideosink" and +"autoaudiosink" algorithms to guess what are the "best" plugins to use +on your system). A different reason for no audio occurred when a user +with a firewall only opened two udp network ports: **three** are +required (the third one receives the audio data). + +**Raspberry Pi** devices (_Pi 4B+ and earlier: this does not apply to +the Pi 5, which does not provide hardware h264 decoding, and does not +need it_) work best with hardware GPU h264 video decoding if the +Video4Linux2 plugin in GStreamer v1.20.x or earlier has been patched +(see the UxPlay +[Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches) +for patches). This is fixed in GStreamer-1.22, and by backport patches +from this in distributions such as Raspberry Pi OS (Bullseye): **use +option `-bt709` with the GStreamer-1.18.4 from Raspberry Pi OS**. This +also needs the bcm2835-codec kernel module that is not in the standard +Linux kernel (it is available in Raspberry Pi OS, Ubuntu and Manjaro). + +- **If this kernel module is not available in your Raspberry Pi + operating system, or if GStreamer \< 1.22 is not patched, use option + `-avdec` for software h264-decoding.** + +Sometimes "autovideosink" may select the OpenGL renderer "glimagesink" +which may not work correctly on your system. Try the options "-vs +ximagesink" or "-vs xvimagesink" to see if using one of these fixes the +problem. + +Other reported problems are connected to the GStreamer VAAPI plugin (for +hardware-accelerated Intel graphics, but not NVIDIA graphics). Use the +option "-avdec" to force software h264 video decoding: this should +prevent autovideosink from selecting the vaapisink videosink. +Alternatively, find out if the gstreamer1.0-vaapi plugin is installed, +and if so, uninstall it. (If this does not fix the problem, you can +reinstall it.) + +There are some reports of other GStreamer problems with +hardware-accelerated Intel HD graphics. One user (on Debian) solved this +with "sudo apt install intel-media-va-driver-non-free". This is a driver +for 8'th (or later) generation "\*-lake" Intel chips, that seems to be +related to VAAPI accelerated graphics. + +If you _do_ have Intel HD graphics, and have installed the vaapi plugin, +but `-vs vaapisink` does not work, check that vaapi is not "blacklisted" +in your GStreamer installation: run `gst-inspect-1.0 vaapi`, if this +reports `0 features`, you need to `export GST_VAAPI_ALL_DRIVERS=1` +before running uxplay, or set this in the default environment. + +You can try to fix audio or video problems by using the +"`-as `" or "`-vs `" options to choose the +GStreamer audiosink or videosink , rather than letting GStreamer choose +one for you. (See above, in [Starting and running +UxPlay](#starting-and-running-uxplay) for choices of `` or +``.) + +The "OpenGL renderer" window created on Linux by "-vs glimagesink" +sometimes does not close properly when its "close" button is clicked. +(this is a GStreamer issue). You may need to terminate uxplay with +Ctrl-C to close a "zombie" OpenGl window. If similar problems happen +when the client sends the "Stop Mirroring" signal, try the no-close +option "-nc" that leaves the video window open. + +### 4. GStreamer issues (missing plugins, etc.): + +- clearing the user's GStreamer cache with + `rm -rf ~/.cache/gstreamer-1.0/*` may be the solution to problems + where gst-inspect-1.0 does not show a plugin that you believe is + installed. The cache will be regenerated next time GStreamer is + started. **This is the solution to puzzling problems that turn out + to come from corruption of the cache, and should be tried first.** + +If UxPlay fails to start, with a message that a required GStreamer +plugin (such as "libav") was not found, first check with the GStreamer +tool gst-inspect-1.0 to see what GStreamer knows is available. (You may +need to install some additional GStreamer "tools" package to get +gst-inspect-1.0). For, _e.g._ a libav problem, check with +"`gst-inspect-1.0 libav`". If it is not shown as available to GStreamer, +but your package manager shows the relevant package as installed (as one +user found), try entirely removing and reinstalling the package. That +user found that a solution to a "**Required gstreamer plugin 'libav' not +found**" message that kept recurring was to clear the user's gstreamer +cache. + +If it fails to start with an error like '`no element "avdec_aac"`' this +is because even though gstreamer-libav is installed. it is incomplete +because some plugin features are missing: +"`gst-inspect-1.0 | grep avdec_aac`" will show if avdec_aac is +available. Unlike other GStreamer plugins, the libav plugin is a front +end to FFmpeg codecs which provide avdec\_\*. + +- Some distributions (RedHat, SUSE, etc) provide incomplete versions + of FFmpeg because of patent issues with codecs used by certain + plugins. In those cases there will be some "extra package" provider + like [RPM fusion](https://rpmfusion.org) (RedHat), + [packman](http://packman.links2linux.org/) (SUSE) where you can get + complete packages (your distribution will usually provide + instructions for this, Mageia puts them in an optional "tainted" + repo). The packages needed may be "ffmpeg\*" or "libav\*" packages: + the GStreamer libav plugin package does not contain any codecs + itself, it just provides a way for GStreamer to use ffmpeg/libav + codec libraries which must be installed separately. For similar + reasons, distributions may ship incomplete packages of GStreamer + "plugins-bad". Use user on Fedora thought they had installed from + rpmfusion, but the system had not obeyed: _"Adding --allowerasing to + the dnf command fixed it after a restart"_. + +- starting with release UxPlay-1.65.3, UxPlay will continue to + function, but without audio in mirror mode, if avdec_aac is missing. + +To troubleshoot GStreamer execute "export GST_DEBUG=2" to set the +GStreamer debug-level environment-variable in the terminal where you +will run uxplay, so that you see warning and error messages; see +[GStreamer debugging +tools](https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html) +for how to see much more of what is happening inside GStreamer. Run +"gst-inspect-1.0" to see which GStreamer plugins are installed on your +system. + +Some extra GStreamer packages for special plugins may need to be +installed (or reinstalled: a user using a Wayland display system as an +alternative to X11 reported that after reinstalling Lubuntu 18.4, UxPlay +would not work until gstreamer1.0-x was installed, presumably for +Wayland's X11-compatibility mode). Different distributions may break up +GStreamer 1.x into packages in different ways; the packages listed above +in the build instructions should bring in other required GStreamer +packages as dependencies, but will not install all possible plugins. + +The GStreamer video pipeline, which is shown in the initial output from +`uxplay -d`, has the default form + + appsrc name=video_source ! queue ! h264parse ! decodebin ! videoconvert ! autovideosink name=video_sink sync=false + +The pipeline is fully configurable: default elements "h264parse", +"decodebin", "videoconvert", and "autovideosink" can respectively be +replaced by using uxplay options `-vp`, `-vd`, `-vc`, and `-vs`, if +there is any need to modify it (entries can be given in quotes "..." to +include options). + +### 5. Mirror screen freezes (a network problem): + +This can happen if the TCP video stream from the client stops arriving +at the server, probably because of network problems (the UDP audio +stream may continue to arrive). At 3-second intervals, UxPlay checks +that the client is still connected by sending it a request for a NTP +time signal. If a reply is not received from the client within a 0.3 sec +time-window, an "ntp timeout" is registered. If a certain number +(currently 5) of consecutive ntp timeouts occur, UxPlay assumes that the +client is "dead", and resets the connection, becoming available for +connection to a new client, or reconnection to the previous one. +Sometimes the connection may recover before the timeout limit is +reached, and if the default limit is not right for your network, it can +be modified using the option "-reset _n_", where _n_ is the desired +timeout-limit value (_n_ = 0 means "no limit"). If the connection starts +to recover after ntp timeouts, a corrupt video packet from before the +timeout may trigger a "connection reset by peer" error, which also +causes UxPlay to reset the connection. + +- When the connection is reset, the "frozen" mirror screen of the + previous connection is left in place, but does **not** block new + connections, and will be taken over by a new client connection when + it is made. + +### 6. Protocol issues (with decryption of the encrypted audio and video streams sent by the client). + +A protocol failure may trigger an unending stream of error messages, and +means that the audio decryption key (also used in video decryption) was +not correctly extracted from data sent by the client. + +The protocol was modifed in UxPlay-1.65 after it was discovered that the +client-server "pairing" step could be avoided (leading to a much quicker +connection setup, without a 5 second delay) by disabling "Supports +Legacy Pairing" (bit 27) in the "features" code UxPlay advertises on +DNS-SD Service Discovery. Most clients will then not attempt the setup +of a "shared secret key" when pairing, which is used by AppleTV for +simultaneous handling of multiple clients (UxPlay only supports one +client at a time). **This change is now well-tested, but in case it +causes any protocol failures, UxPlay can be reverted to the previous +behavior by uncommenting the previous "FEATURES_1" setting (and +commenting out the new one) in lib/dnssdint.h, and then rebuilding +UxPlay.** ("Pairing" is re-enabled when the new Apple-style one-time +"pin" authentication is activated by running UxPlay with the "-pin" +option introduced in UxPlay 1.67.) + +Protocol failure should not happen for iOS 9.3 or later clients. +However, if a client uses the same older version of the protocol that is +used by the Windows-based AirPlay client emulator _AirMyPC_, the +protocol can be switched to the older version by the setting +`OLD_PROTOCOL_CLIENT_USER_AGENT_LIST` in `UxPlay/lib/global.h`. UxPlay +reports the client's "User Agent" string when it connects. If some other +client also fails to decrypt all audio and video, try adding its "User +Agent" string in place of "xxx" in the entry "AirMyPC/2.0;xxx" in +global.h and rebuild uxplay. + +Note that for DNS-SD Service Discovery, Uxplay declares itself to be an +AppleTV3,2 (a 32 bit device) with a sourceVersion 220.68; this can also +be changed in global.h. UxPlay also works if it declares itself as an +AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen, +introduced 2017, running tvOS 12.2.1), so it does not seem to matter +what version UxPlay claims to be. + +# Changelog + +1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially +only for YouTube movies. Fix issue with NTP timeout on Windows. + +1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x +2160). Fix issue with GStreamer \>= 1.24 when client sleeps, then wakes. + +1.69 2024-08-09 Internal improvements (e.g. in -nohold option, +identifying GStreamer videosink selected by autovideosink, finding X11 +display) in anticipation of future HLS video support. New -nofreeze +option to not leave frozen video in place when a network connection is +reset. Fixes for GStreamer-1.24.x changes. + +1.68 2023-12-31 New simpler (default) method for generating a persistent +public key from the server MAC address (which can now be set with the -m +option). (The previous method is still available with -key option). New +option -reg to maintain a register of pin-authenticated clients. +Corrected volume-control: now interprets AirPlay volume range -30dB:0dB +as decibel gain attenuation, with new option -db low\[:high\] for "flat" +rescaling of the dB range. Add -taper option for a "tapered" AirPlay +volume-control profile. + +1.67 2023-11-30 Add support for Apple-style one-time pin authentication +of clients with option "-pin": (uses SRP6a authentication protocol and +public key persistence). Detection with error message of (currently) +unsupported H265 video when requesting high resolution over wired +ethernet. Removed rpi\* options (which are not valid with new Raspberry +Pi model 5, and can be replaced by combinations of other options). Added +optional argument "mac" to "-m" option, to specify a replacement MAC +address/Device ID. Update llhttp to v. 9.1.3. Add -dacp option for +exporting current client DACP info (for remotes). + +1.66 2023-09-05 Fix IPV6 support. Add option to restrict clients to +those on a list of allowed deviceIDs, or to block connections from +clients on a list of blocked deviceIDs. Fix for #207 from @thiccaxe +(screen lag in vsync mode after client wakes from sleep). + +1.65.3 2023-07-23 Add RPM spec file; add warning if required gstreamer +libav feature "avdec_aac" is missing: (this occurs in RPM-based +distributions that ship an incomplete FFmpeg for Patent or License +reasons, and rely on users installing an externally-supplied complete +FFmpeg). Mirror-mode airplay will now work without audio if avdec_aac is +missing. + +1.65 2023-06-03 Eliminate pair_setup part of connection protocol to +allow faster connections with clients (thanks to @shuax #176 for this +discovery); to revert, uncomment a line in lib/dnssdint.h. Disconnect +from audio device when connection closes, to not block its use by other +apps if uxplay is running but not connected. Fix for AirMyPC client +(broken since 1.60), so its older non-NTP timestamp protocol works with +-vsync. Corrected parsing of configuration file entries that were in +quotes. + +1.64 2023-04-23 Timestamp-based synchronization of audio and video is +now the default in Mirror mode. (Use "-vsync no" to restore previous +behavior.) A configuration file can now be used for startup options. +Also some internal cleanups and a minor bugfix that fixes #192. + +1.63 2023-02-12 Reworked audio-video synchronization, with new options +-vsync (for Mirror mode) and -async (for Audio-Only mode, to sync with +client video). Option -vsync makes software h264 decoding of streamed +videos with option -avdec viable on some recent Raspberry Pi models. +Internal change: all times are now processed in nanoseconds units. +Removed -ao option introduced in 1.62. + +1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user +synchronization of ALAC audio playing on the server with video, song +lyrics, etc. playing on the client. x = 5.0 appears to be optimal in +many cases. Quality fixes: cleanup in volume changes, timestamps, some +bugfixes. + +1.61 2022-12-30 Removed -t option (workaround for an Avahi issue, +correctly solved by opening network port UDP 5353 in firewall). Remove +-g debug flag from CMAKE_CFLAGS. Postpend (instead of prepend) build +environment CFLAGS to CMAKE_CFLAGS. Refactor parts of uxplay.cpp + +1.60 2022-12-15 Added exit with error message if DNSServiceRegister +fails (instead of just stalling). Test for Client's attempt to using +unsupported AirPlay 2 "REMOTE CONTROL" protocol (with no timing +channel), and exit if this occurs. Reworked metadata processing to +correctly parse DMAP header (previous version worked with DMAP messages +currently received, but was not correct). + +1.59 2022-12-12 remove "ZOOMFIX" compile option and make compilation +with X11-dependence the default if X11 development libraries are +detected (this now also provides fullscreen mode with a F11 or Alt+Enter +key toggle); ZOOMFIX is now automatically applied for GStreamer \< 1.20. +New cmake option -DNO_X11_DEPS compiles uxplay without X11 dependence. +Reworked internal metadata handling. Fix segfault with "-vs 0". + +1.58 2022-10-29 Add option "-nohold" that will drop existing connections +when a new client connects. Update llhttp to v8.1.0. + +1.57 2022-10-09 Minor fixes: (fix coredump on AUR on "stop mirroring", +occurs when compiled with AUR CFLAGS -DFORTIFY_SOURCE); graceful exit +when required plugins are missing; improved support for builds on +Windows. Include audioresample in GStreamer audio pipeline. + +1.56 2022-09-01 Added support for building and running UxPlay-1.56 on +Windows (no changes to Unix (Linux, \*BSD, macOS) codebase.) + +1.56 2022-07-30 Remove -bt709 from -rpi, -rpiwl, -rpifb as GStreamer is +now fixed. + +1.55 2022-07-04 Remove the bt709 fix from -v4l2 and create a new -bt709 +option (previous "-v4l2" is now "-v4l2 -bt709"). This allows the +currently-required -bt709 option to be used on its own on RPi without +-v4l2 (sometimes this give better results). + +1.54 2022-06-25 Add support for "Cover Art" display in Audio-only (ALAC) +mode. Reverted a change that caused VAAPI to crash with AMD POLARIS +graphics cards. Minor internal changes to plist code and uxplay option +parsing. + +1.53 2022-06-13 Internal changes to audio sync code, revised +documentation, Minor bugfix (fix assertion crash when resent audio +packets are empty). + +1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted +streaming debug output (readable aligned timestamps with decimal points +in seconds). Eliminate memory leaks (found by valgrind). Support for +display of ALAC (audio-only) metadata (soundtrack artist names, titles +etc.) in the uxplay terminal. + +1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option +-v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, +-v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from +1.48 that broke reconnection after "Stop Mirroring" is sent by client. + +1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI +plugins only), Changed -rpi to be for framebuffer ("lite") RPi systems +and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop +systems. Also modified timestamps from "DTS" to "PTS" for latency +improvement, plus internal cleanups. + +1.49 2022-03-28 Addded options for dumping video and/or audio to file, +for debugging, etc. h264 PPS/SPS NALU's are shown with -d. Fixed +video-not-working for M1 Mac clients. + +1.48 2022-03-11 Made the GStreamer video pipeline fully configurable, +for use with hardware h264 decoding. Support for Raspberry Pi. + +1.47 2022-02-05 Added -FPSdata option to display (in the terminal) +regular reports sent by the client about video streaming performance. +Internal cleanups of processing of video packets received from the +client. Added -reset n option to reset the connection after n ntp +timeouts (also reset after "connection reset by peer" error in video +stream). + +1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken hardware +acceleration): once again use decodebin in the video pipeline; introduce +new option "-avdec" to force software h264 decoding by libav h264, if +needed (to prevent selection of vaapisink by autovideosink). Update +llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2. Restrict +connections to one client at a time (second client must now wait for +first client to disconnect). + +1.45 2022-01-10 New behavior: close video window when client requests +"stop mirroring". (A new "no close" option "-nc" is added for users who +wish to retain previous behavior that does not close the video window). + +1.44 2021-12-13 Omit hash of aeskey with ecdh_secret for an AirMyPC +client; make an internal rearrangement of where this hash is done. Fully +report all initial communications between client and server in -d debug +mode. Replace decodebin in GStreamer video pipeline by h264-specific +elements. + +1.43 2021-12-07 Various internal changes, such as tests for successful +decryption, uniform treatment of informational/debug messages, etc., +updated README. + +1.42 2021-11-20 Fix MAC detection to work with modern Linux interface +naming practices, MacOS and \*BSD. + +1.41 2021-11-11 Further cleanups of multiple audio format support +(internal changes, separated RAOP and GStreamer audio/video startup) + +1.40 2021-11-09 Cleanup segfault in ALAC support, manpage location fix, +show request Plists in debug mode. + +1.39 2021-11-06 Added support for Apple Lossless (ALAC) audio streams. + +1.38 2021-10-8 Add -as _audiosink_ option to allow user to choose the +GStreamer audiosink. + +1.37 2021-09-29 Append "@hostname" to AirPlay Server name, where +"hostname" is the name of the server running uxplay (reworked change in +1.36). + +1.36 2021-09-29 Implemented suggestion (by @mrbesen and @PetrusZ) to use +hostname of machine runing uxplay as the default server name + +1.35.1 2021-09-28 Added the -vs 0 option for streaming audio, but not +displaying video. + +1.35 2021-09-10 now uses a GLib MainLoop, and builds on macOS (tested on +Intel Mac, 10.15 ). New option -t _timeout_ for relaunching server if no +connections were active in previous _timeout_ seconds (to renew Bonjour +registration). + +1.341 2021-09-04 fixed: render logger was not being destroyed by +stop_server() + +1.34 2021-08-27 Fixed "ZOOMFIX": the X11 window name fix was only being +made the first time the GStreamer window was created by uxplay, and not +if the server was relaunched after the GStreamer window was closed, with +uxplay still running. Corrected in v. 1.34 + +### Building OpenSSL \>= 1.1.1 from source. + +If you need to do this, note that you may be able to use a newer version +(OpenSSL-3.0.1 is known to work). You will need the standard development +toolset (autoconf, automake, libtool). Download the source code from +. Install the downloaded openssl by +opening a terminal in your Downloads directory, and unpacking the source +distribution: ("tar -xvzf openssl-3.0.1.tar.gz ; cd openssl-3.0.1"). +Then build/install with "./config ; make ; sudo make install_dev". This +will typically install the needed library `libcrypto.*`, either in +/usr/local/lib or /usr/local/lib64. + +_(Ignore the following for builds on MacOS:)_ On some systems like +Debian or Ubuntu, you may also need to add a missing entry +`/usr/local/lib64` in /etc/ld.so.conf (or place a file containing +"/usr/local/lib64/libcrypto.so" in /etc/ld.so.conf.d) and then run "sudo +ldconfig". + +### Building libplist \>= 2.0.0 from source. + +_(Note: on Debian 9 "Stretch" or Ubuntu 16.04 LTS editions, you can +avoid this step by installing libplist-dev and libplist3 from Debian 10 +or Ubuntu 18.04.)_ As well as the usual build tools (autoconf, automake, +libtool), you may need to also install some libpython\*-dev package. +Download the latest source with git from +, or get the source from +the Releases section (use the \*.tar.bz2 release, **not** the \*.zip or +\*.tar.gz versions): download +[libplist-2.3.0](https://github.com/libimobiledevice/libplist/releases/download/2.3.0/libplist-2.3.0.tar.bz2), +then unpack it ("tar -xvjf libplist-2.3.0.tar.bz2 ; cd libplist-2.3.0"), +and build/install it: ("./configure ; make ; sudo make install"). This +will probably install libplist-2.0.\* in /usr/local/lib. The new +libplist-2.3.0 release should be compatible with UxPlay; +[libplist-2.2.0](https://github.com/libimobiledevice/libplist/releases/download/2.2.0/libplist-2.2.0.tar.bz2) +is also available if there are any issues. + +_(Ignore the following for builds on MacOS:)_ On some systems like +Debian or Ubuntu, you may also need to add a missing entry +`/usr/local/lib` in /etc/ld.so.conf (or place a file containing +"/usr/local/lib/libplist-2.0.so" in /etc/ld.so.conf.d) and then run +"sudo ldconfig". + +# Disclaimer + +All the resources in this repository are written using only freely +available information from the internet. The code and related resources +are meant for educational purposes only. It is the responsibility of the +user to make sure all local laws are adhered to. + +This project makes use of a third-party GPL library for handling +FairPlay. The legal status of that library is unclear. Should you be a +representative of Apple and have any objections against the legality of +the library and its use in this project, please contact the developers +and the appropriate steps will be taken. + +Given the large number of third-party AirPlay receivers (mostly +closed-source) available for purchase, it is our understanding that an +open source implementation of the same functionality wouldn't violate +any of Apple's rights either. + +# UxPlay authors + +_\[adapted from fdraschbacher's notes on RPiPlay antecedents\]_ + +The code in this repository accumulated from various sources over time. +Here is an attempt at listing the various authors and the components +they created: + +UxPlay was initially created by **antimof** from RPiPlay, by replacing +its Raspberry-Pi-adapted OpenMAX video and audio rendering system with +GStreamer rendering for desktop Linux systems; the antimof work on code +in `renderers/` was later backported to RPiPlay, and the antimof project +became dormant, but was later revived at the [current GitHub +site](http://github.com/FDH2/UxPlay) to serve a wider community of +users. + +The previous authors of code included in UxPlay by inheritance from +RPiPlay include: + +- **EstebanKubata**: Created a FairPlay library called + [PlayFair](https://github.com/EstebanKubata/playfair). Located in + the `lib/playfair` folder. License: GNU GPL +- **Juho Vähä-Herttua** and contributors: Created an AirPlay audio + server called [ShairPlay](https://github.com/juhovh/shairplay), + including support for Fairplay based on PlayFair. Most of the code + in `lib/` originally stems from this project. License: GNU LGPLv2.1+ +- **dsafa22**: Created an AirPlay 2 mirroring server + [AirplayServer](https://github.com/dsafa22/AirplayServer) (seems + gone now), for Android based on ShairPlay. Code is preserved + [here](https://github.com/jiangban/AirplayServer), and [see + here](https://github.com/FDH2/UxPlay/wiki/AirPlay2) for the + description of the analysis of the AirPlay 2 mirror protocol that + made RPiPlay possible, by the AirplayServer author. All code in + `lib/` concerning mirroring is dsafa22's work. License: GNU + LGPLv2.1+ +- **Florian Draschbacher** (FD-) and contributors: adapted dsafa22's + Android project for the Raspberry Pi, with extensive cleanups, + debugging and improvements. The project + [RPiPlay](https://github.com/FD-/RPiPlay) is basically a port of + dsafa22's code to the Raspberry Pi, utilizing OpenMAX and OpenSSL + for better performance on the Pi. License GPL v3. FD- has written an + interesting note on the history of [Airplay protocol + versions](http://github.com/FD-/RPiPlay#airplay-protocol-versions), + available at the RPiPlay github repository. + +Independent of UxPlay, but used by it and bundled with it: + +- **Fedor Indutny** (of Node.js, and formerly Joyent, Inc) and + contributors: Created an http parsing library called + [llhttp](https://github.com/nodejs/llhttp). Located at + `lib/llhttp/`. License: MIT diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..0ee38e9 --- /dev/null +++ b/README.txt @@ -0,0 +1,1956 @@ +# UxPlay 1.71: AirPlay-Mirror and AirPlay-Audio server for Linux, macOS, and Unix (now also runs on Windows). + +### **Now developed at the GitHub site (where ALL user issues should be posted, and latest versions can be found).** + +- ***NEW in v1.71**: Support for (YouTube) HLS (HTTP Live Streaming) + video with the new "-hls" option.* Click on the airplay icon in the + YouTube app to stream video. (You may need to wait until + advertisements have finished or been skipped before clicking the + YouTube airplay icon.) **Please report any issues with this new + feature of UxPlay**. + +## Highlights: + +- GPLv3, open source. +- Originally supported only AirPlay Mirror protocol, now has added + support for AirPlay Audio-only (Apple Lossless ALAC) streaming from + current iOS/iPadOS clients. **Now with support for Airplay HLS + video-streaming (currently only YouTube video).** +- macOS computers (2011 or later, both Intel and "Apple Silicon" M1/M2 + systems) can act either as AirPlay clients, or as the server running + UxPlay. Using AirPlay, UxPlay can emulate a second display for macOS + clients. +- Support for older iOS clients (such as 32-bit iPad 2nd gen., iPod + Touch 5th gen. and iPhone 4S, when upgraded to iOS 9.3.5, or later + 64-bit devices), plus a Windows AirPlay-client emulator, AirMyPC. +- Uses GStreamer plugins for audio and video rendering (with options + to select different hardware-appropriate output "videosinks" and + "audiosinks", and a fully-user-configurable video streaming + pipeline). +- Support for server behind a firewall. +- Raspberry Pi support **both with and without hardware video + decoding** by the Broadcom GPU. *Tested on Raspberry Pi Zero 2 W, 3 + Model B+, 4 Model B, and 5.* +- Support for running on Microsoft Windows (builds with the MinGW-64 + compiler in the unix-like MSYS2 environment). + +Note: AirPlay2 multi-room audio streaming is not supported: use +[shairport-sync](https://github.com/mikebrady/shairport-sync) for that. + +## Packaging status (Linux and \*BSD distributions) + +[![Current Packaging +status](https://repology.org/badge/vertical-allrepos/uxplay.svg)](https://repology.org/project/uxplay/versions). + +- Install uxplay on Debian-based Linux systems with + "`sudo apt install uxplay`"; on FreeBSD with + "`sudo pkg install uxplay`". Also available on Arch-based systems + through AUR. Since v. 1.66, uxplay is now also packaged in RPM + format by Fedora 38 ("`sudo dnf install uxplay`"). + +- For other RPM-based distributions which have not yet packaged + UxPlay, a RPM "specfile" **uxplay.spec** is now provided with recent + [releases](https://github.com/FDH2/UxPlay/releases) (see their + "Assets"), and can also be found in the UxPlay source top directory. + See the section on using this specfile for [building an installable + RPM package](#building-an-installable-rpm-package). + +After installation: + +- (On Linux and \*BSD): if a firewall is active on the server hosting + UxPlay, make sure the default network port (UDP 5353) for + mDNS/DNS-SD queries is open (see [Troubleshooting](#troubleshooting) + below for more details); also open three UDP and three TCP ports for + Uxplay, and use the "uxplay -p ``{=html}" option (see + "`man uxplay`" or "`uxplay -h`"). + +- Even if you install your distribution's pre-compiled uxplay binary + package, you may need to read the instructions below for [running + UxPlay](#running-uxplay) to see which of your distribution's + **GStreamer plugin packages** you should also install. + +- For Audio-only mode (Apple Music, etc.) best quality is obtained + with the option "uxplay -async", but there is then a 2 second + latency imposed by iOS. + +- Add any UxPlay options you want to use as defaults to a startup file + `~/.uxplayrc` (see "`man uxplay`" or "`uxplay -h`" for format and + other possible locations). In particular, if your system uses + PipeWire audio or Wayland video systems, you may wish to add "as + pipewiresink" or "vs waylandsink" as defaults to the file. *(Output + from terminal commands "ps waux \| grep pulse" or "pactl info" will + contain "pipewire" if your Linux/BSD system uses it).* + +- On Raspberry Pi: models using hardware h264 video decoding by the + Broadcom GPU (models 4B and earlier) may require the uxplay option + -bt709. If you use Ubuntu 22.10 or earlier, GStreamer must be + [patched](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches) + to use hardware video decoding by the Broadcom GPU (also recommended + but optional for Raspberry Pi OS (Bullseye): the patched GStreamer + does not need option " -bt709\`". The need for -bt709 when hardware + video decoding is used seems to have reappeared starting with + GStreamer-1.22. + +To (easily) compile the latest UxPlay from source, see the section +[Getting UxPlay](#getting-uxplay). + +# Detailed description of UxPlay + +This project is a GPLv3 open source unix AirPlay2 Mirror server for +Linux, macOS, and \*BSD. It was initially developed by +[antimof](http://github.com/antimof/Uxplay) using code from +OpenMAX-based [RPiPlay](https://github.com/FD-/RPiPlay), which in turn +derives from [AirplayServer](https://github.com/KqsMea8/AirplayServer), +[shairplay](https://github.com/juhovh/shairplay), and +[playfair](https://github.com/EstebanKubata/playfair). (The antimof site +is no longer involved in development, but periodically posts updates +pulled from the new main [UxPlay site](https://github.com/FDH2/UxPlay)). + +UxPlay is tested on a number of systems, including (among others) Debian +(10 "Buster", 11 "Bullseye", 12 "Bookworm"), Ubuntu (20.04 LTS, 22.04 +LTS, 23.04 (also Ubuntu derivatives Linux Mint, Pop!\_OS), Red Hat and +clones (Fedora 38, Rocky Linux 9.2), openSUSE Leap 15.5, Mageia 9, +OpenMandriva "ROME", PCLinuxOS, Arch Linux, Manjaro, and should run on +any Linux system. Also tested on macOS Catalina and Ventura (Intel) and +Sonoma (M2), FreeBSD 14.0, Windows 10 and 11 (64 bit). + +On Raspberry Pi 4 model B, it is tested on Raspberry Pi OS (Bullseye and +Bookworm) (32- and 64-bit), Ubuntu 22.04 LTS and 23.04, Manjaro RPi4 +23.02, and (without hardware video decoding) on openSUSE 15.5. Also +tested on Raspberry Pi Zero 2 W, 3 model B+, and now 5. + +Its main use is to act like an AppleTV for screen-mirroring (with audio) +of iOS/iPadOS/macOS clients (iPhone, iPod Touch, iPad, Mac computers) on +the server display of a host running Linux, macOS, or other unix (and +now also Microsoft Windows). UxPlay supports Apple's AirPlay2 protocol +using "Legacy Protocol", but some features are missing. (Details of what +is publicly known about Apple's AirPlay 2 protocol can be found +[here](https://openairplay.github.io/airplay-spec/), +[here](https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol) +and [here](https://emanuelecozzi.net/docs/airplay2); see also +[pyatv](https://pyatv.dev/documentation/protocols) which could be a +resource for adding modern protocols.) While there is no guarantee that +future iOS releases will keep supporting "Legacy Protocol", iOS 17 +continues support. + +The UxPlay server and its client must be on the same local area network, +on which a **Bonjour/Zeroconf mDNS/DNS-SD server** is also running (only +DNS-SD "Service Discovery" service is strictly necessary, it is not +necessary that the local network also be of the ".local" mDNS-based +type). On Linux and BSD Unix servers, this is usually provided by +[Avahi](https://www.avahi.org), through the avahi-daemon service, and is +included in most Linux distributions (this service can also be provided +by macOS, iOS or Windows servers). + +Connections to the UxPlay server by iOS/MacOS clients can be initiated +both in **AirPlay Mirror** mode (which streams lossily-compressed AAC +audio while mirroring the client screen, or in the alternative **AirPlay +Audio** mode which streams Apple Lossless (ALAC) audio without screen +mirroring. In **Audio** mode, metadata is displayed in the uxplay +terminal; if UxPlay option `-ca ` is used, the accompanying cover +art is also output to a periodically-updated file ``, and can be +viewed with a (reloading) graphics viewer of your choice. *Switching +between* **Mirror** *and* **Audio** *modes during an active connection +is possible: in* **Mirror** *mode, stop mirroring (or close the mirror +window) and start an* **Audio** *mode connection, switch back by +initiating a* **Mirror** *mode connection; cover-art display +stops/restarts as you leave/re-enter* **Audio** *mode.* + +- **Note that Apple video-DRM (as found in "Apple TV app" content on + the client) cannot be decrypted by UxPlay, and the Apple TV app + cannot be watched using UxPlay's AirPlay Mirror mode (only the + unprotected audio will be streamed, in AAC format).** + +- **With the new "-hls" option, UxPlay now also supports non-Mirror + AirPlay video streaming (where the client controls a web server on + the AirPlay server that directly receives HLS content to avoid it + being decoded and re-encoded by the client). This currently only + supports streaming of YouTube videos. Without the -hls option, using + the icon for AirPlay video in apps such as the YouTube app will only + send audio (in lossless ALAC format) without the accompanying + video.** + +### Possibility for using hardware-accelerated h264/h265 video-decoding, if available. + +UxPlay uses [GStreamer](https://gstreamer.freedesktop.org) "plugins" for +rendering audio and video. This means that video and audio are supported +"out of the box", using a choice of plugins. AirPlay streams video in +h264 format: gstreamer decoding is plugin agnostic, and uses accelerated +GPU hardware h264 decoders if available; if not, software decoding is +used. + +- **VAAPI for Intel and AMD integrated graphics, NVIDIA with "Nouveau" + open-source driver** + + With an Intel or AMD GPU, hardware decoding with the open-source + VAAPI gstreamer plugin is preferable. The open-source "Nouveau" + drivers for NVIDIA graphics are also in principle supported: see + [here](https://nouveau.freedesktop.org/VideoAcceleration.html), but + this requires VAAPI to be supplemented with firmware extracted from + the proprietary NVIDIA drivers. + +- **NVIDIA with proprietary drivers** + + The `nvh264dec` plugin (included in gstreamer1.0-plugins-bad since + GStreamer-1.18.0) can be used for accelerated video decoding on the + NVIDIA GPU after NVIDIA's CUDA driver `libcuda.so` is installed. For + GStreamer-1.16.3 or earlier, the plugin is called `nvdec`, and must + be [built by the + user](https://github.com/FDH2/UxPlay/wiki/NVIDIA-nvdec-and-nvenc-plugins). + +- **Video4Linux2 support for h264 hardware decoding on Raspberry Pi + (Pi 4B and older)** + + Raspberry Pi (RPi) computers (tested on Pi 4 Model B) can now run + UxPlay using software video decoding, but hardware-accelerated + h264/h265 decoding by firmware in the Pi's Broadcom 2835 GPU is + prefered. UxPlay accesses this using the GStreamer-1.22 Video4Linux2 + (v4l2) plugin; Uses the out-of-mainline Linux kernel module + bcm2835-codec maintained by Raspberry Pi, so far only included in + Raspberry Pi OS, and two other distributions (Ubuntu, Manjaro) + available with Raspberry Pi Imager. *(For GStreamer \< 1.22, see the + [UxPlay + Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches))*. + Pi model 5 has no support for hardware H264 decoding, as its CPU is + powerful enough for satisfactory software H264 decoding + +- **Support for h265 (HEVC) hardware decoding on Raspberry Pi (Pi 4 + model B and Pi 5)** + + These Raspberry Pi models have a dedicated HEVC decoding block (not + the GPU), with a driver "rpivid" which is not yet in the mainline + Linux kernel (but is planned to be there in future). Unfortunately + it produces decoded video in a non-standard pixel format (NC30 or + "SAND") which will not be supported by GStreamer until the driver is + in the mainline kernel; without this support, UxPlay support for + HEVC hardware decoding on Raspberry Pi will not work. + +### Note to packagers: + +UxPlay's GPLv3 license does not have an added "GPL exception" explicitly +allowing it to be distributed in compiled form when linked to OpenSSL +versions **prior to v. 3.0.0** (older versions of OpenSSL have a license +clause incompatible with the GPL unless OpenSSL can be regarded as a +"System Library", which it is in \*BSD). Many Linux distributions treat +OpenSSL as a "System Library", but some (e.g. Debian) do not: in this +case, the issue is solved by linking with OpenSSL-3.0.0 or later. + +# Getting UxPlay + +Either download and unzip +[UxPlay-master.zip](https://github.com/FDH2/UxPlay/archive/refs/heads/master.zip), +or (if git is installed): "git clone https://github.com/FDH2/UxPlay". +You can also download a recent or earlier version listed in +[Releases](https://github.com/FDH2/UxPlay/releases). + +- A recent UxPlay can also be found on the original [antimof + site](https://github.com/antimof/UxPlay); that original project is + inactive, but is usually kept current or almost-current with the + [active UxPlay github site](https://github.com/FDH2/UxPlay) (thank + you antimof!). + +## Building UxPlay on Linux (or \*BSD): + +### Debian-based systems: + +(Adapt these instructions for non-Debian-based Linuxes or \*BSD; for +macOS, see specific instruction below). See +[Troubleshooting](#troubleshooting) below for help with any +difficulties. + +You need a C/C++ compiler (e.g. g++) with the standard development +libraries installed. Debian-based systems provide a package +"build-essential" for use in compiling software. You also need +pkg-config: if it is not found by "`which pkg-config`", install +pkg-config or its work-alike replacement pkgconf. Also make sure that +cmake\>=3.10 is installed: "`sudo apt install cmake`" (add +`build-essential` and `pkg-config` (or `pkgconf`) to this if needed). + +Make sure that your distribution provides OpenSSL 1.1.1 or later, and +libplist 2.0 or later. (This means Debian 10 "Buster" based systems +(e.g, Ubuntu 18.04) or newer; on Debian 10 systems "libplist" is an +older version, you need "libplist3".) If it does not, you may need to +build and install these from source (see instructions at the end of this +README). + +If you have a non-standard OpenSSL installation, you may need to set the +environment variable OPENSSL_ROOT_DIR (*e.g.* , +"`export OPENSSL_ROOT_DIR=/usr/local/lib64`" if that is where it is +installed). Similarly, for non-standard (or multiple) GStreamer +installations, set the environment variable GSTREAMER_ROOT_DIR to the +directory that contains the ".../gstreamer-1.0/" directory of the +gstreamer installation that UxPlay should use (if this is *e.g.* +"\~/my_gstreamer/lib/gstreamer-1.0/", set this location with +"`export GSTREAMER_ROOT_DIR=$HOME/my_gstreamer/lib`"). + +- Most users will use the GStreamer supplied by their distribution, + but a few (in particular users of Raspberry Pi OS Lite Legacy + (Buster) on a Raspberry Pi model 4B who wish to stay on that + unsupported Legacy OS for compatibility with other apps) should + instead build a newer Gstreamer from source following [these + instructions](https://github.com/FDH2/UxPlay/wiki/Building-latest-GStreamer-from-source-on-distributions-with-older-GStreamer-(e.g.-Raspberry-Pi-OS-).) + . **Do this *before* building UxPlay**. + +In a terminal window, change directories to the source directory of the +downloaded source code ("UxPlay-\*", "\*" = "master" or the release tag +for zipfile downloads, "UxPlay" for "git clone" downloads), then follow +the instructions below: + +**Note:** By default UxPlay will be built with optimization for the +computer it is built on; when this is not the case, as when you are +packaging for a distribution, use the cmake option +`-DNO_MARCH_NATIVE=ON`. + +If you use X11 Windows on Linux or \*BSD, and wish to toggle in/out of +fullscreen mode with a keypress (F11 or Alt_L+Enter) UxPlay needs to be +built with a dependence on X11. Starting with UxPlay-1.59, this will be +done by default **IF** the X11 development libraries are installed and +detected. Install these with "`sudo apt install libx11-dev`". If +GStreamer \< 1.20 is detected, a fix needed by screen-sharing apps +(*e.g.*, Zoom) will also be made. + +- If X11 development libraries are present, but you wish to build + UxPlay *without* any X11 dependence, use the cmake option + `-DNO_X11_DEPS=ON`. + +1. `sudo apt install libssl-dev libplist-dev`". (*unless you need to + build OpenSSL and libplist from source*). +2. `sudo apt install libavahi-compat-libdnssd-dev` +3. `sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev`. + (\**Skip if you built Gstreamer from source*) +4. `cmake .` (*For a cleaner build, which is useful if you modify the + source, replace this by* "`mkdir build; cd build; cmake ..`": *you + can then delete the contents of the `build` directory if needed, + without affecting the source.*) Also add any cmake "`-D`" options + here as needed (e.g, `-DNO_X11_DEPS=ON` or `-DNO_MARCH_NATIVE=ON`). +5. `make` +6. `sudo make install` (you can afterwards uninstall with + `sudo make uninstall` in the same directory in which this was run). + +This installs the executable file "`uxplay`" to `/usr/local/bin`, (and +installs a manpage to somewhere standard like +`/usr/local/share/man/man1` and README files to somewhere like +`/usr/local/share/doc/uxplay`). (If "man uxplay" fails, check if +\$MANPATH is set: if so, the path to the manpage (usually +/usr/local/share/man/) needs to be added to \$MANPATH .) The uxplay +executable can also be found in the build directory after the build +process, if you wish to test before installing (in which case the +GStreamer plugins must first be installed). + +### Building on non-Debian Linux and \*BSD + +\*\*For those with RPM-based distributions, a RPM spec file uxplay.spec +is also available: see [Building an installable rpm +package](#building-an-installable-rpm-package). + +- **Red Hat, or clones like CentOS (now continued as Rocky Linux or + Alma Linux):** (sudo dnf install, or sudo yum install) openssl-devel + libplist-devel avahi-compat-libdns_sd-devel gstreamer1-devel + gstreamer1-plugins-base-devel (+libX11-devel for fullscreen X11) + *(some of these may be in the "CodeReady" add-on repository, called + "PowerTools" by clones)* + +- **Mageia, PCLinuxOS, OpenMandriva:** Same as Red Hat, except for + name changes: (Mageia) "gstreamer1.0-devel", + "gstreamer-plugins-base1.0-devel"; (OpenMandriva) + "libopenssl-devel", "gstreamer-devel", + "libgst-plugins-base1.0-devel". PCLinuxOS: same as Mageia, but uses + synaptic (or apt) as its package manager. + +- **openSUSE:** (sudo zypper install) libopenssl-3-devel (formerly + libopenssl-devel) libplist-2_0-devel (formerly libplist-devel) + avahi-compat-mDNSResponder-devel gstreamer-devel + gstreamer-plugins-base-devel (+ libX11-devel for fullscreen X11). + +- **Arch Linux** (*Also available as a package in AUR*): (sudo pacman + -Syu) openssl libplist avahi gst-plugins-base. + +- **FreeBSD:** (sudo pkg install) libplist gstreamer1. Either + avahi-libdns or mDNSResponder must also be installed to provide the + dns_sd library. OpenSSL is already installed as a System Library. + +#### Building an installable RPM package + +First-time RPM builders should first install the rpm-build and +rpmdevtools packages, then create the rpmbuild tree with +"`rpmdev-setuptree`". Then download and copy uxplay.spec into +`~/rpmbuild/SPECS`. In that directory, run +"`rpmdev-spectool -g -R uxplay.spec`" to download the corresponding +source file `uxplay-*.tar.gz` into `~/rpmbuild/SOURCES` +("rpmdev-spectool" may also be just called "spectool"); then run +"`rpmbuild -ba uxplay.spec`" (you will need to install any required +dependencies this reports). This should create the uxplay RPM package in +a subdirectory of `~/rpmbuild/RPMS`. (**uxplay.spec** is tested on +Fedora 38, Rocky Linux 9.2, openSUSE Leap 15.5, Mageia 9, OpenMandriva, +PCLinuxOS; it can be easily modified to include dependency lists for +other RPM-based distributions.) + +## Running UxPlay + +### Installing plugins (Debian-based Linux distributions, including Ubuntu and Raspberry Pi OS) (*skip if you built a complete GStreamer from source*) + +Next install the GStreamer plugins that are needed with +`sudo apt install gstreamer1.0-`. Values of `` required +are: + +1. "**plugins-base**" +2. "**libav**" (for sound), +3. "**plugins-good**" (for v4l2 hardware h264 decoding) +4. "**plugins-bad**" (for h264 decoding). + +**Debian-based distributions split some of the plugin packages into +smaller pieces:** some that may also be needed include "**gl**" for +OpenGL support (this provides the "-vs glimagesink" videosink, which can +be very useful in many systems (including Raspberry Pi), and should +always be used when using h264/h265 decoding by a NVIDIA GPU), +"**gtk3**" (which provides the "-vs gtksink" videosink), and "**x**" for +X11 support, although these may already be installed; "**vaapi**" is +needed for hardware-accelerated h264 video decoding by Intel or AMD +graphics (but not for use with NVIDIA using proprietary drivers). If +sound is not working, "**alsa**"","**pulseaudio**", or "**pipewire**" +plugins may need to be installed, depending on how your audio is set up. + +- Also install "**gstreamer1.0-tools**" to get the utility + gst-inspect-1.0 for examining the GStreamer installation. + +### Installing plugins (Non-Debian-based Linux or \*BSD) (*skip if you built a complete GStreamer from source*) + +In some cases, because of patent issues, the libav plugin feature +**avdec_aac** needed for decoding AAC audio in mirror mode is not +provided in the official distribution: get it from community +repositories for those distributions. + +- **Red Hat, or clones like CentOS (now continued as Rocky Linux or + Alma Linux):** Install gstreamer1-libav gstreamer1-plugins-bad-free + (+ gstreamer1-vaapi for Intel/AMD graphics). In recent Fedora, + gstreamer1-libav is renamed gstreamer1-plugin-libav. **To get + avdec_aac, install packages from + [rpmfusion.org](https://rpmfusion.org)**: (get ffmpeg-libs from + rpmfusion; on RHEL or clones, but not recent Fedora, also get + gstreamer1-libav from there). + +- **Mageia, PCLinuxOS, OpenMandriva:** Install gstreamer1.0-libav + gstreamer1.0-plugins-bad (+ gstreamer1.0-vaapi for Intel/AMD + graphics). **On Mageia, to get avdec_aac, install ffmpeg from the + "tainted" repository**, (which also provides a more complete + gstreamer1.0-plugins-bad). + +- **openSUSE:** Install gstreamer-plugins-libav gstreamer-plugins-bad + (+ gstreamer-plugins-vaapi for Intel/AMD graphics). **To get + avdec_aac, install libav\* packages for openSUSE from + [Packman](https://ftp.gwdg.de/pub/linux/misc/packman/suse/) + "Essentials"**; recommendation: after adding the Packman repository, + use the option in YaST Software management to switch all system + packages for multimedia to Packman). + +- **Arch Linux** Install gst-plugins-good gst-plugins-bad gst-libav (+ + gstreamer-vaapi for Intel/AMD graphics). + +- **FreeBSD:** Install gstreamer1-libav, gstreamer1-plugins, + gstreamer1-plugins-\* (\* = core, good, bad, x, gtk, gl, vulkan, + pulse, v4l2, ...), (+ gstreamer1-vaapi for Intel/AMD graphics). + +### Starting and running UxPlay + +Since UxPlay-1.64, UxPlay can be started with options read from a +configuration file, which will be the first found of (1) a file with a +path given by environment variable `$UXPLAYRC`, (2) `~/.uxplayrc` in the +user's home directory ("\~"), (3) `~/.config/uxplayrc`. The format is +one option per line, omitting the initial `"-"` of the command-line +option. Lines in the configuration file beginning with `"#"` are treated +as comments and ignored. + +**Run uxplay in a terminal window**. On some systems, you can specify +fullscreen mode with the `-fs` option, or toggle into and out of +fullscreen mode with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C +(or close the window) to terminate it when done. If the UxPlay server is +not seen by the iOS client's drop-down "Screen Mirroring" panel, check +that your DNS-SD server (usually avahi-daemon) is running: do this in a +terminal window with `systemctl status avahi-daemon`. If this shows the +avahi-daemon is not running, control it with +`sudo systemctl [start,stop,enable,disable] avahi-daemon` (on +non-systemd systems, such as \*BSD, use +`sudo service avahi-daemon [status, start, stop, restart, ...]`). If +UxPlay is seen, but the client fails to connect when it is selected, +there may be a firewall on the server that prevents UxPlay from +receiving client connection requests unless some network ports are +opened: **if a firewall is active, also open UDP port 5353 (for mDNS +queries) needed by Avahi**. See [Troubleshooting](#troubleshooting) +below for help with this or other problems. + +- Unlike an Apple TV, the UxPlay server does not by default require + clients to initially "pair" with it using a pin code displayed by + the server (after which the client "trusts" the server, and does not + need to repeat this). Since v1.67, Uxplay offers such + "pin-authentication" as an option: see "`-pin`" and "`-reg`" in + [Usage](#usage) for details, if you wish to use it. *Some clients + with MDM (Mobile Device Management, often present on employer-owned + devices) are required to use pin-authentication: UxPlay will provide + this even when running without the pin option.* + +- By default, UxPlay is locked to its current client until that client + drops the connection; since UxPlay-1.58, the option `-nohold` + modifies this behavior so that when a new client requests a + connection, it removes the current client and takes over. UxPlay + 1.66 introduces a mechanism ( `-restrict`, `-allow `, + `-block `) to control which clients are allowed to connect, + using their "deviceID" (which in Apple devices appears to be + immutable). + +- In Mirror mode, GStreamer has a choice of **two** methods to play + video with its accompanying audio: prior to UxPlay-1.64, the video + and audio streams were both played as soon as possible after they + arrived (the GStreamer "*sync=false*" method), with a GStreamer + internal clock used to try to keep them synchronized. **Starting + with UxPlay-1.64, the other method (GStreamer's "*sync=true*" mode), + which uses timestamps in the audio and video streams sent by the + client, is the new default**. On low-decoding-power UxPlay hosts + (such as Raspberry Pi Zero W or 3 B+ models) this will drop video + frames that cannot be decoded in time to play with the audio, making + the video jerky, but still synchronized. + +The older method which does not drop late video frames worked well on +more powerful systems, and is still available with the UxPlay option +"`-vsync no`"; this method is adapted to "live streaming", and may be +better when using UxPlay as a second monitor for a Mac computer, for +example, while the new default timestamp-based method is best for +watching a video, to keep lip movements and voices synchronized. +(Without use of timestamps, video will eventually lag behind audio if it +cannot be decoded fast enough: hardware-accelerated video-decoding +helped to prevent this previously when timestamps were not being used.) + +- In Audio-only mode the GStreamer "sync=false" mode (not using + timestamps) is still the default, but if you want to keep the audio + playing on the server synchronized with the video showing on the + client, use the `-async` timestamp-based option. (An example might + be if you want to follow the Apple Music lyrics on the client while + listening to superior sound on the UxPlay server). This delays the + video on the client to match audio on the server, so leads to a + slight delay before a pause or track-change initiated on the client + takes effect on the audio played by the server. + +AirPlay volume-control attenuates volume (gain) by up to -30dB: the +decibel range -30:0 can be rescaled from *Low*:0, or *Low*:*High*, using +the option `-db` ("-db *Low*" or "-db *Low*:*High*"), *Low* must be +negative. Rescaling is linear in decibels. Note that GStreamer's audio +format will "clip" any audio gain above +20db, so keep *High* below that +level. The option `-taper` provides a "tapered" AirPlay volume-control +profile some users may prefer. + +The -vsync and -async options also allow an optional positive (or +negative) audio-delay adjustment in *milliseconds* for fine-tuning : +`-vsync 20.5` delays audio relative to video by 0.0205 secs; a negative +value advances it.) + +- you may find video is improved by the setting -fps 60 that allows + some video to be played at 60 frames per second. (You can see what + framerate is actually streaming by using -vs fpsdisplaysink, and/or + -FPSdata.) When using this, you should use the default + timestamp-based synchronization option `-vsync`. + +- Since UxPlay-1.54, you can display the accompanying "Cover Art" from + sources like Apple Music in Audio-Only (ALAC) mode: run + "`uxplay -ca &`" in the background, then run a image viewer + with an autoreload feature: an example is "feh": run + "`feh -R 1 `" in the foreground; terminate feh and then Uxplay + with "`ctrl-C fg ctrl-C`". + +By default, GStreamer uses an algorithm to search for the best +"videosink" (GStreamer's term for a graphics driver to display images) +to use. You can overide this with the uxplay option `-vs `. +Which videosinks are available depends on your operating system and +graphics hardware: use +"`gst-inspect-1.0 | grep sink | grep -e video -e Video -e image`" to see +what is available. Some possibilites on Linux/\*BSD are: + +- **glimagesink** (OpenGL), **waylandsink** + +- **xvimagesink**, **ximagesink** (X11) + +- **kmssink**, **fbdevsink** (console graphics without X11) + +- **vaapisink** (for Intel/AMD hardware-accelerated graphics); for + NVIDIA hardware graphics (with CUDA) use **glimagesink** combined + with "`-vd nvh264dec`" (or "nvh264sldec", a new variant which will + become "nvh264dec" in GStreamer-1.24). + +- If the server is "headless" (no attached monitor, renders audio + only) use `-vs 0`. + +Note that videosink options can set using quoted arguments to -vs: +*e.g.*, `-vs "xvimagesink display=:0"`: ximagesink and xvimagesink allow +an X11 display name to be specified, and waylandsink has a similar +option. Videosink options ("properties") can be found in their GStreamer +description pages,such as +https://gstreamer.freedesktop.org/documentation/xvimagesink . + +GStreamer also searches for the best "audiosink"; override its choice +with `-as `. Choices on Linux include pulsesink, alsasink, +pipewiresink, oss4sink; see what is available with +`gst-inspect-1.0 | grep sink | grep -e audio -e Audio`. + +**One common problem involves GStreamer attempting to use +incorrectly-configured or absent accelerated hardware h264 video +decoding (e.g., VAAPI). Try "`uxplay -avdec`" to force software video +decoding; if this works you can then try to fix accelerated hardware +video decoding if you need it, or just uninstall the GStreamer vaapi +plugin.** + +See [Usage](#usage) for more run-time options. + +### **Special instructions for Raspberry Pi (tested on Raspberry Pi Zero 2 W, 3 Model B+, 4 Model B, and 5 only)**: + +- For Framebuffer video (for Raspberry Pi OS "Lite" and other non-X11 + distributions) use the KMS videosink "-vs kmssink" (the DirectFB + framebuffer videosink "dfbvideosink" is broken on the Pi, and + segfaults). *In this case you should explicitly use the "-vs + kmssink" option, as without it, autovideosink does not find the + correct videosink.* + +- Raspberry Pi 5 does not provide hardware H264 decoding (and does not + need it). + +- Pi Zero 2 W, 3 Model B+ and 4 Model B should use hardware H264 + decoding by the Broadcom GPU, but it requires an out-of-mainstream + kernel module bcm2835_codec maintained in the [Raspberry Pi kernel + tree](https://github.com/raspberrypi/linux); distributions that are + known to supply it include Raspberry Pi OS, Ubuntu, and + Manjaro-RPi4. Use software decoding (option -avdec) if this module + is not available. + +- Uxplay uses the Video4Linux2 (v4l2) plugin from GStreamer-1.22 and + later to access the GPU, if hardware H264 decoding is used. This + should happen automatically. The option -v4l2 can be used, but it is + usually best to just let GStreamer find the best video pipeline by + itself. + +- On older distributions (GStreamer \< 1.22), the v4l2 plugin needs a + patch: see the [UxPlay + Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches). + Legacy Raspberry Pi OS (Bullseye) has a partially-patched + GStreamer-1.18.4 which needs the uxplay option -bt709 (and don't use + -v4l2); it is still better to apply the full patch from the UxPlay + Wiki in this case. + +- **It appears that when hardware h264 video decoding is used, the + option -bt709 became needed again in GStreamer-1.22 and later.** + +- For "double-legacy" Raspberry Pi OS (Buster), there is no patch for + GStreamer-1.14. Instead, first build a complete newer + GStreamer-1.18.6 from source using [these + instructions](https://github.com/FDH2/UxPlay/wiki/Building-latest-GStreamer-from-source-on-distributions-with-older-GStreamer-(e.g.-Raspberry-Pi-OS-).) + before building UxPlay. + +- Raspberry Pi 3 Model B+ running a 32 bit OS can also access the GPU + with the GStreamer OMX plugin (use option "`-vd omxh264dec`"), but + this is broken by Pi 4 Model B firmware. OMX support was removed + from Raspberry Pi OS (Bullseye), but is present in Buster. + +- **H265 (4K)** video is potentially supported by hardware decoding on + Raspberry Pi 5 models, as well as on Raspberry Pi 4 model B, using a + dedicated HEVC decoding block, but the "rpivid" kernel driver for + this is not yet supported by GStreamer (this driver decodes video + into a non-standard format that cannot be supported by GStreamer + until the driver is in the mainline Linux kernel). Raspberry Pi + provides a version of ffmpeg that can use that format, but at + present UxPlay cannot use this. The best solution would be for the + driver to be "upstreamed" to the kernel, allowing GStreamer support. + (Software HEVC decoding works, but does not seem to give + satisfactory results on the Pi). + +Even with GPU video decoding, some frames may be dropped by the +lower-power models to keep audio and video synchronized using +timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config +"Performance Options" allows specifying how much memory to allocate to +the GPU, but this setting appears to be absent in Bookworm (but it can +still be set to e.g. 128MB by adding a line "gpu_mem=128" in +/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well +when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to +the GPU (default seems to be 64MB). + +The basic uxplay options for R Pi are `uxplay [-vs ]`. The +choice `` = `glimagesink` is sometimes useful. With the +Wayland video compositor, use `` = `waylandsink`. With +framebuffer video, use `` = `kmssink`. + +- Tip: to start UxPlay on a remote host (such as a Raspberry Pi) using + ssh: + +```{=html} + +``` + ssh user@remote_host + export DISPLAY=:0 + nohup uxplay [options] > FILE & + +Sound and video will play on the remote host; "nohup" will keep uxplay +running if the ssh session is closed. Terminal output is saved to FILE +(which can be /dev/null to discard it) + +## Building UxPlay on macOS: **(Intel X86_64 and "Apple Silicon" M1/M2 Macs)** + +*Note: A native AirPlay Server feature is included in macOS 12 Monterey, +but is restricted to recent hardware. UxPlay can run on older macOS +systems that will not be able to run Monterey, or can run Monterey but +not AirPlay.* + +These instructions for macOS assume that the Xcode command-line +developer tools are installed (if Xcode is installed, open the Terminal, +type "sudo xcode-select --install" and accept the conditions). + +It is also assumed that CMake \>= 3.13 is installed: this can be done +with package managers [MacPorts](http://www.macports.org) +(`sudo port install cmake`), [Homebrew](http://brew.sh) +(`brew install cmake`), or by a download from +. Also install `git` if you will use it to +fetch UxPlay. + +Next install libplist and openssl-3.x. Note that static versions of +these libraries will be used in the macOS builds, so they can be +uninstalled after building uxplay, if you wish. + +- If you use Homebrew: `brew install libplist openssl@3` + +- if you use MacPorts: `sudo port install libplist-devel openssl3` + +Otherwise, build libplist and openssl from source: see instructions near +the end of this README; requires development tools (autoconf, automake, +libtool, *etc.*) to be installed. + +Next get the latest macOS release of GStreamer-1.0. + +**Using "Official" GStreamer (Recommended for both MacPorts and Homebrew +users)**: install the GStreamer release for macOS from +. (This release contains +its own pkg-config, so you don't have to install one.) Install both the +gstreamer-1.0 and gstreamer-1.0-devel packages. After downloading, +Shift-Click on them to install (they install to +/Library/FrameWorks/GStreamer.framework). Homebrew or MacPorts users +should **not** install (or should uninstall) the GStreamer supplied by +their package manager, if they use the "official" release. + +- Since GStreamer v1.22, the "Official" (gstreamer.freedesktop.org) + macOS binaries require a wrapper "gst_macos_main" around the actual + main program (uxplay). This should have been applied during the + UxPlay compilation process, and the initial UxPlay terminal message + should confirm it is being used. (UxPlay can also be built using + "Official" GStreamer v.1.20.7 binaries, which work without the + wrapper.) + +**Using Homebrew's GStreamer**: pkg-config is needed: ("brew install +pkg-config gstreamer"). This causes a large number of extra packages to +be installed by Homebrew as dependencies. The [Homebrew gstreamer +installation](https://formulae.brew.sh/formula/gstreamer#default) has +recently been reworked into a single "formula" named `gstreamer`, which +now works without needing GST_PLUGIN_PATH to be set in the enviroment. +Homebrew installs gstreamer to `HOMEBREW_PREFIX/lib/gstreamer-1.0` where +by default `HOMEBREW_PREFIX/*` is `/opt/homebrew/*` on Apple Silicon +Macs, and `/usr/local/*` on Intel Macs; do not put any extra +non-Homebrew plugins (that you build yourself) there, and instead set +GST_PLUGIN_PATH to point to their location (Homebrew does not supply a +complete GStreamer, but seems to have everything needed for UxPlay). +**New: the UxPlay build script will now also detect Homebrew +installations in non-standard locations indicated by the environment +variable `$HOMEBREW_PREFIX`.** + +**Using GStreamer installed from MacPorts**: this is **not** +recommended, as currently the MacPorts GStreamer is old (v1.16.2), +unmaintained, and built to use X11: + +- Instead [build gstreamer + yourself](https://github.com/FDH2/UxPlay/wiki/Building-GStreamer-from-Source-on-macOS-with-MacPorts) + if you use MacPorts and do not want to use the "Official" Gstreamer + binaries. + +*(If you really wish to use the MacPorts GStreamer-1.16.2, install +pkgconf ("sudo port install pkgconf"), then "sudo port install +gstreamer1-gst-plugins-base gstreamer1-gst-plugins-good +gstreamer1-gst-plugins-bad gstreamer1-gst-libav". For X11 support on +macOS, compile UxPlay using a special cmake option `-DUSE_X11=ON`, and +run it from an XQuartz terminal with -vs ximagesink; older non-retina +macs require a lower resolution when using X11: `uxplay -s 800x600`.)* + +After installing GStreamer, build and install uxplay: open a terminal +and change into the UxPlay source directory ("UxPlay-master" for zipfile +downloads, "UxPlay" for "git clone" downloads) and build/install with +"cmake . ; make ; sudo make install" (same as for Linux). + +- Running UxPlay while checking for GStreamer warnings (do this with + "export GST_DEBUG=2" before runnng UxPlay) reveals that with the + default (since UxPlay 1.64) use of timestamps for video + synchonization, many video frames are being dropped (only on macOS), + perhaps due to another error (about videometa) that shows up in the + GStreamer warnings. **Recommendation: use the new UxPlay "no + timestamp" option "`-vsync no`"** (you can add a line "vsync no" in + the uxplayrc configuration file). + +- On macOS with this installation of GStreamer, the only videosinks + available seem to be glimagesink (default choice made by + autovideosink) and osxvideosink. The window title does not show the + Airplay server name, but the window is visible to screen-sharing + apps (e.g., Zoom). The only available audiosink seems to be + osxaudiosink. + +- The option -nc is always used, whether or not it is selected. This + is a workaround for a problem with GStreamer videosinks on macOS: if + the GStreamer pipeline is destroyed while the mirror window is still + open, a segfault occurs. + +- In the case of glimagesink, the resolution settings "-s wxh" do not + affect the (small) initial OpenGL mirror window size, but the window + can be expanded using the mouse or trackpad. In contrast, a window + created with "-vs osxvideosink" is initially big, but has the wrong + aspect ratio (stretched image); in this case the aspect ratio + changes when the window width is changed by dragging its side; the + option `-vs "osxvideosink force-aspect-ratio=true"` can be used to + make the window have the correct aspect ratio when it first opens. + +## Building UxPlay on Microsoft Windows, using MSYS2 with the MinGW-64 compiler. + +- tested on Windows 10 and 11, 64-bit. + +1. Download and install **Bonjour SDK for Windows v3.0**. You can + download the SDK without any registration at + [softpedia.com](https://www.softpedia.com/get/Programming/SDK-DDK/Bonjour-SDK.shtml), + or get it from the official Apple site + [https://developer.apple.com/download](https://developer.apple.com/download/all/?q=Bonjour%20SDK%20for%20Windows) + (Apple makes you register as a developer to access it from their + site). This should install the Bonjour SDK as + `C:\Program Files\Bonjour SDK`. + +2. (This is for 64-bit Windows; a build for 32-bit Windows should be + possible, but is not tested.) The unix-like MSYS2 build environment + will be used: download and install MSYS2 from the official site + [https://www.msys2.org/](https://www.msys2.org). Accept the default + installation location `C:\mysys64`. + +3. [MSYS2 packages](https://packages.msys2.org/package/) are installed + with a variant of the "pacman" package manager used by Arch Linux. + Open a "MSYS2 MINGW64" terminal from the MSYS2 tab in the Windows + Start menu, and update the new MSYS2 installation with "pacman + -Syu". Then install the **MinGW-64** compiler and **cmake** + + pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc + + The compiler with all required dependencies will be installed in the + msys64 directory, with default path `C:/msys64/mingw64`. Here we + will simply build UxPlay from the command line in the MSYS2 + environment (this uses "`ninja`" in place of "`make`" for the build + system). + +4. Download the latest UxPlay from github **(to use `git`, install it + with `pacman -S git`, then + "`git clone https://github.com/FDH2/UxPlay`")**, then install UxPlay + dependencies (openssl is already installed with MSYS2): + + `pacman -S mingw-w64-x86_64-libplist mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base` + + If you are trying a different Windows build system, MSVC versions of + GStreamer for Windows are available from the [official GStreamer + site](https://gstreamer.freedesktop.org/download/), but only the + MinGW 64-bit build on MSYS2 has been tested. + +5. cd to the UxPlay source directory, then "`mkdir build`" and + "`cd build`". The build process assumes that the Bonjour SDK is + installed at `C:\Program Files\Bonjour SDK`. If it is somewhere + else, set the enviroment variable BONJOUR_SDK_HOME to point to its + location. Then build UxPlay with + + `cmake ..` + + `ninja` + +6. Assuming no error in either of these, you will have built the uxplay + executable **uxplay.exe** in the current ("build") directory. The + "sudo make install" and "sudo make uninstall" features offered in + the other builds are not available on Windows; instead, the MSYS2 + environment has `/mingw64/...` available, and you can install the + uxplay.exe executable in `C:/msys64/mingw64/bin` (plus manpage and + documentation in `C:/msys64/mingw64/share/...`) with + + `cmake --install . --prefix /mingw64` + + To be able to view the manpage, you need to install the manpage + viewer with "`pacman -S man`". + +To run **uxplay.exe** you need to install some gstreamer plugin packages +with `pacman -S mingw-w64-x86_64-gst-`, where the required ones +have `` given by + +1. **libav** +2. **plugins-good** +3. **plugins-bad** + +Other possible MSYS2 gstreamer plugin packages you might use are listed +in [MSYS2 packages](https://packages.msys2.org/package/). + +You also will need to grant permission to the uxplay executable +uxplay.exe to access data through the Windows firewall. You may +automatically be offered the choice to do this when you first run +uxplay, or you may need to do it using **Windows Settings-\>Update and +Security-\>Windows Security-\>Firewall & network protection -\> allow an +app through firewall**. If your virus protection flags uxplay.exe as +"suspicious" (but without a true malware signature) you may need to give +it an exception. + +Now test by running "`uxplay`" (in a MSYS2 terminal window). If you need +to specify the audiosink, there are two main choices on Windows: the +older DirectSound plugin "`-as directsoundsink`", and the more modern +Windows Audio Session API (wasapi) plugin "`-as wasapisink`", which +supports [additional +options](https://gstreamer.freedesktop.org/documentation/wasapi/wasapisink.html) +such as + + uxplay -as 'wasapisink device=\"\"' + +where `` specifies an available audio device by its GUID, which +can be found using "`gst-device-monitor-1.0 Audio`": `` has a form +like `\{0.0.0.00000000\}.\{98e35b2b-8eba-412e-b840-fd2c2492cf44\}`. If +"`device`" is not specified, the default audio device is used. + +If you wish to specify the videosink using the `-vs ` option, +some choices for `` are `d3d11videosink`, `d3dvideosink`, +`glimagesink`, `gtksink`. + +- With Direct3D 11.0 or greater, you can either always be in + fullscreen mode using option + `-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true"`, + or get the ability to toggle into and out of fullscreen mode using + the Alt-Enter key combination with option + `-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`. For + convenience, these options will be added if just + `-vs d3d11videosink` with or without the fullscreen option "-fs" is + used. *(Windows users may wish to add "`vs d3d11videosink`" (no + initial "`-`") to the UxPlay startup options file; see "man uxplay" + or "uxplay -h".)* + +The executable uxplay.exe can also be run without the MSYS2 environment, +in the Windows Terminal, with `C:\msys64\mingw64\bin\uxplay`. + +# Usage + +Options: + +- These can also be written (one option per line, without the initial + "`-`" character) in the UxPlay startup file (either given by + environment variable `$UXPLAYRC`, or `~/.uxplayrc` or + `~/.config/uxplayrc`); lines begining with "`#`" are treated as + comments, and ignored. Command line options supersede options in the + startup file. + +**-n server_name** (Default: UxPlay); server_name@\_hostname\_ will be +the name that appears offering AirPlay services to your iPad, iPhone +etc, where *hostname* is the name of the server running uxplay. This +will also now be the name shown above the mirror display (X11) window. + +**-nh** Do not append "@_hostname_" at the end of the AirPlay server +name. + +**-h265** Activate "ScreenMultiCodec" support (AirPlay "Features" bit +42) for accepting h265 (4K/HEVC) video in addition to h264 video (1080p) +in screen-mirror mode. When this option is used, two "video pipelines" +(one for h264, one for h265) are created. If any GStreamer plugins in +the pipeline are specific for h264 or h265, the correct version will be +used in each pipeline. A wired Client-Server ethernet connection is +preferred over Wifi for 4K video, and might be required by the client. +Only recent Apple devices (M1/M2 Macs or iPads, and some iPhones) can +send h265 video if a resolution "-s wxh" with h \> 1080 is requested. +The "-h265" option changes the default resolution ("-s" option) from +1920x1080 to 3840x2160, and leaves default maximum framerate ("-fps" +option) at 30fps. + +**-hls** Activate HTTP Live Streaming support. With this option YouTube +videos can be streamed directly from YouTube servers to UxPlay (without +passing through the client) by clicking on the AirPlay icon in the +YouTube app. + +**-pin \[nnnn\]**: (since v1.67) use Apple-style (one-time) "pin" +authentication when a new client connects for the first time: a +four-digit pin code is displayed on the terminal, and the client screen +shows a login prompt for this to be entered. When "-pin" is used by +itself, a new random pin code is chosen for each authentication; if +"-pin nnnn" (e.g., "-pin 3939") is used, this will set an unchanging +fixed code. Authentication adds the server to the client's list of +"trusted servers" and the client will not need to reauthenticate +provided that the client and server public keys remain unchanged. (By +default since v1.68, the server public key is generated from the MAC +address, which can be changed with the -m option; see the -key option +for an alternative method of key generation). *(Add a line "pin" in the +UxPlay startup file if you wish the UxPlay server to use the pin +authentication protocol).* + +**-reg \[*filename*\]**: (since v1.68). If "-pin" is used, this option +maintains a register of pin-authenticated "trusted clients" in +\$HOME/.uxplay.register (or optionally, in *filename*). Without this +option, returning clients that skip pin-authentication are trusted and +not checked. This option may be useful if UxPlay is used in a more +public environment, to record client details; the register is text, one +line per client, with client's public key (base-64 format), Device ID, +and Device name; commenting out (with "\#") or deleting a line +deregisters the corresponding client (see options -restrict, -block, +-allow for more ways to control client access). *(Add a line "reg" in +the startup file if you wish to use this feature.)* + +**-vsync \[x\]** (In Mirror mode:) this option (**now the default**) +uses timestamps to synchronize audio with video on the server, with an +optional audio delay in (decimal) milliseconds (*x* = "20.5" means +0.0205 seconds delay: positive or negative delays less than a second are +allowed.) It is needed on low-power systems such as Raspberry Pi without +hardware video decoding. + +**-vsync no** (In Mirror mode:) this switches off timestamp-based +audio-video synchronization, restoring the default behavior prior to +UxPlay-1.64. Standard desktop systems seem to work well without use of +timestamps: this mode is appropriate for "live streaming" such as using +UxPlay as a second monitor for a mac computer, or monitoring a webcam; +with it, no video frames are dropped. + +**-async \[x\]** (In Audio-Only (ALAC) mode:) this option uses +timestamps to synchronize audio on the server with video on the client, +with an optional audio delay in (decimal) milliseconds (*x* = "20.5" +means 0.0205 seconds delay: positive or negative delays less than a +second are allowed.) Because the client adds a video delay to account +for latency, the server in -async mode adds an equivalent audio delay, +which means that audio changes such as a pause or a track-change will +not take effect immediately. *This might in principle be mitigated by +using the `-al` audio latency setting to change the latency (default +0.25 secs) that the server reports to the client, but at present +changing this does not seem to have any effect*. + +**-async no**. This is the still the default behavior in Audio-only +mode, but this option may be useful as a command-line option to switch +off a `-async` option set in a "uxplayrc" configuration file. + +**-db *low*\[:*high*\]** Rescales the AirPlay volume-control attenuation +(gain) from -30dB:0dB to *low*:0dB or *low*:*high*. The lower limit +*low* must be negative (attenuation); the upper limit *high* can be +either sign. (GStreamer restricts volume-augmentation by *high* so that +it cannot exceed +20dB). The rescaling is "flat", so that for -db +-50:10, a change in Airplay attenuation by -7dB is translated to a -7 x +(60/30) = -14dB attenuation, and the maximum volume (AirPlay 0dB) is a +10dB augmentation, and Airplay -30dB would become -50dB. Note that the +minimum AirPlay value (-30dB exactly) is translated to "mute". + +**-taper** Provides a "tapered" Airplay volume-control profile (matching +the one called "dasl-tapering" in +[shairport-sync](https://github.com/mikebrady/shairport-sync)): each +time the length of the volume slider (or the number of steps above mute, +where 16 steps = full volume) is reduced by 50%, the perceived volume is +halved (a 10dB attenuation). (This is modified at low volumes, to use +the "untapered" volume if it is louder.) + +**-s wxh** e.g. -s 1920x1080 (= "1080p"), the default width and height +resolutions in pixels for h264 video. (The default becomes 3840x2160 (= +"4K") when the -h265 option is used.) This is just a request made to the +AirPlay client, and perhaps will not be the final resolution you get. w +and h are whole numbers with four digits or less. Note that the +**height** pixel size is the controlling one used by the client for +determining the streaming format; the width is dynamically adjusted to +the shape of the image (portrait or landscape format, depending on how +an iPad is held, for example). + +**-s wxh@r** As above, but also informs the AirPlay client about the +screen refresh rate of the display. Default is r=60 (60 Hz); r must be a +whole number less than 256. + +**-o** turns on an "overscanned" option for the display window. This +reduces the image resolution by using some of the pixels requested by +option -s wxh (or their default values 1920x1080) by adding an empty +boundary frame of unused pixels (which would be lost in a full-screen +display that overscans, and is not displayed by gstreamer). +Recommendation: **don't use this option** unless there is some special +reason to use it. + +**-fs** uses fullscreen mode, but only works with X11, Wayland, VAAPI, +and D3D11 (Windows). + +**-p** allows you to select the network ports used by UxPlay (these need +to be opened if the server is behind a firewall). By itself, -p sets +"legacy" ports TCP 7100, 7000, 7001, UDP 6000, 6001, 7011. -p n (e.g. -p +35000) sets TCP and UDP ports n, n+1, n+2. -p n1,n2,n3 (comma-separated +values) sets each port separately; -p n1,n2 sets ports n1,n2,n2+1. -p +tcp n or -p udp n sets just the TCP or UDP ports. Ports must be in the +range \[1024-65535\]. + +If the -p option is not used, the ports are chosen dynamically +(randomly), which will not work if a firewall is running. + +**-avdec** forces use of software h264 decoding using Gstreamer element +avdec_h264 (libav h264 decoder). This option should prevent +autovideosink choosing a hardware-accelerated videosink plugin such as +vaapisink. + +**-vp *parser*** choses the GStreamer pipeline's h264 parser element, +default is h264parse. Using quotes "..." allows options to be added. + +**-vd *decoder*** chooses the GStreamer pipeline's h264 decoder element, +instead of the default value "decodebin" which chooses it for you. +Software decoding is done by avdec_h264; various hardware decoders +include: vaapih264dec, nvdec, nvh264dec, v4l2h264dec (these require that +the appropriate hardware is available). Using quotes "..." allows some +parameters to be included with the decoder name. + +**-vc *converter*** chooses the GStreamer pipeline's videoconverter +element, instead of the default value "videoconvert". When using +Video4Linux2 hardware-decoding by a GPU,`-vc v4l2convert` will also use +the GPU for video conversion. Using quotes "..." allows some parameters +to be included with the converter name. + +**-vs *videosink*** chooses the GStreamer videosink, instead of the +default value "autovideosink" which chooses it for you. Some videosink +choices are: ximagesink, xvimagesink, vaapisink (for intel graphics), +gtksink, glimagesink, waylandsink, osxvideosink (for macOS), kmssink +(for systems without X11, like Raspberry Pi OS lite) or fpsdisplaysink +(which shows the streaming framerate in fps). Using quotes "..." allows +some parameters to be included with the videosink name. For example, +**fullscreen** mode is supported by the vaapisink plugin, and is +obtained using `-vs "vaapisink fullscreen=true"`; this also works with +`waylandsink`. The syntax of such options is specific to a given plugin +(see GStreamer documentation), and some choices of videosink might not +work on your system. + +**-vs 0** suppresses display of streamed video. In mirror mode, the +client's screen is still mirrored at a reduced rate of 1 frame per +second, but is not rendered or displayed. This option should always be +used if the server is "headless" (with no attached screen to display +video), and only used to render audio, which will be AAC +lossily-compressed audio in mirror mode with unrendered video, and +superior-quality ALAC Apple Lossless audio in Airplay audio-only mode. + +**-v4l2** Video settings for hardware h264 video decoding in the GPU by +Video4Linux2. Equivalent to `-vd v4l2h264dec -vc v4l2convert`. + +**-bt709** A workaround for the failure of the older Video4Linux2 plugin +to recognize Apple's use of an uncommon (but permitted) "full-range +color" variant of the bt709 color standard for digital TV. This is no +longer needed by GStreamer-1.20.4 and backports from it. + +**-rpi** Equivalent to "-v4l2" (Not valid for Raspberry Pi model 5, and +removed in UxPlay 1.67) + +**-rpigl** Equivalent to "-rpi -vs glimagesink". (Removed since UxPlay +1.67) + +**-rpifb** Equivalent to "-rpi -vs kmssink" (Removed since UxPlay 1.67) + +**-rpiwl** Equivalent to "-rpi -vs waylandsink". (Removed since UxPlay +1.67) + +**-as *audiosink*** chooses the GStreamer audiosink, instead of letting +autoaudiosink pick it for you. Some audiosink choices are: pulsesink, +alsasink, pipewiresink, osssink, oss4sink, jackaudiosink, osxaudiosink +(for macOS), wasapisink, directsoundsink (for Windows). Using quotes +"..." might allow some optional parameters +(e.g. `-as "alsasink device=..."` to specify a non-default output +device). The syntax of such options is specific to a given plugin (see +GStreamer documentation), and some choices of audiosink might not work +on your system. + +**-as 0** (or just **-a**) suppresses playing of streamed audio, but +displays streamed video. + +**-al *x*** specifies an audio latency *x* in (decimal) seconds in +Audio-only (ALAC), that is reported to the client. Values in the range +\[0.0, 10.0\] seconds are allowed, and will be converted to a whole +number of microseconds. Default is 0.25 sec (250000 usec). *(However, +the client appears to ignore this reported latency, so this option seems +non-functional.)* + +**-ca *filename*** provides a file (where *filename* can include a full +path) used for output of "cover art" (from Apple Music, *etc.*,) in +audio-only ALAC mode. This file is overwritten with the latest cover art +as it arrives. Cover art (jpeg format) is discarded if this option is +not used. Use with a image viewer that reloads the image if it changes, +or regularly (*e.g.* once per second.). To achieve this, run +"`uxplay -ca [path/to/]filename &`" in the background, then run the the +image viewer in the foreground. Example, using `feh` as the viewer: run +"`feh -R 1 [path/to/]filename`" (in the same terminal window in which +uxplay was put into the background). To quit, use `ctrl-C fg ctrl-C` to +terminate the image viewer, bring `uxplay` into the foreground, and +terminate it too. + +**-reset n** sets a limit of *n* consecutive timeout failures of the +client to respond to ntp requests from the server (these are sent every +3 seconds to check if the client is still present, and synchronize with +it). After *n* failures, the client will be presumed to be offline, and +the connection will be reset to allow a new connection. The default +value of *n* is 5; the value *n* = 0 means "no limit" on timeouts. + +**-nofreeze** closes the video window after a reset due to ntp timeout +(default is to leave window open to allow a smoother reconection to the +same client). This option may be useful in fullscreen mode. + +**-nc** maintains previous UxPlay \< 1.45 behavior that does **not +close** the video window when the the client sends the "Stop Mirroring" +signal. *This option is currently used by default in macOS, as the +window created in macOS by GStreamer does not terminate correctly (it +causes a segfault) if it is still open when the GStreamer pipeline is +closed.* + +**-nohold** Drops the current connection when a new client attempts to +connect. Without this option, the current client maintains exclusive +ownership of UxPlay until it disconnects. + +**-restrict** Restrict clients allowed to connect to those specified by +`-allow `. The deviceID has the form of a MAC address which is +displayed by UxPlay when the client attempts to connect, and appears to +be immutable. It has the format `XX:XX:XX:XX:XX:XX`, X = 0-9,A-F, and is +possibly the "true" hardware MAC address of the device. Note that iOS +clients generally expose different random "private Wi_Fi addresses" +("fake" MAC addresses) to different networks (for privacy reasons, to +prevent tracking), which may change, and do not correpond to the +deviceID. + +**-restrict no** Remove restrictions (default). This is useful as a +command-line argument to overide restrictions set in the Startup file. + +**-allow *id*** Adds the deviceID = *id* to the list of allowed clients +when client restrictions are being enforced. Usually this will be an +entry in the uxplayrc startup file. + +**-block *id*** Always block clients with deviceID = *id*, even when +client restrictions are not being enforced generally. Usually this will +be an entry in the uxplayrc startup file. + +**-FPSdata** Turns on monitoring of regular reports about video +streaming performance that are sent by the client. These will be +displayed in the terminal window if this option is used. The data is +updated by the client at 1 second intervals. + +**-fps n** sets a maximum frame rate (in frames per second) for the +AirPlay client to stream video; n must be a whole number less than 256. +(The client may choose to serve video at any frame rate lower than this; +default is 30 fps.) A setting of 60 fps may give you improved video but +is not recommended on Raspberry Pi. A setting below 30 fps might be +useful to reduce latency if you are running more than one instance of +uxplay at the same time. *This setting is only an advisory to the client +device, so setting a high value will not force a high framerate.* (You +can test using "-vs fpsdisplaysink" to see what framerate is being +received, or use the option -FPSdata which displays video-stream +performance data continuously sent by the client during +video-streaming.) + +**-f {H\|V\|I}** implements "videoflip" image transforms: H = horizontal +flip (right-left flip, or mirror image); V = vertical flip ; I = 180 +degree rotation or inversion (which is the combination of H with V). + +**-r {R\|L}** 90 degree Right (clockwise) or Left (counter-clockwise) +rotations; these image transforms are carried out after any **-f** +transforms. + +**-m \[mac\]** changes the MAC address (Device ID) used by UxPlay +(default is to use the true hardware MAC address reported by the host +computer's network card). (Different server_name, MAC addresses, and +network ports are needed for each running uxplay if you attempt to run +more than one instance of uxplay on the same computer.) If \[mac\] (in +form xx:xx:xx:xx:xx:xx, 6 hex octets) is not given, a random MAC address +is generated. If UxPlay fails to find the true MAC address of a network +card, (more specifically, the MAC address used by the first active +network interface detected) a random MAC address will be used even if +option **-m** was not specified. (Note that a random MAC address will be +different each time UxPlay is started). + +**-key \[*filename*\]**: This (more secure) option for generating and +storing a persistant public key (needed for the -pin option) has been +replaced by default with a (less secure) method which generates a key +from the server's "device ID" (MAC address, which can be changed with +the -m option, conveniently as a startup file option). When the -key +option is used, a securely generated keypair is generated and stored in +`$HOME/.uxplay.pem`, if that file does not exist, or read from it, if it +exists. (Optionally, the key can be stored in *filename*.) This method +is more secure than the new default method, (because the Device ID is +broadcast in the DNS_SD announcement) but still leaves the private key +exposed to anyone who can access the pem file. This option should be set +in the UxPlay startup file as a line "key" or "key *filename*" (no +initial "-"), where *filename* is a full path which should be enclosed +in quotes (`"...."`) if it contains any blank spaces. **Because the +default method is simpler, and security of client access to uxplay is +unlikely to be an important issue, the -key option is no longer +recommended**. + +**-dacp \[*filename*\]**: Export current client DACP-ID and +Active-Remote key to file: default is \$HOME/.uxplay.dacp. (optionally +can be changed to *filename*). Can be used by remote control +applications. File is transient: only exists while client is connected. + +**-vdmp** Dumps h264 video to file videodump.h264. -vdmp n dumps not +more than n NAL units to videodump.x.h264; x= 1,2,... increases each +time a SPS/PPS NAL unit arrives. To change the name *videodump*, use +-vdmp \[n\] *filename*. + +**-admp** Dumps audio to file audiodump.x.aac (AAC-ELD format audio), +audiodump.x.alac (ALAC format audio) or audiodump.x.aud (other-format +audio), where x = 1,2,3... increases each time the audio format changes. +-admp *n* restricts the number of packets dumped to a file to *n* or +less. To change the name *audiodump*, use -admp \[n\] *filename*. *Note +that (unlike dumped video) the dumped audio is currently only useful for +debugging, as it is not containerized to make it playable with standard +audio players.* + +**-d** Enable debug output. Note: this does not show GStreamer error or +debug messages. To see GStreamer error and warning messages, set the +environment variable GST_DEBUG with "export GST_DEBUG=2" before running +uxplay. To see GStreamer information messages, set GST_DEBUG=4; for +DEBUG messages, GST_DEBUG=5; increase this to see even more of the +GStreamer inner workings. + +# Troubleshooting + +Note: `uxplay` is run from a terminal command line, and informational +messages are written to the terminal. + +### 0. Problems in compiling UxPlay. + +One user (on Ubuntu) found compilation failed with messages about +linking to "usr/local/lib/libcrypto.a" and "zlib". This was because (in +addition to the standard ubuntu installation of libssl-dev), the user +was unaware that a second installation with libcrypto in /usr/local was +present. Solution: when more than one installation of OpenSSL is +present, set the environment variable OPEN_SSL_ROOT_DIR to point to the +correct one; on 64-bit Ubuntu, this is done by running +`export OPENSSL_ROOT_DIR=/usr/lib/X86_64-linux-gnu/` before running +cmake. + +### 1. **Avahi/DNS_SD Bonjour/Zeroconf issues** + +The DNS_SD Service-Discovery ("Bonjour" or "Zeroconf") service is +required for UxPlay to work. On Linux, it will be usually provided by +Avahi, and to troubleshoot this, you should use the tool `avahi-browse`. +(You may need to install a separate package with a name like +`avahi-utils` to get this.) + +On Linux, make sure Avahi is installed, and start the avahi-daemon +service on the system running uxplay (your distribution will document +how to do this, for example: `sudo systemctl avahi-daemon` or +`sudo service avahi-daemon `, with `` one of enable, disable, +start, stop, status. You might need to edit the avahi-daemon.conf file +(it is typically in /etc/avahi/, find it with +"`sudo find /etc -name avahi-daemon.conf`"): make sure that +"disable-publishing" is **not** a selected option). Some systems may +instead use the mdnsd daemon as an alternative to provide DNS-SD +service. (FreeBSD offers both alternatives, but only Avahi was tested; +see [here](https://gist.github.com/reidransom/6033227).) + +- **uxplay starts, but either stalls or stops after "Initialized + server socket(s)" appears (*without the server name showing on the + client*)**. + +If UxPlay stops with the "No DNS-SD Server found" message, this means +that your network **does not have a running Bonjour/zeroconf DNS-SD +server.** Before v1.60, UxPlay used to stall silently if DNS-SD service +registration failed, but now stops with an error message returned by the +DNSServiceRegister function: kDNSServiceErr_Unknown if no DNS-SD server +was found: *(A NixOS user found that in NixOS, this error can also occur +if avahi-daemon service IS running with publishing enabled, but reports +"the error disappeared on NixOS by setting services.avahi.openFirewall +to true".)* Other mDNS error codes are in the range FFFE FF00 (-65792) +to FFFE FFFF (-65537), and are listed in the dnssd.h file. An older +version of this (the one used by avahi) is found +[here](https://github.com/lathiat/avahi/blob/master/avahi-compat-libdns_sd/dns_sd.h). +A few additional error codes are defined in a later version from +[Apple](https://opensource.apple.com/source/mDNSResponder/mDNSResponder-544/mDNSShared/dns_sd.h.auto.html). + +If UxPlay stalls *without an error message* and *without the server name +showing on the client*, **this is a network problem** (if your UxPlay +version is older than 1.60, it is also the behavior when no DNS-SD +server is found.) + +A useful tool for examining such network problems from the client end is +the (free) Discovery DNS-SD browser [available in the Apple App +Store](https://apps.apple.com/us/developer/lily-ballard/id305441020) for +both iOS (works on iPadOS too) and macOS. + +- Some users using dual-band (2.4GHz/5GHz) routers have reported that + clients using the 5GHz band (sometimes) "fail to see UxPlay" (i.e., + do not get a response to their mDNS queries), but the 2.4GHz band + works. Other projects using Bonjour/mDNS have had similar reports; + the issue seems to be router-specific, perhaps related to "auto" + rather than fixed channel selection (5GHz has many more channels to + switch between), or channel width selections; one speculation is + that since mDNS uses UDP protocol (where "lost" messages are not + resent), a mDNS query might get lost if channel switching occurs + during the query. + +If your router has this problem, a reported "fix" is to (at least on +5GHz) use fixed channel and/or fixed (not dynamic) channel width. + +- **Avahi works at first, but new clients do not see UxPlay, or + clients that initially saw it stop doing so after they disconnect**. + +This is usually because Avahi is only using the "loopback" network +interface, and is not receiving mDNS queries from new clients that were +not listening when UxPlay started. + +To check this, after starting uxplay, use the utility +`avahi-browse -a -t` **in a different terminal window** on the server to +verify that the UxPlay AirTunes and AirPlay services are correctly +registered (only the AirTunes service is used in the "Legacy" AirPlay +Mirror mode used by UxPlay, but the AirPlay service is used for the +initial contact). + +The results returned by avahi-browse should show entries for uxplay like + + + eno1 IPv6 UxPlay AirPlay Remote Video local + + eno1 IPv4 UxPlay AirPlay Remote Video local + + lo IPv4 UxPlay AirPlay Remote Video local + + eno1 IPv6 863EA27598FE@UxPlay AirTunes Remote Audio local + + eno1 IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local + + lo IPv4 863EA27598FE@UxPlay AirTunes Remote Audio local + +If only the loopback ("lo") entries are shown, a firewall on the UxPlay +host is probably blocking full DNS-SD service, and you need to open the +default UDP port 5353 for mDNS requests, as loopback-based DNS-SD +service is unreliable. + +If the UxPlay services are listed by avahi-browse as above, but are not +seen by the client, the problem is likely to be a problem with the local +network. + +### 2. uxplay starts, but stalls after "Initialized server socket(s)" appears, *with the server name showing on the client* (but the client fails to connect when the UxPlay server is selected). + +This shows that a *DNS-SD* service is working, clients hear UxPlay is +available, but the UxPlay server is not receiving the response from the +client. This is usually because a firewall on the server is blocking the +connection request from the client. (One user who insisted that the +firewall had been turned off turned out to have had *two* active +firewalls (*firewalld* and *ufw*) *both* running on the server!) If +possible, either turn off the firewall to see if that is the problem, or +get three consecutive network ports, starting at port n, all three in +the range 1024-65535, opened for both tcp and udp, and use "uxplay -p n" +(or open UDP 7011,6001,6000 TCP 7100,7000,7001 and use "uxplay -p"). + +If you are *really* sure there is no firewall, you may need to +investigate your network transmissions with a tool like netstat, but +almost always this is a firewall issue. + +### 3. Problems *after* the client-server connection has been made: + +If you do *not* see the message `raop_rtp_mirror starting mirroring`, +something went wrong before the client-server negotiations were +finished. For such problems, use "uxplay -d" (debug log option) to see +what is happening: it will show how far the connection process gets +before the failure occurs. You can compare your debug output to that +from a successful start of UxPlay in the [UxPlay +Wiki](https://github.com/FDH2/UxPlay/wiki). + +**If UxPlay reports that mirroring started, but you get no video or +audio, the problem is probably from a GStreamer plugin that doesn't work +on your system** (by default, GStreamer uses the "autovideosink" and +"autoaudiosink" algorithms to guess what are the "best" plugins to use +on your system). A different reason for no audio occurred when a user +with a firewall only opened two udp network ports: **three** are +required (the third one receives the audio data). + +**Raspberry Pi** devices (*Pi 4B+ and earlier: this does not apply to +the Pi 5, which does not provide hardware h264 decoding, and does not +need it*) work best with hardware GPU h264 video decoding if the +Video4Linux2 plugin in GStreamer v1.20.x or earlier has been patched +(see the UxPlay +[Wiki](https://github.com/FDH2/UxPlay/wiki/Gstreamer-Video4Linux2-plugin-patches) +for patches). This is fixed in GStreamer-1.22, and by backport patches +from this in distributions such as Raspberry Pi OS (Bullseye): **use +option `-bt709` with the GStreamer-1.18.4 from Raspberry Pi OS**. This +also needs the bcm2835-codec kernel module that is not in the standard +Linux kernel (it is available in Raspberry Pi OS, Ubuntu and Manjaro). + +- **If this kernel module is not available in your Raspberry Pi + operating system, or if GStreamer \< 1.22 is not patched, use option + `-avdec` for software h264-decoding.** + +Sometimes "autovideosink" may select the OpenGL renderer "glimagesink" +which may not work correctly on your system. Try the options "-vs +ximagesink" or "-vs xvimagesink" to see if using one of these fixes the +problem. + +Other reported problems are connected to the GStreamer VAAPI plugin (for +hardware-accelerated Intel graphics, but not NVIDIA graphics). Use the +option "-avdec" to force software h264 video decoding: this should +prevent autovideosink from selecting the vaapisink videosink. +Alternatively, find out if the gstreamer1.0-vaapi plugin is installed, +and if so, uninstall it. (If this does not fix the problem, you can +reinstall it.) + +There are some reports of other GStreamer problems with +hardware-accelerated Intel HD graphics. One user (on Debian) solved this +with "sudo apt install intel-media-va-driver-non-free". This is a driver +for 8'th (or later) generation "\*-lake" Intel chips, that seems to be +related to VAAPI accelerated graphics. + +If you *do* have Intel HD graphics, and have installed the vaapi plugin, +but `-vs vaapisink` does not work, check that vaapi is not "blacklisted" +in your GStreamer installation: run `gst-inspect-1.0 vaapi`, if this +reports `0 features`, you need to `export GST_VAAPI_ALL_DRIVERS=1` +before running uxplay, or set this in the default environment. + +You can try to fix audio or video problems by using the +"`-as `" or "`-vs `" options to choose the +GStreamer audiosink or videosink , rather than letting GStreamer choose +one for you. (See above, in [Starting and running +UxPlay](#starting-and-running-uxplay) for choices of `` or +``.) + +The "OpenGL renderer" window created on Linux by "-vs glimagesink" +sometimes does not close properly when its "close" button is clicked. +(this is a GStreamer issue). You may need to terminate uxplay with +Ctrl-C to close a "zombie" OpenGl window. If similar problems happen +when the client sends the "Stop Mirroring" signal, try the no-close +option "-nc" that leaves the video window open. + +### 4. GStreamer issues (missing plugins, etc.): + +- clearing the user's GStreamer cache with + `rm -rf ~/.cache/gstreamer-1.0/*` may be the solution to problems + where gst-inspect-1.0 does not show a plugin that you believe is + installed. The cache will be regenerated next time GStreamer is + started. **This is the solution to puzzling problems that turn out + to come from corruption of the cache, and should be tried first.** + +If UxPlay fails to start, with a message that a required GStreamer +plugin (such as "libav") was not found, first check with the GStreamer +tool gst-inspect-1.0 to see what GStreamer knows is available. (You may +need to install some additional GStreamer "tools" package to get +gst-inspect-1.0). For, *e.g.* a libav problem, check with +"`gst-inspect-1.0 libav`". If it is not shown as available to GStreamer, +but your package manager shows the relevant package as installed (as one +user found), try entirely removing and reinstalling the package. That +user found that a solution to a "**Required gstreamer plugin 'libav' not +found**" message that kept recurring was to clear the user's gstreamer +cache. + +If it fails to start with an error like '`no element "avdec_aac"`' this +is because even though gstreamer-libav is installed. it is incomplete +because some plugin features are missing: +"`gst-inspect-1.0 | grep avdec_aac`" will show if avdec_aac is +available. Unlike other GStreamer plugins, the libav plugin is a front +end to FFmpeg codecs which provide avdec\_\*. + +- Some distributions (RedHat, SUSE, etc) provide incomplete versions + of FFmpeg because of patent issues with codecs used by certain + plugins. In those cases there will be some "extra package" provider + like [RPM fusion](https://rpmfusion.org) (RedHat), + [packman](http://packman.links2linux.org/) (SUSE) where you can get + complete packages (your distribution will usually provide + instructions for this, Mageia puts them in an optional "tainted" + repo). The packages needed may be "ffmpeg\*" or "libav\*" packages: + the GStreamer libav plugin package does not contain any codecs + itself, it just provides a way for GStreamer to use ffmpeg/libav + codec libraries which must be installed separately. For similar + reasons, distributions may ship incomplete packages of GStreamer + "plugins-bad". Use user on Fedora thought they had installed from + rpmfusion, but the system had not obeyed: *"Adding --allowerasing to + the dnf command fixed it after a restart"*. + +- starting with release UxPlay-1.65.3, UxPlay will continue to + function, but without audio in mirror mode, if avdec_aac is missing. + +To troubleshoot GStreamer execute "export GST_DEBUG=2" to set the +GStreamer debug-level environment-variable in the terminal where you +will run uxplay, so that you see warning and error messages; see +[GStreamer debugging +tools](https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html) +for how to see much more of what is happening inside GStreamer. Run +"gst-inspect-1.0" to see which GStreamer plugins are installed on your +system. + +Some extra GStreamer packages for special plugins may need to be +installed (or reinstalled: a user using a Wayland display system as an +alternative to X11 reported that after reinstalling Lubuntu 18.4, UxPlay +would not work until gstreamer1.0-x was installed, presumably for +Wayland's X11-compatibility mode). Different distributions may break up +GStreamer 1.x into packages in different ways; the packages listed above +in the build instructions should bring in other required GStreamer +packages as dependencies, but will not install all possible plugins. + +The GStreamer video pipeline, which is shown in the initial output from +`uxplay -d`, has the default form + + appsrc name=video_source ! queue ! h264parse ! decodebin ! videoconvert ! autovideosink name=video_sink sync=false + +The pipeline is fully configurable: default elements "h264parse", +"decodebin", "videoconvert", and "autovideosink" can respectively be +replaced by using uxplay options `-vp`, `-vd`, `-vc`, and `-vs`, if +there is any need to modify it (entries can be given in quotes "..." to +include options). + +### 5. Mirror screen freezes (a network problem): + +This can happen if the TCP video stream from the client stops arriving +at the server, probably because of network problems (the UDP audio +stream may continue to arrive). At 3-second intervals, UxPlay checks +that the client is still connected by sending it a request for a NTP +time signal. If a reply is not received from the client within a 0.3 sec +time-window, an "ntp timeout" is registered. If a certain number +(currently 5) of consecutive ntp timeouts occur, UxPlay assumes that the +client is "dead", and resets the connection, becoming available for +connection to a new client, or reconnection to the previous one. +Sometimes the connection may recover before the timeout limit is +reached, and if the default limit is not right for your network, it can +be modified using the option "-reset *n*", where *n* is the desired +timeout-limit value (*n* = 0 means "no limit"). If the connection starts +to recover after ntp timeouts, a corrupt video packet from before the +timeout may trigger a "connection reset by peer" error, which also +causes UxPlay to reset the connection. + +- When the connection is reset, the "frozen" mirror screen of the + previous connection is left in place, but does **not** block new + connections, and will be taken over by a new client connection when + it is made. + +### 6. Protocol issues (with decryption of the encrypted audio and video streams sent by the client). + +A protocol failure may trigger an unending stream of error messages, and +means that the audio decryption key (also used in video decryption) was +not correctly extracted from data sent by the client. + +The protocol was modifed in UxPlay-1.65 after it was discovered that the +client-server "pairing" step could be avoided (leading to a much quicker +connection setup, without a 5 second delay) by disabling "Supports +Legacy Pairing" (bit 27) in the "features" code UxPlay advertises on +DNS-SD Service Discovery. Most clients will then not attempt the setup +of a "shared secret key" when pairing, which is used by AppleTV for +simultaneous handling of multiple clients (UxPlay only supports one +client at a time). **This change is now well-tested, but in case it +causes any protocol failures, UxPlay can be reverted to the previous +behavior by uncommenting the previous "FEATURES_1" setting (and +commenting out the new one) in lib/dnssdint.h, and then rebuilding +UxPlay.** ("Pairing" is re-enabled when the new Apple-style one-time +"pin" authentication is activated by running UxPlay with the "-pin" +option introduced in UxPlay 1.67.) + +Protocol failure should not happen for iOS 9.3 or later clients. +However, if a client uses the same older version of the protocol that is +used by the Windows-based AirPlay client emulator *AirMyPC*, the +protocol can be switched to the older version by the setting +`OLD_PROTOCOL_CLIENT_USER_AGENT_LIST` in `UxPlay/lib/global.h`. UxPlay +reports the client's "User Agent" string when it connects. If some other +client also fails to decrypt all audio and video, try adding its "User +Agent" string in place of "xxx" in the entry "AirMyPC/2.0;xxx" in +global.h and rebuild uxplay. + +Note that for DNS-SD Service Discovery, Uxplay declares itself to be an +AppleTV3,2 (a 32 bit device) with a sourceVersion 220.68; this can also +be changed in global.h. UxPlay also works if it declares itself as an +AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen, +introduced 2017, running tvOS 12.2.1), so it does not seem to matter +what version UxPlay claims to be. + +# Changelog + +1.71 2024-12-13 Add support for HTTP Live Streaming (HLS), initially +only for YouTube movies. Fix issue with NTP timeout on Windows. + +1.70 2024-10-04 Add support for 4K (h265) video (resolution 3840 x +2160). Fix issue with GStreamer \>= 1.24 when client sleeps, then wakes. + +1.69 2024-08-09 Internal improvements (e.g. in -nohold option, +identifying GStreamer videosink selected by autovideosink, finding X11 +display) in anticipation of future HLS video support. New -nofreeze +option to not leave frozen video in place when a network connection is +reset. Fixes for GStreamer-1.24.x changes. + +1.68 2023-12-31 New simpler (default) method for generating a persistent +public key from the server MAC address (which can now be set with the -m +option). (The previous method is still available with -key option). New +option -reg to maintain a register of pin-authenticated clients. +Corrected volume-control: now interprets AirPlay volume range -30dB:0dB +as decibel gain attenuation, with new option -db low\[:high\] for "flat" +rescaling of the dB range. Add -taper option for a "tapered" AirPlay +volume-control profile. + +1.67 2023-11-30 Add support for Apple-style one-time pin authentication +of clients with option "-pin": (uses SRP6a authentication protocol and +public key persistence). Detection with error message of (currently) +unsupported H265 video when requesting high resolution over wired +ethernet. Removed rpi\* options (which are not valid with new Raspberry +Pi model 5, and can be replaced by combinations of other options). Added +optional argument "mac" to "-m" option, to specify a replacement MAC +address/Device ID. Update llhttp to v. 9.1.3. Add -dacp option for +exporting current client DACP info (for remotes). + +1.66 2023-09-05 Fix IPV6 support. Add option to restrict clients to +those on a list of allowed deviceIDs, or to block connections from +clients on a list of blocked deviceIDs. Fix for #207 from @thiccaxe +(screen lag in vsync mode after client wakes from sleep). + +1.65.3 2023-07-23 Add RPM spec file; add warning if required gstreamer +libav feature "avdec_aac" is missing: (this occurs in RPM-based +distributions that ship an incomplete FFmpeg for Patent or License +reasons, and rely on users installing an externally-supplied complete +FFmpeg). Mirror-mode airplay will now work without audio if avdec_aac is +missing. + +1.65 2023-06-03 Eliminate pair_setup part of connection protocol to +allow faster connections with clients (thanks to @shuax #176 for this +discovery); to revert, uncomment a line in lib/dnssdint.h. Disconnect +from audio device when connection closes, to not block its use by other +apps if uxplay is running but not connected. Fix for AirMyPC client +(broken since 1.60), so its older non-NTP timestamp protocol works with +-vsync. Corrected parsing of configuration file entries that were in +quotes. + +1.64 2023-04-23 Timestamp-based synchronization of audio and video is +now the default in Mirror mode. (Use "-vsync no" to restore previous +behavior.) A configuration file can now be used for startup options. +Also some internal cleanups and a minor bugfix that fixes #192. + +1.63 2023-02-12 Reworked audio-video synchronization, with new options +-vsync (for Mirror mode) and -async (for Audio-Only mode, to sync with +client video). Option -vsync makes software h264 decoding of streamed +videos with option -avdec viable on some recent Raspberry Pi models. +Internal change: all times are now processed in nanoseconds units. +Removed -ao option introduced in 1.62. + +1.62 2023-01-18 Added Audio-only mode time offset -ao x to allow user +synchronization of ALAC audio playing on the server with video, song +lyrics, etc. playing on the client. x = 5.0 appears to be optimal in +many cases. Quality fixes: cleanup in volume changes, timestamps, some +bugfixes. + +1.61 2022-12-30 Removed -t option (workaround for an Avahi issue, +correctly solved by opening network port UDP 5353 in firewall). Remove +-g debug flag from CMAKE_CFLAGS. Postpend (instead of prepend) build +environment CFLAGS to CMAKE_CFLAGS. Refactor parts of uxplay.cpp + +1.60 2022-12-15 Added exit with error message if DNSServiceRegister +fails (instead of just stalling). Test for Client's attempt to using +unsupported AirPlay 2 "REMOTE CONTROL" protocol (with no timing +channel), and exit if this occurs. Reworked metadata processing to +correctly parse DMAP header (previous version worked with DMAP messages +currently received, but was not correct). + +1.59 2022-12-12 remove "ZOOMFIX" compile option and make compilation +with X11-dependence the default if X11 development libraries are +detected (this now also provides fullscreen mode with a F11 or Alt+Enter +key toggle); ZOOMFIX is now automatically applied for GStreamer \< 1.20. +New cmake option -DNO_X11_DEPS compiles uxplay without X11 dependence. +Reworked internal metadata handling. Fix segfault with "-vs 0". + +1.58 2022-10-29 Add option "-nohold" that will drop existing connections +when a new client connects. Update llhttp to v8.1.0. + +1.57 2022-10-09 Minor fixes: (fix coredump on AUR on "stop mirroring", +occurs when compiled with AUR CFLAGS -DFORTIFY_SOURCE); graceful exit +when required plugins are missing; improved support for builds on +Windows. Include audioresample in GStreamer audio pipeline. + +1.56 2022-09-01 Added support for building and running UxPlay-1.56 on +Windows (no changes to Unix (Linux, \*BSD, macOS) codebase.) + +1.56 2022-07-30 Remove -bt709 from -rpi, -rpiwl, -rpifb as GStreamer is +now fixed. + +1.55 2022-07-04 Remove the bt709 fix from -v4l2 and create a new -bt709 +option (previous "-v4l2" is now "-v4l2 -bt709"). This allows the +currently-required -bt709 option to be used on its own on RPi without +-v4l2 (sometimes this give better results). + +1.54 2022-06-25 Add support for "Cover Art" display in Audio-only (ALAC) +mode. Reverted a change that caused VAAPI to crash with AMD POLARIS +graphics cards. Minor internal changes to plist code and uxplay option +parsing. + +1.53 2022-06-13 Internal changes to audio sync code, revised +documentation, Minor bugfix (fix assertion crash when resent audio +packets are empty). + +1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted +streaming debug output (readable aligned timestamps with decimal points +in seconds). Eliminate memory leaks (found by valgrind). Support for +display of ALAC (audio-only) metadata (soundtrack artist names, titles +etc.) in the uxplay terminal. + +1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option +-v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, +-v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from +1.48 that broke reconnection after "Stop Mirroring" is sent by client. + +1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI +plugins only), Changed -rpi to be for framebuffer ("lite") RPi systems +and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop +systems. Also modified timestamps from "DTS" to "PTS" for latency +improvement, plus internal cleanups. + +1.49 2022-03-28 Addded options for dumping video and/or audio to file, +for debugging, etc. h264 PPS/SPS NALU's are shown with -d. Fixed +video-not-working for M1 Mac clients. + +1.48 2022-03-11 Made the GStreamer video pipeline fully configurable, +for use with hardware h264 decoding. Support for Raspberry Pi. + +1.47 2022-02-05 Added -FPSdata option to display (in the terminal) +regular reports sent by the client about video streaming performance. +Internal cleanups of processing of video packets received from the +client. Added -reset n option to reset the connection after n ntp +timeouts (also reset after "connection reset by peer" error in video +stream). + +1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken hardware +acceleration): once again use decodebin in the video pipeline; introduce +new option "-avdec" to force software h264 decoding by libav h264, if +needed (to prevent selection of vaapisink by autovideosink). Update +llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2. Restrict +connections to one client at a time (second client must now wait for +first client to disconnect). + +1.45 2022-01-10 New behavior: close video window when client requests +"stop mirroring". (A new "no close" option "-nc" is added for users who +wish to retain previous behavior that does not close the video window). + +1.44 2021-12-13 Omit hash of aeskey with ecdh_secret for an AirMyPC +client; make an internal rearrangement of where this hash is done. Fully +report all initial communications between client and server in -d debug +mode. Replace decodebin in GStreamer video pipeline by h264-specific +elements. + +1.43 2021-12-07 Various internal changes, such as tests for successful +decryption, uniform treatment of informational/debug messages, etc., +updated README. + +1.42 2021-11-20 Fix MAC detection to work with modern Linux interface +naming practices, MacOS and \*BSD. + +1.41 2021-11-11 Further cleanups of multiple audio format support +(internal changes, separated RAOP and GStreamer audio/video startup) + +1.40 2021-11-09 Cleanup segfault in ALAC support, manpage location fix, +show request Plists in debug mode. + +1.39 2021-11-06 Added support for Apple Lossless (ALAC) audio streams. + +1.38 2021-10-8 Add -as *audiosink* option to allow user to choose the +GStreamer audiosink. + +1.37 2021-09-29 Append "@hostname" to AirPlay Server name, where +"hostname" is the name of the server running uxplay (reworked change in +1.36). + +1.36 2021-09-29 Implemented suggestion (by @mrbesen and @PetrusZ) to use +hostname of machine runing uxplay as the default server name + +1.35.1 2021-09-28 Added the -vs 0 option for streaming audio, but not +displaying video. + +1.35 2021-09-10 now uses a GLib MainLoop, and builds on macOS (tested on +Intel Mac, 10.15 ). New option -t *timeout* for relaunching server if no +connections were active in previous *timeout* seconds (to renew Bonjour +registration). + +1.341 2021-09-04 fixed: render logger was not being destroyed by +stop_server() + +1.34 2021-08-27 Fixed "ZOOMFIX": the X11 window name fix was only being +made the first time the GStreamer window was created by uxplay, and not +if the server was relaunched after the GStreamer window was closed, with +uxplay still running. Corrected in v. 1.34 + +### Building OpenSSL \>= 1.1.1 from source. + +If you need to do this, note that you may be able to use a newer version +(OpenSSL-3.0.1 is known to work). You will need the standard development +toolset (autoconf, automake, libtool). Download the source code from +. Install the downloaded openssl by +opening a terminal in your Downloads directory, and unpacking the source +distribution: ("tar -xvzf openssl-3.0.1.tar.gz ; cd openssl-3.0.1"). +Then build/install with "./config ; make ; sudo make install_dev". This +will typically install the needed library `libcrypto.*`, either in +/usr/local/lib or /usr/local/lib64. + +*(Ignore the following for builds on MacOS:)* On some systems like +Debian or Ubuntu, you may also need to add a missing entry +`/usr/local/lib64` in /etc/ld.so.conf (or place a file containing +"/usr/local/lib64/libcrypto.so" in /etc/ld.so.conf.d) and then run "sudo +ldconfig". + +### Building libplist \>= 2.0.0 from source. + +*(Note: on Debian 9 "Stretch" or Ubuntu 16.04 LTS editions, you can +avoid this step by installing libplist-dev and libplist3 from Debian 10 +or Ubuntu 18.04.)* As well as the usual build tools (autoconf, automake, +libtool), you may need to also install some libpython\*-dev package. +Download the latest source with git from +, or get the source from +the Releases section (use the \*.tar.bz2 release, **not** the \*.zip or +\*.tar.gz versions): download +[libplist-2.3.0](https://github.com/libimobiledevice/libplist/releases/download/2.3.0/libplist-2.3.0.tar.bz2), +then unpack it ("tar -xvjf libplist-2.3.0.tar.bz2 ; cd libplist-2.3.0"), +and build/install it: ("./configure ; make ; sudo make install"). This +will probably install libplist-2.0.\* in /usr/local/lib. The new +libplist-2.3.0 release should be compatible with UxPlay; +[libplist-2.2.0](https://github.com/libimobiledevice/libplist/releases/download/2.2.0/libplist-2.2.0.tar.bz2) +is also available if there are any issues. + +*(Ignore the following for builds on MacOS:)* On some systems like +Debian or Ubuntu, you may also need to add a missing entry +`/usr/local/lib` in /etc/ld.so.conf (or place a file containing +"/usr/local/lib/libplist-2.0.so" in /etc/ld.so.conf.d) and then run +"sudo ldconfig". + +# Disclaimer + +All the resources in this repository are written using only freely +available information from the internet. The code and related resources +are meant for educational purposes only. It is the responsibility of the +user to make sure all local laws are adhered to. + +This project makes use of a third-party GPL library for handling +FairPlay. The legal status of that library is unclear. Should you be a +representative of Apple and have any objections against the legality of +the library and its use in this project, please contact the developers +and the appropriate steps will be taken. + +Given the large number of third-party AirPlay receivers (mostly +closed-source) available for purchase, it is our understanding that an +open source implementation of the same functionality wouldn't violate +any of Apple's rights either. + +# UxPlay authors + +*\[adapted from fdraschbacher's notes on RPiPlay antecedents\]* + +The code in this repository accumulated from various sources over time. +Here is an attempt at listing the various authors and the components +they created: + +UxPlay was initially created by **antimof** from RPiPlay, by replacing +its Raspberry-Pi-adapted OpenMAX video and audio rendering system with +GStreamer rendering for desktop Linux systems; the antimof work on code +in `renderers/` was later backported to RPiPlay, and the antimof project +became dormant, but was later revived at the [current GitHub +site](http://github.com/FDH2/UxPlay) to serve a wider community of +users. + +The previous authors of code included in UxPlay by inheritance from +RPiPlay include: + +- **EstebanKubata**: Created a FairPlay library called + [PlayFair](https://github.com/EstebanKubata/playfair). Located in + the `lib/playfair` folder. License: GNU GPL +- **Juho Vähä-Herttua** and contributors: Created an AirPlay audio + server called [ShairPlay](https://github.com/juhovh/shairplay), + including support for Fairplay based on PlayFair. Most of the code + in `lib/` originally stems from this project. License: GNU LGPLv2.1+ +- **dsafa22**: Created an AirPlay 2 mirroring server + [AirplayServer](https://github.com/dsafa22/AirplayServer) (seems + gone now), for Android based on ShairPlay. Code is preserved + [here](https://github.com/jiangban/AirplayServer), and [see + here](https://github.com/FDH2/UxPlay/wiki/AirPlay2) for the + description of the analysis of the AirPlay 2 mirror protocol that + made RPiPlay possible, by the AirplayServer author. All code in + `lib/` concerning mirroring is dsafa22's work. License: GNU + LGPLv2.1+ +- **Florian Draschbacher** (FD-) and contributors: adapted dsafa22's + Android project for the Raspberry Pi, with extensive cleanups, + debugging and improvements. The project + [RPiPlay](https://github.com/FD-/RPiPlay) is basically a port of + dsafa22's code to the Raspberry Pi, utilizing OpenMAX and OpenSSL + for better performance on the Pi. License GPL v3. FD- has written an + interesting note on the history of [Airplay protocol + versions](http://github.com/FD-/RPiPlay#airplay-protocol-versions), + available at the RPiPlay github repository. + +Independent of UxPlay, but used by it and bundled with it: + +- **Fedor Indutny** (of Node.js, and formerly Joyent, Inc) and + contributors: Created an http parsing library called + [llhttp](https://github.com/nodejs/llhttp). Located at + `lib/llhttp/`. License: MIT diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 0000000..7db7869 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + execute_process( + COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/compile_commands.json b/compile_commands.json new file mode 120000 index 0000000..95eca4c --- /dev/null +++ b/compile_commands.json @@ -0,0 +1 @@ +./compile_commands.json \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..520c5bb --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,173 @@ +cmake_minimum_required(VERSION 3.5) +include_directories( playfair llhttp ) + +message( STATUS "*** CFLAGS \"" ${CMAKE_C_FLAGS} "\" from build environment will be postpended to CMAKE_CFLAGS" ) + +# Common x86/x86_64 cflags +if( NOT NO_MARCH_NATIVE AND CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)" ) + set( CMAKE_C_FLAGS "-Ofast -march=native ${CMAKE_C_FLAGS}" ) + message( STATUS "Using CFLAGS with -march=native" ) + message( STATUS "*** ONLY USE THIS WHEN COMPILING ON THE MACHINE THAT WILL RUN UXPLAY" ) + message( STATUS " run \"cmake -DNO_MARCH_NATIVE=ON\" to switch off this compiler option" ) +else() + message( STATUS "Not using -march=native" ) + set( CMAKE_C_FLAGS "-O2 ${CMAKE_C_FLAGS}" ) +endif() + +# Common Linux cflags +if ( UNIX AND NOT APPLE ) + set( CMAKE_C_FLAGS "-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall ${CMAKE_C_FLAGS}" ) +endif() + +if ( WIN32 ) + message( STATUS "Building for Windows " ) + set( CMAKE_C_FLAGS "-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_WIN32 -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall ${CMAKE_C_FLAGS}" ) +endif() + +message( STATUS "using CMAKE_CFLAGS: " ${CMAKE_C_FLAGS} ) + +#activate the NOHOLD feature to drop existing connections if a third connection is made +add_definitions( -DNOHOLD ) + +INCLUDE (CheckIncludeFiles) +if( WIN32 ) +CHECK_INCLUDE_FILES ("winsock2.h" WINSOCK2 ) +else() +# for BSD Unix (e.g. FreeBSD) +CHECK_INCLUDE_FILES ("sys/endian.h" BSD ) +if ( BSD ) + add_definitions( -DSYS_ENDIAN_H ) +endif ( BSD ) +endif() + +if( APPLE ) + set( ENV{PKG_CONFIG_PATH} "/usr/local/lib/pkgconfig" ) # standard location, and Brew + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/homebrew/lib/pkgconfig" ) # Brew for M1 macs + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:$ENV{HOMEBREW_PREFIX}/lib/pkgconfig" ) # Brew using prefix + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/local/lib/pkgconfig/" ) # MacPorts + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/openssl@3/lib/pkgconfig" ) # Brew openssl + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/homebrew/opt/openssl@3/lib/pkgconfig" ) # Brew M1 openssl + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:$ENV{HOMEBREW_PREFIX}/opt/openssl@3/lib/pkgconfig" ) # Brew using prefix openssl + message( "PKG_CONFIG_PATH (Apple, lib) = " $ENV{PKG_CONFIG_PATH} ) + find_program( PKG_CONFIG_EXECUTABLE pkg-config PATHS /Library/FrameWorks/GStreamer.framework/Commands ) + message( "PKG_CONFIG_EXECUTABLE " ${PKG_CONFIG_EXECUTABLE} ) +endif() +find_package(PkgConfig REQUIRED) + +aux_source_directory(. play_src) +set(DIR_SRCS ${play_src}) + +add_library( airplay STATIC + ${DIR_SRCS} + ) + +if ( APPLE ) + target_link_libraries( airplay + pthread + playfair + llhttp ) +elseif( WIN32 ) + target_link_libraries( airplay + pthread + playfair + llhttp + wsock32 + iphlpapi + ws2_32 ) +else() + target_link_libraries( airplay PUBLIC + pthread + playfair + llhttp ) +endif() + +# libplist + +if( APPLE ) + # use static linking + pkg_search_module(PLIST REQUIRED libplist-2.0) + find_library( LIBPLIST libplist-2.0.a REQUIRED ) + message( STATUS "(Static linking) LIBPLIST " ${LIBPLIST} ) + target_link_libraries ( airplay ${LIBPLIST} ) +elseif( WIN32) +pkg_search_module(PLIST REQUIRED libplist-2.0) + find_library( LIBPLIST ${PLIST_LIBRARIES} PATH ${PLIST_LIBDIR} ) + target_link_libraries ( airplay ${LIBPLIST} ) +else () + pkg_search_module(PLIST libplist>=2.0) + if(NOT PLIST_FOUND) + pkg_search_module(PLIST REQUIRED libplist-2.0) + endif() + find_library( LIBPLIST ${PLIST_LIBRARIES} PATH ${PLIST_LIBDIR} ) + target_link_libraries ( airplay PUBLIC ${LIBPLIST} ) +endif() +if ( PLIST_FOUND ) + message( STATUS "found libplist-${PLIST_VERSION}" ) +endif() +target_include_directories( airplay PRIVATE ${PLIST_INCLUDE_DIRS} ) + +#libcrypto +if( APPLE ) + # use static linking + # can either compile Openssl 1.1.1 or 3.0.0 from source (install_dev to /usr/local) or use Macports or Brew + # MacPorts needs zlib with it. Brew has a "keg-only" installation in /usr/local/opt/openssl@3 + # Brew on M1 macs puts this in /opt/homebrew/opt/openssl@3 + pkg_check_modules( OPENSSL REQUIRED openssl>=1.1.1) + message( "OPENSSL_LIBRARY_DIRS " ${OPENSSL_LIBRARY_DIRS} ) + message( "OPENSSL_INCLUDE_DIRS " ${OPENSSL_INCLUDE_DIRS} ) + find_library( LIBCRYPTO libcrypto.a PATHS ${OPENSSL_LIBRARY_DIRS} REQUIRED ) + message( "(Static linking) LIBCRYPTO " ${LIBCRYPTO} ) + target_link_libraries( airplay ${LIBCRYPTO} ) + if( LIBCRYPTO MATCHES "/opt/local/lib/libcrypto.a" ) #MacPorts openssl + find_library( LIBZ libz.a) # needed by MacPorts openssl + message("(MacPorts) LIBZ= " ${LIBZ} ) + target_link_libraries( airplay ${LIBZ} ) + endif() + target_include_directories( airplay PRIVATE ${OPENSSL_INCLUDE_DIRS} ) +elseif( WIN32 ) + find_package(OpenSSL 1.1.1 REQUIRED) + target_compile_definitions( airplay PUBLIC OPENSSL_API_COMPAT=0x10101000L ) + target_link_libraries( airplay OpenSSL::Crypto ) +else() + find_package(OpenSSL 1.1.1 REQUIRED) + target_compile_definitions( airplay PUBLIC OPENSSL_API_COMPAT=0x10101000L ) + target_link_libraries( airplay PUBLIC OpenSSL::Crypto ) +endif() + +#dns_sd +if ( NOT APPLE ) + pkg_search_module(AVAHI_DNSSD avahi-compat-libdns_sd) + if (AVAHI_DNSSD_FOUND) + target_include_directories( airplay PRIVATE ${AVAHI_DNSSD_INCLUDE_DIRS} ) + find_library( DNSSD ${AVAHI_DNSSD_LIBRARIES} PATH ${AVAHI_DNSSD_LIBDIR}) + target_link_libraries(airplay PUBLIC ${DNSSD} ) + else() # can also build if mDNSResponder or another implementation of dns_sd is present instead of Avahi + if ( WIN32 ) + if (DEFINED ENV{BONJOUR_SDK_HOME}) + set(BONJOUR_SDK "$ENV{BONJOUR_SDK_HOME}" ) + else() + set(BONJOUR_SDK "C:\\Program Files\\Bonjour SDK") + endif() + message( STATUS "BONJOUR_SDK_HOME " ${BONJOUR_SDK} ) + set(DNSSD "${BONJOUR_SDK}/Lib/x64/dnssd.lib") + target_link_libraries(airplay ${DNSSD} ) + message( STATUS "dns_sd: using " ${DNSSD} ) + find_path(DNSSD_INCLUDE_DIR dns_sd.h HINTS ${BONJOUR_SDK}/Include ) + else() + find_library( DNSSD dns_sd ) + if( NOT DNSSD ) + message( FATAL_ERROR "libdns_sd missing; can be provided by avahi_compat-libdns_sd or mDNSResponder." ) + else() + message( STATUS "dns_sd: found" ${DNSSD} ) + endif() + target_link_libraries(airplay PUBLIC ${DNSSD} ) + find_path(DNSSD_INCLUDE_DIR dns_sd.h HINTS ${CMAKE_INSTALL_INCLUDEDIR} ) + endif() + if ( NOT DNSSD_INCLUDE_DIR ) + message( FATAL_ERROR " dns_sd.h not found ") + else() + message( STATUS "found dns_sd.h in " ${DNSSD_INCLUDE_DIR} ) + endif() + target_include_directories( airplay PRIVATE ${DNSSD_INCLUDE_DIR} ) + endif() +endif() diff --git a/lib/airplay_video.c b/lib/airplay_video.c new file mode 100644 index 0000000..5749057 --- /dev/null +++ b/lib/airplay_video.c @@ -0,0 +1,322 @@ +/** + * Copyright (c) 2024 fduncanh + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +// it should only start and stop the media_data_store that handles all HLS transactions, without +// otherwise participating in them. + +#include +#include +#include +#include +#include + +#include "raop.h" +#include "airplay_video.h" + +struct media_item_s { + char *uri; + char *playlist; + int access; +}; + +struct airplay_video_s { + raop_t *raop; + char apple_session_id[37]; + char playback_uuid[37]; + char *uri_prefix; + char local_uri_prefix[23]; + int next_uri; + int FCUP_RequestID; + float start_position_seconds; + playback_info_t *playback_info; + // The local port of the airplay server on the AirPlay server + unsigned short airplay_port; + char *master_uri; + char *master_playlist; + media_item_t *media_data_store; + int num_uri; +}; + +// initialize airplay_video service. +int airplay_video_service_init(raop_t *raop, unsigned short http_port, + const char *session_id) { + char uri[] = "http://localhost:xxxxx"; + assert(raop); + + airplay_video_t *airplay_video = deregister_airplay_video(raop); + if (airplay_video) { + airplay_video_service_destroy(airplay_video); + } + + airplay_video = (airplay_video_t *) calloc(1, sizeof(airplay_video_t)); + if (!airplay_video) { + return -1; + } + + /* create local_uri_prefix string */ + strncpy(airplay_video->local_uri_prefix, uri, sizeof(airplay_video->local_uri_prefix)); + char *ptr = strstr(airplay_video->local_uri_prefix, "xxxxx"); + snprintf(ptr, 6, "%-5u", http_port); + ptr = strstr(airplay_video->local_uri_prefix, " "); + if (ptr) { + *ptr = '\0'; + } + + if (!register_airplay_video(raop, airplay_video)) { + return -2; + } + + //printf(" %p %p\n", airplay_video, get_airplay_video(raop)); + + airplay_video->raop = raop; + + + airplay_video->FCUP_RequestID = 0; + + + size_t len = strlen(session_id); + assert(len == 36); + strncpy(airplay_video->apple_session_id, session_id, len); + (airplay_video->apple_session_id)[len] = '\0'; + + airplay_video->start_position_seconds = 0.0f; + + airplay_video->master_uri = NULL; + airplay_video->media_data_store = NULL; + airplay_video->master_playlist = NULL; + airplay_video->num_uri = 0; + airplay_video->next_uri = 0; + return 0; +} + +// destroy the airplay_video service +void +airplay_video_service_destroy(airplay_video_t *airplay_video) +{ + + if (airplay_video->uri_prefix) { + free(airplay_video->uri_prefix); + } + if (airplay_video->master_uri) { + free (airplay_video->master_uri); + } + if (airplay_video->media_data_store) { + destroy_media_data_store(airplay_video); + } + if (airplay_video->master_playlist) { + free (airplay_video->master_playlist); + } + + + free (airplay_video); +} + +const char *get_apple_session_id(airplay_video_t *airplay_video) { + return airplay_video->apple_session_id; +} + +float get_start_position_seconds(airplay_video_t *airplay_video) { + return airplay_video->start_position_seconds; +} + +void set_start_position_seconds(airplay_video_t *airplay_video, float start_position_seconds) { + airplay_video->start_position_seconds = start_position_seconds; +} + +void set_playback_uuid(airplay_video_t *airplay_video, const char *playback_uuid) { + size_t len = strlen(playback_uuid); + assert(len == 36); + memcpy(airplay_video->playback_uuid, playback_uuid, len); + (airplay_video->playback_uuid)[len] = '\0'; +} + +void set_uri_prefix(airplay_video_t *airplay_video, char *uri_prefix, int uri_prefix_len) { + if (airplay_video->uri_prefix) { + free (airplay_video->uri_prefix); + } + airplay_video->uri_prefix = (char *) calloc(uri_prefix_len + 1, sizeof(char)); + memcpy(airplay_video->uri_prefix, uri_prefix, uri_prefix_len); +} + +char *get_uri_prefix(airplay_video_t *airplay_video) { + return airplay_video->uri_prefix; +} + +char *get_uri_local_prefix(airplay_video_t *airplay_video) { + return airplay_video->local_uri_prefix; +} + + +char *get_master_uri(airplay_video_t *airplay_video) { + return airplay_video->master_uri; +} + + +int get_next_FCUP_RequestID(airplay_video_t *airplay_video) { + return ++(airplay_video->FCUP_RequestID); +} + +void set_next_media_uri_id(airplay_video_t *airplay_video, int num) { + airplay_video->next_uri = num; +} + +int get_next_media_uri_id(airplay_video_t *airplay_video) { + return airplay_video->next_uri; +} + + +/* master playlist */ + +void store_master_playlist(airplay_video_t *airplay_video, char *master_playlist) { + if (airplay_video->master_playlist) { + free (airplay_video->master_playlist); + } + airplay_video->master_playlist = master_playlist; +} + +char *get_master_playlist(airplay_video_t *airplay_video) { + return airplay_video->master_playlist; +} + +/* media_data_store */ + +int get_num_media_uri(airplay_video_t *airplay_video) { + return airplay_video->num_uri; +} + +void destroy_media_data_store(airplay_video_t *airplay_video) { + media_item_t *media_data_store = airplay_video->media_data_store; + if (media_data_store) { + for (int i = 0; i < airplay_video->num_uri ; i ++ ) { + if (media_data_store[i].uri) { + free (media_data_store[i].uri); + } + if (media_data_store[i].playlist) { + free (media_data_store[i].playlist); + } + } + } + free (media_data_store); + airplay_video->num_uri = 0; +} + +void create_media_data_store(airplay_video_t * airplay_video, char ** uri_list, int num_uri) { + destroy_media_data_store(airplay_video); + media_item_t *media_data_store = calloc(num_uri, sizeof(media_item_t)); + for (int i = 0; i < num_uri; i++) { + media_data_store[i].uri = uri_list[i]; + media_data_store[i].playlist = NULL; + media_data_store[i].access = 0; + } + airplay_video->media_data_store = media_data_store; + airplay_video->num_uri = num_uri; +} + +int store_media_data_playlist_by_num(airplay_video_t *airplay_video, char * media_playlist, int num) { + media_item_t *media_data_store = airplay_video->media_data_store; + if ( num < 0 || num >= airplay_video->num_uri) { + return -1; + } else if (media_data_store[num].playlist) { + return -2; + } + media_data_store[num].playlist = media_playlist; + return 0; +} + +char * get_media_playlist_by_num(airplay_video_t *airplay_video, int num) { + media_item_t *media_data_store = airplay_video->media_data_store; + if (media_data_store == NULL) { + return NULL; + } + if (num >= 0 && num num_uri) { + return media_data_store[num].playlist; + } + return NULL; +} + +int get_media_playlist_by_uri(airplay_video_t *airplay_video, const char *uri) { + /* Problem: there can be more than one StreamInf playlist with the same uri: + * they differ by choice of partner Media (audio, subtitles) playlists + * If the same uri is requested again, one of the other ones will be returned + * (the least-previously-requested one will be served up) + */ + // modified to return the position of the media playlist in the master playlist + media_item_t *media_data_store = airplay_video->media_data_store; + if (media_data_store == NULL) { + return -2; + } + int found = 0;; + int num = -1; + int access = -1; + for (int i = 0; i < airplay_video->num_uri; i++) { + if (strstr(media_data_store[i].uri, uri)) { + if (!found) { + found = 1; + num = i; + access = media_data_store[i].access; + } else { + /* change > below to >= to reverse the order of choice */ + if (access > media_data_store[i].access) { + access = media_data_store[i].access; + num = i; + } + } + } + } + if (found) { + //printf("found %s\n", media_data_store[num].uri); + ++media_data_store[num].access; + return num; + } + return -1; +} + +char * get_media_uri_by_num(airplay_video_t *airplay_video, int num) { + media_item_t * media_data_store = airplay_video->media_data_store; + if (media_data_store == NULL) { + return NULL; + } + if (num >= 0 && num < airplay_video->num_uri) { + return media_data_store[num].uri; + } + return NULL; +} + +int get_media_uri_num(airplay_video_t *airplay_video, char * uri) { + media_item_t *media_data_store = airplay_video->media_data_store; + for (int i = 0; i < airplay_video->num_uri ; i++) { + if (strstr(media_data_store[i].uri, uri)) { + return i; + } + } + return -1; +} + +int analyze_media_playlist(char *playlist, float *duration) { + float next; + int count = 0; + char *ptr = strstr(playlist, "#EXTINF:"); + *duration = 0.0f; + while (ptr != NULL) { + char *end; + ptr += strlen("#EXTINF:"); + next = strtof(ptr, &end); + *duration += next; + count++; + ptr = strstr(end, "#EXTINF:"); + } + return count; +} diff --git a/lib/airplay_video.h b/lib/airplay_video.h new file mode 100644 index 0000000..81fb589 --- /dev/null +++ b/lib/airplay_video.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 fduncanh, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + */ + +#ifndef AIRPLAY_VIDEO_H +#define AIRPLAY_VIDEO_H + + +#include +#include +#include "raop.h" +#include "logger.h" + +typedef struct airplay_video_s airplay_video_t; +typedef struct media_item_s media_item_t; + +const char *get_apple_session_id(airplay_video_t *airplay_video); +void set_start_position_seconds(airplay_video_t *airplay_video, float start_position_seconds); +float get_start_position_seconds(airplay_video_t *airplay_video); +void set_playback_uuid(airplay_video_t *airplay_video, const char *playback_uuid); +void set_uri_prefix(airplay_video_t *airplay_video, char *uri_prefix, int uri_prefix_len); +char *get_uri_prefix(airplay_video_t *airplay_video); +char *get_uri_local_prefix(airplay_video_t *airplay_video); +int get_next_FCUP_RequestID(airplay_video_t *airplay_video); +void set_next_media_uri_id(airplay_video_t *airplay_video, int id); +int get_next_media_uri_id(airplay_video_t *airplay_video); +int get_media_playlist_by_uri(airplay_video_t *airplay_video, const char *uri); +void store_master_playlist(airplay_video_t *airplay_video, char *master_playlist); +char *get_master_playlist(airplay_video_t *airplay_video); +int get_num_media_uri(airplay_video_t *airplay_video); +void destroy_media_data_store(airplay_video_t *airplay_video); +void create_media_data_store(airplay_video_t * airplay_video, char ** media_data_store, int num_uri); +int store_media_data_playlist_by_num(airplay_video_t *airplay_video, char * media_playlist, int num); +char *get_media_playlist_by_num(airplay_video_t *airplay_video, int num); +char *get_media_uri_by_num(airplay_video_t *airplay_video, int num); +int get_media_uri_num(airplay_video_t *airplay_video, char * uri); +int analyze_media_playlist(char *playlist, float *duration); + +void airplay_video_service_destroy(airplay_video_t *airplay_video); + +// C wrappers for c++ class MediaDataStore +//create the media_data_store, return a pointer to it. +void* media_data_store_create(void *conn_opaque, uint16_t port); + +//delete the media_data_store +void media_data_store_destroy(void *media_data_store); + +// called by the POST /action handler: +char *process_media_data(void *media_data_store, const char *url, const char *data, int datalen); + +//called by the POST /play handler +bool request_media_data(void *media_data_store, const char *primary_url, const char * session_id); + +//called by airplay_video_media_http_connection::get_handler: &path = req.uri) +char *query_media_data(void *media_data_store, const char *url, int *len); + +//called by the post_stop_handler: +void media_data_store_reset(void *media_data_store); + +const char *adjust_primary_uri(void *media_data_store, const char *url); + +#endif //AIRPLAY_VIDEO_H diff --git a/lib/byteutils.c b/lib/byteutils.c new file mode 100644 index 0000000..1687fb5 --- /dev/null +++ b/lib/byteutils.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019 dsafa22, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2021-23 + */ + + +#define SECOND_IN_NSECS 1000000000UL + +#include +#ifdef _WIN32 +# include +#else +# include +#endif + +#include "byteutils.h" + +#ifdef _WIN32 +# ifndef ntonll +# define ntohll(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32))) +# endif +#else +# ifndef htonll +# ifdef SYS_ENDIAN_H +# include +# else +# include +# endif +# define htonll(x) htobe64(x) +# define ntohll(x) be64toh(x) +# endif +#endif + +// The functions in this file assume a little endian cpu architecture! + +/** + * Reads a little endian unsigned 16 bit integer from the buffer at position offset + */ +uint16_t byteutils_get_short(unsigned char* b, int offset) { + return *((uint16_t*)(b + offset)); +} + +/** + * Reads a little endian unsigned 32 bit integer from the buffer at position offset + */ +uint32_t byteutils_get_int(unsigned char* b, int offset) { + return *((uint32_t*)(b + offset)); +} + +/** + * Reads a little endian unsigned 64 bit integer from the buffer at position offset + */ +uint64_t byteutils_get_long(unsigned char* b, int offset) { + return *((uint64_t*)(b + offset)); +} + +/** + * Reads a big endian unsigned 16 bit integer from the buffer at position offset + */ +uint16_t byteutils_get_short_be(unsigned char* b, int offset) { + return ntohs(byteutils_get_short(b, offset)); +} + +/** + * Reads a big endian unsigned 32 bit integer from the buffer at position offset + */ +uint32_t byteutils_get_int_be(unsigned char* b, int offset) { + return ntohl(byteutils_get_int(b, offset)); +} + +/** + * Reads a big endian unsigned 64 bit integer from the buffer at position offset + */ +uint64_t byteutils_get_long_be(unsigned char* b, int offset) { + return ntohll(byteutils_get_long(b, offset)); +} + +/** + * Reads a float from the buffer at position offset + */ +float byteutils_get_float(unsigned char* b, int offset) { + return *((float*)(b + offset)); +} + +/** + * Writes a little endian unsigned 32 bit integer to the buffer at position offset + */ +void byteutils_put_int(unsigned char* b, int offset, uint32_t value) { + *((uint32_t*)(b + offset)) = value; +} + +/** + * Reads an ntp timestamp and returns it as nano seconds since the Unix epoch + */ +uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset) { + uint64_t seconds = ntohl(((unsigned int) byteutils_get_int(b, offset))) - SECONDS_FROM_1900_TO_1970; + uint64_t fraction = ntohl((unsigned int) byteutils_get_int(b, offset + 4)); + return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32); +} + +/** + * Writes a time given as nano seconds since the Unix time epoch as an ntp timestamp + * into the buffer at position offset + */ +void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t ns_since_1970) { + uint64_t seconds = ns_since_1970 / SECOND_IN_NSECS; + uint64_t nanoseconds = ns_since_1970 % SECOND_IN_NSECS; + seconds += SECONDS_FROM_1900_TO_1970; + uint64_t fraction = (nanoseconds << 32) / SECOND_IN_NSECS; + + // Write in big endian! + byteutils_put_int(b, offset, htonl(seconds)); + byteutils_put_int(b, offset + 4, htonl(fraction)); +} + diff --git a/lib/byteutils.h b/lib/byteutils.h new file mode 100644 index 0000000..9f8a4c5 --- /dev/null +++ b/lib/byteutils.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 dsafa22, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef AIRPLAYSERVER_BYTEUTILS_H +#define AIRPLAYSERVER_BYTEUTILS_H +#include + +uint16_t byteutils_get_short(unsigned char* b, int offset); +uint32_t byteutils_get_int(unsigned char* b, int offset); +uint64_t byteutils_get_long(unsigned char* b, int offset); +uint16_t byteutils_get_short_be(unsigned char* b, int offset); +uint32_t byteutils_get_int_be(unsigned char* b, int offset); +uint64_t byteutils_get_long_be(unsigned char* b, int offset); +float byteutils_get_float(unsigned char* b, int offset); + +#define SECONDS_FROM_1900_TO_1970 2208988800ULL + +uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset); +void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970); + +#endif //AIRPLAYSERVER_BYTEUTILS_H diff --git a/lib/compat.c b/lib/compat.c new file mode 100644 index 0000000..cba80fc --- /dev/null +++ b/lib/compat.c @@ -0,0 +1,36 @@ + +/* + * Copyright (c) 2024 F. Duncanh, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + */ + +#ifdef _WIN32 +#include +#include +#include "compat.h" + +#define MAX_SOCKET_ERROR_MESSAGE_LENGTH 256 + +/* Windows (winsock2) socket error message text */ +char *wsa_strerror(int errnum) { + static char message[MAX_SOCKET_ERROR_MESSAGE_LENGTH] = { 0 }; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + 0, errnum, 0, message, sizeof(message), 0); + char *nl = strchr(message, '\n'); + if (nl) { + *nl = 0; /* remove any trailing newline, or truncate to one line */ + } + return message; +} +#endif diff --git a/lib/compat.h b/lib/compat.h new file mode 100644 index 0000000..4ccfb02 --- /dev/null +++ b/lib/compat.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef COMPAT_H +#define COMPAT_H + +#if defined(WIN32) +#include +#include +#ifndef snprintf +#define snprintf _snprintf +#endif +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "sockets.h" +#include "threads.h" + +#endif diff --git a/lib/crypto.c b/lib/crypto.c new file mode 100644 index 0000000..d530d97 --- /dev/null +++ b/lib/crypto.c @@ -0,0 +1,581 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Copyright (C) 2020 Jaslo Ziska + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + *=================================================================== + * modified by fduncanh 2021-2022 + */ + +#include "crypto.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "utils.h" + +#define SALT_PK "UxPlay-Persistent-Not-Secure-Public-Key" + +struct aes_ctx_s { + EVP_CIPHER_CTX *cipher_ctx; + uint8_t key[AES_128_BLOCK_SIZE]; + uint8_t iv[AES_128_BLOCK_SIZE]; + aes_direction_t direction; + uint8_t block_offset; +}; + +uint8_t waste[AES_128_BLOCK_SIZE]; + +// Common AES utilities + +void handle_error(const char* location) { + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + fprintf(stderr, "Crypto error at %s: %s\n", location, error_str); + exit(EXIT_FAILURE); +} + +aes_ctx_t *aes_init(const uint8_t *key, const uint8_t *iv, const EVP_CIPHER *type, aes_direction_t direction) { + aes_ctx_t *ctx = malloc(sizeof(aes_ctx_t)); + assert(ctx != NULL); + ctx->cipher_ctx = EVP_CIPHER_CTX_new(); + assert(ctx->cipher_ctx != NULL); + + ctx->block_offset = 0; + ctx->direction = direction; + + if (direction == AES_ENCRYPT) { + if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) { + handle_error(__func__); + } + } else { + if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) { + handle_error(__func__); + } + } + + memcpy(ctx->key, key, AES_128_BLOCK_SIZE); + memcpy(ctx->iv, iv, AES_128_BLOCK_SIZE); + EVP_CIPHER_CTX_set_padding(ctx->cipher_ctx, 0); + return ctx; +} + +void aes_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) { + int out_len_e = 0; + if (!EVP_EncryptUpdate(ctx->cipher_ctx, out, &out_len_e, in, in_len)) { + handle_error(__func__); + } + int out_len_f = in_len - out_len_e; + if (!EVP_EncryptFinal_ex(ctx->cipher_ctx, out + out_len_e, &out_len_f)) { + handle_error(__func__); + } + assert(out_len_e + out_len_f <= in_len); +} + +void aes_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) { + int out_len_d = 0; + if (!EVP_DecryptUpdate(ctx->cipher_ctx, out, &out_len_d, in, in_len)) { + handle_error(__func__); + } + int out_len_f = in_len - out_len_d; + if (!EVP_DecryptFinal_ex(ctx->cipher_ctx, out + out_len_d, &out_len_f)) { + handle_error(__func__); + } + assert(out_len_f + out_len_d <= in_len); +} + +void aes_destroy(aes_ctx_t *ctx) { + if (ctx) { + EVP_CIPHER_CTX_free(ctx->cipher_ctx); + free(ctx); + } +} + +void aes_reset(aes_ctx_t *ctx, const EVP_CIPHER *type, aes_direction_t direction) { + uint8_t key[AES_128_BLOCK_SIZE], iv[AES_128_BLOCK_SIZE]; + memcpy(key, ctx->key, AES_128_BLOCK_SIZE); + memcpy(iv, ctx->iv, AES_128_BLOCK_SIZE); + + if (!EVP_CIPHER_CTX_reset(ctx->cipher_ctx)) { + handle_error(__func__); + } + + if (direction == AES_ENCRYPT) { + if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) { + handle_error(__func__); + } + } else { + if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) { + handle_error(__func__); + } + } + + memcpy(ctx->key, key, AES_128_BLOCK_SIZE); + memcpy(ctx->iv, iv, AES_128_BLOCK_SIZE); + EVP_CIPHER_CTX_set_padding(ctx->cipher_ctx, 0); +} + +// AES CTR + +aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv) { + return aes_init(key, iv, EVP_aes_128_ctr(), AES_ENCRYPT); +} + +void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) { + aes_encrypt(ctx, in, out, len); + ctx->block_offset = (ctx->block_offset + len) % AES_128_BLOCK_SIZE; +} + +void aes_ctr_start_fresh_block(aes_ctx_t *ctx) { + // Is there a better way to do this? + if (ctx->block_offset == 0) return; + aes_ctr_encrypt(ctx, waste, waste, AES_128_BLOCK_SIZE - ctx->block_offset); +} + +void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) { + aes_encrypt(ctx, in, out, len); +} + +void aes_ctr_reset(aes_ctx_t *ctx) { + aes_reset(ctx, EVP_aes_128_ctr(), AES_ENCRYPT); +} + +void aes_ctr_destroy(aes_ctx_t *ctx) { + aes_destroy(ctx); +} + +// AES CBC + +aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction) { + return aes_init(key, iv, EVP_aes_128_cbc(), direction); +} + +void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) { + assert(ctx->direction == AES_ENCRYPT); + aes_encrypt(ctx, in, out, len); +} + +void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) { + assert(ctx->direction == AES_DECRYPT); + aes_decrypt(ctx, in, out, len); +} + +void aes_cbc_reset(aes_ctx_t *ctx) { + aes_reset(ctx, EVP_aes_128_cbc(), ctx->direction); +} + +void aes_cbc_destroy(aes_ctx_t *ctx) { + aes_destroy(ctx); +} + +// X25519 + +struct x25519_key_s { + EVP_PKEY *pkey; +}; + +x25519_key_t *x25519_key_generate(void) { + x25519_key_t *key; + EVP_PKEY_CTX *pctx; + + key = calloc(1, sizeof(x25519_key_t)); + assert(key); + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); + if (!pctx) { + handle_error(__func__); + } + if (!EVP_PKEY_keygen_init(pctx)) { + handle_error(__func__); + } + if (!EVP_PKEY_keygen(pctx, &key->pkey)) { + handle_error(__func__); + } + EVP_PKEY_CTX_free(pctx); + + return key; +} + +x25519_key_t *x25519_key_from_raw(const unsigned char data[X25519_KEY_SIZE]) { + x25519_key_t *key; + + key = malloc(sizeof(x25519_key_t)); + assert(key); + + key->pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, data, X25519_KEY_SIZE); + if (!key->pkey) { + handle_error(__func__); + } + + return key; +} + +void x25519_key_get_raw(unsigned char data[X25519_KEY_SIZE], const x25519_key_t *key) { + assert(key); + if (!EVP_PKEY_get_raw_public_key(key->pkey, data, &(size_t) {X25519_KEY_SIZE})) { + handle_error(__func__); + } +} + +void x25519_key_destroy(x25519_key_t *key) { + if (key) { + EVP_PKEY_free(key->pkey); + free(key); + } +} + +void x25519_derive_secret(unsigned char secret[X25519_KEY_SIZE], const x25519_key_t *ours, const x25519_key_t *theirs) { + EVP_PKEY_CTX *pctx; + + assert(ours); + assert(theirs); + + pctx = EVP_PKEY_CTX_new(ours->pkey, NULL); + if (!pctx) { + handle_error(__func__); + } + if (!EVP_PKEY_derive_init(pctx)) { + handle_error(__func__); + } + if (!EVP_PKEY_derive_set_peer(pctx, theirs->pkey)) { + handle_error(__func__); + } + if (!EVP_PKEY_derive(pctx, secret, &(size_t) {X25519_KEY_SIZE})) { + handle_error(__func__); + } + EVP_PKEY_CTX_free(pctx); +} + +// GCM AES 128 + +int gcm_encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *ciphertext, + unsigned char *key, unsigned char *iv, unsigned char *tag) +{ + EVP_CIPHER_CTX *ctx; + + int len; + + int ciphertext_len; + + if(!(ctx = EVP_CIPHER_CTX_new())) + handle_error(__func__); + + if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) + handle_error(__func__); + + if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) + handle_error(__func__); + + if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) + handle_error(__func__); + + if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) + handle_error(__func__); + ciphertext_len = len; + + if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) + handle_error(__func__); + ciphertext_len += len; + + if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) + handle_error(__func__); + + EVP_CIPHER_CTX_free(ctx); + + return ciphertext_len; +} + +int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *plaintext, + unsigned char *key, unsigned char *iv, unsigned char *tag) +{ + EVP_CIPHER_CTX *ctx; + int len; + int plaintext_len; + int ret; + + if(!(ctx = EVP_CIPHER_CTX_new())) + handle_error(__func__); + + if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) + handle_error(__func__); + + if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) + handle_error(__func__); + + if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) + handle_error(__func__); + + if(!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) + handle_error(__func__); + plaintext_len = len; + + if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) + handle_error(__func__); + + ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len); + + EVP_CIPHER_CTX_free(ctx); + + if(ret > 0) { + /* Success */ + plaintext_len += len; + return plaintext_len; + } else { + /* Verify failed */ + return -1; + } +} + +// ED25519 + +struct ed25519_key_s { + EVP_PKEY *pkey; +}; + +ed25519_key_t *ed25519_key_generate(const char *device_id, const char *keyfile, int *result) { + ed25519_key_t *key; + EVP_PKEY_CTX *pctx; + BIO *bp; + FILE *file; + bool new_pk = false; + bool use_keyfile = strlen(keyfile); + + *result = 0; + + key = calloc(1, sizeof(ed25519_key_t)); + assert(key); + + if (use_keyfile) { + file = fopen(keyfile, "r"); + if (file) { + bp = BIO_new_fp(file, BIO_NOCLOSE); + key->pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL); + BIO_free(bp); + fclose(file); + if (!key->pkey) { + new_pk = true; + } + } else { + new_pk = true; + } + } else { + /* generate (insecure) persistent keypair using device_id */ + unsigned char hash[SHA512_DIGEST_LENGTH]; + char salt[] = SALT_PK; + sha_ctx_t *ctx = sha_init(); + sha_update(ctx, (const unsigned char *) salt, (unsigned int) strlen(salt)); + sha_update(ctx, (const unsigned char *) device_id, (unsigned int) strlen(device_id)); + sha_final(ctx, hash, NULL); + sha_destroy(ctx); + key->pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, (const unsigned char *) hash, ED25519_KEY_SIZE); + } + + if (new_pk) { + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL); + if (!pctx) { + handle_error(__func__); + } + if (!EVP_PKEY_keygen_init(pctx)) { + handle_error(__func__); + } + if (!EVP_PKEY_keygen(pctx, &key->pkey)) { + handle_error(__func__); + } + EVP_PKEY_CTX_free(pctx); + if (use_keyfile) { + file = fopen(keyfile, "w"); + if (file) { + bp = BIO_new_fp(file, BIO_NOCLOSE); + PEM_write_bio_PrivateKey(bp, key->pkey, NULL, NULL, 0, NULL, NULL); + BIO_free(bp); + fclose(file); + *result = 1; + } + } + } + return key; +} + +ed25519_key_t *ed25519_key_from_raw(const unsigned char data[ED25519_KEY_SIZE]) { + ed25519_key_t *key; + + key = malloc(sizeof(ed25519_key_t)); + assert(key); + + key->pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, data, ED25519_KEY_SIZE); + if (!key->pkey) { + handle_error(__func__); + } + + return key; +} + +void ed25519_key_get_raw(unsigned char data[ED25519_KEY_SIZE], const ed25519_key_t *key) { + assert(key); + if (!EVP_PKEY_get_raw_public_key(key->pkey, data, &(size_t) {ED25519_KEY_SIZE})) { + handle_error(__func__); + } +} + +ed25519_key_t *ed25519_key_copy(const ed25519_key_t *key) { + ed25519_key_t *new_key; + + assert(key); + + new_key = malloc(sizeof(ed25519_key_t)); + assert(new_key); + + new_key->pkey = key->pkey; + if (!EVP_PKEY_up_ref(key->pkey)) { + handle_error(__func__); + } + + return new_key; +} + +void ed25519_sign(unsigned char *signature, size_t signature_len, + const unsigned char *data, size_t data_len, + const ed25519_key_t *key) +{ + EVP_MD_CTX *mctx; + + mctx = EVP_MD_CTX_new(); + if (!mctx) { + handle_error(__func__); + } + + if (!EVP_DigestSignInit(mctx, NULL, NULL, NULL, key->pkey)) { + handle_error(__func__); + } + if (!EVP_DigestSign(mctx, signature, &signature_len, data, data_len)) { + handle_error(__func__); + } + + EVP_MD_CTX_free(mctx); +} + +int ed25519_verify(const unsigned char *signature, size_t signature_len, + const unsigned char *data, size_t data_len, + const ed25519_key_t *key) +{ + EVP_MD_CTX *mctx; + + mctx = EVP_MD_CTX_new(); + if (!mctx) { + handle_error(__func__); + } + + if (!EVP_DigestVerifyInit(mctx, NULL, NULL, NULL, key->pkey)) { + handle_error(__func__); + } + + int ret = EVP_DigestVerify(mctx, signature, signature_len, data, data_len); + if (ret < 0) { + handle_error(__func__); + } + + EVP_MD_CTX_free(mctx); + + return ret; +} + +void ed25519_key_destroy(ed25519_key_t *key) { + if (key) { + EVP_PKEY_free(key->pkey); + free(key); + } +} + +// SHA 512 + +struct sha_ctx_s { + EVP_MD_CTX *digest_ctx; +}; + +sha_ctx_t *sha_init() { + sha_ctx_t *ctx = malloc(sizeof(sha_ctx_t)); + assert(ctx != NULL); + ctx->digest_ctx = EVP_MD_CTX_new(); + assert(ctx->digest_ctx != NULL); + + if (!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) { + handle_error(__func__); + } + return ctx; +} + +void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len) { + if (!EVP_DigestUpdate(ctx->digest_ctx, in, len)) { + handle_error(__func__); + } +} + +void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len) { + if (!EVP_DigestFinal_ex(ctx->digest_ctx, out, len)) { + handle_error(__func__); + } +} + +void sha_reset(sha_ctx_t *ctx) { + if (!EVP_MD_CTX_reset(ctx->digest_ctx) || + !EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) { + + handle_error(__func__); + } +} + +void sha_destroy(sha_ctx_t *ctx) { + if (ctx) { + EVP_MD_CTX_free(ctx->digest_ctx); + free(ctx); + } +} + +int get_random_bytes(unsigned char *buf, int num) { + return RAND_bytes(buf, num); +} +#include +void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len) { + memset(pk_base64, 0, len); + int len64 = (4 * (pk_len /3)) + (pk_len % 3 ? 4 : 0); + + assert (len > len64); + + BIO *b64 = BIO_new(BIO_f_base64()); + BIO *bio = BIO_new(BIO_s_mem()); + BUF_MEM *bufferPtr; + + + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + BIO_write(bio, pk, pk_len); + BIO_flush(bio); + + BIO_get_mem_ptr(bio, &bufferPtr); + BIO_set_close(bio, BIO_NOCLOSE); + BIO_free_all(bio); + memcpy(pk_base64,(*bufferPtr).data, len64); +} + diff --git a/lib/crypto.h b/lib/crypto.h new file mode 100644 index 0000000..7bd2d5e --- /dev/null +++ b/lib/crypto.h @@ -0,0 +1,115 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Copyright (C) 2020 Jaslo Ziska + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * modified by fduncanh 2023 + */ + +/* + * Helper methods for various crypto operations. + * Uses OpenSSL behind the scenes. +*/ + +#ifndef CRYPTO_H +#define CRYPTO_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// 128bit AES in CTR mode + +#define AES_128_BLOCK_SIZE 16 + +typedef enum aes_direction_e { AES_DECRYPT, AES_ENCRYPT } aes_direction_t; + +typedef struct aes_ctx_s aes_ctx_t; + +aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv); +void aes_ctr_reset(aes_ctx_t *ctx); +void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len); +void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len); +void aes_ctr_start_fresh_block(aes_ctx_t *ctx); +void aes_ctr_destroy(aes_ctx_t *ctx); + +aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction); +void aes_cbc_reset(aes_ctx_t *ctx); +void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len); +void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len); +void aes_cbc_destroy(aes_ctx_t *ctx); + +// X25519 + +#define X25519_KEY_SIZE 32 + +typedef struct x25519_key_s x25519_key_t; + +x25519_key_t *x25519_key_generate(void); +x25519_key_t *x25519_key_from_raw(const unsigned char data[X25519_KEY_SIZE]); +void x25519_key_get_raw(unsigned char data[X25519_KEY_SIZE], const x25519_key_t *key); +void x25519_key_destroy(x25519_key_t *key); +int get_random_bytes(unsigned char *buf, int num); +void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len); + +void x25519_derive_secret(unsigned char secret[X25519_KEY_SIZE], const x25519_key_t *ours, const x25519_key_t *theirs); + +// GCM AES 128 + +int gcm_encrypt(const unsigned char *plaintext, int plaintext_len, unsigned char *ciphertext, + unsigned char *key, unsigned char *iv, unsigned char *tag); +int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *plaintext, + unsigned char *key, unsigned char *iv, unsigned char *tag); +// ED25519 + +#define ED25519_KEY_SIZE 32 + +typedef struct ed25519_key_s ed25519_key_t; + +ed25519_key_t *ed25519_key_generate(const char *device_id, const char * keyfile, int * result); +ed25519_key_t *ed25519_key_from_raw(const unsigned char data[ED25519_KEY_SIZE]); +void ed25519_key_get_raw(unsigned char data[ED25519_KEY_SIZE], const ed25519_key_t *key); +/* + * Note that this function does *not copy* the OpenSSL key but only the wrapper. The internal OpenSSL key is still the + * same. Only the reference count is increased so destroying both the original and the copy is allowed. + */ +ed25519_key_t *ed25519_key_copy(const ed25519_key_t *key); +void ed25519_key_destroy(ed25519_key_t *key); + +void ed25519_sign(unsigned char *signature, size_t signature_len, + const unsigned char *data, size_t data_len, + const ed25519_key_t *key); +int ed25519_verify(const unsigned char *signature, size_t signature_len, + const unsigned char *data, size_t data_len, + const ed25519_key_t *key); + +// SHA512 + +typedef struct sha_ctx_s sha_ctx_t; +sha_ctx_t *sha_init(); +void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len); +void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len); +void sha_reset(sha_ctx_t *ctx); +void sha_destroy(sha_ctx_t *ctx); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/dnssd.c b/lib/dnssd.c new file mode 100644 index 0000000..0725937 --- /dev/null +++ b/lib/dnssd.c @@ -0,0 +1,482 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2022 + */ + +/* These defines allow us to compile on iOS */ +#ifndef __has_feature +# define __has_feature(x) 0 +#endif +#ifndef __has_extension +# define __has_extension __has_feature +#endif + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dnssdint.h" +#include "dnssd.h" +#include "global.h" +#include "compat.h" +#include "utils.h" + +#include + +#define MAX_DEVICEID 18 +#define MAX_SERVNAME 256 + +#if defined(HAVE_LIBDL) && !defined(__APPLE__) +# define USE_LIBDL 1 +#else +# define USE_LIBDL 0 +#endif + +#if defined(_WIN32) || USE_LIBDL +# ifdef _WIN32 +# include +# if !defined(EFI32) && !defined(EFI64) +# define DNSSD_STDCALL __stdcall +# else +# define DNSSD_STDCALL +# endif +# else +# include +# define DNSSD_STDCALL +# endif + +typedef struct _DNSServiceRef_t *DNSServiceRef; +#ifndef _WIN32 +typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; +#endif +typedef uint32_t DNSServiceFlags; +typedef int32_t DNSServiceErrorType; + +typedef void (DNSSD_STDCALL *DNSServiceRegisterReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context + ); + +#else +//# include +# define DNSSD_STDCALL +#endif + +typedef DNSServiceErrorType (DNSSD_STDCALL *DNSServiceRegister_t) + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + const char *host, + uint16_t port, + uint16_t txtLen, + const void *txtRecord, + DNSServiceRegisterReply callBack, + void *context + ); +typedef void (DNSSD_STDCALL *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef); +typedef void (DNSSD_STDCALL *TXTRecordCreate_t) + ( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer + ); +typedef void (DNSSD_STDCALL *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord); +typedef DNSServiceErrorType (DNSSD_STDCALL *TXTRecordSetValue_t) + ( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, + const void *value + ); +typedef uint16_t (DNSSD_STDCALL *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord); +typedef const void * (DNSSD_STDCALL *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord); + + +struct dnssd_s { +#ifdef WIN32 + HMODULE module; +#elif USE_LIBDL + void *module; +#endif + + DNSServiceRegister_t DNSServiceRegister; + DNSServiceRefDeallocate_t DNSServiceRefDeallocate; + TXTRecordCreate_t TXTRecordCreate; + TXTRecordSetValue_t TXTRecordSetValue; + TXTRecordGetLength_t TXTRecordGetLength; + TXTRecordGetBytesPtr_t TXTRecordGetBytesPtr; + TXTRecordDeallocate_t TXTRecordDeallocate; + + TXTRecordRef raop_record; + TXTRecordRef airplay_record; + + DNSServiceRef raop_service; + DNSServiceRef airplay_service; + + char *name; + int name_len; + + char *hw_addr; + int hw_addr_len; + + char *pk; + + uint32_t features1; + uint32_t features2; + + unsigned char require_pw; +}; + + + +dnssd_t * +dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len, int *error, int require_pw) +{ + dnssd_t *dnssd; + char *end; + unsigned long features; + + if (error) *error = DNSSD_ERROR_NOERROR; + + dnssd = calloc(1, sizeof(dnssd_t)); + if (!dnssd) { + if (error) *error = DNSSD_ERROR_OUTOFMEM; + return NULL; + } + + dnssd->require_pw = (unsigned char) require_pw; + + features = strtoul(FEATURES_1, &end, 16); + if (!end || (features & 0xFFFFFFFF) != features) { + free (dnssd); + if (error) *error = DNSSD_ERROR_BADFEATURES; + return NULL; + } + dnssd->features1 = (uint32_t) features; + + features = strtoul(FEATURES_2, &end, 16); + if (!end || (features & 0xFFFFFFFF) != features) { + free (dnssd); + if (error) *error = DNSSD_ERROR_BADFEATURES; + return NULL; + } + dnssd->features2 = (uint32_t) features; + +#ifdef WIN32 + dnssd->module = LoadLibraryA("dnssd.dll"); + if (!dnssd->module) { + if (error) *error = DNSSD_ERROR_LIBNOTFOUND; + free(dnssd); + return NULL; + } + dnssd->DNSServiceRegister = (DNSServiceRegister_t)GetProcAddress(dnssd->module, "DNSServiceRegister"); + dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)GetProcAddress(dnssd->module, "DNSServiceRefDeallocate"); + dnssd->TXTRecordCreate = (TXTRecordCreate_t)GetProcAddress(dnssd->module, "TXTRecordCreate"); + dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)GetProcAddress(dnssd->module, "TXTRecordSetValue"); + dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)GetProcAddress(dnssd->module, "TXTRecordGetLength"); + dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)GetProcAddress(dnssd->module, "TXTRecordGetBytesPtr"); + dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)GetProcAddress(dnssd->module, "TXTRecordDeallocate"); + + if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate || + !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr || + !dnssd->TXTRecordDeallocate) { + if (error) *error = DNSSD_ERROR_PROCNOTFOUND; + FreeLibrary(dnssd->module); + free(dnssd); + return NULL; + } +#elif USE_LIBDL + dnssd->module = dlopen("libdns_sd.so", RTLD_LAZY); + if (!dnssd->module) { + if (error) *error = DNSSD_ERROR_LIBNOTFOUND; + free(dnssd); + return NULL; + } + dnssd->DNSServiceRegister = (DNSServiceRegister_t)dlsym(dnssd->module, "DNSServiceRegister"); + dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)dlsym(dnssd->module, "DNSServiceRefDeallocate"); + dnssd->TXTRecordCreate = (TXTRecordCreate_t)dlsym(dnssd->module, "TXTRecordCreate"); + dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)dlsym(dnssd->module, "TXTRecordSetValue"); + dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)dlsym(dnssd->module, "TXTRecordGetLength"); + dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)dlsym(dnssd->module, "TXTRecordGetBytesPtr"); + dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)dlsym(dnssd->module, "TXTRecordDeallocate"); + + if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate || + !dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr || + !dnssd->TXTRecordDeallocate) { + if (error) *error = DNSSD_ERROR_PROCNOTFOUND; + dlclose(dnssd->module); + free(dnssd); + return NULL; + } +#else + dnssd->DNSServiceRegister = &DNSServiceRegister; + dnssd->DNSServiceRefDeallocate = &DNSServiceRefDeallocate; + dnssd->TXTRecordCreate = &TXTRecordCreate; + dnssd->TXTRecordSetValue = &TXTRecordSetValue; + dnssd->TXTRecordGetLength = &TXTRecordGetLength; + dnssd->TXTRecordGetBytesPtr = &TXTRecordGetBytesPtr; + dnssd->TXTRecordDeallocate = &TXTRecordDeallocate; +#endif + + dnssd->name_len = name_len; + dnssd->name = calloc(1, name_len + 1); + if (!dnssd->name) { + free(dnssd); + if (error) *error = DNSSD_ERROR_OUTOFMEM; + return NULL; + } + memcpy(dnssd->name, name, name_len); + + dnssd->hw_addr_len = hw_addr_len; + dnssd->hw_addr = calloc(1, dnssd->hw_addr_len); + if (!dnssd->hw_addr) { + free(dnssd->name); + free(dnssd); + if (error) *error = DNSSD_ERROR_OUTOFMEM; + return NULL; + } + + memcpy(dnssd->hw_addr, hw_addr, hw_addr_len); + + return dnssd; +} + +void +dnssd_destroy(dnssd_t *dnssd) +{ + if (dnssd) { +#ifdef WIN32 + FreeLibrary(dnssd->module); +#elif USE_LIBDL + dlclose(dnssd->module); +#endif + free(dnssd); + } +} + +int +dnssd_register_raop(dnssd_t *dnssd, unsigned short port) +{ + char servname[MAX_SERVNAME]; + DNSServiceErrorType retval; + char features[22]; + + assert(dnssd); + + snprintf(features, sizeof(features), "0x%X,0x%X", dnssd->features1, dnssd->features2); + + dnssd->TXTRecordCreate(&dnssd->raop_record, 0, NULL); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "ch", strlen(RAOP_CH), RAOP_CH); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "cn", strlen(RAOP_CN), RAOP_CN); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "da", strlen(RAOP_DA), RAOP_DA); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "et", strlen(RAOP_ET), RAOP_ET); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "vv", strlen(RAOP_VV), RAOP_VV); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "ft", strlen(features), features); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "am", strlen(GLOBAL_MODEL), GLOBAL_MODEL); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "md", strlen(RAOP_MD), RAOP_MD); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "rhd", strlen(RAOP_RHD), RAOP_RHD); + if (dnssd->require_pw) { + dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("true"), "true"); + } else { + dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("false"), "false"); + } + dnssd->TXTRecordSetValue(&dnssd->raop_record, "sr", strlen(RAOP_SR), RAOP_SR); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "ss", strlen(RAOP_SS), RAOP_SS); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "sv", strlen(RAOP_SV), RAOP_SV); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "tp", strlen(RAOP_TP), RAOP_TP); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "txtvers", strlen(RAOP_TXTVERS), RAOP_TXTVERS); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", strlen(RAOP_SF), RAOP_SF); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "vs", strlen(RAOP_VS), RAOP_VS); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "vn", strlen(RAOP_VN), RAOP_VN); + dnssd->TXTRecordSetValue(&dnssd->raop_record, "pk", strlen(dnssd->pk), dnssd->pk); + + /* Convert hardware address to string */ + if (utils_hwaddr_raop(servname, sizeof(servname), dnssd->hw_addr, dnssd->hw_addr_len) < 0) { + /* FIXME: handle better */ + return -1; + } + + /* Check that we have bytes for 'hw@name' format */ + if (sizeof(servname) < strlen(servname) + 1 + dnssd->name_len + 1) { + /* FIXME: handle better */ + return -2; + } + + strncat(servname, "@", sizeof(servname)-strlen(servname)-1); + strncat(servname, dnssd->name, sizeof(servname)-strlen(servname)-1); + + /* Register the service */ + retval = dnssd->DNSServiceRegister(&dnssd->raop_service, 0, 0, + servname, "_raop._tcp", + NULL, NULL, + htons(port), + dnssd->TXTRecordGetLength(&dnssd->raop_record), + dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record), + NULL, NULL); + + return (int) retval; /* error codes are listed in Apple's dns_sd.h */ +} + +int +dnssd_register_airplay(dnssd_t *dnssd, unsigned short port) +{ + char device_id[3 * MAX_HWADDR_LEN]; + DNSServiceErrorType retval; + char features[22]; + + assert(dnssd); + + snprintf(features, sizeof(features), "0x%X,0x%X", dnssd->features1, dnssd->features2); + + /* Convert hardware address to string */ + if (utils_hwaddr_airplay(device_id, sizeof(device_id), dnssd->hw_addr, dnssd->hw_addr_len) < 0) { + /* FIXME: handle better */ + return -1; + } + + + dnssd->TXTRecordCreate(&dnssd->airplay_record, 0, NULL); + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "deviceid", strlen(device_id), device_id); + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "features", strlen(features), features); + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", strlen(AIRPLAY_FLAGS), AIRPLAY_FLAGS); + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL); + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pk", strlen(dnssd->pk), dnssd->pk); + if (dnssd->require_pw) { + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("true"), "true"); + } else { + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pw", strlen("false"), "false"); + } + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pi", strlen(AIRPLAY_PI), AIRPLAY_PI); + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS); + dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV); + + /* Register the service */ + retval = dnssd->DNSServiceRegister(&dnssd->airplay_service, 0, 0, + dnssd->name, "_airplay._tcp", + NULL, NULL, + htons(port), + dnssd->TXTRecordGetLength(&dnssd->airplay_record), + dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record), + NULL, NULL); + + return (int) retval; /* error codes are listed in Apple's dns_sd.h */ +} + +const char * +dnssd_get_airplay_txt(dnssd_t *dnssd, int *length) +{ + *length = dnssd->TXTRecordGetLength(&dnssd->airplay_record); + return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record); +} + +const char * +dnssd_get_name(dnssd_t *dnssd, int *length) +{ + *length = dnssd->name_len; + return dnssd->name; +} + +const char * +dnssd_get_hw_addr(dnssd_t *dnssd, int *length) +{ + *length = dnssd->hw_addr_len; + return dnssd->hw_addr; +} + +void +dnssd_unregister_raop(dnssd_t *dnssd) +{ + assert(dnssd); + + if (!dnssd->raop_service) { + return; + } + + /* Deallocate TXT record */ + dnssd->TXTRecordDeallocate(&dnssd->raop_record); + + dnssd->DNSServiceRefDeallocate(dnssd->raop_service); + dnssd->raop_service = NULL; + + if (dnssd->airplay_service == NULL) { + free(dnssd->name); + free(dnssd->hw_addr); + } +} + +void +dnssd_unregister_airplay(dnssd_t *dnssd) +{ + assert(dnssd); + + if (!dnssd->airplay_service) { + return; + } + + /* Deallocate TXT record */ + dnssd->TXTRecordDeallocate(&dnssd->airplay_record); + + dnssd->DNSServiceRefDeallocate(dnssd->airplay_service); + dnssd->airplay_service = NULL; + + if (dnssd->raop_service == NULL) { + free(dnssd->name); + free(dnssd->hw_addr); + } +} + +uint64_t dnssd_get_airplay_features(dnssd_t *dnssd) { + uint64_t features = ((uint64_t) dnssd->features2) << 32; + features += (uint64_t) dnssd->features1; + return features; +} + +void dnssd_set_pk(dnssd_t *dnssd, char * pk_str) { + dnssd->pk = pk_str; +} + +void dnssd_set_airplay_features(dnssd_t *dnssd, int bit, int val) { + uint32_t mask; + uint32_t *features; + if (bit < 0 || bit > 63) return; + if (val < 0 || val > 1) return; + if (bit >= 32) { + mask = 0x1 << (bit - 32); + features = &(dnssd->features2); + } else { + mask = 0x1 << bit; + features = &(dnssd->features1); + } + if (val) { + *features = *features | mask; + } else { + *features = *features & ~mask; + } +} diff --git a/lib/dnssd.h b/lib/dnssd.h new file mode 100644 index 0000000..1f83210 --- /dev/null +++ b/lib/dnssd.h @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef DNSSD_H +#define DNSSD_H +#include + +#if defined(WIN32) && defined(DLL_EXPORT) +# define DNSSD_API __declspec(dllexport) +#else +# define DNSSD_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define DNSSD_ERROR_NOERROR 0 +#define DNSSD_ERROR_HWADDRLEN 1 +#define DNSSD_ERROR_OUTOFMEM 2 +#define DNSSD_ERROR_LIBNOTFOUND 3 +#define DNSSD_ERROR_PROCNOTFOUND 4 +#define DNSSD_ERROR_BADFEATURES 5 + +typedef struct dnssd_s dnssd_t; + + DNSSD_API dnssd_t *dnssd_init(const char *name, int name_len, const char *hw_addr, int hw_addr_len, int *error, int require_pw); + +DNSSD_API int dnssd_register_raop(dnssd_t *dnssd, unsigned short port); +DNSSD_API int dnssd_register_airplay(dnssd_t *dnssd, unsigned short port); + +DNSSD_API void dnssd_unregister_raop(dnssd_t *dnssd); +DNSSD_API void dnssd_unregister_airplay(dnssd_t *dnssd); + +DNSSD_API const char *dnssd_get_airplay_txt(dnssd_t *dnssd, int *length); +DNSSD_API const char *dnssd_get_name(dnssd_t *dnssd, int *length); +DNSSD_API const char *dnssd_get_hw_addr(dnssd_t *dnssd, int *length); +DNSSD_API void dnssd_set_airplay_features(dnssd_t *dnssd, int bit, int val); +DNSSD_API uint64_t dnssd_get_airplay_features(dnssd_t *dnssd); +DNSSD_API void dnssd_set_pk(dnssd_t *dnssd, char * pk_str); + +DNSSD_API void dnssd_destroy(dnssd_t *dnssd); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/dnssdint.h b/lib/dnssdint.h new file mode 100644 index 0000000..6a457d0 --- /dev/null +++ b/lib/dnssdint.h @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2022 + */ + +#ifndef DNSSDINT_H +#define DNSSDINT_H + +#include "global.h" + +/* the previous behavior of announcing UxPlay with a fixed public key PK + * can be restored by uncommenting the following line */ +//#define PK "b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7" + +#define RAOP_TXTVERS "1" +#define RAOP_CH "2" /* Audio channels: 2 */ +#define RAOP_CN "0,1,2,3" /* Audio codec: PCM, ALAC, AAC, AAC ELD */ +#define RAOP_ET "0,3,5" /* Encryption type: None, FairPlay, FairPlay SAPv2.5 */ +#define RAOP_VV "2" +#define FEATURES_1 "0x5A7FFEE6" /* first 32 bits of features, with bit 27 ("supports legacy pairing") ON */ +//#define FEATURES_1 "0x527FFEE6" /* first 32 bits of features, with bit 27 ("supports legacy pairing") OFF */ +#define FEATURES_2 "0x0" /* second 32 bits of features */ +#define RAOP_RHD "5.6.0.0" +#define RAOP_SF "0x4" +#define RAOP_SV "false" +#define RAOP_DA "true" +#define RAOP_SR "44100" /* Sample rate: 44100 */ +#define RAOP_SS "16" /* Sample size: 16 */ +#define RAOP_VS GLOBAL_VERSION /* defined in global.h */ +#define RAOP_TP "UDP" /* Transport protocol. Possible values: UDP or TCP or TCP,UDP */ +#define RAOP_MD "0,1,2" /* Metadata: text, artwork, progress */ +#define RAOP_VN "65537" + +#define AIRPLAY_SRCVERS GLOBAL_VERSION /*defined in global.h */ +#define AIRPLAY_FLAGS "0x4" +#define AIRPLAY_VV "2" +#define AIRPLAY_PI "2e388006-13ba-4041-9a67-25dd4a43d536" + +#endif diff --git a/lib/fairplay.h b/lib/fairplay.h new file mode 100644 index 0000000..2b7c30f --- /dev/null +++ b/lib/fairplay.h @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2018 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef FAIRPLAY_H +#define FAIRPLAY_H + +#include "logger.h" + +typedef struct fairplay_s fairplay_t; + +fairplay_t *fairplay_init(logger_t *logger); +int fairplay_setup(fairplay_t *fp, const unsigned char req[16], unsigned char res[142]); +int fairplay_handshake(fairplay_t *fp, const unsigned char req[164], unsigned char res[32]); +int fairplay_decrypt(fairplay_t *fp, const unsigned char input[72], unsigned char output[16]); +void fairplay_destroy(fairplay_t *fp); + +#endif diff --git a/lib/fairplay_playfair.c b/lib/fairplay_playfair.c new file mode 100644 index 0000000..8308c77 --- /dev/null +++ b/lib/fairplay_playfair.c @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2018 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include +#include +#include + +#include "fairplay.h" +#include "playfair/playfair.h" + +char reply_message[4][142] = {{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x00,0x0f,0x9f,0x3f,0x9e,0x0a,0x25,0x21,0xdb,0xdf,0x31,0x2a,0xb2,0xbf,0xb2,0x9e,0x8d,0x23,0x2b,0x63,0x76,0xa8,0xc8,0x18,0x70,0x1d,0x22,0xae,0x93,0xd8,0x27,0x37,0xfe,0xaf,0x9d,0xb4,0xfd,0xf4,0x1c,0x2d,0xba,0x9d,0x1f,0x49,0xca,0xaa,0xbf,0x65,0x91,0xac,0x1f,0x7b,0xc6,0xf7,0xe0,0x66,0x3d,0x21,0xaf,0xe0,0x15,0x65,0x95,0x3e,0xab,0x81,0xf4,0x18,0xce,0xed,0x09,0x5a,0xdb,0x7c,0x3d,0x0e,0x25,0x49,0x09,0xa7,0x98,0x31,0xd4,0x9c,0x39,0x82,0x97,0x34,0x34,0xfa,0xcb,0x42,0xc6,0x3a,0x1c,0xd9,0x11,0xa6,0xfe,0x94,0x1a,0x8a,0x6d,0x4a,0x74,0x3b,0x46,0xc3,0xa7,0x64,0x9e,0x44,0xc7,0x89,0x55,0xe4,0x9d,0x81,0x55,0x00,0x95,0x49,0xc4,0xe2,0xf7,0xa3,0xf6,0xd5,0xba}, + {0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x01,0xcf,0x32,0xa2,0x57,0x14,0xb2,0x52,0x4f,0x8a,0xa0,0xad,0x7a,0xf1,0x64,0xe3,0x7b,0xcf,0x44,0x24,0xe2,0x00,0x04,0x7e,0xfc,0x0a,0xd6,0x7a,0xfc,0xd9,0x5d,0xed,0x1c,0x27,0x30,0xbb,0x59,0x1b,0x96,0x2e,0xd6,0x3a,0x9c,0x4d,0xed,0x88,0xba,0x8f,0xc7,0x8d,0xe6,0x4d,0x91,0xcc,0xfd,0x5c,0x7b,0x56,0xda,0x88,0xe3,0x1f,0x5c,0xce,0xaf,0xc7,0x43,0x19,0x95,0xa0,0x16,0x65,0xa5,0x4e,0x19,0x39,0xd2,0x5b,0x94,0xdb,0x64,0xb9,0xe4,0x5d,0x8d,0x06,0x3e,0x1e,0x6a,0xf0,0x7e,0x96,0x56,0x16,0x2b,0x0e,0xfa,0x40,0x42,0x75,0xea,0x5a,0x44,0xd9,0x59,0x1c,0x72,0x56,0xb9,0xfb,0xe6,0x51,0x38,0x98,0xb8,0x02,0x27,0x72,0x19,0x88,0x57,0x16,0x50,0x94,0x2a,0xd9,0x46,0x68,0x8a}, + {0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x02,0xc1,0x69,0xa3,0x52,0xee,0xed,0x35,0xb1,0x8c,0xdd,0x9c,0x58,0xd6,0x4f,0x16,0xc1,0x51,0x9a,0x89,0xeb,0x53,0x17,0xbd,0x0d,0x43,0x36,0xcd,0x68,0xf6,0x38,0xff,0x9d,0x01,0x6a,0x5b,0x52,0xb7,0xfa,0x92,0x16,0xb2,0xb6,0x54,0x82,0xc7,0x84,0x44,0x11,0x81,0x21,0xa2,0xc7,0xfe,0xd8,0x3d,0xb7,0x11,0x9e,0x91,0x82,0xaa,0xd7,0xd1,0x8c,0x70,0x63,0xe2,0xa4,0x57,0x55,0x59,0x10,0xaf,0x9e,0x0e,0xfc,0x76,0x34,0x7d,0x16,0x40,0x43,0x80,0x7f,0x58,0x1e,0xe4,0xfb,0xe4,0x2c,0xa9,0xde,0xdc,0x1b,0x5e,0xb2,0xa3,0xaa,0x3d,0x2e,0xcd,0x59,0xe7,0xee,0xe7,0x0b,0x36,0x29,0xf2,0x2a,0xfd,0x16,0x1d,0x87,0x73,0x53,0xdd,0xb9,0x9a,0xdc,0x8e,0x07,0x00,0x6e,0x56,0xf8,0x50,0xce}, + {0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x03,0x90,0x01,0xe1,0x72,0x7e,0x0f,0x57,0xf9,0xf5,0x88,0x0d,0xb1,0x04,0xa6,0x25,0x7a,0x23,0xf5,0xcf,0xff,0x1a,0xbb,0xe1,0xe9,0x30,0x45,0x25,0x1a,0xfb,0x97,0xeb,0x9f,0xc0,0x01,0x1e,0xbe,0x0f,0x3a,0x81,0xdf,0x5b,0x69,0x1d,0x76,0xac,0xb2,0xf7,0xa5,0xc7,0x08,0xe3,0xd3,0x28,0xf5,0x6b,0xb3,0x9d,0xbd,0xe5,0xf2,0x9c,0x8a,0x17,0xf4,0x81,0x48,0x7e,0x3a,0xe8,0x63,0xc6,0x78,0x32,0x54,0x22,0xe6,0xf7,0x8e,0x16,0x6d,0x18,0xaa,0x7f,0xd6,0x36,0x25,0x8b,0xce,0x28,0x72,0x6f,0x66,0x1f,0x73,0x88,0x93,0xce,0x44,0x31,0x1e,0x4b,0xe6,0xc0,0x53,0x51,0x93,0xe5,0xef,0x72,0xe8,0x68,0x62,0x33,0x72,0x9c,0x22,0x7d,0x82,0x0c,0x99,0x94,0x45,0xd8,0x92,0x46,0xc8,0xc3,0x59}}; + +char fp_header[] = {0x46, 0x50, 0x4c, 0x59, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14}; + +struct fairplay_s { + logger_t *logger; + + unsigned char keymsg[164]; + unsigned int keymsglen; +}; + +fairplay_t * +fairplay_init(logger_t *logger) +{ + fairplay_t *fp; + + fp = calloc(1, sizeof(fairplay_t)); + if (!fp) { + return NULL; + } + fp->logger = logger; + + return fp; +} + +int +fairplay_setup(fairplay_t *fp, const unsigned char req[16], unsigned char res[142]) +{ + int mode; + + assert(fp); + + if (req[4] != 0x03) { + /* Unsupported fairplay version */ + return -1; + } + + mode = req[14]; + memcpy(res, reply_message[mode], 142); + fp->keymsglen = 0; + return 0; +} + +int +fairplay_handshake(fairplay_t *fp, const unsigned char req[164], unsigned char res[32]) +{ + assert(fp); + + if (req[4] != 0x03) { + /* Unsupported fairplay version */ + return -1; + } + + memcpy(fp->keymsg, req, 164); + fp->keymsglen = 164; + + memcpy(res, fp_header, 12); + memcpy(res + 12, req + 144, 20); + return 0; +} + +int +fairplay_decrypt(fairplay_t *fp, const unsigned char input[72], unsigned char output[16]) +{ + if (fp->keymsglen != 164) { + return -1; + } + + playfair_decrypt(fp->keymsg, (unsigned char *) input, output); + return 0; +} + +void +fairplay_destroy(fairplay_t *fp) +{ + free(fp); +} diff --git a/lib/fcup_request.h b/lib/fcup_request.h new file mode 100644 index 0000000..166ffa9 --- /dev/null +++ b/lib/fcup_request.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 fduncanh + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +/* this file is part of raop.c via http_handlers.h and should not be included in any other file */ + + +//produces the fcup request plist in xml format as a null-terminated string +char *create_fcup_request(const char *url, int request_id, const char *client_session_id, int *datalen) { + char *plist_xml = NULL; + /* values taken from apsdk-public; */ + /* these seem to be arbitrary choices */ + const int sessionID = 1; + const int FCUP_Response_ClientInfo = 1; + const int FCUP_Response_ClientRef = 40030004; + + /* taken from a working AppleTV? */ + const char User_Agent[] = "AppleCoreMedia/1.0.0.11B554a (Apple TV; U; CPU OS 7_0_4 like Mac OS X; en_us"; + + plist_t req_root_node = plist_new_dict(); + + plist_t session_id_node = plist_new_uint((int64_t) sessionID); + plist_dict_set_item(req_root_node, "sessionID", session_id_node); + plist_t type_node = plist_new_string("unhandledURLRequest"); + plist_dict_set_item(req_root_node, "type", type_node); + + plist_t fcup_request_node = plist_new_dict(); + + plist_t client_info_node = plist_new_uint(FCUP_Response_ClientInfo); + plist_dict_set_item(fcup_request_node, "FCUP_Response_ClientInfo", client_info_node); + plist_t client_ref_node = plist_new_uint((int64_t) FCUP_Response_ClientRef); + plist_dict_set_item(fcup_request_node, "FCUP_Response_ClientRef", client_ref_node); + plist_t request_id_node = plist_new_uint((int64_t) request_id); + plist_dict_set_item(fcup_request_node, "FCUP_Response_RequestID", request_id_node); + plist_t url_node = plist_new_string(url); + plist_dict_set_item(fcup_request_node, "FCUP_Response_URL", url_node); + plist_t session_id1_node = plist_new_uint((int64_t) sessionID); + plist_dict_set_item(fcup_request_node, "sessionID", session_id1_node); + + plist_t fcup_response_header_node = plist_new_dict(); + plist_t playback_session_id_node = plist_new_string(client_session_id); + plist_dict_set_item(fcup_response_header_node, "X-Playback-Session-Id", playback_session_id_node); + plist_t user_agent_node = plist_new_string(User_Agent); + plist_dict_set_item(fcup_response_header_node, "User-Agent", user_agent_node); + + plist_dict_set_item(fcup_request_node, "FCUP_Response_Headers", fcup_response_header_node); + plist_dict_set_item(req_root_node, "request", fcup_request_node); + + uint32_t uint_val; + + plist_to_xml(req_root_node, &plist_xml, &uint_val); + *datalen = (int) uint_val; + plist_free(req_root_node); + assert(plist_xml[*datalen] == '\0'); + return plist_xml; //needs to be freed after use +} + +int fcup_request(void *conn_opaque, const char *media_url, const char *client_session_id, int request_id) { + + raop_conn_t *conn = (raop_conn_t *) conn_opaque; + int datalen = 0; + int requestlen; + + int socket_fd = httpd_get_connection_socket_by_type(conn->raop->httpd, CONNECTION_TYPE_PTTH, 1); + + logger_log(conn->raop->logger, LOGGER_DEBUG, "fcup_request send socket = %d", socket_fd); + + /* create xml plist request data */ + char *plist_xml = create_fcup_request(media_url, request_id, client_session_id, &datalen); + + /* use http_response tools for creating the reverse http request */ + http_response_t *request = http_response_create(); + http_response_reverse_request_init(request, "POST", "/event", "HTTP/1.1"); + http_response_add_header(request, "X-Apple-Session-ID", client_session_id); + http_response_add_header(request, "Content-Type", "text/x-apple-plist+xml"); + http_response_finish(request, plist_xml, datalen); + + free(plist_xml); + + const char *http_request = http_response_get_data(request, &requestlen); + int send_len = send(socket_fd, http_request, requestlen, 0); + if (send_len < 0) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(conn->raop->logger, LOGGER_ERR, "fcup_request: send error %d:%s\n", + sock_err, strerror(sock_err)); + http_response_destroy(request); + /* shut down connection? */ + return -1; + } + + if (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG) { + char *request_str = utils_data_to_text(http_request, requestlen); + logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s", request_str); + free (request_str); + } + http_response_destroy(request); + logger_log(conn->raop->logger, LOGGER_DEBUG,"fcup_request: send sent Request of %d bytes from socket %d\n", + send_len, socket_fd); + return 0; +} diff --git a/lib/global.h b/lib/global.h new file mode 100644 index 0000000..03f2dcb --- /dev/null +++ b/lib/global.h @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2021-2022 + */ + +#ifndef GLOBAL_H +#define GLOBAL_H + +#define GLOBAL_MODEL "AppleTV3,2" +#define GLOBAL_VERSION "220.68" + +/* use old protocol for audio AES key if client's User-Agent string is contained in these strings */ +/* replace xxx by any new User-Agent string as needed */ +#define OLD_PROTOCOL_CLIENT_USER_AGENT_LIST "AirMyPC/2.0;xxx" + +#define DECRYPTION_TEST 0 /* set to 1 or 2 to examine audio decryption */ + +#define MAX_HWADDR_LEN 6 + +#endif diff --git a/lib/http_handlers.h b/lib/http_handlers.h new file mode 100644 index 0000000..a47f482 --- /dev/null +++ b/lib/http_handlers.h @@ -0,0 +1,997 @@ +/** + * Copyright (c) 2024 fduncanh + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +/* this file is part of raop.c and should not be included in any other file */ + +#include "airplay_video.h" +#include "fcup_request.h" + +static void +http_handler_server_info(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + + assert(conn->raop->dnssd); + int hw_addr_raw_len = 0; + const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len); + + char *hw_addr = calloc(1, 3 * hw_addr_raw_len); + //int hw_addr_len = + utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len); + + plist_t r_node = plist_new_dict(); + + /* first 12 AirPlay features bits (R to L): 0x27F = 0010 0111 1111 + * Only bits 0-6 and bit 9 are set: + * 0. video supported + * 1. photo supported + * 2. video protected wirh FairPlay DRM + * 3. volume control supported for video + * 4. HLS supported + * 5. slideshow supported + * 6. (unknown) + * 9. audio supported. + */ + plist_t features_node = plist_new_uint(0x27F); + plist_dict_set_item(r_node, "features", features_node); + + plist_t mac_address_node = plist_new_string(hw_addr); + plist_dict_set_item(r_node, "macAddress", mac_address_node); + + plist_t model_node = plist_new_string(GLOBAL_MODEL); + plist_dict_set_item(r_node, "model", model_node); + + plist_t os_build_node = plist_new_string("12B435"); + plist_dict_set_item(r_node, "osBuildVersion", os_build_node); + + plist_t protovers_node = plist_new_string("1.0"); + plist_dict_set_item(r_node, "protovers", protovers_node); + + plist_t source_version_node = plist_new_string(GLOBAL_VERSION); + plist_dict_set_item(r_node, "srcvers", source_version_node); + + plist_t vv_node = plist_new_uint(strtol(AIRPLAY_VV, NULL, 10)); + plist_dict_set_item(r_node, "vv", vv_node); + + plist_t device_id_node = plist_new_string(hw_addr); + plist_dict_set_item(r_node, "deviceid", device_id_node); + + plist_to_xml(r_node, response_data, (uint32_t *) response_datalen); + + //assert(*response_datalen == strlen(*response_data)); + + /* last character (at *response_data[response_datalen - 1]) is 0x0a = '\n' + * (*response_data[response_datalen] is '\0'). + * apsdk removes the last "\n" by overwriting it with '\0', and reducing response_datalen by 1. + * TODO: check if this is necessary */ + + plist_free(r_node); + http_response_add_header(response, "Content-Type", "text/x-apple-plist+xml"); + free(hw_addr); + + /* initialize the airplay video service */ + const char *session_id = http_request_get_header(request, "X-Apple-Session-ID"); + + airplay_video_service_init(conn->raop, conn->raop->port, session_id); + +} + +static void +http_handler_scrub(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + const char *url = http_request_get_url(request); + const char *data = strstr(url, "?"); + float scrub_position = 0.0f; + if (data) { + data++; + const char *position = strstr(data, "=") + 1; + char *end; + double value = strtod(position, &end); + if (end && end != position) { + scrub_position = (float) value; + logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_scrub: got position = %.6f", + scrub_position); + } + } + logger_log(conn->raop->logger, LOGGER_DEBUG, "**********************SCRUB %f ***********************",scrub_position); + conn->raop->callbacks.on_video_scrub(conn->raop->callbacks.cls, scrub_position); +} + +static void +http_handler_rate(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + + const char *url = http_request_get_url(request); + const char *data = strstr(url, "?"); + float rate_value = 0.0f; + if (data) { + data++; + const char *rate = strstr(data, "=") + 1; + char *end; + float value = strtof(rate, &end); + if (end && end != rate) { + rate_value = value; + logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_rate: got rate = %.6f", rate_value); + } + } + conn->raop->callbacks.on_video_rate(conn->raop->callbacks.cls, rate_value); +} + +static void +http_handler_stop(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + logger_log(conn->raop->logger, LOGGER_INFO, "client HTTP request POST stop"); + + conn->raop->callbacks.on_video_stop(conn->raop->callbacks.cls); +} + +/* handles PUT /setProperty http requests from Client to Server */ + +static void +http_handler_set_property(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + + const char *url = http_request_get_url(request); + const char *property = url + strlen("/setProperty?"); + logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_set_property: %s", property); + + /* actionAtItemEnd: values: + 0: advance (advance to next item, if there is one) + 1: pause (pause playing) + 2: none (do nothing) + + reverseEndTime (only used when rate < 0) time at which reverse playback ends + forwardEndTime (only used when rate > 0) time at which reverse playback ends + */ + + if (!strcmp(property, "reverseEndTime") || + !strcmp(property, "forwardEndTime") || + !strcmp(property, "actionAtItemEnd")) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "property %s is known but unhandled", property); + + plist_t errResponse = plist_new_dict(); + plist_t errCode = plist_new_uint(0); + plist_dict_set_item(errResponse, "errorCode", errCode); + plist_to_xml(errResponse, response_data, (uint32_t *) response_datalen); + plist_free(errResponse); + http_response_add_header(response, "Content-Type", "text/x-apple-plist+xml"); + } else { + logger_log(conn->raop->logger, LOGGER_DEBUG, "property %s is unknown, unhandled", property); + http_response_add_header(response, "Content-Length", "0"); + } +} + +/* handles GET /getProperty http requests from Client to Server. (not implemented) */ + +static void +http_handler_get_property(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + const char *url = http_request_get_url(request); + const char *property = url + strlen("getProperty?"); + logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_get_property: %s (unhandled)", property); +} + +/* this request (for a variant FairPlay decryption) cannot be handled by UxPlay */ +static void +http_handler_fpsetup2(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + logger_log(conn->raop->logger, LOGGER_WARNING, "client HTTP request POST fp-setup2 is unhandled"); + http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); + int req_datalen; + const unsigned char *req_data = (unsigned char *) http_request_get_data(request, &req_datalen); + logger_log(conn->raop->logger, LOGGER_ERR, "only FairPlay version 0x03 is implemented, version is 0x%2.2x", + req_data[4]); + http_response_init(response, "HTTP/1.1", 421, "Misdirected Request"); +} + +// called by http_handler_playback_info while preparing response to a GET /playback_info request from the client. + +typedef struct time_range_s { + double start; + double duration; +} time_range_t; + +void time_range_to_plist(void *time_ranges, const int n_time_ranges, + plist_t time_ranges_node) { + time_range_t *tr = (time_range_t *) time_ranges; + for (int i = 0 ; i < n_time_ranges; i++) { + plist_t time_range_node = plist_new_dict(); + plist_t duration_node = plist_new_real(tr[i].duration); + plist_dict_set_item(time_range_node, "duration", duration_node); + plist_t start_node = plist_new_real(tr[i].start); + plist_dict_set_item(time_range_node, "start", start_node); + plist_array_append_item(time_ranges_node, time_range_node); + } +} + +// called by http_handler_playback_info while preparing response to a GET /playback_info request from the client. + +int create_playback_info_plist_xml(playback_info_t *playback_info, char **plist_xml) { + + plist_t res_root_node = plist_new_dict(); + + plist_t duration_node = plist_new_real(playback_info->duration); + plist_dict_set_item(res_root_node, "duration", duration_node); + + plist_t position_node = plist_new_real(playback_info->position); + plist_dict_set_item(res_root_node, "position", position_node); + + plist_t rate_node = plist_new_real(playback_info->rate); + plist_dict_set_item(res_root_node, "rate", rate_node); + + /* should these be int or bool? */ + plist_t ready_to_play_node = plist_new_uint(playback_info->ready_to_play); + plist_dict_set_item(res_root_node, "readyToPlay", ready_to_play_node); + + plist_t playback_buffer_empty_node = plist_new_uint(playback_info->playback_buffer_empty); + plist_dict_set_item(res_root_node, "playbackBufferEmpty", playback_buffer_empty_node); + + plist_t playback_buffer_full_node = plist_new_uint(playback_info->playback_buffer_full); + plist_dict_set_item(res_root_node, "playbackBufferFull", playback_buffer_full_node); + + plist_t playback_likely_to_keep_up_node = plist_new_uint(playback_info->playback_likely_to_keep_up); + plist_dict_set_item(res_root_node, "playbackLikelyToKeepUp", playback_likely_to_keep_up_node); + + plist_t loaded_time_ranges_node = plist_new_array(); + time_range_to_plist(playback_info->loadedTimeRanges, playback_info->num_loaded_time_ranges, + loaded_time_ranges_node); + plist_dict_set_item(res_root_node, "loadedTimeRanges", loaded_time_ranges_node); + + plist_t seekable_time_ranges_node = plist_new_array(); + time_range_to_plist(playback_info->seekableTimeRanges, playback_info->num_seekable_time_ranges, + seekable_time_ranges_node); + plist_dict_set_item(res_root_node, "seekableTimeRanges", seekable_time_ranges_node); + + int len; + plist_to_xml(res_root_node, plist_xml, (uint32_t *) &len); + /* plist_xml is null-terminated, last character is '/n' */ + + plist_free(res_root_node); + + return len; +} + + +/* this handles requests from the Client for "Playback information" while the Media is playing on the + Media Player. (The Server gets this information by monitoring the Media Player). The Client could use + the information to e.g. update the slider it shows with progress to the player (0%-100%). + It does not affect playing of the Media*/ + +static void +http_handler_playback_info(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_playback_info"); + //const char *session_id = http_request_get_header(request, "X-Apple-Session-ID"); + playback_info_t playback_info; + + playback_info.stallcount = 0; + playback_info.ready_to_play = true; // ???; + playback_info.playback_buffer_empty = false; // maybe need to get this from playbin + playback_info.playback_buffer_full = true; + playback_info.playback_likely_to_keep_up = true; + + conn->raop->callbacks.on_video_acquire_playback_info(conn->raop->callbacks.cls, &playback_info); + if (playback_info.duration == -1.0) { + /* video has finished, reset */ + logger_log(conn->raop->logger, LOGGER_DEBUG, "playback_info not available (finishing)"); + //httpd_remove_known_connections(conn->raop->httpd); + http_response_set_disconnect(response,1); + conn->raop->callbacks.video_reset(conn->raop->callbacks.cls); + return; + } else if (playback_info.position == -1.0) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "playback_info not available"); + return; + } + + playback_info.num_loaded_time_ranges = 1; + time_range_t time_ranges_loaded[1]; + time_ranges_loaded[0].start = playback_info.position; + time_ranges_loaded[0].duration = playback_info.duration - playback_info.position; + playback_info.loadedTimeRanges = (void *) &time_ranges_loaded; + + playback_info.num_seekable_time_ranges = 1; + time_range_t time_ranges_seekable[1]; + time_ranges_seekable[0].start = 0.0; + time_ranges_seekable[0].duration = playback_info.position; + playback_info.seekableTimeRanges = (void *) &time_ranges_seekable; + + *response_datalen = create_playback_info_plist_xml(&playback_info, response_data); + http_response_add_header(response, "Content-Type", "text/x-apple-plist+xml"); +} + +/* this handles the POST /reverse request from Client to Server on a AirPlay http channel to "Upgrade" + to "PTTH/1.0" Reverse HTTP protocol proposed in 2009 Internet-Draft + + https://datatracker.ietf.org/doc/id/draft-lentczner-rhttp-00.txt . + + After the Upgrade the channel becomes a reverse http "AirPlay (reversed)" channel for + http requests from Server to Client. + */ + +static void +http_handler_reverse(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + + /* get http socket for send */ + int socket_fd = httpd_get_connection_socket (conn->raop->httpd, (void *) conn); + if (socket_fd < 0) { + logger_log(conn->raop->logger, LOGGER_ERR, "fcup_request failed to retrieve socket_fd from httpd"); + /* shut down connection? */ + } + + const char *purpose = http_request_get_header(request, "X-Apple-Purpose"); + const char *connection = http_request_get_header(request, "Connection"); + const char *upgrade = http_request_get_header(request, "Upgrade"); + logger_log(conn->raop->logger, LOGGER_INFO, "client requested reverse connection: %s; purpose: %s \"%s\"", + connection, upgrade, purpose); + + httpd_set_connection_type(conn->raop->httpd, (void *) conn, CONNECTION_TYPE_PTTH); + int type_PTTH = httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_PTTH); + + if (type_PTTH == 1) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "will use socket %d for %s connections", socket_fd, purpose); + http_response_init(response, "HTTP/1.1", 101, "Switching Protocols"); + http_response_add_header(response, "Connection", "Upgrade"); + http_response_add_header(response, "Upgrade", "PTTH/1.0"); + + } else { + logger_log(conn->raop->logger, LOGGER_ERR, "multiple TPPH connections (%d) are forbidden", type_PTTH ); + } +} + +/* this copies a Media Playlist into a null-terminated string. If it has the "#YT-EXT-CONDENSED-URI" + header, it is also expanded into the full Media Playlist format */ + +char *adjust_yt_condensed_playlist(const char *media_playlist) { + /* expands a YT-EXT_CONDENSED-URL media playlist into a full media playlist + * returns a pointer to the expanded playlist, WHICH MUST BE FREED AFTER USE */ + + const char *base_uri_begin; + const char *params_begin; + const char *prefix_begin; + size_t base_uri_len; + size_t params_len; + size_t prefix_len; + const char* ptr = strstr(media_playlist, "#EXTM3U\n"); + + ptr += strlen("#EXTM3U\n"); + assert(ptr); + if (strncmp(ptr, "#YT-EXT-CONDENSED-URL", strlen("#YT-EXT-CONDENSED-URL"))) { + size_t len = strlen(media_playlist); + char * playlist_copy = (char *) malloc(len + 1); + memcpy(playlist_copy, media_playlist, len); + playlist_copy[len] = '\0'; + return playlist_copy; + } + ptr = strstr(ptr, "BASE-URI="); + base_uri_begin = strchr(ptr, '"'); + base_uri_begin++; + ptr = strchr(base_uri_begin, '"'); + base_uri_len = ptr - base_uri_begin; + char *base_uri = (char *) calloc(base_uri_len + 1, sizeof(char)); + assert(base_uri); + memcpy(base_uri, base_uri_begin, base_uri_len); //must free + + ptr = strstr(ptr, "PARAMS="); + params_begin = strchr(ptr, '"'); + params_begin++; + ptr = strchr(params_begin,'"'); + params_len = ptr - params_begin; + char *params = (char *) calloc(params_len + 1, sizeof(char)); + assert(params); + memcpy(params, params_begin, params_len); //must free + + ptr = strstr(ptr, "PREFIX="); + prefix_begin = strchr(ptr, '"'); + prefix_begin++; + ptr = strchr(prefix_begin,'"'); + prefix_len = ptr - prefix_begin; + char *prefix = (char *) calloc(prefix_len + 1, sizeof(char)); + assert(prefix); + memcpy(prefix, prefix_begin, prefix_len); //must free + + /* expand params */ + int nparams = 0; + int *params_size = NULL; + const char **params_start = NULL; + if (strlen(params)) { + nparams = 1; + char * comma = strchr(params, ','); + while (comma) { + nparams++; + comma++; + comma = strchr(comma, ','); + } + params_start = (const char **) calloc(nparams, sizeof(char *)); //must free + params_size = (int *) calloc(nparams, sizeof(int)); //must free + ptr = params; + for (int i = 0; i < nparams; i++) { + comma = strchr(ptr, ','); + params_start[i] = ptr; + if (comma) { + params_size[i] = (int) (comma - ptr); + ptr = comma; + ptr++; + } else { + params_size[i] = (int) (params + params_len - ptr); + break; + } + } + } + + int count = 0; + ptr = strstr(media_playlist, "#EXTINF"); + while (ptr) { + count++; + ptr = strstr(++ptr, "#EXTINF"); + } + + size_t old_size = strlen(media_playlist); + size_t new_size = old_size; + new_size += count * (base_uri_len + params_len); + + char * new_playlist = (char *) calloc( new_size + 100, sizeof(char)); + const char *old_pos = media_playlist; + char *new_pos = new_playlist; + ptr = old_pos; + ptr = strstr(old_pos, "#EXTINF:"); + size_t len = ptr - old_pos; + /* copy header section before chunks */ + memcpy(new_pos, old_pos, len); + old_pos += len; + new_pos += len; + int counter = 0; + while (ptr) { + counter++; + /* for each chunk */ + const char *end = NULL; + char *start = strstr(ptr, prefix); + len = start - ptr; + /* copy first line of chunk entry */ + memcpy(new_pos, old_pos, len); + old_pos += len; + new_pos += len; + + /* copy base uri to replace prefix*/ + memcpy(new_pos, base_uri, base_uri_len); + new_pos += base_uri_len; + old_pos += prefix_len; + ptr = strstr(old_pos, "#EXTINF:"); + + /* insert the PARAMS separators on the slices line */ + end = old_pos; + int last = nparams - 1; + for (int i = 0; i < nparams; i++) { + if (i != last) { + end = strchr(end, '/'); + } else { + end = strstr(end, "#EXT"); /* the next line starts with either #EXTINF (usually) or #EXT-X-ENDLIST (at last chunk)*/ + } + *new_pos = '/'; + new_pos++; + memcpy(new_pos, params_start[i], params_size[i]); + new_pos += params_size[i]; + *new_pos = '/'; + new_pos++; + + len = end - old_pos; + end++; + + memcpy (new_pos, old_pos, len); + new_pos += len; + old_pos += len; + if (i != last) { + old_pos++; /* last entry is not followed by "/" separator */ + } + } + } + /* copy tail */ + + len = media_playlist + strlen(media_playlist) - old_pos; + memcpy(new_pos, old_pos, len); + new_pos += len; + old_pos += len; + + new_playlist[new_size] = '\0'; + + free (prefix); + free (base_uri); + free (params); + if (params_size) { + free (params_size); + } + if (params_start) { + free (params_start); + } + + return new_playlist; +} + +/* this adjusts the uri prefixes in the Master Playlist, for sending to the Media Player running on the Server Host */ + +char *adjust_master_playlist (char *fcup_response_data, int fcup_response_datalen, char *uri_prefix, char *uri_local_prefix) { + + size_t uri_prefix_len = strlen(uri_prefix); + size_t uri_local_prefix_len = strlen(uri_local_prefix); + int counter = 0; + char *ptr = strstr(fcup_response_data, uri_prefix); + while (ptr != NULL) { + counter++; + ptr++; + ptr = strstr(ptr, uri_prefix); + } + + size_t len = uri_local_prefix_len - uri_prefix_len; + len *= counter; + len += fcup_response_datalen; + char *new_master = (char *) malloc(len + 1); + *(new_master + len) = '\0'; + char *first = fcup_response_data; + char *new = new_master; + char *last = strstr(first, uri_prefix); + counter = 0; + while (last != NULL) { + counter++; + len = last - first; + memcpy(new, first, len); + first = last + uri_prefix_len; + new += len; + memcpy(new, uri_local_prefix, uri_local_prefix_len); + new += uri_local_prefix_len; + last = strstr(last + uri_prefix_len, uri_prefix); + if (last == NULL) { + len = fcup_response_data + fcup_response_datalen - first; + memcpy(new, first, len); + break; + } + } + return new_master; +} + +/* this parses the Master Playlist to make a table of the Media Playlist uri's that it lists */ + +int create_media_uri_table(const char *url_prefix, const char *master_playlist_data, int datalen, + char ***media_uri_table, int *num_uri) { + char *ptr = strstr(master_playlist_data, url_prefix); + char ** table = NULL; + if (ptr == NULL) { + return -1; + } + int count = 0; + while (ptr != NULL) { + char *end = strstr(ptr, "m3u8"); + if (end == NULL) { + return 1; + } + end += sizeof("m3u8"); + count++; + ptr = strstr(end, url_prefix); + } + table = (char **) calloc(count, sizeof(char *)); + if (!table) { + return -1; + } + for (int i = 0; i < count; i++) { + table[i] = NULL; + } + ptr = strstr(master_playlist_data, url_prefix); + count = 0; + while (ptr != NULL) { + char *end = strstr(ptr, "m3u8"); + char *uri; + if (end == NULL) { + return 0; + } + end += sizeof("m3u8"); + size_t len = end - ptr - 1; + uri = (char *) calloc(len + 1, sizeof(char)); + memcpy(uri , ptr, len); + table[count] = uri; + uri = NULL; + count ++; + ptr = strstr(end, url_prefix); + } + *num_uri = count; + + *media_uri_table = table; + return 0; +} + +/* the POST /action request from Client to Server on the AirPlay http channel follows a POST /event "FCUP Request" + from Server to Client on the reverse http channel, for a HLS playlist (first the Master Playlist, then the Media Playlists + listed in the Master Playlist. The POST /action request contains the playlist requested by the Server in + the preceding "FCUP Request". The FCUP Request sequence continues until all Media Playlists have been obtained by the Server */ + +static void +http_handler_action(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + + bool data_is_plist = false; + plist_t req_root_node = NULL; + uint64_t uint_val; + int request_id = 0; + int fcup_response_statuscode = 0; + bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); + + + const char* session_id = http_request_get_header(request, "X-Apple-Session-ID"); + if (!session_id) { + logger_log(conn->raop->logger, LOGGER_ERR, "Play request had no X-Apple-Session-ID"); + goto post_action_error; + } + const char *apple_session_id = get_apple_session_id(conn->raop->airplay_video); + if (strcmp(session_id, apple_session_id)){ + logger_log(conn->raop->logger, LOGGER_ERR, "X-Apple-Session-ID has changed:\n was:\"%s\"\n now:\"%s\"", + apple_session_id, session_id); + goto post_action_error; + } + + /* verify that this request contains a binary plist*/ + char *header_str = NULL; + http_request_get_header_string(request, &header_str); + logger_log(conn->raop->logger, LOGGER_DEBUG, "request header: %s", header_str); + data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL); + free(header_str); + if (!data_is_plist) { + logger_log(conn->raop->logger, LOGGER_INFO, "POST /action: did not receive expected plist from client"); + goto post_action_error; + } + + /* extract the root_node plist */ + int request_datalen = 0; + const char *request_data = http_request_get_data(request, &request_datalen); + if (request_datalen == 0) { + logger_log(conn->raop->logger, LOGGER_INFO, "POST /action: did not receive expected plist from client"); + goto post_action_error; + } + plist_from_bin(request_data, request_datalen, &req_root_node); + + /* determine type of data */ + plist_t req_type_node = plist_dict_get_item(req_root_node, "type"); + if (!PLIST_IS_STRING(req_type_node)) { + goto post_action_error; + } + + /* three possible types are known */ + char *type = NULL; + int action_type = 0; + plist_get_string_val(req_type_node, &type); + logger_log(conn->raop->logger, LOGGER_DEBUG, "action type is %s", type); + if (strstr(type, "unhandledURLResponse")) { + action_type = 1; + } else if (strstr(type, "playlistInsert")) { + action_type = 2; + } else if (strstr(type, "playlistRemove")) { + action_type = 3; + } + free (type); + + plist_t req_params_node = NULL; + switch (action_type) { + case 1: + goto unhandledURLResponse; + case 2: + logger_log(conn->raop->logger, LOGGER_INFO, "unhandled action type playlistInsert (add new playback)"); + goto finish; + case 3: + logger_log(conn->raop->logger, LOGGER_INFO, "unhandled action type playlistRemove (stop playback)"); + goto finish; + default: + logger_log(conn->raop->logger, LOGGER_INFO, "unknown action type (unhandled)"); + goto finish; + } + + unhandledURLResponse:; + + req_params_node = plist_dict_get_item(req_root_node, "params"); + if (!PLIST_IS_DICT (req_params_node)) { + goto post_action_error; + } + + /* handling type "unhandledURLResponse" (case 1)*/ + uint_val = 0; + int fcup_response_datalen = 0; + + if (logger_debug) { + plist_t plist_fcup_response_statuscode_node = plist_dict_get_item(req_params_node, + "FCUP_Response_StatusCode"); + if (plist_fcup_response_statuscode_node) { + plist_get_uint_val(plist_fcup_response_statuscode_node, &uint_val); + fcup_response_statuscode = (int) uint_val; + uint_val = 0; + logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response_StatusCode = %d", + fcup_response_statuscode); + } + + plist_t plist_fcup_response_requestid_node = plist_dict_get_item(req_params_node, + "FCUP_Response_RequestID"); + if (plist_fcup_response_requestid_node) { + plist_get_uint_val(plist_fcup_response_requestid_node, &uint_val); + request_id = (int) uint_val; + uint_val = 0; + logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response_RequestID = %d", request_id); + } + } + + plist_t plist_fcup_response_url_node = plist_dict_get_item(req_params_node, "FCUP_Response_URL"); + if (!PLIST_IS_STRING(plist_fcup_response_url_node)) { + goto post_action_error; + } + char *fcup_response_url = NULL; + plist_get_string_val(plist_fcup_response_url_node, &fcup_response_url); + if (!fcup_response_url) { + goto post_action_error; + } + logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response_URL = %s", fcup_response_url); + + plist_t plist_fcup_response_data_node = plist_dict_get_item(req_params_node, "FCUP_Response_Data"); + if (!PLIST_IS_DATA(plist_fcup_response_data_node)){ + goto post_action_error; + } + + uint_val = 0; + char *fcup_response_data = NULL; + plist_get_data_val(plist_fcup_response_data_node, &fcup_response_data, &uint_val); + fcup_response_datalen = (int) uint_val; + + if (!fcup_response_data) { + free (fcup_response_url); + goto post_action_error; + } + + if (logger_debug) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "FCUP_Response datalen = %d", fcup_response_datalen); + char *data = malloc(fcup_response_datalen + 1); + memcpy(data, fcup_response_data, fcup_response_datalen); + data[fcup_response_datalen] = '\0'; + logger_log(conn->raop->logger, LOGGER_DEBUG, "begin FCUP Response data:\n%s\nend FCUP Response data",data); + free (data); + } + + + char *ptr = strstr(fcup_response_url, "/master.m3u8"); + if (ptr) { + /* this is a master playlist */ + char *uri_prefix = get_uri_prefix(conn->raop->airplay_video); + char ** media_data_store = NULL; + int num_uri = 0; + + char *uri_local_prefix = get_uri_local_prefix(conn->raop->airplay_video); + char *new_master = adjust_master_playlist (fcup_response_data, fcup_response_datalen, uri_prefix, uri_local_prefix); + store_master_playlist(conn->raop->airplay_video, new_master); + create_media_uri_table(uri_prefix, fcup_response_data, fcup_response_datalen, &media_data_store, &num_uri); + create_media_data_store(conn->raop->airplay_video, media_data_store, num_uri); + num_uri = get_num_media_uri(conn->raop->airplay_video); + set_next_media_uri_id(conn->raop->airplay_video, 0); + } else { + /* this is a media playlist */ + assert(fcup_response_data); + char *playlist = (char *) calloc(fcup_response_datalen + 1, sizeof(char)); + memcpy(playlist, fcup_response_data, fcup_response_datalen); + int uri_num = get_next_media_uri_id(conn->raop->airplay_video); + --uri_num; // (next num is current num + 1) + store_media_data_playlist_by_num(conn->raop->airplay_video, playlist, uri_num); + float duration = 0.0f; + int count = analyze_media_playlist(playlist, &duration); + if (count) { + logger_log(conn->raop->logger, LOGGER_DEBUG, + "\n%s:\nreceived media playlist has %5d chunks, total duration %9.3f secs\n", + fcup_response_url, count, duration); + } + } + + if (fcup_response_data) { + free (fcup_response_data); + } + if (fcup_response_url) { + free (fcup_response_url); + } + + int num_uri = get_num_media_uri(conn->raop->airplay_video); + int uri_num = get_next_media_uri_id(conn->raop->airplay_video); + if (uri_num < num_uri) { + fcup_request((void *) conn, get_media_uri_by_num(conn->raop->airplay_video, uri_num), + apple_session_id, + get_next_FCUP_RequestID(conn->raop->airplay_video)); + set_next_media_uri_id(conn->raop->airplay_video, ++uri_num); + } else { + char * uri_local_prefix = get_uri_local_prefix(conn->raop->airplay_video); + conn->raop->callbacks.on_video_play(conn->raop->callbacks.cls, + strcat(uri_local_prefix, "/master.m3u8"), + get_start_position_seconds(conn->raop->airplay_video)); + } + + finish: + plist_free(req_root_node); + return; + + post_action_error:; + http_response_init(response, "HTTP/1.1", 400, "Bad Request"); + + if (req_root_node) { + plist_free(req_root_node); + } + +} + +/* The POST /play request from the Client to Server on the AirPlay http channel contains (among other information) + the "Content Location" that specifies the HLS Playlists for the video to be streamed, as well as the video + "start position in seconds". Once this request is received by the Sever, the Server sends a POST /event + "FCUP Request" request to the Client on the reverse http channel, to request the HLS Master Playlist */ + +static void +http_handler_play(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + + char* playback_location = NULL; + plist_t req_root_node = NULL; + float start_position_seconds = 0.0f; + bool data_is_binary_plist = false; + bool data_is_text = false; + bool data_is_octet = false; + + logger_log(conn->raop->logger, LOGGER_DEBUG, "http_handler_play"); + + const char* session_id = http_request_get_header(request, "X-Apple-Session-ID"); + if (!session_id) { + logger_log(conn->raop->logger, LOGGER_ERR, "Play request had no X-Apple-Session-ID"); + goto play_error; + } + const char *apple_session_id = get_apple_session_id(conn->raop->airplay_video); + if (strcmp(session_id, apple_session_id)){ + logger_log(conn->raop->logger, LOGGER_ERR, "X-Apple-Session-ID has changed:\n was:\"%s\"\n now:\"%s\"", + apple_session_id, session_id); + goto play_error; + } + + int request_datalen = -1; + const char *request_data = http_request_get_data(request, &request_datalen); + + if (request_datalen > 0) { + char *header_str = NULL; + http_request_get_header_string(request, &header_str); + logger_log(conn->raop->logger, LOGGER_DEBUG, "request header:\n%s", header_str); + data_is_binary_plist = (strstr(header_str, "x-apple-binary-plist") != NULL); + data_is_text = (strstr(header_str, "text/parameters") != NULL); + data_is_octet = (strstr(header_str, "octet-stream") != NULL); + free (header_str); + } + if (!data_is_text && !data_is_octet && !data_is_binary_plist) { + goto play_error; + } + + if (data_is_text) { + logger_log(conn->raop->logger, LOGGER_ERR, "Play request Content is text (unsupported)"); + goto play_error; + } + + if (data_is_octet) { + logger_log(conn->raop->logger, LOGGER_ERR, "Play request Content is octet-stream (unsupported)"); + goto play_error; + } + + if (data_is_binary_plist) { + plist_from_bin(request_data, request_datalen, &req_root_node); + + plist_t req_uuid_node = plist_dict_get_item(req_root_node, "uuid"); + if (!req_uuid_node) { + goto play_error; + } else { + char* playback_uuid = NULL; + plist_get_string_val(req_uuid_node, &playback_uuid); + set_playback_uuid(conn->raop->airplay_video, playback_uuid); + free (playback_uuid); + } + + plist_t req_content_location_node = plist_dict_get_item(req_root_node, "Content-Location"); + if (!req_content_location_node) { + goto play_error; + } else { + plist_get_string_val(req_content_location_node, &playback_location); + } + + plist_t req_start_position_seconds_node = plist_dict_get_item(req_root_node, "Start-Position-Seconds"); + if (!req_start_position_seconds_node) { + logger_log(conn->raop->logger, LOGGER_INFO, "No Start-Position-Seconds in Play request"); + } else { + double start_position = 0.0; + plist_get_real_val(req_start_position_seconds_node, &start_position); + start_position_seconds = (float) start_position; + } + set_start_position_seconds(conn->raop->airplay_video, (float) start_position_seconds); + } + + char *ptr = strstr(playback_location, "/master.m3u8"); + int prefix_len = (int) (ptr - playback_location); + set_uri_prefix(conn->raop->airplay_video, playback_location, prefix_len); + set_next_media_uri_id(conn->raop->airplay_video, 0); + fcup_request((void *) conn, playback_location, apple_session_id, get_next_FCUP_RequestID(conn->raop->airplay_video)); + + if (playback_location) { + free (playback_location); + } + + if (req_root_node) { + plist_free(req_root_node); + } + return; + + play_error:; + if (req_root_node) { + plist_free(req_root_node); + } + logger_log(conn->raop->logger, LOGGER_ERR, "Could not find valid Plist Data for /play, Unhandled"); + http_response_init(response, "HTTP/1.1", 400, "Bad Request"); +} + +/* the HLS handler handles http requests GET /[uri] on the HLS channel from the media player to the Server, asking for + (adjusted) copies of Playlists: first the Master Playlist (adjusted to change the uri prefix to + "http://localhost:[port]/.......m3u8"), then the Media Playlists that the media player wishes to use. + If the client supplied Media playlists with the "YT-EXT-CONDENSED-URI" header, these must be adjusted into + the standard uncondensed form before sending with the response. The uri in the request is the uri for the + Media Playlist, taken from the Master Playlist, with the uri prefix removed. +*/ + +static void +http_handler_hls(raop_conn_t *conn, http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + const char *method = http_request_get_method(request); + assert (!strcmp(method, "GET")); + const char *url = http_request_get_url(request); + const char* upgrade = http_request_get_header(request, "Upgrade"); + if (upgrade) { + //don't accept Upgrade: h2c request ? + return; + } + + if (!strcmp(url, "/master.m3u8")){ + char * master_playlist = get_master_playlist(conn->raop->airplay_video); + size_t len = strlen(master_playlist); + char * data = (char *) malloc(len + 1); + memcpy(data, master_playlist, len); + data[len] = '\0'; + *response_data = data; + *response_datalen = (int ) len; + } else { + int num = get_media_playlist_by_uri(conn->raop->airplay_video, url); + if (num < 0) { + logger_log(conn->raop->logger, LOGGER_ERR,"Requested playlist %s not found", url); + assert(0); + } else { + char *media_playlist = get_media_playlist_by_num(conn->raop->airplay_video, num); + assert(media_playlist); + char *data = adjust_yt_condensed_playlist(media_playlist); + *response_data = data; + *response_datalen = strlen(data); + float duration = 0.0f; + int chunks = analyze_media_playlist(data, &duration); + logger_log(conn->raop->logger, LOGGER_INFO, + "Requested media_playlist %s has %5d chunks, total duration %9.3f secs", url, chunks, duration); + } + } + + http_response_add_header(response, "Access-Control-Allow-Headers", "Content-type"); + http_response_add_header(response, "Access-Control-Allow-Origin", "*"); + const char *date; + date = gmt_time_string(); + http_response_add_header(response, "Date", date); + if (*response_datalen > 0) { + http_response_add_header(response, "Content-Type", "application/x-mpegURL; charset=utf-8"); + } else if (*response_datalen == 0) { + http_response_init(response, "HTTP/1.1", 404, "Not Found"); + } +} diff --git a/lib/http_request.c b/lib/http_request.c new file mode 100644 index 0000000..76bece6 --- /dev/null +++ b/lib/http_request.c @@ -0,0 +1,344 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2021 + */ + +#include +#include +#include +#include + +#include "http_request.h" +#include "llhttp/llhttp.h" + +struct http_request_s { + llhttp_t parser; + llhttp_settings_t parser_settings; + + bool is_reverse; // if true, this is a reverse-response from client + const char *method; + char *url; + char protocol[9]; + + char **headers; + int headers_size; + int headers_index; + + char *data; + int datalen; + + int complete; +}; + +static int +on_url(llhttp_t *parser, const char *at, size_t length) +{ + http_request_t *request = parser->data; + int urllen = request->url ? strlen(request->url) : 0; + + request->url = realloc(request->url, urllen+length+1); + assert(request->url); + + request->url[urllen] = '\0'; + strncat(request->url, at, length); + + strncpy(request->protocol, at + length + 1, 8); + + return 0; +} + +static int +on_header_field(llhttp_t *parser, const char *at, size_t length) +{ + http_request_t *request = parser->data; + + /* Check if our index is a value */ + if (request->headers_index%2 == 1) { + request->headers_index++; + } + + /* Allocate space for new field-value pair */ + if (request->headers_index == request->headers_size) { + request->headers_size += 2; + request->headers = realloc(request->headers, + request->headers_size*sizeof(char*)); + assert(request->headers); + request->headers[request->headers_index] = NULL; + request->headers[request->headers_index+1] = NULL; + } + + /* Allocate space in the current header string */ + if (request->headers[request->headers_index] == NULL) { + request->headers[request->headers_index] = calloc(1, length+1); + } else { + request->headers[request->headers_index] = realloc( + request->headers[request->headers_index], + strlen(request->headers[request->headers_index])+length+1 + ); + } + assert(request->headers[request->headers_index]); + + strncat(request->headers[request->headers_index], at, length); + return 0; +} + +static int +on_header_value(llhttp_t *parser, const char *at, size_t length) +{ + http_request_t *request = parser->data; + + /* Check if our index is a field */ + if (request->headers_index%2 == 0) { + request->headers_index++; + } + + /* Allocate space in the current header string */ + if (request->headers[request->headers_index] == NULL) { + request->headers[request->headers_index] = calloc(1, length+1); + } else { + request->headers[request->headers_index] = realloc( + request->headers[request->headers_index], + strlen(request->headers[request->headers_index])+length+1 + ); + } + assert(request->headers[request->headers_index]); + + strncat(request->headers[request->headers_index], at, length); + return 0; +} + +static int +on_body(llhttp_t *parser, const char *at, size_t length) +{ + http_request_t *request = parser->data; + + request->data = realloc(request->data, request->datalen+length); + assert(request->data); + + memcpy(request->data+request->datalen, at, length); + request->datalen += length; + return 0; +} + +static int +on_message_complete(llhttp_t *parser) +{ + http_request_t *request = parser->data; + + request->method = llhttp_method_name(request->parser.method); + request->complete = 1; + return 0; +} + +http_request_t * +http_request_init(void) +{ + http_request_t *request; + + request = calloc(1, sizeof(http_request_t)); + if (!request) { + return NULL; + } + + llhttp_settings_init(&request->parser_settings); + request->parser_settings.on_url = &on_url; + request->parser_settings.on_header_field = &on_header_field; + request->parser_settings.on_header_value = &on_header_value; + request->parser_settings.on_body = &on_body; + request->parser_settings.on_message_complete = &on_message_complete; + + llhttp_init(&request->parser, HTTP_REQUEST, &request->parser_settings); + request->parser.data = request; + request->is_reverse = false; + return request; +} + +void +http_request_destroy(http_request_t *request) +{ + int i; + + if (request) { + free(request->url); + for (i=0; iheaders_size; i++) { + free(request->headers[i]); + } + free(request->headers); + free(request->data); + free(request); + } +} + +int +http_request_add_data(http_request_t *request, const char *data, int datalen) +{ + int ret; + + assert(request); + + ret = llhttp_execute(&request->parser, data, datalen); + + /* support for "Upgrade" to reverse http ("PTTH/1.0") protocol */ + llhttp_resume_after_upgrade(&request->parser); + + return ret; +} + +int +http_request_is_complete(http_request_t *request) +{ + assert(request); + return request->complete; +} + +int +http_request_has_error(http_request_t *request) +{ + assert(request); + if (request->is_reverse) { + return 0; + } + return (llhttp_get_errno(&request->parser) != HPE_OK); +} + +const char * +http_request_get_error_name(http_request_t *request) +{ + assert(request); + if (request->is_reverse) { + return NULL; + } + return llhttp_errno_name(llhttp_get_errno(&request->parser)); +} + +const char * +http_request_get_error_description(http_request_t *request) +{ + assert(request); + if (request->is_reverse) { + return NULL; + } + return llhttp_get_error_reason(&request->parser); +} + +const char * +http_request_get_method(http_request_t *request) +{ + assert(request); + if (request->is_reverse) { + return NULL; + } + return request->method; +} + +const char * +http_request_get_url(http_request_t *request) +{ + assert(request); + if (request->is_reverse) { + return NULL; + } + return request->url; +} + +const char * +http_request_get_protocol(http_request_t *request) +{ + assert(request); + if (request->is_reverse) { + return NULL; + } + return request->protocol; +} + +const char * +http_request_get_header(http_request_t *request, const char *name) +{ + int i; + + assert(request); + if (request->is_reverse) { + return NULL; + } + + for (i=0; iheaders_size; i+=2) { + if (!strcmp(request->headers[i], name)) { + return request->headers[i+1]; + } + } + return NULL; +} + +const char * +http_request_get_data(http_request_t *request, int *datalen) +{ + assert(request); + if (datalen) { + *datalen = request->datalen; + } + return request->data; +} + +int +http_request_get_header_string(http_request_t *request, char **header_str) +{ + if(!request || request->headers_size == 0) { + *header_str = NULL; + return 0; + } + if (request->is_reverse) { + *header_str = NULL; + return 0; + } + int len = 0; + for (int i = 0; i < request->headers_size; i++) { + len += strlen(request->headers[i]); + if (i%2 == 0) { + len += 2; + } else { + len++; + } + } + char *str = calloc(len+1, sizeof(char)); + assert(str); + *header_str = str; + char *p = str; + int n = len + 1; + for (int i = 0; i < request->headers_size; i++) { + int hlen = strlen(request->headers[i]); + snprintf(p, n, "%s", request->headers[i]); + n -= hlen; + p += hlen; + if (i%2 == 0) { + snprintf(p, n, ": "); + n -= 2; + p += 2; + } else { + snprintf(p, n, "\n"); + n--; + p++; + } + } + assert(p == &(str[len])); + return len; +} + +bool http_request_is_reverse(http_request_t *request) { + return request->is_reverse; +} + +void http_request_set_reverse(http_request_t *request) { + request->is_reverse = true; +} diff --git a/lib/http_request.h b/lib/http_request.h new file mode 100644 index 0000000..dd13680 --- /dev/null +++ b/lib/http_request.h @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef HTTP_REQUEST_H +#define HTTP_REQUEST_H + +#include + +typedef struct http_request_s http_request_t; + +http_request_t *http_request_init(void); + +int http_request_add_data(http_request_t *request, const char *data, int datalen); +int http_request_is_complete(http_request_t *request); +int http_request_has_error(http_request_t *request); + +const char *http_request_get_error_name(http_request_t *request); +const char *http_request_get_error_description(http_request_t *request); +const char *http_request_get_method(http_request_t *request); +const char *http_request_get_url(http_request_t *request); +const char *http_request_get_protocol(http_request_t *request); +const char *http_request_get_header(http_request_t *request, const char *name); +const char *http_request_get_data(http_request_t *request, int *datalen); +int http_request_get_header_string(http_request_t *request, char **header_str); +bool http_request_is_reverse(http_request_t *request); +void http_request_set_reverse(http_request_t *request); + +void http_request_destroy(http_request_t *request); + +#endif diff --git a/lib/http_response.c b/lib/http_response.c new file mode 100644 index 0000000..a194338 --- /dev/null +++ b/lib/http_response.c @@ -0,0 +1,198 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include +#include +#include +#include + +#include "http_response.h" +#include "compat.h" + +struct http_response_s { + int complete; + int disconnect; + + char *data; + int data_size; + int data_length; +}; + + +static void +http_response_add_data(http_response_t *response, const char *data, int datalen) +{ + int newdatasize; + + assert(response); + assert(data); + assert(datalen > 0); + + newdatasize = response->data_size; + while (response->data_size+datalen > newdatasize) { + newdatasize *= 2; + } + if (newdatasize != response->data_size) { + response->data = realloc(response->data, newdatasize); + assert(response->data); + } + memcpy(response->data+response->data_length, data, datalen); + response->data_length += datalen; +} + + +http_response_t * +http_response_create() +{ + http_response_t *response = (http_response_t *) calloc(1, sizeof(http_response_t)); + if (!response) { + return NULL; + } + /* Allocate response data */ + response->data_size = 1024; + response->data = (char *) malloc(response->data_size); + if (!response->data) { + free(response); + return NULL; + } + return response; +} + +void +http_response_init(http_response_t *response, const char *protocol, int code, const char *message) +{ + assert(response); + response->data_length = 0; /* can be used to reinitialize a previously-initialized response */ + char codestr[4]; + + assert(code >= 100 && code < 1000); + + /* Convert code into string */ + memset(codestr, 0, sizeof(codestr)); + snprintf(codestr, sizeof(codestr), "%u", code); + + /* Add first line of response to the data array */ + http_response_add_data(response, protocol, strlen(protocol)); + http_response_add_data(response, " ", 1); + http_response_add_data(response, codestr, strlen(codestr)); + http_response_add_data(response, " ", 1); + http_response_add_data(response, message, strlen(message)); + http_response_add_data(response, "\r\n", 2); +} + +void +http_response_reverse_request_init(http_response_t *request, const char *method, const char *url, const char *protocol) +{ + assert(request); + request->data_length = 0; /* reinitialize a previously-initialized response as a reverse-HTTP (PTTH/1.0) request */ + + /* Add first line of response to the data array */ + http_response_add_data(request, method, strlen(method)); + http_response_add_data(request, " ", 1); + http_response_add_data(request, url, strlen(url)); + http_response_add_data(request, " ", 1); + http_response_add_data(request, protocol, strlen(protocol)); + http_response_add_data(request, "\r\n", 2); +} + +void +http_response_destroy(http_response_t *response) +{ + if (response) { + free(response->data); + free(response); + } +} + +void +http_response_add_header(http_response_t *response, const char *name, const char *value) +{ + assert(response); + assert(name); + assert(value); + + http_response_add_data(response, name, strlen(name)); + http_response_add_data(response, ": ", 2); + http_response_add_data(response, value, strlen(value)); + http_response_add_data(response, "\r\n", 2); +} + +void +http_response_finish(http_response_t *response, const char *data, int datalen) +{ + assert(response); + assert(datalen==0 || (data && datalen > 0)); + + if (data && datalen > 0) { + const char *hdrname = "Content-Length"; + char hdrvalue[16]; + + memset(hdrvalue, 0, sizeof(hdrvalue)); + snprintf(hdrvalue, sizeof(hdrvalue)-1, "%d", datalen); + + /* Add Content-Length header first */ + http_response_add_data(response, hdrname, strlen(hdrname)); + http_response_add_data(response, ": ", 2); + http_response_add_data(response, hdrvalue, strlen(hdrvalue)); + http_response_add_data(response, "\r\n\r\n", 4); + + /* Add data to the end of response */ + http_response_add_data(response, data, datalen); + } else { + /* check for "Content-Type" header, with datalen = 0 */ + const char *item = "Content-Type"; + int item_len = strlen(item); + const char *part = response->data; + int end = response->data_length - item_len; + for (int i = 0; i < end; i++) { + if (memcmp (part, item, item_len) == 0) { + const char *hdrname = "Content-Length: 0"; + http_response_add_data(response, hdrname, strlen(hdrname)); + http_response_add_data(response, "\r\n", 2); + break; + } + part++; + } + /* Add extra end of line after headers */ + http_response_add_data(response, "\r\n", 2); + } + response->complete = 1; +} + +void +http_response_set_disconnect(http_response_t *response, int disconnect) +{ + assert(response); + + response->disconnect = !!disconnect; +} + +int +http_response_get_disconnect(http_response_t *response) +{ + assert(response); + + return response->disconnect; +} + +const char * +http_response_get_data(http_response_t *response, int *datalen) +{ + assert(response); + assert(datalen); + assert(response->complete); + + *datalen = response->data_length; + return response->data; +} diff --git a/lib/http_response.h b/lib/http_response.h new file mode 100644 index 0000000..e34ba0c --- /dev/null +++ b/lib/http_response.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *============================================================== + * modified fduncanh 2024 + */ + +#ifndef HTTP_RESPONSE_H +#define HTTP_RESPONSE_H + +typedef struct http_response_s http_response_t; + +http_response_t *http_response_create(); +void http_response_init(http_response_t *response, const char *protocol, int code, const char *message); +void http_response_reverse_request_init(http_response_t *request, const char *method, const char *url, + const char *protocol); + +void http_response_add_header(http_response_t *response, const char *name, const char *value); +void http_response_finish(http_response_t *response, const char *data, int datalen); + +void http_response_set_disconnect(http_response_t *response, int disconnect); +int http_response_get_disconnect(http_response_t *response); + +const char *http_response_get_data(http_response_t *response, int *datalen); + +void http_response_destroy(http_response_t *response); + +#endif diff --git a/lib/httpd.c b/lib/httpd.c new file mode 100644 index 0000000..28f3d3c --- /dev/null +++ b/lib/httpd.c @@ -0,0 +1,663 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *=================================================================== + * modified by fduncanh 2022 + */ + +#include +#include +#include +#include +#include +#include + +#include "httpd.h" +#include "netutils.h" +#include "http_request.h" +#include "compat.h" +#include "logger.h" +#include "utils.h" + +static const char *typename[] = { + [CONNECTION_TYPE_UNKNOWN] = "Unknown", + [CONNECTION_TYPE_RAOP] = "RAOP", + [CONNECTION_TYPE_AIRPLAY] = "AirPlay", + [CONNECTION_TYPE_PTTH] = "AirPlay (reversed)", + [CONNECTION_TYPE_HLS] = "HLS" +}; + +struct http_connection_s { + int connected; + + int socket_fd; + void *user_data; + connection_type_t type; + http_request_t *request; +}; +typedef struct http_connection_s http_connection_t; + +struct httpd_s { + logger_t *logger; + httpd_callbacks_t callbacks; + + int max_connections; + int open_connections; + http_connection_t *connections; + char nohold; + + /* These variables only edited mutex locked */ + int running; + int joined; + thread_handle_t thread; + mutex_handle_t run_mutex; + + /* Server fds for accepting connections */ + int server_fd4; + int server_fd6; +}; + +const char * +httpd_get_connection_typename (connection_type_t type) { + return typename[type]; +} + +int +httpd_get_connection_socket (httpd_t *httpd, void *user_data) { + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + if (connection->user_data == user_data) { + return connection->socket_fd; + } + } + return -1; +} + +int +httpd_set_connection_type (httpd_t *httpd, void *user_data, connection_type_t type) { + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + if (connection->user_data == user_data) { + connection->type = type; + return i; + } + } + return -1; +} + +int +httpd_count_connection_type (httpd_t *httpd, connection_type_t type) { + int count = 0; + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + if (connection->type == type) { + count++; + } + } + return count; +} + +int +httpd_get_connection_socket_by_type (httpd_t *httpd, connection_type_t type, int instance){ + int count = 0; + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + if (connection->type == type) { + count++; + if (count == instance) { + return connection->socket_fd; + } + } + } + return 0; +} + +void * +httpd_get_connection_by_type (httpd_t *httpd, connection_type_t type, int instance){ + int count = 0; + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + if (connection->type == type) { + count++; + if (count == instance) { + return connection->user_data; + } + } + } + return NULL; +} + +#define MAX_CONNECTIONS 12 /* value used in AppleTV 3*/ +httpd_t * +httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold) +{ + httpd_t *httpd; + assert(logger); + assert(callbacks); + + /* Allocate the httpd_t structure */ + httpd = calloc(1, sizeof(httpd_t)); + if (!httpd) { + return NULL; + } + + httpd->nohold = (nohold ? 1 : 0); + httpd->max_connections = MAX_CONNECTIONS; + httpd->connections = calloc(httpd->max_connections, sizeof(http_connection_t)); + if (!httpd->connections) { + free(httpd); + return NULL; + } + + /* Use the logger provided */ + httpd->logger = logger; + + /* Save callback pointers */ + memcpy(&httpd->callbacks, callbacks, sizeof(httpd_callbacks_t)); + + /* Initial status joined */ + httpd->running = 0; + httpd->joined = 1; + + return httpd; +} + +void +httpd_destroy(httpd_t *httpd) +{ + if (httpd) { + httpd_stop(httpd); + + free(httpd->connections); + free(httpd); + } +} + +static void +httpd_remove_connection(httpd_t *httpd, http_connection_t *connection) +{ + if (connection->request) { + http_request_destroy(connection->request); + connection->request = NULL; + } + httpd->callbacks.conn_destroy(connection->user_data); + shutdown(connection->socket_fd, SHUT_WR); + closesocket(connection->socket_fd); + connection->connected = 0; + connection->user_data = NULL; + connection->type = CONNECTION_TYPE_UNKNOWN; + httpd->open_connections--; +} + +static int +httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len, unsigned char *remote, + int remote_len, unsigned int zone_id) +{ + void *user_data; + int i; + + for (i=0; imax_connections; i++) { + if (!httpd->connections[i].connected) { + break; + } + } + if (i == httpd->max_connections) { + /* This code should never be reached, we do not select server_fds when full */ + logger_log(httpd->logger, LOGGER_INFO, "Max connections reached"); + return -1; + } + user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len, zone_id); + if (!user_data) { + logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler"); + return -1; + } + + httpd->open_connections++; + httpd->connections[i].socket_fd = fd; + httpd->connections[i].connected = 1; + httpd->connections[i].user_data = user_data; + httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN; //should not be necessary ... + return 0; +} + +static int +httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6) +{ + struct sockaddr_storage remote_saddr; + socklen_t remote_saddrlen; + struct sockaddr_storage local_saddr; + socklen_t local_saddrlen; + unsigned char *local, *remote; + unsigned int local_zone_id, remote_zone_id; + int local_len, remote_len; + int ret, fd; + + remote_saddrlen = sizeof(remote_saddr); + fd = accept(server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen); + if (fd == -1) { + /* FIXME: Error happened */ + return -1; + } + + local_saddrlen = sizeof(local_saddr); + ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen); + if (ret == -1) { + shutdown(fd, SHUT_RDWR); + closesocket(fd); + return 0; + } + + logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d", + (is_ipv6 ? "IPv6" : "IPv4"), fd); + local = netutils_get_address(&local_saddr, &local_len, &local_zone_id); + remote = netutils_get_address(&remote_saddr, &remote_len, &remote_zone_id); + assert (local_zone_id == remote_zone_id); + + ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len, local_zone_id); + if (ret == -1) { + shutdown(fd, SHUT_RDWR); + closesocket(fd); + return 0; + } + return 1; +} + +bool +httpd_nohold(httpd_t *httpd) { + return (httpd->nohold ? true: false); +} + +void +httpd_remove_known_connections(httpd_t *httpd) { + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected || connection->type == CONNECTION_TYPE_UNKNOWN) { + continue; + } + httpd_remove_connection(httpd, connection); + } +} + +static THREAD_RETVAL +httpd_thread(void *arg) +{ + httpd_t *httpd = arg; + char http[] = "HTTP/1.1"; + char buffer[1024]; + int i; + + bool logger_debug = (logger_get_level(httpd->logger) >= LOGGER_DEBUG); + assert(httpd); + + while (1) { + fd_set rfds; + struct timeval tv; + int nfds=0; + int ret; + int new_request; + + MUTEX_LOCK(httpd->run_mutex); + if (!httpd->running) { + MUTEX_UNLOCK(httpd->run_mutex); + break; + } + MUTEX_UNLOCK(httpd->run_mutex); + + /* Set timeout value to 5ms */ + tv.tv_sec = 1; + tv.tv_usec = 5000; + + /* Get the correct nfds value and set rfds */ + FD_ZERO(&rfds); + if (httpd->open_connections < httpd->max_connections) { + if (httpd->server_fd4 != -1) { + FD_SET(httpd->server_fd4, &rfds); + if (nfds <= httpd->server_fd4) { + nfds = httpd->server_fd4+1; + } + } + if (httpd->server_fd6 != -1) { + FD_SET(httpd->server_fd6, &rfds); + if (nfds <= httpd->server_fd6) { + nfds = httpd->server_fd6+1; + } + } + } + for (i=0; imax_connections; i++) { + int socket_fd; + if (!httpd->connections[i].connected) { + continue; + } + socket_fd = httpd->connections[i].socket_fd; + FD_SET(socket_fd, &rfds); + if (nfds <= socket_fd) { + nfds = socket_fd+1; + } + } + + ret = select(nfds, &rfds, NULL, NULL, &tv); + if (ret == 0) { + /* Timeout happened */ + continue; + } else if (ret == -1) { + logger_log(httpd->logger, LOGGER_ERR, "httpd error in select: %d %s", errno, strerror(errno)); + break; + } + + if (httpd->open_connections < httpd->max_connections && + httpd->server_fd4 != -1 && FD_ISSET(httpd->server_fd4, &rfds)) { + ret = httpd_accept_connection(httpd, httpd->server_fd4, 0); + if (ret == -1) { + logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv4"); + break; + } else if (ret == 0) { + continue; + } + } + if (httpd->open_connections < httpd->max_connections && + httpd->server_fd6 != -1 && FD_ISSET(httpd->server_fd6, &rfds)) { + ret = httpd_accept_connection(httpd, httpd->server_fd6, 1); + if (ret == -1) { + logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv6"); + break; + } else if (ret == 0) { + continue; + } + } + for (i=0; imax_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + + if (!connection->connected) { + continue; + } + if (!FD_ISSET(connection->socket_fd, &rfds)) { + continue; + } + + /* If not in the middle of request, allocate one */ + if (!connection->request) { + connection->request = http_request_init(); + assert(connection->request); + new_request = 1; + if (connection->type == CONNECTION_TYPE_PTTH) { + http_request_is_reverse(connection->request); + } + logger_log(httpd->logger, LOGGER_DEBUG, "new request, connection %d, socket %d type %s", + i, connection->socket_fd, typename [connection->type]); + } else { + new_request = 0; + } + + logger_log(httpd->logger, LOGGER_DEBUG, "httpd receiving on socket %d, connection %d", + connection->socket_fd, i); + if (logger_debug) { + logger_log(httpd->logger, LOGGER_DEBUG,"\nhttpd: current connections:"); + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if(!connection->connected) { + continue; + } + if (!FD_ISSET(connection->socket_fd, &rfds)) { + logger_log(httpd->logger, LOGGER_DEBUG, "connection %d type %d socket %d conn %p %s", i, + connection->type, connection->socket_fd, + connection->user_data, typename [connection->type]); + } else { + logger_log(httpd->logger, LOGGER_DEBUG, "connection %d type %d socket %d conn %p %s ACTIVE CONNECTION", + i, connection->type, connection->socket_fd, connection->user_data, typename [connection->type]); + } + } + logger_log(httpd->logger, LOGGER_DEBUG, " "); + } + /* reverse-http responses from the client must not be sent to the llhttp parser: + * such messages start with "HTTP/1.1" */ + if (new_request) { + int readstart = 0; + new_request = 0; + while (readstart < 8) { + ret = recv(connection->socket_fd, buffer + readstart, sizeof(buffer) - 1 - readstart, 0); + if (ret == 0) { + logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d", + connection->socket_fd); + break; + } else if (ret == -1) { + if (errno == EAGAIN) { + continue; + } else { + int sock_err = SOCKET_GET_ERROR(); + logger_log(httpd->logger, LOGGER_ERR, "httpd: recv socket error %d:%s", + sock_err, strerror(sock_err)); + break; + } + } else { + readstart += ret; + ret = readstart; + } + } + if (!memcmp(buffer, http, 8)) { + http_request_set_reverse(connection->request); + } + } else { + ret = recv(connection->socket_fd, buffer, sizeof(buffer) - 1, 0); + if (ret == 0) { + logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d", + connection->socket_fd); + httpd_remove_connection(httpd, connection); + continue; + } + } + if (http_request_is_reverse(connection->request)) { + /* this is a response from the client to a + * GET /event reverse HTTP request from the server */ + if (ret && logger_debug) { + buffer[ret] = '\0'; + logger_log(httpd->logger, LOGGER_INFO, "<<<< received response from client" + " (reversed HTTP = \"PTTH/1.0\") connection" + " on socket %d:\n%s\n", connection->socket_fd, buffer); + } + if (ret == 0) { + httpd_remove_connection(httpd, connection); + } + continue; + } + + /* Parse HTTP request from data read from connection */ + http_request_add_data(connection->request, buffer, ret); + if (http_request_has_error(connection->request)) { + logger_log(httpd->logger, LOGGER_ERR, "httpd error in parsing: %s", + http_request_get_error_name(connection->request)); + httpd_remove_connection(httpd, connection); + continue; + } + + /* If request is finished, process and deallocate */ + if (http_request_is_complete(connection->request)) { + http_response_t *response = NULL; + // Callback the received data to raop + if (logger_debug) { + const char *method = http_request_get_method(connection->request); + const char *url = http_request_get_url(connection->request); + const char *protocol = http_request_get_protocol(connection->request); + logger_log(httpd->logger, LOGGER_INFO, "httpd request received on socket %d, " + "connection %d, method = %s, url = %s, protocol = %s", + connection->socket_fd, i, method, url, protocol); + } + httpd->callbacks.conn_request(connection->user_data, connection->request, &response); + http_request_destroy(connection->request); + connection->request = NULL; + + if (response) { + const char *data; + int datalen; + int written; + int ret; + + /* Get response data and datalen */ + data = http_response_get_data(response, &datalen); + + written = 0; + while (written < datalen) { + ret = send(connection->socket_fd, data+written, datalen-written, 0); + if (ret == -1) { + logger_log(httpd->logger, LOGGER_ERR, "httpd error in sending data"); + break; + } + written += ret; + } + + if (http_response_get_disconnect(response)) { + logger_log(httpd->logger, LOGGER_INFO, "Disconnecting on software request"); + httpd_remove_connection(httpd, connection); + } + } else { + logger_log(httpd->logger, LOGGER_WARNING, "httpd didn't get response"); + } + http_response_destroy(response); + } else { + logger_log(httpd->logger, LOGGER_DEBUG, "Request not complete, waiting for more data..."); + } + } + } + + /* Remove all connections that are still connected */ + for (i=0; imax_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + + if (!connection->connected) { + continue; + } + logger_log(httpd->logger, LOGGER_INFO, "Removing connection for socket %d", connection->socket_fd); + httpd_remove_connection(httpd, connection); + } + + /* Close server sockets since they are not used any more */ + if (httpd->server_fd4 != -1) { + shutdown(httpd->server_fd4, SHUT_RDWR); + closesocket(httpd->server_fd4); + httpd->server_fd4 = -1; + } + if (httpd->server_fd6 != -1) { + shutdown(httpd->server_fd6, SHUT_RDWR); + closesocket(httpd->server_fd6); + httpd->server_fd6 = -1; + } + + // Ensure running reflects the actual state + MUTEX_LOCK(httpd->run_mutex); + httpd->running = 0; + MUTEX_UNLOCK(httpd->run_mutex); + + logger_log(httpd->logger, LOGGER_DEBUG, "Exiting HTTP thread"); + + return 0; +} + +int +httpd_start(httpd_t *httpd, unsigned short *port) +{ + /* How many connection attempts are kept in queue */ + int backlog = 5; + + assert(httpd); + assert(port); + + MUTEX_LOCK(httpd->run_mutex); + if (httpd->running || !httpd->joined) { + MUTEX_UNLOCK(httpd->run_mutex); + return 0; + } + + httpd->server_fd4 = netutils_init_socket(port, 0, 0); + if (httpd->server_fd4 == -1) { + logger_log(httpd->logger, LOGGER_ERR, "Error initialising socket %d", SOCKET_GET_ERROR()); + MUTEX_UNLOCK(httpd->run_mutex); + return -1; + } + httpd->server_fd6 = netutils_init_socket(port, 1, 0); + if (httpd->server_fd6 == -1) { + logger_log(httpd->logger, LOGGER_WARNING, "Error initialising IPv6 socket %d", SOCKET_GET_ERROR()); + logger_log(httpd->logger, LOGGER_WARNING, "Continuing without IPv6 support"); + } + + if (httpd->server_fd4 != -1 && listen(httpd->server_fd4, backlog) == -1) { + logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv4 socket"); + closesocket(httpd->server_fd4); + closesocket(httpd->server_fd6); + MUTEX_UNLOCK(httpd->run_mutex); + return -2; + } + if (httpd->server_fd6 != -1 && listen(httpd->server_fd6, backlog) == -1) { + logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv6 socket"); + closesocket(httpd->server_fd4); + closesocket(httpd->server_fd6); + MUTEX_UNLOCK(httpd->run_mutex); + return -2; + } + logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket(s)"); + + /* Set values correctly and create new thread */ + httpd->running = 1; + httpd->joined = 0; + THREAD_CREATE(httpd->thread, httpd_thread, httpd); + MUTEX_UNLOCK(httpd->run_mutex); + + return 1; +} + +int +httpd_is_running(httpd_t *httpd) +{ + int running; + + assert(httpd); + + MUTEX_LOCK(httpd->run_mutex); + running = httpd->running || !httpd->joined; + MUTEX_UNLOCK(httpd->run_mutex); + + return running; +} + +void +httpd_stop(httpd_t *httpd) +{ + assert(httpd); + + MUTEX_LOCK(httpd->run_mutex); + if (!httpd->running || httpd->joined) { + MUTEX_UNLOCK(httpd->run_mutex); + return; + } + httpd->running = 0; + MUTEX_UNLOCK(httpd->run_mutex); + + THREAD_JOIN(httpd->thread); + + MUTEX_LOCK(httpd->run_mutex); + httpd->joined = 1; + MUTEX_UNLOCK(httpd->run_mutex); +} diff --git a/lib/httpd.h b/lib/httpd.h new file mode 100644 index 0000000..1df0e6b --- /dev/null +++ b/lib/httpd.h @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef HTTPD_H +#define HTTPD_H + +#include "logger.h" +#include "http_request.h" +#include "http_response.h" + +typedef struct httpd_s httpd_t; + +typedef enum connectype_type_e { + CONNECTION_TYPE_UNKNOWN, + CONNECTION_TYPE_RAOP, + CONNECTION_TYPE_AIRPLAY, + CONNECTION_TYPE_PTTH, + CONNECTION_TYPE_HLS +} connection_type_t; + +struct httpd_callbacks_s { + void* opaque; + void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote, + int remotelen, unsigned int zone_id); + void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response); + void (*conn_destroy)(void *ptr); +}; +typedef struct httpd_callbacks_s httpd_callbacks_t; +bool httpd_nohold(httpd_t *httpd); +void httpd_remove_known_connections(httpd_t *httpd); + +int httpd_set_connection_type (httpd_t *http, void *user_data, connection_type_t type); +int httpd_count_connection_type (httpd_t *http, connection_type_t type); +int httpd_get_connection_socket (httpd_t *httpd, void *user_data); +int httpd_get_connection_socket_by_type (httpd_t *httpd, connection_type_t type, int instance); +const char *httpd_get_connection_typename (connection_type_t type); +void *httpd_get_connection_by_type (httpd_t *httpd, connection_type_t type, int instance); +httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold); + +int httpd_is_running(httpd_t *httpd); + +int httpd_start(httpd_t *httpd, unsigned short *port); +void httpd_stop(httpd_t *httpd); + +void httpd_destroy(httpd_t *httpd); + + +#endif diff --git a/lib/llhttp/CMakeLists.txt b/lib/llhttp/CMakeLists.txt new file mode 100644 index 0000000..3895909 --- /dev/null +++ b/lib/llhttp/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.5) +aux_source_directory(. llhttp_src) +set(DIR_SRCS ${llhttp_src}) +include_directories(.) +add_library( llhttp + STATIC + ${DIR_SRCS}) diff --git a/lib/llhttp/LICENSE-MIT b/lib/llhttp/LICENSE-MIT new file mode 100644 index 0000000..6c1512d --- /dev/null +++ b/lib/llhttp/LICENSE-MIT @@ -0,0 +1,22 @@ +This software is licensed under the MIT License. + +Copyright Fedor Indutny, 2018. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/llhttp/api.c b/lib/llhttp/api.c new file mode 100644 index 0000000..8c2ce3d --- /dev/null +++ b/lib/llhttp/api.c @@ -0,0 +1,510 @@ +#include +#include +#include + +#include "llhttp.h" + +#define CALLBACK_MAYBE(PARSER, NAME) \ + do { \ + const llhttp_settings_t* settings; \ + settings = (const llhttp_settings_t*) (PARSER)->settings; \ + if (settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME((PARSER)); \ + } while (0) + +#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \ + do { \ + const llhttp_settings_t* settings; \ + settings = (const llhttp_settings_t*) (PARSER)->settings; \ + if (settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME((PARSER), (START), (LEN)); \ + if (err == -1) { \ + err = HPE_USER; \ + llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \ + } \ + } while (0) + +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings) { + llhttp__internal_init(parser); + + parser->type = type; + parser->settings = (void*) settings; +} + + +#if defined(__wasm__) + +extern int wasm_on_message_begin(llhttp_t * p); +extern int wasm_on_url(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_status(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_headers_complete(llhttp_t * p, int status_code, + uint8_t upgrade, int should_keep_alive); +extern int wasm_on_body(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_message_complete(llhttp_t * p); + +static int wasm_on_headers_complete_wrap(llhttp_t* p) { + return wasm_on_headers_complete(p, p->status_code, p->upgrade, + llhttp_should_keep_alive(p)); +} + +const llhttp_settings_t wasm_settings = { + wasm_on_message_begin, + wasm_on_url, + wasm_on_status, + NULL, + NULL, + wasm_on_header_field, + wasm_on_header_value, + NULL, + NULL, + wasm_on_headers_complete_wrap, + wasm_on_body, + wasm_on_message_complete, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + + +llhttp_t* llhttp_alloc(llhttp_type_t type) { + llhttp_t* parser = malloc(sizeof(llhttp_t)); + llhttp_init(parser, type, &wasm_settings); + return parser; +} + +void llhttp_free(llhttp_t* parser) { + free(parser); +} + +#endif // defined(__wasm__) + +/* Some getters required to get stuff from the parser */ + +uint8_t llhttp_get_type(llhttp_t* parser) { + return parser->type; +} + +uint8_t llhttp_get_http_major(llhttp_t* parser) { + return parser->http_major; +} + +uint8_t llhttp_get_http_minor(llhttp_t* parser) { + return parser->http_minor; +} + +uint8_t llhttp_get_method(llhttp_t* parser) { + return parser->method; +} + +int llhttp_get_status_code(llhttp_t* parser) { + return parser->status_code; +} + +uint8_t llhttp_get_upgrade(llhttp_t* parser) { + return parser->upgrade; +} + + +void llhttp_reset(llhttp_t* parser) { + llhttp_type_t type = parser->type; + const llhttp_settings_t* settings = parser->settings; + void* data = parser->data; + uint16_t lenient_flags = parser->lenient_flags; + + llhttp__internal_init(parser); + + parser->type = type; + parser->settings = (void*) settings; + parser->data = data; + parser->lenient_flags = lenient_flags; +} + + +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) { + return llhttp__internal_execute(parser, data, data + len); +} + + +void llhttp_settings_init(llhttp_settings_t* settings) { + memset(settings, 0, sizeof(*settings)); +} + + +llhttp_errno_t llhttp_finish(llhttp_t* parser) { + int err; + + /* We're in an error state. Don't bother doing anything. */ + if (parser->error != 0) { + return 0; + } + + switch (parser->finish) { + case HTTP_FINISH_SAFE_WITH_CB: + CALLBACK_MAYBE(parser, on_message_complete); + if (err != HPE_OK) return err; + + /* FALLTHROUGH */ + case HTTP_FINISH_SAFE: + return HPE_OK; + case HTTP_FINISH_UNSAFE: + parser->reason = "Invalid EOF state"; + return HPE_INVALID_EOF_STATE; + default: + abort(); + } +} + + +void llhttp_pause(llhttp_t* parser) { + if (parser->error != HPE_OK) { + return; + } + + parser->error = HPE_PAUSED; + parser->reason = "Paused"; +} + + +void llhttp_resume(llhttp_t* parser) { + if (parser->error != HPE_PAUSED) { + return; + } + + parser->error = 0; +} + + +void llhttp_resume_after_upgrade(llhttp_t* parser) { + if (parser->error != HPE_PAUSED_UPGRADE) { + return; + } + + parser->error = 0; +} + + +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) { + return parser->error; +} + + +const char* llhttp_get_error_reason(const llhttp_t* parser) { + return parser->reason; +} + + +void llhttp_set_error_reason(llhttp_t* parser, const char* reason) { + parser->reason = reason; +} + + +const char* llhttp_get_error_pos(const llhttp_t* parser) { + return parser->error_pos; +} + + +const char* llhttp_errno_name(llhttp_errno_t err) { +#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME; + switch (err) { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) + default: abort(); + } +#undef HTTP_ERRNO_GEN +} + + +const char* llhttp_method_name(llhttp_method_t method) { +#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING; + switch (method) { + HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN) + default: abort(); + } +#undef HTTP_METHOD_GEN +} + +const char* llhttp_status_name(llhttp_status_t status) { +#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING; + switch (status) { + HTTP_STATUS_MAP(HTTP_STATUS_GEN) + default: abort(); + } +#undef HTTP_STATUS_GEN +} + + +void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_HEADERS; + } else { + parser->lenient_flags &= ~LENIENT_HEADERS; + } +} + + +void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_CHUNKED_LENGTH; + } else { + parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH; + } +} + + +void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_KEEP_ALIVE; + } else { + parser->lenient_flags &= ~LENIENT_KEEP_ALIVE; + } +} + +void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_TRANSFER_ENCODING; + } else { + parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING; + } +} + +void llhttp_set_lenient_version(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_VERSION; + } else { + parser->lenient_flags &= ~LENIENT_VERSION; + } +} + +void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE; + } else { + parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE; + } +} + +void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR; + } else { + parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR; + } +} + +void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; + } else { + parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; + } +} + +void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF; + } else { + parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF; + } +} + +void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE; + } else { + parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE; + } +} + +/* Callbacks */ + + +int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_begin); + return err; +} + + +int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p); + return err; +} + + +int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_url_complete); + return err; +} + + +int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p); + return err; +} + + +int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_status_complete); + return err; +} + + +int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p); + return err; +} + + +int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_method_complete); + return err; +} + + +int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p); + return err; +} + + +int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_version_complete); + return err; +} + + +int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p); + return err; +} + + +int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_field_complete); + return err; +} + + +int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p); + return err; +} + + +int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_value_complete); + return err; +} + + +int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_headers_complete); + return err; +} + + +int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_complete); + return err; +} + + +int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p); + return err; +} + + +int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_header); + return err; +} + + +int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p); + return err; +} + + +int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_extension_name_complete); + return err; +} + + +int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p); + return err; +} + + +int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_extension_value_complete); + return err; +} + + +int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_complete); + return err; +} + + +int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_reset); + return err; +} + + +/* Private */ + + +void llhttp__debug(llhttp_t* s, const char* p, const char* endp, + const char* msg) { + if (p == endp) { + fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type, + s->flags, msg); + } else { + fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s, + s->type, s->flags, *p, msg); + } +} diff --git a/lib/llhttp/http.c b/lib/llhttp/http.c new file mode 100644 index 0000000..1ab91a5 --- /dev/null +++ b/lib/llhttp/http.c @@ -0,0 +1,170 @@ +#include +#ifndef LLHTTP__TEST +# include "llhttp.h" +#else +# define llhttp_t llparse_t +#endif /* */ + +int llhttp_message_needs_eof(const llhttp_t* parser); +int llhttp_should_keep_alive(const llhttp_t* parser); + +int llhttp__before_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + return 0; +} + + +/* Return values: + * 0 - No body, `restart`, message_complete + * 1 - CONNECT request, `restart`, message_complete, and pause + * 2 - chunk_size_start + * 3 - body_identity + * 4 - body_identity_eof + * 5 - invalid transfer-encoding for request + */ +int llhttp__after_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + int hasBody; + + hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; + if ( + (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) || + /* See RFC 2616 section 4.4 - 1xx e.g. Continue */ + (parser->type == HTTP_RESPONSE && parser->status_code == 101) + ) { + /* Exit, the rest of the message is in a different protocol. */ + return 1; + } + + if (parser->type == HTTP_RESPONSE && parser->status_code == 100) { + /* No body, restart as the message is complete */ + return 0; + } + + /* See RFC 2616 section 4.4 */ + if ( + parser->flags & F_SKIPBODY || /* response to a HEAD request */ + ( + parser->type == HTTP_RESPONSE && ( + parser->status_code == 102 || /* Processing */ + parser->status_code == 103 || /* Early Hints */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 /* Not Modified */ + ) + ) + ) { + return 0; + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header, prepare for a chunk */ + return 2; + } else if (parser->flags & F_TRANSFER_ENCODING) { + if (parser->type == HTTP_REQUEST && + (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 && + (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field + * is present in a request and the chunked transfer coding is not + * the final encoding, the message body length cannot be determined + * reliably; the server MUST respond with the 400 (Bad Request) + * status code and then close the connection. + */ + return 5; + } else { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field is present in a response and + * the chunked transfer coding is not the final encoding, the + * message body length is determined by reading the connection until + * it is closed by the server. + */ + return 4; + } + } else { + if (!(parser->flags & F_CONTENT_LENGTH)) { + if (!llhttp_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + return 0; + } else { + /* Read body until EOF */ + return 4; + } + } else if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + return 0; + } else { + /* Content-Length header given and non-zero */ + return 3; + } + } +} + + +int llhttp__after_message_complete(llhttp_t* parser, const char* p, + const char* endp) { + int should_keep_alive; + + should_keep_alive = llhttp_should_keep_alive(parser); + parser->finish = HTTP_FINISH_SAFE; + parser->flags = 0; + + /* NOTE: this is ignored in loose parsing mode */ + return should_keep_alive; +} + + +int llhttp_message_needs_eof(const llhttp_t* parser) { + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ + return 0; + } + + /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ + if ((parser->flags & F_TRANSFER_ENCODING) && + (parser->flags & F_CHUNKED) == 0) { + return 1; + } + + if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { + return 0; + } + + return 1; +} + + +int llhttp_should_keep_alive(const llhttp_t* parser) { + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !llhttp_message_needs_eof(parser); +} diff --git a/lib/llhttp/llhttp.c b/lib/llhttp/llhttp.c new file mode 100644 index 0000000..3ef3b81 --- /dev/null +++ b/lib/llhttp/llhttp.c @@ -0,0 +1,10168 @@ +#include +#include +#include + +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) +#endif /* _MSC_VER */ + +#include "llhttp.h" + +typedef int (*llhttp__internal__span_cb)( + llhttp__internal_t*, const char*, const char*); + +static const unsigned char llparse_blob0[] = { + 'o', 'n' +}; +static const unsigned char llparse_blob1[] = { + 'e', 'c', 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob2[] = { + 'l', 'o', 's', 'e' +}; +static const unsigned char llparse_blob3[] = { + 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' +}; +static const unsigned char llparse_blob4[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob5[] = { + 'c', 'h', 'u', 'n', 'k', 'e', 'd' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob6[] = { + 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob7[] = { + '!', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', + 'Z', '^', 'z', '|', '|' +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob8[] = { + '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob9[] = { + 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' +}; +static const unsigned char llparse_blob10[] = { + 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', + 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob11[] = { + 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', + 'o', 'd', 'i', 'n', 'g' +}; +static const unsigned char llparse_blob12[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob13[] = { + 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob14[] = { + 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa +}; +static const unsigned char llparse_blob15[] = { + 'C', 'E', '/' +}; +static const unsigned char llparse_blob16[] = { + 'T', 'S', 'P', '/' +}; +static const unsigned char llparse_blob17[] = { + 'N', 'O', 'U', 'N', 'C', 'E' +}; +static const unsigned char llparse_blob18[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob19[] = { + 'E', 'C', 'K', 'O', 'U', 'T' +}; +static const unsigned char llparse_blob20[] = { + 'N', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob21[] = { + 'E', 'T', 'E' +}; +static const unsigned char llparse_blob22[] = { + 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob23[] = { + 'L', 'U', 'S', 'H' +}; +static const unsigned char llparse_blob24[] = { + 'E', 'T' +}; +static const unsigned char llparse_blob25[] = { + 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' +}; +static const unsigned char llparse_blob26[] = { + 'E', 'A', 'D' +}; +static const unsigned char llparse_blob27[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob28[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob29[] = { + 'S', 'E', 'A', 'R', 'C', 'H' +}; +static const unsigned char llparse_blob30[] = { + 'R', 'G', 'E' +}; +static const unsigned char llparse_blob31[] = { + 'C', 'T', 'I', 'V', 'I', 'T', 'Y' +}; +static const unsigned char llparse_blob32[] = { + 'L', 'E', 'N', 'D', 'A', 'R' +}; +static const unsigned char llparse_blob33[] = { + 'V', 'E' +}; +static const unsigned char llparse_blob34[] = { + 'O', 'T', 'I', 'F', 'Y' +}; +static const unsigned char llparse_blob35[] = { + 'P', 'T', 'I', 'O', 'N', 'S' +}; +static const unsigned char llparse_blob36[] = { + 'C', 'H' +}; +static const unsigned char llparse_blob37[] = { + 'S', 'E' +}; +static const unsigned char llparse_blob38[] = { + 'A', 'Y' +}; +static const unsigned char llparse_blob39[] = { + 'S', 'T' +}; +static const unsigned char llparse_blob40[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob41[] = { + 'A', 'T', 'C', 'H' +}; +static const unsigned char llparse_blob42[] = { + 'G', 'E' +}; +static const unsigned char llparse_blob43[] = { + 'U', 'E', 'R', 'Y' +}; +static const unsigned char llparse_blob44[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob45[] = { + 'O', 'R', 'D' +}; +static const unsigned char llparse_blob46[] = { + 'I', 'R', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob47[] = { + 'O', 'R', 'T' +}; +static const unsigned char llparse_blob48[] = { + 'R', 'C', 'H' +}; +static const unsigned char llparse_blob49[] = { + 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' +}; +static const unsigned char llparse_blob50[] = { + 'U', 'R', 'C', 'E' +}; +static const unsigned char llparse_blob51[] = { + 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob52[] = { + 'A', 'R', 'D', 'O', 'W', 'N' +}; +static const unsigned char llparse_blob53[] = { + 'A', 'C', 'E' +}; +static const unsigned char llparse_blob54[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob55[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob56[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob57[] = { + 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob58[] = { + 'H', 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob59[] = { + 'A', 'D' +}; +static const unsigned char llparse_blob60[] = { + 'T', 'P', '/' +}; + +enum llparse_match_status_e { + kMatchComplete, + kMatchPause, + kMatchMismatch +}; +typedef enum llparse_match_status_e llparse_match_status_t; + +struct llparse_match_s { + llparse_match_status_t status; + const unsigned char* current; +}; +typedef struct llparse_match_s llparse_match_t; + +static llparse_match_t llparse__match_sequence_to_lower( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p)); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_to_lower_unsafe( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) | 0x20); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_id( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = *p; + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +enum llparse_state_e { + s_error, + s_n_llhttp__internal__n_closed, + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, + s_n_llhttp__internal__n_pause_1, + s_n_llhttp__internal__n_invoke_is_equal_upgrade, + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, + s_n_llhttp__internal__n_chunk_data_almost_done_1, + s_n_llhttp__internal__n_chunk_data_almost_done, + s_n_llhttp__internal__n_consume_content_length, + s_n_llhttp__internal__n_span_start_llhttp__on_body, + s_n_llhttp__internal__n_invoke_is_equal_content_length, + s_n_llhttp__internal__n_chunk_size_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_9, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2, + s_n_llhttp__internal__n_invoke_test_lenient_flags_10, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1, + s_n_llhttp__internal__n_chunk_extension_quoted_value_done, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2, + s_n_llhttp__internal__n_error_30, + s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair, + s_n_llhttp__internal__n_error_31, + s_n_llhttp__internal__n_chunk_extension_quoted_value, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3, + s_n_llhttp__internal__n_error_33, + s_n_llhttp__internal__n_chunk_extension_value, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value, + s_n_llhttp__internal__n_error_34, + s_n_llhttp__internal__n_chunk_extension_name, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name, + s_n_llhttp__internal__n_chunk_extensions, + s_n_llhttp__internal__n_chunk_size_otherwise, + s_n_llhttp__internal__n_chunk_size, + s_n_llhttp__internal__n_chunk_size_digit, + s_n_llhttp__internal__n_invoke_update_content_length_1, + s_n_llhttp__internal__n_consume_content_length_1, + s_n_llhttp__internal__n_span_start_llhttp__on_body_1, + s_n_llhttp__internal__n_eof, + s_n_llhttp__internal__n_span_start_llhttp__on_body_2, + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, + s_n_llhttp__internal__n_error_5, + s_n_llhttp__internal__n_headers_almost_done, + s_n_llhttp__internal__n_header_field_colon_discard_ws, + s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value, + s_n_llhttp__internal__n_header_value_discard_lws, + s_n_llhttp__internal__n_header_value_discard_ws_almost_done, + s_n_llhttp__internal__n_header_value_lws, + s_n_llhttp__internal__n_header_value_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_17, + s_n_llhttp__internal__n_header_value_lenient, + s_n_llhttp__internal__n_error_54, + s_n_llhttp__internal__n_header_value_otherwise, + s_n_llhttp__internal__n_header_value_connection_token, + s_n_llhttp__internal__n_header_value_connection_ws, + s_n_llhttp__internal__n_header_value_connection_1, + s_n_llhttp__internal__n_header_value_connection_2, + s_n_llhttp__internal__n_header_value_connection_3, + s_n_llhttp__internal__n_header_value_connection, + s_n_llhttp__internal__n_error_56, + s_n_llhttp__internal__n_error_57, + s_n_llhttp__internal__n_header_value_content_length_ws, + s_n_llhttp__internal__n_header_value_content_length, + s_n_llhttp__internal__n_error_59, + s_n_llhttp__internal__n_error_58, + s_n_llhttp__internal__n_header_value_te_token_ows, + s_n_llhttp__internal__n_header_value, + s_n_llhttp__internal__n_header_value_te_token, + s_n_llhttp__internal__n_header_value_te_chunked_last, + s_n_llhttp__internal__n_header_value_te_chunked, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, + s_n_llhttp__internal__n_header_value_discard_ws, + s_n_llhttp__internal__n_invoke_load_header_state, + s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete, + s_n_llhttp__internal__n_header_field_general_otherwise, + s_n_llhttp__internal__n_header_field_general, + s_n_llhttp__internal__n_header_field_colon, + s_n_llhttp__internal__n_header_field_3, + s_n_llhttp__internal__n_header_field_4, + s_n_llhttp__internal__n_header_field_2, + s_n_llhttp__internal__n_header_field_1, + s_n_llhttp__internal__n_header_field_5, + s_n_llhttp__internal__n_header_field_6, + s_n_llhttp__internal__n_header_field_7, + s_n_llhttp__internal__n_header_field, + s_n_llhttp__internal__n_span_start_llhttp__on_header_field, + s_n_llhttp__internal__n_header_field_start, + s_n_llhttp__internal__n_headers_start, + s_n_llhttp__internal__n_url_to_http_09, + s_n_llhttp__internal__n_url_skip_to_http09, + s_n_llhttp__internal__n_url_skip_lf_to_http09_1, + s_n_llhttp__internal__n_url_skip_lf_to_http09, + s_n_llhttp__internal__n_req_pri_upgrade, + s_n_llhttp__internal__n_req_http_complete_crlf, + s_n_llhttp__internal__n_req_http_complete, + s_n_llhttp__internal__n_invoke_load_method_1, + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete, + s_n_llhttp__internal__n_error_66, + s_n_llhttp__internal__n_error_73, + s_n_llhttp__internal__n_req_http_minor, + s_n_llhttp__internal__n_error_74, + s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_error_75, + s_n_llhttp__internal__n_req_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version, + s_n_llhttp__internal__n_req_http_start_1, + s_n_llhttp__internal__n_req_http_start_2, + s_n_llhttp__internal__n_req_http_start_3, + s_n_llhttp__internal__n_req_http_start, + s_n_llhttp__internal__n_url_to_http, + s_n_llhttp__internal__n_url_skip_to_http, + s_n_llhttp__internal__n_url_fragment, + s_n_llhttp__internal__n_span_end_stub_query_3, + s_n_llhttp__internal__n_url_query, + s_n_llhttp__internal__n_url_query_or_fragment, + s_n_llhttp__internal__n_url_path, + s_n_llhttp__internal__n_span_start_stub_path_2, + s_n_llhttp__internal__n_span_start_stub_path, + s_n_llhttp__internal__n_span_start_stub_path_1, + s_n_llhttp__internal__n_url_server_with_at, + s_n_llhttp__internal__n_url_server, + s_n_llhttp__internal__n_url_schema_delim_1, + s_n_llhttp__internal__n_url_schema_delim, + s_n_llhttp__internal__n_span_end_stub_schema, + s_n_llhttp__internal__n_url_schema, + s_n_llhttp__internal__n_url_start, + s_n_llhttp__internal__n_span_start_llhttp__on_url_1, + s_n_llhttp__internal__n_url_entry_normal, + s_n_llhttp__internal__n_span_start_llhttp__on_url, + s_n_llhttp__internal__n_url_entry_connect, + s_n_llhttp__internal__n_req_spaces_before_url, + s_n_llhttp__internal__n_req_first_space_before_url, + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1, + s_n_llhttp__internal__n_after_start_req_2, + s_n_llhttp__internal__n_after_start_req_3, + s_n_llhttp__internal__n_after_start_req_1, + s_n_llhttp__internal__n_after_start_req_4, + s_n_llhttp__internal__n_after_start_req_6, + s_n_llhttp__internal__n_after_start_req_8, + s_n_llhttp__internal__n_after_start_req_9, + s_n_llhttp__internal__n_after_start_req_7, + s_n_llhttp__internal__n_after_start_req_5, + s_n_llhttp__internal__n_after_start_req_12, + s_n_llhttp__internal__n_after_start_req_13, + s_n_llhttp__internal__n_after_start_req_11, + s_n_llhttp__internal__n_after_start_req_10, + s_n_llhttp__internal__n_after_start_req_14, + s_n_llhttp__internal__n_after_start_req_17, + s_n_llhttp__internal__n_after_start_req_16, + s_n_llhttp__internal__n_after_start_req_15, + s_n_llhttp__internal__n_after_start_req_18, + s_n_llhttp__internal__n_after_start_req_20, + s_n_llhttp__internal__n_after_start_req_21, + s_n_llhttp__internal__n_after_start_req_19, + s_n_llhttp__internal__n_after_start_req_23, + s_n_llhttp__internal__n_after_start_req_24, + s_n_llhttp__internal__n_after_start_req_26, + s_n_llhttp__internal__n_after_start_req_28, + s_n_llhttp__internal__n_after_start_req_29, + s_n_llhttp__internal__n_after_start_req_27, + s_n_llhttp__internal__n_after_start_req_25, + s_n_llhttp__internal__n_after_start_req_30, + s_n_llhttp__internal__n_after_start_req_22, + s_n_llhttp__internal__n_after_start_req_31, + s_n_llhttp__internal__n_after_start_req_32, + s_n_llhttp__internal__n_after_start_req_35, + s_n_llhttp__internal__n_after_start_req_36, + s_n_llhttp__internal__n_after_start_req_34, + s_n_llhttp__internal__n_after_start_req_37, + s_n_llhttp__internal__n_after_start_req_38, + s_n_llhttp__internal__n_after_start_req_42, + s_n_llhttp__internal__n_after_start_req_43, + s_n_llhttp__internal__n_after_start_req_41, + s_n_llhttp__internal__n_after_start_req_40, + s_n_llhttp__internal__n_after_start_req_39, + s_n_llhttp__internal__n_after_start_req_45, + s_n_llhttp__internal__n_after_start_req_44, + s_n_llhttp__internal__n_after_start_req_33, + s_n_llhttp__internal__n_after_start_req_46, + s_n_llhttp__internal__n_after_start_req_49, + s_n_llhttp__internal__n_after_start_req_50, + s_n_llhttp__internal__n_after_start_req_51, + s_n_llhttp__internal__n_after_start_req_52, + s_n_llhttp__internal__n_after_start_req_48, + s_n_llhttp__internal__n_after_start_req_47, + s_n_llhttp__internal__n_after_start_req_55, + s_n_llhttp__internal__n_after_start_req_57, + s_n_llhttp__internal__n_after_start_req_58, + s_n_llhttp__internal__n_after_start_req_56, + s_n_llhttp__internal__n_after_start_req_54, + s_n_llhttp__internal__n_after_start_req_59, + s_n_llhttp__internal__n_after_start_req_60, + s_n_llhttp__internal__n_after_start_req_53, + s_n_llhttp__internal__n_after_start_req_62, + s_n_llhttp__internal__n_after_start_req_63, + s_n_llhttp__internal__n_after_start_req_61, + s_n_llhttp__internal__n_after_start_req_66, + s_n_llhttp__internal__n_after_start_req_68, + s_n_llhttp__internal__n_after_start_req_69, + s_n_llhttp__internal__n_after_start_req_67, + s_n_llhttp__internal__n_after_start_req_70, + s_n_llhttp__internal__n_after_start_req_65, + s_n_llhttp__internal__n_after_start_req_64, + s_n_llhttp__internal__n_after_start_req, + s_n_llhttp__internal__n_span_start_llhttp__on_method_1, + s_n_llhttp__internal__n_res_line_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_30, + s_n_llhttp__internal__n_res_status, + s_n_llhttp__internal__n_span_start_llhttp__on_status, + s_n_llhttp__internal__n_res_status_code_otherwise, + s_n_llhttp__internal__n_res_status_code_digit_3, + s_n_llhttp__internal__n_res_status_code_digit_2, + s_n_llhttp__internal__n_res_status_code_digit_1, + s_n_llhttp__internal__n_res_after_version, + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1, + s_n_llhttp__internal__n_error_89, + s_n_llhttp__internal__n_error_103, + s_n_llhttp__internal__n_res_http_minor, + s_n_llhttp__internal__n_error_104, + s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_error_105, + s_n_llhttp__internal__n_res_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version_1, + s_n_llhttp__internal__n_start_res, + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete, + s_n_llhttp__internal__n_req_or_res_method_2, + s_n_llhttp__internal__n_invoke_update_type_1, + s_n_llhttp__internal__n_req_or_res_method_3, + s_n_llhttp__internal__n_req_or_res_method_1, + s_n_llhttp__internal__n_req_or_res_method, + s_n_llhttp__internal__n_span_start_llhttp__on_method, + s_n_llhttp__internal__n_start_req_or_res, + s_n_llhttp__internal__n_invoke_load_type, + s_n_llhttp__internal__n_invoke_update_finish, + s_n_llhttp__internal__n_start, +}; +typedef enum llparse_state_e llparse_state_t; + +int llhttp__on_method( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_url( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_version( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_field( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_body( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_name( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_status( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_initial_message_completed( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->initial_message_completed; +} + +int llhttp__on_reset( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 2; + return 0; +} + +int llhttp__on_message_begin( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->type; +} + +int llhttp__internal__c_store_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->method = match; + return 0; +} + +int llhttp__on_method_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method == 5; +} + +int llhttp__internal__c_update_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_major = 0; + return 0; +} + +int llhttp__internal__c_update_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_minor = 9; + return 0; +} + +int llhttp__on_url_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_test_lenient_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 1) == 1; +} + +int llhttp__internal__c_test_lenient_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 256) == 256; +} + +int llhttp__internal__c_test_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 128) == 128; +} + +int llhttp__on_chunk_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->upgrade == 1; +} + +int llhttp__after_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->content_length = 0; + return 0; +} + +int llhttp__internal__c_update_initial_message_completed( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->initial_message_completed = 1; + return 0; +} + +int llhttp__internal__c_update_finish_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 0; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 4) == 4; +} + +int llhttp__internal__c_test_lenient_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 32) == 32; +} + +int llhttp__before_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__after_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_mul_add_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 16) { + return 1; + } + + state->content_length *= 16; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 512) == 512; +} + +int llhttp__on_chunk_header( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->content_length == 0; +} + +int llhttp__internal__c_test_lenient_flags_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 128) == 128; +} + +int llhttp__internal__c_or_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 128; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 64) == 64; +} + +int llhttp__on_chunk_extension_name_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_value_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 1; + return 0; +} + +int llhttp__internal__c_or_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 64; + return 0; +} + +int llhttp__internal__c_update_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->upgrade = 1; + return 0; +} + +int llhttp__internal__c_store_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->header_state = match; + return 0; +} + +int llhttp__on_header_field_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->header_state; +} + +int llhttp__internal__c_test_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 512) == 512; +} + +int llhttp__internal__c_test_lenient_flags_22( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 2) == 2; +} + +int llhttp__internal__c_or_flags_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 1; + return 0; +} + +int llhttp__internal__c_update_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 1; + return 0; +} + +int llhttp__on_header_value_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_or_flags_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 2; + return 0; +} + +int llhttp__internal__c_or_flags_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 4; + return 0; +} + +int llhttp__internal__c_or_flags_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 8; + return 0; +} + +int llhttp__internal__c_update_header_state_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 6; + return 0; +} + +int llhttp__internal__c_update_header_state_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 0; + return 0; +} + +int llhttp__internal__c_update_header_state_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 5; + return 0; +} + +int llhttp__internal__c_update_header_state_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 7; + return 0; +} + +int llhttp__internal__c_test_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 32) == 32; +} + +int llhttp__internal__c_mul_add_content_length_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 10) { + return 1; + } + + state->content_length *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_or_flags_17( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 32; + return 0; +} + +int llhttp__internal__c_test_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 8) == 8; +} + +int llhttp__internal__c_test_lenient_flags_20( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 8) == 8; +} + +int llhttp__internal__c_or_flags_18( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 512; + return 0; +} + +int llhttp__internal__c_and_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags &= -9; + return 0; +} + +int llhttp__internal__c_update_header_state_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 8; + return 0; +} + +int llhttp__internal__c_or_flags_20( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 16; + return 0; +} + +int llhttp__internal__c_load_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method; +} + +int llhttp__internal__c_store_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_major = match; + return 0; +} + +int llhttp__internal__c_store_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_minor = match; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_24( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 16) == 16; +} + +int llhttp__on_version_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->http_major; +} + +int llhttp__internal__c_load_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->http_minor; +} + +int llhttp__internal__c_update_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->status_code = 0; + return 0; +} + +int llhttp__internal__c_mul_add_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->status_code > 0xffff / 10) { + return 1; + } + + state->status_code *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->status_code > 0xffff - match) { + return 1; + } + } else { + if (state->status_code < 0 - match) { + return 1; + } + } + state->status_code += match; + return 0; +} + +int llhttp__on_status_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 1; + return 0; +} + +int llhttp__internal__c_update_type_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 2; + return 0; +} + +int llhttp__internal_init(llhttp__internal_t* state) { + memset(state, 0, sizeof(*state)); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; + return 0; +} + +static llparse_state_t llhttp__internal__run( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + int match; + switch ((llparse_state_t) (intptr_t) state->_current) { + case s_n_llhttp__internal__n_closed: + s_n_llhttp__internal__n_closed: { + if (p == endp) { + return s_n_llhttp__internal__n_closed; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_closed; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_closed; + } + default: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { + switch (llhttp__after_message_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_update_content_length; + default: + goto s_n_llhttp__internal__n_invoke_update_finish_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_pause_1: + s_n_llhttp__internal__n_pause_1: { + state->error = 0x16; + state->reason = "Pause on CONNECT/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_upgrade: + s_n_llhttp__internal__n_invoke_is_equal_upgrade: { + switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + default: + goto s_n_llhttp__internal__n_pause_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_13; + default: + goto s_n_llhttp__internal__n_error_38; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done_1: + s_n_llhttp__internal__n_chunk_data_almost_done_1: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done: + s_n_llhttp__internal__n_chunk_data_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_data_almost_done_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length: + s_n_llhttp__internal__n_consume_content_length: { + size_t avail; + uint64_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body: + s_n_llhttp__internal__n_span_start_llhttp__on_body: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_content_length: + s_n_llhttp__internal__n_invoke_is_equal_content_length: { + switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body; + default: + goto s_n_llhttp__internal__n_invoke_or_flags; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_almost_done: + s_n_llhttp__internal__n_chunk_size_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_9: + s_n_llhttp__internal__n_invoke_test_lenient_flags_9: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_20; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9; + case 21: + goto s_n_llhttp__internal__n_pause_5; + default: + goto s_n_llhttp__internal__n_error_19; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + case 21: + goto s_n_llhttp__internal__n_pause_6; + default: + goto s_n_llhttp__internal__n_error_21; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extensions; + case 21: + goto s_n_llhttp__internal__n_pause_7; + default: + goto s_n_llhttp__internal__n_error_22; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_10: + s_n_llhttp__internal__n_invoke_test_lenient_flags_10: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_25; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10; + case 21: + goto s_n_llhttp__internal__n_pause_8; + default: + goto s_n_llhttp__internal__n_error_24; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + case 21: + goto s_n_llhttp__internal__n_pause_9; + default: + goto s_n_llhttp__internal__n_error_26; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value_done: + s_n_llhttp__internal__n_chunk_extension_quoted_value_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_extensions; + } + default: { + goto s_n_llhttp__internal__n_error_29; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + case 21: + goto s_n_llhttp__internal__n_pause_10; + default: + goto s_n_llhttp__internal__n_error_27; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_30: + s_n_llhttp__internal__n_error_30: { + state->error = 0x2; + state->reason = "Invalid quoted-pair in chunk extensions quoted value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: + s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_31: + s_n_llhttp__internal__n_error_31: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions quoted value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value: + s_n_llhttp__internal__n_chunk_extension_quoted_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extensions; + case 21: + goto s_n_llhttp__internal__n_pause_11; + default: + goto s_n_llhttp__internal__n_error_32; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_33: + s_n_llhttp__internal__n_error_33: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_value: + s_n_llhttp__internal__n_chunk_extension_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_value; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_value; + } + case 4: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_extension_value; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_34: + s_n_llhttp__internal__n_error_34: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions name"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extension_name: + s_n_llhttp__internal__n_chunk_extension_name: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_name; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_name; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_extension_name; + goto s_n_llhttp__internal__n_chunk_extension_name; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_extensions: + s_n_llhttp__internal__n_chunk_extensions: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extensions; + } + switch (*p) { + case 13: { + p++; + goto s_n_llhttp__internal__n_error_17; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_18; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_otherwise: + s_n_llhttp__internal__n_chunk_size_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_otherwise; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_extensions; + } + default: { + goto s_n_llhttp__internal__n_error_35; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size: + s_n_llhttp__internal__n_chunk_size: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_chunk_size_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_digit: + s_n_llhttp__internal__n_chunk_size_digit: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_digit; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_error_37; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_content_length_1: + s_n_llhttp__internal__n_invoke_update_content_length_1: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_chunk_size_digit; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length_1: + s_n_llhttp__internal__n_consume_content_length_1: { + size_t avail; + uint64_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: + s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_eof: + s_n_llhttp__internal__n_eof: { + if (p == endp) { + return s_n_llhttp__internal__n_eof; + } + p++; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: + s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { + switch (llhttp__after_headers_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_content_length_1; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + case 4: + goto s_n_llhttp__internal__n_invoke_update_finish_3; + case 5: + goto s_n_llhttp__internal__n_error_39; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_5: + s_n_llhttp__internal__n_error_5: { + state->error = 0xa; + state->reason = "Invalid header field char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_headers_almost_done: + s_n_llhttp__internal__n_headers_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_flags_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_colon_discard_ws: + s_n_llhttp__internal__n_header_field_colon_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon_discard_ws; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_field_colon_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_header_field_colon; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: { + switch (llhttp__on_header_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_field_start; + case 21: + goto s_n_llhttp__internal__n_pause_18; + default: + goto s_n_llhttp__internal__n_error_48; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_lws: + s_n_llhttp__internal__n_header_value_discard_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_lws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: + s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_lws; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lws: + s_n_llhttp__internal__n_header_value_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lws; + } + switch (*p) { + case 9: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; + } + case ' ': { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_5; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_almost_done: + s_n_llhttp__internal__n_header_value_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_lws; + } + default: { + goto s_n_llhttp__internal__n_error_53; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_17: + s_n_llhttp__internal__n_invoke_test_lenient_flags_17: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_almost_done; + default: + goto s_n_llhttp__internal__n_error_51; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lenient: + s_n_llhttp__internal__n_header_value_lenient: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lenient; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; + } + default: { + p++; + goto s_n_llhttp__internal__n_header_value_lenient; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_54: + s_n_llhttp__internal__n_error_54: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_otherwise: + s_n_llhttp__internal__n_header_value_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_otherwise; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_token: + s_n_llhttp__internal__n_header_value_connection_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_connection_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_ws: + s_n_llhttp__internal__n_header_value_connection_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case 13: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + case ',': { + p++; + goto s_n_llhttp__internal__n_invoke_load_header_state_6; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_5; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_1: + s_n_llhttp__internal__n_header_value_connection_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_1; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_2: + s_n_llhttp__internal__n_header_value_connection_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_2; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_6; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_3: + s_n_llhttp__internal__n_header_value_connection_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_3; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection: + s_n_llhttp__internal__n_header_value_connection: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_1; + } + case 'k': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_2; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_3; + } + default: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_56: + s_n_llhttp__internal__n_error_56: { + state->error = 0xb; + state->reason = "Content-Length overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_57: + s_n_llhttp__internal__n_error_57: { + state->error = 0xb; + state->reason = "Invalid character in Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length_ws: + s_n_llhttp__internal__n_header_value_content_length_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_or_flags_17; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_or_flags_17; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length: + s_n_llhttp__internal__n_header_value_content_length: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_59: + s_n_llhttp__internal__n_error_59: { + state->error = 0xf; + state->reason = "Invalid `Transfer-Encoding` header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_58: + s_n_llhttp__internal__n_error_58: { + state->error = 0xf; + state->reason = "Invalid `Transfer-Encoding` header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token_ows: + s_n_llhttp__internal__n_header_value_te_token_ows: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token_ows; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value: + s_n_llhttp__internal__n_header_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob6); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 6, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_value; + } + goto s_n_llhttp__internal__n_header_value_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token: + s_n_llhttp__internal__n_header_value_te_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked_last: + s_n_llhttp__internal__n_header_value_te_chunked_last: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked_last; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case ',': { + goto s_n_llhttp__internal__n_invoke_load_type_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked: + s_n_llhttp__internal__n_header_value_te_chunked: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_invoke_load_header_state_3; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws: + s_n_llhttp__internal__n_header_value_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_header_state: + s_n_llhttp__internal__n_invoke_load_header_state: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_4; + case 3: + goto s_n_llhttp__internal__n_invoke_test_flags_5; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: { + switch (llhttp__on_header_field_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_header_state; + case 21: + goto s_n_llhttp__internal__n_pause_19; + default: + goto s_n_llhttp__internal__n_error_45; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general_otherwise: + s_n_llhttp__internal__n_header_field_general_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general_otherwise; + } + switch (*p) { + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2; + } + default: { + goto s_n_llhttp__internal__n_error_62; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general: + s_n_llhttp__internal__n_header_field_general: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 16, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + ranges = _mm_loadu_si128((__m128i const*) llparse_blob8); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 2, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_field_general; + } + default: { + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_colon: + s_n_llhttp__internal__n_header_field_colon: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon; + } + switch (*p) { + case ' ': { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13; + } + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_3: + s_n_llhttp__internal__n_header_field_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_3; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_4: + s_n_llhttp__internal__n_header_field_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_4; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_2: + s_n_llhttp__internal__n_header_field_2: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_2; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 'n': { + p++; + goto s_n_llhttp__internal__n_header_field_3; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_4; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_1: + s_n_llhttp__internal__n_header_field_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_1; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_field_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_5: + s_n_llhttp__internal__n_header_field_5: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_5; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_5; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_6: + s_n_llhttp__internal__n_header_field_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_6; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_7: + s_n_llhttp__internal__n_header_field_7: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_7; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_7; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field: + s_n_llhttp__internal__n_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_field_1; + } + case 'p': { + p++; + goto s_n_llhttp__internal__n_header_field_5; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_6; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_field_7; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: + s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_field; + goto s_n_llhttp__internal__n_header_field; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_start: + s_n_llhttp__internal__n_header_field_start: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_headers_almost_done; + } + case ':': { + goto s_n_llhttp__internal__n_error_44; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_headers_start: + s_n_llhttp__internal__n_headers_start: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags; + } + default: { + goto s_n_llhttp__internal__n_header_field_start; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_to_http_09: + s_n_llhttp__internal__n_url_to_http_09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_to_http_09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_http_major; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http09: + s_n_llhttp__internal__n_url_skip_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + p++; + goto s_n_llhttp__internal__n_url_to_http_09; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09_1: + s_n_llhttp__internal__n_url_skip_lf_to_http09_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_url_to_http_09; + } + default: { + goto s_n_llhttp__internal__n_error_63; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09: + s_n_llhttp__internal__n_url_skip_lf_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1; + } + default: { + goto s_n_llhttp__internal__n_error_63; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_pri_upgrade: + s_n_llhttp__internal__n_req_pri_upgrade: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_pri_upgrade; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_error_71; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_pri_upgrade; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_72; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_complete_crlf: + s_n_llhttp__internal__n_req_http_complete_crlf: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_complete_crlf; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_headers_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_complete: + s_n_llhttp__internal__n_req_http_complete: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_complete; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_req_http_complete_crlf; + } + default: { + goto s_n_llhttp__internal__n_error_70; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_method_1: + s_n_llhttp__internal__n_invoke_load_method_1: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 34: + goto s_n_llhttp__internal__n_req_pri_upgrade; + default: + goto s_n_llhttp__internal__n_req_http_complete; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: { + switch (llhttp__on_version_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method_1; + case 21: + goto s_n_llhttp__internal__n_pause_21; + default: + goto s_n_llhttp__internal__n_error_67; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_66: + s_n_llhttp__internal__n_error_66: { + state->error = 0x9; + state->reason = "Invalid HTTP version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_73: + s_n_llhttp__internal__n_error_73: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_minor: + s_n_llhttp__internal__n_req_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_74: + s_n_llhttp__internal__n_error_74: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_dot: + s_n_llhttp__internal__n_req_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_req_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_75: + s_n_llhttp__internal__n_error_75: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_major: + s_n_llhttp__internal__n_req_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_version: + s_n_llhttp__internal__n_span_start_llhttp__on_version: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_version; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_version; + goto s_n_llhttp__internal__n_req_http_major; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_1: + s_n_llhttp__internal__n_req_http_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_load_method; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_2: + s_n_llhttp__internal__n_req_http_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_load_method_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_3: + s_n_llhttp__internal__n_req_http_start_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_load_method_3; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start: + s_n_llhttp__internal__n_req_http_start: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_http_start; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_http_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_req_http_start_2; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_req_http_start_3; + } + default: { + goto s_n_llhttp__internal__n_error_78; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_to_http: + s_n_llhttp__internal__n_url_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_to_http; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http: + s_n_llhttp__internal__n_url_skip_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + p++; + goto s_n_llhttp__internal__n_url_to_http; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_fragment: + s_n_llhttp__internal__n_url_fragment: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_fragment; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + default: { + goto s_n_llhttp__internal__n_error_79; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_query_3: + s_n_llhttp__internal__n_span_end_stub_query_3: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_query_3; + } + p++; + goto s_n_llhttp__internal__n_url_fragment; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query: + s_n_llhttp__internal__n_url_query: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_query; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 6: { + goto s_n_llhttp__internal__n_span_end_stub_query_3; + } + default: { + goto s_n_llhttp__internal__n_error_80; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query_or_fragment: + s_n_llhttp__internal__n_url_query_or_fragment: { + if (p == endp) { + return s_n_llhttp__internal__n_url_query_or_fragment; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; + } + case ' ': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; + } + case '#': { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + case '?': { + p++; + goto s_n_llhttp__internal__n_url_query; + } + default: { + goto s_n_llhttp__internal__n_error_81; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_path: + s_n_llhttp__internal__n_url_path: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_path; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_url_path; + } + default: { + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_2: + s_n_llhttp__internal__n_span_start_stub_path_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_2; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path: + s_n_llhttp__internal__n_span_start_stub_path: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_1: + s_n_llhttp__internal__n_span_start_stub_path_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_1; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server_with_at: + s_n_llhttp__internal__n_url_server_with_at: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, + 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server_with_at; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 6: { + goto s_n_llhttp__internal__n_span_start_stub_path_1; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 8: { + p++; + goto s_n_llhttp__internal__n_error_82; + } + default: { + goto s_n_llhttp__internal__n_error_83; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server: + s_n_llhttp__internal__n_url_server: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, + 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 6: { + goto s_n_llhttp__internal__n_span_start_stub_path; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 8: { + p++; + goto s_n_llhttp__internal__n_url_server_with_at; + } + default: { + goto s_n_llhttp__internal__n_error_84; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim_1: + s_n_llhttp__internal__n_url_schema_delim_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim_1; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_url_server; + } + default: { + goto s_n_llhttp__internal__n_error_85; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim: + s_n_llhttp__internal__n_url_schema_delim: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case '/': { + p++; + goto s_n_llhttp__internal__n_url_schema_delim_1; + } + default: { + goto s_n_llhttp__internal__n_error_85; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_schema: + s_n_llhttp__internal__n_span_end_stub_schema: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_schema; + } + p++; + goto s_n_llhttp__internal__n_url_schema_delim; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema: + s_n_llhttp__internal__n_url_schema: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_schema; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_stub_schema; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_86; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_start: + s_n_llhttp__internal__n_url_start: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_start; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_start_stub_path_2; + } + case 3: { + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_87; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: + s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_entry_normal: + s_n_llhttp__internal__n_url_entry_normal: { + if (p == endp) { + return s_n_llhttp__internal__n_url_entry_normal; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url: + s_n_llhttp__internal__n_span_start_llhttp__on_url: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_server; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_entry_connect: + s_n_llhttp__internal__n_url_entry_connect: { + if (p == endp) { + return s_n_llhttp__internal__n_url_entry_connect; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_spaces_before_url: + s_n_llhttp__internal__n_req_spaces_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_spaces_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_invoke_is_equal_method; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_first_space_before_url: + s_n_llhttp__internal__n_req_first_space_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_first_space_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_error_88; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: { + switch (llhttp__on_method_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_first_space_before_url; + case 21: + goto s_n_llhttp__internal__n_pause_26; + default: + goto s_n_llhttp__internal__n_error_107; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_2: + s_n_llhttp__internal__n_after_start_req_2: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_2; + } + switch (*p) { + case 'L': { + p++; + match = 19; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_3: + s_n_llhttp__internal__n_after_start_req_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 36; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_1: + s_n_llhttp__internal__n_after_start_req_1: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_1; + } + switch (*p) { + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_2; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_3; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_4: + s_n_llhttp__internal__n_after_start_req_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_4; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 16; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_6: + s_n_llhttp__internal__n_after_start_req_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_6; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 22; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_8: + s_n_llhttp__internal__n_after_start_req_8: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_8; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_8; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_9: + s_n_llhttp__internal__n_after_start_req_9: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_9; + } + switch (*p) { + case 'Y': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_7: + s_n_llhttp__internal__n_after_start_req_7: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_7; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_8; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_9; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_5: + s_n_llhttp__internal__n_after_start_req_5: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_5; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_after_start_req_6; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_7; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_12: + s_n_llhttp__internal__n_after_start_req_12: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_12; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_12; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_13: + s_n_llhttp__internal__n_after_start_req_13: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_13; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 35; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_13; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_11: + s_n_llhttp__internal__n_after_start_req_11: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_11; + } + switch (*p) { + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_12; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_13; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_10: + s_n_llhttp__internal__n_after_start_req_10: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_10; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_11; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_14: + s_n_llhttp__internal__n_after_start_req_14: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_14; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 45; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_14; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_17: + s_n_llhttp__internal__n_after_start_req_17: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_17; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 41; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_17; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_16: + s_n_llhttp__internal__n_after_start_req_16: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_16; + } + switch (*p) { + case '_': { + p++; + goto s_n_llhttp__internal__n_after_start_req_17; + } + default: { + match = 1; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_15: + s_n_llhttp__internal__n_after_start_req_15: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_15; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_after_start_req_16; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_15; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_18: + s_n_llhttp__internal__n_after_start_req_18: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_18; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_18; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_20: + s_n_llhttp__internal__n_after_start_req_20: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_20; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 31; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_20; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_21: + s_n_llhttp__internal__n_after_start_req_21: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_21; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_21; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_19: + s_n_llhttp__internal__n_after_start_req_19: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_19; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_after_start_req_20; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_21; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_23: + s_n_llhttp__internal__n_after_start_req_23: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_23; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 24; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_23; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_24: + s_n_llhttp__internal__n_after_start_req_24: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_24; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 23; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_24; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_26: + s_n_llhttp__internal__n_after_start_req_26: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_26; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 21; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_26; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_28: + s_n_llhttp__internal__n_after_start_req_28: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_28; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 30; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_28; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_29: + s_n_llhttp__internal__n_after_start_req_29: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_29; + } + switch (*p) { + case 'L': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_27: + s_n_llhttp__internal__n_after_start_req_27: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_27; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_28; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_29; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_25: + s_n_llhttp__internal__n_after_start_req_25: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_25; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_26; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_27; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_30: + s_n_llhttp__internal__n_after_start_req_30: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_30; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_30; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_22: + s_n_llhttp__internal__n_after_start_req_22: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_22; + } + switch (*p) { + case '-': { + p++; + goto s_n_llhttp__internal__n_after_start_req_23; + } + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_24; + } + case 'K': { + p++; + goto s_n_llhttp__internal__n_after_start_req_25; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_30; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_31: + s_n_llhttp__internal__n_after_start_req_31: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_31; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 25; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_31; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_32: + s_n_llhttp__internal__n_after_start_req_32: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_32; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_32; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_35: + s_n_llhttp__internal__n_after_start_req_35: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_35; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 28; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_35; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_36: + s_n_llhttp__internal__n_after_start_req_36: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_36; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 39; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_36; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_34: + s_n_llhttp__internal__n_after_start_req_34: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_34; + } + switch (*p) { + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_35; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_36; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_37: + s_n_llhttp__internal__n_after_start_req_37: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_37; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 38; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_37; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_38: + s_n_llhttp__internal__n_after_start_req_38: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_38; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_38; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_42: + s_n_llhttp__internal__n_after_start_req_42: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_42; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_42; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_43: + s_n_llhttp__internal__n_after_start_req_43: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_43; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_43; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_41: + s_n_llhttp__internal__n_after_start_req_41: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_41; + } + switch (*p) { + case 'F': { + p++; + goto s_n_llhttp__internal__n_after_start_req_42; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_43; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_40: + s_n_llhttp__internal__n_after_start_req_40: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_40; + } + switch (*p) { + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_41; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_39: + s_n_llhttp__internal__n_after_start_req_39: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_39; + } + switch (*p) { + case 'I': { + p++; + match = 34; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_40; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_45: + s_n_llhttp__internal__n_after_start_req_45: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_45; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 29; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_45; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_44: + s_n_llhttp__internal__n_after_start_req_44: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_44; + } + switch (*p) { + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_45; + } + case 'T': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_33: + s_n_llhttp__internal__n_after_start_req_33: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_33; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_34; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_37; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_38; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_39; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_44; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_46: + s_n_llhttp__internal__n_after_start_req_46: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_46; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 46; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_46; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_49: + s_n_llhttp__internal__n_after_start_req_49: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_49; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 17; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_49; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_50: + s_n_llhttp__internal__n_after_start_req_50: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_50; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 44; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_50; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_51: + s_n_llhttp__internal__n_after_start_req_51: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_51; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 43; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_51; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_52: + s_n_llhttp__internal__n_after_start_req_52: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_52; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 20; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_52; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_48: + s_n_llhttp__internal__n_after_start_req_48: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_48; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_49; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_50; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_after_start_req_51; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_52; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_47: + s_n_llhttp__internal__n_after_start_req_47: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_47; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_48; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_55: + s_n_llhttp__internal__n_after_start_req_55: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_55; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_55; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_57: + s_n_llhttp__internal__n_after_start_req_57: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_57; + } + switch (*p) { + case 'P': { + p++; + match = 37; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_58: + s_n_llhttp__internal__n_after_start_req_58: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_58; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 42; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_58; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_56: + s_n_llhttp__internal__n_after_start_req_56: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_56; + } + switch (*p) { + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_57; + } + case '_': { + p++; + goto s_n_llhttp__internal__n_after_start_req_58; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_54: + s_n_llhttp__internal__n_after_start_req_54: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_54; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_55; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_56; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_59: + s_n_llhttp__internal__n_after_start_req_59: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_59; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 33; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_59; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_60: + s_n_llhttp__internal__n_after_start_req_60: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_60; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 26; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_60; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_53: + s_n_llhttp__internal__n_after_start_req_53: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_53; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_54; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_59; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_60; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_62: + s_n_llhttp__internal__n_after_start_req_62: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_62; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 40; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_62; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_63: + s_n_llhttp__internal__n_after_start_req_63: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_63; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_63; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_61: + s_n_llhttp__internal__n_after_start_req_61: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_61; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_62; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_63; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_66: + s_n_llhttp__internal__n_after_start_req_66: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_66; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 18; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_66; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_68: + s_n_llhttp__internal__n_after_start_req_68: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_68; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 32; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_68; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_69: + s_n_llhttp__internal__n_after_start_req_69: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_69; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_69; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_67: + s_n_llhttp__internal__n_after_start_req_67: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_67; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_after_start_req_68; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_69; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_70: + s_n_llhttp__internal__n_after_start_req_70: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_70; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 27; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_70; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_65: + s_n_llhttp__internal__n_after_start_req_65: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_65; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_66; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_67; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_70; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req_64: + s_n_llhttp__internal__n_after_start_req_64: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_64; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_65; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_after_start_req: + s_n_llhttp__internal__n_after_start_req: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_1; + } + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_4; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_5; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_after_start_req_10; + } + case 'F': { + p++; + goto s_n_llhttp__internal__n_after_start_req_14; + } + case 'G': { + p++; + goto s_n_llhttp__internal__n_after_start_req_15; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_after_start_req_18; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_19; + } + case 'M': { + p++; + goto s_n_llhttp__internal__n_after_start_req_22; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_31; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_32; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_33; + } + case 'Q': { + p++; + goto s_n_llhttp__internal__n_after_start_req_46; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_47; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_53; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_61; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_64; + } + default: { + goto s_n_llhttp__internal__n_error_108; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_method_1: + s_n_llhttp__internal__n_span_start_llhttp__on_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_method; + goto s_n_llhttp__internal__n_after_start_req; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_line_almost_done: + s_n_llhttp__internal__n_res_line_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_res_line_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_30: + s_n_llhttp__internal__n_invoke_test_lenient_flags_30: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_94; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status: + s_n_llhttp__internal__n_res_status: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; + } + default: { + p++; + goto s_n_llhttp__internal__n_res_status; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_status: + s_n_llhttp__internal__n_span_start_llhttp__on_status: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_status; + goto s_n_llhttp__internal__n_res_status; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_otherwise: + s_n_llhttp__internal__n_res_status_code_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_otherwise; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + default: { + goto s_n_llhttp__internal__n_error_95; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_digit_3: + s_n_llhttp__internal__n_res_status_code_digit_3: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_3; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + default: { + goto s_n_llhttp__internal__n_error_97; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_digit_2: + s_n_llhttp__internal__n_res_status_code_digit_2: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_2; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + default: { + goto s_n_llhttp__internal__n_error_99; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_digit_1: + s_n_llhttp__internal__n_res_status_code_digit_1: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_1; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_101; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_after_version: + s_n_llhttp__internal__n_res_after_version: { + if (p == endp) { + return s_n_llhttp__internal__n_res_after_version; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_update_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_102; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: { + switch (llhttp__on_version_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_res_after_version; + case 21: + goto s_n_llhttp__internal__n_pause_25; + default: + goto s_n_llhttp__internal__n_error_90; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_89: + s_n_llhttp__internal__n_error_89: { + state->error = 0x9; + state->reason = "Invalid HTTP version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_103: + s_n_llhttp__internal__n_error_103: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_minor: + s_n_llhttp__internal__n_res_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_104: + s_n_llhttp__internal__n_error_104: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_dot: + s_n_llhttp__internal__n_res_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_res_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_105: + s_n_llhttp__internal__n_error_105: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_major: + s_n_llhttp__internal__n_res_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_version_1: + s_n_llhttp__internal__n_span_start_llhttp__on_version_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_version; + goto s_n_llhttp__internal__n_res_http_major; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_res: + s_n_llhttp__internal__n_start_res: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_res; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_res; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_109; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: { + switch (llhttp__on_method_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_first_space_before_url; + case 21: + goto s_n_llhttp__internal__n_pause_23; + default: + goto s_n_llhttp__internal__n_error_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_2: + s_n_llhttp__internal__n_req_or_res_method_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_type_1: + s_n_llhttp__internal__n_invoke_update_type_1: { + switch (llhttp__internal__c_update_type_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_3: + s_n_llhttp__internal__n_req_or_res_method_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_1: + s_n_llhttp__internal__n_req_or_res_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_1; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_2; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_3; + } + default: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method: + s_n_llhttp__internal__n_req_or_res_method: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_106; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_method: + s_n_llhttp__internal__n_span_start_llhttp__on_method: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_method; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_method; + goto s_n_llhttp__internal__n_req_or_res_method; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_or_res: + s_n_llhttp__internal__n_start_req_or_res: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_or_res; + } + switch (*p) { + case 'H': { + goto s_n_llhttp__internal__n_span_start_llhttp__on_method; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_type_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_type: + s_n_llhttp__internal__n_invoke_load_type: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + case 2: + goto s_n_llhttp__internal__n_start_res; + default: + goto s_n_llhttp__internal__n_start_req_or_res; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_finish: + s_n_llhttp__internal__n_invoke_update_finish: { + switch (llhttp__internal__c_update_finish(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start: + s_n_llhttp__internal__n_start: { + if (p == endp) { + return s_n_llhttp__internal__n_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_initial_message_completed; + } + } + /* UNREACHABLE */; + abort(); + } + default: + /* UNREACHABLE */ + abort(); + } + s_n_llhttp__internal__n_error_2: { + state->error = 0x7; + state->reason = "Invalid characters in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_2: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_initial_message_completed: { + switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_finish_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_content_length: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_8: { + state->error = 0x5; + state->reason = "Data after `Connection: close`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_3: { + switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_closed; + default: + goto s_n_llhttp__internal__n_error_8; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_2: { + switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; + default: + goto s_n_llhttp__internal__n_closed; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_1: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_13: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_38: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_15: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_40: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_15; + default: + goto s_n_llhttp__internal__n_error_40; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_2: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_9: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_pause_1; + case 21: + goto s_n_llhttp__internal__n_pause_2; + default: + goto s_n_llhttp__internal__n_error_9; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_36: { + state->error = 0xc; + state->reason = "Chunk size overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_10: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_4: { + switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_otherwise; + default: + goto s_n_llhttp__internal__n_error_10; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_3: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_14: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_content_length_1; + case 21: + goto s_n_llhttp__internal__n_pause_3; + default: + goto s_n_llhttp__internal__n_error_14; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_13: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk data"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_6: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + default: + goto s_n_llhttp__internal__n_error_13; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_15: { + state->error = 0x2; + state->reason = "Expected LF after chunk data"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_7: { + switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + default: + goto s_n_llhttp__internal__n_error_15; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_chunk_data_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags: { + switch (llhttp__internal__c_or_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_4: { + state->error = 0x15; + state->reason = "on_chunk_header pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_12: { + state->error = 0x13; + state->reason = "`on_chunk_header` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { + switch (llhttp__on_chunk_header(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_content_length; + case 21: + goto s_n_llhttp__internal__n_pause_4; + default: + goto s_n_llhttp__internal__n_error_12; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_16: { + state->error = 0x2; + state->reason = "Expected LF after chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_8: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + default: + goto s_n_llhttp__internal__n_error_16; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_11: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_5: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_11; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_17: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_18: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_20: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension name"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_5: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_19: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_6: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_21: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_7: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_22: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_25: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_8: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_24: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_9: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_26: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_28: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_11: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_28; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_29: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions quote value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_10: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_27: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_30; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_31; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_11: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_32: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_33; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_12: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_23: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extension_value; + case 21: + goto s_n_llhttp__internal__n_pause_12; + default: + goto s_n_llhttp__internal__n_error_23; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_34; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_35: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length: { + switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_36; + default: + goto s_n_llhttp__internal__n_chunk_size; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_37: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_3: { + switch (llhttp__internal__c_update_finish_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_39: { + state->error = 0xf; + state->reason = "Request has invalid `Transfer-Encoding`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_7: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + case 21: + goto s_n_llhttp__internal__n_pause; + default: + goto s_n_llhttp__internal__n_error_7; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_1: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_2: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_upgrade: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_14: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_6: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_14; + default: + goto s_n_llhttp__internal__n_error_6; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_1: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags; + default: + goto s_n_llhttp__internal__n_error_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_17: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_42: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_17; + default: + goto s_n_llhttp__internal__n_error_42; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_3: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_4: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_upgrade_1: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_16: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_41: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_3; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade_1; + case 21: + goto s_n_llhttp__internal__n_pause_16; + default: + goto s_n_llhttp__internal__n_error_41; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_1: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_43: { + state->error = 0x2; + state->reason = "Expected LF after headers"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_12: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags_1; + default: + goto s_n_llhttp__internal__n_error_43; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_44: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_5; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_13: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_field_colon_discard_ws; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_60: { + state->error = 0xb; + state->reason = "Content-Length can't be present with Transfer-Encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_47: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_15: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_ws; + default: + goto s_n_llhttp__internal__n_error_47; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_49: { + state->error = 0xb; + state->reason = "Empty Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_18: { + state->error = 0x15; + state->reason = "on_header_value_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_48: { + state->error = 0x1d; + state->reason = "`on_header_value_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_5: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_6: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_7: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_8: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_2: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_5; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_6; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_7; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_8; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_1: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_error_49; + default: + goto s_n_llhttp__internal__n_invoke_load_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_46: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_14: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_lws; + default: + goto s_n_llhttp__internal__n_error_46; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_50: { + state->error = 0x2; + state->reason = "Expected LF after CR"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_16: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_lws; + default: + goto s_n_llhttp__internal__n_error_50; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_1: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_4: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 8: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_52: { + state->error = 0xa; + state->reason = "Unexpected whitespace after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_18: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_load_header_state_4; + default: + goto s_n_llhttp__internal__n_error_52; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_2: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_9: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_10: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_11: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_12: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_5: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_9; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_10; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_11; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_12; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_53: { + state->error = 0x3; + state->reason = "Missing expected LF after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_51: { + state->error = 0x19; + state->reason = "Missing expected CR after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54; + return s_error; + } + goto s_n_llhttp__internal__n_error_54; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_19: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_lenient; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_4: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_13: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_14: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_15: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_16: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_6: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_13; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_14; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_15; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_16; + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_5: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_token; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_3: { + switch (llhttp__internal__c_update_header_state_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_6: { + switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_7: { + switch (llhttp__internal__c_update_header_state_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56; + return s_error; + } + goto s_n_llhttp__internal__n_error_56; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { + switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6; + default: + goto s_n_llhttp__internal__n_header_value_content_length; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_17: { + switch (llhttp__internal__c_or_flags_17(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57; + return s_error; + } + goto s_n_llhttp__internal__n_error_57; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_55: { + state->error = 0x4; + state->reason = "Duplicate Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_2: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_value_content_length; + default: + goto s_n_llhttp__internal__n_error_55; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_59; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_8: { + switch (llhttp__internal__c_update_header_state_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_58; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_20: { + switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8; + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_type_1: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20; + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_9: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_and_flags: { + switch (llhttp__internal__c_and_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_19: { + switch (llhttp__internal__c_or_flags_18(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_and_flags; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_21: { + switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_19; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_type_2: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_19; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_18: { + switch (llhttp__internal__c_or_flags_18(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_and_flags; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_3: { + switch (llhttp__internal__c_test_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_load_type_2; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_18; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_20: { + switch (llhttp__internal__c_or_flags_20(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_3: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_connection; + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_2; + case 3: + goto s_n_llhttp__internal__n_invoke_test_flags_3; + case 4: + goto s_n_llhttp__internal__n_invoke_or_flags_20; + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_22: { + switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_60; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_4: { + switch (llhttp__internal__c_test_flags_4(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_61: { + state->error = 0xf; + state->reason = "Transfer-Encoding can't be present with Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_23: { + switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_61; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_5: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_19: { + state->error = 0x15; + state->reason = "on_header_field_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_45: { + state->error = 0x1c; + state->reason = "`on_header_field_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_62: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_10: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_header_state: { + switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_header_field_colon; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_11: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_4: { + state->error = 0x1e; + state->reason = "Unexpected space after start line"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_field_start; + default: + goto s_n_llhttp__internal__n_error_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_20: { + state->error = 0x15; + state->reason = "on_url_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_3: { + state->error = 0x1a; + state->reason = "`on_url_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: { + switch (llhttp__on_url_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_headers_start; + case 21: + goto s_n_llhttp__internal__n_pause_20; + default: + goto s_n_llhttp__internal__n_error_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_minor: { + switch (llhttp__internal__c_update_http_minor(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_major: { + switch (llhttp__internal__c_update_http_major(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_http_minor; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_63: { + state->error = 0x7; + state->reason = "Expected CRLF"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_71: { + state->error = 0x17; + state->reason = "Pause on PRI/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_72: { + state->error = 0x9; + state->reason = "Expected HTTP/2 Connection Preface"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_69: { + state->error = 0x2; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_26: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_headers_start; + default: + goto s_n_llhttp__internal__n_error_69; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_68: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_25: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_req_http_complete_crlf; + default: + goto s_n_llhttp__internal__n_error_68; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_70: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_21: { + state->error = 0x15; + state->reason = "on_version_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_67: { + state->error = 0x21; + state->reason = "`on_version_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_66; + return s_error; + } + goto s_n_llhttp__internal__n_error_66; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 9: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_1: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_2: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_major: { + switch (llhttp__internal__c_load_http_major(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_http_minor; + case 1: + goto s_n_llhttp__internal__n_invoke_load_http_minor_1; + case 2: + goto s_n_llhttp__internal__n_invoke_load_http_minor_2; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_24: { + switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_invoke_load_http_major; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_73; + return s_error; + } + goto s_n_llhttp__internal__n_error_73; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74; + return s_error; + } + goto s_n_llhttp__internal__n_error_74; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75; + return s_error; + } + goto s_n_llhttp__internal__n_error_75; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_65: { + state->error = 0x8; + state->reason = "Invalid method for HTTP/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_method: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 1: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 2: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 4: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 5: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 6: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 7: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 8: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 9: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 10: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 11: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 12: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 13: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 14: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 15: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 16: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 17: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 18: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 19: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 20: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 21: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 22: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 23: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 24: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 25: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 26: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 27: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 28: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 29: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 30: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 31: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 32: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 33: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 34: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 46: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + default: + goto s_n_llhttp__internal__n_error_65; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_78: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_76: { + state->error = 0x8; + state->reason = "Expected SOURCE method for ICE/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_method_2: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 33: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + default: + goto s_n_llhttp__internal__n_error_76; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_77: { + state->error = 0x8; + state->reason = "Invalid method for RTSP/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_method_3: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 6: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 35: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 36: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 37: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 38: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 39: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 40: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 41: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 42: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 43: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 44: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + case 45: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + default: + goto s_n_llhttp__internal__n_error_77; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_22: { + state->error = 0x15; + state->reason = "on_url_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_64: { + state->error = 0x1a; + state->reason = "`on_url_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: { + switch (llhttp__on_url_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_http_start; + case 21: + goto s_n_llhttp__internal__n_pause_22; + default: + goto s_n_llhttp__internal__n_error_64; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_79: { + state->error = 0x7; + state->reason = "Invalid char in url fragment start"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_80: { + state->error = 0x7; + state->reason = "Invalid char in url query"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_81: { + state->error = 0x7; + state->reason = "Invalid char in url path"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_82: { + state->error = 0x7; + state->reason = "Double @ in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_83: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_84: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_85: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_86: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_87: { + state->error = 0x7; + state->reason = "Unexpected start char in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_is_equal_method: { + switch (llhttp__internal__c_is_equal_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_url_entry_normal; + default: + goto s_n_llhttp__internal__n_url_entry_connect; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_88: { + state->error = 0x6; + state->reason = "Expected space after method"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_26: { + state->error = 0x15; + state->reason = "on_method_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_107: { + state->error = 0x20; + state->reason = "`on_method_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_method_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method_1: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_108: { + state->error = 0x6; + state->reason = "Invalid method encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_100: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_98: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_96: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_24: { + state->error = 0x15; + state->reason = "on_status_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_92: { + state->error = 0x1b; + state->reason = "`on_status_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: { + switch (llhttp__on_status_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_headers_start; + case 21: + goto s_n_llhttp__internal__n_pause_24; + default: + goto s_n_llhttp__internal__n_error_92; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_91: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_28: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_91; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_93: { + state->error = 0x2; + state->reason = "Expected LF after CR"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_29: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_93; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_94: { + state->error = 0x19; + state->reason = "Missing expected CR after response line"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_95: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code_2: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_96; + default: + goto s_n_llhttp__internal__n_res_status_code_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_97: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code_1: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_98; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_99: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_100; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_101: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_status_code: { + switch (llhttp__internal__c_update_status_code(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_res_status_code_digit_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_102: { + state->error = 0x9; + state->reason = "Expected space after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_25: { + state->error = 0x15; + state->reason = "on_version_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_90: { + state->error = 0x21; + state->reason = "`on_version_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_89; + return s_error; + } + goto s_n_llhttp__internal__n_error_89; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_3: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 9: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_4: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_minor_5: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_http_major_1: { + switch (llhttp__internal__c_load_http_major(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_http_minor_3; + case 1: + goto s_n_llhttp__internal__n_invoke_load_http_minor_4; + case 2: + goto s_n_llhttp__internal__n_invoke_load_http_minor_5; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_27: { + switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_invoke_load_http_major_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor_1: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_103; + return s_error; + } + goto s_n_llhttp__internal__n_error_103; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_104; + return s_error; + } + goto s_n_llhttp__internal__n_error_104; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major_1: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_res_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_105; + return s_error; + } + goto s_n_llhttp__internal__n_error_105; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_109: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_23: { + state->error = 0x15; + state->reason = "on_method_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_1: { + state->error = 0x20; + state->reason = "`on_method_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_method: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_method; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_update_type; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_106: { + state->error = 0x8; + state->reason = "Invalid word encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_method_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_update_type_1; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type_2: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_27: { + state->error = 0x15; + state->reason = "on_message_begin pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error: { + state->error = 0x10; + state->reason = "`on_message_begin` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { + switch (llhttp__on_message_begin(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_type; + case 21: + goto s_n_llhttp__internal__n_pause_27; + default: + goto s_n_llhttp__internal__n_error; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_28: { + state->error = 0x15; + state->reason = "on_reset pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_110: { + state->error = 0x1f; + state->reason = "`on_reset` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_reset: { + switch (llhttp__on_reset(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_finish; + case 21: + goto s_n_llhttp__internal__n_pause_28; + default: + goto s_n_llhttp__internal__n_error_110; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_initial_message_completed: { + switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_reset; + default: + goto s_n_llhttp__internal__n_invoke_update_finish; + } + /* UNREACHABLE */; + abort(); + } +} + +int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { + llparse_state_t next; + + /* check lingering errors */ + if (state->error != 0) { + return state->error; + } + + /* restart spans */ + if (state->_span_pos0 != NULL) { + state->_span_pos0 = (void*) p; + } + + next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); + if (next == s_error) { + return state->error; + } + state->_current = (void*) (intptr_t) next; + + /* execute spans */ + if (state->_span_pos0 != NULL) { + int error; + + error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); + if (error != 0) { + state->error = error; + state->error_pos = endp; + return error; + } + } + + return 0; +} \ No newline at end of file diff --git a/lib/llhttp/llhttp.h b/lib/llhttp/llhttp.h new file mode 100644 index 0000000..37b7934 --- /dev/null +++ b/lib/llhttp/llhttp.h @@ -0,0 +1,903 @@ + +#ifndef INCLUDE_LLHTTP_H_ +#define INCLUDE_LLHTTP_H_ + +#define LLHTTP_VERSION_MAJOR 9 +#define LLHTTP_VERSION_MINOR 2 +#define LLHTTP_VERSION_PATCH 1 + +#ifndef INCLUDE_LLHTTP_ITSELF_H_ +#define INCLUDE_LLHTTP_ITSELF_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct llhttp__internal_s llhttp__internal_t; +struct llhttp__internal_s { + int32_t _index; + void* _span_pos0; + void* _span_cb0; + int32_t error; + const char* reason; + const char* error_pos; + void* data; + void* _current; + uint64_t content_length; + uint8_t type; + uint8_t method; + uint8_t http_major; + uint8_t http_minor; + uint8_t header_state; + uint16_t lenient_flags; + uint8_t upgrade; + uint8_t finish; + uint16_t flags; + uint16_t status_code; + uint8_t initial_message_completed; + void* settings; +}; + +int llhttp__internal_init(llhttp__internal_t* s); +int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_ITSELF_H_ */ + + +#ifndef LLLLHTTP_C_HEADERS_ +#define LLLLHTTP_C_HEADERS_ +#ifdef __cplusplus +extern "C" { +#endif + +enum llhttp_errno { + HPE_OK = 0, + HPE_INTERNAL = 1, + HPE_STRICT = 2, + HPE_CR_EXPECTED = 25, + HPE_LF_EXPECTED = 3, + HPE_UNEXPECTED_CONTENT_LENGTH = 4, + HPE_UNEXPECTED_SPACE = 30, + HPE_CLOSED_CONNECTION = 5, + HPE_INVALID_METHOD = 6, + HPE_INVALID_URL = 7, + HPE_INVALID_CONSTANT = 8, + HPE_INVALID_VERSION = 9, + HPE_INVALID_HEADER_TOKEN = 10, + HPE_INVALID_CONTENT_LENGTH = 11, + HPE_INVALID_CHUNK_SIZE = 12, + HPE_INVALID_STATUS = 13, + HPE_INVALID_EOF_STATE = 14, + HPE_INVALID_TRANSFER_ENCODING = 15, + HPE_CB_MESSAGE_BEGIN = 16, + HPE_CB_HEADERS_COMPLETE = 17, + HPE_CB_MESSAGE_COMPLETE = 18, + HPE_CB_CHUNK_HEADER = 19, + HPE_CB_CHUNK_COMPLETE = 20, + HPE_PAUSED = 21, + HPE_PAUSED_UPGRADE = 22, + HPE_PAUSED_H2_UPGRADE = 23, + HPE_USER = 24, + HPE_CB_URL_COMPLETE = 26, + HPE_CB_STATUS_COMPLETE = 27, + HPE_CB_METHOD_COMPLETE = 32, + HPE_CB_VERSION_COMPLETE = 33, + HPE_CB_HEADER_FIELD_COMPLETE = 28, + HPE_CB_HEADER_VALUE_COMPLETE = 29, + HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34, + HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35, + HPE_CB_RESET = 31 +}; +typedef enum llhttp_errno llhttp_errno_t; + +enum llhttp_flags { + F_CONNECTION_KEEP_ALIVE = 0x1, + F_CONNECTION_CLOSE = 0x2, + F_CONNECTION_UPGRADE = 0x4, + F_CHUNKED = 0x8, + F_UPGRADE = 0x10, + F_CONTENT_LENGTH = 0x20, + F_SKIPBODY = 0x40, + F_TRAILING = 0x80, + F_TRANSFER_ENCODING = 0x200 +}; +typedef enum llhttp_flags llhttp_flags_t; + +enum llhttp_lenient_flags { + LENIENT_HEADERS = 0x1, + LENIENT_CHUNKED_LENGTH = 0x2, + LENIENT_KEEP_ALIVE = 0x4, + LENIENT_TRANSFER_ENCODING = 0x8, + LENIENT_VERSION = 0x10, + LENIENT_DATA_AFTER_CLOSE = 0x20, + LENIENT_OPTIONAL_LF_AFTER_CR = 0x40, + LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80, + LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100, + LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200 +}; +typedef enum llhttp_lenient_flags llhttp_lenient_flags_t; + +enum llhttp_type { + HTTP_BOTH = 0, + HTTP_REQUEST = 1, + HTTP_RESPONSE = 2 +}; +typedef enum llhttp_type llhttp_type_t; + +enum llhttp_finish { + HTTP_FINISH_SAFE = 0, + HTTP_FINISH_SAFE_WITH_CB = 1, + HTTP_FINISH_UNSAFE = 2 +}; +typedef enum llhttp_finish llhttp_finish_t; + +enum llhttp_method { + HTTP_DELETE = 0, + HTTP_GET = 1, + HTTP_HEAD = 2, + HTTP_POST = 3, + HTTP_PUT = 4, + HTTP_CONNECT = 5, + HTTP_OPTIONS = 6, + HTTP_TRACE = 7, + HTTP_COPY = 8, + HTTP_LOCK = 9, + HTTP_MKCOL = 10, + HTTP_MOVE = 11, + HTTP_PROPFIND = 12, + HTTP_PROPPATCH = 13, + HTTP_SEARCH = 14, + HTTP_UNLOCK = 15, + HTTP_BIND = 16, + HTTP_REBIND = 17, + HTTP_UNBIND = 18, + HTTP_ACL = 19, + HTTP_REPORT = 20, + HTTP_MKACTIVITY = 21, + HTTP_CHECKOUT = 22, + HTTP_MERGE = 23, + HTTP_MSEARCH = 24, + HTTP_NOTIFY = 25, + HTTP_SUBSCRIBE = 26, + HTTP_UNSUBSCRIBE = 27, + HTTP_PATCH = 28, + HTTP_PURGE = 29, + HTTP_MKCALENDAR = 30, + HTTP_LINK = 31, + HTTP_UNLINK = 32, + HTTP_SOURCE = 33, + HTTP_PRI = 34, + HTTP_DESCRIBE = 35, + HTTP_ANNOUNCE = 36, + HTTP_SETUP = 37, + HTTP_PLAY = 38, + HTTP_PAUSE = 39, + HTTP_TEARDOWN = 40, + HTTP_GET_PARAMETER = 41, + HTTP_SET_PARAMETER = 42, + HTTP_REDIRECT = 43, + HTTP_RECORD = 44, + HTTP_FLUSH = 45, + HTTP_QUERY = 46 +}; +typedef enum llhttp_method llhttp_method_t; + +enum llhttp_status { + HTTP_STATUS_CONTINUE = 100, + HTTP_STATUS_SWITCHING_PROTOCOLS = 101, + HTTP_STATUS_PROCESSING = 102, + HTTP_STATUS_EARLY_HINTS = 103, + HTTP_STATUS_RESPONSE_IS_STALE = 110, + HTTP_STATUS_REVALIDATION_FAILED = 111, + HTTP_STATUS_DISCONNECTED_OPERATION = 112, + HTTP_STATUS_HEURISTIC_EXPIRATION = 113, + HTTP_STATUS_MISCELLANEOUS_WARNING = 199, + HTTP_STATUS_OK = 200, + HTTP_STATUS_CREATED = 201, + HTTP_STATUS_ACCEPTED = 202, + HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + HTTP_STATUS_NO_CONTENT = 204, + HTTP_STATUS_RESET_CONTENT = 205, + HTTP_STATUS_PARTIAL_CONTENT = 206, + HTTP_STATUS_MULTI_STATUS = 207, + HTTP_STATUS_ALREADY_REPORTED = 208, + HTTP_STATUS_TRANSFORMATION_APPLIED = 214, + HTTP_STATUS_IM_USED = 226, + HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299, + HTTP_STATUS_MULTIPLE_CHOICES = 300, + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + HTTP_STATUS_NOT_MODIFIED = 304, + HTTP_STATUS_USE_PROXY = 305, + HTTP_STATUS_SWITCH_PROXY = 306, + HTTP_STATUS_TEMPORARY_REDIRECT = 307, + HTTP_STATUS_PERMANENT_REDIRECT = 308, + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED = 401, + HTTP_STATUS_PAYMENT_REQUIRED = 402, + HTTP_STATUS_FORBIDDEN = 403, + HTTP_STATUS_NOT_FOUND = 404, + HTTP_STATUS_METHOD_NOT_ALLOWED = 405, + HTTP_STATUS_NOT_ACCEPTABLE = 406, + HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + HTTP_STATUS_REQUEST_TIMEOUT = 408, + HTTP_STATUS_CONFLICT = 409, + HTTP_STATUS_GONE = 410, + HTTP_STATUS_LENGTH_REQUIRED = 411, + HTTP_STATUS_PRECONDITION_FAILED = 412, + HTTP_STATUS_PAYLOAD_TOO_LARGE = 413, + HTTP_STATUS_URI_TOO_LONG = 414, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, + HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416, + HTTP_STATUS_EXPECTATION_FAILED = 417, + HTTP_STATUS_IM_A_TEAPOT = 418, + HTTP_STATUS_PAGE_EXPIRED = 419, + HTTP_STATUS_ENHANCE_YOUR_CALM = 420, + HTTP_STATUS_MISDIRECTED_REQUEST = 421, + HTTP_STATUS_UNPROCESSABLE_ENTITY = 422, + HTTP_STATUS_LOCKED = 423, + HTTP_STATUS_FAILED_DEPENDENCY = 424, + HTTP_STATUS_TOO_EARLY = 425, + HTTP_STATUS_UPGRADE_REQUIRED = 426, + HTTP_STATUS_PRECONDITION_REQUIRED = 428, + HTTP_STATUS_TOO_MANY_REQUESTS = 429, + HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430, + HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HTTP_STATUS_LOGIN_TIMEOUT = 440, + HTTP_STATUS_NO_RESPONSE = 444, + HTTP_STATUS_RETRY_WITH = 449, + HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450, + HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451, + HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460, + HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463, + HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494, + HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495, + HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496, + HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497, + HTTP_STATUS_INVALID_TOKEN = 498, + HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499, + HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + HTTP_STATUS_NOT_IMPLEMENTED = 501, + HTTP_STATUS_BAD_GATEWAY = 502, + HTTP_STATUS_SERVICE_UNAVAILABLE = 503, + HTTP_STATUS_GATEWAY_TIMEOUT = 504, + HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505, + HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506, + HTTP_STATUS_INSUFFICIENT_STORAGE = 507, + HTTP_STATUS_LOOP_DETECTED = 508, + HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509, + HTTP_STATUS_NOT_EXTENDED = 510, + HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511, + HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520, + HTTP_STATUS_WEB_SERVER_IS_DOWN = 521, + HTTP_STATUS_CONNECTION_TIMEOUT = 522, + HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523, + HTTP_STATUS_TIMEOUT_OCCURED = 524, + HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525, + HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526, + HTTP_STATUS_RAILGUN_ERROR = 527, + HTTP_STATUS_SITE_IS_OVERLOADED = 529, + HTTP_STATUS_SITE_IS_FROZEN = 530, + HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561, + HTTP_STATUS_NETWORK_READ_TIMEOUT = 598, + HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599 +}; +typedef enum llhttp_status llhttp_status_t; + +#define HTTP_ERRNO_MAP(XX) \ + XX(0, OK, OK) \ + XX(1, INTERNAL, INTERNAL) \ + XX(2, STRICT, STRICT) \ + XX(25, CR_EXPECTED, CR_EXPECTED) \ + XX(3, LF_EXPECTED, LF_EXPECTED) \ + XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \ + XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \ + XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \ + XX(6, INVALID_METHOD, INVALID_METHOD) \ + XX(7, INVALID_URL, INVALID_URL) \ + XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \ + XX(9, INVALID_VERSION, INVALID_VERSION) \ + XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \ + XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \ + XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ + XX(13, INVALID_STATUS, INVALID_STATUS) \ + XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ + XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \ + XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ + XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ + XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ + XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ + XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ + XX(21, PAUSED, PAUSED) \ + XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \ + XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \ + XX(24, USER, USER) \ + XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \ + XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \ + XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \ + XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \ + XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \ + XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \ + XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \ + XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \ + XX(31, CB_RESET, CB_RESET) \ + + +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + XX(30, MKCALENDAR, MKCALENDAR) \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + XX(33, SOURCE, SOURCE) \ + XX(46, QUERY, QUERY) \ + + +#define RTSP_METHOD_MAP(XX) \ + XX(1, GET, GET) \ + XX(3, POST, POST) \ + XX(6, OPTIONS, OPTIONS) \ + XX(35, DESCRIBE, DESCRIBE) \ + XX(36, ANNOUNCE, ANNOUNCE) \ + XX(37, SETUP, SETUP) \ + XX(38, PLAY, PLAY) \ + XX(39, PAUSE, PAUSE) \ + XX(40, TEARDOWN, TEARDOWN) \ + XX(41, GET_PARAMETER, GET_PARAMETER) \ + XX(42, SET_PARAMETER, SET_PARAMETER) \ + XX(43, REDIRECT, REDIRECT) \ + XX(44, RECORD, RECORD) \ + XX(45, FLUSH, FLUSH) \ + + +#define HTTP_ALL_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + XX(30, MKCALENDAR, MKCALENDAR) \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + XX(33, SOURCE, SOURCE) \ + XX(34, PRI, PRI) \ + XX(35, DESCRIBE, DESCRIBE) \ + XX(36, ANNOUNCE, ANNOUNCE) \ + XX(37, SETUP, SETUP) \ + XX(38, PLAY, PLAY) \ + XX(39, PAUSE, PAUSE) \ + XX(40, TEARDOWN, TEARDOWN) \ + XX(41, GET_PARAMETER, GET_PARAMETER) \ + XX(42, SET_PARAMETER, SET_PARAMETER) \ + XX(43, REDIRECT, REDIRECT) \ + XX(44, RECORD, RECORD) \ + XX(45, FLUSH, FLUSH) \ + XX(46, QUERY, QUERY) \ + + +#define HTTP_STATUS_MAP(XX) \ + XX(100, CONTINUE, CONTINUE) \ + XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \ + XX(102, PROCESSING, PROCESSING) \ + XX(103, EARLY_HINTS, EARLY_HINTS) \ + XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \ + XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \ + XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \ + XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \ + XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \ + XX(200, OK, OK) \ + XX(201, CREATED, CREATED) \ + XX(202, ACCEPTED, ACCEPTED) \ + XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \ + XX(204, NO_CONTENT, NO_CONTENT) \ + XX(205, RESET_CONTENT, RESET_CONTENT) \ + XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \ + XX(207, MULTI_STATUS, MULTI_STATUS) \ + XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \ + XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \ + XX(226, IM_USED, IM_USED) \ + XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \ + XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \ + XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \ + XX(302, FOUND, FOUND) \ + XX(303, SEE_OTHER, SEE_OTHER) \ + XX(304, NOT_MODIFIED, NOT_MODIFIED) \ + XX(305, USE_PROXY, USE_PROXY) \ + XX(306, SWITCH_PROXY, SWITCH_PROXY) \ + XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \ + XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \ + XX(400, BAD_REQUEST, BAD_REQUEST) \ + XX(401, UNAUTHORIZED, UNAUTHORIZED) \ + XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \ + XX(403, FORBIDDEN, FORBIDDEN) \ + XX(404, NOT_FOUND, NOT_FOUND) \ + XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \ + XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \ + XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \ + XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \ + XX(409, CONFLICT, CONFLICT) \ + XX(410, GONE, GONE) \ + XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \ + XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \ + XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \ + XX(414, URI_TOO_LONG, URI_TOO_LONG) \ + XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \ + XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \ + XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \ + XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \ + XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \ + XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \ + XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \ + XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \ + XX(423, LOCKED, LOCKED) \ + XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \ + XX(425, TOO_EARLY, TOO_EARLY) \ + XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \ + XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \ + XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \ + XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \ + XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \ + XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \ + XX(444, NO_RESPONSE, NO_RESPONSE) \ + XX(449, RETRY_WITH, RETRY_WITH) \ + XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \ + XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \ + XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \ + XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \ + XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \ + XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \ + XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \ + XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \ + XX(498, INVALID_TOKEN, INVALID_TOKEN) \ + XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \ + XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \ + XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \ + XX(502, BAD_GATEWAY, BAD_GATEWAY) \ + XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \ + XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \ + XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \ + XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \ + XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \ + XX(508, LOOP_DETECTED, LOOP_DETECTED) \ + XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \ + XX(510, NOT_EXTENDED, NOT_EXTENDED) \ + XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \ + XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \ + XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \ + XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \ + XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \ + XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \ + XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \ + XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \ + XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \ + XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \ + XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \ + XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \ + XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \ + XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* LLLLHTTP_C_HEADERS_ */ + + +#ifndef INCLUDE_LLHTTP_API_H_ +#define INCLUDE_LLHTTP_API_H_ +#ifdef __cplusplus +extern "C" { +#endif +#include + +#if defined(__wasm__) +#define LLHTTP_EXPORT __attribute__((visibility("default"))) +#elif defined(_WIN32) +#define LLHTTP_EXPORT __declspec(dllexport) +#else +#define LLHTTP_EXPORT +#endif + +typedef llhttp__internal_t llhttp_t; +typedef struct llhttp_settings_s llhttp_settings_t; + +typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); +typedef int (*llhttp_cb)(llhttp_t*); + +struct llhttp_settings_s { + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_begin; + + /* Possible return values 0, -1, HPE_USER */ + llhttp_data_cb on_url; + llhttp_data_cb on_status; + llhttp_data_cb on_method; + llhttp_data_cb on_version; + llhttp_data_cb on_header_field; + llhttp_data_cb on_header_value; + llhttp_data_cb on_chunk_extension_name; + llhttp_data_cb on_chunk_extension_value; + + /* Possible return values: + * 0 - Proceed normally + * 1 - Assume that request/response has no body, and proceed to parsing the + * next message + * 2 - Assume absence of body (as above) and make `llhttp_execute()` return + * `HPE_PAUSED_UPGRADE` + * -1 - Error + * `HPE_PAUSED` + */ + llhttp_cb on_headers_complete; + + /* Possible return values 0, -1, HPE_USER */ + llhttp_data_cb on_body; + + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_complete; + llhttp_cb on_url_complete; + llhttp_cb on_status_complete; + llhttp_cb on_method_complete; + llhttp_cb on_version_complete; + llhttp_cb on_header_field_complete; + llhttp_cb on_header_value_complete; + llhttp_cb on_chunk_extension_name_complete; + llhttp_cb on_chunk_extension_value_complete; + + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + * Possible return values 0, -1, `HPE_PAUSED` + */ + llhttp_cb on_chunk_header; + llhttp_cb on_chunk_complete; + llhttp_cb on_reset; +}; + +/* Initialize the parser with specific type and user settings. + * + * NOTE: lifetime of `settings` has to be at least the same as the lifetime of + * the `parser` here. In practice, `settings` has to be either a static + * variable or be allocated with `malloc`, `new`, etc. + */ +LLHTTP_EXPORT +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings); + +LLHTTP_EXPORT +llhttp_t* llhttp_alloc(llhttp_type_t type); + +LLHTTP_EXPORT +void llhttp_free(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_type(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_http_major(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_http_minor(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_method(llhttp_t* parser); + +LLHTTP_EXPORT +int llhttp_get_status_code(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_upgrade(llhttp_t* parser); + +/* Reset an already initialized parser back to the start state, preserving the + * existing parser type, callback settings, user data, and lenient flags. + */ +LLHTTP_EXPORT +void llhttp_reset(llhttp_t* parser); + +/* Initialize the settings object */ +LLHTTP_EXPORT +void llhttp_settings_init(llhttp_settings_t* settings); + +/* Parse full or partial request/response, invoking user callbacks along the + * way. + * + * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing + * interrupts, and such errno is returned from `llhttp_execute()`. If + * `HPE_PAUSED` was used as a errno, the execution can be resumed with + * `llhttp_resume()` call. + * + * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` + * is returned after fully parsing the request/response. If the user wishes to + * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. + * + * NOTE: if this function ever returns a non-pause type error, it will continue + * to return the same error upon each successive call up until `llhttp_init()` + * is called. + */ +LLHTTP_EXPORT +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); + +/* This method should be called when the other side has no further bytes to + * send (e.g. shutdown of readable side of the TCP connection.) + * + * Requests without `Content-Length` and other messages might require treating + * all incoming bytes as the part of the body, up to the last byte of the + * connection. This method will invoke `on_message_complete()` callback if the + * request was terminated safely. Otherwise a error code would be returned. + */ +LLHTTP_EXPORT +llhttp_errno_t llhttp_finish(llhttp_t* parser); + +/* Returns `1` if the incoming message is parsed until the last byte, and has + * to be completed by calling `llhttp_finish()` on EOF + */ +LLHTTP_EXPORT +int llhttp_message_needs_eof(const llhttp_t* parser); + +/* Returns `1` if there might be any other messages following the last that was + * successfully parsed. + */ +LLHTTP_EXPORT +int llhttp_should_keep_alive(const llhttp_t* parser); + +/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set + * appropriate error reason. + * + * Important: do not call this from user callbacks! User callbacks must return + * `HPE_PAUSED` if pausing is required. + */ +LLHTTP_EXPORT +void llhttp_pause(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. + */ +LLHTTP_EXPORT +void llhttp_resume(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` + */ +LLHTTP_EXPORT +void llhttp_resume_after_upgrade(llhttp_t* parser); + +/* Returns the latest return error */ +LLHTTP_EXPORT +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); + +/* Returns the verbal explanation of the latest returned error. + * + * Note: User callback should set error reason when returning the error. See + * `llhttp_set_error_reason()` for details. + */ +LLHTTP_EXPORT +const char* llhttp_get_error_reason(const llhttp_t* parser); + +/* Assign verbal description to the returned error. Must be called in user + * callbacks right before returning the errno. + * + * Note: `HPE_USER` error code might be useful in user callbacks. + */ +LLHTTP_EXPORT +void llhttp_set_error_reason(llhttp_t* parser, const char* reason); + +/* Returns the pointer to the last parsed byte before the returned error. The + * pointer is relative to the `data` argument of `llhttp_execute()`. + * + * Note: this method might be useful for counting the number of parsed bytes. + */ +LLHTTP_EXPORT +const char* llhttp_get_error_pos(const llhttp_t* parser); + +/* Returns textual name of error code */ +LLHTTP_EXPORT +const char* llhttp_errno_name(llhttp_errno_t err); + +/* Returns textual name of HTTP method */ +LLHTTP_EXPORT +const char* llhttp_method_name(llhttp_method_t method); + +/* Returns textual name of HTTP status */ +LLHTTP_EXPORT +const char* llhttp_status_name(llhttp_status_t status); + +/* Enables/disables lenient header value parsing (disabled by default). + * + * Lenient parsing disables header value token checks, extending llhttp's + * protocol support to highly non-compliant clients/server. No + * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when + * lenient parsing is "on". + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_headers(llhttp_t* parser, int enabled); + + +/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and + * `Content-Length` headers (disabled by default). + * + * Normally `llhttp` would error when `Transfer-Encoding` is present in + * conjunction with `Content-Length`. This error is important to prevent HTTP + * request smuggling, but may be less desirable for small number of cases + * involving legacy servers. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled); + + +/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0 + * requests responses. + * + * Normally `llhttp` would error on (in strict mode) or discard (in loose mode) + * the HTTP request/response after the request/response with `Connection: close` + * and `Content-Length`. This is important to prevent cache poisoning attacks, + * but might interact badly with outdated and insecure clients. With this flag + * the extra request/response will be parsed normally. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * poisoning attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of `Transfer-Encoding` header. + * + * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value + * and another value after it (either in a single header or in multiple + * headers whose value are internally joined using `, `). + * This is mandated by the spec to reliably determine request body size and thus + * avoid request smuggling. + * With this flag the extra value will be parsed normally. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of HTTP version. + * + * Normally `llhttp` would error when the HTTP version in the request or status line + * is not `0.9`, `1.0`, `1.1` or `2.0`. + * With this flag the invalid value will be parsed normally. + * + * **Enabling this flag can pose a security issue since you will allow unsupported + * HTTP versions. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_version(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of additional data received after a message ends + * and keep-alive is disabled. + * + * Normally `llhttp` would error when additional unexpected data is received if the message + * contains the `Connection` header with `close` value. + * With this flag the extra data will discarded without throwing an error. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * poisoning attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of incomplete CRLF sequences. + * + * Normally `llhttp` would error when a CR is not followed by LF when terminating the + * request line, the status line, the headers or a chunk header. + * With this flag only a CR is required to terminate such sections. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled); + +/* + * Enables/disables lenient handling of line separators. + * + * Normally `llhttp` would error when a LF is not preceded by CR when terminating the + * request line, the status line, the headers, a chunk header or a chunk data. + * With this flag only a LF is required to terminate such sections. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of chunks not separated via CRLF. + * + * Normally `llhttp` would error when after a chunk data a CRLF is missing before + * starting a new chunk. + * With this flag the new chunk can start immediately after the previous one. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of spaces after chunk size. + * + * Normally `llhttp` would error when after a chunk size is followed by one or more + * spaces are present instead of a CRLF or `;`. + * With this flag this check is disabled. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_API_H_ */ + + +#endif /* INCLUDE_LLHTTP_H_ */ diff --git a/lib/logger.c b/lib/logger.c new file mode 100644 index 0000000..fb3f560 --- /dev/null +++ b/lib/logger.c @@ -0,0 +1,155 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *=============================================================== + * modified by fduncanh 2023 + */ + +#include +#include +#include +#include + +#include "logger.h" +#include "compat.h" + +struct logger_s { + mutex_handle_t lvl_mutex; + mutex_handle_t cb_mutex; + + int level; + void *cls; + logger_callback_t callback; +}; + +logger_t * +logger_init() +{ + logger_t *logger = calloc(1, sizeof(logger_t)); + assert(logger); + + MUTEX_CREATE(logger->lvl_mutex); + MUTEX_CREATE(logger->cb_mutex); + + logger->level = LOGGER_WARNING; + logger->callback = NULL; + return logger; +} + +void +logger_destroy(logger_t *logger) +{ + MUTEX_DESTROY(logger->lvl_mutex); + MUTEX_DESTROY(logger->cb_mutex); + free(logger); +} + +void +logger_set_level(logger_t *logger, int level) +{ + assert(logger); + + MUTEX_LOCK(logger->lvl_mutex); + logger->level = level; + MUTEX_UNLOCK(logger->lvl_mutex); +} + +int +logger_get_level(logger_t *logger) +{ + int level; + assert(logger); + + MUTEX_LOCK(logger->lvl_mutex); + level = logger->level; + MUTEX_UNLOCK(logger->lvl_mutex); + + return level; +} + +void +logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls) +{ + assert(logger); + + MUTEX_LOCK(logger->cb_mutex); + logger->cls = cls; + logger->callback = callback; + MUTEX_UNLOCK(logger->cb_mutex); +} + +static char * +logger_utf8_to_local(const char *str) +{ + char *ret = NULL; + +/* FIXME: This is only implemented on Windows for now */ +#if defined(_WIN32) || defined(_WIN64) + int wclen, mblen; + WCHAR *wcstr; + BOOL failed; + + wclen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + wcstr = malloc(sizeof(WCHAR) * wclen); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wcstr, wclen); + + mblen = WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, NULL, 0, NULL, &failed); + if (failed) { + /* Invalid characters in input, conversion failed */ + free(wcstr); + return NULL; + } + + ret = malloc(sizeof(CHAR) * mblen); + WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, ret, mblen, NULL, NULL); + free(wcstr); +#endif + + return ret; +} + +void +logger_log(logger_t *logger, int level, const char *fmt, ...) +{ + char buffer[4096]; + va_list ap; + + MUTEX_LOCK(logger->lvl_mutex); + if (level > logger->level) { + MUTEX_UNLOCK(logger->lvl_mutex); + return; + } + MUTEX_UNLOCK(logger->lvl_mutex); + + buffer[sizeof(buffer)-1] = '\0'; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); + va_end(ap); + + MUTEX_LOCK(logger->cb_mutex); + if (logger->callback) { + logger->callback(logger->cls, level, buffer); + MUTEX_UNLOCK(logger->cb_mutex); + } else { + char *local; + MUTEX_UNLOCK(logger->cb_mutex); + local = logger_utf8_to_local(buffer); + if (local) { + fprintf(stderr, "%s\n", local); + free(local); + } else { + fprintf(stderr, "%s\n", buffer); + } + } +} + diff --git a/lib/logger.h b/lib/logger.h new file mode 100644 index 0000000..970344f --- /dev/null +++ b/lib/logger.h @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2023 + */ + +#ifndef LOGGER_H +#define LOGGER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Define syslog style log levels */ +#define LOGGER_EMERG 0 /* system is unusable */ +#define LOGGER_ALERT 1 /* action must be taken immediately */ +#define LOGGER_CRIT 2 /* critical conditions */ +#define LOGGER_ERR 3 /* error conditions */ +#define LOGGER_WARNING 4 /* warning conditions */ +#define LOGGER_NOTICE 5 /* normal but significant condition */ +#define LOGGER_INFO 6 /* informational */ +#define LOGGER_DEBUG 7 /* debug-level messages */ + +typedef void (*logger_callback_t)(void *cls, int level, const char *msg); + +typedef struct logger_s logger_t; + +logger_t *logger_init(); +void logger_destroy(logger_t *logger); + +void logger_set_level(logger_t *logger, int level); +int logger_get_level(logger_t *logger); +void logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls); + +void logger_log(logger_t *logger, int level, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/mirror_buffer.c b/lib/mirror_buffer.c new file mode 100644 index 0000000..b88fe24 --- /dev/null +++ b/lib/mirror_buffer.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019 dsafa22, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================ + * modified by fduncanh 2022 + */ + +#include "mirror_buffer.h" +#include "raop_rtp.h" +#include "raop_rtp.h" +#include +#include "crypto.h" +#include "compat.h" +#include +#include +#include +#include +#include +#include + +struct mirror_buffer_s { + logger_t *logger; + aes_ctx_t *aes_ctx; + int nextDecryptCount; + uint8_t og[16]; + /* audio aes key is used in a hash for the video aes key and iv */ + unsigned char aeskey_audio[RAOP_AESKEY_LEN]; +}; + +void +mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, const uint64_t *streamConnectionID) +{ + unsigned char aeskey_video[64]; + unsigned char aesiv_video[64]; + + assert(mirror_buffer); + assert(streamConnectionID); + + /* AES key and IV */ + // Need secondary processing to use + + snprintf((char*) aeskey_video, sizeof(aeskey_video), "AirPlayStreamKey%" PRIu64, *streamConnectionID); + snprintf((char*) aesiv_video, sizeof(aesiv_video), "AirPlayStreamIV%" PRIu64, *streamConnectionID); + + sha_ctx_t *ctx = sha_init(); + sha_update(ctx, aeskey_video, strlen((char*) aeskey_video)); + sha_update(ctx, mirror_buffer->aeskey_audio, RAOP_AESKEY_LEN); + sha_final(ctx, aeskey_video, NULL); + + sha_reset(ctx); + sha_update(ctx, aesiv_video, strlen((char*) aesiv_video)); + sha_update(ctx, mirror_buffer->aeskey_audio, RAOP_AESKEY_LEN); + sha_final(ctx, aesiv_video, NULL); + sha_destroy(ctx); + + // Need to be initialized externally + mirror_buffer->aes_ctx = aes_ctr_init(aeskey_video, aesiv_video); +} + +mirror_buffer_t * +mirror_buffer_init(logger_t *logger, const unsigned char *aeskey) +{ + mirror_buffer_t *mirror_buffer; + assert(aeskey); + mirror_buffer = calloc(1, sizeof(mirror_buffer_t)); + if (!mirror_buffer) { + return NULL; + } + memcpy(mirror_buffer->aeskey_audio, aeskey, RAOP_AESKEY_LEN); + mirror_buffer->logger = logger; + mirror_buffer->nextDecryptCount = 0; + return mirror_buffer; +} + +void mirror_buffer_decrypt(mirror_buffer_t *mirror_buffer, unsigned char* input, unsigned char* output, int inputLen) { + // Start decrypting + if (mirror_buffer->nextDecryptCount > 0) {//mirror_buffer->nextDecryptCount = 10 + for (int i = 0; i < mirror_buffer->nextDecryptCount; i++) { + output[i] = (input[i] ^ mirror_buffer->og[(16 - mirror_buffer->nextDecryptCount) + i]); + } + } + // Handling encrypted bytes + int encryptlen = ((inputLen - mirror_buffer->nextDecryptCount) / 16) * 16; + // Aes decryption + aes_ctr_start_fresh_block(mirror_buffer->aes_ctx); + aes_ctr_decrypt(mirror_buffer->aes_ctx, input + mirror_buffer->nextDecryptCount, + input + mirror_buffer->nextDecryptCount, encryptlen); + // Copy to output + memcpy(output + mirror_buffer->nextDecryptCount, input + mirror_buffer->nextDecryptCount, encryptlen); + // int outputlength = mirror_buffer->nextDecryptCount + encryptlen; + // Processing remaining length + int restlen = (inputLen - mirror_buffer->nextDecryptCount) % 16; + int reststart = inputLen - restlen; + mirror_buffer->nextDecryptCount = 0; + if (restlen > 0) { + memset(mirror_buffer->og, 0, 16); + memcpy(mirror_buffer->og, input + reststart, restlen); + aes_ctr_decrypt(mirror_buffer->aes_ctx, mirror_buffer->og, mirror_buffer->og, 16); + for (int j = 0; j < restlen; j++) { + output[reststart + j] = mirror_buffer->og[j]; + } + //outputlength += restlen; + mirror_buffer->nextDecryptCount = 16 - restlen;// Difference 16-6=10 bytes + } +} + +void +mirror_buffer_destroy(mirror_buffer_t *mirror_buffer) +{ + if (mirror_buffer) { + aes_ctr_destroy(mirror_buffer->aes_ctx); + free(mirror_buffer); + } +} diff --git a/lib/mirror_buffer.h b/lib/mirror_buffer.h new file mode 100644 index 0000000..2b65ab0 --- /dev/null +++ b/lib/mirror_buffer.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 dsafa22, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2022 + */ + +#ifndef MIRROR_BUFFER_H +#define MIRROR_BUFFER_H + +#include +#include "logger.h" + +typedef struct mirror_buffer_s mirror_buffer_t; + + +mirror_buffer_t *mirror_buffer_init( logger_t *logger, const unsigned char *aeskey); +void mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, const uint64_t *streamConnectionID); +void mirror_buffer_decrypt(mirror_buffer_t *raop_mirror, unsigned char* input, unsigned char* output, int datalen); +void mirror_buffer_destroy(mirror_buffer_t *mirror_buffer); +#endif //MIRROR_BUFFER_H diff --git a/lib/netutils.c b/lib/netutils.c new file mode 100644 index 0000000..642efba --- /dev/null +++ b/lib/netutils.c @@ -0,0 +1,212 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2022 + */ + +#include +#include +#include + +#include "compat.h" + +int +netutils_init() +{ +#ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + int ret; + + wVersionRequested = MAKEWORD(2, 2); + ret = WSAStartup(wVersionRequested, &wsaData); + if (ret) { + return -1; + } + + if (LOBYTE(wsaData.wVersion) != 2 || + HIBYTE(wsaData.wVersion) != 2) { + /* Version mismatch, requested version not found */ + return -1; + } +#endif + return 0; +} + +void +netutils_cleanup() +{ +#ifdef WIN32 + WSACleanup(); +#endif +} + +unsigned char * +netutils_get_address(void *sockaddr, int *length, unsigned int *zone_id) +{ + unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 }; + struct sockaddr *address = sockaddr; + + assert(address); + assert(length); + assert(zone_id); + if (address->sa_family == AF_INET) { + struct sockaddr_in *sin; + *zone_id = 0; + sin = (struct sockaddr_in *)address; + *length = sizeof(sin->sin_addr.s_addr); + return (unsigned char *)&sin->sin_addr.s_addr; + } else if (address->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)address; + if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) { + /* Actually an embedded IPv4 address */ + *zone_id = 0; + *length = sizeof(sin6->sin6_addr.s6_addr)-12; + return (sin6->sin6_addr.s6_addr+12); + } + *zone_id = (unsigned int) sin6->sin6_scope_id; + *length = sizeof(sin6->sin6_addr.s6_addr); + return sin6->sin6_addr.s6_addr; + } + + *length = 0; + return NULL; +} + +int +netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp) +{ + int family = use_ipv6 ? AF_INET6 : AF_INET; + int type = use_udp ? SOCK_DGRAM : SOCK_STREAM; + int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP; + + struct sockaddr_storage saddr; + socklen_t socklen; + int server_fd; + int ret; +#ifndef _WIN32 + int reuseaddr = 1; +#else + const char reuseaddr = 1; +#endif + + assert(port); + + server_fd = socket(family, type, proto); + if (server_fd == -1) { + goto cleanup; + } + + ret = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof (reuseaddr)); + if (ret == -1) { + goto cleanup; + } + + memset(&saddr, 0, sizeof(saddr)); + if (use_ipv6) { + struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr; + + /* Initialize sockaddr for bind */ + sin6ptr->sin6_family = family; + sin6ptr->sin6_addr = in6addr_any; + sin6ptr->sin6_port = htons(*port); + +#ifndef _WIN32 + int v6only = 1; + /* Make sure we only listen to IPv6 addresses */ + setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, + (char *) &v6only, sizeof(v6only)); +#endif + + socklen = sizeof(*sin6ptr); + ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen); + if (ret == -1) { + goto cleanup; + } + + ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen); + if (ret == -1) { + goto cleanup; + } + *port = ntohs(sin6ptr->sin6_port); + } else { + struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr; + + /* Initialize sockaddr for bind */ + sinptr->sin_family = family; + sinptr->sin_addr.s_addr = INADDR_ANY; + sinptr->sin_port = htons(*port); + + socklen = sizeof(*sinptr); + ret = bind(server_fd, (struct sockaddr *)sinptr, socklen); + if (ret == -1) { + goto cleanup; + } + + ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen); + if (ret == -1) { + goto cleanup; + } + *port = ntohs(sinptr->sin_port); + } + return server_fd; + + cleanup: + ret = SOCKET_GET_ERROR(); + if (server_fd != -1) { + closesocket(server_fd); + } + SOCKET_SET_ERROR(ret); + return -1; +} + +// Src is the ip address +int +netutils_parse_address(int family, const char *src, void *dst, int dstlen) +{ + struct addrinfo *result; + struct addrinfo *ptr; + struct addrinfo hints; + int length; + int ret; + + if (family != AF_INET && family != AF_INET6) { + return -1; + } + if (!src || !dst) { + return -1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + + ret = getaddrinfo(src, NULL, &hints, &result); + if (ret != 0) { + return -1; + } + + length = -1; + for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) { + if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) { + memcpy(dst, ptr->ai_addr, ptr->ai_addrlen); + length = ptr->ai_addrlen; + break; + } + } + freeaddrinfo(result); + return length; +} diff --git a/lib/netutils.h b/lib/netutils.h new file mode 100644 index 0000000..e64c638 --- /dev/null +++ b/lib/netutils.h @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef NETUTILS_H +#define NETUTILS_H + +int netutils_init(); +void netutils_cleanup(); + +int netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp); +unsigned char *netutils_get_address(void *sockaddr, int *length, unsigned int *zone_id); +int netutils_parse_address(int family, const char *src, void *dst, int dstlen); + +#endif diff --git a/lib/pairing.c b/lib/pairing.c new file mode 100644 index 0000000..e60273c --- /dev/null +++ b/lib/pairing.c @@ -0,0 +1,466 @@ +/** + * Copyright (C) 2018 Juho Vähä-Herttua + * Copyright (C) 2020 Jaslo Ziska + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + *================================================================== + * modified by fduncanh 2021, 2023 + */ + +#include +#include +#include +#include +#include // for SHA512_DIGEST_LENGTH + +#include "pairing.h" +#include "crypto.h" +#include "srp.h" + +#define SALT_KEY "Pair-Verify-AES-Key" +#define SALT_IV "Pair-Verify-AES-IV" + +typedef struct srp_s { + unsigned char salt[SRP_SALT_SIZE]; + unsigned char verifier[SRP_VERIFIER_SIZE]; + unsigned char session_key[SRP_SESSION_KEY_SIZE]; + unsigned char private_key[SRP_PRIVATE_KEY_SIZE]; +} srp_t; + +struct pairing_s { + ed25519_key_t *ed; +}; + +typedef enum { + STATUS_INITIAL, + STATUS_SETUP, + STATUS_HANDSHAKE, + STATUS_FINISHED +} status_t; + +struct pairing_session_s { + status_t status; + + ed25519_key_t *ed_ours; + ed25519_key_t *ed_theirs; + + x25519_key_t *ecdh_ours; + x25519_key_t *ecdh_theirs; + unsigned char ecdh_secret[X25519_KEY_SIZE]; + + char username[SRP_USERNAME_SIZE + 1]; + unsigned char client_pk[ED25519_KEY_SIZE]; + bool pair_setup; + + /* srp items */ + srp_t *srp; +}; + +static int +derive_key_internal(pairing_session_t *session, const unsigned char *salt, unsigned int saltlen, unsigned char *key, unsigned int keylen) +{ + unsigned char hash[SHA512_DIGEST_LENGTH]; + + if (keylen > sizeof(hash)) { + return -1; + } + + sha_ctx_t *ctx = sha_init(); + sha_update(ctx, salt, saltlen); + sha_update(ctx, session->ecdh_secret, X25519_KEY_SIZE); + sha_final(ctx, hash, NULL); + sha_destroy(ctx); + + memcpy(key, hash, keylen); + return 0; +} + +pairing_t * +pairing_init_generate(const char *device_id, const char *keyfile, int *result) +{ + pairing_t *pairing; + *result = 0; + pairing = calloc(1, sizeof(pairing_t)); + if (!pairing) { + return NULL; + } + + pairing->ed = ed25519_key_generate(device_id, keyfile, result); + + return pairing; +} + +void +pairing_get_public_key(pairing_t *pairing, unsigned char public_key[ED25519_KEY_SIZE]) +{ + assert(pairing); + ed25519_key_get_raw(public_key, pairing->ed); +} + +int +pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE]) +{ + assert(session); + switch (session->status) { + case STATUS_INITIAL: + return 0; + default: + memcpy(ecdh_secret, session->ecdh_secret, X25519_KEY_SIZE); + return 1; + } +} + +pairing_session_t * +pairing_session_init(pairing_t *pairing) +{ + pairing_session_t *session; + + if (!pairing) { + return NULL; + } + + session = calloc(1, sizeof(pairing_session_t)); + if (!session) { + return NULL; + } + + session->ed_ours = ed25519_key_copy(pairing->ed); + + session->status = STATUS_INITIAL; + session->srp = NULL; + session->pair_setup = false; + return session; +} + +void +pairing_session_set_setup_status(pairing_session_t *session) +{ + assert(session); + session->status = STATUS_SETUP; +} + +int +pairing_session_check_handshake_status(pairing_session_t *session) +{ + assert(session); + switch (session->status) { + case STATUS_SETUP: + case STATUS_HANDSHAKE: + return 0; + default: + return -1; + } +} + +int +pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[X25519_KEY_SIZE], + const unsigned char ed_key[ED25519_KEY_SIZE]) +{ + assert(session); + + if (session->status == STATUS_FINISHED) { + return -1; + } + + session->ecdh_theirs = x25519_key_from_raw(ecdh_key); + session->ed_theirs = ed25519_key_from_raw(ed_key); + + session->ecdh_ours = x25519_key_generate(); + + x25519_derive_secret(session->ecdh_secret, session->ecdh_ours, session->ecdh_theirs); + + session->status = STATUS_HANDSHAKE; + return 0; +} + +int +pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[X25519_KEY_SIZE]) +{ + assert(session); + + if (session->status != STATUS_HANDSHAKE) { + return -1; + } + + x25519_key_get_raw(ecdh_key, session->ecdh_ours); + + return 0; +} + +int +pairing_session_get_signature(pairing_session_t *session, unsigned char signature[PAIRING_SIG_SIZE]) +{ + unsigned char sig_msg[PAIRING_SIG_SIZE]; + unsigned char key[AES_128_BLOCK_SIZE]; + unsigned char iv[AES_128_BLOCK_SIZE]; + aes_ctx_t *aes_ctx; + + assert(session); + + if (session->status != STATUS_HANDSHAKE) { + return -1; + } + + /* First sign the public ECDH keys of both parties */ + x25519_key_get_raw(sig_msg, session->ecdh_ours); + x25519_key_get_raw(sig_msg + X25519_KEY_SIZE, session->ecdh_theirs); + + ed25519_sign(signature, PAIRING_SIG_SIZE, sig_msg, PAIRING_SIG_SIZE, session->ed_ours); + + /* Then encrypt the result with keys derived from the shared secret */ + derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key)); + derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(iv)); + + aes_ctx = aes_ctr_init(key, iv); + aes_ctr_encrypt(aes_ctx, signature, signature, PAIRING_SIG_SIZE); + aes_ctr_destroy(aes_ctx); + + return 0; +} + +int +pairing_session_finish(pairing_session_t *session, const unsigned char signature[PAIRING_SIG_SIZE]) +{ + unsigned char sig_buffer[PAIRING_SIG_SIZE]; + unsigned char sig_msg[PAIRING_SIG_SIZE]; + unsigned char key[AES_128_BLOCK_SIZE]; + unsigned char iv[AES_128_BLOCK_SIZE]; + aes_ctx_t *aes_ctx; + + assert(session); + + if (session->status != STATUS_HANDSHAKE) { + return -1; + } + + /* First decrypt the signature with keys derived from the shared secret */ + derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key)); + derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(iv)); + + aes_ctx = aes_ctr_init(key, iv); + /* One fake round for the initial handshake encryption */ + aes_ctr_encrypt(aes_ctx, sig_buffer, sig_buffer, PAIRING_SIG_SIZE); + aes_ctr_encrypt(aes_ctx, signature, sig_buffer, PAIRING_SIG_SIZE); + aes_ctr_destroy(aes_ctx); + + /* Then verify the signature with public ECDH keys of both parties */ + x25519_key_get_raw(sig_msg, session->ecdh_theirs); + x25519_key_get_raw(sig_msg + X25519_KEY_SIZE, session->ecdh_ours); + + if (!ed25519_verify(sig_buffer, PAIRING_SIG_SIZE, sig_msg, PAIRING_SIG_SIZE, session->ed_theirs)) { + return -2; + } + + session->status = STATUS_FINISHED; + return 0; +} + +void +pairing_session_destroy(pairing_session_t *session) +{ + if (session) { + ed25519_key_destroy(session->ed_ours); + ed25519_key_destroy(session->ed_theirs); + + x25519_key_destroy(session->ecdh_ours); + x25519_key_destroy(session->ecdh_theirs); + if (session->srp) { + free(session->srp); + session->srp = NULL; + } + free(session); + } +} + +void +pairing_destroy(pairing_t *pairing) +{ + if (pairing) { + ed25519_key_destroy(pairing->ed); + free(pairing); + } +} + +int +random_pin() { + unsigned char random_bytes[2] = { 0 }; + unsigned short random_short = 0; + int ret; + /* create a random unsigned short in range 1-9999 */ + while (!random_short) { + if ((ret = get_random_bytes(random_bytes, sizeof(random_bytes)) < 1)) { + return -1; + } + memcpy(&random_short, random_bytes, sizeof(random_bytes)); + random_short = random_short % 10000; + } + return (int) random_short; +} + +int +srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin, + const char **salt, int *len_salt, const char **pk, int *len_pk) { + if (strlen(device_id) > SRP_USERNAME_SIZE) { + return -1; + } + + strncpy(session->username, device_id, SRP_USERNAME_SIZE); + + if (session->srp) { + free (session->srp); + session->srp = NULL; + } + session->srp = (srp_t *) calloc(1, sizeof(srp_t)); + if (!session->srp) { + return -2; + } + + get_random_bytes(session->srp->private_key, SRP_PRIVATE_KEY_SIZE); + + const unsigned char *srp_b = session->srp->private_key; + unsigned char * srp_B; + unsigned char * srp_s; + unsigned char * srp_v; + int len_b = SRP_PRIVATE_KEY_SIZE; + int len_B; + int len_s; + int len_v; + srp_create_salted_verification_key(SRP_SHA, SRP_NG, device_id, + (const unsigned char *) pin, strlen (pin), + (const unsigned char **) &srp_s, &len_s, + (const unsigned char **) &srp_v, &len_v, + NULL, NULL); + if (len_s != SRP_SALT_SIZE || len_v != SRP_VERIFIER_SIZE) { + return -3; + } + + memcpy(session->srp->salt, srp_s, SRP_SALT_SIZE); + memcpy(session->srp->verifier, srp_v, SRP_VERIFIER_SIZE); + + *salt = (char *) session->srp->salt; + *len_salt = len_s; + + srp_create_server_ephemeral_key(SRP_SHA, SRP_NG, + srp_v, len_v, + srp_b, len_b, + (const unsigned char **) &srp_B, &len_B, + NULL, NULL, 1); + + *pk = (char *) srp_B; + *len_pk = len_B; + + return 0; +} + +int +srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A, + int len_A, unsigned char *proof, int client_proof_len, int proof_len) { + int authenticated = 0; + const unsigned char *B = NULL; + const unsigned char *b = session->srp->private_key; + int len_b = SRP_PRIVATE_KEY_SIZE; + int len_B = 0; + int len_K = 0; + const unsigned char *session_key = NULL; + const unsigned char *M2 = NULL; + + struct SRPVerifier *verifier = srp_verifier_new(SRP_SHA, SRP_NG, (const char *) session->username, + (const unsigned char *) session->srp->salt, SRP_SALT_SIZE, + (const unsigned char *) session->srp->verifier, SRP_VERIFIER_SIZE, + A, len_A, + b, len_b, + &B, &len_B, NULL, NULL, 1); + + srp_verifier_verify_session(verifier, proof, &M2); + authenticated = srp_verifier_is_authenticated(verifier); + if (authenticated == 0) { + /* HTTP 470 should be sent to client if not verified.*/ + srp_verifier_delete(verifier); + free (session->srp); + session->srp = NULL; + return -1; + } + session_key = srp_verifier_get_session_key(verifier, &len_K); + if (len_K != SRP_SESSION_KEY_SIZE) { + return -2; + } + memcpy(session->srp->session_key, session_key, len_K); + memcpy(proof, M2, proof_len); + srp_verifier_delete(verifier); + return 0; +} +int +srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, + unsigned char *epk, unsigned char *auth_tag) { + unsigned char aesKey[16], aesIV[16]; + unsigned char hash[SHA512_DIGEST_LENGTH]; + unsigned char pk[ED25519_KEY_SIZE]; + int pk_len_client, epk_len; + /* decrypt client epk to get client pk, authenticate with auth_tag*/ + + const char *salt = "Pair-Setup-AES-Key"; + sha_ctx_t *ctx = sha_init(); + sha_update(ctx, (const unsigned char *) salt, strlen(salt)); + sha_update(ctx, session->srp->session_key, SRP_SESSION_KEY_SIZE); + sha_final(ctx, hash, NULL); + sha_destroy(ctx); + memcpy(aesKey, hash, 16); + + salt = "Pair-Setup-AES-IV"; + ctx = sha_init(); + sha_update(ctx, (const unsigned char *) salt, strlen(salt)); + sha_update(ctx, session->srp->session_key, SRP_SESSION_KEY_SIZE); + sha_final(ctx, hash, NULL); + sha_destroy(ctx); + memcpy(aesIV, hash, 16); + aesIV[15]++; + + /* SRP6a data is no longer needed */ + free(session->srp); + session->srp = NULL; + + /* decrypt client epk to authenticate client using auth_tag */ + pk_len_client = gcm_decrypt(epk, ED25519_KEY_SIZE, pk, aesKey, aesIV, auth_tag); + if (pk_len_client <= 0) { + /* authentication failed */ + return pk_len_client; + } + + /* success, from server viewpoint */ + memcpy(session->client_pk, pk, ED25519_KEY_SIZE); + session->pair_setup = true; + + /* encrypt server epk so client can also authenticate server using auth_tag */ + pairing_get_public_key(pairing, pk); + + /* encryption needs this previously undocumented additional "nonce" */ + aesIV[15]++; + epk_len = gcm_encrypt(pk, ED25519_KEY_SIZE, epk, aesKey, aesIV, auth_tag); + return epk_len; +} + +void access_client_session_data(pairing_session_t *session, char **username, char **client_pk64, bool *setup) { + int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1; + setup = &(session->pair_setup); + *username = session->username; + if (setup) { + *client_pk64 = (char *) malloc(len64); + pk_to_base64(session->client_pk, ED25519_KEY_SIZE, *client_pk64, len64); + } else { + *client_pk64 = NULL; + } +} + +void ed25519_pk_to_base64(const unsigned char *pk, char **pk64) { + int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1; + *pk64 = (char *) malloc(len64); + pk_to_base64(pk, ED25519_KEY_SIZE, *pk64, len64); +} diff --git a/lib/pairing.h b/lib/pairing.h new file mode 100644 index 0000000..54db62b --- /dev/null +++ b/lib/pairing.h @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2018 Juho Vähä-Herttua + * Copyright (C) 2020 Jaslo Ziska + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "crypto.h" + +#ifndef PAIRING_H +#define PAIRING_H + +#define PAIRING_SIG_SIZE (2 * X25519_KEY_SIZE) + + +#define SRP_USERNAME_SIZE 24 /* accomodates up to an 8-octet MAC address */ +#define SRP_SESSION_KEY_SIZE 40 +#define SRP_VERIFIER_SIZE 256 +#define SRP_SALT_SIZE 16 +#define SRP_PK_SIZE 256 +#define SRP_SHA SRP_SHA1 +#define SRP_NG SRP_NG_2048 +#define SRP_M2_SIZE 64 +#define SRP_PRIVATE_KEY_SIZE 32 +#define GCM_AUTHTAG_SIZE 16 +#define SHA512_KEY_LENGTH 64 + +typedef struct pairing_s pairing_t; +typedef struct pairing_session_s pairing_session_t; + +pairing_t *pairing_init_generate(const char *device_id, const char *keyfile, int *result); +void pairing_get_public_key(pairing_t *pairing, unsigned char public_key[ED25519_KEY_SIZE]); + +pairing_session_t *pairing_session_init(pairing_t *pairing); +void pairing_session_set_setup_status(pairing_session_t *session); +int pairing_session_check_handshake_status(pairing_session_t *session); +int pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[X25519_KEY_SIZE], + const unsigned char ed_key[ED25519_KEY_SIZE]); +int pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[X25519_KEY_SIZE]); +int random_pin(); +int pairing_session_get_signature(pairing_session_t *session, unsigned char signature[PAIRING_SIG_SIZE]); +int pairing_session_finish(pairing_session_t *session, const unsigned char signature[PAIRING_SIG_SIZE]); +void pairing_session_destroy(pairing_session_t *session); + +void pairing_destroy(pairing_t *pairing); + +int pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE]); + +int srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin, + const char **salt, int *len_salt, const char **pk, int *len_pk); +int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A, + int len_A, unsigned char *proof, int client_proof_len, int proof_len); +int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsigned char *epk, + unsigned char *auth_tag); +void access_client_session_data(pairing_session_t *session, char **username, char **client_pk, bool *setup); +void ed25519_pk_to_base64(const unsigned char *pk, char **pk64); +#endif diff --git a/lib/playfair/CMakeLists.txt b/lib/playfair/CMakeLists.txt new file mode 100644 index 0000000..163f9aa --- /dev/null +++ b/lib/playfair/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.5) +aux_source_directory(. playfair_src) +set(DIR_SRCS ${playfair_src}) +include_directories(.) +add_library( playfair + STATIC + ${DIR_SRCS}) diff --git a/lib/playfair/LICENSE.md b/lib/playfair/LICENSE.md new file mode 100644 index 0000000..09f984d --- /dev/null +++ b/lib/playfair/LICENSE.md @@ -0,0 +1,637 @@ + +# GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/) + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most +of our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can do +these things. + +To protect your rights, we need to prevent others from denying you these rights +or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must pass on to the recipients the same freedoms that you received. +You must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: + + 1. assert copyright on the software, and + 2. offer you this License giving you legal permission to copy, distribute + and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that +there is no warranty for this free software. For both users' and authors' sake, +the GPL requires that modified versions be marked as changed, so that their +problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to +change the software. The systematic pattern of such abuse occurs in the area of +products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +*This License* refers to version 3 of the GNU General Public License. + +*Copyright* also means copyright-like laws that apply to other kinds of works, +such as semiconductor masks. + +*The Program* refers to any copyrightable work licensed under this License. +Each licensee is addressed as *you*. *Licensees* and *recipients* may be +individuals or organizations. + +To *modify* a work means to copy from or adapt all or part of the work in a +fashion requiring copyright permission, other than the making of an exact copy. +The resulting work is called a *modified version* of the earlier work or a work +*based on* the earlier work. + +A *covered work* means either the unmodified Program or a work based on the +Program. + +To *propagate* a work means to do anything with it that, without permission, +would make you directly or secondarily liable for infringement under applicable +copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To *convey* a work means any kind of propagation that enables other parties to +make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays *Appropriate Legal Notices* to the +extent that it includes a convenient and prominently visible feature that + + 1. displays an appropriate copyright notice, and + 2. tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the work + under this License, and how to view a copy of this License. + +If the interface presents a list of user commands or options, such as a menu, a +prominent item in the list meets this criterion. + +### 1. Source Code. + +The *source code* for a work means the preferred form of the work for making +modifications to it. *Object code* means any non-source form of a work. + +A *Standard Interface* means an interface that either is an official standard +defined by a recognized standards body, or, in the case of interfaces specified +for a particular programming language, one that is widely used among developers +working in that language. + +The *System Libraries* of an executable work include anything, other than the +work as a whole, that (a) is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and (b) serves only +to enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code +form. A *Major Component*, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on +which the executable work runs, or a compiler used to produce the work, or an +object code interpreter used to run it. + +The *Corresponding Source* for a work in object code form means all the source +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose +tools or generally available free programs which are used unmodified in +performing those activities but which are not part of the work. For example, +Corresponding Source includes interface definition files associated with source +files for the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, such as +by intimate data communication or control flow between those subprograms and +other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This +License explicitly affirms your unlimited permission to run the unmodified +Program. The output from running a covered work is covered by this License only +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, +provided that you comply with the terms of this License in conveying all +material for which you do not control copyright. Those thus making or running +the covered works for you must do so exclusively on your behalf, under your +direction and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes it +unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure +under any applicable law fulfilling obligations under article 11 of the WIPO +copyright treaty adopted on 20 December 1996, or similar laws prohibiting or +restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention is +effected by exercising rights under this License with respect to the covered +work, and you disclaim any intention to limit operation or modification of the +work as a means of enforcing, against the work's users, your or third parties' +legal rights to forbid circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on +each copy an appropriate copyright notice; keep intact all notices stating that +this License and any non-permissive terms added in accord with section 7 apply +to the code; keep intact all notices of the absence of any warranty; and give +all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: + + - a) The work must carry prominent notices stating that you modified it, and + giving a relevant date. + - b) The work must carry prominent notices stating that it is released under + this License and any conditions added under section 7. This requirement + modifies the requirement in section 4 to *keep intact all notices*. + - c) You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will therefore + apply, along with any applicable section 7 additional terms, to the whole + of the work, and all its parts, regardless of how they are packaged. This + License gives no permission to license the work in any other way, but it + does not invalidate such permission if you have separately received it. + - d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your work need + not make them do so. + +A compilation of a covered work with other separate and independent works, +which are not by their nature extensions of the covered work, and which are not +combined with it such as to form a larger program, in or on a volume of a +storage or distribution medium, is called an *aggregate* if the compilation and +its resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a +covered work in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 +and 5, provided that you also convey the machine-readable Corresponding Source +under the terms of this License, in one of these ways: + + - a) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by the Corresponding Source + fixed on a durable physical medium customarily used for software + interchange. + - b) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by a written offer, valid for + at least three years and valid for as long as you offer spare parts or + customer support for that product model, to give anyone who possesses the + object code either + 1. a copy of the Corresponding Source for all the software in the product + that is covered by this License, on a durable physical medium + customarily used for software interchange, for a price no more than your + reasonable cost of physically performing this conveying of source, or + 2. access to copy the Corresponding Source from a network server at no + charge. + - c) Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed only + occasionally and noncommercially, and only if you received the object code + with such an offer, in accord with subsection 6b. + - d) Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the Corresponding + Source in the same way through the same place at no further charge. You + need not require recipients to copy the Corresponding Source along with the + object code. If the place to copy the object code is a network server, the + Corresponding Source may be on a different server operated by you or a + third party) that supports equivalent copying facilities, provided you + maintain clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the Corresponding + Source, you remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + - e) Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the + work are being offered to the general public at no charge under subsection + 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A *User Product* is either + + 1. a *consumer product*, which means any tangible personal property which is + normally used for personal, family, or household purposes, or + 2. anything designed or sold for incorporation into a dwelling. + +In determining whether a product is a consumer product, doubtful cases shall be +resolved in favor of coverage. For a particular product received by a +particular user, *normally used* refers to a typical or common use of that +class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, +the product. A product is a consumer product regardless of whether the product +has substantial commercial, industrial or non-consumer uses, unless such uses +represent the only significant mode of use of the product. + +*Installation Information* for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified +version of its Corresponding Source. The information must suffice to ensure +that the continued functioning of the modified object code is in no case +prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as part of a +transaction in which the right of possession and use of the User Product is +transferred to the recipient in perpetuity or for a fixed term (regardless of +how the transaction is characterized), the Corresponding Source conveyed under +this section must be accompanied by the Installation Information. But this +requirement does not apply if neither you nor any third party retains the +ability to install modified object code on the User Product (for example, the +work has been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates for a +work that has been modified or installed by the recipient, or for the User +Product in which it has been modified or installed. Access to a network may be +denied when the modification itself materially and adversely affects the +operation of the network or violates the rules and protocols for communication +across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord +with this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require +no special password or key for unpacking, reading or copying. + +### 7. Additional Terms. + +*Additional permissions* are terms that supplement the terms of this License by +making exceptions from one or more of its conditions. Additional permissions +that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may +be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when +you modify the work.) You may place additional permissions on material, added +by you to a covered work, for which you have or can give appropriate copyright +permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + + - a) Disclaiming warranty or limiting liability differently from the terms of + sections 15 and 16 of this License; or + - b) Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices displayed + by works containing it; or + - c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in reasonable + ways as different from the original version; or + - d) Limiting the use for publicity purposes of names of licensors or authors + of the material; or + - e) Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or + - f) Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with + contractual assumptions of liability to the recipient, for any liability + that these contractual assumptions directly impose on those licensors and + authors. + +All other non-permissive additional terms are considered *further restrictions* +within the meaning of section 10. If the Program as you received it, or any +part of it, contains a notice stating that it is governed by this License along +with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or +conveying under this License, you may add to a covered work material governed +by the terms of that license document, provided that the further restriction +does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, +in the relevant source files, a statement of the additional terms that apply to +those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements +apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly provided +under this License. Any attempt otherwise to propagate or modify it is void, +and will automatically terminate your rights under this License (including any +patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated + + - a) provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and + - b) permanently, if the copyright holder fails to notify you of the + violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated +permanently if the copyright holder notifies you of the violation by some +reasonable means, this is the first time you have received notice of violation +of this License (for any work) from that copyright holder, and you cure the +violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses +of parties who have received copies or rights from you under this License. If +your rights have been terminated and not permanently reinstated, you do not +qualify to receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy +of the Program. Ancillary propagation of a covered work occurring solely as a +consequence of using peer-to-peer transmission to receive a copy likewise does +not require acceptance. However, nothing other than this License grants you +permission to propagate or modify any covered work. These actions infringe +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to do +so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a +license from the original licensors, to run, modify and propagate that work, +subject to this License. You are not responsible for enforcing compliance by +third parties with this License. + +An *entity transaction* is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered work +results from an entity transaction, each party to that transaction who receives +a copy of the work also receives whatever licenses to the work the party's +predecessor in interest had or could give under the previous paragraph, plus a +right to possession of the Corresponding Source of the work from the +predecessor in interest, if the predecessor has it or can get it with +reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights +granted or affirmed under this License. For example, you may not impose a +license fee, royalty, or other charge for exercise of rights granted under this +License, and you may not initiate litigation (including a cross-claim or +counterclaim in a lawsuit) alleging that any patent claim is infringed by +making, using, selling, offering for sale, or importing the Program or any +portion of it. + +### 11. Patents. + +A *contributor* is a copyright holder who authorizes use under this License of +the Program or a work on which the Program is based. The work thus licensed is +called the contributor's *contributor version*. + +A contributor's *essential patent claims* are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would +be infringed only as a consequence of further modification of the contributor +version. For purposes of this definition, *control* includes the right to grant +patent sublicenses in a manner consistent with the requirements of this +License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent +license under the contributor's essential patent claims, to make, use, sell, +offer for sale, import and otherwise run, modify and propagate the contents of +its contributor version. + +In the following three paragraphs, a *patent license* is any express agreement +or commitment, however denominated, not to enforce a patent (such as an express +permission to practice a patent or covenant not to sue for patent +infringement). To *grant* such a patent license to a party means to make such +an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of +charge and under the terms of this License, through a publicly available +network server or other readily accessible means, then you must either + + 1. cause the Corresponding Source to be so available, or + 2. arrange to deprive yourself of the benefit of the patent license for this + particular work, or + 3. arrange, in a manner consistent with the requirements of this License, to + extend the patent license to downstream recipients. + +*Knowingly relying* means you have actual knowledge that, but for the patent +license, your conveying the covered work in a country, or your recipient's use +of the covered work in a country, would infringe one or more identifiable +patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a +patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, +then the patent license you grant is automatically extended to all recipients +of the covered work and works based on it. + +A patent license is *discriminatory* if it does not include within the scope of +its coverage, prohibits the exercise of, or is conditioned on the non-exercise +of one or more of the rights that are specifically granted under this License. +You may not convey a covered work if you are a party to an arrangement with a +third party that is in the business of distributing software, under which you +make payment to the third party based on the extent of your activity of +conveying the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory patent +license + + - a) in connection with copies of the covered work conveyed by you (or copies + made from those copies), or + - b) primarily for and in connection with specific products or compilations + that contain the covered work, unless you entered into that arrangement, or + that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to +you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not excuse +you from the conditions of this License. If you cannot convey a covered work so +as to satisfy simultaneously your obligations under this License and any other +pertinent obligations, then as a consequence you may not convey it at all. For +example, if you agree to terms that obligate you to collect a royalty for +further conveying from those to whom you convey the Program, the only way you +could satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to +link or combine any covered work with a work licensed under version 3 of the +GNU Affero General Public License into a single combined work, and to convey +the resulting work. The terms of this License will continue to apply to the +part which is the covered work, but the special requirements of the GNU Affero +General Public License, section 13, concerning interaction through a network +will apply to the combination as such. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program specifies +that a certain numbered version of the GNU General Public License *or any later +version* applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by +the Free Software Foundation. If the Program does not specify a version number +of the GNU General Public License, you may choose any version ever published by +the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the +GNU General Public License can be used, that proxy's public statement of +acceptance of a version permanently authorizes you to choose that version for +the Program. + +Later license versions may give you additional or different permissions. +However, no additional obligations are imposed on any author or copyright +holder as a result of your choosing to follow a later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE +LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER +PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot +be given local legal effect according to their terms, reviewing courts shall +apply local law that most closely approximates an absolute waiver of all civil +liability in connection with the Program, unless a warranty or assumption of +liability accompanies a copy of the Program in return for a fee. + +## END OF TERMS AND CONDITIONS ### + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion +of warranty; and each file should have at least the *copyright* line and a +pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like +this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate +parts of the General Public License. Of course, your program's commands might +be different; for a GUI interface, you would use an *about box*. + +You should also get your employer (if you work as a programmer) or school, if +any, to sign a *copyright disclaimer* for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. But first, please read +[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html). diff --git a/lib/playfair/hand_garble.c b/lib/playfair/hand_garble.c new file mode 100644 index 0000000..8e8361b --- /dev/null +++ b/lib/playfair/hand_garble.c @@ -0,0 +1,443 @@ +#include +#include + +#define printf(...) (void)0; + +uint8_t rol8(uint8_t x, int y); +uint32_t rol8x(uint8_t x, int y); + +uint32_t weird_ror8(uint8_t input, int count) +{ + if (count == 0) + return 0; + return ((input >> count) & 0xff) | (input & 0xff) << (8-count); + +} + +uint32_t weird_rol8(uint8_t input, int count) +{ + if (count == 0) + return 0; + return ((input << count) & 0xff) | (input & 0xff) >> (8-count); +} + +uint32_t weird_rol32(uint8_t input, int count) +{ + if (count == 0) + return 0; + return (input << count) ^ (input >> (8 - count)); +} + +// I do not know why it is doing all of this, and there is still a possibility for a gremlin or two to be lurking in the background +// I DO know it is not trivial. It could be purely random garbage, of course. +void garble(unsigned char* buffer0, unsigned char* buffer1, unsigned char* buffer2, unsigned char* buffer3, unsigned char* buffer4) +{ + unsigned int tmp, tmp2, tmp3; + unsigned int A, B, C, D, E, M, J, G, F, H, K, R, S, T, U, V, W, X, Y, Z; + // buffer1[64] = A + // (buffer1[99] / 3) = B + // 0ABAAABB + // Then we AND with a complex expression, and add 20 just for good measure + buffer2[12] = 0x14 + (((buffer1[64] & 92) | ((buffer1[99] / 3) & 35)) & buffer4[rol8x(buffer4[(buffer1[206] % 21)],4) % 21]); + printf("buffer2[12] = %02x\n", buffer2[12]); + + // This is a bit simpler: 2*B*B/25 + buffer1[4] = (buffer1[99] / 5) * (buffer1[99] / 5) * 2; + printf("buffer1[4] = %02x\n", buffer1[4]); + + // Simpler still! + buffer2[34] = 0xb8; + printf("buffer2[34] = %02x\n", buffer2[34]); + + // ... + buffer1[153] ^= (buffer2[buffer1[203] % 35] * buffer2[buffer1[203] % 35] * buffer1[190]); + printf("buffer1[153] = %02x\n", buffer1[153]); + + // This one looks simple, but wow was it not :( + buffer0[3] -= (((buffer4[buffer1[205] % 21]>>1) & 80) | 0xe6440); + printf("buffer0[3] = %02x\n", buffer0[3]); + + // This is always 0x93 + buffer0[16] = 0x93; + printf("buffer0[16] = %02x\n", buffer0[16]); + + // This is always 0x62 + buffer0[13] = 0x62; + printf("buffer0[13] = %02x\n", buffer0[13]); + + buffer1[33] -= (buffer4[buffer1[36] % 21] & 0xf6); + printf("buffer1[33] = %02x\n", buffer1[33]); + + // This is always 7 + tmp2 = buffer2[buffer1[67] % 35]; + buffer2[12] = 0x07; + printf("buffer2[12] = %02x\n", buffer2[12]); + + // This is pretty easy! + tmp = buffer0[buffer1[181] % 20]; + buffer1[2] -= 3136; + printf("buffer1[2] = %02x\n", buffer1[2]); + + buffer0[19] = buffer4[buffer1[58] % 21]; + printf("buffer0[19] = %02x\n", buffer0[19]); + + buffer3[0] = 92 - buffer2[buffer1[32] % 35]; + printf("buffer3[0] = %02x\n", buffer3[0]); + + buffer3[4] = buffer2[buffer1[15] % 35] + 0x9e; + printf("buffer3[4] = %02x\n", buffer3[4]); + + buffer1[34] += (buffer4[((buffer2[buffer1[15] % 35] + 0x9e) & 0xff) % 21] / 5); + printf("buffer1[34] = %02x\n", buffer1[34]); + + buffer0[19] += 0xfffffee6 - ((buffer0[buffer3[4] % 20]>>1) & 102); + printf("buffer0[19] = %02x\n", buffer0[19]); + + // This LOOKS like it should be a rol8x, but it just doesnt work out because if the shift amount is 0, then the output is 0 too :( + // FIXME: Switch to weird_ror8 + buffer1[15] = (3*(((buffer1[72] >> (buffer4[buffer1[190] % 21] & 7)) ^ (buffer1[72] << ((7 - (buffer4[buffer1[190] % 21]-1)&7)))) - (3*buffer4[buffer1[126] % 21]))) ^ buffer1[15]; + printf("buffer1[15] = %02x\n", buffer1[15]); + + buffer0[15] ^= buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35]; + printf("buffer0[15] = %02x\n", buffer0[15]); + + buffer2[4] ^= buffer1[202]/3; + printf("buffer2[4] = %02x\n", buffer2[4]); + + // This could probably be quite a bit simpler. + A = 92 - buffer0[buffer3[0] % 20]; + E = (A & 0xc6) | (~buffer1[105] & 0xc6) | (A & (~buffer1[105])); + buffer2[1] += (E*E*E); + printf("buffer2[1] = %02x\n", buffer2[1]); + + buffer0[19] ^= ((224 | (buffer4[buffer1[92] % 21] & 27)) * buffer2[buffer1[41] % 35]) / 3; + printf("buffer0[19] = %02x\n", buffer0[19]); + + buffer1[140] += weird_ror8(92, buffer1[5] & 7); + printf("buffer1[140] = %02x\n", buffer1[140]); + + // Is this as simple as it could be? + buffer2[12] += ((((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) | buffer1[182]) & 192) | (((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) & buffer1[182]); + printf("buffer2[12] = %02x\n", buffer2[12]); + + buffer1[36] += 125; + printf("buffer1[36] = %02x\n", buffer1[36]); + + buffer1[124] = rol8x((((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15])) & buffer0[buffer1[43] % 20]) | (((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15]) | buffer0[buffer1[43] % 20]) & 95), 4); + printf("buffer1[124] = %02x\n", buffer1[124]); + + buffer3[8] = ((((buffer0[buffer3[4] % 20] & 95)) & ((buffer4[buffer1[68] % 21] & 46) << 1)) | 16) ^ 92; + printf("buffer3[8] = %02x\n", buffer3[8]); + + A = buffer1[177] + buffer4[buffer1[79] % 21]; + D = (((A >> 1) | ((3 * buffer1[148]) / 5)) & buffer2[1]) | ((A >> 1) & ((3 * buffer1[148])/5)); + buffer3[12] = ((-34 - D)); + printf("buffer3[12] = %02x\n", buffer3[12]); + + A = 8 - ((buffer2[22] & 7)); // FIXME: buffer2[22] = 74, so A is always 6 and B^C is just ror8(buffer1[33], 6) + B = (buffer1[33] >> (A & 7)); + C = buffer1[33] << (buffer2[22] & 7); + buffer2[16] += ((buffer2[buffer3[0] % 35] & 159) | buffer0[buffer3[4] % 20] | 8) - ((B^C) | 128); + printf("buffer2[16] = %02x\n", buffer2[16]); + + // This one was very easy so I just skipped ahead and did it + buffer0[14] ^= buffer2[buffer3[12] % 35]; + printf("buffer0[14] = %02x\n", buffer0[14]); + + // Monster goes here + A = weird_rol8(buffer4[buffer0[buffer1[201] % 20] % 21], ((buffer2[buffer1[112] % 35] << 1) & 7)); + D = (buffer0[buffer1[208] % 20] & 131) | (buffer0[buffer1[164] % 20] & 124); + buffer1[19] += (A & (D/5)) | ((A | (D/5)) & 37); + printf("buffer1[19] = %02x\n", buffer1[19]); + + buffer2[8] = weird_ror8(140, ((buffer4[buffer1[45] % 21] + 92) * (buffer4[buffer1[45] % 21] + 92)) & 7); + printf("buffer2[8] = %02x\n", buffer2[8]); + + buffer1[190] = 56; + printf("buffer1[190] = %02x\n", buffer1[190]); + + buffer2[8] ^= buffer3[0]; + printf("buffer2[8] = %02x\n", buffer2[8]); + + buffer1[53] = ~((buffer0[buffer1[83] % 20] | 204)/5); + printf("buffer1[53] = %02x\n", buffer1[53]); + + buffer0[13] += buffer0[buffer1[41] % 20]; + printf("buffer0[13] = %02x\n", buffer0[13]); + + buffer0[10] = ((buffer2[buffer3[0] % 35] & buffer1[2]) | ((buffer2[buffer3[0] % 35] | buffer1[2]) & buffer3[12])) / 15; + printf("buffer0[10] = %02x\n", buffer0[10]); + + A = (((56 | (buffer4[buffer1[2] % 21] & 68)) | buffer2[buffer3[8] % 35]) & 42) | (((buffer4[buffer1[2] % 21] & 68) | 56) & buffer2[buffer3[8] % 35]); + buffer3[16] = (A*A) + 110; + printf("buffer3[16] = %02x\n", buffer3[16]); + + buffer3[20] = 202 - buffer3[16]; + printf("buffer3[20] = %02x\n", buffer3[20]); + + buffer3[24] = buffer1[151]; + printf("buffer3[24] = %02x\n", buffer3[24]); + + buffer2[13] ^= buffer4[buffer3[0] % 21]; + printf("buffer2[13] = %02x\n", buffer2[13]); + + B = ((buffer2[buffer1[179] % 35] - 38) & 177) | (buffer3[12] & 177); + C = ((buffer2[buffer1[179] % 35] - 38)) & buffer3[12]; + buffer3[28] = 30 + ((B | C) * (B | C)); + printf("buffer3[28] = %02x\n", buffer3[28]); + + buffer3[32] = buffer3[28] + 62; + printf("buffer3[32] = %02x\n", buffer3[32]); + + // eek + A = ((buffer3[20] + (buffer3[0] & 74)) | ~buffer4[buffer3[0] % 21]) & 121; + B = ((buffer3[20] + (buffer3[0] & 74)) & ~buffer4[buffer3[0] % 21]); + tmp3 = (A|B); + C = ((((A|B) ^ 0xffffffa6) | buffer3[0]) & 4) | (((A|B) ^ 0xffffffa6) & buffer3[0]); + buffer1[47] = (buffer2[buffer1[89] % 35] + C) ^ buffer1[47]; + printf("buffer1[47] = %02x\n", buffer1[47]); + + buffer3[36] = ((rol8((tmp & 179) + 68, 2) & buffer0[3]) | (tmp2 & ~buffer0[3])) - 15; + printf("buffer3[36] = %02x\n", buffer3[36]); + + buffer1[123] ^= 221; + printf("buffer1[123] = %02x\n", buffer1[123]); + + A = ((buffer4[buffer3[0] % 21]) / 3) - buffer2[buffer3[4] % 35]; + C = (((buffer3[0] & 163) + 92) & 246) | (buffer3[0] & 92); + E = ((C | buffer3[24]) & 54) | (C & buffer3[24]); + buffer3[40] = A - E; + printf("buffer3[40] = %02x\n", buffer3[40]); + + buffer3[44] = tmp3 ^ 81 ^ (((buffer3[0] >> 1) & 101) + 26); + printf("buffer3[44] = %02x\n", buffer3[44]); + + buffer3[48] = buffer2[buffer3[4] % 35] & 27; + printf("buffer3[48] = %02x\n", buffer3[48]); + buffer3[52] = 27; + printf("buffer3[52] = %02x\n", buffer3[52]); + buffer3[56] = 199; + printf("buffer3[56] = %02x\n", buffer3[56]); + + // caffeine + buffer3[64] = buffer3[4] + (((((((buffer3[40] | buffer3[24]) & 177) | (buffer3[40] & buffer3[24])) & ((((buffer4[buffer3[0] % 20] & 177) | 176)) | ((buffer4[buffer3[0] % 21]) & ~3))) | ((((buffer3[40] & buffer3[24]) | ((buffer3[40] | buffer3[24]) & 177)) & 199) | ((((buffer4[buffer3[0] % 21] & 1) + 176) | (buffer4[buffer3[0] % 21] & ~3)) & buffer3[56]))) & (~buffer3[52])) | buffer3[48]); + printf("buffer3[64] = %02x (want E7)\n", buffer3[64]); + + buffer2[33] ^= buffer1[26]; + printf("buffer2[33] = %02x\n", buffer2[33]); + + buffer1[106] ^= buffer3[20] ^ 133; + printf("buffer1[106] = %02x\n", buffer1[106]); + + buffer2[30] = ((buffer3[64] / 3) - (275 | (buffer3[0] & 247))) ^ buffer0[buffer1[122] % 20]; + printf("buffer2[130] = %02x\n", buffer2[30]); + + buffer1[22] = (buffer2[buffer1[90] % 35] & 95) | 68; + printf("buffer1[22] = %02x\n", buffer1[22]); + + A = (buffer4[buffer3[36] % 21] & 184) | (buffer2[buffer3[44] % 35] & ~184); + buffer2[18] += ((A*A*A) >> 1); + printf("buffer2[18] = %02x\n", buffer2[18]); + + buffer2[5] -= buffer4[buffer1[92] % 21]; + printf("buffer2[5] = %02x\n", buffer2[5]); + + A = (((buffer1[41] & ~24)|(buffer2[buffer1[183] % 35] & 24)) & (buffer3[16] + 53)) | (buffer3[20] & buffer2[buffer3[20] % 35]); + B = (buffer1[17] & (~buffer3[44])) | (buffer0[buffer1[59] % 20] & buffer3[44]); + buffer2[18] ^= (A*B); + printf("buffer2[18] = %02x\n", buffer2[18]); + + + A = weird_ror8(buffer1[11], buffer2[buffer1[28] % 35] & 7) & 7; + B = (((buffer0[buffer1[93] % 20] & ~buffer0[14]) | (buffer0[14] & 150)) & ~28) | (buffer1[7] & 28); + buffer2[22] = (((((B | weird_rol8(buffer2[buffer3[0] % 35], A)) & buffer2[33]) | (B & weird_rol8(buffer2[buffer3[0] % 35], A))) + 74) & 0xff); + printf("buffer2[22] = %02x\n", buffer2[22]); + + A = buffer4[(buffer0[buffer1[39] % 20] ^ 217) % 21]; // X5 + buffer0[15] -= ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0])) & A) | ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0]) | A) & buffer3[32]); + printf("buffer0[15] = %02x\n", buffer0[15]); + + // We need to save T here, and boy is it complicated to calculate! + B = (((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer0[buffer3[64] % 20] | buffer2[buffer1[57] % 35]) & 95) | (buffer3[64] & 45) | 82) & 32); + C = ((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer2[buffer1[57] % 35] | buffer0[buffer3[64] % 20]) & 95)) & ((buffer3[64] & 45) | 82); + D = ((((buffer3[0]/3) - (buffer3[64]|buffer1[22]))) ^ (buffer3[28] + 62) ^ ((B|C))); + T = buffer0[(D & 0xff) % 20]; + + buffer3[68] = (buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20]) | buffer2[buffer3[64] % 35]; + printf("buffer3[68] = %02x\n", buffer3[68]); + + U = buffer0[buffer1[50] % 20]; // this is also v100 + W = buffer2[buffer1[138] % 35]; + X = buffer4[buffer1[39] % 21]; + Y = buffer0[buffer1[4] % 20]; // this is also v120 + Z = buffer4[buffer1[202] % 21]; // also v124 + V = buffer0[buffer1[151] % 20]; + S = buffer2[buffer1[14] % 35]; + R = buffer0[buffer1[145] % 20]; + + A = (buffer2[buffer3[68] % 35] & buffer0[buffer1[209] % 20]) | ((buffer2[buffer3[68] % 35] | buffer0[buffer1[209] % 20]) & 24); + B = weird_rol8(buffer4[buffer1[127] % 21], buffer2[buffer3[68] % 35] & 7); + C = (A & buffer0[10]) | (B & ~buffer0[10]); + D = 7 ^ (buffer4[buffer2[buffer3[36] % 35] % 21] << 1); + buffer3[72] = (C & 71) | (D & ~71); + printf("buffer3[72] = %02x\n", buffer3[72]); + + buffer2[2] += (((buffer0[buffer3[20] % 20] << 1) & 159) | (buffer4[buffer1[190] % 21] & ~159)) & ((((buffer4[buffer3[64] % 21] & 110) | (buffer0[buffer1[25] % 20] & ~110)) & ~150) | (buffer1[25] & 150)); + printf("buffer2[2] = %02x\n", buffer2[2]); + + buffer2[14] -= ((buffer2[buffer3[20] % 35] & (buffer3[72] ^ buffer2[buffer1[100] % 35])) & ~34) | (buffer1[97] & 34); + printf("buffer2[14] = %02x\n", buffer2[14]); + + buffer0[17] = 115; + printf("buffer0[17] = %02x\n", buffer0[17]); + + buffer1[23] ^= ((((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20])) & (buffer1[50]/3)) | + ((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20]) | (buffer1[50] / 3)) & 246)) << 1); + printf("buffer1[23] = %02x\n", buffer1[23]); + + buffer0[13] = ((((((buffer0[buffer3[40] % 20] | buffer1[10]) & 82) | (buffer0[buffer3[40] % 20] & buffer1[10])) & 209) | + ((buffer0[buffer1[39] % 20] << 1) & 46)) >> 1); + printf("buffer0[13] = %02x\n", buffer0[13]); + + buffer2[33] -= buffer1[113] & 9; + printf("buffer2[33] = %02x\n", buffer2[33]); + + buffer2[28] -= ((((2 | (buffer1[110] & 222)) >> 1) & ~223) | (buffer3[20] & 223)); + printf("buffer2[28] = %02x\n", buffer2[28]); + + J = weird_rol8((V | Z), (U & 7)); // OK + A = (buffer2[16] & T) | (W & (~buffer2[16])); + B = (buffer1[33] & 17) | (X & ~17); + E = ((Y | ((A+B) / 5)) & 147) | + (Y & ((A+B) / 5)); // OK + M = (buffer3[40] & buffer4[((buffer3[8] + J + E) & 0xff) % 21]) | + ((buffer3[40] | buffer4[((buffer3[8] + J + E) & 0xff) % 21]) & buffer2[23]); + + buffer0[15] = (((buffer4[buffer3[20] % 21] - 48) & (~buffer1[184])) | ((buffer4[buffer3[20] % 21] - 48) & 189) | (189 & ~buffer1[184])) & (M*M*M); + printf("buffer0[15] = %02x\n", buffer0[15]); + + buffer2[22] += buffer1[183]; + printf("buffer2[22] = %02x\n", buffer2[22]); + + buffer3[76] = (3 * buffer4[buffer1[1] % 21]) ^ buffer3[0]; + printf("buffer3[76] = %02x\n", buffer3[76]); + + A = buffer2[((buffer3[8] + (J + E)) & 0xff) % 35]; + F = (((buffer4[buffer1[178] % 21] & A) | ((buffer4[buffer1[178] % 21] | A) & 209)) * buffer0[buffer1[13] % 20]) * (buffer4[buffer1[26] % 21] >> 1); + G = (F + 0x733ffff9) * 198 - (((F + 0x733ffff9) * 396 + 212) & 212) + 85; + buffer3[80] = buffer3[36] + (G ^ 148) + ((G ^ 107) << 1) - 127; + printf("buffer3[80] = %02x\n", buffer3[80]); + + buffer3[84] = ((buffer2[buffer3[64] % 35]) & 245) | (buffer2[buffer3[20] % 35] & 10); + printf("buffer3[84] = %02x\n", buffer3[84]); + + A = buffer0[buffer3[68] % 20] | 81; + buffer2[18] -= ((A*A*A) & ~buffer0[15]) | ((buffer3[80] / 15) & buffer0[15]); + printf("buffer2[18] = %02x\n", buffer2[18]); + + buffer3[88] = buffer3[8] + J + E - buffer0[buffer1[160] % 20] + (buffer4[buffer0[((buffer3[8] + J + E) & 255) % 20] % 21] / 3); + printf("buffer3[88] = %02x\n", buffer3[88]); + + B = ((R ^ buffer3[72]) & ~198) | ((S * S) & 198); + F = (buffer4[buffer1[69] % 21] & buffer1[172]) | ((buffer4[buffer1[69] % 21] | buffer1[172] ) & ((buffer3[12] - B) + 77)); + buffer0[16] = 147 - ((buffer3[72] & ((F & 251) | 1)) | (((F & 250) | buffer3[72]) & 198)); + printf("buffer0[16] = %02x\n", buffer0[16]); + + C = (buffer4[buffer1[168] % 21] & buffer0[buffer1[29] % 20] & 7) | ((buffer4[buffer1[168] % 21] | buffer0[buffer1[29] % 20]) & 6); + F = (buffer4[buffer1[155] % 21] & buffer1[105]) | ((buffer4[buffer1[155] % 21] | buffer1[105]) & 141); + buffer0[3] -= buffer4[weird_rol32(F, C) % 21]; + printf("buffer0[3] = %02x\n", buffer0[3]); + + buffer1[5] = weird_ror8(buffer0[12], ((buffer0[buffer1[61] % 20] / 5) & 7)) ^ (((~buffer2[buffer3[84] % 35]) & 0xffffffff) / 5); + printf("buffer1[5] = %02x\n", buffer1[5]); + + buffer1[198] += buffer1[3]; + printf("buffer1[198] = %02x\n", buffer1[198]); + + A = (162 | buffer2[buffer3[64] % 35]); + buffer1[164] += ((A*A)/5); + printf("buffer1[164] = %02x\n", buffer1[164]); + + G = weird_ror8(139, (buffer3[80] & 7)); + C = ((buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21]) & 95) | (buffer0[buffer3[40] % 20] & ~95); + buffer3[92] = (G & 12) | (buffer0[buffer3[20] % 20] & 12) | (G & buffer0[buffer3[20] % 20]) | C; + printf("buffer3[92] = %02x\n", buffer3[92]); + + buffer2[12] += ((buffer1[103] & 32) | (buffer3[92] & ((buffer1[103] | 60))) | 16)/3; + printf("buffer2[12] = %02x\n", buffer2[12]); + + buffer3[96] = buffer1[143]; + printf("buffer3[96] = %02x\n", buffer3[96]); + + buffer3[100] = 27; + printf("buffer3[100] = %02x\n", buffer3[100]); + + buffer3[104] = (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) ^ 119; + printf("buffer3[104] = %02x\n", buffer3[104]); + + buffer3[108] = 238 & ((((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) << 1); + printf("buffer3[108] = %02x\n", buffer3[108]); + + buffer3[112] = (~buffer3[64] & (buffer3[84] / 3)) ^ 49; + printf("buffer3[112] = %02x\n", buffer3[112]); + + buffer3[116] = 98 & ((~buffer3[64] & (buffer3[84] / 3)) << 1); + printf("buffer3[116] = %02x\n", buffer3[116]); + + // finale + A = (buffer1[35] & buffer2[8]) | (buffer3[40] & ~buffer2[8]); + B = (A & buffer3[64]) | (((buffer3[84] / 3) & ~buffer3[64])); + buffer1[143] = buffer3[96] - ((B & (86 + ((buffer1[172] & 64) >> 1))) | (((((buffer1[172] & 65) >> 1) ^ 86) | ((~buffer3[64] & (buffer3[84] / 3)) | (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]))) & buffer3[100])); + printf("buffer1[143] = %02x\n", buffer1[143]); + + buffer2[29] = 162; + printf("buffer2[29] = %02x\n", buffer2[29]); + + A = ((((buffer4[buffer3[88] % 21]) & 160) | (buffer0[buffer1[125] % 20] & 95)) >> 1); + B = buffer2[buffer1[149] % 35] ^ (buffer1[43] * buffer1[43]); + buffer0[15] += (B&A) | ((A|B) & 115); + printf("buffer0[15] = %02x\n", buffer0[15]); + + buffer3[120] = buffer3[64] - buffer0[buffer3[40] % 20]; + printf("buffer3[120] = %02x\n", buffer3[120]); + + buffer1[95] = buffer4[buffer3[20] % 21]; + printf("buffer1[95] = %02x\n", buffer1[95]); + + A = weird_ror8(buffer2[buffer3[80] % 35], (buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35]) & 7); + buffer0[7] -= (A*A); + printf("buffer0[7] = %02x\n", buffer0[7]); + + buffer2[8] = buffer2[8] - buffer1[184] + (buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21]); + printf("buffer2[8] = %02x\n", buffer2[8]); + + buffer0[16] = (buffer2[buffer1[102] % 35] << 1) & 132; + printf("buffer0[16] = %02x\n", buffer0[16]); + + buffer3[124] = (buffer4[buffer3[40] % 21] >> 1) ^ buffer3[68]; + printf("buffer3[124] = %02x\n", buffer3[124]); + + buffer0[7] -= (buffer0[buffer1[191] % 20] - (((buffer4[buffer1[80] % 21] << 1) & ~177) | (buffer4[buffer4[buffer3[88] % 21] % 21] & 177))); + printf("buffer0[7] = %02x\n", buffer0[7]); + + buffer0[6] = buffer0[buffer1[119] % 20]; + printf("buffer0[6] = %02x\n", buffer0[6]); + + A = (buffer4[buffer1[190] % 21] & ~209) | (buffer1[118] & 209); + B = buffer0[buffer3[120] % 20] * buffer0[buffer3[120] % 20]; + buffer0[12] = (buffer0[buffer3[84] % 20] ^ (buffer2[buffer1[71] % 35] + buffer2[buffer1[15] % 35])) & ((A & B) | ((A | B) & 27)); + printf("buffer0[12] = %02x\n", buffer0[12]); + + B = (buffer1[32] & buffer2[buffer3[88] % 35]) | ((buffer1[32] | buffer2[buffer3[88] % 35]) & 23); + D = (((buffer4[buffer1[57] % 21] * 231) & 169) | (B & 86)); + F = (((buffer0[buffer1[82] % 20] & ~29) | (buffer4[buffer3[124] % 21] & 29)) & 190) | (buffer4[(D/5) % 21] & ~190); + H = buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20]; + K = (H & buffer1[82]) | (H & 92) | (buffer1[82] & 92); + buffer3[128] = ((F & K) | ((F | K) & 192)) ^ (D/5); + printf("buffer3[128] = %02x\n", buffer3[128]); + + buffer2[25] ^= ((buffer0[buffer3[120] % 20] << 1) * buffer1[5]) - (weird_rol8(buffer3[76], (buffer4[buffer3[124] % 21] & 7)) & (buffer3[20] + 110)); + printf("buffer2[25] = %02x\n", buffer2[25]); + //exit(0); + +} diff --git a/lib/playfair/modified_md5.c b/lib/playfair/modified_md5.c new file mode 100644 index 0000000..3758a33 --- /dev/null +++ b/lib/playfair/modified_md5.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#define printf(...) (void)0; + +int shift[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + +uint32_t F(uint32_t B, uint32_t C, uint32_t D) +{ + return (B & C) | (~B & D); +} + +uint32_t G(uint32_t B, uint32_t C, uint32_t D) +{ + return (B & D) | (C & ~D); +} + +uint32_t H(uint32_t B, uint32_t C, uint32_t D) +{ + return B ^ C ^ D; +} + +uint32_t I(uint32_t B, uint32_t C, uint32_t D) +{ + return C ^ (B | ~D); +} + + +uint32_t rol(uint32_t input, int count) +{ + return ((input << count) & 0xffffffff) | (input & 0xffffffff) >> (32-count); +} + +void swap(uint32_t* a, uint32_t* b) +{ + printf("%08x <-> %08x\n", *a, *b); + uint32_t c = *a; + *a = *b; + *b = c; +} + +void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut) +{ + unsigned char blockIn[64]; + uint32_t* block_words = (uint32_t*)blockIn; + uint32_t* key_words = (uint32_t*)keyIn; + uint32_t* out_words = (uint32_t*)keyOut; + uint32_t A, B, C, D, Z, tmp; + int i; + + memcpy(blockIn, originalblockIn, 64); + + // Each cycle does something like this: + A = key_words[0]; + B = key_words[1]; + C = key_words[2]; + D = key_words[3]; + for (i = 0; i < 64; i++) + { + uint32_t input; + int j; + if (i < 16) + j = i; + else if (i < 32) + j = (5*i + 1) % 16; + else if (i < 48) + j = (3*i + 5) % 16; + else if (i < 64) + j = 7*i % 16; + + input = blockIn[4*j] << 24 | blockIn[4*j+1] << 16 | blockIn[4*j+2] << 8 | blockIn[4*j+3]; + printf("Key = %08x\n", A); + Z = A + input + (int)(long long)((1LL << 32) * fabs(sin(i + 1))); + if (i < 16) + Z = rol(Z + F(B,C,D), shift[i]); + else if (i < 32) + Z = rol(Z + G(B,C,D), shift[i]); + else if (i < 48) + Z = rol(Z + H(B,C,D), shift[i]); + else if (i < 64) + Z = rol(Z + I(B,C,D), shift[i]); + if (i == 63) + printf("Ror is %08x\n", Z); + printf("Output of round %d: %08X + %08X = %08X (shift %d, constant %08X)\n", i, Z, B, Z+B, shift[i], (int)(long long)((1LL << 32) * fabs(sin(i + 1)))); + Z = Z + B; + tmp = D; + D = C; + C = B; + B = Z; + A = tmp; + if (i == 31) + { + // swapsies + swap(&block_words[A & 15], &block_words[B & 15]); + swap(&block_words[C & 15], &block_words[D & 15]); + swap(&block_words[(A & (15<<4))>>4], &block_words[(B & (15<<4))>>4]); + swap(&block_words[(A & (15<<8))>>8], &block_words[(B & (15<<8))>>8]); + swap(&block_words[(A & (15<<12))>>12], &block_words[(B & (15<<12))>>12]); + } + } + printf("%08X %08X %08X %08X\n", A, B, C, D); + // Now we can actually compute the output + printf("Out:\n"); + printf("%08x + %08x = %08x\n", key_words[0], A, key_words[0] + A); + printf("%08x + %08x = %08x\n", key_words[1], B, key_words[1] + B); + printf("%08x + %08x = %08x\n", key_words[2], C, key_words[2] + C); + printf("%08x + %08x = %08x\n", key_words[3], D, key_words[3] + D); + out_words[0] = key_words[0] + A; + out_words[1] = key_words[1] + B; + out_words[2] = key_words[2] + C; + out_words[3] = key_words[3] + D; + +} diff --git a/lib/playfair/omg_hax.c b/lib/playfair/omg_hax.c new file mode 100644 index 0000000..96f79a9 --- /dev/null +++ b/lib/playfair/omg_hax.c @@ -0,0 +1,540 @@ +void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut); +void sap_hash(unsigned char* blockIn, unsigned char* keyOut); + +#include +#include +#include +#include +#include "omg_hax.h" + +#define printf(...) (void)0; + +void xor_blocks(unsigned char* a, unsigned char* b, unsigned char* out) +{ + for (int i = 0; i < 16; i++) + out[i] = a[i] ^ b[i]; +} + + +void z_xor(unsigned char* in, unsigned char* out, int blocks) +{ + for (int j = 0; j < blocks; j++) + for (int i = 0; i < 16; i++) + out[j*16+i] = in[j*16+i] ^ z_key[i]; +} + +void x_xor(unsigned char* in, unsigned char* out, int blocks) +{ + for (int j = 0; j < blocks; j++) + for (int i = 0; i < 16; i++) + out[j*16+i] = in[j*16+i] ^ x_key[i]; +} + + +void t_xor(unsigned char* in, unsigned char* out) +{ + for (int i = 0; i < 16; i++) + out[i] = in[i] ^ t_key[i]; +} + +unsigned char sap_iv[] = {0x2B,0x84,0xFB,0x79,0xDA,0x75,0xB9,0x04,0x6C,0x24,0x73,0xF7,0xD1,0xC4,0xAB,0x0E,0x2B,0x84,0xFB,0x79,0x75,0xB9,0x04,0x6C,0x24,0x73}; + +unsigned char sap_key_material[] = {0xA1, 0x1A, 0x4A, 0x83, + 0xF2, 0x7A, 0x75, 0xEE, + 0xA2, 0x1A, 0x7D, 0xB8, + 0x8D, 0x77, 0x92, 0xAB}; + +unsigned char index_mangle[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C}; + +unsigned char* table_index(int i) +{ + return &table_s1[((31*i) % 0x28) << 8]; +} + +unsigned char* message_table_index(int i) +{ + return &table_s2[(97*i % 144) << 8]; +} + +void print_block(char* msg, unsigned char* dword) +{ + printf("%s", msg); + for (int i = 0; i < 16; i++) + printf("%02X ", dword[i]); + printf("\n"); +} + +void permute_block_1(unsigned char* block) +{ + block[0] = table_s3[block[0]]; + block[4] = table_s3[0x400+block[4]]; + block[8] = table_s3[0x800+block[8]]; + block[12] = table_s3[0xc00+block[12]]; + + unsigned char tmp = block[13]; + block[13] = table_s3[0x100+block[9]]; + block[9] = table_s3[0xd00+block[5]]; + block[5] = table_s3[0x900+block[1]]; + block[1] = table_s3[0x500+tmp]; + + tmp = block[2]; + block[2] = table_s3[0xa00+block[10]]; + block[10] = table_s3[0x200+tmp]; + tmp = block[6]; + block[6] = table_s3[0xe00+block[14]]; + block[14] = table_s3[0x600+tmp]; + + tmp = block[3]; + block[3] = table_s3[0xf00+block[7]]; + block[7] = table_s3[0x300+block[11]]; + block[11] = table_s3[0x700+block[15]]; + block[15] = table_s3[0xb00+tmp]; + print_block("Permutation complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel +} + +unsigned char* permute_table_2(unsigned int i) +{ + return &table_s4[((71 * i) % 144) << 8]; +} + +void permute_block_2(unsigned char* block, int round) +{ + // round is 0..8? + printf("Permuting via table2, round %d... (block[0] = %02X)\n", round, block[0]); + block[0] = permute_table_2(round*16+0)[block[0]]; + block[4] = permute_table_2(round*16+4)[block[4]]; + block[8] = permute_table_2(round*16+8)[block[8]]; + block[12] = permute_table_2(round*16+12)[block[12]]; + + unsigned char tmp = block[13]; + block[13] = permute_table_2(round*16+13)[block[9]]; + block[9] = permute_table_2(round*16+9)[block[5]]; + block[5] = permute_table_2(round*16+5)[block[1]]; + block[1] = permute_table_2(round*16+1)[tmp]; + + tmp = block[2]; + block[2] = permute_table_2(round*16+2)[block[10]]; + block[10] = permute_table_2(round*16+10)[tmp]; + tmp = block[6]; + block[6] = permute_table_2(round*16+6)[block[14]]; + block[14] = permute_table_2(round*16+14)[tmp]; + + tmp = block[3]; + block[3] = permute_table_2(round*16+3)[block[7]]; + block[7] = permute_table_2(round*16+7)[block[11]]; + block[11] = permute_table_2(round*16+11)[block[15]]; + block[15] = permute_table_2(round*16+15)[tmp]; + print_block("Permutation (2) complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel +} + +// This COULD just be Rijndael key expansion, but with a different set of S-boxes +void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4]) +{ + uint32_t key_data[4]; + int i; + for (i = 0; i < 11; i++) + { + key_schedule[i][0] = 0xdeadbeef; + key_schedule[i][1] = 0xdeadbeef; + key_schedule[i][2] = 0xdeadbeef; + key_schedule[i][3] = 0xdeadbeef; + } + unsigned char* buffer = (unsigned char*)key_data; + int ti = 0; + printf("Generating key schedule\n"); + // G + print_block("Raw key material: ", key_material); + t_xor(key_material, buffer); + print_block("G has produced: ", buffer); + for (int round = 0; round < 11; round++) + { + printf("Starting round %d\n", round); + // H + key_schedule[round][0] = key_data[0]; + printf("H has set chunk 1 of round %d %08X\n", round, key_schedule[round][0]); + printf("H complete\n"); + // I + unsigned char* table1 = table_index(ti); + unsigned char* table2 = table_index(ti+1); + unsigned char* table3 = table_index(ti+2); + unsigned char* table4 = table_index(ti+3); + ti += 4; + //buffer[0] = (buffer[0] - (4 & (buffer[0] << 1)) + 2) ^ 2 ^ index_mangle[round] ^ table1[buffer[0x0d]]; + printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0d], table1[buffer[0x0d]]); + printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0e], table2[buffer[0x0e]]); + printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0f], table3[buffer[0x0f]]); + printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0c], table4[buffer[0x0c]]); + buffer[0] ^= table1[buffer[0x0d]] ^ index_mangle[round]; + buffer[1] ^= table2[buffer[0x0e]]; + buffer[2] ^= table3[buffer[0x0f]]; + buffer[3] ^= table4[buffer[0x0c]]; + print_block("After I, buffer is now: ", buffer); + printf("I complete\n"); + // H + key_schedule[round][1] = key_data[1]; + printf("H has set chunk 2 to %08X\n", key_schedule[round][1]); + + printf("H complete\n"); + // J + key_data[1] ^= key_data[0]; + printf("J complete\n"); + print_block("Buffer is now ", buffer); + // H + key_schedule[round][2] = key_data[2]; + printf("H has set chunk3 to %08X\n", key_schedule[round][2]); + printf("H complete\n"); + + // J + key_data[2] ^= key_data[1]; + printf("J complete\n"); + // K and L + // Implement K and L to fill in other bits of the key schedule + key_schedule[round][3] = key_data[3]; + // J again + key_data[3] ^= key_data[2]; + printf("J complete\n"); + } + for (i = 0; i < 11; i++) + print_block("Schedule: ", (unsigned char*)key_schedule[i]); +} + +// This MIGHT just be AES, or some variant thereof. +void cycle(unsigned char* block, uint32_t key_schedule[11][4]) +{ + uint32_t ptr1 = 0; + uint32_t ptr2 = 0; + uint32_t ptr3 = 0; + uint32_t ptr4 = 0; + uint32_t ab; + unsigned char* buffer = (unsigned char*)&ab; + uint32_t* bWords = (uint32_t*)block; + bWords[0] ^= key_schedule[10][0]; + bWords[1] ^= key_schedule[10][1]; + bWords[2] ^= key_schedule[10][2]; + bWords[3] ^= key_schedule[10][3]; + // First, these are permuted + permute_block_1(block); + + for (int round = 0; round < 9; round++) + { + // E + // Note that table_s5 is a table of 4-byte words. Therefore we do not need to <<2 these indices + // TODO: Are these just T-tables? + unsigned char* key0 = (unsigned char*)&key_schedule[9-round][0]; + ptr1 = table_s5[block[3] ^ key0[3]]; + ptr2 = table_s6[block[2] ^ key0[2]]; + ptr3 = table_s8[block[0] ^ key0[0]]; + ptr4 = table_s7[block[1] ^ key0[1]]; + + // A B + ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4; + printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab); + // C + ((uint32_t*)block)[0] = ab; + printf("f7 = %02X\n", block[7]); + unsigned char* key1 = (unsigned char*)&key_schedule[9-round][1]; + ptr2 = table_s5[block[7] ^ key1[3]]; + ptr1 = table_s6[block[6] ^ key1[2]]; + ptr4 = table_s7[block[5] ^ key1[1]]; + ptr3 = table_s8[block[4] ^ key1[0]]; + // A B again + ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4; + printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab); + // D is a bit of a nightmare, but it is really not as complicated as you might think + unsigned char* key2 = (unsigned char*)&key_schedule[9-round][2]; + unsigned char* key3 = (unsigned char*)&key_schedule[9-round][3]; + ((uint32_t*)block)[1] = ab; + ((uint32_t*)block)[2] = table_s5[block[11] ^ key2[3]] ^ + table_s6[block[10] ^ key2[2]] ^ + table_s7[block[9] ^ key2[1]] ^ + table_s8[block[8] ^ key2[0]]; + + ((uint32_t*)block)[3] = table_s5[block[15] ^ key3[3]] ^ + table_s6[block[14] ^ key3[2]] ^ + table_s7[block[13] ^ key3[1]] ^ + table_s8[block[12] ^ key3[0]]; + printf("Set block2 = %08X, block3 = %08X\n", ((uint32_t*)block)[2], ((uint32_t*)block)[3]); + // In the last round, instead of the permute, we do F + permute_block_2(block, 8-round); + } + printf("Using last bit of key up: %08X xor %08X -> %08X\n", ((uint32_t*)block)[0], key_schedule[0][0], ((uint32_t*)block)[0] ^ key_schedule[0][0]); + ((uint32_t*)block)[0] ^= key_schedule[0][0]; + ((uint32_t*)block)[1] ^= key_schedule[0][1]; + ((uint32_t*)block)[2] ^= key_schedule[0][2]; + ((uint32_t*)block)[3] ^= key_schedule[0][3]; +} + + +void decrypt_sap(unsigned char* sapIn, unsigned char* sapOut) +{ + uint32_t key_schedule[11][4]; + unsigned char* iv; + print_block("Base sap: ", &sapIn[0xf0]); + z_xor(sapIn, sapOut, 16); + generate_key_schedule(sap_key_material, key_schedule); + print_block("lastSap before cycle: ", &sapOut[0xf0]); + for (int i = 0xf0; i >= 0x00; i-=0x10) + { + printf("Ready to cycle %02X\n", i); + cycle(&sapOut[i], key_schedule); + print_block("After cycling, block is: ", &sapOut[i]); + if (i > 0) + { // xor with previous block + iv = &sapOut[i-0x10]; + } + else + { // xor with sap IV + iv = sap_iv; + } + for (int j = 0; j < 16; j++) + { + printf("%02X ^ %02X -> %02X\n", sapOut[i+j], iv[j], sapOut[i+j] ^ iv[j]); + sapOut[i+j] = sapOut[i+j] ^ iv[j]; + } + printf("Decrypted SAP %02X-%02X:\n", i, i+0xf); + print_block("", &sapOut[i]); + } + // Lastly grind the whole thing through x_key. This is the last time we modify sap + x_xor(sapOut, sapOut, 16); + printf("Sap is decrypted to\n"); + for (int i = 0xf0; i >= 0x00; i-=0x10) + { + printf("Final SAP %02X-%02X: ", i, i+0xf); + print_block("", &sapOut[i]); + } +} + +unsigned char initial_session_key[] = {0xDC, 0xDC, 0xF3, 0xB9, 0x0B, 0x74, 0xDC, 0xFB, 0x86, 0x7F, 0xF7, 0x60, 0x16, 0x72, 0x90, 0x51}; + + +void decrypt_key(unsigned char* decryptedSap, unsigned char* keyIn, unsigned char* iv, unsigned char* keyOut) +{ + unsigned char blockIn[16]; + uint32_t key_schedule[11][4]; + uint32_t mode_key_schedule[11][4]; + generate_key_schedule(&decryptedSap[8], key_schedule); + printf("Generating mode key:\n"); + generate_key_schedule(initial_session_key, mode_key_schedule); + z_xor(keyIn, blockIn, 1); + print_block("Input to cycle is: ", blockIn); + cycle(blockIn, key_schedule); + for (int j = 0; j < 16; j++) + keyOut[j] = blockIn[j] ^ iv[j]; + print_block("Output from cycle is: ", keyOut); + x_xor(keyOut, keyOut, 1); +} + + +void decryptMessage(unsigned char* messageIn, unsigned char* decryptedMessage) +{ + unsigned char buffer[16]; + int i, j; + unsigned char tmp; + uint32_t key_schedule[11][4]; + int mode = messageIn[12]; // 0,1,2,3 + printf("mode = %02x\n", mode); + generate_key_schedule(initial_session_key, key_schedule); + + // For M0-M6 we follow the same pattern + for (i = 0; i < 8; i++) + { + // First, copy in the nth block (we must start with the last one) + for (j = 0; j < 16; j++) + { + if (mode == 3) + buffer[j] = messageIn[(0x80-0x10*i)+j]; + else if (mode == 2 || mode == 1 || mode == 0) + buffer[j] = messageIn[(0x10*(i+1))+j]; + } + // do this permutation and update 9 times. Could this be cycle(), or the reverse of cycle()? + for (j = 0; j < 9; j++) + { + int base = 0x80 - 0x10*j; + //print_block("About to cycle. Buffer is currently: ", buffer); + buffer[0x0] = message_table_index(base+0x0)[buffer[0x0]] ^ message_key[mode][base+0x0]; + buffer[0x4] = message_table_index(base+0x4)[buffer[0x4]] ^ message_key[mode][base+0x4]; + buffer[0x8] = message_table_index(base+0x8)[buffer[0x8]] ^ message_key[mode][base+0x8]; + buffer[0xc] = message_table_index(base+0xc)[buffer[0xc]] ^ message_key[mode][base+0xc]; + + tmp = buffer[0x0d]; + buffer[0xd] = message_table_index(base+0xd)[buffer[0x9]] ^ message_key[mode][base+0xd]; + buffer[0x9] = message_table_index(base+0x9)[buffer[0x5]] ^ message_key[mode][base+0x9]; + buffer[0x5] = message_table_index(base+0x5)[buffer[0x1]] ^ message_key[mode][base+0x5]; + buffer[0x1] = message_table_index(base+0x1)[tmp] ^ message_key[mode][base+0x1]; + + tmp = buffer[0x02]; + buffer[0x2] = message_table_index(base+0x2)[buffer[0xa]] ^ message_key[mode][base+0x2]; + buffer[0xa] = message_table_index(base+0xa)[tmp] ^ message_key[mode][base+0xa]; + tmp = buffer[0x06]; + buffer[0x6] = message_table_index(base+0x6)[buffer[0xe]] ^ message_key[mode][base+0x6]; + buffer[0xe] = message_table_index(base+0xe)[tmp] ^ message_key[mode][base+0xe]; + + tmp = buffer[0x3]; + buffer[0x3] = message_table_index(base+0x3)[buffer[0x7]] ^ message_key[mode][base+0x3]; + buffer[0x7] = message_table_index(base+0x7)[buffer[0xb]] ^ message_key[mode][base+0x7]; + buffer[0xb] = message_table_index(base+0xb)[buffer[0xf]] ^ message_key[mode][base+0xb]; + buffer[0xf] = message_table_index(base+0xf)[tmp] ^ message_key[mode][base+0xf]; + + // Now we must replace the entire buffer with 4 words that we read and xor together + uint32_t word; + uint32_t* block = (uint32_t*)buffer; + + block[0] = table_s9[0x000 + buffer[0x0]] ^ + table_s9[0x100 + buffer[0x1]] ^ + table_s9[0x200 + buffer[0x2]] ^ + table_s9[0x300 + buffer[0x3]]; + block[1] = table_s9[0x000 + buffer[0x4]] ^ + table_s9[0x100 + buffer[0x5]] ^ + table_s9[0x200 + buffer[0x6]] ^ + table_s9[0x300 + buffer[0x7]]; + block[2] = table_s9[0x000 + buffer[0x8]] ^ + table_s9[0x100 + buffer[0x9]] ^ + table_s9[0x200 + buffer[0xa]] ^ + table_s9[0x300 + buffer[0xb]]; + block[3] = table_s9[0x000 + buffer[0xc]] ^ + table_s9[0x100 + buffer[0xd]] ^ + table_s9[0x200 + buffer[0xe]] ^ + table_s9[0x300 + buffer[0xf]]; + } + // Next, another permute with a different table + buffer[0x0] = table_s10[(0x0 << 8) + buffer[0x0]]; + buffer[0x4] = table_s10[(0x4 << 8) + buffer[0x4]]; + buffer[0x8] = table_s10[(0x8 << 8) + buffer[0x8]]; + buffer[0xc] = table_s10[(0xc << 8) + buffer[0xc]]; + + tmp = buffer[0x0d]; + buffer[0xd] = table_s10[(0xd << 8) + buffer[0x9]]; + buffer[0x9] = table_s10[(0x9 << 8) + buffer[0x5]]; + buffer[0x5] = table_s10[(0x5 << 8) + buffer[0x1]]; + buffer[0x1] = table_s10[(0x1 << 8) + tmp]; + + tmp = buffer[0x02]; + buffer[0x2] = table_s10[(0x2 << 8) + buffer[0xa]]; + buffer[0xa] = table_s10[(0xa << 8) + tmp]; + tmp = buffer[0x06]; + buffer[0x6] = table_s10[(0x6 << 8) + buffer[0xe]]; + buffer[0xe] = table_s10[(0xe << 8) + tmp]; + + tmp = buffer[0x3]; + buffer[0x3] = table_s10[(0x3 << 8) + buffer[0x7]]; + buffer[0x7] = table_s10[(0x7 << 8) + buffer[0xb]]; + buffer[0xb] = table_s10[(0xb << 8) + buffer[0xf]]; + buffer[0xf] = table_s10[(0xf << 8) + tmp]; + + // And finally xor with the previous block of the message, except in mode-2 where we do this in reverse + if (mode == 2 || mode == 1 || mode == 0) + { + if (i > 0) + { + xor_blocks(buffer, &messageIn[0x10*i], &decryptedMessage[0x10*i]); // remember that the first 0x10 bytes are the header + } + else + xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x10*i]); + print_block(" ", &decryptedMessage[0x10*i]); + } + else + { + if (i < 7) + xor_blocks(buffer, &messageIn[0x70 - 0x10*i], &decryptedMessage[0x70 - 0x10*i]); + else + xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x70 - 0x10*i]); + printf("Decrypted message block %02X-%02X:", 0x70 - 0x10*i, 0x70 - 0x10*i+0xf); + print_block(" ", &decryptedMessage[0x70 - 0x10*i]); + } + } +} + +unsigned char static_source_1[] = {0xFA, 0x9C, 0xAD, 0x4D, 0x4B, 0x68, 0x26, 0x8C, 0x7F, 0xF3, 0x88, 0x99, 0xDE, 0x92, 0x2E, 0x95, + 0x1E}; +unsigned char static_source_2[] = {0xEC, 0x4E, 0x27, 0x5E, 0xFD, 0xF2, 0xE8, 0x30, 0x97, 0xAE, 0x70, 0xFB, 0xE0, 0x00, 0x3F, 0x1C, + 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x09, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00}; + +void swap_bytes(unsigned char* a, unsigned char *b) +{ + unsigned char c = *a; + *a = *b; + *b = c; +} + +void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey) +{ + unsigned char decryptedMessage[128]; + unsigned char newSap[320]; + unsigned char Q[210]; + int i; + int round; + unsigned char md5[16]; + unsigned char otherHash[16]; + + decryptMessage(messageIn, decryptedMessage); + // Now that we have the decrypted message, we can combine it with our initial sap to form the 5 blocks needed to generate the 5 words which, when added together, give + // the session key. + memcpy(&newSap[0x000], static_source_1, 0x11); + memcpy(&newSap[0x011], decryptedMessage, 0x80); + memcpy(&newSap[0x091], &oldSap[0x80], 0x80); + memcpy(&newSap[0x111], static_source_2, 0x2f); + memcpy(sessionKey, initial_session_key, 16); + + for (round = 0; round < 5; round++) + { + unsigned char* base = &newSap[round * 64]; + print_block("Input block: ", &base[0]); + print_block("Input block: ", &base[0x10]); + print_block("Input block: ", &base[0x20]); + print_block("Input block: ", &base[0x30]); + modified_md5(base, sessionKey, md5); + printf("MD5 OK\n"); + sap_hash(base, sessionKey); + printf("OtherHash OK\n"); + + printf("MD5 = "); + for (i = 0; i < 4; i++) + printf("%08x ", ((uint32_t*)md5)[i]); + printf("\nOtherHash = "); + for (i = 0; i < 4; i++) + printf("%08x ", ((uint32_t*)sessionKey)[i]); + printf("\n"); + + uint32_t* sessionKeyWords = (uint32_t*)sessionKey; + uint32_t* md5Words = (uint32_t*)md5; + for (i = 0; i < 4; i++) + { + sessionKeyWords[i] = (sessionKeyWords[i] + md5Words[i]) & 0xffffffff; + } + printf("Current key: "); + for (i = 0; i < 16; i++) + printf("%02x", sessionKey[i]); + printf("\n"); + } + for (i = 0; i < 16; i+=4) + { + swap_bytes(&sessionKey[i], &sessionKey[i+3]); + swap_bytes(&sessionKey[i+1], &sessionKey[i+2]); + } + + // Finally the whole thing is XORd with 121: + for (i = 0; i < 16; i++) + sessionKey[i] ^= 121; + print_block("Session key computed as: ", sessionKey); +} + +unsigned char default_sap[] = + { 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x53, + 0x00, 0x01, 0xcc, 0x34, 0x2a, 0x5e, 0x5b, 0x1a, 0x67, 0x73, 0xc2, 0x0e, 0x21, 0xb8, 0x22, 0x4d, + 0xf8, 0x62, 0x48, 0x18, 0x64, 0xef, 0x81, 0x0a, 0xae, 0x2e, 0x37, 0x03, 0xc8, 0x81, 0x9c, 0x23, + 0x53, 0x9d, 0xe5, 0xf5, 0xd7, 0x49, 0xbc, 0x5b, 0x7a, 0x26, 0x6c, 0x49, 0x62, 0x83, 0xce, 0x7f, + 0x03, 0x93, 0x7a, 0xe1, 0xf6, 0x16, 0xde, 0x0c, 0x15, 0xff, 0x33, 0x8c, 0xca, 0xff, 0xb0, 0x9e, + 0xaa, 0xbb, 0xe4, 0x0f, 0x5d, 0x5f, 0x55, 0x8f, 0xb9, 0x7f, 0x17, 0x31, 0xf8, 0xf7, 0xda, 0x60, + 0xa0, 0xec, 0x65, 0x79, 0xc3, 0x3e, 0xa9, 0x83, 0x12, 0xc3, 0xb6, 0x71, 0x35, 0xa6, 0x69, 0x4f, + 0xf8, 0x23, 0x05, 0xd9, 0xba, 0x5c, 0x61, 0x5f, 0xa2, 0x54, 0xd2, 0xb1, 0x83, 0x45, 0x83, 0xce, + 0xe4, 0x2d, 0x44, 0x26, 0xc8, 0x35, 0xa7, 0xa5, 0xf6, 0xc8, 0x42, 0x1c, 0x0d, 0xa3, 0xf1, 0xc7, + 0x00, 0x50, 0xf2, 0xe5, 0x17, 0xf8, 0xd0, 0xfa, 0x77, 0x8d, 0xfb, 0x82, 0x8d, 0x40, 0xc7, 0x8e, + 0x94, 0x1e, 0x1e, 0x1e}; diff --git a/lib/playfair/omg_hax.h b/lib/playfair/omg_hax.h new file mode 100644 index 0000000..d6f19d3 --- /dev/null +++ b/lib/playfair/omg_hax.h @@ -0,0 +1,32 @@ +unsigned char message_key[4][144] = {{0x1D, 0x24, 0x03, 0x40, 0xDC, 0xAE, 0xC7, 0xA8, 0x26, 0x7C, 0x20, 0x99, 0x5D, 0x7E, 0x89, 0x2E, 0xA2, 0x58, 0xAF, 0xBE, 0xB8, 0x07, 0x9A, 0x2F, 0x87, 0x77, 0xD3, 0xCE, 0x37, 0x3E, 0x1B, 0x16, 0x41, 0x4F, 0x4E, 0xBE, 0x62, 0x5A, 0x00, 0x77, 0xC6, 0xEB, 0xDA, 0x4B, 0x97, 0x1A, 0x61, 0x8D, 0x31, 0x32, 0x1C, 0xA2, 0x78, 0x9B, 0x66, 0x72, 0x60, 0x94, 0x44, 0x86, 0xCB, 0x09, 0xBD, 0x3A, 0x77, 0x57, 0xC1, 0x72, 0x61, 0x1D, 0x32, 0xC7, 0x85, 0xD1, 0xEF, 0xE5, 0x4D, 0x95, 0x0B, 0xF0, 0xD8, 0x18, 0xE7, 0x4A, 0xDC, 0x77, 0xCA, 0x55, 0x28, 0x32, 0x93, 0x2A, 0x7B, 0x3E, 0x3A, 0xD4, 0x97, 0xFD, 0x7D, 0x6D, 0x95, 0x71, 0x27, 0x9C, 0x77, 0x6A, 0x7C, 0xD5, 0xBF, 0x9D, 0x0E, 0xF2, 0x0F, 0x55, 0x91, 0x29, 0xCF, 0xAA, 0x58, 0x1C, 0x7A, 0xE7, 0xCB, 0x8B, 0x20, 0x07, 0x53, 0xAA, 0x59, 0x40, 0x3B, 0x03, 0xBE, 0x33, 0x47, 0x47, 0x5A, 0x4F, 0x86, 0x31, 0x8D, 0x30, 0xF9, 0x1C}, + {0xF1, 0xA2, 0x04, 0x7A, 0xAE, 0xE9, 0xD8, 0xBF, 0xD4, 0xC0, 0x6B, 0x77, 0xC1, 0x05, 0x8C, 0x99, 0xA9, 0xFD, 0x3D, 0x44, 0xEE, 0x7B, 0x6C, 0x28, 0x42, 0x31, 0x63, 0x87, 0x6D, 0xD2, 0x6D, 0x48, 0xCC, 0x4E, 0x93, 0x31, 0x7B, 0x27, 0x14, 0xFC, 0x2D, 0x71, 0x5D, 0xE4, 0xB0, 0xF9, 0x4B, 0x82, 0x76, 0x52, 0xD5, 0x02, 0x6C, 0xB6, 0xCF, 0x57, 0xFE, 0xB2, 0xBF, 0xB7, 0x30, 0x56, 0x7B, 0x9B, 0x3E, 0x3E, 0xB0, 0x47, 0x10, 0x63, 0xE8, 0x72, 0x1C, 0x38, 0x2D, 0x79, 0xC4, 0x77, 0x3C, 0xD1, 0xED, 0x02, 0x43, 0x03, 0x5C, 0xBC, 0x57, 0x9E, 0x43, 0x02, 0x67, 0xA1, 0x9B, 0x8C, 0xF3, 0x54, 0xE4, 0x46, 0xE1, 0x1C, 0x4F, 0xDC, 0xF7, 0x9F, 0xF4, 0x49, 0x76, 0x4F, 0x13, 0x96, 0x86, 0xCF, 0xF1, 0x7A, 0x01, 0xAC, 0xE4, 0xD5, 0x32, 0x5B, 0x5D, 0x7D, 0xEE, 0xCA, 0xBF, 0x76, 0xFB, 0x50, 0xD7, 0xEC, 0x9C, 0xA3, 0xF6, 0x2E, 0xBE, 0x9B, 0xC7, 0xC8, 0x0F, 0xF2, 0xB7, 0x3B, 0xDE, 0x8A}, + {0x18, 0x6E, 0xD3, 0x73, 0x5E, 0xE9, 0x5A, 0x8F, 0x66, 0x3F, 0xF1, 0xB8, 0x4A, 0x62, 0xD9, 0xC0, 0xD2, 0x08, 0x13, 0x61, 0xCB, 0xF3, 0xAD, 0xA6, 0x26, 0x4D, 0x3A, 0x7B, 0x06, 0xB5, 0x51, 0x56, 0xFE, 0x66, 0x0A, 0xD8, 0x3A, 0xAA, 0x47, 0x49, 0xD3, 0x7C, 0xC3, 0x68, 0x70, 0xD0, 0x96, 0x80, 0x6A, 0x05, 0x90, 0xEF, 0xAF, 0x43, 0x42, 0xC4, 0x2E, 0x50, 0x4C, 0x96, 0x13, 0xB5, 0x2E, 0x4C, 0x80, 0xA2, 0x8D, 0x23, 0xEE, 0xE2, 0x5E, 0x78, 0xF4, 0x3D, 0x65, 0xCA, 0x71, 0x4F, 0x68, 0x9E, 0x4B, 0x43, 0x58, 0x7B, 0x47, 0x96, 0x40, 0x81, 0x8A, 0x98, 0x6C, 0x04, 0x33, 0x0F, 0x2F, 0x1C, 0x33, 0x8E, 0xEA, 0xA1, 0x4F, 0xA8, 0x37, 0x93, 0x17, 0x1D, 0x8D, 0x18, 0xA9, 0x6A, 0x1B, 0x07, 0x7C, 0xB6, 0x08, 0x58, 0x1F, 0x12, 0x00, 0xFA, 0x37, 0x4D, 0x7F, 0xBA, 0xA5, 0x00, 0x6B, 0x72, 0x78, 0x9C, 0x33, 0xE8, 0x41, 0x07, 0xB7, 0xC1, 0x67, 0x9B, 0x76, 0xBB, 0xDD, 0x91, 0x3D, 0x3D}, + {0x47, 0x69, 0x9F, 0x08, 0xB8, 0x82, 0xFB, 0xA1, 0x95, 0xE5, 0x6F, 0x41, 0x79, 0x1E, 0x0C, 0xB6, 0xA1, 0xCA, 0x11, 0x0A, 0xE2, 0x87, 0x2C, 0x7E, 0x39, 0xBC, 0x98, 0xA5, 0x1E, 0xB2, 0xFA, 0x1F, 0xEE, 0x73, 0x42, 0xD7, 0xA9, 0x09, 0x42, 0xC0, 0xEF, 0xC4, 0x44, 0x0C, 0x0F, 0x6F, 0x97, 0x09, 0x08, 0xBC, 0x66, 0x31, 0x33, 0xFF, 0xCA, 0x7E, 0xB5, 0xE9, 0x7D, 0x77, 0x98, 0xC0, 0xD2, 0x6A, 0xFD, 0x2F, 0x0B, 0x6C, 0x9D, 0xAB, 0xAA, 0x78, 0x4C, 0x76, 0xDE, 0x21, 0xBF, 0xF4, 0x3A, 0x28, 0x2A, 0xC4, 0x74, 0xB4, 0xA9, 0x1B, 0x9A, 0x38, 0x21, 0x4C, 0xEB, 0xBD, 0x72, 0x51, 0xA6, 0x15, 0xD4, 0x9E, 0x17, 0xF3, 0x94, 0x26, 0x6D, 0x07, 0x5F, 0x92, 0xAA, 0xA4, 0x4E, 0xF2, 0xCD, 0x3F, 0x02, 0x4F, 0x05, 0x35, 0xE3, 0x58, 0xDF, 0x82, 0x7E, 0x6A, 0x17, 0xF0, 0x5F, 0x6B, 0xDC, 0xE9, 0x3A, 0xCF, 0x04, 0xB3, 0x01, 0x44, 0x87, 0xD7, 0xBC, 0xAD, 0x3D, 0x74, 0x96, 0x74, 0xA3, 0x99}}; +unsigned char message_iv[4][16] = {{0x57, 0x52, 0xF1, 0xB7, 0x54, 0x9D, 0x8F, 0x87, 0x0C, 0x10, 0x48, 0x5A, 0x60, 0x88, 0xCA, 0xDB}, + {0xDF, 0x7B, 0x15, 0x63, 0xF0, 0x05, 0x58, 0x77, 0x52, 0xA9, 0x04, 0x02, 0xB9, 0xA3, 0x92, 0x95}, + {0x68, 0xB5, 0x46, 0x11, 0xFB, 0x04, 0xDE, 0x67, 0x6C, 0x96, 0x8E, 0xFB, 0x8C, 0x9D, 0xB0, 0xC9}, + {0x27, 0x07, 0x8B, 0x21, 0x23, 0x36, 0x1E, 0x7A, 0xDC, 0x9D, 0x0B, 0x11, 0x53, 0x54, 0x69, 0x0D}}; + +unsigned char z_key[] = {0x1a, 0x64, 0xf9, 0x60, 0x6c, 0xe3, 0x01, 0xa9, 0x54, 0x48, 0x1b, 0xd4, 0xab, 0x81, 0xfc, 0xc6}; +unsigned char x_key[] = {0x8e, 0xba, 0x07, 0xcc, 0xb6, 0x5a, 0xf6, 0x20, 0x33, 0xcf, 0xf8, 0x42, 0xe5, 0xd5, 0x5a, 0x7d}; +unsigned char t_key[] = {0xd0, 0x04, 0xa9, 0x61, 0x6b, 0xa4, 0x00, 0x87, 0x68, 0x8b, 0x5f, 0x15, 0x15, 0x35, 0xd9, 0xa9}; + +unsigned long table_s5[] = {0x21aa8423, 0x2fa1892a, 0x3dbc9e31, 0x33b79338, 0x1986b007, 0x178dbd0e, 0x590aa15, 0xb9ba71c, 0x51f2ec6b, 0x5ff9e162, 0x4de4f679, 0x43effb70, 0x69ded84f, 0x67d5d546, 0x75c8c25d, 0x7bc3cf54, 0xc11a54b3, 0xcf1159ba, 0xdd0c4ea1, 0xd30743a8, 0xf9366097, 0xf73d6d9e, 0xe5207a85, 0xeb2b778c, 0xb1423cfb, 0xbf4931f2, 0xad5426e9, 0xa35f2be0, 0x896e08df, 0x876505d6, 0x957812cd, 0x9b731fc4, 0xfad13f18, 0xf4da3211, 0xe6c7250a, 0xe8cc2803, 0xc2fd0b3c, 0xccf60635, 0xdeeb112e, 0xd0e01c27, 0x8a895750, 0x84825a59, 0x969f4d42, 0x9894404b, 0xb2a56374, 0xbcae6e7d, 0xaeb37966, 0xa0b8746f, 0x1a61ef88, 0x146ae281, 0x677f59a, 0x87cf893, 0x224ddbac, 0x2c46d6a5, 0x3e5bc1be, 0x3050ccb7, 0x6a3987c0, 0x64328ac9, 0x762f9dd2, 0x782490db, 0x5215b3e4, 0x5c1ebeed, 0x4e03a9f6, 0x4008a4ff, 0x8c5ce955, 0x8257e45c, 0x904af347, 0x9e41fe4e, 0xb470dd71, 0xba7bd078, 0xa866c763, 0xa66dca6a, 0xfc04811d, 0xf20f8c14, 0xe0129b0f, 0xee199606, 0xc428b539, 0xca23b830, 0xd83eaf2b, 0xd635a222, 0x6cec39c5, 0x62e734cc, 0x70fa23d7, 0x7ef12ede, 0x54c00de1, 0x5acb00e8, 0x48d617f3, 0x46dd1afa, 0x1cb4518d, 0x12bf5c84, 0xa24b9f, 0xea94696, 0x249865a9, 0x2a9368a0, 0x388e7fbb, 0x368572b2, 0x5727526e, 0x592c5f67, 0x4b31487c, 0x453a4575, 0x6f0b664a, 0x61006b43, 0x731d7c58, 0x7d167151, 0x277f3a26, 0x2974372f, 0x3b692034, 0x35622d3d, 0x1f530e02, 0x1158030b, 0x3451410, 0xd4e1919, 0xb79782fe, 0xb99c8ff7, 0xab8198ec, 0xa58a95e5, 0x8fbbb6da, 0x81b0bbd3, 0x93adacc8, 0x9da6a1c1, 0xc7cfeab6, 0xc9c4e7bf, 0xdbd9f0a4, 0xd5d2fdad, 0xffe3de92, 0xf1e8d39b, 0xe3f5c480, 0xedfec989, 0x605d5ecf, 0x6e5653c6, 0x7c4b44dd, 0x724049d4, 0x58716aeb, 0x567a67e2, 0x446770f9, 0x4a6c7df0, 0x10053687, 0x1e0e3b8e, 0xc132c95, 0x218219c, 0x282902a3, 0x26220faa, 0x343f18b1, 0x3a3415b8, 0x80ed8e5f, 0x8ee68356, 0x9cfb944d, 0x92f09944, 0xb8c1ba7b, 0xb6cab772, 0xa4d7a069, 0xaadcad60, 0xf0b5e617, 0xfebeeb1e, 0xeca3fc05, 0xe2a8f10c, 0xc899d233, 0xc692df3a, 0xd48fc821, 0xda84c528, 0xbb26e5f4, 0xb52de8fd, 0xa730ffe6, 0xa93bf2ef, 0x830ad1d0, 0x8d01dcd9, 0x9f1ccbc2, 0x9117c6cb, 0xcb7e8dbc, 0xc57580b5, 0xd76897ae, 0xd9639aa7, 0xf352b998, 0xfd59b491, 0xef44a38a, 0xe14fae83, 0x5b963564, 0x559d386d, 0x47802f76, 0x498b227f, 0x63ba0140, 0x6db10c49, 0x7fac1b52, 0x71a7165b, 0x2bce5d2c, 0x25c55025, 0x37d8473e, 0x39d34a37, 0x13e26908, 0x1de96401, 0xff4731a, 0x1ff7e13, 0xcdab33b9, 0xc3a03eb0, 0xd1bd29ab, 0xdfb624a2, 0xf587079d, 0xfb8c0a94, 0xe9911d8f, 0xe79a1086, 0xbdf35bf1, 0xb3f856f8, 0xa1e541e3, 0xafee4cea, 0x85df6fd5, 0x8bd462dc, 0x99c975c7, 0x97c278ce, 0x2d1be329, 0x2310ee20, 0x310df93b, 0x3f06f432, 0x1537d70d, 0x1b3cda04, 0x921cd1f, 0x72ac016, 0x5d438b61, 0x53488668, 0x41559173, 0x4f5e9c7a, 0x656fbf45, 0x6b64b24c, 0x7979a557, 0x7772a85e, 0x16d08882, 0x18db858b, 0xac69290, 0x4cd9f99, 0x2efcbca6, 0x20f7b1af, 0x32eaa6b4, 0x3ce1abbd, 0x6688e0ca, 0x6883edc3, 0x7a9efad8, 0x7495f7d1, 0x5ea4d4ee, 0x50afd9e7, 0x42b2cefc, 0x4cb9c3f5, 0xf6605812, 0xf86b551b, 0xea764200, 0xe47d4f09, 0xce4c6c36, 0xc047613f, 0xd25a7624, 0xdc517b2d, 0x8638305a, 0x88333d53, 0x9a2e2a48, 0x94252741, 0xbe14047e, 0xb01f0977, 0xa2021e6c, 0xac091365}; + +unsigned long table_s6[] = {0x5ee7493, 0xce07f9e, 0x17f26289, 0x1efc6984, 0x21d658a7, 0x28d853aa, 0x33ca4ebd, 0x3ac445b0, 0x4d9e2cfb, 0x449027f6, 0x5f823ae1, 0x568c31ec, 0x69a600cf, 0x60a80bc2, 0x7bba16d5, 0x72b41dd8, 0x950ec443, 0x9c00cf4e, 0x8712d259, 0x8e1cd954, 0xb136e877, 0xb838e37a, 0xa32afe6d, 0xaa24f560, 0xdd7e9c2b, 0xd4709726, 0xcf628a31, 0xc66c813c, 0xf946b01f, 0xf048bb12, 0xeb5aa605, 0xe254ad08, 0x3e350f28, 0x373b0425, 0x2c291932, 0x2527123f, 0x1a0d231c, 0x13032811, 0x8113506, 0x11f3e0b, 0x76455740, 0x7f4b5c4d, 0x6459415a, 0x6d574a57, 0x527d7b74, 0x5b737079, 0x40616d6e, 0x496f6663, 0xaed5bff8, 0xa7dbb4f5, 0xbcc9a9e2, 0xb5c7a2ef, 0x8aed93cc, 0x83e398c1, 0x98f185d6, 0x91ff8edb, 0xe6a5e790, 0xefabec9d, 0xf4b9f18a, 0xfdb7fa87, 0xc29dcba4, 0xcb93c0a9, 0xd081ddbe, 0xd98fd6b3, 0x734382fe, 0x7a4d89f3, 0x615f94e4, 0x68519fe9, 0x577baeca, 0x5e75a5c7, 0x4567b8d0, 0x4c69b3dd, 0x3b33da96, 0x323dd19b, 0x292fcc8c, 0x2021c781, 0x1f0bf6a2, 0x1605fdaf, 0xd17e0b8, 0x419ebb5, 0xe3a3322e, 0xeaad3923, 0xf1bf2434, 0xf8b12f39, 0xc79b1e1a, 0xce951517, 0xd5870800, 0xdc89030d, 0xabd36a46, 0xa2dd614b, 0xb9cf7c5c, 0xb0c17751, 0x8feb4672, 0x86e54d7f, 0x9df75068, 0x94f95b65, 0x4898f945, 0x4196f248, 0x5a84ef5f, 0x538ae452, 0x6ca0d571, 0x65aede7c, 0x7ebcc36b, 0x77b2c866, 0xe8a12d, 0x9e6aa20, 0x12f4b737, 0x1bfabc3a, 0x24d08d19, 0x2dde8614, 0x36cc9b03, 0x3fc2900e, 0xd8784995, 0xd1764298, 0xca645f8f, 0xc36a5482, 0xfc4065a1, 0xf54e6eac, 0xee5c73bb, 0xe75278b6, 0x900811fd, 0x99061af0, 0x821407e7, 0x8b1a0cea, 0xb4303dc9, 0xbd3e36c4, 0xa62c2bd3, 0xaf2220de, 0xe9af8349, 0xe0a18844, 0xfbb39553, 0xf2bd9e5e, 0xcd97af7d, 0xc499a470, 0xdf8bb967, 0xd685b26a, 0xa1dfdb21, 0xa8d1d02c, 0xb3c3cd3b, 0xbacdc636, 0x85e7f715, 0x8ce9fc18, 0x97fbe10f, 0x9ef5ea02, 0x794f3399, 0x70413894, 0x6b532583, 0x625d2e8e, 0x5d771fad, 0x547914a0, 0x4f6b09b7, 0x466502ba, 0x313f6bf1, 0x383160fc, 0x23237deb, 0x2a2d76e6, 0x150747c5, 0x1c094cc8, 0x71b51df, 0xe155ad2, 0xd274f8f2, 0xdb7af3ff, 0xc068eee8, 0xc966e5e5, 0xf64cd4c6, 0xff42dfcb, 0xe450c2dc, 0xed5ec9d1, 0x9a04a09a, 0x930aab97, 0x8818b680, 0x8116bd8d, 0xbe3c8cae, 0xb73287a3, 0xac209ab4, 0xa52e91b9, 0x42944822, 0x4b9a432f, 0x50885e38, 0x59865535, 0x66ac6416, 0x6fa26f1b, 0x74b0720c, 0x7dbe7901, 0xae4104a, 0x3ea1b47, 0x18f80650, 0x11f60d5d, 0x2edc3c7e, 0x27d23773, 0x3cc02a64, 0x35ce2169, 0x9f027524, 0x960c7e29, 0x8d1e633e, 0x84106833, 0xbb3a5910, 0xb234521d, 0xa9264f0a, 0xa0284407, 0xd7722d4c, 0xde7c2641, 0xc56e3b56, 0xcc60305b, 0xf34a0178, 0xfa440a75, 0xe1561762, 0xe8581c6f, 0xfe2c5f4, 0x6eccef9, 0x1dfed3ee, 0x14f0d8e3, 0x2bdae9c0, 0x22d4e2cd, 0x39c6ffda, 0x30c8f4d7, 0x47929d9c, 0x4e9c9691, 0x558e8b86, 0x5c80808b, 0x63aab1a8, 0x6aa4baa5, 0x71b6a7b2, 0x78b8acbf, 0xa4d90e9f, 0xadd70592, 0xb6c51885, 0xbfcb1388, 0x80e122ab, 0x89ef29a6, 0x92fd34b1, 0x9bf33fbc, 0xeca956f7, 0xe5a75dfa, 0xfeb540ed, 0xf7bb4be0, 0xc8917ac3, 0xc19f71ce, 0xda8d6cd9, 0xd38367d4, 0x3439be4f, 0x3d37b542, 0x2625a855, 0x2f2ba358, 0x1001927b, 0x190f9976, 0x21d8461, 0xb138f6c, 0x7c49e627, 0x7547ed2a, 0x6e55f03d, 0x675bfb30, 0x5871ca13, 0x517fc11e, 0x4a6ddc09, 0x4363d704}; + +unsigned long table_s7[] = {0xb33a6e73, 0xbe336078, 0xa9287265, 0xa4217c6e, 0x871e565f, 0x8a175854, 0x9d0c4a49, 0x90054442, 0xdb721e2b, 0xd67b1020, 0xc160023d, 0xcc690c36, 0xef562607, 0xe25f280c, 0xf5443a11, 0xf84d341a, 0x63aa8ec3, 0x6ea380c8, 0x79b892d5, 0x74b19cde, 0x578eb6ef, 0x5a87b8e4, 0x4d9caaf9, 0x4095a4f2, 0xbe2fe9b, 0x6ebf090, 0x11f0e28d, 0x1cf9ec86, 0x3fc6c6b7, 0x32cfc8bc, 0x25d4daa1, 0x28ddd4aa, 0x801b508, 0x508bb03, 0x1213a91e, 0x1f1aa715, 0x3c258d24, 0x312c832f, 0x26379132, 0x2b3e9f39, 0x6049c550, 0x6d40cb5b, 0x7a5bd946, 0x7752d74d, 0x546dfd7c, 0x5964f377, 0x4e7fe16a, 0x4376ef61, 0xd89155b8, 0xd5985bb3, 0xc28349ae, 0xcf8a47a5, 0xecb56d94, 0xe1bc639f, 0xf6a77182, 0xfbae7f89, 0xb0d925e0, 0xbdd02beb, 0xaacb39f6, 0xa7c237fd, 0x84fd1dcc, 0x89f413c7, 0x9eef01da, 0x93e60fd1, 0xde4cc385, 0xd345cd8e, 0xc45edf93, 0xc957d198, 0xea68fba9, 0xe761f5a2, 0xf07ae7bf, 0xfd73e9b4, 0xb604b3dd, 0xbb0dbdd6, 0xac16afcb, 0xa11fa1c0, 0x82208bf1, 0x8f2985fa, 0x983297e7, 0x953b99ec, 0xedc2335, 0x3d52d3e, 0x14ce3f23, 0x19c73128, 0x3af81b19, 0x37f11512, 0x20ea070f, 0x2de30904, 0x6694536d, 0x6b9d5d66, 0x7c864f7b, 0x718f4170, 0x52b06b41, 0x5fb9654a, 0x48a27757, 0x45ab795c, 0x657718fe, 0x687e16f5, 0x7f6504e8, 0x726c0ae3, 0x515320d2, 0x5c5a2ed9, 0x4b413cc4, 0x464832cf, 0xd3f68a6, 0x3666ad, 0x172d74b0, 0x1a247abb, 0x391b508a, 0x34125e81, 0x23094c9c, 0x2e004297, 0xb5e7f84e, 0xb8eef645, 0xaff5e458, 0xa2fcea53, 0x81c3c062, 0x8ccace69, 0x9bd1dc74, 0x96d8d27f, 0xddaf8816, 0xd0a6861d, 0xc7bd9400, 0xcab49a0b, 0xe98bb03a, 0xe482be31, 0xf399ac2c, 0xfe90a227, 0x69d62f84, 0x64df218f, 0x73c43392, 0x7ecd3d99, 0x5df217a8, 0x50fb19a3, 0x47e00bbe, 0x4ae905b5, 0x19e5fdc, 0xc9751d7, 0x1b8c43ca, 0x16854dc1, 0x35ba67f0, 0x38b369fb, 0x2fa87be6, 0x22a175ed, 0xb946cf34, 0xb44fc13f, 0xa354d322, 0xae5ddd29, 0x8d62f718, 0x806bf913, 0x9770eb0e, 0x9a79e505, 0xd10ebf6c, 0xdc07b167, 0xcb1ca37a, 0xc615ad71, 0xe52a8740, 0xe823894b, 0xff389b56, 0xf231955d, 0xd2edf4ff, 0xdfe4faf4, 0xc8ffe8e9, 0xc5f6e6e2, 0xe6c9ccd3, 0xebc0c2d8, 0xfcdbd0c5, 0xf1d2dece, 0xbaa584a7, 0xb7ac8aac, 0xa0b798b1, 0xadbe96ba, 0x8e81bc8b, 0x8388b280, 0x9493a09d, 0x999aae96, 0x27d144f, 0xf741a44, 0x186f0859, 0x15660652, 0x36592c63, 0x3b502268, 0x2c4b3075, 0x21423e7e, 0x6a356417, 0x673c6a1c, 0x70277801, 0x7d2e760a, 0x5e115c3b, 0x53185230, 0x4403402d, 0x490a4e26, 0x4a08272, 0x9a98c79, 0x1eb29e64, 0x13bb906f, 0x3084ba5e, 0x3d8db455, 0x2a96a648, 0x279fa843, 0x6ce8f22a, 0x61e1fc21, 0x76faee3c, 0x7bf3e037, 0x58ccca06, 0x55c5c40d, 0x42ded610, 0x4fd7d81b, 0xd43062c2, 0xd9396cc9, 0xce227ed4, 0xc32b70df, 0xe0145aee, 0xed1d54e5, 0xfa0646f8, 0xf70f48f3, 0xbc78129a, 0xb1711c91, 0xa66a0e8c, 0xab630087, 0x885c2ab6, 0x855524bd, 0x924e36a0, 0x9f4738ab, 0xbf9b5909, 0xb2925702, 0xa589451f, 0xa8804b14, 0x8bbf6125, 0x86b66f2e, 0x91ad7d33, 0x9ca47338, 0xd7d32951, 0xdada275a, 0xcdc13547, 0xc0c83b4c, 0xe3f7117d, 0xeefe1f76, 0xf9e50d6b, 0xf4ec0360, 0x6f0bb9b9, 0x6202b7b2, 0x7519a5af, 0x7810aba4, 0x5b2f8195, 0x56268f9e, 0x413d9d83, 0x4c349388, 0x743c9e1, 0xa4ac7ea, 0x1d51d5f7, 0x1058dbfc, 0x3367f1cd, 0x3e6effc6, 0x2975eddb, 0x247ce3d0}; + +unsigned long table_s8[] = {0xb4469bf0, 0xbf4b92fe, 0xa25c89ec, 0xa95180e2, 0x9872bfc8, 0x937fb6c6, 0x8e68add4, 0x8565a4da, 0xec2ed380, 0xe723da8e, 0xfa34c19c, 0xf139c892, 0xc01af7b8, 0xcb17feb6, 0xd600e5a4, 0xdd0decaa, 0x4960b10, 0xf9b021e, 0x128c190c, 0x19811002, 0x28a22f28, 0x23af2626, 0x3eb83d34, 0x35b5343a, 0x5cfe4360, 0x57f34a6e, 0x4ae4517c, 0x41e95872, 0x70ca6758, 0x7bc76e56, 0x66d07544, 0x6ddd7c4a, 0xcffda02b, 0xc4f0a925, 0xd9e7b237, 0xd2eabb39, 0xe3c98413, 0xe8c48d1d, 0xf5d3960f, 0xfede9f01, 0x9795e85b, 0x9c98e155, 0x818ffa47, 0x8a82f349, 0xbba1cc63, 0xb0acc56d, 0xadbbde7f, 0xa6b6d771, 0x7f2d30cb, 0x742039c5, 0x693722d7, 0x623a2bd9, 0x531914f3, 0x58141dfd, 0x450306ef, 0x4e0e0fe1, 0x274578bb, 0x2c4871b5, 0x315f6aa7, 0x3a5263a9, 0xb715c83, 0x7c558d, 0x1d6b4e9f, 0x16664791, 0x422bed5d, 0x4926e453, 0x5431ff41, 0x5f3cf64f, 0x6e1fc965, 0x6512c06b, 0x7805db79, 0x7308d277, 0x1a43a52d, 0x114eac23, 0xc59b731, 0x754be3f, 0x36778115, 0x3d7a881b, 0x206d9309, 0x2b609a07, 0xf2fb7dbd, 0xf9f674b3, 0xe4e16fa1, 0xefec66af, 0xdecf5985, 0xd5c2508b, 0xc8d54b99, 0xc3d84297, 0xaa9335cd, 0xa19e3cc3, 0xbc8927d1, 0xb7842edf, 0x86a711f5, 0x8daa18fb, 0x90bd03e9, 0x9bb00ae7, 0x3990d686, 0x329ddf88, 0x2f8ac49a, 0x2487cd94, 0x15a4f2be, 0x1ea9fbb0, 0x3bee0a2, 0x8b3e9ac, 0x61f89ef6, 0x6af597f8, 0x77e28cea, 0x7cef85e4, 0x4dccbace, 0x46c1b3c0, 0x5bd6a8d2, 0x50dba1dc, 0x89404666, 0x824d4f68, 0x9f5a547a, 0x94575d74, 0xa574625e, 0xae796b50, 0xb36e7042, 0xb863794c, 0xd1280e16, 0xda250718, 0xc7321c0a, 0xcc3f1504, 0xfd1c2a2e, 0xf6112320, 0xeb063832, 0xe00b313c, 0x439c77b1, 0x48917ebf, 0x558665ad, 0x5e8b6ca3, 0x6fa85389, 0x64a55a87, 0x79b24195, 0x72bf489b, 0x1bf43fc1, 0x10f936cf, 0xdee2ddd, 0x6e324d3, 0x37c01bf9, 0x3ccd12f7, 0x21da09e5, 0x2ad700eb, 0xf34ce751, 0xf841ee5f, 0xe556f54d, 0xee5bfc43, 0xdf78c369, 0xd475ca67, 0xc962d175, 0xc26fd87b, 0xab24af21, 0xa029a62f, 0xbd3ebd3d, 0xb633b433, 0x87108b19, 0x8c1d8217, 0x910a9905, 0x9a07900b, 0x38274c6a, 0x332a4564, 0x2e3d5e76, 0x25305778, 0x14136852, 0x1f1e615c, 0x2097a4e, 0x9047340, 0x604f041a, 0x6b420d14, 0x76551606, 0x7d581f08, 0x4c7b2022, 0x4776292c, 0x5a61323e, 0x516c3b30, 0x88f7dc8a, 0x83fad584, 0x9eedce96, 0x95e0c798, 0xa4c3f8b2, 0xafcef1bc, 0xb2d9eaae, 0xb9d4e3a0, 0xd09f94fa, 0xdb929df4, 0xc68586e6, 0xcd888fe8, 0xfcabb0c2, 0xf7a6b9cc, 0xeab1a2de, 0xe1bcabd0, 0xb5f1011c, 0xbefc0812, 0xa3eb1300, 0xa8e61a0e, 0x99c52524, 0x92c82c2a, 0x8fdf3738, 0x84d23e36, 0xed99496c, 0xe6944062, 0xfb835b70, 0xf08e527e, 0xc1ad6d54, 0xcaa0645a, 0xd7b77f48, 0xdcba7646, 0x52191fc, 0xe2c98f2, 0x133b83e0, 0x18368aee, 0x2915b5c4, 0x2218bcca, 0x3f0fa7d8, 0x3402aed6, 0x5d49d98c, 0x5644d082, 0x4b53cb90, 0x405ec29e, 0x717dfdb4, 0x7a70f4ba, 0x6767efa8, 0x6c6ae6a6, 0xce4a3ac7, 0xc54733c9, 0xd85028db, 0xd35d21d5, 0xe27e1eff, 0xe97317f1, 0xf4640ce3, 0xff6905ed, 0x962272b7, 0x9d2f7bb9, 0x803860ab, 0x8b3569a5, 0xba16568f, 0xb11b5f81, 0xac0c4493, 0xa7014d9d, 0x7e9aaa27, 0x7597a329, 0x6880b83b, 0x638db135, 0x52ae8e1f, 0x59a38711, 0x44b49c03, 0x4fb9950d, 0x26f2e257, 0x2dffeb59, 0x30e8f04b, 0x3be5f945, 0xac6c66f, 0x1cbcf61, 0x1cdcd473, 0x17d1dd7d}; + +unsigned char table_s1[] = {0x66,0xac,0x60,0xcb,0x1d,0x74,0x99,0xfc,0x54,0x21,0x15,0xef,0x46,0xcc,0x63,0x55,0x5,0x3,0x17,0x80,0x25,0x84,0xf1,0x76,0x6f,0xb3,0xfe,0x14,0x1c,0x9e,0x4f,0xa2,0x1f,0x73,0xd0,0xe7,0xa,0xb6,0x4d,0x30,0xc,0x2,0x7c,0xca,0x9f,0xa0,0xe5,0xb4,0x5f,0xb0,0xbc,0x9d,0xf3,0xf9,0x95,0x6e,0xdd,0xd2,0x77,0xba,0x1e,0xec,0xf,0xc0,0xb1,0x98,0xe4,0x39,0x7f,0x4e,0x28,0x64,0xbd,0x24,0x20,0x8a,0x2c,0x33,0x38,0x34,0xd3,0xeb,0x3d,0x8f,0xe2,0x9b,0xed,0xe0,0xb5,0x16,0x8,0xbf,0x85,0xcd,0x86,0x32,0x3e,0x97,0x7e,0x5a,0x7b,0xea,0xaa,0xbe,0x79,0x70,0xb8,0x83,0xf8,0xb2,0xdc,0x69,0xa4,0x68,0xfd,0x3a,0x48,0x5d,0xcf,0xad,0x57,0xd9,0x4a,0xd5,0x4b,0x88,0x6c,0x8c,0x4,0xf2,0xc4,0xc5,0xa7,0x92,0x3b,0x50,0x53,0xe9,0xfb,0x89,0xf5,0x37,0x6a,0x61,0xc9,0x8e,0x52,0xd1,0x2e,0x7a,0x18,0xf6,0x7,0x4c,0xb9,0x41,0x3f,0x71,0xfa,0x29,0x81,0x1a,0x67,0x90,0xd4,0x51,0xc8,0xa6,0x26,0x96,0xc1,0xdb,0xae,0xb7,0xd7,0x5e,0xff,0x1b,0xf4,0x59,0xe,0xd6,0x62,0x40,0xf0,0xa9,0xd,0x27,0xc3,0xee,0xc6,0x42,0x2b,0x12,0x56,0x3c,0x8b,0xe8,0x31,0x72,0x10,0xd8,0xb,0x58,0x82,0x43,0x5c,0xa3,0x91,0x11,0x44,0x94,0x9,0xa1,0xf7,0x5b,0x6d,0x65,0xdf,0xc7,0x2f,0xce,0x0,0x93,0xde,0xda,0xab,0x36,0x8d,0x9c,0xe3,0x2d,0x6,0x49,0x6b,0x13,0xaf,0x7d,0x75,0x45,0x2a,0x35,0xe1,0x47,0x23,0x19,0xbb,0xa5,0xc2,0x9a,0x1,0xe6,0xa8,0x87,0x78,0x22,0xc7,0xa9,0x30,0xb5,0xf1,0x6,0x7b,0xe0,0x3f,0xb6,0xd6,0xcf,0xba,0xa0,0xf7,0x47,0x21,0x3,0xb7,0x6f,0x38,0x95,0x7a,0x9e,0x23,0xa7,0x8f,0xa2,0x46,0x6c,0xc8,0x91,0x31,0x5a,0xf3,0xc6,0xa4,0xa5,0x93,0x65,0x0,0xb,0x56,0x94,0xe8,0x9a,0x88,0x32,0x97,0x79,0x1b,0x4f,0xb0,0x33,0xef,0xa8,0x48,0x9b,0x10,0x5e,0x20,0xd8,0x2d,0x66,0x4c,0x82,0xfd,0xec,0x57,0xca,0xbb,0xbf,0x24,0x14,0x1c,0xce,0x72,0xa,0x28,0x67,0xc4,0xda,0x78,0x42,0x26,0x80,0x54,0x4b,0x43,0x19,0xe6,0xc9,0x87,0x60,0xfb,0xa3,0x13,0x50,0x89,0xea,0x5d,0x37,0x73,0x4a,0xc2,0x3d,0x22,0xe3,0x39,0x6a,0xb9,0x71,0x3a,0x96,0xc0,0x68,0xf5,0x25,0x70,0xf0,0xf2,0x61,0xaf,0x4e,0xa6,0xbe,0x4,0xc,0x51,0x2c,0xd7,0x6b,0x86,0xb1,0x12,0x7e,0xd5,0x84,0xc1,0xfe,0xab,0x1d,0x63,0x6d,0xf,0xf4,0x98,0x92,0xfc,0xdd,0xd1,0x3e,0xa1,0x6e,0x8d,0x7f,0xdb,0x16,0xb3,0xbc,0x9d,0xf8,0x15,0x7c,0xaa,0x1,0xcd,0x7,0x34,0x2,0xad,0x27,0x8e,0x74,0x40,0x35,0x17,0x90,0xe5,0x44,0xe1,0x76,0x62,0x64,0xc3,0x2e,0xff,0x7d,0x75,0x9f,0xd2,0xe,0xdf,0xcb,0x8b,0x1a,0x3b,0x1f,0xf6,0x5f,0x8,0xbd,0xd3,0x99,0xe2,0xd9,0x11,0x18,0xcc,0xae,0x3c,0x29,0x5b,0x9c,0x9,0xc5,0xed,0xd,0xe9,0x2a,0xb4,0x2b,0xb8,0x36,0x5,0x49,0x2f,0x1e,0x58,0x85,0xf9,0xd0,0x55,0x59,0x52,0x4d,0xeb,0x41,0x45,0xdc,0x81,0x8c,0xfa,0x83,0xee,0x5c,0x8a,0xb2,0x53,0xe7,0xac,0xe4,0xde,0x69,0x77,0xd4,0x17,0xa7,0xf0,0xea,0x9f,0x86,0xe6,0x6f,0xb0,0x2b,0x56,0xa1,0xe5,0x60,0xf9,0x97,0xc1,0x98,0x3c,0x16,0xf2,0xdf,0xf7,0x73,0xce,0x2a,0xc5,0x68,0x3f,0xe7,0x53,0x71,0x62,0xd8,0xca,0xb8,0xc4,0x6,0x5b,0x50,0x35,0xc3,0xf5,0xf4,0x96,0xa3,0xa,0x61,0x36,0x7d,0x88,0x70,0xe,0x40,0xcb,0x18,0xf8,0xbf,0x63,0xe0,0x1f,0x4b,0x29,0xc7,0x37,0x78,0x5a,0x22,0x9e,0x4c,0x44,0x74,0xef,0xeb,0x9a,0x7,0xbc,0xad,0xd2,0x1c,0xf3,0xab,0x30,0xd7,0x99,0xb6,0x49,0x13,0x1b,0x4,0xd0,0x76,0x12,0x28,0x8a,0x94,0x21,0xe9,0x3a,0x69,0xb3,0x72,0x6d,0x92,0x1a,0x23,0x67,0xd,0xba,0xd9,0x0,0x43,0x5c,0x54,0xee,0xf6,0x1e,0xff,0x31,0xa2,0xa0,0x20,0x75,0xa5,0x38,0x90,0xc6,0x6a,0x3d,0x33,0x4d,0xfb,0xae,0x91,0xd4,0x85,0x2e,0x42,0xe1,0xd6,0x3b,0x87,0x7c,0x1,0xec,0xe3,0x46,0x8b,0x2f,0xdd,0x3e,0xf1,0x6e,0x81,0x8d,0xac,0xc2,0xc8,0xa4,0x5f,0x65,0x10,0x24,0xde,0x77,0xfd,0x52,0x64,0x57,0x9d,0x51,0xfa,0x2c,0x45,0xa8,0xcd,0x5e,0x82,0xcf,0x25,0x2d,0xaf,0x7e,0x93,0x34,0x32,0x26,0xb1,0x14,0xb5,0xc0,0x47,0x48,0x41,0x89,0xb2,0xc9,0x83,0xed,0x58,0xf,0xa6,0x4f,0x6b,0x4a,0xdb,0x9b,0x8f,0x66,0xe8,0x7b,0xe4,0x7a,0xb9,0x5d,0xbd,0x95,0x59,0xcc,0xb,0x79,0x6c,0xfe,0x9c,0x8c,0x15,0x11,0xbb,0x1d,0x2,0x9,0x5,0x80,0xa9,0xd5,0x8,0x4e,0x7f,0x19,0x55,0x84,0x27,0x39,0x8e,0xb4,0xfc,0xb7,0x3,0xe2,0xda,0xc,0xbe,0xd3,0xaa,0xdc,0xd1,0x1c,0xf8,0x5e,0xf3,0xd1,0x9,0x47,0x65,0xae,0xf7,0x20,0xa,0xe9,0xc4,0x45,0xc1,0x1d,0x86,0x97,0x60,0x56,0xd3,0xa1,0xcf,0x91,0x21,0xdc,0xc6,0xb0,0xa9,0x59,0xd0,0x89,0xce,0xd6,0x55,0x7d,0x29,0xf1,0x1f,0x4b,0x0,0x46,0xbe,0x76,0x38,0x2e,0xfd,0xf5,0x3,0xc2,0xc3,0x95,0xa0,0x57,0x3c,0xee,0x54,0x8e,0xfc,0x30,0xf2,0x66,0x6d,0x32,0x2d,0x40,0xe6,0x1e,0x24,0xa2,0xbc,0x9d,0xc5,0xe1,0x6,0x80,0xaf,0x25,0x7f,0xdd,0xd9,0x31,0xac,0x9b,0x8a,0x2a,0xe4,0x4e,0x1,0x14,0x6c,0x7a,0xa8,0x42,0x72,0x16,0x96,0x93,0x43,0xa6,0xe,0x5c,0xf0,0x62,0x6a,0xc0,0xd8,0xc9,0x28,0x94,0x7,0x15,0x2c,0x3b,0x51,0xef,0x8c,0x75,0x36,0xdf,0x17,0x5f,0xc,0x44,0x85,0xa4,0x5b,0xb7,0x58,0x9a,0xbb,0xfe,0xf4,0x69,0x92,0xd5,0xda,0xbd,0x70,0xeb,0x19,0xc7,0x8,0x74,0x18,0xe0,0xd7,0xb1,0xd,0x37,0x4a,0x5,0xb,0xcd,0x7b,0xa7,0x98,0xb3,0xe2,0x4,0x2,0x87,0x10,0x83,0x22,0x71,0xf6,0xb4,0x68,0x13,0xf9,0x99,0x1b,0xa5,0x48,0xab,0x61,0xcc,0x67,0x73,0x1a,0xfb,0x9e,0x26,0x53,0xe8,0x12,0xcb,0x41,0x52,0x64,0x6f,0xa3,0x3d,0xfa,0x5a,0x4f,0xaa,0xc8,0xde,0x50,0xd2,0x4d,0x8f,0x4c,0x8b,0x6b,0x90,0x39,0x5d,0x79,0xed,0x7c,0xb9,0xad,0x77,0x7e,0x84,0xbf,0xb5,0xff,0x6e,0xdb,0xec,0xd4,0x88,0x3a,0x9c,0xe5,0xe7,0xea,0x11,0xb2,0xb8,0xf,0xca,0x82,0x35,0x81,0x9f,0xb6,0x3e,0xe3,0x49,0x78,0x63,0x2f,0x23,0xba,0x8d,0x27,0x34,0x2b,0x33,0x3f,0x3c,0x23,0xf7,0x51,0x35,0xf,0xad,0xb3,0xd4,0x8c,0x17,0xf0,0xbe,0x91,0x6e,0x34,0xc8,0xcc,0xbd,0x20,0x9b,0x8a,0xf5,0x3b,0x10,0x5f,0x7d,0x5,0xb9,0x6b,0x63,0x53,0x87,0x7,0x52,0x82,0x1f,0xb7,0xe1,0x4d,0x7b,0x73,0xc9,0xd1,0x39,0xd8,0x16,0x85,0x3d,0x4,0x40,0x2a,0x9d,0xfe,0x27,0x64,0x6,0xce,0x1d,0x4e,0x94,0x55,0x4a,0xb5,0xe9,0xd,0xe2,0x4f,0x18,0xc0,0x74,0x56,0xe6,0xbf,0x1b,0x31,0xd5,0xf8,0xd0,0x54,0x97,0xc,0x71,0x86,0xc2,0x47,0xde,0xb0,0x30,0x80,0xd7,0xcd,0xb8,0xa1,0xc1,0x48,0xdf,0x98,0x44,0xc7,0x38,0x6c,0xe,0xe0,0x11,0x5a,0xaf,0x57,0x29,0x67,0xec,0x3f,0x12,0xe4,0xd2,0xd3,0xb1,0x84,0x2d,0x46,0x45,0xff,0xed,0x9f,0xe3,0x21,0x7c,0x77,0xb2,0x7e,0xeb,0x2c,0x5e,0x4b,0xd9,0xbb,0x41,0xcf,0x5c,0xc3,0x5d,0x9e,0x7a,0x9a,0x28,0x81,0x68,0x4c,0x6d,0xfc,0xbc,0xa8,0x6f,0x66,0xae,0x95,0xee,0xa4,0xca,0x7f,0xc5,0xfd,0x2b,0x99,0xf4,0x8d,0xfb,0xf6,0xa3,0x0,0x1e,0xa9,0x93,0xdb,0x90,0x24,0xa7,0x8e,0xf2,0x2f,0x69,0x58,0x3e,0x72,0xab,0x32,0x36,0x9c,0x3a,0x25,0x2e,0x22,0x49,0xa6,0xaa,0x8b,0xe5,0xef,0x83,0x78,0xcb,0xc4,0x61,0xac,0x8,0xfa,0x19,0xd6,0x9,0x65,0xc6,0xf1,0x1c,0xa0,0x5b,0x26,0x1a,0x14,0x6a,0xdc,0x89,0xb6,0xf3,0xa2,0x13,0x15,0x1,0x96,0x33,0x92,0xe7,0x60,0x79,0xa5,0xe8,0x2,0xa,0x88,0x59,0xb4,0x70,0xba,0x76,0xdd,0xb,0x62,0x8f,0xea,0x42,0x37,0x3,0xf9,0x50,0xda,0x75,0x43,0x37,0xac,0xbd,0x4a,0x7c,0xf9,0x8b,0xe5,0xbb,0xb,0xf6,0xec,0x9a,0x83,0x73,0xfa,0x36,0xd2,0x74,0xd9,0xfb,0x23,0x6d,0x4f,0x84,0xdd,0xa,0x20,0xc3,0xee,0x6f,0xeb,0xdf,0x29,0xe8,0xe9,0xbf,0x8a,0x7d,0x16,0xc4,0x7e,0xa4,0xd6,0x1a,0xd8,0x4c,0x47,0xa3,0xe4,0xfc,0x7f,0x57,0x3,0xdb,0x35,0x61,0x2a,0x6c,0x94,0x5c,0x12,0x4,0xd7,0xf7,0xf3,0x1b,0x86,0xb1,0xa0,0x0,0xce,0x64,0x2b,0x3e,0x46,0x50,0x82,0x68,0x58,0x18,0x7,0x6a,0xcc,0x34,0xe,0x88,0x96,0xb7,0xef,0xcb,0x2c,0xaa,0x85,0xf,0x55,0x3f,0x6,0x11,0x7b,0xc5,0xa6,0x5f,0x1c,0xf5,0x3d,0x75,0x26,0x6e,0xaf,0x8e,0x71,0x3c,0xbc,0xb9,0x69,0x8c,0x24,0x76,0xda,0x48,0x40,0xea,0xf2,0xe3,0x2,0xbe,0x2d,0x5e,0x32,0xca,0xfd,0x9b,0x27,0x1d,0x60,0x2f,0x21,0xe7,0x51,0x8d,0xb2,0x99,0xc8,0x9d,0x72,0xb0,0x91,0xd4,0xde,0x43,0xb8,0xff,0xf0,0x97,0x5a,0xc1,0x33,0xed,0x22,0x81,0x4b,0xe6,0x4d,0x59,0x30,0xd1,0xb4,0xc,0x79,0xc2,0x38,0xe1,0x6b,0x78,0x4e,0x2e,0x28,0xad,0x3a,0xa9,0x8,0x5b,0xdc,0x9e,0x42,0x39,0xd3,0xb3,0x31,0x8f,0x62,0xba,0x13,0x77,0x53,0xc7,0x56,0x93,0x87,0x5d,0x54,0xae,0x95,0x9f,0xd5,0x44,0xf1,0x45,0x89,0x17,0xd0,0x70,0x65,0x80,0xe2,0xf4,0x7a,0xf8,0x67,0xa5,0x66,0xa1,0x41,0xb5,0x9c,0x14,0xc9,0x63,0x52,0x49,0x5,0x9,0x90,0xa7,0xd,0x1e,0x1,0x19,0x15,0xc6,0xfe,0xa2,0x10,0xb6,0xcf,0xcd,0xc0,0x3b,0x98,0x92,0x25,0xe0,0xa8,0x1f,0xab,0xb3,0x52,0xee,0x7d,0x18,0x10,0xba,0xa2,0xdc,0x74,0x26,0x8a,0x6c,0xec,0xe9,0x39,0x3e,0xff,0xde,0x21,0xa5,0x6d,0x25,0x76,0x95,0xf6,0xf,0x4c,0x6f,0x56,0x41,0x2b,0xfa,0xd5,0x5f,0x5,0xe7,0xbf,0x9b,0x7c,0x64,0x5e,0xd8,0xc6,0x48,0x57,0x3a,0x9c,0x0,0xd2,0x38,0x8,0x34,0x7b,0x6e,0x16,0xe1,0xf0,0x50,0x9e,0xa7,0xa3,0x4b,0xd6,0xc,0x42,0x54,0x87,0x31,0x7a,0x3c,0xc4,0x7,0x53,0x8b,0x65,0xf3,0xb4,0xac,0x2f,0x4a,0x88,0x1c,0x17,0x94,0x2e,0xf4,0x86,0xef,0xda,0x2d,0x46,0x8f,0x79,0xb8,0xb9,0x93,0xbe,0x3f,0xbb,0xd4,0x8d,0x5a,0x70,0xab,0x73,0x3d,0x1f,0x66,0x82,0x24,0x89,0xca,0xd3,0x23,0xaa,0xeb,0x5b,0xa6,0xbc,0x2c,0xa9,0xdb,0xb5,0x67,0xfc,0xed,0x1a,0xb0,0xf8,0x4f,0xfb,0x6b,0xc8,0xc2,0x75,0xe6,0x9f,0x9d,0x90,0x96,0xae,0xf2,0x40,0x4e,0x51,0x49,0x45,0x59,0xc0,0xf7,0x5d,0x33,0x2,0x19,0x55,0xe5,0xcc,0x44,0x99,0xf5,0x36,0xf1,0x11,0xa4,0x2a,0xa8,0x37,0x20,0x35,0xd0,0xb2,0x15,0xd9,0x47,0x80,0xcf,0x85,0x14,0xa1,0xd,0x4,0xfe,0xc5,0x97,0x6,0xc3,0xd7,0xea,0x43,0x27,0x3,0xe3,0x61,0xdf,0x32,0xce,0x12,0x69,0x83,0xf9,0x58,0xb,0x8c,0x7e,0x78,0xfd,0x6a,0xb1,0x3b,0x28,0x1e,0x5c,0x29,0x92,0x68,0x9,0x60,0x81,0xe4,0xd1,0x1b,0xb6,0x1d,0x91,0x63,0xbd,0x72,0xaf,0xa0,0xc7,0xa,0x84,0x8e,0x13,0xe8,0xcd,0x22,0xe0,0xc1,0xdd,0xe2,0xc9,0x98,0x7f,0x71,0xb7,0x1,0xcb,0x77,0x4d,0x30,0xe,0x62,0x9a,0xad,0x9a,0x2a,0xd7,0xcd,0xbb,0xa2,0x52,0xdb,0x16,0x8d,0x9c,0x6b,0x5d,0xd8,0xaa,0xc4,0xa5,0xfc,0x2b,0x1,0xe2,0xcf,0x4e,0xca,0x17,0xf3,0x55,0xf8,0xda,0x2,0x4c,0x6e,0xe5,0x5f,0x85,0xf7,0x3b,0xf9,0x6d,0x66,0xfe,0x8,0xc9,0xc8,0x9e,0xab,0x5c,0x37,0x40,0xb,0x4d,0xb5,0x7d,0x33,0x25,0xf6,0x82,0xc5,0xdd,0x5e,0x76,0x22,0xfa,0x14,0x45,0xa,0x1f,0x67,0x71,0xa3,0x49,0x79,0xd6,0xd2,0x3a,0xa7,0x90,0x81,0x21,0xef,0x96,0xce,0xea,0xd,0x8b,0xa4,0x2e,0x74,0x39,0x26,0x4b,0xed,0x15,0x2f,0xa9,0xb7,0xd4,0x1c,0x54,0x7,0x4f,0x8e,0xaf,0x50,0x1e,0x27,0x30,0x5a,0xe4,0x87,0x7e,0x3d,0x69,0x61,0xcb,0xd3,0xc2,0x23,0x9f,0xc,0x1d,0x9d,0x98,0x48,0xad,0x5,0x57,0xfb,0xe,0x0,0xc6,0x70,0xac,0x93,0xb8,0xe9,0x7f,0x13,0xeb,0xdc,0xba,0x6,0x3c,0x41,0xde,0xd1,0xb6,0x7b,0xe0,0x12,0xcc,0x3,0xbc,0x53,0x91,0xb0,0xf5,0xff,0x62,0x99,0x2d,0x58,0xe3,0x19,0xc0,0x4a,0x59,0x6f,0xa0,0x6a,0xc7,0x6c,0x78,0x11,0xf0,0x95,0xbf,0x63,0x18,0xf2,0x92,0x10,0xae,0x43,0xf,0x9,0x8c,0x1b,0x88,0x29,0x7a,0xfd,0x7c,0x75,0x8f,0xb4,0xbe,0xf4,0x65,0xd0,0x9b,0x32,0x56,0x72,0xe6,0x77,0xb2,0xa6,0xd5,0x5b,0xd9,0x46,0x84,0x47,0x80,0x60,0x64,0xa8,0x36,0xf1,0x51,0x44,0xa1,0xc3,0x28,0xb1,0x86,0x2c,0x3f,0x20,0x38,0x34,0x94,0xbd,0x35,0xe8,0x42,0x73,0x68,0x24,0x1a,0xb9,0xb3,0x4,0xc1,0x89,0x3e,0x8a,0xe7,0xdf,0x83,0x31,0x97,0xee,0xec,0xe1,0x44,0x2e,0x39,0x0,0x23,0x60,0x99,0xfa,0x19,0x4a,0x2,0xca,0x4e,0xb1,0x90,0x51,0x56,0x86,0x83,0x3,0xe5,0x49,0x1b,0xb3,0xcd,0xd5,0x7f,0x77,0x12,0x81,0x3d,0xdc,0xb9,0x24,0xcc,0xc8,0xf1,0x3f,0x9f,0x8e,0x79,0x1,0x14,0x5b,0x67,0x57,0xbd,0x6f,0xf3,0x55,0x38,0x27,0xa9,0xb7,0x31,0xb,0x13,0xf4,0xd0,0x88,0x6a,0x30,0xba,0x95,0xd6,0xd7,0x16,0xe0,0x29,0x42,0xb5,0x80,0xe9,0x9b,0x41,0xfb,0x78,0x73,0xe7,0x25,0x40,0xc3,0xdb,0x9c,0xa,0xe4,0x3c,0x68,0xab,0x53,0x15,0x5e,0xe8,0x3b,0x2d,0x63,0x75,0x82,0x93,0x8,0xda,0xb4,0xc6,0x43,0xd3,0xc9,0x34,0x84,0xc5,0x4c,0xbc,0xa5,0xe6,0x4b,0xed,0x9,0x70,0x52,0x1c,0xc4,0x1f,0x35,0xe2,0xbb,0xd4,0x50,0xd1,0xfc,0xf6,0x2b,0xa3,0x8a,0x3a,0x76,0x6d,0x5c,0x32,0x98,0xaf,0x36,0x2a,0x26,0x3e,0x21,0x2f,0x9d,0xc1,0xf9,0xff,0xf2,0xf0,0x89,0x1a,0xad,0xa7,0x4,0x94,0x20,0x97,0xdf,0x6c,0x48,0x2c,0x85,0xb8,0xac,0x69,0xf8,0xaa,0x91,0x6b,0x62,0xce,0x7b,0xea,0xa0,0xef,0x28,0xb6,0x7a,0xdd,0xbf,0x5a,0x4f,0x58,0xc7,0x45,0xcb,0x7e,0x9e,0x59,0x9a,0x72,0xd9,0x74,0xbe,0x8b,0xee,0xf,0x66,0x7,0xfd,0x46,0x33,0x71,0x47,0x54,0xde,0x5,0x92,0x17,0x11,0xe3,0x64,0x37,0x96,0xec,0x6,0x7d,0xa1,0x5d,0xb0,0xe,0x8c,0xc2,0xf5,0xd,0x61,0x5f,0x22,0x18,0xa4,0x6e,0xd8,0x1e,0x10,0xf7,0xa6,0x8d,0xb2,0xae,0x8f,0x4d,0xa2,0x87,0x7c,0xe1,0xeb,0x65,0xa8,0xcf,0xc0,0x1d,0xd2,0xc,0xfe,0x96,0x4a,0x31,0xdb,0xbb,0x39,0x87,0x6a,0x26,0x20,0xa5,0x32,0xa1,0x0,0x53,0xd4,0x4,0x71,0xca,0x30,0xe9,0x63,0x70,0x46,0x89,0x43,0xee,0x45,0x51,0x38,0xd9,0xbc,0xf7,0xf8,0x9f,0x52,0xc9,0x3b,0xe5,0x2a,0x95,0x7a,0xb8,0x99,0xdc,0xd6,0x4b,0xb0,0x27,0x29,0xef,0x59,0x85,0xba,0x91,0xc0,0x56,0x3a,0xc2,0xf5,0x93,0x2f,0x15,0x68,0x33,0x90,0x9a,0x2d,0xe8,0xa0,0x17,0xa3,0xce,0xf6,0xaa,0x18,0xbe,0xc7,0xc5,0xc8,0x1,0x98,0xaf,0x5,0x16,0x9,0x11,0x1d,0xbd,0x94,0x1c,0xc1,0x6b,0x5a,0x41,0xd,0xfc,0x72,0xf0,0x6f,0xad,0x6e,0xa9,0x49,0x4d,0x81,0x1f,0xd8,0x78,0x6d,0x88,0xea,0x55,0x5c,0xa6,0x9d,0x97,0xdd,0x4c,0xf9,0xb2,0x1b,0x7f,0x5b,0xcf,0x5e,0x9b,0x8f,0x69,0x22,0x64,0x9c,0x54,0x1a,0xc,0xdf,0xab,0xec,0xf4,0x77,0x5f,0xb,0xd3,0x3d,0xcc,0x76,0xac,0xde,0x12,0xd0,0x44,0x4f,0xd7,0x21,0xe0,0xe1,0xb7,0x82,0x75,0x1e,0x8c,0xd5,0x2,0x28,0xcb,0xe6,0x67,0xe3,0x3e,0xda,0x7c,0xd1,0xf3,0x2b,0x65,0x47,0xb3,0x3,0xfe,0xe4,0x92,0x8b,0x7b,0xf2,0x3f,0xa4,0xb5,0x42,0x74,0xf1,0x83,0xed,0x40,0x48,0xe2,0xfa,0xeb,0xa,0xb6,0x25,0x34,0xb4,0xb1,0x61,0x84,0x2c,0x7e,0xd2,0xfd,0x35,0x7d,0x2e,0x66,0xa7,0x86,0x79,0x37,0xe,0x19,0x73,0xcd,0xae,0x57,0x14,0xbf,0xe7,0xc3,0x24,0xa2,0x8d,0x7,0x5d,0x10,0xf,0x62,0xc4,0x3c,0x6,0x80,0x9e,0x6c,0x23,0x36,0x4e,0x58,0x8a,0x60,0x50,0xff,0xfb,0x13,0x8e,0xb9,0xa8,0x8,0xc6,0xa9,0x80,0xfc,0x21,0x67,0x56,0x30,0x7c,0xa5,0x3c,0x38,0x92,0x34,0x2b,0x20,0x2c,0xcb,0xf3,0x25,0x97,0xfa,0x83,0xf5,0xf8,0xad,0xe,0x10,0xa7,0x9d,0xd5,0x9e,0x2a,0x26,0x8f,0x66,0x42,0x63,0xf2,0xb2,0xa6,0x61,0x68,0xa0,0x9b,0xe0,0xaa,0xc4,0x71,0xbc,0x70,0xe5,0x22,0x50,0x45,0xd7,0xb5,0x4f,0xc1,0x52,0xcd,0x53,0x90,0x74,0x94,0x7e,0xb4,0x78,0xd3,0x5,0x6c,0x81,0xe4,0x4c,0x39,0xd,0xf7,0x5e,0xd4,0x7b,0x4d,0x1d,0x1b,0xf,0x98,0x3d,0x9c,0xe9,0x6e,0x77,0xab,0xe6,0xc,0x4,0x86,0x57,0xba,0x7,0x6b,0xc8,0xff,0x12,0xae,0x55,0x28,0x14,0x1a,0x64,0xd2,0x87,0xb8,0xfd,0xac,0x47,0xa8,0xa4,0x85,0xeb,0xe1,0x8d,0x76,0xc5,0xca,0x6f,0xa2,0x6,0xf4,0x17,0xd8,0x33,0xa,0x4e,0x24,0x93,0xf0,0x29,0x6a,0x8,0xc0,0x13,0x40,0x9a,0x5b,0x44,0xbb,0x89,0x9,0x5c,0x8c,0x11,0xb9,0xef,0x43,0x75,0x7d,0xc7,0xdf,0x37,0xd6,0x18,0x8b,0xc6,0xc2,0xb3,0x2e,0x95,0x84,0xfb,0x35,0x1e,0x51,0x73,0xb,0xb7,0x65,0x6d,0x5d,0x32,0x2d,0xf9,0x5f,0x3b,0x1,0xa3,0xbd,0xda,0x82,0x19,0xfe,0xb0,0x9f,0x60,0x3a,0x1c,0xea,0xdc,0xdd,0xbf,0x8a,0x23,0x48,0x4b,0xf1,0xe3,0x91,0xed,0x2f,0x72,0x79,0xd1,0x96,0x4a,0xc9,0x36,0x62,0x0,0xee,0x1f,0x54,0xa1,0x59,0x27,0x69,0xe2,0x31,0x99,0x2,0x7f,0x88,0xcc,0x49,0xd0,0xbe,0x3e,0x8e,0xd9,0xc3,0xb6,0xaf,0xcf,0x46,0xe7,0x3,0xec,0x41,0x16,0xce,0x7a,0x58,0xe8,0xb1,0x15,0x3f,0xdb,0xf6,0xde,0x5a,0x7d,0x31,0x2a,0x1b,0xb1,0x6c,0xe4,0xcd,0x6d,0x61,0x79,0x66,0x75,0xdf,0xe8,0x71,0xb8,0xb5,0xb7,0xce,0x68,0xda,0x86,0xbe,0xd3,0x67,0xd0,0x98,0x5d,0xea,0xe0,0x43,0xff,0xeb,0x2e,0xbf,0x2b,0xf,0x6b,0xc2,0x89,0x3c,0xad,0xe7,0xed,0xd6,0x2c,0x25,0x9a,0xf8,0x1d,0x8,0xa8,0x6f,0xf1,0x3d,0x39,0xd9,0x1e,0xdd,0x1f,0x80,0x2,0x8c,0xcc,0xa9,0x48,0x21,0x35,0x9e,0x33,0xf9,0x36,0x0,0x13,0x99,0x40,0xba,0x1,0x74,0xa4,0x23,0x70,0xd1,0x42,0xd5,0x50,0x56,0x1a,0xf7,0x49,0xcb,0xab,0x41,0x3a,0xe6,0x18,0x65,0x5f,0xe3,0x85,0xb2,0x4a,0x26,0xb0,0xe1,0xca,0xf5,0x29,0x9f,0x59,0x57,0xc0,0x3b,0xa6,0xac,0xe9,0xc8,0xa,0xe5,0x5a,0x95,0x4b,0xb9,0x22,0xef,0x88,0x87,0x64,0x27,0xde,0xbd,0x3,0x69,0x7e,0x47,0x9,0xf6,0xd7,0x16,0x5e,0xd,0x45,0x8d,0xa2,0xe,0x5c,0xf4,0x11,0xc1,0xc4,0x44,0x55,0xc6,0x7a,0x9b,0x8a,0x92,0x38,0x30,0xb6,0x78,0xd8,0xc9,0xfe,0x63,0x8b,0x8f,0x20,0x10,0xfa,0x28,0x3e,0x46,0x53,0x1c,0xee,0xf0,0x76,0x4c,0xb4,0x12,0x7f,0x60,0x2d,0x77,0xfd,0xd2,0x54,0xb3,0x97,0xcf,0x6e,0x5,0xf2,0xc7,0x91,0x90,0x51,0xa7,0x3f,0x34,0xa0,0x62,0xae,0xdc,0x6,0xbc,0x4d,0xa3,0x7b,0x2f,0x7,0x84,0x9c,0xdb,0xaf,0x7c,0x6a,0x24,0xec,0x14,0x52,0x19,0x9d,0xf3,0x81,0x4,0x32,0xc5,0xd4,0x4f,0x82,0xb,0xfb,0xe2,0x94,0x8e,0x73,0xc3,0x37,0x15,0x5b,0x83,0xa1,0xc,0xaa,0x4e,0x93,0x17,0x96,0xbb,0x58,0x72,0xa5,0xfc,0x5b,0x97,0x9,0xce,0x6e,0x7b,0x9e,0xfc,0xea,0x64,0xe6,0x79,0xbb,0x78,0xbf,0x5f,0xa4,0xd,0x69,0x4d,0xd9,0x48,0x8d,0x99,0x43,0x4a,0xb0,0x8b,0x81,0xcb,0x5a,0xef,0xd8,0xe0,0xbc,0xe,0xa8,0xd1,0xd3,0xde,0x25,0x86,0x8c,0x3b,0xfe,0xb6,0x1,0xb5,0xab,0x82,0xa,0xd7,0x7d,0x4c,0x57,0x1b,0x17,0x8e,0xb9,0x13,0x0,0x1f,0x7,0xb,0x83,0x6c,0xae,0x8f,0xca,0xc0,0x5d,0xa6,0xe1,0xee,0x89,0x44,0xdf,0x2d,0xf3,0x3c,0x40,0x2c,0xd4,0xe3,0x85,0x39,0x3,0x7e,0x31,0x3f,0xf9,0x4f,0x93,0xac,0x87,0xd6,0x30,0x36,0xb3,0x24,0xb7,0x16,0x45,0xc2,0x80,0x5c,0x27,0xcd,0xad,0x2f,0x91,0x7c,0x9f,0x55,0xf8,0x53,0x47,0x2e,0xcf,0xaa,0x12,0x67,0xdc,0x26,0xff,0x75,0x66,0x50,0x6,0x19,0x74,0xd2,0x2a,0x10,0x96,0x88,0xa9,0xf1,0xd5,0x32,0xb4,0x9b,0x11,0x4b,0xe9,0xed,0x5,0x98,0xaf,0xbe,0x1e,0xd0,0x7a,0x35,0x20,0x58,0x4e,0x9c,0x76,0x46,0x22,0xa2,0xa7,0x77,0x92,0x3a,0x68,0xc4,0x56,0x5e,0xf4,0xec,0xfd,0x1c,0xa0,0x33,0x21,0x18,0xf,0x65,0xdb,0xb8,0x41,0x2,0xeb,0x23,0x6b,0x38,0x70,0xb1,0x90,0x6f,0x28,0xcc,0x6a,0xc7,0xe5,0x3d,0x73,0x51,0x9a,0xc3,0x14,0x3e,0xdd,0xf0,0x71,0xf5,0x29,0xb2,0xa3,0x54,0x62,0xe7,0x95,0xfb,0xa5,0x15,0xe8,0xf2,0x84,0x9d,0x6d,0xe4,0xbd,0xfa,0xe2,0x61,0x49,0x1d,0xc5,0x2b,0x7f,0x34,0x72,0x8a,0x42,0xc,0x1a,0xc9,0xc1,0x37,0xf6,0xf7,0xa1,0x94,0x63,0x8,0xda,0x60,0xba,0xc8,0x4,0xc6,0x52,0x59,0x42,0x2e,0x8d,0xba,0x57,0xeb,0x10,0x6d,0x51,0x5f,0x21,0x97,0xc2,0xfd,0xb8,0xe9,0x2,0xed,0xe1,0xc0,0xae,0xa4,0xc8,0x33,0x80,0x8f,0x2a,0xe7,0x43,0xb1,0x52,0x9d,0x3b,0xf1,0x3d,0x96,0x40,0x29,0xc4,0xa1,0x9,0x7c,0x48,0xb2,0x1b,0x91,0x3e,0x8,0x58,0x5e,0x4a,0xdd,0x78,0xd9,0xac,0x2b,0x32,0xee,0xa3,0x49,0x41,0xc3,0x12,0xff,0x63,0xca,0x23,0x7,0x26,0xb7,0xf7,0xe3,0x24,0x2d,0xe5,0xde,0xa5,0xef,0x81,0x34,0xf9,0x35,0xa0,0x67,0x15,0x0,0x92,0xf0,0xa,0x84,0x17,0x88,0x16,0xd5,0x31,0xd1,0xec,0xc5,0xb9,0x64,0x22,0x13,0x75,0x39,0xe0,0x79,0x7d,0xd7,0x71,0x6e,0x65,0x69,0x8e,0xb6,0x60,0xd2,0xbf,0xc6,0xb0,0xbd,0xe8,0x4b,0x55,0xe2,0xd8,0x90,0xdb,0x6f,0xdc,0x47,0x3a,0xcd,0x89,0xc,0x95,0xfb,0x7b,0xcb,0x9c,0x86,0xf3,0xea,0x8a,0x3,0xa2,0x46,0xa9,0x4,0x53,0x8b,0x3f,0x1d,0xad,0xf4,0x50,0x7a,0x9e,0xb3,0x9b,0x1f,0x59,0xaf,0x99,0x98,0xfa,0xcf,0x66,0xd,0xe,0xb4,0xa6,0xd4,0xa8,0x6a,0x37,0x3c,0x94,0xd3,0xf,0x8c,0x73,0x27,0x45,0xab,0x5a,0x11,0xe4,0x1c,0x62,0x2c,0xa7,0x74,0x83,0x87,0xf6,0x6b,0xd0,0xc1,0xbe,0x70,0x5b,0x14,0x36,0x4e,0xf2,0x20,0x28,0x18,0x77,0x68,0xbc,0x1a,0x7e,0x44,0xe6,0xf8,0x9f,0xc7,0x5c,0xbb,0xf5,0xda,0x25,0x7f,0x76,0x4f,0xb,0x61,0xd6,0xb5,0x6c,0x2f,0x4d,0x85,0x56,0x5,0xdf,0x1e,0x1,0xfe,0xcc,0x4c,0x19,0xc9,0x54,0xfc,0xaa,0x6,0x30,0x38,0x82,0x9a,0x72,0x93,0x5d,0xce,0x43,0x72,0x14,0x58,0x8d,0xa4,0xd8,0x5,0x10,0xf,0x4,0x8,0x81,0x18,0x1c,0xb6,0xde,0xa7,0xd1,0xdc,0xef,0xd7,0x1,0xb3,0xb9,0xf1,0xba,0xe,0x89,0x2a,0x34,0x83,0x47,0xd6,0x96,0x82,0x2,0xab,0x42,0x66,0xc4,0x8e,0xe0,0x55,0x45,0x4c,0x84,0xbf,0x74,0x61,0xf3,0x91,0x98,0x54,0xc1,0x6,0x77,0xb4,0x50,0xb0,0x6b,0xe5,0x76,0xe9,0x21,0x48,0xa5,0xc0,0x5a,0x90,0x5c,0xf7,0x7a,0xf0,0x5f,0x69,0x68,0x1d,0x29,0xd3,0x19,0xb8,0xcd,0x4a,0x39,0x3f,0x2b,0xbc,0x20,0xa2,0x73,0x9e,0x53,0x8f,0xc2,0x28,0x36,0x8a,0x71,0xc,0x23,0x4f,0xec,0xdb,0xa3,0x9c,0xd9,0x88,0x30,0x3e,0x40,0xf6,0xcf,0xc5,0xa9,0x52,0x63,0x8c,0x80,0xa1,0x22,0xd0,0x33,0xfc,0xe1,0xee,0x4b,0x86,0xb7,0xd4,0xd,0x4e,0x17,0x2e,0x6a,0x0,0xbe,0x7f,0x60,0x9f,0x2c,0xe4,0x37,0x64,0x35,0x9d,0xcb,0x67,0xad,0x2d,0x78,0xa8,0x13,0xf2,0x3c,0xaf,0x51,0x59,0xe3,0xfb,0xb1,0xa0,0xdf,0x11,0xe2,0xe6,0x97,0xa,0x93,0x41,0x49,0x79,0x3a,0x75,0x57,0x2f,0x1f,0x25,0x87,0x99,0x16,0x9,0xdd,0x7b,0x94,0xbb,0x44,0x1e,0xfe,0xa6,0x3d,0xda,0x9b,0xae,0x7,0x6c,0x38,0xce,0xf8,0xf9,0xc9,0xb,0x56,0x5d,0x6f,0xd5,0xc7,0xb5,0x12,0x46,0x24,0xca,0xf5,0xb2,0x6e,0xed,0x3,0x4d,0xc6,0x15,0x3b,0x70,0x85,0x7d,0xe8,0x6d,0xf4,0x9a,0xbd,0x26,0x5b,0xac,0x92,0x8b,0xeb,0x62,0x1a,0xaa,0xfd,0xe7,0x32,0xea,0x5e,0x7c,0xc3,0x27,0xc8,0x65,0xff,0xd2,0xfa,0x7e,0xcc,0x95,0x31,0x1b,0x88,0x17,0x84,0xa,0xd1,0x31,0xd5,0x16,0x67,0xa0,0x35,0xf9,0xf0,0x92,0x0,0x15,0xde,0xe5,0x2d,0x24,0x34,0x81,0xef,0xa5,0x7,0x23,0xca,0x63,0xe3,0xf7,0xb7,0x26,0xe2,0x55,0x4b,0xe8,0x6f,0xdb,0x90,0xd8,0xd2,0x60,0xb6,0x8e,0xbd,0xb0,0xc6,0xbf,0xd7,0x7d,0x79,0xe0,0x69,0x65,0x6e,0x71,0x64,0xb9,0xc5,0xec,0x39,0x75,0x13,0x22,0xe7,0x2a,0x8f,0x80,0x9d,0x52,0xb1,0x43,0xc0,0xe1,0xed,0x2,0x33,0xc8,0xa4,0xae,0x97,0x21,0x5f,0x51,0xe9,0xb8,0xfd,0xc2,0xba,0x8d,0x2e,0x42,0x6d,0x10,0xeb,0x57,0x49,0xa3,0xee,0x32,0xff,0x12,0xc3,0x41,0xdd,0x4a,0x5e,0x58,0x2b,0xac,0xd9,0x78,0xb2,0x48,0x7c,0x9,0x8,0x3e,0x91,0x1b,0x96,0x3d,0xf1,0x3b,0xa1,0xc4,0x29,0x40,0xbb,0x5c,0xc7,0x9f,0x7f,0x25,0xda,0xf5,0x1a,0xbc,0x68,0x77,0xf8,0xe6,0x44,0x7e,0x4e,0x36,0x14,0x5b,0x18,0x28,0x20,0xf2,0x6b,0xf6,0x87,0x83,0x70,0xbe,0xc1,0xd0,0x9a,0x82,0x38,0x30,0xce,0x5d,0x93,0x72,0xc9,0x19,0x4c,0xcc,0x6,0xaa,0xfc,0x54,0x5,0x56,0x85,0x4d,0xfe,0x1,0x1e,0xdf,0x61,0xb,0x4f,0x76,0x2f,0x6c,0xb5,0xd6,0x7a,0x50,0xf4,0xad,0x1f,0x9b,0xb3,0x9e,0x4,0xa9,0x46,0xa2,0x1d,0x3f,0x8b,0x53,0x86,0x9c,0xcb,0x7b,0x3,0x8a,0xea,0xf3,0xcd,0x3a,0x47,0xdc,0xfb,0x95,0xc,0x89,0x1c,0xe4,0x11,0x5a,0x74,0xa7,0x2c,0x62,0x8c,0xf,0xd3,0x94,0xab,0x45,0x27,0x73,0xd4,0xa6,0xb4,0xe,0x3c,0x37,0x6a,0xa8,0x98,0x99,0xaf,0x59,0xd,0x66,0xcf,0xfa,0xce,0x57,0x53,0xf9,0x5f,0x40,0x4b,0x47,0xc2,0xeb,0x97,0x4a,0xc,0x3d,0x5b,0x17,0xc6,0x65,0x7b,0xcc,0xf6,0xbe,0xf5,0x41,0xa0,0x98,0x4e,0xfc,0x91,0xe8,0x9e,0x93,0xa,0x3,0xcb,0xf0,0x8b,0xc1,0xaf,0x1a,0x4d,0xe4,0xd,0x29,0x8,0x99,0xd9,0xcd,0x24,0xaa,0x39,0xa6,0x38,0xfb,0x1f,0xff,0xd7,0x1b,0x8e,0x49,0x3b,0x2e,0xbc,0xde,0x27,0x52,0x66,0x9c,0x35,0xbf,0x10,0x26,0x15,0xdf,0x13,0xb8,0x6e,0x7,0xea,0x8f,0x1c,0xc0,0x8d,0x67,0x6f,0xed,0x3c,0xd1,0x76,0x70,0x64,0xf3,0x56,0xf7,0x82,0x5,0x7f,0x71,0xf,0xb9,0xec,0xd3,0x96,0xc7,0x6c,0x0,0xa3,0x94,0x79,0xc5,0x3e,0x43,0xae,0xa1,0x4,0xc9,0x6d,0x9f,0x7c,0xb3,0x2c,0xc3,0xcf,0xee,0x80,0x8a,0xe6,0x1d,0x63,0xab,0x78,0x2b,0xf1,0x30,0x2f,0xd0,0x58,0x61,0x25,0x4f,0xf8,0x9b,0x42,0x1,0x1e,0x16,0xac,0xb4,0x5c,0xbd,0x73,0xe0,0xe2,0x62,0x37,0xe7,0x7a,0xd2,0x84,0x28,0x75,0x3a,0x18,0x60,0xdc,0xe,0x6,0x36,0xad,0xa9,0xd8,0x45,0xfe,0xef,0x90,0x5e,0xb1,0xe9,0x72,0x95,0xdb,0xf4,0xb,0x51,0x59,0x46,0x92,0x34,0x50,0x6a,0xc8,0xd6,0x20,0x9a,0x88,0xfa,0x86,0x44,0x19,0x12,0x77,0x81,0xb7,0xb6,0xd4,0xe1,0x48,0x23,0x74,0x3f,0xca,0x32,0x4c,0x2,0x89,0x5a,0xba,0xfd,0x21,0xa2,0x5d,0x9,0x6b,0x85,0x55,0xe5,0xb2,0xa8,0xdd,0xc4,0xa4,0x2d,0xf2,0x69,0x14,0xe3,0xa7,0x22,0xbb,0xd5,0x83,0xda,0x7e,0x54,0xb0,0x9d,0xb5,0x31,0x8c,0x68,0x87,0x2a,0x7d,0xa5,0x11,0x33,0xc,0x6f,0x96,0xd5,0xf6,0xcf,0xd8,0xb2,0xa7,0x66,0x47,0xb8,0x3c,0xf4,0xbc,0xef,0x45,0xed,0xbf,0x13,0xf5,0x75,0x70,0xa0,0x2a,0xcb,0x77,0xe4,0x81,0x89,0x23,0x3b,0x78,0x69,0xc9,0x7,0x3e,0x3a,0xd2,0x4f,0x99,0x4b,0xa1,0x91,0xad,0xe2,0xf7,0x8f,0xfd,0xc7,0x41,0x5f,0xd1,0xce,0xa3,0x5,0x63,0x4c,0xc6,0x9c,0x7e,0x26,0x2,0xe5,0x76,0x43,0xb4,0xdf,0x16,0xe0,0x21,0x20,0xd3,0x11,0x85,0x8e,0xd,0xb7,0x6d,0x1f,0x9e,0xca,0x12,0xfc,0x6a,0x2d,0x35,0xb6,0x95,0xdb,0xcd,0x1e,0xa8,0xe3,0xa5,0x5d,0xb5,0x30,0x42,0x2c,0xfe,0x65,0x74,0x83,0x53,0x4a,0xba,0x33,0x72,0xc2,0x3f,0x25,0x32,0xea,0xa4,0x86,0xff,0x1b,0xbd,0x10,0xa,0x27,0xa6,0x22,0x4d,0x14,0xc3,0xe9,0xaa,0x9b,0x80,0xcc,0x7c,0x55,0xdd,0x0,0xd7,0xc8,0xd0,0xdc,0xc0,0x59,0x6e,0xc4,0x7f,0x6,0x4,0x9,0xf,0x37,0x6b,0xd9,0x29,0x61,0xd6,0x62,0xf2,0x51,0x5b,0xec,0xe,0x9f,0x5a,0x4e,0x73,0xda,0xbe,0x9a,0x56,0x1c,0x8d,0x38,0x94,0x9d,0x67,0x5c,0xb9,0xac,0x49,0x2b,0x8c,0x40,0xde,0x19,0x6c,0xaf,0x68,0x88,0x3d,0xb3,0x31,0xae,0x90,0xf9,0x18,0x7d,0x48,0x82,0x2f,0x84,0x28,0xa2,0xb1,0x87,0xc5,0xb0,0xb,0xf1,0x60,0xc1,0x92,0x15,0xe7,0xe1,0x64,0xf3,0x7a,0xf8,0x46,0xab,0x57,0x8b,0xf0,0x1a,0x52,0xee,0xd4,0xa9,0x97,0xfb,0x3,0x34,0x44,0x7b,0x50,0x1,0xe6,0xe8,0x2e,0x98,0x1d,0x17,0x8a,0x71,0x54,0xbb,0x79,0x58,0x8,0xfa,0x24,0xeb,0x36,0x39,0x5e,0x93,0x1,0x52,0x1a,0xd2,0x56,0xa9,0x88,0x49,0x5c,0x36,0x21,0x18,0x3b,0x78,0x81,0xe2,0xd5,0xcd,0x67,0x6f,0xa,0x99,0x25,0xc4,0x4e,0x9e,0x9b,0x1b,0xfd,0x51,0x3,0xab,0x61,0x19,0xc,0x43,0x7f,0x4f,0xa5,0x77,0xa1,0x3c,0xd4,0xd0,0xe9,0x27,0x87,0x96,0xb,0xec,0xc8,0x90,0x72,0x28,0xa2,0x8d,0xeb,0x4d,0x20,0x3f,0xb1,0xaf,0x29,0x13,0xf1,0x83,0x59,0xe3,0x60,0x6b,0xff,0x3d,0xce,0xcf,0xe,0xf8,0x31,0x5a,0xad,0x98,0xb3,0x4b,0xd,0x46,0xf0,0x23,0x35,0x7b,0x58,0xdb,0xc3,0x84,0x12,0xfc,0x24,0x70,0xcb,0xd1,0x2c,0x9c,0xdd,0x54,0xa4,0xbd,0x6d,0x9a,0x8b,0x10,0xc2,0xac,0xde,0x5b,0x7,0x2d,0xfa,0xa3,0xcc,0x48,0xc9,0xe4,0xfe,0x53,0xf5,0x11,0x68,0x4a,0x4,0xdc,0x2a,0x80,0xb7,0x2e,0x32,0x3e,0x26,0x39,0xee,0x33,0xbb,0x92,0x22,0x6e,0x75,0x44,0x2,0xb5,0xbf,0x1c,0x8c,0x38,0x8f,0xc7,0x37,0x85,0xd9,0xe1,0xe7,0xea,0xe8,0x91,0xb2,0x89,0x73,0x7a,0xd6,0x63,0xf2,0xb8,0x74,0x50,0x34,0x9d,0xa0,0xb4,0x71,0xe0,0x40,0xdf,0x5d,0xd3,0x66,0x86,0x41,0x82,0xf7,0x30,0xae,0x62,0xc5,0xa7,0x42,0x57,0x1f,0xe5,0x5e,0x2b,0x69,0x5f,0x4c,0xc6,0x6a,0xc1,0x6c,0xa6,0x93,0xf6,0x17,0x7e,0xf4,0x1e,0x65,0xb9,0x45,0xa8,0x16,0x94,0x1d,0x8a,0xf,0x9,0xfb,0x7c,0x2f,0x8e,0x76,0xc0,0x6,0x8,0xef,0xbe,0x95,0xaa,0xda,0xed,0x15,0x79,0x47,0x3a,0x0,0xbc,0x7d,0xb0,0xd7,0xd8,0x5,0xca,0x14,0xe6,0xb6,0x97,0x55,0xba,0x9f,0x64,0xf9,0xf3,0x52,0x53,0x65,0x93,0xc7,0xac,0x5,0x30,0x1e,0x6c,0x7e,0xc4,0xf6,0xfd,0xa0,0x62,0x46,0xc5,0x19,0x5e,0x61,0x8f,0xed,0xb9,0xd6,0x2e,0xdb,0x90,0xbe,0x6d,0xe6,0xa8,0x7,0xf0,0x8d,0x16,0x31,0x5f,0xc6,0x43,0x4c,0x56,0x1,0xb1,0xc9,0x40,0x20,0x39,0xce,0x63,0x8c,0x68,0xd7,0xf5,0x41,0x99,0xb0,0x9a,0x3e,0x67,0xd5,0x51,0x79,0x54,0xab,0xc1,0x85,0xbc,0xe5,0xa6,0x7f,0x1c,0xcf,0x9c,0x4f,0x87,0x34,0xcb,0xd4,0x15,0x3,0xd3,0x86,0x6,0xcc,0x60,0x36,0x9e,0x50,0x48,0xf2,0xfa,0x4,0x97,0x59,0xb8,0xa1,0x3c,0x4d,0x49,0xba,0x74,0xb,0x1a,0x84,0xfc,0xde,0x91,0xd2,0xe2,0xea,0x38,0xd0,0x76,0xa2,0xbd,0x32,0x2c,0x8e,0xb4,0x71,0x96,0xd,0x55,0xb5,0xef,0x10,0x3f,0x5c,0xf7,0x3b,0xf1,0x6b,0xe,0xe3,0x8a,0x78,0x82,0xb6,0xc3,0xc2,0xf4,0x5b,0xd1,0x17,0x80,0x94,0x92,0xe1,0x66,0x13,0xb2,0x83,0x69,0x24,0xf8,0x35,0xd8,0x9,0x8b,0x70,0x47,0xe4,0x88,0xa7,0xda,0x21,0x9d,0x5d,0xeb,0x95,0x9b,0x23,0x72,0x37,0x8,0xa,0x2b,0x27,0xc8,0xf9,0x2,0x6e,0x64,0x2d,0xe0,0x45,0x4a,0x57,0x98,0x7b,0x89,0xae,0x73,0xf,0x26,0xf3,0xbf,0xd9,0xe8,0x1d,0xb7,0xb3,0x2a,0xa3,0xaf,0xa4,0xbb,0x18,0xaa,0x7c,0x44,0x77,0x7a,0xc,0x75,0x28,0x9f,0x81,0x22,0xa5,0x11,0x5a,0x12,0xcd,0xe9,0x0,0xa9,0x29,0x3d,0x7d,0xec,0x14,0x2f,0xe7,0xee,0xfe,0x4b,0x25,0x6f,0xad,0x6a,0xff,0x33,0x3a,0x58,0xca,0xdf,0x42,0xdd,0x4e,0xc0,0x1b,0xfb,0x1f,0xdc,0xab,0x6,0xe9,0xd,0xb2,0x90,0x24,0xfc,0xd5,0xff,0x5b,0x2,0xb0,0x34,0x1c,0x31,0x62,0x95,0xe8,0x73,0x54,0x3a,0xa3,0x26,0x29,0x33,0x64,0xd4,0xac,0x25,0x45,0x5c,0x23,0xa0,0x7c,0x3b,0x4,0xea,0x88,0xdc,0xb3,0x4b,0xbe,0xf5,0xdb,0x8,0x83,0xcd,0x37,0x36,0x0,0xf6,0xa2,0xc9,0x60,0x55,0x7b,0x9,0x1b,0xa1,0x93,0x98,0xc5,0x7,0xb5,0x13,0xc7,0xd8,0x57,0x49,0xeb,0xd1,0x14,0xf3,0x68,0x30,0xd0,0x8a,0x75,0x5a,0xc4,0x59,0x28,0x2c,0xdf,0x11,0x6e,0x7f,0xe1,0x99,0xbb,0xf4,0xb7,0x87,0x8f,0x5d,0x66,0xb6,0xe3,0x63,0xa9,0x5,0x53,0xfb,0x35,0x2d,0x97,0x9f,0x61,0xf2,0x3c,0xdd,0xce,0xa4,0xe0,0xd9,0x80,0xc3,0x1a,0x79,0xaa,0xf9,0x2a,0xe2,0x51,0xae,0xb1,0x70,0x6f,0x4e,0x42,0xad,0x9c,0x67,0xb,0x1,0x48,0x85,0x20,0x2f,0x32,0xfd,0x1e,0xec,0x15,0x22,0x81,0xed,0xc2,0xbf,0x44,0xf8,0x38,0x8e,0xf0,0xfe,0x46,0x17,0x52,0x6d,0x72,0xe5,0xf1,0xf7,0x84,0x3,0x76,0xd7,0xe6,0xc,0x41,0x9d,0x50,0xbd,0x6c,0xee,0x39,0x92,0x5e,0x94,0xe,0x6b,0x86,0xef,0x1d,0xe7,0xd3,0xa6,0xa7,0x91,0x3e,0xb4,0xc8,0xf,0x9a,0x56,0x5f,0x3d,0xaf,0xba,0x27,0xb8,0x2b,0xa5,0x7e,0x9e,0x7a,0xb9,0xa8,0x8c,0x65,0xcc,0x4c,0x58,0x18,0x89,0x71,0x4a,0x82,0x8b,0x9b,0x2e,0x40,0xa,0x7d,0xcf,0x19,0x21,0x12,0x1f,0x69,0x10,0x4d,0xfa,0xe4,0x47,0xc0,0x74,0x3f,0x77,0xcb,0x16,0x6a,0x43,0x96,0xda,0xbc,0x8d,0x78,0xd2,0xd6,0x4f,0xc6,0xca,0xc1,0xde,0xe8,0xd3,0x1b,0x12,0x2,0xb7,0xd9,0x93,0x31,0x15,0xfc,0x55,0xd5,0xc1,0x81,0x10,0xbe,0x21,0xb2,0x3c,0xe7,0x7,0xe3,0x20,0x51,0x96,0x3,0xcf,0xc6,0xa4,0x36,0x23,0xe1,0x4b,0x4f,0xd6,0x5f,0x53,0x58,0x47,0x52,0x8f,0xf3,0xda,0xf,0x43,0x25,0x14,0xd4,0x63,0x7d,0xde,0x59,0xed,0xa6,0xee,0xe4,0x56,0x80,0xb8,0x8b,0x86,0xf0,0x89,0xa1,0x17,0x69,0x67,0xdf,0x8e,0xcb,0xf4,0x8c,0xbb,0x18,0x74,0x5b,0x26,0xdd,0x61,0xd1,0x1c,0xb9,0xb6,0xab,0x64,0x87,0x75,0xf6,0xd7,0xdb,0x34,0x5,0xfe,0x92,0x98,0x84,0x7e,0x4a,0x3f,0x3e,0x8,0xa7,0x2d,0xa0,0xb,0xc7,0xd,0x97,0xf2,0x1f,0x76,0x7f,0x95,0xd8,0x4,0xc9,0x24,0xf5,0x77,0xeb,0x7c,0x68,0x6e,0x1d,0x9a,0xef,0x4e,0x78,0x0,0x22,0x6d,0x2e,0x1e,0x16,0xc4,0x5d,0xc0,0xb1,0xb5,0x46,0x88,0xf7,0xe6,0x8d,0x6a,0xf1,0xa9,0x49,0x13,0xec,0xc3,0x2c,0x8a,0x5e,0x41,0xce,0xd0,0x72,0x48,0x33,0x60,0xb3,0x7b,0xc8,0x37,0x28,0xe9,0x57,0x3d,0x79,0x40,0x19,0x5a,0x83,0xe0,0xac,0xb4,0xe,0x6,0xf8,0x6b,0xa5,0x44,0xff,0x2f,0x7a,0xfa,0x30,0x9c,0xca,0x62,0xb0,0xaa,0xfd,0x4d,0x35,0xbc,0xdc,0xc5,0xfb,0xc,0x71,0xea,0xcd,0xa3,0x3a,0xbf,0x4c,0x66,0xc2,0x9b,0x29,0xad,0x85,0xa8,0x32,0x9f,0x70,0x94,0x2b,0x9,0xbd,0x65,0xe2,0x90,0x82,0x38,0xa,0x1,0x5c,0x9e,0xae,0xaf,0x99,0x6f,0x3b,0x50,0xf9,0xcc,0x2a,0xd2,0x27,0x6c,0x42,0x91,0x1a,0x54,0xba,0x39,0xe5,0xa2,0x9d,0x73,0x11,0x45,0xd8,0x96,0x1d,0xce,0xe0,0xab,0x5e,0xa6,0xc9,0x9d,0xff,0x11,0x2e,0x69,0xb5,0x36,0x12,0xd0,0x8d,0x86,0xb4,0xe,0x1c,0x6e,0x40,0x75,0xdc,0xb7,0xe3,0x15,0x23,0x22,0x24,0x9,0x21,0xa5,0x17,0x4e,0xea,0xc0,0xe9,0x31,0x85,0xa7,0x18,0xfc,0x13,0xbe,0x49,0x50,0x30,0xb9,0xc1,0x71,0x26,0x3c,0x33,0xb6,0x2f,0x41,0x66,0xfd,0x80,0x77,0xc8,0x29,0xe7,0x74,0x8a,0x82,0x38,0x20,0xee,0x46,0x10,0xbc,0x76,0xf6,0xa3,0x73,0x65,0xa4,0xbb,0x44,0xf7,0x3f,0xec,0xbf,0x6c,0xf,0xd6,0x95,0xcc,0xf5,0xb1,0xdb,0x4f,0x60,0x9f,0xc5,0x25,0x7d,0xe6,0x1,0xc4,0xfe,0x5c,0x42,0xcd,0xd2,0x6,0xa0,0x48,0x9a,0x92,0xa2,0xe1,0xae,0x8c,0xf4,0x6a,0x7b,0x4,0xca,0x39,0x3d,0x4c,0xd1,0xfb,0x79,0xa8,0x45,0x88,0x54,0x19,0xf3,0xc2,0x63,0x16,0x91,0xe2,0xe4,0xf0,0x67,0xa1,0x2b,0x84,0xb2,0xb3,0xc6,0xf2,0x8,0xfa,0x93,0x7e,0x1b,0x81,0x4b,0x87,0x2c,0xf9,0xb,0xe8,0x27,0x3a,0x35,0x90,0x5d,0x14,0x1e,0x72,0x89,0xb8,0x57,0x5b,0x7a,0x78,0x47,0x2,0x53,0xeb,0xe5,0x9b,0x2d,0xed,0x51,0xaa,0xd7,0xf8,0x94,0x37,0x0,0x62,0x2a,0x61,0xd5,0x52,0xf1,0xef,0x58,0x5,0x7c,0xa,0x7,0x34,0xc,0xda,0x68,0xcb,0xd4,0xdf,0xd3,0x5a,0xc3,0xc7,0x6d,0x98,0xa9,0xcf,0x83,0x56,0x7f,0x3,0xde,0xac,0x6f,0x8b,0x6b,0xb0,0x3e,0xad,0x32,0xaf,0xba,0x28,0x4a,0x43,0x8f,0x1a,0xdd,0x1f,0x55,0x3b,0x8e,0x9e,0x97,0x5f,0x64,0x9c,0xd,0x4d,0x59,0xd9,0x70,0x99,0xbd,0x26,0x7c,0xf6,0xd9,0x5f,0xb8,0x9c,0xc4,0xe5,0xfb,0x7d,0x47,0xbf,0x19,0x74,0x6b,0x2b,0x1b,0xf1,0x23,0x35,0x4d,0x58,0x17,0xbd,0x73,0xd3,0xc2,0xf5,0x68,0x80,0x84,0x5e,0xcd,0x71,0x90,0x81,0x99,0x33,0x3b,0xa9,0x5,0x57,0xff,0x1a,0xca,0xcf,0x4f,0x2,0xfd,0xdc,0x1d,0x55,0x6,0x4e,0x86,0x6f,0x2c,0xd5,0xb6,0x8,0x62,0x75,0x4c,0x98,0x1c,0x9d,0xb0,0x53,0x79,0xae,0xf7,0x3c,0x1e,0x50,0x88,0xaa,0x7,0xa1,0x45,0x89,0x0,0xf0,0xe9,0x9f,0x85,0x78,0xc8,0x96,0xf8,0x8a,0xf,0x39,0xce,0xdf,0x44,0xa4,0x77,0x61,0x2f,0xe7,0x1f,0x59,0x12,0x46,0xa8,0x70,0x24,0xc,0x8f,0x97,0xd0,0x34,0x3f,0xab,0x69,0xa5,0xd7,0xd,0xb7,0x65,0xe,0xf9,0xcc,0x9a,0x9b,0x5a,0xac,0x32,0xd2,0x15,0xd6,0x14,0x8b,0x9,0x87,0x91,0xf3,0x16,0x3,0xa3,0x64,0xfa,0x36,0x82,0x37,0xa6,0xec,0xe6,0xdd,0x27,0x2e,0xf4,0xe0,0x25,0xb4,0x20,0x4,0x60,0xc9,0xd8,0x6c,0xdb,0x93,0x56,0xe1,0xeb,0x48,0xb3,0xbe,0xbc,0xc5,0x63,0xd1,0x8d,0xb5,0x66,0x6a,0x72,0x6d,0x7e,0xd4,0xe3,0x7a,0x76,0x3a,0x21,0x10,0xba,0x67,0xef,0xc6,0x51,0x9e,0x40,0xb2,0x29,0xe4,0x83,0x8c,0xcb,0x30,0xad,0xa7,0xe2,0xc3,0x1,0xee,0xbb,0xea,0xc1,0xfe,0x22,0x94,0x52,0x5c,0x13,0x6e,0x54,0xe8,0x8e,0xb9,0x41,0x2d,0x11,0xfc,0x42,0xc0,0xa0,0x4a,0x31,0xed,0xaf,0x28,0x7b,0xda,0x49,0xde,0x5b,0x5d,0x3d,0xb,0x18,0x92,0x4b,0xb1,0xa,0x7f,0xc7,0xa2,0x43,0x2a,0x3e,0x95,0x38,0xf2,0xe9,0xe1,0x5b,0x43,0xab,0x4a,0x84,0x17,0x15,0x95,0xc0,0x10,0x8d,0x25,0x73,0xdf,0x94,0x5c,0x8f,0xdc,0x6,0xc7,0xd8,0x27,0xaf,0x96,0xd2,0xb8,0xf,0x6c,0xb5,0xf6,0x46,0x1e,0x85,0x62,0x2c,0x3,0xfc,0xa6,0xae,0xb1,0x65,0xc3,0xa7,0x9d,0x3f,0x21,0x82,0xcd,0xef,0x97,0x2b,0xf9,0xf1,0xc1,0x5a,0x5e,0x2f,0xb2,0x9,0x18,0x67,0xa9,0x83,0xc8,0x3d,0xc5,0xbb,0xf5,0x7e,0xad,0x4d,0xa,0xd6,0x55,0xaa,0xfe,0x9c,0x72,0xd7,0x6d,0x7f,0xd,0x71,0xb3,0xee,0xe5,0x80,0x76,0x40,0x41,0x23,0x16,0xbf,0xd4,0x74,0x2d,0x89,0xa3,0x47,0x6a,0x42,0xc6,0x7b,0x9f,0x70,0xdd,0x8a,0x52,0xe6,0xc4,0xa2,0x12,0x45,0x5f,0x2a,0x33,0x53,0xda,0x5,0x9e,0xe3,0x14,0x50,0xd5,0x4c,0x22,0x31,0x92,0x8c,0x3b,0x1,0x49,0x2,0xb6,0x57,0x6f,0xb9,0xb,0x66,0x1f,0x69,0x64,0x39,0xa0,0xa4,0xe,0xa8,0xb7,0xbc,0xb0,0x35,0x1c,0x60,0xbd,0xfb,0xca,0xac,0xe0,0xd3,0x5d,0xce,0x51,0xcf,0xc,0xe8,0x8,0x20,0xec,0x79,0xbe,0xcc,0xd9,0x4b,0x29,0xfd,0xf4,0x3c,0x7,0x7c,0x36,0x58,0xed,0xba,0x13,0xfa,0xde,0xff,0x6e,0x2e,0x3a,0xeb,0x37,0x7a,0x90,0x98,0x1a,0xcb,0x26,0x81,0x87,0x93,0x4,0xa1,0x0,0x75,0xf2,0xd0,0xa5,0x91,0x6b,0xc2,0x48,0xe7,0xd1,0xe2,0x28,0xe4,0x4f,0x99,0xf0,0x1d,0x78,0x59,0x56,0xf3,0x3e,0x9a,0x68,0x8b,0x44,0xdb,0x34,0x38,0x19,0x77,0x7d,0x11,0xea,0x88,0x86,0xf8,0x4e,0x1b,0x24,0x61,0x30,0x9b,0xf7,0x54,0x63,0x8e,0x32,0xc9,0xb4,0x41,0xb,0x65,0xd0,0xc0,0xc9,0x1,0x3a,0xc2,0x53,0x13,0x7,0x87,0x2e,0xc7,0xe3,0xf2,0x31,0xd5,0x35,0xee,0x60,0xf3,0x6c,0xf1,0xe4,0x76,0x14,0x1d,0xd1,0x44,0x83,0x95,0x8a,0x81,0x8d,0x4,0x9d,0x99,0x33,0xc6,0xf7,0x91,0xdd,0x8,0x21,0x5d,0x80,0x3c,0x74,0x3f,0x8b,0xc,0xaf,0xb1,0x6,0x5b,0x22,0x54,0x59,0x6a,0x52,0x84,0x36,0x26,0x19,0x5c,0xd,0xb5,0xbb,0xc5,0x73,0xb3,0xf,0xf4,0x89,0xa6,0xca,0x69,0x5e,0xa7,0x55,0xb6,0x79,0x64,0x6b,0xce,0x3,0x4a,0x40,0x2c,0xd7,0xe6,0x9,0x5,0x24,0xff,0x75,0xda,0xec,0xed,0x98,0xac,0x56,0xa4,0xcd,0x20,0x45,0xdf,0x15,0xd9,0x72,0xa5,0x27,0xf6,0x1b,0xd6,0xa,0x47,0xad,0x9c,0x3d,0x48,0xcf,0xbc,0xba,0xae,0x39,0x16,0xc4,0xcc,0xfc,0xbf,0xf0,0xd2,0xaa,0x34,0x25,0x5a,0x94,0x67,0x63,0x12,0x8f,0x11,0x3e,0xc1,0x9b,0x7b,0x23,0xb8,0x5f,0x9a,0xa0,0x2,0x1c,0x93,0x8c,0x58,0xfe,0x3b,0xfa,0xe5,0x1a,0xa9,0x61,0xb2,0xe1,0x32,0x51,0x88,0xcb,0x92,0xab,0xef,0x85,0x96,0x77,0xb9,0x2a,0xd4,0xdc,0x66,0x7e,0xb0,0x18,0x4e,0xe2,0x28,0xa8,0xfd,0x2d,0x17,0xe,0x6e,0xe7,0x9f,0x2f,0x78,0x62,0x6d,0xe8,0x71,0x1f,0x38,0xa3,0xde,0x29,0x7a,0x57,0x7f,0xfb,0x49,0x10,0xb4,0x9e,0xb7,0x6f,0xdb,0xf9,0x46,0xa2,0x4d,0xe0,0x4c,0x8e,0xd3,0xd8,0xea,0x50,0x42,0x30,0x1e,0x2b,0x82,0xe9,0xbd,0x4b,0x7d,0x7c,0x86,0xc8,0x43,0x90,0xbe,0xf5,0x0,0xf8,0x97,0xc3,0xa1,0x4f,0x70,0x37,0xeb,0x68,0xc2,0xab,0x46,0x23,0xb9,0x73,0xbf,0x14,0x99,0x13,0xbc,0x8a,0x8b,0xfe,0xca,0x30,0xfa,0x5b,0x2e,0xa9,0xda,0xdc,0xc8,0x5f,0xc3,0x41,0x90,0x7d,0xb0,0x6c,0x21,0xcb,0xd5,0x69,0x92,0xef,0xc0,0xac,0xf,0x38,0x40,0x7f,0x3a,0x6b,0xd3,0xdd,0xa3,0x15,0x2c,0x26,0x4a,0xb1,0x80,0x6f,0x63,0x42,0xc1,0x33,0xd0,0x1f,0x2,0xd,0xa8,0x65,0xa0,0x91,0xf7,0xbb,0x6e,0x47,0x3b,0xe6,0xf3,0xec,0xe7,0xeb,0x62,0xfb,0xff,0x55,0x3d,0x44,0x32,0x3f,0xc,0x34,0xe2,0x50,0x5a,0x12,0x59,0xed,0x6a,0xc9,0xd7,0x60,0xa4,0x35,0x75,0x61,0xe1,0x48,0xa1,0x85,0x27,0x6d,0x3,0xb6,0xa6,0xaf,0x67,0x5c,0x97,0x82,0x10,0x72,0x7b,0xb7,0x22,0xe5,0x94,0x57,0xb3,0x53,0x88,0x6,0x95,0xa,0x78,0x4d,0xe4,0x8f,0xdb,0x2d,0x1b,0x1a,0x2a,0xe8,0xb5,0xbe,0x8c,0x36,0x24,0x56,0xf1,0xa5,0xc7,0x29,0x16,0x51,0x8d,0xe,0xe0,0xae,0x25,0xf6,0xd8,0x93,0x66,0x9e,0xb,0x8e,0x17,0x79,0x5e,0xc5,0xb8,0x4f,0x71,0x68,0x8,0x81,0xf9,0x49,0x1e,0x4,0xd1,0x9,0xbd,0x9f,0x20,0xc4,0x2b,0x86,0x1c,0x31,0x19,0x9d,0x2f,0x76,0xd2,0xf8,0x54,0x37,0xee,0xad,0xf4,0xcd,0x89,0xe3,0x5d,0x9c,0x83,0x7c,0xcf,0x7,0xd4,0x87,0xd6,0x7e,0x28,0x84,0x4e,0xce,0x9b,0x4b,0xf0,0x11,0xdf,0x4c,0xb2,0xba,0x0,0x18,0x52,0x43,0x3c,0xf2,0x1,0x5,0x74,0xe9,0x70,0xa2,0xaa,0x9a,0xd9,0x96,0xb4,0xcc,0xfc,0xc6,0x64,0x7a,0xf5,0xea,0x3e,0x98,0x77,0x58,0xa7,0xfd,0x1d,0x45,0xde,0x39,0xa3,0xa9,0x34,0xcf,0xea,0x5,0xc7,0xe6,0xb6,0x44,0x9a,0x55,0x88,0x87,0xe0,0x2d,0xec,0x50,0x6a,0x17,0x29,0x45,0xbd,0x8a,0xfa,0xc5,0xee,0xbf,0x58,0x56,0x90,0x26,0xde,0x7f,0x2c,0xab,0x59,0x5f,0xda,0x4d,0xc4,0x46,0xf8,0x15,0xe9,0x35,0x4e,0xa4,0x2e,0x47,0xa6,0xc3,0xf6,0x3c,0x91,0x3a,0x96,0x1c,0xf,0x39,0x7b,0xe,0xb5,0x4f,0x7,0x12,0xf7,0x95,0x32,0xfe,0x60,0xa7,0xd2,0x11,0xd6,0x36,0x83,0xd,0x8f,0x10,0xb0,0x21,0xe4,0xf0,0xcd,0x64,0x0,0x24,0xe8,0xa2,0x33,0x86,0x2a,0x23,0xd9,0xe2,0xc1,0xb8,0xba,0xb7,0xb1,0x89,0xd5,0x67,0x97,0xdf,0x68,0xdc,0x4c,0xef,0xe5,0x52,0x14,0x25,0x3e,0x72,0xc2,0xeb,0x63,0xbe,0x69,0x76,0x6e,0x62,0x7e,0xe7,0xd0,0x7a,0x8c,0x54,0x1a,0x38,0x41,0xa5,0x3,0xae,0xb4,0x99,0x18,0x9c,0xf3,0xaa,0x7d,0x57,0xb,0x8e,0xfc,0x92,0x40,0xdb,0xca,0x3d,0xed,0xf4,0x4,0x8d,0xcc,0x7c,0x81,0x9b,0x20,0x74,0xac,0x42,0xd4,0x93,0x8b,0x8,0x2b,0x65,0x73,0xa0,0x16,0x5d,0x1b,0xe3,0xc8,0xfd,0xa,0x61,0xa8,0x5e,0x9f,0x9e,0x6d,0xaf,0x3b,0x30,0xb3,0x9,0xd3,0xa1,0x43,0x79,0xff,0xe1,0x6f,0x70,0x1d,0xbb,0xdd,0xf2,0x78,0x22,0xc0,0x98,0xbc,0x5b,0xc6,0xd7,0x77,0xb9,0x80,0x84,0x6c,0xf1,0x27,0xf5,0x1f,0x2f,0x13,0x5c,0x49,0x31,0xfb,0x53,0x1,0xad,0x4b,0xcb,0xce,0x1e,0x94,0x75,0xc9,0x5a,0x3f,0x37,0x9d,0x85,0xb2,0xd1,0x28,0x6b,0x48,0x71,0x66,0xc,0x19,0xd8,0xf9,0x6,0x82,0x4a,0x2,0x51,0xe8,0xcc,0x25,0x8c,0xc,0x18,0x58,0xc9,0x31,0xa,0xc2,0xcb,0xdb,0x6e,0x0,0x4a,0x88,0x4f,0xda,0x16,0x1f,0x7d,0xef,0xfa,0x67,0xf8,0x6b,0xe5,0x3e,0xde,0x3a,0xf9,0x8b,0x56,0x2a,0x3,0xd6,0x9a,0xfc,0xcd,0x38,0x92,0x96,0xf,0x86,0x8a,0x81,0x9e,0x3d,0x8f,0x59,0x61,0x52,0x5f,0x29,0x50,0xd,0xba,0xa4,0x7,0x80,0x34,0x7f,0x37,0x55,0x62,0xc1,0xad,0x82,0xff,0x4,0xb8,0x78,0xce,0xb0,0xbe,0x6,0x57,0x12,0x2d,0x2f,0xe,0x2,0xed,0xdc,0x27,0x4b,0x41,0x8,0xc5,0x60,0x6f,0x72,0xbd,0x5e,0xac,0x79,0xd2,0x1e,0xd4,0x4e,0x2b,0xc6,0xaf,0x5d,0xa7,0x93,0xe6,0xe7,0xd1,0x7e,0xf4,0x32,0xa5,0xb1,0xb7,0xc4,0x43,0x36,0x97,0xa6,0x4c,0x1,0xdd,0x10,0xfd,0x2c,0xae,0x84,0x19,0x68,0x6c,0x9f,0x51,0x2e,0x3f,0xa1,0xd9,0xfb,0xb4,0xf7,0xc7,0xcf,0x1d,0xf5,0x53,0x87,0x98,0x17,0x9,0xab,0x91,0x54,0xb3,0x28,0x70,0x90,0xca,0x35,0x1a,0x8e,0xe4,0xa0,0x99,0xc0,0x83,0x5a,0x39,0xea,0xb9,0x6a,0xa2,0x11,0xee,0xf1,0x30,0x26,0xf6,0xa3,0x23,0xe9,0x45,0x13,0xbb,0x75,0x6d,0xd7,0xdf,0x21,0xb2,0x7c,0x9d,0x22,0xd5,0xa8,0x33,0x14,0x7a,0xe3,0x66,0x69,0x73,0x24,0x94,0xec,0x65,0x5,0x1c,0xeb,0x46,0xa9,0x4d,0xf2,0xd0,0x64,0xbc,0x95,0xbf,0x1b,0x42,0xf0,0x74,0x5c,0x71,0x77,0x76,0x40,0xb6,0xe2,0x89,0x20,0x15,0x3b,0x49,0x5b,0xe1,0xd3,0xd8,0x85,0x47,0x63,0xe0,0x3c,0x7b,0x44,0xaa,0xc8,0x9c,0xf3,0xb,0xfe,0xb5,0x9b,0x48,0xc3,0x8d,0x66,0x2e,0x65,0xd1,0x56,0xf5,0xeb,0x5c,0x1,0x78,0xe,0x3,0x30,0x8,0xde,0x6c,0xcf,0xd0,0xdb,0xd7,0x5e,0xc7,0xc3,0x69,0x9c,0xad,0xcb,0x87,0x52,0x7b,0x7,0xda,0xa8,0x6b,0x8f,0x6f,0xb4,0x3a,0xa9,0x36,0xab,0xbe,0x2c,0x4e,0x47,0x8b,0x1e,0xd9,0x1b,0x51,0x3f,0x8a,0x9a,0x93,0x5b,0x60,0x98,0x9,0x49,0x5d,0xdd,0x74,0x9d,0xb9,0xff,0x7d,0xac,0x41,0x8c,0x50,0x1d,0xf7,0xc6,0x67,0x12,0x95,0xe6,0xe0,0xf4,0x63,0xa5,0x2f,0x80,0xb6,0xb7,0xc2,0xf6,0xc,0xfe,0x97,0x7a,0x1f,0x85,0x4f,0x83,0x28,0xfd,0xf,0xec,0x23,0x3e,0x31,0x94,0x59,0x10,0x1a,0x76,0x8d,0xbc,0x53,0x5f,0x7e,0x7c,0x43,0x6,0x57,0xef,0xe1,0x9f,0x29,0xe9,0x55,0xae,0xd3,0xfc,0x90,0x33,0x4,0xcc,0x2d,0xe3,0x70,0x8e,0x86,0x3c,0x24,0xea,0x42,0x14,0xb8,0x72,0xf2,0xa7,0x77,0x61,0xa0,0xbf,0x40,0xf3,0x3b,0xe8,0xbb,0x68,0xb,0xd2,0x91,0xc8,0xf1,0xb5,0xdf,0x4b,0x64,0x9b,0xc1,0x21,0x79,0xe2,0x5,0xc0,0xfa,0x58,0x46,0xc9,0xd6,0x2,0xa4,0x4c,0x9e,0x96,0xa6,0xe5,0xaa,0x88,0xf0,0x6e,0x7f,0x0,0xce,0x3d,0x39,0x48,0xd5,0xdc,0x92,0x19,0xca,0xe4,0xaf,0x5a,0xa2,0xcd,0x99,0xfb,0x15,0x2a,0x6d,0xb1,0x32,0x16,0xd4,0x89,0x82,0xb0,0xa,0x18,0x6a,0x44,0x71,0xd8,0xb3,0xe7,0x11,0x27,0x26,0x20,0xd,0x25,0xa1,0x13,0x4a,0xee,0xc4,0xed,0x35,0x81,0xa3,0x1c,0xf8,0x17,0xba,0x4d,0x54,0x34,0xbd,0xc5,0x75,0x22,0x38,0x37,0xb2,0x2b,0x45,0x62,0xf9,0x84,0x73,0xec,0x24,0x6c,0x3f,0x77,0xb6,0x97,0x68,0x26,0x1f,0x8,0x62,0xdc,0xbf,0x46,0x5,0x51,0x59,0xf3,0xeb,0xfa,0x1b,0xa7,0x34,0x25,0xa5,0xa0,0x70,0x95,0x3d,0x6f,0xc3,0x7d,0x32,0x27,0x5f,0x49,0x9b,0x71,0x41,0xee,0xea,0x2,0x9f,0xa8,0xb9,0x19,0xd7,0xae,0xf6,0xd2,0x35,0xb3,0x9c,0x16,0x4c,0x1,0x1e,0x73,0xd5,0x2d,0x17,0x91,0x8f,0xdd,0x67,0xbd,0xcf,0x3,0xc1,0x55,0x5e,0xc6,0x30,0xf1,0xf0,0xa6,0x93,0x64,0xf,0x78,0x33,0x75,0x8d,0x45,0xb,0x1d,0xce,0xba,0xfd,0xe5,0x66,0x4e,0x1a,0xc2,0x2c,0xa2,0x12,0xef,0xf5,0x83,0x9a,0x6a,0xe3,0x2e,0xb5,0xa4,0x53,0x65,0xe0,0x92,0xfc,0x9d,0xc4,0x13,0x39,0xda,0xf7,0x76,0xf2,0x2f,0xcb,0x6d,0xc0,0xe2,0x3a,0x74,0x56,0x10,0x89,0xbe,0x14,0x7,0x18,0x0,0xc,0xac,0x85,0xd,0xd0,0x7a,0x4b,0x50,0x1c,0x22,0x81,0x8b,0x3c,0xf9,0xb1,0x6,0xb2,0xdf,0xe7,0xbb,0x9,0xaf,0xd6,0xd4,0xd9,0x44,0x4d,0xb7,0x8c,0x86,0xcc,0x5d,0xe8,0xa3,0xa,0x6e,0x4a,0xde,0x4f,0x8a,0x9e,0xed,0x63,0xe1,0x7e,0xbc,0x7f,0xb8,0x58,0x5c,0x90,0xe,0xc9,0x69,0x7c,0x99,0xfb,0x15,0x60,0xdb,0x21,0xf8,0x72,0x61,0x57,0x98,0x52,0xff,0x54,0x40,0x29,0xc8,0xad,0x87,0x5b,0x20,0xca,0xaa,0x28,0x96,0x7b,0x37,0x31,0xb4,0x23,0xb0,0x11,0x42,0xc5,0x36,0x38,0xfe,0x48,0x94,0xab,0x80,0xd1,0x47,0x2b,0xd3,0xe4,0x82,0x3e,0x4,0x79,0xe6,0xe9,0x8e,0x43,0xd8,0x2a,0xf4,0x3b,0x84,0x6b,0xa9,0x88,0xcd,0xc7,0x5a,0xa1,0x42,0x90,0x98,0xa8,0xeb,0xa4,0x86,0xfe,0x60,0x71,0xe,0xc0,0x33,0x37,0x46,0xdb,0x45,0x6a,0x95,0xcf,0x2f,0x77,0xec,0xb,0xce,0xf4,0x56,0x48,0xc7,0xd8,0xc,0xaa,0x6f,0xae,0xb1,0x4e,0xfd,0x35,0xe6,0xb5,0x66,0x5,0xdc,0x9f,0xc6,0xff,0xbb,0xd1,0xc2,0x23,0xed,0x7e,0x80,0x88,0x32,0x2a,0xe4,0x4c,0x1a,0xb6,0x7c,0xfc,0xa9,0x79,0x43,0x5a,0x3a,0xb3,0xcb,0x7b,0x2c,0x36,0x39,0xbc,0x25,0x4b,0x6c,0xf7,0x8a,0x7d,0x2e,0x3,0x2b,0xaf,0x1d,0x44,0xe0,0xca,0xe3,0x3b,0x8f,0xad,0x12,0xf6,0x19,0xb4,0x18,0xda,0x87,0x8c,0xbe,0x4,0x16,0x64,0x4a,0x7f,0xd6,0xbd,0xe9,0x1f,0x29,0x28,0xd2,0x9c,0x17,0xc4,0xea,0xa1,0x54,0xac,0xc3,0x97,0xf5,0x1b,0x24,0x63,0xbf,0x3c,0x15,0x5f,0x31,0x84,0x94,0x9d,0x55,0x6e,0x96,0x7,0x47,0x53,0xd3,0x7a,0x93,0xb7,0xa6,0x65,0x81,0x61,0xba,0x34,0xa7,0x38,0xa5,0xb0,0x22,0x40,0x49,0x85,0x10,0xd7,0xc1,0xde,0xd5,0xd9,0x50,0xc9,0xcd,0x67,0x92,0xa3,0xc5,0x89,0x5c,0x75,0x9,0xd4,0x68,0x20,0x6b,0xdf,0x58,0xfb,0xe5,0x52,0xf,0x76,0x0,0xd,0x3e,0x6,0xd0,0x62,0x72,0x4d,0x8,0x59,0xe1,0xef,0x91,0x27,0xe7,0x5b,0xa0,0xdd,0xf2,0x9e,0x3d,0xa,0xf3,0x1,0xe2,0x2d,0x30,0x3f,0x9a,0x57,0x1e,0x14,0x78,0x83,0xb2,0x5d,0x51,0x70,0xab,0x21,0x8e,0xb8,0xb9,0xcc,0xf8,0x2,0xf0,0x99,0x74,0x11,0x8b,0x41,0x8d,0x26,0xf1,0x73,0xa2,0x4f,0x82,0x5e,0x13,0xf9,0xc8,0x69,0x1c,0x9b,0xe8,0xee,0xfa,0x6d,0x66,0x21,0xfd,0x7e,0x81,0xd5,0xb7,0x59,0xa8,0xe3,0x16,0xee,0x90,0xde,0x55,0x86,0xab,0x5d,0x6b,0x6a,0x8,0x3d,0x94,0xff,0xfc,0x46,0x54,0x26,0x5a,0x98,0xc5,0xce,0x50,0xb4,0x5b,0xf6,0xa1,0x79,0xcd,0xef,0x5f,0x6,0xa2,0x88,0x6c,0x41,0x69,0xed,0x2e,0xb5,0xc8,0x3f,0x7b,0xfe,0x67,0x9,0x89,0x39,0x6e,0x74,0x1,0x18,0x78,0xf1,0x3e,0xbe,0xeb,0x3b,0xa6,0xe,0x58,0xf4,0xc2,0xca,0x70,0x68,0x80,0x61,0xaf,0x3c,0x84,0xbd,0xf9,0x93,0x24,0x47,0x9e,0xdd,0xbf,0x77,0xa4,0xf7,0x2d,0xec,0xf3,0xc,0x85,0x9a,0x4e,0xe8,0x8c,0xb6,0x14,0xa,0x6d,0x35,0xae,0x49,0x7,0x28,0xd7,0x8d,0x71,0x75,0x4,0x99,0x22,0x33,0x4c,0x82,0xa9,0xe6,0xc4,0xbc,0x0,0xd2,0xda,0xea,0xaa,0xac,0xb8,0x2f,0x8a,0x2b,0x5e,0xd9,0xc0,0x1c,0x51,0xbb,0xb3,0x31,0xe0,0xd,0xc9,0x3,0xcf,0x64,0xb2,0xdb,0x36,0x53,0xfb,0x8e,0xba,0x40,0xe9,0x63,0xcc,0xfa,0xf0,0x1f,0x13,0x32,0x5c,0x56,0x3a,0xc1,0x72,0x7d,0xd8,0x15,0xb1,0x43,0xa0,0x6f,0xb0,0xdc,0x7f,0x48,0xa5,0x19,0xe2,0x9f,0xa3,0xad,0xd3,0x65,0x30,0xf,0x4a,0x1b,0x7c,0x44,0x92,0x20,0x4d,0x34,0x42,0x4f,0x1a,0xb9,0xa7,0x10,0x2a,0x62,0x29,0x9d,0x1e,0x37,0x4b,0x96,0xd0,0xe1,0x87,0xcb,0x12,0x8b,0x8f,0x25,0x83,0x9c,0x97,0x9b,0xb,0xc7,0x52,0x95,0xe7,0xf2,0x60,0x2,0xf8,0x76,0xe5,0x7a,0xe4,0x27,0xc3,0x23,0x91,0x38,0xd1,0xf5,0xd4,0x45,0x5,0x11,0xd6,0xdf,0x17,0x2c,0x57,0x1d,0x73,0xc6,0xc1,0x99,0xbd,0x5a,0xdc,0xf3,0x79,0x23,0x6e,0x71,0x1c,0xba,0x42,0x78,0xfe,0xe0,0x12,0x5d,0x48,0x30,0x26,0xf4,0x1e,0x2e,0x81,0x85,0x6d,0xf0,0xc7,0xd6,0x76,0xb8,0x3e,0x36,0x9c,0x84,0x95,0x74,0xc8,0x5b,0x4a,0xca,0xcf,0x1f,0xfa,0x52,0x0,0xac,0x83,0x4b,0x3,0x50,0x18,0xd9,0xf8,0x7,0x49,0x70,0x67,0xd,0xb3,0xd0,0x29,0x6a,0xf2,0xab,0x7c,0x56,0xb5,0x98,0x19,0x9d,0x40,0xa4,0x2,0xaf,0x8d,0x55,0x1b,0x39,0xcd,0x7d,0x80,0x9a,0xec,0xf5,0x5,0x8c,0x41,0xda,0xcb,0x3c,0xa,0x8f,0xfd,0x93,0x17,0x5c,0x1a,0xe2,0x2a,0x64,0x72,0xa1,0xd5,0x92,0x8a,0x9,0x21,0x75,0xad,0x43,0xb2,0x8,0xd2,0xa0,0x6c,0xae,0x3a,0x31,0xa9,0x5f,0x9e,0x9f,0xc9,0xfc,0xb,0x60,0x82,0xc,0x8e,0x11,0xd3,0x10,0xd7,0x37,0x33,0xff,0x61,0xa6,0x6,0x13,0xf6,0x94,0x2b,0x22,0xd8,0xe3,0xe9,0xa3,0x32,0x87,0xcc,0x65,0x1,0x25,0xb1,0x20,0xe5,0xf1,0x4d,0xee,0xe4,0x53,0x96,0xde,0x69,0xdd,0xb0,0x88,0xd4,0x66,0xc0,0xb9,0xbb,0xb6,0x7f,0xe6,0xd1,0x7b,0x68,0x77,0x6f,0x63,0xc3,0xea,0x62,0xbf,0x15,0x24,0x3f,0x73,0x89,0x86,0xe1,0x2c,0xb7,0x45,0x9b,0x54,0xeb,0x4,0xc6,0xe7,0xa2,0xa8,0x35,0xce,0x59,0x57,0x91,0x27,0xfb,0xc4,0xef,0xbe,0x28,0x44,0xbc,0x8b,0xed,0x51,0x6b,0x16,0xe8,0x34,0x4f,0xa5,0xc5,0x47,0xf9,0x14,0x58,0x5e,0xdb,0x4c,0xdf,0x7e,0x2d,0xaa,0x7a,0xf,0xb4,0x4e,0x97,0x1d,0xe,0x38,0xf7,0x3d,0x90,0x3b,0x2f,0x46,0xa7,0xc2,0x3,0xab,0xfd,0x51,0x9b,0x1b,0x4e,0x9e,0x25,0xc4,0xa,0x99,0x67,0x6f,0xd5,0xcd,0x81,0xe2,0x3b,0x78,0x21,0x18,0x5c,0x36,0x88,0x49,0x56,0xa9,0x1a,0xd2,0x1,0x52,0x29,0x13,0xb1,0xaf,0x20,0x3f,0xeb,0x4d,0xa2,0x8d,0x72,0x28,0xc8,0x90,0xb,0xec,0x87,0x96,0xe9,0x27,0xd4,0xd0,0xa1,0x3c,0xa5,0x77,0x7f,0x4f,0xc,0x43,0x61,0x19,0x24,0x70,0x12,0xfc,0xc3,0x84,0x58,0xdb,0x35,0x7b,0xf0,0x23,0xd,0x46,0xb3,0x4b,0xad,0x98,0x31,0x5a,0xe,0xf8,0xce,0xcf,0xff,0x3d,0x60,0x6b,0x59,0xe3,0xf1,0x83,0x4,0xdc,0x68,0x4a,0xf5,0x11,0xfe,0x53,0xc9,0xe4,0xcc,0x48,0xfa,0xa3,0x7,0x2d,0xde,0x5b,0xc2,0xac,0x8b,0x10,0x6d,0x9a,0xa4,0xbd,0xdd,0x54,0x2c,0x9c,0xcb,0xd1,0xe8,0x91,0xe7,0xea,0xd9,0xe1,0x37,0x85,0x8f,0xc7,0x8c,0x38,0xbf,0x1c,0x2,0xb5,0x75,0x44,0x22,0x6e,0xbb,0x92,0xee,0x33,0x26,0x39,0x32,0x3e,0xb7,0x2e,0x2a,0x80,0x42,0x57,0xc5,0xa7,0xae,0x62,0xf7,0x30,0x41,0x82,0x66,0x86,0x5d,0xd3,0x40,0xdf,0x71,0xe0,0xa0,0xb4,0x34,0x9d,0x74,0x50,0xf2,0xb8,0xd6,0x63,0x73,0x7a,0xb2,0x89,0x2f,0x8e,0xfb,0x7c,0xf,0x9,0x1d,0x8a,0x16,0x94,0x45,0xa8,0x65,0xb9,0xf4,0x1e,0x17,0x7e,0x93,0xf6,0x6c,0xa6,0x6a,0xc1,0x4c,0xc6,0x69,0x5f,0x5e,0x2b,0x1f,0xe5,0xf9,0xf3,0x9f,0x64,0x55,0xba,0xb6,0x97,0x14,0xe6,0x5,0xca,0xd7,0xd8,0x7d,0xb0,0x0,0xbc,0x47,0x3a,0x15,0x79,0xda,0xed,0x95,0xaa,0xef,0xbe,0x6,0x8,0x76,0xc0,0x8,0x5b,0x88,0x40,0xf3,0xc,0x13,0xd2,0x6c,0x6,0x42,0x7b,0x22,0x61,0xb8,0xdb,0x97,0x8f,0x35,0x3d,0xc3,0x50,0x9e,0x7f,0xc4,0x14,0x41,0xc1,0xb,0xa7,0xf1,0x59,0x43,0x3b,0x19,0x56,0x15,0x25,0x2d,0xff,0x66,0xfb,0x8a,0x8e,0x7d,0xb3,0xcc,0xdd,0xb6,0x51,0xca,0x92,0x72,0x28,0xd7,0xf8,0x17,0xb1,0x65,0x7a,0xf5,0xeb,0x49,0x73,0xd9,0xab,0xb9,0x3,0x31,0x3a,0x67,0xa5,0x95,0x94,0xa2,0x54,0x0,0x6b,0xc2,0xf7,0x11,0xe9,0x1c,0x57,0x79,0xaa,0x21,0x6f,0x81,0x2,0xde,0x99,0xa6,0x48,0x2a,0x7e,0x8b,0x91,0xc6,0x76,0xe,0x87,0xe7,0xfe,0xc0,0x37,0x4a,0xd1,0xf6,0x98,0x1,0x84,0x77,0x5d,0xf9,0xa0,0x12,0x96,0xbe,0x93,0x9,0xa4,0x4b,0xaf,0x10,0x32,0x86,0x5e,0xda,0x70,0x74,0xed,0x64,0x68,0x63,0x7c,0x69,0xb4,0xc8,0xe1,0x34,0x78,0x1e,0x2f,0xef,0x58,0x46,0xe5,0x62,0xd6,0x9d,0xd5,0xdf,0x6d,0xbb,0x83,0xb0,0xbd,0xcb,0xb2,0xd3,0xe8,0x20,0x29,0x39,0x8c,0xe2,0xa8,0xa,0x2e,0xc7,0x6e,0xee,0xfa,0xba,0x2b,0x85,0x1a,0x89,0x7,0xdc,0x3c,0xd8,0x1b,0x6a,0xad,0x38,0xf4,0xfd,0x9f,0xd,0x18,0xbf,0x45,0x71,0x4,0x5,0x33,0x9c,0x16,0x9b,0x30,0xfc,0x36,0xac,0xc9,0x24,0x4d,0x44,0xae,0xe3,0x3f,0xf2,0x1f,0xce,0x4c,0xd0,0x47,0x53,0x55,0x26,0xa1,0xd4,0x75,0x9a,0x2c,0x52,0x5c,0xe4,0xb5,0xf0,0xcf,0xb7,0x80,0x23,0x4f,0x60,0x1d,0xe6,0x5a,0xea,0x27,0x82,0x8d,0x90,0x5f,0xbc,0x4e,0xcd,0xec,0xe0,0xf,0x3e,0xc5,0xa9,0xa3,0x1,0xf6,0xe7,0x7c,0xae,0xc0,0xb2,0x37,0xa7,0xbd,0x40,0xf0,0xb1,0x38,0xc8,0xd1,0x92,0x3f,0x99,0x7d,0x4,0x26,0x68,0xb0,0x6b,0x41,0x96,0xcf,0xa0,0x24,0xa5,0x88,0xa2,0xa3,0x62,0x94,0x5d,0x36,0xc1,0xf4,0x9d,0xef,0x35,0x8f,0xc,0x7,0x93,0x51,0x34,0xb7,0xaf,0xe8,0x7e,0x90,0x48,0x1c,0xdf,0x27,0x61,0x2a,0x9c,0x4f,0x59,0x17,0xcd,0x50,0xb8,0xbc,0x85,0x4b,0xeb,0xfa,0xd,0x75,0x60,0x2f,0x13,0x23,0xc9,0x1b,0x87,0x21,0x4c,0x53,0xdd,0xc3,0x45,0x7f,0x67,0x80,0xa4,0xfc,0x1e,0x44,0xce,0xe1,0x30,0x5a,0x4d,0x74,0x57,0x14,0xed,0x8e,0x6d,0x3e,0x76,0xbe,0x3a,0xc5,0xe4,0x25,0x22,0xf2,0xf7,0x77,0x91,0x3d,0x6f,0xc7,0xb9,0xa1,0xb,0x3,0x66,0xf5,0x49,0xa8,0xb6,0x81,0x79,0x15,0x2b,0x56,0x6c,0xd0,0x1a,0xac,0x6a,0x64,0x83,0xd2,0xf9,0xc6,0xda,0xfb,0x39,0xd6,0xf3,0x8,0x95,0x9f,0x11,0xdc,0xbb,0xb4,0x69,0xa6,0x78,0x8a,0x6,0xad,0x0,0xca,0xff,0x9a,0x7b,0x12,0x73,0x89,0x32,0x47,0x5,0x33,0x20,0xaa,0x71,0xe6,0x63,0x65,0x97,0x10,0x43,0xe2,0x98,0x72,0x9,0xd5,0x29,0xc4,0x7a,0xf8,0x18,0x3c,0x58,0xf1,0xcc,0xd8,0x1d,0x8c,0xde,0xe5,0x1f,0x16,0xba,0xf,0x9e,0xd4,0x9b,0x5c,0xc2,0xe,0xa9,0xcb,0x2e,0x3b,0x2c,0xb3,0x31,0xbf,0xa,0xea,0x2d,0xee,0x82,0x5f,0xd7,0xfe,0x4e,0x2,0x19,0x28,0x46,0xec,0xdb,0x42,0x5e,0x52,0x4a,0x55,0x5b,0xe9,0xb5,0x8d,0x8b,0x86,0x84,0xfd,0x6e,0xd9,0xd3,0x70,0xe0,0x54,0xe3,0xab,0x2c,0x80,0xd2,0x7a,0x9f,0x4f,0x4a,0xca,0xdb,0x48,0xf4,0x15,0x4,0x1c,0xb6,0xbe,0xea,0xa9,0x50,0x33,0x8d,0xe7,0xf0,0xc9,0x87,0x78,0x59,0x98,0xd0,0x83,0xcb,0x3,0x60,0x7e,0xf8,0xc2,0x3a,0x9c,0xf1,0xee,0xa3,0xf9,0x73,0x5c,0xda,0x3d,0x19,0x41,0x38,0xf6,0x56,0x47,0x70,0xed,0x5,0x1,0xae,0x9e,0x74,0xa6,0xb0,0xc8,0xdd,0x92,0xc3,0x2d,0xf5,0xa1,0x89,0xa,0x12,0x55,0x21,0xf2,0xe4,0xaa,0x62,0x9a,0xdc,0x97,0xe0,0x8b,0x7c,0x49,0x1f,0x1e,0xdf,0x29,0xb1,0xba,0x2e,0xec,0x20,0x52,0x88,0x32,0xb9,0x9b,0xd5,0xd,0x2f,0x82,0x24,0xc0,0x1d,0x99,0x18,0x35,0xd6,0xfc,0x2b,0x72,0x13,0x7d,0xf,0x8a,0xbc,0x4b,0x5a,0xc1,0xc,0x85,0x75,0x6c,0x1a,0x0,0xfd,0x4d,0x36,0x3b,0x39,0x40,0xe6,0x54,0x8,0x30,0x5d,0xe9,0x5e,0x16,0xd3,0x64,0x6e,0xcd,0xf3,0xbf,0xa4,0x95,0x3f,0xe2,0x6a,0x43,0xe3,0xef,0xf7,0xe8,0xfb,0x51,0x66,0xff,0x14,0x76,0x93,0x86,0x26,0xe1,0x7f,0xb3,0xb7,0x57,0x90,0x53,0x91,0xe,0x8c,0x2,0x71,0x65,0xa0,0x31,0xa5,0x81,0xe5,0x4c,0x7,0xb2,0x23,0x69,0x63,0x58,0xa2,0xab,0x2a,0xad,0xfe,0x5f,0xcc,0x5b,0xde,0xd8,0x94,0x79,0xc7,0x45,0x25,0xcf,0xb4,0x68,0x42,0x27,0xc6,0xaf,0xbb,0x10,0xbd,0x77,0xb8,0x8e,0x9d,0x17,0xce,0x34,0x8f,0xfa,0x4e,0xb5,0x28,0x22,0x67,0x46,0x84,0x6b,0xd4,0x1b,0xc5,0x37,0xac,0x61,0x6,0x9,0x96,0xeb,0xd1,0x6d,0xb,0x3c,0xc4,0xa8,0x3e,0x6f,0x44,0x7b,0xa7,0x11,0xd7,0xd9,0x7a,0x41,0xbb,0xb2,0x1e,0xab,0x3a,0x70,0xbc,0x98,0xfc,0x55,0x68,0x7c,0xb9,0x28,0x88,0x17,0x95,0x1b,0xae,0x4e,0x89,0x4a,0x3f,0xf8,0x66,0xaa,0xd,0x6f,0x8a,0x9f,0xe2,0x48,0x7f,0xe6,0xfa,0xf6,0xee,0xf1,0x26,0xfb,0x73,0x5a,0xea,0xa6,0xbd,0x8c,0xca,0x7d,0x77,0xd4,0x44,0xf0,0x47,0xf,0xff,0x4d,0x11,0x29,0x2f,0x22,0x20,0x59,0xbe,0x8,0xce,0xc0,0x27,0x76,0x5d,0x62,0x12,0x25,0xdd,0xb1,0x8f,0xf2,0xc8,0x74,0xb5,0x78,0x1f,0x10,0xcd,0x2,0xdc,0x2e,0x7e,0x5f,0x9d,0x72,0x57,0xac,0x31,0x3b,0xd7,0x2d,0x96,0xe3,0xa1,0x97,0x84,0xe,0xa2,0x9,0xa4,0x6e,0x5b,0x3e,0xdf,0xb6,0x3c,0xd6,0xad,0x71,0x8d,0x60,0xde,0x5c,0xd5,0x42,0xc7,0xc1,0x33,0xb4,0xe7,0x46,0xa9,0xd1,0xc4,0x8b,0xb7,0x87,0x6d,0xbf,0x69,0xf4,0x1c,0x18,0x21,0xef,0x4f,0x5e,0xc3,0x24,0x0,0x58,0xba,0xe0,0x6a,0x45,0x23,0x85,0xe8,0xf7,0x79,0x67,0xe1,0xdb,0xc9,0x9a,0xd2,0x1a,0x9e,0x61,0x40,0x81,0x94,0xfe,0xe9,0xd0,0xf3,0xb0,0x49,0x2a,0x1d,0x5,0xaf,0xa7,0xc2,0x51,0xed,0xc,0x86,0x56,0x53,0xd3,0x35,0x99,0xcb,0x63,0x3,0x19,0xe4,0x54,0x15,0x9c,0x6c,0x75,0xa5,0x52,0x43,0xd8,0xa,0x64,0x16,0x93,0xcf,0xe5,0x32,0x6b,0x4,0x80,0x1,0x2c,0x36,0x9b,0x3d,0xd9,0xa0,0x82,0xcc,0x14,0x39,0x4b,0x91,0x2b,0xa8,0xa3,0x37,0xf5,0x6,0x7,0xc6,0x30,0xf9,0x92,0x65,0x50,0x7b,0x83,0xc5,0x8e,0x38,0xeb,0xfd,0xb3,0x90,0x13,0xb,0x4c,0xda,0x34,0xec,0xb8,0xb8,0xd1,0x30,0x55,0x60,0xaa,0x7,0xac,0x0,0x8a,0x99,0xaf,0xed,0x98,0x23,0xd9,0x48,0xe9,0xba,0x3d,0xcf,0xc9,0x4c,0xdb,0x52,0xd0,0x6e,0x83,0x7f,0xa3,0xd8,0x32,0x7a,0xc6,0xfc,0x81,0xbf,0xd3,0x2b,0x1c,0x6c,0x53,0x78,0x29,0xce,0xc0,0x6,0xb0,0x35,0x3f,0xa2,0x59,0x7c,0x93,0x51,0x70,0x20,0xd2,0xc,0xc3,0x1e,0x11,0x76,0xbb,0x82,0xb3,0xa8,0xe4,0x54,0x7d,0xf5,0x28,0xff,0xe0,0xf8,0xf4,0xe8,0x71,0x46,0xec,0x57,0x2e,0x2c,0x21,0x27,0x1f,0x43,0xf1,0x1,0x49,0xfe,0x4a,0xda,0x79,0x73,0xc4,0x26,0xb7,0x72,0x66,0x5b,0xf2,0x96,0xb2,0x7e,0x34,0xa5,0x10,0xbc,0xb5,0x4f,0x74,0x91,0x84,0x61,0x3,0xa4,0x68,0xf6,0x31,0x44,0x87,0x40,0xa0,0x15,0x9b,0x19,0x86,0x5e,0x6b,0x9c,0xf7,0x3e,0xc8,0x9,0x8,0xfb,0x39,0xad,0xa6,0x25,0x9f,0x45,0x37,0xb6,0xe2,0x3a,0xd4,0x42,0x5,0x1d,0x9e,0xbd,0xf3,0xe5,0x36,0x80,0xcb,0x8d,0x75,0x9d,0x18,0x6a,0x4,0xd6,0x4d,0x5c,0xab,0x7b,0x62,0x92,0x1b,0x5a,0xea,0x17,0xd,0x1a,0xc2,0x8c,0xae,0xd7,0x33,0x95,0x38,0x22,0xf,0x8e,0xa,0x65,0x3c,0xeb,0xc1,0x24,0x47,0xbe,0xfd,0xde,0xe7,0xf0,0x9a,0x8f,0x4e,0x6f,0x90,0x14,0xdc,0x94,0xc7,0x6d,0xc5,0x97,0x3b,0xdd,0x5d,0x58,0x88,0x2,0xe3,0x5f,0xcc,0xa9,0xa1,0xb,0x13,0x50,0x41,0xe1,0x2f,0x16,0x12,0xfa,0x67,0xb1,0x63,0x89,0xb9,0x85,0xca,0xdf,0xa7,0xd5,0xef,0x69,0x77,0xf9,0xe6,0x8b,0x2d,0x4b,0x64,0xee,0xb4,0x56,0xe,0x2a,0xcd}; + +unsigned char table_s3[] = {0xdd,0xaa,0x28,0xa0,0xc5,0xb4,0x9c,0xe8,0xcd,0xb7,0xde,0xe2,0x1f,0xef,0x73,0xbe,0x72,0xf5,0x1,0x22,0x50,0xa8,0x4a,0x9a,0xf2,0x84,0x69,0x29,0x3c,0x79,0x67,0xe3,0xb3,0x56,0xe4,0x2c,0x55,0x26,0x5a,0x0,0xb6,0x60,0xa9,0x98,0x83,0xc4,0xd0,0x7c,0xd9,0x90,0x78,0xdb,0x25,0x96,0xee,0x49,0x61,0xfa,0xd6,0x14,0xe,0xf8,0x41,0xce,0x9,0x37,0x53,0x12,0x93,0x3d,0xb1,0x4,0xf7,0x82,0x35,0x9f,0xb0,0xe9,0xf,0x1b,0xab,0xc7,0xa6,0x7e,0x77,0xd2,0x63,0xd1,0xd3,0xb8,0x8e,0x38,0xc,0x40,0xd4,0xe0,0xfe,0x21,0x2b,0x30,0x16,0xa7,0xd5,0xbc,0xbd,0xeb,0x5f,0x65,0xfc,0x4c,0x2e,0x64,0x6,0x7,0x5e,0x3b,0x2f,0xba,0x39,0x7d,0xd8,0x88,0xf3,0x58,0x15,0x23,0x86,0xae,0x74,0xca,0x8,0x66,0x43,0xa2,0xc8,0xda,0xd7,0x46,0x19,0xe5,0xc6,0xcb,0x3,0xf6,0x91,0xcc,0x3e,0x2d,0x8c,0xcf,0x71,0x7a,0x62,0xc9,0x59,0x11,0x1a,0xc3,0x45,0x75,0x8f,0x9e,0x97,0xdc,0x54,0x4d,0x6e,0x44,0x81,0x99,0xa5,0xb9,0x70,0x13,0x34,0x24,0x95,0x5,0x1d,0x6d,0x7f,0x5b,0x94,0xac,0x3f,0xad,0xbb,0x31,0x51,0xdf,0x4f,0xa1,0x6b,0x80,0xbf,0x92,0x18,0xec,0xa4,0x42,0x68,0xaf,0xc1,0xe7,0xed,0x7b,0xe1,0x10,0x5c,0xc2,0x27,0x85,0xa,0x87,0x8b,0x33,0x5d,0xfb,0x9d,0xb2,0xea,0xf4,0x6f,0xb,0x8a,0x8d,0xfd,0x47,0x20,0x2,0xd,0x17,0xf0,0x4b,0xb5,0x2a,0x36,0x4e,0x52,0xe6,0x6a,0x57,0x76,0x89,0x1e,0x32,0x48,0x3a,0xa3,0x1c,0x9b,0xc0,0x6c,0xf1,0xf9,0xff,0xd3,0xcd,0x2c,0x48,0xdc,0x7a,0x95,0xba,0xa0,0x2d,0x14,0xac,0xe5,0x7b,0xa2,0x0,0x5c,0xca,0x37,0xc6,0x88,0x4f,0xc0,0xe6,0xcb,0x3f,0x65,0x83,0xa7,0x4c,0xb5,0x98,0xd6,0x4b,0xd8,0xde,0x3b,0x84,0xe7,0xbc,0x15,0x39,0x1d,0x6f,0x70,0x4d,0xae,0x51,0x69,0x11,0xc1,0x75,0x6c,0xd7,0xd,0x92,0x25,0x7,0x30,0x2a,0xaa,0xad,0x60,0xda,0xe4,0x3d,0x52,0x62,0xee,0x45,0x36,0x7e,0xe8,0xab,0x5d,0x56,0xeb,0xb6,0xa,0x19,0xec,0xe1,0xd1,0x24,0x61,0xf0,0xc2,0x3e,0x85,0x64,0xfd,0xef,0xed,0x53,0x41,0x2f,0xf8,0x76,0x86,0x68,0x8a,0x18,0x16,0x9c,0x7c,0x58,0x8b,0xb3,0x22,0xb2,0x4a,0x3a,0x34,0x57,0x3,0x13,0xbe,0xa6,0x9e,0x82,0x6a,0x73,0x63,0x49,0xb9,0xa8,0xfb,0xb0,0x67,0x2b,0xc7,0xf3,0x9f,0xf4,0x1f,0xa9,0xf5,0x50,0xf6,0x44,0xe0,0x8c,0x59,0x81,0xce,0x97,0x3c,0x28,0xa5,0xd0,0xb8,0x12,0x1a,0xb4,0x23,0x96,0x10,0x2e,0x35,0x74,0x4,0x32,0x89,0xa1,0xaf,0xff,0x7f,0xd4,0x9d,0x8,0x5a,0x1e,0x20,0x21,0x1c,0x79,0x6b,0xdb,0x43,0x9,0xcc,0x9a,0x42,0x78,0x80,0x31,0x9b,0xf2,0x6,0xd9,0x17,0xc,0x5e,0x1b,0xc4,0x40,0xa3,0xd5,0xe,0x4e,0x8f,0x77,0xbd,0x6d,0xd2,0x55,0x5,0x26,0xc8,0x38,0x99,0x54,0x90,0xea,0xc5,0xf9,0x93,0xe2,0xcf,0xbb,0x8d,0xfa,0x87,0xf,0xdf,0x29,0xe9,0x66,0xdd,0x46,0x33,0xf1,0xb1,0x2,0x6e,0xc9,0xb7,0xfe,0xfc,0x5f,0xe3,0xa4,0x5b,0xf7,0x47,0x91,0xbf,0x8e,0x1,0x72,0x27,0x7d,0x71,0x94,0xb,0xc3,0xb8,0x2d,0x7f,0x3b,0x5,0x4,0x39,0x5c,0x21,0x17,0xac,0x84,0x8a,0xda,0x5a,0xf1,0xa5,0x14,0xbe,0xd7,0x23,0xfc,0x32,0x29,0x4e,0xfe,0x66,0x2c,0xe9,0xbf,0x67,0x5d,0xd0,0x75,0xd3,0x61,0xc5,0xa9,0x7c,0xa4,0x42,0xe,0xe2,0xd6,0xba,0xd1,0x3a,0x8c,0x3f,0x91,0x6,0xb3,0x35,0xb,0x10,0x51,0xeb,0xb2,0x19,0xd,0x80,0xf5,0x9d,0x37,0x94,0x27,0x4b,0xec,0x92,0xdb,0xd9,0x7a,0xfa,0xc,0xcc,0x43,0xf8,0x63,0x16,0xd4,0x24,0x57,0x2,0x58,0x54,0xb1,0x2e,0xe6,0xc6,0x81,0x7e,0xd2,0x62,0xb4,0x9a,0xab,0xaa,0x52,0x98,0x48,0xf7,0x70,0x20,0x3,0x7b,0x3e,0xe1,0x65,0x86,0xf0,0x2b,0x6b,0xb6,0xc7,0xea,0x9e,0xa8,0xdf,0xa2,0x2a,0xed,0x1d,0xbc,0x71,0xb5,0xcf,0xe0,0xdc,0x30,0x1c,0x38,0x4a,0x55,0x68,0x8b,0x74,0xf3,0x6e,0xfd,0xfb,0x1e,0xa1,0xc2,0x99,0x0,0x22,0x15,0xf,0x8f,0x88,0x45,0xff,0x4c,0x34,0xe4,0x50,0x49,0xf2,0x28,0xb7,0x85,0x8,0x31,0x89,0xc0,0x5e,0x87,0x25,0xf6,0xe8,0x9,0x6d,0xf9,0x5f,0xb0,0x9f,0xee,0x1a,0x40,0xa6,0x82,0x69,0x90,0xbd,0x79,0xef,0x12,0xe3,0xad,0x6a,0xe5,0xc3,0x59,0x7d,0xae,0x96,0x7,0x97,0x6f,0x1f,0xdd,0x53,0xa3,0x4d,0xaf,0x3d,0x33,0xb9,0x4f,0x56,0x46,0x6c,0x9c,0x8d,0xde,0x95,0x11,0x72,0x26,0x36,0x9b,0x83,0xbb,0xa7,0xcd,0x8e,0x78,0x73,0xce,0x93,0x2f,0x3c,0xc1,0x18,0x77,0x47,0xcb,0x60,0x13,0x5b,0xa0,0x41,0xd8,0xca,0xc8,0x76,0x64,0xa,0xc9,0xc4,0xf4,0x1,0x44,0xd5,0xe7,0x1b,0x3d,0xe5,0x84,0xe8,0x92,0x20,0x91,0x34,0x7b,0xcd,0xfb,0x90,0xa3,0x97,0x3,0x4f,0x51,0x10,0x74,0x4a,0x47,0xf2,0x7e,0xd0,0xdc,0x76,0xc1,0xb4,0x58,0x4c,0xaa,0xf3,0x78,0x1d,0x44,0x45,0x3e,0x7a,0xf9,0x6c,0x1b,0xb0,0xcb,0x9b,0xed,0xc5,0x60,0x56,0x73,0x68,0x62,0xbd,0xff,0x96,0xe4,0x55,0x26,0x1c,0xa8,0xfe,0x27,0x6d,0xf,0xbf,0x61,0x42,0xb6,0x31,0xd9,0x9,0xeb,0x13,0x6a,0x2a,0xc7,0xb1,0xa0,0x24,0x3a,0x7f,0xe3,0x6b,0xe9,0x9e,0xab,0xdf,0xf7,0x86,0xa1,0x9d,0xf4,0x8e,0xfd,0x30,0xac,0x5c,0x98,0x3b,0xd3,0x9a,0xa,0xad,0xd5,0x66,0x57,0x95,0xb9,0x22,0x8d,0x2,0xbb,0x4d,0x6f,0xa7,0x15,0xf0,0x43,0x19,0x65,0x16,0xdb,0xea,0x23,0xf5,0x3f,0x93,0x87,0xc0,0xc6,0x64,0x81,0x1f,0x70,0xc8,0xc4,0x49,0xf1,0xde,0xb8,0x1e,0x48,0x2c,0xb7,0xa9,0xd1,0xfc,0xc3,0x28,0x1,0xe7,0xaf,0x5b,0xa4,0x82,0xec,0x2b,0x53,0xa2,0x38,0xae,0xca,0x35,0x14,0x29,0x79,0xb,0x71,0x5d,0x83,0xd8,0x5f,0xe0,0xbc,0xba,0xb2,0x2f,0x4,0xbe,0xce,0xc9,0x54,0x4e,0x41,0x63,0x69,0xf6,0x8,0xb3,0xa5,0x11,0xd,0x75,0x6e,0x7d,0x8f,0xd2,0x39,0x32,0x8c,0xcf,0x52,0x1a,0x8a,0x21,0x36,0x6,0x80,0x59,0x25,0x4b,0x89,0x37,0x99,0x8b,0xe1,0x0,0xa6,0x5a,0x5,0x94,0xb5,0x40,0x88,0x85,0x2e,0x5e,0x46,0xd6,0xef,0xd7,0x18,0x3c,0x72,0xf8,0xee,0x7c,0xe2,0xc,0x9c,0x12,0x9f,0xd4,0xdd,0xcc,0x7,0x2d,0xe,0x17,0xfa,0xe6,0xda,0xc2,0x67,0x77,0x50,0x33,0x1f,0xd8,0xb6,0x90,0x9a,0xc,0x96,0x67,0x1c,0xf7,0xc8,0xe5,0x6f,0x9b,0xd3,0x35,0x2a,0x8c,0xea,0xc5,0x9d,0x83,0x18,0x7c,0x2b,0xb5,0x50,0xf2,0x7d,0xf0,0xfc,0x44,0x87,0x3c,0xc2,0x5d,0x41,0x39,0x25,0x91,0xfd,0xfa,0x8a,0x30,0x57,0x75,0x7a,0x60,0xd4,0x6b,0xec,0xb7,0x1b,0x86,0x8e,0x88,0x1d,0x20,0x1,0xfe,0x69,0x45,0x3f,0x4d,0xa0,0x31,0x6e,0x92,0xb1,0xbc,0x74,0x81,0x3,0xbd,0x7f,0x11,0x34,0xd5,0xbf,0xad,0x15,0xbe,0x2e,0x66,0x6d,0xb4,0x32,0x2,0xe6,0xbb,0x49,0x5a,0xfb,0xb8,0x6,0xd,0xf6,0xee,0xd2,0xce,0x7,0x64,0x43,0x53,0xf8,0xe9,0xe0,0xab,0x23,0x3a,0x19,0x33,0x48,0xda,0xcc,0x46,0x26,0xa8,0x38,0xd6,0xe2,0x72,0x6a,0x1a,0x8,0x2c,0xe3,0xdb,0x80,0xf5,0x42,0xe8,0xc7,0x9e,0x78,0x6c,0x7e,0x40,0x24,0x65,0xe4,0x4a,0xc6,0x73,0xa4,0xcf,0xf9,0x4f,0x7b,0x37,0xa3,0x97,0xdc,0xb0,0xd1,0x9,0x0,0xa5,0x14,0xa6,0xca,0x9c,0x28,0x12,0x8b,0x3b,0x59,0x13,0x89,0x56,0x5c,0x47,0x61,0xd0,0xa2,0xcb,0xaf,0xff,0x84,0x2f,0x62,0x54,0xf1,0xd9,0x71,0x70,0x29,0x4c,0x58,0xcd,0x4e,0xa,0xba,0xc0,0xa9,0x95,0x68,0x98,0x4,0xc9,0xaa,0xdd,0x5f,0xd7,0xb2,0xc3,0xeb,0x9f,0x85,0xf3,0x1e,0x5e,0x4b,0xe,0x10,0x94,0x5,0x82,0x76,0x55,0x27,0xdf,0x3d,0xed,0xc1,0x17,0xde,0xef,0xf4,0xb3,0xa7,0xb,0xc4,0x21,0x93,0x5b,0x22,0x51,0x2d,0x77,0x16,0x8d,0xa1,0x63,0x79,0x8f,0x36,0xb9,0xae,0xe7,0xf,0xac,0x52,0xe1,0x99,0x3e,0xdb,0x80,0xe3,0x5c,0xb9,0xbf,0x2c,0xb1,0x36,0xc9,0x2a,0x17,0x8,0x7a,0x5e,0x72,0xf5,0x6a,0xb0,0xb,0x12,0xa6,0x76,0xe,0xbd,0x7,0xca,0xcd,0x4d,0x57,0x60,0x42,0xdd,0xf2,0x1d,0xbb,0x2f,0x4b,0xaa,0xb4,0x67,0xc5,0x1c,0x82,0xcb,0x73,0x4a,0xc7,0x81,0xa7,0x28,0xef,0xa1,0x50,0xad,0x3b,0xff,0xd2,0x2b,0xc0,0xe4,0x2,0x58,0xac,0xfb,0x71,0x7f,0xed,0xf,0xe1,0x11,0x9f,0x5d,0x2d,0xd5,0x45,0xd4,0xec,0x3f,0x1b,0xe5,0xf9,0xc1,0xd9,0x74,0x64,0x30,0x53,0xd7,0x9c,0xcf,0xde,0x2e,0x4,0x14,0xd,0x19,0x51,0x22,0x89,0x5,0x35,0x5a,0x83,0x7e,0x6d,0xd1,0x8c,0x31,0x3a,0xcc,0x8f,0x59,0xa5,0x97,0x6,0x43,0xb6,0x86,0x8b,0x48,0x26,0x34,0x8a,0x88,0x9a,0x3,0xe2,0xb3,0x18,0x98,0xc8,0xc6,0xee,0x55,0x63,0x1e,0x7b,0x46,0x47,0x79,0x3d,0x6f,0xfa,0x1f,0x25,0xfd,0xab,0x6e,0x24,0xbc,0xc,0x6b,0x70,0xbe,0x61,0x95,0xfc,0x56,0xe7,0xce,0x78,0x93,0xf8,0x94,0xa0,0x4c,0x0,0xe6,0x3e,0xeb,0x87,0x23,0x91,0x37,0x92,0x75,0xdf,0xb7,0xc2,0x4f,0x5b,0xf0,0xa9,0x13,0x52,0x49,0x77,0xf1,0x44,0xd3,0x7d,0x96,0x54,0x21,0xba,0x1,0x8e,0x4e,0xb8,0x38,0x9b,0x99,0xd0,0xae,0x9,0x65,0xd6,0xe9,0xd8,0xf6,0x20,0x90,0x3c,0xc3,0x84,0xa4,0x6c,0xf3,0x16,0x1a,0x40,0x15,0x66,0x29,0x69,0xb2,0xc4,0x27,0xa3,0x7c,0x39,0x41,0x62,0x32,0xb5,0xa,0xda,0x10,0xe8,0x9e,0xa2,0x8d,0xf7,0x33,0xfe,0x5f,0xaf,0x68,0xe0,0x9d,0xea,0xdc,0xa8,0x85,0xf4,0xa4,0x66,0x4a,0xd1,0x7e,0xf1,0x48,0xbe,0x6b,0xc8,0x20,0x69,0xf9,0x5e,0x26,0x95,0x28,0x19,0xd0,0x6,0xcc,0x60,0x74,0x33,0x9c,0x54,0xe6,0x3,0xb0,0xea,0x96,0xe5,0x99,0xd9,0x34,0x42,0x53,0xd7,0xc9,0x8c,0x92,0xb1,0x45,0xc2,0x2a,0xfa,0x18,0xe0,0x52,0x6e,0x7,0x7d,0xe,0xc3,0x5f,0xaf,0x10,0x98,0x1a,0x6d,0x58,0x2c,0x4,0x75,0xe8,0x43,0x38,0x68,0x1e,0x36,0x93,0xa5,0x8b,0xee,0xb7,0xb6,0xcd,0x89,0xa,0x9f,0xd5,0xef,0x5b,0xd,0xd4,0x9e,0xfc,0x4c,0x80,0x9b,0x91,0x4e,0xc,0x65,0x17,0xa6,0x88,0x3e,0x8,0x63,0x50,0x64,0xf0,0xbc,0xce,0x16,0x77,0x1b,0x61,0xd3,0x62,0xc7,0x2f,0x85,0x32,0x47,0xab,0xbf,0x59,0x0,0xa2,0xe3,0x87,0xb9,0xb4,0x1,0x8d,0x23,0x81,0xb,0x1d,0x8f,0x11,0xff,0x6f,0xe1,0xdd,0xad,0xb5,0x25,0x1c,0x24,0xeb,0xcf,0x9,0x15,0x29,0x31,0x94,0x84,0xa3,0xc0,0x6c,0x27,0x2e,0x3f,0xf4,0xde,0xfd,0xe4,0xa1,0xe9,0x79,0xd2,0xc5,0xf5,0x73,0xaa,0x9d,0x8e,0x7c,0x21,0xca,0xc1,0x7f,0x3c,0x55,0xa9,0xf6,0x67,0x46,0xb3,0x7b,0x76,0xd6,0xb8,0x7a,0xc4,0x6a,0x78,0x12,0xf3,0x70,0x2b,0xac,0x13,0x4f,0x49,0x41,0xdc,0x39,0xc6,0xe7,0xda,0x8a,0xf8,0x82,0xae,0x9a,0x5,0xfb,0x40,0x56,0xe2,0xfe,0x86,0xf7,0x4d,0x3d,0x3a,0xa7,0xbd,0xb2,0x90,0x2,0x2d,0x4b,0xed,0xbb,0xdf,0x44,0x5a,0x35,0x97,0x72,0xec,0x83,0x3b,0x37,0xba,0x57,0x71,0x1f,0xd8,0xa0,0x51,0xcb,0x5d,0x22,0xf,0x30,0xdb,0xf2,0x14,0x5c,0xa8,0x5,0xe3,0xab,0x5f,0xd5,0xf8,0xc7,0x2c,0x57,0xa6,0x3c,0xaa,0xa0,0x86,0xe8,0x2f,0x74,0xcc,0xc0,0x4d,0xc2,0x60,0x85,0x1b,0x4c,0x28,0xb3,0xad,0xf5,0xda,0xbc,0x1a,0x50,0x4a,0x45,0x67,0x0,0xba,0xca,0xcd,0xa1,0x15,0x9,0x71,0x6d,0xf2,0xc,0xb7,0x7d,0xf,0x75,0x59,0xce,0x31,0x10,0x2d,0xb8,0xbe,0xb6,0x2b,0x87,0xdc,0x5b,0xe4,0x9d,0x8f,0xe5,0x4,0x21,0x4f,0x8d,0x33,0xb1,0x44,0x8c,0x81,0xa2,0x5e,0x1,0x90,0x3d,0x36,0x88,0xcb,0x6a,0x79,0x8b,0xd6,0x32,0x2,0x84,0x5d,0x56,0x1e,0x8e,0x25,0x3,0x29,0xa,0x13,0x9b,0xd0,0xd9,0xc8,0x63,0x73,0x54,0x37,0xfe,0xe2,0xde,0xc6,0xeb,0xd3,0x1c,0x38,0x2a,0x5a,0x42,0xd2,0xe6,0x8,0x98,0x16,0x76,0xfc,0xea,0x78,0x43,0xf6,0x7a,0xd4,0x55,0x14,0x70,0x4e,0x5c,0x48,0xae,0xf7,0xd8,0x72,0xc5,0xb0,0x96,0x24,0x95,0x30,0x39,0xe1,0x80,0xec,0xa7,0x93,0x7,0x4b,0x7f,0xc9,0xff,0x94,0xfb,0x92,0xe0,0x51,0x77,0x6c,0x66,0xb9,0x23,0x69,0xb,0xbb,0x22,0x18,0xac,0xfa,0x3a,0x7e,0xfd,0x68,0x7c,0x19,0x40,0x41,0xe9,0xc1,0x64,0x52,0x1f,0xb4,0xcf,0x9f,0xaf,0xdb,0xf3,0x82,0xe7,0x6f,0xed,0x9a,0xf9,0x34,0xa8,0x58,0xa5,0x99,0xf0,0x8a,0xdd,0xd,0xef,0x17,0x65,0x46,0xb2,0x35,0xa4,0x20,0x3e,0x7b,0x6e,0x2e,0xc3,0xb5,0x47,0x1d,0x61,0x12,0x6b,0xa3,0x11,0xf4,0x3b,0x97,0x83,0xc4,0xdf,0xee,0x27,0xf1,0xe,0xa9,0xd1,0x62,0x9c,0x3f,0xd7,0x9e,0x89,0x6,0xbf,0x49,0x53,0x91,0xbd,0x26,0x86,0xf3,0x44,0xee,0xc1,0x98,0x7e,0x6a,0x78,0x46,0x22,0x63,0xe2,0x4c,0xc0,0x75,0xa2,0xc9,0xff,0x49,0x7d,0x31,0xa5,0x91,0xda,0xb6,0xd7,0xf,0x6,0xa3,0x12,0xa0,0xcc,0x9a,0x2e,0x14,0x8d,0x3d,0x5f,0x15,0x8f,0x50,0x5a,0x41,0x67,0xd6,0xa4,0xcd,0xa9,0xf9,0x82,0x29,0x64,0x52,0xf7,0xdf,0x77,0x76,0x2f,0x4a,0x5e,0xcb,0x48,0xc,0xbc,0xc6,0xaf,0x93,0x6e,0x9e,0x2,0xcf,0xac,0xdb,0x59,0xd1,0xb4,0xc5,0xed,0x99,0x83,0xf5,0x18,0x58,0x4d,0x8,0x16,0x92,0x3,0x84,0x70,0x53,0x21,0xd9,0x3b,0xeb,0xc7,0x11,0xd8,0xe9,0xf2,0xb5,0xa1,0xd,0xc2,0x27,0x95,0x5d,0x24,0x57,0x2b,0x71,0x10,0x8b,0xa7,0x65,0x7f,0x89,0x30,0xbf,0xa8,0xe1,0x9,0xaa,0x54,0xe7,0x9f,0x38,0x19,0xde,0xb0,0x96,0x9c,0xa,0x90,0x61,0x1a,0xf1,0xce,0xe3,0x69,0x9d,0xd5,0x33,0x2c,0x8a,0xec,0xc3,0x9b,0x85,0x1e,0x7a,0x2d,0xb3,0x56,0xf4,0x7b,0xf6,0xfa,0x42,0x81,0x3a,0xc4,0x5b,0x47,0x3f,0x23,0x97,0xfb,0xfc,0x8c,0x36,0x51,0x73,0x7c,0x66,0xd2,0x6d,0xea,0xb1,0x1d,0x80,0x88,0x8e,0x1b,0x26,0x7,0xf8,0x6f,0x43,0x39,0x4b,0xa6,0x37,0x68,0x94,0xb7,0xba,0x72,0x87,0x5,0xbb,0x79,0x17,0x32,0xd3,0xb9,0xab,0x13,0xb8,0x28,0x60,0x6b,0xb2,0x34,0x4,0xe0,0xbd,0x4f,0x5c,0xfd,0xbe,0x0,0xb,0xf0,0xe8,0xd4,0xc8,0x1,0x62,0x45,0x55,0xfe,0xef,0xe6,0xad,0x25,0x3c,0x1f,0x35,0x4e,0xdc,0xca,0x40,0x20,0xae,0x3e,0xd0,0xe4,0x74,0x6c,0x1c,0xe,0x2a,0xe5,0xdd,0x20,0xa8,0xd5,0xa2,0x94,0xe0,0xcd,0xbc,0xd6,0xea,0xc5,0xbf,0x7b,0xb6,0x17,0xe7,0x9,0x2a,0x7a,0xfd,0x42,0x92,0x58,0xa0,0x61,0x21,0xfa,0x8c,0x6f,0xeb,0x34,0x71,0xec,0x24,0xbb,0x5e,0x52,0x8,0x5d,0x2e,0xa1,0x90,0xbe,0x68,0xd8,0x74,0x8b,0xcc,0x70,0xd3,0xd1,0x98,0xe6,0x41,0x2d,0x9e,0xde,0x1c,0x69,0xf2,0x49,0xc6,0x6,0xf0,0x5b,0x1a,0x1,0x3f,0xb9,0xc,0x9b,0x35,0x3d,0x97,0xff,0x8a,0x7,0x13,0xb8,0xe1,0xae,0x76,0xa3,0xcf,0x6b,0xd9,0x7f,0xda,0x86,0x30,0xdb,0xb0,0xdc,0xe8,0x4,0x48,0x23,0x38,0xf6,0x29,0xdd,0xb4,0x1e,0xaf,0x57,0x6d,0xb5,0xe3,0x26,0x6c,0xf4,0x44,0x56,0x33,0xe,0xf,0x31,0x75,0x27,0xb2,0xfb,0x50,0xd0,0x80,0x8e,0xa6,0x1d,0x2b,0x0,0x6e,0x7c,0xc2,0xc0,0xd2,0x4b,0xaa,0x11,0xed,0xdf,0x4e,0xb,0xfe,0xce,0xc3,0x36,0x25,0x99,0xc4,0x79,0x72,0x84,0xc7,0x51,0x19,0x6a,0xc1,0x4d,0x7d,0x12,0xcb,0x9f,0xd4,0x87,0x96,0x66,0x4c,0x5c,0x45,0xad,0xb1,0x89,0x91,0x3c,0x2c,0x78,0x1b,0x15,0x65,0x9d,0xd,0x9c,0xa4,0x77,0x53,0xb3,0x39,0x37,0xa5,0x47,0xa9,0x59,0xd7,0xb7,0x9a,0x63,0x88,0xac,0x4a,0x10,0xe4,0xc9,0xef,0x60,0xa7,0xe9,0x18,0xe5,0x73,0x2f,0x8d,0x54,0xca,0x83,0x3b,0x2,0x8f,0x95,0xba,0x55,0xf3,0x67,0x3,0xe2,0xfc,0xf5,0x4f,0x82,0x85,0x5,0x1f,0x28,0xa,0xbd,0x22,0xf8,0x43,0x5a,0xee,0x3e,0x46,0x7e,0x81,0x62,0x5f,0x40,0x32,0x16,0x3a,0x93,0xc8,0xab,0x14,0xf1,0xf7,0x64,0xf9,0x4f,0xd0,0xa,0xb1,0xa8,0x1c,0xcc,0xb4,0x7,0xbd,0x70,0x77,0xf7,0xed,0xda,0xf8,0x61,0x3a,0x59,0xe6,0x3,0x5,0x96,0xb,0x8c,0x73,0x90,0xad,0xb2,0xc0,0xe4,0xc8,0x3b,0x1d,0x92,0x55,0x1b,0xea,0x17,0x81,0x45,0x68,0x91,0x7a,0x5e,0xb8,0xe2,0x16,0x67,0x48,0xa7,0x1,0x95,0xf1,0x10,0xe,0xdd,0x7f,0xa6,0x38,0x71,0xc9,0xf0,0x7d,0x5f,0x43,0x7b,0x63,0xce,0xde,0x8a,0xe9,0x6d,0x26,0x75,0x64,0x94,0xbe,0xae,0xb7,0x41,0xcb,0xc5,0x57,0xb5,0x5b,0xab,0x25,0xe7,0x97,0x6f,0xff,0x6e,0x56,0x85,0xa1,0xe3,0x1f,0x2d,0xbc,0xf9,0xc,0x3c,0x31,0xf2,0x9c,0x8e,0x30,0x32,0x20,0xb9,0x58,0xa3,0xeb,0x98,0x33,0xbf,0x8f,0xe0,0x39,0xc4,0xd7,0x6b,0x36,0x8b,0x80,0x76,0x35,0xa5,0x9f,0x47,0x11,0xd4,0x9e,0x6,0xb6,0xd1,0xca,0x4,0xdb,0x2f,0x46,0xec,0x5d,0x9,0xa2,0x22,0x72,0x7c,0x54,0xef,0xd9,0xa4,0xc1,0xfc,0xfd,0xc3,0x87,0xd5,0x40,0xcf,0x65,0xd,0x78,0xf5,0xe1,0x4a,0x13,0xa9,0xe8,0xf3,0xcd,0x4b,0xfe,0x69,0xc7,0x74,0xc2,0x29,0x42,0x2e,0x1a,0xf6,0xba,0x5c,0x84,0x51,0x3d,0x99,0x2b,0x8d,0x28,0x53,0x62,0x4c,0x9a,0x2a,0x86,0x79,0x3e,0x1e,0xd6,0x49,0xac,0xa0,0xfa,0xaf,0xdc,0x2c,0xee,0x9b,0x0,0xbb,0x34,0xf4,0x2,0x82,0x21,0x23,0x6a,0x14,0xb3,0xdf,0x6c,0x24,0x18,0x37,0x4d,0x89,0x44,0xe5,0x15,0xd2,0x5a,0x27,0x50,0x66,0x12,0x3f,0x4e,0x93,0xd3,0x8,0x7e,0x9d,0x19,0xc6,0x83,0xfb,0xd8,0x88,0xf,0xb0,0x60,0xaa,0x52,0x8,0xb2,0xc2,0xc5,0x58,0x42,0x4d,0x6f,0x65,0xfa,0x4,0xbf,0xa9,0x1d,0x1,0x79,0xc6,0x39,0x18,0x25,0x75,0x7,0x7d,0x51,0x8f,0xd4,0x53,0xec,0xb0,0xb6,0xbe,0x23,0xdd,0xf0,0xcf,0x24,0xd,0xeb,0xa3,0x57,0xa8,0x8e,0xe0,0x27,0x5f,0xae,0x34,0xa2,0xca,0x68,0x8d,0x13,0x7c,0xc4,0xc8,0x45,0xfd,0xd2,0xb4,0x12,0x44,0x20,0xbb,0xa5,0x93,0xd8,0xd1,0xc0,0xb,0x21,0x2,0x1b,0xf6,0xea,0xd6,0xce,0x6b,0x7b,0x5c,0x3f,0x22,0x52,0x4a,0xda,0xe3,0xdb,0x14,0x30,0x7e,0xf4,0xe2,0x70,0xee,0x0,0x90,0x1e,0x29,0x47,0x85,0x3b,0x95,0x87,0xed,0xc,0xaa,0x56,0x9,0x98,0xb9,0x4c,0x84,0x89,0x62,0x71,0x83,0xde,0x35,0x3e,0x80,0xc3,0x5e,0x16,0x86,0x2d,0x3a,0xa,0x8c,0x55,0x7f,0x64,0x6e,0xb1,0xf3,0x9a,0xe8,0x59,0x2a,0x10,0xa4,0xf2,0x2b,0x61,0x3,0xb3,0x74,0x11,0x48,0x49,0x32,0x76,0xf5,0x60,0x17,0xbc,0xc7,0x97,0xe1,0xc9,0x6c,0x5a,0x5d,0x1c,0x78,0x46,0x4b,0xfe,0x72,0xdc,0xd0,0x7a,0xcd,0xb8,0x54,0x40,0xa6,0xff,0x31,0xe9,0x88,0xe4,0x9e,0x2c,0x9d,0x38,0x77,0xc1,0xf7,0x9c,0xaf,0x9b,0xf,0x43,0x63,0xab,0x19,0xfc,0x4f,0x15,0x69,0x1a,0xd7,0xe6,0x2f,0xf9,0x33,0x9f,0x8b,0xcc,0x94,0x37,0xdf,0x96,0x6,0xa1,0xd9,0x6a,0x5b,0x99,0xb5,0x2e,0x81,0xe,0xb7,0x41,0xef,0x67,0xe5,0x92,0xa7,0xd3,0xfb,0x8a,0xad,0x91,0xf8,0x82,0xf1,0x3c,0xa0,0x50,0x6d,0x4e,0xba,0x3d,0xd5,0x5,0xe7,0x1f,0x66,0x26,0xcb,0xbd,0xac,0x28,0x36,0x73,0x61,0xdb,0x16,0x11,0x91,0x8b,0xbc,0x9e,0x29,0xb6,0x6c,0xd7,0xce,0x7a,0xaa,0xd2,0xea,0x15,0xf6,0xcb,0xd4,0xa6,0x82,0xae,0x7,0x5c,0x3f,0x80,0x65,0x63,0xf0,0x6d,0x23,0xe,0xf7,0x1c,0x38,0xde,0x84,0x70,0x5d,0x7b,0xf4,0x33,0x7d,0x8c,0x71,0xe7,0xbb,0x19,0xc0,0x5e,0x17,0xaf,0x96,0x1b,0x1,0x2e,0xc1,0x67,0xf3,0x97,0x76,0x68,0xb,0x40,0x13,0x2,0xf2,0xd8,0xc8,0xd1,0x39,0x25,0x1d,0x5,0xa8,0xb8,0xec,0x8f,0x81,0xf1,0x9,0x99,0x8,0x30,0xe3,0xc7,0x27,0xad,0xa3,0x31,0xd3,0x3d,0xcd,0x43,0x94,0xfa,0xe8,0x56,0x54,0x46,0xdf,0x3e,0x85,0x79,0x4b,0xda,0x9f,0x6a,0x5a,0x57,0xa2,0xb1,0xd,0x50,0xed,0xe6,0x10,0x53,0xc5,0x8d,0xfe,0x55,0xd9,0xe9,0x86,0x5f,0xb7,0xac,0x62,0xbd,0x49,0x20,0x8a,0x3b,0xc3,0xf9,0x21,0x77,0xb2,0xf8,0x60,0xd0,0xc2,0xa7,0x9a,0x9b,0xa5,0xe1,0xb3,0x26,0x6f,0xc4,0x44,0x14,0x1a,0x32,0x89,0xbf,0xcf,0x8e,0x95,0xab,0x2d,0x98,0xf,0xa1,0xa9,0x3,0x6b,0x1e,0x93,0x87,0x2c,0x75,0x3a,0xe2,0x37,0x5b,0xff,0x4d,0xeb,0x4e,0x12,0xa4,0x4f,0x24,0x48,0x7c,0x90,0xdc,0x78,0xb0,0x2f,0xca,0xc6,0x9c,0xc9,0xba,0x35,0x4,0x2a,0xfc,0x4c,0xe0,0x1f,0x58,0xe4,0x47,0x45,0xc,0x72,0xd5,0xb9,0xa,0x4a,0x88,0xfd,0x66,0xdd,0x52,0x92,0x64,0xb4,0x3c,0x41,0x36,0x0,0x74,0x59,0x28,0x42,0x7e,0x51,0x2b,0xef,0x22,0x83,0x73,0x9d,0xbe,0xee,0x69,0xd6,0x6,0xcc,0x34,0xf5,0xb5,0x6e,0x18,0xfb,0x7f,0xa0,0xe5,0x68,0xc6,0x4a,0xff,0xf2,0xcc,0xa8,0xe9,0x4b,0x12,0xf4,0xe0,0xc,0x79,0xce,0x64,0x8c,0x29,0x98,0x2a,0x50,0x3c,0x5d,0x85,0xf7,0xbb,0x2f,0x1b,0x28,0x43,0x75,0xc3,0xed,0x5c,0x2e,0x47,0x5,0xda,0xd0,0xcb,0x7,0xb7,0xd5,0x9f,0x46,0x10,0xa4,0x9e,0xd4,0x41,0xc2,0x86,0xfd,0xfc,0xa5,0xc0,0xee,0xd8,0x7d,0x55,0x23,0x73,0x8,0xa3,0x3e,0x4f,0x67,0x13,0x26,0x51,0xd3,0x5b,0xe4,0x14,0x88,0x45,0x36,0x4c,0x25,0x19,0xab,0x53,0xb1,0x61,0x89,0xe,0xfa,0xd9,0xc7,0x82,0x9c,0x18,0x9,0x7f,0x92,0xd2,0xae,0xdd,0xa1,0xfb,0x48,0xad,0x1f,0xd7,0x78,0x3f,0x2b,0x87,0x4d,0x9b,0x52,0x63,0xde,0x6d,0x15,0xb2,0x22,0x6b,0x83,0x20,0xf5,0x3,0xba,0x35,0x9a,0x1,0x2d,0xef,0xe3,0x17,0x5f,0xb9,0x90,0x7b,0x44,0x69,0x16,0x80,0x1a,0xeb,0x93,0x54,0x3a,0x1c,0xf1,0x7c,0x70,0xc8,0xa7,0x39,0xdc,0x7e,0x11,0xf,0x94,0xf0,0xa6,0x0,0x66,0x49,0xdb,0xf9,0xf6,0xec,0x71,0x76,0x6,0xbc,0xcd,0xb5,0xa9,0x1d,0xb,0xb0,0x4e,0xd1,0xe5,0xc9,0xb3,0xc1,0x91,0xac,0x8d,0x72,0x97,0xa,0x2,0x4,0x58,0xe7,0x60,0x3b,0xb8,0x59,0x33,0x21,0x8f,0x31,0xf3,0x9d,0x3d,0x30,0xf8,0xd,0x2c,0xbd,0xe2,0x1e,0x77,0x34,0x8a,0x81,0x6a,0x37,0xc5,0xd6,0xe1,0x38,0xbe,0x8e,0x99,0x32,0xa2,0xea,0xaf,0xb6,0x95,0xbf,0x74,0x65,0x6c,0x27,0x8b,0xe8,0xcf,0xdf,0x7a,0x62,0x5e,0x42,0x84,0xa0,0x6f,0x57,0x6e,0xfe,0xe6,0x96,0xaa,0x24,0xb4,0x5a,0xc4,0x56,0x40,0xca,0x50,0xc5,0x46,0x2,0x79,0x78,0x21,0x44,0x6a,0x5c,0xf9,0xd1,0xa7,0xf7,0x8c,0x27,0x69,0xd8,0xaa,0xc3,0x81,0x5e,0x54,0x4f,0x83,0x33,0x51,0x1b,0xc2,0x94,0x20,0x1a,0x8,0xad,0x1c,0xae,0xd4,0xb8,0xd9,0x1,0x73,0x3f,0xab,0x9f,0xac,0xc7,0xf1,0x47,0xec,0x42,0xce,0x7b,0x76,0x48,0x2c,0x6d,0xcf,0x96,0x70,0x64,0x88,0xfd,0x4a,0xe0,0x5a,0xe9,0x91,0x36,0xa6,0xef,0x7,0xa4,0x71,0x87,0x3e,0xb1,0x1e,0x85,0xa9,0x6b,0x2a,0x59,0x25,0x7f,0xcc,0x29,0x9b,0x53,0xfc,0xbb,0xaf,0x3,0xc9,0x1f,0xd6,0xe7,0x2f,0xd7,0x35,0xe5,0xd,0x8a,0x7e,0x5d,0x43,0x6,0x18,0x9c,0x8d,0xfb,0x16,0x56,0xba,0xcb,0xe3,0x97,0xa2,0xd5,0x57,0xdf,0x60,0x90,0xc,0xc1,0xb2,0xc8,0xa1,0x9d,0x61,0x4d,0x37,0x45,0x15,0x28,0x9,0xf6,0x13,0x8e,0x86,0x80,0xdc,0x63,0xe4,0xbf,0x5f,0x7d,0x72,0x68,0xf5,0xf2,0x82,0x38,0x49,0x31,0x2d,0x99,0x8f,0x34,0xca,0x55,0x75,0xf8,0xf4,0x4c,0x23,0xbd,0x58,0xfa,0x95,0x8b,0x10,0x74,0x22,0x84,0xe2,0xcd,0x67,0x93,0xdb,0x3d,0x14,0xff,0xc0,0xed,0x92,0x4,0x9e,0x6f,0x17,0xd0,0xbe,0x98,0x0,0x24,0xeb,0xd3,0xea,0x7a,0x62,0x12,0x2e,0xa0,0x30,0xde,0x40,0xd2,0xc4,0x4e,0x2b,0x32,0x11,0x3b,0xf0,0xe1,0xe8,0xa3,0xf,0x6c,0x4b,0x5b,0xfe,0xe6,0xda,0xc6,0xf3,0xb0,0xe,0x5,0xee,0xb3,0x41,0x52,0x65,0xbc,0x3a,0xa,0x1d,0xb6,0x26,0x6e,0x3c,0xdd,0xb7,0xa5,0xb,0xb5,0x77,0x19,0xb9,0xb4,0x7c,0x89,0xa8,0x39,0x66,0x9a,0x55,0x44,0x4d,0x6,0x8e,0x97,0xb4,0x9e,0x5b,0x43,0x7f,0x63,0xaa,0xc9,0xee,0xfe,0x4f,0xdf,0xc7,0xb7,0xa5,0x81,0x4e,0x76,0xe5,0x77,0x61,0xeb,0x8b,0x5,0x95,0x7b,0xae,0x10,0xd2,0xbc,0x99,0x78,0x12,0x0,0xd,0x9c,0xc3,0x3f,0x1c,0x11,0xd9,0x2c,0x4b,0x16,0xe4,0xf7,0x56,0x15,0xab,0xa0,0xb8,0x13,0x83,0xcb,0xc0,0x19,0x9f,0xaf,0x50,0x57,0x27,0x9d,0xfa,0xd8,0xd7,0xcd,0x2a,0x91,0x6f,0xf0,0xec,0x94,0x88,0x3c,0xb0,0x8d,0xac,0x53,0xc4,0xe8,0x92,0xe0,0x79,0xc6,0x41,0x1a,0xb6,0x2b,0x23,0x25,0xb1,0x5a,0x65,0x48,0xc2,0x36,0x7e,0x98,0xb2,0x75,0x1b,0x3d,0x37,0xa1,0x3b,0xca,0x86,0x18,0xfd,0x5f,0xd0,0x5d,0x51,0xe9,0x87,0x21,0x47,0x68,0x30,0x2e,0xb5,0xd1,0x69,0x8c,0x3e,0xf6,0x8f,0xfc,0x80,0xda,0x6c,0xba,0x73,0x42,0x59,0x1e,0xa,0xa6,0x3,0x4a,0xa2,0x1,0xff,0x4c,0x34,0x93,0xbb,0x20,0xc,0xce,0xd4,0x22,0x9b,0x14,0x7,0x70,0xf2,0x7a,0x1f,0x6e,0x46,0x32,0x17,0x6d,0x4,0x38,0xc5,0x35,0xa9,0x64,0xa8,0x2f,0xdb,0xf8,0x8a,0x72,0x90,0x40,0x28,0x5e,0xb3,0xf3,0xe6,0xa3,0xbd,0x39,0x24,0xfb,0xf1,0xea,0xcc,0x7d,0xf,0x66,0x67,0x31,0x85,0xbf,0x26,0x96,0xf4,0xbe,0xdc,0xdd,0x84,0xe1,0xf5,0x60,0xe3,0xa7,0x2,0x52,0x29,0x82,0xcf,0xf9,0x5c,0x74,0xd3,0xed,0x89,0xc8,0x49,0xe7,0x6b,0xde,0x2d,0x58,0xef,0x45,0x6a,0x33,0xd5,0xc1,0x71,0x1d,0x7c,0xa4,0xad,0x8,0xb9,0xb,0x9,0x62,0x54,0xe2,0xd6,0x9a,0xe,0x3a}; + +unsigned char table_s4[] = {0x5b,0x9c,0x13,0x35,0x8f,0x19,0xe4,0x15,0x74,0x9f,0x66,0x4b,0x18,0xec,0xb6,0x50,0xf,0xa9,0x46,0x69,0x0,0x1e,0xff,0x9b,0x36,0xa8,0x71,0xd3,0x73,0xfe,0xc7,0x7f,0xbf,0x4,0xde,0x41,0xba,0xc2,0x12,0xa6,0x79,0x7e,0xb3,0x9,0xf6,0xd4,0xe3,0xf9,0xe8,0x57,0x34,0x6f,0x5,0x98,0xb,0xd,0xa3,0x9e,0x7d,0x82,0xc6,0xea,0xce,0xbc,0xb2,0x23,0x11,0xed,0x3f,0x32,0x2,0xf7,0x3e,0x80,0x92,0xfc,0x56,0xb7,0x2e,0x3c,0x3d,0x96,0xe5,0xad,0x37,0xee,0x81,0xb1,0x38,0x65,0xd9,0xca,0x3b,0x78,0x8e,0x85,0x6d,0x75,0x4d,0x51,0xe7,0x84,0xd0,0xc0,0x6a,0x7b,0x28,0x63,0xb9,0xa0,0xb0,0x9a,0x59,0xcb,0xc5,0x4f,0x2b,0xa5,0x55,0xbb,0xf1,0x61,0x99,0xe9,0xaf,0x8b,0x58,0x60,0x76,0x3,0x6b,0xc1,0x1d,0x44,0xef,0xfb,0xc3,0xfd,0xe6,0xa7,0xc9,0x67,0xf0,0x45,0x4c,0x27,0xcc,0x7a,0xb4,0xf8,0x14,0x20,0x33,0x5f,0x8a,0x52,0x26,0x83,0x25,0x97,0x1f,0x49,0x91,0xab,0xb8,0x8,0x90,0xda,0xd5,0xa,0xc4,0xdf,0x53,0xe2,0x48,0x21,0x7c,0x2c,0xac,0x7,0xd7,0xe1,0x5a,0x72,0xf3,0xf2,0xcf,0xaa,0x4e,0xdb,0x89,0xcd,0x43,0x39,0x16,0x2a,0x1b,0xeb,0x4a,0x87,0x5e,0x29,0x54,0xdc,0x40,0x31,0x1c,0x68,0x70,0x6,0xdd,0x9d,0x8d,0xc8,0x17,0x93,0x1,0x86,0xd6,0xf5,0x5c,0xa4,0x6e,0xbe,0x94,0x42,0x6c,0x5d,0x30,0x77,0x88,0x24,0xa2,0x47,0xd8,0x10,0xd2,0xa1,0xf4,0xae,0xe,0x95,0xe0,0x22,0xc,0xfa,0x3a,0xb5,0x64,0x2d,0x2f,0x8c,0x62,0xd1,0xbd,0x1a,0x91,0x37,0xd8,0xf7,0x9e,0x80,0x61,0x5,0xa8,0x36,0xef,0x4d,0xed,0x60,0x59,0xe1,0xc5,0x2,0x8d,0xab,0x11,0x87,0x7a,0x8b,0xea,0x1,0xf8,0xd5,0x86,0x72,0x28,0xce,0x76,0xc9,0xaa,0xf1,0x9b,0x6,0x95,0x93,0x3d,0x0,0xe3,0x1c,0x58,0x74,0x50,0x22,0x21,0x9a,0x40,0xdf,0x24,0x5c,0x8c,0x38,0xe7,0xe0,0x2d,0x97,0x68,0x4a,0x7d,0x67,0xa3,0x8,0x7b,0x33,0xa9,0x70,0x1f,0x2f,0xa6,0xfb,0x47,0x54,0xa5,0xe6,0x10,0x1b,0x2c,0xbd,0x8f,0x73,0xa1,0xac,0x9c,0x69,0xa0,0x1e,0xc,0x62,0xc8,0x29,0xb0,0xa2,0xc7,0x55,0x5b,0xd1,0xb5,0x3b,0xcb,0x25,0x6f,0xff,0x7,0x77,0x31,0x15,0xc6,0xfe,0xf3,0xeb,0xd3,0xcf,0x79,0x1a,0x4e,0x5e,0xf4,0xe5,0xb6,0xfd,0x27,0x3e,0x2e,0x4,0xd2,0xb9,0x52,0xe4,0x2a,0x66,0x8a,0xbe,0xad,0xc1,0x14,0xcc,0xb8,0x1d,0xbb,0x9,0xe8,0x9d,0xf5,0x5f,0x83,0xda,0x71,0x65,0x5d,0x63,0x78,0x39,0x57,0xf9,0x6e,0xdb,0xe2,0xb2,0x32,0x99,0x49,0x7f,0xc4,0xec,0x6d,0x6c,0x51,0x34,0xd0,0x45,0x17,0x53,0x81,0xd7,0xf,0x35,0x26,0x96,0xe,0x44,0x4b,0x94,0x5a,0x41,0xcd,0x7c,0xd6,0xbf,0xee,0x98,0x43,0x3,0x13,0x56,0x89,0xd,0x9f,0x18,0x48,0x6b,0xc2,0x3a,0xf0,0x20,0xdd,0xa7,0x88,0xb4,0x85,0x75,0xd4,0x19,0xc0,0xb7,0xca,0x42,0xde,0xaf,0x82,0xf6,0x90,0xb,0x7e,0xbc,0x92,0x64,0xa4,0x2b,0xfa,0xb3,0xb1,0x12,0xfc,0x4f,0x23,0x84,0xa,0xdc,0xf2,0xc3,0xae,0xe9,0x16,0xba,0x3c,0xd9,0x46,0x8e,0x4c,0x3f,0x6a,0x30,0xc4,0xe9,0x10,0xfb,0xdf,0x39,0x63,0x97,0xba,0x9c,0x13,0xd4,0x9a,0x6b,0x96,0x0,0x5c,0xfe,0x27,0xb9,0xf0,0x48,0x71,0xfc,0xe6,0xc9,0x26,0x80,0x14,0x70,0x91,0x8f,0x86,0x3c,0xf1,0xf6,0x76,0x6c,0x5b,0x79,0xce,0x51,0x8b,0x30,0x29,0x9d,0x4d,0x35,0xd,0xf2,0x11,0x2c,0x33,0x41,0x65,0x49,0xe0,0xbb,0xd8,0x67,0x82,0x84,0x17,0x8a,0x73,0x1d,0xf,0xb1,0xb3,0xa1,0x38,0xd9,0x62,0x9e,0xac,0x3d,0x78,0x8d,0xbd,0xb0,0x45,0x56,0xea,0xb7,0xa,0x1,0xf7,0xb4,0x22,0x6a,0x19,0xb2,0x3e,0xe,0x61,0xb8,0xec,0xa7,0xf4,0xe5,0x15,0x3f,0x2f,0x36,0xde,0xc2,0xfa,0xe2,0x4f,0x5f,0xb,0x68,0x66,0x16,0xee,0x7e,0xef,0xd7,0x4,0x20,0xc0,0x4a,0x44,0xd6,0x34,0xda,0x2a,0xa4,0x28,0x69,0x72,0x4c,0xca,0x7f,0xe8,0x46,0x4e,0xe4,0x8c,0xf9,0x74,0x60,0xcb,0x92,0xdd,0x5,0xd0,0xbc,0x18,0xaa,0xc,0xa9,0xf5,0x43,0xa8,0xc3,0xaf,0x9b,0x77,0x3b,0x50,0x4b,0x85,0x5a,0xae,0xc7,0x6d,0xdc,0x24,0x1e,0xc6,0x90,0x55,0x1f,0x87,0x37,0x25,0x40,0x7d,0x7c,0x42,0x6,0x54,0xc1,0x88,0x23,0xa3,0xf3,0xfd,0xd5,0x6e,0x58,0x53,0xdb,0xa6,0xd1,0xe7,0x93,0xbe,0xcf,0xa5,0x99,0xb6,0xcc,0x8,0xc5,0x64,0x94,0x7a,0x59,0x9,0x8e,0x31,0xe1,0x2b,0xd3,0x12,0x52,0x89,0xff,0x1c,0x98,0x47,0x2,0x9f,0x57,0xc8,0x2d,0x21,0x7b,0x2e,0x5d,0xd2,0xe3,0xcd,0x1b,0xab,0x7,0xf8,0xbf,0x3,0xa0,0xa2,0xeb,0x95,0x32,0x5e,0xed,0xad,0x6f,0x1a,0x81,0x3a,0xb5,0x75,0x83,0xbc,0xba,0xb2,0x2f,0x83,0xd8,0x5f,0xe0,0x79,0xb,0x71,0x5d,0xca,0x35,0x14,0x29,0xa5,0x11,0xd,0x75,0x69,0xf6,0x8,0xb3,0x54,0x4e,0x41,0x63,0x4,0xbe,0xce,0xc9,0x48,0x2c,0xb7,0xa9,0xf1,0xde,0xb8,0x1e,0x70,0xc8,0xc4,0x49,0xc6,0x64,0x81,0x1f,0x53,0xa2,0x38,0xae,0xa4,0x82,0xec,0x2b,0x1,0xe7,0xaf,0x5b,0xd1,0xfc,0xc3,0x28,0xe2,0xc,0x9c,0x12,0x72,0xf8,0xee,0x7c,0xef,0xd7,0x18,0x3c,0x2e,0x5e,0x46,0xd6,0x67,0x77,0x50,0x33,0xfa,0xe6,0xda,0xc2,0x7,0x2d,0xe,0x17,0x9f,0xd4,0xdd,0xcc,0x36,0x6,0x80,0x59,0x52,0x1a,0x8a,0x21,0x39,0x32,0x8c,0xcf,0x6e,0x7d,0x8f,0xd2,0xb5,0x40,0x88,0x85,0xa6,0x5a,0x5,0x94,0x99,0x8b,0xe1,0x0,0x25,0x4b,0x89,0x37,0xed,0xc5,0x60,0x56,0x1b,0xb0,0xcb,0x9b,0x3e,0x7a,0xf9,0x6c,0x78,0x1d,0x44,0x45,0x27,0x6d,0xf,0xbf,0x26,0x1c,0xa8,0xfe,0xff,0x96,0xe4,0x55,0x73,0x68,0x62,0xbd,0xa3,0x97,0x3,0x4f,0x7b,0xcd,0xfb,0x90,0x92,0x20,0x91,0x34,0x3d,0xe5,0x84,0xe8,0x58,0x4c,0xaa,0xf3,0xdc,0x76,0xc1,0xb4,0x47,0xf2,0x7e,0xd0,0x51,0x10,0x74,0x4a,0x8d,0x2,0xbb,0x4d,0x57,0x95,0xb9,0x22,0xa,0xad,0xd5,0x66,0x98,0x3b,0xd3,0x9a,0x3f,0x93,0x87,0xc0,0xdb,0xea,0x23,0xf5,0x43,0x19,0x65,0x16,0x6f,0xa7,0x15,0xf0,0xa0,0x24,0x3a,0x7f,0x6a,0x2a,0xc7,0xb1,0xd9,0x9,0xeb,0x13,0x61,0x42,0xb6,0x31,0xfd,0x30,0xac,0x5c,0xa1,0x9d,0xf4,0x8e,0xab,0xdf,0xf7,0x86,0xe3,0x6b,0xe9,0x9e,0x8b,0x4c,0xc3,0xe5,0x5f,0xc9,0x34,0xc5,0xa4,0x4f,0xb6,0x9b,0xc8,0x3c,0x66,0x80,0xdf,0x79,0x96,0xb9,0xd0,0xce,0x2f,0x4b,0xe6,0x78,0xa1,0x3,0xa3,0x2e,0x17,0xaf,0x6f,0xd4,0xe,0x91,0x6a,0x12,0xc2,0x76,0xa9,0xae,0x63,0xd9,0x26,0x4,0x33,0x29,0x38,0x87,0xe4,0xbf,0xd5,0x48,0xdb,0xdd,0x73,0x4e,0xad,0x52,0x16,0x3a,0x1e,0x6c,0x62,0xf3,0xc1,0x3d,0xef,0xe2,0xd2,0x27,0xee,0x50,0x42,0x2c,0x86,0x67,0xfe,0xec,0xed,0x46,0x35,0x7d,0xe7,0x3e,0x51,0x61,0xe8,0xb5,0x9,0x1a,0xeb,0xa8,0x5e,0x55,0xbd,0xa5,0x9d,0x81,0x37,0x54,0x0,0x10,0xba,0xab,0xf8,0xb3,0x69,0x70,0x60,0x4a,0x89,0x1b,0x15,0x9f,0xfb,0x75,0x85,0x6b,0x21,0xb1,0x49,0x39,0x7f,0x5b,0x88,0xb0,0xa6,0xd3,0xbb,0x11,0xcd,0x94,0x3f,0x2b,0x13,0x2d,0x36,0x77,0x19,0xb7,0x20,0x95,0x9c,0xf7,0x1c,0xaa,0x64,0x28,0xc4,0xf0,0xe3,0x8f,0x5a,0x82,0xf6,0x53,0xf5,0x47,0xcf,0x99,0x41,0x7b,0x68,0xd8,0x40,0xa,0x5,0xda,0x14,0xf,0x83,0x32,0x98,0xf1,0xac,0xfc,0x7c,0xd7,0x7,0x31,0x8a,0xa2,0x23,0x22,0x1f,0x7a,0x9e,0xb,0x59,0x1d,0x93,0xe9,0xc6,0xfa,0xcb,0x3b,0x9a,0x57,0x8e,0xf9,0x84,0xc,0x90,0xe1,0xcc,0xb8,0xa0,0xd6,0xd,0x4d,0x5d,0x18,0xc7,0x43,0xd1,0x56,0x6,0x25,0x8c,0x74,0xbe,0x6e,0x44,0x92,0xbc,0x8d,0xe0,0xa7,0x58,0xf4,0x72,0x97,0x8,0xc0,0x2,0x71,0x24,0x7e,0xde,0x45,0x30,0xf2,0xdc,0x2a,0xea,0x65,0xb4,0xfd,0xff,0x5c,0xb2,0x1,0x6d,0xca,0x41,0xe7,0x8,0x27,0x4e,0x50,0xb1,0xd5,0x78,0xe6,0x3f,0x9d,0x3d,0xb0,0x89,0x31,0x15,0xd2,0x5d,0x7b,0xc1,0x57,0xaa,0x5b,0x3a,0xd1,0x28,0x5,0x56,0xa2,0xf8,0x1e,0xa6,0x19,0x7a,0x21,0x4b,0xd6,0x45,0x43,0xed,0xd0,0x33,0xcc,0x88,0xa4,0x80,0xf2,0xf1,0x4a,0x90,0xf,0xf4,0x8c,0x5c,0xe8,0x37,0x30,0xfd,0x47,0xb8,0x9a,0xad,0xb7,0x73,0xd8,0xab,0xe3,0x79,0xa0,0xcf,0xff,0x76,0x2b,0x97,0x84,0x75,0x36,0xc0,0xcb,0xfc,0x6d,0x5f,0xa3,0x71,0x7c,0x4c,0xb9,0x70,0xce,0xdc,0xb2,0x18,0xf9,0x60,0x72,0x17,0x85,0x8b,0x1,0x65,0xeb,0x1b,0xf5,0xbf,0x2f,0xd7,0xa7,0xe1,0xc5,0x16,0x2e,0x23,0x3b,0x3,0x1f,0xa9,0xca,0x9e,0x8e,0x24,0x35,0x66,0x2d,0xf7,0xee,0xfe,0xd4,0x2,0x69,0x82,0x34,0xfa,0xb6,0x5a,0x6e,0x7d,0x11,0xc4,0x1c,0x68,0xcd,0x6b,0xd9,0x38,0x4d,0x25,0x8f,0x53,0xa,0xa1,0xb5,0x8d,0xb3,0xa8,0xe9,0x87,0x29,0xbe,0xb,0x32,0x62,0xe2,0x49,0x99,0xaf,0x14,0x3c,0xbd,0xbc,0x81,0xe4,0x0,0x95,0xc7,0x83,0x51,0x7,0xdf,0xe5,0xf6,0x46,0xde,0x94,0x9b,0x44,0x8a,0x91,0x1d,0xac,0x6,0x6f,0x3e,0x48,0x93,0xd3,0xc3,0x86,0x59,0xdd,0x4f,0xc8,0x98,0xbb,0x12,0xea,0x20,0xf0,0xd,0x77,0x58,0x64,0x55,0xa5,0x4,0xc9,0x10,0x67,0x1a,0x92,0xe,0x7f,0x52,0x26,0x40,0xdb,0xae,0x6c,0x42,0xb4,0x74,0xfb,0x2a,0x63,0x61,0xc2,0x2c,0x9f,0xf3,0x54,0xda,0xc,0x22,0x13,0x7e,0x39,0xc6,0x6a,0xec,0x9,0x96,0x5e,0x9c,0xef,0xba,0xe0,0xb,0x26,0xdf,0x34,0x10,0xf6,0xac,0x58,0x75,0x53,0xdc,0x1b,0x55,0xa4,0x59,0xcf,0x93,0x31,0xe8,0x76,0x3f,0x87,0xbe,0x33,0x29,0x6,0xe9,0x4f,0xdb,0xbf,0x5e,0x40,0x49,0xf3,0x3e,0x39,0xb9,0xa3,0x94,0xb6,0x1,0x9e,0x44,0xff,0xe6,0x52,0x82,0xfa,0xc2,0x3d,0xde,0xe3,0xfc,0x8e,0xaa,0x86,0x2f,0x74,0x17,0xa8,0x4d,0x4b,0xd8,0x45,0xbc,0xd2,0xc0,0x7e,0x7c,0x6e,0xf7,0x16,0xad,0x51,0x63,0xf2,0xb7,0x42,0x72,0x7f,0x8a,0x99,0x25,0x78,0xc5,0xce,0x38,0x7b,0xed,0xa5,0xd6,0x7d,0xf1,0xc1,0xae,0x77,0x23,0x68,0x3b,0x2a,0xda,0xf0,0xe0,0xf9,0x11,0xd,0x35,0x2d,0x80,0x90,0xc4,0xa7,0xa9,0xd9,0x21,0xb1,0x20,0x18,0xcb,0xef,0xf,0x85,0x8b,0x19,0xfb,0x15,0xe5,0x6b,0xe7,0xa6,0xbd,0x83,0x5,0xb0,0x27,0x89,0x81,0x2b,0x43,0x36,0xbb,0xaf,0x4,0x5d,0x12,0xca,0x1f,0x73,0xd7,0x65,0xc3,0x66,0x3a,0x8c,0x67,0xc,0x60,0x54,0xb8,0xf4,0x9f,0x84,0x4a,0x95,0x61,0x8,0xa2,0x13,0xeb,0xd1,0x9,0x5f,0x9a,0xd0,0x48,0xf8,0xea,0x8f,0xb2,0xb3,0x8d,0xc9,0x9b,0xe,0x47,0xec,0x6c,0x3c,0x32,0x1a,0xa1,0x97,0x9c,0x14,0x69,0x1e,0x28,0x5c,0x71,0x0,0x6a,0x56,0x79,0x3,0xc7,0xa,0xab,0x5b,0xb5,0x96,0xc6,0x41,0xfe,0x2e,0xe4,0x1c,0xdd,0x9d,0x46,0x30,0xd3,0x57,0x88,0xcd,0x50,0x98,0x7,0xe2,0xee,0xb4,0xe1,0x92,0x1d,0x2c,0x2,0xd4,0x64,0xc8,0x37,0x70,0xcc,0x6f,0x6d,0x24,0x5a,0xfd,0x91,0x22,0x62,0xa0,0xd5,0x4e,0xf5,0x7a,0xba,0x4c,0x3d,0x3b,0x33,0xae,0x2,0x59,0xde,0x61,0xf8,0x8a,0xf0,0xdc,0x4b,0xb4,0x95,0xa8,0x24,0x90,0x8c,0xf4,0xe8,0x77,0x89,0x32,0xd5,0xcf,0xc0,0xe2,0x85,0x3f,0x4f,0x48,0xc9,0xad,0x36,0x28,0x70,0x5f,0x39,0x9f,0xf1,0x49,0x45,0xc8,0x47,0xe5,0x0,0x9e,0xd2,0x23,0xb9,0x2f,0x25,0x3,0x6d,0xaa,0x80,0x66,0x2e,0xda,0x50,0x7d,0x42,0xa9,0x63,0x8d,0x1d,0x93,0xf3,0x79,0x6f,0xfd,0x6e,0x56,0x99,0xbd,0xaf,0xdf,0xc7,0x57,0xe6,0xf6,0xd1,0xb2,0x7b,0x67,0x5b,0x43,0x86,0xac,0x8f,0x96,0x1e,0x55,0x5c,0x4d,0xb7,0x87,0x1,0xd8,0xd3,0x9b,0xb,0xa0,0xb8,0xb3,0xd,0x4e,0xef,0xfc,0xe,0x53,0x34,0xc1,0x9,0x4,0x27,0xdb,0x84,0x15,0x18,0xa,0x60,0x81,0xa4,0xca,0x8,0xb6,0x6c,0x44,0xe1,0xd7,0x9a,0x31,0x4a,0x1a,0xbf,0xfb,0x78,0xed,0xf9,0x9c,0xc5,0xc4,0xa6,0xec,0x8e,0x3e,0xa7,0x9d,0x29,0x7f,0x7e,0x17,0x65,0xd4,0xf2,0xe9,0xe3,0x3c,0x22,0x16,0x82,0xce,0xfa,0x4c,0x7a,0x11,0x13,0xa1,0x10,0xb5,0xbc,0x64,0x5,0x69,0xd9,0xcd,0x2b,0x72,0x5d,0xf7,0x40,0x35,0xc6,0x73,0xff,0x51,0xd0,0x91,0xf5,0xcb,0xc,0x83,0x3a,0xcc,0xd6,0x14,0x38,0xa3,0x8b,0x2c,0x54,0xe7,0x19,0xba,0x52,0x1b,0xbe,0x12,0x6,0x41,0x5a,0x6b,0xa2,0x74,0xc2,0x98,0xe4,0x97,0xee,0x26,0x94,0x71,0x21,0xa5,0xbb,0xfe,0xeb,0xab,0x46,0x30,0x58,0x88,0x6a,0x92,0xe0,0xc3,0x37,0xb0,0x7c,0xb1,0x2d,0xdd,0x20,0x1c,0x75,0xf,0x2a,0x5e,0x76,0x7,0x62,0xea,0x68,0x1f,0x60,0xa7,0x28,0xe,0xb4,0x22,0xdf,0x2e,0x4f,0xa4,0x5d,0x70,0x23,0xd7,0x8d,0x6b,0x34,0x92,0x7d,0x52,0x3b,0x25,0xc4,0xa0,0xd,0x93,0x4a,0xe8,0x48,0xc5,0xfc,0x44,0x84,0x3f,0xe5,0x7a,0x81,0xf9,0x29,0x9d,0x42,0x45,0x88,0x32,0xcd,0xef,0xd8,0xc2,0xd3,0x6c,0xf,0x54,0x3e,0xa3,0x30,0x36,0x98,0xa5,0x46,0xb9,0xfd,0xd1,0xf5,0x87,0x89,0x18,0x2a,0xd6,0x4,0x9,0x39,0xcc,0x5,0xbb,0xa9,0xc7,0x6d,0x8c,0x15,0x7,0x6,0xad,0xde,0x96,0xc,0xd5,0xba,0x8a,0x3,0x5e,0xe2,0xf1,0x0,0x43,0xb5,0xbe,0x56,0x4e,0x76,0x6a,0xdc,0xbf,0xeb,0xfb,0x51,0x40,0x13,0x58,0x82,0x9b,0x8b,0xa1,0x62,0xf0,0xfe,0x74,0x10,0x9e,0x6e,0x80,0xca,0x5a,0xa2,0xd2,0x94,0xb0,0x63,0x5b,0x4d,0x38,0x50,0xfa,0x26,0x7f,0xd4,0xc0,0xf8,0xc6,0xdd,0x9c,0xf2,0x5c,0xcb,0x7e,0x77,0x1c,0xf7,0x41,0x8f,0xc3,0x2f,0x1b,0x8,0x64,0xb1,0x69,0x1d,0xb8,0x1e,0xac,0x24,0x72,0xaa,0x90,0x83,0x33,0xab,0xe1,0xee,0x31,0xff,0xe4,0x68,0xd9,0x73,0x1a,0x47,0x17,0x97,0x3c,0xec,0xda,0x61,0x49,0xc8,0xc9,0xf4,0x91,0x75,0xe0,0xb2,0xf6,0x78,0x2,0x2d,0x11,0x20,0xd0,0x71,0xbc,0x65,0x12,0x6f,0xe7,0x7b,0xa,0x27,0x53,0x4b,0x3d,0xe6,0xa6,0xb6,0xf3,0x2c,0xa8,0x3a,0xbd,0xed,0xce,0x67,0x9f,0x55,0x85,0xaf,0x79,0x57,0x66,0xb,0x4c,0xb3,0x1f,0x99,0x7c,0xe3,0x2b,0xe9,0x9a,0xcf,0x95,0x35,0xae,0xdb,0x19,0x37,0xc1,0x1,0x8e,0x5f,0x16,0x14,0xb7,0x59,0xea,0x86,0x21,0xaa,0xc,0xe3,0xcc,0xa5,0xbb,0x5a,0x3e,0x93,0xd,0xd4,0x76,0xd6,0x5b,0x62,0xda,0xfe,0x39,0xb6,0x90,0x2a,0xbc,0x41,0xb0,0xd1,0x3a,0xc3,0xee,0xbd,0x49,0x13,0xf5,0x4d,0xf2,0x91,0xca,0xa0,0x3d,0xae,0xa8,0x6,0x3b,0xd8,0x27,0x63,0x4f,0x6b,0x19,0x1a,0xa1,0x7b,0xe4,0x1f,0x67,0xb7,0x3,0xdc,0xdb,0x16,0xac,0x53,0x71,0x46,0x5c,0x98,0x33,0x40,0x8,0x92,0x4b,0x24,0x14,0x9d,0xc0,0x7c,0x6f,0x9e,0xdd,0x2b,0x20,0x17,0x86,0xb4,0x48,0x9a,0x97,0xa7,0x52,0x9b,0x25,0x37,0x59,0xf3,0x12,0x8b,0x99,0xfc,0x6e,0x60,0xea,0x8e,0x0,0xf0,0x1e,0x54,0xc4,0x3c,0x4c,0xa,0x2e,0xfd,0xc5,0xc8,0xd0,0xe8,0xf4,0x42,0x21,0x75,0x65,0xcf,0xde,0x8d,0xc6,0x1c,0x5,0x15,0x3f,0xe9,0x82,0x69,0xdf,0x11,0x5d,0xb1,0x85,0x96,0xfa,0x2f,0xf7,0x83,0x26,0x80,0x32,0xd3,0xa6,0xce,0x64,0xb8,0xe1,0x4a,0x5e,0x66,0x58,0x43,0x2,0x6c,0xc2,0x55,0xe0,0xd9,0x89,0x9,0xa2,0x72,0x44,0xff,0xd7,0x56,0x57,0x6a,0xf,0xeb,0x7e,0x2c,0x68,0xba,0xec,0x34,0xe,0x1d,0xad,0x35,0x7f,0x70,0xaf,0x61,0x7a,0xf6,0x47,0xed,0x84,0xd5,0xa3,0x78,0x38,0x28,0x6d,0xb2,0x36,0xa4,0x23,0x73,0x50,0xf9,0x1,0xcb,0x1b,0xe6,0x9c,0xb3,0x8f,0xbe,0x4e,0xef,0x22,0xfb,0x8c,0xf1,0x79,0xe5,0x94,0xb9,0xcd,0xab,0x30,0x45,0x87,0xa9,0x5f,0x9f,0x10,0xc1,0x88,0x8a,0x29,0xc7,0x74,0x18,0xbf,0x31,0xe7,0xc9,0xf8,0x95,0xd2,0x2d,0x81,0x7,0xe2,0x7d,0xb5,0x77,0x4,0x51,0xb,0xf3,0xde,0x27,0xcc,0xe8,0xe,0x54,0xa0,0x8d,0xab,0x24,0xe3,0xad,0x5c,0xa1,0x37,0x6b,0xc9,0x10,0x8e,0xc7,0x7f,0x46,0xcb,0xd1,0xfe,0x11,0xb7,0x23,0x47,0xa6,0xb8,0xb1,0xb,0xc6,0xc1,0x41,0x5b,0x6c,0x4e,0xf9,0x66,0xbc,0x7,0x1e,0xaa,0x7a,0x2,0x3a,0xc5,0x26,0x1b,0x4,0x76,0x52,0x7e,0xd7,0x8c,0xef,0x50,0xb5,0xb3,0x20,0xbd,0x44,0x2a,0x38,0x86,0x84,0x96,0xf,0xee,0x55,0xa9,0x9b,0xa,0x4f,0xba,0x8a,0x87,0x72,0x61,0xdd,0x80,0x3d,0x36,0xc0,0x83,0x15,0x5d,0x2e,0x85,0x9,0x39,0x56,0x8f,0xdb,0x90,0xc3,0xd2,0x22,0x8,0x18,0x1,0xe9,0xf5,0xcd,0xd5,0x78,0x68,0x3c,0x5f,0x51,0x21,0xd9,0x49,0xd8,0xe0,0x33,0x17,0xf7,0x7d,0x73,0xe1,0x3,0xed,0x1d,0x93,0x1f,0x5e,0x45,0x7b,0xfd,0x48,0xdf,0x71,0x79,0xd3,0xbb,0xce,0x43,0x57,0xfc,0xa5,0xea,0x32,0xe7,0x8b,0x2f,0x9d,0x3b,0x9e,0xc2,0x74,0x9f,0xf4,0x98,0xac,0x40,0xc,0x67,0x7c,0xb2,0x6d,0x99,0xf0,0x5a,0xeb,0x13,0x29,0xf1,0xa7,0x62,0x28,0xb0,0x0,0x12,0x77,0x4a,0x4b,0x75,0x31,0x63,0xf6,0xbf,0x14,0x94,0xc4,0xca,0xe2,0x59,0x6f,0x64,0xec,0x91,0xe6,0xd0,0xa4,0x89,0xf8,0x92,0xae,0x81,0xfb,0x3f,0xf2,0x53,0xa3,0x4d,0x6e,0x3e,0xb9,0x6,0xd6,0x1c,0xe4,0x25,0x65,0xbe,0xc8,0x2b,0xaf,0x70,0x35,0xa8,0x60,0xff,0x1a,0x16,0x4c,0x19,0x6a,0xe5,0xd4,0xfa,0x2c,0x9c,0x30,0xcf,0x88,0x34,0x97,0x95,0xdc,0xa2,0x5,0x69,0xda,0x9a,0x58,0x2d,0xb6,0xd,0x82,0x42,0xb4,0xb,0xd,0x5,0x98,0x34,0x6f,0xe8,0x57,0xce,0xbc,0xc6,0xea,0x7d,0x82,0xa3,0x9e,0x12,0xa6,0xba,0xc2,0xde,0x41,0xbf,0x4,0xe3,0xf9,0xf6,0xd4,0xb3,0x9,0x79,0x7e,0xff,0x9b,0x0,0x1e,0x46,0x69,0xf,0xa9,0xc7,0x7f,0x73,0xfe,0x71,0xd3,0x36,0xa8,0xe4,0x15,0x8f,0x19,0x13,0x35,0x5b,0x9c,0xb6,0x50,0x18,0xec,0x66,0x4b,0x74,0x9f,0x55,0xbb,0x2b,0xa5,0xc5,0x4f,0x59,0xcb,0x58,0x60,0xaf,0x8b,0x99,0xe9,0xf1,0x61,0xd0,0xc0,0xe7,0x84,0x4d,0x51,0x6d,0x75,0xb0,0x9a,0xb9,0xa0,0x28,0x63,0x6a,0x7b,0x81,0xb1,0x37,0xee,0xe5,0xad,0x3d,0x96,0x8e,0x85,0x3b,0x78,0xd9,0xca,0x38,0x65,0x2,0xf7,0x3f,0x32,0x11,0xed,0xb2,0x23,0x2e,0x3c,0x56,0xb7,0x92,0xfc,0x3e,0x80,0x5a,0x72,0xd7,0xe1,0xac,0x7,0x7c,0x2c,0x89,0xcd,0x4e,0xdb,0xcf,0xaa,0xf3,0xf2,0x90,0xda,0xb8,0x8,0x91,0xab,0x1f,0x49,0x48,0x21,0x53,0xe2,0xc4,0xdf,0xd5,0xa,0x14,0x20,0xb4,0xf8,0xcc,0x7a,0x4c,0x27,0x25,0x97,0x26,0x83,0x8a,0x52,0x33,0x5f,0xef,0xfb,0x1d,0x44,0x6b,0xc1,0x76,0x3,0xf0,0x45,0xc9,0x67,0xe6,0xa7,0xc3,0xfd,0x3a,0xb5,0xc,0xfa,0xe0,0x22,0xe,0x95,0xbd,0x1a,0x62,0xd1,0x2f,0x8c,0x64,0x2d,0x88,0x24,0x30,0x77,0x6c,0x5d,0x94,0x42,0xf4,0xae,0xd2,0xa1,0xd8,0x10,0xa2,0x47,0x17,0x93,0x8d,0xc8,0xdd,0x9d,0x70,0x6,0x6e,0xbe,0x5c,0xa4,0xd6,0xf5,0x1,0x86,0x4a,0x87,0x1b,0xeb,0x16,0x2a,0x43,0x39,0x1c,0x68,0x40,0x31,0x54,0xdc,0x5e,0x29,0x11,0xd6,0x59,0x7f,0xc5,0x53,0xae,0x5f,0x3e,0xd5,0x2c,0x1,0x52,0xa6,0xfc,0x1a,0x45,0xe3,0xc,0x23,0x4a,0x54,0xb5,0xd1,0x7c,0xe2,0x3b,0x99,0x39,0xb4,0x8d,0x35,0xf5,0x4e,0x94,0xb,0xf0,0x88,0x58,0xec,0x33,0x34,0xf9,0x43,0xbc,0x9e,0xa9,0xb3,0xa2,0x1d,0x7e,0x25,0x4f,0xd2,0x41,0x47,0xe9,0xd4,0x37,0xc8,0x8c,0xa0,0x84,0xf6,0xf8,0x69,0x5b,0xa7,0x75,0x78,0x48,0xbd,0x74,0xca,0xd8,0xb6,0x1c,0xfd,0x64,0x76,0x77,0xdc,0xaf,0xe7,0x7d,0xa4,0xcb,0xfb,0x72,0x2f,0x93,0x80,0x71,0x32,0xc4,0xcf,0x27,0x3f,0x7,0x1b,0xad,0xce,0x9a,0x8a,0x20,0x31,0x62,0x29,0xf3,0xea,0xfa,0xd0,0x13,0x81,0x8f,0x5,0x61,0xef,0x1f,0xf1,0xbb,0x2b,0xd3,0xa3,0xe5,0xc1,0x12,0x2a,0x3c,0x49,0x21,0x8b,0x57,0xe,0xa5,0xb1,0x89,0xb7,0xac,0xed,0x83,0x2d,0xba,0xf,0x6,0x6d,0x86,0x30,0xfe,0xb2,0x5e,0x6a,0x79,0x15,0xc0,0x18,0x6c,0xc9,0x6f,0xdd,0x55,0x3,0xdb,0xe1,0xf2,0x42,0xda,0x90,0x9f,0x40,0x8e,0x95,0x19,0xa8,0x2,0x6b,0x36,0x66,0xe6,0x4d,0x9d,0xab,0x10,0x38,0xb9,0xb8,0x85,0xe0,0x4,0x91,0xc3,0x87,0x9,0x73,0x5c,0x60,0x51,0xa1,0x0,0xcd,0x14,0x63,0x1e,0x96,0xa,0x7b,0x56,0x22,0x3a,0x4c,0x97,0xd7,0xc7,0x82,0x5d,0xd9,0x4b,0xcc,0x9c,0xbf,0x16,0xee,0x24,0xf4,0xde,0x8,0x26,0x17,0x7a,0x3d,0xc2,0x6e,0xe8,0xd,0x92,0x5a,0x98,0xeb,0xbe,0xe4,0x44,0xdf,0xaa,0x68,0x46,0xb0,0x70,0xff,0x2e,0x67,0x65,0xc6,0x28,0x9b,0xf7,0x50,0x2f,0x89,0x66,0x49,0x20,0x3e,0xdf,0xbb,0x16,0x88,0x51,0xf3,0x53,0xde,0xe7,0x5f,0x7b,0xbc,0x33,0x15,0xaf,0x39,0xc4,0x35,0x54,0xbf,0x46,0x6b,0x38,0xcc,0x96,0x70,0xc8,0x77,0x14,0x4f,0x25,0xb8,0x2b,0x2d,0x83,0xbe,0x5d,0xa2,0xe6,0xca,0xee,0x9c,0x9f,0x24,0xfe,0x61,0x9a,0xe2,0x32,0x86,0x59,0x5e,0x93,0x29,0xd6,0xf4,0xc3,0xd9,0x1d,0xb6,0xc5,0x8d,0x17,0xce,0xa1,0x91,0x18,0x45,0xf9,0xea,0x1b,0x58,0xae,0xa5,0x92,0x3,0x31,0xcd,0x1f,0x12,0x22,0xd7,0x1e,0xa0,0xb2,0xdc,0x76,0x97,0xe,0x1c,0x79,0xeb,0xe5,0x6f,0xb,0x85,0x75,0x9b,0xd1,0x41,0xb9,0xc9,0x8f,0xab,0x78,0x40,0x4d,0x55,0x6d,0x71,0xc7,0xa4,0xf0,0xe0,0x4a,0x5b,0x8,0x43,0x99,0x80,0x90,0xba,0x6c,0x7,0xec,0x5a,0x94,0xd8,0x34,0x0,0x13,0x7f,0xaa,0x72,0x6,0xa3,0x5,0xb7,0x56,0x23,0x4b,0xe1,0x3d,0x64,0xcf,0xdb,0xe3,0xdd,0xc6,0x87,0xe9,0x47,0xd0,0x65,0x5c,0xc,0x8c,0x27,0xf7,0xc1,0x7a,0x52,0xd3,0xd2,0xef,0x8a,0x6e,0xfb,0xa9,0xed,0x3f,0x69,0xb1,0x8b,0x98,0x28,0xb0,0xfa,0xf5,0x2a,0xe4,0xff,0x73,0xc2,0x68,0x1,0x50,0x26,0xfd,0xbd,0xad,0xe8,0x37,0xb3,0x21,0xa6,0xf6,0xd5,0x7c,0x84,0x4e,0x9e,0x63,0x19,0x36,0xa,0x3b,0xcb,0x6a,0xa7,0x7e,0x9,0x74,0xfc,0x60,0x11,0x3c,0x48,0x2e,0xb5,0xc0,0x2,0x2c,0xda,0x1a,0x95,0x44,0xd,0xf,0xac,0x42,0xf1,0x9d,0x3a,0xb4,0x62,0x4c,0x7d,0x10,0x57,0xa8,0x4,0x82,0x67,0xf8,0x30,0xf2,0x81,0xd4,0x8e,0x36,0x1b,0xe2,0x9,0x2d,0xcb,0x91,0x65,0x48,0x6e,0xe1,0x26,0x68,0x99,0x64,0xf2,0xae,0xc,0xd5,0x4b,0x2,0xba,0x83,0xe,0x14,0x3b,0xd4,0x72,0xe6,0x82,0x63,0x7d,0x74,0xce,0x3,0x4,0x84,0x9e,0xa9,0x8b,0x3c,0xa3,0x79,0xc2,0xdb,0x6f,0xbf,0xc7,0xff,0x0,0xe3,0xde,0xc1,0xb3,0x97,0xbb,0x12,0x49,0x2a,0x95,0x70,0x76,0xe5,0x78,0x81,0xef,0xfd,0x43,0x41,0x53,0xca,0x2b,0x90,0x6c,0x5e,0xcf,0x8a,0x7f,0x4f,0x42,0xb7,0xa4,0x18,0x45,0xf8,0xf3,0x5,0x46,0xd0,0x98,0xeb,0x40,0xcc,0xfc,0x93,0x4a,0x1e,0x55,0x6,0x17,0xe7,0xcd,0xdd,0xc4,0x2c,0x30,0x8,0x10,0xbd,0xad,0xf9,0x9a,0x94,0xe4,0x1c,0x8c,0x1d,0x25,0xf6,0xd2,0x32,0xb8,0xb6,0x24,0xc6,0x28,0xd8,0x56,0xda,0x9b,0x80,0xbe,0x38,0x8d,0x1a,0xb4,0xbc,0x16,0x7e,0xb,0x86,0x92,0x39,0x60,0x2f,0xf7,0x22,0x4e,0xea,0x58,0xfe,0x5b,0x7,0xb1,0x5a,0x31,0x5d,0x69,0x85,0xc9,0xa2,0xb9,0x77,0xa8,0x5c,0x35,0x9f,0x2e,0xd6,0xec,0x34,0x62,0xa7,0xed,0x75,0xc5,0xd7,0xb2,0x8f,0x8e,0xb0,0xf4,0xa6,0x33,0x7a,0xd1,0x51,0x1,0xf,0x27,0x9c,0xaa,0xa1,0x29,0x54,0x23,0x15,0x61,0x4c,0x3d,0x57,0x6b,0x44,0x3e,0xfa,0x37,0x96,0x66,0x88,0xab,0xfb,0x7c,0xc3,0x13,0xd9,0x21,0xe0,0xa0,0x7b,0xd,0xee,0x6a,0xb5,0xf0,0x6d,0xa5,0x3a,0xdf,0xd3,0x89,0xdc,0xaf,0x20,0x11,0x3f,0xe9,0x59,0xf5,0xa,0x4d,0xf1,0x52,0x50,0x19,0x67,0xc0,0xac,0x1f,0x5f,0x9d,0xe8,0x73,0xc8,0x47,0x87,0x71,0x85,0x83,0x8b,0x16,0xba,0xe1,0x66,0xd9,0x40,0x32,0x48,0x64,0xf3,0xc,0x2d,0x10,0x9c,0x28,0x34,0x4c,0x50,0xcf,0x31,0x8a,0x6d,0x77,0x78,0x5a,0x3d,0x87,0xf7,0xf0,0x71,0x15,0x8e,0x90,0xc8,0xe7,0x81,0x27,0x49,0xf1,0xfd,0x70,0xff,0x5d,0xb8,0x26,0x6a,0x9b,0x1,0x97,0x9d,0xbb,0xd5,0x12,0x38,0xde,0x96,0x62,0xe8,0xc5,0xfa,0x11,0xdb,0x35,0xa5,0x2b,0x4b,0xc1,0xd7,0x45,0xd6,0xee,0x21,0x5,0x17,0x67,0x7f,0xef,0x5e,0x4e,0x69,0xa,0xc3,0xdf,0xe3,0xfb,0x3e,0x14,0x37,0x2e,0xa6,0xed,0xe4,0xf5,0xf,0x3f,0xb9,0x60,0x6b,0x23,0xb3,0x18,0x0,0xb,0xb5,0xf6,0x57,0x44,0xb6,0xeb,0x8c,0x79,0xb1,0xbc,0x9f,0x63,0x3c,0xad,0xa0,0xb2,0xd8,0x39,0x1c,0x72,0xb0,0xe,0xd4,0xfc,0x59,0x6f,0x22,0x89,0xf2,0xa2,0x7,0x43,0xc0,0x55,0x41,0x24,0x7d,0x7c,0x1e,0x54,0x36,0x86,0x1f,0x25,0x91,0xc7,0xc6,0xaf,0xdd,0x6c,0x4a,0x51,0x5b,0x84,0x9a,0xae,0x3a,0x76,0x42,0xf4,0xc2,0xa9,0xab,0x19,0xa8,0xd,0x4,0xdc,0xbd,0xd1,0x61,0x75,0x93,0xca,0xe5,0x4f,0xf8,0x8d,0x7e,0xcb,0x47,0xe9,0x68,0x29,0x4d,0x73,0xb4,0x3b,0x82,0x74,0x6e,0xac,0x80,0x1b,0x33,0x94,0xec,0x5f,0xa1,0x2,0xea,0xa3,0x6,0xaa,0xbe,0xf9,0xe2,0xd3,0x1a,0xcc,0x7a,0x20,0x5c,0x2f,0x56,0x9e,0x2c,0xc9,0x99,0x1d,0x3,0x46,0x53,0x13,0xfe,0x88,0xe0,0x30,0xd2,0x2a,0x58,0x7b,0x8f,0x8,0xc4,0x9,0x95,0x65,0x98,0xa4,0xcd,0xb7,0x92,0xe6,0xce,0xbf,0xda,0x52,0xd0,0xa7,0x4f,0x88,0x7,0x21,0x9b,0xd,0xf0,0x1,0x60,0x8b,0x72,0x5f,0xc,0xf8,0xa2,0x44,0x1b,0xbd,0x52,0x7d,0x14,0xa,0xeb,0x8f,0x22,0xbc,0x65,0xc7,0x67,0xea,0xd3,0x6b,0xab,0x10,0xca,0x55,0xae,0xd6,0x6,0xb2,0x6d,0x6a,0xa7,0x1d,0xe2,0xc0,0xf7,0xed,0xfc,0x43,0x20,0x7b,0x11,0x8c,0x1f,0x19,0xb7,0x8a,0x69,0x96,0xd2,0xfe,0xda,0xa8,0xa6,0x37,0x5,0xf9,0x2b,0x26,0x16,0xe3,0x2a,0x94,0x86,0xe8,0x42,0xa3,0x3a,0x28,0x29,0x82,0xf1,0xb9,0x23,0xfa,0x95,0xa5,0x2c,0x71,0xcd,0xde,0x2f,0x6c,0x9a,0x91,0x79,0x61,0x59,0x45,0xf3,0x90,0xc4,0xd4,0x7e,0x6f,0x3c,0x77,0xad,0xb4,0xa4,0x8e,0x4d,0xdf,0xd1,0x5b,0x3f,0xb1,0x41,0xaf,0xe5,0x75,0x8d,0xfd,0xbb,0x9f,0x4c,0x74,0x62,0x17,0x7f,0xd5,0x9,0x50,0xfb,0xef,0xd7,0xe9,0xf2,0xb3,0xdd,0x73,0xe4,0x51,0x58,0x33,0xd8,0x6e,0xa0,0xec,0x0,0x34,0x27,0x4b,0x9e,0x46,0x32,0x97,0x31,0x83,0xb,0x5d,0x85,0xbf,0xac,0x1c,0x84,0xce,0xc1,0x1e,0xd0,0xcb,0x47,0xf6,0x5c,0x35,0x68,0x38,0xb8,0x13,0xc3,0xf5,0x4e,0x66,0xe7,0xe6,0xdb,0xbe,0x5a,0xcf,0x9d,0xd9,0x57,0x2d,0x2,0x3e,0xf,0xff,0x5e,0x93,0x4a,0x3d,0x40,0xc8,0x54,0x25,0x8,0x7c,0x64,0x12,0xc9,0x89,0x99,0xdc,0x3,0x87,0x15,0x92,0xc2,0xe1,0x48,0xb0,0x7a,0xaa,0x80,0x56,0x78,0x49,0x24,0x63,0x9c,0x30,0xb6,0x53,0xcc,0x4,0xc6,0xb5,0xe0,0xba,0x1a,0x81,0xf4,0x36,0x18,0xee,0x2e,0xa1,0x70,0x39,0x3b,0x98,0x76,0xc5,0xa9,0xe,0xe2,0x44,0xab,0x84,0xed,0xf3,0x12,0x76,0xdb,0x45,0x9c,0x3e,0x9e,0x13,0x2a,0x92,0xb6,0x71,0xfe,0xd8,0x62,0xf4,0x9,0xf8,0x99,0x72,0x8b,0xa6,0xf5,0x1,0x5b,0xbd,0x5,0xba,0xd9,0x82,0xe8,0x75,0xe6,0xe0,0x4e,0x73,0x90,0x6f,0x2b,0x7,0x23,0x51,0x52,0xe9,0x33,0xac,0x57,0x2f,0xff,0x4b,0x94,0x93,0x5e,0xe4,0x1b,0x39,0xe,0x14,0xd0,0x7b,0x8,0x40,0xda,0x3,0x6c,0x5c,0xd5,0x88,0x34,0x27,0xd6,0x95,0x63,0x68,0x5f,0xce,0xfc,0x0,0xd2,0xdf,0xef,0x1a,0xd3,0x6d,0x7f,0x11,0xbb,0x5a,0xc3,0xd1,0xb4,0x26,0x28,0xa2,0xc6,0x48,0xb8,0x56,0x1c,0x8c,0x74,0x4,0x42,0x66,0xb5,0x8d,0x80,0x98,0xa0,0xbc,0xa,0x69,0x3d,0x2d,0x87,0x96,0xc5,0x8e,0x54,0x4d,0x5d,0x77,0xa1,0xca,0x21,0x97,0x59,0x15,0xf9,0xcd,0xde,0xb2,0x67,0xbf,0xcb,0x6e,0xc8,0x7a,0x9b,0xee,0x86,0x2c,0xf0,0xa9,0x2,0x16,0x2e,0x10,0xb,0x4a,0x24,0x8a,0x1d,0xa8,0x91,0xc1,0x41,0xea,0x3a,0xc,0xb7,0x9f,0x1e,0x1f,0x22,0x47,0xa3,0x36,0x64,0x20,0xf2,0xa4,0x7c,0x46,0x55,0xe5,0x7d,0x37,0x38,0xe7,0x29,0x32,0xbe,0xf,0xa5,0xcc,0x9d,0xeb,0x30,0x70,0x60,0x25,0xfa,0x7e,0xec,0x6b,0x3b,0x18,0xb1,0x49,0x83,0x53,0xae,0xd4,0xfb,0xc7,0xf6,0x6,0xa7,0x6a,0xb3,0xc4,0xb9,0x31,0xad,0xdc,0xf1,0x85,0xe3,0x78,0xd,0xcf,0xe1,0x17,0xd7,0x58,0x89,0xc0,0xc2,0x61,0x8f,0x3c,0x50,0xf7,0x79,0xaf,0x81,0xb0,0xdd,0x9a,0x65,0xc9,0x4f,0xaa,0x35,0xfd,0x3f,0x4c,0x19,0x43,0xb9,0x94,0x6d,0x86,0xa2,0x44,0x1e,0xea,0xc7,0xe1,0x6e,0xa9,0xe7,0x16,0xeb,0x7d,0x21,0x83,0x5a,0xc4,0x8d,0x35,0xc,0x81,0x9b,0xb4,0x5b,0xfd,0x69,0xd,0xec,0xf2,0xfb,0x41,0x8c,0x8b,0xb,0x11,0x26,0x4,0xb3,0x2c,0xf6,0x4d,0x54,0xe0,0x30,0x48,0x70,0x8f,0x6c,0x51,0x4e,0x3c,0x18,0x34,0x9d,0xc6,0xa5,0x1a,0xff,0xf9,0x6a,0xf7,0xe,0x60,0x72,0xcc,0xce,0xdc,0x45,0xa4,0x1f,0xe3,0xd1,0x40,0x5,0xf0,0xc0,0xcd,0x38,0x2b,0x97,0xca,0x77,0x7c,0x8a,0xc9,0x5f,0x17,0x64,0xcf,0x43,0x73,0x1c,0xc5,0x91,0xda,0x89,0x98,0x68,0x42,0x52,0x4b,0xa3,0xbf,0x87,0x9f,0x32,0x22,0x76,0x15,0x1b,0x6b,0x93,0x3,0x92,0xaa,0x79,0x5d,0xbd,0x37,0x39,0xab,0x49,0xa7,0x57,0xd9,0x55,0x14,0xf,0x31,0xb7,0x2,0x95,0x3b,0x33,0x99,0xf1,0x84,0x9,0x1d,0xb6,0xef,0xa0,0x78,0xad,0xc1,0x65,0xd7,0x71,0xd4,0x88,0x3e,0xd5,0xbe,0xd2,0xe6,0xa,0x46,0x2d,0x36,0xf8,0x27,0xd3,0xba,0x10,0xa1,0x59,0x63,0xbb,0xed,0x28,0x62,0xfa,0x4a,0x58,0x3d,0x0,0x1,0x3f,0x7b,0x29,0xbc,0xf5,0x5e,0xde,0x8e,0x80,0xa8,0x13,0x25,0x2e,0xa6,0xdb,0xac,0x9a,0xee,0xc3,0xb2,0xd8,0xe4,0xcb,0xb1,0x75,0xb8,0x19,0xe9,0x7,0x24,0x74,0xf3,0x4c,0x9c,0x56,0xae,0x6f,0x2f,0xf4,0x82,0x61,0xe5,0x3a,0x7f,0xe2,0x2a,0xb5,0x50,0x5c,0x6,0x53,0x20,0xaf,0x9e,0xb0,0x66,0xd6,0x7a,0x85,0xc2,0x7e,0xdd,0xdf,0x96,0xe8,0x4f,0x23,0x90,0xd0,0x12,0x67,0xfc,0x47,0xc8,0x8,0xfe,0xb7,0xb1,0xb9,0x24,0x88,0xd3,0x54,0xeb,0x72,0x0,0x7a,0x56,0xc1,0x3e,0x1f,0x22,0xae,0x1a,0x6,0x7e,0x62,0xfd,0x3,0xb8,0x5f,0x45,0x4a,0x68,0xf,0xb5,0xc5,0xc2,0x43,0x27,0xbc,0xa2,0xfa,0xd5,0xb3,0x15,0x7b,0xc3,0xcf,0x42,0xcd,0x6f,0x8a,0x14,0x58,0xa9,0x33,0xa5,0xaf,0x89,0xe7,0x20,0xa,0xec,0xa4,0x50,0xda,0xf7,0xc8,0x23,0xe9,0x7,0x97,0x19,0x79,0xf3,0xe5,0x77,0xe4,0xdc,0x13,0x37,0x25,0x55,0x4d,0xdd,0x6c,0x7c,0x5b,0x38,0xf1,0xed,0xd1,0xc9,0xc,0x26,0x5,0x1c,0x94,0xdf,0xd6,0xc7,0x3d,0xd,0x8b,0x52,0x59,0x11,0x81,0x2a,0x32,0x39,0x87,0xc4,0x65,0x76,0x84,0xd9,0xbe,0x4b,0x83,0x8e,0xad,0x51,0xe,0x9f,0x92,0x80,0xea,0xb,0x2e,0x40,0x82,0x3c,0xe6,0xce,0x6b,0x5d,0x10,0xbb,0xc0,0x90,0x35,0x71,0xf2,0x67,0x73,0x16,0x4f,0x4e,0x2c,0x66,0x4,0xb4,0x2d,0x17,0xa3,0xf5,0xf4,0x9d,0xef,0x5e,0x78,0x63,0x69,0xb6,0xa8,0x9c,0x8,0x44,0x70,0xc6,0xf0,0x9b,0x99,0x2b,0x9a,0x3f,0x36,0xee,0x8f,0xe3,0x53,0x47,0xa1,0xf8,0xd7,0x7d,0xca,0xbf,0x4c,0xf9,0x75,0xdb,0x5a,0x1b,0x7f,0x41,0x86,0x9,0xb0,0x46,0x5c,0x9e,0xb2,0x29,0x1,0xa6,0xde,0x6d,0x93,0x30,0xd8,0x91,0x34,0x98,0x8c,0xcb,0xd0,0xe1,0x28,0xfe,0x48,0x12,0x6e,0x1d,0x64,0xac,0x1e,0xfb,0xab,0x2f,0x31,0x74,0x61,0x21,0xcc,0xba,0xd2,0x2,0xe0,0x18,0x6a,0x49,0xbd,0x3a,0xf6,0x3b,0xa7,0x57,0xaa,0x96,0xff,0x85,0xa0,0xd4,0xfc,0x8d,0xe8,0x60,0xe2,0x95,0x8d,0x4a,0xc5,0xe3,0x59,0xcf,0x32,0xc3,0xa2,0x49,0xb0,0x9d,0xce,0x3a,0x60,0x86,0xd9,0x7f,0x90,0xbf,0xd6,0xc8,0x29,0x4d,0xe0,0x7e,0xa7,0x5,0xa5,0x28,0x11,0xa9,0x69,0xd2,0x8,0x97,0x6c,0x14,0xc4,0x70,0xaf,0xa8,0x65,0xdf,0x20,0x2,0x35,0x2f,0x3e,0x81,0xe2,0xb9,0xd3,0x4e,0xdd,0xdb,0x75,0x48,0xab,0x54,0x10,0x3c,0x18,0x6a,0x64,0xf5,0xc7,0x3b,0xe9,0xe4,0xd4,0x21,0xe8,0x56,0x44,0x2a,0x80,0x61,0xf8,0xea,0xeb,0x40,0x33,0x7b,0xe1,0x38,0x57,0x67,0xee,0xb3,0xf,0x1c,0xed,0xae,0x58,0x53,0xbb,0xa3,0x9b,0x87,0x31,0x52,0x6,0x16,0xbc,0xad,0xfe,0xb5,0x6f,0x76,0x66,0x4c,0x8f,0x1d,0x13,0x99,0xfd,0x73,0x83,0x6d,0x27,0xb7,0x4f,0x3f,0x79,0x5d,0x8e,0xb6,0xa0,0xd5,0xbd,0x17,0xcb,0x92,0x39,0x2d,0x15,0x2b,0x30,0x71,0x1f,0xb1,0x26,0x93,0x9a,0xf1,0x1a,0xac,0x62,0x2e,0xc2,0xf6,0xe5,0x89,0x5c,0x84,0xf0,0x55,0xf3,0x41,0xc9,0x9f,0x47,0x7d,0x6e,0xde,0x46,0xc,0x3,0xdc,0x12,0x9,0x85,0x34,0x9e,0xf7,0xaa,0xfa,0x7a,0xd1,0x1,0x37,0x8c,0xa4,0x25,0x24,0x19,0x7c,0x98,0xd,0x5f,0x1b,0x95,0xef,0xc0,0xfc,0xcd,0x3d,0x9c,0x51,0x88,0xff,0x82,0xa,0x96,0xe7,0xca,0xbe,0xa6,0xd0,0xb,0x4b,0x5b,0x1e,0xc1,0x45,0xd7,0x50,0x0,0x23,0x8a,0x72,0xb8,0x68,0x42,0x94,0xba,0x8b,0xe6,0xa1,0x5e,0xf2,0x74,0x91,0xe,0xc6,0x4,0x77,0x22,0x78,0xd8,0x43,0x36,0xf4,0xda,0x2c,0xec,0x63,0xb2,0xfb,0xf9,0x5a,0xb4,0x7,0x6b,0xcc,0xb7,0x11,0xfe,0xd1,0xb8,0xa6,0x47,0x23,0x8e,0x10,0xc9,0x6b,0xcb,0x46,0x7f,0xc7,0xe3,0x24,0xab,0x8d,0x37,0xa1,0x5c,0xad,0xcc,0x27,0xde,0xf3,0xa0,0x54,0xe,0xe8,0x50,0xef,0x8c,0xd7,0xbd,0x20,0xb3,0xb5,0x1b,0x26,0xc5,0x3a,0x7e,0x52,0x76,0x4,0x7,0xbc,0x66,0xf9,0x2,0x7a,0xaa,0x1e,0xc1,0xc6,0xb,0xb1,0x4e,0x6c,0x5b,0x41,0x85,0x2e,0x5d,0x15,0x8f,0x56,0x39,0x9,0x80,0xdd,0x61,0x72,0x83,0xc0,0x36,0x3d,0xa,0x9b,0xa9,0x55,0x87,0x8a,0xba,0x4f,0x86,0x38,0x2a,0x44,0xee,0xf,0x96,0x84,0xe1,0x73,0x7d,0xf7,0x93,0x1d,0xed,0x3,0x49,0xd9,0x21,0x51,0x17,0x33,0xe0,0xd8,0xd5,0xcd,0xf5,0xe9,0x5f,0x3c,0x68,0x78,0xd2,0xc3,0x90,0xdb,0x1,0x18,0x8,0x22,0xf4,0x9f,0x74,0xc2,0xc,0x40,0xac,0x98,0x8b,0xe7,0x32,0xea,0x9e,0x3b,0x9d,0x2f,0xce,0xbb,0xd3,0x79,0xa5,0xfc,0x57,0x43,0x7b,0x45,0x5e,0x1f,0x71,0xdf,0x48,0xfd,0xc4,0x94,0x14,0xbf,0x6f,0x59,0xe2,0xca,0x4b,0x4a,0x77,0x12,0xf6,0x63,0x31,0x75,0xa7,0xf1,0x29,0x13,0x0,0xb0,0x28,0x62,0x6d,0xb2,0x7c,0x67,0xeb,0x5a,0xf0,0x99,0xc8,0xbe,0x65,0x25,0x35,0x70,0xaf,0x2b,0xb9,0x3e,0x6e,0x4d,0xe4,0x1c,0xd6,0x6,0xfb,0x81,0xae,0x92,0xa3,0x53,0xf2,0x3f,0xe6,0x91,0xec,0x64,0xf8,0x89,0xa4,0xd0,0xb6,0x2d,0x58,0x9a,0xb4,0x42,0x82,0xd,0xdc,0x95,0x97,0x34,0xda,0x69,0x5,0xa2,0x2c,0xfa,0xd4,0xe5,0x88,0xcf,0x30,0x9c,0x1a,0xff,0x60,0xa8,0x6a,0x19,0x4c,0x16,0x8e,0xa3,0x5a,0xb1,0x95,0x73,0x29,0xdd,0xf0,0xd6,0x59,0x9e,0xd0,0x21,0xdc,0x4a,0x16,0xb4,0x6d,0xf3,0xba,0x2,0x3b,0xb6,0xac,0x83,0x6c,0xca,0x5e,0x3a,0xdb,0xc5,0xcc,0x76,0xbb,0xbc,0x3c,0x26,0x11,0x33,0x84,0x1b,0xc1,0x7a,0x63,0xd7,0x7,0x7f,0x47,0xb8,0x5b,0x66,0x79,0xb,0x2f,0x3,0xaa,0xf1,0x92,0x2d,0xc8,0xce,0x5d,0xc0,0x39,0x57,0x45,0xfb,0xf9,0xeb,0x72,0x93,0x28,0xd4,0xe6,0x77,0x32,0xc7,0xf7,0xfa,0xf,0x1c,0xa0,0xfd,0x40,0x4b,0xbd,0xfe,0x68,0x20,0x53,0xf8,0x74,0x44,0x2b,0xf2,0xa6,0xed,0xbe,0xaf,0x5f,0x75,0x65,0x7c,0x94,0x88,0xb0,0xa8,0x5,0x15,0x41,0x22,0x2c,0x5c,0xa4,0x34,0xa5,0x9d,0x4e,0x6a,0x8a,0x0,0xe,0x9c,0x7e,0x90,0x60,0xee,0x62,0x23,0x38,0x6,0x80,0x35,0xa2,0xc,0x4,0xae,0xc6,0xb3,0x3e,0x2a,0x81,0xd8,0x97,0x4f,0x9a,0xf6,0x52,0xe0,0x46,0xe3,0xbf,0x9,0xe2,0x89,0xe5,0xd1,0x3d,0x71,0x1a,0x1,0xcf,0x10,0xe4,0x8d,0x27,0x96,0x6e,0x54,0x8c,0xda,0x1f,0x55,0xcd,0x7d,0x6f,0xa,0x37,0x36,0x8,0x4c,0x1e,0x8b,0xc2,0x69,0xe9,0xb9,0xb7,0x9f,0x24,0x12,0x19,0x91,0xec,0x9b,0xad,0xd9,0xf4,0x85,0xef,0xd3,0xfc,0x86,0x42,0x8f,0x2e,0xde,0x30,0x13,0x43,0xc4,0x7b,0xab,0x61,0x99,0x58,0x18,0xc3,0xb5,0x56,0xd2,0xd,0x48,0xd5,0x1d,0x82,0x67,0x6b,0x31,0x64,0x17,0x98,0xa9,0x87,0x51,0xe1,0x4d,0xb2,0xf5,0x49,0xea,0xe8,0xa1,0xdf,0x78,0x14,0xa7,0xe7,0x25,0x50,0xcb,0x70,0xff,0x3f,0xc9,0x75,0x73,0x7b,0xe6,0x4a,0x11,0x96,0x29,0xb0,0xc2,0xb8,0x94,0x3,0xfc,0xdd,0xe0,0x6c,0xd8,0xc4,0xbc,0xa0,0x3f,0xc1,0x7a,0x9d,0x87,0x88,0xaa,0xcd,0x77,0x7,0x0,0x81,0xe5,0x7e,0x60,0x38,0x17,0x71,0xd7,0xb9,0x1,0xd,0x80,0xf,0xad,0x48,0xd6,0x9a,0x6b,0xf1,0x67,0x6d,0x4b,0x25,0xe2,0xc8,0x2e,0x66,0x92,0x18,0x35,0xa,0xe1,0x2b,0xc5,0x55,0xdb,0xbb,0x31,0x27,0xb5,0x26,0x1e,0xd1,0xf5,0xe7,0x97,0x8f,0x1f,0xae,0xbe,0x99,0xfa,0x33,0x2f,0x13,0xb,0xce,0xe4,0xc7,0xde,0x56,0x1d,0x14,0x5,0xff,0xcf,0x49,0x90,0x9b,0xd3,0x43,0xe8,0xf0,0xfb,0x45,0x6,0xa7,0xb4,0x46,0x1b,0x7c,0x89,0x41,0x4c,0x6f,0x93,0xcc,0x5d,0x50,0x42,0x28,0xc9,0xec,0x82,0x40,0xfe,0x24,0xc,0xa9,0x9f,0xd2,0x79,0x2,0x52,0xf7,0xb3,0x30,0xa5,0xb1,0xd4,0x8d,0x8c,0xee,0xa4,0xc6,0x76,0xef,0xd5,0x61,0x37,0x36,0x5f,0x2d,0x9c,0xba,0xa1,0xab,0x74,0x6a,0x5e,0xca,0x86,0xb2,0x4,0x32,0x59,0x5b,0xe9,0x58,0xfd,0xf4,0x2c,0x4d,0x21,0x91,0x85,0x63,0x3a,0x15,0xbf,0x8,0x7d,0x8e,0x3b,0xb7,0x19,0x98,0xd9,0xbd,0x83,0x44,0xcb,0x72,0x84,0x9e,0x5c,0x70,0xeb,0xc3,0x64,0x1c,0xaf,0x51,0xf2,0x1a,0x53,0xf6,0x5a,0x4e,0x9,0x12,0x23,0xea,0x3c,0x8a,0xd0,0xac,0xdf,0xa6,0x6e,0xdc,0x39,0x69,0xed,0xf3,0xb6,0xa3,0xe3,0xe,0x78,0x10,0xc0,0x22,0xda,0xa8,0x8b,0x7f,0xf8,0x34,0xf9,0x65,0x95,0x68,0x54,0x3d,0x47,0x62,0x16,0x3e,0x4f,0x2a,0xa2,0x20,0x57,0x17,0xd0,0x5f,0x79,0xc3,0x55,0xa8,0x59,0x38,0xd3,0x2a,0x7,0x54,0xa0,0xfa,0x1c,0x43,0xe5,0xa,0x25,0x4c,0x52,0xb3,0xd7,0x7a,0xe4,0x3d,0x9f,0x3f,0xb2,0x8b,0x33,0xf3,0x48,0x92,0xd,0xf6,0x8e,0x5e,0xea,0x35,0x32,0xff,0x45,0xba,0x98,0xaf,0xb5,0xa4,0x1b,0x78,0x23,0x49,0xd4,0x47,0x41,0xef,0xd2,0x31,0xce,0x8a,0xa6,0x82,0xf0,0xfe,0x6f,0x5d,0xa1,0x73,0x7e,0x4e,0xbb,0x72,0xcc,0xde,0xb0,0x1a,0xfb,0x62,0x70,0x71,0xda,0xa9,0xe1,0x7b,0xa2,0xcd,0xfd,0x74,0x29,0x95,0x86,0x77,0x34,0xc2,0xc9,0x21,0x39,0x1,0x1d,0xab,0xc8,0x9c,0x8c,0x26,0x37,0x64,0x2f,0xf5,0xec,0xfc,0xd6,0x15,0x87,0x89,0x3,0x67,0xe9,0x19,0xf7,0xbd,0x2d,0xd5,0xa5,0xe3,0xc7,0x14,0x2c,0x3a,0x4f,0x27,0x8d,0x51,0x8,0xa3,0xb7,0x8f,0xb1,0xaa,0xeb,0x85,0x2b,0xbc,0x9,0x0,0x6b,0x80,0x36,0xf8,0xb4,0x58,0x6c,0x7f,0x13,0xc6,0x1e,0x6a,0xcf,0x69,0xdb,0x53,0x5,0xdd,0xe7,0xf4,0x44,0xdc,0x96,0x99,0x46,0x88,0x93,0x1f,0xae,0x4,0x6d,0x30,0x60,0xe0,0x4b,0x9b,0xad,0x16,0x3e,0xbf,0xbe,0x83,0xe6,0x2,0x97,0xc5,0x81,0xf,0x75,0x5a,0x66,0x57,0xa7,0x6,0xcb,0x12,0x65,0x18,0x90,0xc,0x7d,0x50,0x24,0x3c,0x4a,0x91,0xd1,0xc1,0x84,0x5b,0xdf,0x4d,0xca,0x9a,0xb9,0x10,0xe8,0x22,0xf2,0xd8,0xe,0x20,0x11,0x7c,0x3b,0xc4,0x68,0xee,0xb,0x94,0x5c,0x9e,0xed,0xb8,0xe2,0x42,0xd9,0xac,0x6e,0x40,0xb6,0x76,0xf9,0x28,0x61,0x63,0xc0,0x2e,0x9d,0xf1,0x56,0xb7,0x11,0xfe,0xd1,0xb8,0xa6,0x47,0x23,0x8e,0x10,0xc9,0x6b,0xcb,0x46,0x7f,0xc7,0xe3,0x24,0xab,0x8d,0x37,0xa1,0x5c,0xad,0xcc,0x27,0xde,0xf3,0xa0,0x54,0xe,0xe8,0x50,0xef,0x8c,0xd7,0xbd,0x20,0xb3,0xb5,0x1b,0x26,0xc5,0x3a,0x7e,0x52,0x76,0x4,0x7,0xbc,0x66,0xf9,0x2,0x7a,0xaa,0x1e,0xc1,0xc6,0xb,0xb1,0x4e,0x6c,0x5b,0x41,0x85,0x2e,0x5d,0x15,0x8f,0x56,0x39,0x9,0x80,0xdd,0x61,0x72,0x83,0xc0,0x36,0x3d,0xa,0x9b,0xa9,0x55,0x87,0x8a,0xba,0x4f,0x86,0x38,0x2a,0x44,0xee,0xf,0x96,0x84,0xe1,0x73,0x7d,0xf7,0x93,0x1d,0xed,0x3,0x49,0xd9,0x21,0x51,0x17,0x33,0xe0,0xd8,0xd5,0xcd,0xf5,0xe9,0x5f,0x3c,0x68,0x78,0xd2,0xc3,0x90,0xdb,0x1,0x18,0x8,0x22,0xf4,0x9f,0x74,0xc2,0xc,0x40,0xac,0x98,0x8b,0xe7,0x32,0xea,0x9e,0x3b,0x9d,0x2f,0xce,0xbb,0xd3,0x79,0xa5,0xfc,0x57,0x43,0x7b,0x45,0x5e,0x1f,0x71,0xdf,0x48,0xfd,0xc4,0x94,0x14,0xbf,0x6f,0x59,0xe2,0xca,0x4b,0x4a,0x77,0x12,0xf6,0x63,0x31,0x75,0xa7,0xf1,0x29,0x13,0x0,0xb0,0x28,0x62,0x6d,0xb2,0x7c,0x67,0xeb,0x5a,0xf0,0x99,0xc8,0xbe,0x65,0x25,0x35,0x70,0xaf,0x2b,0xb9,0x3e,0x6e,0x4d,0xe4,0x1c,0xd6,0x6,0xfb,0x81,0xae,0x92,0xa3,0x53,0xf2,0x3f,0xe6,0x91,0xec,0x64,0xf8,0x89,0xa4,0xd0,0xb6,0x2d,0x58,0x9a,0xb4,0x42,0x82,0xd,0xdc,0x95,0x97,0x34,0xda,0x69,0x5,0xa2,0x2c,0xfa,0xd4,0xe5,0x88,0xcf,0x30,0x9c,0x1a,0xff,0x60,0xa8,0x6a,0x19,0x4c,0x16,0xb3,0x9e,0x67,0x8c,0xa8,0x4e,0x14,0xe0,0xcd,0xeb,0x64,0xa3,0xed,0x1c,0xe1,0x77,0x2b,0x89,0x50,0xce,0x87,0x3f,0x6,0x8b,0x91,0xbe,0x51,0xf7,0x63,0x7,0xe6,0xf8,0xf1,0x4b,0x86,0x81,0x1,0x1b,0x2c,0xe,0xb9,0x26,0xfc,0x47,0x5e,0xea,0x3a,0x42,0x7a,0x85,0x66,0x5b,0x44,0x36,0x12,0x3e,0x97,0xcc,0xaf,0x10,0xf5,0xf3,0x60,0xfd,0x4,0x6a,0x78,0xc6,0xc4,0xd6,0x4f,0xae,0x15,0xe9,0xdb,0x4a,0xf,0xfa,0xca,0xc7,0x32,0x21,0x9d,0xc0,0x7d,0x76,0x80,0xc3,0x55,0x1d,0x6e,0xc5,0x49,0x79,0x16,0xcf,0x9b,0xd0,0x83,0x92,0x62,0x48,0x58,0x41,0xa9,0xb5,0x8d,0x95,0x38,0x28,0x7c,0x1f,0x11,0x61,0x99,0x9,0x98,0xa0,0x73,0x57,0xb7,0x3d,0x33,0xa1,0x43,0xad,0x5d,0xd3,0x5f,0x1e,0x5,0x3b,0xbd,0x8,0x9f,0x31,0x39,0x93,0xfb,0x8e,0x3,0x17,0xbc,0xe5,0xaa,0x72,0xa7,0xcb,0x6f,0xdd,0x7b,0xde,0x82,0x34,0xdf,0xb4,0xd8,0xec,0x0,0x4c,0x27,0x3c,0xf2,0x2d,0xd9,0xb0,0x1a,0xab,0x53,0x69,0xb1,0xe7,0x22,0x68,0xf0,0x40,0x52,0x37,0xa,0xb,0x35,0x71,0x23,0xb6,0xff,0x54,0xd4,0x84,0x8a,0xa2,0x19,0x2f,0x24,0xac,0xd1,0xa6,0x90,0xe4,0xc9,0xb8,0xd2,0xee,0xc1,0xbb,0x7f,0xb2,0x13,0xe3,0xd,0x2e,0x7e,0xf9,0x46,0x96,0x5c,0xa4,0x65,0x25,0xfe,0x88,0x6b,0xef,0x30,0x75,0xe8,0x20,0xbf,0x5a,0x56,0xc,0x59,0x2a,0xa5,0x94,0xba,0x6c,0xdc,0x70,0x8f,0xc8,0x74,0xd7,0xd5,0x9c,0xe2,0x45,0x29,0x9a,0xda,0x18,0x6d,0xf6,0x4d,0xc2,0x2,0xf4,0xb8,0xbe,0xb6,0x2b,0x87,0xdc,0x5b,0xe4,0x7d,0xf,0x75,0x59,0xce,0x31,0x10,0x2d,0xa1,0x15,0x9,0x71,0x6d,0xf2,0xc,0xb7,0x50,0x4a,0x45,0x67,0x0,0xba,0xca,0xcd,0x4c,0x28,0xb3,0xad,0xf5,0xda,0xbc,0x1a,0x74,0xcc,0xc0,0x4d,0xc2,0x60,0x85,0x1b,0x57,0xa6,0x3c,0xaa,0xa0,0x86,0xe8,0x2f,0x5,0xe3,0xab,0x5f,0xd5,0xf8,0xc7,0x2c,0xe6,0x8,0x98,0x16,0x76,0xfc,0xea,0x78,0xeb,0xd3,0x1c,0x38,0x2a,0x5a,0x42,0xd2,0x63,0x73,0x54,0x37,0xfe,0xe2,0xde,0xc6,0x3,0x29,0xa,0x13,0x9b,0xd0,0xd9,0xc8,0x32,0x2,0x84,0x5d,0x56,0x1e,0x8e,0x25,0x3d,0x36,0x88,0xcb,0x6a,0x79,0x8b,0xd6,0xb1,0x44,0x8c,0x81,0xa2,0x5e,0x1,0x90,0x9d,0x8f,0xe5,0x4,0x21,0x4f,0x8d,0x33,0xe9,0xc1,0x64,0x52,0x1f,0xb4,0xcf,0x9f,0x3a,0x7e,0xfd,0x68,0x7c,0x19,0x40,0x41,0x23,0x69,0xb,0xbb,0x22,0x18,0xac,0xfa,0xfb,0x92,0xe0,0x51,0x77,0x6c,0x66,0xb9,0xa7,0x93,0x7,0x4b,0x7f,0xc9,0xff,0x94,0x96,0x24,0x95,0x30,0x39,0xe1,0x80,0xec,0x5c,0x48,0xae,0xf7,0xd8,0x72,0xc5,0xb0,0x43,0xf6,0x7a,0xd4,0x55,0x14,0x70,0x4e,0x89,0x6,0xbf,0x49,0x53,0x91,0xbd,0x26,0xe,0xa9,0xd1,0x62,0x9c,0x3f,0xd7,0x9e,0x3b,0x97,0x83,0xc4,0xdf,0xee,0x27,0xf1,0x47,0x1d,0x61,0x12,0x6b,0xa3,0x11,0xf4,0xa4,0x20,0x3e,0x7b,0x6e,0x2e,0xc3,0xb5,0xdd,0xd,0xef,0x17,0x65,0x46,0xb2,0x35,0xf9,0x34,0xa8,0x58,0xa5,0x99,0xf0,0x8a,0xaf,0xdb,0xf3,0x82,0xe7,0x6f,0xed,0x9a,0x38,0xff,0x70,0x56,0xec,0x7a,0x87,0x76,0x17,0xfc,0x5,0x28,0x7b,0x8f,0xd5,0x33,0x6c,0xca,0x25,0xa,0x63,0x7d,0x9c,0xf8,0x55,0xcb,0x12,0xb0,0x10,0x9d,0xa4,0x1c,0xdc,0x67,0xbd,0x22,0xd9,0xa1,0x71,0xc5,0x1a,0x1d,0xd0,0x6a,0x95,0xb7,0x80,0x9a,0x8b,0x34,0x57,0xc,0x66,0xfb,0x68,0x6e,0xc0,0xfd,0x1e,0xe1,0xa5,0x89,0xad,0xdf,0xd1,0x40,0x72,0x8e,0x5c,0x51,0x61,0x94,0x5d,0xe3,0xf1,0x9f,0x35,0xd4,0x4d,0x5f,0x5e,0xf5,0x86,0xce,0x54,0x8d,0xe2,0xd2,0x5b,0x6,0xba,0xa9,0x58,0x1b,0xed,0xe6,0xe,0x16,0x2e,0x32,0x84,0xe7,0xb3,0xa3,0x9,0x18,0x4b,0x0,0xda,0xc3,0xd3,0xf9,0x3a,0xa8,0xa6,0x2c,0x48,0xc6,0x36,0xd8,0x92,0x2,0xfa,0x8a,0xcc,0xe8,0x3b,0x3,0x15,0x60,0x8,0xa2,0x7e,0x27,0x8c,0x98,0xa0,0x9e,0x85,0xc4,0xaa,0x4,0x93,0x26,0x2f,0x44,0xaf,0x19,0xd7,0x9b,0x77,0x43,0x50,0x3c,0xe9,0x31,0x45,0xe0,0x46,0xf4,0x7c,0x2a,0xf2,0xc8,0xdb,0x6b,0xf3,0xb9,0xb6,0x69,0xa7,0xbc,0x30,0x81,0x2b,0x42,0x1f,0x4f,0xcf,0x64,0xb4,0x82,0x39,0x11,0x90,0x91,0xac,0xc9,0x2d,0xb8,0xea,0xae,0x20,0x5a,0x75,0x49,0x78,0x88,0x29,0xe4,0x3d,0x4a,0x37,0xbf,0x23,0x52,0x7f,0xb,0x13,0x65,0xbe,0xfe,0xee,0xab,0x74,0xf0,0x62,0xe5,0xb5,0x96,0x3f,0xc7,0xd,0xdd,0xf7,0x21,0xf,0x3e,0x53,0x14,0xeb,0x47,0xc1,0x24,0xbb,0x73,0xb1,0xc2,0x97,0xcd,0x6d,0xf6,0x83,0x41,0x6f,0x99,0x59,0xd6,0x7,0x4e,0x4c,0xef,0x1,0xb2,0xde,0x79,0xff,0x59,0xb6,0x99,0xf0,0xee,0xf,0x6b,0xc6,0x58,0x81,0x23,0x83,0xe,0x37,0x8f,0xab,0x6c,0xe3,0xc5,0x7f,0xe9,0x14,0xe5,0x84,0x6f,0x96,0xbb,0xe8,0x1c,0x46,0xa0,0x18,0xa7,0xc4,0x9f,0xf5,0x68,0xfb,0xfd,0x53,0x6e,0x8d,0x72,0x36,0x1a,0x3e,0x4c,0x4f,0xf4,0x2e,0xb1,0x4a,0x32,0xe2,0x56,0x89,0x8e,0x43,0xf9,0x6,0x24,0x13,0x9,0xcd,0x66,0x15,0x5d,0xc7,0x1e,0x71,0x41,0xc8,0x95,0x29,0x3a,0xcb,0x88,0x7e,0x75,0x42,0xd3,0xe1,0x1d,0xcf,0xc2,0xf2,0x7,0xce,0x70,0x62,0xc,0xa6,0x47,0xde,0xcc,0xa9,0x3b,0x35,0xbf,0xdb,0x55,0xa5,0x4b,0x1,0x91,0x69,0x19,0x5f,0x7b,0xa8,0x90,0x9d,0x85,0xbd,0xa1,0x17,0x74,0x20,0x30,0x9a,0x8b,0xd8,0x93,0x49,0x50,0x40,0x6a,0xbc,0xd7,0x3c,0x8a,0x44,0x8,0xe4,0xd0,0xc3,0xaf,0x7a,0xa2,0xd6,0x73,0xd5,0x67,0x86,0xf3,0x9b,0x31,0xed,0xb4,0x1f,0xb,0x33,0xd,0x16,0x57,0x39,0x97,0x0,0xb5,0x8c,0xdc,0x5c,0xf7,0x27,0x11,0xaa,0x82,0x3,0x2,0x3f,0x5a,0xbe,0x2b,0x79,0x3d,0xef,0xb9,0x61,0x5b,0x48,0xf8,0x60,0x2a,0x25,0xfa,0x34,0x2f,0xa3,0x12,0xb8,0xd1,0x80,0xf6,0x2d,0x6d,0x7d,0x38,0xe7,0x63,0xf1,0x76,0x26,0x5,0xac,0x54,0x9e,0x4e,0xb3,0xc9,0xe6,0xda,0xeb,0x1b,0xba,0x77,0xae,0xd9,0xa4,0x2c,0xb0,0xc1,0xec,0x98,0xfe,0x65,0x10,0xd2,0xfc,0xa,0xca,0x45,0x94,0xdd,0xdf,0x7c,0x92,0x21,0x4d,0xea,0x64,0xb2,0x9c,0xad,0xc0,0x87,0x78,0xd4,0x52,0xb7,0x28,0xe0,0x22,0x51,0x4,0x5e,0x61,0x4c,0xb5,0x5e,0x7a,0x9c,0xc6,0x32,0x1f,0x39,0xb6,0x71,0x3f,0xce,0x33,0xa5,0xf9,0x5b,0x82,0x1c,0x55,0xed,0xd4,0x59,0x43,0x6c,0x83,0x25,0xb1,0xd5,0x34,0x2a,0x23,0x99,0x54,0x53,0xd3,0xc9,0xfe,0xdc,0x6b,0xf4,0x2e,0x95,0x8c,0x38,0xe8,0x90,0xa8,0x57,0xb4,0x89,0x96,0xe4,0xc0,0xec,0x45,0x1e,0x7d,0xc2,0x27,0x21,0xb2,0x2f,0xd6,0xb8,0xaa,0x14,0x16,0x4,0x9d,0x7c,0xc7,0x3b,0x9,0x98,0xdd,0x28,0x18,0x15,0xe0,0xf3,0x4f,0x12,0xaf,0xa4,0x52,0x11,0x87,0xcf,0xbc,0x17,0x9b,0xab,0xc4,0x1d,0x49,0x2,0x51,0x40,0xb0,0x9a,0x8a,0x93,0x7b,0x67,0x5f,0x47,0xea,0xfa,0xae,0xcd,0xc3,0xb3,0x4b,0xdb,0x4a,0x72,0xa1,0x85,0x65,0xef,0xe1,0x73,0x91,0x7f,0x8f,0x1,0x8d,0xcc,0xd7,0xe9,0x6f,0xda,0x4d,0xe3,0xeb,0x41,0x29,0x5c,0xd1,0xc5,0x6e,0x37,0x78,0xa0,0x75,0x19,0xbd,0xf,0xa9,0xc,0x50,0xe6,0xd,0x66,0xa,0x3e,0xd2,0x9e,0xf5,0xee,0x20,0xff,0xb,0x62,0xc8,0x79,0x81,0xbb,0x63,0x35,0xf0,0xba,0x22,0x92,0x80,0xe5,0xd8,0xd9,0xe7,0xa3,0xf1,0x64,0x2d,0x86,0x6,0x56,0x58,0x70,0xcb,0xfd,0xf6,0x7e,0x3,0x74,0x42,0x36,0x1b,0x6a,0x0,0x3c,0x13,0x69,0xad,0x60,0xc1,0x31,0xdf,0xfc,0xac,0x2b,0x94,0x44,0x8e,0x76,0xb7,0xf7,0x2c,0x5a,0xb9,0x3d,0xe2,0xa7,0x3a,0xf2,0x6d,0x88,0x84,0xde,0x8b,0xf8,0x77,0x46,0x68,0xbe,0xe,0xa2,0x5d,0x1a,0xa6,0x5,0x7,0x4e,0x30,0x97,0xfb,0x48,0x8,0xca,0xbf,0x24,0x9f,0x10,0xd0,0x26,0x4,0x2,0xa,0x97,0x3b,0x60,0xe7,0x58,0xc1,0xb3,0xc9,0xe5,0x72,0x8d,0xac,0x91,0x1d,0xa9,0xb5,0xcd,0xd1,0x4e,0xb0,0xb,0xec,0xf6,0xf9,0xdb,0xbc,0x6,0x76,0x71,0xf0,0x94,0xf,0x11,0x49,0x66,0x0,0xa6,0xc8,0x70,0x7c,0xf1,0x7e,0xdc,0x39,0xa7,0xeb,0x1a,0x80,0x16,0x1c,0x3a,0x54,0x93,0xb9,0x5f,0x17,0xe3,0x69,0x44,0x7b,0x90,0x5a,0xb4,0x24,0xaa,0xca,0x40,0x56,0xc4,0x57,0x6f,0xa0,0x84,0x96,0xe6,0xfe,0x6e,0xdf,0xcf,0xe8,0x8b,0x42,0x5e,0x62,0x7a,0xbf,0x95,0xb6,0xaf,0x27,0x6c,0x65,0x74,0x8e,0xbe,0x38,0xe1,0xea,0xa2,0x32,0x99,0x81,0x8a,0x34,0x77,0xd6,0xc5,0x37,0x6a,0xd,0xf8,0x30,0x3d,0x1e,0xe2,0xbd,0x2c,0x21,0x33,0x59,0xb8,0x9d,0xf3,0x31,0x8f,0x55,0x7d,0xd8,0xee,0xa3,0x8,0x73,0x23,0x86,0xc2,0x41,0xd4,0xc0,0xa5,0xfc,0xfd,0x9f,0xd5,0xb7,0x7,0x9e,0xa4,0x10,0x46,0x47,0x2e,0x5c,0xed,0xcb,0xd0,0xda,0x5,0x1b,0x2f,0xbb,0xf7,0xc3,0x75,0x43,0x28,0x2a,0x98,0x29,0x8c,0x85,0x5d,0x3c,0x50,0xe0,0xf4,0x12,0x4b,0x64,0xce,0x79,0xc,0xff,0x4a,0xc6,0x68,0xe9,0xa8,0xcc,0xf2,0x35,0xba,0x3,0xf5,0xef,0x2d,0x1,0x9a,0xb2,0x15,0x6d,0xde,0x20,0x83,0x6b,0x22,0x87,0x2b,0x3f,0x78,0x63,0x52,0x9b,0x4d,0xfb,0xa1,0xdd,0xae,0xd7,0x1f,0xad,0x48,0x18,0x9c,0x82,0xc7,0xd2,0x92,0x7f,0x9,0x61,0xb1,0x53,0xab,0xd9,0xfa,0xe,0x89,0x45,0x88,0x14,0xe4,0x19,0x25,0x4c,0x36,0x13,0x67,0x4f,0x3e,0x5b,0xd3,0x51,0x26,0xb2,0x75,0xfa,0xdc,0x66,0xf0,0xd,0xfc,0x9d,0x76,0x8f,0xa2,0xf1,0x5,0x5f,0xb9,0xe6,0x40,0xaf,0x80,0xe9,0xf7,0x16,0x72,0xdf,0x41,0x98,0x3a,0x9a,0x17,0x2e,0x96,0x56,0xed,0x37,0xa8,0x53,0x2b,0xfb,0x4f,0x90,0x97,0x5a,0xe0,0x1f,0x3d,0xa,0x10,0x1,0xbe,0xdd,0x86,0xec,0x71,0xe2,0xe4,0x4a,0x77,0x94,0x6b,0x2f,0x3,0x27,0x55,0x5b,0xca,0xf8,0x4,0xd6,0xdb,0xeb,0x1e,0xd7,0x69,0x7b,0x15,0xbf,0x5e,0xc7,0xd5,0xd4,0x7f,0xc,0x44,0xde,0x7,0x68,0x58,0xd1,0x8c,0x30,0x23,0xd2,0x91,0x67,0x6c,0x84,0x9c,0xa4,0xb8,0xe,0x6d,0x39,0x29,0x83,0x92,0xc1,0x8a,0x50,0x49,0x59,0x73,0xb0,0x22,0x2c,0xa6,0xc2,0x4c,0xbc,0x52,0x18,0x88,0x70,0x0,0x46,0x62,0xb1,0x89,0x9f,0xea,0x82,0x28,0xf4,0xad,0x6,0x12,0x2a,0x14,0xf,0x4e,0x20,0x8e,0x19,0xac,0xa5,0xce,0x25,0x93,0x5d,0x11,0xfd,0xc9,0xda,0xb6,0x63,0xbb,0xcf,0x6a,0xcc,0x7e,0xf6,0xa0,0x78,0x42,0x51,0xe1,0x79,0x33,0x3c,0xe3,0x2d,0x36,0xba,0xb,0xa1,0xc8,0x95,0xc5,0x45,0xee,0x3e,0x8,0xb3,0x9b,0x1a,0x1b,0x26,0x43,0xa7,0x32,0x60,0x24,0xaa,0xd0,0xff,0xc3,0xf2,0x2,0xa3,0x6e,0xb7,0xc0,0xbd,0x35,0xa9,0xd8,0xf5,0x81,0x99,0xef,0x34,0x74,0x64,0x21,0xfe,0x7a,0xe8,0x6f,0x3f,0x1c,0xb5,0x4d,0x87,0x57,0x7d,0xab,0x85,0xb4,0xd9,0x9e,0x61,0xcd,0x4b,0xae,0x31,0xf9,0x3b,0x48,0x1d,0x47,0xe7,0x7c,0x9,0xcb,0xe5,0x13,0xd3,0x5c,0x8d,0xc4,0xc6,0x65,0x8b,0x38,0x54,0xf3,0x67,0xc1,0x2e,0x1,0x68,0x76,0x97,0xf3,0x5e,0xc0,0x19,0xbb,0x1b,0x96,0xaf,0x17,0x33,0xf4,0x7b,0x5d,0xe7,0x71,0x8c,0x7d,0x1c,0xf7,0xe,0x23,0x70,0x84,0xde,0x38,0x80,0x3f,0x5c,0x7,0x6d,0xf0,0x63,0x65,0xcb,0xf6,0x15,0xea,0xae,0x82,0xa6,0xd4,0xd7,0x6c,0xb6,0x29,0xd2,0xaa,0x7a,0xce,0x11,0x16,0xdb,0x61,0x9e,0xbc,0x8b,0x91,0x55,0xfe,0x8d,0xc5,0x5f,0x86,0xe9,0xd9,0x50,0xd,0xb1,0xa2,0x53,0x10,0xe6,0xed,0xda,0x4b,0x79,0x85,0x57,0x5a,0x6a,0x9f,0x56,0xe8,0xfa,0x94,0x3e,0xdf,0x46,0x54,0x31,0xa3,0xad,0x27,0x43,0xcd,0x3d,0xd3,0x99,0x9,0xf1,0x81,0xc7,0xe3,0x30,0x8,0x5,0x1d,0x25,0x39,0x8f,0xec,0xb8,0xa8,0x2,0x13,0x40,0xb,0xd1,0xc8,0xd8,0xf2,0x24,0x4f,0xa4,0x12,0xdc,0x90,0x7c,0x48,0x5b,0x37,0xe2,0x3a,0x4e,0xeb,0x4d,0xff,0x1e,0x6b,0x3,0xa9,0x75,0x2c,0x87,0x93,0xab,0x95,0x8e,0xcf,0xa1,0xf,0x98,0x2d,0x14,0x44,0xc4,0x6f,0xbf,0x89,0x32,0x1a,0x9b,0x9a,0xa7,0xc2,0x26,0xb3,0xe1,0xa5,0x77,0x21,0xf9,0xc3,0xd0,0x60,0xf8,0xb2,0xbd,0x62,0xac,0xb7,0x3b,0x8a,0x20,0x49,0x18,0x6e,0xb5,0xf5,0xe5,0xa0,0x7f,0xfb,0x69,0xee,0xbe,0x9d,0x34,0xcc,0x6,0xd6,0x2b,0x51,0x7e,0x42,0x73,0x83,0x22,0xef,0x36,0x41,0x3c,0xb4,0x28,0x59,0x74,0x0,0x66,0xfd,0x88,0x4a,0x64,0x92,0x52,0xdd,0xc,0x45,0x47,0xe4,0xa,0xb9,0xd5,0x72,0xfc,0x2a,0x4,0x35,0x58,0x1f,0xe0,0x4c,0xca,0x2f,0xb0,0x78,0xba,0xc9,0x9c,0xc6,0x41,0x6c,0x95,0x7e,0x5a,0xbc,0xe6,0x12,0x3f,0x19,0x96,0x51,0x1f,0xee,0x13,0x85,0xd9,0x7b,0xa2,0x3c,0x75,0xcd,0xf4,0x79,0x63,0x4c,0xa3,0x5,0x91,0xf5,0x14,0xa,0x3,0xb9,0x74,0x73,0xf3,0xe9,0xde,0xfc,0x4b,0xd4,0xe,0xb5,0xac,0x18,0xc8,0xb0,0x88,0x77,0x94,0xa9,0xb6,0xc4,0xe0,0xcc,0x65,0x3e,0x5d,0xe2,0x7,0x1,0x92,0xf,0xf6,0x98,0x8a,0x34,0x36,0x24,0xbd,0x5c,0xe7,0x1b,0x29,0xb8,0xfd,0x8,0x38,0x35,0xc0,0xd3,0x6f,0x32,0x8f,0x84,0x72,0x31,0xa7,0xef,0x9c,0x37,0xbb,0x8b,0xe4,0x3d,0x69,0x22,0x71,0x60,0x90,0xba,0xaa,0xb3,0x5b,0x47,0x7f,0x67,0xca,0xda,0x8e,0xed,0xe3,0x93,0x6b,0xfb,0x6a,0x52,0x81,0xa5,0x45,0xcf,0xc1,0x53,0xb1,0x5f,0xaf,0x21,0xad,0xec,0xf7,0xc9,0x4f,0xfa,0x6d,0xc3,0xcb,0x61,0x9,0x7c,0xf1,0xe5,0x4e,0x17,0x58,0x80,0x55,0x39,0x9d,0x2f,0x89,0x2c,0x70,0xc6,0x2d,0x46,0x2a,0x1e,0xf2,0xbe,0xd5,0xce,0x0,0xdf,0x2b,0x42,0xe8,0x59,0xa1,0x9b,0x43,0x15,0xd0,0x9a,0x2,0xb2,0xa0,0xc5,0xf8,0xf9,0xc7,0x83,0xd1,0x44,0xd,0xa6,0x26,0x76,0x78,0x50,0xeb,0xdd,0xd6,0x5e,0x23,0x54,0x62,0x16,0x3b,0x4a,0x20,0x1c,0x33,0x49,0x8d,0x40,0xe1,0x11,0xff,0xdc,0x8c,0xb,0xb4,0x64,0xae,0x56,0x97,0xd7,0xc,0x7a,0x99,0x1d,0xc2,0x87,0x1a,0xd2,0x4d,0xa8,0xa4,0xfe,0xab,0xd8,0x57,0x66,0x48,0x9e,0x2e,0x82,0x7d,0x3a,0x86,0x25,0x27,0x6e,0x10,0xb7,0xdb,0x68,0x28,0xea,0x9f,0x4,0xbf,0x30,0xf0,0x6,0xf4,0xf2,0xfa,0x67,0xcb,0x90,0x17,0xa8,0x31,0x43,0x39,0x15,0x82,0x7d,0x5c,0x61,0xed,0x59,0x45,0x3d,0x21,0xbe,0x40,0xfb,0x1c,0x6,0x9,0x2b,0x4c,0xf6,0x86,0x81,0x0,0x64,0xff,0xe1,0xb9,0x96,0xf0,0x56,0x38,0x80,0x8c,0x1,0x8e,0x2c,0xc9,0x57,0x1b,0xea,0x70,0xe6,0xec,0xca,0xa4,0x63,0x49,0xaf,0xe7,0x13,0x99,0xb4,0x8b,0x60,0xaa,0x44,0xd4,0x5a,0x3a,0xb0,0xa6,0x34,0xa7,0x9f,0x50,0x74,0x66,0x16,0xe,0x9e,0x2f,0x3f,0x18,0x7b,0xb2,0xae,0x92,0x8a,0x4f,0x65,0x46,0x5f,0xd7,0x9c,0x95,0x84,0x7e,0x4e,0xc8,0x11,0x1a,0x52,0xc2,0x69,0x71,0x7a,0xc4,0x87,0x26,0x35,0xc7,0x9a,0xfd,0x8,0xc0,0xcd,0xee,0x12,0x4d,0xdc,0xd1,0xc3,0xa9,0x48,0x6d,0x3,0xc1,0x7f,0xa5,0x8d,0x28,0x1e,0x53,0xf8,0x83,0xd3,0x76,0x32,0xb1,0x24,0x30,0x55,0xc,0xd,0x6f,0x25,0x47,0xf7,0x6e,0x54,0xe0,0xb6,0xb7,0xde,0xac,0x1d,0x3b,0x20,0x2a,0xf5,0xeb,0xdf,0x4b,0x7,0x33,0x85,0xb3,0xd8,0xda,0x68,0xd9,0x7c,0x75,0xad,0xcc,0xa0,0x10,0x4,0xe2,0xbb,0x94,0x3e,0x89,0xfc,0xf,0xba,0x36,0x98,0x19,0x58,0x3c,0x2,0xc5,0x4a,0xf3,0x5,0x1f,0xdd,0xf1,0x6a,0x42,0xe5,0x9d,0x2e,0xd0,0x73,0x9b,0xd2,0x77,0xdb,0xcf,0x88,0x93,0xa2,0x6b,0xbd,0xb,0x51,0x2d,0x5e,0x27,0xef,0x5d,0xb8,0xe8,0x6c,0x72,0x37,0x22,0x62,0x8f,0xf9,0x91,0x41,0xa3,0x5b,0x29,0xa,0xfe,0x79,0xb5,0x78,0xe4,0x14,0xe9,0xd5,0xbc,0xc6,0xe3,0x97,0xbf,0xce,0xab,0x23,0xa1,0xd6,0xfc,0x3b,0xb4,0x92,0x28,0xbe,0x43,0xb2,0xd3,0x38,0xc1,0xec,0xbf,0x4b,0x11,0xf7,0xa8,0xe,0xe1,0xce,0xa7,0xb9,0x58,0x3c,0x91,0xf,0xd6,0x74,0xd4,0x59,0x60,0xd8,0x18,0xa3,0x79,0xe6,0x1d,0x65,0xb5,0x1,0xde,0xd9,0x14,0xae,0x51,0x73,0x44,0x5e,0x4f,0xf0,0x93,0xc8,0xa2,0x3f,0xac,0xaa,0x4,0x39,0xda,0x25,0x61,0x4d,0x69,0x1b,0x15,0x84,0xb6,0x4a,0x98,0x95,0xa5,0x50,0x99,0x27,0x35,0x5b,0xf1,0x10,0x89,0x9b,0x9a,0x31,0x42,0xa,0x90,0x49,0x26,0x16,0x9f,0xc2,0x7e,0x6d,0x9c,0xdf,0x29,0x22,0xca,0xd2,0xea,0xf6,0x40,0x23,0x77,0x67,0xcd,0xdc,0x8f,0xc4,0x1e,0x7,0x17,0x3d,0xfe,0x6c,0x62,0xe8,0x8c,0x2,0xf2,0x1c,0x56,0xc6,0x3e,0x4e,0x8,0x2c,0xff,0xc7,0xd1,0xa4,0xcc,0x66,0xba,0xe3,0x48,0x5c,0x64,0x5a,0x41,0x0,0x6e,0xc0,0x57,0xe2,0xeb,0x80,0x6b,0xdd,0x13,0x5f,0xb3,0x87,0x94,0xf8,0x2d,0xf5,0x81,0x24,0x82,0x30,0xb8,0xee,0x36,0xc,0x1f,0xaf,0x37,0x7d,0x72,0xad,0x63,0x78,0xf4,0x45,0xef,0x86,0xdb,0x8b,0xb,0xa0,0x70,0x46,0xfd,0xd5,0x54,0x55,0x68,0xd,0xe9,0x7c,0x2e,0x6a,0xe4,0x9e,0xb1,0x8d,0xbc,0x4c,0xed,0x20,0xf9,0x8e,0xf3,0x7b,0xe7,0x96,0xbb,0xcf,0xd7,0xa1,0x7a,0x3a,0x2a,0x6f,0xb0,0x34,0xa6,0x21,0x71,0x52,0xfb,0x3,0xc9,0x19,0x33,0xe5,0xcb,0xfa,0x97,0xd0,0x2f,0x83,0x5,0xe0,0x7f,0xb7,0x75,0x6,0x53,0x9,0xa9,0x32,0x47,0x85,0xab,0x5d,0x9d,0x12,0xc3,0x8a,0x88,0x2b,0xc5,0x76,0x1a,0xbd,0x93,0x35,0xda,0xf5,0x9c,0x82,0x63,0x7,0xaa,0x34,0xed,0x4f,0xef,0x62,0x5b,0xe3,0xc7,0x0,0x8f,0xa9,0x13,0x85,0x78,0x89,0xe8,0x3,0xfa,0xd7,0x84,0x70,0x2a,0xcc,0x74,0xcb,0xa8,0xf3,0x99,0x4,0x97,0x91,0x3f,0x2,0xe1,0x1e,0x5a,0x76,0x52,0x20,0x23,0x98,0x42,0xdd,0x26,0x5e,0x8e,0x3a,0xe5,0xe2,0x2f,0x95,0x6a,0x48,0x7f,0x65,0xa1,0xa,0x79,0x31,0xab,0x72,0x1d,0x2d,0xa4,0xf9,0x45,0x56,0xa7,0xe4,0x12,0x19,0x2e,0xbf,0x8d,0x71,0xa3,0xae,0x9e,0x6b,0xa2,0x1c,0xe,0x60,0xca,0x2b,0xb2,0xa0,0xc5,0x57,0x59,0xd3,0xb7,0x39,0xc9,0x27,0x6d,0xfd,0x5,0x75,0x33,0x17,0xc4,0xfc,0xf1,0xe9,0xd1,0xcd,0x7b,0x18,0x4c,0x5c,0xf6,0xe7,0xb4,0xff,0x25,0x3c,0x2c,0x6,0xd0,0xbb,0x50,0xe6,0x28,0x64,0x88,0xbc,0xaf,0xc3,0x16,0xce,0xba,0x1f,0xb9,0xb,0xea,0x9f,0xf7,0x5d,0x81,0xd8,0x73,0x67,0x5f,0x61,0x7a,0x3b,0x55,0xfb,0x6c,0xd9,0xe0,0xb0,0x30,0x9b,0x4b,0x7d,0xc6,0xee,0x6f,0x6e,0x53,0x36,0xd2,0x47,0x15,0x51,0x83,0xd5,0xd,0x37,0x24,0x94,0xc,0x46,0x49,0x96,0x58,0x43,0xcf,0x7e,0xd4,0xbd,0xec,0x9a,0x41,0x1,0x11,0x54,0x8b,0xf,0x9d,0x1a,0x4a,0x69,0xc0,0x38,0xf2,0x22,0xdf,0xa5,0x8a,0xb6,0x87,0x77,0xd6,0x1b,0xc2,0xb5,0xc8,0x40,0xdc,0xad,0x80,0xf4,0x92,0x9,0x7c,0xbe,0x90,0x66,0xa6,0x29,0xf8,0xb1,0xb3,0x10,0xfe,0x4d,0x21,0x86,0x8,0xde,0xf0,0xc1,0xac,0xeb,0x14,0xb8,0x3e,0xdb,0x44,0x8c,0x4e,0x3d,0x68,0x32,0x4b,0x66,0x9f,0x74,0x50,0xb6,0xec,0x18,0x35,0x13,0x9c,0x5b,0x15,0xe4,0x19,0x8f,0xd3,0x71,0xa8,0x36,0x7f,0xc7,0xfe,0x73,0x69,0x46,0xa9,0xf,0x9b,0xff,0x1e,0x0,0x9,0xb3,0x7e,0x79,0xf9,0xe3,0xd4,0xf6,0x41,0xde,0x4,0xbf,0xa6,0x12,0xc2,0xba,0x82,0x7d,0x9e,0xa3,0xbc,0xce,0xea,0xc6,0x6f,0x34,0x57,0xe8,0xd,0xb,0x98,0x5,0xfc,0x92,0x80,0x3e,0x3c,0x2e,0xb7,0x56,0xed,0x11,0x23,0xb2,0xf7,0x2,0x32,0x3f,0xca,0xd9,0x65,0x38,0x85,0x8e,0x78,0x3b,0xad,0xe5,0x96,0x3d,0xb1,0x81,0xee,0x37,0x63,0x28,0x7b,0x6a,0x9a,0xb0,0xa0,0xb9,0x51,0x4d,0x75,0x6d,0xc0,0xd0,0x84,0xe7,0xe9,0x99,0x61,0xf1,0x60,0x58,0x8b,0xaf,0x4f,0xc5,0xcb,0x59,0xbb,0x55,0xa5,0x2b,0xa7,0xe6,0xfd,0xc3,0x45,0xf0,0x67,0xc9,0xc1,0x6b,0x3,0x76,0xfb,0xef,0x44,0x1d,0x52,0x8a,0x5f,0x33,0x97,0x25,0x83,0x26,0x7a,0xcc,0x27,0x4c,0x20,0x14,0xf8,0xb4,0xdf,0xc4,0xa,0xd5,0x21,0x48,0xe2,0x53,0xab,0x91,0x49,0x1f,0xda,0x90,0x8,0xb8,0xaa,0xcf,0xf2,0xf3,0xcd,0x89,0xdb,0x4e,0x7,0xac,0x2c,0x7c,0x72,0x5a,0xe1,0xd7,0xdc,0x54,0x29,0x5e,0x68,0x1c,0x31,0x40,0x2a,0x16,0x39,0x43,0x87,0x4a,0xeb,0x1b,0xf5,0xd6,0x86,0x1,0xbe,0x6e,0xa4,0x5c,0x9d,0xdd,0x6,0x70,0x93,0x17,0xc8,0x8d,0x10,0xd8,0x47,0xa2,0xae,0xf4,0xa1,0xd2,0x5d,0x6c,0x42,0x94,0x24,0x88,0x77,0x30,0x8c,0x2f,0x2d,0x64,0x1a,0xbd,0xd1,0x62,0x22,0xe0,0x95,0xe,0xb5,0x3a,0xfa,0xc,0xf6,0xf0,0xf8,0x65,0xc9,0x92,0x15,0xaa,0x33,0x41,0x3b,0x17,0x80,0x7f,0x5e,0x63,0xef,0x5b,0x47,0x3f,0x23,0xbc,0x42,0xf9,0x1e,0x4,0xb,0x29,0x4e,0xf4,0x84,0x83,0x2,0x66,0xfd,0xe3,0xbb,0x94,0xf2,0x54,0x3a,0x82,0x8e,0x3,0x8c,0x2e,0xcb,0x55,0x19,0xe8,0x72,0xe4,0xee,0xc8,0xa6,0x61,0x4b,0xad,0xe5,0x11,0x9b,0xb6,0x89,0x62,0xa8,0x46,0xd6,0x58,0x38,0xb2,0xa4,0x36,0xa5,0x9d,0x52,0x76,0x64,0x14,0xc,0x9c,0x2d,0x3d,0x1a,0x79,0xb0,0xac,0x90,0x88,0x4d,0x67,0x44,0x5d,0xd5,0x9e,0x97,0x86,0x7c,0x4c,0xca,0x13,0x18,0x50,0xc0,0x6b,0x73,0x78,0xc6,0x85,0x24,0x37,0xc5,0x98,0xff,0xa,0xc2,0xcf,0xec,0x10,0x4f,0xde,0xd3,0xc1,0xab,0x4a,0x6f,0x1,0xc3,0x7d,0xa7,0x8f,0x2a,0x1c,0x51,0xfa,0x81,0xd1,0x74,0x30,0xb3,0x26,0x32,0x57,0xe,0xf,0x6d,0x27,0x45,0xf5,0x6c,0x56,0xe2,0xb4,0xb5,0xdc,0xae,0x1f,0x39,0x22,0x28,0xf7,0xe9,0xdd,0x49,0x5,0x31,0x87,0xb1,0xda,0xd8,0x6a,0xdb,0x7e,0x77,0xaf,0xce,0xa2,0x12,0x6,0xe0,0xb9,0x96,0x3c,0x8b,0xfe,0xd,0xb8,0x34,0x9a,0x1b,0x5a,0x3e,0x0,0xc7,0x48,0xf1,0x7,0x1d,0xdf,0xf3,0x68,0x40,0xe7,0x9f,0x2c,0xd2,0x71,0x99,0xd0,0x75,0xd9,0xcd,0x8a,0x91,0xa0,0x69,0xbf,0x9,0x53,0x2f,0x5c,0x25,0xed,0x5f,0xba,0xea,0x6e,0x70,0x35,0x20,0x60,0x8d,0xfb,0x93,0x43,0xa1,0x59,0x2b,0x8,0xfc,0x7b,0xb7,0x7a,0xe6,0x16,0xeb,0xd7,0xbe,0xc4,0xe1,0x95,0xbd,0xcc,0xa9,0x21,0xa3,0xd4,0x49,0x8e,0x1,0x27,0x9d,0xb,0xf6,0x7,0x66,0x8d,0x74,0x59,0xa,0xfe,0xa4,0x42,0x1d,0xbb,0x54,0x7b,0x12,0xc,0xed,0x89,0x24,0xba,0x63,0xc1,0x61,0xec,0xd5,0x6d,0xad,0x16,0xcc,0x53,0xa8,0xd0,0x0,0xb4,0x6b,0x6c,0xa1,0x1b,0xe4,0xc6,0xf1,0xeb,0xfa,0x45,0x26,0x7d,0x17,0x8a,0x19,0x1f,0xb1,0x8c,0x6f,0x90,0xd4,0xf8,0xdc,0xae,0xa0,0x31,0x3,0xff,0x2d,0x20,0x10,0xe5,0x2c,0x92,0x80,0xee,0x44,0xa5,0x3c,0x2e,0x2f,0x84,0xf7,0xbf,0x25,0xfc,0x93,0xa3,0x2a,0x77,0xcb,0xd8,0x29,0x6a,0x9c,0x97,0x7f,0x67,0x5f,0x43,0xf5,0x96,0xc2,0xd2,0x78,0x69,0x3a,0x71,0xab,0xb2,0xa2,0x88,0x4b,0xd9,0xd7,0x5d,0x39,0xb7,0x47,0xa9,0xe3,0x73,0x8b,0xfb,0xbd,0x99,0x4a,0x72,0x64,0x11,0x79,0xd3,0xf,0x56,0xfd,0xe9,0xd1,0xef,0xf4,0xb5,0xdb,0x75,0xe2,0x57,0x5e,0x35,0xde,0x68,0xa6,0xea,0x6,0x32,0x21,0x4d,0x98,0x40,0x34,0x91,0x37,0x85,0xd,0x5b,0x83,0xb9,0xaa,0x1a,0x82,0xc8,0xc7,0x18,0xd6,0xcd,0x41,0xf0,0x5a,0x33,0x6e,0x3e,0xbe,0x15,0xc5,0xf3,0x48,0x60,0xe1,0xe0,0xdd,0xb8,0x5c,0xc9,0x9b,0xdf,0x51,0x2b,0x4,0x38,0x9,0xf9,0x58,0x95,0x4c,0x3b,0x46,0xce,0x52,0x23,0xe,0x7a,0x62,0x14,0xcf,0x8f,0x9f,0xda,0x5,0x81,0x13,0x94,0xc4,0xe7,0x4e,0xb6,0x7c,0xac,0x86,0x50,0x7e,0x4f,0x22,0x65,0x9a,0x36,0xb0,0x55,0xca,0x2,0xc0,0xb3,0xe6,0xbc,0x1c,0x87,0xf2,0x30,0x1e,0xe8,0x28,0xa7,0x76,0x3f,0x3d,0x9e,0x70,0xc3,0xaf,0x8,0x7a,0xdc,0x33,0x1c,0x75,0x6b,0x8a,0xee,0x43,0xdd,0x4,0xa6,0x6,0x8b,0xb2,0xa,0x2e,0xe9,0x66,0x40,0xfa,0x6c,0x91,0x60,0x1,0xea,0x13,0x3e,0x6d,0x99,0xc3,0x25,0x9d,0x22,0x41,0x1a,0x70,0xed,0x7e,0x78,0xd6,0xeb,0x8,0xf7,0xb3,0x9f,0xbb,0xc9,0xca,0x71,0xab,0x34,0xcf,0xb7,0x67,0xd3,0xc,0xb,0xc6,0x7c,0x83,0xa1,0x96,0x8c,0x48,0xe3,0x90,0xd8,0x42,0x9b,0xf4,0xc4,0x4d,0x10,0xac,0xbf,0x4e,0xd,0xfb,0xf0,0xc7,0x56,0x64,0x98,0x4a,0x47,0x77,0x82,0x4b,0xf5,0xe7,0x89,0x23,0xc2,0x5b,0x49,0x2c,0xbe,0xb0,0x3a,0x5e,0xd0,0x20,0xce,0x84,0x14,0xec,0x9c,0xda,0xfe,0x2d,0x15,0x18,0x0,0x38,0x24,0x92,0xf1,0xa5,0xb5,0x1f,0xe,0x5d,0x16,0xcc,0xd5,0xc5,0xef,0x39,0x52,0xb9,0xf,0xc1,0x8d,0x61,0x55,0x46,0x2a,0xff,0x27,0x53,0xf6,0x50,0xe2,0x3,0x76,0x1e,0xb4,0x68,0x31,0x9a,0x8e,0xb6,0x88,0x93,0xd2,0xbc,0x12,0x85,0x30,0x9,0x59,0xd9,0x72,0xa2,0x94,0x2f,0x7,0x86,0x87,0xba,0xdf,0x3b,0xae,0xfc,0xb8,0x6a,0x3c,0xe4,0xde,0xcd,0x7d,0xe5,0xaf,0xa0,0x7f,0xb1,0xaa,0x26,0x97,0x3d,0x54,0x5,0x73,0xa8,0xe8,0xf8,0xbd,0x62,0xe6,0x74,0xf3,0xa3,0x80,0x29,0xd1,0x1b,0xcb,0x36,0x4c,0x63,0x5f,0x6e,0x9e,0x3f,0xf2,0x2b,0x5c,0x21,0xa9,0x35,0x44,0x69,0x1d,0x7b,0xe0,0x95,0x57,0x79,0x8f,0x4f,0xc0,0x11,0x58,0x5a,0xf9,0x17,0xa4,0xc8,0x6f,0xe1,0x37,0x19,0x28,0x45,0x2,0xfd,0x51,0xd7,0x32,0xad,0x65,0xa7,0xd4,0x81,0xdb,0xa4,0x89,0x70,0x9b,0xbf,0x59,0x3,0xf7,0xda,0xfc,0x73,0xb4,0xfa,0xb,0xf6,0x60,0x3c,0x9e,0x47,0xd9,0x90,0x28,0x11,0x9c,0x86,0xa9,0x46,0xe0,0x74,0x10,0xf1,0xef,0xe6,0x5c,0x91,0x96,0x16,0xc,0x3b,0x19,0xae,0x31,0xeb,0x50,0x49,0xfd,0x2d,0x55,0x6d,0x92,0x71,0x4c,0x53,0x21,0x5,0x29,0x80,0xdb,0xb8,0x7,0xe2,0xe4,0x77,0xea,0x13,0x7d,0x6f,0xd1,0xd3,0xc1,0x58,0xb9,0x2,0xfe,0xcc,0x5d,0x18,0xed,0xdd,0xd0,0x25,0x36,0x8a,0xd7,0x6a,0x61,0x97,0xd4,0x42,0xa,0x79,0xd2,0x5e,0x6e,0x1,0xd8,0x8c,0xc7,0x94,0x85,0x75,0x5f,0x4f,0x56,0xbe,0xa2,0x9a,0x82,0x2f,0x3f,0x6b,0x8,0x6,0x76,0x8e,0x1e,0x8f,0xb7,0x64,0x40,0xa0,0x2a,0x24,0xb6,0x54,0xba,0x4a,0xc4,0x48,0x9,0x12,0x2c,0xaa,0x1f,0x88,0x26,0x2e,0x84,0xec,0x99,0x14,0x0,0xab,0xf2,0xbd,0x65,0xb0,0xdc,0x78,0xca,0x6c,0xc9,0x95,0x23,0xc8,0xa3,0xcf,0xfb,0x17,0x5b,0x30,0x2b,0xe5,0x3a,0xce,0xa7,0xd,0xbc,0x44,0x7e,0xa6,0xf0,0x35,0x7f,0xe7,0x57,0x45,0x20,0x1d,0x1c,0x22,0x66,0x34,0xa1,0xe8,0x43,0xc3,0x93,0x9d,0xb5,0xe,0x38,0x33,0xbb,0xc6,0xb1,0x87,0xf3,0xde,0xaf,0xc5,0xf9,0xd6,0xac,0x68,0xa5,0x4,0xf4,0x1a,0x39,0x69,0xee,0x51,0x81,0x4b,0xb3,0x72,0x32,0xe9,0x9f,0x7c,0xf8,0x27,0x62,0xff,0x37,0xa8,0x4d,0x41,0x1b,0x4e,0x3d,0xb2,0x83,0xad,0x7b,0xcb,0x67,0x98,0xdf,0x63,0xc0,0xc2,0x8b,0xf5,0x52,0x3e,0x8d,0xcd,0xf,0x7a,0xe1,0x5a,0xd5,0x15,0xe3,0x8a,0x8c,0x84,0x19,0xb5,0xee,0x69,0xd6,0x4f,0x3d,0x47,0x6b,0xfc,0x3,0x22,0x1f,0x93,0x27,0x3b,0x43,0x5f,0xc0,0x3e,0x85,0x62,0x78,0x77,0x55,0x32,0x88,0xf8,0xff,0x7e,0x1a,0x81,0x9f,0xc7,0xe8,0x8e,0x28,0x46,0xfe,0xf2,0x7f,0xf0,0x52,0xb7,0x29,0x65,0x94,0xe,0x98,0x92,0xb4,0xda,0x1d,0x37,0xd1,0x99,0x6d,0xe7,0xca,0xf5,0x1e,0xd4,0x3a,0xaa,0x24,0x44,0xce,0xd8,0x4a,0xd9,0xe1,0x2e,0xa,0x18,0x68,0x70,0xe0,0x51,0x41,0x66,0x5,0xcc,0xd0,0xec,0xf4,0x31,0x1b,0x38,0x21,0xa9,0xe2,0xeb,0xfa,0x0,0x30,0xb6,0x6f,0x64,0x2c,0xbc,0x17,0xf,0x4,0xba,0xf9,0x58,0x4b,0xb9,0xe4,0x83,0x76,0xbe,0xb3,0x90,0x6c,0x33,0xa2,0xaf,0xbd,0xd7,0x36,0x13,0x7d,0xbf,0x1,0xdb,0xf3,0x56,0x60,0x2d,0x86,0xfd,0xad,0x8,0x4c,0xcf,0x5a,0x4e,0x2b,0x72,0x73,0x11,0x5b,0x39,0x89,0x10,0x2a,0x9e,0xc8,0xc9,0xa0,0xd2,0x63,0x45,0x5e,0x54,0x8b,0x95,0xa1,0x35,0x79,0x4d,0xfb,0xcd,0xa6,0xa4,0x16,0xa7,0x2,0xb,0xd3,0xb2,0xde,0x6e,0x7a,0x9c,0xc5,0xea,0x40,0xf7,0x82,0x71,0xc4,0x48,0xe6,0x67,0x26,0x42,0x7c,0xbb,0x34,0x8d,0x7b,0x61,0xa3,0x8f,0x14,0x3c,0x9b,0xe3,0x50,0xae,0xd,0xe5,0xac,0x9,0xa5,0xb1,0xf6,0xed,0xdc,0x15,0xc3,0x75,0x2f,0x53,0x20,0x59,0x91,0x23,0xc6,0x96,0x12,0xc,0x49,0x5c,0x1c,0xf1,0x87,0xef,0x3f,0xdd,0x25,0x57,0x74,0x80,0x7,0xcb,0x6,0x9a,0x6a,0x97,0xab,0xc2,0xb8,0x9d,0xe9,0xc1,0xb0,0xd5,0x5d,0xdf,0xa8,0xec,0x2b,0xa4,0x82,0x38,0xae,0x53,0xa2,0xc3,0x28,0xd1,0xfc,0xaf,0x5b,0x1,0xe7,0xb8,0x1e,0xf1,0xde,0xb7,0xa9,0x48,0x2c,0x81,0x1f,0xc6,0x64,0xc4,0x49,0x70,0xc8,0x8,0xb3,0x69,0xf6,0xd,0x75,0xa5,0x11,0xce,0xc9,0x4,0xbe,0x41,0x63,0x54,0x4e,0x5f,0xe0,0x83,0xd8,0xb2,0x2f,0xbc,0xba,0x14,0x29,0xca,0x35,0x71,0x5d,0x79,0xb,0x5,0x94,0xa6,0x5a,0x88,0x85,0xb5,0x40,0x89,0x37,0x25,0x4b,0xe1,0x0,0x99,0x8b,0x8a,0x21,0x52,0x1a,0x80,0x59,0x36,0x6,0x8f,0xd2,0x6e,0x7d,0x8c,0xcf,0x39,0x32,0xda,0xc2,0xfa,0xe6,0x50,0x33,0x67,0x77,0xdd,0xcc,0x9f,0xd4,0xe,0x17,0x7,0x2d,0xee,0x7c,0x72,0xf8,0x9c,0x12,0xe2,0xc,0x46,0xd6,0x2e,0x5e,0x18,0x3c,0xef,0xd7,0xc1,0xb4,0xdc,0x76,0xaa,0xf3,0x58,0x4c,0x74,0x4a,0x51,0x10,0x7e,0xd0,0x47,0xf2,0xfb,0x90,0x7b,0xcd,0x3,0x4f,0xa3,0x97,0x84,0xe8,0x3d,0xe5,0x91,0x34,0x92,0x20,0xa8,0xfe,0x26,0x1c,0xf,0xbf,0x27,0x6d,0x62,0xbd,0x73,0x68,0xe4,0x55,0xff,0x96,0xcb,0x9b,0x1b,0xb0,0x60,0x56,0xed,0xc5,0x44,0x45,0x78,0x1d,0xf9,0x6c,0x3e,0x7a,0xf4,0x8e,0xa1,0x9d,0xac,0x5c,0xfd,0x30,0xe9,0x9e,0xe3,0x6b,0xf7,0x86,0xab,0xdf,0xc7,0xb1,0x6a,0x2a,0x3a,0x7f,0xa0,0x24,0xb6,0x31,0x61,0x42,0xeb,0x13,0xd9,0x9,0x23,0xf5,0xdb,0xea,0x87,0xc0,0x3f,0x93,0x15,0xf0,0x6f,0xa7,0x65,0x16,0x43,0x19,0xb9,0x22,0x57,0x95,0xbb,0x4d,0x8d,0x2,0xd3,0x9a,0x98,0x3b,0xd5,0x66,0xa,0xad,0xaa,0xc,0xe3,0xcc,0xa5,0xbb,0x5a,0x3e,0x93,0xd,0xd4,0x76,0xd6,0x5b,0x62,0xda,0xfe,0x39,0xb6,0x90,0x2a,0xbc,0x41,0xb0,0xd1,0x3a,0xc3,0xee,0xbd,0x49,0x13,0xf5,0x4d,0xf2,0x91,0xca,0xa0,0x3d,0xae,0xa8,0x6,0x3b,0xd8,0x27,0x63,0x4f,0x6b,0x19,0x1a,0xa1,0x7b,0xe4,0x1f,0x67,0xb7,0x3,0xdc,0xdb,0x16,0xac,0x53,0x71,0x46,0x5c,0x98,0x33,0x40,0x8,0x92,0x4b,0x24,0x14,0x9d,0xc0,0x7c,0x6f,0x9e,0xdd,0x2b,0x20,0x17,0x86,0xb4,0x48,0x9a,0x97,0xa7,0x52,0x9b,0x25,0x37,0x59,0xf3,0x12,0x8b,0x99,0xfc,0x6e,0x60,0xea,0x8e,0x0,0xf0,0x1e,0x54,0xc4,0x3c,0x4c,0xa,0x2e,0xfd,0xc5,0xc8,0xd0,0xe8,0xf4,0x42,0x21,0x75,0x65,0xcf,0xde,0x8d,0xc6,0x1c,0x5,0x15,0x3f,0xe9,0x82,0x69,0xdf,0x11,0x5d,0xb1,0x85,0x96,0xfa,0x2f,0xf7,0x83,0x26,0x80,0x32,0xd3,0xa6,0xce,0x64,0xb8,0xe1,0x4a,0x5e,0x66,0x58,0x43,0x2,0x6c,0xc2,0x55,0xe0,0xd9,0x89,0x9,0xa2,0x72,0x44,0xff,0xd7,0x56,0x57,0x6a,0xf,0xeb,0x7e,0x2c,0x68,0xba,0xec,0x34,0xe,0x1d,0xad,0x35,0x7f,0x70,0xaf,0x61,0x7a,0xf6,0x47,0xed,0x84,0xd5,0xa3,0x78,0x38,0x28,0x6d,0xb2,0x36,0xa4,0x23,0x73,0x50,0xf9,0x1,0xcb,0x1b,0xe6,0x9c,0xb3,0x8f,0xbe,0x4e,0xef,0x22,0xfb,0x8c,0xf1,0x79,0xe5,0x94,0xb9,0xcd,0xab,0x30,0x45,0x87,0xa9,0x5f,0x9f,0x10,0xc1,0x88,0x8a,0x29,0xc7,0x74,0x18,0xbf,0x31,0xe7,0xc9,0xf8,0x95,0xd2,0x2d,0x81,0x7,0xe2,0x7d,0xb5,0x77,0x4,0x51,0xb,0x53,0x7e,0x87,0x6c,0x48,0xae,0xf4,0x0,0x2d,0xb,0x84,0x43,0xd,0xfc,0x1,0x97,0xcb,0x69,0xb0,0x2e,0x67,0xdf,0xe6,0x6b,0x71,0x5e,0xb1,0x17,0x83,0xe7,0x6,0x18,0x11,0xab,0x66,0x61,0xe1,0xfb,0xcc,0xee,0x59,0xc6,0x1c,0xa7,0xbe,0xa,0xda,0xa2,0x9a,0x65,0x86,0xbb,0xa4,0xd6,0xf2,0xde,0x77,0x2c,0x4f,0xf0,0x15,0x13,0x80,0x1d,0xe4,0x8a,0x98,0x26,0x24,0x36,0xaf,0x4e,0xf5,0x9,0x3b,0xaa,0xef,0x1a,0x2a,0x27,0xd2,0xc1,0x7d,0x20,0x9d,0x96,0x60,0x23,0xb5,0xfd,0x8e,0x25,0xa9,0x99,0xf6,0x2f,0x7b,0x30,0x63,0x72,0x82,0xa8,0xb8,0xa1,0x49,0x55,0x6d,0x75,0xd8,0xc8,0x9c,0xff,0xf1,0x81,0x79,0xe9,0x78,0x40,0x93,0xb7,0x57,0xdd,0xd3,0x41,0xa3,0x4d,0xbd,0x33,0xbf,0xfe,0xe5,0xdb,0x5d,0xe8,0x7f,0xd1,0xd9,0x73,0x1b,0x6e,0xe3,0xf7,0x5c,0x5,0x4a,0x92,0x47,0x2b,0x8f,0x3d,0x9b,0x3e,0x62,0xd4,0x3f,0x54,0x38,0xc,0xe0,0xac,0xc7,0xdc,0x12,0xcd,0x39,0x50,0xfa,0x4b,0xb3,0x89,0x51,0x7,0xc2,0x88,0x10,0xa0,0xb2,0xd7,0xea,0xeb,0xd5,0x91,0xc3,0x56,0x1f,0xb4,0x34,0x64,0x6a,0x42,0xf9,0xcf,0xc4,0x4c,0x31,0x46,0x70,0x4,0x29,0x58,0x32,0xe,0x21,0x5b,0x9f,0x52,0xf3,0x3,0xed,0xce,0x9e,0x19,0xa6,0x76,0xbc,0x44,0x85,0xc5,0x1e,0x68,0x8b,0xf,0xd0,0x95,0x8,0xc0,0x5f,0xba,0xb6,0xec,0xb9,0xca,0x45,0x74,0x5a,0x8c,0x3c,0x90,0x6f,0x28,0x94,0x37,0x35,0x7c,0x2,0xa5,0xc9,0x7a,0x3a,0xf8,0x8d,0x16,0xad,0x22,0xe2,0x14,0xc6,0xc0,0xc8,0x55,0xf9,0xa2,0x25,0x9a,0x3,0x71,0xb,0x27,0xb0,0x4f,0x6e,0x53,0xdf,0x6b,0x77,0xf,0x13,0x8c,0x72,0xc9,0x2e,0x34,0x3b,0x19,0x7e,0xc4,0xb4,0xb3,0x32,0x56,0xcd,0xd3,0x8b,0xa4,0xc2,0x64,0xa,0xb2,0xbe,0x33,0xbc,0x1e,0xfb,0x65,0x29,0xd8,0x42,0xd4,0xde,0xf8,0x96,0x51,0x7b,0x9d,0xd5,0x21,0xab,0x86,0xb9,0x52,0x98,0x76,0xe6,0x68,0x8,0x82,0x94,0x6,0x95,0xad,0x62,0x46,0x54,0x24,0x3c,0xac,0x1d,0xd,0x2a,0x49,0x80,0x9c,0xa0,0xb8,0x7d,0x57,0x74,0x6d,0xe5,0xae,0xa7,0xb6,0x4c,0x7c,0xfa,0x23,0x28,0x60,0xf0,0x5b,0x43,0x48,0xf6,0xb5,0x14,0x7,0xf5,0xa8,0xcf,0x3a,0xf2,0xff,0xdc,0x20,0x7f,0xee,0xe3,0xf1,0x9b,0x7a,0x5f,0x31,0xf3,0x4d,0x97,0xbf,0x1a,0x2c,0x61,0xca,0xb1,0xe1,0x44,0x0,0x83,0x16,0x2,0x67,0x3e,0x3f,0x5d,0x17,0x75,0xc5,0x5c,0x66,0xd2,0x84,0x85,0xec,0x9e,0x2f,0x9,0x12,0x18,0xc7,0xd9,0xed,0x79,0x35,0x1,0xb7,0x81,0xea,0xe8,0x5a,0xeb,0x4e,0x47,0x9f,0xfe,0x92,0x22,0x36,0xd0,0x89,0xa6,0xc,0xbb,0xce,0x3d,0x88,0x4,0xaa,0x2b,0x6a,0xe,0x30,0xf7,0x78,0xc1,0x37,0x2d,0xef,0xc3,0x58,0x70,0xd7,0xaf,0x1c,0xe2,0x41,0xa9,0xe0,0x45,0xe9,0xfd,0xba,0xa1,0x90,0x59,0x8f,0x39,0x63,0x1f,0x6c,0x15,0xdd,0x6f,0x8a,0xda,0x5e,0x40,0x5,0x10,0x50,0xbd,0xcb,0xa3,0x73,0x91,0x69,0x1b,0x38,0xcc,0x4b,0x87,0x4a,0xd6,0x26,0xdb,0xe7,0x8e,0xf4,0xd1,0xa5,0x8d,0xfc,0x99,0x11,0x93,0xe4,0x79,0xbe,0x31,0x17,0xad,0x3b,0xc6,0x37,0x56,0xbd,0x44,0x69,0x3a,0xce,0x94,0x72,0x2d,0x8b,0x64,0x4b,0x22,0x3c,0xdd,0xb9,0x14,0x8a,0x53,0xf1,0x51,0xdc,0xe5,0x5d,0x9d,0x26,0xfc,0x63,0x98,0xe0,0x30,0x84,0x5b,0x5c,0x91,0x2b,0xd4,0xf6,0xc1,0xdb,0xca,0x75,0x16,0x4d,0x27,0xba,0x29,0x2f,0x81,0xbc,0x5f,0xa0,0xe4,0xc8,0xec,0x9e,0x90,0x1,0x33,0xcf,0x1d,0x10,0x20,0xd5,0x1c,0xa2,0xb0,0xde,0x74,0x95,0xc,0x1e,0x1f,0xb4,0xc7,0x8f,0x15,0xcc,0xa3,0x93,0x1a,0x47,0xfb,0xe8,0x19,0x5a,0xac,0xa7,0x4f,0x57,0x6f,0x73,0xc5,0xa6,0xf2,0xe2,0x48,0x59,0xa,0x41,0x9b,0x82,0x92,0xb8,0x7b,0xe9,0xe7,0x6d,0x9,0x87,0x77,0x99,0xd3,0x43,0xbb,0xcb,0x8d,0xa9,0x7a,0x42,0x54,0x21,0x49,0xe3,0x3f,0x66,0xcd,0xd9,0xe1,0xdf,0xc4,0x85,0xeb,0x45,0xd2,0x67,0x6e,0x5,0xee,0x58,0x96,0xda,0x36,0x2,0x11,0x7d,0xa8,0x70,0x4,0xa1,0x7,0xb5,0x3d,0x6b,0xb3,0x89,0x9a,0x2a,0xb2,0xf8,0xf7,0x28,0xe6,0xfd,0x71,0xc0,0x6a,0x3,0x5e,0xe,0x8e,0x25,0xf5,0xc3,0x78,0x50,0xd1,0xd0,0xed,0x88,0x6c,0xf9,0xab,0xef,0x61,0x1b,0x34,0x8,0x39,0xc9,0x68,0xa5,0x7c,0xb,0x76,0xfe,0x62,0x13,0x3e,0x4a,0x52,0x24,0xff,0xbf,0xaf,0xea,0x35,0xb1,0x23,0xa4,0xf4,0xd7,0x7e,0x86,0x4c,0x9c,0xb6,0x60,0x4e,0x7f,0x12,0x55,0xaa,0x6,0x80,0x65,0xfa,0x32,0xf0,0x83,0xd6,0x8c,0x2c,0xb7,0xc2,0x0,0x2e,0xd8,0x18,0x97,0x46,0xf,0xd,0xae,0x40,0xf3,0x9f,0x38,0xc6,0x60,0x8f,0xa0,0xc9,0xd7,0x36,0x52,0xff,0x61,0xb8,0x1a,0xba,0x37,0xe,0xb6,0x92,0x55,0xda,0xfc,0x46,0xd0,0x2d,0xdc,0xbd,0x56,0xaf,0x82,0xd1,0x25,0x7f,0x99,0x21,0x9e,0xfd,0xa6,0xcc,0x51,0xc2,0xc4,0x6a,0x57,0xb4,0x4b,0xf,0x23,0x7,0x75,0x76,0xcd,0x17,0x88,0x73,0xb,0xdb,0x6f,0xb0,0xb7,0x7a,0xc0,0x3f,0x1d,0x2a,0x30,0xf4,0x5f,0x2c,0x64,0xfe,0x27,0x48,0x78,0xf1,0xac,0x10,0x3,0xf2,0xb1,0x47,0x4c,0x7b,0xea,0xd8,0x24,0xf6,0xfb,0xcb,0x3e,0xf7,0x49,0x5b,0x35,0x9f,0x7e,0xe7,0xf5,0x90,0x2,0xc,0x86,0xe2,0x6c,0x9c,0x72,0x38,0xa8,0x50,0x20,0x66,0x42,0x91,0xa9,0xa4,0xbc,0x84,0x98,0x2e,0x4d,0x19,0x9,0xa3,0xb2,0xe1,0xaa,0x70,0x69,0x79,0x53,0x85,0xee,0x5,0xb3,0x7d,0x31,0xdd,0xe9,0xfa,0x96,0x43,0x9b,0xef,0x4a,0xec,0x5e,0xbf,0xca,0xa2,0x8,0xd4,0x8d,0x26,0x32,0xa,0x34,0x2f,0x6e,0x0,0xae,0x39,0x8c,0xb5,0xe5,0x65,0xce,0x1e,0x28,0x93,0xbb,0x3a,0x3b,0x6,0x63,0x87,0x12,0x40,0x4,0xd6,0x80,0x58,0x62,0x71,0xc1,0x59,0x13,0x1c,0xc3,0xd,0x16,0x9a,0x2b,0x81,0xe8,0xb9,0xcf,0x14,0x54,0x44,0x1,0xde,0x5a,0xc8,0x4f,0x1f,0x3c,0x95,0x6d,0xa7,0x77,0x8a,0xf0,0xdf,0xe3,0xd2,0x22,0x83,0x4e,0x97,0xe0,0x9d,0x15,0x89,0xf8,0xd5,0xa1,0xc7,0x5c,0x29,0xeb,0xc5,0x33,0xf3,0x7c,0xad,0xe4,0xe6,0x45,0xab,0x18,0x74,0xd3,0x5d,0x8b,0xa5,0x94,0xf9,0xbe,0x41,0xed,0x6b,0x8e,0x11,0xd9,0x1b,0x68,0x3d,0x67,0x7c,0x51,0xa8,0x43,0x67,0x81,0xdb,0x2f,0x2,0x24,0xab,0x6c,0x22,0xd3,0x2e,0xb8,0xe4,0x46,0x9f,0x1,0x48,0xf0,0xc9,0x44,0x5e,0x71,0x9e,0x38,0xac,0xc8,0x29,0x37,0x3e,0x84,0x49,0x4e,0xce,0xd4,0xe3,0xc1,0x76,0xe9,0x33,0x88,0x91,0x25,0xf5,0x8d,0xb5,0x4a,0xa9,0x94,0x8b,0xf9,0xdd,0xf1,0x58,0x3,0x60,0xdf,0x3a,0x3c,0xaf,0x32,0xcb,0xa5,0xb7,0x9,0xb,0x19,0x80,0x61,0xda,0x26,0x14,0x85,0xc0,0x35,0x5,0x8,0xfd,0xee,0x52,0xf,0xb2,0xb9,0x4f,0xc,0x9a,0xd2,0xa1,0xa,0x86,0xb6,0xd9,0x0,0x54,0x1f,0x4c,0x5d,0xad,0x87,0x97,0x8e,0x66,0x7a,0x42,0x5a,0xf7,0xe7,0xb3,0xd0,0xde,0xae,0x56,0xc6,0x57,0x6f,0xbc,0x98,0x78,0xf2,0xfc,0x6e,0x8c,0x62,0x92,0x1c,0x90,0xd1,0xca,0xf4,0x72,0xc7,0x50,0xfe,0xf6,0x5c,0x34,0x41,0xcc,0xd8,0x73,0x2a,0x65,0xbd,0x68,0x4,0xa0,0x12,0xb4,0x11,0x4d,0xfb,0x10,0x7b,0x17,0x23,0xcf,0x83,0xe8,0xf3,0x3d,0xe2,0x16,0x7f,0xd5,0x64,0x9c,0xa6,0x7e,0x28,0xed,0xa7,0x3f,0x8f,0x9d,0xf8,0xc5,0xc4,0xfa,0xbe,0xec,0x79,0x30,0x9b,0x1b,0x4b,0x45,0x6d,0xd6,0xe0,0xeb,0x63,0x1e,0x69,0x5f,0x2b,0x6,0x77,0x1d,0x21,0xe,0x74,0xb0,0x7d,0xdc,0x2c,0xc2,0xe1,0xb1,0x36,0x89,0x59,0x93,0x6b,0xaa,0xea,0x31,0x47,0xa4,0x20,0xff,0xba,0x27,0xef,0x70,0x95,0x99,0xc3,0x96,0xe5,0x6a,0x5b,0x75,0xa3,0x13,0xbf,0x40,0x7,0xbb,0x18,0x1a,0x53,0x2d,0x8a,0xe6,0x55,0x15,0xd7,0xa2,0x39,0x82,0xd,0xcd,0x3b,0x34,0x32,0x3a,0xa7,0xb,0x50,0xd7,0x68,0xf1,0x83,0xf9,0xd5,0x42,0xbd,0x9c,0xa1,0x2d,0x99,0x85,0xfd,0xe1,0x7e,0x80,0x3b,0xdc,0xc6,0xc9,0xeb,0x8c,0x36,0x46,0x41,0xc0,0xa4,0x3f,0x21,0x79,0x56,0x30,0x96,0xf8,0x40,0x4c,0xc1,0x4e,0xec,0x9,0x97,0xdb,0x2a,0xb0,0x26,0x2c,0xa,0x64,0xa3,0x89,0x6f,0x27,0xd3,0x59,0x74,0x4b,0xa0,0x6a,0x84,0x14,0x9a,0xfa,0x70,0x66,0xf4,0x67,0x5f,0x90,0xb4,0xa6,0xd6,0xce,0x5e,0xef,0xff,0xd8,0xbb,0x72,0x6e,0x52,0x4a,0x8f,0xa5,0x86,0x9f,0x17,0x5c,0x55,0x44,0xbe,0x8e,0x8,0xd1,0xda,0x92,0x2,0xa9,0xb1,0xba,0x4,0x47,0xe6,0xf5,0x7,0x5a,0x3d,0xc8,0x0,0xd,0x2e,0xd2,0x8d,0x1c,0x11,0x3,0x69,0x88,0xad,0xc3,0x1,0xbf,0x65,0x4d,0xe8,0xde,0x93,0x38,0x43,0x13,0xb6,0xf2,0x71,0xe4,0xf0,0x95,0xcc,0xcd,0xaf,0xe5,0x87,0x37,0xae,0x94,0x20,0x76,0x77,0x1e,0x6c,0xdd,0xfb,0xe0,0xea,0x35,0x2b,0x1f,0x8b,0xc7,0xf3,0x45,0x73,0x18,0x1a,0xa8,0x19,0xbc,0xb5,0x6d,0xc,0x60,0xd0,0xc4,0x22,0x7b,0x54,0xfe,0x49,0x3c,0xcf,0x7a,0xf6,0x58,0xd9,0x98,0xfc,0xc2,0x5,0x8a,0x33,0xc5,0xdf,0x1d,0x31,0xaa,0x82,0x25,0x5d,0xee,0x10,0xb3,0x5b,0x12,0xb7,0x1b,0xf,0x48,0x53,0x62,0xab,0x7d,0xcb,0x91,0xed,0x9e,0xe7,0x2f,0x9d,0x78,0x28,0xac,0xb2,0xf7,0xe2,0xa2,0x4f,0x39,0x51,0x81,0x63,0x9b,0xe9,0xca,0x3e,0xb9,0x75,0xb8,0x24,0xd4,0x29,0x15,0x7c,0x6,0x23,0x57,0x7f,0xe,0x6b,0xe3,0x61,0x16,0xd3,0x14,0x9b,0xbd,0x7,0x91,0x6c,0x9d,0xfc,0x17,0xee,0xc3,0x90,0x64,0x3e,0xd8,0x87,0x21,0xce,0xe1,0x88,0x96,0x77,0x13,0xbe,0x20,0xf9,0x5b,0xfb,0x76,0x4f,0xf7,0x37,0x8c,0x56,0xc9,0x32,0x4a,0x9a,0x2e,0xf1,0xf6,0x3b,0x81,0x7e,0x5c,0x6b,0x71,0x60,0xdf,0xbc,0xe7,0x8d,0x10,0x83,0x85,0x2b,0x16,0xf5,0xa,0x4e,0x62,0x46,0x34,0x3a,0xab,0x99,0x65,0xb7,0xba,0x8a,0x7f,0xb6,0x8,0x1a,0x74,0xde,0x3f,0xa6,0xb4,0xb5,0x1e,0x6d,0x25,0xbf,0x66,0x9,0x39,0xb0,0xed,0x51,0x42,0xb3,0xf0,0x6,0xd,0xe5,0xfd,0xc5,0xd9,0x6f,0xc,0x58,0x48,0xe2,0xf3,0xa0,0xeb,0x31,0x28,0x38,0x12,0xd1,0x43,0x4d,0xc7,0xa3,0x2d,0xdd,0x33,0x79,0xe9,0x11,0x61,0x27,0x3,0xd0,0xe8,0xfe,0x8b,0xe3,0x49,0x95,0xcc,0x67,0x73,0x4b,0x75,0x6e,0x2f,0x41,0xef,0x78,0xcd,0xc4,0xaf,0x44,0xf2,0x3c,0x70,0x9c,0xa8,0xbb,0xd7,0x2,0xda,0xae,0xb,0xad,0x1f,0x97,0xc1,0x19,0x23,0x30,0x80,0x18,0x52,0x5d,0x82,0x4c,0x57,0xdb,0x6a,0xc0,0xa9,0xf4,0xa4,0x24,0x8f,0x5f,0x69,0xd2,0xfa,0x7b,0x7a,0x47,0x22,0xc6,0x53,0x1,0x45,0xcb,0xb1,0x9e,0xa2,0x93,0x63,0xc2,0xf,0xd6,0xa1,0xdc,0x54,0xc8,0xb9,0x94,0xe0,0xf8,0x8e,0x55,0x15,0x5,0x40,0x9f,0x1b,0x89,0xe,0x5e,0x7d,0xd4,0x2c,0xe6,0x36,0x1c,0xca,0xe4,0xd5,0xb8,0xff,0x0,0xac,0x2a,0xcf,0x50,0x98,0x5a,0x29,0x7c,0x26,0x86,0x1d,0x68,0xaa,0x84,0x72,0xb2,0x3d,0xec,0xa5,0xa7,0x4,0xea,0x59,0x35,0x92,0x38,0x9e,0x71,0x5e,0x37,0x29,0xc8,0xac,0x1,0x9f,0x46,0xe4,0x44,0xc9,0xf0,0x48,0x6c,0xab,0x24,0x2,0xb8,0x2e,0xd3,0x22,0x43,0xa8,0x51,0x7c,0x2f,0xdb,0x81,0x67,0xdf,0x60,0x3,0x58,0x32,0xaf,0x3c,0x3a,0x94,0xa9,0x4a,0xb5,0xf1,0xdd,0xf9,0x8b,0x88,0x33,0xe9,0x76,0x8d,0xf5,0x25,0x91,0x4e,0x49,0x84,0x3e,0xc1,0xe3,0xd4,0xce,0xa,0xa1,0xd2,0x9a,0x0,0xd9,0xb6,0x86,0xf,0x52,0xee,0xfd,0xc,0x4f,0xb9,0xb2,0x85,0x14,0x26,0xda,0x8,0x5,0x35,0xc0,0x9,0xb7,0xa5,0xcb,0x61,0x80,0x19,0xb,0x6e,0xfc,0xf2,0x78,0x1c,0x92,0x62,0x8c,0xc6,0x56,0xae,0xde,0x98,0xbc,0x6f,0x57,0x5a,0x42,0x7a,0x66,0xd0,0xb3,0xe7,0xf7,0x5d,0x4c,0x1f,0x54,0x8e,0x97,0x87,0xad,0x7b,0x10,0xfb,0x4d,0x83,0xcf,0x23,0x17,0x4,0x68,0xbd,0x65,0x11,0xb4,0x12,0xa0,0x41,0x34,0x5c,0xf6,0x2a,0x73,0xd8,0xcc,0xf4,0xca,0xd1,0x90,0xfe,0x50,0xc7,0x72,0x4b,0x1b,0x9b,0x30,0xe0,0xd6,0x6d,0x45,0xc4,0xc5,0xf8,0x9d,0x79,0xec,0xbe,0xfa,0x28,0x7e,0xa6,0x9c,0x8f,0x3f,0xa7,0xed,0xe2,0x3d,0xf3,0xe8,0x64,0xd5,0x7f,0x16,0x47,0x31,0xea,0xaa,0xba,0xff,0x20,0xa4,0x36,0xb1,0xe1,0xc2,0x6b,0x93,0x59,0x89,0x74,0xe,0x21,0x1d,0x2c,0xdc,0x7d,0xb0,0x69,0x1e,0x63,0xeb,0x77,0x6,0x2b,0x5f,0x39,0xa2,0xd7,0x15,0x3b,0xcd,0xd,0x82,0x53,0x1a,0x18,0xbb,0x55,0xe6,0x8a,0x2d,0xa3,0x75,0x5b,0x6a,0x7,0x40,0xbf,0x13,0x95,0x70,0xef,0x27,0xe5,0x96,0xc3,0x99,0x99,0xb4,0x4d,0xa6,0x82,0x64,0x3e,0xca,0xe7,0xc1,0x4e,0x89,0xc7,0x36,0xcb,0x5d,0x1,0xa3,0x7a,0xe4,0xad,0x15,0x2c,0xa1,0xbb,0x94,0x7b,0xdd,0x49,0x2d,0xcc,0xd2,0xdb,0x61,0xac,0xab,0x2b,0x31,0x6,0x24,0x93,0xc,0xd6,0x6d,0x74,0xc0,0x10,0x68,0x50,0xaf,0x4c,0x71,0x6e,0x1c,0x38,0x14,0xbd,0xe6,0x85,0x3a,0xdf,0xd9,0x4a,0xd7,0x2e,0x40,0x52,0xec,0xee,0xfc,0x65,0x84,0x3f,0xc3,0xf1,0x60,0x25,0xd0,0xe0,0xed,0x18,0xb,0xb7,0xea,0x57,0x5c,0xaa,0xe9,0x7f,0x37,0x44,0xef,0x63,0x53,0x3c,0xe5,0xb1,0xfa,0xa9,0xb8,0x48,0x62,0x72,0x6b,0x83,0x9f,0xa7,0xbf,0x12,0x2,0x56,0x35,0x3b,0x4b,0xb3,0x23,0xb2,0x8a,0x59,0x7d,0x9d,0x17,0x19,0x8b,0x69,0x87,0x77,0xf9,0x75,0x34,0x2f,0x11,0x97,0x22,0xb5,0x1b,0x13,0xb9,0xd1,0xa4,0x29,0x3d,0x96,0xcf,0x80,0x58,0x8d,0xe1,0x45,0xf7,0x51,0xf4,0xa8,0x1e,0xf5,0x9e,0xf2,0xc6,0x2a,0x66,0xd,0x16,0xd8,0x7,0xf3,0x9a,0x30,0x81,0x79,0x43,0x9b,0xcd,0x8,0x42,0xda,0x6a,0x78,0x1d,0x20,0x21,0x1f,0x5b,0x9,0x9c,0xd5,0x7e,0xfe,0xae,0xa0,0x88,0x33,0x5,0xe,0x86,0xfb,0x8c,0xba,0xce,0xe3,0x92,0xf8,0xc4,0xeb,0x91,0x55,0x98,0x39,0xc9,0x27,0x4,0x54,0xd3,0x6c,0xbc,0x76,0x8e,0x4f,0xf,0xd4,0xa2,0x41,0xc5,0x1a,0x5f,0xc2,0xa,0x95,0x70,0x7c,0x26,0x73,0x0,0x8f,0xbe,0x90,0x46,0xf6,0x5a,0xa5,0xe2,0x5e,0xfd,0xff,0xb6,0xc8,0x6f,0x3,0xb0,0xf0,0x32,0x47,0xdc,0x67,0xe8,0x28,0xde,0xe9,0xef,0xe7,0x7a,0xd6,0x8d,0xa,0xb5,0x2c,0x5e,0x24,0x8,0x9f,0x60,0x41,0x7c,0xf0,0x44,0x58,0x20,0x3c,0xa3,0x5d,0xe6,0x1,0x1b,0x14,0x36,0x51,0xeb,0x9b,0x9c,0x1d,0x79,0xe2,0xfc,0xa4,0x8b,0xed,0x4b,0x25,0x9d,0x91,0x1c,0x93,0x31,0xd4,0x4a,0x6,0xf7,0x6d,0xfb,0xf1,0xd7,0xb9,0x7e,0x54,0xb2,0xfa,0xe,0x84,0xa9,0x96,0x7d,0xb7,0x59,0xc9,0x47,0x27,0xad,0xbb,0x29,0xba,0x82,0x4d,0x69,0x7b,0xb,0x13,0x83,0x32,0x22,0x5,0x66,0xaf,0xb3,0x8f,0x97,0x52,0x78,0x5b,0x42,0xca,0x81,0x88,0x99,0x63,0x53,0xd5,0xc,0x7,0x4f,0xdf,0x74,0x6c,0x67,0xd9,0x9a,0x3b,0x28,0xda,0x87,0xe0,0x15,0xdd,0xd0,0xf3,0xf,0x50,0xc1,0xcc,0xde,0xb4,0x55,0x70,0x1e,0xdc,0x62,0xb8,0x90,0x35,0x3,0x4e,0xe5,0x9e,0xce,0x6b,0x2f,0xac,0x39,0x2d,0x48,0x11,0x10,0x72,0x38,0x5a,0xea,0x73,0x49,0xfd,0xab,0xaa,0xc3,0xb1,0x0,0x26,0x3d,0x37,0xe8,0xf6,0xc2,0x56,0x1a,0x2e,0x98,0xae,0xc5,0xc7,0x75,0xc4,0x61,0x68,0xb0,0xd1,0xbd,0xd,0x19,0xff,0xa6,0x89,0x23,0x94,0xe1,0x12,0xa7,0x2b,0x85,0x4,0x45,0x21,0x1f,0xd8,0x57,0xee,0x18,0x2,0xc0,0xec,0x77,0x5f,0xf8,0x80,0x33,0xcd,0x6e,0x86,0xcf,0x6a,0xc6,0xd2,0x95,0x8e,0xbf,0x76,0xa0,0x16,0x4c,0x30,0x43,0x3a,0xf2,0x40,0xa5,0xf5,0x71,0x6f,0x2a,0x3f,0x7f,0x92,0xe4,0x8c,0x5c,0xbe,0x46,0x34,0x17,0xe3,0x64,0xa8,0x65,0xf9,0x9,0xf4,0xc8,0xa1,0xdb,0xfe,0x8a,0xa2,0xd3,0xb6,0x3e,0xbc,0xcb,0xc3,0x4,0x8b,0xad,0x17,0x81,0x7c,0x8d,0xec,0x7,0xfe,0xd3,0x80,0x74,0x2e,0xc8,0x97,0x31,0xde,0xf1,0x98,0x86,0x67,0x3,0xae,0x30,0xe9,0x4b,0xeb,0x66,0x5f,0xe7,0x27,0x9c,0x46,0xd9,0x22,0x5a,0x8a,0x3e,0xe1,0xe6,0x2b,0x91,0x6e,0x4c,0x7b,0x61,0x70,0xcf,0xac,0xf7,0x9d,0x0,0x93,0x95,0x3b,0x6,0xe5,0x1a,0x5e,0x72,0x56,0x24,0x2a,0xbb,0x89,0x75,0xa7,0xaa,0x9a,0x6f,0xa6,0x18,0xa,0x64,0xce,0x2f,0xb6,0xa4,0xa5,0xe,0x7d,0x35,0xaf,0x76,0x19,0x29,0xa0,0xfd,0x41,0x52,0xa3,0xe0,0x16,0x1d,0xf5,0xed,0xd5,0xc9,0x7f,0x1c,0x48,0x58,0xf2,0xe3,0xb0,0xfb,0x21,0x38,0x28,0x2,0xc1,0x53,0x5d,0xd7,0xb3,0x3d,0xcd,0x23,0x69,0xf9,0x1,0x71,0x37,0x13,0xc0,0xf8,0xee,0x9b,0xf3,0x59,0x85,0xdc,0x77,0x63,0x5b,0x65,0x7e,0x3f,0x51,0xff,0x68,0xdd,0xd4,0xbf,0x54,0xe2,0x2c,0x60,0x8c,0xb8,0xab,0xc7,0x12,0xca,0xbe,0x1b,0xbd,0xf,0x87,0xd1,0x9,0x33,0x20,0x90,0x8,0x42,0x4d,0x92,0x5c,0x47,0xcb,0x7a,0xd0,0xb9,0xe4,0xb4,0x34,0x9f,0x4f,0x79,0xc2,0xea,0x6b,0x6a,0x57,0x32,0xd6,0x43,0x11,0x55,0xdb,0xa1,0x8e,0xb2,0x83,0x73,0xd2,0x1f,0xc6,0xb1,0xcc,0x44,0xd8,0xa9,0x84,0xf0,0xe8,0x9e,0x45,0x5,0x15,0x50,0x8f,0xb,0x99,0x1e,0x4e,0x6d,0xc4,0x3c,0xf6,0x26,0xc,0xda,0xf4,0xc5,0xa8,0xef,0x10,0xbc,0x3a,0xdf,0x40,0x88,0x4a,0x39,0x6c,0x36,0x96,0xd,0x78,0xba,0x94,0x62,0xa2,0x2d,0xfc,0xb5,0xb7,0x14,0xfa,0x49,0x25,0x82,0x59,0xff,0x10,0x3f,0x56,0x48,0xa9,0xcd,0x60,0xfe,0x27,0x85,0x25,0xa8,0x91,0x29,0xd,0xca,0x45,0x63,0xd9,0x4f,0xb2,0x43,0x22,0xc9,0x30,0x1d,0x4e,0xba,0xe0,0x6,0xbe,0x1,0x62,0x39,0x53,0xce,0x5d,0x5b,0xf5,0xc8,0x2b,0xd4,0x90,0xbc,0x98,0xea,0xe9,0x52,0x88,0x17,0xec,0x94,0x44,0xf0,0x2f,0x28,0xe5,0x5f,0xa0,0x82,0xb5,0xaf,0x6b,0xc0,0xb3,0xfb,0x61,0xb8,0xd7,0xe7,0x6e,0x33,0x8f,0x9c,0x6d,0x2e,0xd8,0xd3,0xe4,0x75,0x47,0xbb,0x69,0x64,0x54,0xa1,0x68,0xd6,0xc4,0xaa,0x0,0xe1,0x78,0x6a,0xf,0x9d,0x93,0x19,0x7d,0xf3,0x3,0xed,0xa7,0x37,0xcf,0xbf,0xf9,0xdd,0xe,0x36,0x3b,0x23,0x1b,0x7,0xb1,0xd2,0x86,0x96,0x3c,0x2d,0x7e,0x35,0xef,0xf6,0xe6,0xcc,0x1a,0x71,0x9a,0x2c,0xe2,0xae,0x42,0x76,0x65,0x9,0xdc,0x4,0x70,0xd5,0x73,0xc1,0x20,0x55,0x3d,0x97,0x4b,0x12,0xb9,0xad,0x95,0xab,0xb0,0xf1,0x9f,0x31,0xa6,0x13,0x2a,0x7a,0xfa,0x51,0x81,0xb7,0xc,0x24,0xa5,0xa4,0x99,0xfc,0x18,0x8d,0xdf,0x9b,0x49,0x1f,0xc7,0xfd,0xee,0x5e,0xc6,0x8c,0x83,0x5c,0x92,0x89,0x5,0xb4,0x1e,0x77,0x26,0x50,0x8b,0xcb,0xdb,0x9e,0x41,0xc5,0x57,0xd0,0x80,0xa3,0xa,0xf2,0x38,0xe8,0x15,0x6f,0x40,0x7c,0x4d,0xbd,0x1c,0xd1,0x8,0x7f,0x2,0x8a,0x16,0x67,0x4a,0x3e,0x58,0xc3,0xb6,0x74,0x5a,0xac,0x6c,0xe3,0x32,0x7b,0x79,0xda,0x34,0x87,0xeb,0x4c,0xc2,0x14,0x3a,0xb,0x66,0x21,0xde,0x72,0xf4,0x11,0x8e,0x46,0x84,0xf7,0xa2,0xf8,0x81,0xac,0x55,0xbe,0x9a,0x7c,0x26,0xd2,0xff,0xd9,0x56,0x91,0xdf,0x2e,0xd3,0x45,0x19,0xbb,0x62,0xfc,0xb5,0xd,0x34,0xb9,0xa3,0x8c,0x63,0xc5,0x51,0x35,0xd4,0xca,0xc3,0x79,0xb4,0xb3,0x33,0x29,0x1e,0x3c,0x8b,0x14,0xce,0x75,0x6c,0xd8,0x8,0x70,0x48,0xb7,0x54,0x69,0x76,0x4,0x20,0xc,0xa5,0xfe,0x9d,0x22,0xc7,0xc1,0x52,0xcf,0x36,0x58,0x4a,0xf4,0xf6,0xe4,0x7d,0x9c,0x27,0xdb,0xe9,0x78,0x3d,0xc8,0xf8,0xf5,0x0,0x13,0xaf,0xf2,0x4f,0x44,0xb2,0xf1,0x67,0x2f,0x5c,0xf7,0x7b,0x4b,0x24,0xfd,0xa9,0xe2,0xb1,0xa0,0x50,0x7a,0x6a,0x73,0x9b,0x87,0xbf,0xa7,0xa,0x1a,0x4e,0x2d,0x23,0x53,0xab,0x3b,0xaa,0x92,0x41,0x65,0x85,0xf,0x1,0x93,0x71,0x9f,0x6f,0xe1,0x6d,0x2c,0x37,0x9,0x8f,0x3a,0xad,0x3,0xb,0xa1,0xc9,0xbc,0x31,0x25,0x8e,0xd7,0x98,0x40,0x95,0xf9,0x5d,0xef,0x49,0xec,0xb0,0x6,0xed,0x86,0xea,0xde,0x32,0x7e,0x15,0xe,0xc0,0x1f,0xeb,0x82,0x28,0x99,0x61,0x5b,0x83,0xd5,0x10,0x5a,0xc2,0x72,0x60,0x5,0x38,0x39,0x7,0x43,0x11,0x84,0xcd,0x66,0xe6,0xb6,0xb8,0x90,0x2b,0x1d,0x16,0x9e,0xe3,0x94,0xa2,0xd6,0xfb,0x8a,0xe0,0xdc,0xf3,0x89,0x4d,0x80,0x21,0xd1,0x3f,0x1c,0x4c,0xcb,0x74,0xa4,0x6e,0x96,0x57,0x17,0xcc,0xba,0x59,0xdd,0x2,0x47,0xda,0x12,0x8d,0x68,0x64,0x3e,0x6b,0x18,0x97,0xa6,0x88,0x5e,0xee,0x42,0xbd,0xfa,0x46,0xe5,0xe7,0xae,0xd0,0x77,0x1b,0xa8,0xe8,0x2a,0x5f,0xc4,0x7f,0xf0,0x30,0xc6,0x2e,0x28,0x20,0xbd,0x11,0x4a,0xcd,0x72,0xeb,0x99,0xe3,0xcf,0x58,0xa7,0x86,0xbb,0x37,0x83,0x9f,0xe7,0xfb,0x64,0x9a,0x21,0xc6,0xdc,0xd3,0xf1,0x96,0x2c,0x5c,0x5b,0xda,0xbe,0x25,0x3b,0x63,0x4c,0x2a,0x8c,0xe2,0x5a,0x56,0xdb,0x54,0xf6,0x13,0x8d,0xc1,0x30,0xaa,0x3c,0x36,0x10,0x7e,0xb9,0x93,0x75,0x3d,0xc9,0x43,0x6e,0x51,0xba,0x70,0x9e,0xe,0x80,0xe0,0x6a,0x7c,0xee,0x7d,0x45,0x8a,0xae,0xbc,0xcc,0xd4,0x44,0xf5,0xe5,0xc2,0xa1,0x68,0x74,0x48,0x50,0x95,0xbf,0x9c,0x85,0xd,0x46,0x4f,0x5e,0xa4,0x94,0x12,0xcb,0xc0,0x88,0x18,0xb3,0xab,0xa0,0x1e,0x5d,0xfc,0xef,0x1d,0x40,0x27,0xd2,0x1a,0x17,0x34,0xc8,0x97,0x6,0xb,0x19,0x73,0x92,0xb7,0xd9,0x1b,0xa5,0x7f,0x57,0xf2,0xc4,0x89,0x22,0x59,0x9,0xac,0xe8,0x6b,0xfe,0xea,0x8f,0xd6,0xd7,0xb5,0xff,0x9d,0x2d,0xb4,0x8e,0x3a,0x6c,0x6d,0x4,0x76,0xc7,0xe1,0xfa,0xf0,0x2f,0x31,0x5,0x91,0xdd,0xe9,0x5f,0x69,0x2,0x0,0xb2,0x3,0xa6,0xaf,0x77,0x16,0x7a,0xca,0xde,0x38,0x61,0x4e,0xe4,0x53,0x26,0xd5,0x60,0xec,0x42,0xc3,0x82,0xe6,0xd8,0x1f,0x90,0x29,0xdf,0xc5,0x7,0x2b,0xb0,0x98,0x3f,0x47,0xf4,0xa,0xa9,0x41,0x8,0xad,0x1,0x15,0x52,0x49,0x78,0xb1,0x67,0xd1,0x8b,0xf7,0x84,0xfd,0x35,0x87,0x62,0x32,0xb6,0xa8,0xed,0xf8,0xb8,0x55,0x23,0x4b,0x9b,0x79,0x81,0xf3,0xd0,0x24,0xa3,0x6f,0xa2,0x3e,0xce,0x33,0xf,0x66,0x1c,0x39,0x4d,0x65,0x14,0x71,0xf9,0x7b,0xc,0xf3,0x34,0xbb,0x9d,0x27,0xb1,0x4c,0xbd,0xdc,0x37,0xce,0xe3,0xb0,0x44,0x1e,0xf8,0xa7,0x1,0xee,0xc1,0xa8,0xb6,0x57,0x33,0x9e,0x0,0xd9,0x7b,0xdb,0x56,0x6f,0xd7,0x17,0xac,0x76,0xe9,0x12,0x6a,0xba,0xe,0xd1,0xd6,0x1b,0xa1,0x5e,0x7c,0x4b,0x51,0x40,0xff,0x9c,0xc7,0xad,0x30,0xa3,0xa5,0xb,0x36,0xd5,0x2a,0x6e,0x42,0x66,0x14,0x1a,0x8b,0xb9,0x45,0x97,0x9a,0xaa,0x5f,0x96,0x28,0x3a,0x54,0xfe,0x1f,0x86,0x94,0x95,0x3e,0x4d,0x5,0x9f,0x46,0x29,0x19,0x90,0xcd,0x71,0x62,0x93,0xd0,0x26,0x2d,0xc5,0xdd,0xe5,0xf9,0x4f,0x2c,0x78,0x68,0xc2,0xd3,0x80,0xcb,0x11,0x8,0x18,0x32,0xf1,0x63,0x6d,0xe7,0x83,0xd,0xfd,0x13,0x59,0xc9,0x31,0x41,0x7,0x23,0xf0,0xc8,0xde,0xab,0xc3,0x69,0xb5,0xec,0x47,0x53,0x6b,0x55,0x4e,0xf,0x61,0xcf,0x58,0xed,0xe4,0x8f,0x64,0xd2,0x1c,0x50,0xbc,0x88,0x9b,0xf7,0x22,0xfa,0x8e,0x2b,0x8d,0x3f,0xb7,0xe1,0x39,0x3,0x10,0xa0,0x38,0x72,0x7d,0xa2,0x6c,0x77,0xfb,0x4a,0xe0,0x89,0xd4,0x84,0x4,0xaf,0x7f,0x49,0xf2,0xda,0x5b,0x5a,0x67,0x2,0xe6,0x73,0x21,0x65,0xeb,0x91,0xbe,0x82,0xb3,0x43,0xe2,0x2f,0xf6,0x81,0xfc,0x74,0xe8,0x99,0xb4,0xc0,0xd8,0xae,0x75,0x35,0x25,0x60,0xbf,0x3b,0xa9,0x2e,0x7e,0x5d,0xf4,0xc,0xc6,0x16,0x3c,0xea,0xc4,0xf5,0x98,0xdf,0x20,0x8c,0xa,0xef,0x70,0xb8,0x7a,0x9,0x5c,0x6,0xa6,0x3d,0x48,0x8a,0xa4,0x52,0x92,0x1d,0xcc,0x85,0x87,0x24,0xca,0x79,0x15,0xb2,0xc8,0x6e,0x81,0xae,0xc7,0xd9,0x38,0x5c,0xf1,0x6f,0xb6,0x14,0xb4,0x39,0x0,0xb8,0x9c,0x5b,0xd4,0xf2,0x48,0xde,0x23,0xd2,0xb3,0x58,0xa1,0x8c,0xdf,0x2b,0x71,0x97,0x2f,0x90,0xf3,0xa8,0xc2,0x5f,0xcc,0xca,0x64,0x59,0xba,0x45,0x1,0x2d,0x9,0x7b,0x78,0xc3,0x19,0x86,0x7d,0x5,0xd5,0x61,0xbe,0xb9,0x74,0xce,0x31,0x13,0x24,0x3e,0xfa,0x51,0x22,0x6a,0xf0,0x29,0x46,0x76,0xff,0xa2,0x1e,0xd,0xfc,0xbf,0x49,0x42,0x75,0xe4,0xd6,0x2a,0xf8,0xf5,0xc5,0x30,0xf9,0x47,0x55,0x3b,0x91,0x70,0xe9,0xfb,0x9e,0xc,0x2,0x88,0xec,0x62,0x92,0x7c,0x36,0xa6,0x5e,0x2e,0x68,0x4c,0x9f,0xa7,0xaa,0xb2,0x8a,0x96,0x20,0x43,0x17,0x7,0xad,0xbc,0xef,0xa4,0x7e,0x67,0x77,0x5d,0x8b,0xe0,0xb,0xbd,0x73,0x3f,0xd3,0xe7,0xf4,0x98,0x4d,0x95,0xe1,0x44,0xe2,0x50,0xb1,0xc4,0xac,0x6,0xda,0x83,0x28,0x3c,0x4,0x3a,0x21,0x60,0xe,0xa0,0x37,0x82,0xbb,0xeb,0x6b,0xc0,0x10,0x26,0x9d,0xb5,0x34,0x35,0x8,0x6d,0x89,0x1c,0x4e,0xa,0xd8,0x8e,0x56,0x6c,0x7f,0xcf,0x57,0x1d,0x12,0xcd,0x3,0x18,0x94,0x25,0x8f,0xe6,0xb7,0xc1,0x1a,0x5a,0x4a,0xf,0xd0,0x54,0xc6,0x41,0x11,0x32,0x9b,0x63,0xa9,0x79,0x84,0xfe,0xd1,0xed,0xdc,0x2c,0x8d,0x40,0x99,0xee,0x93,0x1b,0x87,0xf6,0xdb,0xaf,0xc9,0x52,0x27,0xe5,0xcb,0x3d,0xfd,0x72,0xa3,0xea,0xe8,0x4b,0xa5,0x16,0x7a,0xdd,0x53,0x85,0xab,0x9a,0xf7,0xb0,0x4f,0xe3,0x65,0x80,0x1f,0xd7,0x15,0x66,0x33,0x69,0x2d,0x0,0xf9,0x12,0x36,0xd0,0x8a,0x7e,0x53,0x75,0xfa,0x3d,0x73,0x82,0x7f,0xe9,0xb5,0x17,0xce,0x50,0x19,0xa1,0x98,0x15,0xf,0x20,0xcf,0x69,0xfd,0x99,0x78,0x66,0x6f,0xd5,0x18,0x1f,0x9f,0x85,0xb2,0x90,0x27,0xb8,0x62,0xd9,0xc0,0x74,0xa4,0xdc,0xe4,0x1b,0xf8,0xc5,0xda,0xa8,0x8c,0xa0,0x9,0x52,0x31,0x8e,0x6b,0x6d,0xfe,0x63,0x9a,0xf4,0xe6,0x58,0x5a,0x48,0xd1,0x30,0x8b,0x77,0x45,0xd4,0x91,0x64,0x54,0x59,0xac,0xbf,0x3,0x5e,0xe3,0xe8,0x1e,0x5d,0xcb,0x83,0xf0,0x5b,0xd7,0xe7,0x88,0x51,0x5,0x4e,0x1d,0xc,0xfc,0xd6,0xc6,0xdf,0x37,0x2b,0x13,0xb,0xa6,0xb6,0xe2,0x81,0x8f,0xff,0x7,0x97,0x6,0x3e,0xed,0xc9,0x29,0xa3,0xad,0x3f,0xdd,0x33,0xc3,0x4d,0xc1,0x80,0x9b,0xa5,0x23,0x96,0x1,0xaf,0xa7,0xd,0x65,0x10,0x9d,0x89,0x22,0x7b,0x34,0xec,0x39,0x55,0xf1,0x43,0xe5,0x40,0x1c,0xaa,0x41,0x2a,0x46,0x72,0x9e,0xd2,0xb9,0xa2,0x6c,0xb3,0x47,0x2e,0x84,0x35,0xcd,0xf7,0x2f,0x79,0xbc,0xf6,0x6e,0xde,0xcc,0xa9,0x94,0x95,0xab,0xef,0xbd,0x28,0x61,0xca,0x4a,0x1a,0x14,0x3c,0x87,0xb1,0xba,0x32,0x4f,0x38,0xe,0x7a,0x57,0x26,0x4c,0x70,0x5f,0x25,0xe1,0x2c,0x8d,0x7d,0x93,0xb0,0xe0,0x67,0xd8,0x8,0xc2,0x3a,0xfb,0xbb,0x60,0x16,0xf5,0x71,0xae,0xeb,0x76,0xbe,0x21,0xc4,0xc8,0x92,0xc7,0xb4,0x3b,0xa,0x24,0xf2,0x42,0xee,0x11,0x56,0xea,0x49,0x4b,0x2,0x7c,0xdb,0xb7,0x4,0x44,0x86,0xf3,0x68,0xd3,0x5c,0x9c,0x6a,0x43,0x45,0x4d,0xd0,0x7c,0x27,0xa0,0x1f,0x86,0xf4,0x8e,0xa2,0x35,0xca,0xeb,0xd6,0x5a,0xee,0xf2,0x8a,0x96,0x9,0xf7,0x4c,0xab,0xb1,0xbe,0x9c,0xfb,0x41,0x31,0x36,0xb7,0xd3,0x48,0x56,0xe,0x21,0x47,0xe1,0x8f,0x37,0x3b,0xb6,0x39,0x9b,0x7e,0xe0,0xac,0x5d,0xc7,0x51,0x5b,0x7d,0x13,0xd4,0xfe,0x18,0x50,0xa4,0x2e,0x3,0x3c,0xd7,0x1d,0xf3,0x63,0xed,0x8d,0x7,0x11,0x83,0x10,0x28,0xe7,0xc3,0xd1,0xa1,0xb9,0x29,0x98,0x88,0xaf,0xcc,0x5,0x19,0x25,0x3d,0xf8,0xd2,0xf1,0xe8,0x60,0x2b,0x22,0x33,0xc9,0xf9,0x7f,0xa6,0xad,0xe5,0x75,0xde,0xc6,0xcd,0x73,0x30,0x91,0x82,0x70,0x2d,0x4a,0xbf,0x77,0x7a,0x59,0xa5,0xfa,0x6b,0x66,0x74,0x1e,0xff,0xda,0xb4,0x76,0xc8,0x12,0x3a,0x9f,0xa9,0xe4,0x4f,0x34,0x64,0xc1,0x85,0x6,0x93,0x87,0xe2,0xbb,0xba,0xd8,0x92,0xf0,0x40,0xd9,0xe3,0x57,0x1,0x0,0x69,0x1b,0xaa,0x8c,0x97,0x9d,0x42,0x5c,0x68,0xfc,0xb0,0x84,0x32,0x4,0x6f,0x6d,0xdf,0x6e,0xcb,0xc2,0x1a,0x7b,0x17,0xa7,0xb3,0x55,0xc,0x23,0x89,0x3e,0x4b,0xb8,0xd,0x81,0x2f,0xae,0xef,0x8b,0xb5,0x72,0xfd,0x44,0xb2,0xa8,0x6a,0x46,0xdd,0xf5,0x52,0x2a,0x99,0x67,0xc4,0x2c,0x65,0xc0,0x6c,0x78,0x3f,0x24,0x15,0xdc,0xa,0xbc,0xe6,0x9a,0xe9,0x90,0x58,0xea,0xf,0x5f,0xdb,0xc5,0x80,0x95,0xd5,0x38,0x4e,0x26,0xf6,0x14,0xec,0x9e,0xbd,0x49,0xce,0x2,0xcf,0x53,0xa3,0x5e,0x62,0xb,0x71,0x54,0x20,0x8,0x79,0x1c,0x94,0x16,0x61,0xc4,0x3,0x8c,0xaa,0x10,0x86,0x7b,0x8a,0xeb,0x0,0xf9,0xd4,0x87,0x73,0x29,0xcf,0x90,0x36,0xd9,0xf6,0x9f,0x81,0x60,0x4,0xa9,0x37,0xee,0x4c,0xec,0x61,0x58,0xe0,0x20,0x9b,0x41,0xde,0x25,0x5d,0x8d,0x39,0xe6,0xe1,0x2c,0x96,0x69,0x4b,0x7c,0x66,0x77,0xc8,0xab,0xf0,0x9a,0x7,0x94,0x92,0x3c,0x1,0xe2,0x1d,0x59,0x75,0x51,0x23,0x2d,0xbc,0x8e,0x72,0xa0,0xad,0x9d,0x68,0xa1,0x1f,0xd,0x63,0xc9,0x28,0xb1,0xa3,0xa2,0x9,0x7a,0x32,0xa8,0x71,0x1e,0x2e,0xa7,0xfa,0x46,0x55,0xa4,0xe7,0x11,0x1a,0xf2,0xea,0xd2,0xce,0x78,0x1b,0x4f,0x5f,0xf5,0xe4,0xb7,0xfc,0x26,0x3f,0x2f,0x5,0xc6,0x54,0x5a,0xd0,0xb4,0x3a,0xca,0x24,0x6e,0xfe,0x6,0x76,0x30,0x14,0xc7,0xff,0xe9,0x9c,0xf4,0x5e,0x82,0xdb,0x70,0x64,0x5c,0x62,0x79,0x38,0x56,0xf8,0x6f,0xda,0xd3,0xb8,0x53,0xe5,0x2b,0x67,0x8b,0xbf,0xac,0xc0,0x15,0xcd,0xb9,0x1c,0xba,0x8,0x80,0xd6,0xe,0x34,0x27,0x97,0xf,0x45,0x4a,0x95,0x5b,0x40,0xcc,0x7d,0xd7,0xbe,0xe3,0xb3,0x33,0x98,0x48,0x7e,0xc5,0xed,0x6c,0x6d,0x50,0x35,0xd1,0x44,0x16,0x52,0xdc,0xa6,0x89,0xb5,0x84,0x74,0xd5,0x18,0xc1,0xb6,0xcb,0x43,0xdf,0xae,0x83,0xf7,0xef,0x99,0x42,0x2,0x12,0x57,0x88,0xc,0x9e,0x19,0x49,0x6a,0xc3,0x3b,0xf1,0x21,0xb,0xdd,0xf3,0xc2,0xaf,0xe8,0x17,0xbb,0x3d,0xd8,0x47,0x8f,0x4d,0x3e,0x6b,0x31,0x91,0xa,0x7f,0xbd,0x93,0x65,0xa5,0x2a,0xfb,0xb2,0xb0,0x13,0xfd,0x4e,0x22,0x85,0x45,0xe3,0xc,0x23,0x4a,0x54,0xb5,0xd1,0x7c,0xe2,0x3b,0x99,0x39,0xb4,0x8d,0x35,0x11,0xd6,0x59,0x7f,0xc5,0x53,0xae,0x5f,0x3e,0xd5,0x2c,0x1,0x52,0xa6,0xfc,0x1a,0xa2,0x1d,0x7e,0x25,0x4f,0xd2,0x41,0x47,0xe9,0xd4,0x37,0xc8,0x8c,0xa0,0x84,0xf6,0xf5,0x4e,0x94,0xb,0xf0,0x88,0x58,0xec,0x33,0x34,0xf9,0x43,0xbc,0x9e,0xa9,0xb3,0x77,0xdc,0xaf,0xe7,0x7d,0xa4,0xcb,0xfb,0x72,0x2f,0x93,0x80,0x71,0x32,0xc4,0xcf,0xf8,0x69,0x5b,0xa7,0x75,0x78,0x48,0xbd,0x74,0xca,0xd8,0xb6,0x1c,0xfd,0x64,0x76,0x13,0x81,0x8f,0x5,0x61,0xef,0x1f,0xf1,0xbb,0x2b,0xd3,0xa3,0xe5,0xc1,0x12,0x2a,0x27,0x3f,0x7,0x1b,0xad,0xce,0x9a,0x8a,0x20,0x31,0x62,0x29,0xf3,0xea,0xfa,0xd0,0x6,0x6d,0x86,0x30,0xfe,0xb2,0x5e,0x6a,0x79,0x15,0xc0,0x18,0x6c,0xc9,0x6f,0xdd,0x3c,0x49,0x21,0x8b,0x57,0xe,0xa5,0xb1,0x89,0xb7,0xac,0xed,0x83,0x2d,0xba,0xf,0x36,0x66,0xe6,0x4d,0x9d,0xab,0x10,0x38,0xb9,0xb8,0x85,0xe0,0x4,0x91,0xc3,0x87,0x55,0x3,0xdb,0xe1,0xf2,0x42,0xda,0x90,0x9f,0x40,0x8e,0x95,0x19,0xa8,0x2,0x6b,0x3a,0x4c,0x97,0xd7,0xc7,0x82,0x5d,0xd9,0x4b,0xcc,0x9c,0xbf,0x16,0xee,0x24,0xf4,0x9,0x73,0x5c,0x60,0x51,0xa1,0x0,0xcd,0x14,0x63,0x1e,0x96,0xa,0x7b,0x56,0x22,0x44,0xdf,0xaa,0x68,0x46,0xb0,0x70,0xff,0x2e,0x67,0x65,0xc6,0x28,0x9b,0xf7,0x50,0xde,0x8,0x26,0x17,0x7a,0x3d,0xc2,0x6e,0xe8,0xd,0x92,0x5a,0x98,0xeb,0xbe,0xe4,0x93,0xbe,0x47,0xac,0x88,0x6e,0x34,0xc0,0xed,0xcb,0x44,0x83,0xcd,0x3c,0xc1,0x57,0xb,0xa9,0x70,0xee,0xa7,0x1f,0x26,0xab,0xb1,0x9e,0x71,0xd7,0x43,0x27,0xc6,0xd8,0xd1,0x6b,0xa6,0xa1,0x21,0x3b,0xc,0x2e,0x99,0x6,0xdc,0x67,0x7e,0xca,0x1a,0x62,0x5a,0xa5,0x46,0x7b,0x64,0x16,0x32,0x1e,0xb7,0xec,0x8f,0x30,0xd5,0xd3,0x40,0xdd,0x24,0x4a,0x58,0xe6,0xe4,0xf6,0x6f,0x8e,0x35,0xc9,0xfb,0x6a,0x2f,0xda,0xea,0xe7,0x12,0x1,0xbd,0xe0,0x5d,0x56,0xa0,0xe3,0x75,0x3d,0x4e,0xe5,0x69,0x59,0x36,0xef,0xbb,0xf0,0xa3,0xb2,0x42,0x68,0x78,0x61,0x89,0x95,0xad,0xb5,0x18,0x8,0x5c,0x3f,0x31,0x41,0xb9,0x29,0xb8,0x80,0x53,0x77,0x97,0x1d,0x13,0x81,0x63,0x8d,0x7d,0xf3,0x7f,0x3e,0x25,0x1b,0x9d,0x28,0xbf,0x11,0x19,0xb3,0xdb,0xae,0x23,0x37,0x9c,0xc5,0x8a,0x52,0x87,0xeb,0x4f,0xfd,0x5b,0xfe,0xa2,0x14,0xff,0x94,0xf8,0xcc,0x20,0x6c,0x7,0x1c,0xd2,0xd,0xf9,0x90,0x3a,0x8b,0x73,0x49,0x91,0xc7,0x2,0x48,0xd0,0x60,0x72,0x17,0x2a,0x2b,0x15,0x51,0x3,0x96,0xdf,0x74,0xf4,0xa4,0xaa,0x82,0x39,0xf,0x4,0x8c,0xf1,0x86,0xb0,0xc4,0xe9,0x98,0xf2,0xce,0xe1,0x9b,0x5f,0x92,0x33,0xc3,0x2d,0xe,0x5e,0xd9,0x66,0xb6,0x7c,0x84,0x45,0x5,0xde,0xa8,0x4b,0xcf,0x10,0x55,0xc8,0x0,0x9f,0x7a,0x76,0x2c,0x79,0xa,0x85,0xb4,0x9a,0x4c,0xfc,0x50,0xaf,0xe8,0x54,0xf7,0xf5,0xbc,0xc2,0x65,0x9,0xba,0xfa,0x38,0x4d,0xd6,0x6d,0xe2,0x22,0xd4,0x52,0x54,0x5c,0xc1,0x6d,0x36,0xb1,0xe,0x97,0xe5,0x9f,0xb3,0x24,0xdb,0xfa,0xc7,0x4b,0xff,0xe3,0x9b,0x87,0x18,0xe6,0x5d,0xba,0xa0,0xaf,0x8d,0xea,0x50,0x20,0x27,0xa6,0xc2,0x59,0x47,0x1f,0x30,0x56,0xf0,0x9e,0x26,0x2a,0xa7,0x28,0x8a,0x6f,0xf1,0xbd,0x4c,0xd6,0x40,0x4a,0x6c,0x2,0xc5,0xef,0x9,0x41,0xb5,0x3f,0x12,0x2d,0xc6,0xc,0xe2,0x72,0xfc,0x9c,0x16,0x0,0x92,0x1,0x39,0xf6,0xd2,0xc0,0xb0,0xa8,0x38,0x89,0x99,0xbe,0xdd,0x14,0x8,0x34,0x2c,0xe9,0xc3,0xe0,0xf9,0x71,0x3a,0x33,0x22,0xd8,0xe8,0x6e,0xb7,0xbc,0xf4,0x64,0xcf,0xd7,0xdc,0x62,0x21,0x80,0x93,0x61,0x3c,0x5b,0xae,0x66,0x6b,0x48,0xb4,0xeb,0x7a,0x77,0x65,0xf,0xee,0xcb,0xa5,0x67,0xd9,0x3,0x2b,0x8e,0xb8,0xf5,0x5e,0x25,0x75,0xd0,0x94,0x17,0x82,0x96,0xf3,0xaa,0xab,0xc9,0x83,0xe1,0x51,0xc8,0xf2,0x46,0x10,0x11,0x78,0xa,0xbb,0x9d,0x86,0x8c,0x53,0x4d,0x79,0xed,0xa1,0x95,0x23,0x15,0x7e,0x7c,0xce,0x7f,0xda,0xd3,0xb,0x6a,0x6,0xb6,0xa2,0x44,0x1d,0x32,0x98,0x2f,0x5a,0xa9,0x1c,0x90,0x3e,0xbf,0xfe,0x9a,0xa4,0x63,0xec,0x55,0xa3,0xb9,0x7b,0x57,0xcc,0xe4,0x43,0x3b,0x88,0x76,0xd5,0x3d,0x74,0xd1,0x7d,0x69,0x2e,0x35,0x4,0xcd,0x1b,0xad,0xf7,0x8b,0xf8,0x81,0x49,0xfb,0x1e,0x4e,0xca,0xd4,0x91,0x84,0xc4,0x29,0x5f,0x37,0xe7,0x5,0xfd,0x8f,0xac,0x58,0xdf,0x13,0xde,0x42,0xb2,0x4f,0x73,0x1a,0x60,0x45,0x31,0x19,0x68,0xd,0x85,0x7,0x70,0x76,0xb1,0x3e,0x18,0xa2,0x34,0xc9,0x38,0x59,0xb2,0x4b,0x66,0x35,0xc1,0x9b,0x7d,0x22,0x84,0x6b,0x44,0x2d,0x33,0xd2,0xb6,0x1b,0x85,0x5c,0xfe,0x5e,0xd3,0xea,0x52,0x92,0x29,0xf3,0x6c,0x97,0xef,0x3f,0x8b,0x54,0x53,0x9e,0x24,0xdb,0xf9,0xce,0xd4,0xc5,0x7a,0x19,0x42,0x28,0xb5,0x26,0x20,0x8e,0xb3,0x50,0xaf,0xeb,0xc7,0xe3,0x91,0x9f,0xe,0x3c,0xc0,0x12,0x1f,0x2f,0xda,0x13,0xad,0xbf,0xd1,0x7b,0x9a,0x3,0x11,0x10,0xbb,0xc8,0x80,0x1a,0xc3,0xac,0x9c,0x15,0x48,0xf4,0xe7,0x16,0x55,0xa3,0xa8,0x40,0x58,0x60,0x7c,0xca,0xa9,0xfd,0xed,0x47,0x56,0x5,0x4e,0x94,0x8d,0x9d,0xb7,0x74,0xe6,0xe8,0x62,0x6,0x88,0x78,0x96,0xdc,0x4c,0xb4,0xc4,0x82,0xa6,0x75,0x4d,0x5b,0x2e,0x46,0xec,0x30,0x69,0xc2,0xd6,0xee,0xd0,0xcb,0x8a,0xe4,0x4a,0xdd,0x68,0x61,0xa,0xe1,0x57,0x99,0xd5,0x39,0xd,0x1e,0x72,0xa7,0x7f,0xb,0xae,0x8,0xba,0x32,0x64,0xbc,0x86,0x95,0x25,0xbd,0xf7,0xf8,0x27,0xe9,0xf2,0x7e,0xcf,0x65,0xc,0x51,0x1,0x81,0x2a,0xfa,0xcc,0x77,0x5f,0xde,0xdf,0xe2,0x87,0x63,0xf6,0xa4,0xe0,0x6e,0x14,0x3b,0x7,0x36,0xc6,0x67,0xaa,0x73,0x4,0x79,0xf1,0x6d,0x1c,0x31,0x45,0x5d,0x2b,0xf0,0xb0,0xa0,0xe5,0x3a,0xbe,0x2c,0xab,0xfb,0xd8,0x71,0x89,0x43,0x93,0xb9,0x6f,0x41,0x70,0x1d,0x5a,0xa5,0x9,0x8f,0x6a,0xf5,0x3d,0xff,0x8c,0xd9,0x83,0x23,0xb8,0xcd,0xf,0x21,0xd7,0x17,0x98,0x49,0x0,0x2,0xa1,0x4f,0xfc,0x90,0x37,0x86,0x20,0xcf,0xe0,0x89,0x97,0x76,0x12,0xbf,0x21,0xf8,0x5a,0xfa,0x77,0x4e,0xf6,0xd2,0x15,0x9a,0xbc,0x6,0x90,0x6d,0x9c,0xfd,0x16,0xef,0xc2,0x91,0x65,0x3f,0xd9,0x61,0xde,0xbd,0xe6,0x8c,0x11,0x82,0x84,0x2a,0x17,0xf4,0xb,0x4f,0x63,0x47,0x35,0x36,0x8d,0x57,0xc8,0x33,0x4b,0x9b,0x2f,0xf0,0xf7,0x3a,0x80,0x7f,0x5d,0x6a,0x70,0xb4,0x1f,0x6c,0x24,0xbe,0x67,0x8,0x38,0xb1,0xec,0x50,0x43,0xb2,0xf1,0x7,0xc,0x3b,0xaa,0x98,0x64,0xb6,0xbb,0x8b,0x7e,0xb7,0x9,0x1b,0x75,0xdf,0x3e,0xa7,0xb5,0xd0,0x42,0x4c,0xc6,0xa2,0x2c,0xdc,0x32,0x78,0xe8,0x10,0x60,0x26,0x2,0xd1,0xe9,0xe4,0xfc,0xc4,0xd8,0x6e,0xd,0x59,0x49,0xe3,0xf2,0xa1,0xea,0x30,0x29,0x39,0x13,0xc5,0xae,0x45,0xf3,0x3d,0x71,0x9d,0xa9,0xba,0xd6,0x3,0xdb,0xaf,0xa,0xac,0x1e,0xff,0x8a,0xe2,0x48,0x94,0xcd,0x66,0x72,0x4a,0x74,0x6f,0x2e,0x40,0xee,0x79,0xcc,0xf5,0xa5,0x25,0x8e,0x5e,0x68,0xd3,0xfb,0x7a,0x7b,0x46,0x23,0xc7,0x52,0x0,0x44,0x96,0xc0,0x18,0x22,0x31,0x81,0x19,0x53,0x5c,0x83,0x4d,0x56,0xda,0x6b,0xc1,0xa8,0xf9,0x8f,0x54,0x14,0x4,0x41,0x9e,0x1a,0x88,0xf,0x5f,0x7c,0xd5,0x2d,0xe7,0x37,0xca,0xb0,0x9f,0xa3,0x92,0x62,0xc3,0xe,0xd7,0xa0,0xdd,0x55,0xc9,0xb8,0x95,0xe1,0x87,0x1c,0x69,0xab,0x85,0x73,0xb3,0x3c,0xed,0xa4,0xa6,0x5,0xeb,0x58,0x34,0x93,0x1d,0xcb,0xe5,0xd4,0xb9,0xfe,0x1,0xad,0x2b,0xce,0x51,0x99,0x5b,0x28,0x7d,0x27,0x6e,0x43,0xba,0x51,0x75,0x93,0xc9,0x3d,0x10,0x36,0xb9,0x7e,0x30,0xc1,0x3c,0xaa,0xf6,0x54,0x8d,0x13,0x5a,0xe2,0xdb,0x56,0x4c,0x63,0x8c,0x2a,0xbe,0xda,0x3b,0x25,0x2c,0x96,0x5b,0x5c,0xdc,0xc6,0xf1,0xd3,0x64,0xfb,0x21,0x9a,0x83,0x37,0xe7,0x9f,0xa7,0x58,0xbb,0x86,0x99,0xeb,0xcf,0xe3,0x4a,0x11,0x72,0xcd,0x28,0x2e,0xbd,0x20,0xd9,0xb7,0xa5,0x1b,0x19,0xb,0x92,0x73,0xc8,0x34,0x6,0x97,0xd2,0x27,0x17,0x1a,0xef,0xfc,0x40,0x1d,0xa0,0xab,0x5d,0x1e,0x88,0xc0,0xb3,0x18,0x94,0xa4,0xcb,0x12,0x46,0xd,0x5e,0x4f,0xbf,0x95,0x85,0x9c,0x74,0x68,0x50,0x48,0xe5,0xf5,0xa1,0xc2,0xcc,0xbc,0x44,0xd4,0x45,0x7d,0xae,0x8a,0x6a,0xe0,0xee,0x7c,0x9e,0x70,0x80,0xe,0x82,0xc3,0xd8,0xe6,0x60,0xd5,0x42,0xec,0xe4,0x4e,0x26,0x53,0xde,0xca,0x61,0x38,0x77,0xaf,0x7a,0x16,0xb2,0x0,0xa6,0x3,0x5f,0xe9,0x2,0x69,0x5,0x31,0xdd,0x91,0xfa,0xe1,0x2f,0xf0,0x4,0x6d,0xc7,0x76,0x8e,0xb4,0x6c,0x3a,0xff,0xb5,0x2d,0x9d,0x8f,0xea,0xd7,0xd6,0xe8,0xac,0xfe,0x6b,0x22,0x89,0x9,0x59,0x57,0x7f,0xc4,0xf2,0xf9,0x71,0xc,0x7b,0x4d,0x39,0x14,0x65,0xf,0x33,0x1c,0x66,0xa2,0x6f,0xce,0x3e,0xd0,0xf3,0xa3,0x24,0x9b,0x4b,0x81,0x79,0xb8,0xf8,0x23,0x55,0xb6,0x32,0xed,0xa8,0x35,0xfd,0x62,0x87,0x8b,0xd1,0x84,0xf7,0x78,0x49,0x67,0xb1,0x1,0xad,0x52,0x15,0xa9,0xa,0x8,0x41,0x3f,0x98,0xf4,0x47,0x7,0xc5,0xb0,0x2b,0x90,0x1f,0xdf,0x29,0x95,0x93,0x9b,0x6,0xaa,0xf1,0x76,0xc9,0x50,0x22,0x58,0x74,0xe3,0x1c,0x3d,0x0,0x8c,0x38,0x24,0x5c,0x40,0xdf,0x21,0x9a,0x7d,0x67,0x68,0x4a,0x2d,0x97,0xe7,0xe0,0x61,0x5,0x9e,0x80,0xd8,0xf7,0x91,0x37,0x59,0xe1,0xed,0x60,0xef,0x4d,0xa8,0x36,0x7a,0x8b,0x11,0x87,0x8d,0xab,0xc5,0x2,0x28,0xce,0x86,0x72,0xf8,0xd5,0xea,0x1,0xcb,0x25,0xb5,0x3b,0x5b,0xd1,0xc7,0x55,0xc6,0xfe,0x31,0x15,0x7,0x77,0x6f,0xff,0x4e,0x5e,0x79,0x1a,0xd3,0xcf,0xf3,0xeb,0x2e,0x4,0x27,0x3e,0xb6,0xfd,0xf4,0xe5,0x1f,0x2f,0xa9,0x70,0x7b,0x33,0xa3,0x8,0x10,0x1b,0xa5,0xe6,0x47,0x54,0xa6,0xfb,0x9c,0x69,0xa1,0xac,0x8f,0x73,0x2c,0xbd,0xb0,0xa2,0xc8,0x29,0xc,0x62,0xa0,0x1e,0xc4,0xec,0x49,0x7f,0x32,0x99,0xe2,0xb2,0x17,0x53,0xd0,0x45,0x51,0x34,0x6d,0x6c,0xe,0x44,0x26,0x96,0xf,0x35,0x81,0xd7,0xd6,0xbf,0xcd,0x7c,0x5a,0x41,0x4b,0x94,0x8a,0xbe,0x2a,0x66,0x52,0xe4,0xd2,0xb9,0xbb,0x9,0xb8,0x1d,0x14,0xcc,0xad,0xc1,0x71,0x65,0x83,0xda,0xf5,0x5f,0xe8,0x9d,0x6e,0xdb,0x57,0xf9,0x78,0x39,0x5d,0x63,0xa4,0x2b,0x92,0x64,0x7e,0xbc,0x90,0xb,0x23,0x84,0xfc,0x4f,0xb1,0x12,0xfa,0xb3,0x16,0xba,0xae,0xe9,0xf2,0xc3,0xa,0xdc,0x6a,0x30,0x4c,0x3f,0x46,0x8e,0x3c,0xd9,0x89,0xd,0x13,0x56,0x43,0x3,0xee,0x98,0xf0,0x20,0xc2,0x3a,0x48,0x6b,0x9f,0x18,0xd4,0x19,0x85,0x75,0x88,0xb4,0xdd,0xa7,0x82,0xf6,0xde,0xaf,0xca,0x42,0xc0,0xb7,0x56,0x91,0x1e,0x38,0x82,0x14,0xe9,0x18,0x79,0x92,0x6b,0x46,0x15,0xe1,0xbb,0x5d,0x2,0xa4,0x4b,0x64,0xd,0x13,0xf2,0x96,0x3b,0xa5,0x7c,0xde,0x7e,0xf3,0xca,0x72,0xb2,0x9,0xd3,0x4c,0xb7,0xcf,0x1f,0xab,0x74,0x73,0xbe,0x4,0xfb,0xd9,0xee,0xf4,0xe5,0x5a,0x39,0x62,0x8,0x95,0x6,0x0,0xae,0x93,0x70,0x8f,0xcb,0xe7,0xc3,0xb1,0xbf,0x2e,0x1c,0xe0,0x32,0x3f,0xf,0xfa,0x33,0x8d,0x9f,0xf1,0x5b,0xba,0x23,0x31,0x30,0x9b,0xe8,0xa0,0x3a,0xe3,0x8c,0xbc,0x35,0x68,0xd4,0xc7,0x36,0x75,0x83,0x88,0x60,0x78,0x40,0x5c,0xea,0x89,0xdd,0xcd,0x67,0x76,0x25,0x6e,0xb4,0xad,0xbd,0x97,0x54,0xc6,0xc8,0x42,0x26,0xa8,0x58,0xb6,0xfc,0x6c,0x94,0xe4,0xa2,0x86,0x55,0x6d,0x7b,0xe,0x66,0xcc,0x10,0x49,0xe2,0xf6,0xce,0xf0,0xeb,0xaa,0xc4,0x6a,0xfd,0x48,0x41,0x2a,0xc1,0x77,0xb9,0xf5,0x19,0x2d,0x3e,0x52,0x87,0x5f,0x2b,0x8e,0x28,0x9a,0x12,0x44,0x9c,0xa6,0xb5,0x5,0x9d,0xd7,0xd8,0x7,0xc9,0xd2,0x5e,0xef,0x45,0x2c,0x71,0x21,0xa1,0xa,0xda,0xec,0x57,0x7f,0xfe,0xff,0xc2,0xa7,0x43,0xd6,0x84,0xc0,0x4e,0x34,0x1b,0x27,0x16,0xe6,0x47,0x8a,0x53,0x24,0x59,0xd1,0x4d,0x3c,0x11,0x65,0x7d,0xb,0xd0,0x90,0x80,0xc5,0x1a,0x9e,0xc,0x8b,0xdb,0xf8,0x51,0xa9,0x63,0xb3,0x99,0x4f,0x61,0x50,0x3d,0x7a,0x85,0x29,0xaf,0x4a,0xd5,0x1d,0xdf,0xac,0xf9,0xa3,0x3,0x98,0xed,0x2f,0x1,0xf7,0x37,0xb8,0x69,0x20,0x22,0x81,0x6f,0xdc,0xb0,0x17,0x3a,0x9c,0x73,0x5c,0x35,0x2b,0xca,0xae,0x3,0x9d,0x44,0xe6,0x46,0xcb,0xf2,0x4a,0x6e,0xa9,0x26,0x0,0xba,0x2c,0xd1,0x20,0x41,0xaa,0x53,0x7e,0x2d,0xd9,0x83,0x65,0xdd,0x62,0x1,0x5a,0x30,0xad,0x3e,0x38,0x96,0xab,0x48,0xb7,0xf3,0xdf,0xfb,0x89,0x8a,0x31,0xeb,0x74,0x8f,0xf7,0x27,0x93,0x4c,0x4b,0x86,0x3c,0xc3,0xe1,0xd6,0xcc,0x8,0xa3,0xd0,0x98,0x2,0xdb,0xb4,0x84,0xd,0x50,0xec,0xff,0xe,0x4d,0xbb,0xb0,0x87,0x16,0x24,0xd8,0xa,0x7,0x37,0xc2,0xb,0xb5,0xa7,0xc9,0x63,0x82,0x1b,0x9,0x6c,0xfe,0xf0,0x7a,0x1e,0x90,0x60,0x8e,0xc4,0x54,0xac,0xdc,0x9a,0xbe,0x6d,0x55,0x58,0x40,0x78,0x64,0xd2,0xb1,0xe5,0xf5,0x5f,0x4e,0x1d,0x56,0x8c,0x95,0x85,0xaf,0x79,0x12,0xf9,0x4f,0x81,0xcd,0x21,0x15,0x6,0x6a,0xbf,0x67,0x13,0xb6,0x10,0xa2,0x43,0x36,0x5e,0xf4,0x28,0x71,0xda,0xce,0xf6,0xc8,0xd3,0x92,0xfc,0x52,0xc5,0x70,0x49,0x19,0x99,0x32,0xe2,0xd4,0x6f,0x47,0xc6,0xc7,0xfa,0x9f,0x7b,0xee,0xbc,0xf8,0x2a,0x7c,0xa4,0x9e,0x8d,0x3d,0xa5,0xef,0xe0,0x3f,0xf1,0xea,0x66,0xd7,0x7d,0x14,0x45,0x33,0xe8,0xa8,0xb8,0xfd,0x22,0xa6,0x34,0xb3,0xe3,0xc0,0x69,0x91,0x5b,0x8b,0x76,0xc,0x23,0x1f,0x2e,0xde,0x7f,0xb2,0x6b,0x1c,0x61,0xe9,0x75,0x4,0x29,0x5d,0x3b,0xa0,0xd5,0x17,0x39,0xcf,0xf,0x80,0x51,0x18,0x1a,0xb9,0x57,0xe4,0x88,0x2f,0xa1,0x77,0x59,0x68,0x5,0x42,0xbd,0x11,0x97,0x72,0xed,0x25,0xe7,0x94,0xc1,0x9b,0xda,0xf7,0xe,0xe5,0xc1,0x27,0x7d,0x89,0xa4,0x82,0xd,0xca,0x84,0x75,0x88,0x1e,0x42,0xe0,0x39,0xa7,0xee,0x56,0x6f,0xe2,0xf8,0xd7,0x38,0x9e,0xa,0x6e,0x8f,0x91,0x98,0x22,0xef,0xe8,0x68,0x72,0x45,0x67,0xd0,0x4f,0x95,0x2e,0x37,0x83,0x53,0x2b,0x13,0xec,0xf,0x32,0x2d,0x5f,0x7b,0x57,0xfe,0xa5,0xc6,0x79,0x9c,0x9a,0x9,0x94,0x6d,0x3,0x11,0xaf,0xad,0xbf,0x26,0xc7,0x7c,0x80,0xb2,0x23,0x66,0x93,0xa3,0xae,0x5b,0x48,0xf4,0xa9,0x14,0x1f,0xe9,0xaa,0x3c,0x74,0x7,0xac,0x20,0x10,0x7f,0xa6,0xf2,0xb9,0xea,0xfb,0xb,0x21,0x31,0x28,0xc0,0xdc,0xe4,0xfc,0x51,0x41,0x15,0x76,0x78,0x8,0xf0,0x60,0xf1,0xc9,0x1a,0x3e,0xde,0x54,0x5a,0xc8,0x2a,0xc4,0x34,0xba,0x36,0x77,0x6c,0x52,0xd4,0x61,0xf6,0x58,0x50,0xfa,0x92,0xe7,0x6a,0x7e,0xd5,0x8c,0xc3,0x1b,0xce,0xa2,0x6,0xb4,0x12,0xb7,0xeb,0x5d,0xb6,0xdd,0xb1,0x85,0x69,0x25,0x4e,0x55,0x9b,0x44,0xb0,0xd9,0x73,0xc2,0x3a,0x0,0xd8,0x8e,0x4b,0x1,0x99,0x29,0x3b,0x5e,0x63,0x62,0x5c,0x18,0x4a,0xdf,0x96,0x3d,0xbd,0xed,0xe3,0xcb,0x70,0x46,0x4d,0xc5,0xb8,0xcf,0xf9,0x8d,0xa0,0xd1,0xbb,0x87,0xa8,0xd2,0x16,0xdb,0x7a,0x8a,0x64,0x47,0x17,0x90,0x2f,0xff,0x35,0xcd,0xc,0x4c,0x97,0xe1,0x2,0x86,0x59,0x1c,0x81,0x49,0xd6,0x33,0x3f,0x65,0x30,0x43,0xcc,0xfd,0xd3,0x5,0xb5,0x19,0xe6,0xa1,0x1d,0xbe,0xbc,0xf5,0x8b,0x2c,0x40,0xf3,0xb3,0x71,0x4,0x9f,0x24,0xab,0x6b,0x9d,0xea,0xec,0xe4,0x79,0xd5,0x8e,0x9,0xb6,0x2f,0x5d,0x27,0xb,0x9c,0x63,0x42,0x7f,0xf3,0x47,0x5b,0x23,0x3f,0xa0,0x5e,0xe5,0x2,0x18,0x17,0x35,0x52,0xe8,0x98,0x9f,0x1e,0x7a,0xe1,0xff,0xa7,0x88,0xee,0x48,0x26,0x9e,0x92,0x1f,0x90,0x32,0xd7,0x49,0x5,0xf4,0x6e,0xf8,0xf2,0xd4,0xba,0x7d,0x57,0xb1,0xf9,0xd,0x87,0xaa,0x95,0x7e,0xb4,0x5a,0xca,0x44,0x24,0xae,0xb8,0x2a,0xb9,0x81,0x4e,0x6a,0x78,0x8,0x10,0x80,0x31,0x21,0x6,0x65,0xac,0xb0,0x8c,0x94,0x51,0x7b,0x58,0x41,0xc9,0x82,0x8b,0x9a,0x60,0x50,0xd6,0xf,0x4,0x4c,0xdc,0x77,0x6f,0x64,0xda,0x99,0x38,0x2b,0xd9,0x84,0xe3,0x16,0xde,0xd3,0xf0,0xc,0x53,0xc2,0xcf,0xdd,0xb7,0x56,0x73,0x1d,0xdf,0x61,0xbb,0x93,0x36,0x0,0x4d,0xe6,0x9d,0xcd,0x68,0x2c,0xaf,0x3a,0x2e,0x4b,0x12,0x13,0x71,0x3b,0x59,0xe9,0x70,0x4a,0xfe,0xa8,0xa9,0xc0,0xb2,0x3,0x25,0x3e,0x34,0xeb,0xf5,0xc1,0x55,0x19,0x2d,0x9b,0xad,0xc6,0xc4,0x76,0xc7,0x62,0x6b,0xb3,0xd2,0xbe,0xe,0x1a,0xfc,0xa5,0x8a,0x20,0x97,0xe2,0x11,0xa4,0x28,0x86,0x7,0x46,0x22,0x1c,0xdb,0x54,0xed,0x1b,0x1,0xc3,0xef,0x74,0x5c,0xfb,0x83,0x30,0xce,0x6d,0x85,0xcc,0x69,0xc5,0xd1,0x96,0x8d,0xbc,0x75,0xa3,0x15,0x4f,0x33,0x40,0x39,0xf1,0x43,0xa6,0xf6,0x72,0x6c,0x29,0x3c,0x7c,0x91,0xe7,0x8f,0x5f,0xbd,0x45,0x37,0x14,0xe0,0x67,0xab,0x66,0xfa,0xa,0xf7,0xcb,0xa2,0xd8,0xfd,0x89,0xa1,0xd0,0xb5,0x3d,0xbf,0xc8,0x51,0x96,0x19,0x3f,0x85,0x13,0xee,0x1f,0x7e,0x95,0x6c,0x41,0x12,0xe6,0xbc,0x5a,0x5,0xa3,0x4c,0x63,0xa,0x14,0xf5,0x91,0x3c,0xa2,0x7b,0xd9,0x79,0xf4,0xcd,0x75,0xb5,0xe,0xd4,0x4b,0xb0,0xc8,0x18,0xac,0x73,0x74,0xb9,0x3,0xfc,0xde,0xe9,0xf3,0xe2,0x5d,0x3e,0x65,0xf,0x92,0x1,0x7,0xa9,0x94,0x77,0x88,0xcc,0xe0,0xc4,0xb6,0xb8,0x29,0x1b,0xe7,0x35,0x38,0x8,0xfd,0x34,0x8a,0x98,0xf6,0x5c,0xbd,0x24,0x36,0x37,0x9c,0xef,0xa7,0x3d,0xe4,0x8b,0xbb,0x32,0x6f,0xd3,0xc0,0x31,0x72,0x84,0x8f,0x67,0x7f,0x47,0x5b,0xed,0x8e,0xda,0xca,0x60,0x71,0x22,0x69,0xb3,0xaa,0xba,0x90,0x53,0xc1,0xcf,0x45,0x21,0xaf,0x5f,0xb1,0xfb,0x6b,0x93,0xe3,0xa5,0x81,0x52,0x6a,0x7c,0x9,0x61,0xcb,0x17,0x4e,0xe5,0xf1,0xc9,0xf7,0xec,0xad,0xc3,0x6d,0xfa,0x4f,0x46,0x2d,0xc6,0x70,0xbe,0xf2,0x1e,0x2a,0x39,0x55,0x80,0x58,0x2c,0x89,0x2f,0x9d,0x15,0x43,0x9b,0xa1,0xb2,0x2,0x9a,0xd0,0xdf,0x0,0xce,0xd5,0x59,0xe8,0x42,0x2b,0x76,0x26,0xa6,0xd,0xdd,0xeb,0x50,0x78,0xf9,0xf8,0xc5,0xa0,0x44,0xd1,0x83,0xc7,0x49,0x33,0x1c,0x20,0x11,0xe1,0x40,0x8d,0x54,0x23,0x5e,0xd6,0x4a,0x3b,0x16,0x62,0x7a,0xc,0xd7,0x97,0x87,0xc2,0x1d,0x99,0xb,0x8c,0xdc,0xff,0x56,0xae,0x64,0xb4,0x9e,0x48,0x66,0x57,0x3a,0x7d,0x82,0x2e,0xa8,0x4d,0xd2,0x1a,0xd8,0xab,0xfe,0xa4,0x4,0x9f,0xea,0x28,0x6,0xf0,0x30,0xbf,0x6e,0x27,0x25,0x86,0x68,0xdb,0xb7,0x10,0x36,0x90,0x7f,0x50,0x39,0x27,0xc6,0xa2,0xf,0x91,0x48,0xea,0x4a,0xc7,0xfe,0x46,0x62,0xa5,0x2a,0xc,0xb6,0x20,0xdd,0x2c,0x4d,0xa6,0x5f,0x72,0x21,0xd5,0x8f,0x69,0xd1,0x6e,0xd,0x56,0x3c,0xa1,0x32,0x34,0x9a,0xa7,0x44,0xbb,0xff,0xd3,0xf7,0x85,0x86,0x3d,0xe7,0x78,0x83,0xfb,0x2b,0x9f,0x40,0x47,0x8a,0x30,0xcf,0xed,0xda,0xc0,0x4,0xaf,0xdc,0x94,0xe,0xd7,0xb8,0x88,0x1,0x5c,0xe0,0xf3,0x2,0x41,0xb7,0xbc,0x8b,0x1a,0x28,0xd4,0x6,0xb,0x3b,0xce,0x7,0xb9,0xab,0xc5,0x6f,0x8e,0x17,0x5,0x60,0xf2,0xfc,0x76,0x12,0x9c,0x6c,0x82,0xc8,0x58,0xa0,0xd0,0x96,0xb2,0x61,0x59,0x54,0x4c,0x74,0x68,0xde,0xbd,0xe9,0xf9,0x53,0x42,0x11,0x5a,0x80,0x99,0x89,0xa3,0x75,0x1e,0xf5,0x43,0x8d,0xc1,0x2d,0x19,0xa,0x66,0xb3,0x6b,0x1f,0xba,0x1c,0xae,0x4f,0x3a,0x52,0xf8,0x24,0x7d,0xd6,0xc2,0xfa,0xc4,0xdf,0x9e,0xf0,0x5e,0xc9,0x7c,0x45,0x15,0x95,0x3e,0xee,0xd8,0x63,0x4b,0xca,0xcb,0xf6,0x93,0x77,0xe2,0xb0,0xf4,0x26,0x70,0xa8,0x92,0x81,0x31,0xa9,0xe3,0xec,0x33,0xfd,0xe6,0x6a,0xdb,0x71,0x18,0x49,0x3f,0xe4,0xa4,0xb4,0xf1,0x2e,0xaa,0x38,0xbf,0xef,0xcc,0x65,0x9d,0x57,0x87,0x7a,0x0,0x2f,0x13,0x22,0xd2,0x73,0xbe,0x67,0x10,0x6d,0xe5,0x79,0x8,0x25,0x51,0x37,0xac,0xd9,0x1b,0x35,0xc3,0x3,0x8c,0x5d,0x14,0x16,0xb5,0x5b,0xe8,0x84,0x23,0xad,0x7b,0x55,0x64,0x9,0x4e,0xb1,0x1d,0x9b,0x7e,0xe1,0x29,0xeb,0x98,0xcd,0x97,0xa0,0x8d,0x74,0x9f,0xbb,0x5d,0x7,0xf3,0xde,0xf8,0x77,0xb0,0xfe,0xf,0xf2,0x64,0x38,0x9a,0x43,0xdd,0x94,0x2c,0x15,0x98,0x82,0xad,0x42,0xe4,0x70,0x14,0xf5,0xeb,0xe2,0x58,0x95,0x92,0x12,0x8,0x3f,0x1d,0xaa,0x35,0xef,0x54,0x4d,0xf9,0x29,0x51,0x69,0x96,0x75,0x48,0x57,0x25,0x1,0x2d,0x84,0xdf,0xbc,0x3,0xe6,0xe0,0x73,0xee,0x17,0x79,0x6b,0xd5,0xd7,0xc5,0x5c,0xbd,0x6,0xfa,0xc8,0x59,0x1c,0xe9,0xd9,0xd4,0x21,0x32,0x8e,0xd3,0x6e,0x65,0x93,0xd0,0x46,0xe,0x7d,0xd6,0x5a,0x6a,0x5,0xdc,0x88,0xc3,0x90,0x81,0x71,0x5b,0x4b,0x52,0xba,0xa6,0x9e,0x86,0x2b,0x3b,0x6f,0xc,0x2,0x72,0x8a,0x1a,0x8b,0xb3,0x60,0x44,0xa4,0x2e,0x20,0xb2,0x50,0xbe,0x4e,0xc0,0x4c,0xd,0x16,0x28,0xae,0x1b,0x8c,0x22,0x2a,0x80,0xe8,0x9d,0x10,0x4,0xaf,0xf6,0xb9,0x61,0xb4,0xd8,0x7c,0xce,0x68,0xcd,0x91,0x27,0xcc,0xa7,0xcb,0xff,0x13,0x5f,0x34,0x2f,0xe1,0x3e,0xca,0xa3,0x9,0xb8,0x40,0x7a,0xa2,0xf4,0x31,0x7b,0xe3,0x53,0x41,0x24,0x19,0x18,0x26,0x62,0x30,0xa5,0xec,0x47,0xc7,0x97,0x99,0xb1,0xa,0x3c,0x37,0xbf,0xc2,0xb5,0x83,0xf7,0xda,0xab,0xc1,0xfd,0xd2,0xa8,0x6c,0xa1,0x0,0xf0,0x1e,0x3d,0x6d,0xea,0x55,0x85,0x4f,0xb7,0x76,0x36,0xed,0x9b,0x78,0xfc,0x23,0x66,0xfb,0x33,0xac,0x49,0x45,0x1f,0x4a,0x39,0xb6,0x87,0xa9,0x7f,0xcf,0x63,0x9c,0xdb,0x67,0xc4,0xc6,0x8f,0xf1,0x56,0x3a,0x89,0xc9,0xb,0x7e,0xe5,0x5e,0xd1,0x11,0xe7,0xc5,0xc3,0xcb,0x56,0xfa,0xa1,0x26,0x99,0x0,0x72,0x8,0x24,0xb3,0x4c,0x6d,0x50,0xdc,0x68,0x74,0xc,0x10,0x8f,0x71,0xca,0x2d,0x37,0x38,0x1a,0x7d,0xc7,0xb7,0xb0,0x31,0x55,0xce,0xd0,0x88,0xa7,0xc1,0x67,0x9,0xb1,0xbd,0x30,0xbf,0x1d,0xf8,0x66,0x2a,0xdb,0x41,0xd7,0xdd,0xfb,0x95,0x52,0x78,0x9e,0xd6,0x22,0xa8,0x85,0xba,0x51,0x9b,0x75,0xe5,0x6b,0xb,0x81,0x97,0x5,0x96,0xae,0x61,0x45,0x57,0x27,0x3f,0xaf,0x1e,0xe,0x29,0x4a,0x83,0x9f,0xa3,0xbb,0x7e,0x54,0x77,0x6e,0xe6,0xad,0xa4,0xb5,0x4f,0x7f,0xf9,0x20,0x2b,0x63,0xf3,0x58,0x40,0x4b,0xf5,0xb6,0x17,0x4,0xf6,0xab,0xcc,0x39,0xf1,0xfc,0xdf,0x23,0x7c,0xed,0xe0,0xf2,0x98,0x79,0x5c,0x32,0xf0,0x4e,0x94,0xbc,0x19,0x2f,0x62,0xc9,0xb2,0xe2,0x47,0x3,0x80,0x15,0x1,0x64,0x3d,0x3c,0x5e,0x14,0x76,0xc6,0x5f,0x65,0xd1,0x87,0x86,0xef,0x9d,0x2c,0xa,0x11,0x1b,0xc4,0xda,0xee,0x7a,0x36,0x2,0xb4,0x82,0xe9,0xeb,0x59,0xe8,0x4d,0x44,0x9c,0xfd,0x91,0x21,0x35,0xd3,0x8a,0xa5,0xf,0xb8,0xcd,0x3e,0x8b,0x7,0xa9,0x28,0x69,0xd,0x33,0xf4,0x7b,0xc2,0x34,0x2e,0xec,0xc0,0x5b,0x73,0xd4,0xac,0x1f,0xe1,0x42,0xaa,0xe3,0x46,0xea,0xfe,0xb9,0xa2,0x93,0x5a,0x8c,0x3a,0x60,0x1c,0x6f,0x16,0xde,0x6c,0x89,0xd9,0x5d,0x43,0x6,0x13,0x53,0xbe,0xc8,0xa0,0x70,0x92,0x6a,0x18,0x3b,0xcf,0x48,0x84,0x49,0xd5,0x25,0xd8,0xe4,0x8d,0xf7,0xd2,0xa6,0x8e,0xff,0x9a,0x12,0x90,0xe7,0xf8,0x3f,0xb0,0x96,0x2c,0xba,0x47,0xb6,0xd7,0x3c,0xc5,0xe8,0xbb,0x4f,0x15,0xf3,0xac,0xa,0xe5,0xca,0xa3,0xbd,0x5c,0x38,0x95,0xb,0xd2,0x70,0xd0,0x5d,0x64,0xdc,0x1c,0xa7,0x7d,0xe2,0x19,0x61,0xb1,0x5,0xda,0xdd,0x10,0xaa,0x55,0x77,0x40,0x5a,0x4b,0xf4,0x97,0xcc,0xa6,0x3b,0xa8,0xae,0x0,0x3d,0xde,0x21,0x65,0x49,0x6d,0x1f,0x11,0x80,0xb2,0x4e,0x9c,0x91,0xa1,0x54,0x9d,0x23,0x31,0x5f,0xf5,0x14,0x8d,0x9f,0x9e,0x35,0x46,0xe,0x94,0x4d,0x22,0x12,0x9b,0xc6,0x7a,0x69,0x98,0xdb,0x2d,0x26,0xce,0xd6,0xee,0xf2,0x44,0x27,0x73,0x63,0xc9,0xd8,0x8b,0xc0,0x1a,0x3,0x13,0x39,0xfa,0x68,0x66,0xec,0x88,0x6,0xf6,0x18,0x52,0xc2,0x3a,0x4a,0xc,0x28,0xfb,0xc3,0xd5,0xa0,0xc8,0x62,0xbe,0xe7,0x4c,0x58,0x60,0x5e,0x45,0x4,0x6a,0xc4,0x53,0xe6,0xef,0x84,0x6f,0xd9,0x17,0x5b,0xb7,0x83,0x90,0xfc,0x29,0xf1,0x85,0x20,0x86,0x34,0xbc,0xea,0x32,0x8,0x1b,0xab,0x33,0x79,0x76,0xa9,0x67,0x7c,0xf0,0x41,0xeb,0x82,0xdf,0x8f,0xf,0xa4,0x74,0x42,0xf9,0xd1,0x50,0x51,0x6c,0x9,0xed,0x78,0x2a,0x6e,0xe0,0x9a,0xb5,0x89,0xb8,0x48,0xe9,0x24,0xfd,0x8a,0xf7,0x7f,0xe3,0x92,0xbf,0xcb,0xd3,0xa5,0x7e,0x3e,0x2e,0x6b,0xb4,0x30,0xa2,0x25,0x75,0x56,0xff,0x7,0xcd,0x1d,0x37,0xe1,0xcf,0xfe,0x93,0xd4,0x2b,0x87,0x1,0xe4,0x7b,0xb3,0x71,0x2,0x57,0xd,0xad,0x36,0x43,0x81,0xaf,0x59,0x99,0x16,0xc7,0x8e,0x8c,0x2f,0xc1,0x72,0x1e,0xb9,0xab,0xd,0xe2,0xcd,0xa4,0xba,0x5b,0x3f,0x92,0xc,0xd5,0x77,0xd7,0x5a,0x63,0xdb,0xff,0x38,0xb7,0x91,0x2b,0xbd,0x40,0xb1,0xd0,0x3b,0xc2,0xef,0xbc,0x48,0x12,0xf4,0x4c,0xf3,0x90,0xcb,0xa1,0x3c,0xaf,0xa9,0x7,0x3a,0xd9,0x26,0x62,0x4e,0x6a,0x18,0x1b,0xa0,0x7a,0xe5,0x1e,0x66,0xb6,0x2,0xdd,0xda,0x17,0xad,0x52,0x70,0x47,0x5d,0x99,0x32,0x41,0x9,0x93,0x4a,0x25,0x15,0x9c,0xc1,0x7d,0x6e,0x9f,0xdc,0x2a,0x21,0x16,0x87,0xb5,0x49,0x9b,0x96,0xa6,0x53,0x9a,0x24,0x36,0x58,0xf2,0x13,0x8a,0x98,0xfd,0x6f,0x61,0xeb,0x8f,0x1,0xf1,0x1f,0x55,0xc5,0x3d,0x4d,0xb,0x2f,0xfc,0xc4,0xc9,0xd1,0xe9,0xf5,0x43,0x20,0x74,0x64,0xce,0xdf,0x8c,0xc7,0x1d,0x4,0x14,0x3e,0xe8,0x83,0x68,0xde,0x10,0x5c,0xb0,0x84,0x97,0xfb,0x2e,0xf6,0x82,0x27,0x81,0x33,0xd2,0xa7,0xcf,0x65,0xb9,0xe0,0x4b,0x5f,0x67,0x59,0x42,0x3,0x6d,0xc3,0x54,0xe1,0xd8,0x88,0x8,0xa3,0x73,0x45,0xfe,0xd6,0x57,0x56,0x6b,0xe,0xea,0x7f,0x2d,0x69,0xbb,0xed,0x35,0xf,0x1c,0xac,0x34,0x7e,0x71,0xae,0x60,0x7b,0xf7,0x46,0xec,0x85,0xd4,0xa2,0x79,0x39,0x29,0x6c,0xb3,0x37,0xa5,0x22,0x72,0x51,0xf8,0x0,0xca,0x1a,0xe7,0x9d,0xb2,0x8e,0xbf,0x4f,0xee,0x23,0xfa,0x8d,0xf0,0x78,0xe4,0x95,0xb8,0xcc,0xaa,0x31,0x44,0x86,0xa8,0x5e,0x9e,0x11,0xc0,0x89,0x8b,0x28,0xc6,0x75,0x19,0xbe,0x30,0xe6,0xc8,0xf9,0x94,0xd3,0x2c,0x80,0x6,0xe3,0x7c,0xb4,0x76,0x5,0x50,0xa,0x8b,0xa6,0x5f,0xb4,0x90,0x76,0x2c,0xd8,0xf5,0xd3,0x5c,0x9b,0xd5,0x24,0xd9,0x4f,0x13,0xb1,0x68,0xf6,0xbf,0x7,0x3e,0xb3,0xa9,0x86,0x69,0xcf,0x5b,0x3f,0xde,0xc0,0xc9,0x73,0xbe,0xb9,0x39,0x23,0x14,0x36,0x81,0x1e,0xc4,0x7f,0x66,0xd2,0x2,0x7a,0x42,0xbd,0x5e,0x63,0x7c,0xe,0x2a,0x6,0xaf,0xf4,0x97,0x28,0xcd,0xcb,0x58,0xc5,0x3c,0x52,0x40,0xfe,0xfc,0xee,0x77,0x96,0x2d,0xd1,0xe3,0x72,0x37,0xc2,0xf2,0xff,0xa,0x19,0xa5,0xf8,0x45,0x4e,0xb8,0xfb,0x6d,0x25,0x56,0xfd,0x71,0x41,0x2e,0xf7,0xa3,0xe8,0xbb,0xaa,0x5a,0x70,0x60,0x79,0x91,0x8d,0xb5,0xad,0x0,0x10,0x44,0x27,0x29,0x59,0xa1,0x31,0xa0,0x98,0x4b,0x6f,0x8f,0x5,0xb,0x99,0x7b,0x95,0x65,0xeb,0x67,0x26,0x3d,0x3,0x85,0x30,0xa7,0x9,0x1,0xab,0xc3,0xb6,0x3b,0x2f,0x84,0xdd,0x92,0x4a,0x9f,0xf3,0x57,0xe5,0x43,0xe6,0xba,0xc,0xe7,0x8c,0xe0,0xd4,0x38,0x74,0x1f,0x4,0xca,0x15,0xe1,0x88,0x22,0x93,0x6b,0x51,0x89,0xdf,0x1a,0x50,0xc8,0x78,0x6a,0xf,0x32,0x33,0xd,0x49,0x1b,0x8e,0xc7,0x6c,0xec,0xbc,0xb2,0x9a,0x21,0x17,0x1c,0x94,0xe9,0x9e,0xa8,0xdc,0xf1,0x80,0xea,0xd6,0xf9,0x83,0x47,0x8a,0x2b,0xdb,0x35,0x16,0x46,0xc1,0x7e,0xae,0x64,0x9c,0x5d,0x1d,0xc6,0xb0,0x53,0xd7,0x8,0x4d,0xd0,0x18,0x87,0x62,0x6e,0x34,0x61,0x12,0x9d,0xac,0x82,0x54,0xe4,0x48,0xb7,0xf0,0x4c,0xef,0xed,0xa4,0xda,0x7d,0x11,0xa2,0xe2,0x20,0x55,0xce,0x75,0xfa,0x3a,0xcc,0x7e,0x78,0x70,0xed,0x41,0x1a,0x9d,0x22,0xbb,0xc9,0xb3,0x9f,0x8,0xf7,0xd6,0xeb,0x67,0xd3,0xcf,0xb7,0xab,0x34,0xca,0x71,0x96,0x8c,0x83,0xa1,0xc6,0x7c,0xc,0xb,0x8a,0xee,0x75,0x6b,0x33,0x1c,0x7a,0xdc,0xb2,0xa,0x6,0x8b,0x4,0xa6,0x43,0xdd,0x91,0x60,0xfa,0x6c,0x66,0x40,0x2e,0xe9,0xc3,0x25,0x6d,0x99,0x13,0x3e,0x1,0xea,0x20,0xce,0x5e,0xd0,0xb0,0x3a,0x2c,0xbe,0x2d,0x15,0xda,0xfe,0xec,0x9c,0x84,0x14,0xa5,0xb5,0x92,0xf1,0x38,0x24,0x18,0x0,0xc5,0xef,0xcc,0xd5,0x5d,0x16,0x1f,0xe,0xf4,0xc4,0x42,0x9b,0x90,0xd8,0x48,0xe3,0xfb,0xf0,0x4e,0xd,0xac,0xbf,0x4d,0x10,0x77,0x82,0x4a,0x47,0x64,0x98,0xc7,0x56,0x5b,0x49,0x23,0xc2,0xe7,0x89,0x4b,0xf5,0x2f,0x7,0xa2,0x94,0xd9,0x72,0x9,0x59,0xfc,0xb8,0x3b,0xae,0xba,0xdf,0x86,0x87,0xe5,0xaf,0xcd,0x7d,0xe4,0xde,0x6a,0x3c,0x3d,0x54,0x26,0x97,0xb1,0xaa,0xa0,0x7f,0x61,0x55,0xc1,0x8d,0xb9,0xf,0x39,0x52,0x50,0xe2,0x53,0xf6,0xff,0x27,0x46,0x2a,0x9a,0x8e,0x68,0x31,0x1e,0xb4,0x3,0x76,0x85,0x30,0xbc,0x12,0x93,0xd2,0xb6,0x88,0x4f,0xc0,0x79,0x8f,0x95,0x57,0x7b,0xe0,0xc8,0x6f,0x17,0xa4,0x5a,0xf9,0x11,0x58,0xfd,0x51,0x45,0x2,0x19,0x28,0xe1,0x37,0x81,0xdb,0xa7,0xd4,0xad,0x65,0xd7,0x32,0x62,0xe6,0xf8,0xbd,0xa8,0xe8,0x5,0x73,0x1b,0xcb,0x29,0xd1,0xa3,0x80,0x74,0xf3,0x3f,0xf2,0x6e,0x9e,0x63,0x5f,0x36,0x4c,0x69,0x1d,0x35,0x44,0x21,0xa9,0x2b,0x5c,0x46,0x81,0xe,0x28,0x92,0x4,0xf9,0x8,0x69,0x82,0x7b,0x56,0x5,0xf1,0xab,0x4d,0x12,0xb4,0x5b,0x74,0x1d,0x3,0xe2,0x86,0x2b,0xb5,0x6c,0xce,0x6e,0xe3,0xda,0x62,0xa2,0x19,0xc3,0x5c,0xa7,0xdf,0xf,0xbb,0x64,0x63,0xae,0x14,0xeb,0xc9,0xfe,0xe4,0xf5,0x4a,0x29,0x72,0x18,0x85,0x16,0x10,0xbe,0x83,0x60,0x9f,0xdb,0xf7,0xd3,0xa1,0xaf,0x3e,0xc,0xf0,0x22,0x2f,0x1f,0xea,0x23,0x9d,0x8f,0xe1,0x4b,0xaa,0x33,0x21,0x20,0x8b,0xf8,0xb0,0x2a,0xf3,0x9c,0xac,0x25,0x78,0xc4,0xd7,0x26,0x65,0x93,0x98,0x70,0x68,0x50,0x4c,0xfa,0x99,0xcd,0xdd,0x77,0x66,0x35,0x7e,0xa4,0xbd,0xad,0x87,0x44,0xd6,0xd8,0x52,0x36,0xb8,0x48,0xa6,0xec,0x7c,0x84,0xf4,0xb2,0x96,0x45,0x7d,0x6b,0x1e,0x76,0xdc,0x0,0x59,0xf2,0xe6,0xde,0xe0,0xfb,0xba,0xd4,0x7a,0xed,0x58,0x51,0x3a,0xd1,0x67,0xa9,0xe5,0x9,0x3d,0x2e,0x42,0x97,0x4f,0x3b,0x9e,0x38,0x8a,0x2,0x54,0x8c,0xb6,0xa5,0x15,0x8d,0xc7,0xc8,0x17,0xd9,0xc2,0x4e,0xff,0x55,0x3c,0x61,0x31,0xb1,0x1a,0xca,0xfc,0x47,0x6f,0xee,0xef,0xd2,0xb7,0x53,0xc6,0x94,0xd0,0x5e,0x24,0xb,0x37,0x6,0xf6,0x57,0x9a,0x43,0x34,0x49,0xc1,0x5d,0x2c,0x1,0x75,0x6d,0x1b,0xc0,0x80,0x90,0xd5,0xa,0x8e,0x1c,0x9b,0xcb,0xe8,0x41,0xb9,0x73,0xa3,0x89,0x5f,0x71,0x40,0x2d,0x6a,0x95,0x39,0xbf,0x5a,0xc5,0xd,0xcf,0xbc,0xe9,0xb3,0x13,0x88,0xfd,0x3f,0x11,0xe7,0x27,0xa8,0x79,0x30,0x32,0x91,0x7f,0xcc,0xa0,0x7,0x43,0xe5,0xa,0x25,0x4c,0x52,0xb3,0xd7,0x7a,0xe4,0x3d,0x9f,0x3f,0xb2,0x8b,0x33,0x17,0xd0,0x5f,0x79,0xc3,0x55,0xa8,0x59,0x38,0xd3,0x2a,0x7,0x54,0xa0,0xfa,0x1c,0xa4,0x1b,0x78,0x23,0x49,0xd4,0x47,0x41,0xef,0xd2,0x31,0xce,0x8a,0xa6,0x82,0xf0,0xf3,0x48,0x92,0xd,0xf6,0x8e,0x5e,0xea,0x35,0x32,0xff,0x45,0xba,0x98,0xaf,0xb5,0x71,0xda,0xa9,0xe1,0x7b,0xa2,0xcd,0xfd,0x74,0x29,0x95,0x86,0x77,0x34,0xc2,0xc9,0xfe,0x6f,0x5d,0xa1,0x73,0x7e,0x4e,0xbb,0x72,0xcc,0xde,0xb0,0x1a,0xfb,0x62,0x70,0x15,0x87,0x89,0x3,0x67,0xe9,0x19,0xf7,0xbd,0x2d,0xd5,0xa5,0xe3,0xc7,0x14,0x2c,0x21,0x39,0x1,0x1d,0xab,0xc8,0x9c,0x8c,0x26,0x37,0x64,0x2f,0xf5,0xec,0xfc,0xd6,0x0,0x6b,0x80,0x36,0xf8,0xb4,0x58,0x6c,0x7f,0x13,0xc6,0x1e,0x6a,0xcf,0x69,0xdb,0x3a,0x4f,0x27,0x8d,0x51,0x8,0xa3,0xb7,0x8f,0xb1,0xaa,0xeb,0x85,0x2b,0xbc,0x9,0x30,0x60,0xe0,0x4b,0x9b,0xad,0x16,0x3e,0xbf,0xbe,0x83,0xe6,0x2,0x97,0xc5,0x81,0x53,0x5,0xdd,0xe7,0xf4,0x44,0xdc,0x96,0x99,0x46,0x88,0x93,0x1f,0xae,0x4,0x6d,0x3c,0x4a,0x91,0xd1,0xc1,0x84,0x5b,0xdf,0x4d,0xca,0x9a,0xb9,0x10,0xe8,0x22,0xf2,0xf,0x75,0x5a,0x66,0x57,0xa7,0x6,0xcb,0x12,0x65,0x18,0x90,0xc,0x7d,0x50,0x24,0x42,0xd9,0xac,0x6e,0x40,0xb6,0x76,0xf9,0x28,0x61,0x63,0xc0,0x2e,0x9d,0xf1,0x56,0xd8,0xe,0x20,0x11,0x7c,0x3b,0xc4,0x68,0xee,0xb,0x94,0x5c,0x9e,0xed,0xb8,0xe2,0xc2,0xef,0x16,0xfd,0xd9,0x3f,0x65,0x91,0xbc,0x9a,0x15,0xd2,0x9c,0x6d,0x90,0x6,0x5a,0xf8,0x21,0xbf,0xf6,0x4e,0x77,0xfa,0xe0,0xcf,0x20,0x86,0x12,0x76,0x97,0x89,0x80,0x3a,0xf7,0xf0,0x70,0x6a,0x5d,0x7f,0xc8,0x57,0x8d,0x36,0x2f,0x9b,0x4b,0x33,0xb,0xf4,0x17,0x2a,0x35,0x47,0x63,0x4f,0xe6,0xbd,0xde,0x61,0x84,0x82,0x11,0x8c,0x75,0x1b,0x9,0xb7,0xb5,0xa7,0x3e,0xdf,0x64,0x98,0xaa,0x3b,0x7e,0x8b,0xbb,0xb6,0x43,0x50,0xec,0xb1,0xc,0x7,0xf1,0xb2,0x24,0x6c,0x1f,0xb4,0x38,0x8,0x67,0xbe,0xea,0xa1,0xf2,0xe3,0x13,0x39,0x29,0x30,0xd8,0xc4,0xfc,0xe4,0x49,0x59,0xd,0x6e,0x60,0x10,0xe8,0x78,0xe9,0xd1,0x2,0x26,0xc6,0x4c,0x42,0xd0,0x32,0xdc,0x2c,0xa2,0x2e,0x6f,0x74,0x4a,0xcc,0x79,0xee,0x40,0x48,0xe2,0x8a,0xff,0x72,0x66,0xcd,0x94,0xdb,0x3,0xd6,0xba,0x1e,0xac,0xa,0xaf,0xf3,0x45,0xae,0xc5,0xa9,0x9d,0x71,0x3d,0x56,0x4d,0x83,0x5c,0xa8,0xc1,0x6b,0xda,0x22,0x18,0xc0,0x96,0x53,0x19,0x81,0x31,0x23,0x46,0x7b,0x7a,0x44,0x0,0x52,0xc7,0x8e,0x25,0xa5,0xf5,0xfb,0xd3,0x68,0x5e,0x55,0xdd,0xa0,0xd7,0xe1,0x95,0xb8,0xc9,0xa3,0x9f,0xb0,0xca,0xe,0xc3,0x62,0x92,0x7c,0x5f,0xf,0x88,0x37,0xe7,0x2d,0xd5,0x14,0x54,0x8f,0xf9,0x1a,0x9e,0x41,0x4,0x99,0x51,0xce,0x2b,0x27,0x7d,0x28,0x5b,0xd4,0xe5,0xcb,0x1d,0xad,0x1,0xfe,0xb9,0x5,0xa6,0xa4,0xed,0x93,0x34,0x58,0xeb,0xab,0x69,0x1c,0x87,0x3c,0xb3,0x73,0x85,0xae,0xa8,0xa0,0x3d,0x91,0xca,0x4d,0xf2,0x6b,0x19,0x63,0x4f,0xd8,0x27,0x6,0x3b,0xb7,0x3,0x1f,0x67,0x7b,0xe4,0x1a,0xa1,0x46,0x5c,0x53,0x71,0x16,0xac,0xdc,0xdb,0x5a,0x3e,0xa5,0xbb,0xe3,0xcc,0xaa,0xc,0x62,0xda,0xd6,0x5b,0xd4,0x76,0x93,0xd,0x41,0xb0,0x2a,0xbc,0xb6,0x90,0xfe,0x39,0x13,0xf5,0xbd,0x49,0xc3,0xee,0xd1,0x3a,0xf0,0x1e,0x8e,0x0,0x60,0xea,0xfc,0x6e,0xfd,0xc5,0xa,0x2e,0x3c,0x4c,0x54,0xc4,0x75,0x65,0x42,0x21,0xe8,0xf4,0xc8,0xd0,0x15,0x3f,0x1c,0x5,0x8d,0xc6,0xcf,0xde,0x24,0x14,0x92,0x4b,0x40,0x8,0x98,0x33,0x2b,0x20,0x9e,0xdd,0x7c,0x6f,0x9d,0xc0,0xa7,0x52,0x9a,0x97,0xb4,0x48,0x17,0x86,0x8b,0x99,0xf3,0x12,0x37,0x59,0x9b,0x25,0xff,0xd7,0x72,0x44,0x9,0xa2,0xd9,0x89,0x2c,0x68,0xeb,0x7e,0x6a,0xf,0x56,0x57,0x35,0x7f,0x1d,0xad,0x34,0xe,0xba,0xec,0xed,0x84,0xf6,0x47,0x61,0x7a,0x70,0xaf,0xb1,0x85,0x11,0x5d,0x69,0xdf,0xe9,0x82,0x80,0x32,0x83,0x26,0x2f,0xf7,0x96,0xfa,0x4a,0x5e,0xb8,0xe1,0xce,0x64,0xd3,0xa6,0x55,0xe0,0x6c,0xc2,0x43,0x2,0x66,0x58,0x9f,0x10,0xa9,0x5f,0x45,0x87,0xab,0x30,0x18,0xbf,0xc7,0x74,0x8a,0x29,0xc1,0x88,0x2d,0x81,0x95,0xd2,0xc9,0xf8,0x31,0xe7,0x51,0xb,0x77,0x4,0x7d,0xb5,0x7,0xe2,0xb2,0x36,0x28,0x6d,0x78,0x38,0xd5,0xa3,0xcb,0x1b,0xf9,0x1,0x73,0x50,0xa4,0x23,0xef,0x22,0xbe,0x4e,0xb3,0x8f,0xe6,0x9c,0xb9,0xcd,0xe5,0x94,0xf1,0x79,0xfb,0x8c,0x61,0xa6,0x29,0xf,0xb5,0x23,0xde,0x2f,0x4e,0xa5,0x5c,0x71,0x22,0xd6,0x8c,0x6a,0x35,0x93,0x7c,0x53,0x3a,0x24,0xc5,0xa1,0xc,0x92,0x4b,0xe9,0x49,0xc4,0xfd,0x45,0x85,0x3e,0xe4,0x7b,0x80,0xf8,0x28,0x9c,0x43,0x44,0x89,0x33,0xcc,0xee,0xd9,0xc3,0xd2,0x6d,0xe,0x55,0x3f,0xa2,0x31,0x37,0x99,0xa4,0x47,0xb8,0xfc,0xd0,0xf4,0x86,0x88,0x19,0x2b,0xd7,0x5,0x8,0x38,0xcd,0x4,0xba,0xa8,0xc6,0x6c,0x8d,0x14,0x6,0x7,0xac,0xdf,0x97,0xd,0xd4,0xbb,0x8b,0x2,0x5f,0xe3,0xf0,0x1,0x42,0xb4,0xbf,0x57,0x4f,0x77,0x6b,0xdd,0xbe,0xea,0xfa,0x50,0x41,0x12,0x59,0x83,0x9a,0x8a,0xa0,0x63,0xf1,0xff,0x75,0x11,0x9f,0x6f,0x81,0xcb,0x5b,0xa3,0xd3,0x95,0xb1,0x62,0x5a,0x4c,0x39,0x51,0xfb,0x27,0x7e,0xd5,0xc1,0xf9,0xc7,0xdc,0x9d,0xf3,0x5d,0xca,0x7f,0x76,0x1d,0xf6,0x40,0x8e,0xc2,0x2e,0x1a,0x9,0x65,0xb0,0x68,0x1c,0xb9,0x1f,0xad,0x25,0x73,0xab,0x91,0x82,0x32,0xaa,0xe0,0xef,0x30,0xfe,0xe5,0x69,0xd8,0x72,0x1b,0x46,0x16,0x96,0x3d,0xed,0xdb,0x60,0x48,0xc9,0xc8,0xf5,0x90,0x74,0xe1,0xb3,0xf7,0x79,0x3,0x2c,0x10,0x21,0xd1,0x70,0xbd,0x64,0x13,0x6e,0xe6,0x7a,0xb,0x26,0x52,0x4a,0x3c,0xe7,0xa7,0xb7,0xf2,0x2d,0xa9,0x3b,0xbc,0xec,0xcf,0x66,0x9e,0x54,0x84,0xae,0x78,0x56,0x67,0xa,0x4d,0xb2,0x1e,0x98,0x7d,0xe2,0x2a,0xe8,0x9b,0xce,0x94,0x34,0xaf,0xda,0x18,0x36,0xc0,0x0,0x8f,0x5e,0x17,0x15,0xb6,0x58,0xeb,0x87,0x20,0x3e,0x98,0x77,0x58,0x31,0x2f,0xce,0xaa,0x7,0x99,0x40,0xe2,0x42,0xcf,0xf6,0x4e,0x6a,0xad,0x22,0x4,0xbe,0x28,0xd5,0x24,0x45,0xae,0x57,0x7a,0x29,0xdd,0x87,0x61,0xd9,0x66,0x5,0x5e,0x34,0xa9,0x3a,0x3c,0x92,0xaf,0x4c,0xb3,0xf7,0xdb,0xff,0x8d,0x8e,0x35,0xef,0x70,0x8b,0xf3,0x23,0x97,0x48,0x4f,0x82,0x38,0xc7,0xe5,0xd2,0xc8,0xc,0xa7,0xd4,0x9c,0x6,0xdf,0xb0,0x80,0x9,0x54,0xe8,0xfb,0xa,0x49,0xbf,0xb4,0x83,0x12,0x20,0xdc,0xe,0x3,0x33,0xc6,0xf,0xb1,0xa3,0xcd,0x67,0x86,0x1f,0xd,0x68,0xfa,0xf4,0x7e,0x1a,0x94,0x64,0x8a,0xc0,0x50,0xa8,0xd8,0x9e,0xba,0x69,0x51,0x5c,0x44,0x7c,0x60,0xd6,0xb5,0xe1,0xf1,0x5b,0x4a,0x19,0x52,0x88,0x91,0x81,0xab,0x7d,0x16,0xfd,0x4b,0x85,0xc9,0x25,0x11,0x2,0x6e,0xbb,0x63,0x17,0xb2,0x14,0xa6,0x47,0x32,0x5a,0xf0,0x2c,0x75,0xde,0xca,0xf2,0xcc,0xd7,0x96,0xf8,0x56,0xc1,0x74,0x4d,0x1d,0x9d,0x36,0xe6,0xd0,0x6b,0x43,0xc2,0xc3,0xfe,0x9b,0x7f,0xea,0xb8,0xfc,0x2e,0x78,0xa0,0x9a,0x89,0x39,0xa1,0xeb,0xe4,0x3b,0xf5,0xee,0x62,0xd3,0x79,0x10,0x41,0x37,0xec,0xac,0xbc,0xf9,0x26,0xa2,0x30,0xb7,0xe7,0xc4,0x6d,0x95,0x5f,0x8f,0x72,0x8,0x27,0x1b,0x2a,0xda,0x7b,0xb6,0x6f,0x18,0x65,0xed,0x71,0x0,0x2d,0x59,0x3f,0xa4,0xd1,0x13,0x3d,0xcb,0xb,0x84,0x55,0x1c,0x1e,0xbd,0x53,0xe0,0x8c,0x2b,0xa5,0x73,0x5d,0x6c,0x1,0x46,0xb9,0x15,0x93,0x76,0xe9,0x21,0xe3,0x90,0xc5,0x9f,0xc,0x21,0xd8,0x33,0x17,0xf1,0xab,0x5f,0x72,0x54,0xdb,0x1c,0x52,0xa3,0x5e,0xc8,0x94,0x36,0xef,0x71,0x38,0x80,0xb9,0x34,0x2e,0x1,0xee,0x48,0xdc,0xb8,0x59,0x47,0x4e,0xf4,0x39,0x3e,0xbe,0xa4,0x93,0xb1,0x6,0x99,0x43,0xf8,0xe1,0x55,0x85,0xfd,0xc5,0x3a,0xd9,0xe4,0xfb,0x89,0xad,0x81,0x28,0x73,0x10,0xaf,0x4a,0x4c,0xdf,0x42,0xbb,0xd5,0xc7,0x79,0x7b,0x69,0xf0,0x11,0xaa,0x56,0x64,0xf5,0xb0,0x45,0x75,0x78,0x8d,0x9e,0x22,0x7f,0xc2,0xc9,0x3f,0x7c,0xea,0xa2,0xd1,0x7a,0xf6,0xc6,0xa9,0x70,0x24,0x6f,0x3c,0x2d,0xdd,0xf7,0xe7,0xfe,0x16,0xa,0x32,0x2a,0x87,0x97,0xc3,0xa0,0xae,0xde,0x26,0xb6,0x27,0x1f,0xcc,0xe8,0x8,0x82,0x8c,0x1e,0xfc,0x12,0xe2,0x6c,0xe0,0xa1,0xba,0x84,0x2,0xb7,0x20,0x8e,0x86,0x2c,0x44,0x31,0xbc,0xa8,0x3,0x5a,0x15,0xcd,0x18,0x74,0xd0,0x62,0xc4,0x61,0x3d,0x8b,0x60,0xb,0x67,0x53,0xbf,0xf3,0x98,0x83,0x4d,0x92,0x66,0xf,0xa5,0x14,0xec,0xd6,0xe,0x58,0x9d,0xd7,0x4f,0xff,0xed,0x88,0xb5,0xb4,0x8a,0xce,0x9c,0x9,0x40,0xeb,0x6b,0x3b,0x35,0x1d,0xa6,0x90,0x9b,0x13,0x6e,0x19,0x2f,0x5b,0x76,0x7,0x6d,0x51,0x7e,0x4,0xc0,0xd,0xac,0x5c,0xb2,0x91,0xc1,0x46,0xf9,0x29,0xe3,0x1b,0xda,0x9a,0x41,0x37,0xd4,0x50,0x8f,0xca,0x57,0x9f,0x0,0xe5,0xe9,0xb3,0xe6,0x95,0x1a,0x2b,0x5,0xd3,0x63,0xcf,0x30,0x77,0xcb,0x68,0x6a,0x23,0x5d,0xfa,0x96,0x25,0x65,0xa7,0xd2,0x49,0xf2,0x7d,0xbd,0x4b,0x19,0x1f,0x17,0x8a,0x26,0x7d,0xfa,0x45,0xdc,0xae,0xd4,0xf8,0x6f,0x90,0xb1,0x8c,0x0,0xb4,0xa8,0xd0,0xcc,0x53,0xad,0x16,0xf1,0xeb,0xe4,0xc6,0xa1,0x1b,0x6b,0x6c,0xed,0x89,0x12,0xc,0x54,0x7b,0x1d,0xbb,0xd5,0x6d,0x61,0xec,0x63,0xc1,0x24,0xba,0xf6,0x7,0x9d,0xb,0x1,0x27,0x49,0x8e,0xa4,0x42,0xa,0xfe,0x74,0x59,0x66,0x8d,0x47,0xa9,0x39,0xb7,0xd7,0x5d,0x4b,0xd9,0x4a,0x72,0xbd,0x99,0x8b,0xfb,0xe3,0x73,0xc2,0xd2,0xf5,0x96,0x5f,0x43,0x7f,0x67,0xa2,0x88,0xab,0xb2,0x3a,0x71,0x78,0x69,0x93,0xa3,0x25,0xfc,0xf7,0xbf,0x2f,0x84,0x9c,0x97,0x29,0x6a,0xcb,0xd8,0x2a,0x77,0x10,0xe5,0x2d,0x20,0x3,0xff,0xa0,0x31,0x3c,0x2e,0x44,0xa5,0x80,0xee,0x2c,0x92,0x48,0x60,0xc5,0xf3,0xbe,0x15,0x6e,0x3e,0x9b,0xdf,0x5c,0xc9,0xdd,0xb8,0xe1,0xe0,0x82,0xc8,0xaa,0x1a,0x83,0xb9,0xd,0x5b,0x5a,0x33,0x41,0xf0,0xd6,0xcd,0xc7,0x18,0x6,0x32,0xa6,0xea,0xde,0x68,0x5e,0x35,0x37,0x85,0x34,0x91,0x98,0x40,0x21,0x4d,0xfd,0xe9,0xf,0x56,0x79,0xd3,0x64,0x11,0xe2,0x57,0xdb,0x75,0xf4,0xb5,0xd1,0xef,0x28,0xa7,0x1e,0xe8,0xf2,0x30,0x1c,0x87,0xaf,0x8,0x70,0xc3,0x3d,0x9e,0x76,0x3f,0x9a,0x36,0x22,0x65,0x7e,0x4f,0x86,0x50,0xe6,0xbc,0xc0,0xb3,0xca,0x2,0xb0,0x55,0x5,0x81,0x9f,0xda,0xcf,0x8f,0x62,0x14,0x7c,0xac,0x4e,0xb6,0xc4,0xe7,0x13,0x94,0x58,0x95,0x9,0xf9,0x4,0x38,0x51,0x2b,0xe,0x7a,0x52,0x23,0x46,0xce,0x4c,0x3b,0xcf,0x8,0x87,0xa1,0x1b,0x8d,0x70,0x81,0xe0,0xb,0xf2,0xdf,0x8c,0x78,0x22,0xc4,0x9b,0x3d,0xd2,0xfd,0x94,0x8a,0x6b,0xf,0xa2,0x3c,0xe5,0x47,0xe7,0x6a,0x53,0xeb,0x2b,0x90,0x4a,0xd5,0x2e,0x56,0x86,0x32,0xed,0xea,0x27,0x9d,0x62,0x40,0x77,0x6d,0x7c,0xc3,0xa0,0xfb,0x91,0xc,0x9f,0x99,0x37,0xa,0xe9,0x16,0x52,0x7e,0x5a,0x28,0x26,0xb7,0x85,0x79,0xab,0xa6,0x96,0x63,0xaa,0x14,0x6,0x68,0xc2,0x23,0xba,0xa8,0xa9,0x2,0x71,0x39,0xa3,0x7a,0x15,0x25,0xac,0xf1,0x4d,0x5e,0xaf,0xec,0x1a,0x11,0xf9,0xe1,0xd9,0xc5,0x73,0x10,0x44,0x54,0xfe,0xef,0xbc,0xf7,0x2d,0x34,0x24,0xe,0xcd,0x5f,0x51,0xdb,0xbf,0x31,0xc1,0x2f,0x65,0xf5,0xd,0x7d,0x3b,0x1f,0xcc,0xf4,0xe2,0x97,0xff,0x55,0x89,0xd0,0x7b,0x6f,0x57,0x69,0x72,0x33,0x5d,0xf3,0x64,0xd1,0xd8,0xb3,0x58,0xee,0x20,0x6c,0x80,0xb4,0xa7,0xcb,0x1e,0xc6,0xb2,0x17,0xb1,0x3,0x8b,0xdd,0x5,0x3f,0x2c,0x9c,0x4,0x4e,0x41,0x9e,0x50,0x4b,0xc7,0x76,0xdc,0xb5,0xe8,0xb8,0x38,0x93,0x43,0x75,0xce,0xe6,0x67,0x66,0x5b,0x3e,0xda,0x4f,0x1d,0x59,0xd7,0xad,0x82,0xbe,0x8f,0x7f,0xde,0x13,0xca,0xbd,0xc0,0x48,0xd4,0xa5,0x88,0xfc,0xe4,0x92,0x49,0x9,0x19,0x5c,0x83,0x7,0x95,0x12,0x42,0x61,0xc8,0x30,0xfa,0x2a,0x0,0xd6,0xf8,0xc9,0xa4,0xe3,0x1c,0xb0,0x36,0xd3,0x4c,0x84,0x46,0x35,0x60,0x3a,0x9a,0x1,0x74,0xb6,0x98,0x6e,0xae,0x21,0xf0,0xb9,0xbb,0x18,0xf6,0x45,0x29,0x8e,0xaf,0x9,0xe6,0xc9,0xa0,0xbe,0x5f,0x3b,0x96,0x8,0xd1,0x73,0xd3,0x5e,0x67,0xdf,0xfb,0x3c,0xb3,0x95,0x2f,0xb9,0x44,0xb5,0xd4,0x3f,0xc6,0xeb,0xb8,0x4c,0x16,0xf0,0x48,0xf7,0x94,0xcf,0xa5,0x38,0xab,0xad,0x3,0x3e,0xdd,0x22,0x66,0x4a,0x6e,0x1c,0x1f,0xa4,0x7e,0xe1,0x1a,0x62,0xb2,0x6,0xd9,0xde,0x13,0xa9,0x56,0x74,0x43,0x59,0x9d,0x36,0x45,0xd,0x97,0x4e,0x21,0x11,0x98,0xc5,0x79,0x6a,0x9b,0xd8,0x2e,0x25,0x12,0x83,0xb1,0x4d,0x9f,0x92,0xa2,0x57,0x9e,0x20,0x32,0x5c,0xf6,0x17,0x8e,0x9c,0xf9,0x6b,0x65,0xef,0x8b,0x5,0xf5,0x1b,0x51,0xc1,0x39,0x49,0xf,0x2b,0xf8,0xc0,0xcd,0xd5,0xed,0xf1,0x47,0x24,0x70,0x60,0xca,0xdb,0x88,0xc3,0x19,0x0,0x10,0x3a,0xec,0x87,0x6c,0xda,0x14,0x58,0xb4,0x80,0x93,0xff,0x2a,0xf2,0x86,0x23,0x85,0x37,0xd6,0xa3,0xcb,0x61,0xbd,0xe4,0x4f,0x5b,0x63,0x5d,0x46,0x7,0x69,0xc7,0x50,0xe5,0xdc,0x8c,0xc,0xa7,0x77,0x41,0xfa,0xd2,0x53,0x52,0x6f,0xa,0xee,0x7b,0x29,0x6d,0xbf,0xe9,0x31,0xb,0x18,0xa8,0x30,0x7a,0x75,0xaa,0x64,0x7f,0xf3,0x42,0xe8,0x81,0xd0,0xa6,0x7d,0x3d,0x2d,0x68,0xb7,0x33,0xa1,0x26,0x76,0x55,0xfc,0x4,0xce,0x1e,0xe3,0x99,0xb6,0x8a,0xbb,0x4b,0xea,0x27,0xfe,0x89,0xf4,0x7c,0xe0,0x91,0xbc,0xc8,0xae,0x35,0x40,0x82,0xac,0x5a,0x9a,0x15,0xc4,0x8d,0x8f,0x2c,0xc2,0x71,0x1d,0xba,0x34,0xe2,0xcc,0xfd,0x90,0xd7,0x28,0x84,0x2,0xe7,0x78,0xb0,0x72,0x1,0x54,0xe,0xe5,0xc8,0x31,0xda,0xfe,0x18,0x42,0xb6,0x9b,0xbd,0x32,0xf5,0xbb,0x4a,0xb7,0x21,0x7d,0xdf,0x6,0x98,0xd1,0x69,0x50,0xdd,0xc7,0xe8,0x7,0xa1,0x35,0x51,0xb0,0xae,0xa7,0x1d,0xd0,0xd7,0x57,0x4d,0x7a,0x58,0xef,0x70,0xaa,0x11,0x8,0xbc,0x6c,0x14,0x2c,0xd3,0x30,0xd,0x12,0x60,0x44,0x68,0xc1,0x9a,0xf9,0x46,0xa3,0xa5,0x36,0xab,0x52,0x3c,0x2e,0x90,0x92,0x80,0x19,0xf8,0x43,0xbf,0x8d,0x1c,0x59,0xac,0x9c,0x91,0x64,0x77,0xcb,0x96,0x2b,0x20,0xd6,0x95,0x3,0x4b,0x38,0x93,0x1f,0x2f,0x40,0x99,0xcd,0x86,0xd5,0xc4,0x34,0x1e,0xe,0x17,0xff,0xe3,0xdb,0xc3,0x6e,0x7e,0x2a,0x49,0x47,0x37,0xcf,0x5f,0xce,0xf6,0x25,0x1,0xe1,0x6b,0x65,0xf7,0x15,0xfb,0xb,0x85,0x9,0x48,0x53,0x6d,0xeb,0x5e,0xc9,0x67,0x6f,0xc5,0xad,0xd8,0x55,0x41,0xea,0xb3,0xfc,0x24,0xf1,0x9d,0x39,0x8b,0x2d,0x88,0xd4,0x62,0x89,0xe2,0x8e,0xba,0x56,0x1a,0x71,0x6a,0xa4,0x7b,0x8f,0xe6,0x4c,0xfd,0x5,0x3f,0xe7,0xb1,0x74,0x3e,0xa6,0x16,0x4,0x61,0x5c,0x5d,0x63,0x27,0x75,0xe0,0xa9,0x2,0x82,0xd2,0xdc,0xf4,0x4f,0x79,0x72,0xfa,0x87,0xf0,0xc6,0xb2,0x9f,0xee,0x84,0xb8,0x97,0xed,0x29,0xe4,0x45,0xb5,0x5b,0x78,0x28,0xaf,0x10,0xc0,0xa,0xf2,0x33,0x73,0xa8,0xde,0x3d,0xb9,0x66,0x23,0xbe,0x76,0xe9,0xc,0x0,0x5a,0xf,0x7c,0xf3,0xc2,0xec,0x3a,0x8a,0x26,0xd9,0x9e,0x22,0x81,0x83,0xca,0xb4,0x13,0x7f,0xcc,0x8c,0x4e,0x3b,0xa0,0x1b,0x94,0x54,0xa2,0x8d,0x8b,0x83,0x1e,0xb2,0xe9,0x6e,0xd1,0x48,0x3a,0x40,0x6c,0xfb,0x4,0x25,0x18,0x94,0x20,0x3c,0x44,0x58,0xc7,0x39,0x82,0x65,0x7f,0x70,0x52,0x35,0x8f,0xff,0xf8,0x79,0x1d,0x86,0x98,0xc0,0xef,0x89,0x2f,0x41,0xf9,0xf5,0x78,0xf7,0x55,0xb0,0x2e,0x62,0x93,0x9,0x9f,0x95,0xb3,0xdd,0x1a,0x30,0xd6,0x9e,0x6a,0xe0,0xcd,0xf2,0x19,0xd3,0x3d,0xad,0x23,0x43,0xc9,0xdf,0x4d,0xde,0xe6,0x29,0xd,0x1f,0x6f,0x77,0xe7,0x56,0x46,0x61,0x2,0xcb,0xd7,0xeb,0xf3,0x36,0x1c,0x3f,0x26,0xae,0xe5,0xec,0xfd,0x7,0x37,0xb1,0x68,0x63,0x2b,0xbb,0x10,0x8,0x3,0xbd,0xfe,0x5f,0x4c,0xbe,0xe3,0x84,0x71,0xb9,0xb4,0x97,0x6b,0x34,0xa5,0xa8,0xba,0xd0,0x31,0x14,0x7a,0xb8,0x6,0xdc,0xf4,0x51,0x67,0x2a,0x81,0xfa,0xaa,0xf,0x4b,0xc8,0x5d,0x49,0x2c,0x75,0x74,0x16,0x5c,0x3e,0x8e,0x17,0x2d,0x99,0xcf,0xce,0xa7,0xd5,0x64,0x42,0x59,0x53,0x8c,0x92,0xa6,0x32,0x7e,0x4a,0xfc,0xca,0xa1,0xa3,0x11,0xa0,0x5,0xc,0xd4,0xb5,0xd9,0x69,0x7d,0x9b,0xc2,0xed,0x47,0xf0,0x85,0x76,0xc3,0x4f,0xe1,0x60,0x21,0x45,0x7b,0xbc,0x33,0x8a,0x7c,0x66,0xa4,0x88,0x13,0x3b,0x9c,0xe4,0x57,0xa9,0xa,0xe2,0xab,0xe,0xa2,0xb6,0xf1,0xea,0xdb,0x12,0xc4,0x72,0x28,0x54,0x27,0x5e,0x96,0x24,0xc1,0x91,0x15,0xb,0x4e,0x5b,0x1b,0xf6,0x80,0xe8,0x38,0xda,0x22,0x50,0x73,0x87,0x0,0xcc,0x1,0x9d,0x6d,0x90,0xac,0xc5,0xbf,0x9a,0xee,0xc6,0xb7,0xd2,0x5a,0xd8,0xaf,0x84,0x43,0xcc,0xea,0x50,0xc6,0x3b,0xca,0xab,0x40,0xb9,0x94,0xc7,0x33,0x69,0x8f,0xd0,0x76,0x99,0xb6,0xdf,0xc1,0x20,0x44,0xe9,0x77,0xae,0xc,0xac,0x21,0x18,0xa0,0x60,0xdb,0x1,0x9e,0x65,0x1d,0xcd,0x79,0xa6,0xa1,0x6c,0xd6,0x29,0xb,0x3c,0x26,0x37,0x88,0xeb,0xb0,0xda,0x47,0xd4,0xd2,0x7c,0x41,0xa2,0x5d,0x19,0x35,0x11,0x63,0x6d,0xfc,0xce,0x32,0xe0,0xed,0xdd,0x28,0xe1,0x5f,0x4d,0x23,0x89,0x68,0xf1,0xe3,0xe2,0x49,0x3a,0x72,0xe8,0x31,0x5e,0x6e,0xe7,0xba,0x6,0x15,0xe4,0xa7,0x51,0x5a,0xb2,0xaa,0x92,0x8e,0x38,0x5b,0xf,0x1f,0xb5,0xa4,0xf7,0xbc,0x66,0x7f,0x6f,0x45,0x86,0x14,0x1a,0x90,0xf4,0x7a,0x8a,0x64,0x2e,0xbe,0x46,0x36,0x70,0x54,0x87,0xbf,0xa9,0xdc,0xb4,0x1e,0xc2,0x9b,0x30,0x24,0x1c,0x22,0x39,0x78,0x16,0xb8,0x2f,0x9a,0x93,0xf8,0x13,0xa5,0x6b,0x27,0xcb,0xff,0xec,0x80,0x55,0x8d,0xf9,0x5c,0xfa,0x48,0xc0,0x96,0x4e,0x74,0x67,0xd7,0x4f,0x5,0xa,0xd5,0x1b,0x0,0x8c,0x3d,0x97,0xfe,0xa3,0xf3,0x73,0xd8,0x8,0x3e,0x85,0xad,0x2c,0x2d,0x10,0x75,0x91,0x4,0x56,0x12,0x9c,0xe6,0xc9,0xf5,0xc4,0x34,0x95,0x58,0x81,0xf6,0x8b,0x3,0x9f,0xee,0xc3,0xb7,0xaf,0xd9,0x2,0x42,0x52,0x17,0xc8,0x4c,0xde,0x59,0x9,0x2a,0x83,0x7b,0xb1,0x61,0x4b,0x9d,0xb3,0x82,0xef,0xa8,0x57,0xfb,0x7d,0x98,0x7,0xcf,0xd,0x7e,0x2b,0x71,0xd1,0x4a,0x3f,0xfd,0xd3,0x25,0xe5,0x6a,0xbb,0xf2,0xf0,0x53,0xbd,0xe,0x62,0xc5,0xda,0x7c,0x93,0xbc,0xd5,0xcb,0x2a,0x4e,0xe3,0x7d,0xa4,0x6,0xa6,0x2b,0x12,0xaa,0x8e,0x49,0xc6,0xe0,0x5a,0xcc,0x31,0xc0,0xa1,0x4a,0xb3,0x9e,0xcd,0x39,0x63,0x85,0x3d,0x82,0xe1,0xba,0xd0,0x4d,0xde,0xd8,0x76,0x4b,0xa8,0x57,0x13,0x3f,0x1b,0x69,0x6a,0xd1,0xb,0x94,0x6f,0x17,0xc7,0x73,0xac,0xab,0x66,0xdc,0x23,0x1,0x36,0x2c,0xe8,0x43,0x30,0x78,0xe2,0x3b,0x54,0x64,0xed,0xb0,0xc,0x1f,0xee,0xad,0x5b,0x50,0x67,0xf6,0xc4,0x38,0xea,0xe7,0xd7,0x22,0xeb,0x55,0x47,0x29,0x83,0x62,0xfb,0xe9,0x8c,0x1e,0x10,0x9a,0xfe,0x70,0x80,0x6e,0x24,0xb4,0x4c,0x3c,0x7a,0x5e,0x8d,0xb5,0xb8,0xa0,0x98,0x84,0x32,0x51,0x5,0x15,0xbf,0xae,0xfd,0xb6,0x6c,0x75,0x65,0x4f,0x99,0xf2,0x19,0xaf,0x61,0x2d,0xc1,0xf5,0xe6,0x8a,0x5f,0x87,0xf3,0x56,0xf0,0x42,0xa3,0xd6,0xbe,0x14,0xc8,0x91,0x3a,0x2e,0x16,0x28,0x33,0x72,0x1c,0xb2,0x25,0x90,0xa9,0xf9,0x79,0xd2,0x2,0x34,0x8f,0xa7,0x26,0x27,0x1a,0x7f,0x9b,0xe,0x5c,0x18,0xca,0x9c,0x44,0x7e,0x6d,0xdd,0x45,0xf,0x0,0xdf,0x11,0xa,0x86,0x37,0x9d,0xf4,0xa5,0xd3,0x8,0x48,0x58,0x1d,0xc2,0x46,0xd4,0x53,0x3,0x20,0x89,0x71,0xbb,0x6b,0x96,0xec,0xc3,0xff,0xce,0x3e,0x9f,0x52,0x8b,0xfc,0x81,0x9,0x95,0xe4,0xc9,0xbd,0xdb,0x40,0x35,0xf7,0xd9,0x2f,0xef,0x60,0xb1,0xf8,0xfa,0x59,0xb7,0x4,0x68,0xcf,0x41,0x97,0xb9,0x88,0xe5,0xa2,0x5d,0xf1,0x77,0x92,0xd,0xc5,0x7,0x74,0x21,0x7b,0x3f,0x12,0xeb,0x0,0x24,0xc2,0x98,0x6c,0x41,0x67,0xe8,0x2f,0x61,0x90,0x6d,0xfb,0xa7,0x5,0xdc,0x42,0xb,0xb3,0x8a,0x7,0x1d,0x32,0xdd,0x7b,0xef,0x8b,0x6a,0x74,0x7d,0xc7,0xa,0xd,0x8d,0x97,0xa0,0x82,0x35,0xaa,0x70,0xcb,0xd2,0x66,0xb6,0xce,0xf6,0x9,0xea,0xd7,0xc8,0xba,0x9e,0xb2,0x1b,0x40,0x23,0x9c,0x79,0x7f,0xec,0x71,0x88,0xe6,0xf4,0x4a,0x48,0x5a,0xc3,0x22,0x99,0x65,0x57,0xc6,0x83,0x76,0x46,0x4b,0xbe,0xad,0x11,0x4c,0xf1,0xfa,0xc,0x4f,0xd9,0x91,0xe2,0x49,0xc5,0xf5,0x9a,0x43,0x17,0x5c,0xf,0x1e,0xee,0xc4,0xd4,0xcd,0x25,0x39,0x1,0x19,0xb4,0xa4,0xf0,0x93,0x9d,0xed,0x15,0x85,0x14,0x2c,0xff,0xdb,0x3b,0xb1,0xbf,0x2d,0xcf,0x21,0xd1,0x5f,0xd3,0x92,0x89,0xb7,0x31,0x84,0x13,0xbd,0xb5,0x1f,0x77,0x2,0x8f,0x9b,0x30,0x69,0x26,0xfe,0x2b,0x47,0xe3,0x51,0xf7,0x52,0xe,0xb8,0x53,0x38,0x54,0x60,0x8c,0xc0,0xab,0xb0,0x7e,0xa1,0x55,0x3c,0x96,0x27,0xdf,0xe5,0x3d,0x6b,0xae,0xe4,0x7c,0xcc,0xde,0xbb,0x86,0x87,0xb9,0xfd,0xaf,0x3a,0x73,0xd8,0x58,0x8,0x6,0x2e,0x95,0xa3,0xa8,0x20,0x5d,0x2a,0x1c,0x68,0x45,0x34,0x5e,0x62,0x4d,0x37,0xf3,0x3e,0x9f,0x6f,0x81,0xa2,0xf2,0x75,0xca,0x1a,0xd0,0x28,0xe9,0xa9,0x72,0x4,0xe7,0x63,0xbc,0xf9,0x64,0xac,0x33,0xd6,0xda,0x80,0xd5,0xa6,0x29,0x18,0x36,0xe0,0x50,0xfc,0x3,0x44,0xf8,0x5b,0x59,0x10,0x6e,0xc9,0xa5,0x16,0x56,0x94,0xe1,0x7a,0xc1,0x4e,0x8e,0x78,0xe6,0xe0,0xe8,0x75,0xd9,0x82,0x5,0xba,0x23,0x51,0x2b,0x7,0x90,0x6f,0x4e,0x73,0xff,0x4b,0x57,0x2f,0x33,0xac,0x52,0xe9,0xe,0x14,0x1b,0x39,0x5e,0xe4,0x94,0x93,0x12,0x76,0xed,0xf3,0xab,0x84,0xe2,0x44,0x2a,0x92,0x9e,0x13,0x9c,0x3e,0xdb,0x45,0x9,0xf8,0x62,0xf4,0xfe,0xd8,0xb6,0x71,0x5b,0xbd,0xf5,0x1,0x8b,0xa6,0x99,0x72,0xb8,0x56,0xc6,0x48,0x28,0xa2,0xb4,0x26,0xb5,0x8d,0x42,0x66,0x74,0x4,0x1c,0x8c,0x3d,0x2d,0xa,0x69,0xa0,0xbc,0x80,0x98,0x5d,0x77,0x54,0x4d,0xc5,0x8e,0x87,0x96,0x6c,0x5c,0xda,0x3,0x8,0x40,0xd0,0x7b,0x63,0x68,0xd6,0x95,0x34,0x27,0xd5,0x88,0xef,0x1a,0xd2,0xdf,0xfc,0x0,0x5f,0xce,0xc3,0xd1,0xbb,0x5a,0x7f,0x11,0xd3,0x6d,0xb7,0x9f,0x3a,0xc,0x41,0xea,0x91,0xc1,0x64,0x20,0xa3,0x36,0x22,0x47,0x1e,0x1f,0x7d,0x37,0x55,0xe5,0x7c,0x46,0xf2,0xa4,0xa5,0xcc,0xbe,0xf,0x29,0x32,0x38,0xe7,0xf9,0xcd,0x59,0x15,0x21,0x97,0xa1,0xca,0xc8,0x7a,0xcb,0x6e,0x67,0xbf,0xde,0xb2,0x2,0x16,0xf0,0xa9,0x86,0x2c,0x9b,0xee,0x1d,0xa8,0x24,0x8a,0xb,0x4a,0x2e,0x10,0xd7,0x58,0xe1,0x17,0xd,0xcf,0xe3,0x78,0x50,0xf7,0x8f,0x3c,0xc2,0x61,0x89,0xc0,0x65,0xc9,0xdd,0x9a,0x81,0xb0,0x79,0xaf,0x19,0x43,0x3f,0x4c,0x35,0xfd,0x4f,0xaa,0xfa,0x7e,0x60,0x25,0x30,0x70,0x9d,0xeb,0x83,0x53,0xb1,0x49,0x3b,0x18,0xec,0x6b,0xa7,0x6a,0xf6,0x6,0xfb,0xc7,0xae,0xd4,0xf1,0x85,0xad,0xdc,0xb9,0x31,0xb3,0xc4,0x41,0x86,0x9,0x2f,0x95,0x3,0xfe,0xf,0x6e,0x85,0x7c,0x51,0x2,0xf6,0xac,0x4a,0x15,0xb3,0x5c,0x73,0x1a,0x4,0xe5,0x81,0x2c,0xb2,0x6b,0xc9,0x69,0xe4,0xdd,0x65,0xa5,0x1e,0xc4,0x5b,0xa0,0xd8,0x8,0xbc,0x63,0x64,0xa9,0x13,0xec,0xce,0xf9,0xe3,0xf2,0x4d,0x2e,0x75,0x1f,0x82,0x11,0x17,0xb9,0x84,0x67,0x98,0xdc,0xf0,0xd4,0xa6,0xa8,0x39,0xb,0xf7,0x25,0x28,0x18,0xed,0x24,0x9a,0x88,0xe6,0x4c,0xad,0x34,0x26,0x27,0x8c,0xff,0xb7,0x2d,0xf4,0x9b,0xab,0x22,0x7f,0xc3,0xd0,0x21,0x62,0x94,0x9f,0x77,0x6f,0x57,0x4b,0xfd,0x9e,0xca,0xda,0x70,0x61,0x32,0x79,0xa3,0xba,0xaa,0x80,0x43,0xd1,0xdf,0x55,0x31,0xbf,0x4f,0xa1,0xeb,0x7b,0x83,0xf3,0xb5,0x91,0x42,0x7a,0x6c,0x19,0x71,0xdb,0x7,0x5e,0xf5,0xe1,0xd9,0xe7,0xfc,0xbd,0xd3,0x7d,0xea,0x5f,0x56,0x3d,0xd6,0x60,0xae,0xe2,0xe,0x3a,0x29,0x45,0x90,0x48,0x3c,0x99,0x3f,0x8d,0x5,0x53,0x8b,0xb1,0xa2,0x12,0x8a,0xc0,0xcf,0x10,0xde,0xc5,0x49,0xf8,0x52,0x3b,0x66,0x36,0xb6,0x1d,0xcd,0xfb,0x40,0x68,0xe9,0xe8,0xd5,0xb0,0x54,0xc1,0x93,0xd7,0x59,0x23,0xc,0x30,0x1,0xf1,0x50,0x9d,0x44,0x33,0x4e,0xc6,0x5a,0x2b,0x6,0x72,0x6a,0x1c,0xc7,0x87,0x97,0xd2,0xd,0x89,0x1b,0x9c,0xcc,0xef,0x46,0xbe,0x74,0xa4,0x8e,0x58,0x76,0x47,0x2a,0x6d,0x92,0x3e,0xb8,0x5d,0xc2,0xa,0xc8,0xbb,0xee,0xb4,0x14,0x8f,0xfa,0x38,0x16,0xe0,0x20,0xaf,0x7e,0x37,0x35,0x96,0x78,0xcb,0xa7,0x0,0x6d,0xcb,0x24,0xb,0x62,0x7c,0x9d,0xf9,0x54,0xca,0x13,0xb1,0x11,0x9c,0xa5,0x1d,0x39,0xfe,0x71,0x57,0xed,0x7b,0x86,0x77,0x16,0xfd,0x4,0x29,0x7a,0x8e,0xd4,0x32,0x8a,0x35,0x56,0xd,0x67,0xfa,0x69,0x6f,0xc1,0xfc,0x1f,0xe0,0xa4,0x88,0xac,0xde,0xdd,0x66,0xbc,0x23,0xd8,0xa0,0x70,0xc4,0x1b,0x1c,0xd1,0x6b,0x94,0xb6,0x81,0x9b,0x5f,0xf4,0x87,0xcf,0x55,0x8c,0xe3,0xd3,0x5a,0x7,0xbb,0xa8,0x59,0x1a,0xec,0xe7,0xd0,0x41,0x73,0x8f,0x5d,0x50,0x60,0x95,0x5c,0xe2,0xf0,0x9e,0x34,0xd5,0x4c,0x5e,0x3b,0xa9,0xa7,0x2d,0x49,0xc7,0x37,0xd9,0x93,0x3,0xfb,0x8b,0xcd,0xe9,0x3a,0x2,0xf,0x17,0x2f,0x33,0x85,0xe6,0xb2,0xa2,0x8,0x19,0x4a,0x1,0xdb,0xc2,0xd2,0xf8,0x2e,0x45,0xae,0x18,0xd6,0x9a,0x76,0x42,0x51,0x3d,0xe8,0x30,0x44,0xe1,0x47,0xf5,0x14,0x61,0x9,0xa3,0x7f,0x26,0x8d,0x99,0xa1,0x9f,0x84,0xc5,0xab,0x5,0x92,0x27,0x1e,0x4e,0xce,0x65,0xb5,0x83,0x38,0x10,0x91,0x90,0xad,0xc8,0x2c,0xb9,0xeb,0xaf,0x7d,0x2b,0xf3,0xc9,0xda,0x6a,0xf2,0xb8,0xb7,0x68,0xa6,0xbd,0x31,0x80,0x2a,0x43,0x12,0x64,0xbf,0xff,0xef,0xaa,0x75,0xf1,0x63,0xe4,0xb4,0x97,0x3e,0xc6,0xc,0xdc,0x21,0x5b,0x74,0x48,0x79,0x89,0x28,0xe5,0x3c,0x4b,0x36,0xbe,0x22,0x53,0x7e,0xa,0x6c,0xf7,0x82,0x40,0x6e,0x98,0x58,0xd7,0x6,0x4f,0x4d,0xee,0x0,0xb3,0xdf,0x78,0xf6,0x20,0xe,0x3f,0x52,0x15,0xea,0x46,0xc0,0x25,0xba,0x72,0xb0,0xc3,0x96,0xcc,0xb8,0x95,0x6c,0x87,0xa3,0x45,0x1f,0xeb,0xc6,0xe0,0x6f,0xa8,0xe6,0x17,0xea,0x7c,0x20,0x82,0x5b,0xc5,0x8c,0x34,0xd,0x80,0x9a,0xb5,0x5a,0xfc,0x68,0xc,0xed,0xf3,0xfa,0x40,0x8d,0x8a,0xa,0x10,0x27,0x5,0xb2,0x2d,0xf7,0x4c,0x55,0xe1,0x31,0x49,0x71,0x8e,0x6d,0x50,0x4f,0x3d,0x19,0x35,0x9c,0xc7,0xa4,0x1b,0xfe,0xf8,0x6b,0xf6,0xf,0x61,0x73,0xcd,0xcf,0xdd,0x44,0xa5,0x1e,0xe2,0xd0,0x41,0x4,0xf1,0xc1,0xcc,0x39,0x2a,0x96,0xcb,0x76,0x7d,0x8b,0xc8,0x5e,0x16,0x65,0xce,0x42,0x72,0x1d,0xc4,0x90,0xdb,0x88,0x99,0x69,0x43,0x53,0x4a,0xa2,0xbe,0x86,0x9e,0x33,0x23,0x77,0x14,0x1a,0x6a,0x92,0x2,0x93,0xab,0x78,0x5c,0xbc,0x36,0x38,0xaa,0x48,0xa6,0x56,0xd8,0x54,0x15,0xe,0x30,0xb6,0x3,0x94,0x3a,0x32,0x98,0xf0,0x85,0x8,0x1c,0xb7,0xee,0xa1,0x79,0xac,0xc0,0x64,0xd6,0x70,0xd5,0x89,0x3f,0xd4,0xbf,0xd3,0xe7,0xb,0x47,0x2c,0x37,0xf9,0x26,0xd2,0xbb,0x11,0xa0,0x58,0x62,0xba,0xec,0x29,0x63,0xfb,0x4b,0x59,0x3c,0x1,0x0,0x3e,0x7a,0x28,0xbd,0xf4,0x5f,0xdf,0x8f,0x81,0xa9,0x12,0x24,0x2f,0xa7,0xda,0xad,0x9b,0xef,0xc2,0xb3,0xd9,0xe5,0xca,0xb0,0x74,0xb9,0x18,0xe8,0x6,0x25,0x75,0xf2,0x4d,0x9d,0x57,0xaf,0x6e,0x2e,0xf5,0x83,0x60,0xe4,0x3b,0x7e,0xe3,0x2b,0xb4,0x51,0x5d,0x7,0x52,0x21,0xae,0x9f,0xb1,0x67,0xd7,0x7b,0x84,0xc3,0x7f,0xdc,0xde,0x97,0xe9,0x4e,0x22,0x91,0xd1,0x13,0x66,0xfd,0x46,0xc9,0x9,0xff,0x76,0x70,0x78,0xe5,0x49,0x12,0x95,0x2a,0xb3,0xc1,0xbb,0x97,0x0,0xff,0xde,0xe3,0x6f,0xdb,0xc7,0xbf,0xa3,0x3c,0xc2,0x79,0x9e,0x84,0x8b,0xa9,0xce,0x74,0x4,0x3,0x82,0xe6,0x7d,0x63,0x3b,0x14,0x72,0xd4,0xba,0x2,0xe,0x83,0xc,0xae,0x4b,0xd5,0x99,0x68,0xf2,0x64,0x6e,0x48,0x26,0xe1,0xcb,0x2d,0x65,0x91,0x1b,0x36,0x9,0xe2,0x28,0xc6,0x56,0xd8,0xb8,0x32,0x24,0xb6,0x25,0x1d,0xd2,0xf6,0xe4,0x94,0x8c,0x1c,0xad,0xbd,0x9a,0xf9,0x30,0x2c,0x10,0x8,0xcd,0xe7,0xc4,0xdd,0x55,0x1e,0x17,0x6,0xfc,0xcc,0x4a,0x93,0x98,0xd0,0x40,0xeb,0xf3,0xf8,0x46,0x5,0xa4,0xb7,0x45,0x18,0x7f,0x8a,0x42,0x4f,0x6c,0x90,0xcf,0x5e,0x53,0x41,0x2b,0xca,0xef,0x81,0x43,0xfd,0x27,0xf,0xaa,0x9c,0xd1,0x7a,0x1,0x51,0xf4,0xb0,0x33,0xa6,0xb2,0xd7,0x8e,0x8f,0xed,0xa7,0xc5,0x75,0xec,0xd6,0x62,0x34,0x35,0x5c,0x2e,0x9f,0xb9,0xa2,0xa8,0x77,0x69,0x5d,0xc9,0x85,0xb1,0x7,0x31,0x5a,0x58,0xea,0x5b,0xfe,0xf7,0x2f,0x4e,0x22,0x92,0x86,0x60,0x39,0x16,0xbc,0xb,0x7e,0x8d,0x38,0xb4,0x1a,0x9b,0xda,0xbe,0x80,0x47,0xc8,0x71,0x87,0x9d,0x5f,0x73,0xe8,0xc0,0x67,0x1f,0xac,0x52,0xf1,0x19,0x50,0xf5,0x59,0x4d,0xa,0x11,0x20,0xe9,0x3f,0x89,0xd3,0xaf,0xdc,0xa5,0x6d,0xdf,0x3a,0x6a,0xee,0xf0,0xb5,0xa0,0xe0,0xd,0x7b,0x13,0xc3,0x21,0xd9,0xab,0x88,0x7c,0xfb,0x37,0xfa,0x66,0x96,0x6b,0x57,0x3e,0x44,0x61,0x15,0x3d,0x4c,0x29,0xa1,0x23,0x54,0xc8,0xf,0x80,0xa6,0x1c,0x8a,0x77,0x86,0xe7,0xc,0xf5,0xd8,0x8b,0x7f,0x25,0xc3,0x9c,0x3a,0xd5,0xfa,0x93,0x8d,0x6c,0x8,0xa5,0x3b,0xe2,0x40,0xe0,0x6d,0x54,0xec,0x2c,0x97,0x4d,0xd2,0x29,0x51,0x81,0x35,0xea,0xed,0x20,0x9a,0x65,0x47,0x70,0x6a,0x7b,0xc4,0xa7,0xfc,0x96,0xb,0x98,0x9e,0x30,0xd,0xee,0x11,0x55,0x79,0x5d,0x2f,0x21,0xb0,0x82,0x7e,0xac,0xa1,0x91,0x64,0xad,0x13,0x1,0x6f,0xc5,0x24,0xbd,0xaf,0xae,0x5,0x76,0x3e,0xa4,0x7d,0x12,0x22,0xab,0xf6,0x4a,0x59,0xa8,0xeb,0x1d,0x16,0xfe,0xe6,0xde,0xc2,0x74,0x17,0x43,0x53,0xf9,0xe8,0xbb,0xf0,0x2a,0x33,0x23,0x9,0xca,0x58,0x56,0xdc,0xb8,0x36,0xc6,0x28,0x62,0xf2,0xa,0x7a,0x3c,0x18,0xcb,0xf3,0xe5,0x90,0xf8,0x52,0x8e,0xd7,0x7c,0x68,0x50,0x6e,0x75,0x34,0x5a,0xf4,0x63,0xd6,0xdf,0xb4,0x5f,0xe9,0x27,0x6b,0x87,0xb3,0xa0,0xcc,0x19,0xc1,0xb5,0x10,0xb6,0x4,0x8c,0xda,0x2,0x38,0x2b,0x9b,0x3,0x49,0x46,0x99,0x57,0x4c,0xc0,0x71,0xdb,0xb2,0xef,0xbf,0x3f,0x94,0x44,0x72,0xc9,0xe1,0x60,0x61,0x5c,0x39,0xdd,0x48,0x1a,0x5e,0xd0,0xaa,0x85,0xb9,0x88,0x78,0xd9,0x14,0xcd,0xba,0xc7,0x4f,0xd3,0xa2,0x8f,0xfb,0xe3,0x95,0x4e,0xe,0x1e,0x5b,0x84,0x0,0x92,0x15,0x45,0x66,0xcf,0x37,0xfd,0x2d,0x7,0xd1,0xff,0xce,0xa3,0xe4,0x1b,0xb7,0x31,0xd4,0x4b,0x83,0x41,0x32,0x67,0x3d,0x9d,0x6,0x73,0xb1,0x9f,0x69,0xa9,0x26,0xf7,0xbe,0xbc,0x1f,0xf1,0x42,0x2e,0x89,0xa3,0x5,0xea,0xc5,0xac,0xb2,0x53,0x37,0x9a,0x4,0xdd,0x7f,0xdf,0x52,0x6b,0xd3,0xf7,0x30,0xbf,0x99,0x23,0xb5,0x48,0xb9,0xd8,0x33,0xca,0xe7,0xb4,0x40,0x1a,0xfc,0x44,0xfb,0x98,0xc3,0xa9,0x34,0xa7,0xa1,0xf,0x32,0xd1,0x2e,0x6a,0x46,0x62,0x10,0x13,0xa8,0x72,0xed,0x16,0x6e,0xbe,0xa,0xd5,0xd2,0x1f,0xa5,0x5a,0x78,0x4f,0x55,0x91,0x3a,0x49,0x1,0x9b,0x42,0x2d,0x1d,0x94,0xc9,0x75,0x66,0x97,0xd4,0x22,0x29,0x1e,0x8f,0xbd,0x41,0x93,0x9e,0xae,0x5b,0x92,0x2c,0x3e,0x50,0xfa,0x1b,0x82,0x90,0xf5,0x67,0x69,0xe3,0x87,0x9,0xf9,0x17,0x5d,0xcd,0x35,0x45,0x3,0x27,0xf4,0xcc,0xc1,0xd9,0xe1,0xfd,0x4b,0x28,0x7c,0x6c,0xc6,0xd7,0x84,0xcf,0x15,0xc,0x1c,0x36,0xe0,0x8b,0x60,0xd6,0x18,0x54,0xb8,0x8c,0x9f,0xf3,0x26,0xfe,0x8a,0x2f,0x89,0x3b,0xda,0xaf,0xc7,0x6d,0xb1,0xe8,0x43,0x57,0x6f,0x51,0x4a,0xb,0x65,0xcb,0x5c,0xe9,0xd0,0x80,0x0,0xab,0x7b,0x4d,0xf6,0xde,0x5f,0x5e,0x63,0x6,0xe2,0x77,0x25,0x61,0xb3,0xe5,0x3d,0x7,0x14,0xa4,0x3c,0x76,0x79,0xa6,0x68,0x73,0xff,0x4e,0xe4,0x8d,0xdc,0xaa,0x71,0x31,0x21,0x64,0xbb,0x3f,0xad,0x2a,0x7a,0x59,0xf0,0x8,0xc2,0x12,0xef,0x95,0xba,0x86,0xb7,0x47,0xe6,0x2b,0xf2,0x85,0xf8,0x70,0xec,0x9d,0xb0,0xc4,0xa2,0x39,0x4c,0x8e,0xa0,0x56,0x96,0x19,0xc8,0x81,0x83,0x20,0xce,0x7d,0x11,0xb6,0x38,0xee,0xc0,0xf1,0x9c,0xdb,0x24,0x88,0xe,0xeb,0x74,0xbc,0x7e,0xd,0x58,0x2,0x9f,0xb2,0x4b,0xa0,0x84,0x62,0x38,0xcc,0xe1,0xc7,0x48,0x8f,0xc1,0x30,0xcd,0x5b,0x7,0xa5,0x7c,0xe2,0xab,0x13,0x2a,0xa7,0xbd,0x92,0x7d,0xdb,0x4f,0x2b,0xca,0xd4,0xdd,0x67,0xaa,0xad,0x2d,0x37,0x0,0x22,0x95,0xa,0xd0,0x6b,0x72,0xc6,0x16,0x6e,0x56,0xa9,0x4a,0x77,0x68,0x1a,0x3e,0x12,0xbb,0xe0,0x83,0x3c,0xd9,0xdf,0x4c,0xd1,0x28,0x46,0x54,0xea,0xe8,0xfa,0x63,0x82,0x39,0xc5,0xf7,0x66,0x23,0xd6,0xe6,0xeb,0x1e,0xd,0xb1,0xec,0x51,0x5a,0xac,0xef,0x79,0x31,0x42,0xe9,0x65,0x55,0x3a,0xe3,0xb7,0xfc,0xaf,0xbe,0x4e,0x64,0x74,0x6d,0x85,0x99,0xa1,0xb9,0x14,0x4,0x50,0x33,0x3d,0x4d,0xb5,0x25,0xb4,0x8c,0x5f,0x7b,0x9b,0x11,0x1f,0x8d,0x6f,0x81,0x71,0xff,0x73,0x32,0x29,0x17,0x91,0x24,0xb3,0x1d,0x15,0xbf,0xd7,0xa2,0x2f,0x3b,0x90,0xc9,0x86,0x5e,0x8b,0xe7,0x43,0xf1,0x57,0xf2,0xae,0x18,0xf3,0x98,0xf4,0xc0,0x2c,0x60,0xb,0x10,0xde,0x1,0xf5,0x9c,0x36,0x87,0x7f,0x45,0x9d,0xcb,0xe,0x44,0xdc,0x6c,0x7e,0x1b,0x26,0x27,0x19,0x5d,0xf,0x9a,0xd3,0x78,0xf8,0xa8,0xa6,0x8e,0x35,0x3,0x8,0x80,0xfd,0x8a,0xbc,0xc8,0xe5,0x94,0xfe,0xc2,0xed,0x97,0x53,0x9e,0x3f,0xcf,0x21,0x2,0x52,0xd5,0x6a,0xba,0x70,0x88,0x49,0x9,0xd2,0xa4,0x47,0xc3,0x1c,0x59,0xc4,0xc,0x93,0x76,0x7a,0x20,0x75,0x6,0x89,0xb8,0x96,0x40,0xf0,0x5c,0xa3,0xe4,0x58,0xfb,0xf9,0xb0,0xce,0x69,0x5,0xb6,0xf6,0x34,0x41,0xda,0x61,0xee,0x2e,0xd8,0xa2,0xa4,0xac,0x31,0x9d,0xc6,0x41,0xfe,0x67,0x15,0x6f,0x43,0xd4,0x2b,0xa,0x37,0xbb,0xf,0x13,0x6b,0x77,0xe8,0x16,0xad,0x4a,0x50,0x5f,0x7d,0x1a,0xa0,0xd0,0xd7,0x56,0x32,0xa9,0xb7,0xef,0xc0,0xa6,0x0,0x6e,0xd6,0xda,0x57,0xd8,0x7a,0x9f,0x1,0x4d,0xbc,0x26,0xb0,0xba,0x9c,0xf2,0x35,0x1f,0xf9,0xb1,0x45,0xcf,0xe2,0xdd,0x36,0xfc,0x12,0x82,0xc,0x6c,0xe6,0xf0,0x62,0xf1,0xc9,0x6,0x22,0x30,0x40,0x58,0xc8,0x79,0x69,0x4e,0x2d,0xe4,0xf8,0xc4,0xdc,0x19,0x33,0x10,0x9,0x81,0xca,0xc3,0xd2,0x28,0x18,0x9e,0x47,0x4c,0x4,0x94,0x3f,0x27,0x2c,0x92,0xd1,0x70,0x63,0x91,0xcc,0xab,0x5e,0x96,0x9b,0xb8,0x44,0x1b,0x8a,0x87,0x95,0xff,0x1e,0x3b,0x55,0x97,0x29,0xf3,0xdb,0x7e,0x48,0x5,0xae,0xd5,0x85,0x20,0x64,0xe7,0x72,0x66,0x3,0x5a,0x5b,0x39,0x73,0x11,0xa1,0x38,0x2,0xb6,0xe0,0xe1,0x88,0xfa,0x4b,0x6d,0x76,0x7c,0xa3,0xbd,0x89,0x1d,0x51,0x65,0xd3,0xe5,0x8e,0x8c,0x3e,0x8f,0x2a,0x23,0xfb,0x9a,0xf6,0x46,0x52,0xb4,0xed,0xc2,0x68,0xdf,0xaa,0x59,0xec,0x60,0xce,0x4f,0xe,0x6a,0x54,0x93,0x1c,0xa5,0x53,0x49,0x8b,0xa7,0x3c,0x14,0xb3,0xcb,0x78,0x86,0x25,0xcd,0x84,0x21,0x8d,0x99,0xde,0xc5,0xf4,0x3d,0xeb,0x5d,0x7,0x7b,0x8,0x71,0xb9,0xb,0xee,0xbe,0x3a,0x24,0x61,0x74,0x34,0xd9,0xaf,0xc7,0x17,0xf5,0xd,0x7f,0x5c,0xa8,0x2f,0xe3,0x2e,0xb2,0x42,0xbf,0x83,0xea,0x90,0xb5,0xc1,0xe9,0x98,0xfd,0x75,0xf7,0x80,0x2d,0xea,0x65,0x43,0xf9,0x6f,0x92,0x63,0x2,0xe9,0x10,0x3d,0x6e,0x9a,0xc0,0x26,0x79,0xdf,0x30,0x1f,0x76,0x68,0x89,0xed,0x40,0xde,0x7,0xa5,0x5,0x88,0xb1,0x9,0xc9,0x72,0xa8,0x37,0xcc,0xb4,0x64,0xd0,0xf,0x8,0xc5,0x7f,0x80,0xa2,0x95,0x8f,0x9e,0x21,0x42,0x19,0x73,0xee,0x7d,0x7b,0xd5,0xe8,0xb,0xf4,0xb0,0x9c,0xb8,0xca,0xc4,0x55,0x67,0x9b,0x49,0x44,0x74,0x81,0x48,0xf6,0xe4,0x8a,0x20,0xc1,0x58,0x4a,0x4b,0xe0,0x93,0xdb,0x41,0x98,0xf7,0xc7,0x4e,0x13,0xaf,0xbc,0x4d,0xe,0xf8,0xf3,0x1b,0x3,0x3b,0x27,0x91,0xf2,0xa6,0xb6,0x1c,0xd,0x5e,0x15,0xcf,0xd6,0xc6,0xec,0x2f,0xbd,0xb3,0x39,0x5d,0xd3,0x23,0xcd,0x87,0x17,0xef,0x9f,0xd9,0xfd,0x2e,0x16,0x0,0x75,0x1d,0xb7,0x6b,0x32,0x99,0x8d,0xb5,0x8b,0x90,0xd1,0xbf,0x11,0x86,0x33,0x3a,0x51,0xba,0xc,0xc2,0x8e,0x62,0x56,0x45,0x29,0xfc,0x24,0x50,0xf5,0x53,0xe1,0x69,0x3f,0xe7,0xdd,0xce,0x7e,0xe6,0xac,0xa3,0x7c,0xb2,0xa9,0x25,0x94,0x3e,0x57,0xa,0x5a,0xda,0x71,0xa1,0x97,0x2c,0x4,0x85,0x84,0xb9,0xdc,0x38,0xad,0xff,0xbb,0x35,0x4f,0x60,0x5c,0x6d,0x9d,0x3c,0xf1,0x28,0x5f,0x22,0xaa,0x36,0x47,0x6a,0x1e,0x6,0x70,0xab,0xeb,0xfb,0xbe,0x61,0xe5,0x77,0xf0,0xa0,0x83,0x2a,0xd2,0x18,0xc8,0xe2,0x34,0x1a,0x2b,0x46,0x1,0xfe,0x52,0xd4,0x31,0xae,0x66,0xa4,0xd7,0x82,0xd8,0x78,0xe3,0x96,0x54,0x7a,0x8c,0x4c,0xc3,0x12,0x5b,0x59,0xfa,0x14,0xa7,0xcb,0x6c,0x47,0xe1,0xe,0x21,0x48,0x56,0xb7,0xd3,0x7e,0xe0,0x39,0x9b,0x3b,0xb6,0x8f,0x37,0x13,0xd4,0x5b,0x7d,0xc7,0x51,0xac,0x5d,0x3c,0xd7,0x2e,0x3,0x50,0xa4,0xfe,0x18,0xa0,0x1f,0x7c,0x27,0x4d,0xd0,0x43,0x45,0xeb,0xd6,0x35,0xca,0x8e,0xa2,0x86,0xf4,0xf7,0x4c,0x96,0x9,0xf2,0x8a,0x5a,0xee,0x31,0x36,0xfb,0x41,0xbe,0x9c,0xab,0xb1,0x75,0xde,0xad,0xe5,0x7f,0xa6,0xc9,0xf9,0x70,0x2d,0x91,0x82,0x73,0x30,0xc6,0xcd,0xfa,0x6b,0x59,0xa5,0x77,0x7a,0x4a,0xbf,0x76,0xc8,0xda,0xb4,0x1e,0xff,0x66,0x74,0x11,0x83,0x8d,0x7,0x63,0xed,0x1d,0xf3,0xb9,0x29,0xd1,0xa1,0xe7,0xc3,0x10,0x28,0x25,0x3d,0x5,0x19,0xaf,0xcc,0x98,0x88,0x22,0x33,0x60,0x2b,0xf1,0xe8,0xf8,0xd2,0x4,0x6f,0x84,0x32,0xfc,0xb0,0x5c,0x68,0x7b,0x17,0xc2,0x1a,0x6e,0xcb,0x6d,0xdf,0x3e,0x4b,0x23,0x89,0x55,0xc,0xa7,0xb3,0x8b,0xb5,0xae,0xef,0x81,0x2f,0xb8,0xd,0x34,0x64,0xe4,0x4f,0x9f,0xa9,0x12,0x3a,0xbb,0xba,0x87,0xe2,0x6,0x93,0xc1,0x85,0x57,0x1,0xd9,0xe3,0xf0,0x40,0xd8,0x92,0x9d,0x42,0x8c,0x97,0x1b,0xaa,0x0,0x69,0x38,0x4e,0x95,0xd5,0xc5,0x80,0x5f,0xdb,0x49,0xce,0x9e,0xbd,0x14,0xec,0x26,0xf6,0xb,0x71,0x5e,0x62,0x53,0xa3,0x2,0xcf,0x16,0x61,0x1c,0x94,0x8,0x79,0x54,0x20,0x46,0xdd,0xa8,0x6a,0x44,0xb2,0x72,0xfd,0x2c,0x65,0x67,0xc4,0x2a,0x99,0xf5,0x52,0xdc,0xa,0x24,0x15,0x78,0x3f,0xc0,0x6c,0xea,0xf,0x90,0x58,0x9a,0xe9,0xbc,0xe6,0x9e,0xb3,0x4a,0xa1,0x85,0x63,0x39,0xcd,0xe0,0xc6,0x49,0x8e,0xc0,0x31,0xcc,0x5a,0x6,0xa4,0x7d,0xe3,0xaa,0x12,0x2b,0xa6,0xbc,0x93,0x7c,0xda,0x4e,0x2a,0xcb,0xd5,0xdc,0x66,0xab,0xac,0x2c,0x36,0x1,0x23,0x94,0xb,0xd1,0x6a,0x73,0xc7,0x17,0x6f,0x57,0xa8,0x4b,0x76,0x69,0x1b,0x3f,0x13,0xba,0xe1,0x82,0x3d,0xd8,0xde,0x4d,0xd0,0x29,0x47,0x55,0xeb,0xe9,0xfb,0x62,0x83,0x38,0xc4,0xf6,0x67,0x22,0xd7,0xe7,0xea,0x1f,0xc,0xb0,0xed,0x50,0x5b,0xad,0xee,0x78,0x30,0x43,0xe8,0x64,0x54,0x3b,0xe2,0xb6,0xfd,0xae,0xbf,0x4f,0x65,0x75,0x6c,0x84,0x98,0xa0,0xb8,0x15,0x5,0x51,0x32,0x3c,0x4c,0xb4,0x24,0xb5,0x8d,0x5e,0x7a,0x9a,0x10,0x1e,0x8c,0x6e,0x80,0x70,0xfe,0x72,0x33,0x28,0x16,0x90,0x25,0xb2,0x1c,0x14,0xbe,0xd6,0xa3,0x2e,0x3a,0x91,0xc8,0x87,0x5f,0x8a,0xe6,0x42,0xf0,0x56,0xf3,0xaf,0x19,0xf2,0x99,0xf5,0xc1,0x2d,0x61,0xa,0x11,0xdf,0x0,0xf4,0x9d,0x37,0x86,0x7e,0x44,0x9c,0xca,0xf,0x45,0xdd,0x6d,0x7f,0x1a,0x27,0x26,0x18,0x5c,0xe,0x9b,0xd2,0x79,0xf9,0xa9,0xa7,0x8f,0x34,0x2,0x9,0x81,0xfc,0x8b,0xbd,0xc9,0xe4,0x95,0xff,0xc3,0xec,0x96,0x52,0x9f,0x3e,0xce,0x20,0x3,0x53,0xd4,0x6b,0xbb,0x71,0x89,0x48,0x8,0xd3,0xa5,0x46,0xc2,0x1d,0x58,0xc5,0xd,0x92,0x77,0x7b,0x21,0x74,0x7,0x88,0xb9,0x97,0x41,0xf1,0x5d,0xa2,0xe5,0x59,0xfa,0xf8,0xb1,0xcf,0x68,0x4,0xb7,0xf7,0x35,0x40,0xdb,0x60,0xef,0x2f,0xd9,0x5d,0x5b,0x53,0xce,0x62,0x39,0xbe,0x1,0x98,0xea,0x90,0xbc,0x2b,0xd4,0xf5,0xc8,0x44,0xf0,0xec,0x94,0x88,0x17,0xe9,0x52,0xb5,0xaf,0xa0,0x82,0xe5,0x5f,0x2f,0x28,0xa9,0xcd,0x56,0x48,0x10,0x3f,0x59,0xff,0x91,0x29,0x25,0xa8,0x27,0x85,0x60,0xfe,0xb2,0x43,0xd9,0x4f,0x45,0x63,0xd,0xca,0xe0,0x6,0x4e,0xba,0x30,0x1d,0x22,0xc9,0x3,0xed,0x7d,0xf3,0x93,0x19,0xf,0x9d,0xe,0x36,0xf9,0xdd,0xcf,0xbf,0xa7,0x37,0x86,0x96,0xb1,0xd2,0x1b,0x7,0x3b,0x23,0xe6,0xcc,0xef,0xf6,0x7e,0x35,0x3c,0x2d,0xd7,0xe7,0x61,0xb8,0xb3,0xfb,0x6b,0xc0,0xd8,0xd3,0x6d,0x2e,0x8f,0x9c,0x6e,0x33,0x54,0xa1,0x69,0x64,0x47,0xbb,0xe4,0x75,0x78,0x6a,0x0,0xe1,0xc4,0xaa,0x68,0xd6,0xc,0x24,0x81,0xb7,0xfa,0x51,0x2a,0x7a,0xdf,0x9b,0x18,0x8d,0x99,0xfc,0xa5,0xa4,0xc6,0x8c,0xee,0x5e,0xc7,0xfd,0x49,0x1f,0x1e,0x77,0x5,0xb4,0x92,0x89,0x83,0x5c,0x42,0x76,0xe2,0xae,0x9a,0x2c,0x1a,0x71,0x73,0xc1,0x70,0xd5,0xdc,0x4,0x65,0x9,0xb9,0xad,0x4b,0x12,0x3d,0x97,0x20,0x55,0xa6,0x13,0x9f,0x31,0xb0,0xf1,0x95,0xab,0x6c,0xe3,0x5a,0xac,0xb6,0x74,0x58,0xc3,0xeb,0x4c,0x34,0x87,0x79,0xda,0x32,0x7b,0xde,0x72,0x66,0x21,0x3a,0xb,0xc2,0x14,0xa2,0xf8,0x84,0xf7,0x8e,0x46,0xf4,0x11,0x41,0xc5,0xdb,0x9e,0x8b,0xcb,0x26,0x50,0x38,0xe8,0xa,0xf2,0x80,0xa3,0x57,0xd0,0x1c,0xd1,0x4d,0xbd,0x40,0x7c,0x15,0x6f,0x4a,0x3e,0x16,0x67,0x2,0x8a,0x8,0x7f,0xd9,0x1e,0x91,0xb7,0xd,0x9b,0x66,0x97,0xf6,0x1d,0xe4,0xc9,0x9a,0x6e,0x34,0xd2,0x8d,0x2b,0xc4,0xeb,0x82,0x9c,0x7d,0x19,0xb4,0x2a,0xf3,0x51,0xf1,0x7c,0x45,0xfd,0x3d,0x86,0x5c,0xc3,0x38,0x40,0x90,0x24,0xfb,0xfc,0x31,0x8b,0x74,0x56,0x61,0x7b,0x6a,0xd5,0xb6,0xed,0x87,0x1a,0x89,0x8f,0x21,0x1c,0xff,0x0,0x44,0x68,0x4c,0x3e,0x30,0xa1,0x93,0x6f,0xbd,0xb0,0x80,0x75,0xbc,0x2,0x10,0x7e,0xd4,0x35,0xac,0xbe,0xbf,0x14,0x67,0x2f,0xb5,0x6c,0x3,0x33,0xba,0xe7,0x5b,0x48,0xb9,0xfa,0xc,0x7,0xef,0xf7,0xcf,0xd3,0x65,0x6,0x52,0x42,0xe8,0xf9,0xaa,0xe1,0x3b,0x22,0x32,0x18,0xdb,0x49,0x47,0xcd,0xa9,0x27,0xd7,0x39,0x73,0xe3,0x1b,0x6b,0x2d,0x9,0xda,0xe2,0xf4,0x81,0xe9,0x43,0x9f,0xc6,0x6d,0x79,0x41,0x7f,0x64,0x25,0x4b,0xe5,0x72,0xc7,0xce,0xa5,0x4e,0xf8,0x36,0x7a,0x96,0xa2,0xb1,0xdd,0x8,0xd0,0xa4,0x1,0xa7,0x15,0x9d,0xcb,0x13,0x29,0x3a,0x8a,0x12,0x58,0x57,0x88,0x46,0x5d,0xd1,0x60,0xca,0xa3,0xfe,0xae,0x2e,0x85,0x55,0x63,0xd8,0xf0,0x71,0x70,0x4d,0x28,0xcc,0x59,0xb,0x4f,0xc1,0xbb,0x94,0xa8,0x99,0x69,0xc8,0x5,0xdc,0xab,0xd6,0x5e,0xc2,0xb3,0x9e,0xea,0xf2,0x84,0x5f,0x1f,0xf,0x4a,0x95,0x11,0x83,0x4,0x54,0x77,0xde,0x26,0xec,0x3c,0x16,0xc0,0xee,0xdf,0xb2,0xf5,0xa,0xa6,0x20,0xc5,0x5a,0x92,0x50,0x23,0x76,0x2c,0x8c,0x17,0x62,0xa0,0x8e,0x78,0xb8,0x37,0xe6,0xaf,0xad,0xe,0xe0,0x53,0x3f,0x98,0x85,0x23,0xcc,0xe3,0x8a,0x94,0x75,0x11,0xbc,0x22,0xfb,0x59,0xf9,0x74,0x4d,0xf5,0xd1,0x16,0x99,0xbf,0x5,0x93,0x6e,0x9f,0xfe,0x15,0xec,0xc1,0x92,0x66,0x3c,0xda,0x62,0xdd,0xbe,0xe5,0x8f,0x12,0x81,0x87,0x29,0x14,0xf7,0x8,0x4c,0x60,0x44,0x36,0x35,0x8e,0x54,0xcb,0x30,0x48,0x98,0x2c,0xf3,0xf4,0x39,0x83,0x7c,0x5e,0x69,0x73,0xb7,0x1c,0x6f,0x27,0xbd,0x64,0xb,0x3b,0xb2,0xef,0x53,0x40,0xb1,0xf2,0x4,0xf,0x38,0xa9,0x9b,0x67,0xb5,0xb8,0x88,0x7d,0xb4,0xa,0x18,0x76,0xdc,0x3d,0xa4,0xb6,0xd3,0x41,0x4f,0xc5,0xa1,0x2f,0xdf,0x31,0x7b,0xeb,0x13,0x63,0x25,0x1,0xd2,0xea,0xe7,0xff,0xc7,0xdb,0x6d,0xe,0x5a,0x4a,0xe0,0xf1,0xa2,0xe9,0x33,0x2a,0x3a,0x10,0xc6,0xad,0x46,0xf0,0x3e,0x72,0x9e,0xaa,0xb9,0xd5,0x0,0xd8,0xac,0x9,0xaf,0x1d,0xfc,0x89,0xe1,0x4b,0x97,0xce,0x65,0x71,0x49,0x77,0x6c,0x2d,0x43,0xed,0x7a,0xcf,0xf6,0xa6,0x26,0x8d,0x5d,0x6b,0xd0,0xf8,0x79,0x78,0x45,0x20,0xc4,0x51,0x3,0x47,0x95,0xc3,0x1b,0x21,0x32,0x82,0x1a,0x50,0x5f,0x80,0x4e,0x55,0xd9,0x68,0xc2,0xab,0xfa,0x8c,0x57,0x17,0x7,0x42,0x9d,0x19,0x8b,0xc,0x5c,0x7f,0xd6,0x2e,0xe4,0x34,0xc9,0xb3,0x9c,0xa0,0x91,0x61,0xc0,0xd,0xd4,0xa3,0xde,0x56,0xca,0xbb,0x96,0xe2,0x84,0x1f,0x6a,0xa8,0x86,0x70,0xb0,0x3f,0xee,0xa7,0xa5,0x6,0xe8,0x5b,0x37,0x90,0x1e,0xc8,0xe6,0xd7,0xba,0xfd,0x2,0xae,0x28,0xcd,0x52,0x9a,0x58,0x2b,0x7e,0x24,0xf1,0xdc,0x25,0xce,0xea,0xc,0x56,0xa2,0x8f,0xa9,0x26,0xe1,0xaf,0x5e,0xa3,0x35,0x69,0xcb,0x12,0x8c,0xc5,0x7d,0x44,0xc9,0xd3,0xfc,0x13,0xb5,0x21,0x45,0xa4,0xba,0xb3,0x9,0xc4,0xc3,0x43,0x59,0x6e,0x4c,0xfb,0x64,0xbe,0x5,0x1c,0xa8,0x78,0x0,0x38,0xc7,0x24,0x19,0x6,0x74,0x50,0x7c,0xd5,0x8e,0xed,0x52,0xb7,0xb1,0x22,0xbf,0x46,0x28,0x3a,0x84,0x86,0x94,0xd,0xec,0x57,0xab,0x99,0x8,0x4d,0xb8,0x88,0x85,0x70,0x63,0xdf,0x82,0x3f,0x34,0xc2,0x81,0x17,0x5f,0x2c,0x87,0xb,0x3b,0x54,0x8d,0xd9,0x92,0xc1,0xd0,0x20,0xa,0x1a,0x3,0xeb,0xf7,0xcf,0xd7,0x7a,0x6a,0x3e,0x5d,0x53,0x23,0xdb,0x4b,0xda,0xe2,0x31,0x15,0xf5,0x7f,0x71,0xe3,0x1,0xef,0x1f,0x91,0x1d,0x5c,0x47,0x79,0xff,0x4a,0xdd,0x73,0x7b,0xd1,0xb9,0xcc,0x41,0x55,0xfe,0xa7,0xe8,0x30,0xe5,0x89,0x2d,0x9f,0x39,0x9c,0xc0,0x76,0x9d,0xf6,0x9a,0xae,0x42,0xe,0x65,0x7e,0xb0,0x6f,0x9b,0xf2,0x58,0xe9,0x11,0x2b,0xf3,0xa5,0x60,0x2a,0xb2,0x2,0x10,0x75,0x48,0x49,0x77,0x33,0x61,0xf4,0xbd,0x16,0x96,0xc6,0xc8,0xe0,0x5b,0x6d,0x66,0xee,0x93,0xe4,0xd2,0xa6,0x8b,0xfa,0x90,0xac,0x83,0xf9,0x3d,0xf0,0x51,0xa1,0x4f,0x6c,0x3c,0xbb,0x4,0xd4,0x1e,0xe6,0x27,0x67,0xbc,0xca,0x29,0xad,0x72,0x37,0xaa,0x62,0xfd,0x18,0x14,0x4e,0x1b,0x68,0xe7,0xd6,0xf8,0x2e,0x9e,0x32,0xcd,0x8a,0x36,0x95,0x97,0xde,0xa0,0x7,0x6b,0xd8,0x98,0x5a,0x2f,0xb4,0xf,0x80,0x40,0xb6,0xa6,0xa0,0xa8,0x35,0x99,0xc2,0x45,0xfa,0x63,0x11,0x6b,0x47,0xd0,0x2f,0xe,0x33,0xbf,0xb,0x17,0x6f,0x73,0xec,0x12,0xa9,0x4e,0x54,0x5b,0x79,0x1e,0xa4,0xd4,0xd3,0x52,0x36,0xad,0xb3,0xeb,0xc4,0xa2,0x4,0x6a,0xd2,0xde,0x53,0xdc,0x7e,0x9b,0x5,0x49,0xb8,0x22,0xb4,0xbe,0x98,0xf6,0x31,0x1b,0xfd,0xb5,0x41,0xcb,0xe6,0xd9,0x32,0xf8,0x16,0x86,0x8,0x68,0xe2,0xf4,0x66,0xf5,0xcd,0x2,0x26,0x34,0x44,0x5c,0xcc,0x7d,0x6d,0x4a,0x29,0xe0,0xfc,0xc0,0xd8,0x1d,0x37,0x14,0xd,0x85,0xce,0xc7,0xd6,0x2c,0x1c,0x9a,0x43,0x48,0x0,0x90,0x3b,0x23,0x28,0x96,0xd5,0x74,0x67,0x95,0xc8,0xaf,0x5a,0x92,0x9f,0xbc,0x40,0x1f,0x8e,0x83,0x91,0xfb,0x1a,0x3f,0x51,0x93,0x2d,0xf7,0xdf,0x7a,0x4c,0x1,0xaa,0xd1,0x81,0x24,0x60,0xe3,0x76,0x62,0x7,0x5e,0x5f,0x3d,0x77,0x15,0xa5,0x3c,0x6,0xb2,0xe4,0xe5,0x8c,0xfe,0x4f,0x69,0x72,0x78,0xa7,0xb9,0x8d,0x19,0x55,0x61,0xd7,0xe1,0x8a,0x88,0x3a,0x8b,0x2e,0x27,0xff,0x9e,0xf2,0x42,0x56,0xb0,0xe9,0xc6,0x6c,0xdb,0xae,0x5d,0xe8,0x64,0xca,0x4b,0xa,0x6e,0x50,0x97,0x18,0xa1,0x57,0x4d,0x8f,0xa3,0x38,0x10,0xb7,0xcf,0x7c,0x82,0x21,0xc9,0x80,0x25,0x89,0x9d,0xda,0xc1,0xf0,0x39,0xef,0x59,0x3,0x7f,0xc,0x75,0xbd,0xf,0xea,0xba,0x3e,0x20,0x65,0x70,0x30,0xdd,0xab,0xc3,0x13,0xf1,0x9,0x7b,0x58,0xac,0x2b,0xe7,0x2a,0xb6,0x46,0xbb,0x87,0xee,0x94,0xb1,0xc5,0xed,0x9c,0xf9,0x71,0xf3,0x84,0xef,0x28,0xa7,0x81,0x3b,0xad,0x50,0xa1,0xc0,0x2b,0xd2,0xff,0xac,0x58,0x2,0xe4,0xbb,0x1d,0xf2,0xdd,0xb4,0xaa,0x4b,0x2f,0x82,0x1c,0xc5,0x67,0xc7,0x4a,0x73,0xcb,0xb,0xb0,0x6a,0xf5,0xe,0x76,0xa6,0x12,0xcd,0xca,0x7,0xbd,0x42,0x60,0x57,0x4d,0x5c,0xe3,0x80,0xdb,0xb1,0x2c,0xbf,0xb9,0x17,0x2a,0xc9,0x36,0x72,0x5e,0x7a,0x8,0x6,0x97,0xa5,0x59,0x8b,0x86,0xb6,0x43,0x8a,0x34,0x26,0x48,0xe2,0x3,0x9a,0x88,0x89,0x22,0x51,0x19,0x83,0x5a,0x35,0x5,0x8c,0xd1,0x6d,0x7e,0x8f,0xcc,0x3a,0x31,0xd9,0xc1,0xf9,0xe5,0x53,0x30,0x64,0x74,0xde,0xcf,0x9c,0xd7,0xd,0x14,0x4,0x2e,0xed,0x7f,0x71,0xfb,0x9f,0x11,0xe1,0xf,0x45,0xd5,0x2d,0x5d,0x1b,0x3f,0xec,0xd4,0xc2,0xb7,0xdf,0x75,0xa9,0xf0,0x5b,0x4f,0x77,0x49,0x52,0x13,0x7d,0xd3,0x44,0xf1,0xf8,0x93,0x78,0xce,0x0,0x4c,0xa0,0x94,0x87,0xeb,0x3e,0xe6,0x92,0x37,0x91,0x23,0xab,0xfd,0x25,0x1f,0xc,0xbc,0x24,0x6e,0x61,0xbe,0x70,0x6b,0xe7,0x56,0xfc,0x95,0xc8,0x98,0x18,0xb3,0x63,0x55,0xee,0xc6,0x47,0x46,0x7b,0x1e,0xfa,0x6f,0x3d,0x79,0xf7,0x8d,0xa2,0x9e,0xaf,0x5f,0xfe,0x33,0xea,0x9d,0xe0,0x68,0xf4,0x85,0xa8,0xdc,0xc4,0xb2,0x69,0x29,0x39,0x7c,0xa3,0x27,0xb5,0x32,0x62,0x41,0xe8,0x10,0xda,0xa,0x20,0xf6,0xd8,0xe9,0x84,0xc3,0x3c,0x90,0x16,0xf3,0x6c,0xa4,0x66,0x15,0x40,0x1a,0xba,0x21,0x54,0x96,0xb8,0x4e,0x8e,0x1,0xd0,0x99,0x9b,0x38,0xd6,0x65,0x9,0xae,0xf9,0x5f,0xb0,0x9f,0xf6,0xe8,0x9,0x6d,0xc0,0x5e,0x87,0x25,0x85,0x8,0x31,0x89,0xad,0x6a,0xe5,0xc3,0x79,0xef,0x12,0xe3,0x82,0x69,0x90,0xbd,0xee,0x1a,0x40,0xa6,0x1e,0xa1,0xc2,0x99,0xf3,0x6e,0xfd,0xfb,0x55,0x68,0x8b,0x74,0x30,0x1c,0x38,0x4a,0x49,0xf2,0x28,0xb7,0x4c,0x34,0xe4,0x50,0x8f,0x88,0x45,0xff,0x0,0x22,0x15,0xf,0xcb,0x60,0x13,0x5b,0xc1,0x18,0x77,0x47,0xce,0x93,0x2f,0x3c,0xcd,0x8e,0x78,0x73,0x44,0xd5,0xe7,0x1b,0xc9,0xc4,0xf4,0x1,0xc8,0x76,0x64,0xa,0xa0,0x41,0xd8,0xca,0xaf,0x3d,0x33,0xb9,0xdd,0x53,0xa3,0x4d,0x7,0x97,0x6f,0x1f,0x59,0x7d,0xae,0x96,0x9b,0x83,0xbb,0xa7,0x11,0x72,0x26,0x36,0x9c,0x8d,0xde,0x95,0x4f,0x56,0x46,0x6c,0xba,0xd1,0x3a,0x8c,0x42,0xe,0xe2,0xd6,0xc5,0xa9,0x7c,0xa4,0xd0,0x75,0xd3,0x61,0x80,0xf5,0x9d,0x37,0xeb,0xb2,0x19,0xd,0x35,0xb,0x10,0x51,0x3f,0x91,0x6,0xb3,0x8a,0xda,0x5a,0xf1,0x21,0x17,0xac,0x84,0x5,0x4,0x39,0x5c,0xb8,0x2d,0x7f,0x3b,0xe9,0xbf,0x67,0x5d,0x4e,0xfe,0x66,0x2c,0x23,0xfc,0x32,0x29,0xa5,0x14,0xbe,0xd7,0x86,0xf0,0x2b,0x6b,0x7b,0x3e,0xe1,0x65,0xf7,0x70,0x20,0x3,0xaa,0x52,0x98,0x48,0xb5,0xcf,0xe0,0xdc,0xed,0x1d,0xbc,0x71,0xa8,0xdf,0xa2,0x2a,0xb6,0xc7,0xea,0x9e,0xf8,0x63,0x16,0xd4,0xfa,0xc,0xcc,0x43,0x92,0xdb,0xd9,0x7a,0x94,0x27,0x4b,0xec,0x62,0xb4,0x9a,0xab,0xc6,0x81,0x7e,0xd2,0x54,0xb1,0x2e,0xe6,0x24,0x57,0x2,0x58,0x51,0x7c,0x85,0x6e,0x4a,0xac,0xf6,0x2,0x2f,0x9,0x86,0x41,0xf,0xfe,0x3,0x95,0xc9,0x6b,0xb2,0x2c,0x65,0xdd,0xe4,0x69,0x73,0x5c,0xb3,0x15,0x81,0xe5,0x4,0x1a,0x13,0xa9,0x64,0x63,0xe3,0xf9,0xce,0xec,0x5b,0xc4,0x1e,0xa5,0xbc,0x8,0xd8,0xa0,0x98,0x67,0x84,0xb9,0xa6,0xd4,0xf0,0xdc,0x75,0x2e,0x4d,0xf2,0x17,0x11,0x82,0x1f,0xe6,0x88,0x9a,0x24,0x26,0x34,0xad,0x4c,0xf7,0xb,0x39,0xa8,0xed,0x18,0x28,0x25,0xd0,0xc3,0x7f,0x22,0x9f,0x94,0x62,0x21,0xb7,0xff,0x8c,0x27,0xab,0x9b,0xf4,0x2d,0x79,0x32,0x61,0x70,0x80,0xaa,0xba,0xa3,0x4b,0x57,0x6f,0x77,0xda,0xca,0x9e,0xfd,0xf3,0x83,0x7b,0xeb,0x7a,0x42,0x91,0xb5,0x55,0xdf,0xd1,0x43,0xa1,0x4f,0xbf,0x31,0xbd,0xfc,0xe7,0xd9,0x5f,0xea,0x7d,0xd3,0xdb,0x71,0x19,0x6c,0xe1,0xf5,0x5e,0x7,0x48,0x90,0x45,0x29,0x8d,0x3f,0x99,0x3c,0x60,0xd6,0x3d,0x56,0x3a,0xe,0xe2,0xae,0xc5,0xde,0x10,0xcf,0x3b,0x52,0xf8,0x49,0xb1,0x8b,0x53,0x5,0xc0,0x8a,0x12,0xa2,0xb0,0xd5,0xe8,0xe9,0xd7,0x93,0xc1,0x54,0x1d,0xb6,0x36,0x66,0x68,0x40,0xfb,0xcd,0xc6,0x4e,0x33,0x44,0x72,0x6,0x2b,0x5a,0x30,0xc,0x23,0x59,0x9d,0x50,0xf1,0x1,0xef,0xcc,0x9c,0x1b,0xa4,0x74,0xbe,0x46,0x87,0xc7,0x1c,0x6a,0x89,0xd,0xd2,0x97,0xa,0xc2,0x5d,0xb8,0xb4,0xee,0xbb,0xc8,0x47,0x76,0x58,0x8e,0x3e,0x92,0x6d,0x2a,0x96,0x35,0x37,0x7e,0x0,0xa7,0xcb,0x78,0x38,0xfa,0x8f,0x14,0xaf,0x20,0xe0,0x16,0x64,0x62,0x6a,0xf7,0x5b,0x0,0x87,0x38,0xa1,0xd3,0xa9,0x85,0x12,0xed,0xcc,0xf1,0x7d,0xc9,0xd5,0xad,0xb1,0x2e,0xd0,0x6b,0x8c,0x96,0x99,0xbb,0xdc,0x66,0x16,0x11,0x90,0xf4,0x6f,0x71,0x29,0x6,0x60,0xc6,0xa8,0x10,0x1c,0x91,0x1e,0xbc,0x59,0xc7,0x8b,0x7a,0xe0,0x76,0x7c,0x5a,0x34,0xf3,0xd9,0x3f,0x77,0x83,0x9,0x24,0x1b,0xf0,0x3a,0xd4,0x44,0xca,0xaa,0x20,0x36,0xa4,0x37,0xf,0xc0,0xe4,0xf6,0x86,0x9e,0xe,0xbf,0xaf,0x88,0xeb,0x22,0x3e,0x2,0x1a,0xdf,0xf5,0xd6,0xcf,0x47,0xc,0x5,0x14,0xee,0xde,0x58,0x81,0x8a,0xc2,0x52,0xf9,0xe1,0xea,0x54,0x17,0xb6,0xa5,0x57,0xa,0x6d,0x98,0x50,0x5d,0x7e,0x82,0xdd,0x4c,0x41,0x53,0x39,0xd8,0xfd,0x93,0x51,0xef,0x35,0x1d,0xb8,0x8e,0xc3,0x68,0x13,0x43,0xe6,0xa2,0x21,0xb4,0xa0,0xc5,0x9c,0x9d,0xff,0xb5,0xd7,0x67,0xfe,0xc4,0x70,0x26,0x27,0x4e,0x3c,0x8d,0xab,0xb0,0xba,0x65,0x7b,0x4f,0xdb,0x97,0xa3,0x15,0x23,0x48,0x4a,0xf8,0x49,0xec,0xe5,0x3d,0x5c,0x30,0x80,0x94,0x72,0x2b,0x4,0xae,0x19,0x6c,0x9f,0x2a,0xa6,0x8,0x89,0xc8,0xac,0x92,0x55,0xda,0x63,0x95,0x8f,0x4d,0x61,0xfa,0xd2,0x75,0xd,0xbe,0x40,0xe3,0xb,0x42,0xe7,0x4b,0x5f,0x18,0x3,0x32,0xfb,0x2d,0x9b,0xc1,0xbd,0xce,0xb7,0x7f,0xcd,0x28,0x78,0xfc,0xe2,0xa7,0xb2,0xf2,0x1f,0x69,0x1,0xd1,0x33,0xcb,0xb9,0x9a,0x6e,0xe9,0x25,0xe8,0x74,0x84,0x79,0x45,0x2c,0x56,0x73,0x7,0x2f,0x5e,0x3b,0xb3,0x31,0x46,0x83,0x44,0xcb,0xed,0x57,0xc1,0x3c,0xcd,0xac,0x47,0xbe,0x93,0xc0,0x34,0x6e,0x88,0xd7,0x71,0x9e,0xb1,0xd8,0xc6,0x27,0x43,0xee,0x70,0xa9,0xb,0xab,0x26,0x1f,0xa7,0x67,0xdc,0x6,0x99,0x62,0x1a,0xca,0x7e,0xa1,0xa6,0x6b,0xd1,0x2e,0xc,0x3b,0x21,0x30,0x8f,0xec,0xb7,0xdd,0x40,0xd3,0xd5,0x7b,0x46,0xa5,0x5a,0x1e,0x32,0x16,0x64,0x6a,0xfb,0xc9,0x35,0xe7,0xea,0xda,0x2f,0xe6,0x58,0x4a,0x24,0x8e,0x6f,0xf6,0xe4,0xe5,0x4e,0x3d,0x75,0xef,0x36,0x59,0x69,0xe0,0xbd,0x1,0x12,0xe3,0xa0,0x56,0x5d,0xb5,0xad,0x95,0x89,0x3f,0x5c,0x8,0x18,0xb2,0xa3,0xf0,0xbb,0x61,0x78,0x68,0x42,0x81,0x13,0x1d,0x97,0xf3,0x7d,0x8d,0x63,0x29,0xb9,0x41,0x31,0x77,0x53,0x80,0xb8,0xae,0xdb,0xb3,0x19,0xc5,0x9c,0x37,0x23,0x1b,0x25,0x3e,0x7f,0x11,0xbf,0x28,0x9d,0x94,0xff,0x14,0xa2,0x6c,0x20,0xcc,0xf8,0xeb,0x87,0x52,0x8a,0xfe,0x5b,0xfd,0x4f,0xc7,0x91,0x49,0x73,0x60,0xd0,0x48,0x2,0xd,0xd2,0x1c,0x7,0x8b,0x3a,0x90,0xf9,0xa4,0xf4,0x74,0xdf,0xf,0x39,0x82,0xaa,0x2b,0x2a,0x17,0x72,0x96,0x3,0x51,0x15,0x9b,0xe1,0xce,0xf2,0xc3,0x33,0x92,0x5f,0x86,0xf1,0x8c,0x4,0x98,0xe9,0xc4,0xb0,0xa8,0xde,0x5,0x45,0x55,0x10,0xcf,0x4b,0xd9,0x5e,0xe,0x2d,0x84,0x7c,0xb6,0x66,0x4c,0x9a,0xb4,0x85,0xe8,0xaf,0x50,0xfc,0x7a,0x9f,0x0,0xc8,0xa,0x79,0x2c,0x76,0xd6,0x4d,0x38,0xfa,0xd4,0x22,0xe2,0x6d,0xbc,0xf5,0xf7,0x54,0xba,0x9,0x65,0xc2,0xd6,0x70,0x9f,0xb0,0xd9,0xc7,0x26,0x42,0xef,0x71,0xa8,0xa,0xaa,0x27,0x1e,0xa6,0x82,0x45,0xca,0xec,0x56,0xc0,0x3d,0xcc,0xad,0x46,0xbf,0x92,0xc1,0x35,0x6f,0x89,0x31,0x8e,0xed,0xb6,0xdc,0x41,0xd2,0xd4,0x7a,0x47,0xa4,0x5b,0x1f,0x33,0x17,0x65,0x66,0xdd,0x7,0x98,0x63,0x1b,0xcb,0x7f,0xa0,0xa7,0x6a,0xd0,0x2f,0xd,0x3a,0x20,0xe4,0x4f,0x3c,0x74,0xee,0x37,0x58,0x68,0xe1,0xbc,0x0,0x13,0xe2,0xa1,0x57,0x5c,0x6b,0xfa,0xc8,0x34,0xe6,0xeb,0xdb,0x2e,0xe7,0x59,0x4b,0x25,0x8f,0x6e,0xf7,0xe5,0x80,0x12,0x1c,0x96,0xf2,0x7c,0x8c,0x62,0x28,0xb8,0x40,0x30,0x76,0x52,0x81,0xb9,0xb4,0xac,0x94,0x88,0x3e,0x5d,0x9,0x19,0xb3,0xa2,0xf1,0xba,0x60,0x79,0x69,0x43,0x95,0xfe,0x15,0xa3,0x6d,0x21,0xcd,0xf9,0xea,0x86,0x53,0x8b,0xff,0x5a,0xfc,0x4e,0xaf,0xda,0xb2,0x18,0xc4,0x9d,0x36,0x22,0x1a,0x24,0x3f,0x7e,0x10,0xbe,0x29,0x9c,0xa5,0xf5,0x75,0xde,0xe,0x38,0x83,0xab,0x2a,0x2b,0x16,0x73,0x97,0x2,0x50,0x14,0xc6,0x90,0x48,0x72,0x61,0xd1,0x49,0x3,0xc,0xd3,0x1d,0x6,0x8a,0x3b,0x91,0xf8,0xa9,0xdf,0x4,0x44,0x54,0x11,0xce,0x4a,0xd8,0x5f,0xf,0x2c,0x85,0x7d,0xb7,0x67,0x9a,0xe0,0xcf,0xf3,0xc2,0x32,0x93,0x5e,0x87,0xf0,0x8d,0x5,0x99,0xe8,0xc5,0xb1,0xd7,0x4c,0x39,0xfb,0xd5,0x23,0xe3,0x6c,0xbd,0xf4,0xf6,0x55,0xbb,0x8,0x64,0xc3,0x4d,0x9b,0xb5,0x84,0xe9,0xae,0x51,0xfd,0x7b,0x9e,0x1,0xc9,0xb,0x78,0x2d,0x77,0x77,0x5a,0xa3,0x48,0x6c,0x8a,0xd0,0x24,0x9,0x2f,0xa0,0x67,0x29,0xd8,0x25,0xb3,0xef,0x4d,0x94,0xa,0x43,0xfb,0xc2,0x4f,0x55,0x7a,0x95,0x33,0xa7,0xc3,0x22,0x3c,0x35,0x8f,0x42,0x45,0xc5,0xdf,0xe8,0xca,0x7d,0xe2,0x38,0x83,0x9a,0x2e,0xfe,0x86,0xbe,0x41,0xa2,0x9f,0x80,0xf2,0xd6,0xfa,0x53,0x8,0x6b,0xd4,0x31,0x37,0xa4,0x39,0xc0,0xae,0xbc,0x2,0x0,0x12,0x8b,0x6a,0xd1,0x2d,0x1f,0x8e,0xcb,0x3e,0xe,0x3,0xf6,0xe5,0x59,0x4,0xb9,0xb2,0x44,0x7,0x91,0xd9,0xaa,0x1,0x8d,0xbd,0xd2,0xb,0x5f,0x14,0x47,0x56,0xa6,0x8c,0x9c,0x85,0x6d,0x71,0x49,0x51,0xfc,0xec,0xb8,0xdb,0xd5,0xa5,0x5d,0xcd,0x5c,0x64,0xb7,0x93,0x73,0xf9,0xf7,0x65,0x87,0x69,0x99,0x17,0x9b,0xda,0xc1,0xff,0x79,0xcc,0x5b,0xf5,0xfd,0x57,0x3f,0x4a,0xc7,0xd3,0x78,0x21,0x6e,0xb6,0x63,0xf,0xab,0x19,0xbf,0x1a,0x46,0xf0,0x1b,0x70,0x1c,0x28,0xc4,0x88,0xe3,0xf8,0x36,0xe9,0x1d,0x74,0xde,0x6f,0x97,0xad,0x75,0x23,0xe6,0xac,0x34,0x84,0x96,0xf3,0xce,0xcf,0xf1,0xb5,0xe7,0x72,0x3b,0x90,0x10,0x40,0x4e,0x66,0xdd,0xeb,0xe0,0x68,0x15,0x62,0x54,0x20,0xd,0x7c,0x16,0x2a,0x5,0x7f,0xbb,0x76,0xd7,0x27,0xc9,0xea,0xba,0x3d,0x82,0x52,0x98,0x60,0xa1,0xe1,0x3a,0x4c,0xaf,0x2b,0xf4,0xb1,0x2c,0xe4,0x7b,0x9e,0x92,0xc8,0x9d,0xee,0x61,0x50,0x7e,0xa8,0x18,0xb4,0x4b,0xc,0xb0,0x13,0x11,0x58,0x26,0x81,0xed,0x5e,0x1e,0xdc,0xa9,0x32,0x89,0x6,0xc6,0x30,0xc9,0xcf,0xc7,0x5a,0xf6,0xad,0x2a,0x95,0xc,0x7e,0x4,0x28,0xbf,0x40,0x61,0x5c,0xd0,0x64,0x78,0x0,0x1c,0x83,0x7d,0xc6,0x21,0x3b,0x34,0x16,0x71,0xcb,0xbb,0xbc,0x3d,0x59,0xc2,0xdc,0x84,0xab,0xcd,0x6b,0x5,0xbd,0xb1,0x3c,0xb3,0x11,0xf4,0x6a,0x26,0xd7,0x4d,0xdb,0xd1,0xf7,0x99,0x5e,0x74,0x92,0xda,0x2e,0xa4,0x89,0xb6,0x5d,0x97,0x79,0xe9,0x67,0x7,0x8d,0x9b,0x9,0x9a,0xa2,0x6d,0x49,0x5b,0x2b,0x33,0xa3,0x12,0x2,0x25,0x46,0x8f,0x93,0xaf,0xb7,0x72,0x58,0x7b,0x62,0xea,0xa1,0xa8,0xb9,0x43,0x73,0xf5,0x2c,0x27,0x6f,0xff,0x54,0x4c,0x47,0xf9,0xba,0x1b,0x8,0xfa,0xa7,0xc0,0x35,0xfd,0xf0,0xd3,0x2f,0x70,0xe1,0xec,0xfe,0x94,0x75,0x50,0x3e,0xfc,0x42,0x98,0xb0,0x15,0x23,0x6e,0xc5,0xbe,0xee,0x4b,0xf,0x8c,0x19,0xd,0x68,0x31,0x30,0x52,0x18,0x7a,0xca,0x53,0x69,0xdd,0x8b,0x8a,0xe3,0x91,0x20,0x6,0x1d,0x17,0xc8,0xd6,0xe2,0x76,0x3a,0xe,0xb8,0x8e,0xe5,0xe7,0x55,0xe4,0x41,0x48,0x90,0xf1,0x9d,0x2d,0x39,0xdf,0x86,0xa9,0x3,0xb4,0xc1,0x32,0x87,0xb,0xa5,0x24,0x65,0x1,0x3f,0xf8,0x77,0xce,0x38,0x22,0xe0,0xcc,0x57,0x7f,0xd8,0xa0,0x13,0xed,0x4e,0xa6,0xef,0x4a,0xe6,0xf2,0xb5,0xae,0x9f,0x56,0x80,0x36,0x6c,0x10,0x63,0x1a,0xd2,0x60,0x85,0xd5,0x51,0x4f,0xa,0x1f,0x5f,0xb2,0xc4,0xac,0x7c,0x9e,0x66,0x14,0x37,0xc3,0x44,0x88,0x45,0xd9,0x29,0xd4,0xe8,0x81,0xfb,0xde,0xaa,0x82,0xf3,0x96,0x1e,0x9c,0xeb,0x92,0x55,0xda,0xfc,0x46,0xd0,0x2d,0xdc,0xbd,0x56,0xaf,0x82,0xd1,0x25,0x7f,0x99,0xc6,0x60,0x8f,0xa0,0xc9,0xd7,0x36,0x52,0xff,0x61,0xb8,0x1a,0xba,0x37,0xe,0xb6,0x76,0xcd,0x17,0x88,0x73,0xb,0xdb,0x6f,0xb0,0xb7,0x7a,0xc0,0x3f,0x1d,0x2a,0x30,0x21,0x9e,0xfd,0xa6,0xcc,0x51,0xc2,0xc4,0x6a,0x57,0xb4,0x4b,0xf,0x23,0x7,0x75,0x7b,0xea,0xd8,0x24,0xf6,0xfb,0xcb,0x3e,0xf7,0x49,0x5b,0x35,0x9f,0x7e,0xe7,0xf5,0xf4,0x5f,0x2c,0x64,0xfe,0x27,0x48,0x78,0xf1,0xac,0x10,0x3,0xf2,0xb1,0x47,0x4c,0xa4,0xbc,0x84,0x98,0x2e,0x4d,0x19,0x9,0xa3,0xb2,0xe1,0xaa,0x70,0x69,0x79,0x53,0x90,0x2,0xc,0x86,0xe2,0x6c,0x9c,0x72,0x38,0xa8,0x50,0x20,0x66,0x42,0x91,0xa9,0xbf,0xca,0xa2,0x8,0xd4,0x8d,0x26,0x32,0xa,0x34,0x2f,0x6e,0x0,0xae,0x39,0x8c,0x85,0xee,0x5,0xb3,0x7d,0x31,0xdd,0xe9,0xfa,0x96,0x43,0x9b,0xef,0x4a,0xec,0x5e,0xd6,0x80,0x58,0x62,0x71,0xc1,0x59,0x13,0x1c,0xc3,0xd,0x16,0x9a,0x2b,0x81,0xe8,0xb5,0xe5,0x65,0xce,0x1e,0x28,0x93,0xbb,0x3a,0x3b,0x6,0x63,0x87,0x12,0x40,0x4,0x8a,0xf0,0xdf,0xe3,0xd2,0x22,0x83,0x4e,0x97,0xe0,0x9d,0x15,0x89,0xf8,0xd5,0xa1,0xb9,0xcf,0x14,0x54,0x44,0x1,0xde,0x5a,0xc8,0x4f,0x1f,0x3c,0x95,0x6d,0xa7,0x77,0x5d,0x8b,0xa5,0x94,0xf9,0xbe,0x41,0xed,0x6b,0x8e,0x11,0xd9,0x1b,0x68,0x3d,0x67,0xc7,0x5c,0x29,0xeb,0xc5,0x33,0xf3,0x7c,0xad,0xe4,0xe6,0x45,0xab,0x18,0x74,0xd3,0xf0,0x56,0xb9,0x96,0xff,0xe1,0x0,0x64,0xc9,0x57,0x8e,0x2c,0x8c,0x1,0x38,0x80,0xa4,0x63,0xec,0xca,0x70,0xe6,0x1b,0xea,0x8b,0x60,0x99,0xb4,0xe7,0x13,0x49,0xaf,0x17,0xa8,0xcb,0x90,0xfa,0x67,0xf4,0xf2,0x5c,0x61,0x82,0x7d,0x39,0x15,0x31,0x43,0x40,0xfb,0x21,0xbe,0x45,0x3d,0xed,0x59,0x86,0x81,0x4c,0xf6,0x9,0x2b,0x1c,0x6,0xc2,0x69,0x1a,0x52,0xc8,0x11,0x7e,0x4e,0xc7,0x9a,0x26,0x35,0xc4,0x87,0x71,0x7a,0x4d,0xdc,0xee,0x12,0xc0,0xcd,0xfd,0x8,0xc1,0x7f,0x6d,0x3,0xa9,0x48,0xd1,0xc3,0xa6,0x34,0x3a,0xb0,0xd4,0x5a,0xaa,0x44,0xe,0x9e,0x66,0x16,0x50,0x74,0xa7,0x9f,0x92,0x8a,0xb2,0xae,0x18,0x7b,0x2f,0x3f,0x95,0x84,0xd7,0x9c,0x46,0x5f,0x4f,0x65,0xb3,0xd8,0x33,0x85,0x4b,0x7,0xeb,0xdf,0xcc,0xa0,0x75,0xad,0xd9,0x7c,0xda,0x68,0x89,0xfc,0x94,0x3e,0xe2,0xbb,0x10,0x4,0x3c,0x2,0x19,0x58,0x36,0x98,0xf,0xba,0x83,0xd3,0x53,0xf8,0x28,0x1e,0xa5,0x8d,0xc,0xd,0x30,0x55,0xb1,0x24,0x76,0x32,0xe0,0xb6,0x6e,0x54,0x47,0xf7,0x6f,0x25,0x2a,0xf5,0x3b,0x20,0xac,0x1d,0xb7,0xde,0x8f,0xf9,0x22,0x62,0x72,0x37,0xe8,0x6c,0xfe,0x79,0x29,0xa,0xa3,0x5b,0x91,0x41,0xbc,0xc6,0xe9,0xd5,0xe4,0x14,0xb5,0x78,0xa1,0xd6,0xab,0x23,0xbf,0xce,0xe3,0x97,0xf1,0x6a,0x1f,0xdd,0xf3,0x5,0xc5,0x4a,0x9b,0xd2,0xd0,0x73,0x9d,0x2e,0x42,0xe5,0x6b,0xbd,0x93,0xa2,0xcf,0x88,0x77,0xdb,0x5d,0xb8,0x27,0xef,0x2d,0x5e,0xb,0x51,0xe,0x23,0xda,0x31,0x15,0xf3,0xa9,0x5d,0x70,0x56,0xd9,0x1e,0x50,0xa1,0x5c,0xca,0x96,0x34,0xed,0x73,0x3a,0x82,0xbb,0x36,0x2c,0x3,0xec,0x4a,0xde,0xba,0x5b,0x45,0x4c,0xf6,0x3b,0x3c,0xbc,0xa6,0x91,0xb3,0x4,0x9b,0x41,0xfa,0xe3,0x57,0x87,0xff,0xc7,0x38,0xdb,0xe6,0xf9,0x8b,0xaf,0x83,0x2a,0x71,0x12,0xad,0x48,0x4e,0xdd,0x40,0xb9,0xd7,0xc5,0x7b,0x79,0x6b,0xf2,0x13,0xa8,0x54,0x66,0xf7,0xb2,0x47,0x77,0x7a,0x8f,0x9c,0x20,0x7d,0xc0,0xcb,0x3d,0x7e,0xe8,0xa0,0xd3,0x78,0xf4,0xc4,0xab,0x72,0x26,0x6d,0x3e,0x2f,0xdf,0xf5,0xe5,0xfc,0x14,0x8,0x30,0x28,0x85,0x95,0xc1,0xa2,0xac,0xdc,0x24,0xb4,0x25,0x1d,0xce,0xea,0xa,0x80,0x8e,0x1c,0xfe,0x10,0xe0,0x6e,0xe2,0xa3,0xb8,0x86,0x0,0xb5,0x22,0x8c,0x84,0x2e,0x46,0x33,0xbe,0xaa,0x1,0x58,0x17,0xcf,0x1a,0x76,0xd2,0x60,0xc6,0x63,0x3f,0x89,0x62,0x9,0x65,0x51,0xbd,0xf1,0x9a,0x81,0x4f,0x90,0x64,0xd,0xa7,0x16,0xee,0xd4,0xc,0x5a,0x9f,0xd5,0x4d,0xfd,0xef,0x8a,0xb7,0xb6,0x88,0xcc,0x9e,0xb,0x42,0xe9,0x69,0x39,0x37,0x1f,0xa4,0x92,0x99,0x11,0x6c,0x1b,0x2d,0x59,0x74,0x5,0x6f,0x53,0x7c,0x6,0xc2,0xf,0xae,0x5e,0xb0,0x93,0xc3,0x44,0xfb,0x2b,0xe1,0x19,0xd8,0x98,0x43,0x35,0xd6,0x52,0x8d,0xc8,0x55,0x9d,0x2,0xe7,0xeb,0xb1,0xe4,0x97,0x18,0x29,0x7,0xd1,0x61,0xcd,0x32,0x75,0xc9,0x6a,0x68,0x21,0x5f,0xf8,0x94,0x27,0x67,0xa5,0xd0,0x4b,0xf0,0x7f,0xbf,0x49,0xcd,0xcb,0xc3,0x5e,0xf2,0xa9,0x2e,0x91,0x8,0x7a,0x0,0x2c,0xbb,0x44,0x65,0x58,0xd4,0x60,0x7c,0x4,0x18,0x87,0x79,0xc2,0x25,0x3f,0x30,0x12,0x75,0xcf,0xbf,0xb8,0x39,0x5d,0xc6,0xd8,0x80,0xaf,0xc9,0x6f,0x1,0xb9,0xb5,0x38,0xb7,0x15,0xf0,0x6e,0x22,0xd3,0x49,0xdf,0xd5,0xf3,0x9d,0x5a,0x70,0x96,0xde,0x2a,0xa0,0x8d,0xb2,0x59,0x93,0x7d,0xed,0x63,0x3,0x89,0x9f,0xd,0x9e,0xa6,0x69,0x4d,0x5f,0x2f,0x37,0xa7,0x16,0x6,0x21,0x42,0x8b,0x97,0xab,0xb3,0x76,0x5c,0x7f,0x66,0xee,0xa5,0xac,0xbd,0x47,0x77,0xf1,0x28,0x23,0x6b,0xfb,0x50,0x48,0x43,0xfd,0xbe,0x1f,0xc,0xfe,0xa3,0xc4,0x31,0xf9,0xf4,0xd7,0x2b,0x74,0xe5,0xe8,0xfa,0x90,0x71,0x54,0x3a,0xf8,0x46,0x9c,0xb4,0x11,0x27,0x6a,0xc1,0xba,0xea,0x4f,0xb,0x88,0x1d,0x9,0x6c,0x35,0x34,0x56,0x1c,0x7e,0xce,0x57,0x6d,0xd9,0x8f,0x8e,0xe7,0x95,0x24,0x2,0x19,0x13,0xcc,0xd2,0xe6,0x72,0x3e,0xa,0xbc,0x8a,0xe1,0xe3,0x51,0xe0,0x45,0x4c,0x94,0xf5,0x99,0x29,0x3d,0xdb,0x82,0xad,0x7,0xb0,0xc5,0x36,0x83,0xf,0xa1,0x20,0x61,0x5,0x3b,0xfc,0x73,0xca,0x3c,0x26,0xe4,0xc8,0x53,0x7b,0xdc,0xa4,0x17,0xe9,0x4a,0xa2,0xeb,0x4e,0xe2,0xf6,0xb1,0xaa,0x9b,0x52,0x84,0x32,0x68,0x14,0x67,0x1e,0xd6,0x64,0x81,0xd1,0x55,0x4b,0xe,0x1b,0x5b,0xb6,0xc0,0xa8,0x78,0x9a,0x62,0x10,0x33,0xc7,0x40,0x8c,0x41,0xdd,0x2d,0xd0,0xec,0x85,0xff,0xda,0xae,0x86,0xf7,0x92,0x1a,0x98,0xef,0x80,0x47,0xc8,0xee,0x54,0xc2,0x3f,0xce,0xaf,0x44,0xbd,0x90,0xc3,0x37,0x6d,0x8b,0xd4,0x72,0x9d,0xb2,0xdb,0xc5,0x24,0x40,0xed,0x73,0xaa,0x8,0xa8,0x25,0x1c,0xa4,0x64,0xdf,0x5,0x9a,0x61,0x19,0xc9,0x7d,0xa2,0xa5,0x68,0xd2,0x2d,0xf,0x38,0x22,0x33,0x8c,0xef,0xb4,0xde,0x43,0xd0,0xd6,0x78,0x45,0xa6,0x59,0x1d,0x31,0x15,0x67,0x69,0xf8,0xca,0x36,0xe4,0xe9,0xd9,0x2c,0xe5,0x5b,0x49,0x27,0x8d,0x6c,0xf5,0xe7,0xe6,0x4d,0x3e,0x76,0xec,0x35,0x5a,0x6a,0xe3,0xbe,0x2,0x11,0xe0,0xa3,0x55,0x5e,0xb6,0xae,0x96,0x8a,0x3c,0x5f,0xb,0x1b,0xb1,0xa0,0xf3,0xb8,0x62,0x7b,0x6b,0x41,0x82,0x10,0x1e,0x94,0xf0,0x7e,0x8e,0x60,0x2a,0xba,0x42,0x32,0x74,0x50,0x83,0xbb,0xad,0xd8,0xb0,0x1a,0xc6,0x9f,0x34,0x20,0x18,0x26,0x3d,0x7c,0x12,0xbc,0x2b,0x9e,0x97,0xfc,0x17,0xa1,0x6f,0x23,0xcf,0xfb,0xe8,0x84,0x51,0x89,0xfd,0x58,0xfe,0x4c,0xc4,0x92,0x4a,0x70,0x63,0xd3,0x4b,0x1,0xe,0xd1,0x1f,0x4,0x88,0x39,0x93,0xfa,0xa7,0xf7,0x77,0xdc,0xc,0x3a,0x81,0xa9,0x28,0x29,0x14,0x71,0x95,0x0,0x52,0x16,0x98,0xe2,0xcd,0xf1,0xc0,0x30,0x91,0x5c,0x85,0xf2,0x8f,0x7,0x9b,0xea,0xc7,0xb3,0xab,0xdd,0x6,0x46,0x56,0x13,0xcc,0x48,0xda,0x5d,0xd,0x2e,0x87,0x7f,0xb5,0x65,0x4f,0x99,0xb7,0x86,0xeb,0xac,0x53,0xff,0x79,0x9c,0x3,0xcb,0x9,0x7a,0x2f,0x75,0xd5,0x4e,0x3b,0xf9,0xd7,0x21,0xe1,0x6e,0xbf,0xf6,0xf4,0x57,0xb9,0xa,0x66,0xc1,0x4e,0xe8,0x7,0x28,0x41,0x5f,0xbe,0xda,0x77,0xe9,0x30,0x92,0x32,0xbf,0x86,0x3e,0x1a,0xdd,0x52,0x74,0xce,0x58,0xa5,0x54,0x35,0xde,0x27,0xa,0x59,0xad,0xf7,0x11,0xa9,0x16,0x75,0x2e,0x44,0xd9,0x4a,0x4c,0xe2,0xdf,0x3c,0xc3,0x87,0xab,0x8f,0xfd,0xfe,0x45,0x9f,0x0,0xfb,0x83,0x53,0xe7,0x38,0x3f,0xf2,0x48,0xb7,0x95,0xa2,0xb8,0x7c,0xd7,0xa4,0xec,0x76,0xaf,0xc0,0xf0,0x79,0x24,0x98,0x8b,0x7a,0x39,0xcf,0xc4,0xf3,0x62,0x50,0xac,0x7e,0x73,0x43,0xb6,0x7f,0xc1,0xd3,0xbd,0x17,0xf6,0x6f,0x7d,0x18,0x8a,0x84,0xe,0x6a,0xe4,0x14,0xfa,0xb0,0x20,0xd8,0xa8,0xee,0xca,0x19,0x21,0x2c,0x34,0xc,0x10,0xa6,0xc5,0x91,0x81,0x2b,0x3a,0x69,0x22,0xf8,0xe1,0xf1,0xdb,0xd,0x66,0x8d,0x3b,0xf5,0xb9,0x55,0x61,0x72,0x1e,0xcb,0x13,0x67,0xc2,0x64,0xd6,0x37,0x42,0x2a,0x80,0x5c,0x5,0xae,0xba,0x82,0xbc,0xa7,0xe6,0x88,0x26,0xb1,0x4,0x3d,0x6d,0xed,0x46,0x96,0xa0,0x1b,0x33,0xb2,0xb3,0x8e,0xeb,0xf,0x9a,0xc8,0x8c,0x5e,0x8,0xd0,0xea,0xf9,0x49,0xd1,0x9b,0x94,0x4b,0x85,0x9e,0x12,0xa3,0x9,0x60,0x31,0x47,0x9c,0xdc,0xcc,0x89,0x56,0xd2,0x40,0xc7,0x97,0xb4,0x1d,0xe5,0x2f,0xff,0x2,0x78,0x57,0x6b,0x5a,0xaa,0xb,0xc6,0x1f,0x68,0x15,0x9d,0x1,0x70,0x5d,0x29,0x4f,0xd4,0xa1,0x63,0x4d,0xbb,0x7b,0xf4,0x25,0x6c,0x6e,0xcd,0x23,0x90,0xfc,0x5b,0xd5,0x3,0x2d,0x1c,0x71,0x36,0xc9,0x65,0xe3,0x6,0x99,0x51,0x93,0xe0,0xb5,0xef,0x70,0x5d,0xa4,0x4f,0x6b,0x8d,0xd7,0x23,0xe,0x28,0xa7,0x60,0x2e,0xdf,0x22,0xb4,0xe8,0x4a,0x93,0xd,0x44,0xfc,0xc5,0x48,0x52,0x7d,0x92,0x34,0xa0,0xc4,0x25,0x3b,0x32,0x88,0x45,0x42,0xc2,0xd8,0xef,0xcd,0x7a,0xe5,0x3f,0x84,0x9d,0x29,0xf9,0x81,0xb9,0x46,0xa5,0x98,0x87,0xf5,0xd1,0xfd,0x54,0xf,0x6c,0xd3,0x36,0x30,0xa3,0x3e,0xc7,0xa9,0xbb,0x5,0x7,0x15,0x8c,0x6d,0xd6,0x2a,0x18,0x89,0xcc,0x39,0x9,0x4,0xf1,0xe2,0x5e,0x3,0xbe,0xb5,0x43,0x0,0x96,0xde,0xad,0x6,0x8a,0xba,0xd5,0xc,0x58,0x13,0x40,0x51,0xa1,0x8b,0x9b,0x82,0x6a,0x76,0x4e,0x56,0xfb,0xeb,0xbf,0xdc,0xd2,0xa2,0x5a,0xca,0x5b,0x63,0xb0,0x94,0x74,0xfe,0xf0,0x62,0x80,0x6e,0x9e,0x10,0x9c,0xdd,0xc6,0xf8,0x7e,0xcb,0x5c,0xf2,0xfa,0x50,0x38,0x4d,0xc0,0xd4,0x7f,0x26,0x69,0xb1,0x64,0x8,0xac,0x1e,0xb8,0x1d,0x41,0xf7,0x1c,0x77,0x1b,0x2f,0xc3,0x8f,0xe4,0xff,0x31,0xee,0x1a,0x73,0xd9,0x68,0x90,0xaa,0x72,0x24,0xe1,0xab,0x33,0x83,0x91,0xf4,0xc9,0xc8,0xf6,0xb2,0xe0,0x75,0x3c,0x97,0x17,0x47,0x49,0x61,0xda,0xec,0xe7,0x6f,0x12,0x65,0x53,0x27,0xa,0x7b,0x11,0x2d,0x2,0x78,0xbc,0x71,0xd0,0x20,0xce,0xed,0xbd,0x3a,0x85,0x55,0x9f,0x67,0xa6,0xe6,0x3d,0x4b,0xa8,0x2c,0xf3,0xb6,0x2b,0xe3,0x7c,0x99,0x95,0xcf,0x9a,0xe9,0x66,0x57,0x79,0xaf,0x1f,0xb3,0x4c,0xb,0xb7,0x14,0x16,0x5f,0x21,0x86,0xea,0x59,0x19,0xdb,0xae,0x35,0x8e,0x1,0xc1,0x37,0xf4,0xf2,0xfa,0x67,0xcb,0x90,0x17,0xa8,0x31,0x43,0x39,0x15,0x82,0x7d,0x5c,0x61,0xed,0x59,0x45,0x3d,0x21,0xbe,0x40,0xfb,0x1c,0x6,0x9,0x2b,0x4c,0xf6,0x86,0x81,0x0,0x64,0xff,0xe1,0xb9,0x96,0xf0,0x56,0x38,0x80,0x8c,0x1,0x8e,0x2c,0xc9,0x57,0x1b,0xea,0x70,0xe6,0xec,0xca,0xa4,0x63,0x49,0xaf,0xe7,0x13,0x99,0xb4,0x8b,0x60,0xaa,0x44,0xd4,0x5a,0x3a,0xb0,0xa6,0x34,0xa7,0x9f,0x50,0x74,0x66,0x16,0xe,0x9e,0x2f,0x3f,0x18,0x7b,0xb2,0xae,0x92,0x8a,0x4f,0x65,0x46,0x5f,0xd7,0x9c,0x95,0x84,0x7e,0x4e,0xc8,0x11,0x1a,0x52,0xc2,0x69,0x71,0x7a,0xc4,0x87,0x26,0x35,0xc7,0x9a,0xfd,0x8,0xc0,0xcd,0xee,0x12,0x4d,0xdc,0xd1,0xc3,0xa9,0x48,0x6d,0x3,0xc1,0x7f,0xa5,0x8d,0x28,0x1e,0x53,0xf8,0x83,0xd3,0x76,0x32,0xb1,0x24,0x30,0x55,0xc,0xd,0x6f,0x25,0x47,0xf7,0x6e,0x54,0xe0,0xb6,0xb7,0xde,0xac,0x1d,0x3b,0x20,0x2a,0xf5,0xeb,0xdf,0x4b,0x7,0x33,0x85,0xb3,0xd8,0xda,0x68,0xd9,0x7c,0x75,0xad,0xcc,0xa0,0x10,0x4,0xe2,0xbb,0x94,0x3e,0x89,0xfc,0xf,0xba,0x36,0x98,0x19,0x58,0x3c,0x2,0xc5,0x4a,0xf3,0x5,0x1f,0xdd,0xf1,0x6a,0x42,0xe5,0x9d,0x2e,0xd0,0x73,0x9b,0xd2,0x77,0xdb,0xcf,0x88,0x93,0xa2,0x6b,0xbd,0xb,0x51,0x2d,0x5e,0x27,0xef,0x5d,0xb8,0xe8,0x6c,0x72,0x37,0x22,0x62,0x8f,0xf9,0x91,0x41,0xa3,0x5b,0x29,0xa,0xfe,0x79,0xb5,0x78,0xe4,0x14,0xe9,0xd5,0xbc,0xc6,0xe3,0x97,0xbf,0xce,0xab,0x23,0xa1,0xd6,0x44,0x83,0xc,0x2a,0x90,0x6,0xfb,0xa,0x6b,0x80,0x79,0x54,0x7,0xf3,0xa9,0x4f,0x10,0xb6,0x59,0x76,0x1f,0x1,0xe0,0x84,0x29,0xb7,0x6e,0xcc,0x6c,0xe1,0xd8,0x60,0xa0,0x1b,0xc1,0x5e,0xa5,0xdd,0xd,0xb9,0x66,0x61,0xac,0x16,0xe9,0xcb,0xfc,0xe6,0xf7,0x48,0x2b,0x70,0x1a,0x87,0x14,0x12,0xbc,0x81,0x62,0x9d,0xd9,0xf5,0xd1,0xa3,0xad,0x3c,0xe,0xf2,0x20,0x2d,0x1d,0xe8,0x21,0x9f,0x8d,0xe3,0x49,0xa8,0x31,0x23,0x22,0x89,0xfa,0xb2,0x28,0xf1,0x9e,0xae,0x27,0x7a,0xc6,0xd5,0x24,0x67,0x91,0x9a,0x72,0x6a,0x52,0x4e,0xf8,0x9b,0xcf,0xdf,0x75,0x64,0x37,0x7c,0xa6,0xbf,0xaf,0x85,0x46,0xd4,0xda,0x50,0x34,0xba,0x4a,0xa4,0xee,0x7e,0x86,0xf6,0xb0,0x94,0x47,0x7f,0x69,0x1c,0x74,0xde,0x2,0x5b,0xf0,0xe4,0xdc,0xe2,0xf9,0xb8,0xd6,0x78,0xef,0x5a,0x53,0x38,0xd3,0x65,0xab,0xe7,0xb,0x3f,0x2c,0x40,0x95,0x4d,0x39,0x9c,0x3a,0x88,0x0,0x56,0x8e,0xb4,0xa7,0x17,0x8f,0xc5,0xca,0x15,0xdb,0xc0,0x4c,0xfd,0x57,0x3e,0x63,0x33,0xb3,0x18,0xc8,0xfe,0x45,0x6d,0xec,0xed,0xd0,0xb5,0x51,0xc4,0x96,0xd2,0x5c,0x26,0x9,0x35,0x4,0xf4,0x55,0x98,0x41,0x36,0x4b,0xc3,0x5f,0x2e,0x3,0x77,0x6f,0x19,0xc2,0x82,0x92,0xd7,0x8,0x8c,0x1e,0x99,0xc9,0xea,0x43,0xbb,0x71,0xa1,0x8b,0x5d,0x73,0x42,0x2f,0x68,0x97,0x3b,0xbd,0x58,0xc7,0xf,0xcd,0xbe,0xeb,0xb1,0x11,0x8a,0xff,0x3d,0x13,0xe5,0x25,0xaa,0x7b,0x32,0x30,0x93,0x7d,0xce,0xa2,0x5,0xed,0x4b,0xa4,0x8b,0xe2,0xfc,0x1d,0x79,0xd4,0x4a,0x93,0x31,0x91,0x1c,0x25,0x9d,0xb9,0x7e,0xf1,0xd7,0x6d,0xfb,0x6,0xf7,0x96,0x7d,0x84,0xa9,0xfa,0xe,0x54,0xb2,0xa,0xb5,0xd6,0x8d,0xe7,0x7a,0xe9,0xef,0x41,0x7c,0x9f,0x60,0x24,0x8,0x2c,0x5e,0x5d,0xe6,0x3c,0xa3,0x58,0x20,0xf0,0x44,0x9b,0x9c,0x51,0xeb,0x14,0x36,0x1,0x1b,0xdf,0x74,0x7,0x4f,0xd5,0xc,0x63,0x53,0xda,0x87,0x3b,0x28,0xd9,0x9a,0x6c,0x67,0x50,0xc1,0xf3,0xf,0xdd,0xd0,0xe0,0x15,0xdc,0x62,0x70,0x1e,0xb4,0x55,0xcc,0xde,0xbb,0x29,0x27,0xad,0xc9,0x47,0xb7,0x59,0x13,0x83,0x7b,0xb,0x4d,0x69,0xba,0x82,0x8f,0x97,0xaf,0xb3,0x5,0x66,0x32,0x22,0x88,0x99,0xca,0x81,0x5b,0x42,0x52,0x78,0xae,0xc5,0x2e,0x98,0x56,0x1a,0xf6,0xc2,0xd1,0xbd,0x68,0xb0,0xc4,0x61,0xc7,0x75,0x94,0xe1,0x89,0x23,0xff,0xa6,0xd,0x19,0x21,0x1f,0x4,0x45,0x2b,0x85,0x12,0xa7,0x9e,0xce,0x4e,0xe5,0x35,0x3,0xb8,0x90,0x11,0x10,0x2d,0x48,0xac,0x39,0x6b,0x2f,0xfd,0xab,0x73,0x49,0x5a,0xea,0x72,0x38,0x37,0xe8,0x26,0x3d,0xb1,0x0,0xaa,0xc3,0x92,0xe4,0x3f,0x7f,0x6f,0x2a,0xf5,0x71,0xe3,0x64,0x34,0x17,0xbe,0x46,0x8c,0x5c,0xa1,0xdb,0xf4,0xc8,0xf9,0x9,0xa8,0x65,0xbc,0xcb,0xb6,0x3e,0xa2,0xd3,0xfe,0x8a,0xec,0x77,0x2,0xc0,0xee,0x18,0xd8,0x57,0x86,0xcf,0xcd,0x6e,0x80,0x33,0x5f,0xf8,0x76,0xa0,0x8e,0xbf,0xd2,0x95,0x6a,0xc6,0x40,0xa5,0x3a,0xf2,0x30,0x43,0x16,0x4c,0xb3,0x9e,0x67,0x8c,0xa8,0x4e,0x14,0xe0,0xcd,0xeb,0x64,0xa3,0xed,0x1c,0xe1,0x77,0x2b,0x89,0x50,0xce,0x87,0x3f,0x6,0x8b,0x91,0xbe,0x51,0xf7,0x63,0x7,0xe6,0xf8,0xf1,0x4b,0x86,0x81,0x1,0x1b,0x2c,0xe,0xb9,0x26,0xfc,0x47,0x5e,0xea,0x3a,0x42,0x7a,0x85,0x66,0x5b,0x44,0x36,0x12,0x3e,0x97,0xcc,0xaf,0x10,0xf5,0xf3,0x60,0xfd,0x4,0x6a,0x78,0xc6,0xc4,0xd6,0x4f,0xae,0x15,0xe9,0xdb,0x4a,0xf,0xfa,0xca,0xc7,0x32,0x21,0x9d,0xc0,0x7d,0x76,0x80,0xc3,0x55,0x1d,0x6e,0xc5,0x49,0x79,0x16,0xcf,0x9b,0xd0,0x83,0x92,0x62,0x48,0x58,0x41,0xa9,0xb5,0x8d,0x95,0x38,0x28,0x7c,0x1f,0x11,0x61,0x99,0x9,0x98,0xa0,0x73,0x57,0xb7,0x3d,0x33,0xa1,0x43,0xad,0x5d,0xd3,0x5f,0x1e,0x5,0x3b,0xbd,0x8,0x9f,0x31,0x39,0x93,0xfb,0x8e,0x3,0x17,0xbc,0xe5,0xaa,0x72,0xa7,0xcb,0x6f,0xdd,0x7b,0xde,0x82,0x34,0xdf,0xb4,0xd8,0xec,0x0,0x4c,0x27,0x3c,0xf2,0x2d,0xd9,0xb0,0x1a,0xab,0x53,0x69,0xb1,0xe7,0x22,0x68,0xf0,0x40,0x52,0x37,0xa,0xb,0x35,0x71,0x23,0xb6,0xff,0x54,0xd4,0x84,0x8a,0xa2,0x19,0x2f,0x24,0xac,0xd1,0xa6,0x90,0xe4,0xc9,0xb8,0xd2,0xee,0xc1,0xbb,0x7f,0xb2,0x13,0xe3,0xd,0x2e,0x7e,0xf9,0x46,0x96,0x5c,0xa4,0x65,0x25,0xfe,0x88,0x6b,0xef,0x30,0x75,0xe8,0x20,0xbf,0x5a,0x56,0xc,0x59,0x2a,0xa5,0x94,0xba,0x6c,0xdc,0x70,0x8f,0xc8,0x74,0xd7,0xd5,0x9c,0xe2,0x45,0x29,0x9a,0xda,0x18,0x6d,0xf6,0x4d,0xc2,0x2,0xf4,0x7e,0x78,0x70,0xed,0x41,0x1a,0x9d,0x22,0xbb,0xc9,0xb3,0x9f,0x8,0xf7,0xd6,0xeb,0x67,0xd3,0xcf,0xb7,0xab,0x34,0xca,0x71,0x96,0x8c,0x83,0xa1,0xc6,0x7c,0xc,0xb,0x8a,0xee,0x75,0x6b,0x33,0x1c,0x7a,0xdc,0xb2,0xa,0x6,0x8b,0x4,0xa6,0x43,0xdd,0x91,0x60,0xfa,0x6c,0x66,0x40,0x2e,0xe9,0xc3,0x25,0x6d,0x99,0x13,0x3e,0x1,0xea,0x20,0xce,0x5e,0xd0,0xb0,0x3a,0x2c,0xbe,0x2d,0x15,0xda,0xfe,0xec,0x9c,0x84,0x14,0xa5,0xb5,0x92,0xf1,0x38,0x24,0x18,0x0,0xc5,0xef,0xcc,0xd5,0x5d,0x16,0x1f,0xe,0xf4,0xc4,0x42,0x9b,0x90,0xd8,0x48,0xe3,0xfb,0xf0,0x4e,0xd,0xac,0xbf,0x4d,0x10,0x77,0x82,0x4a,0x47,0x64,0x98,0xc7,0x56,0x5b,0x49,0x23,0xc2,0xe7,0x89,0x4b,0xf5,0x2f,0x7,0xa2,0x94,0xd9,0x72,0x9,0x59,0xfc,0xb8,0x3b,0xae,0xba,0xdf,0x86,0x87,0xe5,0xaf,0xcd,0x7d,0xe4,0xde,0x6a,0x3c,0x3d,0x54,0x26,0x97,0xb1,0xaa,0xa0,0x7f,0x61,0x55,0xc1,0x8d,0xb9,0xf,0x39,0x52,0x50,0xe2,0x53,0xf6,0xff,0x27,0x46,0x2a,0x9a,0x8e,0x68,0x31,0x1e,0xb4,0x3,0x76,0x85,0x30,0xbc,0x12,0x93,0xd2,0xb6,0x88,0x4f,0xc0,0x79,0x8f,0x95,0x57,0x7b,0xe0,0xc8,0x6f,0x17,0xa4,0x5a,0xf9,0x11,0x58,0xfd,0x51,0x45,0x2,0x19,0x28,0xe1,0x37,0x81,0xdb,0xa7,0xd4,0xad,0x65,0xd7,0x32,0x62,0xe6,0xf8,0xbd,0xa8,0xe8,0x5,0x73,0x1b,0xcb,0x29,0xd1,0xa3,0x80,0x74,0xf3,0x3f,0xf2,0x6e,0x9e,0x63,0x5f,0x36,0x4c,0x69,0x1d,0x35,0x44,0x21,0xa9,0x2b,0x5c,0x10,0xd7,0x58,0x7e,0xc4,0x52,0xaf,0x5e,0x3f,0xd4,0x2d,0x0,0x53,0xa7,0xfd,0x1b,0x44,0xe2,0xd,0x22,0x4b,0x55,0xb4,0xd0,0x7d,0xe3,0x3a,0x98,0x38,0xb5,0x8c,0x34,0xf4,0x4f,0x95,0xa,0xf1,0x89,0x59,0xed,0x32,0x35,0xf8,0x42,0xbd,0x9f,0xa8,0xb2,0xa3,0x1c,0x7f,0x24,0x4e,0xd3,0x40,0x46,0xe8,0xd5,0x36,0xc9,0x8d,0xa1,0x85,0xf7,0xf9,0x68,0x5a,0xa6,0x74,0x79,0x49,0xbc,0x75,0xcb,0xd9,0xb7,0x1d,0xfc,0x65,0x77,0x76,0xdd,0xae,0xe6,0x7c,0xa5,0xca,0xfa,0x73,0x2e,0x92,0x81,0x70,0x33,0xc5,0xce,0x26,0x3e,0x6,0x1a,0xac,0xcf,0x9b,0x8b,0x21,0x30,0x63,0x28,0xf2,0xeb,0xfb,0xd1,0x12,0x80,0x8e,0x4,0x60,0xee,0x1e,0xf0,0xba,0x2a,0xd2,0xa2,0xe4,0xc0,0x13,0x2b,0x3d,0x48,0x20,0x8a,0x56,0xf,0xa4,0xb0,0x88,0xb6,0xad,0xec,0x82,0x2c,0xbb,0xe,0x7,0x6c,0x87,0x31,0xff,0xb3,0x5f,0x6b,0x78,0x14,0xc1,0x19,0x6d,0xc8,0x6e,0xdc,0x54,0x2,0xda,0xe0,0xf3,0x43,0xdb,0x91,0x9e,0x41,0x8f,0x94,0x18,0xa9,0x3,0x6a,0x37,0x67,0xe7,0x4c,0x9c,0xaa,0x11,0x39,0xb8,0xb9,0x84,0xe1,0x5,0x90,0xc2,0x86,0x8,0x72,0x5d,0x61,0x50,0xa0,0x1,0xcc,0x15,0x62,0x1f,0x97,0xb,0x7a,0x57,0x23,0x3b,0x4d,0x96,0xd6,0xc6,0x83,0x5c,0xd8,0x4a,0xcd,0x9d,0xbe,0x17,0xef,0x25,0xf5,0xdf,0x9,0x27,0x16,0x7b,0x3c,0xc3,0x6f,0xe9,0xc,0x93,0x5b,0x99,0xea,0xbf,0xe5,0x45,0xde,0xab,0x69,0x47,0xb1,0x71,0xfe,0x2f,0x66,0x64,0xc7,0x29,0x9a,0xf6,0x51,0x14,0xb2,0x5d,0x72,0x1b,0x5,0xe4,0x80,0x2d,0xb3,0x6a,0xc8,0x68,0xe5,0xdc,0x64,0x40,0x87,0x8,0x2e,0x94,0x2,0xff,0xe,0x6f,0x84,0x7d,0x50,0x3,0xf7,0xad,0x4b,0xf3,0x4c,0x2f,0x74,0x1e,0x83,0x10,0x16,0xb8,0x85,0x66,0x99,0xdd,0xf1,0xd5,0xa7,0xa4,0x1f,0xc5,0x5a,0xa1,0xd9,0x9,0xbd,0x62,0x65,0xa8,0x12,0xed,0xcf,0xf8,0xe2,0x26,0x8d,0xfe,0xb6,0x2c,0xf5,0x9a,0xaa,0x23,0x7e,0xc2,0xd1,0x20,0x63,0x95,0x9e,0xa9,0x38,0xa,0xf6,0x24,0x29,0x19,0xec,0x25,0x9b,0x89,0xe7,0x4d,0xac,0x35,0x27,0x42,0xd0,0xde,0x54,0x30,0xbe,0x4e,0xa0,0xea,0x7a,0x82,0xf2,0xb4,0x90,0x43,0x7b,0x76,0x6e,0x56,0x4a,0xfc,0x9f,0xcb,0xdb,0x71,0x60,0x33,0x78,0xa2,0xbb,0xab,0x81,0x57,0x3c,0xd7,0x61,0xaf,0xe3,0xf,0x3b,0x28,0x44,0x91,0x49,0x3d,0x98,0x3e,0x8c,0x6d,0x18,0x70,0xda,0x6,0x5f,0xf4,0xe0,0xd8,0xe6,0xfd,0xbc,0xd2,0x7c,0xeb,0x5e,0x67,0x37,0xb7,0x1c,0xcc,0xfa,0x41,0x69,0xe8,0xe9,0xd4,0xb1,0x55,0xc0,0x92,0xd6,0x4,0x52,0x8a,0xb0,0xa3,0x13,0x8b,0xc1,0xce,0x11,0xdf,0xc4,0x48,0xf9,0x53,0x3a,0x6b,0x1d,0xc6,0x86,0x96,0xd3,0xc,0x88,0x1a,0x9d,0xcd,0xee,0x47,0xbf,0x75,0xa5,0x58,0x22,0xd,0x31,0x0,0xf0,0x51,0x9c,0x45,0x32,0x4f,0xc7,0x5b,0x2a,0x7,0x73,0x15,0x8e,0xfb,0x39,0x17,0xe1,0x21,0xae,0x7f,0x36,0x34,0x97,0x79,0xca,0xa6,0x1,0x8f,0x59,0x77,0x46,0x2b,0x6c,0x93,0x3f,0xb9,0x5c,0xc3,0xb,0xc9,0xba,0xef,0xb5,0xf8,0xd5,0x2c,0xc7,0xe3,0x5,0x5f,0xab,0x86,0xa0,0x2f,0xe8,0xa6,0x57,0xaa,0x3c,0x60,0xc2,0x1b,0x85,0xcc,0x74,0x4d,0xc0,0xda,0xf5,0x1a,0xbc,0x28,0x4c,0xad,0xb3,0xba,0x0,0xcd,0xca,0x4a,0x50,0x67,0x45,0xf2,0x6d,0xb7,0xc,0x15,0xa1,0x71,0x9,0x31,0xce,0x2d,0x10,0xf,0x7d,0x59,0x75,0xdc,0x87,0xe4,0x5b,0xbe,0xb8,0x2b,0xb6,0x4f,0x21,0x33,0x8d,0x8f,0x9d,0x4,0xe5,0x5e,0xa2,0x90,0x1,0x44,0xb1,0x81,0x8c,0x79,0x6a,0xd6,0x8b,0x36,0x3d,0xcb,0x88,0x1e,0x56,0x25,0x8e,0x2,0x32,0x5d,0x84,0xd0,0x9b,0xc8,0xd9,0x29,0x3,0x13,0xa,0xe2,0xfe,0xc6,0xde,0x73,0x63,0x37,0x54,0x5a,0x2a,0xd2,0x42,0xd3,0xeb,0x38,0x1c,0xfc,0x76,0x78,0xea,0x8,0xe6,0x16,0x98,0x14,0x55,0x4e,0x70,0xf6,0x43,0xd4,0x7a,0x72,0xd8,0xb0,0xc5,0x48,0x5c,0xf7,0xae,0xe1,0x39,0xec,0x80,0x24,0x96,0x30,0x95,0xc9,0x7f,0x94,0xff,0x93,0xa7,0x4b,0x7,0x6c,0x77,0xb9,0x66,0x92,0xfb,0x51,0xe0,0x18,0x22,0xfa,0xac,0x69,0x23,0xbb,0xb,0x19,0x7c,0x41,0x40,0x7e,0x3a,0x68,0xfd,0xb4,0x1f,0x9f,0xcf,0xc1,0xe9,0x52,0x64,0x6f,0xe7,0x9a,0xed,0xdb,0xaf,0x82,0xf3,0x99,0xa5,0x8a,0xf0,0x34,0xf9,0x58,0xa8,0x46,0x65,0x35,0xb2,0xd,0xdd,0x17,0xef,0x2e,0x6e,0xb5,0xc3,0x20,0xa4,0x7b,0x3e,0xa3,0x6b,0xf4,0x11,0x1d,0x47,0x12,0x61,0xee,0xdf,0xf1,0x27,0x97,0x3b,0xc4,0x83,0x3f,0x9c,0x9e,0xd7,0xa9,0xe,0x62,0xd1,0x91,0x53,0x26,0xbd,0x6,0x89,0x49,0xbf,0x32,0x34,0x3c,0xa1,0xd,0x56,0xd1,0x6e,0xf7,0x85,0xff,0xd3,0x44,0xbb,0x9a,0xa7,0x2b,0x9f,0x83,0xfb,0xe7,0x78,0x86,0x3d,0xda,0xc0,0xcf,0xed,0x8a,0x30,0x40,0x47,0xc6,0xa2,0x39,0x27,0x7f,0x50,0x36,0x90,0xfe,0x46,0x4a,0xc7,0x48,0xea,0xf,0x91,0xdd,0x2c,0xb6,0x20,0x2a,0xc,0x62,0xa5,0x8f,0x69,0x21,0xd5,0x5f,0x72,0x4d,0xa6,0x6c,0x82,0x12,0x9c,0xfc,0x76,0x60,0xf2,0x61,0x59,0x96,0xb2,0xa0,0xd0,0xc8,0x58,0xe9,0xf9,0xde,0xbd,0x74,0x68,0x54,0x4c,0x89,0xa3,0x80,0x99,0x11,0x5a,0x53,0x42,0xb8,0x88,0xe,0xd7,0xdc,0x94,0x4,0xaf,0xb7,0xbc,0x2,0x41,0xe0,0xf3,0x1,0x5c,0x3b,0xce,0x6,0xb,0x28,0xd4,0x8b,0x1a,0x17,0x5,0x6f,0x8e,0xab,0xc5,0x7,0xb9,0x63,0x4b,0xee,0xd8,0x95,0x3e,0x45,0x15,0xb0,0xf4,0x77,0xe2,0xf6,0x93,0xca,0xcb,0xa9,0xe3,0x81,0x31,0xa8,0x92,0x26,0x70,0x71,0x18,0x6a,0xdb,0xfd,0xe6,0xec,0x33,0x2d,0x19,0x8d,0xc1,0xf5,0x43,0x75,0x1e,0x1c,0xae,0x1f,0xba,0xb3,0x6b,0xa,0x66,0xd6,0xc2,0x24,0x7d,0x52,0xf8,0x4f,0x3a,0xc9,0x7c,0xf0,0x5e,0xdf,0x9e,0xfa,0xc4,0x3,0x8c,0x35,0xc3,0xd9,0x1b,0x37,0xac,0x84,0x23,0x5b,0xe8,0x16,0xb5,0x5d,0x14,0xb1,0x1d,0x9,0x4e,0x55,0x64,0xad,0x7b,0xcd,0x97,0xeb,0x98,0xe1,0x29,0x9b,0x7e,0x2e,0xaa,0xb4,0xf1,0xe4,0xa4,0x49,0x3f,0x57,0x87,0x65,0x9d,0xef,0xcc,0x38,0xbf,0x73,0xbe,0x22,0xd2,0x2f,0x13,0x7a,0x0,0x25,0x51,0x79,0x8,0x6d,0xe5,0x67,0x10,0xae,0x69,0xe6,0xc0,0x7a,0xec,0x11,0xe0,0x81,0x6a,0x93,0xbe,0xed,0x19,0x43,0xa5,0xfa,0x5c,0xb3,0x9c,0xf5,0xeb,0xa,0x6e,0xc3,0x5d,0x84,0x26,0x86,0xb,0x32,0x8a,0x4a,0xf1,0x2b,0xb4,0x4f,0x37,0xe7,0x53,0x8c,0x8b,0x46,0xfc,0x3,0x21,0x16,0xc,0x1d,0xa2,0xc1,0x9a,0xf0,0x6d,0xfe,0xf8,0x56,0x6b,0x88,0x77,0x33,0x1f,0x3b,0x49,0x47,0xd6,0xe4,0x18,0xca,0xc7,0xf7,0x2,0xcb,0x75,0x67,0x9,0xa3,0x42,0xdb,0xc9,0xc8,0x63,0x10,0x58,0xc2,0x1b,0x74,0x44,0xcd,0x90,0x2c,0x3f,0xce,0x8d,0x7b,0x70,0x98,0x80,0xb8,0xa4,0x12,0x71,0x25,0x35,0x9f,0x8e,0xdd,0x96,0x4c,0x55,0x45,0x6f,0xac,0x3e,0x30,0xba,0xde,0x50,0xa0,0x4e,0x4,0x94,0x6c,0x1c,0x5a,0x7e,0xad,0x95,0x83,0xf6,0x9e,0x34,0xe8,0xb1,0x1a,0xe,0x36,0x8,0x13,0x52,0x3c,0x92,0x5,0xb0,0xb9,0xd2,0x39,0x8f,0x41,0xd,0xe1,0xd5,0xc6,0xaa,0x7f,0xa7,0xd3,0x76,0xd0,0x62,0xea,0xbc,0x64,0x5e,0x4d,0xfd,0x65,0x2f,0x20,0xff,0x31,0x2a,0xa6,0x17,0xbd,0xd4,0x89,0xd9,0x59,0xf2,0x22,0x14,0xaf,0x87,0x6,0x7,0x3a,0x5f,0xbb,0x2e,0x7c,0x38,0xb6,0xcc,0xe3,0xdf,0xee,0x1e,0xbf,0x72,0xab,0xdc,0xa1,0x29,0xb5,0xc4,0xe9,0x9d,0x85,0xf3,0x28,0x68,0x78,0x3d,0xe2,0x66,0xf4,0x73,0x23,0x0,0xa9,0x51,0x9b,0x4b,0x61,0xb7,0x99,0xa8,0xc5,0x82,0x7d,0xd1,0x57,0xb2,0x2d,0xe5,0x27,0x54,0x1,0x5b,0xfb,0x60,0x15,0xd7,0xf9,0xf,0xcf,0x40,0x91,0xd8,0xda,0x79,0x97,0x24,0x48,0xef,0x8c,0x2a,0xc5,0xea,0x83,0x9d,0x7c,0x18,0xb5,0x2b,0xf2,0x50,0xf0,0x7d,0x44,0xfc,0xd8,0x1f,0x90,0xb6,0xc,0x9a,0x67,0x96,0xf7,0x1c,0xe5,0xc8,0x9b,0x6f,0x35,0xd3,0x6b,0xd4,0xb7,0xec,0x86,0x1b,0x88,0x8e,0x20,0x1d,0xfe,0x1,0x45,0x69,0x4d,0x3f,0x3c,0x87,0x5d,0xc2,0x39,0x41,0x91,0x25,0xfa,0xfd,0x30,0x8a,0x75,0x57,0x60,0x7a,0xbe,0x15,0x66,0x2e,0xb4,0x6d,0x2,0x32,0xbb,0xe6,0x5a,0x49,0xb8,0xfb,0xd,0x6,0x31,0xa0,0x92,0x6e,0xbc,0xb1,0x81,0x74,0xbd,0x3,0x11,0x7f,0xd5,0x34,0xad,0xbf,0xda,0x48,0x46,0xcc,0xa8,0x26,0xd6,0x38,0x72,0xe2,0x1a,0x6a,0x2c,0x8,0xdb,0xe3,0xee,0xf6,0xce,0xd2,0x64,0x7,0x53,0x43,0xe9,0xf8,0xab,0xe0,0x3a,0x23,0x33,0x19,0xcf,0xa4,0x4f,0xf9,0x37,0x7b,0x97,0xa3,0xb0,0xdc,0x9,0xd1,0xa5,0x0,0xa6,0x14,0xf5,0x80,0xe8,0x42,0x9e,0xc7,0x6c,0x78,0x40,0x7e,0x65,0x24,0x4a,0xe4,0x73,0xc6,0xff,0xaf,0x2f,0x84,0x54,0x62,0xd9,0xf1,0x70,0x71,0x4c,0x29,0xcd,0x58,0xa,0x4e,0x9c,0xca,0x12,0x28,0x3b,0x8b,0x13,0x59,0x56,0x89,0x47,0x5c,0xd0,0x61,0xcb,0xa2,0xf3,0x85,0x5e,0x1e,0xe,0x4b,0x94,0x10,0x82,0x5,0x55,0x76,0xdf,0x27,0xed,0x3d,0xc0,0xba,0x95,0xa9,0x98,0x68,0xc9,0x4,0xdd,0xaa,0xd7,0x5f,0xc3,0xb2,0x9f,0xeb,0x8d,0x16,0x63,0xa1,0x8f,0x79,0xb9,0x36,0xe7,0xae,0xac,0xf,0xe1,0x52,0x3e,0x99,0x17,0xc1,0xef,0xde,0xb3,0xf4,0xb,0xa7,0x21,0xc4,0x5b,0x93,0x51,0x22,0x77,0x2d,0x58,0x75,0x8c,0x67,0x43,0xa5,0xff,0xb,0x26,0x0,0x8f,0x48,0x6,0xf7,0xa,0x9c,0xc0,0x62,0xbb,0x25,0x6c,0xd4,0xed,0x60,0x7a,0x55,0xba,0x1c,0x88,0xec,0xd,0x13,0x1a,0xa0,0x6d,0x6a,0xea,0xf0,0xc7,0xe5,0x52,0xcd,0x17,0xac,0xb5,0x1,0xd1,0xa9,0x91,0x6e,0x8d,0xb0,0xaf,0xdd,0xf9,0xd5,0x7c,0x27,0x44,0xfb,0x1e,0x18,0x8b,0x16,0xef,0x81,0x93,0x2d,0x2f,0x3d,0xa4,0x45,0xfe,0x2,0x30,0xa1,0xe4,0x11,0x21,0x2c,0xd9,0xca,0x76,0x2b,0x96,0x9d,0x6b,0x28,0xbe,0xf6,0x85,0x2e,0xa2,0x92,0xfd,0x24,0x70,0x3b,0x68,0x79,0x89,0xa3,0xb3,0xaa,0x42,0x5e,0x66,0x7e,0xd3,0xc3,0x97,0xf4,0xfa,0x8a,0x72,0xe2,0x73,0x4b,0x98,0xbc,0x5c,0xd6,0xd8,0x4a,0xa8,0x46,0xb6,0x38,0xb4,0xf5,0xee,0xd0,0x56,0xe3,0x74,0xda,0xd2,0x78,0x10,0x65,0xe8,0xfc,0x57,0xe,0x41,0x99,0x4c,0x20,0x84,0x36,0x90,0x35,0x69,0xdf,0x34,0x5f,0x33,0x7,0xeb,0xa7,0xcc,0xd7,0x19,0xc6,0x32,0x5b,0xf1,0x40,0xb8,0x82,0x5a,0xc,0xc9,0x83,0x1b,0xab,0xb9,0xdc,0xe1,0xe0,0xde,0x9a,0xc8,0x5d,0x14,0xbf,0x3f,0x6f,0x61,0x49,0xf2,0xc4,0xcf,0x47,0x3a,0x4d,0x7b,0xf,0x22,0x53,0x39,0x5,0x2a,0x50,0x94,0x59,0xf8,0x8,0xe6,0xc5,0x95,0x12,0xad,0x7d,0xb7,0x4f,0x8e,0xce,0x15,0x63,0x80,0x4,0xdb,0x9e,0x3,0xcb,0x54,0xb1,0xbd,0xe7,0xb2,0xc1,0x4e,0x7f,0x51,0x87,0x37,0x9b,0x64,0x23,0x9f,0x3c,0x3e,0x77,0x9,0xae,0xc2,0x71,0x31,0xf3,0x86,0x1d,0xa6,0x29,0xe9,0x1f,0xf,0x9,0x1,0x9c,0x30,0x6b,0xec,0x53,0xca,0xb8,0xc2,0xee,0x79,0x86,0xa7,0x9a,0x16,0xa2,0xbe,0xc6,0xda,0x45,0xbb,0x0,0xe7,0xfd,0xf2,0xd0,0xb7,0xd,0x7d,0x7a,0xfb,0x9f,0x4,0x1a,0x42,0x6d,0xb,0xad,0xc3,0x7b,0x77,0xfa,0x75,0xd7,0x32,0xac,0xe0,0x11,0x8b,0x1d,0x17,0x31,0x5f,0x98,0xb2,0x54,0x1c,0xe8,0x62,0x4f,0x70,0x9b,0x51,0xbf,0x2f,0xa1,0xc1,0x4b,0x5d,0xcf,0x5c,0x64,0xab,0x8f,0x9d,0xed,0xf5,0x65,0xd4,0xc4,0xe3,0x80,0x49,0x55,0x69,0x71,0xb4,0x9e,0xbd,0xa4,0x2c,0x67,0x6e,0x7f,0x85,0xb5,0x33,0xea,0xe1,0xa9,0x39,0x92,0x8a,0x81,0x3f,0x7c,0xdd,0xce,0x3c,0x61,0x6,0xf3,0x3b,0x36,0x15,0xe9,0xb6,0x27,0x2a,0x38,0x52,0xb3,0x96,0xf8,0x3a,0x84,0x5e,0x76,0xd3,0xe5,0xa8,0x3,0x78,0x28,0x8d,0xc9,0x4a,0xdf,0xcb,0xae,0xf7,0xf6,0x94,0xde,0xbc,0xc,0x95,0xaf,0x1b,0x4d,0x4c,0x25,0x57,0xe6,0xc0,0xdb,0xd1,0xe,0x10,0x24,0xb0,0xfc,0xc8,0x7e,0x48,0x23,0x21,0x93,0x22,0x87,0x8e,0x56,0x37,0x5b,0xeb,0xff,0x19,0x40,0x6f,0xc5,0x72,0x7,0xf4,0x41,0xcd,0x63,0xe2,0xa3,0xc7,0xf9,0x3e,0xb1,0x8,0xfe,0xe4,0x26,0xa,0x91,0xb9,0x1e,0x66,0xd5,0x2b,0x88,0x60,0x29,0x8c,0x20,0x34,0x73,0x68,0x59,0x90,0x46,0xf0,0xaa,0xd6,0xa5,0xdc,0x14,0xa6,0x43,0x13,0x97,0x89,0xcc,0xd9,0x99,0x74,0x2,0x6a,0xba,0x58,0xa0,0xd2,0xf1,0x5,0x82,0x4e,0x83,0x1f,0xef,0x12,0x2e,0x47,0x3d,0x18,0x6c,0x44,0x35,0x50,0xd8,0x5a,0x2d}; + +unsigned char table_s2[] = {0xab,0xd,0xb5,0xc8,0x9d,0xed,0x11,0x50,0x63,0x8b,0x83,0x79,0x12,0x7c,0x8d,0x23,0xa1,0xeb,0x4c,0xe6,0xd2,0x53,0x7b,0x62,0xf0,0x14,0x28,0xae,0x9e,0xaf,0xb6,0x8c,0xc6,0xb3,0x85,0x40,0x94,0x6d,0x32,0x58,0xf7,0xf8,0x86,0xb1,0xdd,0xdb,0xfc,0x37,0x5b,0xa4,0xee,0x46,0x27,0x1a,0x41,0x93,0x89,0xdf,0x3,0x5a,0x59,0x87,0x60,0xd5,0xe5,0xb7,0xc1,0x3c,0xce,0x1f,0xc2,0x6f,0x36,0x9,0x5f,0xe0,0x10,0x52,0x4a,0x61,0x4b,0xe7,0x42,0xbe,0xa0,0xde,0x2d,0xa9,0x91,0x21,0xfe,0x39,0xb9,0x26,0x68,0xbb,0x25,0xec,0x4f,0x97,0xa,0x4e,0xfb,0x81,0x49,0x5c,0x67,0xc7,0x1c,0x17,0xb8,0xa7,0x16,0xad,0xf9,0x69,0xe4,0x3e,0x0,0xd9,0xac,0x3d,0x2e,0x47,0xd1,0x74,0x1d,0x54,0x7,0x9b,0xd3,0x56,0x29,0xdc,0xa6,0xc9,0x33,0x9c,0x2a,0xfd,0x72,0xf5,0xb,0x7d,0x22,0x80,0x96,0x4,0x3f,0xe9,0x2f,0x34,0x65,0x84,0x38,0xa3,0x2b,0xe1,0x24,0xc4,0x7f,0x57,0xc0,0xb4,0xea,0xca,0x45,0x99,0x90,0x75,0xbc,0x71,0xb0,0xf6,0x1b,0xcb,0x9f,0x6,0xff,0x98,0x20,0xd4,0xfa,0x9a,0xd0,0x8a,0x18,0x70,0xb2,0x5e,0x13,0x3a,0xe3,0x35,0x5d,0x1e,0xd8,0xd6,0xda,0x51,0x2,0x66,0x8,0xd7,0xcf,0xf1,0x30,0x2c,0x6b,0x95,0xaa,0xf2,0xba,0xc3,0xef,0x78,0xc,0x8f,0xa2,0x4d,0x44,0x43,0x7a,0x77,0x15,0xa8,0xc5,0xf3,0x5,0xf4,0xe,0xf,0x55,0x48,0xbf,0xe2,0xbd,0x92,0x7e,0x6c,0xcd,0x82,0x31,0x3b,0xcc,0x19,0x6e,0x6a,0x1,0x76,0x73,0x64,0xe8,0x8e,0xa5,0x88,0x28,0xef,0x30,0x80,0xaa,0x79,0x37,0xa8,0xaf,0x53,0xf6,0x5a,0xb8,0x3c,0xcf,0xb1,0xf1,0x4e,0x18,0x27,0x70,0x5b,0x43,0x1,0x2d,0xd0,0xa6,0xf4,0x7e,0xd3,0xe,0xdf,0x56,0x3f,0x2c,0xbd,0x45,0xc,0x65,0xc0,0x78,0xe8,0xbc,0x7,0xc8,0x11,0x2f,0xf5,0xd6,0x76,0x4d,0x58,0xb6,0xa9,0x6,0xd,0x86,0x5e,0xfd,0x34,0x90,0xea,0x5f,0x1b,0xbf,0x39,0x5,0xe1,0x9d,0xa7,0xbe,0x8f,0xf7,0x5d,0xfa,0xb0,0x73,0x6a,0x42,0xc3,0x68,0x92,0x9a,0x72,0x32,0x9c,0x6d,0x3,0xd9,0xa4,0x1c,0xba,0x41,0x0,0xfc,0x8c,0x4b,0x12,0xce,0x98,0xc4,0x71,0x96,0x48,0x57,0xff,0xb5,0x4a,0x82,0x50,0xb,0x36,0xa0,0x97,0xe9,0xe6,0x26,0xed,0xca,0xcc,0x51,0x94,0xa2,0xd7,0x49,0x23,0x7c,0x85,0x5c,0xb3,0x9e,0x1d,0x66,0x6b,0x52,0x55,0xe3,0xbb,0x84,0x7a,0x69,0xfe,0xd2,0xab,0xc6,0x19,0x77,0x13,0x3d,0x21,0xe0,0xde,0xf,0x4c,0x24,0xf2,0x40,0xcb,0xc7,0xc9,0x75,0x62,0x67,0x10,0x99,0xb4,0x9f,0xf9,0x2a,0x20,0x93,0xdc,0x7b,0x7f,0x8,0xdd,0xf3,0xae,0x59,0x44,0x7d,0x6f,0x83,0xac,0xe2,0xd4,0xb9,0x4,0x1e,0x1f,0xe5,0x14,0xb2,0x29,0x95,0x74,0xd5,0x35,0xf0,0x3a,0x15,0x87,0x91,0x33,0x25,0x3e,0xf8,0x2e,0xec,0x3b,0x8d,0x22,0x6c,0x1a,0xe4,0x63,0x47,0xc2,0x8a,0x16,0xd8,0xb7,0xcd,0x38,0x61,0x9,0x9b,0xc1,0x2b,0x2,0x4f,0xa3,0x89,0xee,0x17,0x8e,0x8b,0xeb,0xc5,0x31,0x60,0xad,0x64,0x81,0xda,0xa,0xe7,0xa1,0xa5,0xd1,0x46,0x6e,0x88,0x54,0xdb,0xfb,0x83,0x21,0x7,0x95,0xea,0x3c,0x37,0x2c,0x87,0x66,0xa0,0x3b,0xe2,0x28,0xc7,0x27,0x98,0x4,0x55,0xd0,0xdf,0x2a,0xca,0xa5,0x9f,0x30,0xfe,0x29,0xf6,0x71,0x7e,0x8,0x5,0x9c,0x9b,0xfc,0xd7,0x23,0x99,0xf9,0x89,0xd3,0x73,0x1b,0x5d,0xb1,0x39,0x10,0x54,0x7c,0xb7,0xc3,0xc9,0xe9,0x9a,0x46,0x76,0x93,0x72,0xbf,0xf5,0xb3,0xc8,0x18,0x96,0x68,0xf1,0xa9,0xc0,0xb9,0x7b,0xec,0x8c,0xf,0x4e,0xa1,0x40,0x47,0x74,0x79,0x36,0xe0,0x1d,0x5e,0xd5,0xdb,0x52,0xd9,0x65,0x1,0xd4,0xb,0xf2,0xcc,0x2f,0x33,0x81,0xce,0x38,0x32,0x1a,0xcf,0x69,0x6d,0x75,0x2,0x67,0x70,0x8d,0xeb,0x8b,0xa6,0xab,0x16,0xf0,0xc6,0xf7,0x6,0xc,0xd,0x4b,0x56,0xe1,0xbc,0x91,0xbe,0x6f,0x7d,0xe8,0xa2,0xe5,0x4f,0x50,0xd1,0x61,0x78,0x17,0xf3,0xad,0x2b,0xac,0x9d,0x8f,0xb5,0xe,0xa8,0xcb,0xb6,0xee,0x9e,0x53,0x12,0x88,0x60,0x7a,0x80,0x7f,0x11,0x20,0x8e,0xa7,0x58,0x45,0xed,0x19,0x24,0x90,0x42,0xdc,0x8a,0x59,0x0,0x84,0x5a,0xd6,0x63,0xb0,0xc5,0x43,0x86,0x6e,0x97,0x5b,0x31,0xfb,0xf4,0xb2,0x85,0xd8,0xde,0x34,0xff,0xe4,0x48,0xbd,0x41,0xdd,0xa3,0xaa,0x2e,0x22,0x92,0x3a,0xfd,0x25,0xba,0xb8,0x6b,0xb4,0xe6,0x3f,0xc2,0x1c,0xcd,0x6c,0xc1,0xa,0x35,0xe3,0x5c,0x51,0x13,0x62,0x49,0xae,0x15,0x6a,0xfa,0x3d,0xe7,0xda,0x3,0x3e,0xaf,0x44,0x2d,0x77,0xd2,0x57,0x1e,0xef,0x26,0x94,0x4c,0x4d,0x9,0x82,0xf8,0x5f,0x4a,0xc4,0x64,0x14,0x1f,0xa4,0xbb,0x47,0xbb,0x4e,0xe2,0x28,0xac,0xa5,0xdb,0xfb,0x3c,0x94,0x24,0x6d,0xbe,0xbc,0x23,0xc4,0x39,0xe0,0xb2,0xc7,0x6a,0xcb,0x1a,0x5a,0xe5,0x33,0xc,0x4f,0x64,0x15,0x57,0xfc,0x6c,0x13,0xa8,0x5,0xdc,0xe1,0x3b,0x2b,0x42,0xa9,0x38,0x18,0x51,0xd4,0x71,0x4a,0x92,0x20,0xe9,0xfe,0x84,0xf,0x4b,0x62,0xc2,0x4c,0x59,0xbd,0xa2,0x19,0x12,0x49,0xe3,0xa4,0xee,0x7e,0x67,0xd7,0x56,0x2d,0xab,0xf5,0x11,0xb3,0x89,0x9b,0xaa,0xb0,0xcd,0xae,0x8,0x14,0x55,0x98,0xe8,0x86,0x7c,0x66,0x8e,0x88,0x26,0x17,0x79,0xeb,0x43,0x5e,0xa1,0x44,0x96,0x22,0x1f,0x6,0x5f,0x8c,0xda,0x65,0xd0,0x5c,0x82,0x80,0x45,0xc3,0xb6,0x37,0x5d,0x91,0x68,0x83,0xb4,0xf2,0xfd,0xf9,0x32,0xd8,0xde,0xaf,0xf7,0x6e,0x90,0xea,0x7d,0xbf,0xc6,0xa7,0x48,0x9,0x8a,0x7f,0x72,0x41,0x46,0x58,0x1b,0xe6,0x30,0xdf,0x54,0xdd,0xd3,0xd,0xd2,0x7,0x63,0x35,0x29,0xca,0xf4,0x34,0x3e,0xc8,0x87,0x6b,0x6f,0xc9,0x1c,0x76,0x61,0x4,0x73,0xa0,0x8d,0xed,0x8b,0xc0,0xf6,0x10,0xad,0xb,0xa,0x0,0xf1,0xba,0xe7,0x50,0x4d,0x7b,0x69,0xb8,0x97,0x93,0x1,0x27,0x85,0x2a,0x31,0x3a,0xec,0x3d,0xa6,0x60,0x81,0x21,0xc1,0x2e,0xe4,0xd6,0x53,0x2,0x9e,0xa3,0xcc,0x2c,0xd9,0x2f,0xf8,0x36,0x99,0xe,0x78,0x77,0xf0,0xfa,0x9d,0x9a,0x3,0xff,0x9f,0x25,0xd1,0x1d,0x75,0xd5,0x8f,0x16,0x3f,0xb7,0x5b,0xc5,0xb1,0x7a,0x52,0x40,0x9c,0xef,0xcf,0xb9,0x74,0x95,0x70,0x1e,0xce,0xb5,0xf3,0x6c,0xca,0x72,0xf,0x5a,0x2a,0xd6,0x97,0xa4,0x4c,0x44,0xbe,0xd5,0xbb,0x4a,0xe4,0x66,0x2c,0x8b,0x21,0x15,0x94,0xbc,0xa5,0x37,0xd3,0xef,0x69,0x59,0x68,0x71,0x4b,0x1,0x74,0x42,0x87,0x53,0xaa,0xf5,0x9f,0x30,0x3f,0x41,0x76,0x1a,0x1c,0x3b,0xf0,0x9c,0x63,0x29,0x81,0xe0,0xdd,0x86,0x54,0x4e,0x18,0xc4,0x9d,0x9e,0x40,0xa7,0x12,0x22,0x70,0x6,0xfb,0x9,0xd8,0x5,0xa8,0xf1,0xce,0x98,0x27,0xd7,0x95,0x8d,0xa6,0x8c,0x20,0x85,0x79,0x67,0x19,0xea,0x6e,0x56,0xe6,0x39,0xfe,0x7e,0xe1,0xaf,0x7c,0xe2,0x2b,0x88,0x50,0xcd,0x89,0x3c,0x46,0x8e,0x9b,0xa0,0x0,0xdb,0xd0,0x7f,0x60,0xd1,0x6a,0x3e,0xae,0x23,0xf9,0xc7,0x1e,0x6b,0xfa,0xe9,0x80,0x16,0xb3,0xda,0x93,0xc0,0x5c,0x14,0x91,0xee,0x1b,0x61,0xe,0xf4,0x5b,0xed,0x3a,0xb5,0x32,0xcc,0xba,0xe5,0x47,0x51,0xc3,0xf8,0x2e,0xe8,0xf3,0xa2,0x43,0xff,0x64,0xec,0x26,0xe3,0x3,0xb8,0x90,0x7,0x73,0x2d,0xd,0x82,0x5e,0x57,0xb2,0x7b,0xb6,0x77,0x31,0xdc,0xc,0x58,0xc1,0x38,0x5f,0xe7,0x13,0x3d,0x5d,0x17,0x4d,0xdf,0xb7,0x75,0x99,0xd4,0xfd,0x24,0xf2,0x9a,0xd9,0x1f,0x11,0x1d,0x96,0xc5,0xa1,0xcf,0x10,0x8,0x36,0xf7,0xeb,0xac,0x52,0x6d,0x35,0x7d,0x4,0x28,0xbf,0xcb,0x48,0x65,0x8a,0x83,0x84,0xbd,0xb0,0xd2,0x6f,0x2,0x34,0xc2,0x33,0xc9,0xc8,0x92,0x8f,0x78,0x25,0x7a,0x55,0xb9,0xab,0xa,0x45,0xf6,0xfc,0xb,0xde,0xa9,0xad,0xc6,0xb1,0xb4,0xa3,0x2f,0x49,0x62,0x4f,0xad,0x6a,0xb5,0x5,0x2f,0xfc,0xb2,0x2d,0x2a,0xd6,0x73,0xdf,0x3d,0xb9,0x4a,0x34,0x74,0xcb,0x9d,0xa2,0xf5,0xde,0xc6,0x84,0xa8,0x55,0x23,0x71,0xfb,0x56,0x8b,0x5a,0xd3,0xba,0xa9,0x38,0xc0,0x89,0xe0,0x45,0xfd,0x6d,0x39,0x82,0x4d,0x94,0xaa,0x70,0x53,0xf3,0xc8,0xdd,0x33,0x2c,0x83,0x88,0x3,0xdb,0x78,0xb1,0x15,0x6f,0xda,0x9e,0x3a,0xbc,0x80,0x64,0x18,0x22,0x3b,0xa,0x72,0xd8,0x7f,0x35,0xf6,0xef,0xc7,0x46,0xed,0x17,0x1f,0xf7,0xb7,0x19,0xe8,0x86,0x5c,0x21,0x99,0x3f,0xc4,0x85,0x79,0x9,0xce,0x97,0x4b,0x1d,0x41,0xf4,0x13,0xcd,0xd2,0x7a,0x30,0xcf,0x7,0xd5,0x8e,0xb3,0x25,0x12,0x6c,0x63,0xa3,0x68,0x4f,0x49,0xd4,0x11,0x27,0x52,0xcc,0xa6,0xf9,0x0,0xd9,0x36,0x1b,0x98,0xe3,0xee,0xd7,0xd0,0x66,0x3e,0x1,0xff,0xec,0x7b,0x57,0x2e,0x43,0x9c,0xf2,0x96,0xb8,0xa4,0x65,0x5b,0x8a,0xc9,0xa1,0x77,0xc5,0x4e,0x42,0x4c,0xf0,0xe7,0xe2,0x95,0x1c,0x31,0x1a,0x7c,0xaf,0xa5,0x16,0x59,0xfe,0xfa,0x8d,0x58,0x76,0x2b,0xdc,0xc1,0xf8,0xea,0x6,0x29,0x67,0x51,0x3c,0x81,0x9b,0x9a,0x60,0x91,0x37,0xac,0x10,0xf1,0x50,0xb0,0x75,0xbf,0x90,0x2,0x14,0xb6,0xa0,0xbb,0x7d,0xab,0x69,0xbe,0x8,0xa7,0xe9,0x9f,0x61,0xe6,0xc2,0x47,0xf,0x93,0x5d,0x32,0x48,0xbd,0xe4,0x8c,0x1e,0x44,0xae,0x87,0xca,0x26,0xc,0x6b,0x92,0xb,0xe,0x6e,0x40,0xb4,0xe5,0x28,0xe1,0x4,0x5f,0x8f,0x62,0x24,0x20,0x54,0xc3,0xeb,0xd,0xd1,0x5e,0x7e,0x94,0x36,0x10,0x82,0xfd,0x2b,0x20,0x3b,0x90,0x71,0xb7,0x2c,0xf5,0x3f,0xd0,0x30,0x8f,0x13,0x42,0xc7,0xc8,0x3d,0xdd,0xb2,0x88,0x27,0xe9,0x3e,0xe1,0x66,0x69,0x1f,0x12,0x8b,0x8c,0xeb,0xc0,0x34,0x8e,0xee,0x9e,0xc4,0x64,0xc,0x4a,0xa6,0x2e,0x7,0x43,0x6b,0xa0,0xd4,0xde,0xfe,0x8d,0x51,0x61,0x84,0x65,0xa8,0xe2,0xa4,0xdf,0xf,0x81,0x7f,0xe6,0xbe,0xd7,0xae,0x6c,0xfb,0x9b,0x18,0x59,0xb6,0x57,0x50,0x63,0x6e,0x21,0xf7,0xa,0x49,0xc2,0xcc,0x45,0xce,0x72,0x16,0xc3,0x1c,0xe5,0xdb,0x38,0x24,0x96,0xd9,0x2f,0x25,0xd,0xd8,0x7e,0x7a,0x62,0x15,0x70,0x67,0x9a,0xfc,0x9c,0xb1,0xbc,0x1,0xe7,0xd1,0xe0,0x11,0x1b,0x1a,0x5c,0x41,0xf6,0xab,0x86,0xa9,0x78,0x6a,0xff,0xb5,0xf2,0x58,0x47,0xc6,0x76,0x6f,0x0,0xe4,0xba,0x3c,0xbb,0x8a,0x98,0xa2,0x19,0xbf,0xdc,0xa1,0xf9,0x89,0x44,0x5,0x9f,0x77,0x6d,0x97,0x68,0x6,0x37,0x99,0xb0,0x4f,0x52,0xfa,0xe,0x33,0x87,0x55,0xcb,0x9d,0x4e,0x17,0x93,0x4d,0xc1,0x74,0xa7,0xd2,0x54,0x91,0x79,0x80,0x4c,0x26,0xec,0xe3,0xa5,0x92,0xcf,0xc9,0x23,0xe8,0xf3,0x5f,0xaa,0x56,0xca,0xb4,0xbd,0x39,0x35,0x85,0x2d,0xea,0x32,0xad,0xaf,0x7c,0xa3,0xf1,0x28,0xd5,0xb,0xda,0x7b,0xd6,0x1d,0x22,0xf4,0x4b,0x46,0x4,0x75,0x5e,0xb9,0x2,0x7d,0xed,0x2a,0xf0,0xcd,0x14,0x29,0xb8,0x53,0x3a,0x60,0xc5,0x40,0x9,0xf8,0x31,0x83,0x5b,0x5a,0x1e,0x95,0xef,0x48,0x5d,0xd3,0x73,0x3,0x8,0xb3,0xac,0x8c,0x70,0x85,0x29,0xe3,0x67,0x6e,0x10,0x30,0xf7,0x5f,0xef,0xa6,0x75,0x77,0xe8,0xf,0xf2,0x2b,0x79,0xc,0xa1,0x0,0xd1,0x91,0x2e,0xf8,0xc7,0x84,0xaf,0xde,0x9c,0x37,0xa7,0xd8,0x63,0xce,0x17,0x2a,0xf0,0xe0,0x89,0x62,0xf3,0xd3,0x9a,0x1f,0xba,0x81,0x59,0xeb,0x22,0x35,0x4f,0xc4,0x80,0xa9,0x9,0x87,0x92,0x76,0x69,0xd2,0xd9,0x82,0x28,0x6f,0x25,0xb5,0xac,0x1c,0x9d,0xe6,0x60,0x3e,0xda,0x78,0x42,0x50,0x61,0x7b,0x6,0x65,0xc3,0xdf,0x9e,0x53,0x23,0x4d,0xb7,0xad,0x45,0x43,0xed,0xdc,0xb2,0x20,0x88,0x95,0x6a,0x8f,0x5d,0xe9,0xd4,0xcd,0x94,0x47,0x11,0xae,0x1b,0x97,0x49,0x4b,0x8e,0x8,0x7d,0xfc,0x96,0x5a,0xa3,0x48,0x7f,0x39,0x36,0x32,0xf9,0x13,0x15,0x64,0x3c,0xa5,0x5b,0x21,0xb6,0x74,0xd,0x6c,0x83,0xc2,0x41,0xb4,0xb9,0x8a,0x8d,0x93,0xd0,0x2d,0xfb,0x14,0x9f,0x16,0x18,0xc6,0x19,0xcc,0xa8,0xfe,0xe2,0x1,0x3f,0xff,0xf5,0x3,0x4c,0xa0,0xa4,0x2,0xd7,0xbd,0xaa,0xcf,0xb8,0x6b,0x46,0x26,0x40,0xb,0x3d,0xdb,0x66,0xc0,0xc1,0xcb,0x3a,0x71,0x2c,0x9b,0x86,0xb0,0xa2,0x73,0x5c,0x58,0xca,0xec,0x4e,0xe1,0xfa,0xf1,0x27,0xf6,0x6d,0xab,0x4a,0xea,0xa,0xe5,0x2f,0x1d,0x98,0xc9,0x55,0x68,0x7,0xe7,0x12,0xe4,0x33,0xfd,0x52,0xc5,0xb3,0xbc,0x3b,0x31,0x56,0x51,0xc8,0x34,0x54,0xee,0x1a,0xd6,0xbe,0x1e,0x44,0xdd,0xf4,0x7c,0x90,0xe,0x7a,0xb1,0x99,0x8b,0x57,0x24,0x4,0x72,0xbf,0x5e,0xbb,0xd5,0x5,0x7e,0x38,0x1,0xa7,0x1f,0x62,0x37,0x47,0xbb,0xfa,0xc9,0x21,0x29,0xd3,0xb8,0xd6,0x27,0x89,0xb,0x41,0xe6,0x4c,0x78,0xf9,0xd1,0xc8,0x5a,0xbe,0x82,0x4,0x34,0x5,0x1c,0x26,0x6c,0x19,0x2f,0xea,0x3e,0xc7,0x98,0xf2,0x5d,0x52,0x2c,0x1b,0x77,0x71,0x56,0x9d,0xf1,0xe,0x44,0xec,0x8d,0xb0,0xeb,0x39,0x23,0x75,0xa9,0xf0,0xf3,0x2d,0xca,0x7f,0x4f,0x1d,0x6b,0x96,0x64,0xb5,0x68,0xc5,0x9c,0xa3,0xf5,0x4a,0xba,0xf8,0xe0,0xcb,0xe1,0x4d,0xe8,0x14,0xa,0x74,0x87,0x3,0x3b,0x8b,0x54,0x93,0x13,0x8c,0xc2,0x11,0x8f,0x46,0xe5,0x3d,0xa0,0xe4,0x51,0x2b,0xe3,0xf6,0xcd,0x6d,0xb6,0xbd,0x12,0xd,0xbc,0x7,0x53,0xc3,0x4e,0x94,0xaa,0x73,0x6,0x97,0x84,0xed,0x7b,0xde,0xb7,0xfe,0xad,0x31,0x79,0xfc,0x83,0x76,0xc,0x63,0x99,0x36,0x80,0x57,0xd8,0x5f,0xa1,0xd7,0x88,0x2a,0x3c,0xae,0x95,0x43,0x85,0x9e,0xcf,0x2e,0x92,0x9,0x81,0x4b,0x8e,0x6e,0xd5,0xfd,0x6a,0x1e,0x40,0x60,0xef,0x33,0x3a,0xdf,0x16,0xdb,0x1a,0x5c,0xb1,0x61,0x35,0xac,0x55,0x32,0x8a,0x7e,0x50,0x30,0x7a,0x20,0xb2,0xda,0x18,0xf4,0xb9,0x90,0x49,0x9f,0xf7,0xb4,0x72,0x7c,0x70,0xfb,0xa8,0xcc,0xa2,0x7d,0x65,0x5b,0x9a,0x86,0xc1,0x3f,0x0,0x58,0x10,0x69,0x45,0xd2,0xa6,0x25,0x8,0xe7,0xee,0xe9,0xd0,0xdd,0xbf,0x2,0x6f,0x59,0xaf,0x5e,0xa4,0xa5,0xff,0xe2,0x15,0x48,0x17,0x38,0xd4,0xc6,0x67,0x28,0x9b,0x91,0x66,0xb3,0xc4,0xc0,0xab,0xdc,0xd9,0xce,0x42,0x24,0xf,0x22,0xf4,0x33,0xec,0x5c,0x76,0xa5,0xeb,0x74,0x73,0x8f,0x2a,0x86,0x64,0xe0,0x13,0x6d,0x2d,0x92,0xc4,0xfb,0xac,0x87,0x9f,0xdd,0xf1,0xc,0x7a,0x28,0xa2,0xf,0xd2,0x3,0x8a,0xe3,0xf0,0x61,0x99,0xd0,0xb9,0x1c,0xa4,0x34,0x60,0xdb,0x14,0xcd,0xf3,0x29,0xa,0xaa,0x91,0x84,0x6a,0x75,0xda,0xd1,0x5a,0x82,0x21,0xe8,0x4c,0x36,0x83,0xc7,0x63,0xe5,0xd9,0x3d,0x41,0x7b,0x62,0x53,0x2b,0x81,0x26,0x6c,0xaf,0xb6,0x9e,0x1f,0xb4,0x4e,0x46,0xae,0xee,0x40,0xb1,0xdf,0x5,0x78,0xc0,0x66,0x9d,0xdc,0x20,0x50,0x97,0xce,0x12,0x44,0x18,0xad,0x4a,0x94,0x8b,0x23,0x69,0x96,0x5e,0x8c,0xd7,0xea,0x7c,0x4b,0x35,0x3a,0xfa,0x31,0x16,0x10,0x8d,0x48,0x7e,0xb,0x95,0xff,0xa0,0x59,0x80,0x6f,0x42,0xc1,0xba,0xb7,0x8e,0x89,0x3f,0x67,0x58,0xa6,0xb5,0x22,0xe,0x77,0x1a,0xc5,0xab,0xcf,0xe1,0xfd,0x3c,0x2,0xd3,0x90,0xf8,0x2e,0x9c,0x17,0x1b,0x15,0xa9,0xbe,0xbb,0xcc,0x45,0x68,0x43,0x25,0xf6,0xfc,0x4f,0x0,0xa7,0xa3,0xd4,0x1,0x2f,0x72,0x85,0x98,0xa1,0xb3,0x5f,0x70,0x3e,0x8,0x65,0xd8,0xc2,0xc3,0x39,0xc8,0x6e,0xf5,0x49,0xa8,0x9,0xe9,0x2c,0xe6,0xc9,0x5b,0x4d,0xef,0xf9,0xe2,0x24,0xf2,0x30,0xe7,0x51,0xfe,0xb0,0xc6,0x38,0xbf,0x9b,0x1e,0x56,0xca,0x4,0x6b,0x11,0xe4,0xbd,0xd5,0x47,0x1d,0xf7,0xde,0x93,0x7f,0x55,0x32,0xcb,0x52,0x57,0x37,0x19,0xed,0xbc,0x71,0xb8,0x5d,0x6,0xd6,0x3b,0x7d,0x79,0xd,0x9a,0xb2,0x54,0x88,0x7,0x27,0x31,0x93,0xb5,0x27,0x58,0x8e,0x85,0x9e,0x35,0xd4,0x12,0x89,0x50,0x9a,0x75,0x95,0x2a,0xb6,0xe7,0x62,0x6d,0x98,0x78,0x17,0x2d,0x82,0x4c,0x9b,0x44,0xc3,0xcc,0xba,0xb7,0x2e,0x29,0x4e,0x65,0x91,0x2b,0x4b,0x3b,0x61,0xc1,0xa9,0xef,0x3,0x8b,0xa2,0xe6,0xce,0x5,0x71,0x7b,0x5b,0x28,0xf4,0xc4,0x21,0xc0,0xd,0x47,0x1,0x7a,0xaa,0x24,0xda,0x43,0x1b,0x72,0xb,0xc9,0x5e,0x3e,0xbd,0xfc,0x13,0xf2,0xf5,0xc6,0xcb,0x84,0x52,0xaf,0xec,0x67,0x69,0xe0,0x6b,0xd7,0xb3,0x66,0xb9,0x40,0x7e,0x9d,0x81,0x33,0x7c,0x8a,0x80,0xa8,0x7d,0xdb,0xdf,0xc7,0xb0,0xd5,0xc2,0x3f,0x59,0x39,0x14,0x19,0xa4,0x42,0x74,0x45,0xb4,0xbe,0xbf,0xf9,0xe4,0x53,0xe,0x23,0xc,0xdd,0xcf,0x5a,0x10,0x57,0xfd,0xe2,0x63,0xd3,0xca,0xa5,0x41,0x1f,0x99,0x1e,0x2f,0x3d,0x7,0xbc,0x1a,0x79,0x4,0x5c,0x2c,0xe1,0xa0,0x3a,0xd2,0xc8,0x32,0xcd,0xa3,0x92,0x3c,0x15,0xea,0xf7,0x5f,0xab,0x96,0x22,0xf0,0x6e,0x38,0xeb,0xb2,0x36,0xe8,0x64,0xd1,0x2,0x77,0xf1,0x34,0xdc,0x25,0xe9,0x83,0x49,0x46,0x0,0x37,0x6a,0x6c,0x86,0x4d,0x56,0xfa,0xf,0xf3,0x6f,0x11,0x18,0x9c,0x90,0x20,0x88,0x4f,0x97,0x8,0xa,0xd9,0x6,0x54,0x8d,0x70,0xae,0x7f,0xde,0x73,0xb8,0x87,0x51,0xee,0xe3,0xa1,0xd0,0xfb,0x1c,0xa7,0xd8,0x48,0x8f,0x55,0x68,0xb1,0x8c,0x1d,0xf6,0x9f,0xc5,0x60,0xe5,0xac,0x5d,0x94,0x26,0xfe,0xff,0xbb,0x30,0x4a,0xed,0xf8,0x76,0xd6,0xa6,0xad,0x16,0x9,0x66,0x9a,0x6f,0xc3,0x9,0x8d,0x84,0xfa,0xda,0x1d,0xb5,0x5,0x4c,0x9f,0x9d,0x2,0xe5,0x18,0xc1,0x93,0xe6,0x4b,0xea,0x3b,0x7b,0xc4,0x12,0x2d,0x6e,0x45,0x34,0x76,0xdd,0x4d,0x32,0x89,0x24,0xfd,0xc0,0x1a,0xa,0x63,0x88,0x19,0x39,0x70,0xf5,0x50,0x6b,0xb3,0x1,0xc8,0xdf,0xa5,0x2e,0x6a,0x43,0xe3,0x6d,0x78,0x9c,0x83,0x38,0x33,0x68,0xc2,0x85,0xcf,0x5f,0x46,0xf6,0x77,0xc,0x8a,0xd4,0x30,0x92,0xa8,0xba,0x8b,0x91,0xec,0x8f,0x29,0x35,0x74,0xb9,0xc9,0xa7,0x5d,0x47,0xaf,0xa9,0x7,0x36,0x58,0xca,0x62,0x7f,0x80,0x65,0xb7,0x3,0x3e,0x27,0x7e,0xad,0xfb,0x44,0xf1,0x7d,0xa3,0xa1,0x64,0xe2,0x97,0x16,0x7c,0xb0,0x49,0xa2,0x95,0xd3,0xdc,0xd8,0x13,0xf9,0xff,0x8e,0xd6,0x4f,0xb1,0xcb,0x5c,0x9e,0xe7,0x86,0x69,0x28,0xab,0x5e,0x53,0x60,0x67,0x79,0x3a,0xc7,0x11,0xfe,0x75,0xfc,0xf2,0x2c,0xf3,0x26,0x42,0x14,0x8,0xeb,0xd5,0x15,0x1f,0xe9,0xa6,0x4a,0x4e,0xe8,0x3d,0x57,0x40,0x25,0x52,0x81,0xac,0xcc,0xaa,0xe1,0xd7,0x31,0x8c,0x2a,0x2b,0x21,0xd0,0x9b,0xc6,0x71,0x6c,0x5a,0x48,0x99,0xb6,0xb2,0x20,0x6,0xa4,0xb,0x10,0x1b,0xcd,0x1c,0x87,0x41,0xa0,0x0,0xe0,0xf,0xc5,0xf7,0x72,0x23,0xbf,0x82,0xed,0xd,0xf8,0xe,0xd9,0x17,0xb8,0x2f,0x59,0x56,0xd1,0xdb,0xbc,0xbb,0x22,0xde,0xbe,0x4,0xf0,0x3c,0x54,0xf4,0xae,0x37,0x1e,0x96,0x7a,0xe4,0x90,0x5b,0x73,0x61,0xbd,0xce,0xee,0x98,0x55,0xb4,0x51,0x3f,0xef,0x94,0xd2,0x2b,0x8d,0x35,0x48,0x1d,0x6d,0x91,0xd0,0xe3,0xb,0x3,0xf9,0x92,0xfc,0xd,0xa3,0x21,0x6b,0xcc,0x66,0x52,0xd3,0xfb,0xe2,0x70,0x94,0xa8,0x2e,0x1e,0x2f,0x36,0xc,0x46,0x33,0x5,0xc0,0x14,0xed,0xb2,0xd8,0x77,0x78,0x6,0x31,0x5d,0x5b,0x7c,0xb7,0xdb,0x24,0x6e,0xc6,0xa7,0x9a,0xc1,0x13,0x9,0x5f,0x83,0xda,0xd9,0x7,0xe0,0x55,0x65,0x37,0x41,0xbc,0x4e,0x9f,0x42,0xef,0xb6,0x89,0xdf,0x60,0x90,0xd2,0xca,0xe1,0xcb,0x67,0xc2,0x3e,0x20,0x5e,0xad,0x29,0x11,0xa1,0x7e,0xb9,0x39,0xa6,0xe8,0x3b,0xa5,0x6c,0xcf,0x17,0x8a,0xce,0x7b,0x1,0xc9,0xdc,0xe7,0x47,0x9c,0x97,0x38,0x27,0x96,0x2d,0x79,0xe9,0x64,0xbe,0x80,0x59,0x2c,0xbd,0xae,0xc7,0x51,0xf4,0x9d,0xd4,0x87,0x1b,0x53,0xd6,0xa9,0x5c,0x26,0x49,0xb3,0x1c,0xaa,0x7d,0xf2,0x75,0x8b,0xfd,0xa2,0x0,0x16,0x84,0xbf,0x69,0xaf,0xb4,0xe5,0x4,0xb8,0x23,0xab,0x61,0xa4,0x44,0xff,0xd7,0x40,0x34,0x6a,0x4a,0xc5,0x19,0x10,0xf5,0x3c,0xf1,0x30,0x76,0x9b,0x4b,0x1f,0x86,0x7f,0x18,0xa0,0x54,0x7a,0x1a,0x50,0xa,0x98,0xf0,0x32,0xde,0x93,0xba,0x63,0xb5,0xdd,0x9e,0x58,0x56,0x5a,0xd1,0x82,0xe6,0x88,0x57,0x4f,0x71,0xb0,0xac,0xeb,0x15,0x2a,0x72,0x3a,0x43,0x6f,0xf8,0x8c,0xf,0x22,0xcd,0xc4,0xc3,0xfa,0xf7,0x95,0x28,0x45,0x73,0x85,0x74,0x8e,0x8f,0xd5,0xc8,0x3f,0x62,0x3d,0x12,0xfe,0xec,0x4d,0x2,0xb1,0xbb,0x4c,0x99,0xee,0xea,0x81,0xf6,0xf3,0xe4,0x68,0xe,0x25,0x8,0xfa,0x3d,0xe2,0x52,0x78,0xab,0xe5,0x7a,0x7d,0x81,0x24,0x88,0x6a,0xee,0x1d,0x63,0x23,0x9c,0xca,0xf5,0xa2,0x89,0x91,0xd3,0xff,0x2,0x74,0x26,0xac,0x1,0xdc,0xd,0x84,0xed,0xfe,0x6f,0x97,0xde,0xb7,0x12,0xaa,0x3a,0x6e,0xd5,0x1a,0xc3,0xfd,0x27,0x4,0xa4,0x9f,0x8a,0x64,0x7b,0xd4,0xdf,0x54,0x8c,0x2f,0xe6,0x42,0x38,0x8d,0xc9,0x6d,0xeb,0xd7,0x33,0x4f,0x75,0x6c,0x5d,0x25,0x8f,0x28,0x62,0xa1,0xb8,0x90,0x11,0xba,0x40,0x48,0xa0,0xe0,0x4e,0xbf,0xd1,0xb,0x76,0xce,0x68,0x93,0xd2,0x2e,0x5e,0x99,0xc0,0x1c,0x4a,0x16,0xa3,0x44,0x9a,0x85,0x2d,0x67,0x98,0x50,0x82,0xd9,0xe4,0x72,0x45,0x3b,0x34,0xf4,0x3f,0x18,0x1e,0x83,0x46,0x70,0x5,0x9b,0xf1,0xae,0x57,0x8e,0x61,0x4c,0xcf,0xb4,0xb9,0x80,0x87,0x31,0x69,0x56,0xa8,0xbb,0x2c,0x0,0x79,0x14,0xcb,0xa5,0xc1,0xef,0xf3,0x32,0xc,0xdd,0x9e,0xf6,0x20,0x92,0x19,0x15,0x1b,0xa7,0xb0,0xb5,0xc2,0x4b,0x66,0x4d,0x2b,0xf8,0xf2,0x41,0xe,0xa9,0xad,0xda,0xf,0x21,0x7c,0x8b,0x96,0xaf,0xbd,0x51,0x7e,0x30,0x6,0x6b,0xd6,0xcc,0xcd,0x37,0xc6,0x60,0xfb,0x47,0xa6,0x7,0xe7,0x22,0xe8,0xc7,0x55,0x43,0xe1,0xf7,0xec,0x2a,0xfc,0x3e,0xe9,0x5f,0xf0,0xbe,0xc8,0x36,0xb1,0x95,0x10,0x58,0xc4,0xa,0x65,0x1f,0xea,0xb3,0xdb,0x49,0x13,0xf9,0xd0,0x9d,0x71,0x5b,0x3c,0xc5,0x5c,0x59,0x39,0x17,0xe3,0xb2,0x7f,0xb6,0x53,0x8,0xd8,0x35,0x73,0x77,0x3,0x94,0xbc,0x5a,0x86,0x9,0x29,0x3f,0x9d,0xbb,0x29,0x56,0x80,0x8b,0x90,0x3b,0xda,0x1c,0x87,0x5e,0x94,0x7b,0x9b,0x24,0xb8,0xe9,0x6c,0x63,0x96,0x76,0x19,0x23,0x8c,0x42,0x95,0x4a,0xcd,0xc2,0xb4,0xb9,0x20,0x27,0x40,0x6b,0x9f,0x25,0x45,0x35,0x6f,0xcf,0xa7,0xe1,0xd,0x85,0xac,0xe8,0xc0,0xb,0x7f,0x75,0x55,0x26,0xfa,0xca,0x2f,0xce,0x3,0x49,0xf,0x74,0xa4,0x2a,0xd4,0x4d,0x15,0x7c,0x5,0xc7,0x50,0x30,0xb3,0xf2,0x1d,0xfc,0xfb,0xc8,0xc5,0x8a,0x5c,0xa1,0xe2,0x69,0x67,0xee,0x65,0xd9,0xbd,0x68,0xb7,0x4e,0x70,0x93,0x8f,0x3d,0x72,0x84,0x8e,0xa6,0x73,0xd5,0xd1,0xc9,0xbe,0xdb,0xcc,0x31,0x57,0x37,0x1a,0x17,0xaa,0x4c,0x7a,0x4b,0xba,0xb0,0xb1,0xf7,0xea,0x5d,0x0,0x2d,0x2,0xd3,0xc1,0x54,0x1e,0x59,0xf3,0xec,0x6d,0xdd,0xc4,0xab,0x4f,0x11,0x97,0x10,0x21,0x33,0x9,0xb2,0x14,0x77,0xa,0x52,0x22,0xef,0xae,0x34,0xdc,0xc6,0x3c,0xc3,0xad,0x9c,0x32,0x1b,0xe4,0xf9,0x51,0xa5,0x98,0x2c,0xfe,0x60,0x36,0xe5,0xbc,0x38,0xe6,0x6a,0xdf,0xc,0x79,0xff,0x3a,0xd2,0x2b,0xe7,0x8d,0x47,0x48,0xe,0x39,0x64,0x62,0x88,0x43,0x58,0xf4,0x1,0xfd,0x61,0x1f,0x16,0x92,0x9e,0x2e,0x86,0x41,0x99,0x6,0x4,0xd7,0x8,0x5a,0x83,0x7e,0xa0,0x71,0xd0,0x7d,0xb6,0x89,0x5f,0xe0,0xed,0xaf,0xde,0xf5,0x12,0xa9,0xd6,0x46,0x81,0x5b,0x66,0xbf,0x82,0x13,0xf8,0x91,0xcb,0x6e,0xeb,0xa2,0x53,0x9a,0x28,0xf0,0xf1,0xb5,0x3e,0x44,0xe3,0xf6,0x78,0xd8,0xa8,0xa3,0x18,0x7,0xef,0x13,0xe6,0x4a,0x80,0x4,0xd,0x73,0x53,0x94,0x3c,0x8c,0xc5,0x16,0x14,0x8b,0x6c,0x91,0x48,0x1a,0x6f,0xc2,0x63,0xb2,0xf2,0x4d,0x9b,0xa4,0xe7,0xcc,0xbd,0xff,0x54,0xc4,0xbb,0x0,0xad,0x74,0x49,0x93,0x83,0xea,0x1,0x90,0xb0,0xf9,0x7c,0xd9,0xe2,0x3a,0x88,0x41,0x56,0x2c,0xa7,0xe3,0xca,0x6a,0xe4,0xf1,0x15,0xa,0xb1,0xba,0xe1,0x4b,0xc,0x46,0xd6,0xcf,0x7f,0xfe,0x85,0x3,0x5d,0xb9,0x1b,0x21,0x33,0x2,0x18,0x65,0x6,0xa0,0xbc,0xfd,0x30,0x40,0x2e,0xd4,0xce,0x26,0x20,0x8e,0xbf,0xd1,0x43,0xeb,0xf6,0x9,0xec,0x3e,0x8a,0xb7,0xae,0xf7,0x24,0x72,0xcd,0x78,0xf4,0x2a,0x28,0xed,0x6b,0x1e,0x9f,0xf5,0x39,0xc0,0x2b,0x1c,0x5a,0x55,0x51,0x9a,0x70,0x76,0x7,0x5f,0xc6,0x38,0x42,0xd5,0x17,0x6e,0xf,0xe0,0xa1,0x22,0xd7,0xda,0xe9,0xee,0xf0,0xb3,0x4e,0x98,0x77,0xfc,0x75,0x7b,0xa5,0x7a,0xaf,0xcb,0x9d,0x81,0x62,0x5c,0x9c,0x96,0x60,0x2f,0xc3,0xc7,0x61,0xb4,0xde,0xc9,0xac,0xdb,0x8,0x25,0x45,0x23,0x68,0x5e,0xb8,0x5,0xa3,0xa2,0xa8,0x59,0x12,0x4f,0xf8,0xe5,0xd3,0xc1,0x10,0x3f,0x3b,0xa9,0x8f,0x2d,0x82,0x99,0x92,0x44,0x95,0xe,0xc8,0x29,0x89,0x69,0x86,0x4c,0x7e,0xfb,0xaa,0x36,0xb,0x64,0x84,0x71,0x87,0x50,0x9e,0x31,0xa6,0xd0,0xdf,0x58,0x52,0x35,0x32,0xab,0x57,0x37,0x8d,0x79,0xb5,0xdd,0x7d,0x27,0xbe,0x97,0x1f,0xf3,0x6d,0x19,0xd2,0xfa,0xe8,0x34,0x47,0x67,0x11,0xdc,0x3d,0xd8,0xb6,0x66,0x1d,0x5b,0x89,0x2f,0x97,0xea,0xbf,0xcf,0x33,0x72,0x41,0xa9,0xa1,0x5b,0x30,0x5e,0xaf,0x1,0x83,0xc9,0x6e,0xc4,0xf0,0x71,0x59,0x40,0xd2,0x36,0xa,0x8c,0xbc,0x8d,0x94,0xae,0xe4,0x91,0xa7,0x62,0xb6,0x4f,0x10,0x7a,0xd5,0xda,0xa4,0x93,0xff,0xf9,0xde,0x15,0x79,0x86,0xcc,0x64,0x5,0x38,0x63,0xb1,0xab,0xfd,0x21,0x78,0x7b,0xa5,0x42,0xf7,0xc7,0x95,0xe3,0x1e,0xec,0x3d,0xe0,0x4d,0x14,0x2b,0x7d,0xc2,0x32,0x70,0x68,0x43,0x69,0xc5,0x60,0x9c,0x82,0xfc,0xf,0x8b,0xb3,0x3,0xdc,0x1b,0x9b,0x4,0x4a,0x99,0x7,0xce,0x6d,0xb5,0x28,0x6c,0xd9,0xa3,0x6b,0x7e,0x45,0xe5,0x3e,0x35,0x9a,0x85,0x34,0x8f,0xdb,0x4b,0xc6,0x1c,0x22,0xfb,0x8e,0x1f,0xc,0x65,0xf3,0x56,0x3f,0x76,0x25,0xb9,0xf1,0x74,0xb,0xfe,0x84,0xeb,0x11,0xbe,0x8,0xdf,0x50,0xd7,0x29,0x5f,0x0,0xa2,0xb4,0x26,0x1d,0xcb,0xd,0x16,0x47,0xa6,0x1a,0x81,0x9,0xc3,0x6,0xe6,0x5d,0x75,0xe2,0x96,0xc8,0xe8,0x67,0xbb,0xb2,0x57,0x9e,0x53,0x92,0xd4,0x39,0xe9,0xbd,0x24,0xdd,0xba,0x2,0xf6,0xd8,0xb8,0xf2,0xa8,0x3a,0x52,0x90,0x7c,0x31,0x18,0xc1,0x17,0x7f,0x3c,0xfa,0xf4,0xf8,0x73,0x20,0x44,0x2a,0xf5,0xed,0xd3,0x12,0xe,0x49,0xb7,0x88,0xd0,0x98,0xe1,0xcd,0x5a,0x2e,0xad,0x80,0x6f,0x66,0x61,0x58,0x55,0x37,0x8a,0xe7,0xd1,0x27,0xd6,0x2c,0x2d,0x77,0x6a,0x9d,0xc0,0x9f,0xb0,0x5c,0x4e,0xef,0xa0,0x13,0x19,0xee,0x3b,0x4c,0x48,0x23,0x54,0x51,0x46,0xca,0xac,0x87,0xaa,0x76,0xb1,0x6e,0xde,0xf4,0x27,0x69,0xf6,0xf1,0xd,0xa8,0x4,0xe6,0x62,0x91,0xef,0xaf,0x10,0x46,0x79,0x2e,0x5,0x1d,0x5f,0x73,0x8e,0xf8,0xaa,0x20,0x8d,0x50,0x81,0x8,0x61,0x72,0xe3,0x1b,0x52,0x3b,0x9e,0x26,0xb6,0xe2,0x59,0x96,0x4f,0x71,0xab,0x88,0x28,0x13,0x6,0xe8,0xf7,0x58,0x53,0xd8,0x0,0xa3,0x6a,0xce,0xb4,0x1,0x45,0xe1,0x67,0x5b,0xbf,0xc3,0xf9,0xe0,0xd1,0xa9,0x3,0xa4,0xee,0x2d,0x34,0x1c,0x9d,0x36,0xcc,0xc4,0x2c,0x6c,0xc2,0x33,0x5d,0x87,0xfa,0x42,0xe4,0x1f,0x5e,0xa2,0xd2,0x15,0x4c,0x90,0xc6,0x9a,0x2f,0xc8,0x16,0x9,0xa1,0xeb,0x14,0xdc,0xe,0x55,0x68,0xfe,0xc9,0xb7,0xb8,0x78,0xb3,0x94,0x92,0xf,0xca,0xfc,0x89,0x17,0x7d,0x22,0xdb,0x2,0xed,0xc0,0x43,0x38,0x35,0xc,0xb,0xbd,0xe5,0xda,0x24,0x37,0xa0,0x8c,0xf5,0x98,0x47,0x29,0x4d,0x63,0x7f,0xbe,0x80,0x51,0x12,0x7a,0xac,0x1e,0x95,0x99,0x97,0x2b,0x3c,0x39,0x4e,0xc7,0xea,0xc1,0xa7,0x74,0x7e,0xcd,0x82,0x25,0x21,0x56,0x83,0xad,0xf0,0x7,0x1a,0x23,0x31,0xdd,0xf2,0xbc,0x8a,0xe7,0x5a,0x40,0x41,0xbb,0x4a,0xec,0x77,0xcb,0x2a,0x8b,0x6b,0xae,0x64,0x4b,0xd9,0xcf,0x6d,0x7b,0x60,0xa6,0x70,0xb2,0x65,0xd3,0x7c,0x32,0x44,0xba,0x3d,0x19,0x9c,0xd4,0x48,0x86,0xe9,0x93,0x66,0x3f,0x57,0xc5,0x9f,0x75,0x5c,0x11,0xfd,0xd7,0xb0,0x49,0xd0,0xd5,0xb5,0x9b,0x6f,0x3e,0xf3,0x3a,0xdf,0x84,0x54,0xb9,0xff,0xfb,0x8f,0x18,0x30,0xd6,0xa,0x85,0xa5,0x17,0xb5,0x93,0x1,0x7e,0xa8,0xa3,0xb8,0x13,0xf2,0x34,0xaf,0x76,0xbc,0x53,0xb3,0xc,0x90,0xc1,0x44,0x4b,0xbe,0x5e,0x31,0xb,0xa4,0x6a,0xbd,0x62,0xe5,0xea,0x9c,0x91,0x8,0xf,0x68,0x43,0xb7,0xd,0x6d,0x1d,0x47,0xe7,0x8f,0xc9,0x25,0xad,0x84,0xc0,0xe8,0x23,0x57,0x5d,0x7d,0xe,0xd2,0xe2,0x7,0xe6,0x2b,0x61,0x27,0x5c,0x8c,0x2,0xfc,0x65,0x3d,0x54,0x2d,0xef,0x78,0x18,0x9b,0xda,0x35,0xd4,0xd3,0xe0,0xed,0xa2,0x74,0x89,0xca,0x41,0x4f,0xc6,0x4d,0xf1,0x95,0x40,0x9f,0x66,0x58,0xbb,0xa7,0x15,0x5a,0xac,0xa6,0x8e,0x5b,0xfd,0xf9,0xe1,0x96,0xf3,0xe4,0x19,0x7f,0x1f,0x32,0x3f,0x82,0x64,0x52,0x63,0x92,0x98,0x99,0xdf,0xc2,0x75,0x28,0x5,0x2a,0xfb,0xe9,0x7c,0x36,0x71,0xdb,0xc4,0x45,0xf5,0xec,0x83,0x67,0x39,0xbf,0x38,0x9,0x1b,0x21,0x9a,0x3c,0x5f,0x22,0x7a,0xa,0xc7,0x86,0x1c,0xf4,0xee,0x14,0xeb,0x85,0xb4,0x1a,0x33,0xcc,0xd1,0x79,0x8d,0xb0,0x4,0xd6,0x48,0x1e,0xcd,0x94,0x10,0xce,0x42,0xf7,0x24,0x51,0xd7,0x12,0xfa,0x3,0xcf,0xa5,0x6f,0x60,0x26,0x11,0x4c,0x4a,0xa0,0x6b,0x70,0xdc,0x29,0xd5,0x49,0x37,0x3e,0xba,0xb6,0x6,0xae,0x69,0xb1,0x2e,0x2c,0xff,0x20,0x72,0xab,0x56,0x88,0x59,0xf8,0x55,0x9e,0xa1,0x77,0xc8,0xc5,0x87,0xf6,0xdd,0x3a,0x81,0xfe,0x6e,0xa9,0x73,0x4e,0x97,0xaa,0x3b,0xd0,0xb9,0xe3,0x46,0xc3,0x8a,0x7b,0xb2,0x0,0xd8,0xd9,0x9d,0x16,0x6c,0xcb,0xde,0x50,0xf0,0x80,0x8b,0x30,0x2f,0x34,0xc8,0x3d,0x91,0x5b,0xdf,0xd6,0xa8,0x88,0x4f,0xe7,0x57,0x1e,0xcd,0xcf,0x50,0xb7,0x4a,0x93,0xc1,0xb4,0x19,0xb8,0x69,0x29,0x96,0x40,0x7f,0x3c,0x17,0x66,0x24,0x8f,0x1f,0x60,0xdb,0x76,0xaf,0x92,0x48,0x58,0x31,0xda,0x4b,0x6b,0x22,0xa7,0x2,0x39,0xe1,0x53,0x9a,0x8d,0xf7,0x7c,0x38,0x11,0xb1,0x3f,0x2a,0xce,0xd1,0x6a,0x61,0x3a,0x90,0xd7,0x9d,0xd,0x14,0xa4,0x25,0x5e,0xd8,0x86,0x62,0xc0,0xfa,0xe8,0xd9,0xc3,0xbe,0xdd,0x7b,0x67,0x26,0xeb,0x9b,0xf5,0xf,0x15,0xfd,0xfb,0x55,0x64,0xa,0x98,0x30,0x2d,0xd2,0x37,0xe5,0x51,0x6c,0x75,0x2c,0xff,0xa9,0x16,0xa3,0x2f,0xf1,0xf3,0x36,0xb0,0xc5,0x44,0x2e,0xe2,0x1b,0xf0,0xc7,0x81,0x8e,0x8a,0x41,0xab,0xad,0xdc,0x84,0x1d,0xe3,0x99,0xe,0xcc,0xb5,0xd4,0x3b,0x7a,0xf9,0xc,0x1,0x32,0x35,0x2b,0x68,0x95,0x43,0xac,0x27,0xae,0xa0,0x7e,0xa1,0x74,0x10,0x46,0x5a,0xb9,0x87,0x47,0x4d,0xbb,0xf4,0x18,0x1c,0xba,0x6f,0x5,0x12,0x77,0x0,0xd3,0xfe,0x9e,0xf8,0xb3,0x85,0x63,0xde,0x78,0x79,0x73,0x82,0xc9,0x94,0x23,0x3e,0x8,0x1a,0xcb,0xe4,0xe0,0x72,0x54,0xf6,0x59,0x42,0x49,0x9f,0x4e,0xd5,0x13,0xf2,0x52,0xb2,0x5d,0x97,0xa5,0x20,0x71,0xed,0xd0,0xbf,0x5f,0xaa,0x5c,0x8b,0x45,0xea,0x7d,0xb,0x4,0x83,0x89,0xee,0xe9,0x70,0x8c,0xec,0x56,0xa2,0x6e,0x6,0xa6,0xfc,0x65,0x4c,0xc4,0x28,0xb6,0xc2,0x9,0x21,0x33,0xef,0x9c,0xbc,0xca,0x7,0xe6,0x3,0x6d,0xbd,0xc6,0x80,0x5b,0xfd,0x45,0x38,0x6d,0x1d,0xe1,0xa0,0x93,0x7b,0x73,0x89,0xe2,0x8c,0x7d,0xd3,0x51,0x1b,0xbc,0x16,0x22,0xa3,0x8b,0x92,0x0,0xe4,0xd8,0x5e,0x6e,0x5f,0x46,0x7c,0x36,0x43,0x75,0xb0,0x64,0x9d,0xc2,0xa8,0x7,0x8,0x76,0x41,0x2d,0x2b,0xc,0xc7,0xab,0x54,0x1e,0xb6,0xd7,0xea,0xb1,0x63,0x79,0x2f,0xf3,0xaa,0xa9,0x77,0x90,0x25,0x15,0x47,0x31,0xcc,0x3e,0xef,0x32,0x9f,0xc6,0xf9,0xaf,0x10,0xe0,0xa2,0xba,0x91,0xbb,0x17,0xb2,0x4e,0x50,0x2e,0xdd,0x59,0x61,0xd1,0xe,0xc9,0x49,0xd6,0x98,0x4b,0xd5,0x1c,0xbf,0x67,0xfa,0xbe,0xb,0x71,0xb9,0xac,0x97,0x37,0xec,0xe7,0x48,0x57,0xe6,0x5d,0x9,0x99,0x14,0xce,0xf0,0x29,0x5c,0xcd,0xde,0xb7,0x21,0x84,0xed,0xa4,0xf7,0x6b,0x23,0xa6,0xd9,0x2c,0x56,0x39,0xc3,0x6c,0xda,0xd,0x82,0x5,0xfb,0x8d,0xd2,0x70,0x66,0xf4,0xcf,0x19,0xdf,0xc4,0x95,0x74,0xc8,0x53,0xdb,0x11,0xd4,0x34,0x8f,0xa7,0x30,0x44,0x1a,0x3a,0xb5,0x69,0x60,0x85,0x4c,0x81,0x40,0x6,0xeb,0x3b,0x6f,0xf6,0xf,0x68,0xd0,0x24,0xa,0x6a,0x20,0x7a,0xe8,0x80,0x42,0xae,0xe3,0xca,0x13,0xc5,0xad,0xee,0x28,0x26,0x2a,0xa1,0xf2,0x96,0xf8,0x27,0x3f,0x1,0xc0,0xdc,0x9b,0x65,0x5a,0x2,0x4a,0x33,0x1f,0x88,0xfc,0x7f,0x52,0xbd,0xb4,0xb3,0x8a,0x87,0xe5,0x58,0x35,0x3,0xf5,0x4,0xfe,0xff,0xa5,0xb8,0x4f,0x12,0x4d,0x62,0x8e,0x9c,0x3d,0x72,0xc1,0xcb,0x3c,0xe9,0x9e,0x9a,0xf1,0x86,0x83,0x94,0x18,0x7e,0x55,0x78,0xc0,0x7,0xd8,0x68,0x42,0x91,0xdf,0x40,0x47,0xbb,0x1e,0xb2,0x50,0xd4,0x27,0x59,0x19,0xa6,0xf0,0xcf,0x98,0xb3,0xab,0xe9,0xc5,0x38,0x4e,0x1c,0x96,0x3b,0xe6,0x37,0xbe,0xd7,0xc4,0x55,0xad,0xe4,0x8d,0x28,0x90,0x0,0x54,0xef,0x20,0xf9,0xc7,0x1d,0x3e,0x9e,0xa5,0xb0,0x5e,0x41,0xee,0xe5,0x6e,0xb6,0x15,0xdc,0x78,0x2,0xb7,0xf3,0x57,0xd1,0xed,0x9,0x75,0x4f,0x56,0x67,0x1f,0xb5,0x12,0x58,0x9b,0x82,0xaa,0x2b,0x80,0x7a,0x72,0x9a,0xda,0x74,0x85,0xeb,0x31,0x4c,0xf4,0x52,0xa9,0xe8,0x14,0x64,0xa3,0xfa,0x26,0x70,0x2c,0x99,0x7e,0xa0,0xbf,0x17,0x5d,0xa2,0x6a,0xb8,0xe3,0xde,0x48,0x7f,0x1,0xe,0xce,0x5,0x22,0x24,0xb9,0x7c,0x4a,0x3f,0xa1,0xcb,0x94,0x6d,0xb4,0x5b,0x76,0xf5,0x8e,0x83,0xba,0xbd,0xb,0x53,0x6c,0x92,0x81,0x16,0x3a,0x43,0x2e,0xf1,0x9f,0xfb,0xd5,0xc9,0x8,0x36,0xe7,0xa4,0xcc,0x1a,0xa8,0x23,0x2f,0x21,0x9d,0x8a,0x8f,0xf8,0x71,0x5c,0x77,0x11,0xc2,0xc8,0x7b,0x34,0x93,0x97,0xe0,0x35,0x1b,0x46,0xb1,0xac,0x95,0x87,0x6b,0x44,0xa,0x3c,0x51,0xec,0xf6,0xf7,0xd,0xfc,0x5a,0xc1,0x7d,0x9c,0x3d,0xdd,0x18,0xd2,0xfd,0x6f,0x79,0xdb,0xcd,0xd6,0x10,0xc6,0x4,0xd3,0x65,0xca,0x84,0xf2,0xc,0x8b,0xaf,0x2a,0x62,0xfe,0x30,0x5f,0x25,0xd0,0x89,0xe1,0x73,0x29,0xc3,0xea,0xa7,0x4b,0x61,0x6,0xff,0x66,0x63,0x3,0x2d,0xd9,0x88,0x45,0x8c,0x69,0x32,0xe2,0xf,0x49,0x4d,0x39,0xae,0x86,0x60,0xbc,0x33,0x13,0xc8,0x6a,0x4c,0xde,0xa1,0x77,0x7c,0x67,0xcc,0x2d,0xeb,0x70,0xa9,0x63,0x8c,0x6c,0xd3,0x4f,0x1e,0x9b,0x94,0x61,0x81,0xee,0xd4,0x7b,0xb5,0x62,0xbd,0x3a,0x35,0x43,0x4e,0xd7,0xd0,0xb7,0x9c,0x68,0xd2,0xb2,0xc2,0x98,0x38,0x50,0x16,0xfa,0x72,0x5b,0x1f,0x37,0xfc,0x88,0x82,0xa2,0xd1,0xd,0x3d,0xd8,0x39,0xf4,0xbe,0xf8,0x83,0x53,0xdd,0x23,0xba,0xe2,0x8b,0xf2,0x30,0xa7,0xc7,0x44,0x5,0xea,0xb,0xc,0x3f,0x32,0x7d,0xab,0x56,0x15,0x9e,0x90,0x19,0x92,0x2e,0x4a,0x9f,0x40,0xb9,0x87,0x64,0x78,0xca,0x85,0x73,0x79,0x51,0x84,0x22,0x26,0x3e,0x49,0x2c,0x3b,0xc6,0xa0,0xc0,0xed,0xe0,0x5d,0xbb,0x8d,0xbc,0x4d,0x47,0x46,0x0,0x1d,0xaa,0xf7,0xda,0xf5,0x24,0x36,0xa3,0xe9,0xae,0x4,0x1b,0x9a,0x2a,0x33,0x5c,0xb8,0xe6,0x60,0xe7,0xd6,0xc4,0xfe,0x45,0xe3,0x80,0xfd,0xa5,0xd5,0x18,0x59,0xc3,0x2b,0x31,0xcb,0x34,0x5a,0x6b,0xc5,0xec,0x13,0xe,0xa6,0x52,0x6f,0xdb,0x9,0x97,0xc1,0x12,0x4b,0xcf,0x11,0x9d,0x28,0xfb,0x8e,0x8,0xcd,0x25,0xdc,0x10,0x7a,0xb0,0xbf,0xf9,0xce,0x93,0x95,0x7f,0xb4,0xaf,0x3,0xf6,0xa,0x96,0xe8,0xe1,0x65,0x69,0xd9,0x71,0xb6,0x6e,0xf1,0xf3,0x20,0xff,0xad,0x74,0x89,0x57,0x86,0x27,0x8a,0x41,0x7e,0xa8,0x17,0x1a,0x58,0x29,0x2,0xe5,0x5e,0x21,0xb1,0x76,0xac,0x91,0x48,0x75,0xe4,0xf,0x66,0x3c,0x99,0x1c,0x55,0xa4,0x6d,0xdf,0x7,0x6,0x42,0xc9,0xb3,0x14,0x1,0x8f,0x2f,0x5f,0x54,0xef,0xf0,0xee,0x12,0xe7,0x4b,0x81,0x5,0xc,0x72,0x52,0x95,0x3d,0x8d,0xc4,0x17,0x15,0x8a,0x6d,0x90,0x49,0x1b,0x6e,0xc3,0x62,0xb3,0xf3,0x4c,0x9a,0xa5,0xe6,0xcd,0xbc,0xfe,0x55,0xc5,0xba,0x1,0xac,0x75,0x48,0x92,0x82,0xeb,0x0,0x91,0xb1,0xf8,0x7d,0xd8,0xe3,0x3b,0x89,0x40,0x57,0x2d,0xa6,0xe2,0xcb,0x6b,0xe5,0xf0,0x14,0xb,0xb0,0xbb,0xe0,0x4a,0xd,0x47,0xd7,0xce,0x7e,0xff,0x84,0x2,0x5c,0xb8,0x1a,0x20,0x32,0x3,0x19,0x64,0x7,0xa1,0xbd,0xfc,0x31,0x41,0x2f,0xd5,0xcf,0x27,0x21,0x8f,0xbe,0xd0,0x42,0xea,0xf7,0x8,0xed,0x3f,0x8b,0xb6,0xaf,0xf6,0x25,0x73,0xcc,0x79,0xf5,0x2b,0x29,0xec,0x6a,0x1f,0x9e,0xf4,0x38,0xc1,0x2a,0x1d,0x5b,0x54,0x50,0x9b,0x71,0x77,0x6,0x5e,0xc7,0x39,0x43,0xd4,0x16,0x6f,0xe,0xe1,0xa0,0x23,0xd6,0xdb,0xe8,0xef,0xf1,0xb2,0x4f,0x99,0x76,0xfd,0x74,0x7a,0xa4,0x7b,0xae,0xca,0x9c,0x80,0x63,0x5d,0x9d,0x97,0x61,0x2e,0xc2,0xc6,0x60,0xb5,0xdf,0xc8,0xad,0xda,0x9,0x24,0x44,0x22,0x69,0x5f,0xb9,0x4,0xa2,0xa3,0xa9,0x58,0x13,0x4e,0xf9,0xe4,0xd2,0xc0,0x11,0x3e,0x3a,0xa8,0x8e,0x2c,0x83,0x98,0x93,0x45,0x94,0xf,0xc9,0x28,0x88,0x68,0x87,0x4d,0x7f,0xfa,0xab,0x37,0xa,0x65,0x85,0x70,0x86,0x51,0x9f,0x30,0xa7,0xd1,0xde,0x59,0x53,0x34,0x33,0xaa,0x56,0x36,0x8c,0x78,0xb4,0xdc,0x7c,0x26,0xbf,0x96,0x1e,0xf2,0x6c,0x18,0xd3,0xfb,0xe9,0x35,0x46,0x66,0x10,0xdd,0x3c,0xd9,0xb7,0x67,0x1c,0x5a,0xa7,0x1,0xb9,0xc4,0x91,0xe1,0x1d,0x5c,0x6f,0x87,0x8f,0x75,0x1e,0x70,0x81,0x2f,0xad,0xe7,0x40,0xea,0xde,0x5f,0x77,0x6e,0xfc,0x18,0x24,0xa2,0x92,0xa3,0xba,0x80,0xca,0xbf,0x89,0x4c,0x98,0x61,0x3e,0x54,0xfb,0xf4,0x8a,0xbd,0xd1,0xd7,0xf0,0x3b,0x57,0xa8,0xe2,0x4a,0x2b,0x16,0x4d,0x9f,0x85,0xd3,0xf,0x56,0x55,0x8b,0x6c,0xd9,0xe9,0xbb,0xcd,0x30,0xc2,0x13,0xce,0x63,0x3a,0x5,0x53,0xec,0x1c,0x5e,0x46,0x6d,0x47,0xeb,0x4e,0xb2,0xac,0xd2,0x21,0xa5,0x9d,0x2d,0xf2,0x35,0xb5,0x2a,0x64,0xb7,0x29,0xe0,0x43,0x9b,0x6,0x42,0xf7,0x8d,0x45,0x50,0x6b,0xcb,0x10,0x1b,0xb4,0xab,0x1a,0xa1,0xf5,0x65,0xe8,0x32,0xc,0xd5,0xa0,0x31,0x22,0x4b,0xdd,0x78,0x11,0x58,0xb,0x97,0xdf,0x5a,0x25,0xd0,0xaa,0xc5,0x3f,0x90,0x26,0xf1,0x7e,0xf9,0x7,0x71,0x2e,0x8c,0x9a,0x8,0x33,0xe5,0x23,0x38,0x69,0x88,0x34,0xaf,0x27,0xed,0x28,0xc8,0x73,0x5b,0xcc,0xb8,0xe6,0xc6,0x49,0x95,0x9c,0x79,0xb0,0x7d,0xbc,0xfa,0x17,0xc7,0x93,0xa,0xf3,0x94,0x2c,0xd8,0xf6,0x96,0xdc,0x86,0x14,0x7c,0xbe,0x52,0x1f,0x36,0xef,0x39,0x51,0x12,0xd4,0xda,0xd6,0x5d,0xe,0x6a,0x4,0xdb,0xc3,0xfd,0x3c,0x20,0x67,0x99,0xa6,0xfe,0xb6,0xcf,0xe3,0x74,0x0,0x83,0xae,0x41,0x48,0x4f,0x76,0x7b,0x19,0xa4,0xc9,0xff,0x9,0xf8,0x2,0x3,0x59,0x44,0xb3,0xee,0xb1,0x9e,0x72,0x60,0xc1,0x8e,0x3d,0x37,0xc0,0x15,0x62,0x66,0xd,0x7a,0x7f,0x68,0xe4,0x82,0xa9,0x84,0x28,0xef,0x30,0x80,0xaa,0x79,0x37,0xa8,0xaf,0x53,0xf6,0x5a,0xb8,0x3c,0xcf,0xb1,0xf1,0x4e,0x18,0x27,0x70,0x5b,0x43,0x1,0x2d,0xd0,0xa6,0xf4,0x7e,0xd3,0xe,0xdf,0x56,0x3f,0x2c,0xbd,0x45,0xc,0x65,0xc0,0x78,0xe8,0xbc,0x7,0xc8,0x11,0x2f,0xf5,0xd6,0x76,0x4d,0x58,0xb6,0xa9,0x6,0xd,0x86,0x5e,0xfd,0x34,0x90,0xea,0x5f,0x1b,0xbf,0x39,0x5,0xe1,0x9d,0xa7,0xbe,0x8f,0xf7,0x5d,0xfa,0xb0,0x73,0x6a,0x42,0xc3,0x68,0x92,0x9a,0x72,0x32,0x9c,0x6d,0x3,0xd9,0xa4,0x1c,0xba,0x41,0x0,0xfc,0x8c,0x4b,0x12,0xce,0x98,0xc4,0x71,0x96,0x48,0x57,0xff,0xb5,0x4a,0x82,0x50,0xb,0x36,0xa0,0x97,0xe9,0xe6,0x26,0xed,0xca,0xcc,0x51,0x94,0xa2,0xd7,0x49,0x23,0x7c,0x85,0x5c,0xb3,0x9e,0x1d,0x66,0x6b,0x52,0x55,0xe3,0xbb,0x84,0x7a,0x69,0xfe,0xd2,0xab,0xc6,0x19,0x77,0x13,0x3d,0x21,0xe0,0xde,0xf,0x4c,0x24,0xf2,0x40,0xcb,0xc7,0xc9,0x75,0x62,0x67,0x10,0x99,0xb4,0x9f,0xf9,0x2a,0x20,0x93,0xdc,0x7b,0x7f,0x8,0xdd,0xf3,0xae,0x59,0x44,0x7d,0x6f,0x83,0xac,0xe2,0xd4,0xb9,0x4,0x1e,0x1f,0xe5,0x14,0xb2,0x29,0x95,0x74,0xd5,0x35,0xf0,0x3a,0x15,0x87,0x91,0x33,0x25,0x3e,0xf8,0x2e,0xec,0x3b,0x8d,0x22,0x6c,0x1a,0xe4,0x63,0x47,0xc2,0x8a,0x16,0xd8,0xb7,0xcd,0x38,0x61,0x9,0x9b,0xc1,0x2b,0x2,0x4f,0xa3,0x89,0xee,0x17,0x8e,0x8b,0xeb,0xc5,0x31,0x60,0xad,0x64,0x81,0xda,0xa,0xe7,0xa1,0xa5,0xd1,0x46,0x6e,0x88,0x54,0xdb,0xfb,0x6c,0xce,0xe8,0x7a,0x5,0xd3,0xd8,0xc3,0x68,0x89,0x4f,0xd4,0xd,0xc7,0x28,0xc8,0x77,0xeb,0xba,0x3f,0x30,0xc5,0x25,0x4a,0x70,0xdf,0x11,0xc6,0x19,0x9e,0x91,0xe7,0xea,0x73,0x74,0x13,0x38,0xcc,0x76,0x16,0x66,0x3c,0x9c,0xf4,0xb2,0x5e,0xd6,0xff,0xbb,0x93,0x58,0x2c,0x26,0x6,0x75,0xa9,0x99,0x7c,0x9d,0x50,0x1a,0x5c,0x27,0xf7,0x79,0x87,0x1e,0x46,0x2f,0x56,0x94,0x3,0x63,0xe0,0xa1,0x4e,0xaf,0xa8,0x9b,0x96,0xd9,0xf,0xf2,0xb1,0x3a,0x34,0xbd,0x36,0x8a,0xee,0x3b,0xe4,0x1d,0x23,0xc0,0xdc,0x6e,0x21,0xd7,0xdd,0xf5,0x20,0x86,0x82,0x9a,0xed,0x88,0x9f,0x62,0x4,0x64,0x49,0x44,0xf9,0x1f,0x29,0x18,0xe9,0xe3,0xe2,0xa4,0xb9,0xe,0x53,0x7e,0x51,0x80,0x92,0x7,0x4d,0xa,0xa0,0xbf,0x3e,0x8e,0x97,0xf8,0x1c,0x42,0xc4,0x43,0x72,0x60,0x5a,0xe1,0x47,0x24,0x59,0x1,0x71,0xbc,0xfd,0x67,0x8f,0x95,0x6f,0x90,0xfe,0xcf,0x61,0x48,0xb7,0xaa,0x2,0xf6,0xcb,0x7f,0xad,0x33,0x65,0xb6,0xef,0x6b,0xb5,0x39,0x8c,0x5f,0x2a,0xac,0x69,0x81,0x78,0xb4,0xde,0x14,0x1b,0x5d,0x6a,0x37,0x31,0xdb,0x10,0xb,0xa7,0x52,0xae,0x32,0x4c,0x45,0xc1,0xcd,0x7d,0xd5,0x12,0xca,0x55,0x57,0x84,0x5b,0x9,0xd0,0x2d,0xf3,0x22,0x83,0x2e,0xe5,0xda,0xc,0xb3,0xbe,0xfc,0x8d,0xa6,0x41,0xfa,0x85,0x15,0xd2,0x8,0x35,0xec,0xd1,0x40,0xab,0xc2,0x98,0x3d,0xb8,0xf1,0x0,0xc9,0x7b,0xa3,0xa2,0xe6,0x6d,0x17,0xb0,0xa5,0x2b,0x8b,0xfb,0xf0,0x4b,0x54,0x29,0xd5,0x20,0x8c,0x46,0xc2,0xcb,0xb5,0x95,0x52,0xfa,0x4a,0x3,0xd0,0xd2,0x4d,0xaa,0x57,0x8e,0xdc,0xa9,0x4,0xa5,0x74,0x34,0x8b,0x5d,0x62,0x21,0xa,0x7b,0x39,0x92,0x2,0x7d,0xc6,0x6b,0xb2,0x8f,0x55,0x45,0x2c,0xc7,0x56,0x76,0x3f,0xba,0x1f,0x24,0xfc,0x4e,0x87,0x90,0xea,0x61,0x25,0xc,0xac,0x22,0x37,0xd3,0xcc,0x77,0x7c,0x27,0x8d,0xca,0x80,0x10,0x9,0xb9,0x38,0x43,0xc5,0x9b,0x7f,0xdd,0xe7,0xf5,0xc4,0xde,0xa3,0xc0,0x66,0x7a,0x3b,0xf6,0x86,0xe8,0x12,0x8,0xe0,0xe6,0x48,0x79,0x17,0x85,0x2d,0x30,0xcf,0x2a,0xf8,0x4c,0x71,0x68,0x31,0xe2,0xb4,0xb,0xbe,0x32,0xec,0xee,0x2b,0xad,0xd8,0x59,0x33,0xff,0x6,0xed,0xda,0x9c,0x93,0x97,0x5c,0xb6,0xb0,0xc1,0x99,0x0,0xfe,0x84,0x13,0xd1,0xa8,0xc9,0x26,0x67,0xe4,0x11,0x1c,0x2f,0x28,0x36,0x75,0x88,0x5e,0xb1,0x3a,0xb3,0xbd,0x63,0xbc,0x69,0xd,0x5b,0x47,0xa4,0x9a,0x5a,0x50,0xa6,0xe9,0x5,0x1,0xa7,0x72,0x18,0xf,0x6a,0x1d,0xce,0xe3,0x83,0xe5,0xae,0x98,0x7e,0xc3,0x65,0x64,0x6e,0x9f,0xd4,0x89,0x3e,0x23,0x15,0x7,0xd6,0xf9,0xfd,0x6f,0x49,0xeb,0x44,0x5f,0x54,0x82,0x53,0xc8,0xe,0xef,0x4f,0xaf,0x40,0x8a,0xb8,0x3d,0x6c,0xf0,0xcd,0xa2,0x42,0xb7,0x41,0x96,0x58,0xf7,0x60,0x16,0x19,0x9e,0x94,0xf3,0xf4,0x6d,0x91,0xf1,0x4b,0xbf,0x73,0x1b,0xbb,0xe1,0x78,0x51,0xd9,0x35,0xab,0xdf,0x14,0x3c,0x2e,0xf2,0x81,0xa1,0xd7,0x1a,0xfb,0x1e,0x70,0xa0,0xdb,0x9d,0xc8,0x6e,0xd6,0xab,0xfe,0x8e,0x72,0x33,0x0,0xe8,0xe0,0x1a,0x71,0x1f,0xee,0x40,0xc2,0x88,0x2f,0x85,0xb1,0x30,0x18,0x1,0x93,0x77,0x4b,0xcd,0xfd,0xcc,0xd5,0xef,0xa5,0xd0,0xe6,0x23,0xf7,0xe,0x51,0x3b,0x94,0x9b,0xe5,0xd2,0xbe,0xb8,0x9f,0x54,0x38,0xc7,0x8d,0x25,0x44,0x79,0x22,0xf0,0xea,0xbc,0x60,0x39,0x3a,0xe4,0x3,0xb6,0x86,0xd4,0xa2,0x5f,0xad,0x7c,0xa1,0xc,0x55,0x6a,0x3c,0x83,0x73,0x31,0x29,0x2,0x28,0x84,0x21,0xdd,0xc3,0xbd,0x4e,0xca,0xf2,0x42,0x9d,0x5a,0xda,0x45,0xb,0xd8,0x46,0x8f,0x2c,0xf4,0x69,0x2d,0x98,0xe2,0x2a,0x3f,0x4,0xa4,0x7f,0x74,0xdb,0xc4,0x75,0xce,0x9a,0xa,0x87,0x5d,0x63,0xba,0xcf,0x5e,0x4d,0x24,0xb2,0x17,0x7e,0x37,0x64,0xf8,0xb0,0x35,0x4a,0xbf,0xc5,0xaa,0x50,0xff,0x49,0x9e,0x11,0x96,0x68,0x1e,0x41,0xe3,0xf5,0x67,0x5c,0x8a,0x4c,0x57,0x6,0xe7,0x5b,0xc0,0x48,0x82,0x47,0xa7,0x1c,0x34,0xa3,0xd7,0x89,0xa9,0x26,0xfa,0xf3,0x16,0xdf,0x12,0xd3,0x95,0x78,0xa8,0xfc,0x65,0x9c,0xfb,0x43,0xb7,0x99,0xf9,0xb3,0xe9,0x7b,0x13,0xd1,0x3d,0x70,0x59,0x80,0x56,0x3e,0x7d,0xbb,0xb5,0xb9,0x32,0x61,0x5,0x6b,0xb4,0xac,0x92,0x53,0x4f,0x8,0xf6,0xc9,0x91,0xd9,0xa0,0x8c,0x1b,0x6f,0xec,0xc1,0x2e,0x27,0x20,0x19,0x14,0x76,0xcb,0xa6,0x90,0x66,0x97,0x6d,0x6c,0x36,0x2b,0xdc,0x81,0xde,0xf1,0x1d,0xf,0xae,0xe1,0x52,0x58,0xaf,0x7a,0xd,0x9,0x62,0x15,0x10,0x7,0x8b,0xed,0xc6,0xeb,0x21,0xe6,0x39,0x89,0xa3,0x70,0x3e,0xa1,0xa6,0x5a,0xff,0x53,0xb1,0x35,0xc6,0xb8,0xf8,0x47,0x11,0x2e,0x79,0x52,0x4a,0x8,0x24,0xd9,0xaf,0xfd,0x77,0xda,0x7,0xd6,0x5f,0x36,0x25,0xb4,0x4c,0x5,0x6c,0xc9,0x71,0xe1,0xb5,0xe,0xc1,0x18,0x26,0xfc,0xdf,0x7f,0x44,0x51,0xbf,0xa0,0xf,0x4,0x8f,0x57,0xf4,0x3d,0x99,0xe3,0x56,0x12,0xb6,0x30,0xc,0xe8,0x94,0xae,0xb7,0x86,0xfe,0x54,0xf3,0xb9,0x7a,0x63,0x4b,0xca,0x61,0x9b,0x93,0x7b,0x3b,0x95,0x64,0xa,0xd0,0xad,0x15,0xb3,0x48,0x9,0xf5,0x85,0x42,0x1b,0xc7,0x91,0xcd,0x78,0x9f,0x41,0x5e,0xf6,0xbc,0x43,0x8b,0x59,0x2,0x3f,0xa9,0x9e,0xe0,0xef,0x2f,0xe4,0xc3,0xc5,0x58,0x9d,0xab,0xde,0x40,0x2a,0x75,0x8c,0x55,0xba,0x97,0x14,0x6f,0x62,0x5b,0x5c,0xea,0xb2,0x8d,0x73,0x60,0xf7,0xdb,0xa2,0xcf,0x10,0x7e,0x1a,0x34,0x28,0xe9,0xd7,0x6,0x45,0x2d,0xfb,0x49,0xc2,0xce,0xc0,0x7c,0x6b,0x6e,0x19,0x90,0xbd,0x96,0xf0,0x23,0x29,0x9a,0xd5,0x72,0x76,0x1,0xd4,0xfa,0xa7,0x50,0x4d,0x74,0x66,0x8a,0xa5,0xeb,0xdd,0xb0,0xd,0x17,0x16,0xec,0x1d,0xbb,0x20,0x9c,0x7d,0xdc,0x3c,0xf9,0x33,0x1c,0x8e,0x98,0x3a,0x2c,0x37,0xf1,0x27,0xe5,0x32,0x84,0x2b,0x65,0x13,0xed,0x6a,0x4e,0xcb,0x83,0x1f,0xd1,0xbe,0xc4,0x31,0x68,0x0,0x92,0xc8,0x22,0xb,0x46,0xaa,0x80,0xe7,0x1e,0x87,0x82,0xe2,0xcc,0x38,0x69,0xa4,0x6d,0x88,0xd3,0x3,0xee,0xa8,0xac,0xd8,0x4f,0x67,0x81,0x5d,0xd2,0xf2,0xe9,0x4b,0x6d,0xff,0x80,0x56,0x5d,0x46,0xed,0xc,0xca,0x51,0x88,0x42,0xad,0x4d,0xf2,0x6e,0x3f,0xba,0xb5,0x40,0xa0,0xcf,0xf5,0x5a,0x94,0x43,0x9c,0x1b,0x14,0x62,0x6f,0xf6,0xf1,0x96,0xbd,0x49,0xf3,0x93,0xe3,0xb9,0x19,0x71,0x37,0xdb,0x53,0x7a,0x3e,0x16,0xdd,0xa9,0xa3,0x83,0xf0,0x2c,0x1c,0xf9,0x18,0xd5,0x9f,0xd9,0xa2,0x72,0xfc,0x2,0x9b,0xc3,0xaa,0xd3,0x11,0x86,0xe6,0x65,0x24,0xcb,0x2a,0x2d,0x1e,0x13,0x5c,0x8a,0x77,0x34,0xbf,0xb1,0x38,0xb3,0xf,0x6b,0xbe,0x61,0x98,0xa6,0x45,0x59,0xeb,0xa4,0x52,0x58,0x70,0xa5,0x3,0x7,0x1f,0x68,0xd,0x1a,0xe7,0x81,0xe1,0xcc,0xc1,0x7c,0x9a,0xac,0x9d,0x6c,0x66,0x67,0x21,0x3c,0x8b,0xd6,0xfb,0xd4,0x5,0x17,0x82,0xc8,0x8f,0x25,0x3a,0xbb,0xb,0x12,0x7d,0x99,0xc7,0x41,0xc6,0xf7,0xe5,0xdf,0x64,0xc2,0xa1,0xdc,0x84,0xf4,0x39,0x78,0xe2,0xa,0x10,0xea,0x15,0x7b,0x4a,0xe4,0xcd,0x32,0x2f,0x87,0x73,0x4e,0xfa,0x28,0xb6,0xe0,0x33,0x6a,0xee,0x30,0xbc,0x9,0xda,0xaf,0x29,0xec,0x4,0xfd,0x31,0x5b,0x91,0x9e,0xd8,0xef,0xb2,0xb4,0x5e,0x95,0x8e,0x22,0xd7,0x2b,0xb7,0xc9,0xc0,0x44,0x48,0xf8,0x50,0x97,0x4f,0xd0,0xd2,0x1,0xde,0x8c,0x55,0xa8,0x76,0xa7,0x6,0xab,0x60,0x5f,0x89,0x36,0x3b,0x79,0x8,0x23,0xc4,0x7f,0x0,0x90,0x57,0x8d,0xb0,0x69,0x54,0xc5,0x2e,0x47,0x1d,0xb8,0x3d,0x74,0x85,0x4c,0xfe,0x26,0x27,0x63,0xe8,0x92,0x35,0x20,0xae,0xe,0x7e,0x75,0xce,0xd1,0x8e,0x72,0x87,0x2b,0xe1,0x65,0x6c,0x12,0x32,0xf5,0x5d,0xed,0xa4,0x77,0x75,0xea,0xd,0xf0,0x29,0x7b,0xe,0xa3,0x2,0xd3,0x93,0x2c,0xfa,0xc5,0x86,0xad,0xdc,0x9e,0x35,0xa5,0xda,0x61,0xcc,0x15,0x28,0xf2,0xe2,0x8b,0x60,0xf1,0xd1,0x98,0x1d,0xb8,0x83,0x5b,0xe9,0x20,0x37,0x4d,0xc6,0x82,0xab,0xb,0x85,0x90,0x74,0x6b,0xd0,0xdb,0x80,0x2a,0x6d,0x27,0xb7,0xae,0x1e,0x9f,0xe4,0x62,0x3c,0xd8,0x7a,0x40,0x52,0x63,0x79,0x4,0x67,0xc1,0xdd,0x9c,0x51,0x21,0x4f,0xb5,0xaf,0x47,0x41,0xef,0xde,0xb0,0x22,0x8a,0x97,0x68,0x8d,0x5f,0xeb,0xd6,0xcf,0x96,0x45,0x13,0xac,0x19,0x95,0x4b,0x49,0x8c,0xa,0x7f,0xfe,0x94,0x58,0xa1,0x4a,0x7d,0x3b,0x34,0x30,0xfb,0x11,0x17,0x66,0x3e,0xa7,0x59,0x23,0xb4,0x76,0xf,0x6e,0x81,0xc0,0x43,0xb6,0xbb,0x88,0x8f,0x91,0xd2,0x2f,0xf9,0x16,0x9d,0x14,0x1a,0xc4,0x1b,0xce,0xaa,0xfc,0xe0,0x3,0x3d,0xfd,0xf7,0x1,0x4e,0xa2,0xa6,0x0,0xd5,0xbf,0xa8,0xcd,0xba,0x69,0x44,0x24,0x42,0x9,0x3f,0xd9,0x64,0xc2,0xc3,0xc9,0x38,0x73,0x2e,0x99,0x84,0xb2,0xa0,0x71,0x5e,0x5a,0xc8,0xee,0x4c,0xe3,0xf8,0xf3,0x25,0xf4,0x6f,0xa9,0x48,0xe8,0x8,0xe7,0x2d,0x1f,0x9a,0xcb,0x57,0x6a,0x5,0xe5,0x10,0xe6,0x31,0xff,0x50,0xc7,0xb1,0xbe,0x39,0x33,0x54,0x53,0xca,0x36,0x56,0xec,0x18,0xd4,0xbc,0x1c,0x46,0xdf,0xf6,0x7e,0x92,0xc,0x78,0xb3,0x9b,0x89,0x55,0x26,0x6,0x70,0xbd,0x5c,0xb9,0xd7,0x7,0x7c,0x3a,0xfa,0x3b,0x87,0x55,0xc6,0x13,0xdc,0x89,0xb8,0xc7,0x67,0xc9,0xbc,0x82,0x28,0x44,0xaf,0xdf,0x77,0xd4,0x5,0x49,0xf6,0x7d,0x7,0xd9,0xca,0xf9,0xe1,0x8,0x29,0x4,0xae,0xd8,0x65,0xad,0x63,0xc2,0x23,0x61,0x7f,0x1f,0x7e,0x2,0x7c,0x79,0xcf,0x15,0x60,0xf5,0x4b,0xa7,0x58,0x11,0x2a,0x9d,0x39,0xc,0x56,0xa3,0x12,0xbf,0x69,0x9a,0x6a,0x7b,0x2d,0x27,0x34,0xcb,0x99,0xf4,0x3e,0x80,0xe,0xd1,0x90,0x78,0xe6,0xe4,0x76,0xa0,0x6e,0xf3,0x2b,0x33,0x41,0x3c,0x3d,0x4a,0x83,0x73,0x50,0xa6,0x42,0x1b,0xb4,0xb,0x96,0x18,0x48,0x3,0xe8,0x47,0x5d,0xcd,0xaa,0x86,0x16,0xbd,0x62,0xfb,0xbb,0x32,0x6f,0xfe,0xe7,0x8f,0x1d,0xff,0xb7,0xa4,0xd,0x5f,0xd7,0x52,0x94,0x6c,0x71,0xc8,0xb2,0x25,0x6d,0xec,0xd6,0xa5,0x35,0xa1,0xd2,0xc0,0x46,0x40,0x1c,0x20,0xbe,0x4f,0xee,0xb9,0xf0,0xea,0xba,0xd0,0x38,0x3f,0x8a,0x2e,0xf,0x21,0x8c,0xb6,0x5c,0x43,0xd5,0xf2,0x68,0x4d,0xf7,0xb1,0x57,0xd3,0xfc,0xda,0xc5,0xfd,0x8e,0x66,0xab,0xe3,0x17,0x88,0x72,0x92,0xac,0x3a,0x1,0x8d,0xc1,0x85,0x24,0xf8,0xf1,0xef,0x59,0x97,0x64,0xce,0x8b,0x75,0x9b,0x5b,0x51,0xa,0x37,0x0,0xde,0xc3,0x81,0x7a,0x9e,0xeb,0xe0,0x2c,0x9c,0x1e,0x9f,0xa9,0x6,0xe2,0xcc,0x70,0x5e,0x26,0xb5,0x5a,0x9,0xc4,0xed,0xdd,0xa2,0x1a,0x91,0x84,0xb0,0x22,0x4e,0x2f,0x10,0x98,0x45,0xe9,0xe5,0x4c,0x74,0x95,0x93,0x54,0xdb,0x36,0xa8,0x30,0x31,0x19,0x6b,0xb3,0x53,0x14,0x43,0x84,0x5b,0xeb,0xc1,0x12,0x5c,0xc3,0xc4,0x38,0x9d,0x31,0xd3,0x57,0xa4,0xda,0x9a,0x25,0x73,0x4c,0x1b,0x30,0x28,0x6a,0x46,0xbb,0xcd,0x9f,0x15,0xb8,0x65,0xb4,0x3d,0x54,0x47,0xd6,0x2e,0x67,0xe,0xab,0x13,0x83,0xd7,0x6c,0xa3,0x7a,0x44,0x9e,0xbd,0x1d,0x26,0x33,0xdd,0xc2,0x6d,0x66,0xed,0x35,0x96,0x5f,0xfb,0x81,0x34,0x70,0xd4,0x52,0x6e,0x8a,0xf6,0xcc,0xd5,0xe4,0x9c,0x36,0x91,0xdb,0x18,0x1,0x29,0xa8,0x3,0xf9,0xf1,0x19,0x59,0xf7,0x6,0x68,0xb2,0xcf,0x77,0xd1,0x2a,0x6b,0x97,0xe7,0x20,0x79,0xa5,0xf3,0xaf,0x1a,0xfd,0x23,0x3c,0x94,0xde,0x21,0xe9,0x3b,0x60,0x5d,0xcb,0xfc,0x82,0x8d,0x4d,0x86,0xa1,0xa7,0x3a,0xff,0xc9,0xbc,0x22,0x48,0x17,0xee,0x37,0xd8,0xf5,0x76,0xd,0x0,0x39,0x3e,0x88,0xd0,0xef,0x11,0x2,0x95,0xb9,0xc0,0xad,0x72,0x1c,0x78,0x56,0x4a,0x8b,0xb5,0x64,0x27,0x4f,0x99,0x2b,0xa0,0xac,0xa2,0x1e,0x9,0xc,0x7b,0xf2,0xdf,0xf4,0x92,0x41,0x4b,0xf8,0xb7,0x10,0x14,0x63,0xb6,0x98,0xc5,0x32,0x2f,0x16,0x4,0xe8,0xc7,0x89,0xbf,0xd2,0x6f,0x75,0x74,0x8e,0x7f,0xd9,0x42,0xfe,0x1f,0xbe,0x5e,0x9b,0x51,0x7e,0xec,0xfa,0x58,0x4e,0x55,0x93,0x45,0x87,0x50,0xe6,0x49,0x7,0x71,0x8f,0x8,0x2c,0xa9,0xe1,0x7d,0xb3,0xdc,0xa6,0x53,0xa,0x62,0xf0,0xaa,0x40,0x69,0x24,0xc8,0xe2,0x85,0x7c,0xe5,0xe0,0x80,0xae,0x5a,0xb,0xc6,0xf,0xea,0xb1,0x61,0x8c,0xca,0xce,0xba,0x2d,0x5,0xe3,0x3f,0xb0,0x90,0xc2,0x60,0x46,0xd4,0xab,0x7d,0x76,0x6d,0xc6,0x27,0xe1,0x7a,0xa3,0x69,0x86,0x66,0xd9,0x45,0x14,0x91,0x9e,0x6b,0x8b,0xe4,0xde,0x71,0xbf,0x68,0xb7,0x30,0x3f,0x49,0x44,0xdd,0xda,0xbd,0x96,0x62,0xd8,0xb8,0xc8,0x92,0x32,0x5a,0x1c,0xf0,0x78,0x51,0x15,0x3d,0xf6,0x82,0x88,0xa8,0xdb,0x7,0x37,0xd2,0x33,0xfe,0xb4,0xf2,0x89,0x59,0xd7,0x29,0xb0,0xe8,0x81,0xf8,0x3a,0xad,0xcd,0x4e,0xf,0xe0,0x1,0x6,0x35,0x38,0x77,0xa1,0x5c,0x1f,0x94,0x9a,0x13,0x98,0x24,0x40,0x95,0x4a,0xb3,0x8d,0x6e,0x72,0xc0,0x8f,0x79,0x73,0x5b,0x8e,0x28,0x2c,0x34,0x43,0x26,0x31,0xcc,0xaa,0xca,0xe7,0xea,0x57,0xb1,0x87,0xb6,0x47,0x4d,0x4c,0xa,0x17,0xa0,0xfd,0xd0,0xff,0x2e,0x3c,0xa9,0xe3,0xa4,0xe,0x11,0x90,0x20,0x39,0x56,0xb2,0xec,0x6a,0xed,0xdc,0xce,0xf4,0x4f,0xe9,0x8a,0xf7,0xaf,0xdf,0x12,0x53,0xc9,0x21,0x3b,0xc1,0x3e,0x50,0x61,0xcf,0xe6,0x19,0x4,0xac,0x58,0x65,0xd1,0x3,0x9d,0xcb,0x18,0x41,0xc5,0x1b,0x97,0x22,0xf1,0x84,0x2,0xc7,0x2f,0xd6,0x1a,0x70,0xba,0xb5,0xf3,0xc4,0x99,0x9f,0x75,0xbe,0xa5,0x9,0xfc,0x0,0x9c,0xe2,0xeb,0x6f,0x63,0xd3,0x7b,0xbc,0x64,0xfb,0xf9,0x2a,0xf5,0xa7,0x7e,0x83,0x5d,0x8c,0x2d,0x80,0x4b,0x74,0xa2,0x1d,0x10,0x52,0x23,0x8,0xef,0x54,0x2b,0xbb,0x7c,0xa6,0x9b,0x42,0x7f,0xee,0x5,0x6c,0x36,0x93,0x16,0x5f,0xae,0x67,0xd5,0xd,0xc,0x48,0xc3,0xb9,0x1e,0xb,0x85,0x25,0x55,0x5e,0xe5,0xfa,0xd5,0xa8,0xbf,0xa7,0xfa,0x67,0xe2,0x34,0xd6,0x8f,0xc4,0x32,0x17,0xe7,0xa9,0xde,0xd,0x60,0xa0,0x5f,0xb9,0xb3,0xfe,0xef,0x72,0x70,0x4,0xec,0x9a,0x45,0xaa,0x14,0x89,0x6b,0x73,0x1b,0xfb,0x6a,0x2f,0xa6,0x0,0xf8,0x43,0xc6,0x99,0xcb,0x23,0x30,0x7c,0xd3,0xdc,0x97,0x2,0x8c,0x20,0x9f,0xf6,0x6f,0x82,0x29,0x3e,0x12,0xc9,0x59,0x62,0xe9,0x91,0xdd,0xe3,0x40,0x3b,0x4b,0xbd,0x90,0x75,0x9c,0x5e,0x6d,0x93,0x4d,0x48,0x1d,0x52,0x87,0x13,0xc1,0x6e,0xaf,0xbc,0xd0,0x28,0x16,0xf3,0x5d,0x2c,0x53,0xbe,0x9,0xcc,0x85,0xdf,0x33,0xf4,0x61,0xfd,0xe,0x86,0x2b,0xc2,0x37,0xad,0x98,0xb7,0xf5,0xf7,0x56,0xf1,0x39,0x3a,0x4c,0x5b,0x81,0xe8,0xed,0xea,0x96,0xeb,0x8b,0xb,0x3d,0x8,0x8a,0x74,0xb8,0xa,0x7f,0x21,0xce,0xca,0xb2,0x58,0xe4,0x92,0x76,0xf,0xcf,0x1f,0xe1,0xf0,0x5a,0xcd,0x3,0x15,0xee,0x4a,0x57,0xa3,0x94,0xc5,0x9e,0x4f,0xa2,0x7,0xc0,0xe0,0x1,0x71,0xd8,0xc7,0x80,0xff,0x27,0xa5,0x8d,0x3c,0xa4,0x5,0x10,0x36,0x8e,0x79,0x49,0x9d,0x50,0xd1,0x7d,0x84,0xc,0xda,0xbb,0x24,0xb6,0x2e,0x44,0x64,0x7e,0x7a,0x2d,0x2a,0xdb,0x18,0x22,0x9b,0xb5,0x1e,0xba,0xac,0xab,0x42,0x31,0xf9,0x78,0x26,0xb1,0xe5,0x5c,0x88,0xb4,0xd2,0xd4,0x46,0x54,0xa1,0x35,0x38,0xae,0xe6,0x6,0x83,0x1c,0x3f,0x77,0x65,0x7b,0xb0,0x6c,0x55,0x11,0x95,0x19,0x63,0x25,0xfc,0xd9,0x41,0x66,0xc8,0xd7,0x1a,0xf2,0x51,0x69,0x68,0x4e,0xc3,0x47,0x44,0xe2,0x5a,0x27,0x72,0x2,0xfe,0xbf,0x8c,0x64,0x6c,0x96,0xfd,0x93,0x62,0xcc,0x4e,0x4,0xa3,0x9,0x3d,0xbc,0x94,0x8d,0x1f,0xfb,0xc7,0x41,0x71,0x40,0x59,0x63,0x29,0x5c,0x6a,0xaf,0x7b,0x82,0xdd,0xb7,0x18,0x17,0x69,0x5e,0x32,0x34,0x13,0xd8,0xb4,0x4b,0x1,0xa9,0xc8,0xf5,0xae,0x7c,0x66,0x30,0xec,0xb5,0xb6,0x68,0x8f,0x3a,0xa,0x58,0x2e,0xd3,0x21,0xf0,0x2d,0x80,0xd9,0xe6,0xb0,0xf,0xff,0xbd,0xa5,0x8e,0xa4,0x8,0xad,0x51,0x4f,0x31,0xc2,0x46,0x7e,0xce,0x11,0xd6,0x56,0xc9,0x87,0x54,0xca,0x3,0xa0,0x78,0xe5,0xa1,0x14,0x6e,0xa6,0xb3,0x88,0x28,0xf3,0xf8,0x57,0x48,0xf9,0x42,0x16,0x86,0xb,0xd1,0xef,0x36,0x43,0xd2,0xc1,0xa8,0x3e,0x9b,0xf2,0xbb,0xe8,0x74,0x3c,0xb9,0xc6,0x33,0x49,0x26,0xdc,0x73,0xc5,0x12,0x9d,0x1a,0xe4,0x92,0xcd,0x6f,0x79,0xeb,0xd0,0x6,0xc0,0xdb,0x8a,0x6b,0xd7,0x4c,0xc4,0xe,0xcb,0x2b,0x90,0xb8,0x2f,0x5b,0x5,0x25,0xaa,0x76,0x7f,0x9a,0x53,0x9e,0x5f,0x19,0xf4,0x24,0x70,0xe9,0x10,0x77,0xcf,0x3b,0x15,0x75,0x3f,0x65,0xf7,0x9f,0x5d,0xb1,0xfc,0xd5,0xc,0xda,0xb2,0xf1,0x37,0x39,0x35,0xbe,0xed,0x89,0xe7,0x38,0x20,0x1e,0xdf,0xc3,0x84,0x7a,0x45,0x1d,0x55,0x2c,0x0,0x97,0xe3,0x60,0x4d,0xa2,0xab,0xac,0x95,0x98,0xfa,0x47,0x2a,0x1c,0xea,0x1b,0xe1,0xe0,0xba,0xa7,0x50,0xd,0x52,0x7d,0x91,0x83,0x22,0x6d,0xde,0xd4,0x23,0xf6,0x81,0x85,0xee,0x99,0x9c,0x8b,0x7,0x61,0x4a,0x67,0xd0,0x17,0xc8,0x78,0x52,0x81,0xcf,0x50,0x57,0xab,0xe,0xa2,0x40,0xc4,0x37,0x49,0x9,0xb6,0xe0,0xdf,0x88,0xa3,0xbb,0xf9,0xd5,0x28,0x5e,0xc,0x86,0x2b,0xf6,0x27,0xae,0xc7,0xd4,0x45,0xbd,0xf4,0x9d,0x38,0x80,0x10,0x44,0xff,0x30,0xe9,0xd7,0xd,0x2e,0x8e,0xb5,0xa0,0x4e,0x51,0xfe,0xf5,0x7e,0xa6,0x5,0xcc,0x68,0x12,0xa7,0xe3,0x47,0xc1,0xfd,0x19,0x65,0x5f,0x46,0x77,0xf,0xa5,0x2,0x48,0x8b,0x92,0xba,0x3b,0x90,0x6a,0x62,0x8a,0xca,0x64,0x95,0xfb,0x21,0x5c,0xe4,0x42,0xb9,0xf8,0x4,0x74,0xb3,0xea,0x36,0x60,0x3c,0x89,0x6e,0xb0,0xaf,0x7,0x4d,0xb2,0x7a,0xa8,0xf3,0xce,0x58,0x6f,0x11,0x1e,0xde,0x15,0x32,0x34,0xa9,0x6c,0x5a,0x2f,0xb1,0xdb,0x84,0x7d,0xa4,0x4b,0x66,0xe5,0x9e,0x93,0xaa,0xad,0x1b,0x43,0x7c,0x82,0x91,0x6,0x2a,0x53,0x3e,0xe1,0x8f,0xeb,0xc5,0xd9,0x18,0x26,0xf7,0xb4,0xdc,0xa,0xb8,0x33,0x3f,0x31,0x8d,0x9a,0x9f,0xe8,0x61,0x4c,0x67,0x1,0xd2,0xd8,0x6b,0x24,0x83,0x87,0xf0,0x25,0xb,0x56,0xa1,0xbc,0x85,0x97,0x7b,0x54,0x1a,0x2c,0x41,0xfc,0xe6,0xe7,0x1d,0xec,0x4a,0xd1,0x6d,0x8c,0x2d,0xcd,0x8,0xc2,0xed,0x7f,0x69,0xcb,0xdd,0xc6,0x0,0xd6,0x14,0xc3,0x75,0xda,0x94,0xe2,0x1c,0x9b,0xbf,0x3a,0x72,0xee,0x20,0x4f,0x35,0xc0,0x99,0xf1,0x63,0x39,0xd3,0xfa,0xb7,0x5b,0x71,0x16,0xef,0x76,0x73,0x13,0x3d,0xc9,0x98,0x55,0x9c,0x79,0x22,0xf2,0x1f,0x59,0x5d,0x29,0xbe,0x96,0x70,0xac,0x23,0x3,0x69,0x2b,0x29,0x88,0x2f,0xe7,0xe4,0x92,0x85,0x5f,0x36,0x33,0x34,0x48,0x35,0x55,0x60,0xd7,0x12,0x5b,0x1,0xed,0x2a,0xbf,0x23,0xd0,0x58,0xf5,0x1c,0xe9,0x73,0x46,0x96,0xc3,0x8c,0x59,0xcd,0x1f,0xb0,0x71,0x62,0xe,0xf6,0xc8,0x2d,0x83,0xf2,0x8d,0xbc,0x37,0x4f,0x3,0x3d,0x9e,0xe5,0x95,0x63,0x4e,0xab,0x42,0x80,0xb3,0x4d,0x93,0xa2,0xd,0x2,0x49,0xdc,0x52,0xfe,0x41,0x28,0xb1,0x5c,0xf7,0xe0,0xcc,0x17,0x87,0x57,0xb5,0xad,0xc5,0x25,0xb4,0xf1,0x78,0xde,0x26,0x9d,0x18,0x47,0x15,0xfd,0xee,0xd3,0xbe,0x7e,0x81,0x67,0x6d,0x20,0x31,0xac,0xae,0xda,0x32,0x44,0x9b,0x74,0xca,0xb,0x76,0x61,0x79,0x24,0xb9,0x3c,0xea,0x8,0x51,0x1a,0xec,0xc9,0x39,0x77,0x0,0xbd,0xfb,0x22,0x7,0x9f,0xb8,0x16,0x9,0xc4,0x2c,0x8f,0xb7,0xb6,0x90,0x1d,0x99,0xe6,0x70,0x38,0xd8,0x5d,0xc2,0xe1,0xa9,0xbb,0xa5,0x6e,0xb2,0x8b,0xcf,0x4b,0xc7,0x9c,0xef,0x27,0xa6,0xf8,0x6f,0x3b,0x82,0x56,0x6a,0xc,0xa,0x98,0x8a,0x7f,0xeb,0xf0,0x9a,0xba,0xa0,0xa4,0xf3,0xf4,0x5,0xc6,0xfc,0x45,0x6b,0xc0,0x64,0x72,0x75,0xdb,0xce,0xe8,0x50,0xa7,0x97,0x43,0x8e,0xf,0xa3,0x5a,0xd2,0x4,0x65,0xfa,0x68,0x91,0x7c,0xd9,0x1e,0x3e,0xdf,0xaf,0x6,0x19,0x5e,0x21,0xf9,0x7b,0x53,0xe2,0x7a,0xd1,0x11,0xc1,0x3f,0x2e,0x84,0x13,0xdd,0xcb,0x30,0x94,0x89,0x7d,0x4a,0x1b,0x40,0xd5,0xe3,0xd6,0x54,0xaa,0x66,0xd4,0xa1,0xff,0x10,0x14,0x6c,0x86,0x3a,0x4c,0xa8,0x72,0x8e,0x7b,0xd7,0x1d,0x99,0x90,0xee,0xce,0x9,0xa1,0x11,0x58,0x8b,0x89,0x16,0xf1,0xc,0xd5,0x87,0xf2,0x5f,0xfe,0x2f,0x6f,0xd0,0x6,0x39,0x7a,0x51,0x20,0x62,0xc9,0x59,0x26,0x9d,0x30,0xe9,0xd4,0xe,0x1e,0x77,0x9c,0xd,0x2d,0x64,0xe1,0x44,0x7f,0xa7,0x15,0xdc,0xcb,0xb1,0x3a,0x7e,0x57,0xf7,0x79,0x6c,0x88,0x97,0x2c,0x27,0x7c,0xd6,0x91,0xdb,0x4b,0x52,0xe2,0x63,0x18,0x9e,0xc0,0x24,0x86,0xbc,0xae,0x9f,0x85,0xf8,0x9b,0x3d,0x21,0x60,0xad,0xdd,0xb3,0x49,0x53,0xbb,0xbd,0x13,0x22,0x4c,0xde,0x76,0x6b,0x94,0x71,0xa3,0x17,0x2a,0x33,0x6a,0xb9,0xef,0x50,0xe5,0x69,0xb7,0xb5,0x70,0xf6,0x83,0x2,0x68,0xa4,0x5d,0xb6,0x81,0xc7,0xc8,0xcc,0x7,0xed,0xeb,0x9a,0xc2,0x5b,0xa5,0xdf,0x48,0x8a,0xf3,0x92,0x7d,0x3c,0xbf,0x4a,0x47,0x74,0x73,0x6d,0x2e,0xd3,0x5,0xea,0x61,0xe8,0xe6,0x38,0xe7,0x32,0x56,0x0,0x1c,0xff,0xc1,0x1,0xb,0xfd,0xb2,0x5e,0x5a,0xfc,0x29,0x43,0x54,0x31,0x46,0x95,0xb8,0xd8,0xbe,0xf5,0xc3,0x25,0x98,0x3e,0x3f,0x35,0xc4,0x8f,0xd2,0x65,0x78,0x4e,0x5c,0x8d,0xa2,0xa6,0x34,0x12,0xb0,0x1f,0x4,0xf,0xd9,0x8,0x93,0x55,0xb4,0x14,0xf4,0x1b,0xd1,0xe3,0x66,0x37,0xab,0x96,0xf9,0x19,0xec,0x1a,0xcd,0x3,0xac,0x3b,0x4d,0x42,0xc5,0xcf,0xa8,0xaf,0x36,0xca,0xaa,0x10,0xe4,0x28,0x40,0xe0,0xba,0x23,0xa,0x82,0x6e,0xf0,0x84,0x4f,0x67,0x75,0xa9,0xda,0xfa,0x8c,0x41,0xa0,0x45,0x2b,0xfb,0x80,0xc6,0x5,0xa3,0x1b,0x66,0x33,0x43,0xbf,0xfe,0xcd,0x25,0x2d,0xd7,0xbc,0xd2,0x23,0x8d,0xf,0x45,0xe2,0x48,0x7c,0xfd,0xd5,0xcc,0x5e,0xba,0x86,0x0,0x30,0x1,0x18,0x22,0x68,0x1d,0x2b,0xee,0x3a,0xc3,0x9c,0xf6,0x59,0x56,0x28,0x1f,0x73,0x75,0x52,0x99,0xf5,0xa,0x40,0xe8,0x89,0xb4,0xef,0x3d,0x27,0x71,0xad,0xf4,0xf7,0x29,0xce,0x7b,0x4b,0x19,0x6f,0x92,0x60,0xb1,0x6c,0xc1,0x98,0xa7,0xf1,0x4e,0xbe,0xfc,0xe4,0xcf,0xe5,0x49,0xec,0x10,0xe,0x70,0x83,0x7,0x3f,0x8f,0x50,0x97,0x17,0x88,0xc6,0x15,0x8b,0x42,0xe1,0x39,0xa4,0xe0,0x55,0x2f,0xe7,0xf2,0xc9,0x69,0xb2,0xb9,0x16,0x9,0xb8,0x3,0x57,0xc7,0x4a,0x90,0xae,0x77,0x2,0x93,0x80,0xe9,0x7f,0xda,0xb3,0xfa,0xa9,0x35,0x7d,0xf8,0x87,0x72,0x8,0x67,0x9d,0x32,0x84,0x53,0xdc,0x5b,0xa5,0xd3,0x8c,0x2e,0x38,0xaa,0x91,0x47,0x81,0x9a,0xcb,0x2a,0x96,0xd,0x85,0x4f,0x8a,0x6a,0xd1,0xf9,0x6e,0x1a,0x44,0x64,0xeb,0x37,0x3e,0xdb,0x12,0xdf,0x1e,0x58,0xb5,0x65,0x31,0xa8,0x51,0x36,0x8e,0x7a,0x54,0x34,0x7e,0x24,0xb6,0xde,0x1c,0xf0,0xbd,0x94,0x4d,0x9b,0xf3,0xb0,0x76,0x78,0x74,0xff,0xac,0xc8,0xa6,0x79,0x61,0x5f,0x9e,0x82,0xc5,0x3b,0x4,0x5c,0x14,0x6d,0x41,0xd6,0xa2,0x21,0xc,0xe3,0xea,0xed,0xd4,0xd9,0xbb,0x6,0x6b,0x5d,0xab,0x5a,0xa0,0xa1,0xfb,0xe6,0x11,0x4c,0x13,0x3c,0xd0,0xc2,0x63,0x2c,0x9f,0x95,0x62,0xb7,0xc0,0xc4,0xaf,0xd8,0xdd,0xca,0x46,0x20,0xb,0x26,0xde,0x50,0xfc,0x43,0xa0,0xf,0x0,0x4b,0xe2,0xce,0x15,0x85,0x2a,0xb3,0x5e,0xf5,0x27,0xb6,0xf3,0x7a,0x55,0xb7,0xaf,0xc7,0x45,0x17,0xff,0xec,0xdc,0x24,0x9f,0x1a,0x65,0x6f,0x22,0x33,0xd1,0xbc,0x7c,0x83,0x46,0x99,0x76,0xc8,0xae,0xac,0xd8,0x30,0x26,0xbb,0x3e,0xe8,0x9,0x74,0x63,0x7b,0xcb,0x3b,0x75,0x2,0xa,0x53,0x18,0xee,0x2d,0xe5,0xe6,0x90,0x6b,0x29,0x2b,0x8a,0x36,0x4a,0x37,0x57,0x87,0x5d,0x34,0x31,0x3,0xef,0x28,0xbd,0x62,0xd5,0x10,0x59,0x1e,0xeb,0x71,0x44,0x21,0xd2,0x5a,0xf7,0xcf,0x1d,0xb2,0x73,0x94,0xc1,0x8e,0x5b,0x2f,0x81,0xf0,0x8f,0x60,0xc,0xf4,0xca,0x3f,0x9c,0xe7,0x97,0xbe,0x35,0x4d,0x1,0x82,0xb1,0x4f,0x91,0x61,0x4c,0xa9,0x40,0xa5,0x95,0x41,0x8c,0xd9,0xcc,0xea,0x52,0x6,0x67,0xf8,0x6a,0xd,0xa1,0x58,0xd0,0x3c,0xdd,0xad,0x4,0x93,0x7e,0xdb,0x1c,0x79,0x51,0xe0,0x78,0x1b,0x5c,0x23,0xfb,0x2c,0x86,0x11,0xdf,0xd3,0x13,0xc3,0x3d,0x7f,0x48,0x19,0x42,0xc9,0x32,0x96,0x8b,0xa8,0x64,0xd6,0xa3,0xd7,0xe1,0xd4,0x56,0x84,0x38,0x4e,0xaa,0xfd,0x12,0x16,0x6e,0x9d,0xba,0x14,0xb,0xbf,0xf9,0x20,0x5,0xb4,0x92,0x1f,0x9b,0xc6,0x2e,0x8d,0xb5,0x5f,0xc0,0xe3,0xab,0xe4,0x72,0x3a,0xda,0x89,0xcd,0x49,0xc5,0xb9,0xa7,0x6c,0xb0,0xfa,0x6d,0x39,0x80,0x9e,0xed,0x25,0xa4,0x9a,0x88,0x7d,0xe9,0x54,0x68,0xe,0x8,0xa6,0xf1,0xf6,0x7,0xf2,0x98,0xb8,0xa2,0xc2,0x66,0x70,0x77,0xc4,0xfe,0x47,0x69,0xfe,0x5c,0x7a,0xe8,0x97,0x41,0x4a,0x51,0xfa,0x1b,0xdd,0x46,0x9f,0x55,0xba,0x5a,0xe5,0x79,0x28,0xad,0xa2,0x57,0xb7,0xd8,0xe2,0x4d,0x83,0x54,0x8b,0xc,0x3,0x75,0x78,0xe1,0xe6,0x81,0xaa,0x5e,0xe4,0x84,0xf4,0xae,0xe,0x66,0x20,0xcc,0x44,0x6d,0x29,0x1,0xca,0xbe,0xb4,0x94,0xe7,0x3b,0xb,0xee,0xf,0xc2,0x88,0xce,0xb5,0x65,0xeb,0x15,0x8c,0xd4,0xbd,0xc4,0x6,0x91,0xf1,0x72,0x33,0xdc,0x3d,0x3a,0x9,0x4,0x4b,0x9d,0x60,0x23,0xa8,0xa6,0x2f,0xa4,0x18,0x7c,0xa9,0x76,0x8f,0xb1,0x52,0x4e,0xfc,0xb3,0x45,0x4f,0x67,0xb2,0x14,0x10,0x8,0x7f,0x1a,0xd,0xf0,0x96,0xf6,0xdb,0xd6,0x6b,0x8d,0xbb,0x8a,0x7b,0x71,0x70,0x36,0x2b,0x9c,0xc1,0xec,0xc3,0x12,0x0,0x95,0xdf,0x98,0x32,0x2d,0xac,0x1c,0x5,0x6a,0x8e,0xd0,0x56,0xd1,0xe0,0xf2,0xc8,0x73,0xd5,0xb6,0xcb,0x93,0xe3,0x2e,0x6f,0xf5,0x1d,0x7,0xfd,0x2,0x6c,0x5d,0xf3,0xda,0x25,0x38,0x90,0x64,0x59,0xed,0x3f,0xa1,0xf7,0x24,0x7d,0xf9,0x27,0xab,0x1e,0xcd,0xb8,0x3e,0xfb,0x13,0xea,0x26,0x4c,0x86,0x89,0xcf,0xf8,0xa5,0xa3,0x49,0x82,0x99,0x35,0xc0,0x3c,0xa0,0xde,0xd7,0x53,0x5f,0xef,0x47,0x80,0x58,0xc7,0xc5,0x16,0xc9,0x9b,0x42,0xbf,0x61,0xb0,0x11,0xbc,0x77,0x48,0x9e,0x21,0x2c,0x6e,0x1f,0x34,0xd3,0x68,0x17,0x87,0x40,0x9a,0xa7,0x7e,0x43,0xd2,0x39,0x50,0xa,0xaf,0x2a,0x63,0x92,0x5b,0xe9,0x31,0x30,0x74,0xff,0x85,0x22,0x37,0xb9,0x19,0x69,0x62,0xd9,0xc6,0x71,0x8d,0x78,0xd4,0x1e,0x9a,0x93,0xed,0xcd,0xa,0xa2,0x12,0x5b,0x88,0x8a,0x15,0xf2,0xf,0xd6,0x84,0xf1,0x5c,0xfd,0x2c,0x6c,0xd3,0x5,0x3a,0x79,0x52,0x23,0x61,0xca,0x5a,0x25,0x9e,0x33,0xea,0xd7,0xd,0x1d,0x74,0x9f,0xe,0x2e,0x67,0xe2,0x47,0x7c,0xa4,0x16,0xdf,0xc8,0xb2,0x39,0x7d,0x54,0xf4,0x7a,0x6f,0x8b,0x94,0x2f,0x24,0x7f,0xd5,0x92,0xd8,0x48,0x51,0xe1,0x60,0x1b,0x9d,0xc3,0x27,0x85,0xbf,0xad,0x9c,0x86,0xfb,0x98,0x3e,0x22,0x63,0xae,0xde,0xb0,0x4a,0x50,0xb8,0xbe,0x10,0x21,0x4f,0xdd,0x75,0x68,0x97,0x72,0xa0,0x14,0x29,0x30,0x69,0xba,0xec,0x53,0xe6,0x6a,0xb4,0xb6,0x73,0xf5,0x80,0x1,0x6b,0xa7,0x5e,0xb5,0x82,0xc4,0xcb,0xcf,0x4,0xee,0xe8,0x99,0xc1,0x58,0xa6,0xdc,0x4b,0x89,0xf0,0x91,0x7e,0x3f,0xbc,0x49,0x44,0x77,0x70,0x6e,0x2d,0xd0,0x6,0xe9,0x62,0xeb,0xe5,0x3b,0xe4,0x31,0x55,0x3,0x1f,0xfc,0xc2,0x2,0x8,0xfe,0xb1,0x5d,0x59,0xff,0x2a,0x40,0x57,0x32,0x45,0x96,0xbb,0xdb,0xbd,0xf6,0xc0,0x26,0x9b,0x3d,0x3c,0x36,0xc7,0x8c,0xd1,0x66,0x7b,0x4d,0x5f,0x8e,0xa1,0xa5,0x37,0x11,0xb3,0x1c,0x7,0xc,0xda,0xb,0x90,0x56,0xb7,0x17,0xf7,0x18,0xd2,0xe0,0x65,0x34,0xa8,0x95,0xfa,0x1a,0xef,0x19,0xce,0x0,0xaf,0x38,0x4e,0x41,0xc6,0xcc,0xab,0xac,0x35,0xc9,0xa9,0x13,0xe7,0x2b,0x43,0xe3,0xb9,0x20,0x9,0x81,0x6d,0xf3,0x87,0x4c,0x64,0x76,0xaa,0xd9,0xf9,0x8f,0x42,0xa3,0x46,0x28,0xf8,0x83,0xc5,0x9f,0x76,0x93,0xbe,0x4e,0x90,0x6e,0x5d,0xde,0x92,0xea,0x61,0x48,0x38,0x43,0xe0,0x15,0x2b,0xd3,0xbf,0x50,0x2f,0x5e,0xf0,0x84,0x51,0x1e,0x4b,0xac,0x6d,0xc2,0x10,0x28,0x85,0xd,0xfe,0x9b,0xae,0x34,0xc1,0x86,0xcf,0xa,0xbd,0x62,0xf7,0x30,0xdc,0xee,0xeb,0x82,0x58,0x88,0xe8,0x95,0xe9,0x55,0xf4,0xf6,0xb4,0x4f,0x39,0x3a,0xf2,0x31,0xc7,0x8c,0xd5,0xdd,0xaa,0xe4,0x14,0xa4,0xbc,0xab,0xd6,0x37,0xe1,0x64,0xf9,0xef,0x7,0x73,0x71,0x17,0xa9,0x46,0x99,0x5c,0xa3,0x63,0xe,0xec,0xfd,0xb0,0xba,0xc5,0x40,0xfb,0x3,0x33,0x20,0xc8,0x9a,0x18,0x70,0x68,0x8a,0xa5,0x2c,0x69,0xf8,0x2a,0x81,0x6c,0xf5,0x5a,0xca,0x11,0x3d,0x94,0xdf,0xd0,0x7f,0x9c,0x23,0x8f,0x1,0xb6,0x98,0x21,0x1b,0xa8,0xaf,0xb9,0x1d,0x7d,0x67,0x47,0x2d,0xd8,0x29,0x2e,0x79,0xd7,0xd1,0xb7,0x8b,0x36,0xa2,0x57,0x45,0x7b,0xfa,0x32,0x41,0x5f,0xe6,0xb2,0x25,0x6f,0xb3,0x78,0x66,0x1a,0x96,0x12,0x56,0x5,0xe5,0xad,0x3b,0x74,0x3c,0x1f,0x80,0x6a,0x52,0xf1,0x19,0x44,0xc0,0x4d,0x6b,0xda,0xff,0x26,0x60,0xd4,0xcb,0x65,0x42,0xb1,0xc9,0xcd,0x22,0x75,0x91,0xe7,0x5b,0x89,0xb,0x3e,0x8,0x7c,0x9,0xbb,0x77,0x54,0x49,0xed,0x16,0x9d,0xc6,0x97,0xa0,0xe2,0x1c,0xcc,0xc,0x0,0xce,0x59,0xf3,0x24,0xfc,0x83,0xc4,0xa7,0x3f,0x8e,0xa6,0xc3,0x4,0xa1,0x4c,0xdb,0x72,0x2,0xe3,0xf,0x87,0x7e,0xd2,0xb5,0x27,0xb8,0xd9,0x8d,0x35,0x13,0x6,0x53,0x9e,0x4a,0x7a,0x36,0xf1,0x2e,0x9e,0xb4,0x67,0x29,0xb6,0xb1,0x4d,0xe8,0x44,0xa6,0x22,0xd1,0xaf,0xef,0x50,0x6,0x39,0x6e,0x45,0x5d,0x1f,0x33,0xce,0xb8,0xea,0x60,0xcd,0x10,0xc1,0x48,0x21,0x32,0xa3,0x5b,0x12,0x7b,0xde,0x66,0xf6,0xa2,0x19,0xd6,0xf,0x31,0xeb,0xc8,0x68,0x53,0x46,0xa8,0xb7,0x18,0x13,0x98,0x40,0xe3,0x2a,0x8e,0xf4,0x41,0x5,0xa1,0x27,0x1b,0xff,0x83,0xb9,0xa0,0x91,0xe9,0x43,0xe4,0xae,0x6d,0x74,0x5c,0xdd,0x76,0x8c,0x84,0x6c,0x2c,0x82,0x73,0x1d,0xc7,0xba,0x2,0xa4,0x5f,0x1e,0xe2,0x92,0x55,0xc,0xd0,0x86,0xda,0x6f,0x88,0x56,0x49,0xe1,0xab,0x54,0x9c,0x4e,0x15,0x28,0xbe,0x89,0xf7,0xf8,0x38,0xf3,0xd4,0xd2,0x4f,0x8a,0xbc,0xc9,0x57,0x3d,0x62,0x9b,0x42,0xad,0x80,0x3,0x78,0x75,0x4c,0x4b,0xfd,0xa5,0x9a,0x64,0x77,0xe0,0xcc,0xb5,0xd8,0x7,0x69,0xd,0x23,0x3f,0xfe,0xc0,0x11,0x52,0x3a,0xec,0x5e,0xd5,0xd9,0xd7,0x6b,0x7c,0x79,0xe,0x87,0xaa,0x81,0xe7,0x34,0x3e,0x8d,0xc2,0x65,0x61,0x16,0xc3,0xed,0xb0,0x47,0x5a,0x63,0x71,0x9d,0xb2,0xfc,0xca,0xa7,0x1a,0x0,0x1,0xfb,0xa,0xac,0x37,0x8b,0x6a,0xcb,0x2b,0xee,0x24,0xb,0x99,0x8f,0x2d,0x3b,0x20,0xe6,0x30,0xf2,0x25,0x93,0x3c,0x72,0x4,0xfa,0x7d,0x59,0xdc,0x94,0x8,0xc6,0xa9,0xd3,0x26,0x7f,0x17,0x85,0xdf,0x35,0x1c,0x51,0xbd,0x97,0xf0,0x9,0x90,0x95,0xf5,0xdb,0x2f,0x7e,0xb3,0x7a,0x9f,0xc4,0x14,0xf9,0xbf,0xbb,0xcf,0x58,0x70,0x96,0x4a,0xc5,0xe5,0x2c,0x8e,0xa8,0x3a,0x45,0x93,0x98,0x83,0x28,0xc9,0xf,0x94,0x4d,0x87,0x68,0x88,0x37,0xab,0xfa,0x7f,0x70,0x85,0x65,0xa,0x30,0x9f,0x51,0x86,0x59,0xde,0xd1,0xa7,0xaa,0x33,0x34,0x53,0x78,0x8c,0x36,0x56,0x26,0x7c,0xdc,0xb4,0xf2,0x1e,0x96,0xbf,0xfb,0xd3,0x18,0x6c,0x66,0x46,0x35,0xe9,0xd9,0x3c,0xdd,0x10,0x5a,0x1c,0x67,0xb7,0x39,0xc7,0x5e,0x6,0x6f,0x16,0xd4,0x43,0x23,0xa0,0xe1,0xe,0xef,0xe8,0xdb,0xd6,0x99,0x4f,0xb2,0xf1,0x7a,0x74,0xfd,0x76,0xca,0xae,0x7b,0xa4,0x5d,0x63,0x80,0x9c,0x2e,0x61,0x97,0x9d,0xb5,0x60,0xc6,0xc2,0xda,0xad,0xc8,0xdf,0x22,0x44,0x24,0x9,0x4,0xb9,0x5f,0x69,0x58,0xa9,0xa3,0xa2,0xe4,0xf9,0x4e,0x13,0x3e,0x11,0xc0,0xd2,0x47,0xd,0x4a,0xe0,0xff,0x7e,0xce,0xd7,0xb8,0x5c,0x2,0x84,0x3,0x32,0x20,0x1a,0xa1,0x7,0x64,0x19,0x41,0x31,0xfc,0xbd,0x27,0xcf,0xd5,0x2f,0xd0,0xbe,0x8f,0x21,0x8,0xf7,0xea,0x42,0xb6,0x8b,0x3f,0xed,0x73,0x25,0xf6,0xaf,0x2b,0xf5,0x79,0xcc,0x1f,0x6a,0xec,0x29,0xc1,0x38,0xf4,0x9e,0x54,0x5b,0x1d,0x2a,0x77,0x71,0x9b,0x50,0x4b,0xe7,0x12,0xee,0x72,0xc,0x5,0x81,0x8d,0x3d,0x95,0x52,0x8a,0x15,0x17,0xc4,0x1b,0x49,0x90,0x6d,0xb3,0x62,0xc3,0x6e,0xa5,0x9a,0x4c,0xf3,0xfe,0xbc,0xcd,0xe6,0x1,0xba,0xc5,0x55,0x92,0x48,0x75,0xac,0x91,0x0,0xeb,0x82,0xd8,0x7d,0xf8,0xb1,0x40,0x89,0x3b,0xe3,0xe2,0xa6,0x2d,0x57,0xf0,0xe5,0x6b,0xcb,0xbb,0xb0,0xb,0x14,0x31,0x23,0xd6,0x42,0xff,0xc3,0xa5,0xa3,0x51,0xc6,0x92,0x2b,0x35,0x46,0x8e,0xf,0x69,0xcd,0xdb,0xdc,0x6f,0x55,0xec,0xc2,0xd,0x5a,0x5d,0xac,0x59,0x33,0x13,0x9,0x1f,0x39,0xb4,0x30,0x6d,0x85,0x26,0x1e,0x36,0x11,0xbf,0xa0,0x14,0x52,0x8b,0xae,0x22,0x66,0xe2,0x6e,0x12,0xc,0xc7,0x1b,0xf4,0x6b,0x48,0x0,0x4f,0xd9,0x91,0x71,0xd4,0xe3,0xb2,0xe9,0x62,0x99,0x3d,0x20,0x87,0x2d,0xba,0x74,0x78,0xb8,0x68,0x96,0x2f,0x93,0xe5,0x1,0x56,0xb9,0xbd,0xc5,0x3,0xcf,0x7d,0x8,0x7c,0x4a,0x7f,0xfd,0xad,0xcc,0x53,0xc1,0xa6,0xa,0xf3,0x7b,0xe,0x3e,0xea,0x27,0x72,0x67,0x41,0xf9,0xd2,0xfa,0x4b,0xd3,0xb0,0xf7,0x88,0x50,0x97,0x76,0x6,0xaf,0x38,0xd5,0x70,0xb7,0x84,0x2a,0x5b,0x24,0xcb,0xa7,0x5f,0x61,0x64,0xb6,0x19,0xd8,0x3f,0x6a,0x25,0xf0,0x29,0x1a,0xe4,0x3a,0xca,0xe7,0x2,0xeb,0x94,0x37,0x4c,0x3c,0x15,0x9e,0xe6,0xaa,0x9d,0xe1,0x9c,0xfc,0x2c,0xf6,0x9f,0x9a,0x86,0x4e,0x4d,0x3b,0xc0,0x82,0x80,0x21,0xb5,0x40,0xda,0xef,0x8a,0x79,0xf1,0x5c,0xa8,0x44,0x83,0x16,0xc9,0x7e,0xbb,0xf2,0xed,0x32,0xdd,0x63,0x5,0x7,0x73,0x9b,0xce,0xc4,0x89,0x98,0x7a,0x17,0xd7,0x28,0x60,0x90,0xde,0xa9,0xa1,0xf8,0xb3,0x45,0x8d,0x10,0x95,0x43,0xa2,0xdf,0xc8,0xd0,0x49,0x65,0xbe,0x2e,0x81,0x18,0xf5,0x5e,0x75,0xfb,0x57,0xe8,0xb,0xa4,0xab,0xe0,0xee,0xbc,0x54,0x47,0x77,0x8f,0x34,0xb1,0x8c,0x1d,0x58,0xd1,0xfe,0x1c,0x4,0x6c,0x6a,0xcc,0x74,0x9,0x5c,0x2c,0xd0,0x91,0xa2,0x4a,0x42,0xb8,0xd3,0xbd,0x4c,0xe2,0x60,0x2a,0x8d,0x27,0x13,0x92,0xba,0xa3,0x31,0xd5,0xe9,0x6f,0x5f,0x6e,0x77,0x4d,0x7,0x72,0x44,0x81,0x55,0xac,0xf3,0x99,0x36,0x39,0x47,0x70,0x1c,0x1a,0x3d,0xf6,0x9a,0x65,0x2f,0x87,0xe6,0xdb,0x80,0x52,0x48,0x1e,0xc2,0x9b,0x98,0x46,0xa1,0x14,0x24,0x76,0x0,0xfd,0xf,0xde,0x3,0xae,0xf7,0xc8,0x9e,0x21,0xd1,0x93,0x8b,0xa0,0x8a,0x26,0x83,0x7f,0x61,0x1f,0xec,0x68,0x50,0xe0,0x3f,0xf8,0x78,0xe7,0xa9,0x7a,0xe4,0x2d,0x8e,0x56,0xcb,0x8f,0x3a,0x40,0x88,0x9d,0xa6,0x6,0xdd,0xd6,0x79,0x66,0xd7,0x6c,0x38,0xa8,0x25,0xff,0xc1,0x18,0x6d,0xfc,0xef,0x86,0x10,0xb5,0xdc,0x95,0xc6,0x5a,0x12,0x97,0xe8,0x1d,0x67,0x8,0xf2,0x5d,0xeb,0x3c,0xb3,0x34,0xca,0xbc,0xe3,0x41,0x57,0xc5,0xfe,0x28,0xee,0xf5,0xa4,0x45,0xf9,0x62,0xea,0x20,0xe5,0x5,0xbe,0x96,0x1,0x75,0x2b,0xb,0x84,0x58,0x51,0xb4,0x7d,0xb0,0x71,0x37,0xda,0xa,0x5e,0xc7,0x3e,0x59,0xe1,0x15,0x3b,0x5b,0x11,0x4b,0xd9,0xb1,0x73,0x9f,0xd2,0xfb,0x22,0xf4,0x9c,0xdf,0x19,0x17,0x1b,0x90,0xc3,0xa7,0xc9,0x16,0xe,0x30,0xf1,0xed,0xaa,0x54,0x6b,0x33,0x7b,0x2,0x2e,0xb9,0xcd,0x4e,0x63,0x8c,0x85,0x82,0xbb,0xb6,0xd4,0x69,0x4,0x32,0xc4,0x35,0xcf,0xce,0x94,0x89,0x7e,0x23,0x7c,0x53,0xbf,0xad,0xc,0x43,0xf0,0xfa,0xd,0xd8,0xaf,0xab,0xc0,0xb7,0xb2,0xa5,0x29,0x4f,0x64,0x49,0xad,0x6a,0xb5,0x5,0x2f,0xfc,0xb2,0x2d,0x2a,0xd6,0x73,0xdf,0x3d,0xb9,0x4a,0x34,0x74,0xcb,0x9d,0xa2,0xf5,0xde,0xc6,0x84,0xa8,0x55,0x23,0x71,0xfb,0x56,0x8b,0x5a,0xd3,0xba,0xa9,0x38,0xc0,0x89,0xe0,0x45,0xfd,0x6d,0x39,0x82,0x4d,0x94,0xaa,0x70,0x53,0xf3,0xc8,0xdd,0x33,0x2c,0x83,0x88,0x3,0xdb,0x78,0xb1,0x15,0x6f,0xda,0x9e,0x3a,0xbc,0x80,0x64,0x18,0x22,0x3b,0xa,0x72,0xd8,0x7f,0x35,0xf6,0xef,0xc7,0x46,0xed,0x17,0x1f,0xf7,0xb7,0x19,0xe8,0x86,0x5c,0x21,0x99,0x3f,0xc4,0x85,0x79,0x9,0xce,0x97,0x4b,0x1d,0x41,0xf4,0x13,0xcd,0xd2,0x7a,0x30,0xcf,0x7,0xd5,0x8e,0xb3,0x25,0x12,0x6c,0x63,0xa3,0x68,0x4f,0x49,0xd4,0x11,0x27,0x52,0xcc,0xa6,0xf9,0x0,0xd9,0x36,0x1b,0x98,0xe3,0xee,0xd7,0xd0,0x66,0x3e,0x1,0xff,0xec,0x7b,0x57,0x2e,0x43,0x9c,0xf2,0x96,0xb8,0xa4,0x65,0x5b,0x8a,0xc9,0xa1,0x77,0xc5,0x4e,0x42,0x4c,0xf0,0xe7,0xe2,0x95,0x1c,0x31,0x1a,0x7c,0xaf,0xa5,0x16,0x59,0xfe,0xfa,0x8d,0x58,0x76,0x2b,0xdc,0xc1,0xf8,0xea,0x6,0x29,0x67,0x51,0x3c,0x81,0x9b,0x9a,0x60,0x91,0x37,0xac,0x10,0xf1,0x50,0xb0,0x75,0xbf,0x90,0x2,0x14,0xb6,0xa0,0xbb,0x7d,0xab,0x69,0xbe,0x8,0xa7,0xe9,0x9f,0x61,0xe6,0xc2,0x47,0xf,0x93,0x5d,0x32,0x48,0xbd,0xe4,0x8c,0x1e,0x44,0xae,0x87,0xca,0x26,0xc,0x6b,0x92,0xb,0xe,0x6e,0x40,0xb4,0xe5,0x28,0xe1,0x4,0x5f,0x8f,0x62,0x24,0x20,0x54,0xc3,0xeb,0xd,0xd1,0x5e,0x7e,0x57,0xf5,0xd3,0x41,0x3e,0xe8,0xe3,0xf8,0x53,0xb2,0x74,0xef,0x36,0xfc,0x13,0xf3,0x4c,0xd0,0x81,0x4,0xb,0xfe,0x1e,0x71,0x4b,0xe4,0x2a,0xfd,0x22,0xa5,0xaa,0xdc,0xd1,0x48,0x4f,0x28,0x3,0xf7,0x4d,0x2d,0x5d,0x7,0xa7,0xcf,0x89,0x65,0xed,0xc4,0x80,0xa8,0x63,0x17,0x1d,0x3d,0x4e,0x92,0xa2,0x47,0xa6,0x6b,0x21,0x67,0x1c,0xcc,0x42,0xbc,0x25,0x7d,0x14,0x6d,0xaf,0x38,0x58,0xdb,0x9a,0x75,0x94,0x93,0xa0,0xad,0xe2,0x34,0xc9,0x8a,0x1,0xf,0x86,0xd,0xb1,0xd5,0x0,0xdf,0x26,0x18,0xfb,0xe7,0x55,0x1a,0xec,0xe6,0xce,0x1b,0xbd,0xb9,0xa1,0xd6,0xb3,0xa4,0x59,0x3f,0x5f,0x72,0x7f,0xc2,0x24,0x12,0x23,0xd2,0xd8,0xd9,0x9f,0x82,0x35,0x68,0x45,0x6a,0xbb,0xa9,0x3c,0x76,0x31,0x9b,0x84,0x5,0xb5,0xac,0xc3,0x27,0x79,0xff,0x78,0x49,0x5b,0x61,0xda,0x7c,0x1f,0x62,0x3a,0x4a,0x87,0xc6,0x5c,0xb4,0xae,0x54,0xab,0xc5,0xf4,0x5a,0x73,0x8c,0x91,0x39,0xcd,0xf0,0x44,0x96,0x8,0x5e,0x8d,0xd4,0x50,0x8e,0x2,0xb7,0x64,0x11,0x97,0x52,0xba,0x43,0x8f,0xe5,0x2f,0x20,0x66,0x51,0xc,0xa,0xe0,0x2b,0x30,0x9c,0x69,0x95,0x9,0x77,0x7e,0xfa,0xf6,0x46,0xee,0x29,0xf1,0x6e,0x6c,0xbf,0x60,0x32,0xeb,0x16,0xc8,0x19,0xb8,0x15,0xde,0xe1,0x37,0x88,0x85,0xc7,0xb6,0x9d,0x7a,0xc1,0xbe,0x2e,0xe9,0x33,0xe,0xd7,0xea,0x7b,0x90,0xf9,0xa3,0x6,0x83,0xca,0x3b,0xf2,0x40,0x98,0x99,0xdd,0x56,0x2c,0x8b,0x9e,0x10,0xb0,0xc0,0xcb,0x70,0x6f,0x55,0xa9,0x5c,0xf0,0x3a,0xbe,0xb7,0xc9,0xe9,0x2e,0x86,0x36,0x7f,0xac,0xae,0x31,0xd6,0x2b,0xf2,0xa0,0xd5,0x78,0xd9,0x8,0x48,0xf7,0x21,0x1e,0x5d,0x76,0x7,0x45,0xee,0x7e,0x1,0xba,0x17,0xce,0xf3,0x29,0x39,0x50,0xbb,0x2a,0xa,0x43,0xc6,0x63,0x58,0x80,0x32,0xfb,0xec,0x96,0x1d,0x59,0x70,0xd0,0x5e,0x4b,0xaf,0xb0,0xb,0x0,0x5b,0xf1,0xb6,0xfc,0x6c,0x75,0xc5,0x44,0x3f,0xb9,0xe7,0x3,0xa1,0x9b,0x89,0xb8,0xa2,0xdf,0xbc,0x1a,0x6,0x47,0x8a,0xfa,0x94,0x6e,0x74,0x9c,0x9a,0x34,0x5,0x6b,0xf9,0x51,0x4c,0xb3,0x56,0x84,0x30,0xd,0x14,0x4d,0x9e,0xc8,0x77,0xc2,0x4e,0x90,0x92,0x57,0xd1,0xa4,0x25,0x4f,0x83,0x7a,0x91,0xa6,0xe0,0xef,0xeb,0x20,0xca,0xcc,0xbd,0xe5,0x7c,0x82,0xf8,0x6f,0xad,0xd4,0xb5,0x5a,0x1b,0x98,0x6d,0x60,0x53,0x54,0x4a,0x9,0xf4,0x22,0xcd,0x46,0xcf,0xc1,0x1f,0xc0,0x15,0x71,0x27,0x3b,0xd8,0xe6,0x26,0x2c,0xda,0x95,0x79,0x7d,0xdb,0xe,0x64,0x73,0x16,0x61,0xb2,0x9f,0xff,0x99,0xd2,0xe4,0x2,0xbf,0x19,0x18,0x12,0xe3,0xa8,0xf5,0x42,0x5f,0x69,0x7b,0xaa,0x85,0x81,0x13,0x35,0x97,0x38,0x23,0x28,0xfe,0x2f,0xb4,0x72,0x93,0x33,0xd3,0x3c,0xf6,0xc4,0x41,0x10,0x8c,0xb1,0xde,0x3e,0xcb,0x3d,0xea,0x24,0x8b,0x1c,0x6a,0x65,0xe2,0xe8,0x8f,0x88,0x11,0xed,0x8d,0x37,0xc3,0xf,0x67,0xc7,0x9d,0x4,0x2d,0xa5,0x49,0xd7,0xa3,0x68,0x40,0x52,0x8e,0xfd,0xdd,0xab,0x66,0x87,0x62,0xc,0xdc,0xa7,0xe1,0x46,0xe0,0x58,0x25,0x70,0x0,0xfc,0xbd,0x8e,0x66,0x6e,0x94,0xff,0x91,0x60,0xce,0x4c,0x6,0xa1,0xb,0x3f,0xbe,0x96,0x8f,0x1d,0xf9,0xc5,0x43,0x73,0x42,0x5b,0x61,0x2b,0x5e,0x68,0xad,0x79,0x80,0xdf,0xb5,0x1a,0x15,0x6b,0x5c,0x30,0x36,0x11,0xda,0xb6,0x49,0x3,0xab,0xca,0xf7,0xac,0x7e,0x64,0x32,0xee,0xb7,0xb4,0x6a,0x8d,0x38,0x8,0x5a,0x2c,0xd1,0x23,0xf2,0x2f,0x82,0xdb,0xe4,0xb2,0xd,0xfd,0xbf,0xa7,0x8c,0xa6,0xa,0xaf,0x53,0x4d,0x33,0xc0,0x44,0x7c,0xcc,0x13,0xd4,0x54,0xcb,0x85,0x56,0xc8,0x1,0xa2,0x7a,0xe7,0xa3,0x16,0x6c,0xa4,0xb1,0x8a,0x2a,0xf1,0xfa,0x55,0x4a,0xfb,0x40,0x14,0x84,0x9,0xd3,0xed,0x34,0x41,0xd0,0xc3,0xaa,0x3c,0x99,0xf0,0xb9,0xea,0x76,0x3e,0xbb,0xc4,0x31,0x4b,0x24,0xde,0x71,0xc7,0x10,0x9f,0x18,0xe6,0x90,0xcf,0x6d,0x7b,0xe9,0xd2,0x4,0xc2,0xd9,0x88,0x69,0xd5,0x4e,0xc6,0xc,0xc9,0x29,0x92,0xba,0x2d,0x59,0x7,0x27,0xa8,0x74,0x7d,0x98,0x51,0x9c,0x5d,0x1b,0xf6,0x26,0x72,0xeb,0x12,0x75,0xcd,0x39,0x17,0x77,0x3d,0x67,0xf5,0x9d,0x5f,0xb3,0xfe,0xd7,0xe,0xd8,0xb0,0xf3,0x35,0x3b,0x37,0xbc,0xef,0x8b,0xe5,0x3a,0x22,0x1c,0xdd,0xc1,0x86,0x78,0x47,0x1f,0x57,0x2e,0x2,0x95,0xe1,0x62,0x4f,0xa0,0xa9,0xae,0x97,0x9a,0xf8,0x45,0x28,0x1e,0xe8,0x19,0xe3,0xe2,0xb8,0xa5,0x52,0xf,0x50,0x7f,0x93,0x81,0x20,0x6f,0xdc,0xd6,0x21,0xf4,0x83,0x87,0xec,0x9b,0x9e,0x89,0x5,0x63,0x48,0x65,0x11,0xd6,0x9,0xb9,0x93,0x40,0xe,0x91,0x96,0x6a,0xcf,0x63,0x81,0x5,0xf6,0x88,0xc8,0x77,0x21,0x1e,0x49,0x62,0x7a,0x38,0x14,0xe9,0x9f,0xcd,0x47,0xea,0x37,0xe6,0x6f,0x6,0x15,0x84,0x7c,0x35,0x5c,0xf9,0x41,0xd1,0x85,0x3e,0xf1,0x28,0x16,0xcc,0xef,0x4f,0x74,0x61,0x8f,0x90,0x3f,0x34,0xbf,0x67,0xc4,0xd,0xa9,0xd3,0x66,0x22,0x86,0x0,0x3c,0xd8,0xa4,0x9e,0x87,0xb6,0xce,0x64,0xc3,0x89,0x4a,0x53,0x7b,0xfa,0x51,0xab,0xa3,0x4b,0xb,0xa5,0x54,0x3a,0xe0,0x9d,0x25,0x83,0x78,0x39,0xc5,0xb5,0x72,0x2b,0xf7,0xa1,0xfd,0x48,0xaf,0x71,0x6e,0xc6,0x8c,0x73,0xbb,0x69,0x32,0xf,0x99,0xae,0xd0,0xdf,0x1f,0xd4,0xf3,0xf5,0x68,0xad,0x9b,0xee,0x70,0x1a,0x45,0xbc,0x65,0x8a,0xa7,0x24,0x5f,0x52,0x6b,0x6c,0xda,0x82,0xbd,0x43,0x50,0xc7,0xeb,0x92,0xff,0x20,0x4e,0x2a,0x4,0x18,0xd9,0xe7,0x36,0x75,0x1d,0xcb,0x79,0xf2,0xfe,0xf0,0x4c,0x5b,0x5e,0x29,0xa0,0x8d,0xa6,0xc0,0x13,0x19,0xaa,0xe5,0x42,0x46,0x31,0xe4,0xca,0x97,0x60,0x7d,0x44,0x56,0xba,0x95,0xdb,0xed,0x80,0x3d,0x27,0x26,0xdc,0x2d,0x8b,0x10,0xac,0x4d,0xec,0xc,0xc9,0x3,0x2c,0xbe,0xa8,0xa,0x1c,0x7,0xc1,0x17,0xd5,0x2,0xb4,0x1b,0x55,0x23,0xdd,0x5a,0x7e,0xfb,0xb3,0x2f,0xe1,0x8e,0xf4,0x1,0x58,0x30,0xa2,0xf8,0x12,0x3b,0x76,0x9a,0xb0,0xd7,0x2e,0xb7,0xb2,0xd2,0xfc,0x8,0x59,0x94,0x5d,0xb8,0xe3,0x33,0xde,0x98,0x9c,0xe8,0x7f,0x57,0xb1,0x6d,0xe2,0xc2,0xa6,0x4,0x22,0xb0,0xcf,0x19,0x12,0x9,0xa2,0x43,0x85,0x1e,0xc7,0xd,0xe2,0x2,0xbd,0x21,0x70,0xf5,0xfa,0xf,0xef,0x80,0xba,0x15,0xdb,0xc,0xd3,0x54,0x5b,0x2d,0x20,0xb9,0xbe,0xd9,0xf2,0x6,0xbc,0xdc,0xac,0xf6,0x56,0x3e,0x78,0x94,0x1c,0x35,0x71,0x59,0x92,0xe6,0xec,0xcc,0xbf,0x63,0x53,0xb6,0x57,0x9a,0xd0,0x96,0xed,0x3d,0xb3,0x4d,0xd4,0x8c,0xe5,0x9c,0x5e,0xc9,0xa9,0x2a,0x6b,0x84,0x65,0x62,0x51,0x5c,0x13,0xc5,0x38,0x7b,0xf0,0xfe,0x77,0xfc,0x40,0x24,0xf1,0x2e,0xd7,0xe9,0xa,0x16,0xa4,0xeb,0x1d,0x17,0x3f,0xea,0x4c,0x48,0x50,0x27,0x42,0x55,0xa8,0xce,0xae,0x83,0x8e,0x33,0xd5,0xe3,0xd2,0x23,0x29,0x28,0x6e,0x73,0xc4,0x99,0xb4,0x9b,0x4a,0x58,0xcd,0x87,0xc0,0x6a,0x75,0xf4,0x44,0x5d,0x32,0xd6,0x88,0xe,0x89,0xb8,0xaa,0x90,0x2b,0x8d,0xee,0x93,0xcb,0xbb,0x76,0x37,0xad,0x45,0x5f,0xa5,0x5a,0x34,0x5,0xab,0x82,0x7d,0x60,0xc8,0x3c,0x1,0xb5,0x67,0xf9,0xaf,0x7c,0x25,0xa1,0x7f,0xf3,0x46,0x95,0xe0,0x66,0xa3,0x4b,0xb2,0x7e,0x14,0xde,0xd1,0x97,0xa0,0xfd,0xfb,0x11,0xda,0xc1,0x6d,0x98,0x64,0xf8,0x86,0x8f,0xb,0x7,0xb7,0x1f,0xd8,0x0,0x9f,0x9d,0x4e,0x91,0xc3,0x1a,0xe7,0x39,0xe8,0x49,0xe4,0x2f,0x10,0xc6,0x79,0x74,0x36,0x47,0x6c,0x8b,0x30,0x4f,0xdf,0x18,0xc2,0xff,0x26,0x1b,0x8a,0x61,0x8,0x52,0xf7,0x72,0x3b,0xca,0x3,0xb1,0x69,0x68,0x2c,0xa7,0xdd,0x7a,0x6f,0xe1,0x41,0x31,0x3a,0x81,0x9e,0xbc,0x40,0xb5,0x19,0xd3,0x57,0x5e,0x20,0x0,0xc7,0x6f,0xdf,0x96,0x45,0x47,0xd8,0x3f,0xc2,0x1b,0x49,0x3c,0x91,0x30,0xe1,0xa1,0x1e,0xc8,0xf7,0xb4,0x9f,0xee,0xac,0x7,0x97,0xe8,0x53,0xfe,0x27,0x1a,0xc0,0xd0,0xb9,0x52,0xc3,0xe3,0xaa,0x2f,0x8a,0xb1,0x69,0xdb,0x12,0x5,0x7f,0xf4,0xb0,0x99,0x39,0xb7,0xa2,0x46,0x59,0xe2,0xe9,0xb2,0x18,0x5f,0x15,0x85,0x9c,0x2c,0xad,0xd6,0x50,0xe,0xea,0x48,0x72,0x60,0x51,0x4b,0x36,0x55,0xf3,0xef,0xae,0x63,0x13,0x7d,0x87,0x9d,0x75,0x73,0xdd,0xec,0x82,0x10,0xb8,0xa5,0x5a,0xbf,0x6d,0xd9,0xe4,0xfd,0xa4,0x77,0x21,0x9e,0x2b,0xa7,0x79,0x7b,0xbe,0x38,0x4d,0xcc,0xa6,0x6a,0x93,0x78,0x4f,0x9,0x6,0x2,0xc9,0x23,0x25,0x54,0xc,0x95,0x6b,0x11,0x86,0x44,0x3d,0x5c,0xb3,0xf2,0x71,0x84,0x89,0xba,0xbd,0xa3,0xe0,0x1d,0xcb,0x24,0xaf,0x26,0x28,0xf6,0x29,0xfc,0x98,0xce,0xd2,0x31,0xf,0xcf,0xc5,0x33,0x7c,0x90,0x94,0x32,0xe7,0x8d,0x9a,0xff,0x88,0x5b,0x76,0x16,0x70,0x3b,0xd,0xeb,0x56,0xf0,0xf1,0xfb,0xa,0x41,0x1c,0xab,0xb6,0x80,0x92,0x43,0x6c,0x68,0xfa,0xdc,0x7e,0xd1,0xca,0xc1,0x17,0xc6,0x5d,0x9b,0x7a,0xda,0x3a,0xd5,0x1f,0x2d,0xa8,0xf9,0x65,0x58,0x37,0xd7,0x22,0xd4,0x3,0xcd,0x62,0xf5,0x83,0x8c,0xb,0x1,0x66,0x61,0xf8,0x4,0x64,0xde,0x2a,0xe6,0x8e,0x2e,0x74,0xed,0xc4,0x4c,0xa0,0x3e,0x4a,0x81,0xa9,0xbb,0x67,0x14,0x34,0x42,0x8f,0x6e,0x8b,0xe5,0x35,0x4e,0x8,0x62,0xc4,0x7c,0x1,0x54,0x24,0xd8,0x99,0xaa,0x42,0x4a,0xb0,0xdb,0xb5,0x44,0xea,0x68,0x22,0x85,0x2f,0x1b,0x9a,0xb2,0xab,0x39,0xdd,0xe1,0x67,0x57,0x66,0x7f,0x45,0xf,0x7a,0x4c,0x89,0x5d,0xa4,0xfb,0x91,0x3e,0x31,0x4f,0x78,0x14,0x12,0x35,0xfe,0x92,0x6d,0x27,0x8f,0xee,0xd3,0x88,0x5a,0x40,0x16,0xca,0x93,0x90,0x4e,0xa9,0x1c,0x2c,0x7e,0x8,0xf5,0x7,0xd6,0xb,0xa6,0xff,0xc0,0x96,0x29,0xd9,0x9b,0x83,0xa8,0x82,0x2e,0x8b,0x77,0x69,0x17,0xe4,0x60,0x58,0xe8,0x37,0xf0,0x70,0xef,0xa1,0x72,0xec,0x25,0x86,0x5e,0xc3,0x87,0x32,0x48,0x80,0x95,0xae,0xe,0xd5,0xde,0x71,0x6e,0xdf,0x64,0x30,0xa0,0x2d,0xf7,0xc9,0x10,0x65,0xf4,0xe7,0x8e,0x18,0xbd,0xd4,0x9d,0xce,0x52,0x1a,0x9f,0xe0,0x15,0x6f,0x0,0xfa,0x55,0xe3,0x34,0xbb,0x3c,0xc2,0xb4,0xeb,0x49,0x5f,0xcd,0xf6,0x20,0xe6,0xfd,0xac,0x4d,0xf1,0x6a,0xe2,0x28,0xed,0xd,0xb6,0x9e,0x9,0x7d,0x23,0x3,0x8c,0x50,0x59,0xbc,0x75,0xb8,0x79,0x3f,0xd2,0x2,0x56,0xcf,0x36,0x51,0xe9,0x1d,0x33,0x53,0x19,0x43,0xd1,0xb9,0x7b,0x97,0xda,0xf3,0x2a,0xfc,0x94,0xd7,0x11,0x1f,0x13,0x98,0xcb,0xaf,0xc1,0x1e,0x6,0x38,0xf9,0xe5,0xa2,0x5c,0x63,0x3b,0x73,0xa,0x26,0xb1,0xc5,0x46,0x6b,0x84,0x8d,0x8a,0xb3,0xbe,0xdc,0x61,0xc,0x3a,0xcc,0x3d,0xc7,0xc6,0x9c,0x81,0x76,0x2b,0x74,0x5b,0xb7,0xa5,0x4,0x4b,0xf8,0xf2,0x5,0xd0,0xa7,0xa3,0xc8,0xbf,0xba,0xad,0x21,0x47,0x6c,0x41,0x1c,0xdb,0x4,0xb4,0x9e,0x4d,0x3,0x9c,0x9b,0x67,0xc2,0x6e,0x8c,0x8,0xfb,0x85,0xc5,0x7a,0x2c,0x13,0x44,0x6f,0x77,0x35,0x19,0xe4,0x92,0xc0,0x4a,0xe7,0x3a,0xeb,0x62,0xb,0x18,0x89,0x71,0x38,0x51,0xf4,0x4c,0xdc,0x88,0x33,0xfc,0x25,0x1b,0xc1,0xe2,0x42,0x79,0x6c,0x82,0x9d,0x32,0x39,0xb2,0x6a,0xc9,0x0,0xa4,0xde,0x6b,0x2f,0x8b,0xd,0x31,0xd5,0xa9,0x93,0x8a,0xbb,0xc3,0x69,0xce,0x84,0x47,0x5e,0x76,0xf7,0x5c,0xa6,0xae,0x46,0x6,0xa8,0x59,0x37,0xed,0x90,0x28,0x8e,0x75,0x34,0xc8,0xb8,0x7f,0x26,0xfa,0xac,0xf0,0x45,0xa2,0x7c,0x63,0xcb,0x81,0x7e,0xb6,0x64,0x3f,0x2,0x94,0xa3,0xdd,0xd2,0x12,0xd9,0xfe,0xf8,0x65,0xa0,0x96,0xe3,0x7d,0x17,0x48,0xb1,0x68,0x87,0xaa,0x29,0x52,0x5f,0x66,0x61,0xd7,0x8f,0xb0,0x4e,0x5d,0xca,0xe6,0x9f,0xf2,0x2d,0x43,0x27,0x9,0x15,0xd4,0xea,0x3b,0x78,0x10,0xc6,0x74,0xff,0xf3,0xfd,0x41,0x56,0x53,0x24,0xad,0x80,0xab,0xcd,0x1e,0x14,0xa7,0xe8,0x4f,0x4b,0x3c,0xe9,0xc7,0x9a,0x6d,0x70,0x49,0x5b,0xb7,0x98,0xd6,0xe0,0x8d,0x30,0x2a,0x2b,0xd1,0x20,0x86,0x1d,0xa1,0x40,0xe1,0x1,0xc4,0xe,0x21,0xb3,0xa5,0x7,0x11,0xa,0xcc,0x1a,0xd8,0xf,0xb9,0x16,0x58,0x2e,0xd0,0x57,0x73,0xf6,0xbe,0x22,0xec,0x83,0xf9,0xc,0x55,0x3d,0xaf,0xf5,0x1f,0x36,0x7b,0x97,0xbd,0xda,0x23,0xba,0xbf,0xdf,0xf1,0x5,0x54,0x99,0x50,0xb5,0xee,0x3e,0xd3,0x95,0x91,0xe5,0x72,0x5a,0xbc,0x60,0xef,0xcf,0x7a,0xd8,0xfe,0x6c,0x13,0xc5,0xce,0xd5,0x7e,0x9f,0x59,0xc2,0x1b,0xd1,0x3e,0xde,0x61,0xfd,0xac,0x29,0x26,0xd3,0x33,0x5c,0x66,0xc9,0x7,0xd0,0xf,0x88,0x87,0xf1,0xfc,0x65,0x62,0x5,0x2e,0xda,0x60,0x0,0x70,0x2a,0x8a,0xe2,0xa4,0x48,0xc0,0xe9,0xad,0x85,0x4e,0x3a,0x30,0x10,0x63,0xbf,0x8f,0x6a,0x8b,0x46,0xc,0x4a,0x31,0xe1,0x6f,0x91,0x8,0x50,0x39,0x40,0x82,0x15,0x75,0xf6,0xb7,0x58,0xb9,0xbe,0x8d,0x80,0xcf,0x19,0xe4,0xa7,0x2c,0x22,0xab,0x20,0x9c,0xf8,0x2d,0xf2,0xb,0x35,0xd6,0xca,0x78,0x37,0xc1,0xcb,0xe3,0x36,0x90,0x94,0x8c,0xfb,0x9e,0x89,0x74,0x12,0x72,0x5f,0x52,0xef,0x9,0x3f,0xe,0xff,0xf5,0xf4,0xb2,0xaf,0x18,0x45,0x68,0x47,0x96,0x84,0x11,0x5b,0x1c,0xb6,0xa9,0x28,0x98,0x81,0xee,0xa,0x54,0xd2,0x55,0x64,0x76,0x4c,0xf7,0x51,0x32,0x4f,0x17,0x67,0xaa,0xeb,0x71,0x99,0x83,0x79,0x86,0xe8,0xd9,0x77,0x5e,0xa1,0xbc,0x14,0xe0,0xdd,0x69,0xbb,0x25,0x73,0xa0,0xf9,0x7d,0xa3,0x2f,0x9a,0x49,0x3c,0xba,0x7f,0x97,0x6e,0xa2,0xc8,0x2,0xd,0x4b,0x7c,0x21,0x27,0xcd,0x6,0x1d,0xb1,0x44,0xb8,0x24,0x5a,0x53,0xd7,0xdb,0x6b,0xc3,0x4,0xdc,0x43,0x41,0x92,0x4d,0x1f,0xc6,0x3b,0xe5,0x34,0x95,0x38,0xf3,0xcc,0x1a,0xa5,0xa8,0xea,0x9b,0xb0,0x57,0xec,0x93,0x3,0xc4,0x1e,0x23,0xfa,0xc7,0x56,0xbd,0xd4,0x8e,0x2b,0xae,0xe7,0x16,0xdf,0x6d,0xb5,0xb4,0xf0,0x7b,0x1,0xa6,0xb3,0x3d,0x9d,0xed,0xe6,0x5d,0x42,0x15,0xe9,0x1c,0xb0,0x7a,0xfe,0xf7,0x89,0xa9,0x6e,0xc6,0x76,0x3f,0xec,0xee,0x71,0x96,0x6b,0xb2,0xe0,0x95,0x38,0x99,0x48,0x8,0xb7,0x61,0x5e,0x1d,0x36,0x47,0x5,0xae,0x3e,0x41,0xfa,0x57,0x8e,0xb3,0x69,0x79,0x10,0xfb,0x6a,0x4a,0x3,0x86,0x23,0x18,0xc0,0x72,0xbb,0xac,0xd6,0x5d,0x19,0x30,0x90,0x1e,0xb,0xef,0xf0,0x4b,0x40,0x1b,0xb1,0xf6,0xbc,0x2c,0x35,0x85,0x4,0x7f,0xf9,0xa7,0x43,0xe1,0xdb,0xc9,0xf8,0xe2,0x9f,0xfc,0x5a,0x46,0x7,0xca,0xba,0xd4,0x2e,0x34,0xdc,0xda,0x74,0x45,0x2b,0xb9,0x11,0xc,0xf3,0x16,0xc4,0x70,0x4d,0x54,0xd,0xde,0x88,0x37,0x82,0xe,0xd0,0xd2,0x17,0x91,0xe4,0x65,0xf,0xc3,0x3a,0xd1,0xe6,0xa0,0xaf,0xab,0x60,0x8a,0x8c,0xfd,0xa5,0x3c,0xc2,0xb8,0x2f,0xed,0x94,0xf5,0x1a,0x5b,0xd8,0x2d,0x20,0x13,0x14,0xa,0x49,0xb4,0x62,0x8d,0x6,0x8f,0x81,0x5f,0x80,0x55,0x31,0x67,0x7b,0x98,0xa6,0x66,0x6c,0x9a,0xd5,0x39,0x3d,0x9b,0x4e,0x24,0x33,0x56,0x21,0xf2,0xdf,0xbf,0xd9,0x92,0xa4,0x42,0xff,0x59,0x58,0x52,0xa3,0xe8,0xb5,0x2,0x1f,0x29,0x3b,0xea,0xc5,0xc1,0x53,0x75,0xd7,0x78,0x63,0x68,0xbe,0x6f,0xf4,0x32,0xd3,0x73,0x93,0x7c,0xb6,0x84,0x1,0x50,0xcc,0xf1,0x9e,0x7e,0x8b,0x7d,0xaa,0x64,0xcb,0x5c,0x2a,0x25,0xa2,0xa8,0xcf,0xc8,0x51,0xad,0xcd,0x77,0x83,0x4f,0x27,0x87,0xdd,0x44,0x6d,0xe5,0x9,0x97,0xe3,0x28,0x0,0x12,0xce,0xbd,0x9d,0xeb,0x26,0xc7,0x22,0x4c,0x9c,0xe7,0xa1,0xe0,0x46,0xfe,0x83,0xd6,0xa6,0x5a,0x1b,0x28,0xc0,0xc8,0x32,0x59,0x37,0xc6,0x68,0xea,0xa0,0x7,0xad,0x99,0x18,0x30,0x29,0xbb,0x5f,0x63,0xe5,0xd5,0xe4,0xfd,0xc7,0x8d,0xf8,0xce,0xb,0xdf,0x26,0x79,0x13,0xbc,0xb3,0xcd,0xfa,0x96,0x90,0xb7,0x7c,0x10,0xef,0xa5,0xd,0x6c,0x51,0xa,0xd8,0xc2,0x94,0x48,0x11,0x12,0xcc,0x2b,0x9e,0xae,0xfc,0x8a,0x77,0x85,0x54,0x89,0x24,0x7d,0x42,0x14,0xab,0x5b,0x19,0x1,0x2a,0x0,0xac,0x9,0xf5,0xeb,0x95,0x66,0xe2,0xda,0x6a,0xb5,0x72,0xf2,0x6d,0x23,0xf0,0x6e,0xa7,0x4,0xdc,0x41,0x5,0xb0,0xca,0x2,0x17,0x2c,0x8c,0x57,0x5c,0xf3,0xec,0x5d,0xe6,0xb2,0x22,0xaf,0x75,0x4b,0x92,0xe7,0x76,0x65,0xc,0x9a,0x3f,0x56,0x1f,0x4c,0xd0,0x98,0x1d,0x62,0x97,0xed,0x82,0x78,0xd7,0x61,0xb6,0x39,0xbe,0x40,0x36,0x69,0xcb,0xdd,0x4f,0x74,0xa2,0x64,0x7f,0x2e,0xcf,0x73,0xe8,0x60,0xaa,0x6f,0x8f,0x34,0x1c,0x8b,0xff,0xa1,0x81,0xe,0xd2,0xdb,0x3e,0xf7,0x3a,0xfb,0xbd,0x50,0x80,0xd4,0x4d,0xb4,0xd3,0x6b,0x9f,0xb1,0xd1,0x9b,0xc1,0x53,0x3b,0xf9,0x15,0x58,0x71,0xa8,0x7e,0x16,0x55,0x93,0x9d,0x91,0x1a,0x49,0x2d,0x43,0x9c,0x84,0xba,0x7b,0x67,0x20,0xde,0xe1,0xb9,0xf1,0x88,0xa4,0x33,0x47,0xc4,0xe9,0x6,0xf,0x8,0x31,0x3c,0x5e,0xe3,0x8e,0xb8,0x4e,0xbf,0x45,0x44,0x1e,0x3,0xf4,0xa9,0xf6,0xd9,0x35,0x27,0x86,0xc9,0x7a,0x70,0x87,0x52,0x25,0x21,0x4a,0x3d,0x38,0x2f,0xa3,0xc5,0xee,0xc3,0x7f,0xb8,0x67,0xd7,0xfd,0x2e,0x60,0xff,0xf8,0x4,0xa1,0xd,0xef,0x6b,0x98,0xe6,0xa6,0x19,0x4f,0x70,0x27,0xc,0x14,0x56,0x7a,0x87,0xf1,0xa3,0x29,0x84,0x59,0x88,0x1,0x68,0x7b,0xea,0x12,0x5b,0x32,0x97,0x2f,0xbf,0xeb,0x50,0x9f,0x46,0x78,0xa2,0x81,0x21,0x1a,0xf,0xe1,0xfe,0x51,0x5a,0xd1,0x9,0xaa,0x63,0xc7,0xbd,0x8,0x4c,0xe8,0x6e,0x52,0xb6,0xca,0xf0,0xe9,0xd8,0xa0,0xa,0xad,0xe7,0x24,0x3d,0x15,0x94,0x3f,0xc5,0xcd,0x25,0x65,0xcb,0x3a,0x54,0x8e,0xf3,0x4b,0xed,0x16,0x57,0xab,0xdb,0x1c,0x45,0x99,0xcf,0x93,0x26,0xc1,0x1f,0x0,0xa8,0xe2,0x1d,0xd5,0x7,0x5c,0x61,0xf7,0xc0,0xbe,0xb1,0x71,0xba,0x9d,0x9b,0x6,0xc3,0xf5,0x80,0x1e,0x74,0x2b,0xd2,0xb,0xe4,0xc9,0x4a,0x31,0x3c,0x5,0x2,0xb4,0xec,0xd3,0x2d,0x3e,0xa9,0x85,0xfc,0x91,0x4e,0x20,0x44,0x6a,0x76,0xb7,0x89,0x58,0x1b,0x73,0xa5,0x17,0x9c,0x90,0x9e,0x22,0x35,0x30,0x47,0xce,0xe3,0xc8,0xae,0x7d,0x77,0xc4,0x8b,0x2c,0x28,0x5f,0x8a,0xa4,0xf9,0xe,0x13,0x2a,0x38,0xd4,0xfb,0xb5,0x83,0xee,0x53,0x49,0x48,0xb2,0x43,0xe5,0x7e,0xc2,0x23,0x82,0x62,0xa7,0x6d,0x42,0xd0,0xc6,0x64,0x72,0x69,0xaf,0x79,0xbb,0x6c,0xda,0x75,0x3b,0x4d,0xb3,0x34,0x10,0x95,0xdd,0x41,0x8f,0xe0,0x9a,0x6f,0x36,0x5e,0xcc,0x96,0x7c,0x55,0x18,0xf4,0xde,0xb9,0x40,0xd9,0xdc,0xbc,0x92,0x66,0x37,0xfa,0x33,0xd6,0x8d,0x5d,0xb0,0xf6,0xf2,0x86,0x11,0x39,0xdf,0x3,0x8c,0xac,0x24,0x86,0xa0,0x32,0x4d,0x9b,0x90,0x8b,0x20,0xc1,0x7,0x9c,0x45,0x8f,0x60,0x80,0x3f,0xa3,0xf2,0x77,0x78,0x8d,0x6d,0x2,0x38,0x97,0x59,0x8e,0x51,0xd6,0xd9,0xaf,0xa2,0x3b,0x3c,0x5b,0x70,0x84,0x3e,0x5e,0x2e,0x74,0xd4,0xbc,0xfa,0x16,0x9e,0xb7,0xf3,0xdb,0x10,0x64,0x6e,0x4e,0x3d,0xe1,0xd1,0x34,0xd5,0x18,0x52,0x14,0x6f,0xbf,0x31,0xcf,0x56,0xe,0x67,0x1e,0xdc,0x4b,0x2b,0xa8,0xe9,0x6,0xe7,0xe0,0xd3,0xde,0x91,0x47,0xba,0xf9,0x72,0x7c,0xf5,0x7e,0xc2,0xa6,0x73,0xac,0x55,0x6b,0x88,0x94,0x26,0x69,0x9f,0x95,0xbd,0x68,0xce,0xca,0xd2,0xa5,0xc0,0xd7,0x2a,0x4c,0x2c,0x1,0xc,0xb1,0x57,0x61,0x50,0xa1,0xab,0xaa,0xec,0xf1,0x46,0x1b,0x36,0x19,0xc8,0xda,0x4f,0x5,0x42,0xe8,0xf7,0x76,0xc6,0xdf,0xb0,0x54,0xa,0x8c,0xb,0x3a,0x28,0x12,0xa9,0xf,0x6c,0x11,0x49,0x39,0xf4,0xb5,0x2f,0xc7,0xdd,0x27,0xd8,0xb6,0x87,0x29,0x0,0xff,0xe2,0x4a,0xbe,0x83,0x37,0xe5,0x7b,0x2d,0xfe,0xa7,0x23,0xfd,0x71,0xc4,0x17,0x62,0xe4,0x21,0xc9,0x30,0xfc,0x96,0x5c,0x53,0x15,0x22,0x7f,0x79,0x93,0x58,0x43,0xef,0x1a,0xe6,0x7a,0x4,0xd,0x89,0x85,0x35,0x9d,0x5a,0x82,0x1d,0x1f,0xcc,0x13,0x41,0x98,0x65,0xbb,0x6a,0xcb,0x66,0xad,0x92,0x44,0xfb,0xf6,0xb4,0xc5,0xee,0x9,0xb2,0xcd,0x5d,0x9a,0x40,0x7d,0xa4,0x99,0x8,0xe3,0x8a,0xd0,0x75,0xf0,0xb9,0x48,0x81,0x33,0xeb,0xea,0xae,0x25,0x5f,0xf8,0xed,0x63,0xc3,0xb3,0xb8,0x3,0x1c,0x4a,0xb6,0x43,0xef,0x25,0xa1,0xa8,0xd6,0xf6,0x31,0x99,0x29,0x60,0xb3,0xb1,0x2e,0xc9,0x34,0xed,0xbf,0xca,0x67,0xc6,0x17,0x57,0xe8,0x3e,0x1,0x42,0x69,0x18,0x5a,0xf1,0x61,0x1e,0xa5,0x8,0xd1,0xec,0x36,0x26,0x4f,0xa4,0x35,0x15,0x5c,0xd9,0x7c,0x47,0x9f,0x2d,0xe4,0xf3,0x89,0x2,0x46,0x6f,0xcf,0x41,0x54,0xb0,0xaf,0x14,0x1f,0x44,0xee,0xa9,0xe3,0x73,0x6a,0xda,0x5b,0x20,0xa6,0xf8,0x1c,0xbe,0x84,0x96,0xa7,0xbd,0xc0,0xa3,0x5,0x19,0x58,0x95,0xe5,0x8b,0x71,0x6b,0x83,0x85,0x2b,0x1a,0x74,0xe6,0x4e,0x53,0xac,0x49,0x9b,0x2f,0x12,0xb,0x52,0x81,0xd7,0x68,0xdd,0x51,0x8f,0x8d,0x48,0xce,0xbb,0x3a,0x50,0x9c,0x65,0x8e,0xb9,0xff,0xf0,0xf4,0x3f,0xd5,0xd3,0xa2,0xfa,0x63,0x9d,0xe7,0x70,0xb2,0xcb,0xaa,0x45,0x4,0x87,0x72,0x7f,0x4c,0x4b,0x55,0x16,0xeb,0x3d,0xd2,0x59,0xd0,0xde,0x0,0xdf,0xa,0x6e,0x38,0x24,0xc7,0xf9,0x39,0x33,0xc5,0x8a,0x66,0x62,0xc4,0x11,0x7b,0x6c,0x9,0x7e,0xad,0x80,0xe0,0x86,0xcd,0xfb,0x1d,0xa0,0x6,0x7,0xd,0xfc,0xb7,0xea,0x5d,0x40,0x76,0x64,0xb5,0x9a,0x9e,0xc,0x2a,0x88,0x27,0x3c,0x37,0xe1,0x30,0xab,0x6d,0x8c,0x2c,0xcc,0x23,0xe9,0xdb,0x5e,0xf,0x93,0xae,0xc1,0x21,0xd4,0x22,0xf5,0x3b,0x94,0x3,0x75,0x7a,0xfd,0xf7,0x90,0x97,0xe,0xf2,0x92,0x28,0xdc,0x10,0x78,0xd8,0x82,0x1b,0x32,0xba,0x56,0xc8,0xbc,0x77,0x5f,0x4d,0x91,0xe2,0xc2,0xb4,0x79,0x98,0x7d,0x13,0xc3,0xb8,0xfe,0x5f,0xf9,0x41,0x3c,0x69,0x19,0xe5,0xa4,0x97,0x7f,0x77,0x8d,0xe6,0x88,0x79,0xd7,0x55,0x1f,0xb8,0x12,0x26,0xa7,0x8f,0x96,0x4,0xe0,0xdc,0x5a,0x6a,0x5b,0x42,0x78,0x32,0x47,0x71,0xb4,0x60,0x99,0xc6,0xac,0x3,0xc,0x72,0x45,0x29,0x2f,0x8,0xc3,0xaf,0x50,0x1a,0xb2,0xd3,0xee,0xb5,0x67,0x7d,0x2b,0xf7,0xae,0xad,0x73,0x94,0x21,0x11,0x43,0x35,0xc8,0x3a,0xeb,0x36,0x9b,0xc2,0xfd,0xab,0x14,0xe4,0xa6,0xbe,0x95,0xbf,0x13,0xb6,0x4a,0x54,0x2a,0xd9,0x5d,0x65,0xd5,0xa,0xcd,0x4d,0xd2,0x9c,0x4f,0xd1,0x18,0xbb,0x63,0xfe,0xba,0xf,0x75,0xbd,0xa8,0x93,0x33,0xe8,0xe3,0x4c,0x53,0xe2,0x59,0xd,0x9d,0x10,0xca,0xf4,0x2d,0x58,0xc9,0xda,0xb3,0x25,0x80,0xe9,0xa0,0xf3,0x6f,0x27,0xa2,0xdd,0x28,0x52,0x3d,0xc7,0x68,0xde,0x9,0x86,0x1,0xff,0x89,0xd6,0x74,0x62,0xf0,0xcb,0x1d,0xdb,0xc0,0x91,0x70,0xcc,0x57,0xdf,0x15,0xd0,0x30,0x8b,0xa3,0x34,0x40,0x1e,0x3e,0xb1,0x6d,0x64,0x81,0x48,0x85,0x44,0x2,0xef,0x3f,0x6b,0xf2,0xb,0x6c,0xd4,0x20,0xe,0x6e,0x24,0x7e,0xec,0x84,0x46,0xaa,0xe7,0xce,0x17,0xc1,0xa9,0xea,0x2c,0x22,0x2e,0xa5,0xf6,0x92,0xfc,0x23,0x3b,0x5,0xc4,0xd8,0x9f,0x61,0x5e,0x6,0x4e,0x37,0x1b,0x8c,0xf8,0x7b,0x56,0xb9,0xb0,0xb7,0x8e,0x83,0xe1,0x5c,0x31,0x7,0xf1,0x0,0xfa,0xfb,0xa1,0xbc,0x4b,0x16,0x49,0x66,0x8a,0x98,0x39,0x76,0xc5,0xcf,0x38,0xed,0x9a,0x9e,0xf5,0x82,0x87,0x90,0x1c,0x7a,0x51,0x7c,0x1,0xc6,0x19,0xa9,0x83,0x50,0x1e,0x81,0x86,0x7a,0xdf,0x73,0x91,0x15,0xe6,0x98,0xd8,0x67,0x31,0xe,0x59,0x72,0x6a,0x28,0x4,0xf9,0x8f,0xdd,0x57,0xfa,0x27,0xf6,0x7f,0x16,0x5,0x94,0x6c,0x25,0x4c,0xe9,0x51,0xc1,0x95,0x2e,0xe1,0x38,0x6,0xdc,0xff,0x5f,0x64,0x71,0x9f,0x80,0x2f,0x24,0xaf,0x77,0xd4,0x1d,0xb9,0xc3,0x76,0x32,0x96,0x10,0x2c,0xc8,0xb4,0x8e,0x97,0xa6,0xde,0x74,0xd3,0x99,0x5a,0x43,0x6b,0xea,0x41,0xbb,0xb3,0x5b,0x1b,0xb5,0x44,0x2a,0xf0,0x8d,0x35,0x93,0x68,0x29,0xd5,0xa5,0x62,0x3b,0xe7,0xb1,0xed,0x58,0xbf,0x61,0x7e,0xd6,0x9c,0x63,0xab,0x79,0x22,0x1f,0x89,0xbe,0xc0,0xcf,0xf,0xc4,0xe3,0xe5,0x78,0xbd,0x8b,0xfe,0x60,0xa,0x55,0xac,0x75,0x9a,0xb7,0x34,0x4f,0x42,0x7b,0x7c,0xca,0x92,0xad,0x53,0x40,0xd7,0xfb,0x82,0xef,0x30,0x5e,0x3a,0x14,0x8,0xc9,0xf7,0x26,0x65,0xd,0xdb,0x69,0xe2,0xee,0xe0,0x5c,0x4b,0x4e,0x39,0xb0,0x9d,0xb6,0xd0,0x3,0x9,0xba,0xf5,0x52,0x56,0x21,0xf4,0xda,0x87,0x70,0x6d,0x54,0x46,0xaa,0x85,0xcb,0xfd,0x90,0x2d,0x37,0x36,0xcc,0x3d,0x9b,0x0,0xbc,0x5d,0xfc,0x1c,0xd9,0x13,0x3c,0xae,0xb8,0x1a,0xc,0x17,0xd1,0x7,0xc5,0x12,0xa4,0xb,0x45,0x33,0xcd,0x4a,0x6e,0xeb,0xa3,0x3f,0xf1,0x9e,0xe4,0x11,0x48,0x20,0xb2,0xe8,0x2,0x2b,0x66,0x8a,0xa0,0xc7,0x3e,0xa7,0xa2,0xc2,0xec,0x18,0x49,0x84,0x4d,0xa8,0xf3,0x23,0xce,0x88,0x8c,0xf8,0x6f,0x47,0xa1,0x7d,0xf2,0xd2,0x7,0xa5,0x83,0x11,0x6e,0xb8,0xb3,0xa8,0x3,0xe2,0x24,0xbf,0x66,0xac,0x43,0xa3,0x1c,0x80,0xd1,0x54,0x5b,0xae,0x4e,0x21,0x1b,0xb4,0x7a,0xad,0x72,0xf5,0xfa,0x8c,0x81,0x18,0x1f,0x78,0x53,0xa7,0x1d,0x7d,0xd,0x57,0xf7,0x9f,0xd9,0x35,0xbd,0x94,0xd0,0xf8,0x33,0x47,0x4d,0x6d,0x1e,0xc2,0xf2,0x17,0xf6,0x3b,0x71,0x37,0x4c,0x9c,0x12,0xec,0x75,0x2d,0x44,0x3d,0xff,0x68,0x8,0x8b,0xca,0x25,0xc4,0xc3,0xf0,0xfd,0xb2,0x64,0x99,0xda,0x51,0x5f,0xd6,0x5d,0xe1,0x85,0x50,0x8f,0x76,0x48,0xab,0xb7,0x5,0x4a,0xbc,0xb6,0x9e,0x4b,0xed,0xe9,0xf1,0x86,0xe3,0xf4,0x9,0x6f,0xf,0x22,0x2f,0x92,0x74,0x42,0x73,0x82,0x88,0x89,0xcf,0xd2,0x65,0x38,0x15,0x3a,0xeb,0xf9,0x6c,0x26,0x61,0xcb,0xd4,0x55,0xe5,0xfc,0x93,0x77,0x29,0xaf,0x28,0x19,0xb,0x31,0x8a,0x2c,0x4f,0x32,0x6a,0x1a,0xd7,0x96,0xc,0xe4,0xfe,0x4,0xfb,0x95,0xa4,0xa,0x23,0xdc,0xc1,0x69,0x9d,0xa0,0x14,0xc6,0x58,0xe,0xdd,0x84,0x0,0xde,0x52,0xe7,0x34,0x41,0xc7,0x2,0xea,0x13,0xdf,0xb5,0x7f,0x70,0x36,0x1,0x5c,0x5a,0xb0,0x7b,0x60,0xcc,0x39,0xc5,0x59,0x27,0x2e,0xaa,0xa6,0x16,0xbe,0x79,0xa1,0x3e,0x3c,0xef,0x30,0x62,0xbb,0x46,0x98,0x49,0xe8,0x45,0x8e,0xb1,0x67,0xd8,0xd5,0x97,0xe6,0xcd,0x2a,0x91,0xee,0x7e,0xb9,0x63,0x5e,0x87,0xba,0x2b,0xc0,0xa9,0xf3,0x56,0xd3,0x9a,0x6b,0xa2,0x10,0xc8,0xc9,0x8d,0x6,0x7c,0xdb,0xce,0x40,0xe0,0x90,0x9b,0x20,0x3f,0xf9,0x5,0xf0,0x5c,0x96,0x12,0x1b,0x65,0x45,0x82,0x2a,0x9a,0xd3,0x0,0x2,0x9d,0x7a,0x87,0x5e,0xc,0x79,0xd4,0x75,0xa4,0xe4,0x5b,0x8d,0xb2,0xf1,0xda,0xab,0xe9,0x42,0xd2,0xad,0x16,0xbb,0x62,0x5f,0x85,0x95,0xfc,0x17,0x86,0xa6,0xef,0x6a,0xcf,0xf4,0x2c,0x9e,0x57,0x40,0x3a,0xb1,0xf5,0xdc,0x7c,0xf2,0xe7,0x3,0x1c,0xa7,0xac,0xf7,0x5d,0x1a,0x50,0xc0,0xd9,0x69,0xe8,0x93,0x15,0x4b,0xaf,0xd,0x37,0x25,0x14,0xe,0x73,0x10,0xb6,0xaa,0xeb,0x26,0x56,0x38,0xc2,0xd8,0x30,0x36,0x98,0xa9,0xc7,0x55,0xfd,0xe0,0x1f,0xfa,0x28,0x9c,0xa1,0xb8,0xe1,0x32,0x64,0xdb,0x6e,0xe2,0x3c,0x3e,0xfb,0x7d,0x8,0x89,0xe3,0x2f,0xd6,0x3d,0xa,0x4c,0x43,0x47,0x8c,0x66,0x60,0x11,0x49,0xd0,0x2e,0x54,0xc3,0x1,0x78,0x19,0xf6,0xb7,0x34,0xc1,0xcc,0xff,0xf8,0xe6,0xa5,0x58,0x8e,0x61,0xea,0x63,0x6d,0xb3,0x6c,0xb9,0xdd,0x8b,0x97,0x74,0x4a,0x8a,0x80,0x76,0x39,0xd5,0xd1,0x77,0xa2,0xc8,0xdf,0xba,0xcd,0x1e,0x33,0x53,0x35,0x7e,0x48,0xae,0x13,0xb5,0xb4,0xbe,0x4f,0x4,0x59,0xee,0xf3,0xc5,0xd7,0x6,0x29,0x2d,0xbf,0x99,0x3b,0x94,0x8f,0x84,0x52,0x83,0x18,0xde,0x3f,0x9f,0x7f,0x90,0x5a,0x68,0xed,0xbc,0x20,0x1d,0x72,0x92,0x67,0x91,0x46,0x88,0x27,0xb0,0xc6,0xc9,0x4e,0x44,0x23,0x24,0xbd,0x41,0x21,0x9b,0x6f,0xa3,0xcb,0x6b,0x31,0xa8,0x81,0x9,0xe5,0x7b,0xf,0xc4,0xec,0xfe,0x22,0x51,0x71,0x7,0xca,0x2b,0xce,0xa0,0x70,0xb,0x4d,0x87,0x21,0x99,0xe4,0xb1,0xc1,0x3d,0x7c,0x4f,0xa7,0xaf,0x55,0x3e,0x50,0xa1,0xf,0x8d,0xc7,0x60,0xca,0xfe,0x7f,0x57,0x4e,0xdc,0x38,0x4,0x82,0xb2,0x83,0x9a,0xa0,0xea,0x9f,0xa9,0x6c,0xb8,0x41,0x1e,0x74,0xdb,0xd4,0xaa,0x9d,0xf1,0xf7,0xd0,0x1b,0x77,0x88,0xc2,0x6a,0xb,0x36,0x6d,0xbf,0xa5,0xf3,0x2f,0x76,0x75,0xab,0x4c,0xf9,0xc9,0x9b,0xed,0x10,0xe2,0x33,0xee,0x43,0x1a,0x25,0x73,0xcc,0x3c,0x7e,0x66,0x4d,0x67,0xcb,0x6e,0x92,0x8c,0xf2,0x1,0x85,0xbd,0xd,0xd2,0x15,0x95,0xa,0x44,0x97,0x9,0xc0,0x63,0xbb,0x26,0x62,0xd7,0xad,0x65,0x70,0x4b,0xeb,0x30,0x3b,0x94,0x8b,0x3a,0x81,0xd5,0x45,0xc8,0x12,0x2c,0xf5,0x80,0x11,0x2,0x6b,0xfd,0x58,0x31,0x78,0x2b,0xb7,0xff,0x7a,0x5,0xf0,0x8a,0xe5,0x1f,0xb0,0x6,0xd1,0x5e,0xd9,0x27,0x51,0xe,0xac,0xba,0x28,0x13,0xc5,0x3,0x18,0x49,0xa8,0x14,0x8f,0x7,0xcd,0x8,0xe8,0x53,0x7b,0xec,0x98,0xc6,0xe6,0x69,0xb5,0xbc,0x59,0x90,0x5d,0x9c,0xda,0x37,0xe7,0xb3,0x2a,0xd3,0xb4,0xc,0xf8,0xd6,0xb6,0xfc,0xa6,0x34,0x5c,0x9e,0x72,0x3f,0x16,0xcf,0x19,0x71,0x32,0xf4,0xfa,0xf6,0x7d,0x2e,0x4a,0x24,0xfb,0xe3,0xdd,0x1c,0x0,0x47,0xb9,0x86,0xde,0x96,0xef,0xc3,0x54,0x20,0xa3,0x8e,0x61,0x68,0x6f,0x56,0x5b,0x39,0x84,0xe9,0xdf,0x29,0xd8,0x22,0x23,0x79,0x64,0x93,0xce,0x91,0xbe,0x52,0x40,0xe1,0xae,0x1d,0x17,0xe0,0x35,0x42,0x46,0x2d,0x5a,0x5f,0x48,0xc4,0xa2,0x89,0xa4,0xe4,0x23,0xfc,0x4c,0x66,0xb5,0xfb,0x64,0x63,0x9f,0x3a,0x96,0x74,0xf0,0x3,0x7d,0x3d,0x82,0xd4,0xeb,0xbc,0x97,0x8f,0xcd,0xe1,0x1c,0x6a,0x38,0xb2,0x1f,0xc2,0x13,0x9a,0xf3,0xe0,0x71,0x89,0xc0,0xa9,0xc,0xb4,0x24,0x70,0xcb,0x4,0xdd,0xe3,0x39,0x1a,0xba,0x81,0x94,0x7a,0x65,0xca,0xc1,0x4a,0x92,0x31,0xf8,0x5c,0x26,0x93,0xd7,0x73,0xf5,0xc9,0x2d,0x51,0x6b,0x72,0x43,0x3b,0x91,0x36,0x7c,0xbf,0xa6,0x8e,0xf,0xa4,0x5e,0x56,0xbe,0xfe,0x50,0xa1,0xcf,0x15,0x68,0xd0,0x76,0x8d,0xcc,0x30,0x40,0x87,0xde,0x2,0x54,0x8,0xbd,0x5a,0x84,0x9b,0x33,0x79,0x86,0x4e,0x9c,0xc7,0xfa,0x6c,0x5b,0x25,0x2a,0xea,0x21,0x6,0x0,0x9d,0x58,0x6e,0x1b,0x85,0xef,0xb0,0x49,0x90,0x7f,0x52,0xd1,0xaa,0xa7,0x9e,0x99,0x2f,0x77,0x48,0xb6,0xa5,0x32,0x1e,0x67,0xa,0xd5,0xbb,0xdf,0xf1,0xed,0x2c,0x12,0xc3,0x80,0xe8,0x3e,0x8c,0x7,0xb,0x5,0xb9,0xae,0xab,0xdc,0x55,0x78,0x53,0x35,0xe6,0xec,0x5f,0x10,0xb7,0xb3,0xc4,0x11,0x3f,0x62,0x95,0x88,0xb1,0xa3,0x4f,0x60,0x2e,0x18,0x75,0xc8,0xd2,0xd3,0x29,0xd8,0x7e,0xe5,0x59,0xb8,0x19,0xf9,0x3c,0xf6,0xd9,0x4b,0x5d,0xff,0xe9,0xf2,0x34,0xe2,0x20,0xf7,0x41,0xee,0xa0,0xd6,0x28,0xaf,0x8b,0xe,0x46,0xda,0x14,0x7b,0x1,0xf4,0xad,0xc5,0x57,0xd,0xe7,0xce,0x83,0x6f,0x45,0x22,0xdb,0x42,0x47,0x27,0x9,0xfd,0xac,0x61,0xa8,0x4d,0x16,0xc6,0x2b,0x6d,0x69,0x1d,0x8a,0xa2,0x44,0x98,0x17,0x37,0x7f,0xdd,0xfb,0x69,0x16,0xc0,0xcb,0xd0,0x7b,0x9a,0x5c,0xc7,0x1e,0xd4,0x3b,0xdb,0x64,0xf8,0xa9,0x2c,0x23,0xd6,0x36,0x59,0x63,0xcc,0x2,0xd5,0xa,0x8d,0x82,0xf4,0xf9,0x60,0x67,0x0,0x2b,0xdf,0x65,0x5,0x75,0x2f,0x8f,0xe7,0xa1,0x4d,0xc5,0xec,0xa8,0x80,0x4b,0x3f,0x35,0x15,0x66,0xba,0x8a,0x6f,0x8e,0x43,0x9,0x4f,0x34,0xe4,0x6a,0x94,0xd,0x55,0x3c,0x45,0x87,0x10,0x70,0xf3,0xb2,0x5d,0xbc,0xbb,0x88,0x85,0xca,0x1c,0xe1,0xa2,0x29,0x27,0xae,0x25,0x99,0xfd,0x28,0xf7,0xe,0x30,0xd3,0xcf,0x7d,0x32,0xc4,0xce,0xe6,0x33,0x95,0x91,0x89,0xfe,0x9b,0x8c,0x71,0x17,0x77,0x5a,0x57,0xea,0xc,0x3a,0xb,0xfa,0xf0,0xf1,0xb7,0xaa,0x1d,0x40,0x6d,0x42,0x93,0x81,0x14,0x5e,0x19,0xb3,0xac,0x2d,0x9d,0x84,0xeb,0xf,0x51,0xd7,0x50,0x61,0x73,0x49,0xf2,0x54,0x37,0x4a,0x12,0x62,0xaf,0xee,0x74,0x9c,0x86,0x7c,0x83,0xed,0xdc,0x72,0x5b,0xa4,0xb9,0x11,0xe5,0xd8,0x6c,0xbe,0x20,0x76,0xa5,0xfc,0x78,0xa6,0x2a,0x9f,0x4c,0x39,0xbf,0x7a,0x92,0x6b,0xa7,0xcd,0x7,0x8,0x4e,0x79,0x24,0x22,0xc8,0x3,0x18,0xb4,0x41,0xbd,0x21,0x5f,0x56,0xd2,0xde,0x6e,0xc6,0x1,0xd9,0x46,0x44,0x97,0x48,0x1a,0xc3,0x3e,0xe0,0x31,0x90,0x3d,0xf6,0xc9,0x1f,0xa0,0xad,0xef,0x9e,0xb5,0x52,0xe9,0x96,0x6,0xc1,0x1b,0x26,0xff,0xc2,0x53,0xb8,0xd1,0x8b,0x2e,0xab,0xe2,0x13,0xda,0x68,0xb0,0xb1,0xf5,0x7e,0x4,0xa3,0xb6,0x38,0x98,0xe8,0xe3,0x58,0x47,0x97,0x6b,0x9e,0x32,0xf8,0x7c,0x75,0xb,0x2b,0xec,0x44,0xf4,0xbd,0x6e,0x6c,0xf3,0x14,0xe9,0x30,0x62,0x17,0xba,0x1b,0xca,0x8a,0x35,0xe3,0xdc,0x9f,0xb4,0xc5,0x87,0x2c,0xbc,0xc3,0x78,0xd5,0xc,0x31,0xeb,0xfb,0x92,0x79,0xe8,0xc8,0x81,0x4,0xa1,0x9a,0x42,0xf0,0x39,0x2e,0x54,0xdf,0x9b,0xb2,0x12,0x9c,0x89,0x6d,0x72,0xc9,0xc2,0x99,0x33,0x74,0x3e,0xae,0xb7,0x7,0x86,0xfd,0x7b,0x25,0xc1,0x63,0x59,0x4b,0x7a,0x60,0x1d,0x7e,0xd8,0xc4,0x85,0x48,0x38,0x56,0xac,0xb6,0x5e,0x58,0xf6,0xc7,0xa9,0x3b,0x93,0x8e,0x71,0x94,0x46,0xf2,0xcf,0xd6,0x8f,0x5c,0xa,0xb5,0x0,0x8c,0x52,0x50,0x95,0x13,0x66,0xe7,0x8d,0x41,0xb8,0x53,0x64,0x22,0x2d,0x29,0xe2,0x8,0xe,0x7f,0x27,0xbe,0x40,0x3a,0xad,0x6f,0x16,0x77,0x98,0xd9,0x5a,0xaf,0xa2,0x91,0x96,0x88,0xcb,0x36,0xe0,0xf,0x84,0xd,0x3,0xdd,0x2,0xd7,0xb3,0xe5,0xf9,0x1a,0x24,0xe4,0xee,0x18,0x57,0xbb,0xbf,0x19,0xcc,0xa6,0xb1,0xd4,0xa3,0x70,0x5d,0x3d,0x5b,0x10,0x26,0xc0,0x7d,0xdb,0xda,0xd0,0x21,0x6a,0x37,0x80,0x9d,0xab,0xb9,0x68,0x47,0x43,0xd1,0xf7,0x55,0xfa,0xe1,0xea,0x3c,0xed,0x76,0xb0,0x51,0xf1,0x11,0xfe,0x34,0x6,0x83,0xd2,0x4e,0x73,0x1c,0xfc,0x9,0xff,0x28,0xe6,0x49,0xde,0xa8,0xa7,0x20,0x2a,0x4d,0x4a,0xd3,0x2f,0x4f,0xf5,0x1,0xcd,0xa5,0x5,0x5f,0xc6,0xef,0x67,0x8b,0x15,0x61,0xaa,0x82,0x90,0x4c,0x3f,0x1f,0x69,0xa4,0x45,0xa0,0xce,0x1e,0x65,0x23,0x6a,0xcc,0x74,0x9,0x5c,0x2c,0xd0,0x91,0xa2,0x4a,0x42,0xb8,0xd3,0xbd,0x4c,0xe2,0x60,0x2a,0x8d,0x27,0x13,0x92,0xba,0xa3,0x31,0xd5,0xe9,0x6f,0x5f,0x6e,0x77,0x4d,0x7,0x72,0x44,0x81,0x55,0xac,0xf3,0x99,0x36,0x39,0x47,0x70,0x1c,0x1a,0x3d,0xf6,0x9a,0x65,0x2f,0x87,0xe6,0xdb,0x80,0x52,0x48,0x1e,0xc2,0x9b,0x98,0x46,0xa1,0x14,0x24,0x76,0x0,0xfd,0xf,0xde,0x3,0xae,0xf7,0xc8,0x9e,0x21,0xd1,0x93,0x8b,0xa0,0x8a,0x26,0x83,0x7f,0x61,0x1f,0xec,0x68,0x50,0xe0,0x3f,0xf8,0x78,0xe7,0xa9,0x7a,0xe4,0x2d,0x8e,0x56,0xcb,0x8f,0x3a,0x40,0x88,0x9d,0xa6,0x6,0xdd,0xd6,0x79,0x66,0xd7,0x6c,0x38,0xa8,0x25,0xff,0xc1,0x18,0x6d,0xfc,0xef,0x86,0x10,0xb5,0xdc,0x95,0xc6,0x5a,0x12,0x97,0xe8,0x1d,0x67,0x8,0xf2,0x5d,0xeb,0x3c,0xb3,0x34,0xca,0xbc,0xe3,0x41,0x57,0xc5,0xfe,0x28,0xee,0xf5,0xa4,0x45,0xf9,0x62,0xea,0x20,0xe5,0x5,0xbe,0x96,0x1,0x75,0x2b,0xb,0x84,0x58,0x51,0xb4,0x7d,0xb0,0x71,0x37,0xda,0xa,0x5e,0xc7,0x3e,0x59,0xe1,0x15,0x3b,0x5b,0x11,0x4b,0xd9,0xb1,0x73,0x9f,0xd2,0xfb,0x22,0xf4,0x9c,0xdf,0x19,0x17,0x1b,0x90,0xc3,0xa7,0xc9,0x16,0xe,0x30,0xf1,0xed,0xaa,0x54,0x6b,0x33,0x7b,0x2,0x2e,0xb9,0xcd,0x4e,0x63,0x8c,0x85,0x82,0xbb,0xb6,0xd4,0x69,0x4,0x32,0xc4,0x35,0xcf,0xce,0x94,0x89,0x7e,0x23,0x7c,0x53,0xbf,0xad,0xc,0x43,0xf0,0xfa,0xd,0xd8,0xaf,0xab,0xc0,0xb7,0xb2,0xa5,0x29,0x4f,0x64,0x49,0x8e,0x49,0x96,0x26,0xc,0xdf,0x91,0xe,0x9,0xf5,0x50,0xfc,0x1e,0x9a,0x69,0x17,0x57,0xe8,0xbe,0x81,0xd6,0xfd,0xe5,0xa7,0x8b,0x76,0x0,0x52,0xd8,0x75,0xa8,0x79,0xf0,0x99,0x8a,0x1b,0xe3,0xaa,0xc3,0x66,0xde,0x4e,0x1a,0xa1,0x6e,0xb7,0x89,0x53,0x70,0xd0,0xeb,0xfe,0x10,0xf,0xa0,0xab,0x20,0xf8,0x5b,0x92,0x36,0x4c,0xf9,0xbd,0x19,0x9f,0xa3,0x47,0x3b,0x1,0x18,0x29,0x51,0xfb,0x5c,0x16,0xd5,0xcc,0xe4,0x65,0xce,0x34,0x3c,0xd4,0x94,0x3a,0xcb,0xa5,0x7f,0x2,0xba,0x1c,0xe7,0xa6,0x5a,0x2a,0xed,0xb4,0x68,0x3e,0x62,0xd7,0x30,0xee,0xf1,0x59,0x13,0xec,0x24,0xf6,0xad,0x90,0x6,0x31,0x4f,0x40,0x80,0x4b,0x6c,0x6a,0xf7,0x32,0x4,0x71,0xef,0x85,0xda,0x23,0xfa,0x15,0x38,0xbb,0xc0,0xcd,0xf4,0xf3,0x45,0x1d,0x22,0xdc,0xcf,0x58,0x74,0xd,0x60,0xbf,0xd1,0xb5,0x9b,0x87,0x46,0x78,0xa9,0xea,0x82,0x54,0xe6,0x6d,0x61,0x6f,0xd3,0xc4,0xc1,0xb6,0x3f,0x12,0x39,0x5f,0x8c,0x86,0x35,0x7a,0xdd,0xd9,0xae,0x7b,0x55,0x8,0xff,0xe2,0xdb,0xc9,0x25,0xa,0x44,0x72,0x1f,0xa2,0xb8,0xb9,0x43,0xb2,0x14,0x8f,0x33,0xd2,0x73,0x93,0x56,0x9c,0xb3,0x21,0x37,0x95,0x83,0x98,0x5e,0x88,0x4a,0x9d,0x2b,0x84,0xca,0xbc,0x42,0xc5,0xe1,0x64,0x2c,0xb0,0x7e,0x11,0x6b,0x9e,0xc7,0xaf,0x3d,0x67,0x8d,0xa4,0xe9,0x5,0x2f,0x48,0xb1,0x28,0x2d,0x4d,0x63,0x97,0xc6,0xb,0xc2,0x27,0x7c,0xac,0x41,0x7,0x3,0x77,0xe0,0xc8,0x2e,0xf2,0x7d,0x5d,0xa4,0x6,0x20,0xb2,0xcd,0x1b,0x10,0xb,0xa0,0x41,0x87,0x1c,0xc5,0xf,0xe0,0x0,0xbf,0x23,0x72,0xf7,0xf8,0xd,0xed,0x82,0xb8,0x17,0xd9,0xe,0xd1,0x56,0x59,0x2f,0x22,0xbb,0xbc,0xdb,0xf0,0x4,0xbe,0xde,0xae,0xf4,0x54,0x3c,0x7a,0x96,0x1e,0x37,0x73,0x5b,0x90,0xe4,0xee,0xce,0xbd,0x61,0x51,0xb4,0x55,0x98,0xd2,0x94,0xef,0x3f,0xb1,0x4f,0xd6,0x8e,0xe7,0x9e,0x5c,0xcb,0xab,0x28,0x69,0x86,0x67,0x60,0x53,0x5e,0x11,0xc7,0x3a,0x79,0xf2,0xfc,0x75,0xfe,0x42,0x26,0xf3,0x2c,0xd5,0xeb,0x8,0x14,0xa6,0xe9,0x1f,0x15,0x3d,0xe8,0x4e,0x4a,0x52,0x25,0x40,0x57,0xaa,0xcc,0xac,0x81,0x8c,0x31,0xd7,0xe1,0xd0,0x21,0x2b,0x2a,0x6c,0x71,0xc6,0x9b,0xb6,0x99,0x48,0x5a,0xcf,0x85,0xc2,0x68,0x77,0xf6,0x46,0x5f,0x30,0xd4,0x8a,0xc,0x8b,0xba,0xa8,0x92,0x29,0x8f,0xec,0x91,0xc9,0xb9,0x74,0x35,0xaf,0x47,0x5d,0xa7,0x58,0x36,0x7,0xa9,0x80,0x7f,0x62,0xca,0x3e,0x3,0xb7,0x65,0xfb,0xad,0x7e,0x27,0xa3,0x7d,0xf1,0x44,0x97,0xe2,0x64,0xa1,0x49,0xb0,0x7c,0x16,0xdc,0xd3,0x95,0xa2,0xff,0xf9,0x13,0xd8,0xc3,0x6f,0x9a,0x66,0xfa,0x84,0x8d,0x9,0x5,0xb5,0x1d,0xda,0x2,0x9d,0x9f,0x4c,0x93,0xc1,0x18,0xe5,0x3b,0xea,0x4b,0xe6,0x2d,0x12,0xc4,0x7b,0x76,0x34,0x45,0x6e,0x89,0x32,0x4d,0xdd,0x1a,0xc0,0xfd,0x24,0x19,0x88,0x63,0xa,0x50,0xf5,0x70,0x39,0xc8,0x1,0xb3,0x6b,0x6a,0x2e,0xa5,0xdf,0x78,0x6d,0xe3,0x43,0x33,0x38,0x83,0x9c,0x6f,0x93,0x66,0xca,0x0,0x84,0x8d,0xf3,0xd3,0x14,0xbc,0xc,0x45,0x96,0x94,0xb,0xec,0x11,0xc8,0x9a,0xef,0x42,0xe3,0x32,0x72,0xcd,0x1b,0x24,0x67,0x4c,0x3d,0x7f,0xd4,0x44,0x3b,0x80,0x2d,0xf4,0xc9,0x13,0x3,0x6a,0x81,0x10,0x30,0x79,0xfc,0x59,0x62,0xba,0x8,0xc1,0xd6,0xac,0x27,0x63,0x4a,0xea,0x64,0x71,0x95,0x8a,0x31,0x3a,0x61,0xcb,0x8c,0xc6,0x56,0x4f,0xff,0x7e,0x5,0x83,0xdd,0x39,0x9b,0xa1,0xb3,0x82,0x98,0xe5,0x86,0x20,0x3c,0x7d,0xb0,0xc0,0xae,0x54,0x4e,0xa6,0xa0,0xe,0x3f,0x51,0xc3,0x6b,0x76,0x89,0x6c,0xbe,0xa,0x37,0x2e,0x77,0xa4,0xf2,0x4d,0xf8,0x74,0xaa,0xa8,0x6d,0xeb,0x9e,0x1f,0x75,0xb9,0x40,0xab,0x9c,0xda,0xd5,0xd1,0x1a,0xf0,0xf6,0x87,0xdf,0x46,0xb8,0xc2,0x55,0x97,0xee,0x8f,0x60,0x21,0xa2,0x57,0x5a,0x69,0x6e,0x70,0x33,0xce,0x18,0xf7,0x7c,0xf5,0xfb,0x25,0xfa,0x2f,0x4b,0x1d,0x1,0xe2,0xdc,0x1c,0x16,0xe0,0xaf,0x43,0x47,0xe1,0x34,0x5e,0x49,0x2c,0x5b,0x88,0xa5,0xc5,0xa3,0xe8,0xde,0x38,0x85,0x23,0x22,0x28,0xd9,0x92,0xcf,0x78,0x65,0x53,0x41,0x90,0xbf,0xbb,0x29,0xf,0xad,0x2,0x19,0x12,0xc4,0x15,0x8e,0x48,0xa9,0x9,0xe9,0x6,0xcc,0xfe,0x7b,0x2a,0xb6,0x8b,0xe4,0x4,0xf1,0x7,0xd0,0x1e,0xb1,0x26,0x50,0x5f,0xd8,0xd2,0xb5,0xb2,0x2b,0xd7,0xb7,0xd,0xf9,0x35,0x5d,0xfd,0xa7,0x3e,0x17,0x9f,0x73,0xed,0x99,0x52,0x7a,0x68,0xb4,0xc7,0xe7,0x91,0x5c,0xbd,0x58,0x36,0xe6,0x9d,0xdb,0x96,0x30,0x88,0xf5,0xa0,0xd0,0x2c,0x6d,0x5e,0xb6,0xbe,0x44,0x2f,0x41,0xb0,0x1e,0x9c,0xd6,0x71,0xdb,0xef,0x6e,0x46,0x5f,0xcd,0x29,0x15,0x93,0xa3,0x92,0x8b,0xb1,0xfb,0x8e,0xb8,0x7d,0xa9,0x50,0xf,0x65,0xca,0xc5,0xbb,0x8c,0xe0,0xe6,0xc1,0xa,0x66,0x99,0xd3,0x7b,0x1a,0x27,0x7c,0xae,0xb4,0xe2,0x3e,0x67,0x64,0xba,0x5d,0xe8,0xd8,0x8a,0xfc,0x1,0xf3,0x22,0xff,0x52,0xb,0x34,0x62,0xdd,0x2d,0x6f,0x77,0x5c,0x76,0xda,0x7f,0x83,0x9d,0xe3,0x10,0x94,0xac,0x1c,0xc3,0x4,0x84,0x1b,0x55,0x86,0x18,0xd1,0x72,0xaa,0x37,0x73,0xc6,0xbc,0x74,0x61,0x5a,0xfa,0x21,0x2a,0x85,0x9a,0x2b,0x90,0xc4,0x54,0xd9,0x3,0x3d,0xe4,0x91,0x0,0x13,0x7a,0xec,0x49,0x20,0x69,0x3a,0xa6,0xee,0x6b,0x14,0xe1,0x9b,0xf4,0xe,0xa1,0x17,0xc0,0x4f,0xc8,0x36,0x40,0x1f,0xbd,0xab,0x39,0x2,0xd4,0x12,0x9,0x58,0xb9,0x5,0x9e,0x16,0xdc,0x19,0xf9,0x42,0x6a,0xfd,0x89,0xd7,0xf7,0x78,0xa4,0xad,0x48,0x81,0x4c,0x8d,0xcb,0x26,0xf6,0xa2,0x3b,0xc2,0xa5,0x1d,0xe9,0xc7,0xa7,0xed,0xb7,0x25,0x4d,0x8f,0x63,0x2e,0x7,0xde,0x8,0x60,0x23,0xe5,0xeb,0xe7,0x6c,0x3f,0x5b,0x35,0xea,0xf2,0xcc,0xd,0x11,0x56,0xa8,0x97,0xcf,0x87,0xfe,0xd2,0x45,0x31,0xb2,0x9f,0x70,0x79,0x7e,0x47,0x4a,0x28,0x95,0xf8,0xce,0x38,0xc9,0x33,0x32,0x68,0x75,0x82,0xdf,0x80,0xaf,0x43,0x51,0xf0,0xbf,0xc,0x6,0xf1,0x24,0x53,0x57,0x3c,0x4b,0x4e,0x59,0xd5,0xb3,0x98,0xb5,0x81,0x46,0x99,0x29,0x3,0xd0,0x9e,0x1,0x6,0xfa,0x5f,0xf3,0x11,0x95,0x66,0x18,0x58,0xe7,0xb1,0x8e,0xd9,0xf2,0xea,0xa8,0x84,0x79,0xf,0x5d,0xd7,0x7a,0xa7,0x76,0xff,0x96,0x85,0x14,0xec,0xa5,0xcc,0x69,0xd1,0x41,0x15,0xae,0x61,0xb8,0x86,0x5c,0x7f,0xdf,0xe4,0xf1,0x1f,0x0,0xaf,0xa4,0x2f,0xf7,0x54,0x9d,0x39,0x43,0xf6,0xb2,0x16,0x90,0xac,0x48,0x34,0xe,0x17,0x26,0x5e,0xf4,0x53,0x19,0xda,0xc3,0xeb,0x6a,0xc1,0x3b,0x33,0xdb,0x9b,0x35,0xc4,0xaa,0x70,0xd,0xb5,0x13,0xe8,0xa9,0x55,0x25,0xe2,0xbb,0x67,0x31,0x6d,0xd8,0x3f,0xe1,0xfe,0x56,0x1c,0xe3,0x2b,0xf9,0xa2,0x9f,0x9,0x3e,0x40,0x4f,0x8f,0x44,0x63,0x65,0xf8,0x3d,0xb,0x7e,0xe0,0x8a,0xd5,0x2c,0xf5,0x1a,0x37,0xb4,0xcf,0xc2,0xfb,0xfc,0x4a,0x12,0x2d,0xd3,0xc0,0x57,0x7b,0x2,0x6f,0xb0,0xde,0xba,0x94,0x88,0x49,0x77,0xa6,0xe5,0x8d,0x5b,0xe9,0x62,0x6e,0x60,0xdc,0xcb,0xce,0xb9,0x30,0x1d,0x36,0x50,0x83,0x89,0x3a,0x75,0xd2,0xd6,0xa1,0x74,0x5a,0x7,0xf0,0xed,0xd4,0xc6,0x2a,0x5,0x4b,0x7d,0x10,0xad,0xb7,0xb6,0x4c,0xbd,0x1b,0x80,0x3c,0xdd,0x7c,0x9c,0x59,0x93,0xbc,0x2e,0x38,0x9a,0x8c,0x97,0x51,0x87,0x45,0x92,0x24,0x8b,0xc5,0xb3,0x4d,0xca,0xee,0x6b,0x23,0xbf,0x71,0x1e,0x64,0x91,0xc8,0xa0,0x32,0x68,0x82,0xab,0xe6,0xa,0x20,0x47,0xbe,0x27,0x22,0x42,0x6c,0x98,0xc9,0x4,0xcd,0x28,0x73,0xa3,0x4e,0x8,0xc,0x78,0xef,0xc7,0x21,0xfd,0x72,0x52,0xdf,0x7d,0x5b,0xc9,0xb6,0x60,0x6b,0x70,0xdb,0x3a,0xfc,0x67,0xbe,0x74,0x9b,0x7b,0xc4,0x58,0x9,0x8c,0x83,0x76,0x96,0xf9,0xc3,0x6c,0xa2,0x75,0xaa,0x2d,0x22,0x54,0x59,0xc0,0xc7,0xa0,0x8b,0x7f,0xc5,0xa5,0xd5,0x8f,0x2f,0x47,0x1,0xed,0x65,0x4c,0x8,0x20,0xeb,0x9f,0x95,0xb5,0xc6,0x1a,0x2a,0xcf,0x2e,0xe3,0xa9,0xef,0x94,0x44,0xca,0x34,0xad,0xf5,0x9c,0xe5,0x27,0xb0,0xd0,0x53,0x12,0xfd,0x1c,0x1b,0x28,0x25,0x6a,0xbc,0x41,0x2,0x89,0x87,0xe,0x85,0x39,0x5d,0x88,0x57,0xae,0x90,0x73,0x6f,0xdd,0x92,0x64,0x6e,0x46,0x93,0x35,0x31,0x29,0x5e,0x3b,0x2c,0xd1,0xb7,0xd7,0xfa,0xf7,0x4a,0xac,0x9a,0xab,0x5a,0x50,0x51,0x17,0xa,0xbd,0xe0,0xcd,0xe2,0x33,0x21,0xb4,0xfe,0xb9,0x13,0xc,0x8d,0x3d,0x24,0x4b,0xaf,0xf1,0x77,0xf0,0xc1,0xd3,0xe9,0x52,0xf4,0x97,0xea,0xb2,0xc2,0xf,0x4e,0xd4,0x3c,0x26,0xdc,0x23,0x4d,0x7c,0xd2,0xfb,0x4,0x19,0xb1,0x45,0x78,0xcc,0x1e,0x80,0xd6,0x5,0x5c,0xd8,0x6,0x8a,0x3f,0xec,0x99,0x1f,0xda,0x32,0xcb,0x7,0x6d,0xa7,0xa8,0xee,0xd9,0x84,0x82,0x68,0xa3,0xb8,0x14,0xe1,0x1d,0x81,0xff,0xf6,0x72,0x7e,0xce,0x66,0xa1,0x79,0xe6,0xe4,0x37,0xe8,0xba,0x63,0x9e,0x40,0x91,0x30,0x9d,0x56,0x69,0xbf,0x0,0xd,0x4f,0x3e,0x15,0xf2,0x49,0x36,0xa6,0x61,0xbb,0x86,0x5f,0x62,0xf3,0x18,0x71,0x2b,0x8e,0xb,0x42,0xb3,0x7a,0xc8,0x10,0x11,0x55,0xde,0xa4,0x3,0x16,0x98,0x38,0x48,0x43,0xf8,0xe7,0x11,0xed,0x18,0xb4,0x7e,0xfa,0xf3,0x8d,0xad,0x6a,0xc2,0x72,0x3b,0xe8,0xea,0x75,0x92,0x6f,0xb6,0xe4,0x91,0x3c,0x9d,0x4c,0xc,0xb3,0x65,0x5a,0x19,0x32,0x43,0x1,0xaa,0x3a,0x45,0xfe,0x53,0x8a,0xb7,0x6d,0x7d,0x14,0xff,0x6e,0x4e,0x7,0x82,0x27,0x1c,0xc4,0x76,0xbf,0xa8,0xd2,0x59,0x1d,0x34,0x94,0x1a,0xf,0xeb,0xf4,0x4f,0x44,0x1f,0xb5,0xf2,0xb8,0x28,0x31,0x81,0x0,0x7b,0xfd,0xa3,0x47,0xe5,0xdf,0xcd,0xfc,0xe6,0x9b,0xf8,0x5e,0x42,0x3,0xce,0xbe,0xd0,0x2a,0x30,0xd8,0xde,0x70,0x41,0x2f,0xbd,0x15,0x8,0xf7,0x12,0xc0,0x74,0x49,0x50,0x9,0xda,0x8c,0x33,0x86,0xa,0xd4,0xd6,0x13,0x95,0xe0,0x61,0xb,0xc7,0x3e,0xd5,0xe2,0xa4,0xab,0xaf,0x64,0x8e,0x88,0xf9,0xa1,0x38,0xc6,0xbc,0x2b,0xe9,0x90,0xf1,0x1e,0x5f,0xdc,0x29,0x24,0x17,0x10,0xe,0x4d,0xb0,0x66,0x89,0x2,0x8b,0x85,0x5b,0x84,0x51,0x35,0x63,0x7f,0x9c,0xa2,0x62,0x68,0x9e,0xd1,0x3d,0x39,0x9f,0x4a,0x20,0x37,0x52,0x25,0xf6,0xdb,0xbb,0xdd,0x96,0xa0,0x46,0xfb,0x5d,0x5c,0x56,0xa7,0xec,0xb1,0x6,0x1b,0x2d,0x3f,0xee,0xc1,0xc5,0x57,0x71,0xd3,0x7c,0x67,0x6c,0xba,0x6b,0xf0,0x36,0xd7,0x77,0x97,0x78,0xb2,0x80,0x5,0x54,0xc8,0xf5,0x9a,0x7a,0x8f,0x79,0xae,0x60,0xcf,0x58,0x2e,0x21,0xa6,0xac,0xcb,0xcc,0x55,0xa9,0xc9,0x73,0x87,0x4b,0x23,0x83,0xd9,0x40,0x69,0xe1,0xd,0x93,0xe7,0x2c,0x4,0x16,0xca,0xb9,0x99,0xef,0x22,0xc3,0x26,0x48,0x98,0xe3,0xa5,0x81,0x27,0x9f,0xe2,0xb7,0xc7,0x3b,0x7a,0x49,0xa1,0xa9,0x53,0x38,0x56,0xa7,0x9,0x8b,0xc1,0x66,0xcc,0xf8,0x79,0x51,0x48,0xda,0x3e,0x2,0x84,0xb4,0x85,0x9c,0xa6,0xec,0x99,0xaf,0x6a,0xbe,0x47,0x18,0x72,0xdd,0xd2,0xac,0x9b,0xf7,0xf1,0xd6,0x1d,0x71,0x8e,0xc4,0x6c,0xd,0x30,0x6b,0xb9,0xa3,0xf5,0x29,0x70,0x73,0xad,0x4a,0xff,0xcf,0x9d,0xeb,0x16,0xe4,0x35,0xe8,0x45,0x1c,0x23,0x75,0xca,0x3a,0x78,0x60,0x4b,0x61,0xcd,0x68,0x94,0x8a,0xf4,0x7,0x83,0xbb,0xb,0xd4,0x13,0x93,0xc,0x42,0x91,0xf,0xc6,0x65,0xbd,0x20,0x64,0xd1,0xab,0x63,0x76,0x4d,0xed,0x36,0x3d,0x92,0x8d,0x3c,0x87,0xd3,0x43,0xce,0x14,0x2a,0xf3,0x86,0x17,0x4,0x6d,0xfb,0x5e,0x37,0x7e,0x2d,0xb1,0xf9,0x7c,0x3,0xf6,0x8c,0xe3,0x19,0xb6,0x0,0xd7,0x58,0xdf,0x21,0x57,0x8,0xaa,0xbc,0x2e,0x15,0xc3,0x5,0x1e,0x4f,0xae,0x12,0x89,0x1,0xcb,0xe,0xee,0x55,0x7d,0xea,0x9e,0xc0,0xe0,0x6f,0xb3,0xba,0x5f,0x96,0x5b,0x9a,0xdc,0x31,0xe1,0xb5,0x2c,0xd5,0xb2,0xa,0xfe,0xd0,0xb0,0xfa,0xa0,0x32,0x5a,0x98,0x74,0x39,0x10,0xc9,0x1f,0x77,0x34,0xf2,0xfc,0xf0,0x7b,0x28,0x4c,0x22,0xfd,0xe5,0xdb,0x1a,0x6,0x41,0xbf,0x80,0xd8,0x90,0xe9,0xc5,0x52,0x26,0xa5,0x88,0x67,0x6e,0x69,0x50,0x5d,0x3f,0x82,0xef,0xd9,0x2f,0xde,0x24,0x25,0x7f,0x62,0x95,0xc8,0x97,0xb8,0x54,0x46,0xe7,0xa8,0x1b,0x11,0xe6,0x33,0x44,0x40,0x2b,0x5c,0x59,0x4e,0xc2,0xa4,0x8f,0xa2,0xe4,0x23,0xfc,0x4c,0x66,0xb5,0xfb,0x64,0x63,0x9f,0x3a,0x96,0x74,0xf0,0x3,0x7d,0x3d,0x82,0xd4,0xeb,0xbc,0x97,0x8f,0xcd,0xe1,0x1c,0x6a,0x38,0xb2,0x1f,0xc2,0x13,0x9a,0xf3,0xe0,0x71,0x89,0xc0,0xa9,0xc,0xb4,0x24,0x70,0xcb,0x4,0xdd,0xe3,0x39,0x1a,0xba,0x81,0x94,0x7a,0x65,0xca,0xc1,0x4a,0x92,0x31,0xf8,0x5c,0x26,0x93,0xd7,0x73,0xf5,0xc9,0x2d,0x51,0x6b,0x72,0x43,0x3b,0x91,0x36,0x7c,0xbf,0xa6,0x8e,0xf,0xa4,0x5e,0x56,0xbe,0xfe,0x50,0xa1,0xcf,0x15,0x68,0xd0,0x76,0x8d,0xcc,0x30,0x40,0x87,0xde,0x2,0x54,0x8,0xbd,0x5a,0x84,0x9b,0x33,0x79,0x86,0x4e,0x9c,0xc7,0xfa,0x6c,0x5b,0x25,0x2a,0xea,0x21,0x6,0x0,0x9d,0x58,0x6e,0x1b,0x85,0xef,0xb0,0x49,0x90,0x7f,0x52,0xd1,0xaa,0xa7,0x9e,0x99,0x2f,0x77,0x48,0xb6,0xa5,0x32,0x1e,0x67,0xa,0xd5,0xbb,0xdf,0xf1,0xed,0x2c,0x12,0xc3,0x80,0xe8,0x3e,0x8c,0x7,0xb,0x5,0xb9,0xae,0xab,0xdc,0x55,0x78,0x53,0x35,0xe6,0xec,0x5f,0x10,0xb7,0xb3,0xc4,0x11,0x3f,0x62,0x95,0x88,0xb1,0xa3,0x4f,0x60,0x2e,0x18,0x75,0xc8,0xd2,0xd3,0x29,0xd8,0x7e,0xe5,0x59,0xb8,0x19,0xf9,0x3c,0xf6,0xd9,0x4b,0x5d,0xff,0xe9,0xf2,0x34,0xe2,0x20,0xf7,0x41,0xee,0xa0,0xd6,0x28,0xaf,0x8b,0xe,0x46,0xda,0x14,0x7b,0x1,0xf4,0xad,0xc5,0x57,0xd,0xe7,0xce,0x83,0x6f,0x45,0x22,0xdb,0x42,0x47,0x27,0x9,0xfd,0xac,0x61,0xa8,0x4d,0x16,0xc6,0x2b,0x6d,0x69,0x1d,0x8a,0xa2,0x44,0x98,0x17,0x37,0x71,0x7b,0x36,0x27,0xc5,0xa8,0x68,0x97,0x52,0x8d,0x62,0xdc,0xba,0xb8,0xcc,0x24,0x32,0xaf,0x2a,0xfc,0x1d,0x60,0x77,0x6f,0xdf,0x2f,0x61,0x16,0x1e,0x47,0xc,0xfa,0xca,0x44,0xe8,0x57,0xb4,0x1b,0x14,0x5f,0xf6,0xda,0x1,0x91,0x3e,0xa7,0x4a,0xe1,0x33,0xa2,0xe7,0x6e,0x41,0xa3,0xbb,0xd3,0x51,0x3,0xeb,0xf8,0xc8,0x30,0x8b,0xe,0xdb,0x9,0xa6,0x67,0x80,0xd5,0x9a,0x4f,0x3b,0x95,0xe4,0x9b,0x74,0x18,0xe0,0xde,0x2b,0x88,0xf3,0x83,0xaa,0x21,0x59,0x15,0x96,0xa5,0x5b,0x85,0x75,0x58,0xbd,0x54,0x39,0xf1,0xf2,0x84,0x7f,0x3d,0x3f,0x9e,0x22,0x5e,0x23,0x43,0x93,0x49,0x20,0x25,0x17,0xfb,0x3c,0xa9,0x76,0xc1,0x4,0x4d,0xa,0xff,0x65,0x50,0x35,0xc6,0x4e,0xe3,0x38,0x92,0x5,0xcb,0xc7,0x7,0xd7,0x29,0x6b,0x5c,0xd,0x56,0xdd,0x26,0x82,0x9f,0xbc,0x70,0xc2,0xb7,0xc3,0xf5,0xc0,0x42,0x90,0x2c,0x5a,0xbe,0xe9,0x6,0x2,0x7a,0xb1,0x81,0x55,0x98,0xcd,0xd8,0xfe,0x46,0x12,0x73,0xec,0x7e,0x19,0xb5,0x4c,0xc4,0x28,0xc9,0xb9,0x10,0x87,0x6a,0xcf,0x8,0x6d,0x45,0xf4,0x6c,0xf,0x48,0x37,0xef,0xee,0x79,0x2d,0x94,0x8a,0xf9,0x31,0xb0,0x8e,0x9c,0x69,0xfd,0x40,0x7c,0x1a,0x1c,0xb2,0xe5,0xe2,0x13,0xe6,0x8c,0xac,0xb6,0xd6,0x72,0x64,0x63,0xd0,0xea,0x53,0x7d,0x89,0xae,0x0,0x1f,0xab,0xed,0x34,0x11,0xa0,0x86,0xb,0x8f,0xd2,0x3a,0x99,0xa1,0x4b,0xd4,0xf7,0xbf,0xf0,0x66,0x2e,0xce,0x9d,0xd9,0x5d,0xd1,0xad,0xb3,0x78,0xa4,0x4e,0xb2,0x47,0xeb,0x21,0xa5,0xac,0xd2,0xf2,0x35,0x9d,0x2d,0x64,0xb7,0xb5,0x2a,0xcd,0x30,0xe9,0xbb,0xce,0x63,0xc2,0x13,0x53,0xec,0x3a,0x5,0x46,0x6d,0x1c,0x5e,0xf5,0x65,0x1a,0xa1,0xc,0xd5,0xe8,0x32,0x22,0x4b,0xa0,0x31,0x11,0x58,0xdd,0x78,0x43,0x9b,0x29,0xe0,0xf7,0x8d,0x6,0x42,0x6b,0xcb,0x45,0x50,0xb4,0xab,0x10,0x1b,0x40,0xea,0xad,0xe7,0x77,0x6e,0xde,0x5f,0x24,0xa2,0xfc,0x18,0xba,0x80,0x92,0xa3,0xb9,0xc4,0xa7,0x1,0x1d,0x5c,0x91,0xe1,0x8f,0x75,0x6f,0x87,0x81,0x2f,0x1e,0x70,0xe2,0x4a,0x57,0xa8,0x4d,0x9f,0x2b,0x16,0xf,0x56,0x85,0xd3,0x6c,0xd9,0x55,0x8b,0x89,0x4c,0xca,0xbf,0x3e,0x54,0x98,0x61,0x8a,0xbd,0xfb,0xf4,0xf0,0x3b,0xd1,0xd7,0xa6,0xfe,0x67,0x99,0xe3,0x74,0xb6,0xcf,0xae,0x41,0x0,0x83,0x76,0x7b,0x48,0x4f,0x51,0x12,0xef,0x39,0xd6,0x5d,0xd4,0xda,0x4,0xdb,0xe,0x6a,0x3c,0x20,0xc3,0xfd,0x3d,0x37,0xc1,0x8e,0x62,0x66,0xc0,0x15,0x7f,0x68,0xd,0x7a,0xa9,0x84,0xe4,0x82,0xc9,0xff,0x19,0xa4,0x2,0x3,0x9,0xf8,0xb3,0xee,0x59,0x44,0x72,0x60,0xb1,0x9e,0x9a,0x8,0x2e,0x8c,0x23,0x38,0x33,0xe5,0x34,0xaf,0x69,0x88,0x28,0xc8,0x27,0xed,0xdf,0x5a,0xb,0x97,0xaa,0xc5,0x25,0xd0,0x26,0xf1,0x3f,0x90,0x7,0x71,0x7e,0xf9,0xf3,0x94,0x93,0xa,0xf6,0x96,0x2c,0xd8,0x14,0x7c,0xdc,0x86,0x1f,0x36,0xbe,0x52,0xcc,0xb8,0x73,0x5b,0x49,0x95,0xe6,0xc6,0xb0,0x7d,0x9c,0x79,0x17,0xc7,0xbc,0xfa,0x2d,0x8b,0x33,0x4e,0x1b,0x6b,0x97,0xd6,0xe5,0xd,0x5,0xff,0x94,0xfa,0xb,0xa5,0x27,0x6d,0xca,0x60,0x54,0xd5,0xfd,0xe4,0x76,0x92,0xae,0x28,0x18,0x29,0x30,0xa,0x40,0x35,0x3,0xc6,0x12,0xeb,0xb4,0xde,0x71,0x7e,0x0,0x37,0x5b,0x5d,0x7a,0xb1,0xdd,0x22,0x68,0xc0,0xa1,0x9c,0xc7,0x15,0xf,0x59,0x85,0xdc,0xdf,0x1,0xe6,0x53,0x63,0x31,0x47,0xba,0x48,0x99,0x44,0xe9,0xb0,0x8f,0xd9,0x66,0x96,0xd4,0xcc,0xe7,0xcd,0x61,0xc4,0x38,0x26,0x58,0xab,0x2f,0x17,0xa7,0x78,0xbf,0x3f,0xa0,0xee,0x3d,0xa3,0x6a,0xc9,0x11,0x8c,0xc8,0x7d,0x7,0xcf,0xda,0xe1,0x41,0x9a,0x91,0x3e,0x21,0x90,0x2b,0x7f,0xef,0x62,0xb8,0x86,0x5f,0x2a,0xbb,0xa8,0xc1,0x57,0xf2,0x9b,0xd2,0x81,0x1d,0x55,0xd0,0xaf,0x5a,0x20,0x4f,0xb5,0x1a,0xac,0x7b,0xf4,0x73,0x8d,0xfb,0xa4,0x6,0x10,0x82,0xb9,0x6f,0xa9,0xb2,0xe3,0x2,0xbe,0x25,0xad,0x67,0xa2,0x42,0xf9,0xd1,0x46,0x32,0x6c,0x4c,0xc3,0x1f,0x16,0xf3,0x3a,0xf7,0x36,0x70,0x9d,0x4d,0x19,0x80,0x79,0x1e,0xa6,0x52,0x7c,0x1c,0x56,0xc,0x9e,0xf6,0x34,0xd8,0x95,0xbc,0x65,0xb3,0xdb,0x98,0x5e,0x50,0x5c,0xd7,0x84,0xe0,0x8e,0x51,0x49,0x77,0xb6,0xaa,0xed,0x13,0x2c,0x74,0x3c,0x45,0x69,0xfe,0x8a,0x9,0x24,0xcb,0xc2,0xc5,0xfc,0xf1,0x93,0x2e,0x43,0x75,0x83,0x72,0x88,0x89,0xd3,0xce,0x39,0x64,0x3b,0x14,0xf8,0xea,0x4b,0x4,0xb7,0xbd,0x4a,0x9f,0xe8,0xec,0x87,0xf0,0xf5,0xe2,0x6e,0x8,0x23,0xe,0xaf,0xf8,0x59,0xa8,0xc6,0xac,0xfc,0xe6,0x38,0x9c,0x29,0x2e,0xa0,0x9a,0x37,0x19,0x33,0xa4,0xde,0x67,0xb3,0xc0,0xfa,0x7b,0xd6,0xc4,0xb7,0x23,0x36,0xa,0x56,0x50,0x9e,0x1,0xf5,0xbd,0x2c,0xba,0x84,0x64,0x93,0xd7,0x9b,0x17,0xf9,0xe7,0xee,0x32,0xe4,0xc3,0x55,0x4a,0xa7,0xe1,0x5b,0x7e,0xcc,0xea,0xc5,0x41,0x70,0x98,0xeb,0xd3,0x3a,0xf6,0xfd,0x88,0xbf,0x89,0x8,0x8a,0x66,0xda,0xf4,0x10,0x4c,0xa3,0x30,0x48,0xd8,0x72,0x81,0x4f,0x4d,0x8d,0x63,0x9d,0x16,0x21,0x1c,0x47,0x6c,0x97,0xd5,0xc8,0x83,0x62,0x5a,0xf3,0x20,0xcd,0x42,0x85,0xf,0x27,0x26,0xbe,0x2,0x45,0xa5,0x7d,0xcb,0xfb,0xd2,0x1f,0x92,0x87,0xc,0xb4,0x39,0x58,0x34,0xa6,0xff,0x53,0x8e,0x6,0xc2,0x61,0xc9,0xb9,0x6b,0xe0,0x5f,0x13,0xef,0xdc,0xcf,0x11,0x12,0x3f,0x1e,0xf7,0x43,0x91,0x2d,0xec,0x9f,0xca,0x5,0xd0,0xdf,0x71,0xd1,0xae,0x52,0x3e,0x94,0xaa,0xb1,0x5d,0xe3,0x76,0x8b,0x3c,0x7,0x4e,0xb5,0x40,0x1a,0x2f,0x8c,0x7f,0xa9,0x4,0xbb,0x73,0xce,0xb8,0x77,0x35,0xd4,0x75,0x14,0x68,0x9,0x69,0x3,0xd9,0x6f,0x6a,0xe5,0x78,0xb6,0x60,0x2a,0x57,0x25,0x3d,0x65,0x95,0x5c,0x2b,0xd,0x54,0xb0,0x46,0x31,0x3b,0x6d,0x7c,0xe2,0x8f,0xdd,0x22,0xc7,0x18,0x96,0x28,0xf2,0xf0,0x6e,0x86,0xe8,0x79,0x24,0xad,0xe9,0xb,0x99,0xf1,0x49,0x1b,0xb2,0xa1,0x7a,0x82,0x44,0xc1,0xe,0x80,0x1d,0xa2,0x51,0xfe,0x15,0x5e,0x90,0xbc,0xdb,0x4b,0xed,0x74,0xab,0x0,0x33,0x91,0xb7,0x25,0x5a,0x8c,0x87,0x9c,0x37,0xd6,0x10,0x8b,0x52,0x98,0x77,0x97,0x28,0xb4,0xe5,0x60,0x6f,0x9a,0x7a,0x15,0x2f,0x80,0x4e,0x99,0x46,0xc1,0xce,0xb8,0xb5,0x2c,0x2b,0x4c,0x67,0x93,0x29,0x49,0x39,0x63,0xc3,0xab,0xed,0x1,0x89,0xa0,0xe4,0xcc,0x7,0x73,0x79,0x59,0x2a,0xf6,0xc6,0x23,0xc2,0xf,0x45,0x3,0x78,0xa8,0x26,0xd8,0x41,0x19,0x70,0x9,0xcb,0x5c,0x3c,0xbf,0xfe,0x11,0xf0,0xf7,0xc4,0xc9,0x86,0x50,0xad,0xee,0x65,0x6b,0xe2,0x69,0xd5,0xb1,0x64,0xbb,0x42,0x7c,0x9f,0x83,0x31,0x7e,0x88,0x82,0xaa,0x7f,0xd9,0xdd,0xc5,0xb2,0xd7,0xc0,0x3d,0x5b,0x3b,0x16,0x1b,0xa6,0x40,0x76,0x47,0xb6,0xbc,0xbd,0xfb,0xe6,0x51,0xc,0x21,0xe,0xdf,0xcd,0x58,0x12,0x55,0xff,0xe0,0x61,0xd1,0xc8,0xa7,0x43,0x1d,0x9b,0x1c,0x2d,0x3f,0x5,0xbe,0x18,0x7b,0x6,0x5e,0x2e,0xe3,0xa2,0x38,0xd0,0xca,0x30,0xcf,0xa1,0x90,0x3e,0x17,0xe8,0xf5,0x5d,0xa9,0x94,0x20,0xf2,0x6c,0x3a,0xe9,0xb0,0x34,0xea,0x66,0xd3,0x0,0x75,0xf3,0x36,0xde,0x27,0xeb,0x81,0x4b,0x44,0x2,0x35,0x68,0x6e,0x84,0x4f,0x54,0xf8,0xd,0xf1,0x6d,0x13,0x1a,0x9e,0x92,0x22,0x8a,0x4d,0x95,0xa,0x8,0xdb,0x4,0x56,0x8f,0x72,0xac,0x7d,0xdc,0x71,0xba,0x85,0x53,0xec,0xe1,0xa3,0xd2,0xf9,0x1e,0xa5,0xda,0x4a,0x8d,0x57,0x6a,0xb3,0x8e,0x1f,0xf4,0x9d,0xc7,0x62,0xe7,0xae,0x5f,0x96,0x24,0xfc,0xfd,0xb9,0x32,0x48,0xef,0xfa,0x74,0xd4,0xa4,0xaf,0x14,0xb,0xb5,0x49,0xbc,0x10,0xda,0x5e,0x57,0x29,0x9,0xce,0x66,0xd6,0x9f,0x4c,0x4e,0xd1,0x36,0xcb,0x12,0x40,0x35,0x98,0x39,0xe8,0xa8,0x17,0xc1,0xfe,0xbd,0x96,0xe7,0xa5,0xe,0x9e,0xe1,0x5a,0xf7,0x2e,0x13,0xc9,0xd9,0xb0,0x5b,0xca,0xea,0xa3,0x26,0x83,0xb8,0x60,0xd2,0x1b,0xc,0x76,0xfd,0xb9,0x90,0x30,0xbe,0xab,0x4f,0x50,0xeb,0xe0,0xbb,0x11,0x56,0x1c,0x8c,0x95,0x25,0xa4,0xdf,0x59,0x7,0xe3,0x41,0x7b,0x69,0x58,0x42,0x3f,0x5c,0xfa,0xe6,0xa7,0x6a,0x1a,0x74,0x8e,0x94,0x7c,0x7a,0xd4,0xe5,0x8b,0x19,0xb1,0xac,0x53,0xb6,0x64,0xd0,0xed,0xf4,0xad,0x7e,0x28,0x97,0x22,0xae,0x70,0x72,0xb7,0x31,0x44,0xc5,0xaf,0x63,0x9a,0x71,0x46,0x0,0xf,0xb,0xc0,0x2a,0x2c,0x5d,0x5,0x9c,0x62,0x18,0x8f,0x4d,0x34,0x55,0xba,0xfb,0x78,0x8d,0x80,0xb3,0xb4,0xaa,0xe9,0x14,0xc2,0x2d,0xa6,0x2f,0x21,0xff,0x20,0xf5,0x91,0xc7,0xdb,0x38,0x6,0xc6,0xcc,0x3a,0x75,0x99,0x9d,0x3b,0xee,0x84,0x93,0xf6,0x81,0x52,0x7f,0x1f,0x79,0x32,0x4,0xe2,0x5f,0xf9,0xf8,0xf2,0x3,0x48,0x15,0xa2,0xbf,0x89,0x9b,0x4a,0x65,0x61,0xf3,0xd5,0x77,0xd8,0xc3,0xc8,0x1e,0xcf,0x54,0x92,0x73,0xd3,0x33,0xdc,0x16,0x24,0xa1,0xf0,0x6c,0x51,0x3e,0xde,0x2b,0xdd,0xa,0xc4,0x6b,0xfc,0x8a,0x85,0x2,0x8,0x6f,0x68,0xf1,0xd,0x6d,0xd7,0x23,0xef,0x87,0x27,0x7d,0xe4,0xcd,0x45,0xa9,0x37,0x43,0x88,0xa0,0xb2,0x6e,0x1d,0x3d,0x4b,0x86,0x67,0x82,0xec,0x3c,0x47,0x1,0x4c,0x62,0xcf,0xf5,0x7b,0x7c,0xc9,0x6d,0xb3,0xa9,0xf9,0x93,0xfd,0xc,0xad,0xfa,0x5,0x3,0x5f,0x63,0x76,0xe2,0x91,0x83,0x2e,0xaf,0x95,0xe6,0x32,0x8b,0xf1,0x66,0x67,0xbb,0xb2,0xac,0x42,0xce,0x82,0xc6,0x31,0xd1,0xef,0x79,0xe8,0xa0,0x54,0xcb,0x86,0xbe,0xcd,0x25,0x14,0x90,0xbf,0x99,0x2b,0xe,0xb4,0xf2,0x1f,0x0,0x96,0xb1,0x1d,0x65,0xf6,0x19,0x45,0xa1,0x8f,0x33,0xdf,0x5d,0xdc,0xea,0xdd,0xa8,0xa3,0x6f,0x9d,0x80,0xc2,0x39,0x12,0x49,0x74,0x43,0xc8,0x36,0xd8,0x18,0x1a,0xd4,0x27,0x8d,0x28,0xf0,0x10,0x57,0xeb,0x73,0x72,0x5a,0xd0,0x17,0x98,0x75,0xa6,0xf,0x37,0xd6,0x53,0xdb,0x6,0xaa,0xf3,0x61,0xd,0x6c,0xe1,0x59,0xd2,0xc7,0x4a,0x87,0xae,0x9e,0xa2,0x4b,0x6a,0x47,0x44,0x9a,0x89,0xba,0x46,0xa,0xb5,0x3e,0xec,0x9c,0x34,0x97,0xff,0xc1,0x6b,0x7,0xfb,0x84,0x24,0x8a,0x85,0x50,0x9f,0xca,0xb9,0x78,0xc4,0x16,0x51,0xfc,0x2a,0xd9,0x7a,0x4f,0x15,0xe0,0x1b,0x52,0x69,0xde,0x23,0xb6,0x8,0xe4,0x3f,0x3a,0x8c,0x56,0x3c,0x5c,0x3d,0x41,0x20,0x81,0x60,0x22,0xed,0x9b,0x26,0xee,0x13,0xe5,0x1,0x58,0x7e,0x9,0xc0,0x30,0x68,0x70,0x2,0x7f,0x35,0xe3,0x2d,0xb0,0xd3,0x3b,0xa5,0xa7,0x7d,0xc3,0x4d,0x92,0x77,0x88,0xda,0xb7,0x29,0x38,0x6e,0x64,0x94,0x11,0xd7,0x2f,0xf4,0xe7,0x4e,0x1c,0xa4,0xcc,0x5e,0xbc,0xf8,0x71,0x2c,0xbd,0x55,0xfe,0x21,0xb8,0x1e,0x8e,0xe9,0xc5,0xb,0x40,0xab,0x4,0xf7,0x48,0xd5,0x5b,0x67,0xa0,0x7f,0xcf,0xe5,0x36,0x78,0xe7,0xe0,0x1c,0xb9,0x15,0xf7,0x73,0x80,0xfe,0xbe,0x1,0x57,0x68,0x3f,0x14,0xc,0x4e,0x62,0x9f,0xe9,0xbb,0x31,0x9c,0x41,0x90,0x19,0x70,0x63,0xf2,0xa,0x43,0x2a,0x8f,0x37,0xa7,0xf3,0x48,0x87,0x5e,0x60,0xba,0x99,0x39,0x2,0x17,0xf9,0xe6,0x49,0x42,0xc9,0x11,0xb2,0x7b,0xdf,0xa5,0x10,0x54,0xf0,0x76,0x4a,0xae,0xd2,0xe8,0xf1,0xc0,0xb8,0x12,0xb5,0xff,0x3c,0x25,0xd,0x8c,0x27,0xdd,0xd5,0x3d,0x7d,0xd3,0x22,0x4c,0x96,0xeb,0x53,0xf5,0xe,0x4f,0xb3,0xc3,0x4,0x5d,0x81,0xd7,0x8b,0x3e,0xd9,0x7,0x18,0xb0,0xfa,0x5,0xcd,0x1f,0x44,0x79,0xef,0xd8,0xa6,0xa9,0x69,0xa2,0x85,0x83,0x1e,0xdb,0xed,0x98,0x6,0x6c,0x33,0xca,0x13,0xfc,0xd1,0x52,0x29,0x24,0x1d,0x1a,0xac,0xf4,0xcb,0x35,0x26,0xb1,0x9d,0xe4,0x89,0x56,0x38,0x5c,0x72,0x6e,0xaf,0x91,0x40,0x3,0x6b,0xbd,0xf,0x84,0x88,0x86,0x3a,0x2d,0x28,0x5f,0xd6,0xfb,0xd0,0xb6,0x65,0x6f,0xdc,0x93,0x34,0x30,0x47,0x92,0xbc,0xe1,0x16,0xb,0x32,0x20,0xcc,0xe3,0xad,0x9b,0xf6,0x4b,0x51,0x50,0xaa,0x5b,0xfd,0x66,0xda,0x3b,0x9a,0x7a,0xbf,0x75,0x5a,0xc8,0xde,0x7c,0x6a,0x71,0xb7,0x61,0xa3,0x74,0xc2,0x6d,0x23,0x55,0xab,0x2c,0x8,0x8d,0xc5,0x59,0x97,0xf8,0x82,0x77,0x2e,0x46,0xd4,0x8e,0x64,0x4d,0x0,0xec,0xc6,0xa1,0x58,0xc1,0xc4,0xa4,0x8a,0x7e,0x2f,0xe2,0x2b,0xce,0x95,0x45,0xa8,0xee,0xea,0x9e,0x9,0x21,0xc7,0x1b,0x94,0xb4,0x9a,0x38,0x1e,0x8c,0xf3,0x25,0x2e,0x35,0x9e,0x7f,0xb9,0x22,0xfb,0x31,0xde,0x3e,0x81,0x1d,0x4c,0xc9,0xc6,0x33,0xd3,0xbc,0x86,0x29,0xe7,0x30,0xef,0x68,0x67,0x11,0x1c,0x85,0x82,0xe5,0xce,0x3a,0x80,0xe0,0x90,0xca,0x6a,0x2,0x44,0xa8,0x20,0x9,0x4d,0x65,0xae,0xda,0xd0,0xf0,0x83,0x5f,0x6f,0x8a,0x6b,0xa6,0xec,0xaa,0xd1,0x1,0x8f,0x71,0xe8,0xb0,0xd9,0xa0,0x62,0xf5,0x95,0x16,0x57,0xb8,0x59,0x5e,0x6d,0x60,0x2f,0xf9,0x4,0x47,0xcc,0xc2,0x4b,0xc0,0x7c,0x18,0xcd,0x12,0xeb,0xd5,0x36,0x2a,0x98,0xd7,0x21,0x2b,0x3,0xd6,0x70,0x74,0x6c,0x1b,0x7e,0x69,0x94,0xf2,0x92,0xbf,0xb2,0xf,0xe9,0xdf,0xee,0x1f,0x15,0x14,0x52,0x4f,0xf8,0xa5,0x88,0xa7,0x76,0x64,0xf1,0xbb,0xfc,0x56,0x49,0xc8,0x78,0x61,0xe,0xea,0xb4,0x32,0xb5,0x84,0x96,0xac,0x17,0xb1,0xd2,0xaf,0xf7,0x87,0x4a,0xb,0x91,0x79,0x63,0x99,0x66,0x8,0x39,0x97,0xbe,0x41,0x5c,0xf4,0x0,0x3d,0x89,0x5b,0xc5,0x93,0x40,0x19,0x9d,0x43,0xcf,0x7a,0xa9,0xdc,0x5a,0x9f,0x77,0x8e,0x42,0x28,0xe2,0xed,0xab,0x9c,0xc1,0xc7,0x2d,0xe6,0xfd,0x51,0xa4,0x58,0xc4,0xba,0xb3,0x37,0x3b,0x8b,0x23,0xe4,0x3c,0xa3,0xa1,0x72,0xad,0xff,0x26,0xdb,0x5,0xd4,0x75,0xd8,0x13,0x2c,0xfa,0x45,0x48,0xa,0x7b,0x50,0xb7,0xc,0x73,0xe3,0x24,0xfe,0xc3,0x1a,0x27,0xb6,0x5d,0x34,0x6e,0xcb,0x4e,0x7,0xf6,0x3f,0x8d,0x55,0x54,0x10,0x9b,0xe1,0x46,0x53,0xdd,0x7d,0xd,0x6,0xbd,0xa2,0x31,0x14,0xae,0xe8,0x5,0x1a,0x8c,0xab,0x9c,0xa4,0xd7,0x3f,0xe,0x8a,0xa5,0x83,0x2b,0xcb,0xf5,0x63,0xf2,0xba,0x4e,0xd1,0x7d,0xa1,0xa8,0xb6,0x58,0xd4,0x98,0xdc,0x34,0xb5,0x8f,0xfc,0x28,0x91,0xeb,0x7c,0x1f,0x19,0x45,0x79,0x6c,0xf8,0x8b,0x99,0xa9,0xb3,0xe3,0x89,0xe7,0x16,0xb7,0xe0,0x56,0x78,0xd5,0xef,0x61,0x66,0xd3,0x77,0xfb,0x43,0xc8,0xdd,0x50,0x9d,0xb4,0x84,0x49,0xc1,0x1c,0xb0,0xe9,0x7b,0x17,0x76,0xca,0xd,0x82,0x6f,0xbc,0x15,0x2d,0xcc,0x32,0xea,0xa,0x4d,0xf1,0x69,0x68,0x40,0xd2,0x2c,0xc2,0x2,0x0,0xce,0x3d,0x97,0x87,0x9a,0xd8,0x23,0x8,0x53,0x6e,0x59,0xc5,0x47,0xc6,0xf0,0xc7,0xb2,0xb9,0x75,0x7,0x7f,0xec,0x3,0x5f,0xbb,0x95,0x29,0x3a,0x9b,0x7a,0x38,0xf7,0x81,0x3c,0xf4,0x25,0x20,0x96,0x4c,0x26,0x46,0x27,0x5b,0x1,0x48,0x73,0xc4,0x39,0xac,0x12,0xfe,0x4b,0xe6,0x30,0xc3,0x60,0x55,0xf,0xfa,0x9f,0x4a,0x85,0xd0,0xa3,0x62,0xde,0xc,0xe5,0xdb,0x71,0x1d,0xe1,0x9e,0x3e,0x90,0x5c,0x10,0xaf,0x24,0xf6,0x86,0x2e,0x8d,0xb8,0x51,0x70,0x5d,0x5e,0x80,0x93,0xa0,0x11,0x5a,0xb1,0x1e,0xed,0x52,0xcf,0x41,0x4f,0xe4,0x3b,0xa2,0x4,0x94,0xf3,0xdf,0xbe,0xd6,0x44,0xa6,0xe2,0x6b,0x36,0xa7,0x8e,0xb,0xcd,0x35,0xee,0xfd,0x54,0x6,0x6d,0x92,0xc0,0xad,0x33,0x22,0x74,0x7e,0xc9,0x21,0xbf,0xbd,0x67,0xd9,0x57,0x88,0x72,0x6a,0x18,0x65,0x2f,0xf9,0x37,0xaa,0x9,0xff,0x1b,0x42,0x64,0x13,0xda,0x2a,0x2d,0x8b,0x33,0x4e,0x1b,0x6b,0x97,0xd6,0xe5,0xd,0x5,0xff,0x94,0xfa,0xb,0xa5,0x27,0x6d,0xca,0x60,0x54,0xd5,0xfd,0xe4,0x76,0x92,0xae,0x28,0x18,0x29,0x30,0xa,0x40,0x35,0x3,0xc6,0x12,0xeb,0xb4,0xde,0x71,0x7e,0x0,0x37,0x5b,0x5d,0x7a,0xb1,0xdd,0x22,0x68,0xc0,0xa1,0x9c,0xc7,0x15,0xf,0x59,0x85,0xdc,0xdf,0x1,0xe6,0x53,0x63,0x31,0x47,0xba,0x48,0x99,0x44,0xe9,0xb0,0x8f,0xd9,0x66,0x96,0xd4,0xcc,0xe7,0xcd,0x61,0xc4,0x38,0x26,0x58,0xab,0x2f,0x17,0xa7,0x78,0xbf,0x3f,0xa0,0xee,0x3d,0xa3,0x6a,0xc9,0x11,0x8c,0xc8,0x7d,0x7,0xcf,0xda,0xe1,0x41,0x9a,0x91,0x3e,0x21,0x90,0x2b,0x7f,0xef,0x62,0xb8,0x86,0x5f,0x2a,0xbb,0xa8,0xc1,0x57,0xf2,0x9b,0xd2,0x81,0x1d,0x55,0xd0,0xaf,0x5a,0x20,0x4f,0xb5,0x1a,0xac,0x7b,0xf4,0x73,0x8d,0xfb,0xa4,0x6,0x10,0x82,0xb9,0x6f,0xa9,0xb2,0xe3,0x2,0xbe,0x25,0xad,0x67,0xa2,0x42,0xf9,0xd1,0x46,0x32,0x6c,0x4c,0xc3,0x1f,0x16,0xf3,0x3a,0xf7,0x36,0x70,0x9d,0x4d,0x19,0x80,0x79,0x1e,0xa6,0x52,0x7c,0x1c,0x56,0xc,0x9e,0xf6,0x34,0xd8,0x95,0xbc,0x65,0xb3,0xdb,0x98,0x5e,0x50,0x5c,0xd7,0x84,0xe0,0x8e,0x51,0x49,0x77,0xb6,0xaa,0xed,0x13,0x2c,0x74,0x3c,0x45,0x69,0xfe,0x8a,0x9,0x24,0xcb,0xc2,0xc5,0xfc,0xf1,0x93,0x2e,0x43,0x75,0x83,0x72,0x88,0x89,0xd3,0xce,0x39,0x64,0x3b,0x14,0xf8,0xea,0x4b,0x4,0xb7,0xbd,0x4a,0x9f,0xe8,0xec,0x87,0xf0,0xf5,0xe2,0x6e,0x8,0x23,0xe,0xfa,0x3d,0xe2,0x52,0x78,0xab,0xe5,0x7a,0x7d,0x81,0x24,0x88,0x6a,0xee,0x1d,0x63,0x23,0x9c,0xca,0xf5,0xa2,0x89,0x91,0xd3,0xff,0x2,0x74,0x26,0xac,0x1,0xdc,0xd,0x84,0xed,0xfe,0x6f,0x97,0xde,0xb7,0x12,0xaa,0x3a,0x6e,0xd5,0x1a,0xc3,0xfd,0x27,0x4,0xa4,0x9f,0x8a,0x64,0x7b,0xd4,0xdf,0x54,0x8c,0x2f,0xe6,0x42,0x38,0x8d,0xc9,0x6d,0xeb,0xd7,0x33,0x4f,0x75,0x6c,0x5d,0x25,0x8f,0x28,0x62,0xa1,0xb8,0x90,0x11,0xba,0x40,0x48,0xa0,0xe0,0x4e,0xbf,0xd1,0xb,0x76,0xce,0x68,0x93,0xd2,0x2e,0x5e,0x99,0xc0,0x1c,0x4a,0x16,0xa3,0x44,0x9a,0x85,0x2d,0x67,0x98,0x50,0x82,0xd9,0xe4,0x72,0x45,0x3b,0x34,0xf4,0x3f,0x18,0x1e,0x83,0x46,0x70,0x5,0x9b,0xf1,0xae,0x57,0x8e,0x61,0x4c,0xcf,0xb4,0xb9,0x80,0x87,0x31,0x69,0x56,0xa8,0xbb,0x2c,0x0,0x79,0x14,0xcb,0xa5,0xc1,0xef,0xf3,0x32,0xc,0xdd,0x9e,0xf6,0x20,0x92,0x19,0x15,0x1b,0xa7,0xb0,0xb5,0xc2,0x4b,0x66,0x4d,0x2b,0xf8,0xf2,0x41,0xe,0xa9,0xad,0xda,0xf,0x21,0x7c,0x8b,0x96,0xaf,0xbd,0x51,0x7e,0x30,0x6,0x6b,0xd6,0xcc,0xcd,0x37,0xc6,0x60,0xfb,0x47,0xa6,0x7,0xe7,0x22,0xe8,0xc7,0x55,0x43,0xe1,0xf7,0xec,0x2a,0xfc,0x3e,0xe9,0x5f,0xf0,0xbe,0xc8,0x36,0xb1,0x95,0x10,0x58,0xc4,0xa,0x65,0x1f,0xea,0xb3,0xdb,0x49,0x13,0xf9,0xd0,0x9d,0x71,0x5b,0x3c,0xc5,0x5c,0x59,0x39,0x17,0xe3,0xb2,0x7f,0xb6,0x53,0x8,0xd8,0x35,0x73,0x77,0x3,0x94,0xbc,0x5a,0x86,0x9,0x29,0x6,0xbe,0x98,0x8d,0xd8,0x15,0xc1,0xf1,0x84,0xc,0xf5,0x59,0x3e,0xac,0x33,0x52,0x48,0x8f,0x2a,0xc7,0x50,0xf9,0x89,0x68,0xaf,0x77,0x8,0x4f,0x2c,0xb4,0x5,0x2d,0x69,0x97,0x47,0x87,0x8b,0x45,0xd2,0x78,0xdf,0xc2,0x66,0x9d,0x16,0x4d,0x1c,0x2b,0x2,0x80,0xb5,0x83,0xf7,0x82,0x30,0xfc,0x3a,0x42,0x46,0xa9,0xfe,0x1a,0x6c,0xd0,0x51,0x74,0xad,0xeb,0x5f,0x40,0xee,0xc9,0xe1,0xd9,0x7a,0x92,0xcf,0x4b,0xc6,0xe0,0x8e,0x6e,0x26,0xb0,0xff,0xb7,0x94,0xb,0xe4,0x38,0xf3,0xed,0x91,0x1d,0x99,0xdd,0xf0,0x71,0xb9,0xca,0xd4,0x6d,0x39,0xae,0x5c,0x5a,0x3c,0x0,0xbd,0x29,0xdc,0xce,0xf6,0xec,0xcc,0xa6,0x53,0xa2,0xa5,0xf2,0x3d,0x13,0xaa,0x90,0x23,0x24,0x32,0x96,0x1f,0x54,0x5b,0xf4,0x17,0xa8,0x4,0x8a,0xa1,0xa,0xe7,0x7e,0xd1,0x41,0x9a,0xb6,0x93,0xfb,0xe3,0x1,0x2e,0xa7,0xe2,0x73,0x4e,0xcb,0x70,0x88,0xb8,0xab,0x43,0x11,0xd7,0x28,0xe8,0x85,0x67,0x76,0x3b,0x31,0x64,0x8c,0xf8,0xfa,0x9c,0x22,0xcd,0x12,0x2f,0x37,0x20,0x5d,0xbc,0x6a,0xef,0x72,0xba,0x4c,0x7,0x5e,0x56,0x21,0x6f,0x9f,0xde,0x7f,0x7d,0x3f,0xc4,0xb2,0xb1,0x79,0x65,0x60,0x9,0xd3,0x3,0x63,0x1e,0x62,0xd,0x44,0x81,0x36,0xe9,0x7c,0xbb,0x57,0xa3,0xe,0x86,0x75,0x10,0x25,0xbf,0x4a,0xf,0xda,0x95,0xc0,0x27,0xe6,0x49,0x9b,0x9e,0xa0,0x58,0x34,0xdb,0xa4,0xd5,0x7b,0x55,0x19,0x61,0xea,0xc3,0xb3,0xc8,0x6b,0x14,0xfd,0x18,0x35,0xc5,0x1b,0xe5,0xd6,0x36,0xca,0x3f,0x93,0x59,0xdd,0xd4,0xaa,0x8a,0x4d,0xe5,0x55,0x1c,0xcf,0xcd,0x52,0xb5,0x48,0x91,0xc3,0xb6,0x1b,0xba,0x6b,0x2b,0x94,0x42,0x7d,0x3e,0x15,0x64,0x26,0x8d,0x1d,0x62,0xd9,0x74,0xad,0x90,0x4a,0x5a,0x33,0xd8,0x49,0x69,0x20,0xa5,0x0,0x3b,0xe3,0x51,0x98,0x8f,0xf5,0x7e,0x3a,0x13,0xb3,0x3d,0x28,0xcc,0xd3,0x68,0x63,0x38,0x92,0xd5,0x9f,0xf,0x16,0xa6,0x27,0x5c,0xda,0x84,0x60,0xc2,0xf8,0xea,0xdb,0xc1,0xbc,0xdf,0x79,0x65,0x24,0xe9,0x99,0xf7,0xd,0x17,0xff,0xf9,0x57,0x66,0x8,0x9a,0x32,0x2f,0xd0,0x35,0xe7,0x53,0x6e,0x77,0x2e,0xfd,0xab,0x14,0xa1,0x2d,0xf3,0xf1,0x34,0xb2,0xc7,0x46,0x2c,0xe0,0x19,0xf2,0xc5,0x83,0x8c,0x88,0x43,0xa9,0xaf,0xde,0x86,0x1f,0xe1,0x9b,0xc,0xce,0xb7,0xd6,0x39,0x78,0xfb,0xe,0x3,0x30,0x37,0x29,0x6a,0x97,0x41,0xae,0x25,0xac,0xa2,0x7c,0xa3,0x76,0x12,0x44,0x58,0xbb,0x85,0x45,0x4f,0xb9,0xf6,0x1a,0x1e,0xb8,0x6d,0x7,0x10,0x75,0x2,0xd1,0xfc,0x9c,0xfa,0xb1,0x87,0x61,0xdc,0x7a,0x7b,0x71,0x80,0xcb,0x96,0x21,0x3c,0xa,0x18,0xc9,0xe6,0xe2,0x70,0x56,0xf4,0x5b,0x40,0x4b,0x9d,0x4c,0xd7,0x11,0xf0,0x50,0xb0,0x5f,0x95,0xa7,0x22,0x73,0xef,0xd2,0xbd,0x5d,0xa8,0x5e,0x89,0x47,0xe8,0x7f,0x9,0x6,0x81,0x8b,0xec,0xeb,0x72,0x8e,0xee,0x54,0xa0,0x6c,0x4,0xa4,0xfe,0x67,0x4e,0xc6,0x2a,0xb4,0xc0,0xb,0x23,0x31,0xed,0x9e,0xbe,0xc8,0x5,0xe4,0x1,0x6f,0xbf,0xc4,0x82,0x29,0x8f,0x37,0x4a,0x1f,0x6f,0x93,0xd2,0xe1,0x9,0x1,0xfb,0x90,0xfe,0xf,0xa1,0x23,0x69,0xce,0x64,0x50,0xd1,0xf9,0xe0,0x72,0x96,0xaa,0x2c,0x1c,0x2d,0x34,0xe,0x44,0x31,0x7,0xc2,0x16,0xef,0xb0,0xda,0x75,0x7a,0x4,0x33,0x5f,0x59,0x7e,0xb5,0xd9,0x26,0x6c,0xc4,0xa5,0x98,0xc3,0x11,0xb,0x5d,0x81,0xd8,0xdb,0x5,0xe2,0x57,0x67,0x35,0x43,0xbe,0x4c,0x9d,0x40,0xed,0xb4,0x8b,0xdd,0x62,0x92,0xd0,0xc8,0xe3,0xc9,0x65,0xc0,0x3c,0x22,0x5c,0xaf,0x2b,0x13,0xa3,0x7c,0xbb,0x3b,0xa4,0xea,0x39,0xa7,0x6e,0xcd,0x15,0x88,0xcc,0x79,0x3,0xcb,0xde,0xe5,0x45,0x9e,0x95,0x3a,0x25,0x94,0x2f,0x7b,0xeb,0x66,0xbc,0x82,0x5b,0x2e,0xbf,0xac,0xc5,0x53,0xf6,0x9f,0xd6,0x85,0x19,0x51,0xd4,0xab,0x5e,0x24,0x4b,0xb1,0x1e,0xa8,0x7f,0xf0,0x77,0x89,0xff,0xa0,0x2,0x14,0x86,0xbd,0x6b,0xad,0xb6,0xe7,0x6,0xba,0x21,0xa9,0x63,0xa6,0x46,0xfd,0xd5,0x42,0x36,0x68,0x48,0xc7,0x1b,0x12,0xf7,0x3e,0xf3,0x32,0x74,0x99,0x49,0x1d,0x84,0x7d,0x1a,0xa2,0x56,0x78,0x18,0x52,0x8,0x9a,0xf2,0x30,0xdc,0x91,0xb8,0x61,0xb7,0xdf,0x9c,0x5a,0x54,0x58,0xd3,0x80,0xe4,0x8a,0x55,0x4d,0x73,0xb2,0xae,0xe9,0x17,0x28,0x70,0x38,0x41,0x6d,0xfa,0x8e,0xd,0x20,0xcf,0xc6,0xc1,0xf8,0xf5,0x97,0x2a,0x47,0x71,0x87,0x76,0x8c,0x8d,0xd7,0xca,0x3d,0x60,0x3f,0x10,0xfc,0xee,0x4f,0x0,0xb3,0xb9,0x4e,0x9b,0xec,0xe8,0x83,0xf4,0xf1,0xe6,0x6a,0xc,0x27,0xa,0x1,0xc6,0x19,0xa9,0x83,0x50,0x1e,0x81,0x86,0x7a,0xdf,0x73,0x91,0x15,0xe6,0x98,0xd8,0x67,0x31,0xe,0x59,0x72,0x6a,0x28,0x4,0xf9,0x8f,0xdd,0x57,0xfa,0x27,0xf6,0x7f,0x16,0x5,0x94,0x6c,0x25,0x4c,0xe9,0x51,0xc1,0x95,0x2e,0xe1,0x38,0x6,0xdc,0xff,0x5f,0x64,0x71,0x9f,0x80,0x2f,0x24,0xaf,0x77,0xd4,0x1d,0xb9,0xc3,0x76,0x32,0x96,0x10,0x2c,0xc8,0xb4,0x8e,0x97,0xa6,0xde,0x74,0xd3,0x99,0x5a,0x43,0x6b,0xea,0x41,0xbb,0xb3,0x5b,0x1b,0xb5,0x44,0x2a,0xf0,0x8d,0x35,0x93,0x68,0x29,0xd5,0xa5,0x62,0x3b,0xe7,0xb1,0xed,0x58,0xbf,0x61,0x7e,0xd6,0x9c,0x63,0xab,0x79,0x22,0x1f,0x89,0xbe,0xc0,0xcf,0xf,0xc4,0xe3,0xe5,0x78,0xbd,0x8b,0xfe,0x60,0xa,0x55,0xac,0x75,0x9a,0xb7,0x34,0x4f,0x42,0x7b,0x7c,0xca,0x92,0xad,0x53,0x40,0xd7,0xfb,0x82,0xef,0x30,0x5e,0x3a,0x14,0x8,0xc9,0xf7,0x26,0x65,0xd,0xdb,0x69,0xe2,0xee,0xe0,0x5c,0x4b,0x4e,0x39,0xb0,0x9d,0xb6,0xd0,0x3,0x9,0xba,0xf5,0x52,0x56,0x21,0xf4,0xda,0x87,0x70,0x6d,0x54,0x46,0xaa,0x85,0xcb,0xfd,0x90,0x2d,0x37,0x36,0xcc,0x3d,0x9b,0x0,0xbc,0x5d,0xfc,0x1c,0xd9,0x13,0x3c,0xae,0xb8,0x1a,0xc,0x17,0xd1,0x7,0xc5,0x12,0xa4,0xb,0x45,0x33,0xcd,0x4a,0x6e,0xeb,0xa3,0x3f,0xf1,0x9e,0xe4,0x11,0x48,0x20,0xb2,0xe8,0x2,0x2b,0x66,0x8a,0xa0,0xc7,0x3e,0xa7,0xa2,0xc2,0xec,0x18,0x49,0x84,0x4d,0xa8,0xf3,0x23,0xce,0x88,0x8c,0xf8,0x6f,0x47,0xa1,0x7d,0xf2,0xd2,0xfc,0x5e,0x78,0xea,0x95,0x43,0x48,0x53,0xf8,0x19,0xdf,0x44,0x9d,0x57,0xb8,0x58,0xe7,0x7b,0x2a,0xaf,0xa0,0x55,0xb5,0xda,0xe0,0x4f,0x81,0x56,0x89,0xe,0x1,0x77,0x7a,0xe3,0xe4,0x83,0xa8,0x5c,0xe6,0x86,0xf6,0xac,0xc,0x64,0x22,0xce,0x46,0x6f,0x2b,0x3,0xc8,0xbc,0xb6,0x96,0xe5,0x39,0x9,0xec,0xd,0xc0,0x8a,0xcc,0xb7,0x67,0xe9,0x17,0x8e,0xd6,0xbf,0xc6,0x4,0x93,0xf3,0x70,0x31,0xde,0x3f,0x38,0xb,0x6,0x49,0x9f,0x62,0x21,0xaa,0xa4,0x2d,0xa6,0x1a,0x7e,0xab,0x74,0x8d,0xb3,0x50,0x4c,0xfe,0xb1,0x47,0x4d,0x65,0xb0,0x16,0x12,0xa,0x7d,0x18,0xf,0xf2,0x94,0xf4,0xd9,0xd4,0x69,0x8f,0xb9,0x88,0x79,0x73,0x72,0x34,0x29,0x9e,0xc3,0xee,0xc1,0x10,0x2,0x97,0xdd,0x9a,0x30,0x2f,0xae,0x1e,0x7,0x68,0x8c,0xd2,0x54,0xd3,0xe2,0xf0,0xca,0x71,0xd7,0xb4,0xc9,0x91,0xe1,0x2c,0x6d,0xf7,0x1f,0x5,0xff,0x0,0x6e,0x5f,0xf1,0xd8,0x27,0x3a,0x92,0x66,0x5b,0xef,0x3d,0xa3,0xf5,0x26,0x7f,0xfb,0x25,0xa9,0x1c,0xcf,0xba,0x3c,0xf9,0x11,0xe8,0x24,0x4e,0x84,0x8b,0xcd,0xfa,0xa7,0xa1,0x4b,0x80,0x9b,0x37,0xc2,0x3e,0xa2,0xdc,0xd5,0x51,0x5d,0xed,0x45,0x82,0x5a,0xc5,0xc7,0x14,0xcb,0x99,0x40,0xbd,0x63,0xb2,0x13,0xbe,0x75,0x4a,0x9c,0x23,0x2e,0x6c,0x1d,0x36,0xd1,0x6a,0x15,0x85,0x42,0x98,0xa5,0x7c,0x41,0xd0,0x3b,0x52,0x8,0xad,0x28,0x61,0x90,0x59,0xeb,0x33,0x32,0x76,0xfd,0x87,0x20,0x35,0xbb,0x1b,0x6b,0x60,0xdb,0xc4,0xa2,0x5e,0xab,0x7,0xcd,0x49,0x40,0x3e,0x1e,0xd9,0x71,0xc1,0x88,0x5b,0x59,0xc6,0x21,0xdc,0x5,0x57,0x22,0x8f,0x2e,0xff,0xbf,0x0,0xd6,0xe9,0xaa,0x81,0xf0,0xb2,0x19,0x89,0xf6,0x4d,0xe0,0x39,0x4,0xde,0xce,0xa7,0x4c,0xdd,0xfd,0xb4,0x31,0x94,0xaf,0x77,0xc5,0xc,0x1b,0x61,0xea,0xae,0x87,0x27,0xa9,0xbc,0x58,0x47,0xfc,0xf7,0xac,0x6,0x41,0xb,0x9b,0x82,0x32,0xb3,0xc8,0x4e,0x10,0xf4,0x56,0x6c,0x7e,0x4f,0x55,0x28,0x4b,0xed,0xf1,0xb0,0x7d,0xd,0x63,0x99,0x83,0x6b,0x6d,0xc3,0xf2,0x9c,0xe,0xa6,0xbb,0x44,0xa1,0x73,0xc7,0xfa,0xe3,0xba,0x69,0x3f,0x80,0x35,0xb9,0x67,0x65,0xa0,0x26,0x53,0xd2,0xb8,0x74,0x8d,0x66,0x51,0x17,0x18,0x1c,0xd7,0x3d,0x3b,0x4a,0x12,0x8b,0x75,0xf,0x98,0x5a,0x23,0x42,0xad,0xec,0x6f,0x9a,0x97,0xa4,0xa3,0xbd,0xfe,0x3,0xd5,0x3a,0xb1,0x38,0x36,0xe8,0x37,0xe2,0x86,0xd0,0xcc,0x2f,0x11,0xd1,0xdb,0x2d,0x62,0x8e,0x8a,0x2c,0xf9,0x93,0x84,0xe1,0x96,0x45,0x68,0x8,0x6e,0x25,0x13,0xf5,0x48,0xee,0xef,0xe5,0x14,0x5f,0x2,0xb5,0xa8,0x9e,0x8c,0x5d,0x72,0x76,0xe4,0xc2,0x60,0xcf,0xd4,0xdf,0x9,0xd8,0x43,0x85,0x64,0xc4,0x24,0xcb,0x1,0x33,0xb6,0xe7,0x7b,0x46,0x29,0xc9,0x3c,0xca,0x1d,0xd3,0x7c,0xeb,0x9d,0x92,0x15,0x1f,0x78,0x7f,0xe6,0x1a,0x7a,0xc0,0x34,0xf8,0x90,0x30,0x6a,0xf3,0xda,0x52,0xbe,0x20,0x54,0x9f,0xb7,0xa5,0x79,0xa,0x2a,0x5c,0x91,0x70,0x95,0xfb,0x2b,0x50,0x16,0xf,0xa9,0x11,0x6c,0x39,0x49,0xb5,0xf4,0xc7,0x2f,0x27,0xdd,0xb6,0xd8,0x29,0x87,0x5,0x4f,0xe8,0x42,0x76,0xf7,0xdf,0xc6,0x54,0xb0,0x8c,0xa,0x3a,0xb,0x12,0x28,0x62,0x17,0x21,0xe4,0x30,0xc9,0x96,0xfc,0x53,0x5c,0x22,0x15,0x79,0x7f,0x58,0x93,0xff,0x0,0x4a,0xe2,0x83,0xbe,0xe5,0x37,0x2d,0x7b,0xa7,0xfe,0xfd,0x23,0xc4,0x71,0x41,0x13,0x65,0x98,0x6a,0xbb,0x66,0xcb,0x92,0xad,0xfb,0x44,0xb4,0xf6,0xee,0xc5,0xef,0x43,0xe6,0x1a,0x4,0x7a,0x89,0xd,0x35,0x85,0x5a,0x9d,0x1d,0x82,0xcc,0x1f,0x81,0x48,0xeb,0x33,0xae,0xea,0x5f,0x25,0xed,0xf8,0xc3,0x63,0xb8,0xb3,0x1c,0x3,0xb2,0x9,0x5d,0xcd,0x40,0x9a,0xa4,0x7d,0x8,0x99,0x8a,0xe3,0x75,0xd0,0xb9,0xf0,0xa3,0x3f,0x77,0xf2,0x8d,0x78,0x2,0x6d,0x97,0x38,0x8e,0x59,0xd6,0x51,0xaf,0xd9,0x86,0x24,0x32,0xa0,0x9b,0x4d,0x8b,0x90,0xc1,0x20,0x9c,0x7,0x8f,0x45,0x80,0x60,0xdb,0xf3,0x64,0x10,0x4e,0x6e,0xe1,0x3d,0x34,0xd1,0x18,0xd5,0x14,0x52,0xbf,0x6f,0x3b,0xa2,0x5b,0x3c,0x84,0x70,0x5e,0x3e,0x74,0x2e,0xbc,0xd4,0x16,0xfa,0xb7,0x9e,0x47,0x91,0xf9,0xba,0x7c,0x72,0x7e,0xf5,0xa6,0xc2,0xac,0x73,0x6b,0x55,0x94,0x88,0xcf,0x31,0xe,0x56,0x1e,0x67,0x4b,0xdc,0xa8,0x2b,0x6,0xe9,0xe0,0xe7,0xde,0xd3,0xb1,0xc,0x61,0x57,0xa1,0x50,0xaa,0xab,0xf1,0xec,0x1b,0x46,0x19,0x36,0xda,0xc8,0x69,0x26,0x95,0x9f,0x68,0xbd,0xca,0xce,0xa5,0xd2,0xd7,0xc0,0x4c,0x2a,0x1,0x2c,0xab,0x6c,0xb3,0x3,0x29,0xfa,0xb4,0x2b,0x2c,0xd0,0x75,0xd9,0x3b,0xbf,0x4c,0x32,0x72,0xcd,0x9b,0xa4,0xf3,0xd8,0xc0,0x82,0xae,0x53,0x25,0x77,0xfd,0x50,0x8d,0x5c,0xd5,0xbc,0xaf,0x3e,0xc6,0x8f,0xe6,0x43,0xfb,0x6b,0x3f,0x84,0x4b,0x92,0xac,0x76,0x55,0xf5,0xce,0xdb,0x35,0x2a,0x85,0x8e,0x5,0xdd,0x7e,0xb7,0x13,0x69,0xdc,0x98,0x3c,0xba,0x86,0x62,0x1e,0x24,0x3d,0xc,0x74,0xde,0x79,0x33,0xf0,0xe9,0xc1,0x40,0xeb,0x11,0x19,0xf1,0xb1,0x1f,0xee,0x80,0x5a,0x27,0x9f,0x39,0xc2,0x83,0x7f,0xf,0xc8,0x91,0x4d,0x1b,0x47,0xf2,0x15,0xcb,0xd4,0x7c,0x36,0xc9,0x1,0xd3,0x88,0xb5,0x23,0x14,0x6a,0x65,0xa5,0x6e,0x49,0x4f,0xd2,0x17,0x21,0x54,0xca,0xa0,0xff,0x6,0xdf,0x30,0x1d,0x9e,0xe5,0xe8,0xd1,0xd6,0x60,0x38,0x7,0xf9,0xea,0x7d,0x51,0x28,0x45,0x9a,0xf4,0x90,0xbe,0xa2,0x63,0x5d,0x8c,0xcf,0xa7,0x71,0xc3,0x48,0x44,0x4a,0xf6,0xe1,0xe4,0x93,0x1a,0x37,0x1c,0x7a,0xa9,0xa3,0x10,0x5f,0xf8,0xfc,0x8b,0x5e,0x70,0x2d,0xda,0xc7,0xfe,0xec,0x0,0x2f,0x61,0x57,0x3a,0x87,0x9d,0x9c,0x66,0x97,0x31,0xaa,0x16,0xf7,0x56,0xb6,0x73,0xb9,0x96,0x4,0x12,0xb0,0xa6,0xbd,0x7b,0xad,0x6f,0xb8,0xe,0xa1,0xef,0x99,0x67,0xe0,0xc4,0x41,0x9,0x95,0x5b,0x34,0x4e,0xbb,0xe2,0x8a,0x18,0x42,0xa8,0x81,0xcc,0x20,0xa,0x6d,0x94,0xd,0x8,0x68,0x46,0xb2,0xe3,0x2e,0xe7,0x2,0x59,0x89,0x64,0x22,0x26,0x52,0xc5,0xed,0xb,0xd7,0x58,0x78,0x89,0x2b,0xd,0x9f,0xe0,0x36,0x3d,0x26,0x8d,0x6c,0xaa,0x31,0xe8,0x22,0xcd,0x2d,0x92,0xe,0x5f,0xda,0xd5,0x20,0xc0,0xaf,0x95,0x3a,0xf4,0x23,0xfc,0x7b,0x74,0x2,0xf,0x96,0x91,0xf6,0xdd,0x29,0x93,0xf3,0x83,0xd9,0x79,0x11,0x57,0xbb,0x33,0x1a,0x5e,0x76,0xbd,0xc9,0xc3,0xe3,0x90,0x4c,0x7c,0x99,0x78,0xb5,0xff,0xb9,0xc2,0x12,0x9c,0x62,0xfb,0xa3,0xca,0xb3,0x71,0xe6,0x86,0x5,0x44,0xab,0x4a,0x4d,0x7e,0x73,0x3c,0xea,0x17,0x54,0xdf,0xd1,0x58,0xd3,0x6f,0xb,0xde,0x1,0xf8,0xc6,0x25,0x39,0x8b,0xc4,0x32,0x38,0x10,0xc5,0x63,0x67,0x7f,0x8,0x6d,0x7a,0x87,0xe1,0x81,0xac,0xa1,0x1c,0xfa,0xcc,0xfd,0xc,0x6,0x7,0x41,0x5c,0xeb,0xb6,0x9b,0xb4,0x65,0x77,0xe2,0xa8,0xef,0x45,0x5a,0xdb,0x6b,0x72,0x1d,0xf9,0xa7,0x21,0xa6,0x97,0x85,0xbf,0x4,0xa2,0xc1,0xbc,0xe4,0x94,0x59,0x18,0x82,0x6a,0x70,0x8a,0x75,0x1b,0x2a,0x84,0xad,0x52,0x4f,0xe7,0x13,0x2e,0x9a,0x48,0xd6,0x80,0x53,0xa,0x8e,0x50,0xdc,0x69,0xba,0xcf,0x49,0x8c,0x64,0x9d,0x51,0x3b,0xf1,0xfe,0xb8,0x8f,0xd2,0xd4,0x3e,0xf5,0xee,0x42,0xb7,0x4b,0xd7,0xa9,0xa0,0x24,0x28,0x98,0x30,0xf7,0x2f,0xb0,0xb2,0x61,0xbe,0xec,0x35,0xc8,0x16,0xc7,0x66,0xcb,0x0,0x3f,0xe9,0x56,0x5b,0x19,0x68,0x43,0xa4,0x1f,0x60,0xf0,0x37,0xed,0xd0,0x9,0x34,0xa5,0x4e,0x27,0x7d,0xd8,0x5d,0x14,0xe5,0x2c,0x9e,0x46,0x47,0x3,0x88,0xf2,0x55,0x40,0xce,0x6e,0x1e,0x15,0xae,0xb1,0xdb,0x27,0xd2,0x7e,0xb4,0x30,0x39,0x47,0x67,0xa0,0x8,0xb8,0xf1,0x22,0x20,0xbf,0x58,0xa5,0x7c,0x2e,0x5b,0xf6,0x57,0x86,0xc6,0x79,0xaf,0x90,0xd3,0xf8,0x89,0xcb,0x60,0xf0,0x8f,0x34,0x99,0x40,0x7d,0xa7,0xb7,0xde,0x35,0xa4,0x84,0xcd,0x48,0xed,0xd6,0xe,0xbc,0x75,0x62,0x18,0x93,0xd7,0xfe,0x5e,0xd0,0xc5,0x21,0x3e,0x85,0x8e,0xd5,0x7f,0x38,0x72,0xe2,0xfb,0x4b,0xca,0xb1,0x37,0x69,0x8d,0x2f,0x15,0x7,0x36,0x2c,0x51,0x32,0x94,0x88,0xc9,0x4,0x74,0x1a,0xe0,0xfa,0x12,0x14,0xba,0x8b,0xe5,0x77,0xdf,0xc2,0x3d,0xd8,0xa,0xbe,0x83,0x9a,0xc3,0x10,0x46,0xf9,0x4c,0xc0,0x1e,0x1c,0xd9,0x5f,0x2a,0xab,0xc1,0xd,0xf4,0x1f,0x28,0x6e,0x61,0x65,0xae,0x44,0x42,0x33,0x6b,0xf2,0xc,0x76,0xe1,0x23,0x5a,0x3b,0xd4,0x95,0x16,0xe3,0xee,0xdd,0xda,0xc4,0x87,0x7a,0xac,0x43,0xc8,0x41,0x4f,0x91,0x4e,0x9b,0xff,0xa9,0xb5,0x56,0x68,0xa8,0xa2,0x54,0x1b,0xf7,0xf3,0x55,0x80,0xea,0xfd,0x98,0xef,0x3c,0x11,0x71,0x17,0x5c,0x6a,0x8c,0x31,0x97,0x96,0x9c,0x6d,0x26,0x7b,0xcc,0xd1,0xe7,0xf5,0x24,0xb,0xf,0x9d,0xbb,0x19,0xb6,0xad,0xa6,0x70,0xa1,0x3a,0xfc,0x1d,0xbd,0x5d,0xb2,0x78,0x4a,0xcf,0x9e,0x2,0x3f,0x50,0xb0,0x45,0xb3,0x64,0xaa,0x5,0x92,0xe4,0xeb,0x6c,0x66,0x1,0x6,0x9f,0x63,0x3,0xb9,0x4d,0x81,0xe9,0x49,0x13,0x8a,0xa3,0x2b,0xc7,0x59,0x2d,0xe6,0xce,0xdc,0x0,0x73,0x53,0x25,0xe8,0x9,0xec,0x82,0x52,0x29,0x6f,0x8f,0x29,0x91,0xec,0xb9,0xc9,0x35,0x74,0x47,0xaf,0xa7,0x5d,0x36,0x58,0xa9,0x7,0x85,0xcf,0x68,0xc2,0xf6,0x77,0x5f,0x46,0xd4,0x30,0xc,0x8a,0xba,0x8b,0x92,0xa8,0xe2,0x97,0xa1,0x64,0xb0,0x49,0x16,0x7c,0xd3,0xdc,0xa2,0x95,0xf9,0xff,0xd8,0x13,0x7f,0x80,0xca,0x62,0x3,0x3e,0x65,0xb7,0xad,0xfb,0x27,0x7e,0x7d,0xa3,0x44,0xf1,0xc1,0x93,0xe5,0x18,0xea,0x3b,0xe6,0x4b,0x12,0x2d,0x7b,0xc4,0x34,0x76,0x6e,0x45,0x6f,0xc3,0x66,0x9a,0x84,0xfa,0x9,0x8d,0xb5,0x5,0xda,0x1d,0x9d,0x2,0x4c,0x9f,0x1,0xc8,0x6b,0xb3,0x2e,0x6a,0xdf,0xa5,0x6d,0x78,0x43,0xe3,0x38,0x33,0x9c,0x83,0x32,0x89,0xdd,0x4d,0xc0,0x1a,0x24,0xfd,0x88,0x19,0xa,0x63,0xf5,0x50,0x39,0x70,0x23,0xbf,0xf7,0x72,0xd,0xf8,0x82,0xed,0x17,0xb8,0xe,0xd9,0x56,0xd1,0x2f,0x59,0x6,0xa4,0xb2,0x20,0x1b,0xcd,0xb,0x10,0x41,0xa0,0x1c,0x87,0xf,0xc5,0x0,0xe0,0x5b,0x73,0xe4,0x90,0xce,0xee,0x61,0xbd,0xb4,0x51,0x98,0x55,0x94,0xd2,0x3f,0xef,0xbb,0x22,0xdb,0xbc,0x4,0xf0,0xde,0xbe,0xf4,0xae,0x3c,0x54,0x96,0x7a,0x37,0x1e,0xc7,0x11,0x79,0x3a,0xfc,0xf2,0xfe,0x75,0x26,0x42,0x2c,0xf3,0xeb,0xd5,0x14,0x8,0x4f,0xb1,0x8e,0xd6,0x9e,0xe7,0xcb,0x5c,0x28,0xab,0x86,0x69,0x60,0x67,0x5e,0x53,0x31,0x8c,0xe1,0xd7,0x21,0xd0,0x2a,0x2b,0x71,0x6c,0x9b,0xc6,0x99,0xb6,0x5a,0x48,0xe9,0xa6,0x15,0x1f,0xe8,0x3d,0x4a,0x4e,0x25,0x52,0x57,0x40,0xcc,0xaa,0x81,0xac,0x55,0x92,0x4d,0xfd,0xd7,0x4,0x4a,0xd5,0xd2,0x2e,0x8b,0x27,0xc5,0x41,0xb2,0xcc,0x8c,0x33,0x65,0x5a,0xd,0x26,0x3e,0x7c,0x50,0xad,0xdb,0x89,0x3,0xae,0x73,0xa2,0x2b,0x42,0x51,0xc0,0x38,0x71,0x18,0xbd,0x5,0x95,0xc1,0x7a,0xb5,0x6c,0x52,0x88,0xab,0xb,0x30,0x25,0xcb,0xd4,0x7b,0x70,0xfb,0x23,0x80,0x49,0xed,0x97,0x22,0x66,0xc2,0x44,0x78,0x9c,0xe0,0xda,0xc3,0xf2,0x8a,0x20,0x87,0xcd,0xe,0x17,0x3f,0xbe,0x15,0xef,0xe7,0xf,0x4f,0xe1,0x10,0x7e,0xa4,0xd9,0x61,0xc7,0x3c,0x7d,0x81,0xf1,0x36,0x6f,0xb3,0xe5,0xb9,0xc,0xeb,0x35,0x2a,0x82,0xc8,0x37,0xff,0x2d,0x76,0x4b,0xdd,0xea,0x94,0x9b,0x5b,0x90,0xb7,0xb1,0x2c,0xe9,0xdf,0xaa,0x34,0x5e,0x1,0xf8,0x21,0xce,0xe3,0x60,0x1b,0x16,0x2f,0x28,0x9e,0xc6,0xf9,0x7,0x14,0x83,0xaf,0xd6,0xbb,0x64,0xa,0x6e,0x40,0x5c,0x9d,0xa3,0x72,0x31,0x59,0x8f,0x3d,0xb6,0xba,0xb4,0x8,0x1f,0x1a,0x6d,0xe4,0xc9,0xe2,0x84,0x57,0x5d,0xee,0xa1,0x6,0x2,0x75,0xa0,0x8e,0xd3,0x24,0x39,0x0,0x12,0xfe,0xd1,0x9f,0xa9,0xc4,0x79,0x63,0x62,0x98,0x69,0xcf,0x54,0xe8,0x9,0xa8,0x48,0x8d,0x47,0x68,0xfa,0xec,0x4e,0x58,0x43,0x85,0x53,0x91,0x46,0xf0,0x5f,0x11,0x67,0x99,0x1e,0x3a,0xbf,0xf7,0x6b,0xa5,0xca,0xb0,0x45,0x1c,0x74,0xe6,0xbc,0x56,0x7f,0x32,0xde,0xf4,0x93,0x6a,0xf3,0xf6,0x96,0xb8,0x4c,0x1d,0xd0,0x19,0xfc,0xa7,0x77,0x9a,0xdc,0xd8,0xac,0x3b,0x13,0xf5,0x29,0xa6,0x86,0x27,0x85,0xa3,0x31,0x4e,0x98,0x93,0x88,0x23,0xc2,0x4,0x9f,0x46,0x8c,0x63,0x83,0x3c,0xa0,0xf1,0x74,0x7b,0x8e,0x6e,0x1,0x3b,0x94,0x5a,0x8d,0x52,0xd5,0xda,0xac,0xa1,0x38,0x3f,0x58,0x73,0x87,0x3d,0x5d,0x2d,0x77,0xd7,0xbf,0xf9,0x15,0x9d,0xb4,0xf0,0xd8,0x13,0x67,0x6d,0x4d,0x3e,0xe2,0xd2,0x37,0xd6,0x1b,0x51,0x17,0x6c,0xbc,0x32,0xcc,0x55,0xd,0x64,0x1d,0xdf,0x48,0x28,0xab,0xea,0x5,0xe4,0xe3,0xd0,0xdd,0x92,0x44,0xb9,0xfa,0x71,0x7f,0xf6,0x7d,0xc1,0xa5,0x70,0xaf,0x56,0x68,0x8b,0x97,0x25,0x6a,0x9c,0x96,0xbe,0x6b,0xcd,0xc9,0xd1,0xa6,0xc3,0xd4,0x29,0x4f,0x2f,0x2,0xf,0xb2,0x54,0x62,0x53,0xa2,0xa8,0xa9,0xef,0xf2,0x45,0x18,0x35,0x1a,0xcb,0xd9,0x4c,0x6,0x41,0xeb,0xf4,0x75,0xc5,0xdc,0xb3,0x57,0x9,0x8f,0x8,0x39,0x2b,0x11,0xaa,0xc,0x6f,0x12,0x4a,0x3a,0xf7,0xb6,0x2c,0xc4,0xde,0x24,0xdb,0xb5,0x84,0x2a,0x3,0xfc,0xe1,0x49,0xbd,0x80,0x34,0xe6,0x78,0x2e,0xfd,0xa4,0x20,0xfe,0x72,0xc7,0x14,0x61,0xe7,0x22,0xca,0x33,0xff,0x95,0x5f,0x50,0x16,0x21,0x7c,0x7a,0x90,0x5b,0x40,0xec,0x19,0xe5,0x79,0x7,0xe,0x8a,0x86,0x36,0x9e,0x59,0x81,0x1e,0x1c,0xcf,0x10,0x42,0x9b,0x66,0xb8,0x69,0xc8,0x65,0xae,0x91,0x47,0xf8,0xf5,0xb7,0xc6,0xed,0xa,0xb1,0xce,0x5e,0x99,0x43,0x7e,0xa7,0x9a,0xb,0xe0,0x89,0xd3,0x76,0xf3,0xba,0x4b,0x82,0x30,0xe8,0xe9,0xad,0x26,0x5c,0xfb,0xee,0x60,0xc0,0xb0,0xbb,0x0,0x1f,0xd7,0x2b,0xde,0x72,0xb8,0x3c,0x35,0x4b,0x6b,0xac,0x4,0xb4,0xfd,0x2e,0x2c,0xb3,0x54,0xa9,0x70,0x22,0x57,0xfa,0x5b,0x8a,0xca,0x75,0xa3,0x9c,0xdf,0xf4,0x85,0xc7,0x6c,0xfc,0x83,0x38,0x95,0x4c,0x71,0xab,0xbb,0xd2,0x39,0xa8,0x88,0xc1,0x44,0xe1,0xda,0x2,0xb0,0x79,0x6e,0x14,0x9f,0xdb,0xf2,0x52,0xdc,0xc9,0x2d,0x32,0x89,0x82,0xd9,0x73,0x34,0x7e,0xee,0xf7,0x47,0xc6,0xbd,0x3b,0x65,0x81,0x23,0x19,0xb,0x3a,0x20,0x5d,0x3e,0x98,0x84,0xc5,0x8,0x78,0x16,0xec,0xf6,0x1e,0x18,0xb6,0x87,0xe9,0x7b,0xd3,0xce,0x31,0xd4,0x6,0xb2,0x8f,0x96,0xcf,0x1c,0x4a,0xf5,0x40,0xcc,0x12,0x10,0xd5,0x53,0x26,0xa7,0xcd,0x1,0xf8,0x13,0x24,0x62,0x6d,0x69,0xa2,0x48,0x4e,0x3f,0x67,0xfe,0x0,0x7a,0xed,0x2f,0x56,0x37,0xd8,0x99,0x1a,0xef,0xe2,0xd1,0xd6,0xc8,0x8b,0x76,0xa0,0x4f,0xc4,0x4d,0x43,0x9d,0x42,0x97,0xf3,0xa5,0xb9,0x5a,0x64,0xa4,0xae,0x58,0x17,0xfb,0xff,0x59,0x8c,0xe6,0xf1,0x94,0xe3,0x30,0x1d,0x7d,0x1b,0x50,0x66,0x80,0x3d,0x9b,0x9a,0x90,0x61,0x2a,0x77,0xc0,0xdd,0xeb,0xf9,0x28,0x7,0x3,0x91,0xb7,0x15,0xba,0xa1,0xaa,0x7c,0xad,0x36,0xf0,0x11,0xb1,0x51,0xbe,0x74,0x46,0xc3,0x92,0xe,0x33,0x5c,0xbc,0x49,0xbf,0x68,0xa6,0x9,0x9e,0xe8,0xe7,0x60,0x6a,0xd,0xa,0x93,0x6f,0xf,0xb5,0x41,0x8d,0xe5,0x45,0x1f,0x86,0xaf,0x27,0xcb,0x55,0x21,0xea,0xc2,0xd0,0xc,0x7f,0x5f,0x29,0xe4,0x5,0xe0,0x8e,0x5e,0x25,0x63,0xcc,0x6a,0xd2,0xaf,0xfa,0x8a,0x76,0x37,0x4,0xec,0xe4,0x1e,0x75,0x1b,0xea,0x44,0xc6,0x8c,0x2b,0x81,0xb5,0x34,0x1c,0x5,0x97,0x73,0x4f,0xc9,0xf9,0xc8,0xd1,0xeb,0xa1,0xd4,0xe2,0x27,0xf3,0xa,0x55,0x3f,0x90,0x9f,0xe1,0xd6,0xba,0xbc,0x9b,0x50,0x3c,0xc3,0x89,0x21,0x40,0x7d,0x26,0xf4,0xee,0xb8,0x64,0x3d,0x3e,0xe0,0x7,0xb2,0x82,0xd0,0xa6,0x5b,0xa9,0x78,0xa5,0x8,0x51,0x6e,0x38,0x87,0x77,0x35,0x2d,0x6,0x2c,0x80,0x25,0xd9,0xc7,0xb9,0x4a,0xce,0xf6,0x46,0x99,0x5e,0xde,0x41,0xf,0xdc,0x42,0x8b,0x28,0xf0,0x6d,0x29,0x9c,0xe6,0x2e,0x3b,0x0,0xa0,0x7b,0x70,0xdf,0xc0,0x71,0xca,0x9e,0xe,0x83,0x59,0x67,0xbe,0xcb,0x5a,0x49,0x20,0xb6,0x13,0x7a,0x33,0x60,0xfc,0xb4,0x31,0x4e,0xbb,0xc1,0xae,0x54,0xfb,0x4d,0x9a,0x15,0x92,0x6c,0x1a,0x45,0xe7,0xf1,0x63,0x58,0x8e,0x48,0x53,0x2,0xe3,0x5f,0xc4,0x4c,0x86,0x43,0xa3,0x18,0x30,0xa7,0xd3,0x8d,0xad,0x22,0xfe,0xf7,0x12,0xdb,0x16,0xd7,0x91,0x7c,0xac,0xf8,0x61,0x98,0xff,0x47,0xb3,0x9d,0xfd,0xb7,0xed,0x7f,0x17,0xd5,0x39,0x74,0x5d,0x84,0x52,0x3a,0x79,0xbf,0xb1,0xbd,0x36,0x65,0x1,0x6f,0xb0,0xa8,0x96,0x57,0x4b,0xc,0xf2,0xcd,0x95,0xdd,0xa4,0x88,0x1f,0x6b,0xe8,0xc5,0x2a,0x23,0x24,0x1d,0x10,0x72,0xcf,0xa2,0x94,0x62,0x93,0x69,0x68,0x32,0x2f,0xd8,0x85,0xda,0xf5,0x19,0xb,0xaa,0xe5,0x56,0x5c,0xab,0x7e,0x9,0xd,0x66,0x11,0x14,0x3,0x8f,0xe9,0xc2,0xef,0xb5,0x72,0xad,0x1d,0x37,0xe4,0xaa,0x35,0x32,0xce,0x6b,0xc7,0x25,0xa1,0x52,0x2c,0x6c,0xd3,0x85,0xba,0xed,0xc6,0xde,0x9c,0xb0,0x4d,0x3b,0x69,0xe3,0x4e,0x93,0x42,0xcb,0xa2,0xb1,0x20,0xd8,0x91,0xf8,0x5d,0xe5,0x75,0x21,0x9a,0x55,0x8c,0xb2,0x68,0x4b,0xeb,0xd0,0xc5,0x2b,0x34,0x9b,0x90,0x1b,0xc3,0x60,0xa9,0xd,0x77,0xc2,0x86,0x22,0xa4,0x98,0x7c,0x0,0x3a,0x23,0x12,0x6a,0xc0,0x67,0x2d,0xee,0xf7,0xdf,0x5e,0xf5,0xf,0x7,0xef,0xaf,0x1,0xf0,0x9e,0x44,0x39,0x81,0x27,0xdc,0x9d,0x61,0x11,0xd6,0x8f,0x53,0x5,0x59,0xec,0xb,0xd5,0xca,0x62,0x28,0xd7,0x1f,0xcd,0x96,0xab,0x3d,0xa,0x74,0x7b,0xbb,0x70,0x57,0x51,0xcc,0x9,0x3f,0x4a,0xd4,0xbe,0xe1,0x18,0xc1,0x2e,0x3,0x80,0xfb,0xf6,0xcf,0xc8,0x7e,0x26,0x19,0xe7,0xf4,0x63,0x4f,0x36,0x5b,0x84,0xea,0x8e,0xa0,0xbc,0x7d,0x43,0x92,0xd1,0xb9,0x6f,0xdd,0x56,0x5a,0x54,0xe8,0xff,0xfa,0x8d,0x4,0x29,0x2,0x64,0xb7,0xbd,0xe,0x41,0xe6,0xe2,0x95,0x40,0x6e,0x33,0xc4,0xd9,0xe0,0xf2,0x1e,0x31,0x7f,0x49,0x24,0x99,0x83,0x82,0x78,0x89,0x2f,0xb4,0x8,0xe9,0x48,0xa8,0x6d,0xa7,0x88,0x1a,0xc,0xae,0xb8,0xa3,0x65,0xb3,0x71,0xa6,0x10,0xbf,0xf1,0x87,0x79,0xfe,0xda,0x5f,0x17,0x8b,0x45,0x2a,0x50,0xa5,0xfc,0x94,0x6,0x5c,0xb6,0x9f,0xd2,0x3e,0x14,0x73,0x8a,0x13,0x16,0x76,0x58,0xac,0xfd,0x30,0xf9,0x1c,0x47,0x97,0x7a,0x3c,0x38,0x4c,0xdb,0xf3,0x15,0xc9,0x46,0x66,0x26,0x84,0xa2,0x30,0x4f,0x99,0x92,0x89,0x22,0xc3,0x5,0x9e,0x47,0x8d,0x62,0x82,0x3d,0xa1,0xf0,0x75,0x7a,0x8f,0x6f,0x0,0x3a,0x95,0x5b,0x8c,0x53,0xd4,0xdb,0xad,0xa0,0x39,0x3e,0x59,0x72,0x86,0x3c,0x5c,0x2c,0x76,0xd6,0xbe,0xf8,0x14,0x9c,0xb5,0xf1,0xd9,0x12,0x66,0x6c,0x4c,0x3f,0xe3,0xd3,0x36,0xd7,0x1a,0x50,0x16,0x6d,0xbd,0x33,0xcd,0x54,0xc,0x65,0x1c,0xde,0x49,0x29,0xaa,0xeb,0x4,0xe5,0xe2,0xd1,0xdc,0x93,0x45,0xb8,0xfb,0x70,0x7e,0xf7,0x7c,0xc0,0xa4,0x71,0xae,0x57,0x69,0x8a,0x96,0x24,0x6b,0x9d,0x97,0xbf,0x6a,0xcc,0xc8,0xd0,0xa7,0xc2,0xd5,0x28,0x4e,0x2e,0x3,0xe,0xb3,0x55,0x63,0x52,0xa3,0xa9,0xa8,0xee,0xf3,0x44,0x19,0x34,0x1b,0xca,0xd8,0x4d,0x7,0x40,0xea,0xf5,0x74,0xc4,0xdd,0xb2,0x56,0x8,0x8e,0x9,0x38,0x2a,0x10,0xab,0xd,0x6e,0x13,0x4b,0x3b,0xf6,0xb7,0x2d,0xc5,0xdf,0x25,0xda,0xb4,0x85,0x2b,0x2,0xfd,0xe0,0x48,0xbc,0x81,0x35,0xe7,0x79,0x2f,0xfc,0xa5,0x21,0xff,0x73,0xc6,0x15,0x60,0xe6,0x23,0xcb,0x32,0xfe,0x94,0x5e,0x51,0x17,0x20,0x7d,0x7b,0x91,0x5a,0x41,0xed,0x18,0xe4,0x78,0x6,0xf,0x8b,0x87,0x37,0x9f,0x58,0x80,0x1f,0x1d,0xce,0x11,0x43,0x9a,0x67,0xb9,0x68,0xc9,0x64,0xaf,0x90,0x46,0xf9,0xf4,0xb6,0xc7,0xec,0xb,0xb0,0xcf,0x5f,0x98,0x42,0x7f,0xa6,0x9b,0xa,0xe1,0x88,0xd2,0x77,0xf2,0xbb,0x4a,0x83,0x31,0xe9,0xe8,0xac,0x27,0x5d,0xfa,0xef,0x61,0xc1,0xb1,0xba,0x1,0x1e,0x99,0x65,0x90,0x3c,0xf6,0x72,0x7b,0x5,0x25,0xe2,0x4a,0xfa,0xb3,0x60,0x62,0xfd,0x1a,0xe7,0x3e,0x6c,0x19,0xb4,0x15,0xc4,0x84,0x3b,0xed,0xd2,0x91,0xba,0xcb,0x89,0x22,0xb2,0xcd,0x76,0xdb,0x2,0x3f,0xe5,0xf5,0x9c,0x77,0xe6,0xc6,0x8f,0xa,0xaf,0x94,0x4c,0xfe,0x37,0x20,0x5a,0xd1,0x95,0xbc,0x1c,0x92,0x87,0x63,0x7c,0xc7,0xcc,0x97,0x3d,0x7a,0x30,0xa0,0xb9,0x9,0x88,0xf3,0x75,0x2b,0xcf,0x6d,0x57,0x45,0x74,0x6e,0x13,0x70,0xd6,0xca,0x8b,0x46,0x36,0x58,0xa2,0xb8,0x50,0x56,0xf8,0xc9,0xa7,0x35,0x9d,0x80,0x7f,0x9a,0x48,0xfc,0xc1,0xd8,0x81,0x52,0x4,0xbb,0xe,0x82,0x5c,0x5e,0x9b,0x1d,0x68,0xe9,0x83,0x4f,0xb6,0x5d,0x6a,0x2c,0x23,0x27,0xec,0x6,0x0,0x71,0x29,0xb0,0x4e,0x34,0xa3,0x61,0x18,0x79,0x96,0xd7,0x54,0xa1,0xac,0x9f,0x98,0x86,0xc5,0x38,0xee,0x1,0x8a,0x3,0xd,0xd3,0xc,0xd9,0xbd,0xeb,0xf7,0x14,0x2a,0xea,0xe0,0x16,0x59,0xb5,0xb1,0x17,0xc2,0xa8,0xbf,0xda,0xad,0x7e,0x53,0x33,0x55,0x1e,0x28,0xce,0x73,0xd5,0xd4,0xde,0x2f,0x64,0x39,0x8e,0x93,0xa5,0xb7,0x66,0x49,0x4d,0xdf,0xf9,0x5b,0xf4,0xef,0xe4,0x32,0xe3,0x78,0xbe,0x5f,0xff,0x1f,0xf0,0x3a,0x8,0x8d,0xdc,0x40,0x7d,0x12,0xf2,0x7,0xf1,0x26,0xe8,0x47,0xd0,0xa6,0xa9,0x2e,0x24,0x43,0x44,0xdd,0x21,0x41,0xfb,0xf,0xc3,0xab,0xb,0x51,0xc8,0xe1,0x69,0x85,0x1b,0x6f,0xa4,0x8c,0x9e,0x42,0x31,0x11,0x67,0xaa,0x4b,0xae,0xc0,0x10,0x6b,0x2d,0xca,0x6c,0xd4,0xa9,0xfc,0x8c,0x70,0x31,0x2,0xea,0xe2,0x18,0x73,0x1d,0xec,0x42,0xc0,0x8a,0x2d,0x87,0xb3,0x32,0x1a,0x3,0x91,0x75,0x49,0xcf,0xff,0xce,0xd7,0xed,0xa7,0xd2,0xe4,0x21,0xf5,0xc,0x53,0x39,0x96,0x99,0xe7,0xd0,0xbc,0xba,0x9d,0x56,0x3a,0xc5,0x8f,0x27,0x46,0x7b,0x20,0xf2,0xe8,0xbe,0x62,0x3b,0x38,0xe6,0x1,0xb4,0x84,0xd6,0xa0,0x5d,0xaf,0x7e,0xa3,0xe,0x57,0x68,0x3e,0x81,0x71,0x33,0x2b,0x0,0x2a,0x86,0x23,0xdf,0xc1,0xbf,0x4c,0xc8,0xf0,0x40,0x9f,0x58,0xd8,0x47,0x9,0xda,0x44,0x8d,0x2e,0xf6,0x6b,0x2f,0x9a,0xe0,0x28,0x3d,0x6,0xa6,0x7d,0x76,0xd9,0xc6,0x77,0xcc,0x98,0x8,0x85,0x5f,0x61,0xb8,0xcd,0x5c,0x4f,0x26,0xb0,0x15,0x7c,0x35,0x66,0xfa,0xb2,0x37,0x48,0xbd,0xc7,0xa8,0x52,0xfd,0x4b,0x9c,0x13,0x94,0x6a,0x1c,0x43,0xe1,0xf7,0x65,0x5e,0x88,0x4e,0x55,0x4,0xe5,0x59,0xc2,0x4a,0x80,0x45,0xa5,0x1e,0x36,0xa1,0xd5,0x8b,0xab,0x24,0xf8,0xf1,0x14,0xdd,0x10,0xd1,0x97,0x7a,0xaa,0xfe,0x67,0x9e,0xf9,0x41,0xb5,0x9b,0xfb,0xb1,0xeb,0x79,0x11,0xd3,0x3f,0x72,0x5b,0x82,0x54,0x3c,0x7f,0xb9,0xb7,0xbb,0x30,0x63,0x7,0x69,0xb6,0xae,0x90,0x51,0x4d,0xa,0xf4,0xcb,0x93,0xdb,0xa2,0x8e,0x19,0x6d,0xee,0xc3,0x2c,0x25,0x22,0x1b,0x16,0x74,0xc9,0xa4,0x92,0x64,0x95,0x6f,0x6e,0x34,0x29,0xde,0x83,0xdc,0xf3,0x1f,0xd,0xac,0xe3,0x50,0x5a,0xad,0x78,0xf,0xb,0x60,0x17,0x12,0x5,0x89,0xef,0xc4,0xe9,0x9f,0x58,0x87,0x37,0x1d,0xce,0x80,0x1f,0x18,0xe4,0x41,0xed,0xf,0x8b,0x78,0x6,0x46,0xf9,0xaf,0x90,0xc7,0xec,0xf4,0xb6,0x9a,0x67,0x11,0x43,0xc9,0x64,0xb9,0x68,0xe1,0x88,0x9b,0xa,0xf2,0xbb,0xd2,0x77,0xcf,0x5f,0xb,0xb0,0x7f,0xa6,0x98,0x42,0x61,0xc1,0xfa,0xef,0x1,0x1e,0xb1,0xba,0x31,0xe9,0x4a,0x83,0x27,0x5d,0xe8,0xac,0x8,0x8e,0xb2,0x56,0x2a,0x10,0x9,0x38,0x40,0xea,0x4d,0x7,0xc4,0xdd,0xf5,0x74,0xdf,0x25,0x2d,0xc5,0x85,0x2b,0xda,0xb4,0x6e,0x13,0xab,0xd,0xf6,0xb7,0x4b,0x3b,0xfc,0xa5,0x79,0x2f,0x73,0xc6,0x21,0xff,0xe0,0x48,0x2,0xfd,0x35,0xe7,0xbc,0x81,0x17,0x20,0x5e,0x51,0x91,0x5a,0x7d,0x7b,0xe6,0x23,0x15,0x60,0xfe,0x94,0xcb,0x32,0xeb,0x4,0x29,0xaa,0xd1,0xdc,0xe5,0xe2,0x54,0xc,0x33,0xcd,0xde,0x49,0x65,0x1c,0x71,0xae,0xc0,0xa4,0x8a,0x96,0x57,0x69,0xb8,0xfb,0x93,0x45,0xf7,0x7c,0x70,0x7e,0xc2,0xd5,0xd0,0xa7,0x2e,0x3,0x28,0x4e,0x9d,0x97,0x24,0x6b,0xcc,0xc8,0xbf,0x6a,0x44,0x19,0xee,0xf3,0xca,0xd8,0x34,0x1b,0x55,0x63,0xe,0xb3,0xa9,0xa8,0x52,0xa3,0x5,0x9e,0x22,0xc3,0x62,0x82,0x47,0x8d,0xa2,0x30,0x26,0x84,0x92,0x89,0x4f,0x99,0x5b,0x8c,0x3a,0x95,0xdb,0xad,0x53,0xd4,0xf0,0x75,0x3d,0xa1,0x6f,0x0,0x7a,0x8f,0xd6,0xbe,0x2c,0x76,0x9c,0xb5,0xf8,0x14,0x3e,0x59,0xa0,0x39,0x3c,0x5c,0x72,0x86,0xd7,0x1a,0xd3,0x36,0x6d,0xbd,0x50,0x16,0x12,0x66,0xf1,0xd9,0x3f,0xe3,0x6c,0x4c,0x9f,0x3d,0x1b,0x89,0xf6,0x20,0x2b,0x30,0x9b,0x7a,0xbc,0x27,0xfe,0x34,0xdb,0x3b,0x84,0x18,0x49,0xcc,0xc3,0x36,0xd6,0xb9,0x83,0x2c,0xe2,0x35,0xea,0x6d,0x62,0x14,0x19,0x80,0x87,0xe0,0xcb,0x3f,0x85,0xe5,0x95,0xcf,0x6f,0x7,0x41,0xad,0x25,0xc,0x48,0x60,0xab,0xdf,0xd5,0xf5,0x86,0x5a,0x6a,0x8f,0x6e,0xa3,0xe9,0xaf,0xd4,0x4,0x8a,0x74,0xed,0xb5,0xdc,0xa5,0x67,0xf0,0x90,0x13,0x52,0xbd,0x5c,0x5b,0x68,0x65,0x2a,0xfc,0x1,0x42,0xc9,0xc7,0x4e,0xc5,0x79,0x1d,0xc8,0x17,0xee,0xd0,0x33,0x2f,0x9d,0xd2,0x24,0x2e,0x6,0xd3,0x75,0x71,0x69,0x1e,0x7b,0x6c,0x91,0xf7,0x97,0xba,0xb7,0xa,0xec,0xda,0xeb,0x1a,0x10,0x11,0x57,0x4a,0xfd,0xa0,0x8d,0xa2,0x73,0x61,0xf4,0xbe,0xf9,0x53,0x4c,0xcd,0x7d,0x64,0xb,0xef,0xb1,0x37,0xb0,0x81,0x93,0xa9,0x12,0xb4,0xd7,0xaa,0xf2,0x82,0x4f,0xe,0x94,0x7c,0x66,0x9c,0x63,0xd,0x3c,0x92,0xbb,0x44,0x59,0xf1,0x5,0x38,0x8c,0x5e,0xc0,0x96,0x45,0x1c,0x98,0x46,0xca,0x7f,0xac,0xd9,0x5f,0x9a,0x72,0x8b,0x47,0x2d,0xe7,0xe8,0xae,0x99,0xc4,0xc2,0x28,0xe3,0xf8,0x54,0xa1,0x5d,0xc1,0xbf,0xb6,0x32,0x3e,0x8e,0x26,0xe1,0x39,0xa6,0xa4,0x77,0xa8,0xfa,0x23,0xde,0x0,0xd1,0x70,0xdd,0x16,0x29,0xff,0x40,0x4d,0xf,0x7e,0x55,0xb2,0x9,0x76,0xe6,0x21,0xfb,0xc6,0x1f,0x22,0xb3,0x58,0x31,0x6b,0xce,0x4b,0x2,0xf3,0x3a,0x88,0x50,0x51,0x15,0x9e,0xe4,0x43,0x56,0xd8,0x78,0x8,0x3,0xb8,0xa7,0xe7,0x1b,0xee,0x42,0x88,0xc,0x5,0x7b,0x5b,0x9c,0x34,0x84,0xcd,0x1e,0x1c,0x83,0x64,0x99,0x40,0x12,0x67,0xca,0x6b,0xba,0xfa,0x45,0x93,0xac,0xef,0xc4,0xb5,0xf7,0x5c,0xcc,0xb3,0x8,0xa5,0x7c,0x41,0x9b,0x8b,0xe2,0x9,0x98,0xb8,0xf1,0x74,0xd1,0xea,0x32,0x80,0x49,0x5e,0x24,0xaf,0xeb,0xc2,0x62,0xec,0xf9,0x1d,0x2,0xb9,0xb2,0xe9,0x43,0x4,0x4e,0xde,0xc7,0x77,0xf6,0x8d,0xb,0x55,0xb1,0x13,0x29,0x3b,0xa,0x10,0x6d,0xe,0xa8,0xb4,0xf5,0x38,0x48,0x26,0xdc,0xc6,0x2e,0x28,0x86,0xb7,0xd9,0x4b,0xe3,0xfe,0x1,0xe4,0x36,0x82,0xbf,0xa6,0xff,0x2c,0x7a,0xc5,0x70,0xfc,0x22,0x20,0xe5,0x63,0x16,0x97,0xfd,0x31,0xc8,0x23,0x14,0x52,0x5d,0x59,0x92,0x78,0x7e,0xf,0x57,0xce,0x30,0x4a,0xdd,0x1f,0x66,0x7,0xe8,0xa9,0x2a,0xdf,0xd2,0xe1,0xe6,0xf8,0xbb,0x46,0x90,0x7f,0xf4,0x7d,0x73,0xad,0x72,0xa7,0xc3,0x95,0x89,0x6a,0x54,0x94,0x9e,0x68,0x27,0xcb,0xcf,0x69,0xbc,0xd6,0xc1,0xa4,0xd3,0x0,0x2d,0x4d,0x2b,0x60,0x56,0xb0,0xd,0xab,0xaa,0xa0,0x51,0x1a,0x47,0xf0,0xed,0xdb,0xc9,0x18,0x37,0x33,0xa1,0x87,0x25,0x8a,0x91,0x9a,0x4c,0x9d,0x6,0xc0,0x21,0x81,0x61,0x8e,0x44,0x76,0xf3,0xa2,0x3e,0x3,0x6c,0x8c,0x79,0x8f,0x58,0x96,0x39,0xae,0xd8,0xd7,0x50,0x5a,0x3d,0x3a,0xa3,0x5f,0x3f,0x85,0x71,0xbd,0xd5,0x75,0x2f,0xb6,0x9f,0x17,0xfb,0x65,0x11,0xda,0xf2,0xe0,0x3c,0x4f,0x6f,0x19,0xd4,0x35,0xd0,0xbe,0x6e,0x15,0x53,0xa7,0x1,0xb9,0xc4,0x91,0xe1,0x1d,0x5c,0x6f,0x87,0x8f,0x75,0x1e,0x70,0x81,0x2f,0xad,0xe7,0x40,0xea,0xde,0x5f,0x77,0x6e,0xfc,0x18,0x24,0xa2,0x92,0xa3,0xba,0x80,0xca,0xbf,0x89,0x4c,0x98,0x61,0x3e,0x54,0xfb,0xf4,0x8a,0xbd,0xd1,0xd7,0xf0,0x3b,0x57,0xa8,0xe2,0x4a,0x2b,0x16,0x4d,0x9f,0x85,0xd3,0xf,0x56,0x55,0x8b,0x6c,0xd9,0xe9,0xbb,0xcd,0x30,0xc2,0x13,0xce,0x63,0x3a,0x5,0x53,0xec,0x1c,0x5e,0x46,0x6d,0x47,0xeb,0x4e,0xb2,0xac,0xd2,0x21,0xa5,0x9d,0x2d,0xf2,0x35,0xb5,0x2a,0x64,0xb7,0x29,0xe0,0x43,0x9b,0x6,0x42,0xf7,0x8d,0x45,0x50,0x6b,0xcb,0x10,0x1b,0xb4,0xab,0x1a,0xa1,0xf5,0x65,0xe8,0x32,0xc,0xd5,0xa0,0x31,0x22,0x4b,0xdd,0x78,0x11,0x58,0xb,0x97,0xdf,0x5a,0x25,0xd0,0xaa,0xc5,0x3f,0x90,0x26,0xf1,0x7e,0xf9,0x7,0x71,0x2e,0x8c,0x9a,0x8,0x33,0xe5,0x23,0x38,0x69,0x88,0x34,0xaf,0x27,0xed,0x28,0xc8,0x73,0x5b,0xcc,0xb8,0xe6,0xc6,0x49,0x95,0x9c,0x79,0xb0,0x7d,0xbc,0xfa,0x17,0xc7,0x93,0xa,0xf3,0x94,0x2c,0xd8,0xf6,0x96,0xdc,0x86,0x14,0x7c,0xbe,0x52,0x1f,0x36,0xef,0x39,0x51,0x12,0xd4,0xda,0xd6,0x5d,0xe,0x6a,0x4,0xdb,0xc3,0xfd,0x3c,0x20,0x67,0x99,0xa6,0xfe,0xb6,0xcf,0xe3,0x74,0x0,0x83,0xae,0x41,0x48,0x4f,0x76,0x7b,0x19,0xa4,0xc9,0xff,0x9,0xf8,0x2,0x3,0x59,0x44,0xb3,0xee,0xb1,0x9e,0x72,0x60,0xc1,0x8e,0x3d,0x37,0xc0,0x15,0x62,0x66,0xd,0x7a,0x7f,0x68,0xe4,0x82,0xa9,0x84,0xb,0xcc,0x13,0xa3,0x89,0x5a,0x14,0x8b,0x8c,0x70,0xd5,0x79,0x9b,0x1f,0xec,0x92,0xd2,0x6d,0x3b,0x4,0x53,0x78,0x60,0x22,0xe,0xf3,0x85,0xd7,0x5d,0xf0,0x2d,0xfc,0x75,0x1c,0xf,0x9e,0x66,0x2f,0x46,0xe3,0x5b,0xcb,0x9f,0x24,0xeb,0x32,0xc,0xd6,0xf5,0x55,0x6e,0x7b,0x95,0x8a,0x25,0x2e,0xa5,0x7d,0xde,0x17,0xb3,0xc9,0x7c,0x38,0x9c,0x1a,0x26,0xc2,0xbe,0x84,0x9d,0xac,0xd4,0x7e,0xd9,0x93,0x50,0x49,0x61,0xe0,0x4b,0xb1,0xb9,0x51,0x11,0xbf,0x4e,0x20,0xfa,0x87,0x3f,0x99,0x62,0x23,0xdf,0xaf,0x68,0x31,0xed,0xbb,0xe7,0x52,0xb5,0x6b,0x74,0xdc,0x96,0x69,0xa1,0x73,0x28,0x15,0x83,0xb4,0xca,0xc5,0x5,0xce,0xe9,0xef,0x72,0xb7,0x81,0xf4,0x6a,0x0,0x5f,0xa6,0x7f,0x90,0xbd,0x3e,0x45,0x48,0x71,0x76,0xc0,0x98,0xa7,0x59,0x4a,0xdd,0xf1,0x88,0xe5,0x3a,0x54,0x30,0x1e,0x2,0xc3,0xfd,0x2c,0x6f,0x7,0xd1,0x63,0xe8,0xe4,0xea,0x56,0x41,0x44,0x33,0xba,0x97,0xbc,0xda,0x9,0x3,0xb0,0xff,0x58,0x5c,0x2b,0xfe,0xd0,0x8d,0x7a,0x67,0x5e,0x4c,0xa0,0x8f,0xc1,0xf7,0x9a,0x27,0x3d,0x3c,0xc6,0x37,0x91,0xa,0xb6,0x57,0xf6,0x16,0xd3,0x19,0x36,0xa4,0xb2,0x10,0x6,0x1d,0xdb,0xd,0xcf,0x18,0xae,0x1,0x4f,0x39,0xc7,0x40,0x64,0xe1,0xa9,0x35,0xfb,0x94,0xee,0x1b,0x42,0x2a,0xb8,0xe2,0x8,0x21,0x6c,0x80,0xaa,0xcd,0x34,0xad,0xa8,0xc8,0xe6,0x12,0x43,0x8e,0x47,0xa2,0xf9,0x29,0xc4,0x82,0x86,0xf2,0x65,0x4d,0xab,0x77,0xf8,0xd8,0xea,0x48,0x6e,0xfc,0x83,0x55,0x5e,0x45,0xee,0xf,0xc9,0x52,0x8b,0x41,0xae,0x4e,0xf1,0x6d,0x3c,0xb9,0xb6,0x43,0xa3,0xcc,0xf6,0x59,0x97,0x40,0x9f,0x18,0x17,0x61,0x6c,0xf5,0xf2,0x95,0xbe,0x4a,0xf0,0x90,0xe0,0xba,0x1a,0x72,0x34,0xd8,0x50,0x79,0x3d,0x15,0xde,0xaa,0xa0,0x80,0xf3,0x2f,0x1f,0xfa,0x1b,0xd6,0x9c,0xda,0xa1,0x71,0xff,0x1,0x98,0xc0,0xa9,0xd0,0x12,0x85,0xe5,0x66,0x27,0xc8,0x29,0x2e,0x1d,0x10,0x5f,0x89,0x74,0x37,0xbc,0xb2,0x3b,0xb0,0xc,0x68,0xbd,0x62,0x9b,0xa5,0x46,0x5a,0xe8,0xa7,0x51,0x5b,0x73,0xa6,0x0,0x4,0x1c,0x6b,0xe,0x19,0xe4,0x82,0xe2,0xcf,0xc2,0x7f,0x99,0xaf,0x9e,0x6f,0x65,0x64,0x22,0x3f,0x88,0xd5,0xf8,0xd7,0x6,0x14,0x81,0xcb,0x8c,0x26,0x39,0xb8,0x8,0x11,0x7e,0x9a,0xc4,0x42,0xc5,0xf4,0xe6,0xdc,0x67,0xc1,0xa2,0xdf,0x87,0xf7,0x3a,0x7b,0xe1,0x9,0x13,0xe9,0x16,0x78,0x49,0xe7,0xce,0x31,0x2c,0x84,0x70,0x4d,0xf9,0x2b,0xb5,0xe3,0x30,0x69,0xed,0x33,0xbf,0xa,0xd9,0xac,0x2a,0xef,0x7,0xfe,0x32,0x58,0x92,0x9d,0xdb,0xec,0xb1,0xb7,0x5d,0x96,0x8d,0x21,0xd4,0x28,0xb4,0xca,0xc3,0x47,0x4b,0xfb,0x53,0x94,0x4c,0xd3,0xd1,0x2,0xdd,0x8f,0x56,0xab,0x75,0xa4,0x5,0xa8,0x63,0x5c,0x8a,0x35,0x38,0x7a,0xb,0x20,0xc7,0x7c,0x3,0x93,0x54,0x8e,0xb3,0x6a,0x57,0xc6,0x2d,0x44,0x1e,0xbb,0x3e,0x77,0x86,0x4f,0xfd,0x25,0x24,0x60,0xeb,0x91,0x36,0x23,0xad,0xd,0x7d,0x76,0xcd,0xd2,0x13,0xef,0x1a,0xb6,0x7c,0xf8,0xf1,0x8f,0xaf,0x68,0xc0,0x70,0x39,0xea,0xe8,0x77,0x90,0x6d,0xb4,0xe6,0x93,0x3e,0x9f,0x4e,0xe,0xb1,0x67,0x58,0x1b,0x30,0x41,0x3,0xa8,0x38,0x47,0xfc,0x51,0x88,0xb5,0x6f,0x7f,0x16,0xfd,0x6c,0x4c,0x5,0x80,0x25,0x1e,0xc6,0x74,0xbd,0xaa,0xd0,0x5b,0x1f,0x36,0x96,0x18,0xd,0xe9,0xf6,0x4d,0x46,0x1d,0xb7,0xf0,0xba,0x2a,0x33,0x83,0x2,0x79,0xff,0xa1,0x45,0xe7,0xdd,0xcf,0xfe,0xe4,0x99,0xfa,0x5c,0x40,0x1,0xcc,0xbc,0xd2,0x28,0x32,0xda,0xdc,0x72,0x43,0x2d,0xbf,0x17,0xa,0xf5,0x10,0xc2,0x76,0x4b,0x52,0xb,0xd8,0x8e,0x31,0x84,0x8,0xd6,0xd4,0x11,0x97,0xe2,0x63,0x9,0xc5,0x3c,0xd7,0xe0,0xa6,0xa9,0xad,0x66,0x8c,0x8a,0xfb,0xa3,0x3a,0xc4,0xbe,0x29,0xeb,0x92,0xf3,0x1c,0x5d,0xde,0x2b,0x26,0x15,0x12,0xc,0x4f,0xb2,0x64,0x8b,0x0,0x89,0x87,0x59,0x86,0x53,0x37,0x61,0x7d,0x9e,0xa0,0x60,0x6a,0x9c,0xd3,0x3f,0x3b,0x9d,0x48,0x22,0x35,0x50,0x27,0xf4,0xd9,0xb9,0xdf,0x94,0xa2,0x44,0xf9,0x5f,0x5e,0x54,0xa5,0xee,0xb3,0x4,0x19,0x2f,0x3d,0xec,0xc3,0xc7,0x55,0x73,0xd1,0x7e,0x65,0x6e,0xb8,0x69,0xf2,0x34,0xd5,0x75,0x95,0x7a,0xb0,0x82,0x7,0x56,0xca,0xf7,0x98,0x78,0x8d,0x7b,0xac,0x62,0xcd,0x5a,0x2c,0x23,0xa4,0xae,0xc9,0xce,0x57,0xab,0xcb,0x71,0x85,0x49,0x21,0x81,0xdb,0x42,0x6b,0xe3,0xf,0x91,0xe5,0x2e,0x6,0x14,0xc8,0xbb,0x9b,0xed,0x20,0xc1,0x24,0x4a,0x9a,0xe1,0xa7,0x18,0xbe,0x6,0x7b,0x2e,0x5e,0xa2,0xe3,0xd0,0x38,0x30,0xca,0xa1,0xcf,0x3e,0x90,0x12,0x58,0xff,0x55,0x61,0xe0,0xc8,0xd1,0x43,0xa7,0x9b,0x1d,0x2d,0x1c,0x5,0x3f,0x75,0x0,0x36,0xf3,0x27,0xde,0x81,0xeb,0x44,0x4b,0x35,0x2,0x6e,0x68,0x4f,0x84,0xe8,0x17,0x5d,0xf5,0x94,0xa9,0xf2,0x20,0x3a,0x6c,0xb0,0xe9,0xea,0x34,0xd3,0x66,0x56,0x4,0x72,0x8f,0x7d,0xac,0x71,0xdc,0x85,0xba,0xec,0x53,0xa3,0xe1,0xf9,0xd2,0xf8,0x54,0xf1,0xd,0x13,0x6d,0x9e,0x1a,0x22,0x92,0x4d,0x8a,0xa,0x95,0xdb,0x8,0x96,0x5f,0xfc,0x24,0xb9,0xfd,0x48,0x32,0xfa,0xef,0xd4,0x74,0xaf,0xa4,0xb,0x14,0xa5,0x1e,0x4a,0xda,0x57,0x8d,0xb3,0x6a,0x1f,0x8e,0x9d,0xf4,0x62,0xc7,0xae,0xe7,0xb4,0x28,0x60,0xe5,0x9a,0x6f,0x15,0x7a,0x80,0x2f,0x99,0x4e,0xc1,0x46,0xb8,0xce,0x91,0x33,0x25,0xb7,0x8c,0x5a,0x9c,0x87,0xd6,0x37,0x8b,0x10,0x98,0x52,0x97,0x77,0xcc,0xe4,0x73,0x7,0x59,0x79,0xf6,0x2a,0x23,0xc6,0xf,0xc2,0x3,0x45,0xa8,0x78,0x2c,0xb5,0x4c,0x2b,0x93,0x67,0x49,0x29,0x63,0x39,0xab,0xc3,0x1,0xed,0xa0,0x89,0x50,0x86,0xee,0xad,0x6b,0x65,0x69,0xe2,0xb1,0xd5,0xbb,0x64,0x7c,0x42,0x83,0x9f,0xd8,0x26,0x19,0x41,0x9,0x70,0x5c,0xcb,0xbf,0x3c,0x11,0xfe,0xf7,0xf0,0xc9,0xc4,0xa6,0x1b,0x76,0x40,0xb6,0x47,0xbd,0xbc,0xe6,0xfb,0xc,0x51,0xe,0x21,0xcd,0xdf,0x7e,0x31,0x82,0x88,0x7f,0xaa,0xdd,0xd9,0xb2,0xc5,0xc0,0xd7,0x5b,0x3d,0x16,0x3b,0xe4,0x23,0xfc,0x4c,0x66,0xb5,0xfb,0x64,0x63,0x9f,0x3a,0x96,0x74,0xf0,0x3,0x7d,0x3d,0x82,0xd4,0xeb,0xbc,0x97,0x8f,0xcd,0xe1,0x1c,0x6a,0x38,0xb2,0x1f,0xc2,0x13,0x9a,0xf3,0xe0,0x71,0x89,0xc0,0xa9,0xc,0xb4,0x24,0x70,0xcb,0x4,0xdd,0xe3,0x39,0x1a,0xba,0x81,0x94,0x7a,0x65,0xca,0xc1,0x4a,0x92,0x31,0xf8,0x5c,0x26,0x93,0xd7,0x73,0xf5,0xc9,0x2d,0x51,0x6b,0x72,0x43,0x3b,0x91,0x36,0x7c,0xbf,0xa6,0x8e,0xf,0xa4,0x5e,0x56,0xbe,0xfe,0x50,0xa1,0xcf,0x15,0x68,0xd0,0x76,0x8d,0xcc,0x30,0x40,0x87,0xde,0x2,0x54,0x8,0xbd,0x5a,0x84,0x9b,0x33,0x79,0x86,0x4e,0x9c,0xc7,0xfa,0x6c,0x5b,0x25,0x2a,0xea,0x21,0x6,0x0,0x9d,0x58,0x6e,0x1b,0x85,0xef,0xb0,0x49,0x90,0x7f,0x52,0xd1,0xaa,0xa7,0x9e,0x99,0x2f,0x77,0x48,0xb6,0xa5,0x32,0x1e,0x67,0xa,0xd5,0xbb,0xdf,0xf1,0xed,0x2c,0x12,0xc3,0x80,0xe8,0x3e,0x8c,0x7,0xb,0x5,0xb9,0xae,0xab,0xdc,0x55,0x78,0x53,0x35,0xe6,0xec,0x5f,0x10,0xb7,0xb3,0xc4,0x11,0x3f,0x62,0x95,0x88,0xb1,0xa3,0x4f,0x60,0x2e,0x18,0x75,0xc8,0xd2,0xd3,0x29,0xd8,0x7e,0xe5,0x59,0xb8,0x19,0xf9,0x3c,0xf6,0xd9,0x4b,0x5d,0xff,0xe9,0xf2,0x34,0xe2,0x20,0xf7,0x41,0xee,0xa0,0xd6,0x28,0xaf,0x8b,0xe,0x46,0xda,0x14,0x7b,0x1,0xf4,0xad,0xc5,0x57,0xd,0xe7,0xce,0x83,0x6f,0x45,0x22,0xdb,0x42,0x47,0x27,0x9,0xfd,0xac,0x61,0xa8,0x4d,0x16,0xc6,0x2b,0x6d,0x69,0x1d,0x8a,0xa2,0x44,0x98,0x17,0x37,0x90,0x32,0x14,0x86,0xf9,0x2f,0x24,0x3f,0x94,0x75,0xb3,0x28,0xf1,0x3b,0xd4,0x34,0x8b,0x17,0x46,0xc3,0xcc,0x39,0xd9,0xb6,0x8c,0x23,0xed,0x3a,0xe5,0x62,0x6d,0x1b,0x16,0x8f,0x88,0xef,0xc4,0x30,0x8a,0xea,0x9a,0xc0,0x60,0x8,0x4e,0xa2,0x2a,0x3,0x47,0x6f,0xa4,0xd0,0xda,0xfa,0x89,0x55,0x65,0x80,0x61,0xac,0xe6,0xa0,0xdb,0xb,0x85,0x7b,0xe2,0xba,0xd3,0xaa,0x68,0xff,0x9f,0x1c,0x5d,0xb2,0x53,0x54,0x67,0x6a,0x25,0xf3,0xe,0x4d,0xc6,0xc8,0x41,0xca,0x76,0x12,0xc7,0x18,0xe1,0xdf,0x3c,0x20,0x92,0xdd,0x2b,0x21,0x9,0xdc,0x7a,0x7e,0x66,0x11,0x74,0x63,0x9e,0xf8,0x98,0xb5,0xb8,0x5,0xe3,0xd5,0xe4,0x15,0x1f,0x1e,0x58,0x45,0xf2,0xaf,0x82,0xad,0x7c,0x6e,0xfb,0xb1,0xf6,0x5c,0x43,0xc2,0x72,0x6b,0x4,0xe0,0xbe,0x38,0xbf,0x8e,0x9c,0xa6,0x1d,0xbb,0xd8,0xa5,0xfd,0x8d,0x40,0x1,0x9b,0x73,0x69,0x93,0x6c,0x2,0x33,0x9d,0xb4,0x4b,0x56,0xfe,0xa,0x37,0x83,0x51,0xcf,0x99,0x4a,0x13,0x97,0x49,0xc5,0x70,0xa3,0xd6,0x50,0x95,0x7d,0x84,0x48,0x22,0xe8,0xe7,0xa1,0x96,0xcb,0xcd,0x27,0xec,0xf7,0x5b,0xae,0x52,0xce,0xb0,0xb9,0x3d,0x31,0x81,0x29,0xee,0x36,0xa9,0xab,0x78,0xa7,0xf5,0x2c,0xd1,0xf,0xde,0x7f,0xd2,0x19,0x26,0xf0,0x4f,0x42,0x0,0x71,0x5a,0xbd,0x6,0x79,0xe9,0x2e,0xf4,0xc9,0x10,0x2d,0xbc,0x57,0x3e,0x64,0xc1,0x44,0xd,0xfc,0x35,0x87,0x5f,0x5e,0x1a,0x91,0xeb,0x4c,0x59,0xd7,0x77,0x7,0xc,0xb7,0xa8,0x23,0xdf,0x2a,0x86,0x4c,0xc8,0xc1,0xbf,0x9f,0x58,0xf0,0x40,0x9,0xda,0xd8,0x47,0xa0,0x5d,0x84,0xd6,0xa3,0xe,0xaf,0x7e,0x3e,0x81,0x57,0x68,0x2b,0x0,0x71,0x33,0x98,0x8,0x77,0xcc,0x61,0xb8,0x85,0x5f,0x4f,0x26,0xcd,0x5c,0x7c,0x35,0xb0,0x15,0x2e,0xf6,0x44,0x8d,0x9a,0xe0,0x6b,0x2f,0x6,0xa6,0x28,0x3d,0xd9,0xc6,0x7d,0x76,0x2d,0x87,0xc0,0x8a,0x1a,0x3,0xb3,0x32,0x49,0xcf,0x91,0x75,0xd7,0xed,0xff,0xce,0xd4,0xa9,0xca,0x6c,0x70,0x31,0xfc,0x8c,0xe2,0x18,0x2,0xea,0xec,0x42,0x73,0x1d,0x8f,0x27,0x3a,0xc5,0x20,0xf2,0x46,0x7b,0x62,0x3b,0xe8,0xbe,0x1,0xb4,0x38,0xe6,0xe4,0x21,0xa7,0xd2,0x53,0x39,0xf5,0xc,0xe7,0xd0,0x96,0x99,0x9d,0x56,0xbc,0xba,0xcb,0x93,0xa,0xf4,0x8e,0x19,0xdb,0xa2,0xc3,0x2c,0x6d,0xee,0x1b,0x16,0x25,0x22,0x3c,0x7f,0x82,0x54,0xbb,0x30,0xb9,0xb7,0x69,0xb6,0x63,0x7,0x51,0x4d,0xae,0x90,0x50,0x5a,0xac,0xe3,0xf,0xb,0xad,0x78,0x12,0x5,0x60,0x17,0xc4,0xe9,0x89,0xef,0xa4,0x92,0x74,0xc9,0x6f,0x6e,0x64,0x95,0xde,0x83,0x34,0x29,0x1f,0xd,0xdc,0xf3,0xf7,0x65,0x43,0xe1,0x4e,0x55,0x5e,0x88,0x59,0xc2,0x4,0xe5,0x45,0xa5,0x4a,0x80,0xb2,0x37,0x66,0xfa,0xc7,0xa8,0x48,0xbd,0x4b,0x9c,0x52,0xfd,0x6a,0x1c,0x13,0x94,0x9e,0xf9,0xfe,0x67,0x9b,0xfb,0x41,0xb5,0x79,0x11,0xb1,0xeb,0x72,0x5b,0xd3,0x3f,0xa1,0xd5,0x1e,0x36,0x24,0xf8,0x8b,0xab,0xdd,0x10,0xf1,0x14,0x7a,0xaa,0xd1,0x97,0xce,0x68,0xd0,0xad,0xf8,0x88,0x74,0x35,0x6,0xee,0xe6,0x1c,0x77,0x19,0xe8,0x46,0xc4,0x8e,0x29,0x83,0xb7,0x36,0x1e,0x7,0x95,0x71,0x4d,0xcb,0xfb,0xca,0xd3,0xe9,0xa3,0xd6,0xe0,0x25,0xf1,0x8,0x57,0x3d,0x92,0x9d,0xe3,0xd4,0xb8,0xbe,0x99,0x52,0x3e,0xc1,0x8b,0x23,0x42,0x7f,0x24,0xf6,0xec,0xba,0x66,0x3f,0x3c,0xe2,0x5,0xb0,0x80,0xd2,0xa4,0x59,0xab,0x7a,0xa7,0xa,0x53,0x6c,0x3a,0x85,0x75,0x37,0x2f,0x4,0x2e,0x82,0x27,0xdb,0xc5,0xbb,0x48,0xcc,0xf4,0x44,0x9b,0x5c,0xdc,0x43,0xd,0xde,0x40,0x89,0x2a,0xf2,0x6f,0x2b,0x9e,0xe4,0x2c,0x39,0x2,0xa2,0x79,0x72,0xdd,0xc2,0x73,0xc8,0x9c,0xc,0x81,0x5b,0x65,0xbc,0xc9,0x58,0x4b,0x22,0xb4,0x11,0x78,0x31,0x62,0xfe,0xb6,0x33,0x4c,0xb9,0xc3,0xac,0x56,0xf9,0x4f,0x98,0x17,0x90,0x6e,0x18,0x47,0xe5,0xf3,0x61,0x5a,0x8c,0x4a,0x51,0x0,0xe1,0x5d,0xc6,0x4e,0x84,0x41,0xa1,0x1a,0x32,0xa5,0xd1,0x8f,0xaf,0x20,0xfc,0xf5,0x10,0xd9,0x14,0xd5,0x93,0x7e,0xae,0xfa,0x63,0x9a,0xfd,0x45,0xb1,0x9f,0xff,0xb5,0xef,0x7d,0x15,0xd7,0x3b,0x76,0x5f,0x86,0x50,0x38,0x7b,0xbd,0xb3,0xbf,0x34,0x67,0x3,0x6d,0xb2,0xaa,0x94,0x55,0x49,0xe,0xf0,0xcf,0x97,0xdf,0xa6,0x8a,0x1d,0x69,0xea,0xc7,0x28,0x21,0x26,0x1f,0x12,0x70,0xcd,0xa0,0x96,0x60,0x91,0x6b,0x6a,0x30,0x2d,0xda,0x87,0xd8,0xf7,0x1b,0x9,0xa8,0xe7,0x54,0x5e,0xa9,0x7c,0xb,0xf,0x64,0x13,0x16,0x1,0x8d,0xeb,0xc0,0xed,0x2,0xc5,0x1a,0xaa,0x80,0x53,0x1d,0x82,0x85,0x79,0xdc,0x70,0x92,0x16,0xe5,0x9b,0xdb,0x64,0x32,0xd,0x5a,0x71,0x69,0x2b,0x7,0xfa,0x8c,0xde,0x54,0xf9,0x24,0xf5,0x7c,0x15,0x6,0x97,0x6f,0x26,0x4f,0xea,0x52,0xc2,0x96,0x2d,0xe2,0x3b,0x5,0xdf,0xfc,0x5c,0x67,0x72,0x9c,0x83,0x2c,0x27,0xac,0x74,0xd7,0x1e,0xba,0xc0,0x75,0x31,0x95,0x13,0x2f,0xcb,0xb7,0x8d,0x94,0xa5,0xdd,0x77,0xd0,0x9a,0x59,0x40,0x68,0xe9,0x42,0xb8,0xb0,0x58,0x18,0xb6,0x47,0x29,0xf3,0x8e,0x36,0x90,0x6b,0x2a,0xd6,0xa6,0x61,0x38,0xe4,0xb2,0xee,0x5b,0xbc,0x62,0x7d,0xd5,0x9f,0x60,0xa8,0x7a,0x21,0x1c,0x8a,0xbd,0xc3,0xcc,0xc,0xc7,0xe0,0xe6,0x7b,0xbe,0x88,0xfd,0x63,0x9,0x56,0xaf,0x76,0x99,0xb4,0x37,0x4c,0x41,0x78,0x7f,0xc9,0x91,0xae,0x50,0x43,0xd4,0xf8,0x81,0xec,0x33,0x5d,0x39,0x17,0xb,0xca,0xf4,0x25,0x66,0xe,0xd8,0x6a,0xe1,0xed,0xe3,0x5f,0x48,0x4d,0x3a,0xb3,0x9e,0xb5,0xd3,0x0,0xa,0xb9,0xf6,0x51,0x55,0x22,0xf7,0xd9,0x84,0x73,0x6e,0x57,0x45,0xa9,0x86,0xc8,0xfe,0x93,0x2e,0x34,0x35,0xcf,0x3e,0x98,0x3,0xbf,0x5e,0xff,0x1f,0xda,0x10,0x3f,0xad,0xbb,0x19,0xf,0x14,0xd2,0x4,0xc6,0x11,0xa7,0x8,0x46,0x30,0xce,0x49,0x6d,0xe8,0xa0,0x3c,0xf2,0x9d,0xe7,0x12,0x4b,0x23,0xb1,0xeb,0x1,0x28,0x65,0x89,0xa3,0xc4,0x3d,0xa4,0xa1,0xc1,0xef,0x1b,0x4a,0x87,0x4e,0xab,0xf0,0x20,0xcd,0x8b,0x8f,0xfb,0x6c,0x44,0xa2,0x7e,0xf1,0xd1,0x3a,0x98,0xbe,0x2c,0x53,0x85,0x8e,0x95,0x3e,0xdf,0x19,0x82,0x5b,0x91,0x7e,0x9e,0x21,0xbd,0xec,0x69,0x66,0x93,0x73,0x1c,0x26,0x89,0x47,0x90,0x4f,0xc8,0xc7,0xb1,0xbc,0x25,0x22,0x45,0x6e,0x9a,0x20,0x40,0x30,0x6a,0xca,0xa2,0xe4,0x8,0x80,0xa9,0xed,0xc5,0xe,0x7a,0x70,0x50,0x23,0xff,0xcf,0x2a,0xcb,0x6,0x4c,0xa,0x71,0xa1,0x2f,0xd1,0x48,0x10,0x79,0x0,0xc2,0x55,0x35,0xb6,0xf7,0x18,0xf9,0xfe,0xcd,0xc0,0x8f,0x59,0xa4,0xe7,0x6c,0x62,0xeb,0x60,0xdc,0xb8,0x6d,0xb2,0x4b,0x75,0x96,0x8a,0x38,0x77,0x81,0x8b,0xa3,0x76,0xd0,0xd4,0xcc,0xbb,0xde,0xc9,0x34,0x52,0x32,0x1f,0x12,0xaf,0x49,0x7f,0x4e,0xbf,0xb5,0xb4,0xf2,0xef,0x58,0x5,0x28,0x7,0xd6,0xc4,0x51,0x1b,0x5c,0xf6,0xe9,0x68,0xd8,0xc1,0xae,0x4a,0x14,0x92,0x15,0x24,0x36,0xc,0xb7,0x11,0x72,0xf,0x57,0x27,0xea,0xab,0x31,0xd9,0xc3,0x39,0xc6,0xa8,0x99,0x37,0x1e,0xe1,0xfc,0x54,0xa0,0x9d,0x29,0xfb,0x65,0x33,0xe0,0xb9,0x3d,0xe3,0x6f,0xda,0x9,0x7c,0xfa,0x3f,0xd7,0x2e,0xe2,0x88,0x42,0x4d,0xb,0x3c,0x61,0x67,0x8d,0x46,0x5d,0xf1,0x4,0xf8,0x64,0x1a,0x13,0x97,0x9b,0x2b,0x83,0x44,0x9c,0x3,0x1,0xd2,0xd,0x5f,0x86,0x7b,0xa5,0x74,0xd5,0x78,0xb3,0x8c,0x5a,0xe5,0xe8,0xaa,0xdb,0xf0,0x17,0xac,0xd3,0x43,0x84,0x5e,0x63,0xba,0x87,0x16,0xfd,0x94,0xce,0x6b,0xee,0xa7,0x56,0x9f,0x2d,0xf5,0xf4,0xb0,0x3b,0x41,0xe6,0xf3,0x7d,0xdd,0xad,0xa6,0x1d,0x2,0x6d,0x91,0x64,0xc8,0x2,0x86,0x8f,0xf1,0xd1,0x16,0xbe,0xe,0x47,0x94,0x96,0x9,0xee,0x13,0xca,0x98,0xed,0x40,0xe1,0x30,0x70,0xcf,0x19,0x26,0x65,0x4e,0x3f,0x7d,0xd6,0x46,0x39,0x82,0x2f,0xf6,0xcb,0x11,0x1,0x68,0x83,0x12,0x32,0x7b,0xfe,0x5b,0x60,0xb8,0xa,0xc3,0xd4,0xae,0x25,0x61,0x48,0xe8,0x66,0x73,0x97,0x88,0x33,0x38,0x63,0xc9,0x8e,0xc4,0x54,0x4d,0xfd,0x7c,0x7,0x81,0xdf,0x3b,0x99,0xa3,0xb1,0x80,0x9a,0xe7,0x84,0x22,0x3e,0x7f,0xb2,0xc2,0xac,0x56,0x4c,0xa4,0xa2,0xc,0x3d,0x53,0xc1,0x69,0x74,0x8b,0x6e,0xbc,0x8,0x35,0x2c,0x75,0xa6,0xf0,0x4f,0xfa,0x76,0xa8,0xaa,0x6f,0xe9,0x9c,0x1d,0x77,0xbb,0x42,0xa9,0x9e,0xd8,0xd7,0xd3,0x18,0xf2,0xf4,0x85,0xdd,0x44,0xba,0xc0,0x57,0x95,0xec,0x8d,0x62,0x23,0xa0,0x55,0x58,0x6b,0x6c,0x72,0x31,0xcc,0x1a,0xf5,0x7e,0xf7,0xf9,0x27,0xf8,0x2d,0x49,0x1f,0x3,0xe0,0xde,0x1e,0x14,0xe2,0xad,0x41,0x45,0xe3,0x36,0x5c,0x4b,0x2e,0x59,0x8a,0xa7,0xc7,0xa1,0xea,0xdc,0x3a,0x87,0x21,0x20,0x2a,0xdb,0x90,0xcd,0x7a,0x67,0x51,0x43,0x92,0xbd,0xb9,0x2b,0xd,0xaf,0x0,0x1b,0x10,0xc6,0x17,0x8c,0x4a,0xab,0xb,0xeb,0x4,0xce,0xfc,0x79,0x28,0xb4,0x89,0xe6,0x6,0xf3,0x5,0xd2,0x1c,0xb3,0x24,0x52,0x5d,0xda,0xd0,0xb7,0xb0,0x29,0xd5,0xb5,0xf,0xfb,0x37,0x5f,0xff,0xa5,0x3c,0x15,0x9d,0x71,0xef,0x9b,0x50,0x78,0x6a,0xb6,0xc5,0xe5,0x93,0x5e,0xbf,0x5a,0x34,0xe4,0x9f,0xd9,0xc8,0x6e,0xd6,0xab,0xfe,0x8e,0x72,0x33,0x0,0xe8,0xe0,0x1a,0x71,0x1f,0xee,0x40,0xc2,0x88,0x2f,0x85,0xb1,0x30,0x18,0x1,0x93,0x77,0x4b,0xcd,0xfd,0xcc,0xd5,0xef,0xa5,0xd0,0xe6,0x23,0xf7,0xe,0x51,0x3b,0x94,0x9b,0xe5,0xd2,0xbe,0xb8,0x9f,0x54,0x38,0xc7,0x8d,0x25,0x44,0x79,0x22,0xf0,0xea,0xbc,0x60,0x39,0x3a,0xe4,0x3,0xb6,0x86,0xd4,0xa2,0x5f,0xad,0x7c,0xa1,0xc,0x55,0x6a,0x3c,0x83,0x73,0x31,0x29,0x2,0x28,0x84,0x21,0xdd,0xc3,0xbd,0x4e,0xca,0xf2,0x42,0x9d,0x5a,0xda,0x45,0xb,0xd8,0x46,0x8f,0x2c,0xf4,0x69,0x2d,0x98,0xe2,0x2a,0x3f,0x4,0xa4,0x7f,0x74,0xdb,0xc4,0x75,0xce,0x9a,0xa,0x87,0x5d,0x63,0xba,0xcf,0x5e,0x4d,0x24,0xb2,0x17,0x7e,0x37,0x64,0xf8,0xb0,0x35,0x4a,0xbf,0xc5,0xaa,0x50,0xff,0x49,0x9e,0x11,0x96,0x68,0x1e,0x41,0xe3,0xf5,0x67,0x5c,0x8a,0x4c,0x57,0x6,0xe7,0x5b,0xc0,0x48,0x82,0x47,0xa7,0x1c,0x34,0xa3,0xd7,0x89,0xa9,0x26,0xfa,0xf3,0x16,0xdf,0x12,0xd3,0x95,0x78,0xa8,0xfc,0x65,0x9c,0xfb,0x43,0xb7,0x99,0xf9,0xb3,0xe9,0x7b,0x13,0xd1,0x3d,0x70,0x59,0x80,0x56,0x3e,0x7d,0xbb,0xb5,0xb9,0x32,0x61,0x5,0x6b,0xb4,0xac,0x92,0x53,0x4f,0x8,0xf6,0xc9,0x91,0xd9,0xa0,0x8c,0x1b,0x6f,0xec,0xc1,0x2e,0x27,0x20,0x19,0x14,0x76,0xcb,0xa6,0x90,0x66,0x97,0x6d,0x6c,0x36,0x2b,0xdc,0x81,0xde,0xf1,0x1d,0xf,0xae,0xe1,0x52,0x58,0xaf,0x7a,0xd,0x9,0x62,0x15,0x10,0x7,0x8b,0xed,0xc6,0xeb,0xf3,0x4d,0xc3,0x1c,0x5d,0xb5,0x2b,0x29,0xa7,0xb6,0xe0,0xea,0xf9,0x6,0x54,0x39,0xf0,0x87,0x4e,0xbe,0x9d,0x6b,0x8f,0xd6,0xbb,0x6d,0xa3,0x3e,0xe6,0xfe,0x8c,0xf1,0x90,0x0,0x67,0x4b,0xdb,0x70,0xaf,0x36,0x79,0xc6,0x5b,0xd5,0x85,0xce,0x25,0x8a,0x7a,0x69,0xc0,0x92,0x1a,0x9f,0x59,0xa1,0x76,0xff,0xa2,0x33,0x2a,0x42,0xd0,0x32,0x75,0xa,0xaa,0x4,0x71,0x4f,0xe5,0x89,0x37,0xf6,0x4a,0x98,0xb,0xde,0x11,0x44,0xca,0x14,0x7,0x34,0x2c,0xc5,0xe4,0xc9,0x62,0x12,0xba,0x19,0xc8,0x84,0x3b,0xb0,0xb2,0xd2,0xb3,0xcf,0xb1,0xb4,0x2,0xd8,0x63,0x15,0xa8,0x60,0xae,0xf,0xee,0xac,0xf4,0xc1,0x9b,0x6e,0xdf,0x72,0xa4,0x57,0xad,0x38,0x86,0x6a,0x95,0xdc,0xe7,0x50,0x9c,0xc7,0xfa,0xcd,0x13,0xe,0x4c,0xb7,0x94,0x5a,0xa9,0x3,0x46,0xb8,0x56,0x96,0xcb,0x2f,0x1,0xbd,0x93,0xeb,0x78,0x97,0x53,0x26,0x2d,0xe1,0x51,0xd3,0x52,0x64,0x7d,0xef,0x83,0xe2,0xdd,0x55,0x88,0x24,0xc4,0x9,0x20,0x10,0x6f,0xd7,0x5c,0x49,0x65,0xfd,0xfc,0xd4,0xa6,0x7e,0x9e,0xd9,0x28,0x81,0xb9,0x58,0x5e,0x99,0x16,0xfb,0xf8,0x6c,0x1f,0xd,0x8b,0x8d,0xd1,0xed,0xbc,0x5,0x7f,0xe8,0xa0,0x21,0x1b,0x68,0xf5,0xf2,0x47,0xe3,0xc2,0xec,0x41,0x7b,0x73,0x82,0x23,0x74,0x3d,0x27,0x77,0x1d,0x9a,0x1e,0x31,0x17,0x8,0x30,0x43,0xab,0x91,0x8e,0x18,0x3f,0xa5,0x80,0x3a,0x7c,0xcc,0x40,0xc,0x48,0xe9,0x35,0x3c,0x22,0x66,0x2e,0xda,0x45,0xbf,0x5f,0x61,0xf7,0xb0,0x12,0x34,0xa6,0xd9,0xf,0x4,0x1f,0xb4,0x55,0x93,0x8,0xd1,0x1b,0xf4,0x14,0xab,0x37,0x66,0xe3,0xec,0x19,0xf9,0x96,0xac,0x3,0xcd,0x1a,0xc5,0x42,0x4d,0x3b,0x36,0xaf,0xa8,0xcf,0xe4,0x10,0xaa,0xca,0xba,0xe0,0x40,0x28,0x6e,0x82,0xa,0x23,0x67,0x4f,0x84,0xf0,0xfa,0xda,0xa9,0x75,0x45,0xa0,0x41,0x8c,0xc6,0x80,0xfb,0x2b,0xa5,0x5b,0xc2,0x9a,0xf3,0x8a,0x48,0xdf,0xbf,0x3c,0x7d,0x92,0x73,0x74,0x47,0x4a,0x5,0xd3,0x2e,0x6d,0xe6,0xe8,0x61,0xea,0x56,0x32,0xe7,0x38,0xc1,0xff,0x1c,0x0,0xb2,0xfd,0xb,0x1,0x29,0xfc,0x5a,0x5e,0x46,0x31,0x54,0x43,0xbe,0xd8,0xb8,0x95,0x98,0x25,0xc3,0xf5,0xc4,0x35,0x3f,0x3e,0x78,0x65,0xd2,0x8f,0xa2,0x8d,0x5c,0x4e,0xdb,0x91,0xd6,0x7c,0x63,0xe2,0x52,0x4b,0x24,0xc0,0x9e,0x18,0x9f,0xae,0xbc,0x86,0x3d,0x9b,0xf8,0x85,0xdd,0xad,0x60,0x21,0xbb,0x53,0x49,0xb3,0x4c,0x22,0x13,0xbd,0x94,0x6b,0x76,0xde,0x2a,0x17,0xa3,0x71,0xef,0xb9,0x6a,0x33,0xb7,0x69,0xe5,0x50,0x83,0xf6,0x70,0xb5,0x5d,0xa4,0x68,0x2,0xc8,0xc7,0x81,0xb6,0xeb,0xed,0x7,0xcc,0xd7,0x7b,0x8e,0x72,0xee,0x90,0x99,0x1d,0x11,0xa1,0x9,0xce,0x16,0x89,0x8b,0x58,0x87,0xd5,0xc,0xf1,0x2f,0xfe,0x5f,0xf2,0x39,0x6,0xd0,0x6f,0x62,0x20,0x51,0x7a,0x9d,0x26,0x59,0xc9,0xe,0xd4,0xe9,0x30,0xd,0x9c,0x77,0x1e,0x44,0xe1,0x64,0x2d,0xdc,0x15,0xa7,0x7f,0x7e,0x3a,0xb1,0xcb,0x6c,0x79,0xf7,0x57,0x27,0x2c,0x97,0x88,0xd,0xf1,0x4,0xa8,0x62,0xe6,0xef,0x91,0xb1,0x76,0xde,0x6e,0x27,0xf4,0xf6,0x69,0x8e,0x73,0xaa,0xf8,0x8d,0x20,0x81,0x50,0x10,0xaf,0x79,0x46,0x5,0x2e,0x5f,0x1d,0xb6,0x26,0x59,0xe2,0x4f,0x96,0xab,0x71,0x61,0x8,0xe3,0x72,0x52,0x1b,0x9e,0x3b,0x0,0xd8,0x6a,0xa3,0xb4,0xce,0x45,0x1,0x28,0x88,0x6,0x13,0xf7,0xe8,0x53,0x58,0x3,0xa9,0xee,0xa4,0x34,0x2d,0x9d,0x1c,0x67,0xe1,0xbf,0x5b,0xf9,0xc3,0xd1,0xe0,0xfa,0x87,0xe4,0x42,0x5e,0x1f,0xd2,0xa2,0xcc,0x36,0x2c,0xc4,0xc2,0x6c,0x5d,0x33,0xa1,0x9,0x14,0xeb,0xe,0xdc,0x68,0x55,0x4c,0x15,0xc6,0x90,0x2f,0x9a,0x16,0xc8,0xca,0xf,0x89,0xfc,0x7d,0x17,0xdb,0x22,0xc9,0xfe,0xb8,0xb7,0xb3,0x78,0x92,0x94,0xe5,0xbd,0x24,0xda,0xa0,0x37,0xf5,0x8c,0xed,0x2,0x43,0xc0,0x35,0x38,0xb,0xc,0x12,0x51,0xac,0x7a,0x95,0x1e,0x97,0x99,0x47,0x98,0x4d,0x29,0x7f,0x63,0x80,0xbe,0x7e,0x74,0x82,0xcd,0x21,0x25,0x83,0x56,0x3c,0x2b,0x4e,0x39,0xea,0xc7,0xa7,0xc1,0x8a,0xbc,0x5a,0xe7,0x41,0x40,0x4a,0xbb,0xf0,0xad,0x1a,0x7,0x31,0x23,0xf2,0xdd,0xd9,0x4b,0x6d,0xcf,0x60,0x7b,0x70,0xa6,0x77,0xec,0x2a,0xcb,0x6b,0x8b,0x64,0xae,0x9c,0x19,0x48,0xd4,0xe9,0x86,0x66,0x93,0x65,0xb2,0x7c,0xd3,0x44,0x32,0x3d,0xba,0xb0,0xd7,0xd0,0x49,0xb5,0xd5,0x6f,0x9b,0x57,0x3f,0x9f,0xc5,0x5c,0x75,0xfd,0x11,0x8f,0xfb,0x30,0x18,0xa,0xd6,0xa5,0x85,0xf3,0x3e,0xdf,0x3a,0x54,0x84,0xff,0xb9,0x7a,0xdb,0x3a,0x78,0xb7,0xc1,0x7c,0xb4,0x65,0x60,0xd6,0xc,0x66,0x6,0x67,0x1b,0x41,0x8,0x33,0x84,0x79,0xec,0x52,0xbe,0xb,0xa6,0x70,0x83,0x20,0x15,0x4f,0xba,0xdf,0xa,0xc5,0x90,0xe3,0x22,0x9e,0x4c,0xa5,0x9b,0x31,0x5d,0xa1,0xde,0x7e,0xd0,0x1c,0x50,0xef,0x64,0xb6,0xc6,0x6e,0xcd,0xf8,0x11,0x30,0x1d,0x1e,0xc0,0xd3,0xe0,0x51,0x1a,0xf1,0x5e,0xad,0x12,0x8f,0x1,0xf,0xa4,0x7b,0xe2,0x44,0xd4,0xb3,0x9f,0xfe,0x96,0x4,0xe6,0xa2,0x2b,0x76,0xe7,0xce,0x4b,0x8d,0x75,0xae,0xbd,0x14,0x46,0x2d,0xd2,0x80,0xed,0x73,0x62,0x34,0x3e,0x89,0x61,0xff,0xfd,0x27,0x99,0x17,0xc8,0x32,0x2a,0x58,0x25,0x6f,0xb9,0x77,0xea,0x49,0xbf,0x5b,0x2,0x24,0x53,0x9a,0x6a,0x71,0x54,0xee,0xa8,0x45,0x5a,0xcc,0xeb,0xdc,0xe4,0x97,0x7f,0x4e,0xca,0xe5,0xc3,0x6b,0x8b,0xb5,0x23,0xb2,0xfa,0xe,0x91,0x3d,0xe1,0xe8,0xf6,0x18,0x94,0xd8,0x9c,0x74,0xf5,0xcf,0xbc,0x68,0xd1,0xab,0x3c,0x5f,0x59,0x5,0x39,0x2c,0xb8,0xcb,0xd9,0xe9,0xf3,0xa3,0xc9,0xa7,0x56,0xf7,0xa0,0x16,0x38,0x95,0xaf,0x21,0x26,0x93,0x37,0xbb,0x3,0x88,0x9d,0x10,0xdd,0xf4,0xc4,0x9,0x81,0x5c,0xf0,0xa9,0x3b,0x57,0x36,0x8a,0x4d,0xc2,0x2f,0xfc,0x55,0x6d,0x8c,0x72,0xaa,0x4a,0xd,0xb1,0x29,0x28,0x0,0x92,0x6c,0x82,0x42,0x40,0x8e,0x7d,0xd7,0xc7,0xda,0x98,0x63,0x48,0x13,0x2e,0x19,0x85,0x7,0x86,0xb0,0x87,0xf2,0xf9,0x35,0x47,0x3f,0xac,0x43,0x1f,0xfb,0xd5,0x69,0x53,0x94,0x4b,0xfb,0xd1,0x2,0x4c,0xd3,0xd4,0x28,0x8d,0x21,0xc3,0x47,0xb4,0xca,0x8a,0x35,0x63,0x5c,0xb,0x20,0x38,0x7a,0x56,0xab,0xdd,0x8f,0x5,0xa8,0x75,0xa4,0x2d,0x44,0x57,0xc6,0x3e,0x77,0x1e,0xbb,0x3,0x93,0xc7,0x7c,0xb3,0x6a,0x54,0x8e,0xad,0xd,0x36,0x23,0xcd,0xd2,0x7d,0x76,0xfd,0x25,0x86,0x4f,0xeb,0x91,0x24,0x60,0xc4,0x42,0x7e,0x9a,0xe6,0xdc,0xc5,0xf4,0x8c,0x26,0x81,0xcb,0x8,0x11,0x39,0xb8,0x13,0xe9,0xe1,0x9,0x49,0xe7,0x16,0x78,0xa2,0xdf,0x67,0xc1,0x3a,0x7b,0x87,0xf7,0x30,0x69,0xb5,0xe3,0xbf,0xa,0xed,0x33,0x2c,0x84,0xce,0x31,0xf9,0x2b,0x70,0x4d,0xdb,0xec,0x92,0x9d,0x5d,0x96,0xb1,0xb7,0x2a,0xef,0xd9,0xac,0x32,0x58,0x7,0xfe,0x27,0xc8,0xe5,0x66,0x1d,0x10,0x29,0x2e,0x98,0xc0,0xff,0x1,0x12,0x85,0xa9,0xd0,0xbd,0x62,0xc,0x68,0x46,0x5a,0x9b,0xa5,0x74,0x37,0x5f,0x89,0x3b,0xb0,0xbc,0xb2,0xe,0x19,0x1c,0x6b,0xe2,0xcf,0xe4,0x82,0x51,0x5b,0xe8,0xa7,0x0,0x4,0x73,0xa6,0x88,0xd5,0x22,0x3f,0x6,0x14,0xf8,0xd7,0x99,0xaf,0xc2,0x7f,0x65,0x64,0x9e,0x6f,0xc9,0x52,0xee,0xf,0xae,0x4e,0x8b,0x41,0x6e,0xfc,0xea,0x48,0x5e,0x45,0x83,0x55,0x97,0x40,0xf6,0x59,0x17,0x61,0x9f,0x18,0x3c,0xb9,0xf1,0x6d,0xa3,0xcc,0xb6,0x43,0x1a,0x72,0xe0,0xba,0x50,0x79,0x34,0xd8,0xf2,0x95,0x6c,0xf5,0xf0,0x90,0xbe,0x4a,0x1b,0xd6,0x1f,0xfa,0xa1,0x71,0x9c,0xda,0xde,0xaa,0x3d,0x15,0xf3,0x2f,0xa0,0x80,0x63,0xc1,0xe7,0x75,0xa,0xdc,0xd7,0xcc,0x67,0x86,0x40,0xdb,0x2,0xc8,0x27,0xc7,0x78,0xe4,0xb5,0x30,0x3f,0xca,0x2a,0x45,0x7f,0xd0,0x1e,0xc9,0x16,0x91,0x9e,0xe8,0xe5,0x7c,0x7b,0x1c,0x37,0xc3,0x79,0x19,0x69,0x33,0x93,0xfb,0xbd,0x51,0xd9,0xf0,0xb4,0x9c,0x57,0x23,0x29,0x9,0x7a,0xa6,0x96,0x73,0x92,0x5f,0x15,0x53,0x28,0xf8,0x76,0x88,0x11,0x49,0x20,0x59,0x9b,0xc,0x6c,0xef,0xae,0x41,0xa0,0xa7,0x94,0x99,0xd6,0x0,0xfd,0xbe,0x35,0x3b,0xb2,0x39,0x85,0xe1,0x34,0xeb,0x12,0x2c,0xcf,0xd3,0x61,0x2e,0xd8,0xd2,0xfa,0x2f,0x89,0x8d,0x95,0xe2,0x87,0x90,0x6d,0xb,0x6b,0x46,0x4b,0xf6,0x10,0x26,0x17,0xe6,0xec,0xed,0xab,0xb6,0x1,0x5c,0x71,0x5e,0x8f,0x9d,0x8,0x42,0x5,0xaf,0xb0,0x31,0x81,0x98,0xf7,0x13,0x4d,0xcb,0x4c,0x7d,0x6f,0x55,0xee,0x48,0x2b,0x56,0xe,0x7e,0xb3,0xf2,0x68,0x80,0x9a,0x60,0x9f,0xf1,0xc0,0x6e,0x47,0xb8,0xa5,0xd,0xf9,0xc4,0x70,0xa2,0x3c,0x6a,0xb9,0xe0,0x64,0xba,0x36,0x83,0x50,0x25,0xa3,0x66,0x8e,0x77,0xbb,0xd1,0x1b,0x14,0x52,0x65,0x38,0x3e,0xd4,0x1f,0x4,0xa8,0x5d,0xa1,0x3d,0x43,0x4a,0xce,0xc2,0x72,0xda,0x1d,0xc5,0x5a,0x58,0x8b,0x54,0x6,0xdf,0x22,0xfc,0x2d,0x8c,0x21,0xea,0xd5,0x3,0xbc,0xb1,0xf3,0x82,0xa9,0x4e,0xf5,0x8a,0x1a,0xdd,0x7,0x3a,0xe3,0xde,0x4f,0xa4,0xcd,0x97,0x32,0xb7,0xfe,0xf,0xc6,0x74,0xac,0xad,0xe9,0x62,0x18,0xbf,0xaa,0x24,0x84,0xf4,0xff,0x44,0x5b,0x9c,0xb9,0x60,0x26,0x92,0x8d,0x23,0x4,0x2c,0x14,0xb7,0x5f,0x2,0x86,0xb,0x2d,0x43,0xa3,0xeb,0x7d,0x32,0x7a,0x59,0xc6,0x29,0xf5,0x3e,0x20,0x5c,0xd0,0x54,0x10,0x3d,0xbc,0x74,0x7,0x19,0xa0,0xf4,0x63,0x91,0x97,0xf1,0xcd,0x70,0xe4,0x11,0x3,0x3b,0x21,0x1,0x6b,0x9e,0x6f,0x68,0x3f,0xf0,0xde,0x67,0x5d,0xee,0xe9,0xff,0x5b,0xcb,0x73,0x55,0x40,0x15,0xd8,0xc,0x3c,0x49,0xc1,0x38,0x94,0xf3,0x61,0xfe,0x9f,0x85,0x42,0xe7,0xa,0x9d,0x34,0x44,0xa5,0x62,0xba,0xc5,0x82,0xe1,0x79,0xc8,0xe0,0xa4,0x5a,0x8a,0x4a,0x46,0x88,0x1f,0xb5,0x12,0xf,0xab,0x50,0xdb,0x80,0xd1,0xe6,0xcf,0x4d,0x78,0x4e,0x3a,0x4f,0xfd,0x31,0xf7,0x8f,0x8b,0x64,0x33,0xd7,0xa1,0x1d,0x13,0xb2,0xb0,0xf2,0x9,0x7f,0x7c,0xb4,0xa8,0xad,0xc4,0x1e,0xce,0xae,0xd3,0xaf,0xc0,0x89,0x4c,0xfb,0x24,0xb1,0x76,0x9a,0x6e,0xc3,0x4b,0xb8,0xdd,0xe8,0x72,0x87,0xc2,0x17,0x58,0xd,0xea,0x2b,0x84,0x56,0x53,0x6d,0x95,0xf9,0x16,0x69,0x18,0xb6,0x98,0xd4,0xac,0x27,0xe,0x7e,0x5,0xa6,0xd9,0x30,0xd5,0xf8,0x8,0xd6,0x28,0x1b,0xd2,0x99,0x96,0x39,0xda,0x65,0xc9,0x47,0x6c,0xc7,0x2a,0xb3,0x1c,0x8c,0x57,0x7b,0x5e,0x36,0x2e,0xcc,0xe3,0x6a,0x2f,0xbe,0x83,0x6,0xbd,0x45,0x75,0x66,0x8e,0xdc,0x1a,0xe5,0x25,0x48,0xaa,0xbb,0xf6,0xfc,0xa9,0x41,0x35,0x37,0x51,0xef,0x0,0xdf,0xe2,0xfa,0xed,0x90,0x71,0xa7,0x22,0xbf,0x77,0x81,0xca,0x93,0x9b,0xec,0xa2,0x52,0xa1,0x7,0xbf,0xc2,0x97,0xe7,0x1b,0x5a,0x69,0x81,0x89,0x73,0x18,0x76,0x87,0x29,0xab,0xe1,0x46,0xec,0xd8,0x59,0x71,0x68,0xfa,0x1e,0x22,0xa4,0x94,0xa5,0xbc,0x86,0xcc,0xb9,0x8f,0x4a,0x9e,0x67,0x38,0x52,0xfd,0xf2,0x8c,0xbb,0xd7,0xd1,0xf6,0x3d,0x51,0xae,0xe4,0x4c,0x2d,0x10,0x4b,0x99,0x83,0xd5,0x9,0x50,0x53,0x8d,0x6a,0xdf,0xef,0xbd,0xcb,0x36,0xc4,0x15,0xc8,0x65,0x3c,0x3,0x55,0xea,0x1a,0x58,0x40,0x6b,0x41,0xed,0x48,0xb4,0xaa,0xd4,0x27,0xa3,0x9b,0x2b,0xf4,0x33,0xb3,0x2c,0x62,0xb1,0x2f,0xe6,0x45,0x9d,0x0,0x44,0xf1,0x8b,0x43,0x56,0x6d,0xcd,0x16,0x1d,0xb2,0xad,0x1c,0xa7,0xf3,0x63,0xee,0x34,0xa,0xd3,0xa6,0x37,0x24,0x4d,0xdb,0x7e,0x17,0x5e,0xd,0x91,0xd9,0x5c,0x23,0xd6,0xac,0xc3,0x39,0x96,0x20,0xf7,0x78,0xff,0x1,0x77,0x28,0x8a,0x9c,0xe,0x35,0xe3,0x25,0x3e,0x6f,0x8e,0x32,0xa9,0x21,0xeb,0x2e,0xce,0x75,0x5d,0xca,0xbe,0xe0,0xc0,0x4f,0x93,0x9a,0x7f,0xb6,0x7b,0xba,0xfc,0x11,0xc1,0x95,0xc,0xf5,0x92,0x2a,0xde,0xf0,0x90,0xda,0x80,0x12,0x7a,0xb8,0x54,0x19,0x30,0xe9,0x3f,0x57,0x14,0xd2,0xdc,0xd0,0x5b,0x8,0x6c,0x2,0xdd,0xc5,0xfb,0x3a,0x26,0x61,0x9f,0xa0,0xf8,0xb0,0xc9,0xe5,0x72,0x6,0x85,0xa8,0x47,0x4e,0x49,0x70,0x7d,0x1f,0xa2,0xcf,0xf9,0xf,0xfe,0x4,0x5,0x5f,0x42,0xb5,0xe8,0xb7,0x98,0x74,0x66,0xc7,0x88,0x3b,0x31,0xc6,0x13,0x64,0x60,0xb,0x7c,0x79,0x6e,0xe2,0x84,0xaf,0x82,0x28,0xef,0x30,0x80,0xaa,0x79,0x37,0xa8,0xaf,0x53,0xf6,0x5a,0xb8,0x3c,0xcf,0xb1,0xf1,0x4e,0x18,0x27,0x70,0x5b,0x43,0x1,0x2d,0xd0,0xa6,0xf4,0x7e,0xd3,0xe,0xdf,0x56,0x3f,0x2c,0xbd,0x45,0xc,0x65,0xc0,0x78,0xe8,0xbc,0x7,0xc8,0x11,0x2f,0xf5,0xd6,0x76,0x4d,0x58,0xb6,0xa9,0x6,0xd,0x86,0x5e,0xfd,0x34,0x90,0xea,0x5f,0x1b,0xbf,0x39,0x5,0xe1,0x9d,0xa7,0xbe,0x8f,0xf7,0x5d,0xfa,0xb0,0x73,0x6a,0x42,0xc3,0x68,0x92,0x9a,0x72,0x32,0x9c,0x6d,0x3,0xd9,0xa4,0x1c,0xba,0x41,0x0,0xfc,0x8c,0x4b,0x12,0xce,0x98,0xc4,0x71,0x96,0x48,0x57,0xff,0xb5,0x4a,0x82,0x50,0xb,0x36,0xa0,0x97,0xe9,0xe6,0x26,0xed,0xca,0xcc,0x51,0x94,0xa2,0xd7,0x49,0x23,0x7c,0x85,0x5c,0xb3,0x9e,0x1d,0x66,0x6b,0x52,0x55,0xe3,0xbb,0x84,0x7a,0x69,0xfe,0xd2,0xab,0xc6,0x19,0x77,0x13,0x3d,0x21,0xe0,0xde,0xf,0x4c,0x24,0xf2,0x40,0xcb,0xc7,0xc9,0x75,0x62,0x67,0x10,0x99,0xb4,0x9f,0xf9,0x2a,0x20,0x93,0xdc,0x7b,0x7f,0x8,0xdd,0xf3,0xae,0x59,0x44,0x7d,0x6f,0x83,0xac,0xe2,0xd4,0xb9,0x4,0x1e,0x1f,0xe5,0x14,0xb2,0x29,0x95,0x74,0xd5,0x35,0xf0,0x3a,0x15,0x87,0x91,0x33,0x25,0x3e,0xf8,0x2e,0xec,0x3b,0x8d,0x22,0x6c,0x1a,0xe4,0x63,0x47,0xc2,0x8a,0x16,0xd8,0xb7,0xcd,0x38,0x61,0x9,0x9b,0xc1,0x2b,0x2,0x4f,0xa3,0x89,0xee,0x17,0x8e,0x8b,0xeb,0xc5,0x31,0x60,0xad,0x64,0x81,0xda,0xa,0xe7,0xa1,0xa5,0xd1,0x46,0x6e,0x88,0x54,0xdb,0xfb,0xc8,0x1a,0xb5,0x74,0x93,0xc6,0x89,0x5c,0x28,0x86,0xf7,0x88,0x67,0xb,0xf3,0xcd,0x38,0x9b,0xe0,0x90,0xb9,0x32,0x4a,0x6,0x85,0xb6,0x48,0x96,0x66,0x4b,0xae,0x47,0x2a,0xe2,0xe1,0x97,0x6c,0x2e,0x2c,0x8d,0x31,0x4d,0x30,0x50,0x80,0x5a,0x33,0x36,0x4,0xe8,0x2f,0xba,0x65,0xd2,0x17,0x5e,0x19,0xec,0x76,0x43,0x26,0xd5,0x5d,0xf0,0x62,0x68,0x25,0x34,0xd6,0xbb,0x7b,0x84,0x41,0x9e,0x71,0xcf,0xa9,0xab,0xdf,0x37,0x21,0xbc,0x39,0xef,0xe,0x73,0x64,0x7c,0xcc,0x3c,0x72,0x5,0xd,0x54,0x1f,0xe9,0xd9,0x57,0xfb,0x44,0xa7,0x8,0x7,0x4c,0xe5,0xc9,0x12,0x82,0x2d,0xb4,0x59,0xf2,0x20,0xb1,0xf4,0x7d,0x52,0xb0,0xa8,0xc0,0x42,0x10,0xf8,0xeb,0xdb,0x23,0x98,0x1d,0xfd,0x6a,0x3e,0x87,0x99,0xea,0x22,0xa3,0x9d,0x8f,0x7a,0xee,0x53,0x6f,0x9,0xf,0xa1,0xf6,0xf1,0x0,0xf5,0x9f,0xbf,0xa5,0xc5,0x61,0x77,0x70,0xc3,0xf9,0x40,0x6e,0x9a,0xbd,0x13,0xc,0xb8,0xfe,0x27,0x2,0xb3,0x95,0x18,0x9c,0xc1,0x29,0x8a,0xb2,0x58,0xc7,0xe4,0xac,0xe3,0x75,0x3d,0xdd,0x8e,0xca,0x4e,0xc2,0xbe,0xa0,0x6b,0xb7,0x2b,0x81,0x16,0xd8,0xd4,0x14,0xc4,0x3a,0x78,0x4f,0x1e,0x45,0xce,0x35,0x91,0x8c,0xaf,0x63,0xd1,0xa4,0xd0,0xe6,0xd3,0x51,0x83,0x3f,0x49,0xad,0xfa,0x15,0x11,0x69,0xa2,0x92,0x46,0x8b,0xde,0xcb,0xed,0x55,0x1,0x60,0xff,0x6d,0xa,0xa6,0x5f,0xd7,0x3b,0xda,0xaa,0x3,0x94,0x79,0xdc,0x1b,0x7e,0x56,0xe7,0x7f,0x1c,0x5b,0x24,0xfc,0xf0,0xc,0xf9,0x55,0x9f,0x1b,0x12,0x6c,0x4c,0x8b,0x23,0x93,0xda,0x9,0xb,0x94,0x73,0x8e,0x57,0x5,0x70,0xdd,0x7c,0xad,0xed,0x52,0x84,0xbb,0xf8,0xd3,0xa2,0xe0,0x4b,0xdb,0xa4,0x1f,0xb2,0x6b,0x56,0x8c,0x9c,0xf5,0x1e,0x8f,0xaf,0xe6,0x63,0xc6,0xfd,0x25,0x97,0x5e,0x49,0x33,0xb8,0xfc,0xd5,0x75,0xfb,0xee,0xa,0x15,0xae,0xa5,0xfe,0x54,0x13,0x59,0xc9,0xd0,0x60,0xe1,0x9a,0x1c,0x42,0xa6,0x4,0x3e,0x2c,0x1d,0x7,0x7a,0x19,0xbf,0xa3,0xe2,0x2f,0x5f,0x31,0xcb,0xd1,0x39,0x3f,0x91,0xa0,0xce,0x5c,0xf4,0xe9,0x16,0xf3,0x21,0x95,0xa8,0xb1,0xe8,0x3b,0x6d,0xd2,0x67,0xeb,0x35,0x37,0xf2,0x74,0x1,0x80,0xea,0x26,0xdf,0x34,0x3,0x45,0x4a,0x4e,0x85,0x6f,0x69,0x18,0x40,0xd9,0x27,0x5d,0xca,0x8,0x71,0x10,0xff,0xbe,0x3d,0xc8,0xc5,0xf6,0xf1,0xef,0xac,0x51,0x87,0x68,0xe3,0x6a,0x64,0xba,0x65,0xb0,0xd4,0x82,0x9e,0x7d,0x43,0x83,0x89,0x7f,0x30,0xdc,0xd8,0x7e,0xab,0xc1,0xd6,0xb3,0xc4,0x17,0x3a,0x5a,0x3c,0x77,0x41,0xa7,0x1a,0xbc,0xbd,0xb7,0x46,0xd,0x50,0xe7,0xfa,0xcc,0xde,0xf,0x20,0x24,0xb6,0x90,0x32,0x9d,0x86,0x8d,0x5b,0x8a,0x11,0xd7,0x36,0x96,0x76,0x99,0x53,0x61,0xe4,0xb5,0x29,0x14,0x7b,0x9b,0x6e,0x98,0x4f,0x81,0x2e,0xb9,0xcf,0xc0,0x47,0x4d,0x2a,0x2d,0xb4,0x48,0x28,0x92,0x66,0xaa,0xc2,0x62,0x38,0xa1,0x88,0x0,0xec,0x72,0x6,0xcd,0xe5,0xf7,0x2b,0x58,0x78,0xe,0xc3,0x22,0xc7,0xa9,0x79,0x2,0x44,0x8f,0x29,0x91,0xec,0xb9,0xc9,0x35,0x74,0x47,0xaf,0xa7,0x5d,0x36,0x58,0xa9,0x7,0x85,0xcf,0x68,0xc2,0xf6,0x77,0x5f,0x46,0xd4,0x30,0xc,0x8a,0xba,0x8b,0x92,0xa8,0xe2,0x97,0xa1,0x64,0xb0,0x49,0x16,0x7c,0xd3,0xdc,0xa2,0x95,0xf9,0xff,0xd8,0x13,0x7f,0x80,0xca,0x62,0x3,0x3e,0x65,0xb7,0xad,0xfb,0x27,0x7e,0x7d,0xa3,0x44,0xf1,0xc1,0x93,0xe5,0x18,0xea,0x3b,0xe6,0x4b,0x12,0x2d,0x7b,0xc4,0x34,0x76,0x6e,0x45,0x6f,0xc3,0x66,0x9a,0x84,0xfa,0x9,0x8d,0xb5,0x5,0xda,0x1d,0x9d,0x2,0x4c,0x9f,0x1,0xc8,0x6b,0xb3,0x2e,0x6a,0xdf,0xa5,0x6d,0x78,0x43,0xe3,0x38,0x33,0x9c,0x83,0x32,0x89,0xdd,0x4d,0xc0,0x1a,0x24,0xfd,0x88,0x19,0xa,0x63,0xf5,0x50,0x39,0x70,0x23,0xbf,0xf7,0x72,0xd,0xf8,0x82,0xed,0x17,0xb8,0xe,0xd9,0x56,0xd1,0x2f,0x59,0x6,0xa4,0xb2,0x20,0x1b,0xcd,0xb,0x10,0x41,0xa0,0x1c,0x87,0xf,0xc5,0x0,0xe0,0x5b,0x73,0xe4,0x90,0xce,0xee,0x61,0xbd,0xb4,0x51,0x98,0x55,0x94,0xd2,0x3f,0xef,0xbb,0x22,0xdb,0xbc,0x4,0xf0,0xde,0xbe,0xf4,0xae,0x3c,0x54,0x96,0x7a,0x37,0x1e,0xc7,0x11,0x79,0x3a,0xfc,0xf2,0xfe,0x75,0x26,0x42,0x2c,0xf3,0xeb,0xd5,0x14,0x8,0x4f,0xb1,0x8e,0xd6,0x9e,0xe7,0xcb,0x5c,0x28,0xab,0x86,0x69,0x60,0x67,0x5e,0x53,0x31,0x8c,0xe1,0xd7,0x21,0xd0,0x2a,0x2b,0x71,0x6c,0x9b,0xc6,0x99,0xb6,0x5a,0x48,0xe9,0xa6,0x15,0x1f,0xe8,0x3d,0x4a,0x4e,0x25,0x52,0x57,0x40,0xcc,0xaa,0x81,0xac,0xa7,0x25,0x10,0x26,0x52,0x27,0x95,0x59,0x9f,0xe7,0xe3,0xc,0x5b,0xbf,0xc9,0x75,0xcc,0x32,0xe2,0x22,0x2e,0xe0,0x77,0xdd,0x7a,0x67,0xc3,0x38,0xb3,0xe8,0xb9,0x8e,0xed,0x2a,0x8f,0x62,0xf5,0x5c,0x2c,0xcd,0xa,0xd2,0xad,0xea,0x89,0x11,0xa0,0x88,0xa3,0x1b,0x3d,0x28,0x7d,0xb0,0x64,0x54,0x21,0xa9,0x50,0xfc,0x9b,0x9,0x96,0xf7,0x53,0x49,0x69,0x3,0xf6,0x7,0x0,0x57,0x98,0xb6,0xf,0x35,0x86,0x81,0x97,0x33,0x55,0xd4,0x1c,0x6f,0x71,0xc8,0x9c,0xb,0xf9,0xff,0x99,0xa5,0x18,0x8c,0x79,0x6b,0x2b,0xcb,0x83,0x15,0x5a,0x12,0x31,0xae,0x41,0x9d,0x56,0x48,0x34,0xb8,0x3c,0x78,0xf4,0xd1,0x8,0x4e,0xfa,0xe5,0x4b,0x6c,0x44,0x7c,0xdf,0x37,0x6a,0xee,0x63,0x45,0x8a,0x92,0x85,0xf8,0x19,0xcf,0x4a,0xd7,0x1f,0xe9,0xa2,0xfb,0xf3,0x84,0xca,0x3a,0x72,0x8d,0x4d,0x20,0xc2,0xd3,0x9e,0x94,0xc1,0x29,0x5d,0x5f,0x39,0x87,0x68,0xb7,0x36,0x5e,0x46,0xa4,0x8b,0x2,0x47,0xd6,0xeb,0x6e,0xd5,0x2d,0x1d,0xe,0xe6,0xb4,0xba,0xf1,0xfe,0x51,0xb2,0xd,0xa1,0x2f,0x4,0xaf,0x42,0xdb,0x74,0xe4,0x3f,0x13,0xf0,0xbc,0xc4,0x4f,0x66,0x16,0x6d,0xce,0xb1,0x58,0xbd,0x90,0x60,0xbe,0x40,0x73,0xaa,0x7f,0x30,0x65,0x82,0x43,0xec,0x3e,0x3b,0x5,0xfd,0x91,0x7e,0x1,0x70,0xde,0xa8,0xe1,0x24,0x93,0x4c,0xd9,0x1e,0xf2,0x6,0xab,0x23,0xd0,0xb5,0x80,0x1a,0xef,0x7b,0xda,0xd8,0x9a,0x61,0x17,0x14,0xdc,0xc0,0xc5,0xac,0x76,0xa6,0xc6,0xbb,0xc7,0x7e,0xdc,0xfa,0x68,0x17,0xc1,0xca,0xd1,0x7a,0x9b,0x5d,0xc6,0x1f,0xd5,0x3a,0xda,0x65,0xf9,0xa8,0x2d,0x22,0xd7,0x37,0x58,0x62,0xcd,0x3,0xd4,0xb,0x8c,0x83,0xf5,0xf8,0x61,0x66,0x1,0x2a,0xde,0x64,0x4,0x74,0x2e,0x8e,0xe6,0xa0,0x4c,0xc4,0xed,0xa9,0x81,0x4a,0x3e,0x34,0x14,0x67,0xbb,0x8b,0x6e,0x8f,0x42,0x8,0x4e,0x35,0xe5,0x6b,0x95,0xc,0x54,0x3d,0x44,0x86,0x11,0x71,0xf2,0xb3,0x5c,0xbd,0xba,0x89,0x84,0xcb,0x1d,0xe0,0xa3,0x28,0x26,0xaf,0x24,0x98,0xfc,0x29,0xf6,0xf,0x31,0xd2,0xce,0x7c,0x33,0xc5,0xcf,0xe7,0x32,0x94,0x90,0x88,0xff,0x9a,0x8d,0x70,0x16,0x76,0x5b,0x56,0xeb,0xd,0x3b,0xa,0xfb,0xf1,0xf0,0xb6,0xab,0x1c,0x41,0x6c,0x43,0x92,0x80,0x15,0x5f,0x18,0xb2,0xad,0x2c,0x9c,0x85,0xea,0xe,0x50,0xd6,0x51,0x60,0x72,0x48,0xf3,0x55,0x36,0x4b,0x13,0x63,0xae,0xef,0x75,0x9d,0x87,0x7d,0x82,0xec,0xdd,0x73,0x5a,0xa5,0xb8,0x10,0xe4,0xd9,0x6d,0xbf,0x21,0x77,0xa4,0xfd,0x79,0xa7,0x2b,0x9e,0x4d,0x38,0xbe,0x7b,0x93,0x6a,0xa6,0xcc,0x6,0x9,0x4f,0x78,0x25,0x23,0xc9,0x2,0x19,0xb5,0x40,0xbc,0x20,0x5e,0x57,0xd3,0xdf,0x6f,0xc7,0x0,0xd8,0x47,0x45,0x96,0x49,0x1b,0xc2,0x3f,0xe1,0x30,0x91,0x3c,0xf7,0xc8,0x1e,0xa1,0xac,0xee,0x9f,0xb4,0x53,0xe8,0x97,0x7,0xc0,0x1a,0x27,0xfe,0xc3,0x52,0xb9,0xd0,0x8a,0x2f,0xaa,0xe3,0x12,0xdb,0x69,0xb1,0xb0,0xf4,0x7f,0x5,0xa2,0xb7,0x39,0x99,0xe9,0xe2,0x59,0x46,0x54,0xa8,0x5d,0xf1,0x3b,0xbf,0xb6,0xc8,0xe8,0x2f,0x87,0x37,0x7e,0xad,0xaf,0x30,0xd7,0x2a,0xf3,0xa1,0xd4,0x79,0xd8,0x9,0x49,0xf6,0x20,0x1f,0x5c,0x77,0x6,0x44,0xef,0x7f,0x0,0xbb,0x16,0xcf,0xf2,0x28,0x38,0x51,0xba,0x2b,0xb,0x42,0xc7,0x62,0x59,0x81,0x33,0xfa,0xed,0x97,0x1c,0x58,0x71,0xd1,0x5f,0x4a,0xae,0xb1,0xa,0x1,0x5a,0xf0,0xb7,0xfd,0x6d,0x74,0xc4,0x45,0x3e,0xb8,0xe6,0x2,0xa0,0x9a,0x88,0xb9,0xa3,0xde,0xbd,0x1b,0x7,0x46,0x8b,0xfb,0x95,0x6f,0x75,0x9d,0x9b,0x35,0x4,0x6a,0xf8,0x50,0x4d,0xb2,0x57,0x85,0x31,0xc,0x15,0x4c,0x9f,0xc9,0x76,0xc3,0x4f,0x91,0x93,0x56,0xd0,0xa5,0x24,0x4e,0x82,0x7b,0x90,0xa7,0xe1,0xee,0xea,0x21,0xcb,0xcd,0xbc,0xe4,0x7d,0x83,0xf9,0x6e,0xac,0xd5,0xb4,0x5b,0x1a,0x99,0x6c,0x61,0x52,0x55,0x4b,0x8,0xf5,0x23,0xcc,0x47,0xce,0xc0,0x1e,0xc1,0x14,0x70,0x26,0x3a,0xd9,0xe7,0x27,0x2d,0xdb,0x94,0x78,0x7c,0xda,0xf,0x65,0x72,0x17,0x60,0xb3,0x9e,0xfe,0x98,0xd3,0xe5,0x3,0xbe,0x18,0x19,0x13,0xe2,0xa9,0xf4,0x43,0x5e,0x68,0x7a,0xab,0x84,0x80,0x12,0x34,0x96,0x39,0x22,0x29,0xff,0x2e,0xb5,0x73,0x92,0x32,0xd2,0x3d,0xf7,0xc5,0x40,0x11,0x8d,0xb0,0xdf,0x3f,0xca,0x3c,0xeb,0x25,0x8a,0x1d,0x6b,0x64,0xe3,0xe9,0x8e,0x89,0x10,0xec,0x8c,0x36,0xc2,0xe,0x66,0xc6,0x9c,0x5,0x2c,0xa4,0x48,0xd6,0xa2,0x69,0x41,0x53,0x8f,0xfc,0xdc,0xaa,0x67,0x86,0x63,0xd,0xdd,0xa6,0xe0}; + +uint32_t table_s9[] = {0x7cba6f01,0x2c06b734,0x220ec640,0x72b21e75,0x82c46d87,0xd278b5b2,0xdc70c4c6,0x8ccc1cf3,0xa4a8a905,0xf4147130,0xfa1c0044,0xaaa0d871,0x5ad6ab83,0xa6a73b6,0x46202c2,0x54dedaf7,0x2854fa3e,0x78e8220b,0x76e0537f,0x265c8b4a,0xd62af8b8,0x8696208d,0x889e51f9,0xd82289cc,0xf0463c3a,0xa0fae40f,0xaef2957b,0xfe4e4d4e,0xe383ebc,0x5e84e689,0x508c97fd,0x304fc8,0x613fde08,0x3183063d,0x3f8b7749,0x6f37af7c,0x9f41dc8e,0xcffd04bb,0xc1f575cf,0x9149adfa,0xb92d180c,0xe991c039,0xe799b14d,0xb7256978,0x47531a8a,0x17efc2bf,0x19e7b3cb,0x495b6bfe,0x35d14b37,0x656d9302,0x6b65e276,0x3bd93a43,0xcbaf49b1,0x9b139184,0x951be0f0,0xc5a738c5,0xedc38d33,0xbd7f5506,0xb3772472,0xe3cbfc47,0x13bd8fb5,0x43015780,0x4d0926f4,0x1db5fec1,0x8ab92d9,0x58174aec,0x561f3b98,0x6a3e3ad,0xf6d5905f,0xa669486a,0xa861391e,0xf8dde12b,0xd0b954dd,0x80058ce8,0x8e0dfd9c,0xdeb125a9,0x2ec7565b,0x7e7b8e6e,0x7073ff1a,0x20cf272f,0x5c4507e6,0xcf9dfd3,0x2f1aea7,0x524d7692,0xa23b0560,0xf287dd55,0xfc8fac21,0xac337414,0x8457c1e2,0xd4eb19d7,0xdae368a3,0x8a5fb096,0x7a29c364,0x2a951b51,0x249d6a25,0x7421b210,0x152e23d0,0x4592fbe5,0x4b9a8a91,0x1b2652a4,0xeb502156,0xbbecf963,0xb5e48817,0xe5585022,0xcd3ce5d4,0x9d803de1,0x93884c95,0xc33494a0,0x3342e752,0x63fe3f67,0x6df64e13,0x3d4a9626,0x41c0b6ef,0x117c6eda,0x1f741fae,0x4fc8c79b,0xbfbeb469,0xef026c5c,0xe10a1d28,0xb1b6c51d,0x99d270eb,0xc96ea8de,0xc766d9aa,0x97da019f,0x67ac726d,0x3710aa58,0x3918db2c,0x69a40319,0xec5d851c,0xbce15d29,0xb2e92c5d,0xe255f468,0x1223879a,0x429f5faf,0x4c972edb,0x1c2bf6ee,0x344f4318,0x64f39b2d,0x6afbea59,0x3a47326c,0xca31419e,0x9a8d99ab,0x9485e8df,0xc43930ea,0xb8b31023,0xe80fc816,0xe607b962,0xb6bb6157,0x46cd12a5,0x1671ca90,0x1879bbe4,0x48c563d1,0x60a1d627,0x301d0e12,0x3e157f66,0x6ea9a753,0x9edfd4a1,0xce630c94,0xc06b7de0,0x90d7a5d5,0xf1d83415,0xa164ec20,0xaf6c9d54,0xffd04561,0xfa63693,0x5f1aeea6,0x51129fd2,0x1ae47e7,0x29caf211,0x79762a24,0x777e5b50,0x27c28365,0xd7b4f097,0x870828a2,0x890059d6,0xd9bc81e3,0xa536a12a,0xf58a791f,0xfb82086b,0xab3ed05e,0x5b48a3ac,0xbf47b99,0x5fc0aed,0x5540d2d8,0x7d24672e,0x2d98bf1b,0x2390ce6f,0x732c165a,0x835a65a8,0xd3e6bd9d,0xddeecce9,0x8d5214dc,0x984c78c4,0xc8f0a0f1,0xc6f8d185,0x964409b0,0x66327a42,0x368ea277,0x3886d303,0x683a0b36,0x405ebec0,0x10e266f5,0x1eea1781,0x4e56cfb4,0xbe20bc46,0xee9c6473,0xe0941507,0xb028cd32,0xcca2edfb,0x9c1e35ce,0x921644ba,0xc2aa9c8f,0x32dcef7d,0x62603748,0x6c68463c,0x3cd49e09,0x14b02bff,0x440cf3ca,0x4a0482be,0x1ab85a8b,0xeace2979,0xba72f14c,0xb47a8038,0xe4c6580d,0x85c9c9cd,0xd57511f8,0xdb7d608c,0x8bc1b8b9,0x7bb7cb4b,0x2b0b137e,0x2503620a,0x75bfba3f,0x5ddb0fc9,0xd67d7fc,0x36fa688,0x53d37ebd,0xa3a50d4f,0xf319d57a,0xfd11a40e,0xadad7c3b,0xd1275cf2,0x819b84c7,0x8f93f5b3,0xdf2f2d86,0x2f595e74,0x7fe58641,0x71edf735,0x21512f00,0x9359af6,0x598942c3,0x578133b7,0x73deb82,0xf74b9870,0xa7f74045,0xa9ff3131,0xf943e904,0xcdbd827d,0x7165b72d,0x7914c323,0xc5ccf673,0xb3bf0483,0xf6731d3,0x71645dd,0xbbce708d,0xdf7b86a5,0x63a3b3f5,0x6bd2c7fb,0xd70af2ab,0xa179005b,0x1da1350b,0x15d04105,0xa9087455,0x2328bd29,0x9ff08879,0x9781fc77,0x2b59c927,0x5d2a3bd7,0xe1f20e87,0xe9837a89,0x555b4fd9,0x31eeb9f1,0x8d368ca1,0x8547f8af,0x399fcdff,0x4fec3f0f,0xf3340a5f,0xfb457e51,0x479d4b01,0x480c8b60,0xf4d4be30,0xfca5ca3e,0x407dff6e,0x360e0d9e,0x8ad638ce,0x82a74cc0,0x3e7f7990,0x5aca8fb8,0xe612bae8,0xee63cee6,0x52bbfbb6,0x24c80946,0x98103c16,0x90614818,0x2cb97d48,0xa699b434,0x1a418164,0x1230f56a,0xaee8c03a,0xd89b32ca,0x6443079a,0x6c327394,0xd0ea46c4,0xb45fb0ec,0x88785bc,0xf6f1b2,0xbc2ec4e2,0xca5d3612,0x76850342,0x7ef4774c,0xc22c421c,0xdc405a09,0x60986f59,0x68e91b57,0xd4312e07,0xa242dcf7,0x1e9ae9a7,0x16eb9da9,0xaa33a8f9,0xce865ed1,0x725e6b81,0x7a2f1f8f,0xc6f72adf,0xb084d82f,0xc5ced7f,0x42d9971,0xb8f5ac21,0x32d5655d,0x8e0d500d,0x867c2403,0x3aa41153,0x4cd7e3a3,0xf00fd6f3,0xf87ea2fd,0x44a697ad,0x20136185,0x9ccb54d5,0x94ba20db,0x2862158b,0x5e11e77b,0xe2c9d22b,0xeab8a625,0x56609375,0x59f15314,0xe5296644,0xed58124a,0x5180271a,0x27f3d5ea,0x9b2be0ba,0x935a94b4,0x2f82a1e4,0x4b3757cc,0xf7ef629c,0xff9e1692,0x434623c2,0x3535d132,0x89ede462,0x819c906c,0x3d44a53c,0xb7646c40,0xbbc5910,0x3cd2d1e,0xbf15184e,0xc966eabe,0x75bedfee,0x7dcfabe0,0xc1179eb0,0xa5a26898,0x197a5dc8,0x110b29c6,0xadd31c96,0xdba0ee66,0x6778db36,0x6f09af38,0xd3d19a68,0x2a579fed,0x968faabd,0x9efedeb3,0x2226ebe3,0x54551913,0xe88d2c43,0xe0fc584d,0x5c246d1d,0x38919b35,0x8449ae65,0x8c38da6b,0x30e0ef3b,0x46931dcb,0xfa4b289b,0xf23a5c95,0x4ee269c5,0xc4c2a0b9,0x781a95e9,0x706be1e7,0xccb3d4b7,0xbac02647,0x6181317,0xe696719,0xb2b15249,0xd604a461,0x6adc9131,0x62ade53f,0xde75d06f,0xa806229f,0x14de17cf,0x1caf63c1,0xa0775691,0xafe696f0,0x133ea3a0,0x1b4fd7ae,0xa797e2fe,0xd1e4100e,0x6d3c255e,0x654d5150,0xd9956400,0xbd209228,0x1f8a778,0x989d376,0xb551e626,0xc32214d6,0x7ffa2186,0x778b5588,0xcb5360d8,0x4173a9a4,0xfdab9cf4,0xf5dae8fa,0x4902ddaa,0x3f712f5a,0x83a91a0a,0x8bd86e04,0x37005b54,0x53b5ad7c,0xef6d982c,0xe71cec22,0x5bc4d972,0x2db72b82,0x916f1ed2,0x991e6adc,0x25c65f8c,0x3baa4799,0x877272c9,0x8f0306c7,0x33db3397,0x45a8c167,0xf970f437,0xf1018039,0x4dd9b569,0x296c4341,0x95b47611,0x9dc5021f,0x211d374f,0x576ec5bf,0xebb6f0ef,0xe3c784e1,0x5f1fb1b1,0xd53f78cd,0x69e74d9d,0x61963993,0xdd4e0cc3,0xab3dfe33,0x17e5cb63,0x1f94bf6d,0xa34c8a3d,0xc7f97c15,0x7b214945,0x73503d4b,0xcf88081b,0xb9fbfaeb,0x523cfbb,0xd52bbb5,0xb18a8ee5,0xbe1b4e84,0x2c37bd4,0xab20fda,0xb66a3a8a,0xc019c87a,0x7cc1fd2a,0x74b08924,0xc868bc74,0xacdd4a5c,0x10057f0c,0x18740b02,0xa4ac3e52,0xd2dfcca2,0x6e07f9f2,0x66768dfc,0xdaaeb8ac,0x508e71d0,0xec564480,0xe427308e,0x58ff05de,0x2e8cf72e,0x9254c27e,0x9a25b670,0x26fd8320,0x42487508,0xfe904058,0xf6e13456,0x4a390106,0x3c4af3f6,0x8092c6a6,0x88e3b2a8,0x343b87f8,0xd78d63dd,0xfb83361,0x7ecc3d69,0xa6f96dd5,0xd50b9da3,0xd3ecd1f,0x7c4ac317,0xa47f93ab,0x1189bbcf,0xc9bceb73,0xb8c8e57b,0x60fdb5c7,0x130f45b1,0xcb3a150d,0xba4e1b05,0x627b4bb9,0x42b23733,0x9a87678f,0xebf36987,0x33c6393b,0x4034c94d,0x980199f1,0xe97597f9,0x3140c745,0x84b6ef21,0x5c83bf9d,0x2df7b195,0xf5c2e129,0x8630115f,0x5e0541e3,0x2f714feb,0xf7441f57,0x66847e58,0xbeb12ee4,0xcfc520ec,0x17f07050,0x64028026,0xbc37d09a,0xcd43de92,0x15768e2e,0xa080a64a,0x78b5f6f6,0x9c1f8fe,0xd1f4a842,0xa2065834,0x7a330888,0xb470680,0xd372563c,0xf3bb2ab6,0x2b8e7a0a,0x5afa7402,0x82cf24be,0xf13dd4c8,0x29088474,0x587c8a7c,0x8049dac0,0x35bff2a4,0xed8aa218,0x9cfeac10,0x44cbfcac,0x37390cda,0xef0c5c66,0x9e78526e,0x464d02d2,0x2a5517cc,0xf2604770,0x83144978,0x5b2119c4,0x28d3e9b2,0xf0e6b90e,0x8192b706,0x59a7e7ba,0xec51cfde,0x34649f62,0x4510916a,0x9d25c1d6,0xeed731a0,0x36e2611c,0x47966f14,0x9fa33fa8,0xbf6a4322,0x675f139e,0x162b1d96,0xce1e4d2a,0xbdecbd5c,0x65d9ede0,0x14ade3e8,0xcc98b354,0x796e9b30,0xa15bcb8c,0xd02fc584,0x81a9538,0x7be8654e,0xa3dd35f2,0xd2a93bfa,0xa9c6b46,0x9b5c0a49,0x43695af5,0x321d54fd,0xea280441,0x99daf437,0x41efa48b,0x309baa83,0xe8aefa3f,0x5d58d25b,0x856d82e7,0xf4198cef,0x2c2cdc53,0x5fde2c25,0x87eb7c99,0xf69f7291,0x2eaa222d,0xe635ea7,0xd6560e1b,0xa7220013,0x7f1750af,0xce5a0d9,0xd4d0f065,0xa5a4fe6d,0x7d91aed1,0xc86786b5,0x1052d609,0x6126d801,0xb91388bd,0xcae178cb,0x12d42877,0x63a0267f,0xbb9576c3,0x3d90f33a,0xe5a5a386,0x94d1ad8e,0x4ce4fd32,0x3f160d44,0xe7235df8,0x965753f0,0x4e62034c,0xfb942b28,0x23a17b94,0x52d5759c,0x8ae02520,0xf912d556,0x212785ea,0x50538be2,0x8866db5e,0xa8afa7d4,0x709af768,0x1eef960,0xd9dba9dc,0xaa2959aa,0x721c0916,0x368071e,0xdb5d57a2,0x6eab7fc6,0xb69e2f7a,0xc7ea2172,0x1fdf71ce,0x6c2d81b8,0xb418d104,0xc56cdf0c,0x1d598fb0,0x8c99eebf,0x54acbe03,0x25d8b00b,0xfdede0b7,0x8e1f10c1,0x562a407d,0x275e4e75,0xff6b1ec9,0x4a9d36ad,0x92a86611,0xe3dc6819,0x3be938a5,0x481bc8d3,0x902e986f,0xe15a9667,0x396fc6db,0x19a6ba51,0xc193eaed,0xb0e7e4e5,0x68d2b459,0x1b20442f,0xc3151493,0xb2611a9b,0x6a544a27,0xdfa26243,0x79732ff,0x76e33cf7,0xaed66c4b,0xdd249c3d,0x511cc81,0x7465c289,0xac509235,0xc048872b,0x187dd797,0x6909d99f,0xb13c8923,0xc2ce7955,0x1afb29e9,0x6b8f27e1,0xb3ba775d,0x64c5f39,0xde790f85,0xaf0d018d,0x77385131,0x4caa147,0xdcfff1fb,0xad8bfff3,0x75beaf4f,0x5577d3c5,0x8d428379,0xfc368d71,0x2403ddcd,0x57f12dbb,0x8fc47d07,0xfeb0730f,0x268523b3,0x93730bd7,0x4b465b6b,0x3a325563,0xe20705df,0x91f5f5a9,0x49c0a515,0x38b4ab1d,0xe081fba1,0x71419aae,0xa974ca12,0xd800c41a,0x3594a6,0x73c764d0,0xabf2346c,0xda863a64,0x2b36ad8,0xb74542bc,0x6f701200,0x1e041c08,0xc6314cb4,0xb5c3bcc2,0x6df6ec7e,0x1c82e276,0xc4b7b2ca,0xe47ece40,0x3c4b9efc,0x4d3f90f4,0x950ac048,0xe6f8303e,0x3ecd6082,0x4fb96e8a,0x978c3e36,0x227a1652,0xfa4f46ee,0x8b3b48e6,0x530e185a,0x20fce82c,0xf8c9b890,0x89bdb698,0x5188e624,0x52ae490,0x307a5848,0x44745039,0x7124ece1,0x83d49a92,0xb684264a,0xc28a2e3b,0xf7da92e3,0x1f2f656,0x34a24a8e,0x40ac42ff,0x75fcfe27,0x870c8854,0xb25c348c,0xc6523cfd,0xf3028025,0x3a7e0a05,0xf2eb6dd,0x7b20beac,0x4e700274,0xbc807407,0x89d0c8df,0xfddec0ae,0xc88e7c76,0x3ea618c3,0xbf6a41b,0x7ff8ac6a,0x4aa810b2,0xb85866c1,0x8d08da19,0xf906d268,0xcc566eb0,0xc376121,0x3967ddf9,0x4d69d588,0x78396950,0x8ac91f23,0xbf99a3fb,0xcb97ab8a,0xfec71752,0x8ef73e7,0x3dbfcf3f,0x49b1c74e,0x7ce17b96,0x8e110de5,0xbb41b13d,0xcf4fb94c,0xfa1f0594,0x33638fb4,0x633336c,0x723d3b1d,0x476d87c5,0xb59df1b6,0x80cd4d6e,0xf4c3451f,0xc193f9c7,0x37bb9d72,0x2eb21aa,0x76e529db,0x43b59503,0xb145e370,0x84155fa8,0xf01b57d9,0xc54beb01,0xdd5ef56d,0xe80e49b5,0x9c0041c4,0xa950fd1c,0x5ba08b6f,0x6ef037b7,0x1afe3fc6,0x2fae831e,0xd986e7ab,0xecd65b73,0x98d85302,0xad88efda,0x5f7899a9,0x6a282571,0x1e262d00,0x2b7691d8,0xe20a1bf8,0xd75aa720,0xa354af51,0x96041389,0x64f465fa,0x51a4d922,0x25aad153,0x10fa6d8b,0xe6d2093e,0xd382b5e6,0xa78cbd97,0x92dc014f,0x602c773c,0x557ccbe4,0x2172c395,0x14227f4d,0xd44370dc,0xe113cc04,0x951dc475,0xa04d78ad,0x52bd0ede,0x67edb206,0x13e3ba77,0x26b306af,0xd09b621a,0xe5cbdec2,0x91c5d6b3,0xa4956a6b,0x56651c18,0x6335a0c0,0x173ba8b1,0x226b1469,0xeb179e49,0xde472291,0xaa492ae0,0x9f199638,0x6de9e04b,0x58b95c93,0x2cb754e2,0x19e7e83a,0xefcf8c8f,0xda9f3057,0xae913826,0x9bc184fe,0x6931f28d,0x5c614e55,0x286f4624,0x1d3ffafc,0x18ba037a,0x2deabfa2,0x59e4b7d3,0x6cb40b0b,0x9e447d78,0xab14c1a0,0xdf1ac9d1,0xea4a7509,0x1c6211bc,0x2932ad64,0x5d3ca515,0x686c19cd,0x9a9c6fbe,0xafccd366,0xdbc2db17,0xee9267cf,0x27eeedef,0x12be5137,0x66b05946,0x53e0e59e,0xa11093ed,0x94402f35,0xe04e2744,0xd51e9b9c,0x2336ff29,0x166643f1,0x62684b80,0x5738f758,0xa5c8812b,0x90983df3,0xe4963582,0xd1c6895a,0x11a786cb,0x24f73a13,0x50f93262,0x65a98eba,0x9759f8c9,0xa2094411,0xd6074c60,0xe357f0b8,0x157f940d,0x202f28d5,0x542120a4,0x61719c7c,0x9381ea0f,0xa6d156d7,0xd2df5ea6,0xe78fe27e,0x2ef3685e,0x1ba3d486,0x6faddcf7,0x5afd602f,0xa80d165c,0x9d5daa84,0xe953a2f5,0xdc031e2d,0x2a2b7a98,0x1f7bc640,0x6b75ce31,0x5e2572e9,0xacd5049a,0x9985b842,0xed8bb033,0xd8db0ceb,0xc0ce1287,0xf59eae5f,0x8190a62e,0xb4c01af6,0x46306c85,0x7360d05d,0x76ed82c,0x323e64f4,0xc4160041,0xf146bc99,0x8548b4e8,0xb0180830,0x42e87e43,0x77b8c29b,0x3b6caea,0x36e67632,0xff9afc12,0xcaca40ca,0xbec448bb,0x8b94f463,0x79648210,0x4c343ec8,0x383a36b9,0xd6a8a61,0xfb42eed4,0xce12520c,0xba1c5a7d,0x8f4ce6a5,0x7dbc90d6,0x48ec2c0e,0x3ce2247f,0x9b298a7,0xc9d39736,0xfc832bee,0x888d239f,0xbddd9f47,0x4f2de934,0x7a7d55ec,0xe735d9d,0x3b23e145,0xcd0b85f0,0xf85b3928,0x8c553159,0xb9058d81,0x4bf5fbf2,0x7ea5472a,0xaab4f5b,0x3ffbf383,0xf68779a3,0xc3d7c57b,0xb7d9cd0a,0x828971d2,0x707907a1,0x4529bb79,0x3127b308,0x4770fd0,0xf25f6b65,0xc70fd7bd,0xb301dfcc,0x86516314,0x74a11567,0x41f1a9bf,0x35ffa1ce,0xaf1d16}; + +unsigned char table_s10[] = {0x54,0x67,0xc5,0x1a,0x53,0x9,0xa6,0xeb,0xac,0x7,0xc2,0xe7,0x6c,0xa7,0x8d,0x7b,0xc1,0x83,0x2a,0x6,0x51,0x21,0xb7,0x36,0x52,0xe6,0x74,0x14,0x99,0x8e,0xf,0x17,0x55,0x4f,0x48,0x35,0xc,0xe3,0x3f,0x2e,0x42,0x97,0x82,0x1f,0x84,0xe,0x4d,0x7f,0xe4,0x81,0xc3,0xbf,0xa1,0xa9,0xaf,0x1c,0x57,0xd4,0x28,0x7e,0xb4,0x18,0x66,0x41,0xcc,0x95,0x45,0x70,0x90,0xe9,0x8f,0xb3,0xe5,0xbd,0x3e,0x8c,0x3c,0xbb,0xa0,0xfc,0x3a,0x9c,0x65,0xca,0x5b,0x4e,0x34,0x4,0x4c,0x2b,0x1d,0x30,0xda,0x3b,0xa3,0x8a,0xf1,0x93,0xe0,0xc6,0x77,0x7a,0x5d,0x92,0x6a,0xf4,0x76,0xcf,0x23,0x2c,0x40,0x4b,0xb6,0xde,0xd,0x39,0x56,0x20,0xe2,0x5e,0x44,0xea,0xfe,0x25,0x9b,0x62,0xb9,0x31,0xf2,0xd9,0xcb,0x61,0xee,0x1e,0xd1,0x80,0xa5,0xc9,0x24,0xd7,0xe8,0x12,0xed,0x3d,0xe1,0x8,0x5c,0x38,0xba,0xd3,0x64,0xb5,0x26,0xd2,0xaa,0x91,0xbe,0x16,0x6b,0x5,0x6d,0xfb,0xdf,0x5f,0x19,0x4a,0x75,0x89,0xd6,0xf8,0x9a,0x22,0x85,0xd8,0x33,0xd0,0x3,0x68,0x87,0x13,0xb1,0xdb,0xc7,0x43,0x1,0x9d,0xf9,0xb8,0xd5,0xa4,0xf6,0xfa,0x46,0x2f,0x6e,0x73,0xc4,0x8b,0x94,0x71,0xb2,0xec,0x27,0x11,0xa,0xc8,0x6f,0xae,0x69,0x96,0xce,0x2,0x10,0x15,0x59,0x7d,0xfd,0xdd,0xb,0xb0,0xef,0xff,0x2d,0xa8,0x7c,0x9e,0x9f,0x98,0xa2,0x88,0xad,0x37,0xab,0xf0,0x50,0xdc,0x0,0x86,0xf7,0x79,0x5a,0x58,0xf5,0x60,0xc0,0x63,0x29,0xf3,0x78,0x32,0x72,0xbc,0x49,0x47,0x1b,0xcd,0xee,0xc3,0xf5,0x92,0x54,0x7d,0xe5,0x4,0x14,0xbb,0x42,0xe4,0xda,0xea,0x90,0x85,0x52,0xe0,0x63,0x3b,0x22,0x7e,0x65,0xe2,0xae,0x9b,0x4b,0x12,0x6d,0x51,0x37,0x4e,0xfb,0x20,0x34,0x9a,0xef,0x67,0xbc,0x45,0xe7,0xd3,0x0,0x68,0x80,0x3c,0xfe,0x88,0x11,0xa8,0x2a,0xb4,0x95,0x9e,0xf2,0xfd,0x18,0x3e,0x4d,0x2f,0x4c,0x83,0xa4,0xa9,0xca,0xaa,0x38,0x8c,0xc9,0xd1,0x50,0x47,0xd8,0xf4,0x5d,0x1f,0xe8,0x69,0xff,0x8f,0x39,0x1c,0xd9,0x72,0xa5,0x53,0x79,0xb2,0xc4,0x1b,0xb9,0x8a,0x35,0x78,0xd7,0x8d,0xa0,0xf6,0xa,0x89,0x9f,0xb8,0xc6,0x6a,0x61,0x1d,0x5f,0x3a,0xc2,0x71,0x77,0x7f,0xc1,0x5c,0x49,0x9c,0xa1,0x93,0xd0,0x5a,0xeb,0x96,0x91,0x8b,0xf0,0xe1,0x3d,0xd2,0x6e,0xd5,0x3,0x23,0x76,0xf3,0x21,0x31,0xdc,0x10,0x48,0xb7,0xa3,0x87,0xcb,0xce,0xcf,0xf9,0x32,0x6c,0x70,0xb1,0x16,0xd4,0xad,0xb0,0xf1,0x98,0xaf,0x4a,0x55,0x1a,0x62,0xac,0xec,0xa6,0x13,0xc5,0x99,0x97,0xbe,0x2b,0x86,0x84,0x2d,0xf7,0xbd,0x1e,0x2,0x8e,0x2e,0x75,0xa7,0x29,0x58,0xde,0x46,0x41,0x40,0xa2,0xe9,0x73,0x56,0x7c,0x4f,0x74,0xc,0xf8,0xdb,0xb5,0xc8,0x60,0xe6,0x82,0xd6,0x3f,0x6b,0xba,0xd,0x64,0x9,0xfa,0x17,0x7b,0xe3,0x33,0xcc,0x36,0xbf,0x15,0x7,0x2c,0x5e,0xf,0xc0,0x30,0x66,0x27,0x43,0xdf,0x24,0x28,0x7a,0xb,0xcd,0x59,0xb6,0xdd,0x9d,0x19,0x5,0x6f,0xfc,0x44,0x26,0x8,0xe,0xed,0x6,0x5b,0x81,0x1,0x25,0xb3,0x57,0xab,0x94,0xc7,0x26,0xcf,0x16,0x72,0xfd,0x94,0x9b,0x4a,0xfc,0x8,0xbf,0x84,0x38,0x90,0x2b,0x45,0xf7,0xdc,0x4f,0xe5,0x30,0xc0,0xae,0xff,0xe7,0x8b,0xf9,0xa,0x3c,0xc6,0x13,0xc3,0x46,0x2d,0x3d,0xa9,0xf5,0x9f,0x6d,0xe9,0xb3,0x2f,0x96,0xd7,0x8a,0xfb,0xd4,0xd8,0xd5,0x43,0x71,0xf1,0x64,0x37,0xa7,0x5b,0xd6,0xf8,0xc,0xb4,0xf6,0xab,0xfe,0x1d,0xb8,0x47,0x2c,0xe0,0x3b,0x3e,0x53,0x77,0xf3,0xd3,0x9e,0x25,0xd1,0xc1,0x86,0x3,0x1,0x68,0x5d,0x40,0xa5,0xea,0x5f,0xba,0xc2,0x9c,0x3f,0x9,0xe6,0x24,0x80,0x41,0x76,0x74,0x4e,0xdb,0x4d,0xee,0xdd,0x7,0x1c,0x56,0x92,0x5c,0x69,0x67,0xe3,0x35,0xb0,0x52,0xb6,0xb1,0xa6,0x8c,0x19,0x83,0xde,0x85,0xf2,0x7e,0xa8,0x2e,0x57,0xd9,0xad,0xef,0x28,0x4,0xf,0x7f,0x18,0x99,0xc8,0x7c,0x3a,0x5a,0xa0,0xb7,0x39,0x21,0x49,0x7a,0x34,0xeb,0x27,0x7d,0xc5,0x88,0x29,0x82,0xc9,0xec,0x89,0x42,0x55,0xa3,0xaf,0xca,0x91,0xed,0x87,0x8f,0x32,0x81,0xfa,0x79,0x50,0x6,0x36,0x9a,0x6f,0x48,0x61,0x7b,0x1b,0x66,0xcd,0x22,0x0,0x11,0xb9,0x6c,0x31,0xac,0x20,0xaa,0x51,0x63,0xb2,0x14,0xe4,0x4b,0x60,0x75,0x2a,0x1a,0x5,0x62,0x1e,0x33,0x15,0xf4,0xa4,0x8d,0xbb,0xe2,0x5e,0x6b,0xc7,0xbe,0x9d,0xa1,0x93,0xcb,0xa2,0x10,0x95,0x12,0xd2,0x8e,0xf0,0x98,0x17,0x23,0xe,0x78,0x70,0xcc,0xc4,0x6a,0xb,0xd0,0x4c,0xb5,0x1f,0x97,0xbd,0xdf,0xe8,0xce,0x54,0x59,0xbc,0x73,0xda,0x44,0xe1,0x58,0x2,0xd,0x65,0x6e,0x3,0xac,0x5c,0xfa,0x52,0x62,0x3d,0x28,0x7b,0x56,0x2a,0x4d,0xc5,0xec,0xbc,0x5d,0x23,0x16,0xaa,0xf3,0xe9,0xd5,0xf6,0x8f,0x58,0xea,0x83,0xdb,0xc6,0x9a,0x5a,0xdd,0x6b,0x5f,0xd0,0xb8,0x84,0x38,0x30,0x46,0x98,0x43,0x22,0x8c,0xdf,0x57,0xfd,0x4,0x86,0xa0,0x97,0xf5,0x3b,0xf4,0x11,0x1c,0x10,0xa9,0xc,0x92,0x26,0x2d,0x45,0x4a,0x4c,0x60,0xa7,0xe5,0xd1,0x50,0x37,0x47,0x12,0x72,0x34,0x80,0x69,0x71,0xff,0xe8,0xa3,0x7c,0x32,0x1,0xc0,0x8d,0x35,0x6f,0xa4,0x81,0xca,0x61,0xeb,0x1d,0xa,0xc1,0xa5,0xd9,0x82,0xe7,0xc9,0x7a,0xc7,0xcf,0x4e,0x18,0x31,0xb2,0x0,0x27,0xd2,0x7e,0x2e,0x53,0x33,0x29,0x59,0x48,0x6a,0x85,0xe4,0x79,0x24,0xf1,0x2b,0x19,0xe2,0x68,0xa8,0x64,0xf,0xf0,0x3f,0x1b,0x76,0x73,0x6d,0xd6,0x9b,0xbb,0x4b,0xce,0x89,0x99,0x8,0x15,0x20,0x49,0xf2,0x17,0xa2,0xed,0x41,0x77,0xd4,0x8a,0x9,0xc8,0x6c,0xae,0x93,0x6,0x3c,0x3e,0x4f,0x95,0xa6,0x5,0x14,0xda,0x1e,0x54,0x7d,0xab,0x2f,0x21,0xf9,0xfe,0x1a,0xf8,0xcb,0x51,0xc4,0xee,0x36,0xba,0xcd,0x96,0x91,0x1f,0x66,0xe0,0x3a,0x5e,0x87,0x6e,0x2,0xd3,0xdc,0xb5,0xcc,0xf7,0x40,0xb4,0xd,0x63,0xd8,0x70,0xad,0x7,0x94,0xbf,0xb7,0xe6,0x88,0x78,0x42,0xb1,0xc3,0xaf,0x8b,0x5b,0x8e,0x74,0xe1,0x75,0x65,0xe,0xa1,0x25,0xd7,0xbd,0x9f,0xde,0x67,0xfb,0x90,0x9c,0xb3,0xc2,0xb9,0x39,0xb,0x9d,0x13,0xef,0x7f,0x2c,0xfc,0x44,0xb0,0x9e,0x55,0xb6,0xe3,0xbe,0xe4,0xd7,0x75,0xaa,0xe3,0xb9,0x16,0x5b,0x1c,0xb7,0x72,0x57,0xdc,0x17,0x3d,0xcb,0x71,0x33,0x9a,0xb6,0xe1,0x91,0x7,0x86,0xe2,0x56,0xc4,0xa4,0x29,0x3e,0xbf,0xa7,0xe5,0xff,0xf8,0x85,0xbc,0x53,0x8f,0x9e,0xf2,0x27,0x32,0xaf,0x34,0xbe,0xfd,0xcf,0x54,0x31,0x73,0xf,0x11,0x19,0x1f,0xac,0xe7,0x64,0x98,0xce,0x4,0xa8,0xd6,0xf1,0x7c,0x25,0xf5,0xc0,0x20,0x59,0x3f,0x3,0x55,0xd,0x8e,0x3c,0x8c,0xb,0x10,0x4c,0x8a,0x2c,0xd5,0x7a,0xeb,0xfe,0x84,0xb4,0xfc,0x9b,0xad,0x80,0x6a,0x8b,0x13,0x3a,0x41,0x23,0x50,0x76,0xc7,0xca,0xed,0x22,0xda,0x44,0xc6,0x7f,0x93,0x9c,0xf0,0xfb,0x6,0x6e,0xbd,0x89,0xe6,0x90,0x52,0xee,0xf4,0x5a,0x4e,0x95,0x2b,0xd2,0x9,0x81,0x42,0x69,0x7b,0xd1,0x5e,0xae,0x61,0x30,0x15,0x79,0x94,0x67,0x58,0xa2,0x5d,0x8d,0x51,0xb8,0xec,0x88,0xa,0x63,0xd4,0x5,0x96,0x62,0x1a,0x21,0xe,0xa6,0xdb,0xb5,0xdd,0x4b,0x6f,0xef,0xa9,0xfa,0xc5,0x39,0x66,0x48,0x2a,0x92,0x35,0x68,0x83,0x60,0xb3,0xd8,0x37,0xa3,0x1,0x6b,0x77,0xf3,0xb1,0x2d,0x49,0x8,0x65,0x14,0x46,0x4a,0xf6,0x9f,0xde,0xc3,0x74,0x3b,0x24,0xc1,0x2,0x5c,0x97,0xa1,0xba,0x78,0xdf,0x1e,0xd9,0x26,0x7e,0xb2,0xa0,0xa5,0xe9,0xcd,0x4d,0x6d,0xbb,0x0,0x5f,0x4f,0x9d,0x18,0xcc,0x2e,0x2f,0x28,0x12,0x38,0x1d,0x87,0x1b,0x40,0xe0,0x6c,0xb0,0x36,0x47,0xc9,0xea,0xe8,0x45,0xd0,0x70,0xd3,0x99,0x43,0xc8,0x82,0xc2,0xc,0xf9,0xf7,0xab,0x7d,0x3d,0x10,0x26,0x41,0x87,0xae,0x36,0xd7,0xc7,0x68,0x91,0x37,0x9,0x39,0x43,0x56,0x81,0x33,0xb0,0xe8,0xf1,0xad,0xb6,0x31,0x7d,0x48,0x98,0xc1,0xbe,0x82,0xe4,0x9d,0x28,0xf3,0xe7,0x49,0x3c,0xb4,0x6f,0x96,0x34,0x0,0xd3,0xbb,0x53,0xef,0x2d,0x5b,0xc2,0x7b,0xf9,0x67,0x46,0x4d,0x21,0x2e,0xcb,0xed,0x9e,0xfc,0x9f,0x50,0x77,0x7a,0x19,0x79,0xeb,0x5f,0x1a,0x2,0x83,0x94,0xb,0x27,0x8e,0xcc,0x3b,0xba,0x2c,0x5c,0xea,0xcf,0xa,0xa1,0x76,0x80,0xaa,0x61,0x17,0xc8,0x6a,0x59,0xe6,0xab,0x4,0x5e,0x73,0x25,0xd9,0x5a,0x4c,0x6b,0x15,0xb9,0xb2,0xce,0x8c,0xe9,0x11,0xa2,0xa4,0xac,0x12,0x8f,0x9a,0x4f,0x72,0x40,0x3,0x89,0x38,0x45,0x42,0x58,0x23,0x32,0xee,0x1,0xbd,0x6,0xd0,0xf0,0xa5,0x20,0xf2,0xe2,0xf,0xc3,0x9b,0x64,0x70,0x54,0x18,0x1d,0x1c,0x2a,0xe1,0xbf,0xa3,0x62,0xc5,0x7,0x7e,0x63,0x22,0x4b,0x7c,0x99,0x86,0xc9,0xb1,0x7f,0x3f,0x75,0xc0,0x16,0x4a,0x44,0x6d,0xf8,0x55,0x57,0xfe,0x24,0x6e,0xcd,0xd1,0x5d,0xfd,0xa6,0x74,0xfa,0x8b,0xd,0x95,0x92,0x93,0x71,0x3a,0xa0,0x85,0xaf,0x9c,0xa7,0xdf,0x2b,0x8,0x66,0x1b,0xb3,0x35,0x51,0x5,0xec,0xb8,0x69,0xde,0xb7,0xda,0x29,0xc4,0xa8,0x30,0xe0,0x1f,0xe5,0x6c,0xc6,0xd4,0xff,0x8d,0xdc,0x13,0xe3,0xb5,0xf4,0x90,0xc,0xf7,0xfb,0xa9,0xd8,0x1e,0x8a,0x65,0xe,0x4e,0xca,0xd6,0xbc,0x2f,0x97,0xf5,0xdb,0xdd,0x3e,0xd5,0x88,0x52,0xd2,0xf6,0x60,0x84,0x78,0x47,0x14,0x2c,0xc5,0x1c,0x78,0xf7,0x9e,0x91,0x40,0xf6,0x2,0xb5,0x8e,0x32,0x9a,0x21,0x4f,0xfd,0xd6,0x45,0xef,0x3a,0xca,0xa4,0xf5,0xed,0x81,0xf3,0x0,0x36,0xcc,0x19,0xc9,0x4c,0x27,0x37,0xa3,0xff,0x95,0x67,0xe3,0xb9,0x25,0x9c,0xdd,0x80,0xf1,0xde,0xd2,0xdf,0x49,0x7b,0xfb,0x6e,0x3d,0xad,0x51,0xdc,0xf2,0x6,0xbe,0xfc,0xa1,0xf4,0x17,0xb2,0x4d,0x26,0xea,0x31,0x34,0x59,0x7d,0xf9,0xd9,0x94,0x2f,0xdb,0xcb,0x8c,0x9,0xb,0x62,0x57,0x4a,0xaf,0xe0,0x55,0xb0,0xc8,0x96,0x35,0x3,0xec,0x2e,0x8a,0x4b,0x7c,0x7e,0x44,0xd1,0x47,0xe4,0xd7,0xd,0x16,0x5c,0x98,0x56,0x63,0x6d,0xe9,0x3f,0xba,0x58,0xbc,0xbb,0xac,0x86,0x13,0x89,0xd4,0x8f,0xf8,0x74,0xa2,0x24,0x5d,0xd3,0xa7,0xe5,0x22,0xe,0x5,0x75,0x12,0x93,0xc2,0x76,0x30,0x50,0xaa,0xbd,0x33,0x2b,0x43,0x70,0x3e,0xe1,0x2d,0x77,0xcf,0x82,0x23,0x88,0xc3,0xe6,0x83,0x48,0x5f,0xa9,0xa5,0xc0,0x9b,0xe7,0x8d,0x85,0x38,0x8b,0xf0,0x73,0x5a,0xc,0x3c,0x90,0x65,0x42,0x6b,0x71,0x11,0x6c,0xc7,0x28,0xa,0x1b,0xb3,0x66,0x3b,0xa6,0x2a,0xa0,0x5b,0x69,0xb8,0x1e,0xee,0x41,0x6a,0x7f,0x20,0x10,0xf,0x68,0x14,0x39,0x1f,0xfe,0xae,0x87,0xb1,0xe8,0x54,0x61,0xcd,0xb4,0x97,0xab,0x99,0xc1,0xa8,0x1a,0x9f,0x18,0xd8,0x84,0xfa,0x92,0x1d,0x29,0x4,0x72,0x7a,0xc6,0xce,0x60,0x1,0xda,0x46,0xbf,0x15,0x9d,0xb7,0xd5,0xe2,0xc4,0x5e,0x53,0xb6,0x79,0xd0,0x4e,0xeb,0x52,0x8,0x7,0x6f,0x64,0x9d,0x32,0xc2,0x64,0xcc,0xfc,0xa3,0xb6,0xe5,0xc8,0xb4,0xd3,0x5b,0x72,0x22,0xc3,0xbd,0x88,0x34,0x6d,0x77,0x4b,0x68,0x11,0xc6,0x74,0x1d,0x45,0x58,0x4,0xc4,0x43,0xf5,0xc1,0x4e,0x26,0x1a,0xa6,0xae,0xd8,0x6,0xdd,0xbc,0x12,0x41,0xc9,0x63,0x9a,0x18,0x3e,0x9,0x6b,0xa5,0x6a,0x8f,0x82,0x8e,0x37,0x92,0xc,0xb8,0xb3,0xdb,0xd4,0xd2,0xfe,0x39,0x7b,0x4f,0xce,0xa9,0xd9,0x8c,0xec,0xaa,0x1e,0xf7,0xef,0x61,0x76,0x3d,0xe2,0xac,0x9f,0x5e,0x13,0xab,0xf1,0x3a,0x1f,0x54,0xff,0x75,0x83,0x94,0x5f,0x3b,0x47,0x1c,0x79,0x57,0xe4,0x59,0x51,0xd0,0x86,0xaf,0x2c,0x9e,0xb9,0x4c,0xe0,0xb0,0xcd,0xad,0xb7,0xc7,0xd6,0xf4,0x1b,0x7a,0xe7,0xba,0x6f,0xb5,0x87,0x7c,0xf6,0x36,0xfa,0x91,0x6e,0xa1,0x85,0xe8,0xed,0xf3,0x48,0x5,0x25,0xd5,0x50,0x17,0x7,0x96,0x8b,0xbe,0xd7,0x6c,0x89,0x3c,0x73,0xdf,0xe9,0x4a,0x14,0x97,0x56,0xf2,0x30,0xd,0x98,0xa2,0xa0,0xd1,0xb,0x38,0x9b,0x8a,0x44,0x80,0xca,0xe3,0x35,0xb1,0xbf,0x67,0x60,0x84,0x66,0x55,0xcf,0x5a,0x70,0xa8,0x24,0x53,0x8,0xf,0x81,0xf8,0x7e,0xa4,0xc0,0x19,0xf0,0x9c,0x4d,0x42,0x2b,0x52,0x69,0xde,0x2a,0x93,0xfd,0x46,0xee,0x33,0x99,0xa,0x21,0x29,0x78,0x16,0xe6,0xdc,0x2f,0x5d,0x31,0x15,0xc5,0x10,0xea,0x7f,0xeb,0xfb,0x90,0x3f,0xbb,0x49,0x23,0x1,0x40,0xf9,0x65,0xe,0x2,0x2d,0x5c,0x27,0xa7,0x95,0x3,0x8d,0x71,0xe1,0xb2,0x62,0xda,0x2e,0x0,0xcb,0x28,0x7d,0x20,0x86,0xb5,0x17,0xc8,0x81,0xdb,0x74,0x39,0x7e,0xd5,0x10,0x35,0xbe,0x75,0x5f,0xa9,0x13,0x51,0xf8,0xd4,0x83,0xf3,0x65,0xe4,0x80,0x34,0xa6,0xc6,0x4b,0x5c,0xdd,0xc5,0x87,0x9d,0x9a,0xe7,0xde,0x31,0xed,0xfc,0x90,0x45,0x50,0xcd,0x56,0xdc,0x9f,0xad,0x36,0x53,0x11,0x6d,0x73,0x7b,0x7d,0xce,0x85,0x6,0xfa,0xac,0x66,0xca,0xb4,0x93,0x1e,0x47,0x97,0xa2,0x42,0x3b,0x5d,0x61,0x37,0x6f,0xec,0x5e,0xee,0x69,0x72,0x2e,0xe8,0x4e,0xb7,0x18,0x89,0x9c,0xe6,0xd6,0x9e,0xf9,0xcf,0xe2,0x8,0xe9,0x71,0x58,0x23,0x41,0x32,0x14,0xa5,0xa8,0x8f,0x40,0xb8,0x26,0xa4,0x1d,0xf1,0xfe,0x92,0x99,0x64,0xc,0xdf,0xeb,0x84,0xf2,0x30,0x8c,0x96,0x38,0x2c,0xf7,0x49,0xb0,0x6b,0xe3,0x20,0xb,0x19,0xb3,0x3c,0xcc,0x3,0x52,0x77,0x1b,0xf6,0x5,0x3a,0xc0,0x3f,0xef,0x33,0xda,0x8e,0xea,0x68,0x1,0xb6,0x67,0xf4,0x0,0x78,0x43,0x6c,0xc4,0xb9,0xd7,0xbf,0x29,0xd,0x8d,0xcb,0x98,0xa7,0x5b,0x4,0x2a,0x48,0xf0,0x57,0xa,0xe1,0x2,0xd1,0xba,0x55,0xc1,0x63,0x9,0x15,0x91,0xd3,0x4f,0x2b,0x6a,0x7,0x76,0x24,0x28,0x94,0xfd,0xbc,0xa1,0x16,0x59,0x46,0xa3,0x60,0x3e,0xf5,0xc3,0xd8,0x1a,0xbd,0x7c,0xbb,0x44,0x1c,0xd0,0xc2,0xc7,0x8b,0xaf,0x2f,0xf,0xd9,0x62,0x3d,0x2d,0xff,0x7a,0xae,0x4c,0x4d,0x4a,0x70,0x5a,0x7f,0xe5,0x79,0x22,0x82,0xe,0xd2,0x54,0x25,0xab,0x88,0x8a,0x27,0xb2,0x12,0xb1,0xfb,0x21,0xaa,0xe0,0xa0,0x6e,0x9b,0x95,0xc9,0x1f,0xcd,0xe0,0xd6,0xb1,0x77,0x5e,0xc6,0x27,0x37,0x98,0x61,0xc7,0xf9,0xc9,0xb3,0xa6,0x71,0xc3,0x40,0x18,0x1,0x5d,0x46,0xc1,0x8d,0xb8,0x68,0x31,0x4e,0x72,0x14,0x6d,0xd8,0x3,0x17,0xb9,0xcc,0x44,0x9f,0x66,0xc4,0xf0,0x23,0x4b,0xa3,0x1f,0xdd,0xab,0x32,0x8b,0x9,0x97,0xb6,0xbd,0xd1,0xde,0x3b,0x1d,0x6e,0xc,0x6f,0xa0,0x87,0x8a,0xe9,0x89,0x1b,0xaf,0xea,0xf2,0x73,0x64,0xfb,0xd7,0x7e,0x3c,0xcb,0x4a,0xdc,0xac,0x1a,0x3f,0xfa,0x51,0x86,0x70,0x5a,0x91,0xe7,0x38,0x9a,0xa9,0x16,0x5b,0xf4,0xae,0x83,0xd5,0x29,0xaa,0xbc,0x9b,0xe5,0x49,0x42,0x3e,0x7c,0x19,0xe1,0x52,0x54,0x5c,0xe2,0x7f,0x6a,0xbf,0x82,0xb0,0xf3,0x79,0xc8,0xb5,0xb2,0xa8,0xd3,0xc2,0x1e,0xf1,0x4d,0xf6,0x20,0x0,0x55,0xd0,0x2,0x12,0xff,0x33,0x6b,0x94,0x80,0xa4,0xe8,0xed,0xec,0xda,0x11,0x4f,0x53,0x92,0x35,0xf7,0x8e,0x93,0xd2,0xbb,0x8c,0x69,0x76,0x39,0x41,0x8f,0xcf,0x85,0x30,0xe6,0xba,0xb4,0x9d,0x8,0xa5,0xa7,0xe,0xd4,0x9e,0x3d,0x21,0xad,0xd,0x56,0x84,0xa,0x7b,0xfd,0x65,0x62,0x63,0x81,0xca,0x50,0x75,0x5f,0x6c,0x57,0x2f,0xdb,0xf8,0x96,0xeb,0x43,0xc5,0xa1,0xf5,0x1c,0x48,0x99,0x2e,0x47,0x2a,0xd9,0x34,0x58,0xc0,0x10,0xef,0x15,0x9c,0x36,0x24,0xf,0x7d,0x2c,0xe3,0x13,0x45,0x4,0x60,0xfc,0x7,0xb,0x59,0x28,0xee,0x7a,0x95,0xfe,0xbe,0x3a,0x26,0x4c,0xdf,0x67,0x5,0x2b,0x2d,0xce,0x25,0x78,0xa2,0x22,0x6,0x90,0x74,0x88,0xb7,0xe4,0x76,0x9f,0x46,0x22,0xad,0xc4,0xcb,0x1a,0xac,0x58,0xef,0xd4,0x68,0xc0,0x7b,0x15,0xa7,0x8c,0x1f,0xb5,0x60,0x90,0xfe,0xaf,0xb7,0xdb,0xa9,0x5a,0x6c,0x96,0x43,0x93,0x16,0x7d,0x6d,0xf9,0xa5,0xcf,0x3d,0xb9,0xe3,0x7f,0xc6,0x87,0xda,0xab,0x84,0x88,0x85,0x13,0x21,0xa1,0x34,0x67,0xf7,0xb,0x86,0xa8,0x5c,0xe4,0xa6,0xfb,0xae,0x4d,0xe8,0x17,0x7c,0xb0,0x6b,0x6e,0x3,0x27,0xa3,0x83,0xce,0x75,0x81,0x91,0xd6,0x53,0x51,0x38,0xd,0x10,0xf5,0xba,0xf,0xea,0x92,0xcc,0x6f,0x59,0xb6,0x74,0xd0,0x11,0x26,0x24,0x1e,0x8b,0x1d,0xbe,0x8d,0x57,0x4c,0x6,0xc2,0xc,0x39,0x37,0xb3,0x65,0xe0,0x2,0xe6,0xe1,0xf6,0xdc,0x49,0xd3,0x8e,0xd5,0xa2,0x2e,0xf8,0x7e,0x7,0x89,0xfd,0xbf,0x78,0x54,0x5f,0x2f,0x48,0xc9,0x98,0x2c,0x6a,0xa,0xf0,0xe7,0x69,0x71,0x19,0x2a,0x64,0xbb,0x77,0x2d,0x95,0xd8,0x79,0xd2,0x99,0xbc,0xd9,0x12,0x5,0xf3,0xff,0x9a,0xc1,0xbd,0xd7,0xdf,0x62,0xd1,0xaa,0x29,0x0,0x56,0x66,0xca,0x3f,0x18,0x31,0x2b,0x4b,0x36,0x9d,0x72,0x50,0x41,0xe9,0x3c,0x61,0xfc,0x70,0xfa,0x1,0x33,0xe2,0x44,0xb4,0x1b,0x30,0x25,0x7a,0x4a,0x55,0x32,0x4e,0x63,0x45,0xa4,0xf4,0xdd,0xeb,0xb2,0xe,0x3b,0x97,0xee,0xcd,0xf1,0xc3,0x9b,0xf2,0x40,0xc5,0x42,0x82,0xde,0xa0,0xc8,0x47,0x73,0x5e,0x28,0x20,0x9c,0x94,0x3a,0x5b,0x80,0x1c,0xe5,0x4f,0xc7,0xed,0x8f,0xb8,0x9e,0x4,0x9,0xec,0x23,0x8a,0x14,0xb1,0x8,0x52,0x5d,0x35,0x3e,0xc4,0x6b,0x9b,0x3d,0x95,0xa5,0xfa,0xef,0xbc,0x91,0xed,0x8a,0x2,0x2b,0x7b,0x9a,0xe4,0xd1,0x6d,0x34,0x2e,0x12,0x31,0x48,0x9f,0x2d,0x44,0x1c,0x1,0x5d,0x9d,0x1a,0xac,0x98,0x17,0x7f,0x43,0xff,0xf7,0x81,0x5f,0x84,0xe5,0x4b,0x18,0x90,0x3a,0xc3,0x41,0x67,0x50,0x32,0xfc,0x33,0xd6,0xdb,0xd7,0x6e,0xcb,0x55,0xe1,0xea,0x82,0x8d,0x8b,0xa7,0x60,0x22,0x16,0x97,0xf0,0x80,0xd5,0xb5,0xf3,0x47,0xae,0xb6,0x38,0x2f,0x64,0xbb,0xf5,0xc6,0x7,0x4a,0xf2,0xa8,0x63,0x46,0xd,0xa6,0x2c,0xda,0xcd,0x6,0x62,0x1e,0x45,0x20,0xe,0xbd,0x0,0x8,0x89,0xdf,0xf6,0x75,0xc7,0xe0,0x15,0xb9,0xe9,0x94,0xf4,0xee,0x9e,0x8f,0xad,0x42,0x23,0xbe,0xe3,0x36,0xec,0xde,0x25,0xaf,0x6f,0xa3,0xc8,0x37,0xf8,0xdc,0xb1,0xb4,0xaa,0x11,0x5c,0x7c,0x8c,0x9,0x4e,0x5e,0xcf,0xd2,0xe7,0x8e,0x35,0xd0,0x65,0x2a,0x86,0xb0,0x13,0x4d,0xce,0xf,0xab,0x69,0x54,0xc1,0xfb,0xf9,0x88,0x52,0x61,0xc2,0xd3,0x1d,0xd9,0x93,0xba,0x6c,0xe8,0xe6,0x3e,0x39,0xdd,0x3f,0xc,0x96,0x3,0x29,0xf1,0x7d,0xa,0x51,0x56,0xd8,0xa1,0x27,0xfd,0x99,0x40,0xa9,0xc5,0x14,0x1b,0x72,0xb,0x30,0x87,0x73,0xca,0xa4,0x1f,0xb7,0x6a,0xc0,0x53,0x78,0x70,0x21,0x4f,0xbf,0x85,0x76,0x4,0x68,0x4c,0x9c,0x49,0xb3,0x26,0xb2,0xa2,0xc9,0x66,0xe2,0x10,0x7a,0x58,0x19,0xa0,0x3c,0x57,0x5b,0x74,0x5,0x7e,0xfe,0xcc,0x5a,0xd4,0x28,0xb8,0xeb,0x3b,0x83,0x77,0x59,0x92,0x71,0x24,0x79,0xf,0x3c,0x9e,0x41,0x8,0x52,0xfd,0xb0,0xf7,0x5c,0x99,0xbc,0x37,0xfc,0xd6,0x20,0x9a,0xd8,0x71,0x5d,0xa,0x7a,0xec,0x6d,0x9,0xbd,0x2f,0x4f,0xc2,0xd5,0x54,0x4c,0xe,0x14,0x13,0x6e,0x57,0xb8,0x64,0x75,0x19,0xcc,0xd9,0x44,0xdf,0x55,0x16,0x24,0xbf,0xda,0x98,0xe4,0xfa,0xf2,0xf4,0x47,0xc,0x8f,0x73,0x25,0xef,0x43,0x3d,0x1a,0x97,0xce,0x1e,0x2b,0xcb,0xb2,0xd4,0xe8,0xbe,0xe6,0x65,0xd7,0x67,0xe0,0xfb,0xa7,0x61,0xc7,0x3e,0x91,0x0,0x15,0x6f,0x5f,0x17,0x70,0x46,0x6b,0x81,0x60,0xf8,0xd1,0xaa,0xc8,0xbb,0x9d,0x2c,0x21,0x6,0xc9,0x31,0xaf,0x2d,0x94,0x78,0x77,0x1b,0x10,0xed,0x85,0x56,0x62,0xd,0x7b,0xb9,0x5,0x1f,0xb1,0xa5,0x7e,0xc0,0x39,0xe2,0x6a,0xa9,0x82,0x90,0x3a,0xb5,0x45,0x8a,0xdb,0xfe,0x92,0x7f,0x8c,0xb3,0x49,0xb6,0x66,0xba,0x53,0x7,0x63,0xe1,0x88,0x3f,0xee,0x7d,0x89,0xf1,0xca,0xe5,0x4d,0x30,0x5e,0x36,0xa0,0x84,0x4,0x42,0x11,0x2e,0xd2,0x8d,0xa3,0xc1,0x79,0xde,0x83,0x68,0x8b,0x58,0x33,0xdc,0x48,0xea,0x80,0x9c,0x18,0x5a,0xc6,0xa2,0xe3,0x8e,0xff,0xad,0xa1,0x1d,0x74,0x35,0x28,0x9f,0xd0,0xcf,0x2a,0xe9,0xb7,0x7c,0x4a,0x51,0x93,0x34,0xf5,0x32,0xcd,0x95,0x59,0x4b,0x4e,0x2,0x26,0xa6,0x86,0x50,0xeb,0xb4,0xa4,0x76,0xf3,0x27,0xc5,0xc4,0xc3,0xf9,0xd3,0xf6,0x6c,0xf0,0xab,0xb,0x87,0x5b,0xdd,0xac,0x22,0x1,0x3,0xae,0x3b,0x9b,0x38,0x72,0xa8,0x23,0x69,0x29,0xe7,0x12,0x1c,0x40,0x96,0xae,0x83,0xb5,0xd2,0x14,0x3d,0xa5,0x44,0x54,0xfb,0x2,0xa4,0x9a,0xaa,0xd0,0xc5,0x12,0xa0,0x23,0x7b,0x62,0x3e,0x25,0xa2,0xee,0xdb,0xb,0x52,0x2d,0x11,0x77,0xe,0xbb,0x60,0x74,0xda,0xaf,0x27,0xfc,0x5,0xa7,0x93,0x40,0x28,0xc0,0x7c,0xbe,0xc8,0x51,0xe8,0x6a,0xf4,0xd5,0xde,0xb2,0xbd,0x58,0x7e,0xd,0x6f,0xc,0xc3,0xe4,0xe9,0x8a,0xea,0x78,0xcc,0x89,0x91,0x10,0x7,0x98,0xb4,0x1d,0x5f,0xa8,0x29,0xbf,0xcf,0x79,0x5c,0x99,0x32,0xe5,0x13,0x39,0xf2,0x84,0x5b,0xf9,0xca,0x75,0x38,0x97,0xcd,0xe0,0xb6,0x4a,0xc9,0xdf,0xf8,0x86,0x2a,0x21,0x5d,0x1f,0x7a,0x82,0x31,0x37,0x3f,0x81,0x1c,0x9,0xdc,0xe1,0xd3,0x90,0x1a,0xab,0xd6,0xd1,0xcb,0xb0,0xa1,0x7d,0x92,0x2e,0x95,0x43,0x63,0x36,0xb3,0x61,0x71,0x9c,0x50,0x8,0xf7,0xe3,0xc7,0x8b,0x8e,0x8f,0xb9,0x72,0x2c,0x30,0xf1,0x56,0x94,0xed,0xf0,0xb1,0xd8,0xef,0xa,0x15,0x5a,0x22,0xec,0xac,0xe6,0x53,0x85,0xd9,0xd7,0xfe,0x6b,0xc6,0xc4,0x6d,0xb7,0xfd,0x5e,0x42,0xce,0x6e,0x35,0xe7,0x69,0x18,0x9e,0x6,0x1,0x0,0xe2,0xa9,0x33,0x16,0x3c,0xf,0x34,0x4c,0xb8,0x9b,0xf5,0x88,0x20,0xa6,0xc2,0x96,0x7f,0x2b,0xfa,0x4d,0x24,0x49,0xba,0x57,0x3b,0xa3,0x73,0x8c,0x76,0xff,0x55,0x47,0x6c,0x1e,0x4f,0x80,0x70,0x26,0x67,0x3,0x9f,0x64,0x68,0x3a,0x4b,0x8d,0x19,0xf6,0x9d,0xdd,0x59,0x45,0x2f,0xbc,0x4,0x66,0x48,0x4e,0xad,0x46,0x1b,0xc1,0x41,0x65,0xf3,0x17,0xeb,0xd4,0x87,0x11,0xf8,0x21,0x45,0xca,0xa3,0xac,0x7d,0xcb,0x3f,0x88,0xb3,0xf,0xa7,0x1c,0x72,0xc0,0xeb,0x78,0xd2,0x7,0xf7,0x99,0xc8,0xd0,0xbc,0xce,0x3d,0xb,0xf1,0x24,0xf4,0x71,0x1a,0xa,0x9e,0xc2,0xa8,0x5a,0xde,0x84,0x18,0xa1,0xe0,0xbd,0xcc,0xe3,0xef,0xe2,0x74,0x46,0xc6,0x53,0x0,0x90,0x6c,0xe1,0xcf,0x3b,0x83,0xc1,0x9c,0xc9,0x2a,0x8f,0x70,0x1b,0xd7,0xc,0x9,0x64,0x40,0xc4,0xe4,0xa9,0x12,0xe6,0xf6,0xb1,0x34,0x36,0x5f,0x6a,0x77,0x92,0xdd,0x68,0x8d,0xf5,0xab,0x8,0x3e,0xd1,0x13,0xb7,0x76,0x41,0x43,0x79,0xec,0x7a,0xd9,0xea,0x30,0x2b,0x61,0xa5,0x6b,0x5e,0x50,0xd4,0x2,0x87,0x65,0x81,0x86,0x91,0xbb,0x2e,0xb4,0xe9,0xb2,0xc5,0x49,0x9f,0x19,0x60,0xee,0x9a,0xd8,0x1f,0x33,0x38,0x48,0x2f,0xae,0xff,0x4b,0xd,0x6d,0x97,0x80,0xe,0x16,0x7e,0x4d,0x3,0xdc,0x10,0x4a,0xf2,0xbf,0x1e,0xb5,0xfe,0xdb,0xbe,0x75,0x62,0x94,0x98,0xfd,0xa6,0xda,0xb0,0xb8,0x5,0xb6,0xcd,0x4e,0x67,0x31,0x1,0xad,0x58,0x7f,0x56,0x4c,0x2c,0x51,0xfa,0x15,0x37,0x26,0x8e,0x5b,0x6,0x9b,0x17,0x9d,0x66,0x54,0x85,0x23,0xd3,0x7c,0x57,0x42,0x1d,0x2d,0x32,0x55,0x29,0x4,0x22,0xc3,0x93,0xba,0x8c,0xd5,0x69,0x5c,0xf0,0x89,0xaa,0x96,0xa4,0xfc,0x95,0x27,0xa2,0x25,0xe5,0xb9,0xc7,0xaf,0x20,0x14,0x39,0x4f,0x47,0xfb,0xf3,0x5d,0x3c,0xe7,0x7b,0x82,0x28,0xa0,0x8a,0xe8,0xdf,0xf9,0x63,0x6e,0x8b,0x44,0xed,0x73,0xd6,0x6f,0x35,0x3a,0x52,0x59,0x93,0x3c,0xcc,0x6a,0xc2,0xf2,0xad,0xb8,0xeb,0xc6,0xba,0xdd,0x55,0x7c,0x2c,0xcd,0xb3,0x86,0x3a,0x63,0x79,0x45,0x66,0x1f,0xc8,0x7a,0x13,0x4b,0x56,0xa,0xca,0x4d,0xfb,0xcf,0x40,0x28,0x14,0xa8,0xa0,0xd6,0x8,0xd3,0xb2,0x1c,0x4f,0xc7,0x6d,0x94,0x16,0x30,0x7,0x65,0xab,0x64,0x81,0x8c,0x80,0x39,0x9c,0x2,0xb6,0xbd,0xd5,0xda,0xdc,0xf0,0x37,0x75,0x41,0xc0,0xa7,0xd7,0x82,0xe2,0xa4,0x10,0xf9,0xe1,0x6f,0x78,0x33,0xec,0xa2,0x91,0x50,0x1d,0xa5,0xff,0x34,0x11,0x5a,0xf1,0x7b,0x8d,0x9a,0x51,0x35,0x49,0x12,0x77,0x59,0xea,0x57,0x5f,0xde,0x88,0xa1,0x22,0x90,0xb7,0x42,0xee,0xbe,0xc3,0xa3,0xb9,0xc9,0xd8,0xfa,0x15,0x74,0xe9,0xb4,0x61,0xbb,0x89,0x72,0xf8,0x38,0xf4,0x9f,0x60,0xaf,0x8b,0xe6,0xe3,0xfd,0x46,0xb,0x2b,0xdb,0x5e,0x19,0x9,0x98,0x85,0xb0,0xd9,0x62,0x87,0x32,0x7d,0xd1,0xe7,0x44,0x1a,0x99,0x58,0xfc,0x3e,0x3,0x96,0xac,0xae,0xdf,0x5,0x36,0x95,0x84,0x4a,0x8e,0xc4,0xed,0x3b,0xbf,0xb1,0x69,0x6e,0x8a,0x68,0x5b,0xc1,0x54,0x7e,0xa6,0x2a,0x5d,0x6,0x1,0x8f,0xf6,0x70,0xaa,0xce,0x17,0xfe,0x92,0x43,0x4c,0x25,0x5c,0x67,0xd0,0x24,0x9d,0xf3,0x48,0xe0,0x3d,0x97,0x4,0x2f,0x27,0x76,0x18,0xe8,0xd2,0x21,0x53,0x3f,0x1b,0xcb,0x1e,0xe4,0x71,0xe5,0xf5,0x9e,0x31,0xb5,0x47,0x2d,0xf,0x4e,0xf7,0x6b,0x0,0xc,0x23,0x52,0x29,0xa9,0x9b,0xd,0x83,0x7f,0xef,0xbc,0x6c,0xd4,0x20,0xe,0xc5,0x26,0x73,0x2e}; diff --git a/lib/playfair/playfair.c b/lib/playfair/playfair.c new file mode 100644 index 0000000..782c17e --- /dev/null +++ b/lib/playfair/playfair.c @@ -0,0 +1,31 @@ +#include + +#include "playfair.h" + +void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4]); +void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey); +void cycle(unsigned char* block, uint32_t key_schedule[11][4]); +void z_xor(unsigned char* in, unsigned char* out, int blocks); +void x_xor(unsigned char* in, unsigned char* out, int blocks); + +extern unsigned char default_sap[]; + +void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut) +{ + unsigned char* chunk1 = &cipherText[16]; + unsigned char* chunk2 = &cipherText[56]; + int i; + unsigned char blockIn[16]; + unsigned char sapKey[16]; + uint32_t key_schedule[11][4]; + generate_session_key(default_sap, message3, sapKey); + generate_key_schedule(sapKey, key_schedule); + z_xor(chunk2, blockIn, 1); + cycle(blockIn, key_schedule); + for (i = 0; i < 16; i++) { + keyOut[i] = blockIn[i] ^ chunk1[i]; + } + x_xor(keyOut, keyOut, 1); + z_xor(keyOut, keyOut, 1); +} + diff --git a/lib/playfair/playfair.h b/lib/playfair/playfair.h new file mode 100644 index 0000000..5909a99 --- /dev/null +++ b/lib/playfair/playfair.h @@ -0,0 +1,6 @@ +#ifndef PLAYFAIR_H +#define PLAYFAIR_H + +void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut); + +#endif diff --git a/lib/playfair/sap_hash.c b/lib/playfair/sap_hash.c new file mode 100644 index 0000000..9e7ae85 --- /dev/null +++ b/lib/playfair/sap_hash.c @@ -0,0 +1,96 @@ +#include +#include +#include + +#define printf(...) (void)0; + +void garble(unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned char*); + +unsigned char rol8(unsigned char input, int count) +{ + return ((input << count) & 0xff) | (input & 0xff) >> (8-count); +} + +uint32_t rol8x(unsigned char input, int count) +{ + return ((input << count)) | (input) >> (8-count); +} + + +void sap_hash(unsigned char* blockIn, unsigned char* keyOut) +{ + uint32_t* block_words = (uint32_t*)blockIn; + uint32_t* out_words = (uint32_t*)keyOut; + unsigned char buffer0[20] = {0x96, 0x5F, 0xC6, 0x53, 0xF8, 0x46, 0xCC, 0x18, 0xDF, 0xBE, 0xB2, 0xF8, 0x38, 0xD7, 0xEC, 0x22, 0x03, 0xD1, 0x20, 0x8F}; + unsigned char buffer1[210]; + unsigned char buffer2[35] = {0x43, 0x54, 0x62, 0x7A, 0x18, 0xC3, 0xD6, 0xB3, 0x9A, 0x56, 0xF6, 0x1C, 0x14, 0x3F, 0x0C, 0x1D, 0x3B, 0x36, 0x83, 0xB1, 0x39, 0x51, 0x4A, 0xAA, 0x09, 0x3E, 0xFE, 0x44, 0xAF, 0xDE, 0xC3, 0x20, 0x9D, 0x42, 0x3A}; + unsigned char buffer3[132]; + unsigned char buffer4[21] = {0xED, 0x25, 0xD1, 0xBB, 0xBC, 0x27, 0x9F, 0x02, 0xA2, 0xA9, 0x11, 0x00, 0x0C, 0xB3, 0x52, 0xC0, 0xBD, 0xE3, 0x1B, 0x49, 0xC7}; + int i0_index[11] = {18, 22, 23, 0, 5, 19, 32, 31, 10, 21, 30}; + uint8_t w,x,y,z; + int i, j; + + // Load the input into the buffer + for (i = 0; i < 210; i++) + { + // We need to swap the byte order around so it is the right endianness + uint32_t in_word = block_words[((i % 64)>>2)]; + uint32_t in_byte = (in_word >> ((3-(i % 4)) << 3)) & 0xff; + buffer1[i] = in_byte; + } + // Next a scrambling + for (i = 0; i < 840; i++) + { + // We have to do unsigned, 32-bit modulo, or we get the wrong indices + x = buffer1[((i-155) & 0xffffffff) % 210]; + y = buffer1[((i-57) & 0xffffffff) % 210]; + z = buffer1[((i-13) & 0xffffffff) % 210]; + w = buffer1[(i & 0xffffffff) % 210]; + buffer1[i % 210] = (rol8(y, 5) + (rol8(z, 3) ^ w) - rol8(x,7)) & 0xff; + } + printf("Garbling...\n"); + // I have no idea what this is doing (yet), but it gives the right output + garble(buffer0, buffer1, buffer2, buffer3, buffer4); + + // Fill the output with 0xE1 + for (i = 0; i < 16; i++) + keyOut[i] = 0xE1; + + // Now we use all the buffers we have calculated to grind out the output. First buffer3 + for (i = 0; i < 11; i++) + { + // Note that this is addition (mod 255) and not XOR + // Also note that we only use certain indices + // And that index 3 is hard-coded to be 0x3d (Maybe we can hack this up by changing buffer3[0] to be 0xdc? + if (i == 3) + keyOut[i] = 0x3d; + else + keyOut[i] = ((keyOut[i] + buffer3[i0_index[i] * 4]) & 0xff); + } + + // Then buffer0 + for (i = 0; i < 20; i++) + keyOut[i % 16] ^= buffer0[i]; + + // Then buffer2 + for (i = 0; i < 35; i++) + keyOut[i % 16] ^= buffer2[i]; + + // Do buffer1 + for (i = 0; i < 210; i++) + keyOut[(i % 16)] ^= buffer1[i]; + + + // Now we do a kind of reverse-scramble + for (j = 0; j < 16; j++) + { + for (i = 0; i < 16; i++) + { + x = keyOut[((i-7) & 0xffffffff) % 16]; + y = keyOut[i % 16]; + z = keyOut[((i-37) & 0xffffffff) % 16]; + w = keyOut[((i-177) & 0xffffffff) % 16]; + keyOut[i] = rol8(x, 1) ^ y ^ rol8(z, 6) ^ rol8(w, 5); + } + } +} diff --git a/lib/raop.c b/lib/raop.c new file mode 100644 index 0000000..c275dc1 --- /dev/null +++ b/lib/raop.c @@ -0,0 +1,772 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *=================================================================== + * modified by fduncanh 2021-23 + */ + +#include +#include +#include +#include + +#include "raop.h" +#include "raop_rtp.h" +#include "pairing.h" +#include "httpd.h" + +#include "global.h" +#include "fairplay.h" +#include "netutils.h" +#include "logger.h" +#include "compat.h" +#include "raop_rtp_mirror.h" +#include "raop_ntp.h" + +struct raop_s { + /* Callbacks for audio and video */ + raop_callbacks_t callbacks; + + /* Logger instance */ + logger_t *logger; + + /* Pairing, HTTP daemon and RSA key */ + pairing_t *pairing; + httpd_t *httpd; + + dnssd_t *dnssd; + + /* local network ports */ + unsigned short port; + unsigned short timing_lport; + unsigned short control_lport; + unsigned short data_lport; + unsigned short mirror_data_lport; + + /* configurable plist items: width, height, refreshRate, maxFPS, overscanned * + * also clientFPSdata, which controls whether video stream info received * + * from the client is shown on terminal monitor. */ + uint16_t width; + uint16_t height; + uint8_t refreshRate; + uint8_t maxFPS; + uint8_t overscanned; + uint8_t clientFPSdata; + + int audio_delay_micros; + int max_ntp_timeouts; + + /* for temporary storage of pin during pair-pin start */ + unsigned short pin; + bool use_pin; + + /* public key as string */ + char pk_str[2*ED25519_KEY_SIZE + 1]; + + /* place to store media_data_store */ + airplay_video_t *airplay_video; + + /* activate support for HLS live streaming */ + bool hls_support; +}; + +struct raop_conn_s { + raop_t *raop; + raop_ntp_t *raop_ntp; + raop_rtp_t *raop_rtp; + raop_rtp_mirror_t *raop_rtp_mirror; + fairplay_t *fairplay; + pairing_session_t *session; + airplay_video_t *airplay_video; + + unsigned char *local; + int locallen; + + unsigned char *remote; + int remotelen; + + unsigned int zone_id; + + connection_type_t connection_type; + + char *client_session_id; + + bool have_active_remote; +}; +typedef struct raop_conn_s raop_conn_t; + +#include "raop_handlers.h" +#include "http_handlers.h" + +static void * +conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen, unsigned int zone_id) { + raop_t *raop = opaque; + raop_conn_t *conn; + char ip_address[40]; + assert(raop); + + conn = calloc(1, sizeof(raop_conn_t)); + if (!conn) { + return NULL; + } + conn->raop = raop; + conn->raop_rtp = NULL; + conn->raop_rtp_mirror = NULL; + conn->raop_ntp = NULL; + conn->fairplay = fairplay_init(raop->logger); + + if (!conn->fairplay) { + free(conn); + return NULL; + } + conn->session = pairing_session_init(raop->pairing); + if (!conn->session) { + fairplay_destroy(conn->fairplay); + free(conn); + return NULL; + } + + + utils_ipaddress_to_string(locallen, local, zone_id, ip_address, (int) sizeof(ip_address)); + logger_log(conn->raop->logger, LOGGER_INFO, "Local : %s", ip_address); + + utils_ipaddress_to_string(remotelen, remote, zone_id, ip_address, (int) sizeof(ip_address)); + logger_log(conn->raop->logger, LOGGER_INFO, "Remote: %s", ip_address); + + conn->local = malloc(locallen); + assert(conn->local); + memcpy(conn->local, local, locallen); + + conn->remote = malloc(remotelen); + assert(conn->remote); + memcpy(conn->remote, remote, remotelen); + + conn->zone_id = zone_id; + + conn->locallen = locallen; + conn->remotelen = remotelen; + + conn->connection_type = CONNECTION_TYPE_UNKNOWN; + conn->client_session_id = NULL; + conn->airplay_video = NULL; + + + conn->have_active_remote = false; + + if (raop->callbacks.conn_init) { + raop->callbacks.conn_init(raop->callbacks.cls); + } + + return conn; +} + +static void +conn_request(void *ptr, http_request_t *request, http_response_t **response) { + char *response_data = NULL; + int response_datalen = 0; + raop_conn_t *conn = ptr; + bool hls_request = false; + logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request"); + bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); + + /* + All requests arriving here have been parsed by llhttp to obtain + method | url | protocol (RTSP/1.0 or HTTP/1.1) + + There are three types of connections supplying these requests: + Connections from the AirPlay client: + (1) type RAOP connections with CSeq seqence header, and no X-Apple-Session-ID header + (2) type AIRPLAY connection with an X-Apple-Sequence-ID header and no Cseq header + Connections from localhost: + (3) type HLS internal connections from the local HLS server (gstreamer) at localhost with neither + of these headers, but a Host: localhost:[port] header. method = GET. + */ + + const char *method = http_request_get_method(request); + + if (!method) { + return; + } + +/* this rejects messages from _airplay._tcp for video streaming protocol unless bool raop->hls_support is true*/ + const char *cseq = http_request_get_header(request, "CSeq"); + const char *protocol = http_request_get_protocol(request); + if (!cseq && !conn->raop->hls_support) { + logger_log(conn->raop->logger, LOGGER_INFO, "ignoring AirPlay video streaming request (use option -hls to activate HLS support)"); + return; + } + + const char *url = http_request_get_url(request); + const char *client_session_id = http_request_get_header(request, "X-Apple-Session-ID"); + const char *host = http_request_get_header(request, "Host"); + hls_request = (host && !cseq && !client_session_id); + + if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) { + if (cseq) { + if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) { + char ipaddr[40]; + utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr))); + if (httpd_nohold(conn->raop->httpd)) { + logger_log(conn->raop->logger, LOGGER_INFO, "\"nohold\" feature: switch to new connection request from %s", ipaddr); + if (conn->raop->callbacks.video_reset) { + conn->raop->callbacks.video_reset(conn->raop->callbacks.cls); + } + httpd_remove_known_connections(conn->raop->httpd); + } else { + logger_log(conn->raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr); + *response = http_response_create(); + http_response_init(*response, protocol, 409, "Conflict: Server is connected to another client"); + goto finish; + } + } + logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type RAOP", ptr); + httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_RAOP); + conn->connection_type = CONNECTION_TYPE_RAOP; + } else if (client_session_id) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type AirPlay", ptr); + httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_AIRPLAY); + conn->connection_type = CONNECTION_TYPE_AIRPLAY; + size_t len = strlen(client_session_id) + 1; + conn->client_session_id = (char *) malloc(len); + strncpy(conn->client_session_id, client_session_id, len); + /* airplay video has been requested: shut down any running RAOP udp services */ + raop_conn_t *raop_conn = (raop_conn_t *) httpd_get_connection_by_type(conn->raop->httpd, CONNECTION_TYPE_RAOP, 1); + if (raop_conn) { + raop_rtp_mirror_t *raop_rtp_mirror = raop_conn->raop_rtp_mirror; + if (raop_rtp_mirror) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP mirror" + " service on RAOP connection %p", raop_conn); + raop_rtp_mirror_stop(raop_rtp_mirror); + } + + raop_rtp_t *raop_rtp = raop_conn->raop_rtp; + if (raop_rtp) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP audio" + " service on RAOP connection %p", raop_conn); + raop_rtp_stop(raop_rtp); + } + + raop_ntp_t *raop_ntp = raop_conn->raop_ntp; + if (raop_rtp) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping NTP time" + " service on RAOP connection %p", raop_conn); + raop_ntp_stop(raop_ntp); + } + } + } else if (host) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type HLS", ptr); + httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_HLS); + conn->connection_type = CONNECTION_TYPE_HLS; + } else { + logger_log(conn->raop->logger, LOGGER_WARNING, "connection from unknown connection type"); + } + } + + /* this response code and message will be modified by the handler if necessary */ + *response = http_response_create(); + http_response_init(*response, protocol, 200, "OK"); + + /* is this really necessary? or is it obsolete? (added for all RTSP requests EXCEPT "RECORD") */ + if (cseq && strcmp(method, "RECORD")) { + http_response_add_header(*response, "Audio-Jack-Status", "connected; type=digital"); + } + + if (!conn->have_active_remote) { + const char *active_remote = http_request_get_header(request, "Active-Remote"); + if (active_remote) { + conn->have_active_remote = true; + if (conn->raop->callbacks.export_dacp) { + const char *dacp_id = http_request_get_header(request, "DACP-ID"); + conn->raop->callbacks.export_dacp(conn->raop->callbacks.cls, active_remote, dacp_id); + } + } + } + + logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s %s", method, url, protocol); + char *header_str= NULL; + http_request_get_header_string(request, &header_str); + if (header_str) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", header_str); + bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL); + bool data_is_text = (strstr(header_str,"text/") != NULL); + free(header_str); + int request_datalen; + const char *request_data = http_request_get_data(request, &request_datalen); + if (request_data && logger_debug) { + if (request_datalen > 0) { + /* logger has a buffer limit of 4096 */ + if (data_is_plist) { + plist_t req_root_node = NULL; + plist_from_bin(request_data, request_datalen, &req_root_node); + char * plist_xml; + uint32_t plist_len; + plist_to_xml(req_root_node, &plist_xml, &plist_len); + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml); + free(plist_xml); + plist_free(req_root_node); + } else if (data_is_text) { + char *data_str = utils_data_to_text((char *) request_data, request_datalen); + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); + free(data_str); + } else { + char *data_str = utils_data_to_string((unsigned char *) request_data, request_datalen, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); + free(data_str); + } + } + } + } + + if (client_session_id) { + assert(!strcmp(client_session_id, conn->client_session_id)); + } + + logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url); + raop_handler_t handler = NULL; + if (!hls_request && !strcmp(protocol, "RTSP/1.0")) { + if (!strcmp(method, "POST")) { + if (!strcmp(url, "/feedback")) { + handler = &raop_handler_feedback; + } else if (!strcmp(url, "/pair-pin-start")) { + handler = &raop_handler_pairpinstart; + } else if (!strcmp(url, "/pair-setup-pin")) { + handler = &raop_handler_pairsetup_pin; + } else if (!strcmp(url, "/pair-setup")) { + handler = &raop_handler_pairsetup; + } else if (!strcmp(url, "/pair-verify")) { + handler = &raop_handler_pairverify; + } else if (!strcmp(url, "/fp-setup")) { + handler = &raop_handler_fpsetup; + } else if (!strcmp(url, "/getProperty")) { + handler = &http_handler_get_property; + } else if (!strcmp(url, "/audioMode")) { + //handler = &http_handler_audioMode; + } + } else if (!strcmp(method, "GET")) { + if (!strcmp(url, "/info")) { + handler = &raop_handler_info; + } + } else if (!strcmp(method, "OPTIONS")) { + handler = &raop_handler_options; + } else if (!strcmp(method, "SETUP")) { + handler = &raop_handler_setup; + } else if (!strcmp(method, "GET_PARAMETER")) { + handler = &raop_handler_get_parameter; + } else if (!strcmp(method, "SET_PARAMETER")) { + handler = &raop_handler_set_parameter; + } else if (!strcmp(method, "RECORD")) { + handler = &raop_handler_record; + } else if (!strcmp(method, "FLUSH")) { + handler = &raop_handler_flush; + } else if (!strcmp(method, "TEARDOWN")) { + handler = &raop_handler_teardown; + } + } else if (!hls_request && !strcmp(protocol, "HTTP/1.1")) { + if (!strcmp(method, "POST")) { + if (!strcmp(url, "/reverse")) { + handler = &http_handler_reverse; + } else if (!strcmp(url, "/play")) { + handler = &http_handler_play; + } else if (!strncmp (url, "/getProperty?", strlen("/getProperty?"))) { + handler = &http_handler_get_property; + } else if (!strncmp(url, "/scrub?", strlen("/scrub?"))) { + handler = &http_handler_scrub; + } else if (!strncmp(url, "/rate?", strlen("/rate?"))) { + handler = &http_handler_rate; + } else if (!strcmp(url, "/stop")) { + handler = &http_handler_stop; + } else if (!strcmp(url, "/action")) { + handler = &http_handler_action; + } else if (!strcmp(url, "/fp-setup2")) { + handler = &http_handler_fpsetup2; + } + } else if (!strcmp(method, "GET")) { + if (!strcmp(url, "/server-info")) { + handler = &http_handler_server_info; + } else if (!strcmp(url, "/playback-info")) { + handler = &http_handler_playback_info; + } + } else if (!strcmp(method, "PUT")) { + if (!strncmp (url, "/setProperty?", strlen("/setProperty?"))) { + handler = &http_handler_set_property; + } else { + } + } + } else if (hls_request) { + handler = &http_handler_hls; + } + + if (handler != NULL) { + handler(conn, request, *response, &response_data, &response_datalen); + } else { + logger_log(conn->raop->logger, LOGGER_INFO, + "Unhandled Client Request: %s %s %s", method, url, protocol); + } + + finish:; + if (!hls_request) { + http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION); + if (cseq) { + http_response_add_header(*response, "CSeq", cseq); + } + } + http_response_finish(*response, response_data, response_datalen); + + int len; + const char *data = http_response_get_data(*response, &len); + if (response_data && response_datalen > 0) { + len -= response_datalen; + } else { + len -= 2; + } + header_str = utils_data_to_text(data, len); + logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s", header_str); + + bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL); + bool data_is_text = (strstr(header_str,"text/") != NULL || + strstr(header_str, "x-mpegURL") != NULL); + free(header_str); + if (response_data) { + if (response_datalen > 0 && logger_debug) { + /* logger has a buffer limit of 4096 */ + if (data_is_plist) { + plist_t res_root_node = NULL; + plist_from_bin(response_data, response_datalen, &res_root_node); + char * plist_xml; + uint32_t plist_len; + plist_to_xml(res_root_node, &plist_xml, &plist_len); + plist_free(res_root_node); + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml); + free(plist_xml); + } else if (data_is_text) { + char *data_str = utils_data_to_text((char*) response_data, response_datalen); + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); + free(data_str); + } else { + char *data_str = utils_data_to_string((unsigned char *) response_data, response_datalen, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); + free(data_str); + } + } + if (response_data) { + free(response_data); + } + } +} + +static void +conn_destroy(void *ptr) { + raop_conn_t *conn = ptr; + + logger_log(conn->raop->logger, LOGGER_DEBUG, "Destroying connection"); + + if (conn->raop->callbacks.conn_destroy) { + conn->raop->callbacks.conn_destroy(conn->raop->callbacks.cls); + } + + if (conn->raop_rtp) { + /* This is done in case TEARDOWN was not called */ + raop_rtp_destroy(conn->raop_rtp); + } + if (conn->raop_rtp_mirror) { + /* This is done in case TEARDOWN was not called */ + raop_rtp_mirror_destroy(conn->raop_rtp_mirror); + } + if (conn->raop_ntp) { + raop_ntp_destroy(conn->raop_ntp); + } + + if (conn->raop->callbacks.video_flush) { + conn->raop->callbacks.video_flush(conn->raop->callbacks.cls); + } + + free(conn->local); + free(conn->remote); + pairing_session_destroy(conn->session); + fairplay_destroy(conn->fairplay); + if (conn->client_session_id) { + free(conn->client_session_id); + } + if (conn->airplay_video) { + airplay_video_service_destroy(conn->airplay_video); + } + + free(conn); +} + +raop_t * +raop_init(raop_callbacks_t *callbacks) { + raop_t *raop; + + assert(callbacks); + + /* Initialize the network */ + if (netutils_init() < 0) { + return NULL; + } + + /* Validate the callbacks structure */ + if (!callbacks->audio_process || + !callbacks->video_process) { + return NULL; + } + + /* Allocate the raop_t structure */ + raop = calloc(1, sizeof(raop_t)); + if (!raop) { + return NULL; + } + + /* Initialize the logger */ + raop->logger = logger_init(); + + /* Copy callbacks structure */ + memcpy(&raop->callbacks, callbacks, sizeof(raop_callbacks_t)); + + /* initialize network port list */ + raop->port = 0; + raop->timing_lport = 0; + raop->control_lport = 0; + raop->data_lport = 0; + raop->mirror_data_lport = 0; + + /* initialize configurable plist parameters */ + raop->width = 1920; + raop->height = 1080; + raop->refreshRate = 60; + raop->maxFPS = 30; + raop->overscanned = 0; + + /* initialise stored pin */ + raop->pin = 0; + raop->use_pin = false; + + /* initialize switch for display of client's streaming data records */ + raop->clientFPSdata = 0; + + raop->max_ntp_timeouts = 0; + raop->audio_delay_micros = 250000; + + raop->hls_support = false; + + return raop; +} + +int +raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile) { + pairing_t *pairing; + httpd_t *httpd; + httpd_callbacks_t httpd_cbs; + + /* create a new public key for pairing */ + int new_key; + pairing = pairing_init_generate(device_id, keyfile, &new_key); + if (!pairing) { + logger_log(raop->logger, LOGGER_ERR, "failed to create new public key for pairing"); + return -1; + } + /* store PK as a string in raop->pk_str */ + memset(raop->pk_str, 0, sizeof(raop->pk_str)); +#ifdef PK + strncpy(raop->pk_str, PK, 2*ED25519_KEY_SIZE); +#else + unsigned char public_key[ED25519_KEY_SIZE]; + pairing_get_public_key(pairing, public_key); + char *pk_str = utils_pk_to_string(public_key, ED25519_KEY_SIZE); + strncpy(raop->pk_str, (const char *) pk_str, 2*ED25519_KEY_SIZE); + free(pk_str); +#endif + if (new_key) { + logger_log(raop->logger, LOGGER_INFO,"*** A new Public Key has been created and stored in %s", keyfile); + } + + /* Set HTTP callbacks to our handlers */ + memset(&httpd_cbs, 0, sizeof(httpd_cbs)); + httpd_cbs.opaque = raop; + httpd_cbs.conn_init = &conn_init; + httpd_cbs.conn_request = &conn_request; + httpd_cbs.conn_destroy = &conn_destroy; + + /* Initialize the http daemon */ + httpd = httpd_init(raop->logger, &httpd_cbs, nohold); + if (!httpd) { + logger_log(raop->logger, LOGGER_ERR, "failed to initialize http daemon"); + pairing_destroy(pairing); + return -1; + } + + raop->pairing = pairing; + raop->httpd = httpd; + return 0; +} + +void +raop_destroy(raop_t *raop) { + if (raop) { + raop_destroy_airplay_video(raop); + raop_stop(raop); + pairing_destroy(raop->pairing); + httpd_destroy(raop->httpd); + logger_destroy(raop->logger); + free(raop); + + /* Cleanup the network */ + netutils_cleanup(); + } +} + +int +raop_is_running(raop_t *raop) { + assert(raop); + + return httpd_is_running(raop->httpd); +} + +void +raop_set_log_level(raop_t *raop, int level) { + assert(raop); + + logger_set_level(raop->logger, level); +} + +int raop_set_plist(raop_t *raop, const char *plist_item, const int value) { + int retval = 0; + assert(raop); + assert(plist_item); + + if (strcmp(plist_item, "width") == 0) { + raop->width = (uint16_t) value; + if ((int) raop->width != value) retval = 1; + } else if (strcmp(plist_item, "height") == 0) { + raop->height = (uint16_t) value; + if ((int) raop->height != value) retval = 1; + } else if (strcmp(plist_item, "refreshRate") == 0) { + raop->refreshRate = (uint8_t) value; + if ((int) raop->refreshRate != value) retval = 1; + } else if (strcmp(plist_item, "maxFPS") == 0) { + raop->maxFPS = (uint8_t) value; + if ((int) raop->maxFPS != value) retval = 1; + } else if (strcmp(plist_item, "overscanned") == 0) { + raop->overscanned = (uint8_t) (value ? 1 : 0); + if ((int) raop->overscanned != value) retval = 1; + } else if (strcmp(plist_item, "clientFPSdata") == 0) { + raop->clientFPSdata = (value ? 1 : 0); + if ((int) raop->clientFPSdata != value) retval = 1; + } else if (strcmp(plist_item, "max_ntp_timeouts") == 0) { + raop->max_ntp_timeouts = (value > 0 ? value : 0); + if (raop->max_ntp_timeouts != value) retval = 1; + } else if (strcmp(plist_item, "audio_delay_micros") == 0) { + if (value >= 0 && value <= 10 * SECOND_IN_USECS) { + raop->audio_delay_micros = value; + } + if (raop->audio_delay_micros != value) retval = 1; + } else if (strcmp(plist_item, "pin") == 0) { + raop->pin = value; + raop->use_pin = true; + } else if (strcmp(plist_item, "hls") == 0) { + raop->hls_support = (value > 0 ? true : false); + } else { + retval = -1; + } + return retval; +} + +void +raop_set_port(raop_t *raop, unsigned short port) { + assert(raop); + raop->port = port; +} + +void +raop_set_udp_ports(raop_t *raop, unsigned short udp[3]) { + assert(raop); + raop->timing_lport = udp[0]; + raop->control_lport = udp[1]; + raop->data_lport = udp[2]; +} + +void +raop_set_tcp_ports(raop_t *raop, unsigned short tcp[2]) { + assert(raop); + raop->mirror_data_lport = tcp[0]; + raop->port = tcp[1]; +} + +unsigned short +raop_get_port(raop_t *raop) { + assert(raop); + return raop->port; +} + +void * +raop_get_callback_cls(raop_t *raop) { + assert(raop); + return raop->callbacks.cls; +} + +void +raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls) { + assert(raop); + + logger_set_callback(raop->logger, callback, cls); +} + +void +raop_set_dnssd(raop_t *raop, dnssd_t *dnssd) { + assert(dnssd); + dnssd_set_pk(dnssd, raop->pk_str); + raop->dnssd = dnssd; +} + + +int +raop_start(raop_t *raop, unsigned short *port) { + assert(raop); + assert(port); + return httpd_start(raop->httpd, port); +} + +void +raop_stop(raop_t *raop) { + assert(raop); + httpd_stop(raop->httpd); +} + +void raop_remove_known_connections(raop_t * raop) { + httpd_remove_known_connections(raop->httpd); +} + +airplay_video_t *deregister_airplay_video(raop_t *raop) { + airplay_video_t *airplay_video = raop->airplay_video; + raop->airplay_video = NULL; + return airplay_video; +} + +bool register_airplay_video(raop_t *raop, airplay_video_t *airplay_video) { + if (raop->airplay_video) { + return false; + } + raop->airplay_video = airplay_video; + return true; +} + +airplay_video_t * get_airplay_video(raop_t *raop) { + return raop->airplay_video; +} + +void raop_destroy_airplay_video(raop_t *raop) { + if (raop->airplay_video) { + airplay_video_service_destroy(raop->airplay_video); + raop->airplay_video = NULL; + } +} diff --git a/lib/raop.h b/lib/raop.h new file mode 100644 index 0000000..e0a2062 --- /dev/null +++ b/lib/raop.h @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2012-2015 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *=================================================================== + * modified by fduncanh 2021-23 + */ + +#ifndef RAOP_H +#define RAOP_H + +#include "dnssd.h" +#include "stream.h" +#include "raop_ntp.h" +#include "airplay_video.h" + +#if defined (WIN32) && defined(DLL_EXPORT) +# define RAOP_API __declspec(dllexport) +#else +# define RAOP_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct raop_s raop_t; + +typedef void (*raop_log_callback_t)(void *cls, int level, const char *msg); + + +typedef struct playback_info_s { + //char * uuid; + uint32_t stallcount; + double duration; + double position; + float rate; + bool ready_to_play; + bool playback_buffer_empty; + bool playback_buffer_full; + bool playback_likely_to_keep_up; + int num_loaded_time_ranges; + int num_seekable_time_ranges; + void *loadedTimeRanges; + void *seekableTimeRanges; +} playback_info_t; + +typedef enum video_codec_e { + VIDEO_CODEC_UNKNOWN, + VIDEO_CODEC_H264, + VIDEO_CODEC_H265 +} video_codec_t; + +struct raop_callbacks_s { + void* cls; + + void (*audio_process)(void *cls, raop_ntp_t *ntp, audio_decode_struct *data); + void (*video_process)(void *cls, raop_ntp_t *ntp, video_decode_struct *data); + void (*video_pause)(void *cls); + void (*video_resume)(void *cls); + + /* Optional but recommended callback functions */ + void (*conn_init)(void *cls); + void (*conn_destroy)(void *cls); + void (*conn_reset) (void *cls, int timeouts, bool reset_video); + void (*conn_teardown)(void *cls, bool *teardown_96, bool *teardown_110 ); + void (*audio_flush)(void *cls); + void (*video_flush)(void *cls); + void (*audio_set_volume)(void *cls, float volume); + void (*audio_set_metadata)(void *cls, const void *buffer, int buflen); + void (*audio_set_coverart)(void *cls, const void *buffer, int buflen); + void (*audio_remote_control_id)(void *cls, const char *dacp_id, const char *active_remote_header); + void (*audio_set_progress)(void *cls, unsigned int start, unsigned int curr, unsigned int end); + void (*audio_get_format)(void *cls, unsigned char *ct, unsigned short *spf, bool *usingScreen, bool *isMedia, uint64_t *audioFormat); + void (*video_report_size)(void *cls, float *width_source, float *height_source, float *width, float *height); + void (*report_client_request) (void *cls, char *deviceid, char *model, char *name, bool *admit); + void (*display_pin) (void *cls, char * pin); + void (*register_client) (void *cls, const char *device_id, const char *pk_str, const char *name); + bool (*check_register) (void *cls, const char *pk_str); + void (*export_dacp) (void *cls, const char *active_remote, const char *dacp_id); + void (*video_reset) (void *cls); + void (*video_set_codec)(void *cls, video_codec_t codec); + /* for HLS video player controls */ + void (*on_video_play) (void *cls, const char *location, const float start_position); + void (*on_video_scrub) (void *cls, const float position); + void (*on_video_rate) (void *cls, const float rate); + void (*on_video_stop) (void *cls); + void (*on_video_acquire_playback_info) (void *cls, playback_info_t *playback_video); + +}; +typedef struct raop_callbacks_s raop_callbacks_t; +raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote, + int remote_addr_len, unsigned short timing_rport, + timing_protocol_t *time_protocol); + +int airplay_video_service_init(raop_t *raop, unsigned short port, const char *session_id); + +bool register_airplay_video(raop_t *raop, airplay_video_t *airplay_video); +airplay_video_t *get_airplay_video(raop_t *raop); +airplay_video_t *deregister_airplay_video(raop_t *raop); + +RAOP_API raop_t *raop_init(raop_callbacks_t *callbacks); +RAOP_API int raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile); +RAOP_API void raop_set_log_level(raop_t *raop, int level); +RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls); +RAOP_API int raop_set_plist(raop_t *raop, const char *plist_item, const int value); +RAOP_API void raop_set_port(raop_t *raop, unsigned short port); +RAOP_API void raop_set_udp_ports(raop_t *raop, unsigned short port[3]); +RAOP_API void raop_set_tcp_ports(raop_t *raop, unsigned short port[2]); +RAOP_API unsigned short raop_get_port(raop_t *raop); +RAOP_API void *raop_get_callback_cls(raop_t *raop); +RAOP_API int raop_start(raop_t *raop, unsigned short *port); +RAOP_API int raop_is_running(raop_t *raop); +RAOP_API void raop_stop(raop_t *raop); +RAOP_API void raop_set_dnssd(raop_t *raop, dnssd_t *dnssd); +RAOP_API void raop_destroy(raop_t *raop); +RAOP_API void raop_remove_known_connections(raop_t * raop); +RAOP_API void raop_destroy_airplay_video(raop_t *raop); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/raop_buffer.c b/lib/raop_buffer.c new file mode 100644 index 0000000..4fc8d76 --- /dev/null +++ b/lib/raop_buffer.c @@ -0,0 +1,311 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2021-2023 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "raop_buffer.h" +#include "raop_rtp.h" + +#include "crypto.h" +#include "compat.h" +#include "stream.h" +#include "global.h" +#include "utils.h" +#include "byteutils.h" + +#define RAOP_BUFFER_LENGTH 32 + +typedef struct { + /* Data available */ + int filled; + + /* RTP header */ + unsigned short seqnum; + uint64_t rtp_timestamp; + uint64_t ntp_timestamp; + + /* Payload data */ + unsigned int payload_size; + void *payload_data; +} raop_buffer_entry_t; + +struct raop_buffer_s { + logger_t *logger; + /* AES CTX used for decryption */ + aes_ctx_t *aes_ctx; + + /* First and last seqnum */ + int is_empty; + unsigned short first_seqnum; + unsigned short last_seqnum; + + /* RTP buffer entries */ + raop_buffer_entry_t entries[RAOP_BUFFER_LENGTH]; +}; + +raop_buffer_t * +raop_buffer_init(logger_t *logger, + const unsigned char *aeskey, + const unsigned char *aesiv) +{ + raop_buffer_t *raop_buffer; + assert(aeskey); + assert(aesiv); + raop_buffer = calloc(1, sizeof(raop_buffer_t)); + if (!raop_buffer) { + return NULL; + } + raop_buffer->logger = logger; + // Need to be initialized internally + raop_buffer->aes_ctx = aes_cbc_init(aeskey, aesiv, AES_DECRYPT); + + for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) { + raop_buffer_entry_t *entry = &raop_buffer->entries[i]; + entry->payload_data = NULL; + entry->payload_size = 0; + } + + raop_buffer->is_empty = 1; + + return raop_buffer; +} + +void +raop_buffer_destroy(raop_buffer_t *raop_buffer) +{ + for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) { + raop_buffer_entry_t *entry = &raop_buffer->entries[i]; + if (entry->payload_data != NULL) { + free(entry->payload_data); + } + } + + if (raop_buffer) { + aes_cbc_destroy(raop_buffer->aes_ctx); + free(raop_buffer); + } + +} + +static short +seqnum_cmp(unsigned short s1, unsigned short s2) +{ + return (s1 - s2); +} + +int +raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output, unsigned int payload_size, unsigned int *outputlen) +{ + assert(raop_buffer); + int encryptedlen; + if (DECRYPTION_TEST) { + char *str = utils_data_to_string(data,12,12); + logger_log(raop_buffer->logger, LOGGER_INFO, "encrypted 12 byte header %s", str); + free(str); + if (payload_size) { + str = utils_data_to_string(&data[12],16,16); + logger_log(raop_buffer->logger, LOGGER_INFO, "len %d before decryption:\n%s", payload_size, str); + free(str); + } + } + encryptedlen = payload_size / 16*16; + memset(output, 0, payload_size); + + aes_cbc_decrypt(raop_buffer->aes_ctx, &data[12], output, encryptedlen); + aes_cbc_reset(raop_buffer->aes_ctx); + + memcpy(output + encryptedlen, &data[12 + encryptedlen], payload_size - encryptedlen); + *outputlen = payload_size; + if (payload_size && DECRYPTION_TEST){ + switch (output[0]) { + case 0x8c: + case 0x8d: + case 0x8e: + case 0x80: + case 0x81: + case 0x82: + case 0x20: + break; + default: + logger_log(raop_buffer->logger, LOGGER_INFO, "***ERROR AUDIO FRAME IS NOT AAC_ELD OR ALAC"); + break; + } + if (DECRYPTION_TEST == 2) { + logger_log(raop_buffer->logger, LOGGER_INFO, "decrypted audio frame, len = %d", *outputlen); + char *str = utils_data_to_string(output,payload_size,16); + logger_log(raop_buffer->logger, LOGGER_INFO,"%s",str); + free(str); + } else { + char *str = utils_data_to_string(output,16,16); + logger_log(raop_buffer->logger, LOGGER_INFO, "%d after \n%s", payload_size, str); + free(str); + } + } + return 1; +} + +int +raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, int use_seqnum) { + unsigned char empty_packet_marker[] = { 0x00, 0x68, 0x34, 0x00 }; + assert(raop_buffer); + + /* Check packet data length is valid */ + if (datalen < 12 || datalen > RAOP_PACKET_LEN) { + return -1; + } + /* before time is synchronized, some empty data packets are sent */ + if (datalen == 16 && !memcmp(&data[12], empty_packet_marker, 4)) { + return 0; + } + int payload_size = datalen - 12; + + /* Get correct seqnum for the packet */ + unsigned short seqnum; + if (use_seqnum) { + seqnum = byteutils_get_short_be(data, 2); + } else { + seqnum = raop_buffer->first_seqnum; + } + + /* If this packet is too late, just skip it */ + if (!raop_buffer->is_empty && seqnum_cmp(seqnum, raop_buffer->first_seqnum) < 0) { + return 0; + } + + /* Check that there is always space in the buffer, otherwise flush */ + if (seqnum_cmp(seqnum, raop_buffer->first_seqnum + RAOP_BUFFER_LENGTH) >= 0) { + raop_buffer_flush(raop_buffer, seqnum); + } + + /* Get entry corresponding our seqnum */ + raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH]; + if (entry->filled && seqnum_cmp(entry->seqnum, seqnum) == 0) { + /* Packet resend, we can safely ignore */ + return 0; + } + + /* Update the raop_buffer entry header */ + entry->seqnum = seqnum; + entry->rtp_timestamp = *rtp_timestamp; + entry->ntp_timestamp = *ntp_timestamp; + entry->filled = 1; + + entry->payload_data = malloc(payload_size); + int decrypt_ret = raop_buffer_decrypt(raop_buffer, data, entry->payload_data, payload_size, &entry->payload_size); + assert(decrypt_ret >= 0); + assert(entry->payload_size <= payload_size); + + /* Update the raop_buffer seqnums */ + if (raop_buffer->is_empty) { + raop_buffer->first_seqnum = seqnum; + raop_buffer->last_seqnum = seqnum; + raop_buffer->is_empty = 0; + } + if (seqnum_cmp(seqnum, raop_buffer->last_seqnum) > 0) { + raop_buffer->last_seqnum = seqnum; + } + return 1; +} + +void * +raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, unsigned short *seqnum, int no_resend) { + assert(raop_buffer); + + /* Calculate number of entries in the current buffer */ + short entry_count = seqnum_cmp(raop_buffer->last_seqnum, raop_buffer->first_seqnum)+1; + + /* Cannot dequeue from empty buffer */ + if (raop_buffer->is_empty || entry_count <= 0) { + return NULL; + } + + /* Get the first buffer entry for inspection */ + raop_buffer_entry_t *entry = &raop_buffer->entries[raop_buffer->first_seqnum % RAOP_BUFFER_LENGTH]; + if (no_resend) { + /* If we do no resends, always return the first entry */ + } else if (!entry->filled) { + /* Check how much we have space left in the buffer */ + if (entry_count < RAOP_BUFFER_LENGTH) { + /* Return nothing and hope resend gets on time */ + return NULL; + } + /* Risk of buffer overrun, return empty buffer */ + } + + /* Update buffer and validate entry */ + raop_buffer->first_seqnum += 1; + if (!entry->filled) { + return NULL; + } + entry->filled = 0; + + /* Return entry payload buffer */ + *rtp_timestamp = entry->rtp_timestamp; + *ntp_timestamp = entry->ntp_timestamp; + *seqnum = entry->seqnum; + *length = entry->payload_size; + entry->payload_size = 0; + void* data = entry->payload_data; + entry->payload_data = NULL; + return data; +} + +void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque) { + assert(raop_buffer); + assert(resend_cb); + + if (seqnum_cmp(raop_buffer->first_seqnum, raop_buffer->last_seqnum) < 0) { + unsigned short seqnum, count = 0; + logger_log(raop_buffer->logger, LOGGER_DEBUG, "raop_buffer_handle_resends first_seqnum=%u last seqnum=%u", + raop_buffer->first_seqnum, raop_buffer->last_seqnum); + for (seqnum = raop_buffer->first_seqnum; seqnum_cmp(seqnum, raop_buffer->last_seqnum) < 0; seqnum++) { + raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH]; + if (entry->filled) { + break; + } + count++; + } + if (count){ + resend_cb(opaque, raop_buffer->first_seqnum, count); + } + } +} + +void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq) { + assert(raop_buffer); + + for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) { + if (raop_buffer->entries[i].payload_data) { + free(raop_buffer->entries[i].payload_data); + raop_buffer->entries[i].payload_data = NULL; + raop_buffer->entries[i].payload_size = 0; + } + raop_buffer->entries[i].filled = 0; + } + if (next_seq < 0 || next_seq > 0xffff) { + raop_buffer->is_empty = 1; + } else { + raop_buffer->first_seqnum = next_seq; + raop_buffer->last_seqnum = next_seq - 1; + } +} diff --git a/lib/raop_buffer.h b/lib/raop_buffer.h new file mode 100644 index 0000000..1093188 --- /dev/null +++ b/lib/raop_buffer.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2021-23 + */ + +#ifndef RAOP_BUFFER_H +#define RAOP_BUFFER_H + +#include "logger.h" +#include "raop_rtp.h" + +typedef struct raop_buffer_s raop_buffer_t; + +typedef int (*raop_resend_cb_t)(void *opaque, unsigned short seqno, unsigned short count); + +raop_buffer_t *raop_buffer_init(logger_t *logger, + const unsigned char *aeskey, + const unsigned char *aesiv); +int raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, int use_seqnum); +void *raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *ntp_timestamp, uint64_t *rtp_timestamp, unsigned short *seqnum, int no_resend); +void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque); +void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq); + +int raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output, + unsigned int datalen, unsigned int *outputlen); +void raop_buffer_destroy(raop_buffer_t *raop_buffer); + +#endif diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h new file mode 100644 index 0000000..3b2c838 --- /dev/null +++ b/lib/raop_handlers.h @@ -0,0 +1,1085 @@ +/** + * Copyright (C) 2018 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *=================================================================== + * modfied by fduncanh 2021-2023 + */ + +/* This file should be only included from raop.c as it defines static handler + * functions and depends on raop internals */ + +#include "dnssdint.h" +#include "utils.h" +#include +#include +#include +#define AUDIO_SAMPLE_RATE 44100 /* all supported AirPlay audio format use this sample rate */ +#define SECOND_IN_USECS 1000000 + +typedef void (*raop_handler_t)(raop_conn_t *, http_request_t *, + http_response_t *, char **, int *); + +static void +raop_handler_info(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + assert(conn->raop->dnssd); + + plist_t res_node = plist_new_dict(); + + /* deviceID is the physical hardware address, and will not change */ + int hw_addr_raw_len = 0; + const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len); + char *hw_addr = calloc(1, 3 * hw_addr_raw_len); + //int hw_addr_len = + utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len); + plist_t device_id_node = plist_new_string(hw_addr); + plist_dict_set_item(res_node, "deviceID", device_id_node); + + /* Persistent Public Key */ + int pk_len = 0; + char *pk = utils_parse_hex(conn->raop->pk_str, strlen(conn->raop->pk_str), &pk_len); + plist_t pk_node = plist_new_data(pk, pk_len); + plist_dict_set_item(res_node, "pk", pk_node); + + /* airplay_txt is from the _airplay._tcp dnssd announuncement, may not be necessary */ + int airplay_txt_len = 0; + const char *airplay_txt = dnssd_get_airplay_txt(conn->raop->dnssd, &airplay_txt_len); + plist_t txt_airplay_node = plist_new_data(airplay_txt, airplay_txt_len); + plist_dict_set_item(res_node, "txtAirPlay", txt_airplay_node); + + uint64_t features = dnssd_get_airplay_features(conn->raop->dnssd); + plist_t features_node = plist_new_uint(features); + plist_dict_set_item(res_node, "features", features_node); + + int name_len = 0; + const char *name = dnssd_get_name(conn->raop->dnssd, &name_len); + plist_t name_node = plist_new_string(name); + plist_dict_set_item(res_node, "name", name_node); + + plist_t audio_latencies_node = plist_new_array(); + plist_t audio_latencies_0_node = plist_new_dict(); + plist_t audio_latencies_0_output_latency_micros_node = plist_new_bool(0); + plist_t audio_latencies_0_type_node = plist_new_uint(100); + plist_t audio_latencies_0_audio_type_node = plist_new_string("default"); + plist_t audio_latencies_0_input_latency_micros_node = plist_new_uint(0); + plist_dict_set_item(audio_latencies_0_node, "type", audio_latencies_0_type_node); + plist_dict_set_item(audio_latencies_0_node, "inputLatencyMicros", audio_latencies_0_input_latency_micros_node); + plist_dict_set_item(audio_latencies_0_node, "audioType", audio_latencies_0_audio_type_node); + plist_dict_set_item(audio_latencies_0_node, "outputLatencyMicros", audio_latencies_0_output_latency_micros_node); + plist_array_append_item(audio_latencies_node, audio_latencies_0_node); + + plist_t audio_latencies_1_node = plist_new_dict(); + plist_t audio_latencies_1_output_latency_micros_node = plist_new_bool(0); + plist_t audio_latencies_1_type_node = plist_new_uint(101); + plist_t audio_latencies_1_audio_type_node = plist_new_string("default"); + plist_t audio_latencies_1_input_latency_micros_node = plist_new_uint(0); + plist_dict_set_item(audio_latencies_1_node, "type", audio_latencies_1_type_node); + plist_dict_set_item(audio_latencies_1_node, "audioType", audio_latencies_1_audio_type_node); + plist_dict_set_item(audio_latencies_1_node, "inputLatencyMicros", audio_latencies_1_input_latency_micros_node); + plist_dict_set_item(audio_latencies_1_node, "outputLatencyMicros", audio_latencies_1_output_latency_micros_node); + plist_array_append_item(audio_latencies_node, audio_latencies_1_node); + plist_dict_set_item(res_node, "audioLatencies", audio_latencies_node); + + plist_t audio_formats_node = plist_new_array(); + plist_t audio_format_0_node = plist_new_dict(); + plist_t audio_format_0_type_node = plist_new_uint(100); + plist_t audio_format_0_audio_input_formats_node = plist_new_uint(0x3fffffc); + plist_t audio_format_0_audio_output_formats_node = plist_new_uint(0x3fffffc); + plist_dict_set_item(audio_format_0_node, "audioOutputFormats", audio_format_0_audio_output_formats_node); + plist_dict_set_item(audio_format_0_node, "type", audio_format_0_type_node); + plist_dict_set_item(audio_format_0_node, "audioInputFormats", audio_format_0_audio_input_formats_node); + plist_array_append_item(audio_formats_node, audio_format_0_node); + + plist_t audio_format_1_node = plist_new_dict(); + plist_t audio_format_1_type_node = plist_new_uint(101); + plist_t audio_format_1_audio_input_formats_node = plist_new_uint(0x3fffffc); + plist_t audio_format_1_audio_output_formats_node = plist_new_uint(0x3fffffc); + plist_dict_set_item(audio_format_1_node, "audioOutputFormats", audio_format_1_audio_output_formats_node); + plist_dict_set_item(audio_format_1_node, "type", audio_format_1_type_node); + plist_dict_set_item(audio_format_1_node, "audioInputFormats", audio_format_1_audio_input_formats_node); + plist_array_append_item(audio_formats_node, audio_format_1_node); + plist_dict_set_item(res_node, "audioFormats", audio_formats_node); + + plist_t pi_node = plist_new_string(AIRPLAY_PI); + plist_dict_set_item(res_node, "pi", pi_node); + + plist_t vv_node = plist_new_uint(strtol(AIRPLAY_VV, NULL, 10)); + plist_dict_set_item(res_node, "vv", vv_node); + + plist_t status_flags_node = plist_new_uint(68); + plist_dict_set_item(res_node, "statusFlags", status_flags_node); + + plist_t keep_alive_low_power_node = plist_new_uint(1); + plist_dict_set_item(res_node, "keepAliveLowPower", keep_alive_low_power_node); + + plist_t source_version_node = plist_new_string(GLOBAL_VERSION); + plist_dict_set_item(res_node, "sourceVersion", source_version_node); + + plist_t keep_alive_send_stats_as_body_node = plist_new_bool(1); + plist_dict_set_item(res_node, "keepAliveSendStatsAsBody", keep_alive_send_stats_as_body_node); + + plist_t model_node = plist_new_string(GLOBAL_MODEL); + plist_dict_set_item(res_node, "model", model_node); + + plist_t mac_address_node = plist_new_string(hw_addr); + plist_dict_set_item(res_node, "macAddress", mac_address_node); + + plist_t displays_node = plist_new_array(); + plist_t displays_0_node = plist_new_dict(); + plist_t displays_0_width_physical_node = plist_new_uint(0); + plist_t displays_0_height_physical_node = plist_new_uint(0); + plist_t displays_0_uuid_node = plist_new_string("e0ff8a27-6738-3d56-8a16-cc53aacee925"); + plist_t displays_0_width_node = plist_new_uint(conn->raop->width); + plist_t displays_0_height_node = plist_new_uint(conn->raop->height); + plist_t displays_0_width_pixels_node = plist_new_uint(conn->raop->width); + plist_t displays_0_height_pixels_node = plist_new_uint(conn->raop->height); + plist_t displays_0_rotation_node = plist_new_bool(0); /* set to true in AppleTV gen 3 (which has features bit 8 set */ + plist_t displays_0_refresh_rate_node = plist_new_real((double) 1.0 / conn->raop->refreshRate); /* set as real 0.166666 = 60hz in AppleTV gen 3 */ + plist_t displays_0_max_fps_node = plist_new_uint(conn->raop->maxFPS); + plist_t displays_0_overscanned_node = plist_new_bool(conn->raop->overscanned); + plist_t displays_0_features = plist_new_uint(14); + + plist_dict_set_item(displays_0_node, "uuid", displays_0_uuid_node); + plist_dict_set_item(displays_0_node, "widthPhysical", displays_0_width_physical_node); + plist_dict_set_item(displays_0_node, "heightPhysical", displays_0_height_physical_node); + plist_dict_set_item(displays_0_node, "width", displays_0_width_node); + plist_dict_set_item(displays_0_node, "height", displays_0_height_node); + plist_dict_set_item(displays_0_node, "widthPixels", displays_0_width_pixels_node); + plist_dict_set_item(displays_0_node, "heightPixels", displays_0_height_pixels_node); + plist_dict_set_item(displays_0_node, "rotation", displays_0_rotation_node); + plist_dict_set_item(displays_0_node, "refreshRate", displays_0_refresh_rate_node); + plist_dict_set_item(displays_0_node, "maxFPS", displays_0_max_fps_node); + plist_dict_set_item(displays_0_node, "overscanned", displays_0_overscanned_node); + plist_dict_set_item(displays_0_node, "features", displays_0_features); + plist_array_append_item(displays_node, displays_0_node); + plist_dict_set_item(res_node, "displays", displays_node); + + plist_to_bin(res_node, response_data, (uint32_t *) response_datalen); + plist_free(res_node); + http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); + free(pk); + free(hw_addr); +} + +static void +raop_handler_pairpinstart(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + logger_log(conn->raop->logger, LOGGER_INFO, "client sent PAIR-PIN-START request"); + int pin_4; + if (conn->raop->pin > 9999) { + pin_4 = conn->raop->pin % 10000; + } else { + pin_4 = random_pin(); + if (pin_4 < 0) { + logger_log(conn->raop->logger, LOGGER_ERR, "Failed to generate random pin"); + } else { + conn->raop->pin = (unsigned short) pin_4 % 10000; + } + } + char pin[6]; + snprintf(pin, 5, "%04u", pin_4); + if (conn->raop->callbacks.display_pin) { + conn->raop->callbacks.display_pin(conn->raop->callbacks.cls, pin); + } + logger_log(conn->raop->logger, LOGGER_INFO, "*** CLIENT MUST NOW ENTER PIN = \"%s\" AS AIRPLAY PASSWORD", pin); + *response_data = NULL; + response_datalen = 0; +} + +static void +raop_handler_pairsetup_pin(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) { + + const char *request_data = NULL;; + int request_datalen = 0; + bool data_is_plist = false; + bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); + request_data = http_request_get_data(request, &request_datalen); + logger_log(conn->raop->logger, LOGGER_INFO, "client requested pair-setup-pin, datalen = %d", request_datalen); + if (request_datalen > 0) { + char *header_str= NULL; + http_request_get_header_string(request, &header_str); + logger_log(conn->raop->logger, LOGGER_INFO, "request header: %s", header_str); + data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL); + free(header_str); + } + if (!data_is_plist) { + logger_log(conn->raop->logger, LOGGER_INFO, "did not receive expected plist from client, request_datalen = %d", + request_datalen); + goto authentication_failed; + } + + /* process the pair-setup-pin request */ + plist_t req_root_node = NULL; + plist_from_bin(request_data, request_datalen, &req_root_node); + plist_t req_method_node = plist_dict_get_item(req_root_node, "method"); + plist_t req_user_node = plist_dict_get_item(req_root_node, "user"); + plist_t req_pk_node = plist_dict_get_item(req_root_node, "pk"); + plist_t req_proof_node = plist_dict_get_item(req_root_node, "proof"); + plist_t req_epk_node = plist_dict_get_item(req_root_node, "epk"); + plist_t req_authtag_node = plist_dict_get_item(req_root_node, "authTag"); + + if (PLIST_IS_STRING(req_method_node) && PLIST_IS_STRING(req_user_node)) { + /* this is the initial pair-setup-pin request */ + const char *salt; + char pin[6]; + const char *pk; + int len_pk, len_salt; + char *method = NULL; + char *user = NULL; + plist_get_string_val(req_method_node, &method); + if (strncmp(method, "pin", strlen (method))) { + logger_log(conn->raop->logger, LOGGER_ERR, "error, required method is \"pin\", client requested \"%s\"", method); + *response_data = NULL; + response_datalen = 0; + free (method); + plist_free (req_root_node); + return; + } + free (method); + plist_get_string_val(req_user_node, &user); + logger_log(conn->raop->logger, LOGGER_INFO, "pair-setup-pin: device_id = %s", user); + snprintf(pin, 6, "%04u", conn->raop->pin % 10000); + if (conn->raop->pin < 10000) { + conn->raop->pin = 0; + } + int ret = srp_new_user(conn->session, conn->raop->pairing, (const char *) user, + (const char *) pin, &salt, &len_salt, &pk, &len_pk); + free(user); + plist_free(req_root_node); + if (ret < 0) { + logger_log(conn->raop->logger, LOGGER_ERR, "failed to create user, err = %d", ret); + goto authentication_failed; + } + plist_t res_root_node = plist_new_dict(); + plist_t res_salt_node = plist_new_data(salt, len_salt); + plist_t res_pk_node = plist_new_data(pk, len_pk); + plist_dict_set_item(res_root_node, "pk", res_pk_node); + plist_dict_set_item(res_root_node, "salt", res_salt_node); + plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen); + plist_free(res_root_node); + http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); + return; + } else if (PLIST_IS_DATA(req_pk_node) && PLIST_IS_DATA(req_proof_node)) { + /* this is the second part of pair-setup-pin request */ + char *client_pk = NULL; + char *client_proof = NULL; + unsigned char proof[64]; + memset(proof, 0, sizeof(proof)); + uint64_t client_pk_len; + uint64_t client_proof_len; + plist_get_data_val(req_pk_node, &client_pk, &client_pk_len); + plist_get_data_val(req_proof_node, &client_proof, &client_proof_len); + if (logger_debug) { + char *str = utils_data_to_string((const unsigned char *) client_proof, client_proof_len, 20); + logger_log(conn->raop->logger, LOGGER_DEBUG, "client SRP6a proof :\n%s", str); + free (str); + } + memcpy(proof, client_proof, (int) client_proof_len); + free (client_proof); + int ret = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk, + (int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof)); + free (client_pk); + plist_free(req_root_node); + if (ret < 0) { + logger_log(conn->raop->logger, LOGGER_ERR, "Client Authentication Failure (client proof not validated)"); + goto authentication_failed; + } + if (logger_debug) { + char *str = utils_data_to_string((const unsigned char *) proof, sizeof(proof), 20); + logger_log(conn->raop->logger, LOGGER_DEBUG, "server SRP6a proof :\n%s", str); + free (str); + } + plist_t res_root_node = plist_new_dict(); + plist_t res_proof_node = plist_new_data((const char *) proof, 20); + plist_dict_set_item(res_root_node, "proof", res_proof_node); + plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen); + plist_free(res_root_node); + http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); + return; + } else if (PLIST_IS_DATA(req_epk_node) && PLIST_IS_DATA(req_authtag_node)) { + /* this is the third part of pair-setup-pin request */ + char *client_epk = NULL; + char *client_authtag = NULL; + uint64_t client_epk_len; + uint64_t client_authtag_len; + unsigned char epk[ED25519_KEY_SIZE]; + unsigned char authtag[GCM_AUTHTAG_SIZE]; + int ret; + plist_get_data_val(req_epk_node, &client_epk, &client_epk_len); + plist_get_data_val(req_authtag_node, &client_authtag, &client_authtag_len); + + if (logger_debug) { + char *str = utils_data_to_string((const unsigned char *) client_epk, client_epk_len, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "client_epk %d:\n%s\n", (int) client_epk_len, str); + str = utils_data_to_string((const unsigned char *) client_authtag, client_authtag_len, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "client_authtag %d:\n%s\n", (int) client_authtag_len, str); + free (str); + } + + memcpy(epk, client_epk, ED25519_KEY_SIZE); + memcpy(authtag, client_authtag, GCM_AUTHTAG_SIZE); + free (client_authtag); + free (client_epk); + plist_free(req_root_node); + ret = srp_confirm_pair_setup(conn->session, conn->raop->pairing, epk, authtag); + if (ret < 0) { + logger_log(conn->raop->logger, LOGGER_ERR, "pair-pin-setup (step 3): client authentication failed\n"); + goto authentication_failed; + } else { + logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-pin-setup success\n"); + } + pairing_session_set_setup_status(conn->session); + plist_t res_root_node = plist_new_dict(); + plist_t res_epk_node = plist_new_data((const char *) epk, 32); + plist_t res_authtag_node = plist_new_data((const char *) authtag, 16); + plist_dict_set_item(res_root_node, "epk", res_epk_node); + plist_dict_set_item(res_root_node, "authTag", res_authtag_node); + plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen); + plist_free(res_root_node); + http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); + return; + } + authentication_failed:; + http_response_init(response, "RTSP/1.0", 470, "Client Authentication Failure"); +} + +static void +raop_handler_pairsetup(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + unsigned char public_key[ED25519_KEY_SIZE]; + //const char *data; + int datalen; + + //data = + http_request_get_data(request, &datalen); + if (datalen != 32) { + logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data"); + return; + } + + pairing_get_public_key(conn->raop->pairing, public_key); + pairing_session_set_setup_status(conn->session); + + *response_data = malloc(sizeof(public_key)); + if (*response_data) { + http_response_add_header(response, "Content-Type", "application/octet-stream"); + memcpy(*response_data, public_key, sizeof(public_key)); + *response_datalen = sizeof(public_key); + } +} + +static void +raop_handler_pairverify(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + bool register_check = false; + if (pairing_session_check_handshake_status(conn->session)) { + if (conn->raop->use_pin) { + pairing_session_set_setup_status(conn->session); + register_check = true; + } else { + return; + } + } + unsigned char public_key[X25519_KEY_SIZE]; + unsigned char signature[PAIRING_SIG_SIZE]; + const unsigned char *data; + int datalen; + + data = (unsigned char *) http_request_get_data(request, &datalen); + if (datalen < 4) { + logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data"); + return; + } + switch (data[0]) { + case 1: + if (datalen != 4 + X25519_KEY_SIZE + ED25519_KEY_SIZE) { + logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data"); + return; + } + /* We can fall through these errors, the result will just be garbage... */ + if (pairing_session_handshake(conn->session, data + 4, data + 4 + X25519_KEY_SIZE)) { + logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake"); + } + if (pairing_session_get_public_key(conn->session, public_key)) { + logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ECDH public key"); + } + if (pairing_session_get_signature(conn->session, signature)) { + logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature"); + } + if (register_check) { + bool registered_client = true; + if (conn->raop->callbacks.check_register) { + const unsigned char *pk = data + 4 + X25519_KEY_SIZE; + char *pk64; + ed25519_pk_to_base64(pk, &pk64); + registered_client = conn->raop->callbacks.check_register(conn->raop->callbacks.cls, pk64); + free (pk64); + } + + if (!registered_client) { + return; + } + } + *response_data = malloc(sizeof(public_key) + sizeof(signature)); + if (*response_data) { + http_response_add_header(response, "Content-Type", "application/octet-stream"); + memcpy(*response_data, public_key, sizeof(public_key)); + memcpy(*response_data + sizeof(public_key), signature, sizeof(signature)); + *response_datalen = sizeof(public_key) + sizeof(signature); + } + break; + case 0: + logger_log(conn->raop->logger, LOGGER_DEBUG, "2nd pair-verify step: checking signature"); + if (datalen != 4 + PAIRING_SIG_SIZE) { + logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data"); + return; + } + + if (pairing_session_finish(conn->session, data + 4)) { + logger_log(conn->raop->logger, LOGGER_ERR, "Incorrect pair-verify signature"); + http_response_set_disconnect(response, 1); + return; + } + logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-verify: signature is verified"); + http_response_add_header(response, "Content-Type", "application/octet-stream"); + break; + } +} + +static void +raop_handler_fpsetup(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + const unsigned char *data; + int datalen; + + data = (unsigned char *) http_request_get_data(request, &datalen); + if (datalen == 16) { + *response_data = malloc(142); + if (*response_data) { + http_response_add_header(response, "Content-Type", "application/octet-stream"); + if (!fairplay_setup(conn->fairplay, data, (unsigned char *) *response_data)) { + *response_datalen = 142; + } else { + // Handle error? + free(*response_data); + *response_data = NULL; + } + } + } else if (datalen == 164) { + *response_data = malloc(32); + if (*response_data) { + http_response_add_header(response, "Content-Type", "application/octet-stream"); + if (!fairplay_handshake(conn->fairplay, data, (unsigned char *) *response_data)) { + *response_datalen = 32; + } else { + // Handle error? + free(*response_data); + *response_data = NULL; + } + } + } else { + logger_log(conn->raop->logger, LOGGER_ERR, "Invalid fp-setup data length"); + return; + } +} + +static void +raop_handler_options(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + http_response_add_header(response, "Public", "SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER"); +} + +static void +raop_handler_setup(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + const char *dacp_id; + const char *active_remote_header; + bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); + + const char *data; + int data_len; + data = http_request_get_data(request, &data_len); + + dacp_id = http_request_get_header(request, "DACP-ID"); + active_remote_header = http_request_get_header(request, "Active-Remote"); + + if (dacp_id && active_remote_header) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id); + logger_log(conn->raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header); + if (conn->raop_rtp) { + raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header); + } + } + + // Parsing bplist + plist_t req_root_node = NULL; + plist_from_bin(data, data_len, &req_root_node); + plist_t req_ekey_node = plist_dict_get_item(req_root_node, "ekey"); + plist_t req_eiv_node = plist_dict_get_item(req_root_node, "eiv"); + + // For the response + plist_t res_root_node = plist_new_dict(); + + if (PLIST_IS_DATA(req_eiv_node) && PLIST_IS_DATA(req_ekey_node)) { + // The first SETUP call that initializes keys and timing + + unsigned char aesiv[16]; + unsigned char aeskey[16]; + unsigned char eaeskey[72]; + + logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1"); + + // First setup + char* eiv = NULL; + uint64_t eiv_len = 0; + + char *deviceID = NULL; + char *model = NULL; + char *name = NULL; + bool admit_client = true; + plist_t req_deviceid_node = plist_dict_get_item(req_root_node, "deviceID"); + plist_get_string_val(req_deviceid_node, &deviceID); + plist_t req_model_node = plist_dict_get_item(req_root_node, "model"); + plist_get_string_val(req_model_node, &model); + plist_t req_name_node = plist_dict_get_item(req_root_node, "name"); + plist_get_string_val(req_name_node, &name); + if (conn->raop->callbacks.report_client_request) { + conn->raop->callbacks.report_client_request(conn->raop->callbacks.cls, deviceID, model, name, &admit_client); + } + if (admit_client && deviceID && name && conn->raop->callbacks.register_client) { + bool pending_registration; + char *client_device_id; + char *client_pk; /* encoded as null-terminated base64 string*/ + access_client_session_data(conn->session, &client_device_id, &client_pk, &pending_registration); + if (pending_registration) { + if (client_pk && !strcmp(deviceID, client_device_id)) { + conn->raop->callbacks.register_client(conn->raop->callbacks.cls, client_device_id, client_pk, name); + } + } + if (client_pk) { + free (client_pk); + } + } + if (deviceID) { + free (deviceID); + deviceID = NULL; + } + if (model) { + free (model); + model = NULL; + } + if (name) { + free (name); + name = NULL; + } + if (admit_client == false) { + /* client is not authorized to connect */ + plist_free(res_root_node); + plist_free(req_root_node); + return; + } + + plist_get_data_val(req_eiv_node, &eiv, &eiv_len); + memcpy(aesiv, eiv, 16); + free(eiv); + logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len); + if (logger_debug) { + char* str = utils_data_to_string(aesiv, 16, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str); + free(str); + } + + char* ekey = NULL; + uint64_t ekey_len = 0; + plist_get_data_val(req_ekey_node, &ekey, &ekey_len); + memcpy(eaeskey,ekey,72); + free(ekey); + logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len); + // eaeskey is 72 bytes, aeskey is 16 bytes + if (logger_debug) { + char *str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str); + free (str); + } + int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) eaeskey, aeskey); + logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret); + if (logger_debug) { + char *str = utils_data_to_string(aeskey, 16, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str); + free(str); + } + + const char *user_agent = http_request_get_header(request, "User-Agent"); + logger_log(conn->raop->logger, LOGGER_INFO, "Client identified as User-Agent: %s", user_agent); + + bool old_protocol = false; +#ifdef OLD_PROTOCOL_CLIENT_USER_AGENT_LIST /* set in global.h */ + if (strstr(OLD_PROTOCOL_CLIENT_USER_AGENT_LIST, user_agent)) old_protocol = true; +#endif + if (old_protocol) { /* some windows AirPlay-client emulators use old AirPlay 1 protocol with unhashed AES key */ + logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)"); + } else { + unsigned char ecdh_secret[X25519_KEY_SIZE]; + if (pairing_get_ecdh_secret_key(conn->session, ecdh_secret)) { + /* In this case (legacy) pairing with client was successfully set up and created the shared ecdh_secret: + * aeskey must now be hashed with it + * + * If byte 27 of features ("supports legacy pairing") is turned off, the client does not request pairsetup + * and does NOT set up pairing (this eliminates a 5 second delay in connecting with no apparent bad effects). + * In this case, ecdh_secret does not exist, so aeskey should NOT be hashed with it. + + * UxPlay may be able to function with byte 27 turned off because it currently does not support connections + * with more than one client at a time. AppleTV supports up to 12 clients, uses pairing to give each a distinct + * SessionID and ecdh_secret. + + * The "old protocol" Windows AirPlay client AirMyPC seems not to respect the byte 27 setting, and always sets + * up the ecdh_secret, but decryption fails if aeskey is hashed.*/ + + if (logger_debug) { + char *str = utils_data_to_string(ecdh_secret, X25519_KEY_SIZE, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "32 byte shared ecdh_secret:\n%s", str); + free(str); + } + memcpy(eaeskey, aeskey, 16); + sha_ctx_t *ctx = sha_init(); + sha_update(ctx, eaeskey, 16); + sha_update(ctx, ecdh_secret, 32); + sha_final(ctx, eaeskey, NULL); + sha_destroy(ctx); + memcpy(aeskey, eaeskey, 16); + if (logger_debug) { + char *str = utils_data_to_string(aeskey, 16, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey after sha-256 hash with ecdh_secret:\n%s", str); + free(str); + } + } + } + + // Time port + plist_t req_is_remote_control_only_node = plist_dict_get_item(req_root_node, "isRemoteControlOnly"); + if (req_is_remote_control_only_node) { + uint8_t bool_val = 0; + plist_get_bool_val(req_is_remote_control_only_node, &bool_val); + if (bool_val) { + logger_log(conn->raop->logger, LOGGER_ERR, "Client specified AirPlay2 \"Remote Control\" protocol\n" + " Only AirPlay v1 protocol (using NTP and timing port) is supported"); + } + } + char *timing_protocol = NULL; + timing_protocol_t time_protocol; + plist_t req_timing_protocol_node = plist_dict_get_item(req_root_node, "timingProtocol"); + plist_get_string_val(req_timing_protocol_node, &timing_protocol); + if (timing_protocol) { + int string_len = strlen(timing_protocol); + if (strncmp(timing_protocol, "NTP", string_len) == 0) { + time_protocol = NTP; + } else if (strncmp(timing_protocol, "None", string_len) == 0) { + time_protocol = TP_NONE; + } else { + time_protocol = TP_OTHER; + } + if (time_protocol != NTP) { + logger_log(conn->raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s," + " but timingProtocol= NTP is required here", timing_protocol); + } + free (timing_protocol); + timing_protocol = NULL; + } else { + logger_log(conn->raop->logger, LOGGER_DEBUG, "Client did not specify timingProtocol," + " old protocol without offset will be used"); + time_protocol = TP_UNSPECIFIED; + } + uint64_t timing_rport = 0; + plist_t req_timing_port_node = plist_dict_get_item(req_root_node, "timingPort"); + if (req_timing_port_node) { + plist_get_uint_val(req_timing_port_node, &timing_rport); + } + if (timing_rport) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport); + } else { + logger_log(conn->raop->logger, LOGGER_ERR, "Client did not supply timing_rport," + " may be using unsupported AirPlay2 \"Remote Control\" protocol"); + } + unsigned short timing_lport = conn->raop->timing_lport; + + conn->raop_ntp = NULL; + conn->raop_rtp = NULL; + conn->raop_rtp_mirror = NULL; + char remote[40]; + int len = utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, remote, (int) sizeof(remote)); + if (!len || len > sizeof(remote)) { + char *str = utils_data_to_string(conn->remote, conn->remotelen, 16); + logger_log(conn->raop->logger, LOGGER_ERR, "failed to extract valid client ip address:\n" + "*** UxPlay will be unable to send communications to client.\n" + "*** address length %d, zone_id %u address data:\n%sparser returned \"%s\"\n", + conn->remotelen, conn->zone_id, str, remote); + free(str); + } + conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, remote, + conn->remotelen, (unsigned short) timing_rport, &time_protocol); + raop_ntp_start(conn->raop_ntp, &timing_lport, conn->raop->max_ntp_timeouts); + conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, + remote, conn->remotelen, aeskey, aesiv); + conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, + conn->raop_ntp, remote, conn->remotelen, aeskey); + + /* the event port is not used in mirror mode or audio mode */ + unsigned short event_port = 0; + plist_t res_event_port_node = plist_new_uint(event_port); + plist_t res_timing_port_node = plist_new_uint(timing_lport); + plist_dict_set_item(res_root_node, "timingPort", res_timing_port_node); + plist_dict_set_item(res_root_node, "eventPort", res_event_port_node); + + logger_log(conn->raop->logger, LOGGER_DEBUG, "eport = %d, tport = %d", event_port, timing_lport); + } + + // Process stream setup requests + plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams"); + if (PLIST_IS_ARRAY(req_streams_node)) { + plist_t res_streams_node = plist_new_array(); + + int count = plist_array_get_size(req_streams_node); + for (int i = 0; i < count; i++) { + plist_t req_stream_node = plist_array_get_item(req_streams_node, i); + plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type"); + uint64_t type; + plist_get_uint_val(req_stream_type_node, &type); + logger_log(conn->raop->logger, LOGGER_DEBUG, "type = %llu", type); + + switch (type) { + case 110: { + // Mirroring + unsigned short dport = conn->raop->mirror_data_lport; + plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID"); + uint64_t stream_connection_id; + plist_get_uint_val(stream_id_node, &stream_connection_id); + logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption" + " key and iv): %llu", stream_connection_id); + + if (conn->raop_rtp_mirror) { + raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id); + raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata); + logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully"); + } else { + logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!"); + http_response_set_disconnect(response, 1); + } + + plist_t res_stream_node = plist_new_dict(); + plist_t res_stream_data_port_node = plist_new_uint(dport); + plist_t res_stream_type_node = plist_new_uint(110); + plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node); + plist_dict_set_item(res_stream_node, "type", res_stream_type_node); + plist_array_append_item(res_streams_node, res_stream_node); + + break; + } case 96: { + // Audio + unsigned short cport = conn->raop->control_lport, dport = conn->raop->data_lport; + unsigned short remote_cport = 0; + unsigned char ct; + unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */ + + uint64_t uint_val = 0; + plist_t req_stream_control_port_node = plist_dict_get_item(req_stream_node, "controlPort"); + plist_get_uint_val(req_stream_control_port_node, &uint_val); + remote_cport = (unsigned short) uint_val; /* must != 0 to activate audio resend requests */ + + plist_t req_stream_ct_node = plist_dict_get_item(req_stream_node, "ct"); + plist_get_uint_val(req_stream_ct_node, &uint_val); + ct = (unsigned char) uint_val; + + if (conn->raop->callbacks.audio_get_format) { + /* get additional audio format parameters */ + uint64_t audioFormat; + unsigned short spf; + bool isMedia; + bool usingScreen; + uint8_t bool_val = 0; + + plist_t req_stream_spf_node = plist_dict_get_item(req_stream_node, "spf"); + plist_get_uint_val(req_stream_spf_node, &uint_val); + spf = (unsigned short) uint_val; + + plist_t req_stream_audio_format_node = plist_dict_get_item(req_stream_node, "audioFormat"); + plist_get_uint_val(req_stream_audio_format_node, &audioFormat); + + plist_t req_stream_is_media_node = plist_dict_get_item(req_stream_node, "isMedia"); + if (req_stream_is_media_node) { + plist_get_bool_val(req_stream_is_media_node, &bool_val); + isMedia = (bool) bool_val; + } else { + isMedia = false; + } + + plist_t req_stream_using_screen_node = plist_dict_get_item(req_stream_node, "usingScreen"); + if (req_stream_using_screen_node) { + plist_get_bool_val(req_stream_using_screen_node, &bool_val); + usingScreen = (bool) bool_val; + } else { + usingScreen = false; + } + + conn->raop->callbacks.audio_get_format(conn->raop->callbacks.cls, &ct, &spf, &usingScreen, &isMedia, &audioFormat); + } + + if (conn->raop_rtp) { + raop_rtp_start_audio(conn->raop_rtp, &remote_cport, &cport, &dport, &ct, &sr); + logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success"); + } else { + logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!"); + http_response_set_disconnect(response, 1); + } + + plist_t res_stream_node = plist_new_dict(); + plist_t res_stream_data_port_node = plist_new_uint(dport); + plist_t res_stream_control_port_node = plist_new_uint(cport); + plist_t res_stream_type_node = plist_new_uint(96); + plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node); + plist_dict_set_item(res_stream_node, "controlPort", res_stream_control_port_node); + plist_dict_set_item(res_stream_node, "type", res_stream_type_node); + plist_array_append_item(res_streams_node, res_stream_node); + + break; + } + + default: + logger_log(conn->raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type); + http_response_set_disconnect(response, 1); + break; + } + } + + plist_dict_set_item(res_root_node, "streams", res_streams_node); + } + + plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen); + plist_free(res_root_node); + plist_free(req_root_node); + http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); +} + +static void +raop_handler_get_parameter(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + const char *content_type; + const char *data; + int datalen; + + content_type = http_request_get_header(request, "Content-Type"); + data = http_request_get_data(request, &datalen); + if (!strcmp(content_type, "text/parameters")) { + const char *current = data; + + while (current && (datalen - (current - data) > 0)) { + const char *next; + + /* This is a bit ugly, but seems to be how airport works too */ + if ((datalen - (current - data) >= 8) && !strncmp(current, "volume\r\n", 8)) { + const char volume[] = "volume: 0.0\r\n"; + + http_response_add_header(response, "Content-Type", "text/parameters"); + *response_data = strdup(volume); + if (*response_data) { + *response_datalen = strlen(*response_data); + } + return; + } + + for (next = current ; (datalen - (next - data) > 0) ; ++next) + if (*next == '\r') + break; + + if ((datalen - (next - data) >= 2) && !strncmp(next, "\r\n", 2)) { + if ((next - current) > 0) { + logger_log(conn->raop->logger, LOGGER_WARNING, + "Found an unknown parameter: %.*s", (next - current), current); + } + current = next + 2; + } else { + current = NULL; + } + } + } +} + +static void +raop_handler_set_parameter(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + const char *content_type; + const char *data; + int datalen; + + content_type = http_request_get_header(request, "Content-Type"); + data = http_request_get_data(request, &datalen); + if (!strcmp(content_type, "text/parameters")) { + char *datastr; + datastr = calloc(1, datalen+1); + if (data && datastr && conn->raop_rtp) { + memcpy(datastr, data, datalen); + if ((datalen >= 8) && !strncmp(datastr, "volume: ", 8)) { + float vol = 0.0; + sscanf(datastr+8, "%f", &vol); + raop_rtp_set_volume(conn->raop_rtp, vol); + } else if ((datalen >= 10) && !strncmp(datastr, "progress: ", 10)) { + unsigned int start, curr, end; + sscanf(datastr+10, "%u/%u/%u", &start, &curr, &end); + raop_rtp_set_progress(conn->raop_rtp, start, curr, end); + } + } else if (!conn->raop_rtp) { + logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER"); + } + free(datastr); + } else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "Got image data of %d bytes", datalen); + if (conn->raop_rtp) { + raop_rtp_set_coverart(conn->raop_rtp, data, datalen); + } else { + logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart"); + } + } else if (!strcmp(content_type, "application/x-dmap-tagged")) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "Got metadata of %d bytes", datalen); + if (conn->raop_rtp) { + raop_rtp_set_metadata(conn->raop_rtp, data, datalen); + } else { + logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata"); + } + } +} + + +static void +raop_handler_feedback(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_feedback"); +} + +static void +raop_handler_record(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + char audio_latency[12]; + unsigned int ad = (unsigned int) (((uint64_t) conn->raop->audio_delay_micros) * AUDIO_SAMPLE_RATE / SECOND_IN_USECS); + snprintf(audio_latency, sizeof(audio_latency), "%u", ad); + logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_record"); + http_response_add_header(response, "Audio-Latency", audio_latency); + http_response_add_header(response, "Audio-Jack-Status", "connected; type=analog"); +} + +static void +raop_handler_flush(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + const char *rtpinfo; + int next_seq = -1; + + rtpinfo = http_request_get_header(request, "RTP-Info"); + if (rtpinfo) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "Flush with RTP-Info: %s", rtpinfo); + if (!strncmp(rtpinfo, "seq=", 4)) { + next_seq = strtol(rtpinfo + 4, NULL, 10); + } + } + if (conn->raop_rtp) { + raop_rtp_flush(conn->raop_rtp, next_seq); + } else { + logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH"); + } +} + +static void +raop_handler_teardown(raop_conn_t *conn, + http_request_t *request, http_response_t *response, + char **response_data, int *response_datalen) +{ + /* get the teardown request type(s): (type 96, 110, or none) */ + const char *data; + int data_len; + bool teardown_96 = false, teardown_110 = false; + data = http_request_get_data(request, &data_len); + plist_t req_root_node = NULL; + plist_from_bin(data, data_len, &req_root_node); + char * plist_xml; + uint32_t plist_len; + plist_to_xml(req_root_node, &plist_xml, &plist_len); + logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml); + free(plist_xml); + plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams"); + /* Process stream teardown requests */ + if (PLIST_IS_ARRAY(req_streams_node)) { + uint64_t val; + int count = plist_array_get_size(req_streams_node); + for (int i = 0; i < count; i++) { + plist_t req_stream_node = plist_array_get_item(req_streams_node,0); + plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type"); + plist_get_uint_val(req_stream_type_node, &val); + if (val == 96) { + teardown_96 = true; + } else if (val == 110) { + teardown_110 = true; + } + } + } + plist_free(req_root_node); + if (conn->raop->callbacks.conn_teardown) { + conn->raop->callbacks.conn_teardown(conn->raop->callbacks.cls, &teardown_96, &teardown_110); + } + logger_log(conn->raop->logger, LOGGER_DEBUG, "TEARDOWN request, 96=%d, 110=%d", teardown_96, teardown_110); + + http_response_add_header(response, "Connection", "close"); + + if (teardown_96) { + if (conn->raop_rtp) { + /* Stop our audio RTP session */ + raop_rtp_stop(conn->raop_rtp); + } + } else if (teardown_110) { + if (conn->raop_rtp_mirror) { + /* Stop our video RTP session */ + raop_rtp_mirror_stop(conn->raop_rtp_mirror); + } + } else { + /* Destroy our sessions */ + if (conn->raop_rtp) { + raop_rtp_destroy(conn->raop_rtp); + conn->raop_rtp = NULL; + } + if (conn->raop_rtp_mirror) { + raop_rtp_mirror_destroy(conn->raop_rtp_mirror); + conn->raop_rtp_mirror = NULL; + } + } +} diff --git a/lib/raop_ntp.c b/lib/raop_ntp.c new file mode 100644 index 0000000..1a932e4 --- /dev/null +++ b/lib/raop_ntp.c @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2019 dsafa22 and 2014 Joakim Plate, modified by Florian Draschbacher, + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2021-23 + */ + +// Some of the code in here comes from https://github.com/juhovh/shairplay/pull/25/files + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#define CAST (char *) +#else +#define CAST +#endif + +#include "raop.h" +#include "threads.h" +#include "compat.h" +#include "netutils.h" +#include "byteutils.h" +#include "utils.h" + +#define SECOND_IN_NSECS 1000000000UL +#define RAOP_NTP_DATA_COUNT 8 +#define RAOP_NTP_PHI_PPM 15ull // PPM +#define RAOP_NTP_R_RHO ((1ull << 32) / 1000u) // packet precision +#define RAOP_NTP_S_RHO ((1ull << 32) / 1000u) // system clock precision +#define RAOP_NTP_MAX_DIST ((1500ull << 32) / 1000u) // maximum allowed distance +#define RAOP_NTP_MAX_DISP ((16ull << 32)) // maximum dispersion + +#define RAOP_NTP_CLOCK_BASE (2208988800ull << 32) + +typedef struct raop_ntp_data_s { + uint64_t time; // The local wall clock time at time of ntp packet arrival + uint64_t dispersion; + int64_t delay; // The round trip delay + int64_t offset; // The difference between remote and local wall clock time +} raop_ntp_data_t; + +struct raop_ntp_s { + logger_t *logger; + raop_callbacks_t callbacks; + + int max_ntp_timeouts; + + thread_handle_t thread; + mutex_handle_t run_mutex; + + mutex_handle_t wait_mutex; + cond_handle_t wait_cond; + + raop_ntp_data_t data[RAOP_NTP_DATA_COUNT]; + int data_index; + + // The clock sync params are periodically updated to the AirPlay client's NTP clock + mutex_handle_t sync_params_mutex; + int64_t sync_offset; + int64_t sync_dispersion; + int64_t sync_delay; + + // Socket address of the AirPlay client + struct sockaddr_storage remote_saddr; + socklen_t remote_saddr_len; + + // The remote port of the NTP server on the AirPlay client + unsigned short timing_rport; + + // The local port of the NTP client on the AirPlay server + unsigned short timing_lport; + + /* MUTEX LOCKED VARIABLES START */ + /* These variables only edited mutex locked */ + int running; + int joined; + + // UDP socket + int tsock; + + timing_protocol_t time_protocol; +}; + + +/* + * Used for sorting the data array by delay + */ +static int +raop_ntp_compare(const void* av, const void* bv) +{ + const raop_ntp_data_t* a = (const raop_ntp_data_t*)av; + const raop_ntp_data_t* b = (const raop_ntp_data_t*)bv; + if (a->delay < b->delay) { + return -1; + } else if(a->delay > b->delay) { + return 1; + } else { + return 0; + } +} + +static int +raop_ntp_parse_remote(raop_ntp_t *raop_ntp, const char *remote, int remote_addr_len) +{ + int family; + int ret; + assert(raop_ntp); + if (remote_addr_len == 4) { + family = AF_INET; + } else if (remote_addr_len == 16) { + family = AF_INET6; + } else { + return -1; + } + logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp parse remote ip = %s", remote); + ret = netutils_parse_address(family, remote, + &raop_ntp->remote_saddr, + sizeof(raop_ntp->remote_saddr)); + if (ret < 0) { + return -1; + } + raop_ntp->remote_saddr_len = ret; + return 0; +} + +raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote, + int remote_addr_len, unsigned short timing_rport, timing_protocol_t *time_protocol) { + raop_ntp_t *raop_ntp; + + assert(logger); + assert(callbacks); + + raop_ntp = calloc(1, sizeof(raop_ntp_t)); + if (!raop_ntp) { + return NULL; + } + raop_ntp->time_protocol = *time_protocol; + raop_ntp->logger = logger; + memcpy(&raop_ntp->callbacks, callbacks, sizeof(raop_callbacks_t)); + raop_ntp->timing_rport = timing_rport; + + if (raop_ntp_parse_remote(raop_ntp, remote, remote_addr_len) < 0) { + free(raop_ntp); + return NULL; + } + + // Set port on the remote address struct + ((struct sockaddr_in *) &raop_ntp->remote_saddr)->sin_port = htons(timing_rport); + + raop_ntp->running = 0; + raop_ntp->joined = 1; + + uint64_t time = raop_ntp_get_local_time(raop_ntp); + + for (int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) { + raop_ntp->data[i].offset = 0ll; + raop_ntp->data[i].delay = RAOP_NTP_MAX_DISP; + raop_ntp->data[i].dispersion = RAOP_NTP_MAX_DISP; + raop_ntp->data[i].time = time; + } + + raop_ntp->sync_delay = 0; + raop_ntp->sync_dispersion = 0; + raop_ntp->sync_offset = 0; + + MUTEX_CREATE(raop_ntp->run_mutex); + MUTEX_CREATE(raop_ntp->wait_mutex); + COND_CREATE(raop_ntp->wait_cond); + MUTEX_CREATE(raop_ntp->sync_params_mutex); + return raop_ntp; +} + +void +raop_ntp_destroy(raop_ntp_t *raop_ntp) +{ + if (raop_ntp) { + raop_ntp_stop(raop_ntp); + MUTEX_DESTROY(raop_ntp->run_mutex); + MUTEX_DESTROY(raop_ntp->wait_mutex); + COND_DESTROY(raop_ntp->wait_cond); + MUTEX_DESTROY(raop_ntp->sync_params_mutex); + free(raop_ntp); + } +} + +unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp) { + return raop_ntp->timing_lport; +} + +static int +raop_ntp_init_socket(raop_ntp_t *raop_ntp, int use_ipv6) +{ + assert(raop_ntp); + unsigned short tport = raop_ntp->timing_lport; + int tsock = netutils_init_socket(&tport, use_ipv6, 1); + + if (tsock == -1) { + goto sockets_cleanup; + } + + // We're calling recvfrom without knowing whether there is any data, so we need a timeout + uint32_t recv_timeout_msec = 300; +#ifdef _WIN32 + DWORD tv = recv_timeout_msec; +#else + struct timeval tv; + tv.tv_sec = recv_timeout_msec / (uint32_t) 1000; + tv.tv_usec = ((uint32_t) 1000) * (recv_timeout_msec % (uint32_t) 1000); +#endif + if (setsockopt(tsock, SOL_SOCKET, SO_RCVTIMEO, CAST &tv, sizeof(tv)) < 0) { + goto sockets_cleanup; + } + + /* Set socket descriptors */ + raop_ntp->tsock = tsock; + + /* Set port values */ + raop_ntp->timing_lport = tport; + logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp local timing port socket %d port UDP %d", tsock, tport); + return 0; + + sockets_cleanup: + if (tsock != -1) closesocket(tsock); + return -1; +} + +static void +raop_ntp_flush_socket(int fd) +{ +#ifdef _WIN32 +#define IOCTL ioctlsocket + u_long bytes_available = 0; +#else +#define IOCTL ioctl + int bytes_available = 0; +#endif + while (IOCTL(fd, FIONREAD, &bytes_available) == 0 && bytes_available > 0) + { + // We are guaranteed that we won't block, because bytes are available. + // Read 1 byte. Extra bytes in the datagram will be discarded. + char c; + int result = recvfrom(fd, &c, sizeof(c), 0, NULL, NULL); + if (result < 0) + { + break; + } + } +} + +static THREAD_RETVAL +raop_ntp_thread(void *arg) +{ + raop_ntp_t *raop_ntp = arg; + assert(raop_ntp); + unsigned char response[128]; + int response_len; + unsigned char request[32] = {0x80, 0xd2, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + raop_ntp_data_t data_sorted[RAOP_NTP_DATA_COUNT]; + const unsigned two_pow_n[RAOP_NTP_DATA_COUNT] = {2, 4, 8, 16, 32, 64, 128, 256}; + int timeout_counter = 0; + bool conn_reset = false; + bool logger_debug = (logger_get_level(raop_ntp->logger) >= LOGGER_DEBUG); + + while (1) { + MUTEX_LOCK(raop_ntp->run_mutex); + if (!raop_ntp->running) { + MUTEX_UNLOCK(raop_ntp->run_mutex); + break; + } + MUTEX_UNLOCK(raop_ntp->run_mutex); + + // Flush the socket in case a super delayed response arrived or something + raop_ntp_flush_socket(raop_ntp->tsock); + + // Send request + uint64_t send_time = raop_ntp_get_local_time(raop_ntp); + byteutils_put_ntp_timestamp(request, 24, send_time); + int send_len = sendto(raop_ntp->tsock, (char *)request, sizeof(request), 0, + (struct sockaddr *) &raop_ntp->remote_saddr, raop_ntp->remote_saddr_len); + if (logger_debug) { + char *str = utils_data_to_string(request, sizeof(request), 16); + logger_log(raop_ntp->logger, LOGGER_DEBUG, "\nraop_ntp send time type_t=%d packetlen = %d, now = %8.6f\n%s", + request[1] &~0x80, sizeof(request), (double) send_time / SECOND_IN_NSECS, str); + free(str); + } + if (send_len < 0) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp error sending request. Error %d:%s", + sock_err, SOCKET_ERROR_STRING(sock_err)); + } else { + // Read response + response_len = recvfrom(raop_ntp->tsock, (char *)response, sizeof(response), 0, NULL, NULL); + if (response_len < 0) { + timeout_counter++; + char time[30]; + int level = (timeout_counter == 1 ? LOGGER_DEBUG : LOGGER_ERR); + ntp_timestamp_to_time(send_time, time, sizeof(time)); + logger_log(raop_ntp->logger, level, "raop_ntp receive timeout %d (limit %d) (request sent %s)", + timeout_counter, raop_ntp->max_ntp_timeouts, time); + if (timeout_counter == raop_ntp->max_ntp_timeouts) { + conn_reset = true; /* client is no longer responding */ + break; + } + } else { + //local time of the server when the NTP response packet returns + int64_t t3 = (int64_t) raop_ntp_get_local_time(raop_ntp); + timeout_counter = 0; + + // Local time of the server when the NTP request packet leaves the server + int64_t t0 = (int64_t) byteutils_get_ntp_timestamp(response, 8); + + // Local time of the client when the NTP request packet arrives at the client + int64_t t1 = (int64_t) raop_remote_timestamp_to_nano_seconds(raop_ntp, byteutils_get_long_be(response, 16)); + + // Local time of the client when the response message leaves the client + int64_t t2 = (int64_t) raop_remote_timestamp_to_nano_seconds(raop_ntp, byteutils_get_long_be(response, 24)); + + if (logger_debug) { + char *str = utils_data_to_string(response, response_len, 16); + logger_log(raop_ntp->logger, LOGGER_DEBUG, + "raop_ntp receive time type_t=%d packetlen = %d, now = %8.6f t1 = %8.6f, t2 = %8.6f\n%s", + response[1] &~0x80, response_len, (double) t3 / SECOND_IN_NSECS, (double) t1 / SECOND_IN_NSECS, + (double) t2 / SECOND_IN_NSECS, str); + free(str); + } + // The iOS client device sends its time in seconds relative to an arbitrary Epoch (the last boot). + // For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970. + // This means we have to expect some rather huge offset, but its growth or shrink over time should be small. + + raop_ntp->data_index = (raop_ntp->data_index + 1) % RAOP_NTP_DATA_COUNT; + raop_ntp->data[raop_ntp->data_index].time = t3; + raop_ntp->data[raop_ntp->data_index].offset = ((t1 - t0) + (t2 - t3)) / 2; + raop_ntp->data[raop_ntp->data_index].delay = ((t3 - t0) - (t2 - t1)); + raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS; + + // Sort by delay + memcpy(data_sorted, raop_ntp->data, sizeof(data_sorted)); + qsort(data_sorted, RAOP_NTP_DATA_COUNT, sizeof(data_sorted[0]), raop_ntp_compare); + + uint64_t dispersion = 0ull; + int64_t offset = data_sorted[0].offset; + int64_t delay = data_sorted[RAOP_NTP_DATA_COUNT - 1].delay; + + // Calculate dispersion + for(int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) { + unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS; + dispersion += disp / two_pow_n[i]; + } + + MUTEX_LOCK(raop_ntp->sync_params_mutex); + + int64_t correction = offset - raop_ntp->sync_offset; + raop_ntp->sync_offset = offset; + raop_ntp->sync_dispersion = dispersion; + raop_ntp->sync_delay = delay; + MUTEX_UNLOCK(raop_ntp->sync_params_mutex); + + logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp sync correction = %lld", correction); + } + } + + // Sleep for 3 seconds + struct timespec wait_time; + MUTEX_LOCK(raop_ntp->wait_mutex); + clock_gettime(CLOCK_REALTIME, &wait_time); + wait_time.tv_sec += 3; + pthread_cond_timedwait(&raop_ntp->wait_cond, &raop_ntp->wait_mutex, &wait_time); + MUTEX_UNLOCK(raop_ntp->wait_mutex); + } + + // Ensure running reflects the actual state + MUTEX_LOCK(raop_ntp->run_mutex); + raop_ntp->running = false; + MUTEX_UNLOCK(raop_ntp->run_mutex); + + logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp exiting thread"); + if (conn_reset && raop_ntp->callbacks.conn_reset) { + const bool video_reset = false; /* leave "frozen video" in place */ + raop_ntp->callbacks.conn_reset(raop_ntp->callbacks.cls, timeout_counter, video_reset); + } + return 0; +} + +void +raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_timeouts) +{ + logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp starting time"); + int use_ipv6 = 0; + + assert(raop_ntp); + assert(timing_lport); + + raop_ntp->max_ntp_timeouts = max_ntp_timeouts; + raop_ntp->timing_lport = *timing_lport; + + MUTEX_LOCK(raop_ntp->run_mutex); + if (raop_ntp->running || !raop_ntp->joined) { + MUTEX_UNLOCK(raop_ntp->run_mutex); + return; + } + + /* Initialize ports and sockets */ + if (raop_ntp->remote_saddr.ss_family == AF_INET6) { + use_ipv6 = 1; + } + //use_ipv6 = 0; + if (raop_ntp_init_socket(raop_ntp, use_ipv6) < 0) { + logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp initializing timing socket failed"); + MUTEX_UNLOCK(raop_ntp->run_mutex); + return; + } + *timing_lport = raop_ntp->timing_lport; + + /* Create the thread and initialize running values */ + raop_ntp->running = 1; + raop_ntp->joined = 0; + + THREAD_CREATE(raop_ntp->thread, raop_ntp_thread, raop_ntp); + MUTEX_UNLOCK(raop_ntp->run_mutex); +} + +void +raop_ntp_stop(raop_ntp_t *raop_ntp) +{ + assert(raop_ntp); + + /* Check that we are running and thread is not + * joined (should never be while still running) */ + MUTEX_LOCK(raop_ntp->run_mutex); + if (!raop_ntp->running || raop_ntp->joined) { + MUTEX_UNLOCK(raop_ntp->run_mutex); + return; + } + raop_ntp->running = 0; + MUTEX_UNLOCK(raop_ntp->run_mutex); + + logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopping time thread"); + + MUTEX_LOCK(raop_ntp->wait_mutex); + COND_SIGNAL(raop_ntp->wait_cond); + MUTEX_UNLOCK(raop_ntp->wait_mutex); + + if (raop_ntp->tsock != -1) { + closesocket(raop_ntp->tsock); + raop_ntp->tsock = -1; + } + + THREAD_JOIN(raop_ntp->thread); + + logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopped time thread"); + + /* Mark thread as joined */ + MUTEX_LOCK(raop_ntp->run_mutex); + raop_ntp->joined = 1; + MUTEX_UNLOCK(raop_ntp->run_mutex); +} + +/** + * Converts from a little endian ntp timestamp to nano seconds since the Unix epoch. + * Does the same thing as byteutils_get_ntp_timestamp, except its input is an uint64_t + * and expected to already be in little endian. + * Please note this just converts to a different representation, the clock remains the + * same. + */ +uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) { + uint64_t seconds = ((ntp_timestamp >> 32) & 0xffffffff) - (account_for_epoch_diff ? SECONDS_FROM_1900_TO_1970 : 0); + uint64_t fraction = (ntp_timestamp & 0xffffffff); + return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32); +} + +uint64_t raop_remote_timestamp_to_nano_seconds(raop_ntp_t *raop_ntp, uint64_t timestamp) { + uint64_t seconds = ((timestamp >> 32) & 0xffffffff); + if (raop_ntp->time_protocol == NTP) seconds -= SECONDS_FROM_1900_TO_1970; + uint64_t fraction = (timestamp & 0xffffffff); + return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32); +} +/** + * Returns the current time in nano seconds according to the local wall clock. + * The system Unix time is used as the local wall clock. + */ +uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp) { + struct timespec time; + clock_gettime(CLOCK_REALTIME, &time); + return ((uint64_t) time.tv_nsec) + (uint64_t) time.tv_sec * SECOND_IN_NSECS; +} + +/** + * Returns the current time in nano seconds according to the remote wall clock. + */ +uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp) { + MUTEX_LOCK(raop_ntp->sync_params_mutex); + int64_t offset = raop_ntp->sync_offset; + MUTEX_UNLOCK(raop_ntp->sync_params_mutex); + return (uint64_t) ((int64_t) raop_ntp_get_local_time(raop_ntp) + offset); +} + +/** + * Returns the local wall clock time in nano seconds for the given point in remote clock time + */ +uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time) { + MUTEX_LOCK(raop_ntp->sync_params_mutex); + int64_t offset = raop_ntp->sync_offset; + MUTEX_UNLOCK(raop_ntp->sync_params_mutex); + return (uint64_t) ((int64_t) remote_time - offset); +} + +/** + * Returns the remote wall clock time in nano seconds for the given point in local clock time + */ +uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time) { + MUTEX_LOCK(raop_ntp->sync_params_mutex); + int64_t offset = raop_ntp->sync_offset; + MUTEX_UNLOCK(raop_ntp->sync_params_mutex); + return (uint64_t) ((int64_t) local_time + offset); +} diff --git a/lib/raop_ntp.h b/lib/raop_ntp.h new file mode 100644 index 0000000..bf2f5bb --- /dev/null +++ b/lib/raop_ntp.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 dsafa22, modified by Florian Draschbacher, + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2021-2023 + */ + +#ifndef RAOP_NTP_H +#define RAOP_NTP_H + +#include +#include +#include "logger.h" + +typedef struct raop_ntp_s raop_ntp_t; + +typedef enum timing_protocol_e { NTP, TP_NONE, TP_OTHER, TP_UNSPECIFIED } timing_protocol_t; + +void raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_timeouts); + +void raop_ntp_stop(raop_ntp_t *raop_ntp); + +unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp); + +void raop_ntp_destroy(raop_ntp_t *raop_rtp); + +uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff); +uint64_t raop_remote_timestamp_to_nano_seconds(raop_ntp_t *raop_ntp, uint64_t timestamp); + +uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp); +uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp); +uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time); +uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time); + +#endif //RAOP_NTP_H diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c new file mode 100644 index 0000000..62543f7 --- /dev/null +++ b/lib/raop_rtp.c @@ -0,0 +1,920 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2021-2023 + */ + +#include +#include +#include +#include +#include +#include + +#include "raop_rtp.h" +#include "raop.h" +#include "raop_buffer.h" +#include "netutils.h" +#include "compat.h" +#include "logger.h" +#include "byteutils.h" +#include "mirror_buffer.h" +#include "stream.h" +#include "utils.h" + +#define NO_FLUSH (-42) + +#define SECOND_IN_NSECS 1000000000 +#define RAOP_RTP_SYNC_DATA_COUNT 8 +#define SEC SECOND_IN_NSECS + +#define DELAY_AAC 0.275 //empirical, matches audio latency of about -0.25 sec after first clock sync event + +/* note: it is unclear what will happen in the unlikely event that this code is running at the time of the unix-time + * epoch event on 2038-01-19 at 3:14:08 UTC ! (but Apple will surely have removed AirPlay "legacy pairing" by then!) */ + +typedef struct raop_rtp_sync_data_s { + uint64_t ntp_time; // The local wall clock time (unix time in usec) at the time of rtp_time + uint64_t rtp_time; // The remote rtp clock time corresponding to ntp_time +} raop_rtp_sync_data_t; + +struct raop_rtp_s { + logger_t *logger; + raop_callbacks_t callbacks; + + // Time and sync + raop_ntp_t *ntp; + double rtp_clock_rate; + int64_t rtp_sync_offset; + raop_rtp_sync_data_t sync_data[RAOP_RTP_SYNC_DATA_COUNT]; + int sync_data_index; + uint64_t ntp_start_time; + uint64_t rtp_start_time; + uint64_t rtp_time; + bool rtp_clock_started; + + // Transmission Stats, could be used if a playout buffer is needed + // float interarrival_jitter; // As defined by RTP RFC 3550, Section 6.4.1 + // unsigned int last_packet_transit_time; + //int transit = (packet_receive_time - packet_send_time); + // int d = transit - last_packet_transit_time; + // if (d < 0) d = -d; + // interarrival_jitter = (1.f / 16.f) * ((double) d - interarrival_jitter); + + /* Buffer to handle all resends */ + raop_buffer_t *buffer; + + /* Remote address as sockaddr */ + struct sockaddr_storage remote_saddr; + socklen_t remote_saddr_len; + + /* MUTEX LOCKED VARIABLES START */ + /* These variables only edited mutex locked */ + int running; + int joined; + + float volume; + int volume_changed; + unsigned char *metadata; + int metadata_len; + unsigned char *coverart; + int coverart_len; + char *dacp_id; + char *active_remote_header; + unsigned int progress_start; + unsigned int progress_curr; + unsigned int progress_end; + int progress_changed; + + int flush; + thread_handle_t thread; + mutex_handle_t run_mutex; + /* MUTEX LOCKED VARIABLES END */ + + /* Remote control and timing ports */ + unsigned short control_rport; + + /* Sockets for control and data */ + int csock, dsock; + + /* Local control, timing and data ports */ + unsigned short control_lport; + unsigned short data_lport; + + /* Initialized after the first control packet */ + struct sockaddr_storage control_saddr; + socklen_t control_saddr_len; + unsigned short control_seqnum; + + /* audio compression type: ct = 2 (ALAC), ct = 8 (AAC_ELD) (ct = 4 would be AAC-MAIN) */ + unsigned char ct; +}; + +static int +raop_rtp_parse_remote(raop_rtp_t *raop_rtp, const char *remote, int remotelen) +{ + int family; + int ret; + assert(raop_rtp); + if (remotelen == 4) { + family = AF_INET; + } else if (remotelen == 16) { + family = AF_INET6; + } else { + return -1; + } + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp parse remote ip = %s", remote); + ret = netutils_parse_address(family, remote, + &raop_rtp->remote_saddr, + sizeof(raop_rtp->remote_saddr)); + if (ret < 0) { + return -1; + } + raop_rtp->remote_saddr_len = ret; + return 0; +} + +raop_rtp_t * +raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const char *remote, + int remotelen, const unsigned char *aeskey, const unsigned char *aesiv) +{ + raop_rtp_t *raop_rtp; + + assert(logger); + assert(callbacks); + + raop_rtp = calloc(1, sizeof(raop_rtp_t)); + if (!raop_rtp) { + return NULL; + } + raop_rtp->logger = logger; + raop_rtp->ntp = ntp; + + raop_rtp->rtp_sync_offset = 0; + raop_rtp->sync_data_index = 0; + for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; ++i) { + raop_rtp->sync_data[i].ntp_time = 0; + raop_rtp->sync_data[i].rtp_time = 0; + } + raop_rtp->ntp_start_time = 0; + raop_rtp->rtp_start_time = 0; + raop_rtp->rtp_clock_started = false; + + + raop_rtp->dacp_id = NULL; + raop_rtp->active_remote_header = NULL; + raop_rtp->metadata = NULL; + raop_rtp->coverart = NULL; + + memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t)); + raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv); + if (!raop_rtp->buffer) { + free(raop_rtp); + return NULL; + } + if (raop_rtp_parse_remote(raop_rtp, remote, remotelen) < 0) { + free(raop_rtp); + return NULL; + } + + raop_rtp->running = 0; + raop_rtp->joined = 1; + raop_rtp->flush = NO_FLUSH; + + MUTEX_CREATE(raop_rtp->run_mutex); + return raop_rtp; +} + + +void +raop_rtp_destroy(raop_rtp_t *raop_rtp) +{ + if (raop_rtp) { + raop_rtp_stop(raop_rtp); + MUTEX_DESTROY(raop_rtp->run_mutex); + raop_buffer_destroy(raop_rtp->buffer); + free(raop_rtp->metadata); + free(raop_rtp->coverart); + free(raop_rtp->dacp_id); + free(raop_rtp->active_remote_header); + free(raop_rtp); + } +} + +static int +raop_rtp_resend_callback(void *opaque, unsigned short seqnum, unsigned short count) +{ + raop_rtp_t *raop_rtp = opaque; + unsigned char packet[8]; + unsigned short ourseqnum; + struct sockaddr *addr; + socklen_t addrlen; + int ret; + + addr = (struct sockaddr *)&raop_rtp->control_saddr; + addrlen = raop_rtp->control_saddr_len; + + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp got resend request %d %d", seqnum, count); + ourseqnum = raop_rtp->control_seqnum++; + + /* Fill the request buffer */ + packet[0] = 0x80; + packet[1] = 0x55|0x80; + packet[2] = (ourseqnum >> 8); + packet[3] = ourseqnum; + packet[4] = (seqnum >> 8); + packet[5] = seqnum; + packet[6] = (count >> 8); + packet[7] = count; + + ret = sendto(raop_rtp->csock, (const char *)packet, sizeof(packet), 0, addr, addrlen); + if (ret == -1) { + logger_log(raop_rtp->logger, LOGGER_WARNING, "raop_rtp resend failed: %d", SOCKET_GET_ERROR()); + } + + return 0; +} + +static int +raop_rtp_init_sockets(raop_rtp_t *raop_rtp, int use_ipv6) +{ + assert(raop_rtp); + + unsigned short cport = raop_rtp->control_lport; + unsigned short dport = raop_rtp->data_lport; + int csock = netutils_init_socket(&cport, use_ipv6, 1); + int dsock = netutils_init_socket(&dport, use_ipv6, 1); + + if (csock == -1 || dsock == -1) { + goto sockets_cleanup; + } + + /* Set socket descriptors */ + raop_rtp->csock = csock; + raop_rtp->dsock = dsock; + + /* Set port values */ + raop_rtp->control_lport = cport; + raop_rtp->data_lport = dport; + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp local control port socket %d port UDP %d", csock, cport); + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp local data port socket %d port UDP %d", dsock, dport); + return 0; + + sockets_cleanup: + if (csock != -1) closesocket(csock); + if (dsock != -1) closesocket(dsock); + return -1; +} + +static int +raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data) +{ + int flush; + float volume; + int volume_changed; + unsigned char *metadata; + int metadata_len; + unsigned char *coverart; + int coverart_len; + char *dacp_id; + char *active_remote_header; + unsigned int progress_start; + unsigned int progress_curr; + unsigned int progress_end; + int progress_changed; + + assert(raop_rtp); + + MUTEX_LOCK(raop_rtp->run_mutex); + if (!raop_rtp->running) { + MUTEX_UNLOCK(raop_rtp->run_mutex); + return 1; + } + + /* Read the volume level */ + volume = raop_rtp->volume; + volume_changed = raop_rtp->volume_changed; + raop_rtp->volume_changed = 0; + + /* Read the flush value */ + flush = raop_rtp->flush; + raop_rtp->flush = NO_FLUSH; + + /* Read the metadata */ + metadata = raop_rtp->metadata; + metadata_len = raop_rtp->metadata_len; + raop_rtp->metadata = NULL; + raop_rtp->metadata_len = 0; + + /* Read the coverart */ + coverart = raop_rtp->coverart; + coverart_len = raop_rtp->coverart_len; + raop_rtp->coverart = NULL; + raop_rtp->coverart_len = 0; + + /* Read DACP remote control data */ + dacp_id = raop_rtp->dacp_id; + active_remote_header = raop_rtp->active_remote_header; + raop_rtp->dacp_id = NULL; + raop_rtp->active_remote_header = NULL; + + /* Read the progress values */ + progress_start = raop_rtp->progress_start; + progress_curr = raop_rtp->progress_curr; + progress_end = raop_rtp->progress_end; + progress_changed = raop_rtp->progress_changed; + raop_rtp->progress_changed = 0; + + MUTEX_UNLOCK(raop_rtp->run_mutex); + + /* Call set_volume callback if changed */ + if (volume_changed) { + //raop_buffer_flush(raop_rtp->buffer, flush); /* seems to be unnecessary, may cause audio artefacts */ + if (raop_rtp->callbacks.audio_set_volume) { + raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, volume); + } + } + + /* Handle flush if requested */ + if (flush != NO_FLUSH) { + if (raop_rtp->callbacks.audio_flush) { + raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls); + } + } + + if (metadata != NULL) { + if (raop_rtp->callbacks.audio_set_metadata) { + raop_rtp->callbacks.audio_set_metadata(raop_rtp->callbacks.cls, metadata, metadata_len); + } + free(metadata); + metadata = NULL; + } + + if (coverart != NULL) { + if (raop_rtp->callbacks.audio_set_coverart) { + raop_rtp->callbacks.audio_set_coverart(raop_rtp->callbacks.cls, coverart, coverart_len); + } + free(coverart); + coverart = NULL; + } + if (dacp_id && active_remote_header) { + if (raop_rtp->callbacks.audio_remote_control_id) { + raop_rtp->callbacks.audio_remote_control_id(raop_rtp->callbacks.cls, dacp_id, active_remote_header); + } + free(dacp_id); + free(active_remote_header); + dacp_id = NULL; + active_remote_header = NULL; + } + + if (progress_changed) { + if (raop_rtp->callbacks.audio_set_progress) { + raop_rtp->callbacks.audio_set_progress(raop_rtp->callbacks.cls, progress_start, progress_curr, progress_end); + } + } + return 0; +} + +void raop_rtp_sync_clock(raop_rtp_t *raop_rtp, uint64_t *ntp_time, uint64_t *rtp_time) { + /* ntp_time = (uint64_t)(((int64_t)(raop_rtp->rtp_clock_rate * rtp_time)) + raop_rtp->rtp_sync_offset) */ + int latest, valid_data_count = 0; + uint64_t ntp_sum = 0, rtp_sum = 0; + double offset = ((double) *ntp_time) - raop_rtp->rtp_clock_rate * *rtp_time; + int64_t correction = 0; + + raop_rtp->sync_data_index = (raop_rtp->sync_data_index + 1) % RAOP_RTP_SYNC_DATA_COUNT; + latest = raop_rtp->sync_data_index; + raop_rtp->sync_data[latest].rtp_time = *rtp_time; + raop_rtp->sync_data[latest].ntp_time = *ntp_time; + + for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; i++) { + if (raop_rtp->sync_data[i].ntp_time == 0) continue; + valid_data_count++; + if (i == latest) continue; + ntp_sum += *ntp_time - raop_rtp->sync_data[i].ntp_time; + rtp_sum += *rtp_time - raop_rtp->sync_data[i].rtp_time; + } + + if (valid_data_count > 1) { + correction -= raop_rtp->rtp_sync_offset; + offset += (((double) ntp_sum) - raop_rtp->rtp_clock_rate * rtp_sum) / valid_data_count; + } + raop_rtp->rtp_sync_offset = (int64_t) offset; + correction += raop_rtp->rtp_sync_offset; + + logger_log(raop_rtp->logger, LOGGER_DEBUG, "dataset %d raop_rtp sync correction=%lld, rtp_sync_offset = %lld ", + valid_data_count, correction, raop_rtp->rtp_sync_offset); +} + +uint64_t rtp64_time (raop_rtp_t *raop_rtp, const uint32_t *rtp32) { + /* convert from 32-bit to 64-bit rtp time: + * the rtp_time 32-bit epoch at 44.1kHz has length of about 27 hours + * using 64-bit rtp time avoids any epoch issues. + * initial call sets epoch to 1; subsequent calls maintain consistent epoch. + * (assumes successive calls are close in time) */ + + if (raop_rtp->rtp_clock_started) { + uint32_t diff1 = *rtp32 - ((uint32_t) raop_rtp->rtp_time); + uint32_t diff2 = ((uint32_t) raop_rtp->rtp_time) - *rtp32; + if (diff1 <= diff2) { + raop_rtp->rtp_time += (uint64_t) diff1; + } else { + raop_rtp->rtp_time -= (uint64_t) diff2; + } + } else { + raop_rtp->rtp_time = (0x01ULL << 32 ) + (uint64_t) *rtp32; + raop_rtp->rtp_start_time = raop_rtp->rtp_time; + raop_rtp->rtp_clock_started = true; + } + //assert(*rtp32 == (uint32_t) raop_rtp->rtp_time); + return raop_rtp->rtp_time; +} + +static THREAD_RETVAL +raop_rtp_thread_udp(void *arg) +{ + raop_rtp_t *raop_rtp = arg; + unsigned char packet[RAOP_PACKET_LEN]; + unsigned int packetlen; + struct sockaddr_storage saddr; + socklen_t saddrlen; + bool got_remote_control_saddr = false; + + /* for initial rtp to ntp conversions */ + bool have_synced = false; + bool no_data_yet = true; + unsigned char no_data_marker[] = {0x00, 0x68, 0x34, 0x00 }; + int rtp_count = 0; + double sync_adjustment = 0; + unsigned short seqnum1 = 0, seqnum2 = 0; + + assert(raop_rtp); + bool logger_debug = (logger_get_level(raop_rtp->logger) >= LOGGER_DEBUG); + raop_rtp->ntp_start_time = raop_ntp_get_local_time(raop_rtp->ntp); + raop_rtp->rtp_clock_started = false; + for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; i++) { + raop_rtp->sync_data[i].ntp_time = 0; + } + + int no_resend = (raop_rtp->control_rport == 0); /* true when control_rport is not set */ + + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp start_time = %8.6f (raop_rtp audio)", + ((double) raop_rtp->ntp_start_time) / SEC); + + while(1) { + fd_set rfds; + struct timeval tv; + int nfds, ret; + /* Check if we are still running and process callbacks */ + if (raop_rtp_process_events(raop_rtp, NULL)) { + break; + } + + /* Set timeout value to 5ms */ + tv.tv_sec = 0; + tv.tv_usec = 5000; + + /* Get the correct nfds value */ + nfds = raop_rtp->csock+1; + if (raop_rtp->dsock >= nfds) + nfds = raop_rtp->dsock+1; + + /* Set rfds and call select */ + FD_ZERO(&rfds); + FD_SET(raop_rtp->csock, &rfds); + FD_SET(raop_rtp->dsock, &rfds); + + ret = select(nfds, &rfds, NULL, NULL, &tv); + if (ret == 0) { + /* Timeout happened */ + continue; + } else if (ret == -1) { + logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp error in select"); + break; + } + + if (FD_ISSET(raop_rtp->csock, &rfds)) { + if (got_remote_control_saddr== false) { + saddrlen = sizeof(saddr); + packetlen = recvfrom(raop_rtp->csock, (char *)packet, sizeof(packet), 0, + (struct sockaddr *)&saddr, &saddrlen); + if (packetlen > 0) { + memcpy(&raop_rtp->control_saddr, &saddr, saddrlen); + raop_rtp->control_saddr_len = saddrlen; + got_remote_control_saddr = true; + } + } else { + packetlen = recvfrom(raop_rtp->csock, (char *)packet, sizeof(packet), 0, NULL, NULL); + } + int type_c = packet[1] & ~0x80; + logger_log(raop_rtp->logger, LOGGER_DEBUG, "\nraop_rtp type_c 0x%02x, packetlen = %d", type_c, packetlen); + + if (type_c == 0x56 && packetlen >= 8) { + /* Handle resent data packet, which begins at offset 4 of these packets */ + unsigned char *resent_packet = &packet[4]; + unsigned int resent_packetlen = packetlen - 4; + unsigned short seqnum = byteutils_get_short_be(resent_packet, 2); + if (resent_packetlen >= 12) { + uint32_t timestamp = byteutils_get_int_be(resent_packet, 4); + uint64_t rtp_time = rtp64_time(raop_rtp, ×tamp); + uint64_t ntp_time = 0; + if (have_synced) { + ntp_time = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp_time)); + } + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp resent audio packet: seqnum=%u", seqnum); + int result = raop_buffer_enqueue(raop_rtp->buffer, resent_packet, resent_packetlen, &ntp_time, &rtp_time, 1); + assert(result >= 0); + } else if (logger_debug) { + /* type_c = 0x56 packets with length 8 have been reported */ + char *str = utils_data_to_string(packet, packetlen, 16); + logger_log(raop_rtp->logger, LOGGER_DEBUG, "Received empty resent audio packet length %d, seqnum=%u:\n%s", + packetlen, seqnum, str); + free (str); + } + } else if (type_c == 0x54 && packetlen >= 20) { + /* packet[0] = 0x90 (first sync ?) or 0x80 (subsequent ones) + * packet[1] = 0xd4, (0xd4 && ~0x80 = type 0x54) + * packet[2:3] = 0x00 0x04 + * packet[4:7] : sync_rtp (big-endian uint32_t) + * packet[8:15]: remote ntp timestamp (big-endian uint64_t) + * packet[16:20]: next_rtp (big-endian uint32_t) + * next_rtp = sync_rtp + 7497 = 441 * 17 (0.17 sec) for AAC-ELD + * next_rtp = sync_rtp + 77175 = 441 * 175 (1.75 sec) for ALAC */ + + // The unit for the rtp clock is 1 / sample rate = 1 / 44100 + uint32_t sync_rtp = byteutils_get_int_be(packet, 4); + uint64_t sync_rtp64 = rtp64_time(raop_rtp, &sync_rtp); + if (have_synced == false) { + logger_log(raop_rtp->logger, LOGGER_DEBUG, "first audio rtp sync"); + have_synced = true; + } + uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8); + uint64_t sync_ntp_remote = raop_remote_timestamp_to_nano_seconds(raop_rtp->ntp, sync_ntp_raw); + if (logger_debug) { + uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote); + char *str = utils_data_to_string(packet, packetlen, 20); + logger_log(raop_rtp->logger, LOGGER_DEBUG, + "raop_rtp sync: client ntp=%8.6f, ntp = %8.6f, ntp_start_time %8.6f\nts_client = %8.6f sync_rtp=%u\n%s", + (double) sync_ntp_remote / SEC, (double) sync_ntp_local / SEC, + (double) raop_rtp->ntp_start_time / SEC, (double) sync_ntp_remote / SEC, sync_rtp, str); + free(str); + } + raop_rtp_sync_clock(raop_rtp, &sync_ntp_remote, &sync_rtp64); + } else if (logger_debug) { + char *str = utils_data_to_string(packet, packetlen, 16); + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp unknown udp control packet\n%s", str); + free(str); + } + } + + /* rtp audio data packets: + * packet[0] 0x80 + * packet[1] 0x60 = 96 + * packet[2:3] seqnum (big-endian unsigned short) + * packet[4:7] rtp timestamp (big-endian unsigned int) + * packet[8:11] 0x00 0x00 0x00 0x00 + * packet[12:packetlen - 1] encrypted audio payload + * For (AAC-ELD only), the payload of initial packets at the start of + * the stream may be replaced by a 4-byte "no_data_marker" 0x00 0x68 0x34 0x00 */ + + /* consecutive AAC-ELD rtp timestamps differ by spf = 480 + * consecutive ALAC rtp timestamps differ by spf = 352 + * both have PCM uncompressed sampling rate = 441000 Hz */ + + /* clock time in microseconds advances at (rtp_timestamp * 1000000)/44100 between frames */ + + /* every AAC-ELD packet is sent three times: 0 0 1 0 1 2 1 2 3 2 3 4 ... + * (after decoding AAC-ELD into PCM, the sound frame is three times bigger) + * ALAC packets are sent once only 0 1 2 3 4 5 ... */ + + /* When the AAC-ELD audio stream starts, the initial packets are length-16 packets with + * a four-byte "no_data_marker" 0x00 0x68 0x34 0x00 replacing the payload. + * The 12-byte packetheader contains a secnum and rtp_timestamp, and each packets is sent + * three times; the secnum and rtp_timestamp increment according to the same pattern as + * AAC-ELD packets with audio content.*/ + + /* When the ALAC audio stream starts, the initial packets are length-44 packets with + * the same 32-byte encrypted payload which after decryption is the beginning of a + * 32-byte ALAC packet, presumably with format information, but not actual audio data. + * The secnum and rtp_timestamp in the packet header increment according to the same + * pattern as ALAC packets with audio content */ + + /* The first ALAC packet with data seems to be decoded just before the first sync event + * so its dequeuing should be delayed until the first rtp sync has occurred */ + + + if (FD_ISSET(raop_rtp->dsock, &rfds)) { + //logger_log(raop_rtp->logger, LOGGER_INFO, "Would have data packet in queue"); + // Receiving audio data here + saddrlen = sizeof(saddr); + packetlen = recvfrom(raop_rtp->dsock, (char *)packet, sizeof(packet), 0, NULL, NULL); + // rtp payload type + //int type_d = packet[1] & ~0x80; + //logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp_thread_udp type_d 0x%02x, packetlen = %d", type_d, packetlen); + + if (packetlen < 12) { + if (logger_debug) { + char *str = utils_data_to_string(packet, packetlen, 16); + logger_log(raop_rtp->logger, LOGGER_DEBUG, "Received short type_d = 0x%2x packet with length %d:\n%s", + packet[1] & ~0x80, packetlen, str); + free (str); + } + continue; + } + + uint32_t rtp_timestamp = byteutils_get_int_be(packet, 4); + uint64_t rtp_time = rtp64_time(raop_rtp, &rtp_timestamp); + uint64_t ntp_time = 0; + + if (raop_rtp->ct == 2 && packetlen == 44) continue; /* ignore the ALAC packets with format information only. */ + + if (have_synced) { + ntp_time = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp_time)); + } else if (packetlen == 16 && memcmp(packet + 12, no_data_marker, 4) == 0) { + /* use the special "no_data" packet to help determine an initial offset before the first rtp sync. + * until the first rtp sync occurs, we don't know the exact client ntp timestamp that matches the client rtp timestamp */ + if (no_data_yet) { + int64_t sync_ntp = ((int64_t) raop_ntp_get_local_time(raop_rtp->ntp)) - ((int64_t) raop_rtp->ntp_start_time) ; + int64_t sync_rtp = ((int64_t) rtp_time) - ((int64_t) raop_rtp->rtp_start_time); + unsigned short seqnum = byteutils_get_short_be(packet, 2); + if (rtp_count == 0) { + sync_adjustment = ((double) sync_ntp); + rtp_count = 1; + seqnum1 = seqnum; + seqnum2 = seqnum; + } + if (seqnum2 != seqnum) { /* for AAC-ELD only use copy 1 of the 3 copies of each frame */ + rtp_count++; + sync_adjustment += (((double) sync_ntp) - raop_rtp->rtp_clock_rate * sync_rtp - sync_adjustment) / rtp_count; + } + seqnum2 = seqnum1; + seqnum1 = seqnum; + } + continue; + } else { + no_data_yet = false; + } + int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, &ntp_time, &rtp_time, 1); + assert(result >= 0); + + if (raop_rtp->ct == 2 && !have_synced) { + /* in ALAC Audio-only mode wait until the first sync before dequeing */ + continue; + } else { + // Render continuous buffer entries + void *payload = NULL; + unsigned int payload_size; + unsigned short seqnum; + uint64_t rtp64_timestamp; + uint64_t ntp_timestamp; + + while ((payload = raop_buffer_dequeue(raop_rtp->buffer, &payload_size, &ntp_timestamp, &rtp64_timestamp, &seqnum, no_resend))) { + audio_decode_struct audio_data; + audio_data.rtp_time = rtp64_timestamp; + audio_data.seqnum = seqnum; + audio_data.data_len = payload_size; + audio_data.data = payload; + audio_data.ct = raop_rtp->ct; + if (have_synced) { + if (ntp_timestamp == 0) { + ntp_timestamp = (uint64_t) (raop_rtp->rtp_sync_offset + (int64_t) (raop_rtp->rtp_clock_rate * rtp64_timestamp)); + } + audio_data.ntp_time_remote = ntp_timestamp; + audio_data.ntp_time_local = raop_ntp_convert_remote_time(raop_rtp->ntp, audio_data.ntp_time_remote); + audio_data.sync_status = 1; + } else { + double elapsed_time = raop_rtp->rtp_clock_rate * (rtp64_timestamp - raop_rtp->rtp_start_time) + sync_adjustment + + DELAY_AAC * SECOND_IN_NSECS; + audio_data.ntp_time_local = raop_rtp->ntp_start_time + (uint64_t) elapsed_time; + audio_data.ntp_time_remote = raop_ntp_convert_local_time(raop_rtp->ntp, audio_data.ntp_time_local); + audio_data.sync_status = 0; + } + raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, raop_rtp->ntp, &audio_data); + free(payload); + if (logger_debug) { + uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp); + int64_t latency = ((int64_t) ntp_now) - ((int64_t) audio_data.ntp_time_local); + logger_log(raop_rtp->logger, LOGGER_DEBUG, + "raop_rtp audio: now = %8.6f, ntp = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u", + (double) ntp_now / SEC, (double) audio_data.ntp_time_local / SEC, (double) latency / SEC, + (uint32_t) rtp64_timestamp, seqnum); + } + } + + /* Handle possible resend requests */ + if (!no_resend) { + raop_buffer_handle_resends(raop_rtp->buffer, raop_rtp_resend_callback, raop_rtp); + } + } + } + } + + // Ensure running reflects the actual state + MUTEX_LOCK(raop_rtp->run_mutex); + raop_rtp->running = false; + MUTEX_UNLOCK(raop_rtp->run_mutex); + + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp exiting thread"); + + return 0; +} + +// Start rtp service, using two udp ports +void +raop_rtp_start_audio(raop_rtp_t *raop_rtp, unsigned short *control_rport, unsigned short *control_lport, + unsigned short *data_lport, unsigned char *ct, unsigned int *sr) +{ + logger_log(raop_rtp->logger, LOGGER_INFO, "raop_rtp starting audio"); + int use_ipv6 = 0; + + assert(raop_rtp); + + MUTEX_LOCK(raop_rtp->run_mutex); + if (raop_rtp->running || !raop_rtp->joined) { + MUTEX_UNLOCK(raop_rtp->run_mutex); + return; + } + + raop_rtp->ct = *ct; + raop_rtp->rtp_clock_rate = SECOND_IN_NSECS / *sr; + + /* Initialize ports and sockets */ + raop_rtp->control_lport = *control_lport; + raop_rtp->data_lport = *data_lport; + raop_rtp->control_rport = *control_rport; + if (raop_rtp->remote_saddr.ss_family == AF_INET6) { + use_ipv6 = 1; + } + //use_ipv6 = 0; + if (raop_rtp_init_sockets(raop_rtp, use_ipv6) < 0) { + logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp initializing sockets failed"); + MUTEX_UNLOCK(raop_rtp->run_mutex); + return; + } + *control_lport = raop_rtp->control_lport; + *data_lport = raop_rtp->data_lport; + /* Create the thread and initialize running values */ + raop_rtp->running = 1; + raop_rtp->joined = 0; + + THREAD_CREATE(raop_rtp->thread, raop_rtp_thread_udp, raop_rtp); + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +void +raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume) +{ + assert(raop_rtp); + + if (volume > 0.0f) { + volume = 0.0f; + } else if (volume < -144.0f) { + volume = -144.0f; + } + + /* Set volume in thread instead */ + MUTEX_LOCK(raop_rtp->run_mutex); + raop_rtp->volume = volume; + raop_rtp->volume_changed = 1; + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +void +raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen) +{ + unsigned char *metadata; + + assert(raop_rtp); + + if (datalen <= 0) { + return; + } + metadata = malloc(datalen); + assert(metadata); + memcpy(metadata, data, datalen); + + /* Set metadata in thread instead */ + MUTEX_LOCK(raop_rtp->run_mutex); + raop_rtp->metadata = metadata; + raop_rtp->metadata_len = datalen; + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +void +raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen) +{ + unsigned char *coverart; + + assert(raop_rtp); + + if (datalen <= 0) { + return; + } + coverart = malloc(datalen); + assert(coverart); + memcpy(coverart, data, datalen); + + /* Set coverart in thread instead */ + MUTEX_LOCK(raop_rtp->run_mutex); + raop_rtp->coverart = coverart; + raop_rtp->coverart_len = datalen; + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +void +raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header) +{ + assert(raop_rtp); + + if (!dacp_id || !active_remote_header) { + return; + } + + /* Set dacp stuff in thread instead */ + MUTEX_LOCK(raop_rtp->run_mutex); + if (raop_rtp->dacp_id) { + free(raop_rtp->dacp_id); + } + raop_rtp->dacp_id = strdup(dacp_id); + if (raop_rtp->active_remote_header) { + free(raop_rtp->active_remote_header); + } + raop_rtp->active_remote_header = strdup(active_remote_header); + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +void +raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end) +{ + assert(raop_rtp); + + /* Set progress in thread instead */ + MUTEX_LOCK(raop_rtp->run_mutex); + raop_rtp->progress_start = start; + raop_rtp->progress_curr = curr; + raop_rtp->progress_end = end; + raop_rtp->progress_changed = 1; + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +void +raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq) +{ + assert(raop_rtp); + + /* Call flush in thread instead */ + MUTEX_LOCK(raop_rtp->run_mutex); + raop_rtp->flush = next_seq; + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +void +raop_rtp_stop(raop_rtp_t *raop_rtp) +{ + assert(raop_rtp); + + /* Check that we are running and thread is not + * joined (should never be while still running) */ + MUTEX_LOCK(raop_rtp->run_mutex); + if (!raop_rtp->running || raop_rtp->joined) { + MUTEX_UNLOCK(raop_rtp->run_mutex); + return; + } + raop_rtp->running = 0; + MUTEX_UNLOCK(raop_rtp->run_mutex); + + /* Join the thread */ + THREAD_JOIN(raop_rtp->thread); + + if (raop_rtp->csock != -1) closesocket(raop_rtp->csock); + if (raop_rtp->dsock != -1) closesocket(raop_rtp->dsock); + + /* Flush buffer into initial state */ + raop_buffer_flush(raop_rtp->buffer, -1); + + /* Mark thread as joined */ + MUTEX_LOCK(raop_rtp->run_mutex); + raop_rtp->joined = 1; + MUTEX_UNLOCK(raop_rtp->run_mutex); +} + +int +raop_rtp_is_running(raop_rtp_t *raop_rtp) +{ + assert(raop_rtp); + MUTEX_LOCK(raop_rtp->run_mutex); + int running = raop_rtp->running; + MUTEX_UNLOCK(raop_rtp->run_mutex); + return running; +} diff --git a/lib/raop_rtp.h b/lib/raop_rtp.h new file mode 100644 index 0000000..e1276bd --- /dev/null +++ b/lib/raop_rtp.h @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2021-2023 + */ + +#ifndef RAOP_RTP_H +#define RAOP_RTP_H + +/* For raop_callbacks_t */ +#include "raop.h" +#include "logger.h" +#include "raop_ntp.h" + +#define RAOP_AESIV_LEN 16 +#define RAOP_AESKEY_LEN 16 +#define RAOP_PACKET_LEN 32768 + +typedef struct raop_rtp_s raop_rtp_t; + +raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const char *remote, + int remotelen, const unsigned char *aeskey, const unsigned char *aesiv); + +void raop_rtp_start_audio(raop_rtp_t *raop_rtp, unsigned short *control_rport, unsigned short *control_lport, + unsigned short *data_lport, unsigned char *ct, unsigned int *sr); + +void raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume); +void raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen); +void raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen); +void raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header); +void raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end); +void raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq); +void raop_rtp_stop(raop_rtp_t *raop_rtp); +int raop_rtp_is_running(raop_rtp_t *raop_rtp); +void raop_rtp_destroy(raop_rtp_t *raop_rtp); + +#endif diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c new file mode 100644 index 0000000..bc88472 --- /dev/null +++ b/lib/raop_rtp_mirror.c @@ -0,0 +1,923 @@ +/* + * Copyright (c) 2019 dsafa22, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2021-2023 + */ + +#include "raop_rtp_mirror.h" + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "raop.h" +#include "netutils.h" +#include "compat.h" +#include "logger.h" +#include "byteutils.h" +#include "mirror_buffer.h" +#include "stream.h" +#include "utils.h" +#include "plist/plist.h" + +#ifdef _WIN32 +#define CAST (char *) +/* are these keepalive settings for WIN32 correct? */ +/* (taken from https://github.com/wegank/ludimus) */ +#define TCP_KEEPALIVE 3 +#define TCP_KEEPCNT 16 +#define TCP_KEEPIDLE TCP_KEEPALIVE +#define TCP_KEEPINTVL 17 +#else +#define CAST +#endif + +#define SECOND_IN_NSECS 1000000000UL +#define SEC SECOND_IN_NSECS + +/* for MacOS, where SOL_TCP and TCP_KEEPIDLE are not defined */ +#if !defined(SOL_TCP) && defined(IPPROTO_TCP) +#define SOL_TCP IPPROTO_TCP +#endif +#if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) +#define TCP_KEEPIDLE TCP_KEEPALIVE +#endif + +//struct h264codec_s { +// unsigned char compatibility; +// short pps_size; +// short sps_size; +// unsigned char level; +// unsigned char number_of_pps; +// unsigned char* picture_parameter_set; +// unsigned char profile_high; +// unsigned char reserved_3_and_sps; +// unsigned char reserved_6_and_nal; +// unsigned char* sequence_parameter_set; +// unsigned char version; +//}; + +struct raop_rtp_mirror_s { + logger_t *logger; + raop_callbacks_t callbacks; + raop_ntp_t *ntp; + + /* mirror buffer for decryption */ + mirror_buffer_t *buffer; + + /* Remote address as sockaddr */ + struct sockaddr_storage remote_saddr; + socklen_t remote_saddr_len; + + /* MUTEX LOCKED VARIABLES START */ + /* These variables only edited mutex locked */ + int running; + int joined; + + int flush; + thread_handle_t thread_mirror; + mutex_handle_t run_mutex; + + /* MUTEX LOCKED VARIABLES END */ + int mirror_data_sock; + + unsigned short mirror_data_lport; + + /* switch for displaying client FPS data */ + uint8_t show_client_FPS_data; +}; + +static int +raop_rtp_mirror_parse_remote(raop_rtp_mirror_t *raop_rtp_mirror, const char *remote, int remotelen) +{ + int family; + int ret; + assert(raop_rtp_mirror); + if (remotelen == 4) { + family = AF_INET; + } else if (remotelen == 16) { + family = AF_INET6; + } else { + return -1; + } + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror parse remote ip = %s", remote); + ret = netutils_parse_address(family, remote, + &raop_rtp_mirror->remote_saddr, + sizeof(raop_rtp_mirror->remote_saddr)); + if (ret < 0) { + return -1; + } + raop_rtp_mirror->remote_saddr_len = ret; + return 0; +} + +#define NO_FLUSH (-42) +raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, + const char *remote, int remotelen, const unsigned char *aeskey) +{ + raop_rtp_mirror_t *raop_rtp_mirror; + + assert(logger); + assert(callbacks); + + raop_rtp_mirror = calloc(1, sizeof(raop_rtp_mirror_t)); + if (!raop_rtp_mirror) { + return NULL; + } + raop_rtp_mirror->logger = logger; + raop_rtp_mirror->ntp = ntp; + + memcpy(&raop_rtp_mirror->callbacks, callbacks, sizeof(raop_callbacks_t)); + raop_rtp_mirror->buffer = mirror_buffer_init(logger, aeskey); + if (!raop_rtp_mirror->buffer) { + free(raop_rtp_mirror); + return NULL; + } + if (raop_rtp_mirror_parse_remote(raop_rtp_mirror, remote, remotelen) < 0) { + free(raop_rtp_mirror); + return NULL; + } + raop_rtp_mirror->running = 0; + raop_rtp_mirror->joined = 1; + raop_rtp_mirror->flush = NO_FLUSH; + + MUTEX_CREATE(raop_rtp_mirror->run_mutex); + return raop_rtp_mirror; +} + +void +raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID) +{ + mirror_buffer_init_aes(raop_rtp_mirror->buffer, streamConnectionID); +} + +#define RAOP_PACKET_LEN 32768 +/** + * Mirror + */ +static THREAD_RETVAL +raop_rtp_mirror_thread(void *arg) +{ + raop_rtp_mirror_t *raop_rtp_mirror = arg; + assert(raop_rtp_mirror); + + int stream_fd = -1; + unsigned char packet[128]; + memset(packet, 0 , 128); + unsigned char* sps_pps = NULL; + bool prepend_sps_pps = false; + int sps_pps_len = 0; + unsigned char* payload = NULL; + unsigned int readstart = 0; + bool conn_reset = false; + uint64_t ntp_timestamp_nal = 0; + uint64_t ntp_timestamp_raw = 0; + uint64_t ntp_timestamp_remote = 0; + uint64_t ntp_timestamp_local = 0; + unsigned char nal_start_code[4] = { 0x00, 0x00, 0x00, 0x01 }; + bool logger_debug = (logger_get_level(raop_rtp_mirror->logger) >= LOGGER_DEBUG); + bool h265_video = false; + video_codec_t codec; + const char h264[] = "h264"; + const char h265[] = "h265"; + bool unsupported_codec = false; + bool video_stream_suspended = false; + + while (1) { + fd_set rfds; + struct timeval tv; + int nfds, ret; + MUTEX_LOCK(raop_rtp_mirror->run_mutex); + if (!raop_rtp_mirror->running) { + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); + logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror->running is no longer true"); + break; + } + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); + + /* Set timeout valu to 5ms */ + tv.tv_sec = 0; + tv.tv_usec = 5000; + + /* Get the correct nfds value and set rfds */ + FD_ZERO(&rfds); + if (stream_fd == -1) { + FD_SET(raop_rtp_mirror->mirror_data_sock, &rfds); + nfds = raop_rtp_mirror->mirror_data_sock+1; + } else { + FD_SET(stream_fd, &rfds); + nfds = stream_fd+1; + } + ret = select(nfds, &rfds, NULL, NULL, &tv); + if (ret == 0) { + /* Timeout happened */ + continue; + } else if (ret == -1) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in select"); + break; + } + + if (stream_fd == -1 && + (raop_rtp_mirror && raop_rtp_mirror->mirror_data_sock >= 0) && + FD_ISSET(raop_rtp_mirror->mirror_data_sock, &rfds)) { + struct sockaddr_storage saddr; + socklen_t saddrlen; + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror accepting client"); + saddrlen = sizeof(saddr); + stream_fd = accept(raop_rtp_mirror->mirror_data_sock, (struct sockaddr *)&saddr, &saddrlen); + if (stream_fd == -1) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, + "raop_rtp_mirror error in accept %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + break; + } + + // We're calling recv for a certain amount of data, so we need a timeout + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 5000; + if (setsockopt(stream_fd, SOL_SOCKET, SO_RCVTIMEO, CAST &tv, sizeof(tv)) < 0) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, + "raop_rtp_mirror could not set stream socket timeout %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + break; + } + + int option; + option = 1; + if (setsockopt(stream_fd, SOL_SOCKET, SO_KEEPALIVE, CAST &option, sizeof(option)) < 0) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, + "raop_rtp_mirror could not set stream socket keepalive %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + } + option = 60; + if (setsockopt(stream_fd, SOL_TCP, TCP_KEEPIDLE, CAST &option, sizeof(option)) < 0) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, + "raop_rtp_mirror could not set stream socket keepalive time %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + } + option = 10; + if (setsockopt(stream_fd, SOL_TCP, TCP_KEEPINTVL, CAST &option, sizeof(option)) < 0) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, + "raop_rtp_mirror could not set stream socket keepalive interval %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + } + option = 6; + if (setsockopt(stream_fd, SOL_TCP, TCP_KEEPCNT, CAST &option, sizeof(option)) < 0) { + int sock_err = SOCKET_GET_ERROR(); + logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, + "raop_rtp_mirror could not set stream socket keepalive probes %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + } + readstart = 0; + } + + if (stream_fd != -1 && FD_ISSET(stream_fd, &rfds)) { + + // The first 128 bytes are some kind of header for the payload that follows + while (payload == NULL && readstart < 128) { + unsigned char* pos = packet + readstart; + ret = recv(stream_fd, CAST pos, 128 - readstart, 0); + if (ret <= 0) break; + readstart = readstart + ret; + } + + if (payload == NULL && ret == 0) { + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, + "raop_rtp_mirror tcp socket was closed by client (recv returned 0); got %d bytes of 128 byte header",readstart); + FD_CLR(stream_fd, &rfds); + stream_fd = -1; + continue; + } else if (payload == NULL && ret == -1) { + int sock_err = SOCKET_GET_ERROR(); + if (sock_err == SOCKET_ERRORNAME(EAGAIN) || sock_err == SOCKET_ERRORNAME(EWOULDBLOCK)) continue; // Timeouts can happen even if the connection is fine + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, + "raop_rtp_mirror error in header recv: %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + if (sock_err == SOCKET_ERRORNAME(ECONNRESET)) conn_reset = true;; + break; + } + + /*packet[0:3] contains the payload size */ + int payload_size = byteutils_get_int(packet, 0); + char packet_description[13] = {0}; + char *p = packet_description; + int n = sizeof(packet_description); + for (int i = 4; i < 8; i++) { + snprintf(p, n, "%2.2x ", (unsigned int) packet[i]); + n -= 3; + p += 3; + } + ntp_timestamp_raw = byteutils_get_long(packet, 8); + ntp_timestamp_remote = raop_ntp_timestamp_to_nano_seconds(ntp_timestamp_raw, false); + + /* packet[4] + packet[5] identify the payload type: values seen are: * + * 0x00 0x00: encrypted packet containing a non-IDR type 1 VCL NAL unit * + * 0x00 0x10: encrypted packet containing an IDR type 5 VCL NAL unit * + * 0x01 0x00: unencrypted packet containing a type 7 SPS NAL + a type 8 PPS NAL unit * + * 0x02 0x00: unencrypted packet (old protocol) no payload, sent once every second * + * 0x05 0x00 unencrypted packet with a "streaming report", sent once per second. */ + + /* packet[6] + packet[7] may list a payload "option": values seen are: * + * 0x00 0x00 : encrypted and "streaming report" packets * + * 0x1e 0x00 : old protocol (seen in AirMyPC) no-payload once-per-second packets * + * 0x16 0x01 : seen in most unencrypted h264 SPS+PPS packets * + * 0x56 0x01 : unencrypted h264 SPS+PPS packets (video stream stops, client sleeps) * + * 0x1e 0x01 : unencrypted h265/HEVC SPS+PPS packets + * 0x5e 0x01 : unencrypted h265 SPS+PPS packets (video stream stops, client sleeps) */ + + /* unencrypted packets with a SPS and a PPS NAL are sent initially, and also when a * + * change in video format (e.g. width, height) subsequently occurs. They seem always * + * to be followed by a packet with a type 5 encrypted IDR VCL NAL, with an identical * + * timestamp. On M1/M2 Mac clients, this type 5 NAL is prepended with a type 6 SEI * + * NAL unit. Here we prepend the SPS+PPS NALs to the next encrypted packet, which * + * always has the same timestamp, and is (almost?) always an IDR NAL unit. */ + + /* Unencrypted SPS/PPS packets also have image-size data in (parts of) packet[16:127] */ + + /* "streaming report" packets have no timestamp in packet[8:15] */ + + if (payload == NULL) { + payload = malloc(payload_size); + readstart = 0; + } + + while (readstart < payload_size) { + // Payload data + unsigned char *pos = payload + readstart; + ret = recv(stream_fd, CAST pos, payload_size - readstart, 0); + if (ret <= 0) break; + readstart = readstart + ret; + } + + if (ret == 0) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket was closed by client (recv returned 0)"); + break; + } else if (ret == -1) { + int sock_err = SOCKET_GET_ERROR(); + if (sock_err == SOCKET_ERRORNAME(EAGAIN) || sock_err == SOCKET_ERRORNAME(EWOULDBLOCK)) continue; // Timeouts can happen even if the connection is fine + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in recv: %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); + if (errno == SOCKET_ERRORNAME(ECONNRESET)) conn_reset = true; + break; + } + + switch (packet[4]) { + case 0x00: + // Normal video data (VCL NAL) + + // Conveniently, the video data is already stamped with the remote wall clock time, + // so no additional clock syncing needed. The only thing odd here is that the video + // ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just + // counting nano seconds since last boot. + + ntp_timestamp_local = raop_ntp_convert_remote_time(raop_rtp_mirror->ntp, ntp_timestamp_remote); + if (logger_debug) { + uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp_mirror->ntp); + int64_t latency = ((int64_t) ntp_now) - ((int64_t) ntp_timestamp_local); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, + "raop_rtp video: now = %8.6f, ntp = %8.6f, latency = %8.6f, ts = %8.6f, %s %s", + (double) ntp_now / SEC, (double) ntp_timestamp_local / SEC, (double) latency / SEC, + (double) ntp_timestamp_remote / SEC, packet_description, h265_video ? h265 : h264); + } + + unsigned char* payload_out; + unsigned char* payload_decrypted; + /* + * nal_types:1 Coded non-partitioned slice of a non-IDR picture + * 5 Coded non-partitioned slice of an IDR picture + * 6 Supplemental enhancement information (SEI) + * 7 Sequence parameter set (SPS) + * 8 Picture parameter set (PPS) + * + * if a previous unencrypted packet contains an SPS (type 7) and PPS (type 8) NAL which has not + * yet been sent, it should be prepended to the current NAL. The M1 Macs have increased the h264 level, + * and now the first encrypted packet after the unencrypted SPS+PPS packet may also contain a SEI (type 6) NAL + * prepended to its VCL NAL. + * + * The flag prepend_sps_pps = true will signal that the previous packet contained a SPS NAL + a PPS NAL, + * that has not yet been sent. This will trigger prepending it to the current NAL, and the prepend_sps_pps + * flag will be set to false after it has been prepended. */ + + if (prepend_sps_pps & (ntp_timestamp_raw != ntp_timestamp_nal)) { + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, + "raop_rtp_mirror: prepended sps_pps timestamp does not match timestamp of " + "video payload\n%llu\n%llu , discarding", ntp_timestamp_raw, ntp_timestamp_nal); + free (sps_pps); + sps_pps = NULL; + prepend_sps_pps = false; + } + + if (prepend_sps_pps) { + assert(sps_pps); + payload_out = (unsigned char*) malloc(payload_size + sps_pps_len); + payload_decrypted = payload_out + sps_pps_len; + memcpy(payload_out, sps_pps, sps_pps_len); + free (sps_pps); + sps_pps = NULL; + } else { + payload_out = (unsigned char*) malloc(payload_size); + payload_decrypted = payload_out; + } + // Decrypt data + mirror_buffer_decrypt(raop_rtp_mirror->buffer, payload, payload_decrypted, payload_size); + + // It seems the AirPlay protocol prepends NALs with their size, which we're replacing with the 4-byte + // start code for the NAL Byte-Stream Format. + bool valid_data = true; + int nalu_size = 0; + int nalus_count = 0; + while (nalu_size < payload_size) { + int nc_len = byteutils_get_int_be(payload_decrypted, nalu_size); + if (nc_len < 0 || nalu_size + 4 > payload_size) { + valid_data = false; + break; + } + memcpy(payload_decrypted + nalu_size, nal_start_code, 4); + nalu_size += 4; + nalus_count++; + /* first bit of h264 nalu MUST be 0 ("forbidden_zero_bit") */ + if (payload_decrypted[nalu_size] & 0x80) { + valid_data = false; + break; + } + int nalu_type; + if (h265_video) { + nalu_type = payload_decrypted[nalu_size] & 0x7e >> 1;; + //logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG," h265 video, NALU type %d, size %d", nalu_type, nc_len); + } else { + nalu_type = payload_decrypted[nalu_size] & 0x1f; + int ref_idc = (payload_decrypted[nalu_size] >> 5); + switch (nalu_type) { + case 14: /* Prefix NALu , seen before all VCL Nalu's in AirMyPc */ + case 5: /*IDR, slice_layer_without_partitioning */ + case 1: /*non-IDR, slice_layer_without_partitioning */ + break; + case 2: /* slice data partition A */ + case 3: /* slice data partition B */ + case 4: /* slice data partition C */ + logger_log(raop_rtp_mirror->logger, LOGGER_INFO, + "unexpected partitioned VCL NAL unit: nalu_type = %d, ref_idc = %d, nalu_size = %d," + "processed bytes %d, payloadsize = %d nalus_count = %d", + nalu_type, ref_idc, nc_len, nalu_size, payload_size, nalus_count); + break; + case 6: + if (logger_debug) { + char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror SEI NAL size = %d", nc_len); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, + "raop_rtp_mirror h264 Supplemental Enhancement Information:\n%s", str); + free(str); + } + break; + case 7: + if (logger_debug) { + char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror SPS NAL size = %d", nc_len); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, + "raop_rtp_mirror h264 Sequence Parameter Set:\n%s", str); + free(str); + } + break; + case 8: + if (logger_debug) { + char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror PPS NAL size = %d", nc_len); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, + "raop_rtp_mirror h264 Picture Parameter Set :\n%s", str); + free(str); + } + break; + default: + logger_log(raop_rtp_mirror->logger, LOGGER_INFO, + "unexpected non-VCL NAL unit: nalu_type = %d, ref_idc = %d, nalu_size = %d," + "processed bytes %d, payloadsize = %d nalus_count = %d", + nalu_type, ref_idc, nc_len, nalu_size, payload_size, nalus_count); + break; + } + } + nalu_size += nc_len; + } + if (nalu_size != payload_size) valid_data = false; + if(!valid_data) { + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "nalu marked as invalid"); + payload_out[0] = 1; /* mark video data as invalid h264 (failed decryption) */ + } + + + payload_decrypted = NULL; + video_decode_struct video_data; + video_data.is_h265 = h265_video; + video_data.ntp_time_local = ntp_timestamp_local; + video_data.ntp_time_remote = ntp_timestamp_remote; + video_data.nal_count = nalus_count; /*nal_count will be the number of nal units in the packet */ + video_data.data_len = payload_size; + video_data.data = payload_out; + if (prepend_sps_pps) { + video_data.data_len += sps_pps_len; + video_data.nal_count += 2; + if (h265_video) { + video_data.nal_count++; + } + prepend_sps_pps = false; + } + + raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &video_data); + free(payload_out); + break; + case 0x01: + /* 128-byte observed packet header structure + bytes 0-15: length + timestamp + bytes 16-19 float width_source (value is x.0000, x = unsigned short) + bytes 20-23 float height_source (value is x.0000, x = unsigned short) + bytes 24-39 all 0x0 + bytes 40-43 float width_source (value is x.0000, x = unsigned short) + bytes 44-47 float height_source (value is x.0000, x = unsigned short) + bytes 48-51 ??? float "other_w" (value seems to be x.0000, x = unsigned short) + bytes 48-51 ??? float "other_h" (value seems to be x.0000, x = unsigned short) + bytes 56-59 width + bytes 60-63 height + bytes 64-127 all 0x0 + */ + + // The information in the payload contains an SPS and a PPS NAL + // The sps_pps is not encrypted + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived unencrypted codec packet from client:" + " payload_size %d header %s ts_client = %8.6f", + payload_size, packet_description, (double) ntp_timestamp_remote / SEC); + + if (!video_stream_suspended && (packet[6] == 0x56 || packet[6] == 0x5e)) { + video_stream_suspended = true; + raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls); + } else if (video_stream_suspended && (packet[6] == 0x16 || packet[6] == 0x1e)) { + raop_rtp_mirror->callbacks.video_resume(raop_rtp_mirror->callbacks.cls); + video_stream_suspended = false; + } + + codec = VIDEO_CODEC_UNKNOWN; + assert (raop_rtp_mirror->callbacks.video_set_codec); + ntp_timestamp_nal = ntp_timestamp_raw; + + /* these "floats" are in fact integers that fit into unsigned shorts */ + float width_0 = byteutils_get_float(packet, 16); + float height_0 = byteutils_get_float(packet, 20); + float width_source = byteutils_get_float(packet, 40); // duplication of width_0 + float height_source = byteutils_get_float(packet, 44); // duplication of height_0 + float unknown_w = byteutils_get_float(packet, 48); + float unknown_h = byteutils_get_float(packet, 52); + float width = byteutils_get_float(packet, 56); + float height = byteutils_get_float(packet, 60); + + if (width != width_0 || height != height_0) { + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror: Unexpected : data %f," + " %f != width_source = %f, height_source = %f", width_0, height_0, width_source, height_source); + } + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror: unidentified extra header data %f, %f", unknown_w, unknown_h); + if (raop_rtp_mirror->callbacks.video_report_size) { + raop_rtp_mirror->callbacks.video_report_size(raop_rtp_mirror->callbacks.cls, &width_source, &height_source, &width, &height); + } + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror width_source = %f height_source = %f width = %f height = %f", + width_source, height_source, width, height); + + if (payload_size == 0) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror: received type 0x01 packet with no payload:\n" + "this indicates non-h264 video but Airplay features bit 42 (SupportsScreenMultiCodec) is not set\n" + "use startup option \"-h265\" to set this bit and support h265 (4K) video"); + unsupported_codec = true; + break; + } + if (sps_pps) { + free(sps_pps); + sps_pps = NULL; + } + /* test for a H265 VPS/SPS/PPS */ + unsigned char hvc1[] = { 0x68, 0x76, 0x63, 0x31 }; + + if (!memcmp(payload + 4, hvc1, 4)) { + /* hvc1 HECV detected */ + codec = VIDEO_CODEC_H265; + h265_video = true; + raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec); + unsigned char vps_start_code[] = { 0xa0, 0x00, 0x01, 0x00 }; + unsigned char sps_start_code[] = { 0xa1, 0x00, 0x01, 0x00 }; + unsigned char pps_start_code[] = { 0xa2, 0x00, 0x01, 0x00 }; + unsigned char *vps; + short vps_size; + unsigned char *sps; + short sps_size; + unsigned char *pps; + short pps_size; + + unsigned char * ptr = payload + 0x75; + + if (memcmp(ptr, vps_start_code, 4)) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "non-conforming HEVC VPS/SPS/PPS payload (VPS)"); + raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls); + break; + } + vps_size = byteutils_get_short_be(ptr, 3); + ptr += 5; + vps = ptr; + if (logger_debug) { + char *str = utils_data_to_string(vps, vps_size, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "h265 vps size %d\n%s",vps_size, str); + free(str); + } + ptr += vps_size; + if (memcmp(ptr, sps_start_code, 4)) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "non-conforming HEVC VPS/SPS/PPS payload (SPS)"); + raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls); + break; + } + sps_size = byteutils_get_short_be(ptr, 3); + ptr += 5; + sps = ptr; + if (logger_debug) { + char *str = utils_data_to_string(sps, sps_size, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "h265 sps size %d\n%s",vps_size, str); + free(str); + } + ptr += sps_size; + if (memcmp(ptr, pps_start_code, 4)) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "non-conforming HEVC VPS/SPS/PPS payload (PPS)"); + raop_rtp_mirror->callbacks.video_pause(raop_rtp_mirror->callbacks.cls); + break; + } + pps_size = byteutils_get_short_be(ptr, 3); + ptr += 5; + pps = ptr; + if (logger_debug) { + char *str = utils_data_to_string(pps, pps_size, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "h265 pps size %d\n%s",pps_size, str); + free(str); + } + + sps_pps_len = vps_size + sps_size + pps_size + 12; + sps_pps = (unsigned char*) malloc(sps_pps_len); + assert(sps_pps); + ptr = sps_pps; + memcpy(ptr, nal_start_code, 4); + ptr += 4; + memcpy(ptr, vps, vps_size); + ptr += vps_size; + memcpy(ptr, nal_start_code, 4); + ptr += 4; + memcpy(ptr, sps, sps_size); + ptr += sps_size; + memcpy(ptr, nal_start_code, 4); + ptr += 4; + memcpy(ptr, pps, pps_size); + } else { + codec = VIDEO_CODEC_H264; + h265_video = false; + raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec); + short sps_size = byteutils_get_short_be(payload,6); + unsigned char *sequence_parameter_set = payload + 8; + short pps_size = byteutils_get_short_be(payload, sps_size + 9); + unsigned char *picture_parameter_set = payload + sps_size + 11; + int data_size = 6; + if (logger_debug) { + char *str = utils_data_to_string(payload, data_size, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror: SPS+PPS header size = %d", data_size); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror h264 SPS+PPS header:\n%s", str); + free(str); + str = utils_data_to_string(sequence_parameter_set, sps_size,16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror SPS NAL size = %d", sps_size); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror h264 Sequence Parameter Set:\n%s", str); + free(str); + str = utils_data_to_string(picture_parameter_set, pps_size, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror PPS NAL size = %d", pps_size); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror h264 Picture Parameter Set:\n%s", str); + free(str); + } + data_size = payload_size - sps_size - pps_size - 11; + if (data_size > 0 && logger_debug) { + char *str = utils_data_to_string (picture_parameter_set + pps_size, data_size, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "remainder size = %d", data_size); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "remainder of SPS+PPS packet:\n%s", str); + free(str); + } else if (data_size < 0) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, " pps_sps error: packet remainder size = %d < 0", data_size); + } + + // Copy the sps and pps into a buffer to prepend to the next NAL unit. + sps_pps_len = sps_size + pps_size + 8; + sps_pps = (unsigned char*) malloc(sps_pps_len); + assert(sps_pps); + memcpy(sps_pps, nal_start_code, 4); + memcpy(sps_pps + 4, sequence_parameter_set, sps_size); + memcpy(sps_pps + sps_size + 4, nal_start_code, 4); + memcpy(sps_pps + sps_size + 8, payload + sps_size + 11, pps_size); + } + prepend_sps_pps = true; + // h264codec_t h264; + // h264.version = payload[0]; + // h264.profile_high = payload[1]; + // h264.compatibility = payload[2]; + // h264.level = payload[3]; + // h264.reserved_6_and_nal = payload[4]; + // h264.reserved_3_and_sps = payload[5]; + // h264.sps_size = sps_size; + // h264.sequence_parameter_set = malloc(h264.sps_size); + // memcpy(h264.sequence_parameter_set, sequence_parameter_set, sps_size); + // h264.number_of_pps = payload[h264.sps_size + 8]; + // h264.pps_size = pps_size; + // h264.picture_parameter_set = malloc(h264.pps_size); + // memcpy(h264.picture_parameter_set, picture_parameter_set, pps_size); + break; + case 0x02: + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived old-protocol once-per-second packet from client:" + " payload_size %d header %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw); + /* "old protocol" (used by AirMyPC), rest of 128-byte packet is empty */ + case 0x05: + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived video streaming performance info packet from client:" + " payload_size %d header %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw); + /* payloads with packet[4] = 0x05 have no timestamp, and carry video info from the client as a binary plist * + * Sometimes (e.g, when the client has a locked screen), there is a 25kB trailer attached to the packet. * + * This 25000 Byte trailer with unidentified content seems to be the same data each time it is sent. */ + + if (payload_size && raop_rtp_mirror->show_client_FPS_data) { + //char *str = utils_data_to_string(packet, 128, 16); + //logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "type 5 video packet header:\n%s", str); + //free (str); + + int plist_size = payload_size; + if (payload_size > 25000) { + plist_size = payload_size - 25000; + if (logger_debug) { + char *str = utils_data_to_string(payload + plist_size, 16, 16); + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, + "video_info packet had 25kB trailer; first 16 bytes are:\n%s", str); + free(str); + } + } + if (plist_size) { + char *plist_xml; + uint32_t plist_len; + plist_t root_node = NULL; + plist_from_bin((char *) payload, plist_size, &root_node); + plist_to_xml(root_node, &plist_xml, &plist_len); + logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "%s", plist_xml); + free(plist_xml); + } + } + break; + default: + logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "\nReceived unexpected TCP packet from client, " + "size %d, %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw); + break; + } + + free(payload); + payload = NULL; + memset(packet, 0, 128); + readstart = 0; + if (unsupported_codec) { + break; + } + } + } + /* Close the stream file descriptor */ + if (stream_fd != -1) { + closesocket(stream_fd); + } + + // Ensure running reflects the actual state + MUTEX_LOCK(raop_rtp_mirror->run_mutex); + raop_rtp_mirror->running = false; + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); + + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror exiting TCP thread"); + if (conn_reset && raop_rtp_mirror->callbacks.conn_reset) { + const bool video_reset = false; /* leave "frozen video" showing */ + raop_rtp_mirror->callbacks.conn_reset(raop_rtp_mirror->callbacks.cls, 0, video_reset); + } + + if (unsupported_codec) { + closesocket(raop_rtp_mirror->mirror_data_sock); + raop_rtp_mirror_stop(raop_rtp_mirror); + raop_rtp_mirror->callbacks.video_reset(raop_rtp_mirror->callbacks.cls); + } + + return 0; +} + +static int +raop_rtp_mirror_init_socket(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6) +{ + assert(raop_rtp_mirror); + + unsigned short dport = raop_rtp_mirror->mirror_data_lport; + int dsock = netutils_init_socket(&dport, use_ipv6, 0); + if (dsock == -1) { + goto sockets_cleanup; + } + + /* Listen to the data socket if using TCP */ + if (listen(dsock, 1) < 0) { + goto sockets_cleanup; + } + + /* Set socket descriptors */ + raop_rtp_mirror->mirror_data_sock = dsock; + + /* Set port values */ + raop_rtp_mirror->mirror_data_lport = dport; + logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror local data port socket %d port TCP %d", + dsock, dport); + return 0; + + sockets_cleanup: + if (dsock != -1) closesocket(dsock); + return -1; +} + +void +raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport, + uint8_t show_client_FPS_data) +{ + logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror starting mirroring"); + int use_ipv6 = 0; + + assert(raop_rtp_mirror); + assert(mirror_data_lport); + raop_rtp_mirror->show_client_FPS_data = show_client_FPS_data; + + MUTEX_LOCK(raop_rtp_mirror->run_mutex); + if (raop_rtp_mirror->running || !raop_rtp_mirror->joined) { + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); + return; + } + + if (raop_rtp_mirror->remote_saddr.ss_family == AF_INET6) { + use_ipv6 = 1; + } + //use_ipv6 = 0; + + raop_rtp_mirror->mirror_data_lport = *mirror_data_lport; + if (raop_rtp_mirror_init_socket(raop_rtp_mirror, use_ipv6) < 0) { + logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror initializing socket failed"); + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); + return; + } + *mirror_data_lport = raop_rtp_mirror->mirror_data_lport; + + /* Create the thread and initialize running values */ + raop_rtp_mirror->running = 1; + raop_rtp_mirror->joined = 0; + + THREAD_CREATE(raop_rtp_mirror->thread_mirror, raop_rtp_mirror_thread, raop_rtp_mirror); + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); +} + +void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror) { + assert(raop_rtp_mirror); + + /* Check that we are running and thread is not + * joined (should never be while still running) */ + MUTEX_LOCK(raop_rtp_mirror->run_mutex); + if (!raop_rtp_mirror->running || raop_rtp_mirror->joined) { + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); + return; + } + raop_rtp_mirror->running = 0; + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); + + if (raop_rtp_mirror->mirror_data_sock != -1) { + closesocket(raop_rtp_mirror->mirror_data_sock); + raop_rtp_mirror->mirror_data_sock = -1; + } + + /* Join the thread */ + THREAD_JOIN(raop_rtp_mirror->thread_mirror); + + /* Mark thread as joined */ + MUTEX_LOCK(raop_rtp_mirror->run_mutex); + raop_rtp_mirror->joined = 1; + MUTEX_UNLOCK(raop_rtp_mirror->run_mutex); +} + +void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror) { + if (raop_rtp_mirror) { + raop_rtp_mirror_stop(raop_rtp_mirror); + MUTEX_DESTROY(raop_rtp_mirror->run_mutex); + mirror_buffer_destroy(raop_rtp_mirror->buffer); + free(raop_rtp_mirror); + } +} diff --git a/lib/raop_rtp_mirror.h b/lib/raop_rtp_mirror.h new file mode 100644 index 0000000..2e7914d --- /dev/null +++ b/lib/raop_rtp_mirror.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 dsafa22, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2021-2023 + */ + +#ifndef RAOP_RTP_MIRROR_H +#define RAOP_RTP_MIRROR_H + +#include +#include "raop.h" +#include "logger.h" + +typedef struct raop_rtp_mirror_s raop_rtp_mirror_t; +typedef struct h264codec_s h264codec_t; + +raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, + const char *remote, int remotelen, const unsigned char *aeskey); +void raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID); +void raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport, uint8_t show_client_FPS_data); +void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror); +void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror); +#endif //RAOP_RTP_MIRROR_H diff --git a/lib/sockets.h b/lib/sockets.h new file mode 100644 index 0000000..7fb2585 --- /dev/null +++ b/lib/sockets.h @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef SOCKETS_H +#define SOCKETS_H + +#if defined(WIN32) + +char *wsa_strerror(int errnum); + +typedef int socklen_t; + +#ifndef SHUT_RD +# define SHUT_RD SD_RECEIVE +#endif +#ifndef SHUT_WR +# define SHUT_WR SD_SEND +#endif +#ifndef SHUT_RDWR +# define SHUT_RDWR SD_BOTH +#endif + +#define SOCKET_GET_ERROR() WSAGetLastError() +#define SOCKET_SET_ERROR(value) WSASetLastError(value) +#define SOCKET_ERRORNAME(name) WSA##name +#define SOCKET_ERROR_STRING(errnum) wsa_strerror(errnum) + +#define WSAEAGAIN WSAEWOULDBLOCK +#define WSAENOMEM WSA_NOT_ENOUGH_MEMORY + +#else + +#define closesocket close +#define ioctlsocket ioctl + +#define SOCKET_GET_ERROR() (errno) +#define SOCKET_SET_ERROR(value) (errno = (value)) +#define SOCKET_ERRORNAME(name) name +#define SOCKET_ERROR_STRING(errnum) strerror(errnum) +#endif + +#endif diff --git a/lib/srp.c b/lib/srp.c new file mode 100644 index 0000000..d909cbf --- /dev/null +++ b/lib/srp.c @@ -0,0 +1,1180 @@ +/* + * Secure Remote Password 6a implementation + * Copyright (c) 2010 Tom Cocagne. All rights reserved. + * https://github.com/cocagne/csrp + * + * The MIT License (MIT) + * + * Copyright (c) 2013 Tom Cocagne + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + *=========================================================================== + * updated (2023) by fduncanh to replace deprecated openssl SHA* hash functions + * modified (2023) by fduncanh for use with Apple's pair-setup-pin protocol + */ +#define APPLE_VARIANT +#ifdef WIN32 +# include +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "srp.h" + +static int g_initialized = 0; + +typedef struct +{ + BIGNUM * N; + BIGNUM * g; +} NGConstant; + +struct NGHex +{ + const char * n_hex; + const char * g_hex; +}; + +/* All constants here were pulled from Appendix A of RFC 5054 */ +static struct NGHex global_Ng_constants[] = { +#if 0 /* begin removed section 1 */ + { /* 1024 */ + "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496" + "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E" + "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA" + "9AFD5138FE8376435B9FC61D2FC0EB06E3", + "2" + }, + { /* 1536 */ + "9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA961" + "4B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F843" + "80B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0B" + "E3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF5" + "6EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734A" + "F7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E" + "8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB", + "2" + }, +#endif /* end removed section 1 */ + { /* 2048 */ + "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4" + "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60" + "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF" + "747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907" + "8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861" + "60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB" + "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73", + "2" + }, +#if 0 /* begin removed section 2 */ + { /* 3072 */ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B" + "139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485" + "B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1F" + "E649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23" + "DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32" + "905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558" + "17183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521" + "ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D7" + "1E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B1817" + "7B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82" + "D120A93AD2CAFFFFFFFFFFFFFFFF", + "5" + }, + { /* 4096 */ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + "FFFFFFFFFFFFFFFF", + "5" + }, + { /* 6144 */ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF", + "5" + }, + { /* 8192 */ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA" + "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C" + "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886" + "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6" + "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5" + "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268" + "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6" + "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", + "13" + }, + #endif /* end removed section 2 */ + {0,0} /* null sentinel */ +}; + + +static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * g_hex ) +{ + NGConstant * ng = (NGConstant *) malloc( sizeof(NGConstant) ); + ng->N = BN_new(); + ng->g = BN_new(); + + if( !ng || !ng->N || !ng->g ) + return 0; + + if ( ng_type != SRP_NG_CUSTOM ) + { + int idx = ng_type; + if ( ng_type > SRP_NG_CUSTOM ) + idx -= 1; + n_hex = global_Ng_constants[ idx ].n_hex; + g_hex = global_Ng_constants[ idx ].g_hex; + } + BN_hex2bn( &ng->N, n_hex ); + BN_hex2bn( &ng->g, g_hex ); + + return ng; +} + +static void delete_ng( NGConstant * ng ) +{ + if (ng) + { + BN_free( ng->N ); + BN_free( ng->g ); + ng->N = 0; + ng->g = 0; + free(ng); + } +} + +typedef struct HashCTX_s { + EVP_MD_CTX *digest_ctx; +} HashCTX_t; + + +struct SRPVerifier +{ + SRP_HashAlgorithm hash_alg; + NGConstant *ng; + const char * username; + const unsigned char * bytes_B; + int authenticated; + int rfc5054; + unsigned char M [SHA512_DIGEST_LENGTH]; + unsigned char H_AMK [SHA512_DIGEST_LENGTH]; +#ifdef APPLE_VARIANT + unsigned char session_key [2 * SHA512_DIGEST_LENGTH]; +#else + unsigned char session_key [SHA512_DIGEST_LENGTH]; +#endif +}; + +#if 0 /*begin removed section 3*/ +struct SRPUser +{ + SRP_HashAlgorithm hash_alg; + NGConstant *ng; + + BIGNUM *a; + BIGNUM *A; + BIGNUM *S; + + const unsigned char * bytes_A; + int authenticated; + int rfc5054; + + const char * username; + const unsigned char * password; + int password_len; + + unsigned char M [SHA512_DIGEST_LENGTH]; + unsigned char H_AMK [SHA512_DIGEST_LENGTH]; +#ifdef APPLE_VARIANT + unsigned char session_key [2 * SHA512_DIGEST_LENGTH]; +#else + unsigned char session_key [SHA512_DIGEST_LENGTH]; +#endif + +}; +#endif /*end removed section 3*/ +static void handle_error(const char* location) { + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + fprintf(stderr, "SRP error at %s: %s\n", location, error_str); + exit(EXIT_FAILURE); +} + +static void hash_destroy( HashCTX_t *ctx) { + if (ctx) { + EVP_MD_CTX_free(ctx->digest_ctx); + free(ctx); + } +} + +static HashCTX_t *hash_create() +{ + HashCTX_t *ctx = (HashCTX_t *) malloc(sizeof(HashCTX_t)); + assert(ctx != NULL); + ctx->digest_ctx = EVP_MD_CTX_new(); + assert(ctx->digest_ctx != NULL); + return ctx; +} + +int hash_reset( HashCTX_t *ctx) +{ + return EVP_MD_CTX_reset( ctx->digest_ctx); +} + +static int hash_init( SRP_HashAlgorithm alg, HashCTX_t *ctx) +{ + int ret; + switch (alg) + { + case SRP_SHA1 : + ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha1(), NULL); + break; + case SRP_SHA224 : + ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha224(), NULL); + break; + case SRP_SHA256 : + ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha256(), NULL); + break; + case SRP_SHA384 : + ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha384(), NULL); + break; + case SRP_SHA512 : + ret = EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL); + break; + default: + return -1; + } + return ret; +} + +static int hash_update( SRP_HashAlgorithm alg, HashCTX_t *ctx, const void *data, size_t len ) +{ + int ret = EVP_DigestUpdate(ctx->digest_ctx, data, len); + if (!ret) { + handle_error(__func__); + } + return ret; +} + +static int hash_final( SRP_HashAlgorithm alg, HashCTX_t *ctx, unsigned char *md, unsigned int *md_len ) +{ + int ret = EVP_DigestFinal_ex(ctx->digest_ctx, md, md_len); + if (!ret) { + handle_error(__func__); + } + return ret; +} + +static unsigned char * hash( SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md ) +{ + switch (alg) + { + case SRP_SHA1 : return SHA1( d, n, md ); + case SRP_SHA224: return SHA224( d, n, md ); + case SRP_SHA256: return SHA256( d, n, md ); + case SRP_SHA384: return SHA384( d, n, md ); + case SRP_SHA512: return SHA512( d, n, md ); + default: + return 0; + }; +} + +static int hash_length( SRP_HashAlgorithm alg ) +{ + switch (alg) + { + case SRP_SHA1 : return SHA_DIGEST_LENGTH; + case SRP_SHA224: return SHA224_DIGEST_LENGTH; + case SRP_SHA256: return SHA256_DIGEST_LENGTH; + case SRP_SHA384: return SHA384_DIGEST_LENGTH; + case SRP_SHA512: return SHA512_DIGEST_LENGTH; + default: + return -1; + }; +} + +static BIGNUM * H_nn_orig( SRP_HashAlgorithm alg, const BIGNUM * n1, const BIGNUM * n2 ) +{ + unsigned char buff[ SHA512_DIGEST_LENGTH ]; + int len_n1 = BN_num_bytes(n1); + int len_n2 = BN_num_bytes(n2); + int nbytes = len_n1 + len_n2; + unsigned char * bin = (unsigned char *) malloc( nbytes ); + if (!bin) + return 0; + BN_bn2bin(n1, bin); + BN_bn2bin(n2, bin + len_n1); + hash( alg, bin, nbytes, buff ); + free(bin); + return BN_bin2bn(buff, hash_length(alg), NULL); +} + +static BIGNUM * H_nn_rfc5054( SRP_HashAlgorithm alg, const BIGNUM * N, const BIGNUM * n1, const BIGNUM * n2 ) +{ + unsigned char buff[ SHA512_DIGEST_LENGTH ]; + int len_N = BN_num_bytes(N); + int len_n1 = BN_num_bytes(n1); + int len_n2 = BN_num_bytes(n2); + int nbytes = len_N * 2; + unsigned char * bin = (unsigned char *) malloc( nbytes ); + if (!bin) + return 0; + + if (len_n1 > len_N || len_n2 > len_N) + return 0; + + memset(bin, 0, nbytes); + BN_bn2bin(n1, bin + (len_N - len_n1)); + BN_bn2bin(n2, bin + (len_N + len_N - len_n2)); + hash( alg, bin, nbytes, buff ); + free(bin); + return BN_bin2bn(buff, hash_length(alg), NULL); +} + +static BIGNUM * H_ns( SRP_HashAlgorithm alg, const BIGNUM * n, const unsigned char * bytes, int len_bytes ) +{ + unsigned char buff[ SHA512_DIGEST_LENGTH ]; + int len_n = BN_num_bytes(n); + int nbytes = len_n + len_bytes; + unsigned char * bin = (unsigned char *) malloc( nbytes ); + if (!bin) + return 0; + BN_bn2bin(n, bin); + memcpy( bin + len_n, bytes, len_bytes ); + hash( alg, bin, nbytes, buff ); + free(bin); + return BN_bin2bn(buff, hash_length(alg), NULL); +} + +static BIGNUM * calculate_x( SRP_HashAlgorithm alg, const BIGNUM * salt, const char * username, const unsigned char * password, int password_len ) +{ + unsigned char ucp_hash[SHA512_DIGEST_LENGTH]; + unsigned int ucp_hash_len; + HashCTX_t *ctx = hash_create(); + + hash_init( alg, ctx); + hash_update( alg, ctx, username, strlen(username) ); + hash_update( alg, ctx, ":", 1 ); + hash_update( alg, ctx, password, password_len ); + hash_final( alg, ctx, ucp_hash, &ucp_hash_len ); + hash_destroy ( ctx); + + return H_ns( alg, salt, ucp_hash, hash_length(alg) ); +} + +static void update_hash_n( SRP_HashAlgorithm alg, HashCTX_t *ctx, const BIGNUM * n ) +{ + unsigned long len = BN_num_bytes(n); + unsigned char * n_bytes = (unsigned char *) malloc( len ); + if (!n_bytes) + return; + BN_bn2bin(n, n_bytes); + hash_update(alg, ctx, n_bytes, len); + free(n_bytes); +} + +static void hash_num( SRP_HashAlgorithm alg, const BIGNUM * n, unsigned char * dest ) +{ + int nbytes = BN_num_bytes(n); + unsigned char * bin = (unsigned char *) malloc( nbytes ); + if(!bin) + return; + BN_bn2bin(n, bin); + hash( alg, bin, nbytes, dest ); + free(bin); +} + +#ifdef APPLE_VARIANT +/* added for compatibility with Apple's modified srp + * see https://htmlpreview.github.io/?https://github.com/philippe44/RAOP-Player/blob/master/doc/auth_protocol.html + */ + +static int hash_session_key( SRP_HashAlgorithm alg, const BIGNUM * n, unsigned char * dest ) +{ + HashCTX_t *ctx; + int nbytes = BN_num_bytes(n); + unsigned char * bin = (unsigned char *) malloc( nbytes ); + unsigned char fourbytes[4] = { 0 }; //Apple's modified SRP protocol + unsigned int len1, len2; + if(!bin) + return -1; + BN_bn2bin(n, bin); + + ctx = hash_create(); + hash_init(alg, ctx); + hash_update( alg, ctx, bin, nbytes); + hash_update( alg, ctx, fourbytes, sizeof(fourbytes)); + hash_final( alg, ctx, dest, &len1); + hash_reset( ctx); + + fourbytes[3] = 1; + hash_init(alg, ctx); + hash_update( alg, ctx, bin, nbytes); + hash_update( alg, ctx, fourbytes, sizeof(fourbytes)); + hash_final( alg, ctx, dest + len1, &len2); + hash_destroy( ctx); + + free(bin); + return len1 + len2; +} +#endif + +static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * dest, const char * I, const BIGNUM * s, + const BIGNUM * A, const BIGNUM * B, const unsigned char * K ) +{ + unsigned char H_N[ SHA512_DIGEST_LENGTH ]; + unsigned char H_g[ SHA512_DIGEST_LENGTH ]; + unsigned char H_I[ SHA512_DIGEST_LENGTH ]; + unsigned char H_xor[ SHA512_DIGEST_LENGTH ]; + unsigned int dest_len; + HashCTX_t *ctx; + int i = 0; + int hash_len = hash_length(alg); + + hash_num( alg, ng->N, H_N ); + hash_num( alg, ng->g, H_g ); + hash(alg, (const unsigned char *)I, strlen(I), H_I); + + for (i=0; i < hash_len; i++ ) + H_xor[i] = H_N[i] ^ H_g[i]; + + ctx = hash_create(); + hash_init( alg, ctx); + hash_update( alg, ctx, H_xor, hash_len ); + hash_update( alg, ctx, H_I, hash_len ); + update_hash_n( alg, ctx, s ); + update_hash_n( alg, ctx, A ); + update_hash_n( alg, ctx, B ); +#ifdef APPLE_VARIANT /* Apple's SRP session key length is 2 x hash_len */ + hash_update( alg, ctx, K, 2 * hash_len ); +#else + hash_update( alg, ctx, K, hash_len ); +#endif + hash_final( alg, ctx, dest, &dest_len ); + hash_destroy ( ctx); + +} + +static void calculate_H_AMK( SRP_HashAlgorithm alg, unsigned char *dest, const BIGNUM * A, const unsigned char * M, const unsigned char * K ) +{ + HashCTX_t *ctx; + unsigned int dest_len; + + ctx = hash_create(); + hash_init( alg, ctx); + update_hash_n( alg, ctx, A ); + hash_update( alg, ctx, M, hash_length(alg) ); +#ifdef APPLE_VARIANT + hash_update( alg, ctx, K, 2 * hash_length(alg) ); +#else + hash_update( alg, ctx, K, hash_length(alg) ); +#endif + hash_final( alg, ctx, dest, &dest_len ); + hash_destroy ( ctx); +} + + + +static void init_random() +{ + if (g_initialized) + return; + +#ifdef WIN32 + HCRYPTPROV wctx; +#else + FILE *fp = 0; +#endif + unsigned char buff[64]; + +#ifdef WIN32 + CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + CryptGenRandom(wctx, sizeof(buff), (BYTE*) buff); + CryptReleaseContext(wctx, 0); + g_initialized = 1; +#else + fp = fopen("/dev/urandom", "r"); + if (fp) + { + size_t count = fread(buff, sizeof(buff), 1, fp); + fclose(fp); + if (count == 1) { + g_initialized = 1; + } + } +#endif + if (g_initialized) + RAND_seed( buff, sizeof(buff) ); +} + + +/*********************************************************************************************************** + * + * Exported Functions + * + ***********************************************************************************************************/ + +void srp_random_seed( const unsigned char * random_data, int data_length ) +{ + g_initialized = 1; + + if (random_data) + RAND_seed( random_data, data_length ); +} + + +void srp_create_salted_verification_key( SRP_HashAlgorithm alg, + SRP_NGType ng_type, const char * username, + const unsigned char * password, int len_password, + const unsigned char ** bytes_s, int * len_s, + const unsigned char ** bytes_v, int * len_v, + const char * n_hex, const char * g_hex ) +{ + BIGNUM * s = BN_new(); + BIGNUM * v = BN_new(); + BIGNUM * x = 0; + BN_CTX * ctx = BN_CTX_new(); + NGConstant * ng = new_ng( ng_type, n_hex, g_hex ); + + if( !s || !v || !ctx || !ng ) + goto cleanup_and_exit; + + init_random(); /* Only happens once */ + +#ifdef APPLE_VARIANT //use a 16 byte salt + BN_rand(s, 128, -1, 0); +#else + BN_rand(s, 32, -1, 0); +#endif + x = calculate_x( alg, s, username, password, len_password ); + + if( !x ) + goto cleanup_and_exit; + + BN_mod_exp(v, ng->g, x, ng->N, ctx); + + *len_s = BN_num_bytes(s); + *len_v = BN_num_bytes(v); + + *bytes_s = (const unsigned char *) malloc( *len_s ); + *bytes_v = (const unsigned char *) malloc( *len_v ); + + if (!bytes_s || !bytes_v) + goto cleanup_and_exit; + + BN_bn2bin(s, (unsigned char *) *bytes_s); + BN_bn2bin(v, (unsigned char *) *bytes_v); + + cleanup_and_exit: + delete_ng( ng ); + BN_free(s); + BN_free(v); + BN_free(x); + BN_CTX_free(ctx); +} +#ifdef APPLE_VARIANT + +/* Out: bytes_B, len_B, bytes_b, len_b + * On failure, bytes_B and bytes_b will be set to NULL + * len_B and len_will be set to 0 + */ +void srp_create_server_ephemeral_key( SRP_HashAlgorithm alg, SRP_NGType ng_type, + const unsigned char * bytes_v, int len_v, + const unsigned char * bytes_b, int len_b, + const unsigned char ** bytes_B, int * len_B, + const char * n_hex, const char * g_hex, + int rfc5054_compat ) { + BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL); + BIGNUM *tmp1 = BN_new(); + BIGNUM *tmp2 = BN_new(); + BIGNUM *B = BN_new(); + BIGNUM *b = BN_new(); + BIGNUM *k = 0; + BN_CTX *ctx = BN_CTX_new(); + NGConstant *ng = new_ng( ng_type, n_hex, g_hex ); + + *len_B = 0; + *bytes_B = 0; + + if( !v || !B || !b || !tmp1 || !tmp2 || !ctx || !ng ) + goto cleanup_and_exit; + + b = BN_bin2bn(bytes_b, len_b, NULL); + + if (rfc5054_compat) + k = H_nn_rfc5054(alg, ng->N, ng->N, ng->g); + else + k = H_nn_orig(alg, ng->N, ng->g); + + if(!k) + goto cleanup_and_exit; + + /* B = kv + g^b */ + if (rfc5054_compat) + { + BN_mod_mul(tmp1, k, v, ng->N, ctx); + BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); + BN_mod_add(B, tmp1, tmp2, ng->N, ctx); + } + else + { + BN_mul(tmp1, k, v, ctx); + BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); + BN_add(B, tmp1, tmp2); + } + + *len_B = BN_num_bytes(B); + *bytes_B = (const unsigned char *)malloc( *len_B ); + BN_bn2bin( B, (unsigned char *) *bytes_B ); + + cleanup_and_exit: + BN_free(v); + if (k) BN_free(k); + BN_free(B); + BN_free(b); + BN_free(tmp1); + BN_free(tmp2); + BN_CTX_free(ctx); +} +#endif + +/* Out: bytes_B, len_B. + * + * On failure, bytes_B will be set to NULL and len_B will be set to 0 + */ +struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, + const unsigned char * bytes_s, int len_s, + const unsigned char * bytes_v, int len_v, + const unsigned char * bytes_A, int len_A, +#ifdef APPLE_VARIANT + const unsigned char * bytes_b, int len_b, +#endif + const unsigned char ** bytes_B, int * len_B, + const char * n_hex, const char * g_hex, + int rfc5054_compat ) +{ + BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); + BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL); + BIGNUM *A = BN_bin2bn(bytes_A, len_A, NULL); + BIGNUM *u = 0; + BIGNUM *B = BN_new(); + BIGNUM *S = BN_new(); + BIGNUM *b = BN_new(); + BIGNUM *k = 0; + BIGNUM *tmp1 = BN_new(); + BIGNUM *tmp2 = BN_new(); + BN_CTX *ctx = BN_CTX_new(); + int ulen = strlen(username) + 1; + NGConstant *ng = new_ng( ng_type, n_hex, g_hex ); + struct SRPVerifier *ver = 0; + + *len_B = 0; + *bytes_B = 0; + + if( !s || !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !ctx || !ng ) + goto cleanup_and_exit; + + ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) ); + + if (!ver) + goto cleanup_and_exit; + + init_random(); /* Only happens once */ + + ver->username = (char *) malloc( ulen ); + ver->hash_alg = alg; + ver->ng = ng; + + if (!ver->username) + { + free(ver); + ver = 0; + goto cleanup_and_exit; + } + + memcpy( (char*)ver->username, username, ulen ); + + ver->authenticated = 0; + ver->rfc5054 = rfc5054_compat; + + /* SRP-6a safety check */ + BN_mod(tmp1, A, ng->N, ctx); + if ( !BN_is_zero(tmp1) ) + { +#ifdef APPLE_VARIANT + if ( !len_b || !bytes_b) { +#endif + BN_rand(b, 256, -1, 0); +#ifdef APPLE_VARIANT + } else { + b = BN_bin2bn(bytes_b, len_b, NULL); + } +#endif + + if (rfc5054_compat) + k = H_nn_rfc5054(alg, ng->N, ng->N, ng->g); + else + k = H_nn_orig(alg, ng->N, ng->g); + + if(!k) + { + free(ver); + ver = 0; + goto cleanup_and_exit; + } + + /* B = kv + g^b */ + if (rfc5054_compat) + { + BN_mod_mul(tmp1, k, v, ng->N, ctx); + BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); + BN_mod_add(B, tmp1, tmp2, ng->N, ctx); + } + else + { + BN_mul(tmp1, k, v, ctx); + BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); + BN_add(B, tmp1, tmp2); + } + + if (rfc5054_compat) + u = H_nn_rfc5054(alg, ng->N, A, B); + else + u = H_nn_orig(alg, A, B); + + if(!u) + { + free(ver); + ver = 0; + goto cleanup_and_exit; + } + + /* S = (A *(v^u)) ^ b */ + BN_mod_exp(tmp1, v, u, ng->N, ctx); + BN_mul(tmp2, A, tmp1, ctx); + BN_mod_exp(S, tmp2, b, ng->N, ctx); + +#ifdef APPLE_VARIANT + hash_session_key(alg, S, ver->session_key); +#else + hash_num(alg, S, ver->session_key); +#endif + calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key ); + calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key ); + + *len_B = BN_num_bytes(B); + *bytes_B = (const unsigned char *)malloc( *len_B ); + + if( !((const unsigned char *)*bytes_B) ) + { + free( (void*) ver->username ); + free( ver ); + ver = 0; + *len_B = 0; + goto cleanup_and_exit; + } + + BN_bn2bin( B, (unsigned char *) *bytes_B ); + + ver->bytes_B = *bytes_B; + } else { + free(ver); + ver = 0; + } + + cleanup_and_exit: + BN_free(s); + BN_free(v); + BN_free(A); + if (u) BN_free(u); + if (k) BN_free(k); + BN_free(B); + BN_free(S); + BN_free(b); + BN_free(tmp1); + BN_free(tmp2); + BN_CTX_free(ctx); + + return ver; +} + +void srp_verifier_delete( struct SRPVerifier * ver ) +{ + if (ver) + { + delete_ng( ver->ng ); + free( (char *) ver->username ); + free( (unsigned char *) ver->bytes_B ); + memset(ver, 0, sizeof(*ver)); + free( ver ); + } +} + +int srp_verifier_is_authenticated( struct SRPVerifier * ver ) +{ + return ver->authenticated; +} + +const char * srp_verifier_get_username( struct SRPVerifier * ver ) +{ + return ver->username; +} + +const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length ) +{ + if (key_length) +#ifdef APPLE_VARIANT + *key_length = 2 * hash_length( ver->hash_alg ); +#else + *key_length = hash_length( ver->hash_alg ); +#endif + return ver->session_key; +} + +int srp_verifier_get_session_key_length( struct SRPVerifier * ver ) +{ +#ifdef APPLE_VARIANT + return 2 * hash_length( ver->hash_alg ); +#else + return hash_length( ver->hash_alg ); +#endif +} + +/* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */ +void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK ) +{ + if ( memcmp( ver->M, user_M, hash_length(ver->hash_alg) ) == 0 ) + { + ver->authenticated = 1; + *bytes_HAMK = ver->H_AMK; + } + else + *bytes_HAMK = NULL; +} + +/*******************************************************************************/ +#if 0 /*begin removed section 4 */ +struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, + const unsigned char * bytes_password, int len_password, + const char * n_hex, const char * g_hex, + int rfc5054_compat ) +{ + struct SRPUser *usr = (struct SRPUser *) malloc( sizeof(struct SRPUser) ); + int ulen = strlen(username) + 1; + + if (!usr) + goto err_exit; + + init_random(); /* Only happens once */ + usr->hash_alg = alg; + usr->ng = new_ng( ng_type, n_hex, g_hex ); + usr->a = BN_new(); + usr->A = BN_new(); + usr->S = BN_new(); + + if (!usr->ng || !usr->a || !usr->A || !usr->S) + goto err_exit; + + usr->username = (const char *) malloc(ulen); + usr->password = (const unsigned char *) malloc(len_password); + usr->password_len = len_password; + + if (!usr->username || !usr->password) + goto err_exit; + + memcpy((char *)usr->username, username, ulen); + memcpy((char *)usr->password, bytes_password, len_password); + + usr->authenticated = 0; + usr->rfc5054 = rfc5054_compat; + + usr->bytes_A = 0; + + return usr; + + err_exit: + if (usr) + { + BN_free(usr->a); + BN_free(usr->A); + BN_free(usr->S); + if (usr->username) + free((void*)usr->username); + if (usr->password) + { + memset((void*)usr->password, 0, usr->password_len); + free((void*)usr->password); + } + free(usr); + } + + return 0; +} + +void srp_user_delete( struct SRPUser * usr ) +{ + if( usr ) + { + BN_free( usr->a ); + BN_free( usr->A ); + BN_free( usr->S ); + delete_ng( usr->ng ); + + memset((void*)usr->password, 0, usr->password_len); + free((char *)usr->username); + free((char *)usr->password); + if (usr->bytes_A) + free( (char *)usr->bytes_A ); + + memset(usr, 0, sizeof(*usr)); + free( usr ); + } +} + +int srp_user_is_authenticated( struct SRPUser * usr) +{ + return usr->authenticated; +} + +const char * srp_user_get_username( struct SRPUser * usr ) +{ + return usr->username; +} + +const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length ) +{ + if (key_length) +#ifdef APPLE_VARIANT + *key_length = 2 * hash_length( usr->hash_alg ); +#else + *key_length = hash_length( usr->hash_alg ); +#endif + return usr->session_key; +} + +int srp_user_get_session_key_length( struct SRPUser * usr ) +{ +#ifdef APPLE_VARIANT + return 2 * hash_length( usr->hash_alg ); +#else + return hash_length( usr->hash_alg ); +#endif +} + +/* Output: username, bytes_A, len_A */ +void srp_user_start_authentication( struct SRPUser * usr, const char ** username, + const unsigned char ** bytes_A, int * len_A ) +{ + BN_CTX *ctx = BN_CTX_new(); + BN_rand(usr->a, 256, -1, 0); + BN_mod_exp(usr->A, usr->ng->g, usr->a, usr->ng->N, ctx); + BN_CTX_free(ctx); + + *len_A = BN_num_bytes(usr->A); + *bytes_A = (const unsigned char *)malloc( *len_A ); + + if (!*bytes_A) + { + *len_A = 0; + *bytes_A = 0; + *username = 0; + return; + } + + BN_bn2bin( usr->A, (unsigned char *) *bytes_A ); + usr->bytes_A = *bytes_A; + *username = usr->username; +} + +/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */ +void srp_user_process_challenge( struct SRPUser * usr, + const unsigned char * bytes_s, int len_s, + const unsigned char * bytes_B, int len_B, + const unsigned char ** bytes_M, int * len_M) +{ + BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); + BIGNUM *B = BN_bin2bn(bytes_B, len_B, NULL); + BIGNUM *u = 0; + BIGNUM *x = 0; + BIGNUM *k = 0; + BIGNUM *v = BN_new(); + BIGNUM *tmp1 = BN_new(); + BIGNUM *tmp2 = BN_new(); + BIGNUM *tmp3 = BN_new(); + BN_CTX *ctx = BN_CTX_new(); + + *len_M = 0; + *bytes_M = 0; + + if( !s || !B || !v || !tmp1 || !tmp2 || !tmp3 || !ctx ) + goto cleanup_and_exit; + + if (usr->rfc5054) + u = H_nn_rfc5054(usr->hash_alg, usr->ng->N, usr->A, B); + else + u = H_nn_orig(usr->hash_alg, usr->A, B); + + if (!u) + goto cleanup_and_exit; + + x = calculate_x( usr->hash_alg, s, usr->username, usr->password, usr->password_len ); + + if (!x) + goto cleanup_and_exit; + + if (usr->rfc5054) + k = H_nn_rfc5054(usr->hash_alg, usr->ng->N, usr->ng->N, usr->ng->g); + else + k = H_nn_orig(usr->hash_alg, usr->ng->N, usr->ng->g); + + if (!k) + goto cleanup_and_exit; + + /* SRP-6a safety check */ + if ( !BN_is_zero(B) && !BN_is_zero(u) ) + { + BN_mod_exp(v, usr->ng->g, x, usr->ng->N, ctx); + + /* S = (B - k*(g^x)) ^ (a + ux) */ + BN_mul(tmp1, u, x, ctx); + BN_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ + BN_mod_exp(tmp1, usr->ng->g, x, usr->ng->N, ctx); + BN_mul(tmp3, k, tmp1, ctx); /* tmp3 = k*(g^x) */ + BN_sub(tmp1, B, tmp3); /* tmp1 = (B - K*(g^x)) */ + BN_mod_exp(usr->S, tmp1, tmp2, usr->ng->N, ctx); + +#ifdef APPLE_VARIANT + hash_session_key(usr->hash_alg, usr->S, usr->session_key); +#else + hash_num(usr->hash_alg, usr->S, usr->session_key); +#endif + calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key ); + calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key ); + *bytes_M = usr->M; + if (len_M) + *len_M = hash_length( usr->hash_alg ); + } + else + { + *bytes_M = NULL; + if (len_M) + *len_M = 0; + } + + cleanup_and_exit: + BN_free(s); + BN_free(B); + BN_free(u); + BN_free(x); + BN_free(k); + BN_free(v); + BN_free(tmp1); + BN_free(tmp2); + BN_free(tmp3); + BN_CTX_free(ctx); +} + +void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK ) +{ + if ( memcmp( usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg) ) == 0 ) + usr->authenticated = 1; +} +#endif /*end removed section 4 */ diff --git a/lib/srp.h b/lib/srp.h new file mode 100644 index 0000000..6af96a7 --- /dev/null +++ b/lib/srp.h @@ -0,0 +1,236 @@ +/* + * Secure Remote Password 6a implementation + * Copyright (c) 2010 Tom Cocagne. All rights reserved. + * https://github.com/cocagne/csrp + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Tom Cocagne + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + *=========================================================================== + * updated (2023) by fduncanh to replace deprecated openssl SHA* hash functions + * modified (2023) by fduncanh for use with Apple's pair-setup-pin protocol + */ + +/* + * + * Purpose: This is a direct implementation of the Secure Remote Password + * Protocol version 6a as described by + * http://srp.stanford.edu/design.html + * + * Author: tom.cocagne@gmail.com (Tom Cocagne) + * + * Dependencies: OpenSSL (and Advapi32.lib on Windows) + * + * Usage: Refer to test_srp.c for a demonstration + * + * Notes: + * This library allows multiple combinations of hashing algorithms and + * prime number constants. For authentication to succeed, the hash and + * prime number constants must match between + * srp_create_salted_verification_key(), srp_user_new(), + * and srp_verifier_new(). A recommended approach is to determine the + * desired level of security for an application and globally define the + * hash and prime number constants to the predetermined values. + * + * As one might suspect, more bits means more security. As one might also + * suspect, more bits also means more processing time. The test_srp.c + * program can be easily modified to profile various combinations of + * hash & prime number pairings. + */ + +#ifndef SRP_H +#define SRP_H +#define APPLE_VARIANT + +struct SRPVerifier; +#if 0 /*begin removed section 1*/ +struct SRPUser; +#endif /*end removed section 1*/ +typedef enum +{ +#if 0 /* begin removed section 2*/ + SRP_NG_1024, + SRP_NG_1536, +#endif /* end removed section 2*/ + SRP_NG_2048, +#if 0 /* begin removed section 3*/ + SRP_NG_3072, + SRP_NG_4096, + SRP_NG_6144, + SRP_NG_8192, +#endif /* end removed section 3*/ + SRP_NG_CUSTOM +} SRP_NGType; + +typedef enum +{ + SRP_SHA1, + SRP_SHA224, + SRP_SHA256, + SRP_SHA384, + SRP_SHA512 +} SRP_HashAlgorithm; + + +/* This library will automatically seed the OpenSSL random number generator + * using cryptographically sound random data on Windows & Linux. If this is + * undesirable behavior or the host OS does not provide a /dev/urandom file, + * this function may be called to seed the random number generator with + * alternate data. + * + * The random data should include at least as many bits of entropy as the + * largest hash function used by the application. So, for example, if a + * 512-bit hash function is used, the random data requies at least 512 + * bits of entropy. + * + * Passing a null pointer to this function will cause this library to skip + * seeding the random number generator. This is only legitimate if it is + * absolutely known that the OpenSSL random number generator has already + * been sufficiently seeded within the running application. + * + * Notes: + * * This function is optional on Windows & Linux and mandatory on all + * other platforms. + */ +void srp_random_seed( const unsigned char * random_data, int data_length ); + + +/* Out: bytes_s, len_s, bytes_v, len_v + * + * The caller is responsible for freeing the memory allocated for bytes_s and bytes_v + * + * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type. + * If provided, they must contain ASCII text of the hexidecimal notation. + * + */ +void srp_create_salted_verification_key( SRP_HashAlgorithm alg, + SRP_NGType ng_type, const char * username, + const unsigned char * password, int len_password, + const unsigned char ** bytes_s, int * len_s, + const unsigned char ** bytes_v, int * len_v, + const char * n_hex, const char * g_hex ); + + +#ifdef APPLE_VARIANT +/* Out: bytes_B, len_B + * On failure, bytes_B will be set to NULL and len_B will be set to 0 + * + * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type + * + * bytes_b should be a pointer to a cryptographically secure random array of length + * len_b bytes (for example, produced with OpenSSL's RAND_bytes(bytes_b, len_b)). + */ +void srp_create_server_ephemeral_key( SRP_HashAlgorithm alg, SRP_NGType ng_type, + const unsigned char * bytes_v, int len_v, + const unsigned char * bytes_b, int len_b, + const unsigned char ** bytes_B, int * len_B, + const char * n_hex, const char * g_hex, + int rfc5054_compat ); +#endif + +/* Out: bytes_B, len_B. + * + * On failure, bytes_B will be set to NULL and len_B will be set to 0 + * + * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type + * + * If rfc5054_compat is non-zero the resulting verifier will be RFC 5054 compaliant. This + * breaks compatibility with previous versions of the csrp library but is recommended + * for new code. + */ +struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, + const unsigned char * bytes_s, int len_s, + const unsigned char * bytes_v, int len_v, + const unsigned char * bytes_A, int len_A, +#ifdef APPLE_VARIANT + const unsigned char * bytes_b, int len_b, +#endif + const unsigned char ** bytes_B, int * len_B, + const char * n_hex, const char * g_hex, + int rfc5054_compat ); + + +void srp_verifier_delete( struct SRPVerifier * ver ); + + +int srp_verifier_is_authenticated( struct SRPVerifier * ver ); + + +const char * srp_verifier_get_username( struct SRPVerifier * ver ); + +/* key_length may be null */ +const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length ); + + +int srp_verifier_get_session_key_length( struct SRPVerifier * ver ); + + +/* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */ +/* (in APPLE_VARIANT case, session_key_length is DOUBLE the length of user_M) */ +void srp_verifier_verify_session( struct SRPVerifier * ver, + const unsigned char * user_M, + const unsigned char ** bytes_HAMK ); + +/*******************************************************************************/ + +/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type + * + * If rfc5054_compat is non-zero the resulting verifier will be RFC 5054 compaliant. This + * breaks compatibility with previous versions of the csrp library but is recommended + * for new code. + */ +#if 0 /*begin removed section 4 */ +struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, + const unsigned char * bytes_password, int len_password, + const char * n_hex, const char * g_hex, + int rfc5054_compat ); + +void srp_user_delete( struct SRPUser * usr ); + +int srp_user_is_authenticated( struct SRPUser * usr); + + +const char * srp_user_get_username( struct SRPUser * usr ); + +/* key_length may be null */ +const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length ); + +int srp_user_get_session_key_length( struct SRPUser * usr ); + +/* Output: username, bytes_A, len_A */ +void srp_user_start_authentication( struct SRPUser * usr, const char ** username, + const unsigned char ** bytes_A, int * len_A ); + +/* Output: bytes_M, len_M (len_M may be null and will always be + * srp_user_get_session_key_length() bytes in size) */ +/* (in APPLE_VARIANT case, session_key_length is DOUBLE the length of bytes_M) */ +void srp_user_process_challenge( struct SRPUser * usr, + const unsigned char * bytes_s, int len_s, + const unsigned char * bytes_B, int len_B, + const unsigned char ** bytes_M, int * len_M ); + +/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */ +/* (in APPLE_VARIANT case, session_key_length is DOUBLE the length of bytes_HAMK) */ +void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK ); + +#endif /*end removed section 4*/ +#endif /* Include Guard */ diff --git a/lib/stream.h b/lib/stream.h new file mode 100644 index 0000000..6905dcd --- /dev/null +++ b/lib/stream.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 dsafa22, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2022-2023 + */ + +#ifndef AIRPLAYSERVER_STREAM_H +#define AIRPLAYSERVER_STREAM_H + +#include +#include + +typedef struct { + bool is_h265; + int nal_count; + unsigned char *data; + int data_len; + uint64_t ntp_time_local; + uint64_t ntp_time_remote; +} video_decode_struct; + +typedef struct { + unsigned char *data; + unsigned char ct; + int data_len; + int sync_status; + uint64_t ntp_time_local; + uint64_t ntp_time_remote; + uint64_t rtp_time; + unsigned short seqnum; +} audio_decode_struct; + +#endif //AIRPLAYSERVER_STREAM_H diff --git a/lib/threads.h b/lib/threads.h new file mode 100644 index 0000000..d9881da --- /dev/null +++ b/lib/threads.h @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2022 + */ + +#ifndef THREADS_H +#define THREADS_H + +/* Always use pthread library */ + +#include +#include + +#define sleepms(x) usleep((x)*1000) + +typedef pthread_t thread_handle_t; + +#define THREAD_RETVAL void * +#define THREAD_CREATE(handle, func, arg) \ + if (pthread_create(&(handle), NULL, func, arg)) handle = 0 +#define THREAD_JOIN(handle) pthread_join(handle, NULL) + +typedef pthread_mutex_t mutex_handle_t; + +typedef pthread_cond_t cond_handle_t; + +#define MUTEX_CREATE(handle) pthread_mutex_init(&(handle), NULL) +#define MUTEX_LOCK(handle) pthread_mutex_lock(&(handle)) +#define MUTEX_UNLOCK(handle) pthread_mutex_unlock(&(handle)) +#define MUTEX_DESTROY(handle) pthread_mutex_destroy(&(handle)) + +#define COND_CREATE(handle) pthread_cond_init(&(handle), NULL) +#define COND_SIGNAL(handle) pthread_cond_signal(&(handle)) +#define COND_DESTROY(handle) pthread_cond_destroy(&(handle)) + +#endif /* THREADS_H */ diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..8f82186 --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,295 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================= + * modified by fduncanh 2021-2022 + */ + +#include +#include +#include +#include +#include +#include +#define SECOND_IN_NSECS 1000000000UL + +char * +utils_strsep(char **stringp, const char *delim) +{ + char *original; + char *strptr; + + if (*stringp == NULL) { + return NULL; + } + + original = *stringp; + strptr = strstr(*stringp, delim); + if (strptr == NULL) { + *stringp = NULL; + return original; + } + *strptr = '\0'; + *stringp = strptr+strlen(delim); + return original; +} + +int +utils_read_file(char **dst, const char *filename) +{ + FILE *stream; + int filesize; + char *buffer; + int read_bytes; + + /* Open stream for reading */ + stream = fopen(filename, "rb"); + if (!stream) { + return -1; + } + + /* Find out file size */ + fseek(stream, 0, SEEK_END); + filesize = ftell(stream); + fseek(stream, 0, SEEK_SET); + + /* Allocate one extra byte for zero */ + buffer = malloc(filesize+1); + if (!buffer) { + fclose(stream); + return -2; + } + + /* Read data in a loop to buffer */ + read_bytes = 0; + do { + int ret = fread(buffer+read_bytes, 1, + filesize-read_bytes, stream); + if (ret == 0) { + break; + } + read_bytes += ret; + } while (read_bytes < filesize); + + /* Add final null byte and close stream */ + buffer[read_bytes] = '\0'; + fclose(stream); + + /* If read didn't finish, return error */ + if (read_bytes != filesize) { + free(buffer); + return -3; + } + + /* Return buffer */ + *dst = buffer; + return filesize; +} + +int +utils_hwaddr_raop(char *str, int strlen, const char *hwaddr, int hwaddrlen) +{ + int i,j; + + /* Check that our string is long enough */ + if (strlen == 0 || strlen < 2*hwaddrlen+1) + return -1; + + /* Convert hardware address to hex string */ + for (i=0,j=0; i>4) & 0x0f; + int lo = hwaddr[i] & 0x0f; + + if (hi < 10) str[j++] = '0' + hi; + else str[j++] = 'A' + hi-10; + if (lo < 10) str[j++] = '0' + lo; + else str[j++] = 'A' + lo-10; + } + + /* Add string terminator */ + str[j++] = '\0'; + return j; +} + +int +utils_hwaddr_airplay(char *str, int strlen, const char *hwaddr, int hwaddrlen) +{ + int i,j; + + /* Check that our string is long enough */ + if (strlen == 0 || strlen < 2*hwaddrlen+hwaddrlen) + return -1; + + /* Convert hardware address to hex string */ + for (i=0,j=0; i>4) & 0x0f; + int lo = hwaddr[i] & 0x0f; + + if (hi < 10) str[j++] = '0' + hi; + else str[j++] = 'a' + hi-10; + if (lo < 10) str[j++] = '0' + lo; + else str[j++] = 'a' + lo-10; + + str[j++] = ':'; + } + + /* Add string terminator */ + if (j != 0) j--; + str[j++] = '\0'; + return j; +} + +char *utils_parse_hex(const char *str, int str_len, int *data_len) { + assert(str_len % 2 == 0); + + char *data = malloc(str_len / 2); + + for (int i = 0; i < (str_len / 2); i++) { + char c_1 = str[i * 2]; + if (c_1 >= 97 && c_1 <= 102) { + c_1 -= (97 - 10); + } else if (c_1 >= 65 && c_1 <= 70) { + c_1 -= (65 - 10); + } else if (c_1 >= 48 && c_1 <= 57) { + c_1 -= 48; + } else { + free(data); + return NULL; + } + + char c_2 = str[(i * 2) + 1]; + if (c_2 >= 97 && c_2 <= 102) { + c_2 -= (97 - 10); + } else if (c_2 >= 65 && c_2 <= 70) { + c_2 -= (65 - 10); + } else if (c_2 >= 48 && c_2 <= 57) { + c_2 -= 48; + } else { + free(data); + return NULL; + } + + data[i] = (c_1 << 4) | c_2; + } + + *data_len = (str_len / 2); + return data; +} + +char *utils_pk_to_string(const unsigned char *pk, int pk_len) { + char *pk_str = (char *) malloc(2*pk_len + 1); + char* pos = pk_str; + for (int i = 0; i < pk_len; i++) { + snprintf(pos, 3, "%2.2x", *(pk + i)); + pos +=2; + } + return pk_str; +} + +char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line) { + assert(datalen >= 0); + assert(chars_per_line > 0); + int len = 3*datalen + 1; + if (datalen > chars_per_line) { + len += (datalen-1)/chars_per_line; + } + char *str = (char *) calloc(len + 1, sizeof(char)); + assert(str); + char *p = str; + int n = len + 1; + for (int i = 0; i < datalen; i++) { + if (i > 0 && i % chars_per_line == 0) { + snprintf(p, n, "\n"); + n--; + p++; + } + snprintf(p, n, "%2.2x ", (unsigned int) data[i]); + n -= 3; + p += 3; + } + snprintf(p, n, "\n"); + n--; + p++; + assert(p == &(str[len])); + assert(len == strlen(str)); + return str; +} + +char *utils_data_to_text(const char *data, int datalen) { + char *ptr = (char *) calloc(datalen + 1, sizeof(char)); + assert(ptr); + strncpy(ptr, data, datalen); + char *p = ptr; + while (p) { + p = strchr(p, '\r'); /* replace occurences of '\r' by ' ' */ + if (p) *p = ' '; + } + return ptr; +} + +void ntp_timestamp_to_time(uint64_t ntp_timestamp, char *timestamp, size_t maxsize) { + time_t rawtime = (time_t) (ntp_timestamp / SECOND_IN_NSECS); + struct tm ts = *localtime(&rawtime); + assert(maxsize > 29); +#ifdef _WIN32 /*modification for compiling for Windows */ + strftime(timestamp, 20, "%Y-%m-%d %H:%M:%S", &ts); +#else + strftime(timestamp, 20, "%F %T", &ts); +#endif + snprintf(timestamp + 19, 11,".%9.9lu", (unsigned long) ntp_timestamp % SECOND_IN_NSECS); +} + +void ntp_timestamp_to_seconds(uint64_t ntp_timestamp, char *timestamp, size_t maxsize) { + time_t rawtime = (time_t) (ntp_timestamp / SECOND_IN_NSECS); + struct tm ts = *localtime(&rawtime); + assert(maxsize > 12); + strftime(timestamp, 3, "%S", &ts); + snprintf(timestamp + 2, 11,".%9.9lu", (unsigned long) ntp_timestamp % SECOND_IN_NSECS); +} + +int utils_ipaddress_to_string(int addresslen, const unsigned char *address, unsigned int zone_id, char *string, int sizeof_string) { + int ret = 0; + unsigned char ipv6_link_local_prefix[] = { 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + assert(sizeof_string > 0); + assert(string); + if (addresslen != 4 && addresslen != 16) { //invalid address length (only ipv4 and ipv6 allowed) + string[0] = '\0'; + } + if (addresslen == 4) { /* IPV4 */ + ret = snprintf(string, sizeof_string, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]); + } else if (zone_id) { /* IPV6 link-local */ + if (memcmp(address, ipv6_link_local_prefix, 8)) { + string[0] = '\0'; //only link-local ipv6 addresses can have a zone_id + } else { + ret = snprintf(string, sizeof_string, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x%%%u", + address[8], address[9], address[10], address[11], + address[12], address[13], address[14], address[15], zone_id); + } + } else { /* IPV6 standard*/ + ret = snprintf(string, sizeof_string, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + address[0], address[1], address[2], address[3], address[4], address[5], address[6], address[7], + address[8], address[9], address[10], address[11], address[12], address[13], address[14], address[15]); + } + return ret; +} + +const char *gmt_time_string() { + static char date_buf[64]; + memset(date_buf, 0, 64); + + time_t now = time(0); + if (strftime(date_buf, 63, "%c GMT", gmtime(&now))) + return date_buf; + else + return ""; +} diff --git a/lib/utils.h b/lib/utils.h new file mode 100644 index 0000000..82df1f5 --- /dev/null +++ b/lib/utils.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + *================================================================== + * modified by fduncanh 2021-2022 + */ + +#ifndef UTILS_H +#define UTILS_H + +#include + +char *utils_strsep(char **stringp, const char *delim); +int utils_read_file(char **dst, const char *pemstr); +int utils_hwaddr_raop(char *str, int strlen, const char *hwaddr, int hwaddrlen); +int utils_hwaddr_airplay(char *str, int strlen, const char *hwaddr, int hwaddrlen); +char *utils_parse_hex(const char *str, int str_len, int *data_len); +char *utils_pk_to_string(const unsigned char *pk, int pk_len); +char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line); +char *utils_data_to_text(const char *data, int datalen); +void ntp_timestamp_to_time(uint64_t ntp_timestamp, char *timestamp, size_t maxsize); +void ntp_timestamp_to_seconds(uint64_t ntp_timestamp, char *timestamp, size_t maxsize); +const char *gmt_time_string(); +int utils_ipaddress_to_string(int addresslen, const unsigned char *address, + unsigned int zone_id, char *string, int len); +#endif diff --git a/renderers/CMakeLists.txt b/renderers/CMakeLists.txt new file mode 100644 index 0000000..9c49df1 --- /dev/null +++ b/renderers/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.5) + +if (APPLE ) + set( ENV{PKG_CONFIG_PATH} "/Library/FrameWorks/GStreamer.framework/Libraries/pkgconfig" ) # GStreamer.framework, preferred + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig" ) # Brew or self-installed gstreamer + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/homebrew/lib/pkgconfig" ) # Brew, M1/M2 macs + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:$ENV{HOMEBREW_PREFIX}/lib/pkgconfig" ) # Brew, using prefix + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/local/lib/pkgconfig/" ) # MacPorts + message( "PKG_CONFIG_PATH (Apple, renderers) = " $ENV{PKG_CONFIG_PATH} ) + find_program( PKG_CONFIG_EXECUTABLE pkg-config PATHS /Library/FrameWorks/GStreamer.framework/Commands ) + set(PKG_CONFIG_EXECUTABLE ${PKG_CONFIG_EXECUTABLE} --define-prefix ) +else() + if ( DEFINED ENV{GSTREAMER_ROOT_DIR} ) + if ( EXISTS "$ENV{GSTREAMER_ROOT_DIR}/pkgconfig" ) + message ( STATUS "*** Using GSTREAMER_ROOT_DIR = " $ENV{GSTREAMER_ROOT_DIR} ) + set( ENV{PKG_CONFIG_PATH} "$ENV{GSTREAMER_ROOT_DIR}/pkgconfig:$ENV{PKG_CONFIG_PATH}" ) + endif() + endif() + set( ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig" ) # standard location for self-installed gstreamer +endif() + +find_package( PkgConfig REQUIRED ) +if ( X11_FOUND ) + message (STATUS "Will use X_DISPLAY_FIX" ) + add_definitions( -DX_DISPLAY_FIX ) + pkg_check_modules (GST120 gstreamer-1.0>=1.20) + if ( GST120_FOUND ) + message( "-- ZOOMFIX will NOT be applied as Gstreamer version is >= 1.20" ) + else() + message( "-- Failure to find Gstreamer >= 1.20 is NOT an error!" ) + message( "-- ZOOMFIX will be applied as Gstreamer version is < 1.20" ) + add_definitions( -DZOOM_WINDOW_NAME_FIX ) + endif() +endif() + +pkg_check_modules(GST REQUIRED gstreamer-1.0>=1.4 + gstreamer-sdp-1.0>=1.4 + gstreamer-video-1.0>=1.4 + gstreamer-app-1.0>=1.4 +) + +add_library( renderers + STATIC + audio_renderer.c + video_renderer.c ) + +target_link_libraries ( renderers PUBLIC airplay ) + +# hacks to fix cmake confusion due to links in path with macOS FrameWorks + +if( GST_INCLUDE_DIRS MATCHES "/Library/FrameWorks/GStreamer.framework/include" ) + set( GST_INCLUDE_DIRS "/Library/FrameWorks/GStreamer.framework/Headers") + message( STATUS "GST_INCLUDE_DIRS" ${GST_INCLUDE_DIRS} ) +# fix to use -DGST_MACOS for "Official" GStreamer >= 1.22 packages + pkg_check_modules ( GST122 gstreamer-1.0>=1.22 ) + if ( GST122_FOUND ) + set( GST_MACOS "1" CACHE STRING "define GST_MACOS in uxplay.cpp" ) + endif() +endif() + +# set GST_MACOS for all Apple when GStreamer >= 1.24 +if ( APPLE AND NOT GST_MACOS ) + pkg_check_modules ( GST124 gstreamer-1.0>=1.24 ) + if ( GST124_FOUND ) + set( GST_MACOS "1" CACHE STRING "define GST_MACOS in uxplay.cpp" ) + endif() +endif() + +target_include_directories ( renderers PUBLIC ${GST_INCLUDE_DIRS} ) + +if( GST_LIBRARY_DIRS MATCHES "/Library/FrameWorks/GStreamer.framework/lib" ) + set( GST_LIBRARY_DIRS "/Library/FrameWorks/GStreamer.framework/Libraries") + message( STATUS "GST_LIBRARY_DIRS" ${GST_LIBRARY_DIRS} ) + target_link_libraries( renderers PUBLIC ${GST_LIBRARIES} ) + if( CMAKE_VERSION VERSION_LESS "3.13" ) + message( FATAL_ERROR "This macOS build needs cmake >= 3.13" ) + endif() + target_link_directories ( renderers PUBLIC ${GST_LIBRARY_DIRS} ) +elseif( CMAKE_VERSION VERSION_LESS "3.12" ) + target_link_libraries ( renderers PUBLIC ${GST_LIBRARIES} ) +else() + target_link_libraries( renderers PUBLIC ${GST_LINK_LIBRARIES} ) +endif() + + + diff --git a/renderers/audio_renderer.c b/renderers/audio_renderer.c new file mode 100644 index 0000000..c390e68 --- /dev/null +++ b/renderers/audio_renderer.c @@ -0,0 +1,378 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Modified for: + * UxPlay - An open-source AirPlay mirroring server + * Copyright (C) 2021-23 F. Duncanh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "audio_renderer.h" +#define SECOND_IN_NSECS 1000000000UL + +#define NFORMATS 2 /* set to 4 to enable AAC_LD and PCM: allowed, but never seen in real-world use */ + +static GstClockTime gst_audio_pipeline_base_time = GST_CLOCK_TIME_NONE; +static logger_t *logger = NULL; +const char * format[NFORMATS]; + +static const gchar *avdec_aac = "avdec_aac"; +static const gchar *avdec_alac = "avdec_alac"; +static gboolean aac = FALSE; +static gboolean alac = FALSE; +static gboolean render_audio = FALSE; +static gboolean async = FALSE; +static gboolean vsync = FALSE; +static gboolean sync = FALSE; + +typedef struct audio_renderer_s { + GstElement *appsrc; + GstElement *pipeline; + GstElement *volume; + unsigned char ct; +} audio_renderer_t ; +static audio_renderer_t *renderer_type[NFORMATS]; +static audio_renderer_t *renderer = NULL; + +/* GStreamer Caps strings for Airplay-defined audio compression types (ct) */ + +/* ct = 1; linear PCM (uncompressed): 44100/16/2, S16LE */ +static const char lpcm_caps[]="audio/x-raw,rate=(int)44100,channels=(int)2,format=S16LE,layout=interleaved"; + +/* ct = 2; codec_data is ALAC magic cookie: 44100/16/2 spf = 352 */ +static const char alac_caps[] = "audio/x-alac,mpegversion=(int)4,channnels=(int)2,rate=(int)44100,stream-format=raw,codec_data=(buffer)" + "00000024""616c6163""00000000""00000160""0010280a""0e0200ff""00000000""00000000""0000ac44"; + +/* ct = 4; codec_data from MPEG v4 ISO 14996-3 Section 1.6.2.1: AAC-LC 44100/2 spf = 1024 */ +static const char aac_lc_caps[] ="audio/mpeg,mpegversion=(int)4,channnels=(int)2,rate=(int)44100,stream-format=raw,codec_data=(buffer)1210"; + +/* ct = 8; codec_data from MPEG v4 ISO 14996-3 Section 1.6.2.1: AAC_ELD 44100/2 spf = 480 */ +static const char aac_eld_caps[] ="audio/mpeg,mpegversion=(int)4,channnels=(int)2,rate=(int)44100,stream-format=raw,codec_data=(buffer)f8e85000"; + +static gboolean check_plugins (void) +{ + gboolean ret; + GstRegistry *registry; + const gchar *needed[] = { "app", "libav", "playback", "autodetect", "videoparsersbad", NULL}; + const gchar *gst[] = {"plugins-base", "libav", "plugins-base", "plugins-good", "plugins-bad", NULL}; + registry = gst_registry_get (); + ret = TRUE; + for (int i = 0; i < g_strv_length ((gchar **) needed); i++) { + GstPlugin *plugin; + plugin = gst_registry_find_plugin (registry, needed[i]); + if (!plugin) { + g_print ("Required gstreamer plugin '%s' not found\n" + "Missing plugin is contained in '[GStreamer 1.x]-%s'\n",needed[i], gst[i]); + ret = FALSE; + continue; + } + gst_object_unref (plugin); + plugin = NULL; + } + if (ret == FALSE) { + g_print ("\nif the plugin is installed, but not found, your gstreamer registry may have been corrupted.\n" + "to rebuild it when gstreamer next starts, clear your gstreamer cache with:\n" + "\"rm -rf ~/.cache/gstreamer-1.0\"\n\n"); + } + return ret; +} + +static gboolean check_plugin_feature (const gchar *needed_feature) +{ + gboolean ret; + GstPluginFeature *plugin_feature; + GstRegistry *registry = gst_registry_get (); + ret = TRUE; + + plugin_feature = gst_registry_find_feature (registry, needed_feature, GST_TYPE_ELEMENT_FACTORY); + if (!plugin_feature) { + g_print ("Required gstreamer libav plugin feature '%s' not found:\n\n" + "This may be missing because the FFmpeg package used by GStreamer-1.x-libav is incomplete.\n" + "(Some distributions provide an incomplete FFmpeg due to License or Patent issues:\n" + "in such cases a complete version for that distribution is usually made available elsewhere)\n", + needed_feature); + ret = FALSE; + } else { + gst_object_unref (plugin_feature); + plugin_feature = NULL; + } + if (ret == FALSE) { + g_print ("\nif the plugin feature is installed, but not found, your gstreamer registry may have been corrupted.\n" + "to rebuild it when gstreamer next starts, clear your gstreamer cache with:\n" + "\"rm -rf ~/.cache/gstreamer-1.0\"\n\n"); + } + return ret; +} + +bool gstreamer_init(){ + gst_init(NULL,NULL); + return (bool) check_plugins (); +} + +void audio_renderer_init(logger_t *render_logger, const char* audiosink, const bool* audio_sync, const bool* video_sync) { + GError *error = NULL; + GstCaps *caps = NULL; + GstClock *clock = gst_system_clock_obtain(); + g_object_set(clock, "clock-type", GST_CLOCK_TYPE_REALTIME, NULL); + + logger = render_logger; + + aac = check_plugin_feature (avdec_aac); + alac = check_plugin_feature (avdec_alac); + + for (int i = 0; i < NFORMATS ; i++) { + renderer_type[i] = (audio_renderer_t *) calloc(1,sizeof(audio_renderer_t)); + g_assert(renderer_type[i]); + GString *launch = g_string_new("appsrc name=audio_source ! "); + g_string_append(launch, "queue ! "); + switch (i) { + case 0: /* AAC-ELD */ + case 2: /* AAC-LC */ + if (aac) g_string_append(launch, "avdec_aac ! "); + break; + case 1: /* ALAC */ + if (alac) g_string_append(launch, "avdec_alac ! "); + break; + case 3: /*PCM*/ + break; + default: + break; + } + g_string_append (launch, "audioconvert ! "); + g_string_append (launch, "audioresample ! "); /* wasapisink must resample from 44.1 kHz to 48 kHz */ + g_string_append (launch, "volume name=volume ! level ! "); + g_string_append (launch, audiosink); + switch(i) { + case 1: /*ALAC*/ + if (*audio_sync) { + g_string_append (launch, " sync=true"); + async = TRUE; + } else { + g_string_append (launch, " sync=false"); + async = FALSE; + } + break; + default: + if (*video_sync) { + g_string_append (launch, " sync=true"); + vsync = TRUE; + } else { + g_string_append (launch, " sync=false"); + vsync = FALSE; + } + break; + } + renderer_type[i]->pipeline = gst_parse_launch(launch->str, &error); + if (error) { + g_error ("gst_parse_launch error (audio %d):\n %s\n", i+1, error->message); + g_clear_error (&error); + } + + g_assert (renderer_type[i]->pipeline); + gst_pipeline_use_clock(GST_PIPELINE_CAST(renderer_type[i]->pipeline), clock); + + renderer_type[i]->appsrc = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "audio_source"); + renderer_type[i]->volume = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "volume"); + switch (i) { + case 0: + caps = gst_caps_from_string(aac_eld_caps); + renderer_type[i]->ct = 8; + format[i] = "AAC-ELD 44100/2"; + break; + case 1: + caps = gst_caps_from_string(alac_caps); + renderer_type[i]->ct = 2; + format[i] = "ALAC 44100/16/2"; + break; + case 2: + caps = gst_caps_from_string(aac_lc_caps); + renderer_type[i]->ct = 4; + format[i] = "AAC-LC 44100/2"; + break; + case 3: + caps = gst_caps_from_string(lpcm_caps); + renderer_type[i]->ct = 1; + format[i] = "PCM 44100/16/2 S16LE"; + break; + default: + break; + } + logger_log(logger, LOGGER_DEBUG, "Audio format %d: %s",i+1,format[i]); + logger_log(logger, LOGGER_DEBUG, "GStreamer audio pipeline %d: \"%s\"", i+1, launch->str); + g_string_free(launch, TRUE); + g_object_set(renderer_type[i]->appsrc, "caps", caps, "stream-type", 0, "is-live", TRUE, "format", GST_FORMAT_TIME, NULL); + gst_caps_unref(caps); + g_object_unref(clock); + } +} + +void audio_renderer_stop() { + if (renderer) { + gst_app_src_end_of_stream(GST_APP_SRC(renderer->appsrc)); + gst_element_set_state (renderer->pipeline, GST_STATE_NULL); + renderer = NULL; + } +} + +static void get_renderer_type(unsigned char *ct, int *id) { + render_audio = FALSE; + *id = -1; + for (int i = 0; i < NFORMATS; i++) { + if (renderer_type[i]->ct == *ct) { + *id = i; + break; + } + } + switch (*id) { + case 2: + case 0: + if (aac) { + render_audio = TRUE; + } else { + logger_log(logger, LOGGER_INFO, "*** GStreamer libav plugin feature avdec_aac is missing, cannot decode AAC audio"); + } + sync = vsync; + break; + case 1: + if (alac) { + render_audio = TRUE; + } else { + logger_log(logger, LOGGER_INFO, "*** GStreamer libav plugin feature avdec_alac is missing, cannot decode ALAC audio"); + } + sync = async; + break; + case 3: + render_audio = TRUE; + sync = FALSE; + break; + default: + break; + } +} + +void audio_renderer_start(unsigned char *ct) { + int id = -1; + get_renderer_type(ct, &id); + if (id >= 0 && renderer) { + if(*ct != renderer->ct) { + gst_app_src_end_of_stream(GST_APP_SRC(renderer->appsrc)); + gst_element_set_state (renderer->pipeline, GST_STATE_NULL); + logger_log(logger, LOGGER_INFO, "changed audio connection, format %s", format[id]); + renderer = renderer_type[id]; + gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING); + gst_audio_pipeline_base_time = gst_element_get_base_time(renderer->appsrc); + } + } else if (id >= 0) { + logger_log(logger, LOGGER_INFO, "start audio connection, format %s", format[id]); + renderer = renderer_type[id]; + gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING); + gst_audio_pipeline_base_time = gst_element_get_base_time(renderer->appsrc); + } else { + logger_log(logger, LOGGER_ERR, "unknown audio compression type ct = %d", *ct); + } +} + +void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned short *seqnum, uint64_t *ntp_time) { + GstBuffer *buffer; + bool valid; + + if (!render_audio) return; /* do nothing unless render_audio == TRUE */ + + GstClockTime pts = (GstClockTime) *ntp_time ; /* now in nsecs */ + //GstClockTimeDiff latency = GST_CLOCK_DIFF(gst_element_get_current_clock_time (renderer->appsrc), pts); + if (sync) { + if (pts >= gst_audio_pipeline_base_time) { + pts -= gst_audio_pipeline_base_time; + } else { + logger_log(logger, LOGGER_ERR, "*** invalid ntp_time < gst_audio_pipeline_base_time\n%8.6f ntp_time\n%8.6f base_time", + ((double) *ntp_time) / SECOND_IN_NSECS, ((double) gst_audio_pipeline_base_time) / SECOND_IN_NSECS); + return; + } + } + if (data_len == 0 || renderer == NULL) return; + + /* all audio received seems to be either ct = 8 (AAC_ELD 44100/2 spf 460 ) AirPlay Mirror protocol * + * or ct = 2 (ALAC 44100/16/2 spf 352) AirPlay protocol. * + * first byte data[0] of ALAC frame is 0x20, * + * first byte of AAC_ELD is 0x8c, 0x8d or 0x8e: 0x100011(00,01,10) in modern devices * + * but is 0x80, 0x81 or 0x82: 0x100000(00,01,10) in ios9, ios10 devices * + * first byte of AAC_LC should be 0xff (ADTS) (but has never been seen). */ + + buffer = gst_buffer_new_allocate(NULL, *data_len, NULL); + g_assert(buffer != NULL); + //g_print("audio latency %8.6f\n", (double) latency / SECOND_IN_NSECS); + if (sync) { + GST_BUFFER_PTS(buffer) = pts; + } + gst_buffer_fill(buffer, 0, data, *data_len); + switch (renderer->ct){ + case 8: /*AAC-ELD*/ + switch (data[0]){ + case 0x8c: + case 0x8d: + case 0x8e: + case 0x80: + case 0x81: + case 0x82: + valid = true; + break; + default: + valid = false; + break; + } + break; + case 2: /*ALAC*/ + valid = (data[0] == 0x20); + break; + case 4: /*AAC_LC */ + valid = (data[0] == 0xff ); + break; + default: + valid = true; + break; + } + if (valid) { + gst_app_src_push_buffer(GST_APP_SRC(renderer->appsrc), buffer); + } else { + logger_log(logger, LOGGER_ERR, "*** ERROR invalid audio frame (compression_type %d) skipped ", renderer->ct); + logger_log(logger, LOGGER_ERR, "*** first byte of invalid frame was 0x%2.2x ", (unsigned int) data[0]); + } +} + +void audio_renderer_set_volume(double volume) { + volume = (volume > 10.0) ? 10.0 : volume; + volume = (volume < 0.0) ? 0.0 : volume; + g_object_set(renderer->volume, "volume", volume, NULL); +} + +void audio_renderer_flush() { +} + +void audio_renderer_destroy() { + audio_renderer_stop(); + for (int i = 0; i < NFORMATS ; i++ ) { + gst_object_unref (renderer_type[i]->volume); + renderer_type[i]->volume = NULL; + gst_object_unref (renderer_type[i]->appsrc); + renderer_type[i]->appsrc = NULL; + gst_object_unref (renderer_type[i]->pipeline); + renderer_type[i]->pipeline = NULL; + free(renderer_type[i]); + } +} diff --git a/renderers/audio_renderer.h b/renderers/audio_renderer.h new file mode 100644 index 0000000..232da28 --- /dev/null +++ b/renderers/audio_renderer.h @@ -0,0 +1,48 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Modified for: + * UxPlay - An open-source AirPlay mirroring server + * Copyright (C) 2021-23 F. Duncanh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AUDIO_RENDERER_H +#define AUDIO_RENDERER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include "../lib/logger.h" + +bool gstreamer_init(); +void audio_renderer_init(logger_t *logger, const char* audiosink, const bool *audio_sync, const bool *video_sync); +void audio_renderer_start(unsigned char* compression_type); +void audio_renderer_stop(); +void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned short *seqnum, uint64_t *ntp_time); +void audio_renderer_set_volume(double volume); +void audio_renderer_flush(); +void audio_renderer_destroy(); + +#ifdef __cplusplus +} +#endif + +#endif //AUDIO_RENDERER_H diff --git a/renderers/video_renderer.c b/renderers/video_renderer.c new file mode 100644 index 0000000..c89be2e --- /dev/null +++ b/renderers/video_renderer.c @@ -0,0 +1,800 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Modified for: + * UxPlay - An open-source AirPlay mirroring server + * Copyright (C) 2021-24 F. Duncanh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "video_renderer.h" + +#define SECOND_IN_NSECS 1000000000UL +#ifdef X_DISPLAY_FIX +#include +#include "x_display_fix.h" +static bool fullscreen = false; +static bool alt_keypress = false; +static unsigned char X11_search_attempts; +#endif + +static GstClockTime gst_video_pipeline_base_time = GST_CLOCK_TIME_NONE; +static logger_t *logger = NULL; +static unsigned short width, height, width_source, height_source; /* not currently used */ +static bool first_packet = false; +static bool sync = false; +static bool auto_videosink = true; +static bool hls_video = false; +#ifdef X_DISPLAY_FIX +static bool use_x11 = false; +#endif +static bool logger_debug = false; +static bool video_terminate = false; + +#define NCODECS 2 /* renderers for h264 and h265 */ + +struct video_renderer_s { + GstElement *appsrc, *pipeline; + GstBus *bus; + const char *codec; + bool autovideo, state_pending; + int id; + gboolean terminate; + gint64 duration; + gint buffering_level; +#ifdef X_DISPLAY_FIX + bool use_x11; + const char * server_name; + X11_Window_t * gst_window; +#endif +}; + +static video_renderer_t *renderer = NULL; +static video_renderer_t *renderer_type[NCODECS] = {0}; +static int n_renderers = NCODECS; +static char h264[] = "h264"; +static char h265[] = "h265"; +static char hls[] = "hls"; + +static void append_videoflip (GString *launch, const videoflip_t *flip, const videoflip_t *rot) { + /* videoflip image transform */ + switch (*flip) { + case INVERT: + switch (*rot) { + case LEFT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_90R ! "); + break; + case RIGHT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_90L ! "); + break; + default: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_180 ! "); + break; + } + break; + case HFLIP: + switch (*rot) { + case LEFT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_UL_LR ! "); + break; + case RIGHT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_UR_LL ! "); + break; + default: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_HORIZ ! "); + break; + } + break; + case VFLIP: + switch (*rot) { + case LEFT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_UR_LL ! "); + break; + case RIGHT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_UL_LR ! "); + break; + default: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_VERT ! "); + break; + } + break; + default: + switch (*rot) { + case LEFT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_90L ! "); + break; + case RIGHT: + g_string_append(launch, "videoflip video-direction=GST_VIDEO_ORIENTATION_90R ! "); + break; + default: + break; + } + break; + } +} + +/* apple uses colorimetry=1:3:5:1 * + * (not recognized by v4l2 plugin in Gstreamer < 1.20.4) * + * See .../gst-libs/gst/video/video-color.h in gst-plugins-base * + * range = 1 -> GST_VIDEO_COLOR_RANGE_0_255 ("full RGB") * + * matrix = 3 -> GST_VIDEO_COLOR_MATRIX_BT709 * + * transfer = 5 -> GST_VIDEO_TRANSFER_BT709 * + * primaries = 1 -> GST_VIDEO_COLOR_PRIMARIES_BT709 * + * closest used by GStreamer < 1.20.4 is BT709, 2:3:5:1 with * * + * range = 2 -> GST_VIDEO_COLOR_RANGE_16_235 ("limited RGB") */ + +static const char h264_caps[]="video/x-h264,stream-format=(string)byte-stream,alignment=(string)au"; +static const char h265_caps[]="video/x-h265,stream-format=(string)byte-stream,alignment=(string)au"; + +void video_renderer_size(float *f_width_source, float *f_height_source, float *f_width, float *f_height) { + width_source = (unsigned short) *f_width_source; + height_source = (unsigned short) *f_height_source; + width = (unsigned short) *f_width; + height = (unsigned short) *f_height; + logger_log(logger, LOGGER_DEBUG, "begin video stream wxh = %dx%d; source %dx%d", width, height, width_source, height_source); +} + +GstElement *make_video_sink(const char *videosink, const char *videosink_options) { + /* used to build a videosink for playbin, using the user-specified string "videosink" */ + GstElement *video_sink = gst_element_factory_make(videosink, "videosink"); + if (!video_sink) { + return NULL; + } + + /* process the video_sink_optons */ + size_t len = strlen(videosink_options); + if (!len) { + return video_sink; + } + + char *options = (char *) malloc(len + 1); + strncpy(options, videosink_options, len + 1); + + /* remove any extension begining with "!" */ + char *end = strchr(options, '!'); + if (end) { + *end = '\0'; + } + + /* add any fullscreen options "property=pval" included in string videosink_options*/ + /* OK to use strtok_r in Windows with MSYS2 (POSIX); use strtok_s for MSVC */ + char *token; + char *text = options; + + while((token = strtok_r(text, " ", &text))) { + char *pval = strchr(token, '='); + if (pval) { + *pval = '\0'; + pval++; + const gchar *property_name = (const gchar *) token; + const gchar *value = (const gchar *) pval; + g_print("playbin_videosink property: \"%s\" \"%s\"\n", property_name, value); + gst_util_set_object_arg(G_OBJECT (video_sink), property_name, value); + } + } + free(options); + return video_sink; +} + +void video_renderer_init(logger_t *render_logger, const char *server_name, videoflip_t videoflip[2], const char *parser, + const char *decoder, const char *converter, const char *videosink, const char *videosink_options, + bool initial_fullscreen, bool video_sync, bool h265_support, const char *uri) { + GError *error = NULL; + GstCaps *caps = NULL; + hls_video = (uri != NULL); + /* videosink choices that are auto */ + auto_videosink = (strstr(videosink, "autovideosink") || strstr(videosink, "fpsdisplaysink")); + + logger = render_logger; + logger_debug = (logger_get_level(logger) >= LOGGER_DEBUG); + video_terminate = false; + + const gchar *appname = g_get_application_name(); + if (!appname || strcmp(appname,server_name)) g_set_application_name(server_name); + appname = NULL; + + if (hls_video) { + n_renderers = 1; + } else { + n_renderers = h265_support ? 2 : 1; + } + g_assert (n_renderers <= NCODECS); + for (int i = 0; i < n_renderers; i++) { + g_assert (i < 2); + renderer_type[i] = (video_renderer_t *) calloc(1, sizeof(video_renderer_t)); + g_assert(renderer_type[i]); + renderer_type[i]->autovideo = auto_videosink; + renderer_type[i]->id = i; + renderer_type[i]->bus = NULL; + if (hls_video) { + renderer_type[i]->pipeline = gst_element_factory_make("playbin3", "hls-playbin3"); + g_assert(renderer_type[i]->pipeline); + renderer_type[i]->appsrc = NULL; + renderer_type[i]->codec = hls; + if(strcmp(videosink, "autovideosink")) { + GstElement *playbin_videosink = make_video_sink(videosink, videosink_options); + if (!playbin_videosink) { + logger_log(logger, LOGGER_ERR, "video_renderer_init: failed to create playbin_videosink"); + } else { + logger_log(logger, LOGGER_DEBUG, "video_renderer_init: create playbin_videosink at %p", playbin_videosink); + g_object_set(G_OBJECT (renderer_type[i]->pipeline), "video-sink", playbin_videosink, NULL); + } + } + g_object_set (G_OBJECT (renderer_type[i]->pipeline), "uri", uri, NULL); + } else { + switch (i) { + case 0: + renderer_type[i]->codec = h264; + caps = gst_caps_from_string(h264_caps); + break; + case 1: + renderer_type[i]->codec = h265; + caps = gst_caps_from_string(h265_caps); + break; + default: + g_assert(0); + } + GString *launch = g_string_new(""); + + // Add static image source + g_string_append(launch, "filesrc location=/path/to/placeholder.jpg ! jpegdec ! imagefreeze ! videoconvert ! "); + g_string_append(launch, "queue ! "); + g_string_append(launch, "input-selector name=selector ! "); + + // Add video source + g_string_append(launch, "appsrc name=video_source ! "); + g_string_append(launch, "queue ! "); + g_string_append(launch, parser); + g_string_append(launch, " ! "); + g_string_append(launch, decoder); + g_string_append(launch, " ! "); + append_videoflip(launch, &videoflip[0], &videoflip[1]); + g_string_append(launch, converter); + g_string_append(launch, " ! "); + g_string_append(launch, "videoscale ! "); + g_string_append(launch, videosink); + g_string_append(launch, " name="); + g_string_append(launch, videosink); + g_string_append(launch, "_"); + g_string_append(launch, renderer_type[i]->codec); + g_string_append(launch, videosink_options); + if (video_sync) { + g_string_append(launch, " sync=true"); + sync = true; + } else { + g_string_append(launch, " sync=false"); + sync = false; + } + + if (!strcmp(renderer_type[i]->codec, h264)) { + char *pos = launch->str; + while ((pos = strstr(pos,h265))){ + pos +=3; + *pos = '4'; + } + } else if (!strcmp(renderer_type[i]->codec, h265)) { + char *pos = launch->str; + while ((pos = strstr(pos,h264))){ + pos +=3; + *pos = '5'; + } + } + + logger_log(logger, LOGGER_DEBUG, "GStreamer video pipeline %d:\n\"%s\"", i + 1, launch->str); + renderer_type[i]->pipeline = gst_parse_launch(launch->str, &error); + if (error) { + g_error ("get_parse_launch error (video) :\n %s\n",error->message); + g_clear_error (&error); + } + g_assert (renderer_type[i]->pipeline); + + GstClock *clock = gst_system_clock_obtain(); + g_object_set(clock, "clock-type", GST_CLOCK_TYPE_REALTIME, NULL); + gst_pipeline_use_clock(GST_PIPELINE_CAST(renderer_type[i]->pipeline), clock); + renderer_type[i]->appsrc = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "video_source"); + g_assert(renderer_type[i]->appsrc); + + // Get the input-selector element + GstElement *selector = gst_bin_get_by_name(GST_BIN(renderer_type[i]->pipeline), "selector"); + g_assert(selector); + + // Set initial input to static image + g_object_set(selector, "active-pad", gst_element_get_static_pad(selector, "sink_0"), NULL); + gst_object_unref(selector); + + g_object_set(renderer_type[i]->appsrc, "caps", caps, "stream-type", 0, "is-live", TRUE, "format", GST_FORMAT_TIME, NULL); + g_string_free(launch, TRUE); + gst_caps_unref(caps); + gst_object_unref(clock); + } +#ifdef X_DISPLAY_FIX + use_x11 = (strstr(videosink, "xvimagesink") || strstr(videosink, "ximagesink") || auto_videosink); + fullscreen = initial_fullscreen; + renderer_type[i]->server_name = server_name; + renderer_type[i]->gst_window = NULL; + renderer_type[i]->use_x11 = false; + X11_search_attempts = 0; + if (use_x11) { + if (i == 0) { + renderer_type[0]->gst_window = (X11_Window_t *) calloc(1, sizeof(X11_Window_t)); + g_assert(renderer_type[0]->gst_window); + get_X11_Display(renderer_type[0]->gst_window); + if (renderer_type[0]->gst_window->display) { + renderer_type[i]->use_x11 = true; + } else { + free(renderer_type[0]->gst_window); + renderer_type[0]->gst_window = NULL; + } + } else if (renderer_type[0]->use_x11) { + renderer_type[i]->gst_window = (X11_Window_t *) calloc(1, sizeof(X11_Window_t)); + g_assert(renderer_type[i]->gst_window); + memcpy(renderer_type[i]->gst_window, renderer_type[0]->gst_window, sizeof(X11_Window_t)); + renderer_type[i]->use_x11 = true; + } + } +#endif + gst_element_set_state (renderer_type[i]->pipeline, GST_STATE_READY); + GstState state; + if (gst_element_get_state (renderer_type[i]->pipeline, &state, NULL, 100 * GST_MSECOND)) { + if (state == GST_STATE_READY) { + logger_log(logger, LOGGER_DEBUG, "Initialized GStreamer video renderer %d", i + 1); + if (hls_video && i == 0) { + renderer = renderer_type[i]; + } + } else { + logger_log(logger, LOGGER_ERR, "Failed to initialize GStreamer video renderer %d", i + 1); + } + } else { + logger_log(logger, LOGGER_ERR, "Failed to initialize GStreamer video renderer %d", i + 1); + } + } +} + +void video_renderer_pause() { + if (!renderer) { + return; + } + logger_log(logger, LOGGER_DEBUG, "video renderer paused"); + gst_element_set_state(renderer->pipeline, GST_STATE_PAUSED); +} + +void video_renderer_resume() { + if (!renderer) { + return; + } + gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING); + GstState state; + /* wait with timeout 100 msec for pipeline to change state from PAUSED to PLAYING */ + gst_element_get_state(renderer->pipeline, &state, NULL, 100 * GST_MSECOND); + const gchar *state_name = gst_element_state_get_name(state); + logger_log(logger, LOGGER_DEBUG, "video renderer resumed: state %s", state_name); + if (renderer->appsrc) { + gst_video_pipeline_base_time = gst_element_get_base_time(renderer->appsrc); + } +} + +void video_renderer_start() { + if (hls_video) { + renderer->bus = gst_element_get_bus(renderer->pipeline); + gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING); + return; + } + /* when not hls, start both h264 and h265 pipelines; will shut down the "wrong" one when we know the codec */ + for (int i = 0; i < n_renderers; i++) { + gst_element_set_state (renderer_type[i]->pipeline, GST_STATE_PLAYING); + if (renderer_type[i]->appsrc) { + gst_video_pipeline_base_time = gst_element_get_base_time(renderer_type[i]->appsrc); + } + renderer_type[i]->bus = gst_element_get_bus(renderer_type[i]->pipeline); + } + renderer = NULL; + first_packet = true; +#ifdef X_DISPLAY_FIX + X11_search_attempts = 0; +#endif +} + +/* used to find any X11 Window used by the playbin (HLS) pipeline after it starts playing. +* if use_x11 is true, called every 100 ms after playbin state is READY until the x11 window is found*/ +bool waiting_for_x11_window() { + if (!hls_video) { + return false; + } +#ifdef X_DISPLAY_FIX + if (use_x11 && renderer->gst_window) { + get_x_window(renderer->gst_window, renderer->server_name); + if (!renderer->gst_window->window) { + return true; /* window still not found */ + } + } +#endif + return false; +} + +void video_renderer_render_buffer(unsigned char* data, int *data_len, int *nal_count, uint64_t *ntp_time) { + GstBuffer *buffer; + GstClockTime pts = (GstClockTime) *ntp_time; /*now in nsecs */ + if (sync) { + if (pts >= gst_video_pipeline_base_time) { + pts -= gst_video_pipeline_base_time; + } else { + logger_log(logger, LOGGER_ERR, "*** invalid ntp_time < gst_video_pipeline_base_time\n%8.6f ntp_time\n%8.6f base_time", + ((double) *ntp_time) / SECOND_IN_NSECS, ((double) gst_video_pipeline_base_time) / SECOND_IN_NSECS); + return; + } + } + g_assert(data_len != 0); + if (data[0]) { + logger_log(logger, LOGGER_ERR, "*** ERROR decryption of video packet failed "); + } else { + if (first_packet) { + logger_log(logger, LOGGER_INFO, "Begin streaming to GStreamer video pipeline"); + first_packet = false; + // Switch to video source when first valid frame is received + video_renderer_switch_source(true); + } + buffer = gst_buffer_new_allocate(NULL, *data_len, NULL); + g_assert(buffer != NULL); + if (sync) { + GST_BUFFER_PTS(buffer) = pts; + } + gst_buffer_fill(buffer, 0, data, *data_len); + gst_app_src_push_buffer (GST_APP_SRC(renderer->appsrc), buffer); +#ifdef X_DISPLAY_FIX + if (renderer->gst_window && !(renderer->gst_window->window) && renderer->use_x11) { + X11_search_attempts++; + logger_log(logger, LOGGER_DEBUG, "Looking for X11 UxPlay Window, attempt %d", (int) X11_search_attempts); + get_x_window(renderer->gst_window, renderer->server_name); + if (renderer->gst_window->window) { + logger_log(logger, LOGGER_INFO, "\n*** X11 Windows: Use key F11 or (left Alt)+Enter to toggle full-screen mode\n"); + if (fullscreen) { + set_fullscreen(renderer->gst_window, &fullscreen); + } + } + } +#endif + } +} + +void video_renderer_flush() { +} + +void video_renderer_stop() { + if (renderer) { + if (renderer->appsrc) { + gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc)); + // Switch back to static image when video is stopped + video_renderer_switch_source(false); + } + gst_element_set_state (renderer->pipeline, GST_STATE_NULL); + } +} + +static void video_renderer_destroy_h26x(video_renderer_t *renderer) { + if (renderer) { + GstState state; + gst_element_get_state(renderer->pipeline, &state, NULL, 100 * GST_MSECOND); + if (state != GST_STATE_NULL) { + if (!hls_video) { + gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc)); + } + gst_element_set_state (renderer->pipeline, GST_STATE_NULL); + } + gst_object_unref(renderer->bus); + if (renderer->appsrc) { + gst_object_unref (renderer->appsrc); + } + gst_object_unref (renderer->pipeline); +#ifdef X_DISPLAY_FIX + if (renderer->gst_window) { + free(renderer->gst_window); + renderer->gst_window = NULL; + } +#endif + free (renderer); + renderer = NULL; + } +} + +void video_renderer_destroy() { + for (int i = 0; i < n_renderers; i++) { + if (renderer_type[i]) { + video_renderer_destroy_h26x(renderer_type[i]); + } + } +} + +gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void *loop) { + + /* identify which pipeline sent the message */ + int type = -1; + for (int i = 0 ; i < n_renderers ; i ++ ) { + if (renderer_type[i]->bus == bus) { + type = i; + break; + } + } + g_assert(type != -1); + + if (logger_debug) { + g_print("GStreamer %s bus message: %s %s\n", renderer_type[type]->codec, GST_MESSAGE_SRC_NAME(message), GST_MESSAGE_TYPE_NAME(message)); + } + + if (logger_debug && hls_video) { + gint64 pos; + gst_element_query_position (renderer_type[type]->pipeline, GST_FORMAT_TIME, &pos); + if (GST_CLOCK_TIME_IS_VALID(pos)) { + g_print("GStreamer bus message %s %s; position: %" GST_TIME_FORMAT "\n", GST_MESSAGE_SRC_NAME(message), + GST_MESSAGE_TYPE_NAME(message), GST_TIME_ARGS(pos)); + } else { + g_print("GStreamer bus message %s %s; position: none\n", GST_MESSAGE_SRC_NAME(message), + GST_MESSAGE_TYPE_NAME(message)); + } + } + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_DURATION: + renderer_type[type]->duration = GST_CLOCK_TIME_NONE; + break; + case GST_MESSAGE_BUFFERING: + if (hls_video) { + gint percent = -1; + gst_message_parse_buffering(message, &percent); + if (percent >= 0) { + renderer_type[type]->buffering_level = percent; + logger_log(logger, LOGGER_DEBUG, "Buffering :%u percent done", percent); + if (percent < 100) { + gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_PAUSED); + } else { + gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_PLAYING); + } + } + } + break; + case GST_MESSAGE_ERROR: { + GError *err; + gchar *debug; + gboolean flushing; + gst_message_parse_error (message, &err, &debug); + logger_log(logger, LOGGER_INFO, "GStreamer error: %s %s", GST_MESSAGE_SRC_NAME(message),err->message); + if (!hls_video && strstr(err->message,"Internal data stream error")) { + logger_log(logger, LOGGER_INFO, + "*** This is a generic GStreamer error that usually means that GStreamer\n" + "*** was unable to construct a working video pipeline.\n\n" + "*** If you are letting the default autovideosink select the videosink,\n" + "*** GStreamer may be trying to use non-functional hardware h264 video decoding.\n" + "*** Try using option -avdec to force software decoding or use -vs \n" + "*** to select a videosink of your choice (see \"man uxplay\").\n\n" + "*** Raspberry Pi models 4B and earlier using Video4Linux2 may need \"-bt709\" uxplay option"); + } + g_error_free (err); + g_free (debug); + if (renderer_type[type]->appsrc) { + gst_app_src_end_of_stream (GST_APP_SRC(renderer_type[type]->appsrc)); + } + gst_bus_set_flushing(bus, TRUE); + gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_READY); + renderer_type[type]->terminate = TRUE; + g_main_loop_quit( (GMainLoop *) loop); + break; + } + case GST_MESSAGE_EOS: + /* end-of-stream */ + logger_log(logger, LOGGER_INFO, "GStreamer: End-Of-Stream"); + if (hls_video) { + gst_bus_set_flushing(bus, TRUE); + gst_element_set_state (renderer_type[type]->pipeline, GST_STATE_READY); + renderer_type[type]->terminate = TRUE; + g_main_loop_quit( (GMainLoop *) loop); + } + break; + case GST_MESSAGE_STATE_CHANGED: + if (renderer_type[type]->state_pending && strstr(GST_MESSAGE_SRC_NAME(message), "pipeline")) { + GstState state; + gst_element_get_state(renderer_type[type]->pipeline, &state, NULL, 100 * GST_MSECOND); + if (state == GST_STATE_NULL) { + gst_element_set_state(renderer_type[type]->pipeline, GST_STATE_PLAYING); + } else if (state == GST_STATE_PLAYING) { + renderer_type[type]->state_pending = false; + } + } + if (renderer_type[type]->autovideo) { + char *sink = strstr(GST_MESSAGE_SRC_NAME(message), "-actual-sink-"); + if (sink) { + sink += strlen("-actual-sink-"); + if (strstr(GST_MESSAGE_SRC_NAME(message), renderer_type[type]->codec)) { + logger_log(logger, LOGGER_DEBUG, "GStreamer: automatically-selected videosink" + " (renderer %d: %s) is \"%ssink\"", renderer_type[type]->id + 1, + renderer_type[type]->codec, sink); +#ifdef X_DISPLAY_FIX + renderer_type[type]->use_x11 = (strstr(sink, "ximage") || strstr(sink, "xvimage")); +#endif + renderer_type[type]->autovideo = false; + } + } + } + break; +#ifdef X_DISPLAY_FIX + case GST_MESSAGE_ELEMENT: + if (renderer_type[type]->gst_window && renderer_type[type]->gst_window->window) { + GstNavigationMessageType message_type = gst_navigation_message_get_type (message); + if (message_type == GST_NAVIGATION_MESSAGE_EVENT) { + GstEvent *event = NULL; + if (gst_navigation_message_parse_event (message, &event)) { + GstNavigationEventType event_type = gst_navigation_event_get_type (event); + const gchar *key; + switch (event_type) { + case GST_NAVIGATION_EVENT_KEY_PRESS: + if (gst_navigation_event_parse_key_event (event, &key)) { + if ((strcmp (key, "F11") == 0) || (alt_keypress && strcmp (key, "Return") == 0)) { + fullscreen = !(fullscreen); + set_fullscreen(renderer_type[type]->gst_window, &fullscreen); + } else if (strcmp (key, "Alt_L") == 0) { + alt_keypress = true; + } + } + break; + case GST_NAVIGATION_EVENT_KEY_RELEASE: + if (gst_navigation_event_parse_key_event (event, &key)) { + if (strcmp (key, "Alt_L") == 0) { + alt_keypress = false; + } + } + default: + break; + } + } + if (event) { + gst_event_unref (event); + } + } + } + break; +#endif + default: + /* unhandled message */ + break; + } + return TRUE; +} + +void video_renderer_choose_codec (bool video_is_h265) { + g_assert(!hls_video); + /* set renderer to h264 or h265, depending on pps/sps received by raop_rtp_mirror */ + video_renderer_t *renderer_new = video_is_h265 ? renderer_type[1] : renderer_type[0]; + if (renderer == renderer_new) { + return; + } + video_renderer_t *renderer_prev = renderer; + renderer = renderer_new; + gst_video_pipeline_base_time = gst_element_get_base_time(renderer->appsrc); + /* it seems unlikely that the codec will change between h264 and h265 during a connection, + * but in case it does, we set the previous renderer to GST_STATE_NULL, detect + * when this is finished by listening for the bus message, and then reset it to + * GST_STATE_READY, so it can be reused if the codec changes again. */ + if (renderer_prev) { + gst_app_src_end_of_stream (GST_APP_SRC(renderer_prev->appsrc)); + gst_bus_set_flushing(renderer_prev->bus, TRUE); + /* set state of previous renderer to GST_STATE_NULL to (hopefully?) close its video window */ + gst_element_set_state (renderer_prev->pipeline, GST_STATE_NULL); + renderer_prev->state_pending = true; // will set state to PLAYING once state is NULL + } +} + +unsigned int video_reset_callback(void * loop) { + if (video_terminate) { + video_terminate = false; + if (renderer->appsrc) { + gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc)); + } + gboolean flushing = TRUE; + gst_bus_set_flushing(renderer->bus, flushing); + gst_element_set_state (renderer->pipeline, GST_STATE_NULL); + g_main_loop_quit( (GMainLoop *) loop); + } + return (unsigned int) TRUE; +} + +bool video_get_playback_info(double *duration, double *position, float *rate) { + gint64 pos = 0; + GstState state; + *duration = 0.0; + *position = -1.0; + *rate = 0.0f; + if (!renderer) { + + return true; + } + gst_element_get_state(renderer->pipeline, &state, NULL, 0); + *rate = 0.0f; + switch (state) { + case GST_STATE_PLAYING: + *rate = 1.0f; + default: + break; + } + + if (!GST_CLOCK_TIME_IS_VALID(renderer->duration)) { + if (!gst_element_query_duration (renderer->pipeline, GST_FORMAT_TIME, &renderer->duration)) { + return true; + } + } + *duration = ((double) renderer->duration) / GST_SECOND; + if (*duration) { + if (gst_element_query_position (renderer->pipeline, GST_FORMAT_TIME, &pos) && + GST_CLOCK_TIME_IS_VALID(pos)) { + *position = ((double) pos) / GST_SECOND; + } + } + + logger_log(logger, LOGGER_DEBUG, "********* video_get_playback_info: position %" GST_TIME_FORMAT " duration %" GST_TIME_FORMAT " %s *********", + GST_TIME_ARGS (pos), GST_TIME_ARGS (renderer->duration), gst_element_state_get_name(state)); + + return true; +} + +void video_renderer_seek(float position) { + double pos = (double) position; + pos *= GST_SECOND; + gint64 seek_position = (gint64) pos; + seek_position = seek_position < 1000 ? 1000 : seek_position; + seek_position = seek_position > renderer->duration - 1000 ? renderer->duration - 1000: seek_position; + g_print("SCRUB: seek to %f secs = %" GST_TIME_FORMAT ", duration = %" GST_TIME_FORMAT "\n", position, + GST_TIME_ARGS(seek_position), GST_TIME_ARGS(renderer->duration)); + gboolean result = gst_element_seek_simple(renderer->pipeline, GST_FORMAT_TIME, + (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), + seek_position); + if (result) { + g_print("seek succeeded\n"); + gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING); + } else { + g_print("seek failed\n"); + } +} + +unsigned int video_renderer_listen(void *loop, int id) { + g_assert(id >= 0 && id < n_renderers); + return (unsigned int) gst_bus_add_watch(renderer_type[id]->bus,(GstBusFunc) + gstreamer_pipeline_bus_callback, (gpointer) loop); +} + +void video_renderer_switch_source(bool use_video) { + if (!renderer || hls_video) { + return; + } + + GstElement *selector = gst_bin_get_by_name(GST_BIN(renderer->pipeline), "selector"); + if (!selector) { + logger_log(logger, LOGGER_ERR, "Failed to get input-selector element"); + return; + } + + // Switch to video source (sink_1) or static image (sink_0) + GstPad *pad = gst_element_get_static_pad(selector, use_video ? "sink_1" : "sink_0"); + if (!pad) { + logger_log(logger, LOGGER_ERR, "Failed to get pad for %s", use_video ? "video source" : "static image"); + gst_object_unref(selector); + return; + } + + g_object_set(selector, "active-pad", pad, NULL); + gst_object_unref(pad); + gst_object_unref(selector); + + logger_log(logger, LOGGER_DEBUG, "Switched to %s source", use_video ? "video" : "static image"); +} diff --git a/renderers/video_renderer.h b/renderers/video_renderer.h new file mode 100644 index 0000000..aecc72f --- /dev/null +++ b/renderers/video_renderer.h @@ -0,0 +1,75 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Modified for: + * UxPlay - An open-source AirPlay mirroring server + * Copyright (C) 2021-23 F. Duncanh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * H264 renderer using gstreamer +*/ + +#ifndef VIDEO_RENDERER_H +#define VIDEO_RENDERER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include "../lib/logger.h" + +typedef enum videoflip_e { + NONE, + LEFT, + RIGHT, + INVERT, + VFLIP, + HFLIP, +} videoflip_t; + +typedef struct video_renderer_s video_renderer_t; + +void video_renderer_init (logger_t *logger, const char *server_name, videoflip_t videoflip[2], const char *parser, + const char *decoder, const char *converter, const char *videosink, const char *videosink_options, + bool initial_fullscreen, bool video_sync, bool h265_support, const char *uri, const char *default_image); +void video_renderer_start (); +void video_renderer_stop (); +void video_renderer_pause (); +void video_renderer_seek(float position); +void video_renderer_resume (); +bool video_renderer_is_paused(); +void video_renderer_render_buffer (unsigned char* data, int *data_len, int *nal_count, uint64_t *ntp_time); +void video_renderer_flush (); +unsigned int video_renderer_listen(void *loop, int id); +void video_renderer_destroy (); +void video_renderer_size(float *width_source, float *height_source, float *width, float *height); +bool waiting_for_x11_window(); +bool video_get_playback_info(double *duration, double *position, float *rate); +void video_renderer_choose_codec(bool is_h265); +unsigned int video_renderer_listen(void *loop, int id); +unsigned int video_reset_callback(void *loop); +void video_renderer_switch_source(bool use_video); +#ifdef __cplusplus +} +#endif + +#endif //VIDEO_RENDERER_H + diff --git a/renderers/x_display_fix.h b/renderers/x_display_fix.h new file mode 100644 index 0000000..90d5180 --- /dev/null +++ b/renderers/x_display_fix.h @@ -0,0 +1,119 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Modified for: + * UxPlay - An open-source AirPlay mirroring server + * Copyright (C) 2021-23 F. Duncanh + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* based on code from David Ventura https://github.com/DavidVentura/UxPlay */ + +/* This file should be only included from video_renderer.c as it defines static + * functions and depends on video_renderer internals */ + +#ifndef X_DISPLAY_FIX_H +#define X_DISPLAY_FIX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +struct X11_Window_s { + Display * display; + Window window; +} typedef X11_Window_t; + +static void get_X11_Display(X11_Window_t * X11) { + X11->display = XOpenDisplay(NULL); + X11->window = (Window) NULL; +} + +static Window enum_windows(const char * str, Display * display, Window window, int depth) { + int i; + XTextProperty text; + XGetWMName(display, window, &text); + char* name = NULL; + XFetchName(display, window, &name); + if (name != 0 && strcmp(str, name) == 0) { + return window; + } + Window _root, parent; + Window* children = NULL; + unsigned int n; + XQueryTree(display, window, &_root, &parent, &children, &n); + if (children != NULL) { + for (i = 0; i < n; i++) { + Window w = enum_windows(str, display, children[i], depth + 1); + if (w) return w; + } + XFree(children); + } + return (Window) NULL; +} + +int X11_error_catcher( Display *disp, XErrorEvent *xe ) { + // do nothing + return 0; +} + +static void get_x_window(X11_Window_t * X11, const char * name) { + Window root = XDefaultRootWindow(X11->display); + XSetErrorHandler(X11_error_catcher); + X11->window = enum_windows(name, X11->display, root, 0); + XSetErrorHandler(NULL); +#ifdef ZOOM_WINDOW_NAME_FIX + if (X11->window) { + Atom _NET_WM_NAME = XInternAtom(X11->display, "_NET_WM_NAME", 0); + Atom UTF8_STRING = XInternAtom(X11->display, "UTF8_STRING", 0); + XChangeProperty(X11->display, X11->window, _NET_WM_NAME, UTF8_STRING, + 8, 0, (const unsigned char *) name, strlen(name)); + XSync(X11->display, False); + } +#endif +} + +static void set_fullscreen(X11_Window_t * X11, bool * fullscreen) { + XClientMessageEvent msg = { + .type = ClientMessage, + .display = X11->display, + .window = X11->window, + .message_type = XInternAtom(X11->display, "_NET_WM_STATE", True), + .format = 32, + .data = { .l = { + *fullscreen, + XInternAtom(X11->display, "_NET_WM_STATE_FULLSCREEN", True), + None, + 0, + 1 + }} + }; + XSendEvent(X11->display, XRootWindow(X11->display, XDefaultScreen(X11->display)), + False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &msg); + XSync(X11->display, False); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/uxplay.1 b/uxplay.1 new file mode 100644 index 0000000..8b79965 --- /dev/null +++ b/uxplay.1 @@ -0,0 +1,185 @@ +.TH UXPLAY "1" "December 2024" "1.71" "User Commands" +.SH NAME +uxplay \- start AirPlay server +.SH SYNOPSIS +.B uxplay +[\fI\,-n name\/\fR] [\fI\,-s wxh\/\fR] [\fI\,-p \/\fR[\fI\,n\/\fR]] [more \fI OPTIONS \/\fR ...] +.SH DESCRIPTION +UxPlay 1.71: An open\-source AirPlay mirroring (+ audio streaming) server: +.SH OPTIONS +.TP +.B +\fB\-n\fR name Specify the network name of the AirPlay server +.TP +\fB\-nh\fR Do \fBNOT\fR append "@\fIhostname\fR" at end of AirPlay server name +.TP +\fB\-h265\fR Support h265 (4K) video (with h265 versions of h264 plugins) +.TP +\fB\-hls\fR Support HTTP Live Streaming (currently YouTube video only) +.TP +\fB\-pin\fI[xxxx]\fRUse a 4-digit pin code to control client access (default: no) +.IP + without option, pin is random: optionally use fixed pin xxxx. +.TP +\fB\-reg\fI [fn]\fR Keep a register in $HOME/.uxplay.register to verify returning +.IP + client pin-registration; (option: use file "fn" for this) +.TP +\fB\-vsync\fI[x]\fR Mirror mode: sync audio to video using timestamps (default) +.IP + \fIx\fR is optional audio delay: millisecs, decimal, can be neg. +.TP +\fB\-vsync\fR no Switch off audio/(server)video timestamp synchronization. +.TP +\fB\-async\fR[\fIx\fR] Audio-Only mode: sync audio to client video (default: no). +.TP +\fB\-async\fR no Switch off audio/(client)video timestamp synchronization. +.TP +\fB\-db\fI l[:h]\fR Set minumum volume attenuation to l dB (decibels, negative); +.IP + optional: set maximum to h dB (+ or -); default -30.0:0.0 +.PP +.TP +\fB\-taper\fR Use a "tapered" AirPlay volume-control profile. +.TP +\fB\-s\fR wxh[@r]Request to client for video display resolution [refresh_rate] +.IP + default 1920x1080[@60] (or 3840x2160[@60] with -h265 option). +.PP +.TP +\fB\-o\fR Set display "overscanned" mode on (not usually needed) +.TP +\fB-fs\fR Full-screen (only works with X11, Wayland, VAAPI, D3D11) +.TP +\fB\-p\fR Use legacy ports UDP 6000:6001:7011 TCP 7000:7001:7100 +.TP +\fB\-p\fR n Use TCP and UDP ports n,n+1,n+2. range 1024\-65535 +.IP + use "\-p n1,n2,n3" to set each port, "n1,n2" for n3 = n2+1 +.IP + "\-p tcp n" or "\-p udp n" sets TCP or UDP ports separately. +.PP +.TP +\fB\-avdec\fR Force software h264 video decoding with libav decoder. +.TP +\fB\-vp\fI prs \fR Choose GStreamer h264 parser; default "h264parse" +.TP +\fB\-vd\fI dec \fR Choose GStreamer h264 decoder; default "decodebin" +.IP + choices: (software) avdec_h264; (hardware) v4l2h264dec, +.IP + nvdec, nvh264dec, vaapih264dec, vtdec, ... +.TP +\fB\-vc\fI cnv \fR Choose GStreamer videoconverter; default "videoconvert" +.IP + another choice when using v4l2h264dec: v4l2convert. +.TP +\fB\-vs\fI sink\fR Choose the GStreamer videosink; default "autovideosink" +.IP + choices: ximagesink,xvimagesink,vaapisink,glimagesink, +.IP + gtksink,waylandsink,osxvideosink,kmssink,d3d11videosink,... +.PP +.TP +\fB\-vs\fR 0 Streamed audio only, with no video display window. +.TP +\fB\-v4l2\fR Use Video4Linux2 for GPU hardware h264 video decoding. +.TP +\fB\-bt709\fR Sometimes needed for Raspberry Pi models using Video4Linux2. +.TP +\fB\-as\fI sink\fR Choose the GStreamer audiosink; default "autoaudiosink" +.IP + choices:pulsesink,alsasink,pipewiresink,osssink,oss4sink, +.IP + jackaudiosink,osxaudiosink,wasapisink,directsoundsink,.. +.PP +.TP +\fB\-as\fR 0 (or \fB\-a\fR) Turn audio off, streamed video only. +.TP +\fB\-al\fR x Audio latency in seconds (default 0.25) reported to client. +.TP +\fB\-ca\fI fn \fR In Airplay Audio (ALAC) mode, write cover-art to file fn. +.TP +\fB\-reset\fR n Reset after 3n seconds client silence (default 5, 0=never). +.TP +\fB\-nofreeze\fR Do NOT leave frozen screen in place after reset. +.TP +\fB\-nc\fR Do NOT close video window when client stops mirroring +.TP +\fB\-nohold\fR Drop current connection when new client connects. +.TP +\fB\-restrict\fR Restrict clients to those specified by "-allow deviceID". +.IP + Uxplay displays deviceID when a client attempts to connect. +.IP + Use "-restrict no" for no client restrictions (default). +.PP +\fB\-allow\fR id Permit deviceID = id to connect if restrictions are imposed. +.TP +\fB\-block\fR id Always block connections from deviceID = id. +.TP +\fB\-FPSdata\fR Show video-streaming performance reports sent by client. +.TP +\fB\-fps\fR n Set maximum allowed streaming framerate, default 30 +.TP +\fB\-f\fR {H|V|I}Horizontal|Vertical flip, or both=Inversion=rotate 180 deg +.TP +\fB\-r\fR {R|L} Rotate 90 degrees Right (cw) or Left (ccw) +.TP +\fB\-m\fI [mac]\fR Set MAC address (also Device ID); use for concurrent UxPlays +.IP + if mac xx:xx:xx:xx:xx:xx is not given, a random MAC is used. +.PP +.TP +\fB\-key\fI [fn]\fR Store private key in $HOME/.uxplay.pem (or in file "fn") +.PP +.TP +\fB\-dacp\fI [fn]\fRExport client DACP information to file $HOME/.uxplay.dacp +.IP + (option to use file "fn" instead); used for client remote. +.PP +.TP +\fB\-vdmp\fR [n] Dump h264 video output to "fn.h264"; fn="videodump", change +.IP + with "-vdmp [n] filename". If [n] is given, file fn.x.h264 +.IP + x=1,2,.. opens whenever a new SPS/PPS NAL arrives, and <=n +.IP + NAL units are dumped. +.PP +.TP +\fB\-admp\fR [n] Dump audio output to "fn.x.fmt", fmt ={aac, alac, aud}, x +.IP + =1,2,..; fn="audiodump"; change with "-admp [n] filename". +.IP + x increases when audio format changes. If n is given, <= n +.IP + audio packets are dumped. "aud"= unknown format. +.PP +.TP +\fB\-d\fR Enable debug logging +.TP +\fB\-v\fR Displays version information +.TP +\fB\-h\fR Displays help information +.SH +FILES +Options in one of $UXPLAYRC, or ~/.uxplayrc, or ~/.config/uxplayrc +.TP +are applied first (command-line options may modify them). Format: +.TP +one option per line,\fI no\fR initial "-"; lines beginning with "#" ignored. +.SH +AUTHORS +.TP +Various, see website or distribution. +.SH +COPYRIGHT +.TP +Various, see website or distribution. License: GPL v3+: +.TP +GNU GPL version 3 or later. (some parts LGPL v.2.1+ or MIT). +.SH +SEE ALSO +.TP +Website: diff --git a/uxplay.cpp b/uxplay.cpp new file mode 100644 index 0000000..867329b --- /dev/null +++ b/uxplay.cpp @@ -0,0 +1,2360 @@ +/** + * RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi + * Copyright (C) 2019 Florian Draschbacher + * Modified extensively to become + * UxPlay - An open-souce AirPlay mirroring server. + * Modifications Copyright (C) 2021-23 F. Duncanh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 /*modifications for Windows compilation */ +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +# ifdef __linux__ +# include +# else +# include +# endif +#endif + +#include "lib/raop.h" +#include "lib/stream.h" +#include "lib/logger.h" +#include "lib/dnssd.h" +#include "renderers/video_renderer.h" +#include "renderers/audio_renderer.h" + +#define VERSION "1.71" + +#define SECOND_IN_USECS 1000000 +#define SECOND_IN_NSECS 1000000000UL +#define DEFAULT_NAME "UxPlay" +#define DEFAULT_DEBUG_LOG false +#define LOWEST_ALLOWED_PORT 1024 +#define HIGHEST_PORT 65535 +#define NTP_TIMEOUT_LIMIT 5 +#define BT709_FIX "capssetter caps=\"video/x-h264, colorimetry=bt709\"" + +static std::string server_name = DEFAULT_NAME; +static dnssd_t *dnssd = NULL; +static raop_t *raop = NULL; +static logger_t *render_logger = NULL; +static bool audio_sync = false; +static bool video_sync = true; +static int64_t audio_delay_alac = 0; +static int64_t audio_delay_aac = 0; +static bool relaunch_video = false; +static bool reset_loop = false; +static unsigned int open_connections= 0; +static std::string videosink = "autovideosink"; +static std::string videosink_options = ""; +static std::string default_image = ""; // Add this line +static videoflip_t videoflip[2] = { NONE , NONE }; +static bool use_video = true; +static unsigned char compression_type = 0; +static std::string audiosink = "autoaudiosink"; +static int audiodelay = -1; +static bool use_audio = true; +static bool new_window_closing_behavior = true; +static bool close_window; +static std::string video_parser = "h264parse"; +static std::string video_decoder = "decodebin"; +static std::string video_converter = "videoconvert"; +static bool show_client_FPS_data = false; +static unsigned int max_ntp_timeouts = NTP_TIMEOUT_LIMIT; +static FILE *video_dumpfile = NULL; +static std::string video_dumpfile_name = "videodump"; +static int video_dump_limit = 0; +static int video_dumpfile_count = 0; +static int video_dump_count = 0; +static bool dump_video = false; +static unsigned char mark[] = { 0x00, 0x00, 0x00, 0x01 }; +static FILE *audio_dumpfile = NULL; +static std::string audio_dumpfile_name = "audiodump"; +static int audio_dump_limit = 0; +static int audio_dumpfile_count = 0; +static int audio_dump_count = 0; +static bool dump_audio = false; +static unsigned char audio_type = 0x00; +static unsigned char previous_audio_type = 0x00; +static bool fullscreen = false; +static std::string coverart_filename = ""; +static bool do_append_hostname = true; +static bool use_random_hw_addr = false; +static unsigned short display[5] = {0}, tcp[3] = {0}, udp[3] = {0}; +static bool debug_log = DEFAULT_DEBUG_LOG; +static int log_level = LOGGER_INFO; +static bool bt709_fix = false; +static int nohold = 0; +static bool nofreeze = false; +static unsigned short raop_port; +static unsigned short airplay_port; +static uint64_t remote_clock_offset = 0; +static std::vector allowed_clients; +static std::vector blocked_clients; +static bool restrict_clients; +static bool setup_legacy_pairing = false; +static bool require_password = false; +static unsigned short pin = 0; +static std::string keyfile = ""; +static std::string mac_address = ""; +static std::string dacpfile = ""; +static bool registration_list = false; +static std::string pairing_register = ""; +static std::vector registered_keys; +static double db_low = -30.0; +static double db_high = 0.0; +static bool taper_volume = false; +static bool h265_support = false; +static int n_renderers = 0; +static bool hls_support = false; +static std::string url = ""; +static guint gst_x11_window_id = 0; +static guint gst_hls_position_id = 0; +static bool preserve_connections = false; + +/* logging */ + +static void log(int level, const char* format, ...) { + va_list vargs; + if (level > log_level) return; + switch (level) { + case 0: + case 1: + case 2: + case 3: + printf("*** ERROR: "); + break; + case 4: + printf("*** WARNING: "); + break; + default: + break; + } + va_start(vargs, format); + vprintf(format, vargs); + printf("\n"); + va_end(vargs); +} + +#define LOGD(...) log(LOGGER_DEBUG, __VA_ARGS__) +#define LOGI(...) log(LOGGER_INFO, __VA_ARGS__) +#define LOGW(...) log(LOGGER_WARNING, __VA_ARGS__) +#define LOGE(...) log(LOGGER_ERR, __VA_ARGS__) + +static bool file_has_write_access (const char * filename) { + bool exists = false; + bool write = false; +#ifdef _WIN32 + if ((exists = _access(filename, 0) != -1)) { + write = (_access(filename, 2) != -1); + } +#else + if ((exists = access(filename, F_OK) != -1)) { + write = (access(filename, W_OK) != -1); + } +#endif + if (!exists) { + FILE *fp = fopen(filename, "w"); + if (fp) { + write = true; + fclose(fp); + remove(filename); + } + } + return write; +} + +/* 95 byte png file with a 1x1 white square (single pixel): placeholder for coverart*/ +static const unsigned char empty_image[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xdb, 0x56, + 0xca, 0x00, 0x00, 0x00, 0x03, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xa7, 0x7a, 0x3d, 0xda, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, + 0x0a, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xe2, + 0x21, 0xbc, 0x33, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + +static size_t write_coverart(const char *filename, const void *image, size_t len) { + FILE *fp = fopen(filename, "wb"); + size_t count = fwrite(image, 1, len, fp); + fclose(fp); + return count; +} + +static char *create_pin_display(char *pin_str, int margin, int gap) { + char *ptr; + char num[2] = { 0 }; + int w = 10; + int h = 8; + char digits[10][8][11] = { "0821111380", "2114005113", "1110000111", "1110000111", "1110000111", "1110000111", "5113002114", "0751111470", + "0002111000", "0021111000", "0000111000", "0000111000", "0000111000", "0000111000", "0000111000", "0011111110", + "0811112800", "2114005113", "0000000111", "0000082114", "0862111470", "2114700000", "1117000000", "1111111111", + "0821111380", "2114005113", "0000082114", "0000111170", "0000075130", "1110000111", "5113002114", "0751111470", + "0000211110", "0001401110", "0021401110", "0214001110", "2110001110", "1111111111", "0000001110", "0000001110", + "1111111110", "1110000000", "1110000000", "1112111380", "0000075113", "0000000111", "5113002114", "0711114700", + "0821111380", "2114005113", "1110000000", "1112111380", "1114075113", "1110000111", "5113002114", "0751111470", + "1111111111", "0000002114", "0000021140", "0000211400", "0002114000", "0021140000", "0211400000", "2114000000", + "0831111280", "2114002114", "5113802114", "0751111170", "8214775138", "1110000111", "5113002114", "0751111470", + "0821111380", "2114005113", "1110000111", "5113802111", "0751114111", "0000000111", "5113002114", "0751111470" + }; + + char pixels[9] = { ' ', '8', 'd', 'b', 'P', 'Y', 'o', '"', '.' }; + /* Ascii art used here is derived from the FIGlet font "collosal" */ + + int pin_val = (int) strtoul(pin_str, &ptr, 10); + if (*ptr) { + return NULL; + } + int len = strlen(pin_str); + int *pin = (int *) calloc( len, sizeof(int)); + if(!pin) { + return NULL; + } + + for (int i = 0; i < len; i++) { + pin[len - 1 - i] = pin_val % 10; + pin_val = pin_val / 10; + } + + int size = 4 + h*(margin + len*(w + gap + 1)); + char *pin_image = (char *) calloc(size, sizeof(char)); + if (!pin_image) { + return NULL; + } + char *pos = pin_image; + snprintf(pos, 2, "\n"); + pos++; + + for (int i = 0; i < h; i++) { + for (int j = 0; j < margin; j++) { + snprintf(pos, 2, " "); + pos++; + } + + for (int j = 0; j < len; j++) { + int l = pin[j]; + char *p = digits[l][i]; + for (int k = 0; k < w; k++) { + char *ptr; + strncpy(num, p++, 1); + int r = (int) strtoul(num, &ptr, 10); + snprintf(pos, 2, "%c", pixels[r]); + pos++; + } + for (int n=0; n < gap ; n++) { + snprintf(pos, 2, " "); + pos++; + } + } + snprintf(pos, 2, "\n"); + pos++; + } + snprintf(pos, 2, "\n"); + return pin_image; +} + +static void dump_audio_to_file(unsigned char *data, int datalen, unsigned char type) { + if (!audio_dumpfile && audio_type != previous_audio_type) { + char suffix[20]; + std::string fn = audio_dumpfile_name; + previous_audio_type = audio_type; + audio_dumpfile_count++; + audio_dump_count = 0; + /* type 0x20 is lossless ALAC, type 0x80 is compressed AAC-ELD, type 0x10 is "other" */ + if (audio_type == 0x20) { + snprintf(suffix, sizeof(suffix), ".%d.alac", audio_dumpfile_count); + } else if (audio_type == 0x80) { + snprintf(suffix, sizeof(suffix), ".%d.aac", audio_dumpfile_count); + } else { + snprintf(suffix, sizeof(suffix), ".%d.aud", audio_dumpfile_count); + } + fn.append(suffix); + audio_dumpfile = fopen(fn.c_str(),"w"); + if (audio_dumpfile == NULL) { + LOGE("could not open file %s for dumping audio frames",fn.c_str()); + } + } + + if (audio_dumpfile) { + fwrite(data, 1, datalen, audio_dumpfile); + if (audio_dump_limit) { + audio_dump_count++; + if (audio_dump_count == audio_dump_limit) { + fclose(audio_dumpfile); + audio_dumpfile = NULL; + } + } + } +} + +static void dump_video_to_file(unsigned char *data, int datalen) { + /* SPS NAL has (data[4] & 0x1f) = 0x07 */ + if ((data[4] & 0x1f) == 0x07 && video_dumpfile && video_dump_limit) { + fwrite(mark, 1, sizeof(mark), video_dumpfile); + fclose(video_dumpfile); + video_dumpfile = NULL; + video_dump_count = 0; + } + + if (!video_dumpfile) { + std::string fn = video_dumpfile_name; + if (video_dump_limit) { + char suffix[20]; + video_dumpfile_count++; + snprintf(suffix, sizeof(suffix), ".%d", video_dumpfile_count); + fn.append(suffix); + } + fn.append(".h264"); + video_dumpfile = fopen (fn.c_str(),"w"); + if (video_dumpfile == NULL) { + LOGE("could not open file %s for dumping h264 frames",fn.c_str()); + } + } + + if (video_dumpfile) { + if (video_dump_limit == 0) { + fwrite(data, 1, datalen, video_dumpfile); + } else if (video_dump_count < video_dump_limit) { + video_dump_count++; + fwrite(data, 1, datalen, video_dumpfile); + } + } +} + +static gboolean reset_callback(gpointer loop) { + if (reset_loop) { + g_main_loop_quit((GMainLoop *) loop); + } + return TRUE; +} + +static gboolean x11_window_callback(gpointer loop) { + /* called while trying to find an x11 window used by playbin (HLS mode) */ + if (waiting_for_x11_window()) { + return TRUE; + } + g_source_remove(gst_x11_window_id); + gst_x11_window_id = 0; + return FALSE; +} + +static gboolean sigint_callback(gpointer loop) { + relaunch_video = false; + g_main_loop_quit((GMainLoop *) loop); + return TRUE; +} + +static gboolean sigterm_callback(gpointer loop) { + relaunch_video = false; + g_main_loop_quit((GMainLoop *) loop); + return TRUE; +} + +#ifdef _WIN32 +struct signal_handler { + GSourceFunc handler; + gpointer user_data; +}; + +static std::unordered_map u = {}; + +static void SignalHandler(int signum) { + if (signum == SIGTERM || signum == SIGINT) { + u[signum].handler(u[signum].user_data); + } +} + +static guint g_unix_signal_add(gint signum, GSourceFunc handler, gpointer user_data) { + u[signum] = signal_handler{handler, user_data}; + (void) signal(signum, SignalHandler); + return 0; +} +#endif + +static void main_loop() { + guint gst_bus_watch_id[2] = { 0 }; + g_assert(n_renderers <= 2); + GMainLoop *loop = g_main_loop_new(NULL,FALSE); + relaunch_video = false; + if (use_video) { + relaunch_video = true; + if (url.empty()) { + n_renderers = h265_support ? 2 : 1; + gst_x11_window_id = 0; + } else { + /* hls video will be rendered */ + n_renderers = 1; + url.erase(); + gst_x11_window_id = g_timeout_add(100, (GSourceFunc) x11_window_callback, (gpointer) loop); + } + for (int i = 0; i < n_renderers; i++) { + gst_bus_watch_id[i] = (guint) video_renderer_listen((void *)loop, i); + } + } + guint reset_watch_id = g_timeout_add(100, (GSourceFunc) reset_callback, (gpointer) loop); + guint video_reset_watch_id = g_timeout_add(100, (GSourceFunc) video_reset_callback, (gpointer) loop); + guint sigterm_watch_id = g_unix_signal_add(SIGTERM, (GSourceFunc) sigterm_callback, (gpointer) loop); + guint sigint_watch_id = g_unix_signal_add(SIGINT, (GSourceFunc) sigint_callback, (gpointer) loop); + g_main_loop_run(loop); + + for (int i = 0; i < n_renderers; i++) { + if (gst_bus_watch_id[i] > 0) g_source_remove(gst_bus_watch_id[i]); + } + if (gst_x11_window_id > 0) g_source_remove(gst_x11_window_id); + if (sigint_watch_id > 0) g_source_remove(sigint_watch_id); + if (sigterm_watch_id > 0) g_source_remove(sigterm_watch_id); + if (reset_watch_id > 0) g_source_remove(reset_watch_id); + if (video_reset_watch_id > 0) g_source_remove(video_reset_watch_id); + g_main_loop_unref(loop); +} + +static int parse_hw_addr (std::string str, std::vector &hw_addr) { + for (int i = 0; i < (int) str.length(); i += 3) { + hw_addr.push_back((char) stol(str.substr(i), NULL, 16)); + } + return 0; +} + +static const char *get_homedir() { + const char *homedir = getenv("XDG_CONFIG_HOMEDIR"); + if (homedir == NULL) { + homedir = getenv("HOME"); + } +#ifndef _WIN32 + if (homedir == NULL){ + homedir = getpwuid(getuid())->pw_dir; + } +#endif + return homedir; +} + +static std::string find_uxplay_config_file() { + std::string no_config_file = ""; + const char *homedir = NULL; + const char *uxplayrc = NULL; + std::string config0, config1, config2; + struct stat sb; + uxplayrc = getenv("UXPLAYRC"); /* first look for $UXPLAYRC */ + if (uxplayrc) { + config0 = uxplayrc; + if (stat(config0.c_str(), &sb) == 0) return config0; + } + homedir = get_homedir(); + if (homedir) { + config1 = homedir; + config1.append("/.uxplayrc"); + if (stat(config1.c_str(), &sb) == 0) return config1; /* look for ~/.uxplayrc */ + config2 = homedir; + config2.append("/.config/uxplayrc"); /* look for ~/.config/uxplayrc */ + if (stat(config2.c_str(), &sb) == 0) return config2; + } + return no_config_file; +} + +static std::string find_mac () { +/* finds the MAC address of a network interface * + * in a Windows, Linux, *BSD or macOS system. */ + std::string mac = ""; + char str[3]; +#ifdef _WIN32 + ULONG buflen = sizeof(IP_ADAPTER_ADDRESSES); + PIP_ADAPTER_ADDRESSES addresses = (IP_ADAPTER_ADDRESSES*) malloc(buflen); + if (addresses == NULL) { + return mac; + } + if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, addresses, &buflen) == ERROR_BUFFER_OVERFLOW) { + free(addresses); + addresses = (IP_ADAPTER_ADDRESSES*) malloc(buflen); + if (addresses == NULL) { + return mac; + } + } + if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, addresses, &buflen) == NO_ERROR) { + for (PIP_ADAPTER_ADDRESSES address = addresses; address != NULL; address = address->Next) { + if (address->PhysicalAddressLength != 6 /* MAC has 6 octets */ + || (address->IfType != 6 && address->IfType != 71) /* Ethernet or Wireless interface */ + || address->OperStatus != 1) { /* interface is up */ + continue; + } + mac.erase(); + for (int i = 0; i < 6; i++) { + snprintf(str, sizeof(str), "%02x", int(address->PhysicalAddress[i])); + mac = mac + str; + if (i < 5) mac = mac + ":"; + } + break; + } + } + free(addresses); + return mac; +#else + struct ifaddrs *ifap, *ifaptr; + int non_null_octets = 0; + unsigned char octet[6]; + if (getifaddrs(&ifap) == 0) { + for(ifaptr = ifap; ifaptr != NULL; ifaptr = ifaptr->ifa_next) { + if(ifaptr->ifa_addr == NULL) continue; +#ifdef __linux__ + if (ifaptr->ifa_addr->sa_family != AF_PACKET) continue; + struct sockaddr_ll *s = (struct sockaddr_ll*) ifaptr->ifa_addr; + for (int i = 0; i < 6; i++) { + if ((octet[i] = s->sll_addr[i]) != 0) non_null_octets++; + } +#else /* macOS and *BSD */ + if (ifaptr->ifa_addr->sa_family != AF_LINK) continue; + unsigned char *ptr = (unsigned char *) LLADDR((struct sockaddr_dl *) ifaptr->ifa_addr); + for (int i= 0; i < 6 ; i++) { + if ((octet[i] = *ptr) != 0) non_null_octets++; + ptr++; + } +#endif + if (non_null_octets) { + mac.erase(); + for (int i = 0; i < 6 ; i++) { + snprintf(str, sizeof(str), "%02x", octet[i]); + mac = mac + str; + if (i < 5) mac = mac + ":"; + } + break; + } + } + } + freeifaddrs(ifap); +#endif + return mac; +} + +#define MULTICAST 0 +#define LOCAL 1 +#define OCTETS 6 + +static bool validate_mac(char * mac_address) { + char c; + if (strlen(mac_address) != 17) return false; + for (int i = 0; i < 17; i++) { + c = *(mac_address + i); + if (i % 3 == 2) { + if (c != ':') return false; + } else { + if (c < '0') return false; + if (c > '9' && c < 'A') return false; + if (c > 'F' && c < 'a') return false; + if (c > 'f') return false; + } + } + return true; +} + +static std::string random_mac () { + char str[3]; + int octet = rand() % 64; + octet = (octet << 1) + LOCAL; + octet = (octet << 1) + MULTICAST; + snprintf(str,3,"%02x",octet); + std::string mac_address(str); + for (int i = 1; i < OCTETS; i++) { + mac_address = mac_address + ":"; + octet = rand() % 256; + snprintf(str,3,"%02x",octet); + mac_address = mac_address + str; + } + return mac_address; +} + +static void print_info (char *name) { + printf("UxPlay %s: An open-source AirPlay mirroring server.\n", VERSION); + printf("=========== Website: https://github.com/FDH2/UxPlay ==========\n"); + printf("Usage: %s [-n name] [-s wxh] [-p [n]] [(other options)]\n", name); + printf("Options:\n"); + printf("-n name Specify the network name of the AirPlay server\n"); + printf("-nh Do not add \"@hostname\" at the end of AirPlay server name\n"); + printf("-h265 Support h265 (4K) video (with h265 versions of h264 plugins)\n"); + printf("-hls Support HTTP Live Streaming (currently Youtube video only) \n"); + printf("-pin[xxxx]Use a 4-digit pin code to control client access (default: no)\n"); + printf("-di Set default image file to display when video is not available\n"); + printf(" default pin is random: optionally use fixed pin xxxx\n"); + printf("-reg [fn] Keep a register in $HOME/.uxplay.register to verify returning\n"); + printf(" client pin-registration; (option: use file \"fn\" for this)\n"); + printf("-vsync [x]Mirror mode: sync audio to video using timestamps (default)\n"); + printf(" x is optional audio delay: millisecs, decimal, can be neg.\n"); + printf("-vsync no Switch off audio/(server)video timestamp synchronization \n"); + printf("-async [x]Audio-Only mode: sync audio to client video (default: no)\n"); + printf("-async no Switch off audio/(client)video timestamp synchronization\n"); + printf("-db l[:h] Set minimum volume attenuation to l dB (decibels, negative);\n"); + printf(" optional: set maximum to h dB (+ or -) default: -30.0:0.0 dB\n"); + printf("-taper Use a \"tapered\" AirPlay volume-control profile\n"); + printf("-s wxh[@r]Request to client for video display resolution [refresh_rate]\n"); + printf(" default 1920x1080[@60] (or 3840x2160[@60] with -h265 option)\n"); + printf("-o Set display \"overscanned\" mode on (not usually needed)\n"); + printf("-fs Full-screen (only works with X11, Wayland, VAAPI, D3D11)\n"); + printf("-p Use legacy ports UDP 6000:6001:7011 TCP 7000:7001:7100\n"); + printf("-p n Use TCP and UDP ports n,n+1,n+2. range %d-%d\n", LOWEST_ALLOWED_PORT, HIGHEST_PORT); + printf(" use \"-p n1,n2,n3\" to set each port, \"n1,n2\" for n3 = n2+1\n"); + printf(" \"-p tcp n\" or \"-p udp n\" sets TCP or UDP ports separately\n"); + printf("-avdec Force software h264 video decoding with libav decoder\n"); + printf("-vp ... Choose the GSteamer h264 parser: default \"h264parse\"\n"); + printf("-vd ... Choose the GStreamer h264 decoder; default \"decodebin\"\n"); + printf(" choices: (software) avdec_h264; (hardware) v4l2h264dec,\n"); + printf(" nvdec, nvh264dec, vaapih64dec, vtdec,etc.\n"); + printf(" choices: avdec_h264,vaapih264dec,nvdec,nvh264dec,v4l2h264dec\n"); + printf("-vc ... Choose the GStreamer videoconverter; default \"videoconvert\"\n"); + printf(" another choice when using v4l2h264dec: v4l2convert\n"); + printf("-vs ... Choose the GStreamer videosink; default \"autovideosink\"\n"); + printf(" some choices: ximagesink,xvimagesink,vaapisink,glimagesink,\n"); + printf(" gtksink,waylandsink,osxvideosink,kmssink,d3d11videosink etc.\n"); + printf("-vs 0 Streamed audio only, with no video display window\n"); + printf("-v4l2 Use Video4Linux2 for GPU hardware h264 decoding\n"); + printf("-bt709 Sometimes needed for Raspberry Pi models using Video4Linux2 \n"); + printf("-as ... Choose the GStreamer audiosink; default \"autoaudiosink\"\n"); + printf(" some choices:pulsesink,alsasink,pipewiresink,jackaudiosink,\n"); + printf(" osssink,oss4sink,osxaudiosink,wasapisink,directsoundsink.\n"); + printf("-as 0 (or -a) Turn audio off, streamed video only\n"); + printf("-al x Audio latency in seconds (default 0.25) reported to client.\n"); + printf("-ca In Airplay Audio (ALAC) mode, write cover-art to file \n"); + printf("-reset n Reset after 3n seconds client silence (default %d, 0=never)\n", NTP_TIMEOUT_LIMIT); + printf("-nofreeze Do NOT leave frozen screen in place after reset\n"); + printf("-nc Do NOT Close video window when client stops mirroring\n"); + printf("-nohold Drop current connection when new client connects.\n"); + printf("-restrict Restrict clients to those specified by \"-allow \"\n"); + printf(" UxPlay displays deviceID when a client attempts to connect\n"); + printf(" Use \"-restrict no\" for no client restrictions (default)\n"); + printf("-allow Permit deviceID = to connect if restrictions are imposed\n"); + printf("-block Always block connections from deviceID = \n"); + printf("-FPSdata Show video-streaming performance reports sent by client.\n"); + printf("-fps n Set maximum allowed streaming framerate, default 30\n"); + printf("-f {H|V|I}Horizontal|Vertical flip, or both=Inversion=rotate 180 deg\n"); + printf("-r {R|L} Rotate 90 degrees Right (cw) or Left (ccw)\n"); + printf("-m [mac] Set MAC address (also Device ID);use for concurrent UxPlays\n"); + printf(" if mac xx:xx:xx:xx:xx:xx is not given, a random MAC is used\n"); + printf("-key [fn] Store private key in $HOME/.uxplay.pem (or in file \"fn\")\n"); + printf("-dacp [fn]Export client DACP information to file $HOME/.uxplay.dacp\n"); + printf(" (option to use file \"fn\" instead); used for client remote\n"); + printf("-vdmp [n] Dump h264 video output to \"fn.h264\"; fn=\"videodump\",change\n"); + printf(" with \"-vdmp [n] filename\". If [n] is given, file fn.x.h264\n"); + printf(" x=1,2,.. opens whenever a new SPS/PPS NAL arrives, and <=n\n"); + printf(" NAL units are dumped.\n"); + printf("-admp [n] Dump audio output to \"fn.x.fmt\", fmt ={aac, alac, aud}, x\n"); + printf(" =1,2,..; fn=\"audiodump\"; change with \"-admp [n] filename\".\n"); + printf(" x increases when audio format changes. If n is given, <= n\n"); + printf(" audio packets are dumped. \"aud\"= unknown format.\n"); + printf("-d Enable debug logging\n"); + printf("-v Displays version information\n"); + printf("-h Displays this help\n"); + printf("Startup options in $UXPLAYRC, ~/.uxplayrc, or ~/.config/uxplayrc are\n"); + printf("applied first (command-line options may modify them): format is one \n"); + printf("option per line, no initial \"-\"; lines starting with \"#\" are ignored.\n"); +} + +static bool option_has_value(const int i, const int argc, std::string option, const char *next_arg) { + if (i >= argc - 1 || next_arg[0] == '-') { + LOGE("invalid: \"%s\" had no argument", option.c_str()); + return false; + } + return true; +} + +static bool get_display_settings (std::string value, unsigned short *w, unsigned short *h, unsigned short *r) { + // assume str = wxh@r is valid if w and h are positive decimal integers + // with no more than 4 digits, r < 256 (stored in one byte). + char *end; + std::size_t pos = value.find_first_of("x"); + if (pos == std::string::npos) return false; + std::string str1 = value.substr(pos+1); + value.erase(pos); + if (value.length() == 0 || value.length() > 4 || value[0] == '-') return false; + *w = (unsigned short) strtoul(value.c_str(), &end, 10); + if (*end || *w == 0) return false; + pos = str1.find_first_of("@"); + if(pos != std::string::npos) { + std::string str2 = str1.substr(pos+1); + if (str2.length() == 0 || str2.length() > 3 || str2[0] == '-') return false; + *r = (unsigned short) strtoul(str2.c_str(), &end, 10); + if (*end || *r == 0 || *r > 255) return false; + str1.erase(pos); + } + if (str1.length() == 0 || str1.length() > 4 || str1[0] == '-') return false; + *h = (unsigned short) strtoul(str1.c_str(), &end, 10); + if (*end || *h == 0) return false; + return true; +} + +static bool get_value (const char *str, unsigned int *n) { + // if n > 0 str must be a positive decimal <= input value *n + // if n = 0, str must be a non-negative decimal + if (strlen(str) == 0 || strlen(str) > 10 || str[0] == '-') return false; + char *end; + unsigned long l = strtoul(str, &end, 10); + if (*end) return false; + if (*n && (l == 0 || l > *n)) return false; + *n = (unsigned int) l; + return true; +} + +static bool get_ports (int nports, std::string option, const char * value, unsigned short * const port) { + /*valid entries are comma-separated values port_1,port_2,...,port_r, 0 < r <= nports */ + /*where ports are distinct, and are in the allowed range. */ + /*missing values are consecutive to last given value (at least one value needed). */ + char *end; + unsigned long l; + std::size_t pos; + std::string val(value), str; + for (int i = 0; i <= nports ; i++) { + if(i == nports) break; + pos = val.find_first_of(','); + str = val.substr(0,pos); + if(str.length() == 0 || str.length() > 5 || str[0] == '-') break; + l = strtoul(str.c_str(), &end, 10); + if (*end || l < LOWEST_ALLOWED_PORT || l > HIGHEST_PORT) break; + *(port + i) = (unsigned short) l; + for (int j = 0; j < i ; j++) { + if( *(port + j) == *(port + i)) break; + } + if(pos == std::string::npos) { + if (nports + *(port + i) > i + 1 + HIGHEST_PORT) break; + for (int j = i + 1; j < nports; j++) { + *(port + j) = *(port + j - 1) + 1; + } + return true; + } + val.erase(0, pos+1); + } + LOGE("invalid \"%s %s\", all %d ports must be in range [%d,%d]", + option.c_str(), value, nports, LOWEST_ALLOWED_PORT, HIGHEST_PORT); + return false; +} + +static bool get_videoflip (const char *str, videoflip_t *videoflip) { + if (strlen(str) > 1) return false; + switch (str[0]) { + case 'I': + *videoflip = INVERT; + break; + case 'H': + *videoflip = HFLIP; + break; + case 'V': + *videoflip = VFLIP; + break; + default: + return false; + } + return true; +} + +static bool get_videorotate (const char *str, videoflip_t *videoflip) { + if (strlen(str) > 1) return false; + switch (str[0]) { + case 'L': + *videoflip = LEFT; + break; + case 'R': + *videoflip = RIGHT; + break; + default: + return false; + } + return true; +} + +static void append_hostname(std::string &server_name) { +#ifdef _WIN32 /*modification for compilation on Windows */ + char buffer[256] = ""; + unsigned long size = sizeof(buffer); + if (GetComputerNameA(buffer, &size)) { + std::string name = server_name; + name.append("@"); + name.append(buffer); + server_name = name; + } +#else + struct utsname buf; + if (!uname(&buf)) { + std::string name = server_name; + name.append("@"); + name.append(buf.nodename); + server_name = name; + } +#endif +} + +static void parse_arguments (int argc, char *argv[]) { + // Parse arguments + for (int i = 1; i < argc; i++) { + std::string arg(argv[i]); + if (arg == "-di") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + default_image = std::string(argv[++i]); + LOGI("Using default image: %s", default_image.c_str()); + } else if (arg == "-allow") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + i++; + allowed_clients.push_back(argv[i]); + } else if (arg == "-block") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + i++; + blocked_clients.push_back(argv[i]); + } else if (arg == "-restrict") { + if (i < argc - 1) { + if (strlen(argv[i+1]) == 2 && strncmp(argv[i+1], "no", 2) == 0) { + restrict_clients = false; + i++; + continue; + } + } + restrict_clients = true; + } else if (arg == "-n") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + server_name = std::string(argv[++i]); + } else if (arg == "-nh") { + do_append_hostname = false; + } else if (arg == "-async") { + audio_sync = true; + if (i < argc - 1) { + if (strlen(argv[i+1]) == 2 && strncmp(argv[i+1], "no", 2) == 0) { + audio_sync = false; + i++; + continue; + } + char *end; + int n = (int) (strtof(argv[i + 1], &end) * 1000); + if (*end == '\0') { + i++; + if (n > -SECOND_IN_USECS && n < SECOND_IN_USECS) { + audio_delay_alac = n * 1000; /* units are nsecs */ + } else { + fprintf(stderr, "invalid -async %s: requested delays must be smaller than +/- 1000 millisecs\n", argv[i] ); + exit (1); + } + } + } + } else if (arg == "-vsync") { + video_sync = true; + if (i < argc - 1) { + if (strlen(argv[i+1]) == 2 && strncmp(argv[i+1], "no", 2) == 0) { + video_sync = false; + i++; + continue; + } + char *end; + int n = (int) (strtof(argv[i + 1], &end) * 1000); + if (*end == '\0') { + i++; + if (n > -SECOND_IN_USECS && n < SECOND_IN_USECS) { + audio_delay_aac = n * 1000; /* units are nsecs */ + } else { + fprintf(stderr, "invalid -vsync %s: requested delays must be smaller than +/- 1000 millisecs\n", argv[i]); + exit (1); + } + } + } + } else if (arg == "-s") { + if (!option_has_value(i, argc, argv[i], argv[i+1])) exit(1); + std::string value(argv[++i]); + if (!get_display_settings(value, &display[0], &display[1], &display[2])) { + fprintf(stderr, "invalid \"-s %s\"; -s wxh : max w,h=9999; -s wxh@r : max r=255\n", + argv[i]); + exit(1); + } + } else if (arg == "-fps") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + unsigned int n = 255; + if (!get_value(argv[++i], &n)) { + fprintf(stderr, "invalid \"-fps %s\"; -fps n : max n=255, default n=30\n", argv[i]); + exit(1); + } + display[3] = (unsigned short) n; + } else if (arg == "-o") { + display[4] = 1; + } else if (arg == "-f") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + if (!get_videoflip(argv[++i], &videoflip[0])) { + fprintf(stderr,"invalid \"-f %s\" , unknown flip type, choices are H, V, I\n",argv[i]); + exit(1); + } + } else if (arg == "-r") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + if (!get_videorotate(argv[++i], &videoflip[1])) { + fprintf(stderr,"invalid \"-r %s\" , unknown rotation type, choices are R, L\n",argv[i]); + exit(1); + } + } else if (arg == "-p") { + if (i == argc - 1 || argv[i + 1][0] == '-') { + tcp[0] = 7100; tcp[1] = 7000; tcp[2] = 7001; + udp[0] = 7011; udp[1] = 6001; udp[2] = 6000; + continue; + } + std::string value(argv[++i]); + if (value == "tcp") { + arg.append(" tcp"); + if(!get_ports(3, arg, argv[++i], tcp)) exit(1); + } else if (value == "udp") { + arg.append( " udp"); + if(!get_ports(3, arg, argv[++i], udp)) exit(1); + } else { + if(!get_ports(3, arg, argv[i], tcp)) exit(1); + for (int j = 1; j < 3; j++) { + udp[j] = tcp[j]; + } + } + } else if (arg == "-m") { + if (i < argc - 1 && *argv[i+1] != '-') { + if (validate_mac(argv[++i])) { + mac_address.erase(); + mac_address = argv[i]; + use_random_hw_addr = false; + } else { + fprintf(stderr,"invalid mac address \"%s\": address must have form" + " \"xx:xx:xx:xx:xx:xx\", x = 0-9, A-F or a-f\n", argv[i]); + exit(1); + } + } else { + use_random_hw_addr = true; + } + } else if (arg == "-a") { + use_audio = false; + } else if (arg == "-d") { + debug_log = !debug_log; + } else if (arg == "-h" || arg == "--help" || arg == "-?" || arg == "-help") { + print_info(argv[0]); + exit(0); + } else if (arg == "-v") { + printf("UxPlay version %s; for help, use option \"-h\"\n", VERSION); + exit(0); + } else if (arg == "-vp") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + video_parser.erase(); + video_parser.append(argv[++i]); + } else if (arg == "-vd") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + video_decoder.erase(); + video_decoder.append(argv[++i]); + } else if (arg == "-vc") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + video_converter.erase(); + video_converter.append(argv[++i]); + } else if (arg == "-vs") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + videosink.erase(); + videosink.append(argv[++i]); + std::size_t pos = videosink.find(" "); + if (pos != std::string::npos) { + videosink_options.erase(); + videosink_options = videosink.substr(pos); + videosink.erase(pos); + } + } else if (arg == "-as") { + if (!option_has_value(i, argc, arg, argv[i+1])) exit(1); + audiosink.erase(); + audiosink.append(argv[++i]); + } else if (arg == "-t") { + fprintf(stderr,"The uxplay option \"-t\" has been removed: it was a workaround for an Avahi issue.\n"); + fprintf(stderr,"The correct solution is to open network port UDP 5353 in the firewall for mDNS queries\n"); + exit(1); + } else if (arg == "-nc") { + new_window_closing_behavior = false; + } else if (arg == "-avdec") { + video_parser.erase(); + video_parser = "h264parse"; + video_decoder.erase(); + video_decoder = "avdec_h264"; + video_converter.erase(); + video_converter = "videoconvert"; + } else if (arg == "-v4l2") { + video_decoder.erase(); + video_decoder = "v4l2h264dec"; + video_converter.erase(); + video_converter = "v4l2convert"; + } else if (arg == "-rpi" || arg == "-rpifb" || arg == "-rpigl" || arg == "-rpiwl") { + fprintf(stderr,"*** -rpi* options do not apply to Raspberry Pi model 5, and have been removed\n"); + fprintf(stderr," For models 3 and 4, use their equivalents, if needed:\n"); + fprintf(stderr," -rpi was equivalent to \"-v4l2\"\n"); + fprintf(stderr," -rpifb was equivalent to \"-v4l2 -vs kmssink\"\n"); + fprintf(stderr," -rpigl was equivalent to \"-v4l2 -vs glimagesink\"\n"); + fprintf(stderr," -rpiwl was equivalent to \"-v4l2 -vs waylandsink\"\n"); + fprintf(stderr," for GStreamer < 1.22, \"-bt709\" may also be needed\n"); + exit(1); + } else if (arg == "-fs" ) { + fullscreen = true; + } else if (arg == "-FPSdata") { + show_client_FPS_data = true; + } else if (arg == "-reset") { + max_ntp_timeouts = 0; + if (!get_value(argv[++i], &max_ntp_timeouts)) { + fprintf(stderr, "invalid \"-reset %s\"; -reset n must have n >= 0, default n = %d\n", argv[i], NTP_TIMEOUT_LIMIT); + exit(1); + } + } else if (arg == "-vdmp") { + dump_video = true; + if (i < argc - 1 && *argv[i+1] != '-') { + unsigned int n = 0; + if (get_value (argv[++i], &n)) { + if (n == 0) { + fprintf(stderr, "invalid \"-vdmp 0 %s\"; -vdmp n needs a non-zero value of n\n", argv[i]); + exit(1); + } + video_dump_limit = n; + if (option_has_value(i, argc, arg, argv[i+1])) { + video_dumpfile_name.erase(); + video_dumpfile_name.append(argv[++i]); + } + } else { + video_dumpfile_name.erase(); + video_dumpfile_name.append(argv[i]); + } + const char *fn = video_dumpfile_name.c_str(); + if (!file_has_write_access(fn)) { + fprintf(stderr, "%s cannot be written to:\noption \"-vdmp \" must be to a file with write access\n", fn); + exit(1); + } + } + } else if (arg == "-admp") { + dump_audio = true; + if (i < argc - 1 && *argv[i+1] != '-') { + unsigned int n = 0; + if (get_value (argv[++i], &n)) { + if (n == 0) { + fprintf(stderr, "invalid \"-admp 0 %s\"; -admp n needs a non-zero value of n\n", argv[i]); + exit(1); + } + audio_dump_limit = n; + if (option_has_value(i, argc, arg, argv[i+1])) { + audio_dumpfile_name.erase(); + audio_dumpfile_name.append(argv[++i]); + } + } else { + audio_dumpfile_name.erase(); + audio_dumpfile_name.append(argv[i]); + } + const char *fn = audio_dumpfile_name.c_str(); + if (!file_has_write_access(fn)) { + fprintf(stderr, "%s cannot be written to:\noption \"-admp \" must be to a file with write access\n", fn); + exit(1); + } + } + } else if (arg == "-ca" ) { + if (option_has_value(i, argc, arg, argv[i+1])) { + coverart_filename.erase(); + coverart_filename.append(argv[++i]); + const char *fn = coverart_filename.c_str(); + if (!file_has_write_access(fn)) { + fprintf(stderr, "%s cannot be written to:\noption \"-ca \" must be to a file with write access\n", fn); + exit(1); + } + } else { + fprintf(stderr,"option -ca must be followed by a filename for cover-art output\n"); + exit(1); + } + } else if (arg == "-bt709") { + bt709_fix = true; + } else if (arg == "-nohold") { + nohold = 1; + } else if (arg == "-al") { + int n; + char *end; + if (i < argc - 1 && *argv[i+1] != '-') { + n = (int) (strtof(argv[++i], &end) * SECOND_IN_USECS); + if (*end == '\0' && n >=0 && n <= 10 * SECOND_IN_USECS) { + audiodelay = n; + continue; + } + } + fprintf(stderr, "invalid -al %s: value must be a decimal time offset in seconds, range [0,10]\n" + "(like 5 or 4.8, which will be converted to a whole number of microseconds)\n", argv[i]); + exit(1); + } else if (arg == "-pin") { + setup_legacy_pairing = true; + require_password = true; + if (i < argc - 1 && *argv[i+1] != '-') { + unsigned int n = 9999; + if (!get_value(argv[++i], &n)) { + fprintf(stderr, "invalid \"-pin %s\"; -pin nnnn : max nnnn=9999, (4 digits)\n", argv[i]); + exit(1); + } + pin = n + 10000; + } + } else if (arg == "-reg") { + registration_list = true; + pairing_register.erase(); + if (i < argc - 1 && *argv[i+1] != '-') { + pairing_register.append(argv[++i]); + const char * fn = pairing_register.c_str(); + if (!file_has_write_access(fn)) { + fprintf(stderr, "%s cannot be written to:\noption \"-key \" must be to a file with write access\n", fn); + exit(1); + } + } + } else if (arg == "-key") { + keyfile.erase(); + if (i < argc - 1 && *argv[i+1] != '-') { + keyfile.append(argv[++i]); + const char * fn = keyfile.c_str(); + if (!file_has_write_access(fn)) { + fprintf(stderr, "%s cannot be written to:\noption \"-key \" must be to a file with write access\n", fn); + exit(1); + } + } else { + // fprintf(stderr, "option \"-key \" requires a path to a file for persistent key storage\n"); + // exit(1); + keyfile.erase(); + keyfile.append("0"); + } + } else if (arg == "-dacp") { + dacpfile.erase(); + if (i < argc - 1 && *argv[i+1] != '-') { + dacpfile.append(argv[++i]); + const char *fn = dacpfile.c_str(); + if (!file_has_write_access(fn)) { + fprintf(stderr, "%s cannot be written to:\noption \"-dacp \" must be to a file with write access\n", fn); + exit(1); + } + } else { + dacpfile.append(get_homedir()); + dacpfile.append("/.uxplay.dacp"); + } + } else if (arg == "-taper") { + taper_volume = true; + } else if (arg == "-db") { + bool db_bad = true; + double db1, db2; + char *start = NULL; + if ( i < argc -1) { + char *end1, *end2; + start = argv[i+1]; + db1 = strtod(start, &end1); + if (end1 > start && *end1 == ':') { + db2 = strtod(++end1, &end2); + if ( *end2 == '\0' && end2 > end1 && db1 < 0 && db1 < db2) { + db_bad = false; + } + } else if (*end1 =='\0' && end1 > start && db1 < 0 ) { + db_bad = false; + db2 = 0.0; + } + } + if (db_bad) { + fprintf(stderr, "invalid %s %s: db value must be \"low\" or \"low:high\", low < 0 and high > low are decibel gains\n", argv[i], start); + exit(1); + } + i++; + db_low = db1; + db_high = db2; + printf("db range %f:%f\n", db_low, db_high); + } else if (arg == "-hls") { + hls_support = true; + } else if (arg == "-h265") { + h265_support = true; + } else if (arg == "-nofreeze") { + nofreeze = true; + } else { + fprintf(stderr, "unknown option %s, stopping (for help use option \"-h\")\n",argv[i]); + exit(1); + } + } +} + +static void process_metadata(int count, const char *dmap_tag, const unsigned char* metadata, int datalen) { + int dmap_type = 0; + /* DMAP metadata items can be strings (dmap_type = 9); other types are byte, short, int, long, date, and list. * + * The DMAP item begins with a 4-character (4-letter) "dmap_tag" string that identifies the type. */ + + if (debug_log) { + printf("%d: dmap_tag [%s], %d\n", count, dmap_tag, datalen); + } + + /* UTF-8 String-type DMAP tags seen in Apple Music Radio are processed here. * + * (DMAP tags "asal", "asar", "ascp", "asgn", "minm" ). TODO expand this */ + + if (datalen == 0) { + return; + } + + if (dmap_tag[0] == 'a' && dmap_tag[1] == 's') { + dmap_type = 9; + switch (dmap_tag[2]) { + case 'a': + switch (dmap_tag[3]) { + case 'a': + printf("Album artist: "); /*asaa*/ + break; + case 'l': + printf("Album: "); /*asal*/ + break; + case 'r': + printf("Artist: "); /*asar*/ + break; + default: + dmap_type = 0; + break; + } + break; + case 'c': + switch (dmap_tag[3]) { + case 'm': + printf("Comment: "); /*ascm*/ + break; + case 'n': + printf("Content description: "); /*ascn*/ + break; + case 'p': + printf("Composer: "); /*ascp*/ + break; + case 't': + printf("Category: "); /*asct*/ + break; + default: + dmap_type = 0; + break; + } + break; + case 's': + switch (dmap_tag[3]) { + case 'a': + printf("Sort Artist: "); /*assa*/ + break; + case 'c': + printf("Sort Composer: "); /*assc*/ + break; + case 'l': + printf("Sort Album artist: "); /*assl*/ + break; + case 'n': + printf("Sort Name: "); /*assn*/ + break; + case 's': + printf("Sort Series: "); /*asss*/ + break; + case 'u': + printf("Sort Album: "); /*assu*/ + break; + default: + dmap_type = 0; + break; + } + break; + default: + if (strcmp(dmap_tag, "asdt") == 0) { + printf("Description: "); + } else if (strcmp (dmap_tag, "asfm") == 0) { + printf("Format: "); + } else if (strcmp (dmap_tag, "asgn") == 0) { + printf("Genre: "); + } else if (strcmp (dmap_tag, "asky") == 0) { + printf("Keywords: "); + } else if (strcmp (dmap_tag, "aslc") == 0) { + printf("Long Content Description: "); + } else { + dmap_type = 0; + } + break; + } + } else if (strcmp (dmap_tag, "minm") == 0) { + dmap_type = 9; + printf("Title: "); + } + + if (dmap_type == 9) { + char *str = (char *) calloc(1, datalen + 1); + memcpy(str, metadata, datalen); + printf("%s", str); + free(str); + } else if (debug_log) { + for (int i = 0; i < datalen; i++) { + if (i > 0 && i % 16 == 0) printf("\n"); + printf("%2.2x ", (int) metadata[i]); + } + } + printf("\n"); +} + +static int parse_dmap_header(const unsigned char *metadata, char *tag, int *len) { + const unsigned char *header = metadata; + + bool istag = true; + for (int i = 0; i < 4; i++) { + tag[i] = (char) *header; + if (!isalpha(tag[i])) { + istag = false; + } + header++; + } + + *len = 0; + for (int i = 0; i < 4; i++) { + *len <<= 8; + *len += (int) *header; + header++; + } + if (!istag || *len < 0) { + return 1; + } + return 0; +} + +static int register_dnssd() { + int dnssd_error; + uint64_t features; + + if ((dnssd_error = dnssd_register_raop(dnssd, raop_port))) { + if (dnssd_error == -65537) { + LOGE("No DNS-SD Server found (DNSServiceRegister call returned kDNSServiceErr_Unknown)"); + } else if (dnssd_error == -65548) { + LOGE("DNSServiceRegister call returned kDNSServiceErr_NameConflict"); + LOGI("Is another instance of %s running with the same DeviceID (MAC address) or using same network ports?", + DEFAULT_NAME); + LOGI("Use options -m ... and -p ... to allow multiple instances of %s to run concurrently", DEFAULT_NAME); + } else { + LOGE("dnssd_register_raop failed with error code %d\n" + "mDNS Error codes are in range FFFE FF00 (-65792) to FFFE FFFF (-65537) " + "(see Apple's dns_sd.h)", dnssd_error); + } + return -3; + } + if ((dnssd_error = dnssd_register_airplay(dnssd, airplay_port))) { + LOGE("dnssd_register_airplay failed with error code %d\n" + "mDNS Error codes are in range FFFE FF00 (-65792) to FFFE FFFF (-65537) " + "(see Apple's dns_sd.h)", dnssd_error); + return -4; + } + + LOGD("register_dnssd: advertised AirPlay service with \"Features\" code = 0x%llX", + dnssd_get_airplay_features(dnssd)); + return 0; +} + +static void unregister_dnssd() { + if (dnssd) { + dnssd_unregister_raop(dnssd); + dnssd_unregister_airplay(dnssd); + } + return; +} + +static void stop_dnssd() { + if (dnssd) { + unregister_dnssd(); + dnssd_destroy(dnssd); + dnssd = NULL; + return; + } +} + +static int start_dnssd(std::vector hw_addr, std::string name) { + int dnssd_error; + int require_pw = (require_password ? 1 : 0); + if (dnssd) { + LOGE("start_dnssd error: dnssd != NULL"); + return 2; + } + dnssd = dnssd_init(name.c_str(), strlen(name.c_str()), hw_addr.data(), hw_addr.size(), &dnssd_error, require_pw); + if (dnssd_error) { + LOGE("Could not initialize dnssd library!: error %d", dnssd_error); + return 1; + } + + /* after dnssd starts, reset the default feature set here + * (overwrites features set in dnssdint.h) + * default: FEATURES_1 = 0x5A7FFEE6, FEATURES_2 = 0 */ + + dnssd_set_airplay_features(dnssd, 0, 0); // AirPlay video supported + dnssd_set_airplay_features(dnssd, 1, 1); // photo supported + dnssd_set_airplay_features(dnssd, 2, 1); // video protected with FairPlay DRM + dnssd_set_airplay_features(dnssd, 3, 0); // volume control supported for videos + + dnssd_set_airplay_features(dnssd, 4, 0); // http live streaming (HLS) supported + dnssd_set_airplay_features(dnssd, 5, 1); // slideshow supported + dnssd_set_airplay_features(dnssd, 6, 1); // + dnssd_set_airplay_features(dnssd, 7, 1); // mirroring supported + + dnssd_set_airplay_features(dnssd, 8, 0); // screen rotation supported + dnssd_set_airplay_features(dnssd, 9, 1); // audio supported + dnssd_set_airplay_features(dnssd, 10, 1); // + dnssd_set_airplay_features(dnssd, 11, 1); // audio packet redundancy supported + + dnssd_set_airplay_features(dnssd, 12, 1); // FaiPlay secure auth supported + dnssd_set_airplay_features(dnssd, 13, 1); // photo preloading supported + dnssd_set_airplay_features(dnssd, 14, 1); // Authentication bit 4: FairPlay authentication + dnssd_set_airplay_features(dnssd, 15, 1); // Metadata bit 1 support: Artwork + + dnssd_set_airplay_features(dnssd, 16, 1); // Metadata bit 2 support: Soundtrack Progress + dnssd_set_airplay_features(dnssd, 17, 1); // Metadata bit 0 support: Text (DAACP) "Now Playing" info. + dnssd_set_airplay_features(dnssd, 18, 1); // Audio format 1 support: + dnssd_set_airplay_features(dnssd, 19, 1); // Audio format 2 support: must be set for AirPlay 2 multiroom audio + + dnssd_set_airplay_features(dnssd, 20, 1); // Audio format 3 support: must be set for AirPlay 2 multiroom audio + dnssd_set_airplay_features(dnssd, 21, 1); // Audio format 4 support: + dnssd_set_airplay_features(dnssd, 22, 1); // Authentication type 4: FairPlay authentication + dnssd_set_airplay_features(dnssd, 23, 0); // Authentication type 1: RSA Authentication + + dnssd_set_airplay_features(dnssd, 24, 0); // + dnssd_set_airplay_features(dnssd, 25, 1); // + dnssd_set_airplay_features(dnssd, 26, 0); // Has Unified Advertiser info + dnssd_set_airplay_features(dnssd, 27, 1); // Supports Legacy Pairing + + dnssd_set_airplay_features(dnssd, 28, 1); // + dnssd_set_airplay_features(dnssd, 29, 0); // + dnssd_set_airplay_features(dnssd, 30, 1); // RAOP support: with this bit set, the AirTunes service is not required. + dnssd_set_airplay_features(dnssd, 31, 0); // + + + /* bits 32-63: see https://emanualcozzi.net/docs/airplay2/features + dnssd_set_airplay_features(dnssd, 32, 0); // isCarPlay when ON,; Supports InitialVolume when OFF + dnssd_set_airplay_features(dnssd, 33, 0); // Supports Air Play Video Play Queue + dnssd_set_airplay_features(dnssd, 34, 0); // Supports Air Play from cloud (requires that bit 6 is ON) + dnssd_set_airplay_features(dnssd, 35, 0); // Supports TLS_PSK + + dnssd_set_airplay_features(dnssd, 36, 0); // + dnssd_set_airplay_features(dnssd, 37, 0); // + dnssd_set_airplay_features(dnssd, 38, 0); // Supports Unified Media Control (CoreUtils Pairing and Encryption) + dnssd_set_airplay_features(dnssd, 39, 0); // + + dnssd_set_airplay_features(dnssd, 40, 0); // Supports Buffered Audio + dnssd_set_airplay_features(dnssd, 41, 0); // Supports PTP + dnssd_set_airplay_features(dnssd, 42, 0); // Supports Screen Multi Codec (allows h265 video) + dnssd_set_airplay_features(dnssd, 43, 0); // Supports System Pairing + + dnssd_set_airplay_features(dnssd, 44, 0); // is AP Valeria Screen Sender + dnssd_set_airplay_features(dnssd, 45, 0); // + dnssd_set_airplay_features(dnssd, 46, 0); // Supports HomeKit Pairing and Access Control + dnssd_set_airplay_features(dnssd, 47, 0); // + + dnssd_set_airplay_features(dnssd, 48, 0); // Supports CoreUtils Pairing and Encryption + dnssd_set_airplay_features(dnssd, 49, 0); // + dnssd_set_airplay_features(dnssd, 50, 0); // Metadata bit 3: "Now Playing" info sent by bplist not DAACP test + dnssd_set_airplay_features(dnssd, 51, 0); // Supports Unified Pair Setup and MFi Authentication + + dnssd_set_airplay_features(dnssd, 52, 0); // Supports Set Peers Extended Message + dnssd_set_airplay_features(dnssd, 53, 0); // + dnssd_set_airplay_features(dnssd, 54, 0); // Supports AP Sync + dnssd_set_airplay_features(dnssd, 55, 0); // Supports WoL + + dnssd_set_airplay_features(dnssd, 56, 0); // Supports Wol + dnssd_set_airplay_features(dnssd, 57, 0); // + dnssd_set_airplay_features(dnssd, 58, 0); // Supports Hangdog Remote Control + dnssd_set_airplay_features(dnssd, 59, 0); // Supports AudioStreamConnection setup + + dnssd_set_airplay_features(dnssd, 60, 0); // Supports Audo Media Data Control + dnssd_set_airplay_features(dnssd, 61, 0); // Supports RFC2198 redundancy + */ + + /* needed for HLS video support */ + dnssd_set_airplay_features(dnssd, 0, (int) hls_support); + dnssd_set_airplay_features(dnssd, 4, (int) hls_support); + // not sure about this one (bit 8, screen rotation supported): + //dnssd_set_airplay_features(dnssd, 8, (int) hls_support); + + /* needed for h265 video support */ + dnssd_set_airplay_features(dnssd, 42, (int) h265_support); + + /* bit 27 of Features determines whether the AirPlay2 client-pairing protocol will be used (1) or not (0) */ + dnssd_set_airplay_features(dnssd, 27, (int) setup_legacy_pairing); + return 0; +} + +static bool check_client(char *deviceid) { + bool ret = false; + int list = allowed_clients.size(); + for (int i = 0; i < list ; i++) { + if (!strcmp(deviceid,allowed_clients[i].c_str())) { + ret = true; + break; + } + } + return ret; +} + +static bool check_blocked_client(char *deviceid) { + bool ret = false; + int list = blocked_clients.size(); + for (int i = 0; i < list ; i++) { + if (!strcmp(deviceid,blocked_clients[i].c_str())) { + ret = true; + break; + } + } + return ret; +} + +// Server callbacks + +extern "C" void video_reset(void *cls) { + LOGD("video_reset"); + url.erase(); + reset_loop = true; + remote_clock_offset = 0; + relaunch_video = true; +} + +extern "C" void video_set_codec(void *cls, video_codec_t codec) { + if (use_video) { + bool video_is_h265 = (codec == VIDEO_CODEC_H265); + video_renderer_choose_codec(video_is_h265); + } +} + +extern "C" void display_pin(void *cls, char *pin) { + int margin = 10; + int spacing = 3; + char *image = create_pin_display(pin, margin, spacing); + if (!image) { + LOGE("create_pin_display could not create pin image, pin = %s", pin); + } else { + LOGI("%s\n",image); + free (image); + } +} + +extern "C" void export_dacp(void *cls, const char *active_remote, const char *dacp_id) { + if (dacpfile.length()) { + FILE *fp = fopen(dacpfile.c_str(), "w"); + if (fp) { + fprintf(fp,"%s\n%s\n", dacp_id, active_remote); + fclose(fp); + } else { + LOGE("failed to open DACP export file \"%s\"", dacpfile.c_str()); + } + } +} + +extern "C" void conn_init (void *cls) { + open_connections++; + LOGD("Open connections: %i", open_connections); + //video_renderer_update_background(1); +} + +extern "C" void conn_destroy (void *cls) { + //video_renderer_update_background(-1); + open_connections--; + LOGD("Open connections: %i", open_connections); + if (open_connections == 0) { + remote_clock_offset = 0; + if (use_audio) { + audio_renderer_stop(); + } + if (dacpfile.length()) { + remove (dacpfile.c_str()); + } + } +} + +extern "C" void conn_reset (void *cls, int timeouts, bool reset_video) { + LOGI("***ERROR lost connection with client (network problem?)"); + if (timeouts) { + LOGI(" Client no-response limit of %d timeouts (%d seconds) reached:", timeouts, 3*timeouts); + LOGI(" Sometimes the network connection may recover after a longer delay:\n" + " the default timeout limit n = %d can be changed with the \"-reset n\" option", NTP_TIMEOUT_LIMIT); + } + if (!nofreeze) { + close_window = reset_video; /* leave "frozen" window open if reset_video is false */ + } + raop_stop(raop); + reset_loop = true; +} + +extern "C" void conn_teardown(void *cls, bool *teardown_96, bool *teardown_110) { + if (*teardown_110 && close_window) { + reset_loop = true; + } +} + +extern "C" void report_client_request(void *cls, char *deviceid, char * model, char *name, bool * admit) { + LOGI("connection request from %s (%s) with deviceID = %s\n", name, model, deviceid); + if (restrict_clients) { + *admit = check_client(deviceid); + if (*admit == false) { + LOGI("client connections have been restricted to those with listed deviceID,\nuse \"-allow %s\" to allow this client to connect.\n", + deviceid); + } + } else { + *admit = true; + } + if (check_blocked_client(deviceid)) { + *admit = false; + LOGI("*** attempt to connect by blocked client (clientID %s): DENIED\n", deviceid); + } +} + +extern "C" void audio_process (void *cls, raop_ntp_t *ntp, audio_decode_struct *data) { + if (dump_audio) { + dump_audio_to_file(data->data, data->data_len, (data->data)[0] & 0xf0); + } + if (use_audio) { + if (!remote_clock_offset) { + remote_clock_offset = data->ntp_time_local - data->ntp_time_remote; + } + data->ntp_time_remote = data->ntp_time_remote + remote_clock_offset; + switch (data->ct) { + case 2: + if (audio_delay_alac) { + data->ntp_time_remote = (uint64_t) ((int64_t) data->ntp_time_remote + audio_delay_alac); + } + break; + case 4: + case 8: + if (audio_delay_aac) { + data->ntp_time_remote = (uint64_t) ((int64_t) data->ntp_time_remote + audio_delay_aac); + } + break; + default: + break; + } + audio_renderer_render_buffer(data->data, &(data->data_len), &(data->seqnum), &(data->ntp_time_remote)); + } +} + +extern "C" void video_process (void *cls, raop_ntp_t *ntp, video_decode_struct *data) { + if (dump_video) { + dump_video_to_file(data->data, data->data_len); + } + if (use_video) { + if (!remote_clock_offset) { + remote_clock_offset = data->ntp_time_local - data->ntp_time_remote; + } + data->ntp_time_remote = data->ntp_time_remote + remote_clock_offset; + video_renderer_render_buffer(data->data, &(data->data_len), &(data->nal_count), &(data->ntp_time_remote)); + } +} + +extern "C" void video_pause (void *cls) { + if (use_video) { + video_renderer_pause(); + } +} + +extern "C" void video_resume (void *cls) { + if (use_video) { + video_renderer_resume(); + } +} + + +extern "C" void audio_flush (void *cls) { + if (use_audio) { + audio_renderer_flush(); + } +} + +extern "C" void video_flush (void *cls) { + if (use_video) { + video_renderer_flush(); + } +} + +extern "C" void audio_set_volume (void *cls, float volume) { + double db, db_flat, frac, gst_volume; + if (!use_audio) { + return; + } + /* convert from AirPlay dB volume in range {-30dB : 0dB}, to GStreamer volume */ + if (volume == -144.0f) { /* AirPlay "mute" signal */ + frac = 0.0; + } else if (volume < -30.0f) { + LOGE(" invalid AirPlay volume %f", volume); + frac = 0.0; + } else if (volume > 0.0f) { + LOGE(" invalid AirPlay volume %f", volume); + frac = 1.0; + } else if (volume == -30.0f) { + frac = 0.0; + } else if (volume == 0.0f) { + frac = 1.0; + } else { + frac = (double) ( (30.0f + volume) / 30.0f); + frac = (frac > 1.0) ? 1.0 : frac; + } + + /* frac is length of volume slider as fraction of max length */ + /* also (steps/16) where steps is number of discrete steps above mute (16 = full volume) */ + if (frac == 0.0) { + gst_volume = 0.0; + } else { + /* flat rescaling of decibel range from {-30dB : 0dB} to {db_low : db_high} */ + db_flat = db_low + (db_high-db_low) * frac; + if (taper_volume) { + /* taper the volume reduction by the (rescaled) Airplay {-30:0} range so each reduction of + * the remaining slider length by 50% reduces the perceived volume by 50% (-10dB gain) + * (This is the "dasl-tapering" scheme offered by shairport-sync) */ + db = db_high + 10.0 * (log10(frac) / log10(2.0)); + db = (db > db_flat) ? db : db_flat; + } else { + db = db_flat; + } + /* conversion from (gain) decibels to GStreamer's linear volume scale */ + gst_volume = pow(10.0, 0.05*db); + } + audio_renderer_set_volume(gst_volume); +} + +extern "C" void audio_get_format (void *cls, unsigned char *ct, unsigned short *spf, bool *usingScreen, bool *isMedia, uint64_t *audioFormat) { + unsigned char type; + LOGI("ct=%d spf=%d usingScreen=%d isMedia=%d audioFormat=0x%lx",*ct, *spf, *usingScreen, *isMedia, (unsigned long) *audioFormat); + switch (*ct) { + case 2: + type = 0x20; + break; + case 8: + type = 0x80; + break; + default: + type = 0x10; + break; + } + if (audio_dumpfile && type != audio_type) { + fclose(audio_dumpfile); + audio_dumpfile = NULL; + } + audio_type = type; + + if (use_audio) { + audio_renderer_start(ct); + } + + if (coverart_filename.length()) { + write_coverart(coverart_filename.c_str(), (const void *) empty_image, sizeof(empty_image)); + } +} + +extern "C" void video_report_size(void *cls, float *width_source, float *height_source, float *width, float *height) { + if (use_video) { + video_renderer_size(width_source, height_source, width, height); + } +} + +extern "C" void audio_set_coverart(void *cls, const void *buffer, int buflen) { + if (buffer && coverart_filename.length()) { + write_coverart(coverart_filename.c_str(), buffer, buflen); + LOGI("coverart size %d written to %s", buflen, coverart_filename.c_str()); + } +} + +extern "C" void audio_set_progress(void *cls, unsigned int start, unsigned int curr, unsigned int end) { + int duration = (int) (end - start)/44100; + int position = (int) (curr - start)/44100; + int remain = duration - position; + printf("audio progress (min:sec): %d:%2.2d; remaining: %d:%2.2d; track length %d:%2.2d\n", + position/60, position%60, remain/60, remain%60, duration/60, duration%60); +} + +extern "C" void audio_set_metadata(void *cls, const void *buffer, int buflen) { + char dmap_tag[5] = {0x0}; + const unsigned char *metadata = (const unsigned char *) buffer; + int datalen; + int count = 0; + + printf("==============Audio Metadata=============\n"); + + if (buflen < 8) { + LOGE("received invalid metadata, length %d < 8", buflen); + return; + } else if (parse_dmap_header(metadata, dmap_tag, &datalen)) { + LOGE("received invalid metadata, tag [%s] datalen %d", dmap_tag, datalen); + return; + } + metadata += 8; + buflen -= 8; + + if (strcmp(dmap_tag, "mlit") != 0 || datalen != buflen) { + LOGE("received metadata with tag %s, but is not a DMAP listingitem, or datalen = %d != buflen %d", + dmap_tag, datalen, buflen); + return; + } + while (buflen >= 8) { + count++; + if (parse_dmap_header(metadata, dmap_tag, &datalen)) { + LOGE("received metadata with invalid DMAP header: tag = [%s], datalen = %d", dmap_tag, datalen); + return; + } + metadata += 8; + buflen -= 8; + process_metadata(count, (const char *) dmap_tag, metadata, datalen); + metadata += datalen; + buflen -= datalen; + } + if (buflen != 0) { + LOGE("%d bytes of metadata were not processed", buflen); + } +} + +extern "C" void register_client(void *cls, const char *device_id, const char *client_pk, const char *client_name) { + if (!registration_list) { + /* we are not maintaining a list of registered clients */ + return; + } + LOGI("registered new client: %s DeviceID = %s PK = \n%s", client_name, device_id, client_pk); + registered_keys.push_back(client_pk); + if (strlen(pairing_register.c_str())) { + FILE *fp = fopen(pairing_register.c_str(), "a"); + if (fp) { + fprintf(fp, "%s,%s,%s\n", client_pk, device_id, client_name); + fclose(fp); + } + } +} + +extern "C" bool check_register(void *cls, const char *client_pk) { + if (!registration_list) { + /* we are not maintaining a list of registered clients */ + return true; + } + LOGD("check returning client's pairing registration"); + std::string pk = client_pk; + if (std::find(registered_keys.rbegin(), registered_keys.rend(), pk) != registered_keys.rend()) { + LOGD("registration found: PK=%s", client_pk); + return true; + } else { + LOGE("returning client's pairing registration not found: PK=%s", client_pk); + return false; + } +} +/* control callbacks for video player (unimplemented) */ + +extern "C" void on_video_play(void *cls, const char* location, const float start_position) { + /* start_position needs to be implemented */ + url.erase(); + url.append(location); + reset_loop = true; + relaunch_video = true; + preserve_connections = true; + LOGD("********************on_video_play: location = %s***********************", url.c_str()); +} + +extern "C" void on_video_scrub(void *cls, const float position) { + LOGI("on_video_scrub: position = %7.5f\n", position); + video_renderer_seek(position); +} + +extern "C" void on_video_rate(void *cls, const float rate) { + LOGI("on_video_rate = %7.5f\n", rate); + if (rate == 1.0f) { + video_renderer_resume(); + } else if (rate == 0.0f) { + video_renderer_pause(); + } else { + LOGI("on_video_rate: ignoring unexpected value rate = %f\n", rate); + } +} + +extern "C" void on_video_stop(void *cls) { + LOGI("on_video_stop\n"); +} + +extern "C" void on_video_acquire_playback_info (void *cls, playback_info_t *playback_info) { + int buffering_level; + LOGD("on_video_acquire_playback info\n"); + bool still_playing = video_get_playback_info(&playback_info->duration, &playback_info->position, + &playback_info->rate); + LOGD("on_video_acquire_playback info done\n"); + if (!still_playing) { + LOGI(" video has finished, %f", playback_info->position); + playback_info->position = -1.0; + playback_info->duration = -1.0; + video_renderer_stop(); + } +} + +extern "C" void log_callback (void *cls, int level, const char *msg) { + switch (level) { + case LOGGER_DEBUG: { + LOGD("%s", msg); + break; + } + case LOGGER_WARNING: { + LOGW("%s", msg); + break; + } + case LOGGER_INFO: { + LOGI("%s", msg); + break; + } + case LOGGER_ERR: { + LOGE("%s", msg); + break; + } + default: + break; + } +} + +static int start_raop_server (unsigned short display[5], unsigned short tcp[3], unsigned short udp[3], bool debug_log) { + raop_callbacks_t raop_cbs; + memset(&raop_cbs, 0, sizeof(raop_cbs)); + raop_cbs.conn_init = conn_init; + raop_cbs.conn_destroy = conn_destroy; + raop_cbs.conn_reset = conn_reset; + raop_cbs.conn_teardown = conn_teardown; + raop_cbs.audio_process = audio_process; + raop_cbs.video_process = video_process; + raop_cbs.audio_flush = audio_flush; + raop_cbs.video_flush = video_flush; + raop_cbs.video_pause = video_pause; + raop_cbs.video_resume = video_resume; + raop_cbs.audio_set_volume = audio_set_volume; + raop_cbs.audio_get_format = audio_get_format; + raop_cbs.video_report_size = video_report_size; + raop_cbs.audio_set_metadata = audio_set_metadata; + raop_cbs.audio_set_coverart = audio_set_coverart; + raop_cbs.audio_set_progress = audio_set_progress; + raop_cbs.report_client_request = report_client_request; + raop_cbs.display_pin = display_pin; + raop_cbs.register_client = register_client; + raop_cbs.check_register = check_register; + raop_cbs.export_dacp = export_dacp; + raop_cbs.video_reset = video_reset; + raop_cbs.video_set_codec = video_set_codec; + raop_cbs.on_video_play = on_video_play; + raop_cbs.on_video_scrub = on_video_scrub; + raop_cbs.on_video_rate = on_video_rate; + raop_cbs.on_video_stop = on_video_stop; + raop_cbs.on_video_acquire_playback_info = on_video_acquire_playback_info; + + raop = raop_init(&raop_cbs); + if (raop == NULL) { + LOGE("Error initializing raop!"); + return -1; + } + raop_set_log_callback(raop, log_callback, NULL); + raop_set_log_level(raop, log_level); + /* set nohold = 1 to allow capture by new client */ + if (raop_init2(raop, nohold, mac_address.c_str(), keyfile.c_str())){ + LOGE("Error initializing raop (2)!"); + free (raop); + return -1; + } + + /* write desired display pixel width, pixel height, refresh_rate, max_fps, overscanned. */ + /* use 0 for default values 1920,1080,60,30,0; these are sent to the Airplay client */ + + if (display[0]) raop_set_plist(raop, "width", (int) display[0]); + if (display[1]) raop_set_plist(raop, "height", (int) display[1]); + if (display[2]) raop_set_plist(raop, "refreshRate", (int) display[2]); + if (display[3]) raop_set_plist(raop, "maxFPS", (int) display[3]); + if (display[4]) raop_set_plist(raop, "overscanned", (int) display[4]); + + if (show_client_FPS_data) raop_set_plist(raop, "clientFPSdata", 1); + raop_set_plist(raop, "max_ntp_timeouts", max_ntp_timeouts); + if (audiodelay >= 0) raop_set_plist(raop, "audio_delay_micros", audiodelay); + if (require_password) raop_set_plist(raop, "pin", (int) pin); + if (hls_support) raop_set_plist(raop, "hls", 1); + + /* network port selection (ports listed as "0" will be dynamically assigned) */ + raop_set_tcp_ports(raop, tcp); + raop_set_udp_ports(raop, udp); + + raop_port = raop_get_port(raop); + raop_start(raop, &raop_port); + raop_set_port(raop, raop_port); + + /* use raop_port for airplay_port (instead of tcp[2]) */ + airplay_port = raop_port; + + if (dnssd) { + raop_set_dnssd(raop, dnssd); + } else { + LOGE("raop_set failed to set dnssd"); + return -2; + } + return 0; +} + +static void stop_raop_server () { + if (raop) { + raop_destroy(raop); + raop = NULL; + } + return; +} + +static void read_config_file(const char * filename, const char * uxplay_name) { + std::string config_file = filename; + std::string option_char = "-"; + std::vector options; + options.push_back(uxplay_name); + std::ifstream file(config_file); + if (file.is_open()) { + fprintf(stdout,"UxPlay: reading configuration from %s\n", config_file.c_str()); + std::string line; + while (std::getline(file, line)) { + if (line[0] == '#') continue; + // first process line into separate option items with '\0' as delimiter + bool is_part_of_item, in_quotes; + char endchar; + is_part_of_item = false; + for (int i = 0; i < (int) line.size(); i++) { + if (is_part_of_item == false) { + if (line[i] == ' ') { + line[i] = '\0'; + } else { + // start of new item + is_part_of_item = true; + switch (line[i]) { + case '\'': + case '\"': + endchar = line[i]; + line[i] = '\0'; + in_quotes = true; + break; + default: + in_quotes = false; + endchar = ' '; + break; + } + } + } else { + /* previous character was inside this item */ + if (line[i] == endchar) { + if (in_quotes) { + /* cases where endchar is inside quoted item */ + if (i > 0 && line[i - 1] == '\\') continue; + if (i + 1 < (int) line.size() && line[i + 1] != ' ') continue; + } + line[i] = '\0'; + is_part_of_item = false; + } + } + } + + // now tokenize the processed line + std::istringstream iss(line); + std::string token; + bool first = true; + while (std::getline(iss, token, '\0')) { + if (token.size() > 0) { + if (first) { + options.push_back(option_char + token.c_str()); + first = false; + } else { + options.push_back(token.c_str()); + } + } + } + } + file.close(); + } else { + fprintf(stderr,"UxPlay: failed to open configuration file at %s\n", config_file.c_str()); + } + if (options.size() > 1) { + int argc = options.size(); + char **argv = (char **) malloc(sizeof(char*) * argc); + for (int i = 0; i < argc; i++) { + argv[i] = (char *) options[i].c_str(); + } + parse_arguments (argc, argv); + free (argv); + } +} +#ifdef GST_MACOS +/* workaround for GStreamer >= 1.22 "Official Builds" on macOS */ +#include +#include +void real_main (int argc, char *argv[]); + +int main (int argc, char *argv[]) { + LOGI("*=== Using gst_macos_main wrapper for GStreamer >= 1.22 on macOS ===*"); + return gst_macos_main ((GstMainFunc) real_main, argc, argv , NULL); +} + +void real_main (int argc, char *argv[]) { +#else +int main (int argc, char *argv[]) { +#endif + std::vector server_hw_addr; + std::string config_file = ""; + +#ifdef SUPPRESS_AVAHI_COMPAT_WARNING + // suppress avahi_compat nag message. avahi emits a "nag" warning (once) + // if getenv("AVAHI_COMPAT_NOWARN") returns null. + static char avahi_compat_nowarn[] = "AVAHI_COMPAT_NOWARN=1"; + if (!getenv("AVAHI_COMPAT_NOWARN")) putenv(avahi_compat_nowarn); +#endif + + config_file = find_uxplay_config_file(); + if (config_file.length()) { + read_config_file(config_file.c_str(), argv[0]); + } + parse_arguments (argc, argv); + + log_level = (debug_log ? LOGGER_DEBUG : LOGGER_INFO); + +#ifdef _WIN32 /* use utf-8 terminal output; don't buffer stdout in WIN32 when debug_log = false */ + SetConsoleOutputCP(CP_UTF8); + if (!debug_log) { + setbuf(stdout, NULL); + } +#endif + + LOGI("UxPlay %s: An Open-Source AirPlay mirroring and audio-streaming server.", VERSION); + + if (audiosink == "0") { + use_audio = false; + dump_audio = false; + } + if (dump_video) { + if (video_dump_limit > 0) { + LOGI("dump video using \"-vdmp %d %s\"", video_dump_limit, video_dumpfile_name.c_str()); + } else { + LOGI("dump video using \"-vdmp %s\"", video_dumpfile_name.c_str()); + } + } + if (dump_audio) { + if (audio_dump_limit > 0) { + LOGI("dump audio using \"-admp %d %s\"", audio_dump_limit, audio_dumpfile_name.c_str()); + } else { + LOGI("dump audio using \"-admp %s\"", audio_dumpfile_name.c_str()); + } + } + +#if __APPLE__ + /* force use of -nc option on macOS */ + LOGI("macOS detected: using -nc option as workaround for GStreamer problem"); + new_window_closing_behavior = false; +#endif + + if (videosink == "0") { + use_video = false; + videosink.erase(); + videosink.append("fakesink"); + videosink_options.erase(); + LOGI("video_disabled"); + display[3] = 1; /* set fps to 1 frame per sec when no video will be shown */ + } + + if (fullscreen && use_video) { + if (videosink == "waylandsink" || videosink == "vaapisink") { + videosink_options.append(" fullscreen=true"); + } + } + + if (videosink == "d3d11videosink" && videosink_options.empty() && use_video) { + if (fullscreen) { + videosink_options.append(" fullscreen-toggle-mode=GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY fullscreen=true "); + } else { + videosink_options.append(" fullscreen-toggle-mode=GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER "); + } + LOGI("d3d11videosink is being used with option fullscreen-toggle-mode=alt-enter\n" + "Use Alt-Enter key combination to toggle into/out of full-screen mode"); + } + + if (bt709_fix && use_video) { + video_parser.append(" ! "); + video_parser.append(BT709_FIX); + } + + if (require_password && registration_list) { + if (pairing_register == "") { + const char * homedir = get_homedir(); + if (homedir) { + pairing_register = homedir; + pairing_register.append("/.uxplay.register"); + } + } + } + + /* read in public keys that were previously registered with pair-setup-pin */ + if (require_password && registration_list && strlen(pairing_register.c_str())) { + size_t len = 0; + std::string key; + int clients = 0; + std::ifstream file(pairing_register); + if (file.is_open()) { + std::string line; + while (std::getline(file, line)) { + /*32 bytes pk -> base64 -> strlen(pk64) = 44 chars = line[0:43]; add '\0' at line[44] */ + line[44] = '\0'; + std::string pk = line.c_str(); + registered_keys.push_back(key.assign(pk)); + clients ++; + } + if (clients) { + LOGI("Register %s lists %d pin-registered clients", pairing_register.c_str(), clients); + } + file.close(); + } + } + + if (require_password && keyfile == "0") { + const char * homedir = get_homedir(); + if (homedir) { + keyfile.erase(); + keyfile = homedir; + keyfile.append("/.uxplay.pem"); + } else { + LOGE("could not determine $HOME: public key wiil not be saved, and so will not be persistent"); + } + } + + if (keyfile != "") { + LOGI("public key storage (for persistence) is in %s", keyfile.c_str()); + } + + if (do_append_hostname) { + append_hostname(server_name); + } + + if (!gstreamer_init()) { + LOGE ("stopping"); + exit (1); + } + + render_logger = logger_init(); + logger_set_callback(render_logger, log_callback, NULL); + logger_set_level(render_logger, log_level); + + if (use_audio) { + audio_renderer_init(render_logger, audiosink.c_str(), &audio_sync, &video_sync); + } else { + LOGI("audio_disabled"); + } + if (use_video) { + video_renderer_init(render_logger, server_name.c_str(), videoflip, video_parser.c_str(), + video_decoder.c_str(), video_converter.c_str(), videosink.c_str(), + videosink_options.c_str(), fullscreen, video_sync, h265_support, NULL, default_image.c_str()); + video_renderer_start(); + } + + if (udp[0]) { + LOGI("using network ports UDP %d %d %d TCP %d %d %d", udp[0], udp[1], udp[2], tcp[0], tcp[1], tcp[2]); + } + + if (!use_random_hw_addr) { + if (strlen(mac_address.c_str()) == 0) { + mac_address = find_mac(); + LOGI("using system MAC address %s",mac_address.c_str()); + } else { + LOGI("using user-set MAC address %s",mac_address.c_str()); + } + } + if (mac_address.empty()) { + srand(time(NULL) * getpid()); + mac_address = random_mac(); + LOGI("using randomly-generated MAC address %s",mac_address.c_str()); + } + parse_hw_addr(mac_address, server_hw_addr); + + if (coverart_filename.length()) { + LOGI("any AirPlay audio cover-art will be written to file %s",coverart_filename.c_str()); + write_coverart(coverart_filename.c_str(), (const void *) empty_image, sizeof(empty_image)); + } + + /* set default resolutions for h264 or h265*/ + if (!display[0] && !display[1]) { + if (h265_support) { + display[0] = 3840; + display[1] = 2160; + } else { + display[0] = 1920; + display[1] = 1080; + } + } + + restart: + if (start_dnssd(server_hw_addr, server_name)) { + goto cleanup; + } + if (start_raop_server(display, tcp, udp, debug_log)) { + stop_dnssd(); + goto cleanup; + } + if (register_dnssd()) { + stop_raop_server(); + stop_dnssd(); + goto cleanup; + } + reconnect: + compression_type = 0; + close_window = new_window_closing_behavior; + + main_loop(); + if (relaunch_video || reset_loop) { + if(reset_loop) { + reset_loop = false; + } else { + raop_stop(raop); + } + if (use_audio) audio_renderer_stop(); + if (use_video && (close_window || preserve_connections)) { + video_renderer_destroy(); + if (!preserve_connections) { + raop_destroy_airplay_video(raop); + url.erase(); + raop_remove_known_connections(raop); + } + preserve_connections = false; + const char *uri = (url.empty() ? NULL : url.c_str()); + video_renderer_init(render_logger, server_name.c_str(), videoflip, video_parser.c_str(), + video_decoder.c_str(), video_converter.c_str(), videosink.c_str(), + videosink_options.c_str(), fullscreen, video_sync, h265_support, uri); + video_renderer_start(); + } + if (relaunch_video) { + unsigned short port = raop_get_port(raop); + raop_start(raop, &port); + raop_set_port(raop, port); + goto reconnect; + } else { + LOGI("Re-launching RAOP server..."); + stop_raop_server(); + stop_dnssd(); + goto restart; + } + } else { + LOGI("Stopping..."); + stop_raop_server(); + stop_dnssd(); + } + cleanup: + if (use_audio) { + audio_renderer_destroy(); + } + if (use_video) { + video_renderer_destroy(); + } + logger_destroy(render_logger); + render_logger = NULL; + if(audio_dumpfile) { + fclose(audio_dumpfile); + } + if (video_dumpfile) { + fwrite(mark, 1, sizeof(mark), video_dumpfile); + fclose(video_dumpfile); + } + if (coverart_filename.length()) { + remove (coverart_filename.c_str()); + } +} diff --git a/uxplay.spec b/uxplay.spec new file mode 100644 index 0000000..9dd17df --- /dev/null +++ b/uxplay.spec @@ -0,0 +1,141 @@ +Name: uxplay +Version: 1.71.1 +Release: 1%{?dist} + +%global gittag v%{version} + +Summary: AirPlay-Mirror and AirPlay-Audio server +License: GPLv3+ +URL: https://github.com/FDH2/UxPlay +Source0: https://github.com/FDH2/UxPlay/archive/%{gittag}/%{name}-%{version}.tar.gz + +Packager: UxPlay maintainer + +BuildRequires: cmake >= 3.5 +BuildRequires: make +BuildRequires: gcc +BuildRequires: gcc-c++ +Requires: avahi + +#RedHat and clones +%if %{defined fedora} || %{defined rhel} +BuildRequires: pkgconf +BuildRequires: openssl-devel >= 3.0 +BuildRequires: libplist-devel >= 2.0 +BuildRequires: avahi-compat-libdns_sd-devel +BuildRequires: gstreamer1-devel +BuildRequires: gstreamer1-plugins-base-devel +Requires: openssl-libs >= 3.0 +Requires: libplist >= 2.0 +Requires: gstreamer1-plugins-base +Requires: gstreamer1-plugins-good +Requires: gstreamer1-plugins-bad-free +Requires: gstreamer1-libav +%define cmake_builddir redhat-linux-build +%endif + +#SUSE +%if "%{_host_vendor}" == "suse" +BuildRequires: pkg-config +BuildRequires: libopenssl-3-devel +BuildRequires: libplist-2_0-devel +BuildRequires: avahi-compat-mDNSResponder-devel +BuildRequires: gstreamer-devel +BuildRequires: gstreamer-plugins-base-devel +Requires: libopenssl3 +Requires: libplist-2_0-3 +Requires: gstreamer-plugins-base +Requires: gstreamer-plugins-good +Requires: gstreamer-plugins-bad +Requires: gstreamer-plugins-libav +%endif + +#Mageia, OpenMandriva, PCLinuxOS (Mandriva/Mandrake descendents) +%if "%{_host_vendor}" == "mageia" || %{defined omvver} || "%{_host_vendor}" == "mandriva" +%if "%{_host_vendor}" == "mandriva" +# host_vendor = "mandriva" identifies PCLinuxOS. +# As of 07/2023, PCLinuxOS does not seem to supply openssl >= 3.0. +# Note that UxPlay does not have a "GPL exception" allowing it to be +# distributed in binary form when linked to openssl < 3.0, unless that +# OpenSSL < 3.0 qualifies as a "system library". +BuildRequires: pkgconfig +BuildRequires: %{mklibname openssl-devel} >= 1.1.1 +Requires: %{mklibname openssl1.1.0} +%else +BuildRequires: pkgconf +BuildRequires: %{mklibname openssl-devel} >= 3.0 +%if %{defined omvver} +Requires: openssl >= 3.0 +%else +Requires: %{mklibname openssl3} +%endif +%endif +BuildRequires: %{mklibname plist-devel} >= 2.0 +BuildRequires: %{mklibname avahi-compat-libdns_sd-devel} +%if %{defined omvver} +BuildRequires: %{mklibname gstreamer-devel} +BuildRequires: %{mklibname gst-plugins-base1.0-devel} +Requires: %{mklibname plist} >= 2.0 +%else +BuildRequires: %{mklibname gstreamer1.0-devel} +BuildRequires: %{mklibname gstreamer-plugins-base1.0-devel} +Requires: %{mklibname plist2.0_3} +%endif +Requires: gstreamer1.0-plugins-base +Requires: gstreamer1.0-plugins-good +Requires: gstreamer1.0-plugins-bad +Requires: gstreamer1.0-libav +%endif + +%description +An AirPlay2 Mirror and AirPlay2 Audio (but not Video) server that provides +screen-mirroring (with audio) of iOS/MacOS clients in a display window on +the server host (which can be shared using a screen-sharing application); +Apple Lossless Audio (ALAC) (e.g.,iTunes) can be streamed from client to +server in non-mirror mode + +%prep + +%autosetup -n UxPlay-%{version} + +# if you are building for a distribution, add cmake option -DNO_MARCH_NATIVE=ON +%cmake -DCMAKE_INSTALL_DOCDIR=%{_docdir}/%{name} + +%build + +%if %{defined cmake_builddir} +cd %{cmake_builddir} +%else +cd build +%endif + +%make_build + +%install + +%if %{defined cmake_builddir} +cd %{cmake_builddir} +%else +cd build +%endif + +%make_install + +%files +%{_bindir}/uxplay +%{_mandir}/man1/uxplay.1* + +%doc +%{_docdir}/%{name}/README.txt +%{_docdir}/%{name}/README.html +%{_docdir}/%{name}/README.md + +%license +%{_docdir}/%{name}/LICENSE +%{_docdir}/%{name}/llhttp/LICENSE-MIT + +%changelog +* Fri Nov 15 2024 UxPlay maintainer + Initial uxplay.spec: tested on Fedora 38, Rocky Linux 9.2, OpenSUSE + Leap 15.5, Mageia 9, OpenMandriva ROME, PCLinuxOS +-