feat: Phase 1 网络端到端打通 + FirstInit/Login/DevMode 完善

- 用户态共享网络协议栈 CODE/tools/net_stack.h(eth/ARP/IPv4/ICMP/UDP-DNS/TCP static inline,轮询模型)
- shell 内建 ping/curl/run 命令;ping.elf/curl.elf 对齐 net_stack.h 真实协议栈
- shell dev_mode 串口镜像(g_serial_mirror)便于自动化验证
- DSK 启动流程:firstInit.txt 双行格式(firstInit + dev_mode),dev_mode=1 触发 shell 自动回归测试
- FirstInit 完成→持久化 USER.CONF + 翻转 firstInit 0->1(保留 dev_mode)
- firstInit=1 时 DSK 加载 login.elf 进行用户选择 + 密码校验后再进 desktop
- 新增 CODE/login/ 登录应用、CODE/DKM/dkm_shared.h、Logo.png
- ahci/e1000/virtio_net/fat32/console_fb 驱动更新;utsm serial/block/dsk_loader 调整
- build.ps1 打包 ping.elf/curl.elf 进 SATA FAT32 根目录
- 移除 CODE/tools/bash(与 shell.elf 重复)
This commit is contained in:
xyou
2026-07-26 21:07:36 +08:00
parent 48dcb5568e
commit efa626ed17
83 changed files with 4993 additions and 1785 deletions
+110 -138
View File
@@ -4,100 +4,7 @@
* and ports so later block I/O can be added safely.
*/
#include <stdint.h>
#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) {
+38 -151
View File
@@ -17,105 +17,7 @@
* 部分高级特性(ANI/MRR/AGC 调优)留待硬件验证后增量完善。
*/
#include <stdint.h>
#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);
+10 -162
View File
@@ -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 <stdint.h>
@@ -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;
+331
View File
@@ -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 <stdint.h>
/* ---- 基本类型 (与 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 */
+26 -119
View File
@@ -3,98 +3,7 @@
* Finds e1000 via PCI, maps MMIO, reads MAC address.
*/
#include <stdint.h>
#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");
+2
View File
@@ -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 {
+44 -161
View File
@@ -4,110 +4,23 @@
* it does not negotiate features, create virtqueues, or transmit packets.
*/
#include <stdint.h>
#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 {
+39 -3
View File
@@ -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);
Binary file not shown.
Binary file not shown.
+3
View File
@@ -1,6 +1,7 @@
#include <utsm/utsm.h>
#include <utsm/segment.h>
#include <utsm/pckc.h>
#include <utsm/process.h>
#include <utsm/log.h>
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) {
Binary file not shown.
+6 -1
View File
@@ -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 {
Binary file not shown.
Binary file not shown.
+4
View File
@@ -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);
+9 -8
View File
@@ -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 \
}
/* ===================================================================
+16 -1
View File
@@ -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) {
Binary file not shown.
+2 -1
View File
@@ -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();
Binary file not shown.
Binary file not shown.
+3
View File
@@ -25,6 +25,9 @@ SECTIONS
.data : ALIGN(4K) {
*(.data*)
*(COMMON)
} :data
.bss : ALIGN(4K) {
*(.bss*)
} :data
}
+12 -3
View File
@@ -1,16 +1,25 @@
#include <utsm/utsm.h>
#include <utsm/log.h>
#include <utsm/segment.h>
#include <utsm/arena.h>
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;
Binary file not shown.
+12 -4
View File
@@ -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
}
+173 -45
View File
@@ -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");
+4 -1
View File
@@ -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
+9 -12
View File
@@ -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
}
+518 -149
View File
@@ -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<SPR; y0++) {
i64 ry = by + y0;
if (ry < 0) ry = 0;
if ((u64)ry >= 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;x0++) ln[x0]=row_bg[y0];
}
{
i64 ro=r+thk, ri=r-thk;
for (i64 dq=0; dq<4; dq++) {
i64 sa=base+dq*90, sw2=82; u32 clr=arc_colors[dq];
for(i64 dy=-ro-1;dy<=ro+1;dy++){i64 yy=scy+dy; if(yy<0||yy>=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<ri*ri-2||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<xr){xr=yr;yr=(yr+vv/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<SPR;sr++){i64 sy=by+sr; if(sy<0||(u64)sy>=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<SPR;sc++){i64 sx=bx+sc; if(sx<0||(u64)sx>=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");
}
BIN
View File
Binary file not shown.
+12 -4
View File
@@ -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
}
+559 -143
View File
@@ -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<ASCII_H; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<ASCII_W; c++) {
u32 a = g[r * ASCII_W + c];
if (a == 0) continue;
u32 eff_a = ((u32)a * global_alpha) >> 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<h; r++) {
@@ -136,7 +208,9 @@ static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y,
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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; r<WIFI_SCAN_COUNT; r++) draw_wifi_row(fb, card_x, card_y, net, r, active, fg);
fill_rounded_rect(fb, card_x + 470, card_y + 120, 390, 260, 12, INPUT_BG);
stroke_rounded_rect(fb, card_x + 470, card_y + 120, 390, 260, 12, 1, 0xFFD2E3F0);
fb_text(fb, "Highlighted network", card_x + 494, card_y + 148, 0xFF7F97AC, INPUT_BG);
int preview = active < WIFI_SCAN_COUNT ? active : net->selected;
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<WIFI_SCAN_COUNT; r++) {
i64 ry = card_y + 132 + (i64)r * 64;
if (*mx >= 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;i<p;i++) conf[i]^=hash[i&31];
logl("[FirstInit] user.conf encrypted in memory (disk write pending)");
g_conf_size = (u32)p;
logl("[FirstInit] user.conf built and encrypted");
}
__attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
logl("[FirstInit] boot");
logl("[FirstInit] calibrating TSC (实机 CPU 频率计算)");
tsc_calibrate_fi();
if (!ctx || ctx->magic != 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 */
Binary file not shown.
+90 -90
View File
@@ -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;
+2 -2
View File
@@ -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),
@@ -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
+24
View File
@@ -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
}
+565
View File
@@ -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<ASCII_H; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<ASCII_W; c++) {
u32 a = g[r * ASCII_W + c];
if (a == 0) continue;
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 color = bg_at_y(yy);
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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<len && i<55;i++) block[i]=(u8)msg[i];
block[len]=0x80; u64 bit=len*8;
for(int i=0;i<8;i++) block[63-i]=(u8)(bit>>(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=<pc>
* username=<user>
* 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);
}
}
}
+12 -4
View File
@@ -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
}
+12 -4
View File
@@ -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
}
+43 -1
View File
@@ -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;i<loops;i++) { __asm__ volatile("pause"); }
/* 兼容旧调用: 粗略转换为 ms (实机以 TSC 为准) */
if (g_tsc_per_ms) {
delay_ms_nm(loops / 10000 + 1);
} else {
for (volatile u32 i=0;i<loops;i++) { __asm__ volatile("pause"); }
}
}
/* 构造并发送一个 DHCP 报文。type: 1=DISCOVER 3=REQUEST */
@@ -617,6 +657,8 @@ __attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
logl("[netman] boot");
if (!ctx || ctx->magic != 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);
Binary file not shown.
+12 -4
View File
@@ -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
}
+1276 -14
View File
@@ -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 <text> 显示文本\n");
term_puts(" version 显示系统版本\n");
term_puts(" echo <text> 显示文本(-n 不换行, > file 重定向写盘)\n");
term_puts(" ls / dir 列出根目录文件\n");
term_puts(" cat <file> 显示文件内容\n");
term_puts(" cp <s> <d> 复制文件\n");
term_puts(" mv <s> <d> 移动/重命名文件\n");
term_puts(" rm <file> 删除文件\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 <url> HTTP GET 请求(http://host[:port]/path)\n");
term_puts(" run <NAME.ELF> 运行根目录下的工具程序\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 <NAME.ELF>\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; }
BIN
View File
Binary file not shown.
-16
View File
@@ -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
}
-456
View File
@@ -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 <text> 显示文本\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 <file> 显示文件内容\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();
}
}
+12 -4
View File
@@ -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
}
+12 -4
View File
@@ -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
}
+112 -21
View File
@@ -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();
+12 -4
View File
@@ -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
}
+12 -4
View File
@@ -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
}
+716
View File
@@ -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 */
+12 -4
View File
@@ -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
}
+80 -29
View File
@@ -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();