diff --git a/CLAUDE.md b/CLAUDE.md index d5aba54..121f67e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,13 +14,13 @@ CODE/dsk -> SYSTEM/system/deshab64/deshab.elf DSK 主内核(`deshab.elf`)当前已实现: - 内嵌 FAT32 只读解析器(256 扇区批量读取 + 内存内文件查找) - PIE ELF loader(PT_LOAD + PT_DYNAMIC + R_X86_64_RELATIVE 重定位) -- 旋转加载动画(双缓冲精灵、顺时针、comet-tail 渐变弧) +- 静态启动 Logo(嵌入 Logo.png RGBA 数据,居中 alpha 混合绘制于渐变背景上,取代原旋转加载动画) - 首次启动检测:读取 `firstInit.txt`,值为 `0` 则触发首次启动流程 - 系统初始化调度:DSK 直接加载并调用 `mouseInit.elf`(PS/2 鼠标安全初始化) - 用户设置向导:DSK 加载并跳转 `FirstInit.elf`(圆角卡片 UI、键盘输入、SHA256 密码、加密配置缓冲) FirstInit(`CODE/firstInit`)只负责用户级设置向导: -- 淡蓝背景 → 旋转环淡出 → "欢迎使用 Deshab" → 设置提示 → 账户设置卡片 +- 淡蓝背景 → "欢迎使用 Deshab"(中英双语,英文小字副标题)→ 设置提示 → 账户设置卡片 - PS/2 键盘轮询输入:计算机名、用户名、密码 - SHA256 密码摘要 + XOR 加密配置缓冲(user.conf 格式已定义,写盘待 FAT32 write API) - 18px 黑体中文位图(预渲染嵌入 ELF) @@ -126,7 +126,7 @@ DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解 - **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC MMIO 只读寄存器验证;当前保留 PIC IRQ 路由 - **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举 - **pci** (stage0): PCI config space 扫描, 设备枚举 -- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景 + 居中四色圆弧加载环(纯整数运算,无浮点依赖) +- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景清屏(原四色圆弧加载环已移除,启动画面改由 DSK 静态 Logo 接管) - **ahci** (stage1): PCI AHCI `8086:2922` 探测, BAR5/ABAR HHDM MMIO 映射, HBA CAP/GHC/PI/VS 与 port 枚举;已接入 `kernel_api.dma`,支持 SATA disk IDENTIFY 与 LBA0 READ 最小 DMA 路径;可向 `kernel_api.block` 注册 `ahci0` block provider;默认无 SATA 盘/ATAPI 场景会跳过命令 - **nvme** (stage1): PCI NVMe `1b36:0010` 探测, BAR0/BAR1 解析, IRQ line 读取;当前 QEMU BAR0 位于 4G 以上,MMIO register 读取等待高位 PCI MMIO 映射能力 - **bootfs** (stage1): Limine boot module 内存文件系统 diff --git a/CODE/DKM/ahci/ahci.c b/CODE/DKM/ahci/ahci.c index 9a056c7..404e4ef 100644 --- a/CODE/DKM/ahci/ahci.c +++ b/CODE/DKM/ahci/ahci.c @@ -4,100 +4,7 @@ * and ports so later block I/O can be added safely. */ -#include - -#define DKM_DRIVER_MAGIC 0x444B4D31u -#define DKM_ABI_VERSION 1u - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; -typedef long long i64; - -#define NULL ((void *)0) - -struct dkm_log_api { - void (*info)(const char *msg); - void (*warn)(const char *msg); - void (*error)(const char *msg); - void (*panic)(const char *msg); -}; - -struct dkm_dma_buffer { - void *virt; - u64 phys; - u64 size; -}; - -struct dkm_dma_api { - int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out); -}; - -struct dkm_block_device_desc { - const char *name; - u64 sector_size; - u64 sector_count; - void *ctx; - int (*read)(void *ctx, u64 lba, u32 count, void *buffer); -}; - -struct dkm_block_api { - int (*register_device)(const struct dkm_block_device_desc *desc); - u32 (*device_count)(void); - int (*read)(u32 index, u64 lba, u32 count, void *buffer); - u64 (*sector_size)(u32 index); - const char *(*device_name)(u32 index); -}; - -struct dkm_kernel_api { - u32 version; - u32 size; - u64 feature_bits; - const struct dkm_log_api *log; - const void *mem; - const void *utsm; - const void *irq; - const void *pci; - const struct dkm_dma_api *dma; - const void *vfs; - const void *net; - const void *timer; - const void *drr; - const void *rsdp_address; - const void *fb_address; - u64 fb_width; - u64 fb_height; - u64 fb_pitch; - u16 fb_bpp; - const void *boot_modules_response; - int (*irq_register)(u8 irq, void *handler); - u64 hhdm_offset; - const struct dkm_block_api *block; -}; - -struct dkm_driver_handle; - -struct dkm_driver_desc { - u32 magic; - u16 abi_version; - u16 desc_size; - const char *name; - const char *version; - const char *vendor; - u32 driver_class; - u32 stage; - u32 flags; - u32 priority; - const char *const *depends; - u32 depends_count; - const char *const *provides; - u32 provides_count; - u64 min_kernel_abi; - u64 feature_bits; - u64 reserved0; - u64 reserved1; -}; +#include "../dkm_shared.h" static const char *const g_depends[] = { "pci", "irq" }; static const char *const g_provides[] = { "block" }; @@ -124,21 +31,6 @@ const struct dkm_driver_desc driver_desc = { .reserved1 = 0 }; -#define PCI_ADDR 0xCF8 -#define PCI_DATA 0xCFC -#define PCI_VENDOR_ID 0x00 -#define PCI_COMMAND 0x04 -#define PCI_PROG_IF 0x09 -#define PCI_SUBCLASS 0x0A -#define PCI_CLASS 0x0B -#define PCI_HEADER 0x0E -#define PCI_BAR5 0x24 -#define PCI_IRQ_LINE 0x3C - -#define PCI_CMD_IO (1u << 0) -#define PCI_CMD_MEM (1u << 1) -#define PCI_CMD_BUSM (1u << 2) - #define AHCI_CLASS_STORAGE 0x01 #define AHCI_SUBCLASS_SATA 0x06 #define AHCI_PROGIF_AHCI 0x01 @@ -170,12 +62,15 @@ const struct dkm_driver_desc driver_desc = { #define AHCI_CMD_ST (1u << 0) #define AHCI_CMD_FRE (1u << 4) +#define AHCI_CMD_W (1u << 6) #define AHCI_CMD_FR (1u << 14) #define AHCI_CMD_CR (1u << 15) #define AHCI_CMD_ICC_ACTIVE (1u << 28) #define ATA_CMD_IDENTIFY 0xEC #define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_DMA_EXT 0x35 typedef struct __attribute__((packed)) ahci_cmd_header { u16 flags; @@ -207,34 +102,12 @@ static ahci_cmd_header *g_cmd_header; static struct dkm_dma_buffer g_cmd_table; static struct dkm_dma_buffer g_data; -static __inline__ void outl(u16 port, u32 value) { - __asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port)); -} - -static __inline__ u32 inl(u16 port) { - u32 value; - __asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port)); - return value; -} - static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { - u32 addr = (1u << 31) - | ((u32)bus << 16) - | ((u32)dev << 11) - | ((u32)func << 8) - | ((u32)reg & 0xFC); - outl(PCI_ADDR, addr); - return inl(PCI_DATA); + return dkm_pci_read(bus, dev, func, reg); } static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) { - u32 addr = (1u << 31) - | ((u32)bus << 16) - | ((u32)dev << 11) - | ((u32)func << 8) - | ((u32)reg & 0xFC); - outl(PCI_ADDR, addr); - outl(PCI_DATA, value); + dkm_pci_write(bus, dev, func, reg, value); } static void log_hex(const char *prefix, u64 value) { @@ -333,15 +206,21 @@ static int ahci_stop_port(u32 port) { u32 cmd = g_abar[(base + PxCMD) / 4]; cmd &= ~AHCI_CMD_ST; g_abar[(base + PxCMD) / 4] = cmd; - for (u32 i = 0; i < 100000; i++) { + /* 实机: 500ms TSC 超时等待 CR 清除 */ + u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500; + while (dkm_rdtsc() < deadline) { cmd = g_abar[(base + PxCMD) / 4]; if (!(cmd & AHCI_CMD_CR)) break; + __asm__ volatile("pause"); } cmd &= ~AHCI_CMD_FRE; g_abar[(base + PxCMD) / 4] = cmd; - for (u32 i = 0; i < 100000; i++) { + /* 实机: 500ms TSC 超时等待 FR 清除 */ + deadline = dkm_rdtsc() + dkm_tsc_per_ms * 500; + while (dkm_rdtsc() < deadline) { cmd = g_abar[(base + PxCMD) / 4]; if (!(cmd & AHCI_CMD_FR)) return 0; + __asm__ volatile("pause"); } return -1; } @@ -368,7 +247,23 @@ static void ahci_fill_read_fis(u8 *fis, u64 lba, u32 count) { ahci_zero(fis, 64); fis[0] = 0x27; fis[1] = 1u << 7; - fis[2] = 0x25; /* READ DMA EXT */ + fis[2] = ATA_CMD_READ_DMA_EXT; + fis[4] = (u8)(lba & 0xff); + fis[5] = (u8)((lba >> 8) & 0xff); + fis[6] = (u8)((lba >> 16) & 0xff); + fis[7] = (1u << 6) | (u8)((lba >> 24) & 0x0f); + fis[8] = (u8)((lba >> 24) & 0xff); + fis[9] = (u8)((lba >> 32) & 0xff); + fis[10] = (u8)((lba >> 40) & 0xff); + fis[12] = (u8)(count & 0xff); + fis[13] = (u8)((count >> 8) & 0xff); +} + +static void ahci_fill_write_fis(u8 *fis, u64 lba, u32 count) { + ahci_zero(fis, 64); + fis[0] = 0x27; + fis[1] = 1u << 7; + fis[2] = ATA_CMD_WRITE_DMA_EXT; fis[4] = (u8)(lba & 0xff); fis[5] = (u8)((lba >> 8) & 0xff); fis[6] = (u8)((lba >> 16) & 0xff); @@ -420,7 +315,81 @@ static int ahci_read_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_ return -3; } +static int ahci_write_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data, u64 lba, u32 count, const void *buffer) { + if (!buffer || count == 0) return -1; + if (count > 8) return -2; + u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE; + ahci_cmd_table *tbl = (ahci_cmd_table *)table->virt; + u32 bytes = count * 512; + ahci_zero(tbl, sizeof(ahci_cmd_table)); + + /* copy data from user buffer into DMA buffer */ + { + u8 *dst = (u8 *)data->virt; + const u8 *src = (const u8 *)buffer; + for (u32 j = 0; j < bytes; j++) dst[j] = src[j]; + } + + hdr[0].flags = 5 | (1u << 6); /* CFL=5, W=1 (write) */ + hdr[0].prdtl = 1; + hdr[0].prdbc = 0; + hdr[0].ctba = (u32)(table->phys & 0xffffffffu); + hdr[0].ctbau = (u32)(table->phys >> 32); + + tbl->prdt[0].dba = (u32)(data->phys & 0xffffffffu); + tbl->prdt[0].dbau = (u32)(data->phys >> 32); + tbl->prdt[0].dbc_i = (bytes - 1) | (1u << 31); + ahci_fill_write_fis(tbl->cfis, lba, count); + + /* set PxCMD.W to indicate write direction */ + u32 cmd = g_abar[(base + PxCMD) / 4]; + cmd |= AHCI_CMD_W; + g_abar[(base + PxCMD) / 4] = cmd; + + g_abar[(base + PxIS) / 4] = 0xffffffffu; + g_abar[HBA_IS / 4] = (1u << port); + g_abar[(base + PxCI) / 4] = 1; + for (u32 i = 0; i < 5000000; i++) { + if ((g_abar[(base + PxCI) / 4] & 1u) == 0) { + /* clear PxCMD.W after write completes */ + cmd = g_abar[(base + PxCMD) / 4]; + cmd &= ~AHCI_CMD_W; + g_abar[(base + PxCMD) / 4] = cmd; + return 0; + } + } + + /* timeout: clear PxCMD.W */ + cmd = g_abar[(base + PxCMD) / 4]; + cmd &= ~AHCI_CMD_W; + g_abar[(base + PxCMD) / 4] = cmd; + + g_log->warn("[ahci] WRITE timeout"); + log_hex("[ahci] WRITE lba=", lba); + log_hex("[ahci] WRITE count=", count); + log_hex("[ahci] WRITE CI=", g_abar[(base + PxCI) / 4]); + log_hex("[ahci] WRITE IS=", g_abar[(base + PxIS) / 4]); + log_hex("[ahci] WRITE TFD=", g_abar[(base + PxTFD) / 4]); + return -3; +} + static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) { + (void)ctx; + if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1; + log_hex("[ahci] block_read lba=", lba); + log_hex("[ahci] block_read count=", count); + while (count > 0) { + u32 chunk = count > 8 ? 8 : count; + int status = ahci_read_blocks_internal(g_ready_port, g_cmd_header, &g_cmd_table, &g_data, lba, chunk, buffer); + if (status != 0) return status; + lba += chunk; + count -= chunk; + buffer = (void *)((u8 *)buffer + (chunk * 512)); + } + return 0; +} + +static int ahci_block_write(void *ctx, u64 lba, u32 count, const void *buffer) { (void)ctx; if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1; /* ensure port is in ST+FRE state */ @@ -433,11 +402,11 @@ static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) { } while (count > 0) { u32 chunk = count > 8 ? 8 : count; - int status = ahci_read_blocks_internal(g_ready_port, g_cmd_header, &g_cmd_table, &g_data, lba, chunk, buffer); + int status = ahci_write_blocks_internal(g_ready_port, g_cmd_header, &g_cmd_table, &g_data, lba, chunk, buffer); if (status != 0) return status; lba += chunk; count -= chunk; - buffer = (void *)((u8 *)buffer + (chunk * 512)); + buffer = (const void *)((const u8 *)buffer + (chunk * 512)); } return 0; } @@ -536,6 +505,8 @@ __attribute__((visibility("default"))) int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) { (void)handle; + /* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */ + dkm_tsc_calibrate(); if (!api || !api->log) return -1; g_log = api->log; @@ -628,6 +599,7 @@ int driver_init(const struct dkm_kernel_api *api, desc.sector_count = 0; desc.ctx = 0; desc.read = ahci_block_read; + desc.write = ahci_block_write; int index = api->block->register_device(&desc); log_hex("[ahci] block provider index=", (u64)(i64)index); } else if (identified) { diff --git a/CODE/DKM/ath9k/ath9k.c b/CODE/DKM/ath9k/ath9k.c index 05fd8e2..561e771 100644 --- a/CODE/DKM/ath9k/ath9k.c +++ b/CODE/DKM/ath9k/ath9k.c @@ -17,105 +17,7 @@ * 部分高级特性(ANI/MRR/AGC 调优)留待硬件验证后增量完善。 */ -#include - -#define DKM_DRIVER_MAGIC 0x444B4D31u -#define DKM_ABI_VERSION 1u - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; -typedef long long i64; -typedef signed char i8; -#define NULL ((void *)0) - -/* ---- DKM ABI ---- */ -struct dkm_log_api { - void (*info)(const char *msg); - void (*warn)(const char *msg); - void (*error)(const char *msg); - void (*panic)(const char *msg); -}; - -struct dkm_net_scan_result { - char ssid[33]; - u8 bssid[6]; - u8 channel; - i8 rssi; - u8 security; -}; - -struct dkm_net_device_desc { - const char *name; - u8 mac[6]; - u32 flags; - void *ctx; - int (*tx)(void *ctx, const void *packet, u32 length); - int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length); - int (*scan_start)(void *ctx); - int (*scan_count)(void *ctx); - int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); - int (*is_wireless)(void *ctx); -}; - -struct dkm_net_api { - int (*register_device)(const struct dkm_net_device_desc *desc); - u32 (*device_count)(void); - int (*device_info)(u32 index, void *out); - int (*tx)(u32 index, const void *packet, u32 length); - int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); - int (*scan_start)(u32 index); - int (*scan_count)(u32 index); - int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); - int (*is_wireless)(u32 index); -}; - -struct dkm_dma_buffer { - void *virt; - u64 phys; - u64 size; -}; - -struct dkm_dma_api { - int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out); -}; - -#define DKM_NET_F_LINK_UP (1u << 0) -#define DKM_NET_F_TX_READY (1u << 1) -#define DKM_NET_F_RX_READY (1u << 2) -#define DKM_NET_F_WIRELESS (1u << 3) - -#define DKM_NET_SEC_OPEN 0u -#define DKM_NET_SEC_WEP 1u -#define DKM_NET_SEC_WPA 2u -#define DKM_NET_SEC_WPA2 3u -#define DKM_NET_SEC_WPA3 4u - -struct dkm_kernel_api { - u32 version; u32 size; u64 feature_bits; - const struct dkm_log_api *log; - const void *mem,*utsm,*irq_api,*pci_api; - const struct dkm_dma_api *dma; - const void *vfs_api; - const struct dkm_net_api *net; - const void *timer,*drr; - const void *rsdp_address,*fb_address; - u64 fb_width,fb_height,fb_pitch; u16 fb_bpp; - const void *boot_modules_response; - int (*irq_register)(u8 irq, void *handler); - u64 hhdm_offset; -}; - -struct dkm_driver_handle; -struct dkm_driver_desc { - u32 magic; u16 abi_version; u16 desc_size; - const char *name,*version,*vendor; - u32 driver_class,stage,flags,priority; - const char *const *depends; u32 depends_count; - const char *const *provides; u32 provides_count; - u64 min_kernel_abi,feature_bits,reserved0,reserved1; -}; +#include "../dkm_shared.h" static const char *const g_depends[] = {"pci","irq"}; static const char *const g_provides[] = {"netdev"}; @@ -129,19 +31,6 @@ const struct dkm_driver_desc driver_desc = { .provides=g_provides,.provides_count=1,.min_kernel_abi=1, }; -/* ---- PCI config ---- */ -#define PCI_ADDR 0xCF8 -#define PCI_DATA 0xCFC -#define PCI_VENDOR_ID 0x00 -#define PCI_COMMAND 0x04 -#define PCI_BAR0 0x10 -#define PCI_BAR1 0x14 -#define PCI_IRQ_LINE 0x3C - -#define PCI_CMD_IO (1u << 0) -#define PCI_CMD_MEM (1u << 1) -#define PCI_CMD_BUSMST (1u << 2) - /* ---- ath9k 寄存器(AR9280/9285/9287 PCIe MAC,Linux ath9k reg.h 关键子集) ---- */ #define AR_RC 0x4000u #define AR_RC_MAC 0x00000001u @@ -285,20 +174,20 @@ static u32 g_scan_count; static int g_scanning; /* ---- 串口日志(参考 e1000 raw_log) ---- */ -static __inline__ void outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p):"memory"); } -static __inline__ u8 inb(u16 p) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } -static __inline__ void outl(u16 p, u32 v) { __asm__ volatile("outl %0,%1"::"a"(v),"Nd"(p):"memory"); } -static __inline__ u32 inl(u16 p) { u32 v; __asm__ volatile("inl %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } +#define COM1 0x3F8 + +/* 串口发送等待 thin wrapper — 调用 dkm 共享实现 */ +static void serial_wait_tx(void) { dkm_serial_wait_tx(COM1); } static void raw_log(const char *s) { while (*s) { char c = *s++; if (c == '\n') { - for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; - outb(0x3F8, '\r'); + serial_wait_tx(); + dkm_outb(COM1, '\r'); } - for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; - outb(0x3F8, (u8)c); + serial_wait_tx(); + dkm_outb(COM1, (u8)c); } } @@ -307,26 +196,19 @@ static void raw_hex(const char *prefix, u64 v) { raw_log(prefix); raw_log("0x"); for (int i=15; i>=0; i--) { - for (u32 wait=0; wait<100000; wait++) if (inb(0x3F8+5)&0x20) break; - outb(0x3F8, (u8)h[(v >> (i * 4)) & 0xf]); + serial_wait_tx(); + dkm_outb(COM1, (u8)h[(v >> (i * 4)) & 0xf]); } raw_log("\n"); } -static void delay_us(u32 us) { - /* 粗略延时:每个 iteration ~1us on QEMU x86 */ - for (u32 i = 0; i < us * 10; i++) __asm__ volatile("pause"); -} - /* ---- PCI 配置空间 ---- */ static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { - u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); - outl(PCI_ADDR,a); return inl(PCI_DATA); + return dkm_pci_read(bus, dev, func, reg); } static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) { - u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); - outl(PCI_ADDR,a); outl(PCI_DATA,v); + dkm_pci_write(bus, dev, func, reg, v); } /* ath9k PCI 设备 ID 表(vendor 0x168c Atheros) */ @@ -403,11 +285,12 @@ static u16 ath9k_eeprom_read(u16 addr) { /* AR5416_EEPROM_CMD: bit0=READ, bit1=BUSY, bit2=RESET */ /* 启动 read:写 cmd=READ | addr<<3 */ ath9k_reg_write(AR_EEPROM_CMD, AR_EEPROM_CMD_RESET); - delay_us(5); + dkm_delay_us(5); u32 cmd = AR_EEPROM_CMD_READ | ((u32)(addr & 0x3FF) << 3); ath9k_reg_write(AR_EEPROM_CMD, cmd); - /* 等待 RDBUSY 清除 */ - for (u32 i = 0; i < 100000; i++) { + /* 等待 RDBUSY 清除 (实机: 100ms TSC 超时) */ + u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100; + while (dkm_rdtsc() < deadline) { u32 sts = ath9k_reg_read(AR_EEPROM_STS); if (!(sts & AR_EEPROM_STS_RDBUSY)) { if (sts & AR_EEPROM_STS_VALID) { @@ -415,7 +298,7 @@ static u16 ath9k_eeprom_read(u16 addr) { } return 0xffff; } - delay_us(1); + dkm_delay_us(1); } return 0xffff; } @@ -425,27 +308,29 @@ static int ath9k_reset(void) { raw_log("[ath9k.raw] reset sequence\n"); /* 1. 强制唤醒(Power-On) */ ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT); - delay_us(50); - /* 2. 等待 RTC 进入 ON 状态 */ - for (u32 i = 0; i < 100000; i++) { + dkm_delay_us(50); + /* 2. 等待 RTC 进入 ON 状态 (实机: 100ms TSC 超时) */ + u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100; + while (dkm_rdtsc() < deadline) { u32 s = ath9k_reg_read(AR_RTC_STATUS); if ((s & 0x3) == AR_RTC_STATUS_ON) break; - delay_us(1); + dkm_delay_us(1); } /* 3. 全 chip 复位 */ ath9k_reg_write(AR_RC, AR_RC_MAC | AR_RC_BB | AR_RC_RPCU); - delay_us(100); + dkm_delay_us(100); ath9k_reg_write(AR_RC, 0); - delay_us(100); + dkm_delay_us(100); /* 4. 重新唤醒 */ ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT); - delay_us(50); - /* 5. 等待 RTC ON */ + dkm_delay_us(50); + /* 5. 等待 RTC ON (实机: 100ms TSC 超时) */ u32 ok = 0; - for (u32 i = 0; i < 100000; i++) { + u64 deadline2 = dkm_rdtsc() + dkm_tsc_per_ms * 100; + while (dkm_rdtsc() < deadline2) { u32 s = ath9k_reg_read(AR_RTC_STATUS); if ((s & 0x3) == AR_RTC_STATUS_ON) { ok = 1; break; } - delay_us(1); + dkm_delay_us(1); } if (!ok) { raw_log("[ath9k.raw] reset: RTC not ON\n"); return -1; } raw_log("[ath9k.raw] reset: RTC ON\n"); @@ -461,7 +346,7 @@ static int ath9k_hw_init(void) { raw_log("[ath9k.raw] hw_init minimal\n"); /* 关闭 RX/DMA,清 STA_ID1 */ ath9k_reg_write(AR_CR, AR_CR_RXD); - delay_us(10); + dkm_delay_us(10); ath9k_reg_write(AR_STA_ID1, 0); /* 清中断 */ ath9k_reg_write(AR_ISR, 0xFFFFFFFFu); @@ -650,13 +535,13 @@ static void ath9k_set_channel(u8 channel) { /* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel) */ /* 触发 RFBUS 请求,等待 grant */ ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1); - delay_us(5); + dkm_delay_us(5); for (u32 i = 0; i < 1000; i++) { if (ath9k_reg_read(AR_PHY_RFBUS_REQ) & 0x2) break; - delay_us(1); + dkm_delay_us(1); } ath9k_reg_write(AR_PHY_RFBUS_REQ, 0); - delay_us(50); + dkm_delay_us(50); } /* ---- 被动扫描状态机 ---- @@ -684,7 +569,7 @@ static int ath9k_scan_start_impl(void *ctx) { /* 在该信道停留 ~400ms,期间轮询 RX 环 */ for (u32 tick = 0; tick < 400; tick++) { ath9k_rx_drain(); - delay_us(1000); + dkm_delay_us(1000); } } g_scanning = 0; @@ -752,6 +637,8 @@ __attribute__((visibility("default"))) int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) { (void)handle; + /* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */ + dkm_tsc_calibrate(); raw_log("[ath9k.raw] driver_init entered\n"); if (!api || !api->log) return -1; g_log = api->log; @@ -766,7 +653,7 @@ int driver_init(const struct dkm_kernel_api *api, /* 2. 启用 PCI 命令(MEM + Bus Master) */ u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND); - pci_write(bus, dev, 0, PCI_COMMAND, cmd | PCI_CMD_MEM | PCI_CMD_BUSMST); + pci_write(bus, dev, 0, PCI_COMMAND, cmd | PCI_CMD_MEM | PCI_CMD_BUSM); /* 3. 读取 BAR0 */ u32 bar0 = pci_read(bus, dev, 0, PCI_BAR0); diff --git a/CODE/DKM/console_fb/console_fb.c b/CODE/DKM/console_fb/console_fb.c index 2cdfc87..4718144 100644 --- a/CODE/DKM/console_fb/console_fb.c +++ b/CODE/DKM/console_fb/console_fb.c @@ -1,6 +1,8 @@ /* DKM Console Framebuffer Driver * Stage 1, optional, provides "console". - * Light-blue background + centered circular loading animation (4-color arc ring). + * Clears the framebuffer to a solid light-blue background so the screen is + * clean before DSK takes over and draws the Logo. The former 4-color arc + * loading ring has been removed — DSK now shows a static Logo instead. */ #include @@ -51,7 +53,7 @@ __attribute__((visibility("default"), used)) const struct dkm_driver_desc driver_desc = { .magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION, .desc_size=sizeof(struct dkm_driver_desc), - .name="console_fb", .version="0.2.0", .vendor="Deshab", + .name="console_fb", .version="0.3.0", .vendor="Deshab", .driver_class=5, .stage=1, .flags=0, .priority=0, .depends=NULL, .depends_count=0, .provides=g_provides, .provides_count=1, @@ -61,18 +63,11 @@ const struct dkm_driver_desc driver_desc = { /* --------------------------------------------------------------- * Colors * --------------------------------------------------------------- */ -static const u32 BG = 0xFFC8E0F0; /* light blue background */ -static const u32 ARC[4] = { 0xFF4488CC, 0xFF2266AA, 0xFF66AAEE, 0xFF88CCFF }; +static const u32 BG = 0xFFC8E0F0; /* light blue background — cleared before DSK Logo */ /* --------------------------------------------------------------- * Drawing helpers – all integer arithmetic, no soft-float * --------------------------------------------------------------- */ -static u64 fb_w, fb_h, fb_p; - -/* double-buffer sprite for ring area (128×128 = no flicker) */ -#define SPR_SIZE 128 -static u32 g_sprite[SPR_SIZE * SPR_SIZE]; - static void fb_fill(u32 *fb, u64 pitch, u64 w, u64 h, u32 color) { for (u64 y = 0; y < h; y++) { u32 *line = (u32 *)((u8 *)fb + y * pitch); @@ -80,105 +75,6 @@ static void fb_fill(u32 *fb, u64 pitch, u64 w, u64 h, u32 color) { } } -/* alpha-blend c2 over c1, alpha 0-255 */ -static u32 blend(u32 c1, u32 c2, u32 a) { - u32 na = 256 - a; - u32 r = ((c1 & 0xFF) * na + (c2 & 0xFF) * a) >> 8; - u32 g = (((c1 >> 8) & 0xFF) * na + ((c2 >> 8) & 0xFF) * a) >> 8; - u32 b = (((c1 >> 16) & 0xFF) * na + ((c2 >> 16) & 0xFF) * a) >> 8; - return 0xFF000000 | (b << 16) | (g << 8) | r; -} - -/* integer sqrt – returns floor(sqrt(v)) */ -static u64 isqrt(u64 v) { - u64 x = v, y = (x + 1) >> 1; - while (y < x) { x = y; y = (y + v / y) >> 1; } - return x; -} - -/* approximate atan2 in degrees (0=right, CCW). dx,dy = pixel offset from center */ -static i64 approx_angle(i64 dx, i64 dy) { - if (dx == 0) return dy < 0 ? 90 : 270; - i64 ax = dx < 0 ? -dx : dx; - i64 ay = dy < 0 ? -dy : dy; - i64 a; - if (ax > ay) { - a = ay * 45 / ax; - } else { - a = 90 - ax * 45 / (ay ? ay : 1); - } - /* quadrant correction */ - if (dx >= 0 && dy <= 0) return a; /* Q1 */ - else if (dx < 0 && dy <= 0) return 180 - a; /* Q2 */ - else if (dx < 0 && dy > 0) return 180 + a; /* Q3 */ - else return 360 - a; /* Q4 */ -} - -/* draw one arc segment: rounded caps (6° head/tail), bright mid, comet-tail fade */ -static void arc_segment(u32 *fb, i64 cx, i64 cy, i64 r, i64 start, i64 sweep, u32 color, i64 thk) { - i64 ro = r + thk, ri = r - thk; - i64 ri2 = ri * ri, ro2 = (ro + 1) * (ro + 1); - i64 rnd = 6; /* rounding zone at each end, in degrees */ - - for (i64 dy = -ro - 1; dy <= ro + 1; dy++) { - i64 y = cy + dy; - if ((u64)y >= fb_h) continue; - u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p); - for (i64 dx = -ro - 1; dx <= ro + 1; dx++) { - i64 x = cx + dx; - if ((u64)x >= fb_w) continue; - i64 d2 = dx * dx + dy * dy; - if (d2 < ri2 - 2 || d2 > ro2 + 2) continue; - - i64 ang = approx_angle(dx, dy); - i64 a = ang - start; - if (a < 0) a += 360; - if ((u64)a > (u64)sweep) continue; - - /* rounded-cap fade: 0→255 over rnd°, then 255→0 over last rnd° */ - u32 fade; - if ((u64)a < (u64)rnd) { - fade = (u32)(a * 255 / rnd); - } else if ((u64)a > (u64)(sweep - rnd)) { - fade = (u32)((sweep - a) * 255 / rnd); - } else { - fade = 255; - } - - i64 dist = (i64)isqrt((u64)d2); - u32 effective = blend(BG, color, fade); - if (dist >= ri && dist <= ro) { - line[(u64)x] = effective; - } else if (dist >= ri - 2 && dist < ri) { - line[(u64)x] = blend(BG, effective, (u32)((dist - (ri - 2)) * 85)); - } else if (dist > ro && dist <= ro + 2) { - line[(u64)x] = blend(effective, BG, (u32)((dist - ro) * 85)); - } - } - } -} - -/* filled disc with anti-aliased edge */ -static void fill_disc(u32 *fb, i64 cx, i64 cy, i64 r, u32 color) { - i64 r2 = r * r, o2 = (r + 1) * (r + 1); - for (i64 dy = -r - 2; dy <= r + 2; dy++) { - i64 y = cy + dy; - if ((u64)y >= fb_h) continue; - u32 *line = (u32 *)((u8 *)fb + (u64)y * fb_p); - for (i64 dx = -r - 2; dx <= r + 2; dx++) { - i64 x = cx + dx; - if ((u64)x >= fb_w) continue; - i64 d2 = dx * dx + dy * dy; - if (d2 <= r2) { - line[(u64)x] = color; - } else if (d2 <= o2) { - u32 a = (u32)((o2 - d2) * 255 / (o2 - r2)); - line[(u64)x] = blend(BG, color, a); - } - } - } -} - /* --------------------------------------------------------------- * Driver entry * --------------------------------------------------------------- */ @@ -192,61 +88,13 @@ int driver_init(const struct dkm_kernel_api *api, return 0; } - api->log->info("[console_fb] boot screen"); + api->log->info("[console_fb] clearing boot screen"); - fb_w = api->fb_width; fb_h = api->fb_height; fb_p = api->fb_pitch; + /* Solid background fill only — DSK draws the Logo next. Clearing here + * avoids firmware/UEFI leftover garbage being visible during the + * UTSM→DSK handoff. */ u32 *fb = (u32 *)api->fb_address; - i64 cx = (i64)fb_w / 2, cy = (i64)fb_h / 2, r = 48, thk = 4; - - /* light-blue background */ - fb_fill(fb, fb_p, fb_w, fb_h, BG); - - /* animation: 2000 frames × 2° clockwise × ~1ms, no flicker */ - i64 spr_p = SPR_SIZE * 4; - i64 scx = SPR_SIZE / 2; - i64 scy = SPR_SIZE / 2; - i64 frame = 0, base = 0; - i64 bx = cx - scx; /* screen position of sprite top-left */ - i64 by = cy - scy; - - while (frame < 2000) { - /* draw into sprite buffer — swap globals to sprite layout */ - u64 save_w = fb_w, save_h = fb_h, save_p = fb_p; - fb_w = SPR_SIZE; fb_h = SPR_SIZE; fb_p = spr_p; - - fb_fill(g_sprite, spr_p, SPR_SIZE, SPR_SIZE, BG); - - arc_segment(g_sprite, scx, scy, r, base + 0, 82, ARC[0], thk); - arc_segment(g_sprite, scx, scy, r, base + 90, 82, ARC[1], thk); - arc_segment(g_sprite, scx, scy, r, base +180, 82, ARC[2], thk); - arc_segment(g_sprite, scx, scy, r, base +270, 82, ARC[3], thk); - - fill_disc(g_sprite, scx, scy, r - 8, blend(BG, 0xFFD0F0FF, 80)); - - /* restore screen globals for blit */ - fb_w = save_w; fb_h = save_h; fb_p = save_p; - - /* blit sprite to screen */ - for (i64 sr = 0; sr < SPR_SIZE; sr++) { - i64 sy = by + sr; - if ((u64)sy >= fb_h) continue; - u32 *dst = (u32 *)((u8 *)fb + (u64)sy * fb_p) + bx; - u32 *src = &g_sprite[sr * SPR_SIZE]; - for (i64 sc = 0; sc < SPR_SIZE; sc++) { - i64 sx = bx + sc; - if ((u64)sx >= fb_w) continue; - dst[sc] = src[sc]; - } - } - - for (volatile u32 d = 0; d < 72000; d++) { - __asm__ volatile ("pause"); - } - - base -= 2; - if (base < 0) base += 360; - frame++; - } + fb_fill(fb, api->fb_pitch, api->fb_width, api->fb_height, BG); api->log->info("[console_fb] ready"); return 0; diff --git a/CODE/DKM/dkm_shared.h b/CODE/DKM/dkm_shared.h new file mode 100644 index 0000000..0c01224 --- /dev/null +++ b/CODE/DKM/dkm_shared.h @@ -0,0 +1,331 @@ +/* DKM 共享头文件 — 消除各驱动重复的结构体/宏/PCI/TSC 代码 + * + * 设计要点: + * - 结构体定义与 UTSM/include/utsm/dkm.h 保持布局一致 (canonical) + * - PCI I/O 函数使用 static __inline__, 每个 .drv 文件获得独立副本, 避免链接冲突 + * - TSC 校准函数使用 static __inline__, 每个 .drv 文件获得自己的 dkm_tsc_per_ms + * - 驱动源文件只需 #include "../dkm_shared.h", 保留各自的 driver_desc 和 driver_init + * + * 使用约定: + * 1. 在 driver_init 开头调用 dkm_tsc_calibrate() 完成校准 + * 2. 时序等待使用 dkm_delay_ms() / dkm_delay_us() + * 3. 串口发送等待使用 dkm_serial_wait_tx(COM1) + * 4. MMIO 寄存器读取等待使用 dkm_rdtsc() + dkm_tsc_per_ms 直接计算 deadline + */ +#ifndef DKM_SHARED_H +#define DKM_SHARED_H + +#include + +/* ---- 基本类型 (与 UTSM/types.h 一致) ---- */ +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef signed char i8; +typedef short i16; +typedef int i32; +typedef long long i64; + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* ---- DKM magic / ABI 版本 ---- */ +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u + +/* ---- DKM 驱动类别 (driver_desc.driver_class) ---- */ +#define DKM_CLASS_PLATFORM 1u +#define DKM_CLASS_BUS 2u +#define DKM_CLASS_INTERRUPT 3u +#define DKM_CLASS_TIMER 4u +#define DKM_CLASS_CONSOLE 5u +#define DKM_CLASS_STORAGE 6u +#define DKM_CLASS_FS 7u +#define DKM_CLASS_NET 8u +#define DKM_CLASS_INPUT 9u +#define DKM_CLASS_GPU 10u +#define DKM_CLASS_MISC 11u + +/* ---- DKM 驱动标志 (driver_desc.flags) ---- */ +#define DKM_F_REQUIRED (1u << 0) +#define DKM_F_BOOT_MODULE (1u << 1) +#define DKM_F_NO_UNLOAD (1u << 2) +#define DKM_F_STRONG_RECOVERY (1u << 3) +#define DKM_F_DMA_REQUIRED (1u << 4) +#define DKM_F_EARLY_LOG (1u << 5) + +/* ---- 网卡链路/能力标志 ---- */ +#define DKM_NET_F_LINK_UP (1u << 0) +#define DKM_NET_F_TX_READY (1u << 1) +#define DKM_NET_F_RX_READY (1u << 2) +#define DKM_NET_F_WIRELESS (1u << 3) + +/* ---- WiFi 扫描加密类型 ---- */ +#define DKM_NET_SEC_OPEN 0u +#define DKM_NET_SEC_WEP 1u +#define DKM_NET_SEC_WPA 2u +#define DKM_NET_SEC_WPA2 3u +#define DKM_NET_SEC_WPA3 4u + +/* ---- DKM 结构体定义 (与 UTSM/include/utsm/dkm.h 布局一致) ---- */ +struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +}; + +struct dkm_dma_buffer { + void *virt; + u64 phys; + u64 size; +}; + +struct dkm_dma_api { + int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out); +}; + +struct dkm_net_scan_result { + char ssid[33]; + u8 bssid[6]; + u8 channel; + i8 rssi; + u8 security; +}; + +struct dkm_net_device_desc { + const char *name; + u8 mac[6]; + u32 flags; + void *ctx; + int (*tx)(void *ctx, const void *packet, u32 length); + int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length); + /* 无线扩展回调(有线驱动置 NULL,向后兼容)。 */ + int (*scan_start)(void *ctx); + int (*scan_count)(void *ctx); + int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(void *ctx); +}; + +struct dkm_net_api { + int (*register_device)(const struct dkm_net_device_desc *desc); + u32 (*device_count)(void); + int (*device_info)(u32 index, void *out); + int (*tx)(u32 index, const void *packet, u32 length); + int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); + int (*scan_start)(u32 index); + int (*scan_count)(u32 index); + int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(u32 index); +}; + +typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer); +typedef int (*dkm_block_write_fn)(void *ctx, u64 lba, u32 count, const void *buffer); + +struct dkm_block_device_desc { + const char *name; + u64 sector_size; + u64 sector_count; + void *ctx; + int (*read)(void *ctx, u64 lba, u32 count, void *buffer); + int (*write)(void *ctx, u64 lba, u32 count, const void *buffer); +}; + +struct dkm_block_api { + int (*register_device)(const struct dkm_block_device_desc *desc); + u32 (*device_count)(void); + int (*read)(u32 index, u64 lba, u32 count, void *buffer); + int (*write)(u32 index, u64 lba, u32 count, const void *buffer); + u64 (*sector_size)(u32 index); + const char *(*device_name)(u32 index); + int (*set_write_fn)(u32 index, dkm_block_write_fn fn); +}; + +/* 与 UTSM/include/utsm/dkm.h 中的 dkm_kernel_api 布局完全一致. + * 字段顺序/类型不可调整 — UTSM 端按此偏移填充. */ +struct dkm_kernel_api { + u32 version; + u32 size; + u64 feature_bits; + const struct dkm_log_api *log; + const void *mem; + const void *utsm; + const void *irq; + const void *pci; + const struct dkm_dma_api *dma; + const void *vfs; + const struct dkm_net_api *net; + const void *timer; + const void *drr; + const void *rsdp_address; + const void *fb_address; + u64 fb_width; + u64 fb_height; + u64 fb_pitch; + u16 fb_bpp; + const void *boot_modules_response; + int (*irq_register)(u8 irq, void *handler); + u64 hhdm_offset; + const struct dkm_block_api *block; /* 末尾字段, 不使用 block 的驱动可不访问 */ +}; + +struct dkm_driver_handle; + +struct dkm_driver_desc { + u32 magic; + u16 abi_version; + u16 desc_size; + const char *name; + const char *version; + const char *vendor; + u32 driver_class; + u32 stage; + u32 flags; + u32 priority; + const char *const *depends; + u32 depends_count; + const char *const *provides; + u32 provides_count; + u64 min_kernel_abi; + u64 feature_bits; + u64 reserved0; + u64 reserved1; +}; + +/* ---- PCI 配置空间常量 ---- */ +#define PCI_ADDR 0xCF8 +#define PCI_DATA 0xCFC +#define PCI_VENDOR_ID 0x00 +#define PCI_DEVICE_ID 0x02 +#define PCI_COMMAND 0x04 +#define PCI_PROG_IF 0x09 +#define PCI_SUBCLASS 0x0A +#define PCI_CLASS 0x0B +#define PCI_HEADER 0x0E +#define PCI_CAP_PTR 0x34 +#define PCI_IRQ_LINE 0x3C +#define PCI_BAR0 0x10 +#define PCI_BAR1 0x14 +#define PCI_BAR2 0x18 +#define PCI_BAR3 0x1C +#define PCI_BAR4 0x20 +#define PCI_BAR5 0x24 + +#define PCI_CMD_IO (1u << 0) +#define PCI_CMD_MEM (1u << 1) +#define PCI_CMD_BUSM (1u << 2) + +/* ---- PCI I/O 端口内联函数 (每个 .drv 获得独立副本) ---- */ +static __inline__ void dkm_outl(u16 port, u32 value) { + __asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port)); +} + +static __inline__ u32 dkm_inl(u16 port) { + u32 value; + __asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static __inline__ void dkm_outb(u16 port, u8 value) { + __asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port)); +} + +static __inline__ u8 dkm_inb(u16 port) { + u8 value; + __asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static __inline__ u32 dkm_pci_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + dkm_outl(PCI_ADDR, addr); + return dkm_inl(PCI_DATA); +} + +static __inline__ void dkm_pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + dkm_outl(PCI_ADDR, addr); + dkm_outl(PCI_DATA, value); +} + +static __inline__ u8 dkm_pci_read8(u8 bus, u8 dev, u8 func, u8 reg) { + u32 v = dkm_pci_read(bus, dev, func, reg); + return (u8)((v >> ((reg & 3) * 8)) & 0xff); +} + +/* ---- TSC 时序: 实机要求用 CPU 频率计算, 不用循环 ---- + * 每个 .drv 包含此头文件后获得自己的 file-local dkm_tsc_per_ms 副本, + * 通过 dkm_tsc_calibrate() 在 driver_init 开头初始化. + */ +static u64 dkm_tsc_per_ms __attribute__((unused)) = 0; + +static __inline__ u64 dkm_rdtsc(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +/* 通过 PIT ch0 + TSC 校准 ~10ms 区间, 推算每毫秒 TSC 周期数. */ +static __inline__ void dkm_tsc_calibrate(void) { + dkm_outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */ + dkm_outb(0x40, 0x7c); /* 11932 low = ~10ms */ + dkm_outb(0x40, 0x2e); /* 11932 high */ + u64 tsc_start = dkm_rdtsc(); + u16 prev = 0; u64 loops = 0; + for (;;) { + dkm_outb(0x43, 0x00); + u16 cur = (u16)dkm_inb(0x40) | ((u16)dkm_inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = dkm_rdtsc(); + dkm_tsc_per_ms = (tsc_end - tsc_start) / 10; +} + +static __inline__ void dkm_delay_ms(u32 ms) { + if (!dkm_tsc_per_ms) { + for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause"); + return; + } + u64 target = dkm_tsc_per_ms * ms; + u64 start = dkm_rdtsc(); + while (dkm_rdtsc() - start < target) __asm__ volatile("pause"); +} + +static __inline__ void dkm_delay_us(u32 us) { + if (!dkm_tsc_per_ms) { + for (volatile u32 i = 0; i < 100 * us; i++) __asm__ volatile("pause"); + return; + } + u64 target = dkm_tsc_per_ms * us / 1000; + u64 start = dkm_rdtsc(); + while (dkm_rdtsc() - start < target) __asm__ volatile("pause"); +} + +/* 实机: 基于 TSC 的串口发送等待 (100us 超时). com_port = 0x3F8 (COM1). */ +static __inline__ void dkm_serial_wait_tx(u16 com_port) { + if (!dkm_tsc_per_ms) { + for (volatile u32 i = 0; i < 100000; i++) { + if (dkm_inb((u16)(com_port + 5)) & 0x20) break; + __asm__ volatile("pause"); + } + return; + } + u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms / 10; + while (dkm_rdtsc() < deadline) { + if (dkm_inb((u16)(com_port + 5)) & 0x20) break; + __asm__ volatile("pause"); + } +} + +#endif /* DKM_SHARED_H */ diff --git a/CODE/DKM/e1000/e1000.c b/CODE/DKM/e1000/e1000.c index c7dfddb..21eaa8e 100644 --- a/CODE/DKM/e1000/e1000.c +++ b/CODE/DKM/e1000/e1000.c @@ -3,98 +3,7 @@ * Finds e1000 via PCI, maps MMIO, reads MAC address. */ -#include - -#define DKM_DRIVER_MAGIC 0x444B4D31u -#define DKM_ABI_VERSION 1u - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; -typedef signed char i8; -typedef long long i64; -#define NULL ((void *)0) - -struct dkm_log_api { - void (*info)(const char *msg); - void (*warn)(const char *msg); - void (*error)(const char *msg); - void (*panic)(const char *msg); -}; - -struct dkm_net_scan_result { - char ssid[33]; - u8 bssid[6]; - u8 channel; - i8 rssi; - u8 security; -}; - -struct dkm_net_device_desc { - const char *name; - u8 mac[6]; - u32 flags; - void *ctx; - int (*tx)(void *ctx, const void *packet, u32 length); - int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length); - /* 无线扩展回调(有线驱动置 NULL,向后兼容)。 */ - int (*scan_start)(void *ctx); - int (*scan_count)(void *ctx); - int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); - int (*is_wireless)(void *ctx); -}; - -struct dkm_net_api { - int (*register_device)(const struct dkm_net_device_desc *desc); - u32 (*device_count)(void); - int (*device_info)(u32 index, void *out); - int (*tx)(u32 index, const void *packet, u32 length); - int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); - int (*scan_start)(u32 index); - int (*scan_count)(u32 index); - int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); - int (*is_wireless)(u32 index); -}; - -struct dkm_dma_buffer { - void *virt; - u64 phys; - u64 size; -}; - -struct dkm_dma_api { - int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out); -}; - -#define DKM_NET_F_LINK_UP (1u << 0) -#define DKM_NET_F_TX_READY (1u << 1) -#define DKM_NET_F_RX_READY (1u << 2) - -struct dkm_kernel_api { - u32 version; u32 size; u64 feature_bits; - const struct dkm_log_api *log; - const void *mem,*utsm,*irq_api,*pci_api; - const struct dkm_dma_api *dma; - const void *vfs_api; - const struct dkm_net_api *net; - const void *timer,*drr; - const void *rsdp_address,*fb_address; - u64 fb_width,fb_height,fb_pitch; u16 fb_bpp; - const void *boot_modules_response; - int (*irq_register)(u8 irq, void *handler); - u64 hhdm_offset; -}; - -struct dkm_driver_handle; -struct dkm_driver_desc { - u32 magic; u16 abi_version; u16 desc_size; - const char *name,*version,*vendor; - u32 driver_class,stage,flags,priority; - const char *const *depends; u32 depends_count; - const char *const *provides; u32 provides_count; - u64 min_kernel_abi,feature_bits,reserved0,reserved1; -}; +#include "../dkm_shared.h" static const char *const g_depends[] = {"pci","irq"}; static const char *const g_provides[] = {"netdev"}; @@ -108,16 +17,6 @@ const struct dkm_driver_desc driver_desc = { .provides=g_provides,.provides_count=1,.min_kernel_abi=1, }; -/* PCI config ports */ -#define PCI_ADDR 0xCF8 -#define PCI_DATA 0xCFC -#define PCI_VENDOR_ID 0x00 -#define PCI_DEVICE_ID 0x02 -#define PCI_COMMAND 0x04 -#define PCI_BAR0 0x10 -#define PCI_BAR1 0x14 -#define PCI_IRQ_LINE 0x3C - /* e1000 MMIO registers */ #define E1000_CTRL 0x0000 #define E1000_STATUS 0x0008 @@ -205,20 +104,20 @@ static u32 g_rx_tail; static u32 g_tx_tail; static int g_rings_ready; -static __inline__ void outl(u16 p, u32 v) { __asm__ volatile("outl %0,%1"::"a"(v),"Nd"(p):"memory"); } -static __inline__ u32 inl(u16 p) { u32 v; __asm__ volatile("inl %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } -static u8 inb(u16 p) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } -static void outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p):"memory"); } +#define COM1 0x3F8 + +/* 串口发送等待 thin wrapper — 调用 dkm 共享实现 */ +static void serial_wait_tx(void) { dkm_serial_wait_tx(COM1); } static void raw_log(const char *s) { while (*s) { char c = *s++; if (c == '\n') { - for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; - outb(0x3F8, '\r'); + serial_wait_tx(); + dkm_outb(COM1, '\r'); } - for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; - outb(0x3F8, (u8)c); + serial_wait_tx(); + dkm_outb(COM1, (u8)c); } } @@ -227,20 +126,18 @@ static void raw_hex(const char *prefix, u64 v) { raw_log(prefix); raw_log("0x"); for (int i=15; i>=0; i--) { - for (u32 wait=0; wait<100000; wait++) if (inb(0x3F8+5)&0x20) break; - outb(0x3F8, (u8)h[(v >> (i * 4)) & 0xf]); + serial_wait_tx(); + dkm_outb(COM1, (u8)h[(v >> (i * 4)) & 0xf]); } raw_log("\n"); } static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { - u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); - outl(PCI_ADDR,a); return inl(PCI_DATA); + return dkm_pci_read(bus, dev, func, reg); } static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) { - u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); - outl(PCI_ADDR,a); outl(PCI_DATA,v); + dkm_pci_write(bus, dev, func, reg, v); } static int e1000_find(u8 *bus, u8 *dev) { @@ -262,9 +159,12 @@ static int e1000_find(u8 *bus, u8 *dev) { static u16 e1000_eeprom_read(u16 addr) { /* EERD: bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位 */ g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u; - for (u32 i=0;i<100000;i++) { + /* 实机: 100ms TSC 超时 */ + u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100; + while (dkm_rdtsc() < deadline) { u32 v = g_mmio[E1000_EERD/4]; if (v & (1u<<4)) return (u16)(v >> 16); + __asm__ volatile("pause"); } return 0xffff; } @@ -382,7 +282,9 @@ static int e1000_tx(void *ctx, const void *packet, u32 length) { g_tx_tail = (index + 1) % E1000_TX_DESC_COUNT; g_mmio[E1000_TDT/4] = g_tx_tail; mmio_flush(); - for (u32 wait=0; wait<1000000; wait++) { + /* 实机: 1秒 TSC 超时等待 TX 完成 */ + u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000; + while (dkm_rdtsc() < deadline) { dma_fence(); if (g_tx_desc[index].status & E1000_TX_STA_DD) { raw_log("[e1000.raw] tx complete\n"); @@ -428,6 +330,8 @@ static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) __attribute__((visibility("default"))) int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) { + /* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */ + dkm_tsc_calibrate(); raw_log("[e1000.raw] driver_init entered\n"); (void)handle; if (!api||!api->log) return -1; @@ -472,8 +376,11 @@ int driver_init(const struct dkm_kernel_api *api, /* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */ g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | (1u << 26); - for (u32 wait=0; wait<1000000; wait++) { + /* 实机: 1秒 TSC 超时等待复位完成 */ + u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 1000; + while (dkm_rdtsc() < deadline) { if (!(g_mmio[E1000_CTRL/4] & (1u << 26))) break; + __asm__ volatile("pause"); } raw_log("[e1000.raw] reset done\n"); diff --git a/CODE/DKM/fat32/fat32.c b/CODE/DKM/fat32/fat32.c index 645ddbd..467c8dd 100644 --- a/CODE/DKM/fat32/fat32.c +++ b/CODE/DKM/fat32/fat32.c @@ -31,8 +31,10 @@ struct dkm_block_api { int (*register_device)(const void *desc); u32 (*device_count)(void); int (*read)(u32 index, u64 lba, u32 count, void *buffer); + int (*write)(u32 index, u64 lba, u32 count, const void *buffer); u64 (*sector_size)(u32 index); const char *(*device_name)(u32 index); + int (*set_write_fn)(u32 index, int (*fn)(void *ctx, u64 lba, u32 count, const void *buf)); }; struct dkm_kernel_api { diff --git a/CODE/DKM/virtio_net/virtio_net.c b/CODE/DKM/virtio_net/virtio_net.c index 1faf920..39be2b3 100644 --- a/CODE/DKM/virtio_net/virtio_net.c +++ b/CODE/DKM/virtio_net/virtio_net.c @@ -4,110 +4,23 @@ * it does not negotiate features, create virtqueues, or transmit packets. */ -#include +#include "../dkm_shared.h" -#define DKM_DRIVER_MAGIC 0x444B4D31u -#define DKM_ABI_VERSION 1u +/* virtio-specific PCI IDs and capability types (driver-local) */ +#define PCI_CLASS_REG 0x08 /* 32-bit class/subclass/prog_if/revision */ -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; -typedef signed char i8; -typedef long long i64; +#define PCI_VENDOR_VIRTIO 0x1AF4u +#define VIRTIO_TRANS_DEVICE_MIN 0x1000u +#define VIRTIO_TRANS_DEVICE_MAX 0x103Fu +#define VIRTIO_MODERN_DEVICE_MIN 0x1040u +#define VIRTIO_MODERN_DEVICE_MAX 0x107Fu +#define VIRTIO_NET_DEVICE_ID 1u -#define NULL ((void *)0) - -struct dkm_log_api { - void (*info)(const char *msg); - void (*warn)(const char *msg); - void (*error)(const char *msg); - void (*panic)(const char *msg); -}; - -struct dkm_net_scan_result { - char ssid[33]; - u8 bssid[6]; - u8 channel; - i8 rssi; - u8 security; -}; - -struct dkm_net_device_desc { - const char *name; - u8 mac[6]; - u32 flags; - void *ctx; - int (*tx)(void *ctx, const void *packet, u32 length); - int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length); - /* 无线扩展回调(有线驱动置 NULL,向后兼容)。 */ - int (*scan_start)(void *ctx); - int (*scan_count)(void *ctx); - int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); - int (*is_wireless)(void *ctx); -}; - -struct dkm_net_api { - int (*register_device)(const struct dkm_net_device_desc *desc); - u32 (*device_count)(void); - int (*device_info)(u32 index, void *out); - int (*tx)(u32 index, const void *packet, u32 length); - int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); - int (*scan_start)(u32 index); - int (*scan_count)(u32 index); - int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); - int (*is_wireless)(u32 index); -}; - -#define DKM_NET_F_LINK_UP (1u << 0) - -struct dkm_kernel_api { - u32 version; - u32 size; - u64 feature_bits; - const struct dkm_log_api *log; - const void *mem; - const void *utsm; - const void *irq; - const void *pci; - const void *dma; - const void *vfs; - const struct dkm_net_api *net; - const void *timer; - const void *drr; - const void *rsdp_address; - const void *fb_address; - u64 fb_width; - u64 fb_height; - u64 fb_pitch; - u16 fb_bpp; - const void *boot_modules_response; - int (*irq_register)(u8 irq, void *handler); - u64 hhdm_offset; -}; - -struct dkm_driver_handle; - -struct dkm_driver_desc { - u32 magic; - u16 abi_version; - u16 desc_size; - const char *name; - const char *version; - const char *vendor; - u32 driver_class; - u32 stage; - u32 flags; - u32 priority; - const char *const *depends; - u32 depends_count; - const char *const *provides; - u32 provides_count; - u64 min_kernel_abi; - u64 feature_bits; - u64 reserved0; - u64 reserved1; -}; +#define VIRTIO_PCI_CAP_COMMON_CFG 1u +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u +#define VIRTIO_PCI_CAP_ISR_CFG 3u +#define VIRTIO_PCI_CAP_DEVICE_CFG 4u +#define VIRTIO_PCI_CAP_PCI_CFG 5u static const char *const g_depends[] = { "pci", "irq" }; static const char *const g_provides[] = { "netdev" }; @@ -134,73 +47,18 @@ const struct dkm_driver_desc driver_desc = { .reserved1 = 0 }; -#define PCI_ADDR 0xCF8 -#define PCI_DATA 0xCFC -#define PCI_VENDOR_ID 0x00 -#define PCI_COMMAND 0x04 -#define PCI_CLASS_REG 0x08 -#define PCI_HEADER 0x0E -#define PCI_CAP_PTR 0x34 -#define PCI_IRQ_LINE 0x3C -#define PCI_BAR0 0x10 -#define PCI_BAR1 0x14 -#define PCI_BAR2 0x18 -#define PCI_BAR3 0x1C -#define PCI_BAR4 0x20 -#define PCI_BAR5 0x24 - -#define PCI_CMD_IO (1u << 0) -#define PCI_CMD_MEM (1u << 1) -#define PCI_CMD_BUSM (1u << 2) - -#define PCI_VENDOR_VIRTIO 0x1AF4u -#define VIRTIO_TRANS_DEVICE_MIN 0x1000u -#define VIRTIO_TRANS_DEVICE_MAX 0x103Fu -#define VIRTIO_MODERN_DEVICE_MIN 0x1040u -#define VIRTIO_MODERN_DEVICE_MAX 0x107Fu -#define VIRTIO_NET_DEVICE_ID 1u - -#define VIRTIO_PCI_CAP_COMMON_CFG 1u -#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u -#define VIRTIO_PCI_CAP_ISR_CFG 3u -#define VIRTIO_PCI_CAP_DEVICE_CFG 4u -#define VIRTIO_PCI_CAP_PCI_CFG 5u - static const struct dkm_log_api *g_log; -static __inline__ void outl(u16 port, u32 value) { - __asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port)); -} - -static __inline__ u32 inl(u16 port) { - u32 value; - __asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port)); - return value; -} - static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { - u32 addr = (1u << 31) - | ((u32)bus << 16) - | ((u32)dev << 11) - | ((u32)func << 8) - | ((u32)reg & 0xFC); - outl(PCI_ADDR, addr); - return inl(PCI_DATA); + return dkm_pci_read(bus, dev, func, reg); } static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) { - u32 addr = (1u << 31) - | ((u32)bus << 16) - | ((u32)dev << 11) - | ((u32)func << 8) - | ((u32)reg & 0xFC); - outl(PCI_ADDR, addr); - outl(PCI_DATA, value); + dkm_pci_write(bus, dev, func, reg, value); } static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) { - u32 v = pci_read(bus, dev, func, reg); - return (u8)((v >> ((reg & 3) * 8)) & 0xff); + return dkm_pci_read8(bus, dev, func, reg); } static void log_hex(const char *prefix, u64 value) { @@ -320,6 +178,28 @@ static void log_virtio_caps(u8 bus, u8 dev, u8 func) { } } +/* ---- tx/rx_poll stub: 安全占位, 不实际收发, 防止 netdev 调用 NULL ---- */ +static int virtio_net_tx(void *ctx, const void *packet, u32 length) { + (void)ctx; + (void)packet; + /* 仅记录日志, 避免每次发包刷屏: 仅记录长度 */ + static u32 s_tx_log_guard = 0; + if (s_tx_log_guard < 4) { + g_log->info("[virtio_net] tx stub: packet dropped (virtqueues not enabled)"); + log_hex("[virtio_net] tx stub len=", length); + s_tx_log_guard++; + } + return 0; +} + +static int virtio_net_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) { + (void)ctx; + (void)buffer; + (void)capacity; + if (out_length) *out_length = 0; + return 0; /* 无数据, virtqueues 未启用 */ +} + __attribute__((visibility("default"))) int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) { @@ -328,6 +208,9 @@ int driver_init(const struct dkm_kernel_api *api, if (!api || !api->log) return -1; g_log = api->log; + /* 实机要求: 在驱动初始化开头校准 TSC, 为后续时序提供准确计时 */ + dkm_tsc_calibrate(); + g_log->info("[virtio_net] init begin"); u8 bus = 0; @@ -379,8 +262,8 @@ int driver_init(const struct dkm_kernel_api *api, for (int i = 0; i < 6; i++) netdev.mac[i] = 0; netdev.flags = DKM_NET_F_LINK_UP; netdev.ctx = 0; - netdev.tx = 0; - netdev.rx_poll = 0; + netdev.tx = virtio_net_tx; /* stub: 安全占位, 不 NULL */ + netdev.rx_poll = virtio_net_rx_poll; int net_index = api->net->register_device(&netdev); log_hex("[virtio_net] netdev register rc=", (u64)(i64)net_index); } else { diff --git a/CODE/UTSM/arch/x86_64/serial.c b/CODE/UTSM/arch/x86_64/serial.c index ca4971b..f89b8f7 100644 --- a/CODE/UTSM/arch/x86_64/serial.c +++ b/CODE/UTSM/arch/x86_64/serial.c @@ -5,20 +5,56 @@ u8 inb(u16 port); #define COM1 0x3F8 +/* ---- TSC-based timing (实机要求: 用 CPU 频率计算, 不用循环) ---- */ +static u64 g_tsc_per_ms = 0; + +static inline u64 rdtsc_ser(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +/* 用 PIT (8254, 1.193182 MHz) 校准 TSC 频率 */ +static void tsc_calibrate_ser(void) { + outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */ + outb(0x40, 0x7c); /* 11932 low = ~10ms */ + outb(0x40, 0x2e); /* 11932 high */ + u64 tsc_start = rdtsc_ser(); + u16 prev = 0; u64 loops = 0; + for (;;) { + outb(0x43, 0x00); + u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = rdtsc_ser(); + g_tsc_per_ms = (tsc_end - tsc_start) / 10; +} + static int serial_ready(void) { return (inb(COM1 + 5) & 0x20) != 0; } +/* 实机: 基于 TSC 的 100ms 超时等待 */ static int serial_wait_ready(void) { - for (u64 spin = 0; spin < 1000000ULL; spin++) { - if (serial_ready()) { - return 1; + if (!g_tsc_per_ms) { + /* 兜底: 校准未完成时使用循环 */ + for (u64 spin = 0; spin < 1000000ULL; spin++) { + if (serial_ready()) return 1; } + return 0; + } + u64 deadline = rdtsc_ser() + g_tsc_per_ms * 100; /* 100ms 超时 */ + while (rdtsc_ser() < deadline) { + if (serial_ready()) return 1; + __asm__ volatile("pause"); } return 0; } void serial_init(void) { + /* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */ + tsc_calibrate_ser(); outb(COM1 + 1, 0x00); outb(COM1 + 3, 0x80); outb(COM1 + 0, 0x03); diff --git a/CODE/UTSM/arch/x86_64/serial.o b/CODE/UTSM/arch/x86_64/serial.o index b25f5f8..afeb06b 100644 Binary files a/CODE/UTSM/arch/x86_64/serial.o and b/CODE/UTSM/arch/x86_64/serial.o differ diff --git a/CODE/UTSM/core/process.o b/CODE/UTSM/core/process.o index 64a7b02..2528390 100644 Binary files a/CODE/UTSM/core/process.o and b/CODE/UTSM/core/process.o differ diff --git a/CODE/UTSM/core/utsm_init.c b/CODE/UTSM/core/utsm_init.c index 85c7a65..50a2482 100644 --- a/CODE/UTSM/core/utsm_init.c +++ b/CODE/UTSM/core/utsm_init.c @@ -1,6 +1,7 @@ #include #include #include +#include #include static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS]; @@ -18,6 +19,8 @@ void utsm_init(void) { log_info("[UTSM] pckc init begin"); utsm_pckc_init(); log_info("[UTSM] pckc init ok"); + + utsm_process_init(); } utsm_segment_desc *utsm_get_segment(u32 slot) { diff --git a/CODE/UTSM/core/utsm_init.o b/CODE/UTSM/core/utsm_init.o index b5b256f..d900d30 100644 Binary files a/CODE/UTSM/core/utsm_init.o and b/CODE/UTSM/core/utsm_init.o differ diff --git a/CODE/UTSM/dkm/boot_modules.c b/CODE/UTSM/dkm/boot_modules.c index 9ec951a..85631d2 100644 --- a/CODE/UTSM/dkm/boot_modules.c +++ b/CODE/UTSM/dkm/boot_modules.c @@ -248,6 +248,9 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan const elf64_ehdr *ehdr = (const elf64_ehdr *)address; const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff); u64 image_offset[64]; + /* Zero the entire array so non-ALLOC section indices yield offset 0 + * instead of stack garbage. */ + for (int zi = 0; zi < 64; zi++) image_offset[zi] = 0; u64 total_size = 0; u64 max_align = 16; u16 alloc_count = 0; @@ -353,7 +356,9 @@ int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan return -1; } - if (rela->offset + 8 <= target->size) { + /* Bounds check: use the actual write width, not always 8. */ + u32 reloc_width = (reloc_type == R_X86_64_64) ? 8 : 4; + if (rela->offset + reloc_width <= target->size) { if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S || reloc_type == R_X86_64_PC32) { *(u32 *)(target_base + rela->offset) = (u32)patch; } else { diff --git a/CODE/UTSM/dkm/boot_modules.o b/CODE/UTSM/dkm/boot_modules.o index fa70a40..ab9b984 100644 Binary files a/CODE/UTSM/dkm/boot_modules.o and b/CODE/UTSM/dkm/boot_modules.o differ diff --git a/CODE/UTSM/drr/drr_stub.o b/CODE/UTSM/drr/drr_stub.o index 515d549..9745c47 100644 Binary files a/CODE/UTSM/drr/drr_stub.o and b/CODE/UTSM/drr/drr_stub.o differ diff --git a/CODE/UTSM/include/utsm/block.h b/CODE/UTSM/include/utsm/block.h index 938552a..803fe16 100644 --- a/CODE/UTSM/include/utsm/block.h +++ b/CODE/UTSM/include/utsm/block.h @@ -6,6 +6,7 @@ #define DKM_BLOCK_MAX_DEVICES 8u typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer); +typedef int (*dkm_block_write_fn)(void *ctx, u64 lba, u32 count, const void *buffer); typedef struct dkm_block_device_desc { const char *name; @@ -13,14 +14,17 @@ typedef struct dkm_block_device_desc { u64 sector_count; void *ctx; dkm_block_read_fn read; + dkm_block_write_fn write; } dkm_block_device_desc; typedef struct dkm_block_api { int (*register_device)(const dkm_block_device_desc *desc); u32 (*device_count)(void); int (*read)(u32 index, u64 lba, u32 count, void *buffer); + int (*write)(u32 index, u64 lba, u32 count, const void *buffer); u64 (*sector_size)(u32 index); const char *(*device_name)(u32 index); + int (*set_write_fn)(u32 index, dkm_block_write_fn fn); } dkm_block_api; void block_init(void); diff --git a/CODE/UTSM/include/utsm/deshab_ui.h b/CODE/UTSM/include/utsm/deshab_ui.h index ba60b97..fc39341 100644 --- a/CODE/UTSM/include/utsm/deshab_ui.h +++ b/CODE/UTSM/include/utsm/deshab_ui.h @@ -195,12 +195,12 @@ typedef long long du_i64; #define DU_ANIM_DELAY_NORMAL 100000u /* 标准动画 */ #define DU_ANIM_DELAY_FAST 40000u /* 快速反馈 */ -/* 弧线动画参数(DSK spinner 专用) */ +/* 弧线动画参数(DSK spinner 专用 — Sealed Arc Comet 设计) */ #define DU_SPINNER_RADIUS 48u -#define DU_SPINNER_THICKNESS 4u -#define DU_SPINNER_ARC_GAP 82u /* 每弧宽度(度) */ -#define DU_SPINNER_ARC_COUNT 4u -#define DU_SPINNER_SPEED 2u /* 每帧旋转度数 */ +#define DU_SPINNER_THICKNESS 5u +#define DU_SPINNER_HEAD_WIDTH 40u /* 弧头宽度(度) */ +#define DU_SPINNER_TAIL_LEN 220u /* 拖尾长度(度) */ +#define DU_SPINNER_SPEED 4u /* 每帧旋转度数 */ #define DU_SPINNER_SPRITE 128u /* 精灵缓冲边长 */ /* 渐变动画步进 */ @@ -673,12 +673,13 @@ static inline void du_draw_progress(du_context *ctx, /* =================================================================== * Spinner(加载弧) — DSK 启动加载动画 * - * 使用 "Sealed Arc" 四弧旋转设计,弧色取自色板。 + * 使用 "Sealed Arc Comet" 单弧彗星尾设计: + * 一个明亮的弧头 + 渐变拖尾,颜色从弧头色过渡到拖尾色。 * =================================================================== */ -/* 默认弧色(从亮到暗的青蓝色) */ +/* 默认弧色:[0]=弧头色(亮蓝), [1]=拖尾色(紫) */ #define DU_SPINNER_ARCS { \ - DP_SEAL_100, DP_SEAL_300, DP_SEAL_500, DP_SEAL_700 \ + DP_SEAL_300, DP_SEAL_700, DP_SEAL_500, DP_SEAL_100 \ } /* =================================================================== diff --git a/CODE/UTSM/kernel/block.c b/CODE/UTSM/kernel/block.c index 5e36527..f118f25 100644 --- a/CODE/UTSM/kernel/block.c +++ b/CODE/UTSM/kernel/block.c @@ -35,6 +35,13 @@ static int block_read_impl(u32 index, u64 lba, u32 count, void *buffer) { return dev->read(dev->ctx, lba, count, buffer); } +static int block_write_impl(u32 index, u64 lba, u32 count, const void *buffer) { + if (index >= g_device_count || !buffer || count == 0) return -1; + dkm_block_device_desc *dev = &g_devices[index]; + if (!dev->write) return -2; + return dev->write(dev->ctx, lba, count, buffer); +} + static u64 block_sector_size_impl(u32 index) { if (index >= g_device_count) return 0; return g_devices[index].sector_size; @@ -45,12 +52,20 @@ static const char *block_device_name_impl(u32 index) { return g_devices[index].name; } +static int block_set_write_fn_impl(u32 index, dkm_block_write_fn fn) { + if (index >= g_device_count) return -1; + g_devices[index].write = fn; + return 0; +} + static const dkm_block_api g_block_api = { .register_device = block_register_device_impl, .device_count = block_device_count_impl, .read = block_read_impl, + .write = block_write_impl, .sector_size = block_sector_size_impl, - .device_name = block_device_name_impl + .device_name = block_device_name_impl, + .set_write_fn = block_set_write_fn_impl }; const dkm_block_api *block_get_api(void) { diff --git a/CODE/UTSM/kernel/block.o b/CODE/UTSM/kernel/block.o index fc324ad..41f6639 100644 Binary files a/CODE/UTSM/kernel/block.o and b/CODE/UTSM/kernel/block.o differ diff --git a/CODE/UTSM/kernel/dsk_loader.c b/CODE/UTSM/kernel/dsk_loader.c index ff6ea0f..837adec 100644 --- a/CODE/UTSM/kernel/dsk_loader.c +++ b/CODE/UTSM/kernel/dsk_loader.c @@ -152,7 +152,8 @@ static struct limine_file *dsk_find_module(void) { /* ---- FAT32 block-provider file reader ---- */ static u8 g_dsk_fat32_disk[65536]; /* 128-sector BPB+FAT+root dir buffer */ static u8 g_dsk_fat32_cluster[4096]; /* 8-sector cluster buffer */ -static u8 g_dsk_fat32_filedata[65536]; /* file data buffer */ +/* 实机要求: 文件缓冲区足够大容纳 deshab.elf (~1.5MB) */ +static u8 g_dsk_fat32_filedata[2097152]; /* 2MB file data buffer */ static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) { const dkm_kernel_api *api = dkm_get_kernel_api(); diff --git a/CODE/UTSM/kernel/dsk_loader.o b/CODE/UTSM/kernel/dsk_loader.o index 12e1f0a..7beab38 100644 Binary files a/CODE/UTSM/kernel/dsk_loader.o and b/CODE/UTSM/kernel/dsk_loader.o differ diff --git a/CODE/UTSM/kernel/main.o b/CODE/UTSM/kernel/main.o index 88fb8e0..84e8746 100644 Binary files a/CODE/UTSM/kernel/main.o and b/CODE/UTSM/kernel/main.o differ diff --git a/CODE/UTSM/linker.ld b/CODE/UTSM/linker.ld index a021a3f..2aede7b 100644 --- a/CODE/UTSM/linker.ld +++ b/CODE/UTSM/linker.ld @@ -25,6 +25,9 @@ SECTIONS .data : ALIGN(4K) { *(.data*) *(COMMON) + } :data + + .bss : ALIGN(4K) { *(.bss*) } :data } diff --git a/CODE/UTSM/test/utsm_selftest.c b/CODE/UTSM/test/utsm_selftest.c index beec582..ae57660 100644 --- a/CODE/UTSM/test/utsm_selftest.c +++ b/CODE/UTSM/test/utsm_selftest.c @@ -1,16 +1,25 @@ #include #include #include +#include int memcmp(const void *a, const void *b, usize len); usize strlen(const char *s); int utsm_selftest_run(void) { - utsm_process_context process; - utsm_process_create_test(&process, 1); + log_info("[UTSM] selftest enter"); + + /* utsm_process_context is ~8KB (256 capabilities × 32 bytes). + * Allocate from arena instead of stack to avoid kernel stack overflow. */ + utsm_process_context *process = (utsm_process_context *)kmem_alloc(sizeof(utsm_process_context)); + if (!process) { + log_error("[UTSM] selftest: failed to allocate process context"); + return UTSM_ERR_NO_MEMORY; + } + utsm_process_create_test(process, 1); utsm_capability cap; - int status = utsm_create_segment(&process, UTSM_PAGE_SIZE, UTSM_SEG_F_READ | UTSM_SEG_F_WRITE | UTSM_SEG_F_STRONG_RECOVERY, &cap); + int status = utsm_create_segment(process, UTSM_PAGE_SIZE, UTSM_SEG_F_READ | UTSM_SEG_F_WRITE | UTSM_SEG_F_STRONG_RECOVERY, &cap); if (status != UTSM_OK) { log_error("[UTSM] selftest create segment failed"); return status; diff --git a/CODE/UTSM/test/utsm_selftest.o b/CODE/UTSM/test/utsm_selftest.o index 9242949..6693997 100644 Binary files a/CODE/UTSM/test/utsm_selftest.o and b/CODE/UTSM/test/utsm_selftest.o differ diff --git a/CODE/desktop/linker.ld b/CODE/desktop/linker.ld index 94dddf7..473e5fc 100644 --- a/CODE/desktop/linker.ld +++ b/CODE/desktop/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(7); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(7); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/desktop/main.c b/CODE/desktop/main.c index d938fd2..5e25b72 100644 --- a/CODE/desktop/main.c +++ b/CODE/desktop/main.c @@ -32,6 +32,10 @@ static void sputc(char c) { static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } } static void slog(const char *s) { swrite("[desktop] "); swrite(s); swrite("\n"); } +/* 前向声明 */ +static void redraw_all(void); +static int ps2_mouse_poll(void); + /* ============================================================ * 常量 * ============================================================ */ @@ -184,15 +188,15 @@ static const u8 cursor_shape[24][24] = { {1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, @@ -308,6 +312,22 @@ static void cursor_draw(int mx, int my) { } } +/* 非阻塞延时:动画期间持续轮询鼠标,保持光标响应。 + * 将 ms 个 50000-iter pause 块拆开,每块结束后轮询一次鼠标。 + * 若鼠标有新数据包,立即擦除旧光标、在新位置保存背景并重绘光标。 */ +static void anim_delay_poll_mouse(u32 ms) { + for (u32 i = 0; i < ms; i++) { + for (volatile u32 j = 0; j < 50000; j++) { + __asm__ volatile("pause"); + } + if (ps2_mouse_poll()) { + cursor_restore_bg(); + cursor_save_bg(g_mouse_x, g_mouse_y); + cursor_draw(g_mouse_x, g_mouse_y); + } + } +} + /* ============================================================ * 窗口管理 * ============================================================ */ @@ -372,6 +392,28 @@ static void win_destroy(desktop_window *w) { if (g_windows[i].id == w->id) { idx = i; break; } } if (idx < 0) return; + + /* Window close animation - shrinking rectangle */ + { + int wx = w->x, wy = w->y, ww = w->w, wh = w->h; + w->visible = 0; + for (int step = 5; step >= 0; step--) { + int scale = (step + 1) * 100 / 6; + int aw = ww * scale / 100; + int ah = wh * scale / 100; + int ax = wx + (ww - aw) / 2; + int ay = wy + (wh - ah) / 2; + cursor_restore_bg(); + redraw_all(); + /* redraw_all 末尾已绘制光标,这里先擦除以保证保存的背景干净(不含光标像素) */ + cursor_restore_bg(); + du_rect_outline(&g_fb, ax, ay, aw, ah, DS_DARK_ACCENT, DU_RADIUS_SM); + cursor_save_bg(g_mouse_x, g_mouse_y); + cursor_draw(g_mouse_x, g_mouse_y); + anim_delay_poll_mouse(16); + } + } + /* 调用应用的 on_destroy */ if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_destroy && w->app_state) { g_apps[w->app_id].on_destroy(w->app_state); @@ -474,7 +516,20 @@ static void draw_taskbar(void) { if (w->minimized) continue; u32 btn_bg = w->focused ? DS_DARK_ACCENT : DP_ABYSS_600; du_fill_rounded_rect(&g_fb, wx, tb_y + 4, 100, 32, btn_bg, DU_RADIUS_SM); - du_draw_string(&g_fb, w->title, wx + 4, tb_y + 8, + /* Truncate title to fit button width (100px - 8px padding = ~11 chars) */ + char truncated[16]; + int max_chars = 11; + int tlen = 0; + while (w->title[tlen] && tlen < max_chars) { truncated[tlen] = w->title[tlen]; tlen++; } + if (tlen == max_chars && w->title[tlen]) { + /* Add ellipsis */ + if (tlen > 8) tlen = 8; + truncated[tlen++] = '.'; + truncated[tlen++] = '.'; + truncated[tlen++] = '.'; + } + truncated[tlen] = 0; + du_draw_string(&g_fb, truncated, wx + 4, tb_y + 8, DS_DARK_TEXT_PRIMARY, btn_bg, DU_ASCII_STEP); wx += 104; if (wx > (int)g_fb_w - 200) break; @@ -507,7 +562,7 @@ static void draw_taskbar(void) { static void draw_desktop_icons(void) { for (int i = 0; i < g_icon_count; i++) { desktop_icon *ic = &g_icons[i]; - int cx = ic->x; + int cx = ic->x + (DESKTOP_ICON_W - 32) / 2; /* center icon in slot */ int cy = ic->y; /* 根据应用 ID 画不同图标 */ switch (ic->app_id) { @@ -520,8 +575,8 @@ static void draw_desktop_icons(void) { /* 标签文字 */ int label_len = 0; while (ic->label[label_len]) label_len++; - int label_x = cx + (32 - label_len * (int)DU_ASCII_STEP) / 2; - if (label_x < cx) label_x = cx; + int label_x = ic->x + (DESKTOP_ICON_W - label_len * (int)DU_ASCII_STEP) / 2; + if (label_x < ic->x) label_x = ic->x; du_draw_string(&g_fb, ic->label, label_x, cy + 36, DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP); } @@ -531,43 +586,94 @@ static void draw_desktop_icons(void) { * PS/2 鼠标处理 * ============================================================ */ +/* PS/2 控制器等待 — 输入缓冲区空(可写) */ +static int ps2_wait_write(void) { + for (int t = 0; t < 200000; t++) { + if (!(inb(0x64) & 0x02)) return 0; + __asm__ volatile("pause"); + } + return -1; +} + +/* PS/2 控制器等待 — 输出缓冲区满(可读) */ +static int ps2_wait_read(void) { + for (int t = 0; t < 200000; t++) { + if (inb(0x64) & 0x01) return 0; + __asm__ volatile("pause"); + } + return -1; +} + +/* 排干输出缓冲区中所有残留数据(带超时) */ +static void ps2_drain(void) { + for (int i = 0; i < 16; i++) { + if (!(inb(0x64) & 0x01)) break; + inb(0x60); + } +} + static void ps2_mouse_init(void) { - /* 启用 AUX 设备 */ - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + /* 1. 刷新固件/上一阶段留下的残留数据 */ + ps2_drain(); + + /* 2. 启用 AUX(鼠标)端口 */ + ps2_wait_write(); outb(0x64, 0xA8); - /* 启用 AUX IRQ */ - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + + /* 3. 读取控制器配置字节 */ + ps2_wait_write(); outb(0x64, 0x20); - for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; } + ps2_wait_read(); u8 cfg = inb(0x60); - cfg |= 0x02; /* enable AUX IRQ12 */ + + /* 4. 设置配置:bit5=0(启用 AUX 时钟), bit1=1(启用 AUX IRQ12), bit6=1(Set2→Set1 转换) */ cfg &= ~0x20; /* enable AUX clock */ - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + cfg |= 0x02; /* enable AUX IRQ12 */ + cfg |= 0x40; /* enable Set2→Set1 translation */ + + /* 5. 写回控制器配置 */ + ps2_wait_write(); outb(0x64, 0x60); - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + ps2_wait_write(); outb(0x60, cfg); - /* 复位鼠标 */ - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + + /* 6. 复位鼠标 (0xFF) — 响应为 ACK(0xFA) + 自检(0xAA) + 设备ID(0x00),共 3 字节 */ + ps2_wait_write(); outb(0x64, 0xD4); - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + ps2_wait_write(); outb(0x60, 0xFF); - for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; } - inb(0x60); /* ACK */ - /* 启用 streaming */ - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + /* 排干所有复位响应字节(最多 8 字节,带超时) */ + for (int i = 0; i < 8; i++) { + if (ps2_wait_read() != 0) break; + inb(0x60); + } + + /* 7. 启用数据流模式 (0xF4) — 响应为 ACK(0xFA) */ + ps2_wait_write(); outb(0x64, 0xD4); - for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; } + ps2_wait_write(); outb(0x60, 0xF4); - for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; } - inb(0x60); /* ACK */ + /* 排干 ACK */ + for (int i = 0; i < 4; i++) { + if (ps2_wait_read() != 0) break; + inb(0x60); + } + + /* 8. 最终刷新:丢弃任何杂散字节,确保数据包从干净状态开始 */ + ps2_drain(); + + /* 重置鼠标数据包解析状态 */ + g_mouse_idx = 0; + g_mouse_has_pkt = 0; + slog("mouse init ok"); } static int ps2_mouse_poll(void) { u8 st = inb(0x64); if (!(st & 1)) return 0; + if (!(st & 0x20)) return 0; /* 不是鼠标数据(键盘数据留给键盘处理) */ u8 data = inb(0x60); - if (!(st & 0x20)) return 0; /* 不是鼠标数据 */ g_mouse_buf[g_mouse_idx++] = data; if (g_mouse_idx < 3) return 0; @@ -578,11 +684,8 @@ static int ps2_mouse_poll(void) { int dx = (int)(i8)g_mouse_buf[1]; int dy = (int)(i8)g_mouse_buf[2]; - if (g_mouse_buf[0] & 0x10) dx = -dx; /* X sign */ - /* 注意:Y 轴方向(鼠标 Y 向上为正,屏幕 Y 向下为正) */ - if (!(g_mouse_buf[0] & 0x20)) dy = -dy; /* Y sign */ - else dy = -dy; - dy = -dy; /* 鼠标 Y 翻转 */ + /* (i8) 转换已处理符号位,无需再检查 sign bits */ + dy = -dy; /* PS/2 Y 向上为正,屏幕 Y 向下为正,需翻转 */ g_mouse_x += dx; g_mouse_y += dy; @@ -591,7 +694,10 @@ static int ps2_mouse_poll(void) { if (g_mouse_x >= (int)g_fb_w - CURSOR_SIZE) g_mouse_x = (int)g_fb_w - CURSOR_SIZE; if (g_mouse_y >= (int)g_fb_h - CURSOR_SIZE) g_mouse_y = (int)g_fb_h - CURSOR_SIZE; + int old_btn = g_mouse_btn; g_mouse_btn = (g_mouse_buf[0] & 0x01) ? 1 : 0; + /* Only signal redraw if something visual changed */ + if (dx == 0 && dy == 0 && g_mouse_btn == old_btn) return 0; g_mouse_has_pkt = 1; return 1; } @@ -1210,6 +1316,24 @@ static void launch_app(int app_id) { win->app_state = app->on_create(&ctx); win->dirty = 1; + + /* Window open animation - expanding rectangle */ + for (int step = 0; step < 6; step++) { + int scale = (step + 1) * 100 / 6; + int aw = win->w * scale / 100; + int ah = win->h * scale / 100; + int ax = win->x + (win->w - aw) / 2; + int ay = win->y + (win->h - ah) / 2; + cursor_restore_bg(); + redraw_all(); + /* redraw_all 末尾已绘制光标,这里先擦除以保证保存的背景干净(不含光标像素) */ + cursor_restore_bg(); + du_rect_outline(&g_fb, ax, ay, aw, ah, DS_DARK_ACCENT, DU_RADIUS_SM); + cursor_save_bg(g_mouse_x, g_mouse_y); + cursor_draw(g_mouse_x, g_mouse_y); + anim_delay_poll_mouse(16); + } + slog("app launched"); } @@ -1229,13 +1353,16 @@ static void redraw_all(void) { desktop_window *w = &g_windows[i]; if (w->minimized || !w->visible) continue; + /* 窗口装饰(先画框架作为窗口背景:主体、标题栏、边框) */ + draw_window_frame(w); + /* 窗口客户区背景 */ int cx = w->x + BORDER_W; int cy = w->y + TITLEBAR_H; int cw = w->w - 2 * BORDER_W; int ch = w->h - TITLEBAR_H - BORDER_W; - /* 调用应用绘制 */ + /* 调用应用绘制(在框架之上绘制客户区内容) */ if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_draw && w->app_state) { app_ctx ctx; ctx.fb = &g_fb; @@ -1247,9 +1374,6 @@ static void redraw_all(void) { ctx.kernel_api = g_kernel_api; g_apps[w->app_id].on_draw(w->app_state, &ctx); } - - /* 窗口装饰(在应用内容之上) */ - draw_window_frame(w); } /* 任务栏 */ @@ -1301,18 +1425,17 @@ void dsk_entry(const dsk_boot_context *ctx) { /* 轮询 PS/2 鼠标 */ if (ps2_mouse_poll()) { - need_redraw = 1; - - /* 拖拽处理 */ + /* 拖拽处理 - 需要全屏重绘 */ if (g_dragging && g_drag_win >= 0) { desktop_window *w = win_find(g_drag_win); if (w) { w->x = g_mouse_x - g_drag_off_x; w->y = g_mouse_y - g_drag_off_y; } + need_redraw = 1; } - /* 鼠标点击事件 */ + /* 鼠标点击事件 - 需要全屏重绘 */ if (g_mouse_btn) { /* 检查任务栏点击 */ int tb_y = (int)g_fb_h - TASKBAR_H; @@ -1431,6 +1554,11 @@ next: if (need_redraw) { cursor_restore_bg(); redraw_all(); + } else if (g_mouse_has_pkt) { + /* 鼠标移动但无需全屏重绘:只更新光标位置 */ + cursor_restore_bg(); + cursor_save_bg(g_mouse_x, g_mouse_y); + cursor_draw(g_mouse_x, g_mouse_y); } __asm__("pause"); diff --git a/CODE/dsk/MAKEFILE b/CODE/dsk/MAKEFILE index 164a6cc..762b8c0 100644 --- a/CODE/dsk/MAKEFILE +++ b/CODE/dsk/MAKEFILE @@ -7,7 +7,10 @@ OUT := $(ROOT)/SYSTEM/system/deshab64/deshab.elf CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2 LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld -C_SOURCES := $(wildcard *.c) +# logo_data.c is #included directly into main.c (the DSK PIE loader only +# handles R_X86_64_RELATIVE, so the logo array must be file-local to main.o +# rather than a cross-unit symbol). Exclude it from standalone compilation. +C_SOURCES := $(filter-out logo_data.c, $(wildcard *.c)) OBJECTS := $(C_SOURCES:.c=.o) .PHONY: all clean diff --git a/CODE/dsk/linker.ld b/CODE/dsk/linker.ld index 78c6294..473e5fc 100644 --- a/CODE/dsk/linker.ld +++ b/CODE/dsk/linker.ld @@ -2,26 +2,23 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(5); /* R-X */ - rodata PT_LOAD FLAGS(4); /* R-- */ - data PT_LOAD FLAGS(6); /* RW- */ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS { . = 0x1000; - - .text : ALIGN(4K) { - *(.text*) - } :text - - .rodata : ALIGN(4K) { - *(.rodata*) - } :rodata - + .text : ALIGN(4K) { *(.text*) } :text + .rodata : ALIGN(4K) { *(.rodata*) } :rodata .data : ALIGN(4K) { *(.data*) + *(.data.rel.ro*) *(COMMON) *(.bss*) } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/dsk/main.c b/CODE/dsk/main.c index 9eac4bb..7c6b497 100644 --- a/CODE/dsk/main.c +++ b/CODE/dsk/main.c @@ -12,6 +12,7 @@ typedef unsigned long long u64; typedef long long i64; typedef int (*block_read_fn)(u32 index, u64 lba, u32 count, void *buf); +typedef int (*block_write_fn)(u32 index, u64 lba, u32 count, const void *buf); static void sputc(char c) { for(unsigned i=0;i<100000;i++){if(inb(COM1+5)&0x20)break;} outb(COM1,(u8)c); } static void swrite(const char *s) { while(*s){if(*s=='\n')sputc('\r');sputc(*s++);} } @@ -61,11 +62,38 @@ typedef struct { u64 offset; u64 info; i64 addend; } elf64_rela; static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_p; static block_read_fn g_block_read; +static block_write_fn g_block_write; +static u64 g_tsc_per_ms = 0; static u8 g_disk[131072]; static u8 g_cluster[4096]; static u8 g_fdata[262144]; static int fat32_read_sectors(u32 lba, u32 count, u8 *out) { return g_block_read ? g_block_read(0,lba,count,out) : -1; } +static int fat32_write_sectors(u32 lba, u32 count, const u8 *buf) { return g_block_write ? g_block_write(0,lba,count,buf) : -1; } + +/* ---- TSC-based timing ---- */ +static u64 rdtsc_dsk(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +static void tsc_calibrate_dsk(void) { + outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */ + outb(0x40, 0x7c); /* 11932 low = ~10ms */ + outb(0x40, 0x2e); /* 11932 high */ + u64 tsc_start = rdtsc_dsk(); + u16 prev = 0; u64 loops = 0; + for (;;) { + outb(0x43, 0x00); + u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = rdtsc_dsk(); + g_tsc_per_ms = (tsc_end - tsc_start) / 10; + logh("[DSK] tsc_per_ms=", g_tsc_per_ms); +} static u32 blend_color(u32 c1, u32 c2, u32 a) { u32 na = 256 - a; @@ -96,6 +124,81 @@ static void dsk_fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) { } } +/* ---- Spinner animation: comet-tail arc, clockwise, double-buffered ---- */ +/* sin×127 lookup table, 256 entries (1.40625° each). cos(d) = sin(d+64). */ +static const i8 g_sin_tab[256] = { + 0,3,6,9,12,16,19,22,25,28,31,34,37,40,43,46, + 49,51,54,57,60,63,65,68,71,73,76,78,81,83,85,88, + 90,92,94,96,98,100,102,104,106,107,109,111,112,113,115,116, + 117,118,120,121,122,122,123,124,125,125,126,126,126,127,127,127, + 127,127,127,127,126,126,126,125,125,124,123,122,122,121,120,118, + 117,116,115,113,112,111,109,107,106,104,102,100,98,96,94,92, + 90,88,85,83,81,78,76,73,71,68,65,63,60,57,54,51, + 49,46,43,40,37,34,31,28,25,22,19,16,12,9,6,3, + 0,-3,-6,-9,-12,-16,-19,-22,-25,-28,-31,-34,-37,-40,-43,-46, + -49,-51,-54,-57,-60,-63,-65,-68,-71,-73,-76,-78,-81,-83,-85,-88, + -90,-92,-94,-96,-98,-100,-102,-104,-106,-107,-109,-111,-112,-113,-115,-116, + -117,-118,-120,-121,-122,-122,-123,-124,-125,-125,-126,-126,-126,-127,-127,-127, + -127,-127,-127,-127,-126,-126,-126,-125,-125,-124,-123,-122,-122,-121,-120,-118, + -117,-116,-115,-113,-112,-111,-109,-107,-106,-104,-102,-100,-98,-96,-94,-92, + -90,-88,-85,-83,-81,-78,-76,-73,-71,-68,-65,-63,-60,-57,-54,-51, + -49,-46,-43,-40,-37,-34,-31,-28,-25,-22,-19,-16,-12,-9,-6,-3, +}; +static i64 dsk_isin(u32 deg) { return (i64)g_sin_tab[deg & 255]; } +static i64 dsk_icos(u32 deg) { return (i64)g_sin_tab[(deg + 64) & 255]; } + +#define SPIN_R 64 +#define SPIN_ARC 110 /* comet-tail span (of 256) ≈ 155° */ +#define SPIN_THICK 3 /* half-thickness of arc band */ +#define SPIN_BOX (SPIN_R + SPIN_THICK + 2) +static u32 g_spin_bg[(SPIN_BOX*2+1) * (SPIN_BOX*2+1)]; + +static void spinner_save_bg(u32 *fb, i64 cx, i64 cy) { + for (i64 dy = -SPIN_BOX; dy <= SPIN_BOX; dy++) { + for (i64 dx = -SPIN_BOX; dx <= SPIN_BOX; dx++) { + i64 x = cx + dx, y = cy + dy; + u32 px = (x >= 0 && (u64)x < g_fb_w && y >= 0 && (u64)y < g_fb_h) + ? ((u32*)((u8*)fb + (u64)y * g_fb_p))[(u64)x] + : dsk_bg_at_y(y); + g_spin_bg[(dy + SPIN_BOX) * (SPIN_BOX*2+1) + (dx + SPIN_BOX)] = px; + } + } +} + +static void spinner_restore_bg(u32 *fb, i64 cx, i64 cy) { + for (i64 dy = -SPIN_BOX; dy <= SPIN_BOX; dy++) { + i64 y = cy + dy; + if (y < 0 || (u64)y >= g_fb_h) continue; + u32 *line = (u32*)((u8*)fb + (u64)y * g_fb_p); + for (i64 dx = -SPIN_BOX; dx <= SPIN_BOX; dx++) { + i64 x = cx + dx; + if (x < 0 || (u64)x >= g_fb_w) continue; + line[(u64)x] = g_spin_bg[(dy + SPIN_BOX) * (SPIN_BOX*2+1) + (dx + SPIN_BOX)]; + } + } +} + +/* Draw comet-tail arc: head at (base+SPIN_ARC) brightest, tail at base fading out. + * Clockwise rotation by advancing `base`. Double-buffered via save/restore. */ +static void spinner_draw(u32 *fb, i64 cx, i64 cy, u32 base, u32 head_color) { + u32 bg_ref = dsk_bg_at_y(cy); + for (u32 d = 0; d < SPIN_ARC; d++) { + u32 ang = (base + d) & 255; + i64 ic = dsk_icos(ang), is = dsk_isin(ang); + u32 alpha = ((d + 1) * 255) / SPIN_ARC; /* tail→head: 0→255 */ + u32 color = blend_color(bg_ref, head_color, alpha); + for (i64 t = -SPIN_THICK; t <= SPIN_THICK; t++) { + i64 r = SPIN_R + t; + if (r <= 0) continue; + i64 x = cx + (r * ic) / 127; + i64 y = cy + (r * is) / 127; + if (x < 0 || (u64)x >= g_fb_w || y < 0 || (u64)y >= g_fb_h) continue; + u32 *line = (u32*)((u8*)fb + (u64)y * g_fb_p); + line[(u64)x] = color; + } + } +} + static int fat32_find_entry(const u8 *clus, u32 clus_sectors, const char *target, u32 *out_clus, u32 *out_size) { const fat32_de *dir = (const fat32_de *)clus; for (u32 e=0; e*32 < clus_sectors*512; e++) { @@ -174,17 +277,238 @@ static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size return 0; } -static int dsk_check_firstinit(int *is_first) { +/* Write a file to the FAT32 root directory by 11-char 8.3 name. + * If the file exists, its content is replaced. If not, a new entry is created. + * Uses g_disk (256-sector buffer) which must already contain the BPB+FAT+root. + * Returns 0 on success. */ +static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) { + u8 *disk = g_disk; + const fat32_bpb *bpb = (const fat32_bpb *)disk; + if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return -1; + u32 spc = bpb->spc; + u32 fat_lba = bpb->rsvd; + u32 fat_sectors = bpb->spf; + u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; + u32 fat_byte_off = bpb->rsvd * 512; + u32 root_clus = bpb->root_clus; + u32 cluster_bytes = spc * 512; + + /* 1. Re-read BPB+FAT to ensure g_disk is fresh */ + if (fat32_read_sectors(0, 256, disk) != 0) return -2; + + /* 2. Find file entry in root directory */ + u32 root_lba = data_lba + (root_clus - 2) * spc; + u8 *root_buf = disk + (u64)root_lba * 512; + u32 max_entries = cluster_bytes / 32; + fat32_de *dir = (fat32_de *)root_buf; + int free_entry = -1; + u32 existing_clus = 0; + int found = 0; + for (u32 e = 0; e < max_entries; e++) { + if (dir[e].name[0] == 0) { if (free_entry < 0) free_entry = (int)e; break; } + if ((u8)dir[e].name[0] == 0xE5) { if (free_entry < 0) free_entry = (int)e; continue; } + if (dir[e].attr == 0x0F) continue; + if (dir[e].attr & 0x08) continue; + if (neq11(dir[e].name, name11)) { + existing_clus = r16((const u8*)&dir[e].clow) | ((u32)r16((const u8*)&dir[e].chigh) << 16); + found = 1; + break; + } + } + + /* 3. Allocate or reuse clusters */ + u32 bytes_needed = size > 0 ? size : 1; + u32 clusters_needed = (bytes_needed + cluster_bytes - 1) / cluster_bytes; + u32 first_clus = 0; + u32 prev_clus = 0; + + if (found && existing_clus >= 2) { + /* Reuse existing chain, extend or shrink as needed */ + first_clus = existing_clus; + u32 cur = existing_clus; + u32 count = 0; + while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) { + prev_clus = cur; + u32 fo = fat_byte_off + cur * 4; + if (fo + 4 > sizeof(g_disk)) break; + cur = r32(disk + fo) & 0x0FFFFFFF; + count++; + } + /* If we need more clusters, allocate them */ + while (count < clusters_needed) { + /* Find a free cluster in FAT */ + u32 newc = 0; + for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) { + u32 fo = fat_byte_off + c * 4; + if (fo + 4 > sizeof(g_disk)) break; + if ((r32(disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; } + } + if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; } + /* Link previous cluster to new */ + u32 pfo = fat_byte_off + prev_clus * 4; + if (pfo + 4 <= sizeof(g_disk)) { + disk[pfo] = (u8)(newc & 0xFF); + disk[pfo+1] = (u8)((newc >> 8) & 0xFF); + disk[pfo+2] = (u8)((newc >> 16) & 0xFF); + disk[pfo+3] = (u8)((newc >> 24) & 0x0F); + } + prev_clus = newc; + count++; + } + /* Terminate the chain */ + if (prev_clus >= 2) { + u32 fo = fat_byte_off + prev_clus * 4; + if (fo + 4 <= sizeof(g_disk)) { + disk[fo] = 0xF8; disk[fo+1] = 0xFF; disk[fo+2] = 0xFF; disk[fo+3] = 0x0F; + } + } + /* Free excess clusters from the old chain */ + u32 next = 0; + u32 fo = fat_byte_off + prev_clus * 4; + if (fo + 4 <= sizeof(g_disk)) next = r32(disk + fo) & 0x0FFFFFFF; + while (next >= 2 && next < 0x0FFFFFF8) { + u32 nfo = fat_byte_off + next * 4; + u32 nn = 0; + if (nfo + 4 <= sizeof(g_disk)) nn = r32(disk + nfo) & 0x0FFFFFFF; + disk[nfo] = 0; disk[nfo+1] = 0; disk[nfo+2] = 0; disk[nfo+3] = 0; + next = nn; + } + } else { + /* Allocate new clusters */ + if (free_entry < 0) { logl("[DSK] fat32 write: no free dir entry"); return -4; } + for (u32 i = 0; i < clusters_needed; i++) { + u32 newc = 0; + for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) { + u32 fo = fat_byte_off + c * 4; + if (fo + 4 > sizeof(g_disk)) break; + if ((r32(disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; } + } + if (newc == 0) { logl("[DSK] fat32 write: no free clusters"); return -3; } + if (i == 0) first_clus = newc; + if (prev_clus >= 2) { + u32 pfo = fat_byte_off + prev_clus * 4; + disk[pfo] = (u8)(newc & 0xFF); + disk[pfo+1] = (u8)((newc >> 8) & 0xFF); + disk[pfo+2] = (u8)((newc >> 16) & 0xFF); + disk[pfo+3] = (u8)((newc >> 24) & 0x0F); + } + prev_clus = newc; + } + /* Terminate chain */ + if (prev_clus >= 2) { + u32 fo = fat_byte_off + prev_clus * 4; + disk[fo] = 0xF8; disk[fo+1] = 0xFF; disk[fo+2] = 0xFF; disk[fo+3] = 0x0F; + } + } + + /* 4. Write data to clusters */ + u32 remaining = size; + const u8 *src = data; + u32 cur = first_clus; + u32 ci = 0; + while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) { + u32 clba = data_lba + (cur - 2) * spc; + u32 chunk = remaining < cluster_bytes ? remaining : cluster_bytes; + /* Read cluster into g_cluster, overlay data, write back */ + if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) { + /* In g_disk buffer — copy data directly */ + u8 *dst = disk + (u64)clba * 512; + for (u32 b = 0; b < chunk; b++) dst[b] = src[b]; + for (u32 b = chunk; b < cluster_bytes; b++) dst[b] = 0; + } else { + /* Out of buffer — use sector write */ + if (fat32_read_sectors(clba, spc, g_cluster) != 0) return -5; + for (u32 b = 0; b < chunk; b++) g_cluster[b] = src[b]; + for (u32 b = chunk; b < cluster_bytes; b++) g_cluster[b] = 0; + if (fat32_write_sectors(clba, spc, g_cluster) != 0) return -6; + } + src += chunk; remaining -= chunk; ci++; + u32 fo = fat_byte_off + cur * 4; + if (fo + 4 > sizeof(g_disk)) break; + cur = r32(disk + fo) & 0x0FFFFFFF; + } + + /* 5. Update directory entry */ + if (!found) { + fat32_de *e = &dir[free_entry]; + for (int i = 0; i < 11; i++) e->name[i] = name11[i]; + e->attr = 0x20; /* archive */ + e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0; + e->adate = 0; e->chigh = (u16)((first_clus >> 16) & 0xFFFF); + e->wtime = 0; e->wdate = 0; e->clow = (u16)(first_clus & 0xFFFF); + } else { + dir[free_entry >= 0 ? (u32)free_entry : 0].chigh = (u16)((first_clus >> 16) & 0xFFFF); + dir[free_entry >= 0 ? (u32)free_entry : 0].clow = (u16)(first_clus & 0xFFFF); + /* Find the existing entry again to update it */ + for (u32 e2 = 0; e2 < max_entries; e2++) { + if (neq11(dir[e2].name, name11)) { + dir[e2].chigh = (u16)((first_clus >> 16) & 0xFFFF); + dir[e2].clow = (u16)(first_clus & 0xFFFF); + dir[e2].fsize = size; + break; + } + } + } + /* Set file size in the directory entry */ + for (u32 e2 = 0; e2 < max_entries; e2++) { + if (neq11(dir[e2].name, name11)) { + dir[e2].fsize = size; + break; + } + } + + /* 6. Write FAT + root directory + data sectors back to disk */ + /* Write FAT (both copies) */ + for (u32 f = 0; f < bpb->fc; f++) { + u32 flba = fat_lba + f * fat_sectors; + if (fat32_write_sectors(flba, fat_sectors, disk + (u64)flba * 512) != 0) return -7; + } + /* Write root directory cluster */ + u32 root_dir_lba = data_lba + (root_clus - 2) * spc; + if ((u64)root_dir_lba * 512 + cluster_bytes <= 256ULL * 512) { + if (fat32_write_sectors(root_dir_lba, spc, disk + (u64)root_dir_lba * 512) != 0) return -8; + } + /* Write data clusters that were in the g_disk buffer */ + cur = first_clus; + ci = 0; + while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) { + u32 clba = data_lba + (cur - 2) * spc; + if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) { + if (fat32_write_sectors(clba, spc, disk + (u64)clba * 512) != 0) return -9; + } + u32 fo = fat_byte_off + cur * 4; + if (fo + 4 > sizeof(g_disk)) break; + cur = r32(disk + fo) & 0x0FFFFFFF; + ci++; + } + + logl("[DSK] fat32 write: success"); + return 0; +} + +static int dsk_check_firstinit(int *is_first, int *dev_mode) { char name[12]; /* "FIRSTINTXT" on stack — no PIE reloc */ name[0]='F';name[1]='I';name[2]='R';name[3]='S';name[4]='T'; name[5]='I';name[6]='N';name[7]='T';name[8]='X';name[9]='T';name[10]=0; u8 *data = 0; u32 size = 0; + *dev_mode = 0; logl("[DSK] fat32_read_path start"); int rc = fat32_read_root_file(name, &data, &size); logh("[DSK] fat32_read_path rc=", (u64)(i64)rc); if (rc != 0) { logl("[DSK] firstInit.txt not found"); *is_first = 1; return 0; } - if (data[0] == '0') { logl("[DSK] firstInit=0"); *is_first = 1; return 0; } - logl("[DSK] firstInit != 0"); *is_first = 0; return 0; + if (data[0] == '0') { logl("[DSK] firstInit=0"); *is_first = 1; } + else { logl("[DSK] firstInit != 0"); *is_first = 0; } + /* 解析第二行 dev_mode 标志(firstInit.txt 格式: "0|1\n0|1") */ + u32 i = 0; + while (i < size && data[i] != '\n') i++; + if (i < size) { + i++; /* 跳过换行 */ + if (i < size && data[i] == '1') { + *dev_mode = 1; + logl("[DSK] dev_mode=1 (developer auto-test)"); + } + } + return 0; } static int dsk_load_elf(u8 *data, u32 size, void **entry_out) { @@ -205,8 +529,10 @@ static int dsk_load_elf(u8 *data, u32 size, void **entry_out) { if (!lc || min_vaddr == ~0ULL) return -4; u64 isize = max_vaddr - min_vaddr; isize = (isize + 0xFFF) & ~0xFFFULL; - static u8 ibuf[262144]; + /* 加载缓冲:需容纳 shell.elf(~536KB,含 FAT32 读写缓冲 BSS)。1MB 留余量。 */ + static u8 ibuf[1048576]; u8 *image = ibuf; + if (isize > 1048576) return -5; /* 镜像过大,防止越界 */ memset_dsk(image, 0, isize); for (u16 i = 0; i < eh->phnum; i++) { const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize); @@ -247,177 +573,220 @@ static int dsk_load_elf(u8 *data, u32 size, void **entry_out) { return 0; } -static void dsk_spinner_and_check(u32 *fb_arg, i64 cx, i64 cy, i64 r, i64 thk, - const u32 arc_colors[4], u32 bg, const void *boot_ctx_ptr) { - (void)bg; - (void)fb_arg; +/* Load an ELF module from the FAT32 root by 11-char 8.3 name, parse it into + * the shared image buffer, and jump to its entry with the boot context. + * Returns 0 if the module ran and returned, non-zero on load failure. */ +static int dsk_load_and_run(const char name11[12], const dsk_boot_context *ctx) { + u8 *data=0; u32 size=0; void *entry=0; + if (fat32_read_root_file(name11, &data, &size) != 0) return -1; + if (dsk_load_elf(data, size, &entry) != 0) return -2; + void(*fn)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))entry; + fn(ctx); + return 0; +} + +/* Load desktop.elf and jump to it. Never returns on success. */ +static void dsk_run_desktop(const dsk_boot_context *ctx) { + char dn[12]; /* DESKTOP ELF */ + dn[0]='D';dn[1]='E';dn[2]='S';dn[3]='K';dn[4]='T';dn[5]='O';dn[6]='P';dn[7]=' ';dn[8]='E';dn[9]='L';dn[10]='F'; + if (dsk_load_and_run(dn, ctx) != 0) logl("[DSK] desktop load failed"); + for(;;)__asm__("hlt"); +} + +/* After FirstInit returns, persist the encrypted user.conf buffer (left in + * ctx->reserved[0/1]) to the FAT32 root as USER.CONF, then flip firstInit.txt + * from '0' to '1' so subsequent boots skip the wizard and go to login. */ +static void dsk_persist_userconf(const dsk_boot_context *ctx) { + dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx; + u64 conf_ptr = ctx_mut->reserved[0]; + u64 conf_sz = ctx_mut->reserved[1]; + if (!conf_ptr || !conf_sz) { + logl("[DSK] no conf buffer from FirstInit, skipping persist"); + return; + } + if (!g_block_write) { + logl("[DSK] no block_write, cannot persist user.conf"); + return; + } + const u8 *conf = (const u8 *)conf_ptr; + + /* 1. Write USER.CONF (encrypted buffer as-is; login decrypts with password) */ + char cn[12]; /* USER CON */ + cn[0]='U';cn[1]='S';cn[2]='E';cn[3]='R';cn[4]=' ';cn[5]=' ';cn[6]=' ';cn[7]=' ';cn[8]='C';cn[9]='O';cn[10]='N'; + logl("[DSK] writing USER.CONF"); + int wrc = fat32_write_root_file(cn, conf, (u32)conf_sz); + logh("[DSK] USER.CONF write rc=", (u64)(i64)wrc); + + /* 2. Flip firstInit.txt: '0' -> '1',保留第二行 dev_mode 标志 */ + char fn[12]; /* FIRSTINTXT */ + fn[0]='F';fn[1]='I';fn[2]='R';fn[3]='S';fn[4]='T';fn[5]='I';fn[6]='N';fn[7]='T';fn[8]='X';fn[9]='T';fn[10]=0; + /* 读取当前 firstInit.txt 以保留 dev_mode(第二行),格式 "0|1\n0|1" */ + u8 dev_mode_byte = '0'; + u8 *fi_data = 0; u32 fi_size = 0; + if (fat32_read_root_file(fn, &fi_data, &fi_size) == 0) { + u32 i = 0; + while (i < fi_size && fi_data[i] != '\n') i++; + if (i < fi_size) { i++; if (i < fi_size) dev_mode_byte = fi_data[i]; } + } + u8 fi_buf[3]; fi_buf[0] = '1'; fi_buf[1] = '\n'; fi_buf[2] = dev_mode_byte; + int frc = fat32_write_root_file(fn, fi_buf, 3); + logh("[DSK] firstInit.txt flip rc=", (u64)(i64)frc); + + /* Clear reserved so login path doesn't see stale pointer */ + ctx_mut->reserved[0] = 0; + ctx_mut->reserved[1] = 0; +} + +/* Draw the spinning loader (comet-tail arc) on the gradient background, + * then run the firstInit check and dispatch system component loading. */ +static void dsk_spinner_and_check(const void *boot_ctx_ptr) { const dsk_boot_context *ctx = (const dsk_boot_context *)boot_ctx_ptr; { u64 api = ctx->dkm_kernel_api; - g_block_read = (block_read_fn)*(u64 *)(api + 0xA8); - if (g_block_read) g_block_read = (block_read_fn)*(u64 *)((u64)g_block_read + 16); } - -#define SPR 128 - static u32 sprite[SPR*SPR]; - i64 sp=SPR*4, scx=SPR/2, scy=SPR/2, bx=cx-scx, by=cy-scy; - i64 frame=0, base=0; int checked=0; void *fe_entry=0; + u64 blk = *(u64 *)(api + 0xA8); + g_block_read = blk ? (block_read_fn)*(u64 *)(blk + 16) : 0; /* block_api.read */ + g_block_write = blk ? (block_write_fn)*(u64 *)(blk + 24) : 0; /* block_api.write */ } g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width; g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch; u32 *sfb=(u32*)(u64)g_fb_addr; - dsk_fill_gradient_rect(sfb, 0, 0, (i64)g_fb_w, (i64)g_fb_h); - u64 sw=g_fb_w, sh=g_fb_h, spv=g_fb_p; logh("[DSK] block_read ptr=", (u64)g_block_read); - logl("[DSK] entering spinner loop"); - while (1) { - g_fb_w=SPR; g_fb_h=SPR; g_fb_p=sp; - /* per-row gradient so sprite interior matches background */ - static u32 row_bg[SPR]; - for (i64 y0=0; y0= sh) ry = (i64)sh - 1; - row_bg[y0] = blend_color(BG_TOP, BG_BOTTOM, (u32)(((u64)ry * 255ULL) / (sh - 1))); - u32 *ln=&sprite[y0*SPR]; for(i64 x0=0;x0=SPR)continue; - u32 sbg=row_bg[yy]; - for(i64 dx=-ro-1;dx<=ro+1;dx++){i64 xx=scx+dx; if(xx<0||xx>=SPR)continue; - i64 d2=dx*dx+dy*dy; if(d2(ro+1)*(ro+1)+2)continue; - i64 ang; if(dx==0)ang=dy<0?90:270; - else{i64 ax=dx<0?-dx:dx,ay=dy<0?-dy:dy,a0=ax>ay?ay*45/ax:90-ax*45/(ay?ay:1); - if(dx>=0&&dy<=0)ang=a0;else if(dx<0&&dy<=0)ang=180-a0; - else if(dx<0&&dy>0)ang=180+a0;else ang=360-a0;} - i64 a=ang-sa; if(a<0)a+=360; if((u64)a>(u64)sw2)continue; - u32 fade; if((u64)a<6)fade=(u32)(a*255/6);else if((u64)a>(u64)(sw2-6))fade=(u32)((sw2-a)*255/6);else fade=255; - u64 vv=(u64)d2; u64 xr=vv,yr=(xr+1)>>1; while(yr>1;} i64 dist=(i64)xr; - u32 eff=((sbg&0xFF)*(256-fade)+(clr&0xFF)*fade)>>8; - u32 ge=(((sbg>>8)&0xFF)*(256-fade)+((clr>>8)&0xFF)*fade)>>8; - u32 be=(((sbg>>16)&0xFF)*(256-fade)+((clr>>16)&0xFF)*fade)>>8; - eff=0xFF000000|(be<<16)|(ge<<8)|eff; - if(dist>=ri&&dist<=ro)sprite[(u64)yy*SPR+(u64)xx]=eff; - }} - } - i64 ri2=r-8; - for(i64 dy=-ri2-2;dy<=ri2+2;dy++){i64 yy2=scy+dy; if(yy2<0||yy2>=SPR)continue; - u32 sbg2=row_bg[yy2]; - for(i64 dx=-ri2-2;dx<=ri2+2;dx++){i64 xx2=scx+dx; if(xx2<0||xx2>=SPR)continue; - i64 d2=dx*dx+dy*dy; - if(d2<=ri2*ri2){ - u32 ic=((sbg2&0xFF)*176+0xD0*80)>>8; - u32 ig=(((sbg2>>8)&0xFF)*176+0xF0*80)>>8; - u32 ib=(((sbg2>>16)&0xFF)*176+0xFF*80)>>8; - sprite[(u64)yy2*SPR+(u64)xx2]=0xFF000000|(ib<<16)|(ig<<8)|ic; - } - }} - } - g_fb_w=sw; g_fb_h=sh; g_fb_p=spv; - dsk_fill_gradient_rect(sfb, bx, by, SPR, SPR); - for(i64 sr=0;sr=g_fb_h)continue; - u32 *dst=(u32*)((u8*)sfb+(u64)sy*g_fb_p)+bx; u32 *src=&sprite[sr*SPR]; - for(i64 sc=0;sc=g_fb_w)continue; dst[sc]=src[sc];}} - for(volatile u32 d=0;d<72000;d++)__asm__("pause"); - base-=2; if(base<0)base+=360; frame++; + tsc_calibrate_dsk(); - if(frame>=100&&!checked){checked=1; /* was 540, reduced for test */ - logl("[DSK] checking firstInit..."); - if(g_block_read){logl("[DSK] have block read, calling dsk_check_firstinit"); - int is_first=0; - if(dsk_check_firstinit(&is_first)==0){ - if(is_first){ - logl("[DSK] first init detected; running system initializers"); + /* Gradient background + spinning loader (~1.1s, or 800ms without block dev). */ + i64 cx = (i64)g_fb_w / 2; + i64 cy = (i64)g_fb_h / 2; + dsk_fill_gradient_rect(sfb, 0, 0, (i64)g_fb_w, (i64)g_fb_h); + spinner_save_bg(sfb, cx, cy); - /* DSK owns system component initialization. FirstInit is only user setup. */ - char mn[12]; /* MOUSE ELF */ - mn[0]='M';mn[1]='O';mn[2]='U';mn[3]='S';mn[4]='E';mn[5]=' ';mn[6]=' ';mn[7]=' ';mn[8]='E';mn[9]='L';mn[10]='F'; - u8 *md=0; u32 ms=0; void *me=0; - if(fat32_read_root_file(mn,&md,&ms)==0){ - if(dsk_load_elf(md,ms,&me)==0){ - logl("[DSK] jumping to mouseInit"); - void(*mentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))me; - mentry(ctx); - logl("[DSK] mouseInit returned"); - } - } + u32 base = 0; + u32 head_color = 0xFFE8F2FC; /* nearly-white light blue */ + u64 spin_start = rdtsc_dsk(); + u64 spin_budget = g_tsc_per_ms * (g_block_read ? 1100 : 800); + while (rdtsc_dsk() - spin_start < spin_budget) { + spinner_restore_bg(sfb, cx, cy); + spinner_draw(sfb, cx, cy, base, head_color); + base = (base + 6) & 255; /* clockwise advance */ + u64 fs = rdtsc_dsk(); + while (rdtsc_dsk() - fs < g_tsc_per_ms * 33) __asm__("pause"); /* ~30fps */ + } + spinner_restore_bg(sfb, cx, cy); - char nn[12]; /* NETMAN ELF */ - nn[0]='N';nn[1]='E';nn[2]='T';nn[3]='M';nn[4]='A';nn[5]='N';nn[6]=' ';nn[7]=' ';nn[8]='E';nn[9]='L';nn[10]='F'; - u8 *nd=0; u32 ns=0; void *ne=0; - if(fat32_read_root_file(nn,&nd,&ns)==0){ - if(dsk_load_elf(nd,ns,&ne)==0){ - logl("[DSK] jumping to netman"); - void(*nentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))ne; - nentry(ctx); - logl("[DSK] netman returned"); - } - } + if (!g_block_read) { + logl("[DSK] no block device, halting"); + for(;;)__asm__("hlt"); + } - logl("[DSK] loading FirstInit.elf"); - char en[12]; /* FIRSTINIT */ - en[0]='F';en[1]='I';en[2]='R';en[3]='S';en[4]='T'; - en[5]='I';en[6]='N';en[7]='I';en[8]='T';en[9]=' ';en[10]=0; - u8 *fd=0; u32 fs=0; - if(fat32_read_root_file(en,&fd,&fs)==0){ - if(dsk_load_elf(fd,fs,&fe_entry)==0) logl("[DSK] FirstInit.elf loaded"); - } + logl("[DSK] checking firstInit..."); + int is_first = 0, dev_mode = 0; + if (dsk_check_firstinit(&is_first, &dev_mode) != 0) { + logl("[DSK] firstInit check failed, halting"); + for(;;)__asm__("hlt"); + } + + /* 开发者模式:加载 shell.elf 自动测试命令序列(cp/mv/echo/ls/cat/rm) */ + if (dev_mode) { + logl("[DSK] dev mode: loading shell.elf for auto-test"); + char sn[12]; /* SHELL ELF */ + sn[0]='S';sn[1]='H';sn[2]='E';sn[3]='L';sn[4]='L';sn[5]=' ';sn[6]=' ';sn[7]=' ';sn[8]='E';sn[9]='L';sn[10]='F'; + dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx; + ctx_mut->reserved[3] = 1; /* dev mode flag for shell */ + if (dsk_load_and_run(sn, ctx) == 0) logl("[DSK] shell auto-test returned"); + else logl("[DSK] shell.elf not found, skipping dev test"); + ctx_mut->reserved[3] = 0; + } + + if (!is_first) { + logl("[DSK] firstInit=1, attempting login before desktop"); + + /* Load login.elf first (into g_fdata, then dsk_load_elf copies to ibuf) */ + char ln[12]; /* LOGIN ELF */ + ln[0]='L';ln[1]='O';ln[2]='G';ln[3]='I';ln[4]='N';ln[5]=' ';ln[6]=' ';ln[7]=' ';ln[8]='E';ln[9]='L';ln[10]='F'; + u8 *ld=0; u32 ls=0; void *le=0; + int login_ok = 0; + if (fat32_read_root_file(ln, &ld, &ls) == 0) { + if (dsk_load_elf(ld, ls, &le) == 0) { + /* Now read USER.CONF into g_fdata (login.elf already copied to ibuf) */ + char cn[12]; /* USER CON */ + cn[0]='U';cn[1]='S';cn[2]='E';cn[3]='R';cn[4]=' ';cn[5]=' ';cn[6]=' ';cn[7]=' ';cn[8]='C';cn[9]='O';cn[10]='N'; + u8 *conf_data=0; u32 conf_size=0; + if (fat32_read_root_file(cn, &conf_data, &conf_size) == 0) { + logl("[DSK] USER.CONF found, passing to login"); + dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx; + ctx_mut->reserved[0] = (u64)conf_data; + ctx_mut->reserved[1] = (u64)conf_size; + ctx_mut->reserved[2] = 0; /* login success flag */ + + logl("[DSK] jumping to login"); + void(*lentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))le; + lentry(ctx); + logl("[DSK] login returned"); + + if (ctx_mut->reserved[2] == 1) { + logl("[DSK] login success"); + login_ok = 1; } else { - logl("[DSK] firstInit=1, skipping user setup, loading shell directly"); + logl("[DSK] login skipped or failed"); } + } else { + logl("[DSK] USER.CONF not found, skipping login"); } + } else { + logl("[DSK] login.elf load failed, skipping login"); } + } else { + logl("[DSK] login.elf not found, skipping login"); } - if(fe_entry&&frame>150){logl("[DSK] jumping to FirstInit"); /* was 600 */ - void(*entry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))fe_entry; - entry(ctx); - logl("[DSK] FirstInit returned, loading desktop"); - /* FirstInit 返回后加载 desktop */ - char dn[12]; /* DESKTOP ELF */ - dn[0]='D';dn[1]='E';dn[2]='S';dn[3]='K';dn[4]='T';dn[5]='O';dn[6]='P';dn[7]=' ';dn[8]='E';dn[9]='L';dn[10]='F'; - u8 *dd=0; u32 ds=0; void *de=0; - if(fat32_read_root_file(dn,&dd,&ds)==0){ - if(dsk_load_elf(dd,ds,&de)==0){ - logl("[DSK] jumping to desktop"); - void(*dentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))de; - dentry(ctx); - } - } - for(;;)__asm__("hlt"); + (void)login_ok; + + logl("[DSK] loading desktop"); + dsk_run_desktop(ctx); + return; + } + + /* First boot: run system initializers, then FirstInit, then desktop */ + logl("[DSK] first init detected; running system initializers"); + + /* mouseInit.elf — safe PS/2 mouse init */ + { char mn[12]; /* MOUSE ELF */ + mn[0]='M';mn[1]='O';mn[2]='U';mn[3]='S';mn[4]='E';mn[5]=' ';mn[6]=' ';mn[7]=' ';mn[8]='E';mn[9]='L';mn[10]='F'; + logl("[DSK] loading mouseInit"); + if (dsk_load_and_run(mn, ctx) == 0) logl("[DSK] mouseInit returned"); + else logl("[DSK] mouseInit not found"); + } + + /* netman.elf — network configuration */ + { char nn[12]; /* NETMAN ELF */ + nn[0]='N';nn[1]='E';nn[2]='T';nn[3]='M';nn[4]='A';nn[5]='N';nn[6]=' ';nn[7]=' ';nn[8]='E';nn[9]='L';nn[10]='F'; + logl("[DSK] loading netman"); + if (dsk_load_and_run(nn, ctx) == 0) logl("[DSK] netman returned"); + else logl("[DSK] netman not found"); + } + + /* FirstInit.elf — user setup wizard; returns to DSK when done */ + { char en[12]; /* FIRSTINIT */ + en[0]='F';en[1]='I';en[2]='R';en[3]='S';en[4]='T'; + en[5]='I';en[6]='N';en[7]='I';en[8]='T';en[9]=' ';en[10]=0; + logl("[DSK] loading FirstInit"); + if (dsk_load_and_run(en, ctx) != 0) { + logl("[DSK] FirstInit not found, loading desktop directly"); + dsk_run_desktop(ctx); + return; } - /* 非首次启动:直接加载 desktop */ - if(checked&&!fe_entry&&frame>150){ - logl("[DSK] loading desktop directly (non-first boot)"); - char dn[12]; /* DESKTOP ELF */ - dn[0]='D';dn[1]='E';dn[2]='S';dn[3]='K';dn[4]='T';dn[5]='O';dn[6]='P';dn[7]=' ';dn[8]='E';dn[9]='L';dn[10]='F'; - u8 *dd=0; u32 ds=0; void *de=0; - if(fat32_read_root_file(dn,&dd,&ds)==0){ - if(dsk_load_elf(dd,ds,&de)==0){ - logl("[DSK] jumping to desktop"); - void(*dentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))de; - dentry(ctx); - } - } - for(;;)__asm__("hlt"); - } - if(!g_block_read&&frame>800){logl("[DSK] no block device, skipping firstInit");break;} - if(checked&&!fe_entry&&frame>800){logl("[DSK] firstInit not found, continuing normal boot");break;} + logl("[DSK] FirstInit returned, persisting user.conf"); + dsk_persist_userconf(ctx); + logl("[DSK] loading desktop"); + dsk_run_desktop(ctx); } } __attribute__((noreturn, visibility("default"))) void dsk_entry(const dsk_boot_context *ctx) { - __asm__ volatile("cli"); /* disable IRQs — ps2kbd/IRQ1 & mouse/IRQ12 handlers crash during spinner */ + __asm__ volatile("cli"); /* disable IRQs — ps2kbd/IRQ1 & mouse/IRQ12 handlers crash during boot */ logl("[DSK] boot"); if(!ctx||ctx->magic!=DSK_BOOT_MAGIC){logl("[DSK] bad context");for(;;)__asm__("hlt");} logl("[DSK] context ok"); - g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width; - g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch; - u32 bg=BG_MID, arcs[4]={0xFF4488CC,0xFF2266AA,0xFF66AAEE,0xFF88CCFF}; - i64 cx=(i64)g_fb_w/2,cy=(i64)g_fb_h/2,r=48,thk=4; - dsk_spinner_and_check((u32*)(u64)g_fb_addr,cx,cy,r,thk,arcs,bg,ctx); + dsk_spinner_and_check(ctx); logl("[DSK] SELFTEST PASS"); for(;;)__asm__("hlt"); } diff --git a/CODE/dsk/main.o b/CODE/dsk/main.o index 9fbeae8..622fb3e 100644 Binary files a/CODE/dsk/main.o and b/CODE/dsk/main.o differ diff --git a/CODE/firstInit/linker.ld b/CODE/firstInit/linker.ld index 825d5f1..473e5fc 100644 --- a/CODE/firstInit/linker.ld +++ b/CODE/firstInit/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(5); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(6); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/firstInit/main.c b/CODE/firstInit/main.c index 8dc7b13..05c069c 100644 --- a/CODE/firstInit/main.c +++ b/CODE/firstInit/main.c @@ -16,6 +16,42 @@ typedef long long i64; static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); } static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; } +/* ---- TSC-based timing (实机要求: 用 CPU 频率计算, 不用循环) ---- */ +static u64 g_tsc_per_ms = 0; + +static __inline__ u64 rdtsc_fi(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +/* 用 PIT (8254, 1.193182 MHz) 校准 TSC 频率 */ +static void tsc_calibrate_fi(void) { + outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */ + outb(0x40, 0x7c); /* 11932 low = ~10ms */ + outb(0x40, 0x2e); /* 11932 high */ + u64 tsc_start = rdtsc_fi(); + u16 prev = 0; u64 loops = 0; + for (;;) { + outb(0x43, 0x00); + u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = rdtsc_fi(); + g_tsc_per_ms = (tsc_end - tsc_start) / 10; +} + +static void delay_ms_fi(u32 ms) { + if (!g_tsc_per_ms) { + for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause"); + return; + } + u64 target = g_tsc_per_ms * ms; + u64 start = rdtsc_fi(); + while (rdtsc_fi() - start < target) __asm__ volatile("pause"); +} + static void sputc(char c) { for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; } outb(COM1, (unsigned char)c); @@ -23,16 +59,20 @@ static void sputc(char c) { static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } } static void logl(const char *s) { swrite(s); swrite("\n"); } -/* ---- PS/2 controller / mouse helpers (timeout-protected) ---- */ +/* ---- PS/2 controller / mouse helpers (timeout-protected, TSC-based) ---- */ static int ps2_wait_write(void) { - for (volatile u32 i = 0; i < 100000; i++) { + u64 deadline = rdtsc_fi() + g_tsc_per_ms * 100; /* 100ms 超时 */ + while (rdtsc_fi() < deadline) { if (!(inb(0x64) & 0x02)) return 0; /* input buffer empty */ + __asm__ volatile("pause"); } return -1; } static int ps2_wait_read(void) { - for (volatile u32 i = 0; i < 100000; i++) { + u64 deadline = rdtsc_fi() + g_tsc_per_ms * 100; /* 100ms 超时 */ + while (rdtsc_fi() < deadline) { if (inb(0x64) & 0x01) return 0; /* output buffer full */ + __asm__ volatile("pause"); } return -1; } @@ -81,12 +121,12 @@ static void mouse_init(void) { logl("[mouse] init done"); } -#define BG_TOP 0xFFC8E0F0u -#define BG_BOTTOM 0xFF49306Fu -#define BG_MID 0xFF8888B0u -#define CARD_BG 0xFFEAF5FBu -#define INPUT_BG 0xFFF0F5FAu -#define TEXT_FG 0xFF5F7FA6u +#define BG_TOP 0xFF2D2D30u +#define BG_BOTTOM 0xFF1A1A1Eu +#define BG_MID 0xFF3C3C40u +#define CARD_BG 0xFF353539u +#define INPUT_BG 0xFF2A2A2Eu +#define TEXT_FG 0xFFE0E0E0u #define WELCOME_FG 0xFFE8F2FCu static u64 fb_a, fb_w, fb_h, fb_p; @@ -127,6 +167,38 @@ static void fb_text(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 bg) { for (i64 i=0; s[i]; i++) fb_char(fb, (u32)(u8)s[i], x+i*ASCII_STEP, y, fg, bg); } +/* width of an ASCII string in pixels (used to center English subtitles) */ +static i64 fb_text_width(const char *s) { + i64 n = 0; + while (s[n]) n++; + return n * ASCII_STEP; +} + +/* Alpha-blended ASCII text — same as fb_text but multiplied by a global alpha, + * used to fade English subtitles in/out alongside the Chinese bitmaps. */ +static void fb_text_alpha(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 bg, u32 global_alpha) { + for (i64 i=0; s[i]; i++) { + u32 ch = (u32)(u8)s[i]; + if (ch < ' ' || ch > '~') ch = ' '; + u32 idx = ch - ' '; + const u8 *g = g_ascii[idx]; + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c> 8; + if (eff_a == 0) continue; + i64 xx = x + i*ASCII_STEP + c; + if (xx < 0 || (u64)xx >= fb_w) continue; + line[(u64)xx] = (eff_a == 255) ? fg : blend(bg, fg, eff_a); + } + } + } +} + /* draw pre-rendered grayscale bitmap – writes only, no framebuffer read */ static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y, u32 fg, u32 bg, u32 global_alpha) { for (i64 r=0; r= fb_w) continue; - u32 a = ((u32)data[r*(u64)w + (u64)c] * global_alpha) >> 8; + u8 val = data[r*(u64)w + (u64)c]; + if (val == 0) continue; /* skip transparent – let background show through */ + u32 a = ((u32)val * global_alpha) >> 8; line[(u64)xx] = blend(bg, fg, a); } } @@ -228,25 +302,21 @@ static void stroke_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, } static void delay_frame(void) { - for (volatile u32 d=0; d<300000; d++) __asm__("pause"); + delay_ms_fi(33); /* 实机: 基于 TSC 的 33ms 帧间隔 (~30fps) */ } #include "text_bitmaps.c" static void fade_ring(u32 *fb, i64 cx, i64 cy, u32 bg) { - /* Smoothly erase a square sprite area with the same vertical gradient as DSK. */ + /* Erase the spinner sprite area with gradient background in one frame. */ (void)bg; i64 r = 72; - for (int step=0; step<12; step++) { - (void)step; - fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1); - delay_frame(); - } + fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1); } static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card, u32 bg_color) { (void)bg_color; - fill_rounded_rect(fb, x, y, w, h, 12, card); + fill_rounded_rect(fb, x, y, w, h, 4, card); } /* Draw gradient background + card in a single pass — per-row blend eliminates flicker */ @@ -266,9 +336,9 @@ static void draw_card_fade(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, u32 } static void draw_rounded_input(u32 *fb, i64 x, i64 y, i64 w, i64 h, const char *value, int mask, u32 fg, u32 card_bg, int active) { - u32 fill = active ? 0xFFFFFFFF : INPUT_BG; - fill_rounded_rect(fb, x, y, w, h, 8, fill); - stroke_rounded_rect(fb, x, y, w, h, 8, 1, active ? 0xFFB6C9DD : card_bg); + u32 fill = active ? 0xFF45454Au : INPUT_BG; + fill_rounded_rect(fb, x, y, w, h, 2, fill); + stroke_rounded_rect(fb, x, y, w, h, 2, 1, active ? 0xFF6C6C72 : 0xFF404044); char out[64]; int i=0; while(value[i] && i<60) { out[i] = mask ? '*' : value[i]; i++; } out[i]=0; @@ -387,17 +457,35 @@ static void cursor_draw(u32 *fb, i64 mx, i64 my) { static void redraw_setup_card(u32 *fb, i64 card_x, i64 card_y, char *pc, char *user, char *pass, int active, u32 bg, u32 fg, i64 mx, i64 my) { u32 card = CARD_BG; + u32 sub_fg = 0xFF9098A0; /* dimmer English subtitle color on card */ cursor_erase(fb); fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); draw_card(fb, card_x, card_y, 900, 600, card, bg); fb_bitmap_alpha(fb, g_txt_title, g_txt_title_w, g_txt_title_h, card_x+40, card_y+40, fg, card, 255); + fb_text(fb, "Account Setup", card_x+40, card_y+40+g_txt_title_h+2, sub_fg, card); fb_bitmap_alpha(fb, g_txt_computer, g_txt_computer_w, g_txt_computer_h, card_x+40, card_y+130, fg, card, 255); + fb_text(fb, "Computer Name", card_x+40, card_y+130+g_txt_computer_h+2, sub_fg, card); draw_rounded_input(fb, card_x+40, card_y+180, 820, 44, pc, 0, fg, card, active==0); fb_bitmap_alpha(fb, g_txt_username, g_txt_username_w, g_txt_username_h, card_x+40, card_y+280, fg, card, 255); + fb_text(fb, "Username", card_x+40, card_y+280+g_txt_username_h+2, sub_fg, card); draw_rounded_input(fb, card_x+40, card_y+330, 820, 44, user, 0, fg, card, active==1); fb_bitmap_alpha(fb, g_txt_password, g_txt_password_w, g_txt_password_h, card_x+40, card_y+430, fg, card, 255); + fb_text(fb, "Password", card_x+40, card_y+430+g_txt_password_h+2, sub_fg, card); draw_rounded_input(fb, card_x+40, card_y+480, 820, 44, pass, 1, fg, card, active==2); fb_bitmap_alpha(fb, g_txt_hint, g_txt_hint_w, g_txt_hint_h, card_x+40, card_y+555, 0xFF7F97AC, card, 255); + fb_text(fb, "Press Enter to confirm each field", card_x+40, card_y+555+g_txt_hint_h+2, 0xFF6F7F8C, card); + cursor_draw(fb, mx, my); +} + +/* 仅重绘当前活动输入框,避免每次按键重绘整个卡片导致闪烁 */ +static void redraw_input_only(u32 *fb, i64 card_x, i64 card_y, int field, char *pc, char *user, char *pass, i64 mx, i64 my, u32 fg) { + u32 card = CARD_BG; + char *buf = field==0 ? pc : (field==1 ? user : pass); + int mask = (field == 2) ? 1 : 0; + i64 fy = card_y + 180 + (i64)field * 150; + cursor_erase(fb); + fill_rect(fb, card_x + 40, fy, 820, 44, card); + draw_rounded_input(fb, card_x+40, fy, 820, 44, buf, mask, fg, card, 1); cursor_draw(fb, mx, my); } @@ -474,7 +562,7 @@ static int read_field(u32 *fb, i64 card_x, i64 card_y, int field, char *pc, char buf[len++] = c; buf[len] = 0; } - redraw_setup_card(fb, card_x, card_y, pc, user, pass, field, bg, fg, *mx, *my); + redraw_input_only(fb, card_x, card_y, field, pc, user, pass, *mx, *my, fg); } } @@ -486,15 +574,64 @@ typedef struct { int theme; } setup_prefs; -static const char *pref_value(int row, int val) { - if (row == 0) return val ? "English" : "Simplified Chinese"; - if (row == 1) return val ? "Global" : "China Mainland"; - if (row == 2) return val ? "UTC" : "Asia/Shanghai"; - if (row == 3) return val ? "CN-QWERTY" : "US-QWERTY"; - if (row == 4) return val ? "Dark" : "Light"; +/* 语言选项: 索引 0-11 */ +static const char *lang_names[] = { + "Simplified Chinese", "English (US)", "English (UK)", "Japanese", + "Korean", "French", "German", "Spanish", + "Portuguese", "Russian", "Arabic", "Hindi" +}; +#define LANG_COUNT 12 + +/* 键盘布局选项: 索引 0-8 */ +static const char *kb_names[] = { + "US-QWERTY", "CN-QWERTY", "UK-QWERTY", "JP-JIS", + "KR-104", "FR-AZERTY", "DE-QWERTZ", "ES-QWERTY", + "RU-JCUKEN" +}; +#define KB_COUNT 9 + +/* 区域选项 */ +static const char *region_names[] = { + "China Mainland", "Global", "United States", "United Kingdom", + "Japan", "Korea", "France", "Germany", + "Spain", "Russia" +}; +#define REGION_COUNT 10 + +/* 时区选项 */ +static const char *tz_names[] = { + "Asia/Shanghai", "UTC", "America/New_York", "America/Los_Angeles", + "Europe/London", "Asia/Tokyo", "Asia/Seoul", "Europe/Paris", + "Europe/Berlin", "Europe/Moscow" +}; +#define TZ_COUNT 10 + +/* 主题选项 */ +static const char *theme_names[] = { "Light", "Dark", "Auto" }; +#define THEME_COUNT 3 + +static int pref_option_count(int row) { + if (row == 0) return LANG_COUNT; + if (row == 1) return REGION_COUNT; + if (row == 2) return TZ_COUNT; + if (row == 3) return KB_COUNT; + if (row == 4) return THEME_COUNT; + return 2; +} + +static const char *pref_option_name(int row, int opt) { + if (row == 0) return lang_names[opt]; + if (row == 1) return region_names[opt]; + if (row == 2) return tz_names[opt]; + if (row == 3) return kb_names[opt]; + if (row == 4) return theme_names[opt]; return ""; } +static const char *pref_value(int row, int val) { + return pref_option_name(row, val); +} + static int pref_get(const setup_prefs *p, int row) { if (row == 0) return p->language; if (row == 1) return p->region; @@ -504,22 +641,41 @@ static int pref_get(const setup_prefs *p, int row) { return 0; } +static void pref_set(setup_prefs *p, int row, int val) { + if (row == 0) p->language = val; + else if (row == 1) p->region = val; + else if (row == 2) p->timezone = val; + else if (row == 3) p->keyboard = val; + else if (row == 4) p->theme = val; +} + static void pref_toggle(setup_prefs *p, int row) { - if (row == 0) p->language ^= 1; - else if (row == 1) p->region ^= 1; - else if (row == 2) p->timezone ^= 1; - else if (row == 3) p->keyboard ^= 1; - else if (row == 4) p->theme ^= 1; + int count = pref_option_count(row); + int cur = pref_get(p, row); + pref_set(p, row, (cur + 1) % count); } static void draw_option_row(u32 *fb, i64 x, i64 y, i64 w, i64 h, const u8 *label, i64 lw, i64 lh, - const char *value, int active, u32 fg, u32 card) { - u32 fill = active ? 0xFFFFFFFF : INPUT_BG; + const char *value, int active, u32 fg, u32 card, + const char *en) { + u32 fill = active ? 0xFF45454Au : INPUT_BG; fb_bitmap_alpha(fb, label, lw, lh, x, y + 4, fg, card, 255); - fill_rounded_rect(fb, x + 220, y, w - 220, h, 8, fill); - stroke_rounded_rect(fb, x + 220, y, w - 220, h, 8, 1, active ? 0xFFB6C9DD : card); + if (en) fb_text(fb, en, x, y + 4 + lh + 1, 0xFF9098A0, card); + fill_rounded_rect(fb, x + 220, y, w - 220, h, 2, fill); + stroke_rounded_rect(fb, x + 220, y, w - 220, h, 2, 1, active ? 0xFF6C6C72 : 0xFF404044); fb_text(fb, value, x + 238, y + (h - ASCII_H) / 2, fg, fill); + /* dropdown arrow */ + { + i64 ax = x + w - 22; + i64 ay = y + (h - 6) / 2; + fill_rect(fb, ax, ay, 11, 1, fg); + fill_rect(fb, ax + 1, ay + 1, 9, 1, fg); + fill_rect(fb, ax + 2, ay + 2, 7, 1, fg); + fill_rect(fb, ax + 3, ay + 3, 5, 1, fg); + fill_rect(fb, ax + 4, ay + 4, 3, 1, fg); + fill_rect(fb, ax + 5, ay + 5, 1, 1, fg); + } } static void redraw_prefs_card(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, int active, i64 mx, i64 my, u32 bg, u32 fg) { @@ -528,12 +684,14 @@ static void redraw_prefs_card(u32 *fb, i64 card_x, i64 card_y, setup_prefs *pref fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); draw_card(fb, card_x, card_y, 900, 600, card, bg); fb_bitmap_alpha(fb, g_txt_prefs_title, g_txt_prefs_title_w, g_txt_prefs_title_h, card_x+40, card_y+40, fg, card, 255); - draw_option_row(fb, card_x+40, card_y+120, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card); - draw_option_row(fb, card_x+40, card_y+200, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card); - draw_option_row(fb, card_x+40, card_y+280, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card); - draw_option_row(fb, card_x+40, card_y+360, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card); - draw_option_row(fb, card_x+40, card_y+440, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card); + fb_text(fb, "Preferences", card_x+40, card_y+40+g_txt_prefs_title_h+2, 0xFF9098A0, card); + draw_option_row(fb, card_x+40, card_y+120, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card, "Language"); + draw_option_row(fb, card_x+40, card_y+200, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card, "Region"); + draw_option_row(fb, card_x+40, card_y+280, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card, "Timezone"); + draw_option_row(fb, card_x+40, card_y+360, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card, "Keyboard"); + draw_option_row(fb, card_x+40, card_y+440, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card, "Theme"); fb_bitmap_alpha(fb, g_txt_prefs_hint, g_txt_prefs_hint_w, g_txt_prefs_hint_h, card_x+40, card_y+555, 0xFF7F97AC, card, 255); + fb_text(fb, "Tab/arrows to navigate, Enter to confirm", card_x+40, card_y+555+g_txt_prefs_hint_h+2, 0xFF6F7F8C, card); cursor_draw(fb, mx, my); } @@ -541,21 +699,52 @@ static void redraw_prefs_row(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs u32 card = CARD_BG; i64 ry = card_y + 120 + (i64)row * 80; cursor_erase(fb); - fill_rect(fb, card_x + 36, ry - 8, 832, 58, card); - if (row == 0) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card); - else if (row == 1) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card); - else if (row == 2) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card); - else if (row == 3) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card); - else if (row == 4) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card); + fill_rounded_rect(fb, card_x + 36, ry - 8, 832, 58, 2, card); + if (row == 0) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card, "Language"); + else if (row == 1) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card, "Region"); + else if (row == 2) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card, "Timezone"); + else if (row == 3) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card, "Keyboard"); + else if (row == 4) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card, "Theme"); cursor_draw(fb, mx, my); } +/* 绘制下拉菜单:最多显示 6 项,超出时显示滚动指示 */ +static void draw_dropdown_menu(u32 *fb, i64 card_x, i64 card_y, int row, const setup_prefs *prefs, u32 fg) { + i64 dd_x = card_x + 260; + i64 dd_y = card_y + 120 + (i64)row * 80 + 42; + i64 dd_w = 600; + int current = pref_get(prefs, row); + int opt_count = pref_option_count(row); + int visible = opt_count > 6 ? 6 : opt_count; + i64 opt_h = 32; + i64 total_h = visible * opt_h + (opt_count > 6 ? 20 : 0); + /* 深色背景 */ + fill_rect(fb, dd_x, dd_y, dd_w, total_h, 0xFF2A2A2E); + /* 选项 */ + for (int opt = 0; opt < visible; opt++) { + u32 bg_opt = (opt == current) ? 0xFF45454A : 0xFF2A2A2E; + fill_rect(fb, dd_x, dd_y + opt * opt_h, dd_w, opt_h, bg_opt); + fb_text(fb, pref_option_name(row, opt), dd_x + 18, dd_y + opt * opt_h + (opt_h - ASCII_H) / 2, fg, bg_opt); + } + /* 超过 6 项时显示滚动指示 */ + if (opt_count > 6) { + fill_rect(fb, dd_x, dd_y + visible * opt_h, dd_w, 20, 0xFF252528); + fb_text(fb, "...more", dd_x + 18, dd_y + visible * opt_h + 1, 0xFF808088, 0xFF252528); + } + /* 边框 */ + fill_rect(fb, dd_x, dd_y, dd_w, 1, 0xFF505055); + fill_rect(fb, dd_x, dd_y + total_h - 1, dd_w, 1, 0xFF505055); + fill_rect(fb, dd_x, dd_y, 1, total_h, 0xFF505055); + fill_rect(fb, dd_x + dd_w - 1, dd_y, 1, total_h, 0xFF505055); +} + static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, i64 *mx, i64 *my, u32 bg, u32 fg) { int active = 0; int mcnt = 0; u8 mpkt[3]; u8 prev_btns = 0; int e0 = 0; + int dropdown_open = -1; redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg); for (;;) { u8 st = inb(0x64); @@ -579,14 +768,40 @@ static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, cursor_draw(fb, *mx, *my); } if ((btns & 1) && !(prev_btns & 1)) { - for (int r = 0; r < 5; r++) { - i64 ry = card_y + 120 + (i64)r * 80; - if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) { - int old = active; - active = r; - pref_toggle(prefs, active); - if (old != active) redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg); - redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg); + if (dropdown_open >= 0) { + /* 检查点击是否在下拉选项上 */ + i64 dd_x = card_x + 260; + i64 dd_y = card_y + 120 + (i64)dropdown_open * 80 + 42; + i64 dd_w = 600; + int opt_count = pref_option_count(dropdown_open); + int visible = opt_count > 6 ? 6 : opt_count; + i64 opt_h = 32; + int clicked_opt = -1; + for (int opt = 0; opt < visible; opt++) { + i64 opt_y = dd_y + opt * opt_h; + if (*mx >= dd_x && *mx < dd_x + dd_w && *my >= opt_y && *my < opt_y + opt_h) { + clicked_opt = opt; + break; + } + } + if (clicked_opt >= 0) { + pref_set(prefs, dropdown_open, clicked_opt); + } + dropdown_open = -1; + redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg); + } else { + /* No dropdown open - check for value area clicks */ + for (int r = 0; r < 5; r++) { + i64 ry = card_y + 120 + (i64)r * 80; + if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) { + active = r; + dropdown_open = r; + redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg); + cursor_erase(fb); + draw_dropdown_menu(fb, card_x, card_y, r, prefs, fg); + cursor_draw(fb, *mx, *my); + break; + } } } } @@ -596,6 +811,13 @@ static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, u8 sc = data; if (sc == 0xE0) { e0 = 1; continue; } if (sc & 0x80) { e0 = 0; continue; } + /* Close dropdown on any real key press */ + if (dropdown_open >= 0) { + dropdown_open = -1; + redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg); + e0 = 0; + continue; + } if (e0 && sc == 0x48) { if (active > 0) { int old = active; active--; redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg); redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg); } e0 = 0; continue; @@ -617,6 +839,9 @@ static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg); redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg); } else { + cursor_bg_valid = 0; + cursor_cur_x = -100; + cursor_cur_y = -100; return; } } @@ -710,10 +935,11 @@ static void wifi_query_scan(u64 kernel_api) { net_scan_count_fn scount = (net_scan_count_fn)*(u64 *)(net_api + 48); net_scan_result_fn sresult = (net_scan_result_fn)*(u64 *)(net_api + 56); net_is_wireless_fn iswl = (net_is_wireless_fn)*(u64 *)(net_api + 64); - if (!dcount || !iswl || !scount || !sresult) { - logl("[FirstInit] net scan api incomplete"); - return; - } + if (!dcount) { logl("[FirstInit] net device_count fn is null"); return; } + if (!sstart) { logl("[FirstInit] net scan_start fn is null"); return; } + if (!scount) { logl("[FirstInit] net scan_count fn is null"); return; } + if (!sresult) { logl("[FirstInit] net scan_result fn is null"); return; } + if (!iswl) { logl("[FirstInit] net is_wireless fn is null"); return; } u32 ndev = dcount(); /* 串口日志:设备数 */ { @@ -723,11 +949,21 @@ static void wifi_query_scan(u64 kernel_api) { b[p++]='0'+(ndev/10); b[p++]='0'+(ndev%10); b[p]=0; logl(b); } - /* 找第一个无线设备 */ + /* 找第一个无线设备 - 通过 device_info 检查 flags 而非调用 is_wireless */ int wifi_idx = -1; for (u32 i = 0; i < ndev; i++) { - int rc = iswl(i); - if (rc == 1) { wifi_idx = (int)i; break; } + /* 使用 device_info 获取 flags,避免直接调用 is_wireless 导致崩溃 */ + u64 devinfo_fn = *(u64 *)(net_api + 16); + if (devinfo_fn) { + /* dkm_net_device_info 布局: name(8) + mac(6) + pad(2) + flags(4) = 20 字节 */ + u8 info[24]; + for (int k = 0; k < 24; k++) info[k] = 0; + int rc = ((int (*)(u32, void *))devinfo_fn)(i, info); + if (rc == 0) { + u32 flags = *(u32 *)(info + 16); + if (flags & 8) { wifi_idx = (int)i; break; } /* DKM_NET_F_WIRELESS = (1<<3) */ + } + } } if (wifi_idx < 0) { logl("[FirstInit] no wireless device found"); @@ -806,63 +1042,84 @@ static void net_select(setup_net *net, int idx) { static void draw_wifi_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int row, int active, u32 fg) { u32 card = CARD_BG; i64 x = card_x + 40; - i64 y = card_y + 132 + (i64)row * 64; - u32 fill = active == row ? 0xFFFFFFFF : (net->selected == row ? 0xFFDCECF8 : INPUT_BG); - fill_rounded_rect(fb, x, y, 390, 50, 9, fill); - stroke_rounded_rect(fb, x, y, 390, 50, 9, 1, active == row ? 0xFF7FA7C8 : card); - fb_text(fb, net->selected == row ? ">" : " ", x + 12, y + 16, fg, fill); + i64 y = card_y + 132 + (i64)row * 56; + u32 fill = active == row ? 0xFF45454A : (net->selected == row ? 0xFF3A3A3E : INPUT_BG); + fill_rounded_rect(fb, x, y, 390, 46, 3, fill); + stroke_rounded_rect(fb, x, y, 390, 46, 3, 1, active == row ? 0xFF6C6C72 : 0xFF404044); + fb_text(fb, net->selected == row ? ">" : " ", x + 12, y + 14, fg, fill); if (row < g_wifi_scan_count) { - fb_text(fb, g_wifi_ssids[row], x + 42, y + 8, fg, fill); - fb_text(fb, g_wifi_meta[row], x + 42, y + 28, 0xFF7F97AC, fill); + fb_text(fb, g_wifi_ssids[row], x + 42, y + 6, fg, fill); + fb_text(fb, g_wifi_meta[row], x + 42, y + 26, 0xFF808088, fill); } else { - /* 无扫描结果时的占位行 */ const char *placeholder = (g_wifi_scan_tried && g_wifi_scan_count == 0) ? "No networks found" : "(empty)"; - fb_text(fb, placeholder, x + 42, y + 16, 0xFF9AACC0, fill); + fb_text(fb, placeholder, x + 42, y + 14, 0xFF606068, fill); } } +/* 网络页面活跃元素索引 */ +#define NET_ACT_WIFI_BASE 0 +#define NET_ACT_SSID 4 +#define NET_ACT_PASS 5 +#define NET_ACT_SAVE 6 +#define NET_ACT_CONNECT 7 +#define NET_ACT_COUNT 8 + static void redraw_network_card(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int active, i64 mx, i64 my, u32 bg, u32 fg) { u32 card = CARD_BG; cursor_erase(fb); fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); draw_card(fb, card_x, card_y, 900, 600, card, bg); fb_bitmap_alpha(fb, g_txt_network_title, g_txt_network_title_w, g_txt_network_title_h, card_x+40, card_y+40, fg, card, 255); - fb_text(fb, "Choose a network", card_x + 40, card_y + 88, 0xFF7F97AC, card); + fb_text(fb, "Network", card_x+40, card_y+40+g_txt_network_title_h+2, 0xFF9098A0, card); + fb_text(fb, "Choose or enter network", card_x + 40, card_y + 92, 0xFF808088, card); + + /* 左侧: WiFi 列表 */ for (int r=0; rselected; - if (preview < g_wifi_scan_count) { - fb_text(fb, g_wifi_ssids[preview], card_x + 494, card_y + 182, fg, INPUT_BG); - fb_text(fb, g_wifi_meta[preview], card_x + 494, card_y + 210, 0xFF7F97AC, INPUT_BG); - } else { - fb_text(fb, g_wifi_scan_tried ? "No networks found" : "WiFi not initialized", - card_x + 494, card_y + 182, 0xFF9AACC0, INPUT_BG); - fb_text(fb, g_wifi_scan_tried ? "Check ath9k driver" : "Run ath9k driver first", - card_x + 494, card_y + 210, 0xFF9AACC0, INPUT_BG); - } - fb_text(fb, "Use Up/Down or click menu", card_x + 494, card_y + 260, 0xFF7F97AC, INPUT_BG); - fb_text(fb, "Enter selects highlighted item", card_x + 494, card_y + 288, 0xFF7F97AC, INPUT_BG); + /* 右侧: 手动输入区 */ + fb_text(fb, "Network name (SSID)", card_x + 470, card_y + 120, 0xFF808088, card); + draw_rounded_input(fb, card_x + 470, card_y + 148, 390, 44, net->ssid, 0, fg, card, active == NET_ACT_SSID); + + fb_text(fb, "Password", card_x + 470, card_y + 210, 0xFF808088, card); + draw_rounded_input(fb, card_x + 470, card_y + 238, 390, 44, net->password, 1, fg, card, active == NET_ACT_PASS); + + /* 保存按钮 */ + { + u32 btn_bg = active == NET_ACT_SAVE ? 0xFF45454A : 0xFF3A3A3E; + fill_rounded_rect(fb, card_x + 470, card_y + 310, 180, 44, 3, btn_bg); + stroke_rounded_rect(fb, card_x + 470, card_y + 310, 180, 44, 3, 1, active == NET_ACT_SAVE ? 0xFF6C6C72 : 0xFF404044); + fb_text(fb, "Save", card_x + 530, card_y + 322, fg, btn_bg); + } + /* 连接按钮 */ + { + u32 btn_bg = active == NET_ACT_CONNECT ? 0xFF4A6A8A : 0xFF3A5070; + fill_rounded_rect(fb, card_x + 680, card_y + 310, 180, 44, 3, btn_bg); + stroke_rounded_rect(fb, card_x + 680, card_y + 310, 180, 44, 3, 1, active == NET_ACT_CONNECT ? 0xFF6A8AAA : 0xFF404060); + fb_text(fb, "Connect", card_x + 725, card_y + 322, 0xFFE0E0E0, btn_bg); + } + + /* 状态提示 */ + if (net->connected) { + fb_text(fb, "Network profile saved", card_x + 470, card_y + 380, 0xFF5FAF6F, card); + } else if (net->ssid[0]) { + fb_text(fb, "Press Save or Connect", card_x + 470, card_y + 380, 0xFF808088, card); + } else { + fb_text(fb, "Select network or type SSID", card_x + 470, card_y + 380, 0xFF606068, card); + } + fb_text(fb, "Save=save config only", card_x + 470, card_y + 510, 0xFF606068, card); + fb_text(fb, "Connect=save and connect", card_x + 470, card_y + 535, 0xFF606068, card); - fb_text(fb, "Password", card_x + 470, card_y + 400, fg, card); - draw_rounded_input(fb, card_x + 470, card_y + 430, 390, 44, net->password, 1, fg, card, active == WIFI_SCAN_COUNT); - u32 btn = active == WIFI_SCAN_COUNT + 1 ? 0xFFFFFFFF : 0xFFDCECF8; - fill_rounded_rect(fb, card_x + 620, card_y + 500, 240, 48, 10, btn); - stroke_rounded_rect(fb, card_x + 620, card_y + 500, 240, 48, 10, 1, active == WIFI_SCAN_COUNT + 1 ? 0xFFB6C9DD : card); - fb_text(fb, "Connect", card_x + 690, card_y + 516, fg, btn); - if (net->connected) fb_text(fb, "Connection profile saved", card_x + 40, card_y + 515, 0xFF4F8A5F, card); - else fb_text(fb, "Select SSID, enter password, then Connect", card_x + 40, card_y + 555, 0xFF7F97AC, card); cursor_draw(fb, mx, my); } static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i64 *mx, i64 *my, u32 bg, u32 fg) { int active = 0, mcnt = 0, e0 = 0, shift = 0; u8 mpkt[3]; u8 prev_btns = 0; - int len = 0; - while (net->password[len]) len++; + int plen = 0; + int slen = 0; + while (net->password[plen]) plen++; + while (net->ssid[slen]) slen++; net_select(net, net->selected); redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); for (;;) { @@ -885,23 +1142,41 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i cursor_draw(fb, *mx, *my); } if ((btns & 1) && !(prev_btns & 1)) { + /* WiFi 列表点击 */ for (int r=0; r= card_x + 40 && *mx < card_x + 430 && *my >= ry && *my < ry + 50) { + i64 ry = card_y + 132 + (i64)r * 56; + if (*mx >= card_x + 40 && *mx < card_x + 430 && *my >= ry && *my < ry + 46) { active = r; net_select(net, r); - len = 0; + slen = 0; + while (net->ssid[slen]) slen++; + plen = 0; redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); } } - if (*mx >= card_x + 470 && *mx < card_x + 860 && *my >= card_y + 430 && *my < card_y + 474) { - active = WIFI_SCAN_COUNT; + /* SSID 输入框 */ + if (*mx >= card_x + 470 && *mx < card_x + 860 && *my >= card_y + 148 && *my < card_y + 192) { + active = NET_ACT_SSID; redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); } - if (*mx >= card_x + 620 && *mx < card_x + 860 && *my >= card_y + 500 && *my < card_y + 548) { + /* 密码输入框 */ + if (*mx >= card_x + 470 && *mx < card_x + 860 && *my >= card_y + 238 && *my < card_y + 282) { + active = NET_ACT_PASS; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + } + /* 保存按钮 */ + if (*mx >= card_x + 470 && *mx < card_x + 650 && *my >= card_y + 310 && *my < card_y + 354) { net->connected = 1; - logl("[FirstInit] wireless profile selected"); - redraw_network_card(fb, card_x, card_y, net, WIFI_SCAN_COUNT + 1, *mx, *my, bg, fg); + logl("[FirstInit] network profile saved"); + redraw_network_card(fb, card_x, card_y, net, NET_ACT_SAVE, *mx, *my, bg, fg); + for (int i=0;i<30;i++) delay_frame(); + return; + } + /* 连接按钮 */ + if (*mx >= card_x + 680 && *mx < card_x + 860 && *my >= card_y + 310 && *my < card_y + 354) { + net->connected = 1; + logl("[FirstInit] network connect requested"); + redraw_network_card(fb, card_x, card_y, net, NET_ACT_CONNECT, *mx, *my, bg, fg); for (int i=0;i<30;i++) delay_frame(); return; } @@ -920,7 +1195,7 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i e0=0; continue; } if (e0 && sc == 0x50) { - if (active < WIFI_SCAN_COUNT + 1) active++; + if (active < NET_ACT_COUNT - 1) active++; redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); e0=0; continue; } @@ -930,13 +1205,22 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i if (c == '\n') { if (active < WIFI_SCAN_COUNT) { net_select(net, active); - len = 0; - active = WIFI_SCAN_COUNT; - } else if (active == WIFI_SCAN_COUNT) { - active = WIFI_SCAN_COUNT + 1; - } else { + slen = 0; + while (net->ssid[slen]) slen++; + active = NET_ACT_PASS; + } else if (active == NET_ACT_SSID) { + active = NET_ACT_PASS; + } else if (active == NET_ACT_PASS) { + active = NET_ACT_CONNECT; + } else if (active == NET_ACT_SAVE) { net->connected = 1; - logl("[FirstInit] wireless profile selected"); + logl("[FirstInit] network profile saved"); + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + for (int i=0;i<30;i++) delay_frame(); + return; + } else if (active == NET_ACT_CONNECT) { + net->connected = 1; + logl("[FirstInit] network connect requested"); redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); for (int i=0;i<30;i++) delay_frame(); return; @@ -945,16 +1229,28 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i continue; } if (c == '\t') { - active = (active + 1) % (WIFI_SCAN_COUNT + 2); + active = (active + 1) % NET_ACT_COUNT; redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); continue; } - if (active == WIFI_SCAN_COUNT) { + /* SSID 输入 */ + if (active == NET_ACT_SSID) { if (c == 8) { - if (len > 0) net->password[--len] = 0; - } else if (len < 63 && c >= 32 && c <= 126) { - net->password[len++] = c; - net->password[len] = 0; + if (slen > 0) net->ssid[--slen] = 0; + } else if (slen < 31 && c >= 32 && c <= 126) { + net->ssid[slen++] = c; + net->ssid[slen] = 0; + } + net->connected = 0; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + } + /* 密码输入 */ + if (active == NET_ACT_PASS) { + if (c == 8) { + if (plen > 0) net->password[--plen] = 0; + } else if (plen < 63 && c >= 32 && c <= 126) { + net->password[plen++] = c; + net->password[plen] = 0; } net->connected = 0; redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); @@ -981,9 +1277,12 @@ static void sha256(const char *msg, u8 out[32]) { for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];} } +static u8 g_conf_data[1024]; +static u32 g_conf_size; + static void build_user_conf(const char *pc, const char *user, const char *pass, const setup_prefs *prefs, const setup_net *net) { u8 hash[32]; sha256(pass, hash); - static char conf[1024]; int p=0; + char *conf = (char *)g_conf_data; int p=0; const char *pre="DESHAB_USERCONF_V1\ncomputer="; for(int i=0;pre[i];i++) conf[p++]=pre[i]; for(int i=0;pc[i]&&p<740;i++) conf[p++]=pc[i]; @@ -1011,15 +1310,18 @@ static void build_user_conf(const char *pc, const char *user, const char *pass, const char *ni="\nnetwork.ip=dhcp"; for(int i=0;ni[i];i++) conf[p++]=ni[i]; const char *dns="\nnetwork.dns=auto"; for(int i=0;dns[i];i++) conf[p++]=dns[i]; conf[p++]='\n'; - /* XOR encrypt buffer with hash-derived stream; real disk write waits for FAT32 write support. */ + /* XOR encrypt buffer with hash-derived stream; DSK writes to FAT32 after return. */ for(int i=0;imagic != 0x44534B31424F4F54ULL) { logl("[FirstInit] bad context"); @@ -1049,13 +1351,20 @@ void dsk_entry(const dsk_boot_context *ctx) { fade_ring(fb, cx, cy, bg); logl("[FirstInit] ring faded"); - /* center text: Chinese bitmaps */ + /* center text: Chinese bitmaps + English subtitle below */ i64 tw = g_txt_welcome_w, tx = cx - tw/2, ty = cy - g_txt_welcome_h/2; + /* English subtitle sits below the Chinese bitmap, centered independently */ + const char *welcome_en = "Welcome to Deshab"; + i64 en_w = fb_text_width(welcome_en); + i64 en_x = cx - en_w/2, en_y = ty + g_txt_welcome_h + 6; + /* subtitle color: dimmer than WELCOME_FG so it reads as secondary */ + u32 sub_fg = 0xFFA8B8C8; logl("[FirstInit] fading in welcome"); for (u32 a=0; a<=255; a+=17) { - fill_gradient_rect(fb, tx-4, ty-4, tw+8, g_txt_welcome_h+8); + fill_gradient_rect(fb, tx-4, ty-4, tw+8, g_txt_welcome_h + 6 + ASCII_H + 4); fb_bitmap_alpha(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, bg_at_y(ty + g_txt_welcome_h / 2), a); + fb_text_alpha(fb, welcome_en, en_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a); delay_frame(); } logl("[FirstInit] welcome drawn"); @@ -1064,16 +1373,26 @@ void dsk_entry(const dsk_boot_context *ctx) { logl("[FirstInit] cross-fading to setup message"); i64 tw2 = g_txt_setup_w, tx2 = cx - tw2/2; - i64 area_x = tx2 < tx ? tx2 : tx; - i64 area_w = (tx2 + tw2 > tx + tw ? tx2 + tw2 : tx + tw) - area_x; - i64 area_h = (g_txt_setup_h > g_txt_welcome_h ? g_txt_setup_h : g_txt_welcome_h) + 8; + const char *setup_en = "Let's set up your system"; + i64 en2_w = fb_text_width(setup_en); + i64 en2_x = cx - en2_w/2; + i64 area_x = (tx2 < tx ? tx2 : tx); + if (en_x < area_x) area_x = en_x; + if (en2_x < area_x) area_x = en2_x; + i64 area_right = tx + tw; if (tx2 + tw2 > area_right) area_right = tx2 + tw2; + if (en_x + en_w > area_right) area_right = en_x + en_w; + if (en2_x + en2_w > area_right) area_right = en2_x + en2_w; + i64 area_w = area_right - area_x; + i64 area_h = (g_txt_setup_h > g_txt_welcome_h ? g_txt_setup_h : g_txt_welcome_h) + 6 + ASCII_H + 8; for (u32 step=0; step<=15; step++) { u32 a2 = step * 17; u32 a1 = 255 - a2; fill_gradient_rect(fb, area_x-4, ty-4, area_w+8, area_h); fb_bitmap_alpha(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, bg_at_y(ty + g_txt_welcome_h / 2), a1); + fb_text_alpha(fb, welcome_en, en_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a1); fb_bitmap_alpha(fb, g_txt_setup, tw2, g_txt_setup_h, tx2, ty, WELCOME_FG, bg_at_y(ty + g_txt_setup_h / 2), a2); + fb_text_alpha(fb, setup_en, en2_x, en_y, sub_fg, bg_at_y(en_y + ASCII_H/2), a2); delay_frame(); } logl("[FirstInit] setup message drawn"); @@ -1089,13 +1408,14 @@ void dsk_entry(const dsk_boot_context *ctx) { logl("[FI] fadein start"); /* Phase 1: fade in card background only (no title) — avoids title/card color mismatch flicker */ for (u32 a=0; a<=255; a+=17) { - draw_card_fade(fb, card_x, card_y, 900, 600, 12, a); + draw_card_fade(fb, card_x, card_y, 900, 600, 4, a); delay_frame(); } /* Phase 2: draw title once on fully-rendered card */ { u32 title_bg = blend(bg_at_y(card_y + 40 + g_txt_title_h/2), CARD_BG, 255); fb_bitmap_alpha(fb, g_txt_title, g_txt_title_w, g_txt_title_h, card_x+40, card_y+40, fg, title_bg, 255); + fb_text(fb, "Account Setup", card_x+40, card_y+40+g_txt_title_h+2, 0xFF9098A0, title_bg); } logl("[FI] fadein done"); logl("[FirstInit] initializing PS/2 mouse"); @@ -1103,10 +1423,89 @@ void dsk_entry(const dsk_boot_context *ctx) { i64 mx = cx, my = cy; { int field = 0; - while (field < 3) { - field = read_field(fb, card_x, card_y, field, pc, user, pass, &mx, &my, bg, fg); + for (;;) { + while (field < 3) { + field = read_field(fb, card_x, card_y, field, pc, user, pass, &mx, &my, bg, fg); + } + /* Validate: all fields must be non-empty */ + if (pc[0] != 0 && user[0] != 0 && pass[0] != 0) break; + /* Show warning message on card (overwrite hint area) */ + cursor_erase(fb); + { + u32 card = CARD_BG; + fill_rect(fb, card_x + 40, card_y + 550, 820, 45, card); + fb_text(fb, "Please fill in all fields", card_x + 40, card_y + 555, 0xFFFF6B6B, card); + fb_text(fb, "Press Enter to continue", card_x + 40, card_y + 575, 0xFFC0C0C0, card); + } + cursor_draw(fb, mx, my); /* 重绘鼠标光标,避免校验失败时光标消失 */ + logl("[FI] validation failed: empty field(s)"); + /* 等待用户确认:Enter/任意键回到 field 0,或点击输入框切换字段。 + * 避免死等 Enter 导致用户点击/输入无响应。 */ + int next_field = 0; + { + int release = 0; + int mcnt = 0; + u8 mpkt[3]; + u8 prev_btns = 0; + int done = 0; + while (!done) { + u8 st = inb(0x64); + if (!(st & 1)) { __asm__("pause"); continue; } + u8 data = inb(0x60); + if (st & 0x20) { + /* 鼠标数据:组装 3 字节包,同步位 bit 3 */ + if (mcnt == 0 && !(data & 0x08)) continue; + mpkt[mcnt++] = data; + if (mcnt < 3) continue; + mcnt = 0; + i64 dx = (i64)(i8)mpkt[1]; + i64 dy = (i64)(i8)mpkt[2]; + u8 btns = mpkt[0] & 0x07; + if (dx != 0 || dy != 0) { + cursor_erase(fb); + mx += dx; + my -= dy; /* invert Y: 鼠标上移 = 屏幕上移 */ + if (mx < 0) mx = 0; + if (my < 0) my = 0; + if ((u64)mx >= fb_w) mx = (i64)fb_w - 1; + if ((u64)my >= fb_h) my = (i64)fb_h - 1; + cursor_draw(fb, mx, my); + } + /* 左键点击输入框:切换到该字段重新输入 */ + if ((btns & 1) && !(prev_btns & 1)) { + for (int f = 0; f < 3; f++) { + i64 fy = card_y + 180 + (i64)f * 150; + if (my >= fy && my < fy + 44 && mx >= card_x + 40 && mx < card_x + 860) { + next_field = f; + done = 1; + break; + } + } + } + prev_btns = btns; + continue; + } + u8 sc = data; + if (sc == 0xF0) { release = 1; continue; } + if (release) { release = 0; continue; } + if (sc & 0x80) continue; /* Set 1 release code */ + /* Enter 或任意按键:退出等待,回到 field 0 重新输入 */ + next_field = 0; + done = 1; + } + } + /* Reset all fields and restart input from next_field. + * read_field() will call redraw_setup_card() which fully + * redraws the card (title, labels, inputs, hint) and cursor. */ + pc[0] = 0; user[0] = 0; pass[0] = 0; + field = next_field; } } + logl("[FI] all fields done"); + /* 重置光标状态,避免页面切换时崩溃 */ + cursor_bg_valid = 0; + cursor_cur_x = -100; + cursor_cur_y = -100; setup_prefs prefs; prefs.language = 0; /* zh-CN */ prefs.region = 0; /* CN */ @@ -1114,6 +1513,10 @@ void dsk_entry(const dsk_boot_context *ctx) { prefs.keyboard = 0; /* US-QWERTY */ prefs.theme = 0; /* Light */ read_prefs_page(fb, card_x, card_y, &prefs, &mx, &my, bg, fg); + /* 重置光标状态,避免页面切换时崩溃 */ + cursor_bg_valid = 0; + cursor_cur_x = -100; + cursor_cur_y = -100; /* 查询真实 WiFi 扫描结果(来自 ath9k 驱动通过 kernel_api.net.scan_*) */ wifi_query_scan(ctx->dkm_kernel_api); setup_net net; @@ -1121,23 +1524,36 @@ void dsk_entry(const dsk_boot_context *ctx) { net.connected = 0; net.ssid[0] = 0; net.password[0] = 0; - if (g_no_wireless_device) { - /* QEMU 或无无线网卡场景:跳过 WiFi 设置页面,network.mode=disabled */ - logl("[FirstInit] no wireless device, skipping WiFi setup page"); - net.connected = 0; - } else { - cursor_bg_valid = 0; - cursor_cur_x = -100; - cursor_cur_y = -100; - logl("[FirstInit] entering wireless setup page"); - read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg); - logl("[FirstInit] wireless setup page returned"); - } + /* 始终显示网络设置页面,即使无无线设备也展示页面让用户确认 */ + cursor_bg_valid = 0; + cursor_cur_x = -100; + cursor_cur_y = -100; + logl("[FirstInit] entering network setup page"); + read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg); + logl("[FirstInit] network setup page returned"); build_user_conf(pc, user, pass, &prefs, &net); + /* Pass config buffer to DSK via boot context reserved fields */ + { + dsk_boot_context *ctx_mut = (dsk_boot_context *)ctx; + ctx_mut->reserved[0] = (u64)(u64*)g_conf_data; + ctx_mut->reserved[1] = (u64)g_conf_size; + } + logl("[FirstInit] config ready for DSK to write"); + cursor_erase(fb); fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); - fb_bitmap_alpha(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 20, fg, bg_at_y(cy), 255); + fb_bitmap_alpha(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 30, fg, bg_at_y(cy), 255); + /* English subtitle below the Chinese done bitmap */ + { const char *done_en = "Setup Complete"; + i64 dw = fb_text_width(done_en); + fb_text(fb, done_en, cx - dw/2, cy + 2, 0xFFA8B8C8, bg_at_y(cy + 10)); + } + /* status line below the subtitle */ + { const char *status = "System starting..."; + i64 sw = fb_text_width(status); + fb_text(fb, status, cx - sw/2, cy + 28, 0xFF808088, bg_at_y(cy + 36)); + } logl("[FirstInit] user setup finished"); /* 短暂停留后返回,由 DSK 接管加载 shell */ diff --git a/CODE/firstInit/main.o b/CODE/firstInit/main.o index 582fbfa..efa29d2 100644 Binary files a/CODE/firstInit/main.o and b/CODE/firstInit/main.o differ diff --git a/CODE/firstInit/text_bitmaps.c b/CODE/firstInit/text_bitmaps.c index 2c693ee..a343ddc 100644 --- a/CODE/firstInit/text_bitmaps.c +++ b/CODE/firstInit/text_bitmaps.c @@ -1,97 +1,97 @@ /* Auto-generated */ -static const int g_txt_welcome_w = 360; -static const int g_txt_welcome_h = 54; -static const unsigned char g_txt_welcome[360*54] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 61, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 202, 137, 76, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 123, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 166, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 207, 118, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 255, 255, 247, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 255, 255, 255, 252, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 218, 255, 255, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 144, 243, 255, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 255, 255, 250, 173, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 161, 0, 0, 0, 0, 0, 0, 0, 0, 20, 70, 132, 209, 255, 255, 255, 255, 255, 226, 27, 5, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 228, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 245, 255, 255, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 255, 255, 255, 242, 8, 0, 0, 0, 0, 0, 0, 198, 255, 255, 255, 255, 255, 255, 255, 255, 254, 215, 120, 26, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 255, 251, 198, 136, 74, 17, 0, 0, 17, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 239, 255, 255, 242, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 255, 255, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 255, 255, 255, 238, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 252, 255, 255, 252, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 246, 255, 255, 255, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 168, 0, 0, 0, 0, 0, 0, 207, 255, 255, 237, 72, 12, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 51, 199, 188, 176, 169, 165, 161, 160, 160, 160, 160, 160, 160, 160, 160, 160, 130, 0, 0, 0, 0, 157, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 244, 8, 0, 0, 0, 0, 0, 200, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 255, 255, 255, 54, 0, 47, 245, 232, 222, 215, 211, 209, 208, 208, 208, 208, 208, 208, 220, 255, 255, 255, 223, 208, 208, 208, 208, 208, 208, 211, 214, 217, 222, 232, 242, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 252, 236, 217, 196, 142, 77, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 169, 0, 0, 0, 0, 213, 255, 255, 248, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 255, 255, 255, 76, 0, 0, 0, 0, 0, 193, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 249, 255, 255, 215, 0, 0, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 137, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 121, 0, 0, 0, 16, 254, 255, 255, 234, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 152, 0, 0, 0, 0, 0, 185, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 129, 0, 0, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 35, 139, 131, 124, 116, 109, 101, 96, 96, 96, 96, 96, 114, 255, 255, 255, 72, 0, 0, 0, 71, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 231, 140, 0, 0, 0, 0, 0, 178, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 233, 255, 255, 255, 112, 0, 0, 17, 87, 73, 64, 57, 52, 50, 48, 48, 48, 48, 48, 48, 100, 255, 255, 255, 113, 48, 48, 48, 48, 48, 49, 52, 55, 58, 63, 74, 84, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 186, 144, 147, 167, 193, 248, 255, 255, 255, 255, 255, 251, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 24, 0, 0, 0, 136, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 134, 53, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 11, 91, 223, 255, 255, 255, 255, 251, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 230, 0, 0, 0, 0, 206, 255, 255, 214, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 212, 255, 255, 255, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 221, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 8, 176, 255, 255, 255, 255, 211, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 172, 0, 0, 0, 27, 254, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 224, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 121, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 226, 255, 255, 226, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 181, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 4, 187, 255, 255, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 0, 0, 210, 255, 255, 114, 0, 0, 0, 119, 255, 255, 253, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 220, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 84, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 196, 255, 255, 255, 201, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 32, 250, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 86, 239, 121, 0, 0, 0, 0, 0, 15, 253, 255, 255, 57, 0, 0, 0, 213, 255, 255, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 255, 255, 255, 250, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 143, 255, 255, 251, 61, 0, 0, 0, 0, 79, 255, 255, 248, 6, 0, 0, 75, 255, 255, 255, 94, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 237, 2, 0, 0, 0, 0, 0, 0, 0, 80, 96, 84, 74, 69, 65, 64, 64, 64, 27, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 11, 231, 255, 255, 208, 240, 255, 255, 112, 0, 0, 0, 0, 97, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 220, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 248, 255, 255, 248, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 236, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 167, 255, 255, 255, 231, 24, 0, 0, 0, 153, 255, 255, 195, 0, 0, 0, 197, 255, 255, 243, 11, 0, 0, 0, 192, 235, 205, 170, 100, 0, 0, 0, 53, 255, 255, 255, 171, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 105, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 85, 240, 255, 255, 112, 0, 0, 0, 0, 90, 255, 255, 255, 88, 32, 32, 32, 32, 32, 32, 88, 255, 255, 255, 102, 32, 32, 32, 32, 32, 32, 32, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 43, 64, 72, 51, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 57, 72, 72, 55, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 4, 43, 69, 73, 55, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 58, 74, 69, 52, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 11, 77, 108, 118, 94, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 8, 201, 255, 255, 255, 191, 3, 0, 0, 227, 255, 255, 128, 0, 0, 64, 255, 255, 255, 159, 0, 0, 0, 0, 194, 255, 255, 255, 154, 0, 0, 0, 53, 153, 198, 249, 107, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 97, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 36, 248, 255, 255, 214, 2, 240, 255, 255, 112, 0, 0, 0, 0, 86, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 179, 244, 255, 255, 255, 255, 250, 191, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 136, 225, 255, 255, 255, 255, 255, 255, 210, 112, 7, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 29, 155, 241, 255, 255, 255, 255, 250, 175, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 130, 222, 255, 255, 255, 255, 255, 253, 202, 100, 3, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 5, 137, 242, 255, 255, 255, 255, 255, 212, 79, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 24, 229, 255, 255, 255, 130, 0, 49, 255, 255, 255, 61, 0, 0, 185, 255, 255, 255, 65, 0, 0, 0, 0, 197, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 8, 11, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 250, 245, 248, 255, 255, 255, 89, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 2, 194, 255, 255, 255, 90, 0, 240, 255, 255, 112, 0, 0, 0, 0, 81, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 25, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 181, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 217, 37, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 71, 238, 255, 255, 255, 255, 255, 255, 255, 255, 243, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 198, 21, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 12, 199, 255, 255, 255, 255, 255, 255, 255, 255, 255, 160, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 58, 250, 255, 255, 252, 57, 125, 255, 255, 234, 3, 0, 0, 22, 101, 201, 227, 2, 0, 0, 0, 0, 202, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 16, 3, 0, 0, 128, 255, 255, 255, 80, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 26, 223, 255, 255, 216, 2, 0, 240, 255, 255, 112, 0, 0, 0, 0, 87, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 214, 0, 0, 0, 0, 0, 0, 44, 229, 255, 255, 255, 255, 252, 230, 237, 255, 255, 255, 255, 255, 208, 9, 0, 0, 0, 0, 0, 0, 0, 0, 74, 252, 255, 255, 255, 255, 249, 231, 236, 254, 255, 255, 255, 255, 228, 18, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 87, 253, 255, 255, 245, 198, 185, 233, 255, 255, 255, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 90, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 199, 3, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 175, 255, 255, 255, 250, 231, 249, 255, 255, 255, 255, 255, 173, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 220, 208, 255, 255, 153, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 210, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 74, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 17, 192, 255, 90, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 95, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 255, 228, 0, 0, 0, 0, 0, 19, 222, 255, 255, 255, 217, 89, 11, 0, 0, 26, 126, 247, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 7, 231, 255, 255, 255, 194, 58, 4, 0, 0, 8, 82, 206, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 154, 247, 255, 251, 130, 15, 0, 0, 3, 117, 254, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 29, 243, 255, 255, 255, 226, 104, 42, 20, 26, 55, 130, 248, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 231, 90, 6, 0, 2, 66, 200, 255, 255, 255, 255, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 184, 255, 255, 255, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 255, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 71, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 21, 183, 2, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 103, 255, 255, 255, 112, 64, 64, 64, 64, 64, 64, 112, 255, 255, 255, 124, 64, 64, 64, 64, 64, 64, 64, 255, 255, 255, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 235, 0, 0, 0, 0, 0, 164, 255, 255, 255, 193, 12, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 252, 29, 0, 0, 0, 0, 0, 0, 75, 255, 255, 255, 215, 7, 0, 0, 0, 0, 0, 0, 18, 241, 255, 255, 227, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 247, 71, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 201, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 231, 30, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 188, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 226, 29, 0, 0, 0, 0, 0, 4, 182, 255, 255, 255, 238, 10, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 239, 255, 255, 255, 255, 241, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 68, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 111, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 236, 0, 0, 0, 0, 37, 252, 255, 255, 244, 25, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 164, 224, 224, 223, 4, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 21, 253, 255, 255, 240, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 1, 224, 255, 255, 243, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 22, 243, 255, 255, 255, 103, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, 255, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 65, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 119, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 255, 255, 255, 166, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 236, 255, 255, 236, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 206, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 255, 255, 229, 0, 0, 0, 0, 132, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 190, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 0, 0, 0, 0, 0, 0, 62, 64, 64, 64, 11, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 9, 0, 0, 0, 0, 0, 192, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 176, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 226, 255, 255, 255, 239, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 9, 125, 16, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 170, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 94, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 220, 255, 255, 255, 199, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255, 255, 217, 0, 0, 0, 0, 198, 255, 255, 255, 153, 112, 112, 112, 112, 112, 112, 112, 112, 112, 138, 255, 255, 255, 241, 1, 0, 0, 0, 0, 0, 35, 255, 255, 255, 252, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 254, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 140, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255, 232, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 1, 90, 226, 255, 32, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 210, 255, 255, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 9, 14, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 255, 255, 255, 194, 0, 0, 0, 0, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 254, 187, 115, 66, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 64, 108, 157, 219, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, 255, 13, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 255, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 255, 255, 246, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 179, 255, 255, 224, 0, 65, 202, 255, 255, 255, 56, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 95, 255, 255, 255, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 18, 197, 175, 0, 0, 0, 0, 0, 0, 145, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255, 255, 199, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 244, 255, 255, 244, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 226, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 255, 255, 255, 165, 0, 0, 0, 12, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 29, 227, 255, 255, 255, 255, 255, 255, 255, 232, 181, 112, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 4, 83, 164, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 253, 255, 255, 255, 30, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 254, 255, 255, 206, 255, 255, 255, 233, 16, 0, 0, 0, 0, 0, 0, 0, 0, 2, 237, 255, 255, 255, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 214, 255, 255, 238, 189, 255, 255, 255, 255, 255, 90, 0, 0, 240, 255, 255, 160, 0, 0, 99, 176, 219, 255, 255, 255, 255, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 55, 227, 255, 255, 119, 0, 0, 0, 0, 0, 175, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 252, 255, 255, 255, 124, 0, 0, 0, 27, 255, 255, 255, 255, 145, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 24, 0, 0, 0, 0, 0, 0, 0, 22, 160, 250, 255, 255, 255, 255, 255, 255, 255, 255, 254, 178, 35, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 73, 222, 255, 255, 255, 255, 255, 255, 255, 255, 234, 160, 209, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 43, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 46, 179, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 231, 255, 255, 222, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 47, 255, 255, 255, 255, 255, 255, 255, 255, 222, 117, 22, 0, 0, 240, 255, 255, 160, 0, 0, 56, 255, 255, 255, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 19, 219, 255, 255, 254, 85, 0, 0, 0, 0, 210, 255, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 255, 69, 0, 0, 0, 14, 255, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 94, 154, 199, 242, 255, 255, 255, 255, 255, 255, 243, 55, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 102, 254, 255, 255, 255, 255, 230, 184, 143, 98, 47, 2, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 255, 255, 32, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 52, 254, 255, 255, 185, 0, 42, 252, 255, 255, 251, 42, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 252, 61, 254, 255, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 202, 255, 255, 255, 255, 255, 255, 212, 89, 3, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 203, 255, 255, 255, 255, 177, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 39, 239, 255, 255, 247, 66, 0, 0, 8, 250, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 255, 255, 252, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 247, 11, 0, 0, 0, 0, 250, 255, 255, 255, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 34, 95, 176, 252, 255, 255, 255, 225, 8, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 39, 251, 255, 255, 255, 168, 45, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 255, 15, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 67, 0, 0, 149, 255, 255, 255, 187, 0, 0, 0, 0, 0, 0, 36, 253, 255, 255, 175, 0, 182, 255, 255, 238, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 124, 255, 255, 255, 255, 217, 88, 2, 0, 0, 0, 0, 0, 0, 244, 255, 255, 166, 0, 0, 0, 118, 251, 208, 143, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 246, 69, 0, 60, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 17, 240, 255, 255, 255, 172, 0, 0, 0, 0, 0, 207, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 16, 16, 16, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 248, 255, 255, 255, 88, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 150, 255, 255, 255, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 238, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 191, 0, 0, 0, 19, 238, 255, 255, 255, 80, 0, 0, 0, 0, 0, 168, 255, 255, 255, 59, 0, 64, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 8, 222, 255, 228, 101, 3, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 173, 0, 0, 0, 7, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 88, 254, 255, 255, 249, 96, 134, 255, 255, 254, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 254, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 255, 255, 64, 0, 0, 0, 0, 0, 141, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 66, 0, 0, 0, 0, 0, 125, 128, 128, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 142, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 212, 255, 255, 255, 81, 0, 0, 0, 0, 0, 0, 0, 0, 12, 226, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255, 255, 255, 182, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 17, 228, 255, 255, 255, 55, 0, 0, 0, 0, 111, 255, 255, 255, 221, 7, 0, 0, 0, 69, 255, 255, 255, 189, 0, 0, 0, 189, 255, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 74, 121, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 255, 255, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 255, 244, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 1, 145, 255, 255, 255, 255, 185, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 249, 14, 0, 0, 0, 0, 0, 232, 255, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 151, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 233, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 2, 169, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 253, 56, 0, 0, 0, 0, 0, 0, 0, 19, 238, 255, 255, 255, 112, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 174, 0, 0, 0, 0, 0, 4, 217, 255, 245, 114, 2, 0, 0, 20, 228, 255, 255, 254, 55, 0, 0, 0, 51, 253, 255, 255, 250, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 255, 255, 255, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 2, 68, 202, 255, 255, 255, 255, 236, 31, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 215, 33, 0, 0, 0, 0, 0, 0, 20, 216, 255, 255, 255, 164, 0, 0, 0, 0, 0, 0, 182, 255, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, 255, 122, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 218, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 23, 178, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 223, 29, 0, 0, 0, 0, 0, 4, 179, 255, 255, 255, 243, 15, 0, 0, 0, - 0, 0, 0, 0, 0, 96, 255, 255, 255, 247, 34, 0, 0, 0, 0, 0, 0, 80, 188, 34, 0, 0, 0, 5, 191, 255, 255, 255, 158, 0, 0, 0, 0, 0, 154, 255, 255, 255, 245, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 232, 255, 255, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 176, 128, 131, 149, 174, 236, 255, 255, 255, 255, 255, 234, 55, 0, 0, 0, 0, 0, 0, 0, 0, 32, 236, 255, 255, 255, 240, 128, 41, 1, 0, 19, 95, 225, 255, 255, 255, 242, 33, 0, 0, 0, 0, 0, 0, 87, 255, 255, 255, 242, 120, 33, 0, 0, 0, 0, 23, 150, 255, 255, 255, 255, 45, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 160, 255, 255, 255, 253, 128, 23, 0, 16, 63, 155, 243, 255, 255, 255, 255, 255, 255, 24, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 233, 100, 15, 0, 10, 77, 204, 255, 255, 255, 255, 120, 0, 0, 0, 0, - 0, 0, 0, 0, 50, 244, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 167, 255, 255, 255, 229, 19, 0, 0, 0, 0, 0, 15, 221, 255, 255, 255, 248, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255, 255, 199, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 247, 255, 255, 255, 255, 246, 102, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 242, 255, 255, 255, 255, 255, 252, 245, 255, 255, 255, 255, 255, 249, 79, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 255, 255, 230, 207, 196, 217, 253, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 47, 251, 255, 255, 255, 255, 255, 248, 255, 255, 255, 255, 255, 244, 220, 255, 255, 255, 62, 0, 0, 0, 0, 0, 192, 255, 255, 255, 195, 255, 255, 255, 255, 246, 255, 255, 255, 255, 255, 255, 180, 1, 0, 0, 0, 0, - 0, 0, 0, 28, 229, 255, 255, 255, 220, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 166, 255, 255, 255, 253, 65, 0, 0, 0, 0, 0, 0, 0, 41, 236, 255, 255, 255, 255, 158, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 247, 255, 255, 255, 255, 253, 172, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 144, 144, 144, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 255, 255, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 235, 255, 255, 255, 255, 255, 255, 255, 198, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 255, 255, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 103, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 228, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 169, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 164, 7, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 93, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 229, 55, 152, 255, 255, 255, 152, 0, 0, 0, 0, 0, 192, 255, 255, 255, 21, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 161, 6, 0, 0, 0, 0, 0, - 0, 0, 7, 208, 255, 255, 255, 254, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 194, 255, 255, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 233, 255, 255, 255, 255, 226, 100, 4, 0, 0, 0, 0, 0, 0, 0, 0, 120, 254, 255, 255, 255, 255, 255, 255, 255, 255, 216, 130, 58, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 241, 255, 255, 255, 160, 199, 255, 255, 255, 255, 255, 201, 102, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 254, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 255, 255, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 248, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 250, 232, 202, 138, 71, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 105, 211, 255, 255, 255, 255, 255, 255, 230, 127, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 176, 248, 255, 255, 255, 255, 255, 255, 253, 187, 72, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 46, 179, 252, 255, 255, 255, 255, 255, 222, 123, 10, 0, 73, 255, 255, 255, 250, 48, 0, 0, 0, 0, 192, 255, 255, 255, 0, 11, 147, 243, 255, 255, 255, 255, 255, 202, 74, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 77, 225, 255, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 99, 238, 255, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 216, 255, 255, 255, 255, 255, 223, 132, 31, 0, 0, 0, 0, 1, 144, 255, 255, 255, 219, 85, 70, 173, 255, 255, 255, 255, 255, 255, 244, 194, 143, 104, 66, 36, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 26, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 31, 179, 255, 255, 255, 255, 136, 0, 3, 122, 245, 255, 255, 255, 255, 255, 251, 199, 135, 81, 38, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 255, 255, 242, 0, 0, 0, 0, 0, 0, 119, 229, 229, 242, 255, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 16, 16, 16, 16, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 76, 97, 105, 86, 43, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 50, 86, 101, 108, 96, 66, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 16, 16, 16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 73, 101, 104, 81, 41, 0, 0, 0, 0, 0, 15, 16, 16, 16, 8, 0, 0, 0, 0, 12, 16, 16, 16, 0, 0, 0, 9, 67, 95, 103, 80, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 14, 169, 222, 12, 0, 0, 0, 0, 0, 0, 0, 0, 2, 80, 207, 255, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 178, 255, 255, 255, 255, 255, 227, 21, 0, 0, 0, 4, 163, 255, 255, 255, 222, 24, 0, 0, 0, 54, 175, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 230, 217, 205, 198, 191, 186, 182, 178, 176, 176, 176, 180, 184, 190, 198, 209, 223, 240, 254, 230, 10, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 129, 0, 0, 0, 0, 0, 36, 157, 250, 255, 255, 255, 249, 109, 0, 0, 0, 0, 29, 163, 252, 255, 255, 255, 255, 255, 255, 255, 255, 251, 228, 202, 183, 173, 69, 0, 0, 0, 0, 0, 63, 255, 255, 255, 239, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 255, 255, 252, 0, 0, 0, 0, 0, 0, 27, 253, 255, 255, 255, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 251, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 114, 249, 255, 255, 253, 57, 0, 0, 0, 7, 175, 255, 255, 255, 246, 48, 0, 0, 0, 0, 0, 0, 33, 132, 219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 137, 0, 0, 15, 95, 184, 252, 255, 255, 255, 255, 219, 51, 0, 0, 0, 0, 0, 0, 0, 34, 148, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 190, 1, 0, 0, 0, 0, 8, 217, 255, 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 205, 255, 255, 255, 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255, 236, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 210, 255, 152, 0, 0, 0, 0, 14, 184, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 120, 184, 231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 255, 255, 255, 145, 0, 12, 194, 255, 255, 255, 255, 255, 249, 141, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 85, 163, 235, 255, 255, 255, 255, 255, 255, 255, 54, 0, 0, 0, 0, 0, 25, 141, 229, 255, 246, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 112, 112, 112, 112, 5, 0, 0, 0, 0, 0, 0, 150, 255, 252, 207, 126, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 193, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 124, 20, 0, 0, 0, 0, 0, 3, 149, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 66, 99, 127, 152, 175, 195, 214, 231, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 255, 255, 255, 153, 0, 0, 5, 168, 255, 255, 254, 172, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 39, 92, 135, 171, 201, 219, 199, 0, 0, 0, 0, 0, 0, 0, 0, 8, 118, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 52, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 17, 29, 39, 47, 54, 61, 66, 70, 74, 77, 78, 79, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 80, 80, 80, 49, 0, 0, 0, 8, 210, 143, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +static const int g_txt_welcome_w = 270; +static const int g_txt_welcome_h = 42; +static const unsigned char g_txt_welcome[270*42] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 105, 70, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 237, 178, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 134, 212, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 118, 242, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 226, 128, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 255, 255, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 254, 255, 255, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 108, 0, 0, 0, 0, 0, 0, 33, 90, 166, 242, 255, 255, 243, 47, 12, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 255, 255, 198, 0, 0, 0, 0, 0, 216, 255, 255, 255, 255, 255, 238, 183, 105, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 250, 255, 244, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 245, 255, 254, 30, 0, 0, 0, 0, 222, 255, 251, 174, 109, 46, 1, 0, 0, 73, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 255, 255, 191, 104, 104, 104, 104, 104, 104, 104, 165, 255, 255, 165, 104, 104, 104, 104, 104, 104, 104, 106, 255, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 28, 147, 136, 127, 123, 120, 120, 120, 120, 120, 120, 120, 103, 0, 0, 0, 174, 255, 255, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, 114, 0, 0, 0, 0, 215, 255, 232, 0, 0, 0, 0, 0, 0, 64, 255, 255, 154, 64, 64, 64, 64, 154, 255, 255, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 255, 255, 53, 0, 166, 174, 164, 160, 157, 156, 156, 156, 156, 174, 255, 255, 204, 156, 156, 156, 156, 157, 160, 163, 170, 180, 114, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 192, 192, 192, 192, 187, 170, 150, 98, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 192, 192, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 192, 192, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 185, 0, 0, 0, 231, 255, 242, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 193, 0, 0, 0, 0, 208, 255, 232, 0, 0, 0, 0, 0, 0, 59, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 29, 252, 255, 215, 0, 0, 228, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 33, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, 182, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 44, 231, 224, 216, 209, 201, 200, 200, 200, 203, 255, 255, 137, 0, 0, 32, 255, 255, 252, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 235, 36, 0, 0, 0, 0, 0, 0, 11, 247, 255, 251, 12, 0, 0, 0, 200, 255, 232, 0, 0, 0, 0, 0, 0, 56, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 58, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 150, 0, 0, 173, 184, 174, 170, 166, 164, 164, 164, 164, 181, 255, 255, 208, 164, 164, 164, 164, 166, 169, 172, 180, 190, 120, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 251, 236, 241, 255, 255, 255, 255, 255, 250, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 255, 255, 88, 0, 0, 93, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 130, 93, 23, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 53, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 14, 240, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 6, 58, 149, 251, 255, 255, 255, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255, 37, 0, 0, 163, 255, 249, 88, 84, 84, 84, 84, 84, 84, 84, 84, 179, 255, 255, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 122, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 57, 240, 255, 255, 225, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 235, 0, 0, 7, 238, 255, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 247, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 0, 12, 235, 255, 255, 255, 148, 0, 0, 0, 42, 68, 68, 68, 68, 68, 68, 68, 103, 255, 255, 159, 68, 68, 68, 68, 68, 68, 68, 68, 3, 0, 0, 0, 0, 0, 0, 0, 0, 26, 255, 255, 207, 140, 140, 140, 140, 140, 140, 140, 187, 255, 255, 187, 140, 140, 140, 140, 140, 140, 140, 141, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 21, 176, 68, 0, 0, 0, 0, 199, 255, 178, 0, 0, 84, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 28, 254, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 148, 0, 0, 0, 150, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0, 25, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 207, 255, 255, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 226, 255, 232, 25, 0, 0, 11, 250, 255, 120, 0, 0, 199, 255, 241, 9, 0, 4, 59, 29, 2, 0, 0, 0, 115, 255, 255, 107, 0, 0, 0, 0, 0, 45, 137, 125, 117, 113, 112, 112, 63, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 22, 242, 255, 205, 244, 255, 148, 0, 0, 0, 136, 255, 255, 223, 216, 216, 216, 216, 223, 255, 255, 235, 216, 216, 216, 216, 216, 246, 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 208, 144, 144, 144, 144, 144, 144, 144, 189, 255, 255, 189, 144, 144, 144, 144, 144, 144, 144, 145, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 122, 255, 255, 244, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 172, 255, 255, 194, 3, 0, 80, 255, 255, 57, 0, 66, 255, 255, 155, 0, 0, 17, 255, 255, 249, 58, 0, 0, 173, 255, 255, 39, 0, 0, 0, 0, 0, 80, 255, 255, 255, 255, 255, 255, 140, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 157, 255, 255, 80, 244, 255, 148, 0, 0, 0, 130, 255, 255, 48, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 192, 255, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 37, 0, 0, 0, 0, 0, 0, 5, 79, 145, 173, 179, 152, 88, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 111, 159, 179, 180, 156, 101, 17, 0, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 13, 103, 154, 180, 171, 123, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 109, 160, 181, 177, 152, 96, 12, 0, 0, 0, 0, 0, 0, 80, 255, 255, 128, 0, 55, 159, 205, 214, 182, 105, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 207, 255, 255, 136, 0, 155, 255, 242, 4, 0, 187, 255, 255, 60, 0, 0, 20, 255, 255, 255, 34, 0, 0, 0, 15, 71, 0, 0, 0, 0, 0, 0, 80, 255, 255, 250, 249, 255, 255, 131, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 63, 254, 255, 210, 1, 244, 255, 148, 0, 0, 0, 125, 255, 255, 48, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 192, 255, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, 75, 0, 0, 0, 0, 0, 76, 223, 255, 255, 255, 255, 255, 255, 225, 63, 0, 0, 0, 0, 0, 0, 0, 1, 132, 251, 255, 255, 255, 255, 255, 255, 241, 93, 0, 0, 0, 0, 0, 8, 255, 255, 200, 0, 79, 240, 255, 255, 255, 255, 255, 245, 76, 0, 0, 0, 0, 0, 0, 2, 131, 250, 255, 255, 255, 255, 255, 255, 236, 76, 0, 0, 0, 0, 0, 80, 255, 255, 128, 121, 253, 255, 255, 255, 255, 255, 223, 48, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 238, 255, 254, 66, 229, 255, 167, 0, 0, 26, 111, 184, 1, 0, 0, 25, 255, 255, 254, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 14, 2, 0, 96, 255, 255, 123, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 92, 249, 255, 84, 0, 244, 255, 148, 0, 0, 0, 130, 255, 255, 48, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 192, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 91, 0, 0, 0, 0, 108, 254, 255, 255, 228, 182, 191, 239, 255, 255, 250, 62, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 210, 175, 180, 222, 255, 255, 254, 78, 0, 0, 0, 0, 8, 255, 255, 200, 93, 254, 255, 208, 144, 165, 239, 255, 255, 242, 25, 0, 0, 0, 0, 0, 154, 255, 255, 255, 244, 214, 214, 248, 255, 255, 249, 50, 0, 0, 0, 0, 80, 255, 255, 210, 255, 255, 219, 175, 197, 252, 255, 255, 242, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 255, 255, 239, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 118, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 72, 206, 1, 0, 244, 255, 148, 0, 0, 0, 138, 255, 255, 48, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 192, 255, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 246, 255, 255, 104, 0, 0, 0, 64, 252, 255, 245, 95, 3, 0, 0, 13, 150, 255, 255, 225, 4, 0, 0, 0, 0, 29, 253, 255, 249, 71, 0, 0, 0, 0, 95, 255, 255, 206, 0, 0, 0, 0, 8, 255, 255, 235, 249, 251, 101, 0, 0, 0, 31, 225, 255, 255, 124, 0, 0, 0, 0, 62, 255, 255, 254, 112, 5, 0, 0, 10, 153, 255, 255, 174, 0, 0, 0, 0, 80, 255, 255, 255, 253, 107, 0, 0, 0, 35, 211, 255, 255, 202, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 157, 255, 255, 255, 248, 12, 0, 0, 0, 0, 0, 0, 0, 0, 58, 255, 255, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 115, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 0, 19, 0, 0, 244, 255, 148, 0, 0, 0, 146, 255, 255, 243, 240, 240, 240, 240, 243, 255, 255, 247, 240, 240, 240, 240, 240, 252, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, 255, 111, 0, 0, 0, 202, 255, 255, 96, 0, 0, 0, 0, 0, 2, 211, 255, 255, 83, 0, 0, 0, 0, 76, 255, 255, 179, 0, 0, 0, 0, 0, 0, 200, 232, 229, 2, 0, 0, 0, 8, 255, 255, 255, 255, 92, 0, 0, 0, 0, 0, 98, 255, 255, 176, 0, 0, 0, 0, 153, 255, 255, 160, 0, 0, 0, 0, 0, 9, 238, 255, 240, 0, 0, 0, 0, 80, 255, 255, 255, 130, 0, 0, 0, 0, 0, 34, 249, 255, 255, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 17, 233, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 255, 246, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 2, 0, 52, 255, 255, 120, 0, 0, 0, 0, 120, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 154, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 205, 136, 136, 136, 136, 136, 136, 136, 184, 255, 255, 184, 136, 136, 136, 136, 136, 136, 136, 137, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 98, 0, 0, 43, 255, 255, 223, 2, 0, 0, 0, 0, 0, 0, 125, 255, 255, 148, 0, 0, 0, 0, 62, 255, 255, 205, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, 172, 0, 0, 0, 0, 0, 0, 40, 255, 255, 191, 0, 0, 0, 0, 35, 48, 48, 18, 0, 0, 0, 0, 0, 4, 220, 255, 255, 8, 0, 0, 0, 80, 255, 255, 243, 11, 0, 0, 0, 0, 0, 0, 166, 255, 255, 139, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 188, 255, 255, 255, 74, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 196, 255, 232, 0, 0, 11, 131, 138, 0, 52, 255, 255, 120, 0, 0, 0, 0, 140, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 49, 80, 80, 80, 80, 80, 80, 80, 144, 255, 255, 134, 80, 80, 80, 80, 80, 80, 80, 80, 4, 0, 0, 0, 0, 0, 0, 0, 0, 54, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 5, 255, 255, 255, 84, 0, 0, 106, 255, 255, 241, 212, 212, 212, 212, 212, 212, 212, 227, 255, 255, 195, 0, 0, 0, 0, 9, 237, 255, 255, 159, 47, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, 57, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 62, 125, 214, 255, 255, 255, 12, 0, 0, 0, 80, 255, 255, 179, 0, 0, 0, 0, 0, 0, 0, 99, 255, 255, 190, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 255, 227, 12, 0, 0, 0, 0, 0, 0, 0, 186, 255, 255, 255, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 198, 255, 232, 3, 100, 230, 255, 166, 0, 52, 255, 255, 120, 0, 0, 0, 17, 223, 255, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 0, 0, 125, 107, 0, 0, 0, 0, 116, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 224, 184, 184, 184, 184, 184, 184, 184, 213, 255, 255, 213, 184, 184, 184, 184, 184, 184, 184, 185, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255, 255, 61, 0, 0, 135, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 213, 0, 0, 0, 0, 0, 94, 254, 255, 255, 255, 244, 204, 155, 92, 18, 0, 0, 0, 0, 0, 0, 8, 255, 255, 240, 2, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 0, 0, 0, 28, 113, 175, 223, 254, 255, 255, 255, 255, 255, 255, 12, 0, 0, 0, 80, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 212, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 176, 255, 254, 191, 255, 255, 140, 0, 0, 0, 0, 0, 0, 7, 246, 255, 255, 255, 220, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 1, 232, 255, 246, 218, 255, 255, 255, 190, 0, 52, 255, 255, 120, 0, 73, 200, 246, 255, 255, 250, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 0, 153, 255, 249, 58, 0, 0, 0, 143, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 254, 13, 0, 0, 147, 255, 255, 184, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 94, 0, 0, 0, 0, 0, 0, 66, 191, 252, 255, 255, 255, 255, 255, 247, 141, 8, 0, 0, 0, 0, 8, 255, 255, 211, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 0, 4, 139, 250, 255, 255, 255, 255, 251, 205, 130, 215, 255, 255, 12, 0, 0, 0, 80, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 55, 255, 255, 223, 0, 0, + 0, 0, 0, 0, 0, 0, 52, 255, 255, 182, 27, 246, 255, 250, 40, 0, 0, 0, 0, 0, 93, 255, 255, 175, 255, 255, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 97, 255, 255, 255, 255, 255, 209, 91, 6, 0, 52, 255, 255, 120, 0, 13, 241, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 0, 44, 241, 255, 236, 39, 0, 0, 178, 255, 245, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 150, 255, 255, 215, 0, 0, 0, 132, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 69, 113, 161, 226, 255, 255, 255, 172, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 0, 153, 255, 255, 242, 152, 91, 48, 8, 0, 0, 196, 255, 255, 12, 0, 0, 0, 80, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 209, 0, 0, + 0, 0, 0, 0, 0, 0, 190, 255, 255, 66, 0, 131, 255, 255, 184, 0, 0, 0, 0, 0, 204, 255, 250, 22, 206, 255, 228, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 112, 255, 255, 255, 210, 81, 1, 0, 0, 0, 55, 255, 255, 123, 0, 0, 163, 233, 177, 88, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 0, 0, 70, 251, 255, 229, 38, 2, 239, 255, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 41, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 13, 234, 255, 255, 130, 0, 0, 0, 98, 255, 255, 182, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 251, 255, 255, 53, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 37, 254, 255, 248, 42, 0, 0, 0, 0, 0, 0, 196, 255, 255, 12, 0, 0, 0, 80, 255, 255, 189, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 184, 0, 0, + 0, 0, 0, 0, 0, 84, 255, 255, 191, 0, 0, 11, 229, 255, 255, 78, 0, 0, 0, 91, 255, 255, 156, 0, 93, 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 4, 213, 223, 92, 2, 0, 0, 0, 0, 0, 58, 255, 255, 131, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 0, 0, 0, 100, 255, 255, 235, 112, 255, 255, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 255, 237, 2, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 0, 0, 150, 255, 255, 252, 28, 0, 0, 0, 33, 255, 255, 244, 16, 0, 0, 0, 0, 0, 0, 64, 255, 255, 235, 0, 0, 0, 0, 115, 160, 160, 14, 0, 0, 0, 0, 0, 0, 155, 255, 255, 109, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 99, 255, 255, 183, 0, 0, 0, 0, 0, 0, 34, 241, 255, 255, 12, 0, 0, 0, 80, 255, 255, 250, 24, 0, 0, 0, 0, 0, 0, 183, 255, 255, 129, 0, 0, + 0, 0, 0, 0, 21, 232, 255, 255, 55, 0, 0, 0, 93, 255, 255, 159, 0, 0, 18, 233, 255, 250, 34, 0, 2, 208, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 33, 7, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 255, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 174, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 200, 0, 0, 0, 0, 17, 155, 255, 255, 255, 127, 0, 0, 0, 0, 0, 185, 255, 255, 157, 1, 0, 0, 0, 0, 1, 178, 255, 255, 163, 0, 0, 0, 0, 159, 255, 255, 69, 0, 0, 0, 0, 0, 0, 160, 255, 255, 104, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 108, 255, 255, 196, 0, 0, 0, 0, 0, 31, 208, 255, 255, 255, 12, 0, 0, 0, 80, 255, 255, 255, 169, 1, 0, 0, 0, 0, 65, 255, 255, 255, 45, 0, 0, + 0, 0, 0, 0, 177, 255, 255, 167, 0, 0, 0, 0, 1, 189, 103, 1, 0, 5, 189, 255, 255, 137, 0, 0, 0, 68, 255, 255, 249, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 237, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 104, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 221, 96, 100, 119, 169, 241, 255, 255, 255, 177, 2, 0, 0, 0, 0, 0, 46, 246, 255, 255, 183, 59, 4, 4, 44, 169, 255, 255, 248, 42, 0, 0, 0, 0, 78, 255, 255, 223, 78, 7, 0, 0, 5, 101, 252, 255, 255, 45, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 66, 255, 255, 255, 120, 12, 3, 41, 129, 241, 255, 255, 255, 255, 16, 0, 0, 0, 80, 255, 255, 255, 255, 171, 37, 0, 14, 101, 240, 255, 255, 173, 0, 0, 0, + 0, 0, 0, 120, 255, 255, 242, 26, 0, 0, 0, 0, 0, 0, 0, 0, 1, 162, 255, 255, 217, 11, 0, 0, 0, 0, 144, 255, 255, 251, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 224, 81, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 61, 250, 255, 255, 255, 203, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 253, 26, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 5, 255, 255, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 134, 2, 0, 0, 0, 0, 0, 0, 0, 79, 246, 255, 255, 255, 253, 252, 255, 255, 255, 251, 89, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 247, 222, 218, 245, 255, 255, 255, 156, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 2, 200, 255, 255, 255, 255, 252, 255, 255, 255, 226, 215, 255, 255, 56, 0, 0, 0, 80, 255, 255, 190, 251, 255, 255, 250, 255, 255, 255, 255, 215, 18, 0, 0, 0, + 0, 0, 85, 254, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 9, 167, 255, 255, 244, 48, 0, 0, 0, 0, 0, 4, 169, 255, 255, 255, 179, 33, 0, 0, 0, 0, 0, 0, 0, 111, 253, 255, 255, 255, 255, 211, 110, 25, 0, 0, 0, 0, 0, 0, 0, 0, 15, 44, 44, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 255, 152, 0, 0, 0, 0, 0, 0, 0, 77, 245, 255, 252, 235, 255, 255, 251, 159, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 46, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, 224, 149, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 191, 255, 255, 255, 255, 255, 255, 207, 57, 0, 0, 0, 0, 0, 0, 0, 4, 126, 241, 255, 255, 255, 255, 255, 255, 244, 125, 2, 0, 0, 0, 0, 8, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 16, 255, 255, 192, 0, 0, 0, 0, 19, 178, 255, 255, 255, 255, 255, 254, 170, 26, 148, 255, 255, 165, 0, 0, 0, 80, 255, 255, 128, 80, 238, 255, 255, 255, 255, 254, 166, 16, 0, 0, 0, 0, + 0, 0, 96, 238, 255, 196, 2, 0, 0, 0, 0, 0, 0, 0, 57, 213, 255, 255, 249, 72, 0, 0, 0, 0, 0, 0, 0, 3, 154, 255, 255, 255, 250, 158, 61, 0, 0, 0, 0, 137, 255, 255, 210, 113, 179, 255, 255, 255, 255, 210, 152, 102, 63, 30, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 18, 0, 0, 0, 0, 0, 0, 0, 252, 255, 155, 0, 0, 0, 0, 0, 17, 156, 255, 255, 248, 80, 18, 158, 255, 255, 255, 255, 214, 141, 79, 34, 2, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 255, 255, 116, 0, 0, 0, 0, 115, 170, 179, 232, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 76, 76, 76, 76, 71, 53, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 106, 135, 141, 115, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 71, 117, 138, 143, 126, 81, 12, 0, 0, 0, 0, 0, 0, 2, 76, 76, 59, 0, 0, 0, 0, 0, 0, 0, 4, 76, 76, 57, 0, 0, 0, 0, 0, 0, 43, 112, 141, 134, 100, 30, 0, 0, 21, 76, 76, 75, 5, 0, 0, 23, 76, 76, 38, 0, 14, 91, 132, 139, 109, 36, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 185, 37, 0, 0, 0, 0, 0, 0, 51, 171, 255, 255, 255, 245, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 249, 255, 255, 255, 98, 0, 0, 2, 156, 255, 255, 209, 14, 0, 0, 54, 171, 251, 255, 255, 255, 255, 255, 255, 255, 241, 228, 218, 212, 206, 201, 197, 198, 199, 201, 207, 217, 227, 243, 255, 105, 0, 0, 0, 0, 0, 0, 2, 255, 255, 162, 0, 0, 0, 24, 135, 242, 255, 255, 229, 61, 0, 0, 0, 65, 193, 255, 255, 255, 255, 255, 255, 249, 225, 203, 170, 12, 0, 0, 0, 80, 255, 255, 231, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 126, 0, 0, 0, 0, 77, 255, 255, 255, 255, 253, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 248, 255, 255, 228, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 212, 255, 171, 0, 0, 0, 115, 255, 255, 240, 36, 0, 0, 0, 0, 0, 25, 111, 189, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 229, 6, 0, 0, 0, 0, 0, 0, 10, 255, 255, 170, 0, 64, 188, 252, 255, 255, 255, 174, 20, 0, 0, 0, 0, 0, 0, 45, 153, 233, 255, 255, 255, 255, 255, 255, 88, 0, 0, 0, 0, 190, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 212, 212, 112, 0, 0, 0, 0, 13, 253, 255, 252, 191, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 247, 186, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 134, 31, 0, 0, 0, 3, 148, 254, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 52, 99, 136, 164, 188, 209, 228, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 143, 0, 0, 0, 0, 0, 0, 0, 18, 255, 255, 178, 0, 6, 161, 255, 255, 207, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 107, 158, 185, 211, 215, 2, 0, 0, 0, 0, 0, 67, 188, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 74, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 16, 27, 35, 42, 48, 53, 56, 58, 59, 21, 0, 0, 0, 0, 0, 0, 0, 5, 60, 60, 42, 0, 0, 4, 152, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -static const int g_txt_setup_w = 384; -static const int g_txt_setup_h = 30; -static const unsigned char g_txt_setup[384*30] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 42, 56, 14, 0, 0, 0, 0, 0, 0, 0, 82, 158, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 56, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 47, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 130, 42, 0, 0, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 56, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 3, 0, 0, 0, 0, 0, 0, 1, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 115, 14, 0, 0, 0, 29, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 115, 14, 0, 0, 0, 29, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 17, 0, 0, 0, 0, 0, 0, 0, 40, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 51, 103, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 89, 111, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 172, 255, 44, 0, 0, 0, 0, 0, 0, 0, 140, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 161, 4, 0, 0, 0, 0, 0, 0, 0, 4, 192, 192, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 72, 0, 0, 220, 255, 32, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 234, 15, 13, 183, 173, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 33, 27, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 156, 255, 107, 0, 0, 0, 0, 0, 0, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 235, 15, 0, 0, 116, 255, 196, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 222, 198, 14, 0, 0, 0, 0, 60, 120, 120, 120, 120, 120, 120, 120, 43, 0, 0, 0, 0, 0, 0, 0, 0, 87, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 128, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 235, 15, 0, 0, 116, 255, 196, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 250, 147, 0, 0, 0, 0, 0, 0, 159, 245, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 34, 58, 91, 124, 157, 198, 247, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 88, 186, 79, 0, 0, 0, 0, 0, 0, 19, 237, 250, 39, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 153, 255, 25, 0, 0, 36, 46, 40, 40, 40, 103, 255, 202, 40, 40, 40, 46, 58, 19, 0, 0, 91, 177, 170, 164, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 164, 171, 177, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 165, 1, 0, 0, 0, 0, 0, 0, 0, 254, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 72, 147, 232, 255, 93, 0, 215, 255, 11, 26, 218, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 110, 0, 19, 224, 255, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, 0, 0, 0, 0, 149, 255, 100, 0, 0, 0, 0, 0, 0, 0, 240, 255, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 201, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 124, 0, 0, 0, 198, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 177, 255, 197, 9, 0, 0, 0, 130, 255, 255, 255, 255, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 190, 168, 168, 184, 255, 179, 168, 168, 179, 255, 201, 168, 168, 168, 253, 221, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 124, 0, 0, 0, 198, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 255, 101, 0, 0, 0, 0, 0, 0, 220, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 203, 211, 215, 222, 229, 241, 253, 255, 255, 255, 255, 255, 237, 208, 184, 161, 145, 132, 32, 0, 0, 0, 0, 0, 0, 175, 255, 156, 0, 0, 0, 0, 0, 0, 0, 117, 255, 167, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 160, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 104, 0, 0, 0, 5, 46, 36, 32, 32, 32, 32, 32, 39, 255, 221, 32, 32, 32, 32, 32, 33, 42, 46, 0, 0, 0, 0, 0, 0, 13, 208, 255, 130, 0, 0, 0, 0, 0, 0, 0, 247, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 88, 116, 158, 208, 252, 255, 255, 233, 175, 94, 0, 211, 255, 3, 58, 240, 254, 107, 0, 0, 0, 0, 0, 0, 0, 0, 8, 236, 230, 8, 0, 0, 54, 250, 252, 57, 0, 108, 166, 157, 152, 152, 152, 112, 0, 0, 0, 0, 5, 46, 36, 32, 32, 32, 32, 32, 39, 255, 221, 32, 32, 32, 32, 32, 33, 42, 46, 0, 0, 0, 0, 0, 0, 149, 144, 138, 132, 128, 128, 128, 128, 248, 252, 0, 0, 0, 0, 0, 142, 255, 93, 0, 0, 0, 0, 0, 0, 0, 225, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 20, 245, 229, 10, 0, 0, 40, 255, 210, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 188, 255, 177, 2, 0, 0, 135, 255, 73, 8, 8, 8, 155, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 64, 0, 0, 48, 255, 32, 0, 0, 32, 255, 96, 0, 0, 0, 248, 211, 0, 0, 0, 0, 0, 0, 0, 20, 245, 229, 10, 0, 0, 40, 255, 210, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 252, 20, 0, 0, 0, 0, 0, 34, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 200, 183, 170, 157, 147, 137, 239, 246, 99, 35, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 252, 232, 15, 0, 0, 38, 45, 37, 32, 32, 46, 154, 48, 32, 32, 33, 41, 49, 17, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 67, 95, 89, 88, 88, 88, 88, 88, 100, 113, 88, 92, 103, 34, 0, 0, 31, 58, 51, 45, 40, 40, 40, 40, 40, 188, 255, 100, 40, 40, 40, 40, 40, 40, 43, 47, 56, 25, 0, 0, 0, 24, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0, 27, 228, 253, 42, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 255, 255, 218, 210, 255, 102, 0, 0, 0, 0, 207, 255, 0, 0, 48, 236, 255, 110, 0, 0, 0, 0, 0, 0, 0, 98, 255, 116, 0, 0, 0, 0, 125, 255, 150, 0, 160, 255, 255, 255, 255, 255, 181, 0, 0, 0, 0, 24, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 245, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 104, 0, 0, 0, 150, 255, 225, 192, 192, 192, 192, 192, 192, 192, 192, 192, 34, 0, 0, 0, 0, 0, 17, 220, 248, 58, 0, 0, 161, 255, 55, 0, 0, 0, 152, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 136, 96, 96, 126, 255, 116, 96, 96, 116, 255, 156, 96, 96, 96, 251, 211, 0, 0, 0, 0, 0, 0, 0, 134, 255, 104, 0, 0, 0, 150, 255, 225, 192, 192, 192, 192, 192, 192, 192, 192, 192, 34, 0, 0, 0, 0, 0, 0, 235, 195, 0, 0, 0, 0, 0, 0, 119, 255, 140, 48, 48, 48, 48, 48, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 254, 134, 0, 0, 0, 7, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 94, 0, 0, 0, 184, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, - 0, 216, 230, 212, 236, 255, 214, 218, 99, 0, 0, 22, 145, 21, 0, 0, 0, 75, 246, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 151, 129, 121, 120, 120, 120, 120, 124, 255, 234, 120, 120, 120, 120, 120, 122, 141, 144, 0, 0, 0, 0, 0, 0, 0, 0, 48, 75, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 72, 18, 0, 112, 255, 88, 0, 0, 0, 0, 204, 255, 0, 0, 0, 64, 250, 177, 2, 0, 0, 0, 0, 0, 3, 218, 255, 28, 0, 3, 40, 38, 6, 98, 0, 0, 39, 42, 30, 24, 53, 255, 173, 0, 0, 0, 0, 15, 151, 129, 121, 120, 120, 120, 120, 124, 255, 234, 120, 120, 120, 120, 120, 122, 141, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 241, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 247, 255, 40, 0, 0, 0, 0, 0, 0, 0, 25, 241, 255, 5, 0, 0, 31, 249, 246, 216, 216, 216, 216, 216, 216, 216, 226, 255, 220, 0, 0, 0, 0, 0, 0, 0, 44, 60, 0, 0, 0, 195, 255, 24, 0, 0, 0, 152, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 131, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 188, 0, 0, 0, 0, 0, 0, 25, 241, 255, 5, 0, 0, 31, 249, 246, 216, 216, 216, 216, 216, 216, 216, 226, 255, 220, 0, 0, 0, 0, 153, 208, 208, 255, 236, 208, 208, 208, 158, 0, 0, 209, 255, 255, 255, 255, 255, 255, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 250, 232, 70, 0, 0, 0, 31, 203, 205, 59, 0, 0, 0, 0, 0, 0, 0, 31, 248, 198, 1, 0, 157, 85, 85, 107, 100, 96, 151, 193, 96, 96, 96, 96, 97, 105, 114, 37, 0, - 0, 192, 212, 202, 233, 255, 207, 211, 96, 0, 0, 150, 255, 156, 0, 0, 0, 135, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 160, 9, 0, 0, 8, 255, 216, 0, 0, 0, 79, 188, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 202, 255, 0, 0, 0, 0, 60, 3, 0, 0, 0, 0, 0, 0, 99, 255, 255, 24, 0, 14, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 169, 0, 0, 0, 0, 0, 0, 0, 30, 160, 9, 0, 0, 8, 255, 216, 0, 0, 0, 79, 188, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 248, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 35, 3, 0, 0, 0, 0, 0, 0, 174, 255, 255, 0, 0, 0, 158, 255, 120, 0, 0, 0, 0, 0, 0, 0, 125, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 250, 224, 0, 0, 0, 0, 152, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 186, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, 255, 0, 0, 0, 158, 255, 120, 0, 0, 0, 0, 0, 0, 0, 125, 255, 124, 0, 0, 0, 0, 180, 255, 207, 200, 200, 200, 205, 255, 186, 0, 61, 255, 204, 128, 128, 128, 128, 128, 236, 223, 0, 0, 0, 0, 0, 0, 0, 0, 5, 121, 253, 198, 25, 0, 0, 0, 81, 237, 255, 234, 120, 5, 0, 0, 0, 0, 0, 0, 180, 252, 48, 0, 49, 255, 254, 65, 0, 0, 0, 173, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 0, 19, 244, 253, 45, 0, 0, 214, 246, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 163, 0, 0, 8, 255, 216, 0, 0, 7, 222, 253, 51, 0, 0, 0, 0, 0, 0, 28, 61, 51, 48, 48, 31, 0, 0, 0, 0, 0, 0, 0, 240, 241, 24, 24, 24, 25, 30, 35, 0, 0, 0, 46, 84, 78, 74, 72, 152, 255, 135, 72, 72, 72, 72, 216, 255, 72, 72, 72, 72, 73, 77, 85, 46, 0, 0, 0, 12, 230, 255, 255, 24, 0, 5, 255, 226, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 123, 255, 163, 0, 0, 8, 255, 216, 0, 0, 7, 222, 253, 51, 0, 0, 0, 0, 0, 0, 0, 0, 151, 240, 240, 240, 240, 240, 240, 240, 255, 255, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 205, 74, 2, 0, 0, 0, 88, 255, 255, 255, 0, 0, 65, 253, 232, 11, 0, 0, 44, 48, 1, 0, 2, 225, 254, 30, 0, 0, 0, 41, 33, 25, 24, 24, 4, 0, 1, 170, 255, 125, 0, 0, 0, 0, 152, 255, 147, 39, 40, 55, 26, 0, 0, 0, 79, 136, 129, 122, 120, 120, 120, 120, 120, 232, 255, 138, 120, 120, 120, 120, 122, 127, 136, 40, 0, 0, 0, 0, 88, 255, 255, 255, 0, 0, 65, 253, 232, 11, 0, 0, 44, 48, 1, 0, 2, 225, 254, 30, 0, 0, 0, 0, 173, 255, 32, 0, 0, 0, 24, 255, 180, 0, 184, 255, 63, 0, 0, 0, 0, 0, 221, 219, 0, 0, 0, 0, 0, 0, 30, 146, 225, 255, 212, 78, 72, 72, 72, 156, 255, 240, 116, 9, 0, 0, 0, 0, 0, 0, 9, 149, 255, 179, 60, 68, 187, 255, 137, 0, 0, 0, 49, 252, 218, 18, 0, 0, 10, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 45, 35, 16, 120, 98, 24, 16, 53, 255, 171, 16, 19, 38, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 200, 255, 76, 0, 8, 255, 216, 0, 0, 118, 255, 152, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 216, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, 140, 255, 254, 255, 24, 0, 1, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 4, 200, 255, 76, 0, 8, 255, 216, 0, 0, 118, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 170, 144, 144, 144, 144, 144, 144, 144, 3, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 222, 255, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 255, 244, 14, 0, 0, 48, 244, 243, 225, 255, 0, 20, 229, 255, 99, 0, 0, 0, 229, 252, 0, 0, 64, 254, 189, 0, 0, 0, 0, 248, 255, 255, 255, 255, 41, 6, 155, 255, 207, 8, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255, 86, 0, 0, 0, 144, 255, 250, 244, 240, 240, 240, 240, 240, 255, 251, 240, 240, 240, 240, 240, 243, 247, 255, 72, 0, 0, 0, 48, 244, 243, 225, 255, 0, 20, 229, 255, 99, 0, 0, 0, 229, 252, 0, 0, 64, 254, 189, 0, 0, 0, 0, 0, 170, 255, 32, 0, 0, 0, 24, 255, 177, 66, 255, 196, 0, 0, 0, 0, 0, 0, 226, 214, 0, 0, 0, 0, 0, 0, 14, 250, 255, 255, 255, 255, 255, 255, 255, 254, 160, 21, 0, 0, 0, 0, 0, 0, 0, 0, 11, 236, 255, 255, 255, 255, 255, 212, 6, 0, 0, 5, 206, 244, 41, 0, 0, 61, 227, 112, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 120, 179, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 253, 196, 0, 8, 255, 216, 0, 20, 240, 235, 18, 0, 0, 0, 0, 0, 0, 0, 41, 92, 84, 136, 255, 155, 0, 0, 0, 0, 0, 0, 0, 240, 246, 104, 104, 104, 105, 110, 102, 0, 0, 0, 38, 69, 62, 59, 57, 143, 255, 124, 56, 56, 56, 56, 187, 255, 81, 56, 56, 56, 57, 61, 70, 38, 0, 0, 56, 252, 218, 204, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 49, 253, 196, 0, 8, 255, 216, 0, 20, 240, 235, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 187, 255, 238, 182, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 192, 247, 255, 144, 0, 0, 0, 204, 255, 104, 200, 255, 0, 109, 244, 188, 0, 0, 0, 0, 220, 243, 0, 0, 0, 21, 33, 0, 0, 0, 0, 141, 135, 131, 216, 255, 31, 22, 172, 212, 24, 0, 0, 0, 0, 0, 16, 127, 151, 152, 152, 152, 57, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 29, 255, 171, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 204, 255, 104, 200, 255, 0, 109, 244, 188, 0, 0, 0, 0, 220, 243, 0, 0, 0, 21, 33, 0, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 199, 255, 59, 0, 0, 0, 0, 0, 0, 230, 207, 0, 0, 0, 0, 0, 0, 0, 97, 91, 61, 43, 57, 195, 255, 214, 62, 0, 0, 45, 150, 18, 0, 0, 0, 0, 0, 0, 96, 130, 91, 61, 216, 253, 55, 0, 0, 1, 157, 255, 90, 0, 0, 0, 41, 239, 253, 79, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 43, 128, 52, 106, 118, 112, 112, 141, 130, 112, 112, 112, 112, 112, 112, 119, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 24, 237, 255, 204, 21, 0, 0, 0, 0, 0, 0, 0, 18, 35, 27, 24, 24, 24, 101, 35, 24, 31, 255, 219, 24, 26, 99, 99, 24, 24, 24, 28, 37, 18, 0, 0, 0, 0, 0, 80, 255, 147, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 149, 255, 52, 0, 0, 0, 97, 69, 0, 0, 0, 5, 218, 255, 73, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 18, 35, 27, 24, 24, 24, 101, 35, 24, 31, 255, 219, 24, 26, 99, 99, 24, 24, 24, 28, 37, 18, 0, 0, 0, 0, 182, 255, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 47, 195, 230, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 222, 175, 17, 0, 0, 0, 23, 151, 1, 200, 255, 0, 0, 16, 24, 0, 0, 0, 0, 217, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 27, 0, 57, 122, 100, 97, 96, 96, 96, 96, 96, 96, 96, 96, 26, 0, 0, 0, 0, 0, 0, 0, 0, 123, 192, 192, 192, 192, 204, 255, 229, 192, 192, 192, 192, 192, 141, 0, 0, 0, 0, 0, 0, 23, 151, 1, 200, 255, 0, 0, 16, 24, 0, 0, 0, 0, 217, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 12, 106, 17, 142, 98, 0, 0, 0, 0, 237, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 104, 237, 246, 122, 5, 0, 0, 3, 167, 255, 222, 42, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 142, 0, 0, 44, 183, 255, 143, 0, 2, 18, 38, 62, 153, 255, 241, 36, 0, 0, - 0, 0, 0, 0, 159, 255, 247, 255, 76, 0, 0, 0, 0, 142, 248, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 44, 230, 255, 216, 27, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 104, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 1, 0, 125, 255, 77, 0, 0, 3, 224, 255, 93, 0, 0, 0, 36, 131, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 104, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 104, 0, 0, 0, 0, 197, 255, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 160, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 5, 83, 2, 0, 216, 240, 0, 3, 111, 35, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 120, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, 13, 0, 0, 0, 0, 0, 0, 0, 0, 158, 249, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 238, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 5, 83, 2, 0, 216, 240, 0, 3, 111, 35, 0, 0, 0, 0, 0, 0, 168, 255, 151, 136, 136, 136, 147, 255, 176, 0, 0, 54, 254, 225, 6, 0, 0, 0, 242, 192, 0, 0, 0, 0, 0, 0, 0, 23, 111, 219, 255, 177, 33, 0, 0, 0, 0, 0, 0, 116, 253, 240, 66, 0, 0, 0, 0, 0, 0, 18, 233, 222, 10, 0, 0, 61, 255, 255, 237, 242, 254, 255, 255, 255, 255, 255, 255, 206, 7, 0, - 1, 50, 121, 204, 255, 255, 198, 81, 29, 53, 35, 32, 33, 231, 255, 84, 32, 32, 32, 32, 32, 38, 50, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 35, 225, 255, 220, 29, 0, 0, 0, 0, 0, 69, 153, 135, 128, 128, 128, 128, 138, 254, 247, 255, 254, 255, 209, 128, 128, 128, 128, 130, 138, 145, 61, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 121, 101, 172, 164, 0, 89, 255, 115, 0, 0, 100, 255, 197, 3, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 69, 153, 135, 128, 128, 128, 128, 138, 254, 247, 255, 254, 255, 209, 128, 128, 128, 128, 130, 138, 145, 61, 0, 0, 0, 0, 213, 255, 203, 200, 200, 200, 200, 200, 200, 200, 34, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 113, 255, 172, 0, 216, 240, 0, 83, 255, 199, 3, 0, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 15, 32, 212, 252, 44, 32, 32, 32, 32, 134, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 242, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 221, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 113, 255, 172, 0, 216, 240, 0, 83, 255, 199, 3, 0, 0, 0, 0, 0, 168, 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 172, 255, 102, 0, 0, 0, 250, 185, 0, 0, 0, 0, 0, 98, 224, 254, 255, 255, 181, 122, 135, 147, 159, 171, 182, 193, 204, 215, 248, 255, 249, 87, 0, 0, 0, 0, 0, 167, 255, 68, 0, 0, 3, 10, 239, 235, 221, 255, 203, 122, 209, 255, 59, 23, 216, 176, 17, 0, - 5, 226, 255, 245, 226, 255, 24, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 32, 223, 255, 220, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 115, 255, 217, 206, 251, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 37, 85, 136, 211, 255, 255, 255, 251, 162, 0, 47, 255, 162, 0, 16, 229, 246, 36, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 115, 255, 217, 206, 251, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 184, 184, 184, 184, 184, 184, 184, 252, 255, 16, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 97, 136, 130, 124, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 155, 255, 205, 120, 120, 123, 130, 122, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 21, 240, 254, 64, 0, 216, 240, 0, 0, 187, 255, 127, 0, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 0, 0, 115, 255, 88, 0, 0, 0, 1, 213, 255, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 21, 240, 254, 64, 0, 216, 240, 0, 0, 187, 255, 127, 0, 0, 0, 0, 0, 168, 255, 46, 16, 16, 16, 38, 255, 176, 0, 0, 0, 54, 255, 220, 2, 0, 2, 255, 177, 0, 0, 0, 0, 0, 59, 255, 255, 255, 244, 233, 223, 217, 219, 255, 235, 196, 183, 170, 159, 148, 158, 255, 254, 100, 0, 0, 18, 156, 255, 226, 147, 177, 212, 249, 40, 16, 0, 64, 255, 132, 0, 176, 255, 24, 0, 26, 1, 0, 0, - 0, 125, 148, 22, 152, 255, 24, 0, 43, 104, 97, 96, 237, 250, 107, 96, 96, 148, 255, 168, 96, 98, 108, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 35, 230, 255, 216, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 181, 9, 255, 216, 45, 245, 247, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 247, 255, 255, 255, 255, 255, 169, 71, 12, 0, 0, 3, 241, 225, 1, 167, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 181, 9, 255, 216, 45, 245, 247, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 255, 241, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 160, 255, 153, 0, 0, 216, 240, 0, 0, 33, 244, 251, 51, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 0, 0, 17, 242, 207, 1, 0, 0, 88, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 241, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 219, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 160, 255, 153, 0, 0, 216, 240, 0, 0, 33, 244, 251, 51, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 203, 255, 79, 0, 12, 255, 166, 0, 0, 0, 0, 0, 1, 55, 25, 5, 0, 0, 0, 0, 48, 255, 152, 0, 0, 0, 0, 0, 0, 117, 167, 18, 0, 0, 51, 255, 255, 255, 255, 244, 212, 173, 0, 0, 0, 71, 255, 122, 0, 176, 255, 24, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 0, 85, 255, 127, 0, 0, 0, 174, 250, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 46, 237, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 219, 18, 8, 255, 216, 0, 78, 251, 255, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 0, 27, 61, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 255, 220, 149, 81, 127, 255, 88, 0, 0, 0, 0, 0, 174, 255, 178, 255, 186, 1, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 219, 18, 8, 255, 216, 0, 78, 251, 255, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 210, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 20, 25, 19, 14, 14, 24, 8, 8, 8, 8, 8, 8, 8, 8, 70, 255, 163, 8, 8, 12, 18, 24, 0, 0, 0, 0, 0, 200, 255, 0, 0, 105, 255, 221, 13, 0, 0, 216, 240, 0, 0, 0, 117, 255, 209, 5, 0, 0, 0, 0, 0, 176, 255, 24, 50, 69, 0, 0, 132, 255, 117, 0, 17, 230, 249, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 251, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 243, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 105, 255, 221, 13, 0, 0, 216, 240, 0, 0, 0, 117, 255, 209, 5, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 105, 201, 56, 0, 22, 255, 152, 0, 0, 0, 0, 0, 0, 0, 7, 92, 1, 0, 0, 0, 48, 255, 152, 0, 0, 5, 115, 6, 0, 0, 0, 0, 0, 0, 0, 159, 121, 70, 27, 0, 0, 0, 0, 0, 0, 99, 255, 96, 0, 176, 255, 24, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 9, 224, 255, 154, 50, 0, 59, 254, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 8, 155, 255, 233, 36, 0, 8, 255, 216, 0, 0, 86, 249, 255, 207, 64, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 21, 214, 212, 3, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 39, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 0, 76, 255, 255, 217, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 8, 155, 255, 233, 36, 0, 8, 255, 216, 0, 0, 86, 249, 255, 207, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 177, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 15, 183, 186, 3, 0, 0, 0, 0, 0, 0, 0, 64, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 61, 250, 251, 57, 0, 0, 0, 216, 240, 0, 0, 0, 6, 220, 255, 124, 0, 0, 0, 0, 0, 176, 255, 60, 229, 194, 0, 0, 10, 223, 252, 90, 186, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 251, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 244, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 61, 250, 251, 57, 0, 0, 0, 216, 240, 0, 0, 0, 6, 220, 255, 124, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 5, 1, 0, 0, 36, 255, 137, 0, 0, 0, 0, 0, 0, 0, 162, 255, 187, 10, 0, 0, 48, 255, 152, 0, 0, 160, 255, 205, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 160, 255, 48, 0, 176, 255, 24, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 2, 78, 187, 255, 255, 194, 214, 237, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 211, 255, 227, 40, 0, 0, 8, 255, 216, 0, 0, 0, 58, 232, 255, 255, 182, 81, 9, 0, 0, 0, 0, 0, 80, 255, 171, 213, 255, 138, 1, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 19, 178, 255, 255, 92, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 58, 211, 255, 227, 40, 0, 0, 8, 255, 216, 0, 0, 0, 58, 232, 255, 255, 182, 81, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 144, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 10, 213, 255, 133, 0, 0, 0, 0, 0, 0, 0, 64, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 97, 251, 111, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 81, 231, 83, 0, 0, 0, 0, 0, 177, 255, 238, 255, 167, 2, 0, 0, 56, 248, 255, 255, 188, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 245, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 229, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 97, 251, 111, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 81, 231, 83, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 122, 0, 0, 0, 0, 0, 0, 148, 255, 222, 52, 0, 0, 0, 48, 255, 152, 0, 0, 29, 192, 255, 244, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 108, 190, 140, 0, 26, 247, 230, 2, 0, 176, 255, 24, 0, 0, 5, 0, 0, - 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 0, 0, 0, 41, 191, 255, 255, 236, 113, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 168, 254, 255, 199, 24, 0, 0, 0, 10, 255, 216, 0, 0, 0, 0, 29, 194, 255, 255, 255, 140, 0, 0, 0, 0, 0, 88, 255, 255, 255, 119, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 1, 90, 232, 255, 213, 251, 231, 33, 0, 0, 235, 175, 10, 0, 0, 0, 0, 201, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 44, 168, 254, 255, 199, 24, 0, 0, 0, 10, 255, 216, 0, 0, 0, 0, 29, 194, 255, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 108, 0, 0, 0, 0, 0, 138, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 44, 247, 252, 56, 0, 0, 0, 0, 0, 0, 64, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 0, 0, 42, 0, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 205, 255, 255, 157, 2, 0, 0, 0, 41, 222, 255, 255, 140, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 254, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 0, 0, 42, 0, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 170, 255, 249, 248, 248, 248, 248, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 108, 0, 0, 0, 0, 7, 165, 255, 202, 22, 0, 0, 0, 0, 48, 255, 152, 0, 0, 0, 3, 137, 255, 255, 142, 1, 0, 0, 28, 73, 120, 173, 229, 255, 255, 244, 110, 0, 170, 255, 128, 0, 0, 176, 255, 24, 0, 6, 218, 106, 1, - 0, 0, 0, 0, 190, 255, 22, 0, 0, 0, 0, 0, 50, 159, 253, 242, 151, 242, 255, 233, 117, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 238, 254, 150, 7, 0, 0, 0, 0, 17, 255, 220, 0, 0, 0, 0, 0, 1, 93, 218, 191, 1, 0, 0, 0, 0, 0, 153, 255, 255, 116, 34, 83, 69, 54, 48, 48, 243, 243, 48, 48, 48, 50, 56, 70, 60, 0, 0, 0, 0, 0, 0, 0, 156, 255, 85, 7, 88, 207, 255, 251, 133, 5, 114, 255, 225, 52, 40, 255, 227, 1, 0, 0, 0, 0, 204, 255, 27, 0, 1, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 57, 255, 168, 0, 0, 0, 61, 238, 254, 150, 7, 0, 0, 0, 0, 17, 255, 220, 0, 0, 0, 0, 0, 1, 93, 218, 191, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 229, 255, 68, 0, 0, 0, 0, 0, 141, 255, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 164, 0, 0, 0, 0, 0, 0, 112, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 3, 0, 0, 0, 0, 0, 0, 23, 243, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 253, 255, 162, 1, 0, 0, 20, 139, 247, 251, 157, 221, 255, 229, 130, 42, 1, 0, 0, 0, 0, 0, 0, 0, 0, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 3, 0, 0, 0, 0, 0, 0, 23, 243, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 172, 160, 160, 160, 169, 255, 183, 0, 0, 0, 0, 0, 0, 0, 15, 186, 255, 83, 0, 0, 0, 43, 208, 255, 186, 12, 0, 0, 0, 0, 0, 95, 255, 148, 0, 0, 0, 0, 0, 99, 253, 252, 59, 0, 0, 169, 255, 255, 255, 206, 136, 65, 6, 0, 126, 255, 221, 12, 0, 0, 176, 255, 35, 0, 67, 255, 210, 0, - 0, 28, 206, 207, 255, 243, 1, 0, 47, 120, 169, 222, 255, 255, 179, 35, 0, 21, 151, 252, 255, 232, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 67, 0, 0, 0, 0, 0, 0, 32, 255, 227, 0, 0, 0, 0, 0, 0, 0, 2, 22, 0, 0, 0, 0, 0, 1, 192, 255, 133, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 0, 58, 190, 218, 255, 255, 49, 60, 245, 255, 204, 50, 0, 0, 0, 135, 255, 253, 240, 255, 131, 0, 0, 0, 0, 0, 207, 255, 30, 0, 4, 255, 224, 0, 0, 0, 0, 0, 0, 171, 201, 241, 255, 157, 0, 0, 0, 0, 61, 67, 0, 0, 0, 0, 0, 0, 32, 255, 227, 0, 0, 0, 0, 0, 0, 0, 2, 22, 0, 0, 0, 0, 0, 0, 0, 125, 225, 225, 233, 254, 255, 238, 15, 0, 0, 0, 0, 0, 148, 255, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 104, 2, 0, 0, 0, 72, 212, 231, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 8, 0, 0, 0, 0, 131, 220, 250, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 199, 197, 7, 17, 78, 167, 248, 255, 180, 42, 0, 7, 115, 228, 255, 255, 237, 194, 145, 0, 0, 115, 225, 219, 213, 235, 253, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 248, 239, 209, 213, 221, 135, 0, 0, 0, 0, 0, 208, 255, 8, 0, 0, 0, 0, 131, 220, 250, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 255, 32, 0, 0, 0, 24, 255, 190, 0, 0, 0, 0, 123, 203, 216, 249, 255, 230, 14, 0, 0, 0, 16, 173, 168, 6, 0, 0, 0, 35, 232, 244, 255, 255, 116, 0, 0, 0, 0, 0, 0, 91, 90, 0, 0, 0, 38, 197, 107, 28, 0, 0, 0, 6, 146, 255, 241, 48, 0, 0, 0, 158, 255, 207, 160, 225, 255, 106, 0, - 0, 0, 195, 255, 241, 94, 0, 0, 48, 255, 255, 225, 143, 48, 0, 0, 0, 0, 0, 48, 202, 224, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 176, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 200, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 123, 1, 0, 38, 95, 85, 82, 80, 80, 80, 80, 80, 80, 80, 81, 84, 92, 69, 0, 0, 0, 0, 2, 219, 255, 226, 110, 0, 0, 93, 100, 2, 0, 0, 0, 0, 0, 94, 233, 255, 225, 17, 0, 0, 0, 0, 0, 215, 255, 38, 0, 12, 255, 224, 0, 0, 0, 0, 0, 0, 125, 255, 255, 223, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 200, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 255, 250, 192, 56, 0, 0, 0, 0, 0, 0, 156, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 242, 255, 228, 122, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 16, 0, 0, 0, 0, 49, 255, 253, 196, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 22, 8, 181, 255, 252, 174, 56, 0, 0, 0, 0, 0, 5, 84, 174, 245, 255, 82, 0, 0, 63, 122, 116, 109, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 107, 110, 118, 74, 0, 0, 0, 0, 0, 216, 255, 16, 0, 0, 0, 0, 49, 255, 253, 196, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 176, 22, 0, 0, 0, 12, 128, 98, 0, 0, 0, 0, 48, 255, 255, 229, 154, 32, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 189, 255, 223, 152, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 250, 238, 57, 0, 0, 0, 0, 47, 213, 247, 248, 245, 163, 4, 0, - 0, 0, 47, 52, 6, 0, 0, 0, 0, 79, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 54, 15, 0, 0, 0, 0, 0, 0, 117, 136, 24, 0, 9, 112, 98, 0, 0, 0, 0, 0, 0, 28, 85, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 80, 61, 35, 5, 0, 0, 0, 0, 0, 0, 0, 0, 10, 16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 45, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 40, 3, 0, 0, 0, 0, 0, 63, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 101, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 58, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 40, 3, 0, 0, 0, 0, 0, 63, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 69, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +static const int g_txt_setup_w = 576; +static const int g_txt_setup_h = 42; +static const unsigned char g_txt_setup[576*42] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 64, 126, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 89, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 94, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 129, 212, 212, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 212, 212, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 32, 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 219, 191, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 255, 243, 168, 30, 0, 0, 14, 148, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 212, 212, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 140, 140, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 207, 107, 16, 0, 0, 0, 0, 130, 198, 112, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 188, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 207, 107, 16, 0, 0, 0, 0, 130, 198, 112, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 168, 86, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 210, 108, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 81, 137, 192, 244, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 230, 255, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 122, 255, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 255, 255, 206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 156, 249, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 113, 50, 0, 0, 73, 255, 255, 171, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 207, 3, 0, 29, 210, 255, 185, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 62, 52, 44, 40, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 19, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 230, 16, 0, 0, 0, 0, 193, 255, 255, 203, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 45, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 197, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116, 255, 255, 230, 16, 0, 0, 0, 0, 193, 255, 255, 203, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, 255, 237, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 17, 36, 56, 79, 111, 145, 178, 211, 244, 255, 255, 255, 255, 255, 255, 240, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 232, 136, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 208, 255, 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 102, 255, 255, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 244, 255, 254, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 139, 132, 126, 119, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 114, 120, 127, 133, 140, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 252, 255, 249, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 72, 155, 241, 255, 215, 9, 0, 69, 255, 255, 148, 0, 0, 61, 241, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 255, 255, 70, 0, 0, 43, 244, 255, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 97, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 255, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 254, 255, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 94, 0, 0, 0, 0, 23, 252, 255, 252, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 252, 255, 255, 125, 0, 0, 0, 0, 0, 0, 66, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 255, 255, 252, 252, 252, 252, 252, 255, 255, 252, 252, 252, 252, 252, 252, 255, 255, 252, 252, 252, 252, 252, 253, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 94, 0, 0, 0, 0, 23, 252, 255, 252, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 255, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 186, 189, 191, 196, 203, 210, 218, 230, 243, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 227, 212, 198, 161, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 243, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 255, 224, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 202, 199, 192, 188, 188, 188, 188, 188, 243, 255, 255, 206, 188, 188, 188, 188, 193, 208, 218, 104, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 255, 255, 243, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 70, 125, 187, 245, 255, 255, 255, 255, 255, 141, 0, 64, 255, 255, 138, 0, 35, 243, 255, 251, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 196, 0, 0, 0, 0, 88, 255, 255, 254, 71, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 205, 1, 0, 0, 0, 0, 113, 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 253, 255, 255, 102, 0, 0, 0, 0, 0, 68, 255, 255, 188, 140, 140, 140, 140, 140, 185, 255, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 224, 0, 0, 0, 0, 0, 200, 255, 48, 0, 0, 0, 0, 48, 255, 255, 16, 0, 0, 0, 0, 116, 255, 255, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 205, 1, 0, 0, 0, 0, 113, 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 236, 255, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 243, 255, 245, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 250, 225, 200, 174, 147, 121, 94, 65, 38, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 251, 162, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 50, 66, 55, 51, 48, 48, 48, 48, 48, 48, 48, 48, 161, 255, 255, 103, 48, 48, 48, 48, 48, 48, 48, 48, 51, 58, 71, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 227, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 127, 150, 184, 226, 255, 255, 255, 255, 255, 255, 252, 207, 149, 89, 21, 0, 60, 255, 255, 131, 0, 0, 113, 254, 255, 253, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 70, 0, 0, 0, 0, 0, 154, 255, 255, 231, 21, 0, 0, 239, 244, 235, 231, 228, 228, 228, 228, 228, 138, 0, 0, 0, 0, 0, 0, 0, 50, 66, 55, 51, 48, 48, 48, 48, 48, 48, 48, 48, 161, 255, 255, 103, 48, 48, 48, 48, 48, 48, 48, 48, 51, 58, 71, 29, 0, 0, 0, 0, 0, 0, 0, 0, 49, 95, 89, 83, 78, 72, 66, 64, 64, 64, 64, 64, 142, 255, 255, 113, 0, 0, 0, 0, 0, 0, 0, 83, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 255, 255, 68, 0, 0, 0, 0, 0, 214, 255, 252, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 252, 71, 0, 0, 0, 0, 78, 255, 255, 100, 0, 0, 0, 0, 0, 100, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 205, 255, 224, 0, 0, 0, 0, 0, 200, 255, 48, 0, 0, 0, 0, 48, 255, 255, 16, 0, 0, 0, 0, 116, 255, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 255, 255, 68, 0, 0, 0, 0, 0, 214, 255, 252, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 184, 177, 159, 143, 129, 116, 104, 94, 83, 74, 212, 255, 241, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 255, 255, 202, 2, 0, 0, 0, 6, 79, 70, 62, 54, 48, 48, 48, 48, 105, 81, 48, 48, 48, 48, 48, 50, 54, 60, 72, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 150, 146, 138, 133, 132, 132, 132, 132, 132, 132, 132, 132, 133, 132, 132, 132, 135, 146, 157, 77, 0, 0, 0, 0, 70, 88, 81, 75, 68, 61, 60, 60, 60, 60, 60, 60, 60, 164, 255, 255, 142, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 65, 68, 77, 86, 56, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 191, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 255, 255, 255, 253, 255, 255, 154, 10, 0, 0, 0, 0, 0, 56, 255, 255, 128, 0, 0, 0, 98, 253, 255, 254, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 237, 255, 204, 0, 0, 0, 0, 0, 0, 9, 219, 255, 255, 145, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 249, 255, 190, 0, 0, 0, 0, 0, 64, 255, 255, 236, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 255, 236, 15, 0, 0, 0, 107, 255, 255, 88, 0, 0, 0, 0, 0, 100, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 226, 16, 16, 16, 16, 16, 203, 255, 61, 16, 16, 16, 16, 61, 255, 255, 31, 16, 16, 16, 16, 124, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 249, 255, 190, 0, 0, 0, 0, 0, 64, 255, 255, 236, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 255, 225, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 254, 53, 0, 0, 0, 0, 20, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, + 0, 38, 106, 80, 66, 57, 134, 255, 255, 85, 63, 75, 57, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 64, 186, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 200, 245, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 255, 222, 168, 113, 58, 48, 255, 255, 132, 0, 0, 0, 0, 0, 0, 52, 255, 255, 128, 0, 0, 0, 0, 111, 255, 255, 254, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 67, 254, 137, 7, 0, 0, 209, 197, 178, 171, 164, 164, 181, 255, 255, 136, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 253, 255, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 56, 0, 0, 0, 0, 0, 182, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 196, 230, 55, 0, 0, 0, 0, 136, 255, 255, 63, 0, 0, 0, 0, 0, 100, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 56, 0, 0, 0, 0, 0, 182, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 22, 0, 0, 0, 0, 0, 34, 56, 56, 56, 150, 255, 245, 58, 56, 56, 56, 56, 56, 9, 0, 0, 5, 237, 255, 254, 200, 200, 200, 200, 200, 200, 200, 200, 200, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 245, 255, 255, 162, 14, 0, 0, 0, 0, 0, 28, 152, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 255, 255, 157, 0, 0, 0, 34, 0, 20, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, + 0, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 168, 0, 0, 0, 0, 50, 189, 176, 0, 0, 0, 0, 0, 0, 117, 255, 255, 176, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 91, 68, 60, 52, 52, 52, 52, 52, 52, 52, 52, 163, 255, 255, 106, 52, 52, 52, 52, 52, 52, 52, 52, 61, 73, 100, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 21, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 50, 255, 255, 128, 0, 0, 0, 0, 0, 149, 255, 181, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 230, 255, 255, 36, 0, 0, 0, 38, 60, 60, 26, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 73, 91, 68, 60, 52, 52, 52, 52, 52, 52, 52, 52, 163, 255, 255, 106, 52, 52, 52, 52, 52, 52, 52, 52, 61, 73, 100, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 253, 255, 255, 0, 0, 0, 0, 0, 49, 255, 255, 235, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 218, 255, 255, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 32, 0, 0, 0, 0, 0, 179, 255, 255, 26, 0, 0, 0, 0, 0, 100, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 253, 255, 255, 0, 0, 0, 0, 0, 49, 255, 255, 235, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 218, 255, 255, 179, 0, 0, 0, 0, 0, 0, 152, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 34, 0, 0, 85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 239, 255, 249, 107, 0, 0, 0, 0, 0, 0, 66, 233, 255, 241, 121, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 239, 21, 0, 0, 62, 250, 143, 28, 46, 35, 25, 20, 16, 16, 133, 204, 47, 16, 16, 16, 16, 16, 16, 19, 28, 36, 44, 23, 0, 0, + 0, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 168, 0, 0, 0, 17, 239, 255, 255, 70, 0, 0, 0, 0, 0, 181, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 132, 37, 0, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 3, 183, 126, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 47, 255, 255, 128, 0, 0, 0, 0, 0, 6, 122, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 255, 255, 255, 36, 0, 0, 0, 152, 255, 255, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 132, 37, 0, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 3, 183, 126, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 115, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 213, 255, 255, 255, 0, 0, 0, 0, 0, 190, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 244, 255, 227, 0, 0, 0, 0, 0, 0, 100, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 185, 155, 143, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 213, 255, 255, 255, 0, 0, 0, 0, 0, 190, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 83, 0, 0, 0, 0, 0, 0, 141, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 24, 0, 0, 193, 255, 255, 193, 192, 192, 192, 192, 192, 192, 192, 241, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 247, 255, 230, 58, 0, 0, 0, 0, 0, 3, 128, 253, 255, 255, 255, 210, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 255, 255, 106, 0, 0, 0, 169, 255, 255, 238, 23, 0, 0, 0, 0, 1, 215, 255, 245, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 73, 64, 54, 45, 126, 255, 255, 75, 52, 64, 49, 0, 0, 0, 0, 108, 255, 255, 213, 3, 0, 0, 0, 9, 245, 255, 222, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 189, 255, 211, 11, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 105, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 46, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 238, 255, 255, 255, 36, 0, 0, 0, 140, 255, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 189, 255, 211, 11, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 105, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 165, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 0, 96, 1, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255, 255, 0, 0, 0, 0, 84, 255, 255, 207, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 241, 255, 236, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 145, 0, 0, 0, 0, 0, 0, 100, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255, 255, 0, 0, 0, 0, 84, 255, 255, 207, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 241, 255, 236, 6, 0, 0, 0, 0, 0, 0, 134, 255, 255, 83, 44, 44, 44, 44, 44, 44, 179, 255, 255, 16, 0, 49, 255, 255, 185, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 59, 184, 255, 255, 197, 25, 0, 0, 0, 0, 0, 33, 196, 255, 255, 255, 202, 79, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 243, 255, 204, 2, 0, 0, 43, 253, 255, 255, 91, 0, 0, 0, 0, 0, 80, 255, 255, 231, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 5, 224, 255, 255, 100, 0, 0, 0, 79, 255, 255, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 166, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 16, 236, 255, 250, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 95, 85, 75, 72, 72, 72, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 235, 36, 36, 36, 36, 36, 39, 43, 54, 50, 0, 0, 0, 0, 0, 104, 129, 121, 116, 112, 109, 108, 131, 255, 255, 184, 108, 108, 108, 108, 108, 108, 133, 255, 255, 182, 108, 108, 108, 108, 108, 108, 110, 114, 120, 131, 105, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 255, 36, 0, 0, 0, 134, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 166, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 16, 236, 255, 250, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 254, 200, 86, 5, 0, 0, 0, 0, 0, 54, 250, 255, 255, 255, 255, 0, 0, 0, 20, 233, 255, 255, 75, 0, 0, 0, 0, 64, 72, 72, 2, 0, 0, 0, 99, 255, 255, 147, 0, 0, 0, 0, 0, 31, 62, 53, 43, 36, 36, 36, 36, 9, 0, 0, 0, 58, 247, 255, 250, 33, 0, 0, 0, 0, 0, 0, 100, 255, 255, 218, 78, 52, 54, 69, 87, 59, 0, 0, 0, 0, 0, 73, 207, 197, 188, 184, 181, 180, 180, 180, 180, 180, 180, 180, 199, 255, 255, 225, 180, 180, 180, 180, 180, 180, 180, 181, 185, 192, 201, 194, 0, 0, 0, 0, 0, 0, 0, 54, 250, 255, 255, 255, 255, 0, 0, 0, 20, 233, 255, 255, 75, 0, 0, 0, 0, 64, 72, 72, 2, 0, 0, 0, 99, 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 12, 0, 181, 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 205, 255, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 231, 255, 255, 255, 232, 114, 108, 108, 108, 108, 108, 126, 243, 255, 255, 219, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 234, 255, 255, 128, 88, 98, 104, 192, 255, 255, 160, 0, 0, 0, 0, 0, 6, 218, 255, 247, 53, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 112, 200, 99, 18, 0, 0, 0, 167, 255, 249, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 214, 255, 255, 76, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 142, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 255, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 196, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 192, 0, 0, 0, 0, 0, 47, 252, 255, 255, 255, 255, 36, 0, 0, 0, 130, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 214, 255, 255, 76, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 142, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 255, 250, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 115, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 255, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 255, 255, 253, 33, 0, 0, 0, 0, 28, 230, 255, 255, 228, 255, 255, 0, 0, 0, 174, 255, 255, 189, 0, 0, 0, 0, 0, 219, 255, 253, 1, 0, 0, 0, 210, 255, 255, 52, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, 65, 0, 0, 55, 237, 255, 255, 117, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255, 255, 255, 255, 255, 255, 255, 132, 0, 0, 0, 0, 0, 88, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 236, 0, 0, 0, 0, 0, 0, 28, 230, 255, 255, 228, 255, 255, 0, 0, 0, 174, 255, 255, 189, 0, 0, 0, 0, 0, 219, 255, 253, 1, 0, 0, 0, 210, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 9, 60, 255, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 123, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 255, 255, 255, 255, 255, 255, 228, 15, 0, 0, 0, 0, 0, 137, 255, 255, 94, 0, 0, 0, 0, 24, 177, 222, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 48, 210, 188, 161, 152, 156, 152, 152, 152, 152, 152, 152, 245, 255, 233, 152, 152, 153, 169, 191, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 85, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 254, 255, 220, 7, 0, 0, 140, 255, 255, 68, 0, 0, 35, 249, 255, 228, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 254, 250, 255, 255, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 196, 0, 0, 0, 0, 0, 183, 234, 226, 220, 217, 215, 214, 219, 255, 255, 234, 212, 212, 212, 212, 212, 212, 213, 255, 255, 240, 212, 212, 212, 212, 212, 212, 215, 219, 224, 235, 183, 0, 0, 0, 0, 3, 200, 255, 255, 222, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 254, 255, 220, 7, 0, 0, 140, 255, 255, 68, 0, 0, 35, 249, 255, 228, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 180, 44, 11, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 19, 60, 200, 255, 255, 179, 0, 0, 0, 0, 10, 206, 255, 255, 155, 172, 255, 255, 0, 0, 119, 255, 255, 245, 34, 0, 0, 0, 0, 0, 207, 255, 243, 0, 0, 0, 7, 111, 175, 194, 0, 0, 0, 0, 0, 0, 116, 255, 255, 255, 255, 255, 255, 255, 55, 0, 60, 249, 255, 255, 160, 1, 0, 0, 0, 0, 0, 0, 0, 20, 229, 255, 255, 255, 255, 255, 255, 255, 132, 0, 0, 0, 0, 0, 47, 132, 122, 113, 109, 106, 104, 104, 104, 104, 104, 104, 104, 193, 255, 255, 116, 104, 104, 104, 104, 104, 104, 104, 107, 111, 117, 127, 125, 0, 0, 0, 0, 0, 10, 206, 255, 255, 155, 172, 255, 255, 0, 0, 119, 255, 255, 245, 34, 0, 0, 0, 0, 0, 207, 255, 243, 0, 0, 0, 7, 111, 175, 194, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 15, 214, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 214, 255, 189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, 238, 212, 196, 185, 180, 180, 248, 255, 255, 255, 179, 32, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 250, 255, 255, 253, 226, 196, 250, 255, 255, 75, 0, 0, 0, 0, 0, 74, 253, 255, 156, 0, 0, 0, 0, 0, 178, 255, 255, 204, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 56, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 116, 255, 206, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 245, 63, 0, 0, 140, 255, 255, 68, 0, 0, 170, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 2, 0, 0, 248, 255, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 234, 28, 28, 28, 28, 28, 31, 35, 45, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 249, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 173, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 245, 63, 0, 0, 140, 255, 255, 68, 0, 0, 170, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 57, 0, 0, 0, 0, 75, 250, 255, 228, 14, 172, 255, 255, 0, 5, 148, 229, 255, 113, 0, 0, 0, 0, 0, 0, 202, 255, 236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 89, 76, 71, 67, 166, 255, 255, 45, 0, 0, 71, 238, 158, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 80, 99, 100, 100, 100, 100, 100, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 255, 246, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 250, 255, 228, 14, 172, 255, 255, 0, 5, 148, 229, 255, 113, 0, 0, 0, 0, 0, 0, 202, 255, 236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 78, 247, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 255, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 43, 10, 0, 0, 0, 0, 43, 198, 255, 255, 229, 83, 0, 0, 0, 0, 35, 180, 216, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 78, 38, 5, 0, 81, 255, 255, 165, 0, 0, 0, 0, 0, 54, 242, 255, 205, 8, 0, 0, 0, 0, 0, 19, 219, 255, 255, 171, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 5, 94, 66, 44, 193, 180, 171, 168, 168, 168, 169, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 173, 182, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 53, 255, 255, 255, 227, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 147, 25, 0, 0, 0, 140, 255, 255, 68, 0, 0, 34, 138, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 228, 255, 200, 0, 0, 0, 0, 0, 0, 103, 31, 0, 0, 0, 0, 0, 0, 69, 252, 255, 245, 32, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 147, 25, 0, 0, 0, 140, 255, 255, 68, 0, 0, 34, 138, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 52, 234, 83, 0, 172, 255, 255, 0, 0, 0, 4, 84, 2, 0, 0, 0, 0, 0, 0, 198, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 41, 0, 0, 10, 77, 33, 23, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 160, 160, 160, 160, 160, 160, 160, 237, 255, 243, 160, 160, 160, 160, 160, 160, 160, 160, 160, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 234, 83, 0, 172, 255, 255, 0, 0, 0, 4, 84, 2, 0, 0, 0, 0, 0, 0, 198, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 28, 195, 43, 0, 52, 176, 13, 0, 0, 0, 0, 0, 0, 226, 255, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 137, 250, 255, 253, 146, 14, 0, 0, 0, 0, 8, 196, 255, 255, 243, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 221, 255, 234, 20, 0, 0, 0, 8, 104, 244, 255, 236, 32, 0, 0, 0, 0, 0, 0, 0, 41, 244, 255, 255, 117, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 134, 226, 255, 103, 0, 0, 0, 0, 0, 0, 0, 148, 170, 68, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 105, 253, 255, 255, 236, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 185, 173, 167, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 214, 255, 255, 188, 164, 164, 164, 164, 164, 164, 164, 164, 164, 166, 170, 176, 187, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 207, 255, 222, 0, 0, 0, 0, 0, 15, 243, 240, 117, 8, 0, 0, 0, 0, 53, 203, 255, 119, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 119, 185, 173, 167, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 214, 255, 255, 188, 164, 164, 164, 164, 164, 164, 164, 164, 164, 166, 170, 176, 187, 119, 0, 0, 0, 0, 0, 0, 27, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 56, 88, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 91, 80, 39, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 38, 0, 0, 52, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 1, 0, 117, 254, 255, 123, 0, 0, 0, 0, 0, 0, 232, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 107, 232, 255, 255, 201, 50, 0, 0, 0, 0, 0, 0, 0, 5, 148, 255, 255, 253, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 89, 0, 0, 0, 0, 160, 255, 255, 255, 158, 100, 110, 124, 141, 161, 181, 203, 228, 251, 255, 255, 255, 252, 61, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 117, 255, 255, 255, 255, 255, 119, 0, 0, 0, 0, 0, 0, 2, 229, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 90, 251, 255, 255, 243, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 3, 0, 0, 182, 255, 249, 3, 0, 0, 0, 0, 114, 255, 255, 243, 43, 0, 0, 0, 0, 0, 6, 125, 4, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 156, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 44, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 240, 240, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 0, 92, 103, 5, 0, 0, 196, 255, 232, 0, 0, 7, 127, 196, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 0, 0, 52, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 255, 156, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 132, 255, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 0, 92, 103, 5, 0, 0, 196, 255, 232, 0, 0, 7, 127, 196, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 213, 204, 204, 204, 204, 204, 204, 237, 255, 255, 8, 0, 0, 0, 45, 252, 255, 237, 13, 0, 0, 0, 0, 0, 238, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 144, 232, 255, 255, 236, 102, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 250, 255, 255, 144, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 249, 255, 179, 0, 0, 0, 0, 0, 68, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 18, 0, 0, + 0, 0, 0, 35, 113, 201, 255, 255, 255, 255, 208, 92, 5, 0, 0, 0, 0, 0, 0, 70, 255, 255, 227, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 78, 248, 255, 255, 244, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 236, 212, 201, 192, 192, 192, 192, 192, 192, 192, 220, 255, 255, 255, 255, 255, 255, 255, 240, 192, 192, 192, 192, 192, 192, 192, 197, 204, 212, 220, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 41, 111, 192, 125, 0, 0, 144, 255, 255, 35, 0, 0, 0, 11, 231, 255, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 154, 236, 212, 201, 192, 192, 192, 192, 192, 192, 192, 220, 255, 255, 255, 255, 255, 255, 255, 240, 192, 192, 192, 192, 192, 192, 192, 197, 204, 212, 220, 137, 0, 0, 0, 0, 0, 0, 60, 255, 255, 222, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 135, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 11, 232, 255, 220, 39, 0, 196, 255, 232, 0, 0, 156, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 0, 0, 9, 48, 48, 214, 255, 247, 52, 48, 48, 48, 48, 48, 48, 48, 169, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 255, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 11, 232, 255, 220, 39, 0, 196, 255, 232, 0, 0, 156, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, 0, 161, 255, 255, 122, 0, 0, 0, 0, 0, 247, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 134, 197, 240, 255, 255, 255, 255, 182, 64, 59, 72, 84, 96, 108, 121, 132, 143, 154, 165, 176, 187, 197, 239, 255, 255, 255, 169, 7, 0, 0, 0, 0, 0, 0, 0, 4, 198, 255, 241, 27, 0, 0, 0, 0, 0, 5, 245, 255, 255, 255, 255, 255, 249, 202, 176, 211, 255, 255, 92, 37, 36, 235, 255, 209, 38, 0, 0, + 0, 100, 220, 255, 255, 255, 255, 255, 255, 90, 0, 0, 15, 227, 203, 184, 176, 176, 176, 218, 255, 255, 222, 176, 176, 176, 176, 176, 176, 176, 176, 177, 188, 201, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 71, 247, 255, 255, 246, 67, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 211, 255, 248, 182, 255, 255, 223, 255, 252, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 224, 233, 255, 255, 255, 154, 0, 0, 103, 255, 255, 75, 0, 0, 0, 126, 255, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 211, 255, 248, 182, 255, 255, 223, 255, 252, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 126, 255, 255, 213, 5, 0, 196, 255, 232, 0, 0, 33, 244, 255, 252, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 117, 255, 255, 66, 0, 0, 0, 0, 0, 0, 6, 235, 255, 251, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 255, 208, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 197, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 0, 126, 255, 255, 213, 5, 0, 196, 255, 232, 0, 0, 33, 244, 255, 252, 59, 0, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 171, 152, 152, 152, 152, 152, 152, 218, 255, 255, 8, 0, 0, 0, 0, 40, 254, 255, 234, 9, 0, 0, 0, 0, 254, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 15, 0, 0, 0, 0, 0, 0, 152, 255, 255, 103, 0, 5, 34, 72, 112, 141, 1, 132, 130, 88, 61, 229, 255, 201, 0, 0, 136, 255, 255, 36, 0, 0, 77, 164, 6, 0, 0, 0, + 0, 60, 255, 255, 255, 232, 188, 255, 255, 36, 0, 0, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 70, 247, 255, 255, 245, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 168, 255, 255, 117, 140, 255, 255, 99, 245, 255, 238, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 68, 116, 165, 217, 254, 255, 255, 255, 255, 255, 255, 222, 117, 0, 0, 60, 255, 255, 131, 0, 0, 36, 244, 255, 234, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 168, 255, 255, 117, 140, 255, 255, 99, 245, 255, 238, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 194, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 6, 210, 201, 191, 186, 183, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 208, 255, 255, 213, 180, 180, 180, 184, 189, 198, 207, 66, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 36, 247, 255, 253, 58, 0, 0, 196, 255, 232, 0, 0, 0, 112, 255, 255, 222, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 24, 251, 255, 157, 0, 0, 0, 0, 0, 0, 95, 255, 255, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 0, 36, 247, 255, 253, 58, 0, 0, 196, 255, 232, 0, 0, 0, 112, 255, 255, 222, 13, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 0, 0, 0, 0, 183, 255, 255, 107, 0, 0, 0, 6, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 255, 247, 234, 222, 212, 204, 196, 191, 205, 255, 255, 209, 169, 156, 143, 131, 118, 107, 96, 86, 122, 254, 255, 255, 204, 5, 0, 0, 0, 40, 179, 255, 255, 253, 199, 228, 253, 255, 255, 255, 166, 0, 0, 0, 0, 0, 223, 255, 198, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 214, 238, 117, 9, 100, 255, 255, 36, 0, 0, 11, 166, 151, 146, 144, 144, 221, 255, 255, 169, 144, 144, 144, 144, 185, 255, 255, 176, 144, 144, 148, 156, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 74, 250, 255, 255, 243, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, 190, 2, 140, 255, 255, 68, 101, 255, 255, 237, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 237, 255, 255, 255, 255, 255, 255, 255, 255, 229, 154, 90, 27, 0, 0, 0, 0, 7, 247, 255, 192, 0, 3, 198, 255, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, 190, 2, 140, 255, 255, 68, 101, 255, 255, 237, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 1, 189, 255, 255, 144, 0, 0, 0, 196, 255, 232, 0, 0, 0, 2, 199, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 0, 165, 255, 250, 30, 0, 0, 0, 0, 5, 221, 255, 254, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 125, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 94, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 1, 189, 255, 255, 144, 0, 0, 0, 196, 255, 232, 0, 0, 0, 2, 199, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 0, 0, 0, 0, 75, 255, 255, 218, 1, 0, 0, 17, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 3, 89, 56, 31, 9, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 199, 42, 0, 0, 0, 0, 99, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 230, 255, 190, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 63, 18, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 28, 247, 255, 188, 1, 0, 0, 0, 0, 166, 255, 236, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 89, 254, 255, 245, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 230, 23, 0, 140, 255, 255, 68, 0, 154, 255, 255, 249, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 255, 255, 231, 182, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 189, 255, 251, 24, 152, 255, 255, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 230, 23, 0, 140, 255, 255, 68, 0, 154, 255, 255, 249, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 5, 173, 167, 161, 155, 150, 144, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 183, 255, 255, 190, 140, 140, 141, 145, 149, 159, 168, 54, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 117, 255, 255, 219, 10, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 42, 249, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 0, 0, 15, 0, 0, 0, 42, 254, 255, 169, 0, 0, 0, 0, 115, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 108, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 73, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 0, 117, 255, 255, 219, 10, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 42, 249, 255, 255, 69, 0, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 0, 0, 0, 0, 2, 227, 255, 255, 65, 0, 0, 26, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 4, 0, 0, 0, 0, 0, 16, 249, 255, 255, 242, 201, 158, 121, 87, 54, 22, 0, 0, 0, 0, 0, 1, 248, 255, 165, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 161, 255, 245, 32, 0, 0, 0, 0, 33, 251, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 238, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 247, 57, 0, 0, 140, 255, 255, 68, 0, 4, 179, 255, 255, 255, 145, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 0, 4, 167, 25, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 247, 184, 114, 44, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 209, 255, 255, 222, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 247, 57, 0, 0, 140, 255, 255, 68, 0, 4, 179, 255, 255, 255, 145, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 57, 251, 255, 251, 55, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 139, 255, 255, 223, 11, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 0, 114, 141, 0, 0, 0, 0, 165, 255, 255, 94, 0, 0, 40, 247, 255, 244, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 0, 57, 251, 255, 251, 55, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 139, 255, 255, 223, 11, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 0, 0, 0, 0, 0, 132, 208, 73, 0, 0, 0, 37, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 169, 39, 0, 0, 0, 0, 0, 0, 72, 255, 255, 100, 0, 0, 0, 0, 102, 171, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 102, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 136, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 54, 254, 255, 213, 70, 2, 0, 0, 0, 169, 255, 244, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 155, 255, 255, 253, 82, 0, 0, 0, 140, 255, 255, 68, 0, 0, 7, 183, 255, 255, 255, 207, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 0, 2, 162, 255, 155, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 77, 10, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 19, 248, 255, 255, 255, 240, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 155, 255, 255, 253, 82, 0, 0, 0, 140, 255, 255, 68, 0, 0, 7, 183, 255, 255, 255, 207, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 255, 19, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 198, 217, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 33, 232, 255, 255, 125, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 14, 234, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 36, 82, 252, 238, 18, 0, 0, 0, 23, 239, 255, 247, 67, 17, 214, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 33, 232, 255, 255, 125, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 14, 234, 255, 255, 145, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 0, 0, 0, 0, 0, 12, 2, 0, 0, 0, 0, 52, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 186, 255, 246, 119, 4, 0, 0, 0, 0, 72, 255, 255, 100, 0, 0, 0, 87, 249, 255, 222, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 86, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 123, 249, 255, 255, 255, 226, 124, 18, 68, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 206, 255, 255, 252, 94, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 6, 166, 255, 255, 255, 253, 165, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 216, 2, 156, 255, 255, 236, 11, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 9, 210, 255, 255, 249, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 206, 255, 255, 252, 94, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 6, 166, 255, 255, 255, 253, 165, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 241, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 180, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 7, 215, 255, 255, 188, 3, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 103, 255, 255, 248, 27, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 117, 250, 255, 255, 116, 0, 0, 0, 0, 96, 255, 255, 246, 206, 255, 255, 186, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 115, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 82, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 7, 215, 255, 255, 188, 3, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 103, 255, 255, 248, 27, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 159, 255, 255, 254, 151, 16, 0, 0, 0, 0, 72, 255, 255, 100, 0, 0, 0, 99, 244, 255, 255, 251, 120, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 92, 48, 0, 0, 0, 185, 255, 255, 29, 0, 0, 136, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 23, 126, 230, 255, 255, 255, 241, 232, 255, 213, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 123, 246, 255, 255, 249, 87, 0, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 1, 132, 254, 255, 255, 255, 255, 193, 97, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 222, 167, 255, 255, 223, 41, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 44, 212, 255, 255, 255, 240, 32, 0, 0, 0, 0, 39, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, 7, 123, 246, 255, 255, 249, 87, 0, 0, 0, 0, 0, 140, 255, 255, 68, 0, 0, 0, 0, 1, 132, 254, 255, 255, 255, 255, 193, 97, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 246, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 85, 244, 224, 20, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 3, 210, 200, 46, 0, 0, 0, 0, 0, 0, 0, 0, 137, 255, 255, 255, 255, 255, 177, 8, 0, 0, 0, 0, 0, 148, 255, 255, 255, 255, 226, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 182, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 164, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 0, 0, 85, 244, 224, 20, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 3, 210, 200, 46, 0, 0, 0, 0, 0, 124, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 255, 52, 0, 0, 0, 0, 0, 0, 0, 0, 1, 157, 255, 255, 240, 75, 0, 0, 0, 0, 0, 0, 72, 255, 255, 100, 0, 0, 0, 0, 36, 206, 255, 255, 255, 182, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 108, 180, 249, 255, 73, 0, 0, 59, 255, 255, 199, 0, 0, 0, 136, 255, 255, 36, 0, 0, 0, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 89, 199, 255, 255, 255, 255, 211, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 93, 220, 255, 255, 255, 237, 62, 0, 0, 0, 0, 0, 0, 141, 255, 255, 68, 0, 0, 0, 0, 0, 0, 76, 237, 255, 255, 255, 255, 255, 235, 32, 0, 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 255, 255, 216, 27, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 132, 0, 0, 0, 0, 0, 7, 127, 248, 255, 255, 252, 255, 255, 202, 9, 0, 0, 0, 89, 253, 154, 29, 0, 0, 0, 0, 0, 0, 0, 173, 255, 255, 36, 0, 0, 0, 128, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 0, 3, 93, 220, 255, 255, 255, 237, 62, 0, 0, 0, 0, 0, 0, 141, 255, 255, 68, 0, 0, 0, 0, 0, 0, 76, 237, 255, 255, 255, 255, 255, 235, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 255, 171, 0, 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 249, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, 255, 0, 0, 0, 47, 49, 0, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 0, 27, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 255, 255, 167, 4, 0, 0, 0, 0, 0, 0, 58, 252, 255, 255, 255, 134, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, 255, 0, 0, 0, 47, 49, 0, 0, 0, 0, 0, 0, 0, 196, 255, 232, 0, 0, 0, 0, 0, 0, 0, 27, 2, 0, 0, 0, 0, 0, 0, 126, 255, 255, 246, 244, 244, 244, 244, 244, 244, 251, 255, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 255, 255, 37, 0, 0, 0, 0, 0, 0, 0, 10, 172, 255, 255, 229, 44, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 100, 0, 0, 0, 0, 0, 7, 153, 255, 255, 255, 224, 46, 0, 0, 0, 0, 0, 0, 0, 16, 65, 119, 172, 230, 255, 255, 255, 255, 255, 80, 0, 6, 202, 255, 255, 98, 0, 0, 0, 136, 255, 255, 36, 0, 0, 0, 122, 180, 55, 0, 0, + 0, 0, 0, 0, 0, 0, 109, 255, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 163, 255, 255, 255, 255, 255, 255, 204, 79, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 196, 255, 255, 255, 255, 207, 33, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 23, 165, 254, 255, 255, 245, 50, 0, 0, 0, 0, 0, 0, 0, 0, 25, 255, 255, 255, 255, 211, 23, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 132, 0, 0, 0, 2, 88, 218, 255, 255, 255, 193, 36, 191, 255, 255, 186, 8, 0, 0, 131, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 37, 0, 0, 0, 129, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 124, 0, 0, 0, 0, 5, 196, 255, 255, 255, 255, 207, 33, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 23, 165, 254, 255, 255, 245, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 255, 134, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 205, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 163, 3, 0, 0, 0, 0, 0, 9, 128, 250, 255, 255, 255, 255, 255, 203, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 172, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 153, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 255, 255, 23, 0, 0, 0, 0, 0, 0, 34, 205, 255, 255, 218, 31, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 100, 0, 0, 0, 0, 0, 0, 0, 110, 253, 255, 255, 240, 62, 0, 0, 0, 45, 182, 227, 255, 255, 255, 255, 255, 255, 254, 209, 138, 68, 7, 0, 147, 255, 255, 214, 3, 0, 0, 0, 136, 255, 255, 36, 0, 0, 0, 169, 255, 255, 115, 0, + 0, 0, 0, 0, 0, 0, 178, 255, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0, 52, 144, 245, 255, 255, 226, 85, 164, 254, 255, 255, 255, 207, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 215, 255, 255, 150, 9, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 181, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 255, 255, 255, 218, 23, 8, 131, 109, 90, 81, 74, 72, 72, 72, 238, 255, 238, 72, 72, 72, 72, 72, 77, 83, 92, 111, 132, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 126, 0, 16, 107, 214, 255, 255, 255, 246, 115, 2, 0, 23, 224, 255, 255, 203, 35, 0, 203, 255, 255, 66, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 40, 0, 0, 0, 131, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 124, 0, 0, 0, 0, 0, 31, 215, 255, 255, 150, 9, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 181, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 239, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 245, 255, 212, 30, 0, 0, 0, 0, 0, 0, 0, 0, 7, 188, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 249, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 255, 174, 3, 0, 0, 0, 0, 8, 104, 226, 255, 255, 246, 127, 104, 235, 255, 255, 255, 219, 123, 45, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 249, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 139, 112, 112, 112, 112, 112, 112, 204, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 250, 255, 240, 2, 0, 0, 0, 0, 0, 97, 243, 255, 255, 205, 21, 0, 0, 0, 0, 0, 0, 0, 0, 4, 153, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 78, 249, 255, 245, 70, 0, 0, 0, 4, 212, 255, 255, 255, 255, 234, 165, 94, 24, 0, 0, 0, 0, 124, 255, 255, 250, 57, 0, 0, 0, 0, 136, 255, 255, 61, 0, 0, 12, 240, 255, 255, 46, 0, + 0, 0, 57, 188, 170, 190, 255, 255, 253, 13, 0, 0, 0, 9, 48, 99, 155, 219, 255, 255, 255, 254, 166, 17, 0, 0, 48, 189, 255, 255, 255, 255, 204, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 199, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 14, 0, 0, 0, 0, 0, 0, 0, 0, 2, 218, 255, 255, 226, 30, 0, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 90, 154, 171, 209, 255, 255, 255, 95, 0, 136, 255, 255, 255, 255, 191, 36, 0, 0, 0, 0, 37, 232, 255, 255, 250, 212, 255, 255, 222, 2, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 44, 0, 0, 0, 135, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 69, 172, 171, 187, 247, 255, 255, 117, 0, 0, 0, 0, 0, 0, 33, 199, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 197, 210, 208, 210, 218, 238, 255, 255, 255, 255, 35, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 133, 6, 0, 0, 0, 0, 0, 24, 186, 192, 210, 244, 255, 255, 255, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 255, 255, 11, 0, 0, 0, 0, 0, 0, 17, 183, 196, 221, 255, 255, 255, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 218, 255, 197, 7, 0, 0, 0, 49, 148, 238, 255, 255, 254, 167, 31, 0, 0, 16, 141, 244, 255, 255, 255, 255, 242, 188, 157, 136, 52, 0, 0, 0, 163, 212, 206, 200, 194, 187, 244, 255, 213, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 203, 255, 254, 185, 187, 190, 196, 206, 191, 0, 0, 0, 0, 0, 0, 0, 0, 183, 255, 255, 11, 0, 0, 0, 0, 0, 0, 17, 183, 196, 221, 255, 255, 255, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 28, 0, 0, 0, 0, 0, 11, 170, 172, 178, 197, 237, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, 36, 199, 255, 189, 13, 0, 0, 0, 0, 0, 1, 185, 224, 233, 251, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 240, 65, 0, 0, 0, 0, 0, 84, 255, 224, 138, 58, 2, 0, 0, 0, 0, 0, 3, 142, 255, 255, 255, 109, 0, 0, 0, 0, 0, 119, 255, 255, 212, 118, 112, 187, 255, 255, 199, 0, 0, + 0, 0, 2, 224, 255, 255, 255, 255, 183, 0, 0, 0, 50, 252, 255, 255, 255, 255, 255, 253, 172, 46, 0, 0, 0, 0, 0, 0, 80, 223, 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 206, 243, 46, 0, 0, 16, 255, 255, 255, 254, 250, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 251, 254, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 34, 250, 255, 255, 255, 255, 226, 15, 0, 0, 161, 255, 231, 93, 0, 0, 0, 0, 0, 0, 0, 34, 216, 255, 255, 255, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 189, 255, 255, 50, 0, 0, 0, 139, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 16, 243, 255, 255, 255, 255, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 255, 255, 255, 255, 255, 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 255, 255, 255, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 17, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 222, 27, 0, 78, 166, 228, 255, 255, 255, 255, 185, 51, 0, 0, 0, 0, 0, 0, 18, 124, 229, 255, 255, 255, 255, 255, 221, 13, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 190, 255, 255, 17, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 255, 48, 0, 0, 0, 0, 0, 0, 164, 255, 255, 37, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 255, 255, 255, 196, 16, 0, 0, 0, 0, 0, 0, 0, 8, 118, 7, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 223, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 2, 60, 1, 0, 0, 0, 0, 0, 0, 0, 34, 198, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 56, 255, 255, 255, 255, 255, 255, 255, 253, 62, 0, 0, + 0, 0, 0, 137, 255, 255, 250, 170, 20, 0, 0, 0, 0, 161, 255, 255, 252, 196, 117, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 139, 253, 185, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 136, 136, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 172, 172, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 71, 0, 0, 0, 1, 22, 11, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 12, 23, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, 255, 233, 148, 25, 0, 0, 0, 15, 119, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 139, 245, 255, 255, 187, 3, 0, 0, 0, 0, 0, 0, 0, 0, 197, 255, 255, 57, 0, 0, 0, 148, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 255, 255, 230, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 172, 172, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 255, 255, 255, 255, 255, 244, 187, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 255, 255, 254, 195, 94, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 255, 27, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 252, 195, 95, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 22, 197, 255, 255, 248, 164, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 66, 155, 231, 255, 255, 91, 0, 0, 0, 0, 46, 56, 50, 44, 37, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 33, 36, 42, 51, 53, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 255, 27, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 252, 195, 95, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 136, 136, 25, 0, 0, 0, 0, 0, 0, 41, 64, 64, 10, 0, 0, 0, 0, 0, 0, 52, 255, 255, 255, 233, 163, 82, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 251, 255, 244, 193, 127, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 191, 255, 254, 122, 0, 0, 0, 0, 0, 0, 0, 0, 104, 214, 242, 244, 244, 244, 223, 91, 0, 0, 0, + 0, 0, 0, 48, 107, 68, 15, 0, 0, 0, 0, 0, 0, 53, 130, 74, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 117, 58, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 78, 74, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 204, 204, 53, 0, 0, 0, 104, 168, 168, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 137, 96, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 123, 106, 87, 61, 30, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 24, 24, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 139, 83, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 60, 60, 8, 0, 0, 0, 0, 0, 0, 0, 0, 107, 69, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 169, 94, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 103, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 60, 60, 8, 0, 0, 0, 0, 0, 0, 0, 0, 107, 69, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 113, 77, 37, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 40, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 145, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const int g_txt_title_w = 240; diff --git a/CODE/font/render_embedded.py b/CODE/font/render_embedded.py index 87ac964..d44ddbd 100644 --- a/CODE/font/render_embedded.py +++ b/CODE/font/render_embedded.py @@ -4,8 +4,8 @@ from PIL import Image, ImageFont, ImageDraw ttf = "SYSTEM/system/font/simhei.ttf" texts = [ - ("welcome", "欢迎使用 Deshab", 48), # only welcome stays large - ("setup", "接下来让我们来引导你设置你的系统", 24), # 32 -> 24 + ("welcome", "欢迎使用 Deshab", 36), + ("setup", "接下来让我们来引导你设置你的系统", 36), ("title", "创建你的 Deshab 账户", 24), ("computer", "计算机名", 24), ("username", "用户名", 24), diff --git a/CODE/tools/bash/MAKEFILE b/CODE/login/MAKEFILE similarity index 69% rename from CODE/tools/bash/MAKEFILE rename to CODE/login/MAKEFILE index 1a9b8a2..fc329ac 100644 --- a/CODE/tools/bash/MAKEFILE +++ b/CODE/login/MAKEFILE @@ -1,10 +1,10 @@ CC := clang LD := ld.lld -ROOT := ../../.. -OUT := $(ROOT)/SYSTEM/system/deshab64/tools/bash.elf +ROOT := ../.. +OUT := $(ROOT)/SYSTEM/system/user/use/login.elf -CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2 +CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -I../firstInit -O2 LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld C_SOURCES := main.c @@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o) all: $(OUT) $(OUT): $(OBJECTS) linker.ld - @if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools" + @if not exist "$(ROOT)\SYSTEM\system\user\use" mkdir "$(ROOT)\SYSTEM\system\user\use" $(LD) $(LDFLAGS) -o $@ $(OBJECTS) %.o: %.c diff --git a/CODE/login/linker.ld b/CODE/login/linker.ld new file mode 100644 index 0000000..473e5fc --- /dev/null +++ b/CODE/login/linker.ld @@ -0,0 +1,24 @@ +ENTRY(dsk_entry) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); +} + +SECTIONS +{ + . = 0x1000; + .text : ALIGN(4K) { *(.text*) } :text + .rodata : ALIGN(4K) { *(.rodata*) } :rodata + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic +} diff --git a/CODE/login/main.c b/CODE/login/main.c new file mode 100644 index 0000000..4c73aee --- /dev/null +++ b/CODE/login/main.c @@ -0,0 +1,565 @@ +/* login.elf — Deshab login screen + * Shown on non-first boot (firstInit=1) before loading desktop. + * Reads USER.CONF via boot context (ctx->reserved[0/1]), + * extracts username + password hash, prompts for password, + * verifies via SHA256 comparison, sets ctx->reserved[2]=1 on success. + * + * Returns to DSK in all cases (success, skip via Esc, or fatal error). + * DSK loads desktop.elf after this returns. + */ + +#include "../UTSM/include/utsm/dsk.h" + +typedef signed char i8; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; + +#define COM1 0x3F8 + +static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); } +static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; } + +/* ---- TSC-based timing (PIT-calibrated) ---- */ +static u64 g_tsc_per_ms = 0; + +static __inline__ u64 rdtsc_fi(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +static void tsc_calibrate_fi(void) { + outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */ + outb(0x40, 0x7c); /* 11932 low = ~10ms */ + outb(0x40, 0x2e); /* 11932 high */ + u64 tsc_start = rdtsc_fi(); + u16 prev = 0; u64 loops = 0; + for (;;) { + outb(0x43, 0x00); + u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = rdtsc_fi(); + g_tsc_per_ms = (tsc_end - tsc_start) / 10; +} + +static void delay_ms_fi(u32 ms) { + if (!g_tsc_per_ms) { + for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause"); + return; + } + u64 target = g_tsc_per_ms * ms; + u64 start = rdtsc_fi(); + while (rdtsc_fi() - start < target) __asm__ volatile("pause"); +} + +static void delay_frame(void) { delay_ms_fi(33); } + +/* ---- Serial logging (COM1) ---- */ +static void sputc(char c) { + for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; } + outb(COM1, (unsigned char)c); +} +static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } } +static void logl(const char *s) { swrite(s); swrite("\n"); } + +/* ---- Colors (dark theme matching FirstInit) ---- */ +#define BG_TOP 0xFF2D2D30u +#define BG_BOTTOM 0xFF1A1A1Eu +#define CARD_BG 0xFF353539u +#define INPUT_BG 0xFF2A2A2Eu +#define TEXT_FG 0xFFE0E0E0u +#define ACCENT 0xFF5A8AC8u +#define ERROR_FG 0xFFE05050u +#define DIM_FG 0xFF808088u + +static u64 fb_a, fb_w, fb_h, fb_p; + +static u32 blend(u32 c1, u32 c2, u32 a) { + u32 na=256-a; + u32 r=((c1&0xFF)*na+(c2&0xFF)*a)>>8; + u32 g=(((c1>>8)&0xFF)*na+((c2>>8)&0xFF)*a)>>8; + u32 b=(((c1>>16)&0xFF)*na+((c2>>16)&0xFF)*a)>>8; + return 0xFF000000|(b<<16)|(g<<8)|r; +} + +/* ---- ASCII font (Consolas 18px grayscale) — shared from firstInit ---- */ +#include "../firstInit/ascii_bitmaps.c" +#define ASCII_W 11 +#define ASCII_H 18 +#define ASCII_STEP 11 + +static void fb_char(u32 *fb, u32 ch, i64 x, i64 y, u32 fg, u32 bg) { + if (ch < ' ' || ch > '~') ch = ' '; + u32 idx = (u32)(ch - ' '); + const u8 *g = g_ascii[idx]; + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = (a == 255) ? fg : blend(bg, fg, a); + } + } +} + +static void fb_text(u32 *fb, const char *s, i64 x, i64 y, u32 fg, u32 bg) { + for (i64 i=0; s[i]; i++) fb_char(fb, (u32)(u8)s[i], x+i*ASCII_STEP, y, fg, bg); +} + +static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) { + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = color; + } + } +} + +static u32 bg_at_y(i64 y) { + if (fb_h <= 1) return BG_TOP; + if (y < 0) y = 0; + if ((u64)y >= fb_h) y = (i64)fb_h - 1; + return blend(BG_TOP, BG_BOTTOM, (u32)(((u64)y * 255ULL) / (fb_h - 1))); +} + +static void fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) { + for (i64 r=0; r= fb_h) continue; + u32 color = bg_at_y(yy); + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = color; + } + } +} + +static int rounded_rect_contains(i64 px, i64 py, i64 x, i64 y, i64 w, i64 h, i64 radius) { + if (px < x || py < y || px >= x + w || py >= y + h) return 0; + if (radius <= 0) return 1; + if (radius * 2 > w) radius = w / 2; + if (radius * 2 > h) radius = h / 2; + i64 left = x + radius; + i64 right = x + w - radius - 1; + i64 top = y + radius; + i64 bottom = y + h - radius - 1; + if (px >= left && px <= right) return 1; + if (py >= top && py <= bottom) return 1; + i64 cx = px < left ? left : right; + i64 cy = py < top ? top : bottom; + i64 dx = px - cx; + i64 dy = py - cy; + return dx * dx + dy * dy <= radius * radius; +} + +static void fill_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, u32 color) { + if (w <= 0 || h <= 0) return; + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + if (rounded_rect_contains(xx, yy, x, y, w, h, radius)) line[(u64)xx] = color; + } + } +} + +static void stroke_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, i64 thickness, u32 color) { + if (w <= 0 || h <= 0 || thickness <= 0) return; + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + int outer = rounded_rect_contains(xx, yy, x, y, w, h, radius); + int inner = rounded_rect_contains(xx, yy, x + thickness, y + thickness, w - thickness * 2, h - thickness * 2, radius - thickness); + if (outer && !inner) line[(u64)xx] = color; + } + } +} + +static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card) { + fill_rounded_rect(fb, x, y, w, h, 4, card); +} + +static void draw_rounded_input(u32 *fb, i64 x, i64 y, i64 w, i64 h, const char *value, int mask, u32 fg, u32 card_bg, int active) { + (void)card_bg; + u32 fill = active ? 0xFF45454Au : INPUT_BG; + fill_rounded_rect(fb, x, y, w, h, 2, fill); + stroke_rounded_rect(fb, x, y, w, h, 2, 1, active ? 0xFF6C6C72 : 0xFF404044); + char out[80]; int i=0; + while(value[i] && i<76) { out[i] = mask ? '*' : value[i]; i++; } + out[i]=0; + if (out[0]) fb_text(fb, out, x+14, y+(h-ASCII_H)/2, fg, fill); + if (active) { + i64 cx2 = x + 14 + i * ASCII_STEP; + fill_rect(fb, cx2, y+10, 2, h-20, fg); + } +} + +/* ---- PS/2 scan code set 1 → ASCII ---- */ +static char scan_to_ascii(u8 sc, int shift) { + static const char normal[58] = { + 0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t', + 'q','w','e','r','t','y','u','i','o','p','[',']','\n',0, + 'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\', + 'z','x','c','v','b','n','m',',','.','/',0,'*',0,' ' + }; + static const char shifted[58] = { + 0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t', + 'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0, + 'A','S','D','F','G','H','J','K','L',':','"','~',0,'|', + 'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' ' + }; + if (sc >= 58) return 0; + return shift ? shifted[sc] : normal[sc]; +} + +/* ---- SHA-256 (compact, copied from FirstInit) ---- */ +#define ROR(x,n) (((x)>>(n))|((x)<<(32-(n)))) +static const u32 K256[64] = {0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2}; +static void sha256(const char *msg, u8 out[32]) { + u32 h[8]={0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19}; + u8 block[64]; u64 len=0; while(msg[len]) len++; + for(int i=0;i<64;i++) block[i]=0; + for(u64 i=0;i>(i*8)); + u32 w[64]; + for(int i=0;i<16;i++) w[i]=((u32)block[i*4]<<24)|((u32)block[i*4+1]<<16)|((u32)block[i*4+2]<<8)|block[i*4+3]; + for(int i=16;i<64;i++){u32 s0=ROR(w[i-15],7)^ROR(w[i-15],18)^(w[i-15]>>3);u32 s1=ROR(w[i-2],17)^ROR(w[i-2],19)^(w[i-2]>>10);w[i]=w[i-16]+s0+w[i-7]+s1;} + u32 a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7]; + for(int i=0;i<64;i++){u32 S1=ROR(e,6)^ROR(e,11)^ROR(e,25);u32 ch=(e&f)^((~e)&g);u32 t1=hh+S1+ch+K256[i]+w[i];u32 S0=ROR(a,2)^ROR(a,13)^ROR(a,22);u32 maj=(a&b)^(a&c)^(b&c);u32 t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;} + h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh; + for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];} +} + +/* ---- conf parsing ---- + * USER.CONF format (built by FirstInit's build_user_conf, but stored unencrypted + * on disk for login.elf to parse): + * DESHAB_USERCONF_V1 + * computer= + * username= + * passwordSha256=<64 hex chars> + * ... + * + * If the buffer does NOT start with "DESHAB_USERCONF_V1", it may be XOR-encrypted + * with the password hash stream. In that case, the user's entered password is + * used to decrypt: SHA256(password) → hash, then buf[i] ^= hash[i & 31]. + * If the decrypted buffer starts with the magic, the password is correct. + */ + +static int streq_n(const char *a, const char *b, u32 n) { + for (u32 i = 0; i < n; i++) { + if (a[i] != b[i]) return 0; + } + return 1; +} + +/* Find "key=" in buf, copy value (up to newline) into out (NUL-terminated). + * Returns 0 on success, -1 if key not found. */ +static int conf_get_value(const u8 *buf, u32 size, const char *key, char *out, u32 out_cap) { + u32 klen = 0; while (key[klen]) klen++; + for (u32 i = 0; i + klen + 1 < size; i++) { + /* Match key at start of line (or start of buffer) */ + if (i > 0 && buf[i-1] != '\n') continue; + if (buf[i + klen] != '=') continue; + if (!streq_n((const char *)buf + i, key, klen)) continue; + /* Found key=, copy value until newline or end */ + u32 vstart = i + klen + 1; + u32 j = 0; + while (vstart + j < size && buf[vstart + j] != '\n' && buf[vstart + j] != '\r' && j + 1 < out_cap) { + out[j] = (char)buf[vstart + j]; + j++; + } + out[j] = 0; + return 0; + } + return -1; +} + +static int starts_with(const u8 *buf, u32 size, const char *prefix) { + u32 i = 0; + while (prefix[i]) { + if (i >= size) return 0; + if (buf[i] != (u8)prefix[i]) return 0; + i++; + } + return 1; +} + +/* ---- hex string comparison ---- */ +static int hexeq(const u8 *hash32, const char *hex64) { + static const char hx[] = "0123456789abcdef"; + for (int i = 0; i < 32; i++) { + if (hex64[i*2] != hx[hash32[i] >> 4]) return 0; + if (hex64[i*2+1] != hx[hash32[i] & 0xf]) return 0; + } + return 1; +} + +/* ---- login state ---- */ +static char g_username[64]; +static char g_password_hash[80]; /* 64 hex chars + NUL */ +static int g_has_user; /* 1 if username extracted */ +static int g_has_hash; /* 1 if passwordSha256 extracted */ +static int g_is_encrypted; /* 1 if conf buffer is XOR-encrypted */ + +/* ---- UI layout ---- */ +#define CARD_W 560 +#define CARD_H 400 + +static void draw_login_card(u32 *fb, i64 card_x, i64 card_y, const char *pass, int active, + const char *msg, u32 msg_color, u32 fg) { + u32 card = CARD_BG; + u32 sub_fg = 0xFF9098A0; + /* Background + card */ + fill_gradient_rect(fb, card_x - 24, card_y - 24, CARD_W + 48, CARD_H + 48); + draw_card(fb, card_x, card_y, CARD_W, CARD_H, card); + /* Title */ + fb_text(fb, "Deshab Login", card_x + 40, card_y + 40, fg, card); + fb_text(fb, "Enter your password to continue", card_x + 40, card_y + 70, sub_fg, card); + /* Username field */ + fb_text(fb, "Username", card_x + 40, card_y + 120, sub_fg, card); + draw_rounded_input(fb, card_x + 40, card_y + 150, CARD_W - 80, 44, + g_has_user ? g_username : "(unknown)", 0, fg, card, 0); + /* Password field */ + fb_text(fb, "Password", card_x + 40, card_y + 210, sub_fg, card); + draw_rounded_input(fb, card_x + 40, card_y + 240, CARD_W - 80, 44, pass, 1, fg, card, active); + /* Login button */ + { + u32 btn_bg = ACCENT; + fill_rounded_rect(fb, card_x + 40, card_y + 310, 160, 44, 3, btn_bg); + fb_text(fb, "Login", card_x + 40 + 56, card_y + 310 + (44 - ASCII_H) / 2, + 0xFFFFFFFFu, btn_bg); + } + /* Status / error message */ + if (msg && msg[0]) { + fb_text(fb, msg, card_x + 220, card_y + 322, msg_color, card); + } + /* Bottom hint */ + fb_text(fb, "Enter=login Esc=skip Backspace=delete", + card_x + 40, card_y + CARD_H - 28, DIM_FG, card); +} + +/* Redraw only the password input field (avoids full card redraw on each keystroke) */ +static void redraw_password_only(u32 *fb, i64 card_x, i64 card_y, const char *pass, u32 fg) { + u32 card = CARD_BG; + i64 fy = card_y + 240; + fill_rect(fb, card_x + 40, fy, CARD_W - 80, 44, card); + draw_rounded_input(fb, card_x + 40, fy, CARD_W - 80, 44, pass, 1, fg, card, 1); +} + +/* ---- verify password against stored hash or encrypted conf ---- + * Returns 1 if password is correct, 0 otherwise. + * For encrypted conf: XOR-decrypt with SHA256(password); if magic matches, correct. + * For unencrypted conf: SHA256(password) == stored passwordSha256 hex string. + */ +static int verify_password(const u8 *conf_buf, u32 conf_size, const char *password) { + if (g_is_encrypted) { + /* Decrypt a copy and check magic */ + u8 hash[32]; + sha256(password, hash); + static u8 scratch[2048]; + u32 sz = conf_size; + if (sz > sizeof(scratch)) sz = sizeof(scratch); + for (u32 i = 0; i < sz; i++) scratch[i] = conf_buf[i] ^ hash[i & 31]; + if (starts_with(scratch, sz, "DESHAB_USERCONF_V1")) { + /* Also extract username from decrypted buffer for display */ + if (!g_has_user) { + conf_get_value(scratch, sz, "username", g_username, sizeof(g_username)); + if (g_username[0]) g_has_user = 1; + } + return 1; + } + return 0; + } else if (g_has_hash) { + u8 hash[32]; + sha256(password, hash); + return hexeq(hash, g_password_hash); + } + return 0; +} + +__attribute__((visibility("default"))) +void dsk_entry(const dsk_boot_context *ctx) { + __asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */ + logl("[login] boot"); + logl("[login] calibrating TSC"); + tsc_calibrate_fi(); + + if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) { + logl("[login] bad context"); + return; + } + + fb_a = ctx->framebuffer_address; + fb_w = ctx->framebuffer_width; + fb_h = ctx->framebuffer_height; + fb_p = ctx->framebuffer_pitch; + + logl("[login] framebuffer ready"); + + /* Get conf data from boot context (passed by DSK) */ + const u8 *conf_buf = (const u8 *)(u64)ctx->reserved[0]; + u32 conf_size = (u32)ctx->reserved[1]; + if (!conf_buf || conf_size == 0) { + logl("[login] no conf data, cannot login"); + /* Mark as skipped (reserved[2]=0) and return */ + ((dsk_boot_context *)ctx)->reserved[2] = 0; + return; + } + logl("[login] conf data received"); + + /* Initialize state */ + g_username[0] = 0; + g_password_hash[0] = 0; + g_has_user = 0; + g_has_hash = 0; + g_is_encrypted = 0; + + /* Check if conf is unencrypted (starts with magic) or encrypted */ + if (starts_with(conf_buf, conf_size, "DESHAB_USERCONF_V1")) { + logl("[login] conf is plaintext, parsing"); + g_is_encrypted = 0; + if (conf_get_value(conf_buf, conf_size, "username", g_username, sizeof(g_username)) == 0) { + g_has_user = 1; + logl("[login] username found"); + } else { + logl("[login] username not found in conf"); + } + if (conf_get_value(conf_buf, conf_size, "passwordSha256", g_password_hash, sizeof(g_password_hash)) == 0) { + g_has_hash = 1; + logl("[login] password hash found"); + } else { + logl("[login] passwordSha256 not found in conf"); + } + } else { + logl("[login] conf appears encrypted, will verify by password decryption"); + g_is_encrypted = 1; + /* Username will be extracted after successful decryption */ + } + + u32 fg = TEXT_FG; + u32 *fb = (u32 *)(u64)fb_a; + + /* Fill background */ + fill_gradient_rect(fb, 0, 0, (i64)fb_w, (i64)fb_h); + + /* Card position (centered) */ + i64 card_x = ((i64)fb_w - CARD_W) / 2; + i64 card_y = ((i64)fb_h - CARD_H) / 2; + + /* Initial message */ + const char *msg = "Enter password and press Enter"; + u32 msg_color = DIM_FG; + + /* Password input buffer */ + char pass[64]; + pass[0] = 0; + int plen = 0; + + /* Draw initial UI */ + draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg); + + /* Input loop */ + int shift = 0; + int release = 0; + for (;;) { + u8 st = inb(0x64); + if (!(st & 1)) { __asm__("pause"); continue; } + u8 data = inb(0x60); + /* Skip mouse data (bit 5 of status) */ + if (st & 0x20) continue; + + u8 sc = data; + /* Handle extended (E0) sequences — ignore (no arrow keys needed for login) */ + if (sc == 0xE0) { continue; } + /* Handle release (F0 in Set 2, or high bit in Set 1) */ + if (sc == 0xF0) { release = 1; continue; } + if (release) { release = 0; /* track shift release below */ } + + /* Shift press/release */ + if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } + if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } + + /* Ignore high-bit (release in Set 1) */ + if (sc & 0x80) continue; + + /* Escape (sc 0x01) — skip login */ + if (sc == 0x01) { + logl("[login] Esc pressed, skipping login"); + ((dsk_boot_context *)ctx)->reserved[2] = 0; + return; + } + + char c = scan_to_ascii(sc, shift); + if (!c) continue; + + if (c == '\n') { + /* Submit password */ + if (plen == 0) { + /* Empty password — show hint, don't verify */ + msg = "Password is empty"; + msg_color = ERROR_FG; + draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg); + continue; + } + logl("[login] verifying password"); + int ok = verify_password(conf_buf, conf_size, pass); + if (ok) { + logl("[login] password correct"); + ((dsk_boot_context *)ctx)->reserved[2] = 1; + /* Show success message briefly */ + draw_login_card(fb, card_x, card_y, pass, 0, + "Login successful", 0xFF5FAF6Fu, fg); + for (int i = 0; i < 30; i++) delay_frame(); + return; + } else { + logl("[login] password wrong"); + msg = "Wrong password, try again"; + msg_color = ERROR_FG; + /* Clear password */ + pass[0] = 0; + plen = 0; + draw_login_card(fb, card_x, card_y, pass, 1, msg, msg_color, fg); + continue; + } + } + + if (c == 8) { + /* Backspace */ + if (plen > 0) { + pass[--plen] = 0; + redraw_password_only(fb, card_x, card_y, pass, fg); + } + continue; + } + + /* Regular character */ + if (plen < 62 && c >= 32 && c <= 126) { + pass[plen++] = c; + pass[plen] = 0; + redraw_password_only(fb, card_x, card_y, pass, fg); + } + } +} diff --git a/CODE/mouse/linker.ld b/CODE/mouse/linker.ld index 825d5f1..473e5fc 100644 --- a/CODE/mouse/linker.ld +++ b/CODE/mouse/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(5); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(6); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/netman/linker.ld b/CODE/netman/linker.ld index 825d5f1..473e5fc 100644 --- a/CODE/netman/linker.ld +++ b/CODE/netman/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(5); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(6); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/netman/main.c b/CODE/netman/main.c index 63a4782..8e293ff 100644 --- a/CODE/netman/main.c +++ b/CODE/netman/main.c @@ -249,8 +249,48 @@ static u16 udp_checksum_ipv4(const u8 *ip, const u8 *udp, u32 udp_len) { return out ? out : 0xffff; } +/* ---- TSC-based timing (实机要求: 用 CPU 频率计算, 不用循环) ---- */ +static u64 g_tsc_per_ms = 0; + +static inline u64 rdtsc_nm(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +static void tsc_calibrate_nm(void) { + outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */ + outb(0x40, 0x7c); /* 11932 low = ~10ms */ + outb(0x40, 0x2e); /* 11932 high */ + u64 tsc_start = rdtsc_nm(); + u16 prev = 0; u64 loops = 0; + for (;;) { + outb(0x43, 0x00); + u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = rdtsc_nm(); + g_tsc_per_ms = (tsc_end - tsc_start) / 10; +} + +static void delay_ms_nm(u32 ms) { + if (!g_tsc_per_ms) { + for (volatile u32 i = 0; i < 100000 * ms; i++) __asm__ volatile("pause"); + return; + } + u64 target = g_tsc_per_ms * ms; + u64 start = rdtsc_nm(); + while (rdtsc_nm() - start < target) __asm__ volatile("pause"); +} + static void delay_spin(u32 loops) { - for (volatile u32 i=0;imagic != DSK_BOOT_MAGIC) { logl("[netman] bad context"); return; } + logl("[netman] calibrating TSC (实机 CPU 频率计算)"); + tsc_calibrate_nm(); u64 api = ctx->dkm_kernel_api; if (api) { u64 block_api = *(u64 *)(api + 0xA8); diff --git a/CODE/netman/main.o b/CODE/netman/main.o index 3c13803..00e1e92 100644 Binary files a/CODE/netman/main.o and b/CODE/netman/main.o differ diff --git a/CODE/shell/linker.ld b/CODE/shell/linker.ld index 825d5f1..473e5fc 100644 --- a/CODE/shell/linker.ld +++ b/CODE/shell/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(5); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(6); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/shell/main.c b/CODE/shell/main.c index ed8a9da..ecada58 100644 --- a/CODE/shell/main.c +++ b/CODE/shell/main.c @@ -15,9 +15,55 @@ typedef long long i64; static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); } static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; } +static __inline__ void outl(u16 port, u32 value) { __asm__ volatile("outl %0,%1"::"a"(value),"Nd"(port)); } +static __inline__ u32 inl(u16 port) { u32 v; __asm__ volatile("inl %1,%0":"=a"(v):"Nd"(port)); return v; } + +/* ---- TSC-based timing (实机要求: 用 CPU 频率计算, 不用循环) ---- */ +static u64 g_tsc_per_ms = 0; + +static __inline__ u64 rdtsc_shell(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +/* 通过 PIT ch0 (16-bit mode 0) 校准 TSC ~10ms 计数 */ +static void tsc_calibrate_shell(void) { + outb(0x43, 0x30); /* ch0, lo+hi, mode 0, binary */ + outb(0x40, 0x7c); /* 11932 low = ~10ms */ + outb(0x40, 0x2e); /* 11932 high */ + u64 tsc_start = rdtsc_shell(); + u16 prev = 0; u64 loops = 0; + for (;;) { + outb(0x43, 0x00); + u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + u64 tsc_end = rdtsc_shell(); + g_tsc_per_ms = (tsc_end - tsc_start) / 10; +} + +/* 串口发送等待: TSC 100us 超时, 校准失败时回退到循环 */ +static void serial_wait_tx_shell(void) { + if (g_tsc_per_ms == 0) { + tsc_calibrate_shell(); + } + if (g_tsc_per_ms == 0) { + for (u32 i = 0; i < 100000; i++) { + if (inb(COM1 + 5) & 0x20) break; + } + return; + } + u64 deadline = rdtsc_shell() + g_tsc_per_ms / 10; /* 100us 超时 */ + while (rdtsc_shell() < deadline) { + if (inb(COM1 + 5) & 0x20) break; + __asm__ volatile("pause"); + } +} static void sputc(char c) { - for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; } + serial_wait_tx_shell(); outb(COM1, (unsigned char)c); } static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } } @@ -26,6 +72,430 @@ static void logl(const char *s) { swrite(s); swrite("\n"); } /* ASCII 字体 — 必须在 deshab_ui.h 之前包含,因为 du_draw_char 引用 g_ascii */ #include "../firstInit/ascii_bitmaps.c" #include "../UTSM/include/utsm/deshab_ui.h" +/* 用户态共享协议栈(ping/curl 内建命令使用) */ +#include "../tools/net_stack.h" + +/* ============================================================ + * Block 设备 + FAT32 读写(移植自 DSK,支持 cp/mv/cat/ls) + * ============================================================ */ + +typedef int (*shell_block_read_fn)(u32 index, u64 lba, u32 count, void *buf); +typedef int (*shell_block_write_fn)(u32 index, u64 lba, u32 count, const void *buf); + +static shell_block_read_fn g_block_read; +static shell_block_write_fn g_block_write; + +/* FAT32 BPB / 目录项结构(与 DSK 一致,packed) */ +typedef struct __attribute__((packed)) { + u8 jmp[3]; char oem[8]; u16 bps; u8 spc; u16 rsvd; u8 fc; u16 root_ent; + u16 ts16; u8 media; u16 spf16; u16 spt; u16 heads; u32 hidden; u32 ts32; + u32 spf; u16 flags; u16 ver; u32 root_clus; u16 fsi; u16 bkboot; + u8 res[12]; u8 drv; u8 ntfl; u8 sig; u32 ser; char lbl[11]; char typ[8]; + u8 code[420]; u16 boot_sig; +} shell_fat32_bpb; + +typedef struct __attribute__((packed)) { + char name[11]; u8 attr; u8 ntr; u8 ctenth; + u16 ctime; u16 cdate; u16 adate; u16 chigh; + u16 wtime; u16 wdate; u16 clow; u32 fsize; +} shell_fat32_de; + +static u32 sh_r32(const u8 *p) { return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24); } +static u16 sh_r16(const u8 *p) { return (u16)p[0]|((u16)p[1]<<8); } +static int sh_neq11(const char *a, const char *b) { for(int i=0;i<11;i++) if(a[i]!=b[i]) return 0; return 1; } + +/* 批量读写缓冲区(与 DSK 对齐:256 扇区覆盖 BPB+FAT+根目录+小文件) */ +static u8 g_disk[131072]; /* 256 扇区 BPB+FAT+根目录缓存 */ +static u8 g_cluster[4096]; /* 单簇缓冲 */ +static u8 g_fdata[262144]; /* 文件数据缓冲(最大 256KB) */ +static int g_disk_loaded = 0; /* g_disk 是否已加载 BPB+FAT */ + +static int sh_read_sectors(u32 lba, u32 count, u8 *out) { return g_block_read ? g_block_read(0,lba,count,out) : -1; } +static int sh_write_sectors(u32 lba, u32 count, const u8 *buf) { return g_block_write ? g_block_write(0,lba,count,buf) : -1; } + +/* 确保 g_disk 已加载 BPB+FAT+根目录(256 扇区) */ +static int sh_disk_load(void) { + if (g_disk_loaded) return 0; + if (sh_read_sectors(0, 256, g_disk) != 0) return -1; + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return -2; + if (bpb->spf == 0 || bpb->root_clus < 2) return -3; + g_disk_loaded = 1; + return 0; +} + +/* 把 "file.txt" / "NAME" 转为 11 字符 8.3 名(大写,空格填充) + * 返回 0 成功,-1 失败(过长或非法字符) */ +static int name_to_83(const char *in, char out[11]) { + for (int i = 0; i < 11; i++) out[i] = ' '; + int inlen = 0; + while (in[inlen] && inlen < 13) inlen++; + if (inlen == 0 || inlen > 12) return -1; + int dot = -1; + for (int i = 0; i < inlen; i++) if (in[i] == '.') dot = i; + int base_end = (dot >= 0) ? dot : inlen; + if (base_end == 0 || base_end > 8) return -1; + for (int j = 0; j < base_end; j++) { + char c = in[j]; + if (c >= 'a' && c <= 'z') c -= 32; + if (c == '.' || c == ' ' || c == '/' || c == '\\') return -1; + out[j] = c; + } + if (dot >= 0) { + int extlen = inlen - dot - 1; + if (extlen > 3) return -1; + for (int j = 0; j < extlen; j++) { + char c = in[dot + 1 + j]; + if (c >= 'a' && c <= 'z') c -= 32; + if (c == '.' || c == ' ' || c == '/' || c == '\\') return -1; + out[8 + j] = c; + } + } + return 0; +} + +/* 把 11 字符 8.3 名转为可显示字符串(如 "README.TXT") */ +static void name_from_83(const char in[11], char out[13]) { + int p = 0; + for (int j = 0; j < 8; j++) { + if (in[j] == ' ') break; + out[p++] = in[j]; + } + if (in[8] != ' ') { + out[p++] = '.'; + for (int j = 8; j < 11; j++) { + if (in[j] == ' ') break; + out[p++] = in[j]; + } + } + out[p] = 0; +} + +/* 在根目录簇链中查找 11 字符名,输出首簇号与文件大小 */ +static int fat32_find_in_root(const u8 *clus, u32 clus_sectors, const char *target, + u32 *out_clus, u32 *out_size, u32 *out_idx) { + const shell_fat32_de *dir = (const shell_fat32_de *)clus; + for (u32 e = 0; e * 32 < clus_sectors * 512; e++) { + if (dir[e].name[0] == 0) break; + if ((u8)dir[e].name[0] == 0xE5) continue; + if (dir[e].attr == 0x0F) continue; + if (dir[e].attr & 0x08) continue; + if (sh_neq11(dir[e].name, target)) { + *out_clus = (u32)sh_r16((const u8*)&dir[e].clow) | ((u32)sh_r16((const u8*)&dir[e].chigh) << 16); + *out_size = dir[e].fsize; + if (out_idx) *out_idx = e; + return 0; + } + } + return -1; +} + +/* 读取根目录下指定 8.3 名文件到 g_fdata。 + * 返回 0 成功,*out_data 指向 g_fdata,*out_size 为字节数;非 0 失败。 */ +static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size) { + if (sh_disk_load() != 0) return -1; + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; + u32 fat_byte_off = bpb->rsvd * 512; + u32 spc = bpb->spc; + + u32 clus = bpb->root_clus; + u32 found_clus = 0, found_size = 0; + int found = 0; + while (clus >= 2 && clus < 0x0FFFFFF8 && !found) { + u32 lba = data_lba + (clus - 2) * spc; + const u8 *cb; + if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512) + cb = g_disk + (u64)lba * 512; + else { + if (sh_read_sectors(lba, 8, g_cluster) != 0) return -4; + cb = g_cluster; + } + if (fat32_find_in_root(cb, spc, name11, &found_clus, &found_size, 0) == 0) { found = 1; break; } + u32 fo = fat_byte_off + clus * 4; + if (fo + 4 > sizeof(g_disk)) break; + clus = sh_r32(g_disk + fo) & 0x0FFFFFFF; + } + if (!found) return -5; + if (found_size > sizeof(g_fdata)) return -6; + + u8 *dst = g_fdata; u32 remaining = found_size; u32 fc = found_clus; + while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) { + u32 fc_lba = data_lba + (fc - 2) * spc; + u32 fc_bytes = spc * 512; + if (fc_bytes > remaining) fc_bytes = remaining; + const u8 *fb; + if ((u64)fc_lba * 512 + (u64)spc * 512 <= 256ULL * 512) + fb = g_disk + (u64)fc_lba * 512; + else { + if (sh_read_sectors(fc_lba, 8, g_cluster) != 0) return -7; + fb = g_cluster; + } + for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b]; + dst += fc_bytes; remaining -= fc_bytes; + u32 fo = fat_byte_off + fc * 4; + if (fo + 4 > sizeof(g_disk)) break; + fc = sh_r32(g_disk + fo) & 0x0FFFFFFF; + } + *out_data = g_fdata; *out_size = found_size; + return 0; +} + +/* 写入根目录下指定 8.3 名文件(存在则替换,不存在则新建)。 + * 依赖 g_disk 已加载 BPB+FAT。返回 0 成功。 */ +static int fat32_write_root_file(const char *name11, const u8 *data, u32 size) { + if (sh_disk_load() != 0) return -1; + /* 写入前重新加载 BPB+FAT,保证与盘上一致 */ + if (sh_read_sectors(0, 256, g_disk) != 0) return -2; + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + u32 spc = bpb->spc; + u32 fat_lba = bpb->rsvd; + u32 fat_sectors = bpb->spf; + u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; + u32 fat_byte_off = bpb->rsvd * 512; + u32 root_clus = bpb->root_clus; + u32 cluster_bytes = spc * 512; + + u32 root_lba = data_lba + (root_clus - 2) * spc; + u8 *root_buf = g_disk + (u64)root_lba * 512; + u32 max_entries = cluster_bytes / 32; + shell_fat32_de *dir = (shell_fat32_de *)root_buf; + int free_entry = -1; + u32 existing_clus = 0; + int found = 0; + u32 existing_idx = 0; + for (u32 e = 0; e < max_entries; e++) { + if (dir[e].name[0] == 0) { if (free_entry < 0) free_entry = (int)e; break; } + if ((u8)dir[e].name[0] == 0xE5) { if (free_entry < 0) free_entry = (int)e; continue; } + if (dir[e].attr == 0x0F) continue; + if (dir[e].attr & 0x08) continue; + if (sh_neq11(dir[e].name, name11)) { + existing_clus = (u32)sh_r16((const u8*)&dir[e].clow) | ((u32)sh_r16((const u8*)&dir[e].chigh) << 16); + found = 1; existing_idx = e; + if (free_entry < 0) free_entry = (int)e; + break; + } + } + + u32 bytes_needed = size > 0 ? size : 1; + u32 clusters_needed = (bytes_needed + cluster_bytes - 1) / cluster_bytes; + u32 first_clus = 0; + u32 prev_clus = 0; + + if (found && existing_clus >= 2) { + first_clus = existing_clus; + u32 cur = existing_clus; + u32 count = 0; + while (cur >= 2 && cur < 0x0FFFFFF8 && count < clusters_needed) { + prev_clus = cur; + u32 fo = fat_byte_off + cur * 4; + if (fo + 4 > sizeof(g_disk)) break; + cur = sh_r32(g_disk + fo) & 0x0FFFFFFF; + count++; + } + while (count < clusters_needed) { + u32 newc = 0; + for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) { + u32 fo = fat_byte_off + c * 4; + if (fo + 4 > sizeof(g_disk)) break; + if ((sh_r32(g_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; } + } + if (newc == 0) return -3; + u32 pfo = fat_byte_off + prev_clus * 4; + if (pfo + 4 <= sizeof(g_disk)) { + g_disk[pfo] = (u8)(newc & 0xFF); + g_disk[pfo+1] = (u8)((newc >> 8) & 0xFF); + g_disk[pfo+2] = (u8)((newc >> 16) & 0xFF); + g_disk[pfo+3] = (u8)((newc >> 24) & 0x0F); + } + prev_clus = newc; + count++; + } + if (prev_clus >= 2) { + u32 fo = fat_byte_off + prev_clus * 4; + if (fo + 4 <= sizeof(g_disk)) { + g_disk[fo] = 0xF8; g_disk[fo+1] = 0xFF; g_disk[fo+2] = 0xFF; g_disk[fo+3] = 0x0F; + } + } + /* 释放多余旧簇 */ + u32 next = 0; + u32 fo = fat_byte_off + prev_clus * 4; + if (fo + 4 <= sizeof(g_disk)) next = sh_r32(g_disk + fo) & 0x0FFFFFFF; + while (next >= 2 && next < 0x0FFFFFF8) { + u32 nfo = fat_byte_off + next * 4; + u32 nn = 0; + if (nfo + 4 <= sizeof(g_disk)) nn = sh_r32(g_disk + nfo) & 0x0FFFFFFF; + g_disk[nfo] = 0; g_disk[nfo+1] = 0; g_disk[nfo+2] = 0; g_disk[nfo+3] = 0; + next = nn; + } + } else { + if (free_entry < 0) return -4; + for (u32 i = 0; i < clusters_needed; i++) { + u32 newc = 0; + for (u32 c = 2; c < (fat_sectors * 512) / 4; c++) { + u32 fo = fat_byte_off + c * 4; + if (fo + 4 > sizeof(g_disk)) break; + if ((sh_r32(g_disk + fo) & 0x0FFFFFFF) == 0) { newc = c; break; } + } + if (newc == 0) return -3; + if (i == 0) first_clus = newc; + if (prev_clus >= 2) { + u32 pfo = fat_byte_off + prev_clus * 4; + g_disk[pfo] = (u8)(newc & 0xFF); + g_disk[pfo+1] = (u8)((newc >> 8) & 0xFF); + g_disk[pfo+2] = (u8)((newc >> 16) & 0xFF); + g_disk[pfo+3] = (u8)((newc >> 24) & 0x0F); + } + prev_clus = newc; + } + if (prev_clus >= 2) { + u32 fo = fat_byte_off + prev_clus * 4; + g_disk[fo] = 0xF8; g_disk[fo+1] = 0xFF; g_disk[fo+2] = 0xFF; g_disk[fo+3] = 0x0F; + } + } + + /* 写数据到簇 */ + u32 remaining = size; + const u8 *src = data; + u32 cur = first_clus; + u32 ci = 0; + while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) { + u32 clba = data_lba + (cur - 2) * spc; + u32 chunk = remaining < cluster_bytes ? remaining : cluster_bytes; + if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) { + u8 *dst = g_disk + (u64)clba * 512; + for (u32 b = 0; b < chunk; b++) dst[b] = src[b]; + for (u32 b = chunk; b < cluster_bytes; b++) dst[b] = 0; + } else { + if (sh_read_sectors(clba, spc, g_cluster) != 0) return -5; + for (u32 b = 0; b < chunk; b++) g_cluster[b] = src[b]; + for (u32 b = chunk; b < cluster_bytes; b++) g_cluster[b] = 0; + if (sh_write_sectors(clba, spc, g_cluster) != 0) return -6; + } + src += chunk; remaining -= chunk; ci++; + u32 fo = fat_byte_off + cur * 4; + if (fo + 4 > sizeof(g_disk)) break; + cur = sh_r32(g_disk + fo) & 0x0FFFFFFF; + } + + /* 更新目录项 */ + u32 entry_idx = found ? existing_idx : (u32)free_entry; + shell_fat32_de *e = &dir[entry_idx]; + if (!found) { + for (int i = 0; i < 11; i++) e->name[i] = name11[i]; + e->attr = 0x20; e->ntr = 0; e->ctenth = 0; e->ctime = 0; e->cdate = 0; e->adate = 0; + } + e->chigh = (u16)((first_clus >> 16) & 0xFFFF); + e->clow = (u16)(first_clus & 0xFFFF); + e->fsize = size; + + /* 回写 FAT(两份)+ 根目录 + 缓冲区内的数据簇 */ + for (u32 f = 0; f < bpb->fc; f++) { + u32 flba = fat_lba + f * fat_sectors; + if (sh_write_sectors(flba, fat_sectors, g_disk + (u64)flba * 512) != 0) return -7; + } + u32 root_dir_lba = data_lba + (root_clus - 2) * spc; + if ((u64)root_dir_lba * 512 + cluster_bytes <= 256ULL * 512) { + if (sh_write_sectors(root_dir_lba, spc, g_disk + (u64)root_dir_lba * 512) != 0) return -8; + } + cur = first_clus; ci = 0; + while (cur >= 2 && cur < 0x0FFFFFF8 && ci < clusters_needed) { + u32 clba = data_lba + (cur - 2) * spc; + if ((u64)clba * 512 + cluster_bytes <= 256ULL * 512) { + if (sh_write_sectors(clba, spc, g_disk + (u64)clba * 512) != 0) return -9; + } + u32 fo = fat_byte_off + cur * 4; + if (fo + 4 > sizeof(g_disk)) break; + cur = sh_r32(g_disk + fo) & 0x0FFFFFFF; + ci++; + } + g_disk_loaded = 0; /* 下次操作重新加载,避免使用脏缓存 */ + return 0; +} + +/* 删除根目录下指定 8.3 名文件:清空簇链 + 标记目录项为 0xE5。 + * 返回 0 成功,-1 未找到,其他负值失败。 */ +static int fat32_delete_root_file(const char *name11) { + if (sh_disk_load() != 0) return -2; + if (sh_read_sectors(0, 256, g_disk) != 0) return -3; + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + u32 spc = bpb->spc; + u32 fat_lba = bpb->rsvd; + u32 fat_sectors = bpb->spf; + u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; + u32 fat_byte_off = bpb->rsvd * 512; + u32 root_clus = bpb->root_clus; + + u32 root_lba = data_lba + (root_clus - 2) * spc; + u8 *root_buf = g_disk + (u64)root_lba * 512; + u32 max_entries = (spc * 512) / 32; + shell_fat32_de *dir = (shell_fat32_de *)root_buf; + u32 found_idx = 0xFFFFFFFFu; + for (u32 e = 0; e < max_entries; e++) { + if (dir[e].name[0] == 0) break; + if ((u8)dir[e].name[0] == 0xE5) continue; + if (dir[e].attr == 0x0F) continue; + if (dir[e].attr & 0x08) continue; + if (sh_neq11(dir[e].name, name11)) { found_idx = e; break; } + } + if (found_idx == 0xFFFFFFFFu) return -1; + + /* 释放簇链 */ + u32 cur = (u32)sh_r16((const u8*)&dir[found_idx].clow) | ((u32)sh_r16((const u8*)&dir[found_idx].chigh) << 16); + while (cur >= 2 && cur < 0x0FFFFFF8) { + u32 fo = fat_byte_off + cur * 4; + u32 next = 0; + if (fo + 4 <= sizeof(g_disk)) next = sh_r32(g_disk + fo) & 0x0FFFFFFF; + g_disk[fo] = 0; g_disk[fo+1] = 0; g_disk[fo+2] = 0; g_disk[fo+3] = 0; + cur = next; + } + /* 标记目录项为已删除 */ + dir[found_idx].name[0] = (char)0xE5; + + /* 回写 FAT + 根目录 */ + for (u32 f = 0; f < bpb->fc; f++) { + u32 flba = fat_lba + f * fat_sectors; + if (sh_write_sectors(flba, fat_sectors, g_disk + (u64)flba * 512) != 0) return -4; + } + if (sh_write_sectors(root_lba, spc, g_disk + (u64)root_lba * 512) != 0) return -5; + g_disk_loaded = 0; + return 0; +} + +/* 列出根目录所有文件/目录,调用回调输出 */ +static int fat32_list_root(int (*emit)(const char *name, u32 size, u8 attr, void *u), void *u) { + if (sh_disk_load() != 0) return -1; + const shell_fat32_bpb *bpb = (const shell_fat32_bpb *)g_disk; + u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; + u32 fat_byte_off = bpb->rsvd * 512; + u32 spc = bpb->spc; + u32 clus = bpb->root_clus; + while (clus >= 2 && clus < 0x0FFFFFF8) { + u32 lba = data_lba + (clus - 2) * spc; + const u8 *cb; + if ((u64)lba * 512 + (u64)spc * 512 <= 256ULL * 512) + cb = g_disk + (u64)lba * 512; + else { + if (sh_read_sectors(lba, 8, g_cluster) != 0) return -4; + cb = g_cluster; + } + const shell_fat32_de *dir = (const shell_fat32_de *)cb; + for (u32 e = 0; e * 32 < spc * 512; e++) { + if (dir[e].name[0] == 0) goto done; + if ((u8)dir[e].name[0] == 0xE5) continue; + if (dir[e].attr == 0x0F) continue; + if (dir[e].attr & 0x08) continue; + char disp[13]; + name_from_83(dir[e].name, disp); + if (emit(disp, dir[e].fsize, dir[e].attr, u) != 0) goto done; + } + u32 fo = fat_byte_off + clus * 4; + if (fo + 4 > sizeof(g_disk)) break; + clus = sh_r32(g_disk + fo) & 0x0FFFFFFF; + } +done: + return 0; +} /* ---- 风格令牌(引用 deshab_ui.h 语义色) ---- */ #define BG_COLOR DS_DARK_BG_PRIMARY @@ -114,7 +584,11 @@ static void term_newline(void) { if (cur_row >= term_h) term_scroll(); } +/* dev_mode 串口镜像:reserved[3]=1 时终端输出同步写 COM1,便于自动化验证 */ +static int g_serial_mirror = 0; + static void term_putc_color(char c, u32 color) { + if (g_serial_mirror) { if (c == '\n') sputc('\r'); sputc(c); } if (c == '\n') { term_newline(); return; } if (c == '\r') { cur_col = 0; return; } if (c == '\t') { @@ -220,6 +694,74 @@ static char input_buf[256]; static int input_len = 0; static int input_cursor = 0; +/* ---- 命令历史(上下箭头浏览) ---- */ +/* 前向声明: redraw_input_line 在下方定义 */ +static void redraw_input_line(void); + +#define HISTORY_MAX 32 +static char g_history[HISTORY_MAX][256]; +static int g_history_count = 0; /* 已记录命令条数 (0..HISTORY_MAX) */ +static int g_history_view = -1; /* 当前浏览索引, -1 表示正在编辑新输入 */ + +/* 将命令推入历史。空字符串或纯空白不入历史;与最近一条相同时跳过。 */ +static void history_push(const char *cmd) { + const char *p = cmd; + while (*p == ' ') p++; + if (*p == 0) return; + /* 与最近一条相同则跳过 */ + if (g_history_count > 0) { + const char *latest = g_history[(g_history_count - 1) % HISTORY_MAX]; + const char *a = latest; + const char *b = p; + while (*a && *b && *a == *b) { a++; b++; } + if (*a == 0 && *b == 0) return; + } + int idx = g_history_count % HISTORY_MAX; + int i = 0; + while (p[i] && i < 255) { g_history[idx][i] = p[i]; i++; } + g_history[idx][i] = 0; + if (g_history_count < HISTORY_MAX) g_history_count++; +} + +/* 加载历史条目到 input_buf 并刷新显示 */ +static void history_load(int idx) { + const char *src = g_history[idx % HISTORY_MAX]; + int i = 0; + while (src[i] && i < 255) { input_buf[i] = src[i]; i++; } + input_buf[i] = 0; + input_len = i; + input_cursor = i; + redraw_input_line(); +} + +/* 上箭头: 浏览更早的命令 */ +static void history_prev(void) { + if (g_history_count == 0) return; + if (g_history_view == -1) { + g_history_view = g_history_count - 1; + } else if (g_history_view > 0) { + g_history_view--; + } else { + return; /* 已到达最早一条 */ + } + history_load(g_history_view); +} + +/* 下箭头: 浏览更新的命令; 超过最新则回到空输入 */ +static void history_next(void) { + if (g_history_view == -1) return; + g_history_view++; + if (g_history_view >= g_history_count) { + g_history_view = -1; + input_len = 0; + input_cursor = 0; + input_buf[0] = 0; + redraw_input_line(); + return; + } + history_load(g_history_view); +} + /* ---- 键盘扫描码 → ASCII(Set 1) ---- */ static char scan_to_ascii(u8 sc, int shift) { static const char normal[58] = { @@ -248,16 +790,25 @@ static void cmd_help(void) { term_puts_color("Deshab Shell — 内置命令列表:\n", PROMPT_FG); term_puts(" help 显示此帮助\n"); term_puts(" clear 清屏\n"); - term_puts(" echo 显示文本\n"); - term_puts(" version 显示系统版本\n"); + term_puts(" echo 显示文本(-n 不换行, > file 重定向写盘)\n"); + term_puts(" ls / dir 列出根目录文件\n"); + term_puts(" cat 显示文件内容\n"); + term_puts(" cp 复制文件\n"); + term_puts(" mv 移动/重命名文件\n"); + term_puts(" rm 删除文件\n"); + term_puts(" pwd 显示当前目录\n"); + term_puts(" whoami 显示当前用户\n"); + term_puts(" id 显示用户ID\n"); + term_puts(" version/ver 显示系统版本\n"); term_puts(" uname 显示系统名称\n"); term_puts(" date 显示当前日期\n"); - term_puts(" ver 显示内核版本(同 version)\n"); term_puts(" about 关于 Deshab\n"); + term_puts(" pci 列出 PCI 设备\n"); + term_puts(" ping [ip] ICMP echo 测试(缺省网关 10.0.2.2)\n"); + term_puts(" curl HTTP GET 请求(http://host[:port]/path)\n"); + term_puts(" run 运行根目录下的工具程序\n"); term_puts(" reboot 重启系统\n"); term_puts(" halt 关机(停止 CPU)\n"); - term_puts(" ls 列出根目录文件(暂未实现,需要 VFS)\n"); - term_puts(" pci 列出 PCI 设备(暂未实现)\n"); } static void cmd_version(void) { @@ -301,9 +852,45 @@ static void cmd_halt(void) { for(;;) __asm__("hlt"); } +/* echo:支持 -n(不换行)和 " > file" 重定向写盘 */ static void cmd_echo(const char *args) { + int newline = 1; + if (args && args[0] == '-' && args[1] == 'n' && (args[2] == ' ' || args[2] == 0)) { + newline = 0; + args += 2; + while (*args == ' ') args++; + } + /* 检测 " > file" 重定向 */ + const char *redir = 0; + if (args) { + const char *p = args; + while (*p) { if (*p == '>') { redir = p; break; } p++; } + } + if (redir) { + int text_len = (int)(redir - args); + while (text_len > 0 && args[text_len-1] == ' ') text_len--; + const char *fname = redir + 1; + while (*fname == ' ') fname++; + char fnbuf[64]; int fl = 0; + while (*fname && *fname != ' ' && fl < 63) fnbuf[fl++] = *fname++; + fnbuf[fl] = 0; + if (fl == 0) { term_puts_color("echo: 重定向缺少文件名\n", ERROR_FG); return; } + char n83[11]; + if (name_to_83(fnbuf, n83) != 0) { term_puts_color("echo: 无效文件名\n", ERROR_FG); return; } + static char echo_buf[4096]; + int el = 0; + for (int i = 0; i < text_len && el < 4095; i++) echo_buf[el++] = args[i]; + if (newline) echo_buf[el++] = '\n'; + if (!g_block_write) { term_puts_color("echo: 无块设备写能力\n", ERROR_FG); return; } + int rc = fat32_write_root_file(n83, (const u8 *)echo_buf, (u32)el); + if (rc != 0) { term_puts_color("echo: 写入失败\n", ERROR_FG); return; } + term_puts_color("echo: 已写入 ", OK_FG); + term_puts_color(fnbuf, OK_FG); + term_putc('\n'); + return; + } if (args && *args) term_puts_color(args, TEXT_FG); - term_putc('\n'); + if (newline) term_putc('\n'); } static u8 read_rtc_reg(u8 reg); @@ -347,6 +934,598 @@ static void cmd_not_impl(const char *cmd) { term_puts_color(": 此命令尚未实现\n", ERROR_FG); } +/* ============================================================ + * 文件命令:ls / cat / cp / mv / rm(FAT32 根目录) + * ============================================================ */ + +/* 从 args 解析一个空白分隔 token 到 out(最多 max-1 字符),返回剩余 args 指针 */ +static const char *parse_token(const char *args, char *out, int max) { + int n = 0; + while (*args == ' ') args++; + while (*args && *args != ' ' && n < max - 1) out[n++] = *args++; + out[n] = 0; + return args; +} + +/* u32 → 十进制字符串 */ +static void u32_to_dec(char *buf, u32 v) { + char tmp[12]; int n = 0; + if (v == 0) { buf[0] = '0'; buf[1] = 0; return; } + while (v && n < 11) { tmp[n++] = (char)('0' + v % 10); v /= 10; } + for (int i = 0; i < n; i++) buf[i] = tmp[n - 1 - i]; + buf[n] = 0; +} + +/* ls 回调:每行打印一个条目 */ +static int ls_emit(const char *name, u32 size, u8 attr, void *u) { + (void)u; + if (attr & 0x10) term_puts_color("[DIR] ", ACCENT_FG); + else term_puts_color(" ", TEXT_FG); + term_puts_color(name, TEXT_FG); + /* 名字对齐填充 */ + int nl = 0; while (name[nl]) nl++; + for (int i = nl; i < 14; i++) term_putc(' '); + if (!(attr & 0x10)) { + char sz[12]; u32_to_dec(sz, size); + term_puts_color(sz, DIM_FG); + term_puts_color(" B", DIM_FG); + } + term_putc('\n'); + return 0; +} + +static void cmd_ls(void) { + if (!g_block_read) { term_puts_color("ls: 无块设备\n", ERROR_FG); return; } + if (fat32_list_root(ls_emit, 0) != 0) { + term_puts_color("ls: 读取根目录失败\n", ERROR_FG); + } +} + +static void cmd_cat(const char *args) { + if (!g_block_read) { term_puts_color("cat: 无块设备\n", ERROR_FG); return; } + char fn[64]; + parse_token(args, fn, 64); + if (fn[0] == 0) { term_puts_color("用法: cat <文件>\n", ERROR_FG); return; } + char n83[11]; + if (name_to_83(fn, n83) != 0) { term_puts_color("cat: 无效文件名\n", ERROR_FG); return; } + u8 *data = 0; u32 size = 0; + if (fat32_read_root_file(n83, &data, &size) != 0) { + term_puts_color("cat: 文件不存在\n", ERROR_FG); + return; + } + for (u32 i = 0; i < size; i++) term_putc((char)data[i]); +} + +static void cmd_cp(const char *args) { + if (!g_block_read || !g_block_write) { term_puts_color("cp: 需要块设备读写能力\n", ERROR_FG); return; } + char src[64], dst[64]; + args = parse_token(args, src, 64); + parse_token(args, dst, 64); + if (src[0] == 0 || dst[0] == 0) { + term_puts_color("用法: cp <源文件> <目标文件>\n", ERROR_FG); + return; + } + char s83[11], d83[11]; + if (name_to_83(src, s83) != 0) { term_puts_color("cp: 无效源文件名\n", ERROR_FG); return; } + if (name_to_83(dst, d83) != 0) { term_puts_color("cp: 无效目标文件名\n", ERROR_FG); return; } + u8 *data = 0; u32 size = 0; + if (fat32_read_root_file(s83, &data, &size) != 0) { + term_puts_color("cp: 源文件不存在或读取失败\n", ERROR_FG); + return; + } + if (fat32_write_root_file(d83, data, size) != 0) { + term_puts_color("cp: 写入目标失败\n", ERROR_FG); + return; + } + term_puts_color("cp: ", OK_FG); + term_puts_color(src, OK_FG); + term_puts_color(" -> ", OK_FG); + term_puts_color(dst, OK_FG); + term_puts_color(" (", DIM_FG); + char sz[12]; u32_to_dec(sz, size); + term_puts_color(sz, DIM_FG); + term_puts_color(" bytes)\n", DIM_FG); +} + +static void cmd_mv(const char *args) { + if (!g_block_read || !g_block_write) { term_puts_color("mv: 需要块设备读写能力\n", ERROR_FG); return; } + char src[64], dst[64]; + args = parse_token(args, src, 64); + parse_token(args, dst, 64); + if (src[0] == 0 || dst[0] == 0) { + term_puts_color("用法: mv <源文件> <目标文件>\n", ERROR_FG); + return; + } + char s83[11], d83[11]; + if (name_to_83(src, s83) != 0) { term_puts_color("mv: 无效源文件名\n", ERROR_FG); return; } + if (name_to_83(dst, d83) != 0) { term_puts_color("mv: 无效目标文件名\n", ERROR_FG); return; } + u8 *data = 0; u32 size = 0; + if (fat32_read_root_file(s83, &data, &size) != 0) { + term_puts_color("mv: 源文件不存在或读取失败\n", ERROR_FG); + return; + } + if (fat32_write_root_file(d83, data, size) != 0) { + term_puts_color("mv: 写入目标失败\n", ERROR_FG); + return; + } + /* 写入后 g_disk 缓存已失效,delete 会重新加载 BPB+FAT */ + if (fat32_delete_root_file(s83) != 0) { + term_puts_color("mv: 警告 — 源文件删除失败,目标已写入\n", DP_WARNING); + return; + } + term_puts_color("mv: ", OK_FG); + term_puts_color(src, OK_FG); + term_puts_color(" -> ", OK_FG); + term_puts_color(dst, OK_FG); + term_putc('\n'); +} + +static void cmd_rm(const char *args) { + if (!g_block_write) { term_puts_color("rm: 需要块设备写能力\n", ERROR_FG); return; } + char fn[64]; + parse_token(args, fn, 64); + if (fn[0] == 0) { term_puts_color("用法: rm <文件>\n", ERROR_FG); return; } + char n83[11]; + if (name_to_83(fn, n83) != 0) { term_puts_color("rm: 无效文件名\n", ERROR_FG); return; } + int rc = fat32_delete_root_file(n83); + if (rc == -1) { term_puts_color("rm: 文件不存在\n", ERROR_FG); return; } + if (rc != 0) { term_puts_color("rm: 删除失败\n", ERROR_FG); return; } + term_puts_color("rm: 已删除 ", OK_FG); + term_puts_color(fn, OK_FG); + term_putc('\n'); +} + +/* ---- PCI 设备列表 ---- */ +static u32 pci_config_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 addr = 0x80000000u | ((u32)bus << 16) | ((u32)dev << 11) + | ((u32)func << 8) | ((u32)reg & 0xFCu); + outl(0xCF8, addr); + return inl(0xCFC); +} + +static void hex_nibble(char *buf, u8 v) { + v = (u8)(v & 0x0F); + buf[0] = (char)(v < 10 ? '0' + v : 'A' + (v - 10)); +} + +static void u8_to_hex(char *buf, u8 v) { + hex_nibble(buf, (u8)(v >> 4)); + hex_nibble(buf + 1, v); + buf[2] = 0; +} + +static void u16_to_hex(char *buf, u16 v) { + hex_nibble(buf, (u8)(v >> 12)); + hex_nibble(buf+1, (u8)(v >> 8)); + hex_nibble(buf+2, (u8)(v >> 4)); + hex_nibble(buf+3, v); + buf[4] = 0; +} + +static void u8_to_dec(char *buf, u8 v) { + if (v >= 100) { + buf[0] = (char)('0' + v / 100); + buf[1] = (char)('0' + (v / 10) % 10); + buf[2] = (char)('0' + v % 10); + buf[3] = 0; + } else if (v >= 10) { + buf[0] = (char)('0' + v / 10); + buf[1] = (char)('0' + v % 10); + buf[2] = 0; + } else { + buf[0] = (char)('0' + v); + buf[1] = 0; + } +} + +static const char *pci_vendor_name(u16 vendor) { + switch (vendor) { + case 0x8086: return "Intel"; + case 0x1234: return "Bochs/QEMU"; + case 0x1AF4: return "Virtio"; + case 0x168C: return "Atheros"; + case 0x10DE: return "NVIDIA"; + case 0x1002: return "AMD"; + case 0x10EC: return "Realtek"; + case 0x14E4: return "Broadcom"; + case 0x1B36: return "Red Hat"; + case 0x1022: return "AMD-x86"; + case 0x106B: return "Apple"; + default: return ""; + } +} + +static const char *pci_class_name(u8 class_code) { + switch (class_code) { + case 0x00: return "Unclassified"; + case 0x01: return "Mass storage"; + case 0x02: return "Network"; + case 0x03: return "Display"; + case 0x04: return "Multimedia"; + case 0x05: return "Memory"; + case 0x06: return "Bridge"; + case 0x07: return "Comm"; + case 0x08: return "Sys peripheral"; + case 0x09: return "Input"; + case 0x0C: return "Serial bus"; + case 0x0D: return "Wireless"; + case 0x10: return "Encryption"; + case 0x11: return "Signal proc"; + case 0x40: return "Coprocessor"; + default: return "Other"; + } +} + +static void cmd_pci(void) { + int count = 0; + term_puts_color("PCI 设备列表:\n", PROMPT_FG); + for (u8 bus = 0; bus < 1; bus++) { + for (u8 dev = 0; dev < 32; dev++) { + u32 vd = pci_config_read(bus, dev, 0, 0x00); + if ((vd & 0xFFFFu) == 0xFFFFu) continue; + u32 hdr_class = pci_config_read(bus, dev, 0, 0x0C); + u8 header_type = (u8)((hdr_class >> 16) & 0xFF); + u8 func_count = (u8)((header_type & 0x80) ? 8 : 1); + for (u8 func = 0; func < func_count; func++) { + u32 vdf = pci_config_read(bus, dev, func, 0x00); + u16 vendor = (u16)(vdf & 0xFFFFu); + u16 device = (u16)(vdf >> 16); + if (vendor == 0xFFFFu) continue; + u32 cls = pci_config_read(bus, dev, func, 0x08); + u8 rev = (u8)(cls & 0xFF); + u8 prog_if = (u8)((cls >> 8) & 0xFF); + u8 subclass = (u8)((cls >> 16) & 0xFF); + u8 class_code = (u8)((cls >> 24) & 0xFF); + + char line[80]; + int p = 0; + /* "00:00.0 " */ + u8_to_hex(line + p, bus); p += 2; + line[p++] = ':'; + u8_to_hex(line + p, dev); p += 2; + line[p++] = '.'; + u8_to_hex(line + p, func); p += 2; + line[p++] = ' '; + /* "VEN:DEV " */ + u16_to_hex(line + p, vendor); p += 4; + line[p++] = ':'; + u16_to_hex(line + p, device); p += 4; + line[p++] = ' '; + /* "rev NN " */ + line[p++] = 'r'; line[p++] = 'e'; line[p++] = 'v'; + line[p++] = ' '; + u8_to_hex(line + p, rev); p += 2; + line[p++] = ' '; + /* "class CC:SS:PP" */ + line[p++] = 'c'; line[p++] = 'l'; line[p++] = 'a'; + line[p++] = 's'; line[p++] = 's'; line[p++] = ' '; + u8_to_hex(line + p, class_code); p += 2; + line[p++] = ':'; + u8_to_hex(line + p, subclass); p += 2; + line[p++] = ':'; + u8_to_hex(line + p, prog_if); p += 2; + line[p++] = ' '; + line[p++] = '['; + const char *cn = pci_class_name(class_code); + while (*cn) line[p++] = *cn++; + line[p++] = ']'; + const char *vn = pci_vendor_name(vendor); + if (*vn) { + line[p++] = ' '; + while (*vn) line[p++] = *vn++; + } + line[p] = 0; + term_puts(line); + term_putc('\n'); + count++; + } + } + } + char tail[32]; + int q = 0; + const char *prefix = "Total: "; + while (prefix[q]) { tail[q] = prefix[q]; q++; } + u8_to_dec(tail + q, (u8)(count > 255 ? 255 : count)); + while (tail[q]) q++; + const char *suffix = " device(s)\n"; + int s = 0; + while (suffix[s]) { tail[q++] = suffix[s++]; } + tail[q] = 0; + term_puts_color(tail, DIM_FG); +} + +/* ============================================================ + * 网络命令:ping / curl(共享协议栈 net_stack.h) + * ============================================================ */ + +static u64 g_kernel_api = 0; +static const dsk_boot_context *g_boot_ctx = 0; +static int g_ns_status = 0; /* 0=未尝试, 1=就绪, <0=初始化失败 */ + +static int shell_net_ready(void) { + if (g_ns_status == 1) return 1; + if (g_ns_status < 0) return 0; + int rc = ns_init(g_kernel_api); + g_ns_status = (rc == 0) ? 1 : -1; + if (rc != 0) { + logl("[shell] ns_init failed"); + term_puts_color("网络初始化失败 (rc=", ERROR_FG); + char rcbuf[4]; + rcbuf[0] = (char)('0' + (-rc % 10)); + rcbuf[1] = 0; + term_puts_color(rcbuf, ERROR_FG); + term_puts_color("),无可用网卡或 net API\n", ERROR_FG); + return 0; + } + logl("[shell] ns_init ok"); + return 1; +} + +/* ping [a.b.c.d] — 缺省网关 10.0.2.2,4 次 echo */ +static void cmd_ping(const char *args) { + u32 ip; + if (!args || !*args) { + ip = NS_GATEWAY_IP; + } else if (ns_parse_ip(args, &ip) != 0) { + term_puts_color("用法: ping [a.b.c.d]\n", ERROR_FG); + return; + } + if (!shell_net_ready()) return; + + char ipstr[24]; + ns_fmt_ip(ip, ipstr); + term_puts_color("PING ", PROMPT_FG); + term_puts_color(ipstr, ACCENT_FG); + term_puts_color(" 32 data bytes\n", DIM_FG); + + int sent = 0, recv = 0; + u32 sum = 0, min = 0xFFFFFFFFu, max = 0; + for (u16 seq = 1; seq <= 4; seq++) { + u32 rtt = 0; u8 ttl = 0; + int rc = ns_ping(ip, seq, 2000, &rtt, &ttl); + sent++; + if (rc == 0) { + recv++; + sum += rtt; + if (rtt < min) min = rtt; + if (rtt > max) max = rtt; + term_puts("reply from "); + term_puts(ipstr); + term_puts(": bytes=32 time="); + char nb[12]; ns_u32_dec(nb, rtt); + term_puts(nb); + term_puts("ms TTL="); + ns_u32_dec(nb, ttl); + term_puts(nb); + term_putc('\n'); + } else if (rc == -1) { + term_puts_color("ARP 解析失败,目标不可达\n", ERROR_FG); + break; + } else { + term_puts_color("request timed out\n", DIM_FG); + } + } + /* 统计行 */ + char nb[12]; + ns_u32_dec(nb, (u32)sent); + term_puts_color("--- ", DIM_FG); + term_puts_color(ipstr, DIM_FG); + term_puts_color(" ping statistics ---\n ", DIM_FG); + term_puts(nb); + term_puts(" transmitted, "); + ns_u32_dec(nb, (u32)recv); + term_puts(nb); + term_puts(" received, "); + u32 loss = sent ? (u32)(sent - recv) * 100 / (u32)sent : 100; + ns_u32_dec(nb, loss); + term_puts(nb); + term_puts("% loss"); + if (recv > 0) { + term_puts(", min/avg/max = "); + ns_u32_dec(nb, min); term_puts(nb); term_puts("/"); + ns_u32_dec(nb, sum / (u32)recv); term_puts(nb); term_puts("/"); + ns_u32_dec(nb, max); term_puts(nb); term_puts(" ms"); + } + term_putc('\n'); +} + +/* curl http://host[:port][/path] — HTTP/1.0 GET,打印 status + body */ +static void cmd_curl(const char *args) { + if (!args || !*args) { + term_puts_color("用法: curl http://host[:port][/path]\n", ERROR_FG); + return; + } + if (!shell_net_ready()) return; + + const char *p = args; + if (p[0]=='h'&&p[1]=='t'&&p[2]=='t'&&p[3]=='p'&&p[4]==':'&&p[5]=='/'&&p[6]=='/') p += 7; + + char host[128]; int hl = 0; + while (*p && *p != ':' && *p != '/' && hl < 127) host[hl++] = *p++; + host[hl] = 0; + if (!hl) { term_puts_color("curl: 缺少主机名\n", ERROR_FG); return; } + + u16 port = 80; + if (*p == ':') { + p++; + u32 pv = 0; int digits = 0; + while (*p >= '0' && *p <= '9') { pv = pv * 10 + (u32)(*p - '0'); p++; digits++; } + if (digits && pv <= 65535) port = (u16)pv; + } + char path[128]; int pl = 0; + if (*p == '/') { + while (*p && *p != ' ' && pl < 127) path[pl++] = *p++; + } + if (!pl) { path[0] = '/'; pl = 1; } + path[pl] = 0; + + /* 解析主机 */ + u32 ip; + term_puts_color("-> 解析 ", DIM_FG); + term_puts_color(host, DIM_FG); + term_puts_color("...\n", DIM_FG); + if (ns_dns_resolve(host, &ip) != 0) { + term_puts_color("curl: DNS 解析失败\n", ERROR_FG); + return; + } + char ipstr[24]; ns_fmt_ip(ip, ipstr); + term_puts_color("-> ", DIM_FG); + term_puts_color(ipstr, DIM_FG); + term_putc('\n'); + + /* 连接 */ + term_puts_color("-> TCP 连接...\n", DIM_FG); + if (ns_tcp_connect(ip, port, 2000) != 0) { + term_puts_color("curl: TCP 连接失败(超时/RST)\n", ERROR_FG); + return; + } + term_puts_color("-> 已连接,发送请求\n", DIM_FG); + + /* 构造 HTTP/1.0 请求 */ + static char req[512]; + int r = 0; + const char *m = "GET "; while (*m) req[r++] = *m++; + for (int i = 0; i < pl; i++) req[r++] = path[i]; + m = " HTTP/1.0\r\nHost: "; while (*m) req[r++] = *m++; + for (int i = 0; i < hl; i++) req[r++] = host[i]; + m = "\r\nConnection: close\r\n\r\n"; while (*m) req[r++] = *m++; + req[r] = 0; + + if (ns_tcp_send((const u8 *)req, (u32)r) != 0) { + term_puts_color("curl: 发送失败\n", ERROR_FG); + ns_tcp_close(); + return; + } + + /* 收至 FIN(数据在协议栈 ns_tcp_rx 中累计) */ + int n = ns_tcp_recv(0, 0, 5000); + ns_tcp_close(); + if (n < 0) { + term_puts_color("curl: 连接被重置\n", ERROR_FG); + return; + } + if (n == 0) { + term_puts_color("curl: 无响应数据(超时)\n", ERROR_FG); + return; + } + + /* 打印状态行 + body(截断提示) */ + u32 total = ns_tcp_rx_len; + u32 show = (u32)n; + for (u32 i = 0; i < show; i++) term_putc((char)ns_tcp_rx[i]); + if (total > show) { + term_puts_color("\n[... 已截断,共 ", DIM_FG); + char nb[12]; ns_u32_dec(nb, total); + term_puts_color(nb, DIM_FG); + term_puts_color(" 字节]\n", DIM_FG); + } + term_putc('\n'); +} + +/* ============================================================ + * run 命令:从 FAT32 根目录加载并跳转 PIE ELF 工具 + * ============================================================ */ + +#define SH_ELFCLASS64 2 +#define SH_EM_X86_64 62 +#define SH_PT_LOAD 1 +#define SH_PT_DYNAMIC 2 +#define SH_DT_RELA 7 +#define SH_DT_RELASZ 8 +#define SH_DT_RELAENT 9 +#define SH_R_X86_64_RELATIVE 8 + +typedef struct { u8 ident[16]; u16 type,machine; u32 ver; u64 entry,phoff,shoff; u32 flags; u16 ehsize,phentsize,phnum,shentsize,shnum,shstrndx; } sh_elf64_ehdr; +typedef struct { u32 type,flags; u64 offset,vaddr,paddr,filesz,memsz,align; } sh_elf64_phdr; +typedef struct { i64 tag; u64 val; } sh_elf64_dyn; +typedef struct { u64 offset; u64 info; i64 addend; } sh_elf64_rela; + +/* run 加载镜像缓冲(容纳工具 ELF 展开后的 memsz) */ +static u8 g_run_image[262144]; + +static int sh_load_elf(u8 *data, void **entry_out) { + const sh_elf64_ehdr *eh = (const sh_elf64_ehdr *)data; + if (eh->ident[0] != 0x7F || eh->ident[4] != SH_ELFCLASS64) return -1; + if (eh->machine != SH_EM_X86_64) return -2; + u64 min_vaddr = ~0ULL, max_vaddr = 0; + u32 lc = 0; + for (u16 i = 0; i < eh->phnum; i++) { + const sh_elf64_phdr *ph = (const sh_elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != SH_PT_LOAD) continue; + if (ph->filesz > ph->memsz) return -3; + if (ph->vaddr < min_vaddr) min_vaddr = ph->vaddr; + if (ph->vaddr + ph->memsz > max_vaddr) max_vaddr = ph->vaddr + ph->memsz; + lc++; + } + if (!lc || min_vaddr == ~0ULL) return -4; + u64 isize = (max_vaddr - min_vaddr + 0xFFF) & ~0xFFFULL; + if (isize > sizeof(g_run_image)) return -5; + u8 *image = g_run_image; + for (u64 i = 0; i < isize; i++) image[i] = 0; + for (u16 i = 0; i < eh->phnum; i++) { + const sh_elf64_phdr *ph = (const sh_elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != SH_PT_LOAD) continue; + u64 off = ph->vaddr - min_vaddr; + for (u64 b = 0; b < ph->filesz; b++) image[off + b] = data[ph->offset + b]; + } + *entry_out = image + (eh->entry - min_vaddr); + + /* R_X86_64_RELATIVE 重定位 */ + u64 load_bias = (u64)image - min_vaddr; + for (u16 i = 0; i < eh->phnum; i++) { + const sh_elf64_phdr *ph = (const sh_elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != SH_PT_DYNAMIC) continue; + const sh_elf64_dyn *dyn = (const sh_elf64_dyn *)(image + (ph->vaddr - min_vaddr)); + u64 rela_off = 0, rela_sz = 0, rela_ent = 24; + for (; dyn->tag != 0; dyn++) { + if (dyn->tag == SH_DT_RELA) rela_off = dyn->val; + else if (dyn->tag == SH_DT_RELASZ) rela_sz = dyn->val; + else if (dyn->tag == SH_DT_RELAENT) rela_ent = dyn->val; + } + if (rela_off && rela_sz) { + u64 cnt = rela_sz / rela_ent; + for (u64 rr = 0; rr < cnt; rr++) { + const sh_elf64_rela *rel = (const sh_elf64_rela *)(image + (rela_off - min_vaddr) + rr * rela_ent); + if ((u32)(rel->info & 0xFFFFFFFF) == SH_R_X86_64_RELATIVE) { + u64 *slot = (u64 *)(image + (rel->offset - min_vaddr)); + *slot = load_bias + (u64)rel->addend; + } + } + } + } + return 0; +} + +/* run NAME.ELF — 加载根目录工具,全屏执行,返回后重绘 shell */ +static void cmd_run(const char *args) { + if (!g_block_read) { term_puts_color("run: 无块设备\n", ERROR_FG); return; } + char fn[64]; + parse_token(args, fn, 64); + if (fn[0] == 0) { term_puts_color("用法: run \n", ERROR_FG); return; } + char n83[11]; + if (name_to_83(fn, n83) != 0) { term_puts_color("run: 无效文件名\n", ERROR_FG); return; } + u8 *data = 0; u32 size = 0; + if (fat32_read_root_file(n83, &data, &size) != 0) { + term_puts_color("run: 文件不存在: ", ERROR_FG); + term_puts_color(fn, ERROR_FG); + term_putc('\n'); + return; + } + void *entry = 0; + int rc = sh_load_elf(data, &entry); + if (rc != 0) { + term_puts_color("run: ELF 加载失败(无效格式或过大)\n", ERROR_FG); + return; + } + logl("[shell] run: jumping to tool"); + void (*fn_entry)(const dsk_boot_context *) = (void (*)(const dsk_boot_context *))entry; + fn_entry(g_boot_ctx); + logl("[shell] run: tool returned"); + /* 工具可能改动磁盘/显示状态:失效 FAT 缓存并重绘 */ + g_disk_loaded = 0; + term_redraw_all(); +} + static void execute_command(const char *cmd) { while (*cmd == ' ') cmd++; if (*cmd == 0) return; @@ -367,9 +1546,15 @@ static void execute_command(const char *cmd) { else if (str_eq(name, "halt") || str_eq(name, "shutdown")) cmd_halt(); else if (str_eq(name, "echo")) cmd_echo(args); else if (str_eq(name, "date") || str_eq(name, "time")) cmd_date(); - else if (str_eq(name, "ls") || str_eq(name, "dir")) cmd_not_impl("ls"); - else if (str_eq(name, "pci")) cmd_not_impl("pci"); - else if (str_eq(name, "cat")) cmd_not_impl("cat"); + else if (str_eq(name, "ls") || str_eq(name, "dir")) cmd_ls(); + else if (str_eq(name, "pci")) cmd_pci(); + else if (str_eq(name, "cat")) cmd_cat(args); + else if (str_eq(name, "cp")) cmd_cp(args); + else if (str_eq(name, "mv")) cmd_mv(args); + else if (str_eq(name, "rm") || str_eq(name, "del")) cmd_rm(args); + else if (str_eq(name, "ping")) cmd_ping(args); + else if (str_eq(name, "curl")) cmd_curl(args); + else if (str_eq(name, "run")) cmd_run(args); else if (str_eq(name, "cd")) cmd_not_impl("cd"); else if (str_eq(name, "pwd")) { term_puts("/\n"); } else if (str_eq(name, "whoami")) { term_puts("root\n"); } @@ -497,6 +1682,54 @@ static void kill_line(void) { redraw_input_line(); } +/* 开发者模式:自动测试命令序列(ls/echo>/cat/cp/mv/rm) + * 由 DSK 在 firstInit.txt 第二行=1 时触发,reserved[3]=1 */ +static void run_dev_tests(void) { + term_puts_color("=== 开发者模式:自动测试命令 ===\n", PROMPT_FG); + term_putc('\n'); + + term_puts_color("[1] ls — 列出根目录\n", ACCENT_FG); + cmd_ls(); + term_putc('\n'); + + term_puts_color("[2] echo \"Deshab dev test\" > TEST.TXT\n", ACCENT_FG); + cmd_echo("Deshab dev test > TEST.TXT"); + + term_puts_color("[3] cat TEST.TXT — 读回验证\n", ACCENT_FG); + cmd_cat("TEST.TXT"); + term_putc('\n'); + + term_puts_color("[4] cp TEST.TXT COPY.TXT\n", ACCENT_FG); + cmd_cp("TEST.TXT COPY.TXT"); + + term_puts_color("[5] ls — 确认复制\n", ACCENT_FG); + cmd_ls(); + term_putc('\n'); + + term_puts_color("[6] mv COPY.TXT MOVED.TXT\n", ACCENT_FG); + cmd_mv("COPY.TXT MOVED.TXT"); + + term_puts_color("[7] ls — 确认移动\n", ACCENT_FG); + cmd_ls(); + term_putc('\n'); + + term_puts_color("[8] rm TEST.TXT\n", ACCENT_FG); + cmd_rm("TEST.TXT"); + term_puts_color("[9] rm MOVED.TXT — 清理\n", ACCENT_FG); + cmd_rm("MOVED.TXT"); + + term_puts_color("[10] ls — 确认清理\n", ACCENT_FG); + cmd_ls(); + + term_puts_color("[11] ping 10.0.2.2 — 网络回归(失败不阻断)\n", ACCENT_FG); + cmd_ping("10.0.2.2"); + + term_putc('\n'); + term_puts_color("=== 自动测试完成 ===\n", OK_FG); + term_puts_color("按 Esc 返回 DSK,或继续输入命令\n", DIM_FG); + term_putc('\n'); +} + /* ---- 主入口 ---- */ __attribute__((visibility("default"))) void dsk_entry(const dsk_boot_context *ctx) { @@ -513,6 +1746,20 @@ void dsk_entry(const dsk_boot_context *ctx) { fb_h = ctx->framebuffer_height; fb_p = ctx->framebuffer_pitch; + /* 保存 boot 上下文(网络协议栈/run 命令使用) */ + g_kernel_api = ctx->dkm_kernel_api; + g_boot_ctx = ctx; + + /* 获取 block 设备 read/write(从 kernel_api + 0xA8 → block_api → +16/+24) */ + { + u64 api = ctx->dkm_kernel_api; + u64 blk = *(u64 *)(api + 0xA8); + g_block_read = blk ? (shell_block_read_fn)*(u64 *)(blk + 16) : 0; + g_block_write = blk ? (shell_block_write_fn)*(u64 *)(blk + 24) : 0; + } + if (g_block_read) logl("[shell] block_read ok"); + if (g_block_write) logl("[shell] block_write ok"); + /* 初始化风格系统渲染上下文 */ du_context_init(&g_ctx, fb_a, fb_w, fb_h, fb_p); @@ -522,10 +1769,19 @@ void dsk_entry(const dsk_boot_context *ctx) { /* 启动 banner — 使用风格令牌 */ term_puts_color("=== Deshab OS v0.1.0 ===\n", ACCENT_FG); - term_puts_color("欢迎使用 Deshab Shell\n", OK_FG); + if (ctx->reserved[3] == 1) + term_puts_color("开发者模式 (自动测试命令)\n", OK_FG); + else + term_puts_color("欢迎使用 Deshab Shell\n", OK_FG); term_puts_color("输入 'help' 查看可用命令\n", DIM_FG); term_putc('\n'); + /* 开发者模式:DSK 设 reserved[3]=1 时自动跑命令测试序列 */ + if (ctx->reserved[3] == 1) { + g_serial_mirror = 1; + run_dev_tests(); + } + /* 主循环 */ int shift = 0; int e0 = 0; @@ -567,21 +1823,27 @@ void dsk_entry(const dsk_boot_context *ctx) { else if (sc == 0x47) cursor_home(); else if (sc == 0x4F) cursor_end(); else if (sc == 0x53) delete_char_fwd(); - else if (sc == 0x48) { } - else if (sc == 0x50) { } + else if (sc == 0x48) { history_prev(); } + else if (sc == 0x50) { history_next(); } e0 = 0; continue; } if (sc == 0x1C) { term_clear_cursor(); input_buf[input_len] = 0; + /* 提交命令前先入历史; 执行后再清空输入 */ + history_push(input_buf); + g_history_view = -1; term_putc('\n'); execute_command(input_buf); + input_len = 0; + input_cursor = 0; + input_buf[0] = 0; cmd_done = 1; continue; } if (sc == 0x0E) { delete_char_back(); continue; } - if (sc == 0x01) { kill_line(); continue; } + if (sc == 0x01) { logl("[shell] esc -> return to DSK"); return; } if (sc == 0x15 && !shift) { kill_line(); continue; } if (sc == 0x17 && !shift) { while (input_cursor > 0) delete_char_back(); continue; } if (sc == 0x0B && !shift) { kill_to_end(); continue; } diff --git a/CODE/shell/main.o b/CODE/shell/main.o index 7b43b8c..aa4542f 100644 Binary files a/CODE/shell/main.o and b/CODE/shell/main.o differ diff --git a/CODE/tools/bash/linker.ld b/CODE/tools/bash/linker.ld deleted file mode 100644 index 94dddf7..0000000 --- a/CODE/tools/bash/linker.ld +++ /dev/null @@ -1,16 +0,0 @@ -ENTRY(dsk_entry) - -PHDRS -{ - text PT_LOAD FLAGS(7); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(7); -} - -SECTIONS -{ - . = 0x1000; - .text : ALIGN(4K) { *(.text*) } :text - .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data -} diff --git a/CODE/tools/bash/main.c b/CODE/tools/bash/main.c deleted file mode 100644 index 790ee6c..0000000 --- a/CODE/tools/bash/main.c +++ /dev/null @@ -1,456 +0,0 @@ -/* Deshab Bash — 全屏终端应用 - * PS/2 键盘输入,命令行编辑,内置命令。 - * Esc 键退出返回 DSK。 - */ - -#include "../../firstInit/ascii_bitmaps.c" -#include "../desktop_app.h" - -/* ---- 终端参数 ---- */ -#define TERM_COLS 120 -#define TERM_ROWS 40 -#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS) -#define CHAR_STEP 12 -#define CHAR_H 18 -#define PROMPT_STR "deshab# " - -static da_app_context g_ac; - -/* 终端缓冲区 */ -static u8 term_ch[TERM_MAX_CHARS]; -static u32 term_fg[TERM_MAX_CHARS]; -static int term_w = TERM_COLS; -static int term_h = TERM_ROWS; -static int cur_col = 0; -static int cur_row = 0; - -/* 输入缓冲 */ -static char input_buf[256]; -static int input_len = 0; -static int input_cursor = 0; - -/* 命令历史 */ -#define HIST_SIZE 16 -static char history[HIST_SIZE][256]; -static int hist_count = 0; -static int hist_idx = 0; - -/* 坐标 */ -static int term_x, term_y; -static int content_h; - -static void term_init_layout(void) { - term_x = 8; - term_y = DA_TITLEBAR_H + 4; - int avail_w = (int)g_ac.fb_w - 16; - int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8; - term_w = avail_w / CHAR_STEP; - term_h = avail_h / CHAR_H; - if (term_w < 10) term_w = 10; - if (term_h < 5) term_h = 5; - if (term_w > TERM_COLS) term_w = TERM_COLS; - if (term_h > TERM_ROWS) term_h = TERM_ROWS; - content_h = term_h * CHAR_H; -} - -static void term_clear(void) { - for (int i = 0; i < TERM_MAX_CHARS; i++) { - term_ch[i] = ' '; - term_fg[i] = DA_TEXT_PRIMARY; - } - cur_col = 0; cur_row = 0; -} - -static void term_scroll(void) { - for (int r = 1; r < term_h; r++) { - for (int c = 0; c < term_w; c++) { - term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c]; - term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c]; - } - } - for (int c = 0; c < term_w; c++) { - term_ch[(term_h-1)*TERM_COLS+c] = ' '; - term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY; - } - cur_row = term_h - 1; cur_col = 0; -} - -static void term_newline(void) { - cur_col = 0; cur_row++; - if (cur_row >= term_h) term_scroll(); -} - -static void term_putc_color(char c, u32 color) { - if (c == '\n') { term_newline(); return; } - if (c == '\r') { cur_col = 0; return; } - if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; } - if (c < ' ' || c > '~') c = '?'; - if (cur_col >= term_w) term_newline(); - int idx = cur_row * TERM_COLS + cur_col; - term_ch[idx] = (u8)c; term_fg[idx] = color; - cur_col++; -} - -static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); } -static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); } -static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); } - -/* ---- 绘制 ---- */ -static void redraw_all(void) { - da_fill_bg(&g_ac, DA_BG_PRIMARY); - da_draw_titlebar(&g_ac, "Deshab Bash", (i64)g_ac.fb_w); - /* 终端字符 */ - for (int r = 0; r < term_h; r++) { - for (int c = 0; c < term_w; c++) { - int idx = r * TERM_COLS + c; - if (term_ch[idx] == ' ') continue; - da_draw_char(&g_ac, term_ch[idx], - term_x + c * CHAR_STEP, term_y + r * CHAR_H, - term_fg[idx], DA_BG_PRIMARY); - } - } - /* 光标 */ - if (cur_col < term_w && cur_row < term_h) { - da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, - term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT); - } - /* 状态栏 */ - char status[64]; - int p = 0; - const char *s1 = "/ root@deshab "; - while (*s1) status[p++] = *s1++; - u8 hh, mm; da_rtc_time(&hh, &mm); - status[p++] = '0' + hh/10; status[p++] = '0' + hh%10; - status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10; - status[p] = 0; - da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h); -} - -/* ---- 内置命令 ---- */ -static int str_eq(const char *a, const char *b) { - while (*a && *b) { if (*a != *b) return 0; a++; b++; } - return *a == *b; -} - -static void cmd_help(void) { - term_puts_color("Deshab Bash - 内置命令:\n", DA_PROMPT_COLOR); - term_puts(" help 显示此帮助\n"); - term_puts(" clear 清屏\n"); - term_puts(" echo 显示文本\n"); - term_puts(" version 显示系统版本\n"); - term_puts(" ver 同 version\n"); - term_puts(" uname 显示系统名称\n"); - term_puts(" date 显示当前日期时间\n"); - term_puts(" about 关于 Deshab\n"); - term_puts(" reboot 重启系统\n"); - term_puts(" halt 关机\n"); - term_puts(" pwd 显示当前目录\n"); - term_puts(" whoami 显示当前用户\n"); - term_puts(" id 显示用户ID\n"); - term_puts(" ls 列出根目录文件\n"); - term_puts(" cat 显示文件内容\n"); -} - -static void cmd_version(void) { - term_puts_color("Deshab OS v0.1.0\n", DA_SUCCESS); - term_puts(" Kernel: DSK (Deshab System Kernel)\n"); - term_puts(" Architecture: x86_64\n"); - term_puts(" ABI: DKM v1\n"); - term_puts(" Bootloader: Limine\n"); -} - -static void cmd_uname(void) { term_puts("Deshab\n"); } - -static void cmd_about(void) { - term_puts_color("=== Deshab OS ===\n", DA_PROMPT_COLOR); - term_puts("单地址空间 Ring0 内核实验系统\n"); - term_puts("架构: UTSM + DSK + DKM\n"); - term_puts_color("Built with Clang + lld\n", DA_TEXT_DIM); -} - -static void cmd_clear(void) { term_clear(); } - -static void cmd_reboot(void) { - term_puts_color("Rebooting...\n", DA_PROMPT_COLOR); - redraw_all(); - outb(0x64, 0xFE); - for(;;) __asm__("hlt"); -} - -static void cmd_halt(void) { - term_puts_color("System halted.\n", DA_PROMPT_COLOR); - redraw_all(); - for(;;) __asm__("hlt"); -} - -static void cmd_echo(const char *args) { - if (args && *args) term_puts_color(args, DA_TEXT_PRIMARY); - term_putc('\n'); -} - -static void cmd_date(void) { - u8 year, month, day, hour, minute, second; - outb(0x70, 9); year = inb(0x71); - outb(0x70, 8); month = inb(0x71); - outb(0x70, 7); day = inb(0x71); - outb(0x70, 4); hour = inb(0x71); - outb(0x70, 2); minute = inb(0x71); - outb(0x70, 0); second = inb(0x71); - year = (u8)((year>>4)*10+(year&0xF)); - month = (u8)((month>>4)*10+(month&0xF)); - day = (u8)((day>>4)*10+(day&0xF)); - hour = (u8)((hour>>4)*10+(hour&0xF)); - minute = (u8)((minute>>4)*10+(minute&0xF)); - second = (u8)((second>>4)*10+(second&0xF)); - char buf[32]; int p = 0; - buf[p++]='2'; buf[p++]='0'; buf[p++]='0'+year/10; buf[p++]='0'+year%10; buf[p++]='-'; - buf[p++]='0'+month/10; buf[p++]='0'+month%10; buf[p++]='-'; - buf[p++]='0'+day/10; buf[p++]='0'+day%10; buf[p++]=' '; - buf[p++]='0'+hour/10; buf[p++]='0'+hour%10; buf[p++]=':'; - buf[p++]='0'+minute/10; buf[p++]='0'+minute%10; buf[p++]=':'; - buf[p++]='0'+second/10; buf[p++]='0'+second%10; buf[p]=0; - term_puts(buf); term_putc('\n'); -} - -static void cmd_ls(void) { - if (!g_ac.block_read) { - term_puts_color("ls: 无块设备可用\n", DA_ERROR); - return; - } - /* 读取 FAT32 根目录 — 简化版:读取 BPB 获取根目录区 */ - u8 bpb[512]; - if (g_ac.block_read(0, 0, 1, bpb) != 0) { - term_puts_color("ls: 读取 BPB 失败\n", DA_ERROR); - return; - } - /* 检查 FAT32 签名 */ - if (bpb[510] != 0x55 || bpb[511] != 0xAA) { - term_puts_color("ls: 无有效 FAT32 分区\n", DA_ERROR); - return; - } - u32 root_cluster = *(u32 *)(bpb + 44); - u16 bytes_per_sector = *(u16 *)(bpb + 11); - u8 sectors_per_cluster = bpb[13]; - u32 fat_start = *(u16 *)(bpb + 14); - u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2; - (void)bytes_per_sector; - (void)sectors_per_cluster; - (void)data_start; - - /* 读取根目录簇 — 简化:只读取第一个簇 */ - u8 dir_buf[8192]; - u32 cluster = root_cluster; - u32 lba = data_start + (cluster - 2) * sectors_per_cluster; - if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) { - term_puts_color("ls: 读取根目录失败\n", DA_ERROR); - return; - } - /* 解析目录项 */ - int found = 0; - for (int i = 0; i < (int)(sectors_per_cluster * 512); i += 32) { - u8 first = dir_buf[i]; - if (first == 0x00) break; - if (first == 0xE5) continue; - if (dir_buf[i+11] & 0x08) continue; /* 卷标 */ - if (dir_buf[i+11] & 0x10) { - /* 目录 */ - term_puts_color("[DIR] ", DA_ACCENT); - } else { - term_puts_color(" ", DA_TEXT_PRIMARY); - } - /* 文件名(8.3) */ - for (int j = 0; j < 8; j++) { - if (dir_buf[i+j] == ' ') break; - term_putc((char)dir_buf[i+j]); - } - if (dir_buf[i+8] != ' ') { - term_putc('.'); - for (int j = 8; j < 11; j++) { - if (dir_buf[i+j] == ' ') break; - term_putc((char)dir_buf[i+j]); - } - } - term_putc('\n'); - found++; - } - if (found == 0) term_puts_color("(empty)\n", DA_TEXT_DIM); -} - -static void cmd_cat(const char *args) { - if (!args || !*args) { - term_puts_color("cat: 需要文件名参数\n", DA_ERROR); - return; - } - if (!g_ac.block_read) { - term_puts_color("cat: 无块设备可用\n", DA_ERROR); - return; - } - term_puts_color("cat: 文件读取暂未完整实现\n", DA_WARNING); -} - -static void execute_command(const char *cmd) { - while (*cmd == ' ') cmd++; - if (*cmd == 0) return; - const char *args = cmd; - while (*args && *args != ' ') args++; - char name[32]; - int nlen = (int)(args - cmd); - if (nlen >= 32) nlen = 31; - for (int i = 0; i < nlen; i++) name[i] = cmd[i]; - name[nlen] = 0; - while (*args == ' ') args++; - - if (str_eq(name,"help")||str_eq(name,"?")) cmd_help(); - else if (str_eq(name,"version")||str_eq(name,"ver")) cmd_version(); - else if (str_eq(name,"uname")) cmd_uname(); - else if (str_eq(name,"about")) cmd_about(); - else if (str_eq(name,"clear")||str_eq(name,"cls")) cmd_clear(); - else if (str_eq(name,"reboot")) cmd_reboot(); - else if (str_eq(name,"halt")||str_eq(name,"shutdown")) cmd_halt(); - else if (str_eq(name,"echo")) cmd_echo(args); - else if (str_eq(name,"date")||str_eq(name,"time")) cmd_date(); - else if (str_eq(name,"ls")||str_eq(name,"dir")) cmd_ls(); - else if (str_eq(name,"cat")) cmd_cat(args); - else if (str_eq(name,"pwd")) { term_puts("/\n"); } - else if (str_eq(name,"whoami")) { term_puts("root\n"); } - else if (str_eq(name,"id")) { term_puts("uid=0(root) gid=0(root)\n"); } - else { - term_puts_color("未知命令: ", DA_ERROR); - term_puts_color(name, DA_ERROR); - term_putc('\n'); - term_puts_color("输入 'help' 查看可用命令\n", DA_TEXT_DIM); - } -} - -static void add_history(const char *cmd) { - if (hist_count < HIST_SIZE) { - int i; - for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i]; - history[hist_count][i] = 0; - hist_count++; - } - hist_idx = hist_count; -} - -/* ---- 输入行重绘 ---- */ -static void redraw_input_line(void) { - int plen = 0; - while (PROMPT_STR[plen]) plen++; - i64 y = term_y + (i64)cur_row * CHAR_H; - da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY); - for (int i = 0; i < plen; i++) - da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY); - for (int i = 0; i < input_len; i++) - da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY); - cur_col = plen + input_cursor; - /* 光标 */ - da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT); -} - -/* ---- 主入口 ---- */ -__attribute__((visibility("default"))) -void dsk_entry(const da_boot_context *ctx) { - __asm__ volatile("cli"); - da_slog("bash", "boot"); - - if (!ctx || ctx->magic != DA_BOOT_MAGIC) { - da_slog("bash", "bad context"); - for(;;) __asm__("hlt"); - } - - da_init(&g_ac, ctx); - term_init_layout(); - term_clear(); - - /* 启动 banner */ - term_puts_color("=== Deshab Bash v0.1 ===\n", DA_ACCENT); - term_puts_color("欢迎使用 Deshab 终端\n", DA_SUCCESS); - term_puts_color("输入 'help' 查看可用命令 | Esc 退出\n", DA_TEXT_DIM); - term_putc('\n'); - - redraw_all(); - - int shift = 0, e0 = 0; - for (;;) { - /* 绘制提示符 */ - term_puts_color(PROMPT_STR, DA_PROMPT_COLOR); - input_len = 0; input_cursor = 0; input_buf[0] = 0; - int plen = 0; while (PROMPT_STR[plen]) plen++; - cur_col = plen; - redraw_all(); - - int cmd_done = 0; - while (!cmd_done) { - u8 st = inb(0x64); - if (!(st & 1)) { __asm__("pause"); continue; } - u8 data = inb(0x60); - if (st & 0x20) continue; - u8 sc = data; - if (sc == 0xE0) { e0 = 1; continue; } - if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } - if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } - if (sc & 0x80) { e0 = 0; continue; } - - /* Esc 退出 */ - if (sc == 0x01 && !e0) { - da_slog("bash", "exit"); - return; - } - - if (e0) { - if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; } - else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; } - else if (sc == 0x47) { input_cursor = 0; } - else if (sc == 0x4F) { input_cursor = input_len; } - else if (sc == 0x48) { /* 上箭头 - 历史 */ - if (hist_idx > 0) { - hist_idx--; - int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i]; - input_len = i; input_cursor = i; input_buf[i] = 0; - } - } - else if (sc == 0x50) { /* 下箭头 - 历史 */ - if (hist_idx < hist_count - 1) { - hist_idx++; - int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i]; - input_len = i; input_cursor = i; input_buf[i] = 0; - } else if (hist_idx < hist_count) { - hist_idx = hist_count; - input_len = 0; input_cursor = 0; input_buf[0] = 0; - } - } - e0 = 0; - redraw_input_line(); - continue; - } - - if (sc == 0x1C) { /* Enter */ - input_buf[input_len] = 0; - term_putc('\n'); - if (input_len > 0) add_history(input_buf); - execute_command(input_buf); - cmd_done = 1; - continue; - } - if (sc == 0x0E) { /* Backspace */ - if (input_cursor > 0) { - for (int i = input_cursor - 1; i < input_len - 1; i++) - input_buf[i] = input_buf[i+1]; - input_len--; input_cursor--; - input_buf[input_len] = 0; - } - redraw_input_line(); - continue; - } - char c = da_scan_to_ascii(sc, shift); - if (c && c >= 32 && c <= 126 && input_len < 255) { - for (int i = input_len; i > input_cursor; i--) - input_buf[i] = input_buf[i-1]; - input_buf[input_cursor++] = c; - input_len++; - redraw_input_line(); - } - } - redraw_all(); - } -} diff --git a/CODE/tools/browser/linker.ld b/CODE/tools/browser/linker.ld index 94dddf7..473e5fc 100644 --- a/CODE/tools/browser/linker.ld +++ b/CODE/tools/browser/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(7); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(7); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/tools/curl/linker.ld b/CODE/tools/curl/linker.ld index 94dddf7..473e5fc 100644 --- a/CODE/tools/curl/linker.ld +++ b/CODE/tools/curl/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(7); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(7); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/tools/curl/main.c b/CODE/tools/curl/main.c index b06724c..f6ba1cb 100644 --- a/CODE/tools/curl/main.c +++ b/CODE/tools/curl/main.c @@ -1,11 +1,11 @@ -/* Deshab curl — 终端风格 HTTP 客户端 stub - * 命令行输入 URL,显示 HTTP GET 请求结果。 - * 当前为 stub,提示需要 TCP/IP 栈支持。 +/* Deshab curl — 终端风格 HTTP 客户端 + * 命令行输入 URL,发送 HTTP/1.0 GET 请求并打印响应(net_stack.h 共享协议栈)。 * Esc 键退出返回 DSK。 */ #include "../../firstInit/ascii_bitmaps.c" #include "../desktop_app.h" +#include "../net_stack.h" /* ---- 终端参数 ---- */ #define TERM_COLS 120 @@ -125,29 +125,121 @@ static void redraw_all(void) { da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h); } -/* ---- URL 请求模拟 ---- */ +/* ---- HTTP GET 实现(net_stack.h) ---- */ +static int g_ns_status = 0; /* 0=未尝试, 1=就绪, <0=失败 */ + +static int curl_net_ready(void) { + if (g_ns_status == 1) return 1; + if (g_ns_status < 0) return 0; + int rc = ns_init((u64)g_ac.kernel_api); + g_ns_status = (rc == 0) ? 1 : -1; + if (rc != 0) { + term_puts_color(" [错误] 网络初始化失败,无可用网卡或 net API\n", DA_ERROR); + return 0; + } + return 1; +} + static void do_http_get(const char *url) { + if (!curl_net_ready()) return; + + const char *p = url; + if (p[0]=='h'&&p[1]=='t'&&p[2]=='t'&&p[3]=='p'&&p[4]==':'&&p[5]=='/'&&p[6]=='/') p += 7; + + char host[128]; int hl = 0; + while (*p && *p != ':' && *p != '/' && hl < 127) host[hl++] = *p++; + host[hl] = 0; + if (!hl) { term_puts_color(" [错误] 缺少主机名\n", DA_ERROR); return; } + + u16 port = 80; + if (*p == ':') { + p++; + u32 pv = 0; int digits = 0; + while (*p >= '0' && *p <= '9') { pv = pv * 10 + (u32)(*p - '0'); p++; digits++; } + if (digits && pv <= 65535) port = (u16)pv; + } + char path[128]; int pl = 0; + if (*p == '/') { + while (*p && *p != ' ' && pl < 127) path[pl++] = *p++; + } + if (!pl) { path[0] = '/'; pl = 1; } + path[pl] = 0; + term_puts_color("GET ", DA_PROMPT_COLOR); term_puts_color(url, DA_ACCENT_LIGHT); term_putc('\n'); - term_puts_color(" -> 解析主机...\n", DA_TEXT_DIM); - term_puts_color(" -> 建立 TCP 连接...\n", DA_TEXT_DIM); - /* stub: 检查 kernel_api 是否有 net 接口 */ - if (!g_ac.kernel_api) { - term_puts_color(" [错误] 无内核 API\n", DA_ERROR); + /* 解析主机 */ + term_puts_color(" -> 解析 ", DA_TEXT_DIM); + term_puts_color(host, DA_TEXT_DIM); + term_puts_color("...\n", DA_TEXT_DIM); + redraw_all(); + u32 ip; + if (ns_dns_resolve(host, &ip) != 0) { + term_puts_color(" [错误] DNS 解析失败\n", DA_ERROR); return; } - /* 尝试读取 net API — 当前为 stub */ - u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0); - if (!net_api) { - term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR); - term_puts_color(" 当前内核未实现网络协议栈。\n", DA_WARNING); - term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM); + char ipstr[24]; ns_fmt_ip(ip, ipstr); + term_puts_color(" -> ", DA_TEXT_DIM); + term_puts_color(ipstr, DA_TEXT_DIM); + term_putc('\n'); + + /* TCP 连接 */ + term_puts_color(" -> TCP 连接...\n", DA_TEXT_DIM); + redraw_all(); + if (ns_tcp_connect(ip, port, 2000) != 0) { + term_puts_color(" [错误] TCP 连接失败(超时/RST)\n", DA_ERROR); return; } - /* 如果有 net API,此处应实现真正的 HTTP GET */ - term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING); + + /* 构造 HTTP/1.0 请求 */ + static char req[512]; + int r = 0; + const char *m = "GET "; while (*m) req[r++] = *m++; + for (int i = 0; i < pl; i++) req[r++] = path[i]; + m = " HTTP/1.0\r\nHost: "; while (*m) req[r++] = *m++; + for (int i = 0; i < hl; i++) req[r++] = host[i]; + m = "\r\nConnection: close\r\n\r\n"; while (*m) req[r++] = *m++; + req[r] = 0; + + term_puts_color(" -> 已连接,发送请求\n", DA_TEXT_DIM); + redraw_all(); + if (ns_tcp_send((const u8 *)req, (u32)r) != 0) { + term_puts_color(" [错误] 发送失败\n", DA_ERROR); + ns_tcp_close(); + return; + } + + /* 收至 FIN */ + term_puts_color(" -> 等待响应...\n", DA_TEXT_DIM); + redraw_all(); + int n = ns_tcp_recv(0, 0, 5000); + ns_tcp_close(); + if (n < 0) { + term_puts_color(" [错误] 连接被重置\n", DA_ERROR); + return; + } + if (n == 0) { + term_puts_color(" [错误] 无响应数据(超时)\n", DA_ERROR); + return; + } + + term_puts_color(" -> 收到 ", DA_SUCCESS); + char nb[12]; ns_u32_dec(nb, (u32)n); + term_puts_color(nb, DA_SUCCESS); + term_puts_color(" 字节\n\n", DA_SUCCESS); + + /* 打印 status line + body */ + u32 total = ns_tcp_rx_len; + u32 show = (u32)n; + for (u32 i = 0; i < show; i++) term_putc((char)ns_tcp_rx[i]); + if (total > show) { + term_puts_color("\n[... 已截断,共 ", DA_TEXT_DIM); + ns_u32_dec(nb, total); + term_puts_color(nb, DA_TEXT_DIM); + term_puts_color(" 字节]\n", DA_TEXT_DIM); + } + term_putc('\n'); } /* ---- 内置命令 ---- */ @@ -168,8 +260,8 @@ static void cmd_help(void) { static void cmd_clear(void) { term_clear(); } static void cmd_version(void) { - term_puts_color("Deshab curl v0.1 (stub)\n", DA_SUCCESS); - term_puts_color(" HTTP 客户端功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM); + term_puts_color("Deshab curl v1.0\n", DA_SUCCESS); + term_puts_color(" HTTP/1.0 GET via net_stack (e1000 + slirp)\n", DA_TEXT_DIM); } static void execute_command(const char *cmd) { @@ -231,10 +323,9 @@ void dsk_entry(const da_boot_context *ctx) { term_clear(); /* 启动 banner */ - term_puts_color("=== Deshab curl v0.1 (stub) ===\n", DA_ACCENT); + term_puts_color("=== Deshab curl v1.0 ===\n", DA_ACCENT); term_puts_color("HTTP 命令行客户端\n", DA_SUCCESS); term_puts_color("输入 URL 发送 GET 请求 | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM); - term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING); term_putc('\n'); redraw_all(); diff --git a/CODE/tools/editor/linker.ld b/CODE/tools/editor/linker.ld index 94dddf7..473e5fc 100644 --- a/CODE/tools/editor/linker.ld +++ b/CODE/tools/editor/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(7); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(7); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/tools/fileman/linker.ld b/CODE/tools/fileman/linker.ld index 94dddf7..473e5fc 100644 --- a/CODE/tools/fileman/linker.ld +++ b/CODE/tools/fileman/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(7); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(7); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/tools/net_stack.h b/CODE/tools/net_stack.h new file mode 100644 index 0000000..6e6554c --- /dev/null +++ b/CODE/tools/net_stack.h @@ -0,0 +1,716 @@ +/* net_stack.h — Deshab 用户态共享网络协议栈(static inline,无库) + * + * 前置条件:包含者已定义 u8/u16/u32/u64/i64 基础类型。 + * 通过 dkm_kernel_api + 0x48 的 net API 收发原始以太帧(轮询模型,无 IRQ)。 + * + * 能力:eth II / ARP(含应答)/ IPv4 / ICMP echo / UDP DNS / TCP 最小 client。 + * 默认拓扑:QEMU slirp — guest 10.0.2.15 / gw 10.0.2.2 / dns 10.0.2.3。 + * + * 用法: + * ns_init(ctx->dkm_kernel_api) — 初始化,选第一个有线 tx/rx ready 设备 + * ns_ping(ip, seq, timeout, &rtt, &ttl) — 单次 ICMP echo + * ns_dns_resolve(host, &ip) — A 记录查询(数字 IP 直接返回) + * ns_tcp_connect(ip, port, timeout) + * ns_tcp_send(data, len) + * ns_tcp_recv(buf, cap, idle_timeout) — 收至 FIN/超时,返回累计长度 + * ns_tcp_close() + * + * IP 以 u32 网络序数值表示(NS_IP4(10,0,2,15))。 + */ + +#ifndef DESHAB_NET_STACK_H +#define DESHAB_NET_STACK_H + +/* ---- 默认网络配置(QEMU user-mode slirp) ---- */ +#define NS_IP4(a,b,c,d) ((u32)((((u32)(a))<<24)|(((u32)(b))<<16)|(((u32)(c))<<8)|((u32)(d)))) +#define NS_GUEST_IP NS_IP4(10,0,2,15) +#define NS_GATEWAY_IP NS_IP4(10,0,2,2) +#define NS_DNS_IP NS_IP4(10,0,2,3) +#define NS_NETMASK NS_IP4(255,255,255,0) + +#define NS_ETH_ARP 0x0806 +#define NS_ETH_IPV4 0x0800 +#define NS_IP_ICMP 1 +#define NS_IP_TCP 6 +#define NS_IP_UDP 17 + +#define NS_ARP_CACHE_SIZE 8 +#define NS_TCP_RX_CAP 65536 + +/* dkm_net_device_desc.flags 位(与 utsm/net.h 一致) */ +#define NS_NET_F_TX_READY 0x2 +#define NS_NET_F_RX_READY 0x4 +#define NS_NET_F_WIRELESS 0x8 + +/* ---- 端口 I/O / TSC(ns_ 前缀避免与包含者冲突) ---- */ +static inline void ns_outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p)); } +static inline u8 ns_inb(u16 p) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(p)); return v; } +static inline u64 ns_rdtsc(void) { + u32 lo, hi; + __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi)); + return ((u64)hi << 32) | lo; +} + +/* PIT ch0 ~10ms 校准 TSC(与 shell/dsk 同法) */ +static u64 ns_tsc_per_ms = 0; +static inline void ns_tsc_calibrate(void) { + if (ns_tsc_per_ms) return; + ns_outb(0x43, 0x30); + ns_outb(0x40, 0x7C); + ns_outb(0x40, 0x2E); + u64 t0 = ns_rdtsc(); + u16 prev = 0; u64 loops = 0; + for (;;) { + ns_outb(0x43, 0x00); + u16 cur = (u16)ns_inb(0x40) | ((u16)ns_inb(0x40) << 8); + if (cur > prev && loops > 10) break; + prev = cur; loops++; + } + ns_tsc_per_ms = (ns_rdtsc() - t0) / 10; + if (!ns_tsc_per_ms) ns_tsc_per_ms = 3000; /* 兜底 ~3GHz */ +} + +static inline int ns_deadline_reached(u64 deadline) { + return (i64)(ns_rdtsc() - deadline) >= 0; +} + +/* ---- 字节序/校验和 ---- */ +static inline void ns_w16(u8 *p, u16 v) { p[0] = (u8)(v >> 8); p[1] = (u8)v; } +static inline void ns_w32(u8 *p, u32 v) { p[0] = (u8)(v >> 24); p[1] = (u8)(v >> 16); p[2] = (u8)(v >> 8); p[3] = (u8)v; } +static inline u16 ns_r16(const u8 *p) { return (u16)(((u16)p[0] << 8) | p[1]); } +static inline u32 ns_r32(const u8 *p) { return ((u32)p[0] << 24) | ((u32)p[1] << 16) | ((u32)p[2] << 8) | p[3]; } + +static inline u16 ns_checksum(const void *data, u32 len) { + const u8 *p = (const u8 *)data; + u32 sum = 0; + while (len > 1) { sum += ((u16)p[0] << 8) | p[1]; p += 2; len -= 2; } + if (len) sum += ((u16)p[0] << 8); + while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); + return (u16)~sum; +} + +static inline void ns_memcpy(void *d, const void *s, u32 n) { + u8 *dd = (u8 *)d; const u8 *ss = (const u8 *)s; + while (n--) *dd++ = *ss++; +} +static inline void ns_memset(void *d, u8 c, u32 n) { + u8 *dd = (u8 *)d; while (n--) *dd++ = c; +} + +/* ---- 十进制/IP 字符串 ---- */ +static inline void ns_u32_dec(char *buf, u32 v) { + char tmp[12]; int n = 0; + if (!v) { buf[0] = '0'; buf[1] = 0; return; } + while (v && n < 11) { tmp[n++] = (char)('0' + v % 10); v /= 10; } + for (int i = 0; i < n; i++) buf[i] = tmp[n - 1 - i]; + buf[n] = 0; +} + +static inline void ns_fmt_ip(u32 ip, char *out) { + int p = 0; + for (int i = 3; i >= 0; i--) { + char d[4]; ns_u32_dec(d, (ip >> (i * 8)) & 0xFF); + for (int j = 0; d[j]; j++) out[p++] = d[j]; + if (i) out[p++] = '.'; + } + out[p] = 0; +} + +/* 解析 "a.b.c.d" → 网络序 u32。返回 0 成功。 */ +static inline int ns_parse_ip(const char *s, u32 *out) { + u32 v = 0; + for (int part = 0; part < 4; part++) { + u32 n = 0; int digits = 0; + while (*s >= '0' && *s <= '9') { + n = n * 10 + (u32)(*s - '0'); + if (n > 255) return -1; + s++; digits++; + } + if (!digits) return -1; + v = (v << 8) | n; + if (part < 3) { + if (*s != '.') return -1; + s++; + } + } + if (*s && *s != ' ') return -1; + *out = v; + return 0; +} + +/* ---- net API 绑定 ---- */ +typedef int (*ns_net_tx_fn)(u32 index, const void *packet, u32 length); +typedef int (*ns_net_rx_poll_fn)(u32 index, void *buffer, u32 capacity, u32 *out_length); +typedef u32 (*ns_net_count_fn)(void); +typedef int (*ns_net_info_fn)(u32 index, void *out); + +static int ns_ready = 0; +static u32 ns_dev_index = 0; +static u8 ns_mac[6] = {0}; +static ns_net_tx_fn ns_net_tx = 0; +static ns_net_rx_poll_fn ns_net_rx_poll_fn_ptr = 0; +static u16 ns_ip_id = 1; + +/* dkm_net_device_info 布局:name ptr @0, mac[6] @8, flags @16(对齐填充后 size 24) */ +typedef struct __attribute__((packed)) { + u64 name; + u8 mac[6]; + u16 pad; + u32 flags; +} ns_dev_info; + +static inline int ns_init(u64 dkm_kernel_api) { + if (ns_ready) return 0; + if (!dkm_kernel_api) return -1; + u64 net = *(u64 *)(dkm_kernel_api + 0x48); + if (!net) return -2; + ns_net_count_fn device_count = (ns_net_count_fn)*(u64 *)(net + 0x08); + ns_net_info_fn device_info = (ns_net_info_fn)*(u64 *)(net + 0x10); + ns_net_tx = (ns_net_tx_fn)*(u64 *)(net + 0x18); + ns_net_rx_poll_fn_ptr = (ns_net_rx_poll_fn)*(u64 *)(net + 0x20); + if (!device_count || !device_info || !ns_net_tx || !ns_net_rx_poll_fn_ptr) return -3; + u32 n = device_count(); + if (!n) return -4; + + int picked = -1; + ns_dev_info info; + for (u32 i = 0; i < n && i < 8; i++) { + ns_memset(&info, 0, sizeof(info)); + if (device_info(i, &info) != 0) continue; + if (info.flags & NS_NET_F_WIRELESS) continue; + if ((info.flags & NS_NET_F_TX_READY) && (info.flags & NS_NET_F_RX_READY)) { + picked = (int)i; + for (int k = 0; k < 6; k++) ns_mac[k] = info.mac[k]; + break; + } + } + if (picked < 0) { + ns_memset(&info, 0, sizeof(info)); + if (device_info(0, &info) != 0) return -5; + picked = 0; + for (int k = 0; k < 6; k++) ns_mac[k] = info.mac[k]; + } + ns_dev_index = (u32)picked; + ns_tsc_calibrate(); + ns_ready = 1; + return 0; +} + +/* ---- 帧缓冲 ---- */ +static u8 ns_txbuf[2048]; +static u8 ns_rxframe[2048]; + +/* 发送以太帧 */ +static inline int ns_send_eth(const u8 dst_mac[6], u16 ethertype, const u8 *payload, u32 len) { + if (len + 14 > sizeof(ns_txbuf)) return -1; + for (int i = 0; i < 6; i++) ns_txbuf[i] = dst_mac[i]; + for (int i = 0; i < 6; i++) ns_txbuf[6 + i] = ns_mac[i]; + ns_w16(ns_txbuf + 12, ethertype); + ns_memcpy(ns_txbuf + 14, payload, len); + u32 total = len + 14; + if (total < 60) { /* 以太网最小帧 */ + ns_memset(ns_txbuf + total, 0, 60 - total); + total = 60; + } + return ns_net_tx(ns_dev_index, ns_txbuf, total); +} + +/* ---- ARP ---- */ +typedef struct { + u32 ip; + u8 mac[6]; + u8 valid; +} ns_arp_entry; + +static ns_arp_entry ns_arp_cache[NS_ARP_CACHE_SIZE]; +static u8 ns_arp_reply_mac[6]; +static volatile int ns_arp_reply_valid = 0; + +static inline void ns_arp_cache_put(u32 ip, const u8 mac[6]) { + int slot = -1; + for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) { + if (ns_arp_cache[i].valid && ns_arp_cache[i].ip == ip) { slot = i; break; } + } + if (slot < 0) { + for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) { + if (!ns_arp_cache[i].valid) { slot = i; break; } + } + } + if (slot < 0) slot = 0; + ns_arp_cache[slot].ip = ip; + for (int k = 0; k < 6; k++) ns_arp_cache[slot].mac[k] = mac[k]; + ns_arp_cache[slot].valid = 1; +} + +static inline int ns_arp_cache_get(u32 ip, u8 mac[6]) { + for (int i = 0; i < NS_ARP_CACHE_SIZE; i++) { + if (ns_arp_cache[i].valid && ns_arp_cache[i].ip == ip) { + for (int k = 0; k < 6; k++) mac[k] = ns_arp_cache[i].mac[k]; + return 0; + } + } + return -1; +} + +/* 构造 ARP 包到 out(42 字节),oper 1=request 2=reply */ +static inline void ns_arp_build(u8 *out, u16 oper, + const u8 sha[6], u32 spa, + const u8 tha[6], u32 tpa) { + ns_w16(out + 0, 1); /* htype eth */ + ns_w16(out + 2, NS_ETH_IPV4); /* ptype */ + out[4] = 6; out[5] = 4; + ns_w16(out + 6, oper); + for (int i = 0; i < 6; i++) out[8 + i] = sha[i]; + ns_w32(out + 14, spa); + for (int i = 0; i < 6; i++) out[18 + i] = tha[i]; + ns_w32(out + 24, tpa); +} + +/* 处理收到的 ARP 包:应答对我们的请求,记录 reply。 */ +static inline void ns_arp_handle(const u8 *pkt, u32 len) { + if (len < 28) return; + u16 oper = ns_r16(pkt + 6); + u32 spa = ns_r32(pkt + 14); + u32 tpa = ns_r32(pkt + 24); + if (oper == 1 && tpa == NS_GUEST_IP) { + /* 请求我们的 IP → 回复 */ + u8 reply[28]; + ns_arp_build(reply, 2, ns_mac, NS_GUEST_IP, pkt + 8, spa); + ns_send_eth(pkt + 8, NS_ETH_ARP, reply, 28); + } else if (oper == 2) { + ns_arp_cache_put(spa, pkt + 8); + for (int k = 0; k < 6; k++) ns_arp_reply_mac[k] = pkt[8 + k]; + ns_arp_reply_valid = 1; + } +} + +/* 轮询一帧;ARP 内部处理(不返回给调用者)。 + * 返回 0=收到非 ARP 帧(*out_et 为 ethertype),1=超时。 */ +static inline int ns_wait_frame(u8 *buf, u32 cap, u32 *out_len, u16 *out_et, u32 timeout_ms) { + u64 deadline = ns_rdtsc() + ns_tsc_per_ms * timeout_ms; + for (;;) { + u32 len = 0; + int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, buf, cap, &len); + if (rc == 0 && len >= 14) { + u16 et = ns_r16(buf + 12); + if (et == NS_ETH_ARP) { + ns_arp_handle(buf + 14, len - 14); + /* 继续等待 */ + } else { + *out_len = len; + *out_et = et; + return 0; + } + } + if (ns_deadline_reached(deadline)) return 1; + __asm__ volatile("pause"); + } +} + +/* ARP 解析 ip → mac,3 次重传。返回 0 成功。 */ +static inline int ns_arp_resolve(u32 ip, u8 out_mac[6]) { + if (ns_arp_cache_get(ip, out_mac) == 0) return 0; + static const u8 bcast[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + static const u8 zero_mac[6] = {0,0,0,0,0,0}; + for (int attempt = 0; attempt < 3; attempt++) { + u8 req[28]; + ns_arp_build(req, 1, ns_mac, NS_GUEST_IP, zero_mac, ip); + ns_arp_reply_valid = 0; + if (ns_send_eth(bcast, NS_ETH_ARP, req, 28) != 0) continue; + u64 deadline = ns_rdtsc() + ns_tsc_per_ms * 1000; + while (!ns_deadline_reached(deadline)) { + u32 len = 0; + int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len); + if (rc == 0 && len >= 14 && ns_r16(ns_rxframe + 12) == NS_ETH_ARP) { + ns_arp_handle(ns_rxframe + 14, len - 14); + if (ns_arp_reply_valid) { + u32 spa = ns_r32(ns_rxframe + 14 + 14); + if (spa == ip) { + for (int k = 0; k < 6; k++) out_mac[k] = ns_arp_reply_mac[k]; + return 0; + } + ns_arp_reply_valid = 0; + } + } + __asm__ volatile("pause"); + } + } + return -1; +} + +/* 目标 IP 的下一跳 MAC(同子网直连,否则走网关)。返回 0 成功。 */ +static inline int ns_route_mac(u32 dst_ip, u8 out_mac[6]) { + u32 next = dst_ip; + if ((dst_ip & NS_NETMASK) != (NS_GUEST_IP & NS_NETMASK)) next = NS_GATEWAY_IP; + return ns_arp_resolve(next, out_mac); +} + +/* ---- IPv4 发送 ---- */ +static inline void ns_ipv4_build(u8 *out, u8 proto, u32 src, u32 dst, u32 payload_len) { + out[0] = 0x45; /* ver=4 ihl=5 */ + out[1] = 0; /* tos */ + ns_w16(out + 2, (u16)(20 + payload_len)); + ns_w16(out + 4, ns_ip_id++); + ns_w16(out + 6, 0x4000); /* DF */ + out[8] = 64; /* ttl */ + out[9] = proto; + ns_w16(out + 10, 0); + ns_w32(out + 12, src); + ns_w32(out + 16, dst); + ns_w16(out + 10, ns_checksum(out, 20)); +} + +/* ---- ICMP echo(ping) ---- + * 返回 0 成功(*out_rtt_ms / *out_ttl 有效),-1 ARP 失败,-2 超时。 */ +static inline int ns_ping(u32 dst_ip, u16 seq, u32 timeout_ms, u32 *out_rtt_ms, u8 *out_ttl) { + u8 dst_mac[6]; + if (ns_route_mac(dst_ip, dst_mac) != 0) return -1; + + u8 pkt[20 + 8 + 32]; + ns_ipv4_build(pkt, NS_IP_ICMP, NS_GUEST_IP, dst_ip, 8 + 32); + u8 *icmp = pkt + 20; + icmp[0] = 8; /* echo request */ + icmp[1] = 0; + ns_w16(icmp + 2, 0); + ns_w16(icmp + 4, 0xD5AB); /* identifier */ + ns_w16(icmp + 6, seq); + for (int i = 0; i < 32; i++) icmp[8 + i] = (u8)(0x61 + (i % 26)); + ns_w16(icmp + 2, ns_checksum(icmp, 8 + 32)); + + if (ns_send_eth(dst_mac, NS_ETH_IPV4, pkt, sizeof(pkt)) != 0) return -3; + u64 t0 = ns_rdtsc(); + u64 deadline = t0 + ns_tsc_per_ms * timeout_ms; + while (!ns_deadline_reached(deadline)) { + u32 len = 0; + int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len); + if (rc == 0 && len >= 14) { + u16 et = ns_r16(ns_rxframe + 12); + if (et == NS_ETH_ARP) { + ns_arp_handle(ns_rxframe + 14, len - 14); + } else if (et == NS_ETH_IPV4 && len >= 14 + 20 + 8) { + const u8 *ip = ns_rxframe + 14; + u8 ihl = (u8)((ip[0] & 0x0F) * 4); + if (ihl < 20 || len < 14 + ihl + 8) continue; + if (ip[9] != NS_IP_ICMP) continue; + const u8 *ic = ip + ihl; + if (ic[0] == 0 && ns_r16(ic + 4) == 0xD5AB && ns_r16(ic + 6) == seq) { + u64 dt = ns_rdtsc() - t0; + if (out_rtt_ms) { + u32 ms = (u32)(dt / ns_tsc_per_ms); + *out_rtt_ms = ms; + } + if (out_ttl) *out_ttl = ip[8]; + return 0; + } + } + } + __asm__ volatile("pause"); + } + return -2; +} + +/* ---- UDP DNS A 查询 ---- + * host 为数字 IP 时直接解析返回。返回 0 成功。 */ +static inline int ns_dns_resolve(const char *host, u32 *out_ip) { + if (ns_parse_ip(host, out_ip) == 0) return 0; + + u8 dst_mac[6]; + if (ns_route_mac(NS_DNS_IP, dst_mac) != 0) return -1; + + /* 构造查询:header + qname + qtype/qclass */ + u8 q[512]; + u16 txid = (u16)(ns_rdtsc() & 0xFFFF); + if (!txid) txid = 0x1234; + ns_w16(q + 0, txid); + ns_w16(q + 2, 0x0100); /* RD */ + ns_w16(q + 4, 1); /* qdcount */ + ns_w16(q + 6, 0); ns_w16(q + 8, 0); ns_w16(q + 10, 0); + u32 p = 12; + u32 i = 0; + while (host[i]) { + u32 start = p++; + u8 n = 0; + while (host[i] && host[i] != '.') { + if (n >= 63 || p >= 500) return -2; + q[p++] = (u8)host[i++]; n++; + } + q[start] = n; + if (host[i] == '.') i++; + } + q[p++] = 0; + ns_w16(q + p, 1); p += 2; /* A */ + ns_w16(q + p, 1); p += 2; /* IN */ + u32 qlen = p; + + u8 pkt[20 + 8 + 512]; + ns_ipv4_build(pkt, NS_IP_UDP, NS_GUEST_IP, NS_DNS_IP, 8 + qlen); + u8 *udp = pkt + 20; + ns_w16(udp + 0, 49153); + ns_w16(udp + 2, 53); + ns_w16(udp + 4, (u16)(8 + qlen)); + ns_w16(udp + 6, 0); /* 不校验 UDP checksum */ + ns_memcpy(udp + 8, q, qlen); + + for (int attempt = 0; attempt < 2; attempt++) { + if (ns_send_eth(dst_mac, NS_ETH_IPV4, pkt, 20 + 8 + qlen) != 0) continue; + u64 deadline = ns_rdtsc() + ns_tsc_per_ms * 3000; + while (!ns_deadline_reached(deadline)) { + u32 len = 0; + int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len); + if (rc == 0 && len >= 14) { + u16 et = ns_r16(ns_rxframe + 12); + if (et == NS_ETH_ARP) { + ns_arp_handle(ns_rxframe + 14, len - 14); + } else if (et == NS_ETH_IPV4 && len >= 14 + 20 + 8 + 12) { + const u8 *ip = ns_rxframe + 14; + u8 ihl = (u8)((ip[0] & 0x0F) * 4); + if (ihl < 20 || ip[9] != NS_IP_UDP) continue; + const u8 *ru = ip + ihl; + if (ns_r16(ru + 2) != 49153) continue; + const u8 *d = ru + 8; + if (ns_r16(d + 0) != txid) continue; + u16 qd = ns_r16(d + 4); + u16 an = ns_r16(d + 6); + u32 off = 12; + /* 跳过问题段 */ + for (u16 qq = 0; qq < qd; qq++) { + while (off < len) { + u8 b = d[off]; + if (b == 0) { off++; break; } + if ((b & 0xC0) == 0xC0) { off += 2; break; } + off += 1 + b; + } + off += 4; + } + /* 遍历应答 RR */ + for (u16 rr = 0; rr < an; rr++) { + while (off < len) { /* 跳过 name */ + u8 b = d[off]; + if (b == 0) { off++; break; } + if ((b & 0xC0) == 0xC0) { off += 2; break; } + off += 1 + b; + } + if (off + 10 > len) break; + u16 rtype = ns_r16(d + off); + u16 rdlen = ns_r16(d + off + 8); + off += 10; + if (rtype == 1 && rdlen == 4 && off + 4 <= len) { + *out_ip = ns_r32(d + off); + return 0; + } + off += rdlen; + } + } + } + __asm__ volatile("pause"); + } + } + return -3; +} + +/* ---- TCP 最小 client ---- */ +#define NS_TCP_CLOSED 0 +#define NS_TCP_SYN_SENT 1 +#define NS_TCP_ESTAB 2 +#define NS_TCP_FIN_WAIT 3 + +static struct { + u32 state; + u32 dst_ip; + u16 dst_port; + u16 src_port; + u32 snd_nxt; + u32 rcv_nxt; + u8 dst_mac[6]; +} ns_tc = {0, 0, 0, 0, 0, 0, {0}}; + +static u8 ns_tcp_rx[NS_TCP_RX_CAP]; +static u32 ns_tcp_rx_len = 0; +static volatile int ns_tcp_got_fin = 0; +static volatile int ns_tcp_got_rst = 0; + +#define NS_TCP_FIN 0x01 +#define NS_TCP_SYN 0x02 +#define NS_TCP_RST 0x04 +#define NS_TCP_PSH 0x08 +#define NS_TCP_ACK 0x10 + +/* 构造并发送 TCP 段(data 可为 0)。 */ +static inline int ns_tcp_emit(u8 flags, const u8 *data, u32 len, u32 seq) { + u8 pkt[20 + 20 + 1460]; + if (len > 1460) return -1; + u32 tcp_len = 20 + len; + ns_ipv4_build(pkt, NS_IP_TCP, NS_GUEST_IP, ns_tc.dst_ip, tcp_len); + u8 *t = pkt + 20; + ns_w16(t + 0, ns_tc.src_port); + ns_w16(t + 2, ns_tc.dst_port); + ns_w32(t + 4, seq); + ns_w32(t + 8, (flags & NS_TCP_ACK) ? ns_tc.rcv_nxt : 0); + t[12] = (5 << 4); /* data offset */ + t[13] = flags; + ns_w16(t + 14, 64240); /* window */ + ns_w16(t + 16, 0); + ns_w16(t + 18, 0); + if (len) ns_memcpy(t + 20, data, len); + /* 伪头部校验和 */ + u8 ph[12]; + ns_w32(ph + 0, NS_GUEST_IP); + ns_w32(ph + 4, ns_tc.dst_ip); + ph[8] = 0; ph[9] = NS_IP_TCP; + ns_w16(ph + 10, (u16)tcp_len); + u32 sum = 0; + for (u32 k = 0; k < 12; k += 2) sum += ((u16)ph[k] << 8) | ph[k + 1]; + for (u32 k = 0; k + 1 < tcp_len; k += 2) sum += ((u16)t[k] << 8) | t[k + 1]; + if (tcp_len & 1) sum += ((u16)t[tcp_len - 1] << 8); + while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); + ns_w16(t + 16, (u16)~sum); + return ns_send_eth(ns_tc.dst_mac, NS_ETH_IPV4, pkt, 20 + tcp_len); +} + +/* 处理收到的 IPv4/TCP 段。返回 1 表示属于本连接并已处理。 */ +static inline int ns_tcp_process(const u8 *ip, u32 ip_len) { + if (ip_len < 20) return 0; + u8 ihl = (u8)((ip[0] & 0x0F) * 4); + if (ihl < 20 || ip_len < ihl + 20) return 0; + if (ip[9] != NS_IP_TCP) return 0; + if (ns_r32(ip + 12) != ns_tc.dst_ip) return 0; + const u8 *t = ip + ihl; + if (ns_r16(t + 0) != ns_tc.dst_port) return 0; + if (ns_r16(t + 2) != ns_tc.src_port) return 0; + + u32 seq = ns_r32(t + 4); + u32 ack = ns_r32(t + 8); + u8 doff = (u8)((t[12] >> 4) * 4); + u8 flags = t[13]; + u16 ip_total = ns_r16(ip + 2); + u32 data_len = (u32)ip_total - ihl - doff; + const u8 *data = t + doff; + + if (flags & NS_TCP_RST) { ns_tcp_got_rst = 1; ns_tc.state = NS_TCP_CLOSED; return 1; } + + if (ns_tc.state == NS_TCP_SYN_SENT) { + if ((flags & (NS_TCP_SYN | NS_TCP_ACK)) == (NS_TCP_SYN | NS_TCP_ACK)) { + ns_tc.rcv_nxt = seq + 1; + ns_tc.snd_nxt = ack; + ns_tc.state = NS_TCP_ESTAB; + ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt); + } + return 1; + } + + if (ns_tc.state == NS_TCP_ESTAB || ns_tc.state == NS_TCP_FIN_WAIT) { + if (data_len > 0) { + if (seq == ns_tc.rcv_nxt) { + u32 room = NS_TCP_RX_CAP - ns_tcp_rx_len; + u32 take = data_len < room ? data_len : room; + ns_memcpy(ns_tcp_rx + ns_tcp_rx_len, data, take); + ns_tcp_rx_len += take; + ns_tc.rcv_nxt += data_len; + ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt); + } else { + /* 乱序超前或重复段:重 ACK 当前期望序号 */ + ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt); + } + } + if (flags & NS_TCP_FIN) { + if (seq + data_len == ns_tc.rcv_nxt || data_len == 0) { + ns_tc.rcv_nxt = seq + data_len + 1; + ns_tcp_emit(NS_TCP_ACK, 0, 0, ns_tc.snd_nxt); + ns_tcp_got_fin = 1; + ns_tc.state = NS_TCP_CLOSED; + } + } + return 1; + } + return 1; +} + +/* 在超时窗口内收帧并喂给 TCP;返回 0=收到本连接段,1=超时。 */ +static inline int ns_tcp_wait(u32 timeout_ms) { + u64 deadline = ns_rdtsc() + ns_tsc_per_ms * timeout_ms; + while (!ns_deadline_reached(deadline)) { + u32 len = 0; + int rc = ns_net_rx_poll_fn_ptr(ns_dev_index, ns_rxframe, sizeof(ns_rxframe), &len); + if (rc == 0 && len >= 14) { + u16 et = ns_r16(ns_rxframe + 12); + if (et == NS_ETH_ARP) { + ns_arp_handle(ns_rxframe + 14, len - 14); + } else if (et == NS_ETH_IPV4) { + if (ns_tcp_process(ns_rxframe + 14, len - 14)) return 0; + } + } + __asm__ volatile("pause"); + } + return 1; +} + +/* 返回 0 成功(state=ESTAB),-1 ARP 失败,-2 超时/RST。 */ +static inline int ns_tcp_connect(u32 dst_ip, u16 dst_port, u32 timeout_ms) { + if (ns_route_mac(dst_ip, ns_tc.dst_mac) != 0) return -1; + ns_tc.dst_ip = dst_ip; + ns_tc.dst_port = dst_port; + ns_tc.src_port = (u16)(49152 + (ns_rdtsc() & 0x0FFF)); + u32 iss = (u32)ns_rdtsc(); + ns_tcp_rx_len = 0; + ns_tcp_got_fin = 0; + ns_tcp_got_rst = 0; + + for (int attempt = 0; attempt < 3; attempt++) { + ns_tc.state = NS_TCP_SYN_SENT; + if (ns_tcp_emit(NS_TCP_SYN, 0, 0, iss) != 0) continue; + u64 deadline = ns_rdtsc() + ns_tsc_per_ms * timeout_ms; + while (!ns_deadline_reached(deadline)) { + if (ns_tcp_got_rst) { ns_tc.state = NS_TCP_CLOSED; return -2; } + if (ns_tc.state == NS_TCP_ESTAB) return 0; + ns_tcp_wait(50); + } + } + ns_tc.state = NS_TCP_CLOSED; + return -2; +} + +/* 发送数据并等待 ACK(数据段可能在等待期间到达,已入缓冲)。 */ +static inline int ns_tcp_send(const u8 *data, u32 len) { + if (ns_tc.state != NS_TCP_ESTAB) return -1; + for (int attempt = 0; attempt < 3; attempt++) { + u32 expect = ns_tc.snd_nxt + len; + if (ns_tcp_emit((u8)(NS_TCP_PSH | NS_TCP_ACK), data, len, ns_tc.snd_nxt) != 0) return -2; + u64 deadline = ns_rdtsc() + ns_tsc_per_ms * 2000; + while (!ns_deadline_reached(deadline)) { + if (ns_tcp_got_rst) { ns_tc.state = NS_TCP_CLOSED; return -3; } + /* 检查 snd_una 是否被 ACK 推进:ns_tcp_process 不记录 una, + * 简单起见此处认为发出后收到任何本连接段即推进。 + * 对 HTTP/1.0 请求-响应模式足够(对端 ACK+数据一起回来)。 */ + if (ns_tcp_wait(100) == 0) { + ns_tc.snd_nxt = expect; + return 0; + } + } + } + return -4; +} + +/* 收至 FIN 或空闲超时;把累计数据拷到 buf(最多 cap)。返回长度或负值。 */ +static inline int ns_tcp_recv(u8 *buf, u32 cap, u32 idle_timeout_ms) { + u64 idle_deadline = ns_rdtsc() + ns_tsc_per_ms * idle_timeout_ms; + for (;;) { + if (ns_tcp_got_rst) return -1; + if (ns_tcp_got_fin) break; + if (ns_deadline_reached(idle_deadline)) break; + if (ns_tcp_wait(100) == 0) { + idle_deadline = ns_rdtsc() + ns_tsc_per_ms * idle_timeout_ms; + } + } + u32 n = ns_tcp_rx_len < cap ? ns_tcp_rx_len : cap; + if (buf && n) ns_memcpy(buf, ns_tcp_rx, n); + return (int)n; +} + +static inline void ns_tcp_close(void) { + if (ns_tc.state == NS_TCP_ESTAB) { + ns_tcp_emit((u8)(NS_TCP_FIN | NS_TCP_ACK), 0, 0, ns_tc.snd_nxt); + } + ns_tc.state = NS_TCP_CLOSED; +} + +#endif /* DESHAB_NET_STACK_H */ diff --git a/CODE/tools/ping/linker.ld b/CODE/tools/ping/linker.ld index 94dddf7..473e5fc 100644 --- a/CODE/tools/ping/linker.ld +++ b/CODE/tools/ping/linker.ld @@ -2,9 +2,10 @@ ENTRY(dsk_entry) PHDRS { - text PT_LOAD FLAGS(7); - rodata PT_LOAD FLAGS(4); - data PT_LOAD FLAGS(7); + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); + dynamic PT_DYNAMIC FLAGS(4); } SECTIONS @@ -12,5 +13,12 @@ SECTIONS . = 0x1000; .text : ALIGN(4K) { *(.text*) } :text .rodata : ALIGN(4K) { *(.rodata*) } :rodata - .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data + .data : ALIGN(4K) { + *(.data*) + *(.data.rel.ro*) + *(COMMON) + *(.bss*) + } :data + .rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data + .dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic } diff --git a/CODE/tools/ping/main.c b/CODE/tools/ping/main.c index f7fc219..d9907c2 100644 --- a/CODE/tools/ping/main.c +++ b/CODE/tools/ping/main.c @@ -1,11 +1,11 @@ -/* Deshab ping — 终端风格 ICMP Echo 工具 stub - * 命令行输入 IP 地址,发送 ICMP Echo Request。 - * 当前为 stub,提示需要 TCP/IP 栈支持。 +/* Deshab ping — 终端风格 ICMP Echo 工具 + * 命令行输入 IP 地址,发送 ICMP Echo Request(net_stack.h 共享协议栈)。 * Esc 键退出返回 DSK。 */ #include "../../firstInit/ascii_bitmaps.c" #include "../desktop_app.h" +#include "../net_stack.h" /* ---- 终端参数 ---- */ #define TERM_COLS 120 @@ -137,32 +137,84 @@ static int is_valid_ip(const char *s) { return dots == 3; } -/* ---- Ping 模拟 ---- */ -static void do_ping(const char *ip) { +/* ---- Ping 实现(net_stack.h) ---- */ +static int g_ns_status = 0; /* 0=未尝试, 1=就绪, <0=失败 */ + +static int ping_net_ready(void) { + if (g_ns_status == 1) return 1; + if (g_ns_status < 0) return 0; + int rc = ns_init((u64)g_ac.kernel_api); + g_ns_status = (rc == 0) ? 1 : -1; + if (rc != 0) { + term_puts_color(" [错误] 网络初始化失败,无可用网卡或 net API\n", DA_ERROR); + return 0; + } + return 1; +} + +static void do_ping(const char *ip_str) { + u32 ip; + if (ns_parse_ip(ip_str, &ip) != 0) { + term_puts_color(" [错误] 无法解析 IP\n", DA_ERROR); + return; + } + if (!ping_net_ready()) return; + + char ipstr[24]; + ns_fmt_ip(ip, ipstr); term_puts_color("PING ", DA_PROMPT_COLOR); - term_puts_color(ip, DA_ACCENT_LIGHT); - term_puts_color(" 56(84) bytes of data.\n", DA_TEXT_DIM); + term_puts_color(ipstr, DA_ACCENT_LIGHT); + term_puts_color(" 32 data bytes\n", DA_TEXT_DIM); - /* stub: 检查 kernel_api 是否有 net 接口 */ - if (!g_ac.kernel_api) { - term_puts_color(" [错误] 无内核 API\n", DA_ERROR); - return; + int sent = 0, recv = 0; + u32 sum = 0, min = 0xFFFFFFFFu, max = 0; + for (u16 seq = 1; seq <= 4; seq++) { + u32 rtt = 0; u8 ttl = 0; + int rc = ns_ping(ip, seq, 2000, &rtt, &ttl); + sent++; + if (rc == 0) { + recv++; + sum += rtt; + if (rtt < min) min = rtt; + if (rtt > max) max = rtt; + term_puts("reply from "); + term_puts(ipstr); + term_puts(": bytes=32 time="); + char nb[12]; ns_u32_dec(nb, rtt); + term_puts(nb); + term_puts("ms TTL="); + ns_u32_dec(nb, ttl); + term_puts(nb); + term_putc('\n'); + } else if (rc == -1) { + term_puts_color("ARP 解析失败,目标不可达\n", DA_ERROR); + break; + } else { + term_puts_color("request timed out\n", DA_TEXT_DIM); + } + redraw_all(); } - u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0); - if (!net_api) { - term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR); - term_puts_color(" 当前内核未实现 ICMP 协议栈。\n", DA_WARNING); - term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM); - term_putc('\n'); - term_puts_color("--- ", DA_TEXT_DIM); - term_puts_color(ip, DA_TEXT_DIM); - term_puts_color(" ping statistics ---\n", DA_TEXT_DIM); - term_puts_color(" 0 packets transmitted, 0 received, 100% packet loss\n", DA_ERROR); - return; + char nb[12]; + ns_u32_dec(nb, (u32)sent); + term_puts_color("--- ", DA_TEXT_DIM); + term_puts_color(ipstr, DA_TEXT_DIM); + term_puts_color(" ping statistics ---\n ", DA_TEXT_DIM); + term_puts(nb); + term_puts(" transmitted, "); + ns_u32_dec(nb, (u32)recv); + term_puts(nb); + term_puts(" received, "); + u32 loss = sent ? (u32)(sent - recv) * 100 / (u32)sent : 100; + ns_u32_dec(nb, loss); + term_puts(nb); + term_puts("% loss"); + if (recv > 0) { + term_puts(", min/avg/max = "); + ns_u32_dec(nb, min); term_puts(nb); term_puts("/"); + ns_u32_dec(nb, sum / (u32)recv); term_puts(nb); term_puts("/"); + ns_u32_dec(nb, max); term_puts(nb); term_puts(" ms"); } - - /* 如果有 net API,此处应实现真正的 ICMP Echo */ - term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING); + term_putc('\n'); } /* ---- 内置命令 ---- */ @@ -183,8 +235,8 @@ static void cmd_help(void) { static void cmd_clear(void) { term_clear(); } static void cmd_version(void) { - term_puts_color("Deshab ping v0.1 (stub)\n", DA_SUCCESS); - term_puts_color(" ICMP Echo 功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM); + term_puts_color("Deshab ping v1.0\n", DA_SUCCESS); + term_puts_color(" ICMP Echo via net_stack (e1000 + slirp)\n", DA_TEXT_DIM); } static void execute_command(const char *cmd) { @@ -246,10 +298,9 @@ void dsk_entry(const da_boot_context *ctx) { term_clear(); /* 启动 banner */ - term_puts_color("=== Deshab ping v0.1 (stub) ===\n", DA_ACCENT); + term_puts_color("=== Deshab ping v1.0 ===\n", DA_ACCENT); term_puts_color("ICMP Echo 请求工具\n", DA_SUCCESS); term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM); - term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING); term_putc('\n'); redraw_all(); diff --git a/ISO/run_qemu.bat b/ISO/run_qemu.bat index 007935b..bb7c5dc 100644 --- a/ISO/run_qemu.bat +++ b/ISO/run_qemu.bat @@ -1,4 +1,4 @@ -@echo off +@echo off setlocal set "SCRIPT_DIR=%~dp0" diff --git a/Logo.png b/Logo.png new file mode 100644 index 0000000..f13ea0e Binary files /dev/null and b/Logo.png differ diff --git a/SYSTEM/boot/utsm.elf b/SYSTEM/boot/utsm.elf index 49b8d3c..fad5b5d 100644 Binary files a/SYSTEM/boot/utsm.elf and b/SYSTEM/boot/utsm.elf differ diff --git a/SYSTEM/driver/block/ahci.drv b/SYSTEM/driver/block/ahci.drv index 4db0c32..ab02bc7 100644 Binary files a/SYSTEM/driver/block/ahci.drv and b/SYSTEM/driver/block/ahci.drv differ diff --git a/SYSTEM/driver/console/console_fb.drv b/SYSTEM/driver/console/console_fb.drv index 51cf902..b515b42 100644 Binary files a/SYSTEM/driver/console/console_fb.drv and b/SYSTEM/driver/console/console_fb.drv differ diff --git a/SYSTEM/driver/fs/fat32.drv b/SYSTEM/driver/fs/fat32.drv index be17425..b9574a0 100644 Binary files a/SYSTEM/driver/fs/fat32.drv and b/SYSTEM/driver/fs/fat32.drv differ diff --git a/SYSTEM/driver/net/ath9k.drv b/SYSTEM/driver/net/ath9k.drv index b183006..b07bec7 100644 Binary files a/SYSTEM/driver/net/ath9k.drv and b/SYSTEM/driver/net/ath9k.drv differ diff --git a/SYSTEM/driver/net/e1000.drv b/SYSTEM/driver/net/e1000.drv index d507d64..0fe2ab8 100644 Binary files a/SYSTEM/driver/net/e1000.drv and b/SYSTEM/driver/net/e1000.drv differ diff --git a/SYSTEM/driver/net/virtio_net.drv b/SYSTEM/driver/net/virtio_net.drv index 1d8871b..0e228d9 100644 Binary files a/SYSTEM/driver/net/virtio_net.drv and b/SYSTEM/driver/net/virtio_net.drv differ diff --git a/SYSTEM/boot/limine.conf b/SYSTEM/limine/limine.conf similarity index 100% rename from SYSTEM/boot/limine.conf rename to SYSTEM/limine/limine.conf diff --git a/SYSTEM/system/deshab64/deshab.elf b/SYSTEM/system/deshab64/deshab.elf index f69adaf..d20c7db 100644 Binary files a/SYSTEM/system/deshab64/deshab.elf and b/SYSTEM/system/deshab64/deshab.elf differ diff --git a/SYSTEM/system/deshab64/desktop.elf b/SYSTEM/system/deshab64/desktop.elf index e4ccfa5..56d3a31 100644 Binary files a/SYSTEM/system/deshab64/desktop.elf and b/SYSTEM/system/deshab64/desktop.elf differ diff --git a/SYSTEM/system/deshab64/mouse/mouseInit.elf b/SYSTEM/system/deshab64/mouse/mouseInit.elf index 145385a..9ba969b 100644 Binary files a/SYSTEM/system/deshab64/mouse/mouseInit.elf and b/SYSTEM/system/deshab64/mouse/mouseInit.elf differ diff --git a/SYSTEM/system/deshab64/network/netman.elf b/SYSTEM/system/deshab64/network/netman.elf index 62d1563..04bbd05 100644 Binary files a/SYSTEM/system/deshab64/network/netman.elf and b/SYSTEM/system/deshab64/network/netman.elf differ diff --git a/SYSTEM/system/deshab64/shell.elf b/SYSTEM/system/deshab64/shell.elf index 4b6d814..e0d0856 100644 Binary files a/SYSTEM/system/deshab64/shell.elf and b/SYSTEM/system/deshab64/shell.elf differ diff --git a/SYSTEM/system/deshab64/tools/bash.elf b/SYSTEM/system/deshab64/tools/bash.elf deleted file mode 100644 index 341d4c6..0000000 Binary files a/SYSTEM/system/deshab64/tools/bash.elf and /dev/null differ diff --git a/SYSTEM/system/deshab64/tools/browser.elf b/SYSTEM/system/deshab64/tools/browser.elf index 954ee56..ba1e3d5 100644 Binary files a/SYSTEM/system/deshab64/tools/browser.elf and b/SYSTEM/system/deshab64/tools/browser.elf differ diff --git a/SYSTEM/system/deshab64/tools/curl.elf b/SYSTEM/system/deshab64/tools/curl.elf index ec3da1c..0606343 100644 Binary files a/SYSTEM/system/deshab64/tools/curl.elf and b/SYSTEM/system/deshab64/tools/curl.elf differ diff --git a/SYSTEM/system/deshab64/tools/editor.elf b/SYSTEM/system/deshab64/tools/editor.elf index 2ab35c8..9cde1c8 100644 Binary files a/SYSTEM/system/deshab64/tools/editor.elf and b/SYSTEM/system/deshab64/tools/editor.elf differ diff --git a/SYSTEM/system/deshab64/tools/fileman.elf b/SYSTEM/system/deshab64/tools/fileman.elf index ea25f32..218dbab 100644 Binary files a/SYSTEM/system/deshab64/tools/fileman.elf and b/SYSTEM/system/deshab64/tools/fileman.elf differ diff --git a/SYSTEM/system/deshab64/tools/ping.elf b/SYSTEM/system/deshab64/tools/ping.elf index dc6f2f6..58159ff 100644 Binary files a/SYSTEM/system/deshab64/tools/ping.elf and b/SYSTEM/system/deshab64/tools/ping.elf differ diff --git a/SYSTEM/system/user/deshab/user.conf b/SYSTEM/system/user/deshab/user.conf index 623296b..527c47b 100644 --- a/SYSTEM/system/user/deshab/user.conf +++ b/SYSTEM/system/user/deshab/user.conf @@ -1,7 +1,14 @@ -# Deshab user configuration placeholder -# Runtime write is pending FAT32 write support. -version=1 -computerName= -username= -passwordSha256= -configEncrypted=0 +DESHAB_USERCONF_V1 +computer=DESHAB-PC +username=admin +passwordSha256=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 +locale=zh-CN +region=CN +timezone=Asia/Shanghai +keyboard=us-qwerty +theme=dark +network.mode=disabled +network.ssid= +network.password= +network.ip=dhcp +network.dns=auto diff --git a/SYSTEM/system/user/use/FirstInit.elf b/SYSTEM/system/user/use/FirstInit.elf index eb34a84..888cbba 100644 Binary files a/SYSTEM/system/user/use/FirstInit.elf and b/SYSTEM/system/user/use/FirstInit.elf differ diff --git a/SYSTEM/system/user/use/firstInit.txt b/SYSTEM/system/user/use/firstInit.txt index 1874828..879cdec 100644 --- a/SYSTEM/system/user/use/firstInit.txt +++ b/SYSTEM/system/user/use/firstInit.txt @@ -1 +1,2 @@ 0 +1 \ No newline at end of file diff --git a/SYSTEM/system/user/use/login.elf b/SYSTEM/system/user/use/login.elf new file mode 100644 index 0000000..92d708c Binary files /dev/null and b/SYSTEM/system/user/use/login.elf differ diff --git a/build.ps1 b/build.ps1 index 16fe132..477c911 100644 --- a/build.ps1 +++ b/build.ps1 @@ -93,12 +93,12 @@ function Get-Crc32([byte[]]$Data) { } function Test-ShortFatName([string]$Name) { - if ($Name -match '[a-z]') { return $false } + if ($Name -cmatch '[a-z]') { return $false } $parts = $Name.Split('.') if ($parts.Count -gt 2) { return $false } if ($parts[0].Length -lt 1 -or $parts[0].Length -gt 8) { return $false } if ($parts.Count -eq 2 -and $parts[1].Length -gt 3) { return $false } - return $Name -match '^[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+(\.[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+)?$' + return ($Name -cmatch '^[A-Z0-9_]+$') -or ($Name -cmatch '^[A-Z0-9_]+\.[A-Z0-9_]+$') } function New-ShortFatName([string]$LongName, $Used) { @@ -513,6 +513,24 @@ if (-not (Test-Path $FirstInitOutput)) { } Write-Host "[build] FirstInit Output: $FirstInitOutput" +$LoginDir = Join-Path $Root 'CODE\login' +$LoginOutput = Join-Path $SystemDir 'system\user\use\login.elf' + +Write-Host '[build] Building login.elf...' +try { + & $make -C "$LoginDir" -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make login failed with exit code $LASTEXITCODE" + } +} +finally { +} + +if (-not (Test-Path $LoginOutput)) { + throw "login build did not produce $LoginOutput" +} +Write-Host "[build] login Output: $LoginOutput" + $MouseDir = Join-Path $Root 'CODE\mouse' $MouseOutput = Join-Path $SystemDir 'system\deshab64\mouse\mouseInit.elf' @@ -589,7 +607,7 @@ Write-Host "[build] desktop Output: $DesktopOutput" $ToolsDir = Join-Path $Root 'CODE\tools' $ToolsOutDir = Join-Path $SystemDir 'system\deshab64\tools' -$toolApps = @('bash', 'editor', 'fileman', 'browser', 'curl', 'ping') +$toolApps = @('editor', 'fileman', 'browser', 'curl', 'ping') foreach ($tool in $toolApps) { $toolDir = Join-Path $ToolsDir $tool @@ -607,6 +625,9 @@ foreach ($tool in $toolApps) { } } +Write-Host '[build] Building DKM framebuffer driver...' +Build-DkmDriver (Join-Path $Root 'CODE\DKM\console_fb\console_fb.c') (Join-Path $SystemDir 'driver\console\console_fb.drv') + Write-Host '[build] Building DKM network drivers...' Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv') Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv')