Ovladač pro barevný TFT displej 76x284 obrazových bodů s čipem ST7789P3. Displej je připojen k Raspberry Pi Pico pomocí SPI sběrnice. Podpora pro UTF-8 znaky. Verze 0.1.

Konstrukce

IMG 20260607 194553

IMG 20260607 194135

IMG 20260607 194530

IMG 20260607 194538

Zapojení

pin na displeji pin na Picu

GND

libovolné GND

VCC

3V3(OUT) (pin 36)

SCL

GP18 (pin 24)

SDA

GP19 (pin 25)

RST

GP15 (pin 20)

DC

GP20 (pin 26)

CS

GP17 (pin 22)

BL

libovolné GND

Zdrojové kódy

CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
set(PICO_BOARD pico)

project(st7789p3_test C CXX ASM)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()

add_executable(st7789p3_test
		st7789p3_76x284_test.c
		utf8.c
		font_spleen_6x12.c
		font_spleen_8x16.c
		font_spleen_16x32.c
		font_10x20.c
    )

target_link_libraries(st7789p3_test pico_stdlib hardware_spi)

# create map/bin/hex file etc.
pico_enable_stdio_usb(st7789p3_test 1)
pico_enable_stdio_uart(st7789p3_test 0)
pico_add_extra_outputs(st7789p3_test)
st7789p3_76x284_test.c
/* RPi Pico driver for display ER-TFT2.25-1 ST7789P3 76x284 pixels
 * (2.25 inch TFT Serial SPI 76x284 LCD Display Module w/ST7789P3)
 * https://www.buydisplay.com/2-25-inch-tft-serial-spi-76x284-lcd-display-module-w-st7789p3
 * 
 * Copyright (c) Jirka Chráska 2026, <jirka@lixis.cz> 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list 
 * of conditions  and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 
 * conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used 
 * to endorse or promote products derived from this software without specific prior 
 * written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY  * THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <pico/stdlib.h>
#include "hardware/spi.h"
#include "font.h"
#include "utf8.h"
 

// barvy
#define RGB(R,G,B)  (((R >> 3) << 11) | ((G >> 2) << 5) | (B>>3))

enum Color {
    BLACK  = RGB(  0,   0,   0),
    GRAY   = RGB(192, 192, 192),
    WHITE  = RGB(255, 255, 255),
    RED    = RGB(255,   0,   0),
    PINK   = RGB(255, 192, 203),
    YELLOW = RGB(255, 255,   0),
    GOLDEN = RGB(255, 215,   0),
    BROWN  = RGB(128,  42,  42),
    BLUE   = RGB(  0,   0, 255),
    GREEN  = RGB(  0, 255,   0),
    PURPLE = RGB(160,  32, 240),
    // Red HTML Color Names
    IndianRed               = RGB(205, 92, 92),
    LightCoral              = RGB(240,128,128),
    Salmon                  = RGB(250,128,114),
    DarkSalmon              = RGB(233,150,122),
    LightSalmon             = RGB(255,160,122),
    Crimson                 = RGB(220, 20, 60),
    Red                     = RGB(255,  0,  0),
    FireBrick               = RGB(178, 34, 34),
    DarkRed                 = RGB(139,  0,  0),
    // Pink HTML Color Names
    Pink                    = RGB(255,192,203),
    LighPink                = RGB(255,182,193),
    HotPink                 = RGB(255,105,180),
    DeepPink                = RGB(255, 20,147),
    MediumVioletRed         = RGB(199, 21,133),
    PaleVioletRed           = RGB(219,112,147),
    // Orange HTML Color Names
    Coral                   = RGB(255,127, 80),
    Tomato                  = RGB(255, 99, 71),
    OrangeRed               = RGB(255, 69,  0),
    DarkOrange              = RGB(255,140,  0),
    Orange                  = RGB(255,165,  0),
    // Yellow HTML Color Names
    Gold                    = RGB(255,215,  0),
    Yellow                  = RGB(255,255,  0),
    LightYellow             = RGB(255,255,224),
    LemonChiffon            = RGB(255,250,205),
    LightGoldenrodYellow    = RGB(250,250,210),
    PapayaWhip              = RGB(255,239,213),
    Moccasin                = RGB(255,228,181),
    PeachPuff               = RGB(255,218,185),
    PaleGoldenrod           = RGB(255,232,170),
    Khaki                   = RGB(240,230,140),
    DarkKhaki               = RGB(189,183,107),
    // Purple HTML Color Names
    Lavender                = RGB(230,230,250),
    Thistle                 = RGB(216,191,216),
    Plum                    = RGB(221,160,221),
    Violet                  = RGB(238,130,238),
    Orchid                  = RGB(218,112,214),
    Fuchsia                 = RGB(255,  0,255),
    Magenta                 = RGB(255,  0,255),
    MediumOrchid            = RGB(186, 85,211),
    MediumPurple            = RGB(147,112,219),
    RebeccaPurple           = RGB(102, 51,153),
    BlueViolet              = RGB(138, 43,226),
    DarkViolet              = RGB(148,  0,211),
    DarkOrchid              = RGB(153, 50,204),
    DarkMagenta             = RGB(139,  0,139),
    Purple                  = RGB(128,  0,128),
    Indigo                  = RGB( 75,  0,130),
    SlateBlue               = RGB(106, 90,205),
    DarkSlateBlue           = RGB( 72, 61,139),
    MediumSlateBlue         = RGB(123,104,238),
    // Green HTML Color Names
    GreenYellow             = RGB(173,255, 47),
    Chartreuse              = RGB(127,255,  0),
    LawnGreen               = RGB(124,252,  0),
    Lime                    = RGB(  0,255,  0),
    LimeGreen               = RGB( 50,255, 50),
    PaleGreen               = RGB(152,251,152),
    LightGreen              = RGB(144,238,144),
    MediumSpringGreen       = RGB(  0,250,154),
    SpringGreen             = RGB(  0,255,127),
    MediumSeaGreen          = RGB( 60,179,113),
    SeaGreen                = RGB( 46,139, 87),
    ForestGreen             = RGB( 34,139, 34),
    Green                   = RGB(  0,128,  0),
    DarkGreen               = RGB(  0,100,  0),
    YellowGreen             = RGB(154,205, 50),
    OliveDrab               = RGB(107,142, 35),
    Olive                   = RGB(128,128,  0),
    DarkOliveGreen          = RGB( 85,107, 47),
    MediumAquamarine        = RGB(102,205,170),
    DarkSeaGreen            = RGB(143,188,139),
    LightSeaGreen           = RGB( 32,178,170),
    DarkCyan                = RGB(  0,139,139),
    Teal                    = RGB(  0,128,128),
    // Blue HTML Color Names
    Aqua                    = RGB(  0,255,255),
    Cyan                    = RGB(  0,255,255),
    LightCyan               = RGB(224,255,255),
    PaleTurquoise           = RGB(175,238,238),
    Aquamarine              = RGB(127,255,212),
    Turquoise               = RGB( 64,224,208),
    MediumTurquoise         = RGB( 72,209,204),
    DarkTurquoise           = RGB(  0,206,209),
    CadetBlue               = RGB( 95,158,160),
    SteelBlue               = RGB( 70,130,180),
    LightSteelBlue          = RGB(176,196,222),
    PowderBlue              = RGB(176,224,230),
    LightBlue               = RGB(173,216,230),
    SkyBlue                 = RGB(135,206,250),
    LightSkyBlue            = RGB(135,206,250),
    DeepSkyBlue             = RGB(  0,191,255),
    DodgerBlue              = RGB( 30,144,255),
    CornflowerBlue          = RGB(100,149,237),
    RoyalBlue               = RGB( 65,105,225),
    Blue                    = RGB(  0,  0,255),
    MediumBlue              = RGB(  0,  0,205),
    DarkBlue                = RGB(  0,  0,139),
    Navy                    = RGB(  0,  0,128),
    MidnightBlue            = RGB( 25, 25,112),
    // Brown HTML Color Names
    Cornsilk                = RGB(255,248,220),
    BlanchedAlmond          = RGB(255,235,205),
    Bisque                  = RGB(255,228,196),
    NavajoWhite             = RGB(255,222,173),
    Wheat                   = RGB(245,222,179),
    BurlyWood               = RGB(222,184,135),
    Tan                     = RGB(210,180,140),
    RosyBrown               = RGB(188,143,143),
    SandyBrown              = RGB(244,164, 96),
    Goldenrod               = RGB(218,165, 32),
    DarkGoldenrod           = RGB(184,134, 11),
    Peru                    = RGB(205,133, 63),
    Chocolate               = RGB(210,105, 30),
    SaddleBrown             = RGB(139, 69, 19),
    Sienna                  = RGB(160, 82, 45),
    Brown                   = RGB(165, 42, 42),
    Maroon                  = RGB(128,  0,  0),
    // White HTML Color Names
    White                   = RGB(255,255,255),
    Snow                    = RGB(255,250,250),
    HoneyDew                = RGB(240,255,240),
    MintCream               = RGB(245,255,250),
    Azure                   = RGB(240,255,255),
    AliceBlue               = RGB(240,248,255),
    GhostWhite              = RGB(248,248,255),
    WhiteSmoke              = RGB(245,245,245),
    SeaShell                = RGB(255,245,220),
    Beige                   = RGB(245,245,220),
    OldLace                 = RGB(253,245,230),
    FloralWhite             = RGB(255,250,240),
    Ivory                   = RGB(255,255,240),
    AntiqueWhite            = RGB(250,235,215),
    Linen                   = RGB(250,240,245),
    LavenderBlush           = RGB(255,240,245),
    MistyRose               = RGB(255,228,225),
    // Gray HTML Color Names
    Gainsboro               = RGB(220,220,220),
    LightGray               = RGB(211,211,211),
    Silver                  = RGB(192,192,192),
    DarkGray                = RGB(169,169,169),
    Gray                    = RGB(128,128,128),
    DimGray                 = RGB(105,105,105),
    LightSlateGray          = RGB(119,136,153),
    SlateGray               = RGB(112,128,144),
    DarkSlateGray           = RGB( 47, 79, 79),
    Black                   = RGB(  0,  0,  0),
};


// barevné formáty obrázků
typedef enum  {
        COLOR_FORMAT_UNKNOWN = 0,
        COLOR_FORMAT_RGB565 = 1,
} color_format_t;

// popis obrázku
typedef struct {
        color_format_t cf;  // barevný formát obrázku
        uint32_t w;         // šířka obrázku
        uint32_t h;         // výška obrázku
        uint32_t data_size; // velikost dat obrázku v bytech
        const uint8_t  *data;     // ukazatel na pole dat obrázku
} image_dsc_t; 

// testovací obrázek
// lze získat z webu: https://lvgl.io/tools/imageconverter
// parametry: LVGL v9, Color format RGB565

const uint8_t malina_map[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x00, 0x00, 0x41, 0x08, 0xe3, 0x20, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0xe6, 0x5b, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0xe6, 0x5b, 0x41, 0x08, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0xe3, 0x20, 0x09, 0x96, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x41, 0x08, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0xe3, 0x20, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0xe3, 0x20, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x25, 0x98, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0xe3, 0x20, 0x03, 0x58, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x25, 0x98, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
};

const image_dsc_t malina = {
  .cf = COLOR_FORMAT_RGB565,
  .w = 65,
  .h = 76,
  .data_size = 4940 * 2,
  .data = malina_map,
};


// hardware 
#define TFT_WIDTH        76
#define TFT_HEIGHT      284

#define SPI             spi0
#define SPI_BAUDRATE    45000000
#define DC_PIN          20          // pokud je na pinu DC 0 posílá se příkaz, je-li 1 posílají se data
#define CS_PIN          17          // chip select pin
#define RST_PIN         15          // reset pin
#define MOSI_PIN        19
#define CLK_PIN         18

#define SCREEN_W        284
#define SCREEN_H        76

static uint8_t buffer[TFT_WIDTH * TFT_HEIGHT * 2];

// -----------------------------------------------------------------------------------------
#define MADCTL          0x36        // Memory access control command
// data pro MADCTL
/* +----------------------------------------+
 * | MY | MX | MV | ML | RGB | MH |  0 |  0 |
 * +----------------------------------------+
 * MY Row Address Order   ( 0 shora dolů,     1 zdola nahoru )
 * MX Colum Address Order ( 0 zleva doprav,   1 zprava doleva )
 * MV Page/Column Order   ( 0 normální režim, 1 obrácený režim )
 * ML Line Address Order  ( 0 shora dolů,     1 zdola nahoru ) posílání řádků
 * RGB pořadí barev       ( 0 RGB,            1 BGR )
 * MH Display Data Latch Data Order pořadí posílání dat sloupců 
 * ( 0 obnovení zleva doprava, 1 obnovení zprava doleva )
 */
