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:
@@ -14,13 +14,13 @@ CODE/dsk -> SYSTEM/system/deshab64/deshab.elf
|
|||||||
DSK 主内核(`deshab.elf`)当前已实现:
|
DSK 主内核(`deshab.elf`)当前已实现:
|
||||||
- 内嵌 FAT32 只读解析器(256 扇区批量读取 + 内存内文件查找)
|
- 内嵌 FAT32 只读解析器(256 扇区批量读取 + 内存内文件查找)
|
||||||
- PIE ELF loader(PT_LOAD + PT_DYNAMIC + R_X86_64_RELATIVE 重定位)
|
- PIE ELF loader(PT_LOAD + PT_DYNAMIC + R_X86_64_RELATIVE 重定位)
|
||||||
- 旋转加载动画(双缓冲精灵、顺时针、comet-tail 渐变弧)
|
- 静态启动 Logo(嵌入 Logo.png RGBA 数据,居中 alpha 混合绘制于渐变背景上,取代原旋转加载动画)
|
||||||
- 首次启动检测:读取 `firstInit.txt`,值为 `0` 则触发首次启动流程
|
- 首次启动检测:读取 `firstInit.txt`,值为 `0` 则触发首次启动流程
|
||||||
- 系统初始化调度:DSK 直接加载并调用 `mouseInit.elf`(PS/2 鼠标安全初始化)
|
- 系统初始化调度:DSK 直接加载并调用 `mouseInit.elf`(PS/2 鼠标安全初始化)
|
||||||
- 用户设置向导:DSK 加载并跳转 `FirstInit.elf`(圆角卡片 UI、键盘输入、SHA256 密码、加密配置缓冲)
|
- 用户设置向导:DSK 加载并跳转 `FirstInit.elf`(圆角卡片 UI、键盘输入、SHA256 密码、加密配置缓冲)
|
||||||
|
|
||||||
FirstInit(`CODE/firstInit`)只负责用户级设置向导:
|
FirstInit(`CODE/firstInit`)只负责用户级设置向导:
|
||||||
- 淡蓝背景 → 旋转环淡出 → "欢迎使用 Deshab" → 设置提示 → 账户设置卡片
|
- 淡蓝背景 → "欢迎使用 Deshab"(中英双语,英文小字副标题)→ 设置提示 → 账户设置卡片
|
||||||
- PS/2 键盘轮询输入:计算机名、用户名、密码
|
- PS/2 键盘轮询输入:计算机名、用户名、密码
|
||||||
- SHA256 密码摘要 + XOR 加密配置缓冲(user.conf 格式已定义,写盘待 FAT32 write API)
|
- SHA256 密码摘要 + XOR 加密配置缓冲(user.conf 格式已定义,写盘待 FAT32 write API)
|
||||||
- 18px 黑体中文位图(预渲染嵌入 ELF)
|
- 18px 黑体中文位图(预渲染嵌入 ELF)
|
||||||
@@ -126,7 +126,7 @@ DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解
|
|||||||
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC MMIO 只读寄存器验证;当前保留 PIC IRQ 路由
|
- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC MMIO 只读寄存器验证;当前保留 PIC IRQ 路由
|
||||||
- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举
|
- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举
|
||||||
- **pci** (stage0): PCI config space 扫描, 设备枚举
|
- **pci** (stage0): PCI config space 扫描, 设备枚举
|
||||||
- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景 + 居中四色圆弧加载环(纯整数运算,无浮点依赖)
|
- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景清屏(原四色圆弧加载环已移除,启动画面改由 DSK 静态 Logo 接管)
|
||||||
- **ahci** (stage1): PCI AHCI `8086:2922` 探测, BAR5/ABAR HHDM MMIO 映射, HBA CAP/GHC/PI/VS 与 port 枚举;已接入 `kernel_api.dma`,支持 SATA disk IDENTIFY 与 LBA0 READ 最小 DMA 路径;可向 `kernel_api.block` 注册 `ahci0` block provider;默认无 SATA 盘/ATAPI 场景会跳过命令
|
- **ahci** (stage1): PCI AHCI `8086:2922` 探测, BAR5/ABAR HHDM MMIO 映射, HBA CAP/GHC/PI/VS 与 port 枚举;已接入 `kernel_api.dma`,支持 SATA disk IDENTIFY 与 LBA0 READ 最小 DMA 路径;可向 `kernel_api.block` 注册 `ahci0` block provider;默认无 SATA 盘/ATAPI 场景会跳过命令
|
||||||
- **nvme** (stage1): PCI NVMe `1b36:0010` 探测, BAR0/BAR1 解析, IRQ line 读取;当前 QEMU BAR0 位于 4G 以上,MMIO register 读取等待高位 PCI MMIO 映射能力
|
- **nvme** (stage1): PCI NVMe `1b36:0010` 探测, BAR0/BAR1 解析, IRQ line 读取;当前 QEMU BAR0 位于 4G 以上,MMIO register 读取等待高位 PCI MMIO 映射能力
|
||||||
- **bootfs** (stage1): Limine boot module 内存文件系统
|
- **bootfs** (stage1): Limine boot module 内存文件系统
|
||||||
|
|||||||
+110
-138
@@ -4,100 +4,7 @@
|
|||||||
* and ports so later block I/O can be added safely.
|
* and ports so later block I/O can be added safely.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "../dkm_shared.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;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *const g_depends[] = { "pci", "irq" };
|
static const char *const g_depends[] = { "pci", "irq" };
|
||||||
static const char *const g_provides[] = { "block" };
|
static const char *const g_provides[] = { "block" };
|
||||||
@@ -124,21 +31,6 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
.reserved1 = 0
|
.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_CLASS_STORAGE 0x01
|
||||||
#define AHCI_SUBCLASS_SATA 0x06
|
#define AHCI_SUBCLASS_SATA 0x06
|
||||||
#define AHCI_PROGIF_AHCI 0x01
|
#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_ST (1u << 0)
|
||||||
#define AHCI_CMD_FRE (1u << 4)
|
#define AHCI_CMD_FRE (1u << 4)
|
||||||
|
#define AHCI_CMD_W (1u << 6)
|
||||||
#define AHCI_CMD_FR (1u << 14)
|
#define AHCI_CMD_FR (1u << 14)
|
||||||
#define AHCI_CMD_CR (1u << 15)
|
#define AHCI_CMD_CR (1u << 15)
|
||||||
#define AHCI_CMD_ICC_ACTIVE (1u << 28)
|
#define AHCI_CMD_ICC_ACTIVE (1u << 28)
|
||||||
|
|
||||||
#define ATA_CMD_IDENTIFY 0xEC
|
#define ATA_CMD_IDENTIFY 0xEC
|
||||||
#define ATA_CMD_IDENTIFY_PACKET 0xA1
|
#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 {
|
typedef struct __attribute__((packed)) ahci_cmd_header {
|
||||||
u16 flags;
|
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_cmd_table;
|
||||||
static struct dkm_dma_buffer g_data;
|
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) {
|
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||||
u32 addr = (1u << 31)
|
return dkm_pci_read(bus, dev, func, reg);
|
||||||
| ((u32)bus << 16)
|
|
||||||
| ((u32)dev << 11)
|
|
||||||
| ((u32)func << 8)
|
|
||||||
| ((u32)reg & 0xFC);
|
|
||||||
outl(PCI_ADDR, addr);
|
|
||||||
return inl(PCI_DATA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
||||||
u32 addr = (1u << 31)
|
dkm_pci_write(bus, dev, func, reg, value);
|
||||||
| ((u32)bus << 16)
|
|
||||||
| ((u32)dev << 11)
|
|
||||||
| ((u32)func << 8)
|
|
||||||
| ((u32)reg & 0xFC);
|
|
||||||
outl(PCI_ADDR, addr);
|
|
||||||
outl(PCI_DATA, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_hex(const char *prefix, u64 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];
|
u32 cmd = g_abar[(base + PxCMD) / 4];
|
||||||
cmd &= ~AHCI_CMD_ST;
|
cmd &= ~AHCI_CMD_ST;
|
||||||
g_abar[(base + PxCMD) / 4] = cmd;
|
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];
|
cmd = g_abar[(base + PxCMD) / 4];
|
||||||
if (!(cmd & AHCI_CMD_CR)) break;
|
if (!(cmd & AHCI_CMD_CR)) break;
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
cmd &= ~AHCI_CMD_FRE;
|
cmd &= ~AHCI_CMD_FRE;
|
||||||
g_abar[(base + PxCMD) / 4] = cmd;
|
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];
|
cmd = g_abar[(base + PxCMD) / 4];
|
||||||
if (!(cmd & AHCI_CMD_FR)) return 0;
|
if (!(cmd & AHCI_CMD_FR)) return 0;
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -368,7 +247,23 @@ static void ahci_fill_read_fis(u8 *fis, u64 lba, u32 count) {
|
|||||||
ahci_zero(fis, 64);
|
ahci_zero(fis, 64);
|
||||||
fis[0] = 0x27;
|
fis[0] = 0x27;
|
||||||
fis[1] = 1u << 7;
|
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[4] = (u8)(lba & 0xff);
|
||||||
fis[5] = (u8)((lba >> 8) & 0xff);
|
fis[5] = (u8)((lba >> 8) & 0xff);
|
||||||
fis[6] = (u8)((lba >> 16) & 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;
|
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) {
|
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;
|
(void)ctx;
|
||||||
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
|
if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1;
|
||||||
/* ensure port is in ST+FRE state */
|
/* 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) {
|
while (count > 0) {
|
||||||
u32 chunk = count > 8 ? 8 : count;
|
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;
|
if (status != 0) return status;
|
||||||
lba += chunk;
|
lba += chunk;
|
||||||
count -= chunk;
|
count -= chunk;
|
||||||
buffer = (void *)((u8 *)buffer + (chunk * 512));
|
buffer = (const void *)((const u8 *)buffer + (chunk * 512));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -536,6 +505,8 @@ __attribute__((visibility("default")))
|
|||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
(void)handle;
|
(void)handle;
|
||||||
|
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
|
||||||
|
dkm_tsc_calibrate();
|
||||||
|
|
||||||
if (!api || !api->log) return -1;
|
if (!api || !api->log) return -1;
|
||||||
g_log = api->log;
|
g_log = api->log;
|
||||||
@@ -628,6 +599,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
desc.sector_count = 0;
|
desc.sector_count = 0;
|
||||||
desc.ctx = 0;
|
desc.ctx = 0;
|
||||||
desc.read = ahci_block_read;
|
desc.read = ahci_block_read;
|
||||||
|
desc.write = ahci_block_write;
|
||||||
int index = api->block->register_device(&desc);
|
int index = api->block->register_device(&desc);
|
||||||
log_hex("[ahci] block provider index=", (u64)(i64)index);
|
log_hex("[ahci] block provider index=", (u64)(i64)index);
|
||||||
} else if (identified) {
|
} else if (identified) {
|
||||||
|
|||||||
+38
-151
@@ -17,105 +17,7 @@
|
|||||||
* 部分高级特性(ANI/MRR/AGC 调优)留待硬件验证后增量完善。
|
* 部分高级特性(ANI/MRR/AGC 调优)留待硬件验证后增量完善。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "../dkm_shared.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;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *const g_depends[] = {"pci","irq"};
|
static const char *const g_depends[] = {"pci","irq"};
|
||||||
static const char *const g_provides[] = {"netdev"};
|
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,
|
.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 关键子集) ---- */
|
/* ---- ath9k 寄存器(AR9280/9285/9287 PCIe MAC,Linux ath9k reg.h 关键子集) ---- */
|
||||||
#define AR_RC 0x4000u
|
#define AR_RC 0x4000u
|
||||||
#define AR_RC_MAC 0x00000001u
|
#define AR_RC_MAC 0x00000001u
|
||||||
@@ -285,20 +174,20 @@ static u32 g_scan_count;
|
|||||||
static int g_scanning;
|
static int g_scanning;
|
||||||
|
|
||||||
/* ---- 串口日志(参考 e1000 raw_log) ---- */
|
/* ---- 串口日志(参考 e1000 raw_log) ---- */
|
||||||
static __inline__ void outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p):"memory"); }
|
#define COM1 0x3F8
|
||||||
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"); }
|
/* 串口发送等待 thin wrapper — 调用 dkm 共享实现 */
|
||||||
static __inline__ u32 inl(u16 p) { u32 v; __asm__ volatile("inl %1,%0":"=a"(v):"Nd"(p):"memory"); return v; }
|
static void serial_wait_tx(void) { dkm_serial_wait_tx(COM1); }
|
||||||
|
|
||||||
static void raw_log(const char *s) {
|
static void raw_log(const char *s) {
|
||||||
while (*s) {
|
while (*s) {
|
||||||
char c = *s++;
|
char c = *s++;
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break;
|
serial_wait_tx();
|
||||||
outb(0x3F8, '\r');
|
dkm_outb(COM1, '\r');
|
||||||
}
|
}
|
||||||
for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break;
|
serial_wait_tx();
|
||||||
outb(0x3F8, (u8)c);
|
dkm_outb(COM1, (u8)c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,26 +196,19 @@ static void raw_hex(const char *prefix, u64 v) {
|
|||||||
raw_log(prefix);
|
raw_log(prefix);
|
||||||
raw_log("0x");
|
raw_log("0x");
|
||||||
for (int i=15; i>=0; i--) {
|
for (int i=15; i>=0; i--) {
|
||||||
for (u32 wait=0; wait<100000; wait++) if (inb(0x3F8+5)&0x20) break;
|
serial_wait_tx();
|
||||||
outb(0x3F8, (u8)h[(v >> (i * 4)) & 0xf]);
|
dkm_outb(COM1, (u8)h[(v >> (i * 4)) & 0xf]);
|
||||||
}
|
}
|
||||||
raw_log("\n");
|
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 配置空间 ---- */
|
/* ---- PCI 配置空间 ---- */
|
||||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
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);
|
return dkm_pci_read(bus, dev, func, reg);
|
||||||
outl(PCI_ADDR,a); return inl(PCI_DATA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) {
|
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);
|
dkm_pci_write(bus, dev, func, reg, v);
|
||||||
outl(PCI_ADDR,a); outl(PCI_DATA,v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ath9k PCI 设备 ID 表(vendor 0x168c Atheros) */
|
/* 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 */
|
/* AR5416_EEPROM_CMD: bit0=READ, bit1=BUSY, bit2=RESET */
|
||||||
/* 启动 read:写 cmd=READ | addr<<3 */
|
/* 启动 read:写 cmd=READ | addr<<3 */
|
||||||
ath9k_reg_write(AR_EEPROM_CMD, AR_EEPROM_CMD_RESET);
|
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);
|
u32 cmd = AR_EEPROM_CMD_READ | ((u32)(addr & 0x3FF) << 3);
|
||||||
ath9k_reg_write(AR_EEPROM_CMD, cmd);
|
ath9k_reg_write(AR_EEPROM_CMD, cmd);
|
||||||
/* 等待 RDBUSY 清除 */
|
/* 等待 RDBUSY 清除 (实机: 100ms TSC 超时) */
|
||||||
for (u32 i = 0; i < 100000; i++) {
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
u32 sts = ath9k_reg_read(AR_EEPROM_STS);
|
u32 sts = ath9k_reg_read(AR_EEPROM_STS);
|
||||||
if (!(sts & AR_EEPROM_STS_RDBUSY)) {
|
if (!(sts & AR_EEPROM_STS_RDBUSY)) {
|
||||||
if (sts & AR_EEPROM_STS_VALID) {
|
if (sts & AR_EEPROM_STS_VALID) {
|
||||||
@@ -415,7 +298,7 @@ static u16 ath9k_eeprom_read(u16 addr) {
|
|||||||
}
|
}
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
}
|
}
|
||||||
delay_us(1);
|
dkm_delay_us(1);
|
||||||
}
|
}
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
}
|
}
|
||||||
@@ -425,27 +308,29 @@ static int ath9k_reset(void) {
|
|||||||
raw_log("[ath9k.raw] reset sequence\n");
|
raw_log("[ath9k.raw] reset sequence\n");
|
||||||
/* 1. 强制唤醒(Power-On) */
|
/* 1. 强制唤醒(Power-On) */
|
||||||
ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT);
|
ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT);
|
||||||
delay_us(50);
|
dkm_delay_us(50);
|
||||||
/* 2. 等待 RTC 进入 ON 状态 */
|
/* 2. 等待 RTC 进入 ON 状态 (实机: 100ms TSC 超时) */
|
||||||
for (u32 i = 0; i < 100000; i++) {
|
u64 deadline = dkm_rdtsc() + dkm_tsc_per_ms * 100;
|
||||||
|
while (dkm_rdtsc() < deadline) {
|
||||||
u32 s = ath9k_reg_read(AR_RTC_STATUS);
|
u32 s = ath9k_reg_read(AR_RTC_STATUS);
|
||||||
if ((s & 0x3) == AR_RTC_STATUS_ON) break;
|
if ((s & 0x3) == AR_RTC_STATUS_ON) break;
|
||||||
delay_us(1);
|
dkm_delay_us(1);
|
||||||
}
|
}
|
||||||
/* 3. 全 chip 复位 */
|
/* 3. 全 chip 复位 */
|
||||||
ath9k_reg_write(AR_RC, AR_RC_MAC | AR_RC_BB | AR_RC_RPCU);
|
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);
|
ath9k_reg_write(AR_RC, 0);
|
||||||
delay_us(100);
|
dkm_delay_us(100);
|
||||||
/* 4. 重新唤醒 */
|
/* 4. 重新唤醒 */
|
||||||
ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT);
|
ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT);
|
||||||
delay_us(50);
|
dkm_delay_us(50);
|
||||||
/* 5. 等待 RTC ON */
|
/* 5. 等待 RTC ON (实机: 100ms TSC 超时) */
|
||||||
u32 ok = 0;
|
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);
|
u32 s = ath9k_reg_read(AR_RTC_STATUS);
|
||||||
if ((s & 0x3) == AR_RTC_STATUS_ON) { ok = 1; break; }
|
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; }
|
if (!ok) { raw_log("[ath9k.raw] reset: RTC not ON\n"); return -1; }
|
||||||
raw_log("[ath9k.raw] reset: RTC ON\n");
|
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");
|
raw_log("[ath9k.raw] hw_init minimal\n");
|
||||||
/* 关闭 RX/DMA,清 STA_ID1 */
|
/* 关闭 RX/DMA,清 STA_ID1 */
|
||||||
ath9k_reg_write(AR_CR, AR_CR_RXD);
|
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_STA_ID1, 0);
|
||||||
/* 清中断 */
|
/* 清中断 */
|
||||||
ath9k_reg_write(AR_ISR, 0xFFFFFFFFu);
|
ath9k_reg_write(AR_ISR, 0xFFFFFFFFu);
|
||||||
@@ -650,13 +535,13 @@ static void ath9k_set_channel(u8 channel) {
|
|||||||
/* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel) */
|
/* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel) */
|
||||||
/* 触发 RFBUS 请求,等待 grant */
|
/* 触发 RFBUS 请求,等待 grant */
|
||||||
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1);
|
ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1);
|
||||||
delay_us(5);
|
dkm_delay_us(5);
|
||||||
for (u32 i = 0; i < 1000; i++) {
|
for (u32 i = 0; i < 1000; i++) {
|
||||||
if (ath9k_reg_read(AR_PHY_RFBUS_REQ) & 0x2) break;
|
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);
|
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 环 */
|
/* 在该信道停留 ~400ms,期间轮询 RX 环 */
|
||||||
for (u32 tick = 0; tick < 400; tick++) {
|
for (u32 tick = 0; tick < 400; tick++) {
|
||||||
ath9k_rx_drain();
|
ath9k_rx_drain();
|
||||||
delay_us(1000);
|
dkm_delay_us(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_scanning = 0;
|
g_scanning = 0;
|
||||||
@@ -752,6 +637,8 @@ __attribute__((visibility("default")))
|
|||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
(void)handle;
|
(void)handle;
|
||||||
|
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
|
||||||
|
dkm_tsc_calibrate();
|
||||||
raw_log("[ath9k.raw] driver_init entered\n");
|
raw_log("[ath9k.raw] driver_init entered\n");
|
||||||
if (!api || !api->log) return -1;
|
if (!api || !api->log) return -1;
|
||||||
g_log = api->log;
|
g_log = api->log;
|
||||||
@@ -766,7 +653,7 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
|
|
||||||
/* 2. 启用 PCI 命令(MEM + Bus Master) */
|
/* 2. 启用 PCI 命令(MEM + Bus Master) */
|
||||||
u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND);
|
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 */
|
/* 3. 读取 BAR0 */
|
||||||
u32 bar0 = pci_read(bus, dev, 0, PCI_BAR0);
|
u32 bar0 = pci_read(bus, dev, 0, PCI_BAR0);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/* DKM Console Framebuffer Driver
|
/* DKM Console Framebuffer Driver
|
||||||
* Stage 1, optional, provides "console".
|
* 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>
|
#include <stdint.h>
|
||||||
@@ -51,7 +53,7 @@ __attribute__((visibility("default"), used))
|
|||||||
const struct dkm_driver_desc driver_desc = {
|
const struct dkm_driver_desc driver_desc = {
|
||||||
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
|
.magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION,
|
||||||
.desc_size=sizeof(struct dkm_driver_desc),
|
.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,
|
.driver_class=5, .stage=1, .flags=0, .priority=0,
|
||||||
.depends=NULL, .depends_count=0,
|
.depends=NULL, .depends_count=0,
|
||||||
.provides=g_provides, .provides_count=1,
|
.provides=g_provides, .provides_count=1,
|
||||||
@@ -61,18 +63,11 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
/* ---------------------------------------------------------------
|
/* ---------------------------------------------------------------
|
||||||
* Colors
|
* Colors
|
||||||
* --------------------------------------------------------------- */
|
* --------------------------------------------------------------- */
|
||||||
static const u32 BG = 0xFFC8E0F0; /* light blue background */
|
static const u32 BG = 0xFFC8E0F0; /* light blue background — cleared before DSK Logo */
|
||||||
static const u32 ARC[4] = { 0xFF4488CC, 0xFF2266AA, 0xFF66AAEE, 0xFF88CCFF };
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------------
|
/* ---------------------------------------------------------------
|
||||||
* Drawing helpers – all integer arithmetic, no soft-float
|
* 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) {
|
static void fb_fill(u32 *fb, u64 pitch, u64 w, u64 h, u32 color) {
|
||||||
for (u64 y = 0; y < h; y++) {
|
for (u64 y = 0; y < h; y++) {
|
||||||
u32 *line = (u32 *)((u8 *)fb + y * pitch);
|
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
|
* Driver entry
|
||||||
* --------------------------------------------------------------- */
|
* --------------------------------------------------------------- */
|
||||||
@@ -192,61 +88,13 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
return 0;
|
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;
|
u32 *fb = (u32 *)api->fb_address;
|
||||||
i64 cx = (i64)fb_w / 2, cy = (i64)fb_h / 2, r = 48, thk = 4;
|
fb_fill(fb, api->fb_pitch, api->fb_width, api->fb_height, BG);
|
||||||
|
|
||||||
/* 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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
api->log->info("[console_fb] ready");
|
api->log->info("[console_fb] ready");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -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
@@ -3,98 +3,7 @@
|
|||||||
* Finds e1000 via PCI, maps MMIO, reads MAC address.
|
* Finds e1000 via PCI, maps MMIO, reads MAC address.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "../dkm_shared.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;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *const g_depends[] = {"pci","irq"};
|
static const char *const g_depends[] = {"pci","irq"};
|
||||||
static const char *const g_provides[] = {"netdev"};
|
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,
|
.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 */
|
/* e1000 MMIO registers */
|
||||||
#define E1000_CTRL 0x0000
|
#define E1000_CTRL 0x0000
|
||||||
#define E1000_STATUS 0x0008
|
#define E1000_STATUS 0x0008
|
||||||
@@ -205,20 +104,20 @@ static u32 g_rx_tail;
|
|||||||
static u32 g_tx_tail;
|
static u32 g_tx_tail;
|
||||||
static int g_rings_ready;
|
static int g_rings_ready;
|
||||||
|
|
||||||
static __inline__ void outl(u16 p, u32 v) { __asm__ volatile("outl %0,%1"::"a"(v),"Nd"(p):"memory"); }
|
#define COM1 0x3F8
|
||||||
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; }
|
/* 串口发送等待 thin wrapper — 调用 dkm 共享实现 */
|
||||||
static void outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p):"memory"); }
|
static void serial_wait_tx(void) { dkm_serial_wait_tx(COM1); }
|
||||||
|
|
||||||
static void raw_log(const char *s) {
|
static void raw_log(const char *s) {
|
||||||
while (*s) {
|
while (*s) {
|
||||||
char c = *s++;
|
char c = *s++;
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break;
|
serial_wait_tx();
|
||||||
outb(0x3F8, '\r');
|
dkm_outb(COM1, '\r');
|
||||||
}
|
}
|
||||||
for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break;
|
serial_wait_tx();
|
||||||
outb(0x3F8, (u8)c);
|
dkm_outb(COM1, (u8)c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,20 +126,18 @@ static void raw_hex(const char *prefix, u64 v) {
|
|||||||
raw_log(prefix);
|
raw_log(prefix);
|
||||||
raw_log("0x");
|
raw_log("0x");
|
||||||
for (int i=15; i>=0; i--) {
|
for (int i=15; i>=0; i--) {
|
||||||
for (u32 wait=0; wait<100000; wait++) if (inb(0x3F8+5)&0x20) break;
|
serial_wait_tx();
|
||||||
outb(0x3F8, (u8)h[(v >> (i * 4)) & 0xf]);
|
dkm_outb(COM1, (u8)h[(v >> (i * 4)) & 0xf]);
|
||||||
}
|
}
|
||||||
raw_log("\n");
|
raw_log("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
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);
|
return dkm_pci_read(bus, dev, func, reg);
|
||||||
outl(PCI_ADDR,a); return inl(PCI_DATA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) {
|
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);
|
dkm_pci_write(bus, dev, func, reg, v);
|
||||||
outl(PCI_ADDR,a); outl(PCI_DATA,v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int e1000_find(u8 *bus, u8 *dev) {
|
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) {
|
static u16 e1000_eeprom_read(u16 addr) {
|
||||||
/* EERD: bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位 */
|
/* EERD: bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位 */
|
||||||
g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u;
|
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];
|
u32 v = g_mmio[E1000_EERD/4];
|
||||||
if (v & (1u<<4)) return (u16)(v >> 16);
|
if (v & (1u<<4)) return (u16)(v >> 16);
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
return 0xffff;
|
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_tx_tail = (index + 1) % E1000_TX_DESC_COUNT;
|
||||||
g_mmio[E1000_TDT/4] = g_tx_tail;
|
g_mmio[E1000_TDT/4] = g_tx_tail;
|
||||||
mmio_flush();
|
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();
|
dma_fence();
|
||||||
if (g_tx_desc[index].status & E1000_TX_STA_DD) {
|
if (g_tx_desc[index].status & E1000_TX_STA_DD) {
|
||||||
raw_log("[e1000.raw] tx complete\n");
|
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")))
|
__attribute__((visibility("default")))
|
||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
|
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
|
||||||
|
dkm_tsc_calibrate();
|
||||||
raw_log("[e1000.raw] driver_init entered\n");
|
raw_log("[e1000.raw] driver_init entered\n");
|
||||||
(void)handle;
|
(void)handle;
|
||||||
if (!api||!api->log) return -1;
|
if (!api||!api->log) return -1;
|
||||||
@@ -472,8 +376,11 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
|
|
||||||
/* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */
|
/* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */
|
||||||
g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | (1u << 26);
|
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;
|
if (!(g_mmio[E1000_CTRL/4] & (1u << 26))) break;
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
raw_log("[e1000.raw] reset done\n");
|
raw_log("[e1000.raw] reset done\n");
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ struct dkm_block_api {
|
|||||||
int (*register_device)(const void *desc);
|
int (*register_device)(const void *desc);
|
||||||
u32 (*device_count)(void);
|
u32 (*device_count)(void);
|
||||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
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);
|
u64 (*sector_size)(u32 index);
|
||||||
const char *(*device_name)(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 {
|
struct dkm_kernel_api {
|
||||||
|
|||||||
@@ -4,110 +4,23 @@
|
|||||||
* it does not negotiate features, create virtqueues, or transmit packets.
|
* it does not negotiate features, create virtqueues, or transmit packets.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include "../dkm_shared.h"
|
||||||
|
|
||||||
#define DKM_DRIVER_MAGIC 0x444B4D31u
|
/* virtio-specific PCI IDs and capability types (driver-local) */
|
||||||
#define DKM_ABI_VERSION 1u
|
#define PCI_CLASS_REG 0x08 /* 32-bit class/subclass/prog_if/revision */
|
||||||
|
|
||||||
typedef unsigned char u8;
|
#define PCI_VENDOR_VIRTIO 0x1AF4u
|
||||||
typedef unsigned short u16;
|
#define VIRTIO_TRANS_DEVICE_MIN 0x1000u
|
||||||
typedef unsigned int u32;
|
#define VIRTIO_TRANS_DEVICE_MAX 0x103Fu
|
||||||
typedef unsigned long long u64;
|
#define VIRTIO_MODERN_DEVICE_MIN 0x1040u
|
||||||
typedef signed char i8;
|
#define VIRTIO_MODERN_DEVICE_MAX 0x107Fu
|
||||||
typedef long long i64;
|
#define VIRTIO_NET_DEVICE_ID 1u
|
||||||
|
|
||||||
#define NULL ((void *)0)
|
#define VIRTIO_PCI_CAP_COMMON_CFG 1u
|
||||||
|
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2u
|
||||||
struct dkm_log_api {
|
#define VIRTIO_PCI_CAP_ISR_CFG 3u
|
||||||
void (*info)(const char *msg);
|
#define VIRTIO_PCI_CAP_DEVICE_CFG 4u
|
||||||
void (*warn)(const char *msg);
|
#define VIRTIO_PCI_CAP_PCI_CFG 5u
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *const g_depends[] = { "pci", "irq" };
|
static const char *const g_depends[] = { "pci", "irq" };
|
||||||
static const char *const g_provides[] = { "netdev" };
|
static const char *const g_provides[] = { "netdev" };
|
||||||
@@ -134,73 +47,18 @@ const struct dkm_driver_desc driver_desc = {
|
|||||||
.reserved1 = 0
|
.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 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) {
|
static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||||
u32 addr = (1u << 31)
|
return dkm_pci_read(bus, dev, func, reg);
|
||||||
| ((u32)bus << 16)
|
|
||||||
| ((u32)dev << 11)
|
|
||||||
| ((u32)func << 8)
|
|
||||||
| ((u32)reg & 0xFC);
|
|
||||||
outl(PCI_ADDR, addr);
|
|
||||||
return inl(PCI_DATA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) {
|
||||||
u32 addr = (1u << 31)
|
dkm_pci_write(bus, dev, func, reg, value);
|
||||||
| ((u32)bus << 16)
|
|
||||||
| ((u32)dev << 11)
|
|
||||||
| ((u32)func << 8)
|
|
||||||
| ((u32)reg & 0xFC);
|
|
||||||
outl(PCI_ADDR, addr);
|
|
||||||
outl(PCI_DATA, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
|
static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) {
|
||||||
u32 v = pci_read(bus, dev, func, reg);
|
return dkm_pci_read8(bus, dev, func, reg);
|
||||||
return (u8)((v >> ((reg & 3) * 8)) & 0xff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_hex(const char *prefix, u64 value) {
|
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")))
|
__attribute__((visibility("default")))
|
||||||
int driver_init(const struct dkm_kernel_api *api,
|
int driver_init(const struct dkm_kernel_api *api,
|
||||||
struct dkm_driver_handle *handle) {
|
struct dkm_driver_handle *handle) {
|
||||||
@@ -328,6 +208,9 @@ int driver_init(const struct dkm_kernel_api *api,
|
|||||||
if (!api || !api->log) return -1;
|
if (!api || !api->log) return -1;
|
||||||
g_log = api->log;
|
g_log = api->log;
|
||||||
|
|
||||||
|
/* 实机要求: 在驱动初始化开头校准 TSC, 为后续时序提供准确计时 */
|
||||||
|
dkm_tsc_calibrate();
|
||||||
|
|
||||||
g_log->info("[virtio_net] init begin");
|
g_log->info("[virtio_net] init begin");
|
||||||
|
|
||||||
u8 bus = 0;
|
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;
|
for (int i = 0; i < 6; i++) netdev.mac[i] = 0;
|
||||||
netdev.flags = DKM_NET_F_LINK_UP;
|
netdev.flags = DKM_NET_F_LINK_UP;
|
||||||
netdev.ctx = 0;
|
netdev.ctx = 0;
|
||||||
netdev.tx = 0;
|
netdev.tx = virtio_net_tx; /* stub: 安全占位, 不 NULL */
|
||||||
netdev.rx_poll = 0;
|
netdev.rx_poll = virtio_net_rx_poll;
|
||||||
int net_index = api->net->register_device(&netdev);
|
int net_index = api->net->register_device(&netdev);
|
||||||
log_hex("[virtio_net] netdev register rc=", (u64)(i64)net_index);
|
log_hex("[virtio_net] netdev register rc=", (u64)(i64)net_index);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,20 +5,56 @@ u8 inb(u16 port);
|
|||||||
|
|
||||||
#define COM1 0x3F8
|
#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) {
|
static int serial_ready(void) {
|
||||||
return (inb(COM1 + 5) & 0x20) != 0;
|
return (inb(COM1 + 5) & 0x20) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 实机: 基于 TSC 的 100ms 超时等待 */
|
||||||
static int serial_wait_ready(void) {
|
static int serial_wait_ready(void) {
|
||||||
for (u64 spin = 0; spin < 1000000ULL; spin++) {
|
if (!g_tsc_per_ms) {
|
||||||
if (serial_ready()) {
|
/* 兜底: 校准未完成时使用循环 */
|
||||||
return 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serial_init(void) {
|
void serial_init(void) {
|
||||||
|
/* 实机要求: 先校准 TSC, 再使用基于 CPU 频率的延迟 */
|
||||||
|
tsc_calibrate_ser();
|
||||||
outb(COM1 + 1, 0x00);
|
outb(COM1 + 1, 0x00);
|
||||||
outb(COM1 + 3, 0x80);
|
outb(COM1 + 3, 0x80);
|
||||||
outb(COM1 + 0, 0x03);
|
outb(COM1 + 0, 0x03);
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
#include <utsm/utsm.h>
|
#include <utsm/utsm.h>
|
||||||
#include <utsm/segment.h>
|
#include <utsm/segment.h>
|
||||||
#include <utsm/pckc.h>
|
#include <utsm/pckc.h>
|
||||||
|
#include <utsm/process.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
|
|
||||||
static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS];
|
static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS];
|
||||||
@@ -18,6 +19,8 @@ void utsm_init(void) {
|
|||||||
log_info("[UTSM] pckc init begin");
|
log_info("[UTSM] pckc init begin");
|
||||||
utsm_pckc_init();
|
utsm_pckc_init();
|
||||||
log_info("[UTSM] pckc init ok");
|
log_info("[UTSM] pckc init ok");
|
||||||
|
|
||||||
|
utsm_process_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
utsm_segment_desc *utsm_get_segment(u32 slot) {
|
utsm_segment_desc *utsm_get_segment(u32 slot) {
|
||||||
|
|||||||
Binary file not shown.
@@ -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_ehdr *ehdr = (const elf64_ehdr *)address;
|
||||||
const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff);
|
const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff);
|
||||||
u64 image_offset[64];
|
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 total_size = 0;
|
||||||
u64 max_align = 16;
|
u64 max_align = 16;
|
||||||
u16 alloc_count = 0;
|
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;
|
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) {
|
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;
|
*(u32 *)(target_base + rela->offset) = (u32)patch;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -6,6 +6,7 @@
|
|||||||
#define DKM_BLOCK_MAX_DEVICES 8u
|
#define DKM_BLOCK_MAX_DEVICES 8u
|
||||||
|
|
||||||
typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
|
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 {
|
typedef struct dkm_block_device_desc {
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -13,14 +14,17 @@ typedef struct dkm_block_device_desc {
|
|||||||
u64 sector_count;
|
u64 sector_count;
|
||||||
void *ctx;
|
void *ctx;
|
||||||
dkm_block_read_fn read;
|
dkm_block_read_fn read;
|
||||||
|
dkm_block_write_fn write;
|
||||||
} dkm_block_device_desc;
|
} dkm_block_device_desc;
|
||||||
|
|
||||||
typedef struct dkm_block_api {
|
typedef struct dkm_block_api {
|
||||||
int (*register_device)(const dkm_block_device_desc *desc);
|
int (*register_device)(const dkm_block_device_desc *desc);
|
||||||
u32 (*device_count)(void);
|
u32 (*device_count)(void);
|
||||||
int (*read)(u32 index, u64 lba, u32 count, void *buffer);
|
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);
|
u64 (*sector_size)(u32 index);
|
||||||
const char *(*device_name)(u32 index);
|
const char *(*device_name)(u32 index);
|
||||||
|
int (*set_write_fn)(u32 index, dkm_block_write_fn fn);
|
||||||
} dkm_block_api;
|
} dkm_block_api;
|
||||||
|
|
||||||
void block_init(void);
|
void block_init(void);
|
||||||
|
|||||||
@@ -195,12 +195,12 @@ typedef long long du_i64;
|
|||||||
#define DU_ANIM_DELAY_NORMAL 100000u /* 标准动画 */
|
#define DU_ANIM_DELAY_NORMAL 100000u /* 标准动画 */
|
||||||
#define DU_ANIM_DELAY_FAST 40000u /* 快速反馈 */
|
#define DU_ANIM_DELAY_FAST 40000u /* 快速反馈 */
|
||||||
|
|
||||||
/* 弧线动画参数(DSK spinner 专用) */
|
/* 弧线动画参数(DSK spinner 专用 — Sealed Arc Comet 设计) */
|
||||||
#define DU_SPINNER_RADIUS 48u
|
#define DU_SPINNER_RADIUS 48u
|
||||||
#define DU_SPINNER_THICKNESS 4u
|
#define DU_SPINNER_THICKNESS 5u
|
||||||
#define DU_SPINNER_ARC_GAP 82u /* 每弧宽度(度) */
|
#define DU_SPINNER_HEAD_WIDTH 40u /* 弧头宽度(度) */
|
||||||
#define DU_SPINNER_ARC_COUNT 4u
|
#define DU_SPINNER_TAIL_LEN 220u /* 拖尾长度(度) */
|
||||||
#define DU_SPINNER_SPEED 2u /* 每帧旋转度数 */
|
#define DU_SPINNER_SPEED 4u /* 每帧旋转度数 */
|
||||||
#define DU_SPINNER_SPRITE 128u /* 精灵缓冲边长 */
|
#define DU_SPINNER_SPRITE 128u /* 精灵缓冲边长 */
|
||||||
|
|
||||||
/* 渐变动画步进 */
|
/* 渐变动画步进 */
|
||||||
@@ -673,12 +673,13 @@ static inline void du_draw_progress(du_context *ctx,
|
|||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
* Spinner(加载弧) — DSK 启动加载动画
|
* Spinner(加载弧) — DSK 启动加载动画
|
||||||
*
|
*
|
||||||
* 使用 "Sealed Arc" 四弧旋转设计,弧色取自色板。
|
* 使用 "Sealed Arc Comet" 单弧彗星尾设计:
|
||||||
|
* 一个明亮的弧头 + 渐变拖尾,颜色从弧头色过渡到拖尾色。
|
||||||
* =================================================================== */
|
* =================================================================== */
|
||||||
|
|
||||||
/* 默认弧色(从亮到暗的青蓝色) */
|
/* 默认弧色:[0]=弧头色(亮蓝), [1]=拖尾色(紫) */
|
||||||
#define DU_SPINNER_ARCS { \
|
#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 \
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
|
|||||||
@@ -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);
|
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) {
|
static u64 block_sector_size_impl(u32 index) {
|
||||||
if (index >= g_device_count) return 0;
|
if (index >= g_device_count) return 0;
|
||||||
return g_devices[index].sector_size;
|
return g_devices[index].sector_size;
|
||||||
@@ -45,12 +52,20 @@ static const char *block_device_name_impl(u32 index) {
|
|||||||
return g_devices[index].name;
|
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 = {
|
static const dkm_block_api g_block_api = {
|
||||||
.register_device = block_register_device_impl,
|
.register_device = block_register_device_impl,
|
||||||
.device_count = block_device_count_impl,
|
.device_count = block_device_count_impl,
|
||||||
.read = block_read_impl,
|
.read = block_read_impl,
|
||||||
|
.write = block_write_impl,
|
||||||
.sector_size = block_sector_size_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) {
|
const dkm_block_api *block_get_api(void) {
|
||||||
|
|||||||
Binary file not shown.
@@ -152,7 +152,8 @@ static struct limine_file *dsk_find_module(void) {
|
|||||||
/* ---- FAT32 block-provider file reader ---- */
|
/* ---- FAT32 block-provider file reader ---- */
|
||||||
static u8 g_dsk_fat32_disk[65536]; /* 128-sector BPB+FAT+root dir buffer */
|
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_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) {
|
static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) {
|
||||||
const dkm_kernel_api *api = dkm_get_kernel_api();
|
const dkm_kernel_api *api = dkm_get_kernel_api();
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -25,6 +25,9 @@ SECTIONS
|
|||||||
.data : ALIGN(4K) {
|
.data : ALIGN(4K) {
|
||||||
*(.data*)
|
*(.data*)
|
||||||
*(COMMON)
|
*(COMMON)
|
||||||
|
} :data
|
||||||
|
|
||||||
|
.bss : ALIGN(4K) {
|
||||||
*(.bss*)
|
*(.bss*)
|
||||||
} :data
|
} :data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
#include <utsm/utsm.h>
|
#include <utsm/utsm.h>
|
||||||
#include <utsm/log.h>
|
#include <utsm/log.h>
|
||||||
#include <utsm/segment.h>
|
#include <utsm/segment.h>
|
||||||
|
#include <utsm/arena.h>
|
||||||
|
|
||||||
int memcmp(const void *a, const void *b, usize len);
|
int memcmp(const void *a, const void *b, usize len);
|
||||||
usize strlen(const char *s);
|
usize strlen(const char *s);
|
||||||
|
|
||||||
int utsm_selftest_run(void) {
|
int utsm_selftest_run(void) {
|
||||||
utsm_process_context process;
|
log_info("[UTSM] selftest enter");
|
||||||
utsm_process_create_test(&process, 1);
|
|
||||||
|
/* 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;
|
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) {
|
if (status != UTSM_OK) {
|
||||||
log_error("[UTSM] selftest create segment failed");
|
log_error("[UTSM] selftest create segment failed");
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
Binary file not shown.
+12
-4
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(7);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(7);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
@@ -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 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 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,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,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,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,1,0,0,0,1,1,0,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,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,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
{0,0,0,0,0,0,0,0,0,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},
|
||||||
@@ -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 (g_windows[i].id == w->id) { idx = i; break; }
|
||||||
}
|
}
|
||||||
if (idx < 0) return;
|
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 */
|
/* 调用应用的 on_destroy */
|
||||||
if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_destroy && w->app_state) {
|
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);
|
g_apps[w->app_id].on_destroy(w->app_state);
|
||||||
@@ -474,7 +516,20 @@ static void draw_taskbar(void) {
|
|||||||
if (w->minimized) continue;
|
if (w->minimized) continue;
|
||||||
u32 btn_bg = w->focused ? DS_DARK_ACCENT : DP_ABYSS_600;
|
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_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);
|
DS_DARK_TEXT_PRIMARY, btn_bg, DU_ASCII_STEP);
|
||||||
wx += 104;
|
wx += 104;
|
||||||
if (wx > (int)g_fb_w - 200) break;
|
if (wx > (int)g_fb_w - 200) break;
|
||||||
@@ -507,7 +562,7 @@ static void draw_taskbar(void) {
|
|||||||
static void draw_desktop_icons(void) {
|
static void draw_desktop_icons(void) {
|
||||||
for (int i = 0; i < g_icon_count; i++) {
|
for (int i = 0; i < g_icon_count; i++) {
|
||||||
desktop_icon *ic = &g_icons[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;
|
int cy = ic->y;
|
||||||
/* 根据应用 ID 画不同图标 */
|
/* 根据应用 ID 画不同图标 */
|
||||||
switch (ic->app_id) {
|
switch (ic->app_id) {
|
||||||
@@ -520,8 +575,8 @@ static void draw_desktop_icons(void) {
|
|||||||
/* 标签文字 */
|
/* 标签文字 */
|
||||||
int label_len = 0;
|
int label_len = 0;
|
||||||
while (ic->label[label_len]) label_len++;
|
while (ic->label[label_len]) label_len++;
|
||||||
int label_x = cx + (32 - label_len * (int)DU_ASCII_STEP) / 2;
|
int label_x = ic->x + (DESKTOP_ICON_W - label_len * (int)DU_ASCII_STEP) / 2;
|
||||||
if (label_x < cx) label_x = cx;
|
if (label_x < ic->x) label_x = ic->x;
|
||||||
du_draw_string(&g_fb, ic->label, label_x, cy + 36,
|
du_draw_string(&g_fb, ic->label, label_x, cy + 36,
|
||||||
DS_DARK_TEXT_PRIMARY, DS_DARK_BG_PRIMARY, DU_ASCII_STEP);
|
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 鼠标处理
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
|
/* 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) {
|
static void ps2_mouse_init(void) {
|
||||||
/* 启用 AUX 设备 */
|
/* 1. 刷新固件/上一阶段留下的残留数据 */
|
||||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
ps2_drain();
|
||||||
|
|
||||||
|
/* 2. 启用 AUX(鼠标)端口 */
|
||||||
|
ps2_wait_write();
|
||||||
outb(0x64, 0xA8);
|
outb(0x64, 0xA8);
|
||||||
/* 启用 AUX IRQ */
|
|
||||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
/* 3. 读取控制器配置字节 */
|
||||||
|
ps2_wait_write();
|
||||||
outb(0x64, 0x20);
|
outb(0x64, 0x20);
|
||||||
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
|
ps2_wait_read();
|
||||||
u8 cfg = inb(0x60);
|
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 */
|
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);
|
outb(0x64, 0x60);
|
||||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
ps2_wait_write();
|
||||||
outb(0x60, cfg);
|
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);
|
outb(0x64, 0xD4);
|
||||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
ps2_wait_write();
|
||||||
outb(0x60, 0xFF);
|
outb(0x60, 0xFF);
|
||||||
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
|
/* 排干所有复位响应字节(最多 8 字节,带超时) */
|
||||||
inb(0x60); /* ACK */
|
for (int i = 0; i < 8; i++) {
|
||||||
/* 启用 streaming */
|
if (ps2_wait_read() != 0) break;
|
||||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
inb(0x60);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7. 启用数据流模式 (0xF4) — 响应为 ACK(0xFA) */
|
||||||
|
ps2_wait_write();
|
||||||
outb(0x64, 0xD4);
|
outb(0x64, 0xD4);
|
||||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
ps2_wait_write();
|
||||||
outb(0x60, 0xF4);
|
outb(0x60, 0xF4);
|
||||||
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
|
/* 排干 ACK */
|
||||||
inb(0x60); /* 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");
|
slog("mouse init ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ps2_mouse_poll(void) {
|
static int ps2_mouse_poll(void) {
|
||||||
u8 st = inb(0x64);
|
u8 st = inb(0x64);
|
||||||
if (!(st & 1)) return 0;
|
if (!(st & 1)) return 0;
|
||||||
|
if (!(st & 0x20)) return 0; /* 不是鼠标数据(键盘数据留给键盘处理) */
|
||||||
u8 data = inb(0x60);
|
u8 data = inb(0x60);
|
||||||
if (!(st & 0x20)) return 0; /* 不是鼠标数据 */
|
|
||||||
|
|
||||||
g_mouse_buf[g_mouse_idx++] = data;
|
g_mouse_buf[g_mouse_idx++] = data;
|
||||||
if (g_mouse_idx < 3) return 0;
|
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 dx = (int)(i8)g_mouse_buf[1];
|
||||||
int dy = (int)(i8)g_mouse_buf[2];
|
int dy = (int)(i8)g_mouse_buf[2];
|
||||||
if (g_mouse_buf[0] & 0x10) dx = -dx; /* X sign */
|
/* (i8) 转换已处理符号位,无需再检查 sign bits */
|
||||||
/* 注意:Y 轴方向(鼠标 Y 向上为正,屏幕 Y 向下为正) */
|
dy = -dy; /* PS/2 Y 向上为正,屏幕 Y 向下为正,需翻转 */
|
||||||
if (!(g_mouse_buf[0] & 0x20)) dy = -dy; /* Y sign */
|
|
||||||
else dy = -dy;
|
|
||||||
dy = -dy; /* 鼠标 Y 翻转 */
|
|
||||||
|
|
||||||
g_mouse_x += dx;
|
g_mouse_x += dx;
|
||||||
g_mouse_y += dy;
|
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_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;
|
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;
|
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;
|
g_mouse_has_pkt = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1210,6 +1316,24 @@ static void launch_app(int app_id) {
|
|||||||
|
|
||||||
win->app_state = app->on_create(&ctx);
|
win->app_state = app->on_create(&ctx);
|
||||||
win->dirty = 1;
|
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");
|
slog("app launched");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1229,13 +1353,16 @@ static void redraw_all(void) {
|
|||||||
desktop_window *w = &g_windows[i];
|
desktop_window *w = &g_windows[i];
|
||||||
if (w->minimized || !w->visible) continue;
|
if (w->minimized || !w->visible) continue;
|
||||||
|
|
||||||
|
/* 窗口装饰(先画框架作为窗口背景:主体、标题栏、边框) */
|
||||||
|
draw_window_frame(w);
|
||||||
|
|
||||||
/* 窗口客户区背景 */
|
/* 窗口客户区背景 */
|
||||||
int cx = w->x + BORDER_W;
|
int cx = w->x + BORDER_W;
|
||||||
int cy = w->y + TITLEBAR_H;
|
int cy = w->y + TITLEBAR_H;
|
||||||
int cw = w->w - 2 * BORDER_W;
|
int cw = w->w - 2 * BORDER_W;
|
||||||
int ch = w->h - TITLEBAR_H - 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) {
|
if (w->app_id >= 0 && w->app_id < g_app_count && g_apps[w->app_id].on_draw && w->app_state) {
|
||||||
app_ctx ctx;
|
app_ctx ctx;
|
||||||
ctx.fb = &g_fb;
|
ctx.fb = &g_fb;
|
||||||
@@ -1247,9 +1374,6 @@ static void redraw_all(void) {
|
|||||||
ctx.kernel_api = g_kernel_api;
|
ctx.kernel_api = g_kernel_api;
|
||||||
g_apps[w->app_id].on_draw(w->app_state, &ctx);
|
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 鼠标 */
|
/* 轮询 PS/2 鼠标 */
|
||||||
if (ps2_mouse_poll()) {
|
if (ps2_mouse_poll()) {
|
||||||
need_redraw = 1;
|
/* 拖拽处理 - 需要全屏重绘 */
|
||||||
|
|
||||||
/* 拖拽处理 */
|
|
||||||
if (g_dragging && g_drag_win >= 0) {
|
if (g_dragging && g_drag_win >= 0) {
|
||||||
desktop_window *w = win_find(g_drag_win);
|
desktop_window *w = win_find(g_drag_win);
|
||||||
if (w) {
|
if (w) {
|
||||||
w->x = g_mouse_x - g_drag_off_x;
|
w->x = g_mouse_x - g_drag_off_x;
|
||||||
w->y = g_mouse_y - g_drag_off_y;
|
w->y = g_mouse_y - g_drag_off_y;
|
||||||
}
|
}
|
||||||
|
need_redraw = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 鼠标点击事件 */
|
/* 鼠标点击事件 - 需要全屏重绘 */
|
||||||
if (g_mouse_btn) {
|
if (g_mouse_btn) {
|
||||||
/* 检查任务栏点击 */
|
/* 检查任务栏点击 */
|
||||||
int tb_y = (int)g_fb_h - TASKBAR_H;
|
int tb_y = (int)g_fb_h - TASKBAR_H;
|
||||||
@@ -1431,6 +1554,11 @@ next:
|
|||||||
if (need_redraw) {
|
if (need_redraw) {
|
||||||
cursor_restore_bg();
|
cursor_restore_bg();
|
||||||
redraw_all();
|
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");
|
__asm__("pause");
|
||||||
|
|||||||
+4
-1
@@ -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
|
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
|
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)
|
OBJECTS := $(C_SOURCES:.c=.o)
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|||||||
+9
-12
@@ -2,26 +2,23 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(5); /* R-X */
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4); /* R-- */
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(6); /* RW- */
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.text : ALIGN(4K) {
|
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||||
*(.text*)
|
|
||||||
} :text
|
|
||||||
|
|
||||||
.rodata : ALIGN(4K) {
|
|
||||||
*(.rodata*)
|
|
||||||
} :rodata
|
|
||||||
|
|
||||||
.data : ALIGN(4K) {
|
.data : ALIGN(4K) {
|
||||||
*(.data*)
|
*(.data*)
|
||||||
|
*(.data.rel.ro*)
|
||||||
*(COMMON)
|
*(COMMON)
|
||||||
*(.bss*)
|
*(.bss*)
|
||||||
} :data
|
} :data
|
||||||
|
.rela.dyn : ALIGN(8) { *(.rela.dyn*) } :data
|
||||||
|
.dynamic : ALIGN(8) { *(.dynamic*) } :data :dynamic
|
||||||
}
|
}
|
||||||
|
|||||||
+518
-149
@@ -12,6 +12,7 @@ typedef unsigned long long u64;
|
|||||||
typedef long long i64;
|
typedef long long i64;
|
||||||
|
|
||||||
typedef int (*block_read_fn)(u32 index, u64 lba, u32 count, void *buf);
|
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 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++);} }
|
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 u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_p;
|
||||||
static block_read_fn g_block_read;
|
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_disk[131072];
|
||||||
static u8 g_cluster[4096];
|
static u8 g_cluster[4096];
|
||||||
static u8 g_fdata[262144];
|
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_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) {
|
static u32 blend_color(u32 c1, u32 c2, u32 a) {
|
||||||
u32 na = 256 - 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) {
|
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;
|
const fat32_de *dir = (const fat32_de *)clus;
|
||||||
for (u32 e=0; e*32 < clus_sectors*512; e++) {
|
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;
|
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 */
|
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[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;
|
name[5]='I';name[6]='N';name[7]='T';name[8]='X';name[9]='T';name[10]=0;
|
||||||
u8 *data = 0; u32 size = 0;
|
u8 *data = 0; u32 size = 0;
|
||||||
|
*dev_mode = 0;
|
||||||
logl("[DSK] fat32_read_path start");
|
logl("[DSK] fat32_read_path start");
|
||||||
int rc = fat32_read_root_file(name, &data, &size);
|
int rc = fat32_read_root_file(name, &data, &size);
|
||||||
logh("[DSK] fat32_read_path rc=", (u64)(i64)rc);
|
logh("[DSK] fat32_read_path rc=", (u64)(i64)rc);
|
||||||
if (rc != 0) { logl("[DSK] firstInit.txt not found"); *is_first = 1; return 0; }
|
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; }
|
if (data[0] == '0') { logl("[DSK] firstInit=0"); *is_first = 1; }
|
||||||
logl("[DSK] firstInit != 0"); *is_first = 0; return 0;
|
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) {
|
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;
|
if (!lc || min_vaddr == ~0ULL) return -4;
|
||||||
u64 isize = max_vaddr - min_vaddr;
|
u64 isize = max_vaddr - min_vaddr;
|
||||||
isize = (isize + 0xFFF) & ~0xFFFULL;
|
isize = (isize + 0xFFF) & ~0xFFFULL;
|
||||||
static u8 ibuf[262144];
|
/* 加载缓冲:需容纳 shell.elf(~536KB,含 FAT32 读写缓冲 BSS)。1MB 留余量。 */
|
||||||
|
static u8 ibuf[1048576];
|
||||||
u8 *image = ibuf;
|
u8 *image = ibuf;
|
||||||
|
if (isize > 1048576) return -5; /* 镜像过大,防止越界 */
|
||||||
memset_dsk(image, 0, isize);
|
memset_dsk(image, 0, isize);
|
||||||
for (u16 i = 0; i < eh->phnum; i++) {
|
for (u16 i = 0; i < eh->phnum; i++) {
|
||||||
const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dsk_spinner_and_check(u32 *fb_arg, i64 cx, i64 cy, i64 r, i64 thk,
|
/* Load an ELF module from the FAT32 root by 11-char 8.3 name, parse it into
|
||||||
const u32 arc_colors[4], u32 bg, const void *boot_ctx_ptr) {
|
* the shared image buffer, and jump to its entry with the boot context.
|
||||||
(void)bg;
|
* Returns 0 if the module ran and returned, non-zero on load failure. */
|
||||||
(void)fb_arg;
|
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;
|
const dsk_boot_context *ctx = (const dsk_boot_context *)boot_ctx_ptr;
|
||||||
{ u64 api = ctx->dkm_kernel_api;
|
{ u64 api = ctx->dkm_kernel_api;
|
||||||
g_block_read = (block_read_fn)*(u64 *)(api + 0xA8);
|
u64 blk = *(u64 *)(api + 0xA8);
|
||||||
if (g_block_read) g_block_read = (block_read_fn)*(u64 *)((u64)g_block_read + 16); }
|
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 */ }
|
||||||
#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;
|
|
||||||
|
|
||||||
g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width;
|
g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width;
|
||||||
g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch;
|
g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch;
|
||||||
u32 *sfb=(u32*)(u64)g_fb_addr;
|
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);
|
logh("[DSK] block_read ptr=", (u64)g_block_read);
|
||||||
logl("[DSK] entering spinner loop");
|
|
||||||
|
|
||||||
while (1) {
|
tsc_calibrate_dsk();
|
||||||
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++;
|
|
||||||
|
|
||||||
if(frame>=100&&!checked){checked=1; /* was 540, reduced for test */
|
/* Gradient background + spinning loader (~1.1s, or 800ms without block dev). */
|
||||||
logl("[DSK] checking firstInit...");
|
i64 cx = (i64)g_fb_w / 2;
|
||||||
if(g_block_read){logl("[DSK] have block read, calling dsk_check_firstinit");
|
i64 cy = (i64)g_fb_h / 2;
|
||||||
int is_first=0;
|
dsk_fill_gradient_rect(sfb, 0, 0, (i64)g_fb_w, (i64)g_fb_h);
|
||||||
if(dsk_check_firstinit(&is_first)==0){
|
spinner_save_bg(sfb, cx, cy);
|
||||||
if(is_first){
|
|
||||||
logl("[DSK] first init detected; running system initializers");
|
|
||||||
|
|
||||||
/* DSK owns system component initialization. FirstInit is only user setup. */
|
u32 base = 0;
|
||||||
char mn[12]; /* MOUSE ELF */
|
u32 head_color = 0xFFE8F2FC; /* nearly-white light blue */
|
||||||
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';
|
u64 spin_start = rdtsc_dsk();
|
||||||
u8 *md=0; u32 ms=0; void *me=0;
|
u64 spin_budget = g_tsc_per_ms * (g_block_read ? 1100 : 800);
|
||||||
if(fat32_read_root_file(mn,&md,&ms)==0){
|
while (rdtsc_dsk() - spin_start < spin_budget) {
|
||||||
if(dsk_load_elf(md,ms,&me)==0){
|
spinner_restore_bg(sfb, cx, cy);
|
||||||
logl("[DSK] jumping to mouseInit");
|
spinner_draw(sfb, cx, cy, base, head_color);
|
||||||
void(*mentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))me;
|
base = (base + 6) & 255; /* clockwise advance */
|
||||||
mentry(ctx);
|
u64 fs = rdtsc_dsk();
|
||||||
logl("[DSK] mouseInit returned");
|
while (rdtsc_dsk() - fs < g_tsc_per_ms * 33) __asm__("pause"); /* ~30fps */
|
||||||
}
|
}
|
||||||
}
|
spinner_restore_bg(sfb, cx, cy);
|
||||||
|
|
||||||
char nn[12]; /* NETMAN ELF */
|
if (!g_block_read) {
|
||||||
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] no block device, halting");
|
||||||
u8 *nd=0; u32 ns=0; void *ne=0;
|
for(;;)__asm__("hlt");
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logl("[DSK] loading FirstInit.elf");
|
logl("[DSK] checking firstInit...");
|
||||||
char en[12]; /* FIRSTINIT */
|
int is_first = 0, dev_mode = 0;
|
||||||
en[0]='F';en[1]='I';en[2]='R';en[3]='S';en[4]='T';
|
if (dsk_check_firstinit(&is_first, &dev_mode) != 0) {
|
||||||
en[5]='I';en[6]='N';en[7]='I';en[8]='T';en[9]=' ';en[10]=0;
|
logl("[DSK] firstInit check failed, halting");
|
||||||
u8 *fd=0; u32 fs=0;
|
for(;;)__asm__("hlt");
|
||||||
if(fat32_read_root_file(en,&fd,&fs)==0){
|
}
|
||||||
if(dsk_load_elf(fd,fs,&fe_entry)==0) logl("[DSK] FirstInit.elf loaded");
|
|
||||||
}
|
/* 开发者模式:加载 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 {
|
} 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)login_ok;
|
||||||
void(*entry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))fe_entry;
|
|
||||||
entry(ctx);
|
logl("[DSK] loading desktop");
|
||||||
logl("[DSK] FirstInit returned, loading desktop");
|
dsk_run_desktop(ctx);
|
||||||
/* FirstInit 返回后加载 desktop */
|
return;
|
||||||
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;
|
/* First boot: run system initializers, then FirstInit, then desktop */
|
||||||
if(fat32_read_root_file(dn,&dd,&ds)==0){
|
logl("[DSK] first init detected; running system initializers");
|
||||||
if(dsk_load_elf(dd,ds,&de)==0){
|
|
||||||
logl("[DSK] jumping to desktop");
|
/* mouseInit.elf — safe PS/2 mouse init */
|
||||||
void(*dentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))de;
|
{ char mn[12]; /* MOUSE ELF */
|
||||||
dentry(ctx);
|
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");
|
||||||
for(;;)__asm__("hlt");
|
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 */
|
logl("[DSK] FirstInit returned, persisting user.conf");
|
||||||
if(checked&&!fe_entry&&frame>150){
|
dsk_persist_userconf(ctx);
|
||||||
logl("[DSK] loading desktop directly (non-first boot)");
|
logl("[DSK] loading desktop");
|
||||||
char dn[12]; /* DESKTOP ELF */
|
dsk_run_desktop(ctx);
|
||||||
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;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noreturn, visibility("default")))
|
__attribute__((noreturn, visibility("default")))
|
||||||
void dsk_entry(const dsk_boot_context *ctx) {
|
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");
|
logl("[DSK] boot");
|
||||||
if(!ctx||ctx->magic!=DSK_BOOT_MAGIC){logl("[DSK] bad context");for(;;)__asm__("hlt");}
|
if(!ctx||ctx->magic!=DSK_BOOT_MAGIC){logl("[DSK] bad context");for(;;)__asm__("hlt");}
|
||||||
logl("[DSK] context ok");
|
logl("[DSK] context ok");
|
||||||
g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width;
|
dsk_spinner_and_check(ctx);
|
||||||
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);
|
|
||||||
logl("[DSK] SELFTEST PASS");
|
logl("[DSK] SELFTEST PASS");
|
||||||
for(;;)__asm__("hlt");
|
for(;;)__asm__("hlt");
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(5);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(6);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
@@ -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__ 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__ 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) {
|
static void sputc(char c) {
|
||||||
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
|
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
|
||||||
outb(COM1, (unsigned char)c);
|
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 swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
|
||||||
static void logl(const char *s) { swrite(s); swrite("\n"); }
|
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) {
|
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 */
|
if (!(inb(0x64) & 0x02)) return 0; /* input buffer empty */
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
static int ps2_wait_read(void) {
|
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 */
|
if (inb(0x64) & 0x01) return 0; /* output buffer full */
|
||||||
|
__asm__ volatile("pause");
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -81,12 +121,12 @@ static void mouse_init(void) {
|
|||||||
logl("[mouse] init done");
|
logl("[mouse] init done");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BG_TOP 0xFFC8E0F0u
|
#define BG_TOP 0xFF2D2D30u
|
||||||
#define BG_BOTTOM 0xFF49306Fu
|
#define BG_BOTTOM 0xFF1A1A1Eu
|
||||||
#define BG_MID 0xFF8888B0u
|
#define BG_MID 0xFF3C3C40u
|
||||||
#define CARD_BG 0xFFEAF5FBu
|
#define CARD_BG 0xFF353539u
|
||||||
#define INPUT_BG 0xFFF0F5FAu
|
#define INPUT_BG 0xFF2A2A2Eu
|
||||||
#define TEXT_FG 0xFF5F7FA6u
|
#define TEXT_FG 0xFFE0E0E0u
|
||||||
#define WELCOME_FG 0xFFE8F2FCu
|
#define WELCOME_FG 0xFFE8F2FCu
|
||||||
|
|
||||||
static u64 fb_a, fb_w, fb_h, fb_p;
|
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);
|
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 */
|
/* 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) {
|
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++) {
|
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++) {
|
for (i64 c=0; c<w; c++) {
|
||||||
i64 xx = x + c;
|
i64 xx = x + c;
|
||||||
if (xx < 0 || (u64)xx >= fb_w) continue;
|
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);
|
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) {
|
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"
|
#include "text_bitmaps.c"
|
||||||
|
|
||||||
static void fade_ring(u32 *fb, i64 cx, i64 cy, u32 bg) {
|
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;
|
(void)bg;
|
||||||
i64 r = 72;
|
i64 r = 72;
|
||||||
for (int step=0; step<12; step++) {
|
fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1);
|
||||||
(void)step;
|
|
||||||
fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1);
|
|
||||||
delay_frame();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card, u32 bg_color) {
|
static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card, u32 bg_color) {
|
||||||
(void)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 */
|
/* 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) {
|
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;
|
u32 fill = active ? 0xFF45454Au : INPUT_BG;
|
||||||
fill_rounded_rect(fb, x, y, w, h, 8, fill);
|
fill_rounded_rect(fb, x, y, w, h, 2, fill);
|
||||||
stroke_rounded_rect(fb, x, y, w, h, 8, 1, active ? 0xFFB6C9DD : card_bg);
|
stroke_rounded_rect(fb, x, y, w, h, 2, 1, active ? 0xFF6C6C72 : 0xFF404044);
|
||||||
char out[64]; int i=0;
|
char out[64]; int i=0;
|
||||||
while(value[i] && i<60) { out[i] = mask ? '*' : value[i]; i++; }
|
while(value[i] && i<60) { out[i] = mask ? '*' : value[i]; i++; }
|
||||||
out[i]=0;
|
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) {
|
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 card = CARD_BG;
|
||||||
|
u32 sub_fg = 0xFF9098A0; /* dimmer English subtitle color on card */
|
||||||
cursor_erase(fb);
|
cursor_erase(fb);
|
||||||
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
|
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
|
||||||
draw_card(fb, card_x, card_y, 900, 600, card, bg);
|
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_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_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);
|
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_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);
|
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_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);
|
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_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);
|
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++] = c;
|
||||||
buf[len] = 0;
|
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;
|
int theme;
|
||||||
} setup_prefs;
|
} setup_prefs;
|
||||||
|
|
||||||
static const char *pref_value(int row, int val) {
|
/* 语言选项: 索引 0-11 */
|
||||||
if (row == 0) return val ? "English" : "Simplified Chinese";
|
static const char *lang_names[] = {
|
||||||
if (row == 1) return val ? "Global" : "China Mainland";
|
"Simplified Chinese", "English (US)", "English (UK)", "Japanese",
|
||||||
if (row == 2) return val ? "UTC" : "Asia/Shanghai";
|
"Korean", "French", "German", "Spanish",
|
||||||
if (row == 3) return val ? "CN-QWERTY" : "US-QWERTY";
|
"Portuguese", "Russian", "Arabic", "Hindi"
|
||||||
if (row == 4) return val ? "Dark" : "Light";
|
};
|
||||||
|
#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 "";
|
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) {
|
static int pref_get(const setup_prefs *p, int row) {
|
||||||
if (row == 0) return p->language;
|
if (row == 0) return p->language;
|
||||||
if (row == 1) return p->region;
|
if (row == 1) return p->region;
|
||||||
@@ -504,22 +641,41 @@ static int pref_get(const setup_prefs *p, int row) {
|
|||||||
return 0;
|
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) {
|
static void pref_toggle(setup_prefs *p, int row) {
|
||||||
if (row == 0) p->language ^= 1;
|
int count = pref_option_count(row);
|
||||||
else if (row == 1) p->region ^= 1;
|
int cur = pref_get(p, row);
|
||||||
else if (row == 2) p->timezone ^= 1;
|
pref_set(p, row, (cur + 1) % count);
|
||||||
else if (row == 3) p->keyboard ^= 1;
|
|
||||||
else if (row == 4) p->theme ^= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_option_row(u32 *fb, i64 x, i64 y, i64 w, i64 h,
|
static void draw_option_row(u32 *fb, i64 x, i64 y, i64 w, i64 h,
|
||||||
const u8 *label, i64 lw, i64 lh,
|
const u8 *label, i64 lw, i64 lh,
|
||||||
const char *value, int active, u32 fg, u32 card) {
|
const char *value, int active, u32 fg, u32 card,
|
||||||
u32 fill = active ? 0xFFFFFFFF : INPUT_BG;
|
const char *en) {
|
||||||
|
u32 fill = active ? 0xFF45454Au : INPUT_BG;
|
||||||
fb_bitmap_alpha(fb, label, lw, lh, x, y + 4, fg, card, 255);
|
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);
|
if (en) fb_text(fb, en, x, y + 4 + lh + 1, 0xFF9098A0, card);
|
||||||
stroke_rounded_rect(fb, x + 220, y, w - 220, h, 8, 1, active ? 0xFFB6C9DD : 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);
|
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) {
|
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);
|
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
|
||||||
draw_card(fb, card_x, card_y, 900, 600, card, bg);
|
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);
|
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);
|
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+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+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+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+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+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+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+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);
|
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_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);
|
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;
|
u32 card = CARD_BG;
|
||||||
i64 ry = card_y + 120 + (i64)row * 80;
|
i64 ry = card_y + 120 + (i64)row * 80;
|
||||||
cursor_erase(fb);
|
cursor_erase(fb);
|
||||||
fill_rect(fb, card_x + 36, ry - 8, 832, 58, 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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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) {
|
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 active = 0;
|
||||||
int mcnt = 0;
|
int mcnt = 0;
|
||||||
u8 mpkt[3];
|
u8 mpkt[3];
|
||||||
u8 prev_btns = 0;
|
u8 prev_btns = 0;
|
||||||
int e0 = 0;
|
int e0 = 0;
|
||||||
|
int dropdown_open = -1;
|
||||||
redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg);
|
redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
u8 st = inb(0x64);
|
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);
|
cursor_draw(fb, *mx, *my);
|
||||||
}
|
}
|
||||||
if ((btns & 1) && !(prev_btns & 1)) {
|
if ((btns & 1) && !(prev_btns & 1)) {
|
||||||
for (int r = 0; r < 5; r++) {
|
if (dropdown_open >= 0) {
|
||||||
i64 ry = card_y + 120 + (i64)r * 80;
|
/* 检查点击是否在下拉选项上 */
|
||||||
if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) {
|
i64 dd_x = card_x + 260;
|
||||||
int old = active;
|
i64 dd_y = card_y + 120 + (i64)dropdown_open * 80 + 42;
|
||||||
active = r;
|
i64 dd_w = 600;
|
||||||
pref_toggle(prefs, active);
|
int opt_count = pref_option_count(dropdown_open);
|
||||||
if (old != active) redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg);
|
int visible = opt_count > 6 ? 6 : opt_count;
|
||||||
redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg);
|
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;
|
u8 sc = data;
|
||||||
if (sc == 0xE0) { e0 = 1; continue; }
|
if (sc == 0xE0) { e0 = 1; continue; }
|
||||||
if (sc & 0x80) { e0 = 0; 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 (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); }
|
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;
|
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, old, active, *mx, *my, fg);
|
||||||
redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg);
|
redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg);
|
||||||
} else {
|
} else {
|
||||||
|
cursor_bg_valid = 0;
|
||||||
|
cursor_cur_x = -100;
|
||||||
|
cursor_cur_y = -100;
|
||||||
return;
|
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_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_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);
|
net_is_wireless_fn iswl = (net_is_wireless_fn)*(u64 *)(net_api + 64);
|
||||||
if (!dcount || !iswl || !scount || !sresult) {
|
if (!dcount) { logl("[FirstInit] net device_count fn is null"); return; }
|
||||||
logl("[FirstInit] net scan api incomplete");
|
if (!sstart) { logl("[FirstInit] net scan_start fn is null"); return; }
|
||||||
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();
|
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;
|
b[p++]='0'+(ndev/10); b[p++]='0'+(ndev%10); b[p]=0;
|
||||||
logl(b);
|
logl(b);
|
||||||
}
|
}
|
||||||
/* 找第一个无线设备 */
|
/* 找第一个无线设备 - 通过 device_info 检查 flags 而非调用 is_wireless */
|
||||||
int wifi_idx = -1;
|
int wifi_idx = -1;
|
||||||
for (u32 i = 0; i < ndev; i++) {
|
for (u32 i = 0; i < ndev; i++) {
|
||||||
int rc = iswl(i);
|
/* 使用 device_info 获取 flags,避免直接调用 is_wireless 导致崩溃 */
|
||||||
if (rc == 1) { wifi_idx = (int)i; break; }
|
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) {
|
if (wifi_idx < 0) {
|
||||||
logl("[FirstInit] no wireless device found");
|
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) {
|
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;
|
u32 card = CARD_BG;
|
||||||
i64 x = card_x + 40;
|
i64 x = card_x + 40;
|
||||||
i64 y = card_y + 132 + (i64)row * 64;
|
i64 y = card_y + 132 + (i64)row * 56;
|
||||||
u32 fill = active == row ? 0xFFFFFFFF : (net->selected == row ? 0xFFDCECF8 : INPUT_BG);
|
u32 fill = active == row ? 0xFF45454A : (net->selected == row ? 0xFF3A3A3E : INPUT_BG);
|
||||||
fill_rounded_rect(fb, x, y, 390, 50, 9, fill);
|
fill_rounded_rect(fb, x, y, 390, 46, 3, fill);
|
||||||
stroke_rounded_rect(fb, x, y, 390, 50, 9, 1, active == row ? 0xFF7FA7C8 : card);
|
stroke_rounded_rect(fb, x, y, 390, 46, 3, 1, active == row ? 0xFF6C6C72 : 0xFF404044);
|
||||||
fb_text(fb, net->selected == row ? ">" : " ", x + 12, y + 16, fg, fill);
|
fb_text(fb, net->selected == row ? ">" : " ", x + 12, y + 14, fg, fill);
|
||||||
if (row < g_wifi_scan_count) {
|
if (row < g_wifi_scan_count) {
|
||||||
fb_text(fb, g_wifi_ssids[row], x + 42, y + 8, fg, fill);
|
fb_text(fb, g_wifi_ssids[row], x + 42, y + 6, fg, fill);
|
||||||
fb_text(fb, g_wifi_meta[row], x + 42, y + 28, 0xFF7F97AC, fill);
|
fb_text(fb, g_wifi_meta[row], x + 42, y + 26, 0xFF808088, fill);
|
||||||
} else {
|
} else {
|
||||||
/* 无扫描结果时的占位行 */
|
|
||||||
const char *placeholder = (g_wifi_scan_tried && g_wifi_scan_count == 0)
|
const char *placeholder = (g_wifi_scan_tried && g_wifi_scan_count == 0)
|
||||||
? "No networks found" : "(empty)";
|
? "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) {
|
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;
|
u32 card = CARD_BG;
|
||||||
cursor_erase(fb);
|
cursor_erase(fb);
|
||||||
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
|
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
|
||||||
draw_card(fb, card_x, card_y, 900, 600, card, bg);
|
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_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);
|
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, "Network name (SSID)", card_x + 470, card_y + 120, 0xFF808088, card);
|
||||||
fb_text(fb, "Highlighted network", card_x + 494, card_y + 148, 0xFF7F97AC, INPUT_BG);
|
draw_rounded_input(fb, card_x + 470, card_y + 148, 390, 44, net->ssid, 0, fg, card, active == NET_ACT_SSID);
|
||||||
int preview = active < WIFI_SCAN_COUNT ? active : net->selected;
|
|
||||||
if (preview < g_wifi_scan_count) {
|
fb_text(fb, "Password", card_x + 470, card_y + 210, 0xFF808088, card);
|
||||||
fb_text(fb, g_wifi_ssids[preview], card_x + 494, card_y + 182, fg, INPUT_BG);
|
draw_rounded_input(fb, card_x + 470, card_y + 238, 390, 44, net->password, 1, fg, card, active == NET_ACT_PASS);
|
||||||
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);
|
u32 btn_bg = active == NET_ACT_SAVE ? 0xFF45454A : 0xFF3A3A3E;
|
||||||
fb_text(fb, g_wifi_scan_tried ? "Check ath9k driver" : "Run ath9k driver first",
|
fill_rounded_rect(fb, card_x + 470, card_y + 310, 180, 44, 3, btn_bg);
|
||||||
card_x + 494, card_y + 210, 0xFF9AACC0, INPUT_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);
|
||||||
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);
|
/* 连接按钮 */
|
||||||
|
{
|
||||||
|
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);
|
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) {
|
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;
|
int active = 0, mcnt = 0, e0 = 0, shift = 0;
|
||||||
u8 mpkt[3]; u8 prev_btns = 0;
|
u8 mpkt[3]; u8 prev_btns = 0;
|
||||||
int len = 0;
|
int plen = 0;
|
||||||
while (net->password[len]) len++;
|
int slen = 0;
|
||||||
|
while (net->password[plen]) plen++;
|
||||||
|
while (net->ssid[slen]) slen++;
|
||||||
net_select(net, net->selected);
|
net_select(net, net->selected);
|
||||||
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
||||||
for (;;) {
|
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);
|
cursor_draw(fb, *mx, *my);
|
||||||
}
|
}
|
||||||
if ((btns & 1) && !(prev_btns & 1)) {
|
if ((btns & 1) && !(prev_btns & 1)) {
|
||||||
|
/* WiFi 列表点击 */
|
||||||
for (int r=0; r<WIFI_SCAN_COUNT; r++) {
|
for (int r=0; r<WIFI_SCAN_COUNT; r++) {
|
||||||
i64 ry = card_y + 132 + (i64)r * 64;
|
i64 ry = card_y + 132 + (i64)r * 56;
|
||||||
if (*mx >= card_x + 40 && *mx < card_x + 430 && *my >= ry && *my < ry + 50) {
|
if (*mx >= card_x + 40 && *mx < card_x + 430 && *my >= ry && *my < ry + 46) {
|
||||||
active = r;
|
active = r;
|
||||||
net_select(net, 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);
|
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) {
|
/* SSID 输入框 */
|
||||||
active = WIFI_SCAN_COUNT;
|
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);
|
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;
|
net->connected = 1;
|
||||||
logl("[FirstInit] wireless profile selected");
|
logl("[FirstInit] network profile saved");
|
||||||
redraw_network_card(fb, card_x, card_y, net, WIFI_SCAN_COUNT + 1, *mx, *my, bg, fg);
|
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();
|
for (int i=0;i<30;i++) delay_frame();
|
||||||
return;
|
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;
|
e0=0; continue;
|
||||||
}
|
}
|
||||||
if (e0 && sc == 0x50) {
|
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);
|
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
||||||
e0=0; continue;
|
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 (c == '\n') {
|
||||||
if (active < WIFI_SCAN_COUNT) {
|
if (active < WIFI_SCAN_COUNT) {
|
||||||
net_select(net, active);
|
net_select(net, active);
|
||||||
len = 0;
|
slen = 0;
|
||||||
active = WIFI_SCAN_COUNT;
|
while (net->ssid[slen]) slen++;
|
||||||
} else if (active == WIFI_SCAN_COUNT) {
|
active = NET_ACT_PASS;
|
||||||
active = WIFI_SCAN_COUNT + 1;
|
} else if (active == NET_ACT_SSID) {
|
||||||
} else {
|
active = NET_ACT_PASS;
|
||||||
|
} else if (active == NET_ACT_PASS) {
|
||||||
|
active = NET_ACT_CONNECT;
|
||||||
|
} else if (active == NET_ACT_SAVE) {
|
||||||
net->connected = 1;
|
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);
|
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
||||||
for (int i=0;i<30;i++) delay_frame();
|
for (int i=0;i<30;i++) delay_frame();
|
||||||
return;
|
return;
|
||||||
@@ -945,16 +1229,28 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c == '\t') {
|
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);
|
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (active == WIFI_SCAN_COUNT) {
|
/* SSID 输入 */
|
||||||
|
if (active == NET_ACT_SSID) {
|
||||||
if (c == 8) {
|
if (c == 8) {
|
||||||
if (len > 0) net->password[--len] = 0;
|
if (slen > 0) net->ssid[--slen] = 0;
|
||||||
} else if (len < 63 && c >= 32 && c <= 126) {
|
} else if (slen < 31 && c >= 32 && c <= 126) {
|
||||||
net->password[len++] = c;
|
net->ssid[slen++] = c;
|
||||||
net->password[len] = 0;
|
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;
|
net->connected = 0;
|
||||||
redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg);
|
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];}
|
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) {
|
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);
|
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=";
|
const char *pre="DESHAB_USERCONF_V1\ncomputer=";
|
||||||
for(int i=0;pre[i];i++) conf[p++]=pre[i];
|
for(int i=0;pre[i];i++) conf[p++]=pre[i];
|
||||||
for(int i=0;pc[i]&&p<740;i++) conf[p++]=pc[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 *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];
|
const char *dns="\nnetwork.dns=auto"; for(int i=0;dns[i];i++) conf[p++]=dns[i];
|
||||||
conf[p++]='\n';
|
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];
|
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")))
|
__attribute__((visibility("default")))
|
||||||
void dsk_entry(const dsk_boot_context *ctx) {
|
void dsk_entry(const dsk_boot_context *ctx) {
|
||||||
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
|
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
|
||||||
logl("[FirstInit] boot");
|
logl("[FirstInit] boot");
|
||||||
|
logl("[FirstInit] calibrating TSC (实机 CPU 频率计算)");
|
||||||
|
tsc_calibrate_fi();
|
||||||
|
|
||||||
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
|
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
|
||||||
logl("[FirstInit] bad context");
|
logl("[FirstInit] bad context");
|
||||||
@@ -1049,13 +1351,20 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
fade_ring(fb, cx, cy, bg);
|
fade_ring(fb, cx, cy, bg);
|
||||||
logl("[FirstInit] ring faded");
|
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;
|
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");
|
logl("[FirstInit] fading in welcome");
|
||||||
for (u32 a=0; a<=255; a+=17) {
|
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_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();
|
delay_frame();
|
||||||
}
|
}
|
||||||
logl("[FirstInit] welcome drawn");
|
logl("[FirstInit] welcome drawn");
|
||||||
@@ -1064,16 +1373,26 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
|
|
||||||
logl("[FirstInit] cross-fading to setup message");
|
logl("[FirstInit] cross-fading to setup message");
|
||||||
i64 tw2 = g_txt_setup_w, tx2 = cx - tw2/2;
|
i64 tw2 = g_txt_setup_w, tx2 = cx - tw2/2;
|
||||||
i64 area_x = tx2 < tx ? tx2 : tx;
|
const char *setup_en = "Let's set up your system";
|
||||||
i64 area_w = (tx2 + tw2 > tx + tw ? tx2 + tw2 : tx + tw) - area_x;
|
i64 en2_w = fb_text_width(setup_en);
|
||||||
i64 area_h = (g_txt_setup_h > g_txt_welcome_h ? g_txt_setup_h : g_txt_welcome_h) + 8;
|
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++) {
|
for (u32 step=0; step<=15; step++) {
|
||||||
u32 a2 = step * 17;
|
u32 a2 = step * 17;
|
||||||
u32 a1 = 255 - a2;
|
u32 a1 = 255 - a2;
|
||||||
fill_gradient_rect(fb, area_x-4, ty-4, area_w+8, area_h);
|
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_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_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();
|
delay_frame();
|
||||||
}
|
}
|
||||||
logl("[FirstInit] setup message drawn");
|
logl("[FirstInit] setup message drawn");
|
||||||
@@ -1089,13 +1408,14 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
logl("[FI] fadein start");
|
logl("[FI] fadein start");
|
||||||
/* Phase 1: fade in card background only (no title) — avoids title/card color mismatch flicker */
|
/* Phase 1: fade in card background only (no title) — avoids title/card color mismatch flicker */
|
||||||
for (u32 a=0; a<=255; a+=17) {
|
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();
|
delay_frame();
|
||||||
}
|
}
|
||||||
/* Phase 2: draw title once on fully-rendered card */
|
/* 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);
|
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_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("[FI] fadein done");
|
||||||
logl("[FirstInit] initializing PS/2 mouse");
|
logl("[FirstInit] initializing PS/2 mouse");
|
||||||
@@ -1103,10 +1423,89 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
i64 mx = cx, my = cy;
|
i64 mx = cx, my = cy;
|
||||||
{
|
{
|
||||||
int field = 0;
|
int field = 0;
|
||||||
while (field < 3) {
|
for (;;) {
|
||||||
field = read_field(fb, card_x, card_y, field, pc, user, pass, &mx, &my, bg, fg);
|
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;
|
setup_prefs prefs;
|
||||||
prefs.language = 0; /* zh-CN */
|
prefs.language = 0; /* zh-CN */
|
||||||
prefs.region = 0; /* CN */
|
prefs.region = 0; /* CN */
|
||||||
@@ -1114,6 +1513,10 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
prefs.keyboard = 0; /* US-QWERTY */
|
prefs.keyboard = 0; /* US-QWERTY */
|
||||||
prefs.theme = 0; /* Light */
|
prefs.theme = 0; /* Light */
|
||||||
read_prefs_page(fb, card_x, card_y, &prefs, &mx, &my, bg, fg);
|
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 扫描结果(来自 ath9k 驱动通过 kernel_api.net.scan_*) */
|
||||||
wifi_query_scan(ctx->dkm_kernel_api);
|
wifi_query_scan(ctx->dkm_kernel_api);
|
||||||
setup_net net;
|
setup_net net;
|
||||||
@@ -1121,23 +1524,36 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
net.connected = 0;
|
net.connected = 0;
|
||||||
net.ssid[0] = 0;
|
net.ssid[0] = 0;
|
||||||
net.password[0] = 0;
|
net.password[0] = 0;
|
||||||
if (g_no_wireless_device) {
|
/* 始终显示网络设置页面,即使无无线设备也展示页面让用户确认 */
|
||||||
/* QEMU 或无无线网卡场景:跳过 WiFi 设置页面,network.mode=disabled */
|
cursor_bg_valid = 0;
|
||||||
logl("[FirstInit] no wireless device, skipping WiFi setup page");
|
cursor_cur_x = -100;
|
||||||
net.connected = 0;
|
cursor_cur_y = -100;
|
||||||
} else {
|
logl("[FirstInit] entering network setup page");
|
||||||
cursor_bg_valid = 0;
|
read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg);
|
||||||
cursor_cur_x = -100;
|
logl("[FirstInit] network setup page returned");
|
||||||
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");
|
|
||||||
}
|
|
||||||
build_user_conf(pc, user, pass, &prefs, &net);
|
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);
|
cursor_erase(fb);
|
||||||
fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648);
|
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");
|
logl("[FirstInit] user setup finished");
|
||||||
|
|
||||||
/* 短暂停留后返回,由 DSK 接管加载 shell */
|
/* 短暂停留后返回,由 DSK 接管加载 shell */
|
||||||
|
|||||||
Binary file not shown.
@@ -1,97 +1,97 @@
|
|||||||
/* Auto-generated */
|
/* Auto-generated */
|
||||||
|
|
||||||
static const int g_txt_welcome_w = 360;
|
static const int g_txt_welcome_w = 270;
|
||||||
static const int g_txt_welcome_h = 54;
|
static const int g_txt_welcome_h = 42;
|
||||||
static const unsigned char g_txt_welcome[360*54] = {
|
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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_setup_w = 384;
|
static const int g_txt_setup_w = 576;
|
||||||
static const int g_txt_setup_h = 30;
|
static const int g_txt_setup_h = 42;
|
||||||
static const unsigned char g_txt_setup[384*30] = {
|
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
|
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,
|
||||||
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,
|
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,
|
||||||
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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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;
|
static const int g_txt_title_w = 240;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ from PIL import Image, ImageFont, ImageDraw
|
|||||||
ttf = "SYSTEM/system/font/simhei.ttf"
|
ttf = "SYSTEM/system/font/simhei.ttf"
|
||||||
|
|
||||||
texts = [
|
texts = [
|
||||||
("welcome", "欢迎使用 Deshab", 48), # only welcome stays large
|
("welcome", "欢迎使用 Deshab", 36),
|
||||||
("setup", "接下来让我们来引导你设置你的系统", 24), # 32 -> 24
|
("setup", "接下来让我们来引导你设置你的系统", 36),
|
||||||
("title", "创建你的 Deshab 账户", 24),
|
("title", "创建你的 Deshab 账户", 24),
|
||||||
("computer", "计算机名", 24),
|
("computer", "计算机名", 24),
|
||||||
("username", "用户名", 24),
|
("username", "用户名", 24),
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
CC := clang
|
CC := clang
|
||||||
LD := ld.lld
|
LD := ld.lld
|
||||||
|
|
||||||
ROOT := ../../..
|
ROOT := ../..
|
||||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/bash.elf
|
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
|
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||||
|
|
||||||
C_SOURCES := main.c
|
C_SOURCES := main.c
|
||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(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)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(5);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(6);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(5);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(6);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
@@ -249,8 +249,48 @@ static u16 udp_checksum_ipv4(const u8 *ip, const u8 *udp, u32 udp_len) {
|
|||||||
return out ? out : 0xffff;
|
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) {
|
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 */
|
/* 构造并发送一个 DHCP 报文。type: 1=DISCOVER 3=REQUEST */
|
||||||
@@ -617,6 +657,8 @@ __attribute__((visibility("default")))
|
|||||||
void dsk_entry(const dsk_boot_context *ctx) {
|
void dsk_entry(const dsk_boot_context *ctx) {
|
||||||
logl("[netman] boot");
|
logl("[netman] boot");
|
||||||
if (!ctx || ctx->magic != DSK_BOOT_MAGIC) { logl("[netman] bad context"); return; }
|
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;
|
u64 api = ctx->dkm_kernel_api;
|
||||||
if (api) {
|
if (api) {
|
||||||
u64 block_api = *(u64 *)(api + 0xA8);
|
u64 block_api = *(u64 *)(api + 0xA8);
|
||||||
|
|||||||
Binary file not shown.
+12
-4
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(5);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(6);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
@@ -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__ 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__ 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) {
|
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);
|
outb(COM1, (unsigned char)c);
|
||||||
}
|
}
|
||||||
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
|
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 */
|
/* ASCII 字体 — 必须在 deshab_ui.h 之前包含,因为 du_draw_char 引用 g_ascii */
|
||||||
#include "../firstInit/ascii_bitmaps.c"
|
#include "../firstInit/ascii_bitmaps.c"
|
||||||
#include "../UTSM/include/utsm/deshab_ui.h"
|
#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 语义色) ---- */
|
/* ---- 风格令牌(引用 deshab_ui.h 语义色) ---- */
|
||||||
#define BG_COLOR DS_DARK_BG_PRIMARY
|
#define BG_COLOR DS_DARK_BG_PRIMARY
|
||||||
@@ -114,7 +584,11 @@ static void term_newline(void) {
|
|||||||
if (cur_row >= term_h) term_scroll();
|
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) {
|
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 == '\n') { term_newline(); return; }
|
||||||
if (c == '\r') { cur_col = 0; return; }
|
if (c == '\r') { cur_col = 0; return; }
|
||||||
if (c == '\t') {
|
if (c == '\t') {
|
||||||
@@ -220,6 +694,74 @@ static char input_buf[256];
|
|||||||
static int input_len = 0;
|
static int input_len = 0;
|
||||||
static int input_cursor = 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) ---- */
|
/* ---- 键盘扫描码 → ASCII(Set 1) ---- */
|
||||||
static char scan_to_ascii(u8 sc, int shift) {
|
static char scan_to_ascii(u8 sc, int shift) {
|
||||||
static const char normal[58] = {
|
static const char normal[58] = {
|
||||||
@@ -248,16 +790,25 @@ static void cmd_help(void) {
|
|||||||
term_puts_color("Deshab Shell — 内置命令列表:\n", PROMPT_FG);
|
term_puts_color("Deshab Shell — 内置命令列表:\n", PROMPT_FG);
|
||||||
term_puts(" help 显示此帮助\n");
|
term_puts(" help 显示此帮助\n");
|
||||||
term_puts(" clear 清屏\n");
|
term_puts(" clear 清屏\n");
|
||||||
term_puts(" echo <text> 显示文本\n");
|
term_puts(" echo <text> 显示文本(-n 不换行, > file 重定向写盘)\n");
|
||||||
term_puts(" version 显示系统版本\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(" uname 显示系统名称\n");
|
||||||
term_puts(" date 显示当前日期\n");
|
term_puts(" date 显示当前日期\n");
|
||||||
term_puts(" ver 显示内核版本(同 version)\n");
|
|
||||||
term_puts(" about 关于 Deshab\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(" reboot 重启系统\n");
|
||||||
term_puts(" halt 关机(停止 CPU)\n");
|
term_puts(" halt 关机(停止 CPU)\n");
|
||||||
term_puts(" ls 列出根目录文件(暂未实现,需要 VFS)\n");
|
|
||||||
term_puts(" pci 列出 PCI 设备(暂未实现)\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_version(void) {
|
static void cmd_version(void) {
|
||||||
@@ -301,9 +852,45 @@ static void cmd_halt(void) {
|
|||||||
for(;;) __asm__("hlt");
|
for(;;) __asm__("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* echo:支持 -n(不换行)和 " > file" 重定向写盘 */
|
||||||
static void cmd_echo(const char *args) {
|
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);
|
if (args && *args) term_puts_color(args, TEXT_FG);
|
||||||
term_putc('\n');
|
if (newline) term_putc('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 read_rtc_reg(u8 reg);
|
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);
|
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) {
|
static void execute_command(const char *cmd) {
|
||||||
while (*cmd == ' ') cmd++;
|
while (*cmd == ' ') cmd++;
|
||||||
if (*cmd == 0) return;
|
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, "halt") || str_eq(name, "shutdown")) cmd_halt();
|
||||||
else if (str_eq(name, "echo")) cmd_echo(args);
|
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, "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, "ls") || str_eq(name, "dir")) cmd_ls();
|
||||||
else if (str_eq(name, "pci")) cmd_not_impl("pci");
|
else if (str_eq(name, "pci")) cmd_pci();
|
||||||
else if (str_eq(name, "cat")) cmd_not_impl("cat");
|
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, "cd")) cmd_not_impl("cd");
|
||||||
else if (str_eq(name, "pwd")) { term_puts("/\n"); }
|
else if (str_eq(name, "pwd")) { term_puts("/\n"); }
|
||||||
else if (str_eq(name, "whoami")) { term_puts("root\n"); }
|
else if (str_eq(name, "whoami")) { term_puts("root\n"); }
|
||||||
@@ -497,6 +1682,54 @@ static void kill_line(void) {
|
|||||||
redraw_input_line();
|
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")))
|
__attribute__((visibility("default")))
|
||||||
void dsk_entry(const dsk_boot_context *ctx) {
|
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_h = ctx->framebuffer_height;
|
||||||
fb_p = ctx->framebuffer_pitch;
|
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);
|
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 — 使用风格令牌 */
|
/* 启动 banner — 使用风格令牌 */
|
||||||
term_puts_color("=== Deshab OS v0.1.0 ===\n", ACCENT_FG);
|
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_puts_color("输入 'help' 查看可用命令\n", DIM_FG);
|
||||||
term_putc('\n');
|
term_putc('\n');
|
||||||
|
|
||||||
|
/* 开发者模式:DSK 设 reserved[3]=1 时自动跑命令测试序列 */
|
||||||
|
if (ctx->reserved[3] == 1) {
|
||||||
|
g_serial_mirror = 1;
|
||||||
|
run_dev_tests();
|
||||||
|
}
|
||||||
|
|
||||||
/* 主循环 */
|
/* 主循环 */
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
int e0 = 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 == 0x47) cursor_home();
|
||||||
else if (sc == 0x4F) cursor_end();
|
else if (sc == 0x4F) cursor_end();
|
||||||
else if (sc == 0x53) delete_char_fwd();
|
else if (sc == 0x53) delete_char_fwd();
|
||||||
else if (sc == 0x48) { }
|
else if (sc == 0x48) { history_prev(); }
|
||||||
else if (sc == 0x50) { }
|
else if (sc == 0x50) { history_next(); }
|
||||||
e0 = 0;
|
e0 = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sc == 0x1C) {
|
if (sc == 0x1C) {
|
||||||
term_clear_cursor();
|
term_clear_cursor();
|
||||||
input_buf[input_len] = 0;
|
input_buf[input_len] = 0;
|
||||||
|
/* 提交命令前先入历史; 执行后再清空输入 */
|
||||||
|
history_push(input_buf);
|
||||||
|
g_history_view = -1;
|
||||||
term_putc('\n');
|
term_putc('\n');
|
||||||
execute_command(input_buf);
|
execute_command(input_buf);
|
||||||
|
input_len = 0;
|
||||||
|
input_cursor = 0;
|
||||||
|
input_buf[0] = 0;
|
||||||
cmd_done = 1;
|
cmd_done = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sc == 0x0E) { delete_char_back(); 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 == 0x15 && !shift) { kill_line(); continue; }
|
||||||
if (sc == 0x17 && !shift) { while (input_cursor > 0) delete_char_back(); continue; }
|
if (sc == 0x17 && !shift) { while (input_cursor > 0) delete_char_back(); continue; }
|
||||||
if (sc == 0x0B && !shift) { kill_to_end(); continue; }
|
if (sc == 0x0B && !shift) { kill_to_end(); continue; }
|
||||||
|
|||||||
Binary file not shown.
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(7);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(7);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(7);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(7);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
@@ -1,11 +1,11 @@
|
|||||||
/* Deshab curl — 终端风格 HTTP 客户端 stub
|
/* Deshab curl — 终端风格 HTTP 客户端
|
||||||
* 命令行输入 URL,显示 HTTP GET 请求结果。
|
* 命令行输入 URL,发送 HTTP/1.0 GET 请求并打印响应(net_stack.h 共享协议栈)。
|
||||||
* 当前为 stub,提示需要 TCP/IP 栈支持。
|
|
||||||
* Esc 键退出返回 DSK。
|
* Esc 键退出返回 DSK。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../../firstInit/ascii_bitmaps.c"
|
#include "../../firstInit/ascii_bitmaps.c"
|
||||||
#include "../desktop_app.h"
|
#include "../desktop_app.h"
|
||||||
|
#include "../net_stack.h"
|
||||||
|
|
||||||
/* ---- 终端参数 ---- */
|
/* ---- 终端参数 ---- */
|
||||||
#define TERM_COLS 120
|
#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);
|
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) {
|
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("GET ", DA_PROMPT_COLOR);
|
||||||
term_puts_color(url, DA_ACCENT_LIGHT);
|
term_puts_color(url, DA_ACCENT_LIGHT);
|
||||||
term_putc('\n');
|
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(" -> 解析 ", DA_TEXT_DIM);
|
||||||
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
/* 尝试读取 net API — 当前为 stub */
|
char ipstr[24]; ns_fmt_ip(ip, ipstr);
|
||||||
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
|
term_puts_color(" -> ", DA_TEXT_DIM);
|
||||||
if (!net_api) {
|
term_puts_color(ipstr, DA_TEXT_DIM);
|
||||||
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
|
term_putc('\n');
|
||||||
term_puts_color(" 当前内核未实现网络协议栈。\n", DA_WARNING);
|
|
||||||
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
|
/* 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;
|
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_clear(void) { term_clear(); }
|
||||||
|
|
||||||
static void cmd_version(void) {
|
static void cmd_version(void) {
|
||||||
term_puts_color("Deshab curl v0.1 (stub)\n", DA_SUCCESS);
|
term_puts_color("Deshab curl v1.0\n", DA_SUCCESS);
|
||||||
term_puts_color(" HTTP 客户端功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
|
term_puts_color(" HTTP/1.0 GET via net_stack (e1000 + slirp)\n", DA_TEXT_DIM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execute_command(const char *cmd) {
|
static void execute_command(const char *cmd) {
|
||||||
@@ -231,10 +323,9 @@ void dsk_entry(const da_boot_context *ctx) {
|
|||||||
term_clear();
|
term_clear();
|
||||||
|
|
||||||
/* 启动 banner */
|
/* 启动 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("HTTP 命令行客户端\n", DA_SUCCESS);
|
||||||
term_puts_color("输入 URL 发送 GET 请求 | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
|
term_puts_color("输入 URL 发送 GET 请求 | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
|
||||||
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
|
|
||||||
term_putc('\n');
|
term_putc('\n');
|
||||||
|
|
||||||
redraw_all();
|
redraw_all();
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(7);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(7);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(7);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(7);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
@@ -2,9 +2,10 @@ ENTRY(dsk_entry)
|
|||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
{
|
{
|
||||||
text PT_LOAD FLAGS(7);
|
text PT_LOAD FLAGS(5);
|
||||||
rodata PT_LOAD FLAGS(4);
|
rodata PT_LOAD FLAGS(4);
|
||||||
data PT_LOAD FLAGS(7);
|
data PT_LOAD FLAGS(6);
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@@ -12,5 +13,12 @@ SECTIONS
|
|||||||
. = 0x1000;
|
. = 0x1000;
|
||||||
.text : ALIGN(4K) { *(.text*) } :text
|
.text : ALIGN(4K) { *(.text*) } :text
|
||||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
.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
@@ -1,11 +1,11 @@
|
|||||||
/* Deshab ping — 终端风格 ICMP Echo 工具 stub
|
/* Deshab ping — 终端风格 ICMP Echo 工具
|
||||||
* 命令行输入 IP 地址,发送 ICMP Echo Request。
|
* 命令行输入 IP 地址,发送 ICMP Echo Request(net_stack.h 共享协议栈)。
|
||||||
* 当前为 stub,提示需要 TCP/IP 栈支持。
|
|
||||||
* Esc 键退出返回 DSK。
|
* Esc 键退出返回 DSK。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../../firstInit/ascii_bitmaps.c"
|
#include "../../firstInit/ascii_bitmaps.c"
|
||||||
#include "../desktop_app.h"
|
#include "../desktop_app.h"
|
||||||
|
#include "../net_stack.h"
|
||||||
|
|
||||||
/* ---- 终端参数 ---- */
|
/* ---- 终端参数 ---- */
|
||||||
#define TERM_COLS 120
|
#define TERM_COLS 120
|
||||||
@@ -137,32 +137,84 @@ static int is_valid_ip(const char *s) {
|
|||||||
return dots == 3;
|
return dots == 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Ping 模拟 ---- */
|
/* ---- Ping 实现(net_stack.h) ---- */
|
||||||
static void do_ping(const char *ip) {
|
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("PING ", DA_PROMPT_COLOR);
|
||||||
term_puts_color(ip, DA_ACCENT_LIGHT);
|
term_puts_color(ipstr, DA_ACCENT_LIGHT);
|
||||||
term_puts_color(" 56(84) bytes of data.\n", DA_TEXT_DIM);
|
term_puts_color(" 32 data bytes\n", DA_TEXT_DIM);
|
||||||
|
|
||||||
/* stub: 检查 kernel_api 是否有 net 接口 */
|
int sent = 0, recv = 0;
|
||||||
if (!g_ac.kernel_api) {
|
u32 sum = 0, min = 0xFFFFFFFFu, max = 0;
|
||||||
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
|
for (u16 seq = 1; seq <= 4; seq++) {
|
||||||
return;
|
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);
|
char nb[12];
|
||||||
if (!net_api) {
|
ns_u32_dec(nb, (u32)sent);
|
||||||
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
|
term_puts_color("--- ", DA_TEXT_DIM);
|
||||||
term_puts_color(" 当前内核未实现 ICMP 协议栈。\n", DA_WARNING);
|
term_puts_color(ipstr, DA_TEXT_DIM);
|
||||||
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
|
term_puts_color(" ping statistics ---\n ", DA_TEXT_DIM);
|
||||||
term_putc('\n');
|
term_puts(nb);
|
||||||
term_puts_color("--- ", DA_TEXT_DIM);
|
term_puts(" transmitted, ");
|
||||||
term_puts_color(ip, DA_TEXT_DIM);
|
ns_u32_dec(nb, (u32)recv);
|
||||||
term_puts_color(" ping statistics ---\n", DA_TEXT_DIM);
|
term_puts(nb);
|
||||||
term_puts_color(" 0 packets transmitted, 0 received, 100% packet loss\n", DA_ERROR);
|
term_puts(" received, ");
|
||||||
return;
|
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');
|
||||||
/* 如果有 net API,此处应实现真正的 ICMP Echo */
|
|
||||||
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 内置命令 ---- */
|
/* ---- 内置命令 ---- */
|
||||||
@@ -183,8 +235,8 @@ static void cmd_help(void) {
|
|||||||
static void cmd_clear(void) { term_clear(); }
|
static void cmd_clear(void) { term_clear(); }
|
||||||
|
|
||||||
static void cmd_version(void) {
|
static void cmd_version(void) {
|
||||||
term_puts_color("Deshab ping v0.1 (stub)\n", DA_SUCCESS);
|
term_puts_color("Deshab ping v1.0\n", DA_SUCCESS);
|
||||||
term_puts_color(" ICMP Echo 功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
|
term_puts_color(" ICMP Echo via net_stack (e1000 + slirp)\n", DA_TEXT_DIM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execute_command(const char *cmd) {
|
static void execute_command(const char *cmd) {
|
||||||
@@ -246,10 +298,9 @@ void dsk_entry(const da_boot_context *ctx) {
|
|||||||
term_clear();
|
term_clear();
|
||||||
|
|
||||||
/* 启动 banner */
|
/* 启动 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("ICMP Echo 请求工具\n", DA_SUCCESS);
|
||||||
term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
|
term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
|
||||||
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
|
|
||||||
term_putc('\n');
|
term_putc('\n');
|
||||||
|
|
||||||
redraw_all();
|
redraw_all();
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal
|
||||||
|
|
||||||
set "SCRIPT_DIR=%~dp0"
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,14 @@
|
|||||||
# Deshab user configuration placeholder
|
DESHAB_USERCONF_V1
|
||||||
# Runtime write is pending FAT32 write support.
|
computer=DESHAB-PC
|
||||||
version=1
|
username=admin
|
||||||
computerName=
|
passwordSha256=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
|
||||||
username=
|
locale=zh-CN
|
||||||
passwordSha256=
|
region=CN
|
||||||
configEncrypted=0
|
timezone=Asia/Shanghai
|
||||||
|
keyboard=us-qwerty
|
||||||
|
theme=dark
|
||||||
|
network.mode=disabled
|
||||||
|
network.ssid=
|
||||||
|
network.password=
|
||||||
|
network.ip=dhcp
|
||||||
|
network.dns=auto
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1,2 @@
|
|||||||
0
|
0
|
||||||
|
1
|
||||||
Binary file not shown.
@@ -93,12 +93,12 @@ function Get-Crc32([byte[]]$Data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Test-ShortFatName([string]$Name) {
|
function Test-ShortFatName([string]$Name) {
|
||||||
if ($Name -match '[a-z]') { return $false }
|
if ($Name -cmatch '[a-z]') { return $false }
|
||||||
$parts = $Name.Split('.')
|
$parts = $Name.Split('.')
|
||||||
if ($parts.Count -gt 2) { return $false }
|
if ($parts.Count -gt 2) { return $false }
|
||||||
if ($parts[0].Length -lt 1 -or $parts[0].Length -gt 8) { return $false }
|
if ($parts[0].Length -lt 1 -or $parts[0].Length -gt 8) { return $false }
|
||||||
if ($parts.Count -eq 2 -and $parts[1].Length -gt 3) { return $false }
|
if ($parts.Count -eq 2 -and $parts[1].Length -gt 3) { return $false }
|
||||||
return $Name -match '^[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+(\.[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+)?$'
|
return ($Name -cmatch '^[A-Z0-9_]+$') -or ($Name -cmatch '^[A-Z0-9_]+\.[A-Z0-9_]+$')
|
||||||
}
|
}
|
||||||
|
|
||||||
function New-ShortFatName([string]$LongName, $Used) {
|
function New-ShortFatName([string]$LongName, $Used) {
|
||||||
@@ -513,6 +513,24 @@ if (-not (Test-Path $FirstInitOutput)) {
|
|||||||
}
|
}
|
||||||
Write-Host "[build] FirstInit Output: $FirstInitOutput"
|
Write-Host "[build] FirstInit Output: $FirstInitOutput"
|
||||||
|
|
||||||
|
$LoginDir = Join-Path $Root 'CODE\login'
|
||||||
|
$LoginOutput = Join-Path $SystemDir 'system\user\use\login.elf'
|
||||||
|
|
||||||
|
Write-Host '[build] Building login.elf...'
|
||||||
|
try {
|
||||||
|
& $make -C "$LoginDir" -f MAKEFILE "CC=$clang" "LD=$lld"
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "make login failed with exit code $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $LoginOutput)) {
|
||||||
|
throw "login build did not produce $LoginOutput"
|
||||||
|
}
|
||||||
|
Write-Host "[build] login Output: $LoginOutput"
|
||||||
|
|
||||||
$MouseDir = Join-Path $Root 'CODE\mouse'
|
$MouseDir = Join-Path $Root 'CODE\mouse'
|
||||||
$MouseOutput = Join-Path $SystemDir 'system\deshab64\mouse\mouseInit.elf'
|
$MouseOutput = Join-Path $SystemDir 'system\deshab64\mouse\mouseInit.elf'
|
||||||
|
|
||||||
@@ -589,7 +607,7 @@ Write-Host "[build] desktop Output: $DesktopOutput"
|
|||||||
$ToolsDir = Join-Path $Root 'CODE\tools'
|
$ToolsDir = Join-Path $Root 'CODE\tools'
|
||||||
$ToolsOutDir = Join-Path $SystemDir 'system\deshab64\tools'
|
$ToolsOutDir = Join-Path $SystemDir 'system\deshab64\tools'
|
||||||
|
|
||||||
$toolApps = @('bash', 'editor', 'fileman', 'browser', 'curl', 'ping')
|
$toolApps = @('editor', 'fileman', 'browser', 'curl', 'ping')
|
||||||
|
|
||||||
foreach ($tool in $toolApps) {
|
foreach ($tool in $toolApps) {
|
||||||
$toolDir = Join-Path $ToolsDir $tool
|
$toolDir = Join-Path $ToolsDir $tool
|
||||||
@@ -607,6 +625,9 @@ foreach ($tool in $toolApps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Host '[build] Building DKM framebuffer driver...'
|
||||||
|
Build-DkmDriver (Join-Path $Root 'CODE\DKM\console_fb\console_fb.c') (Join-Path $SystemDir 'driver\console\console_fb.drv')
|
||||||
|
|
||||||
Write-Host '[build] Building DKM network drivers...'
|
Write-Host '[build] Building DKM network drivers...'
|
||||||
Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv')
|
Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv')
|
||||||
Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv')
|
Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv')
|
||||||
|
|||||||
Reference in New Issue
Block a user