#include #include #include #include #include #include #include #include extern "C" int sceSysmoduleLoadModule(int moduleId); // el teclado usb va por aqui. antes lo intentaba con el ime y no habia manera, // resulta que openorbis ya trae libSceKeyboard y era eso desde el principio #include "ps4_keyboard.h" #include #include #include #include #include #include #include #include #include #include static constexpr int OUTPUT_WIDTH = 1920; static constexpr int OUTPUT_HEIGHT = 1080; static const char* PACKAGE_SYSTEM_NP2 = "/app0/assets/system/np2kai"; static const char* RUNTIME_ROOT = "/data/np2kai-runtime"; static const char* SYSTEM_DIRECTORY = "/data/np2kai-runtime/system"; static const char* RUNTIME_SYSTEM_NP2 = "/data/np2kai-runtime/system/np2kai"; static const char* SAVE_DIRECTORY = "/data/np2kai-runtime/save"; static const char* CONTENT_DIRECTORY = "/data/np2kai-runtime/games"; static const char* CONFIG_DIRECTORY = "/data/np2kai-runtime/config"; static const char* GAME_CONFIG_DIRECTORY = "/data/np2kai-runtime/config/games"; static const char* LOG_DIRECTORY = "/data/np2kai-runtime/logs"; static const char* LATEST_LOG_PATH = "/data/np2kai-runtime/logs/latest.log"; static const char* PREVIOUS_LOG_PATH = "/data/np2kai-runtime/logs/previous.log"; static bool g_running = true; static bool g_gameLoaded = false; static bool g_coreInitialized = false; static constexpr unsigned MAX_GAME_ENTRIES = 256; static constexpr unsigned MAX_GAME_NAME = 256; static constexpr unsigned MAX_GAME_PATH = 1024; static constexpr unsigned LIBRARY_VISIBLE_ROWS = 10; struct GameEntry { char name[MAX_GAME_NAME]; char path[MAX_GAME_PATH]; long size; std::int64_t modifiedTime; }; static GameEntry g_gameEntries[MAX_GAME_ENTRIES] {}; static unsigned g_gameEntryCount = 0; static unsigned g_librarySelection = 0; static unsigned g_libraryScroll = 0; // posiciones "de verdad" en pixeles, que persiguen a las de arriba static float g_libraryFocusY = -1.0f; static float g_libraryScrollPixels = 0.0f; static float g_libraryEnter = 1.0f; static int g_libraryEnterFrom = 0; static float g_settingsEnter = 1.0f; static int g_settingsEnterFrom = 0; static float g_settingsFocusY = -1.0f; static float g_pauseEnter = 1.0f; static float g_pauseFocusY = -1.0f; static float g_listFocusY[4] = {-1.0f, -1.0f, -1.0f, -1.0f}; static std::uint32_t g_libraryPreviousButtons = 0; static int g_libraryHeldDirection = 0; static Uint32 g_libraryNextRepeatTick = 0; static char g_libraryStatus[160] = {}; static bool g_libraryScanFailed = false; static bool g_libraryWasTruncated = false; static const char* FRONTEND_CONFIG_PATH = "/data/np2kai-runtime/config/frontend.ini"; static const char* FRONTEND_CONFIG_TEMP_PATH = "/data/np2kai-runtime/config/frontend.ini.tmp"; enum class DisplayMode { Fit4x3 = 0, Integer2x = 1, Stretch16x9 = 2 }; enum class SettingsPage { Root, Display, Audio, Controls, Emulation, Interface, Developer, Support }; enum class InfoDocument { Setup, Controls, Troubleshooting, SystemFiles, Credits, ProjectStory }; enum class DialogAction { Primary, Retry, Back, Quit }; enum class InterfaceLanguage { Spanish = 0, English = 1 }; enum class PauseAction { Continue, Controls, Restart, SwapDisk, Settings, Library, Quit }; enum class GameSessionResult { Restart, SwapDisk, Library, Error, Quit }; // el touchpad y L3+R3 no estan en esta tabla a proposito. si los dejo mapear // y alguien se los asigna a otra cosa, se queda sin poder abrir el menu enum class ControlProfile { GenericPC98 = 0, TouhouPC98 = 1, MouseOriented = 2, Custom = 3 }; enum class ControlBindingType : std::uint8_t { None = 0, Keyboard, MouseLeft, MouseRight }; struct ControlAction { ControlBindingType type; std::uint16_t keycode; }; enum class ControlChoiceCategory : std::uint8_t { Common = 0, Letters, NumbersAndSymbols, Navigation, FunctionKeys, NumericKeypad, Mouse, Count }; struct ControlChoice { ControlAction action; const char* configName; const char* spanishName; const char* englishName; ControlChoiceCategory category; }; enum class RemappableButton { Cross = 0, Circle, Square, Triangle, L1, R1, L2, R2, Options, Share, Count }; static constexpr unsigned REMAPPABLE_BUTTON_COUNT = static_cast(RemappableButton::Count); struct ControlBindings { ControlProfile profile; ControlAction actions[REMAPPABLE_BUTTON_COUNT]; }; static DisplayMode g_displayMode = DisplayMode::Fit4x3; static bool g_audioEnabled = true; static int g_audioVolumePercent = 100; static bool g_analogMovementEnabled = true; static int g_padStickDeadzone = 48; static int g_cpuMultiplier = 12; static bool g_fastMemoryCheck = true; static bool g_showPerformanceOverlay = false; static bool g_showPadProbeOverlay = false; static bool g_showAudioOverlay = false; static InterfaceLanguage g_interfaceLanguage = InterfaceLanguage::English; static bool g_uiAnimationsEnabled = true; static ControlBindings g_defaultControls {}; static ControlBindings g_activeControls {}; static bool g_activeGameHasControlOverride = false; static char g_activeGameControlPath[MAX_GAME_PATH] {}; static char g_settingsStatus[160] = ""; static double g_currentAudioSampleRate = 44100.0; static std::atomic g_audioPaused {false}; static bool g_audioSubsystemInitialized = false; static std::FILE* g_frontendLog = nullptr; static bool g_settingsSaveFailed = false; static bool g_audioWarningShownThisSession = false; static bool g_pendingConfigRecoveryNotice = false; static char g_lastFailureCode[24] = "NP2-0000"; static char g_lastFailureTitleEs[128] = "Error desconocido"; static char g_lastFailureTitleEn[128] = "Unknown error"; static char g_lastFailureMessageEs[512] = "Se produjo un error inesperado."; static char g_lastFailureMessageEn[512] = "An unexpected error occurred."; static char g_lastFailureDetail[768] = {}; static void frontendLog(const char* format, ...) { if (format == nullptr) return; va_list arguments; va_start(arguments, format); if (g_frontendLog != nullptr) { std::vfprintf(g_frontendLog, format, arguments); std::fputc('\n', g_frontendLog); std::fflush(g_frontendLog); } va_end(arguments); } static void setLastFailure( const char* code, const char* titleEs, const char* titleEn, const char* messageEs, const char* messageEn, const char* detail = nullptr) { std::snprintf(g_lastFailureCode, sizeof(g_lastFailureCode), "%s", code != nullptr ? code : "NP2-0000"); std::snprintf(g_lastFailureTitleEs, sizeof(g_lastFailureTitleEs), "%s", titleEs != nullptr ? titleEs : "Error desconocido"); std::snprintf(g_lastFailureTitleEn, sizeof(g_lastFailureTitleEn), "%s", titleEn != nullptr ? titleEn : "Unknown error"); std::snprintf(g_lastFailureMessageEs, sizeof(g_lastFailureMessageEs), "%s", messageEs != nullptr ? messageEs : "Se produjo un error inesperado."); std::snprintf(g_lastFailureMessageEn, sizeof(g_lastFailureMessageEn), "%s", messageEn != nullptr ? messageEn : "An unexpected error occurred."); std::snprintf(g_lastFailureDetail, sizeof(g_lastFailureDetail), "%s", detail != nullptr ? detail : ""); frontendLog("[%s] %s | %s", g_lastFailureCode, g_lastFailureTitleEn, g_lastFailureDetail); } static retro_pixel_format g_pixelFormat = RETRO_PIXEL_FORMAT_0RGB1555; static unsigned g_videoCallbacks = 0; static unsigned g_validFrames = 0; static double g_displayAspect = 4.0 / 3.0; static double g_targetFps = 56.4; static std::uint8_t* g_frameBuffer = nullptr; static std::size_t g_frameCapacity = 0; static std::size_t g_frameSize = 0; static std::size_t g_framePitch = 0; static unsigned g_frameWidth = 0; static unsigned g_frameHeight = 0; static retro_pixel_format g_framePixelFormat = RETRO_PIXEL_FORMAT_0RGB1555; static bool g_frameReady = false; static double g_recentEmulationFps = 0.0; // --- medicion por tramos --------------------------------------------------- // hasta la v0.42 el tiempo de "presentacion" era un solo numero que metia // dentro la conversion de pixeles, el volcado y la espera del vsync. con eso // no habia forma de saber que estaba frenando de verdad. ahora va separado. // el que mas me interesa es el hueco: si sobra tiempo cada frame es que hay // margen, y si sale cero es que va al limite static double g_msWaitBuffer = 0.0; // esperando a que el buffer se libere static double g_msConvert = 0.0; // pc-98 -> framebuffer static double g_msSubmit = 0.0; // lanzar el flip static double g_msSlack = 0.0; // tiempo que sobra antes del siguiente frame static unsigned g_lateFrames = 0; // frames que llegaron tarde a su hora // el promedio de retro_run puede salir bien y aun asi haber frames sueltos que // se pasan de largo. ese es el que se nota al jugar, asi que lo guardo aparte static double g_msWorstRun = 0.0; static unsigned g_audioCutsPerSecond = 0; static double g_recentPresentFps = 0.0; // acumuladores del segundo en curso static Uint64 g_tickWaitBuffer = 0; static Uint64 g_tickConvert = 0; static Uint64 g_tickSubmit = 0; static Uint64 g_tickSlack = 0; static unsigned g_countLate = 0; static Uint64 g_tickWorstRun = 0; static unsigned g_lastUnderrunTotal = 0; static FILE* g_perfLog = nullptr; // para que cada linea del log diga a que juego pertenece static char g_activeGameName[MAX_GAME_NAME] = {0}; static double g_recentRetroRunMs = 0.0; static double g_recentPresentMs = 0.0; // antes esperaba a cada flip antes de empezar el frame siguiente y se me caia // a 30 fps clavados. asi va montando el siguiente mientras el anterior sale. // solo dejo uno pendiente, con mas parpadeaba static std::int64_t g_lastSubmittedFlipId = -1; // video nativo. SDL se queda solo para timers, hilos y eventos static int g_videoHandle = -1; static OrbisKernelEqueue g_flipQueue {}; static OrbisVideoOutBufferAttribute g_videoAttribute {}; static off_t g_directMemoryOffset = 0; static std::size_t g_directMemorySize = 0; static void* g_directMemory = nullptr; static constexpr int DISPLAY_BUFFER_COUNT = 3; static void* g_displayBuffers[DISPLAY_BUFFER_COUNT] = {}; static bool g_displayBufferCleared[DISPLAY_BUFFER_COUNT] = {}; static std::int64_t g_displayBufferFlipId[DISPLAY_BUFFER_COUNT] = { -1, -1, -1 }; static int g_activeDisplayBuffer = 0; static std::int64_t g_nextFlipId = 1; static bool g_directVideoReady = false; static constexpr std::size_t DISPLAY_BYTES_PER_PIXEL = 4; static constexpr std::size_t DIRECT_MEMORY_ALIGNMENT = 0x200000; // la conversion de pixeles se hace en ram normal. a la memoria de video solo // le mando memcpy grandes y seguidos, tocarla de otra forma va lentisimo static std::uint32_t* g_expandedScanline = nullptr; static std::size_t g_expandedScanlinePixels = 0; static unsigned g_scaleSourceX[OUTPUT_WIDTH] {}; static unsigned g_scaleSourceWidth = 0; static unsigned g_scaleDestinationWidth = 0; static std::uint32_t g_rgb565Lut[65536] {}; static std::uint32_t g_0rgb1555Lut[65536] {}; // pinto todos los frames. el ritmo lo marca sceVideoOut static constexpr unsigned PRESENT_EVERY_EMULATED_FRAMES = 1; static retro_keyboard_event_t g_keyboardCallback = nullptr; // teclado y mando acaban en el mismo sitio. doy el teclado por detectado en // cuanto llega la primera tecla, porque no encontre forma de preguntar // "hay un teclado enchufado?" sin mas static bool g_externalKeyboardState[RETROK_LAST] {}; static bool g_externalKeyboardEventSubsystemReady = false; static bool g_externalKeyboardDetected = false; static unsigned g_lastExternalKeyboardKey = RETROK_UNKNOWN; static std::uint32_t g_externalKeyboardEventGeneration = 0; static Uint32 g_lastExternalKeyboardEventTick = 0; enum class InputLayout : std::uint8_t { Controller = 0, Keyboard = 1 }; static InputLayout g_activeInputLayout = InputLayout::Controller; static std::uint32_t g_keyboardUiButtons = 0; static std::uint32_t g_keyboardUiPulseButtons = 0; static bool g_keyboardPauseHeld = false; static bool g_keyboardTextInputStarted = false; // SDL en ps4 no abre el teclado fisico por su cuenta, asi que manda // libSceKeyboard y SDL queda de reserva. se sondea una vez por frame static bool g_nativeKeyboardOpened = false; static bool g_sdlTimerReady = false; // esta mas abajo, con el mando. mando, audio y teclado comparten el user // service y todos tienen que pasar por la misma funcion static bool ensureUserServiceReady(); static Uint32 g_nativeKeyboardNextRetryTick = 0; static constexpr Uint32 NATIVE_KEYBOARD_RETRY_MS = 2000; static constexpr unsigned NATIVE_KEYBOARD_MAX_EVENTS_PER_POLL = 32; // mando. al core le llega como un retropad normal static int g_padHandle = -1; static int32_t g_padUserId = ORBIS_USER_SERVICE_USER_ID_INVALID; static bool g_userServiceReady = false; static bool g_padSubsystemReady = false; static bool g_padConnected = false; static int g_padLastReadResult = -1; static OrbisPadData g_padData {}; static std::uint32_t g_previousPadButtons = 0; static constexpr std::uint32_t ORBIS_PAD_BUTTON_SHARE_RAW = 0x0001u; // stick derecho como raton, para el menu interno de np2kai y los juegos que // lo usan. libretro quiere deltas, no posiciones absolutas static std::int16_t g_mouseDeltaX = 0; static std::int16_t g_mouseDeltaY = 0; static constexpr int RIGHT_STICK_MOUSE_DEADZONE = 18; static constexpr int RIGHT_STICK_MOUSE_MAX_DELTA = 18; // np2kai saca 44100 y la ps4 quiere 48000, asi que hay un remuestreo lineal // por el medio. no es gran cosa pero suena bien. // el hilo de audio se queda bloqueado en sceAudioOutOutput() el solo, para que // esa espera no frene nunca al retro_run() struct AudioStereoFrame { std::int16_t left; std::int16_t right; }; static constexpr std::uint32_t AUDIO_OUTPUT_RATE = 48000; static constexpr std::uint32_t AUDIO_DEFAULT_SOURCE_RATE = 44100; static constexpr std::uint32_t AUDIO_BLOCK_FRAMES = 256; static constexpr std::uint32_t AUDIO_RING_FRAMES = 16384; static constexpr std::uint32_t AUDIO_RING_MASK = AUDIO_RING_FRAMES - 1; static constexpr std::uint32_t AUDIO_PREBUFFER_FRAMES = 4096; static_assert( (AUDIO_RING_FRAMES & (AUDIO_RING_FRAMES - 1)) == 0, "Audio ring size must be a power of two." ); static AudioStereoFrame g_audioRing[AUDIO_RING_FRAMES] {}; static std::atomic g_audioReadIndex {0}; static std::atomic g_audioWriteIndex {0}; static std::atomic g_audioThreadRun {false}; static std::atomic g_audioDeviceAlive {false}; static std::atomic g_audioPlaybackStarted {false}; static std::atomic g_audioUnderruns {0}; static std::atomic g_audioOverruns {0}; static int g_audioHandle = -1; static SDL_Thread* g_audioThread = nullptr; static std::uint32_t g_audioSourceRate = AUDIO_DEFAULT_SOURCE_RATE; static std::uint32_t g_audioResamplePhase = 0; static bool g_audioHavePreviousFrame = false; static AudioStereoFrame g_audioPreviousFrame {}; // 0 = none/healthy, 1 = no user, 2 = init, 3 = open, // 4 = thread creation, 5 = output. static std::atomic g_audioFailureStage {0}; static void coreLog( enum retro_log_level level, const char* format, ...) { const char* label = "INFO"; switch (level) { case RETRO_LOG_DEBUG: label = "DEBUG"; break; case RETRO_LOG_WARN: label = "WARN"; break; case RETRO_LOG_ERROR: label = "ERROR"; break; default: break; } std::printf("[NP2kai/core/%s] ", label); va_list arguments; va_start(arguments, format); std::vprintf(format, arguments); va_end(arguments); } static const char* getCoreOptionValue(const char* key) { if (key == nullptr) { return nullptr; } if (std::strcmp(key, "np2kai_inputmouse") == 0) { return "ON"; } if (std::strcmp(key, "np2kai_joymode") == 0) { // el joy2key del core solo deja doce teclas fijas, asi que lo apago y // mando las pulsaciones como si fueran de teclado de verdad return "OFF"; } if (std::strcmp(key, "np2kai_joynp2menu") == 0) { return "L2"; } if (std::strcmp(key, "np2kai_stick2mouse") == 0) { return "OFF"; } if (std::strcmp(key, "np2kai_keyboard") == 0) { return "Us"; } // que no se quede con la ruta vieja del np2kai.cfg. manda lo que le paso // por retro_load_game() if (std::strcmp( key, "np2kai_uselasthddmount" ) == 0) { return "OFF"; } // Skip/accelerate the PC-98 firmware memory check. if (std::strcmp(key, "np2kai_FastMC") == 0) { return g_fastMemoryCheck ? "ON" : "OFF"; } // el multiplicador de reloj es cuantos ciclos tiene que emular por frame. // lo dejo bajo a proposito; ya lo subire cuando vea que aguanta if (std::strcmp(key, "np2kai_model") == 0) { return "PC-9801VM"; } if (std::strcmp(key, "np2kai_clk_base") == 0) { return "2.4576 MHz"; } if (std::strcmp(key, "np2kai_clk_mult") == 0) { switch (g_cpuMultiplier) { case 8: return "8"; case 10: return "10"; default: return "12"; } } if (std::strcmp(key, "np2kai_cpu_feature") == 0) { return "Intel 80386"; } if (std::strcmp(key, "np2kai_ExMemory") == 0) { return "3"; } // los touhou usan la placa PC-9801-86. de momento dejo el generador // ligero, que va mas suelto if (std::strcmp(key, "np2kai_SNDboard") == 0) { return g_audioEnabled ? "PC9801-86" : "None"; } if (std::strcmp(key, "np2kai_usefmgen") == 0) { return "Default"; } if (std::strcmp(key, "np2kai_jast_snd") == 0) { return "OFF"; } if (std::strcmp(key, "np2kai_Seek_Snd") == 0) { return "OFF"; } if (std::strcmp(key, "np2kai_BEEP_vol") == 0) { return "0"; } if (std::strcmp(key, "np2kai_vf1") == 0) { return "OFF"; } if (std::strcmp(key, "np2kai_lcd") == 0) { return "OFF"; } if (std::strcmp(key, "np2kai_realpal") == 0) { return "OFF"; } if (std::strcmp(key, "np2kai_gdc") == 0) { return "uPD7220"; } if (std::strcmp(key, "np2kai_skipline") == 0) { return "Full 255 lines"; } // upstream lo marca como experimental, asi que se queda apagado if (std::strcmp(key, "np2kai_async_cpu") == 0) { return "OFF"; } return nullptr; } static bool environmentCallback(unsigned command, void* data) { switch (command) { case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME: return true; case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: if (data == nullptr) { return false; } *static_cast(data) = SYSTEM_DIRECTORY; return true; case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: if (data == nullptr) { return false; } *static_cast(data) = SAVE_DIRECTORY; return true; #ifdef RETRO_ENVIRONMENT_GET_CONTENT_DIRECTORY case RETRO_ENVIRONMENT_GET_CONTENT_DIRECTORY: if (data == nullptr) { return false; } *static_cast(data) = CONTENT_DIRECTORY; return true; #endif case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: { if (data == nullptr) { return false; } const retro_pixel_format requested = *static_cast(data); if (requested != RETRO_PIXEL_FORMAT_0RGB1555 && requested != RETRO_PIXEL_FORMAT_RGB565 && requested != RETRO_PIXEL_FORMAT_XRGB8888) { std::printf( "[NP2kai] Unsupported pixel format: %d\n", static_cast(requested) ); return false; } g_pixelFormat = requested; std::printf( "[NP2kai] Pixel format requested: %d\n", static_cast(requested) ); return true; } case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: { if (data == nullptr) { return false; } retro_log_callback* callback = static_cast(data); callback->log = coreLog; return true; } case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: { if (data == nullptr) { g_keyboardCallback = nullptr; return true; } const retro_keyboard_callback* callback = static_cast(data); g_keyboardCallback = callback->callback; return true; } case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION: if (data == nullptr) { return false; } *static_cast(data) = 0; return true; case RETRO_ENVIRONMENT_SET_VARIABLES: return true; case RETRO_ENVIRONMENT_GET_VARIABLE: { if (data == nullptr) { return false; } retro_variable* variable = static_cast(data); if (variable->key == nullptr) { return false; } variable->value = getCoreOptionValue(variable->key); return variable->value != nullptr; } case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: if (data == nullptr) { return false; } *static_cast(data) = false; return true; case RETRO_ENVIRONMENT_GET_CAN_DUPE: if (data == nullptr) { return false; } *static_cast(data) = true; return true; case RETRO_ENVIRONMENT_GET_LANGUAGE: if (data == nullptr) { return false; } *static_cast(data) = RETRO_LANGUAGE_ENGLISH; return true; case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL: case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS: case RETRO_ENVIRONMENT_SET_GEOMETRY: return true; case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE: case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE: case RETRO_ENVIRONMENT_GET_LED_INTERFACE: return false; default: return false; } } static std::size_t alignUp( std::size_t value, std::size_t alignment) { if (alignment == 0) { return value; } return ( (value + alignment - 1) / alignment ) * alignment; } static std::uint32_t encodeDisplayColor( std::uint8_t red, std::uint8_t green, std::uint8_t blue) { // 0x80RRGGBB, igual que el ejemplo de graficos de openorbis return 0x80000000u | (static_cast(red) << 16) | (static_cast(green) << 8) | static_cast(blue); } static bool waitForOneFlipEvent() { OrbisKernelEvent event {}; int eventCount = 0; const int result = sceKernelWaitEqueue( g_flipQueue, &event, 1, &eventCount, 0 ); if (result < 0) { std::printf( "[NP2kai] sceKernelWaitEqueue failed: 0x%x\n", result ); return false; } return true; } static bool waitUntilDisplayBufferReusable(int bufferIndex) { if (g_videoHandle < 0 || bufferIndex < 0 || bufferIndex >= DISPLAY_BUFFER_COUNT) { return false; } const std::int64_t submittedFlipId = g_displayBufferFlipId[bufferIndex]; if (submittedFlipId < 0) { return true; } for (;;) { OrbisVideoOutFlipStatus status {}; const int statusResult = sceVideoOutGetFlipStatus( g_videoHandle, &status ); if (statusResult < 0) { std::printf( "[NP2kai] sceVideoOutGetFlipStatus failed: 0x%x\n", statusResult ); return false; } // si coinciden es que este buffer se esta mostrando ahora mismo, no // se puede pisar todavia if (status.flipArg > submittedFlipId) { return true; } if (!waitForOneFlipEvent()) { return false; } } } static bool waitUntilFlipVisible(std::int64_t flipId) { if (flipId < 0) { return true; } for (;;) { OrbisVideoOutFlipStatus status {}; const int statusResult = sceVideoOutGetFlipStatus( g_videoHandle, &status ); if (statusResult < 0) { std::printf( "[NP2kai] sceVideoOutGetFlipStatus failed: 0x%x\n", statusResult ); return false; } if (status.flipArg >= flipId) { return true; } if (!waitForOneFlipEvent()) { return false; } } } static bool submitActiveDisplayBuffer(); // --- composicion de la interfaz -------------------------------------------- // antes cada pantalla pintaba pixel a pixel directamente en la memoria de // video, y eso ahi es lento de narices. ahora se pinta todo en un buffer de // ram normal y al final se manda de una sola vez con un memcpy grande, que es // lo unico que le sienta bien a esa memoria. // ademas asi puedo repintar en cada frame sin que cueste un ojo, que es lo que // hace falta para que algo se pueda animar static constexpr std::size_t UI_CANVAS_PIXELS = static_cast(OUTPUT_WIDTH) * OUTPUT_HEIGHT; static constexpr std::size_t UI_CANVAS_BYTES = UI_CANVAS_PIXELS * sizeof(std::uint32_t); static std::uint32_t* g_uiCanvas = nullptr; static std::uint32_t* g_uiBackgroundCache = nullptr; static bool g_uiBackgroundCacheReady = false; // reloj de la interfaz. todas las animaciones se mueven con esto, asi que si // un frame tarda mas de la cuenta no se descoloca nada static float g_uiDeltaSeconds = 1.0f / 60.0f; // el foco se deslizaba pero al pulsar X no pasaba nada, asi que no habia // respuesta ninguna. esto es un golpecito: el recuadro se hincha y vuelve static float g_uiPress = 0.0f; static void uiTriggerPress() { g_uiPress = 1.0f; } static Uint32 g_uiLastFrameTick = 0; static bool g_uiClockStarted = false; static bool g_uiCanvasAttempted = false; static bool g_uiDirectMode = false; static bool ensureUiCanvas() { if (g_uiCanvas != nullptr) return true; if (g_uiCanvasAttempted) return false; g_uiCanvasAttempted = true; g_uiCanvas = static_cast(std::malloc(UI_CANVAS_BYTES)); if (g_uiCanvas == nullptr) { std::printf("[NP2kai/ui] sin memoria para el lienzo, tiro en modo directo\n"); return false; } // el cache del fondo es un extra: si no entra, se pinta a mano y ya g_uiBackgroundCache = static_cast(std::malloc(UI_CANVAS_BYTES)); g_uiBackgroundCacheReady = false; return true; } static void releaseUiCanvas() { std::free(g_uiCanvas); std::free(g_uiBackgroundCache); g_uiCanvas = nullptr; g_uiBackgroundCache = nullptr; g_uiBackgroundCacheReady = false; g_uiCanvasAttempted = false; g_uiDirectMode = false; } // devuelve el lienzo donde pintar, o nullptr si algo no esta listo static std::uint32_t* uiFrameBegin() { if (!g_directVideoReady) return nullptr; g_uiDirectMode = !ensureUiCanvas(); if (g_uiDirectMode) { // sin lienzo no hay volcado: se pinta donde se pintaba antes if (!waitUntilDisplayBufferReusable(g_activeDisplayBuffer)) return nullptr; } const Uint32 now = SDL_GetTicks(); if (!g_uiClockStarted) { g_uiDeltaSeconds = 1.0f / 60.0f; g_uiClockStarted = true; } else { const Uint32 elapsed = now - g_uiLastFrameTick; // si vengo de un menu bloqueado o de cargar un juego, el salto puede // ser enorme. lo capo para que nada pegue un tiron raro g_uiDeltaSeconds = elapsed > 100u ? 0.1f : static_cast(elapsed) / 1000.0f; if (g_uiDeltaSeconds < 0.001f) g_uiDeltaSeconds = 0.001f; } g_uiLastFrameTick = now; if (g_uiPress > 0.0f) { g_uiPress -= g_uiDeltaSeconds / 0.16f; if (g_uiPress < 0.0f) g_uiPress = 0.0f; } if (g_uiDirectMode) { return static_cast(g_displayBuffers[g_activeDisplayBuffer]); } return g_uiCanvas; } // vuelca el lienzo al buffer de video y lanza el flip static bool uiFrameSubmit() { if (g_uiDirectMode) { // ya se pinto en el buffer bueno, solo queda lanzarlo g_displayBufferCleared[g_activeDisplayBuffer] = false; return submitActiveDisplayBuffer(); } if (g_uiCanvas == nullptr) return false; if (!waitUntilDisplayBufferReusable(g_activeDisplayBuffer)) return false; void* target = g_displayBuffers[g_activeDisplayBuffer]; if (target == nullptr) return false; std::memcpy(target, g_uiCanvas, UI_CANVAS_BYTES); g_displayBufferCleared[g_activeDisplayBuffer] = false; return submitActiveDisplayBuffer(); } // --- animacion -------------------------------------------------------------- // no uso duraciones ni fotogramas contados, uso una constante de tiempo: cada // frame me acerco un porcentaje de lo que falta. sale solo la curva esa de // arrancar rapido y frenar al final, y da igual a cuantos fps vaya static float uiApproach(float current, float target, float tau) { if (!g_uiAnimationsEnabled || tau <= 0.0f) return target; const float difference = target - current; if (difference < 0.35f && difference > -0.35f) return target; // 1 - exp(-dt/tau), aproximado sin cmath para no arrastrar mas cabeceras float ratio = g_uiDeltaSeconds / tau; if (ratio > 1.0f) ratio = 1.0f; const float factor = ratio * (1.55f - 0.55f * ratio); return current + difference * factor; } // para cosas que solo van de 0 a 1 (aparecer, desvanecer) static float uiApproachUnit(float current, float target, float tau) { if (!g_uiAnimationsEnabled || tau <= 0.0f) return target; const float difference = target - current; if (difference < 0.004f && difference > -0.004f) return target; float ratio = g_uiDeltaSeconds / tau; if (ratio > 1.0f) ratio = 1.0f; const float factor = ratio * (1.55f - 0.55f * ratio); return current + difference * factor; } // tiempos. cortos a proposito: la interfaz tiene que ir por delante del dedo static constexpr float UI_TAU_FOCUS = 0.070f; // el recuadro que se desliza static constexpr float UI_TAU_SCROLL = 0.085f; // el desplazamiento de listas static constexpr float UI_TAU_SCREEN = 0.095f; // entrar y salir de pantallas // repone una franja horizontal con el fondo limpio. lo uso como recorte: pinto // la lista entera aunque se salga, y luego tapo lo que sobra por arriba y por // abajo. mas facil que meter recorte en todas las funciones de dibujo static void uiRestoreBackgroundBand(std::uint32_t* output, int y, int height) { if (output == nullptr || g_uiBackgroundCache == nullptr || height <= 0) return; if (!g_uiBackgroundCacheReady) return; if (y < 0) { height += y; y = 0; } if (y >= OUTPUT_HEIGHT || height <= 0) return; if (y + height > OUTPUT_HEIGHT) height = OUTPUT_HEIGHT - y; const std::size_t offset = static_cast(y) * OUTPUT_WIDTH; std::memcpy(output + offset, g_uiBackgroundCache + offset, static_cast(height) * OUTPUT_WIDTH * sizeof(std::uint32_t)); } static int uiRound(float value) { return static_cast(value < 0.0f ? value - 0.5f : value + 0.5f); } static bool submitActiveDisplayBuffer() { if (!g_directVideoReady || g_videoHandle < 0 || g_displayBuffers[g_activeDisplayBuffer] == nullptr) { return false; } // como mucho un frame pendiente. con mas se encolaban de golpe y // parpadeaba if (!waitUntilFlipVisible(g_lastSubmittedFlipId)) { return false; } const int submittedBuffer = g_activeDisplayBuffer; const std::int64_t flipId = g_nextFlipId++; const int submitResult = sceVideoOutSubmitFlip( g_videoHandle, submittedBuffer, ORBIS_VIDEO_OUT_FLIP_VSYNC, flipId ); if (submitResult < 0) { std::printf( "[NP2kai] sceVideoOutSubmitFlip failed: 0x%x\n", submitResult ); return false; } g_displayBufferFlipId[submittedBuffer] = flipId; g_lastSubmittedFlipId = flipId; g_activeDisplayBuffer = (g_activeDisplayBuffer + 1) % DISPLAY_BUFFER_COUNT; return true; } static void initializeColorLookupTables() { for (unsigned value = 0; value < 65536u; ++value) { const std::uint16_t pixel = static_cast(value); const std::uint8_t red565 = static_cast( ((pixel >> 11) & 0x1f) * 255 / 31 ); const std::uint8_t green565 = static_cast( ((pixel >> 5) & 0x3f) * 255 / 63 ); const std::uint8_t blue565 = static_cast( (pixel & 0x1f) * 255 / 31 ); g_rgb565Lut[value] = encodeDisplayColor( red565, green565, blue565 ); const std::uint8_t red1555 = static_cast( ((pixel >> 10) & 0x1f) * 255 / 31 ); const std::uint8_t green1555 = static_cast( ((pixel >> 5) & 0x1f) * 255 / 31 ); const std::uint8_t blue1555 = static_cast( (pixel & 0x1f) * 255 / 31 ); g_0rgb1555Lut[value] = encodeDisplayColor( red1555, green1555, blue1555 ); } } static bool ensureExpandedScanline(unsigned pixels) { if (pixels == 0) { return false; } if (pixels <= g_expandedScanlinePixels && g_expandedScanline != nullptr) { return true; } void* resized = std::realloc( g_expandedScanline, static_cast(pixels) * sizeof(std::uint32_t) ); if (resized == nullptr) { std::printf( "[NP2kai] Expanded scanline allocation failed: %u pixels\n", pixels ); return false; } g_expandedScanline = static_cast(resized); g_expandedScanlinePixels = pixels; return true; } static bool initializeDirectVideo() { initializeColorLookupTables(); const std::size_t frameBufferSize = static_cast(OUTPUT_WIDTH) * static_cast(OUTPUT_HEIGHT) * DISPLAY_BYTES_PER_PIXEL; const std::size_t requestedMemory = frameBufferSize * DISPLAY_BUFFER_COUNT; g_videoHandle = sceVideoOutOpen( ORBIS_VIDEO_USER_MAIN, ORBIS_VIDEO_OUT_BUS_MAIN, 0, nullptr ); if (g_videoHandle < 0) { std::printf( "[NP2kai] sceVideoOutOpen failed: 0x%x\n", g_videoHandle ); return false; } int result = sceKernelCreateEqueue( &g_flipQueue, "np2kai-video-flips" ); if (result < 0) { std::printf( "[NP2kai] sceKernelCreateEqueue failed: 0x%x\n", result ); return false; } result = sceVideoOutAddFlipEvent( g_flipQueue, g_videoHandle, nullptr ); if (result < 0) { std::printf( "[NP2kai] sceVideoOutAddFlipEvent failed: 0x%x\n", result ); return false; } g_directMemorySize = alignUp( requestedMemory, DIRECT_MEMORY_ALIGNMENT ); result = sceKernelAllocateDirectMemory( 0, static_cast( sceKernelGetDirectMemorySize() ), g_directMemorySize, DIRECT_MEMORY_ALIGNMENT, 3, &g_directMemoryOffset ); if (result < 0) { std::printf( "[NP2kai] sceKernelAllocateDirectMemory failed: 0x%x\n", result ); return false; } result = sceKernelMapDirectMemory( &g_directMemory, g_directMemorySize, 0x33, 0, g_directMemoryOffset, DIRECT_MEMORY_ALIGNMENT ); if (result < 0) { std::printf( "[NP2kai] sceKernelMapDirectMemory failed: 0x%x\n", result ); return false; } std::uint8_t* memory = static_cast( g_directMemory ); for (int index = 0; index < DISPLAY_BUFFER_COUNT; ++index) { g_displayBuffers[index] = memory + frameBufferSize * static_cast(index); std::memset( g_displayBuffers[index], 0, frameBufferSize ); // el negro de verdad aqui es 0x80000000, no 0. si lo dejo a 0 salen // los bordes raros g_displayBufferCleared[index] = false; } sceVideoOutSetBufferAttribute( &g_videoAttribute, 0x80000000, 1, 0, OUTPUT_WIDTH, OUTPUT_HEIGHT, OUTPUT_WIDTH ); result = sceVideoOutRegisterBuffers( g_videoHandle, 0, g_displayBuffers, DISPLAY_BUFFER_COUNT, &g_videoAttribute ); if (result < 0) { std::printf( "[NP2kai] sceVideoOutRegisterBuffers failed: 0x%x\n", result ); return false; } result = sceVideoOutSetFlipRate( g_videoHandle, 0 ); if (result < 0) { std::printf( "[NP2kai] sceVideoOutSetFlipRate failed: 0x%x\n", result ); return false; } g_directVideoReady = true; std::printf( "[NP2kai] Native sceVideoOut ready: " "%dx%d, %d buffers, %zu bytes direct memory.\n", OUTPUT_WIDTH, OUTPUT_HEIGHT, DISPLAY_BUFFER_COUNT, g_directMemorySize ); return true; } static void shutdownDirectVideo() { releaseUiCanvas(); if (g_videoHandle >= 0) { sceVideoOutUnregisterBuffers( g_videoHandle, 0 ); sceVideoOutClose( g_videoHandle ); g_videoHandle = -1; } if (g_flipQueue) { sceKernelDeleteEqueue( g_flipQueue ); g_flipQueue = {}; } if (g_directMemorySize != 0) { sceKernelReleaseDirectMemory( g_directMemoryOffset, g_directMemorySize ); } g_directMemory = nullptr; g_directMemoryOffset = 0; g_directMemorySize = 0; for (int index = 0; index < DISPLAY_BUFFER_COUNT; ++index) { g_displayBuffers[index] = nullptr; g_displayBufferCleared[index] = false; g_displayBufferFlipId[index] = -1; } g_lastSubmittedFlipId = -1; g_directVideoReady = false; } static void fillRectangle( std::uint32_t* output, int x, int y, int width, int height, std::uint32_t color) { if (output == nullptr || width <= 0 || height <= 0) { return; } const int left = x < 0 ? 0 : x; const int top = y < 0 ? 0 : y; const int right = x + width > OUTPUT_WIDTH ? OUTPUT_WIDTH : x + width; const int bottom = y + height > OUTPUT_HEIGHT ? OUTPUT_HEIGHT : y + height; if (left >= right || top >= bottom) { return; } for (int row = top; row < bottom; ++row) { std::uint32_t* destination = output + static_cast(row) * OUTPUT_WIDTH + left; for (int column = left; column < right; ++column) { *destination++ = color; } } } static void showSolidScreen( std::uint8_t red, std::uint8_t green, std::uint8_t blue) { if (!g_directVideoReady) { return; } if (!waitUntilDisplayBufferReusable( g_activeDisplayBuffer )) { return; } std::uint32_t* output = static_cast( g_displayBuffers[ g_activeDisplayBuffer ] ); const std::uint32_t color = encodeDisplayColor( red, green, blue ); fillRectangle( output, 0, 0, OUTPUT_WIDTH, OUTPUT_HEIGHT, color ); g_displayBufferCleared[ g_activeDisplayBuffer ] = color == 0x80000000u; submitActiveDisplayBuffer(); } static void videoCallback( const void* data, unsigned width, unsigned height, std::size_t pitch) { ++g_videoCallbacks; // si viene null es que quiere repetir el frame anterior if (data == nullptr || width == 0 || height == 0 || pitch == 0) { return; } if (height > static_cast( static_cast(-1) / pitch )) { std::printf( "[NP2kai] Invalid framebuffer size: " "%ux%u pitch=%zu\n", width, height, pitch ); g_running = false; return; } const std::size_t required = pitch * static_cast(height); if (required > g_frameCapacity) { void* resized = std::realloc( g_frameBuffer, required ); if (resized == nullptr) { std::printf( "[NP2kai] Framebuffer allocation failed: " "%zu bytes\n", required ); g_running = false; return; } g_frameBuffer = static_cast( resized ); g_frameCapacity = required; } std::memcpy( g_frameBuffer, data, required ); g_frameSize = required; g_framePitch = pitch; g_frameWidth = width; g_frameHeight = height; g_framePixelFormat = g_pixelFormat; g_frameReady = true; ++g_validFrames; if (g_validFrames == 1) { std::printf( "[NP2kai] FIRST COPIED FRAME: " "%ux%u pitch=%zu format=%d\n", width, height, pitch, static_cast( g_framePixelFormat ) ); } } static void drawSevenSegmentDigit( std::uint32_t* output, int digit, int x, int y, int scale, std::uint32_t color) { static const unsigned char segments[10] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f }; if (output == nullptr || digit < 0 || digit > 9) { return; } const int thickness = scale; const int horizontal = 5 * scale; const int vertical = 5 * scale; const unsigned char mask = segments[digit]; const int rectangles[7][4] = { { x + thickness, y, horizontal, thickness }, { x + horizontal + thickness, y + thickness, thickness, vertical }, { x + horizontal + thickness, y + vertical + 2 * thickness, thickness, vertical }, { x + thickness, y + 2 * vertical + 2 * thickness, horizontal, thickness }, { x, y + vertical + 2 * thickness, thickness, vertical }, { x, y + thickness, thickness, vertical }, { x + thickness, y + vertical + thickness, horizontal, thickness } }; for (int segment = 0; segment < 7; ++segment) { if ((mask & (1u << segment)) == 0) { continue; } fillRectangle( output, rectangles[segment][0], rectangles[segment][1], rectangles[segment][2], rectangles[segment][3], color ); } } static void drawProfilerNumber( std::uint32_t* output, double value, int x, int y, int scale, std::uint32_t color) { if (value < 0.0) { value = 0.0; } if (value > 999.9) { value = 999.9; } const int encoded = static_cast( value * 10.0 + 0.5 ); const int hundreds = (encoded / 1000) % 10; const int tens = (encoded / 100) % 10; const int units = (encoded / 10) % 10; const int decimal = encoded % 10; const int digitAdvance = 8 * scale; int cursor = x; if (hundreds != 0) { drawSevenSegmentDigit( output, hundreds, cursor, y, scale, color ); cursor += digitAdvance; } if (hundreds != 0 || tens != 0) { drawSevenSegmentDigit( output, tens, cursor, y, scale, color ); cursor += digitAdvance; } drawSevenSegmentDigit( output, units, cursor, y, scale, color ); cursor += digitAdvance; fillRectangle( output, cursor, y + 11 * scale, scale, scale, color ); cursor += 3 * scale; drawSevenSegmentDigit( output, decimal, cursor, y, scale, color ); } static void drawUnsignedInteger( std::uint32_t* output, unsigned value, int minimumDigits, int x, int y, int scale, std::uint32_t color) { char digits[16] {}; std::snprintf( digits, sizeof(digits), "%u", value ); const int length = static_cast( std::strlen(digits) ); const int digitAdvance = 8 * scale; int cursor = x; for (int padding = length; padding < minimumDigits; ++padding) { drawSevenSegmentDigit( output, 0, cursor, y, scale, color ); cursor += digitAdvance; } for (int index = 0; index < length; ++index) { const int digit = digits[index] - '0'; if (digit < 0 || digit > 9) { continue; } drawSevenSegmentDigit( output, digit, cursor, y, scale, color ); cursor += digitAdvance; } } static bool padButtonPressed(std::uint32_t mask) { return g_padConnected && (g_padData.buttons & mask) != 0; } static void drawPadProbeButton( std::uint32_t* output, int x, int y, int width, int height, bool pressed, std::uint32_t activeColor) { fillRectangle( output, x, y, width, height, encodeDisplayColor(55, 55, 55) ); fillRectangle( output, x + 3, y + 3, width - 6, height - 6, pressed ? activeColor : encodeDisplayColor(12, 12, 12) ); } static void drawPadStickProbe( std::uint32_t* output, int x, int y, std::uint8_t stickX, std::uint8_t stickY, std::uint32_t accent) { static constexpr int BOX_SIZE = 88; fillRectangle( output, x, y, BOX_SIZE, BOX_SIZE, encodeDisplayColor(45, 45, 45) ); fillRectangle( output, x + 3, y + 3, BOX_SIZE - 6, BOX_SIZE - 6, encodeDisplayColor(5, 5, 5) ); fillRectangle( output, x + BOX_SIZE / 2, y + 4, 1, BOX_SIZE - 8, encodeDisplayColor(65, 65, 65) ); fillRectangle( output, x + 4, y + BOX_SIZE / 2, BOX_SIZE - 8, 1, encodeDisplayColor(65, 65, 65) ); const int markerX = x + 5 + static_cast(stickX) * (BOX_SIZE - 14) / 255; const int markerY = y + 5 + static_cast(stickY) * (BOX_SIZE - 14) / 255; fillRectangle( output, markerX, markerY, 9, 9, accent ); } static void drawPadProbeOverlay( std::uint32_t* output) { if (output == nullptr) { return; } // 640x400 escalado x2 deja 320 px de borde a cada lado en 1080p. pongo el // diagnostico abajo a la derecha para no tapar el juego ni el fps de goldhen const int panelX = OUTPUT_WIDTH - 284 - 18; const int panelY = 760; const int panelWidth = 284; const int panelHeight = 300; fillRectangle( output, panelX, panelY, panelWidth, panelHeight, encodeDisplayColor(0, 0, 0) ); const std::uint32_t connectedColor = g_padConnected ? encodeDisplayColor(55, 230, 95) : ( g_padSubsystemReady ? encodeDisplayColor(235, 180, 35) : encodeDisplayColor(225, 45, 45) ); fillRectangle( output, panelX + 8, panelY + 8, 12, panelHeight - 16, connectedColor ); const unsigned audioFailure = g_audioFailureStage.load(std::memory_order_acquire); std::uint32_t audioColor = encodeDisplayColor(225, 45, 45); if (g_audioDeviceAlive.load(std::memory_order_acquire)) { audioColor = g_audioPlaybackStarted.load(std::memory_order_acquire) ? encodeDisplayColor(65, 220, 235) : encodeDisplayColor(235, 180, 35); } else if (audioFailure == 2u) { audioColor = encodeDisplayColor(235, 65, 210); } else if (audioFailure == 3u) { audioColor = encodeDisplayColor(245, 135, 35); } else if (audioFailure == 4u) { audioColor = encodeDisplayColor(150, 75, 235); } else if (audioFailure == 5u) { audioColor = encodeDisplayColor(245, 245, 245); } fillRectangle( output, panelX + 23, panelY + 8, 5, panelHeight - 16, audioColor ); // si el audio falla pinto de 1 a 5 cuadraditos segun donde se rompio, asi // con una captura ya se por donde va for (unsigned blockIndex = 0; blockIndex < audioFailure && blockIndex < 5u; ++blockIndex) { fillRectangle( output, panelX + 31 + static_cast(blockIndex) * 7, panelY + panelHeight - 13, 5, 5, encodeDisplayColor(245, 245, 245) ); } const std::uint32_t leftAccent = encodeDisplayColor(65, 220, 235); const std::uint32_t rightAccent = encodeDisplayColor(210, 90, 245); const std::uint32_t active = encodeDisplayColor(75, 235, 105); drawPadStickProbe( output, panelX + 32, panelY + 18, g_padData.leftStick.x, g_padData.leftStick.y, leftAccent ); drawPadStickProbe( output, panelX + 164, panelY + 18, g_padData.rightStick.x, g_padData.rightStick.y, rightAccent ); drawUnsignedInteger( output, g_padData.leftStick.x, 3, panelX + 32, panelY + 116, 1, leftAccent ); drawUnsignedInteger( output, g_padData.leftStick.y, 3, panelX + 80, panelY + 116, 1, leftAccent ); drawUnsignedInteger( output, g_padData.rightStick.x, 3, panelX + 164, panelY + 116, 1, rightAccent ); drawUnsignedInteger( output, g_padData.rightStick.y, 3, panelX + 212, panelY + 116, 1, rightAccent ); // D-pad cluster. drawPadProbeButton( output, panelX + 58, panelY + 151, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_UP), active ); drawPadProbeButton( output, panelX + 58, panelY + 203, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_DOWN), active ); drawPadProbeButton( output, panelX + 32, panelY + 177, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_LEFT), active ); drawPadProbeButton( output, panelX + 84, panelY + 177, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_RIGHT), active ); // Face-button cluster, placed like the physical DS4. drawPadProbeButton( output, panelX + 204, panelY + 151, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_TRIANGLE), active ); drawPadProbeButton( output, panelX + 230, panelY + 177, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_CIRCLE), active ); drawPadProbeButton( output, panelX + 204, panelY + 203, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_CROSS), active ); drawPadProbeButton( output, panelX + 178, panelY + 177, 24, 24, padButtonPressed(ORBIS_PAD_BUTTON_SQUARE), active ); // L1, R1, Options and touchpad click. drawPadProbeButton( output, panelX + 32, panelY + 249, 42, 18, padButtonPressed(ORBIS_PAD_BUTTON_L1), active ); drawPadProbeButton( output, panelX + 80, panelY + 249, 42, 18, padButtonPressed(ORBIS_PAD_BUTTON_R1), active ); drawPadProbeButton( output, panelX + 128, panelY + 249, 42, 18, padButtonPressed(ORBIS_PAD_BUTTON_OPTIONS), active ); drawPadProbeButton( output, panelX + 176, panelY + 249, 78, 18, padButtonPressed(ORBIS_PAD_BUTTON_TOUCH_PAD), active ); drawUnsignedInteger( output, static_cast( g_padData.buttons & 0xffffu ), 1, panelX + 32, panelY + 277, 1, connectedColor ); } static void drawPerformanceOverlay( std::uint32_t* output) { if (output == nullptr) { return; } const double target = g_targetFps > 1.0 ? g_targetFps : 56.4; double ratio = g_recentEmulationFps / target; if (ratio < 0.0) { ratio = 0.0; } else if (ratio > 1.0) { ratio = 1.0; } const std::uint32_t barBackground = encodeDisplayColor(42, 42, 42); std::uint32_t barColor = encodeDisplayColor(50, 205, 70); if (ratio < 0.5) { barColor = encodeDisplayColor(220, 45, 45); } else if (ratio < 0.9) { barColor = encodeDisplayColor(225, 175, 35); } fillRectangle( output, 0, 0, OUTPUT_WIDTH, 10, barBackground ); fillRectangle( output, 0, 0, static_cast( ratio * OUTPUT_WIDTH ), 10, barColor ); const int panelWidth = 310; const int panelHeight = 126; const int panelX = OUTPUT_WIDTH - panelWidth - 18; const int panelY = 18; const int scale = 2; fillRectangle( output, panelX, panelY, panelWidth, panelHeight, encodeDisplayColor(0, 0, 0) ); // cian = el pipeline de un frame esta funcionando const std::uint32_t modeIndicator = encodeDisplayColor(65, 220, 235); fillRectangle( output, panelX + 12, panelY + 12, 16, panelHeight - 24, modeIndicator ); // rojo tiempo del core, azul lo que tarda en pintar, verde retro_run por // segundo real drawProfilerNumber( output, g_recentRetroRunMs, panelX + 48, panelY + 8, scale, encodeDisplayColor(245, 55, 55) ); drawProfilerNumber( output, g_recentPresentMs, panelX + 48, panelY + 46, scale, encodeDisplayColor(65, 145, 255) ); drawProfilerNumber( output, g_recentEmulationFps, panelX + 48, panelY + 84, scale, encodeDisplayColor(70, 235, 90) ); } static void drawTinyText( std::uint32_t* output, int x, int y, int scale, std::uint32_t color, const char* textValue, unsigned maxCharacters); static std::uint32_t audioRingCount(); static void drawAudioStatusOverlay( std::uint32_t* output) { if (output == nullptr) { return; } const int panelX = 20; const int panelY = 720; const int panelWidth = 200; const int panelHeight = 160; const std::uint32_t black = encodeDisplayColor(0, 0, 0); const std::uint32_t white = encodeDisplayColor(238, 243, 250); const std::uint32_t muted = encodeDisplayColor(168, 180, 202); fillRectangle( output, panelX, panelY, panelWidth, panelHeight, black ); std::uint32_t stateColor = encodeDisplayColor(230, 65, 65); const char* stateText = "ERROR"; if (!g_audioEnabled) { stateColor = encodeDisplayColor(145, 145, 155); stateText = "OFF"; } else if (g_audioDeviceAlive.load(std::memory_order_acquire)) { if (g_audioPlaybackStarted.load(std::memory_order_acquire)) { stateColor = encodeDisplayColor(65, 215, 225); stateText = "PLAY"; } else { stateColor = encodeDisplayColor(235, 185, 55); stateText = "BUFFER"; } } fillRectangle(output, panelX + 12, panelY + 12, 14, 136, stateColor); drawTinyText(output, panelX + 42, panelY + 16, 2, white, "AUDIO", 0); drawTinyText(output, panelX + 42, panelY + 46, 2, stateColor, stateText, 0); char queueText[48] {}; char underrunText[48] {}; char overrunText[48] {}; std::snprintf(queueText, sizeof(queueText), "Q %u", audioRingCount()); std::snprintf( underrunText, sizeof(underrunText), "U %u", g_audioUnderruns.load(std::memory_order_relaxed) ); std::snprintf( overrunText, sizeof(overrunText), "O %u", g_audioOverruns.load(std::memory_order_relaxed) ); drawTinyText(output, panelX + 42, panelY + 78, 2, muted, queueText, 0); drawTinyText(output, panelX + 42, panelY + 106, 2, muted, underrunText, 0); drawTinyText(output, panelX + 42, panelY + 134, 2, muted, overrunText, 0); } static bool blitLatestFrame( std::uint32_t* output) { if (output == nullptr || g_frameBuffer == nullptr || g_frameSize == 0 || g_frameWidth == 0 || g_frameHeight == 0 || g_framePitch == 0) { return false; } const unsigned bytesPerPixel = g_framePixelFormat == RETRO_PIXEL_FORMAT_XRGB8888 ? 4u : 2u; if (g_framePitch < static_cast( g_frameWidth ) * bytesPerPixel) { std::printf( "[NP2kai] Invalid source pitch: " "%zu for %ux%u format=%d\n", g_framePitch, g_frameWidth, g_frameHeight, static_cast( g_framePixelFormat ) ); return false; } unsigned destinationWidth = OUTPUT_HEIGHT * 4u / 3u; unsigned destinationHeight = OUTPUT_HEIGHT; int destinationX = (OUTPUT_WIDTH - static_cast(destinationWidth)) / 2; int destinationY = 0; if (g_displayMode == DisplayMode::Integer2x) { destinationWidth = g_frameWidth * 2u; destinationHeight = g_frameHeight * 2u; if (destinationWidth > static_cast(OUTPUT_WIDTH) || destinationHeight > static_cast(OUTPUT_HEIGHT)) { destinationWidth = OUTPUT_HEIGHT * 4u / 3u; destinationHeight = OUTPUT_HEIGHT; } destinationX = (OUTPUT_WIDTH - static_cast(destinationWidth)) / 2; destinationY = (OUTPUT_HEIGHT - static_cast(destinationHeight)) / 2; } else if (g_displayMode == DisplayMode::Stretch16x9) { destinationWidth = OUTPUT_WIDTH; destinationHeight = OUTPUT_HEIGHT; destinationX = 0; destinationY = 0; } if (!ensureExpandedScanline(destinationWidth)) { return false; } if (g_scaleSourceWidth != g_frameWidth || g_scaleDestinationWidth != destinationWidth) { for (unsigned destinationPixel = 0; destinationPixel < destinationWidth; ++destinationPixel) { g_scaleSourceX[destinationPixel] = static_cast( static_cast(destinationPixel) * g_frameWidth / destinationWidth ); } g_scaleSourceWidth = g_frameWidth; g_scaleDestinationWidth = destinationWidth; } // el menu pinta los tres buffers, asi que al entrar a un juego limpio cada // uno una vez o los bordes se quedan con restos if (!g_displayBufferCleared[g_activeDisplayBuffer]) { fillRectangle( output, 0, 0, OUTPUT_WIDTH, OUTPUT_HEIGHT, encodeDisplayColor(0, 0, 0) ); g_displayBufferCleared[g_activeDisplayBuffer] = true; } unsigned cachedSourceY = static_cast(-1); for (unsigned destinationRowIndex = 0; destinationRowIndex < destinationHeight; ++destinationRowIndex) { const unsigned sourceY = static_cast( static_cast(destinationRowIndex) * g_frameHeight / destinationHeight ); if (sourceY != cachedSourceY) { const std::uint8_t* sourceRow = g_frameBuffer + static_cast(sourceY) * g_framePitch; if (g_framePixelFormat == RETRO_PIXEL_FORMAT_XRGB8888) { const std::uint32_t* pixels = reinterpret_cast(sourceRow); for (unsigned x = 0; x < destinationWidth; ++x) { g_expandedScanline[x] = 0x80000000u | (pixels[g_scaleSourceX[x]] & 0x00ffffffu); } } else { const std::uint16_t* pixels = reinterpret_cast(sourceRow); const std::uint32_t* lookup = g_framePixelFormat == RETRO_PIXEL_FORMAT_RGB565 ? g_rgb565Lut : g_0rgb1555Lut; for (unsigned x = 0; x < destinationWidth; ++x) { g_expandedScanline[x] = lookup[pixels[g_scaleSourceX[x]]]; } } cachedSourceY = sourceY; } std::uint32_t* destinationRow = output + static_cast( destinationY + static_cast(destinationRowIndex) ) * OUTPUT_WIDTH + destinationX; std::memcpy( destinationRow, g_expandedScanline, static_cast(destinationWidth) * sizeof(std::uint32_t) ); } return true; } // escribe una linea por segundo en un archivo aparte, para poder sacarlo por // ftp y compararlo entre versiones. el klog se pierde al reiniciar y ademas no // se puede pegar en ningun sitio; esto si static void writePerfSample(unsigned long long emulated, unsigned long long presented) { std::printf("[NP2kai/perf] emu_fps=%.2f video_fps=%.2f run=%.2f espera=%.2f " "convierte=%.2f flip=%.2f hueco=%.2f peor_run=%.2f tarde=%u cortes=%u\n", g_recentEmulationFps, g_recentPresentFps, g_recentRetroRunMs, g_msWaitBuffer, g_msConvert, g_msSubmit, g_msSlack, g_msWorstRun, g_lateFrames, g_audioCutsPerSecond); if (g_perfLog == nullptr) { g_perfLog = std::fopen("/data/np2kai-runtime/logs/perf.log", "a"); if (g_perfLog == nullptr) return; std::fprintf(g_perfLog, "# juego\tsegundo\temu_fps\tvideo_fps\trun_ms\tespera_ms\t" "convierte_ms\tflip_ms\thueco_ms\tpeor_run_ms\ttarde\tcola\tcortes_s\n"); } // el segundo de sesion, para poder ver la forma de la curva y no solo el // ultimo valor. no cierro el archivo en cada linea: con fflush basta y // abrir y cerrar cada segundo dentro del bucle de juego daba tirones static unsigned second = 0; ++second; std::fprintf(g_perfLog, "%s\t%u\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%u\t%u\t%u\n", g_activeGameName[0] != '\0' ? g_activeGameName : "sin-juego", second, g_recentEmulationFps, g_recentPresentFps, g_recentRetroRunMs, g_msWaitBuffer, g_msConvert, g_msSubmit, g_msSlack, g_msWorstRun, g_lateFrames, audioRingCount(), g_audioCutsPerSecond); std::fflush(g_perfLog); (void)emulated; (void)presented; } static void closePerfLog() { if (g_perfLog != nullptr) { std::fclose(g_perfLog); g_perfLog = nullptr; } } static bool presentLatestFrame() { if (!g_frameReady) { return true; } if (!g_directVideoReady) { return false; } const Uint64 waitStart = SDL_GetPerformanceCounter(); if (!waitUntilDisplayBufferReusable( g_activeDisplayBuffer )) { return false; } const Uint64 waitEnd = SDL_GetPerformanceCounter(); g_tickWaitBuffer += waitEnd - waitStart; std::uint32_t* output = static_cast( g_displayBuffers[ g_activeDisplayBuffer ] ); if (!blitLatestFrame(output)) { showSolidScreen(130, 20, 30); return false; } g_tickConvert += SDL_GetPerformanceCounter() - waitEnd; if (g_showPerformanceOverlay) { drawPerformanceOverlay(output); } if (g_showPadProbeOverlay) { drawPadProbeOverlay(output); } if (g_showAudioOverlay) { drawAudioStatusOverlay(output); } const Uint64 submitStart = SDL_GetPerformanceCounter(); if (!submitActiveDisplayBuffer()) { return false; } g_tickSubmit += SDL_GetPerformanceCounter() - submitStart; g_frameReady = false; return true; } static std::uint32_t audioRingCount() { const std::uint32_t readIndex = g_audioReadIndex.load(std::memory_order_acquire); const std::uint32_t writeIndex = g_audioWriteIndex.load(std::memory_order_acquire); return (writeIndex - readIndex) & AUDIO_RING_MASK; } static bool enqueueAudioFrame(const AudioStereoFrame& frame) { const std::uint32_t writeIndex = g_audioWriteIndex.load(std::memory_order_relaxed); const std::uint32_t nextWrite = (writeIndex + 1u) & AUDIO_RING_MASK; const std::uint32_t readIndex = g_audioReadIndex.load(std::memory_order_acquire); if (nextWrite == readIndex) { g_audioOverruns.fetch_add(1u, std::memory_order_relaxed); return false; } g_audioRing[writeIndex] = frame; g_audioWriteIndex.store( nextWrite, std::memory_order_release ); return true; } static std::uint32_t dequeueAudioFrames( AudioStereoFrame* destination, std::uint32_t requested) { if (destination == nullptr || requested == 0) { return 0; } std::uint32_t readIndex = g_audioReadIndex.load(std::memory_order_relaxed); const std::uint32_t writeIndex = g_audioWriteIndex.load(std::memory_order_acquire); const std::uint32_t available = (writeIndex - readIndex) & AUDIO_RING_MASK; const std::uint32_t count = available < requested ? available : requested; for (std::uint32_t index = 0; index < count; ++index) { destination[index] = g_audioRing[readIndex]; readIndex = (readIndex + 1u) & AUDIO_RING_MASK; } g_audioReadIndex.store( readIndex, std::memory_order_release ); return count; } static int audioOutputThread(void*) { AudioStereoFrame block[AUDIO_BLOCK_FRAMES] {}; while (g_audioThreadRun.load(std::memory_order_acquire)) { if (g_audioPaused.load(std::memory_order_acquire)) { std::memset(block, 0, sizeof(block)); sceAudioOutOutput(g_audioHandle, nullptr); sceAudioOutOutput(g_audioHandle, block); continue; } bool started = g_audioPlaybackStarted.load(std::memory_order_acquire); if (!started && audioRingCount() >= AUDIO_PREBUFFER_FRAMES) { started = true; g_audioPlaybackStarted.store( true, std::memory_order_release ); std::printf( "[NP2kai/audio] prebuffer complete: %u frames.\n", audioRingCount() ); } std::uint32_t copied = 0; if (started) { copied = dequeueAudioFrames( block, AUDIO_BLOCK_FRAMES ); } if (copied < AUDIO_BLOCK_FRAMES) { std::memset( block + copied, 0, static_cast( AUDIO_BLOCK_FRAMES - copied ) * sizeof(AudioStereoFrame) ); if (started) { g_audioUnderruns.fetch_add( 1u, std::memory_order_relaxed ); } } // igual que el ejemplo audio-wav de openorbis: esperar el bloque // anterior y mandar el siguiente const int waitResult = sceAudioOutOutput( g_audioHandle, nullptr ); if (waitResult < 0) { std::printf( "[NP2kai/audio] sceAudioOutOutput(wait) failed: 0x%x\n", waitResult ); g_audioFailureStage.store(5u, std::memory_order_release); g_audioDeviceAlive.store( false, std::memory_order_release ); break; } const int outputResult = sceAudioOutOutput( g_audioHandle, block ); if (outputResult < 0) { std::printf( "[NP2kai/audio] sceAudioOutOutput failed: 0x%x\n", outputResult ); g_audioFailureStage.store(5u, std::memory_order_release); g_audioDeviceAlive.store( false, std::memory_order_release ); break; } } return 0; } static bool initializeAudioOutput(double sourceRate) { g_audioFailureStage.store(0u, std::memory_order_relaxed); if (g_padUserId == ORBIS_USER_SERVICE_USER_ID_INVALID) { std::printf( "[NP2kai/audio] No valid PS4 user; audio unavailable.\n" ); g_audioFailureStage.store(1u, std::memory_order_release); return false; } g_audioSourceRate = sourceRate >= 8000.0 && sourceRate <= 192000.0 ? static_cast(sourceRate + 0.5) : AUDIO_DEFAULT_SOURCE_RATE; g_audioReadIndex.store(0, std::memory_order_relaxed); g_audioWriteIndex.store(0, std::memory_order_relaxed); g_audioPlaybackStarted.store(false, std::memory_order_relaxed); g_audioUnderruns.store(0, std::memory_order_relaxed); g_audioOverruns.store(0, std::memory_order_relaxed); g_audioResamplePhase = 0; g_audioHavePreviousFrame = false; if (!g_audioSubsystemInitialized) { const int initResult = sceAudioOutInit(); if (initResult < 0) { std::printf( "[NP2kai/audio] sceAudioOutInit failed: 0x%x\n", initResult ); g_audioFailureStage.store(2u, std::memory_order_release); return false; } g_audioSubsystemInitialized = true; } g_audioHandle = sceAudioOutOpen( ORBIS_USER_SERVICE_USER_ID_SYSTEM, ORBIS_AUDIO_OUT_PORT_TYPE_MAIN, 0, AUDIO_BLOCK_FRAMES, AUDIO_OUTPUT_RATE, ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO ); if (g_audioHandle <= 0) { std::printf( "[NP2kai/audio] sceAudioOutOpen failed: 0x%x\n", g_audioHandle ); g_audioFailureStage.store(3u, std::memory_order_release); return false; } g_audioThreadRun.store(true, std::memory_order_release); g_audioDeviceAlive.store(true, std::memory_order_release); g_audioThread = SDL_CreateThread( audioOutputThread, "np2kai-audio", nullptr ); if (g_audioThread == nullptr) { std::printf( "[NP2kai/audio] SDL_CreateThread failed: %s\n", SDL_GetError() ); g_audioThreadRun.store(false, std::memory_order_release); g_audioFailureStage.store(4u, std::memory_order_release); g_audioDeviceAlive.store(false, std::memory_order_release); sceAudioOutClose(g_audioHandle); g_audioHandle = -1; return false; } std::printf( "[NP2kai/audio] ready: source=%u Hz output=%u Hz " "block=%u prebuffer=%u ring=%u handle=0x%x\n", g_audioSourceRate, AUDIO_OUTPUT_RATE, AUDIO_BLOCK_FRAMES, AUDIO_PREBUFFER_FRAMES, AUDIO_RING_FRAMES, g_audioHandle ); return true; } static void shutdownAudioOutput() { g_audioThreadRun.store(false, std::memory_order_release); if (g_audioThread != nullptr) { SDL_WaitThread(g_audioThread, nullptr); g_audioThread = nullptr; } g_audioDeviceAlive.store(false, std::memory_order_release); if (g_audioHandle >= 0) { sceAudioOutClose(g_audioHandle); g_audioHandle = -1; } } static void submitCoreAudioFrame( std::int16_t left, std::int16_t right) { if (!g_audioEnabled || g_audioPaused.load(std::memory_order_acquire) || !g_audioDeviceAlive.load(std::memory_order_acquire)) { return; } const std::int32_t scaledLeft = static_cast(left) * g_audioVolumePercent / 100; const std::int32_t scaledRight = static_cast(right) * g_audioVolumePercent / 100; const AudioStereoFrame current { static_cast(scaledLeft), static_cast(scaledRight) }; if (!g_audioHavePreviousFrame) { g_audioPreviousFrame = current; g_audioHavePreviousFrame = true; return; } std::uint32_t phase = g_audioResamplePhase; while (phase < AUDIO_OUTPUT_RATE) { const std::int64_t leftDifference = static_cast(current.left) - static_cast(g_audioPreviousFrame.left); const std::int64_t rightDifference = static_cast(current.right) - static_cast(g_audioPreviousFrame.right); AudioStereoFrame outputFrame {}; outputFrame.left = static_cast( static_cast(g_audioPreviousFrame.left) + leftDifference * phase / AUDIO_OUTPUT_RATE ); outputFrame.right = static_cast( static_cast(g_audioPreviousFrame.right) + rightDifference * phase / AUDIO_OUTPUT_RATE ); enqueueAudioFrame(outputFrame); phase += g_audioSourceRate; } phase -= AUDIO_OUTPUT_RATE; g_audioResamplePhase = phase; g_audioPreviousFrame = current; } static void audioSampleCallback( std::int16_t left, std::int16_t right) { submitCoreAudioFrame(left, right); } static std::size_t audioBatchCallback( const std::int16_t* data, std::size_t frames) { if (data == nullptr) { return frames; } for (std::size_t index = 0; index < frames; ++index) { submitCoreAudioFrame( data[index * 2u], data[index * 2u + 1u] ); } return frames; } static void setActiveInputLayout(InputLayout layout) { if (g_activeInputLayout == layout) return; g_activeInputLayout = layout; frontendLog("Input layout changed: %s", layout == InputLayout::Keyboard ? "keyboard" : "controller"); } static std::uint32_t keyboardUiMaskForRetroKey(unsigned keycode) { switch (keycode) { case RETROK_UP: case RETROK_w: return ORBIS_PAD_BUTTON_UP; case RETROK_DOWN: case RETROK_s: return ORBIS_PAD_BUTTON_DOWN; case RETROK_LEFT: case RETROK_a: return ORBIS_PAD_BUTTON_LEFT; case RETROK_RIGHT: case RETROK_d: return ORBIS_PAD_BUTTON_RIGHT; case RETROK_RETURN: case RETROK_KP_ENTER: case RETROK_SPACE: case RETROK_z: return ORBIS_PAD_BUTTON_CROSS; case RETROK_ESCAPE: case RETROK_BACKSPACE: case RETROK_x: return ORBIS_PAD_BUTTON_CIRCLE; case RETROK_c: return ORBIS_PAD_BUTTON_SQUARE; case RETROK_F5: case RETROK_r: return ORBIS_PAD_BUTTON_TRIANGLE; case RETROK_q: case RETROK_PAGEUP: return ORBIS_PAD_BUTTON_L1; case RETROK_e: case RETROK_PAGEDOWN: return ORBIS_PAD_BUTTON_R1; case RETROK_TAB: case RETROK_F10: return ORBIS_PAD_BUTTON_OPTIONS; default: return 0; } } static std::uint32_t frontendUiButtons() { const std::uint32_t controllerButtons = g_padConnected ? g_padData.buttons : 0; return controllerButtons | g_keyboardUiButtons; } static bool frontendInputAvailable() { return g_padConnected || g_externalKeyboardDetected; } static bool frontendPauseShortcutHeld() { const bool controllerPause = g_padConnected && (g_padData.buttons & ORBIS_PAD_BUTTON_L3) != 0 && (g_padData.buttons & ORBIS_PAD_BUTTON_R3) != 0; return controllerPause || g_keyboardPauseHeld; } static void tryOpenNativeKeyboardBackend(bool forceRetry = false) { if (g_nativeKeyboardOpened) return; // SDL_GetTicks() no sirve hasta que SDL arranca, asi que el primer intento // va a pelo y el temporizador entra despues const Uint32 now = g_sdlTimerReady ? SDL_GetTicks() : 0; if (!forceRetry && ps4KeyboardOpenAttempts() != 0 && now != 0 && now < g_nativeKeyboardNextRetryTick) { return; } // el teclado necesita un usuario valido igual que el mando y el audio. // esto ya no le pisa el arranque al mando (ver ensureUserServiceReady) if (!ensureUserServiceReady()) { if (now != 0) g_nativeKeyboardNextRetryTick = now + NATIVE_KEYBOARD_RETRY_MS; return; } g_nativeKeyboardOpened = ps4KeyboardOpenDevice(g_padUserId); if (!g_nativeKeyboardOpened && now != 0) { g_nativeKeyboardNextRetryTick = now + NATIVE_KEYBOARD_RETRY_MS; } } static void initializeExternalKeyboardInput() { // todo el lio de abrir el teclado esta en ps4_keyboard.cpp tryOpenNativeKeyboardBackend(); // el text input de SDL se queda encendido como respaldo. no consta que // llegue a dispararse en ps4, pero no cuesta nada if (g_externalKeyboardEventSubsystemReady && !g_keyboardTextInputStarted) { SDL_StartTextInput(); g_keyboardTextInputStarted = true; std::printf( "[NP2kai/keyboard] SDL text input started native_open=%d\n", g_nativeKeyboardOpened ? 1 : 0); } } static void shutdownExternalKeyboardInput() { if (g_keyboardTextInputStarted) { SDL_StopTextInput(); g_keyboardTextInputStarted = false; } ps4KeyboardCloseDevice(); g_nativeKeyboardOpened = false; g_keyboardUiButtons = 0; g_keyboardUiPulseButtons = 0; g_keyboardPauseHeld = false; } static void updatePadState() { if (!g_padSubsystemReady || g_padHandle < 0) { g_padConnected = false; return; } OrbisPadData nextState {}; const int result = scePadReadState( g_padHandle, &nextState ); g_padLastReadResult = result; if (result < 0) { g_padConnected = false; return; } g_padData = nextState; g_padConnected = g_padData.connected != 0; const std::uint32_t newlyPressed = g_padData.buttons & ~g_previousPadButtons; if (g_padConnected && newlyPressed != 0) { setActiveInputLayout(InputLayout::Controller); } if (g_padData.buttons != g_previousPadButtons) { std::printf( "[NP2kai/pad] connected=%d " "buttons=0x%08x " "LX=%u LY=%u RX=%u RY=%u\n", g_padConnected ? 1 : 0, g_padData.buttons, static_cast( g_padData.leftStick.x ), static_cast( g_padData.leftStick.y ), static_cast( g_padData.rightStick.x ), static_cast( g_padData.rightStick.y ) ); g_previousPadButtons = g_padData.buttons; } } // OJO: sceUserServiceInitialize solo se puede llamar una vez en todo el // programa. lo comparten mando, audio y teclado. // el teclado la llamaba primero, al mando le // devolvia ALREADY_INITIALIZED (que es negativo), el mando se rendia ahi y // como el user id se quedaba invalido, el audio tambien se moria. // dos cosas rotas por una llamada de mas. no mover de aqui static constexpr int ORBIS_USER_SERVICE_ALREADY_INITIALIZED = static_cast(0x80960003u); static bool ensureUserServiceReady() { if (g_userServiceReady && g_padUserId != ORBIS_USER_SERVICE_USER_ID_INVALID) { return true; } if (!g_userServiceReady) { const int initResult = sceUserServiceInitialize(nullptr); if (initResult < 0 && initResult != ORBIS_USER_SERVICE_ALREADY_INITIALIZED) { std::printf( "[NP2kai/user] sceUserServiceInitialize failed: 0x%x\n", initResult); // no me corto aqui: si estaba abierto por algo que no controlo, la // consulta de abajo ya dira si sirve o no } g_userServiceReady = true; } const int userResult = sceUserServiceGetInitialUser(&g_padUserId); if (userResult < 0 || g_padUserId == ORBIS_USER_SERVICE_USER_ID_INVALID) { std::printf( "[NP2kai/user] sceUserServiceGetInitialUser failed: " "result=0x%x user=%d\n", userResult, g_padUserId); return false; } return true; } static bool initializePadInput() { if (!ensureUserServiceReady()) { std::printf("[NP2kai/pad] no usable PS4 user; pad unavailable.\n"); return false; } const int padInitResult = scePadInit(); if (padInitResult < 0) { std::printf( "[NP2kai/pad] scePadInit failed: 0x%x\n", padInitResult ); return false; } g_padHandle = scePadOpen( g_padUserId, ORBIS_PAD_PORT_TYPE_STANDARD, 0, nullptr ); if (g_padHandle < 0) { std::printf( "[NP2kai/pad] scePadOpen failed: 0x%x\n", g_padHandle ); return false; } g_padSubsystemReady = true; updatePadState(); std::printf( "[NP2kai/pad] ready: user=%d handle=0x%x\n", g_padUserId, g_padHandle ); return true; } static void shutdownPadInput() { if (g_padHandle >= 0) { scePadClose(g_padHandle); g_padHandle = -1; } g_padSubsystemReady = false; g_padConnected = false; if (g_userServiceReady && !ps4KeyboardIsOpen()) { sceUserServiceTerminate(); g_userServiceReady = false; g_padUserId = ORBIS_USER_SERVICE_USER_ID_INVALID; } } static bool leftStickLeft() { return g_analogMovementEnabled && g_padConnected && static_cast( g_padData.leftStick.x ) < 128 - g_padStickDeadzone; } static bool leftStickRight() { return g_analogMovementEnabled && g_padConnected && static_cast( g_padData.leftStick.x ) > 128 + g_padStickDeadzone; } static bool leftStickUp() { return g_analogMovementEnabled && g_padConnected && static_cast( g_padData.leftStick.y ) < 128 - g_padStickDeadzone; } static bool leftStickDown() { return g_analogMovementEnabled && g_padConnected && static_cast( g_padData.leftStick.y ) > 128 + g_padStickDeadzone; } static std::int16_t rightStickAxisToMouseDelta(std::uint8_t rawValue) { const int centered = static_cast(rawValue) - 128; const int magnitude = std::abs(centered); if (magnitude <= RIGHT_STICK_MOUSE_DEADZONE) { return 0; } const int usableRange = 127 - RIGHT_STICK_MOUSE_DEADZONE; const int adjusted = magnitude - RIGHT_STICK_MOUSE_DEADZONE; // le meto una curva para poder afinar pixel a pixel en el centro y aun asi // cruzar la pantalla rapido al fondo int delta = 1 + (adjusted * RIGHT_STICK_MOUSE_MAX_DELTA) / usableRange; delta += (adjusted * adjusted * 5) / (usableRange * usableRange); if (delta > RIGHT_STICK_MOUSE_MAX_DELTA) { delta = RIGHT_STICK_MOUSE_MAX_DELTA; } return static_cast(centered < 0 ? -delta : delta); } static void setSettingsStatus(const char* spanish, const char* english); static const std::uint32_t REMAPPABLE_BUTTON_MASKS[REMAPPABLE_BUTTON_COUNT] = { ORBIS_PAD_BUTTON_CROSS, ORBIS_PAD_BUTTON_CIRCLE, ORBIS_PAD_BUTTON_SQUARE, ORBIS_PAD_BUTTON_TRIANGLE, ORBIS_PAD_BUTTON_L1, ORBIS_PAD_BUTTON_R1, ORBIS_PAD_BUTTON_L2, ORBIS_PAD_BUTTON_R2, ORBIS_PAD_BUTTON_OPTIONS, ORBIS_PAD_BUTTON_SHARE_RAW }; static const char* controlProfileConfigName(ControlProfile profile) { switch (profile) { case ControlProfile::GenericPC98: return "generic_pc98"; case ControlProfile::MouseOriented: return "mouse_oriented"; case ControlProfile::Custom: return "custom"; default: return "touhou_pc98"; } } static ControlProfile parseControlProfile(const char* value) { if (value != nullptr && std::strcmp(value, "generic_pc98") == 0) { return ControlProfile::GenericPC98; } if (value != nullptr && std::strcmp(value, "mouse_oriented") == 0) { return ControlProfile::MouseOriented; } if (value != nullptr && std::strcmp(value, "custom") == 0) { return ControlProfile::Custom; } return ControlProfile::TouhouPC98; } static ControlAction makeNoneControlAction() { return {ControlBindingType::None, RETROK_UNKNOWN}; } static ControlAction makeKeyboardControlAction(unsigned keycode) { return {ControlBindingType::Keyboard, static_cast(keycode)}; } static ControlAction makeMouseControlAction(bool left) { return {left ? ControlBindingType::MouseLeft : ControlBindingType::MouseRight, RETROK_UNKNOWN}; } static bool controlActionsEqual(const ControlAction& left, const ControlAction& right) { return left.type == right.type && left.keycode == right.keycode; } static const ControlChoice CONTROL_CHOICES[] = { {{ControlBindingType::None, RETROK_UNKNOWN}, "none", "Ninguna", "None", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_SPACE}, "space", "Espacio", "Space", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_RETURN}, "return", "Intro", "Enter", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_ESCAPE}, "escape", "Escape", "Escape", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_TAB}, "tab", "Tabulador", "Tab", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_BACKSPACE}, "backspace", "Retroceso", "Backspace", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_LCTRL}, "control", "Control izquierdo", "Left Ctrl", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_RCTRL}, "right_control", "Control derecho", "Right Ctrl", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_RSHIFT}, "shift", "Mayúsculas derecha", "Right Shift", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_LSHIFT}, "left_shift", "Mayúsculas izquierda", "Left Shift", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_LALT}, "left_alt", "Alt izquierdo / NFER", "Left Alt / NFER", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_RALT}, "right_alt", "Alt derecho / XFER", "Right Alt / XFER", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_CAPSLOCK}, "caps_lock", "Bloq Mayús", "Caps Lock", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_LSUPER}, "left_super", "Super izquierda", "Left Super", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_RSUPER}, "right_super", "Super derecha", "Right Super", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_MENU}, "menu", "Menú", "Menu", ControlChoiceCategory::Common}, {{ControlBindingType::Keyboard, RETROK_a}, "key_a", "A", "A", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_b}, "key_b", "B", "B", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_c}, "key_c", "C", "C", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_d}, "key_d", "D", "D", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_e}, "key_e", "E", "E", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_f}, "key_f", "F", "F", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_g}, "key_g", "G", "G", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_h}, "key_h", "H", "H", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_i}, "key_i", "I", "I", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_j}, "key_j", "J", "J", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_k}, "key_k", "K", "K", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_l}, "key_l", "L", "L", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_m}, "key_m", "M", "M", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_n}, "key_n", "N", "N", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_o}, "key_o", "O", "O", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_p}, "key_p", "P", "P", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_q}, "key_q", "Q", "Q", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_r}, "key_r", "R", "R", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_s}, "key_s", "S", "S", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_t}, "key_t", "T", "T", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_u}, "key_u", "U", "U", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_v}, "key_v", "V", "V", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_w}, "key_w", "W", "W", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_x}, "key_x", "X", "X", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_y}, "key_y", "Y", "Y", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_z}, "key_z", "Z", "Z", ControlChoiceCategory::Letters}, {{ControlBindingType::Keyboard, RETROK_1}, "key_1", "1", "1", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_2}, "key_2", "2", "2", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_3}, "key_3", "3", "3", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_4}, "key_4", "4", "4", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_5}, "key_5", "5", "5", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_6}, "key_6", "6", "6", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_7}, "key_7", "7", "7", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_8}, "key_8", "8", "8", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_9}, "key_9", "9", "9", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_0}, "key_0", "0", "0", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_MINUS}, "minus", "- / =", "- / =", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_EQUALS}, "equals", "^ / `", "^ / `", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_BACKSLASH}, "backslash", "Yen / \\", "Yen / \\", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_LEFTBRACKET}, "left_bracket", "[ / {", "[ / {", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_RIGHTBRACKET}, "right_bracket", "] / }", "] / }", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_SEMICOLON}, "semicolon", "; / +", "; / +", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_QUOTE}, "quote", ": / *", ": / *", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_COMMA}, "comma", ", / <", ", / <", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_PERIOD}, "period", ". / >", ". / >", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_SLASH}, "slash", "/ / ?", "/ / ?", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_TILDE}, "tilde", "@ / ~", "@ / ~", ControlChoiceCategory::NumbersAndSymbols}, {{ControlBindingType::Keyboard, RETROK_UP}, "up", "Flecha arriba", "Arrow Up", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_DOWN}, "down", "Flecha abajo", "Arrow Down", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_LEFT}, "left", "Flecha izquierda", "Arrow Left", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_RIGHT}, "right", "Flecha derecha", "Arrow Right", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_INSERT}, "insert", "Insertar", "Insert", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_DELETE}, "delete", "Suprimir", "Delete", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_HOME}, "home", "Inicio / CLR", "Home / CLR", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_END}, "end", "Fin / HELP", "End / HELP", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_PAGEUP}, "page_up", "Re Pág / ROLLUP", "Page Up / ROLLUP", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_PAGEDOWN}, "page_down", "Av Pág / ROLLDOWN", "Page Down / ROLLDOWN", ControlChoiceCategory::Navigation}, {{ControlBindingType::Keyboard, RETROK_F1}, "f1", "F1", "F1", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F2}, "f2", "F2", "F2", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F3}, "f3", "F3", "F3", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F4}, "f4", "F4", "F4", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F5}, "f5", "F5", "F5", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F6}, "f6", "F6", "F6", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F7}, "f7", "F7", "F7", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F8}, "f8", "F8", "F8", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F9}, "f9", "F9", "F9", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_F10}, "f10", "F10", "F10", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_PAUSE}, "pause", "Pausa / STOP", "Pause / STOP", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_PRINT}, "print", "Impr Pant / COPY", "Print / COPY", ControlChoiceCategory::FunctionKeys}, {{ControlBindingType::Keyboard, RETROK_KP0}, "kp_0", "Teclado numérico 0", "Keypad 0", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP1}, "kp_1", "Teclado numérico 1", "Keypad 1", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP2}, "kp_2", "Teclado numérico 2", "Keypad 2", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP3}, "kp_3", "Teclado numérico 3", "Keypad 3", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP4}, "kp_4", "Teclado numérico 4", "Keypad 4", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP5}, "kp_5", "Teclado numérico 5", "Keypad 5", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP6}, "kp_6", "Teclado numérico 6", "Keypad 6", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP7}, "kp_7", "Teclado numérico 7", "Keypad 7", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP8}, "kp_8", "Teclado numérico 8", "Keypad 8", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP9}, "kp_9", "Teclado numérico 9", "Keypad 9", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP_PERIOD}, "kp_period", "Teclado numérico .", "Keypad .", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP_DIVIDE}, "kp_divide", "Teclado numérico /", "Keypad /", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP_MULTIPLY}, "kp_multiply", "Teclado numérico *", "Keypad *", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP_MINUS}, "kp_minus", "Teclado numérico -", "Keypad -", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP_PLUS}, "kp_plus", "Teclado numérico +", "Keypad +", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP_ENTER}, "kp_enter", "Intro numérico", "Keypad Enter", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::Keyboard, RETROK_KP_EQUALS}, "kp_equals", "Teclado numérico =", "Keypad =", ControlChoiceCategory::NumericKeypad}, {{ControlBindingType::MouseLeft, RETROK_UNKNOWN}, "mouse_left", "Clic izquierdo", "Mouse left click", ControlChoiceCategory::Mouse}, {{ControlBindingType::MouseRight, RETROK_UNKNOWN}, "mouse_right", "Clic derecho", "Mouse right click", ControlChoiceCategory::Mouse}, }; static constexpr unsigned CONTROL_CHOICE_COUNT = sizeof(CONTROL_CHOICES) / sizeof(CONTROL_CHOICES[0]); static const ControlChoice* findControlChoice(const ControlAction& action) { for (unsigned index = 0; index < CONTROL_CHOICE_COUNT; ++index) { if (controlActionsEqual(CONTROL_CHOICES[index].action, action)) { return &CONTROL_CHOICES[index]; } } return &CONTROL_CHOICES[0]; } static const ControlChoice* findKeyboardControlChoice(unsigned keycode) { for (unsigned index = 0; index < CONTROL_CHOICE_COUNT; ++index) { const ControlAction& action = CONTROL_CHOICES[index].action; if (action.type == ControlBindingType::Keyboard && action.keycode == keycode) { return &CONTROL_CHOICES[index]; } } return nullptr; } static unsigned controlChoiceCountForCategory(ControlChoiceCategory category) { unsigned count = 0; for (const ControlChoice& choice : CONTROL_CHOICES) { if (choice.category == category) ++count; } return count; } static const ControlChoice* controlChoiceForCategoryIndex( ControlChoiceCategory category, unsigned categoryIndex) { for (const ControlChoice& choice : CONTROL_CHOICES) { if (choice.category != category) continue; if (categoryIndex == 0) return &choice; --categoryIndex; } return nullptr; } static unsigned categoryIndexForControlChoice( ControlChoiceCategory category, const ControlAction& action) { unsigned categoryIndex = 0; for (const ControlChoice& choice : CONTROL_CHOICES) { if (choice.category != category) continue; if (controlActionsEqual(choice.action, action)) return categoryIndex; ++categoryIndex; } return 0; } static const char* controlActionConfigName(const ControlAction& action) { return findControlChoice(action)->configName; } static ControlAction parseControlAction(const char* value) { if (value == nullptr) return makeNoneControlAction(); for (const ControlChoice& choice : CONTROL_CHOICES) { if (std::strcmp(value, choice.configName) == 0) return choice.action; } return makeNoneControlAction(); } static const char* remappableButtonConfigName(unsigned index) { static const char* names[REMAPPABLE_BUTTON_COUNT] = { "cross", "circle", "square", "triangle", "l1", "r1", "l2", "r2", "options", "share" }; return index < REMAPPABLE_BUTTON_COUNT ? names[index] : "unknown"; } static void applyControlPreset(ControlBindings& bindings, ControlProfile profile) { for (unsigned index = 0; index < REMAPPABLE_BUTTON_COUNT; ++index) { bindings.actions[index] = makeNoneControlAction(); } bindings.profile = profile; if (profile == ControlProfile::GenericPC98) { bindings.actions[static_cast(RemappableButton::Cross)] = makeKeyboardControlAction(RETROK_RETURN); bindings.actions[static_cast(RemappableButton::Circle)] = makeKeyboardControlAction(RETROK_ESCAPE); bindings.actions[static_cast(RemappableButton::Square)] = makeKeyboardControlAction(RETROK_SPACE); bindings.actions[static_cast(RemappableButton::Triangle)] = makeKeyboardControlAction(RETROK_LCTRL); } else if (profile == ControlProfile::MouseOriented) { bindings.actions[static_cast(RemappableButton::Cross)] = makeMouseControlAction(true); bindings.actions[static_cast(RemappableButton::Circle)] = makeMouseControlAction(false); bindings.actions[static_cast(RemappableButton::Square)] = makeKeyboardControlAction(RETROK_RETURN); bindings.actions[static_cast(RemappableButton::Triangle)] = makeKeyboardControlAction(RETROK_ESCAPE); } else { // el mapeo original, pensado para los touhou bindings.profile = ControlProfile::TouhouPC98; bindings.actions[static_cast(RemappableButton::Cross)] = makeKeyboardControlAction(RETROK_z); bindings.actions[static_cast(RemappableButton::Circle)] = makeKeyboardControlAction(RETROK_x); bindings.actions[static_cast(RemappableButton::Square)] = makeKeyboardControlAction(RETROK_SPACE); bindings.actions[static_cast(RemappableButton::Triangle)] = makeKeyboardControlAction(RETROK_LCTRL); } bindings.actions[static_cast(RemappableButton::L1)] = makeKeyboardControlAction(RETROK_BACKSPACE); bindings.actions[static_cast(RemappableButton::R1)] = makeKeyboardControlAction(RETROK_RSHIFT); bindings.actions[static_cast(RemappableButton::L2)] = makeMouseControlAction(false); bindings.actions[static_cast(RemappableButton::R2)] = makeMouseControlAction(true); bindings.actions[static_cast(RemappableButton::Options)] = makeKeyboardControlAction(RETROK_ESCAPE); bindings.actions[static_cast(RemappableButton::Share)] = makeKeyboardControlAction(RETROK_RETURN); } static bool remappableButtonPressed(unsigned index) { if (!g_padConnected || index >= REMAPPABLE_BUTTON_COUNT) return false; if (padButtonPressed(REMAPPABLE_BUTTON_MASKS[index])) return true; if (index == static_cast(RemappableButton::L2) && g_padData.analogButtons.l2 > 32) return true; if (index == static_cast(RemappableButton::R2) && g_padData.analogButtons.r2 > 32) return true; return false; } static bool controlActionPressed(const ControlAction& action) { if (action.type == ControlBindingType::None) return false; for (unsigned index = 0; index < REMAPPABLE_BUTTON_COUNT; ++index) { if (!controlActionsEqual(g_activeControls.actions[index], action)) continue; if (remappableButtonPressed(index)) return true; } return false; } static bool controllerProducesKeyboardKey(unsigned keycode) { if (!g_padConnected) return false; if (keycode == RETROK_UP && (padButtonPressed(ORBIS_PAD_BUTTON_UP) || leftStickUp())) return true; if (keycode == RETROK_DOWN && (padButtonPressed(ORBIS_PAD_BUTTON_DOWN) || leftStickDown())) return true; if (keycode == RETROK_LEFT && (padButtonPressed(ORBIS_PAD_BUTTON_LEFT) || leftStickLeft())) return true; if (keycode == RETROK_RIGHT && (padButtonPressed(ORBIS_PAD_BUTTON_RIGHT) || leftStickRight())) return true; for (unsigned index = 0; index < REMAPPABLE_BUTTON_COUNT; ++index) { const ControlAction& action = g_activeControls.actions[index]; if (action.type != ControlBindingType::Keyboard || action.keycode != keycode) continue; if (remappableButtonPressed(index)) return true; } return false; } static bool keyboardKeyPressed(unsigned keycode) { if (keycode >= RETROK_LAST) return false; return g_externalKeyboardState[keycode] || controllerProducesKeyboardKey(keycode); } static std::uint32_t hashGameControlPath(const char* value) { std::uint32_t hash = 2166136261u; if (value == nullptr) return hash; while (*value != '\0') { hash ^= static_cast(*value++); hash *= 16777619u; } return hash; } static void makeGameControlPath( const GameEntry& game, char* destination, std::size_t capacity) { if (destination == nullptr || capacity == 0) return; char safeName[88] {}; unsigned output = 0; const char* source = game.name[0] != '\0' ? game.name : "game"; for (unsigned index = 0; source[index] != '\0' && output + 1 < sizeof(safeName); ++index) { const unsigned char character = static_cast(source[index]); const bool alphaNumeric = (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9'); safeName[output++] = alphaNumeric ? static_cast(character) : '_'; } if (output == 0) std::snprintf(safeName, sizeof(safeName), "game"); std::snprintf(destination, capacity, "%s/%s-%08x.ini", GAME_CONFIG_DIRECTORY, safeName, hashGameControlPath(game.path)); } static long fileSize(const char* path); // true si el juego tiene su propio perfil guardado, en vez de usar el global static bool gameHasOwnProfile(const GameEntry& game) { char path[MAX_GAME_PATH] {}; makeGameControlPath(game, path, sizeof(path)); return fileSize(path) > 0; } static bool saveGameControls(const GameEntry& game) { char path[MAX_GAME_PATH] {}; char temporary[MAX_GAME_PATH] {}; makeGameControlPath(game, path, sizeof(path)); std::snprintf(temporary, sizeof(temporary), "%s.tmp", path); std::FILE* output = std::fopen(temporary, "wb"); if (output == nullptr) { setLastFailure("NP2-1210", "No se pudo guardar el perfil del juego", "The game profile could not be saved", "Comprueba que la carpeta de perfiles sea escribible.", "Make sure the game profile folder is writable.", path); return false; } bool success = std::fprintf(output, "config_version=36\n") >= 0 && std::fprintf(output, "profile=%s\n", controlProfileConfigName(g_activeControls.profile)) >= 0; for (unsigned index = 0; success && index < REMAPPABLE_BUTTON_COUNT; ++index) { success = std::fprintf(output, "binding_%s=%s\n", remappableButtonConfigName(index), controlActionConfigName(g_activeControls.actions[index])) >= 0; } success = success && std::fflush(output) == 0; std::fclose(output); if (!success) { std::remove(temporary); setLastFailure("NP2-1211", "No se pudo escribir el perfil del juego", "The game profile could not be written", "El archivo temporal de controles no pudo completarse.", "The temporary controls file could not be completed.", temporary); return false; } if (std::remove(path) != 0 && errno != ENOENT) { std::remove(temporary); setLastFailure("NP2-1212", "No se pudo reemplazar el perfil del juego", "The game profile could not be replaced", "No se pudo eliminar el perfil anterior.", "The previous profile could not be removed.", path); return false; } if (std::rename(temporary, path) != 0) { setLastFailure("NP2-1213", "No se pudo activar el perfil del juego", "The game profile could not be activated", "No se pudo renombrar el archivo temporal.", "The temporary profile could not be renamed.", temporary); return false; } g_activeGameHasControlOverride = true; std::snprintf(g_activeGameControlPath, sizeof(g_activeGameControlPath), "%s", path); frontendLog("Game controls saved: %s profile=%s", path, controlProfileConfigName(g_activeControls.profile)); setSettingsStatus("Controles del juego guardados", "Game controls saved"); return true; } static bool loadGameControls(const GameEntry& game) { g_activeControls = g_defaultControls; g_activeGameHasControlOverride = false; makeGameControlPath(game, g_activeGameControlPath, sizeof(g_activeGameControlPath)); std::FILE* input = std::fopen(g_activeGameControlPath, "rb"); if (input == nullptr) { frontendLog("No per-game controls; using default profile for %s", game.path); return true; } bool malformed = false; char line[256] {}; while (std::fgets(line, sizeof(line), input) != nullptr) { char key[64] {}; char value[128] {}; if (std::sscanf(line, "%63[^=]=%127s", key, value) != 2) { const char* cursor = line; while (*cursor == ' ' || *cursor == '\t' || *cursor == '\r' || *cursor == '\n') ++cursor; if (*cursor != '\0' && *cursor != '#' && *cursor != ';') malformed = true; continue; } if (std::strcmp(key, "profile") == 0) { const ControlProfile profile = parseControlProfile(value); if (profile == ControlProfile::Custom) g_activeControls.profile = profile; else applyControlPreset(g_activeControls, profile); continue; } if (std::strncmp(key, "binding_", 8) == 0) { bool found = false; for (unsigned index = 0; index < REMAPPABLE_BUTTON_COUNT; ++index) { if (std::strcmp(key + 8, remappableButtonConfigName(index)) == 0) { g_activeControls.actions[index] = parseControlAction(value); found = true; break; } } if (!found) malformed = true; } } std::fclose(input); if (malformed) { frontendLog("Malformed per-game controls ignored: %s", g_activeGameControlPath); g_activeControls = g_defaultControls; g_activeGameHasControlOverride = false; return false; } g_activeGameHasControlOverride = true; frontendLog("Game controls loaded: %s profile=%s", g_activeGameControlPath, controlProfileConfigName(g_activeControls.profile)); return true; } static bool removeGameControlsOverride(const GameEntry& game) { char path[MAX_GAME_PATH] {}; makeGameControlPath(game, path, sizeof(path)); if (std::remove(path) != 0 && errno != ENOENT) { setLastFailure("NP2-1214", "No se pudo restablecer el perfil del juego", "The game profile could not be reset", "No se pudo eliminar el archivo de controles del juego.", "The per-game controls file could not be removed.", path); return false; } g_activeControls = g_defaultControls; g_activeGameHasControlOverride = false; std::snprintf(g_activeGameControlPath, sizeof(g_activeGameControlPath), "%s", path); setSettingsStatus("El juego usa el perfil predeterminado", "Game uses the default profile"); frontendLog("Game control override removed: %s", path); return true; } static void processFrontendEvents(); static void inputPollCallback() { // por si algun dia el bucle no llama a los eventos, que el teclado no se // quede desincronizado processFrontendEvents(); updatePadState(); g_mouseDeltaX = 0; g_mouseDeltaY = 0; if (g_padConnected) { g_mouseDeltaX = rightStickAxisToMouseDelta(g_padData.rightStick.x); g_mouseDeltaY = rightStickAxisToMouseDelta(g_padData.rightStick.y); } } static std::int16_t inputStateCallback( unsigned port, unsigned device, unsigned index, unsigned id) { (void)index; if (port != 0) return 0; if (device == RETRO_DEVICE_KEYBOARD) { return keyboardKeyPressed(id) ? 1 : 0; } if (device == RETRO_DEVICE_MOUSE) { if (!g_padConnected) return 0; switch (id) { case RETRO_DEVICE_ID_MOUSE_X: return g_mouseDeltaX; case RETRO_DEVICE_ID_MOUSE_Y: return g_mouseDeltaY; case RETRO_DEVICE_ID_MOUSE_LEFT: return controlActionPressed(makeMouseControlAction(true)); case RETRO_DEVICE_ID_MOUSE_RIGHT: return controlActionPressed(makeMouseControlAction(false)); default: return 0; } } if (device != RETRO_DEVICE_JOYPAD || !g_padConnected) return 0; switch (id) { case RETRO_DEVICE_ID_JOYPAD_UP: return padButtonPressed(ORBIS_PAD_BUTTON_UP) || leftStickUp(); case RETRO_DEVICE_ID_JOYPAD_DOWN: return padButtonPressed(ORBIS_PAD_BUTTON_DOWN) || leftStickDown(); case RETRO_DEVICE_ID_JOYPAD_LEFT: return padButtonPressed(ORBIS_PAD_BUTTON_LEFT) || leftStickLeft(); case RETRO_DEVICE_ID_JOYPAD_RIGHT: return padButtonPressed(ORBIS_PAD_BUTTON_RIGHT) || leftStickRight(); // el touchpad siempre abre el menu de np2kai. el core lo pide como L2, // pero el L2 de verdad lo dejo libre para asignarlo case RETRO_DEVICE_ID_JOYPAD_L2: return padButtonPressed(ORBIS_PAD_BUTTON_TOUCH_PAD); // L3 y R3 sueltos si pasan al juego, pero los dos a la vez no, que esa // es la combinacion de la pausa case RETRO_DEVICE_ID_JOYPAD_L3: return padButtonPressed(ORBIS_PAD_BUTTON_L3) && !padButtonPressed(ORBIS_PAD_BUTTON_R3); case RETRO_DEVICE_ID_JOYPAD_R3: return padButtonPressed(ORBIS_PAD_BUTTON_R3) && !padButtonPressed(ORBIS_PAD_BUTTON_L3); default: return 0; } } static const std::uint8_t* tinyGlyph(char character) { static const std::uint8_t alphabet[26][7] = { {14,17,17,31,17,17,17}, {30,17,17,30,17,17,30}, {14,17,16,16,16,17,14}, {30,17,17,17,17,17,30}, {31,16,16,30,16,16,31}, {31,16,16,30,16,16,16}, {14,17,16,23,17,17,15}, {17,17,17,31,17,17,17}, {14,4,4,4,4,4,14}, {7,2,2,2,2,18,12}, {17,18,20,24,20,18,17}, {16,16,16,16,16,16,31}, {17,27,21,21,17,17,17}, {17,25,21,19,17,17,17}, {14,17,17,17,17,17,14}, {30,17,17,30,16,16,16}, {14,17,17,17,21,18,13}, {30,17,17,30,20,18,17}, {15,16,16,14,1,1,30}, {31,4,4,4,4,4,4}, {17,17,17,17,17,17,14}, {17,17,17,17,17,10,4}, {17,17,17,21,21,21,10}, {17,17,10,4,10,17,17}, {17,17,10,4,4,4,4}, {31,1,2,4,8,16,31} }; static const std::uint8_t digits[10][7] = { {14,17,19,21,25,17,14}, {4,12,4,4,4,4,14}, {14,17,1,2,4,8,31}, {30,1,1,14,1,1,30}, {2,6,10,18,31,2,2}, {31,16,16,30,1,1,30}, {14,16,16,30,17,17,14}, {31,1,2,4,8,8,8}, {14,17,17,14,17,17,14}, {14,17,17,15,1,1,14} }; static const std::uint8_t blank[7] = {0,0,0,0,0,0,0}; static const std::uint8_t question[7] = {14,17,1,2,4,0,4}; static const std::uint8_t dash[7] = {0,0,0,31,0,0,0}; static const std::uint8_t underscore[7] = {0,0,0,0,0,0,31}; static const std::uint8_t dot[7] = {0,0,0,0,0,12,12}; static const std::uint8_t slash[7] = {1,2,2,4,8,8,16}; static const std::uint8_t colon[7] = {0,12,12,0,12,12,0}; static const std::uint8_t leftBracket[7] = {14,8,8,8,8,8,14}; static const std::uint8_t rightBracket[7] = {14,2,2,2,2,2,14}; static const std::uint8_t leftParen[7] = {2,4,8,8,8,4,2}; static const std::uint8_t rightParen[7] = {8,4,2,2,2,4,8}; static const std::uint8_t plus[7] = {0,4,4,31,4,4,0}; static const std::uint8_t percent[7] = {17,2,4,8,16,17,0}; static const std::uint8_t greater[7] = {16,8,4,2,4,8,16}; static const std::uint8_t equal[7] = {0,0,31,0,31,0,0}; if (character >= 'a' && character <= 'z') { character = static_cast(character - 'a' + 'A'); } if (character >= 'A' && character <= 'Z') { return alphabet[character - 'A']; } if (character >= '0' && character <= '9') { return digits[character - '0']; } switch (character) { case ' ': return blank; case '-': return dash; case '_': return underscore; case '.': return dot; case '/': return slash; case ':': return colon; case '[': return leftBracket; case ']': return rightBracket; case '(': return leftParen; case ')': return rightParen; case '+': return plus; case '%': return percent; case '>': return greater; case '=': return equal; default: return question; } } static void drawTinyText( std::uint32_t* output, int x, int y, int scale, std::uint32_t color, const char* textValue, unsigned maxCharacters = 0) { if (output == nullptr || textValue == nullptr || scale <= 0) { return; } int cursorX = x; unsigned characterIndex = 0; while (*textValue != '\0' && (maxCharacters == 0 || characterIndex < maxCharacters)) { const std::uint8_t* glyph = tinyGlyph(*textValue++); for (int row = 0; row < 7; ++row) { for (int column = 0; column < 5; ++column) { if ((glyph[row] & (1u << (4 - column))) != 0) { fillRectangle( output, cursorX + column * scale, y + row * scale, scale, scale, color ); } } } cursorX += 6 * scale; ++characterIndex; } } // ------------------------------------------------------------------------- // las tipografias van embebidas como bitmaps: el pkg no depende de ningun // archivo de fuente externo // ------------------------------------------------------------------------- struct UiGlyph { std::uint32_t codepoint; std::int16_t width; std::int16_t height; std::int16_t bearingX; std::int16_t bearingY; std::int16_t advance64; std::uint32_t offset; }; enum class UiFace { Body, Display }; // : generado con tools/build-ui-font.py static constexpr int UI_FONT_ASCENT = 33; static constexpr int UI_FONT_DESCENT = 10; static constexpr int UI_FONT_LINE_HEIGHT = 43; static const UiGlyph UI_GLYPHS[] = { {32u, 0, 0, 0, 0, 492, 0u}, {33u, 11, 24, 0, -24, 709, 0u}, {34u, 14, 23, 0, -23, 872, 264u}, {35u, 17, 24, 0, -24, 1084, 586u}, {36u, 17, 26, 0, -22, 1103, 994u}, {37u, 24, 24, 0, -24, 1556, 1436u}, {38u, 24, 24, 0, -24, 1484, 2012u}, {39u, 8, 23, 0, -23, 480, 2588u}, {40u, 10, 30, 0, -25, 660, 2772u}, {41u, 10, 30, 0, -25, 660, 3072u}, {42u, 17, 25, 0, -25, 1084, 3372u}, {43u, 17, 19, 0, -19, 1084, 3797u}, {44u, 8, 9, 0, -4, 543, 4120u}, {45u, 10, 12, 0, -12, 666, 4192u}, {46u, 9, 4, 0, -4, 549, 4312u}, {47u, 15, 26, -1, -24, 840, 4348u}, {48u, 17, 22, 0, -22, 1103, 4738u}, {49u, 17, 22, 0, -22, 1103, 5112u}, {50u, 17, 22, 0, -22, 1103, 5486u}, {51u, 17, 22, 0, -22, 1103, 5860u}, {52u, 17, 22, 0, -22, 1103, 6234u}, {53u, 17, 22, 0, -22, 1103, 6608u}, {54u, 17, 24, 0, -24, 1103, 6982u}, {55u, 17, 22, 0, -22, 1103, 7390u}, {56u, 17, 24, 0, -24, 1103, 7764u}, {57u, 17, 22, 0, -22, 1103, 8172u}, {58u, 9, 16, 0, -16, 582, 8546u}, {59u, 9, 20, 0, -15, 582, 8690u}, {60u, 17, 18, 0, -18, 1084, 8870u}, {61u, 17, 15, 0, -15, 1084, 9176u}, {62u, 17, 18, 0, -18, 1084, 9431u}, {63u, 16, 24, 0, -24, 1008, 9737u}, {64u, 30, 27, 0, -22, 1945, 10121u}, {65u, 20, 22, 0, -22, 1259, 10931u}, {66u, 19, 22, 0, -22, 1184, 11371u}, {67u, 18, 22, 0, -22, 1160, 11789u}, {68u, 21, 22, 0, -22, 1339, 12185u}, {69u, 17, 22, 0, -22, 1063, 12647u}, {70u, 16, 22, 0, -22, 1000, 13021u}, {71u, 21, 22, 0, -22, 1373, 13373u}, {72u, 21, 22, 0, -22, 1356, 13835u}, {73u, 9, 22, 0, -22, 548, 14297u}, {74u, 11, 22, 0, -22, 694, 14495u}, {75u, 18, 22, 0, -22, 1131, 14737u}, {76u, 14, 22, 0, -22, 915, 15133u}, {77u, 29, 22, 0, -22, 1860, 15441u}, {78u, 22, 22, 0, -22, 1405, 16079u}, {79u, 23, 22, 0, -22, 1441, 16563u}, {80u, 18, 22, 0, -22, 1124, 17069u}, {81u, 24, 27, 0, -22, 1464, 17465u}, {82u, 18, 22, 0, -22, 1182, 18113u}, {83u, 16, 22, 0, -22, 1000, 18509u}, {84u, 17, 22, 0, -22, 1060, 18861u}, {85u, 22, 22, 0, -22, 1396, 19235u}, {86u, 20, 22, 0, -22, 1235, 19719u}, {87u, 31, 22, 0, -22, 1936, 20159u}, {88u, 18, 22, 0, -22, 1129, 20841u}, {89u, 17, 22, 0, -22, 1060, 21237u}, {90u, 16, 22, 0, -22, 1019, 21611u}, {91u, 10, 31, 0, -26, 667, 21963u}, {92u, 15, 26, -1, -24, 840, 22273u}, {93u, 10, 31, 0, -26, 667, 22663u}, {94u, 17, 23, 0, -23, 1084, 22973u}, {95u, 17, 5, 0, 0, 1084, 23364u}, {96u, 10, 24, 0, -24, 633, 23449u}, {97u, 16, 17, 0, -17, 1042, 23689u}, {98u, 18, 24, 0, -24, 1143, 23961u}, {99u, 14, 17, 0, -17, 920, 24393u}, {100u, 18, 24, 0, -24, 1143, 24631u}, {101u, 17, 17, 0, -17, 1083, 25063u}, {102u, 11, 24, 0, -24, 664, 25352u}, {103u, 16, 23, 0, -17, 1024, 25616u}, {104u, 18, 24, 0, -24, 1143, 25984u}, {105u, 8, 24, 0, -24, 499, 26416u}, {106u, 9, 30, -1, -24, 521, 26608u}, {107u, 16, 24, 0, -24, 989, 26878u}, {108u, 8, 24, 0, -24, 499, 27262u}, {109u, 27, 17, 0, -17, 1738, 27454u}, {110u, 18, 17, 0, -17, 1143, 27913u}, {111u, 18, 17, 0, -17, 1148, 28219u}, {112u, 18, 23, 0, -17, 1143, 28525u}, {113u, 18, 23, 0, -17, 1143, 28939u}, {114u, 12, 17, 0, -17, 759, 29353u}, {115u, 13, 17, 0, -17, 851, 29557u}, {116u, 11, 22, 0, -22, 729, 29778u}, {117u, 18, 17, 0, -17, 1143, 30020u}, {118u, 16, 17, 0, -17, 983, 30326u}, {119u, 24, 17, 0, -17, 1556, 30598u}, {120u, 15, 17, 0, -17, 942, 31006u}, {121u, 16, 23, 0, -17, 985, 31261u}, {122u, 13, 17, 0, -17, 860, 31629u}, {123u, 11, 31, 0, -26, 684, 31850u}, {124u, 16, 31, 0, -25, 1002, 32191u}, {125u, 11, 31, 0, -26, 684, 32687u}, {126u, 17, 13, 0, -13, 1084, 33028u}, {161u, 11, 23, 0, -17, 709, 33249u}, {170u, 14, 24, 0, -24, 876, 33502u}, {171u, 17, 16, 0, -16, 1115, 33838u}, {176u, 12, 24, 0, -24, 737, 34110u}, {183u, 9, 12, 0, -12, 549, 34398u}, {186u, 14, 24, 0, -24, 919, 34506u}, {187u, 17, 16, 0, -16, 1115, 34842u}, {191u, 16, 23, 0, -17, 1008, 35114u}, {193u, 20, 30, 0, -30, 1259, 35482u}, {199u, 18, 28, 0, -22, 1160, 36082u}, {201u, 17, 30, 0, -30, 1063, 36586u}, {205u, 10, 30, 0, -30, 548, 37096u}, {209u, 22, 28, 0, -28, 1405, 37396u}, {211u, 23, 30, 0, -30, 1441, 38012u}, {218u, 22, 30, 0, -30, 1396, 38702u}, {220u, 22, 28, 0, -28, 1396, 39362u}, {225u, 16, 24, 0, -24, 1042, 39978u}, {231u, 14, 23, 0, -17, 920, 40362u}, {233u, 17, 24, 0, -24, 1083, 40684u}, {237u, 10, 24, 0, -24, 499, 41092u}, {241u, 18, 24, 0, -24, 1143, 41332u}, {243u, 18, 24, 0, -24, 1148, 41764u}, {250u, 18, 24, 0, -24, 1143, 42196u}, {252u, 18, 22, 0, -22, 1143, 42628u}, {8211u, 17, 12, 0, -12, 1084, 43024u}, {8216u, 8, 25, 0, -25, 543, 43228u}, {8217u, 8, 24, 0, -24, 543, 43428u}, {8220u, 14, 26, 0, -26, 911, 43620u}, {8221u, 14, 25, 0, -25, 911, 43984u}, {8230u, 23, 4, 0, -4, 1502, 44334u}, }; static const std::uint8_t UI_FONT_ALPHA[] = { 0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0, 0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0, 0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0, 0,0,0,0,0,0,0,206,255,246,0,0,0,0,0,0,0,0,200,255,240,0,0,0,0,0,0,0,0,187,255,229, 0,0,0,0,0,0,0,0,168,255,212,0,0,0,0,0,0,0,0,142,255,189,0,0,0,0,0,0,0,0,117,255, 162,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6, 170,243,179,10,0,0,0,0,0,0,97,255,255,255,114,0,0,0,0,0,0,100,255,255,255,118,0,0,0,0,0,0, 8,176,244,184,13,0,0,0,0,0,12,255,255,124,0,0,8,255,255,128,0,0,0,0,12,255,255,124,0,0,8,255, 255,128,0,0,0,0,12,255,255,124,0,0,8,255,255,128,0,0,0,0,12,255,255,124,0,0,8,255,255,128,0,0, 0,0,9,255,255,120,0,0,5,255,255,124,0,0,0,0,0,240,255,95,0,0,0,236,255,99,0,0,0,0,0,209, 255,64,0,0,0,205,255,68,0,0,0,0,0,131,232,12,0,0,0,127,233,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,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,125,79,0,0,0,122,116,9,0,0,0,0,0,0,0, 0,229,255,123,0,0,24,255,255,72,0,0,0,0,0,0,0,29,255,255,75,0,0,73,255,255,33,0,0,0,0,0, 0,0,77,255,255,27,0,0,121,255,240,0,0,0,0,0,0,0,0,125,255,235,0,0,0,169,255,192,0,0,0,0, 0,0,0,0,173,255,187,0,0,0,217,255,144,0,0,0,0,0,0,0,0,220,255,139,0,0,10,253,255,96,0,0, 0,0,34,233,255,255,255,255,255,255,255,255,255,255,255,255,255,70,0,103,255,255,255,255,255,255,255,255,255,255,255,255, 255,251,22,0,44,88,88,136,255,255,107,88,88,166,255,246,88,87,49,0,0,0,0,0,110,255,247,2,0,0,156,255, 205,0,0,0,0,0,0,0,0,153,255,206,0,0,0,200,255,161,0,0,0,0,0,0,0,0,196,255,164,0,0,1, 243,255,117,0,0,0,0,0,0,0,0,238,255,121,0,0,33,255,255,74,0,0,0,0,4,88,88,99,255,255,144,88, 88,133,255,255,112,88,82,6,0,36,255,255,255,255,255,255,255,255,255,255,255,255,255,255,77,0,29,244,255,255,255,255, 255,255,255,255,255,255,255,255,255,61,0,0,0,0,127,255,233,0,0,0,179,255,181,0,0,0,0,0,0,0,0,171, 255,190,0,0,0,223,255,137,0,0,0,0,0,0,0,0,216,255,147,0,0,12,254,255,93,0,0,0,0,0,0,0, 7,252,255,104,0,0,55,255,255,49,0,0,0,0,0,0,0,49,255,255,61,0,0,99,255,252,8,0,0,0,0,0, 0,0,93,255,255,18,0,0,141,255,217,0,0,0,0,0,0,0,0,138,254,164,0,0,0,110,253,173,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,21,255,151,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,255,135,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,53,255,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,255,103, 0,0,0,0,0,0,0,0,0,0,0,57,165,227,250,255,245,194,109,11,0,0,0,0,0,0,1,144,255,255,255,255, 255,255,255,255,230,65,0,0,0,0,0,121,255,255,218,108,159,255,112,151,247,255,149,0,0,0,0,9,243,255,215,10, 0,144,255,29,0,30,128,13,0,0,0,0,62,255,255,119,0,0,162,255,12,0,0,0,0,0,0,0,0,71,255,255, 133,0,0,180,250,0,0,0,0,0,0,0,0,0,32,255,255,243,70,0,199,233,0,0,0,0,0,0,0,0,0,0, 180,255,255,255,194,235,216,0,0,0,0,0,0,0,0,0,0,19,198,255,255,255,255,255,206,122,19,0,0,0,0,0, 0,0,0,1,90,201,254,255,255,255,255,241,117,0,0,0,0,0,0,0,0,0,0,40,255,238,252,255,255,255,122,0, 0,0,0,0,0,0,0,0,36,255,142,22,143,255,255,241,8,0,0,0,0,0,0,0,0,55,255,124,0,0,158,255, 255,41,0,0,0,0,0,0,0,0,73,255,106,0,0,103,255,255,35,0,0,0,27,114,15,0,0,92,255,88,0,0, 164,255,234,2,0,0,0,193,255,229,103,13,111,255,70,5,120,255,255,121,0,0,0,1,133,255,255,255,252,235,255,207, 241,255,255,177,3,0,0,0,0,0,62,196,255,255,255,255,255,255,239,118,2,0,0,0,0,0,0,0,0,34,95,211, 255,123,74,8,0,0,0,0,0,0,0,0,0,0,0,0,182,249,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,201,231,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,219,213,0,0,0,0,0,0,0,0,0,0,0,67, 187,242,243,189,70,0,0,0,0,0,0,0,0,0,7,184,252,66,0,0,0,0,99,254,255,255,255,255,254,103,0,0, 0,0,0,0,0,0,147,255,137,0,0,0,0,31,247,255,211,114,112,207,255,249,35,0,0,0,0,0,0,74,255,205, 5,0,0,0,0,131,255,230,15,0,0,12,225,255,138,0,0,0,0,0,23,233,245,39,0,0,0,0,0,194,255,142, 0,0,0,0,133,255,202,0,0,0,0,0,182,255,102,0,0,0,0,0,0,223,255,106,0,0,0,0,92,255,230,0, 0,0,0,110,255,175,0,0,0,0,0,0,0,223,255,105,0,0,0,0,91,255,230,0,0,0,45,247,229,19,0,0, 0,0,0,0,0,197,255,139,0,0,0,0,130,255,202,0,0,8,211,254,68,0,0,0,0,0,0,0,0,135,255,234, 13,0,0,10,229,255,135,0,0,147,255,139,0,0,0,0,0,0,0,0,0,32,248,255,214,111,110,211,255,245,29,0, 74,255,207,6,0,0,0,0,0,0,0,0,0,0,103,254,255,255,255,255,253,92,0,23,233,245,40,0,0,0,0,0, 0,0,0,0,0,0,0,70,190,243,243,186,62,0,0,182,255,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,110,255,177,0,0,33,162,233,248,210,109,3,0,0,0,0,0,0,0,0,0,0,0,44,247,230, 20,0,48,238,255,255,255,255,255,170,1,0,0,0,0,0,0,0,0,0,8,211,254,70,0,1,211,255,243,132,103,182, 255,255,96,0,0,0,0,0,0,0,0,0,146,255,142,0,0,67,255,255,57,0,0,0,168,255,206,0,0,0,0,0, 0,0,0,74,255,208,7,0,0,131,255,209,0,0,0,0,65,255,254,16,0,0,0,0,0,0,22,233,246,42,0,0, 0,159,255,171,0,0,0,0,24,255,255,42,0,0,0,0,0,0,182,255,106,0,0,0,0,159,255,170,0,0,0,0, 23,255,255,42,0,0,0,0,0,109,255,179,0,0,0,0,0,133,255,206,0,0,0,0,62,255,254,15,0,0,0,0, 44,247,231,21,0,0,0,0,0,69,255,255,60,0,0,0,172,255,201,0,0,0,0,8,211,254,72,0,0,0,0,0, 0,2,214,255,241,128,101,177,255,255,87,0,0,0,0,146,255,144,0,0,0,0,0,0,0,0,52,241,255,255,255,255, 255,157,0,0,0,0,73,251,185,7,0,0,0,0,0,0,0,0,0,37,166,235,248,208,102,1,0,0,0,0,0,0, 0,0,0,39,150,219,246,242,200,111,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,251,255,255,255,255, 255,255,211,31,0,0,0,0,0,0,0,0,0,0,0,0,0,84,255,255,252,155,97,107,203,255,255,214,7,0,0,0, 0,0,0,0,0,0,0,0,4,226,255,251,62,0,0,0,3,165,255,255,110,0,0,0,0,0,0,0,0,0,0,0, 62,255,255,147,0,0,0,0,0,13,241,255,191,0,0,0,0,0,0,0,0,0,0,0,101,255,255,88,0,0,0,0, 0,0,90,133,74,0,0,0,0,0,0,0,0,0,0,0,100,255,255,93,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,63,255,255,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 6,236,255,250,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,122,255,255,216,18,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,211,255,255,198,12,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,2,94,227,255,255,255,193,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19, 198,255,255,214,246,255,255,188,8,0,0,0,0,6,231,255,92,0,0,0,0,0,22,213,255,255,126,5,63,243,255,255, 184,7,0,0,0,56,255,255,69,0,0,0,0,0,176,255,255,117,0,0,0,56,239,255,255,179,5,0,0,112,255,254, 20,0,0,0,0,48,255,255,211,1,0,0,0,0,49,235,255,255,174,4,0,187,255,208,0,0,0,0,0,124,255,255, 125,0,0,0,0,0,0,43,231,255,255,169,46,254,255,112,0,0,0,0,0,153,255,255,95,0,0,0,0,0,0,0, 37,226,255,255,240,255,237,16,0,0,0,0,0,137,255,255,125,0,0,0,0,0,0,0,0,31,221,255,255,255,104,0, 0,0,0,0,0,81,255,255,223,16,0,0,0,0,0,0,0,0,135,255,255,255,169,3,0,0,0,0,0,6,222,255, 255,185,21,0,0,0,0,0,36,176,255,255,250,255,255,165,2,0,0,0,0,0,61,247,255,255,242,145,97,100,126,192, 252,255,255,155,30,216,255,255,161,2,0,0,0,0,0,64,227,255,255,255,255,255,255,255,255,210,77,0,0,28,222,255, 255,157,1,0,0,0,0,0,11,108,189,236,250,231,206,145,68,2,0,0,0,0,32,204,254,255,152,1,0,0,132,255, 255,4,0,0,0,0,132,255,255,4,0,0,0,0,132,255,255,4,0,0,0,0,132,255,255,4,0,0,0,0,129,255, 254,2,0,0,0,0,104,255,231,0,0,0,0,0,73,255,200,0,0,0,0,0,15,233,126,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,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,125,7,0,0,0, 0,0,0,11,227,255,129,0,0,0,0,0,0,126,255,255,64,0,0,0,0,0,16,240,255,196,0,0,0,0,0,0, 115,255,255,71,0,0,0,0,0,0,218,255,217,0,0,0,0,0,0,52,255,255,130,0,0,0,0,0,0,134,255,255, 43,0,0,0,0,0,0,199,255,222,0,0,0,0,0,0,5,250,255,169,0,0,0,0,0,0,45,255,255,117,0,0, 0,0,0,0,77,255,255,69,0,0,0,0,0,0,94,255,255,47,0,0,0,0,0,0,110,255,255,31,0,0,0,0, 0,0,125,255,255,14,0,0,0,0,0,0,123,255,255,17,0,0,0,0,0,0,108,255,255,33,0,0,0,0,0,0, 92,255,255,50,0,0,0,0,0,0,74,255,255,75,0,0,0,0,0,0,39,255,255,124,0,0,0,0,0,0,3,246, 255,175,0,0,0,0,0,0,0,190,255,229,0,0,0,0,0,0,0,123,255,255,54,0,0,0,0,0,0,40,255,255, 141,0,0,0,0,0,0,0,205,255,227,2,0,0,0,0,0,0,100,255,255,88,0,0,0,0,0,0,8,229,255,211, 1,0,0,0,0,0,0,107,255,255,81,0,0,0,0,0,0,4,212,255,116,0,0,0,0,0,0,0,59,88,0,0, 0,0,78,138,0,0,0,0,0,0,0,54,255,255,62,0,0,0,0,0,0,8,232,255,201,0,0,0,0,0,0,0, 117,255,255,76,0,0,0,0,0,0,12,236,255,191,0,0,0,0,0,0,0,138,255,255,41,0,0,0,0,0,0,51, 255,255,129,0,0,0,0,0,0,0,219,255,209,0,0,0,0,0,0,0,142,255,254,20,0,0,0,0,0,0,90,255, 255,77,0,0,0,0,0,0,38,255,255,120,0,0,0,0,0,0,1,244,255,153,0,0,0,0,0,0,0,224,255,169, 0,0,0,0,0,0,0,208,255,185,0,0,0,0,0,0,0,191,255,201,0,0,0,0,0,0,0,194,255,198,0,0, 0,0,0,0,0,210,255,183,0,0,0,0,0,0,0,227,255,167,0,0,0,0,0,0,3,247,255,150,0,0,0,0, 0,0,45,255,255,114,0,0,0,0,0,0,96,255,255,70,0,0,0,0,0,0,150,255,252,14,0,0,0,0,0,2, 229,255,199,0,0,0,0,0,0,62,255,255,117,0,0,0,0,0,0,150,255,252,29,0,0,0,0,0,20,244,255,175, 0,0,0,0,0,0,134,255,255,59,0,0,0,0,0,15,241,255,183,0,0,0,0,0,0,47,250,251,43,0,0,0, 0,0,0,0,44,103,0,0,0,0,0,0,0,0,0,0,0,0,0,72,255,44,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,72,255,44,0,0,0,0,0,0,0,0,0,0,8,178,53,0,72,255,44,0,64,166,1,0,0,0,0, 0,0,40,227,253,147,78,255,57,160,255,217,24,0,0,0,0,0,0,0,13,145,253,214,255,220,249,130,7,0,0,0, 0,0,0,0,0,0,0,83,242,255,238,66,0,0,0,0,0,0,0,0,0,0,10,136,250,235,253,228,246,122,5,0, 0,0,0,0,0,0,37,223,253,152,84,255,55,166,255,213,25,0,0,0,0,0,0,7,178,56,0,72,255,44,0,70, 176,2,0,0,0,0,0,0,0,0,0,0,72,255,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,255,44, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,255,160,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,196,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,255, 160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 196,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,255,160,0,0,0,0,0,0,0,20,88,88,88,88, 88,88,216,255,193,88,88,88,88,88,88,12,60,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,36,60,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,36,0,0,0,0,0,0,0,196,255,160,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,196,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,255,160,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,196,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,255,160,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,196,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,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,31,204,238,120,0,0, 0,0,149,255,255,253,26,0,0,0,144,255,255,255,54,0,0,0,30,206,254,255,23,0,0,0,0,0,193,210,0,0, 0,0,0,47,253,109,0,0,0,0,8,205,211,4,0,0,0,0,104,236,42,0,0,0,0,0,10,44,0,0,0,0, 0,66,88,88,88,88,88,88,88,16,0,192,255,255,255,255,255,255,255,48,0,192,255,255,255,255,255,255,255,48,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,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,200,238,126,0,0, 0,0,0,165,255,255,255,50,0,0,0,0,168,255,255,255,54,0,0,0,0,37,204,240,133,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,37,222,249,23,0,0,0,0,0,0,0,0,0,0,0,163,255,163,0,0,0,0,0,0,0, 0,0,0,0,23,249,255,53,0,0,0,0,0,0,0,0,0,0,0,128,255,199,0,0,0,0,0,0,0,0,0,0, 0,6,232,255,90,0,0,0,0,0,0,0,0,0,0,0,93,255,230,5,0,0,0,0,0,0,0,0,0,0,0,203, 255,126,0,0,0,0,0,0,0,0,0,0,0,58,255,249,23,0,0,0,0,0,0,0,0,0,0,0,169,255,163,0, 0,0,0,0,0,0,0,0,0,0,27,251,255,53,0,0,0,0,0,0,0,0,0,0,0,134,255,199,0,0,0,0, 0,0,0,0,0,0,0,8,235,255,90,0,0,0,0,0,0,0,0,0,0,0,99,255,230,5,0,0,0,0,0,0, 0,0,0,0,0,208,255,126,0,0,0,0,0,0,0,0,0,0,0,64,255,249,23,0,0,0,0,0,0,0,0,0, 0,0,174,255,163,0,0,0,0,0,0,0,0,0,0,0,31,252,255,53,0,0,0,0,0,0,0,0,0,0,0,139, 255,199,0,0,0,0,0,0,0,0,0,0,0,10,238,255,90,0,0,0,0,0,0,0,0,0,0,0,104,255,230,5, 0,0,0,0,0,0,0,0,0,0,0,213,255,126,0,0,0,0,0,0,0,0,0,0,0,69,255,249,23,0,0,0, 0,0,0,0,0,0,0,0,179,255,163,0,0,0,0,0,0,0,0,0,0,0,35,253,255,53,0,0,0,0,0,0, 0,0,0,0,0,144,255,199,0,0,0,0,0,0,0,0,0,0,0,12,237,223,53,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,66,166,225,248,230,183,91,2,0,0,0,0,0,0,0,7,164,255,255,255,255,255,255,255,199, 26,0,0,0,0,0,1,176,255,255,231,134,95,122,214,255,255,215,17,0,0,0,0,98,255,255,202,18,0,0,0,5, 161,255,255,153,0,0,0,3,230,255,248,30,0,0,0,0,0,6,217,255,254,37,0,0,76,255,255,153,0,0,0,0, 0,0,0,99,255,255,132,0,0,154,255,255,68,0,0,0,0,0,0,0,19,252,255,210,0,0,206,255,255,12,0,0, 0,0,0,0,0,0,216,255,252,10,0,248,255,228,0,0,0,0,0,0,0,0,0,174,255,255,52,10,255,255,208,0, 0,0,0,0,0,0,0,0,154,255,255,69,25,255,255,197,0,0,0,0,0,0,0,0,0,142,255,255,83,25,255,255, 196,0,0,0,0,0,0,0,0,0,141,255,255,84,11,255,255,208,0,0,0,0,0,0,0,0,0,154,255,255,69,0, 249,255,227,0,0,0,0,0,0,0,0,0,173,255,255,52,0,207,255,254,12,0,0,0,0,0,0,0,0,215,255,253, 11,0,156,255,255,67,0,0,0,0,0,0,0,18,252,255,212,0,0,78,255,255,152,0,0,0,0,0,0,0,98,255, 255,135,0,0,4,231,255,247,29,0,0,0,0,0,5,216,255,254,39,0,0,0,100,255,255,200,16,0,0,0,4,158, 255,255,156,0,0,0,0,1,178,255,255,229,132,95,120,210,255,255,217,19,0,0,0,0,0,7,167,255,255,255,255,255, 255,255,202,27,0,0,0,0,0,0,0,0,70,169,227,249,232,185,95,2,0,0,0,0,0,0,0,0,0,0,0,0, 76,245,255,144,0,0,0,0,0,0,0,0,0,0,0,0,99,252,255,255,144,0,0,0,0,0,0,0,0,0,0,0, 125,255,255,255,255,144,0,0,0,0,0,0,0,0,0,3,152,255,255,247,255,255,144,0,0,0,0,0,0,0,0,8, 175,255,255,214,87,255,255,144,0,0,0,0,0,0,0,1,192,255,255,194,17,60,255,255,144,0,0,0,0,0,0,0, 0,122,255,169,7,0,60,255,255,144,0,0,0,0,0,0,0,0,0,45,1,0,0,60,255,255,144,0,0,0,0,0, 0,0,0,0,0,0,0,0,60,255,255,144,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,144,0,0,0, 0,0,0,0,0,0,0,0,0,0,60,255,255,144,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,144,0, 0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,144,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255, 144,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,144,0,0,0,0,0,0,0,0,0,0,0,0,0,60, 255,255,144,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,144,0,0,0,0,0,0,0,0,0,0,0,0, 0,60,255,255,144,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,144,0,0,0,0,0,0,0,0,0,36, 48,48,48,96,255,255,165,48,48,48,33,0,0,0,0,0,196,255,255,255,255,255,255,255,255,255,255,180,0,0,0,0, 0,196,255,255,255,255,255,255,255,255,255,255,180,0,0,0,0,0,0,46,152,218,246,244,212,141,31,0,0,0,0,0, 0,0,0,131,253,255,255,255,255,255,255,245,90,0,0,0,0,0,0,126,255,255,248,149,96,117,187,255,255,252,68,0, 0,0,0,42,251,255,231,39,0,0,0,0,113,255,255,212,0,0,0,0,149,255,255,70,0,0,0,0,0,1,203,255, 255,44,0,0,0,221,255,220,0,0,0,0,0,0,0,135,255,255,84,0,0,1,126,161,77,0,0,0,0,0,0,0, 127,255,255,92,0,0,0,0,0,0,0,0,0,0,0,0,0,163,255,255,63,0,0,0,0,0,0,0,0,0,0,0, 0,13,241,255,239,9,0,0,0,0,0,0,0,0,0,0,0,0,121,255,255,130,0,0,0,0,0,0,0,0,0,0, 0,0,46,247,255,232,18,0,0,0,0,0,0,0,0,0,0,0,20,217,255,253,67,0,0,0,0,0,0,0,0,0, 0,0,11,201,255,255,118,0,0,0,0,0,0,0,0,0,0,0,9,190,255,255,144,0,0,0,0,0,0,0,0,0, 0,0,7,185,255,255,162,1,0,0,0,0,0,0,0,0,0,0,6,180,255,255,173,3,0,0,0,0,0,0,0,0, 0,0,4,174,255,255,183,6,0,0,0,0,0,0,0,0,0,0,3,168,255,255,193,9,0,0,0,0,0,0,0,0, 0,0,2,162,255,255,202,13,0,0,0,0,0,0,0,0,0,0,0,156,255,255,216,70,80,88,88,88,88,88,88,86, 26,0,0,85,255,255,255,255,255,255,255,255,255,255,255,255,255,192,0,0,123,255,255,255,255,255,255,255,255,255,255,255, 255,255,200,0,0,0,0,0,0,38,142,209,243,249,227,172,76,0,0,0,0,0,0,0,0,119,250,255,255,255,255,255, 255,255,178,10,0,0,0,0,0,115,255,255,251,165,110,104,150,245,255,255,167,0,0,0,0,28,249,255,236,48,0,0, 0,0,35,239,255,255,38,0,0,0,126,255,255,89,0,0,0,0,0,0,117,255,255,98,0,0,0,144,235,204,3,0, 0,0,0,0,0,74,255,255,109,0,0,0,0,0,0,0,0,0,0,0,0,0,101,255,255,73,0,0,0,0,0,0, 0,0,0,0,0,0,15,213,255,217,4,0,0,0,0,0,0,0,0,0,9,38,105,222,255,233,54,0,0,0,0,0, 0,0,0,0,64,255,255,255,250,139,25,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,226,102,0,0,0,0, 0,0,0,0,0,0,22,98,121,177,249,255,255,154,0,0,0,0,0,0,0,0,0,0,0,0,0,34,213,255,255,77, 0,0,0,0,0,0,0,0,0,0,0,0,0,54,255,255,178,0,0,0,0,0,0,0,0,0,0,0,0,0,2,249, 255,225,0,0,0,35,89,8,0,0,0,0,0,0,0,0,227,255,232,0,0,61,253,255,127,0,0,0,0,0,0,0, 11,250,255,207,0,0,5,233,255,238,25,0,0,0,0,0,0,108,255,255,141,0,0,0,130,255,255,193,14,0,0,0, 0,68,244,255,249,37,0,0,0,10,215,255,255,229,133,95,106,175,254,255,255,113,0,0,0,0,0,30,206,255,255,255, 255,255,255,255,248,108,0,0,0,0,0,0,0,5,97,183,232,250,242,207,135,29,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,117,255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,51,249,255,255,132,0,0,0,0,0,0,0, 0,0,0,0,11,217,255,255,255,132,0,0,0,0,0,0,0,0,0,0,0,158,255,241,236,255,132,0,0,0,0,0, 0,0,0,0,0,87,255,255,91,228,255,132,0,0,0,0,0,0,0,0,0,31,240,255,163,0,235,255,132,0,0,0, 0,0,0,0,0,3,196,255,222,14,0,236,255,132,0,0,0,0,0,0,0,0,129,255,252,57,0,0,236,255,132,0, 0,0,0,0,0,0,60,252,255,126,0,0,0,236,255,132,0,0,0,0,0,0,16,225,255,196,3,0,0,0,236,255, 132,0,0,0,0,0,0,170,255,240,32,0,0,0,0,236,255,132,0,0,0,0,0,100,255,255,89,0,0,0,0,0, 236,255,132,0,0,0,0,39,244,255,162,0,0,0,0,0,0,236,255,132,0,0,0,6,206,255,221,13,0,0,0,0, 0,0,236,255,132,0,0,0,83,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,172,38,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,167,0,64,88,88,88,88,88,88,88,88,88,242,255,174,88,88,35,0,0,0,0,0, 0,0,0,0,0,0,236,255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,255,132,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,236,255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,255,132,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,236,255,132,0,0,0,0,0,0,0,174,255,255,255,255,255,255,255,255,255,144,0, 0,0,0,0,0,218,255,255,255,255,255,255,255,255,255,114,0,0,0,0,0,8,252,255,136,88,88,88,88,88,87,66, 2,0,0,0,0,0,49,255,255,37,0,0,0,0,0,0,0,0,0,0,0,0,0,93,255,248,3,0,0,0,0,0, 0,0,0,0,0,0,0,0,137,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,255,165,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,224,255,126,42,72,82,64,18,0,0,0,0,0,0,0,0,12,254,255,255,255,255, 255,255,253,187,54,0,0,0,0,0,0,56,255,255,255,255,255,255,255,255,255,250,94,0,0,0,0,0,16,105,156,94, 35,9,13,62,173,255,255,249,48,0,0,0,0,0,0,0,0,0,0,0,0,0,139,255,255,173,0,0,0,0,0,0, 0,0,0,0,0,0,0,8,236,255,246,7,0,0,0,0,0,0,0,0,0,0,0,0,0,174,255,255,39,0,0,0, 0,0,0,0,0,0,0,0,0,0,153,255,255,49,0,0,0,0,0,0,0,0,0,0,0,0,0,171,255,255,28,0, 0,0,0,0,0,0,0,0,0,0,0,2,229,255,233,1,0,0,0,0,0,0,0,0,0,0,0,0,91,255,255,152, 0,0,0,0,130,191,62,0,0,0,0,0,62,240,255,247,30,0,0,0,56,255,255,255,197,117,92,109,176,254,255,255, 90,0,0,0,0,1,117,243,255,255,255,255,255,255,255,238,81,0,0,0,0,0,0,0,20,116,190,229,250,235,194,119, 15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,227,255,252,61,0,0,0,0,0,0,0,0,0,0,0,30, 242,255,255,129,0,0,0,0,0,0,0,0,0,0,0,0,185,255,255,196,3,0,0,0,0,0,0,0,0,0,0,0, 101,255,255,239,30,0,0,0,0,0,0,0,0,0,0,0,29,241,255,255,84,0,0,0,0,0,0,0,0,0,0,0, 0,183,255,255,154,0,0,0,0,0,0,0,0,0,0,0,0,98,255,255,215,10,0,0,0,0,0,0,0,0,0,0, 0,27,240,255,248,47,0,0,0,0,0,0,0,0,0,0,0,0,181,255,255,110,0,0,0,0,0,0,0,0,0,0, 0,0,83,255,255,200,134,220,249,237,184,86,1,0,0,0,0,0,8,224,255,255,254,255,255,255,255,255,255,179,11,0, 0,0,0,100,255,255,255,239,143,97,108,182,255,255,255,169,0,0,0,0,209,255,255,212,26,0,0,0,0,87,253,255, 255,64,0,0,28,255,255,254,50,0,0,0,0,0,0,146,255,255,165,0,0,85,255,255,175,0,0,0,0,0,0,0, 40,255,255,228,0,0,110,255,255,110,0,0,0,0,0,0,0,0,243,255,252,1,0,109,255,255,92,0,0,0,0,0, 0,0,0,229,255,254,4,0,88,255,255,109,0,0,0,0,0,0,0,2,246,255,236,0,0,37,255,255,162,0,0,0, 0,0,0,0,54,255,255,180,0,0,0,216,255,244,23,0,0,0,0,0,0,170,255,255,95,0,0,0,99,255,255,191, 12,0,0,0,0,119,255,255,220,5,0,0,0,1,185,255,255,227,130,94,115,199,255,255,247,53,0,0,0,0,0,10, 175,255,255,255,255,255,255,255,228,57,0,0,0,0,0,0,0,0,73,170,227,249,237,196,114,10,0,0,0,0,0,92, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,36,0,88,255,255,255,255,255,255,255,255,255,255,255,255,255,255,24, 0,10,83,88,88,88,88,88,88,88,88,88,90,236,255,219,0,0,0,0,0,0,0,0,0,0,0,0,0,111,255,255, 104,0,0,0,0,0,0,0,0,0,0,0,0,11,235,255,231,7,0,0,0,0,0,0,0,0,0,0,0,0,116,255, 255,117,0,0,0,0,0,0,0,0,0,0,0,0,8,231,255,238,13,0,0,0,0,0,0,0,0,0,0,0,0,108, 255,255,129,0,0,0,0,0,0,0,0,0,0,0,0,5,225,255,244,19,0,0,0,0,0,0,0,0,0,0,0,0, 100,255,255,141,0,0,0,0,0,0,0,0,0,0,0,0,3,220,255,248,26,0,0,0,0,0,0,0,0,0,0,0, 0,93,255,255,153,0,0,0,0,0,0,0,0,0,0,0,0,2,214,255,252,35,0,0,0,0,0,0,0,0,0,0, 0,0,85,255,255,166,0,0,0,0,0,0,0,0,0,0,0,0,0,207,255,254,45,0,0,0,0,0,0,0,0,0, 0,0,0,77,255,255,178,0,0,0,0,0,0,0,0,0,0,0,0,0,200,255,255,56,0,0,0,0,0,0,0,0, 0,0,0,0,70,255,255,190,0,0,0,0,0,0,0,0,0,0,0,0,0,194,255,255,68,0,0,0,0,0,0,0, 0,0,0,0,0,61,255,255,202,0,0,0,0,0,0,0,0,0,0,0,0,0,186,255,255,80,0,0,0,0,0,0, 0,0,0,0,0,0,54,255,252,162,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,79,174,230,249,236,190,106, 8,0,0,0,0,0,0,0,16,184,255,255,255,255,255,255,255,217,40,0,0,0,0,0,1,189,255,255,233,128,94,115, 210,255,255,226,20,0,0,0,0,88,255,255,211,22,0,0,0,7,171,255,255,144,0,0,0,0,179,255,255,70,0,0, 0,0,0,22,248,255,235,2,0,0,0,220,255,254,10,0,0,0,0,0,0,208,255,255,23,0,0,0,216,255,253,5, 0,0,0,0,0,0,199,255,255,18,0,0,0,179,255,255,43,0,0,0,0,0,5,238,255,238,0,0,0,0,87,255, 255,160,0,0,0,0,0,105,255,255,144,0,0,0,0,3,182,255,255,150,38,5,26,115,248,255,219,22,0,0,0,0, 0,10,146,250,255,255,255,255,255,255,178,30,0,0,0,0,0,0,7,102,228,255,255,255,255,255,245,129,19,0,0,0, 0,0,13,196,255,255,210,123,93,112,188,255,255,229,37,0,0,0,0,182,255,255,155,3,0,0,0,0,106,255,255,226, 12,0,0,53,255,255,225,6,0,0,0,0,0,0,178,255,255,111,0,0,123,255,255,140,0,0,0,0,0,0,0,88, 255,255,182,0,0,156,255,255,102,0,0,0,0,0,0,0,51,255,255,214,0,0,155,255,255,100,0,0,0,0,0,0, 0,48,255,255,214,0,0,128,255,255,138,0,0,0,0,0,0,0,86,255,255,188,0,0,63,255,255,234,15,0,0,0, 0,0,0,180,255,255,122,0,0,2,208,255,255,165,4,0,0,0,0,111,255,255,245,25,0,0,0,52,246,255,255,214, 123,93,112,192,255,255,255,102,0,0,0,0,0,62,230,255,255,255,255,255,255,255,247,104,0,0,0,0,0,0,0,11, 110,188,233,250,239,202,131,26,0,0,0,0,0,0,0,0,0,17,123,200,238,248,226,165,63,0,0,0,0,0,0,0, 0,79,240,255,255,255,255,255,255,255,158,6,0,0,0,0,0,75,253,255,255,175,106,95,135,230,255,255,157,0,0,0, 0,13,233,255,248,69,0,0,0,0,14,194,255,255,62,0,0,0,107,255,255,123,0,0,0,0,0,0,28,249,255,166, 0,0,0,176,255,255,23,0,0,0,0,0,0,0,190,255,231,0,0,0,204,255,252,0,0,0,0,0,0,0,0,162, 255,254,4,0,0,197,255,255,16,0,0,0,0,0,0,0,188,255,254,6,0,0,158,255,255,92,0,0,0,0,0,0, 31,250,255,235,0,0,0,75,255,255,229,36,0,0,0,0,18,199,255,255,177,0,0,0,0,190,255,255,244,151,99,96, 142,236,255,255,255,95,0,0,0,0,21,202,255,255,255,255,255,255,239,255,255,227,6,0,0,0,0,0,6,105,197,242, 248,213,133,167,255,255,94,0,0,0,0,0,0,0,0,0,0,0,0,74,255,255,188,0,0,0,0,0,0,0,0,0, 0,0,0,26,235,255,243,32,0,0,0,0,0,0,0,0,0,0,0,2,190,255,255,104,0,0,0,0,0,0,0,0, 0,0,0,0,125,255,255,187,0,0,0,0,0,0,0,0,0,0,0,0,59,252,255,241,30,0,0,0,0,0,0,0, 0,0,0,0,17,225,255,255,99,0,0,0,0,0,0,0,0,0,0,0,0,174,255,255,183,0,0,0,0,0,0,0, 0,0,0,0,0,107,255,255,239,27,0,0,0,0,0,0,0,0,0,0,0,46,247,255,231,77,0,0,0,0,0,0, 0,0,0,0,16,183,241,154,2,0,0,0,0,129,255,255,255,86,0,0,0,0,132,255,255,255,90,0,0,0,0,19, 189,243,160,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,6,168,241,171,9,0,0,0,0,101,255,255,255,114,0,0, 0,0,104,255,255,255,118,0,0,0,0,8,173,243,177,11,0,0,0,0,6,168,241,171,9,0,0,0,0,101,255,255, 255,114,0,0,0,0,104,255,255,255,118,0,0,0,0,8,173,243,177,11,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,171,242,171,6,0,0,0,0, 85,255,255,255,88,0,0,0,0,80,255,255,255,118,0,0,0,0,4,172,250,255,87,0,0,0,0,0,0,129,251,23, 0,0,0,0,0,8,227,173,0,0,0,0,0,0,149,246,34,0,0,0,0,0,40,254,91,0,0,0,0,0,0,0, 56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,137,0,0,0,0,0,0,0,0,0,0,0,0, 0,19,137,245,244,0,0,0,0,0,0,0,0,0,0,0,14,128,241,255,255,216,0,0,0,0,0,0,0,0,0,10, 118,237,255,255,245,140,20,0,0,0,0,0,0,0,6,108,231,255,255,247,143,23,0,0,0,0,0,0,0,4,99,225, 255,255,248,147,25,0,0,0,0,0,0,0,0,57,218,255,255,240,148,27,0,0,0,0,0,0,0,0,0,0,108,255, 255,252,146,42,0,0,0,0,0,0,0,0,0,0,0,1,83,213,255,255,253,166,39,0,0,0,0,0,0,0,0,0, 0,0,2,92,220,255,255,252,161,35,0,0,0,0,0,0,0,0,0,0,0,4,102,227,255,255,251,156,31,0,0,0, 0,0,0,0,0,0,0,0,7,112,233,255,255,249,129,0,0,0,0,0,0,0,0,0,0,0,0,11,121,238,255,242, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,131,231,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,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,92,0,0,112,255,255,255,255,255,255,255,255,255,255,255,255,255,92,0,0,38,88,88,88,88, 88,88,88,88,88,88,88,88,88,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,38,88,88,88,88,88,88,88,88,88,88,88,88,88,31,0,0,112, 255,255,255,255,255,255,255,255,255,255,255,255,255,92,0,0,112,255,255,255,255,255,255,255,255,255,255,255,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,0,0,0,0,0,0,0,0,0,0,141,20,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,255,243,130,15,0,0,0,0,0,0,0,0,0,0,0,0,0,228,255,255,238, 120,11,0,0,0,0,0,0,0,0,0,0,0,22,143,247,255,255,232,110,7,0,0,0,0,0,0,0,0,0,0,0, 26,148,248,255,255,226,100,4,0,0,0,0,0,0,0,0,0,0,0,29,153,250,255,255,219,90,2,0,0,0,0,0, 0,0,0,0,0,0,32,155,244,255,255,211,47,0,0,0,0,0,0,0,0,0,0,0,46,153,254,255,255,92,0,0, 0,0,0,0,0,0,0,44,173,254,255,255,206,74,0,0,0,0,0,0,0,0,41,168,254,255,255,214,84,1,0,0, 0,0,0,0,0,37,164,253,255,255,222,94,2,0,0,0,0,0,0,0,0,140,252,255,255,228,104,5,0,0,0,0, 0,0,0,0,0,0,254,255,234,114,8,0,0,0,0,0,0,0,0,0,0,0,0,240,124,12,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,76,164,220,246,242,201,119,13,0,0,0,0,0,0,40,203,255,255,255, 255,255,255,255,222,39,0,0,0,0,0,191,255,255,197,116,95,128,224,255,255,220,8,0,0,0,0,52,205,73,0,0, 0,0,8,192,255,255,100,0,0,0,0,0,0,0,0,0,0,0,0,42,255,255,165,0,0,0,0,0,0,0,0,0, 0,0,0,6,255,255,181,0,0,0,0,0,0,0,0,0,0,0,0,39,255,255,158,0,0,0,0,0,0,0,0,0, 0,0,0,156,255,255,85,0,0,0,0,0,0,0,0,0,0,0,98,255,255,205,1,0,0,0,0,0,0,0,0,0, 1,127,255,255,232,41,0,0,0,0,0,0,0,0,0,4,173,255,255,215,38,0,0,0,0,0,0,0,0,0,0,141, 255,255,182,12,0,0,0,0,0,0,0,0,0,0,0,232,255,206,4,0,0,0,0,0,0,0,0,0,0,0,0,228, 255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,208,255,102,0,0,0,0,0,0,0,0,0,0,0,0,0,122, 164,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,90,232,222,65,0,0,0,0,0,0,0,0,0,0,0,7,246,255, 255,217,0,0,0,0,0,0,0,0,0,0,0,8,248,255,255,220,0,0,0,0,0,0,0,0,0,0,0,0,96,234, 226,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,129,190,231,249,239,217,168,98,17,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,178,255,255,255,255,255,255,255,255,255,241,133,12,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,110,248,255,255,237,161,122,96,102,123,175,237,255,255,214,42,0,0,0, 0,0,0,0,0,0,0,0,0,1,144,255,255,224,88,4,0,0,0,0,0,0,7,98,228,255,238,40,0,0,0,0, 0,0,0,0,0,0,0,124,255,255,162,8,0,0,0,0,0,0,0,0,0,0,19,188,255,221,16,0,0,0,0,0, 0,0,0,0,62,254,255,142,0,0,0,0,0,0,0,0,0,0,0,0,0,12,206,255,141,0,0,0,0,0,0,0, 0,3,213,255,186,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,254,249,29,0,0,0,0,0,0,0,87, 255,248,27,0,0,0,0,0,5,95,181,231,250,244,204,113,7,0,0,178,255,114,0,0,0,0,0,0,0,191,255,146, 0,0,0,0,0,40,216,255,255,255,255,255,255,245,5,0,0,100,255,193,0,0,0,0,0,0,20,252,255,46,0,0, 0,0,54,237,255,253,177,110,101,190,255,185,0,0,0,40,255,238,0,0,0,0,0,0,77,255,228,0,0,0,0,11, 222,255,228,54,0,0,0,166,255,120,0,0,0,18,255,255,6,0,0,0,0,0,121,255,177,0,0,0,0,134,255,244, 40,0,0,0,0,232,255,54,0,0,0,4,255,255,22,0,0,0,0,0,143,255,155,0,0,0,3,230,255,124,0,0, 0,0,45,255,241,3,0,0,0,18,255,255,9,0,0,0,0,0,150,255,141,0,0,0,56,255,254,25,0,0,0,0, 112,255,180,0,0,0,0,59,255,234,0,0,0,0,0,0,132,255,159,0,0,0,91,255,227,0,0,0,0,0,184,255, 124,0,0,0,0,130,255,176,0,0,0,0,0,0,111,255,179,0,0,0,107,255,223,0,0,0,0,32,251,255,94,0, 0,0,9,230,255,90,0,0,0,0,0,0,80,255,226,0,0,0,79,255,253,34,0,0,4,185,255,255,122,0,0,1, 158,255,225,7,0,0,0,0,0,0,16,252,255,41,0,0,24,248,255,217,107,112,212,255,146,255,239,119,102,193,255,253, 75,0,0,0,0,0,0,0,0,198,255,129,0,0,0,142,255,255,255,255,255,148,1,211,255,255,255,255,250,101,0,0, 0,0,0,0,0,0,0,103,255,241,18,0,0,1,121,228,246,206,96,0,0,37,186,244,232,166,47,0,0,0,0,0, 0,0,0,0,0,5,224,255,162,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,87,255,255,119,0,0,0,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,150,6,0,0,0,0,0,0,0,0,0,0,0,0,6,44,0,0,0,0,0,0,0,0,0,0, 0,4,172,255,255,220,99,6,0,0,0,0,0,0,0,0,24,112,227,250,23,0,0,0,0,0,0,0,0,0,0,0, 1,125,253,255,255,244,183,129,107,91,102,117,155,204,253,255,255,179,21,0,0,0,0,0,0,0,0,0,0,0,0,0, 43,172,254,255,255,255,255,255,255,255,255,255,255,197,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 33,106,175,213,235,251,237,218,180,118,47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,222,255,255,147,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,255,255,255,236,6,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,158,255,255,255,255,81,0,0,0,0,0,0,0,0,0,0,0,0,0,10,242,255,180,247,255,176,0,0,0,0, 0,0,0,0,0,0,0,0,0,92,255,255,93,174,255,250,20,0,0,0,0,0,0,0,0,0,0,0,0,187,255,248, 15,82,255,255,110,0,0,0,0,0,0,0,0,0,0,0,28,253,255,173,0,7,240,255,205,0,0,0,0,0,0,0, 0,0,0,0,121,255,255,82,0,0,158,255,255,44,0,0,0,0,0,0,0,0,0,0,216,255,240,7,0,0,68,255, 255,139,0,0,0,0,0,0,0,0,0,55,255,255,157,0,0,0,3,231,255,231,3,0,0,0,0,0,0,0,0,151, 255,255,66,0,0,0,0,145,255,255,73,0,0,0,0,0,0,0,7,238,255,229,2,0,0,0,0,55,255,255,169,0, 0,0,0,0,0,0,85,255,255,141,0,0,0,0,0,0,220,255,248,16,0,0,0,0,0,0,180,255,255,110,64,64, 64,64,64,64,170,255,255,103,0,0,0,0,0,23,251,255,255,255,255,255,255,255,255,255,255,255,255,198,0,0,0,0, 0,114,255,255,255,255,255,255,255,255,255,255,255,255,255,255,38,0,0,0,0,209,255,255,42,0,0,0,0,0,0,0, 0,124,255,255,132,0,0,0,48,255,255,209,0,0,0,0,0,0,0,0,0,34,255,255,225,1,0,0,143,255,255,120, 0,0,0,0,0,0,0,0,0,0,200,255,255,66,0,4,233,255,254,31,0,0,0,0,0,0,0,0,0,0,110,255, 255,161,0,77,255,255,197,0,0,0,0,0,0,0,0,0,0,0,23,252,255,244,12,172,255,244,80,0,0,0,0,0, 0,0,0,0,0,0,0,149,254,255,96,0,0,156,255,255,255,255,255,255,249,234,197,139,43,0,0,0,0,0,0,0, 156,255,255,255,255,255,255,255,255,255,255,253,137,0,0,0,0,0,0,156,255,255,156,88,88,88,94,122,199,255,255,255, 119,0,0,0,0,0,156,255,255,104,0,0,0,0,0,0,125,255,255,242,7,0,0,0,0,156,255,255,104,0,0,0, 0,0,0,2,227,255,255,61,0,0,0,0,156,255,255,104,0,0,0,0,0,0,0,177,255,255,84,0,0,0,0,156, 255,255,104,0,0,0,0,0,0,0,181,255,255,58,0,0,0,0,156,255,255,104,0,0,0,0,0,0,10,238,255,235, 10,0,0,0,0,156,255,255,104,0,0,0,0,0,6,162,255,255,103,0,0,0,0,0,156,255,255,156,88,88,88,97, 134,222,255,251,112,0,0,0,0,0,0,156,255,255,255,255,255,255,255,255,255,230,71,0,0,0,0,0,0,0,156,255, 255,255,255,255,255,255,255,255,255,254,180,30,0,0,0,0,0,156,255,255,104,0,0,0,0,13,60,173,255,255,229,25, 0,0,0,0,156,255,255,104,0,0,0,0,0,0,0,162,255,255,155,0,0,0,0,156,255,255,104,0,0,0,0,0, 0,0,37,255,255,231,0,0,0,0,156,255,255,104,0,0,0,0,0,0,0,1,253,255,255,3,0,0,0,156,255,255, 104,0,0,0,0,0,0,0,25,255,255,244,0,0,0,0,156,255,255,104,0,0,0,0,0,0,0,84,255,255,196,0, 0,0,0,156,255,255,104,0,0,0,0,0,0,31,223,255,255,103,0,0,0,0,156,255,255,156,88,88,88,88,103,155, 244,255,255,199,4,0,0,0,0,156,255,255,255,255,255,255,255,255,255,255,255,183,19,0,0,0,0,0,156,255,255,255, 255,255,255,255,243,218,161,69,0,0,0,0,0,0,0,0,0,0,0,20,108,188,225,248,237,211,146,59,0,0,0,0, 0,0,0,0,109,239,255,255,255,255,255,255,255,255,183,29,0,0,0,0,3,157,255,255,255,198,123,95,103,154,240,255, 255,229,9,0,0,0,138,255,255,235,74,0,0,0,0,0,16,161,255,122,0,0,0,61,253,255,245,49,0,0,0,0, 0,0,0,0,43,1,0,0,0,188,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,41,255,255,243,12,0, 0,0,0,0,0,0,0,0,0,0,0,0,112,255,255,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,255, 255,111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,255,255,75,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,218,255,255,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,219,255,255,60,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,202,255,255,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,174,255,255,115,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,118,255,255,173,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,255,255,247, 17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,206,255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,83,255,255,249,58,0,0,0,0,0,0,0,0,102,48,0,0,0,0,170,255,255,240,80,0,0,0,0,0,13,157, 255,235,39,0,0,0,11,189,255,255,255,199,122,94,104,148,233,255,255,206,19,0,0,0,0,8,141,251,255,255,255,255, 255,255,255,254,159,16,0,0,0,0,0,0,0,38,130,203,233,250,231,205,137,49,0,0,0,0,0,176,255,255,255,255, 255,255,255,240,216,162,82,4,0,0,0,0,0,0,0,0,176,255,255,255,255,255,255,255,255,255,255,255,215,71,0,0, 0,0,0,0,0,176,255,255,145,88,88,88,88,105,144,226,255,255,254,113,0,0,0,0,0,0,176,255,255,88,0,0, 0,0,0,0,4,117,252,255,255,96,0,0,0,0,0,176,255,255,88,0,0,0,0,0,0,0,0,90,255,255,244,34, 0,0,0,0,176,255,255,88,0,0,0,0,0,0,0,0,0,162,255,255,154,0,0,0,0,176,255,255,88,0,0,0, 0,0,0,0,0,0,37,254,255,248,17,0,0,0,176,255,255,88,0,0,0,0,0,0,0,0,0,0,199,255,255,81, 0,0,0,176,255,255,88,0,0,0,0,0,0,0,0,0,0,143,255,255,144,0,0,0,176,255,255,88,0,0,0,0, 0,0,0,0,0,0,104,255,255,174,0,0,0,176,255,255,88,0,0,0,0,0,0,0,0,0,0,88,255,255,194,0, 0,0,176,255,255,88,0,0,0,0,0,0,0,0,0,0,89,255,255,193,0,0,0,176,255,255,88,0,0,0,0,0, 0,0,0,0,0,104,255,255,174,0,0,0,176,255,255,88,0,0,0,0,0,0,0,0,0,0,142,255,255,145,0,0, 0,176,255,255,88,0,0,0,0,0,0,0,0,0,0,198,255,255,82,0,0,0,176,255,255,88,0,0,0,0,0,0, 0,0,0,35,254,255,248,17,0,0,0,176,255,255,88,0,0,0,0,0,0,0,0,0,159,255,255,155,0,0,0,0, 176,255,255,88,0,0,0,0,0,0,0,0,87,255,255,245,35,0,0,0,0,176,255,255,88,0,0,0,0,0,0,3, 114,251,255,255,98,0,0,0,0,0,176,255,255,145,88,88,88,88,103,142,223,255,255,254,118,0,0,0,0,0,0,176, 255,255,255,255,255,255,255,255,255,255,255,218,75,0,0,0,0,0,0,0,176,255,255,255,255,255,255,255,241,217,165,86, 5,0,0,0,0,0,0,0,0,152,255,255,255,255,255,255,255,255,255,255,255,255,40,0,0,0,152,255,255,255,255,255, 255,255,255,255,255,255,255,40,0,0,0,152,255,255,164,88,88,88,88,88,88,88,88,88,13,0,0,0,152,255,255,116, 0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255, 255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0, 152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0, 0,0,152,255,255,164,88,88,88,88,88,88,88,23,0,0,0,0,0,152,255,255,255,255,255,255,255,255,255,255,68,0, 0,0,0,0,152,255,255,255,255,255,255,255,255,255,255,68,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0, 0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0, 0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0, 0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0, 0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,164,88,88,88,88,88,88,88,88,88,14,0,0,0,152,255,255, 255,255,255,255,255,255,255,255,255,255,42,0,0,0,152,255,255,255,255,255,255,255,255,255,255,255,255,40,0,0,0,180, 255,255,255,255,255,255,255,255,255,255,255,164,0,0,0,180,255,255,255,255,255,255,255,255,255,255,255,164,0,0,0,180, 255,255,143,88,88,88,88,88,88,88,88,56,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,255,255,255,255,255,255,255,255,64,0,0,0,0,180,255,255,255,255,255,255,255,255,255,255,64,0,0,0,0,180, 255,255,143,88,88,88,88,88,88,88,22,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180, 255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,35,115,186,221,243,246,228,200,139,63,0,0,0,0,0,0,0,0,0,27,159,252,255,255,255,255,255,255, 255,255,255,201,56,0,0,0,0,0,0,57,232,255,255,251,189,124,98,95,120,184,250,255,255,251,63,0,0,0,0,48, 246,255,255,183,36,0,0,0,0,0,0,23,149,252,214,6,0,0,0,12,221,255,255,155,2,0,0,0,0,0,0,0, 0,0,38,25,0,0,0,0,119,255,255,204,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,233,255,255, 70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,255,255,222,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,122,255,255,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,255,255,117,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,174,255,255,99,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,173,255,255,102,0,0,0,0,0,0,0,0,224,255,255,255,255,255,172,0,0,152,255,255,121,0,0, 0,0,0,0,0,0,220,255,255,255,255,255,172,0,0,120,255,255,167,0,0,0,0,0,0,0,0,51,88,88,98,255, 255,172,0,0,54,255,255,231,3,0,0,0,0,0,0,0,0,0,0,16,255,255,172,0,0,2,228,255,255,85,0,0, 0,0,0,0,0,0,0,0,16,255,255,172,0,0,0,109,255,255,218,12,0,0,0,0,0,0,0,0,0,16,255,255, 172,0,0,0,7,212,255,255,175,7,0,0,0,0,0,0,0,0,16,255,255,172,0,0,0,0,36,239,255,255,201,53, 0,0,0,0,0,0,6,108,255,255,172,0,0,0,0,0,45,220,255,255,255,203,134,105,92,109,157,235,255,255,255,160, 0,0,0,0,0,0,16,141,246,255,255,255,255,255,255,255,255,255,237,119,5,0,0,0,0,0,0,0,0,23,103,173, 217,238,251,237,212,160,91,8,0,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0, 0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0, 0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0, 0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0, 0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0, 172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0, 0,0,44,255,255,220,0,0,0,0,172,255,255,148,88,88,88,88,88,88,88,88,88,116,255,255,220,0,0,0,0,172, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,220,0,0,0,0,172,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255, 255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0, 44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255, 92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44, 255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92, 0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255, 255,220,0,0,0,0,172,255,255,92,0,0,0,0,0,0,0,0,0,44,255,255,220,0,0,0,0,60,255,255,204,0, 0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60, 255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0, 0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255, 255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0, 0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255, 204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0,0,60,255,255,204,0,0,0,0, 0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240,0,0, 0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240,0, 0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240, 0,0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255, 240,0,0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,24,255,255,240,0,0,0,0,0,0,0,25,255, 255,235,0,0,0,0,0,0,0,35,255,255,222,0,0,0,0,0,0,0,64,255,255,190,0,0,0,0,0,0,0,124, 255,255,138,0,0,0,0,0,0,18,229,255,255,55,0,0,21,137,101,112,218,255,255,184,0,0,0,84,255,255,255,255, 255,216,23,0,0,0,71,213,246,247,214,129,11,0,0,0,0,0,0,240,255,255,20,0,0,0,0,0,0,14,188,253, 255,150,0,0,0,240,255,255,20,0,0,0,0,0,0,159,255,255,217,10,0,0,0,240,255,255,20,0,0,0,0,0, 76,255,255,251,53,0,0,0,0,240,255,255,20,0,0,0,0,18,231,255,255,125,0,0,0,0,0,240,255,255,20,0, 0,0,0,168,255,255,199,3,0,0,0,0,0,240,255,255,20,0,0,0,85,255,255,244,35,0,0,0,0,0,0,240, 255,255,20,0,0,23,235,255,255,100,0,0,0,0,0,0,0,240,255,255,20,0,0,176,255,255,177,0,0,0,0,0, 0,0,0,240,255,255,20,0,95,255,255,233,21,0,0,0,0,0,0,0,0,240,255,255,101,119,245,255,253,73,0,0, 0,0,0,0,0,0,0,240,255,255,255,255,255,255,147,0,0,0,0,0,0,0,0,0,0,240,255,255,255,255,255,255, 255,110,0,0,0,0,0,0,0,0,0,240,255,255,20,8,140,255,255,249,44,0,0,0,0,0,0,0,0,240,255,255, 20,0,2,197,255,255,203,3,0,0,0,0,0,0,0,240,255,255,20,0,0,38,246,255,255,120,0,0,0,0,0,0, 0,240,255,255,20,0,0,0,114,255,255,248,41,0,0,0,0,0,0,240,255,255,20,0,0,0,2,196,255,255,199,2, 0,0,0,0,0,240,255,255,20,0,0,0,0,37,246,255,255,115,0,0,0,0,0,240,255,255,20,0,0,0,0,0, 113,255,255,246,37,0,0,0,0,240,255,255,20,0,0,0,0,0,2,196,255,255,195,1,0,0,0,240,255,255,20,0, 0,0,0,0,0,37,246,255,255,110,0,0,0,240,255,255,20,0,0,0,0,0,0,0,94,246,255,244,34,0,0,180, 255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0, 0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0, 0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0, 0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255, 80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0, 0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0, 0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180, 255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0,0,0,0,0,0,0,0,0,0,180,255,255,80,0, 0,0,0,0,0,0,0,0,0,180,255,255,190,160,160,160,160,160,160,160,117,0,0,180,255,255,255,255,255,255,255,255, 255,255,188,0,0,180,255,255,255,255,255,255,255,255,255,255,188,0,0,60,255,255,227,25,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,16,220,255,255,76,0,0,0,0,60,255,255,255,161,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,142,255,255,255,76,0,0,0,0,60,255,255,255,254,54,0,0,0,0,0,0,0,0,0,0,0,0,0, 37,250,255,255,255,76,0,0,0,0,60,255,255,255,255,199,1,0,0,0,0,0,0,0,0,0,0,0,0,178,255,255, 255,255,76,0,0,0,0,60,255,255,241,255,255,93,0,0,0,0,0,0,0,0,0,0,0,67,255,255,238,255,255,76, 0,0,0,0,60,255,255,133,248,255,229,11,0,0,0,0,0,0,0,0,0,3,210,255,250,130,255,255,76,0,0,0, 0,60,255,255,107,134,255,255,132,0,0,0,0,0,0,0,0,0,104,255,255,142,96,255,255,76,0,0,0,0,60,255, 255,108,11,228,255,247,32,0,0,0,0,0,0,0,15,234,255,236,16,96,255,255,76,0,0,0,0,60,255,255,108,0, 91,255,255,172,0,0,0,0,0,0,0,140,255,255,107,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,196,255, 255,64,0,0,0,0,0,36,249,255,213,4,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,50,253,255,208,3, 0,0,0,0,176,255,255,72,0,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,0,154,255,255,103,0,0,0, 64,255,255,182,0,0,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,0,21,239,255,235,16,0,2,208,255,251, 41,0,0,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,0,0,111,255,255,143,0,101,255,255,147,0,0,0, 0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,0,0,4,213,255,250,36,228,255,238,19,0,0,0,0,96,255, 255,76,0,0,0,0,60,255,255,108,0,0,0,0,0,68,255,255,214,255,255,112,0,0,0,0,0,96,255,255,76,0, 0,0,0,60,255,255,108,0,0,0,0,0,0,174,255,255,255,217,5,0,0,0,0,0,96,255,255,76,0,0,0,0, 60,255,255,108,0,0,0,0,0,0,33,247,255,255,77,0,0,0,0,0,0,96,255,255,76,0,0,0,0,60,255,255, 108,0,0,0,0,0,0,0,95,209,139,0,0,0,0,0,0,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,96,255,255,76,0,0,0,0,60,255,255,108,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,96,255,255,76,0,0,0,0,140,255,221,29,0,0,0,0,0,0,0,0,0,0,44, 255,255,124,0,0,0,0,140,255,255,195,3,0,0,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255, 255,129,0,0,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,255,252,62,0,0,0,0,0,0,0, 0,44,255,255,124,0,0,0,0,140,255,255,255,255,226,18,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140, 255,255,202,255,255,174,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,51,233,255,255,105,0,0,0, 0,0,0,44,255,255,124,0,0,0,0,140,255,255,28,72,254,255,247,44,0,0,0,0,0,44,255,255,124,0,0,0, 0,140,255,255,28,0,141,255,255,212,9,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,28,0,6,204,255,255, 152,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,28,0,0,36,243,255,255,82,0,0,0,44,255,255,124,0, 0,0,0,140,255,255,28,0,0,0,93,255,255,238,29,0,0,44,255,255,124,0,0,0,0,140,255,255,28,0,0,0, 0,162,255,255,194,3,0,44,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,12,219,255,255,128,0,44,255,255, 124,0,0,0,0,140,255,255,28,0,0,0,0,0,51,249,255,252,61,44,255,255,124,0,0,0,0,140,255,255,28,0, 0,0,0,0,0,114,255,255,226,59,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,1,182,255,255,198, 255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,0,22,231,255,255,255,255,124,0,0,0,0,140,255,255, 28,0,0,0,0,0,0,0,0,68,254,255,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,0,0,0, 136,255,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,0,0,0,4,200,255,255,124,0,0,0,0,140, 255,255,28,0,0,0,0,0,0,0,0,0,0,32,221,255,124,0,0,0,0,0,0,0,0,1,73,154,212,237,248,226, 192,118,31,0,0,0,0,0,0,0,0,0,0,0,0,58,204,255,255,255,255,255,255,255,255,249,140,12,0,0,0,0, 0,0,0,0,0,95,251,255,255,229,148,107,95,120,187,252,255,255,204,22,0,0,0,0,0,0,0,81,255,255,253,125, 6,0,0,0,0,0,44,203,255,255,208,7,0,0,0,0,0,27,238,255,255,94,0,0,0,0,0,0,0,0,13,201, 255,255,145,0,0,0,0,0,147,255,255,164,0,0,0,0,0,0,0,0,0,0,38,247,255,250,29,0,0,0,15,245, 255,254,37,0,0,0,0,0,0,0,0,0,0,0,159,255,255,137,0,0,0,80,255,255,197,0,0,0,0,0,0,0, 0,0,0,0,0,65,255,255,211,0,0,0,143,255,255,139,0,0,0,0,0,0,0,0,0,0,0,0,10,251,255,254, 20,0,0,174,255,255,100,0,0,0,0,0,0,0,0,0,0,0,0,0,221,255,255,49,0,0,194,255,255,84,0,0, 0,0,0,0,0,0,0,0,0,0,0,205,255,255,69,0,0,194,255,255,83,0,0,0,0,0,0,0,0,0,0,0, 0,0,204,255,255,70,0,0,175,255,255,99,0,0,0,0,0,0,0,0,0,0,0,0,0,221,255,255,50,0,0,144, 255,255,137,0,0,0,0,0,0,0,0,0,0,0,0,9,250,255,255,21,0,0,82,255,255,195,0,0,0,0,0,0, 0,0,0,0,0,0,63,255,255,213,0,0,0,17,247,255,254,34,0,0,0,0,0,0,0,0,0,0,0,156,255,255, 140,0,0,0,0,150,255,255,161,0,0,0,0,0,0,0,0,0,0,35,246,255,251,31,0,0,0,0,29,240,255,255, 91,0,0,0,0,0,0,0,0,11,198,255,255,148,0,0,0,0,0,0,84,255,255,252,120,5,0,0,0,0,0,41, 200,255,255,210,8,0,0,0,0,0,0,0,98,251,255,255,227,147,106,95,119,183,251,255,255,205,23,0,0,0,0,0, 0,0,0,0,59,205,255,255,255,255,255,255,255,255,249,142,12,0,0,0,0,0,0,0,0,0,0,0,1,73,155,213, 238,248,227,193,119,31,0,0,0,0,0,0,0,0,0,144,255,255,255,255,255,255,243,222,172,97,9,0,0,0,0,0, 0,144,255,255,255,255,255,255,255,255,255,255,228,66,0,0,0,0,0,144,255,255,166,88,88,88,101,144,230,255,255,248, 60,0,0,0,0,144,255,255,120,0,0,0,0,0,10,176,255,255,211,1,0,0,0,144,255,255,120,0,0,0,0,0, 0,13,236,255,255,51,0,0,0,144,255,255,120,0,0,0,0,0,0,0,164,255,255,104,0,0,0,144,255,255,120,0, 0,0,0,0,0,0,137,255,255,122,0,0,0,144,255,255,120,0,0,0,0,0,0,0,156,255,255,106,0,0,0,144, 255,255,120,0,0,0,0,0,0,5,226,255,255,59,0,0,0,144,255,255,120,0,0,0,0,0,0,135,255,255,221,2, 0,0,0,144,255,255,120,0,0,0,15,65,177,255,255,254,79,0,0,0,0,144,255,255,255,255,255,255,255,255,255,255, 250,107,0,0,0,0,0,144,255,255,255,255,255,255,255,255,247,169,48,0,0,0,0,0,0,144,255,255,166,88,88,87, 74,51,6,0,0,0,0,0,0,0,0,144,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,255,255, 120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,144,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,255,255,120,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,144,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,255,255,120,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,144,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 55,139,205,232,249,230,201,130,44,0,0,0,0,0,0,0,0,0,0,0,0,0,35,179,255,255,255,255,255,255,255,255, 254,163,24,0,0,0,0,0,0,0,0,0,0,59,238,255,255,240,162,112,94,114,171,245,255,255,226,40,0,0,0,0, 0,0,0,0,43,245,255,255,157,16,0,0,0,0,0,25,174,255,255,232,23,0,0,0,0,0,0,6,212,255,255,141, 0,0,0,0,0,0,0,0,2,164,255,255,184,0,0,0,0,0,0,99,255,255,208,4,0,0,0,0,0,0,0,0, 0,12,225,255,255,64,0,0,0,0,0,213,255,255,83,0,0,0,0,0,0,0,0,0,0,0,111,255,255,178,0,0, 0,0,34,255,255,240,5,0,0,0,0,0,0,0,0,0,0,0,19,254,255,247,7,0,0,0,98,255,255,187,0,0, 0,0,0,0,0,0,0,0,0,0,0,216,255,255,61,0,0,0,130,255,255,148,0,0,0,0,0,0,0,0,0,0, 0,0,0,176,255,255,95,0,0,0,150,255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,161,255,255,115,0, 0,0,150,255,255,131,0,0,0,0,0,0,0,0,0,0,0,0,0,159,255,255,119,0,0,0,130,255,255,147,0,0, 0,0,0,0,0,0,0,0,0,0,0,176,255,255,96,0,0,0,99,255,255,185,0,0,0,0,0,0,0,0,0,0, 0,0,0,214,255,255,72,0,0,0,36,255,255,239,4,0,0,0,0,0,0,0,0,0,0,0,17,254,255,252,18,0, 0,0,0,217,255,255,81,0,0,0,0,0,0,0,0,0,0,0,108,255,255,193,0,0,0,0,0,102,255,255,205,3, 0,0,0,0,0,0,0,0,0,11,223,255,255,99,0,0,0,0,0,7,214,255,255,138,0,0,0,0,0,0,0,0, 1,160,255,255,211,3,0,0,0,0,0,0,45,246,255,255,153,14,0,0,0,0,0,22,170,255,255,248,60,0,0,0, 0,0,0,0,0,62,238,255,255,238,159,111,94,113,168,243,255,255,252,81,0,0,0,0,0,0,0,0,0,0,36,180, 255,255,255,255,255,255,255,255,255,255,245,52,0,0,0,0,0,0,0,0,0,0,0,0,55,140,206,233,250,234,208,147, 141,255,255,232,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,216,18,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,188,255,255,196,8,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,16,216,255,255,171,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,36,232,255,255,142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,105,124,123, 25,0,0,144,255,255,255,255,255,253,239,214,162,79,4,0,0,0,0,0,0,144,255,255,255,255,255,255,255,255,255,255, 206,38,0,0,0,0,0,144,255,255,166,88,88,90,107,158,242,255,255,228,21,0,0,0,0,144,255,255,120,0,0,0, 0,0,24,209,255,255,143,0,0,0,0,144,255,255,120,0,0,0,0,0,0,53,255,255,227,0,0,0,0,144,255,255, 120,0,0,0,0,0,0,0,236,255,255,9,0,0,0,144,255,255,120,0,0,0,0,0,0,0,222,255,255,16,0,0, 0,144,255,255,120,0,0,0,0,0,0,8,247,255,241,1,0,0,0,144,255,255,120,0,0,0,0,0,0,97,255,255, 171,0,0,0,0,144,255,255,120,0,0,0,0,0,63,241,255,253,53,0,0,0,0,144,255,255,166,88,88,92,116,183, 254,255,255,115,0,0,0,0,0,144,255,255,255,255,255,255,255,255,255,229,87,0,0,0,0,0,0,144,255,255,255,255, 255,255,255,255,143,8,0,0,0,0,0,0,0,144,255,255,120,0,2,84,253,255,247,51,0,0,0,0,0,0,0,144, 255,255,120,0,0,0,138,255,255,213,7,0,0,0,0,0,0,144,255,255,120,0,0,0,8,215,255,255,139,0,0,0, 0,0,0,144,255,255,120,0,0,0,0,55,252,255,253,59,0,0,0,0,0,144,255,255,120,0,0,0,0,0,137,255, 255,220,11,0,0,0,0,144,255,255,120,0,0,0,0,0,7,214,255,255,150,0,0,0,0,144,255,255,120,0,0,0, 0,0,0,54,252,255,254,69,0,0,0,144,255,255,120,0,0,0,0,0,0,0,136,255,255,226,15,0,0,144,255,255, 120,0,0,0,0,0,0,0,7,183,253,255,160,0,0,0,0,13,120,201,239,249,229,182,105,9,0,0,0,0,0,0, 53,229,255,255,255,255,255,255,255,232,68,0,0,0,0,31,237,255,255,191,108,93,126,220,255,255,196,0,0,0,0,164, 255,255,121,0,0,0,0,3,111,231,59,0,0,0,8,246,255,220,2,0,0,0,0,0,0,0,0,0,0,0,40,255, 255,171,0,0,0,0,0,0,0,0,0,0,0,0,42,255,255,199,0,0,0,0,0,0,0,0,0,0,0,0,12,251, 255,255,111,0,0,0,0,0,0,0,0,0,0,0,0,179,255,255,255,173,53,0,0,0,0,0,0,0,0,0,0,40, 242,255,255,255,255,209,115,19,0,0,0,0,0,0,0,0,44,206,255,255,255,255,255,247,144,10,0,0,0,0,0,0, 0,4,87,188,253,255,255,255,255,219,29,0,0,0,0,0,0,0,0,0,25,113,222,255,255,255,193,0,0,0,0,0, 0,0,0,0,0,0,3,125,255,255,255,45,0,0,0,0,0,0,0,0,0,0,0,0,179,255,255,93,0,0,0,0, 0,0,0,0,0,0,0,0,114,255,255,100,0,0,0,0,0,0,0,0,0,0,0,0,124,255,255,74,0,0,12,113, 17,0,0,0,0,0,0,0,197,255,252,16,0,0,150,255,223,64,0,0,0,0,1,119,255,255,163,0,0,2,198,255, 255,255,190,114,92,114,200,255,255,232,26,0,0,0,8,146,251,255,255,255,255,255,255,255,218,42,0,0,0,0,0,0, 36,129,201,233,250,234,188,104,6,0,0,0,0,152,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,44,152,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,44,52,88,88,88,88,88,124,255,255,224,88,88,88,88,88,88,15, 0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0, 0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0, 0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0, 0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255, 208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56, 255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0, 0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0, 0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0, 0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0, 0,0,0,0,0,0,0,56,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,208,0,0,0,0, 0,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0, 0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136, 255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255, 88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0, 0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172, 255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0, 0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0, 0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0, 0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0, 0,0,0,165,255,255,91,0,0,0,0,0,0,0,0,0,0,139,255,255,117,0,0,0,0,145,255,255,109,0,0,0, 0,0,0,0,0,0,0,157,255,255,96,0,0,0,0,101,255,255,158,0,0,0,0,0,0,0,0,0,0,207,255,255, 53,0,0,0,0,32,254,255,237,9,0,0,0,0,0,0,0,0,40,254,255,236,2,0,0,0,0,0,178,255,255,134, 0,0,0,0,0,0,0,1,183,255,255,129,0,0,0,0,0,0,42,245,255,254,120,2,0,0,0,0,11,161,255,255, 223,16,0,0,0,0,0,0,0,84,250,255,255,215,132,97,101,145,231,255,255,237,49,0,0,0,0,0,0,0,0,0, 62,219,255,255,255,255,255,255,255,255,200,38,0,0,0,0,0,0,0,0,0,0,0,5,94,174,224,246,243,219,161,76, 0,0,0,0,0,0,0,185,255,247,93,0,0,0,0,0,0,0,0,0,0,0,34,229,255,244,11,89,255,255,211,0, 0,0,0,0,0,0,0,0,0,0,138,255,255,160,0,9,241,255,255,44,0,0,0,0,0,0,0,0,0,1,225,255, 255,65,0,0,155,255,255,134,0,0,0,0,0,0,0,0,0,60,255,255,224,1,0,0,59,255,255,222,0,0,0,0, 0,0,0,0,0,149,255,255,130,0,0,0,0,219,255,255,57,0,0,0,0,0,0,0,4,234,255,255,36,0,0,0, 0,125,255,255,146,0,0,0,0,0,0,0,71,255,255,196,0,0,0,0,0,31,254,255,232,3,0,0,0,0,0,0, 161,255,255,101,0,0,0,0,0,0,190,255,255,70,0,0,0,0,0,8,241,255,246,15,0,0,0,0,0,0,95,255, 255,159,0,0,0,0,0,83,255,255,166,0,0,0,0,0,0,0,12,244,255,241,8,0,0,0,0,172,255,255,71,0, 0,0,0,0,0,0,0,161,255,255,82,0,0,0,13,247,255,229,2,0,0,0,0,0,0,0,0,65,255,255,172,0, 0,0,94,255,255,136,0,0,0,0,0,0,0,0,0,1,224,255,247,14,0,0,183,255,255,41,0,0,0,0,0,0, 0,0,0,0,131,255,255,95,0,20,251,255,202,0,0,0,0,0,0,0,0,0,0,0,36,255,255,185,0,106,255,255, 106,0,0,0,0,0,0,0,0,0,0,0,0,196,255,252,20,193,255,249,18,0,0,0,0,0,0,0,0,0,0,0, 0,101,255,255,115,253,255,172,0,0,0,0,0,0,0,0,0,0,0,0,0,15,246,255,234,255,255,77,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,166,255,255,255,233,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,255, 255,255,142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,228,255,255,47,0,0,0,0,0,0,0,0,166, 255,253,138,0,0,0,0,0,0,0,0,0,32,229,251,120,0,0,0,0,0,0,0,0,0,53,237,255,228,0,88,255, 255,242,5,0,0,0,0,0,0,0,0,129,255,255,233,2,0,0,0,0,0,0,0,0,151,255,255,151,0,16,251,255, 255,63,0,0,0,0,0,0,0,0,211,255,255,255,62,0,0,0,0,0,0,0,0,222,255,255,74,0,0,190,255,255, 134,0,0,0,0,0,0,0,36,255,255,255,255,144,0,0,0,0,0,0,0,37,255,255,245,8,0,0,113,255,255,205, 0,0,0,0,0,0,0,119,255,255,232,255,225,0,0,0,0,0,0,0,108,255,255,176,0,0,0,36,255,255,254,22, 0,0,0,0,0,0,201,255,210,142,255,255,51,0,0,0,0,0,0,179,255,255,99,0,0,0,0,215,255,255,92,0, 0,0,0,0,27,254,255,128,62,255,255,133,0,0,0,0,0,6,244,255,254,24,0,0,0,0,138,255,255,163,0,0, 0,0,0,109,255,255,46,2,233,255,214,0,0,0,0,0,65,255,255,201,0,0,0,0,0,61,255,255,233,1,0,0, 0,0,191,255,220,0,0,155,255,255,40,0,0,0,0,136,255,255,124,0,0,0,0,0,3,236,255,255,49,0,0,0, 20,252,255,139,0,0,73,255,255,122,0,0,0,0,207,255,255,47,0,0,0,0,0,0,163,255,255,120,0,0,0,99, 255,255,57,0,0,6,241,255,203,0,0,0,23,254,255,225,0,0,0,0,0,0,0,86,255,255,191,0,0,0,181,255, 229,1,0,0,0,167,255,255,30,0,0,93,255,255,148,0,0,0,0,0,0,0,14,250,255,250,12,0,14,249,255,149, 0,0,0,0,85,255,255,111,0,0,164,255,255,71,0,0,0,0,0,0,0,0,187,255,255,78,0,90,255,255,67,0, 0,0,0,12,247,255,192,0,1,233,255,243,7,0,0,0,0,0,0,0,0,110,255,255,149,0,172,255,237,4,0,0, 0,0,0,179,255,252,21,50,255,255,173,0,0,0,0,0,0,0,0,0,33,255,255,220,8,244,255,160,0,0,0,0, 0,0,97,255,255,100,121,255,255,96,0,0,0,0,0,0,0,0,0,0,212,255,255,106,255,255,78,0,0,0,0,0, 0,19,252,255,176,188,255,253,22,0,0,0,0,0,0,0,0,0,0,135,255,255,221,255,243,8,0,0,0,0,0,0, 0,190,255,238,241,255,198,0,0,0,0,0,0,0,0,0,0,0,58,255,255,255,255,170,0,0,0,0,0,0,0,0, 109,255,255,255,255,121,0,0,0,0,0,0,0,0,0,0,0,2,234,255,255,255,88,0,0,0,0,0,0,0,0,28, 254,255,255,255,44,0,0,0,0,0,0,0,0,0,0,0,0,160,255,255,248,13,0,0,0,0,0,0,0,0,0,202, 255,255,223,0,0,0,0,0,0,0,0,0,0,0,0,0,83,255,255,180,0,0,0,0,0,0,0,0,0,0,121,255, 255,146,0,0,0,0,0,0,0,66,255,255,245,64,0,0,0,0,0,0,0,0,74,250,255,232,15,0,173,255,255,210, 3,0,0,0,0,0,0,5,217,255,255,95,0,0,32,247,255,255,102,0,0,0,0,0,0,109,255,255,197,1,0,0, 0,131,255,255,232,13,0,0,0,0,15,235,255,253,49,0,0,0,0,10,227,255,255,135,0,0,0,0,138,255,255,150, 0,0,0,0,0,0,89,255,255,247,31,0,0,32,248,255,235,18,0,0,0,0,0,0,0,195,255,255,168,0,0,167, 255,255,101,0,0,0,0,0,0,0,0,50,253,255,255,57,53,254,255,202,2,0,0,0,0,0,0,0,0,0,155,255, 255,200,194,255,254,54,0,0,0,0,0,0,0,0,0,0,21,240,255,255,255,255,156,0,0,0,0,0,0,0,0,0, 0,0,0,117,255,255,255,253,24,0,0,0,0,0,0,0,0,0,0,0,0,165,255,255,255,255,104,0,0,0,0,0, 0,0,0,0,0,0,60,255,255,226,255,255,235,16,0,0,0,0,0,0,0,0,0,2,207,255,254,52,195,255,255,144, 0,0,0,0,0,0,0,0,0,104,255,255,163,0,54,254,255,251,40,0,0,0,0,0,0,0,18,236,255,247,30,0, 0,168,255,255,183,0,0,0,0,0,0,0,149,255,255,134,0,0,0,33,249,255,255,75,0,0,0,0,0,46,252,255, 234,14,0,0,0,0,141,255,255,217,5,0,0,0,0,193,255,255,106,0,0,0,0,0,17,238,255,255,114,0,0,0, 88,255,255,215,4,0,0,0,0,0,0,114,255,255,240,21,0,11,227,255,255,77,0,0,0,0,0,0,0,6,222,255, 255,154,0,133,255,255,173,0,0,0,0,0,0,0,0,0,77,247,255,253,48,191,255,250,121,0,0,0,0,0,0,0, 0,18,212,255,255,78,61,255,255,246,18,0,0,0,0,0,0,0,124,255,255,203,0,0,187,255,255,119,0,0,0,0, 0,0,4,228,255,255,74,0,0,56,255,255,226,4,0,0,0,0,0,88,255,255,199,0,0,0,0,182,255,255,86,0, 0,0,0,0,198,255,255,69,0,0,0,0,52,255,255,198,0,0,0,0,50,255,255,195,0,0,0,0,0,0,178,255, 255,53,0,0,0,161,255,255,65,0,0,0,0,0,0,48,254,255,165,0,0,22,248,255,190,0,0,0,0,0,0,0, 0,173,255,250,26,0,125,255,255,60,0,0,0,0,0,0,0,0,44,254,255,136,8,230,255,186,0,0,0,0,0,0, 0,0,0,0,169,255,244,122,255,255,56,0,0,0,0,0,0,0,0,0,0,41,253,255,252,255,181,0,0,0,0,0, 0,0,0,0,0,0,0,164,255,255,255,52,0,0,0,0,0,0,0,0,0,0,0,0,63,255,255,209,0,0,0,0, 0,0,0,0,0,0,0,0,0,56,255,255,204,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,204,0,0, 0,0,0,0,0,0,0,0,0,0,0,56,255,255,204,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,204, 0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,204,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255, 255,204,0,0,0,0,0,0,0,0,0,0,0,0,0,56,255,255,204,0,0,0,0,0,0,0,0,0,0,0,0,0, 56,255,255,204,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,255,255,255,255,255,255,255,28,0,128,255,255,255, 255,255,255,255,255,255,255,255,255,251,13,0,44,88,88,88,88,88,88,88,88,88,217,255,255,149,0,0,0,0,0,0, 0,0,0,0,0,63,255,255,237,19,0,0,0,0,0,0,0,0,0,0,3,208,255,255,107,0,0,0,0,0,0,0, 0,0,0,0,105,255,255,210,3,0,0,0,0,0,0,0,0,0,0,17,235,255,255,65,0,0,0,0,0,0,0,0, 0,0,0,146,255,255,171,0,0,0,0,0,0,0,0,0,0,0,42,251,255,246,31,0,0,0,0,0,0,0,0,0, 0,0,186,255,255,128,0,0,0,0,0,0,0,0,0,0,0,79,255,255,225,9,0,0,0,0,0,0,0,0,0,0, 7,220,255,255,85,0,0,0,0,0,0,0,0,0,0,0,120,255,255,192,0,0,0,0,0,0,0,0,0,0,0,25, 243,255,252,46,0,0,0,0,0,0,0,0,0,0,0,161,255,255,149,0,0,0,0,0,0,0,0,0,0,0,55,254, 255,237,19,0,0,0,0,0,0,0,0,0,0,1,200,255,255,107,0,0,0,0,0,0,0,0,0,0,0,95,255,255, 210,3,0,0,0,0,0,0,0,0,0,0,12,230,255,255,64,0,0,0,0,0,0,0,0,0,0,0,135,255,255,217, 88,88,88,88,88,88,88,88,88,78,0,6,247,255,255,255,255,255,255,255,255,255,255,255,255,228,0,16,255,255,255,255, 255,255,255,255,255,255,255,255,255,228,0,0,0,30,88,88,88,88,88,52,0,0,0,88,255,255,255,255,255,152,0,0, 0,88,255,255,255,255,254,105,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88, 255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255, 24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0, 0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0, 0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0, 0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88, 255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255, 24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0,0,0,0,0,0,88,255,255,24,0, 0,0,0,0,0,88,255,255,103,88,86,20,0,0,0,88,255,255,255,255,255,136,0,0,0,88,255,255,255,255,255,152, 0,24,249,222,40,0,0,0,0,0,0,0,0,0,0,0,0,165,255,170,0,0,0,0,0,0,0,0,0,0,0,0, 56,255,251,27,0,0,0,0,0,0,0,0,0,0,0,0,203,255,132,0,0,0,0,0,0,0,0,0,0,0,0,94, 255,234,7,0,0,0,0,0,0,0,0,0,0,0,7,234,255,94,0,0,0,0,0,0,0,0,0,0,0,0,132,255, 203,0,0,0,0,0,0,0,0,0,0,0,0,27,251,255,56,0,0,0,0,0,0,0,0,0,0,0,0,170,255,165, 0,0,0,0,0,0,0,0,0,0,0,0,61,255,250,24,0,0,0,0,0,0,0,0,0,0,0,0,208,255,127,0, 0,0,0,0,0,0,0,0,0,0,0,100,255,230,5,0,0,0,0,0,0,0,0,0,0,0,9,237,255,89,0,0, 0,0,0,0,0,0,0,0,0,0,138,255,198,0,0,0,0,0,0,0,0,0,0,0,0,32,253,255,51,0,0,0, 0,0,0,0,0,0,0,0,0,176,255,160,0,0,0,0,0,0,0,0,0,0,0,0,67,255,248,21,0,0,0,0, 0,0,0,0,0,0,0,0,213,255,122,0,0,0,0,0,0,0,0,0,0,0,0,105,255,227,4,0,0,0,0,0, 0,0,0,0,0,0,11,240,255,84,0,0,0,0,0,0,0,0,0,0,0,0,143,255,193,0,0,0,0,0,0,0, 0,0,0,0,0,36,254,255,46,0,0,0,0,0,0,0,0,0,0,0,0,182,255,155,0,0,0,0,0,0,0,0, 0,0,0,0,73,255,246,18,0,0,0,0,0,0,0,0,0,0,0,1,217,255,117,0,0,0,0,0,0,0,0,0, 0,0,0,67,229,219,2,0,15,88,88,88,88,88,67,0,0,0,44,255,255,255,255,255,196,0,0,0,14,237,255,255, 255,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255, 196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0, 0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0, 0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0, 0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0, 172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255, 196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0, 0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0,0,0,0,0,172,255,196,0,0,0, 0,69,88,88,200,255,196,0,0,0,28,255,255,255,255,255,196,0,0,0,44,255,255,255,255,255,196,0,0,0,0,0, 0,0,0,0,198,255,158,0,0,0,0,0,0,0,0,0,0,0,0,0,75,255,255,252,41,0,0,0,0,0,0,0, 0,0,0,0,1,206,255,245,255,172,0,0,0,0,0,0,0,0,0,0,0,85,255,253,86,255,254,51,0,0,0,0, 0,0,0,0,0,2,214,255,174,0,195,255,185,0,0,0,0,0,0,0,0,0,94,255,255,51,0,66,255,255,63,0, 0,0,0,0,0,0,5,221,255,183,0,0,0,193,255,198,0,0,0,0,0,0,0,103,255,255,59,0,0,0,63,255, 255,77,0,0,0,0,0,8,227,255,191,0,0,0,0,0,190,255,210,2,0,0,0,0,113,255,255,67,0,0,0,0, 0,61,255,255,90,0,0,0,11,233,255,199,0,0,0,0,0,0,0,188,255,220,5,0,0,122,255,246,66,0,0,0, 0,0,0,0,52,244,255,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,82,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,240,0,0,74,250,255,231,41, 0,0,0,0,0,0,90,254,255,201,2,0,0,0,0,0,0,107,255,255,108,0,0,0,0,0,0,0,126,255,241,27, 0,0,0,0,0,0,0,86,124,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,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,24,122,195,232,249,232,174,61,0,0,0,0,0,0,1,121,245,255,255, 255,255,255,255,255,116,0,0,0,0,0,142,255,255,243,154,101,102,156,253,255,254,61,0,0,0,0,53,210,140,16,0, 0,0,0,88,255,255,179,0,0,0,0,0,0,0,0,0,0,0,0,0,219,255,246,5,0,0,0,0,0,0,0,0, 0,0,0,0,180,255,255,33,0,0,0,0,0,0,0,0,0,0,0,0,168,255,255,47,0,0,0,0,0,0,40,118, 170,212,234,245,255,255,255,48,0,0,0,0,56,193,255,255,255,255,255,255,255,255,255,48,0,0,0,94,251,255,255,239, 178,134,106,95,198,255,255,48,0,0,34,248,255,245,100,6,0,0,0,0,168,255,255,48,0,0,122,255,255,105,0,0, 0,0,0,0,168,255,255,48,0,0,150,255,255,65,0,0,0,0,0,1,191,255,255,48,0,0,126,255,255,138,0,0, 0,0,20,174,255,255,255,48,0,0,48,255,255,253,163,99,105,154,239,250,177,255,255,48,0,0,0,146,255,255,255,255, 255,255,233,70,52,255,255,48,0,0,0,1,102,206,247,242,210,131,21,0,6,223,255,48,0,0,0,156,255,255,80,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,156,255,255,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156, 255,255,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,255,255,80,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,156,255,255,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,255,255,80,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,156,255,255,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,255,255,80,0,47,160, 224,248,229,162,45,0,0,0,0,0,0,156,255,255,80,110,250,255,255,255,255,255,250,92,0,0,0,0,0,156,255,255, 183,255,221,128,96,120,213,255,255,252,51,0,0,0,0,156,255,255,255,153,4,0,0,0,5,175,255,255,190,0,0,0, 0,156,255,255,172,0,0,0,0,0,0,20,245,255,254,31,0,0,0,156,255,255,80,0,0,0,0,0,0,0,173,255, 255,93,0,0,0,156,255,255,80,0,0,0,0,0,0,0,125,255,255,137,0,0,0,156,255,255,80,0,0,0,0,0, 0,0,104,255,255,154,0,0,0,156,255,255,80,0,0,0,0,0,0,0,101,255,255,157,0,0,0,156,255,255,80,0, 0,0,0,0,0,0,115,255,255,141,0,0,0,156,255,255,80,0,0,0,0,0,0,0,152,255,255,106,0,0,0,156, 255,255,80,0,0,0,0,0,0,0,216,255,255,50,0,0,0,156,255,255,85,0,0,0,0,0,0,71,255,255,219,0, 0,0,0,156,255,255,222,33,0,0,0,0,36,227,255,255,104,0,0,0,0,156,255,255,238,244,152,100,100,152,245,255, 255,192,2,0,0,0,0,156,255,255,55,226,255,255,255,255,255,255,192,15,0,0,0,0,0,156,255,220,1,18,139,219, 247,235,190,94,1,0,0,0,0,0,0,0,0,6,105,191,232,249,230,174,76,0,0,0,0,0,39,217,255,255,255,255, 255,255,255,174,10,0,0,25,231,255,255,222,128,94,112,199,255,255,71,0,0,172,255,255,179,9,0,0,0,0,84,111, 0,0,35,254,255,237,16,0,0,0,0,0,0,0,0,0,119,255,255,145,0,0,0,0,0,0,0,0,0,0,174,255, 255,82,0,0,0,0,0,0,0,0,0,0,203,255,255,49,0,0,0,0,0,0,0,0,0,0,216,255,255,39,0,0, 0,0,0,0,0,0,0,0,203,255,255,49,0,0,0,0,0,0,0,0,0,0,178,255,255,83,0,0,0,0,0,0, 0,0,0,0,123,255,255,146,0,0,0,0,0,0,0,0,0,0,43,255,255,238,18,0,0,0,0,0,0,0,0,0, 0,182,255,255,180,8,0,0,0,2,124,211,24,0,0,31,239,255,255,218,122,93,117,211,255,255,121,0,0,0,51,231, 255,255,255,255,255,255,252,141,1,0,0,0,0,15,127,209,244,243,213,145,45,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,116,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116,255,255,120,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,116,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116,255,255,120,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,116,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116,255,255, 120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116,255,255,120,0,0,0,0,0,0,6,108,197,237,246,216,136, 16,116,255,255,120,0,0,0,0,0,30,211,255,255,255,255,255,255,227,155,255,255,120,0,0,0,0,15,220,255,255,235, 141,97,107,176,254,255,255,255,120,0,0,0,0,147,255,255,207,21,0,0,0,0,75,249,255,255,120,0,0,0,16,246, 255,250,37,0,0,0,0,0,0,132,255,255,120,0,0,0,92,255,255,176,0,0,0,0,0,0,0,116,255,255,120,0, 0,0,147,255,255,111,0,0,0,0,0,0,0,116,255,255,120,0,0,0,178,255,255,75,0,0,0,0,0,0,0,116, 255,255,120,0,0,0,192,255,255,63,0,0,0,0,0,0,0,116,255,255,120,0,0,0,184,255,255,69,0,0,0,0, 0,0,0,116,255,255,120,0,0,0,165,255,255,92,0,0,0,0,0,0,0,116,255,255,120,0,0,0,122,255,255,142, 0,0,0,0,0,0,0,116,255,255,120,0,0,0,59,255,255,226,6,0,0,0,0,0,2,180,255,255,120,0,0,0, 1,218,255,255,140,0,0,0,0,11,168,255,255,255,120,0,0,0,0,79,255,255,255,193,110,97,136,230,255,170,255,255, 120,0,0,0,0,0,124,255,255,255,255,255,255,246,98,35,255,255,120,0,0,0,0,0,0,65,178,235,247,220,148,36, 0,3,223,255,120,0,0,0,0,0,0,0,65,160,222,246,240,200,116,10,0,0,0,0,0,0,0,11,172,255,255,255, 255,255,255,255,225,43,0,0,0,0,0,6,198,255,255,235,140,97,102,167,253,255,234,24,0,0,0,0,134,255,255,201, 17,0,0,0,0,65,250,255,166,0,0,0,18,244,255,246,37,0,0,0,0,0,0,145,255,252,28,0,0,95,255,255, 159,0,0,0,0,0,0,0,34,255,255,96,0,0,153,255,255,90,0,0,0,0,0,0,0,0,249,255,145,0,0,179, 255,255,255,255,255,255,255,255,255,255,255,255,255,165,0,0,185,255,255,255,255,255,255,255,255,255,255,255,255,255,166,0, 0,168,255,255,116,88,88,88,88,88,88,88,88,88,88,36,0,0,135,255,255,66,0,0,0,0,0,0,0,0,0,0, 0,0,0,73,255,255,130,0,0,0,0,0,0,0,0,0,0,0,0,0,7,231,255,233,17,0,0,0,0,0,0,0, 0,0,0,0,0,0,111,255,255,193,20,0,0,0,0,0,23,133,124,0,0,0,0,1,182,255,255,241,154,103,91,112, 175,249,255,253,49,0,0,0,0,7,160,255,255,255,255,255,255,255,255,230,79,0,0,0,0,0,0,0,56,155,218,245, 246,225,173,99,8,0,0,0,0,0,0,0,3,103,196,241,247,210,21,0,0,0,5,184,255,255,255,255,255,19,0,0, 0,123,255,255,242,139,95,71,0,0,0,0,227,255,254,56,0,0,0,0,0,0,23,255,255,197,0,0,0,0,0,0, 0,41,255,255,167,0,0,0,0,0,0,0,44,255,255,164,0,0,0,0,0,152,255,255,255,255,255,255,255,255,255,4, 138,255,255,255,255,255,255,255,255,255,4,13,88,149,255,255,214,88,88,88,88,1,0,0,44,255,255,192,0,0,0,0, 0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192,0,0,0, 0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192,0,0, 0,0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192,0, 0,0,0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,44,255,255,192, 0,0,0,0,0,0,0,44,255,255,192,0,0,0,0,0,0,0,0,0,60,166,227,250,239,185,74,0,0,0,0,0, 0,0,0,134,255,255,255,255,255,255,255,252,252,252,252,102,0,0,97,255,255,241,139,99,120,207,255,255,255,255,244,65, 0,0,223,255,245,36,0,0,0,4,185,255,255,63,2,0,0,32,255,255,157,0,0,0,0,0,62,255,255,103,0,0, 0,49,255,255,125,0,0,0,0,0,30,255,255,129,0,0,0,24,255,255,159,0,0,0,0,0,65,255,255,112,0,0, 0,0,207,255,247,41,0,0,0,4,188,255,255,48,0,0,0,0,73,254,255,244,142,99,120,209,255,255,180,0,0,0, 0,0,0,99,249,255,255,255,255,255,255,200,17,0,0,0,0,0,0,120,251,225,225,250,239,194,102,5,0,0,0,0, 0,0,81,255,242,22,0,0,0,0,0,0,0,0,0,0,0,0,178,255,220,2,0,0,0,0,0,0,0,0,0,0, 0,0,175,255,255,201,125,99,82,72,49,15,0,0,0,0,0,0,70,252,255,255,255,255,255,255,255,255,203,105,1,0, 0,0,21,183,255,227,240,255,255,255,255,255,255,255,135,0,0,35,227,255,128,2,0,0,1,15,48,134,251,255,246,7, 0,174,255,201,0,0,0,0,0,0,0,0,161,255,255,28,0,229,255,181,0,0,0,0,0,0,0,0,171,255,248,7, 0,207,255,246,52,0,0,0,0,0,0,84,252,255,162,0,0,97,255,255,251,173,114,92,103,131,197,255,255,226,24,0, 0,0,124,250,255,255,255,255,255,255,255,255,197,32,0,0,0,0,0,36,134,199,236,251,245,220,164,74,1,0,0,0, 0,0,160,255,255,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,255,255,76,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,160,255,255,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,255,255,76,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,160,255,255,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,255,255,76, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,255,255,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 160,255,255,76,0,68,175,230,249,227,156,32,0,0,0,0,0,0,160,255,255,81,156,255,255,255,255,255,255,241,56,0, 0,0,0,0,160,255,255,226,255,221,129,95,119,220,255,255,223,7,0,0,0,0,160,255,255,255,138,4,0,0,0,15, 225,255,255,87,0,0,0,0,160,255,255,136,0,0,0,0,0,0,112,255,255,152,0,0,0,0,160,255,255,76,0,0, 0,0,0,0,61,255,255,186,0,0,0,0,160,255,255,76,0,0,0,0,0,0,37,255,255,199,0,0,0,0,160,255, 255,76,0,0,0,0,0,0,36,255,255,200,0,0,0,0,160,255,255,76,0,0,0,0,0,0,36,255,255,200,0,0, 0,0,160,255,255,76,0,0,0,0,0,0,36,255,255,200,0,0,0,0,160,255,255,76,0,0,0,0,0,0,36,255, 255,200,0,0,0,0,160,255,255,76,0,0,0,0,0,0,36,255,255,200,0,0,0,0,160,255,255,76,0,0,0,0, 0,0,36,255,255,200,0,0,0,0,160,255,255,76,0,0,0,0,0,0,36,255,255,200,0,0,0,0,160,255,255,76, 0,0,0,0,0,0,36,255,255,200,0,0,0,0,160,255,255,76,0,0,0,0,0,0,36,255,255,200,0,0,0,0, 160,255,255,76,0,0,0,0,0,0,36,255,255,200,0,0,0,0,100,235,212,50,0,0,0,18,251,255,255,202,0,0, 0,19,253,255,255,206,0,0,0,0,106,237,215,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0, 0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0, 0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0, 0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0, 0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,0,46,210,235,105,0,0,0,0,0,189,255,255,253, 28,0,0,0,0,192,255,255,254,31,0,0,0,0,50,214,237,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0, 0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72, 255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0, 0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255, 255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0, 0,0,72,255,255,164,0,0,0,0,0,72,255,255,164,0,0,0,0,0,84,255,255,157,0,0,0,0,0,143,255,255, 126,0,0,57,95,148,251,255,255,49,0,0,203,255,255,255,255,144,0,0,0,182,246,245,202,98,0,0,0,0,0,0, 224,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0,224,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0, 224,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0,224,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0, 224,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0,224,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0, 224,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0,224,255,255,12,0,0,0,0,21,202,255,255,86,0,0,0, 224,255,255,12,0,0,0,1,181,255,255,154,0,0,0,0,224,255,255,12,0,0,0,113,255,255,212,9,0,0,0,0, 224,255,255,12,0,0,50,249,255,246,43,0,0,0,0,0,224,255,255,12,0,12,218,255,255,102,0,0,0,0,0,0, 224,255,255,12,0,162,255,255,170,0,0,0,0,0,0,0,224,255,255,13,103,255,255,222,15,0,0,0,0,0,0,0, 224,255,255,255,255,255,233,40,0,0,0,0,0,0,0,0,224,255,255,255,255,255,252,82,0,0,0,0,0,0,0,0, 224,255,255,96,150,255,255,239,27,0,0,0,0,0,0,0,224,255,255,12,0,178,255,255,181,0,0,0,0,0,0,0, 224,255,255,12,0,24,237,255,255,96,0,0,0,0,0,0,224,255,255,12,0,0,88,255,255,239,27,0,0,0,0,0, 224,255,255,12,0,0,0,171,255,255,181,0,0,0,0,0,224,255,255,12,0,0,0,20,233,255,255,96,0,0,0,0, 224,255,255,12,0,0,0,0,81,255,255,239,27,0,0,0,224,255,255,12,0,0,0,0,0,144,251,255,181,0,0,0, 144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0, 144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0, 144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0, 144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0, 144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0, 144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0,144,255,255,92,0,0,0,0, 144,255,200,0,1,97,201,242,231,171,35,0,0,0,61,170,226,249,229,168,51,0,0,0,0,0,0,144,255,254,18,182, 255,255,255,255,255,236,33,0,126,255,255,255,255,255,255,251,92,0,0,0,0,0,144,255,255,208,255,184,107,109,179,255, 255,177,97,255,244,150,101,111,202,255,255,246,34,0,0,0,0,144,255,255,251,91,0,0,0,0,133,255,253,232,247,46, 0,0,0,4,190,255,255,140,0,0,0,0,144,255,255,123,0,0,0,0,0,12,249,255,255,130,0,0,0,0,0,60, 255,255,206,0,0,0,0,144,255,255,92,0,0,0,0,0,0,213,255,255,55,0,0,0,0,0,11,255,255,237,0,0, 0,0,144,255,255,92,0,0,0,0,0,0,200,255,255,36,0,0,0,0,0,0,241,255,251,0,0,0,0,144,255,255, 92,0,0,0,0,0,0,200,255,255,36,0,0,0,0,0,0,240,255,252,0,0,0,0,144,255,255,92,0,0,0,0, 0,0,200,255,255,36,0,0,0,0,0,0,240,255,252,0,0,0,0,144,255,255,92,0,0,0,0,0,0,200,255,255, 36,0,0,0,0,0,0,240,255,252,0,0,0,0,144,255,255,92,0,0,0,0,0,0,200,255,255,36,0,0,0,0, 0,0,240,255,252,0,0,0,0,144,255,255,92,0,0,0,0,0,0,200,255,255,36,0,0,0,0,0,0,240,255,252, 0,0,0,0,144,255,255,92,0,0,0,0,0,0,200,255,255,36,0,0,0,0,0,0,240,255,252,0,0,0,0,144, 255,255,92,0,0,0,0,0,0,200,255,255,36,0,0,0,0,0,0,240,255,252,0,0,0,0,144,255,255,92,0,0, 0,0,0,0,200,255,255,36,0,0,0,0,0,0,240,255,252,0,0,0,0,144,255,255,92,0,0,0,0,0,0,200, 255,255,36,0,0,0,0,0,0,240,255,252,0,0,0,0,144,255,255,92,0,0,0,0,0,0,200,255,255,36,0,0, 0,0,0,0,240,255,252,0,0,0,0,172,255,172,0,0,71,177,229,249,226,154,31,0,0,0,0,0,0,172,255,234, 7,162,255,255,255,255,255,255,240,53,0,0,0,0,0,172,255,255,190,255,217,127,95,120,221,255,255,221,6,0,0,0, 0,172,255,255,254,124,2,0,0,0,16,227,255,255,86,0,0,0,0,172,255,255,121,0,0,0,0,0,0,113,255,255, 152,0,0,0,0,172,255,255,64,0,0,0,0,0,0,62,255,255,186,0,0,0,0,172,255,255,64,0,0,0,0,0, 0,37,255,255,199,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0, 0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172, 255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0, 0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36, 255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0, 0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,0,0,0, 61,155,219,244,243,217,152,55,0,0,0,0,0,0,0,0,11,171,255,255,255,255,255,255,255,255,160,7,0,0,0,0, 0,5,196,255,255,237,145,99,101,151,243,255,255,183,2,0,0,0,0,130,255,255,208,22,0,0,0,0,30,219,255,255, 112,0,0,0,14,241,255,249,36,0,0,0,0,0,0,51,254,255,230,6,0,0,88,255,255,172,0,0,0,0,0,0, 0,0,192,255,255,68,0,0,146,255,255,109,0,0,0,0,0,0,0,0,129,255,255,128,0,0,174,255,255,77,0,0, 0,0,0,0,0,0,94,255,255,156,0,0,187,255,255,67,0,0,0,0,0,0,0,0,83,255,255,171,0,0,174,255, 255,76,0,0,0,0,0,0,0,0,93,255,255,156,0,0,146,255,255,108,0,0,0,0,0,0,0,0,128,255,255,128, 0,0,88,255,255,171,0,0,0,0,0,0,0,0,191,255,255,68,0,0,15,242,255,248,33,0,0,0,0,0,0,48, 253,255,231,6,0,0,0,131,255,255,204,20,0,0,0,0,27,216,255,255,114,0,0,0,0,5,199,255,255,235,143,98, 100,148,241,255,255,186,2,0,0,0,0,0,12,174,255,255,255,255,255,255,255,255,163,8,0,0,0,0,0,0,0,0, 65,159,220,245,245,219,156,58,0,0,0,0,0,0,0,168,255,175,0,0,46,159,223,248,229,164,46,0,0,0,0,0, 0,168,255,237,0,115,251,255,255,255,255,255,250,91,0,0,0,0,0,168,255,255,134,255,221,128,96,118,210,255,255,251, 47,0,0,0,0,168,255,255,255,148,4,0,0,0,3,174,255,255,183,0,0,0,0,168,255,255,162,0,0,0,0,0, 0,22,247,255,253,26,0,0,0,168,255,255,68,0,0,0,0,0,0,0,181,255,255,87,0,0,0,168,255,255,68,0, 0,0,0,0,0,0,130,255,255,132,0,0,0,168,255,255,68,0,0,0,0,0,0,0,107,255,255,150,0,0,0,168, 255,255,68,0,0,0,0,0,0,0,99,255,255,159,0,0,0,168,255,255,68,0,0,0,0,0,0,0,112,255,255,144, 0,0,0,168,255,255,68,0,0,0,0,0,0,0,149,255,255,113,0,0,0,168,255,255,68,0,0,0,0,0,0,0, 213,255,255,56,0,0,0,168,255,255,73,0,0,0,0,0,0,70,255,255,222,2,0,0,0,168,255,255,215,29,0,0, 0,0,37,228,255,255,105,0,0,0,0,168,255,255,255,242,150,100,100,152,245,255,255,189,2,0,0,0,0,168,255,255, 146,247,255,255,255,255,255,255,184,12,0,0,0,0,0,168,255,255,68,37,158,226,249,233,184,86,0,0,0,0,0,0, 0,168,255,255,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,255,255,68,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,168,255,255,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,255,255,68,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,168,255,255,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,255,255,68,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,104,195,236,246,218,140,19,0,212,255,132,0,0,0,0,0, 28,208,255,255,255,255,255,255,232,70,255,255,132,0,0,0,0,13,218,255,255,236,143,98,107,172,253,249,255,255,132,0, 0,0,0,144,255,255,210,22,0,0,0,0,65,245,255,255,132,0,0,0,15,245,255,251,40,0,0,0,0,0,0,120, 255,255,132,0,0,0,92,255,255,177,0,0,0,0,0,0,0,104,255,255,132,0,0,0,147,255,255,111,0,0,0,0, 0,0,0,104,255,255,132,0,0,0,178,255,255,75,0,0,0,0,0,0,0,104,255,255,132,0,0,0,192,255,255,63, 0,0,0,0,0,0,0,104,255,255,132,0,0,0,184,255,255,69,0,0,0,0,0,0,0,104,255,255,132,0,0,0, 165,255,255,92,0,0,0,0,0,0,0,104,255,255,132,0,0,0,122,255,255,142,0,0,0,0,0,0,0,104,255,255, 132,0,0,0,59,255,255,226,6,0,0,0,0,0,1,170,255,255,132,0,0,0,1,218,255,255,141,0,0,0,0,8, 159,255,255,255,132,0,0,0,0,79,255,255,255,195,111,96,133,227,255,210,255,255,132,0,0,0,0,0,123,255,255,255, 255,255,255,250,114,104,255,255,132,0,0,0,0,0,0,64,177,233,248,222,154,43,0,104,255,255,132,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,104,255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,255,255,132,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,104,255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104, 255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,255,255,132,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,104,255,255,132,0,0,0,0,212,255,150,0,7,122,216,249,234,30,0,0,212,255,199,4,180,255,255,255,255, 14,0,0,212,255,219,121,255,255,255,255,233,0,0,0,212,255,245,242,225,83,18,0,0,0,0,0,212,255,255,241,36, 0,0,0,0,0,0,0,212,255,255,127,0,0,0,0,0,0,0,0,212,255,255,33,0,0,0,0,0,0,0,0,212, 255,255,24,0,0,0,0,0,0,0,0,212,255,255,24,0,0,0,0,0,0,0,0,212,255,255,24,0,0,0,0,0, 0,0,0,212,255,255,24,0,0,0,0,0,0,0,0,212,255,255,24,0,0,0,0,0,0,0,0,212,255,255,24,0, 0,0,0,0,0,0,0,212,255,255,24,0,0,0,0,0,0,0,0,212,255,255,24,0,0,0,0,0,0,0,0,212, 255,255,24,0,0,0,0,0,0,0,0,212,255,255,24,0,0,0,0,0,0,0,0,0,16,125,207,244,245,214,141,28, 0,0,0,0,37,225,255,255,255,255,255,255,246,86,0,0,2,207,255,255,206,115,101,146,234,255,162,0,0,61,255,255, 194,3,0,0,0,10,93,17,0,0,101,255,255,103,0,0,0,0,0,0,0,0,0,87,255,255,150,0,0,0,0,0, 0,0,0,0,21,241,255,255,161,36,0,0,0,0,0,0,0,0,88,252,255,255,254,196,104,12,0,0,0,0,0,0, 52,189,254,255,255,255,239,99,0,0,0,0,0,0,0,35,121,219,255,255,255,100,0,0,0,0,0,0,0,0,2,110, 252,255,239,5,0,0,0,0,0,0,0,0,0,171,255,255,32,0,0,0,0,0,0,0,0,0,167,255,255,21,0,24, 173,87,0,0,0,0,31,241,255,220,0,0,161,255,255,199,113,95,135,237,255,255,104,0,0,38,214,255,255,255,255,255, 255,255,149,0,0,0,0,5,104,191,235,250,231,175,70,0,0,0,0,0,0,39,247,240,0,0,0,0,0,0,0,0, 90,255,240,0,0,0,0,0,0,0,0,128,255,240,0,0,0,0,0,0,0,0,166,255,240,0,0,0,0,0,0,0, 0,204,255,240,0,0,0,0,0,76,195,225,254,255,255,255,255,255,255,104,106,255,255,255,255,255,255,255,255,255,104,16, 86,88,253,255,245,88,88,88,88,35,0,0,0,252,255,240,0,0,0,0,0,0,0,0,252,255,240,0,0,0,0,0, 0,0,0,252,255,240,0,0,0,0,0,0,0,0,252,255,240,0,0,0,0,0,0,0,0,252,255,240,0,0,0,0, 0,0,0,0,252,255,240,0,0,0,0,0,0,0,0,252,255,240,0,0,0,0,0,0,0,0,252,255,240,0,0,0, 0,0,0,0,0,252,255,240,0,0,0,0,0,0,0,0,251,255,244,0,0,0,0,0,0,0,0,235,255,255,33,0, 0,24,0,0,0,0,181,255,255,211,119,165,251,48,0,0,0,61,252,255,255,255,255,255,143,0,0,0,0,69,198,247, 242,195,96,2,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0, 0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0, 0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0, 236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140, 0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0, 96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,235,255,255,1,0,0, 0,0,0,0,96,255,255,140,0,0,0,0,223,255,255,24,0,0,0,0,0,0,96,255,255,140,0,0,0,0,189,255, 255,74,0,0,0,0,0,0,144,255,255,140,0,0,0,0,122,255,255,197,4,0,0,0,7,143,255,255,255,140,0,0, 0,0,23,241,255,255,204,111,96,133,224,255,199,255,255,140,0,0,0,0,0,80,250,255,255,255,255,255,254,142,19,255, 255,140,0,0,0,0,0,0,46,168,232,249,226,167,57,0,0,211,255,140,0,0,131,255,252,117,0,0,0,0,0,0, 0,16,224,255,221,0,39,255,255,226,0,0,0,0,0,0,0,96,255,255,130,0,0,204,255,255,52,0,0,0,0,0, 0,179,255,255,40,0,0,113,255,255,134,0,0,0,0,0,13,248,255,204,0,0,0,25,253,255,215,0,0,0,0,0, 88,255,255,113,0,0,0,0,187,255,255,41,0,0,0,0,171,255,253,25,0,0,0,0,96,255,255,122,0,0,0,9, 244,255,187,0,0,0,0,0,14,247,255,204,0,0,0,80,255,255,96,0,0,0,0,0,0,170,255,255,30,0,0,163, 255,247,14,0,0,0,0,0,0,79,255,255,111,0,5,239,255,170,0,0,0,0,0,0,0,6,237,255,192,0,72,255, 255,79,0,0,0,0,0,0,0,0,153,255,252,21,155,255,238,6,0,0,0,0,0,0,0,0,62,255,255,98,235,255, 153,0,0,0,0,0,0,0,0,0,1,225,255,210,255,255,62,0,0,0,0,0,0,0,0,0,0,136,255,255,255,226, 1,0,0,0,0,0,0,0,0,0,0,45,255,255,255,136,0,0,0,0,0,0,0,0,0,0,0,0,210,255,255,45, 0,0,0,0,0,0,130,255,250,96,0,0,0,0,0,0,37,240,254,141,0,0,0,0,0,0,11,223,255,205,52,255, 255,190,0,0,0,0,0,0,125,255,255,238,3,0,0,0,0,0,78,255,255,128,1,230,255,249,10,0,0,0,0,0, 201,255,255,255,60,0,0,0,0,0,148,255,255,51,0,154,255,255,71,0,0,0,0,22,254,255,235,255,134,0,0,0, 0,0,218,255,230,1,0,77,255,255,140,0,0,0,0,97,255,229,149,255,208,0,0,0,0,31,255,255,154,0,0,9, 246,255,209,0,0,0,0,174,255,155,76,255,255,26,0,0,0,102,255,255,77,0,0,0,178,255,254,22,0,0,6,242, 255,78,9,246,255,99,0,0,0,172,255,247,10,0,0,0,101,255,255,90,0,0,70,255,246,10,0,180,255,173,0,0, 3,238,255,180,0,0,0,0,25,254,255,158,0,0,146,255,179,0,0,104,255,241,5,0,56,255,255,103,0,0,0,0, 0,203,255,227,0,0,221,255,102,0,0,28,255,255,65,0,126,255,255,27,0,0,0,0,0,126,255,255,40,42,255,254, 26,0,0,0,207,255,139,0,197,255,206,0,0,0,0,0,0,49,255,255,108,117,255,204,0,0,0,0,131,255,212,12, 251,255,129,0,0,0,0,0,0,0,226,255,170,193,255,126,0,0,0,0,55,255,255,97,255,255,52,0,0,0,0,0, 0,0,150,255,228,250,255,49,0,0,0,0,1,232,255,219,255,231,1,0,0,0,0,0,0,0,73,255,255,255,227,0, 0,0,0,0,0,159,255,255,255,155,0,0,0,0,0,0,0,0,7,244,255,255,150,0,0,0,0,0,0,83,255,255, 255,78,0,0,0,0,0,0,0,0,0,175,255,251,61,0,0,0,0,0,0,12,234,255,247,10,0,0,0,0,36,249, 255,242,53,0,0,0,0,0,62,246,255,208,3,0,136,255,255,200,1,0,0,0,2,207,255,255,60,0,0,12,230,255, 255,91,0,0,0,96,255,255,162,0,0,0,0,94,255,255,227,10,0,9,227,255,241,24,0,0,0,0,1,199,255,255, 128,0,123,255,255,112,0,0,0,0,0,0,53,254,255,245,51,242,255,212,4,0,0,0,0,0,0,0,158,255,255,242, 255,255,64,0,0,0,0,0,0,0,0,23,241,255,255,255,167,0,0,0,0,0,0,0,0,0,0,192,255,255,255,128, 0,0,0,0,0,0,0,0,0,72,255,255,255,255,244,27,0,0,0,0,0,0,0,5,215,255,236,136,255,255,162,0, 0,0,0,0,0,0,113,255,255,114,11,232,255,254,53,0,0,0,0,0,21,240,255,224,7,0,107,255,255,197,0,0, 0,0,0,154,255,255,93,0,0,5,220,255,255,88,0,0,0,48,253,255,209,2,0,0,0,89,255,255,225,9,0,0, 193,255,255,72,0,0,0,0,1,206,255,255,124,0,86,255,255,171,0,0,0,0,0,0,61,242,255,244,26,160,255,251, 112,0,0,0,0,0,0,0,7,211,255,239,8,62,255,255,226,1,0,0,0,0,0,0,79,255,255,151,0,0,219,255, 255,61,0,0,0,0,0,0,165,255,255,55,0,0,123,255,255,150,0,0,0,0,0,8,243,255,215,0,0,0,28,253, 255,235,4,0,0,0,0,83,255,255,119,0,0,0,0,184,255,255,72,0,0,0,0,169,255,252,26,0,0,0,0,86, 255,255,161,0,0,0,10,245,255,183,0,0,0,0,0,7,237,255,241,8,0,0,87,255,255,87,0,0,0,0,0,0, 147,255,255,83,0,0,173,255,238,7,0,0,0,0,0,0,50,255,255,172,0,13,247,255,150,0,0,0,0,0,0,0, 0,208,255,247,14,91,255,255,54,0,0,0,0,0,0,0,0,111,255,255,94,177,255,214,0,0,0,0,0,0,0,0, 0,19,249,255,187,247,255,118,0,0,0,0,0,0,0,0,0,0,172,255,255,255,252,25,0,0,0,0,0,0,0,0, 0,0,74,255,255,255,182,0,0,0,0,0,0,0,0,0,0,0,3,229,255,255,86,0,0,0,0,0,0,0,0,0, 0,0,0,187,255,238,7,0,0,0,0,0,0,0,0,0,0,0,32,253,255,149,0,0,0,0,0,0,0,0,0,0, 0,0,136,255,255,53,0,0,0,0,0,0,0,0,0,0,0,6,234,255,213,0,0,0,0,0,0,0,0,0,0,0, 0,92,255,255,117,0,0,0,0,0,0,0,0,0,0,0,0,198,255,252,25,0,0,0,0,0,0,0,0,0,0,0, 47,255,254,149,0,0,0,0,0,0,0,0,0,0,144,255,255,255,255,255,255,255,255,255,255,112,0,144,255,255,255,255, 255,255,255,255,255,255,101,0,49,88,88,88,88,88,88,108,254,255,239,20,0,0,0,0,0,0,0,0,162,255,255,101, 0,0,0,0,0,0,0,0,66,255,255,194,1,0,0,0,0,0,0,0,8,218,255,249,41,0,0,0,0,0,0,0, 0,132,255,255,128,0,0,0,0,0,0,0,0,42,249,255,216,7,0,0,0,0,0,0,0,1,195,255,254,62,0,0, 0,0,0,0,0,0,101,255,255,156,0,0,0,0,0,0,0,0,23,238,255,233,17,0,0,0,0,0,0,0,0,166, 255,255,89,0,0,0,0,0,0,0,0,71,255,255,183,0,0,0,0,0,0,0,0,9,221,255,245,33,0,0,0,0, 0,0,0,0,133,255,255,182,88,88,88,88,88,88,88,16,0,240,255,255,255,255,255,255,255,255,255,255,48,0,255,255, 255,255,255,255,255,255,255,255,255,48,0,0,0,0,0,2,48,79,67,0,0,0,0,0,0,88,226,255,255,196,0,0, 0,0,0,96,255,255,255,255,159,0,0,0,0,8,235,255,243,94,15,0,0,0,0,0,66,255,255,121,0,0,0,0, 0,0,0,97,255,255,54,0,0,0,0,0,0,0,91,255,255,43,0,0,0,0,0,0,0,68,255,255,60,0,0,0, 0,0,0,0,27,255,255,92,0,0,0,0,0,0,0,0,237,255,131,0,0,0,0,0,0,0,0,194,255,171,0,0, 0,0,0,0,0,0,168,255,201,0,0,0,0,0,0,0,0,162,255,216,0,0,0,0,0,0,0,4,220,255,181,0, 0,0,0,0,0,80,189,255,253,68,0,0,0,0,0,0,196,255,224,76,0,0,0,0,0,0,0,196,255,253,150,3, 0,0,0,0,0,0,13,109,255,255,112,0,0,0,0,0,0,0,0,193,255,198,0,0,0,0,0,0,0,0,158,255, 215,0,0,0,0,0,0,0,0,174,255,194,0,0,0,0,0,0,0,0,207,255,159,0,0,0,0,0,0,0,4,248, 255,119,0,0,0,0,0,0,0,42,255,255,80,0,0,0,0,0,0,0,78,255,255,54,0,0,0,0,0,0,0,97, 255,255,41,0,0,0,0,0,0,0,91,255,255,67,0,0,0,0,0,0,0,46,255,255,169,0,0,0,0,0,0,0, 0,204,255,255,181,102,39,0,0,0,0,0,41,232,255,255,255,187,0,0,0,0,0,0,24,144,218,248,196,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0, 0,0,0,0,0,80,255,248,0,0,0,0,0,0,0,0,6,87,73,30,0,0,0,0,0,0,0,20,255,255,255,193, 41,0,0,0,0,0,6,232,255,255,255,238,33,0,0,0,0,0,0,29,146,255,255,164,0,0,0,0,0,0,0,0, 201,255,241,2,0,0,0,0,0,0,0,135,255,255,20,0,0,0,0,0,0,0,125,255,255,14,0,0,0,0,0,0, 0,144,255,247,0,0,0,0,0,0,0,0,177,255,206,0,0,0,0,0,0,0,0,215,255,161,0,0,0,0,0,0, 0,4,250,255,117,0,0,0,0,0,0,0,29,255,255,93,0,0,0,0,0,0,0,41,255,255,89,0,0,0,0,0, 0,0,11,250,255,151,0,0,0,0,0,0,0,0,146,255,253,150,45,0,0,0,0,0,0,3,127,249,255,120,0,0, 0,0,0,0,28,203,255,255,120,0,0,0,0,0,0,192,255,233,64,5,0,0,0,0,0,23,255,255,119,0,0,0, 0,0,0,0,40,255,255,85,0,0,0,0,0,0,0,22,255,255,99,0,0,0,0,0,0,0,0,243,255,130,0,0, 0,0,0,0,0,0,203,255,176,0,0,0,0,0,0,0,0,164,255,221,0,0,0,0,0,0,0,0,138,255,254,3, 0,0,0,0,0,0,0,122,255,255,20,0,0,0,0,0,0,0,147,255,255,14,0,0,0,0,0,0,16,233,255,223, 0,0,0,0,0,66,116,219,255,255,124,0,0,0,0,13,253,255,255,255,187,6,0,0,0,0,20,255,243,201,109,4, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,255,222,0,0,0,18,146,228,250,222,148,56,0,0, 0,1,208,255,186,0,0,10,212,255,255,255,255,255,255,211,130,101,184,255,255,104,0,0,121,255,255,174,99,130,220,255, 255,255,255,255,255,198,5,0,0,203,255,191,0,0,0,1,63,155,225,250,226,140,12,0,0,0,235,252,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,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,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,172,243,180,12,0,0,0,0,0,0,100,255,255,255,118,0,0,0,0,0,0,98,255,255,255,115,0, 0,0,0,0,0,7,171,243,179,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,255,161, 0,0,0,0,0,0,0,0,141,255,188,0,0,0,0,0,0,0,0,166,255,210,0,0,0,0,0,0,0,0,185,255, 227,0,0,0,0,0,0,0,0,198,255,238,0,0,0,0,0,0,0,0,205,255,245,0,0,0,0,0,0,0,0,208, 255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0, 208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0, 0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0,0,0,208,255,248,0,0,0,0,0,0, 0,0,29,158,231,248,206,63,0,0,0,0,0,0,0,50,239,255,255,255,255,253,53,0,0,0,0,0,0,122,255,190, 109,108,225,255,175,0,0,0,0,0,0,4,44,0,0,0,80,255,221,0,0,0,0,0,0,0,0,0,0,0,42,255, 236,0,0,0,0,0,0,0,62,151,204,236,248,255,236,0,0,0,0,0,0,150,255,208,107,63,80,255,236,0,0,0, 0,0,31,255,238,6,0,0,42,255,236,0,0,0,0,0,14,252,247,49,3,59,213,255,236,0,0,0,0,0,0,100, 242,255,255,225,109,218,236,0,0,0,0,0,0,0,7,36,28,0,0,19,40,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,2,0,0,0,19,24,0,0,0,0, 0,0,0,0,0,27,242,189,0,0,0,166,246,47,0,0,0,0,0,0,0,0,174,255,161,0,0,69,255,242,22,0, 0,0,0,0,0,0,76,255,243,26,0,8,220,255,118,0,0,0,0,0,0,0,11,225,255,119,0,0,133,255,217,6, 0,0,0,0,0,0,0,141,255,218,6,0,42,250,255,73,0,0,0,0,0,0,0,48,252,255,73,0,1,195,255,176, 0,0,0,0,0,0,0,0,108,255,235,16,0,14,245,255,100,0,0,0,0,0,0,0,0,6,214,255,149,0,0,117, 255,236,17,0,0,0,0,0,0,0,0,62,254,252,46,0,4,208,255,146,0,0,0,0,0,0,0,0,0,157,255,191, 0,0,55,253,251,44,0,0,0,0,0,0,0,0,18,234,255,85,0,0,150,255,189,0,0,0,0,0,0,0,0,0, 94,255,206,0,0,15,230,255,54,0,0,0,0,0,0,0,0,0,155,79,0,0,0,82,142,10,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 6,113,209,247,234,167,45,0,0,0,0,12,196,255,255,255,255,255,249,86,0,0,0,158,255,254,165,103,126,225,255,247, 38,0,30,253,255,95,0,0,0,13,212,255,154,0,96,255,207,0,0,0,0,0,82,255,223,0,118,255,163,0,0,0, 0,0,37,255,245,0,96,255,202,0,0,0,0,0,77,255,224,0,30,253,255,90,0,0,0,11,208,255,154,0,0,157, 255,254,161,102,124,223,255,247,37,0,0,12,197,255,255,255,255,255,250,86,0,0,0,0,6,117,211,248,236,171,47,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,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,205,239,151,5,0,0,0,0,221,255,255,255,113,0,0,0,19,255,255,255,255,167,0,0,0,0,223,255,255,255,115, 0,0,0,0,52,208,241,156,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,0,0,65,182,239,247,210,115,6,0,0,0,0,0,0,104,254,255,255,255, 255,255,186,4,0,0,0,0,33,249,255,218,118,103,176,255,255,117,0,0,0,0,130,255,237,16,0,0,0,165,255,221, 0,0,0,0,183,255,159,0,0,0,0,68,255,255,19,0,0,0,199,255,135,0,0,0,0,44,255,255,35,0,0,0, 182,255,159,0,0,0,0,68,255,255,18,0,0,0,128,255,237,15,0,0,0,163,255,220,0,0,0,0,32,249,255,217, 116,102,174,255,255,116,0,0,0,0,0,103,254,255,255,255,255,255,186,4,0,0,0,0,0,0,67,186,240,248,212,118, 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,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,27, 0,0,0,0,42,0,0,0,0,0,0,0,0,0,23,237,192,0,0,0,157,251,46,0,0,0,0,0,0,0,0,6, 224,255,98,0,0,126,255,199,2,0,0,0,0,0,0,0,0,84,255,237,21,0,9,225,255,106,0,0,0,0,0,0, 0,0,0,190,255,163,0,0,86,255,241,25,0,0,0,0,0,0,0,0,44,252,255,68,0,0,192,255,171,0,0,0, 0,0,0,0,0,0,145,255,219,8,0,46,250,255,75,0,0,0,0,0,0,0,0,70,255,253,38,0,6,216,255,139, 0,0,0,0,0,0,0,6,218,255,147,0,0,120,255,233,18,0,0,0,0,0,0,0,118,255,229,14,0,26,243,255, 91,0,0,0,0,0,0,0,25,242,255,82,0,0,164,255,186,0,0,0,0,0,0,0,0,162,255,178,0,0,58,255, 247,35,0,0,0,0,0,0,0,30,254,243,30,0,0,181,255,121,0,0,0,0,0,0,0,0,3,126,104,0,0,0, 62,166,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,102,236,217,54,0,0,0,0,0,0,0,0,0,0,0, 14,250,255,255,201,0,0,0,0,0,0,0,0,0,0,0,16,252,255,255,204,0,0,0,0,0,0,0,0,0,0,0, 0,108,238,221,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,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,84,55,0,0,0,0,0,0,0,0,0,0,0,0,0,109,255,182,0,0,0,0,0,0,0,0,0,0,0,0, 0,139,255,201,0,0,0,0,0,0,0,0,0,0,0,0,0,185,255,215,0,0,0,0,0,0,0,0,0,0,0,0, 125,255,255,177,0,0,0,0,0,0,0,0,0,0,22,182,255,255,225,27,0,0,0,0,0,0,0,0,0,37,222,255, 255,173,19,0,0,0,0,0,0,0,0,0,4,212,255,254,106,0,0,0,0,0,0,0,0,0,0,0,103,255,255,136, 0,0,0,0,0,0,0,0,0,0,0,0,176,255,255,18,0,0,0,0,0,0,0,0,0,0,0,0,198,255,246,0, 0,0,0,0,0,0,0,0,0,0,0,0,182,255,255,35,0,0,0,0,0,0,1,0,0,0,0,0,120,255,255,193, 13,0,0,0,4,113,239,53,0,0,0,0,17,234,255,255,231,148,118,143,226,255,255,175,0,0,0,0,0,56,234,255, 255,255,255,255,255,255,180,21,0,0,0,0,0,0,21,132,208,244,245,219,160,68,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,77,127,128,128,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,255,255,255,163,2,0,0, 0,0,0,0,0,0,0,0,0,0,0,30,237,255,255,162,2,0,0,0,0,0,0,0,0,0,0,0,0,0,4,198, 255,255,160,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,138,255,255,159,2,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,73,254,251,148,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,1,222,255,255,147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,255,255,255,236,6,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,158,255,255,255,255,81,0,0,0,0,0,0,0,0,0,0,0,0,0,10,242,255,180, 247,255,176,0,0,0,0,0,0,0,0,0,0,0,0,0,92,255,255,93,174,255,250,20,0,0,0,0,0,0,0,0, 0,0,0,0,187,255,247,14,82,255,255,110,0,0,0,0,0,0,0,0,0,0,0,28,253,255,171,0,7,240,255,205, 0,0,0,0,0,0,0,0,0,0,0,121,255,255,81,0,0,158,255,255,44,0,0,0,0,0,0,0,0,0,0,216, 255,239,7,0,0,68,255,255,139,0,0,0,0,0,0,0,0,0,55,255,255,156,0,0,0,3,231,255,231,3,0,0, 0,0,0,0,0,0,151,255,255,65,0,0,0,0,145,255,255,73,0,0,0,0,0,0,0,7,238,255,229,2,0,0, 0,0,55,255,255,169,0,0,0,0,0,0,0,85,255,255,140,0,0,0,0,0,0,220,255,248,16,0,0,0,0,0, 0,180,255,255,110,64,64,64,64,64,64,170,255,255,103,0,0,0,0,0,23,251,255,255,255,255,255,255,255,255,255,255, 255,255,198,0,0,0,0,0,114,255,255,255,255,255,255,255,255,255,255,255,255,255,255,38,0,0,0,0,209,255,255,42, 0,0,0,0,0,0,0,0,124,255,255,132,0,0,0,48,255,255,209,0,0,0,0,0,0,0,0,0,34,255,255,225, 1,0,0,143,255,255,120,0,0,0,0,0,0,0,0,0,0,200,255,255,66,0,4,233,255,254,31,0,0,0,0,0, 0,0,0,0,0,110,255,255,161,0,77,255,255,197,0,0,0,0,0,0,0,0,0,0,0,23,252,255,244,12,172,255, 244,80,0,0,0,0,0,0,0,0,0,0,0,0,149,254,255,96,0,0,0,0,0,0,20,108,188,225,248,237,211,146, 59,0,0,0,0,0,0,0,0,108,239,255,255,255,255,255,255,255,255,183,29,0,0,0,0,2,155,255,255,255,198,123, 95,103,154,240,255,255,229,9,0,0,0,136,255,255,235,74,0,0,0,0,0,16,161,255,122,0,0,0,59,253,255,245, 49,0,0,0,0,0,0,0,0,43,1,0,0,0,187,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,40, 255,255,243,12,0,0,0,0,0,0,0,0,0,0,0,0,0,110,255,255,167,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,169,255,255,111,0,0,0,0,0,0,0,0,0,0,0,0,0,0,199,255,255,75,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,218,255,255,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,220,255,255,60,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,204,255,255,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,176,255,255, 115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,122,255,255,173,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,54,255,255,247,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,211,255,255,132,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,87,255,255,249,58,0,0,0,0,0,0,0,0,102,48,0,0,0,0,181,255,255,240,80,0,0, 0,0,0,13,157,255,235,39,0,0,0,13,204,255,255,255,199,122,94,104,148,233,255,255,213,20,0,0,0,0,11,157, 255,255,255,255,255,255,255,255,255,178,22,0,0,0,0,0,0,0,44,148,211,252,255,244,219,164,69,0,0,0,0,0, 0,0,0,0,0,0,0,196,240,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,251,243,144,37,0,0,0, 0,0,0,0,0,0,0,0,0,0,22,128,205,255,247,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,206, 255,135,0,0,0,0,0,0,0,0,0,0,0,1,117,55,69,236,255,83,0,0,0,0,0,0,0,0,0,0,0,25, 202,243,244,206,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,77,127,128,128,51,0,0,0,0,0,0,0, 0,0,0,0,78,255,255,255,163,2,0,0,0,0,0,0,0,0,0,0,30,237,255,255,162,2,0,0,0,0,0,0, 0,0,0,0,4,198,255,255,160,2,0,0,0,0,0,0,0,0,0,0,0,138,255,255,159,2,0,0,0,0,0,0, 0,0,0,0,0,73,254,251,148,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,152,255,255,255,255,255,255,255,255,255,255,255, 255,40,0,0,0,152,255,255,255,255,255,255,255,255,255,255,255,255,40,0,0,0,152,255,255,164,88,88,88,88,88,88, 88,88,88,13,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0, 0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0, 0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116, 0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,164,88,88,88,88,88,88,88,23,0,0,0,0,0,152,255, 255,255,255,255,255,255,255,255,255,68,0,0,0,0,0,152,255,255,255,255,255,255,255,255,255,255,68,0,0,0,0,0, 152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0, 0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0, 0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0, 0,0,0,0,0,0,152,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,164,88,88,88,88,88, 88,88,88,88,14,0,0,0,152,255,255,255,255,255,255,255,255,255,255,255,255,42,0,0,0,152,255,255,255,255,255,255, 255,255,255,255,255,255,40,0,0,0,0,0,0,38,124,128,128,93,0,0,0,0,20,228,255,255,220,29,0,0,0,1, 182,255,255,220,28,0,0,0,0,119,255,255,219,28,0,0,0,0,58,251,255,218,27,0,0,0,0,18,226,255,204,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,60,255,255,204,0,0, 0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0, 0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0, 60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255, 255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204, 0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0, 0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0, 0,0,60,255,255,204,0,0,0,0,0,0,60,255,255,204,0,0,0,0,0,0,0,0,0,0,0,62,218,247,184,55, 0,0,55,255,155,0,0,0,0,0,0,0,0,0,0,0,25,246,255,255,255,254,164,103,212,255,113,0,0,0,0,0, 0,0,0,0,0,0,123,255,208,101,169,255,255,255,255,243,20,0,0,0,0,0,0,0,0,0,0,0,167,255,52,0, 0,62,189,248,219,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,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,221,29,0,0, 0,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,195,3,0,0,0,0,0,0,0,0,0,44,255, 255,124,0,0,0,0,140,255,255,255,129,0,0,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,255, 252,62,0,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,255,255,226,18,0,0,0,0,0,0,0, 44,255,255,124,0,0,0,0,140,255,255,202,255,255,174,0,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255, 255,51,233,255,255,105,0,0,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,28,72,254,255,247,44,0,0,0, 0,0,44,255,255,124,0,0,0,0,140,255,255,28,0,141,255,255,212,9,0,0,0,0,44,255,255,124,0,0,0,0, 140,255,255,28,0,6,204,255,255,152,0,0,0,0,44,255,255,124,0,0,0,0,140,255,255,28,0,0,36,243,255,255, 82,0,0,0,44,255,255,124,0,0,0,0,140,255,255,28,0,0,0,93,255,255,238,29,0,0,44,255,255,124,0,0, 0,0,140,255,255,28,0,0,0,0,162,255,255,194,3,0,44,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0, 12,219,255,255,128,0,44,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,51,249,255,252,61,44,255,255,124, 0,0,0,0,140,255,255,28,0,0,0,0,0,0,114,255,255,226,59,255,255,124,0,0,0,0,140,255,255,28,0,0, 0,0,0,0,1,182,255,255,198,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,0,22,231,255,255,255, 255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,0,0,68,254,255,255,255,124,0,0,0,0,140,255,255,28, 0,0,0,0,0,0,0,0,0,136,255,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,0,0,0,4, 200,255,255,124,0,0,0,0,140,255,255,28,0,0,0,0,0,0,0,0,0,0,32,221,255,124,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,55,126,128,128,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,245, 255,255,184,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,226,255,255,163,5,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,6,197,255,255,141,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,157,255,254,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,128,126,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,1,73,154,212,237,248, 226,192,118,31,0,0,0,0,0,0,0,0,0,0,0,0,58,204,255,255,255,255,255,255,255,255,249,140,12,0,0,0, 0,0,0,0,0,0,95,251,255,255,229,148,107,95,120,187,252,255,255,204,22,0,0,0,0,0,0,0,81,255,255,252, 121,6,0,0,0,0,0,42,199,255,255,208,7,0,0,0,0,0,27,238,255,254,85,0,0,0,0,0,0,0,0,10, 194,255,255,145,0,0,0,0,0,147,255,255,153,0,0,0,0,0,0,0,0,0,0,31,243,255,250,29,0,0,0,15, 245,255,252,29,0,0,0,0,0,0,0,0,0,0,0,149,255,255,137,0,0,0,80,255,255,189,0,0,0,0,0,0, 0,0,0,0,0,0,57,255,255,211,0,0,0,143,255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,6,248,255, 254,19,0,0,175,255,255,97,0,0,0,0,0,0,0,0,0,0,0,0,0,219,255,255,50,0,0,194,255,255,81,0, 0,0,0,0,0,0,0,0,0,0,0,0,202,255,255,70,0,0,195,255,255,86,0,0,0,0,0,0,0,0,0,0, 0,0,0,207,255,255,69,0,0,175,255,255,104,0,0,0,0,0,0,0,0,0,0,0,0,0,225,255,255,50,0,0, 144,255,255,149,0,0,0,0,0,0,0,0,0,0,0,0,17,254,255,255,20,0,0,82,255,255,213,0,0,0,0,0, 0,0,0,0,0,0,0,81,255,255,213,0,0,0,17,247,255,255,59,0,0,0,0,0,0,0,0,0,0,0,181,255, 255,140,0,0,0,0,150,255,255,201,3,0,0,0,0,0,0,0,0,0,69,255,255,251,31,0,0,0,0,29,240,255, 255,154,2,0,0,0,0,0,0,0,44,232,255,255,148,0,0,0,0,0,0,84,255,255,255,194,56,0,0,0,0,14, 114,241,255,255,210,8,0,0,0,0,0,0,0,98,251,255,255,255,226,186,175,199,249,255,255,255,205,23,0,0,0,0, 0,0,0,0,0,59,205,255,255,255,255,255,255,255,255,249,142,12,0,0,0,0,0,0,0,0,0,0,0,1,73,155, 213,238,248,227,193,119,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,127,128,128,40,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,111,255,255,255,125,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,69,252,255,252,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,238,255,247,79,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,215,255,238,60,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,65,128,123,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, 172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0, 0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0, 0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0, 0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124, 0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0, 0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255, 255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88, 0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0, 136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,165,255, 255,91,0,0,0,0,0,0,0,0,0,0,139,255,255,117,0,0,0,0,145,255,255,109,0,0,0,0,0,0,0,0, 0,0,157,255,255,96,0,0,0,0,101,255,255,158,0,0,0,0,0,0,0,0,0,0,207,255,255,53,0,0,0,0, 32,254,255,237,9,0,0,0,0,0,0,0,0,40,254,255,236,2,0,0,0,0,0,178,255,255,134,0,0,0,0,0, 0,0,1,183,255,255,129,0,0,0,0,0,0,42,245,255,254,120,2,0,0,0,0,11,161,255,255,223,16,0,0,0, 0,0,0,0,84,250,255,255,215,132,97,101,145,231,255,255,237,49,0,0,0,0,0,0,0,0,0,62,219,255,255,255, 255,255,255,255,255,200,38,0,0,0,0,0,0,0,0,0,0,0,5,94,174,224,246,243,219,161,76,0,0,0,0,0, 0,0,0,0,0,0,0,1,156,240,142,0,0,0,26,201,232,80,0,0,0,0,0,0,0,0,0,0,0,65,255,255, 255,54,0,0,141,255,255,233,0,0,0,0,0,0,0,0,0,0,0,67,255,255,255,56,0,0,143,255,255,236,0,0, 0,0,0,0,0,0,0,0,0,1,161,241,148,0,0,0,29,205,234,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,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255, 255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0, 0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0, 172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0, 0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0, 0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0, 0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124, 0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0, 0,0,0,0,0,0,0,0,136,255,255,124,0,0,0,0,172,255,255,88,0,0,0,0,0,0,0,0,0,0,136,255, 255,124,0,0,0,0,165,255,255,91,0,0,0,0,0,0,0,0,0,0,139,255,255,117,0,0,0,0,145,255,255,109, 0,0,0,0,0,0,0,0,0,0,157,255,255,96,0,0,0,0,101,255,255,158,0,0,0,0,0,0,0,0,0,0, 207,255,255,53,0,0,0,0,32,254,255,237,9,0,0,0,0,0,0,0,0,40,254,255,236,2,0,0,0,0,0,178, 255,255,134,0,0,0,0,0,0,0,1,183,255,255,129,0,0,0,0,0,0,42,245,255,254,120,2,0,0,0,0,11, 161,255,255,223,16,0,0,0,0,0,0,0,84,250,255,255,215,132,97,101,145,231,255,255,237,49,0,0,0,0,0,0, 0,0,0,62,219,255,255,255,255,255,255,255,255,200,38,0,0,0,0,0,0,0,0,0,0,0,5,94,174,224,246,243, 219,161,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,253,255,199,9,0,0,0,0,0,0,0,0,0, 0,87,255,255,220,21,0,0,0,0,0,0,0,0,0,0,10,226,255,236,38,0,0,0,0,0,0,0,0,0,0,0, 130,255,248,60,0,0,0,0,0,0,0,0,0,0,0,15,195,197,76,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,122, 195,232,249,232,174,61,0,0,0,0,0,0,1,119,244,255,255,255,255,255,255,255,116,0,0,0,0,0,142,255,255,243, 154,101,102,156,253,255,254,61,0,0,0,0,53,210,140,16,0,0,0,0,88,255,255,179,0,0,0,0,0,0,0,0, 0,0,0,0,0,219,255,246,5,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,33,0,0,0,0,0,0,0, 0,0,0,0,0,168,255,255,47,0,0,0,0,0,0,40,118,170,212,234,245,255,255,255,48,0,0,0,0,56,193,255, 255,255,255,255,255,255,255,255,48,0,0,0,94,251,255,255,239,178,134,106,95,198,255,255,48,0,0,34,248,255,245,100, 6,0,0,0,0,168,255,255,48,0,0,122,255,255,105,0,0,0,0,0,0,168,255,255,48,0,0,150,255,255,65,0, 0,0,0,0,1,191,255,255,48,0,0,126,255,255,138,0,0,0,0,20,174,255,255,255,48,0,0,48,255,255,253,163, 99,105,154,239,250,177,255,255,48,0,0,0,146,255,255,255,255,255,255,232,70,52,255,255,48,0,0,0,1,102,206,247, 242,210,129,20,0,6,223,255,48,0,0,0,0,0,5,101,189,232,249,230,174,76,0,0,0,0,0,34,211,255,255,255, 255,255,255,255,174,10,0,0,20,226,255,255,222,128,94,114,203,255,255,74,0,0,161,255,255,184,10,0,0,0,0,94, 122,0,0,27,252,255,241,20,0,0,0,0,0,0,0,0,0,111,255,255,152,0,0,0,0,0,0,0,0,0,0,168, 255,255,87,0,0,0,0,0,0,0,0,0,0,200,255,255,51,0,0,0,0,0,0,0,0,0,0,215,255,255,39,0, 0,0,0,0,0,0,0,0,0,207,255,255,46,0,0,0,0,0,0,0,0,0,0,186,255,255,73,0,0,0,0,0, 0,0,0,0,0,138,255,255,126,0,0,0,0,0,0,0,0,0,0,68,255,255,217,3,0,0,0,0,0,0,0,0, 0,1,218,255,255,119,0,0,0,0,0,51,133,2,0,0,74,255,255,254,143,36,5,29,128,247,255,119,0,0,0,120, 255,255,255,255,255,255,255,255,221,30,0,0,0,0,80,216,255,255,255,255,237,138,12,0,0,0,0,0,0,1,43,240, 217,50,5,0,0,0,0,0,0,0,0,0,44,255,216,90,4,0,0,0,0,0,0,0,0,0,41,144,227,255,193,3, 0,0,0,0,0,0,0,0,0,0,34,255,255,44,0,0,0,0,0,0,0,15,160,101,159,255,242,14,0,0,0,0, 0,0,0,40,202,245,238,186,55,0,0,0,0,0,0,0,0,0,0,0,13,202,255,255,155,0,0,0,0,0,0,0, 0,0,0,0,0,140,255,255,183,4,0,0,0,0,0,0,0,0,0,0,0,39,250,255,206,12,0,0,0,0,0,0, 0,0,0,0,0,0,184,255,225,25,0,0,0,0,0,0,0,0,0,0,0,0,47,196,180,39,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,65,160,222,246,240,200,116,10,0,0,0,0,0,0,0,11,172,255,255,255,255,255,255,255, 224,42,0,0,0,0,0,5,197,255,255,235,140,97,102,167,253,255,232,22,0,0,0,0,132,255,255,201,17,0,0,0, 0,65,250,255,165,0,0,0,17,243,255,246,37,0,0,0,0,0,0,145,255,251,27,0,0,95,255,255,158,0,0,0, 0,0,0,0,33,255,255,95,0,0,153,255,255,90,0,0,0,0,0,0,0,0,249,255,145,0,0,179,255,255,255,255, 255,255,255,255,255,255,255,255,255,165,0,0,185,255,255,255,255,255,255,255,255,255,255,255,255,255,167,0,0,168,255,255, 116,88,88,88,88,88,88,88,88,88,88,36,0,0,134,255,255,66,0,0,0,0,0,0,0,0,0,0,0,0,0,72, 255,255,130,0,0,0,0,0,0,0,0,0,0,0,0,0,6,229,255,233,17,0,0,0,0,0,0,0,0,0,0,0, 0,0,109,255,255,193,20,0,0,0,0,0,23,133,123,0,0,0,0,1,182,255,255,241,154,103,91,112,175,249,255,253, 49,0,0,0,0,7,160,255,255,255,255,255,255,255,255,230,79,0,0,0,0,0,0,0,56,155,218,245,246,225,173,99, 8,0,0,0,0,0,0,0,16,208,255,255,146,0,0,0,0,0,153,255,255,167,1,0,0,0,0,55,254,255,185,5, 0,0,0,0,3,207,255,202,11,0,0,0,0,0,45,148,133,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0, 144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255, 255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92, 0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0, 0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0, 0,0,144,255,255,92,0,0,0,0,0,0,144,255,255,92,0,0,0,0,0,0,0,0,0,0,58,68,12,0,0,0, 71,58,0,0,0,0,0,0,0,0,1,164,255,255,238,118,18,61,251,144,0,0,0,0,0,0,0,0,80,255,255,255, 255,255,255,255,255,74,0,0,0,0,0,0,0,0,153,251,60,21,122,240,255,255,161,0,0,0,0,0,0,0,0,0, 61,68,0,0,0,14,71,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,172,0,0,71,177,229,249,226,154,31, 0,0,0,0,0,0,172,255,234,8,163,255,255,255,255,255,255,240,53,0,0,0,0,0,172,255,255,190,255,217,127,95, 120,221,255,255,221,6,0,0,0,0,172,255,255,254,124,2,0,0,0,16,227,255,255,85,0,0,0,0,172,255,255,121, 0,0,0,0,0,0,112,255,255,152,0,0,0,0,172,255,255,64,0,0,0,0,0,0,62,255,255,186,0,0,0,0, 172,255,255,64,0,0,0,0,0,0,37,255,255,199,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200, 0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0, 36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0, 0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255, 255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0, 0,0,172,255,255,64,0,0,0,0,0,0,36,255,255,200,0,0,0,0,172,255,255,64,0,0,0,0,0,0,36,255, 255,200,0,0,0,0,0,0,0,0,0,0,5,187,254,255,177,2,0,0,0,0,0,0,0,0,0,0,0,0,116,255, 255,201,10,0,0,0,0,0,0,0,0,0,0,0,0,24,241,255,221,22,0,0,0,0,0,0,0,0,0,0,0,0, 0,161,255,236,38,0,0,0,0,0,0,0,0,0,0,0,0,0,30,194,185,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,65,158,220,244,244,219,155,59,0,0,0,0,0,0,0,0,11,173,255,255,255,255,255,255,255, 255,163,7,0,0,0,0,0,5,196,255,255,239,148,100,101,153,244,255,255,183,1,0,0,0,0,129,255,255,209,24,0, 0,0,0,32,220,255,255,111,0,0,0,14,241,255,249,36,0,0,0,0,0,0,51,254,255,230,6,0,0,88,255,255, 172,0,0,0,0,0,0,0,0,192,255,255,68,0,0,146,255,255,109,0,0,0,0,0,0,0,0,129,255,255,128,0, 0,174,255,255,77,0,0,0,0,0,0,0,0,94,255,255,156,0,0,187,255,255,67,0,0,0,0,0,0,0,0,83, 255,255,171,0,0,174,255,255,76,0,0,0,0,0,0,0,0,93,255,255,156,0,0,146,255,255,108,0,0,0,0,0, 0,0,0,127,255,255,128,0,0,88,255,255,170,0,0,0,0,0,0,0,0,190,255,255,68,0,0,14,241,255,248,33, 0,0,0,0,0,0,48,253,255,230,6,0,0,0,129,255,255,204,20,0,0,0,0,27,216,255,255,111,0,0,0,0, 5,196,255,255,235,143,98,100,148,241,255,255,183,2,0,0,0,0,0,11,172,255,255,255,255,255,255,255,255,162,7,0, 0,0,0,0,0,0,0,64,159,220,245,245,219,156,58,0,0,0,0,0,0,0,0,0,0,0,0,0,10,198,255,255, 162,1,0,0,0,0,0,0,0,0,0,0,0,0,137,255,255,180,4,0,0,0,0,0,0,0,0,0,0,0,0,43, 250,255,197,10,0,0,0,0,0,0,0,0,0,0,0,0,0,194,255,212,17,0,0,0,0,0,0,0,0,0,0,0, 0,0,36,148,136,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255, 255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0, 0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0, 0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0, 236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140, 0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0, 96,255,255,140,0,0,0,0,235,255,255,1,0,0,0,0,0,0,96,255,255,140,0,0,0,0,223,255,255,24,0,0, 0,0,0,0,96,255,255,140,0,0,0,0,189,255,255,73,0,0,0,0,0,0,144,255,255,140,0,0,0,0,122,255, 255,196,4,0,0,0,7,143,255,255,255,140,0,0,0,0,23,241,255,255,204,111,96,133,224,255,200,255,255,140,0,0, 0,0,0,80,250,255,255,255,255,255,254,145,20,255,255,140,0,0,0,0,0,0,46,168,232,249,227,169,61,0,0,211, 255,140,0,0,0,0,0,0,74,231,203,30,0,0,50,219,219,50,0,0,0,0,0,0,0,0,221,255,255,154,0,0, 185,255,255,190,0,0,0,0,0,0,0,0,223,255,255,156,0,0,187,255,255,192,0,0,0,0,0,0,0,0,79,233, 208,33,0,0,54,223,222,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140, 0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0, 96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0, 0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255, 255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0, 0,0,236,255,255,0,0,0,0,0,0,0,96,255,255,140,0,0,0,0,235,255,255,1,0,0,0,0,0,0,96,255, 255,140,0,0,0,0,223,255,255,24,0,0,0,0,0,0,96,255,255,140,0,0,0,0,189,255,255,73,0,0,0,0, 0,0,144,255,255,140,0,0,0,0,122,255,255,196,4,0,0,0,7,143,255,255,255,140,0,0,0,0,23,241,255,255, 204,111,96,133,224,255,200,255,255,140,0,0,0,0,0,80,250,255,255,255,255,255,254,145,20,255,255,140,0,0,0,0, 0,0,46,168,232,249,227,169,61,0,0,211,255,140,0,0,0,0,53,88,88,88,88,88,88,88,88,88,88,88,48,0, 0,0,0,156,255,255,255,255,255,255,255,255,255,255,255,140,0,0,0,0,156,255,255,255,255,255,255,255,255,255,255,255, 140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,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,82,2,0,0,0,0,0,148,255,93,0,0,0,0,63,255,224,9,0,0,0,0,179,255,122,0,0,0,0,7,247, 255,64,0,0,0,0,34,255,255,64,0,0,0,0,35,255,255,90,0,0,0,0,7,248,255,174,0,0,0,0,0,181, 255,247,10,0,0,0,0,61,142,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,81,188,81,0,0,0,0,14,250,255,191,0,0,0,0,0,161,255,250,6,0,0,0,0,84, 255,255,31,0,0,0,0,57,255,255,23,0,0,0,0,79,255,235,1,0,0,0,0,141,255,157,0,0,0,0,22,240, 249,40,0,0,0,0,87,255,108,0,0,0,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,24,0,0,0,0,3,21,0,0,0,0,0,0,35,233,156,0,0,0,156,239,28,0, 0,0,0,0,194,255,100,0,0,83,255,210,2,0,0,0,0,67,255,225,2,0,0,210,255,84,0,0,0,0,0,152, 255,161,0,0,40,255,254,18,0,0,0,0,0,202,255,138,0,0,90,255,250,0,0,0,0,0,0,216,255,153,0,0, 104,255,255,9,0,0,0,0,0,196,255,210,0,0,84,255,255,66,0,0,0,0,0,141,255,255,59,0,29,255,255,171, 0,0,0,0,0,49,255,205,40,0,0,192,240,117,0,0,0,0,0,0,40,0,0,0,0,30,10,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,88,92,0,0,0,44,136,6,0,0,0,0, 0,181,255,235,4,0,69,255,255,95,0,0,0,0,0,121,255,255,59,0,20,245,255,171,0,0,0,0,0,29,255,255, 98,0,0,173,255,210,0,0,0,0,0,0,241,255,107,0,0,129,255,219,0,0,0,0,0,0,241,255,80,0,0,130, 255,192,0,0,0,0,0,36,255,252,22,0,0,180,255,131,0,0,0,0,0,119,255,174,0,0,20,242,252,33,0,0, 0,0,15,240,246,36,0,0,144,255,139,0,0,0,0,0,8,155,79,0,0,0,76,162,5,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,208,236,109,0,0,0,0,3,157,241,180,15,0,0,0,0, 37,203,237,119,0,0,185,255,255,254,32,0,0,0,85,255,255,255,130,0,0,0,0,173,255,255,255,42,0,188,255,255, 255,34,0,0,0,88,255,255,255,134,0,0,0,0,176,255,255,255,46,0,48,213,238,116,0,0,0,0,4,163,243,185, 18,0,0,0,0,41,208,239,126,0, }; // _DISPLAY: generado con tools/build-ui-font.py static constexpr int UI_FONT_ASCENT_DISPLAY = 36; static constexpr int UI_FONT_DESCENT_DISPLAY = 12; static constexpr int UI_FONT_LINE_HEIGHT_DISPLAY = 48; static const UiGlyph UI_GLYPHS_DISPLAY[] = { {32u, 0, 0, 0, 0, 566, 0u}, {33u, 11, 24, 0, -24, 698, 0u}, {34u, 11, 27, 0, -27, 703, 264u}, {35u, 30, 25, 0, -25, 1897, 561u}, {36u, 22, 30, 0, -27, 1414, 1311u}, {37u, 27, 24, 0, -24, 1728, 1971u}, {38u, 26, 24, 0, -24, 1656, 2619u}, {39u, 6, 27, 0, -27, 374, 3243u}, {40u, 17, 38, 0, -32, 1071, 3405u}, {41u, 17, 38, 0, -32, 1071, 4051u}, {42u, 17, 25, 0, -25, 1097, 4697u}, {43u, 24, 21, 0, -21, 1554, 5122u}, {44u, 8, 9, 0, -4, 490, 5626u}, {45u, 20, 15, 0, -15, 1273, 5698u}, {46u, 8, 5, 0, -5, 524, 5998u}, {47u, 17, 38, 0, -32, 1118, 6038u}, {48u, 22, 25, 0, -25, 1395, 6684u}, {49u, 12, 25, 0, -25, 762, 7234u}, {50u, 20, 25, 0, -25, 1256, 7534u}, {51u, 20, 26, 0, -26, 1293, 8034u}, {52u, 22, 25, 0, -25, 1408, 8554u}, {53u, 22, 25, 0, -25, 1390, 9104u}, {54u, 22, 26, 0, -26, 1397, 9654u}, {55u, 19, 25, 0, -25, 1219, 10226u}, {56u, 22, 26, 0, -26, 1388, 10701u}, {57u, 22, 26, 0, -26, 1382, 11273u}, {58u, 8, 19, 0, -19, 533, 11845u}, {59u, 10, 24, 0, -19, 662, 11997u}, {60u, 21, 20, 0, -20, 1332, 12237u}, {61u, 26, 18, 0, -18, 1684, 12657u}, {62u, 20, 20, 0, -20, 1284, 13125u}, {63u, 18, 24, 0, -24, 1171, 13525u}, {64u, 35, 29, 0, -22, 2230, 13957u}, {65u, 24, 24, 0, -24, 1517, 14972u}, {66u, 21, 24, 0, -24, 1371, 15548u}, {67u, 26, 24, 0, -24, 1682, 16052u}, {68u, 24, 24, 0, -24, 1543, 16676u}, {69u, 18, 24, 0, -24, 1140, 17252u}, {70u, 18, 24, 0, -24, 1121, 17684u}, {71u, 26, 24, 0, -24, 1682, 18116u}, {72u, 24, 24, 0, -24, 1534, 18740u}, {73u, 9, 24, 0, -24, 574, 19316u}, {74u, 19, 24, 0, -24, 1227, 19532u}, {75u, 22, 24, 0, -24, 1377, 19988u}, {76u, 15, 24, 0, -24, 966, 20516u}, {77u, 30, 24, 0, -24, 1919, 20876u}, {78u, 25, 24, 0, -24, 1569, 21596u}, {79u, 27, 24, 0, -24, 1706, 22196u}, {80u, 20, 24, 0, -24, 1295, 22844u}, {81u, 27, 28, 0, -24, 1713, 23324u}, {82u, 21, 24, 0, -24, 1375, 24080u}, {83u, 21, 24, 0, -24, 1314, 24584u}, {84u, 19, 24, 0, -24, 1227, 25088u}, {85u, 23, 24, 0, -24, 1501, 25544u}, {86u, 24, 24, 0, -24, 1510, 26096u}, {87u, 34, 24, 0, -24, 2174, 26672u}, {88u, 22, 24, 0, -24, 1436, 27488u}, {89u, 21, 24, 0, -24, 1316, 28016u}, {90u, 19, 24, 0, -24, 1214, 28520u}, {91u, 16, 38, 0, -32, 1055, 28976u}, {92u, 24, 38, 0, -32, 1560, 29584u}, {93u, 16, 38, 0, -32, 1053, 30496u}, {94u, 22, 24, 0, -24, 1423, 31104u}, {95u, 27, 5, 0, 0, 1736, 31632u}, {96u, 9, 27, 0, -27, 553, 31767u}, {97u, 23, 19, 0, -19, 1475, 32010u}, {98u, 23, 25, 0, -25, 1475, 32447u}, {99u, 20, 19, 0, -19, 1306, 33022u}, {100u, 23, 25, 0, -25, 1475, 33402u}, {101u, 21, 19, 0, -19, 1343, 33977u}, {102u, 11, 27, 0, -27, 722, 34376u}, {103u, 23, 28, 0, -19, 1475, 34673u}, {104u, 22, 25, 0, -25, 1412, 35317u}, {105u, 9, 26, 0, -26, 574, 35867u}, {106u, 10, 35, -1, -26, 574, 36101u}, {107u, 20, 25, 0, -25, 1206, 36451u}, {108u, 9, 25, 0, -25, 574, 36951u}, {109u, 35, 19, 0, -19, 2261, 37176u}, {110u, 22, 19, 0, -19, 1412, 37841u}, {111u, 22, 19, 0, -19, 1388, 38259u}, {112u, 23, 28, 0, -19, 1475, 38677u}, {113u, 23, 28, 0, -19, 1475, 39321u}, {114u, 13, 19, 0, -19, 836, 39965u}, {115u, 18, 19, 0, -19, 1162, 40212u}, {116u, 13, 24, 0, -24, 809, 40554u}, {117u, 22, 19, 0, -19, 1412, 40866u}, {118u, 20, 19, 0, -19, 1253, 41284u}, {119u, 28, 19, 0, -19, 1795, 41664u}, {120u, 17, 19, 0, -19, 1086, 42196u}, {121u, 20, 28, 0, -19, 1266, 42519u}, {122u, 16, 19, 0, -19, 1025, 43079u}, {123u, 18, 38, 0, -32, 1123, 43383u}, {124u, 11, 31, 0, -27, 705, 44067u}, {125u, 18, 38, 0, -32, 1123, 44408u}, {126u, 19, 14, 0, -14, 1203, 45092u}, {161u, 11, 24, 0, -19, 698, 45358u}, {170u, 16, 24, 0, -24, 992, 45622u}, {171u, 17, 16, 0, -16, 1064, 46006u}, {176u, 15, 24, 0, -24, 929, 46278u}, {183u, 8, 14, 0, -14, 531, 46638u}, {186u, 15, 24, 0, -24, 966, 46750u}, {187u, 17, 16, 0, -16, 1064, 47110u}, {191u, 18, 25, 0, -19, 1171, 47382u}, {193u, 24, 33, 0, -33, 1517, 47832u}, {199u, 26, 33, 0, -24, 1682, 48624u}, {201u, 18, 33, 0, -33, 1140, 49482u}, {205u, 9, 33, 0, -33, 574, 50076u}, {209u, 25, 30, 0, -30, 1569, 50373u}, {211u, 27, 33, 0, -33, 1706, 51123u}, {218u, 23, 33, 0, -33, 1501, 52014u}, {220u, 23, 30, 0, -30, 1501, 52773u}, {225u, 23, 27, 0, -27, 1475, 53463u}, {231u, 20, 28, 0, -19, 1306, 54084u}, {233u, 21, 27, 0, -27, 1343, 54644u}, {237u, 9, 28, 0, -28, 574, 55211u}, {241u, 22, 26, 0, -26, 1412, 55463u}, {243u, 22, 28, 0, -28, 1388, 56035u}, {250u, 22, 28, 0, -28, 1412, 56651u}, {252u, 22, 25, 0, -25, 1412, 57267u}, {8211u, 24, 15, 0, -15, 1541, 57817u}, {8216u, 8, 24, 0, -24, 527, 58177u}, {8217u, 8, 24, 0, -24, 527, 58369u}, {8220u, 14, 24, 0, -24, 918, 58561u}, {8221u, 14, 24, 0, -24, 918, 58897u}, {8230u, 22, 5, 0, -5, 1388, 59233u}, }; static const std::uint8_t UI_FONT_ALPHA_DISPLAY[] = { 0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0, 0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0, 0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,190,243,191,33,0,0,0,0,0,0,191, 255,255,255,190,0,0,0,0,0,0,244,255,255,255,243,0,0,0,0,0,0,192,255,255,255,191,0,0,0,0,0,0, 33,191,244,192,33,0,0,0,0,255,255,255,0,0,255,255,255,0,0,0,255,255,255,0,0,255,255,255,0,0,0,255, 255,255,0,0,255,255,255,0,0,0,255,255,255,0,0,255,255,255,0,0,0,255,255,255,0,0,255,255,255,0,0,0, 255,255,255,0,0,255,255,255,0,0,0,255,255,255,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,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,255,255,23, 0,0,0,0,0,28,255,255,255,170,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,233,255,255,221,0,0,0, 0,0,0,86,255,255,255,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,255,255,255,164,0,0,0,0,0, 0,144,255,255,255,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,255,255,255,106,0,0,0,0,0,0,201, 255,255,248,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,255,255,255,49,0,0,0,0,0,8,249,255,255, 197,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,255,255,244,3,0,0,0,0,0,59,255,255,255,140,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,255,255,255,255,255,255,255,255, 255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0, 0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0, 0,0,92,92,92,92,92,210,255,255,255,101,92,92,92,92,92,115,255,255,255,196,92,92,92,92,92,0,0,0,0,0, 0,0,0,0,0,228,255,255,228,0,0,0,0,0,0,80,255,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,23,255,255,255,176,0,0,0,0,0,0,132,255,255,255,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 76,255,255,255,123,0,0,0,0,0,0,184,255,255,254,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,255, 255,255,71,0,0,0,0,0,0,236,255,255,219,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,255,255,255, 19,0,0,0,0,0,33,255,255,255,167,0,0,0,0,0,0,0,0,0,92,92,92,92,92,92,235,255,255,240,92,92, 92,92,92,92,140,255,255,255,171,92,92,92,92,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,202,255,255,247,6,0,0,0,0,0,54,255,255,255,146, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,250,255,255,194,0,0,0,0,0,0,113,255,255,255,87,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,65,255,255,255,135,0,0,0,0,0,0,173,255,255,255,29,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,124,255,255,255,75,0,0,0,0,0,0,231,255,255,226,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,183,255,255,254,18,0,0,0,0,0,35,255,255,255,167,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,2,240,255,255,213,0,0,0,0,0,0,94,255,255,255,109,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,255,255,69,3,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,40,155,236,255,255,255,255,244,170,52,0,0,0,0,0,0,0,0,0,0,0,123,252,255,255,255, 255,255,255,255,255,254,129,0,0,0,0,0,0,0,0,0,131,255,255,255,255,255,255,255,255,255,255,255,255,114,0,0, 0,0,0,0,0,51,254,255,255,255,148,45,255,255,111,216,255,255,255,245,23,0,0,0,0,0,0,162,255,255,255,137, 0,0,255,255,40,21,236,255,255,255,110,0,0,0,0,0,0,226,255,255,255,24,0,0,255,255,40,0,137,255,255,255, 167,0,0,0,0,0,0,249,255,255,249,0,0,0,255,255,40,0,20,52,52,52,38,0,0,0,0,0,0,233,255,255, 255,28,0,0,255,255,40,0,0,0,0,0,0,0,0,0,0,0,0,199,255,255,255,161,1,0,255,255,40,0,0,0, 0,0,0,0,0,0,0,0,0,106,255,255,255,255,187,58,255,255,40,0,0,0,0,0,0,0,0,0,0,0,0,8, 203,255,255,255,255,255,255,255,105,15,0,0,0,0,0,0,0,0,0,0,0,0,17,179,255,255,255,255,255,255,255,251, 186,88,2,0,0,0,0,0,0,0,0,0,0,0,65,162,241,255,255,255,255,255,255,255,200,27,0,0,0,0,0,0, 0,0,0,0,0,0,4,64,255,255,255,255,255,255,255,214,12,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 78,167,255,255,255,255,114,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,40,0,145,255,255,255,203,0,0,0, 0,0,0,0,0,0,0,0,0,0,255,255,40,0,21,255,255,255,235,0,0,0,0,0,0,94,96,96,96,17,0,0, 255,255,40,0,0,249,255,255,244,0,0,0,0,0,0,236,255,255,255,83,0,0,255,255,40,0,33,255,255,255,214,0, 0,0,0,0,0,185,255,255,255,193,1,0,255,255,40,0,154,255,255,255,139,0,0,0,0,0,0,86,255,255,255,255, 176,50,255,255,78,154,255,255,255,247,35,0,0,0,0,0,0,0,173,255,255,255,255,255,255,255,255,255,255,255,255,106, 0,0,0,0,0,0,0,0,7,153,255,255,255,255,255,255,255,255,255,251,103,0,0,0,0,0,0,0,0,0,0,0, 54,164,239,255,255,255,255,236,157,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,32,255,255,65,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,40,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,255,255,40,0,0,0,0,0,0,0,0,0,0,0,0,3,108,204,245,245,203,106,2,0,0,0, 0,0,0,0,51,254,255,255,93,0,0,0,0,0,0,4,182,255,255,255,255,255,255,178,3,0,0,0,0,0,0,196, 255,255,201,1,0,0,0,0,0,0,113,255,255,210,68,67,210,255,255,111,0,0,0,0,0,88,255,255,254,57,0,0, 0,0,0,0,0,207,255,255,61,0,0,62,255,255,205,0,0,0,0,9,225,255,255,164,0,0,0,0,0,0,0,0, 246,255,255,12,0,0,12,255,255,245,0,0,0,0,126,255,255,245,28,0,0,0,0,0,0,0,0,246,255,255,12,0, 0,14,255,255,245,0,0,0,27,245,255,255,126,0,0,0,0,0,0,0,0,0,206,255,255,62,0,0,65,255,255,204, 0,0,0,164,255,255,226,9,0,0,0,0,0,0,0,0,0,110,255,255,210,66,68,214,255,255,108,0,0,55,254,255, 255,88,0,0,0,0,0,0,0,0,0,0,3,179,255,255,255,255,255,255,176,3,0,1,200,255,255,197,0,0,0,0, 0,0,0,0,0,0,0,0,3,107,204,246,246,204,106,2,0,0,93,255,255,254,52,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,11,228,255,255,159,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,131,255,255,243,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,30,246,255,255,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,169,255,255,223, 8,0,0,0,33,76,77,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,255,83,0,0,31,188,255, 255,255,255,189,31,0,0,0,0,0,0,0,0,0,0,0,2,204,255,255,192,0,0,22,227,255,255,255,255,255,255,226, 21,0,0,0,0,0,0,0,0,0,0,98,255,255,253,48,0,0,142,255,255,189,28,28,189,255,255,141,0,0,0,0, 0,0,0,0,0,13,231,255,255,154,0,0,0,220,255,255,51,0,0,52,255,255,218,0,0,0,0,0,0,0,0,0, 136,255,255,241,22,0,0,0,249,255,255,10,0,0,11,255,255,248,0,0,0,0,0,0,0,0,34,248,255,255,116,0, 0,0,0,243,255,255,13,0,0,14,255,255,242,0,0,0,0,0,0,0,0,174,255,255,219,6,0,0,0,0,198,255, 255,64,0,0,65,255,255,197,0,0,0,0,0,0,0,65,255,255,255,78,0,0,0,0,0,99,255,255,212,67,67,212, 255,255,98,0,0,0,0,0,0,2,208,255,255,188,0,0,0,0,0,0,1,168,255,255,255,255,255,255,168,1,0,0, 0,0,0,0,103,255,255,252,44,0,0,0,0,0,0,0,1,101,201,245,246,202,101,1,0,0,0,0,0,0,0,0, 0,0,12,110,194,232,248,225,176,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,223,255,255,255, 255,255,255,255,164,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,234,255,255,255,255,255,255,255,255,255,132, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,146,255,255,255,241,132,106,196,255,255,255,247,12,0,0,0,0,0, 0,0,0,0,0,0,0,0,222,255,255,255,66,0,0,5,218,255,255,255,74,0,0,0,0,0,0,0,0,0,0,0, 0,0,249,255,255,249,1,0,0,0,146,255,255,255,90,0,0,0,0,0,0,0,0,0,0,0,0,0,235,255,255,255, 28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,180,255,255,255,146,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,255,255,255,254,75,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,177,255,255,255,243,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,78,199,255,255,255,255,237,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,192,255,255, 255,255,255,255,255,237,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,232,255,255,255,255,205,206,255,255,255, 237,48,0,0,0,0,173,255,255,255,129,0,0,0,8,214,255,255,255,224,73,0,13,193,255,255,255,236,48,0,0,89, 255,255,255,215,7,0,0,0,109,255,255,255,220,22,0,0,0,10,186,255,255,255,236,47,23,236,255,255,254,61,0,0, 0,0,198,255,255,255,79,0,0,0,0,0,7,179,255,255,255,236,192,255,255,255,152,0,0,0,0,0,242,255,255,255, 10,0,0,0,0,0,0,5,172,255,255,255,255,255,255,229,15,0,0,0,0,0,250,255,255,255,18,0,0,0,0,0, 0,0,3,164,255,255,255,255,255,75,0,0,0,0,0,0,221,255,255,255,106,0,0,0,0,0,0,0,0,66,252,255, 255,255,254,93,0,0,0,0,0,0,150,255,255,255,245,73,0,0,0,0,0,10,126,250,255,255,255,255,255,252,86,0, 0,0,0,0,35,247,255,255,255,255,187,118,96,114,162,238,255,255,255,255,209,255,255,255,251,79,0,0,0,0,0,77, 251,255,255,255,255,255,255,255,255,255,255,255,255,150,3,135,255,255,255,249,72,0,0,0,0,0,63,213,255,255,255,255, 255,255,255,255,255,216,80,0,0,1,170,255,255,255,246,65,0,0,0,0,0,4,78,155,211,234,250,233,209,152,75,4, 0,0,0,0,9,200,255,255,255,244,59,0,255,255,255,0,0,0,255,255,255,0,0,0,255,255,255,0,0,0,255,255, 255,0,0,0,255,255,255,0,0,0,255,255,255,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,190,255,255,254,82,0,0,0, 0,0,0,0,0,0,0,9,192,255,255,255,113,0,0,0,0,0,0,0,0,0,0,0,162,255,255,255,143,0,0,0, 0,0,0,0,0,0,0,0,105,255,255,255,201,5,0,0,0,0,0,0,0,0,0,0,27,243,255,255,247,35,0,0, 0,0,0,0,0,0,0,0,0,168,255,255,255,131,0,0,0,0,0,0,0,0,0,0,0,46,255,255,255,231,11,0, 0,0,0,0,0,0,0,0,0,0,161,255,255,255,123,0,0,0,0,0,0,0,0,0,0,0,20,249,255,255,246,20, 0,0,0,0,0,0,0,0,0,0,0,102,255,255,255,168,0,0,0,0,0,0,0,0,0,0,0,0,186,255,255,255, 84,0,0,0,0,0,0,0,0,0,0,0,8,249,255,255,249,11,0,0,0,0,0,0,0,0,0,0,0,60,255,255, 255,197,0,0,0,0,0,0,0,0,0,0,0,0,117,255,255,255,138,0,0,0,0,0,0,0,0,0,0,0,0,158, 255,255,255,90,0,0,0,0,0,0,0,0,0,0,0,0,192,255,255,255,55,0,0,0,0,0,0,0,0,0,0,0, 0,222,255,255,255,21,0,0,0,0,0,0,0,0,0,0,0,0,236,255,255,255,4,0,0,0,0,0,0,0,0,0, 0,0,0,247,255,255,249,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,242,0,0,0,0,0,0,0,0, 0,0,0,0,0,244,255,255,252,0,0,0,0,0,0,0,0,0,0,0,0,0,234,255,255,255,7,0,0,0,0,0, 0,0,0,0,0,0,0,214,255,255,255,32,0,0,0,0,0,0,0,0,0,0,0,0,182,255,255,255,67,0,0,0, 0,0,0,0,0,0,0,0,0,149,255,255,255,107,0,0,0,0,0,0,0,0,0,0,0,0,101,255,255,255,163,0, 0,0,0,0,0,0,0,0,0,0,0,45,255,255,255,222,0,0,0,0,0,0,0,0,0,0,0,0,2,237,255,255, 255,41,0,0,0,0,0,0,0,0,0,0,0,0,161,255,255,255,125,0,0,0,0,0,0,0,0,0,0,0,0,79, 255,255,255,215,1,0,0,0,0,0,0,0,0,0,0,0,5,231,255,255,255,70,0,0,0,0,0,0,0,0,0,0, 0,0,128,255,255,255,185,0,0,0,0,0,0,0,0,0,0,0,0,21,241,255,255,255,67,0,0,0,0,0,0,0, 0,0,0,0,0,119,255,255,255,208,3,0,0,0,0,0,0,0,0,0,0,0,7,214,255,255,255,126,0,0,0,0, 0,0,0,0,0,0,0,0,45,247,255,255,251,55,0,0,0,0,0,0,0,0,0,0,0,0,94,254,255,255,230,30, 0,0,0,0,0,0,0,0,0,0,0,0,109,255,255,255,212,0,0,0,206,255,255,255,149,1,0,0,0,0,0,0, 0,0,0,0,0,18,208,255,255,255,155,0,0,0,0,0,0,0,0,0,0,0,0,20,223,255,255,255,121,0,0,0, 0,0,0,0,0,0,0,0,0,49,247,255,255,253,70,0,0,0,0,0,0,0,0,0,0,0,0,106,255,255,255,225, 10,0,0,0,0,0,0,0,0,0,0,0,2,201,255,255,255,135,0,0,0,0,0,0,0,0,0,0,0,0,51,255, 255,255,247,22,0,0,0,0,0,0,0,0,0,0,0,0,181,255,255,255,130,0,0,0,0,0,0,0,0,0,0,0, 0,62,255,255,255,236,6,0,0,0,0,0,0,0,0,0,0,0,0,214,255,255,255,75,0,0,0,0,0,0,0,0, 0,0,0,0,125,255,255,255,161,0,0,0,0,0,0,0,0,0,0,0,0,39,255,255,255,234,0,0,0,0,0,0, 0,0,0,0,0,0,0,229,255,255,255,38,0,0,0,0,0,0,0,0,0,0,0,0,166,255,255,255,97,0,0,0, 0,0,0,0,0,0,0,0,0,114,255,255,255,138,0,0,0,0,0,0,0,0,0,0,0,0,77,255,255,255,173,0, 0,0,0,0,0,0,0,0,0,0,0,41,255,255,255,204,0,0,0,0,0,0,0,0,0,0,0,0,23,255,255,255, 218,0,0,0,0,0,0,0,0,0,0,0,0,11,255,255,255,230,0,0,0,0,0,0,0,0,0,0,0,0,3,255, 255,255,236,0,0,0,0,0,0,0,0,0,0,0,0,14,255,255,255,227,0,0,0,0,0,0,0,0,0,0,0,0, 25,255,255,255,216,0,0,0,0,0,0,0,0,0,0,0,0,53,255,255,255,196,0,0,0,0,0,0,0,0,0,0, 0,0,89,255,255,255,163,0,0,0,0,0,0,0,0,0,0,0,0,129,255,255,255,130,0,0,0,0,0,0,0,0, 0,0,0,0,188,255,255,255,80,0,0,0,0,0,0,0,0,0,0,0,4,244,255,255,255,23,0,0,0,0,0,0, 0,0,0,0,0,72,255,255,255,216,0,0,0,0,0,0,0,0,0,0,0,0,159,255,255,255,135,0,0,0,0,0, 0,0,0,0,0,0,12,240,255,255,255,51,0,0,0,0,0,0,0,0,0,0,0,112,255,255,255,207,0,0,0,0, 0,0,0,0,0,0,0,6,224,255,255,255,96,0,0,0,0,0,0,0,0,0,0,0,118,255,255,255,224,7,0,0, 0,0,0,0,0,0,0,0,24,242,255,255,255,85,0,0,0,0,0,0,0,0,0,0,1,184,255,255,255,186,0,0, 0,0,0,0,0,0,0,0,0,117,255,255,255,232,23,0,0,0,0,0,0,0,0,0,0,81,253,255,255,246,61,0, 0,0,0,0,0,0,0,0,0,43,247,255,255,249,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 239,255,255,24,0,0,0,0,0,0,0,0,0,0,0,0,0,204,255,242,0,0,0,0,0,0,0,0,0,0,151,112, 0,0,169,255,204,0,0,87,141,0,0,0,0,0,44,252,255,168,11,134,255,166,5,144,255,252,44,0,0,0,0,159, 255,255,255,213,138,255,154,196,255,255,255,157,0,0,0,0,2,69,163,244,255,255,255,255,255,244,161,64,1,0,0,0, 0,0,0,0,31,199,255,255,255,194,26,0,0,0,0,0,0,0,2,72,170,249,255,255,255,255,255,244,160,63,1,0, 0,0,0,159,255,255,255,220,148,255,158,207,255,255,255,157,0,0,0,0,42,252,255,189,18,139,255,162,10,166,255,253, 47,0,0,0,0,0,152,149,4,0,173,255,198,0,0,117,156,0,0,0,0,0,0,6,0,0,0,206,255,234,0,0, 0,2,0,0,0,0,0,0,0,0,0,0,239,255,255,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,92,92,92,92,92,92,92,255,255,255,245, 92,92,92,92,92,92,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0, 0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,143,255,255,255, 145,0,0,0,211,255,255,252,32,0,0,24,255,255,255,167,0,0,0,92,255,255,255,51,0,0,0,160,255,255,189,0, 0,0,0,228,255,255,72,0,0,0,41,255,255,211,0,0,0,0,110,255,255,94,0,0,0,0,178,255,228,5,0,0, 0,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,0,0,0,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0, 0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,190,243,191,33,0,0,0,191,255,255,255,190,0,0,0, 244,255,255,255,243,0,0,0,192,255,255,255,191,0,0,0,33,191,244,192,33,0,0,0,0,0,0,0,0,0,0,0, 0,216,255,255,248,7,0,0,0,0,0,0,0,0,0,0,0,22,255,255,255,194,0,0,0,0,0,0,0,0,0,0, 0,0,84,255,255,255,132,0,0,0,0,0,0,0,0,0,0,0,0,146,255,255,255,69,0,0,0,0,0,0,0,0, 0,0,0,0,208,255,255,251,11,0,0,0,0,0,0,0,0,0,0,0,15,253,255,255,201,0,0,0,0,0,0,0, 0,0,0,0,0,76,255,255,255,138,0,0,0,0,0,0,0,0,0,0,0,0,138,255,255,255,76,0,0,0,0,0, 0,0,0,0,0,0,0,200,255,255,253,16,0,0,0,0,0,0,0,0,0,0,0,10,250,255,255,207,0,0,0,0, 0,0,0,0,0,0,0,0,68,255,255,255,145,0,0,0,0,0,0,0,0,0,0,0,0,130,255,255,255,83,0,0, 0,0,0,0,0,0,0,0,0,0,192,255,255,255,21,0,0,0,0,0,0,0,0,0,0,0,6,246,255,255,214,0, 0,0,0,0,0,0,0,0,0,0,0,60,255,255,255,152,0,0,0,0,0,0,0,0,0,0,0,0,122,255,255,255, 89,0,0,0,0,0,0,0,0,0,0,0,0,184,255,255,255,27,0,0,0,0,0,0,0,0,0,0,0,3,242,255, 255,221,0,0,0,0,0,0,0,0,0,0,0,0,52,255,255,255,158,0,0,0,0,0,0,0,0,0,0,0,0,114, 255,255,255,96,0,0,0,0,0,0,0,0,0,0,0,0,176,255,255,255,34,0,0,0,0,0,0,0,0,0,0,0, 1,236,255,255,228,0,0,0,0,0,0,0,0,0,0,0,0,44,255,255,255,165,0,0,0,0,0,0,0,0,0,0, 0,0,106,255,255,255,103,0,0,0,0,0,0,0,0,0,0,0,0,168,255,255,255,41,0,0,0,0,0,0,0,0, 0,0,0,0,229,255,255,233,0,0,0,0,0,0,0,0,0,0,0,0,36,255,255,255,172,0,0,0,0,0,0,0, 0,0,0,0,0,98,255,255,255,110,0,0,0,0,0,0,0,0,0,0,0,0,160,255,255,255,47,0,0,0,0,0, 0,0,0,0,0,0,0,222,255,255,239,2,0,0,0,0,0,0,0,0,0,0,0,27,255,255,255,179,0,0,0,0, 0,0,0,0,0,0,0,0,90,255,255,255,116,0,0,0,0,0,0,0,0,0,0,0,0,152,255,255,255,54,0,0, 0,0,0,0,0,0,0,0,0,0,214,255,255,243,4,0,0,0,0,0,0,0,0,0,0,0,20,254,255,255,185,0, 0,0,0,0,0,0,0,0,0,0,0,82,255,255,255,123,0,0,0,0,0,0,0,0,0,0,0,0,144,255,255,255, 61,0,0,0,0,0,0,0,0,0,0,0,0,205,255,255,247,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,19,105,185,221,245,242,219,179,99,14,0,0,0,0,0,0,0,0,0,0,0,85,235,255,255,255,255,255,255,255, 255,227,71,0,0,0,0,0,0,0,0,0,84,254,255,255,255,255,255,255,255,255,255,255,251,67,0,0,0,0,0,0, 0,28,245,255,255,255,235,143,101,102,147,239,255,255,255,237,16,0,0,0,0,0,0,146,255,255,255,204,17,0,0,0, 0,24,214,255,255,255,126,0,0,0,0,0,7,244,255,255,249,42,0,0,0,0,0,0,53,252,255,255,231,1,0,0, 0,0,72,255,255,255,171,0,0,0,0,0,0,0,0,187,255,255,255,53,0,0,0,0,136,255,255,255,94,0,0,0, 0,0,0,0,0,110,255,255,255,119,0,0,0,0,178,255,255,255,50,0,0,0,0,0,0,0,0,67,255,255,255,160, 0,0,0,0,215,255,255,255,18,0,0,0,0,0,0,0,0,34,255,255,255,198,0,0,0,0,234,255,255,255,1,0, 0,0,0,0,0,0,0,17,255,255,255,216,0,0,0,0,245,255,255,247,0,0,0,0,0,0,0,0,0,8,255,255, 255,228,0,0,0,0,253,255,255,241,0,0,0,0,0,0,0,0,0,2,255,255,255,236,0,0,0,0,244,255,255,248, 0,0,0,0,0,0,0,0,0,9,255,255,255,227,0,0,0,0,233,255,255,255,1,0,0,0,0,0,0,0,0,18, 255,255,255,215,0,0,0,0,212,255,255,255,18,0,0,0,0,0,0,0,0,34,255,255,255,195,0,0,0,0,174,255, 255,255,48,0,0,0,0,0,0,0,0,65,255,255,255,156,0,0,0,0,133,255,255,255,90,0,0,0,0,0,0,0, 0,106,255,255,255,115,0,0,0,0,67,255,255,255,165,0,0,0,0,0,0,0,0,181,255,255,255,49,0,0,0,0, 5,241,255,255,246,36,0,0,0,0,0,0,47,250,255,255,228,0,0,0,0,0,0,142,255,255,255,199,15,0,0,0, 0,21,210,255,255,255,121,0,0,0,0,0,0,25,243,255,255,255,232,141,100,101,146,238,255,255,255,234,14,0,0,0, 0,0,0,0,81,254,255,255,255,255,255,255,255,255,255,255,250,63,0,0,0,0,0,0,0,0,0,82,234,255,255,255, 255,255,255,255,255,226,69,0,0,0,0,0,0,0,0,0,0,0,18,105,185,222,245,243,220,179,98,13,0,0,0,0, 0,0,0,0,92,92,92,92,92,92,86,0,0,0,0,0,255,255,255,255,255,255,240,0,0,0,0,0,255,255,255,255, 255,255,240,0,0,0,0,0,255,255,255,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,2,85,169,224,246,245,225,174,97,6,0,0,0, 0,0,0,0,0,0,42,211,255,255,255,255,255,255,255,255,224,55,0,0,0,0,0,0,0,46,241,255,255,255,255,255, 255,255,255,255,255,248,57,0,0,0,0,0,5,219,255,255,255,251,163,105,100,144,239,255,255,255,227,8,0,0,0,0, 96,255,255,255,244,56,0,0,0,0,29,233,255,255,255,104,0,0,0,0,175,255,255,255,120,0,0,0,0,0,0,110, 255,255,255,180,0,0,0,0,226,255,255,255,29,0,0,0,0,0,0,40,255,255,255,219,0,0,0,0,246,255,255,248, 0,0,0,0,0,0,0,9,255,255,255,235,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,255,255,255,220, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,255,255,255,177,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,174,255,255,255,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,254,255,255,242,16,0,0,0,0, 0,0,0,0,0,0,0,0,0,15,223,255,255,255,123,0,0,0,0,0,0,0,0,0,0,0,0,0,2,174,255,255, 255,211,5,0,0,0,0,0,0,0,0,0,0,0,0,0,153,255,255,255,246,50,0,0,0,0,0,0,0,0,0,0, 0,0,0,143,255,255,255,254,83,0,0,0,0,0,0,0,0,0,0,0,0,3,157,255,255,255,255,107,0,0,0,0, 0,0,0,0,0,0,0,0,9,176,255,255,255,254,105,0,0,0,0,0,0,0,0,0,0,0,0,19,198,255,255,255, 252,96,0,0,0,0,0,0,0,0,0,0,0,0,35,218,255,255,255,247,78,0,0,0,0,0,0,0,0,0,0,0, 0,60,235,255,255,255,238,61,0,0,0,0,0,0,0,0,0,0,0,0,92,248,255,255,255,254,134,92,92,92,92,92, 92,92,92,92,92,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,0,0,0,0,0,0,0,0,19,64,82,81,57,23,0,0,0,0,0,0,0,0,0,0,0,3,102,206,255,255,255, 255,255,254,198,97,1,0,0,0,0,0,0,0,28,206,255,255,255,255,255,255,255,255,255,255,195,17,0,0,0,0,0, 13,221,255,255,255,255,255,255,255,255,255,255,255,255,193,4,0,0,0,0,136,255,255,255,252,153,50,14,21,62,181,255, 255,255,255,89,0,0,0,0,228,255,255,255,105,0,0,0,0,0,0,157,255,255,255,184,0,0,0,14,255,255,255,217, 0,0,0,0,0,0,0,36,255,255,255,222,0,0,0,6,60,60,60,38,0,0,0,0,0,0,0,6,255,255,255,229, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,255,255,255,189,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,6,181,255,255,255,97,0,0,0,0,0,0,0,0,0,0,2,14,47,108,214,255,255,255,186,2,0,0,0,0, 0,0,0,0,0,172,255,255,255,255,255,255,255,174,8,0,0,0,0,0,0,0,0,0,0,172,255,255,255,255,255,250, 86,0,0,0,0,0,0,0,0,0,0,0,0,172,255,255,255,255,255,255,250,130,1,0,0,0,0,0,0,0,0,0, 0,61,96,109,145,212,255,255,255,255,147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,243,255,255,255,58, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,107,255,255,255,162,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,18,255,255,255,221,0,0,0,42,88,88,88,21,0,0,0,0,0,0,0,0,246,255,255,247,0,0,0,109, 255,255,255,97,0,0,0,0,0,0,0,9,254,255,255,241,0,0,0,73,255,255,255,198,1,0,0,0,0,0,0,87, 255,255,255,209,0,0,0,9,243,255,255,255,148,4,0,0,0,0,38,230,255,255,255,136,0,0,0,0,124,255,255,255, 255,218,133,98,103,156,245,255,255,255,246,31,0,0,0,0,5,177,255,255,255,255,255,255,255,255,255,255,255,255,89,0, 0,0,0,0,0,5,130,249,255,255,255,255,255,255,255,255,231,78,0,0,0,0,0,0,0,0,0,24,120,186,230,248, 243,219,180,97,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,120,128,128,128,128,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,121,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,44,249,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,206,255,255,255,255,255,255,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,241,248,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, 0,50,251,255,255,107,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,6,211,255,255,198,2,240,255,255,255, 0,0,0,0,0,0,0,0,0,0,0,0,135,255,255,250,44,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0, 0,55,252,255,255,132,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,8,216,255,255,218,8,0,0,240,255, 255,255,0,0,0,0,0,0,0,0,0,0,142,255,255,255,65,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0, 0,61,253,255,255,157,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,11,220,255,255,233,18,0,0,0,0, 240,255,255,255,0,0,0,0,0,0,0,0,149,255,255,255,89,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 0,67,254,255,255,183,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,13,225,255,255,244,32,0,0,0,0, 0,0,240,255,255,255,0,0,0,0,0,0,156,255,255,255,183,92,92,92,92,92,92,92,245,255,255,255,92,92,92,0, 0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,32,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,32,0,0,0,0,0,0,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,32,0,0,0,0,0,0,255,255,255,245,92,92,92,92,92,92,92, 92,92,92,92,11,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,19,123,205,240,248,227,178,92,3,0,0,0,0,0,0,0,0,255,255,255,240,75,236,255, 255,255,255,255,255,255,208,36,0,0,0,0,0,0,0,255,255,255,251,249,255,255,255,255,255,255,255,255,255,231,32,0, 0,0,0,0,0,255,255,255,255,255,255,190,122,98,123,198,255,255,255,255,188,0,0,0,0,0,0,255,255,255,255,251, 86,0,0,0,0,0,103,255,255,255,255,52,0,0,0,0,0,255,255,255,255,126,0,0,0,0,0,0,0,161,255,255, 255,132,0,0,0,0,0,104,104,104,104,13,0,0,0,0,0,0,0,61,255,255,255,194,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,15,255,255,255,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, 255,255,255,232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,255,255,255,207,0,0,0,0,93,204, 204,204,147,0,0,0,0,0,0,0,0,81,255,255,255,177,0,0,0,0,62,255,255,255,251,36,0,0,0,0,0,0, 0,189,255,255,255,102,0,0,0,0,1,217,255,255,255,220,37,0,0,0,0,1,130,255,255,255,248,18,0,0,0,0, 0,74,255,255,255,255,251,169,119,98,126,208,255,255,255,255,115,0,0,0,0,0,0,0,123,255,255,255,255,255,255,255, 255,255,255,255,255,175,1,0,0,0,0,0,0,0,0,90,236,255,255,255,255,255,255,255,255,252,127,1,0,0,0,0, 0,0,0,0,0,0,12,101,175,224,245,251,236,198,132,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28, 63,85,75,42,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,203,255,255,255,255,255,231,121,6,0,0,0, 0,0,0,0,0,0,0,4,170,255,255,255,255,255,255,255,255,255,201,17,0,0,0,0,0,0,0,0,0,157,255,255, 255,255,255,255,255,255,255,255,255,190,1,0,0,0,0,0,0,0,64,255,255,255,255,163,49,9,26,96,232,255,255,255, 95,0,0,0,0,0,0,0,195,255,255,255,139,0,0,0,0,0,54,252,255,255,202,0,0,0,0,0,0,28,254,255, 255,231,6,0,0,0,0,0,0,184,255,255,254,15,0,0,0,0,0,105,255,255,255,139,0,0,0,0,0,0,0,8, 16,16,16,2,0,0,0,0,0,154,255,255,255,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,195, 255,255,255,37,0,0,5,51,80,71,38,0,0,0,0,0,0,0,0,0,0,225,255,255,255,8,10,137,235,255,255,255, 255,211,94,0,0,0,0,0,0,0,0,238,255,255,251,28,215,255,255,255,255,255,255,255,255,157,3,0,0,0,0,0, 0,250,255,255,245,197,255,255,255,255,255,255,255,255,255,255,120,0,0,0,0,0,0,253,255,255,255,255,255,157,48,7, 17,81,216,255,255,255,248,28,0,0,0,0,0,248,255,255,255,255,106,0,0,0,0,0,18,225,255,255,255,116,0,0, 0,0,0,235,255,255,255,200,0,0,0,0,0,0,0,96,255,255,255,193,0,0,0,0,0,215,255,255,255,123,0,0, 0,0,0,0,0,21,255,255,255,223,0,0,0,0,0,185,255,255,255,96,0,0,0,0,0,0,0,0,247,255,255,247, 0,0,0,0,0,146,255,255,255,107,0,0,0,0,0,0,0,0,250,255,255,236,0,0,0,0,0,91,255,255,255,155, 0,0,0,0,0,0,0,35,255,255,255,209,0,0,0,0,0,22,252,255,255,240,19,0,0,0,0,0,0,130,255,255, 255,151,0,0,0,0,0,0,177,255,255,255,187,13,0,0,0,0,65,248,255,255,255,61,0,0,0,0,0,0,46,250, 255,255,255,232,141,100,105,167,252,255,255,255,183,0,0,0,0,0,0,0,0,107,255,255,255,255,255,255,255,255,255,255, 255,229,25,0,0,0,0,0,0,0,0,0,97,242,255,255,255,255,255,255,255,255,198,25,0,0,0,0,0,0,0,0, 0,0,0,23,119,189,232,249,249,224,172,84,2,0,0,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92, 92,92,92,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,246,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,7,233,255,255,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,255,255, 255,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,211,255,255,189,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,70,255,255,255,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,184,255,255,222,2,0,0,0,0,0,0, 0,0,0,0,0,0,0,42,254,255,255,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,255,255,244,15,0, 0,0,0,0,0,0,0,0,0,0,0,0,21,247,255,255,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127, 255,255,254,40,0,0,0,0,0,0,0,0,0,0,0,0,0,7,232,255,255,184,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,98,255,255,255,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,211,255,255,218,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,70,255,255,255,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,184,255,255,242, 13,0,0,0,0,0,0,0,0,0,0,0,0,0,41,254,255,255,145,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,155,255,255,254,36,0,0,0,0,0,0,0,0,0,0,0,0,0,21,247,255,255,180,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,126,255,255,255,70,0,0,0,0,0,0,0,0,0,0,0,0,0,7,232,255,255,214,1,0,0, 0,0,0,0,0,0,0,0,0,0,0,98,255,255,255,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,255, 255,239,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,76,158,209,232,249,233,210,161,79,7,0,0,0, 0,0,0,0,0,0,0,58,214,255,255,255,255,255,255,255,255,255,218,64,0,0,0,0,0,0,0,0,69,251,255,255, 255,255,255,255,255,255,255,255,255,253,79,0,0,0,0,0,0,21,240,255,255,255,255,182,127,100,126,176,254,255,255,255, 244,29,0,0,0,0,0,125,255,255,255,234,57,0,0,0,0,0,49,228,255,255,255,138,0,0,0,0,0,198,255,255, 255,94,0,0,0,0,0,0,0,78,255,255,255,212,0,0,0,0,0,229,255,255,255,19,0,0,0,0,0,0,0,5, 253,255,255,244,0,0,0,0,0,215,255,255,255,7,0,0,0,0,0,0,0,0,248,255,255,229,0,0,0,0,0,172, 255,255,255,61,0,0,0,0,0,0,0,50,255,255,255,187,0,0,0,0,0,63,255,255,255,203,12,0,0,0,0,0, 10,197,255,255,255,80,0,0,0,0,0,0,148,255,255,255,217,91,23,4,23,89,215,255,255,255,174,1,0,0,0,0, 0,0,1,130,249,255,255,255,255,255,255,255,255,255,255,169,8,0,0,0,0,0,0,0,0,0,118,255,255,255,255,255, 255,255,255,255,195,19,0,0,0,0,0,0,0,0,8,181,255,255,255,255,255,255,255,255,255,255,255,242,102,0,0,0, 0,0,0,1,177,255,255,255,255,206,132,100,100,133,208,255,255,255,255,111,0,0,0,0,0,86,255,255,255,247,89,0, 0,0,0,0,0,98,251,255,255,252,45,0,0,0,0,183,255,255,255,111,0,0,0,0,0,0,0,0,127,255,255,255, 154,0,0,0,0,235,255,255,255,15,0,0,0,0,0,0,0,0,32,255,255,255,214,0,0,0,0,252,255,255,245,0, 0,0,0,0,0,0,0,0,6,255,255,255,234,0,0,0,0,237,255,255,255,20,0,0,0,0,0,0,0,0,38,255, 255,255,220,0,0,0,0,187,255,255,255,125,0,0,0,0,0,0,0,0,143,255,255,255,169,0,0,0,0,94,255,255, 255,252,99,0,0,0,0,0,0,113,254,255,255,255,75,0,0,0,0,3,197,255,255,255,255,208,132,99,100,133,213,255, 255,255,255,179,0,0,0,0,0,0,19,214,255,255,255,255,255,255,255,255,255,255,255,255,201,11,0,0,0,0,0,0, 0,14,148,253,255,255,255,255,255,255,255,255,249,134,9,0,0,0,0,0,0,0,0,0,0,34,117,190,222,245,243,219, 184,109,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,50,75,85,64,33,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,35,158,242,255,255,255,255,255,215,114,3,0,0,0,0,0,0,0,0,0,0,86,245,255,255,255,255, 255,255,255,255,255,202,16,0,0,0,0,0,0,0,0,70,253,255,255,255,255,255,255,255,255,255,255,255,189,2,0,0, 0,0,0,0,9,231,255,255,255,234,104,27,5,29,109,237,255,255,255,86,0,0,0,0,0,0,103,255,255,255,237,35, 0,0,0,0,0,40,241,255,255,206,0,0,0,0,0,0,178,255,255,255,109,0,0,0,0,0,0,0,122,255,255,255, 30,0,0,0,0,0,227,255,255,255,24,0,0,0,0,0,0,0,42,255,255,255,101,0,0,0,0,0,247,255,255,246, 0,0,0,0,0,0,0,0,8,255,255,255,145,0,0,0,0,0,249,255,255,250,0,0,0,0,0,0,0,0,10,255, 255,255,182,0,0,0,0,0,226,255,255,255,43,0,0,0,0,0,0,0,51,255,255,255,210,0,0,0,0,0,176,255, 255,255,148,0,0,0,0,0,0,0,151,255,255,255,221,0,0,0,0,0,93,255,255,255,253,94,0,0,0,0,0,89, 253,255,255,255,232,0,0,0,0,0,5,210,255,255,255,255,196,122,97,112,184,255,255,255,255,255,237,0,0,0,0,0, 0,37,241,255,255,255,255,255,255,255,255,255,255,229,255,255,232,0,0,0,0,0,0,0,40,206,255,255,255,255,255,255, 255,250,120,153,255,255,222,0,0,0,0,0,0,0,0,5,81,170,218,245,242,212,142,39,0,163,255,255,204,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,190,255,255,180,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,228,255,255,145,0,0,0,0,0,37,164,164,164,96,0,0,0,0,0,0,0,35,255,255,255,97,0, 0,0,0,0,23,255,255,255,225,4,0,0,0,0,0,0,137,255,255,255,36,0,0,0,0,0,0,200,255,255,255,158, 4,0,0,0,0,70,250,255,255,206,0,0,0,0,0,0,0,80,255,255,255,255,224,136,104,108,170,253,255,255,255,85, 0,0,0,0,0,0,0,0,156,255,255,255,255,255,255,255,255,255,255,255,170,0,0,0,0,0,0,0,0,0,4,139, 254,255,255,255,255,255,255,255,255,161,6,0,0,0,0,0,0,0,0,0,0,0,40,141,203,237,251,243,213,156,59,0, 0,0,0,0,0,0,0,33,190,243,191,33,0,0,0,192,255,255,255,191,0,0,0,244,255,255,255,243,0,0,0,191, 255,255,255,190,0,0,0,33,191,244,192,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,190,243,191,33,0,0,0,191, 255,255,255,190,0,0,0,244,255,255,255,243,0,0,0,192,255,255,255,191,0,0,0,33,191,244,192,33,0,0,0,33, 190,243,190,31,0,0,0,0,0,192,255,255,255,190,0,0,0,0,0,244,255,255,255,243,0,0,0,0,0,191,255,255, 255,189,0,0,0,0,0,33,191,244,191,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,246,255,255,255,45,0,0,0,0,67, 255,255,255,181,0,0,0,0,0,136,255,255,255,62,0,0,0,0,0,205,255,255,200,0,0,0,0,0,20,254,255,255, 81,0,0,0,0,0,87,255,255,216,2,0,0,0,0,0,156,255,255,100,0,0,0,0,0,0,224,255,230,7,0,0, 0,0,0,38,255,255,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,253,255,255,255,134,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,106,254,255,255,255,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116, 255,255,255,254,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,255,255,255,253,99,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,137,255,255,255,251,88,0,0,0,0,0,0,0,0,0,0,0,0,0,1,148,255,255,255,248, 79,0,0,0,0,0,0,0,0,0,0,0,0,0,2,158,255,255,255,245,70,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,105,255,255,255,255,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,158,255,255,255,245,70,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,149,255,255,255,248,79,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,138,255,255,255,251,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,255,255,255,253, 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,255,255,255,254,110,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,106,254,255,255,255,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,97,253,255, 255,255,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,255,255,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0, 0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,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,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0, 0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,4,166,255,255,255,245,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,156,255, 255,255,248,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,145,255,255,255,250,84,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,135,255,255,255,252,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,255,255, 255,254,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,255,255,255,255,113,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,102,253,255,255,255,124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,255,255,255, 255,69,0,0,0,0,0,0,0,0,0,0,0,0,0,101,253,255,255,255,125,0,0,0,0,0,0,0,0,0,0,0, 0,0,112,255,255,255,255,114,0,0,0,0,0,0,0,0,0,0,0,0,0,123,255,255,255,254,104,0,0,0,0,0, 0,0,0,0,0,0,0,0,134,255,255,255,252,94,0,0,0,0,0,0,0,0,0,0,0,0,1,145,255,255,255,250, 85,0,0,0,0,0,0,0,0,0,0,0,0,2,155,255,255,255,248,76,0,0,0,0,0,0,0,0,0,0,0,0, 4,165,255,255,255,245,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,121,199,229,250,235,198, 129,25,0,0,0,0,0,0,0,0,102,245,255,255,255,255,255,255,255,247,106,0,0,0,0,0,0,109,255,255,255,255, 255,255,255,255,255,255,255,115,0,0,0,0,37,250,255,255,255,225,133,102,134,226,255,255,255,251,38,0,0,0,141,255, 255,255,197,12,0,0,0,14,202,255,255,255,147,0,0,0,211,255,255,255,55,0,0,0,0,0,60,255,255,255,214,0, 0,0,233,248,248,244,6,0,0,0,0,0,8,253,255,255,243,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249, 255,255,246,0,0,0,0,0,0,0,0,0,0,0,0,0,39,255,255,255,222,0,0,0,0,0,0,0,0,0,0,0, 0,1,163,255,255,255,160,0,0,0,0,0,0,0,0,0,0,15,77,188,255,255,255,255,58,0,0,0,0,0,0,13, 216,220,235,255,255,255,255,255,255,140,0,0,0,0,0,0,0,16,255,255,255,255,255,255,255,247,118,0,0,0,0,0, 0,0,0,16,255,255,255,248,231,187,119,24,0,0,0,0,0,0,0,0,0,16,255,255,255,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,16,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,144,144,144,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,190,243,190,31,0,0,0,0,0,0,0,0,0,0,0,0, 0,191,255,255,255,189,0,0,0,0,0,0,0,0,0,0,0,0,0,244,255,255,255,243,0,0,0,0,0,0,0,0, 0,0,0,0,0,192,255,255,255,190,0,0,0,0,0,0,0,0,0,0,0,0,0,34,191,244,191,32,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,82,141,189,224,239,252,244,227,190,140,69,3,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,162,244,255,255,255,255,255,255,255,255,255,255, 255,225,108,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,163,253,255,255,255,255,255,240,225,222,239, 253,255,255,255,255,255,206,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,233,255,255,255,228,143,69,19,0, 0,0,0,8,58,122,222,255,255,255,235,41,0,0,0,0,0,0,0,0,0,0,0,0,106,253,255,255,226,99,4,0, 0,0,0,0,0,0,0,0,0,2,107,245,255,255,223,13,0,0,0,0,0,0,0,0,0,0,109,255,255,255,161,14, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,245,255,255,147,0,0,0,0,0,0,0,0,0,75,252,255,255, 122,0,0,0,6,99,187,236,249,227,158,32,16,168,168,168,27,0,91,255,255,248,24,0,0,0,0,0,0,0,20,234, 255,255,129,0,0,0,39,215,255,255,255,255,255,255,238,94,255,255,253,9,0,1,205,255,255,109,0,0,0,0,0,0, 0,163,255,255,180,1,0,0,51,235,255,255,255,255,255,255,255,255,247,255,255,221,0,0,0,115,255,255,178,0,0,0, 0,0,0,41,254,255,242,28,0,0,14,230,255,255,255,243,147,104,130,221,255,255,255,255,179,0,0,0,49,255,255,223, 0,0,0,0,0,0,160,255,255,134,0,0,0,149,255,255,255,213,26,0,0,0,13,203,255,255,255,137,0,0,0,22, 255,255,244,0,0,0,0,0,9,244,255,250,23,0,0,29,252,255,255,244,29,0,0,0,0,0,68,255,255,255,95,0, 0,0,5,255,255,249,0,0,0,0,0,80,255,255,186,0,0,0,120,255,255,255,134,0,0,0,0,0,0,16,255,255, 255,52,0,0,0,16,255,255,229,0,0,0,0,0,142,255,255,111,0,0,0,187,255,255,255,49,0,0,0,0,0,0, 18,255,255,254,11,0,0,0,40,255,255,206,0,0,0,0,0,189,255,255,63,0,0,0,230,255,255,254,5,0,0,0, 0,0,0,60,255,255,224,0,0,0,0,88,255,255,157,0,0,0,0,0,225,255,255,25,0,0,0,250,255,255,248,0, 0,0,0,0,0,0,152,255,255,182,0,0,0,0,150,255,255,88,0,0,0,0,0,240,255,255,10,0,0,0,247,255, 255,255,29,0,0,0,0,0,49,251,255,255,145,0,0,0,3,231,255,247,19,0,0,0,0,0,252,255,255,6,0,0, 0,219,255,255,255,148,0,0,0,0,50,231,255,255,255,125,0,0,0,104,255,255,144,0,0,0,0,0,0,242,255,255, 26,0,0,0,152,255,255,255,255,183,113,110,168,252,255,247,255,255,160,0,0,40,237,255,243,26,0,0,0,0,0,0, 215,255,255,62,0,0,0,41,249,255,255,255,255,255,255,255,255,228,58,255,255,254,175,166,244,255,253,76,0,0,0,0, 0,0,0,166,255,255,133,0,0,0,0,95,252,255,255,255,255,255,255,196,33,0,162,255,255,255,255,255,251,101,0,0, 0,0,0,0,0,0,91,255,255,229,10,0,0,0,0,51,167,230,250,228,181,84,3,0,0,9,132,221,249,230,163,46, 0,0,0,0,0,0,0,0,0,11,234,255,255,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,109,255,255,255,109,0,0,0,0,0,0,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,186,255,255,255,171,30,0,0,0,0,0,0,0,0,0, 0,11,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,198,255,255,255,253,181,109,57,26,11,3, 15,44,88,157,240,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,152,254,255,255,255,255,255, 255,255,255,255,255,255,255,255,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,178,253, 255,255,255,255,255,255,255,255,255,255,215,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,24,93,148,185,204,213,205,188,153,107,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,142,255,255,255,255,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,229,255,255, 255,255,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,255,255,255,255,255,243,9,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,157,255,255,255,255,255,255,87,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,7,239,255,255,252,255,255,255,178,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,255,255,255,159, 228,255,255,250,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,172,255,255,255,68,143,255,255,255,102,0,0,0, 0,0,0,0,0,0,0,0,0,0,14,247,255,255,233,3,55,255,255,255,193,0,0,0,0,0,0,0,0,0,0,0, 0,0,96,255,255,255,147,0,0,222,255,255,254,28,0,0,0,0,0,0,0,0,0,0,0,0,187,255,255,255,59,0, 0,135,255,255,255,117,0,0,0,0,0,0,0,0,0,0,0,23,252,255,255,225,1,0,0,47,255,255,255,208,0,0, 0,0,0,0,0,0,0,0,0,111,255,255,255,138,0,0,0,0,215,255,255,255,42,0,0,0,0,0,0,0,0,0, 0,202,255,255,255,49,0,0,0,0,128,255,255,255,132,0,0,0,0,0,0,0,0,0,36,255,255,255,217,0,0,0, 0,0,40,255,255,255,222,0,0,0,0,0,0,0,0,0,126,255,255,255,128,0,0,0,0,0,0,208,255,255,255,57, 0,0,0,0,0,0,0,0,216,255,255,255,40,0,0,0,0,0,0,120,255,255,255,147,0,0,0,0,0,0,0,51, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,234,4,0,0,0,0,0,0,141,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,72,0,0,0,0,0,2,229,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 162,0,0,0,0,0,66,255,255,255,221,92,92,92,92,92,92,92,92,92,106,255,255,255,243,9,0,0,0,0,156,255, 255,255,124,0,0,0,0,0,0,0,0,0,0,201,255,255,255,87,0,0,0,7,239,255,255,254,32,0,0,0,0,0, 0,0,0,0,0,107,255,255,255,177,0,0,0,81,255,255,255,192,0,0,0,0,0,0,0,0,0,0,0,19,249,255, 255,250,18,0,0,171,255,255,255,98,0,0,0,0,0,0,0,0,0,0,0,0,175,255,255,255,102,0,0,0,0,255, 255,255,255,255,255,255,255,255,247,224,191,113,25,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255, 255,244,114,1,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,135,0,0,0,0,0,255,255, 255,245,92,92,92,92,92,113,153,244,255,255,255,255,59,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,22,205, 255,255,255,168,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,57,255,255,255,215,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,7,255,255,255,232,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,27,255, 255,255,201,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,134,255,255,255,123,0,0,0,0,255,255,255,240, 0,0,0,0,0,19,58,165,255,255,255,217,12,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,207, 24,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,135,3,0,0,0,0,0,0,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,160,7,0,0,0,0,0,255,255,255,245,92,92,92,92,92,105,157,245,255,255,255,162, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,35,224,255,255,255,68,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,72,255,255,255,173,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,3,251,255,255,232, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,249,255,255,249,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,54,255,255,255,223,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,22,202,255,255,255,168,0, 0,0,0,255,255,255,245,92,92,92,92,92,109,149,242,255,255,255,255,57,0,0,0,0,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,131,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,245,114,1,0,0,0, 0,0,255,255,255,255,255,255,255,255,255,250,227,195,118,28,0,0,0,0,0,0,0,0,0,0,0,0,40,125,186,228, 247,248,231,194,141,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,181,255,255,255,255,255,255,255,255,255,255, 209,75,0,0,0,0,0,0,0,0,0,0,0,119,250,255,255,255,255,255,255,255,255,255,255,255,255,255,157,9,0,0, 0,0,0,0,0,0,151,255,255,255,255,251,187,128,100,106,135,197,255,255,255,255,255,185,7,0,0,0,0,0,0,122, 255,255,255,255,176,31,0,0,0,0,0,0,44,205,255,255,255,255,152,0,0,0,0,0,50,251,255,255,255,124,1,0, 0,0,0,0,0,0,0,8,183,255,255,255,255,65,0,0,0,0,189,255,255,255,161,0,0,0,0,0,0,0,0,0, 0,0,23,236,255,255,255,192,0,0,0,47,255,255,255,232,13,0,0,0,0,0,0,0,0,0,0,0,0,7,12,12, 12,11,0,0,0,132,255,255,255,131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191, 255,255,255,56,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,10,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,255,255,245,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,249,255,255,246,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,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,191,255,255,255,57,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,133, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,255,255,255,234,15,0,0,0,0,0, 0,0,0,0,0,0,0,5,8,8,8,8,0,0,0,0,189,255,255,255,166,0,0,0,0,0,0,0,0,0,0,0, 22,234,255,255,255,194,0,0,0,0,50,251,255,255,255,128,1,0,0,0,0,0,0,0,0,7,181,255,255,255,255,67, 0,0,0,0,0,121,255,255,255,255,177,31,0,0,0,0,0,0,41,202,255,255,255,255,155,0,0,0,0,0,0,0, 150,255,255,255,255,251,186,127,99,105,133,194,255,255,255,255,255,188,8,0,0,0,0,0,0,0,0,120,250,255,255,255, 255,255,255,255,255,255,255,255,255,255,162,10,0,0,0,0,0,0,0,0,0,0,47,185,255,255,255,255,255,255,255,255, 255,255,212,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,128,189,230,248,248,232,196,142,61,1,0,0,0, 0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,244,226,187,132,55,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,255,255,255,255,255,255,255,255,255,255,207,78,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,169,10,0,0,0,0,0,0,0,255,255,255,245,92,92,92,93,108,131,189,248,255,255,255,255,201, 11,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,23,146,254,255,255,255,173,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,86,252,255,255,255,86,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,122,255,255,255,215,1,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,4,225,255, 255,255,65,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,122,255,255,255,143,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,58,255,255,255,195,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,13,255,255,255,231,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,250, 255,255,247,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,247,255,255,250,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,10,255,255,255,235,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,48,255,255,255,202,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,111,255, 255,255,153,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,1,214,255,255,255,78,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,104,255,255,255,228,6,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,70,248,255,255,255,106,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,19,134,252,255,255,255, 193,2,0,0,0,0,0,255,255,255,245,92,92,92,92,106,128,184,246,255,255,255,255,215,20,0,0,0,0,0,0,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,185,18,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255, 255,255,255,255,217,92,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,246,228,190,137,63,1,0,0,0, 0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255, 255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255, 245,92,92,92,92,92,92,92,92,92,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,245,92, 92,92,92,92,92,92,92,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255, 255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,245,92,92,92,92,92,92,92,92,92,0,0,0,0, 0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255, 0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,0,0,0,0,255,255,255,245,92,92,92,92,92,92,92,92,92,92,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,255,255,255,245,92,92,92,92,92,92,92,46,0,0,0,0,0,0,255,255,255, 255,255,255,255,255,255,255,255,128,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,128,0,0,0,0,0, 0,255,255,255,255,255,255,255,255,255,255,255,128,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,34,117,181,225,245,250,235,204,154,79,7,0,0,0,0,0,0,0,0,0, 0,0,0,0,38,173,253,255,255,255,255,255,255,255,255,255,234,116,6,0,0,0,0,0,0,0,0,0,0,110,248,255, 255,255,255,255,255,255,255,255,255,255,255,255,207,38,0,0,0,0,0,0,0,0,142,255,255,255,255,253,195,134,102,101, 129,175,249,255,255,255,255,233,44,0,0,0,0,0,0,116,255,255,255,255,188,39,0,0,0,0,0,0,19,152,255,255, 255,255,222,14,0,0,0,0,46,250,255,255,255,139,3,0,0,0,0,0,0,0,0,0,102,255,255,255,255,151,0,0, 0,0,185,255,255,255,176,0,0,0,0,0,0,0,0,0,0,0,0,149,220,220,220,214,20,0,0,45,255,255,255,238, 19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,255,255,255,138,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,59,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,231,255,255,255,12,0,0,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92, 92,0,0,248,255,255,246,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,248,255,255, 246,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,231,255,255,255,12,0,0,0,0, 0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,190,255,255,255,60,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,65,255,255,255,226,0,0,131,255,255,255,137,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255, 255,255,166,0,0,45,255,255,255,236,17,0,0,0,0,0,0,0,0,0,0,0,0,4,224,255,255,255,81,0,0,0, 185,255,255,255,172,0,0,0,0,0,0,0,0,0,0,0,0,117,255,255,255,220,3,0,0,0,46,249,255,255,255,136, 2,0,0,0,0,0,0,0,0,0,76,250,255,255,255,77,0,0,0,0,0,115,255,255,255,255,188,39,0,0,0,0, 0,0,13,129,252,255,255,255,138,0,0,0,0,0,0,0,142,255,255,255,255,253,194,132,101,98,118,169,240,255,255,255, 255,165,1,0,0,0,0,0,0,0,0,111,248,255,255,255,255,255,255,255,255,255,255,255,255,251,129,4,0,0,0,0, 0,0,0,0,0,0,40,176,254,255,255,255,255,255,255,255,255,255,204,54,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,36,120,183,226,246,251,236,199,142,60,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,245,92,92,92,92,92,92,92,92,92,92,92,255,255,255,240,0,0,0,0,0,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,240,0,0,0,0,0,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,240,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,198,204,204,200,1,0,0,0,0,11,255,255,255,232,0,0, 0,0,0,228,255,255,255,34,0,0,0,0,54,255,255,255,208,0,0,0,0,0,170,255,255,255,155,0,0,0,3,178, 255,255,255,149,0,0,0,0,0,71,255,255,255,255,185,112,116,197,255,255,255,254,49,0,0,0,0,0,0,167,255,255, 255,255,255,255,255,255,255,255,143,0,0,0,0,0,0,0,8,161,255,255,255,255,255,255,255,255,143,3,0,0,0,0, 0,0,0,0,0,62,162,222,247,246,219,156,50,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,77,253,255,255,255,142,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,46,243,255,255,255,177,2,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,23,225,255,255,255,205,11,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,8,199,255,255,255,228,26,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,165,255,255,255,243,48,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,125,255,255,255,252,76,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,85,254,255,255,255,109,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,52,246,255,255,255,147,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,27,229,255,255,255,181,3,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,10,205,255,255,255,209,13,0,0,0,0,0,0,0,0,0,0,0,255,255,255,241,173,255,255,255,230,29,0,0,0, 0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,245,51,0,0,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,255,255,255,255,243,48,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,244,208,255,255,255,229,29,0, 0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,28,230,255,255,255,210,14,0,0,0,0,0,0,0,0,0,0, 0,255,255,255,240,0,51,245,255,255,255,187,5,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,80,253,255, 255,255,158,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,116,255,255,255,255,126,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,153,255,255,255,255,94,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,4,186,255,255,255,250,66,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,15,213,255,255,255,240,43,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,32,233,255,255,255,225,25,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,56,247,255,255,255,205,11,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,86,254,255, 255,255,180,3,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,245,92,92,92,92,92,92,92,92,0, 0,0,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0, 255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,119,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,125,255,255,255,240,0,0,0,0,0,255,255,255,255,227,4,0,0,0,0,0,0,0,0,0,0,0,0,0, 5,230,255,255,255,240,0,0,0,0,0,255,255,255,255,255,88,0,0,0,0,0,0,0,0,0,0,0,0,0,93,255, 255,255,255,240,0,0,0,0,0,255,255,255,255,255,200,0,0,0,0,0,0,0,0,0,0,0,0,0,204,255,255,255, 255,240,0,0,0,0,0,255,255,255,255,255,255,57,0,0,0,0,0,0,0,0,0,0,0,61,255,255,255,255,255,240, 0,0,0,0,0,255,255,255,255,255,255,169,0,0,0,0,0,0,0,0,0,0,0,173,255,255,255,255,255,240,0,0, 0,0,0,255,255,255,255,255,255,251,29,0,0,0,0,0,0,0,0,0,31,252,255,255,255,255,255,240,0,0,0,0, 0,255,255,255,250,251,255,255,137,0,0,0,0,0,0,0,0,0,141,255,255,245,255,255,255,240,0,0,0,0,0,255, 255,255,240,167,255,255,238,11,0,0,0,0,0,0,0,12,240,255,255,151,255,255,255,240,0,0,0,0,0,255,255,255, 240,54,255,255,255,106,0,0,0,0,0,0,0,109,255,255,254,40,255,255,255,240,0,0,0,0,0,255,255,255,240,0, 198,255,255,216,1,0,0,0,0,0,1,218,255,255,183,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,86,255, 255,255,74,0,0,0,0,0,77,255,255,255,72,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,3,225,255,255, 187,0,0,0,0,0,189,255,255,215,1,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,117,255,255,255,43, 0,0,0,45,255,255,255,104,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,16,244,255,255,155,0,0, 0,157,255,255,238,10,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,148,255,255,247,20,0,21,247, 255,255,137,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,37,254,255,255,124,0,125,255,255,251, 29,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,179,255,255,230,11,230,255,255,169,0,0, 0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,67,255,255,255,181,255,255,255,58,0,0,0,0, 255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,209,255,255,255,255,255,202,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,98,255,255,255,255,255,90,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,7,233,255,255,255,229,5,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,129,255,255,255,123,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,23,248,255,247,20,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,255,64,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,255,216,6,0,0, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,255,255,126,0,0,0,0,0,0,0,0,0, 240,255,255,255,0,0,0,0,0,0,255,255,255,255,255,248,37,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0, 0,0,0,255,255,255,255,255,255,188,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,255, 255,255,255,92,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,242,206,255,255,234,18,0,0, 0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,53,253,255,255,155,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,0,255,255,255,240,0,148,255,255,254,60,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 255,255,255,240,0,14,230,255,255,213,5,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,85, 255,255,255,122,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,182,255,255,246,34,0,0, 0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,33,246,255,255,184,0,0,0,240,255,255,255,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,120,255,255,255,88,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,5,211,255,255,231,16,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,58, 254,255,255,151,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,154,255,255,254,56,240,255, 255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,17,233,255,255,209,243,255,255,255,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,92,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,188,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,37, 248,255,255,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,126,255,255,255,255,255,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,6,216,255,255,255,255,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,43,128,188,230, 247,248,230,189,130,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,185,255,255,255,255,255,255,255,255,255, 255,187,51,0,0,0,0,0,0,0,0,0,0,0,0,124,251,255,255,255,255,255,255,255,255,255,255,255,255,252,127,1, 0,0,0,0,0,0,0,0,0,155,255,255,255,255,250,184,126,100,100,126,184,250,255,255,255,255,159,1,0,0,0,0, 0,0,0,126,255,255,255,255,170,27,0,0,0,0,0,0,29,172,255,255,255,255,128,0,0,0,0,0,0,53,252,255, 255,255,117,0,0,0,0,0,0,0,0,0,0,118,255,255,255,252,53,0,0,0,0,0,191,255,255,255,157,0,0,0, 0,0,0,0,0,0,0,0,0,158,255,255,255,190,0,0,0,0,49,255,255,255,230,11,0,0,0,0,0,0,0,0, 0,0,0,0,12,231,255,255,255,48,0,0,0,133,255,255,255,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 131,255,255,255,133,0,0,0,192,255,255,255,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,255,255,255,191, 0,0,0,232,255,255,255,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,255,255,255,231,0,0,0,249,255, 255,245,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,255,255,248,0,0,0,248,255,255,246,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,255,247,0,0,0,231,255,255,255,12,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,13,255,255,255,230,0,0,0,191,255,255,255,58,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,59,255,255,255,189,0,0,0,132,255,255,255,133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,255,255, 255,131,0,0,0,48,255,255,255,233,13,0,0,0,0,0,0,0,0,0,0,0,0,14,233,255,255,255,47,0,0,0, 0,191,255,255,255,161,0,0,0,0,0,0,0,0,0,0,0,0,162,255,255,255,189,0,0,0,0,0,53,252,255,255, 255,119,0,0,0,0,0,0,0,0,0,0,121,255,255,255,252,53,0,0,0,0,0,0,127,255,255,255,255,170,27,0, 0,0,0,0,0,28,173,255,255,255,255,129,0,0,0,0,0,0,0,0,155,255,255,255,255,249,183,125,98,99,125,183, 250,255,255,255,255,160,1,0,0,0,0,0,0,0,0,0,125,251,255,255,255,255,255,255,255,255,255,255,255,255,252,128, 1,0,0,0,0,0,0,0,0,0,0,0,49,187,255,255,255,255,255,255,255,255,255,255,189,51,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,44,129,190,231,249,249,231,190,130,46,0,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,255,255,255,255,255,250,228,204,144,57,1,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255, 190,33,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,240,40,0,0,0,0,255,255,255,245,92, 92,92,92,94,113,166,247,255,255,255,218,7,0,0,0,255,255,255,240,0,0,0,0,0,0,0,35,225,255,255,255,93, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,85,255,255,255,181,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,17,255,255,255,217,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,7,255,255,255,229,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,51,255,255,255,199,0,0,0,255,255,255,240,0,0,0,0,0,0,0,2,180, 255,255,255,133,0,0,0,255,255,255,240,0,0,0,0,2,19,73,190,255,255,255,252,35,0,0,0,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,127,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,253,130,0,0, 0,0,0,255,255,255,255,255,255,255,255,255,255,255,246,172,51,0,0,0,0,0,0,255,255,255,245,92,92,92,92,89, 78,49,7,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,43,128,188,230,247,248,230,189,130,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,185,255, 255,255,255,255,255,255,255,255,255,187,51,0,0,0,0,0,0,0,0,0,0,0,0,124,251,255,255,255,255,255,255,255, 255,255,255,255,255,252,127,1,0,0,0,0,0,0,0,0,0,155,255,255,255,255,250,184,126,100,100,126,184,250,255,255, 255,255,160,1,0,0,0,0,0,0,0,126,255,255,255,255,170,27,0,0,0,0,0,0,29,172,255,255,255,255,130,0, 0,0,0,0,0,53,252,255,255,255,117,0,0,0,0,0,0,0,0,0,0,118,255,255,255,252,55,0,0,0,0,0, 191,255,255,255,157,0,0,0,0,0,0,0,0,0,0,0,0,158,255,255,255,192,0,0,0,0,49,255,255,255,230,11, 0,0,0,0,0,0,0,0,0,0,0,0,12,231,255,255,255,49,0,0,0,133,255,255,255,129,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,131,255,255,255,134,0,0,0,192,255,255,255,55,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,55,255,255,255,191,0,0,0,232,255,255,255,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,255, 255,255,231,0,0,0,249,255,255,245,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,255,255,248,0,0, 0,248,255,255,246,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,255,248,0,0,0,231,255,255,255, 12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,255,255,255,230,0,0,0,191,255,255,255,58,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,59,255,255,255,190,0,0,0,132,255,255,255,133,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,135,255,255,255,130,0,0,0,48,255,255,255,233,13,0,0,0,0,0,0,0,0,0,0,0,0,14, 233,255,255,255,47,0,0,0,0,191,255,255,255,161,0,0,0,0,0,0,0,0,0,0,0,0,162,255,255,255,191,0, 0,0,0,0,53,252,255,255,255,119,0,0,0,0,0,0,0,0,0,0,121,255,255,255,253,51,0,0,0,0,0,0, 126,255,255,255,255,170,27,0,0,0,0,0,0,28,173,255,255,255,255,126,0,0,0,0,0,0,0,0,155,255,255,255, 255,249,183,125,98,99,125,183,250,255,255,255,255,154,1,0,0,0,0,0,0,0,0,0,123,251,255,255,255,255,255,255, 255,255,255,255,255,255,251,120,0,0,0,0,0,0,0,0,0,0,0,0,47,185,255,255,255,255,255,255,255,255,255,255, 255,244,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,127,189,230,248,248,231,189,196,255,255,255,239,47,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,189,255,255,255,233,39,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,202,255,255,255,227,31,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,19,213,255,255,255,219,24,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,27,223,255,255,255,211,18,0,0,0,0,255,255,255,255,255,255,255,255,253,240,207,149,59, 0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,176,21,0,0,0,0,0,0,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,229,27,0,0,0,0,0,255,255,255,245,92,92,92,92,94,115,175,251,255,255, 255,199,2,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,54,241,255,255,255,74,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,107,255,255,255,169,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,27,255,255, 255,208,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,5,255,255,255,233,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,31,255,255,255,218,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,118,255,255,255, 170,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,65,246,255,255,255,84,0,0,0,0,255,255,255,245,92,92, 92,92,94,115,179,253,255,255,255,208,2,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,238,40,0, 0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,198,37,0,0,0,0,0,0,255,255,255,255,255,255,255, 255,255,255,255,199,66,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,168,255,255,255,234,18,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,23,238,255,255,255,156,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 100,255,255,255,254,61,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,1,192,255,255,255,214,6,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,39,248,255,255,255,125,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,127,255,255,255,248,37,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,6,214,255,255,255,189,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,60,254,255,255,255,95,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,153,255,255,255,235,19,0,0,0,0,0,8,87,163,214,236,250,236,203,145,52,0,0,0,0,0,0,0,0,0, 0,78,223,255,255,255,255,255,255,255,255,255,175,23,0,0,0,0,0,0,0,94,254,255,255,255,255,255,255,255,255,255, 255,255,220,27,0,0,0,0,0,47,252,255,255,255,237,145,106,111,150,240,255,255,255,255,186,0,0,0,0,0,162,255, 255,255,186,13,0,0,0,0,16,177,255,255,255,255,52,0,0,0,0,228,255,255,255,35,0,0,0,0,0,0,11,235, 255,255,255,126,0,0,0,0,251,255,255,247,0,0,0,0,0,0,0,0,81,112,112,112,69,0,0,0,0,241,255,255, 255,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,191,15,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,88,255,255,255,255,231,122,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,255,255, 255,255,255,255,218,147,73,8,0,0,0,0,0,0,0,0,0,0,0,108,238,255,255,255,255,255,255,255,243,161,54,0, 0,0,0,0,0,0,0,0,0,14,98,184,247,255,255,255,255,255,255,254,158,6,0,0,0,0,0,0,0,0,0,0, 0,9,72,143,217,255,255,255,255,255,181,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,182,255,255,255,255, 99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,159,255,255,255,189,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,32,255,255,255,228,0,0,0,0,198,204,204,204,37,0,0,0,0,0,0,0,9,255,255,255,226, 0,0,0,0,220,255,255,255,123,0,0,0,0,0,0,0,75,255,255,255,188,0,0,0,0,146,255,255,255,249,70,0, 0,0,0,0,44,224,255,255,255,101,0,0,0,0,29,242,255,255,255,255,185,126,100,123,174,253,255,255,255,219,7,0, 0,0,0,0,63,245,255,255,255,255,255,255,255,255,255,255,255,238,39,0,0,0,0,0,0,0,50,199,255,255,255,255, 255,255,255,255,255,189,31,0,0,0,0,0,0,0,0,0,1,68,146,207,232,250,233,207,149,61,1,0,0,0,0,0, 0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,92,92,92,92,92,92, 245,255,255,255,92,92,92,92,92,92,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255, 255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255, 255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0, 255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,250,255,255,245,0,0,0,0,0,0,0,0,0,0,6,255,255,255,233,0,0,0,0,231,255,255,255,14, 0,0,0,0,0,0,0,0,0,31,255,255,255,214,0,0,0,0,187,255,255,255,78,0,0,0,0,0,0,0,0,0, 96,255,255,255,168,0,0,0,0,113,255,255,255,199,4,0,0,0,0,0,0,0,9,214,255,255,255,91,0,0,0,0, 17,240,255,255,255,171,15,0,0,0,0,0,21,185,255,255,255,225,7,0,0,0,0,0,99,255,255,255,255,240,158,110, 96,112,163,244,255,255,255,254,70,0,0,0,0,0,0,0,131,255,255,255,255,255,255,255,255,255,255,255,255,252,101,0, 0,0,0,0,0,0,0,0,85,228,255,255,255,255,255,255,255,255,255,213,63,0,0,0,0,0,0,0,0,0,0,0, 6,75,158,208,233,249,229,203,145,61,2,0,0,0,0,0,45,255,255,255,240,7,0,0,0,0,0,0,0,0,0,0, 0,0,86,255,255,255,188,0,0,206,255,255,255,82,0,0,0,0,0,0,0,0,0,0,0,0,176,255,255,255,93,0, 0,111,255,255,255,172,0,0,0,0,0,0,0,0,0,0,0,17,249,255,255,243,11,0,0,21,250,255,255,247,14,0, 0,0,0,0,0,0,0,0,0,101,255,255,255,160,0,0,0,0,177,255,255,255,96,0,0,0,0,0,0,0,0,0, 0,191,255,255,255,65,0,0,0,0,82,255,255,255,185,0,0,0,0,0,0,0,0,0,27,253,255,255,225,1,0,0, 0,0,6,236,255,255,252,23,0,0,0,0,0,0,0,0,116,255,255,255,132,0,0,0,0,0,0,148,255,255,255,109, 0,0,0,0,0,0,0,0,206,255,255,255,38,0,0,0,0,0,0,53,255,255,255,199,0,0,0,0,0,0,0,40, 255,255,255,198,0,0,0,0,0,0,0,0,213,255,255,255,33,0,0,0,0,0,0,131,255,255,255,104,0,0,0,0, 0,0,0,0,119,255,255,255,122,0,0,0,0,0,0,220,255,255,248,17,0,0,0,0,0,0,0,0,27,253,255,255, 212,0,0,0,0,0,56,255,255,255,170,0,0,0,0,0,0,0,0,0,0,185,255,255,255,46,0,0,0,0,146,255, 255,255,76,0,0,0,0,0,0,0,0,0,0,90,255,255,255,136,0,0,0,3,233,255,255,233,4,0,0,0,0,0, 0,0,0,0,0,9,241,255,255,224,1,0,0,71,255,255,255,142,0,0,0,0,0,0,0,0,0,0,0,0,156,255, 255,255,59,0,0,162,255,255,255,48,0,0,0,0,0,0,0,0,0,0,0,0,61,255,255,255,149,0,9,242,255,255, 209,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,255,255,234,4,87,255,255,255,114,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,127,255,255,255,73,177,255,255,252,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33, 254,255,255,180,249,255,255,181,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,193,255,255,255,255,255,255,86, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,255,255,255,255,255,240,8,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,13,245,255,255,255,255,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,164,255,255,255,255,59,0,0,0,0,0,0,0,0,0,57,255,255,255,227,0,0,0,0,0,0,0,0,0,47,255, 255,255,255,111,0,0,0,0,0,0,0,0,3,241,255,255,255,42,3,238,255,255,255,36,0,0,0,0,0,0,0,0, 116,255,255,255,255,176,0,0,0,0,0,0,0,0,54,255,255,255,223,0,0,170,255,255,255,100,0,0,0,0,0,0, 0,0,186,255,255,255,255,239,2,0,0,0,0,0,0,0,119,255,255,255,148,0,0,98,255,255,255,164,0,0,0,0, 0,0,0,8,246,255,255,255,255,255,50,0,0,0,0,0,0,0,184,255,255,255,74,0,0,27,255,255,255,227,0,0, 0,0,0,0,0,68,255,255,255,255,255,255,115,0,0,0,0,0,0,4,243,255,255,246,9,0,0,0,211,255,255,255, 36,0,0,0,0,0,0,138,255,255,254,255,255,255,180,0,0,0,0,0,0,58,255,255,255,180,0,0,0,0,140,255, 255,255,100,0,0,0,0,0,0,207,255,255,199,218,255,255,241,3,0,0,0,0,0,123,255,255,255,106,0,0,0,0, 68,255,255,255,164,0,0,0,0,0,21,254,255,255,124,148,255,255,255,53,0,0,0,0,0,188,255,255,255,32,0,0, 0,0,7,245,255,255,227,0,0,0,0,0,90,255,255,255,50,78,255,255,255,118,0,0,0,0,6,246,255,255,212,0, 0,0,0,0,0,181,255,255,255,36,0,0,0,0,159,255,255,231,1,13,250,255,255,183,0,0,0,0,62,255,255,255, 138,0,0,0,0,0,0,110,255,255,255,100,0,0,0,0,228,255,255,158,0,0,194,255,255,243,4,0,0,0,127,255, 255,255,63,0,0,0,0,0,0,38,255,255,255,164,0,0,0,42,255,255,255,83,0,0,124,255,255,255,57,0,0,0, 192,255,255,240,4,0,0,0,0,0,0,0,222,255,255,228,0,0,0,111,255,255,250,14,0,0,54,255,255,255,121,0, 0,8,248,255,255,170,0,0,0,0,0,0,0,0,151,255,255,255,36,0,0,181,255,255,191,0,0,0,2,237,255,255, 186,0,0,66,255,255,255,95,0,0,0,0,0,0,0,0,80,255,255,255,100,0,5,244,255,255,117,0,0,0,0,170, 255,255,245,6,0,131,255,255,254,22,0,0,0,0,0,0,0,0,13,250,255,255,164,0,63,255,255,255,42,0,0,0, 0,100,255,255,255,60,0,196,255,255,202,0,0,0,0,0,0,0,0,0,0,193,255,255,228,0,133,255,255,224,0,0, 0,0,0,29,255,255,255,125,10,250,255,255,127,0,0,0,0,0,0,0,0,0,0,121,255,255,255,36,202,255,255,150, 0,0,0,0,0,0,216,255,255,189,70,255,255,255,52,0,0,0,0,0,0,0,0,0,0,50,255,255,255,118,253,255, 255,76,0,0,0,0,0,0,146,255,255,247,143,255,255,232,1,0,0,0,0,0,0,0,0,0,0,1,232,255,255,236, 255,255,247,10,0,0,0,0,0,0,76,255,255,255,243,255,255,159,0,0,0,0,0,0,0,0,0,0,0,0,163,255, 255,255,255,255,183,0,0,0,0,0,0,0,11,249,255,255,255,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0, 91,255,255,255,255,255,109,0,0,0,0,0,0,0,0,192,255,255,255,255,251,15,0,0,0,0,0,0,0,0,0,0, 0,0,21,254,255,255,255,255,35,0,0,0,0,0,0,0,0,122,255,255,255,255,191,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,204,255,255,255,217,0,0,0,0,0,0,0,0,0,51,255,255,255,255,117,0,0,0,0,0,0,0, 0,18,235,255,255,255,89,0,0,0,0,0,0,0,0,3,210,255,255,255,120,0,0,0,96,255,255,255,231,15,0,0, 0,0,0,0,0,104,255,255,255,214,5,0,0,0,1,194,255,255,255,147,0,0,0,0,0,0,16,235,255,255,255,63, 0,0,0,0,0,43,250,255,255,253,51,0,0,0,0,0,142,255,255,255,161,0,0,0,0,0,0,0,137,255,255,255, 203,2,0,0,0,38,250,255,255,238,21,0,0,0,0,0,0,0,11,225,255,255,255,106,0,0,0,180,255,255,255,102, 0,0,0,0,0,0,0,0,0,79,255,255,255,240,23,0,71,255,255,255,199,1,0,0,0,0,0,0,0,0,0,0, 177,255,255,255,164,4,213,255,255,252,48,0,0,0,0,0,0,0,0,0,0,0,31,245,255,255,255,167,255,255,255,142, 0,0,0,0,0,0,0,0,0,0,0,0,0,119,255,255,255,255,255,255,228,13,0,0,0,0,0,0,0,0,0,0, 0,0,0,5,213,255,255,255,255,255,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,255,255,255,255,183, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,254,255,255,255,173,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,4,208,255,255,255,255,255,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,255,255,255,255, 255,255,222,9,0,0,0,0,0,0,0,0,0,0,0,0,27,243,255,255,227,209,255,255,255,132,0,0,0,0,0,0, 0,0,0,0,0,0,172,255,255,255,90,57,254,255,255,249,40,0,0,0,0,0,0,0,0,0,0,72,255,255,255,198, 1,0,154,255,255,255,190,0,0,0,0,0,0,0,0,0,8,220,255,255,254,54,0,0,18,234,255,255,255,91,0,0, 0,0,0,0,0,0,130,255,255,255,161,0,0,0,0,95,255,255,255,232,16,0,0,0,0,0,0,38,248,255,255,244, 26,0,0,0,0,0,193,255,255,255,149,0,0,0,0,0,0,187,255,255,255,123,0,0,0,0,0,0,42,250,255,255, 253,53,0,0,0,0,89,255,255,255,224,8,0,0,0,0,0,0,0,136,255,255,255,204,2,0,0,14,230,255,255,255, 85,0,0,0,0,0,0,0,0,10,224,255,255,255,109,0,151,255,255,255,179,0,0,0,0,0,0,0,0,0,0,192, 255,255,255,129,0,27,247,255,255,254,44,0,0,0,0,0,0,0,0,55,255,255,255,239,16,0,0,144,255,255,255,162, 0,0,0,0,0,0,0,0,176,255,255,255,123,0,0,0,23,245,255,255,251,30,0,0,0,0,0,0,41,254,255,255, 236,13,0,0,0,0,137,255,255,255,145,0,0,0,0,0,0,160,255,255,255,117,0,0,0,0,0,19,242,255,255,245, 19,0,0,0,0,29,250,255,255,232,10,0,0,0,0,0,0,131,255,255,255,128,0,0,0,0,143,255,255,255,111,0, 0,0,0,0,0,0,16,239,255,255,236,11,0,0,18,244,255,255,228,8,0,0,0,0,0,0,0,0,124,255,255,255, 111,0,0,127,255,255,255,105,0,0,0,0,0,0,0,0,0,13,236,255,255,225,5,10,235,255,255,224,6,0,0,0, 0,0,0,0,0,0,0,118,255,255,255,94,111,255,255,255,99,0,0,0,0,0,0,0,0,0,0,0,10,232,255,255, 213,225,255,255,220,4,0,0,0,0,0,0,0,0,0,0,0,0,111,255,255,255,255,255,255,93,0,0,0,0,0,0, 0,0,0,0,0,0,0,7,228,255,255,255,255,215,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,255,255, 255,255,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,255,255,255,243,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,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,0,0,0,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 0,0,0,92,92,92,92,92,92,92,92,92,92,97,243,255,255,255,176,0,0,0,0,0,0,0,0,0,0,0,0,0, 123,255,255,255,241,27,0,0,0,0,0,0,0,0,0,0,0,0,38,247,255,255,255,104,0,0,0,0,0,0,0,0, 0,0,0,0,1,192,255,255,255,195,1,0,0,0,0,0,0,0,0,0,0,0,0,101,255,255,255,248,40,0,0,0, 0,0,0,0,0,0,0,0,0,24,239,255,255,255,125,0,0,0,0,0,0,0,0,0,0,0,0,0,172,255,255,255, 211,5,0,0,0,0,0,0,0,0,0,0,0,0,78,255,255,255,253,55,0,0,0,0,0,0,0,0,0,0,0,0, 14,228,255,255,255,145,0,0,0,0,0,0,0,0,0,0,0,0,0,150,255,255,255,225,12,0,0,0,0,0,0,0, 0,0,0,0,0,58,254,255,255,255,73,0,0,0,0,0,0,0,0,0,0,0,0,6,213,255,255,255,166,0,0,0, 0,0,0,0,0,0,0,0,0,0,128,255,255,255,236,21,0,0,0,0,0,0,0,0,0,0,0,0,41,249,255,255, 255,94,0,0,0,0,0,0,0,0,0,0,0,0,1,196,255,255,255,185,0,0,0,0,0,0,0,0,0,0,0,0, 0,106,255,255,255,245,33,0,0,0,0,0,0,0,0,0,0,0,0,27,241,255,255,255,114,0,0,0,0,0,0,0, 0,0,0,0,0,0,177,255,255,255,240,95,92,92,92,92,92,92,92,92,92,92,0,0,0,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0, 0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0, 0,0,0,0,0,255,255,255,245,92,92,92,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,245,92,92,92,0,0,0,0, 0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0, 0,0,0,0,0,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,74,255,255,255,133,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,11,249,255,255,202,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,192,255,255,253,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,255, 255,255,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,255,255,255,152,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,239,255,255,221,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,173,255,255,255,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105, 255,255,255,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,255,255,255,171,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,223,255,255,237,2,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,154,255,255,255,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 86,255,255,255,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,253,255,255,189,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,204,255,255,248,9,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,135,255,255,255,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,67,255,255,255,139,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,246,255,255,208,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,255,255,254,22,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,117,255,255,255,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,48,255,255,255,157,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,233,255,255,226,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,166,255,255,255,39,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,98,255,255,255,107,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,29,255,255,255,176,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,255,255,241, 3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,147,255,255,255,57,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,79,255,255,255,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,14,251,255,255,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,255,255, 250,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,255,255,76,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,255,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,4,242,255,255,213,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,178,255, 255,255,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,255,255,255,94,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,255,255,255,163,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,227,255,255,231,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,159, 255,255,255,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,255,255,255,113,0,0,0,0, 0,0,0,0,128,255,255,255,255,255,255,240,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,240,0,0,0,0, 0,0,0,0,128,255,255,255,255,255,255,240,0,0,0,0,0,0,0,0,46,92,92,92,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,46,92,92,92,255,255,255,240,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,240,0,0,0,0, 0,0,0,0,128,255,255,255,255,255,255,240,0,0,0,0,0,0,0,0,128,255,255,255,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,7,233,255,255,255,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,255, 255,255,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,213,255,255,255,255,249,25,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,74,255,255,255,255,255,255,134,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188, 255,255,255,246,255,255,238,11,0,0,0,0,0,0,0,0,0,0,0,0,47,255,255,255,204,127,255,255,255,108,0,0, 0,0,0,0,0,0,0,0,0,0,162,255,255,255,91,22,248,255,255,221,2,0,0,0,0,0,0,0,0,0,0,26, 249,255,255,228,5,0,158,255,255,255,82,0,0,0,0,0,0,0,0,0,0,135,255,255,255,119,0,0,45,255,255,255, 198,0,0,0,0,0,0,0,0,0,11,238,255,255,244,16,0,0,0,189,255,255,255,57,0,0,0,0,0,0,0,0, 108,255,255,255,147,0,0,0,0,76,255,255,255,172,0,0,0,0,0,0,0,2,220,255,255,253,35,0,0,0,0,1, 218,255,255,253,34,0,0,0,0,0,0,82,255,255,255,175,0,0,0,0,0,0,107,255,255,255,146,0,0,0,0,0, 0,196,255,255,255,61,0,0,0,0,0,0,11,239,255,255,244,17,0,0,0,0,55,255,255,255,203,0,0,0,0,0, 0,0,0,138,255,255,255,120,0,0,0,0,170,255,255,255,89,0,0,0,0,0,0,0,0,29,251,255,255,229,6,0, 0,31,252,255,255,226,4,0,0,0,0,0,0,0,0,0,169,255,255,255,94,0,0,143,255,255,255,117,0,0,0,0, 0,0,0,0,0,0,56,255,255,255,209,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92, 92,92,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0, 0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,185,46,0,0,0,0,0,0, 0,255,253,158,27,0,0,0,0,0,255,255,255,245,131,13,0,0,0,139,244,255,255,255,233,104,4,0,0,20,135,242, 255,255,255,123,0,0,0,0,18,131,240,255,128,0,0,0,0,0,0,16,127,110,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,100,183,232,250,233,203,127,31,0,0,240,255,255,255,0,0, 0,0,0,0,0,55,225,255,255,255,255,255,255,255,247,119,1,240,255,255,255,0,0,0,0,0,0,87,248,255,255,255, 255,255,255,255,255,255,255,150,240,255,255,255,0,0,0,0,0,42,246,255,255,255,255,199,125,100,125,201,255,255,255,255, 255,255,255,0,0,0,0,1,207,255,255,255,238,75,0,0,0,0,0,80,240,255,255,255,255,255,0,0,0,0,75,255, 255,255,251,60,0,0,0,0,0,0,0,66,253,255,255,255,255,0,0,0,0,159,255,255,255,151,0,0,0,0,0,0, 0,0,0,158,255,255,255,255,0,0,0,0,213,255,255,255,54,0,0,0,0,0,0,0,0,0,59,255,255,255,255,0, 0,0,0,243,255,255,255,10,0,0,0,0,0,0,0,0,0,12,255,255,255,255,0,0,0,0,252,255,255,247,0,0, 0,0,0,0,0,0,0,0,0,247,255,255,255,0,0,0,0,241,255,255,255,14,0,0,0,0,0,0,0,0,0,12, 255,255,255,255,0,0,0,0,207,255,255,255,65,0,0,0,0,0,0,0,0,0,60,255,255,255,255,0,0,0,0,148, 255,255,255,165,0,0,0,0,0,0,0,0,0,158,255,255,255,255,0,0,0,0,62,255,255,255,254,73,0,0,0,0, 0,0,0,66,253,255,255,255,255,0,0,0,0,0,193,255,255,255,243,87,0,0,0,0,0,79,240,255,255,255,255,255, 0,0,0,0,0,32,242,255,255,255,255,205,126,100,125,200,255,255,255,255,255,255,255,0,0,0,0,0,0,76,245,255, 255,255,255,255,255,255,255,255,255,134,240,255,255,255,0,0,0,0,0,0,0,48,222,255,255,255,255,255,255,255,243,106, 0,240,255,255,255,0,0,0,0,0,0,0,0,7,100,184,234,251,232,199,119,24,0,0,240,255,255,255,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,23,117,198,231,250,233,186,105,10,0,0,0,0,0, 0,0,0,255,255,255,240,0,104,242,255,255,255,255,255,255,255,230,62,0,0,0,0,0,0,0,255,255,255,240,131,255, 255,255,255,255,255,255,255,255,255,251,97,0,0,0,0,0,0,255,255,255,255,255,255,255,203,126,100,125,201,255,255,255, 255,249,46,0,0,0,0,0,255,255,255,255,255,241,82,0,0,0,0,0,77,238,255,255,255,209,1,0,0,0,0,255, 255,255,255,253,67,0,0,0,0,0,0,0,60,252,255,255,255,75,0,0,0,0,255,255,255,255,158,0,0,0,0,0, 0,0,0,0,152,255,255,255,158,0,0,0,0,255,255,255,255,58,0,0,0,0,0,0,0,0,0,54,255,255,255,212, 0,0,0,0,255,255,255,255,11,0,0,0,0,0,0,0,0,0,10,255,255,255,242,0,0,0,0,255,255,255,246,0, 0,0,0,0,0,0,0,0,0,0,247,255,255,252,0,0,0,0,255,255,255,255,12,0,0,0,0,0,0,0,0,0, 15,255,255,255,239,0,0,0,0,255,255,255,255,60,0,0,0,0,0,0,0,0,0,66,255,255,255,205,0,0,0,0, 255,255,255,255,158,0,0,0,0,0,0,0,0,0,166,255,255,255,147,0,0,0,0,255,255,255,255,253,67,0,0,0, 0,0,0,0,73,254,255,255,255,61,0,0,0,0,255,255,255,255,255,241,82,0,0,0,0,0,87,244,255,255,255,193, 0,0,0,0,0,255,255,255,255,255,255,255,202,125,100,126,205,255,255,255,255,243,33,0,0,0,0,0,255,255,255,240, 153,255,255,255,255,255,255,255,255,255,255,245,79,0,0,0,0,0,0,255,255,255,240,2,123,248,255,255,255,255,255,255, 255,222,49,0,0,0,0,0,0,0,255,255,255,240,0,0,33,128,203,234,251,233,183,98,6,0,0,0,0,0,0,0, 0,0,0,3,83,167,220,246,249,233,192,128,33,0,0,0,0,0,0,0,0,0,44,213,255,255,255,255,255,255,255,255, 251,140,5,0,0,0,0,0,0,79,245,255,255,255,255,255,255,255,255,255,255,255,187,6,0,0,0,0,37,243,255,255, 255,243,157,107,103,138,227,255,255,255,255,145,0,0,0,1,202,255,255,255,202,26,0,0,0,0,5,156,255,255,255,252, 39,0,0,71,255,255,255,232,20,0,0,0,0,0,0,1,202,255,255,255,139,0,0,156,255,255,255,118,0,0,0,0, 0,0,0,0,22,48,48,48,35,0,0,212,255,255,255,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,243, 255,255,253,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,243,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,242,255,255,254,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,255,255,255,42, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,255,255,255,120,0,0,0,0,0,0,0,0,24,52,52,52, 38,0,0,68,255,255,255,233,20,0,0,0,0,0,0,1,202,255,255,255,133,0,0,0,198,255,255,255,202,26,0,0, 0,0,3,150,255,255,255,247,30,0,0,0,34,241,255,255,255,243,157,106,102,135,223,255,255,255,255,122,0,0,0,0, 0,75,243,255,255,255,255,255,255,255,255,255,255,255,160,1,0,0,0,0,0,0,41,211,255,255,255,255,255,255,255,255, 245,116,0,0,0,0,0,0,0,0,0,2,82,167,220,247,250,233,189,119,23,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,0,0,0,7,100,182,231,250,233,197,118,25,0,0,240,255,255,255,0,0,0,0,0,0,0,55, 225,255,255,255,255,255,255,255,244,119,1,240,255,255,255,0,0,0,0,0,0,87,248,255,255,255,255,255,255,255,255,255, 255,158,241,255,255,255,0,0,0,0,0,42,246,255,255,255,255,199,125,100,125,201,255,255,255,255,255,255,255,0,0,0, 0,1,207,255,255,255,238,75,0,0,0,0,0,80,240,255,255,255,255,255,0,0,0,0,75,255,255,255,251,60,0,0, 0,0,0,0,0,66,253,255,255,255,255,0,0,0,0,159,255,255,255,151,0,0,0,0,0,0,0,0,0,158,255,255, 255,255,0,0,0,0,213,255,255,255,54,0,0,0,0,0,0,0,0,0,59,255,255,255,255,0,0,0,0,243,255,255, 255,10,0,0,0,0,0,0,0,0,0,12,255,255,255,255,0,0,0,0,252,255,255,247,0,0,0,0,0,0,0,0, 0,0,0,247,255,255,255,0,0,0,0,241,255,255,255,14,0,0,0,0,0,0,0,0,0,12,255,255,255,255,0,0, 0,0,207,255,255,255,65,0,0,0,0,0,0,0,0,0,60,255,255,255,255,0,0,0,0,148,255,255,255,165,0,0, 0,0,0,0,0,0,0,158,255,255,255,255,0,0,0,0,62,255,255,255,254,73,0,0,0,0,0,0,0,66,253,255, 255,255,255,0,0,0,0,0,192,255,255,255,243,87,0,0,0,0,0,79,240,255,255,255,255,255,0,0,0,0,0,32, 241,255,255,255,255,205,126,100,125,200,255,255,255,255,255,255,255,0,0,0,0,0,0,76,244,255,255,255,255,255,255,255, 255,255,255,133,240,255,255,255,0,0,0,0,0,0,0,47,220,255,255,255,255,255,255,255,245,110,0,240,255,255,255,0, 0,0,0,0,0,0,0,6,96,181,232,251,233,202,124,28,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,26, 121,190,233,250,245,219,166,81,2,0,0,0,0,0,0,0,0,0,1,121,249,255,255,255,255,255,255,255,255,211,44,0, 0,0,0,0,0,0,8,183,255,255,255,255,255,255,255,255,255,255,255,246,83,0,0,0,0,0,0,151,255,255,255,255, 209,129,98,106,151,234,255,255,255,245,43,0,0,0,0,75,255,255,255,250,101,0,0,0,0,0,9,149,255,255,255,210, 1,0,0,0,199,255,255,255,105,0,0,0,0,0,0,0,0,165,255,255,255,74,0,0,28,255,255,255,224,2,0,0, 0,0,0,0,0,0,46,255,255,255,155,0,0,84,255,255,255,198,92,92,92,92,92,92,92,92,92,97,255,255,255,206, 0,0,115,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,230,0,0,124,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,233,0,0,113,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,216,0, 0,80,255,255,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,254,255,255,228,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,190,255,255,255,115,0,0,0,0,0,0,0,6,177,204,204,204,58,0,0, 0,64,254,255,255,253,114,2,0,0,0,0,11,169,255,255,255,219,4,0,0,0,0,135,255,255,255,255,218,136,100,106, 146,232,255,255,255,252,66,0,0,0,0,0,3,166,255,255,255,255,255,255,255,255,255,255,255,252,98,0,0,0,0,0, 0,0,0,105,244,255,255,255,255,255,255,255,255,222,68,0,0,0,0,0,0,0,0,0,0,20,114,187,232,250,246,223, 172,91,7,0,0,0,0,0,0,0,0,0,0,0,0,11,52,76,58,0,0,0,0,0,70,187,252,255,255,172,0,0, 0,0,127,255,255,255,255,255,172,0,0,0,58,255,255,255,255,255,255,172,0,0,0,161,255,255,255,222,77,19,2,0, 0,0,215,255,255,255,66,0,0,0,0,0,0,235,255,255,255,11,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,255,255,255,255,255,255,255,255,255,255,0,255,255,255,255,255,255,255,255,255,255,0,255,255,255,255,255,255,255,255,255, 255,0,92,92,245,255,255,255,92,92,92,92,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0, 0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255, 255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,7,100,183,232,250,233,202,125,30,0, 0,240,255,255,255,0,0,0,0,0,0,0,55,225,255,255,255,255,255,255,255,247,121,1,240,255,255,255,0,0,0,0, 0,0,87,248,255,255,255,255,255,255,255,255,255,255,152,240,255,255,255,0,0,0,0,0,42,246,255,255,255,255,199,125, 100,125,201,255,255,255,255,255,255,255,0,0,0,0,1,207,255,255,255,238,75,0,0,0,0,0,80,240,255,255,255,255, 255,0,0,0,0,75,255,255,255,251,60,0,0,0,0,0,0,0,66,253,255,255,255,255,0,0,0,0,159,255,255,255, 151,0,0,0,0,0,0,0,0,0,158,255,255,255,255,0,0,0,0,213,255,255,255,54,0,0,0,0,0,0,0,0, 0,59,255,255,255,255,0,0,0,0,243,255,255,255,10,0,0,0,0,0,0,0,0,0,12,255,255,255,255,0,0,0, 0,252,255,255,247,0,0,0,0,0,0,0,0,0,0,0,247,255,255,255,0,0,0,0,241,255,255,255,14,0,0,0, 0,0,0,0,0,0,12,255,255,255,255,0,0,0,0,207,255,255,255,65,0,0,0,0,0,0,0,0,0,60,255,255, 255,255,0,0,0,0,148,255,255,255,165,0,0,0,0,0,0,0,0,0,158,255,255,255,255,0,0,0,0,62,255,255, 255,254,73,0,0,0,0,0,0,0,66,253,255,255,255,255,0,0,0,0,0,192,255,255,255,243,87,0,0,0,0,0, 79,240,255,255,255,255,255,0,0,0,0,0,32,241,255,255,255,255,205,126,100,125,200,255,255,255,254,255,255,255,0,0, 0,0,0,0,76,244,255,255,255,255,255,255,255,255,255,255,120,240,255,255,255,0,0,0,0,0,0,0,47,220,255,255, 255,255,255,255,255,240,95,0,240,255,255,255,0,0,0,0,0,0,0,0,6,96,181,232,251,232,199,117,21,0,0,240, 255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,255,255,249,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,20,255,255,255,228,0,0,0,0,5,170,172,172,149,0,0,0,0,0,0, 0,0,0,88,255,255,255,183,0,0,0,0,0,216,255,255,255,83,0,0,0,0,0,0,0,6,208,255,255,255,107,0, 0,0,0,0,122,255,255,255,248,94,1,0,0,0,0,13,175,255,255,255,238,14,0,0,0,0,0,13,219,255,255,255, 255,217,138,106,103,148,235,255,255,255,255,85,0,0,0,0,0,0,0,42,227,255,255,255,255,255,255,255,255,255,255,255, 255,136,0,0,0,0,0,0,0,0,0,25,169,255,255,255,255,255,255,255,255,255,241,88,0,0,0,0,0,0,0,0, 0,0,0,0,41,133,192,232,248,249,229,184,111,16,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,3,91,177,227,248,230,197,112,18,0,0,0,0,0,0,0,0,255,255,255,240,37,210,255,255,255,255,255,255,255,233, 67,0,0,0,0,0,0,0,255,255,255,248,231,255,255,255,255,255,255,255,255,255,252,61,0,0,0,0,0,0,255,255, 255,255,255,255,199,121,97,119,197,255,255,255,255,225,10,0,0,0,0,0,255,255,255,255,255,109,0,0,0,0,0,105, 255,255,255,255,91,0,0,0,0,0,255,255,255,255,162,0,0,0,0,0,0,0,161,255,255,255,178,0,0,0,0,0, 255,255,255,255,56,0,0,0,0,0,0,0,56,255,255,255,217,0,0,0,0,0,255,255,255,254,5,0,0,0,0,0, 0,0,5,254,255,255,243,0,0,0,0,0,255,255,255,242,0,0,0,0,0,0,0,0,0,242,255,255,255,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,33,190,243, 191,33,0,0,0,0,192,255,255,255,191,0,0,0,0,244,255,255,255,243,0,0,0,0,191,255,255,255,190,0,0,0, 0,33,191,244,192,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0, 240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0, 0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255,255,255,0,0,0,0,0,240,255, 255,255,0,0,0,0,0,0,33,190,243,191,33,0,0,0,0,0,192,255,255,255,191,0,0,0,0,0,244,255,255,255, 243,0,0,0,0,0,191,255,255,255,190,0,0,0,0,0,33,191,244,192,33,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255, 255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0, 0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 240,255,255,255,0,0,0,0,0,0,241,255,255,255,0,0,0,0,0,20,255,255,255,242,0,0,0,92,103,194,255,255, 255,205,0,0,0,255,255,255,255,255,255,125,0,0,0,255,255,255,255,255,209,12,0,0,0,255,255,246,210,128,12,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,1,171,255,255,255,255,165,1,0,0,0,255,255,255,240,0,0,0,0,0,126,255,255,255,255, 186,5,0,0,0,0,255,255,255,240,0,0,0,0,80,254,255,255,255,203,12,0,0,0,0,0,255,255,255,240,0,0, 0,45,243,255,255,255,218,21,0,0,0,0,0,0,255,255,255,240,0,0,19,222,255,255,255,230,32,0,0,0,0,0, 0,0,255,255,255,240,0,4,190,255,255,255,240,47,0,0,0,0,0,0,0,0,255,255,255,240,0,149,255,255,255,248, 63,0,0,0,0,0,0,0,0,0,255,255,255,240,101,255,255,255,253,82,0,0,0,0,0,0,0,0,0,0,255,255, 255,251,250,255,255,255,103,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,181,0,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,253,254,255,255,255,97,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,126,255, 255,255,252,77,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,164,255,255,255,246,59,0,0,0,0,0,0,0, 0,0,255,255,255,240,0,7,196,255,255,255,238,44,0,0,0,0,0,0,0,0,255,255,255,240,0,0,20,221,255,255, 255,228,30,0,0,0,0,0,0,0,255,255,255,240,0,0,0,41,239,255,255,255,216,20,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,68,251,255,255,255,201,11,0,0,0,0,0,255,255,255,240,0,0,0,0,0,102,255,255,255,255, 184,5,0,0,0,0,255,255,255,240,0,0,0,0,0,0,141,255,255,255,255,165,1,0,0,0,255,255,255,240,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0, 9,109,191,233,248,222,183,91,8,0,0,0,0,8,103,189,236,249,225,188,100,13,0,0,0,0,0,0,0,0,255,255, 255,240,55,225,255,255,255,255,255,255,255,217,46,0,0,47,219,255,255,255,255,255,255,255,227,61,0,0,0,0,0,0, 0,255,255,255,250,243,255,255,255,255,255,255,255,255,255,244,40,49,247,255,255,255,255,255,255,255,255,255,251,59,0,0, 0,0,0,0,255,255,255,255,255,255,199,121,97,119,197,255,255,255,255,207,214,255,255,205,123,97,117,190,255,255,255,255, 226,12,0,0,0,0,0,255,255,255,255,255,109,0,0,0,0,0,105,255,255,255,255,255,255,124,0,0,0,0,0,91, 253,255,255,255,98,0,0,0,0,0,255,255,255,255,162,0,0,0,0,0,0,0,161,255,255,255,255,179,0,0,0,0, 0,0,0,145,255,255,255,190,0,0,0,0,0,255,255,255,255,56,0,0,0,0,0,0,0,56,255,255,255,255,72,0, 0,0,0,0,0,0,40,255,255,255,231,0,0,0,0,0,255,255,255,254,5,0,0,0,0,0,0,0,5,254,255,255, 255,19,0,0,0,0,0,0,0,0,244,255,255,253,4,0,0,0,0,255,255,255,242,0,0,0,0,0,0,0,0,0, 242,255,255,255,2,0,0,0,0,0,0,0,0,226,255,255,255,16,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,224,255,255,255,16,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,224,255,255,255,16,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,224,255,255,255,16,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,224,255,255,255,16,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,224,255,255,255, 16,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,224, 255,255,255,16,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0, 0,0,224,255,255,255,16,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,0,0,0,0,224,255,255,255,16,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0, 0,0,0,0,0,0,0,0,224,255,255,255,16,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255, 255,255,0,0,0,0,0,0,0,0,0,224,255,255,255,16,0,0,0,0,255,255,255,240,0,8,105,188,232,248,225,188, 99,12,0,0,0,0,0,0,0,0,255,255,255,240,54,224,255,255,255,255,255,255,255,225,58,0,0,0,0,0,0,0, 255,255,255,250,243,255,255,255,255,255,255,255,255,255,250,54,0,0,0,0,0,0,255,255,255,255,255,255,199,121,97,119, 197,255,255,255,255,222,8,0,0,0,0,0,255,255,255,255,255,109,0,0,0,0,0,105,255,255,255,255,88,0,0,0, 0,0,255,255,255,255,162,0,0,0,0,0,0,0,161,255,255,255,177,0,0,0,0,0,255,255,255,255,56,0,0,0, 0,0,0,0,56,255,255,255,216,0,0,0,0,0,255,255,255,254,5,0,0,0,0,0,0,0,5,254,255,255,243,0, 0,0,0,0,255,255,255,242,0,0,0,0,0,0,0,0,0,242,255,255,255,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,54,145,205,240,252,240,205, 144,52,0,0,0,0,0,0,0,0,0,0,0,20,178,255,255,255,255,255,255,255,255,255,175,19,0,0,0,0,0,0, 0,0,51,229,255,255,255,255,255,255,255,255,255,255,255,228,51,0,0,0,0,0,0,20,227,255,255,255,254,190,122,100, 125,199,255,255,255,255,226,20,0,0,0,0,0,180,255,255,255,228,58,0,0,0,0,0,72,235,255,255,255,180,0,0, 0,0,55,255,255,255,247,44,0,0,0,0,0,0,0,55,250,255,255,255,55,0,0,0,147,255,255,255,139,0,0,0, 0,0,0,0,0,0,149,255,255,255,146,0,0,0,207,255,255,255,49,0,0,0,0,0,0,0,0,0,53,255,255,255, 206,0,0,0,241,255,255,255,9,0,0,0,0,0,0,0,0,0,11,255,255,255,240,0,0,0,252,255,255,244,0,0, 0,0,0,0,0,0,0,0,0,247,255,255,252,0,0,0,241,255,255,254,3,0,0,0,0,0,0,0,0,0,12,255, 255,255,239,0,0,0,208,255,255,255,45,0,0,0,0,0,0,0,0,0,58,255,255,255,203,0,0,0,150,255,255,255, 129,0,0,0,0,0,0,0,0,0,156,255,255,255,140,0,0,0,62,255,255,255,240,33,0,0,0,0,0,0,0,65, 252,255,255,254,45,0,0,0,0,190,255,255,255,220,44,0,0,0,0,0,84,240,255,255,255,162,0,0,0,0,0,26, 234,255,255,255,252,173,111,99,129,207,255,255,255,255,212,11,0,0,0,0,0,0,60,234,255,255,255,255,255,255,255,255, 255,255,255,214,35,0,0,0,0,0,0,0,0,26,189,255,255,255,255,255,255,255,255,253,151,9,0,0,0,0,0,0, 0,0,0,0,0,63,153,212,244,252,237,197,131,36,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,24,118, 199,232,250,233,184,102,8,0,0,0,0,0,0,0,0,255,255,255,240,0,107,243,255,255,255,255,255,255,255,227,57,0, 0,0,0,0,0,0,255,255,255,240,139,255,255,255,255,255,255,255,255,255,255,249,91,0,0,0,0,0,0,255,255,255, 255,255,255,255,203,126,100,125,201,255,255,255,255,247,44,0,0,0,0,0,255,255,255,255,255,241,82,0,0,0,0,0, 77,238,255,255,255,207,1,0,0,0,0,255,255,255,255,253,67,0,0,0,0,0,0,0,60,252,255,255,255,74,0,0, 0,0,255,255,255,255,158,0,0,0,0,0,0,0,0,0,152,255,255,255,158,0,0,0,0,255,255,255,255,58,0,0, 0,0,0,0,0,0,0,54,255,255,255,212,0,0,0,0,255,255,255,255,11,0,0,0,0,0,0,0,0,0,10,255, 255,255,242,0,0,0,0,255,255,255,246,0,0,0,0,0,0,0,0,0,0,0,247,255,255,252,0,0,0,0,255,255, 255,255,12,0,0,0,0,0,0,0,0,0,15,255,255,255,239,0,0,0,0,255,255,255,255,60,0,0,0,0,0,0, 0,0,0,66,255,255,255,205,0,0,0,0,255,255,255,255,158,0,0,0,0,0,0,0,0,0,166,255,255,255,147,0, 0,0,0,255,255,255,255,253,67,0,0,0,0,0,0,0,73,254,255,255,255,61,0,0,0,0,255,255,255,255,255,241, 82,0,0,0,0,0,87,244,255,255,255,193,0,0,0,0,0,255,255,255,255,255,255,255,202,125,100,126,205,255,255,255, 255,243,33,0,0,0,0,0,255,255,255,240,139,255,255,255,255,255,255,255,255,255,255,245,79,0,0,0,0,0,0,255, 255,255,240,0,105,243,255,255,255,255,255,255,255,222,49,0,0,0,0,0,0,0,255,255,255,240,0,0,25,120,200,233, 251,233,183,98,6,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,100, 182,231,250,233,203,126,30,0,0,240,255,255,255,0,0,0,0,0,0,0,55,225,255,255,255,255,255,255,255,247,120,2, 240,255,255,255,0,0,0,0,0,0,87,248,255,255,255,255,255,255,255,255,255,255,152,240,255,255,255,0,0,0,0,0, 42,246,255,255,255,255,199,125,100,125,201,255,255,255,255,255,255,255,0,0,0,0,1,207,255,255,255,238,75,0,0,0, 0,0,80,240,255,255,255,255,255,0,0,0,0,75,255,255,255,251,60,0,0,0,0,0,0,0,66,253,255,255,255,255, 0,0,0,0,159,255,255,255,151,0,0,0,0,0,0,0,0,0,158,255,255,255,255,0,0,0,0,213,255,255,255,54, 0,0,0,0,0,0,0,0,0,59,255,255,255,255,0,0,0,0,243,255,255,255,10,0,0,0,0,0,0,0,0,0, 12,255,255,255,255,0,0,0,0,252,255,255,247,0,0,0,0,0,0,0,0,0,0,0,247,255,255,255,0,0,0,0, 241,255,255,255,14,0,0,0,0,0,0,0,0,0,12,255,255,255,255,0,0,0,0,207,255,255,255,65,0,0,0,0, 0,0,0,0,0,60,255,255,255,255,0,0,0,0,148,255,255,255,165,0,0,0,0,0,0,0,0,0,158,255,255,255, 255,0,0,0,0,62,255,255,255,254,73,0,0,0,0,0,0,0,66,253,255,255,255,255,0,0,0,0,0,193,255,255, 255,243,87,0,0,0,0,0,79,240,255,255,255,255,255,0,0,0,0,0,33,242,255,255,255,255,205,126,100,125,200,255, 255,255,255,255,255,255,0,0,0,0,0,0,77,245,255,255,255,255,255,255,255,255,255,255,146,240,255,255,255,0,0,0, 0,0,0,0,50,224,255,255,255,255,255,255,255,245,114,1,240,255,255,255,0,0,0,0,0,0,0,0,8,102,186,235, 251,231,199,120,27,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255, 255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 255,255,255,240,0,1,84,181,228,124,0,0,0,255,255,255,240,15,181,255,255,255,128,0,0,0,255,255,255,241,180,255, 255,255,255,128,0,0,0,255,255,255,255,255,255,255,255,255,128,0,0,0,255,255,255,255,255,209,85,23,2,0,0,0, 0,255,255,255,255,208,9,0,0,0,0,0,0,0,255,255,255,255,83,0,0,0,0,0,0,0,0,255,255,255,255,19, 0,0,0,0,0,0,0,0,255,255,255,248,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,67,150,212,236,250,236,197, 131,31,0,0,0,0,0,0,0,27,189,255,255,255,255,255,255,255,255,248,117,0,0,0,0,0,21,220,255,255,255,255, 255,255,255,255,255,255,255,122,0,0,0,0,145,255,255,255,247,158,109,104,145,240,255,255,255,250,33,0,0,0,224,255, 255,255,69,0,0,0,0,25,225,255,255,255,116,0,0,0,244,255,255,251,2,0,0,0,0,0,116,255,255,255,155,0, 0,0,207,255,255,255,117,1,0,0,0,0,0,0,0,0,0,0,0,0,102,255,255,255,255,201,98,24,0,0,0,0, 0,0,0,0,0,0,2,157,255,255,255,255,255,254,207,135,56,0,0,0,0,0,0,0,0,0,88,213,255,255,255,255, 255,255,255,218,90,0,0,0,0,0,0,0,0,0,48,126,197,252,255,255,255,255,255,156,1,0,0,0,0,0,0,0, 0,0,0,17,92,187,255,255,255,255,97,0,0,4,8,8,8,5,0,0,0,0,0,0,96,255,255,255,196,0,0,117, 255,255,255,229,1,0,0,0,0,0,13,255,255,255,229,0,0,63,255,255,255,255,134,1,0,0,0,0,111,255,255,255, 203,0,0,1,203,255,255,255,255,209,126,99,117,186,254,255,255,255,122,0,0,0,31,221,255,255,255,255,255,255,255,255, 255,255,255,207,11,0,0,0,0,26,172,255,255,255,255,255,255,255,255,255,177,18,0,0,0,0,0,0,0,52,138,205, 232,250,235,209,145,58,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0, 0,240,255,255,255,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,0,0,255,255,255,255,255,255,255, 255,255,255,255,0,0,255,255,255,255,255,255,255,255,255,255,255,0,0,92,92,245,255,255,255,92,92,92,92,92,0,0, 0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,0,0,239,255,255,255,1,0,0,0,0, 0,0,0,0,228,255,255,255,42,0,0,0,0,0,0,0,0,189,255,255,255,217,118,93,92,92,0,0,0,0,102,255, 255,255,255,255,255,255,255,0,0,0,0,4,177,255,255,255,255,255,255,255,0,0,0,0,0,1,91,184,233,251,255,255, 255,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0, 255,255,255,242,0,0,0,0,0,0,0,0,0,242,255,255,255,0,0,0,0,0,243,255,255,254,5,0,0,0,0,0, 0,0,6,254,255,255,255,0,0,0,0,0,217,255,255,255,55,0,0,0,0,0,0,0,57,255,255,255,255,0,0,0, 0,0,177,255,255,255,160,0,0,0,0,0,0,0,163,255,255,255,255,0,0,0,0,0,87,255,255,255,255,104,0,0, 0,0,0,109,255,255,255,255,255,0,0,0,0,0,8,221,255,255,255,255,196,118,97,119,199,255,255,255,255,255,255,0, 0,0,0,0,0,51,249,255,255,255,255,255,255,255,255,255,240,250,255,255,255,0,0,0,0,0,0,0,56,224,255,255, 255,255,255,255,255,222,50,240,255,255,255,0,0,0,0,0,0,0,0,12,100,187,225,249,233,189,106,7,0,240,255,255, 255,0,0,0,121,255,255,255,168,0,0,0,0,0,0,0,0,0,23,252,255,255,245,13,27,253,255,255,245,11,0,0, 0,0,0,0,0,0,110,255,255,255,163,0,0,185,255,255,255,90,0,0,0,0,0,0,0,0,199,255,255,255,67,0, 0,89,255,255,255,180,0,0,0,0,0,0,0,32,255,255,255,225,1,0,0,8,240,255,255,250,18,0,0,0,0,0, 0,121,255,255,255,130,0,0,0,0,154,255,255,255,102,0,0,0,0,0,0,210,255,255,255,35,0,0,0,0,58,255, 255,255,191,0,0,0,0,0,42,255,255,255,194,0,0,0,0,0,0,217,255,255,253,27,0,0,0,0,132,255,255,255, 98,0,0,0,0,0,0,122,255,255,255,114,0,0,0,0,220,255,255,245,13,0,0,0,0,0,0,28,253,255,255,203, 0,0,0,54,255,255,255,161,0,0,0,0,0,0,0,0,187,255,255,255,37,0,0,143,255,255,255,65,0,0,0,0, 0,0,0,0,91,255,255,255,126,0,2,229,255,255,223,1,0,0,0,0,0,0,0,0,9,241,255,255,215,0,65,255, 255,255,129,0,0,0,0,0,0,0,0,0,0,155,255,255,255,48,154,255,255,254,34,0,0,0,0,0,0,0,0,0, 0,59,255,255,255,143,237,255,255,192,0,0,0,0,0,0,0,0,0,0,0,0,218,255,255,251,255,255,255,96,0,0, 0,0,0,0,0,0,0,0,0,0,124,255,255,255,255,255,244,12,0,0,0,0,0,0,0,0,0,0,0,0,29,253, 255,255,255,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,255,255,255,255,64,0,0,0,0,0,0,0, 129,255,255,255,104,0,0,0,0,0,0,3,241,255,255,255,87,0,0,0,0,0,0,57,255,255,255,140,50,255,255,255, 169,0,0,0,0,0,0,56,255,255,255,255,152,0,0,0,0,0,0,122,255,255,255,62,0,226,255,255,234,1,0,0, 0,0,0,123,255,255,255,255,216,0,0,0,0,0,0,187,255,255,236,3,0,149,255,255,255,44,0,0,0,0,0,190, 255,255,255,255,255,24,0,0,0,0,6,245,255,255,161,0,0,70,255,255,255,110,0,0,0,0,8,247,255,255,255,255, 255,88,0,0,0,0,62,255,255,255,83,0,0,5,241,255,255,175,0,0,0,0,67,255,255,230,208,255,255,152,0,0, 0,0,127,255,255,248,12,0,0,0,168,255,255,238,2,0,0,0,134,255,255,161,139,255,255,217,0,0,0,0,192,255, 255,182,0,0,0,0,90,255,255,255,50,0,0,0,201,255,255,92,70,255,255,255,25,0,0,8,248,255,255,104,0,0, 0,0,16,250,255,255,115,0,0,15,252,255,255,24,9,248,255,255,89,0,0,67,255,255,254,27,0,0,0,0,0,188, 255,255,181,0,0,78,255,255,210,0,0,189,255,255,153,0,0,132,255,255,203,0,0,0,0,0,0,109,255,255,242,4, 0,145,255,255,141,0,0,120,255,255,217,0,0,197,255,255,125,0,0,0,0,0,0,31,255,255,255,56,0,212,255,255, 72,0,0,51,255,255,255,26,11,250,255,255,47,0,0,0,0,0,0,0,208,255,255,121,23,255,255,249,10,0,0,2, 236,255,255,90,72,255,255,224,0,0,0,0,0,0,0,0,129,255,255,187,89,255,255,190,0,0,0,0,170,255,255,154, 137,255,255,146,0,0,0,0,0,0,0,0,50,255,255,246,163,255,255,121,0,0,0,0,101,255,255,218,202,255,255,68, 0,0,0,0,0,0,0,0,0,227,255,255,250,255,255,52,0,0,0,0,32,255,255,255,253,255,240,5,0,0,0,0, 0,0,0,0,0,149,255,255,255,255,237,2,0,0,0,0,0,220,255,255,255,255,167,0,0,0,0,0,0,0,0,0, 0,70,255,255,255,255,170,0,0,0,0,0,0,151,255,255,255,255,89,0,0,0,0,0,0,0,0,0,0,6,241,255, 255,255,102,0,0,0,0,0,0,82,255,255,255,251,16,0,0,0,0,0,88,255,255,255,239,24,0,0,0,0,0,0, 183,255,255,255,77,0,183,255,255,255,168,0,0,0,0,0,79,255,255,255,167,0,0,33,246,255,255,255,72,0,0,0, 8,222,255,255,236,22,0,0,0,119,255,255,255,223,10,0,0,125,255,255,255,93,0,0,0,0,4,210,255,255,255,138, 0,30,245,255,255,184,0,0,0,0,0,0,56,253,255,255,251,47,172,255,255,244,31,0,0,0,0,0,0,0,150,255, 255,255,224,255,255,255,110,0,0,0,0,0,0,0,0,15,230,255,255,255,255,255,199,2,0,0,0,0,0,0,0,0, 0,86,255,255,255,255,249,42,0,0,0,0,0,0,0,0,0,0,0,200,255,255,255,190,0,0,0,0,0,0,0,0, 0,0,0,45,250,255,255,255,255,72,0,0,0,0,0,0,0,0,0,3,202,255,255,255,255,255,223,10,0,0,0,0, 0,0,0,0,115,255,255,254,225,255,255,255,138,0,0,0,0,0,0,0,35,245,255,255,160,57,254,255,255,251,47,0, 0,0,0,0,0,189,255,255,241,24,0,151,255,255,255,201,2,0,0,0,0,100,255,255,255,114,0,0,15,230,255,255, 255,108,0,0,0,25,239,255,255,215,5,0,0,0,86,255,255,255,242,27,0,0,175,255,255,255,69,0,0,0,0,0, 181,255,255,255,174,0,85,255,255,255,175,0,0,0,0,0,0,31,244,255,255,255,79,118,255,255,255,213,0,0,0,0, 0,0,0,0,0,0,201,255,255,255,51,22,250,255,255,255,50,0,0,0,0,0,0,0,0,41,255,255,255,201,0,0, 172,255,255,255,143,0,0,0,0,0,0,0,0,138,255,255,255,96,0,0,71,255,255,255,232,3,0,0,0,0,0,0, 3,231,255,255,238,9,0,0,2,224,255,255,255,73,0,0,0,0,0,0,75,255,255,255,142,0,0,0,0,126,255,255, 255,166,0,0,0,0,0,0,172,255,255,255,39,0,0,0,0,28,252,255,255,246,13,0,0,0,0,19,249,255,255,188, 0,0,0,0,0,0,180,255,255,255,96,0,0,0,0,110,255,255,255,83,0,0,0,0,0,0,80,255,255,255,188,0, 0,0,0,206,255,255,230,4,0,0,0,0,0,0,4,230,255,255,253,28,0,0,47,255,255,255,129,0,0,0,0,0, 0,0,0,134,255,255,255,118,0,0,144,255,255,252,28,0,0,0,0,0,0,0,0,34,254,255,255,211,0,5,234,255, 255,175,0,0,0,0,0,0,0,0,0,0,188,255,255,255,48,81,255,255,255,70,0,0,0,0,0,0,0,0,0,0, 88,255,255,255,141,178,255,255,220,1,0,0,0,0,0,0,0,0,0,0,7,236,255,255,235,251,255,255,116,0,0,0, 0,0,0,0,0,0,0,0,0,142,255,255,255,255,255,248,19,0,0,0,0,0,0,0,0,0,0,0,0,41,255,255, 255,255,255,162,0,0,0,0,0,0,0,0,0,0,0,0,0,0,197,255,255,255,255,57,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,103,255,255,255,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,153,255,255,255,103,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,14,243,255,255,242,12,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,107,255,255,255,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,212,255,255,255,45,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,62,255,255,255,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,167,255,255,255, 90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,249,255,255,235,7,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,122,255,255,255,136,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,224,255,255,254,34,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,0,0,0,255,255,255,255,255,255,255,255, 255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,92,92,92,92,92,92,92,92, 203,255,255,255,219,0,0,0,0,0,0,0,0,0,0,65,254,255,255,252,57,0,0,0,0,0,0,0,0,0,15,225, 255,255,255,131,0,0,0,0,0,0,0,0,0,0,164,255,255,255,205,5,0,0,0,0,0,0,0,0,0,85,255,255, 255,247,41,0,0,0,0,0,0,0,0,0,26,237,255,255,255,111,0,0,0,0,0,0,0,0,0,1,184,255,255,255, 188,1,0,0,0,0,0,0,0,0,0,109,255,255,255,239,28,0,0,0,0,0,0,0,0,0,40,246,255,255,255,90, 0,0,0,0,0,0,0,0,0,4,203,255,255,255,169,0,0,0,0,0,0,0,0,0,0,131,255,255,255,229,18,0, 0,0,0,0,0,0,0,0,56,252,255,255,255,71,0,0,0,0,0,0,0,0,0,0,219,255,255,255,207,92,92,92, 92,92,92,92,92,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255, 255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,1, 96,196,242,255,255,128,0,0,0,0,0,0,0,0,0,0,2,168,255,255,255,255,255,128,0,0,0,0,0,0,0,0, 0,0,103,255,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,201,255,255,255,213,111,92,46,0,0,0,0, 0,0,0,0,0,0,244,255,255,255,31,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,242,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 249,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,255,244,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,244,255,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,0,242,255,255,252,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,241,255,255,254,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,255,255,252,0,0, 0,0,0,0,0,0,0,0,0,0,0,38,255,255,255,221,0,0,0,0,0,0,0,0,0,0,0,0,12,185,255,255, 255,151,0,0,0,0,0,0,0,0,0,0,0,148,227,255,255,255,221,25,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,229,142,19,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,229,141,18,0,0,0,0,0,0,0,0,0, 0,0,0,148,226,255,255,255,219,24,0,0,0,0,0,0,0,0,0,0,0,0,12,185,255,255,255,149,0,0,0,0, 0,0,0,0,0,0,0,0,0,37,255,255,255,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,255,255,251, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,241,255,255,254,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 242,255,255,252,0,0,0,0,0,0,0,0,0,0,0,0,0,0,244,255,255,248,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,247,255,255,244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,249,255,255,240,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,252,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,240,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,242,0,0,0,0,0,0,0,0,0,0,0,0,0,0,244,255,255,255,30,0,0,0,0,0,0,0,0,0,0,0, 0,0,201,255,255,255,210,110,92,46,0,0,0,0,0,0,0,0,0,0,103,255,255,255,255,255,255,128,0,0,0,0, 0,0,0,0,0,0,2,169,255,255,255,255,255,128,0,0,0,0,0,0,0,0,0,0,0,1,97,197,243,255,255,128, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,255,255,248,211,119, 5,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,187,4,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,255,255,255,112,0,0,0,0,0,0,0,0,0,0,0,92,98,157,255,255,255,203,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,186,255,255,243,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,255,255,255,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,150,255,255,254,0,0,0,0,0,0,0,0,0,0,0,0,0,0,166,255,255,253, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,182,255,255,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 193,255,255,248,0,0,0,0,0,0,0,0,0,0,0,0,0,0,211,255,255,246,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,228,255,255,243,0,0,0,0,0,0,0,0,0,0,0,0,0,0,241,255,255,242,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,251,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,245,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,223,255,255,255,36,0,0,0,0,0,0,0,0,0,0,0,0,0,151,255, 255,255,183,11,0,0,0,0,0,0,0,0,0,0,0,0,25,221,255,255,255,226,147,0,0,0,0,0,0,0,0,0, 0,0,0,18,140,227,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,18,139,227,255,255,255,0,0,0,0,0, 0,0,0,0,0,0,24,219,255,255,255,225,148,0,0,0,0,0,0,0,0,0,0,0,149,255,255,255,183,11,0,0, 0,0,0,0,0,0,0,0,0,0,222,255,255,255,36,0,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,245, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,251,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 241,255,255,242,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,255,255,243,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,211,255,255,246,0,0,0,0,0,0,0,0,0,0,0,0,0,0,193,255,255,248,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,182,255,255,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,167,255,255,253,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,150,255,255,254,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,255, 255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,255,255,243,0,0,0,0,0,0,0,0,0,0,0,92, 97,155,255,255,255,203,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,112,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,255,255,188,4,0,0,0,0,0,0,0,0,0,0,0,255,255,248,212,119,6,0,0,0,0,0, 0,0,0,0,0,0,0,0,5,58,86,68,11,0,0,0,0,0,28,92,92,49,0,0,0,0,83,229,255,255,255,241, 132,14,0,0,0,150,255,255,93,0,0,0,83,254,255,255,255,255,255,255,236,139,102,163,255,255,247,17,0,0,11,233, 255,255,255,255,255,255,255,255,255,255,255,255,255,142,0,0,0,96,255,255,205,48,12,63,182,255,255,255,255,255,255,194, 9,0,0,0,168,255,255,44,0,0,0,0,68,184,242,248,211,117,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,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,190,243,191,33,0,0,0,0,0,0,191,255,255,255, 190,0,0,0,0,0,0,244,255,255,255,243,0,0,0,0,0,0,192,255,255,255,191,0,0,0,0,0,0,33,191,244, 192,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0, 240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0, 0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0, 0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,0,19,129,214,246,237,189,91, 1,255,255,255,8,0,0,0,49,227,255,255,255,255,255,255,173,255,255,255,8,0,0,17,225,255,255,244,143,100,133,234, 255,255,255,255,8,0,0,124,255,255,230,35,0,0,0,21,218,255,255,255,8,0,0,209,255,255,104,0,0,0,0,0, 78,255,255,255,8,0,0,242,255,255,46,0,0,0,0,0,21,255,255,255,8,0,0,240,255,255,47,0,0,0,0,0, 21,255,255,255,8,0,0,203,255,255,109,0,0,0,0,0,78,255,255,255,8,0,0,116,255,255,233,39,0,0,0,20, 218,255,255,255,8,0,0,13,220,255,255,246,144,99,132,234,255,255,255,255,8,0,0,0,44,224,255,255,255,255,255,255, 189,255,255,255,8,0,0,0,0,18,127,212,245,241,199,107,4,255,255,255,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,0,0,0,0,0,0,0,0,155,255,255,250,35,0, 27,247,255,255,165,0,0,0,0,0,36,251,255,255,147,0,0,152,255,255,249,33,0,0,0,0,0,166,255,255,242,22, 0,34,250,255,255,143,0,0,0,0,0,44,253,255,255,126,0,0,163,255,255,240,19,0,0,0,0,0,176,255,255,231, 11,0,42,253,255,255,120,0,0,0,0,0,53,255,255,255,105,0,0,173,255,255,228,9,0,0,0,0,0,54,255,255, 255,104,0,0,174,255,255,227,9,0,0,0,0,0,0,176,255,255,231,11,0,42,253,255,255,120,0,0,0,0,0,0, 45,253,255,255,126,0,0,163,255,255,240,19,0,0,0,0,0,0,166,255,255,242,22,0,35,250,255,255,143,0,0,0, 0,0,0,36,251,255,255,147,0,0,152,255,255,249,33,0,0,0,0,0,0,155,255,255,250,35,0,27,247,255,255,166, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,7,102,187,235,250,229,186,97,4,0,0,0,0,0,35,211,255,255,255,255,255,255,255, 205,32,0,0,0,13,221,255,255,197,78,46,83,203,255,255,212,12,0,0,126,255,255,202,4,0,0,0,6,194,255,255, 113,0,0,209,255,255,68,0,0,0,0,0,63,255,255,204,0,0,245,255,255,19,0,0,0,0,0,10,255,255,240,0, 0,245,255,255,17,0,0,0,0,0,9,255,255,238,0,0,209,255,255,61,0,0,0,0,0,63,255,255,201,0,0,127, 255,255,189,2,0,0,0,7,195,255,255,107,0,0,15,224,255,255,196,82,51,93,215,255,255,204,9,0,0,0,39,217, 255,255,255,255,255,255,255,194,25,0,0,0,0,0,10,111,195,239,249,226,178,87,2,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,189,243,189,30,0,0,0,190,255,255,255,188,0,0,0, 244,255,255,255,243,0,0,0,192,255,255,255,190,0,0,0,33,191,244,191,32,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,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,90,182,228,249,228,184,94,3,0,0,0,0,0,27,197,255,255,255,255,255,255,255,201,29,0,0,0,10,207,255, 255,240,137,100,141,244,255,255,207,9,0,0,111,255,255,235,29,0,0,0,36,239,255,255,109,0,0,203,255,255,110,0, 0,0,0,0,115,255,255,202,0,0,240,255,255,57,0,0,0,0,0,58,255,255,239,0,0,240,255,255,54,0,0,0, 0,0,47,255,255,238,0,0,205,255,255,108,0,0,0,0,0,107,255,255,200,0,0,115,255,255,233,30,0,0,0,40, 232,255,255,103,0,0,12,213,255,255,240,141,107,150,247,255,255,199,7,0,0,0,31,204,255,255,255,255,255,255,255,191, 22,0,0,0,0,0,5,98,187,231,249,227,178,86,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,21,242,255,255,170,0,0,128,255,255,254,51,0,0,0,0,0,0,126,255,255,254,50,0,12, 233,255,255,185,0,0,0,0,0,0,11,231,255,255,184,0,0,109,255,255,255,63,0,0,0,0,0,0,106,255,255,255, 62,0,6,221,255,255,198,0,0,0,0,0,0,4,218,255,255,197,0,0,90,255,255,255,77,0,0,0,0,0,0,85, 255,255,255,76,0,1,207,255,255,210,2,0,0,0,0,0,86,255,255,255,76,0,1,207,255,255,210,2,0,0,0,0, 4,218,255,255,197,0,0,90,255,255,255,77,0,0,0,0,0,106,255,255,255,63,0,5,221,255,255,198,0,0,0,0, 0,11,231,255,255,184,0,0,109,255,255,255,64,0,0,0,0,0,126,255,255,254,51,0,12,233,255,255,185,0,0,0, 0,0,20,242,255,255,171,0,0,127,255,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,0,0,0,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, 191,243,189,31,0,0,0,0,0,0,0,0,0,0,0,0,0,191,255,255,255,189,0,0,0,0,0,0,0,0,0,0, 0,0,0,244,255,255,255,243,0,0,0,0,0,0,0,0,0,0,0,0,0,192,255,255,255,190,0,0,0,0,0,0, 0,0,0,0,0,0,0,34,191,244,190,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100, 100,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,31,97,141,159,255,255,255,240,0,0,0,0,0,0,0,0, 36,180,253,255,255,255,255,255,255,240,0,0,0,0,0,0,0,64,245,255,255,255,255,255,255,255,255,240,0,0,0,0, 0,0,21,239,255,255,255,239,146,78,37,12,3,0,0,0,0,0,0,0,132,255,255,255,211,24,0,0,0,0,0,0, 0,0,0,0,0,0,206,255,255,255,65,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,255,253,4,0,0,0, 0,0,0,0,0,0,0,0,0,0,249,255,255,246,0,0,0,0,0,0,0,16,176,176,168,0,0,0,229,255,255,255, 32,0,0,0,0,0,0,60,255,255,220,0,0,0,174,255,255,255,154,0,0,0,0,0,1,182,255,255,170,0,0,0, 82,255,255,255,255,177,57,15,21,67,194,255,255,255,71,0,0,0,0,187,255,255,255,255,255,255,255,255,255,255,255,173, 0,0,0,0,0,16,195,255,255,255,255,255,255,255,255,255,188,14,0,0,0,0,0,0,4,111,222,255,255,255,255,255, 212,107,1,0,0,0,0,0,0,0,0,0,0,35,73,86,66,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,193,128, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,167,254,255,128,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,17,140,249,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 114,238,255,255,255,236,123,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,234,120,12,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,232,116,10,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,113,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,106,255,255,255,255,108,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,255,255,255,255,199,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,31,254,255,255,255,255,255,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 121,255,255,255,255,255,255,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,211,255,255,254,254,255,255,213, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,255,255,255,193,195,255,255,255,47,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,136,255,255,255,105,107,255,255,255,137,0,0,0,0,0,0,0,0,0,0,0,0,0,1,224, 255,255,251,21,23,252,255,255,226,1,0,0,0,0,0,0,0,0,0,0,0,0,60,255,255,255,185,0,0,188,255,255, 255,62,0,0,0,0,0,0,0,0,0,0,0,0,151,255,255,255,97,0,0,101,255,255,255,152,0,0,0,0,0,0, 0,0,0,0,0,4,235,255,255,248,16,0,0,18,250,255,255,236,5,0,0,0,0,0,0,0,0,0,0,75,255,255, 255,176,0,0,0,0,182,255,255,255,76,0,0,0,0,0,0,0,0,0,0,166,255,255,255,88,0,0,0,0,94,255, 255,255,166,0,0,0,0,0,0,0,0,0,11,244,255,255,245,11,0,0,0,0,14,248,255,255,245,11,0,0,0,0, 0,0,0,0,90,255,255,255,168,0,0,0,0,0,0,175,255,255,255,91,0,0,0,0,0,0,0,0,181,255,255,255, 80,0,0,0,0,0,0,88,255,255,255,181,0,0,0,0,0,0,0,19,250,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,251,20,0,0,0,0,0,0,105,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,105,0,0,0, 0,0,0,196,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,195,0,0,0,0,0,30,254,255,255,243,92, 92,92,92,92,92,92,92,92,94,247,255,255,254,31,0,0,0,0,120,255,255,255,160,0,0,0,0,0,0,0,0,0, 0,169,255,255,255,120,0,0,0,0,211,255,255,255,66,0,0,0,0,0,0,0,0,0,0,75,255,255,255,210,0,0, 0,44,255,255,255,226,2,0,0,0,0,0,0,0,0,0,0,4,232,255,255,255,44,0,0,135,255,255,255,134,0,0, 0,0,0,0,0,0,0,0,0,0,143,255,255,255,134,0,0,0,0,0,0,0,0,0,30,113,179,223,244,250,235,205, 154,79,6,0,0,0,0,0,0,0,0,0,0,0,0,0,32,166,252,255,255,255,255,255,255,255,255,255,233,112,6,0, 0,0,0,0,0,0,0,0,0,99,244,255,255,255,255,255,255,255,255,255,255,255,255,255,204,35,0,0,0,0,0,0, 0,0,129,255,255,255,255,254,197,135,103,100,128,174,249,255,255,255,255,231,38,0,0,0,0,0,0,104,255,255,255,255, 191,41,0,0,0,0,0,0,20,153,255,255,255,255,220,12,0,0,0,0,39,247,255,255,255,141,3,0,0,0,0,0, 0,0,0,0,110,255,255,255,255,147,0,0,0,0,176,255,255,255,178,0,0,0,0,0,0,0,0,0,0,0,0,176, 255,255,255,249,28,0,0,38,254,255,255,239,20,0,0,0,0,0,0,0,0,0,0,0,0,7,24,24,24,24,8,0, 0,124,255,255,255,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,255,255,255,62, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,255,255,255,14,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,255,247,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,251,255,255,245,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,236,255,255,255,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,202,255,255, 255,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,149,255,255,255,124,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,74,255,255,255,226,7,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,225,255,255,255,143,0,0,0,0,0,0,0,0,0,0,0,0,127,220,220, 220,219,36,0,0,0,100,255,255,255,254,92,0,0,0,0,0,0,0,0,0,0,62,253,255,255,255,175,0,0,0,0, 2,193,255,255,255,255,143,9,0,0,0,0,0,0,0,96,246,255,255,255,237,26,0,0,0,0,0,25,223,255,255,255, 255,232,142,78,46,44,71,118,213,255,255,255,255,246,61,0,0,0,0,0,0,0,32,213,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,229,58,0,0,0,0,0,0,0,0,0,12,149,253,255,255,255,255,255,255,255,255,255,255,252,153, 20,0,0,0,0,0,0,0,0,0,0,0,0,36,142,222,255,255,255,255,255,252,202,127,29,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,20,223,255,255,39,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,216,255,255,64,34,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216, 255,255,255,255,216,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,255,255,255,255,255,255, 114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,21,124,255,255,226,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,255,255,247,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,21,40,40,40,40,55,149,255,255,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140, 255,255,255,255,255,255,255,242,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,255,255,255,255,248,215, 149,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,11,126,243,0,0,0,0,0,0,0,0,0,0,0,0,0,4,104,232,255,255,0,0,0,0,0, 0,0,0,0,0,0,0,82,217,255,255,255,255,0,0,0,0,0,0,0,0,0,0,54,199,255,255,255,255,183,58,0, 0,0,0,0,0,0,0,0,0,160,255,255,254,175,51,0,0,0,0,0,0,0,0,0,0,0,0,160,253,168,44,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,85,39,0,0,0,0,0,0,0,0,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,0,0,0,0, 0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255, 0,0,0,0,0,255,255,255,245,92,92,92,92,92,92,92,92,92,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,245,92,92,92,92,92,92,92,92,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255, 255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255, 255,255,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,245,92,92,92,92,92, 92,92,92,92,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255, 255,255,255,255,255,255,255,255,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0, 0,0,0,1,26,0,0,0,0,0,0,61,201,128,0,0,0,0,39,175,255,255,128,0,0,21,148,251,255,255,255,127, 0,122,242,255,255,255,231,115,10,0,255,255,255,229,112,9,0,0,0,255,227,108,7,0,0,0,0,0,105,6,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240, 0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,30,177,243,233,134,13,0,30,246,240,8,0,0,0,0,0,0,0,0,0,0,0,0,0,8,218, 255,255,255,255,230,154,216,255,190,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,255,241,137,182,255,255,255,255, 253,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,240,109,0,0,70,200,243,204,69,0,0,0,0,0,0, 0,0,0,0,0,0,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,64,0,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,255,216,6,0,0,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,255,255,126,0,0,0,0,0,0,0,0,0,240, 255,255,255,0,0,0,0,0,0,255,255,255,255,255,248,37,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,0,255,255,255,255,255,255,188,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,255,255, 255,255,92,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,242,206,255,255,234,18,0,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,53,253,255,255,155,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,0,255,255,255,240,0,148,255,255,254,60,0,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255, 255,255,240,0,14,230,255,255,213,5,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,85,255, 255,255,122,0,0,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,182,255,255,246,34,0,0,0, 240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,33,246,255,255,184,0,0,0,240,255,255,255,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,120,255,255,255,88,0,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240, 0,0,0,0,5,211,255,255,231,16,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,58,254, 255,255,151,0,240,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,154,255,255,254,56,240,255,255, 255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,17,233,255,255,209,243,255,255,255,0,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,0,0,92,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,188,255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,37,248, 255,255,255,255,255,0,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,126,255,255,255,255,255,0,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,6,216,255,255,255,255,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,64,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,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,128,112,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,101,231,255,124,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,73,212,255,255,255,124,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,48,188,255,255,255,255,189,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,253,255,255,255,186,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 184,57,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,54,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,128,188,230,247,248,230,189,130,45,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,49,185,255,255,255,255,255,255,255,255,255,255,187,51,0,0,0,0,0,0,0,0,0,0,0, 0,124,251,255,255,255,255,255,255,255,255,255,255,255,255,252,127,1,0,0,0,0,0,0,0,0,0,155,255,255,255,255, 250,184,126,100,100,126,184,250,255,255,255,255,159,1,0,0,0,0,0,0,0,126,255,255,255,255,170,27,0,0,0,0, 0,0,29,172,255,255,255,255,128,0,0,0,0,0,0,53,252,255,255,255,117,0,0,0,0,0,0,0,0,0,0,118, 255,255,255,252,53,0,0,0,0,0,191,255,255,255,157,0,0,0,0,0,0,0,0,0,0,0,0,158,255,255,255,190, 0,0,0,0,49,255,255,255,230,11,0,0,0,0,0,0,0,0,0,0,0,0,12,231,255,255,255,48,0,0,0,133, 255,255,255,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,131,255,255,255,133,0,0,0,192,255,255,255,55,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,55,255,255,255,191,0,0,0,232,255,255,255,10,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,11,255,255,255,231,0,0,0,249,255,255,245,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,246,255,255,248,0,0,0,248,255,255,246,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247, 255,255,247,0,0,0,231,255,255,255,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,255,255,255,230,0,0, 0,191,255,255,255,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,255,255,255,189,0,0,0,132,255,255,255, 133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,255,255,255,131,0,0,0,48,255,255,255,233,13,0,0,0, 0,0,0,0,0,0,0,0,0,14,233,255,255,255,47,0,0,0,0,191,255,255,255,161,0,0,0,0,0,0,0,0, 0,0,0,0,162,255,255,255,189,0,0,0,0,0,53,252,255,255,255,119,0,0,0,0,0,0,0,0,0,0,121,255, 255,255,252,53,0,0,0,0,0,0,127,255,255,255,255,170,27,0,0,0,0,0,0,28,173,255,255,255,255,129,0,0, 0,0,0,0,0,0,155,255,255,255,255,249,183,125,98,99,125,183,250,255,255,255,255,160,1,0,0,0,0,0,0,0, 0,0,125,251,255,255,255,255,255,255,255,255,255,255,255,255,252,128,1,0,0,0,0,0,0,0,0,0,0,0,49,187, 255,255,255,255,255,255,255,255,255,255,189,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,129,190,231,249, 249,231,190,130,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,5,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,129,243,16,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,7,112,235,255,255,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,94,224,255, 255,255,255,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,211,255,255,255,255,179,56,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,176,255,255,252,168,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 176,249,157,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,28,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,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,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,250,255,255,245,0,0,0, 0,0,0,0,0,0,0,6,255,255,255,233,0,0,0,0,231,255,255,255,14,0,0,0,0,0,0,0,0,0,31,255, 255,255,214,0,0,0,0,187,255,255,255,78,0,0,0,0,0,0,0,0,0,96,255,255,255,168,0,0,0,0,113,255, 255,255,199,4,0,0,0,0,0,0,0,9,214,255,255,255,91,0,0,0,0,17,240,255,255,255,171,15,0,0,0,0, 0,21,185,255,255,255,225,7,0,0,0,0,0,99,255,255,255,255,240,158,110,96,112,163,244,255,255,255,254,70,0,0, 0,0,0,0,0,131,255,255,255,255,255,255,255,255,255,255,255,255,252,101,0,0,0,0,0,0,0,0,0,85,228,255, 255,255,255,255,255,255,255,255,213,63,0,0,0,0,0,0,0,0,0,0,0,6,75,158,208,233,249,229,203,145,61,2, 0,0,0,0,0,0,0,0,0,0,0,0,65,224,229,74,0,0,65,224,229,74,0,0,0,0,0,0,0,0,0,0, 0,0,0,213,255,255,228,0,0,213,255,255,228,0,0,0,0,0,0,0,0,0,0,0,0,0,213,255,255,228,0,0, 213,255,255,228,0,0,0,0,0,0,0,0,0,0,0,0,0,67,226,230,76,0,0,67,226,230,76,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,240,0,0,0,0,0,0,0,0,0,0, 0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0, 255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240, 0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0, 0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255, 240,0,0,0,0,0,0,0,0,0,0,0,255,255,255,240,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0, 0,0,0,255,255,255,240,0,0,0,0,250,255,255,245,0,0,0,0,0,0,0,0,0,0,6,255,255,255,233,0,0, 0,0,231,255,255,255,14,0,0,0,0,0,0,0,0,0,31,255,255,255,214,0,0,0,0,187,255,255,255,78,0,0, 0,0,0,0,0,0,0,96,255,255,255,168,0,0,0,0,113,255,255,255,199,4,0,0,0,0,0,0,0,9,214,255, 255,255,91,0,0,0,0,17,240,255,255,255,171,15,0,0,0,0,0,21,185,255,255,255,225,7,0,0,0,0,0,99, 255,255,255,255,240,158,110,96,112,163,244,255,255,255,254,70,0,0,0,0,0,0,0,131,255,255,255,255,255,255,255,255, 255,255,255,255,252,101,0,0,0,0,0,0,0,0,0,85,228,255,255,255,255,255,255,255,255,255,213,63,0,0,0,0, 0,0,0,0,0,0,0,6,75,158,208,233,249,229,203,145,61,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,4,107,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75,215,255,136,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,187,255,255,255,136,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,24,155,253,255,255,255,207,58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,207,255,255,255,207,81, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,212,255,208,81,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,164,82,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,100,183,232,250,233,203,127,31,0,0,240, 255,255,255,0,0,0,0,0,0,0,55,225,255,255,255,255,255,255,255,247,119,1,240,255,255,255,0,0,0,0,0,0, 87,248,255,255,255,255,255,255,255,255,255,255,150,240,255,255,255,0,0,0,0,0,42,246,255,255,255,255,199,125,100,125, 201,255,255,255,255,255,255,255,0,0,0,0,1,207,255,255,255,238,75,0,0,0,0,0,80,240,255,255,255,255,255,0, 0,0,0,75,255,255,255,251,60,0,0,0,0,0,0,0,66,253,255,255,255,255,0,0,0,0,159,255,255,255,151,0, 0,0,0,0,0,0,0,0,158,255,255,255,255,0,0,0,0,213,255,255,255,54,0,0,0,0,0,0,0,0,0,59, 255,255,255,255,0,0,0,0,243,255,255,255,10,0,0,0,0,0,0,0,0,0,12,255,255,255,255,0,0,0,0,252, 255,255,247,0,0,0,0,0,0,0,0,0,0,0,247,255,255,255,0,0,0,0,241,255,255,255,14,0,0,0,0,0, 0,0,0,0,12,255,255,255,255,0,0,0,0,207,255,255,255,65,0,0,0,0,0,0,0,0,0,60,255,255,255,255, 0,0,0,0,148,255,255,255,165,0,0,0,0,0,0,0,0,0,158,255,255,255,255,0,0,0,0,62,255,255,255,254, 73,0,0,0,0,0,0,0,66,253,255,255,255,255,0,0,0,0,0,193,255,255,255,243,87,0,0,0,0,0,79,240, 255,255,255,255,255,0,0,0,0,0,32,242,255,255,255,255,205,126,100,125,200,255,255,255,255,255,255,255,0,0,0,0, 0,0,76,245,255,255,255,255,255,255,255,255,255,255,134,240,255,255,255,0,0,0,0,0,0,0,48,222,255,255,255,255, 255,255,255,243,106,0,240,255,255,255,0,0,0,0,0,0,0,0,7,100,184,234,251,232,199,119,24,0,0,240,255,255, 255,0,0,0,0,0,0,0,0,1,73,159,215,244,251,236,200,141,47,0,0,0,0,0,0,0,0,0,47,205,255,255, 255,255,255,255,255,255,255,167,18,0,0,0,0,0,0,70,245,255,255,255,255,255,255,255,255,255,255,255,216,25,0,0, 0,0,40,243,255,255,255,251,171,112,100,128,206,255,255,255,255,196,2,0,0,0,191,255,255,255,220,41,0,0,0,0, 0,99,252,255,255,255,92,0,0,58,255,255,255,243,35,0,0,0,0,0,0,0,128,255,255,255,200,0,0,145,255,255, 255,137,0,0,0,0,0,0,0,0,10,80,80,80,79,1,0,204,255,255,255,52,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,239,255,255,255,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,251,255,255,244,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,235,255,255,251,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,210,255,255,255,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,255,255,255,91,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,80,255,255,255,200,1,0,0,0,0,0,0,0,58,212,212,212,192,1,0,8,227,255, 255,255,130,0,0,0,0,0,0,14,206,255,255,255,144,0,0,0,76,255,255,255,255,168,46,0,0,5,77,209,255,255, 255,240,25,0,0,0,0,133,255,255,255,255,255,243,231,253,255,255,255,255,253,83,0,0,0,0,0,0,113,250,255,255, 255,255,255,255,255,255,255,245,89,0,0,0,0,0,0,0,0,42,160,242,255,255,255,255,255,242,158,34,0,0,0,0, 0,0,0,0,0,0,0,5,47,255,255,227,49,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,228, 55,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,247,137,4,0,0,0,0,0,0,0,0, 0,0,0,0,0,255,255,255,255,255,255,138,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,111,255,255,231, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,255,255,248,0,0,0,0,0,0,0,0,0,0,0,38, 40,40,40,51,144,255,255,199,0,0,0,0,0,0,0,0,0,0,0,248,255,255,255,255,255,255,248,61,0,0,0,0, 0,0,0,0,0,0,0,248,255,255,255,253,223,164,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 15,135,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,111,236,255,92,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,88,221,255,255,255,92,0,0,0,0,0,0,0,0,0,0,0,0,0,64,203,255,255,255,255, 183,35,0,0,0,0,0,0,0,0,0,0,0,0,51,255,255,255,254,176,52,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,56,255,253,170,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,163,40,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,26,121,190,233,250,245,219,166,81,2,0,0,0,0,0,0,0,0,0,1,121,249,255,255,255,255,255,255,255,255, 211,44,0,0,0,0,0,0,0,8,183,255,255,255,255,255,255,255,255,255,255,255,246,83,0,0,0,0,0,0,151,255, 255,255,255,209,129,98,106,151,234,255,255,255,245,43,0,0,0,0,75,255,255,255,250,101,0,0,0,0,0,9,149,255, 255,255,210,1,0,0,0,199,255,255,255,105,0,0,0,0,0,0,0,0,165,255,255,255,74,0,0,28,255,255,255,224, 2,0,0,0,0,0,0,0,0,46,255,255,255,155,0,0,84,255,255,255,198,92,92,92,92,92,92,92,92,92,97,255, 255,255,206,0,0,115,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,230,0,0,124,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,233,0,0,113,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,216,0,0,80,255,255,255,160,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,254,255,255,228,3,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,190,255,255,255,115,0,0,0,0,0,0,0,6,177,204,204,204, 58,0,0,0,64,254,255,255,253,114,2,0,0,0,0,11,169,255,255,255,219,4,0,0,0,0,135,255,255,255,255,218, 136,100,106,146,232,255,255,255,252,66,0,0,0,0,0,3,166,255,255,255,255,255,255,255,255,255,255,255,252,98,0,0, 0,0,0,0,0,0,105,244,255,255,255,255,255,255,255,255,222,68,0,0,0,0,0,0,0,0,0,0,20,114,187,232, 250,246,223,172,91,7,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,54,193,128,0,0,0, 0,33,167,254,255,128,0,0,17,140,249,255,255,255,127,0,114,238,255,255,255,235,122,13,0,255,255,255,233,118,11,0, 0,0,255,230,114,10,0,0,0,0,0,109,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255, 255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255, 240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,17,160,240,245,169,35,0,0,200,248,100,0,0,0,0,0,0,0,0,0,0,1,190,255,255,255,255,247, 165,183,255,254,36,0,0,0,0,0,0,0,0,0,0,70,255,251,148,158,248,255,255,255,255,152,0,0,0,0,0,0, 0,0,0,0,0,126,236,147,0,0,36,172,240,222,130,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,255,255,255,240,0,8,105,188,232,248,225,188,99,12,0,0,0,0,0,0,0,0,255,255,255,240,54,224, 255,255,255,255,255,255,255,225,58,0,0,0,0,0,0,0,255,255,255,250,243,255,255,255,255,255,255,255,255,255,250,54, 0,0,0,0,0,0,255,255,255,255,255,255,199,121,97,119,197,255,255,255,255,222,8,0,0,0,0,0,255,255,255,255, 255,109,0,0,0,0,0,105,255,255,255,255,88,0,0,0,0,0,255,255,255,255,162,0,0,0,0,0,0,0,161,255, 255,255,177,0,0,0,0,0,255,255,255,255,56,0,0,0,0,0,0,0,56,255,255,255,216,0,0,0,0,0,255,255, 255,254,5,0,0,0,0,0,0,0,5,254,255,255,243,0,0,0,0,0,255,255,255,242,0,0,0,0,0,0,0,0, 0,242,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0, 255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0, 0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0, 0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0, 0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0, 0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0, 0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255, 255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,8,119,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,93,226,255,255,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,67,206,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,37,182,255,255,255,255,186,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,255,255,255,183,56,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,124,255,181,54,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,70,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,145,205,240,252,240,205,144,52,0,0,0,0,0,0,0,0, 0,0,0,20,178,255,255,255,255,255,255,255,255,255,175,19,0,0,0,0,0,0,0,0,51,229,255,255,255,255,255,255, 255,255,255,255,255,228,51,0,0,0,0,0,0,20,227,255,255,255,254,190,122,100,125,199,255,255,255,255,226,20,0,0, 0,0,0,180,255,255,255,228,58,0,0,0,0,0,72,235,255,255,255,180,0,0,0,0,55,255,255,255,247,44,0,0, 0,0,0,0,0,55,250,255,255,255,55,0,0,0,147,255,255,255,139,0,0,0,0,0,0,0,0,0,149,255,255,255, 146,0,0,0,207,255,255,255,49,0,0,0,0,0,0,0,0,0,53,255,255,255,206,0,0,0,241,255,255,255,9,0, 0,0,0,0,0,0,0,0,11,255,255,255,240,0,0,0,252,255,255,244,0,0,0,0,0,0,0,0,0,0,0,247, 255,255,252,0,0,0,241,255,255,254,3,0,0,0,0,0,0,0,0,0,12,255,255,255,239,0,0,0,208,255,255,255, 45,0,0,0,0,0,0,0,0,0,58,255,255,255,203,0,0,0,150,255,255,255,129,0,0,0,0,0,0,0,0,0, 156,255,255,255,140,0,0,0,62,255,255,255,240,33,0,0,0,0,0,0,0,65,252,255,255,254,45,0,0,0,0,190, 255,255,255,220,44,0,0,0,0,0,84,240,255,255,255,162,0,0,0,0,0,26,234,255,255,255,252,173,111,99,129,207, 255,255,255,255,212,11,0,0,0,0,0,0,60,234,255,255,255,255,255,255,255,255,255,255,255,214,35,0,0,0,0,0, 0,0,0,26,189,255,255,255,255,255,255,255,255,253,151,9,0,0,0,0,0,0,0,0,0,0,0,63,153,212,244,252, 237,197,131,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,6,117,241,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,82,221,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,192,255,255,255,255,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,18,157,253,255,255,255,191,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72, 255,255,255,193,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,255,197,68,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,43,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,240,0,0,0,0,0,0,0,0,0, 240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255, 255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0, 0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0, 0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0, 0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0, 0,0,0,255,255,255,240,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,240,0,0,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,242,0,0,0,0,0,0,0,0,0,242,255,255,255, 0,0,0,0,0,243,255,255,254,5,0,0,0,0,0,0,0,6,254,255,255,255,0,0,0,0,0,217,255,255,255,55, 0,0,0,0,0,0,0,57,255,255,255,255,0,0,0,0,0,177,255,255,255,160,0,0,0,0,0,0,0,163,255,255, 255,255,0,0,0,0,0,87,255,255,255,255,104,0,0,0,0,0,109,255,255,255,255,255,0,0,0,0,0,8,221,255, 255,255,255,196,118,97,119,199,255,255,255,255,255,255,0,0,0,0,0,0,51,249,255,255,255,255,255,255,255,255,255,240, 250,255,255,255,0,0,0,0,0,0,0,56,224,255,255,255,255,255,255,255,222,50,240,255,255,255,0,0,0,0,0,0, 0,0,12,100,187,225,249,233,189,106,7,0,240,255,255,255,0,0,0,0,0,0,0,0,0,76,230,224,63,0,0,76, 230,224,63,0,0,0,0,0,0,0,0,0,0,0,0,229,255,255,212,0,0,229,255,255,212,0,0,0,0,0,0,0, 0,0,0,0,0,229,255,255,212,0,0,229,255,255,212,0,0,0,0,0,0,0,0,0,0,0,0,78,230,225,65,0, 0,78,230,225,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,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,0,0,0, 0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255, 0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,255,0, 0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,240,255, 255,255,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255, 255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0, 240,255,255,255,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255, 255,255,255,0,0,0,0,0,0,0,0,0,240,255,255,255,0,0,0,0,0,255,255,255,255,2,0,0,0,0,0,0, 0,0,242,255,255,255,0,0,0,0,0,243,255,255,255,19,0,0,0,0,0,0,0,6,254,255,255,255,0,0,0,0, 0,217,255,255,255,70,0,0,0,0,0,0,0,57,255,255,255,255,0,0,0,0,0,177,255,255,255,174,0,0,0,0, 0,0,0,163,255,255,255,255,0,0,0,0,0,87,255,255,255,255,115,0,0,0,0,0,108,255,255,255,255,255,0,0, 0,0,0,8,221,255,255,255,255,201,120,97,119,197,255,255,255,255,255,255,0,0,0,0,0,0,51,249,255,255,255,255, 255,255,255,255,255,240,250,255,255,255,0,0,0,0,0,0,0,56,224,255,255,255,255,255,255,255,222,50,240,255,255,255, 0,0,0,0,0,0,0,0,12,100,187,225,249,233,189,106,7,0,240,255,255,255,0,0,0,0,0,0,92,92,92,92, 92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0, 0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,205,255,248,9,0,0,0,64,255,255,191,0,0,0,0,179,255,255,125,0,0,0,38,254,255,255,58, 0,0,0,152,255,255,243,4,0,0,19,246,255,255,181,0,0,0,125,255,255,255,115,0,0,6,231,255,255,255,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,5,244,255,255,254,40,0,0,60,255,255,255,180,0,0,0,126,255,255,255,66,0,0,0,192,255,255,208,0, 0,0,8,248,255,255,94,0,0,0,68,255,255,230,6,0,0,0,134,255,255,122,0,0,0,0,199,255,245,18,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,205,255,248,9,0,0,174,255,255,34,0,0,0,64,255,255,191,0,0,34,253,255,223,0,0,0,0, 179,255,255,125,0,0,147,255,255,157,0,0,0,38,254,255,255,58,0,16,244,255,255,90,0,0,0,152,255,255,243,4, 0,120,255,255,255,24,0,0,19,246,255,255,181,0,5,228,255,255,213,0,0,0,125,255,255,255,115,0,93,255,255,255, 147,0,0,6,231,255,255,255,49,0,206,255,255,255,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,244,255,255,254,40,0,218,255,255,255,70,0, 0,60,255,255,255,180,0,28,255,255,255,211,0,0,0,126,255,255,255,66,0,95,255,255,255,97,0,0,0,192,255,255, 208,0,0,161,255,255,231,7,0,0,8,248,255,255,94,0,0,227,255,255,124,0,0,0,68,255,255,230,6,0,38,255, 255,246,19,0,0,0,134,255,255,122,0,0,105,255,255,151,0,0,0,0,199,255,245,18,0,0,171,255,254,38,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,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,190,243,191,33,0,0,33,190,243,192,33,0,0,33,190,243,191,33,0,0,0,191,255,255,255,190,0,0, 191,255,255,255,190,0,0,191,255,255,255,190,0,0,0,244,255,255,255,243,0,0,244,255,255,255,243,0,0,244,255,255, 255,243,0,0,0,192,255,255,255,191,0,0,192,255,255,255,191,0,0,192,255,255,255,191,0,0,0,33,191,244,192,33, 0,0,33,191,244,192,33,0,0,33,191,244,192,33,0, }; static std::uint32_t decodeUtf8Codepoint(const char*& textValue) { const unsigned char first = static_cast(*textValue++); if (first < 0x80u) return first; if ((first & 0xe0u) == 0xc0u && *textValue != '\0') { const unsigned char second = static_cast(*textValue++); return ((first & 0x1fu) << 6) | (second & 0x3fu); } if ((first & 0xf0u) == 0xe0u && textValue[0] != '\0' && textValue[1] != '\0') { const unsigned char second = static_cast(*textValue++); const unsigned char third = static_cast(*textValue++); return ((first & 0x0fu) << 12) | ((second & 0x3fu) << 6) | (third & 0x3fu); } return static_cast('?'); } // dos tipografias: Carlito para todo, que es estrecha y se lee bien a 19 px, // y Poppins Medium solo para los titulos grandes, que es geometrica y pega con // las esquinas redondeadas del rediseño. mezclar display y texto es lo normal static const UiGlyph* findUiGlyph(std::uint32_t codepoint, UiFace face) { const UiGlyph* table = face == UiFace::Display ? UI_GLYPHS_DISPLAY : UI_GLYPHS; const std::size_t count = face == UiFace::Display ? sizeof(UI_GLYPHS_DISPLAY) / sizeof(UI_GLYPHS_DISPLAY[0]) : sizeof(UI_GLYPHS) / sizeof(UI_GLYPHS[0]); for (std::size_t index = 0; index < count; ++index) { if (table[index].codepoint == codepoint) return &table[index]; } for (std::size_t index = 0; index < count; ++index) { if (table[index].codepoint == static_cast('?')) return &table[index]; } return nullptr; } static const std::uint8_t* uiFaceAlpha(UiFace face) { return face == UiFace::Display ? UI_FONT_ALPHA_DISPLAY : UI_FONT_ALPHA; } static int uiFaceLineHeight(UiFace face) { return face == UiFace::Display ? UI_FONT_LINE_HEIGHT_DISPLAY : UI_FONT_LINE_HEIGHT; } static int uiFaceAscent(UiFace face) { return face == UiFace::Display ? UI_FONT_ASCENT_DISPLAY : UI_FONT_ASCENT; } static std::uint32_t blendDisplayColor( std::uint32_t destination, std::uint32_t source, unsigned alpha) { if (alpha >= 255u) return source; if (alpha == 0u) return destination; const unsigned inverse = 255u - alpha; const unsigned dr = (destination >> 16) & 0xffu; const unsigned dg = (destination >> 8) & 0xffu; const unsigned db = destination & 0xffu; const unsigned sr = (source >> 16) & 0xffu; const unsigned sg = (source >> 8) & 0xffu; const unsigned sb = source & 0xffu; return 0x80000000u | (((dr * inverse + sr * alpha) / 255u) << 16) | (((dg * inverse + sg * alpha) / 255u) << 8) | ((db * inverse + sb * alpha) / 255u); } static int uiTextWidth(const char* value, int pixelHeight, UiFace face = UiFace::Body) { if (value == nullptr || pixelHeight <= 0) return 0; const double scale = static_cast(pixelHeight) / static_cast(uiFaceLineHeight(face)); double width = 0.0; const char* cursor = value; while (*cursor != '\0') { const UiGlyph* glyph = findUiGlyph(decodeUtf8Codepoint(cursor), face); if (glyph != nullptr) width += (static_cast(glyph->advance64) / 64.0) * scale; } return static_cast(width + 0.5); } static void drawUiText( std::uint32_t* output, int x, int y, int pixelHeight, std::uint32_t color, const char* value, int maxWidth = 0, unsigned opacity = 255u, UiFace face = UiFace::Body) { if (output == nullptr || value == nullptr || pixelHeight <= 0 || opacity == 0u) return; const double scale = static_cast(pixelHeight) / static_cast(uiFaceLineHeight(face)); const int baseline = y + static_cast(static_cast(uiFaceAscent(face)) * scale + 0.5); int cursorX = x; const char* cursor = value; while (*cursor != '\0') { const UiGlyph* glyph = findUiGlyph(decodeUtf8Codepoint(cursor), face); if (glyph == nullptr) continue; const int advance = static_cast((static_cast(glyph->advance64) / 64.0) * scale + 0.5); if (maxWidth > 0 && cursorX + advance > x + maxWidth) break; const int targetWidth = glyph->width > 0 ? std::max(1, static_cast(static_cast(glyph->width) * scale + 0.5)) : 0; const int targetHeight = glyph->height > 0 ? std::max(1, static_cast(static_cast(glyph->height) * scale + 0.5)) : 0; const int drawX = cursorX + static_cast(static_cast(glyph->bearingX) * scale + 0.5); const int drawY = baseline + static_cast(static_cast(glyph->bearingY) * scale + 0.5); for (int dy = 0; dy < targetHeight; ++dy) { const int py = drawY + dy; if (py < 0 || py >= OUTPUT_HEIGHT) continue; const int sourceY = dy * glyph->height / targetHeight; for (int dx = 0; dx < targetWidth; ++dx) { const int px = drawX + dx; if (px < 0 || px >= OUTPUT_WIDTH) continue; const int sourceX = dx * glyph->width / targetWidth; const unsigned glyphAlpha = uiFaceAlpha(face)[ glyph->offset + static_cast(sourceY * glyph->width + sourceX) ]; const unsigned alphaValue = glyphAlpha * opacity / 255u; if (alphaValue == 0u) continue; std::uint32_t& destination = output[ static_cast(py) * OUTPUT_WIDTH + px ]; destination = blendDisplayColor(destination, color, alphaValue); } } cursorX += advance; } } static void blendRectangle( std::uint32_t* output, int x, int y, int width, int height, std::uint32_t color, unsigned opacity) { if (output == nullptr || width <= 0 || height <= 0 || opacity == 0u) return; const int left = x < 0 ? 0 : x; const int top = y < 0 ? 0 : y; const int right = x + width > OUTPUT_WIDTH ? OUTPUT_WIDTH : x + width; const int bottom = y + height > OUTPUT_HEIGHT ? OUTPUT_HEIGHT : y + height; for (int py = top; py < bottom; ++py) { std::uint32_t* row = output + static_cast(py) * OUTPUT_WIDTH; for (int px = left; px < right; ++px) row[px] = blendDisplayColor(row[px], color, opacity); } } static void darkenFrame(std::uint32_t* output, unsigned amount) { blendRectangle(output, 0, 0, OUTPUT_WIDTH, OUTPUT_HEIGHT, encodeDisplayColor(5, 10, 22), amount); } static int asciiCaseCompare(const char* left, const char* right) { while (*left != '\0' && *right != '\0') { char leftValue = *left; char rightValue = *right; if (leftValue >= 'a' && leftValue <= 'z') { leftValue = static_cast(leftValue - 'a' + 'A'); } if (rightValue >= 'a' && rightValue <= 'z') { rightValue = static_cast(rightValue - 'a' + 'A'); } if (leftValue != rightValue) { return static_cast(leftValue) - static_cast(rightValue); } ++left; ++right; } return static_cast(*left) - static_cast(*right); } static int compareGameEntries(const void* left, const void* right) { const GameEntry* leftEntry = static_cast(left); const GameEntry* rightEntry = static_cast(right); return asciiCaseCompare(leftEntry->name, rightEntry->name); } static bool supportedGameExtension(const char* name) { if (name == nullptr) { return false; } const char* dot = std::strrchr(name, '.'); if (dot == nullptr || dot[1] == '\0') { return false; } static const char* extensions[] = { "d88", "88d", "d98", "98d", "fdi", "xdf", "hdm", "dup", "2hd", "tfd", "nfd", "hd4", "hd5", "hd9", "fdd", "h01", "hdb", "ddb", "dd6", "dcp", "dcu", "flp", "img", "ima", "bin", "fim", "thd", "nhd", "hdi", "vhd", "slh", "hdn", "cmd" }; for (const char* extension : extensions) { if (asciiCaseCompare(dot + 1, extension) == 0) { return true; } } return false; } static void scanGameDirectory( const char* directoryPath, const char* relativePrefix, unsigned depth) { if (directoryPath == nullptr || relativePrefix == nullptr || depth > 4 || g_gameEntryCount >= MAX_GAME_ENTRIES) { return; } DIR* directory = opendir(directoryPath); if (directory == nullptr) { std::printf( "[NP2kai/library] opendir failed: %s errno=%d\n", directoryPath, errno ); return; } for (;;) { dirent* entry = readdir(directory); if (entry == nullptr) { break; } if (std::strcmp(entry->d_name, ".") == 0 || std::strcmp(entry->d_name, "..") == 0) { continue; } char fullPath[MAX_GAME_PATH] {}; char relativePath[MAX_GAME_NAME] {}; const int fullLength = std::snprintf( fullPath, sizeof(fullPath), "%s/%s", directoryPath, entry->d_name ); const int relativeLength = std::snprintf( relativePath, sizeof(relativePath), "%s%s", relativePrefix, entry->d_name ); if (fullLength <= 0 || static_cast(fullLength) >= sizeof(fullPath) || relativeLength <= 0 || static_cast(relativeLength) >= sizeof(relativePath)) { continue; } struct stat status {}; if (stat(fullPath, &status) != 0) { continue; } if (S_ISDIR(status.st_mode)) { char childPrefix[MAX_GAME_NAME] {}; const int prefixLength = std::snprintf( childPrefix, sizeof(childPrefix), "%s/", relativePath ); if (prefixLength > 0 && static_cast(prefixLength) < sizeof(childPrefix)) { scanGameDirectory( fullPath, childPrefix, depth + 1 ); } continue; } if (!S_ISREG(status.st_mode) || !supportedGameExtension(entry->d_name) || g_gameEntryCount >= MAX_GAME_ENTRIES) { continue; } GameEntry& game = g_gameEntries[g_gameEntryCount++]; std::snprintf(game.name, sizeof(game.name), "%s", relativePath); std::snprintf(game.path, sizeof(game.path), "%s", fullPath); game.size = static_cast(status.st_size); game.modifiedTime = static_cast(status.st_mtime); } closedir(directory); } static void refreshGameLibrary() { g_gameEntryCount = 0; g_libraryScanFailed = false; g_libraryWasTruncated = false; DIR* rootDirectory = opendir(CONTENT_DIRECTORY); if (rootDirectory == nullptr) { g_libraryScanFailed = true; char detail[768] {}; std::snprintf(detail, sizeof(detail), "%s (errno=%d)", CONTENT_DIRECTORY, errno); setLastFailure("NP2-2201", "No se puede abrir la carpeta de juegos", "The games folder cannot be opened", "Comprueba que la carpeta exista y sea accesible.", "Make sure the folder exists and is accessible.", detail); std::snprintf(g_libraryStatus, sizeof(g_libraryStatus), "%s", "LIBRARY READ ERROR"); return; } closedir(rootDirectory); scanGameDirectory(CONTENT_DIRECTORY, "", 0); if (g_gameEntryCount >= MAX_GAME_ENTRIES) g_libraryWasTruncated = true; if (g_gameEntryCount > 1) { std::qsort( g_gameEntries, g_gameEntryCount, sizeof(GameEntry), compareGameEntries ); } if (g_gameEntryCount == 0) { g_librarySelection = 0; g_libraryScroll = 0; std::snprintf( g_libraryStatus, sizeof(g_libraryStatus), "NO SE ENCONTRARON JUEGOS" ); } else { if (g_librarySelection >= g_gameEntryCount) { g_librarySelection = g_gameEntryCount - 1; } if (g_librarySelection < g_libraryScroll) { g_libraryScroll = g_librarySelection; } std::snprintf( g_libraryStatus, sizeof(g_libraryStatus), "%u JUEGO%s ENCONTRADO%s", g_gameEntryCount, g_gameEntryCount == 1 ? "" : "S", g_gameEntryCount == 1 ? "" : "S" ); } std::printf( "[NP2kai/library] scan complete: %u game(s) in %s\n", g_gameEntryCount, CONTENT_DIRECTORY ); } static void formatGameSize(long bytes, char* destination, std::size_t capacity) { if (destination == nullptr || capacity == 0) { return; } if (bytes < 0) { std::snprintf(destination, capacity, "DESCONOCIDO"); } else if (bytes >= 1024L * 1024L) { const long tenths = bytes * 10L / (1024L * 1024L); std::snprintf( destination, capacity, "%ld.%ld MB", tenths / 10L, tenths % 10L ); } else if (bytes >= 1024L) { std::snprintf(destination, capacity, "%ld KB", bytes / 1024L); } else { std::snprintf(destination, capacity, "%ld B", bytes); } } static void drawOutlineRectangle( std::uint32_t* output, int x, int y, int width, int height, int thickness, std::uint32_t color) { if (thickness <= 0) { return; } fillRectangle(output, x, y, width, thickness, color); fillRectangle( output, x, y + height - thickness, width, thickness, color ); fillRectangle(output, x, y, thickness, height, color); fillRectangle( output, x + width - thickness, y, thickness, height, color ); } // dibuja el recuadro del foco persiguiendo su sitio. cada pantalla usa su // propia ranura para no pisarse la posicion con las demas static void uiDrawMovingFocus(std::uint32_t* output, float& state, int x, int targetY, int width, int height, std::uint32_t fill, std::uint32_t outline) { if (state < 0.0f) state = static_cast(targetY); state = uiApproach(state, static_cast(targetY), UI_TAU_FOCUS); const int y = static_cast(state < 0.0f ? state - 0.5f : state + 0.5f); fillRectangle(output, x, y, width, height, fill); drawOutlineRectangle(output, x, y, width, height, 3, outline); } static void fillCircle( std::uint32_t* output, int centerX, int centerY, int radius, std::uint32_t color) { if (output == nullptr || radius <= 0) { return; } const int radiusSquared = radius * radius; for (int offsetY = -radius; offsetY <= radius; ++offsetY) { for (int offsetX = -radius; offsetX <= radius; ++offsetX) { if (offsetX * offsetX + offsetY * offsetY <= radiusSquared) { fillRectangle( output, centerX + offsetX, centerY + offsetY, 1, 1, color ); } } } } static void drawCircleRing( std::uint32_t* output, int centerX, int centerY, int radius, int thickness, std::uint32_t color) { if (output == nullptr || radius <= 0 || thickness <= 0) { return; } const int outerSquared = radius * radius; const int innerRadius = radius - thickness; const int innerSquared = innerRadius > 0 ? innerRadius * innerRadius : 0; for (int offsetY = -radius; offsetY <= radius; ++offsetY) { for (int offsetX = -radius; offsetX <= radius; ++offsetX) { const int distanceSquared = offsetX * offsetX + offsetY * offsetY; if (distanceSquared <= outerSquared && distanceSquared >= innerSquared) { fillRectangle( output, centerX + offsetX, centerY + offsetY, 1, 1, color ); } } } } static void drawTriangleIcon( std::uint32_t* output, int centerX, int centerY, int radius, std::uint32_t color) { for (int row = 0; row < radius * 2; ++row) { const int halfWidth = row / 2; const int y = centerY - radius + row; fillRectangle( output, centerX - halfWidth, y, halfWidth * 2 + 1, 1, color ); } fillCircle( output, centerX, centerY + radius - 2, radius - 4, encodeDisplayColor(47, 59, 82) ); } // --- paleta ---------------------------------------------------------------- // antes cada pantalla se inventaba sus colores a pelo, 119 llamadas sueltas // repartidas por el archivo. asi era imposible cambiar el aspecto sin ir // tocando de una en una. ahora hay una sola paleta con nombres por funcion y // los temas solo cambian esos numeros enum class UiTheme { Grey, Slate, Graphite, Midnight }; struct UiPalette { // fondo std::uint8_t topR, topG, topB; // arriba del degradado std::uint8_t bottomR, bottomG, bottomB; // abajo std::uint8_t sheenR, sheenG, sheenB; // la banda diagonal clara std::uint8_t washR, washG, washB; // la cuña oscura de abajo // contenido std::uint32_t textPrimary; std::uint32_t textMuted; std::uint32_t textDim; std::uint32_t line; std::uint32_t panel; std::uint32_t focusFill; std::uint32_t focusOutline; std::uint32_t good; std::uint32_t warn; std::uint32_t bad; }; static UiTheme g_uiTheme = UiTheme::Grey; static UiPalette g_ui {}; static void applyUiTheme() { switch (g_uiTheme) { default: case UiTheme::Grey: g_ui = { 66, 68, 72, 26, 27, 29, 126, 130, 136, 16, 17, 18, encodeDisplayColor(246, 247, 249), encodeDisplayColor(201, 204, 209), encodeDisplayColor(150, 154, 161), encodeDisplayColor(104, 108, 115), encodeDisplayColor(72, 75, 80), encodeDisplayColor(126, 130, 137), encodeDisplayColor(252, 252, 253), encodeDisplayColor(150, 219, 158), encodeDisplayColor(232, 196, 106), encodeDisplayColor(238, 148, 148) }; break; case UiTheme::Slate: g_ui = { 62, 72, 92, 24, 29, 40, 122, 134, 158, 14, 18, 26, encodeDisplayColor(240, 244, 251), encodeDisplayColor(198, 209, 227), encodeDisplayColor(144, 156, 178), encodeDisplayColor(100, 112, 136), encodeDisplayColor(68, 80, 104), encodeDisplayColor(120, 135, 165), encodeDisplayColor(252, 253, 255), encodeDisplayColor(150, 219, 158), encodeDisplayColor(232, 196, 106), encodeDisplayColor(238, 148, 148) }; break; case UiTheme::Graphite: g_ui = { 52, 54, 57, 20, 21, 22, 104, 108, 114, 12, 13, 14, encodeDisplayColor(240, 241, 243), encodeDisplayColor(178, 181, 186), encodeDisplayColor(128, 131, 137), encodeDisplayColor(104, 107, 113), encodeDisplayColor(56, 58, 62), encodeDisplayColor(104, 108, 115), encodeDisplayColor(250, 250, 251), encodeDisplayColor(140, 208, 148), encodeDisplayColor(226, 188, 100), encodeDisplayColor(232, 140, 140) }; break; case UiTheme::Midnight: g_ui = { 44, 49, 70, 16, 18, 26, 98, 106, 138, 9, 10, 16, encodeDisplayColor(236, 239, 248), encodeDisplayColor(176, 186, 210), encodeDisplayColor(126, 136, 162), encodeDisplayColor(96, 106, 136), encodeDisplayColor(46, 52, 74), encodeDisplayColor(94, 105, 140), encodeDisplayColor(250, 251, 255), encodeDisplayColor(146, 214, 156), encodeDisplayColor(230, 192, 104), encodeDisplayColor(236, 146, 146) }; break; } g_uiBackgroundCacheReady = false; // el fondo hay que volver a pintarlo } static const char* uiThemeConfigName() { switch (g_uiTheme) { case UiTheme::Slate: return "slate"; case UiTheme::Graphite: return "graphite"; case UiTheme::Midnight: return "midnight"; default: return "grey"; } } static void renderBackgroundInto(std::uint32_t* output) { if (output == nullptr) return; // el fondo de antes era una rampa fuerte de arriba abajo con dos diagonales // de borde duro encima. las diagonales se veian como pegatinas. ahora: // degradado suave, una banda de luz con caida gradual y una cuña oscura // abajo a la derecha que se difumina en vez de cortar en seco for (int y = 0; y < OUTPUT_HEIGHT; ++y) { // curva en vez de recta: se queda mas rato en el tono claro de arriba y // cae mas rapido al final, que es lo que hace que no parezca un degradado // de programa de dibujo const int lineal = y * 255 / (OUTPUT_HEIGHT - 1); const int mezcla = (lineal * lineal) / 255; const std::uint8_t red = static_cast( g_ui.topR + (g_ui.bottomR - g_ui.topR) * mezcla / 255); const std::uint8_t green = static_cast( g_ui.topG + (g_ui.bottomG - g_ui.topG) * mezcla / 255); const std::uint8_t blue = static_cast( g_ui.topB + (g_ui.bottomB - g_ui.topB) * mezcla / 255); fillRectangle(output, 0, y, OUTPUT_WIDTH, 1, encodeDisplayColor(red, green, blue)); } const std::uint32_t sheen = encodeDisplayColor(g_ui.sheenR, g_ui.sheenG, g_ui.sheenB); const std::uint32_t wash = encodeDisplayColor(g_ui.washR, g_ui.washG, g_ui.washB); // banda de luz diagonal. el ancho y la transparencia van cambiando con la // altura, y los bordes se desvanecen a los lados en vez de cortar for (int y = 0; y < OUTPUT_HEIGHT; ++y) { const int centro = 250 + y * 62 / 100; const int ancho = 150 + y * 30 / 100; const int borde = ancho / 2; // se apaga segun baja // floja a proposito: si se nota, tapa texto const int fuerza = 16 - y * 12 / OUTPUT_HEIGHT; if (fuerza <= 0) continue; for (int offset = -borde; offset <= borde; ++offset) { const int px = centro + offset; if (px < 0 || px >= OUTPUT_WIDTH) continue; const int distancia = offset < 0 ? -offset : offset; // caida suave hacia los lados const int caida = 255 - distancia * 255 / borde; const unsigned alpha = static_cast(fuerza * caida / 255); if (alpha == 0) continue; std::uint32_t* pixel = output + static_cast(y) * OUTPUT_WIDTH + px; *pixel = blendDisplayColor(*pixel, sheen, alpha); } } // cuña oscura abajo a la derecha, tambien difuminada for (int y = OUTPUT_HEIGHT / 2; y < OUTPUT_HEIGHT; ++y) { const int inicio = 1180 - (y - OUTPUT_HEIGHT / 2) * 85 / 100; const int profundidad = (y - OUTPUT_HEIGHT / 2) * 255 / (OUTPUT_HEIGHT / 2); for (int px = inicio; px < OUTPUT_WIDTH; ++px) { if (px < 0) continue; const int entrada = px - inicio; const int suave = entrada < 220 ? entrada * 255 / 220 : 255; const unsigned alpha = static_cast(profundidad * suave / 255 * 34 / 255); if (alpha == 0) continue; std::uint32_t* pixel = output + static_cast(y) * OUTPUT_WIDTH + px; *pixel = blendDisplayColor(*pixel, wash, alpha); } } // viñeta: oscurece un pelin las esquinas para que el contenido del centro // resalte. es lo que mas cambia la sensacion de "esto esta acabado" for (int y = 0; y < OUTPUT_HEIGHT; ++y) { const int dy = y - OUTPUT_HEIGHT / 2; const int normY = dy * 100 / (OUTPUT_HEIGHT / 2); for (int x = 0; x < OUTPUT_WIDTH; x += 1) { const int dx = x - OUTPUT_WIDTH / 2; const int normX = dx * 100 / (OUTPUT_WIDTH / 2); const int radio = (normX * normX + normY * normY) / 100; if (radio <= 62) continue; unsigned alpha = static_cast((radio - 62) * 30 / 100); if (alpha > 26u) alpha = 26u; std::uint32_t* pixel = output + static_cast(y) * OUTPUT_WIDTH + x; *pixel = blendDisplayColor(*pixel, encodeDisplayColor(0, 0, 0), alpha); } } } // el degradado y los adornos son siempre iguales, asi que los pinto una sola // vez y a partir de ahi cada pantalla empieza copiando esa imagen. era lo mas // caro de todo con diferencia: 1080 filas de 1920 px recalculadas cada vez static void drawPS4StyleBackground(std::uint32_t* output) { if (output == nullptr) return; if (g_uiBackgroundCache != nullptr) { if (!g_uiBackgroundCacheReady) { renderBackgroundInto(g_uiBackgroundCache); g_uiBackgroundCacheReady = true; } std::memcpy(output, g_uiBackgroundCache, UI_CANVAS_BYTES); return; } renderBackgroundInto(output); } static bool interfaceIsEnglish() { return g_interfaceLanguage == InterfaceLanguage::English; } static const char* tr(const char* spanish, const char* english) { return interfaceIsEnglish() ? english : spanish; } static const char* externalKeyboardStatusText() { if (g_externalKeyboardDetected) { return tr("Teclado USB: detectado", "USB keyboard: detected"); } if (!g_nativeKeyboardOpened) { if (ps4KeyboardInitResult() != 0) { return tr("Teclado USB: sceKeyboardInit no pudo iniciar el módulo", "USB keyboard: sceKeyboardInit could not start the module"); } return tr("Teclado USB: sceKeyboardOpen no encuentra el dispositivo", "USB keyboard: sceKeyboardOpen cannot find the device"); } return tr("Teclado USB: abierto; pulsa una tecla", "USB keyboard: opened; press a key"); } static void setSettingsStatus(const char* spanish, const char* english) { std::snprintf(g_settingsStatus, sizeof(g_settingsStatus), "%s", tr(spanish, english)); } static const char* displayModeName() { switch (g_displayMode) { case DisplayMode::Integer2x: return tr("Píxel entero 2x", "Integer 2x"); case DisplayMode::Stretch16x9: return tr("Estirar 16:9", "Stretch 16:9"); default: return tr("Ajuste 4:3", "Fit 4:3"); } } static const char* controlProfileDisplayName(ControlProfile profile) { switch (profile) { case ControlProfile::GenericPC98: return tr("PC-98 genérico", "Generic PC-98"); case ControlProfile::MouseOriented: return tr("Orientado a ratón", "Mouse-oriented"); case ControlProfile::Custom: return tr("Personalizado", "Custom"); default: return "Touhou PC-98"; } } static void resetFrontendSettings() { g_displayMode = DisplayMode::Fit4x3; g_audioEnabled = true; g_audioVolumePercent = 100; g_analogMovementEnabled = true; g_padStickDeadzone = 48; g_cpuMultiplier = 12; g_fastMemoryCheck = true; g_showPerformanceOverlay = false; g_showPadProbeOverlay = false; g_showAudioOverlay = false; g_interfaceLanguage = InterfaceLanguage::English; g_uiAnimationsEnabled = true; g_uiTheme = UiTheme::Grey; applyControlPreset(g_defaultControls, ControlProfile::TouhouPC98); if (!g_activeGameHasControlOverride) g_activeControls = g_defaultControls; setSettingsStatus("Ajustes restablecidos", "Settings reset"); } static bool saveFrontendSettings() { std::FILE* output = std::fopen(FRONTEND_CONFIG_TEMP_PATH, "wb"); if (output == nullptr) { setSettingsStatus("Error al guardar ajustes", "Error saving settings"); g_settingsSaveFailed = true; setLastFailure("NP2-1201", "No se pudieron guardar los ajustes", "Settings could not be saved", "Comprueba que /data/np2kai-runtime/config/ sea escribible.", "Make sure /data/np2kai-runtime/config/ is writable.", FRONTEND_CONFIG_PATH); return false; } const char* displayValue = "fit4x3"; if (g_displayMode == DisplayMode::Integer2x) { displayValue = "integer2x"; } else if (g_displayMode == DisplayMode::Stretch16x9) { displayValue = "stretch16x9"; } bool success = std::fprintf(output, "config_version=37\n") >= 0 && std::fprintf(output, "screen_mode=%s\n", displayValue) >= 0 && std::fprintf(output, "audio_enabled=%d\n", g_audioEnabled ? 1 : 0) >= 0 && std::fprintf(output, "audio_volume=%d\n", g_audioVolumePercent) >= 0 && std::fprintf(output, "analog_enabled=%d\n", g_analogMovementEnabled ? 1 : 0) >= 0 && std::fprintf(output, "deadzone=%d\n", g_padStickDeadzone) >= 0 && std::fprintf(output, "cpu_multiplier=%d\n", g_cpuMultiplier) >= 0 && std::fprintf(output, "fastmc=%d\n", g_fastMemoryCheck ? 1 : 0) >= 0 && std::fprintf(output, "ui_theme=%s\n", uiThemeConfigName()) >= 0 && std::fprintf(output, "show_performance=%d\n", g_showPerformanceOverlay ? 1 : 0) >= 0 && std::fprintf(output, "show_pad=%d\n", g_showPadProbeOverlay ? 1 : 0) >= 0 && std::fprintf(output, "show_audio=%d\n", g_showAudioOverlay ? 1 : 0) >= 0 && std::fprintf(output, "language=%s\n", interfaceIsEnglish() ? "en" : "es") >= 0 && std::fprintf(output, "ui_animations=%d\n", g_uiAnimationsEnabled ? 1 : 0) >= 0 && std::fprintf(output, "default_control_profile=%s\n", controlProfileConfigName(g_defaultControls.profile)) >= 0; for (unsigned index = 0; success && index < REMAPPABLE_BUTTON_COUNT; ++index) { success = std::fprintf(output, "default_binding_%s=%s\n", remappableButtonConfigName(index), controlActionConfigName(g_defaultControls.actions[index])) >= 0; } success = success && std::fflush(output) == 0; std::fclose(output); if (!success) { std::remove(FRONTEND_CONFIG_TEMP_PATH); g_settingsSaveFailed = true; setLastFailure("NP2-1202", "No se pudieron escribir los ajustes", "Settings could not be written", "El archivo temporal de configuración no pudo completarse.", "The temporary configuration file could not be completed.", FRONTEND_CONFIG_TEMP_PATH); return false; } if (std::remove(FRONTEND_CONFIG_PATH) != 0 && errno != ENOENT) { std::remove(FRONTEND_CONFIG_TEMP_PATH); g_settingsSaveFailed = true; setLastFailure("NP2-1203", "No se pudo reemplazar la configuración", "The settings file could not be replaced", "El archivo anterior no se pudo eliminar.", "The previous settings file could not be removed.", FRONTEND_CONFIG_PATH); return false; } if (std::rename( FRONTEND_CONFIG_TEMP_PATH, FRONTEND_CONFIG_PATH ) != 0) { g_settingsSaveFailed = true; setLastFailure("NP2-1204", "No se pudo activar la configuración", "The settings file could not be activated", "El archivo temporal no se pudo renombrar.", "The temporary settings file could not be renamed.", FRONTEND_CONFIG_TEMP_PATH); return false; } g_settingsSaveFailed = false; return true; } static void clampFrontendSettings() { if (g_audioVolumePercent < 0) g_audioVolumePercent = 0; if (g_audioVolumePercent > 100) g_audioVolumePercent = 100; g_audioVolumePercent = (g_audioVolumePercent / 25) * 25; static const int deadzones[] = {32, 48, 64, 80}; int nearest = deadzones[0]; int nearestDistance = std::abs(g_padStickDeadzone - nearest); for (int value : deadzones) { const int distance = std::abs(g_padStickDeadzone - value); if (distance < nearestDistance) { nearest = value; nearestDistance = distance; } } g_padStickDeadzone = nearest; if (g_cpuMultiplier != 8 && g_cpuMultiplier != 10 && g_cpuMultiplier != 12) { g_cpuMultiplier = 12; } } static void loadFrontendSettings() { resetFrontendSettings(); std::FILE* input = std::fopen(FRONTEND_CONFIG_PATH, "rb"); if (input == nullptr) { saveFrontendSettings(); return; } int loadedConfigVersion = 0; bool malformedConfig = false; ControlProfile loadedDefaultProfile = ControlProfile::TouhouPC98; bool sawDefaultProfile = false; bool sawDefaultBinding[REMAPPABLE_BUTTON_COUNT] {}; ControlAction loadedDefaultBindings[REMAPPABLE_BUTTON_COUNT] {}; for (unsigned index = 0; index < REMAPPABLE_BUTTON_COUNT; ++index) { loadedDefaultBindings[index] = g_defaultControls.actions[index]; } char line[256] {}; while (std::fgets(line, sizeof(line), input) != nullptr) { char key[64] {}; char value[128] {}; if (std::sscanf(line, "%63[^=]=%127s", key, value) != 2) { const char* cursor = line; while (*cursor == ' ' || *cursor == '\t' || *cursor == '\r' || *cursor == '\n') ++cursor; if (*cursor != '\0' && *cursor != '#' && *cursor != ';') malformedConfig = true; continue; } if (std::strcmp(key, "config_version") == 0) { loadedConfigVersion = std::atoi(value); } else if (std::strcmp(key, "screen_mode") == 0) { if (std::strcmp(value, "integer2x") == 0) { g_displayMode = DisplayMode::Integer2x; } else if (std::strcmp(value, "stretch16x9") == 0) { g_displayMode = DisplayMode::Stretch16x9; } else { g_displayMode = DisplayMode::Fit4x3; } } else if (std::strcmp(key, "audio_enabled") == 0) { g_audioEnabled = std::atoi(value) != 0; } else if (std::strcmp(key, "audio_volume") == 0) { g_audioVolumePercent = std::atoi(value); } else if (std::strcmp(key, "analog_enabled") == 0) { g_analogMovementEnabled = std::atoi(value) != 0; } else if (std::strcmp(key, "deadzone") == 0) { g_padStickDeadzone = std::atoi(value); } else if (std::strcmp(key, "cpu_multiplier") == 0) { g_cpuMultiplier = std::atoi(value); } else if (std::strcmp(key, "fastmc") == 0) { g_fastMemoryCheck = std::atoi(value) != 0; } else if (std::strcmp(key, "show_performance") == 0) { g_showPerformanceOverlay = std::atoi(value) != 0; } else if (std::strcmp(key, "ui_theme") == 0) { if (std::strcmp(value, "slate") == 0) g_uiTheme = UiTheme::Slate; else if (std::strcmp(value, "graphite") == 0) g_uiTheme = UiTheme::Graphite; else if (std::strcmp(value, "midnight") == 0) g_uiTheme = UiTheme::Midnight; else g_uiTheme = UiTheme::Grey; } else if (std::strcmp(key, "show_pad") == 0) { g_showPadProbeOverlay = std::atoi(value) != 0; } else if (std::strcmp(key, "show_audio") == 0) { g_showAudioOverlay = std::atoi(value) != 0; } else if (std::strcmp(key, "language") == 0) { g_interfaceLanguage = std::strcmp(value, "en") == 0 ? InterfaceLanguage::English : InterfaceLanguage::Spanish; } else if (std::strcmp(key, "ui_animations") == 0) { g_uiAnimationsEnabled = std::atoi(value) != 0; } else if (std::strcmp(key, "default_control_profile") == 0) { loadedDefaultProfile = parseControlProfile(value); sawDefaultProfile = true; } else if (std::strncmp(key, "default_binding_", 16) == 0) { bool found = false; for (unsigned index = 0; index < REMAPPABLE_BUTTON_COUNT; ++index) { if (std::strcmp(key + 16, remappableButtonConfigName(index)) == 0) { loadedDefaultBindings[index] = parseControlAction(value); sawDefaultBinding[index] = true; found = true; break; } } if (!found) malformedConfig = true; } } std::fclose(input); if (sawDefaultProfile) { if (loadedDefaultProfile == ControlProfile::Custom) { applyControlPreset(g_defaultControls, ControlProfile::TouhouPC98); for (unsigned index = 0; index < REMAPPABLE_BUTTON_COUNT; ++index) { if (sawDefaultBinding[index]) { g_defaultControls.actions[index] = loadedDefaultBindings[index]; } } g_defaultControls.profile = ControlProfile::Custom; } else { applyControlPreset(g_defaultControls, loadedDefaultProfile); } } if (malformedConfig) { resetFrontendSettings(); saveFrontendSettings(); g_pendingConfigRecoveryNotice = true; setSettingsStatus("Configuración dañada restablecida", "Damaged configuration reset"); frontendLog("Malformed frontend.ini detected; defaults restored"); return; } clampFrontendSettings(); if (!g_activeGameHasControlOverride) g_activeControls = g_defaultControls; // los perfiles viejos se respetan tal cual. si vienen de una version // antigua sin perfil, se les pone el de touhou como antes if (loadedConfigVersion < 37) { if (loadedConfigVersion < 33) { if (loadedConfigVersion < 32) { g_interfaceLanguage = InterfaceLanguage::English; } applyControlPreset(g_defaultControls, ControlProfile::TouhouPC98); g_activeControls = g_defaultControls; } // hasta la v0.41 las animaciones venian apagadas porque iban a saltos y // bloqueaban el mando. ahora van suaves, asi que al migrar se encienden. // quien las quiera quitar, sigue estando en Ajustes > Interfaz g_uiAnimationsEnabled = true; saveFrontendSettings(); applyUiTheme(); } } static const char* settingsPageTitle(SettingsPage page) { switch (page) { case SettingsPage::Display: return tr("Pantalla", "Display"); case SettingsPage::Audio: return tr("Audio", "Audio"); case SettingsPage::Controls: return tr("Controles", "Controls"); case SettingsPage::Emulation: return tr("Emulación", "Emulation"); case SettingsPage::Interface: return tr("Interfaz", "Interface"); case SettingsPage::Developer: return tr("Desarrollador", "Developer"); case SettingsPage::Support: return tr("Ayuda e información", "Help and information"); default: return tr("Ajustes", "Settings"); } } static const char* uiThemeName() { switch (g_uiTheme) { case UiTheme::Slate: return tr("Azul pizarra", "Slate blue"); case UiTheme::Graphite: return tr("Grafito", "Graphite"); case UiTheme::Midnight: return tr("Medianoche", "Midnight"); default: return tr("Gris", "Grey"); } } static unsigned settingsRowCount(SettingsPage page) { switch (page) { case SettingsPage::Root: return 8; case SettingsPage::Display: return 2; case SettingsPage::Audio: return 3; case SettingsPage::Controls: return 5; case SettingsPage::Emulation: return 3; case SettingsPage::Interface: return 4; case SettingsPage::Developer: return 6; case SettingsPage::Support: return 7; } return 1; } // --- iconos ---------------------------------------------------------------- // dibujados con rectangulos y circulos, nada de imagenes. asi no engordan el // pkg, se ven igual de nitidos a cualquier tamaño y no uso nada de sony enum class UiIcon { Display, Audio, Controls, Emulation, Interface, Developer, Help, Back, Game, Keyboard, Play, Folder }; // un anillo de verdad, sin pintar el agujero de ningun color: solo se tocan // los pixeles que caen entre el radio de fuera y el de dentro. antes lo hacia // con dos circulos y el de dentro llevaba el color del fondo a pelo, asi que // sobre el degradado y sobre la fila marcada se veia un parche que no pegaba static void drawIconRing(std::uint32_t* output, int centerX, int centerY, int radius, int thickness, std::uint32_t color, bool rightHalfOnly = false) { if (output == nullptr || radius <= 0 || thickness <= 0) return; const int inner = radius - thickness; const int outerSquared = radius * radius; const int innerSquared = inner > 0 ? inner * inner : 0; for (int offsetY = -radius; offsetY <= radius; ++offsetY) { for (int offsetX = rightHalfOnly ? 0 : -radius; offsetX <= radius; ++offsetX) { const int distance = offsetX * offsetX + offsetY * offsetY; if (distance <= outerSquared && distance >= innerSquared) { fillRectangle(output, centerX + offsetX, centerY + offsetY, 1, 1, color); } } } } // linea recta entre dos puntos, con grosor. la uso para las flechas y los // chevrones, que a mano me salian casi verticales static void drawIconLine(std::uint32_t* output, int x0, int y0, int x1, int y1, int thickness, std::uint32_t color) { if (output == nullptr) return; if (thickness < 1) thickness = 1; const int deltaX = x1 - x0; const int deltaY = y1 - y0; int steps = (deltaX < 0 ? -deltaX : deltaX); const int absY = (deltaY < 0 ? -deltaY : deltaY); if (absY > steps) steps = absY; if (steps == 0) { fillRectangle(output, x0, y0, thickness, thickness, color); return; } for (int step = 0; step <= steps; ++step) { const int px = x0 + deltaX * step / steps; const int py = y0 + deltaY * step / steps; fillRectangle(output, px, py, thickness, thickness, color); } } // generado con tools/build-ui-icons.py a partir de assets/icons // iconos de Tabler (https://tabler.io/icons), licencia MIT static constexpr int UI_ICON_SIDE = 48; struct UiIconBitmap { const char* name; std::uint32_t offset; }; static const UiIconBitmap UI_ICON_BITMAPS[] = { {"arrow-back-up", 0u}, {"device-desktop", 2304u}, {"device-gamepad", 4608u}, {"disc", 6912u}, {"info-square-rounded", 9216u}, {"playstation-circle", 11520u}, {"playstation-square", 13824u}, {"playstation-triangle", 16128u}, {"playstation-x", 18432u}, {"terminal-2", 20736u}, {"volume", 23040u}, }; static const std::uint8_t UI_ICON_ALPHA[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,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,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,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,2, 0,5,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,4,0,87,217,220,75,0,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,5,0,96, 255,255,255,225,7,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,5,0,96,255,253,249,253,224,7,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,96,255,253, 253,253,255,86,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,5,0,96,255,253,253,253,255,96,0,3,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,96,255,253,253,254, 255,97,0,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,4,0,96,255,253,253,254,252,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,96,255,253,253,253,251,113, 7,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17,12,1,0,0,0,3,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,3,0,85,255,253,254,255,255,252,228,240,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 231,212,164,93,9,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,7,223,254,252,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,215,81,0,2,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,7,223,255,252,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,252,248,251,255,255,144,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,82,254,253,253,255,255,252,228, 240,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,243,255,255,255,251,251,255,144,0,3,0,0,0,0,0,0, 0,0,0,0,0,0,0,2,0,91,254,253,253,253,250,108,7,21,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 26,62,144,250,255,253,250,255,79,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,91,254,253,253,254,251, 87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,223,255,251,254,224,11,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,4,0,91,254,253,252,254,255,92,0,5,1,1,1,1,1,1,1,1,1,1,1,1, 1,3,6,0,49,247,254,251,255,92,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,91,254,253,252, 253,255,91,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,142,255,247,254,166,0,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,4,0,91,254,253,252,253,255,83,0,3,0,0,0,0,0,0,0,0,0,0, 0,0,0,3,2,53,254,252,255,213,2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,91,254, 253,249,253,224,7,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,24,243,255,255,231,12,0,1,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,91,255,255,255,225,7,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,1,0,25,244,255,255,230,12,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0, 84,219,218,77,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,56,255,251,255,213,2,0,1,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,6,0,144,255,247,254,163,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 3,0,0,2,0,0,0,0,0,0,0,0,0,1,1,1,1,3,6,0,51,247,254,251,255,88,0,4,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0,0, 0,0,0,54,227,255,251,254,226,12,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,4,17,17,24,60,143,249,255,253,250,255,80,0,3,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,76,215,236,238, 242,255,255,255,251,251,255,145,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,1,7,224,255,255,255,255,252,248,251,255,255,145,0,3,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,7,224,255,255,255, 255,255,255,255,216,84,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,2,0,76,217,236,238,232,212,163,92,9,0,3,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,17,17, 13,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,3,3,1,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1, 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, 0,0,0,0,0,2,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,3,2,0,0,0,0,0,0,0,0,0,2,0,0,8,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,8,0,0,2,0,0,0,0, 0,0,0,2,0,41,171,222,237,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,238,237,222,168,42,0,2,0,0,0,0,0,2,1,39,237,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,236,40,1,2,0,0, 0,0,3,0,174,255,248,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,254,248,255,175,0,3,0,0,0,1,0,9,228,254,254,254,239,238,238,238,238,238,238,238, 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,239,254,253,254,228,8,0,1,0, 0,1,0,18,238,255,255,239,33,17,18,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,18,17,33,239,255,255,238,18,0,1,0,0,1,0,17,238,255,255,238,13,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,18,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,2,1,18,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,18,1,2,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,18,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,13,238,255,255,238,17,0,1,0,0,1,0,18,238,255,255,239,33,17,18,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,17,33,239,255,255,238,18,0,1,0, 0,1,0,8,226,254,253,254,239,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,238,239,254,253,254,229,9,0,1,0,0,0,3,0,173,255,248,254,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,248,255,175,0,3,0,0, 0,0,2,1,40,235,255,255,255,255,255,255,255,255,255,255,254,254,254,254,255,255,255,255,255,255,255,255,254,254,254,254, 255,255,255,255,255,255,255,255,255,255,237,39,1,2,0,0,0,0,0,2,0,42,170,222,237,238,238,238,238,238,238,239, 254,255,255,254,239,238,238,238,238,238,238,239,254,255,255,254,239,238,238,238,238,238,238,237,224,169,40,0,2,0,0,0, 0,0,0,0,2,0,0,8,17,17,17,17,17,18,17,33,239,255,255,239,33,17,18,17,17,18,17,33,239,255,255,239, 33,17,18,17,17,17,17,17,9,0,0,2,0,0,0,0,0,0,0,0,0,2,3,0,0,0,0,0,0,0,0,14, 238,255,255,238,14,0,0,0,0,0,0,14,238,255,255,238,14,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0, 0,0,0,0,0,0,0,1,1,1,1,1,3,1,0,14,238,255,255,238,14,0,0,0,0,0,0,14,238,255,255,238, 14,0,1,3,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,17,32, 239,255,255,239,33,17,18,17,17,18,17,33,239,255,255,239,32,17,6,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,2,0,76,215,236,239,254,255,255,254,239,238,238,238,238,238,238,239,254,255,255,254, 239,236,217,75,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,7,224,255,255,255, 254,254,254,254,255,255,255,255,255,255,255,255,254,254,254,254,255,255,255,225,7,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,0,7,224,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,225,7,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,76,217,236,238, 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,236,215,77,0,2,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,5,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,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,2,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, 0,0,0,0,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,4,1,0,0,0,0,0,0,0,1,0,0,0,11,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,11,0,0,0,1,0,0,0, 0,0,1,0,5,108,192,228,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,238,238,228,193,106,5,0,1,0,0,0,2,0,29,199,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,200,30,0,2,0, 1,1,7,204,255,249,250,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,250,250,255,205,7,2,1,4,0,109,255,249,254,255,249,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,249,255,255,250,255,107,0,4, 2,0,200,254,250,255,175,36,19,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,19,36,175,255,250,254,199,1,2,0,11,231,255,254,247,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,27,247,254,255,231,11,0, 0,18,239,255,255,237,16,2,2,1,1,1,1,1,3,0,0,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,2,2,17,237,255,255,239,17,0,0,17,239,255,255,238,18,0,1,0,0,0,0,0,0,5, 6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,18,238,255,255,239,17,0, 0,17,239,255,255,238,17,0,1,0,0,0,2,0,76,217,220,76,0,2,0,0,0,0,0,0,0,0,2,0,0,2, 0,0,0,0,0,0,0,1,0,17,238,255,255,239,17,0,0,17,239,255,255,238,17,0,1,0,0,1,2,7,224,255, 255,225,7,2,1,0,0,0,0,0,0,0,0,5,6,0,0,0,0,0,0,0,0,1,0,17,238,255,255,239,17,0, 0,17,239,255,255,238,17,0,1,0,2,0,0,13,238,253,253,238,14,0,0,2,0,0,0,0,2,0,75,217,220,75, 0,2,0,0,0,0,0,1,0,17,238,255,255,239,17,0,0,17,239,255,255,238,17,0,1,0,0,5,17,32,239,255, 255,239,32,17,6,0,0,0,0,1,1,7,224,255,255,225,7,1,1,1,1,0,0,1,0,17,238,255,255,239,17,0, 0,17,239,255,255,238,17,0,3,0,76,215,236,239,254,255,255,254,239,236,217,75,0,2,0,1,0,7,224,255,255,226, 7,0,3,0,0,2,0,1,0,17,238,255,255,239,17,0,0,17,239,255,255,238,17,0,1,7,224,255,255,255,255,255, 255,255,255,255,255,225,7,1,1,0,2,0,77,222,222,78,0,3,0,5,6,0,0,1,0,17,238,255,255,239,17,0, 0,17,239,255,255,238,17,0,1,7,224,255,255,255,255,255,255,255,255,255,255,225,7,1,1,0,0,0,0,8,8,0, 3,0,75,217,220,75,0,3,0,17,238,255,255,239,17,0,0,17,239,255,255,238,17,0,3,0,76,217,236,239,254,255, 255,254,239,236,215,77,0,2,0,0,0,0,2,0,0,3,1,7,224,255,255,225,7,1,0,17,238,255,255,239,17,0, 0,17,239,255,255,238,17,0,1,0,0,6,17,32,239,255,255,238,32,17,4,0,0,0,0,0,0,0,0,1,1,1, 0,7,224,255,255,226,7,1,0,17,238,255,255,239,17,0,0,17,239,255,255,238,17,0,1,0,2,0,0,14,238,253, 253,238,14,0,0,2,0,0,0,0,0,0,0,0,0,0,2,0,77,222,222,78,0,4,0,17,238,255,255,239,17,0, 0,17,239,255,255,238,17,0,1,0,0,1,2,7,224,255,255,225,7,2,1,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,8,8,0,0,1,0,17,238,255,255,239,17,0,0,17,239,255,255,238,17,0,1,0,0,0,2,0,76,219, 218,77,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,1,0,17,238,255,255,239,17,0, 0,17,239,255,255,238,18,0,1,0,0,0,0,0,0,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,1,1,0,0,1,0,18,238,255,255,239,17,0,0,17,239,255,255,237,16,2,2,1,1,1,1,1,3,0, 0,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,16,237,255,255,239,18,0, 0,11,230,255,254,247,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,27,247,254,255,231,11,0,1,1,203,254,250,255,175,36,19,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,36,175,255,250,254,200,0,2, 4,0,110,255,250,255,255,249,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,238,238,250,255,255,250,255,109,0,4,1,2,6,203,255,250,250,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,250,249,255,204,7,1,1, 0,2,0,30,199,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,200,29,0,2,0,0,0,1,0,6,107,195,227,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,228,192,110,6,0,1,0,0, 0,0,0,2,0,0,0,11,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,11,0,0,0,1,0,0,0,0,0,0,0,1,4,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,1,0,0,0,0, 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,3,0,0,0,0,0,0,3,4,3,1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, 4,0,0,0,0,0,9,15,14,9,0,0,0,0,0,4,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,1,3,0,0,7,58,123,170,205,225,235,234,226,207,171,124,58,8,0, 0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,41, 137,219,255,255,255,255,255,255,255,255,255,255,255,255,220,136,38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,3,0,25,144,246,255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255, 244,146,26,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,89,231,255,255, 249,250,255,255,255,255,248,240,241,248,255,255,255,255,250,249,255,255,231,88,0,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251,255,255,230,168,104,58,32,20,21,32,60,104,169,231,255,255, 251,250,255,255,148,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,8,175,255,252,251,255,255, 214,100,17,0,0,0,0,0,0,0,0,0,0,18,103,216,255,255,252,252,255,173,9,2,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,3,1,175,255,250,252,255,246,116,8,0,0,5,8,5,1,0,0,4,3,4,3,0,0,10, 116,247,255,252,250,255,176,1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,4,0,149,255,250,252,255,217,52,0, 0,5,4,0,0,0,2,14,5,0,0,0,0,1,4,0,0,51,218,255,252,250,255,149,0,4,0,0,0,0,0,0, 0,0,0,0,0,3,0,86,255,251,252,255,204,25,0,5,4,0,0,38,117,177,215,231,217,75,0,2,0,0,0,1, 5,0,25,205,255,252,251,255,85,0,3,0,0,0,0,0,0,0,0,0,2,2,23,236,254,251,255,221,23,0,4,3, 0,32,158,246,255,255,255,255,255,225,7,1,1,0,0,0,0,4,0,24,220,255,251,253,236,24,2,2,0,0,0,0, 0,0,0,0,3,0,144,255,250,253,248,49,0,4,3,0,87,236,255,255,249,248,253,255,255,225,7,1,1,0,0,0, 0,0,4,0,48,249,253,250,255,145,0,3,0,0,0,0,0,0,0,2,0,34,245,253,251,255,119,0,5,3,0,113, 255,255,250,252,255,255,255,240,215,77,0,2,0,0,0,0,0,0,0,5,0,120,255,251,253,246,33,0,2,0,0,0, 0,0,0,4,0,136,255,249,255,220,7,2,3,0,87,255,252,252,255,255,200,109,49,24,4,0,0,0,0,0,0,0, 0,0,0,1,2,7,218,254,249,255,136,0,4,0,0,0,0,0,1,0,8,222,255,250,255,98,0,6,1,27,243,254, 252,255,249,109,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,4,0,97,255,250,255,224,9,0,1,0,0, 0,0,3,0,56,255,253,255,235,15,1,4,0,163,255,250,253,248,72,0,2,6,5,0,0,3,2,0,0,0,0,0, 0,0,0,0,1,1,16,233,255,253,255,54,0,3,0,0,0,0,4,0,121,255,247,255,170,0,5,0,37,252,253,251, 255,108,0,6,2,0,0,8,8,0,0,2,0,0,0,0,0,1,1,0,0,3,0,166,255,247,255,121,0,4,0,0, 0,0,3,0,171,255,248,255,102,1,8,0,118,255,249,255,205,1,3,2,0,41,171,223,223,168,42,0,2,0,0,0, 2,0,0,2,0,4,1,104,255,247,255,171,0,3,0,0,0,0,1,1,205,255,251,255,58,0,6,0,180,255,248,255, 102,0,6,1,39,237,255,255,255,255,236,40,1,2,0,0,0,5,6,0,0,3,0,59,255,251,255,205,1,1,0,0, 0,1,0,8,224,255,255,248,32,0,1,3,217,255,253,253,46,0,5,0,174,255,248,254,254,248,255,174,0,3,2,0, 76,217,220,76,0,4,0,32,248,255,255,225,8,0,1,0,0,1,0,14,234,255,255,240,21,0,0,14,232,253,253,242, 23,0,0,9,228,254,254,255,255,253,254,228,9,0,1,6,223,255,255,225,7,1,0,21,240,255,255,234,15,0,1,0, 0,1,0,14,233,255,255,241,22,0,1,7,224,255,255,224,6,1,0,8,226,254,253,255,255,253,254,228,9,0,0,23, 242,253,253,232,14,0,0,20,240,255,255,234,15,0,1,0,0,1,0,7,224,255,255,249,33,0,4,0,76,219,218,77, 0,2,3,0,173,255,248,254,254,248,255,175,0,5,0,48,253,253,255,216,3,1,0,32,248,255,255,224,8,0,1,0, 0,0,1,1,205,255,251,255,58,0,2,0,0,6,5,0,0,0,2,1,40,235,255,255,255,255,237,39,1,6,0,103, 255,248,255,180,0,5,0,57,255,251,255,206,1,1,0,0,0,0,3,0,171,255,248,255,101,1,4,0,2,0,0,2, 0,0,0,2,0,42,170,223,225,168,40,0,2,3,1,205,255,248,255,119,0,8,1,104,255,248,255,170,0,3,0,0, 0,0,4,0,121,255,247,255,167,0,3,0,0,1,1,0,0,0,0,0,2,0,0,8,10,0,0,2,6,0,108,255, 251,253,250,37,0,5,0,170,255,247,255,122,0,4,0,0,0,0,3,0,55,255,252,255,235,15,1,1,0,0,0,0, 0,0,0,0,0,2,3,0,0,5,6,2,0,72,248,253,250,255,162,0,4,0,15,235,255,253,255,55,0,3,0,0, 0,0,1,1,7,220,255,250,255,96,0,4,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,110,249,255,252, 254,242,27,1,5,0,97,255,250,255,222,7,1,1,0,0,0,0,0,4,0,136,255,249,255,217,6,3,1,0,0,0, 0,0,0,0,0,0,0,4,23,48,109,200,255,255,252,252,255,86,0,3,2,6,218,254,249,255,135,0,4,0,0,0, 0,0,0,2,0,34,245,253,251,255,118,0,5,0,0,0,0,0,0,0,2,0,76,215,239,255,255,255,252,251,255,255, 113,0,3,5,0,118,255,251,253,246,34,0,2,0,0,0,0,0,0,0,3,0,143,255,250,253,248,48,0,4,0,0, 0,0,0,1,1,7,224,255,255,253,248,249,255,255,236,88,0,3,4,0,49,248,252,250,255,144,0,3,0,0,0,0, 0,0,0,0,2,2,22,235,254,251,255,220,23,0,4,0,0,0,0,1,0,7,224,255,255,255,255,255,248,161,32,0, 3,4,0,23,220,255,251,254,236,23,2,2,0,0,0,0,0,0,0,0,0,3,0,86,255,251,252,255,206,24,0,5, 1,0,0,0,2,0,76,217,232,215,175,118,40,0,0,4,5,0,25,203,255,252,251,255,84,0,3,0,0,0,0,0, 0,0,0,0,0,0,3,0,148,255,250,252,255,218,51,0,0,4,1,0,0,0,0,5,15,2,0,0,0,3,5,0, 0,53,218,255,252,250,255,148,0,4,0,0,0,0,0,0,0,0,0,0,0,0,1,3,1,176,255,250,252,255,247,116, 8,0,0,3,4,3,4,0,0,1,5,8,5,0,0,10,118,248,255,252,250,255,175,1,3,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,2,9,174,255,252,251,255,255,213,100,18,0,0,0,0,0,0,0,0,0,0,17,101,216, 255,255,252,252,255,174,7,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,149,255,255,250,251, 255,255,231,169,103,58,32,20,19,32,58,103,168,231,255,255,251,250,255,255,148,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,1,0,87,229,255,255,249,250,255,255,255,255,248,240,240,248,255,255,255,255,250,249, 255,255,232,86,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,25,146,247, 255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255,244,146,27,0,3,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,41,138,218,255,255,255,255,255,255,255,255,255,255,255,255,218,139, 38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0, 0,7,59,121,170,206,225,235,236,226,206,171,122,58,7,0,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,0,0,0,0,8,15,15,9,0,0,0,0,0,4, 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,1,3,4,3,0,0,0,0,0,0,3,4,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,4,3,2,0,0,0,0,0,0,0,0,0,0,2,3,4, 3,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,2,4,0,0,0, 0,0,0,0,6,11,13,17,16,13,11,5,0,0,0,0,0,0,0,4,2,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,3,0,0,0,35,84,127,163,192,207,221,229,232,238,237,233,229,220,207,192,163,126, 84,33,0,0,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,37,127,204,249,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,248,205,128,37,0,0,2,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,3,0,13,132,243,255,255,255,249,247,247,250,252,255,255,255,255,255,255,255,255,252,250,248,247, 249,255,255,255,244,134,13,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,41,209,255,255,249,250,255,255, 255,255,255,255,250,244,242,238,239,242,244,250,255,255,255,255,255,255,249,249,255,255,209,40,0,2,0,0,0,0,0,0, 0,0,0,0,0,2,0,38,232,255,250,251,255,255,241,194,141,101,68,50,36,26,23,17,18,22,26,35,50,68,101,140, 191,240,255,255,251,250,255,233,38,0,2,0,0,0,0,0,0,0,0,0,1,2,12,213,255,251,253,255,217,103,27,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,106,219,255,253,251,255,212,14,2,1,0,0,0,0, 0,0,0,0,3,0,135,255,250,252,255,153,11,0,0,1,4,4,3,2,2,2,1,1,1,1,1,2,2,3,4,4, 2,0,0,12,152,255,252,250,255,135,0,3,0,0,0,0,0,0,0,2,0,31,245,253,251,255,158,0,0,4,1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,0,155,255,251,253,244,30,0,2,0,0,0, 0,0,0,4,0,125,255,249,254,223,9,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,2,2,8,222,254,249,255,129,0,4,0,0,0,0,0,1,1,3,210,254,249,255,104,0,4,0,0,0,0, 0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,4,0,105,255,249,254,208,2,1,0,0,0, 0,0,2,0,32,249,255,255,244,25,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0,0, 0,0,0,0,1,1,21,240,255,254,249,34,0,2,0,0,0,0,4,0,83,255,249,255,195,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,5,6,0,0,0,0,0,0,0,0,0,0,0,0,2,0,192,255,249,255,88,0,4,0,0, 0,0,4,0,129,255,247,255,140,0,4,0,0,0,0,0,0,0,0,0,2,0,75,217,220,77,0,2,0,0,0,0, 0,0,0,0,0,4,1,140,255,247,255,128,0,4,0,0,0,0,3,0,163,255,248,255,101,0,4,0,0,0,0,0, 0,0,0,1,1,7,224,255,255,230,10,1,1,0,0,0,0,0,0,0,0,4,0,100,255,247,255,163,0,3,0,0, 0,0,2,0,193,255,250,255,69,0,3,0,0,0,0,0,0,0,0,1,0,7,224,255,255,228,8,1,1,0,0,0, 0,0,0,0,0,3,0,68,255,250,255,193,0,2,0,0,0,0,1,0,207,255,252,255,49,0,2,0,0,0,0,0, 0,0,0,0,2,0,77,220,219,78,0,2,0,0,0,0,0,0,0,0,0,2,0,49,255,252,255,207,0,0,0,0, 0,1,0,6,221,255,255,250,36,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,4,0,0,0,0,0,0,0, 0,0,0,0,0,2,0,36,251,255,255,221,6,0,1,0,0,1,0,11,229,255,255,244,26,0,2,0,0,0,0,0, 0,0,0,0,0,4,19,15,3,0,0,0,0,0,0,0,0,0,0,0,0,2,0,26,244,255,255,230,12,0,1,0, 0,1,0,14,232,255,255,241,23,0,1,0,0,0,0,0,0,0,2,0,76,215,236,237,218,76,0,2,0,0,0,0, 0,0,0,0,0,1,0,23,241,255,255,232,13,0,1,0,0,1,0,16,236,255,255,239,19,0,1,0,0,0,0,0, 0,1,1,7,224,255,255,255,255,224,6,1,1,0,0,0,0,0,0,0,0,1,0,19,239,255,255,236,16,0,1,0, 0,1,0,16,237,255,255,239,19,0,1,0,0,0,0,0,0,1,0,7,224,255,252,253,253,238,18,0,1,0,0,0, 0,0,0,0,0,1,0,18,238,255,255,237,17,0,1,0,0,1,0,13,232,255,255,242,23,0,1,0,0,0,0,0, 0,0,2,0,76,218,252,253,255,237,17,0,1,0,0,0,0,0,0,0,0,1,0,23,242,255,255,232,13,0,1,0, 0,1,0,11,230,255,255,244,26,0,2,0,0,0,0,0,0,0,0,1,0,21,239,255,255,238,17,0,1,0,0,0, 0,0,0,0,0,2,0,27,244,255,255,229,11,0,1,0,0,1,0,6,221,255,255,250,36,0,2,0,0,0,0,0, 0,0,0,1,2,16,238,255,255,238,18,0,1,0,0,0,0,0,0,0,0,2,0,36,250,255,255,221,6,0,1,0, 0,0,1,0,207,255,252,255,48,0,2,0,0,0,0,0,0,0,0,1,0,18,238,255,255,238,16,2,1,0,0,0, 0,0,0,0,0,2,0,49,255,252,255,207,0,1,0,0,0,0,2,0,192,255,250,255,68,0,3,0,0,0,0,0, 0,0,0,1,0,17,238,255,255,239,21,0,1,0,0,0,0,0,0,0,0,3,0,68,255,250,255,193,0,2,0,0, 0,0,3,0,163,255,248,255,100,0,4,0,0,0,0,0,0,0,0,1,0,17,237,255,254,252,218,75,0,2,0,0, 0,0,0,0,0,4,0,101,255,248,255,164,0,4,0,0,0,0,4,0,127,255,247,255,140,0,4,0,0,0,0,0, 0,0,0,1,0,18,238,253,253,252,255,225,7,1,1,0,0,0,0,0,0,4,0,139,255,247,255,128,0,4,0,0, 0,0,4,0,85,255,249,255,196,0,1,0,0,0,0,0,0,0,0,1,1,6,224,255,255,255,255,225,7,1,1,0, 0,0,0,0,0,2,0,193,255,249,255,85,0,4,0,0,0,0,2,0,35,251,254,255,243,24,1,2,0,0,0,0, 0,0,0,0,2,0,76,217,236,236,215,77,0,2,0,0,0,0,0,0,2,1,25,243,255,254,248,31,0,2,0,0, 0,0,0,1,3,207,254,249,255,102,0,4,0,0,0,0,0,0,0,0,0,0,0,5,17,17,4,0,0,0,0,0, 0,0,0,0,4,0,104,255,249,254,209,2,1,0,0,0,0,0,0,4,0,124,255,249,254,221,9,2,2,0,0,0, 0,0,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,2,2,9,222,254,249,255,125,0,4,0,0,0, 0,0,0,2,0,31,244,253,251,255,154,0,0,4,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0, 0,1,4,0,0,155,255,251,253,244,31,0,2,0,0,0,0,0,0,0,3,0,134,255,250,252,255,154,12,0,0,1, 4,4,3,2,2,1,1,1,1,1,1,2,2,3,4,4,1,0,0,12,153,255,252,250,255,135,0,3,0,0,0,0, 0,0,0,0,1,2,14,212,255,251,253,255,217,104,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,27,106,218,255,253,251,255,212,13,2,1,0,0,0,0,0,0,0,0,0,2,0,37,233,255,250,251,255,255,242,194, 139,101,68,49,35,26,23,18,18,22,26,35,50,69,102,141,193,241,255,255,251,250,255,232,37,0,2,0,0,0,0,0, 0,0,0,0,0,0,2,0,41,209,255,255,249,249,255,255,255,255,255,255,250,244,242,239,239,242,244,250,255,255,255,255, 255,255,250,249,255,255,209,40,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,14,132,244,255,255,255,249, 247,247,250,252,255,255,255,255,255,255,255,255,252,250,247,247,249,255,255,255,243,132,12,0,3,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,2,0,0,38,125,204,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,249,205,126,37,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,34,81, 126,162,191,207,220,229,232,237,237,233,229,221,208,193,163,128,85,35,0,0,0,3,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,0,5,11,13,16,16,13,11,6,0,0,0,0, 0,0,0,4,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,2,3, 4,4,2,0,0,0,0,0,0,0,0,0,0,2,3,4,4,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,3,0,0,0,0,0,1,3,4,3,1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, 4,0,0,0,0,0,7,14,14,8,0,0,0,0,0,4,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,1,3,0,0,7,55,120,168,205,223,234,234,224,204,170,121,57,7,0, 0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39, 135,219,255,255,255,255,255,255,255,255,255,255,255,255,219,136,38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,24,142,245,255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255, 244,145,26,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,85,230,255,255, 249,250,255,255,255,255,249,241,241,248,255,255,255,255,250,249,255,255,231,87,0,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251,255,255,231,170,104,59,34,21,21,33,60,106,169,232,255,255, 251,250,255,255,145,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,7,174,255,252,251,255,255, 214,101,19,0,0,0,0,0,0,0,0,0,0,20,103,216,255,255,252,252,255,173,8,2,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,3,0,174,255,250,252,255,248,117,8,0,0,3,4,3,2,2,2,2,3,4,3,0,0,10, 118,249,255,252,250,255,174,1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,4,0,148,255,250,252,255,218,53,0, 0,4,1,1,4,3,0,0,0,0,3,4,2,1,4,0,0,54,218,255,252,250,255,146,0,4,0,0,0,0,0,0, 0,0,0,0,0,3,0,85,255,251,252,255,206,25,0,5,1,1,3,0,0,0,2,13,13,1,0,0,0,3,1,1, 5,0,25,207,255,252,251,255,83,0,3,0,0,0,0,0,0,0,0,0,2,2,23,235,254,251,255,221,24,0,4,0, 2,0,0,25,109,172,214,231,232,213,173,106,25,0,0,2,0,4,0,25,221,255,251,254,235,23,2,2,0,0,0,0, 0,0,0,0,3,0,144,255,250,253,248,50,0,4,0,2,0,7,125,235,255,255,255,255,255,255,255,255,236,125,8,0, 2,0,4,0,49,249,253,250,255,145,0,3,0,0,0,0,0,0,0,2,0,34,245,253,251,255,119,0,5,0,2,0, 32,201,255,255,250,248,252,255,255,252,248,250,255,255,200,31,0,2,0,5,0,120,255,251,253,246,33,0,2,0,0,0, 0,0,0,4,0,136,255,249,255,219,7,3,1,2,0,29,225,255,251,251,255,255,255,243,242,255,255,255,251,250,255,223, 30,0,2,1,2,7,219,254,249,255,135,0,4,0,0,0,0,0,1,0,7,222,255,250,255,99,0,4,1,2,7,205, 255,251,252,255,226,123,53,25,23,54,121,224,255,252,250,255,204,7,2,1,4,0,99,255,250,255,223,9,0,1,0,0, 0,0,3,0,55,255,253,255,235,15,1,1,3,0,123,255,250,252,255,165,19,0,0,0,0,0,0,19,164,255,252,250, 255,124,0,3,1,1,16,233,255,253,255,54,0,3,0,0,0,0,4,0,121,255,247,255,170,0,3,1,0,24,242,253, 251,255,168,0,0,4,2,1,1,2,4,0,0,167,255,251,253,242,24,0,2,3,0,167,255,247,255,121,0,4,0,0, 0,0,3,0,170,255,248,255,102,1,4,4,0,104,255,250,254,227,13,1,3,0,0,0,0,0,0,3,2,15,226,254, 250,255,107,0,4,4,1,104,255,247,255,171,0,3,0,0,0,0,1,1,206,255,251,255,58,0,3,3,0,173,254,247, 255,119,0,4,0,0,0,0,0,0,0,0,4,0,119,255,247,254,177,0,3,3,0,59,255,251,255,205,1,1,0,0, 0,1,0,8,224,255,255,249,33,0,2,0,3,216,255,252,253,50,1,3,0,0,0,0,0,0,0,0,3,1,49,254, 252,255,216,3,0,2,0,33,249,255,255,224,7,0,1,0,0,1,0,14,234,255,255,240,21,0,2,0,13,232,255,255, 242,22,0,1,0,0,0,0,0,0,0,0,1,0,23,242,255,255,232,14,0,2,0,21,240,255,255,234,15,0,1,0, 0,1,0,14,233,255,255,241,22,0,2,0,13,232,255,255,242,23,0,1,0,0,0,0,0,0,0,0,1,0,24,242, 255,255,231,13,0,2,0,20,240,255,255,234,15,0,1,0,0,1,0,7,223,255,255,249,33,0,2,0,3,216,255,252, 254,50,1,3,0,0,0,0,0,0,0,0,3,1,47,253,253,255,217,4,0,2,0,33,249,255,255,224,8,0,1,0, 0,0,1,1,205,255,251,255,58,0,3,3,0,174,254,247,255,118,0,4,0,0,0,0,0,0,0,0,4,0,121,255, 247,254,175,0,3,3,0,58,255,251,255,206,1,1,0,0,0,0,3,0,170,255,248,255,101,1,4,4,0,107,255,250, 255,228,14,1,3,0,0,0,0,0,0,3,1,13,228,254,250,255,104,0,4,4,1,104,255,248,255,170,0,3,0,0, 0,0,4,0,120,255,247,255,167,0,3,1,0,24,241,253,251,255,168,0,0,4,2,1,1,2,4,0,0,168,255,251, 253,241,23,0,1,3,0,170,255,247,255,121,0,4,0,0,0,0,3,0,55,255,252,255,235,15,1,1,3,0,124,255, 250,252,255,165,19,0,0,0,0,0,0,19,166,255,252,250,255,122,0,3,1,1,15,235,255,253,255,54,0,3,0,0, 0,0,1,1,7,220,255,250,255,97,0,4,1,2,8,205,255,251,253,255,226,121,54,24,23,55,121,224,255,252,251,255, 205,8,2,1,4,0,97,255,250,255,222,8,1,1,0,0,0,0,0,4,0,136,255,249,255,219,8,3,1,2,0,30, 222,255,250,251,255,255,255,242,242,255,255,255,251,250,255,226,31,0,2,1,3,7,218,254,249,255,135,0,4,0,0,0, 0,0,0,2,0,34,245,253,251,255,119,0,5,0,2,0,30,201,255,255,250,248,252,255,255,252,248,250,255,255,200,32, 0,2,0,5,0,119,255,251,253,246,34,0,2,0,0,0,0,0,0,0,3,0,143,255,250,253,248,48,0,4,0,2, 0,8,126,235,255,255,255,255,255,255,255,255,236,123,6,0,2,0,4,0,50,248,253,250,255,143,0,3,0,0,0,0, 0,0,0,0,2,2,22,235,254,251,255,221,24,0,4,0,2,0,0,24,108,171,213,232,233,213,173,108,25,0,0,2, 0,4,0,24,221,255,251,254,234,22,2,1,0,0,0,0,0,0,0,0,0,3,0,83,255,251,252,255,206,25,0,5, 1,1,3,0,0,0,1,13,14,1,0,0,0,3,1,1,5,0,25,206,255,252,251,255,83,0,3,0,0,0,0,0, 0,0,0,0,0,0,3,0,147,255,250,252,255,218,53,0,0,4,1,1,4,3,0,0,0,0,3,4,2,1,4,0, 0,53,218,255,252,250,255,146,0,4,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,174,255,250,252,255,249,121, 9,0,0,3,4,3,2,2,2,2,3,4,3,0,0,10,120,249,255,252,250,255,174,0,3,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,2,9,173,255,252,252,255,255,214,101,19,0,0,0,0,0,0,0,0,0,0,17,104,216, 255,255,252,252,255,173,7,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251, 255,255,233,171,105,60,33,22,21,33,60,103,170,231,255,255,251,250,255,255,147,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,2,0,85,229,255,255,249,250,255,255,255,255,248,241,241,248,255,255,255,255,250,249, 255,255,231,86,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,24,141,245, 255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255,244,144,27,0,3,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39,134,216,255,255,255,255,255,255,255,255,255,255,255,255,217,137, 38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0, 0,5,56,120,168,204,224,233,234,224,204,170,120,56,6,0,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,0,0,0,0,8,13,14,7,0,0,0,0,0,4, 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,1,3,4,3,1,0,0,0,0,1,3,4,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,3,0,0,0,0,0,1,3,4,3,1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, 4,0,0,0,0,0,7,14,14,8,0,0,0,0,0,4,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,1,3,0,0,7,55,120,168,205,223,234,234,224,204,170,121,57,7,0, 0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39, 135,219,255,255,255,255,255,255,255,255,255,255,255,255,219,136,38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,24,142,245,255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255, 244,145,26,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,85,230,255,255, 249,250,255,255,255,255,249,241,241,248,255,255,255,255,250,249,255,255,231,87,0,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251,255,255,231,170,104,59,34,21,21,33,60,106,169,232,255,255, 251,250,255,255,145,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,7,174,255,252,251,255,255, 214,101,19,0,0,0,0,0,0,0,0,0,0,20,103,216,255,255,252,252,255,173,8,2,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,3,0,174,255,250,252,255,248,117,8,0,0,3,4,3,2,1,1,2,3,4,3,0,0,10, 118,249,255,252,250,255,174,1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,4,0,148,255,250,252,255,218,53,0, 0,4,2,1,1,1,1,1,1,1,1,1,1,2,4,0,0,54,218,255,252,250,255,146,0,4,0,0,0,0,0,0, 0,0,0,0,0,3,0,85,255,251,252,255,206,25,0,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3, 7,0,25,207,255,252,251,255,83,0,3,0,0,0,0,0,0,0,0,0,2,2,23,235,254,251,255,221,24,0,6,0, 0,8,17,17,17,17,17,17,17,17,17,17,17,17,8,0,0,6,0,25,221,255,251,254,235,23,2,2,0,0,0,0, 0,0,0,0,3,0,144,255,250,253,248,50,0,5,0,41,171,222,237,238,238,238,238,238,238,238,238,238,238,237,222,168, 42,0,5,0,49,249,253,250,255,145,0,3,0,0,0,0,0,0,0,2,0,34,245,253,251,255,119,0,7,1,39,237, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,236,40,1,7,0,120,255,251,253,246,33,0,2,0,0,0, 0,0,0,4,0,136,255,249,255,219,7,3,4,0,174,255,248,254,255,255,255,255,255,255,255,255,255,255,255,255,254,248, 255,175,0,4,2,7,219,254,249,255,135,0,4,0,0,0,0,0,1,0,7,222,255,250,255,99,0,5,0,9,228,254, 254,254,239,238,238,238,238,238,238,238,238,238,238,239,254,253,254,228,8,0,5,0,99,255,250,255,223,9,0,1,0,0, 0,0,3,0,55,255,253,255,235,15,1,2,0,18,238,255,255,239,33,17,18,17,17,17,17,17,17,18,17,33,239,255, 255,238,18,0,2,1,16,233,255,253,255,54,0,3,0,0,0,0,4,0,121,255,247,255,170,0,3,1,0,17,238,255, 255,238,13,0,0,0,0,0,0,0,0,0,0,13,238,255,255,238,17,0,1,3,0,167,255,247,255,121,0,4,0,0, 0,0,3,0,170,255,248,255,102,1,4,1,0,17,238,255,255,238,18,1,2,1,1,1,1,1,1,2,1,18,238,255, 255,238,17,0,1,4,1,104,255,247,255,171,0,3,0,0,0,0,1,1,206,255,251,255,58,0,3,1,0,17,238,255, 255,238,17,0,1,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,3,0,59,255,251,255,205,1,1,0,0, 0,1,0,8,224,255,255,249,33,0,2,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,1,0,17,238,255, 255,238,17,0,1,2,0,33,249,255,255,224,7,0,1,0,0,1,0,14,234,255,255,240,21,0,1,1,0,17,238,255, 255,238,17,0,1,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,1,0,21,240,255,255,234,15,0,1,0, 0,1,0,14,233,255,255,241,22,0,1,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,1,0,17,238,255, 255,238,17,0,1,1,0,20,240,255,255,234,15,0,1,0,0,1,0,7,223,255,255,249,33,0,2,1,0,17,238,255, 255,238,17,0,1,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,2,0,33,249,255,255,224,8,0,1,0, 0,0,1,1,205,255,251,255,58,0,3,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,1,0,17,238,255, 255,238,17,0,1,3,0,58,255,251,255,206,1,1,0,0,0,0,3,0,170,255,248,255,101,1,4,1,0,17,238,255, 255,238,18,1,2,1,1,1,1,1,1,2,1,18,238,255,255,238,17,0,1,4,1,104,255,248,255,170,0,3,0,0, 0,0,4,0,120,255,247,255,167,0,3,1,0,17,238,255,255,238,13,0,0,0,0,0,0,0,0,0,0,13,238,255, 255,238,17,0,1,3,0,170,255,247,255,121,0,4,0,0,0,0,3,0,55,255,252,255,235,15,1,2,0,18,238,255, 255,239,33,17,18,17,17,17,17,17,17,18,17,33,239,255,255,238,18,0,2,1,15,235,255,253,255,54,0,3,0,0, 0,0,1,1,7,220,255,250,255,97,0,5,0,8,226,254,253,254,239,238,238,238,238,238,238,238,238,238,238,239,254,253, 254,229,9,0,5,0,97,255,250,255,222,8,1,1,0,0,0,0,0,4,0,136,255,249,255,219,8,3,4,0,173,255, 248,254,255,255,255,255,255,255,255,255,255,255,255,255,254,248,255,175,0,4,3,7,218,254,249,255,135,0,4,0,0,0, 0,0,0,2,0,34,245,253,251,255,119,0,7,1,40,235,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 237,39,1,7,0,119,255,251,253,246,34,0,2,0,0,0,0,0,0,0,3,0,143,255,250,253,248,48,0,5,0,42, 170,222,237,238,238,238,238,238,238,238,238,238,238,237,224,169,40,0,5,0,50,248,253,250,255,143,0,3,0,0,0,0, 0,0,0,0,2,2,22,235,254,251,255,221,24,0,6,0,0,8,17,17,17,17,17,17,17,17,17,17,17,17,9,0, 0,6,0,24,221,255,251,254,234,22,2,1,0,0,0,0,0,0,0,0,0,3,0,83,255,251,252,255,206,25,0,7, 3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,7,0,25,206,255,252,251,255,83,0,3,0,0,0,0,0, 0,0,0,0,0,0,3,0,147,255,250,252,255,218,53,0,0,4,2,1,1,1,1,1,1,1,1,1,1,2,5,0, 0,53,218,255,252,250,255,146,0,4,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,174,255,250,252,255,249,121, 9,0,0,3,4,3,2,1,1,2,3,4,3,0,0,10,120,249,255,252,250,255,174,0,3,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,2,9,173,255,252,252,255,255,214,101,19,0,0,0,0,0,0,0,0,0,0,17,104,216, 255,255,252,252,255,173,7,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251, 255,255,233,171,105,60,33,22,21,33,60,103,170,231,255,255,251,250,255,255,147,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,2,0,85,229,255,255,249,250,255,255,255,255,248,241,241,248,255,255,255,255,250,249, 255,255,231,86,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,24,141,245, 255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255,244,144,27,0,3,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39,134,216,255,255,255,255,255,255,255,255,255,255,255,255,217,137, 38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0, 0,5,56,120,168,204,224,233,234,224,204,170,120,56,6,0,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,0,0,0,0,8,13,14,7,0,0,0,0,0,4, 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,1,3,4,3,1,0,0,0,0,1,3,4,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,3,0,0,0,0,0,1,3,4,3,1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, 4,0,0,0,0,0,7,14,14,8,0,0,0,0,0,4,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,1,3,0,0,7,55,120,168,205,223,234,234,224,204,170,121,57,7,0, 0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39, 135,219,255,255,255,255,255,255,255,255,255,255,255,255,219,136,38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,24,142,245,255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255, 244,145,26,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,85,230,255,255, 249,250,255,255,255,255,249,241,241,248,255,255,255,255,250,249,255,255,231,87,0,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251,255,255,231,170,104,59,34,21,21,33,60,106,169,232,255,255, 251,250,255,255,145,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,7,174,255,252,251,255,255, 214,101,19,0,0,0,0,0,0,0,0,0,0,20,103,216,255,255,252,252,255,173,8,2,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,3,0,174,255,250,252,255,248,117,8,0,0,3,4,3,4,0,0,4,3,4,3,0,0,10, 118,249,255,252,250,255,174,1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,4,0,148,255,250,252,255,218,53,0, 0,4,1,0,0,1,0,3,5,0,1,0,0,1,4,0,0,54,218,255,252,250,255,146,0,4,0,0,0,0,0,0, 0,0,0,0,0,3,0,85,255,251,252,255,206,25,0,5,1,0,0,0,2,0,72,214,217,75,0,2,0,0,0,1, 5,0,25,207,255,252,251,255,83,0,3,0,0,0,0,0,0,0,0,0,2,2,23,235,254,251,255,221,24,0,4,0, 0,0,0,2,2,21,239,255,255,239,22,2,2,0,0,0,0,4,0,25,221,255,251,254,235,23,2,2,0,0,0,0, 0,0,0,0,3,0,144,255,250,253,248,50,0,4,0,0,0,0,0,3,0,148,255,249,249,255,146,0,4,0,0,0, 0,0,4,0,49,249,253,250,255,145,0,3,0,0,0,0,0,0,0,2,0,34,245,253,251,255,119,0,5,0,0,0, 0,0,2,1,42,250,253,254,254,253,249,43,1,2,0,0,0,0,0,5,0,120,255,251,253,246,33,0,2,0,0,0, 0,0,0,4,0,136,255,249,255,219,7,3,1,0,0,0,0,0,3,0,177,255,252,253,254,252,255,179,0,3,0,0, 0,0,0,1,2,7,219,254,249,255,135,0,4,0,0,0,0,0,1,0,7,222,255,250,255,99,0,4,0,0,0,0, 0,3,0,68,255,253,254,255,255,254,253,255,66,0,3,0,0,0,0,0,4,0,99,255,250,255,223,9,0,1,0,0, 0,0,3,0,55,255,253,255,235,15,1,1,0,0,0,0,1,2,3,207,255,250,255,186,186,255,250,255,206,4,2,1, 0,0,0,0,1,1,16,233,255,253,255,54,0,3,0,0,0,0,4,0,121,255,247,255,170,0,3,0,0,0,0,0, 3,0,97,255,252,252,253,41,43,253,252,251,255,99,0,3,0,0,0,0,0,3,0,167,255,247,255,121,0,4,0,0, 0,0,3,0,170,255,248,255,102,1,4,0,0,0,0,1,2,14,228,254,250,255,164,0,0,162,255,250,254,228,13,2, 1,0,0,0,0,4,1,104,255,247,255,171,0,3,0,0,0,0,1,1,206,255,251,255,58,0,3,0,0,0,0,3, 0,131,255,251,253,243,31,1,1,31,243,253,250,255,130,0,3,0,0,0,0,3,0,59,255,251,255,205,1,1,0,0, 0,1,0,8,224,255,255,249,33,0,2,0,0,0,2,1,30,244,253,251,255,130,0,4,3,0,132,255,251,253,244,31, 1,2,0,0,0,2,0,33,249,255,255,224,7,0,1,0,0,1,0,14,234,255,255,240,21,0,1,0,0,0,3,0, 164,255,250,254,229,13,2,1,1,2,14,227,254,250,255,163,0,3,0,0,0,1,0,21,240,255,255,234,15,0,1,0, 0,1,0,14,233,255,255,241,22,0,1,0,0,3,0,54,253,253,252,255,99,0,3,0,0,3,0,98,255,252,253,253, 53,0,3,0,0,1,0,20,240,255,255,234,15,0,1,0,0,1,0,7,223,255,255,249,33,0,2,0,0,3,0,193, 255,249,255,207,4,3,2,1,1,2,4,4,209,255,250,255,194,0,3,0,0,2,0,33,249,255,255,224,8,0,1,0, 0,0,1,1,205,255,251,255,58,0,3,0,3,0,83,255,252,253,255,63,0,0,0,0,0,0,0,0,63,254,253,252, 255,83,0,3,0,3,0,58,255,251,255,206,1,1,0,0,0,0,3,0,170,255,248,255,101,1,4,1,1,8,218,254, 250,255,186,15,19,17,17,17,17,17,17,19,14,184,255,250,254,217,7,1,1,4,1,104,255,248,255,170,0,3,0,0, 0,0,4,0,120,255,247,255,167,0,3,4,0,114,255,252,255,255,240,239,239,238,238,238,238,238,238,239,239,240,255,255, 252,255,116,0,4,3,0,170,255,247,255,121,0,4,0,0,0,0,3,0,55,255,252,255,235,15,1,0,7,221,252,252, 254,254,255,255,255,255,255,255,255,255,255,255,255,255,254,254,252,252,222,7,0,1,15,235,255,253,255,54,0,3,0,0, 0,0,1,1,7,220,255,250,255,97,0,3,7,225,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,224,7,3,0,97,255,250,255,222,8,1,1,0,0,0,0,0,4,0,136,255,249,255,219,8,5,0,76,217,236, 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,236,215,75,0,5,7,218,254,249,255,135,0,4,0,0,0, 0,0,0,2,0,34,245,253,251,255,119,0,5,0,5,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,4,0,5,0,119,255,251,253,246,34,0,2,0,0,0,0,0,0,0,3,0,143,255,250,253,248,48,0,6,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,50,248,253,250,255,143,0,3,0,0,0,0, 0,0,0,0,2,2,22,235,254,251,255,221,24,0,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,5,0,24,221,255,251,254,234,22,2,1,0,0,0,0,0,0,0,0,0,3,0,83,255,251,252,255,206,25,0,5, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,25,206,255,252,251,255,83,0,3,0,0,0,0,0, 0,0,0,0,0,0,3,0,147,255,250,252,255,218,53,0,0,4,1,0,0,0,0,0,0,0,0,0,0,1,4,0, 0,53,218,255,252,250,255,146,0,4,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,174,255,250,252,255,249,121, 9,0,0,3,4,3,2,1,1,2,3,4,3,0,0,10,120,249,255,252,250,255,174,0,3,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,2,9,173,255,252,252,255,255,214,101,19,0,0,0,0,0,0,0,0,0,0,17,104,216, 255,255,252,252,255,173,7,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251, 255,255,233,171,105,60,33,22,21,33,60,103,170,231,255,255,251,250,255,255,147,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,2,0,85,229,255,255,249,250,255,255,255,255,248,241,241,248,255,255,255,255,250,249, 255,255,231,86,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,24,141,245, 255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255,244,144,27,0,3,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39,134,216,255,255,255,255,255,255,255,255,255,255,255,255,217,137, 38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0, 0,5,56,120,168,204,224,233,234,224,204,170,120,56,6,0,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,0,0,0,0,8,13,14,7,0,0,0,0,0,4, 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,1,3,4,3,1,0,0,0,0,1,3,4,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,3,0,0,0,0,0,1,3,4,3,1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, 4,0,0,0,0,0,7,14,14,8,0,0,0,0,0,4,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,1,3,0,0,7,55,120,168,205,223,234,234,224,204,170,121,57,7,0, 0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39, 135,219,255,255,255,255,255,255,255,255,255,255,255,255,219,136,38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,24,142,245,255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255, 244,145,26,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,85,230,255,255, 249,250,255,255,255,255,249,241,241,248,255,255,255,255,250,249,255,255,231,87,0,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251,255,255,231,170,104,59,34,21,21,33,60,106,169,232,255,255, 251,250,255,255,145,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,7,174,255,252,251,255,255, 214,101,19,0,0,0,0,0,0,0,0,0,0,20,103,216,255,255,252,252,255,173,8,2,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,3,0,174,255,250,252,255,248,117,8,0,0,3,4,3,2,1,1,2,3,4,3,0,0,10, 118,249,255,252,250,255,174,1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,4,0,148,255,250,252,255,218,53,0, 0,4,1,0,0,0,0,0,0,0,0,0,0,1,4,0,0,54,218,255,252,250,255,146,0,4,0,0,0,0,0,0, 0,0,0,0,0,3,0,85,255,251,252,255,206,25,0,5,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, 5,0,25,207,255,252,251,255,83,0,3,0,0,0,0,0,0,0,0,0,2,2,23,235,254,251,255,221,24,0,4,2, 0,0,3,0,0,0,0,0,0,0,0,0,0,3,0,0,2,4,0,25,221,255,251,254,235,23,2,2,0,0,0,0, 0,0,0,0,3,0,144,255,250,253,248,50,0,3,0,0,5,6,0,2,0,0,0,0,0,0,0,0,2,0,5,6, 0,0,4,0,49,249,253,250,255,145,0,3,0,0,0,0,0,0,0,2,0,34,245,253,251,255,119,0,5,2,0,75, 217,220,84,0,4,0,0,0,0,0,0,4,0,87,217,220,75,0,2,5,0,120,255,251,253,246,33,0,2,0,0,0, 0,0,0,4,0,136,255,249,255,219,7,3,2,1,7,224,255,255,255,91,0,5,0,0,0,0,5,0,96,255,255,255, 225,7,1,2,2,7,219,254,249,255,135,0,4,0,0,0,0,0,1,0,7,222,255,250,255,99,0,4,0,0,7,223, 253,249,253,255,91,0,5,0,0,5,0,96,255,253,249,253,224,7,1,1,4,0,99,255,250,255,223,9,0,1,0,0, 0,0,3,0,55,255,253,255,235,15,1,1,0,3,0,82,254,253,252,253,255,91,0,5,5,0,96,255,253,253,253,255, 86,0,3,0,1,1,16,233,255,253,255,54,0,3,0,0,0,0,4,0,121,255,247,255,170,0,3,0,0,0,2,0, 91,254,253,252,253,255,91,0,0,96,255,253,253,253,255,96,0,3,0,0,0,3,0,167,255,247,255,121,0,4,0,0, 0,0,3,0,170,255,248,255,102,1,4,0,0,0,0,4,0,91,254,253,252,253,255,82,87,255,253,253,253,255,96,0, 4,0,0,0,0,4,1,104,255,247,255,171,0,3,0,0,0,0,1,1,206,255,251,255,58,0,3,0,0,0,0,0, 4,0,91,254,253,253,255,255,255,255,253,253,255,96,0,5,0,0,0,0,0,3,0,59,255,251,255,205,1,1,0,0, 0,1,0,8,224,255,255,249,33,0,2,0,0,0,0,0,0,4,0,91,255,255,253,255,255,254,255,255,96,0,5,0, 0,0,0,0,0,2,0,33,249,255,255,224,7,0,1,0,0,1,0,14,234,255,255,240,21,0,1,0,0,0,0,0, 0,0,4,0,81,247,253,254,254,253,248,87,0,5,0,0,0,0,0,0,0,1,0,21,240,255,255,234,15,0,1,0, 0,1,0,14,233,255,255,241,22,0,1,0,0,0,0,0,0,0,5,0,87,248,253,254,254,253,248,81,0,5,0,0, 0,0,0,0,0,1,0,20,240,255,255,234,15,0,1,0,0,1,0,7,223,255,255,249,33,0,2,0,0,0,0,0, 0,5,0,96,255,255,254,255,255,253,255,255,91,0,5,0,0,0,0,0,0,2,0,33,249,255,255,224,8,0,1,0, 0,0,1,1,205,255,251,255,58,0,3,0,0,0,0,0,5,0,96,255,253,253,255,255,255,255,253,253,255,91,0,5, 0,0,0,0,0,3,0,58,255,251,255,206,1,1,0,0,0,0,3,0,170,255,248,255,101,1,4,0,0,0,0,4, 0,96,255,253,253,253,255,87,82,255,254,252,253,255,91,0,4,0,0,0,0,4,1,104,255,248,255,170,0,3,0,0, 0,0,4,0,120,255,247,255,167,0,3,0,0,0,3,0,96,255,253,253,253,255,96,0,0,91,254,253,252,253,255,91, 0,3,0,0,0,3,0,170,255,247,255,121,0,4,0,0,0,0,3,0,55,255,252,255,235,15,1,1,0,3,0,85, 255,253,253,253,255,96,0,5,4,0,91,254,253,252,253,255,83,0,3,0,1,1,15,235,255,253,255,54,0,3,0,0, 0,0,1,1,7,220,255,250,255,97,0,4,1,1,7,223,253,249,253,255,96,0,5,0,0,4,0,91,254,253,249,253, 224,7,1,0,4,0,97,255,250,255,222,8,1,1,0,0,0,0,0,4,0,136,255,249,255,219,8,3,2,1,7,224, 255,255,255,96,0,5,0,0,0,0,4,0,91,255,255,255,225,7,1,2,3,7,218,254,249,255,135,0,4,0,0,0, 0,0,0,2,0,34,245,253,251,255,119,0,5,2,0,76,219,218,87,0,4,0,0,0,0,0,0,4,0,84,219,218, 77,0,2,5,0,119,255,251,253,246,34,0,2,0,0,0,0,0,0,0,3,0,143,255,250,253,248,48,0,4,0,0, 6,5,0,2,0,0,0,0,0,0,0,0,2,0,6,5,0,0,3,0,50,248,253,250,255,143,0,3,0,0,0,0, 0,0,0,0,2,2,22,235,254,251,255,221,24,0,4,2,0,0,3,0,0,0,0,0,0,0,0,0,0,3,0,0, 2,4,0,24,221,255,251,254,234,22,2,1,0,0,0,0,0,0,0,0,0,3,0,83,255,251,252,255,206,25,0,5, 1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,5,0,25,206,255,252,251,255,83,0,3,0,0,0,0,0, 0,0,0,0,0,0,3,0,147,255,250,252,255,218,53,0,0,4,1,0,0,0,0,0,0,0,0,0,0,1,4,0, 0,53,218,255,252,250,255,146,0,4,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,174,255,250,252,255,249,121, 9,0,0,3,4,3,2,1,1,2,3,4,3,0,0,10,120,249,255,252,250,255,174,0,3,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,2,9,173,255,252,252,255,255,214,101,19,0,0,0,0,0,0,0,0,0,0,17,104,216, 255,255,252,252,255,173,7,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,147,255,255,250,251, 255,255,233,171,105,60,33,22,21,33,60,103,170,231,255,255,251,250,255,255,147,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,2,0,85,229,255,255,249,250,255,255,255,255,248,241,241,248,255,255,255,255,250,249, 255,255,231,86,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,24,141,245, 255,255,253,248,248,251,255,255,255,255,251,248,248,253,255,255,244,144,27,0,3,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,39,134,216,255,255,255,255,255,255,255,255,255,255,255,255,217,137, 38,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0, 0,5,56,120,168,204,224,233,234,224,204,170,120,56,6,0,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,0,0,0,0,8,13,14,7,0,0,0,0,0,4, 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,1,3,4,3,1,0,0,0,0,1,3,4,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,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,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,4,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,11,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,11,0,0,0,1,0,0,0,0,0, 0,0,0,0,1,0,5,108,192,228,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,228,193,106,5,0,1,0,0,0,0,0,0,0,2,0,29,199,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,200,30,0,2,0,0,0, 0,0,1,1,7,204,255,249,250,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,250,250,255,205,7,2,1,0,0,0,0,4,0,109,255,249,254,255,249,238,238,238,238,238,238, 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,249,255,255,250,255,107,0,4,0,0, 0,0,2,0,199,254,250,255,175,36,19,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,19,36,175,255,250,254,199,1,2,0,0,0,1,0,11,230,255,254,247,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,27,247,254,255,230,11,0,1,0, 0,1,0,18,238,255,255,237,16,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,2,2,17,237,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,18,0,1,0,0,0,0,1, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,18,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,2,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,5, 6,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,2,0,75,217,220,84,0,4,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,1,1,7,224,255, 255,255,91,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,1,0,7,223,253,249,253,255,91,0,5,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,3,0,82,254, 253,252,253,255,91,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,2,0,91,254,253,252,253,255,91,0,4,0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,4,0, 91,254,253,252,253,255,91,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,4,0,91,255,255,253,253,255,83,0,3,0,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 4,0,81,247,253,252,255,224,7,1,1,0,0,0,0,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,5,0,87,248,253,252,254,224,7,1,1,0,0,0,0,0, 0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,5, 0,96,255,255,253,253,255,86,0,3,1,1,1,1,1,1,1,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,4,0,96,255,253,253,253,255,96,0,5,0,0,0,0,0,0,0, 0,2,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,3,0,96, 255,253,253,253,255,96,0,5,0,4,17,17,17,17,17,17,5,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,3,0,85,255,253,253,253,255,96,0,7,0,76,215,236,238,238,238,238,236, 217,75,0,2,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,1,1,7,223,253, 249,253,255,96,0,5,1,7,224,255,255,255,255,255,255,255,255,225,7,1,1,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,1,0,7,224,255,255,255,96,0,5,1,0,7,224,255,255,255,255,255,255,255, 255,225,7,1,1,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,2,0,76,219, 218,87,0,4,0,0,2,0,76,217,236,238,238,238,238,236,215,77,0,2,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,6,5,0,2,0,0,0,0,0,0,5,17,17,17,17,17,17, 4,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,2,0, 0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,1,0,17,238,255,255,238,17,0,1,0, 0,1,0,17,238,255,255,238,18,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1, 1,0,0,0,0,1,0,18,238,255,255,238,17,0,1,0,0,1,0,17,238,255,255,237,16,2,2,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,16,237,255,255,238,18,0,1,0, 0,1,0,11,229,255,254,247,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,27,247,254,255,230,11,0,1,0,0,0,1,1,202,254,250,255,175,36,19,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,19,36,175,255,250,254,199,0,1,0,0, 0,0,4,0,109,255,250,255,255,249,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, 238,238,238,238,238,238,250,255,255,250,255,109,0,4,0,0,0,0,1,2,6,203,255,250,250,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,250,249,255,204,7,1,1,0,0, 0,0,0,2,0,30,199,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,200,29,0,2,0,0,0,0,0,0,0,1,0,6,107,195,227,238,238,238,238,238,238, 238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,228,192,110,6,0,1,0,0,0,0, 0,0,0,0,0,2,0,0,0,11,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,11,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,4,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,3,1,3,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,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,69,177,202,162,49,0,1,0,0,0,0,0,0,0, 0,1,1,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, 0,91,255,255,255,255,245,57,0,2,0,0,0,0,0,0,0,0,0,12,0,0,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,48,247,253,251,253,248,255,193,2,2,0,0,0,0,0,1, 1,19,177,229,152,8,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,15, 218,255,252,254,255,254,254,235,14,0,1,0,0,0,0,4,0,128,255,255,255,192,15,0,2,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,174,255,251,254,255,255,254,255,237,17,0,1,0,0,0,0,4, 0,128,255,248,250,255,191,10,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,122,255, 251,252,255,167,228,255,255,238,17,0,1,0,0,1,1,1,1,19,203,255,251,250,255,170,0,3,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,3,0,71,255,251,251,255,194,13,236,255,255,238,17,0,1,0,2,0,0,3, 1,0,14,189,255,252,250,255,108,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,3,0,29,234,254,251, 254,235,27,9,241,255,255,238,17,0,1,0,0,5,6,0,2,2,0,11,202,255,251,253,245,36,1,2,0,0,0,0, 0,0,0,0,0,2,3,0,0,0,0,4,198,255,251,251,255,72,0,22,238,255,255,238,17,0,3,0,75,217,220,90, 0,3,4,0,32,237,253,250,255,172,0,3,0,0,0,0,0,0,0,0,2,0,0,8,17,19,17,150,255,251,251,255, 122,0,1,17,238,255,255,238,17,0,1,7,224,255,255,255,114,0,3,4,0,97,255,252,253,252,49,0,3,0,0,0, 0,0,0,2,0,41,171,222,237,238,239,255,254,251,255,174,0,5,0,17,238,255,255,238,17,0,1,7,224,255,249,252, 255,89,0,3,3,0,199,255,248,255,154,0,3,0,0,0,0,0,2,1,39,237,255,255,255,255,255,253,251,255,218,15, 2,3,0,17,238,255,255,238,17,0,4,0,80,248,255,252,253,242,34,1,5,0,77,255,251,255,235,17,0,1,0,0, 0,0,3,0,174,255,248,254,255,255,255,255,255,248,48,0,3,1,0,17,238,255,255,238,17,0,1,2,0,68,247,253, 250,255,166,0,3,1,8,221,255,251,255,76,0,3,0,0,0,1,0,9,228,254,254,254,239,238,238,236,219,78,0,3, 0,1,0,17,238,255,255,238,17,0,1,0,5,0,105,255,252,253,249,34,0,6,0,146,255,247,254,140,0,4,0,0, 0,1,0,18,238,255,255,239,33,17,18,17,5,0,2,0,0,1,0,17,238,255,255,238,17,0,1,0,0,4,0,206, 255,249,255,116,0,8,0,80,255,249,255,191,0,2,0,0,0,1,0,17,238,255,255,238,13,0,0,0,0,2,0,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,4,0,106,255,248,255,184,0,4,0,40,252,254,255,221,6,0,1,0, 0,1,0,17,238,255,255,238,18,1,2,1,1,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,2,0,46, 253,253,255,219,4,0,0,17,237,255,255,239,19,0,1,0,0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,23,242,255,255,233,14,0,0,10,226,255,255,245,28,0,1,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,0,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,1,0,24, 242,255,255,232,14,0,0,9,226,255,255,245,29,0,2,0,0,1,0,17,238,255,255,238,18,1,2,1,1,0,0,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,2,0,45,253,253,255,218,4,0,0,18,238,255,255,239,19,0,1,0, 0,1,0,17,238,255,255,238,13,0,0,0,0,2,0,0,0,1,0,17,238,255,255,238,17,0,1,0,0,4,0,105, 255,248,255,185,0,4,0,41,252,254,255,222,6,0,1,0,0,1,0,18,238,255,255,239,33,17,18,17,5,0,2,0, 0,1,0,17,238,255,255,238,17,0,1,0,0,4,0,205,255,248,255,120,0,8,0,83,255,249,255,191,0,2,0,0, 0,1,0,8,226,254,253,254,239,238,238,236,217,79,0,3,0,1,0,17,238,255,255,238,17,0,1,0,5,0,105,255, 252,253,249,36,0,6,0,144,255,247,254,142,0,4,0,0,0,0,3,0,173,255,248,254,255,255,255,255,255,249,49,0, 3,1,0,17,238,255,255,238,17,0,1,2,0,70,248,252,250,255,166,0,3,1,8,221,255,251,255,74,0,3,0,0, 0,0,2,1,40,235,255,255,255,255,255,253,251,255,219,17,2,3,0,17,238,255,255,238,17,0,4,0,78,248,254,252, 253,242,34,1,6,0,75,255,251,255,236,18,0,1,0,0,0,0,0,2,0,42,170,222,237,238,239,255,254,251,255,175, 0,5,0,17,238,255,255,238,17,0,1,7,224,255,249,252,255,89,0,3,3,0,197,255,248,255,154,0,3,0,0,0, 0,0,0,0,2,0,0,8,17,19,17,151,255,251,251,255,123,0,1,17,238,255,255,238,17,0,1,7,224,255,255,255, 116,0,3,4,0,97,255,252,253,253,51,0,3,0,0,0,0,0,0,0,0,2,3,0,0,0,0,4,199,255,251,252, 255,72,0,22,238,255,255,238,17,0,3,0,76,219,218,91,0,3,4,0,33,237,253,250,255,171,0,3,0,0,0,0, 0,0,0,0,0,0,0,1,1,1,3,0,31,233,254,252,254,236,27,8,241,255,255,238,17,0,1,0,0,6,5,0, 2,2,0,13,204,255,252,252,244,35,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,72,255,252, 251,255,194,14,236,255,255,238,17,0,1,0,2,0,0,3,1,0,14,190,255,252,250,255,111,0,3,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,4,0,122,255,251,252,255,168,228,255,255,238,17,0,1,0,0,1,1,1, 1,20,203,255,251,250,255,166,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,176, 255,251,254,255,255,254,255,237,17,0,1,0,0,0,0,4,0,128,255,248,250,255,195,9,2,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,15,217,255,252,254,255,254,254,234,13,0,1,0,0,0,0,4, 0,128,255,255,255,193,15,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0, 51,247,253,251,253,248,255,192,1,2,0,0,0,0,0,1,1,19,180,229,153,8,0,2,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,89,255,255,255,255,246,56,0,2,0,0,0,0,0,0, 0,0,0,12,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 3,0,69,175,203,164,50,0,1,0,0,0,0,0,0,0,0,1,1,0,3,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,3,3,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; // dibuja un icono del atlas escalandolo. son solo alpha: el color lo pone la // paleta, asi que cambian con el tema sin duplicar nada static void drawIconBitmap(std::uint32_t* output, int x, int y, int size, std::uint32_t color, std::uint32_t offset) { if (output == nullptr || size <= 0) return; for (int dy = 0; dy < size; ++dy) { const int py = y + dy; if (py < 0 || py >= OUTPUT_HEIGHT) continue; const int sy = dy * UI_ICON_SIDE / size; for (int dx = 0; dx < size; ++dx) { const int px = x + dx; if (px < 0 || px >= OUTPUT_WIDTH) continue; const int sx = dx * UI_ICON_SIDE / size; const unsigned alpha = UI_ICON_ALPHA[offset + static_cast(sy * UI_ICON_SIDE + sx)]; if (alpha == 0u) continue; std::uint32_t& destination = output[static_cast(py) * OUTPUT_WIDTH + px]; destination = blendDisplayColor(destination, color, alpha); } } } // -1 si ese icono todavia no tiene bitmap y hay que dibujarlo a mano static int uiIconBitmapOffset(const char* name) { for (const UiIconBitmap& entry : UI_ICON_BITMAPS) { const char* a = entry.name; const char* b = name; while (*a != '\0' && *a == *b) { ++a; ++b; } if (*a == '\0' && *b == '\0') return static_cast(entry.offset); } return -1; } static void drawUiIcon(std::uint32_t* output, int x, int y, int size, std::uint32_t color, UiIcon icon) { if (output == nullptr || size < 12) return; // juego nuevo entero. los de antes eran todos "una caja con cosas dentro" y // a tamaño de fila se parecian demasiado entre si. estos buscan siluetas // distintas: barras, rombo, aguja, bocadillo... // los que ya tienen bitmap de Tabler van por ahi; los que faltan siguen // dibujados a mano hasta que consigamos el png const char* bitmapName = nullptr; switch (icon) { case UiIcon::Display: bitmapName = "device-desktop"; break; case UiIcon::Audio: bitmapName = "volume"; break; case UiIcon::Controls: bitmapName = "device-gamepad"; break; case UiIcon::Developer: bitmapName = "terminal-2"; break; case UiIcon::Help: bitmapName = "info-square-rounded"; break; case UiIcon::Back: bitmapName = "arrow-back-up"; break; case UiIcon::Game: bitmapName = "disc"; break; default: break; } if (bitmapName != nullptr) { const int offset = uiIconBitmapOffset(bitmapName); if (offset >= 0) { drawIconBitmap(output, x, y, size, color, static_cast(offset)); return; } } const int inset = size * 12 / 100; const int box = size - inset * 2; const int ox = x + inset; const int oy = y + inset; auto b = [box](int percent) { return box * percent / 100; }; int stroke = size / 16; if (stroke < 2) stroke = 2; switch (icon) { case UiIcon::Display: { // dos marcos encajados: la imagen del pc-98 dentro de la tele drawOutlineRectangle(output, ox, oy + b(8), box, b(84), stroke, color); fillRectangle(output, ox + b(22), oy + b(32), b(56), b(36), color); break; } case UiIcon::Audio: { // barras de ecualizador, alturas distintas static const int alturas[5] = {40, 74, 100, 58, 26}; for (int barra = 0; barra < 5; ++barra) { const int h = b(alturas[barra]); const int bx = ox + barra * b(21); fillRectangle(output, bx, oy + box - h, b(13), h, color); } break; } case UiIcon::Controls: { // cruceta a la izquierda y rombo de botones a la derecha const int cx = ox + b(24), cy = oy + b(50), arm = b(20); fillRectangle(output, cx - stroke / 2, cy - arm, stroke, arm * 2, color); fillRectangle(output, cx - arm, cy - stroke / 2, arm * 2, stroke, color); const int dx = ox + b(74), dy = oy + b(50), sep = b(22); fillCircle(output, dx, dy - sep, stroke, color); fillCircle(output, dx, dy + sep, stroke, color); fillCircle(output, dx - sep, dy, stroke, color); fillCircle(output, dx + sep, dy, stroke, color); break; } case UiIcon::Emulation: { // aguja de velocimetro: va de "multiplicador de reloj" // esfera con aguja. nada de tapar media circunferencia con un // rectangulo de color 0: eso pinta negro, no borra drawIconRing(output, ox + box / 2, oy + b(54), b(46), stroke, color); drawIconLine(output, ox + box / 2, oy + b(54), ox + b(80), oy + b(20), stroke, color); fillCircle(output, ox + box / 2, oy + b(54), stroke + 1, color); fillRectangle(output, ox + b(30), oy + b(96), b(40), stroke, color); break; } case UiIcon::Interface: { // bloques de maquetacion: una columna estrecha y otra ancha fillRectangle(output, ox, oy + b(10), b(28), b(80), color); drawOutlineRectangle(output, ox + b(38), oy + b(10), b(62), b(34), stroke, color); drawOutlineRectangle(output, ox + b(38), oy + b(56), b(62), b(34), stroke, color); break; } case UiIcon::Developer: { // terminal: un prompt y un cursor drawOutlineRectangle(output, ox, oy + b(12), box, b(76), stroke, color); drawIconLine(output, ox + b(18), oy + b(34), ox + b(38), oy + b(50), stroke, color); drawIconLine(output, ox + b(38), oy + b(50), ox + b(18), oy + b(66), stroke, color); fillRectangle(output, ox + b(50), oy + b(64), b(30), stroke, color); break; } case UiIcon::Help: { // bocadillo de dialogo con la colita drawOutlineRectangle(output, ox, oy + b(8), box, b(62), stroke, color); drawIconLine(output, ox + b(24), oy + b(70), ox + b(18), oy + b(94), stroke, color); drawIconLine(output, ox + b(18), oy + b(94), ox + b(44), oy + b(70), stroke, color); fillRectangle(output, ox + b(22), oy + b(28), b(56), stroke, color); fillRectangle(output, ox + b(22), oy + b(46), b(38), stroke, color); break; } case UiIcon::Back: { // chevron a secas, sin asta drawIconLine(output, ox + b(62), oy + b(8), ox + b(24), oy + b(50), stroke + 1, color); drawIconLine(output, ox + b(24), oy + b(50), ox + b(62), oy + b(92), stroke + 1, color); break; } case UiIcon::Game: { // un disco con su agujero. probe una pila de tres y se solapaban // hasta quedar en un borron drawIconRing(output, ox + box / 2, oy + box / 2, box / 2 - 1, stroke, color); drawIconRing(output, ox + box / 2, oy + box / 2, b(20), stroke, color); drawIconLine(output, ox + b(66), oy + b(20), ox + b(84), oy + b(38), stroke, color); break; } case UiIcon::Keyboard: { // una tecla suelta, en perspectiva plana drawOutlineRectangle(output, ox + b(8), oy + b(8), b(84), b(70), stroke, color); fillRectangle(output, ox + b(24), oy + b(30), b(52), b(26), color); fillRectangle(output, ox, oy + b(84), box, stroke, color); break; } case UiIcon::Play: { // triangulo dentro de un circulo drawIconRing(output, ox + box / 2, oy + box / 2, box / 2 - 1, stroke, color); for (int paso = 0; paso <= b(34); ++paso) { const int h = b(46) - paso * 136 / 100; if (h <= 0) break; fillRectangle(output, ox + b(38) + paso, oy + box / 2 - h / 2, 1, h, color); } break; } case UiIcon::Folder: { // caja de archivo con la tapa marcada fillRectangle(output, ox, oy + b(20), box, b(20), color); drawOutlineRectangle(output, ox + b(8), oy + b(40), b(84), b(48), stroke, color); fillRectangle(output, ox + b(38), oy + b(58), b(24), stroke, color); break; } } } static UiIcon settingsRootIcon(unsigned row) { switch (row) { case 0: return UiIcon::Display; case 1: return UiIcon::Audio; case 2: return UiIcon::Controls; case 3: return UiIcon::Emulation; case 4: return UiIcon::Interface; case 5: return UiIcon::Developer; case 6: return UiIcon::Help; default: return UiIcon::Back; } } static const char* settingsRowLabel(SettingsPage page, unsigned row) { static const char* rootEs[] = { "Pantalla", "Audio", "Controles", "Emulación", "Interfaz", "Desarrollador", "Ayuda e información", "Atrás" }; static const char* rootEn[] = { "Display", "Audio", "Controls", "Emulation", "Interface", "Developer", "Help and information", "Back" }; static const char* displayEs[] = {"Modo de pantalla", "Atrás"}; static const char* displayEn[] = {"Screen mode", "Back"}; static const char* audioEs[] = {"Salida de audio", "Volumen", "Atrás"}; static const char* audioEn[] = {"Audio output", "Volume", "Back"}; static const char* controlsEs[] = { "Perfil predeterminado", "Configurar botones", "Stick izquierdo", "Zona muerta", "Atrás" }; static const char* controlsEn[] = { "Default profile", "Configure buttons", "Left stick", "Dead zone", "Back" }; static const char* emulationEs[] = {"Multiplicador de CPU", "Inicio rápido", "Atrás"}; static const char* emulationEn[] = {"CPU multiplier", "Fast boot", "Back"}; static const char* interfaceEs[] = {"Idioma", "Tema", "Animaciones", "Atrás"}; static const char* interfaceEn[] = {"Language", "Theme", "Animations", "Back"}; static const char* developerEs[] = { "FPS y profiler", "Overlay del mando", "Overlay de audio", "Diagnóstico", "Restablecer ajustes", "Atrás" }; static const char* developerEn[] = { "FPS and profiler", "Controller overlay", "Audio overlay", "Diagnostics", "Reset settings", "Back" }; static const char* supportEs[] = { "Guía de primera configuración", "Guía de controles", "Solución de problemas", "Estado de archivos del sistema", "Créditos", "Sobre este proyecto", "Atrás" }; static const char* supportEn[] = { "First-time setup guide", "Controls guide", "Troubleshooting", "System file status", "Credits", "About this project", "Back" }; const char** rows = interfaceIsEnglish() ? rootEn : rootEs; switch (page) { case SettingsPage::Display: rows = interfaceIsEnglish() ? displayEn : displayEs; break; case SettingsPage::Audio: rows = interfaceIsEnglish() ? audioEn : audioEs; break; case SettingsPage::Controls: rows = interfaceIsEnglish() ? controlsEn : controlsEs; break; case SettingsPage::Emulation: rows = interfaceIsEnglish() ? emulationEn : emulationEs; break; case SettingsPage::Interface: rows = interfaceIsEnglish() ? interfaceEn : interfaceEs; break; case SettingsPage::Developer: rows = interfaceIsEnglish() ? developerEn : developerEs; break; case SettingsPage::Support: rows = interfaceIsEnglish() ? supportEn : supportEs; break; default: break; } return rows[row]; } static void settingsRowValue(SettingsPage page, unsigned row, char* destination, std::size_t capacity) { if (destination == nullptr || capacity == 0) return; destination[0] = '\0'; const char* enabled = tr("Activado", "Enabled"); const char* disabled = tr("Desactivado", "Disabled"); if (page == SettingsPage::Display && row == 0) { std::snprintf(destination, capacity, "%s", displayModeName()); } else if (page == SettingsPage::Audio && row == 0) { std::snprintf(destination, capacity, "%s", g_audioEnabled ? enabled : disabled); } else if (page == SettingsPage::Audio && row == 1) { std::snprintf(destination, capacity, "%d%%", g_audioVolumePercent); } else if (page == SettingsPage::Controls && row == 0) { std::snprintf(destination, capacity, "%s", controlProfileDisplayName(g_defaultControls.profile)); } else if (page == SettingsPage::Controls && row == 1) { std::snprintf(destination, capacity, "%s", tr("Abrir", "Open")); } else if (page == SettingsPage::Controls && row == 2) { std::snprintf(destination, capacity, "%s", g_analogMovementEnabled ? enabled : disabled); } else if (page == SettingsPage::Controls && row == 3) { std::snprintf(destination, capacity, "%d%%", g_padStickDeadzone * 100 / 255); } else if (page == SettingsPage::Emulation && row == 0) { std::snprintf(destination, capacity, "x%d", g_cpuMultiplier); } else if (page == SettingsPage::Emulation && row == 1) { std::snprintf(destination, capacity, "%s", g_fastMemoryCheck ? enabled : disabled); } else if (page == SettingsPage::Interface && row == 0) { std::snprintf(destination, capacity, "%s", interfaceIsEnglish() ? "English" : "Español"); } else if (page == SettingsPage::Interface && row == 1) { std::snprintf(destination, capacity, "%s", uiThemeName()); } else if (page == SettingsPage::Interface && row == 2) { std::snprintf(destination, capacity, "%s", g_uiAnimationsEnabled ? enabled : disabled); } else if (page == SettingsPage::Developer && row == 0) { std::snprintf(destination, capacity, "%s", g_showPerformanceOverlay ? enabled : disabled); } else if (page == SettingsPage::Developer && row == 1) { std::snprintf(destination, capacity, "%s", g_showPadProbeOverlay ? enabled : disabled); } else if (page == SettingsPage::Developer && row == 2) { std::snprintf(destination, capacity, "%s", g_showAudioOverlay ? enabled : disabled); } } static void drawLineSimple(std::uint32_t* output, int x0, int y0, int x1, int y1, int thickness, std::uint32_t color) { const int dx = std::abs(x1 - x0); const int sx = x0 < x1 ? 1 : -1; const int dy = -std::abs(y1 - y0); const int sy = y0 < y1 ? 1 : -1; int error = dx + dy; for (;;) { fillRectangle(output, x0 - thickness / 2, y0 - thickness / 2, thickness, thickness, color); if (x0 == x1 && y0 == y1) break; const int twice = error * 2; if (twice >= dy) { error += dy; x0 += sx; } if (twice <= dx) { error += dx; y0 += sy; } } } enum class FooterButton { Cross, Circle, Square, Triangle, Options, StickCombo }; static const char* keyboardFooterKey(FooterButton button) { switch (button) { case FooterButton::Cross: return "ENTER"; case FooterButton::Circle: return "ESC"; case FooterButton::Square: return "C"; case FooterButton::Triangle: return "F5"; case FooterButton::Options: return "F10"; case FooterButton::StickCombo: return "F12"; default: return "KEY"; } } static int drawFooterHintPolished( std::uint32_t* output, int x, int y, FooterButton button, const char* label) { const std::uint32_t white = g_ui.textPrimary; const std::uint32_t dim = g_ui.textMuted; if (g_activeInputLayout == InputLayout::Keyboard) { const char* key = keyboardFooterKey(button); const int textWidth = uiTextWidth(key, 17); const int keyWidth = std::max(48, textWidth + 22); fillRectangle(output, x, y + 3, keyWidth, 32, g_ui.panel); drawOutlineRectangle(output, x, y + 3, keyWidth, 32, 1, dim); drawUiText(output, x + (keyWidth - textWidth) / 2, y + 8, 17, white, key, keyWidth - 8); drawUiText(output, x + keyWidth + 14, y + 6, 22, white, label); return x + keyWidth + 14 + uiTextWidth(label, 22) + 38; } int iconWidth = 42; const int centerY = y + 18; if (button == FooterButton::Cross || button == FooterButton::Circle || button == FooterButton::Square || button == FooterButton::Triangle) { // los cuatro botones tambien son de Tabler ahora, en vez de circulos y // rayas dibujados a mano que no acababan de cuadrar entre si const char* name = "playstation-x"; if (button == FooterButton::Circle) name = "playstation-circle"; else if (button == FooterButton::Square) name = "playstation-square"; else if (button == FooterButton::Triangle) name = "playstation-triangle"; const int offset = uiIconBitmapOffset(name); if (offset >= 0) { drawIconBitmap(output, x, centerY - 17, 34, white, static_cast(offset)); } } else if (button == FooterButton::Options) { fillRectangle(output, x, y + 5, 78, 28, g_ui.panel); drawOutlineRectangle(output, x, y + 5, 78, 28, 1, dim); drawUiText(output, x + 9, y + 8, 18, white, "OPTIONS", 64); iconWidth = 92; } else { fillRectangle(output, x, y + 4, 40, 30, g_ui.panel); fillRectangle(output, x + 56, y + 4, 40, 30, g_ui.panel); drawUiText(output, x + 8, y + 8, 18, white, "L3"); drawUiText(output, x + 64, y + 8, 18, white, "R3"); drawUiText(output, x + 44, y + 8, 18, dim, "+"); iconWidth = 110; } drawUiText(output, x + iconWidth, y + 6, 22, white, label); return x + iconWidth + uiTextWidth(label, 22) + 38; } static void drawFooterBarBase(std::uint32_t* output) { blendRectangle(output, 0, 944, OUTPUT_WIDTH, 136, g_ui.panel, 230); fillRectangle(output, 0, 944, OUTPUT_WIDTH, 1, g_ui.textDim); } static int drawWrappedUiText(std::uint32_t* output, int x, int y, int pixelHeight, int lineSpacing, int maxWidth, std::uint32_t color, const char* text, int maxLines); // el texto de cada documento, fuera del dibujado. la pantalla solo maqueta static const char* infoDocumentTitle(InfoDocument document) { switch (document) { case InfoDocument::Setup: return tr("Primera configuración", "First-time setup"); case InfoDocument::Controls: return tr("Controles", "Controls"); case InfoDocument::Troubleshooting: return tr("Solución de problemas", "Troubleshooting"); case InfoDocument::SystemFiles: return tr("Archivos del sistema", "System files"); case InfoDocument::Credits: return tr("Créditos", "Credits"); case InfoDocument::ProjectStory: return tr("Sobre este proyecto", "About this project"); } return tr("Ayuda", "Help"); } static const char* infoDocumentBody(InfoDocument document) { switch (document) { case InfoDocument::Setup: return tr( "Copia las ROM del PC-98 (bios.rom, itf.rom, sound.rom) y la fuente a:\n/data/np2kai-runtime/system/np2kai/\n\nY tus juegos a:\n/data/np2kai-runtime/games/\n\nPor FTP con GoldHEN (puerto 2121) o desde un USB.\n\nDespués pulsa Triángulo para volver a comprobarlo.\n\nLas ROM y los juegos no vienen incluidos, los tienes que poner tú.", "Copy the PC-98 ROMs (bios.rom, itf.rom, sound.rom) and the font to:\n/data/np2kai-runtime/system/np2kai/\n\nAnd your games to:\n/data/np2kai-runtime/games/\n\nOver FTP with GoldHEN (port 2121) or from a USB drive.\n\nThen press Triangle to check again.\n\nROMs and games are not included, you have to supply them yourself."); case InfoDocument::Controls: return tr( "Cada botón del mando se asigna a una tecla del PC-98.\n\nAjustes > Controles cambia el mapeo global.\nPausa > Controles cambia solo el juego abierto.\n\nDentro del editor:\nX abre el selector de teclas\nL1 / R1 cambian de categoría\nCuadrado deja el botón sin asignar\n\nEl stick derecho mueve el ratón y R2/L2 son los clics.\n\nUn teclado USB también vale: en el selector, pulsa la tecla que quieras y se asigna sola.", "Each controller button maps to a PC-98 key.\n\nSettings > Controls changes the global mapping.\nPause > Controls changes only the open game.\n\nInside the editor:\nCross opens the key selector\nL1 / R1 switch category\nSquare clears the button\n\nThe right stick moves the mouse and R2/L2 are the clicks.\n\nA USB keyboard works too: in the selector, press the key you want and it gets assigned."); case InfoDocument::Troubleshooting: return tr( "Si un juego no arranca, revisa que el archivo se lea bien y que la extensión sea compatible, y vuelve a escanear la biblioteca.\n\nSi falta un archivo del sistema, la pantalla de error te dice cuál y dónde va.\n\nSi el audio falla puedes reintentarlo o seguir sin sonido.\n\nTodo queda apuntado en:\n/data/np2kai-runtime/logs/latest.log\n\nSi la aplicación se cierra de golpe, la última línea de ese archivo suele ser la pista.", "If a game does not start, check that the file is readable and the extension is supported, then rescan the library.\n\nIf a system file is missing, the error screen tells you which one and where it goes.\n\nIf audio fails you can retry or keep going without sound.\n\nEverything is logged to:\n/data/np2kai-runtime/logs/latest.log\n\nIf the app closes suddenly, the last line of that file is usually the clue."); case InfoDocument::Credits: return tr( "Este port no seria posible sin el trabajo de otra gente. Todo lo de terceros esta aqui.\n\n" "EMULADOR\n" "NP2kai, de AZO234 (MIT). Es el emulador de verdad; yo solo lo he portado.\n" "Neko Project II, de Yui, del que desciende NP2kai.\n\n" "DENTRO DEL EMULADOR\n" "fmgen, de cisc: sintesis FM.\n" "Berkeley SoftFloat, de John R. Hauser: coma flotante.\n" "i386c, dosbox-fpu y componentes de MAME: CPU y periféricos.\n" "zlib, C86CTL, SCCI, GD54XX, TGUI9680, LGY98.\n" "Los textos completos estan en third_party/NP2kai/LICENSES/.\n\n" "CONSOLA\n" "OpenOrbis PS4 Toolchain: sin el no habria nada que compilar.\n" "GoldHEN: instalacion y FTP.\n" "SDL2 (zlib) y la API libretro.\n\n" "INTERFAZ\n" "Iconos de Tabler, de Pawel Kuna (MIT).\n" "Tipografia Carlito, de Lukasz Dziedzic (SIL OFL).\n" "Tipografia Poppins, de Indian Type Foundry y Jonny Pinhorn (SIL OFL).\n\n" "Las ROM del PC-98 y los juegos no se distribuyen: los pone cada uno.", "This port would not exist without other people's work. Everything third-party is here.\n\n" "EMULATOR\n" "NP2kai, by AZO234 (MIT). It is the actual emulator; I only ported it.\n" "Neko Project II, by Yui, which NP2kai descends from.\n\n" "INSIDE THE EMULATOR\n" "fmgen, by cisc: FM synthesis.\n" "Berkeley SoftFloat, by John R. Hauser: floating point.\n" "i386c, dosbox-fpu and MAME components: CPU and peripherals.\n" "zlib, C86CTL, SCCI, GD54XX, TGUI9680, LGY98.\n" "Full texts are in third_party/NP2kai/LICENSES/.\n\n" "CONSOLE\n" "OpenOrbis PS4 Toolchain: nothing would build without it.\n" "GoldHEN: installing and FTP.\n" "SDL2 (zlib) and the libretro API.\n\n" "INTERFACE\n" "Tabler icons, by Pawel Kuna (MIT).\n" "Carlito typeface, by Lukasz Dziedzic (SIL OFL).\n" "Poppins typeface, by Indian Type Foundry and Jonny Pinhorn (SIL OFL).\n\n" "PC-98 ROMs and games are not distributed: you supply your own."); // texto del autor: no reescribir case InfoDocument::ProjectStory: return tr( "Bueno, este proyecto se realizo especialmente por pura cabezoneria por querer jugar los Touhou de PC-98 en PS4... y como no habia NADA relacionado a la emulacion de PC-98, pues he decidido intentar hacer mi propio port del emulador teniendo como base NP2Kai.\n\nHa sido una experiencia... curiosa, ya que nunca he hecho nada similar, y esta es mi primera vez haciendo algo de esta magnitud, asi que me ha servido bastante como experiencia para portear algunas cosas a consolas...\n\nY ya que estas... si quieres mas info a futuro, en Twitter/X o TikTok me encontraras como \"Emii_in_Gensokyo\".", "Well, this project was made mostly out of pure stubbornness because I wanted to play the PC-98 Touhou games on PS4... and since there was NOTHING related to PC-98 emulation, I decided to try making my own emulator port using NP2Kai as the base.\n\nIt has been a... curious experience. I had never done anything similar, and this is my first time working on something of this scale, so it has taught me quite a lot about porting software to consoles...\n\nAnd since you are here... for future information, you can find me on Twitter/X or TikTok as \"Emii_in_Gensokyo\"."); default: break; } return ""; } // texto del panel de la derecha. es contenido nuevo: antes la mitad derecha de // la pantalla estaba vacia en todas las pantallas, asi que aprovecho para // explicar que hace cada cosa sin tener que abrir la guia static const char* settingsRowHelp(SettingsPage page, unsigned row) { switch (page) { case SettingsPage::Root: switch (row) { case 0: return tr("Como se ve la imagen del PC-98 en la tele: escala, proporcion y bordes.", "How the PC-98 image sits on your TV: scale, aspect and borders."); case 1: return tr("Volumen y salida de sonido del emulador.", "Emulator volume and sound output."); case 2: return tr("Perfil de mando global y editor de teclas.", "Global controller profile and key editor."); case 3: return tr("Velocidad de la CPU emulada y opciones del nucleo.", "Emulated CPU speed and core options."); case 4: return tr("Idioma, tema de color y animaciones.", "Language, colour theme and animations."); case 5: return tr("Contadores y herramientas para ver que va lento.", "Counters and tools to find what is slow."); case 6: return tr("Guias, estado de los archivos del sistema y sobre el proyecto.", "Guides, system file status and about the project."); default: return tr("Volver a la biblioteca.", "Back to the library."); } case SettingsPage::Display: return row == 0 ? tr("Entero mantiene los pixeles exactos y deja bordes. Ajustado llena mas pantalla.", "Integer keeps pixels exact and leaves borders. Fitted fills more of the screen.") : tr("Suaviza el escalado. Con pixel art suele verse mejor apagado.", "Smooths scaling. With pixel art it usually looks better off."); case SettingsPage::Audio: if (row == 0) return tr("Corta el sonido del emulador por completo.", "Mutes the emulator completely."); if (row == 1) return tr("Volumen de salida.", "Output volume."); return tr("El PC-98 saca 44100 Hz y la consola pide 48000, asi que hay un remuestreo por medio.", "The PC-98 outputs 44100 Hz and the console wants 48000, so there is a resample in between."); case SettingsPage::Controls: if (row == 0) return tr("Punto de partida del mapeo. Touhou es el que traia la primera version.", "Starting point for the mapping. Touhou is the one the first version shipped with."); if (row == 1) return tr("Asigna cada boton del mando a una tecla del PC-98.", "Assign each controller button to a PC-98 key."); if (row == 2) return tr("El stick derecho mueve el raton. R2 y L2 son los clics.", "The right stick moves the mouse. R2 and L2 are the clicks."); return tr("L3 + R3 abre la pausa y el touchpad el menu interno. Esos dos no se pueden reasignar.", "L3 + R3 opens the pause menu and the touchpad the internal one. Those two cannot be remapped."); case SettingsPage::Emulation: if (row == 0) return tr("Cuantos ciclos emula por frame. Subirlo pide mas CPU: mira antes el hueco libre en Diagnostico.", "How many cycles it emulates per frame. Raising it costs CPU: check the slack in Diagnostics first."); if (row == 1) return tr("Salta comprobaciones de memoria. Va mas rapido pero algun juego puede no arrancar.", "Skips memory checks. Faster, but some games may not start."); return tr("Modelo de PC-98 y placa de sonido que se emulan.", "Emulated PC-98 model and sound board."); case SettingsPage::Interface: if (row == 0) return tr("Castellano o ingles.", "Spanish or English."); if (row == 1) return tr("Gris, azul pizarra, grafito o medianoche.", "Grey, slate blue, graphite or midnight."); if (row == 2) return tr("Los deslizamientos del menu. Apagarlo no acelera el emulador.", "Menu sliding. Turning it off does not speed up the emulator."); return tr("Volver.", "Back."); case SettingsPage::Developer: if (row == 0) return tr("Barra de FPS y tiempos encima del juego.", "FPS bar and timings on top of the game."); if (row == 1) return tr("Estado de los botones del mando en crudo.", "Raw controller button state."); if (row == 2) return tr("Cola de audio y cortes.", "Audio queue and dropouts."); if (row == 3) return tr("Reparto del tiempo por frame: cuanto se va en el nucleo, en dibujar y cuanto sobra.", "Per-frame time breakdown: core, drawing, and what is left over."); if (row == 4) return tr("Deja todos los ajustes como venian de fabrica.", "Puts every setting back to how it shipped."); return tr("Volver.", "Back."); case SettingsPage::Support: if (row == 0) return tr("Que archivos hacen falta y donde van.", "Which files are needed and where they go."); if (row == 1) return tr("Como funciona el mapeo de controles.", "How the control mapping works."); if (row == 2) return tr("Que hacer cuando algo no arranca.", "What to do when something does not start."); if (row == 3) return tr("Comprueba si estan las ROM y la fuente.", "Checks whether the ROMs and font are present."); if (row == 4) return tr("Todo lo de terceros que hace posible esto.", "All the third-party work behind this."); if (row == 5) return tr("De donde sale todo esto.", "Where all this comes from."); return tr("Volver.", "Back."); } return ""; } // ============================================================================ // sistema de maquetacion // ============================================================================ // antes cada pantalla se maquetaba a mano con numeros sueltos, y por eso todas // acababan igual: titulo arriba a la izquierda, lista centrada, pie abajo, y // medio pantallazo vacio a la derecha sin usar. // // ahora hay una rejilla y unas piezas comunes. lo importante del cambio es que // la pantalla se parte en dos: a la izquierda la lista, a la derecha un panel // que cuenta lo que hay seleccionado. asi se llena el hueco que sobraba y de // paso la ficha del juego deja de ser una pantalla aparte namespace ui { constexpr int MARGIN = 100; constexpr int RIGHT_EDGE = OUTPUT_WIDTH - MARGIN; // 1820 // cabecera constexpr int CRUMB_Y = 62; constexpr int TITLE_Y = 86; constexpr int TITLE_SIZE = 46; constexpr int RULE_Y = 178; // zona de contenido constexpr int CONTENT_Y = 218; constexpr int CONTENT_END = 928; // columnas constexpr int LIST_X = MARGIN; constexpr int LIST_W = 640; constexpr int PANEL_X = MARGIN + 700; // 800 constexpr int PANEL_W = RIGHT_EDGE - PANEL_X; // 1020 // filas constexpr int ROW_H = 66; constexpr int ROW_GAP = 4; constexpr int ROW_LABEL = 26; // pie constexpr int FOOTER_RULE = 962; constexpr int FOOTER_Y = 994; inline int rowsThatFit(int height) { return height / (ROW_H + ROW_GAP); } } // namespace ui // rectangulo con las esquinas redondeadas. lo uso para el foco y los paneles; // las esquinas en angulo recto son lo que mas cantaba del diseño viejo static void uiRoundedRect(std::uint32_t* output, int x, int y, int width, int height, int radius, std::uint32_t color) { if (output == nullptr || width <= 0 || height <= 0) return; if (radius * 2 > height) radius = height / 2; if (radius * 2 > width) radius = width / 2; if (radius <= 0) { fillRectangle(output, x, y, width, height, color); return; } fillRectangle(output, x + radius, y, width - radius * 2, height, color); fillRectangle(output, x, y + radius, radius, height - radius * 2, color); fillRectangle(output, x + width - radius, y + radius, radius, height - radius * 2, color); fillCircle(output, x + radius, y + radius, radius, color); fillCircle(output, x + width - radius - 1, y + radius, radius, color); fillCircle(output, x + radius, y + height - radius - 1, radius, color); fillCircle(output, x + width - radius - 1, y + height - radius - 1, radius, color); } // panel de fondo para la columna derecha. muy poca opacidad: tiene que // separarse del fondo sin convertirse en una caja static void uiPanel(std::uint32_t* output, int x, int y, int width, int height) { if (output == nullptr) return; for (int row = 0; row < height; ++row) { const int py = y + row; if (py < 0 || py >= OUTPUT_HEIGHT) continue; for (int px = x; px < x + width; ++px) { if (px < 0 || px >= OUTPUT_WIDTH) continue; std::uint32_t* pixel = output + static_cast(py) * OUTPUT_WIDTH + px; *pixel = blendDisplayColor(*pixel, g_ui.panel, 96u); } } } // cabecera comun: migaja pequeña arriba y titulo grande debajo, con la linea // separadora metida en los margenes en vez de ir de borde a borde static void uiHeader(std::uint32_t* output, const char* crumb, const char* title) { if (crumb != nullptr && crumb[0] != '\0') { drawUiText(output, ui::MARGIN + 3, ui::CRUMB_Y, 19, g_ui.textDim, crumb, 900); } drawUiText(output, ui::MARGIN, ui::TITLE_Y, ui::TITLE_SIZE, g_ui.textPrimary, title, 1400, 255u, UiFace::Display); fillRectangle(output, ui::MARGIN, ui::RULE_Y, ui::RIGHT_EDGE - ui::MARGIN, 1, g_ui.line); } // el foco: relleno redondeado mas una barra brillante pegada al borde // izquierdo. es lo que hace que se lea de un vistazo donde estas sin tener que // pintar un recuadro alrededor de toda la fila static void uiFocusRow(std::uint32_t* output, int x, int y, int width, int height) { // al pulsar crece unos pixeles y la barra lateral se ensancha. dura poco a // proposito: tiene que sentirse como un golpe, no como una animacion const int grow = uiRound(g_uiPress * 5.0f); const int bar = 5 + uiRound(g_uiPress * 4.0f); uiRoundedRect(output, x - grow, y - grow / 2, width + grow * 2, height + grow, 6, g_ui.focusFill); if (g_uiPress > 0.02f) { // un borde claro que aparece y se va con el golpe const unsigned alpha = static_cast(g_uiPress * 150.0f); blendRectangle(output, x - grow, y - grow / 2, width + grow * 2, 2, g_ui.focusOutline, alpha); blendRectangle(output, x - grow, y + height + grow / 2 - 2, width + grow * 2, 2, g_ui.focusOutline, alpha); } uiRoundedRect(output, x - grow, y + 6 - grow / 2, bar, height + grow - 12, 2, g_ui.focusOutline); } // una fila de lista: icono opcional, etiqueta, y valor alineado a la derecha static void uiRow(std::uint32_t* output, int x, int y, int width, bool selected, const UiIcon* icon, const char* label, const char* value) { const std::uint32_t labelColor = selected ? g_ui.textPrimary : g_ui.textMuted; int textX = x + 26; if (icon != nullptr) { drawUiIcon(output, x + 24, y + (ui::ROW_H - 30) / 2, 30, labelColor, *icon); textX = x + 72; } drawUiText(output, textX, y + (ui::ROW_H - ui::ROW_LABEL) / 2 - 2, ui::ROW_LABEL, labelColor, label, width - (textX - x) - 40); if (value != nullptr && value[0] != '\0') { const int valueWidth = uiTextWidth(value, 22); drawUiText(output, x + width - valueWidth - 26, y + (ui::ROW_H - 22) / 2 - 1, 22, selected ? g_ui.textPrimary : g_ui.textDim, value, 300); } } // titulo dentro del panel de la derecha static int uiPanelTitle(std::uint32_t* output, int y, const char* text) { drawUiText(output, ui::PANEL_X + 44, y, 38, g_ui.textPrimary, text, ui::PANEL_W - 88, 255u, UiFace::Display); fillRectangle(output, ui::PANEL_X + 44, y + 58, 52, 2, g_ui.focusOutline); return y + 86; } // pareja etiqueta/valor dentro del panel static int uiPanelField(std::uint32_t* output, int y, const char* label, const char* value, std::uint32_t color) { drawUiText(output, ui::PANEL_X + 44, y, 21, g_ui.textDim, label, 400); drawUiText(output, ui::PANEL_X + 44, y + 28, 29, color, value, ui::PANEL_W - 88); return y + 74; } // parrafo dentro del panel static int uiPanelText(std::uint32_t* output, int y, const char* text, int maxLines) { drawWrappedUiText(output, ui::PANEL_X + 44, y, 25, 37, ui::PANEL_W - 88, g_ui.textMuted, text, maxLines); return y + 37 * maxLines; } // barra de desplazamiento fina, pegada a la columna static void uiScrollBar(std::uint32_t* output, int x, int y, int height, unsigned total, unsigned visible, float scrollPixels, int rowPitch) { if (total <= visible) return; const int maxScroll = static_cast(total - visible) * rowPitch; if (maxScroll <= 0) return; fillRectangle(output, x, y, 3, height, g_ui.panel); int thumb = height * static_cast(visible) / static_cast(total); if (thumb < 40) thumb = 40; float ratio = scrollPixels / static_cast(maxScroll); if (ratio < 0.0f) ratio = 0.0f; if (ratio > 1.0f) ratio = 1.0f; const int thumbY = y + static_cast((height - thumb) * ratio); uiRoundedRect(output, x, thumbY, 3, thumb, 1, g_ui.textDim); } // pie: linea fina y las pistas de botones static void uiFooter(std::uint32_t* output) { fillRectangle(output, ui::MARGIN, ui::FOOTER_RULE, ui::RIGHT_EDGE - ui::MARGIN, 1, g_ui.line); } static bool presentSettingsScreen(SettingsPage page, unsigned selection, int contentOffsetX = 0) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; g_settingsEnter = uiApproachUnit(g_settingsEnter, 1.0f, UI_TAU_SCREEN); contentOffsetX += uiRound((1.0f - g_settingsEnter) * g_settingsEnterFrom * 180.0f); drawPS4StyleBackground(output); uiHeader(output, page == SettingsPage::Root ? "NP2kai PS4" : tr("Ajustes", "Settings"), settingsPageTitle(page)); const unsigned rows = settingsRowCount(page); const int listX = ui::LIST_X + contentOffsetX; // panel de la derecha: cuenta que hace la fila que tienes encima uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, ui::CONTENT_END - ui::CONTENT_Y); int panelY = ui::CONTENT_Y + 52; panelY = uiPanelTitle(output, panelY, settingsRowLabel(page, selection)); char value[160] {}; settingsRowValue(page, selection, value, sizeof(value)); if (value[0] != '\0') { panelY = uiPanelField(output, panelY, tr("Ahora", "Current"), value, g_ui.textPrimary); } const char* ayuda = settingsRowHelp(page, selection); if (ayuda != nullptr && ayuda[0] != '\0') { uiPanelText(output, panelY + 6, ayuda, 6); } // lista de la izquierda const int rowPitch = ui::ROW_H + ui::ROW_GAP; const int focusTarget = ui::CONTENT_Y + static_cast(selection) * rowPitch; if (g_settingsFocusY < 0.0f) g_settingsFocusY = static_cast(focusTarget); g_settingsFocusY = uiApproach(g_settingsFocusY, static_cast(focusTarget), UI_TAU_FOCUS); uiFocusRow(output, listX, uiRound(g_settingsFocusY), ui::LIST_W, ui::ROW_H); for (unsigned row = 0; row < rows; ++row) { const int rowY = ui::CONTENT_Y + static_cast(row) * rowPitch; if (rowY > ui::CONTENT_END - ui::ROW_H) break; char rowValue[160] {}; settingsRowValue(page, row, rowValue, sizeof(rowValue)); const UiIcon icon = settingsRootIcon(row); uiRow(output, listX, rowY, ui::LIST_W, row == selection, page == SettingsPage::Root ? &icon : nullptr, settingsRowLabel(page, row), rowValue); } uiFooter(output); int footerX = ui::MARGIN; footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, page == SettingsPage::Root ? tr("Abrir", "Open") : tr("Cambiar", "Change")); drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Atrás", "Back")); if (g_settingsStatus[0] != '\0') { const int statusWidth = uiTextWidth(g_settingsStatus, 20); drawUiText(output, ui::RIGHT_EDGE - statusWidth, ui::FOOTER_Y + 6, 20, g_ui.textDim, g_settingsStatus, 600); } return uiFrameSubmit(); } // igual que en la biblioteca: solo marca desde donde entra y el bucle la mueve static bool animateSettingsScreen(SettingsPage page, unsigned selection, int direction) { g_settingsEnterFrom = direction; g_settingsEnter = g_uiAnimationsEnabled ? 0.0f : 1.0f; g_settingsFocusY = -1.0f; return presentSettingsScreen(page, selection, 0); } static bool runDiagnosticsScreen(); static void changeSetting(SettingsPage page, unsigned row, int direction) { if (direction == 0) direction = 1; if (page == SettingsPage::Display && row == 0) { int value = static_cast(g_displayMode) + direction; if (value < 0) value = 2; if (value > 2) value = 0; g_displayMode = static_cast(value); } else if (page == SettingsPage::Audio && row == 0) { g_audioEnabled = !g_audioEnabled; } else if (page == SettingsPage::Audio && row == 1) { g_audioVolumePercent += direction * 25; if (g_audioVolumePercent < 0) g_audioVolumePercent = 100; if (g_audioVolumePercent > 100) g_audioVolumePercent = 0; } else if (page == SettingsPage::Controls && row == 0) { int profile = static_cast(g_defaultControls.profile); if (profile < 0 || profile > 2) profile = 1; profile += direction; if (profile < 0) profile = 2; if (profile > 2) profile = 0; applyControlPreset(g_defaultControls, static_cast(profile)); if (!g_activeGameHasControlOverride) g_activeControls = g_defaultControls; setSettingsStatus("Perfil predeterminado cambiado", "Default profile changed"); } else if (page == SettingsPage::Controls && row == 2) { g_analogMovementEnabled = !g_analogMovementEnabled; } else if (page == SettingsPage::Controls && row == 3) { static const int values[] = {32, 48, 64, 80}; int index = 0; for (int i = 0; i < 4; ++i) if (values[i] == g_padStickDeadzone) index = i; index += direction; if (index < 0) index = 3; if (index > 3) index = 0; g_padStickDeadzone = values[index]; } else if (page == SettingsPage::Emulation && row == 0) { static const int values[] = {8, 10, 12}; int index = g_cpuMultiplier == 8 ? 0 : (g_cpuMultiplier == 10 ? 1 : 2); index += direction; if (index < 0) index = 2; if (index > 2) index = 0; g_cpuMultiplier = values[index]; } else if (page == SettingsPage::Emulation && row == 1) { g_fastMemoryCheck = !g_fastMemoryCheck; } else if (page == SettingsPage::Interface && row == 0) { g_interfaceLanguage = interfaceIsEnglish() ? InterfaceLanguage::Spanish : InterfaceLanguage::English; setSettingsStatus("Idioma cambiado", "Language changed"); } else if (page == SettingsPage::Interface && row == 1) { // rueda entre los cuatro temas switch (g_uiTheme) { case UiTheme::Grey: g_uiTheme = UiTheme::Slate; break; case UiTheme::Slate: g_uiTheme = UiTheme::Graphite; break; case UiTheme::Graphite: g_uiTheme = UiTheme::Midnight; break; default: g_uiTheme = UiTheme::Grey; break; } applyUiTheme(); } else if (page == SettingsPage::Interface && row == 2) { g_uiAnimationsEnabled = !g_uiAnimationsEnabled; } else if (page == SettingsPage::Developer && row == 0) { g_showPerformanceOverlay = !g_showPerformanceOverlay; } else if (page == SettingsPage::Developer && row == 1) { g_showPadProbeOverlay = !g_showPadProbeOverlay; } else if (page == SettingsPage::Developer && row == 2) { g_showAudioOverlay = !g_showAudioOverlay; } else if (page == SettingsPage::Developer && row == 3) { runDiagnosticsScreen(); } else if (page == SettingsPage::Developer && row == 4) { resetFrontendSettings(); } clampFrontendSettings(); saveFrontendSettings(); } static long fileSize(const char* path); static bool runDefaultControlsMenu(); static bool runControlChoicePicker( unsigned buttonIndex, const ControlAction& originalAction, ControlAction& selectedAction, bool& accepted); static bool initializePadInput(); static void shutdownPadInput(); static bool waitForInputDevice(); static int drawWrappedUiText( std::uint32_t* output, int x, int y, int pixelHeight, int lineSpacing, int maxWidth, std::uint32_t color, const char* text, int maxLines) { if (output == nullptr || text == nullptr || maxWidth <= 0) return y; char line[512] {}; std::size_t lineLength = 0; const char* cursor = text; int lines = 0; while (*cursor != '\0' && lines < maxLines) { while (*cursor == ' ') ++cursor; if (*cursor == '\n') { drawUiText(output, x, y, pixelHeight, color, line, maxWidth); line[0] = '\0'; lineLength = 0; y += lineSpacing; ++lines; ++cursor; continue; } char word[192] {}; std::size_t wordLength = 0; while (*cursor != '\0' && *cursor != ' ' && *cursor != '\n' && wordLength + 1 < sizeof(word)) { word[wordLength++] = *cursor++; } word[wordLength] = '\0'; char candidate[512] {}; if (lineLength == 0) std::snprintf(candidate, sizeof(candidate), "%s", word); else std::snprintf(candidate, sizeof(candidate), "%s %s", line, word); if (lineLength > 0 && uiTextWidth(candidate, pixelHeight) > maxWidth) { drawUiText(output, x, y, pixelHeight, color, line, maxWidth); y += lineSpacing; ++lines; std::snprintf(line, sizeof(line), "%s", word); lineLength = std::strlen(line); } else { std::snprintf(line, sizeof(line), "%s", candidate); lineLength = std::strlen(line); } if (*cursor == '\n') { drawUiText(output, x, y, pixelHeight, color, line, maxWidth); line[0] = '\0'; lineLength = 0; y += lineSpacing; ++lines; ++cursor; } } if (lineLength > 0 && lines < maxLines) { drawUiText(output, x, y, pixelHeight, color, line, maxWidth); y += lineSpacing; } return y; } static bool systemFileAvailable(const char* fileName) { if (fileName == nullptr) return false; char path[MAX_GAME_PATH] {}; std::snprintf(path, sizeof(path), "%s/%s", RUNTIME_SYSTEM_NP2, fileName); return fileSize(path) > 0; } static bool collectMissingSystemFiles(char* destination, std::size_t capacity) { if (destination == nullptr || capacity == 0) return false; destination[0] = '\0'; const char* required[] = {"bios.rom", "itf.rom", "sound.rom", "np2kai.cfg"}; bool missing = false; for (const char* name : required) { if (!systemFileAvailable(name)) { const std::size_t used = std::strlen(destination); std::snprintf(destination + used, capacity > used ? capacity - used : 0, "%s%s", used > 0 ? "\n" : "", name); missing = true; } } if (!systemFileAvailable("font.bmp") && !systemFileAvailable("FONT.rom") && !systemFileAvailable("FONT.ROM")) { const std::size_t used = std::strlen(destination); std::snprintf(destination + used, capacity > used ? capacity - used : 0, "%sfont.bmp / FONT.rom", used > 0 ? "\n" : ""); missing = true; } return missing; } static bool presentInfoDocument(InfoDocument document) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); uiHeader(output, tr("Ayuda e información", "Help and information"), infoDocumentTitle(document)); // a una columna, con el ancho limitado: son textos para leer y una linea // de 1700 px no hay quien la siga con la vista const int textWidth = 1200; if (document == InfoDocument::SystemFiles) { char missing[512] {}; const bool hasMissing = collectMissingSystemFiles(missing, sizeof(missing)); int y = ui::CONTENT_Y + 10; drawUiText(output, ui::MARGIN, y, 30, hasMissing ? g_ui.bad : g_ui.good, hasMissing ? tr("Faltan archivos", "Missing files") : tr("Está todo", "All present"), textWidth); y += 58; if (hasMissing) { y = drawWrappedUiText(output, ui::MARGIN, y, 25, 37, textWidth, g_ui.textPrimary, missing, 10) + 24; } drawUiText(output, ui::MARGIN, y, 22, g_ui.textDim, RUNTIME_SYSTEM_NP2, textWidth); y += 50; drawWrappedUiText(output, ui::MARGIN, y, 23, 34, textWidth, g_ui.textMuted, tr("Hacen falta bios.rom, itf.rom, sound.rom, np2kai.cfg y una fuente (font.bmp o FONT.rom).", "You need bios.rom, itf.rom, sound.rom, np2kai.cfg and a font (font.bmp or FONT.rom)."), 4); } else { drawWrappedUiText(output, ui::MARGIN, ui::CONTENT_Y + 10, 24, 38, textWidth, g_ui.textPrimary, infoDocumentBody(document), 20); } uiFooter(output); int footerX = ui::MARGIN; if (document == InfoDocument::SystemFiles) { footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Triangle, tr("Comprobar", "Check")); } drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Atrás", "Back")); return uiFrameSubmit(); } static bool runInfoDocument(InfoDocument document) { updatePadState(); std::uint32_t previousButtons = frontendUiButtons(); if (!presentInfoDocument(document)) return false; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return false; if (!presentInfoDocument(document)) return false; updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previousButtons; previousButtons = current; if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0 || (pressed & ORBIS_PAD_BUTTON_CROSS) != 0 || (pressed & ORBIS_PAD_BUTTON_OPTIONS) != 0) return true; // repintado continuo, el vsync marca el ritmo if (!presentInfoDocument(document)) return false; } return false; } static bool presentErrorDialog( const char* code, const char* titleEs, const char* titleEn, const char* messageEs, const char* messageEn, const char* detail, const char* primaryEs, const char* primaryEn, bool allowRetry, bool allowBack) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); // tarjeta centrada. el error es lo unico que importa en ese momento, asi // que fuera cabecera y fuera todo lo demas const int cardW = 1160; const int cardH = 520; const int cardX = (OUTPUT_WIDTH - cardW) / 2; const int cardY = (OUTPUT_HEIGHT - cardH) / 2 - 30; uiRoundedRect(output, cardX, cardY, cardW, cardH, 10, g_ui.panel); uiRoundedRect(output, cardX, cardY, 6, cardH, 3, g_ui.bad); int y = cardY + 54; if (code != nullptr && code[0] != '\0') { drawUiText(output, cardX + 56, y, 19, g_ui.textDim, code, 400); y += 30; } drawUiText(output, cardX + 56, y, 34, g_ui.textPrimary, tr(titleEs, titleEn), cardW - 112); y += 62; y = drawWrappedUiText(output, cardX + 56, y, 24, 35, cardW - 112, g_ui.textMuted, tr(messageEs, messageEn), 7); if (detail != nullptr && detail[0] != '\0') { drawUiText(output, cardX + 56, cardY + cardH - 62, 19, g_ui.textDim, detail, cardW - 112); } uiFooter(output); int footerX = ui::MARGIN; if (primaryEs != nullptr) { footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr(primaryEs, primaryEn)); } if (allowRetry) { footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Triangle, tr("Reintentar", "Retry")); } if (allowBack) { drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Cerrar", "Close")); } return uiFrameSubmit(); } static DialogAction runErrorDialog( const char* code, const char* titleEs, const char* titleEn, const char* messageEs, const char* messageEn, const char* detail, const char* primaryEs, const char* primaryEn, bool allowRetry, bool allowBack) { frontendLog("ERROR SCREEN %s: %s", code != nullptr ? code : "NP2-0000", detail != nullptr ? detail : ""); updatePadState(); std::uint32_t previousButtons = frontendUiButtons(); if (!presentErrorDialog(code, titleEs, titleEn, messageEs, messageEn, detail, primaryEs, primaryEn, allowRetry, allowBack)) return DialogAction::Quit; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return DialogAction::Quit; if (!presentErrorDialog(code, titleEs, titleEn, messageEs, messageEn, detail, primaryEs, primaryEn, allowRetry, allowBack)) return DialogAction::Quit; updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previousButtons; previousButtons = current; if (primaryEs != nullptr && (pressed & ORBIS_PAD_BUTTON_CROSS) != 0) return DialogAction::Primary; if (allowRetry && (pressed & ORBIS_PAD_BUTTON_TRIANGLE) != 0) return DialogAction::Retry; if (allowBack && (pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0) return DialogAction::Back; if ((pressed & ORBIS_PAD_BUTTON_OPTIONS) != 0) return DialogAction::Quit; SDL_Delay(16); } return DialogAction::Quit; } static DialogAction runLastFailureDialog( const char* primaryEs, const char* primaryEn, bool allowRetry, bool allowBack) { return runErrorDialog(g_lastFailureCode, g_lastFailureTitleEs, g_lastFailureTitleEn, g_lastFailureMessageEs, g_lastFailureMessageEn, g_lastFailureDetail, primaryEs, primaryEn, allowRetry, allowBack); } static bool waitForInputDevice() { Uint32 nextRetry = 0; while (g_running) { processFrontendEvents(); updatePadState(); if (frontendInputAvailable()) return true; const Uint32 now = SDL_GetTicks(); if (static_cast(now - nextRetry) >= 0) { if (!g_padSubsystemReady) { shutdownPadInput(); initializePadInput(); } initializeExternalKeyboardInput(); nextRetry = now + 1000u; } if (!presentErrorDialog("NP2-1101", "No se ha detectado ningún dispositivo de entrada", "No input device was detected", "Conecta un DualShock 4 o un teclado USB y pulsa un botón o una tecla. La aplicación seguirá comprobándolo automáticamente.", "Connect a DualShock 4 or USB keyboard and press a button or key. The application will keep checking automatically.", "scePad / libSceKeyboard / libSceIme / SDL events", nullptr, nullptr, false, false)) return false; SDL_Delay(120); } return false; } static bool runSettingsMenu() { SettingsPage page = SettingsPage::Root; unsigned selection = 0; std::uint32_t previousButtons = frontendUiButtons(); int heldDirection = 0; Uint32 nextRepeatTick = 0; if (!animateSettingsScreen(page, selection, 1)) return false; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return false; if (!presentSettingsScreen(page, selection)) return false; updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t currentButtons = frontendUiButtons(); const std::uint32_t pressedButtons = currentButtons & ~previousButtons; previousButtons = currentButtons; int navigationDirection = 0; if ((currentButtons & ORBIS_PAD_BUTTON_UP) != 0 || leftStickUp()) navigationDirection = -1; else if ((currentButtons & ORBIS_PAD_BUTTON_DOWN) != 0 || leftStickDown()) navigationDirection = 1; const Uint32 now = SDL_GetTicks(); if (navigationDirection == 0) heldDirection = 0; else if (navigationDirection != heldDirection) { const unsigned rows = settingsRowCount(page); selection = navigationDirection < 0 ? (selection == 0 ? rows - 1 : selection - 1) : (selection + 1) % rows; heldDirection = navigationDirection; nextRepeatTick = now + 320u; } else if (static_cast(now - nextRepeatTick) >= 0) { const unsigned rows = settingsRowCount(page); selection = navigationDirection < 0 ? (selection == 0 ? rows - 1 : selection - 1) : (selection + 1) % rows; nextRepeatTick = now + 95u; } int horizontal = 0; if ((pressedButtons & ORBIS_PAD_BUTTON_LEFT) != 0) horizontal = -1; if ((pressedButtons & ORBIS_PAD_BUTTON_RIGHT) != 0) horizontal = 1; if (horizontal != 0 && page != SettingsPage::Root && page != SettingsPage::Support && selection + 1 < settingsRowCount(page)) { changeSetting(page, selection, horizontal); } if ((pressedButtons & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); if (page == SettingsPage::Root) { SettingsPage next = page; if (selection == 0) next = SettingsPage::Display; else if (selection == 1) next = SettingsPage::Audio; else if (selection == 2) next = SettingsPage::Controls; else if (selection == 3) next = SettingsPage::Emulation; else if (selection == 4) next = SettingsPage::Interface; else if (selection == 5) next = SettingsPage::Developer; else if (selection == 6) next = SettingsPage::Support; else return true; page = next; selection = 0; if (!animateSettingsScreen(page, selection, 1)) return false; } else if (selection + 1 == settingsRowCount(page)) { page = SettingsPage::Root; selection = 0; if (!animateSettingsScreen(page, selection, -1)) return false; } else if (page == SettingsPage::Controls && selection == 1) { if (!runDefaultControlsMenu()) return false; if (!presentSettingsScreen(page, selection)) return false; updatePadState(); previousButtons = frontendUiButtons(); } else if (page == SettingsPage::Support) { InfoDocument document = InfoDocument::Setup; if (selection == 1) document = InfoDocument::Controls; else if (selection == 2) document = InfoDocument::Troubleshooting; else if (selection == 3) document = InfoDocument::SystemFiles; else if (selection == 4) document = InfoDocument::Credits; else if (selection == 5) document = InfoDocument::ProjectStory; if (!runInfoDocument(document)) return false; if (!presentSettingsScreen(page, selection)) return false; updatePadState(); previousButtons = frontendUiButtons(); } else { changeSetting(page, selection, 1); } } if ((pressedButtons & ORBIS_PAD_BUTTON_CIRCLE) != 0 || (pressedButtons & ORBIS_PAD_BUTTON_OPTIONS) != 0) { if (page == SettingsPage::Root) return true; page = SettingsPage::Root; selection = 0; if (!animateSettingsScreen(page, selection, -1)) return false; } if (g_settingsSaveFailed) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); if (action == DialogAction::Retry) saveFrontendSettings(); else g_settingsSaveFailed = false; if (action == DialogAction::Quit) return false; if (!presentSettingsScreen(page, selection)) return false; updatePadState(); previousButtons = frontendUiButtons(); } // repintado continuo, el vsync marca el ritmo if (!presentSettingsScreen(page, selection)) return false; } return false; } static const char* controlActionDisplayName(const ControlAction& action) { const ControlChoice* choice = findControlChoice(action); return interfaceIsEnglish() ? choice->englishName : choice->spanishName; } static const char* controlChoiceCategoryDisplayName(ControlChoiceCategory category) { switch (category) { case ControlChoiceCategory::Letters: return tr("Letras", "Letters"); case ControlChoiceCategory::NumbersAndSymbols: return tr("Números y símbolos", "Numbers and symbols"); case ControlChoiceCategory::Navigation: return tr("Navegación", "Navigation"); case ControlChoiceCategory::FunctionKeys: return tr("Teclas de función", "Function keys"); case ControlChoiceCategory::NumericKeypad: return tr("Teclado numérico", "Numeric keypad"); case ControlChoiceCategory::Mouse: return tr("Ratón", "Mouse"); default: return tr("Comunes", "Common"); } } static const char* remappableButtonDisplayName(unsigned index) { static const char* names[REMAPPABLE_BUTTON_COUNT] = { "Cross", "Circle", "Square", "Triangle", "L1", "R1", "L2", "R2", "Options", "Share" }; return index < REMAPPABLE_BUTTON_COUNT ? names[index] : "?"; } static constexpr unsigned DEFAULT_CONTROLS_ROW_COUNT = 12; static constexpr unsigned DEFAULT_CONTROLS_VISIBLE_ROWS = 9; static const char* defaultControlsRowLabel(unsigned row) { if (row == 0) return tr("Perfil predeterminado", "Default profile"); if (row >= 1 && row <= REMAPPABLE_BUTTON_COUNT) { return remappableButtonDisplayName(row - 1); } return tr("Atrás", "Back"); } static void defaultControlsRowValue( unsigned row, char* destination, std::size_t capacity) { if (destination == nullptr || capacity == 0) return; destination[0] = '\0'; if (row == 0) { std::snprintf(destination, capacity, "%s", controlProfileDisplayName(g_defaultControls.profile)); } else if (row >= 1 && row <= REMAPPABLE_BUTTON_COUNT) { std::snprintf(destination, capacity, "%s", controlActionDisplayName(g_defaultControls.actions[row - 1])); } } static bool presentDefaultControlsScreen(unsigned selection, unsigned scroll) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); uiHeader(output, tr("Ajustes · Controles", "Settings · Controls"), tr("Perfil global", "Global profile")); const int rowPitch = ui::ROW_H + ui::ROW_GAP; const int listHeight = ui::CONTENT_END - ui::CONTENT_Y; const unsigned visible = static_cast(ui::rowsThatFit(listHeight)); const unsigned last = DEFAULT_CONTROLS_ROW_COUNT < scroll + visible ? DEFAULT_CONTROLS_ROW_COUNT : scroll + visible; // panel: que hace el boton que tienes encima uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, listHeight); int panelY = uiPanelTitle(output, ui::CONTENT_Y + 52, defaultControlsRowLabel(selection)); char focusValue[128] {}; defaultControlsRowValue(selection, focusValue, sizeof(focusValue)); if (focusValue[0] != '\0') { panelY = uiPanelField(output, panelY, tr("Asignado a", "Mapped to"), focusValue, g_ui.textPrimary); } uiPanelText(output, panelY + 6, tr("Este mapeo lo usan los juegos que no tengan uno propio.", "Games without their own mapping use this one."), 6); uiDrawMovingFocus(output, g_listFocusY[0], ui::LIST_X, ui::CONTENT_Y + static_cast(selection - scroll) * rowPitch, ui::LIST_W, ui::ROW_H, g_ui.focusFill, g_ui.focusOutline); for (unsigned row = scroll; row < last; ++row) { const int y = ui::CONTENT_Y + static_cast(row - scroll) * rowPitch; char value[128] {}; defaultControlsRowValue(row, value, sizeof(value)); uiRow(output, ui::LIST_X, y, ui::LIST_W, row == selection, nullptr, defaultControlsRowLabel(row), value); } uiScrollBar(output, ui::LIST_X + ui::LIST_W + 18, ui::CONTENT_Y, listHeight, DEFAULT_CONTROLS_ROW_COUNT, visible, static_cast(scroll * rowPitch), rowPitch); uiFooter(output); int footerX = ui::MARGIN; footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr("Cambiar", "Change")); footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Square, tr("Sin asignar", "Clear")); drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Atrás", "Back")); return uiFrameSubmit(); } static void cycleDefaultControlProfile(int direction) { int profile = static_cast(g_defaultControls.profile); if (profile < 0 || profile > 2) profile = 1; profile += direction; if (profile < 0) profile = 2; if (profile > 2) profile = 0; applyControlPreset(g_defaultControls, static_cast(profile)); } static void cycleDefaultControlBinding(unsigned buttonIndex, int direction) { if (buttonIndex >= REMAPPABLE_BUTTON_COUNT || direction == 0) return; unsigned choiceIndex = 0; for (unsigned index = 0; index < CONTROL_CHOICE_COUNT; ++index) { if (controlActionsEqual(CONTROL_CHOICES[index].action, g_defaultControls.actions[buttonIndex])) { choiceIndex = index; break; } } if (direction < 0) { choiceIndex = choiceIndex == 0 ? CONTROL_CHOICE_COUNT - 1 : choiceIndex - 1; } else { choiceIndex = (choiceIndex + 1) % CONTROL_CHOICE_COUNT; } g_defaultControls.actions[buttonIndex] = CONTROL_CHOICES[choiceIndex].action; g_defaultControls.profile = ControlProfile::Custom; } static bool saveDefaultControlsEditorState() { if (!g_activeGameHasControlOverride) g_activeControls = g_defaultControls; if (saveFrontendSettings()) return true; const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); if (action == DialogAction::Retry) return saveFrontendSettings(); return action != DialogAction::Quit; } // --- pantalla de diagnostico ----------------------------------------------- // todo lo que hace falta para saber por donde se va el tiempo, junto y sin // tener que sacar el log por ftp. los tiempos son del ultimo segundo de juego, // asi que si no has abierto ninguno saldran a cero static bool presentDiagnosticsScreen() { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); uiHeader(output, tr("Desarrollador", "Developer"), tr("Diagnóstico", "Diagnostics")); const double budget = g_targetFps > 1.0 ? 1000.0 / g_targetFps : 1000.0 / 56.4; char value[128] {}; // izquierda: donde se va el tiempo. derecha: estado drawUiText(output, ui::LIST_X, ui::CONTENT_Y, 24, g_ui.textPrimary, tr("Reparto por frame", "Per-frame breakdown"), 600); fillRectangle(output, ui::LIST_X, ui::CONTENT_Y + 38, 46, 2, g_ui.focusOutline); int y = ui::CONTENT_Y + 74; auto medida = [&](const char* label, double ms, bool alerta) { std::snprintf(value, sizeof(value), "%.2f ms", ms); drawUiText(output, ui::LIST_X, y, 23, g_ui.textMuted, label, 380); const int w = uiTextWidth(value, 23); drawUiText(output, ui::LIST_X + ui::LIST_W - w, y, 23, alerta ? g_ui.bad : g_ui.textPrimary, value, 200); // barra proporcional al presupuesto, que un numero solo no dice nada const int barWidth = budget > 0.0 ? static_cast(ms / budget * ui::LIST_W) : 0; fillRectangle(output, ui::LIST_X, y + 30, ui::LIST_W, 3, g_ui.panel); if (barWidth > 0) { fillRectangle(output, ui::LIST_X, y + 30, barWidth > ui::LIST_W ? ui::LIST_W : barWidth, 3, alerta ? g_ui.bad : g_ui.focusOutline); } y += 58; }; medida(tr("Presupuesto", "Budget"), budget, false); medida("retro_run", g_recentRetroRunMs, g_recentRetroRunMs > budget); medida(tr("Peor frame", "Worst frame"), g_msWorstRun, g_msWorstRun > budget); medida(tr("Espera de buffer", "Buffer wait"), g_msWaitBuffer, false); medida(tr("Conversión PC-98", "PC-98 conversion"), g_msConvert, false); medida(tr("Envío del flip", "Flip submit"), g_msSubmit, false); medida(tr("Hueco libre", "Slack"), g_msSlack, g_msSlack < 1.0); uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, ui::CONTENT_END - ui::CONTENT_Y); int panelY = uiPanelTitle(output, ui::CONTENT_Y + 40, tr("Estado", "State")); std::snprintf(value, sizeof(value), "%.1f / %.1f · %s %.1f", g_recentEmulationFps, g_targetFps, tr("vídeo", "video"), g_recentPresentFps); panelY = uiPanelField(output, panelY, tr("FPS", "FPS"), value, g_recentEmulationFps < g_targetFps * 0.9 ? g_ui.bad : g_ui.good); std::snprintf(value, sizeof(value), "%u", g_lateFrames); panelY = uiPanelField(output, panelY, tr("Frames tarde", "Late frames"), value, g_lateFrames > 0 ? g_ui.bad : g_ui.good); std::snprintf(value, sizeof(value), tr("%u en cola · %u cortes/s", "%u queued · %u cuts/s"), audioRingCount(), g_audioCutsPerSecond); panelY = uiPanelField(output, panelY, tr("Audio", "Audio"), value, g_audioCutsPerSecond > 0 ? g_ui.bad : g_ui.textPrimary); panelY = uiPanelField(output, panelY, tr("Juego", "Game"), g_activeGameName[0] != '\0' ? g_activeGameName : tr("ninguno", "none"), g_ui.textPrimary); panelY = uiPanelField(output, panelY, tr("Perfil", "Profile"), controlProfileConfigName(g_activeControls.profile), g_ui.textMuted); std::snprintf(value, sizeof(value), "%dx%d · %s · %s", OUTPUT_WIDTH, OUTPUT_HEIGHT, g_padConnected ? tr("mando", "pad") : tr("sin mando", "no pad"), ps4KeyboardIsOpen() ? tr("teclado", "keyboard") : tr("sin teclado", "no keyboard")); uiPanelField(output, panelY, tr("Sistema", "System"), value, g_ui.textMuted); drawUiText(output, ui::PANEL_X + 44, ui::CONTENT_END - 44, 18, g_ui.textDim, "/data/np2kai-runtime/logs/perf.log", ui::PANEL_W - 88); uiFooter(output); drawFooterHintPolished(output, ui::MARGIN, ui::FOOTER_Y, FooterButton::Circle, tr("Atrás", "Back")); return uiFrameSubmit(); } static bool runDiagnosticsScreen() { updatePadState(); std::uint32_t previous = frontendUiButtons(); while (g_running) { processFrontendEvents(); updatePadState(); const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previous; previous = current; if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0) return true; if (!presentDiagnosticsScreen()) return false; } return false; } static bool runDefaultControlsMenu() { unsigned selection = 0; unsigned scroll = 0; int heldDirection = 0; Uint32 nextRepeatTick = 0; updatePadState(); std::uint32_t previousButtons = frontendUiButtons(); if (!presentDefaultControlsScreen(selection, scroll)) return false; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return false; if (!presentDefaultControlsScreen(selection, scroll)) return false; updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previousButtons; previousButtons = current; int navigation = 0; if ((current & ORBIS_PAD_BUTTON_UP) != 0 || leftStickUp()) navigation = -1; else if ((current & ORBIS_PAD_BUTTON_DOWN) != 0 || leftStickDown()) navigation = 1; const Uint32 now = SDL_GetTicks(); if (navigation == 0) heldDirection = 0; else if (navigation != heldDirection) { selection = navigation < 0 ? (selection == 0 ? DEFAULT_CONTROLS_ROW_COUNT - 1 : selection - 1) : (selection + 1) % DEFAULT_CONTROLS_ROW_COUNT; heldDirection = navigation; nextRepeatTick = now + 320u; } else if (static_cast(now - nextRepeatTick) >= 0) { selection = navigation < 0 ? (selection == 0 ? DEFAULT_CONTROLS_ROW_COUNT - 1 : selection - 1) : (selection + 1) % DEFAULT_CONTROLS_ROW_COUNT; nextRepeatTick = now + 95u; } if (selection < scroll) scroll = selection; if (selection >= scroll + DEFAULT_CONTROLS_VISIBLE_ROWS) { scroll = selection - DEFAULT_CONTROLS_VISIBLE_ROWS + 1; } int horizontal = 0; if ((pressed & ORBIS_PAD_BUTTON_LEFT) != 0) horizontal = -1; if ((pressed & ORBIS_PAD_BUTTON_RIGHT) != 0) horizontal = 1; if (horizontal != 0 && selection <= REMAPPABLE_BUTTON_COUNT) { if (selection == 0) cycleDefaultControlProfile(horizontal); else cycleDefaultControlBinding(selection - 1, horizontal); if (!saveDefaultControlsEditorState()) return false; } if ((pressed & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); if (selection == 0) { cycleDefaultControlProfile(1); if (!saveDefaultControlsEditorState()) return false; } else if (selection >= 1 && selection <= REMAPPABLE_BUTTON_COUNT) { const unsigned buttonIndex = selection - 1; ControlAction chosen = g_defaultControls.actions[buttonIndex]; bool accepted = false; if (!runControlChoicePicker( buttonIndex, chosen, chosen, accepted)) return false; updatePadState(); previousButtons = frontendUiButtons(); if (accepted) { g_defaultControls.actions[buttonIndex] = chosen; g_defaultControls.profile = ControlProfile::Custom; if (!saveDefaultControlsEditorState()) return false; } } else { return true; } } if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0 || (pressed & ORBIS_PAD_BUTTON_OPTIONS) != 0) return true; // repintado continuo, el vsync marca el ritmo if (!presentDefaultControlsScreen(selection, scroll)) return false; } return false; } static constexpr unsigned GAME_CONTROLS_ROW_COUNT = 13; static constexpr unsigned GAME_CONTROLS_VISIBLE_ROWS = 9; static const char* gameControlsRowLabel(unsigned row) { if (row == 0) return tr("Perfil del juego", "Game profile"); if (row >= 1 && row <= REMAPPABLE_BUTTON_COUNT) { return remappableButtonDisplayName(row - 1); } if (row == 11) return tr("Usar perfil predeterminado", "Use default profile"); return tr("Atrás", "Back"); } static void gameControlsRowValue(unsigned row, char* destination, std::size_t capacity) { if (destination == nullptr || capacity == 0) return; destination[0] = '\0'; if (row == 0) { std::snprintf(destination, capacity, "%s", controlProfileDisplayName(g_activeControls.profile)); } else if (row >= 1 && row <= REMAPPABLE_BUTTON_COUNT) { std::snprintf(destination, capacity, "%s", controlActionDisplayName(g_activeControls.actions[row - 1])); } } static bool presentGameControlsScreen( unsigned selection, unsigned scroll, const GameEntry& game) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); uiHeader(output, game.name, tr("Controles del juego", "Game controls")); const int rowPitch = ui::ROW_H + ui::ROW_GAP; const int listHeight = ui::CONTENT_END - ui::CONTENT_Y; const unsigned visible = static_cast(ui::rowsThatFit(listHeight)); const unsigned last = GAME_CONTROLS_ROW_COUNT < scroll + visible ? GAME_CONTROLS_ROW_COUNT : scroll + visible; // panel: que hace el boton que tienes encima uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, listHeight); int panelY = uiPanelTitle(output, ui::CONTENT_Y + 52, gameControlsRowLabel(selection)); char focusValue[128] {}; gameControlsRowValue(selection, focusValue, sizeof(focusValue)); if (focusValue[0] != '\0') { panelY = uiPanelField(output, panelY, tr("Asignado a", "Mapped to"), focusValue, g_ui.textPrimary); } uiPanelText(output, panelY + 6, tr("Solo afecta a este juego. Se guarda aparte del perfil global.", "Only affects this game. Saved separately from the global profile."), 6); uiDrawMovingFocus(output, g_listFocusY[1], ui::LIST_X, ui::CONTENT_Y + static_cast(selection - scroll) * rowPitch, ui::LIST_W, ui::ROW_H, g_ui.focusFill, g_ui.focusOutline); for (unsigned row = scroll; row < last; ++row) { const int y = ui::CONTENT_Y + static_cast(row - scroll) * rowPitch; char value[128] {}; gameControlsRowValue(row, value, sizeof(value)); uiRow(output, ui::LIST_X, y, ui::LIST_W, row == selection, nullptr, gameControlsRowLabel(row), value); } uiScrollBar(output, ui::LIST_X + ui::LIST_W + 18, ui::CONTENT_Y, listHeight, GAME_CONTROLS_ROW_COUNT, visible, static_cast(scroll * rowPitch), rowPitch); uiFooter(output); int footerX = ui::MARGIN; footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr("Cambiar", "Change")); footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Square, tr("Sin asignar", "Clear")); drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Atrás", "Back")); return uiFrameSubmit(); } static void cycleActiveGameProfile(int direction) { int profile = static_cast(g_activeControls.profile); if (profile < 0 || profile > 2) profile = 1; profile += direction; if (profile < 0) profile = 2; if (profile > 2) profile = 0; applyControlPreset(g_activeControls, static_cast(profile)); } static void cycleActiveGameBinding(unsigned buttonIndex, int direction) { if (buttonIndex >= REMAPPABLE_BUTTON_COUNT || direction == 0) return; unsigned choiceIndex = 0; for (unsigned index = 0; index < CONTROL_CHOICE_COUNT; ++index) { if (controlActionsEqual(CONTROL_CHOICES[index].action, g_activeControls.actions[buttonIndex])) { choiceIndex = index; break; } } if (direction < 0) { choiceIndex = choiceIndex == 0 ? CONTROL_CHOICE_COUNT - 1 : choiceIndex - 1; } else { choiceIndex = (choiceIndex + 1) % CONTROL_CHOICE_COUNT; } g_activeControls.actions[buttonIndex] = CONTROL_CHOICES[choiceIndex].action; g_activeControls.profile = ControlProfile::Custom; } static constexpr unsigned CONTROL_PICKER_VISIBLE_ROWS = 8; static bool presentControlChoicePicker( unsigned buttonIndex, ControlChoiceCategory category, unsigned selection, unsigned scroll, const ControlAction& originalAction) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); char crumb[192] {}; std::snprintf(crumb, sizeof(crumb), "%s", defaultControlsRowLabel(buttonIndex)); uiHeader(output, crumb, tr("Elegir tecla", "Choose a key")); // la categoria va como pestañas debajo del titulo, no como un rotulo suelto // en medio de la pantalla como estaba antes char tabs[192] {}; std::snprintf(tabs, sizeof(tabs), "L1 · %s · R1", controlChoiceCategoryDisplayName(category)); drawUiText(output, ui::MARGIN, ui::CONTENT_Y - 4, 22, g_ui.textDim, tabs, 900); const int listTop = ui::CONTENT_Y + 42; const int rowPitch = ui::ROW_H + ui::ROW_GAP; const int listHeight = ui::CONTENT_END - listTop; const unsigned visible = static_cast(ui::rowsThatFit(listHeight)); const unsigned categoryCount = controlChoiceCountForCategory(category); const unsigned last = categoryCount < scroll + visible ? categoryCount : scroll + visible; uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, ui::CONTENT_END - ui::CONTENT_Y); int panelY = uiPanelTitle(output, ui::CONTENT_Y + 52, tr("Teclado USB", "USB keyboard")); panelY = uiPanelText(output, panelY, g_externalKeyboardDetected ? tr("Teclado detectado. Pulsa cualquier tecla y se asigna sola, sin buscarla en la lista.", "Keyboard detected. Press any key and it gets assigned, no need to find it in the list.") : tr("Si conectas un teclado USB, pulsa una tecla y se asigna sola sin buscarla en la lista.", "If you connect a USB keyboard, press a key and it gets assigned without hunting for it."), 4); const ControlChoice* focused = controlChoiceForCategoryIndex(category, selection); if (focused != nullptr) { uiPanelField(output, panelY + 20, tr("Seleccionado", "Selected"), interfaceIsEnglish() ? focused->englishName : focused->spanishName, g_ui.textPrimary); } uiDrawMovingFocus(output, g_listFocusY[2], ui::LIST_X, listTop + static_cast(selection - scroll) * rowPitch, ui::LIST_W, ui::ROW_H, g_ui.focusFill, g_ui.focusOutline); for (unsigned row = scroll; row < last; ++row) { const ControlChoice* choice = controlChoiceForCategoryIndex(category, row); if (choice == nullptr) continue; const int y = listTop + static_cast(row - scroll) * rowPitch; const char* name = interfaceIsEnglish() ? choice->englishName : choice->spanishName; uiRow(output, ui::LIST_X, y, ui::LIST_W, row == selection, nullptr, name, controlActionsEqual(choice->action, originalAction) ? tr("actual", "current") : nullptr); } uiScrollBar(output, ui::LIST_X + ui::LIST_W + 18, listTop, listHeight, categoryCount, visible, static_cast(scroll * rowPitch), rowPitch); uiFooter(output); int footerX = ui::MARGIN; footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr("Asignar", "Assign")); drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Cancelar", "Cancel")); return uiFrameSubmit(); } static bool runControlChoicePicker( unsigned buttonIndex, const ControlAction& originalAction, ControlAction& selectedAction, bool& accepted) { accepted = false; const ControlChoice* originalChoice = findControlChoice(originalAction); ControlChoiceCategory category = originalChoice->category; unsigned selection = categoryIndexForControlChoice(category, originalAction); unsigned scroll = selection >= CONTROL_PICKER_VISIBLE_ROWS ? selection - CONTROL_PICKER_VISIBLE_ROWS + 1 : 0; int heldDirection = 0; Uint32 nextRepeatTick = 0; const std::uint32_t initialKeyboardGeneration = g_externalKeyboardEventGeneration; std::uint32_t observedKeyboardGeneration = initialKeyboardGeneration; updatePadState(); std::uint32_t previousButtons = frontendUiButtons(); if (!presentControlChoicePicker(buttonIndex, category, selection, scroll, originalAction)) { return false; } while (g_running) { processFrontendEvents(); if (g_externalKeyboardEventGeneration != observedKeyboardGeneration) { observedKeyboardGeneration = g_externalKeyboardEventGeneration; const ControlChoice* captured = findKeyboardControlChoice(g_lastExternalKeyboardKey); if (captured != nullptr) { selectedAction = captured->action; accepted = true; return true; } } updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return false; if (!presentControlChoicePicker(buttonIndex, category, selection, scroll, originalAction)) { return false; } updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previousButtons; previousButtons = current; if ((pressed & ORBIS_PAD_BUTTON_L1) != 0 || (pressed & ORBIS_PAD_BUTTON_R1) != 0) { int nextCategory = static_cast(category); nextCategory += (pressed & ORBIS_PAD_BUTTON_L1) != 0 ? -1 : 1; const int categoryCount = static_cast(ControlChoiceCategory::Count); if (nextCategory < 0) nextCategory = categoryCount - 1; if (nextCategory >= categoryCount) nextCategory = 0; category = static_cast(nextCategory); selection = 0; scroll = 0; heldDirection = 0; } int navigation = 0; if ((current & ORBIS_PAD_BUTTON_UP) != 0 || leftStickUp()) navigation = -1; else if ((current & ORBIS_PAD_BUTTON_DOWN) != 0 || leftStickDown()) navigation = 1; const Uint32 now = SDL_GetTicks(); const unsigned categoryCount = controlChoiceCountForCategory(category); if (navigation == 0) heldDirection = 0; else if (navigation != heldDirection) { selection = navigation < 0 ? (selection == 0 ? categoryCount - 1 : selection - 1) : (selection + 1) % categoryCount; heldDirection = navigation; nextRepeatTick = now + 320u; } else if (static_cast(now - nextRepeatTick) >= 0) { selection = navigation < 0 ? (selection == 0 ? categoryCount - 1 : selection - 1) : (selection + 1) % categoryCount; nextRepeatTick = now + 95u; } if (selection < scroll) scroll = selection; if (selection >= scroll + CONTROL_PICKER_VISIBLE_ROWS) { scroll = selection - CONTROL_PICKER_VISIBLE_ROWS + 1; } if ((pressed & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); const ControlChoice* choice = controlChoiceForCategoryIndex(category, selection); if (choice != nullptr) { selectedAction = choice->action; accepted = true; return true; } } if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0 || (pressed & ORBIS_PAD_BUTTON_OPTIONS) != 0) { return true; } // repintado continuo, el vsync marca el ritmo if (!presentControlChoicePicker( buttonIndex, category, selection, scroll, originalAction)) return false; } return false; } static bool runGameControlsMenu(const GameEntry& game) { unsigned selection = 0; unsigned scroll = 0; int heldDirection = 0; Uint32 nextRepeatTick = 0; updatePadState(); std::uint32_t previousButtons = frontendUiButtons(); if (!presentGameControlsScreen(selection, scroll, game)) return false; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return false; if (!presentGameControlsScreen(selection, scroll, game)) return false; updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previousButtons; previousButtons = current; int navigation = 0; if ((current & ORBIS_PAD_BUTTON_UP) != 0 || leftStickUp()) navigation = -1; else if ((current & ORBIS_PAD_BUTTON_DOWN) != 0 || leftStickDown()) navigation = 1; const Uint32 now = SDL_GetTicks(); if (navigation == 0) heldDirection = 0; else if (navigation != heldDirection) { selection = navigation < 0 ? (selection == 0 ? GAME_CONTROLS_ROW_COUNT - 1 : selection - 1) : (selection + 1) % GAME_CONTROLS_ROW_COUNT; heldDirection = navigation; nextRepeatTick = now + 320u; } else if (static_cast(now - nextRepeatTick) >= 0) { selection = navigation < 0 ? (selection == 0 ? GAME_CONTROLS_ROW_COUNT - 1 : selection - 1) : (selection + 1) % GAME_CONTROLS_ROW_COUNT; nextRepeatTick = now + 95u; } if (selection < scroll) scroll = selection; if (selection >= scroll + GAME_CONTROLS_VISIBLE_ROWS) { scroll = selection - GAME_CONTROLS_VISIBLE_ROWS + 1; } int horizontal = 0; if ((pressed & ORBIS_PAD_BUTTON_LEFT) != 0) horizontal = -1; if ((pressed & ORBIS_PAD_BUTTON_RIGHT) != 0) horizontal = 1; if (horizontal != 0 && selection < 11) { if (selection == 0) cycleActiveGameProfile(horizontal); else if (selection >= 1 && selection <= REMAPPABLE_BUTTON_COUNT) { cycleActiveGameBinding(selection - 1, horizontal); } if (!saveGameControls(game)) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); if (action == DialogAction::Quit) return false; } } if ((pressed & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); if (selection == 0) { cycleActiveGameProfile(1); if (!saveGameControls(game)) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); if (action == DialogAction::Quit) return false; } } else if (selection >= 1 && selection <= REMAPPABLE_BUTTON_COUNT) { const unsigned buttonIndex = selection - 1; ControlAction chosen = g_activeControls.actions[buttonIndex]; bool accepted = false; if (!runControlChoicePicker(buttonIndex, chosen, chosen, accepted)) return false; updatePadState(); previousButtons = frontendUiButtons(); if (accepted) { g_activeControls.actions[buttonIndex] = chosen; g_activeControls.profile = ControlProfile::Custom; if (!saveGameControls(game)) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); if (action == DialogAction::Quit) return false; } } } else if (selection == 11) { if (!removeGameControlsOverride(game)) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); if (action == DialogAction::Quit) return false; } } else if (selection == 12) { return true; } } if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0 || (pressed & ORBIS_PAD_BUTTON_OPTIONS) != 0) return true; // repintado continuo, el vsync marca el ritmo if (!presentGameControlsScreen(selection, scroll, game)) return false; } return false; } static bool presentLibraryScreen(int contentOffsetX = 0) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; g_libraryEnter = uiApproachUnit(g_libraryEnter, 1.0f, UI_TAU_SCREEN); contentOffsetX += uiRound((1.0f - g_libraryEnter) * g_libraryEnterFrom * 180.0f); drawPS4StyleBackground(output); char crumb[96] {}; std::snprintf(crumb, sizeof(crumb), "%s · %u", "NP2kai PS4", g_gameEntryCount); uiHeader(output, crumb, tr("Biblioteca", "Library")); const int listX = ui::LIST_X + contentOffsetX; const int rowPitch = ui::ROW_H + ui::ROW_GAP; const int listHeight = ui::CONTENT_END - ui::CONTENT_Y; const unsigned visible = static_cast(ui::rowsThatFit(listHeight)); if (g_gameEntryCount == 0) { uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, listHeight); drawUiText(output, listX + 4, ui::CONTENT_Y + 40, 28, g_ui.textMuted, tr("No hay juegos todavía", "No games yet"), ui::LIST_W); drawWrappedUiText(output, listX + 4, ui::CONTENT_Y + 92, 22, 32, ui::LIST_W, g_ui.textDim, tr("Copia tus imágenes de PC-98 a la carpeta de juegos y vuelve a escanear.", "Copy your PC-98 images to the games folder and rescan."), 4); int emptyY = uiPanelTitle(output, ui::CONTENT_Y + 52, tr("Dónde van", "Where they go")); emptyY = uiPanelField(output, emptyY, tr("Carpeta", "Folder"), CONTENT_DIRECTORY, g_ui.textPrimary); uiPanelText(output, emptyY, tr("Formatos: .hdi, .fdi, .d88, .hdm, .t98", "Formats: .hdi, .fdi, .d88, .hdm, .t98"), 2); } else { if (g_librarySelection >= g_gameEntryCount) g_librarySelection = g_gameEntryCount - 1; if (g_librarySelection < g_libraryScroll) g_libraryScroll = g_librarySelection; if (g_librarySelection >= g_libraryScroll + visible) g_libraryScroll = g_librarySelection - visible + 1; // --- panel: la ficha del juego, siempre a la vista --- // antes esto era una pantalla aparte que habia que abrir con X. ahora // se actualiza sola segun mueves la seleccion const GameEntry& game = g_gameEntries[g_librarySelection]; uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, listHeight); int panelY = uiPanelTitle(output, ui::CONTENT_Y + 52, game.name); char detail[256] {}; const char* dot = std::strrchr(game.name, '.'); std::snprintf(detail, sizeof(detail), "%s", dot != nullptr ? dot + 1 : "?"); for (char* c = detail; *c != '\0'; ++c) { if (*c >= 'a' && *c <= 'z') *c = static_cast(*c - 'a' + 'A'); } panelY = uiPanelField(output, panelY, tr("Formato", "Format"), detail, g_ui.textPrimary); if (game.size >= 1024L * 1024L) { std::snprintf(detail, sizeof(detail), "%ld MB", game.size / (1024L * 1024L)); } else { std::snprintf(detail, sizeof(detail), "%ld KB", game.size / 1024L); } panelY = uiPanelField(output, panelY, tr("Tamaño", "Size"), detail, g_ui.textPrimary); panelY = uiPanelField(output, panelY, tr("Perfil de controles", "Control profile"), gameHasOwnProfile(game) ? tr("propio del juego", "game specific") : tr("el global", "global"), gameHasOwnProfile(game) ? g_ui.good : g_ui.textMuted); drawUiText(output, ui::PANEL_X + 44, ui::CONTENT_END - 84, 20, g_ui.textDim, tr("Ruta", "Path"), 400); drawUiText(output, ui::PANEL_X + 44, ui::CONTENT_END - 58, 21, g_ui.textMuted, game.path, ui::PANEL_W - 88); // --- lista --- const float scrollTarget = static_cast(g_libraryScroll * rowPitch); g_libraryScrollPixels = uiApproach(g_libraryScrollPixels, scrollTarget, UI_TAU_SCROLL); const float focusTarget = static_cast(ui::CONTENT_Y + static_cast(g_librarySelection) * rowPitch); if (g_libraryFocusY < 0.0f) g_libraryFocusY = focusTarget; g_libraryFocusY = uiApproach(g_libraryFocusY, focusTarget, UI_TAU_FOCUS); uiFocusRow(output, listX, uiRound(g_libraryFocusY - g_libraryScrollPixels), ui::LIST_W, ui::ROW_H); const int first = static_cast(g_libraryScroll) - 1; const int last = static_cast(g_libraryScroll + visible) + 1; for (int index = first; index <= last; ++index) { if (index < 0 || index >= static_cast(g_gameEntryCount)) continue; const int rowY = ui::CONTENT_Y + uiRound(index * rowPitch - g_libraryScrollPixels); const UiIcon icon = UiIcon::Game; uiRow(output, listX, rowY, ui::LIST_W, index == static_cast(g_librarySelection), &icon, g_gameEntries[index].name, nullptr); } // la cabecera tapa lo que se sale por arriba; por abajo lo limpio yo uiRestoreBackgroundBand(output, ui::CONTENT_END, OUTPUT_HEIGHT - ui::CONTENT_END); uiScrollBar(output, listX + ui::LIST_W + 18, ui::CONTENT_Y, listHeight, g_gameEntryCount, visible, g_libraryScrollPixels, rowPitch); } // la cabecera va la ultima: la lista se dibuja entera y se tapa lo que sobra uiRestoreBackgroundBand(output, 0, ui::CONTENT_Y - 6); uiHeader(output, crumb, tr("Biblioteca", "Library")); uiFooter(output); int footerX = ui::MARGIN; if (g_gameEntryCount > 0) { footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr("Jugar", "Play")); footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Square, tr("Controles", "Controls")); } footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Triangle, tr("Escanear", "Rescan")); drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Options, tr("Ajustes", "Settings")); return uiFrameSubmit(); } // antes esto era un for con SDL_Delay dentro, o sea que durante la animacion // no se leia el mando y se perdian pulsaciones. ahora solo apunta de donde // tiene que entrar y el propio bucle la va moviendo static bool animateLibraryEntrance(int direction) { g_libraryEnterFrom = direction; g_libraryEnter = g_uiAnimationsEnabled ? 0.0f : 1.0f; return presentLibraryScreen(); } static void moveLibrarySelection(int direction) { if (g_gameEntryCount == 0 || direction == 0) return; if (direction < 0) g_librarySelection = g_librarySelection == 0 ? g_gameEntryCount - 1 : g_librarySelection - 1; else g_librarySelection = (g_librarySelection + 1) % g_gameEntryCount; } static bool runLibraryMenu() { refreshGameLibrary(); updatePadState(); while (g_libraryScanFailed && g_running) { const DialogAction action = runLastFailureDialog("Ver guía", "View guide", true, false); if (action == DialogAction::Primary) { if (!runInfoDocument(InfoDocument::Setup)) return false; } else if (action == DialogAction::Retry) { refreshGameLibrary(); } else { return false; } } g_libraryPreviousButtons = frontendUiButtons(); g_libraryHeldDirection = 0; g_libraryNextRepeatTick = 0; if (!animateLibraryEntrance(1)) return false; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return false; if (!presentLibraryScreen()) return false; updatePadState(); g_libraryPreviousButtons = frontendUiButtons(); } const std::uint32_t currentButtons = frontendUiButtons(); const std::uint32_t pressedButtons = currentButtons & ~g_libraryPreviousButtons; g_libraryPreviousButtons = currentButtons; int navigationDirection = 0; if ((currentButtons & ORBIS_PAD_BUTTON_UP) != 0 || leftStickUp()) navigationDirection = -1; else if ((currentButtons & ORBIS_PAD_BUTTON_DOWN) != 0 || leftStickDown()) navigationDirection = 1; const Uint32 now = SDL_GetTicks(); if (navigationDirection == 0) g_libraryHeldDirection = 0; else if (navigationDirection != g_libraryHeldDirection) { moveLibrarySelection(navigationDirection); g_libraryHeldDirection = navigationDirection; g_libraryNextRepeatTick = now + 320u; } else if (static_cast(now - g_libraryNextRepeatTick) >= 0) { moveLibrarySelection(navigationDirection); g_libraryNextRepeatTick = now + 95u; } if ((pressedButtons & ORBIS_PAD_BUTTON_TRIANGLE) != 0) { refreshGameLibrary(); if (g_libraryScanFailed) { const DialogAction action = runLastFailureDialog("Ver guía", "View guide", true, true); if (action == DialogAction::Primary) runInfoDocument(InfoDocument::Setup); else if (action == DialogAction::Retry) refreshGameLibrary(); else if (action == DialogAction::Quit) return false; updatePadState(); g_libraryPreviousButtons = frontendUiButtons(); } } if ((pressedButtons & ORBIS_PAD_BUTTON_OPTIONS) != 0) { if (!runSettingsMenu()) return false; updatePadState(); g_libraryPreviousButtons = frontendUiButtons(); if (!animateLibraryEntrance(-1)) return false; } // los datos del juego ya se ven en el panel de la derecha segun mueves // la seleccion, asi que X arranca directo y Cuadrado abre los controles. // la pantalla intermedia de la ficha ya no hace falta if ((pressedButtons & ORBIS_PAD_BUTTON_SQUARE) != 0 && g_gameEntryCount > 0) { if (!runGameControlsMenu(g_gameEntries[g_librarySelection])) return false; updatePadState(); g_libraryPreviousButtons = frontendUiButtons(); if (!animateLibraryEntrance(-1)) return false; } if ((pressedButtons & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); if (g_gameEntryCount > 0) { std::printf("[NP2kai/library] selected: %s\n", g_gameEntries[g_librarySelection].path); return true; } else { if (!runInfoDocument(InfoDocument::Setup)) return false; updatePadState(); g_libraryPreviousButtons = frontendUiButtons(); if (!presentLibraryScreen()) return false; } } // se repinta siempre. el flip con vsync ya marca el ritmo, asi que no // hace falta ningun delay y las animaciones tienen frames donde correr if (!presentLibraryScreen()) return false; } return false; } static long fileSize(const char* path) { std::FILE* file = std::fopen(path, "rb"); if (file == nullptr) { return -1; } if (std::fseek(file, 0, SEEK_END) != 0) { std::fclose(file); return -1; } const long size = std::ftell(file); std::fclose(file); return size; } static bool readableFile(const char* path) { const long size = fileSize(path); if (size < 0) { std::printf( "[NP2kai] Missing/unreadable file: %s\n", path ); return false; } std::printf( "[NP2kai] Readable file: %s (%ld bytes)\n", path, size ); return true; } static bool ensureDirectory(const char* path) { if (mkdir(path, 0777) == 0 || errno == EEXIST) { return true; } std::printf( "[NP2kai] mkdir failed: %s errno=%d\n", path, errno ); return false; } static bool copyFileIfNeeded( const char* source, const char* destination, bool preserveExisting) { const long sourceSize = fileSize(source); if (sourceSize < 0) { std::printf( "[NP2kai] Copy source is unreadable: %s\n", source ); return false; } const long destinationSize = fileSize(destination); if (destinationSize >= 0) { if (preserveExisting || destinationSize == sourceSize) { std::printf( "[NP2kai] Keeping runtime file: %s " "(%ld bytes)\n", destination, destinationSize ); return true; } std::printf( "[NP2kai] Replacing size-mismatched file: %s " "(runtime=%ld package=%ld)\n", destination, destinationSize, sourceSize ); } std::FILE* input = std::fopen(source, "rb"); if (input == nullptr) { std::printf( "[NP2kai] fopen source failed: %s errno=%d\n", source, errno ); return false; } std::FILE* output = std::fopen(destination, "wb"); if (output == nullptr) { std::printf( "[NP2kai] fopen destination failed: %s errno=%d\n", destination, errno ); std::fclose(input); return false; } unsigned char buffer[64 * 1024]; bool success = true; for (;;) { const std::size_t bytesRead = std::fread( buffer, 1, sizeof(buffer), input ); if (bytesRead > 0) { const std::size_t bytesWritten = std::fwrite( buffer, 1, bytesRead, output ); if (bytesWritten != bytesRead) { success = false; break; } } if (bytesRead < sizeof(buffer)) { if (std::ferror(input) != 0) { success = false; } break; } } if (std::fflush(output) != 0) { success = false; } std::fclose(output); std::fclose(input); if (!success) { std::printf( "[NP2kai] Copy failed: %s -> %s\n", source, destination ); return false; } const long copiedSize = fileSize(destination); if (copiedSize != sourceSize) { std::printf( "[NP2kai] Copy verification failed: " "%s expected=%ld actual=%ld\n", destination, sourceSize, copiedSize ); return false; } std::printf( "[NP2kai] Copied runtime file: %s -> %s " "(%ld bytes)\n", source, destination, copiedSize ); return true; } static bool configLineMatches( const char* line, const char* key) { if (line == nullptr || key == nullptr) { return false; } while (*line == ' ' || *line == '\t') { ++line; } const std::size_t keyLength = std::strlen(key); if (std::strncmp(line, key, keyLength) != 0) { return false; } const char next = line[keyLength]; return next == '\0' || next == ' ' || next == '\t' || next == '='; } static bool patchRuntimeConfigForGame(const char* gamePath) { if (gamePath == nullptr || gamePath[0] == '\0') { setLastFailure("NP2-2001", "Ruta de juego no válida", "Invalid game path", "La entrada seleccionada no contiene una ruta válida.", "The selected entry does not contain a valid path."); return false; } static const char* configPath = "/data/np2kai-runtime/system/np2kai/" "np2kai.cfg"; static const char* temporaryPath = "/data/np2kai-runtime/system/np2kai/" "np2kai.cfg.tmp"; const char* runtimeFontPath = systemFileAvailable("font.bmp") ? "/data/np2kai-runtime/system/np2kai/font.bmp" : (systemFileAvailable("FONT.rom") ? "/data/np2kai-runtime/system/np2kai/FONT.rom" : "/data/np2kai-runtime/system/np2kai/FONT.ROM"); std::FILE* input = std::fopen(configPath, "rb"); if (input == nullptr) { std::printf( "[NP2kai] Cannot open runtime config: %s errno=%d\n", configPath, errno ); char detail[768] {}; std::snprintf(detail, sizeof(detail), "%s (errno=%d)", configPath, errno); setLastFailure("NP2-2002", "No se pudo abrir la configuración de NP2kai", "The NP2kai configuration could not be opened", "Comprueba que np2kai.cfg exista y sea escribible.", "Make sure np2kai.cfg exists and is writable.", detail); return false; } std::FILE* output = std::fopen(temporaryPath, "wb"); if (output == nullptr) { std::fclose(input); char detail[768] {}; std::snprintf(detail, sizeof(detail), "%s (errno=%d)", temporaryPath, errno); setLastFailure("NP2-2003", "No se pudo actualizar la configuración de NP2kai", "The NP2kai configuration could not be updated", "No se pudo crear el archivo temporal de configuración.", "The temporary configuration file could not be created.", detail); return false; } bool sawHdd1 = false; bool sawFont = false; bool success = true; char line[4096]; while (std::fgets(line, sizeof(line), input) != nullptr) { if (configLineMatches(line, "HDD1FILE")) { success = std::fprintf(output, "HDD1FILE = %s\n", gamePath) >= 0; sawHdd1 = true; if (!success) break; continue; } if (configLineMatches(line, "fontfile")) { success = std::fprintf(output, "fontfile = %s\n", runtimeFontPath) >= 0; sawFont = true; if (!success) break; continue; } if (std::fputs(line, output) < 0) { success = false; break; } } if (std::ferror(input) != 0) success = false; if (success && !sawHdd1) { success = std::fprintf(output, "HDD1FILE = %s\n", gamePath) >= 0; } if (success && !sawFont) { success = std::fprintf(output, "fontfile = %s\n", runtimeFontPath) >= 0; } if (std::fflush(output) != 0) success = false; std::fclose(output); std::fclose(input); if (!success) { std::remove(temporaryPath); setLastFailure("NP2-2004", "No se pudo escribir la configuración del juego", "The game configuration could not be written", "La escritura de np2kai.cfg no pudo completarse.", "Writing np2kai.cfg could not be completed.", temporaryPath); return false; } if (std::remove(configPath) != 0 && errno != ENOENT) { std::remove(temporaryPath); setLastFailure("NP2-2005", "No se pudo reemplazar la configuración anterior", "The previous configuration could not be replaced", "El archivo anterior de np2kai.cfg está bloqueado o no es escribible.", "The previous np2kai.cfg file is locked or not writable.", configPath); return false; } if (std::rename(temporaryPath, configPath) != 0) { setLastFailure("NP2-2006", "No se pudo activar la nueva configuración", "The new configuration could not be activated", "No se pudo renombrar el archivo temporal de configuración.", "The temporary configuration file could not be renamed.", temporaryPath); return false; } std::printf( "[NP2kai] Runtime config patched. HDD1FILE=%s\n", gamePath ); return true; } static void initializePersistentLog() { if (g_frontendLog != nullptr) return; std::remove(PREVIOUS_LOG_PATH); std::rename(LATEST_LOG_PATH, PREVIOUS_LOG_PATH); g_frontendLog = std::fopen(LATEST_LOG_PATH, "wb"); if (g_frontendLog != nullptr) { std::fprintf(g_frontendLog, "NP2kai PS4 v1.0 startup log\n"); std::fflush(g_frontendLog); } } static bool copyOptionalPackageFile( const char* source, const char* destination, bool preserveExisting, bool requiredWhenDestinationMissing) { if (fileSize(destination) > 0) return true; if (fileSize(source) <= 0) { if (requiredWhenDestinationMissing) { char detail[768] {}; std::snprintf(detail, sizeof(detail), "%s\n%s", source, destination); setLastFailure("NP2-1004", "Falta un archivo base de configuración", "A base configuration file is missing", "No se encontró el archivo ni en el PKG ni en el almacenamiento de la aplicación.", "The file was not found in either the PKG or the application storage.", detail); return false; } frontendLog("Optional package file not present: %s", source); return true; } if (!copyFileIfNeeded(source, destination, preserveExisting)) { char detail[768] {}; std::snprintf(detail, sizeof(detail), "%s -> %s", source, destination); setLastFailure("NP2-1003", "No se pudo preparar un archivo del sistema", "A system file could not be prepared", "Comprueba el espacio libre y los permisos de /data/np2kai-runtime/.", "Check free space and permissions for /data/np2kai-runtime/.", detail); return false; } return true; } static bool prepareWritableRuntime() { std::printf( "[NP2kai] Preparing writable runtime at %s...\n", RUNTIME_ROOT ); const char* directories[] = { RUNTIME_ROOT, SYSTEM_DIRECTORY, RUNTIME_SYSTEM_NP2, CONTENT_DIRECTORY, SAVE_DIRECTORY, CONFIG_DIRECTORY, GAME_CONFIG_DIRECTORY, LOG_DIRECTORY }; for (const char* directory : directories) { if (!ensureDirectory(directory)) { char detail[768] {}; std::snprintf(detail, sizeof(detail), "%s (errno=%d)", directory, errno); setLastFailure("NP2-1001", "No se pudo crear el almacenamiento de la aplicación", "Application storage could not be created", "Comprueba que haya espacio libre y que /data sea escribible.", "Check that free space is available and /data is writable.", detail); return false; } } initializePersistentLog(); frontendLog("Runtime directories ready at %s", RUNTIME_ROOT); struct RuntimeFile { const char* source; const char* destination; bool preserveExisting; }; static const RuntimeFile files[] = { { "/app0/assets/system/np2kai/bios.rom", "/data/np2kai-runtime/system/np2kai/" "bios.rom", false }, { "/app0/assets/system/np2kai/sound.rom", "/data/np2kai-runtime/system/np2kai/" "sound.rom", false }, { "/app0/assets/system/np2kai/itf.rom", "/data/np2kai-runtime/system/np2kai/" "itf.rom", false }, { "/app0/assets/system/np2kai/FONT.rom", "/data/np2kai-runtime/system/np2kai/" "FONT.rom", false }, { "/app0/assets/system/np2kai/font.bmp", "/data/np2kai-runtime/system/np2kai/" "font.bmp", false }, { "/app0/assets/system/np2kai/np2kai.cfg", "/data/np2kai-runtime/system/np2kai/" "np2kai.cfg", true } }; for (const RuntimeFile& file : files) { const bool configFile = std::strstr(file.destination, "np2kai.cfg") != nullptr; if (!copyOptionalPackageFile( file.source, file.destination, true, configFile )) { return false; } } return true; } static bool checkRequiredFiles() { char missing[512] {}; if (!collectMissingSystemFiles(missing, sizeof(missing))) { frontendLog("System file validation: OK"); return true; } char detail[768] {}; std::snprintf(detail, sizeof(detail), "%s\n\n%s", RUNTIME_SYSTEM_NP2, missing); setLastFailure("NP2-1002", "Faltan archivos del sistema de PC-98", "PC-98 system files are missing", "Copia tus propios archivos de sistema a la ruta indicada y vuelve a comprobarlos.", "Copy your own system files to the indicated path and check again.", detail); return false; } // --- primer arranque ------------------------------------------------------- // en vez de soltar un error seco cuando falta algo, mira que hay y que no y lo // enseña en una lista con las rutas puestas static const char* SETUP_COMPLETED_MARKER = "/data/np2kai-runtime/config/setup-completed"; struct SetupRequirement { const char* label; const char* detail; bool present; bool required; }; static bool contentDirectoryHasFiles() { DIR* directory = opendir(CONTENT_DIRECTORY); if (directory == nullptr) return false; bool found = false; while (const dirent* entry = readdir(directory)) { if (entry->d_name[0] == '.') continue; found = true; break; } closedir(directory); return found; } static unsigned collectSetupRequirements(SetupRequirement* items, unsigned capacity, bool* allRequiredPresent) { if (items == nullptr || capacity == 0) return 0; static const char* const romNames[] = {"bios.rom", "itf.rom", "sound.rom"}; unsigned count = 0; bool ready = true; for (const char* name : romNames) { if (count >= capacity) break; const bool present = systemFileAvailable(name); items[count++] = {name, tr("ROM del PC-98", "PC-98 ROM"), present, true}; if (!present) ready = false; } if (count < capacity) { const bool present = systemFileAvailable("font.bmp") || systemFileAvailable("FONT.rom") || systemFileAvailable("FONT.ROM"); items[count++] = {"font.bmp / FONT.rom", tr("vale cualquiera de las dos", "either one works"), present, true}; if (!present) ready = false; } if (count < capacity) { const bool present = systemFileAvailable("np2kai.cfg"); items[count++] = {"np2kai.cfg", tr("se copia sola al instalar", "copied in on install"), present, true}; if (!present) ready = false; } if (count < capacity) { items[count++] = {tr("Juegos (.hdi, .fdi, .d88...)", "Games (.hdi, .fdi, .d88...)"), tr("sin esto la biblioteca sale vacía", "without these the library is empty"), contentDirectoryHasFiles(), false}; } if (allRequiredPresent != nullptr) *allRequiredPresent = ready; return count; } static bool presentFirstRunSetup(bool* readyOut) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); uiHeader(output, "NP2kai PS4", tr("Primera configuración", "First-time setup")); SetupRequirement items[8] {}; bool ready = false; const unsigned count = collectSetupRequirements(items, 8, &ready); if (readyOut != nullptr) *readyOut = ready; const int listHeight = ui::CONTENT_END - ui::CONTENT_Y; uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, listHeight); int y = ui::CONTENT_Y + 10; for (unsigned index = 0; index < count; ++index) { const SetupRequirement& item = items[index]; const std::uint32_t color = item.present ? g_ui.good : (item.required ? g_ui.bad : g_ui.textDim); // marca redonda en vez de [OK] / [ ], que parecia una consola de texto if (item.present) { fillCircle(output, ui::LIST_X + 9, y + 14, 8, color); } else { drawIconRing(output, ui::LIST_X + 9, y + 14, 8, 2, color); } drawUiText(output, ui::LIST_X + 34, y, 25, item.present ? g_ui.textPrimary : g_ui.textMuted, item.label, ui::LIST_W - 40); drawUiText(output, ui::LIST_X + 34, y + 30, 19, g_ui.textDim, item.detail, ui::LIST_W - 40); y += 64; } int panelY = uiPanelTitle(output, ui::CONTENT_Y + 52, ready ? tr("Todo listo", "All set") : tr("Dónde va cada cosa", "Where things go")); if (ready) { uiPanelText(output, panelY, tr("Ya puedes continuar.", "You can continue now."), 2); } else { panelY = uiPanelField(output, panelY, tr("ROM y fuente", "ROMs and font"), RUNTIME_SYSTEM_NP2, g_ui.textPrimary); panelY = uiPanelField(output, panelY, tr("Juegos", "Games"), CONTENT_DIRECTORY, g_ui.textPrimary); uiPanelText(output, panelY + 6, tr("Por FTP con GoldHEN (puerto 2121) o desde un USB. Las ROM y los juegos no vienen incluidos.", "Over FTP with GoldHEN (port 2121) or from a USB drive. ROMs and games are not included."), 4); } uiFooter(output); int footerX = ui::MARGIN; if (ready) { footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr("Continuar", "Continue")); } footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Triangle, tr("Comprobar", "Check")); drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Square, tr("Guía", "Guide")); return uiFrameSubmit(); } static void markSetupCompleted() { FILE* marker = std::fopen(SETUP_COMPLETED_MARKER, "w"); if (marker == nullptr) return; std::fprintf(marker, "np2kai-ps4 v1.0 setup completed\n"); std::fclose(marker); } static bool firstRunSetupNeeded() { bool ready = false; SetupRequirement items[8] {}; collectSetupRequirements(items, 8, &ready); if (!ready) return true; return fileSize(SETUP_COMPLETED_MARKER) <= 0; } // devuelve false si el usuario se sale o si el video peta static bool runFirstRunSetup() { updatePadState(); std::uint32_t previousButtons = frontendUiButtons(); bool ready = false; if (!presentFirstRunSetup(&ready)) return false; frontendLog("First-run setup opened, requirements_ready=%d", ready ? 1 : 0); while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return false; if (!presentFirstRunSetup(&ready)) return false; updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previousButtons; previousButtons = current; if (ready && ((pressed & ORBIS_PAD_BUTTON_CROSS) != 0 || (pressed & ORBIS_PAD_BUTTON_OPTIONS) != 0)) { markSetupCompleted(); frontendLog("First-run setup completed"); return true; } if ((pressed & ORBIS_PAD_BUTTON_TRIANGLE) != 0) { if (!presentFirstRunSetup(&ready)) return false; updatePadState(); previousButtons = frontendUiButtons(); } if ((pressed & ORBIS_PAD_BUTTON_SQUARE) != 0) { if (!runInfoDocument(InfoDocument::Setup)) return false; if (!presentFirstRunSetup(&ready)) return false; updatePadState(); previousButtons = frontendUiButtons(); } // repintado continuo, el vsync marca el ritmo if (!presentFirstRunSetup(&ready)) return false; } return false; } static unsigned sdlKeycodeToRetroKey(SDL_Keycode keycode, std::uint32_t scancode) { if (keycode >= 0 && keycode <= 127) { if (keycode >= 'A' && keycode <= 'Z') keycode += 'a' - 'A'; if ((keycode >= 'a' && keycode <= 'z') || (keycode >= '0' && keycode <= '9')) { return static_cast(keycode); } switch (keycode) { case 8: return RETROK_BACKSPACE; case 9: return RETROK_TAB; case 13: return RETROK_RETURN; case 27: return RETROK_ESCAPE; case 32: return RETROK_SPACE; case 39: return RETROK_QUOTE; case 44: return RETROK_COMMA; case 45: return RETROK_MINUS; case 46: return RETROK_PERIOD; case 47: return RETROK_SLASH; case 59: return RETROK_SEMICOLON; case 61: return RETROK_EQUALS; case 91: return RETROK_LEFTBRACKET; case 92: return RETROK_BACKSLASH; case 93: return RETROK_RIGHTBRACKET; case 96: return RETROK_TILDE; case 127: return RETROK_DELETE; default: break; } } switch (keycode) { case SDLK_KP_0: return RETROK_KP0; case SDLK_KP_1: return RETROK_KP1; case SDLK_KP_2: return RETROK_KP2; case SDLK_KP_3: return RETROK_KP3; case SDLK_KP_4: return RETROK_KP4; case SDLK_KP_5: return RETROK_KP5; case SDLK_KP_6: return RETROK_KP6; case SDLK_KP_7: return RETROK_KP7; case SDLK_KP_8: return RETROK_KP8; case SDLK_KP_9: return RETROK_KP9; case SDLK_KP_PERIOD: return RETROK_KP_PERIOD; case SDLK_KP_DIVIDE: return RETROK_KP_DIVIDE; case SDLK_KP_MULTIPLY: return RETROK_KP_MULTIPLY; case SDLK_KP_MINUS: return RETROK_KP_MINUS; case SDLK_KP_PLUS: return RETROK_KP_PLUS; case SDLK_KP_ENTER: return RETROK_KP_ENTER; case SDLK_KP_EQUALS: return RETROK_KP_EQUALS; case SDLK_UP: return RETROK_UP; case SDLK_DOWN: return RETROK_DOWN; case SDLK_RIGHT: return RETROK_RIGHT; case SDLK_LEFT: return RETROK_LEFT; case SDLK_INSERT: return RETROK_INSERT; case SDLK_HOME: return RETROK_HOME; case SDLK_END: return RETROK_END; case SDLK_PAGEUP: return RETROK_PAGEUP; case SDLK_PAGEDOWN: return RETROK_PAGEDOWN; case SDLK_F1: return RETROK_F1; case SDLK_F2: return RETROK_F2; case SDLK_F3: return RETROK_F3; case SDLK_F4: return RETROK_F4; case SDLK_F5: return RETROK_F5; case SDLK_F6: return RETROK_F6; case SDLK_F7: return RETROK_F7; case SDLK_F8: return RETROK_F8; case SDLK_F9: return RETROK_F9; case SDLK_F10: return RETROK_F10; case SDLK_CAPSLOCK: return RETROK_CAPSLOCK; case SDLK_RSHIFT: return RETROK_RSHIFT; case SDLK_LSHIFT: return RETROK_LSHIFT; case SDLK_RCTRL: return RETROK_RCTRL; case SDLK_LCTRL: return RETROK_LCTRL; case SDLK_RALT: return RETROK_RALT; case SDLK_LALT: return RETROK_LALT; case SDLK_LGUI: return RETROK_LSUPER; case SDLK_RGUI: return RETROK_RSUPER; case SDLK_PRINTSCREEN: return RETROK_PRINT; case SDLK_MENU: return RETROK_MENU; default: break; } // el 72 es Pausa. lo pongo como numero porque SDLK_PAUSE no esta en las // cabeceras viejas de openorbis y no compilaba if (scancode == 72u) return RETROK_PAUSE; return RETROK_UNKNOWN; } static std::uint16_t sdlModifiersToRetro(std::uint16_t modifiers) { std::uint16_t result = RETROKMOD_NONE; if ((modifiers & KMOD_SHIFT) != 0) result |= RETROKMOD_SHIFT; if ((modifiers & KMOD_CTRL) != 0) result |= RETROKMOD_CTRL; if ((modifiers & KMOD_ALT) != 0) result |= RETROKMOD_ALT; if ((modifiers & KMOD_GUI) != 0) result |= RETROKMOD_META; if ((modifiers & KMOD_NUM) != 0) result |= RETROKMOD_NUMLOCK; if ((modifiers & KMOD_CAPS) != 0) result |= RETROKMOD_CAPSLOCK; return result; } // los codigos que suelta sceKeyboardReadState son usages HID (pagina 0x07). // aqui los paso a los codigos de libretro. lo que no existe en el pc-98 se tira static unsigned hidUsageToRetroKey(std::uint16_t usage) { if (usage >= 0x04 && usage <= 0x1D) { return RETROK_a + (usage - 0x04); } if (usage >= 0x1E && usage <= 0x26) { return RETROK_1 + (usage - 0x1E); } if (usage >= 0x3A && usage <= 0x43) { return RETROK_F1 + (usage - 0x3A); } if (usage >= 0x59 && usage <= 0x61) { return RETROK_KP1 + (usage - 0x59); } switch (usage) { case 0x27: return RETROK_0; case 0x28: return RETROK_RETURN; case 0x29: return RETROK_ESCAPE; case 0x2A: return RETROK_BACKSPACE; case 0x2B: return RETROK_TAB; case 0x2C: return RETROK_SPACE; case 0x2D: return RETROK_MINUS; case 0x2E: return RETROK_EQUALS; case 0x2F: return RETROK_LEFTBRACKET; case 0x30: return RETROK_RIGHTBRACKET; case 0x31: return RETROK_BACKSLASH; // la tecla de mas de los teclados no-US cae donde el pc-98 espera la // barra invertida extra case 0x32: return RETROK_BACKSLASH; case 0x33: return RETROK_SEMICOLON; case 0x34: return RETROK_QUOTE; case 0x35: return RETROK_TILDE; case 0x36: return RETROK_COMMA; case 0x37: return RETROK_PERIOD; case 0x38: return RETROK_SLASH; case 0x39: return RETROK_CAPSLOCK; // F11 y F12 no existen en el pc-98. me quedo F12 para la pausa, que es // el equivalente de teclado del L3+R3 case 0x44: return RETROK_F11; case 0x45: return RETROK_F12; case 0x46: return RETROK_PRINT; case 0x48: return RETROK_PAUSE; case 0x49: return RETROK_INSERT; case 0x4A: return RETROK_HOME; case 0x4B: return RETROK_PAGEUP; case 0x4C: return RETROK_DELETE; case 0x4D: return RETROK_END; case 0x4E: return RETROK_PAGEDOWN; case 0x4F: return RETROK_RIGHT; case 0x50: return RETROK_LEFT; case 0x51: return RETROK_DOWN; case 0x52: return RETROK_UP; case 0x53: return RETROK_NUMLOCK; case 0x54: return RETROK_KP_DIVIDE; case 0x55: return RETROK_KP_MULTIPLY; case 0x56: return RETROK_KP_MINUS; case 0x57: return RETROK_KP_PLUS; case 0x58: return RETROK_KP_ENTER; case 0x62: return RETROK_KP0; case 0x63: return RETROK_KP_PERIOD; case 0x64: return RETROK_BACKSLASH; case 0x65: return RETROK_MENU; case 0x67: return RETROK_KP_EQUALS; // teclas de teclado japones: ro, yen, henkan y muhenkan. las de // conversion las mando a los alt porque es donde estan en el selector. // sin probar: no tengo un teclado japones a mano case 0x87: return RETROK_BACKSLASH; case 0x89: return RETROK_BACKSLASH; case 0x8A: return RETROK_RALT; case 0x8B: return RETROK_LALT; case 0xE0: return RETROK_LCTRL; case 0xE1: return RETROK_LSHIFT; case 0xE2: return RETROK_LALT; case 0xE3: return RETROK_LSUPER; case 0xE4: return RETROK_RCTRL; case 0xE5: return RETROK_RSHIFT; case 0xE6: return RETROK_RALT; case 0xE7: return RETROK_RSUPER; default: break; } return RETROK_UNKNOWN; } static std::uint16_t nativeModifiersToRetro(std::uint16_t modifiers, std::uint16_t locks) { std::uint16_t result = RETROKMOD_NONE; if ((modifiers & (PS4_KBD_MOD_LEFT_SHIFT | PS4_KBD_MOD_RIGHT_SHIFT)) != 0) { result |= RETROKMOD_SHIFT; } if ((modifiers & (PS4_KBD_MOD_LEFT_CTRL | PS4_KBD_MOD_RIGHT_CTRL)) != 0) { result |= RETROKMOD_CTRL; } if ((modifiers & (PS4_KBD_MOD_LEFT_ALT | PS4_KBD_MOD_RIGHT_ALT)) != 0) { result |= RETROKMOD_ALT; } if ((modifiers & (PS4_KBD_MOD_LEFT_META | PS4_KBD_MOD_RIGHT_META)) != 0) { result |= RETROKMOD_META; } if ((locks & PS4_KBD_LOCK_NUM) != 0) result |= RETROKMOD_NUMLOCK; if ((locks & PS4_KBD_LOCK_CAPS) != 0) result |= RETROKMOD_CAPSLOCK; return result; } // todas las teclas pasan por aqui, vengan del backend nativo o de SDL. si // metes otra fuente de teclas, que entre por esta funcion y ya static void applyExternalKeyEvent(bool down, unsigned keycode, std::uint32_t character, std::uint16_t retroModifiers, bool repeat) { if (keycode == RETROK_UNKNOWN || keycode >= RETROK_LAST) return; g_externalKeyboardDetected = true; if (g_sdlTimerReady) g_lastExternalKeyboardEventTick = SDL_GetTicks(); if (down) setActiveInputLayout(InputLayout::Keyboard); const std::uint32_t uiMask = keyboardUiMaskForRetroKey(keycode); if (down) g_keyboardUiButtons |= uiMask; else g_keyboardUiButtons &= ~uiMask; // F12 es mia, no se la paso al pc-98 if (keycode == RETROK_F12) { g_keyboardPauseHeld = down; return; } g_externalKeyboardState[keycode] = down; if (down && !repeat) { g_lastExternalKeyboardKey = keycode; ++g_externalKeyboardEventGeneration; frontendLog("External keyboard key detected: retro_key=%u", keycode); } if (g_keyboardCallback != nullptr) { g_keyboardCallback(down, keycode, character, retroModifiers); } } static void processNativeKeyboardEvents() { // reintento para que valga enchufar el teclado con la app ya abierta if (!g_nativeKeyboardOpened) tryOpenNativeKeyboardBackend(); if (!ps4KeyboardIsOpen()) { g_nativeKeyboardOpened = false; return; } Ps4KeyboardEvent events[NATIVE_KEYBOARD_MAX_EVENTS_PER_POLL]; const unsigned count = ps4KeyboardPoll(events, NATIVE_KEYBOARD_MAX_EVENTS_PER_POLL); // si la lectura fallo, el backend ya solto el handle. me entero aqui if (!ps4KeyboardIsOpen()) g_nativeKeyboardOpened = false; for (unsigned index = 0; index < count; ++index) { const Ps4KeyboardEvent& event = events[index]; const unsigned keycode = hidUsageToRetroKey(event.usage); if (keycode == RETROK_UNKNOWN) continue; const char character = event.down ? ps4KeyboardUsageToCharacter(event.usage, event.modifiers, event.locks) : 0; applyExternalKeyEvent( event.down, keycode, static_cast(static_cast(character)), nativeModifiersToRetro(event.modifiers, event.locks), false); } } static void processSdlFrontendEvents(); static void processFrontendEvents() { // lo que llega por text input no trae "tecla soltada", asi que lo dejo // pulsado un solo frame y lo quito g_keyboardUiButtons &= ~g_keyboardUiPulseButtons; g_keyboardUiPulseButtons = 0; // el teclado nativo no depende de SDL para nada, asi que va primero y va // aunque SDL no tenga cola de eventos processNativeKeyboardEvents(); if (!g_externalKeyboardEventSubsystemReady) return; processSdlFrontendEvents(); } static void processSdlFrontendEvents() { SDL_Event event {}; while (SDL_PollEvent(&event) != 0) { if (event.type == SDL_QUIT) { g_running = false; continue; } if (event.type == SDL_TEXTINPUT) { for (unsigned index = 0; index < sizeof(event.text.text) && event.text.text[index] != '\0'; ++index) { const unsigned char character = static_cast(event.text.text[index]); if (character >= 0x80u) continue; const unsigned keycode = sdlKeycodeToRetroKey( static_cast(character), 0); if (keycode == RETROK_UNKNOWN || keycode >= RETROK_LAST) continue; // aqui no hay tecla soltada, asi que la pulso y la suelto yo g_keyboardUiPulseButtons |= keyboardUiMaskForRetroKey(keycode); frontendLog("External keyboard text fallback: retro_key=%u", keycode); applyExternalKeyEvent(true, keycode, character, RETROKMOD_NONE, false); applyExternalKeyEvent(false, keycode, character, RETROKMOD_NONE, false); g_keyboardUiButtons |= keyboardUiMaskForRetroKey(keycode); } continue; } if (event.type != SDL_KEYDOWN && event.type != SDL_KEYUP) continue; const bool down = event.type == SDL_KEYDOWN; const unsigned keycode = sdlKeycodeToRetroKey( event.key.keysym.sym, event.key.keysym.scancode); applyExternalKeyEvent(down, keycode, 0, sdlModifiersToRetro(event.key.keysym.mod), event.key.repeat != 0); } } static void clearAudioQueueForPause() { const std::uint32_t write = g_audioWriteIndex.load(std::memory_order_acquire); g_audioReadIndex.store(write, std::memory_order_release); g_audioPlaybackStarted.store(false, std::memory_order_release); g_audioHavePreviousFrame = false; g_audioResamplePhase = 0; } static void setAudioPaused(bool paused) { g_audioPaused.store(paused, std::memory_order_release); clearAudioQueueForPause(); } static void applyAudioSettingToRunningGame() { if (!g_gameLoaded) return; if (!g_audioEnabled && g_audioHandle >= 0) shutdownAudioOutput(); else if (g_audioEnabled && g_audioHandle < 0) initializeAudioOutput(g_currentAudioSampleRate); } static bool presentLoadingScreen(const GameEntry& game, int progress, unsigned spinnerStep) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); // centrado y sin adornos: se ve un segundo const char* label = tr("Cargando", "Loading"); const int labelWidth = uiTextWidth(label, 34); drawUiText(output, (OUTPUT_WIDTH - labelWidth) / 2, OUTPUT_HEIGHT / 2 - 60, 34, g_ui.textPrimary, label, 600); if (game.name[0] != '\0') { const int nameWidth = uiTextWidth(game.name, 22); drawUiText(output, (OUTPUT_WIDTH - nameWidth) / 2, OUTPUT_HEIGHT / 2 - 12, 22, g_ui.textMuted, game.name, 900); } (void)progress; (void)spinnerStep; // barra fina con un bloque que recorre. no sabemos cuanto falta, asi que // no tiene sentido una barra de progreso de verdad const int barW = 420; const int barX = (OUTPUT_WIDTH - barW) / 2; const int barY = OUTPUT_HEIGHT / 2 + 40; fillRectangle(output, barX, barY, barW, 3, g_ui.panel); const int pos = static_cast((SDL_GetTicks() / 6u) % static_cast(barW + 120)) - 120; int blockX = barX + pos; int blockW = 120; if (blockX < barX) { blockW -= barX - blockX; blockX = barX; } if (blockX + blockW > barX + barW) blockW = barX + barW - blockX; if (blockW > 0) uiRoundedRect(output, blockX, barY, blockW, 3, 1, g_ui.focusOutline); return uiFrameSubmit(); } static bool animateLoadingUntil(const GameEntry& game, int progress, Uint32 endTick, unsigned& spinner) { do { if (!presentLoadingScreen(game, progress, spinner++)) { return false; } SDL_Delay(20); } while (static_cast(SDL_GetTicks() - endTick) < 0); return true; } // --- estados de guardado --------------------------------------------------- // el core hace lo dificil: retro_serialize vuelca la maquina entera y // retro_unserialize la restaura. aqui solo hay que gestionar archivos. // // OJO con retro_serialize_size(): no es un getter. por dentro hace una // grabacion completa a disco, la mide y la borra. o sea que llamarla cuesta lo // mismo que guardar, y no se puede ir preguntando el tamaño cada frame como // hacen otros frontends. aqui se llama una sola vez, justo antes de guardar. // // el archivo temporal del core sale del directorio de la fuente // (file_setcd(np2cfg.fontfile)), que es /data/np2kai-runtime/system/np2kai/. // escribible, por suerte: si hubiera caido en /app0 no habria estados extern "C" std::size_t retro_serialize_size(void); extern "C" bool retro_serialize(void* data, std::size_t size); extern "C" bool retro_unserialize(const void* data, std::size_t size); static const char* STATE_DIRECTORY = "/data/np2kai-runtime/states"; static constexpr unsigned STATE_SLOT_COUNT = 4; static constexpr std::uint32_t STATE_MAGIC = 0x324B504Eu; // "NPK2" struct StateHeader { std::uint32_t magic; std::uint32_t version; std::uint64_t payload; // bytes de estado que vienen detras std::int64_t saved; // segundos desde el arranque de la sesion char game[MAX_GAME_NAME]; }; struct StateSlotInfo { bool used; std::uint64_t payload; std::int64_t saved; }; static void makeStatePath(const GameEntry& game, unsigned slot, char* destination, std::size_t capacity) { char safeName[64] {}; std::size_t output = 0; const char* source = game.name; for (std::size_t index = 0; source[index] != '\0' && output + 1 < sizeof(safeName); ++index) { const unsigned char character = static_cast(source[index]); const bool alphaNumeric = (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || (character >= '0' && character <= '9'); safeName[output++] = alphaNumeric ? static_cast(character) : '_'; } if (output == 0) std::snprintf(safeName, sizeof(safeName), "game"); std::snprintf(destination, capacity, "%s/%s-%08x-s%u.state", STATE_DIRECTORY, safeName, hashGameControlPath(game.path), slot); } static bool readStateSlot(const GameEntry& game, unsigned slot, StateSlotInfo& info) { info = StateSlotInfo{}; char path[MAX_GAME_PATH] {}; makeStatePath(game, slot, path, sizeof(path)); FILE* file = std::fopen(path, "rb"); if (file == nullptr) return false; StateHeader header {}; const bool ok = std::fread(&header, sizeof(header), 1, file) == 1 && header.magic == STATE_MAGIC && header.payload > 0; std::fclose(file); if (!ok) return false; info.used = true; info.payload = header.payload; info.saved = header.saved; return true; } static bool saveStateToSlot(const GameEntry& game, unsigned slot, char* problem, std::size_t problemCapacity) { ensureDirectory(STATE_DIRECTORY); const std::size_t size = retro_serialize_size(); if (size == 0) { std::snprintf(problem, problemCapacity, "retro_serialize_size devolvio 0"); return false; } void* buffer = std::malloc(size); if (buffer == nullptr) { std::snprintf(problem, problemCapacity, "sin memoria para %zu bytes", size); return false; } if (!retro_serialize(buffer, size)) { std::free(buffer); std::snprintf(problem, problemCapacity, "retro_serialize fallo"); return false; } char path[MAX_GAME_PATH] {}; char temporary[MAX_GAME_PATH] {}; makeStatePath(game, slot, path, sizeof(path)); std::snprintf(temporary, sizeof(temporary), "%s.tmp", path); // primero al temporal y luego rename: si se corta la luz a mitad, el estado // anterior sigue entero en vez de quedarse a medias FILE* file = std::fopen(temporary, "wb"); if (file == nullptr) { std::free(buffer); std::snprintf(problem, problemCapacity, "no puedo escribir en %s", STATE_DIRECTORY); return false; } StateHeader header {}; header.magic = STATE_MAGIC; header.version = 1u; header.payload = size; header.saved = static_cast(SDL_GetTicks() / 1000u); std::snprintf(header.game, sizeof(header.game), "%s", game.name); const bool written = std::fwrite(&header, sizeof(header), 1, file) == 1 && std::fwrite(buffer, 1, size, file) == size; std::fclose(file); std::free(buffer); if (!written) { std::remove(temporary); std::snprintf(problem, problemCapacity, "escritura incompleta"); return false; } std::remove(path); if (std::rename(temporary, path) != 0) { std::remove(temporary); std::snprintf(problem, problemCapacity, "no pude renombrar el archivo"); return false; } frontendLog("State saved: slot=%u bytes=%zu", slot, size); return true; } static bool loadStateFromSlot(const GameEntry& game, unsigned slot, char* problem, std::size_t problemCapacity) { char path[MAX_GAME_PATH] {}; makeStatePath(game, slot, path, sizeof(path)); FILE* file = std::fopen(path, "rb"); if (file == nullptr) { std::snprintf(problem, problemCapacity, "la ranura esta vacia"); return false; } StateHeader header {}; if (std::fread(&header, sizeof(header), 1, file) != 1 || header.magic != STATE_MAGIC || header.payload == 0) { std::fclose(file); std::snprintf(problem, problemCapacity, "el archivo no es un estado valido"); return false; } void* buffer = std::malloc(static_cast(header.payload)); if (buffer == nullptr) { std::fclose(file); std::snprintf(problem, problemCapacity, "sin memoria para %llu bytes", static_cast(header.payload)); return false; } const std::size_t read = std::fread(buffer, 1, static_cast(header.payload), file); std::fclose(file); if (read != header.payload) { std::free(buffer); std::snprintf(problem, problemCapacity, "archivo truncado"); return false; } const bool ok = retro_unserialize(buffer, static_cast(header.payload)); std::free(buffer); if (!ok) { std::snprintf(problem, problemCapacity, "retro_unserialize rechazo el estado"); return false; } frontendLog("State loaded: slot=%u bytes=%llu", slot, static_cast(header.payload)); return true; } static void deleteStateSlot(const GameEntry& game, unsigned slot) { char path[MAX_GAME_PATH] {}; makeStatePath(game, slot, path, sizeof(path)); std::remove(path); } // --- pantalla de ranuras --------------------------------------------------- static char g_stateStatus[192] = ""; // al cargar un estado hay que volver al juego, no al menu: lo que se ve detras // del menu es el frame de antes de cargar y confunde static bool g_stateJustLoaded = false; static bool presentSaveStateScreen(const GameEntry& game, unsigned selection) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); uiHeader(output, game.name, tr("Estados de guardado", "Save states")); const int rowPitch = ui::ROW_H + ui::ROW_GAP; const int listHeight = ui::CONTENT_END - ui::CONTENT_Y; StateSlotInfo info[STATE_SLOT_COUNT] {}; for (unsigned slot = 0; slot < STATE_SLOT_COUNT; ++slot) { readStateSlot(game, slot, info[slot]); } uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, listHeight); char label[96] {}; std::snprintf(label, sizeof(label), tr("Ranura %u", "Slot %u"), selection + 1); int panelY = uiPanelTitle(output, ui::CONTENT_Y + 52, label); if (info[selection].used) { char value[96] {}; const unsigned long long kb = info[selection].payload / 1024ull; std::snprintf(value, sizeof(value), "%llu KB", kb); panelY = uiPanelField(output, panelY, tr("Tamaño", "Size"), value, g_ui.textPrimary); panelY = uiPanelText(output, panelY + 6, tr("Triángulo carga esta ranura. X la sobrescribe.", "Triangle loads this slot. Cross overwrites it."), 3); } else { panelY = uiPanelText(output, panelY, tr("Ranura vacía. Pulsa X para guardar aquí la partida tal y como está ahora mismo.", "Empty slot. Press Cross to save the game exactly as it is right now."), 4); } if (g_stateStatus[0] != '\0') { drawUiText(output, ui::PANEL_X + 44, ui::CONTENT_END - 58, 22, g_ui.textMuted, g_stateStatus, ui::PANEL_W - 88); } uiDrawMovingFocus(output, g_listFocusY[3], ui::LIST_X, ui::CONTENT_Y + static_cast(selection) * rowPitch, ui::LIST_W, ui::ROW_H, g_ui.focusFill, g_ui.focusOutline); for (unsigned slot = 0; slot < STATE_SLOT_COUNT; ++slot) { const int y = ui::CONTENT_Y + static_cast(slot) * rowPitch; std::snprintf(label, sizeof(label), tr("Ranura %u", "Slot %u"), slot + 1); uiRow(output, ui::LIST_X, y, ui::LIST_W, slot == selection, nullptr, label, info[slot].used ? tr("ocupada", "used") : tr("vacía", "empty")); } uiFooter(output); int footerX = ui::MARGIN; footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr("Guardar", "Save")); if (info[selection].used) { footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Triangle, tr("Cargar", "Load")); footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Square, tr("Borrar", "Delete")); } drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Atrás", "Back")); return uiFrameSubmit(); } // devuelve false solo si hay que cerrar la aplicacion static bool runSaveStateMenu(const GameEntry& game) { unsigned selection = 0; g_stateStatus[0] = '\0'; g_stateJustLoaded = false; g_listFocusY[3] = -1.0f; updatePadState(); std::uint32_t previous = frontendUiButtons(); int heldDirection = 0; Uint32 nextRepeat = 0; while (g_running) { processFrontendEvents(); updatePadState(); const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previous; previous = current; int direction = 0; if ((current & ORBIS_PAD_BUTTON_UP) != 0) direction = -1; else if ((current & ORBIS_PAD_BUTTON_DOWN) != 0) direction = 1; const Uint32 now = SDL_GetTicks(); if (direction == 0) { heldDirection = 0; } else if (heldDirection != direction) { selection = direction < 0 ? (selection == 0 ? STATE_SLOT_COUNT - 1 : selection - 1) : (selection + 1) % STATE_SLOT_COUNT; heldDirection = direction; nextRepeat = now + 320u; } else if (static_cast(now - nextRepeat) >= 0) { selection = direction < 0 ? (selection == 0 ? STATE_SLOT_COUNT - 1 : selection - 1) : (selection + 1) % STATE_SLOT_COUNT; nextRepeat = now + 95u; } char problem[160] {}; if ((pressed & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); if (saveStateToSlot(game, selection, problem, sizeof(problem))) { std::snprintf(g_stateStatus, sizeof(g_stateStatus), tr("Guardado en la ranura %u", "Saved to slot %u"), selection + 1); } else { std::snprintf(g_stateStatus, sizeof(g_stateStatus), tr("No se pudo guardar: %s", "Could not save: %s"), problem); } } if ((pressed & ORBIS_PAD_BUTTON_TRIANGLE) != 0) { if (loadStateFromSlot(game, selection, problem, sizeof(problem))) { g_stateJustLoaded = true; return true; } std::snprintf(g_stateStatus, sizeof(g_stateStatus), tr("No se pudo cargar: %s", "Could not load: %s"), problem); } if ((pressed & ORBIS_PAD_BUTTON_SQUARE) != 0) { deleteStateSlot(game, selection); std::snprintf(g_stateStatus, sizeof(g_stateStatus), tr("Ranura %u borrada", "Slot %u cleared"), selection + 1); } if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0) return true; if (!presentSaveStateScreen(game, selection)) return false; } return false; } static constexpr unsigned PAUSE_ROW_COUNT = 7; static const char* pauseRowLabel(unsigned row) { static const char* es[] = {"Continuar", "Estados de guardado", "Cambiar de disco", "Controles", "Reiniciar juego", "Ajustes", "Volver a la biblioteca"}; static const char* en[] = {"Continue", "Save states", "Swap disk", "Controls", "Restart game", "Settings", "Return to library"}; return interfaceIsEnglish() ? en[row] : es[row]; } static int g_pendingDiskIndex = -1; static bool presentDiskSwapScreen(const GameEntry& current, unsigned selection) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; drawPS4StyleBackground(output); uiHeader(output, current.name, tr("Cambiar de disco", "Swap disk")); const int rowPitch = ui::ROW_H + ui::ROW_GAP; const int listHeight = ui::CONTENT_END - ui::CONTENT_Y; const unsigned visible = static_cast(ui::rowsThatFit(listHeight)); unsigned scroll = 0; if (selection >= visible) scroll = selection - visible + 1; uiPanel(output, ui::PANEL_X, ui::CONTENT_Y, ui::PANEL_W, listHeight); int panelY = uiPanelTitle(output, ui::CONTENT_Y + 52, g_gameEntries[selection].name); panelY = uiPanelText(output, panelY, tr("Se cerrará el juego actual y se abrirá esta imagen. Guarda antes si no quieres perder la partida.", "The current game will be closed and this image opened. Save first if you do not want to lose your progress."), 4); if (std::strcmp(g_gameEntries[selection].path, current.path) == 0) { uiPanelField(output, panelY + 10, tr("Aviso", "Note"), tr("es el disco que ya está puesto", "this is the disk already inserted"), g_ui.textDim); } uiDrawMovingFocus(output, g_listFocusY[3], ui::LIST_X, ui::CONTENT_Y + static_cast(selection - scroll) * rowPitch, ui::LIST_W, ui::ROW_H, g_ui.focusFill, g_ui.focusOutline); for (unsigned row = scroll; row < g_gameEntryCount && row < scroll + visible; ++row) { const int y = ui::CONTENT_Y + static_cast(row - scroll) * rowPitch; const UiIcon icon = UiIcon::Game; uiRow(output, ui::LIST_X, y, ui::LIST_W, row == selection, &icon, g_gameEntries[row].name, std::strcmp(g_gameEntries[row].path, current.path) == 0 ? tr("puesto", "inserted") : nullptr); } uiScrollBar(output, ui::LIST_X + ui::LIST_W + 18, ui::CONTENT_Y, listHeight, g_gameEntryCount, visible, static_cast(scroll * rowPitch), rowPitch); uiFooter(output); int footerX = ui::MARGIN; footerX = drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Cross, tr("Poner este disco", "Insert this disk")); drawFooterHintPolished(output, footerX, ui::FOOTER_Y, FooterButton::Circle, tr("Atrás", "Back")); return uiFrameSubmit(); } // devuelve el indice del juego elegido, -1 si se cancela, -2 si hay que salir. // no intento cambiar la imagen en caliente: se cierra el juego y se abre el // otro, que es mas lento pero no deja al core a medias static int runDiskSwapMenu(const GameEntry& current) { if (g_gameEntryCount == 0) return -1; unsigned selection = g_librarySelection < g_gameEntryCount ? g_librarySelection : 0; g_listFocusY[3] = -1.0f; updatePadState(); std::uint32_t previous = frontendUiButtons(); int held = 0; Uint32 nextRepeat = 0; while (g_running) { processFrontendEvents(); updatePadState(); const std::uint32_t buttons = frontendUiButtons(); const std::uint32_t pressed = buttons & ~previous; previous = buttons; int direction = 0; if ((buttons & ORBIS_PAD_BUTTON_UP) != 0) direction = -1; else if ((buttons & ORBIS_PAD_BUTTON_DOWN) != 0) direction = 1; const Uint32 now = SDL_GetTicks(); if (direction == 0) held = 0; else if (held != direction) { selection = direction < 0 ? (selection == 0 ? g_gameEntryCount - 1 : selection - 1) : (selection + 1) % g_gameEntryCount; held = direction; nextRepeat = now + 320u; } else if (static_cast(now - nextRepeat) >= 0) { selection = direction < 0 ? (selection == 0 ? g_gameEntryCount - 1 : selection - 1) : (selection + 1) % g_gameEntryCount; nextRepeat = now + 95u; } if ((pressed & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); return static_cast(selection); } if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0) return -1; if (!presentDiskSwapScreen(current, selection)) return -2; } return -2; } static bool presentPauseScreen(unsigned selection, const GameEntry& game, int panelOffset = 0) { std::uint32_t* output = uiFrameBegin(); if (output == nullptr) return false; g_pauseEnter = uiApproachUnit(g_pauseEnter, 1.0f, UI_TAU_SCREEN); panelOffset += uiRound((1.0f - g_pauseEnter) * 380.0f); if (!blitLatestFrame(output)) drawPS4StyleBackground(output); darkenFrame(output, 150); const int panelX = 1030 + panelOffset; blendRectangle(output, panelX, 0, OUTPUT_WIDTH - panelX, OUTPUT_HEIGHT, encodeDisplayColor(g_ui.bottomR, g_ui.bottomG, g_ui.bottomB), 235); const std::uint32_t white = g_ui.textPrimary; const std::uint32_t muted = g_ui.textMuted; const std::uint32_t selectedFill = g_ui.focusFill; drawUiText(output, panelX + 70, 70, 54, white, tr("Pausa", "Paused")); drawUiText(output, panelX + 72, 140, 21, muted, game.name, 740); const int listTop = 220, rowHeight = 82, listWidth = 760; // tantas filas como etiquetas. estaba a 6 fijo y al añadir una se perdia // la ultima sin avisar for (unsigned row = 0; row < PAUSE_ROW_COUNT; ++row) { const int y = listTop + static_cast(row) * rowHeight; if (row == selection) { fillRectangle(output, panelX + 44, y, listWidth, rowHeight - 8, selectedFill); drawOutlineRectangle(output, panelX + 44, y, listWidth, rowHeight - 8, 2, white); } drawUiText(output, panelX + 76, y + 22, 30, row == selection ? white : muted, pauseRowLabel(row), 680); } drawFooterBarBase(output); int footerX = 100; footerX = drawFooterHintPolished(output, footerX, 970, FooterButton::Cross, tr("Seleccionar", "Select")); footerX = drawFooterHintPolished(output, footerX, 970, FooterButton::Circle, tr("Continuar", "Continue")); drawFooterHintPolished(output, footerX, 970, FooterButton::StickCombo, tr("Continuar", "Continue")); return uiFrameSubmit(); } static bool animatePauseScreen(unsigned selection, const GameEntry& game, bool opening) { // al cerrar no animo nada: justo despues vuelve el juego y no daria tiempo // a verlo. al abrir, el panel entra deslizando desde la derecha if (!opening) return true; g_pauseEnter = g_uiAnimationsEnabled ? 0.0f : 1.0f; g_pauseFocusY = -1.0f; return presentPauseScreen(selection, game, 0); } static PauseAction runPauseMenu(const GameEntry& game) { setAudioPaused(true); updatePadState(); std::uint32_t previousButtons = frontendUiButtons(); unsigned selection = 0; int heldDirection = 0; Uint32 nextRepeatTick = 0; if (!animatePauseScreen(selection, game, true)) return PauseAction::Quit; const Uint32 comboGuardUntil = SDL_GetTicks() + 260u; bool comboWasDown = true; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) return PauseAction::Quit; if (!presentPauseScreen(selection, game)) return PauseAction::Quit; updatePadState(); previousButtons = frontendUiButtons(); } const std::uint32_t current = frontendUiButtons(); const std::uint32_t pressed = current & ~previousButtons; previousButtons = current; int direction = 0; if ((current & ORBIS_PAD_BUTTON_UP) != 0 || leftStickUp()) direction = -1; else if ((current & ORBIS_PAD_BUTTON_DOWN) != 0 || leftStickDown()) direction = 1; const Uint32 now = SDL_GetTicks(); if (direction == 0) heldDirection = 0; else if (direction != heldDirection) { selection = direction < 0 ? (selection == 0 ? 6 : selection - 1) : (selection + 1) % 7; heldDirection = direction; nextRepeatTick = now + 320u; } else if (static_cast(now - nextRepeatTick) >= 0) { selection = direction < 0 ? (selection == 0 ? 6 : selection - 1) : (selection + 1) % 7; nextRepeatTick = now + 95u; } const bool combo = frontendPauseShortcutHeld(); const bool comboPressed = combo && !comboWasDown; comboWasDown = combo; if ((pressed & ORBIS_PAD_BUTTON_CIRCLE) != 0 || (comboPressed && static_cast(now - comboGuardUntil) >= 0)) { animatePauseScreen(selection, game, false); setAudioPaused(false); return PauseAction::Continue; } if ((pressed & ORBIS_PAD_BUTTON_CROSS) != 0) { uiTriggerPress(); if (selection == 0) { animatePauseScreen(selection, game, false); setAudioPaused(false); return PauseAction::Continue; } if (selection == 1) { if (!runSaveStateMenu(game)) return PauseAction::Quit; if (g_stateJustLoaded) { setAudioPaused(false); return PauseAction::Continue; } updatePadState(); previousButtons = frontendUiButtons(); if (!presentPauseScreen(selection, game)) return PauseAction::Quit; continue; } if (selection == 2) { const int elegido = runDiskSwapMenu(game); if (elegido == -2) return PauseAction::Quit; if (elegido >= 0) { g_pendingDiskIndex = elegido; return PauseAction::SwapDisk; } updatePadState(); previousButtons = frontendUiButtons(); if (!presentPauseScreen(selection, game)) return PauseAction::Quit; continue; } if (selection == 3) { if (!runGameControlsMenu(game)) return PauseAction::Quit; updatePadState(); previousButtons = frontendUiButtons(); if (!animatePauseScreen(selection, game, true)) return PauseAction::Quit; } if (selection == 4) return PauseAction::Restart; if (selection == 5) { if (!runSettingsMenu()) return PauseAction::Quit; applyAudioSettingToRunningGame(); updatePadState(); previousButtons = frontendUiButtons(); if (!animatePauseScreen(selection, game, true)) return PauseAction::Quit; } if (selection == 6) return PauseAction::Library; } if (!presentPauseScreen(selection, game)) return PauseAction::Quit; SDL_Delay(16); } return PauseAction::Quit; } static void stopCurrentGame() { setAudioPaused(false); shutdownAudioOutput(); if (g_gameLoaded) { retro_unload_game(); g_gameLoaded = false; } if (g_coreInitialized) { retro_deinit(); g_coreInitialized = false; } g_activeGameHasControlOverride = false; g_activeControls = g_defaultControls; g_activeGameControlPath[0] = '\0'; g_frameReady = false; for (int index = 0; index < DISPLAY_BUFFER_COUNT; ++index) g_displayBufferCleared[index] = false; } static bool startGame(const GameEntry& game) { g_audioWarningShownThisSession = false; if (!readableFile(game.path)) { setLastFailure("NP2-2101", "No se puede leer el juego seleccionado", "The selected game cannot be read", "El archivo se ha movido, eliminado o no es accesible.", "The file was moved, deleted, or is not accessible.", game.path); return false; } unsigned spinner = 0; const Uint32 minimumEnd = SDL_GetTicks() + 520u; if (!presentLoadingScreen(game, 8, spinner++)) { setLastFailure("NP2-3202", "No se pudo mostrar la pantalla de carga", "The loading screen could not be shown", "sceVideoOut no pudo presentar la interfaz.", "sceVideoOut could not present the interface."); return false; } if (!patchRuntimeConfigForGame(game.path)) return false; if (!loadGameControls(game)) { frontendLog("Per-game controls were damaged; default profile selected for %s", game.path); } if (!presentLoadingScreen(game, 30, spinner++)) { setLastFailure("NP2-3202", "No se pudo mostrar la pantalla de carga", "The loading screen could not be shown", "sceVideoOut no pudo presentar la interfaz.", "sceVideoOut could not present the interface."); return false; } retro_init(); g_coreInitialized = true; retro_game_info info {}; info.path = game.path; if (!retro_load_game(&info)) { setLastFailure("NP2-2102", "NP2kai no pudo cargar el juego", "NP2kai could not load the game", "Comprueba el formato del archivo y que la imagen no esté dañada.", "Check the file format and make sure the image is not corrupted.", game.path); stopCurrentGame(); return false; } g_gameLoaded = true; if (!presentLoadingScreen(game, 72, spinner++)) { setLastFailure("NP2-3202", "No se pudo mostrar la pantalla de carga", "The loading screen could not be shown", "sceVideoOut no pudo presentar la interfaz.", "sceVideoOut could not present the interface."); stopCurrentGame(); return false; } retro_system_av_info avInfo {}; retro_get_system_av_info(&avInfo); g_displayAspect = 4.0 / 3.0; g_targetFps = avInfo.timing.fps > 1.0 ? avInfo.timing.fps : 56.4; g_currentAudioSampleRate = avInfo.timing.sample_rate; if (g_audioEnabled && !initializeAudioOutput(avInfo.timing.sample_rate)) { char audioDetail[256] {}; std::snprintf(audioDetail, sizeof(audioDetail), "AudioOut stage=%u sample_rate=%.2f", g_audioFailureStage.load(std::memory_order_relaxed), avInfo.timing.sample_rate); for (;;) { const DialogAction action = runErrorDialog("NP2-3101", "No se pudo iniciar el audio", "Audio could not be started", "Puedes reintentarlo o continuar temporalmente sin sonido.", "You can retry or continue temporarily without sound.", audioDetail, "Continuar sin audio", "Continue without audio", true, true); if (action == DialogAction::Retry) { shutdownAudioOutput(); if (initializeAudioOutput(avInfo.timing.sample_rate)) break; continue; } if (action == DialogAction::Primary) break; if (action == DialogAction::Back) { setLastFailure("NP2-3102", "Inicio del juego cancelado", "Game startup cancelled", "Se volvió a la biblioteca porque el audio no pudo iniciarse.", "Returned to the library because audio could not be started.", audioDetail); stopCurrentGame(); return false; } g_running = false; stopCurrentGame(); return false; } } if (!animateLoadingUntil(game, 100, minimumEnd, spinner)) { setLastFailure("NP2-3203", "La pantalla de carga se interrumpió", "The loading screen was interrupted", "No se pudo completar la transición al juego.", "The transition into the game could not be completed."); stopCurrentGame(); return false; } for (int index = 0; index < DISPLAY_BUFFER_COUNT; ++index) g_displayBufferCleared[index] = false; showSolidScreen(0, 0, 0); return true; } static bool restartCurrentGameInPlace(const GameEntry& game) { // el retro_unload_game() de np2kai esta vacio y hacer deinit/init una y // otra vez va dejando memoria por el camino. para reiniciar el mismo juego // uso retro_reset y listo setAudioPaused(true); unsigned spinner = 0; const Uint32 minimumEnd = SDL_GetTicks() + 360u; if (!presentLoadingScreen(game, 18, spinner++)) { setLastFailure("NP2-3204", "No se pudo iniciar el reinicio", "The restart could not begin", "No se pudo mostrar la pantalla de reinicio.", "The restart screen could not be displayed."); setAudioPaused(false); return false; } g_frameReady = false; retro_reset(); clearAudioQueueForPause(); if (!animateLoadingUntil(game, 100, minimumEnd, spinner)) { setLastFailure("NP2-3205", "El reinicio no pudo completarse", "The restart could not be completed", "La transición de vídeo falló después de retro_reset().", "The video transition failed after retro_reset()."); setAudioPaused(false); return false; } for (int index = 0; index < DISPLAY_BUFFER_COUNT; ++index) { g_displayBufferCleared[index] = false; } setAudioPaused(false); showSolidScreen(0, 0, 0); std::printf("[NP2kai/lifecycle] in-place retro_reset completed.\n"); return true; } static GameSessionResult runGameSession(const GameEntry& game) { std::snprintf(g_activeGameName, sizeof(g_activeGameName), "%s", game.name); const Uint64 performanceFrequency = SDL_GetPerformanceFrequency(); Uint64 statisticsStart = SDL_GetPerformanceCounter(); Uint64 accumulatedRunTicks = 0, accumulatedPresentTicks = 0; unsigned statisticsFrames = 0, statisticsPresents = 0, framesUntilPresent = 0; unsigned long long totalEmulatedFrames = 0, totalPresentedFrames = 0; const double frameMilliseconds = g_targetFps > 1.0 ? 1000.0 / g_targetFps : 1000.0 / 56.4; Uint64 nextDeadline = SDL_GetPerformanceCounter(); bool pauseComboPrevious = false; Uint32 controllerDisconnectedSince = 0; while (g_running) { processFrontendEvents(); updatePadState(); if (!frontendInputAvailable()) { if (controllerDisconnectedSince == 0) controllerDisconnectedSince = SDL_GetTicks(); if (SDL_GetTicks() - controllerDisconnectedSince >= 800u) { setAudioPaused(true); if (!waitForInputDevice()) return GameSessionResult::Quit; setAudioPaused(false); controllerDisconnectedSince = 0; nextDeadline = SDL_GetPerformanceCounter(); } } else { controllerDisconnectedSince = 0; } if (g_audioEnabled && g_audioHandle >= 0 && !g_audioDeviceAlive.load(std::memory_order_acquire) && !g_audioWarningShownThisSession) { g_audioWarningShownThisSession = true; setAudioPaused(true); const DialogAction audioAction = runErrorDialog("NP2-3103", "La salida de audio se ha detenido", "Audio output has stopped", "La emulación se ha pausado. Puedes intentar reabrir AudioOut o continuar sin sonido.", "Emulation has been paused. You can try reopening AudioOut or continue without sound.", "/data/np2kai-runtime/logs/latest.log", "Continuar sin audio", "Continue without audio", true, true); if (audioAction == DialogAction::Retry) { shutdownAudioOutput(); if (initializeAudioOutput(g_currentAudioSampleRate)) g_audioWarningShownThisSession = false; } else if (audioAction == DialogAction::Back) { setAudioPaused(false); return GameSessionResult::Library; } else if (audioAction == DialogAction::Quit) { return GameSessionResult::Quit; } setAudioPaused(false); nextDeadline = SDL_GetPerformanceCounter(); } const bool pauseCombo = frontendPauseShortcutHeld(); if (pauseCombo && !pauseComboPrevious) { const PauseAction action = runPauseMenu(game); updatePadState(); pauseComboPrevious = frontendPauseShortcutHeld(); nextDeadline = SDL_GetPerformanceCounter(); if (action == PauseAction::Restart) { if (!restartCurrentGameInPlace(game)) { return GameSessionResult::Quit; } statisticsStart = SDL_GetPerformanceCounter(); accumulatedRunTicks = 0; accumulatedPresentTicks = 0; statisticsFrames = 0; statisticsPresents = 0; framesUntilPresent = 0; nextDeadline = statisticsStart; continue; } if (action == PauseAction::SwapDisk) return GameSessionResult::SwapDisk; if (action == PauseAction::Library) return GameSessionResult::Library; if (action == PauseAction::Quit) return GameSessionResult::Quit; } else pauseComboPrevious = pauseCombo; const Uint64 runStart = SDL_GetPerformanceCounter(); retro_run(); const Uint64 runEnd = SDL_GetPerformanceCounter(); accumulatedRunTicks += runEnd - runStart; if (runEnd - runStart > g_tickWorstRun) g_tickWorstRun = runEnd - runStart; ++statisticsFrames; ++framesUntilPresent; ++totalEmulatedFrames; if (g_frameReady && framesUntilPresent >= PRESENT_EVERY_EMULATED_FRAMES) { const Uint64 presentStart = SDL_GetPerformanceCounter(); if (!presentLatestFrame()) { setLastFailure("NP2-3201", "No se pudo presentar el fotograma", "The frame could not be presented", "El conversor de vídeo o sceVideoOut devolvió un error.", "The video converter or sceVideoOut returned an error.", "/data/np2kai-runtime/logs/latest.log"); return GameSessionResult::Error; } accumulatedPresentTicks += SDL_GetPerformanceCounter() - presentStart; ++statisticsPresents; framesUntilPresent = 0; ++totalPresentedFrames; } if (performanceFrequency != 0) { const Uint64 frameTicks = static_cast(frameMilliseconds * static_cast(performanceFrequency) / 1000.0); nextDeadline += frameTicks; Uint64 now = SDL_GetPerformanceCounter(); if (now < nextDeadline) { // esto es lo que sobra. es el numero mas util de todos: si sale // grande hay margen para subir el multiplicador, y si sale cero // es que vamos justos y no hay nada que rascar g_tickSlack += nextDeadline - now; double remainingMs = static_cast(nextDeadline - now) * 1000.0 / static_cast(performanceFrequency); if (remainingMs >= 2.0) SDL_Delay(static_cast(remainingMs - 1.0)); while (SDL_GetPerformanceCounter() < nextDeadline) {} } else { ++g_countLate; if (now - nextDeadline > frameTicks * 4u) nextDeadline = now; } } const Uint64 now = SDL_GetPerformanceCounter(); if (performanceFrequency != 0 && now - statisticsStart >= performanceFrequency) { const double interval = static_cast(now - statisticsStart) / static_cast(performanceFrequency); const double toMs = 1000.0 / static_cast(performanceFrequency); auto perFrame = [&](Uint64 ticks, unsigned long long count) { return count ? static_cast(ticks) * toMs / static_cast(count) : 0.0; }; g_recentEmulationFps = interval > 0.0 ? static_cast(statisticsFrames) / interval : 0.0; g_recentPresentFps = interval > 0.0 ? static_cast(statisticsPresents) / interval : 0.0; g_recentRetroRunMs = perFrame(accumulatedRunTicks, statisticsFrames); g_recentPresentMs = perFrame(accumulatedPresentTicks, statisticsPresents); g_msWaitBuffer = perFrame(g_tickWaitBuffer, statisticsPresents); g_msConvert = perFrame(g_tickConvert, statisticsPresents); g_msSubmit = perFrame(g_tickSubmit, statisticsPresents); g_msSlack = perFrame(g_tickSlack, statisticsFrames); g_lateFrames = g_countLate; g_msWorstRun = static_cast(g_tickWorstRun) * toMs; const unsigned underrunTotal = g_audioUnderruns.load(std::memory_order_relaxed); g_audioCutsPerSecond = underrunTotal - g_lastUnderrunTotal; g_lastUnderrunTotal = underrunTotal; writePerfSample(totalEmulatedFrames, totalPresentedFrames); statisticsStart = now; accumulatedRunTicks = 0; accumulatedPresentTicks = 0; statisticsFrames = 0; statisticsPresents = 0; g_tickWaitBuffer = 0; g_tickConvert = 0; g_tickSubmit = 0; g_tickSlack = 0; g_countLate = 0; g_tickWorstRun = 0; } } return GameSessionResult::Quit; } int main(int argc, char* argv[]) { (void)argc; (void)argv; setvbuf(stdout, nullptr, _IONBF, 0); std::printf("[NP2kai] arrancando v1.0\n"); applyUiTheme(); // que la paleta nunca este a cero aunque falle el ini initializeExternalKeyboardInput(); if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) { std::printf("[NP2kai] SDL event initialization failed: %s\n", SDL_GetError()); SDL_Quit(); if (SDL_Init(SDL_INIT_TIMER) != 0) { std::printf("[NP2kai] SDL timer initialization failed: %s\n", SDL_GetError()); return 1; } g_externalKeyboardEventSubsystemReady = false; std::printf("[NP2kai] Continuing without external keyboard events.\n"); } else { g_externalKeyboardEventSubsystemReady = true; } // a partir de aqui SDL_GetTicks() ya vale g_sdlTimerReady = true; if (!initializeDirectVideo()) { std::printf("[NP2kai] Fatal: video output could not be initialized.\n"); SDL_Quit(); return 1; } showSolidScreen(0, 0, 0); initializePadInput(); initializeExternalKeyboardInput(); if (!frontendInputAvailable()) { if (!waitForInputDevice()) { shutdownExternalKeyboardInput(); shutdownPadInput(); shutdownDirectVideo(); SDL_Quit(); return 1; } } while (!prepareWritableRuntime()) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, false); if (action != DialogAction::Retry) { shutdownPadInput(); shutdownDirectVideo(); SDL_Quit(); return 1; } } frontendLog("Version 1.0 startup"); frontendLog( "Keyboard backend: libSceKeyboard opened=%d sysmodule=0x%x init=0x%x " "open=0x%x attempts=%u user=%d sdl_events=%d", g_nativeKeyboardOpened ? 1 : 0, ps4KeyboardSysmoduleResult(), ps4KeyboardInitResult(), ps4KeyboardOpenResult(), ps4KeyboardOpenAttempts(), ps4KeyboardUserId(), g_externalKeyboardEventSubsystemReady ? 1 : 0); loadFrontendSettings(); applyUiTheme(); if (g_pendingConfigRecoveryNotice) { const DialogAction action = runErrorDialog("NP2-1205", "La configuración estaba dañada", "The settings file was damaged", "Se han restaurado los valores predeterminados para que la aplicación pueda continuar.", "Default values were restored so the application can continue.", FRONTEND_CONFIG_PATH, "Continuar", "Continue", false, false); g_pendingConfigRecoveryNotice = false; if (action == DialogAction::Quit) g_running = false; } if (g_settingsSaveFailed && g_running) { const DialogAction action = runLastFailureDialog("Continuar", "Continue", true, false); if (action == DialogAction::Retry) saveFrontendSettings(); else if (action == DialogAction::Quit) g_running = false; g_settingsSaveFailed = false; } retro_set_environment(environmentCallback); retro_set_video_refresh(videoCallback); retro_set_audio_sample(audioSampleCallback); retro_set_audio_sample_batch(audioBatchCallback); retro_set_input_poll(inputPollCallback); retro_set_input_state(inputStateCallback); if (retro_api_version() != RETRO_API_VERSION) { setLastFailure("NP2-1301", "Versión de libretro incompatible", "Incompatible libretro version", "El frontend y el core no usan la misma versión de la API libretro.", "The frontend and core do not use the same libretro API version."); runLastFailureDialog(nullptr, nullptr, false, false); g_running = false; } retro_system_info systemInfo {}; retro_get_system_info(&systemInfo); if (g_running && (systemInfo.library_name == nullptr || systemInfo.library_version == nullptr)) { setLastFailure("NP2-1302", "El core de NP2kai no respondió correctamente", "The NP2kai core did not respond correctly", "No se pudo leer el nombre o la versión del core.", "The core name or version could not be read."); runLastFailureDialog(nullptr, nullptr, false, false); g_running = false; } if (g_running && firstRunSetupNeeded()) { if (!runFirstRunSetup()) g_running = false; } while (g_running && !checkRequiredFiles()) { const DialogAction action = runLastFailureDialog("Ver guía", "View guide", true, false); if (action == DialogAction::Primary) { if (!runInfoDocument(InfoDocument::Setup)) { g_running = false; break; } } else if (action == DialogAction::Retry) { continue; } else { g_running = false; } } if (g_running) { std::printf("[NP2kai] Core: %s %s\n", systemInfo.library_name, systemInfo.library_version); frontendLog("Core: %s %s", systemInfo.library_name, systemInfo.library_version); } while (g_running) { if (!frontendInputAvailable() && !waitForInputDevice()) break; if (!runLibraryMenu()) break; GameEntry selected = g_gameEntries[g_librarySelection]; bool keepSelectedGame = true; while (g_running && keepSelectedGame) { if (!startGame(selected)) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); stopCurrentGame(); if (action == DialogAction::Retry) continue; if (action == DialogAction::Quit) g_running = false; break; } const GameSessionResult result = runGameSession(selected); stopCurrentGame(); if (result == GameSessionResult::Restart) continue; if (result == GameSessionResult::SwapDisk) { // el juego ya esta cerrado del todo; ahora se abre el otro por // el mismo camino que una carga normal if (g_pendingDiskIndex >= 0 && g_pendingDiskIndex < static_cast(g_gameEntryCount)) { selected = g_gameEntries[g_pendingDiskIndex]; g_librarySelection = static_cast(g_pendingDiskIndex); } g_pendingDiskIndex = -1; continue; } if (result == GameSessionResult::Error) { const DialogAction action = runLastFailureDialog(nullptr, nullptr, true, true); if (action == DialogAction::Retry) continue; if (action == DialogAction::Quit) g_running = false; break; } if (result == GameSessionResult::Quit) { g_running = false; break; } keepSelectedGame = false; } } stopCurrentGame(); frontendLog("Clean shutdown"); if (g_frontendLog != nullptr) { std::fclose(g_frontendLog); g_frontendLog = nullptr; } if (g_frameBuffer != nullptr) { std::free(g_frameBuffer); g_frameBuffer = nullptr; g_frameCapacity = 0; } if (g_expandedScanline != nullptr) { std::free(g_expandedScanline); g_expandedScanline = nullptr; g_expandedScanlinePixels = 0; } shutdownExternalKeyboardInput(); shutdownPadInput(); shutdownDirectVideo(); SDL_Quit(); return 0; }