#define MADCTL_MY       0x80
#define MADCTL_MX       0x40
#define MADCTL_MV       0x20        // Vertical addressing mode
#define MADCTL_ML       0x10
#define MADCTL_RGB      0x08
#define MADCTL_MH       0x04
// -----------------------------------------------------------------------------------------
/* 8.11 Address Control (ST7789P3 strana 86)
 * Čítač adres nastavuje adresy paměti RAM s daty displeje pro zápis a čtení.
 * Data se zapisují po pixelech do matice paměti RAM ovladače DRIVER. 
 * Shromažďují se data pro jeden nebo dva pixely (RGB 6-6-6-bit) v závislosti na datových formátech. 
 * Jakmile jsou tyto informace o pixelových datech kompletní, aktivuje se přístup „Zápis“ v paměti RAM. 
 * Umístění v paměti RAM jsou adresována ukazateli adres. 
 * Rozsahy adres jsou X=0 až X=239 (EFh) a Y=0 až Y=319 (13Fh). 
 * Adresy mimo tyto rozsahy nejsou povoleny.
 * Před zápisem do paměti RAM je nutné definovat okno, do kterého se bude zapisovat. 
 * Okno je programovatelné pomocí příkazových registrů XS, YS, které označují počáteční adresu, 
 * a XE, YE, které označují koncovou adresu.
 * 
 * Například bude zapsán celý obsah zobrazení, okno je definováno následujícími hodnotami:
 * XS=0 (0h) YS=0 (0h) a XE=239 (EFh), YE=319 (13Fh).
 * 
 * Ve vertikálním adresovacím režimu (MV=1) se adresa Y zvyšuje po každém bajtu, 
 * po poslední adrese Y (Y=YE) se Y zalomí na YS a X se zvýší pro adresování dalšího sloupce. 
 * V horizontálním adresovacím režimu (V=0) se adresa X zvýší po každém bajtu, 
 * po poslední adrese X (X=XE) se X zalomí na XS a Y se zvýší pro adresování dalšího řádku. 
 * Po každé poslední adrese (X=XE a Y=Y) se ukazatele adres zalomí na adresu (X=XS a Y=YS).
 * 
 * Pro flexibilitu při práci s širokou škálou architektur displejů definují příkazy 
 * „CASET, RASET a MADCTL“ příznaky MX a MY, které umožňují zrcadlení adresy X a adresy Y. 
 * Jsou povoleny všechny kombinace příznaků. 
 * Sekce 8.12 ukazuje dostupné kombinace zápisu do RAM displeje. 
 * Pokud se změní MX, MY a MV, data se přepíší do RAM displeje.
*/

#define ROTATION_0      0x00        // hodnoty pro rotaci displeje
#define ROTATION_90     0x60
#define ROTATION_180    0xc0
#define ROTATION_270    0xa0

