From e26d25c47a5878fb524c768ced1382e78e4f78a8 Mon Sep 17 00:00:00 2001 From: SirHumza <204067870+SirHumza@users.noreply.github.com> Date: Sun, 27 Sep 2026 15:21:00 +0200 Subject: [PATCH] installer: custom SDL2 reskin + remaster landing - ui.c: fullscreen Discord-blurple cards, embedded bitmap font, pad X-to-continue, progress bar; IME token entry kept as overlay - scripts/genfont.py + installer/font.h: build-time rasterized font - Makefile: -lSDL2 -lSDL2main -lScePigletv2VSH -lSceAudioOut -lSceVideoOut, font.h regen rule; drop stock -lSceMsgDialog - daemon: gateway state machine, status.json heartbeat, gen supersede, no-exit token wait; updater removed per direction - installer: config preserve on reinstall, honest stage logging, token-save verification, daemon liveness on done screen --- installer/Makefile | 9 +- installer/font.h | 2366 +++++++++++++++++++++++++++++++++++++++++ installer/icfg.c | 51 + installer/icfg.h | 4 + installer/installer.c | 50 +- installer/ui.c | 401 +++++-- orbisrpc/cfg.c | 2 +- orbisrpc/cfg.h | 2 +- orbisrpc/daemon.c | 42 +- orbisrpc/discord.c | 19 + orbisrpc/discord.h | 14 +- orbisrpc/updater.c | 455 -------- orbisrpc/updater.h | 9 +- scripts/build.sh | 2 +- scripts/build_sdk.sh | 2 +- scripts/genfont.py | 87 ++ tests/test_utils.c | 31 + 17 files changed, 2947 insertions(+), 599 deletions(-) create mode 100644 installer/font.h delete mode 100644 orbisrpc/updater.c create mode 100644 scripts/genfont.py diff --git a/installer/Makefile b/installer/Makefile index a41fe43..9f11c20 100644 --- a/installer/Makefile +++ b/installer/Makefile @@ -6,7 +6,7 @@ VERSION := 1.0.0 TITLE_ID := ORPC00001 CONTENT_ID := IV0000-ORPC00001_00-ORBISRPCSETUP000 -LIBS := -lc -lkernel -lc++ -lSceMsgDialog -lSceCommonDialog -lSceImeDialog -lSceSysmodule -lSceNet -lSceUserService -lSceSystemService -lScePad +LIBS := -lc -lkernel -lc++ -lSDL2 -lSDL2main -lScePigletv2VSH -lSceAudioOut -lSceVideoOut -lSceCommonDialog -lSceImeDialog -lSceSysmodule -lSceNet -lSceUserService -lSceSystemService -lScePad ASSETS := $(wildcard installer/assets/**/*) LIBMODULES := $(wildcard installer/sce_module/*) @@ -65,6 +65,13 @@ installer/eboot.bin: $(OBJS) $(INTDIR)/%.o: $(PROJDIR)/%.c $(CC) $(CFLAGS) -o $@ $< +# Embedded bitmap font (build-time rasterized; checked in but regenerable). +installer/font.h: scripts/genfont.py + python3 scripts/genfont.py + +$(INTDIR)/ui.o: installer/ui.c installer/font.h + $(CC) $(CFLAGS) -o $@ $< + $(INTDIR)/jsonlite.o: orbisrpc/jsonlite.c $(CC) $(CFLAGS) -o $@ $< diff --git a/installer/font.h b/installer/font.h new file mode 100644 index 0000000..bf57db9 --- /dev/null +++ b/installer/font.h @@ -0,0 +1,2366 @@ +/* font.h - GENERATED by scripts/genfont.py. Do not edit. */ +#ifndef INSTALLER_FONT_H +#define INSTALLER_FONT_H +#include +#define FONT_H 28 +#define FONT_FIRST 32 +#define FONT_COUNT 95 +static const uint8_t font_adv[FONT_COUNT] = {8,4,8,17,16,26,19,3,7,7,10,16,4,8,4,9,16,9,16,16,16,15,15,15,16,15,4,4,18,16,18,14,25,20,17,20,19,17,16,21,18,4,13,18,15,21,18,22,17,22,19,18,18,18,20,28,20,20,18,7,11,7,14,17,7,16,15,15,15,15,8,15,14,4,6,14,3,23,14,16,15,15,8,14,8,14,15,22,15,15,14,11,3,11,18}; +static const unsigned font_off[FONT_COUNT] = {0,224,336,560,1036,1484,2212,2744,2828,3024,3220,3500,3948,4060,4284,4396,4648,5096,5348,5796,6244,6692,7112,7532,7952,8400,8820,8932,9044,9548,9996,10500,10892,11592,12152,12628,13188,13720,14196,14644,15232,15736,15848,16212,16716,17136,17724,18228,18844,19320,19936,20468,20972,21476,21980,22540,23324,23884,24444,24948,25144,25452,25648,26040,26516,26712,27160,27580,28000,28420,28840,29064,29484,29876,29988,30156,30548,30632,31276,31668,32116,32536,32956,33180,33572,33796,34188,34608,35224,35644,36064,36456,36764,36848,37156}; +static const uint8_t font_px[37660] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +136,255,255,116,136,255,255,116,136,255,255,116,136,255,255,116, +136,255,255,116,136,255,255,116,136,255,255,116,131,255,255,111, +113,255,255,93,93,255,255,74,72,255,255,55,52,255,255,36, +32,255,255,17,11,255,252,1,0,246,235,0,0,227,216,0, +0,206,197,0,0,0,0,0,0,0,0,0,136,255,255,100, +136,255,255,100,136,255,255,100,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +197,255,212,0,0,193,255,216,183,255,199,0,0,179,255,202, +168,255,185,0,0,164,255,188,154,255,172,0,0,150,255,174, +139,255,158,0,0,135,255,160,125,255,145,0,0,121,255,146, +110,255,131,0,0,106,255,132,95,255,118,0,0,91,255,118, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,182,255,121,0,0,67,255,229,0, +0,0,0,0,0,0,0,5,243,255,54,0,0,135,255,161, +0,0,0,0,0,0,0,0,61,255,239,3,0,0,203,255, +93,0,0,0,0,0,0,0,0,129,255,175,0,0,17,253, +255,26,0,0,0,0,0,0,0,0,196,255,107,0,0,83, +255,213,0,0,0,0,0,0,0,0,12,250,255,40,0,0, +151,255,145,0,0,0,0,0,0,0,0,75,255,228,0,0, +0,219,255,77,0,0,0,0,0,0,0,0,142,255,161,0, +0,30,255,251,13,0,0,0,0,0,214,255,255,255,255,255, +255,255,255,255,255,255,255,255,138,0,16,254,255,255,255,255, +255,255,255,255,255,255,255,255,255,80,0,0,0,0,90,255, +213,0,0,0,233,255,61,0,0,0,0,0,0,0,0,157, +255,146,0,0,45,255,244,6,0,0,0,0,0,0,0,0, +223,255,79,0,0,112,255,183,0,0,0,0,0,0,0,0, +35,255,253,16,0,0,179,255,117,0,0,0,0,0,173,255, +255,255,255,255,255,255,255,255,255,255,255,255,182,0,0,228, +255,255,255,255,255,255,255,255,255,255,255,255,255,124,0,0, +0,0,1,236,255,65,0,0,127,255,169,0,0,0,0,0, +0,0,0,49,255,247,7,0,0,195,255,101,0,0,0,0, +0,0,0,0,117,255,187,0,0,12,250,255,33,0,0,0, +0,0,0,0,0,184,255,119,0,0,75,255,221,0,0,0, +0,0,0,0,0,6,245,255,52,0,0,143,255,153,0,0, +0,0,0,0,0,0,62,255,238,2,0,0,210,255,85,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,112,244,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,112,244,0,0,0,0,0,0,0,0,0,0,2, +86,180,231,252,255,239,201,134,31,0,0,0,0,0,11,183, +255,255,255,255,255,255,255,255,247,100,0,0,0,0,169,255, +255,150,41,124,244,16,81,218,255,254,71,0,0,58,255,255, +165,0,0,120,244,0,0,25,241,255,197,0,0,138,255,255, +46,0,0,120,244,0,0,0,157,255,251,3,0,162,255,255, +7,0,0,120,244,0,0,0,0,0,0,0,0,141,255,255, +42,0,0,120,244,0,0,0,0,0,0,0,0,68,255,255, +176,2,0,120,244,0,0,0,0,0,0,0,0,0,177,255, +255,203,81,131,244,0,0,0,0,0,0,0,0,0,9,148, +252,255,255,255,255,212,131,40,0,0,0,0,0,0,0,0, +33,126,197,248,255,255,255,255,188,49,0,0,0,0,0,0, +0,0,0,120,244,49,154,251,255,247,71,0,0,0,0,0, +0,0,0,120,244,0,0,62,246,255,233,11,0,0,0,0, +0,0,0,120,244,0,0,0,130,255,255,85,0,0,0,0, +0,0,0,120,244,0,0,0,51,255,255,126,11,255,255,146, +0,0,0,120,244,0,0,0,30,255,255,129,1,246,255,179, +0,0,0,120,244,0,0,0,52,255,255,107,0,200,255,236, +5,0,0,120,244,0,0,0,121,255,255,53,0,115,255,255, +122,0,0,120,244,0,0,26,236,255,220,1,0,8,206,255, +255,156,51,126,244,21,91,225,255,253,72,0,0,0,27,193, +255,255,255,255,255,255,255,255,244,86,0,0,0,0,0,2, +77,167,216,250,255,235,188,118,23,0,0,0,0,0,0,0, +0,0,0,112,244,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,112,244,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,112,244,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,10,230,175, +0,0,0,0,0,0,0,0,0,77,190,242,245,200,96,0, +0,0,0,0,0,0,0,121,252,42,0,0,0,0,0,0, +0,0,136,255,255,255,255,255,255,165,2,0,0,0,0,0, +17,239,158,0,0,0,0,0,0,0,0,84,255,251,125,28, +21,108,245,255,118,0,0,0,0,0,138,248,29,0,0,0, +0,0,0,0,0,201,255,128,0,0,0,0,97,255,233,2, +0,0,0,27,247,141,0,0,0,0,0,0,0,0,1,251, +255,36,0,0,0,0,6,250,255,31,0,0,0,155,241,19, +0,0,0,0,0,0,0,0,1,251,255,35,0,0,0,0, +6,250,255,31,0,0,39,252,124,0,0,0,0,0,0,0, +0,0,0,202,255,129,0,0,0,0,96,255,234,2,0,0, +172,232,11,0,0,0,0,0,0,0,0,0,0,87,255,251, +125,27,20,105,244,255,120,0,0,53,255,107,0,0,0,0, +0,0,0,0,0,0,0,0,140,255,255,255,255,255,255,169, +2,0,0,189,221,5,0,0,0,0,0,0,0,0,0,0, +0,0,0,79,192,243,246,202,98,1,0,0,70,255,89,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,205,207,1,0,0,31,155,228,249,223, +143,21,0,0,0,0,0,0,0,0,0,0,0,0,0,87, +255,72,0,0,56,238,255,255,255,255,255,226,37,0,0,0, +0,0,0,0,0,0,0,0,4,218,192,0,0,13,231,255, +192,51,12,61,209,255,210,3,0,0,0,0,0,0,0,0, +0,0,104,255,56,0,0,105,255,217,9,0,0,0,22,235, +255,75,0,0,0,0,0,0,0,0,0,10,230,175,0,0, +0,156,255,132,0,0,0,0,0,161,255,127,0,0,0,0, +0,0,0,0,0,121,252,42,0,0,0,156,255,131,0,0, +0,0,0,161,255,127,0,0,0,0,0,0,0,0,17,239, +158,0,0,0,0,105,255,217,8,0,0,0,21,234,255,75, +0,0,0,0,0,0,0,0,138,248,29,0,0,0,0,13, +231,255,189,50,11,59,206,255,211,3,0,0,0,0,0,0, +0,27,247,141,0,0,0,0,0,0,57,239,255,255,255,255, +255,227,37,0,0,0,0,0,0,0,0,155,241,19,0,0, +0,0,0,0,0,32,156,229,250,224,144,21,0,0,0,0, +0,0,0,0,39,252,124,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,3,108,203,244,246,207,110,2, +0,0,0,0,0,0,0,0,0,0,4,182,255,255,255,255, +255,255,168,0,0,0,0,0,0,0,0,0,0,123,255,254, +112,16,23,154,255,255,87,0,0,0,0,0,0,0,0,0, +228,255,169,0,0,0,3,221,255,174,0,0,0,0,0,0, +0,0,20,255,255,123,0,0,0,0,175,255,202,0,0,0, +0,0,0,0,0,20,255,255,159,0,0,0,0,210,255,183, +0,0,0,0,0,0,0,0,0,228,255,243,24,0,0,72, +255,255,120,0,0,0,0,0,0,0,0,0,130,255,255,177, +0,46,235,255,238,20,0,0,0,0,0,0,0,0,0,12, +224,255,255,163,243,255,251,78,0,0,0,0,0,0,0,0, +0,0,0,54,249,255,255,255,240,73,0,0,0,0,0,0, +0,0,0,0,0,11,151,254,255,255,255,57,0,0,0,0, +0,0,0,0,0,0,0,43,218,255,255,254,255,255,192,3, +0,0,0,0,0,0,0,0,0,46,239,255,255,194,43,223, +255,255,135,0,0,84,255,255,41,0,0,3,213,255,255,161, +5,0,54,250,255,254,75,0,131,255,249,4,0,0,79,255, +255,199,2,0,0,0,111,255,255,237,31,198,255,199,0,0, +0,146,255,255,69,0,0,0,0,0,173,255,255,209,253,255, +124,0,0,0,173,255,255,16,0,0,0,0,0,15,221,255, +255,255,255,43,0,0,0,167,255,253,2,0,0,0,0,0, +0,51,248,255,255,200,0,0,0,0,123,255,255,51,0,0, +0,0,0,0,0,180,255,255,235,41,0,0,0,38,253,255, +189,6,0,0,0,0,0,120,255,255,255,255,227,31,0,0, +0,154,255,255,210,77,18,20,68,183,255,255,171,194,255,255, +218,23,0,0,10,186,255,255,255,255,255,255,255,251,128,2, +6,161,255,255,208,16,0,0,2,93,183,232,250,233,203,129, +31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,177,255,235,161,255,220,146,255, +204,130,255,189,115,255,173,99,255,158,84,255,142,68,255,127, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +68,255,159,0,0,0,8,219,251,35,0,0,0,125,255,161, +0,0,0,28,245,254,42,0,0,0,158,255,181,0,0,0, +36,252,255,74,0,0,0,145,255,233,3,0,0,3,232,255, +162,0,0,0,58,255,255,98,0,0,0,116,255,255,51,0, +0,0,163,255,255,14,0,0,0,196,255,244,0,0,0,0, +219,255,227,0,0,0,0,230,255,219,0,0,0,0,230,255, +220,0,0,0,0,214,255,229,0,0,0,0,195,255,246,0, +0,0,0,152,255,255,19,0,0,0,103,255,255,58,0,0, +0,39,255,255,108,0,0,0,0,215,255,170,0,0,0,0, +127,255,239,4,0,0,0,28,251,255,78,0,0,0,0,156, +255,184,0,0,0,0,25,242,255,44,0,0,0,0,109,255, +165,0,0,0,0,2,202,252,40,0,0,0,0,46,251,169, +191,244,30,0,0,0,0,60,255,179,0,0,0,0,0,190, +255,82,0,0,0,0,67,255,227,11,0,0,0,0,209,255, +131,0,0,0,0,103,255,242,15,0,0,0,17,252,255,107, +0,0,0,0,195,255,197,0,0,0,0,132,255,254,25,0, +0,0,82,255,255,85,0,0,0,44,255,255,137,0,0,0, +15,255,255,173,0,0,0,0,253,255,199,0,0,0,0,245, +255,211,0,0,0,0,245,255,208,0,0,0,0,252,255,198, +0,0,0,12,255,255,175,0,0,0,37,255,255,141,0,0, +0,74,255,255,94,0,0,0,120,255,255,35,0,0,0,184, +255,213,0,0,0,11,246,255,122,0,0,0,96,255,245,20, +0,0,0,203,255,136,0,0,0,62,255,236,17,0,0,0, +183,255,105,0,0,0,52,255,204,2,0,0,0,180,252,50, +0,0,0,0,3,0,0,28,255,152,0,0,3,0,116,229, +170,129,255,198,152,206,236,6,164,254,255,255,255,255,255,255, +241,52,0,18,77,220,255,255,143,49,2,0,0,0,26,250, +253,255,155,0,0,0,0,0,133,255,131,231,247,20,0,0, +0,9,236,246,20,125,255,123,0,0,0,103,255,153,0,21, +247,229,5,0,0,47,217,41,0,0,155,146,4,0,0,0, +6,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,68,255,255,60,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,132,255,255,160, +132,255,255,160,132,255,255,158,0,0,234,136,0,21,255,82, +10,162,221,5,123,177,34,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,192,255,255,255, +255,255,255,152,192,255,255,255,255,255,255,152,192,255,255,255, +255,255,255,152,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,112,255,255,160, +112,255,255,160,112,255,255,160,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,84,255,227,0,0,0,0,0,0,163,255,148,0,0, +0,0,0,4,238,255,69,0,0,0,0,0,66,255,240,5, +0,0,0,0,0,146,255,166,0,0,0,0,0,0,224,255, +87,0,0,0,0,0,49,255,250,14,0,0,0,0,0,128, +255,185,0,0,0,0,0,0,208,255,105,0,0,0,0,0, +31,255,254,27,0,0,0,0,0,111,255,203,0,0,0,0, +0,0,190,255,123,0,0,0,0,0,17,251,255,44,0,0, +0,0,0,93,255,221,0,0,0,0,0,0,172,255,142,0, +0,0,0,0,7,243,255,62,0,0,0,0,0,75,255,236, +3,0,0,0,0,0,155,255,160,0,0,0,0,0,1,232, +255,81,0,0,0,0,0,58,255,247,10,0,0,0,0,0, +137,255,178,0,0,0,0,0,0,216,255,99,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,11,121,205,244, +244,215,150,45,0,0,0,0,0,0,0,35,220,255,255,255, +255,255,255,251,111,0,0,0,0,0,13,218,255,252,126,29, +10,56,189,255,255,90,0,0,0,0,134,255,255,105,0,0, +0,0,7,208,255,239,17,0,0,11,240,255,212,1,0,0, +0,0,0,85,255,255,116,0,0,83,255,255,120,0,0,0, +0,0,0,11,249,255,198,0,0,151,255,255,58,0,0,0, +0,0,0,0,205,255,251,10,0,200,255,255,11,0,0,0, +0,0,0,0,164,255,255,46,0,234,255,238,0,0,0,0, +0,0,0,0,137,255,255,77,2,254,255,218,0,0,0,0, +0,0,0,0,122,255,255,91,11,255,255,210,0,0,0,0, +0,0,0,0,114,255,255,100,12,255,255,205,0,0,0,0, +0,0,0,0,118,255,255,95,2,255,255,211,0,0,0,0, +0,0,0,0,126,255,255,84,0,242,255,227,0,0,0,0, +0,0,0,0,140,255,255,59,0,207,255,250,3,0,0,0, +0,0,0,0,169,255,255,25,0,163,255,255,40,0,0,0, +0,0,0,0,206,255,227,0,0,96,255,255,103,0,0,0, +0,0,0,13,252,255,161,0,0,20,247,255,201,0,0,0, +0,0,0,105,255,255,69,0,0,0,149,255,255,88,0,0, +0,0,25,231,255,207,1,0,0,0,14,223,255,246,112,24, +13,79,223,255,249,52,0,0,0,0,0,40,218,255,255,255, +255,255,255,244,78,0,0,0,0,0,0,0,14,116,204,239, +249,219,146,30,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,255, +160,0,0,0,0,0,7,234,255,160,0,0,0,0,0,140, +255,255,160,0,18,56,110,196,255,255,255,160,32,255,255,255, +255,255,255,255,160,32,255,255,255,255,255,255,255,160,0,0, +0,0,0,48,255,255,160,0,0,0,0,0,48,255,255,160, +0,0,0,0,0,48,255,255,160,0,0,0,0,0,48,255, +255,160,0,0,0,0,0,48,255,255,160,0,0,0,0,0, +48,255,255,160,0,0,0,0,0,48,255,255,160,0,0,0, +0,0,48,255,255,160,0,0,0,0,0,48,255,255,160,0, +0,0,0,0,48,255,255,160,0,0,0,0,0,48,255,255, +160,0,0,0,0,0,48,255,255,160,0,0,0,0,0,48, +255,255,160,0,0,0,0,0,48,255,255,160,0,0,0,0, +0,48,255,255,160,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,29,127,202,232,250,237,204,136, +27,0,0,0,0,0,0,91,242,255,255,255,255,255,255,255, +246,82,0,0,0,0,66,252,255,235,103,28,6,30,117,246, +255,250,54,0,0,0,204,255,244,39,0,0,0,0,0,68, +255,255,201,0,0,46,255,255,146,0,0,0,0,0,0,0, +187,255,255,44,0,94,255,255,75,0,0,0,0,0,0,0, +131,255,255,95,0,125,255,255,45,0,0,0,0,0,0,0, +132,255,255,103,0,0,0,0,0,0,0,0,0,0,0,0, +199,255,255,71,0,0,0,0,0,0,0,0,0,0,0,95, +255,255,238,9,0,0,0,0,0,0,0,0,0,0,106,251, +255,255,106,0,0,0,0,0,0,0,0,0,24,176,255,255, +255,147,0,0,0,0,0,0,0,0,1,103,239,255,255,246, +105,0,0,0,0,0,0,0,0,40,197,255,255,255,192,38, +0,0,0,0,0,0,0,0,98,246,255,255,234,95,0,0, +0,0,0,0,0,0,0,119,255,255,255,157,17,0,0,0, +0,0,0,0,0,0,77,254,255,242,79,0,0,0,0,0, +0,0,0,0,0,3,222,255,248,58,0,0,0,0,0,0, +0,0,0,0,0,69,255,255,123,0,0,0,0,0,0,0, +0,0,0,0,0,157,255,249,14,0,0,0,0,0,0,0, +0,0,0,0,0,212,255,255,255,255,255,255,255,255,255,255, +255,255,255,88,3,248,255,255,255,255,255,255,255,255,255,255, +255,255,255,88,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,44,150,215,244,245,220,165,69, +0,0,0,0,0,0,0,98,250,255,255,255,255,255,255,255, +169,6,0,0,0,0,62,254,255,221,84,20,9,71,211,255, +255,153,0,0,0,0,211,255,235,24,0,0,0,0,14,216, +255,253,34,0,0,48,255,255,113,0,0,0,0,0,0,97, +255,255,105,0,0,125,255,255,36,0,0,0,0,0,0,48, +255,255,136,0,0,157,255,248,1,0,0,0,0,0,0,53, +255,255,139,0,0,180,255,227,0,0,0,0,0,0,0,100, +255,255,105,0,0,0,0,0,0,0,0,0,0,0,9,213, +255,249,27,0,0,0,0,0,0,0,0,0,19,76,204,255, +255,113,0,0,0,0,0,0,0,0,140,255,255,255,255,253, +99,0,0,0,0,0,0,0,0,0,140,255,255,255,255,255, +242,115,0,0,0,0,0,0,0,0,0,1,14,53,134,243, +255,255,112,0,0,0,0,0,0,0,0,0,0,0,0,51, +250,255,238,13,0,0,0,0,0,0,0,0,0,0,0,0, +170,255,255,61,63,255,255,133,0,0,0,0,0,0,0,0, +126,255,255,88,36,255,255,171,0,0,0,0,0,0,0,0, +130,255,255,71,0,230,255,234,4,0,0,0,0,0,0,0, +197,255,252,17,0,131,255,255,121,0,0,0,0,0,0,101, +255,255,166,0,0,13,224,255,254,149,47,9,10,53,155,254, +255,227,26,0,0,0,39,223,255,255,255,255,255,255,255,255, +206,36,0,0,0,0,0,9,109,187,232,250,244,222,170,84, +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,19,230,255, +152,0,0,0,0,0,0,0,0,0,0,0,0,173,255,255, +152,0,0,0,0,0,0,0,0,0,0,0,96,255,255,255, +152,0,0,0,0,0,0,0,0,0,0,33,242,255,255,255, +152,0,0,0,0,0,0,0,0,0,2,196,255,221,255,255, +152,0,0,0,0,0,0,0,0,0,122,255,247,63,255,255, +152,0,0,0,0,0,0,0,0,51,250,255,110,20,255,255, +152,0,0,0,0,0,0,0,9,215,255,186,1,20,255,255, +152,0,0,0,0,0,0,0,148,255,237,27,0,20,255,255, +152,0,0,0,0,0,0,72,255,255,85,0,0,20,255,255, +152,0,0,0,0,0,19,231,255,162,0,0,0,20,255,255, +152,0,0,0,0,0,174,255,224,14,0,0,0,20,255,255, +152,0,0,0,0,98,255,253,62,0,0,0,0,20,255,255, +152,0,0,0,26,242,255,137,0,0,0,0,0,20,255,255, +152,0,0,0,60,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,176,60,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,176,0,0,0,0,0,0,0,0,0,8,255,255, +152,0,0,0,0,0,0,0,0,0,0,0,0,8,255,255, +152,0,0,0,0,0,0,0,0,0,0,0,0,8,255,255, +152,0,0,0,0,0,0,0,0,0,0,0,0,8,255,255, +152,0,0,0,0,0,0,0,0,0,0,0,0,8,255,255, +152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,190,255,255,255,255,255,255,255,255,255, +255,56,0,0,0,224,255,255,255,255,255,255,255,255,255,255, +56,0,0,5,252,255,59,0,0,0,0,0,0,0,0,0, +0,0,36,255,255,27,0,0,0,0,0,0,0,0,0,0, +0,70,255,249,2,0,0,0,0,0,0,0,0,0,0,0, +104,255,219,0,0,0,0,0,0,0,0,0,0,0,0,138, +255,187,0,0,0,0,0,0,0,0,0,0,0,0,172,255, +155,0,0,0,0,0,0,0,0,0,0,0,0,206,255,132, +111,195,237,250,233,183,93,3,0,0,0,0,239,255,253,255, +255,255,255,255,255,255,203,31,0,0,17,255,255,255,182,65, +15,11,65,188,255,255,222,22,0,51,255,255,129,0,0,0, +0,0,1,155,255,255,164,0,0,0,0,0,0,0,0,0, +0,0,8,231,255,252,20,0,0,0,0,0,0,0,0,0, +0,0,154,255,255,77,0,0,0,0,0,0,0,0,0,0, +0,120,255,255,98,0,0,0,0,0,0,0,0,0,0,0, +124,255,255,83,243,255,204,0,0,0,0,0,0,0,0,171, +255,255,38,195,255,251,25,0,0,0,0,0,0,20,246,255, +217,0,103,255,255,166,0,0,0,0,0,1,168,255,255,97, +0,4,202,255,255,182,62,12,11,60,180,255,255,183,1,0, +0,18,187,255,255,255,255,255,255,255,255,176,10,0,0,0, +0,0,77,173,229,251,241,217,158,68,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,52,161,224,249, +241,212,145,30,0,0,0,0,0,0,127,253,255,255,255,255, +255,255,245,65,0,0,0,0,114,255,255,199,66,11,22,114, +248,255,235,19,0,0,33,249,255,194,6,0,0,0,0,104, +255,255,132,0,0,151,255,251,34,0,0,0,0,0,4,232, +255,218,0,6,242,255,169,0,0,0,0,0,0,0,169,255, +255,5,66,255,255,90,0,0,0,0,0,0,0,0,0,0, +0,121,255,255,37,0,0,0,0,0,0,0,0,0,0,0, +164,255,251,2,40,152,218,246,244,209,133,24,0,0,0,193, +255,232,109,251,255,255,255,255,255,255,240,82,0,0,211,255, +251,255,249,127,33,6,29,111,242,255,251,69,0,220,255,255, +251,68,0,0,0,0,0,73,255,255,217,2,218,255,255,161, +0,0,0,0,0,0,0,203,255,255,54,205,255,255,84,0, +0,0,0,0,0,0,118,255,255,106,187,255,255,54,0,0, +0,0,0,0,0,82,255,255,125,144,255,255,58,0,0,0, +0,0,0,0,78,255,255,114,92,255,255,99,0,0,0,0, +0,0,0,117,255,255,74,15,246,255,188,0,0,0,0,0, +0,0,200,255,242,11,0,148,255,255,92,0,0,0,0,0, +88,255,255,134,0,0,11,215,255,251,133,35,5,28,119,247, +255,211,8,0,0,0,21,196,255,255,255,255,255,255,255,203, +23,0,0,0,0,0,1,90,185,235,250,231,182,95,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,232,255,255,255, +255,255,255,255,255,255,255,255,255,255,176,232,255,255,255,255, +255,255,255,255,255,255,255,255,255,170,0,0,0,0,0,0, +0,0,0,0,0,174,255,252,52,0,0,0,0,0,0,0, +0,0,0,115,255,255,138,0,0,0,0,0,0,0,0,0, +0,46,249,255,215,8,0,0,0,0,0,0,0,0,0,3, +205,255,252,55,0,0,0,0,0,0,0,0,0,0,112,255, +255,146,0,0,0,0,0,0,0,0,0,0,21,240,255,230, +14,0,0,0,0,0,0,0,0,0,0,148,255,255,93,0, +0,0,0,0,0,0,0,0,0,27,248,255,216,2,0,0, +0,0,0,0,0,0,0,0,145,255,255,91,0,0,0,0, +0,0,0,0,0,0,21,246,255,224,3,0,0,0,0,0, +0,0,0,0,0,116,255,255,119,0,0,0,0,0,0,0, +0,0,0,0,216,255,247,19,0,0,0,0,0,0,0,0, +0,0,60,255,255,174,0,0,0,0,0,0,0,0,0,0, +0,143,255,255,88,0,0,0,0,0,0,0,0,0,0,0, +219,255,250,12,0,0,0,0,0,0,0,0,0,0,40,255, +255,197,0,0,0,0,0,0,0,0,0,0,0,101,255,255, +136,0,0,0,0,0,0,0,0,0,0,0,154,255,255,80, +0,0,0,0,0,0,0,0,0,0,0,206,255,255,26,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,36,145,212,242,249,229,177,84,1,0,0,0, +0,0,0,96,248,255,255,255,255,255,255,255,185,12,0,0, +0,0,69,253,255,213,71,12,8,55,184,255,255,180,0,0, +0,0,211,255,247,30,0,0,0,0,3,204,255,255,69,0, +0,37,255,255,165,0,0,0,0,0,0,83,255,255,143,0, +0,69,255,255,128,0,0,0,0,0,0,48,255,255,158,0, +0,56,255,255,152,0,0,0,0,0,0,89,255,255,125,0, +0,7,236,255,240,29,0,0,0,0,5,211,255,254,49,0, +0,0,91,254,255,230,98,26,7,56,189,255,255,152,0,0, +0,0,0,70,219,255,255,255,255,255,255,255,127,2,0,0, +0,0,5,129,246,255,255,255,255,255,255,255,206,46,0,0, +0,1,165,255,255,207,76,15,8,48,155,255,255,242,46,0, +0,87,255,255,201,9,0,0,0,0,0,109,255,255,200,0, +0,193,255,255,59,0,0,0,0,0,0,0,210,255,255,38, +0,238,255,243,1,0,0,0,0,0,0,0,140,255,255,82, +2,254,255,224,0,0,0,0,0,0,0,0,117,255,255,88, +0,238,255,243,2,0,0,0,0,0,0,0,132,255,255,62, +0,182,255,255,66,0,0,0,0,0,0,0,194,255,245,8, +0,83,255,255,205,9,0,0,0,0,0,80,255,255,144,0, +0,0,171,255,255,204,76,17,5,37,133,250,255,219,15,0, +0,0,5,148,254,255,255,255,255,255,255,255,209,30,0,0, +0,0,0,0,44,146,210,242,250,231,182,97,4,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,50,156,220,246,247,216,148,37,0,0,0,0,0, +0,125,253,255,255,255,255,255,255,249,99,0,0,0,0,104, +255,255,211,74,13,11,71,206,255,254,78,0,0,20,244,255, +216,15,0,0,0,0,10,206,255,229,8,0,120,255,255,80, +0,0,0,0,0,0,71,255,255,96,0,191,255,248,6,0, +0,0,0,0,0,6,249,255,180,0,226,255,218,0,0,0, +0,0,0,0,0,227,255,238,0,233,255,219,0,0,0,0, +0,0,0,0,229,255,255,22,212,255,248,1,0,0,0,0, +0,0,6,249,255,255,48,155,255,255,58,0,0,0,0,0, +0,69,255,255,255,59,58,255,255,185,1,0,0,0,0,10, +203,255,255,255,58,0,162,255,255,176,54,9,12,79,211,255, +251,255,255,48,0,9,173,255,255,255,255,255,255,255,210,151, +255,255,26,0,0,0,79,177,231,250,237,193,110,6,168,255, +248,2,0,0,0,0,0,0,0,0,0,0,0,214,255,201, +0,0,0,0,0,0,0,0,0,0,0,18,254,255,141,0, +166,255,255,11,0,0,0,0,0,0,95,255,255,61,0,131, +255,255,62,0,0,0,0,0,1,199,255,216,1,0,53,255, +255,180,0,0,0,0,0,114,255,255,90,0,0,0,175,255, +255,172,44,7,40,150,255,255,176,0,0,0,0,16,197,255, +255,255,255,255,255,255,180,11,0,0,0,0,0,5,100,188, +235,251,237,186,87,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,172,255,255,100,172,255,255,100,172,255,255,100, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,172,255,255,100, +172,255,255,100,172,255,255,100,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,172,255,255,116,172,255,255,116,172,255,255,116, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,172,255,255,116, +172,255,255,116,172,255,255,114,0,22,255,98,0,63,255,48, +28,198,189,0,156,154,14,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,57,155,122,0,0,0,0,0,0,0,0,0,0, +0,0,25,119,216,255,255,136,0,0,0,0,0,0,0,0, +0,6,82,180,252,255,255,255,255,136,0,0,0,0,0,0, +0,46,144,235,255,255,255,255,255,240,152,41,0,0,0,0, +18,107,205,255,255,255,255,255,253,187,90,8,0,0,0,2, +71,169,248,255,255,255,255,255,220,124,29,0,0,0,0,0, +90,227,255,255,255,255,255,243,159,62,0,0,0,0,0,0, +0,0,152,255,255,255,254,193,97,12,0,0,0,0,0,0, +0,0,0,0,152,255,255,234,70,0,0,0,0,0,0,0, +0,0,0,0,0,0,152,255,255,255,254,192,95,11,0,0, +0,0,0,0,0,0,0,0,88,224,255,255,255,255,255,242, +156,59,0,0,0,0,0,0,0,0,0,2,69,166,247,255, +255,255,255,255,218,121,27,0,0,0,0,0,0,0,0,0, +16,106,203,255,255,255,255,255,253,183,86,7,0,0,0,0, +0,0,0,0,0,44,143,234,255,255,255,255,255,237,147,39, +0,0,0,0,0,0,0,0,0,5,82,179,252,255,255,255, +255,136,0,0,0,0,0,0,0,0,0,0,0,0,25,119, +216,255,255,136,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,58,156,122,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,168,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,160,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,134,149,51,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255, +255,209,112,21,0,0,0,0,0,0,0,0,0,0,0,0, +152,255,255,255,255,250,174,76,4,0,0,0,0,0,0,0, +0,0,48,158,243,255,255,255,255,255,231,137,40,0,0,0, +0,0,0,0,0,0,11,96,193,254,255,255,255,255,255,199, +101,14,0,0,0,0,0,0,0,0,0,34,130,225,255,255, +255,255,255,245,162,65,1,0,0,0,0,0,0,0,0,2, +68,165,246,255,255,255,255,255,222,79,0,0,0,0,0,0, +0,0,0,0,15,103,199,255,255,255,255,136,0,0,0,0, +0,0,0,0,0,0,0,0,0,81,239,255,255,136,0,0, +0,0,0,0,0,0,0,0,15,102,198,255,255,255,255,136, +0,0,0,0,0,0,0,1,67,164,246,255,255,255,255,255, +223,79,0,0,0,0,0,34,129,224,255,255,255,255,255,246, +163,66,1,0,0,0,11,95,192,254,255,255,255,255,255,200, +102,14,0,0,0,0,47,157,243,255,255,255,255,255,231,138, +41,0,0,0,0,0,0,0,152,255,255,255,255,250,175,77, +4,0,0,0,0,0,0,0,0,0,152,255,255,210,113,21, +0,0,0,0,0,0,0,0,0,0,0,0,135,150,52,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,42,153,218,245,245,216,151,40,0, +0,0,0,0,115,253,255,255,255,255,255,255,250,106,0,0, +0,94,255,255,184,57,10,13,72,213,255,255,82,0,11,237, +255,194,2,0,0,0,0,29,247,255,223,1,91,255,255,77, +0,0,0,0,0,0,163,255,255,48,150,255,255,13,0,0, +0,0,0,0,123,255,255,79,175,255,241,0,0,0,0,0, +0,0,143,255,255,67,0,0,0,0,0,0,0,0,0,8, +229,255,247,19,0,0,0,0,0,0,0,0,1,164,255,255, +136,0,0,0,0,0,0,0,0,1,154,255,255,174,3,0, +0,0,0,0,0,0,0,156,255,255,175,6,0,0,0,0, +0,0,0,0,120,255,255,177,4,0,0,0,0,0,0,0, +0,29,249,255,207,7,0,0,0,0,0,0,0,0,0,107, +255,255,67,0,0,0,0,0,0,0,0,0,0,157,255,252, +5,0,0,0,0,0,0,0,0,0,0,175,255,236,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,200,255,255,36,0,0,0,0,0,0,0,0,0, +0,200,255,255,36,0,0,0,0,0,0,0,0,0,0,200, +255,255,36,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,8,70,141,198,221,242,248,231,211,160,102,18,0, +0,0,0,0,0,0,0,0,0,0,6,109,232,255,255,255, +255,255,255,255,255,255,255,246,151,17,0,0,0,0,0,0, +0,0,36,215,255,255,209,136,70,38,18,4,18,43,94,171, +249,255,227,57,0,0,0,0,0,0,60,236,255,208,75,0, +0,0,0,0,0,0,0,0,0,29,178,255,240,48,0,0, +0,0,34,238,255,166,7,0,0,0,0,0,0,0,0,0, +0,0,0,1,148,255,215,5,0,0,4,208,255,163,1,0, +0,0,7,122,217,245,208,83,0,101,255,185,0,3,204,255, +102,0,0,98,255,216,10,0,0,0,20,206,255,255,255,255, +255,83,158,255,110,0,0,80,255,188,0,0,221,255,88,0, +0,0,6,203,255,250,118,20,21,144,205,215,255,35,0,0, +8,253,247,0,46,255,232,2,0,0,0,129,255,255,89,0, +0,0,1,214,254,215,0,0,0,0,231,255,15,115,255,166, +0,0,0,11,240,255,175,0,0,0,0,0,188,255,140,0, +0,0,0,229,255,17,148,255,119,0,0,0,75,255,255,60, +0,0,0,0,0,226,255,64,0,0,0,6,250,248,1,169, +255,98,0,0,0,118,255,247,3,0,0,0,0,47,255,240, +4,0,0,0,63,255,192,0,169,255,97,0,0,0,131,255, +236,0,0,0,0,0,156,255,175,0,0,0,0,173,255,105, +0,147,255,120,0,0,0,106,255,255,27,0,0,0,56,249, +255,136,0,0,0,93,255,223,9,0,99,255,178,0,0,0, +34,253,255,186,33,14,89,238,170,255,199,19,26,131,252,248, +58,0,0,30,255,245,18,0,0,0,140,255,255,255,255,255, +197,14,233,255,255,255,255,242,74,0,0,0,0,179,255,137, +0,0,0,1,115,220,248,216,121,8,0,59,205,246,228,154, +31,0,0,0,0,0,52,252,252,68,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,255,241,73,0,0,0,0,0,0,0,0,0,0,0,0, +0,10,147,133,0,0,0,0,0,3,161,255,253,139,21,0, +0,0,0,0,0,0,0,0,6,99,222,255,184,2,0,0, +0,0,0,1,141,255,255,245,163,98,44,23,5,16,38,80, +151,230,255,248,126,3,0,0,0,0,0,0,0,0,57,198, +255,255,255,255,255,255,255,255,255,255,249,159,34,0,0,0, +0,0,0,0,0,0,0,0,0,48,121,187,216,237,252,242, +214,170,103,20,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +164,255,255,246,13,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,12,245,255,255,255,95,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,96,255,255,255,255,186,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,191,255,250,237,255, +252,24,0,0,0,0,0,0,0,0,0,0,0,0,30,254, +255,176,151,255,255,111,0,0,0,0,0,0,0,0,0,0, +0,0,123,255,255,83,63,255,255,202,0,0,0,0,0,0, +0,0,0,0,0,0,217,255,238,7,2,229,255,255,37,0, +0,0,0,0,0,0,0,0,0,56,255,255,152,0,0,144, +255,255,128,0,0,0,0,0,0,0,0,0,0,150,255,255, +59,0,0,56,255,255,218,0,0,0,0,0,0,0,0,0, +6,237,255,221,0,0,0,0,223,255,255,53,0,0,0,0, +0,0,0,0,83,255,255,128,0,0,0,0,136,255,255,144, +0,0,0,0,0,0,0,0,178,255,255,36,0,0,0,0, +48,255,255,231,3,0,0,0,0,0,0,20,250,255,198,0, +0,0,0,0,0,215,255,255,69,0,0,0,0,0,0,110, +255,255,255,255,255,255,255,255,255,255,255,255,160,0,0,0, +0,0,0,205,255,255,255,255,255,255,255,255,255,255,255,255, +242,8,0,0,0,0,42,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,85,0,0,0,0,137,255,255,141,0,0, +0,0,0,0,0,0,142,255,255,176,0,0,0,2,228,255, +255,42,0,0,0,0,0,0,0,0,48,255,255,249,17,0, +0,70,255,255,199,0,0,0,0,0,0,0,0,0,0,211, +255,255,102,0,0,164,255,255,99,0,0,0,0,0,0,0, +0,0,0,118,255,255,192,0,13,245,255,244,12,0,0,0, +0,0,0,0,0,0,0,26,253,255,254,29,97,255,255,157, +0,0,0,0,0,0,0,0,0,0,0,0,187,255,255,118, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,200,255,255,255,255,255,255,255, +255,252,233,196,119,12,0,0,0,200,255,255,255,255,255,255, +255,255,255,255,255,255,229,43,0,0,200,255,255,255,255,255, +255,255,255,255,255,255,255,255,226,7,0,200,255,255,16,0, +0,0,0,4,18,58,147,254,255,255,103,0,200,255,255,16, +0,0,0,0,0,0,0,0,135,255,255,163,0,200,255,255, +16,0,0,0,0,0,0,0,0,49,255,255,189,0,200,255, +255,16,0,0,0,0,0,0,0,0,55,255,255,166,0,200, +255,255,16,0,0,0,0,0,0,0,0,150,255,255,94,0, +200,255,255,16,0,0,0,0,3,20,64,161,255,255,200,4, +0,200,255,255,255,255,255,255,255,255,255,255,255,255,175,16, +0,0,200,255,255,255,255,255,255,255,255,255,255,255,254,164, +37,0,0,200,255,255,255,255,255,255,255,255,255,255,255,255, +255,244,78,0,200,255,255,16,0,0,0,0,0,11,32,91, +203,255,255,244,27,200,255,255,16,0,0,0,0,0,0,0, +0,4,184,255,255,129,200,255,255,16,0,0,0,0,0,0, +0,0,0,64,255,255,185,200,255,255,16,0,0,0,0,0, +0,0,0,0,38,255,255,202,200,255,255,16,0,0,0,0, +0,0,0,0,0,81,255,255,184,200,255,255,16,0,0,0, +0,0,0,0,0,10,205,255,255,124,200,255,255,16,0,0, +0,0,0,3,25,86,209,255,255,245,26,200,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,101,0,200,255,255,255, +255,255,255,255,255,255,255,255,255,244,103,0,0,200,255,255, +255,255,255,255,255,255,251,232,197,126,26,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,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,74,155,211,235,250,234, +205,147,57,0,0,0,0,0,0,0,0,0,55,206,255,255, +255,255,255,255,255,255,255,184,24,0,0,0,0,0,0,82, +250,255,254,165,71,23,5,23,77,184,255,255,227,30,0,0, +0,0,67,251,255,241,66,0,0,0,0,0,0,0,126,255, +255,210,5,0,0,13,225,255,253,62,0,0,0,0,0,0, +0,0,1,177,255,255,116,0,0,122,255,255,148,0,0,0, +0,0,0,0,0,0,0,42,255,255,221,0,3,226,255,255, +35,0,0,0,0,0,0,0,0,0,0,0,209,255,255,27, +51,255,255,204,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,108,255,255,152,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,145,255,255,110,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,163,255,255,93, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +171,255,255,80,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,155,255,255,85,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,137,255,255,100,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,106,255,255,139, +0,0,0,0,0,0,0,0,0,0,0,0,144,255,255,81, +45,255,255,192,0,0,0,0,0,0,0,0,0,0,0,0, +206,255,255,35,1,234,255,252,24,0,0,0,0,0,0,0, +0,0,0,27,252,255,218,0,0,132,255,255,133,0,0,0, +0,0,0,0,0,0,0,134,255,255,124,0,0,19,243,255, +247,43,0,0,0,0,0,0,0,0,35,242,255,235,14,0, +0,0,90,255,255,225,41,0,0,0,0,0,0,27,209,255, +255,78,0,0,0,0,0,123,255,255,248,145,58,19,7,37, +117,232,255,254,108,0,0,0,0,0,0,0,83,232,255,255, +255,255,255,255,255,255,225,78,0,0,0,0,0,0,0,0, +0,8,100,175,226,246,244,221,173,96,8,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,148,255,255,255,255,255,255,255,255,246,221,180, +98,11,0,0,0,0,0,148,255,255,255,255,255,255,255,255, +255,255,255,255,225,72,0,0,0,0,148,255,255,255,255,255, +255,255,255,255,255,255,255,255,252,86,0,0,0,148,255,255, +88,0,0,0,0,2,21,60,139,246,255,255,250,51,0,0, +148,255,255,88,0,0,0,0,0,0,0,0,34,223,255,255, +203,1,0,148,255,255,88,0,0,0,0,0,0,0,0,0, +43,251,255,255,65,0,148,255,255,88,0,0,0,0,0,0, +0,0,0,0,156,255,255,159,0,148,255,255,88,0,0,0, +0,0,0,0,0,0,0,63,255,255,219,0,148,255,255,88, +0,0,0,0,0,0,0,0,0,0,8,252,255,255,13,148, +255,255,88,0,0,0,0,0,0,0,0,0,0,0,221,255, +255,33,148,255,255,88,0,0,0,0,0,0,0,0,0,0, +0,206,255,255,47,148,255,255,88,0,0,0,0,0,0,0, +0,0,0,0,205,255,255,41,148,255,255,88,0,0,0,0, +0,0,0,0,0,0,0,231,255,255,24,148,255,255,88,0, +0,0,0,0,0,0,0,0,0,12,253,255,243,0,148,255, +255,88,0,0,0,0,0,0,0,0,0,0,79,255,255,192, +0,148,255,255,88,0,0,0,0,0,0,0,0,0,0,173, +255,255,122,0,148,255,255,88,0,0,0,0,0,0,0,0, +0,56,254,255,253,31,0,148,255,255,88,0,0,0,0,0, +0,0,0,39,227,255,255,167,0,0,148,255,255,88,0,0, +0,0,1,13,50,131,244,255,255,241,31,0,0,148,255,255, +255,255,255,255,255,255,255,255,255,255,255,250,75,0,0,0, +148,255,255,255,255,255,255,255,255,255,255,255,255,227,64,0, +0,0,0,148,255,255,255,255,255,255,255,255,250,230,182,101, +8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,112,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,64,112,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,64,112,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,64,112,255,255,104,0, +0,0,0,0,0,0,0,0,0,0,0,0,112,255,255,104, +0,0,0,0,0,0,0,0,0,0,0,0,0,112,255,255, +104,0,0,0,0,0,0,0,0,0,0,0,0,0,112,255, +255,104,0,0,0,0,0,0,0,0,0,0,0,0,0,112, +255,255,104,0,0,0,0,0,0,0,0,0,0,0,0,0, +112,255,255,104,0,0,0,0,0,0,0,0,0,0,0,0, +0,112,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +72,0,112,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,72,0,112,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,72,0,112,255,255,104,0,0,0,0,0,0,0,0, +0,0,0,0,0,112,255,255,104,0,0,0,0,0,0,0, +0,0,0,0,0,0,112,255,255,104,0,0,0,0,0,0, +0,0,0,0,0,0,0,112,255,255,104,0,0,0,0,0, +0,0,0,0,0,0,0,0,112,255,255,104,0,0,0,0, +0,0,0,0,0,0,0,0,0,112,255,255,104,0,0,0, +0,0,0,0,0,0,0,0,0,0,112,255,255,104,0,0, +0,0,0,0,0,0,0,0,0,0,0,112,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,120,112,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,120,112,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,120,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,124,112,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,124,112,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,124,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,255,255,255,255,255,255,255,255,255, +255,255,12,0,112,255,255,255,255,255,255,255,255,255,255,255, +255,255,12,0,112,255,255,255,255,255,255,255,255,255,255,255, +255,255,12,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,112,255,255,124,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,43,130,200,230,250,243, +218,172,99,12,0,0,0,0,0,0,0,0,0,19,158,253, +255,255,255,255,255,255,255,255,239,97,0,0,0,0,0,0, +0,31,216,255,255,198,91,28,6,18,61,145,247,255,255,146, +0,0,0,0,0,14,220,255,255,118,1,0,0,0,0,0, +0,36,219,255,255,101,0,0,0,0,164,255,255,118,0,0, +0,0,0,0,0,0,0,40,247,255,231,9,0,0,49,255, +255,197,2,0,0,0,0,0,0,0,0,0,0,152,255,255, +88,0,0,163,255,255,79,0,0,0,0,0,0,0,0,0, +0,0,67,255,255,157,0,3,242,255,239,4,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,50,255,255,188, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,97,255,255,144,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,117,255,255,128,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,133,255,255,115,0, +0,0,0,0,0,88,255,255,255,255,255,255,255,255,255,28, +128,255,255,123,0,0,0,0,0,0,88,255,255,255,255,255, +255,255,255,255,28,109,255,255,139,0,0,0,0,0,0,88, +255,255,255,255,255,255,255,255,255,28,83,255,255,178,0,0, +0,0,0,0,0,0,0,0,0,0,0,163,255,255,28,25, +255,255,229,1,0,0,0,0,0,0,0,0,0,0,0,0, +185,255,255,28,0,217,255,255,55,0,0,0,0,0,0,0, +0,0,0,0,2,237,255,255,28,0,114,255,255,165,0,0, +0,0,0,0,0,0,0,0,0,70,255,255,255,28,0,12, +236,255,254,61,0,0,0,0,0,0,0,0,0,9,206,255, +255,255,28,0,0,77,254,255,235,54,0,0,0,0,0,0, +0,20,186,255,255,255,255,28,0,0,0,120,254,255,252,157, +70,25,5,16,56,133,237,255,235,143,255,255,28,0,0,0, +0,74,222,255,255,255,255,255,255,255,255,255,187,32,7,237, +255,28,0,0,0,0,0,4,90,168,222,245,251,237,204,149, +64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +164,255,255,76,0,0,0,0,0,0,0,0,0,0,128,255, +255,112,164,255,255,76,0,0,0,0,0,0,0,0,0,0, +128,255,255,112,164,255,255,76,0,0,0,0,0,0,0,0, +0,0,128,255,255,112,164,255,255,76,0,0,0,0,0,0, +0,0,0,0,128,255,255,112,164,255,255,76,0,0,0,0, +0,0,0,0,0,0,128,255,255,112,164,255,255,76,0,0, +0,0,0,0,0,0,0,0,128,255,255,112,164,255,255,76, +0,0,0,0,0,0,0,0,0,0,128,255,255,112,164,255, +255,76,0,0,0,0,0,0,0,0,0,0,128,255,255,112, +164,255,255,76,0,0,0,0,0,0,0,0,0,0,128,255, +255,112,164,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,112,164,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,112,164,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,112,164,255,255,76,0,0,0,0, +0,0,0,0,0,0,128,255,255,112,164,255,255,76,0,0, +0,0,0,0,0,0,0,0,128,255,255,112,164,255,255,76, +0,0,0,0,0,0,0,0,0,0,128,255,255,112,164,255, +255,76,0,0,0,0,0,0,0,0,0,0,128,255,255,112, +164,255,255,76,0,0,0,0,0,0,0,0,0,0,128,255, +255,112,164,255,255,76,0,0,0,0,0,0,0,0,0,0, +128,255,255,112,164,255,255,76,0,0,0,0,0,0,0,0, +0,0,128,255,255,112,164,255,255,76,0,0,0,0,0,0, +0,0,0,0,128,255,255,112,164,255,255,76,0,0,0,0, +0,0,0,0,0,0,128,255,255,112,164,255,255,76,0,0, +0,0,0,0,0,0,0,0,128,255,255,112,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,16,255,255,228,16,255,255,228, +16,255,255,228,16,255,255,228,16,255,255,228,16,255,255,228, +16,255,255,228,16,255,255,228,16,255,255,228,16,255,255,228, +16,255,255,228,16,255,255,228,16,255,255,228,16,255,255,228, +16,255,255,228,16,255,255,228,16,255,255,228,16,255,255,228, +16,255,255,228,16,255,255,228,16,255,255,228,16,255,255,228, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,8,255,255,216,0,0,0,0,0,0,0,0,0,8,255, +255,216,0,0,0,0,0,0,0,0,0,8,255,255,216,0, +0,0,0,0,0,0,0,0,8,255,255,216,0,0,0,0, +0,0,0,0,0,8,255,255,216,0,0,0,0,0,0,0, +0,0,8,255,255,216,0,0,0,0,0,0,0,0,0,8, +255,255,216,0,0,0,0,0,0,0,0,0,8,255,255,216, +0,0,0,0,0,0,0,0,0,8,255,255,216,0,0,0, +0,0,0,0,0,0,8,255,255,216,0,0,0,0,0,0, +0,0,0,8,255,255,216,0,0,0,0,0,0,0,0,0, +8,255,255,216,0,0,0,0,0,0,0,0,0,8,255,255, +216,0,0,0,0,0,0,0,0,0,8,255,255,216,0,0, +0,0,0,0,0,0,0,8,255,255,216,124,255,255,64,0, +0,0,0,0,9,255,255,215,123,255,255,64,0,0,0,0, +0,15,255,255,208,110,255,255,72,0,0,0,0,0,31,255, +255,188,82,255,255,109,0,0,0,0,0,67,255,255,146,24, +253,255,199,0,0,0,0,0,156,255,255,72,0,165,255,255, +158,33,5,32,136,254,255,191,2,0,15,198,255,255,255,255, +255,255,255,205,26,0,0,0,3,98,191,239,250,229,188,103, +6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,184,255,255,32,0,0,0,0,0,0,0,0, +20,209,255,255,238,51,184,255,255,32,0,0,0,0,0,0, +0,21,211,255,255,238,50,0,184,255,255,32,0,0,0,0, +0,0,23,212,255,255,237,49,0,0,184,255,255,32,0,0, +0,0,0,24,215,255,255,236,47,0,0,0,184,255,255,32, +0,0,0,0,26,216,255,255,235,46,0,0,0,0,184,255, +255,32,0,0,0,28,219,255,255,234,45,0,0,0,0,0, +184,255,255,32,0,0,29,220,255,255,233,43,0,0,0,0, +0,0,184,255,255,32,0,31,222,255,255,232,42,0,0,0, +0,0,0,0,184,255,255,32,33,224,255,255,235,41,0,0, +0,0,0,0,0,0,184,255,255,66,225,255,255,255,246,40, +0,0,0,0,0,0,0,0,184,255,255,240,255,255,255,255, +255,205,5,0,0,0,0,0,0,0,184,255,255,255,255,222, +61,240,255,255,134,0,0,0,0,0,0,0,184,255,255,255, +220,29,0,96,255,255,253,60,0,0,0,0,0,0,184,255, +255,219,28,0,0,0,177,255,255,223,14,0,0,0,0,0, +184,255,255,45,0,0,0,0,23,235,255,255,161,0,0,0, +0,0,184,255,255,32,0,0,0,0,0,84,255,255,255,84, +0,0,0,0,184,255,255,32,0,0,0,0,0,0,167,255, +255,237,26,0,0,0,184,255,255,32,0,0,0,0,0,0, +18,230,255,255,186,1,0,0,184,255,255,32,0,0,0,0, +0,0,0,74,255,255,255,111,0,0,184,255,255,32,0,0, +0,0,0,0,0,0,156,255,255,247,43,0,184,255,255,32, +0,0,0,0,0,0,0,0,13,224,255,255,207,6,184,255, +255,32,0,0,0,0,0,0,0,0,0,65,254,255,255,138, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,184,255,255,52, +0,0,0,0,0,0,0,0,0,0,0,184,255,255,52,0, +0,0,0,0,0,0,0,0,0,0,184,255,255,52,0,0, +0,0,0,0,0,0,0,0,0,184,255,255,52,0,0,0, +0,0,0,0,0,0,0,0,184,255,255,52,0,0,0,0, +0,0,0,0,0,0,0,184,255,255,52,0,0,0,0,0, +0,0,0,0,0,0,184,255,255,52,0,0,0,0,0,0, +0,0,0,0,0,184,255,255,52,0,0,0,0,0,0,0, +0,0,0,0,184,255,255,52,0,0,0,0,0,0,0,0, +0,0,0,184,255,255,52,0,0,0,0,0,0,0,0,0, +0,0,184,255,255,52,0,0,0,0,0,0,0,0,0,0, +0,184,255,255,52,0,0,0,0,0,0,0,0,0,0,0, +184,255,255,52,0,0,0,0,0,0,0,0,0,0,0,184, +255,255,52,0,0,0,0,0,0,0,0,0,0,0,184,255, +255,52,0,0,0,0,0,0,0,0,0,0,0,184,255,255, +52,0,0,0,0,0,0,0,0,0,0,0,184,255,255,52, +0,0,0,0,0,0,0,0,0,0,0,184,255,255,52,0, +0,0,0,0,0,0,0,0,0,0,184,255,255,52,0,0, +0,0,0,0,0,0,0,0,0,184,255,255,255,255,255,255, +255,255,255,255,255,255,255,24,184,255,255,255,255,255,255,255, +255,255,255,255,255,255,24,184,255,255,255,255,255,255,255,255, +255,255,255,255,255,24,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +200,255,255,255,141,0,0,0,0,0,0,0,0,0,0,0, +119,255,255,255,216,200,255,255,255,225,1,0,0,0,0,0, +0,0,0,0,0,203,255,255,255,216,200,255,255,255,255,55, +0,0,0,0,0,0,0,0,0,32,255,255,255,255,216,200, +255,245,248,255,140,0,0,0,0,0,0,0,0,0,116,255, +253,243,255,216,200,255,236,178,255,224,0,0,0,0,0,0, +0,0,0,201,255,194,227,255,216,200,255,239,93,255,255,54, +0,0,0,0,0,0,0,30,254,255,109,229,255,216,200,255, +244,15,249,255,139,0,0,0,0,0,0,0,114,255,254,26, +231,255,216,200,255,250,0,179,255,224,0,0,0,0,0,0, +0,198,255,196,0,236,255,216,200,255,254,0,94,255,255,53, +0,0,0,0,0,28,254,255,112,0,238,255,216,200,255,255, +0,15,249,255,138,0,0,0,0,0,111,255,254,29,0,240, +255,216,200,255,255,0,0,180,255,223,0,0,0,0,0,196, +255,199,0,0,240,255,216,200,255,255,0,0,96,255,255,52, +0,0,0,26,254,255,114,0,0,240,255,216,200,255,255,0, +0,16,250,255,137,0,0,0,109,255,255,31,0,0,240,255, +216,200,255,255,0,0,0,182,255,222,0,0,0,194,255,201, +0,0,0,240,255,216,200,255,255,0,0,0,97,255,255,51, +0,24,253,255,117,0,0,0,240,255,216,200,255,255,0,0, +0,17,250,255,136,0,107,255,255,33,0,0,0,240,255,216, +200,255,255,0,0,0,0,183,255,221,0,191,255,204,0,0, +0,0,240,255,216,200,255,255,0,0,0,0,98,255,255,73, +253,255,119,0,0,0,0,240,255,216,200,255,255,0,0,0, +0,18,251,255,226,255,255,35,0,0,0,0,240,255,216,200, +255,255,0,0,0,0,0,184,255,255,255,206,0,0,0,0, +0,240,255,216,200,255,255,0,0,0,0,0,99,255,255,255, +122,0,0,0,0,0,240,255,216,200,255,255,0,0,0,0, +0,19,251,255,255,38,0,0,0,0,0,240,255,216,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,184,255,255,241, +26,0,0,0,0,0,0,0,0,0,104,255,255,92,184,255, +255,255,172,0,0,0,0,0,0,0,0,0,104,255,255,92, +184,255,255,255,255,76,0,0,0,0,0,0,0,0,104,255, +255,92,184,255,255,255,255,224,11,0,0,0,0,0,0,0, +104,255,255,92,184,255,255,223,255,255,140,0,0,0,0,0, +0,0,104,255,255,92,184,255,255,75,254,255,251,48,0,0, +0,0,0,0,104,255,255,92,184,255,255,16,154,255,255,201, +2,0,0,0,0,0,104,255,255,92,184,255,255,16,17,232, +255,255,109,0,0,0,0,0,104,255,255,92,184,255,255,16, +0,90,255,255,241,27,0,0,0,0,104,255,255,92,184,255, +255,16,0,0,185,255,255,173,0,0,0,0,104,255,255,92, +184,255,255,16,0,0,34,246,255,255,76,0,0,0,104,255, +255,92,184,255,255,16,0,0,0,121,255,255,225,11,0,0, +104,255,255,92,184,255,255,16,0,0,0,5,211,255,255,141, +0,0,104,255,255,92,184,255,255,16,0,0,0,0,58,254, +255,252,49,0,104,255,255,92,184,255,255,16,0,0,0,0, +0,153,255,255,203,2,104,255,255,92,184,255,255,16,0,0, +0,0,0,16,232,255,255,110,104,255,255,92,184,255,255,16, +0,0,0,0,0,0,89,255,255,242,131,255,255,92,184,255, +255,16,0,0,0,0,0,0,0,184,255,255,245,255,255,92, +184,255,255,16,0,0,0,0,0,0,0,34,246,255,255,255, +255,92,184,255,255,16,0,0,0,0,0,0,0,0,121,255, +255,255,255,92,184,255,255,16,0,0,0,0,0,0,0,0, +5,211,255,255,255,92,184,255,255,16,0,0,0,0,0,0, +0,0,0,58,254,255,255,92,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,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,58,145,202,228,249,234, +212,174,98,22,0,0,0,0,0,0,0,0,0,0,41,200, +255,255,255,255,255,255,255,255,255,246,126,7,0,0,0,0, +0,0,0,80,248,255,255,194,94,32,7,19,55,135,242,255, +255,203,15,0,0,0,0,0,57,246,255,251,99,0,0,0, +0,0,0,0,27,199,255,255,197,7,0,0,0,10,226,255, +255,84,0,0,0,0,0,0,0,0,0,14,211,255,255,126, +0,0,0,126,255,255,159,0,0,0,0,0,0,0,0,0, +0,0,53,253,255,247,18,0,3,230,255,254,33,0,0,0, +0,0,0,0,0,0,0,0,0,181,255,255,109,0,68,255, +255,193,0,0,0,0,0,0,0,0,0,0,0,0,0,86, +255,255,185,0,127,255,255,134,0,0,0,0,0,0,0,0, +0,0,0,0,0,29,255,255,236,0,174,255,255,86,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,243,255,255,13, +195,255,255,66,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,227,255,255,30,208,255,255,52,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,221,255,255,32,197,255,255,58, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,255, +255,15,179,255,255,75,0,0,0,0,0,0,0,0,0,0, +0,0,0,12,254,255,246,1,140,255,255,118,0,0,0,0, +0,0,0,0,0,0,0,0,0,57,255,255,196,0,82,255, +255,177,0,0,0,0,0,0,0,0,0,0,0,0,0,129, +255,255,137,0,15,244,255,249,18,0,0,0,0,0,0,0, +0,0,0,0,1,215,255,255,42,0,0,154,255,255,133,0, +0,0,0,0,0,0,0,0,0,0,81,255,255,188,0,0, +0,30,243,255,247,49,0,0,0,0,0,0,0,0,0,16, +221,255,249,41,0,0,0,0,102,255,255,231,55,0,0,0, +0,0,0,0,21,190,255,255,106,0,0,0,0,0,0,110, +254,255,253,164,79,30,8,16,46,126,231,255,255,118,0,0, +0,0,0,0,0,0,69,212,255,255,255,255,255,255,255,255, +255,217,65,0,0,0,0,0,0,0,0,0,0,3,76,153, +209,233,250,233,209,156,73,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,112,255,255,255, +255,255,255,255,255,255,249,226,168,63,0,0,0,112,255,255, +255,255,255,255,255,255,255,255,255,255,255,150,2,0,112,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,130,0,112, +255,255,124,0,0,0,0,0,4,23,77,198,255,255,248,25, +112,255,255,124,0,0,0,0,0,0,0,0,7,210,255,255, +101,112,255,255,124,0,0,0,0,0,0,0,0,0,112,255, +255,144,112,255,255,124,0,0,0,0,0,0,0,0,0,86, +255,255,158,112,255,255,124,0,0,0,0,0,0,0,0,0, +113,255,255,140,112,255,255,124,0,0,0,0,0,0,0,0, +8,211,255,255,89,112,255,255,124,0,0,0,0,0,7,34, +96,210,255,255,239,12,112,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,110,0,112,255,255,255,255,255,255,255,255, +255,255,255,255,255,149,1,0,112,255,255,255,255,255,255,255, +255,255,250,229,173,70,0,0,0,112,255,255,124,0,0,0, +0,0,0,0,0,0,0,0,0,0,112,255,255,124,0,0, +0,0,0,0,0,0,0,0,0,0,0,112,255,255,124,0, +0,0,0,0,0,0,0,0,0,0,0,0,112,255,255,124, +0,0,0,0,0,0,0,0,0,0,0,0,0,112,255,255, +124,0,0,0,0,0,0,0,0,0,0,0,0,0,112,255, +255,124,0,0,0,0,0,0,0,0,0,0,0,0,0,112, +255,255,124,0,0,0,0,0,0,0,0,0,0,0,0,0, +112,255,255,124,0,0,0,0,0,0,0,0,0,0,0,0, +0,112,255,255,124,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,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,58,145, +202,228,249,234,212,174,98,21,0,0,0,0,0,0,0,0, +0,0,41,200,255,255,255,255,255,255,255,255,255,246,124,7, +0,0,0,0,0,0,0,80,248,255,255,177,86,29,7,19, +54,133,240,255,255,202,14,0,0,0,0,0,57,246,255,241, +71,0,0,0,0,0,0,0,25,196,255,255,195,7,0,0, +0,10,226,255,250,54,0,0,0,0,0,0,0,0,0,12, +207,255,255,123,0,0,0,126,255,255,131,0,0,0,0,0, +0,0,0,0,0,0,48,252,255,245,17,0,3,230,255,250, +20,0,0,0,0,0,0,0,0,0,0,0,0,177,255,255, +107,0,68,255,255,180,0,0,0,0,0,0,0,0,0,0, +0,0,0,83,255,255,183,0,127,255,255,127,0,0,0,0, +0,0,0,0,0,0,0,0,0,25,255,255,235,0,174,255, +255,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +242,255,255,13,195,255,255,65,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,225,255,255,29,208,255,255,52,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,223,255,255,33, +197,255,255,58,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,239,255,255,18,179,255,255,74,0,0,0,0,0,0, +0,0,0,0,0,0,0,17,255,255,245,2,140,255,255,115, +0,0,0,0,0,0,0,0,0,0,0,0,0,64,255,255, +198,0,83,255,255,171,0,0,0,0,0,0,0,0,0,0, +0,0,0,135,255,255,138,0,15,245,255,246,16,0,0,0, +0,0,0,0,23,72,0,0,2,224,255,255,54,0,0,156, +255,255,128,0,0,0,0,0,0,0,171,253,125,1,109,255, +255,198,0,0,0,32,245,255,248,51,0,0,0,0,0,47, +255,255,255,182,242,255,253,56,0,0,0,0,109,255,255,237, +63,0,0,0,0,0,75,233,255,255,255,255,123,0,0,0, +0,0,0,118,255,255,255,173,85,30,8,11,35,137,255,255, +255,255,149,6,0,0,0,0,0,0,72,214,255,255,255,255, +255,255,255,255,255,235,227,255,255,203,32,0,0,0,0,0, +0,4,77,154,210,233,251,242,215,163,78,5,19,189,255,255, +189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,4,145,247,42,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,37,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +92,255,255,255,255,255,255,255,255,255,254,236,214,155,71,0, +0,0,0,92,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,178,13,0,0,92,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,177,0,0,92,255,255,140,0,0,0, +0,0,0,1,17,64,183,255,255,255,52,0,92,255,255,140, +0,0,0,0,0,0,0,0,0,3,196,255,255,131,0,92, +255,255,140,0,0,0,0,0,0,0,0,0,0,98,255,255, +161,0,92,255,255,140,0,0,0,0,0,0,0,0,0,0, +74,255,255,162,0,92,255,255,140,0,0,0,0,0,0,0, +0,0,0,102,255,255,133,0,92,255,255,140,0,0,0,0, +0,0,0,0,0,5,202,255,255,55,0,92,255,255,140,0, +0,0,0,0,0,6,32,90,204,255,255,179,0,0,92,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,189,13,0, +0,92,255,255,255,255,255,255,255,255,255,255,255,255,255,236, +43,0,0,0,92,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,247,60,0,0,92,255,255,140,0,0,0,0,0, +0,5,23,80,213,255,255,210,0,0,92,255,255,140,0,0, +0,0,0,0,0,0,0,26,247,255,255,31,0,92,255,255, +140,0,0,0,0,0,0,0,0,0,0,184,255,255,65,0, +92,255,255,140,0,0,0,0,0,0,0,0,0,0,156,255, +255,77,0,92,255,255,140,0,0,0,0,0,0,0,0,0, +0,143,255,255,87,0,92,255,255,140,0,0,0,0,0,0, +0,0,0,0,131,255,255,100,0,92,255,255,140,0,0,0, +0,0,0,0,0,0,0,117,255,255,134,0,92,255,255,140, +0,0,0,0,0,0,0,0,0,0,101,255,255,228,26,92, +255,255,140,0,0,0,0,0,0,0,0,0,0,59,255,255, +255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,28,127,192,232,249,245,223,174, +98,11,0,0,0,0,0,0,0,116,249,255,255,255,255,255, +255,255,255,232,91,0,0,0,0,0,126,255,255,207,86,26, +5,15,51,134,246,255,255,118,0,0,0,43,252,255,199,8, +0,0,0,0,0,0,45,238,255,251,46,0,0,141,255,255, +55,0,0,0,0,0,0,0,0,114,255,255,148,0,0,193, +255,248,0,0,0,0,0,0,0,0,0,17,254,255,211,0, +0,204,255,252,7,0,0,0,0,0,0,0,0,0,221,255, +236,0,0,171,255,255,104,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,73,255,255,252,138,36,0,0,0,0,0, +0,0,0,0,0,0,0,0,133,255,255,255,255,213,144,79, +18,0,0,0,0,0,0,0,0,0,0,70,193,255,255,255, +255,255,254,210,142,62,2,0,0,0,0,0,0,0,0,37, +126,206,255,255,255,255,255,255,212,69,0,0,0,0,0,0, +0,0,0,0,30,108,189,253,255,255,255,252,86,0,0,0, +0,0,0,0,0,0,0,0,0,30,148,252,255,255,238,16, +0,0,0,0,0,0,0,0,0,0,0,0,0,65,244,255, +255,95,132,255,255,65,0,0,0,0,0,0,0,0,0,0, +131,255,255,139,112,255,255,100,0,0,0,0,0,0,0,0, +0,0,71,255,255,147,58,255,255,170,0,0,0,0,0,0, +0,0,0,0,77,255,255,123,4,225,255,249,33,0,0,0, +0,0,0,0,0,0,145,255,255,61,0,95,255,255,207,23, +0,0,0,0,0,0,0,75,248,255,209,1,0,0,150,255, +255,235,122,46,15,6,28,83,173,254,255,234,44,0,0,0, +0,115,246,255,255,255,255,255,255,255,255,255,177,30,0,0, +0,0,0,0,21,119,187,229,247,248,231,190,129,41,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,132,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,240,132,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,240, +132,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,240,0,0,0,0,0,0,0,68,255,255,176,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,68,255,255,176,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,68,255,255, +176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68, +255,255,176,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,68,255,255,176,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,68,255,255,176,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,68,255,255,176,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,68,255,255,176,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,68,255,255,176,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,68,255,255,176,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,68,255,255, +176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68, +255,255,176,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,68,255,255,176,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,68,255,255,176,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,68,255,255,176,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,68,255,255,176,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,68,255,255,176,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,68,255,255,176,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,68,255,255, +176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,132,255,255,116,0,0,0,0,0,0,0,0, +0,0,132,255,255,112,132,255,255,116,0,0,0,0,0,0, +0,0,0,0,132,255,255,112,132,255,255,116,0,0,0,0, +0,0,0,0,0,0,132,255,255,112,132,255,255,116,0,0, +0,0,0,0,0,0,0,0,132,255,255,112,132,255,255,116, +0,0,0,0,0,0,0,0,0,0,132,255,255,112,132,255, +255,116,0,0,0,0,0,0,0,0,0,0,132,255,255,112, +132,255,255,116,0,0,0,0,0,0,0,0,0,0,132,255, +255,112,132,255,255,116,0,0,0,0,0,0,0,0,0,0, +132,255,255,112,132,255,255,116,0,0,0,0,0,0,0,0, +0,0,132,255,255,112,132,255,255,116,0,0,0,0,0,0, +0,0,0,0,132,255,255,112,132,255,255,116,0,0,0,0, +0,0,0,0,0,0,132,255,255,112,132,255,255,116,0,0, +0,0,0,0,0,0,0,0,132,255,255,112,131,255,255,116, +0,0,0,0,0,0,0,0,0,0,132,255,255,111,124,255, +255,116,0,0,0,0,0,0,0,0,0,0,132,255,255,103, +113,255,255,121,0,0,0,0,0,0,0,0,0,0,134,255, +255,92,86,255,255,145,0,0,0,0,0,0,0,0,0,0, +147,255,255,65,48,255,255,174,0,0,0,0,0,0,0,0, +0,0,180,255,255,28,3,242,255,242,9,0,0,0,0,0, +0,0,0,5,237,255,225,0,0,158,255,255,96,0,0,0, +0,0,0,0,0,101,255,255,137,0,0,39,250,255,237,39, +0,0,0,0,0,0,53,241,255,243,25,0,0,0,106,255, +255,240,119,37,8,18,55,143,249,255,253,86,0,0,0,0, +0,97,242,255,255,255,255,255,255,255,255,236,81,0,0,0, +0,0,0,0,20,118,190,232,250,250,229,184,109,14,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,20,250,255,255, +40,0,0,0,0,0,0,0,0,0,0,1,227,255,255,93, +0,179,255,255,131,0,0,0,0,0,0,0,0,0,0,62, +255,255,245,12,0,88,255,255,220,0,0,0,0,0,0,0, +0,0,0,151,255,255,167,0,0,10,243,255,255,55,0,0, +0,0,0,0,0,0,4,235,255,255,75,0,0,0,162,255, +255,145,0,0,0,0,0,0,0,0,73,255,255,235,4,0, +0,0,71,255,255,232,3,0,0,0,0,0,0,0,162,255, +255,148,0,0,0,0,3,232,255,255,70,0,0,0,0,0, +0,8,242,255,255,57,0,0,0,0,0,145,255,255,160,0, +0,0,0,0,0,84,255,255,221,0,0,0,0,0,0,54, +255,255,241,8,0,0,0,0,0,173,255,255,130,0,0,0, +0,0,0,0,218,255,255,84,0,0,0,0,14,247,255,255, +39,0,0,0,0,0,0,0,128,255,255,174,0,0,0,0, +95,255,255,203,0,0,0,0,0,0,0,0,37,255,255,248, +16,0,0,0,184,255,255,111,0,0,0,0,0,0,0,0, +0,201,255,255,99,0,0,21,251,255,252,24,0,0,0,0, +0,0,0,0,0,110,255,255,189,0,0,106,255,255,185,0, +0,0,0,0,0,0,0,0,0,23,252,255,253,25,0,195, +255,255,93,0,0,0,0,0,0,0,0,0,0,0,184,255, +255,113,29,254,255,245,12,0,0,0,0,0,0,0,0,0, +0,0,93,255,255,204,117,255,255,166,0,0,0,0,0,0, +0,0,0,0,0,0,12,245,255,255,227,255,255,75,0,0, +0,0,0,0,0,0,0,0,0,0,0,167,255,255,255,255, +235,4,0,0,0,0,0,0,0,0,0,0,0,0,0,76, +255,255,255,255,148,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,5,235,255,255,255,56,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,150,255,255,221,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,84,255,255,210, +0,0,0,0,0,0,0,0,133,255,255,237,3,0,0,0, +0,0,0,0,103,255,255,198,19,254,255,254,17,0,0,0, +0,0,0,0,207,255,255,255,58,0,0,0,0,0,0,0, +165,255,255,133,0,209,255,255,77,0,0,0,0,0,0,25, +254,255,255,255,131,0,0,0,0,0,0,0,226,255,255,67, +0,143,255,255,139,0,0,0,0,0,0,98,255,255,255,255, +205,0,0,0,0,0,0,32,255,255,248,8,0,78,255,255, +200,0,0,0,0,0,0,171,255,255,210,255,254,24,0,0, +0,0,0,93,255,255,191,0,0,15,252,255,251,11,0,0, +0,0,4,240,255,218,108,255,255,96,0,0,0,0,0,155, +255,255,125,0,0,0,203,255,255,67,0,0,0,0,63,255, +255,147,36,255,255,170,0,0,0,0,0,217,255,255,60,0, +0,0,137,255,255,129,0,0,0,0,136,255,255,75,0,220, +255,239,4,0,0,0,22,255,255,244,5,0,0,0,72,255, +255,190,0,0,0,0,210,255,248,10,0,149,255,255,61,0, +0,0,84,255,255,184,0,0,0,0,11,250,255,246,5,0, +0,27,255,255,188,0,0,78,255,255,135,0,0,0,145,255, +255,118,0,0,0,0,0,197,255,255,57,0,0,101,255,255, +116,0,0,12,249,255,209,0,0,0,207,255,255,52,0,0, +0,0,0,132,255,255,119,0,0,174,255,255,44,0,0,0, +191,255,255,27,0,14,253,255,240,2,0,0,0,0,0,66, +255,255,180,0,5,242,255,228,0,0,0,0,119,255,255,100, +0,74,255,255,177,0,0,0,0,0,0,8,248,255,239,2, +66,255,255,157,0,0,0,0,48,255,255,174,0,136,255,255, +111,0,0,0,0,0,0,0,191,255,255,47,139,255,255,85, +0,0,0,0,1,231,255,242,5,197,255,255,45,0,0,0, +0,0,0,0,126,255,255,109,213,255,252,17,0,0,0,0, +0,161,255,255,74,249,255,234,1,0,0,0,0,0,0,0, +60,255,255,200,255,255,198,0,0,0,0,0,0,89,255,255, +202,255,255,169,0,0,0,0,0,0,0,0,5,244,255,255, +255,255,126,0,0,0,0,0,0,20,253,255,255,255,255,104, +0,0,0,0,0,0,0,0,0,185,255,255,255,255,54,0, +0,0,0,0,0,0,202,255,255,255,255,38,0,0,0,0, +0,0,0,0,0,120,255,255,255,236,2,0,0,0,0,0, +0,0,131,255,255,255,228,0,0,0,0,0,0,0,0,0, +0,54,255,255,255,167,0,0,0,0,0,0,0,0,59,255, +255,255,162,0,0,0,0,0,0,0,0,0,0,3,241,255, +255,95,0,0,0,0,0,0,0,0,3,239,255,255,96,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,146,255,255, +243,31,0,0,0,0,0,0,0,0,0,163,255,255,205,4, +0,10,219,255,255,185,0,0,0,0,0,0,0,0,73,255, +255,249,44,0,0,0,59,253,255,255,96,0,0,0,0,0, +0,12,225,255,255,120,0,0,0,0,0,140,255,255,238,24, +0,0,0,0,0,149,255,255,201,3,0,0,0,0,0,8, +215,255,255,173,0,0,0,0,59,254,255,247,40,0,0,0, +0,0,0,0,54,252,255,255,84,0,0,7,216,255,255,115, +0,0,0,0,0,0,0,0,0,135,255,255,232,18,0,134, +255,255,196,2,0,0,0,0,0,0,0,0,0,6,211,255, +255,161,48,251,255,245,37,0,0,0,0,0,0,0,0,0, +0,0,50,251,255,255,226,255,255,110,0,0,0,0,0,0, +0,0,0,0,0,0,0,129,255,255,255,255,192,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,5,219,255,255,254, +36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54, +252,255,255,255,118,0,0,0,0,0,0,0,0,0,0,0, +0,0,9,216,255,255,255,255,248,41,0,0,0,0,0,0, +0,0,0,0,0,0,145,255,255,186,143,255,255,202,3,0, +0,0,0,0,0,0,0,0,0,66,254,255,243,31,11,223, +255,255,122,0,0,0,0,0,0,0,0,0,14,225,255,255, +105,0,0,71,255,255,249,44,0,0,0,0,0,0,0,0, +159,255,255,191,1,0,0,0,163,255,255,205,4,0,0,0, +0,0,0,78,255,255,245,34,0,0,0,0,19,234,255,255, +126,0,0,0,0,0,20,233,255,255,110,0,0,0,0,0, +0,91,255,255,250,47,0,0,0,0,173,255,255,195,2,0, +0,0,0,0,0,0,182,255,255,208,5,0,0,93,255,255, +246,38,0,0,0,0,0,0,0,0,30,243,255,255,130,0, +28,239,255,255,116,0,0,0,0,0,0,0,0,0,0,110, +255,255,251,50,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,33,247,255,255, +78,0,0,0,0,0,0,0,0,0,0,9,225,255,255,128, +0,132,255,255,220,7,0,0,0,0,0,0,0,0,0,128, +255,255,225,9,0,10,227,255,255,122,0,0,0,0,0,0, +0,0,31,246,255,255,86,0,0,0,88,255,255,244,27,0, +0,0,0,0,0,0,171,255,255,192,0,0,0,0,0,194, +255,255,165,0,0,0,0,0,0,64,255,255,252,47,0,0, +0,0,0,48,253,255,255,59,0,0,0,0,3,210,255,255, +150,0,0,0,0,0,0,0,151,255,255,206,2,0,0,0, +107,255,255,237,19,0,0,0,0,0,0,0,19,238,255,255, +102,0,0,18,237,255,255,107,0,0,0,0,0,0,0,0, +0,108,255,255,235,16,0,150,255,255,210,3,0,0,0,0, +0,0,0,0,0,3,210,255,255,146,46,252,255,255,65,0, +0,0,0,0,0,0,0,0,0,0,65,255,255,251,210,255, +255,171,0,0,0,0,0,0,0,0,0,0,0,0,0,171, +255,255,255,255,246,31,0,0,0,0,0,0,0,0,0,0, +0,0,0,30,246,255,255,255,128,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,127,255,255,226,9,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,168, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +64,255,255,168,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,64,255,255,168,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,64,255,255,168,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,64,255,255,168, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +64,255,255,168,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,64,255,255,168,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,64,255,255,168,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,88,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,164,0,88, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,164, +0,88,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,163,0,0,0,0,0,0,0,0,0,0,0,0,0,133, +255,255,247,53,0,0,0,0,0,0,0,0,0,0,0,0, +86,255,255,255,89,0,0,0,0,0,0,0,0,0,0,0, +0,49,245,255,255,133,0,0,0,0,0,0,0,0,0,0, +0,0,22,225,255,255,176,1,0,0,0,0,0,0,0,0, +0,0,0,5,195,255,255,210,12,0,0,0,0,0,0,0, +0,0,0,0,0,153,255,255,234,31,0,0,0,0,0,0, +0,0,0,0,0,0,107,255,255,249,60,0,0,0,0,0, +0,0,0,0,0,0,0,64,251,255,255,99,0,0,0,0, +0,0,0,0,0,0,0,0,32,235,255,255,143,0,0,0, +0,0,0,0,0,0,0,0,0,11,209,255,255,184,3,0, +0,0,0,0,0,0,0,0,0,0,1,173,255,255,216,16, +0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,238, +37,0,0,0,0,0,0,0,0,0,0,0,0,82,254,255, +252,68,0,0,0,0,0,0,0,0,0,0,0,0,45,244, +255,255,109,0,0,0,0,0,0,0,0,0,0,0,0,19, +222,255,255,153,0,0,0,0,0,0,0,0,0,0,0,0, +4,190,255,255,192,5,0,0,0,0,0,0,0,0,0,0, +0,0,76,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,164,80,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,164,80,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,164,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,32,255,255,255,255,255,128,32,255,255,255,255, +255,128,32,255,255,92,0,0,0,32,255,255,92,0,0,0, +32,255,255,92,0,0,0,32,255,255,92,0,0,0,32,255, +255,92,0,0,0,32,255,255,92,0,0,0,32,255,255,92, +0,0,0,32,255,255,92,0,0,0,32,255,255,92,0,0, +0,32,255,255,92,0,0,0,32,255,255,92,0,0,0,32, +255,255,92,0,0,0,32,255,255,92,0,0,0,32,255,255, +92,0,0,0,32,255,255,92,0,0,0,32,255,255,92,0, +0,0,32,255,255,92,0,0,0,32,255,255,92,0,0,0, +32,255,255,92,0,0,0,32,255,255,92,0,0,0,32,255, +255,92,0,0,0,32,255,255,92,0,0,0,32,255,255,92, +0,0,0,32,255,255,92,0,0,0,32,255,255,255,255,255, +128,32,255,255,255,255,255,128,211,255,120,0,0,0,0,0, +0,0,0,114,255,217,0,0,0,0,0,0,0,0,22,250, +255,58,0,0,0,0,0,0,0,0,175,255,156,0,0,0, +0,0,0,0,0,78,255,242,10,0,0,0,0,0,0,0, +4,232,255,94,0,0,0,0,0,0,0,0,139,255,191,0, +0,0,0,0,0,0,0,41,255,254,34,0,0,0,0,0, +0,0,0,200,255,130,0,0,0,0,0,0,0,0,102,255, +225,2,0,0,0,0,0,0,0,14,246,255,68,0,0,0, +0,0,0,0,0,164,255,166,0,0,0,0,0,0,0,0, +66,255,247,16,0,0,0,0,0,0,0,1,223,255,104,0, +0,0,0,0,0,0,0,127,255,202,0,0,0,0,0,0, +0,0,31,254,255,43,0,0,0,0,0,0,0,0,188,255, +140,0,0,0,0,0,0,0,0,91,255,233,4,0,0,0, +0,0,0,0,9,240,255,79,0,0,0,0,0,0,0,0, +152,255,176,0,0,0,0,0,0,0,0,54,255,251,22,0, +0,0,0,0,0,0,0,212,255,115,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,80,255,255,255, +255,255,80,80,255,255,255,255,255,80,0,0,0,48,255,255, +80,0,0,0,48,255,255,80,0,0,0,48,255,255,80,0, +0,0,48,255,255,80,0,0,0,48,255,255,80,0,0,0, +48,255,255,80,0,0,0,48,255,255,80,0,0,0,48,255, +255,80,0,0,0,48,255,255,80,0,0,0,48,255,255,80, +0,0,0,48,255,255,80,0,0,0,48,255,255,80,0,0, +0,48,255,255,80,0,0,0,48,255,255,80,0,0,0,48, +255,255,80,0,0,0,48,255,255,80,0,0,0,48,255,255, +80,0,0,0,48,255,255,80,0,0,0,48,255,255,80,0, +0,0,48,255,255,80,0,0,0,48,255,255,80,0,0,0, +48,255,255,80,0,0,0,48,255,255,80,0,0,0,48,255, +255,80,80,255,255,255,255,255,80,80,255,255,255,255,255,80, +0,0,0,0,0,75,255,255,90,0,0,0,0,0,0,0, +0,0,0,176,255,255,192,0,0,0,0,0,0,0,0,0, +25,251,255,255,255,39,0,0,0,0,0,0,0,0,122,255, +238,222,255,140,0,0,0,0,0,0,0,1,221,255,144,121, +255,235,6,0,0,0,0,0,0,69,255,255,42,23,250,255, +88,0,0,0,0,0,0,170,255,194,0,0,171,255,189,0, +0,0,0,0,21,249,255,92,0,0,69,255,254,36,0,0, +0,0,117,255,237,8,0,0,1,220,255,137,0,0,0,0, +217,255,142,0,0,0,0,119,255,233,5,0,0,63,255,255, +40,0,0,0,0,22,250,255,85,0,0,164,255,193,0,0, +0,0,0,0,170,255,187,0,17,247,255,91,0,0,0,0, +0,0,67,255,254,34,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,176,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,25,217,255,250,46,0,0,0,27,220,255,205, +4,0,0,0,30,223,255,124,0,0,0,0,32,225,249,45, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,54,155,217,246,250, +233,195,126,26,0,0,0,0,0,1,147,255,255,255,255,255, +255,255,255,243,70,0,0,0,0,98,255,255,192,60,10,12, +44,141,255,255,230,3,0,0,0,198,255,226,8,0,0,0, +0,0,148,255,255,50,0,0,0,242,255,146,0,0,0,0, +0,0,81,255,255,73,0,0,0,0,0,0,0,0,0,0, +0,0,98,255,255,76,0,0,0,0,0,0,0,0,0,10, +47,99,231,255,255,76,0,0,0,0,0,62,141,189,227,254, +255,255,255,255,255,76,0,0,0,22,188,255,255,255,254,222, +188,162,181,255,255,76,0,0,7,208,255,255,177,70,11,0, +0,0,88,255,255,76,0,0,99,255,255,153,0,0,0,0, +0,0,88,255,255,76,0,0,171,255,255,32,0,0,0,0, +0,0,99,255,255,76,0,0,198,255,250,0,0,0,0,0, +0,0,136,255,255,76,0,0,189,255,255,16,0,0,0,0, +0,6,222,255,255,76,0,0,138,255,255,102,0,0,0,0, +4,157,255,255,255,76,0,0,36,250,255,242,96,15,25,88, +205,255,234,255,255,121,9,1,0,101,254,255,255,255,255,255, +255,179,27,242,255,255,255,12,0,0,65,185,241,246,221,159, +64,0,0,75,218,252,226,9,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,68,255,255,76,0,0,0,0, +0,0,0,0,0,0,0,68,255,255,76,0,0,0,0,0, +0,0,0,0,0,0,68,255,255,76,0,0,0,0,0,0, +0,0,0,0,0,68,255,255,76,0,0,0,0,0,0,0, +0,0,0,0,68,255,255,76,0,0,0,0,0,0,0,0, +0,0,0,68,255,255,76,19,130,209,244,242,205,121,10,0, +0,0,68,255,255,137,237,255,255,255,255,255,255,221,34,0, +0,68,255,255,255,255,177,53,8,32,141,255,255,222,11,0, +68,255,255,255,152,0,0,0,0,0,133,255,255,134,0,68, +255,255,227,8,0,0,0,0,0,9,235,255,234,5,68,255, +255,138,0,0,0,0,0,0,0,151,255,255,53,68,255,255, +82,0,0,0,0,0,0,0,91,255,255,100,68,255,255,51, +0,0,0,0,0,0,0,56,255,255,122,68,255,255,39,0, +0,0,0,0,0,0,43,255,255,135,68,255,255,47,0,0, +0,0,0,0,0,48,255,255,126,68,255,255,62,0,0,0, +0,0,0,0,66,255,255,108,68,255,255,95,0,0,0,0, +0,0,0,103,255,255,64,68,255,255,153,0,0,0,0,0, +0,0,164,255,247,13,68,255,255,235,10,0,0,0,0,0, +14,242,255,171,0,68,255,255,255,138,0,0,0,0,0,145, +255,254,52,0,68,255,255,251,255,154,42,8,36,149,255,255, +146,0,0,68,255,255,97,239,255,255,255,255,255,255,169,4, +0,0,0,0,0,0,30,144,218,247,236,188,86,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +15,126,208,242,246,217,152,42,0,0,0,0,0,0,54,229, +255,255,255,255,255,255,250,102,0,0,0,0,41,240,255,231, +91,19,12,74,222,255,254,58,0,0,1,197,255,246,44,0, +0,0,0,43,252,255,183,0,0,67,255,255,144,0,0,0, +0,0,0,169,255,250,11,0,159,255,255,55,0,0,0,0, +0,0,99,255,255,54,0,216,255,242,4,0,0,0,0,0, +0,0,0,0,0,5,254,255,207,0,0,0,0,0,0,0, +0,0,0,0,21,255,255,189,0,0,0,0,0,0,0,0, +0,0,0,32,255,255,177,0,0,0,0,0,0,0,0,0, +0,0,19,255,255,197,0,0,0,0,0,0,0,0,0,0, +0,2,244,255,222,0,0,0,0,0,0,0,0,0,0,0, +0,192,255,253,26,0,0,0,0,0,0,108,255,255,48,0, +107,255,255,114,0,0,0,0,0,0,191,255,240,4,0,14, +232,255,224,14,0,0,0,0,80,255,255,150,0,0,0,83, +254,255,205,63,10,19,101,242,255,237,24,0,0,0,0,107, +251,255,255,255,255,255,255,229,53,0,0,0,0,0,0,48, +161,226,250,240,199,119,15,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,208,255,184,0, +0,0,0,0,0,0,0,0,0,0,0,208,255,184,0,0, +0,0,0,0,0,0,0,0,0,0,208,255,184,0,0,0, +0,0,0,0,0,0,0,0,0,208,255,184,0,0,0,0, +0,0,0,0,0,0,0,0,208,255,184,0,0,0,0,45, +158,224,248,232,186,89,0,208,255,184,0,0,0,94,248,255, +255,255,255,255,255,164,212,255,184,0,0,61,251,255,234,95, +18,13,89,232,255,255,255,184,0,2,210,255,241,40,0,0, +0,0,40,242,255,255,184,0,77,255,255,118,0,0,0,0, +0,0,123,255,255,184,0,167,255,253,22,0,0,0,0,0, +0,29,255,255,184,0,228,255,213,0,0,0,0,0,0,0, +0,230,255,184,15,255,255,176,0,0,0,0,0,0,0,0, +197,255,184,33,255,255,162,0,0,0,0,0,0,0,0,184, +255,184,38,255,255,161,0,0,0,0,0,0,0,0,188,255, +184,24,255,255,176,0,0,0,0,0,0,0,0,203,255,184, +3,245,255,213,0,0,0,0,0,0,0,0,240,255,184,0, +193,255,254,21,0,0,0,0,0,0,47,255,255,184,0,108, +255,255,113,0,0,0,0,0,0,143,255,255,184,0,15,232, +255,235,25,0,0,0,0,49,248,255,255,184,0,0,81,254, +255,223,82,17,13,89,236,254,227,255,184,0,0,0,103,250, +255,255,255,255,255,251,107,168,255,184,0,0,0,0,49,168, +232,248,225,160,49,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,85,180,230,247,219,154,45,0, +0,0,0,0,0,14,187,255,255,255,255,255,255,253,128,2, +0,0,0,6,198,255,253,139,32,9,51,174,255,255,135,0, +0,0,129,255,255,112,0,0,0,0,1,171,255,254,54,0, +15,245,255,201,1,0,0,0,0,0,27,252,255,166,0,103, +255,255,95,0,0,0,0,0,0,0,193,255,237,1,166,255, +255,31,0,0,0,0,0,0,0,135,255,255,38,210,255,252, +2,0,0,0,0,0,0,0,101,255,255,71,229,255,255,255, +255,255,255,255,255,255,255,255,255,255,87,234,255,255,255,255, +255,255,255,255,255,255,255,255,255,94,219,255,233,0,0,0, +0,0,0,0,0,0,0,0,0,189,255,252,7,0,0,0, +0,0,0,0,0,0,0,0,133,255,255,57,0,0,0,0, +0,0,0,0,0,0,0,47,255,255,145,0,0,0,0,0, +0,0,167,255,248,11,0,183,255,247,43,0,0,0,0,0, +73,253,255,151,0,0,27,233,255,233,96,21,6,38,131,249, +255,224,17,0,0,0,39,220,255,255,255,255,255,255,255,201, +34,0,0,0,0,0,8,115,202,242,244,219,174,88,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,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,53,177,232,251,216, +0,0,21,244,255,255,255,216,0,0,84,255,255,150,13,0, +0,0,104,255,255,73,0,0,0,0,104,255,255,61,0,0, +152,255,255,255,255,255,255,216,152,255,255,255,255,255,255,216, +0,0,104,255,255,52,0,0,0,0,104,255,255,52,0,0, +0,0,104,255,255,52,0,0,0,0,104,255,255,52,0,0, +0,0,104,255,255,52,0,0,0,0,104,255,255,52,0,0, +0,0,104,255,255,52,0,0,0,0,104,255,255,52,0,0, +0,0,104,255,255,52,0,0,0,0,104,255,255,52,0,0, +0,0,104,255,255,52,0,0,0,0,104,255,255,52,0,0, +0,0,104,255,255,52,0,0,0,0,104,255,255,52,0,0, +0,0,104,255,255,52,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,40,157,226,248, +225,173,70,0,200,255,168,0,0,0,93,248,255,255,255,255, +255,255,151,202,255,168,0,0,74,253,255,210,74,16,16,91, +230,255,255,255,168,0,11,229,255,219,18,0,0,0,0,36, +239,255,255,168,0,99,255,255,90,0,0,0,0,0,0,120, +255,255,168,0,181,255,250,10,0,0,0,0,0,0,30,255, +255,168,0,235,255,204,0,0,0,0,0,0,0,0,235,255, +168,8,255,255,178,0,0,0,0,0,0,0,0,212,255,168, +23,255,255,162,0,0,0,0,0,0,0,0,205,255,168,18, +255,255,162,0,0,0,0,0,0,0,0,223,255,168,3,254, +255,180,0,0,0,0,0,0,0,5,246,255,168,0,219,255, +225,0,0,0,0,0,0,0,63,255,255,168,0,160,255,255, +50,0,0,0,0,0,0,158,255,255,168,0,62,255,255,188, +3,0,0,0,0,66,253,255,255,168,0,0,167,255,255,179, +47,7,31,120,245,254,250,255,167,0,0,10,172,255,255,255, +255,255,255,254,111,230,255,161,0,0,0,0,80,184,237,249, +229,174,63,0,239,255,145,0,0,0,0,0,0,0,0,0, +0,0,9,254,255,111,0,0,0,0,0,0,0,0,0,0, +0,63,255,255,52,0,134,255,255,68,0,0,0,0,0,3, +191,255,214,0,0,46,253,255,230,90,22,3,19,74,195,255, +252,58,0,0,0,102,250,255,255,255,255,255,255,255,243,87, +0,0,0,0,0,29,124,199,228,249,233,204,128,30,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,16,255,255,148, +0,0,0,0,0,0,0,0,0,0,16,255,255,148,0,0, +0,0,0,0,0,0,0,0,16,255,255,148,0,0,0,0, +0,0,0,0,0,0,16,255,255,148,0,0,0,0,0,0, +0,0,0,0,16,255,255,148,0,0,0,0,0,0,0,0, +0,0,16,255,255,148,1,91,188,231,249,228,168,53,0,0, +16,255,255,156,179,255,255,255,255,255,255,250,84,0,16,255, +255,252,255,197,68,10,17,96,243,255,239,18,16,255,255,255, +178,4,0,0,0,0,110,255,255,99,16,255,255,253,32,0, +0,0,0,0,33,255,255,150,16,255,255,208,0,0,0,0, +0,0,8,255,255,175,16,255,255,167,0,0,0,0,0,0, +0,252,255,183,16,255,255,152,0,0,0,0,0,0,0,252, +255,184,16,255,255,148,0,0,0,0,0,0,0,252,255,184, +16,255,255,148,0,0,0,0,0,0,0,252,255,184,16,255, +255,148,0,0,0,0,0,0,0,252,255,184,16,255,255,148, +0,0,0,0,0,0,0,252,255,184,16,255,255,148,0,0, +0,0,0,0,0,252,255,184,16,255,255,148,0,0,0,0, +0,0,0,252,255,184,16,255,255,148,0,0,0,0,0,0, +0,252,255,184,16,255,255,148,0,0,0,0,0,0,0,252, +255,184,16,255,255,148,0,0,0,0,0,0,0,252,255,184, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,16,255,255,156,16,255,255,156,16,255,255,156, +0,0,0,0,0,0,0,0,16,255,255,156,16,255,255,156, +16,255,255,156,16,255,255,156,16,255,255,156,16,255,255,156, +16,255,255,156,16,255,255,156,16,255,255,156,16,255,255,156, +16,255,255,156,16,255,255,156,16,255,255,156,16,255,255,156, +16,255,255,156,16,255,255,156,16,255,255,156,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,16,255,255,148,0,0,16,255,255,148, +0,0,16,255,255,148,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,13,220,220,127,0,0,16,255,255,148,0,0, +16,255,255,148,0,0,16,255,255,148,0,0,16,255,255,148, +0,0,16,255,255,148,0,0,16,255,255,148,0,0,16,255, +255,148,0,0,16,255,255,148,0,0,16,255,255,148,0,0, +16,255,255,148,0,0,16,255,255,148,0,0,16,255,255,148, +0,0,16,255,255,148,0,0,16,255,255,148,0,0,16,255, +255,148,0,0,16,255,255,148,0,0,16,255,255,148,0,0, +27,255,255,137,42,90,166,255,255,106,144,255,255,255,241,23, +144,254,236,177,48,0,0,0,0,0,0,0,32,255,255,104, +0,0,0,0,0,0,0,0,0,0,32,255,255,104,0,0, +0,0,0,0,0,0,0,0,32,255,255,104,0,0,0,0, +0,0,0,0,0,0,32,255,255,104,0,0,0,0,0,0, +0,0,0,0,32,255,255,104,0,0,0,0,0,0,0,0, +0,0,32,255,255,104,0,0,0,0,0,67,251,255,234,40, +32,255,255,104,0,0,0,0,38,238,255,239,47,0,32,255, +255,104,0,0,0,17,217,255,243,56,0,0,32,255,255,104, +0,0,4,188,255,247,65,0,0,0,32,255,255,104,0,0, +150,255,250,74,0,0,0,0,32,255,255,104,0,109,255,252, +84,0,0,0,0,0,32,255,255,104,70,252,255,187,0,0, +0,0,0,0,32,255,255,144,240,255,255,255,64,0,0,0, +0,0,32,255,255,255,255,171,255,255,211,4,0,0,0,0, +32,255,255,255,127,0,185,255,255,112,0,0,0,0,32,255, +255,152,0,0,44,252,255,240,23,0,0,0,32,255,255,104, +0,0,0,152,255,255,160,0,0,0,32,255,255,104,0,0, +0,22,241,255,254,57,0,0,32,255,255,104,0,0,0,0, +118,255,255,205,2,0,32,255,255,104,0,0,0,0,7,222, +255,255,104,0,32,255,255,104,0,0,0,0,0,85,255,255, +237,19,32,255,255,104,0,0,0,0,0,0,195,255,255,152, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,255,255,164,255,255,164,255,255,164,255,255,164, +255,255,164,255,255,164,255,255,164,255,255,164,255,255,164,255, +255,164,255,255,164,255,255,164,255,255,164,255,255,164,255,255, +164,255,255,164,255,255,164,255,255,164,255,255,164,255,255,164, +255,255,164,255,255,164,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,16,255,255,140,0,83,187,233, +245,216,137,21,0,11,120,203,241,245,212,125,7,0,0,16, +255,255,147,173,255,255,255,255,255,255,221,53,222,255,255,255, +255,255,255,196,4,0,16,255,255,251,255,177,47,7,43,199, +255,255,254,255,175,47,8,41,175,255,255,109,0,16,255,255, +255,164,1,0,0,0,42,255,255,255,170,0,0,0,0,6, +236,255,194,0,16,255,255,252,25,0,0,0,0,0,241,255, +255,42,0,0,0,0,0,189,255,239,0,16,255,255,204,0, +0,0,0,0,0,224,255,237,0,0,0,0,0,0,180,255, +254,4,16,255,255,166,0,0,0,0,0,0,220,255,214,0, +0,0,0,0,0,180,255,255,12,16,255,255,152,0,0,0, +0,0,0,220,255,208,0,0,0,0,0,0,180,255,255,12, +16,255,255,148,0,0,0,0,0,0,220,255,208,0,0,0, +0,0,0,180,255,255,12,16,255,255,148,0,0,0,0,0, +0,220,255,208,0,0,0,0,0,0,180,255,255,12,16,255, +255,148,0,0,0,0,0,0,220,255,208,0,0,0,0,0, +0,180,255,255,12,16,255,255,148,0,0,0,0,0,0,220, +255,208,0,0,0,0,0,0,180,255,255,12,16,255,255,148, +0,0,0,0,0,0,220,255,208,0,0,0,0,0,0,180, +255,255,12,16,255,255,148,0,0,0,0,0,0,220,255,208, +0,0,0,0,0,0,180,255,255,12,16,255,255,148,0,0, +0,0,0,0,220,255,208,0,0,0,0,0,0,180,255,255, +12,16,255,255,148,0,0,0,0,0,0,220,255,208,0,0, +0,0,0,0,180,255,255,12,16,255,255,148,0,0,0,0, +0,0,220,255,208,0,0,0,0,0,0,180,255,255,12,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,16,255,255,112, +0,79,176,231,249,231,177,65,0,0,16,255,255,123,178,255, +255,255,255,255,255,253,99,0,16,255,255,249,255,198,65,10, +13,96,246,255,245,25,16,255,255,255,190,8,0,0,0,0, +119,255,255,106,16,255,255,252,32,0,0,0,0,0,33,255, +255,153,16,255,255,196,0,0,0,0,0,0,2,253,255,175, +16,255,255,162,0,0,0,0,0,0,0,244,255,183,16,255, +255,151,0,0,0,0,0,0,0,244,255,184,16,255,255,148, +0,0,0,0,0,0,0,244,255,184,16,255,255,148,0,0, +0,0,0,0,0,244,255,184,16,255,255,148,0,0,0,0, +0,0,0,244,255,184,16,255,255,148,0,0,0,0,0,0, +0,244,255,184,16,255,255,148,0,0,0,0,0,0,0,244, +255,184,16,255,255,148,0,0,0,0,0,0,0,244,255,184, +16,255,255,148,0,0,0,0,0,0,0,244,255,184,16,255, +255,148,0,0,0,0,0,0,0,244,255,184,16,255,255,148, +0,0,0,0,0,0,0,244,255,184,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,10,115,198,236,248,224,159,54, +0,0,0,0,0,0,0,48,223,255,255,255,255,255,255,254, +145,3,0,0,0,0,37,236,255,233,94,21,11,53,171,255, +255,158,0,0,0,1,193,255,244,38,0,0,0,0,0,155, +255,255,82,0,0,65,255,255,130,0,0,0,0,0,0,15, +242,255,202,0,0,159,255,255,32,0,0,0,0,0,0,0, +175,255,255,26,0,218,255,220,0,0,0,0,0,0,0,0, +112,255,255,80,7,254,255,177,0,0,0,0,0,0,0,0, +90,255,255,105,24,255,255,156,0,0,0,0,0,0,0,0, +73,255,255,119,31,255,255,151,0,0,0,0,0,0,0,0, +89,255,255,109,18,255,255,164,0,0,0,0,0,0,0,0, +113,255,255,90,1,243,255,200,0,0,0,0,0,0,0,0, +149,255,255,46,0,190,255,249,12,0,0,0,0,0,0,0, +224,255,238,4,0,107,255,255,102,0,0,0,0,0,0,49, +255,255,152,0,0,14,232,255,230,22,0,0,0,0,2,184, +255,249,36,0,0,0,80,254,255,221,83,17,10,55,181,255, +255,122,0,0,0,0,0,96,247,255,255,255,255,255,255,253, +134,0,0,0,0,0,0,0,35,146,218,246,242,217,151,50, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,68,255,255,76,7,104,198,236,245,208,119,8, +0,0,0,68,255,255,102,209,255,255,255,255,255,255,213,27, +0,0,68,255,255,248,255,171,47,10,39,147,255,255,213,7, +0,68,255,255,255,172,1,0,0,0,0,132,255,255,126,0, +68,255,255,253,32,0,0,0,0,0,9,234,255,230,4,68, +255,255,192,0,0,0,0,0,0,0,151,255,255,51,68,255, +255,137,0,0,0,0,0,0,0,92,255,255,100,68,255,255, +97,0,0,0,0,0,0,0,62,255,255,123,68,255,255,82, +0,0,0,0,0,0,0,45,255,255,135,68,255,255,71,0, +0,0,0,0,0,0,49,255,255,123,68,255,255,84,0,0, +0,0,0,0,0,64,255,255,107,68,255,255,108,0,0,0, +0,0,0,0,100,255,255,74,68,255,255,166,0,0,0,0, +0,0,0,162,255,254,17,68,255,255,238,13,0,0,0,0, +0,17,241,255,198,0,68,255,255,255,141,0,0,0,0,0, +160,255,255,80,0,68,255,255,255,255,153,41,8,41,167,255, +255,175,1,0,68,255,255,145,236,255,255,255,255,255,255,188, +11,0,0,68,255,255,92,26,140,216,245,242,196,97,2,0, +0,0,68,255,255,92,0,0,0,0,0,0,0,0,0,0, +0,68,255,255,92,0,0,0,0,0,0,0,0,0,0,0, +68,255,255,92,0,0,0,0,0,0,0,0,0,0,0,68, +255,255,92,0,0,0,0,0,0,0,0,0,0,0,68,255, +255,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,34,150,223,247, +226,177,68,0,200,255,184,0,0,0,74,242,255,255,255,255, +255,255,140,200,255,184,0,0,47,245,255,229,81,11,25,102, +236,255,249,255,184,0,0,197,255,239,36,0,0,0,0,48, +249,255,255,184,0,65,255,255,117,0,0,0,0,0,0,146, +255,255,184,0,155,255,254,24,0,0,0,0,0,0,61,255, +255,184,0,214,255,220,0,0,0,0,0,0,0,5,249,255, +184,4,253,255,184,0,0,0,0,0,0,0,0,231,255,184, +19,255,255,170,0,0,0,0,0,0,0,0,213,255,184,28, +255,255,170,0,0,0,0,0,0,0,0,221,255,184,15,255, +255,186,0,0,0,0,0,0,0,0,236,255,184,1,247,255, +213,0,0,0,0,0,0,0,15,254,255,184,0,199,255,254, +18,0,0,0,0,0,0,71,255,255,184,0,127,255,255,101, +0,0,0,0,0,0,163,255,255,184,0,27,247,255,224,19, +0,0,0,0,58,251,255,255,184,0,0,108,255,255,209,69, +13,25,104,238,255,255,255,184,0,0,0,120,253,255,255,255, +255,255,255,145,236,255,184,0,0,0,0,51,168,232,249,230, +177,73,0,236,255,184,0,0,0,0,0,0,0,0,0,0, +0,0,236,255,184,0,0,0,0,0,0,0,0,0,0,0, +0,236,255,184,0,0,0,0,0,0,0,0,0,0,0,0, +236,255,184,0,0,0,0,0,0,0,0,0,0,0,0,236, +255,184,0,0,0,0,0,0,0,0,0,0,0,0,236,255, +184,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,255,132,0, +54,190,247,164,255,255,132,68,248,255,255,164,255,255,160,241, +255,255,255,164,255,255,252,255,165,48,7,0,255,255,255,154, +0,0,0,0,255,255,245,17,0,0,0,0,255,255,188,0, +0,0,0,0,255,255,165,0,0,0,0,0,255,255,164,0, +0,0,0,0,255,255,164,0,0,0,0,0,255,255,164,0, +0,0,0,0,255,255,164,0,0,0,0,0,255,255,164,0, +0,0,0,0,255,255,164,0,0,0,0,0,255,255,164,0, +0,0,0,0,255,255,164,0,0,0,0,0,255,255,164,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11, +115,198,240,250,231,184,97,2,0,0,0,0,29,215,255,255, +255,255,255,255,255,196,13,0,0,1,203,255,234,89,21,7, +42,142,255,255,172,0,0,71,255,255,86,0,0,0,0,0, +147,255,255,36,0,132,255,255,37,0,0,0,0,0,50,255, +255,90,0,145,255,255,100,0,0,0,0,0,0,0,0,0, +0,105,255,255,245,96,6,0,0,0,0,0,0,0,0,14, +225,255,255,255,241,173,103,32,0,0,0,0,0,0,33,200, +255,255,255,255,255,255,207,108,7,0,0,0,0,0,63,150, +222,255,255,255,255,255,204,14,0,0,0,0,0,0,0,37, +108,191,254,255,255,134,0,0,0,0,0,0,0,0,0,0, +77,252,255,209,0,0,0,0,0,0,0,0,0,0,0,201, +255,229,1,246,255,153,0,0,0,0,0,0,0,192,255,206, +0,188,255,233,15,0,0,0,0,0,36,249,255,132,0,71, +255,255,210,82,22,5,30,106,230,255,233,20,0,0,122,254, +255,255,255,255,255,255,255,233,54,0,0,0,0,56,157,214, +242,252,239,200,127,20,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,136,255,255,32,0,0,0,0,136,255, +255,32,0,0,0,0,136,255,255,32,0,0,0,0,136,255, +255,32,0,0,0,0,136,255,255,32,0,0,168,255,255,255, +255,255,255,160,168,255,255,255,255,255,255,160,0,0,136,255, +255,32,0,0,0,0,136,255,255,32,0,0,0,0,136,255, +255,32,0,0,0,0,136,255,255,32,0,0,0,0,136,255, +255,32,0,0,0,0,136,255,255,32,0,0,0,0,136,255, +255,32,0,0,0,0,136,255,255,32,0,0,0,0,136,255, +255,32,0,0,0,0,136,255,255,32,0,0,0,0,136,255, +255,32,0,0,0,0,129,255,255,36,0,0,0,0,100,255, +255,121,5,0,0,0,24,250,255,255,255,160,0,0,0,88, +218,252,255,160,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,32,255,255,148,0,0,0,0,0,0,36,255, +255,128,32,255,255,148,0,0,0,0,0,0,36,255,255,128, +32,255,255,148,0,0,0,0,0,0,36,255,255,128,32,255, +255,148,0,0,0,0,0,0,36,255,255,128,32,255,255,148, +0,0,0,0,0,0,36,255,255,128,32,255,255,148,0,0, +0,0,0,0,36,255,255,128,32,255,255,148,0,0,0,0, +0,0,36,255,255,128,32,255,255,148,0,0,0,0,0,0, +36,255,255,128,32,255,255,148,0,0,0,0,0,0,36,255, +255,128,32,255,255,148,0,0,0,0,0,0,40,255,255,128, +32,255,255,148,0,0,0,0,0,0,52,255,255,128,31,255, +255,148,0,0,0,0,0,0,80,255,255,128,20,255,255,155, +0,0,0,0,0,0,130,255,255,128,2,252,255,185,0,0, +0,0,0,2,218,255,255,128,0,201,255,245,22,0,0,0, +0,108,255,255,255,128,0,108,255,255,198,49,7,34,132,252, +239,251,255,128,0,3,186,255,255,255,255,255,255,240,65,249, +255,128,0,0,4,112,204,241,249,218,144,27,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,173,255,255,94, +0,0,0,0,0,0,0,93,255,255,127,85,255,255,171,0, +0,0,0,0,0,0,174,255,255,38,10,244,255,242,6,0, +0,0,0,0,9,245,255,205,0,0,167,255,255,70,0,0, +0,0,0,80,255,255,116,0,0,79,255,255,148,0,0,0, +0,0,161,255,254,29,0,0,7,240,255,225,0,0,0,0, +4,237,255,194,0,0,0,0,161,255,255,47,0,0,0,68, +255,255,105,0,0,0,0,73,255,255,124,0,0,0,149,255, +251,21,0,0,0,0,5,237,255,201,0,0,1,228,255,183, +0,0,0,0,0,0,155,255,254,24,0,55,255,255,94,0, +0,0,0,0,0,68,255,255,100,0,137,255,247,14,0,0, +0,0,0,0,3,233,255,178,0,218,255,172,0,0,0,0, +0,0,0,0,149,255,246,52,255,255,83,0,0,0,0,0, +0,0,0,62,255,255,199,255,242,8,0,0,0,0,0,0, +0,0,1,228,255,255,255,161,0,0,0,0,0,0,0,0, +0,0,143,255,255,255,72,0,0,0,0,0,0,0,0,0, +0,56,255,255,235,4,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +154,255,255,68,0,0,0,0,0,226,255,255,77,0,0,0, +0,0,167,255,243,5,86,255,255,126,0,0,0,0,28,255, +255,255,136,0,0,0,0,0,228,255,180,0,19,254,255,183, +0,0,0,0,87,255,255,255,195,0,0,0,0,34,255,255, +111,0,0,206,255,239,1,0,0,0,145,255,241,255,248,6, +0,0,0,96,255,255,43,0,0,137,255,255,43,0,0,0, +204,255,136,255,255,57,0,0,0,157,255,230,0,0,0,69, +255,255,100,0,0,10,251,255,42,230,255,117,0,0,0,219, +255,161,0,0,0,8,247,255,158,0,0,65,255,238,1,171, +255,176,0,0,25,255,255,93,0,0,0,0,189,255,216,0, +0,123,255,180,0,111,255,235,0,0,86,255,255,25,0,0, +0,0,120,255,254,18,0,182,255,122,0,50,255,255,38,0, +148,255,211,0,0,0,0,0,52,255,255,75,1,238,255,63, +0,3,242,255,97,0,209,255,142,0,0,0,0,0,2,237, +255,133,43,255,250,10,0,0,185,255,157,16,253,255,74,0, +0,0,0,0,0,172,255,190,102,255,202,0,0,0,125,255, +216,77,255,249,11,0,0,0,0,0,0,103,255,244,164,255, +143,0,0,0,64,255,255,158,255,192,0,0,0,0,0,0, +0,35,255,255,247,255,84,0,0,0,9,250,255,250,255,124, +0,0,0,0,0,0,0,0,222,255,255,255,26,0,0,0, +0,199,255,255,255,55,0,0,0,0,0,0,0,0,155,255, +255,223,0,0,0,0,0,139,255,255,239,3,0,0,0,0, +0,0,0,0,86,255,255,165,0,0,0,0,0,78,255,255, +174,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,64,255,255,251,44,0,0,0, +0,0,52,250,247,224,19,0,157,255,255,193,0,0,0,0, +2,204,255,255,101,0,0,17,232,255,255,92,0,0,0,107, +255,255,191,1,0,0,0,88,255,255,231,14,0,23,240,255, +246,36,0,0,0,0,0,181,255,255,143,0,164,255,255,118, +0,0,0,0,0,0,31,244,255,251,110,255,255,206,4,0, +0,0,0,0,0,0,113,255,255,255,255,251,49,0,0,0, +0,0,0,0,0,3,203,255,255,255,136,0,0,0,0,0, +0,0,0,0,0,128,255,255,255,56,0,0,0,0,0,0, +0,0,0,32,245,255,255,255,202,2,0,0,0,0,0,0, +0,0,184,255,255,234,255,255,111,0,0,0,0,0,0,0, +91,255,255,194,51,253,255,243,29,0,0,0,0,0,18,234, +255,252,46,0,147,255,255,178,0,0,0,0,0,160,255,255, +147,0,0,15,231,255,255,85,0,0,0,66,255,255,234,16, +0,0,0,91,255,255,230,15,0,8,219,255,255,99,0,0, +0,0,0,190,255,255,153,0,135,255,255,202,1,0,0,0, +0,0,40,249,255,254,60,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,136,255,255,114, +0,0,0,0,0,0,0,108,255,255,123,54,255,255,192,0, +0,0,0,0,0,0,186,255,255,38,1,228,255,252,18,0, +0,0,0,0,14,249,255,208,0,0,148,255,255,91,0,0, +0,0,0,86,255,255,123,0,0,67,255,255,169,0,0,0, +0,0,165,255,255,38,0,0,4,237,255,241,5,0,0,0, +4,238,255,209,0,0,0,0,160,255,255,69,0,0,0,65, +255,255,125,0,0,0,0,79,255,255,146,0,0,0,144,255, +255,41,0,0,0,0,9,245,255,223,0,0,0,221,255,212, +0,0,0,0,0,0,173,255,255,46,0,44,255,255,127,0, +0,0,0,0,0,92,255,255,123,0,123,255,255,42,0,0, +0,0,0,0,16,250,255,201,0,201,255,212,0,0,0,0, +0,0,0,0,185,255,254,49,254,255,125,0,0,0,0,0, +0,0,0,104,255,255,200,255,255,38,0,0,0,0,0,0, +0,0,25,254,255,255,255,204,0,0,0,0,0,0,0,0, +0,0,198,255,255,255,105,0,0,0,0,0,0,0,0,0, +0,117,255,255,244,14,0,0,0,0,0,0,0,0,0,0, +36,255,255,152,0,0,0,0,0,0,0,0,0,0,0,18, +248,255,44,0,0,0,0,0,0,0,0,0,0,0,143,255, +185,0,0,0,0,0,0,0,0,16,25,5,83,250,255,62, +0,0,0,0,0,0,0,0,68,255,255,255,255,171,0,0, +0,0,0,0,0,0,0,58,245,251,224,139,9,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,140,255,255,255,255,255,255,255,255,255,255,255,156,0,140, +255,255,255,255,255,255,255,255,255,255,255,153,0,0,0,0, +0,0,0,0,0,74,255,255,247,43,0,0,0,0,0,0, +0,0,21,232,255,255,111,0,0,0,0,0,0,0,0,0, +177,255,255,186,1,0,0,0,0,0,0,0,0,102,255,255, +237,26,0,0,0,0,0,0,0,0,36,244,255,255,83,0, +0,0,0,0,0,0,0,4,201,255,255,160,0,0,0,0, +0,0,0,0,0,129,255,255,222,13,0,0,0,0,0,0, +0,0,56,252,255,252,59,0,0,0,0,0,0,0,0,12, +220,255,255,132,0,0,0,0,0,0,0,0,0,157,255,255, +204,5,0,0,0,0,0,0,0,0,80,255,255,245,39,0, +0,0,0,0,0,0,0,24,235,255,255,105,0,0,0,0, +0,0,0,0,0,183,255,255,181,0,0,0,0,0,0,0, +0,0,55,255,255,255,255,255,255,255,255,255,255,255,255,228, +60,255,255,255,255,255,255,255,255,255,255,255,255,228,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,114, +184,226,93,0,0,0,0,0,57,237,255,255,255,96,0,0, +0,0,7,226,255,254,145,42,3,0,0,0,0,83,255,255, +123,0,0,0,0,0,0,0,138,255,248,12,0,0,0,0, +0,0,0,159,255,209,0,0,0,0,0,0,0,0,164,255, +191,0,0,0,0,0,0,0,0,164,255,188,0,0,0,0, +0,0,0,0,178,255,180,0,0,0,0,0,0,0,0,221, +255,161,0,0,0,0,0,0,0,94,255,255,107,0,0,0, +0,8,56,151,253,255,237,21,0,0,0,0,160,255,255,255, +226,58,0,0,0,0,0,160,255,237,142,7,0,0,0,0, +0,0,160,255,255,255,213,37,0,0,0,0,0,9,60,165, +255,255,217,7,0,0,0,0,0,0,0,123,255,255,83,0, +0,0,0,0,0,0,5,236,255,147,0,0,0,0,0,0, +0,0,185,255,171,0,0,0,0,0,0,0,0,166,255,186, +0,0,0,0,0,0,0,0,164,255,188,0,0,0,0,0, +0,0,0,164,255,189,0,0,0,0,0,0,0,0,160,255, +205,0,0,0,0,0,0,0,0,139,255,248,13,0,0,0, +0,0,0,0,79,255,255,145,0,0,0,0,0,0,0,3, +209,255,255,184,61,4,0,0,0,0,0,25,185,255,255,255, +96,0,0,0,0,0,0,0,61,154,208,92,228,255,156,228, +255,156,228,255,156,228,255,156,228,255,156,228,255,156,228,255, +156,228,255,156,228,255,156,228,255,156,228,255,156,228,255,156, +228,255,156,228,255,156,228,255,156,228,255,156,228,255,156,228, +255,156,228,255,156,228,255,156,228,255,156,228,255,156,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +96,220,170,85,2,0,0,0,0,0,0,100,255,255,255,201, +22,0,0,0,0,0,3,39,131,250,255,194,0,0,0,0, +0,0,0,0,96,255,255,54,0,0,0,0,0,0,0,4, +238,255,118,0,0,0,0,0,0,0,0,204,255,145,0,0, +0,0,0,0,0,0,188,255,159,0,0,0,0,0,0,0, +0,187,255,160,0,0,0,0,0,0,0,0,173,255,169,0, +0,0,0,0,0,0,0,145,255,210,0,0,0,0,0,0, +0,0,78,255,255,78,0,0,0,0,0,0,0,4,213,255, +250,141,50,7,0,0,0,0,0,33,210,255,255,255,160,0, +0,0,0,0,0,2,129,236,255,160,0,0,0,0,0,19, +196,255,255,255,160,0,0,0,0,0,188,255,255,158,57,9, +0,0,0,0,56,255,255,111,0,0,0,0,0,0,0,127, +255,230,2,0,0,0,0,0,0,0,165,255,178,0,0,0, +0,0,0,0,0,183,255,161,0,0,0,0,0,0,0,0, +188,255,160,0,0,0,0,0,0,0,0,188,255,160,0,0, +0,0,0,0,0,0,201,255,152,0,0,0,0,0,0,0, +11,245,255,125,0,0,0,0,0,0,0,143,255,255,55,0, +0,0,0,5,63,181,255,255,173,0,0,0,0,0,100,255, +255,255,161,8,0,0,0,0,0,96,216,151,51,0,0,0, +0,0,0,0,0,0,43,187,245,235,167,76,1,0,0,0, +0,0,0,42,255,147,0,35,237,255,255,255,255,255,208,85, +2,0,0,0,0,155,255,83,0,172,255,255,255,255,255,255, +255,255,215,102,24,18,124,254,241,14,30,253,251,108,17,33, +110,217,255,255,255,255,255,255,255,255,139,0,104,255,142,0, +0,0,0,2,87,207,255,255,255,255,255,229,20,0,161,255, +40,0,0,0,0,0,0,0,70,175,239,242,177,34,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0, +}; +#endif diff --git a/installer/icfg.c b/installer/icfg.c index b4314ef..6240103 100644 --- a/installer/icfg.c +++ b/installer/icfg.c @@ -194,3 +194,54 @@ int icfg_get_int(const char *path, const char *key, long *out){ jl_free(r); return -1; } + +/* Daemon liveness from status.json (the daemon's alive tick writes + * {"version","state","title","ts"}). now = time(NULL) at the caller, + * max_age_s = freshness bound (120 covers the 60s tick plus slack). + * Returns 1 fresh (state_out gets "state", "" when absent), + * 0 missing/corrupt/stale/unreadable. Never crashes on garbage. */ +int icfg_daemon_state(const char *path, long now, long max_age_s, + char *state_out, size_t state_cap){ + FILE *f; + long sz; + char buf[1024]; + size_t n; + const char *p, *q; + long ts; + if(state_out && state_cap) state_out[0] = 0; + if(!path || max_age_s < 0) return 0; + f = fopen(path, "rb"); + if(!f) return 0; + if(fseek(f, 0, SEEK_END) != 0){ fclose(f); return 0; } + sz = ftell(f); + if(sz <= 0 || sz >= (long)sizeof buf){ fclose(f); return 0; } + if(fseek(f, 0, SEEK_SET) != 0){ fclose(f); return 0; } + n = fread(buf, 1, (size_t)sz, f); + fclose(f); + if(n != (size_t)sz) return 0; + buf[n] = 0; + p = strstr(buf, "\"ts\""); + if(!p) return 0; + p = strchr(p + 4, ':'); + if(!p) return 0; + ts = strtol(p + 1, NULL, 10); + if(ts <= 0 || ts > now || now - ts > max_age_s) return 0; + p = strstr(buf, "\"state\""); + if(p){ + p = strchr(p + 7, ':'); + if(p){ + p = strchr(p + 1, '"'); + if(p){ + p++; + q = strchr(p, '"'); + if(q && state_out && state_cap){ + size_t len = (size_t)(q - p); + if(len >= state_cap) len = state_cap - 1; + memcpy(state_out, p, len); + state_out[len] = 0; + } + } + } + } + return 1; +} diff --git a/installer/icfg.h b/installer/icfg.h index 3c8a12d..cc84a40 100644 --- a/installer/icfg.h +++ b/installer/icfg.h @@ -23,5 +23,9 @@ int icfg_get_str(const char *path, const char *key, char *out, size_t cap); int icfg_get_int(const char *path, const char *key, long *out); /* Count entries in the "titles" map (learned + manual). Fail-soft 0. */ int icfg_titles_count(const char *path); +/* Daemon liveness from status.json: 1 fresh (state_out gets "state"), + * 0 missing/corrupt/stale. now = time(NULL), max_age_s ~ 120. */ +int icfg_daemon_state(const char *path, long now, long max_age_s, + char *state_out, size_t state_cap); #endif diff --git a/installer/installer.c b/installer/installer.c index 25aae3c..0decbd4 100644 --- a/installer/installer.c +++ b/installer/installer.c @@ -24,6 +24,7 @@ #define INST_DIR "/data/orbisRPC" #define INST_LOG "/data/orbisRPC/install.log" #define PAYLOAD_BIN "/data/payloads/orbisrpc.bin" +#define STATUS_PATH "/data/orbisRPC/status.json" /* Stage log: every copy step records errno + sizes to a file we can read * back over FTP. The dialog alone can't say WHICH stage failed. */ @@ -222,17 +223,19 @@ static int step_files(void){ /* Copy daemon payload. */ ui_progress_msg("Copying payload"); ok = copy_file(DAEMON_ELF, PAYLOAD_BIN); - ilog("daemon-copy", ok, 0, 0); + ilogv("daemon-copy", ok, 0); ui_progress_set(60); - /* Pre-save config from PKG asset so step_token() skips on fresh install. - * Copy config.json from /app0/assets/config.json to /data/orbisRPC/config.json. */ + /* Pre-save config from PKG asset on FRESH installs only. A reinstall + * must never clobber the existing config: it holds the token and + * every learned title. step_token() below handles token entry. */ ui_progress_msg("Saving config"); { - FILE *src = fopen("/app0/assets/config.json", "rb"); - if(src){ - fclose(src); + if(exists(ICFG_PATH)){ + ilogv("cfg-keep", 0, 0); + } else if(exists("/app0/assets/config.json")){ int rc = copy_file("/app0/assets/config.json", ICFG_PATH); - ilog("cfg-copy", rc == 0 ? 0 : (errno ? errno : -1), 0, 0); + ilogv("cfg-copy", rc, 0); + if(rc == 0) chmod(ICFG_PATH, 0600); } else { ilog("cfg-noasset", errno, 0, 0); /* Fallback: write template with pre-set token. */ @@ -291,7 +294,7 @@ static int step_files(void){ if(gf){ fprintf(gf, "%ld\n", cur + 1); fclose(gf); - ilog("gen-bump", (int)(cur + 1), 0, 0); + ilogv("gen-bump", cur + 1, 0); } else { ilog("gen-bump", errno ? errno : -1, 0, 0); } @@ -328,8 +331,13 @@ static void step_token(void){ } if(r == 0) return; if(token_valid(tok)){ - r = icfg_token_save(ICFG_PATH, tok); - ui_ok("Config saved."); + if(icfg_token_save(ICFG_PATH, tok) == 0){ + ilogv("token-saved", 0, 0); + ui_ok("Config saved."); + } else { + ilog("token-save", errno, 0, 0); + ui_ok("Could not save the token.\n\nPaste it into /data/orbisRPC/config.json over FTP instead."); + } return; } ui_ok("That doesn't look like a token.\nCheck it and try again, or cancel to skip."); @@ -346,6 +354,22 @@ static void finish(int code){ _exit(code); } +/* Final screen reports what is actually true: daemon liveness comes + * from status.json freshness, not from the copy steps succeeding. */ +static void step_done(void){ + char st[32] = ""; + char msg[256]; + if(icfg_daemon_state(STATUS_PATH, (long)time(NULL), 120, st, sizeof st)){ + snprintf(msg, sizeof msg, + "Done.\n\nDaemon is alive (%s).\nLaunch orbisrpc from the payload launcher after a reboot.", st); + } else { + snprintf(msg, sizeof msg, + "Done.\n\nNo live daemon seen — normal on first install.\nLaunch orbisrpc from the payload launcher."); + } + ilogv("done-shown", 0, 0); + ui_ok(msg); +} + int main(void){ /* Hide the PS4 splash first — dialogs opened while the splash * is visible get auto-dismissed (half-second flash) or never @@ -353,15 +377,13 @@ int main(void){ * run with the splash already gone, so no visible lag. */ sceSystemServiceHideSplashScreen(); crash_guard(); - if(ui_init() != 0) finish(1); + if(ui_init() != 0){ ilogv("ui-init-fail", 0, 0); finish(1); } ui_ok("orbisRPC"); if(step_assets() != 0) finish(1); ilogv("assets-ok", 0, 0); if(step_files() != 0) finish(1); step_token(); ilogv("post-token", 0, 0); - ui_ok("Done.\n\n" - "Payloads in /data/payloads.\n" - "Launch orbisrpc from the payload launcher."); + step_done(); finish(0); } diff --git a/installer/ui.c b/installer/ui.c index c43a35b..abf784f 100644 --- a/installer/ui.c +++ b/installer/ui.c @@ -1,12 +1,15 @@ -/* ui.c - native PS4 dialogs: MsgDialog (ok / yes-no / progress) + ImeDialog. - * Everything here needs the console (dialog system calls); pure logic - * (validation, config merge) lives in icfg.c and is host-tested. */ +/* ui.c - custom SDL2 installer UI: branded fullscreen cards instead of + * stock Sony MsgDialogs. Same ui.h API, so installer.c is untouched. + * Token entry still uses the system IME (proven overlay); everything + * else is rendered by us: Discord-blurple card, progress bar, X-to-continue. + * Needs the console (SDL/Pad/IME system calls); pure logic lives in icfg.c. */ #include "ui.h" +#include "font.h" #include #include #include +#include #include -#include #include #include #include @@ -16,139 +19,325 @@ #include #include +#define SCR_W 1920 +#define SCR_H 1080 + +/* Palette */ +#define C_BG_R 30 /* near-black warm gray */ +#define C_BG_G 31 +#define C_BG_B 34 +#define C_CARD_R 88 /* Discord blurple */ +#define C_CARD_G 101 +#define C_CARD_B 242 +#define C_INK_R 255 /* white text */ +#define C_INK_G 255 +#define C_INK_B 255 +#define C_DIM_R 200 +#define C_DIM_G 203 +#define C_DIM_B 220 +#define C_BAR_BG_R 40 +#define C_BAR_BG_G 43 +#define C_BAR_BG_B 55 +#define C_BAR_FG_R 255 +#define C_BAR_FG_G 255 +#define C_BAR_FG_B 255 + +static SDL_Window *g_win = NULL; +static SDL_Surface *g_surf = NULL; +static int ui_ready = 0; +static int ime_dialog_running = 0; +static int g_pad[4] = { -1, -1, -1, -1 }; +static uint32_t g_prevbtn[4] = { 0, 0, 0, 0 }; + static void ime_dbg(const char *msg){ int fd = open("/data/ime_debug.log", O_WRONLY|O_CREAT|O_APPEND, 0666); if(fd >= 0){ write(fd, msg, strlen(msg)); close(fd); } } -static void base_init(OrbisMsgDialogParam *param){ - memset(param, 0, sizeof(*param)); - param->baseParam.size = (uint32_t)sizeof(param->baseParam); - param->baseParam.magic = - (uint32_t)(ORBIS_COMMON_DIALOG_MAGIC_NUMBER + (uint64_t)¶m->baseParam); - param->size = sizeof(OrbisMsgDialogParam); - param->mode = ORBIS_MSG_DIALOG_MODE_USER_MSG; +/* --- pixel helpers (format-safe via SDL_GetRGB/MapRGB) ---------------- */ +static void blend_px(int x, int y, int r, int g, int b, unsigned a){ + Uint8 dr, dg, db; + Uint32 *p; + if(!g_surf || x < 0 || y < 0 || x >= g_surf->w || y >= g_surf->h || a == 0) return; + p = (Uint32 *)((Uint8 *)g_surf->pixels + (size_t)y * (size_t)g_surf->pitch + (size_t)x * 4); + SDL_GetRGB(*p, g_surf->format, &dr, &dg, &db); + dr = (Uint8)((dr * (255 - a) + (unsigned)r * a) / 255); + dg = (Uint8)((dg * (255 - a) + (unsigned)g * a) / 255); + db = (Uint8)((db * (255 - a) + (unsigned)b * a) / 255); + *p = SDL_MapRGB(g_surf->format, dr, dg, db); } -static int ui_ready = 0; -static int ime_dialog_running = 0; +static void fill_rect(int x, int y, int w, int h, int r, int g, int b){ + SDL_Rect rc; + if(!g_surf) return; + rc.x = x; rc.y = y; rc.w = w; rc.h = h; + SDL_FillRect(g_surf, &rc, SDL_MapRGB(g_surf->format, + (Uint8)r, (Uint8)g, (Uint8)b)); +} -/* Reap any stale dialog from a previous session that died - * mid-flow. Called by ui_init() to ensure a clean start. */ -static void reap_stale(void); +static unsigned text_w(const char *s){ + unsigned w = 0; + for(; *s; s++){ + unsigned c = (unsigned char)*s; + if(c < FONT_FIRST || c >= FONT_FIRST + FONT_COUNT) c = (unsigned)'?'; + w += font_adv[c - FONT_FIRST] + 2; + } + return w; +} + +static void draw_text(int x, int y, const char *s, int r, int g, int b){ + if(!g_surf || !s) return; + if(SDL_MUSTLOCK(g_surf)) SDL_LockSurface(g_surf); + for(; *s; s++){ + unsigned c = (unsigned char)*s; + unsigned gi, gw, gx, gy; + if(c == '\n'){ continue; } /* caller splits lines */ + if(c < FONT_FIRST || c >= FONT_FIRST + FONT_COUNT) c = (unsigned)'?'; + gi = c - FONT_FIRST; + gw = font_adv[gi]; + for(gy = 0; gy < FONT_H; gy++) + for(gx = 0; gx < gw; gx++) + blend_px(x + (int)gx, y + (int)gy, r, g, b, + font_px[font_off[gi] + gy * gw + gx]); + x += (int)gw + 2; + } + if(SDL_MUSTLOCK(g_surf)) SDL_UnlockSurface(g_surf); +} + +static void present(void){ + if(g_win) SDL_UpdateWindowSurface(g_win); +} + +/* --- card layout ------------------------------------------------------ */ +#define CARD_X 260 +#define CARD_Y 240 +#define CARD_W (SCR_W - 2 * CARD_X) +#define CARD_H 600 +#define PAD_X 64 +#define TITLE_Y (CARD_Y + 48) +#define BODY_Y (CARD_Y + 150) +#define LINE_H (FONT_H + 12) +#define MAX_LINES 12 + +/* Greedy word wrap into fixed rows. Unknown glyphs count as '?'. */ +static int wrap_lines(const char *msg, char lines[MAX_LINES][128]){ + int n = 0; + char cur[128]; + size_t cl = 0; + unsigned px = 0; + int have_space = 0; /* a wrap point exists inside cur */ + size_t space_at = 0; + unsigned px_after_space = 0; + cur[0] = 0; + while(*msg && n < MAX_LINES){ + unsigned c, a; + if(*msg == '\n'){ + strncpy(lines[n], cur, 127); lines[n][127] = 0; n++; + cur[0] = 0; cl = 0; px = 0; + have_space = 0; space_at = 0; px_after_space = 0; + msg++; + continue; + } + c = (unsigned char)*msg; + a = (c < FONT_FIRST || c >= FONT_FIRST + FONT_COUNT) + ? font_adv['?' - FONT_FIRST] : font_adv[c - FONT_FIRST]; + if(px + a + 2 > (unsigned)(CARD_W - 2 * PAD_X) && cl > 0){ + if(have_space){ + /* break after the last space */ + size_t tail = cl - (space_at + 1); + memcpy(lines[n], cur, space_at); + lines[n][space_at] = 0; n++; + if(n >= MAX_LINES) break; + memmove(cur, cur + space_at + 1, tail + 1); + cl = tail; px = px - px_after_space; + have_space = 0; space_at = 0; px_after_space = 0; + continue; /* re-process this char */ + } + strncpy(lines[n], cur, 127); lines[n][127] = 0; n++; + if(n >= MAX_LINES) break; + cur[0] = 0; cl = 0; px = 0; + have_space = 0; space_at = 0; px_after_space = 0; + continue; /* re-process this char */ + } + if(cl < sizeof cur - 1){ cur[cl++] = *msg; cur[cl] = 0; px += a + 2; } + if(c == ' '){ have_space = 1; space_at = cl - 1; px_after_space = px; } + msg++; + } + if(cl > 0 && n < MAX_LINES){ strncpy(lines[n], cur, 127); lines[n][127] = 0; n++; } + return n; +} + +static void render_card(const char *title, const char *msg, int pct, int show_bar, + const char *footer){ + char lines[MAX_LINES][128]; + int n, i; + fill_rect(0, 0, SCR_W, SCR_H, C_BG_R, C_BG_G, C_BG_B); + fill_rect(CARD_X, CARD_Y, CARD_W, CARD_H, C_CARD_R, C_CARD_G, C_CARD_B); + draw_text(CARD_X + PAD_X, TITLE_Y, title, C_INK_R, C_INK_G, C_INK_B); + fill_rect(CARD_X + PAD_X, TITLE_Y + FONT_H + 16, CARD_W - 2 * PAD_X, 3, + C_DIM_R, C_DIM_G, C_DIM_B); + n = wrap_lines(msg ? msg : "", lines); + for(i = 0; i < n; i++) + draw_text(CARD_X + PAD_X, BODY_Y + i * LINE_H, lines[i], + C_INK_R, C_INK_G, C_INK_B); + if(show_bar){ + int bx = CARD_X + PAD_X, bw = CARD_W - 2 * PAD_X, by = CARD_Y + CARD_H - 170; + if(pct < 0) pct = 0; + if(pct > 100) pct = 100; + fill_rect(bx, by, bw, 26, C_BAR_BG_R, C_BAR_BG_G, C_BAR_BG_B); + fill_rect(bx, by, (bw * pct) / 100, 26, C_BAR_FG_R, C_BAR_FG_G, C_BAR_FG_B); + { + char pb[16]; + snprintf(pb, sizeof pb, "%d%%", pct); + draw_text(bx + bw / 2 - (int)text_w(pb) / 2, by + 2, pb, + C_CARD_R, C_CARD_G, C_CARD_B); + } + } + if(footer) + draw_text(CARD_X + PAD_X, CARD_Y + CARD_H - 64, footer, + C_DIM_R, C_DIM_G, C_DIM_B); + present(); +} + +/* --- pad: Cross-to-continue ------------------------------------------- */ +static void pad_open_all(void){ + int i; + for(i = 0; i < 4; i++){ + if(g_pad[i] < 0){ + int h = scePadOpen(i + 1, 0, 0, NULL); + if(h >= 0){ g_pad[i] = h; g_prevbtn[i] = 0; } + } + } +} + +/* 1 when Cross is newly pressed on any handle. */ +static int pad_cross_pressed(void){ + int i; + OrbisPadData d; + for(i = 0; i < 4; i++){ + uint32_t now; + if(g_pad[i] < 0) continue; + memset(&d, 0, sizeof d); + if(scePadReadState(g_pad[i], &d) < 0) continue; + now = d.buttons; + if((now & ORBIS_PAD_BUTTON_CROSS) && !(g_prevbtn[i] & ORBIS_PAD_BUTTON_CROSS)){ + g_prevbtn[i] = now; + return 1; + } + g_prevbtn[i] = now; + } + return 0; +} + +static void wait_cross(void){ + /* consume a held Cross first so the press that opened this + * screen does not instantly dismiss it */ + int i; + for(i = 0; i < 40; i++){ + pad_cross_pressed(); + sceKernelUsleep(20000); + } + for(;;){ + if(pad_cross_pressed()) break; + sceKernelUsleep(20000); + } + for(i = 0; i < 25; i++){ /* let go before the next screen arms */ + OrbisPadData d; + int j, held = 0; + for(j = 0; j < 4; j++){ + if(g_pad[j] < 0) continue; + memset(&d, 0, sizeof d); + if(scePadReadState(g_pad[j], &d) == 0 && (d.buttons & ORBIS_PAD_BUTTON_CROSS)) + held = 1; + } + if(!held) break; + sceKernelUsleep(20000); + } +} + +/* --- public API -------------------------------------------------------- */ +static void reap_stale(void){ + if(sceImeDialogGetStatus() == ORBIS_DIALOG_STATUS_RUNNING) + sceImeDialogTerm(); +} int ui_init(void){ if(ui_ready) return 0; - /* Reap any stale dialog from a previous session that died - * mid-flow. This ensures a clean start. */ reap_stale(); { - /* UserService first: dialogs + pad + IME all key off the user. - * Best-effort (already-initialized is fine); uid fallbacks - * downstream keep every path forward-safe. */ OrbisUserServiceInitializeParams up; memset(&up, 0, sizeof up); up.priority = ORBIS_KERNEL_PRIO_FIFO_LOWEST; (void)sceUserServiceInitialize(&up); } - /* Mirrors apollo-ps4 initInternal()/initPad(): - * 1. internal modules (SYSTEM, USER, COMMON_DIALOG) - * 2. sceCommonDialogInitialize() <- BEFORE external modules - * 3. PAD module + scePadInit() - * 4. external modules (MESSAGE_DIALOG, IME_DIALOG, IME_BACKEND) - * Order matters: dialog init must precede external module loads, - * and COMMON_DIALOG must precede sceCommonDialogInitialize(). */ + /* Same proven order as before (apollo pattern): internal modules, + * CommonDialog init BEFORE external loads, PAD, then IME modules. + * MESSAGE_DIALOG is gone (we render our own cards). */ if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE) != 0) return -1; if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE) != 0) return -1; if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_COMMON_DIALOG) != 0) return -1; if(sceCommonDialogInitialize() < 0) return -1; if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_PAD) != 0) return -1; (void)scePadInit(); - if(sceSysmoduleLoadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG) < 0) return -1; - if(sceSysmoduleLoadModule(ORBIS_SYSMODULE_IME_DIALOG) < 0){ - sceSysmoduleUnloadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG); - return -1; - } + if(sceSysmoduleLoadModule(ORBIS_SYSMODULE_IME_DIALOG) < 0) return -1; if(sceSysmoduleLoadModule(ORBIS_SYSMODULE_IME_BACKEND) < 0){ sceSysmoduleUnloadModule(ORBIS_SYSMODULE_IME_DIALOG); - sceSysmoduleUnloadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG); return -1; } + if(SDL_Init(SDL_INIT_VIDEO) < 0) return -1; + g_win = SDL_CreateWindow("orbisRPC Setup", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, SCR_W, SCR_H, SDL_WINDOW_SHOWN); + if(!g_win){ SDL_Quit(); return -1; } + g_surf = SDL_GetWindowSurface(g_win); + if(!g_surf || g_surf->format->BytesPerPixel != 4){ ui_shutdown(); return -1; } + pad_open_all(); ui_ready = 1; return 0; } -/* If a previous session died between open and close, a dialog - * can be left in RUNNING state, causing a fresh open() to fail. - * This function is called by ui_init() to reclaim any stale - * dialog before starting fresh. Currently unused (no stale - * dialogs observed after ui_init), kept for safety. */ -static void reap_stale(void){ - if(sceMsgDialogGetStatus() == ORBIS_COMMON_DIALOG_STATUS_RUNNING) - sceMsgDialogTerminate(); - if(sceImeDialogGetStatus() == ORBIS_DIALOG_STATUS_RUNNING) - sceImeDialogTerm(); -} - int ui_ok(const char *msg){ - OrbisMsgDialogParam param; - OrbisMsgDialogUserMessageParam um; - OrbisMsgDialogResult res; - memset(&res, 0, sizeof res); - sceMsgDialogTerminate(); - if(sceMsgDialogInitialize() < 0) return -1; - base_init(¶m); - memset(&um, 0, sizeof um); - um.msg = msg; - um.buttonType = ORBIS_MSG_DIALOG_BUTTON_TYPE_OK; - param.userMsgParam = &um; - if(sceMsgDialogOpen(¶m) < 0){ sceMsgDialogTerminate(); return -1; } - /* Yield while waiting: a hot spin starves the dialog service on the - * console (the proven pattern on this box sleeps 20 ms per tick). */ - while(sceMsgDialogUpdateStatus() != ORBIS_COMMON_DIALOG_STATUS_FINISHED) - sceKernelUsleep(20000); - sceMsgDialogClose(); - sceMsgDialogGetResult(&res); - sceMsgDialogTerminate(); + if(!ui_ready) return -1; + render_card("orbisRPC Setup", msg, 0, 0, "Press X to continue"); + wait_cross(); return 0; } static int progress_open = 0; +static char progress_msg[256] = ""; +static int progress_pct = 0; + +static void progress_repaint(void){ + render_card("orbisRPC Setup", progress_msg, progress_pct, 1, NULL); +} int ui_progress_open(const char *msg){ - OrbisMsgDialogParam param; - OrbisMsgDialogProgressBarParam bar; - if(progress_open){ - /* Self-healing: a prior session that died between open and close - * leaves the flag set with no dialog behind it. Reclaim it. */ - ui_progress_close(); - } - sceMsgDialogTerminate(); - if(sceMsgDialogInitialize() < 0) return -1; - base_init(¶m); - param.mode = ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR; - memset(&bar, 0, sizeof bar); - bar.barType = ORBIS_MSG_DIALOG_PROGRESSBAR_TYPE_PERCENTAGE; - bar.msg = msg; - param.progBarParam = &bar; - if(sceMsgDialogOpen(¶m) < 0){ sceMsgDialogTerminate(); return -1; } + if(!ui_ready) return -1; + if(msg){ strncpy(progress_msg, msg, sizeof progress_msg - 1); progress_msg[sizeof progress_msg - 1] = 0; } + else progress_msg[0] = 0; + progress_pct = 0; progress_open = 1; + progress_repaint(); return 0; } void ui_progress_msg(const char *msg){ - if(progress_open && msg) sceMsgDialogProgressBarSetMsg(0, msg); + if(!progress_open || !msg) return; + strncpy(progress_msg, msg, sizeof progress_msg - 1); + progress_msg[sizeof progress_msg - 1] = 0; + progress_repaint(); } void ui_progress_set(unsigned pct){ - if(progress_open) sceMsgDialogProgressBarSetValue(0, pct > 100 ? 100 : pct); + if(!progress_open) return; + progress_pct = pct > 100 ? 100 : (int)pct; + progress_repaint(); } void ui_progress_close(void){ if(!progress_open) return; progress_open = 0; - sceMsgDialogClose(); - sceMsgDialogTerminate(); + fill_rect(0, 0, SCR_W, SCR_H, C_BG_R, C_BG_G, C_BG_B); + present(); } int ui_input(const char *title, const char *placeholder, char *out, size_t cap){ @@ -158,7 +347,7 @@ int ui_input(const char *title, const char *placeholder, char *out, size_t cap){ OrbisImeDialogSetting st; int32_t uid = 0; size_t i; - if(!out || cap == 0 || cap > 200) return -1; + if(!ui_ready || !out || cap == 0 || cap > 200) return -1; /* Prefill with current value (ASCII round-trip). */ for(i = 0; i < cap - 1 && out[i]; i++) wbuf[i] = (wchar_t)(unsigned char)out[i]; wbuf[i] = 0; @@ -177,23 +366,18 @@ int ui_input(const char *title, const char *placeholder, char *out, size_t cap){ st.option = 0; st.maxTextLength = (uint32_t)(cap - 1); st.inputTextBuffer = wbuf; - /* Center of 1920x1080. At (0,0) the panel anchors up-left and the - * keyboard renders half off-screen; 960,540 is the layout that is - * known-good on this console (rutracker-ps4 uses the same values). */ st.posx = 960; st.posy = 540; st.horizontalAlignment = ORBIS_H_CENTER; st.verticalAlignment = ORBIS_V_CENTER; st.placeholder = wplace; st.title = wtitle; - /* apollo-ps4 pattern: guard against re-entry, terminate stale - * state, then init. The dialog owns the running flag. */ if(ime_dialog_running){ sceImeDialogTerm(); ime_dialog_running = 0; } ime_dbg("ime_begin\n"); if(sceImeDialogInit(&st, NULL) < 0){ ime_dbg("ime_init_fail\n"); return -1; } ime_dialog_running = 1; { - int spins = 0; + int spins = 0, rc = 0; OrbisDialogStatus ds; ds = sceImeDialogGetStatus(); if(ds == ORBIS_DIALOG_STATUS_RUNNING) ime_dbg("ime_status_running\n"); @@ -207,38 +391,45 @@ int ui_input(const char *title, const char *placeholder, char *out, size_t cap){ sceImeDialogGetResult(&res); sceImeDialogTerm(); ime_dialog_running = 0; - if(res.endstatus != ORBIS_DIALOG_OK) return 0; + if(res.endstatus != ORBIS_DIALOG_OK){ rc = 0; break; } + for(i = 0; i < cap - 1 && wbuf[i]; i++) + out[i] = (wbuf[i] < 128 && wbuf[i] >= 32) ? (char)wbuf[i] : '?'; + out[i] = 0; + rc = 1; break; } if(ds == ORBIS_DIALOG_STATUS_NONE && ++spins > 250){ ime_dbg("ime_timeout\n"); sceImeDialogTerm(); ime_dialog_running = 0; - return -1; + rc = -1; + break; } if(ds != ORBIS_DIALOG_STATUS_NONE) spins = 0; sceKernelUsleep(20000); } + /* IME is a system overlay; repaint our card underneath on return. */ + if(progress_open) progress_repaint(); + else { fill_rect(0, 0, SCR_W, SCR_H, C_BG_R, C_BG_G, C_BG_B); present(); } + return rc; } - for(i = 0; i < cap - 1 && wbuf[i]; i++) - out[i] = (wbuf[i] < 128 && wbuf[i] >= 32) ? (char)wbuf[i] : '?'; - out[i] = 0; - return 1; } -/* Teardown for exit: terminate any live dialog, then unload the - * modules loaded in ui_init. Called before _exit so no dialog - * outlives the app. Must unload PAD too since it was loaded - * internally (ORBIS_SYSMODULE_INTERNAL_PAD). */ void ui_shutdown(void){ - if(!ui_ready) return; + int i; + if(!ui_ready){ + /* Still unload in case init died halfway. */ + } ui_ready = 0; progress_open = 0; ime_dialog_running = 0; - sceMsgDialogTerminate(); + for(i = 0; i < 4; i++){ + if(g_pad[i] >= 0){ scePadClose(g_pad[i]); g_pad[i] = -1; } + } + if(g_win){ SDL_DestroyWindow(g_win); g_win = NULL; g_surf = NULL; } + SDL_Quit(); sceImeDialogTerm(); sceSysmoduleUnloadModule(ORBIS_SYSMODULE_IME_BACKEND); sceSysmoduleUnloadModule(ORBIS_SYSMODULE_IME_DIALOG); - sceSysmoduleUnloadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG); sceSysmoduleUnloadModule(ORBIS_SYSMODULE_INTERNAL_PAD); } diff --git a/orbisrpc/cfg.c b/orbisrpc/cfg.c index a37ad7e..5611b46 100644 --- a/orbisrpc/cfg.c +++ b/orbisrpc/cfg.c @@ -15,7 +15,7 @@ void cfg_defaults(cfg_t *c) { memset(c, 0, sizeof(*c)); c->schema_version = CFG_SCHEMA_VERSION; c->enabled = 1; - c->auto_update = 1; + c->auto_update = 0; /* dormant: network self-update removed; updates via reinstall */ c->poll_interval_s = 12; strncpy(c->token, "SET_ME", sizeof(c->token)-1); strncpy(c->presence_state, "On PS4", sizeof(c->presence_state)-1); diff --git a/orbisrpc/cfg.h b/orbisrpc/cfg.h index b219e1c..b0d535c 100644 --- a/orbisrpc/cfg.h +++ b/orbisrpc/cfg.h @@ -31,7 +31,7 @@ typedef struct { char title_ids[CFG_MAX_TITLES][CFG_TITLEID_LEN]; char title_names[CFG_MAX_TITLES][CFG_TITLENAME_LEN]; int enabled; - int auto_update; /* check GitHub releases once per boot, stage newer */ + int auto_update; /* DORMANT (parsed for old configs, never acted on) */ int debug; /* verbose debug logging (per-poll detail, no secrets) */ int poll_interval_s; /* game-check cadence */ char presence_state[128]; /* activity "state" line, e.g. "On PS4" */ diff --git a/orbisrpc/daemon.c b/orbisrpc/daemon.c index 6fee0c1..afce0ab 100644 --- a/orbisrpc/daemon.c +++ b/orbisrpc/daemon.c @@ -96,12 +96,14 @@ static void sess_save(const char *tid, const char *name, int64_t started){ jl /* status.json heartbeat: machine-readable liveness for users and the * installer (FTP-readable proof the daemon is alive, no klog needed). * Written on state changes + every alive tick. Never fatal. */ -static void status_write(const char *state, const char *title){ +static void status_write(const char *state, const char *title, + const char *gw){ jl_val_t *r = jl_new_object(); if(!r) return; jl_obj_set(r, "version", jl_new_string(ORBISRPC_VERSION)); jl_obj_set(r, "state", jl_new_string(state ? state : "?")); jl_obj_set(r, "title", jl_new_string(title ? title : "")); + jl_obj_set(r, "gw", jl_new_string(gw ? gw : "down")); jl_obj_set(r, "ts", jl_new_number((double)time(NULL))); char *s = jl_stringify(r); jl_free(r); @@ -255,7 +257,7 @@ int daemon_run(const char *fixed_game_name){ * wait for a token to appear (FTP edit, no reboot, no exit). */ health_mark_healthy(); log_msg("no token in %s; waiting (edit \"token\" over FTP)", CFG_PATH); - status_write("waiting_token", ""); + status_write("waiting_token", "", "down"); for(;;){ if(s_stop){ log_close(); return 0; } if(sleep_stop(15)) { log_close(); return 0; } @@ -266,16 +268,9 @@ int daemon_run(const char *fixed_game_name){ log_msg("token appeared; continuing boot"); } - /* Self-update once per boot, before first connect. Never fatal: - * staged artifacts take effect on next launch/injection. */ - if(g_cfg.auto_update && !safe_mode){ - int ur = updater_check_and_stage(); - log_msg("updater: %s (local %s)", - ur > 0 ? "staged newer build" : ur == 0 ? "already current" : "check failed", - ORBISRPC_VERSION); - } else if(safe_mode){ - log_msg("updater: skipped (safe mode)"); - } + /* No network self-update: new versions arrive via reinstall PKG, + * which bumps daemon.gen so this process supersedes cleanly. */ + log_msg("boot: local %s (updates via reinstall)", ORBISRPC_VERSION); discord_t dc; memset(&dc, 0, sizeof dc); /* ws_close guards on connected; zero = safe */ @@ -301,7 +296,7 @@ int daemon_run(const char *fixed_game_name){ long cur = gen_read(); if(cur > boot_gen){ log_msg("superseded by generation %ld; exiting cleanly", cur); - status_write("superseded", ""); + status_write("superseded", "", "down"); if(dc.connected) discord_clear_presence(&dc); ws_close(&dc.ws); break; @@ -426,6 +421,7 @@ int daemon_run(const char *fixed_game_name){ static int cand_hits = 0, miss_hits = 0; int64_t last_poll = 0; int64_t last_alive = 0; + int64_t last_wall = 0; static int healthy_marked = 0; /* re-post after every (re)connect so Discord never sticks on stale */ int need_post = active && last[0]; @@ -433,12 +429,30 @@ int daemon_run(const char *fixed_game_name){ int64_t now = orbis_mono_s(); if(now - last_tsync >= 3600){ last_tsync = now; time_sync(); } if(now != last_poll){ + /* Rest Mode / resume detection: the wall clock jumping + * forward while the monotonic clock barely moved means we + * slept through a suspend. Force a time re-sync and a + * state reconcile so timers and presence heal instead of + * posting stale epochs. */ + { + int64_t wall = (int64_t)time(NULL); + if(last_wall > 0 && wall - last_wall > 900 && + now - last_poll <= 2){ + log_msg("wall jumped +%llds (resumed?); re-syncing", + (long long)(wall - last_wall)); + last_tsync = 0; + if(active && last[0]) need_post = 1; + else home_posted = 0; + } + last_wall = wall; + } last_poll = now; if(now - last_alive >= 60){ last_alive = now; log_msg("alive: %s", active ? last : "idle"); status_write(active ? "playing" : "idle", - active ? last : ""); + active ? last : "", + discord_state_name(&dc)); } /* State reconciliation: re-post current presence every * 15 min so a silently desynced tile (dropped update, diff --git a/orbisrpc/discord.c b/orbisrpc/discord.c index 3ffd49a..22258ae 100644 --- a/orbisrpc/discord.c +++ b/orbisrpc/discord.c @@ -150,15 +150,28 @@ static int send_identify(discord_t *d, const char *token){ return rc < 0 ? -1 : 0; } +const char *discord_state_name(const discord_t *d){ + if(!d || !d->connected) return "down"; + switch(d->state){ + case GW_CONNECTING: return "connecting"; + case GW_HELLO_WAIT: return "hello_wait"; + case GW_IDENTIFYING: return "identifying"; + case GW_READY: return "ready"; + default: return "down"; + } +} + int discord_connect(discord_t *d, const char *token){ if(!d || !token || !token[0]) return -1; memset(d,0,sizeof(*d)); + d->state = GW_CONNECTING; strncpy(d->token, token, sizeof d->token-1); d->token[sizeof d->token-1] = 0; char key[64]=""; make_key(key); int rc=ws_connect(&d->ws, GW_HOST, GW_PORT, GW_PATH, key); if(rc){ log_msg("ws connect fail %d",rc); return -1; } d->connected=1; + d->state = GW_HELLO_WAIT; int64_t now=orbis_mono_s(); d->last_heartbeat=now; d->last_ack=now; /* HELLO (text frame carrying {"op":10,...}) */ @@ -190,6 +203,7 @@ int discord_connect(discord_t *d, const char *token){ ws_close(&d->ws); d->connected=0; return -1; } + d->state = GW_IDENTIFYING; log_msg("discord: identify sent, hb=%llds",(long long)(d->hb_interval_ms/1000)); /* READY confirms the token was accepted. `op` is the WebSocket frame * type; the gateway event is JSON inside — parse it, don't switch on it. */ @@ -212,6 +226,7 @@ int discord_connect(discord_t *d, const char *token){ if(go==11){ d->last_ack=orbis_mono_s(); continue; } if(go==0 && is_ready(buf, (size_t)nr)){ gw_seq(d, buf, (size_t)nr); + d->state = GW_READY; log_msg("discord: gateway ready"); return 0; } @@ -426,6 +441,10 @@ int discord_clear_presence(discord_t *d){ int discord_tick(discord_t *d){ if(!d || !d->connected) return -1; + /* Liveness lives in connected; state records the highest phase + * reached. A drop leaves state at its phase with connected=0 — + * observers must read through discord_state_name(), which reports + * any !connected session as down. */ int64_t now=orbis_mono_s(); long hb_s=(long)(d->hb_interval_ms/1000); if(hb_s<5)hb_s=5; /* gateway must ack heartbeats; 2 missed intervals means it's gone */ diff --git a/orbisrpc/discord.h b/orbisrpc/discord.h index 205e457..4cd87df 100644 --- a/orbisrpc/discord.h +++ b/orbisrpc/discord.h @@ -10,9 +10,21 @@ typedef struct { int64_t last_ack; /* when the gateway last acked (op 11) */ int64_t hb_interval_ms; int seq; /* last dispatch sequence (heartbeat payload) */ - int connected; + int connected; /* 1 iff state == GW_READY (derived mirror) */ int sent_hb; /* at least one heartbeat sent */ + int state; /* gw_state_t: explicit session state */ } discord_t; +/* Explicit gateway state machine. GW_DOWN is 0 so a zeroed struct starts + * DOWN; connected==1 exactly when state==GW_READY. Deliberately no + * RESUME state: we always fresh-IDENTIFY (simpler, stateless recovery). */ +typedef enum { + GW_DOWN = 0, /* no usable session */ + GW_CONNECTING, /* TCP+TLS+WebSocket handshake in flight */ + GW_HELLO_WAIT, /* waiting for op 10 HELLO */ + GW_IDENTIFYING, /* IDENTIFY sent, waiting for READY */ + GW_READY /* live session (connected == 1) */ +} gw_state_t; +const char *discord_state_name(const discord_t *d); /* Returns 0 on READY, -1 net/proto error, -2 auth-fatal (close 4004: don't * retry — the token is wrong and Discord bans IPs that hammer it). */ int discord_connect(discord_t *d, const char *token); diff --git a/orbisrpc/updater.c b/orbisrpc/updater.c deleted file mode 100644 index 0e28574..0000000 --- a/orbisrpc/updater.c +++ /dev/null @@ -1,455 +0,0 @@ -/* updater.c - self-updater. Pure logic (host-tested) plus PS4 HTTPS. - * - * Flow, once per daemon boot when enabled: - * GET https://api.github.com/repos//releases/latest - * -> tag_name + asset browser_download_urls - * if tag newer than ORBISRPC_VERSION: - * download each known asset (cap 4MB), validate ELF magic, - * write .new, rename over target. - * Plugin .prx takes effect on next game launch; payload .bin on next - * injection. Never deletes, never writes unvalidated bytes. - */ -#include "updater.h" -#include "updater_http.h" -#include "version.h" -#include "clock.h" -#include "jsonlite.h" -#include "health.h" -#include "manifest.h" -#include "release_pubkey.h" -#include "log.h" -#include -#include -#include - -/* ---- PS4 HTTPS transport (mirrors ws.c/tmdb.c bring-up) ---- */ -#include "tls.h" -#include -#ifdef ORBISRPC_SDK_PAYLOAD -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#endif -#include -#include -#include - -#define UPD_HOST "api.github.com" -#define UPD_DEADLINE_S 15 -#define UPD_BODY_MAX (4u*1024u*1024u) -#define UPD_MAX_REDIRECTS 3 - -/* Release-channel host allowlist: only GitHub API + release infra. - * Redirects anywhere else are refused (fail closed). */ -static int upd_host_allowed(const char *host){ - static const char *ok[] = { - "api.github.com", - "github.com", - "uploads.github.com", - "objects.githubusercontent.com", - "codeload.github.com", - "raw.githubusercontent.com", - NULL, - }; - if(!host || !host[0]) return 0; - for(int i = 0; ok[i]; i++) if(!strcmp(host, ok[i])) return 1; - return 0; -} - -#ifndef SO_NBIO -#define SO_NBIO 0x2000 /* same local define as ws.c; absent from SDK headers */ -#endif - -static int32_t s_upool = -1; -static int upd_net(void){ - static int ready = 0; - if(ready) return 0; -#ifdef ORBISRPC_SDK_PAYLOAD - ready = 1; - return 0; -#else - uint32_t ur = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET); - if((int)ur < 0) return -1; - if(sceNetInit() < 0) return -1; - s_upool = (int32_t)sceNetPoolCreate("upd", 128*1024, 0); - if(s_upool < 0) return -1; - ready = 1; - return 0; -#endif -} - -static int upd_connect(const char *host, int port){ - if(upd_net() < 0) return -1; -#ifdef ORBISRPC_SDK_PAYLOAD - struct addrinfo hints, *res = NULL; - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - { - char portbuf[16]; - snprintf(portbuf, sizeof portbuf, "%d", port); - if(getaddrinfo(host, portbuf, &hints, &res) != 0 || !res){ - log_msg("updater: dns fail"); - return -1; - } - } - int fd = socket(AF_INET, SOCK_STREAM, 0); - if(fd < 0){ freeaddrinfo(res); return -1; } - struct timeval tv = { .tv_sec = UPD_DEADLINE_S, .tv_usec = 0 }; - setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); - if(connect(fd, res->ai_addr, res->ai_addrlen) < 0){ - close(fd); freeaddrinfo(res); return -1; - } - freeaddrinfo(res); - { - int fl = fcntl(fd, F_GETFL, 0); - if(fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK); - } - return fd; -#else - OrbisNetInAddr in; - memset(&in, 0, sizeof in); - int32_t rid = sceNetResolverCreate("updR", (uint32_t)s_upool, 0); - if(rid < 0) return -1; - int32_t rr = sceNetResolverStartNtoa(rid, host, &in, 8000000, 3, 0); - sceNetResolverDestroy(rid); - if(rr < 0){ log_msg("updater: dns fail"); return -1; } - int fd = sceNetSocket("upd", ORBIS_NET_AF_INET, ORBIS_NET_SOCK_STREAM, 0); - if(fd < 0) return -1; - struct timeval tv = { .tv_sec = UPD_DEADLINE_S, .tv_usec = 0 }; - sceNetSetsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); - OrbisNetSockaddr sa; - memset(&sa, 0, sizeof sa); - sa.sa_family = 2; - *(uint16_t*)sa.sa_data = sceNetHtons((uint16_t)port); - memcpy(sa.sa_data+2, &in.s_addr, 4); - if(sceNetConnect(fd, &sa, sizeof sa) < 0){ - sceNetSocketClose(fd); return -1; - } - int nb = 1; - sceNetSetsockopt(fd, SOL_SOCKET, SO_NBIO, &nb, sizeof nb); - return fd; -#endif -} - -/* HTTPS GET with real HTTP semantics (bounded): - * status line + headers (cap UPD_HDR_MAX) + Content-Length or - * Transfer-Encoding: chunked decoding + redirect capture. - * Returns heap body (caller frees) with out_len; out_status always set; - * out_location gets the redirect target ("" when none). Only the body - * counts against cap; headers are separately bounded. */ -static char *https_get_once(const char *host, const char *path, - size_t cap, int *out_status, size_t *out_len, - char *out_location, size_t loc_cap){ - if(out_status) *out_status = 0; - if(out_len) *out_len = 0; - if(out_location && loc_cap) out_location[0] = 0; - if(!upd_host_allowed(host)){ log_msg("updater: host refused (%s)", host); return NULL; } - int fd = upd_connect(host, 443); - if(fd < 0) return NULL; - tls_ctx_t *t = tls_start(fd, host); - if(!t){ -#ifdef ORBISRPC_SDK_PAYLOAD - close(fd); -#else - sceNetSocketClose(fd); -#endif - return NULL; - } - char req[512]; - int rl = snprintf(req, sizeof req, - "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: orbisRPC/%s\r\nConnection: close\r\n\r\n", - path, host, ORBISRPC_VERSION); - if(rl <= 0 || rl >= (int)sizeof req){ tls_free(t); return NULL; } - if(tls_write(t, req, (size_t)rl) < 0){ tls_free(t); return NULL; } - /* Read raw response (headers + body) up to header cap + body cap. */ - size_t rawcap = UPD_HDR_MAX + cap; - char *raw = (char*)malloc(rawcap + 1); /* +1: NUL pad when a read fills cap exactly */ - if(!raw){ tls_free(t); return NULL; } - size_t rl2 = 0; - int64_t dl = orbis_mono_s() + UPD_DEADLINE_S + 20; - for(;;){ - char tmp[2048]; - int r = tls_read(t, tmp, sizeof tmp); - if(r < 0) break; - if(r == 0){ - if(orbis_mono_s() > dl) break; - usleep(20000); - continue; - } - size_t room = rawcap > rl2 ? rawcap - rl2 : 0; - size_t cp = (size_t)r < room ? (size_t)r : room; - if(cp) memcpy(raw + rl2, tmp, cp); - rl2 += cp; - if(rl2 >= rawcap - 1 || orbis_mono_s() > dl) break; - } - tls_free(t); - if(rl2 == 0){ free(raw); return NULL; } - raw[rl2] = 0; - char *out = upd_parse_response(raw, rl2, cap, out_status, out_len, - out_location, loc_cap); - free(raw); - return out; -} - -/* HTTPS GET following absolute-URL https redirects within the allowlist - * (GitHub release assets redirect to object storage). Relative Location - * values stay on the same host. Refuses non-https, off-allowlist, and - * redirect loops. */ -static char *https_get(const char *host, const char *path, - size_t cap, int *out_status, size_t *out_len){ - char hbuf[128], pbuf[512], loc[512]; - snprintf(hbuf, sizeof hbuf, "%s", host); - snprintf(pbuf, sizeof pbuf, "%s", path); - for(int i = 0; i <= UPD_MAX_REDIRECTS; i++){ - int status = 0; - size_t len = 0; - char *body = https_get_once(hbuf, pbuf, cap, &status, &len, - loc, sizeof loc); - if(!body){ - if(out_status) *out_status = status; - if(out_len) *out_len = 0; - return NULL; - } - if((status == 301 || status == 302 || status == 303 || - status == 307 || status == 308) && loc[0]){ - free(body); - if(!strncmp(loc, "https://", 8)){ - const char *h0 = loc + 8; - const char *p0 = strchr(h0, '/'); - if(!p0 || (size_t)(p0 - h0) >= sizeof hbuf){ - if(out_status) *out_status = status; - if(out_len) *out_len = 0; - return NULL; - } - memcpy(hbuf, h0, (size_t)(p0 - h0)); - hbuf[p0 - h0] = 0; - snprintf(pbuf, sizeof pbuf, "%s", p0); - } else if(loc[0] == '/'){ - snprintf(pbuf, sizeof pbuf, "%s", loc); - } else { - if(out_status) *out_status = status; - if(out_len) *out_len = 0; - return NULL; - } - if(!upd_host_allowed(hbuf)){ - log_msg("updater: redirect refused (off-allowlist %s)", hbuf); - if(out_status) *out_status = status; - if(out_len) *out_len = 0; - return NULL; - } - log_msg("updater: redirect -> %s%s", hbuf, pbuf); - continue; - } - if(out_status) *out_status = status; - if(out_len) *out_len = len; - if(status != 200 || len == 0){ free(body); return NULL; } - return body; - } - if(out_status) *out_status = 0; - if(out_len) *out_len = 0; - return NULL; -} - -static int stage_file(const char *target, const unsigned char *data, size_t n){ - char tmp[192]; - snprintf(tmp, sizeof tmp, "%s.new", target); - if(!updater_image_ok(data, n)){ log_msg("updater: staged bytes failed validation"); return -1; } - FILE *f = fopen(tmp, "wb"); - if(!f){ log_msg("updater: cannot write %s", tmp); return -1; } - int ok = (fwrite(data, 1, n, f) == n); - if(fflush(f) != 0) ok = 0; - if(ok){ int fd = fileno(f); if(fd >= 0 && fsync(fd) != 0) ok = 0; } - if(fclose(f) != 0) ok = 0; - if(!ok){ remove(tmp); return -1; } - /* Atomic verified activation: validates .new, backs up live - * to .bak, activates, re-verifies, auto-restores .bak on failure. - * A refused stage leaves the live target untouched (ORX-UPDATE-003). */ - if(health_stage_activate(target) != 0){ - log_msg("updater: stage activate failed for %s; live kept", target); - return -1; - } - return 0; -} - -/* Download a release asset by exact name. Returns heap body or NULL. */ -static char *fetch_asset(const jl_val_t *assets, const char *want_name, - size_t cap, int *status, size_t *out_len){ - for(size_t i = 0; ; i++){ - const jl_val_t *a = jl_arr_at(assets, i); - if(!a) break; - const jl_val_t *nm = jl_obj_get(a, "name"); - const jl_val_t *dl = jl_obj_get(a, "browser_download_url"); - if(!nm || nm->type != JL_STRING || !dl || dl->type != JL_STRING) continue; - if(strcmp(nm->str, want_name) != 0) continue; - const char *url = dl->str; - if(strncmp(url, "https://", 8) != 0) return NULL; - const char *h0 = url + 8; - const char *p0 = strchr(h0, '/'); - if(!p0 || (size_t)(p0 - h0) >= 128) return NULL; - char host[128]; - memcpy(host, h0, (size_t)(p0 - h0)); host[p0 - h0] = 0; - return https_get(host, p0, cap, status, out_len); - } - return NULL; -} - -/* Decode manifest.sig: raw 64 bytes, or 128 hex chars. 0 ok. */ -static int decode_sig(const char *body, size_t len, unsigned char out[64]){ - if(len == 64){ memcpy(out, body, 64); return 0; } - /* strip whitespace for hex form; exactly 128 hex chars required — - * trailing garbage after the 128 fails closed */ - char hex[129]; - size_t hn = 0, total = 0; - for(size_t i = 0; i < len; i++){ - char c = body[i]; - if(c == ' ' || c == '\t' || c == '\r' || c == '\n') continue; - total++; - if(hn < 128) hex[hn++] = c; - } - if(hn != 128 || total != 128) return -1; - for(int i = 0; i < 64; i++){ - unsigned v = 0; - for(int k = 0; k < 2; k++){ - char c = hex[2 * i + k]; - v <<= 4; - if(c >= '0' && c <= '9') v |= (unsigned)(c - '0'); - else if(c >= 'a' && c <= 'f') v |= (unsigned)(c - 'a' + 10); - else if(c >= 'A' && c <= 'F') v |= (unsigned)(c - 'A' + 10); - else return -1; - } - out[i] = (unsigned char)v; - } - return 0; -} - -int updater_check_and_stage(void){ - char path[160]; - snprintf(path, sizeof path, "/repos/%s/releases/latest", ORBISRPC_REPO); - size_t rl = 0; - int status = 0; - char *js = https_get(UPD_HOST, path, 65536, &status, &rl); - if(!js){ log_msg("updater: release check failed (status=%d)", status); return -1; } - jl_val_t *r = jl_parse(js, rl); - free(js); - if(!r){ log_msg("updater: release parse failed"); return -1; } - const jl_val_t *tag = jl_obj_get(r, "tag_name"); - const jl_val_t *assets = jl_obj_get(r, "assets"); - int updated = 0; - if(tag && tag->type == JL_STRING && tag->str[0] && - updater_cmp(tag->str, ORBISRPC_VERSION) > 0){ - log_msg("updater: %s available (local %s)", tag->str, ORBISRPC_VERSION); - if(assets && assets->type == JL_ARRAY){ - /* Preferred: signed manifest (manifest.json + manifest.sig). - * Verified with the embedded release pubkey; every binary must - * match its listed SHA256. A bad signature refuses the whole - * update (ORX-UPDATE-002). */ - manifest_t mf; - int have_manifest = 0; - size_t ml = 0; - char *mbody = fetch_asset(assets, "manifest.json", 65536, &status, &ml); - if(mbody){ - size_t sl = 0; - char *sbody = fetch_asset(assets, "manifest.sig", 4096, &status, &sl); - if(sbody && manifest_parse(mbody, ml, &mf) == 0 && - manifest_gate(&mf) && manifest_is_newer(&mf, ORBISRPC_VERSION)){ - unsigned char sig[64]; - if(decode_sig(sbody, sl, sig) == 0 && - manifest_verify_sig((unsigned char*)mbody, ml, sig, - ORBISRPC_RELEASE_PUBKEY) == 0){ - have_manifest = 1; - log_msg("updater: manifest %s verified (key %s)", - mf.version, ORBISRPC_RELEASE_KEY_ID); - } else { - log_msg("updater: WARN ORX-UPDATE-002: manifest signature invalid; refusing update"); - } - } else if(sbody){ - log_msg("updater: WARN ORX-UPDATE-002: manifest invalid/gated; refusing update"); - } - free(sbody); - if(!have_manifest){ free(mbody); mbody = NULL; } - } - /* Refuse unsigned updates outright. SHA256SUMS comes from the - * same release as the binaries, so it pins nothing against - * release-asset compromise — exactly what the signed manifest - * defends against (ORX-UPDATE-002). */ - if(!have_manifest){ - log_msg("updater: no valid signed manifest; refusing update (ORX-UPDATE-002)"); - jl_free(r); - return 0; - } - /* Two-phase commit: download + verify EVERY asset first, then - * activate all at once. A failure anywhere stages nothing, so - * the runtime can never mix a new payload with an old plugin - * (or vice versa) — rollback restores the complete runtime. */ - struct { const char *target; char *bin; size_t len; } pend[2]; - memset(pend, 0, sizeof pend); - int pend_n = 0, pend_fail = 0; - for(size_t i = 0; ; i++){ - const jl_val_t *a = jl_arr_at(assets, i); - if(!a) break; - const jl_val_t *nm = jl_obj_get(a, "name"); - const jl_val_t *dl = jl_obj_get(a, "browser_download_url"); - if(!nm || nm->type != JL_STRING || !dl || dl->type != JL_STRING) continue; - const char *target = NULL; - if(!strcmp(nm->str, "orbisrpc.bin")) target = "/data/payloads/orbisrpc.bin"; - else if(!strcmp(nm->str, "orbisrpc_plugin.prx")) target = "/data/GoldHEN/plugins/orbisrpc_plugin.prx"; - else continue; - /* download URLs must be https (fail closed on http/other). */ - const char *url = dl->str; - if(strncmp(url, "https://", 8) != 0){ pend_fail = 1; break; } - const char *h0 = url + 8; - const char *p0 = strchr(h0, '/'); - if(!p0 || (size_t)(p0-h0) >= 128){ pend_fail = 1; break; } - char host[128]; - memcpy(host, h0, (size_t)(p0-h0)); host[p0-h0] = 0; - size_t al = 0; - char *bin = https_get(host, p0, UPD_BODY_MAX, &status, &al); - if(!bin || !updater_image_ok((unsigned char*)bin, al)){ - log_msg("updater: asset %s failed validation", nm->str); - free(bin); - pend_fail = 1; - break; - } - if(manifest_check(&mf, nm->str, (unsigned char*)bin, al) != 0){ - log_msg("updater: asset %s not in manifest or hash mismatch; refusing (ORX-UPDATE-002)", nm->str); - free(bin); - pend_fail = 1; - break; - } - if(pend_n < 2){ - pend[pend_n].target = target; - pend[pend_n].bin = bin; - pend[pend_n].len = al; - pend_n++; - } else { - free(bin); /* more binaries than we stage; ignore extras */ - } - } - if(!pend_fail && pend_n > 0){ - for(int pi = 0; pi < pend_n; pi++){ - if(stage_file(pend[pi].target, - (unsigned char*)pend[pi].bin, pend[pi].len) == 0){ - log_msg("updater: staged %s (%zu bytes)", - pend[pi].target, pend[pi].len); - updated = 1; - } - } - } else if(pend_fail){ - log_msg("updater: incomplete set; staged nothing (versions stay matched)"); - } - for(int pi = 0; pi < pend_n; pi++) free(pend[pi].bin); - free(mbody); - } - } - jl_free(r); - return updated ? 1 : 0; -} diff --git a/orbisrpc/updater.h b/orbisrpc/updater.h index a6e9eec..69dc091 100644 --- a/orbisrpc/updater.h +++ b/orbisrpc/updater.h @@ -1,4 +1,7 @@ -/* updater.h - self-updater: version compare, staged install. */ +/* updater.h - version compare + staged-image validation helpers. + * Network self-update was REMOVED (updates ship via reinstall PKG); + * the remaining routines are pure/host-testable and back local + * integrity checks (health rollback) and the installer. */ #ifndef UPDATER_H #define UPDATER_H #include @@ -9,8 +12,4 @@ int updater_cmp(const char *a, const char *b); int updater_elf_ok(const unsigned char *buf, size_t n); /* Accepts raw ELF (payload .bin) or signed SELF (plugin .prx). */ int updater_image_ok(const unsigned char *buf, size_t n); -/* Check latest GitHub release; download + atomically stage newer - * artifacts the daemon actually runs from. Returns 1 updated, - * 0 already current, -1 failed/checked-off. Never fatal. */ -int updater_check_and_stage(void); #endif diff --git a/scripts/build.sh b/scripts/build.sh index 286e310..e2fad71 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -47,7 +47,7 @@ LDFLAGS="-m elf_x86_64 -pie --eh-frame-hdr -L$SDK/lib $LIBS $SDK/lib/crt1.o --sc export OO_PS4_TOOLCHAIN="$SDK" OUT="$ROOT/build"; mkdir -p "$OUT" echo "=== compiling (CC=$CC LD=$LD SDK=$SDK) ===" -for f in log cfg jsonlite b64 sfo tmdb_crypto tmdb updater updater_http updater_util tls ws detect focus fw discord daemon compat lock timesync art health manifest main; do +for f in log cfg jsonlite b64 sfo tmdb_crypto tmdb updater_http updater_util tls ws detect focus fw discord daemon compat lock timesync art health manifest main; do "$CC" $CFLAGS -c -o "$OUT/$f.o" "orbisrpc/$f.c" || fail "compile $f" done echo "=== mbedtls (skip net_sockets/timing: POSIX-only) ===" diff --git a/scripts/build_sdk.sh b/scripts/build_sdk.sh index 3a57c79..586cab4 100755 --- a/scripts/build_sdk.sh +++ b/scripts/build_sdk.sh @@ -16,7 +16,7 @@ mkdir -p "$OUT" CFLAGS="-O2 -Wall -DORBISRPC_SDK_PAYLOAD -Iorbisrpc -Ithird_party/mbedtls/include -Ithird_party/sqlite/sqlite-amalgamation-3510100" SQLITE_DIR="third_party/sqlite/sqlite-amalgamation-3510100" echo "=== daemon sources (SDK) ===" -for f in log cfg jsonlite b64 sfo tmdb_crypto tmdb updater updater_http updater_util tls ws detect focus fw discord daemon compat lock timesync art health manifest appdb main; do +for f in log cfg jsonlite b64 sfo tmdb_crypto tmdb updater_http updater_util tls ws detect focus fw discord daemon compat lock timesync art health manifest appdb main; do # clock has no .c (header-only helper lives in compat.c); skip if missing [ -f "orbisrpc/$f.c" ] || continue "$CC" $CFLAGS -c -o "$OUT/$f.o" "orbisrpc/$f.c" || { echo "FAIL: $f"; exit 1; } diff --git a/scripts/genfont.py b/scripts/genfont.py new file mode 100644 index 0000000..2fe65c3 --- /dev/null +++ b/scripts/genfont.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +"""Rasterize an embedded bitmap font for the installer UI (no TTF on PS4). + +Reads a system font with PIL, renders printable ASCII at a fixed pixel +size, and emits installer/font.h: 8-bit alpha glyphs, fixed height, +variable advance. Rerun: python3 scripts/genfont.py +""" +import os +import sys + +SIZE = 30 +FIRST, LAST = 32, 126 +CANDIDATES = [ + "/System/Library/Fonts/Helvetica.ttc", + "/System/Library/Fonts/Supplemental/Arial.ttf", + "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", +] + +try: + from PIL import Image, ImageDraw, ImageFont +except ImportError: + sys.exit("PIL required: pip install pillow") + + +def pick_font(): + for p in CANDIDATES: + if os.path.exists(p): + return p + sys.exit("no usable system font found") + + +def main(): + path = pick_font() + out = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "installer", "font.h") + glyphs = [] + max_h = 0 + for code in range(FIRST, LAST + 1): + fnt = ImageFont.truetype(path, SIZE, index=0) + ch = chr(code) + tmp = Image.new("L", (SIZE * 3, SIZE * 3), 0) + d = ImageDraw.Draw(tmp) + d.text((SIZE, SIZE), ch, font=fnt, fill=255, anchor="lm") + bb = tmp.getbbox() + if bb is None: # space and friends: blank, advance from font + adv_w = max(1, int(fnt.getlength(ch))) + glyphs.append((adv_w, [])) + continue + l, t, r, b = bb + w = max(1, r - l) + h = max(1, b - t) + max_h = max(max_h, h) + crop = [list(tmp.crop((l, yy, r, yy + 1)).getdata()) + for yy in range(t, b)] + glyphs.append((w, crop)) + + H = max_h + blob = bytearray() + adv, off = [], [] + for w, crop in glyphs: + off.append(len(blob)) + adv.append(w) + # pad crop rows to (H x w), top-aligned + for y in range(H): + src = crop[y] if y < len(crop) else [0] * w + src = (list(src) + [0] * w)[:w] + blob.extend(src) + + with open(out, "w") as f: + f.write("/* font.h - GENERATED by scripts/genfont.py. Do not edit. */\n") + f.write("#ifndef INSTALLER_FONT_H\n#define INSTALLER_FONT_H\n") + f.write("#include \n") + f.write("#define FONT_H %d\n#define FONT_FIRST %d\n#define FONT_COUNT %d\n" + % (H, FIRST, LAST - FIRST + 1)) + f.write("static const uint8_t font_adv[FONT_COUNT] = {%s};\n" + % ",".join(str(a) for a in adv)) + f.write("static const unsigned font_off[FONT_COUNT] = {%s};\n" + % ",".join(str(o) for o in off)) + f.write("static const uint8_t font_px[%d] = {" % len(blob)) + for i in range(0, len(blob), 16): + f.write("\n") + f.write(",".join(str(v) for v in blob[i:i + 16]) + ",") + f.write("\n};\n#endif\n") + print("wrote %s (%d glyphs, H=%d, %d bytes)" % (out, len(glyphs), H, len(blob))) + + +main() diff --git a/tests/test_utils.c b/tests/test_utils.c index 99d3437..b88b772 100644 --- a/tests/test_utils.c +++ b/tests/test_utils.c @@ -620,6 +620,36 @@ static void test_installer_cfg(void) { assert(icfg_get_str("/nonexistent/x.json", "k", st, sizeof st) != 0); } +static void test_daemon_state(void) { + char dir[64], path[96], st[32]; + FILE *f; + assert(make_tmpdir(dir, sizeof dir) == 0); + snprintf(path, sizeof path, "%s/status.json", dir); + /* missing file: not running */ + assert(icfg_daemon_state(path, 1000000, 120, st, sizeof st) == 0); + /* fresh heartbeat: running, state copied */ + f = fopen(path, "wb"); assert(f); + fputs("{\"version\":\"1.0.0\",\"state\":\"playing\",\"title\":\"Game\",\"ts\":999990}", f); + fclose(f); + assert(icfg_daemon_state(path, 1000000, 120, st, sizeof st) == 1); + assert(!strcmp(st, "playing")); + /* stale heartbeat: not running */ + assert(icfg_daemon_state(path, 1000200, 120, st, sizeof st) == 0); + /* corrupt file: not running, no crash */ + f = fopen(path, "wb"); assert(f); fputs("not json{{{", f); fclose(f); + assert(icfg_daemon_state(path, 1000000, 120, st, sizeof st) == 0); + /* future ts (clock skew): stale-safe */ + f = fopen(path, "wb"); assert(f); fputs("{\"ts\":2000000}", f); fclose(f); + assert(icfg_daemon_state(path, 1000000, 120, st, sizeof st) == 0); + /* ts present, state absent: fresh with "" */ + f = fopen(path, "wb"); assert(f); fputs("{\"ts\":999999}", f); fclose(f); + assert(icfg_daemon_state(path, 1000000, 120, st, sizeof st) == 1); + assert(!strcmp(st, "")); + /* guards */ + assert(icfg_daemon_state(NULL, 1000000, 120, st, sizeof st) == 0); + assert(icfg_daemon_state(path, 1000000, -1, st, sizeof st) == 0); +} + static void test_focus(void) { char tid[16]; /* last event wins; target is the TO side of -> */ @@ -706,6 +736,7 @@ int main(void) { test_cfg_titles(); test_cfg_learn(); test_installer_cfg(); + test_daemon_state(); test_appdb(); test_discord_builder(); test_focus();