#define ROTATION        ROTATION_0

// poslání příkazu do displeje
void sc(uint8_t cmd )
{
    gpio_put(CS_PIN, 0);
    gpio_put(DC_PIN, 0);
    spi_write_blocking(SPI, &cmd, 1);
    gpio_put(CS_PIN, 1);
}

// poslání dat do displeje
void sd(uint8_t cmd )
{
    gpio_put(CS_PIN, 0);
    gpio_put(DC_PIN, 1);
    spi_write_blocking(SPI, &cmd, 1);
    gpio_put(CS_PIN, 1);
}

// vynulování framebufferu
void clear_display()
{
    memset(buffer, 0, sizeof(buffer));
}

// inicializace displeje
void display_init()
{
    gpio_init(RST_PIN);
    gpio_set_dir(RST_PIN, GPIO_OUT);
    gpio_init(DC_PIN);
    gpio_set_dir(DC_PIN, GPIO_OUT);
    gpio_init(CS_PIN);
    gpio_set_dir(CS_PIN, GPIO_OUT);
    
    gpio_set_function(MOSI_PIN, GPIO_FUNC_SPI);
    gpio_set_function(CLK_PIN, GPIO_FUNC_SPI);
    
    gpio_put(CS_PIN, 1);
    spi_init(SPI,SPI_BAUDRATE);
    
    // reset displeje
    gpio_put(CS_PIN,0);
    gpio_put(RST_PIN,1);
    sleep_ms(10);
    gpio_put(RST_PIN,0);
    sleep_ms(20);
    gpio_put(RST_PIN,1);
    sleep_ms(120);
    gpio_put(CS_PIN,1);
    
    // nastavení displeje
    // Porch control
    sc(0xb2); sd(0x0c); sd(0x0c); sd(0x00); sd(0x33), sd(0x33);
    // RAM control
    sc(0xb0); sd(0x00); sd(0xe0);
    // nastavení orientace displeje
    // Memory Data Access Control  -- top to bottom, left to right, normal mode,  RGB, LCD refresh left to right
    // sc(0x36); sd(0x00);
    sc(MADCTL); sd(0);
    // InterfacePixel Format -- 16 bit/pixel 0x05 je asi špatně dám 0x55
    /* COMOD 0x3a command
     * +-------------------------------------+
     * | 0 | D6 | D5 | D4 | 0 | D2 | D1 | D0 |
     * +-------------------------------------+
     *  bity D6, D5, D4: 101 - 65K RGB, 110 262K RGB
     *  bity D2, D1, D0: 011 - 12bit/pixel, 101 - 16bit/pixel, 110 - 18bit/pixel
     */
    sc(0x3a); sd(0x55);    
    // Gate control
    sc(0xb7); sd(0x45);
    // VCOMS
    sc(0xbb); sd(0x1d);
    // LCM control
    sc(0xc0); sd(0x2c);
    // VRH command enable
    sc(0xc2); sd(0x01);
    // VRH set
    sc(0xc3); sd(0x19);
    //
    sc(0xc4); sd(0x20);
    // FR control
    sc(0xc6); sd(0x0f);
    // Power control
    sc(0xd0); sd(0xa4); sd(0xa1);
    // Gate Output Selection in Sleep in Mode
    sc(0xd6); sd(0xa1);
    // Positive Voltage Gamma Control
    sc(0xe0); sd(0xd0); sd(0x10); sd(0x21); sd(0x14); sd(0x15); sd(0x2d); sd(0x41); sd(0x44);sd(0x4f); sd(0x28); sd(0x0e); sd(0x0c); sd(0x1d); sd(0x1f);
    // Negative voltage Gamma Control
    sc(0xe1); sd(0xd0); sd(0x0f); sd(0x1b); sd(0x0d); sd(0x0d); sd(0x26); sd(0x42); sd(0x54);sd(0x50); sd(0x3e); sd(0x1a); sd(0x18); sd(0x22); sd(0x25);
    // 
    sc(0x11);
    sleep_ms(120);
    sc(0x29);
    clear_display();
}

// nastavení jasu displeje
#define WRDISBV 0x51
void display_brightness( uint8_t br)
{
    sc(WRDISBV); sd(br);
}

#define X_OFFSET        0x12
#define Y_OFFSET        0x52
// poslání framebufferu do displeje
void show() 
{
    sc(MADCTL); sd(MADCTL_MV|MADCTL_MX);

    sc(0x2a); // příkaz CASET nastavení adres sloupců 
    // data nastavení adres: XS jsou bity počíteční x adresy, XE jsou bity koncové x adresy
/*  +-------------------------------------------------------+
 *  | XS15 | XS14 | XS13 | XS12 | XS11 | XS10 | XS09 | XS08 |
 *  | XS07 | XS06 | XS05 | XS04 | XS03 | XS02 | XS01 | XS00 |
 *  | XE15 | XE14 | XE13 | XE12 | XE11 | XE10 | XE09 | XE08 |
 *  | XE07 | XE06 | XE05 | XE04 | XE03 | XE02 | XE01 | XE00 |
 *  +-------------------------------------------------------+
 */
    // počáteční adresa XS je 0x0052 (82 dec)
    sd(0);      // 0b00000000
    sd(0+X_OFFSET); // 0b10100010
    // koncová adresa XE je 0x009d (157 dec)
    sd((SCREEN_W+X_OFFSET-1) >> 8);   // 0x4c+0x51=0x9d 0b10011101 >> 8 => 0b00000000
    sd((SCREEN_W+X_OFFSET-1) & 0xff); // 0b10011101 && 0xff => 0b10011101
    // rozdíl je 75
    
    sc(0x2b); // příkaz RASET nastavení adres řádků
    // data nastavení adres: YS jsou bity počíteční y adresy, YE jsou bity koncové y adresy
/*  +-------------------------------------------------------+
 *  | YS15 | YS14 | YS13 | YS12 | YS11 | YS10 | YS09 | YS08 |
 *  | YS07 | YS06 | YS05 | YS04 | YS03 | YS02 | YS01 | YS00 |
 *  | YE15 | YE14 | YE13 | YE12 | YE11 | YE10 | YE09 | YE08 |
 *  | YE07 | YE06 | YE05 | YE04 | YE03 | YE02 | YE01 | YE00 |
 *  +-------------------------------------------------------+
 */
    sd(0);
    sd(Y_OFFSET);
    sd((SCREEN_H+Y_OFFSET-1) >> 8);
    sd((SCREEN_H+Y_OFFSET-1) & 0xff);
    
    sc(0x2c); // poslání dat do displeje
    gpio_put(DC_PIN,1);
    gpio_put(CS_PIN,0);
    spi_write_blocking(SPI, buffer, SCREEN_H*SCREEN_W*2);
    gpio_put(CS_PIN,1);
}


// kreslení bodu do framebufferu
// int x je souřadnice x
// int y je souřadnice y
// uint16_t color je barva ve formátu RGB
void drawPixel(int x, int y, uint16_t color)
{
    if( x >= SCREEN_W || y >= SCREEN_H || x < 0 || y < 0) return;
     
    buffer[x*2 + y*SCREEN_W*2]     = color >> 8;
    buffer[x*2 + y*SCREEN_W*2 + 1] = color;
}

// vyplnění framebufferu barvou pozadí
// uint16_t coler je barva pozací ve formátu RGB
void clear_screen(uint16_t color)
{
static int16_t i, j;
    for(i = 0; i < SCREEN_W; i++) {
        for( j = 0; j < SCREEN_H; j++) {
            drawPixel(i,j,color);
        }
    }
}
// kreslení obrázku
// int x je vodorovná souřadnice horního rohu
// int y je svislá souřadnice horního rohu obrázku
// image_dsc_t *img_desc je popis obrázku a data obrázku (zatím jenom ve formátu RGB565) 
// obrázek se musí vejít na displej
void drawImage(int x, int y, const image_dsc_t *img_desc)
{
static uint8_t *src_ptr, *dst_ptr;
static int i,j,w;
static uint8_t *p;
static uint8_t l, h;

    if( x >= SCREEN_W || y >= SCREEN_H || x < 0 || y < 0) return;
    
    if( img_desc->cf == COLOR_FORMAT_RGB565 ) {
        dst_ptr = &buffer[x*2 + y*SCREEN_W*2];
        src_ptr = img_desc->data;
        if( x+img_desc->w < SCREEN_W ) {
            for(i = y; i < SCREEN_H; i++, dst_ptr += 2*SCREEN_W, src_ptr += 2*img_desc->w) {
                p = dst_ptr;
                memcpy(dst_ptr, src_ptr, 2*img_desc->w);
                // prohození horního a dolního bytu ve framebufferu
                for(j=0; j<2*img_desc->w; j+=2, p+=2) {
                    l = *p;
                    h = *(p+1);
                    *p = h;
                    *(p+1) = l;
                }
            }
        } else { // oříznout zprava
            w = SCREEN_W - x - 1;
            printf("drawImage: x=%d w=%d imgw=%d\n",x,w,img_desc->w);
            for(i = y; i < SCREEN_H; i++, dst_ptr += 2*SCREEN_W, src_ptr += 2*img_desc->w) {
                p = dst_ptr;
                memcpy(dst_ptr, src_ptr, 2*w);
                // prohození horního a dolního bytu ve framebufferu
                for(j=0; j<2*w; j+=2, p+=2) {
                    l = *p;
                    h = *(p+1);
                    *p = h;
                    *(p+1) = l;
                }                
            }
        }
    }
}

// kreslení čáry (Bresenham)
// parametry: x0, y0 - souřadnice 1. bodu úsečky
//            x1, y1 - souřadnice 2. bodu úsečky
//            color  - barva úsečky ve formátu RGB
void drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t color)
{
static int dx, sx;
static int dy, sy;
static int err, e2; /* error value e_xy */
    dx = abs(x1-x0);
    sx = x0<x1 ? 1 : -1;
    dy = -abs(y1-y0);
    sy = y0<y1 ? 1 : -1;
    err = dx+dy;

   for(;;){  /* loop */
      drawPixel(x0, y0, color);
      if (x0==x1 && y0==y1) break;
      e2 = 2*err;
      if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
      if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
   }
}

// kreslení vodorovné čáry
// parametry: x,y - souřadnice levého bodu
//            len - délka v bodech
//            color - barva čáry
void drawHLine( uint16_t x, uint16_t y, uint16_t len, uint16_t color )
{
static int i;
    for(i=x ; i<=x+len; i++) {
        drawPixel(i,y,color);
    }
}

// kreslení svislé čáry
// parametry: x,y - souřadnice horního bodu
//            len - výška v bodech
//            color - barva čáry
void drawVLine( uint16_t x, uint16_t y, uint16_t len, uint16_t color )
{
static int i;
    for(i=y ; i<=y+len; i++) {
        drawPixel(x,i,color);
    }
}

// kreslení plného obdélníku
// parametry: x0, y0 - souřadnice levého horního rohu
//            x1, y1 - souřadnice pravého dolního rohu
//            color  - barva obdélníka včetně výplně
void drawSquare( int x0, int y0, int x1, int y1, uint16_t color )
{
static int x = 0;
static int y = 0;

    if( x0 > x1 ) {
        x = x1;
        x1 = x0;
        x0 = x;
    }
    if( y0 > y1 ) {
        y = y1;
        y1 = y0;
        y0 = y;
    }
    
    for(x=x0 ; x <= x1; x++) {
        for(y=y0; y <= y1; y++) {
            drawPixel(x,y,color);
        }
    }
}

// kreslení prázdného obdélníku
// parametry: x,y - souřadnice levého horního rohu
//            w - šířka obdélníku
//            h - výška obdélníku
//            color - barva obrysu obdélníka
void drawRectangle( uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color )
{
    drawHLine(x  , y  , w, color);
    drawHLine(x  , y+h, w, color);
    drawVLine(x  , y  , h, color);
    drawVLine(x+w, y  , h, color);
}

// kreslení kružnice
// parametry: xm, ym - souřadnice středu kružnice
//            r - poloměr kružnice
//            color - barva kružnice (0x0 až 0xf)
void drawCircle(int xm, int ym, int r, uint16_t color)
{
static int x, y, err; 
    x = -r;
    y = 0;
    err = 2-2*r; /* II. Quadrant */ 
   do {
      drawPixel(xm-x, ym+y, color); /*   I. Quadrant */
      drawPixel(xm-y, ym-x, color); /*  II. Quadrant */
      drawPixel(xm+x, ym-y, color); /* III. Quadrant */
      drawPixel(xm+y, ym+x, color); /*  IV. Quadrant */
      r = err;
      if (r <= y) err += ++y*2+1;           /* e_xy+e_y < 0 */
      if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
   } while (x < 0);
}
// kreslení plného kruhu
// parametry: xm, ym - souřadnice středu kruhu
//            r - poloměr kruhu
//            color - barva (0x0 až 0xf)
void drawFilledCircle(int xm, int ym, int r, uint16_t color)
{
static int x, y, err;
    x = -r;
    y = 0;
    err = 2-2*r;  /* II. Quadrant */ 
   do {
       drawLine(xm-x,ym+y,xm+x,ym+y, color);
       drawLine(xm-x,ym-y,xm+x,ym-y, color);
      r = err;
      if (r <= y) err += ++y*2+1;           /* e_xy+e_y < 0 */
      if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
   } while (x < 0);
}


// kreslení UTF-8 znaku
// parametry: x, y - souřadníce levého horního rohu písmenka
//            Font - ukazatel na bitmapový font
//            Index - UTF-8 codepoint
//            fcolor - barva písma
//            bcolor - barva pozadí
// vrací šířku písmena
uint16_t drawChar(uint32_t x, uint32_t y, const bitmapFONT* Font, 
                  uint32_t Index, uint16_t fcolor, uint16_t bcolor ) 
{
static uint8_t  row, column, forecolor;  
static uint8_t  col, fcol, zb;
static uint8_t  chwidth;
static uint16_t chsize;
static uint32_t bindex;
static uint8_t  chw;
static int      j = 0;

    if( x > SCREEN_W || y > SCREEN_H ) {
	    return 0;
    }
    col    = Font->Width/8; // sloupce v bytech
    fcol   = col;
    zb     = Font->Width%8;

    if( zb != 0) {
        col ++;
    }
    if( Index > Font->Chars ) Index = 0;
    
    chwidth = Font->Widths[Index]; // šířka znaku
    chsize = Font->Height * col;
    bindex = Index*chsize;

    for (row = 0; row < Font->Height; row++ ) {
        // lezeme po bytech (po osmičkách)
        chw = chwidth;
        for (column = 0; column < col; column++, chw-- ) {
            for( j = 0; j<8; j++ ) {
                if( chw == 0 ) break;
                forecolor = ((uint8_t) Font->Bitmap[bindex+row*col+column]) & (0x80 >> j);
                if( forecolor ) {
                    drawPixel(x + column*8 + j, y + row, fcolor);
                } else {
                    drawPixel(x + column*8 + j, y + row, bcolor);
                }
            }
        } 
    }
return chwidth;
}
// nalezení indexu pole znaků metodou půlení intervalu
// const bitmapFONT *font je ukazatel na strukturu fontu
// uint32_t je UTF-8 codepoint
int32_t getUTF8Index(const bitmapFONT *font, uint32_t codepoint)
{
static int32_t l = 0;
static int32_t r = 0;
static int32_t m = 0;
    for(l=0, r= font->Chars; l<=r; ) {
        m = (l+r)/2;
        if(font->Index[m] == codepoint ) return m;
        if(codepoint < font->Index[m]    ) { // jdeme vlevo
            r = m - 1;
        } else { // jdeme vpravo
            l = m + 1;
        }
    }
return -1;
}

// kreslení  UTF-8 řetězce
// parametry: x,y - levý horní roh řetězce
//            *Font - ukazatel na bitmapový font
//            *pstring - ukazatel naa řetězec (UTF-8)
//            fcolor - barva písma  (0x0 - 0xf)
//            bcolor - barva pozadí (0x0 - 0xf)
// vrací šířku řetězce zabranou na displeji v bodech
uint16_t drawString(uint32_t x, uint32_t y, const bitmapFONT * Font, 
                    const char * pString, uint16_t fcolor, uint16_t bcolor )
{
static uint32_t Xpoint;
static uint32_t Ypoint;
static uint16_t index; 
static uint16_t str_width  = 0;
static uint16_t char_width = 0;
static uint32_t codepoint;
static int i = 0;

    Xpoint = x;
    Ypoint = y;
    str_width = 0;
    char_width = 0;

    if (x > SCREEN_W || y > SCREEN_H) {
        return 0;
    }

    utf8_string    ustr = make_utf8_string(pString);
    utf8_char_iter iter = make_utf8_char_iter(ustr);
    utf8_char c;
    while ((c = next_utf8_char(&iter)).byte_len > 0 ) {
        codepoint = unicode_code_point(c);
        index = 0;
        if( (index = getUTF8Index(Font, codepoint)) == -1) continue; // nemáme znak ve fontu
        // test zda se to vejde na displej
        if((Xpoint + Font->Width ) > SCREEN_W ) {
            // zalomíme na další řádek
            Xpoint = x;
            Ypoint += Font->Height;
        }
        if ((Ypoint  + Font->Height ) > SCREEN_H ) {
            // už se to nikam nevejde
            return str_width;
            // Xpoint = x;
            // Ypoint = y;
        }
        char_width = drawChar(Xpoint, Ypoint, Font, index, fcolor, bcolor);
        Xpoint    += char_width;
        str_width += char_width;
    }
return str_width;
}
// --- konec knihovny ------------------------------------------------------------------------
// test

#define RANDOM_MIN 0
#define RANDOM_MAX 255
#define SINUS_TEST 1
#define FONT_10x20_TEST 1

extern bitmapFONT font_spleen_16x32;
extern bitmapFONT font_spleen_8x16;
extern bitmapFONT font_spleen_6x12;
extern bitmapFONT font_10x20;

int main( void )
{
int i,j,k;
int x,y,z;
char buf[128];
uint64_t t;

    stdio_init_all();
    sleep_ms(2000);
    printf("Test displeje ST7789P3 76x284\n");
    display_init();
    display_brightness(0x08);

    clear_screen(BLACK);
    drawString(2,2,&font_spleen_8x16,"Test displeje ST7789P3 76x284 na   šířku. (c) Jirka Chráska 2026.",Gold, BLACK);
    show();
    sleep_ms(2000);

    printf("Fialová obrazovka.\n");
    clear_screen(PURPLE);
    drawString(2,2,&font_spleen_16x32,"Fialová.",LightGray, PURPLE);
    show();
    sleep_ms(2000);

    printf("LimeGreen obrazovka.\n");
    clear_screen(LimeGreen);
    drawString(2,2,&font_spleen_16x32,"LimeGreen.", BLACK, LimeGreen);
    show();
    sleep_ms(2000);

    printf("SteelBlue obrazovka.\n");
    clear_screen(SteelBlue);
    drawString(2,2,&font_spleen_16x32,"SteelBlue.", WHITE, SteelBlue);
    show();
    sleep_ms(2000);
    
    printf("Červená obrazovka (IndianRed).\n");
    clear_screen(IndianRed);
    drawString(2,2,&font_spleen_16x32,"IndianRed.", WHITE, IndianRed);
    show();
    sleep_ms(2000);
    printf("Bílá obrazovka.\n");
    clear_screen(WHITE);
    drawString(2,2,&font_spleen_16x32,"Bílá.", BLACK, WHITE);
    show();
    sleep_ms(2000);
    printf("Černá obrazovka.\n");
    clear_screen(BLACK);
    drawString(2,2,&font_spleen_16x32,"Černá.", WHITE, BLACK);
    show();
    sleep_ms(2000);
    
    // testování obrázku
    printf("Obrázek maliny.\n");
    clear_screen(BLACK);
    drawImage(0,0,&malina);
    show();
    drawImage(66,0,&malina);
    show();
    drawImage(132,0,&malina);
    drawString(200,0,&font_spleen_8x16,"maliny",IndianRed,BLACK);
    show();
    drawImage(240,20,&malina);
    show();
    sleep_ms(10000);
    
    printf("Čáry.\n");
    clear_screen(BLACK);
    for( x=0; x<SCREEN_W; x++) {
        drawPixel(x,0,YELLOW);
        drawPixel(x,1,Gold);
        drawPixel(x,2,YELLOW);
        drawPixel(x,SCREEN_H-1,YELLOW);
        drawPixel(x,SCREEN_H-2,Gold);
        drawPixel(x,SCREEN_H-3,YELLOW);
    }
    for( y=0; y<SCREEN_H; y++) {
        drawPixel(0,y,MediumSeaGreen);
        drawPixel(1,y,SeaGreen);
        drawPixel(2,y,ForestGreen);
        drawPixel(SCREEN_W-1,y,MediumSeaGreen);
        drawPixel(SCREEN_W-2,y,SeaGreen);
        drawPixel(SCREEN_W-3,y,ForestGreen);
    }
    show();
    drawString(4,4,&font_spleen_16x32,"(c) Jirka Chráska 2026.",DodgerBlue,BLACK);
    show();
    for( x=SCREEN_W/2; x<SCREEN_W; x++) {
        drawLine(SCREEN_W/2, SCREEN_H/2, x-3, SCREEN_H-3,OrangeRed);
        show();
    }
    for( y=SCREEN_H; y>=SCREEN_H/2; y--) {
        drawLine(SCREEN_W/2, SCREEN_H/2, SCREEN_W-3, y-3, Coral);
        show();
    }
    sleep_ms(500);
    for( x=SCREEN_W/2; x<SCREEN_W; x++) {
        drawLine(SCREEN_W/2, SCREEN_H/2, x-3, SCREEN_H-3, BLACK);
        show();
    }
    for( y=SCREEN_H; y>=SCREEN_H/2; y--) {
        drawLine(SCREEN_W/2, SCREEN_H/2, SCREEN_W-3, y-3, BLACK);
        show();
    }
    sleep_ms(2000);
#if SINUS_TEST
    // -----------------------------------------------------------
    // sinusovka
    printf("Sinusovka a kosinusovka.\n");
    clear_screen(BLACK);
    show();
    drawString(10,76-16,&font_spleen_8x16,"y=sin(x)", OrangeRed,BLACK);
    drawString(100,10,&font_spleen_8x16,"y=cos(x)", DodgerBlue,BLACK);
    // popisky os
    drawString(3,2,&font_spleen_6x12,"y",Gainsboro,BLACK);
    drawString(278,38,&font_spleen_6x12,"x",Gainsboro,BLACK);
    // osy
    drawLine(0,0,0,127,Gainsboro);
    drawLine(0,38,283,38,Gainsboro);
    // sinusovka a kosinusovka
        
    for( x=0; x<284; x++) {
        y = (-(sin(x*3.1415926/100) * 38)+38);
        drawPixel(x,y,OrangeRed);
        y = (-(cos(x*3.1415926/100) * 38)+38);
        drawPixel(x,y,DodgerBlue);
        if(x==50) {
            drawLine(x,36,x,40,Gray);
            drawString(x+1,42,&font_spleen_8x16,"π/2",Gray,BLACK);
        }
        if(x==100) {
            drawLine(x,36,x,40,Gray);
            drawString(x+10,42,&font_spleen_8x16,"π",Gray,BLACK);
        }
        if(x==150) {
            drawLine(x,36,x,40,Gray);
            drawString(x+1,42,&font_spleen_8x16,"3π/2",Gray,BLACK);
        }
        if(x==200) {
            drawLine(x,36,x,40,Gray);
            drawString(x+1,42,&font_spleen_8x16,"2π",Gray,BLACK);
        }
        if(x==250) {
            drawLine(x,36,x,40,Gray);
            drawString(x+1,42,&font_spleen_8x16,"5π/2",Gray,BLACK);
        }
        // občerstvujeme displej po 4 nakreslených bodech
        x%4==0?show():sleep_us(1);
    }
    sleep_ms(5000);
#endif    
    // -----------------------------------------------------------
    // test fontu font_10x20
#if FONT_10x20_TEST
    printf("font_10x20 test.\n");
    for( k=0; k<font_10x20.Chars; k++) {
        t = time_us_64();
        clear_screen(BLACK);
        drawChar(1,1,&font_10x20,k,Gold,DarkSlateBlue);
        sprintf(buf,"font_10x20", k);
        drawString(40,1,&font_10x20,buf,Gold,BLACK);
        sprintf(buf,"Character index: %d", k);
        drawString(0,26,&font_spleen_6x12,buf,WHITE,BLACK);
        sprintf(buf,"UTF-8 codepoint: %04lx", font_10x20.Index[k]);
        drawString(0,38,&font_spleen_6x12,buf,WHITE,BLACK);
        sprintf(buf,"Počet znaků fontu: %d", font_10x20.Chars);
        drawString(136,38,&font_spleen_6x12,buf,DodgerBlue,BLACK);
        if( k < font_10x20.Chars) {
            for(i=0; i<27; i++) {
                drawChar(0+i*10,76-20,&font_10x20,k+i,Gold,DarkSlateBlue);    
            }
        }
        show();
        t = time_us_64() - t;
        sprintf(buf,"Kreslení trvalo %5.2f ms",(float)t/1000.0);
        drawString(136, 26, &font_spleen_6x12, buf, DodgerBlue,BLACK);
        show();
        sleep_ms(500);
    }
#endif
    // test kružnic
    printf("Kružnice\n");
    clear_screen(BLACK);
    drawString(180,0,&font_spleen_8x16,"Kružnice",Gold,BLACK);
    // kružnice zvětšuje svůj poloměr
    for(int r=10; r<64; r++) {
        drawCircle(127,64,r,RoyalBlue);
        sprintf(buf,"drawCircle(%d, %d, %d, RoyalBlue); ",127,64,r);
        drawString(0,115,&font_spleen_6x12,buf, Gold, BLACK);
        show();
        }
    sleep_ms(3000);
    // kružnice zmenšuje svůj poloměr
       for(int r=63; r>10; r--) {
        drawCircle(127,64,r,BLACK);
        sprintf(buf,"drawCircle(%d, %d, %d, BLACK); ",127,64,r);
        drawString(0,115,&font_spleen_6x12,buf, Gold, BLACK);
        show();
        }
    sleep_ms(3000);
    
    t = time_us_64();
    srand((unsigned int) t);
    uint8_t r,g,b;
    for(int i=0; i<256; i++) {
        for(int j=0; j<256; j++) {
            for(int k=0; k<256; k++) {
                t = time_us_64();
                r = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
                g = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
                b = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
                clear_screen(RGB(r,g,b));
                drawString(2,2,&font_10x20,"Sláva Bohu za všechno!",
                           RGB(255-r,255-g,255-b),RGB(r,g,b));
                show();
                t = time_us_64() - t;
                sprintf(buf,"R=%3u,G=%3u,B=%3u trvalo %lld μs.",r,g,b,t);
                drawString(2, 76-16, &font_spleen_8x16, buf, 
                           RGB(255-r,255-g,255-b),RGB(r,g,b));
                show();
                sleep_ms(800);
            }
        }
    }
}
utf8.h
/**
 * @file utf8.h
 * @brief simple library for working with UTF-8 encoded strings
 *
 * @code
 * #include "utf8.h"
 * #include <stdio.h>
 *
 * int main() {
 *     const char* str = "Hello, こんにちは, Здравствуйте";
 *     utf8_string ustr = make_utf8_string(str);
 *     utf8_string_slice slice = make_utf8_string_slice(ustr, 2, 11);
 *     utf8_char_iter iter = make_utf8_char_iter(ustr);
 *
 *     printf("string: %s\n", ustr.str);
 *     printf("slice: %.*s\n", (int)slice.byte_len, slice.str);
 *
 *     utf8_char ch;
 *     while ((ch = next_utf8_char(&iter)).byte_len > 0) {
 *         printf("character: %.*s\t", (int)ch.byte_len, ch.str);
 *         printf("unicode code point: U+%04X\n", unicode_code_point(ch));
 *     }
 *
 *     return 0;
 * }
 * @endcode
 */

#ifndef ZAHASH_UTF8_H
#define ZAHASH_UTF8_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/**
 * @brief Represents the validity of a UTF-8 encoded string.
 *
 * @details The `utf8_validity` struct indicates whether a given UTF-8 encoded string is valid or not,
 * along with the position up to which it is valid.
 *
 * - Invalid case: "hello\xC0\xC0" => { .valid = false, .valid_upto = 5  }
 * - Valid case:   "hello world"   => { .valid = true,  .valid_upto = 11 }
 */
typedef struct {
    bool valid;          ///< Flag indicating the validity of the UTF-8 string.
    size_t valid_upto;   ///< The position up to which the string is valid.
} utf8_validity;

/**
 * @brief Represents a non-owning UTF-8 encoded string. (just a wrapper type).
 *
 * @details The `utf8_string` struct holds a pointer to a UTF-8 encoded string along with its byte length,
 */
typedef struct {
    const char* str;     ///< Pointer to the UTF-8 encoded string.
    size_t byte_len;     ///< Byte length of the UTF-8 string ('\0' not counted).
} utf8_string;

/**
 * @brief Represents a UTF-8 encoded string that fully owns its data.
 *
 * @details The `owned_utf8_string` struct holds a pointer to a UTF-8 encoded string that is dynamically allocated
 *          and therefore is owned by the struct, which means the caller is responsible for freeing the memory when
 *          it is no longer needed using the `free_owned_utf8_string` function.
 */
typedef struct {
    char* str;          ///< Pointer to the UTF-8 encoded string (owned). This memory is dynamically allocated.
    size_t byte_len;    ///< Byte length of the UTF-8 string ('\0' not counted).
} owned_utf8_string;

/**
 * @brief Represents an iterator for traversing UTF-8 characters in a string.
 *
 * @details The `utf8_char_iter` struct serves as an iterator for traversing UTF-8 characters
 * within a UTF-8 encoded string.
 */
typedef struct {
    const char* str;     ///< Pointer to the current position of the iterator.
} utf8_char_iter;

/**
 * @brief Represents a UTF-8 character.
 *
 * @details The `utf8_char` struct encapsulates a UTF-8 character, including its pointer and byte length.
 * The byte length represents the number of bytes occupied by the UTF-8 character.
 */
typedef struct {
    const char* str;     ///< Pointer to the UTF-8 character.
    uint8_t byte_len;    ///< Byte length of the UTF-8 character.
} utf8_char;

/**
 * @brief Validates whether a given string is UTF-8 compliant in O(n) time.
 *
 * @param str The input string to validate.
 * @return The validity of the UTF-8 string along with the position up to which it is valid.
 */
utf8_validity validate_utf8(const char* str);

/**
 * @brief Wraps a C-style string in a UTF-8 string structure after verifying its UTF-8 compliance.
 *
 * @param str The input C-style string to wrap.
 * @return A UTF-8 string structure containing the wrapped string if valid; otherwise, a structure with NULL string pointer.
 *
 * @code
 * // Example usage:
 * const char *str = "definitely utf8 string こんにちは नमस्ते Здравствуйте";
 * utf8_string ustr = make_utf8_string(str);
 * assert( ustr.str != NULL );
 *
 * const char *s = "non-utf8 sequence \xC0\xC0";
 * utf8_string ustr = make_utf8_string(str);
 * assert( ustr.str == NULL );
 * @endcode
 */
utf8_string make_utf8_string(const char* str);

/**
 * @brief Converts a C-style string to a UTF-8 string, replacing invalid sequences with U+FFFD REPLACEMENT CHARACTER (�).
 *
 * @details It takes a C-style string as input and converts it to a UTF-8 encoded string.
 *          Any invalid UTF-8 sequences in the input string are replaced with the U+FFFD REPLACEMENT CHARACTER (�) to ensure
 *          that the resulting string is valid UTF-8. The resulting string is dynamically allocated and the caller
 *          is responsible for freeing the memory when no longer needed using `free_owned_utf8_string`.
 *
 * @param str The input C-style string to convert. The string can contain invalid UTF-8 sequences.
 * @return An `owned_utf8_string` structure containing the resulting UTF-8 string. If memory allocation fails, the structure
 *         will contain a `NULL` pointer and a `byte_len` of 0.
 *
 * @code
 * // Example usage:
 * const char* str = "hello\xC0\xC0 world!";
 * owned_utf8_string owned_ustr = make_utf8_string_lossy(str);
 * @endcode
 */
owned_utf8_string make_utf8_string_lossy(const char* str);

/**
 * @brief Creates the non-owning UTF-8 encoded string `utf8_string` from an `owned_utf8_string`.
 *
 * @details The resulting `utf8_string` will point to the same underlying string without taking ownership.
 *          The caller must ensure the original `owned_utf8_string` remains valid as long as the reference is used.
 *
 * @param owned_str The owned UTF-8 string from which to create a non-owning reference.
 * @return utf8_string A non-owning UTF-8 string reference (`utf8_string`) pointing to the same data.
 *
 * @note This function does not free or transfer ownership of the `owned_utf8_string`.
 *       The caller is responsible for managing the lifetime of the owned string.
 */
utf8_string as_utf8_string(const owned_utf8_string* owned_str);

/**
 * @brief Frees the memory allocated for an `owned_utf8_string`.
 *
 * @details The `free_owned_utf8_string` function deallocates the memory used by an `owned_utf8_string`
 *          and sets the `str` pointer to `NULL` and `byte_len` to 0.
 *
 * @param owned_str A pointer to the `owned_utf8_string` structure to be freed.
 *
 * @code
 * // Example usage:
 * owned_utf8_string owned_ustr = make_utf8_string_lossy("hello\xC0\xC0 world!");
 * free_owned_utf8_string(&owned_ustr);
 * @endcode
 */
void free_owned_utf8_string(owned_utf8_string* owned_str);

/**
 * @brief Creates a UTF-8 string slice from a specified range of bytes in the original string.
 *
 * @param ustr The original UTF-8 string.
 * @param byte_index The starting byte index of the slice.
 * @param byte_len The byte length of the slice.
 * @return A UTF-8 string representing the specified byte range [offset, offset + byte_len) if valid (range between UTF-8 char boundaries);
 * otherwise { .str = NULL, .byte_len = 0 }
 *
 * @note if `byte_index` >= strlen(ustr.str) then returns terminating '\0' of ustr.str { .str = '\0', .byte_len = 0 }
 * @note if `byte_index` + `byte_len` >= strlen(ustr.str) then only chars till terminating '\0' are considered.
 */
utf8_string slice_utf8_string(utf8_string ustr, size_t byte_index, size_t byte_len);

/**
 * @brief Creates an iterator for traversing UTF-8 characters within a string. (see next_utf8_char( .. ) for traversal)
 *
 * @param ustr The UTF-8 string to iterate over.
 * @return An iterator structure initialized to the start of the string.
 */
utf8_char_iter make_utf8_char_iter(utf8_string ustr);

/**
 * @brief Retrieves the next UTF-8 character from the iterator.
 *
 * @param iter Pointer to the UTF-8 character iterator.
 * @return The next UTF-8 character from the iterator.
 * @note If the iterator reaches the end, it keeps returning terminating '\0' of iter.str { .str = '\0', .byte_len = 0 }
 */
utf8_char next_utf8_char(utf8_char_iter* iter);

/**
 * @brief Retrieves the UTF-8 character at the specified character index within a UTF-8 string in O(n) time.
 *
 * @details The `nth_utf8_char` function returns the UTF-8 character located at the specified character index
 * within the given UTF-8 string. The character index is zero-based, indicating the position of
 * the character in the string. If the index is out of bounds or invalid, the function returns
 * { .str = NULL, .byte_len = 0 }
 *
 * @param ustr The UTF-8 string from which to retrieve the character.
 * @param char_index The zero-based index of the character to retrieve.
 * @return The UTF-8 character at the specified index within the string.
 *
 * @code
 * // Example usage:
 * utf8_string str = make_utf8_string("Hello Здравствуйте こんにちは");
 * utf8_char char_at_index = nth_utf8_char(str, 7);    // д
 * @endcode
 */
utf8_char nth_utf8_char(utf8_string ustr, size_t char_index);

/**
 * @brief Counts the number of UTF-8 characters in the given utf8_string.
 *
 * @param ustr The UTF-8 string whose characters are to be counted.
 * @return The total number of characters in the UTF-8 string.
 */
size_t utf8_char_count(utf8_string ustr);

/**
 * @brief Checks if a given byte is the start of a UTF-8 character. ('\0' is also a valid character boundary)
 *
 * @param str Pointer to the byte to check.
 * @return `true` if the byte is the start of a UTF-8 character; otherwise, `false`.
 */
bool is_utf8_char_boundary(const char* str);

/**
 * @brief Converts a UTF-8 character to its corresponding Unicode code point (which is the same as a UTF-32 value).
 *
 * @param uchar The UTF-8 character to convert.
 * @return The Unicode code point.
 */
uint32_t unicode_code_point(utf8_char uchar);

#endif
utf8.c
#include "utf8.h"

#include <stdlib.h>
#include <string.h>

typedef struct {
    bool valid;
    size_t next_offset;
} utf8_char_validity;

utf8_char_validity validate_utf8_char(const char* str, size_t offset) {
    // Single-byte UTF-8 characters have the form 0xxxxxxx
    if (((uint8_t)str[offset] & 0b10000000) == 0b00000000)
        return (utf8_char_validity) { .valid = true, .next_offset = offset + 1 };

    // Two-byte UTF-8 characters have the form 110xxxxx 10xxxxxx
    if (((uint8_t)str[offset + 0] & 0b11100000) == 0b11000000 &&
        ((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000) {

        // Check for overlong encoding
        // 0(xxxxxxx)
        // 0(1111111)
        // 110(xxxxx) 10(xxxxxx)
        // 110(00001) 10(111111)
        // 110(00010) 10(000000)
        if (((uint8_t)str[offset] & 0b00011111) < 0b00000010)
            return (utf8_char_validity) { .valid = false, .next_offset = offset };

        return (utf8_char_validity) { .valid = true, .next_offset = offset + 2 };
    }

    // Three-byte UTF-8 characters have the form 1110xxxx 10xxxxxx 10xxxxxx
    if (((uint8_t)str[offset + 0] & 0b11110000) == 0b11100000 &&
        ((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000 &&
        ((uint8_t)str[offset + 2] & 0b11000000) == 0b10000000) {

        // Check for overlong encoding
        // 110(xxxxx) 10(xxxxxx)
        // 110(11111) 10(111111)
        // 1110(xxxx) 10(xxxxxx) 10(xxxxxx)
        // 1110(0000) 10(011111) 10(111111)
        // 1110(0000) 10(100000) 10(000000)
        if (((uint8_t)str[offset + 0] & 0b00001111) == 0b00000000 &&
            ((uint8_t)str[offset + 1] & 0b00111111) < 0b00100000)
            return (utf8_char_validity) { .valid = false, .next_offset = offset };

        // Reject UTF-16 surrogates
        // U+D800 to U+DFFF
        // 1110(1101) 10(100000) 10(000000) ED A0 80 to 1110(1101) 10(111111) 10(111111) ED BF BF
        if ((uint8_t)str[offset + 0] == 0b11101101 &&
            (uint8_t)str[offset + 1] >= 0b10100000 &&
            (uint8_t)str[offset + 1] <= 0b10111111)
            return (utf8_char_validity) { .valid = false, .next_offset = offset };

        return (utf8_char_validity) { .valid = true, .next_offset = offset + 3 };
    }

    // Four-byte UTF-8 characters have the form 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    if (((uint8_t)str[offset + 0] & 0b11111000) == 0b11110000 &&
        ((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000 &&
        ((uint8_t)str[offset + 2] & 0b11000000) == 0b10000000 &&
        ((uint8_t)str[offset + 3] & 0b11000000) == 0b10000000) {

        // Check for overlong encoding
        // 1110(xxxx) 10(xxxxxx) 10(xxxxxx)
        // 1110(1111) 10(111111) 10(111111)
        // 11110(xxx) 10(xxxxxx) 10(xxxxxx) 10(xxxxxx)
        // 11110(000) 10(001111) 10(111111) 10(111111)
        // 11110(000) 10(010000) 10(000000) 10(000000)
        if (((uint8_t)str[offset + 0] & 0b00000111) == 0b00000000 &&
            ((uint8_t)str[offset + 1] & 0b00111111) < 0b00010000)
            return (utf8_char_validity) { .valid = false, .next_offset = offset };

        return (utf8_char_validity) { .valid = true, .next_offset = offset + 4 };
    }

    return (utf8_char_validity) { .valid = false, .next_offset = offset };
}

utf8_validity validate_utf8(const char* str) {
    if (str == NULL) return (utf8_validity) { .valid = false, .valid_upto = 0 };

    size_t offset = 0;
    utf8_char_validity char_validity;

    while (str[offset] != '\0') {
        char_validity = validate_utf8_char(str, offset);
        if (char_validity.valid) offset = char_validity.next_offset;
        else return (utf8_validity) { .valid = false, .valid_upto = offset };
    }

    return (utf8_validity) { .valid = true, .valid_upto = offset };
}

utf8_string make_utf8_string(const char* str) {
    utf8_validity validity = validate_utf8(str);
    if (validity.valid) return (utf8_string) { .str = str, .byte_len = validity.valid_upto };
    return (utf8_string) { .str = NULL, .byte_len = 0 };
}

owned_utf8_string make_utf8_string_lossy(const char* str) {
    if (str == NULL) return (owned_utf8_string) { .str = NULL, .byte_len = 0 };

    size_t len = strlen(str);

    // Worst case scenario: every byte is invalid and is replaced with 3 bytes for U+FFFD
    size_t worst_case_size = len * 3 + 1;

    // Allocate buffer for the lossy UTF-8 string
    char* buffer = (char*)malloc(worst_case_size);
    if (!buffer) return (owned_utf8_string) { .str = NULL, .byte_len = 0 }; // failed allocation

    size_t buffer_offset = 0;
    size_t offset = 0;
    utf8_char_validity char_validity;

    while (offset < len) {
        char_validity = validate_utf8_char(str, offset);

        if (char_validity.valid) {
            // Copy valid UTF-8 character sequence to the buffer
            size_t char_len = char_validity.next_offset - offset;
            memcpy(buffer + buffer_offset, str + offset, char_len);
            buffer_offset += char_len;
            offset = char_validity.next_offset;
        } else {
            // Insert the UTF-8 bytes for U+FFFD (�)
            // FFFD = 1111111111111101
            //      = (1111) (111111) (111101)
            //      = 1110(1111) 10(111111) 10(111101)
            //      = EF BF BD
            buffer[buffer_offset++] = 0xEF;
            buffer[buffer_offset++] = 0xBF;
            buffer[buffer_offset++] = 0xBD;
            offset++;
        }
    }

    buffer[buffer_offset] = '\0';

    return (owned_utf8_string) { .str = buffer, .byte_len = buffer_offset };
}

utf8_string as_utf8_string(const owned_utf8_string* owned_str) {
    return (utf8_string) { .str = owned_str->str, .byte_len = owned_str->byte_len };
}

void free_owned_utf8_string(owned_utf8_string* owned_str) {
    if (owned_str->str) {
        free(owned_str->str);
        owned_str->str = NULL;
        owned_str->byte_len = 0;
    }
}

utf8_char_iter make_utf8_char_iter(utf8_string ustr) {
    return (utf8_char_iter) { .str = ustr.str };
}

bool is_utf8_char_boundary(const char* str) {
    return (uint8_t)*str <= 0b01111111 || (uint8_t)*str >= 0b11000000;
}

utf8_string slice_utf8_string(utf8_string ustr, size_t start_byte_index, size_t byte_len) {
    if (start_byte_index > ustr.byte_len) start_byte_index = ustr.byte_len;

    size_t excl_end_byte_index = start_byte_index + byte_len;
    if (excl_end_byte_index > ustr.byte_len) excl_end_byte_index = ustr.byte_len;

    if (is_utf8_char_boundary(ustr.str + start_byte_index) && is_utf8_char_boundary(ustr.str + excl_end_byte_index))
        return (utf8_string) { .str = ustr.str + start_byte_index, .byte_len = excl_end_byte_index - start_byte_index };

    return (utf8_string) { .str = NULL, .byte_len = 0 };
}

utf8_char next_utf8_char(utf8_char_iter* iter) {
    if (*iter->str == '\0') return (utf8_char) { .str = iter->str, .byte_len = 0 };

    // iter->str is at the current char's starting byte (char boundary).
    const char* curr_boundary = iter->str;

    iter->str++;
    uint8_t byte_len = 1;

    // find the next char's starting byte (next char boundary) and set the iter->str to that.
    while (!is_utf8_char_boundary(iter->str)) {
        iter->str++;
        byte_len++;
    }

    return (utf8_char) { .str = curr_boundary, .byte_len = byte_len };
}

utf8_char nth_utf8_char(utf8_string ustr, size_t char_index) {
    utf8_char_iter iter = make_utf8_char_iter(ustr);

    utf8_char ch;
    while ((ch = next_utf8_char(&iter)).byte_len != 0 && char_index-- != 0) {}

    if (ch.byte_len == 0) return (utf8_char) { .str = NULL, .byte_len = 0 };
    return ch;
}

size_t utf8_char_count(utf8_string ustr) {
    utf8_char_iter iter = make_utf8_char_iter(ustr);

    size_t count = 0;
    while (next_utf8_char(&iter).byte_len > 0) count++;
    return count;
}

uint32_t unicode_code_point(utf8_char uchar) {
    switch (uchar.byte_len) {
    case 1: return uchar.str[0] & 0b01111111;
    case 2: return
        (uchar.str[0] & 0b00011111) << 6 |
        (uchar.str[1] & 0b00111111);
    case 3: return
        (uchar.str[0] & 0b00001111) << 12 |
        (uchar.str[1] & 0b00111111) << 6 |
        (uchar.str[2] & 0b00111111);
    case 4: return
        (uchar.str[0] & 0b00000111) << 18 |
        (uchar.str[1] & 0b00111111) << 12 |
        (uchar.str[2] & 0b00111111) << 6 |
        (uchar.str[3] & 0b00111111);
    }

    return 0; // unreachable
}

Zdroje a odkazy