diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c48c072 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +ISO/deshab.img diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d5aba54 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,154 @@ +# Deshab 项目上下文 + +## 项目概览 + +Deshab 当前包含一个内核/系统实验方向,其中启动内核与主内核目标为: + +```text +CODE/UTSM -> SYSTEM/boot/utsm.elf +CODE/dsk -> SYSTEM/system/deshab64/deshab.elf +``` + +`utsm.elf` 是当前由 bootloader 直接加载的首阶段内核,负责 UTSM、DKM 驱动加载、平台发现和早期系统服务。当前已实现两阶段 DSK 加载:优先通过 `kernel_api.block` + 内嵌 FAT32 解析从真实块设备读取 `/system/deshab64/deshab.elf`(在 SATA FAT32 盘场景已验证);无块设备时自动回退 Limine boot module 预加载。 + +DSK 主内核(`deshab.elf`)当前已实现: +- 内嵌 FAT32 只读解析器(256 扇区批量读取 + 内存内文件查找) +- PIE ELF loader(PT_LOAD + PT_DYNAMIC + R_X86_64_RELATIVE 重定位) +- 旋转加载动画(双缓冲精灵、顺时针、comet-tail 渐变弧) +- 首次启动检测:读取 `firstInit.txt`,值为 `0` 则触发首次启动流程 +- 系统初始化调度:DSK 直接加载并调用 `mouseInit.elf`(PS/2 鼠标安全初始化) +- 用户设置向导:DSK 加载并跳转 `FirstInit.elf`(圆角卡片 UI、键盘输入、SHA256 密码、加密配置缓冲) + +FirstInit(`CODE/firstInit`)只负责用户级设置向导: +- 淡蓝背景 → 旋转环淡出 → "欢迎使用 Deshab" → 设置提示 → 账户设置卡片 +- PS/2 键盘轮询输入:计算机名、用户名、密码 +- SHA256 密码摘要 + XOR 加密配置缓冲(user.conf 格式已定义,写盘待 FAT32 write API) +- 18px 黑体中文位图(预渲染嵌入 ELF) + +mouseInit(`CODE/mouse`)是 DSK 直接调度的系统输入初始化器: +- 当前为安全 stub(只读 PS/2 状态,不写控制器),避免阻塞首次启动 +- 后续将实现完整 IRQ12 + 鼠标包解码 + +## 当前核心架构 + +```text +SAS-R0-PCQ + 单地址空间 Ring0 任务模型 + Per-CPU O(1) 位图调度器 + +DRR + Dedicated Recovery Root + 看门狗 / checkpoint / recovery log / A-B 回滚 + +UTSM + UUID-Tuned Sealed Memory + UUID 调谐封缄内存 + +UTRW + UTSM Tagged Reader/Writer + UTSM 带标记读写器 + +DMP + Descriptor Meta Page + 段描述符元页 + +PCKC + Per-CPU Key Cache + 每 CPU 密钥缓存 +``` + +## 关键设计约束 + +1. 优先考虑极高权限和极高效率。 +2. 主系统采用单地址空间 Ring0 模型。 +3. 不追求传统多用户强安全隔离。 +4. DRR 作为恢复根,应尽量独立于普通调度器。 +5. UTSM 数据区连续存密文,UUID 不嵌入数据区。 +6. 调度器热路径必须保持 O(1)。 +7. checkpoint 只处理 dirty shard / dirty page,不扫描全内存。 +8. DRR 必须拥有独立 Emergency Pool,普通 OOM 不应阻断 recovery。 +9. checkpoint metadata 使用 A/B 双槽、CRC 校验和原子 active slot 切换。 +10. 驱动模块采用 DKM ELF64 .drv ABI,通过 driver_desc / driver_init / driver_exit 暴露入口。 +11. 驱动默认通过 dkm_kernel_api 调用内核服务,不直接依赖任意内核符号。 +12. stage0/stage1 驱动必须支持 bootloader 预加载 boot module 方案。 + +## 重要文件 + +```text +SYSTEM/ + 打包为 IMG 后的系统根目录。 + +SYSTEM/driver/manifest.json + 内核启动驱动模块清单,控制 ELF .drv 驱动的分阶段加载。 + +CODE/DKM/README.md + DKM 驱动模块系统说明,记录驱动加载目标、ABI、loader、状态机和实现清单。 + +CODE/UTSM/README.md + UTSM 模块设计说明。 + +RE/UTSM_算法记录.md + UTSM 核心算法记录。 + +RE/UTSM_设计架构.md + UTSM 架构记录。 + +RE/驱动模块ABI设计.md + DKM 驱动模块 ABI、kernel_api、ELF relocation、manifest、状态机和 boot module 方案。 + +ISO/deshab.img + build.ps1 将 SYSTEM 打包生成的 GPT 磁盘镜像,包含一个 FAT32 EFI System Partition。 + +ISO/run_qemu.bat + 使用 QEMU 启动 ISO/deshab.img 的脚本。 +``` + +## 构建相关 + +已知说明: + +```text +SYSTEM/ 是打包为 IMG 后的系统根目录。 +SYSTEM/boot/ 存放启动模块,例如 utsm.elf。 +SYSTEM/driver/ 存放 ELF .drv 驱动模块和 manifest.json。 +CODE/UTSM/README.md: 编译到 SYSTEM/boot/utsm.elf +build.bat: 调用 build.ps1。 +build.ps1: 编译 UTSM,生成 SYSTEM/boot/utsm.elf,并把 SYSTEM 打包为 ISO/deshab.img。deshab.img 是 GPT 磁盘镜像,包含 FAT32 EFI System Partition。 +ISO/run_qemu.bat: 用 QEMU + UEFI 固件启动 ISO/deshab.img,串口输出到 stdio。 +``` + +当前 UTSM 已有首阶段内核代码骨架和构建脚本,可生成 utsm.elf 与 GPT + FAT32 ESP 镜像。早期启动要求:进入 C 前 `cli/cld`,early serial 不允许无限等待硬件 ready 位,未启用 FPU/SSE 前编译必须使用 `-mno-sse -mno-sse2 -mno-mmx -msoft-float`。DATA/BSS 段使用 PF_R|PF_W|PF_X(SAS-R0 下驱动模块内存需可执行)。 + +DKM/DSM 驱动加载系统已完工:内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载 + 全部 14 个外部 `.drv` ET_REL 装载(ELF64/SHT_NOBITS/R_X86_64_64/32/32S/PC32 relocation + driver_desc 校验 + driver_init 调用)。当前镜像额外预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。 + +当前已实现 14 个真实 DKM 驱动: +- **timer** (stage0): PIT 校准, 100ms busy-wait demo +- **apic** (stage0): CPUID Local APIC 探测, IA32_APIC_BASE MSR, ACPI MADT 枚举, LAPIC MMIO 只读寄存器验证;当前保留 PIC IRQ 路由 +- **acpi** (stage0): Limine RSDP → XSDT, ACPI 表枚举 +- **pci** (stage0): PCI config space 扫描, 设备枚举 +- **console_fb** (stage1): Limine framebuffer, 淡蓝色背景 + 居中四色圆弧加载环(纯整数运算,无浮点依赖) +- **ahci** (stage1): PCI AHCI `8086:2922` 探测, BAR5/ABAR HHDM MMIO 映射, HBA CAP/GHC/PI/VS 与 port 枚举;已接入 `kernel_api.dma`,支持 SATA disk IDENTIFY 与 LBA0 READ 最小 DMA 路径;可向 `kernel_api.block` 注册 `ahci0` block provider;默认无 SATA 盘/ATAPI 场景会跳过命令 +- **nvme** (stage1): PCI NVMe `1b36:0010` 探测, BAR0/BAR1 解析, IRQ line 读取;当前 QEMU BAR0 位于 4G 以上,MMIO register 读取等待高位 PCI MMIO 映射能力 +- **bootfs** (stage1): Limine boot module 内存文件系统 +- **vfs** (stage2): 挂载 bootfs, 文件查找/读取 +- **devfs** (stage2): 暴露 `/dev/version`, `/dev/platform`, `/dev/fb0`, `/dev/modules`, `/dev/boot/` +- **fat32** (stage2): 只读 FAT32 BPB/FAT/目录项解析, 通过 `driver/test.fat32` 验证读取 `README.TXT` +- **ps2kbd** (stage3): PS/2 IRQ1 注册, 端口 `0x60` 扫描码读取, scan code set 1 → ASCII demo +- **e1000** (stage3): PCI `8086:10d3` 探测, BAR0 HHDM MMIO 映射, MAC 读取, IRQ 注册, 基础 link setup +- **virtio_net** (stage3): PCI virtio-net `1af4:1001` 探测, BAR 枚举, modern virtio PCI capability 枚举;暂不进行 feature negotiation / virtqueue / 收发包 + +当前内核平台能力:IDT 0–47 stub、异常处理、PIC remap 到 `0x20–0x2f`、IRQ handler 注册与 EOI、Limine RSDP/framebuffer/module/HHDM request。 + +kernel_api 已暴露能力:log, rsdp_address, fb_address/width/height/pitch/bpp, boot_modules_response, irq_register, hhdm_offset, dma.alloc_pages。 + +当前 manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。 + +下一阶段优先路线: +1. **内存与 MMIO 基础设施**: 实现物理页分配器、页表映射接口、低位/高位 PCI MMIO 映射,解决 NVMe 4G 以上 BAR 和后续 DMA 映射问题。 +2. **DMA 与块设备数据路径**: 建立 contiguous DMA buffer、cache/屏障约定、PRDT/队列内存管理,然后推进 AHCI IDENTIFY/READ 与 NVMe admin queue/identify。 +3. **IRQ 后端升级**: 在现有 PIC fallback 稳定基础上扩展 IDT/vector allocator/APIC EOI/IOAPIC redirection,逐步迁移设备 IRQ,最后接入 MSI/MSI-X。 +4. **网络数据路径**: 在 DMA/IRQ 完成后推进 e1000 RX/TX ring 与 virtio-net feature negotiation/virtqueue/RX-TX。 +5. **VFS 与真实块设备接入**: 将 FAT32 从测试镜像迁移到 AHCI/NVMe block provider,完善挂载、读取、目录遍历和错误路径。 +6. **DKM 工程化收尾**: 统一驱动本地 ABI 头、清理 warning、补充自动化 QEMU 场景(e1000/virtio-net/nvme/ahci)。 +7. **FirstInit 完善**: 修复按键崩溃(fb_text 优化问题)、实现鼠标光标移动、user.conf 写盘(需 block write API)、网络配置界面。 +8. **鼠标输入驱动**: 从 mouseInit stub 升级为完整 PS/2 鼠标 IRQ12 + 包解码 + 光标移动。 diff --git a/CODE/DKM/README.md b/CODE/DKM/README.md new file mode 100644 index 0000000..79ee64e --- /dev/null +++ b/CODE/DKM/README.md @@ -0,0 +1,691 @@ +# DKM 驱动模块系统 + +```text +DKM = Deshab Kernel Module +DSM = Driver Startup Manager +``` + +DKM 是 Deshab 内核的驱动模块系统,负责在内核启动时加载 `SYSTEM/driver` 下的 ELF64 `.drv` 驱动模块,并把驱动注册到内核服务体系中。 + +当前实现状态:CODE/UTSM 内核已接入完整 DKM/DSM 驱动加载系统。内置 `console_early` + manifest.json 解析 + Limine boot module 预加载 + 4 stage 分阶段加载。全部 14 个外部 `.drv` 已完成 ET_REL 装载全链路:ELF64 校验、符号扫描、section 拷贝到 arena、SHT_NOBITS 清零、R_X86_64_64/32/32S/PC32 重定位、driver_desc magic 校验、外部 driver_init 调用进入 ACTIVE。当前镜像还预加载 `driver/test.fat32` 作为 FAT32 驱动测试镜像。 + +Linux/Windows 已编译驱动不能直接作为 DKM 驱动使用。真实驱动必须按本文件定义的 DKM ABI 编译,导出 `driver_desc`、`driver_init`、`driver_exit`,并只通过 `dkm_kernel_api` 调用内核服务。 + +当前真实驱动进度:14/14。 + +```text +stage0: timer, apic, acpi, pci +stage1: console_fb, ahci, nvme, bootfs +stage2: vfs, devfs, fat32 +stage3: ps2kbd, e1000, virtio_net +``` + +manifest 中 14 个外部 `.drv` 均已替换为真实 DKM 驱动并可加载执行。当前 IDT/PIC/IRQ 基础设施已在内核侧实现,`apic` 已完成 APIC discovery 并保留 PIC IRQ 路由,`ahci` 已完成 PCI/ABAR/HBA/port discovery,并通过 `kernel_api.dma.alloc_pages` 接入早期低位 DMA buffer,支持 SATA disk IDENTIFY 与 LBA0 READ 最小路径;识别 SATA disk 后可向 `kernel_api.block` 注册 `ahci0` block provider。`fat32` 已优先通过 block provider 读取 FAT32 镜像,无设备或 BPB 无效时回退 boot module。`nvme` 已完成 PCI discovery 并能识别 QEMU NVMe 控制器;由于当前 QEMU BAR0 位于 4G 以上,NVMe MMIO register 读取等待高位 PCI MMIO 映射能力。`ps2kbd` 和 `e1000` 已通过 `kernel_api.irq_register` 验证 IRQ handler 注册路径。`virtio_net` 已完成 PCI discovery 和 modern virtio capability 枚举,尚未进行 feature negotiation / virtqueue / RX-TX。 + +下一阶段 DKM/驱动路线: + +```text +P0: 物理页分配器 + 页表/MMIO 映射接口 +P1: DMA buffer/屏障约定 + AHCI/NVMe block 数据路径 +P2: IDT 256 vectors + vector allocator + APIC/IOAPIC/MSI IRQ backend +P3: e1000 与 virtio_net RX/TX 数据路径 +P4: FAT32 接入真实 block provider,替代 test.fat32 启动镜像 +P5: 驱动 ABI 公共头、warning 清理、QEMU 场景自动化 +``` + +--- + +## 1. 模块目标 + +DKM 的目标: + +```text +1. 从 SYSTEM/driver/manifest.json 读取驱动清单。 +2. 按 stage 分阶段加载驱动。 +3. 支持 ELF64 .drv 模块加载和重定位。 +4. 提供稳定的 driver_desc ABI。 +5. 通过 dkm_kernel_api 向驱动暴露内核服务。 +6. 支持 stage0/stage1 boot module 早期加载。 +7. 将驱动内存纳入 UTSM。 +8. 将驱动状态和恢复操作纳入 DRR。 +``` + +--- + +## 2. 系统目录约定 + +打包为 IMG 后,`SYSTEM` 是系统根目录。 + +```text +SYSTEM/ + boot/ + utsm.elf + limine.conf + + driver/ + manifest.json + + platform/ + timer.drv + apic.drv + acpi.drv + + bus/ + pci.drv + + block/ + ahci.drv + nvme.drv + + fs/ + bootfs.drv + vfs.drv + fat32.drv + devfs.drv + + net/ + e1000.drv + virtio_net.drv + + input/ + ps2kbd.drv + + console/ + console_fb.drv +``` + +当前驱动清单: + +```text +SYSTEM/driver/manifest.json +``` + +--- + +## 3. 启动阶段 + +DKM 使用分阶段加载。 + +```text +stage0: platform + timer / apic / acpi / pci + 建立平台发现、中断、总线和基础计时能力。 + +stage1: boot + console / block / bootfs + 建立启动日志、块设备和启动文件访问能力。 + +stage2: filesystem + vfs / fat32 / devfs + 建立完整 VFS 和可挂载文件系统。 + +stage3: optional + net / input / gpu + 加载网络、输入、图形等非启动关键驱动。 +``` + +规则: + +```text +1. stage0/stage1 可由 bootloader 预加载为 boot module。 +2. stage2/stage3 默认从 SYSTEM/driver 加载。 +3. required=true 的驱动失败时进入 DRR recovery 或 panic。 +4. required=false 的驱动失败时标记 FAILED 并继续启动。 +``` + +--- + +## 4. driver_desc ABI + +每个 `.drv` 必须导出三个符号: + +```text +driver_desc +driver_init +driver_exit +``` + +### 4.1 常量 + +```c +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1 +``` + +`0x444B4D31` 表示: + +```text +DKM1 +``` + +### 4.2 driver_desc 结构 + +```c +struct dkm_driver_desc { + uint32_t magic; + uint16_t abi_version; + uint16_t desc_size; + + const char *name; + const char *version; + const char *vendor; + + uint32_t driver_class; + uint32_t stage; + uint32_t flags; + uint32_t priority; + + const char *const *depends; + uint32_t depends_count; + + const char *const *provides; + uint32_t provides_count; + + uint64_t min_kernel_abi; + uint64_t feature_bits; + + uint64_t reserved0; + uint64_t reserved1; +}; +``` + +### 4.3 driver_class + +```text +DKM_CLASS_PLATFORM +DKM_CLASS_BUS +DKM_CLASS_INTERRUPT +DKM_CLASS_TIMER +DKM_CLASS_CONSOLE +DKM_CLASS_STORAGE +DKM_CLASS_FS +DKM_CLASS_NET +DKM_CLASS_INPUT +DKM_CLASS_GPU +DKM_CLASS_MISC +``` + +### 4.4 flags + +```text +DKM_F_REQUIRED + 当前 stage 中失败则进入 DRR recovery 或 panic。 + +DKM_F_BOOT_MODULE + 可由 bootloader 预加载。 + +DKM_F_NO_UNLOAD + 不允许卸载。 + +DKM_F_STRONG_RECOVERY + 驱动 data/bss 段使用 UTSM 强恢复策略。 + +DKM_F_DMA_REQUIRED + 驱动需要 DMA API。 + +DKM_F_EARLY_LOG + 驱动 init 期间允许使用 early log。 +``` + +--- + +## 5. 驱动入口 + +```c +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle); + +int driver_exit(struct dkm_driver_handle *handle); +``` + +约定: + +```text +1. driver_init 返回 0 表示成功。 +2. driver_init 返回负数表示失败。 +3. driver_exit 仅用于非 DKM_F_NO_UNLOAD 驱动。 +4. required 驱动失败会触发 DRR recovery 或 panic。 +5. optional 驱动失败会被标记为 FAILED。 +``` + +--- + +## 6. kernel_api ABI + +驱动默认不直接依赖任意内核符号,而是通过 `dkm_kernel_api` 调用内核服务。 + +```c +#define DKM_KERNEL_API_VERSION 1 + +struct dkm_kernel_api { + uint32_t version; + uint32_t size; + uint64_t 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 void *net; + const void *timer; + const void *drr; + + const void *rsdp_address; + const void *fb_address; + uint64_t fb_width; + uint64_t fb_height; + uint64_t fb_pitch; + uint16_t fb_bpp; + const void *boot_modules_response; + int (*irq_register)(uint8_t irq, void *handler); + uint64_t hhdm_offset; +}; +``` + +### 6.1 log API + +```c +struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +}; +``` + +### 6.2 memory API + +```c +struct dkm_mem_api { + void *(*alloc)(uint64_t size, uint32_t flags); + void (*free)(void *ptr); + + void *(*alloc_pages)(uint32_t order, uint32_t flags); + void (*free_pages)(void *ptr, uint32_t order); + + void *(*mmio_map)(uint64_t phys, uint64_t size, uint32_t flags); + void (*mmio_unmap)(void *virt, uint64_t size); +}; +``` + +### 6.3 UTSM API + +```c +struct dkm_utsm_api { + int (*create_segment)(uint64_t size, uint32_t flags, struct utsm_capability *out); + int (*read)(struct utsm_capability cap, uint64_t off, void *dst, uint64_t len); + int (*write)(struct utsm_capability cap, uint64_t off, const void *src, uint64_t len); + int (*pin)(struct utsm_capability cap, uint64_t off, uint64_t len, void **window); + int (*unpin)(void *window, uint32_t flags); +}; +``` + +### 6.4 当前平台直通能力 + +当前内核已通过 `dkm_kernel_api` 直接暴露早期平台信息和 IRQ/HHDM 能力: + +```text +rsdp_address Limine RSDP 地址,用于 ACPI 驱动 +fb_address/width/height Limine framebuffer 信息,用于 console_fb +fb_pitch/fb_bpp framebuffer 行跨度和像素格式 +boot_modules_response Limine module response,用于 bootfs/fat32 测试镜像 +irq_register 注册 PIC IRQ0–15 handler,用于 ps2kbd/e1000 +hhdm_offset physical → virtual direct map,用于 PCI BAR MMIO +``` + +### 6.5 IRQ / PCI / DMA API + +```c +struct dkm_irq_api { + int (*register_irq)(uint32_t vector, int (*handler)(void *ctx), void *ctx, uint32_t flags); + int (*unregister_irq)(uint32_t vector); + void (*ack)(uint32_t vector); +}; + +struct dkm_pci_api { + int (*scan)(int (*cb)(const struct pci_device *dev, void *ctx), void *ctx); + int (*enable_device)(const struct pci_device *dev); + uint64_t (*bar_phys)(const struct pci_device *dev, uint32_t bar); + uint64_t (*bar_size)(const struct pci_device *dev, uint32_t bar); +}; + +struct dkm_dma_api { + int (*alloc)(uint64_t size, uint32_t flags, struct dkm_dma_buffer *out); + void (*free)(struct dkm_dma_buffer *buf); + int (*sync_for_device)(struct dkm_dma_buffer *buf); + int (*sync_for_cpu)(struct dkm_dma_buffer *buf); +}; +``` + +--- + +## 7. ELF64 .drv Loader + +第一版仅支持 x86_64 ELF64 小型内核模型。 + +### 7.1 必须支持的 relocation + +```text +R_X86_64_64 + 绝对 64-bit 地址重定位。 + +R_X86_64_RELATIVE + base + addend。 + +R_X86_64_GLOB_DAT + GOT 符号地址。 + +R_X86_64_JUMP_SLOT + PLT 函数地址。 +``` + +### 7.2 建议支持的 relocation + +```text +R_X86_64_PC32 + 32-bit PC-relative 调用。 + +R_X86_64_PLT32 + PLT-relative 调用。 + +R_X86_64_32 / R_X86_64_32S + 仅允许确认地址范围安全时使用。 +``` + +### 7.3 第一版禁止 + +```text +TLS relocation +IFUNC +lazy binding +外部动态库依赖 +用户态 libc 依赖 +异常展开表强依赖 +``` + +### 7.4 加载流程 + +```text +DKM_ELF_LOAD(path_or_boot_module) + 1. 读取 ELF header。 + 2. 校验 ELFCLASS64 / EM_X86_64 / endian / ABI。 + 3. 读取 program headers 或 section headers。 + 4. 计算模块内存大小和对齐。 + 5. 通过 UTSM 创建 code/data 段。 + 6. 加载 PT_LOAD 或 SHF_ALLOC section。 + 7. 清零 .bss。 + 8. 解析 symbol table。 + 9. 解析 relocation section。 + 10. 解析内核导出符号和 dkm_kernel_api。 + 11. 执行 relocation。 + 12. 查找 driver_desc / driver_init / driver_exit。 + 13. 设置 code 段 READ|EXEC|SEALED。 + 14. 设置 data 段 READ|WRITE|STRONG_RECOVERY。 + 15. 返回 module handle。 +``` + +--- + +## 8. manifest.json 格式 + +路径: + +```text +SYSTEM/driver/manifest.json +``` + +顶层结构: + +```json +{ + "schema": "deshab.driver.manifest.v1", + "root": "SYSTEM", + "driverRoot": "SYSTEM/driver", + "format": "elf64-drv", + "abi": {}, + "policy": {}, + "stages": [] +} +``` + +驱动项示例: + +```json +{ + "name": "pci", + "path": "driver/bus/pci.drv", + "class": "bus", + "required": true, + "depends": ["irq"], + "provides": ["pci"] +} +``` + +路径规则: + +```text +manifest 中的 path 相对 SYSTEM 根目录。 +SYSTEM/driver/manifest.json 内的 driver/bus/pci.drv 指向 SYSTEM/driver/bus/pci.drv。 +stage0/stage1 驱动允许来自 boot module。 +stage2/stage3 驱动默认从 SYSTEM/driver 加载。 +``` + +--- + +## 9. 驱动加载状态机 + +正常加载: + +```text +DISCOVERED + -> QUEUED + -> DEP_WAIT + -> LOADING + -> ELF_CHECKED + -> MEMORY_ALLOCATED + -> RELOCATED + -> ABI_CHECKED + -> INITING + -> ACTIVE +``` + +失败状态: + +```text +FAILED_ELF +FAILED_RELOCATION +FAILED_DEPENDENCY +FAILED_ABI +FAILED_INIT +FAILED_REQUIRED +``` + +卸载状态: + +```text +ACTIVE + -> QUIESCING + -> EXITING + -> UNLOADING + -> UNLOADED +``` + +DRR 恢复状态: + +```text +ACTIVE + -> SUSPENDED_BY_DRR + -> RECOVERING + -> REINITING + -> ACTIVE +``` + +--- + +## 10. DSM 加载策略 + +```text +DSM_LOAD_ALL() + 1. 读取 SYSTEM/driver/manifest.json。 + 2. 校验 schema / abi / policy。 + 3. 按 stage 顺序加载。 + 4. 每个 stage 内按 priority 排序。 + 5. 检查 depends 是否已由 provides 满足。 + 6. 未满足依赖的驱动进入 DEP_WAIT。 + 7. 每轮加载成功后更新 provides set。 + 8. 一轮后仍 DEP_WAIT 的 required 驱动进入 FAILED_REQUIRED。 + 9. optional 驱动失败只记录状态。 +``` + +required 失败策略: + +```text +stage0/stage1 required 失败: + DRR report_fault -> boot fallback -> panic / system rollback + +stage2 required 失败: + DRR recovery -> fallback 到 bootfs 或只读模式 + +stage3 optional 失败: + mark FAILED -> continue boot +``` + +--- + +## 11. stage0/stage1 boot module 方案 + +stage0/stage1 不应依赖普通 VFS。 + +bootloader 应预加载: + +```text +SYSTEM/driver/manifest.json +SYSTEM/driver/platform/timer.drv +SYSTEM/driver/platform/acpi.drv +SYSTEM/driver/platform/apic.drv +SYSTEM/driver/bus/pci.drv +SYSTEM/driver/console/console_fb.drv +SYSTEM/driver/fs/bootfs.drv +SYSTEM/driver/test.fat32 +``` + +可选预加载: + +```text +SYSTEM/driver/block/ahci.drv +SYSTEM/driver/block/nvme.drv +SYSTEM/driver/fs/vfs.drv +SYSTEM/driver/fs/devfs.drv +SYSTEM/driver/fs/fat32.drv +SYSTEM/driver/net/e1000.drv +SYSTEM/driver/net/virtio_net.drv +SYSTEM/driver/input/ps2kbd.drv +``` + +boot module 表: + +```c +struct dkm_boot_module { + const char *path; + void *base; + uint64_t size; + uint32_t type; + uint32_t flags; + uint64_t checksum; +}; + +struct dkm_boot_module_table { + uint32_t count; + struct dkm_boot_module modules[]; +}; +``` + +查找规则: + +```text +DSM_LOAD_DRIVER(path) + 1. 如果 path 在 boot module table 中,直接从内存加载。 + 2. 否则如果 VFS 已可用,从 SYSTEM/path 加载。 + 3. 否则返回 BOOT_MODULE_NOT_FOUND。 +``` + +--- + +## 12. 与 UTSM / DRR 的关系 + +### 12.1 UTSM 内存策略 + +```text +驱动 text:READ | EXEC | SEALED +驱动 rodata:READ | SEALED +驱动 data:READ | WRITE | STRONG_RECOVERY +驱动 bss:READ | WRITE | STRONG_RECOVERY +驱动 DMA:DMA | PINNED,必要时 CIPHERTEXT_DMA 或 NO_ENCRYPT +``` + +### 12.2 DRR 记录项 + +```text +driver name +stage +state +module memory range +provided services +recovery ops +init result +last fault +``` + +### 12.3 recovery ops + +```c +struct dkm_driver_recovery_ops { + int (*quiesce)(void *ctx); + int (*reset)(void *ctx); + int (*reinit)(void *ctx); + int (*dump_state)(void *ctx, void *buf, uint64_t len); +}; +``` + +DRR recovery 顺序: + +```text +quiesce -> UTSM rollback -> device reset -> driver reinit -> resume +``` + +--- + +## 13. 第一版实现清单 + +```text +1. manifest.json parser +2. boot module table lookup +3. ELF64 header 校验 +4. ELF64 section/program loading +5. relocation 处理 +6. driver_desc 校验 +7. kernel_api table 初始化 +8. depends/provides resolver +9. staged loader +10. driver state table +11. DRR driver fault reporting +12. UTSM module memory allocation +``` + +--- + +## 14. 相关文件 + +```text +SYSTEM/driver/manifest.json + 驱动启动清单。 + +RE/驱动模块ABI设计.md + DKM ABI 详细设计。 + +CODE/UTSM/README.md + UTSM 与驱动加载的整合说明。 +``` diff --git a/CODE/DKM/acpi/acpi.c b/CODE/DKM/acpi/acpi.c new file mode 100644 index 0000000..e88e7ad --- /dev/null +++ b/CODE/DKM/acpi/acpi.c @@ -0,0 +1,298 @@ +/* DKM ACPI Driver - RSDP scan / RSDT parse / table enumeration + * Stage 0, optional, provides "acpi". + * Scans BIOS memory for RSDP, then walks RSDT/XSDT to list all ACPI tables. + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types + * --------------------------------------------------------------- */ +#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_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 void *net; + const void *timer; + const void *drr; + const void *rsdp_address; +}; + +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; +}; + +/* --------------------------------------------------------------- + * ACPI structures + * --------------------------------------------------------------- */ +typedef struct __attribute__((packed)) { + char signature[8]; + u8 checksum; + char oem_id[6]; + u8 revision; + u32 rsdt_addr; +} acpi_rsdp_v1; + +typedef struct __attribute__((packed)) { + /* RSDP v1 fields */ + char signature[8]; + u8 checksum; + char oem_id[6]; + u8 revision; + u32 rsdt_addr; + /* RSDP v2+ fields */ + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 reserved[3]; +} acpi_rsdp_v2; + +typedef struct __attribute__((packed)) { + char signature[4]; + u32 length; + u8 revision; + u8 checksum; + char oem_id[6]; + char oem_table_id[8]; + u32 oem_revision; + u32 creator_id; + u32 creator_revision; +} acpi_sdt_header; + +/* --------------------------------------------------------------- + * Driver descriptor + * --------------------------------------------------------------- */ +static const char *const g_provides[] = { "acpi" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "acpi", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 1, /* DKM_CLASS_PLATFORM */ + .stage = 0, + .flags = 0, /* optional */ + .priority = 0, + .depends = NULL, + .depends_count = 0, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +/* --------------------------------------------------------------- + * Helpers + * --------------------------------------------------------------- */ +static const struct dkm_log_api *g_log; + +static u32 min_u32(u32 a, u32 b) { return a < b ? a : b; } + +static int memneq(const char *a, const char *b, u32 n) { + for (u32 i = 0; i < n; i++) { + if (a[i] != b[i]) return 1; + } + return 0; +} + +static u8 checksum8(const u8 *data, u32 len) { + u8 sum = 0; + for (u32 i = 0; i < len; i++) { + sum += data[i]; + } + return sum; +} + +static void log_hex(const char *prefix, u64 val) { + static const char hex[] = "0123456789abcdef"; + char buf[32]; + u32 pos = 0; + while (pos < 30) { buf[pos] = 0; pos++; } + pos = 0; + buf[pos++] = '0'; + buf[pos++] = 'x'; + for (int i = 15; i >= 0; i--) { + buf[pos++] = hex[(val >> (i * 4)) & 0xf]; + } + buf[pos] = 0; + + /* manual concat via serial_write-like approach: pass prefix then buf */ + g_log->info(prefix); + g_log->info(buf); +} + +/* --------------------------------------------------------------- + * RSDP scan + * --------------------------------------------------------------- */ +static const acpi_rsdp_v2 *acpi_find_rsdp(const void *limine_rsdp) { + /* If Limine gave us the RSDP, use it directly */ + if (limine_rsdp) { + const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)limine_rsdp; + if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) { + if (checksum8((const u8 *)rsdp, 20) == 0) { + return rsdp; + } + } + } + /* EBDA: extended BIOS data area, base at 0x40E */ + { + u16 ebda_seg = *(const volatile u16 *)(uintptr_t)0x40E; + u64 ebda_base = (u64)ebda_seg << 4; + if (ebda_base >= 0x80000ULL && ebda_base < 0x100000ULL) { + for (u64 off = 0; off < 1024; off += 16) { + const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)(uintptr_t)(ebda_base + off); + if (off + sizeof(acpi_rsdp_v1) > 1024) break; + if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) { + if (checksum8((const u8 *)rsdp, 20) == 0) { + return rsdp; + } + } + } + } + } + + /* BIOS read-only area: 0x000E0000 - 0x000FFFFF */ + for (u64 addr = 0x000E0000ULL; addr < 0x00100000ULL; addr += 16) { + const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)(uintptr_t)addr; + if (memneq(rsdp->signature, "RSD PTR ", 8) == 0) { + if (checksum8((const u8 *)rsdp, 20) == 0) { + return rsdp; + } + } + } + return NULL; +} + +/* --------------------------------------------------------------- + * Table enumeration + * --------------------------------------------------------------- */ +static void acpi_enumerate_rsdt(const acpi_sdt_header *rsdt, u32 entry_size) { + u32 entry_count = (rsdt->length - sizeof(acpi_sdt_header)) / entry_size; + const u8 *entries = (const u8 *)rsdt + sizeof(acpi_sdt_header); + + g_log->info("[acpi] tables found"); + for (u32 i = 0; i < entry_count; i++) { + u64 addr; + if (entry_size == 4) { + addr = (u64)((const u32 *)entries)[i]; + } else { + addr = ((const u64 *)entries)[i]; + } + if (addr == 0) continue; + + const acpi_sdt_header *hdr = (const acpi_sdt_header *)(uintptr_t)addr; + char sig[5]; + sig[0] = hdr->signature[0]; + sig[1] = hdr->signature[1]; + sig[2] = hdr->signature[2]; + sig[3] = hdr->signature[3]; + sig[4] = 0; + + g_log->info("[acpi] "); + g_log->info(sig); + log_hex("[acpi] addr=", addr); + log_hex("[acpi] len =", (u64)hdr->length); + } +} + +/* --------------------------------------------------------------- + * Driver entry points + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) { + return -1; + } + g_log = api->log; + + g_log->info("[acpi] scanning for RSDP"); + + const acpi_rsdp_v2 *rsdp = acpi_find_rsdp(api->rsdp_address); + if (!rsdp) { + g_log->error("[acpi] RSDP not found"); + return 0; /* optional driver, don't fail boot */ + } + + g_log->info("[acpi] RSDP found"); + log_hex("[acpi] revision =", (u64)rsdp->revision); + g_log->info("[acpi] oem_id"); + { + char oem[7]; + for (int i = 0; i < 6; i++) oem[i] = rsdp->oem_id[i]; + oem[6] = 0; + g_log->info(oem); + } + + if (rsdp->revision >= 2 && rsdp->xsdt_addr) { + g_log->info("[acpi] using XSDT"); + const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr; + acpi_enumerate_rsdt(xsdt, 8); + } else if (rsdp->rsdt_addr) { + g_log->info("[acpi] using RSDT"); + const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->rsdt_addr; + acpi_enumerate_rsdt(rsdt, 4); + } else { + g_log->warn("[acpi] no RSDT/XSDT address"); + } + + g_log->info("[acpi] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/ahci/ahci.c b/CODE/DKM/ahci/ahci.c new file mode 100644 index 0000000..9a056c7 --- /dev/null +++ b/CODE/DKM/ahci/ahci.c @@ -0,0 +1,645 @@ +/* DKM AHCI Driver — PCI AHCI discovery and ABAR read-only enumeration + * Stage 1 optional storage driver, depends on "pci" and "irq", provides "block". + * This first version does not issue DMA commands; it only discovers the HBA + * and ports so later block I/O can be added safely. + */ + +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; + +#define NULL ((void *)0) + +struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +}; + +struct dkm_dma_buffer { + void *virt; + u64 phys; + u64 size; +}; + +struct dkm_dma_api { + int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out); +}; + +struct dkm_block_device_desc { + const char *name; + u64 sector_size; + u64 sector_count; + void *ctx; + int (*read)(void *ctx, u64 lba, u32 count, void *buffer); +}; + +struct dkm_block_api { + int (*register_device)(const struct dkm_block_device_desc *desc); + u32 (*device_count)(void); + int (*read)(u32 index, u64 lba, u32 count, void *buffer); + u64 (*sector_size)(u32 index); + const char *(*device_name)(u32 index); +}; + +struct dkm_kernel_api { + u32 version; + u32 size; + u64 feature_bits; + const struct dkm_log_api *log; + const void *mem; + const void *utsm; + const void *irq; + const void *pci; + const struct dkm_dma_api *dma; + const void *vfs; + const void *net; + const void *timer; + const void *drr; + const void *rsdp_address; + const void *fb_address; + u64 fb_width; + u64 fb_height; + u64 fb_pitch; + u16 fb_bpp; + const void *boot_modules_response; + int (*irq_register)(u8 irq, void *handler); + u64 hhdm_offset; + const struct dkm_block_api *block; +}; + +struct dkm_driver_handle; + +struct dkm_driver_desc { + u32 magic; + u16 abi_version; + u16 desc_size; + const char *name; + const char *version; + const char *vendor; + u32 driver_class; + u32 stage; + u32 flags; + u32 priority; + const char *const *depends; + u32 depends_count; + const char *const *provides; + u32 provides_count; + u64 min_kernel_abi; + u64 feature_bits; + u64 reserved0; + u64 reserved1; +}; + +static const char *const g_depends[] = { "pci", "irq" }; +static const char *const g_provides[] = { "block" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "ahci", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 6, /* DKM_CLASS_STORAGE */ + .stage = 1, + .flags = 0, /* optional */ + .priority = 0, + .depends = g_depends, + .depends_count = 2, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 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_SUBCLASS_SATA 0x06 +#define AHCI_PROGIF_AHCI 0x01 + +#define HBA_CAP 0x00 +#define HBA_GHC 0x04 +#define HBA_IS 0x08 +#define HBA_PI 0x0C +#define HBA_VS 0x10 +#define HBA_CAP2 0x24 +#define HBA_BOHC 0x28 +#define HBA_PORT_BASE 0x100 +#define HBA_PORT_SIZE 0x80 + +#define PxCLB 0x00 +#define PxFB 0x08 +#define PxIS 0x10 +#define PxIE 0x14 +#define PxCMD 0x18 +#define PxTFD 0x20 +#define PxSIG 0x24 +#define PxSSTS 0x28 +#define PxSCTL 0x2C +#define PxSERR 0x30 +#define PxSACT 0x34 +#define PxCI 0x38 +#define PxSNTF 0x3C +#define PxFBS 0x40 + +#define AHCI_CMD_ST (1u << 0) +#define AHCI_CMD_FRE (1u << 4) +#define AHCI_CMD_FR (1u << 14) +#define AHCI_CMD_CR (1u << 15) +#define AHCI_CMD_ICC_ACTIVE (1u << 28) + +#define ATA_CMD_IDENTIFY 0xEC +#define ATA_CMD_IDENTIFY_PACKET 0xA1 + +typedef struct __attribute__((packed)) ahci_cmd_header { + u16 flags; + u16 prdtl; + u32 prdbc; + u32 ctba; + u32 ctbau; + u32 reserved[4]; +} ahci_cmd_header; + +typedef struct __attribute__((packed)) ahci_prdt_entry { + u32 dba; + u32 dbau; + u32 reserved; + u32 dbc_i; +} ahci_prdt_entry; + +typedef struct __attribute__((packed)) ahci_cmd_table { + u8 cfis[64]; + u8 acmd[16]; + u8 reserved[48]; + ahci_prdt_entry prdt[1]; +} ahci_cmd_table; + +static const struct dkm_log_api *g_log; +static volatile u32 *g_abar; +static u32 g_ready_port = 0xffffffffu; +static ahci_cmd_header *g_cmd_header; +static struct dkm_dma_buffer g_cmd_table; +static struct dkm_dma_buffer g_data; + +static __inline__ void outl(u16 port, u32 value) { + __asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port)); +} + +static __inline__ u32 inl(u16 port) { + u32 value; + __asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + outl(PCI_ADDR, addr); + return inl(PCI_DATA); +} + +static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + outl(PCI_ADDR, addr); + outl(PCI_DATA, value); +} + +static void log_hex(const char *prefix, u64 value) { + static const char hex[] = "0123456789abcdef"; + char buf[19]; + u32 pos = 0; + buf[pos++] = '0'; + buf[pos++] = 'x'; + for (int i = 15; i >= 0; i--) { + buf[pos++] = hex[(value >> (i * 4)) & 0xf]; + } + buf[pos] = 0; + g_log->info(prefix); + g_log->info(buf); +} + +static void ahci_zero(void *ptr, u32 len) { + u8 *p = (u8 *)ptr; + for (u32 i = 0; i < len; i++) p[i] = 0; +} + +static int ahci_find(u8 *out_bus, u8 *out_dev, u8 *out_func) { + for (u8 bus = 0; bus < 8; bus++) { + for (u8 dev = 0; dev < 32; dev++) { + u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID); + if ((vd & 0xffff) == 0xffff) continue; + + u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16); + u8 func_count = (header & 0x80) ? 8 : 1; + for (u8 func = 0; func < func_count; func++) { + u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID); + if ((vd2 & 0xffff) == 0xffff) continue; + + u32 class_reg = pci_read(bus, dev, func, 0x08); + u8 prog_if = (u8)((class_reg >> 8) & 0xff); + u8 subclass = (u8)((class_reg >> 16) & 0xff); + u8 class_code = (u8)((class_reg >> 24) & 0xff); + + if (class_code == AHCI_CLASS_STORAGE && + subclass == AHCI_SUBCLASS_SATA && + prog_if == AHCI_PROGIF_AHCI) { + *out_bus = bus; + *out_dev = dev; + *out_func = func; + return 0; + } + } + } + } + return -1; +} + +static const char *sata_sig_name(u32 sig) { + if (sig == 0x00000101u) return "SATA drive"; + if (sig == 0xEB140101u) return "ATAPI device"; + if (sig == 0xC33C0101u) return "Enclosure mgmt"; + if (sig == 0x96690101u) return "Port multiplier"; + if (sig == 0x00000000u) return "empty/unknown"; + return "unknown signature"; +} + +static void ahci_log_port(u32 port) { + if (!g_abar || port >= 32) { + return; + } + u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE; + u32 ssts = g_abar[(base + PxSSTS) / 4]; + u32 sig = g_abar[(base + PxSIG) / 4]; + u32 cmd = g_abar[(base + PxCMD) / 4]; + u32 tfd = g_abar[(base + PxTFD) / 4]; + u32 serr = g_abar[(base + PxSERR) / 4]; + u32 ci = g_abar[(base + PxCI) / 4]; + u32 sact = g_abar[(base + PxSACT) / 4]; + + u32 det = ssts & 0xf; + u32 spd = (ssts >> 4) & 0xf; + u32 ipm = (ssts >> 8) & 0xf; + + log_hex("[ahci] port=", port); + log_hex("[ahci] SSTS=", ssts); + log_hex("[ahci] DET=", det); + log_hex("[ahci] SPD=", spd); + log_hex("[ahci] IPM=", ipm); + log_hex("[ahci] SIG=", sig); + g_log->info("[ahci] type"); + g_log->info(sata_sig_name(sig)); + log_hex("[ahci] CMD=", cmd); + log_hex("[ahci] TFD=", tfd); + log_hex("[ahci] SERR=", serr); + log_hex("[ahci] CI=", ci); + log_hex("[ahci] SACT=", sact); +} + +static int ahci_stop_port(u32 port) { + u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE; + u32 cmd = g_abar[(base + PxCMD) / 4]; + cmd &= ~AHCI_CMD_ST; + g_abar[(base + PxCMD) / 4] = cmd; + for (u32 i = 0; i < 100000; i++) { + cmd = g_abar[(base + PxCMD) / 4]; + if (!(cmd & AHCI_CMD_CR)) break; + } + cmd &= ~AHCI_CMD_FRE; + g_abar[(base + PxCMD) / 4] = cmd; + for (u32 i = 0; i < 100000; i++) { + cmd = g_abar[(base + PxCMD) / 4]; + if (!(cmd & AHCI_CMD_FR)) return 0; + } + return -1; +} + +static int ahci_start_port(u32 port) { + u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE; + u32 cmd = g_abar[(base + PxCMD) / 4]; + cmd |= AHCI_CMD_FRE; + g_abar[(base + PxCMD) / 4] = cmd; + cmd |= AHCI_CMD_ST; + g_abar[(base + PxCMD) / 4] = cmd; + return 0; +} + +static void ahci_fill_identify_fis(u8 *fis, u8 command) { + for (u32 i = 0; i < 64; i++) fis[i] = 0; + fis[0] = 0x27; /* Register H2D FIS */ + fis[1] = 1u << 7; /* command */ + fis[2] = command; + fis[7] = 1u << 6; /* LBA mode */ +} + +static void ahci_fill_read_fis(u8 *fis, u64 lba, u32 count) { + ahci_zero(fis, 64); + fis[0] = 0x27; + fis[1] = 1u << 7; + fis[2] = 0x25; /* READ DMA EXT */ + fis[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 int ahci_read_blocks_internal(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data, u64 lba, u32 count, 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)); + ahci_zero(data->virt, bytes); + + hdr[0].flags = 5; + 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_read_fis(tbl->cfis, lba, count); + + 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) { + u8 *dst = (u8 *)buffer; + u8 *src = (u8 *)data->virt; + for (u32 j = 0; j < bytes; j++) dst[j] = src[j]; + return 0; + } + } + g_log->warn("[ahci] READ timeout"); + log_hex("[ahci] READ lba=", lba); + log_hex("[ahci] READ count=", count); + log_hex("[ahci] READ CI=", g_abar[(base + PxCI) / 4]); + log_hex("[ahci] READ IS=", g_abar[(base + PxIS) / 4]); + log_hex("[ahci] READ TFD=", g_abar[(base + PxTFD) / 4]); + return -3; +} + +static int ahci_block_read(void *ctx, u64 lba, u32 count, void *buffer) { + (void)ctx; + if (g_ready_port == 0xffffffffu || !g_cmd_header) return -1; + /* ensure port is in ST+FRE state */ + u32 base = HBA_PORT_BASE + g_ready_port * HBA_PORT_SIZE; + u32 cmd = g_abar[(base + PxCMD) / 4]; + if (!(cmd & AHCI_CMD_ST) || !(cmd & AHCI_CMD_FRE)) { + g_log->warn("[ahci] port not ready; restarting"); + ahci_stop_port(g_ready_port); + ahci_start_port(g_ready_port); + } + 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_read_lba0(u32 port, ahci_cmd_header *hdr, struct dkm_dma_buffer *table, struct dkm_dma_buffer *data) { + int status = ahci_read_blocks_internal(port, hdr, table, data, 0, 1, data->virt); + if (status == 0) { + log_hex("[ahci] READ LBA0 port=", port); + log_hex("[ahci] READ LBA0 bytes=", hdr[0].prdbc); + log_hex("[ahci] READ LBA0 word0=", ((u16 *)data->virt)[0]); + log_hex("[ahci] READ LBA0 word255=", ((u16 *)data->virt)[255]); + return 1; + } + return status; +} + +static int ahci_identify_port(u32 port, const struct dkm_dma_api *dma) { + if (!dma || !dma->alloc_pages) return -1; + + u32 base = HBA_PORT_BASE + port * HBA_PORT_SIZE; + u32 ssts = g_abar[(base + PxSSTS) / 4]; + u32 sig = g_abar[(base + PxSIG) / 4]; + u32 det = ssts & 0xf; + u32 ipm = (ssts >> 8) & 0xf; + if (det != 3 || ipm != 1) return 0; + if (sig != 0x00000101u) { + g_log->info("[ahci] IDENTIFY skipped non-SATA port"); + log_hex("[ahci] IDENTIFY skip port=", port); + log_hex("[ahci] IDENTIFY skip sig=", sig); + return 0; + } + + struct dkm_dma_buffer clb; + struct dkm_dma_buffer fis; + struct dkm_dma_buffer table; + struct dkm_dma_buffer data; + if (dma->alloc_pages(1, 1024, 0x100000000ULL, &clb) != 0) return -2; + if (dma->alloc_pages(1, 256, 0x100000000ULL, &fis) != 0) return -3; + if (dma->alloc_pages(1, 128, 0x100000000ULL, &table) != 0) return -4; + if (dma->alloc_pages(1, 512, 0x100000000ULL, &data) != 0) return -5; + + if (ahci_stop_port(port) != 0) { + g_log->warn("[ahci] port stop timeout"); + return -6; + } + + g_abar[(base + PxCLB) / 4] = (u32)(clb.phys & 0xffffffffu); + g_abar[(base + PxCLB + 4) / 4] = (u32)(clb.phys >> 32); + g_abar[(base + PxFB) / 4] = (u32)(fis.phys & 0xffffffffu); + g_abar[(base + PxFB + 4) / 4] = (u32)(fis.phys >> 32); + + ahci_cmd_header *hdr = (ahci_cmd_header *)clb.virt; + ahci_cmd_table *tbl = (ahci_cmd_table *)table.virt; + hdr[0].flags = 5; /* FIS length in DWORDS */ + hdr[0].prdtl = 1; + 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 = (512 - 1) | (1u << 31); + + u8 cmd = ATA_CMD_IDENTIFY; + ahci_fill_identify_fis(tbl->cfis, cmd); + + g_abar[(base + PxIS) / 4] = 0xffffffffu; + g_abar[HBA_IS / 4] = (1u << port); + ahci_start_port(port); + + g_abar[(base + PxCI) / 4] = 1; + for (u32 i = 0; i < 5000000; i++) { + if ((g_abar[(base + PxCI) / 4] & 1u) == 0) { + log_hex("[ahci] IDENTIFY port=", port); + log_hex("[ahci] IDENTIFY status=", g_abar[(base + PxTFD) / 4]); + log_hex("[ahci] IDENTIFY bytes=", hdr[0].prdbc); + u16 *id = (u16 *)data.virt; + log_hex("[ahci] IDENTIFY word0=", id[0]); + log_hex("[ahci] IDENTIFY word49=", id[49]); + log_hex("[ahci] IDENTIFY word83=", id[83]); + ahci_read_lba0(port, hdr, &table, &data); + g_ready_port = port; + g_cmd_header = hdr; + g_cmd_table = table; + g_data = data; + return 1; + } + } + + g_log->warn("[ahci] IDENTIFY timeout"); + log_hex("[ahci] IDENTIFY CI=", g_abar[(base + PxCI) / 4]); + log_hex("[ahci] IDENTIFY IS=", g_abar[(base + PxIS) / 4]); + log_hex("[ahci] IDENTIFY TFD=", g_abar[(base + PxTFD) / 4]); + return -7; +} + +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) return -1; + g_log = api->log; + + g_log->info("[ahci] init begin"); + + u8 bus = 0; + u8 dev = 0; + u8 func = 0; + if (ahci_find(&bus, &dev, &func) != 0) { + g_log->warn("[ahci] AHCI controller not found"); + g_log->info("[ahci] driver ready"); + return 0; + } + + g_log->info("[ahci] AHCI controller found"); + log_hex("[ahci] bus=", bus); + log_hex("[ahci] dev=", dev); + log_hex("[ahci] func=", func); + + u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID); + log_hex("[ahci] vendor=", vd & 0xffff); + log_hex("[ahci] device=", vd >> 16); + + u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff; + command |= PCI_CMD_MEM | PCI_CMD_BUSM; + pci_write(bus, dev, func, PCI_COMMAND, command); + log_hex("[ahci] PCI command=", command); + + u64 abar_phys = (u64)(pci_read(bus, dev, func, PCI_BAR5) & 0xFFFFFFF0u); + u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff; + log_hex("[ahci] ABAR phys=", abar_phys); + log_hex("[ahci] PCI IRQ line=", irq_line); + + if (!api->hhdm_offset || !abar_phys) { + g_log->warn("[ahci] missing HHDM or ABAR; skip MMIO enumeration"); + g_log->info("[ahci] driver ready"); + return 0; + } + + g_abar = (volatile u32 *)(uintptr_t)(api->hhdm_offset + abar_phys); + log_hex("[ahci] HHDM offset=", api->hhdm_offset); + + u32 cap = g_abar[HBA_CAP / 4]; + u32 ghc = g_abar[HBA_GHC / 4]; + u32 is = g_abar[HBA_IS / 4]; + u32 pi = g_abar[HBA_PI / 4]; + u32 vs = g_abar[HBA_VS / 4]; + u32 cap2 = g_abar[HBA_CAP2 / 4]; + u32 bohc = g_abar[HBA_BOHC / 4]; + + log_hex("[ahci] CAP=", cap); + log_hex("[ahci] GHC=", ghc); + log_hex("[ahci] IS=", is); + log_hex("[ahci] PI=", pi); + log_hex("[ahci] VS=", vs); + log_hex("[ahci] CAP2=", cap2); + log_hex("[ahci] BOHC=", bohc); + log_hex("[ahci] ports count=", (cap & 0x1f) + 1); + log_hex("[ahci] cmd slots=", ((cap >> 8) & 0x1f) + 1); + + u32 implemented = 0; + for (u32 port = 0; port < 32; port++) { + if (pi & (1u << port)) { + implemented++; + ahci_log_port(port); + } + } + log_hex("[ahci] implemented ports=", implemented); + + u32 identified = 0; + if (api->dma) { + for (u32 port = 0; port < 32; port++) { + if (pi & (1u << port)) { + int ident = ahci_identify_port(port, api->dma); + if (ident > 0) { + identified++; + break; + } + } + } + } else { + g_log->warn("[ahci] DMA API unavailable; skip IDENTIFY"); + } + log_hex("[ahci] identified ports=", identified); + if (identified && api->block && api->block->register_device) { + struct dkm_block_device_desc desc; + desc.name = "ahci0"; + desc.sector_size = 512; + desc.sector_count = 0; + desc.ctx = 0; + desc.read = ahci_block_read; + int index = api->block->register_device(&desc); + log_hex("[ahci] block provider index=", (u64)(i64)index); + } else if (identified) { + g_log->warn("[ahci] block API unavailable; provider not registered"); + } + + g_log->info("[ahci] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/apic/apic.c b/CODE/DKM/apic/apic.c new file mode 100644 index 0000000..480d38d --- /dev/null +++ b/CODE/DKM/apic/apic.c @@ -0,0 +1,358 @@ +/* DKM APIC Driver — APIC discovery with PIC fallback + * Stage 0, required, depends on "timer", provides "irq" and "apic". + * Current kernel IRQ backend is still PIC; this driver only discovers APIC + * topology and keeps PIC routing active. + */ + +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; + +#define NULL ((void *)0) + +struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +}; + +struct dkm_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 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; +}; + +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[] = { "timer" }; +static const char *const g_provides[] = { "irq", "apic" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "apic", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 3, /* DKM_CLASS_INTERRUPT */ + .stage = 0, + .flags = 1, /* DKM_F_REQUIRED */ + .priority = 0, + .depends = g_depends, + .depends_count = 1, + .provides = g_provides, + .provides_count = 2, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +typedef struct __attribute__((packed)) { + char signature[8]; + u8 checksum; + char oem_id[6]; + u8 revision; + u32 rsdt_addr; +} acpi_rsdp_v1; + +typedef struct __attribute__((packed)) { + char signature[8]; + u8 checksum; + char oem_id[6]; + u8 revision; + u32 rsdt_addr; + u32 length; + u64 xsdt_addr; + u8 ext_checksum; + u8 reserved[3]; +} acpi_rsdp_v2; + +typedef struct __attribute__((packed)) { + char signature[4]; + u32 length; + u8 revision; + u8 checksum; + char oem_id[6]; + char oem_table_id[8]; + u32 oem_revision; + u32 creator_id; + u32 creator_revision; +} acpi_sdt_header; + +typedef struct __attribute__((packed)) { + acpi_sdt_header hdr; + u32 lapic_addr; + u32 flags; +} acpi_madt; + +struct madt_info { + u64 lapic_phys; + u32 flags; + u32 lapic_count; + u32 ioapic_count; + u32 iso_count; +}; + +static const struct dkm_log_api *g_log; + +static int memeq(const char *a, const char *b, u32 n) { + for (u32 i = 0; i < n; i++) { + if (a[i] != b[i]) return 0; + } + return 1; +} + +static u8 checksum8(const u8 *data, u32 len) { + u8 sum = 0; + for (u32 i = 0; i < len; i++) sum += data[i]; + return sum; +} + +static void log_hex(const char *prefix, u64 val) { + static const char hex[] = "0123456789abcdef"; + char buf[19]; + u32 pos = 0; + buf[pos++] = '0'; + buf[pos++] = 'x'; + for (int i = 15; i >= 0; i--) { + buf[pos++] = hex[(val >> (i * 4)) & 0xf]; + } + buf[pos] = 0; + g_log->info(prefix); + g_log->info(buf); +} + +static void cpuid(u32 leaf, u32 *a, u32 *b, u32 *c, u32 *d) { + __asm__ volatile ("cpuid" + : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d) + : "a"(leaf), "c"(0)); +} + +static u64 rdmsr(u32 msr) { + u32 lo; + u32 hi; + __asm__ volatile ("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr)); + return ((u64)hi << 32) | lo; +} + +static int cpu_has_lapic(void) { + u32 a, b, c, d; + cpuid(1, &a, &b, &c, &d); + (void)a; + (void)b; + (void)c; + return (d & (1u << 9)) != 0; +} + +static const acpi_sdt_header *find_table_from_rsdt(const acpi_sdt_header *rsdt, const char sig[4], u32 entry_size) { + if (!rsdt || rsdt->length < sizeof(acpi_sdt_header)) return NULL; + if (checksum8((const u8 *)rsdt, rsdt->length) != 0) return NULL; + + u32 count = (rsdt->length - sizeof(acpi_sdt_header)) / entry_size; + const u8 *entries = (const u8 *)rsdt + sizeof(acpi_sdt_header); + for (u32 i = 0; i < count; i++) { + u64 addr; + if (entry_size == 4) { + addr = (u64)((const u32 *)entries)[i]; + } else { + addr = ((const u64 *)entries)[i]; + } + if (!addr) continue; + + const acpi_sdt_header *hdr = (const acpi_sdt_header *)(uintptr_t)addr; + if (hdr->length >= sizeof(acpi_sdt_header) && memeq(hdr->signature, sig, 4)) { + if (checksum8((const u8 *)hdr, hdr->length) == 0) return hdr; + } + } + return NULL; +} + +static const acpi_madt *find_madt(const void *rsdp_addr) { + if (!rsdp_addr) return NULL; + + const acpi_rsdp_v2 *rsdp = (const acpi_rsdp_v2 *)rsdp_addr; + if (!memeq(rsdp->signature, "RSD PTR ", 8)) return NULL; + if (checksum8((const u8 *)rsdp, 20) != 0) return NULL; + + if (rsdp->revision >= 2 && rsdp->length >= sizeof(acpi_rsdp_v2) && rsdp->xsdt_addr) { + if (checksum8((const u8 *)rsdp, rsdp->length) == 0) { + const acpi_sdt_header *xsdt = (const acpi_sdt_header *)(uintptr_t)rsdp->xsdt_addr; + const acpi_sdt_header *madt = find_table_from_rsdt(xsdt, "APIC", 8); + if (madt) return (const acpi_madt *)madt; + } + } + + if (rsdp->rsdt_addr) { + const acpi_sdt_header *rsdt = (const acpi_sdt_header *)(uintptr_t)(u64)rsdp->rsdt_addr; + const acpi_sdt_header *madt = find_table_from_rsdt(rsdt, "APIC", 4); + if (madt) return (const acpi_madt *)madt; + } + + return NULL; +} + +static void parse_madt_entries(const acpi_madt *madt, struct madt_info *info) { + const u8 *base = (const u8 *)madt; + u32 off = (u32)sizeof(acpi_madt); + + while (off + 2 <= madt->hdr.length) { + const u8 *entry = base + off; + u8 type = entry[0]; + u8 len = entry[1]; + if (len < 2 || off + len > madt->hdr.length) { + g_log->warn("[apic] malformed MADT entry"); + return; + } + + if (type == 0 && len >= 8) { + u8 apic_id = entry[3]; + u32 flags = *(const u32 *)(const void *)(entry + 4); + if (flags & 1u) info->lapic_count++; + log_hex("[apic] CPU LAPIC id=", apic_id); + } else if (type == 1 && len >= 12) { + u8 ioapic_id = entry[2]; + u32 addr = *(const u32 *)(const void *)(entry + 4); + u32 gsi = *(const u32 *)(const void *)(entry + 8); + info->ioapic_count++; + log_hex("[apic] IOAPIC id=", ioapic_id); + log_hex("[apic] IOAPIC addr=", addr); + log_hex("[apic] IOAPIC gsi_base=", gsi); + } else if (type == 2 && len >= 10) { + u8 source = entry[3]; + u32 gsi = *(const u32 *)(const void *)(entry + 4); + u16 flags = *(const u16 *)(const void *)(entry + 8); + info->iso_count++; + log_hex("[apic] ISO source=", source); + log_hex("[apic] ISO gsi=", gsi); + log_hex("[apic] ISO flags=", flags); + } else if (type == 5 && len >= 12) { + u64 override = *(const u64 *)(const void *)(entry + 4); + info->lapic_phys = override; + log_hex("[apic] LAPIC override=", override); + } + + off += len; + } +} + +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) return -1; + g_log = api->log; + + g_log->info("[apic] init begin"); + + if (!api->irq_register) { + g_log->error("[apic] kernel irq_register missing"); + return -1; + } + g_log->info("[apic] kernel IRQ backend present: PIC"); + + if (!cpu_has_lapic()) { + g_log->warn("[apic] CPUID local APIC not present; PIC fallback active"); + g_log->info("[apic] driver ready"); + return 0; + } + g_log->info("[apic] CPUID local APIC present"); + + u64 apic_base = rdmsr(0x1B); + u64 lapic_phys = apic_base & 0xFFFFF000ULL; + log_hex("[apic] IA32_APIC_BASE=", apic_base); + log_hex("[apic] LAPIC phys base=", lapic_phys); + if (apic_base & (1ULL << 8)) g_log->info("[apic] bootstrap processor"); + if (apic_base & (1ULL << 11)) g_log->info("[apic] LAPIC enabled by firmware"); + + struct madt_info info; + info.lapic_phys = lapic_phys; + info.flags = 0; + info.lapic_count = 0; + info.ioapic_count = 0; + info.iso_count = 0; + + const acpi_madt *madt = find_madt(api->rsdp_address); + if (madt) { + g_log->info("[apic] MADT found"); + info.lapic_phys = (u64)madt->lapic_addr; + info.flags = madt->flags; + log_hex("[apic] MADT lapic_phys=", info.lapic_phys); + log_hex("[apic] MADT flags=", info.flags); + parse_madt_entries(madt, &info); + log_hex("[apic] CPU LAPIC count=", info.lapic_count); + log_hex("[apic] IOAPIC count=", info.ioapic_count); + log_hex("[apic] ISO count=", info.iso_count); + } else { + g_log->warn("[apic] MADT not found; PIC fallback active"); + } + + if (api->hhdm_offset && info.lapic_phys) { + volatile u32 *lapic = (volatile u32 *)(uintptr_t)(api->hhdm_offset + info.lapic_phys); + log_hex("[apic] LAPIC ID reg=", lapic[0x20 / 4]); + log_hex("[apic] LAPIC version reg=", lapic[0x30 / 4]); + log_hex("[apic] LAPIC SVR reg=", lapic[0xF0 / 4]); + } else { + g_log->warn("[apic] HHDM unavailable; skip LAPIC MMIO read"); + } + + g_log->info("[apic] discovery complete; PIC routing retained"); + g_log->info("[apic] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/bootfs/bootfs.c b/CODE/DKM/bootfs/bootfs.c new file mode 100644 index 0000000..50074c4 --- /dev/null +++ b/CODE/DKM/bootfs/bootfs.c @@ -0,0 +1,191 @@ +/* DKM BootFS Driver — exposes Limine boot modules as a simple filesystem + * Stage 1, required, provides "bootfs". + * Lists all boot modules with path, size, and address. + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types + * --------------------------------------------------------------- */ +#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_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_api; + const void *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; +}; + +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; +}; + +/* minimal limine types matching kernel */ +struct limine_file { + u64 revision; + void *address; + u64 size; + char *path; + char *cmdline; + u32 media_type; + u32 unused; + u32 tftp_ip; + u32 tftp_port; + u32 partition_index; + u32 mbr_disk_id; + u8 gpt_disk_uuid[16]; + u8 gpt_part_uuid[16]; + u8 part_uuid[16]; +}; + +struct limine_module_response { + u64 revision; + u64 module_count; + struct limine_file **modules; +}; + +/* --------------------------------------------------------------- + * Driver descriptor + * --------------------------------------------------------------- */ +static const char *const g_provides[] = { "bootfs" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "bootfs", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 7, /* DKM_CLASS_FS */ + .stage = 1, + .flags = 1, /* DKM_F_REQUIRED */ + .priority = 0, + .depends = NULL, + .depends_count = 0, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +/* --------------------------------------------------------------- + * Log helpers + * --------------------------------------------------------------- */ +static const struct dkm_log_api *g_log; + +static void log_hex(const char *prefix, u64 val) { + static const char hex[] = "0123456789abcdef"; + char buf[19]; + u32 pos = 0; + buf[pos++] = '0'; buf[pos++] = 'x'; + for (int i = 15; i >= 0; i--) { + buf[pos++] = hex[(val >> (i * 4)) & 0xf]; + } + buf[pos] = 0; + g_log->info(prefix); + g_log->info(buf); +} + +/* --------------------------------------------------------------- + * Driver entry + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) return -1; + g_log = api->log; + + const struct limine_module_response *rsp = + (const struct limine_module_response *)api->boot_modules_response; + + if (!rsp) { + g_log->error("[bootfs] no boot module response"); + return -1; + } + + g_log->info("[bootfs] boot module filesystem"); + log_hex("[bootfs] file count=", rsp->module_count); + + for (u64 i = 0; i < rsp->module_count; i++) { + struct limine_file *f = rsp->modules[i]; + if (!f) continue; + + g_log->info("[bootfs] ----------"); + if (f->path) { + g_log->info("[bootfs] file"); + g_log->info(f->path); + } + log_hex("[bootfs] size=", f->size); + log_hex("[bootfs] addr=", (u64)(uintptr_t)f->address); + if (f->cmdline) { + g_log->info("[bootfs] cmd"); + g_log->info(f->cmdline); + } + } + + g_log->info("[bootfs] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/console_fb/console_fb.c b/CODE/DKM/console_fb/console_fb.c new file mode 100644 index 0000000..2cdfc87 --- /dev/null +++ b/CODE/DKM/console_fb/console_fb.c @@ -0,0 +1,259 @@ +/* DKM Console Framebuffer Driver + * Stage 1, optional, provides "console". + * Light-blue background + centered circular loading animation (4-color arc ring). + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types + * --------------------------------------------------------------- */ +#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_kernel_api { + u32 version; u32 size; u64 feature_bits; + const struct dkm_log_api *log; + const void *mem, *utsm, *irq, *pci_api, *dma, *vfs, *net, *timer, *drr; + const void *rsdp_address, *fb_address; + u64 fb_width, fb_height, fb_pitch; u16 fbpp; +}; + +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_provides[] = { "console" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION, + .desc_size=sizeof(struct dkm_driver_desc), + .name="console_fb", .version="0.2.0", .vendor="Deshab", + .driver_class=5, .stage=1, .flags=0, .priority=0, + .depends=NULL, .depends_count=0, + .provides=g_provides, .provides_count=1, + .min_kernel_abi=1, +}; + +/* --------------------------------------------------------------- + * Colors + * --------------------------------------------------------------- */ +static const u32 BG = 0xFFC8E0F0; /* light blue background */ +static const u32 ARC[4] = { 0xFF4488CC, 0xFF2266AA, 0xFF66AAEE, 0xFF88CCFF }; + +/* --------------------------------------------------------------- + * Drawing helpers – all integer arithmetic, no soft-float + * --------------------------------------------------------------- */ +static u64 fb_w, fb_h, fb_p; + +/* double-buffer sprite for ring area (128×128 = no flicker) */ +#define SPR_SIZE 128 +static u32 g_sprite[SPR_SIZE * SPR_SIZE]; + +static void fb_fill(u32 *fb, u64 pitch, u64 w, u64 h, u32 color) { + for (u64 y = 0; y < h; y++) { + u32 *line = (u32 *)((u8 *)fb + y * pitch); + for (u64 x = 0; x < w; x++) line[x] = 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 + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + if (!api || !api->log) return -1; + if (!api->fb_address || !api->fb_width) { + api->log->warn("[console_fb] no framebuffer"); + return 0; + } + + api->log->info("[console_fb] boot screen"); + + fb_w = api->fb_width; fb_h = api->fb_height; fb_p = api->fb_pitch; + u32 *fb = (u32 *)api->fb_address; + i64 cx = (i64)fb_w / 2, cy = (i64)fb_h / 2, r = 48, thk = 4; + + /* light-blue background */ + fb_fill(fb, fb_p, fb_w, fb_h, BG); + + /* animation: 2000 frames × 2° clockwise × ~1ms, no flicker */ + i64 spr_p = SPR_SIZE * 4; + i64 scx = SPR_SIZE / 2; + i64 scy = SPR_SIZE / 2; + i64 frame = 0, base = 0; + i64 bx = cx - scx; /* screen position of sprite top-left */ + i64 by = cy - scy; + + while (frame < 2000) { + /* draw into sprite buffer — swap globals to sprite layout */ + u64 save_w = fb_w, save_h = fb_h, save_p = fb_p; + fb_w = SPR_SIZE; fb_h = SPR_SIZE; fb_p = spr_p; + + fb_fill(g_sprite, spr_p, SPR_SIZE, SPR_SIZE, BG); + + arc_segment(g_sprite, scx, scy, r, base + 0, 82, ARC[0], thk); + arc_segment(g_sprite, scx, scy, r, base + 90, 82, ARC[1], thk); + arc_segment(g_sprite, scx, scy, r, base +180, 82, ARC[2], thk); + arc_segment(g_sprite, scx, scy, r, base +270, 82, ARC[3], thk); + + fill_disc(g_sprite, scx, scy, r - 8, blend(BG, 0xFFD0F0FF, 80)); + + /* restore screen globals for blit */ + fb_w = save_w; fb_h = save_h; fb_p = save_p; + + /* blit sprite to screen */ + for (i64 sr = 0; sr < SPR_SIZE; sr++) { + i64 sy = by + sr; + if ((u64)sy >= fb_h) continue; + u32 *dst = (u32 *)((u8 *)fb + (u64)sy * fb_p) + bx; + u32 *src = &g_sprite[sr * SPR_SIZE]; + for (i64 sc = 0; sc < SPR_SIZE; sc++) { + i64 sx = bx + sc; + if ((u64)sx >= fb_w) continue; + dst[sc] = src[sc]; + } + } + + for (volatile u32 d = 0; d < 72000; d++) { + __asm__ volatile ("pause"); + } + + base -= 2; + if (base < 0) base += 360; + frame++; + } + + api->log->info("[console_fb] ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/devfs/devfs.c b/CODE/DKM/devfs/devfs.c new file mode 100644 index 0000000..7c60244 --- /dev/null +++ b/CODE/DKM/devfs/devfs.c @@ -0,0 +1,224 @@ +/* DKM DevFS Driver — virtual device filesystem + * Stage 2, optional, depends on "vfs", provides "devfs". + * Exposes kernel platform info as virtual files under /dev/. + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types + * --------------------------------------------------------------- */ +#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_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_api; + const void *dma; + const void *vfs_api; + 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; +}; + +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; +}; + +struct limine_file { + u64 revision; + void *address; + u64 size; + char *path; + char *cmdline; + u8 _pad[56]; +}; + +struct limine_module_response { + u64 revision; + u64 module_count; + struct limine_file **modules; +}; + +/* --------------------------------------------------------------- + * Driver descriptor + * --------------------------------------------------------------- */ +static const char *const g_depends[] = { "vfs" }; +static const char *const g_provides[] = { "devfs" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "devfs", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 7, + .stage = 2, + .flags = 0, + .priority = 0, + .depends = g_depends, + .depends_count = 1, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +/* --------------------------------------------------------------- + * Helpers + * --------------------------------------------------------------- */ +static const struct dkm_log_api *g_log; + +static void log_hex(const char *prefix, u64 val) { + static const char h[] = "0123456789abcdef"; + char buf[19]; u32 p = 0; + buf[p++] = '0'; buf[p++] = 'x'; + for (int i = 15; i >= 0; i--) buf[p++] = h[(val>>(i*4))&0xf]; + buf[p] = 0; + g_log->info(prefix); g_log->info(buf); +} + +/* --------------------------------------------------------------- + * Virtual file helpers + * --------------------------------------------------------------- */ +static void devfs_file(const char *name, const char *content) { + g_log->info("[devfs] /dev/"); + g_log->info(name); + g_log->info("[devfs] "); + g_log->info(content); +} + +/* --------------------------------------------------------------- + * Driver entry + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + if (!api || !api->log) return -1; + g_log = api->log; + + g_log->info("[devfs] virtual device filesystem"); + + /* /dev/version */ + devfs_file("version", "Deshab kernel 0.1.0"); + + /* /dev/platform */ + devfs_file("platform", "x86_64 SAS-R0"); + + /* /dev/fb0 */ + if (api->fb_address) { + char info[64]; + info[0] = 0; + /* manual number conversion for width */ + { + u64 v = api->fb_width; + char tmp[24]; u32 p = 0; + if (v == 0) tmp[p++] = '0'; + else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; } + tmp[p++] = 'x'; + v = api->fb_height; + if (v == 0) tmp[p++] = '0'; + else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; } + tmp[p++] = 'x'; + v = api->fb_bpp; + if (v == 0) tmp[p++] = '0'; + else { char r[24]; u32 q = 0; while (v) { r[q++] = '0' + (char)(v%10); v/=10; } while (q) tmp[p++] = r[--q]; } + tmp[p++] = ' '; + tmp[p++] = 'f'; + tmp[p++] = 'b'; + tmp[p++] = '0'; + tmp[p] = 0; + for (u32 i = 0; i <= p; i++) info[i] = tmp[i]; + } + devfs_file("fb0", info); + } + + /* /dev/modules — count from boot_modules */ + const struct limine_module_response *rsp = + (const struct limine_module_response *)api->boot_modules_response; + if (rsp) { + char buf[24]; + buf[0] = '0'; buf[1] = 'x'; + u64 v = rsp->module_count; + u32 p = 2; + if (v == 0) buf[p++] = '0'; + else { + char r[18]; u32 q = 0; + while (v) { r[q++] = "0123456789abcdef"[v&0xf]; v>>=4; } + while (q) buf[p++] = r[--q]; + } + buf[p] = 0; + devfs_file("modules", buf); + + /* /dev/boot — list paths */ + g_log->info("[devfs] /dev/boot/"); + for (u64 i = 0; i < rsp->module_count; i++) { + struct limine_file *f = rsp->modules[i]; + if (f && f->path) { + g_log->info("[devfs] "); + g_log->info(f->path); + } + } + } + + g_log->info("[devfs] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/e1000/e1000.c b/CODE/DKM/e1000/e1000.c new file mode 100644 index 0000000..7c94cd5 --- /dev/null +++ b/CODE/DKM/e1000/e1000.c @@ -0,0 +1,464 @@ +/* DKM e1000 Driver — Intel 8254x Gigabit Ethernet + * Stage 3, optional, depends on "pci" and "irq", provides "netdev". + * Finds e1000 via PCI, maps MMIO, reads MAC address. + */ + +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef 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_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); +}; + +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); +}; + +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_provides[] = {"netdev"}; +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION, + .desc_size=sizeof(struct dkm_driver_desc), + .name="e1000",.version="0.1.0",.vendor="Deshab", + .driver_class=9,.stage=3,.flags=0,.priority=0, + .depends=g_depends,.depends_count=2, + .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_IRQ_LINE 0x3C + +/* e1000 MMIO registers */ +#define E1000_CTRL 0x0000 +#define E1000_STATUS 0x0008 +#define E1000_EERD 0x0014 +#define E1000_ICR 0x00C0 +#define E1000_IMS 0x00D0 +#define E1000_IMC 0x00D8 +#define E1000_RAL 0x5400 +#define E1000_RAH 0x5404 +#define E1000_RDBAL 0x2800 +#define E1000_RDBAH 0x2804 +#define E1000_RDLEN 0x2808 +#define E1000_RDH 0x2810 +#define E1000_RDT 0x2818 +#define E1000_RXDCTL 0x2828 +#define E1000_RCTL 0x0100 +#define E1000_TDBAL 0x3800 +#define E1000_TDBAH 0x3804 +#define E1000_TDLEN 0x3808 +#define E1000_TDH 0x3810 +#define E1000_TDT 0x3818 +#define E1000_TXDCTL 0x3828 +#define E1000_TCTL 0x0400 +#define E1000_TIPG 0x0410 + +#define E1000_RX_DESC_COUNT 8u +#define E1000_TX_DESC_COUNT 8u +#define E1000_RX_BUF_SIZE 2048u +#define E1000_TX_BUF_SIZE 2048u +#define E1000_POOL_BYTES(count, size) ((u32)((count) * (size))) +#define E1000_POOL_PAGES(bytes) (((bytes) + 4095u) / 4096u) + +#define E1000_RCTL_EN (1u << 1) +#define E1000_RCTL_SBP (1u << 2) +#define E1000_RCTL_UPE (1u << 3) +#define E1000_RCTL_MPE (1u << 4) +#define E1000_RCTL_BAM (1u << 15) +#define E1000_RCTL_SECRC (1u << 26) +#define E1000_RCTL_BSIZE_2048 0u + +#define E1000_TCTL_EN (1u << 1) +#define E1000_TCTL_PSP (1u << 3) +#define E1000_TCTL_CT_SHIFT 4 +#define E1000_TCTL_COLD_SHIFT 12 + +#define E1000_TX_CMD_EOP (1u << 0) +#define E1000_TX_CMD_IFCS (1u << 1) +#define E1000_TX_CMD_RS (1u << 3) +#define E1000_TX_STA_DD (1u << 0) +#define E1000_RX_STA_DD (1u << 0) +#define E1000_CTRL_FD (1u << 0) +#define E1000_CTRL_SLU (1u << 6) + +struct e1000_rx_desc { + u64 addr; + u16 length; + u16 checksum; + u8 status; + u8 errors; + u16 special; +} __attribute__((packed)); + +struct e1000_tx_desc { + u64 addr; + u16 length; + u8 cso; + u8 cmd; + u8 status; + u8 css; + u16 special; +} __attribute__((packed)); + +static const struct dkm_log_api *g_log; +static volatile u32 *g_mmio; +static u64 g_bar0_phys; +static struct e1000_rx_desc *g_rx_desc; +static struct e1000_tx_desc *g_tx_desc; +static u64 g_rx_desc_phys; +static u64 g_tx_desc_phys; +static u8 *g_rx_buf[E1000_RX_DESC_COUNT]; +static u64 g_rx_buf_phys[E1000_RX_DESC_COUNT]; +static u8 *g_tx_buf[E1000_TX_DESC_COUNT]; +static u64 g_tx_buf_phys[E1000_TX_DESC_COUNT]; +static u32 g_rx_tail; +static u32 g_tx_tail; +static int g_rings_ready; + +static __inline__ void outl(u16 p, u32 v) { __asm__("outl %0,%1"::"a"(v),"Nd"(p)); } +static __inline__ u32 inl(u16 p) { u32 v; __asm__("inl %1,%0":"=a"(v):"Nd"(p)); return v; } +static u8 inb(u16 p) { u8 v; __asm__("inb %1,%0":"=a"(v):"Nd"(p)); return v; } +static void outb(u16 p, u8 v) { __asm__("outb %0,%1"::"a"(v),"Nd"(p)); } + +static void raw_log(const char *s) { + while (*s) { + char c = *s++; + if (c == '\n') { + for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; + outb(0x3F8, '\r'); + } + for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; + outb(0x3F8, (u8)c); + } +} + +static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); + outl(PCI_ADDR,a); return inl(PCI_DATA); +} + +static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) { + u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); + outl(PCI_ADDR,a); outl(PCI_DATA,v); +} + +static int e1000_find(u8 *bus, u8 *dev) { + for (u8 d=0; d<32; d++) { + u32 vd = pci_read(0,d,0,PCI_VENDOR_ID); + if ((vd&0xffff)==0xffff) continue; + if ((vd&0xffff)!=0x8086) continue; + u16 did = (u16)(vd>>16); + /* 82540EM(0x100E) / 82545 等经典 e1000,以及 82574L(0x10D3) e1000e */ + if (did==0x100E || did==0x10D3 || did==0x1004 || did==0x100F || + did==0x10D3 || did==0x153A || did==0x1533) { + *bus=0; *dev=d; return 0; + } + } + return -1; +} + +/* 读取 EEPROM 一个 16-bit 字(经典 e1000 EERD 接口)。失败返回 0xffff。 */ +static u16 e1000_eeprom_read(u16 addr) { + /* EERD: bit0=START, addr<<8, 完成后 bit4=DONE, data 在高 16 位 */ + g_mmio[E1000_EERD/4] = ((u32)addr << 8) | 1u; + for (u32 i=0;i<100000;i++) { + u32 v = g_mmio[E1000_EERD/4]; + if (v & (1u<<4)) return (u16)(v >> 16); + } + return 0xffff; +} + +static int irq_handler(u8 irq) { + (void)irq; + if (!g_mmio) return 0; + u32 icr = g_mmio[E1000_ICR/4]; + if (icr) { + g_mmio[E1000_ICR/4] = icr; /* clear */ + } + return 0; +} + +static void copy_bytes(void *dst, const void *src, u32 len) { + u8 *d = (u8 *)dst; + const u8 *s = (const u8 *)src; + for (u32 i=0;ialloc_pages || !g_mmio) return -1; + + struct dkm_dma_buffer rx_ring, tx_ring; + if (dma->alloc_pages(1, 16, 0x100000000ULL, &rx_ring) != 0) return -2; + if (dma->alloc_pages(1, 16, 0x100000000ULL, &tx_ring) != 0) return -3; + g_rx_desc = (struct e1000_rx_desc *)rx_ring.virt; + g_tx_desc = (struct e1000_tx_desc *)tx_ring.virt; + g_rx_desc_phys = rx_ring.phys; + g_tx_desc_phys = tx_ring.phys; + + for (u32 i=0;ialloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -4; + g_rx_buf[i] = (u8 *)b.virt; + g_rx_buf_phys[i] = b.phys; + g_rx_desc[i].addr = b.phys; + g_rx_desc[i].status = 0; + } + + for (u32 i=0;ialloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -5; + g_tx_buf[i] = (u8 *)b.virt; + g_tx_buf_phys[i] = b.phys; + g_tx_desc[i].addr = b.phys; + g_tx_desc[i].status = E1000_TX_STA_DD; + } + raw_log("[e1000.raw] ring/buffers allocated\n"); + + g_mmio[E1000_RCTL/4] = 0; + g_mmio[E1000_TCTL/4] = 0; + + g_mmio[E1000_RDBAL/4] = (u32)(g_rx_desc_phys & 0xffffffffu); + g_mmio[E1000_RDBAH/4] = (u32)(g_rx_desc_phys >> 32); + g_mmio[E1000_RDLEN/4] = E1000_RX_DESC_COUNT * sizeof(struct e1000_rx_desc); + g_mmio[E1000_RDH/4] = 0; + g_rx_tail = E1000_RX_DESC_COUNT - 1; + g_mmio[E1000_RDT/4] = g_rx_tail; + + g_mmio[E1000_TDBAL/4] = (u32)(g_tx_desc_phys & 0xffffffffu); + g_mmio[E1000_TDBAH/4] = (u32)(g_tx_desc_phys >> 32); + g_mmio[E1000_TDLEN/4] = E1000_TX_DESC_COUNT * sizeof(struct e1000_tx_desc); + g_mmio[E1000_TDH/4] = 0; + g_tx_tail = 0; + g_mmio[E1000_TDT/4] = 0; + + /* e1000e/部分实现需要显式打开队列描述符控制;经典 e1000 会忽略/兼容。 */ + g_mmio[E1000_RXDCTL/4] = (1u << 25); + g_mmio[E1000_TXDCTL/4] = (1u << 25); + + g_mmio[E1000_TIPG/4] = 0x0060200Au; + g_mmio[E1000_RCTL/4] = E1000_RCTL_EN | E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_BAM | E1000_RCTL_SECRC | E1000_RCTL_BSIZE_2048; + g_mmio[E1000_TCTL/4] = E1000_TCTL_EN | E1000_TCTL_PSP | (0x0Fu << E1000_TCTL_CT_SHIFT) | (0x40u << E1000_TCTL_COLD_SHIFT); + + g_rings_ready = 1; + raw_log("[e1000.raw] RX/TX rings initialized\n"); + return 0; +} + +static int e1000_tx(void *ctx, const void *packet, u32 length) { + (void)ctx; + raw_log("[e1000.raw] tx enter\n"); + if (!g_rings_ready || !packet || length == 0 || length > E1000_RX_BUF_SIZE) return -1; + u32 index = g_tx_tail; + if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) return -2; + copy_bytes(g_tx_buf[index], packet, length); + raw_log("[e1000.raw] tx copied\n"); + g_tx_desc[index].length = (u16)length; + g_tx_desc[index].cmd = E1000_TX_CMD_EOP | E1000_TX_CMD_IFCS | E1000_TX_CMD_RS; + g_tx_desc[index].status = 0; + g_tx_tail = (index + 1) % E1000_TX_DESC_COUNT; + g_mmio[E1000_TDT/4] = g_tx_tail; + raw_log("[e1000.raw] tx tdt written\n"); + return 0; +} + +static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) { + (void)ctx; + if (!g_rings_ready || !buffer || !out_length) return -1; + u32 next = (g_rx_tail + 1) % E1000_RX_DESC_COUNT; + if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1; + u32 len = g_rx_desc[next].length; + if (len > capacity) return -2; + copy_bytes(buffer, g_rx_buf[next], len); + *out_length = len; + g_rx_desc[next].status = 0; + g_rx_tail = next; + g_mmio[E1000_RDT/4] = g_rx_tail; + return 0; +} + +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + raw_log("[e1000.raw] driver_init entered\n"); + (void)handle; + if (!api||!api->log) return -1; + g_log=api->log; + + raw_log("[e1000.raw] before find\n"); + u8 bus, dev; + if (e1000_find(&bus,&dev)!=0) { + raw_log("[e1000.raw] device not found\n"); + return 0; + } + + raw_log("[e1000.raw] found device\n"); + + u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND); + cmd |= 0x0007u; /* IO Space | Memory Space | Bus Master */ + pci_write(bus, dev, 0, PCI_COMMAND, cmd); + raw_log("[e1000.raw] PCI command enabled\n"); + + g_bar0_phys = (u64)pci_read(bus,dev,0,PCI_BAR0) & 0xFFFFFFF0ULL; + if (!g_bar0_phys) { + raw_log("[e1000.raw] BAR0 unavailable\n"); + return 0; + } + raw_log("[e1000.raw] BAR0 ok\n"); + + u64 hhdm = api->hhdm_offset; + g_mmio = (volatile u32*)(uintptr_t)(hhdm + g_bar0_phys); + (void)g_mmio[E1000_STATUS/4]; + raw_log("[e1000.raw] MMIO status read ok\n"); + + /* 软件复位,确保 RX/TX/RAL/RAH 从已知状态开始配置。 */ + g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | (1u << 26); + for (u32 wait=0; wait<1000000; wait++) { + if (!(g_mmio[E1000_CTRL/4] & (1u << 26))) break; + } + raw_log("[e1000.raw] reset done\n"); + + u32 ral = g_mmio[E1000_RAL/4]; + u32 rah = g_mmio[E1000_RAH/4]; + u8 mac[6]; + mac[0] = (u8)(ral & 0xff); + mac[1] = (u8)((ral>>8) & 0xff); + mac[2] = (u8)((ral>>16) & 0xff); + mac[3] = (u8)((ral>>24) & 0xff); + mac[4] = (u8)(rah & 0xff); + mac[5] = (u8)((rah>>8) & 0xff); + + /* RAL/RAH 为空时从 EEPROM 读 MAC */ + if (ral == 0 && (rah & 0xffff) == 0) { + u16 w0 = e1000_eeprom_read(0); + u16 w1 = e1000_eeprom_read(1); + u16 w2 = e1000_eeprom_read(2); + if (!(w0==0xffff && w1==0xffff && w2==0xffff)) { + mac[0]=(u8)(w0&0xff); mac[1]=(u8)(w0>>8); + mac[2]=(u8)(w1&0xff); mac[3]=(u8)(w1>>8); + mac[4]=(u8)(w2&0xff); mac[5]=(u8)(w2>>8); + } + } + + /* 仍为全 0 时使用与虚拟机配置一致的回退 MAC,保证收发地址匹配 */ + if (mac[0]==0&&mac[1]==0&&mac[2]==0&&mac[3]==0&&mac[4]==0&&mac[5]==0) { + mac[0]=0x52; mac[1]=0x54; mac[2]=0x00; mac[3]=0x12; mac[4]=0x34; mac[5]=0x56; + raw_log("[e1000.raw] using fallback MAC 52:54:00:12:34:56\n"); + } + + /* 写入 RAL/RAH 并置 AV(bit31) 使能接收该单播地址 */ + g_mmio[E1000_RAL/4] = (u32)mac[0] | ((u32)mac[1]<<8) | ((u32)mac[2]<<16) | ((u32)mac[3]<<24); + g_mmio[E1000_RAH/4] = (u32)mac[4] | ((u32)mac[5]<<8) | (1u<<31); + raw_log("[e1000.raw] MAC read ok\n"); + + u32 irq_line = pci_read(bus,dev,0,PCI_IRQ_LINE) & 0xff; + + g_mmio[E1000_CTRL/4] = g_mmio[E1000_CTRL/4] | E1000_CTRL_FD | E1000_CTRL_SLU; + raw_log("[e1000.raw] link setup\n"); + + g_mmio[E1000_IMC/4] = 0xffffffffu; + (void)g_mmio[E1000_ICR/4]; + raw_log("[e1000.raw] interrupts masked\n"); + + int ring_rc = e1000_init_rings(api->dma); + if (ring_rc != 0) raw_log("[e1000.raw] ring init failed\n"); + + if (api->net && api->net->register_device) { + struct dkm_net_device_desc netdev; + netdev.name = "e1000"; + for (int i=0;i<6;i++) netdev.mac[i] = mac[i]; + netdev.flags = DKM_NET_F_LINK_UP; + if (g_rings_ready) netdev.flags |= DKM_NET_F_TX_READY | DKM_NET_F_RX_READY; + netdev.ctx = 0; + netdev.tx = g_rings_ready ? e1000_tx : 0; + netdev.rx_poll = g_rings_ready ? e1000_rx_poll : 0; + int net_index = api->net->register_device(&netdev); + (void)net_index; + raw_log("[e1000.raw] netdev registered\n"); + } else { + raw_log("[e1000.raw] net API unavailable\n"); + } + + g_mmio[E1000_IMC/4] = 0xffffffffu; + + if (api->irq_register && irq_line < 16) { + api->irq_register((u8)irq_line, (void*)(uintptr_t)irq_handler); + raw_log("[e1000.raw] IRQ registered\n"); + } + + raw_log("[e1000.raw] driver ready\n"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; return 0; +} diff --git a/CODE/DKM/fat32/fat32.c b/CODE/DKM/fat32/fat32.c new file mode 100644 index 0000000..645ddbd --- /dev/null +++ b/CODE/DKM/fat32/fat32.c @@ -0,0 +1,307 @@ +/* DKM FAT32 Driver — read-only FAT32 filesystem parser + * Stage 2, optional, depends on "vfs" and "block". + * Reads from an embedded FAT32 test image (boot module "dkm:test_fat32"). + * Parses BPB, walks FAT, reads root dir, lists files, reads a file. + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types + * --------------------------------------------------------------- */ +#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_block_api { + int (*register_device)(const void *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,*utsm,*irq,*pci_api,*dma,*vfs_api,*net,*timer,*drr; + const void *rsdp_address; + const void *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; + 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,*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; +}; + +struct limine_file { + u64 revision; void *address; u64 size; char *path; char *cmdline; u8 _pad[56]; +}; +struct limine_module_response { + u64 revision; u64 module_count; struct limine_file **modules; +}; + +/* --------------------------------------------------------------- + * FAT32 BPB + * --------------------------------------------------------------- */ +typedef struct __attribute__((packed)) { + u8 jmp[3]; + char oem[8]; + u16 bytes_per_sector; + u8 sectors_per_cluster; + u16 reserved_sector_count; + u8 fat_count; + u16 root_entry_count; + u16 total_sectors_16; + u8 media; + u16 sectors_per_fat_16; + u16 sectors_per_track; + u16 heads; + u32 hidden_sectors; + u32 total_sectors_32; + /* FAT32 extended */ + u32 sectors_per_fat; + u16 flags; + u16 version; + u32 root_cluster; + u16 fsinfo_sector; + u16 backup_boot; + u8 reserved[12]; + u8 drive; + u8 nt_flags; + u8 signature; + u32 serial; + char label[11]; + char type[8]; + u8 code[420]; + u16 boot_sig; +} fat32_bpb; + +typedef struct __attribute__((packed)) { + char name[11]; + u8 attr; + u8 nt_reserved; + u8 creation_tenth; + u16 creation_time; + u16 creation_date; + u16 access_date; + u16 cluster_high; + u16 write_time; + u16 write_date; + u16 cluster_low; + u32 file_size; +} fat32_dir_entry; + +/* --------------------------------------------------------------- + * Driver descriptor + * --------------------------------------------------------------- */ +static const char *const g_depends[] = { "vfs", "block" }; +static const char *const g_provides[] = { "fat32" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION, + .desc_size=sizeof(struct dkm_driver_desc), + .name="fat32", .version="0.1.0", .vendor="Deshab", + .driver_class=7, .stage=2, .flags=0, .priority=0, + .depends=g_depends, .depends_count=2, + .provides=g_provides, .provides_count=1, + .min_kernel_abi=1, +}; + +/* --------------------------------------------------------------- + * Helpers + * --------------------------------------------------------------- */ +static const struct dkm_log_api *g_log; +static u8 g_block_image[65536]; + +static int fat32_bpb_valid(const u8 *disk, u64 disk_size) { + if (!disk || disk_size < 512) return 0; + const fat32_bpb *bpb = (const fat32_bpb *)disk; + if (bpb->boot_sig != 0xAA55) return 0; + if (bpb->bytes_per_sector != 512) return 0; + if (bpb->sectors_per_cluster == 0) return 0; + if (bpb->reserved_sector_count == 0) return 0; + if (bpb->fat_count == 0) return 0; + if (bpb->sectors_per_fat == 0) return 0; + if (bpb->root_cluster < 2) return 0; + return 1; +} + +static void log_hex(const char *p, u64 v) { + static const char h[] = "0123456789abcdef"; + char b[19]; u32 i=0; b[i++]='0'; b[i++]='x'; + for (int j=15;j>=0;j--) b[i++]=h[(v>>(j*4))&0xf]; + b[i]=0; g_log->info(p); g_log->info(b); +} + +static u32 read_u32_le(const u8 *p) { + return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24); +} + +static u16 read_u16_le(const u8 *p) { + return (u16)p[0]|((u16)p[1]<<8); +} + +static int strneq(const char *a, const char *b, int n) { + for (int i=0;iinfo(buf); +} + +static const u8 *fat32_find_boot_module_image(const struct dkm_kernel_api *api, u64 *size_out) { + const struct limine_module_response *rsp = + (const struct limine_module_response *)api->boot_modules_response; + if (!rsp) return NULL; + for (u64 i=0;imodule_count;i++) { + struct limine_file *f=rsp->modules[i]; + if (f&&f->cmdline&&strneq(f->cmdline,"dkm:test_fat32",13)) { + if (size_out) *size_out=f->size; + return (const u8*)f->address; + } + } + return NULL; +} + +static const u8 *fat32_select_disk_image(const struct dkm_kernel_api *api, u64 *size_out) { + if (api->block && api->block->device_count && api->block->read && api->block->sector_size) { + u32 count = api->block->device_count(); + log_hex("[fat32] block devices=", count); + if (count > 0 && api->block->sector_size(0) == 512) { + const char *name = api->block->device_name ? api->block->device_name(0) : NULL; + g_log->info("[fat32] trying block provider"); + if (name) g_log->info(name); + int st = api->block->read(0, 0, 128, g_block_image); + log_hex("[fat32] block read status=", (u64)(i64)st); + if (st == 0 && fat32_bpb_valid(g_block_image, sizeof(g_block_image))) { + g_log->info("[fat32] using block provider image"); + if (size_out) *size_out = sizeof(g_block_image); + return g_block_image; + } + g_log->warn("[fat32] block provider image is not FAT32; fallback"); + } + } + + u64 module_size = 0; + const u8 *module = fat32_find_boot_module_image(api, &module_size); + if (module) { + g_log->info("[fat32] using boot module image"); + if (size_out) *size_out = module_size; + return module; + } + return NULL; +} + +/* --------------------------------------------------------------- + * Driver entry + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + if (!api||!api->log) return -1; + g_log=api->log; + + u64 disk_size = 0; + const u8 *disk = fat32_select_disk_image(api, &disk_size); + if (!disk) { g_log->warn("[fat32] no FAT32 image source"); return 0; } + if (!fat32_bpb_valid(disk, disk_size)) { g_log->warn("[fat32] invalid FAT32 BPB"); return 0; } + + g_log->info("[fat32] parsing FAT32 image"); + const fat32_bpb *bpb=(const fat32_bpb*)disk; + u32 bps=bpb->bytes_per_sector; + u32 spc=bpb->sectors_per_cluster; + u32 rsc=bpb->reserved_sector_count; + u32 spf=bpb->sectors_per_fat; + u32 root_clus=bpb->root_cluster; + + log_hex("[fat32] bytes/sec=",bps); + log_hex("[fat32] sec/cluster=",spc); + log_hex("[fat32] fat start sec=",rsc); + log_hex("[fat32] sec/fat=",spf); + log_hex("[fat32] root cluster=",root_clus); + + /* read root directory */ + u32 cluster_size = bps * spc; + u32 fat_start = rsc * bps; + u32 data_start = fat_start + spf * bps; + + g_log->info("[fat32] root dir entries:"); + + u32 clus = root_clus; + while (clus >= 2 && clus < 0x0FFFFFF8) { + u32 clus_off = data_start + (clus - 2) * cluster_size; + const fat32_dir_entry *dir = (const fat32_dir_entry *)(disk + clus_off); + for (u32 e=0; e*32 < cluster_size; e++) { + if (dir[e].name[0]==0) break; + if ((u8)dir[e].name[0]==0xE5) continue; + if (dir[e].attr==0x0F) continue; /* skip LFN */ + if (dir[e].attr & 0x08) continue; /* skip volume label */ + g_log->info("[fat32] "); + log_name11(dir[e].name); + log_hex("[fat32] size=",dir[e].file_size); + + /* try to read README.TXT content */ + if (strneq(dir[e].name,"README TXT",11)) { + u32 f_clus = read_u16_le((const u8*)&dir[e].cluster_low); + u32 f_off = data_start + (f_clus - 2)*cluster_size; + u32 f_size = dir[e].file_size; + g_log->info("[fat32] --- README.TXT content ---"); + u32 n = f_size < 128 ? f_size : 128; + char line[80]; u32 lp=0; + for (u32 k=0;k=78||k==n-1) { line[lp]=0; g_log->info("[fat32] "); g_log->info(line); lp=0; } + } + g_log->info("[fat32] --- end ---"); + } + } + + /* read next cluster from FAT */ + u32 fat_ent_off = fat_start + clus * 4; + clus = read_u32_le(disk + fat_ent_off) & 0x0FFFFFFF; + } + + g_log->info("[fat32] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; return 0; +} diff --git a/CODE/DKM/nvme/nvme.c b/CODE/DKM/nvme/nvme.c new file mode 100644 index 0000000..b74c3a0 --- /dev/null +++ b/CODE/DKM/nvme/nvme.c @@ -0,0 +1,347 @@ +/* DKM NVMe Driver — PCI NVMe discovery and controller register enumeration + * Stage 1 optional storage driver, depends on "pci" and "irq", provides "block". + * This first version only discovers NVMe controllers and reads BAR0 registers; + * it does not enable queues or submit admin commands. + */ + +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; + +#define NULL ((void *)0) + +struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +}; + +struct dkm_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 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; +}; + +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_provides[] = { "block" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "nvme", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 6, /* DKM_CLASS_STORAGE */ + .stage = 1, + .flags = 0, /* optional */ + .priority = 0, + .depends = g_depends, + .depends_count = 2, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 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_BAR0 0x10 +#define PCI_BAR1 0x14 +#define PCI_IRQ_LINE 0x3C + +#define PCI_CMD_MEM (1u << 1) +#define PCI_CMD_BUSM (1u << 2) + +#define NVME_CLASS_STORAGE 0x01 +#define NVME_SUBCLASS_NVM 0x08 +#define NVME_PROGIF_NVME 0x02 + +#define NVME_CAP 0x00 +#define NVME_VS 0x08 +#define NVME_INTMS 0x0C +#define NVME_INTMC 0x10 +#define NVME_CC 0x14 +#define NVME_CSTS 0x1C +#define NVME_NSSR 0x20 +#define NVME_AQA 0x24 +#define NVME_ASQ 0x28 +#define NVME_ACQ 0x30 +#define NVME_CMBLOC 0x38 +#define NVME_CMBSZ 0x3C + +static const struct dkm_log_api *g_log; +static volatile u8 *g_regs; + +static __inline__ void outl(u16 port, u32 value) { + __asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port)); +} + +static __inline__ u32 inl(u16 port) { + u32 value; + __asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + outl(PCI_ADDR, addr); + return inl(PCI_DATA); +} + +static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + outl(PCI_ADDR, addr); + outl(PCI_DATA, value); +} + +static u32 mmio_read32(u32 off) { + return *(volatile u32 *)(void *)(g_regs + off); +} + +static u64 mmio_read64(u32 off) { + u32 lo = mmio_read32(off); + u32 hi = mmio_read32(off + 4); + return ((u64)hi << 32) | lo; +} + +static void log_hex(const char *prefix, u64 value) { + static const char hex[] = "0123456789abcdef"; + char buf[19]; + u32 pos = 0; + buf[pos++] = '0'; + buf[pos++] = 'x'; + for (int i = 15; i >= 0; i--) { + buf[pos++] = hex[(value >> (i * 4)) & 0xf]; + } + buf[pos] = 0; + g_log->info(prefix); + g_log->info(buf); +} + +static int nvme_find(u8 *out_bus, u8 *out_dev, u8 *out_func) { + for (u8 bus = 0; bus < 16; bus++) { + for (u8 dev = 0; dev < 32; dev++) { + u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID); + if ((vd & 0xffff) == 0xffff) continue; + + u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16); + u8 func_count = (header & 0x80) ? 8 : 1; + for (u8 func = 0; func < func_count; func++) { + u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID); + if ((vd2 & 0xffff) == 0xffff) continue; + + u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG); + u8 prog_if = (u8)((class_reg >> 8) & 0xff); + u8 subclass = (u8)((class_reg >> 16) & 0xff); + u8 class_code = (u8)((class_reg >> 24) & 0xff); + + if (class_code == NVME_CLASS_STORAGE && + subclass == NVME_SUBCLASS_NVM && + prog_if == NVME_PROGIF_NVME) { + *out_bus = bus; + *out_dev = dev; + *out_func = func; + return 0; + } + } + } + } + return -1; +} + +static void log_nvme_version(u32 vs) { + log_hex("[nvme] version major=", (vs >> 16) & 0xffff); + log_hex("[nvme] version minor=", (vs >> 8) & 0xff); + log_hex("[nvme] version tertiary=", vs & 0xff); +} + +static void log_cap_fields(u64 cap) { + log_hex("[nvme] CAP.MQES=", cap & 0xffff); + log_hex("[nvme] CAP.CQR=", (cap >> 16) & 1); + log_hex("[nvme] CAP.AMS=", (cap >> 17) & 3); + log_hex("[nvme] CAP.TO=", (cap >> 24) & 0xff); + log_hex("[nvme] CAP.DSTRD=", (cap >> 32) & 0xf); + log_hex("[nvme] CAP.NSSRS=", (cap >> 36) & 1); + log_hex("[nvme] CAP.CSS=", (cap >> 37) & 0xff); + log_hex("[nvme] CAP.MPSMIN=", (cap >> 48) & 0xf); + log_hex("[nvme] CAP.MPSMAX=", (cap >> 52) & 0xf); +} + +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) return -1; + g_log = api->log; + + g_log->info("[nvme] init begin"); + + u8 bus = 0; + u8 dev = 0; + u8 func = 0; + if (nvme_find(&bus, &dev, &func) != 0) { + g_log->warn("[nvme] NVMe controller not found"); + g_log->info("[nvme] driver ready"); + return 0; + } + + g_log->info("[nvme] NVMe controller found"); + log_hex("[nvme] bus=", bus); + log_hex("[nvme] dev=", dev); + log_hex("[nvme] func=", func); + + u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID); + log_hex("[nvme] vendor=", vd & 0xffff); + log_hex("[nvme] device=", vd >> 16); + + u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff; + command |= PCI_CMD_MEM | PCI_CMD_BUSM; + pci_write(bus, dev, func, PCI_COMMAND, command); + log_hex("[nvme] PCI command=", command); + + u32 bar0_lo = pci_read(bus, dev, func, PCI_BAR0); + u32 bar1_hi = pci_read(bus, dev, func, PCI_BAR1); + u64 bar_phys; + if (bar0_lo & 1u) { + g_log->warn("[nvme] BAR0 is IO space; unsupported"); + g_log->info("[nvme] driver ready"); + return 0; + } + + if ((bar0_lo & 0x6u) == 0x4u) { + bar_phys = ((u64)bar1_hi << 32) | (u64)(bar0_lo & 0xFFFFFFF0u); + } else { + bar_phys = (u64)(bar0_lo & 0xFFFFFFF0u); + } + + u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff; + log_hex("[nvme] BAR0 phys=", bar_phys); + log_hex("[nvme] PCI IRQ line=", irq_line); + + if (!api->hhdm_offset || !bar_phys) { + g_log->warn("[nvme] missing HHDM or BAR0; skip MMIO enumeration"); + g_log->info("[nvme] driver ready"); + return 0; + } + + if (bar_phys >= 0x100000000ULL) { + g_log->warn("[nvme] BAR0 is above 4G; current HHDM MMIO map cannot safely read it"); + g_log->info("[nvme] discovery only; MMIO enumeration skipped"); + g_log->info("[nvme] driver ready"); + return 0; + } + + g_regs = (volatile u8 *)(uintptr_t)(api->hhdm_offset + bar_phys); + log_hex("[nvme] HHDM offset=", api->hhdm_offset); + + u64 cap = mmio_read64(NVME_CAP); + u32 vs = mmio_read32(NVME_VS); + u32 intms = mmio_read32(NVME_INTMS); + u32 intmc = mmio_read32(NVME_INTMC); + u32 cc = mmio_read32(NVME_CC); + u32 csts = mmio_read32(NVME_CSTS); + u32 nssr = mmio_read32(NVME_NSSR); + u32 aqa = mmio_read32(NVME_AQA); + u64 asq = mmio_read64(NVME_ASQ); + u64 acq = mmio_read64(NVME_ACQ); + u32 cmbloc = mmio_read32(NVME_CMBLOC); + u32 cmbsz = mmio_read32(NVME_CMBSZ); + + log_hex("[nvme] CAP=", cap); + log_cap_fields(cap); + log_hex("[nvme] VS=", vs); + log_nvme_version(vs); + log_hex("[nvme] INTMS=", intms); + log_hex("[nvme] INTMC=", intmc); + log_hex("[nvme] CC=", cc); + log_hex("[nvme] CSTS=", csts); + log_hex("[nvme] NSSR=", nssr); + log_hex("[nvme] AQA=", aqa); + log_hex("[nvme] ASQ=", asq); + log_hex("[nvme] ACQ=", acq); + log_hex("[nvme] CMBLOC=", cmbloc); + log_hex("[nvme] CMBSZ=", cmbsz); + + if (cc & 1u) { + g_log->warn("[nvme] controller already enabled; leaving state unchanged"); + } else { + g_log->info("[nvme] controller disabled; queues not configured"); + } + + g_log->info("[nvme] discovery only; admin queues disabled"); + g_log->info("[nvme] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/pci/pci.c b/CODE/DKM/pci/pci.c new file mode 100644 index 0000000..8e061b0 --- /dev/null +++ b/CODE/DKM/pci/pci.c @@ -0,0 +1,251 @@ +/* DKM PCI Driver — PCI bus enumeration via config space (0xCF8/0xCFC) + * Stage 0, required, depends on "irq", provides "pci". + * No IRQ needed for enumeration. Scans all buses/devices/functions. + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types + * --------------------------------------------------------------- */ +#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_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_api; + const void *dma; + const void *vfs; + const void *net; + const void *timer; + const void *drr; + const void *rsdp_address; +}; + +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 constants + * --------------------------------------------------------------- */ +#define PCI_CFG_ADDR 0xCF8 +#define PCI_CFG_DATA 0xCFC + +#define PCI_VENDOR_ID 0x00 +#define PCI_DEVICE_ID 0x02 +#define PCI_COMMAND 0x04 +#define PCI_STATUS 0x06 +#define PCI_CLASS 0x0B /* class code */ +#define PCI_SUBCLASS 0x0A /* subclass */ +#define PCI_PROG_IF 0x09 /* prog IF */ +#define PCI_HEADER_TYPE 0x0E +#define PCI_BAR0 0x10 + +/* --------------------------------------------------------------- + * Driver descriptor + * --------------------------------------------------------------- */ +static const char *const g_depends[] = { "irq" }; +static const char *const g_provides[] = { "pci" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "pci", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 2, /* DKM_CLASS_BUS */ + .stage = 0, + .flags = 1, /* DKM_F_REQUIRED */ + .priority = 0, + .depends = g_depends, + .depends_count = 1, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +/* --------------------------------------------------------------- + * I/O helpers + * --------------------------------------------------------------- */ +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; +} + +/* --------------------------------------------------------------- + * PCI config access + * --------------------------------------------------------------- */ +static u32 pci_cfg_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 addr = (u32)((1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC)); + outl(PCI_CFG_ADDR, addr); + return inl(PCI_CFG_DATA); +} + +static void pci_cfg_write(u8 bus, u8 dev, u8 func, u8 reg, u32 val) { + u32 addr = (u32)((1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC)); + outl(PCI_CFG_ADDR, addr); + outl(PCI_CFG_DATA, val); +} + +/* --------------------------------------------------------------- + * Log helpers + * --------------------------------------------------------------- */ +static void log_hex(const char *prefix, u64 val) { + static const char hex[] = "0123456789abcdef"; + char buf[19]; + u32 pos = 0; + buf[pos++] = '0'; buf[pos++] = 'x'; + for (int i = 15; i >= 0; i--) { + buf[pos++] = hex[(val >> (i * 4)) & 0xf]; + } + buf[pos] = 0; + g_log->info(prefix); + g_log->info(buf); +} + +static const char *pci_class_name(u8 base, u8 sub) { + switch (base) { + case 0x00: return "Legacy"; + case 0x01: return "Storage"; + case 0x02: return "Network"; + case 0x03: return "Display"; + case 0x04: return "Multimedia"; + case 0x05: return "Memory"; + case 0x06: return "Bridge"; + case 0x07: return "SimpleComm"; + case 0x08: return "BasePeriph"; + case 0x09: return "Input"; + case 0x0A: return "Docking"; + case 0x0B: return "CPU"; + case 0x0C: return "SerialBus"; + case 0x0D: return "Wireless"; + case 0x0E: return "IntelligentIO"; + case 0x0F: return "Satellite"; + case 0x10: return "Crypto"; + case 0x11: return "SignalProc"; + case 0x12: return "Accelerator"; + default: return "Unknown"; + } + (void)sub; +} + +/* --------------------------------------------------------------- + * Enumeration + * --------------------------------------------------------------- */ +static void pci_scan_bus(u8 bus) { + for (u8 dev = 0; dev < 32; dev++) { + u32 vid_dev = pci_cfg_read(bus, dev, 0, PCI_VENDOR_ID); + u16 vendor = (u16)(vid_dev & 0xffff); + if (vendor == 0xffff) continue; + + u8 header_type = (u8)(pci_cfg_read(bus, dev, 0, PCI_HEADER_TYPE) >> 16); + int multi_func = (header_type & 0x80) != 0; + + u8 func_count = multi_func ? 8 : 1; + for (u8 func = 0; func < func_count; func++) { + u32 vid_dev2 = pci_cfg_read(bus, dev, func, PCI_VENDOR_ID); + u16 vendor2 = (u16)(vid_dev2 & 0xffff); + if (vendor2 == 0xffff) continue; + + u16 device = (u16)(vid_dev2 >> 16); + u32 cc = pci_cfg_read(bus, dev, func, PCI_CLASS); + u8 class_code = (u8)(cc >> 24); + u8 subclass = (u8)((cc >> 16) & 0xff); + + g_log->info("[pci]"); + log_hex("[pci] bus=", bus); + log_hex("[pci] dev=", dev); + log_hex("[pci] func=", func); + log_hex("[pci] vendor=", vendor2); + log_hex("[pci] device=", device); + g_log->info("[pci] class"); + g_log->info(pci_class_name(class_code, subclass)); + log_hex("[pci] class_hex=", class_code); + log_hex("[pci] subclass=", subclass); + } + } +} + +/* --------------------------------------------------------------- + * Driver entry + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) return -1; + g_log = api->log; + + g_log->info("[pci] scanning PCI bus"); + pci_scan_bus(0); + g_log->info("[pci] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/ps2kbd/ps2kbd.c b/CODE/DKM/ps2kbd/ps2kbd.c new file mode 100644 index 0000000..e9a02ca --- /dev/null +++ b/CODE/DKM/ps2kbd/ps2kbd.c @@ -0,0 +1,139 @@ +/* DKM PS/2 Keyboard Driver — IRQ1 scan code → ASCII + * Stage 3, optional, depends on "irq", provides "keyboard". + * Registers IRQ1 handler, translates scan codes via lookup table. + */ + +#include + +/* DKM ABI types */ +#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_kernel_api { + u32 version; u32 size; u64 feature_bits; + const struct dkm_log_api *log; + const void *mem,*utsm,*irq_api,*pci_api,*dma,*vfs_api,*net,*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); +}; + +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; +}; + +/* Driver descriptor */ +static const char *const g_depends[] = {"irq"}; +static const char *const g_provides[] = {"keyboard"}; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic=DKM_DRIVER_MAGIC, .abi_version=DKM_ABI_VERSION, + .desc_size=sizeof(struct dkm_driver_desc), + .name="ps2kbd", .version="0.1.0", .vendor="Deshab", + .driver_class=10, .stage=3, .flags=0, .priority=0, + .depends=g_depends, .depends_count=1, + .provides=g_provides, .provides_count=1, + .min_kernel_abi=1, +}; + +/* Scan code set 1 → ASCII (lowercase) */ +static const char sc_ascii[] = { + 0,0,'1','2','3','4','5','6','7','8','9','0','-','=',0, + 0,'q','w','e','r','t','y','u','i','o','p','[',']',0, + 0,'a','s','d','f','g','h','j','k','l',';','\'','`',0, + '\\','z','x','c','v','b','n','m',',','.','/',0, + '*',0,' ',0 +}; + +static const struct dkm_log_api *g_log; +static volatile int g_has_key; +static volatile u8 g_last_sc; + +/* inline I/O */ +static u8 inb(u16 p) { u8 v; __asm__("inb %1,%0":"=a"(v):"Nd"(p)); return v; } +static void outb(u16 p, u8 v) { __asm__("outb %0,%1"::"a"(v),"Nd"(p)); } + +static void log_hex(const char *prefix, u64 v) { + static const char h[]="0123456789abcdef"; + char b[19]; u32 i=0; b[i++]='0'; b[i++]='x'; + for (int j=15;j>=0;j--) b[i++]=h[(v>>(j*4))&0xf]; + b[i]=0; g_log->info(prefix); g_log->info(b); +} + +static int kbd_handler(u8 irq) { + (void)irq; + g_last_sc = inb(0x60); + g_has_key = 1; + return 0; /* let kernel send EOI */ +} + +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + if (!api||!api->log) return -1; + g_log=api->log; + + if (!api->irq_register) { + g_log->warn("[kbd] irq_register not available"); + return 0; + } + + if (api->irq_register(1, (void*)(uintptr_t)kbd_handler) != 0) { + g_log->error("[kbd] failed to register IRQ1"); + return -1; + } + + g_log->info("[kbd] IRQ1 registered, waiting for keypress"); + g_log->info("[kbd] (boot will continue, key echo in ISR)"); + + /* spin briefly to show a key echo demo */ + int ticks = 0; + while (ticks < 200000) { + if (g_has_key) { + u8 sc = g_last_sc; + g_has_key = 0; + if (sc < 0x80) { + /* make code */ + log_hex("[kbd] sc=", sc); + if (sc < sizeof(sc_ascii) && sc_ascii[sc]) { + char buf[2] = {sc_ascii[sc], 0}; + g_log->info("[kbd] key"); + g_log->info(buf); + } + } + } + for (volatile int z=0;z<100;z++) {} + ticks++; + } + + g_log->info("[kbd] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; return 0; +} diff --git a/CODE/DKM/stub_driver/build_stubs.ps1 b/CODE/DKM/stub_driver/build_stubs.ps1 new file mode 100644 index 0000000..0b5ce64 --- /dev/null +++ b/CODE/DKM/stub_driver/build_stubs.ps1 @@ -0,0 +1,105 @@ +$ErrorActionPreference = 'Stop' + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$Root = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $ScriptDir)) +$Source = Join-Path $Root 'CODE\DKM\stub_driver\stub_driver.c' +$Manifest = Join-Path $Root 'SYSTEM\driver\manifest.json' +$SystemDriver = Join-Path $Root 'SYSTEM\driver' +$BuildDir = Join-Path $Root '.build_tmp\drivers' + +function Find-Tool($Name, $ExtraNames = @()) { + $names = @($Name) + $ExtraNames + foreach ($n in $names) { + $cmd = Get-Command $n -ErrorAction SilentlyContinue + if ($cmd) { return $cmd.Source } + } + $dirs = @( + 'C:\Program Files\LLVM\bin', + 'C:\Program Files (x86)\LLVM\bin', + 'C:\LLVM\bin', + 'D:\LLVM\bin', + 'D:\Program Files\LLVM\bin', + 'D:\Code\LLVM\bin', + 'C:\msys64\clang64\bin', + 'C:\msys64\mingw64\bin', + 'C:\mingw64\bin' + ) + foreach ($dir in $dirs) { + foreach ($n in $names) { + $candidate = Join-Path $dir $n + if (Test-Path $candidate) { return $candidate } + if (-not $n.EndsWith('.exe')) { + $candidateExe = Join-Path $dir "$n.exe" + if (Test-Path $candidateExe) { return $candidateExe } + } + } + } + return $null +} + +function Get-DriverClassValue([string]$Class) { + switch ($Class) { + 'platform' { return 1 } + 'bus' { return 2 } + 'interrupt' { return 3 } + 'timer' { return 4 } + 'console' { return 5 } + 'storage' { return 6 } + 'fs' { return 7 } + 'fs-core' { return 8 } + 'net' { return 9 } + 'input' { return 10 } + 'gpu' { return 11 } + default { return 0 } + } +} + +$clang = Find-Tool 'clang' +$lld = Find-Tool 'ld.lld' +if (-not $clang) { + throw 'clang was not found. Install LLVM/Clang.' +} +if (-not $lld) { + throw 'ld.lld was not found. Install LLVM lld.' +} + +New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null +$manifestData = Get-Content -Raw -LiteralPath $Manifest | ConvertFrom-Json +$count = 0 + +foreach ($stage in $manifestData.stages) { + foreach ($driver in $stage.drivers) { + $relative = [string]$driver.path + if (-not $relative.StartsWith('driver/')) { + throw "Unexpected driver path: $relative" + } + + $destRelative = $relative.Substring('driver/'.Length).Replace('/', '\') + $dest = Join-Path $SystemDriver $destRelative + $destDir = Split-Path -Parent $dest + New-Item -ItemType Directory -Force -Path $destDir | Out-Null + + $name = [string]$driver.name + $classValue = Get-DriverClassValue ([string]$driver.class) + $stageValue = [int]$stage.id + $flags = 0 + if ([bool]$driver.required) { $flags = $flags -bor 1 } + if ($stageValue -le 1) { $flags = $flags -bor 2 } + + $obj = Join-Path $BuildDir "$name.o" + $drv = Join-Path $BuildDir "$name.drv" + $driverNameDefine = 'DRIVER_NAME=' + $name + + & $clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-stack-protector -fno-pic -fno-pie -mno-red-zone -Wall -Wextra -O2 -c $Source -o $obj "-D$driverNameDefine" "-DDRIVER_CLASS=$classValue" "-DDRIVER_STAGE=$stageValue" "-DDRIVER_FLAGS=$flags" + if ($LASTEXITCODE -ne 0) { throw "failed to compile stub driver: $name" } + + & $lld -r -o $drv $obj + if ($LASTEXITCODE -ne 0) { throw "failed to link stub driver: $name" } + + Copy-Item -LiteralPath $drv -Destination $dest -Force + Write-Host "[drivers] installed $relative" + $count++ + } +} + +Write-Host "[drivers] installed $count DKM stub drivers" diff --git a/CODE/DKM/stub_driver/stub_driver.c b/CODE/DKM/stub_driver/stub_driver.c new file mode 100644 index 0000000..1377814 --- /dev/null +++ b/CODE/DKM/stub_driver/stub_driver.c @@ -0,0 +1,93 @@ +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1 + +#ifndef DRIVER_NAME +#define DRIVER_NAME stub +#endif + +#define DKM_STRINGIFY_INNER(x) #x +#define DKM_STRINGIFY(x) DKM_STRINGIFY_INNER(x) +#define DRIVER_NAME_STRING DKM_STRINGIFY(DRIVER_NAME) + +#ifndef DRIVER_CLASS +#define DRIVER_CLASS 0 +#endif + +#ifndef DRIVER_STAGE +#define DRIVER_STAGE 0 +#endif + +#ifndef DRIVER_FLAGS +#define DRIVER_FLAGS 0 +#endif + +#ifndef DRIVER_PRIORITY +#define DRIVER_PRIORITY 0 +#endif + +struct dkm_driver_desc { + uint32_t magic; + uint16_t abi_version; + uint16_t desc_size; + + const char *name; + const char *version; + const char *vendor; + + uint32_t driver_class; + uint32_t stage; + uint32_t flags; + uint32_t priority; + + const char *const *depends; + uint32_t depends_count; + + const char *const *provides; + uint32_t provides_count; + + uint64_t min_kernel_abi; + uint64_t feature_bits; + + uint64_t reserved0; + uint64_t reserved1; +}; + +struct dkm_kernel_api; +struct dkm_driver_handle; + +__attribute__((visibility("default"))) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = DRIVER_NAME_STRING, + .version = "0.1.0-stub", + .vendor = "Deshab", + .driver_class = DRIVER_CLASS, + .stage = DRIVER_STAGE, + .flags = DRIVER_FLAGS, + .priority = DRIVER_PRIORITY, + .depends = 0, + .depends_count = 0, + .provides = 0, + .provides_count = 0, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle) { + (void)api; + (void)handle; + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/timer/timer.c b/CODE/DKM/timer/timer.c new file mode 100644 index 0000000..c2f3865 --- /dev/null +++ b/CODE/DKM/timer/timer.c @@ -0,0 +1,192 @@ +/* DKM Timer Driver - PIT (Programmable Interval Timer) + * Stage 0 platform driver, provides "timer". + * No IDT/IRQ required - uses PIT counter polling for busy-wait delay. + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types (standalone, no kernel headers) + * --------------------------------------------------------------- */ +#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_kernel_api { + u32 version; + u32 size; + u64 feature_bits; + const struct dkm_log_api *log; +}; + +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; +}; + +/* --------------------------------------------------------------- + * PIT I/O ports + * --------------------------------------------------------------- */ +#define PIT_CH0_DATA 0x40 +#define PIT_CH1_DATA 0x41 +#define PIT_CH2_DATA 0x42 +#define PIT_CMD 0x43 + +/* PIT frequency: 1.193182 MHz */ +#define PIT_BASE_FREQ 1193182ULL + +static const char *const g_provides[] = { "timer" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "timer", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 4, /* DKM_CLASS_TIMER */ + .stage = 0, + .flags = 1, /* DKM_F_REQUIRED */ + .priority = 0, + .depends = NULL, + .depends_count = 0, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +/* --------------------------------------------------------------- + * Inline assembly for port I/O (works in Ring0 SAS-R0) + * --------------------------------------------------------------- */ +static __inline__ void outb(u16 port, u8 value) { + __asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port)); +} + +static __inline__ u8 inb(u16 port) { + u8 value; + __asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +/* --------------------------------------------------------------- + * PIT operations + * --------------------------------------------------------------- */ +static const struct dkm_log_api *g_log; + +static void pit_set_channel(u8 channel, u8 mode, u16 count) { + u8 cmd = (u8)((channel << 6) | (3 << 4) | (mode << 1)); + outb(PIT_CMD, cmd); + outb((u16)(PIT_CH0_DATA + channel), (u8)(count & 0xff)); + outb((u16)(PIT_CH0_DATA + channel), (u8)((count >> 8) & 0xff)); +} + +/* Read back current counter value via latch command */ +static u16 pit_read_counter(void) { + u8 cmd = 0xD0; /* read-back: channel 0, latch count, no status */ + outb(PIT_CMD, cmd); + u8 lo = inb(PIT_CH0_DATA); + u8 hi = inb(PIT_CH0_DATA); + return (u16)(((u16)hi << 8) | lo); +} + +/* Busy-wait delay using PIT channel 0 counter. + * count: PIT divisor (1193182 = ~1 second) + * The counter counts DOWN from 'count' to 0. + * We set it to 'count', then poll until it reaches near 0. + */ +static void pit_delay(u16 count) { + pit_set_channel(0, 0, count); /* mode 0: interrupt on terminal count */ + u16 prev = pit_read_counter(); + u64 loops = 0; + while (1) { + u16 cur = pit_read_counter(); + if (cur > prev && loops > 10) { + break; /* counter wrapped → terminal count reached */ + } + prev = cur; + loops++; + } +} + +/* --------------------------------------------------------------- + * Driver entry points + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) { + return -1; + } + g_log = api->log; + + g_log->info("[timer] PIT driver initializing"); + + /* Quick calibration: set PIT to max count, measure how many + * iterations of read_counter() happen before terminal count. + */ + pit_set_channel(0, 0, 0xFFFF); /* 0xFFFF = max, ~55ms at 1.193 MHz */ + u16 prev = pit_read_counter(); + u64 cal_loops = 0; + while (1) { + u16 cur = pit_read_counter(); + cal_loops++; + if (cur > prev && cal_loops > 5) break; + prev = cur; + } + + /* Expected: PIT_BASE_FREQ / 0xFFFF ≈ 18.2 Hz, period ≈ 55 ms */ + g_log->info("[timer] PIT calibration complete"); + g_log->info("[timer] PIT channel 0 operational"); + + /* Do a quick ~100ms delay demo */ + g_log->info("[timer] running 100ms busy-wait demo"); + pit_delay((u16)(PIT_BASE_FREQ / 10)); /* ~0.1 sec = 119318 counts */ + g_log->info("[timer] 100ms delay done"); + + g_log->info("[timer] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/vfs/vfs.c b/CODE/DKM/vfs/vfs.c new file mode 100644 index 0000000..43538bc --- /dev/null +++ b/CODE/DKM/vfs/vfs.c @@ -0,0 +1,222 @@ +/* DKM VFS Driver — Virtual File System layer + * Stage 2, required, depends on "bootfs", provides "vfs". + * Mounts bootfs modules as files, implements basic open/read. + * Demo: reads and displays first 64 bytes of manifest.json. + */ + +#include + +/* --------------------------------------------------------------- + * DKM ABI types + * --------------------------------------------------------------- */ +#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_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_api; + const void *dma; + const void *vfs_api; + 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; +}; + +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; +}; + +struct limine_file { + u64 revision; + void *address; + u64 size; + char *path; + char *cmdline; + u8 _pad[56]; +}; + +struct limine_module_response { + u64 revision; + u64 module_count; + struct limine_file **modules; +}; + +/* --------------------------------------------------------------- + * Driver descriptor + * --------------------------------------------------------------- */ +static const char *const g_depends[] = { "bootfs" }; +static const char *const g_provides[] = { "vfs" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "vfs", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 8, /* DKM_CLASS_FS_CORE */ + .stage = 2, + .flags = 1, + .priority = 0, + .depends = g_depends, + .depends_count = 1, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +/* --------------------------------------------------------------- + * Helpers + * --------------------------------------------------------------- */ +static const struct dkm_log_api *g_log; + +static void log_hex(const char *prefix, u64 val) { + static const char h[] = "0123456789abcdef"; + char buf[19]; + u32 p = 0; + buf[p++] = '0'; buf[p++] = 'x'; + for (int i = 15; i >= 0; i--) buf[p++] = h[(val >> (i*4)) & 0xf]; + buf[p] = 0; + g_log->info(prefix); g_log->info(buf); +} + +static u64 min64(u64 a, u64 b) { return a < b ? a : b; } + +static int path_match(const char *bp, const char *mp) { + /* strip leading '/' from bootloader path */ + while (*bp == '/') bp++; + u64 i = 0; + while (bp[i] && mp[i] && bp[i] == mp[i]) i++; + return bp[i] == 0 && mp[i] == 0; +} + +static int str_len(const char *s) { + int l = 0; while (s[l]) l++; return l; +} + +/* --------------------------------------------------------------- + * Driver entry + * --------------------------------------------------------------- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + if (!api || !api->log) return -1; + g_log = api->log; + + const struct limine_module_response *rsp = + (const struct limine_module_response *)api->boot_modules_response; + + if (!rsp) { g_log->error("[vfs] no modules"); return -1; } + + g_log->info("[vfs] mounting bootfs as root"); + log_hex("[vfs] files=", rsp->module_count); + + /* list all files */ + for (u64 i = 0; i < rsp->module_count; i++) { + struct limine_file *f = rsp->modules[i]; + if (!f || !f->path) continue; + g_log->info("[vfs] /"); + g_log->info(f->path); + } + + /* ---- demo: open & read /driver/manifest.json ---- */ + g_log->info("[vfs] demo: open //driver/manifest.json"); + + struct limine_file *target = NULL; + for (u64 i = 0; i < rsp->module_count; i++) { + struct limine_file *f = rsp->modules[i]; + if (f && f->path && path_match(f->path, "driver/manifest.json")) { + target = f; break; + } + } + + if (!target) { + g_log->warn("[vfs] manifest.json not found"); + } else { + log_hex("[vfs] size=", target->size); + g_log->info("[vfs] content (first 64 bytes):"); + + /* dump first 64 bytes as hex + ascii */ + u64 n = min64(64, target->size); + const u8 *data = (const u8 *)target->address; + char line[80]; + for (u64 off = 0; off < n; off += 16) { + u32 pos = 0; + for (u32 j = 0; j < 16 && off + j < n; j++) { + u8 b = data[off + j]; + line[pos++] = "0123456789abcdef"[b >> 4]; + line[pos++] = "0123456789abcdef"[b & 0xf]; + line[pos++] = ' '; + } + /* ascii */ + line[pos++] = ' '; + for (u32 j = 0; j < 16 && off + j < n; j++) { + u8 c = data[off + j]; + line[pos++] = (c >= 32 && c < 127) ? (char)c : '.'; + } + line[pos] = 0; + g_log->info(line); + } + } + + g_log->info("[vfs] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/DKM/virtio_net/virtio_net.c b/CODE/DKM/virtio_net/virtio_net.c new file mode 100644 index 0000000..3f5d47a --- /dev/null +++ b/CODE/DKM/virtio_net/virtio_net.c @@ -0,0 +1,379 @@ +/* DKM virtio-net Driver — PCI virtio network discovery + * Stage 3 optional network driver, depends on "pci" and "irq", provides "netdev". + * This first version discovers virtio-net devices and logs PCI capabilities; + * it does not negotiate features, create virtqueues, or transmit packets. + */ + +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; + +#define NULL ((void *)0) + +struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +}; + +struct dkm_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); +}; + +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); +}; + +#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_provides[] = { "netdev" }; + +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(struct dkm_driver_desc), + .name = "virtio_net", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = 9, /* DKM_CLASS_NET */ + .stage = 3, + .flags = 0, + .priority = 0, + .depends = g_depends, + .depends_count = 2, + .provides = g_provides, + .provides_count = 1, + .min_kernel_abi = 1, + .feature_bits = 0, + .reserved0 = 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 __inline__ void outl(u16 port, u32 value) { + __asm__ volatile ("outl %0, %1" :: "a"(value), "Nd"(port)); +} + +static __inline__ u32 inl(u16 port) { + u32 value; + __asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + outl(PCI_ADDR, addr); + return inl(PCI_DATA); +} + +static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 value) { + u32 addr = (1u << 31) + | ((u32)bus << 16) + | ((u32)dev << 11) + | ((u32)func << 8) + | ((u32)reg & 0xFC); + outl(PCI_ADDR, addr); + outl(PCI_DATA, value); +} + +static u8 pci_read8(u8 bus, u8 dev, u8 func, u8 reg) { + u32 v = pci_read(bus, dev, func, reg); + return (u8)((v >> ((reg & 3) * 8)) & 0xff); +} + +static void log_hex(const char *prefix, u64 value) { + static const char hex[] = "0123456789abcdef"; + char buf[19]; + u32 pos = 0; + buf[pos++] = '0'; + buf[pos++] = 'x'; + for (int i = 15; i >= 0; i--) { + buf[pos++] = hex[(value >> (i * 4)) & 0xf]; + } + buf[pos] = 0; + g_log->info(prefix); + g_log->info(buf); +} + +static u16 virtio_subsystem_id(u16 device_id) { + if (device_id >= VIRTIO_TRANS_DEVICE_MIN && device_id <= VIRTIO_TRANS_DEVICE_MAX) { + return (u16)(device_id - VIRTIO_TRANS_DEVICE_MIN); + } + if (device_id >= VIRTIO_MODERN_DEVICE_MIN && device_id <= VIRTIO_MODERN_DEVICE_MAX) { + return (u16)(device_id - VIRTIO_MODERN_DEVICE_MIN); + } + return 0xffff; +} + +static int virtio_net_find(u8 *out_bus, u8 *out_dev, u8 *out_func) { + for (u8 bus = 0; bus < 16; bus++) { + for (u8 dev = 0; dev < 32; dev++) { + u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID); + if ((vd & 0xffff) == 0xffff) continue; + + u8 header = (u8)(pci_read(bus, dev, 0, PCI_HEADER) >> 16); + u8 func_count = (header & 0x80) ? 8 : 1; + for (u8 func = 0; func < func_count; func++) { + u32 vd2 = pci_read(bus, dev, func, PCI_VENDOR_ID); + u16 vendor = (u16)(vd2 & 0xffff); + u16 device = (u16)(vd2 >> 16); + if (vendor != PCI_VENDOR_VIRTIO) continue; + + u16 virtio_id = virtio_subsystem_id(device); + u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG); + u8 class_code = (u8)(class_reg >> 24); + if (virtio_id == VIRTIO_NET_DEVICE_ID && class_code == 0x02) { + *out_bus = bus; + *out_dev = dev; + *out_func = func; + return 0; + } + } + } + } + return -1; +} + +static const char *virtio_cap_name(u8 cfg_type) { + if (cfg_type == VIRTIO_PCI_CAP_COMMON_CFG) return "common_cfg"; + if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) return "notify_cfg"; + if (cfg_type == VIRTIO_PCI_CAP_ISR_CFG) return "isr_cfg"; + if (cfg_type == VIRTIO_PCI_CAP_DEVICE_CFG) return "device_cfg"; + if (cfg_type == VIRTIO_PCI_CAP_PCI_CFG) return "pci_cfg"; + return "unknown_cfg"; +} + +static u32 read_bar_raw(u8 bus, u8 dev, u8 func, u8 bar) { + return pci_read(bus, dev, func, (u8)(PCI_BAR0 + bar * 4)); +} + +static void log_bars(u8 bus, u8 dev, u8 func) { + for (u8 bar = 0; bar < 6; bar++) { + u32 raw = read_bar_raw(bus, dev, func, bar); + if (raw == 0 || raw == 0xffffffffu) continue; + log_hex("[virtio_net] BAR index=", bar); + log_hex("[virtio_net] raw=", raw); + if (raw & 1u) { + log_hex("[virtio_net] io base=", raw & 0xfffffffcU); + } else { + log_hex("[virtio_net] mmio base=", raw & 0xfffffff0U); + } + } +} + +static void log_virtio_caps(u8 bus, u8 dev, u8 func) { + u8 cap = pci_read8(bus, dev, func, PCI_CAP_PTR) & 0xfc; + u32 guard = 0; + u32 found = 0; + + while (cap && guard++ < 32) { + u8 cap_id = pci_read8(bus, dev, func, cap + 0); + u8 next = pci_read8(bus, dev, func, cap + 1) & 0xfc; + + if (cap_id == 0x09) { + u8 cfg_type = pci_read8(bus, dev, func, cap + 3); + u8 bar = pci_read8(bus, dev, func, cap + 4); + u32 offset = pci_read(bus, dev, func, cap + 8); + u32 length = pci_read(bus, dev, func, cap + 12); + found++; + g_log->info("[virtio_net] virtio capability"); + g_log->info(virtio_cap_name(cfg_type)); + log_hex("[virtio_net] cap_offset=", cap); + log_hex("[virtio_net] cfg_type=", cfg_type); + log_hex("[virtio_net] bar=", bar); + log_hex("[virtio_net] offset=", offset); + log_hex("[virtio_net] length=", length); + if (cfg_type == VIRTIO_PCI_CAP_NOTIFY_CFG) { + u32 mult = pci_read(bus, dev, func, cap + 16); + log_hex("[virtio_net] notify_off_multiplier=", mult); + } + } + + cap = next; + } + + log_hex("[virtio_net] virtio cap count=", found); + if (!found) { + g_log->warn("[virtio_net] no modern virtio capabilities; transitional IO path expected"); + } +} + +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + + if (!api || !api->log) return -1; + g_log = api->log; + + g_log->info("[virtio_net] init begin"); + + u8 bus = 0; + u8 dev = 0; + u8 func = 0; + if (virtio_net_find(&bus, &dev, &func) != 0) { + g_log->warn("[virtio_net] virtio-net device not found"); + g_log->info("[virtio_net] driver ready"); + return 0; + } + + g_log->info("[virtio_net] virtio-net device found"); + log_hex("[virtio_net] bus=", bus); + log_hex("[virtio_net] dev=", dev); + log_hex("[virtio_net] func=", func); + + u32 vd = pci_read(bus, dev, func, PCI_VENDOR_ID); + u16 vendor = (u16)(vd & 0xffff); + u16 device = (u16)(vd >> 16); + u16 virtio_id = virtio_subsystem_id(device); + u32 class_reg = pci_read(bus, dev, func, PCI_CLASS_REG); + u8 revision = (u8)(class_reg & 0xff); + u8 prog_if = (u8)((class_reg >> 8) & 0xff); + u8 subclass = (u8)((class_reg >> 16) & 0xff); + u8 class_code = (u8)((class_reg >> 24) & 0xff); + + log_hex("[virtio_net] vendor=", vendor); + log_hex("[virtio_net] device=", device); + log_hex("[virtio_net] virtio_id=", virtio_id); + log_hex("[virtio_net] class=", class_code); + log_hex("[virtio_net] subclass=", subclass); + log_hex("[virtio_net] prog_if=", prog_if); + log_hex("[virtio_net] revision=", revision); + + u32 command = pci_read(bus, dev, func, PCI_COMMAND) & 0xffff; + command |= PCI_CMD_IO | PCI_CMD_MEM | PCI_CMD_BUSM; + pci_write(bus, dev, func, PCI_COMMAND, command); + log_hex("[virtio_net] PCI command=", command); + + u32 irq_line = pci_read(bus, dev, func, PCI_IRQ_LINE) & 0xff; + log_hex("[virtio_net] PCI IRQ line=", irq_line); + + log_bars(bus, dev, func); + log_virtio_caps(bus, dev, func); + + if (api->net && api->net->register_device) { + struct dkm_net_device_desc netdev; + netdev.name = "virtio-net"; + for (int i = 0; i < 6; i++) netdev.mac[i] = 0; + netdev.flags = DKM_NET_F_LINK_UP; + netdev.ctx = 0; + netdev.tx = 0; + netdev.rx_poll = 0; + int net_index = api->net->register_device(&netdev); + log_hex("[virtio_net] netdev register rc=", (u64)(i64)net_index); + } else { + g_log->warn("[virtio_net] net API unavailable; netdev not registered"); + } + + g_log->info("[virtio_net] discovery only; virtqueues disabled"); + g_log->info("[virtio_net] driver ready"); + return 0; +} + +__attribute__((visibility("default"))) +int driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + return 0; +} diff --git a/CODE/UTSM/MAKEFILE b/CODE/UTSM/MAKEFILE new file mode 100644 index 0000000..6e88ac0 --- /dev/null +++ b/CODE/UTSM/MAKEFILE @@ -0,0 +1,31 @@ +CC := clang +LD := ld.lld +OBJCOPY := llvm-objcopy + +ROOT := ../.. +OUT := $(ROOT)/SYSTEM/boot/utsm.elf + +CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -Iinclude -O2 +ASFLAGS := -target x86_64-unknown-none -ffreestanding -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Iinclude +LDFLAGS := -nostdlib -static -z max-page-size=0x1000 -T linker.ld + +C_SOURCES := $(wildcard arch/x86_64/*.c kernel/*.c runtime/*.c mm/*.c core/*.c crypto/*.c pckc/*.c utrw/*.c drr/*.c dkm/*.c test/*.c) +S_SOURCES := $(wildcard arch/x86_64/*.S) +OBJECTS := $(C_SOURCES:.c=.o) $(S_SOURCES:.S=.o) + +.PHONY: all clean + +all: $(OUT) + +$(OUT): $(OBJECTS) linker.ld + @if not exist "$(ROOT)\SYSTEM\boot" mkdir "$(ROOT)\SYSTEM\boot" + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.S + $(CC) $(ASFLAGS) -c $< -o $@ + +clean: + powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }" diff --git a/CODE/UTSM/README.md b/CODE/UTSM/README.md new file mode 100644 index 0000000..031b3ed --- /dev/null +++ b/CODE/UTSM/README.md @@ -0,0 +1,935 @@ +// 编译到 SYSTEM/boot/utsm.elf + +# UTSM 内核模块设计 + +UTSM 是当前内核架构中的封缄内存模块。 + +```text +UTSM = UUID-Tuned Sealed Memory +中文名:UUID 调谐封缄内存 +``` + +UTSM 的目标是在单地址空间 Ring0 高性能内核中提供一种轻量内存语义: + +```text +所有普通内存可以被直接读取,但直接读取到的是密文。 +真正有效的明文读写必须经过 UTSM 读写器。 +UUID 作为进程和内存段的调谐因子。 +DRR 负责 root key、checkpoint、recovery log 和回滚。 +``` + +--- + +## 1. 所属总架构 + +```text +SAS-R0-PCQ + 单地址空间 Ring0 任务模型 + Per-CPU O(1) 位图调度器 + +DRR + Dedicated Recovery Root + 看门狗 / checkpoint / recovery log / A-B 回滚 + +UTSM + UUID-Tuned Sealed Memory + UUID 调谐封缄内存 + +UTRW + UTSM Tagged Reader/Writer + UTSM 带标记读写器 + +DMP + Descriptor Meta Page + 段描述符元页 + +PCKC + Per-CPU Key Cache + 每 CPU 密钥缓存 +``` + +--- + +## 2. UTSM 核心规则 + +```text +1. 数据区只存连续密文。 +2. UUID 不插入数据区。 +3. UUID 使用 128-bit 二进制格式。 +4. 每个加密段有独立 DMP。 +5. 每个任务有 process_uuid 和 capability table。 +6. 读写明文必须经过 UTRW。 +7. 直接读取 cipher_base 只得到密文。 +8. 直接写入 cipher_base 会破坏段一致性,段可被标记为 POISONED。 +9. DRR 保存 root key、checkpoint、recovery log 和恢复状态。 +10. 调度器只加载 crypto context,不扫描段,不破坏 O(1) 调度。 +``` + +--- + +## 3. 内存布局 + +每个 UTSM 段由 DMP 和密文数据区组成。 + +```text ++-------------------------------+ +| DMP: Descriptor Meta Page | +| 段描述符元页 | ++-------------------------------+ +| Cipher Data Page 0 | ++-------------------------------+ +| Cipher Data Page 1 | ++-------------------------------+ +| Cipher Data Page 2 | ++-------------------------------+ +| ... | ++-------------------------------+ +``` + +全局区域: + +```text ++--------------------------------------------------+ +| Normal Encrypted Segment Region | +| 普通加密段数据区 | ++--------------------------------------------------+ +| DMP Pool | +| 段描述符元页池 | ++--------------------------------------------------+ +| Segment Table | +| 全局段表 | ++--------------------------------------------------+ +| PCKC Area | +| Per-CPU Key Cache 区 | ++--------------------------------------------------+ +| DRR Reserved Region | +| root key / checkpoint / recovery log | ++--------------------------------------------------+ +``` + +--- + +## 4. 基础类型 + +```c +typedef struct { + uint64_t hi; + uint64_t lo; +} uuid128_t; +``` + +UUID 不转十进制字符串,原因: + +```text +16 字节固定长度 +比较快 +cache 友好 +适合 KDF +适合 SIMD +避免十进制解析和孔洞布局 +``` + +--- + +## 5. 进程加密上下文 + +```c +struct utsm_process_context { + uuid128_t process_uuid; + + uint64_t crypto_epoch; + uint64_t recovery_generation; + + uint32_t capability_table_slot; + uint32_t capability_count; + + uint32_t flags; +}; +``` + +字段含义: + +```text +process_uuid + 创建任务时生成的进程 UUID。 + +crypto_epoch + 当前密钥版本,用于重加密、回滚和 crypto erase。 + +recovery_generation + 当前 DRR checkpoint 版本。 + +capability_table_slot + 该任务 capability table 的位置。 + +capability_count + 该任务拥有的段能力数量。 + +flags + PWC、DMA、强恢复等策略标志。 +``` + +--- + +## 6. 加密段描述符 + +```c +struct utsm_segment_desc { + uuid128_t segment_uuid; + uuid128_t owner_process_uuid; + + void *cipher_base; + uint64_t cipher_length; + + void *dmp_base; + + uint64_t key_epoch; + uint64_t tweak_seed; + + void *dirty_bitmap; + void *mac_table; + + uint32_t generation; + uint32_t flags; + + uint32_t state; + uint32_t last_cpu; + + uint64_t writer_seq; + uint64_t checkpoint_generation; +}; +``` + +段状态: + +```text +FREE +ACTIVE +CHECKPOINTING +SEALED +RECOVERING +POISONED +DESTROYED +``` + +--- + +## 7. DMP 段描述符元页 + +DMP 是热路径加速结构,替代“UUID 数字位置表”。 + +```c +struct utsm_dmp { + uint64_t magic; + uint32_t version; + uint32_t segment_slot; + + uint64_t fast_base; + uint64_t fast_limit; + + uint64_t fast_tweak_seed; + uint64_t fast_key_epoch; + + uint64_t dirty_bitmap_base; + uint64_t mac_table_base; + + uint64_t last_writer_seq; + uint32_t last_cpu; + uint32_t flags; + + uint64_t crc; +}; +``` + +DMP 用于: + +```text +O(1) 段定位 +快速边界检查 +tweak 生成 +Dirty Bitmap 定位 +MAC Table 定位 +checkpoint +recovery +``` + +--- + +## 8. Capability + +任务访问段时使用 capability,而不是裸 UUID。 + +```c +struct utsm_capability { + uint32_t segment_slot; + uint32_t generation; + + uint32_t rights; + uint32_t epoch; + + uint64_t uuid_digest; + uint64_t auth_tag; +}; +``` + +权限: + +```text +READ +WRITE +EXEC +SHARE +DMA +``` + +热路径通过: + +```text +segment_slot + generation + epoch +``` + +直接定位并验证段。 + +--- + +## 9. 加密规则 + +UTSM 不使用十进制凯撒和 UUID 孔洞,改为按 cache line 的 UUID 调谐流式加密。 + +```text +segment_key = KDF( + DRR_root_key, + process_uuid, + segment_uuid, + key_epoch +) +``` + +每 64B cache line 生成 tweak: + +```text +line_index = offset / 64 +tweak = hash(tweak_seed, line_index, key_epoch) +``` + +加密和解密: + +```text +ciphertext = plaintext XOR stream(segment_key, tweak) +plaintext = ciphertext XOR stream(segment_key, tweak) +``` + +--- + +## 10. UTRW 读写器 + +UTRW 分三层: + +```text +Level 0: Raw Access + 直接读 cipher_base,得到密文。 + +Level 1: Fast Path + capability O(1) 定位段,PCKC 命中,无全局锁,无 trap。 + +Level 2: Slow Path + key miss、epoch mismatch、checkpoint、recovery、MAC fail 等异常处理。 +``` + +### 10.1 读流程 + +```text +UTRW_READ(cap, offset, len, dst) + -> segment_slot 找 desc + -> generation 检查 + -> rights 检查 + -> epoch 检查 + -> 边界检查 + -> PCKC 取 key + -> 按 64B line 解密 + -> 返回明文 +``` + +### 10.2 写流程 + +```text +UTRW_WRITE(cap, offset, len, src) + -> segment_slot 找 desc + -> generation 检查 + -> rights 检查 + -> epoch 检查 + -> 边界检查 + -> PCKC 取 key + -> 按 64B line 加密 + -> 写入 cipher data + -> 标记 dirty bitmap + -> writer_seq++ + -> 按策略写 recovery log +``` + +部分写必须先解密旧 64B line,合并新明文后重新加密整条 line。 + +--- + +## 11. PCKC 每 CPU 密钥缓存 + +```c +struct utsm_pckc { + uuid128_t current_process_uuid; + + uint32_t hot_segment_slot[8]; + uint64_t hot_key_epoch[8]; + + void *key_schedule[8]; + + uint64_t hit_count; + uint64_t miss_count; +}; +``` + +规则: + +```text +每 CPU 一个 PCKC。 +调度切换时只加载 next.crypto 指针和 hot_segment_hint。 +不扫描 capability table。 +不刷新全部 key。 +不破坏 PCQ 的 O(1) 调度。 +``` + +--- + +## 12. 与 SAS-R0-PCQ 的整合 + +任务结构扩展: + +```c +struct task { + uint64_t tid; + uint32_t state; + uint32_t priority; + + uint64_t runtime; + uint64_t time_slice; + + void *stack_top; + void *entry; + + struct context cpu_context; + + uint32_t last_cpu; + uint64_t affinity_mask; + + struct utsm_process_context *crypto; + struct utsm_capability_table *cap_table; + + uint32_t hot_segment_hint; + uint32_t fault_segment_slot; + + uint64_t recovery_generation; + uint64_t heartbeat_seq; + + uint32_t flags; + + struct task *prev; + struct task *next; +}; +``` + +任务状态: + +```text +READY +RUNNING +BLOCKED +SLEEPING +RECOVERING +FAULTED +ZOMBIE +``` + +--- + +## 13. 任务创建流程 + +```text +task_create(entry, priority, flags) + -> 分配 TCB + -> 生成 tid + -> 生成 process_uuid + -> 创建 utsm_process_context + -> 创建 stack segment + -> 创建 heap segment + -> 创建 ipc segment 可选 + -> 生成 capability table + -> cap[0] = stack + -> cap[1] = heap + -> 初始化 CPU context + -> 通过 UTRW_WRITE 写入 start packet + -> hot_segment_hint = stack segment + -> state = READY + -> enqueue_task(current_cpu) +``` + +默认 capability: + +```text +cap[0] = stack segment, READ | WRITE +cap[1] = heap segment, READ | WRITE +cap[2] = ipc segment, READ | WRITE | SHARE,可选 +cap[3] = code segment, READ | EXEC,可选 +``` + +--- + +## 14. 调度切换流程 + +```text +schedule() + -> old = current + -> utsm_switch_out(old) + -> next = pick_next_task() + -> utsm_switch_in(next) + -> context_switch(old, next) +``` + +`utsm_switch_out` 只做: + +```text +标记 PWC dirty flush +保存 hot_segment_hint +更新 recovery_generation +更新 heartbeat +``` + +`utsm_switch_in` 只做: + +```text +设置 PCKC.current_process_uuid +加载 next.crypto +检查 recovery_generation +预热 hot_segment_hint +``` + +禁止在调度路径中: + +```text +扫描所有 segment +扫描 capability table +计算 MAC +写 checkpoint +重加密整段 +``` + +--- + +## 15. Checkpoint / Rollback 协议 + +### 15.1 Checkpoint 类型 + +```text +Light Checkpoint + 保存 segment table、DMP、dirty shard summary、writer_seq、key_epoch、MAC root。 + +Dirty Page Checkpoint + 保存 dirty page 的密文副本、dirty page MAC、dirty shard、segment descriptor。 + +Boot Checkpoint + 保存内核镜像 slot、UTSM root epoch、DRR boot status、启动阶段状态。 +``` + +### 15.2 Dirty Shard 分片 + +Dirty Bitmap 不再只是一整块大 bitmap,而是拆成多个 Dirty Shard。 + +```c +struct utsm_dirty_shard { + uint32_t dirty_count; + uint32_t shard_id; + + uint64_t page_base; + uint64_t page_count; + + uint64_t last_checkpoint_epoch; + void *bitmap; +}; +``` + +规则: + +```text +1. 每个 shard 管理一段连续页范围。 +2. UTRW_WRITE 标记 dirty page 时,同时更新 dirty_count。 +3. checkpoint 时 dirty_count == 0 的 shard 直接跳过。 +4. checkpoint 只扫描 dirty_count > 0 的 shard。 +5. 每 CPU 可以维护 dirty shard list,避免全局扫描。 +``` + +复杂度从: + +```text +扫描全 dirty bitmap +``` + +优化为: + +```text +O(number_of_dirty_shards + number_of_dirty_pages) +``` + +### 15.3 A/B Checkpoint Metadata 双槽 + +每个 checkpoint metadata 使用双槽。 + +```text +Checkpoint Metadata Slot A +Checkpoint Metadata Slot B +``` + +提交规则: + +```text +当前 active = A +新 checkpoint 写 B +B 写完 metadata、dirty index、MAC root、CRC +flush +最后原子切换 active = B +旧 A 保留为 fallback +``` + +恢复规则: + +```text +优先读取 active slot +如果 active slot CRC 错误或 generation 不完整 + fallback 到另一个 slot +如果两个 slot 都损坏 + 进入 system rollback +``` + +这样可以避免 checkpoint 写一半崩溃导致旧稳定点也损坏。 + +### 15.4 正常 checkpoint 流程 + +```text +DRR 发起 checkpoint + -> checkpoint_generation++ + -> 选择 inactive metadata slot + -> UTSM 遍历 dirty shard list + -> 跳过 dirty_count == 0 的 shard + -> 对 dirty shard 内的 ACTIVE 段执行 checkpoint + -> desc.state = CHECKPOINTING + -> 刷出 PWC / per-CPU dirty buffer + -> 复制 dirty ciphertext page 到 checkpoint region + -> 计算 page MAC + -> 写 segment desc snapshot + -> 写 DMP snapshot + -> 写 dirty shard summary + -> 写 checkpoint metadata CRC + -> flush checkpoint metadata + -> 原子切换 active metadata slot + -> CHECKPOINT_COMMIT + -> 清 dirty bitmap / dirty_count + -> desc.state = ACTIVE +``` + +### 15.5 rollback 级别 + +```text +Page Rollback + 单页损坏,从 checkpoint slot 恢复密文页。 + +Segment Rollback + 整段损坏,恢复 segment desc、DMP、key_epoch、dirty shard、MAC table。 + +System Rollback + 控制面严重损坏,写 crash reason,reboot,由 bootloader 切换 last_good_slot。 +``` + +### 15.6 DRR Emergency Pool + +DRR 启动时必须预留独立紧急内存池。 + +```text +DRR Emergency Pool + emergency pages + recovery log buffer + checkpoint metadata buffer + DRR private stack + DRR metadata slab + crash buffer +``` + +规则: + +```text +1. 普通 allocator 永远不能分配 Emergency Pool。 +2. Emergency Pool 只供 watchdog、checkpoint、rollback、crash dump 使用。 +3. 普通系统 OOM 不等于 DRR OOM。 +4. recovery 路径禁止依赖普通堆分配器。 +5. Emergency Pool 耗尽时直接进入 system rollback。 +``` + +--- + +## 16. 启动流程 + +### 16.1 Bootloader 阶段 + +```text +1. 读取 Boot Control Block。 +2. 校验 BCB CRC。 +3. 选择 active_slot。 +4. 如果 active_slot 失败次数超限,切换 last_good_slot。 +5. 将 SYSTEM 作为打包后 IMG 内的系统根目录。 +6. 加载 SYSTEM/boot/utsm.elf。 +7. 加载或暴露 SYSTEM/driver/manifest.json。 +8. 传入 memory map、BCB 地址、DRR reserved region 信息。 +``` + +### 16.2 DRR 初始化 + +```text +1. 保留 DRR region。 +2. 初始化 watchdog。 +3. 建立 root key 或 root key handle。 +4. 初始化 recovery log。 +5. 初始化 checkpoint region。 +6. 初始化 heartbeat page。 +7. 初始化 DRR Emergency Pool。 +8. 初始化 A/B checkpoint metadata slot。 +``` + +### 16.3 UTSM 初始化 + +```text +1. 接收 DRR root key handle。 +2. 初始化 global crypto epoch。 +3. 初始化 segment table。 +4. 初始化 DMP pool。 +5. 初始化 PCKC area。 +6. 初始化 dirty shard pool。 +7. 初始化 dirty bitmap pool。 +8. 初始化 MAC table pool。 +9. 注册 UTRW fast path / slow path。 +``` + +### 16.4 Scheduler 初始化 + +```text +1. 初始化 per-CPU runqueue。 +2. 初始化 per-CPU heartbeat。 +3. 初始化每 CPU PCKC。 +4. 创建 bootstrap task。 +5. 创建核心服务 task。 +6. 将 task 加入 PCQ runqueue。 +``` + +### 16.5 Driver Manifest 加载 + +驱动清单位于系统根目录下: + +```text +SYSTEM/driver/manifest.json +``` + +规则: + +```text +1. manifest.json 是启动驱动模块的唯一默认清单。 +2. 驱动文件使用 ELF64 .drv 格式。 +3. 驱动按 stage 分阶段加载。 +4. required=true 的驱动失败时进入 DRR recovery 或 panic。 +5. required=false 的驱动失败时标记 FAILED 并继续启动。 +6. stage0/stage1 可由 bootloader 预加载为 boot module。 +7. stage2/stage3 在文件系统可用后从 SYSTEM/driver 扫描加载。 +``` + +默认阶段: + +```text +stage0: platform,timer / apic / acpi / pci +stage1: boot,console / block / bootfs +stage2: filesystem,vfs / fat32 / devfs +stage3: optional,net / input / gpu +``` + +### 16.6 DKM 驱动 ABI 摘要 + +驱动模块系统: + +```text +DKM = Deshab Kernel Module +DSM = Driver Startup Manager +``` + +每个 ELF .drv 必须导出: + +```text +driver_desc +driver_init +driver_exit +``` + +`driver_desc` 至少描述: + +```text +magic / abi_version / desc_size +name / version / vendor +driver_class / stage / flags / priority +depends / provides +min_kernel_abi / feature_bits +``` + +驱动入口: + +```c +int driver_init(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle); +int driver_exit(struct dkm_driver_handle *handle); +``` + +驱动默认通过 `dkm_kernel_api` 访问内核服务: + +```text +log +mem +utsm +irq +pci +dma +vfs +net +timer +drr +``` + +ELF loader 第一版支持: + +```text +必须支持: + R_X86_64_64 + R_X86_64_RELATIVE + R_X86_64_GLOB_DAT + R_X86_64_JUMP_SLOT + +建议支持: + R_X86_64_PC32 + R_X86_64_PLT32 + R_X86_64_32 / R_X86_64_32S + +第一版禁止: + TLS relocation + IFUNC + lazy binding + 外部动态库依赖 + 用户态 libc 依赖 +``` + +驱动加载状态机: + +```text +DISCOVERED + -> QUEUED + -> DEP_WAIT + -> LOADING + -> ELF_CHECKED + -> MEMORY_ALLOCATED + -> RELOCATED + -> ABI_CHECKED + -> INITING + -> ACTIVE +``` + +失败状态: + +```text +FAILED_ELF +FAILED_RELOCATION +FAILED_DEPENDENCY +FAILED_ABI +FAILED_INIT +FAILED_REQUIRED +``` + +stage0/stage1 boot module 查找规则: + +```text +1. 如果 path 在 boot module table 中,直接从内存加载。 +2. 否则如果 VFS 已可用,从 SYSTEM/path 加载。 +3. 否则返回 BOOT_MODULE_NOT_FOUND。 +``` + +完整设计见: + +```text +RE/驱动模块ABI设计.md +``` + +--- + +## 17. 错误处理 + +UTRW 错误状态: + +```text +OK +KEY_MISS_RETRY +STALE_CAPABILITY +EPOCH_EXPIRED +OUT_OF_BOUNDS +ACCESS_DENIED +CHECKPOINT_BUSY +RECOVERING +MAC_FAILED +SEGMENT_POISONED +CORRUPTED_DESCRIPTOR +``` + +严重错误处理: + +```text +1. 记录 task.fault_segment_slot。 +2. task.state = FAULTED。 +3. 从 runqueue 移除。 +4. 通知 DRR。 +5. DRR 决定刷新 capability、回滚 segment、kill task 或 system rollback。 +``` + +--- + +## 18. UTSM v1 定稿 + +```text +UTSM v1 包含: + 二进制 UUID + 连续密文数据区 + 独立 DMP + 全局 Segment Table + Segment Capability + Per-CPU Key Cache + UTRW 统一读写器 + Dirty Shard + Dirty Bitmap + MAC Table + DRR Emergency Pool + A/B Checkpoint Metadata + DRR checkpoint / rollback 对接 + SAS-R0-PCQ task 整合 + +加密粒度: + 64B cache line + +Dirty 粒度: + 4KB page + +Dirty 分片: + Dirty Shard,checkpoint 只扫描 dirty_count > 0 的 shard + +MAC 粒度: + 4KB page + +Checkpoint 元数据: + A/B 双槽,CRC 校验,原子切换 active slot + +大页优化: + 2MB hugepage 可选 +``` diff --git a/CODE/UTSM/arch/x86_64/boot.S b/CODE/UTSM/arch/x86_64/boot.S new file mode 100644 index 0000000..40effc9 --- /dev/null +++ b/CODE/UTSM/arch/x86_64/boot.S @@ -0,0 +1,21 @@ +.section .text +.global _start +.type _start, @function +_start: + cli + cld + lea kernel_stack_top(%rip), %rsp + andq $-16, %rsp + call kernel_main +.hang: + cli + hlt + jmp .hang + +.section .bss +.align 16 +.global kernel_stack +.global kernel_stack_top +kernel_stack: + .skip 16384 +kernel_stack_top: diff --git a/CODE/UTSM/arch/x86_64/boot.o b/CODE/UTSM/arch/x86_64/boot.o new file mode 100644 index 0000000..fe22384 Binary files /dev/null and b/CODE/UTSM/arch/x86_64/boot.o differ diff --git a/CODE/UTSM/arch/x86_64/halt.c b/CODE/UTSM/arch/x86_64/halt.c new file mode 100644 index 0000000..ac38053 --- /dev/null +++ b/CODE/UTSM/arch/x86_64/halt.c @@ -0,0 +1,5 @@ +void arch_halt_forever(void) { + for (;;) { + __asm__ volatile ("cli; hlt"); + } +} diff --git a/CODE/UTSM/arch/x86_64/halt.o b/CODE/UTSM/arch/x86_64/halt.o new file mode 100644 index 0000000..7ff613d Binary files /dev/null and b/CODE/UTSM/arch/x86_64/halt.o differ diff --git a/CODE/UTSM/arch/x86_64/idt.c b/CODE/UTSM/arch/x86_64/idt.c new file mode 100644 index 0000000..190af77 --- /dev/null +++ b/CODE/UTSM/arch/x86_64/idt.c @@ -0,0 +1,222 @@ +#include +#include +#include + +/* IDT gate descriptor (64-bit) */ +typedef struct __attribute__((packed)) { + u16 offset_low; + u16 selector; + u8 ist; + u8 type_attr; + u16 offset_mid; + u32 offset_high; + u32 reserved; +} idt_entry; + +/* IDT register */ +typedef struct __attribute__((packed)) { + u16 limit; + u64 base; +} idt_ptr; + +#define IDT_ENTRIES 256 +static idt_entry g_idt[IDT_ENTRIES] __attribute__((aligned(16))); + +#define PIC1_CMD 0x20 +#define PIC1_DATA 0x21 +#define PIC2_CMD 0xA0 +#define PIC2_DATA 0xA1 +#define ICW1_ICW4 0x01 +#define ICW1_INIT 0x10 + +/* assembly stub declarations */ +extern void isr_stub_0(void); +extern void isr_stub_1(void); +extern void isr_stub_2(void); +extern void isr_stub_3(void); +extern void isr_stub_4(void); +extern void isr_stub_5(void); +extern void isr_stub_6(void); +extern void isr_stub_7(void); +extern void isr_stub_8(void); +extern void isr_stub_9(void); +extern void isr_stub_10(void); +extern void isr_stub_11(void); +extern void isr_stub_12(void); +extern void isr_stub_13(void); +extern void isr_stub_14(void); +extern void isr_stub_15(void); +extern void isr_stub_16(void); +extern void isr_stub_17(void); +extern void isr_stub_18(void); +extern void isr_stub_19(void); +extern void isr_stub_20(void); +extern void isr_stub_21(void); +extern void isr_stub_22(void); +extern void isr_stub_23(void); +extern void isr_stub_24(void); +extern void isr_stub_25(void); +extern void isr_stub_26(void); +extern void isr_stub_27(void); +extern void isr_stub_28(void); +extern void isr_stub_29(void); +extern void isr_stub_30(void); +extern void isr_stub_31(void); +extern void isr_stub_32(void); +extern void isr_stub_33(void); +extern void isr_stub_34(void); +extern void isr_stub_35(void); +extern void isr_stub_36(void); +extern void isr_stub_37(void); +extern void isr_stub_38(void); +extern void isr_stub_39(void); +extern void isr_stub_40(void); +extern void isr_stub_41(void); +extern void isr_stub_42(void); +extern void isr_stub_43(void); +extern void isr_stub_44(void); +extern void isr_stub_45(void); +extern void isr_stub_46(void); +extern void isr_stub_47(void); + +static void *g_isr_table[] = { + isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3, + isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7, + isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11, + isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15, + isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19, + isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23, + isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27, + isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31, + isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35, + isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39, + isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43, + isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47, +}; + +static void outb(u16 port, u8 value) { + __asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port)); +} + +static u8 inb(u16 port) { + u8 value; + __asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static void io_wait(void) { + outb(0x80, 0); +} + +static void pic_remap(void) { + /* save masks */ + u8 m1 = inb(PIC1_DATA); + u8 m2 = inb(PIC2_DATA); + + /* ICW1: start init + ICW4 */ + outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4); + io_wait(); + outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4); + io_wait(); + + /* ICW2: vector offsets */ + outb(PIC1_DATA, 0x20); /* master: vectors 0x20-0x27 */ + io_wait(); + outb(PIC2_DATA, 0x28); /* slave: vectors 0x28-0x2F */ + io_wait(); + + /* ICW3: master/slave wiring */ + outb(PIC1_DATA, 0x04); /* master: IRQ2 → slave */ + io_wait(); + outb(PIC2_DATA, 0x02); /* slave: cascade identity */ + io_wait(); + + /* ICW4: 8086 mode */ + outb(PIC1_DATA, 0x01); + io_wait(); + outb(PIC2_DATA, 0x01); + io_wait(); + + /* restore masks: mask all except IRQ1 (keyboard) */ + outb(PIC1_DATA, 0xFD); /* 1111_1101: enable IRQ0 (timer) and IRQ1 (kb) */ + outb(PIC2_DATA, 0xFF); /* mask all slave */ +} + +static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) { + u64 addr = (u64)handler; + g_idt[vector].offset_low = (u16)(addr & 0xffff); + g_idt[vector].selector = 0x08; /* kernel code segment (from GDT) */ + g_idt[vector].ist = ist; + g_idt[vector].type_attr = type; + g_idt[vector].offset_mid = (u16)((addr >> 16) & 0xffff); + g_idt[vector].offset_high = (u32)(addr >> 32); + g_idt[vector].reserved = 0; +} + +void idt_init(void) { + log_info("[IDT] init begin"); + + for (u16 i = 0; i < 48; i++) { + idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E); /* present, ring0, 64-bit interrupt gate */ + } + /* fill unused entries with null */ + for (u16 i = 48; i < IDT_ENTRIES; i++) { + idt_set_entry((u8)i, 0, 0, 0); + } + + idt_ptr idtr; + idtr.limit = (u16)(sizeof(g_idt) - 1); + idtr.base = (u64)&g_idt; + + __asm__ volatile ("lidt %0" :: "m"(idtr)); + + pic_remap(); + log_info("[IDT] PIC remapped (master=0x20 slave=0x28)"); + + __asm__ volatile ("sti"); + log_info("[IDT] interrupts enabled"); + log_info("[IDT] init ok"); +} + +/* IRQ handler table */ +static irq_handler_t g_irq_handlers[16]; + +int irq_register(u8 irq, irq_handler_t handler) { + if (irq >= 16) return -1; + g_irq_handlers[irq] = handler; + return 0; +} + +/* Stack frame pushed by isr_common in idt.S */ +typedef struct { + u64 r11, r10, r9, r8, rax, rcx, rdx, rsi, rdi; + u64 vector; + u64 error_code; + u64 rip, cs, rflags; +} isr_frame; + +void idt_handler(isr_frame *frame) { + if (frame->vector < 32) { + log_error("[IDT] exception"); + log_hex64("[IDT] vector=", frame->vector); + log_hex64("[IDT] err=", frame->error_code); + log_hex64("[IDT] rip=", frame->rip); + for (;;) { __asm__ volatile ("cli; hlt"); } + } + + if (frame->vector >= 32 && frame->vector <= 47) { + u8 irq = (u8)(frame->vector - 0x20); + + /* call registered handler */ + if (irq < 16 && g_irq_handlers[irq]) { + int suppress_eoi = g_irq_handlers[irq](irq); + if (suppress_eoi) return; + } + + /* send EOI */ + if (frame->vector >= 0x28) { + outb(PIC2_CMD, 0x20); + } + outb(PIC1_CMD, 0x20); + } +} diff --git a/CODE/UTSM/arch/x86_64/idt.o b/CODE/UTSM/arch/x86_64/idt.o new file mode 100644 index 0000000..35528da Binary files /dev/null and b/CODE/UTSM/arch/x86_64/idt.o differ diff --git a/CODE/UTSM/arch/x86_64/idt_stubs.S b/CODE/UTSM/arch/x86_64/idt_stubs.S new file mode 100644 index 0000000..39a4ce9 --- /dev/null +++ b/CODE/UTSM/arch/x86_64/idt_stubs.S @@ -0,0 +1,62 @@ +/* IDT assembly stubs — SAS-R0 interrupt vectors */ +.section .text + +.macro isr_no_err n +.global isr_stub_\n +.type isr_stub_\n, @function +isr_stub_\n: + pushq $0 + pushq $\n + jmp isr_common +.endm + +.macro isr_err n +.global isr_stub_\n +.type isr_stub_\n, @function +isr_stub_\n: + pushq $\n + jmp isr_common +.endm + +/* Exceptions 0-31 */ +isr_no_err 0; isr_no_err 1; isr_no_err 2; isr_no_err 3 +isr_no_err 4; isr_no_err 5; isr_no_err 6; isr_no_err 7 +isr_err 8; isr_no_err 9; isr_err 10; isr_err 11 +isr_err 12; isr_err 13; isr_err 14; isr_no_err 15 +isr_no_err 16; isr_err 17; isr_no_err 18; isr_no_err 19 +isr_no_err 20; isr_no_err 21; isr_no_err 22; isr_no_err 23 +isr_no_err 24; isr_no_err 25; isr_no_err 26; isr_no_err 27 +isr_no_err 28; isr_no_err 29; isr_err 30; isr_no_err 31 + +/* IRQ 32-47 */ +isr_no_err 32; isr_no_err 33; isr_no_err 34; isr_no_err 35 +isr_no_err 36; isr_no_err 37; isr_no_err 38; isr_no_err 39 +isr_no_err 40; isr_no_err 41; isr_no_err 42; isr_no_err 43 +isr_no_err 44; isr_no_err 45; isr_no_err 46; isr_no_err 47 + +/* Common ISR */ +isr_common: + pushq %rdi + pushq %rsi + pushq %rdx + pushq %rcx + pushq %rax + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + + movq %rsp, %rdi + call idt_handler + + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rax + popq %rcx + popq %rdx + popq %rsi + popq %rdi + addq $16, %rsp + iretq diff --git a/CODE/UTSM/arch/x86_64/idt_stubs.o b/CODE/UTSM/arch/x86_64/idt_stubs.o new file mode 100644 index 0000000..db48f90 Binary files /dev/null and b/CODE/UTSM/arch/x86_64/idt_stubs.o differ diff --git a/CODE/UTSM/arch/x86_64/io.c b/CODE/UTSM/arch/x86_64/io.c new file mode 100644 index 0000000..26d384d --- /dev/null +++ b/CODE/UTSM/arch/x86_64/io.c @@ -0,0 +1,15 @@ +#include + +void outb(u16 port, u8 value) { + __asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +u8 inb(u16 port) { + u8 value; + __asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +void io_wait(void) { + outb(0x80, 0); +} diff --git a/CODE/UTSM/arch/x86_64/io.o b/CODE/UTSM/arch/x86_64/io.o new file mode 100644 index 0000000..07d8e5e Binary files /dev/null and b/CODE/UTSM/arch/x86_64/io.o differ diff --git a/CODE/UTSM/arch/x86_64/limine.h b/CODE/UTSM/arch/x86_64/limine.h new file mode 100644 index 0000000..b7baddb --- /dev/null +++ b/CODE/UTSM/arch/x86_64/limine.h @@ -0,0 +1,154 @@ +#ifndef UTSM_LIMINE_H +#define UTSM_LIMINE_H + +#include + +#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88ULL, 0x0a82e883a194f07bULL + +struct limine_hhdm_response { + u64 revision; + u64 offset; +}; + +struct limine_hhdm_request { + u64 id[4]; + u64 revision; + struct limine_hhdm_response *response; +}; + +struct limine_kernel_address_response { + u64 revision; + u64 physical_base; + u64 virtual_base; +}; + +struct limine_kernel_address_request { + u64 id[4]; + u64 revision; + struct limine_kernel_address_response *response; +}; + +#define LIMINE_KERNEL_ADDRESS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63ULL, 0xb2644a48c516a487ULL } + +struct limine_file { + u64 revision; + void *address; + u64 size; + char *path; + char *cmdline; + u32 media_type; + u32 unused; + u32 tftp_ip; + u32 tftp_port; + u32 partition_index; + u32 mbr_disk_id; + u8 gpt_disk_uuid[16]; + u8 gpt_part_uuid[16]; + u8 part_uuid[16]; +}; + +struct limine_module_response { + u64 revision; + u64 module_count; + struct limine_file **modules; +}; + +struct limine_module_request { + u64 id[4]; + u64 revision; + struct limine_module_response *response; +}; + +#define LIMINE_MODULE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x3e7e279702be32afULL, 0xca1c4f3bd1280ceeULL } + +struct limine_rsdp_response { + u64 revision; + void *address; +}; + +struct limine_rsdp_request { + u64 id[4]; + u64 revision; + struct limine_rsdp_response *response; +}; + +#define LIMINE_RSDP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43ULL, 0x27637845accdcf3cULL } + +struct limine_framebuffer { + void *address; + u64 width; + u64 height; + u64 pitch; + u16 bpp; + u8 memory_model; + u8 red_mask_size; + u8 red_mask_shift; + u8 green_mask_size; + u8 green_mask_shift; + u8 blue_mask_size; + u8 blue_mask_shift; + u8 unused[7]; + u64 edid_size; + void *edid; + u64 mode_count; + struct limine_video_mode **modes; +}; + +struct limine_video_mode { + u64 pitch; + u64 width; + u64 height; + u16 bpp; + u8 memory_model; + u8 red_mask_size; + u8 red_mask_shift; + u8 green_mask_size; + u8 green_mask_shift; + u8 blue_mask_size; + u8 blue_mask_shift; +}; + +struct limine_framebuffer_response { + u64 revision; + u64 framebuffer_count; + struct limine_framebuffer **framebuffers; +}; + +struct limine_framebuffer_request { + u64 id[4]; + u64 revision; + struct limine_framebuffer_response *response; +}; + +#define LIMINE_FRAMEBUFFER_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75ULL, 0xa3148604f6fab11bULL } + +#define LIMINE_MEMMAP_USABLE 0 +#define LIMINE_MEMMAP_RESERVED 1 +#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2 +#define LIMINE_MEMMAP_ACPI_NVS 3 +#define LIMINE_MEMMAP_BAD_MEMORY 4 +#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5 +#define LIMINE_MEMMAP_KERNEL_AND_MODULES 6 +#define LIMINE_MEMMAP_FRAMEBUFFER 7 + +struct limine_memmap_entry { + u64 base; + u64 length; + u64 type; +}; + +struct limine_memmap_response { + u64 revision; + u64 entry_count; + struct limine_memmap_entry **entries; +}; + +struct limine_memmap_request { + u64 id[4]; + u64 revision; + struct limine_memmap_response *response; +}; + +#define LIMINE_MEMMAP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806fULL, 0xe304acdfc50c3c62ULL } + +#endif diff --git a/CODE/UTSM/arch/x86_64/serial.c b/CODE/UTSM/arch/x86_64/serial.c new file mode 100644 index 0000000..ca4971b --- /dev/null +++ b/CODE/UTSM/arch/x86_64/serial.c @@ -0,0 +1,45 @@ +#include + +void outb(u16 port, u8 value); +u8 inb(u16 port); + +#define COM1 0x3F8 + +static int serial_ready(void) { + return (inb(COM1 + 5) & 0x20) != 0; +} + +static int serial_wait_ready(void) { + for (u64 spin = 0; spin < 1000000ULL; spin++) { + if (serial_ready()) { + return 1; + } + } + return 0; +} + +void serial_init(void) { + outb(COM1 + 1, 0x00); + outb(COM1 + 3, 0x80); + outb(COM1 + 0, 0x03); + outb(COM1 + 1, 0x00); + outb(COM1 + 3, 0x03); + outb(COM1 + 2, 0xC7); + outb(COM1 + 4, 0x0B); +} + +void serial_putc(char c) { + if (c == '\n') { + serial_putc('\r'); + } + if (!serial_wait_ready()) { + return; + } + outb(COM1, (u8)c); +} + +void serial_write(const char *s) { + while (*s) { + serial_putc(*s++); + } +} diff --git a/CODE/UTSM/arch/x86_64/serial.o b/CODE/UTSM/arch/x86_64/serial.o new file mode 100644 index 0000000..b25f5f8 Binary files /dev/null and b/CODE/UTSM/arch/x86_64/serial.o differ diff --git a/CODE/UTSM/core/capability.c b/CODE/UTSM/core/capability.c new file mode 100644 index 0000000..8eec4a2 --- /dev/null +++ b/CODE/UTSM/core/capability.c @@ -0,0 +1,29 @@ +#include +#include + +int utsm_cap_validate(utsm_capability cap, u32 required_rights, u64 offset, u64 len, utsm_segment_desc **out_desc) { + utsm_segment_desc *desc = utsm_get_segment(cap.segment_slot); + if (!desc) { + return UTSM_ERR_INVALID; + } + if (desc->state == UTSM_SEG_POISONED) { + return UTSM_ERR_POISONED; + } + if (desc->state != UTSM_SEG_ACTIVE) { + return UTSM_ERR_STATE; + } + if (desc->generation != cap.generation) { + return UTSM_ERR_STALE_CAP; + } + if ((cap.rights & required_rights) != required_rights) { + return UTSM_ERR_ACCESS; + } + if ((u32)desc->key_epoch != cap.epoch) { + return UTSM_ERR_EPOCH; + } + if (offset > desc->cipher_length || len > desc->cipher_length || offset + len > desc->cipher_length) { + return UTSM_ERR_BOUNDS; + } + *out_desc = desc; + return UTSM_OK; +} diff --git a/CODE/UTSM/core/capability.o b/CODE/UTSM/core/capability.o new file mode 100644 index 0000000..f2df05a Binary files /dev/null and b/CODE/UTSM/core/capability.o differ diff --git a/CODE/UTSM/core/dirty.c b/CODE/UTSM/core/dirty.c new file mode 100644 index 0000000..900f0d3 --- /dev/null +++ b/CODE/UTSM/core/dirty.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +static void bitmap_set(u64 *bitmap, u64 bit) { + bitmap[bit / 64] |= 1ULL << (bit % 64); +} + +static bool bitmap_test(u64 *bitmap, u64 bit) { + return (bitmap[bit / 64] & (1ULL << (bit % 64))) != 0; +} + +void utsm_dirty_init(utsm_segment_desc *desc, u64 page_count) { + desc->dirty_shard_count = 1; + desc->dirty_shards = (utsm_dirty_shard *)kmem_alloc(sizeof(utsm_dirty_shard)); + if (!desc->dirty_shards) { + return; + } + u64 words = (page_count + 63) / 64; + desc->dirty_shards[0].dirty_count = 0; + desc->dirty_shards[0].shard_id = 0; + desc->dirty_shards[0].page_base = 0; + desc->dirty_shards[0].page_count = page_count; + desc->dirty_shards[0].last_checkpoint_epoch = 0; + desc->dirty_shards[0].bitmap = (u64 *)kmem_alloc_aligned(words * sizeof(u64), 8); + if (desc->dirty_shards[0].bitmap) { + for (u64 i = 0; i < words; i++) { + desc->dirty_shards[0].bitmap[i] = 0; + } + } +} + +void utsm_mark_dirty(utsm_segment_desc *desc, u64 offset, u64 len) { + if (!desc || !desc->dirty_shards || !desc->dirty_shards[0].bitmap || len == 0) { + return; + } + u64 first = offset / UTSM_PAGE_SIZE; + u64 last = (offset + len - 1) / UTSM_PAGE_SIZE; + utsm_dirty_shard *shard = &desc->dirty_shards[0]; + for (u64 page = first; page <= last && page < shard->page_count; page++) { + if (!bitmap_test(shard->bitmap, page)) { + bitmap_set(shard->bitmap, page); + shard->dirty_count++; + } + } +} diff --git a/CODE/UTSM/core/dirty.o b/CODE/UTSM/core/dirty.o new file mode 100644 index 0000000..4675261 Binary files /dev/null and b/CODE/UTSM/core/dirty.o differ diff --git a/CODE/UTSM/core/dmp.c b/CODE/UTSM/core/dmp.c new file mode 100644 index 0000000..db22170 --- /dev/null +++ b/CODE/UTSM/core/dmp.c @@ -0,0 +1,20 @@ +#include +#include + +#define UTSM_DMP_MAGIC 0x5554534d444d5031ULL + +void utsm_dmp_init(utsm_dmp *dmp, utsm_segment_desc *desc, u32 slot) { + dmp->magic = UTSM_DMP_MAGIC; + dmp->version = 1; + dmp->segment_slot = slot; + dmp->fast_base = (u64)desc->cipher_base; + dmp->fast_limit = (u64)desc->cipher_base + desc->cipher_length; + dmp->fast_tweak_seed = desc->tweak_seed; + dmp->fast_key_epoch = desc->key_epoch; + dmp->dirty_bitmap_base = desc->dirty_shards ? (u64)desc->dirty_shards[0].bitmap : 0; + dmp->mac_table_base = (u64)desc->mac_table; + dmp->last_writer_seq = desc->writer_seq; + dmp->last_cpu = desc->last_cpu; + dmp->flags = desc->flags; + dmp->crc = dmp->magic ^ dmp->fast_base ^ dmp->fast_limit ^ dmp->fast_tweak_seed; +} diff --git a/CODE/UTSM/core/dmp.o b/CODE/UTSM/core/dmp.o new file mode 100644 index 0000000..57b5b87 Binary files /dev/null and b/CODE/UTSM/core/dmp.o differ diff --git a/CODE/UTSM/core/process.c b/CODE/UTSM/core/process.c new file mode 100644 index 0000000..941ddd3 --- /dev/null +++ b/CODE/UTSM/core/process.c @@ -0,0 +1,11 @@ +#include +#include + +void utsm_process_create_test(utsm_process_context *process, u64 seed) { + process->process_uuid = uuid_make_test(seed); + process->crypto_epoch = 1; + process->recovery_generation = drr_recovery_generation(); + process->capability_table_slot = 0; + process->capability_count = 0; + process->flags = 0; +} diff --git a/CODE/UTSM/core/process.o b/CODE/UTSM/core/process.o new file mode 100644 index 0000000..64a7b02 Binary files /dev/null and b/CODE/UTSM/core/process.o differ diff --git a/CODE/UTSM/core/segment.c b/CODE/UTSM/core/segment.c new file mode 100644 index 0000000..f1ba0de --- /dev/null +++ b/CODE/UTSM/core/segment.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +u64 utsm_next_generation(void); + +static u64 digest_uuid(uuid128_t id) { + return id.hi ^ id.lo ^ (id.hi >> 32) ^ (id.lo << 17); +} + +static u64 make_tweak_seed(uuid128_t process_uuid, uuid128_t segment_uuid, u64 epoch) { + return process_uuid.hi ^ process_uuid.lo ^ segment_uuid.hi ^ segment_uuid.lo ^ (epoch * 0x9e3779b97f4a7c15ULL); +} + +int utsm_create_segment(utsm_process_context *process, u64 size, u32 flags, utsm_capability *out_cap) { + if (!process || !out_cap || size == 0) { + return UTSM_ERR_INVALID; + } + + u32 slot = UTSM_MAX_SEGMENTS; + utsm_segment_desc *desc = NULL; + for (u32 i = 0; i < UTSM_MAX_SEGMENTS; i++) { + utsm_segment_desc *candidate = utsm_get_segment(i); + if (candidate->state == UTSM_SEG_FREE) { + slot = i; + desc = candidate; + break; + } + } + if (!desc) { + return UTSM_ERR_NO_MEMORY; + } + + u64 length = utsm_align_up_u64(size, UTSM_CACHE_LINE_SIZE); + u64 pages = utsm_align_up_u64(length, UTSM_PAGE_SIZE) / UTSM_PAGE_SIZE; + u8 *cipher = (u8 *)kmem_alloc_page(pages); + utsm_dmp *dmp = (utsm_dmp *)kmem_alloc_aligned(sizeof(utsm_dmp), 64); + void *mac_table = kmem_alloc_aligned(pages * sizeof(u64), 8); + if (!cipher || !dmp || !mac_table) { + return UTSM_ERR_NO_MEMORY; + } + + for (u64 i = 0; i < pages * UTSM_PAGE_SIZE; i++) { + cipher[i] = 0; + } + + desc->segment_uuid = uuid_make_test(utsm_next_generation() + slot); + desc->owner_process_uuid = process->process_uuid; + desc->cipher_base = cipher; + desc->cipher_length = length; + desc->dmp_base = dmp; + desc->key_epoch = process->crypto_epoch; + desc->tweak_seed = make_tweak_seed(process->process_uuid, desc->segment_uuid, desc->key_epoch); + desc->mac_table = mac_table; + desc->generation++; + desc->flags = flags; + desc->state = UTSM_SEG_ACTIVE; + desc->last_cpu = 0; + desc->writer_seq = 0; + desc->checkpoint_generation = process->recovery_generation; + + utsm_dirty_init(desc, pages); + utsm_dmp_init(dmp, desc, slot); + + out_cap->segment_slot = slot; + out_cap->generation = desc->generation; + out_cap->rights = 0; + if (flags & UTSM_SEG_F_READ) out_cap->rights |= UTSM_RIGHT_READ; + if (flags & UTSM_SEG_F_WRITE) out_cap->rights |= UTSM_RIGHT_WRITE; + if (flags & UTSM_SEG_F_EXEC) out_cap->rights |= UTSM_RIGHT_EXEC; + if (flags & UTSM_SEG_F_DMA) out_cap->rights |= UTSM_RIGHT_DMA; + out_cap->epoch = (u32)desc->key_epoch; + out_cap->uuid_digest = digest_uuid(desc->segment_uuid); + out_cap->auth_tag = out_cap->uuid_digest ^ digest_uuid(process->process_uuid) ^ desc->key_epoch; + + process->capability_count++; + return UTSM_OK; +} diff --git a/CODE/UTSM/core/segment.o b/CODE/UTSM/core/segment.o new file mode 100644 index 0000000..6751ff4 Binary files /dev/null and b/CODE/UTSM/core/segment.o differ diff --git a/CODE/UTSM/core/utsm_init.c b/CODE/UTSM/core/utsm_init.c new file mode 100644 index 0000000..85c7a65 --- /dev/null +++ b/CODE/UTSM/core/utsm_init.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +static utsm_segment_desc g_segments[UTSM_MAX_SEGMENTS]; +static u64 g_generation = 1; + +void utsm_init(void) { + log_info("[UTSM] segment table reset begin"); + for (u32 i = 0; i < UTSM_MAX_SEGMENTS; i++) { + g_segments[i].state = UTSM_SEG_FREE; + g_segments[i].generation = 1; + } + log_info("[UTSM] segment table reset ok"); + + g_generation = 1; + log_info("[UTSM] pckc init begin"); + utsm_pckc_init(); + log_info("[UTSM] pckc init ok"); +} + +utsm_segment_desc *utsm_get_segment(u32 slot) { + if (slot >= UTSM_MAX_SEGMENTS) { + return NULL; + } + return &g_segments[slot]; +} + +u64 utsm_next_generation(void) { + return g_generation++; +} diff --git a/CODE/UTSM/core/utsm_init.o b/CODE/UTSM/core/utsm_init.o new file mode 100644 index 0000000..b5b256f Binary files /dev/null and b/CODE/UTSM/core/utsm_init.o differ diff --git a/CODE/UTSM/crypto/kdf.c b/CODE/UTSM/crypto/kdf.c new file mode 100644 index 0000000..ea824b9 --- /dev/null +++ b/CODE/UTSM/crypto/kdf.c @@ -0,0 +1,19 @@ +#include +#include + +static u64 mix64(u64 x) { + x ^= x >> 30; + x *= 0xbf58476d1ce4e5b9ULL; + x ^= x >> 27; + x *= 0x94d049bb133111ebULL; + x ^= x >> 31; + return x; +} + +void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key) { + const u64 *root = drr_get_root_key(); + out_key->words[0] = mix64(root[0] ^ process_uuid.hi ^ segment_uuid.lo ^ key_epoch); + out_key->words[1] = mix64(root[1] ^ process_uuid.lo ^ segment_uuid.hi ^ (key_epoch << 1)); + out_key->words[2] = mix64(root[2] ^ process_uuid.hi ^ segment_uuid.hi ^ (key_epoch << 7)); + out_key->words[3] = mix64(root[3] ^ process_uuid.lo ^ segment_uuid.lo ^ (key_epoch << 13)); +} diff --git a/CODE/UTSM/crypto/kdf.o b/CODE/UTSM/crypto/kdf.o new file mode 100644 index 0000000..d10f5d4 Binary files /dev/null and b/CODE/UTSM/crypto/kdf.o differ diff --git a/CODE/UTSM/crypto/stream.c b/CODE/UTSM/crypto/stream.c new file mode 100644 index 0000000..b587b30 --- /dev/null +++ b/CODE/UTSM/crypto/stream.c @@ -0,0 +1,19 @@ +#include +#include + +static u64 next_stream_word(u64 x) { + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + return x; +} + +void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out) { + u64 state = key->words[0] ^ key->words[1] ^ key->words[2] ^ key->words[3] ^ tweak; + for (u64 i = 0; i < UTSM_CACHE_LINE_SIZE; i += 8) { + state = next_stream_word(state + i + 0xa5a5a5a5a5a5a5a5ULL); + for (u64 j = 0; j < 8; j++) { + out[i + j] = in[i + j] ^ (u8)(state >> (j * 8)); + } + } +} diff --git a/CODE/UTSM/crypto/stream.o b/CODE/UTSM/crypto/stream.o new file mode 100644 index 0000000..099c537 Binary files /dev/null and b/CODE/UTSM/crypto/stream.o differ diff --git a/CODE/UTSM/crypto/tweak.c b/CODE/UTSM/crypto/tweak.c new file mode 100644 index 0000000..133a472 --- /dev/null +++ b/CODE/UTSM/crypto/tweak.c @@ -0,0 +1,14 @@ +#include + +static u64 mix64_tweak(u64 x) { + x ^= x >> 33; + x *= 0xff51afd7ed558ccdULL; + x ^= x >> 33; + x *= 0xc4ceb9fe1a85ec53ULL; + x ^= x >> 33; + return x; +} + +u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch) { + return mix64_tweak(tweak_seed ^ (line_index * 0x9e3779b97f4a7c15ULL) ^ (key_epoch << 32)); +} diff --git a/CODE/UTSM/crypto/tweak.o b/CODE/UTSM/crypto/tweak.o new file mode 100644 index 0000000..bf8db0a Binary files /dev/null and b/CODE/UTSM/crypto/tweak.o differ diff --git a/CODE/UTSM/dkm/boot_modules.c b/CODE/UTSM/dkm/boot_modules.c new file mode 100644 index 0000000..9ec951a --- /dev/null +++ b/CODE/UTSM/dkm/boot_modules.c @@ -0,0 +1,444 @@ +#include +#include +#include +#include +#include "../arch/x86_64/limine.h" + +#define ELF_MAGIC0 0x7f +#define ELF_MAGIC1 'E' +#define ELF_MAGIC2 'L' +#define ELF_MAGIC3 'F' +#define ELFCLASS64 2 +#define ELFDATA2LSB 1 +#define ET_REL 1 +#define ET_DYN 3 +#define ET_EXEC 2 +#define EM_X86_64 62 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_PROGBITS 1 +#define SHT_NOBITS 8 + +#define SHF_ALLOC 0x2 +#define SHF_WRITE 0x1 +#define SHF_EXECINSTR 0x4 + +#define R_X86_64_64 1 +#define R_X86_64_32 10 +#define R_X86_64_32S 11 +#define R_X86_64_PC32 2 + +void *memset(void *dst, int value, usize len); +void *memcpy(void *dst, const void *src, usize len); + +__attribute__((used, section(".limine_requests"))) +volatile struct limine_module_request g_module_request = { + .id = LIMINE_MODULE_REQUEST_ID, + .revision = 0, + .response = 0 +}; + +__attribute__((used, section(".limine_requests_start"))) +static volatile u64 g_limine_requests_start[4] = { LIMINE_COMMON_MAGIC, 0xf6b8f4b39de7d1aeULL, 0xfab91a6940fcb9cfULL }; + +__attribute__((used, section(".limine_requests_end"))) +static volatile u64 g_limine_requests_end[2] = { 0, 0 }; + +__attribute__((used, section(".limine_requests"))) +volatile struct limine_rsdp_request g_rsdp_request = { + .id = LIMINE_RSDP_REQUEST_ID, + .revision = 0, + .response = 0 +}; + +__attribute__((used, section(".limine_requests"))) +volatile struct limine_framebuffer_request g_fb_request = { + .id = LIMINE_FRAMEBUFFER_REQUEST_ID, + .revision = 0, + .response = 0 +}; + +__attribute__((used, section(".limine_requests"))) +volatile struct limine_hhdm_request g_hhdm_request = { + .id = { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852ULL, 0x63984e959a98244bULL }, + .revision = 0, + .response = 0 +}; + +__attribute__((used, section(".limine_requests"))) +volatile struct limine_kernel_address_request g_kernel_address_request = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST_ID, + .revision = 0, + .response = 0 +}; + +__attribute__((used, section(".limine_requests"))) +volatile struct limine_memmap_request g_memmap_request = { + .id = LIMINE_MEMMAP_REQUEST_ID, + .revision = 0, + .response = 0 +}; + +typedef struct elf64_ehdr { + u8 ident[16]; + u16 type; + u16 machine; + u32 version; + u64 entry; + u64 phoff; + u64 shoff; + u32 flags; + u16 ehsize; + u16 phentsize; + u16 phnum; + u16 shentsize; + u16 shnum; + u16 shstrndx; +} elf64_ehdr; + +typedef struct elf64_shdr { + u32 name; + u32 type; + u64 flags; + u64 addr; + u64 offset; + u64 size; + u32 link; + u32 info; + u64 addralign; + u64 entsize; +} elf64_shdr; + +typedef struct elf64_sym { + u32 name; + u8 info; + u8 other; + u16 shndx; + u64 value; + u64 size; +} elf64_sym; + +typedef struct elf64_rela { + u64 offset; + u64 info; + i64 addend; +} elf64_rela; + +static int streq(const char *a, const char *b) { + usize i = 0; + while (a[i] && b[i]) { + if (a[i] != b[i]) { + return 0; + } + i++; + } + return a[i] == b[i]; +} + +int dkm_check_elf64(const void *address, u64 size) { + if (!address || size < sizeof(elf64_ehdr)) { + return -1; + } + + const elf64_ehdr *ehdr = (const elf64_ehdr *)address; + if (ehdr->ident[0] != ELF_MAGIC0 || ehdr->ident[1] != ELF_MAGIC1 || ehdr->ident[2] != ELF_MAGIC2 || ehdr->ident[3] != ELF_MAGIC3) { + return -2; + } + if (ehdr->ident[4] != ELFCLASS64 || ehdr->ident[5] != ELFDATA2LSB) { + return -3; + } + if (ehdr->machine != EM_X86_64) { + return -4; + } + if (ehdr->type != ET_REL && ehdr->type != ET_DYN && ehdr->type != ET_EXEC) { + return -5; + } + if (ehdr->ehsize < sizeof(elf64_ehdr)) { + return -6; + } + return 0; +} + +static int range_ok(u64 offset, u64 length, u64 size) { + if (offset > size) { + return 0; + } + if (length > size - offset) { + return 0; + } + return 1; +} + +int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out) { + const elf64_ehdr *ehdr = (const elf64_ehdr *)address; + out->driver_desc = 0; + out->driver_init = 0; + out->driver_exit = 0; + + if (ehdr->shoff == 0 || ehdr->shnum == 0 || ehdr->shentsize < sizeof(elf64_shdr)) { + log_error("[DKM] ELF has no section table"); + return -1; + } + if (!range_ok(ehdr->shoff, (u64)ehdr->shnum * ehdr->shentsize, size)) { + log_error("[DKM] ELF section table out of range"); + return -1; + } + + const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff); + const elf64_shdr *symtab = 0; + const elf64_shdr *strtab = 0; + + for (u16 i = 0; i < ehdr->shnum; i++) { + const elf64_shdr *section = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize); + if (section->type == SHT_SYMTAB) { + symtab = section; + if (section->link < ehdr->shnum) { + strtab = (const elf64_shdr *)((const u8 *)sections + (u64)section->link * ehdr->shentsize); + } + break; + } + } + + if (!symtab || !strtab || strtab->type != SHT_STRTAB) { + log_error("[DKM] missing symtab/strtab"); + return -1; + } + if (symtab->entsize < sizeof(elf64_sym) || !range_ok(symtab->offset, symtab->size, size) || !range_ok(strtab->offset, strtab->size, size)) { + log_error("[DKM] invalid symtab/strtab range"); + return -1; + } + + log_info("[DKM] symtab found"); + log_hex64("[DKM] symbol count=", symtab->size / symtab->entsize); + + for (u64 off = 0; off + sizeof(elf64_sym) <= symtab->size; off += symtab->entsize) { + const elf64_sym *sym = (const elf64_sym *)((const u8 *)address + symtab->offset + off); + if (sym->name >= strtab->size) { + continue; + } + const char *name = (const char *)address + strtab->offset + sym->name; + if (streq(name, "driver_desc")) { + out->driver_desc = sym; + log_info("[DKM] found driver_desc"); + log_hex64("[DKM] driver_desc section=", sym->shndx); + log_hex64("[DKM] driver_desc value=", sym->value); + } else if (streq(name, "driver_init")) { + out->driver_init = sym; + log_info("[DKM] found driver_init"); + log_hex64("[DKM] driver_init section=", sym->shndx); + log_hex64("[DKM] driver_init value=", sym->value); + } else if (streq(name, "driver_exit")) { + out->driver_exit = sym; + log_info("[DKM] found driver_exit"); + log_hex64("[DKM] driver_exit section=", sym->shndx); + log_hex64("[DKM] driver_exit value=", sym->value); + } + } + + if (out->driver_desc && out->driver_init && out->driver_exit) { + log_info("[DKM] required driver symbols ok"); + return 0; + } + log_error("[DKM] required driver symbols missing"); + return -1; +} + +int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan *symbols) { + const elf64_ehdr *ehdr = (const elf64_ehdr *)address; + const elf64_shdr *sections = (const elf64_shdr *)((const u8 *)address + ehdr->shoff); + u64 image_offset[64]; + u64 total_size = 0; + u64 max_align = 16; + u16 alloc_count = 0; + + for (u16 i = 1; i < ehdr->shnum && alloc_count < 64; i++) { + const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize); + if (!(sh->flags & SHF_ALLOC)) { + continue; + } + if (sh->addralign > max_align) { + max_align = sh->addralign; + } + total_size = utsm_align_up_u64(total_size, sh->addralign); + image_offset[i] = total_size; + total_size += sh->size; + alloc_count++; + } + + if (total_size == 0) { + log_error("[DKM] no allocatable sections"); + return -1; + } + + u8 *image = (u8 *)kmem_alloc_aligned(total_size, max_align); + if (!image) { + log_error("[DKM] failed to allocate module memory"); + return -1; + } + memset(image, 0, total_size); + + for (u16 i = 1; i < ehdr->shnum; i++) { + const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize); + if (!(sh->flags & SHF_ALLOC)) { + continue; + } + if (sh->type == SHT_PROGBITS && sh->size > 0 && range_ok(sh->offset, sh->size, size)) { + memcpy(image + image_offset[i], (const u8 *)address + sh->offset, sh->size); + } + } + + for (u16 i = 1; i < ehdr->shnum; i++) { + const elf64_shdr *sh = (const elf64_shdr *)((const u8 *)sections + (u64)i * ehdr->shentsize); + if (sh->type != SHT_RELA) { + continue; + } + if (sh->entsize < sizeof(elf64_rela) || !range_ok(sh->offset, sh->size, size)) { + continue; + } + if (sh->info >= ehdr->shnum) { + continue; + } + const elf64_shdr *target = (const elf64_shdr *)((const u8 *)sections + (u64)sh->info * ehdr->shentsize); + if (!(target->flags & SHF_ALLOC)) { + continue; + } + u8 *target_base = image + image_offset[sh->info]; + + const elf64_shdr *rela_symtab = 0; + if (sh->link < ehdr->shnum) { + rela_symtab = (const elf64_shdr *)((const u8 *)sections + (u64)sh->link * ehdr->shentsize); + } + const elf64_shdr *rela_strtab = 0; + if (rela_symtab && rela_symtab->link < ehdr->shnum) { + rela_strtab = (const elf64_shdr *)((const u8 *)sections + (u64)rela_symtab->link * ehdr->shentsize); + } + (void)rela_strtab; + + for (u64 off = 0; off + sizeof(elf64_rela) <= sh->size; off += sh->entsize) { + const elf64_rela *rela = (const elf64_rela *)((const u8 *)address + sh->offset + off); + u32 sym_idx = (u32)(rela->info >> 32); + u32 reloc_type = (u32)(rela->info & 0xffffffff); + + u64 sym_value = 0; + u16 sym_shndx = 0; + if (rela_symtab && rela_strtab && + rela_symtab->entsize >= sizeof(elf64_sym) && + range_ok(rela_symtab->offset, rela_symtab->size, size)) { + u64 sym_entry_off = (u64)sym_idx * rela_symtab->entsize; + if (sym_entry_off + sizeof(elf64_sym) <= rela_symtab->size) { + const elf64_sym *sym = (const elf64_sym *)((const u8 *)address + rela_symtab->offset + sym_entry_off); + sym_value = sym->value; + sym_shndx = sym->shndx; + } + } + + u64 S = sym_value; + if (sym_shndx > 0 && sym_shndx < 64) { + S = (u64)image + image_offset[sym_shndx] + sym_value; + } + u64 P = (u64)image + image_offset[sh->info] + rela->offset; + u64 A = rela->addend; + + u64 patch = 0; + if (reloc_type == R_X86_64_64) { + patch = S + A; + } else if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S) { + patch = (u64)((i64)(i32)(S + A)); + } else if (reloc_type == R_X86_64_PC32) { + patch = (u64)((i64)(i32)(S + A - P)); + } else { + log_error("[DKM] unsupported relocation type"); + log_hex64("[DKM] reloc type=", (u64)reloc_type); + return -1; + } + + if (rela->offset + 8 <= target->size) { + if (reloc_type == R_X86_64_32 || reloc_type == R_X86_64_32S || reloc_type == R_X86_64_PC32) { + *(u32 *)(target_base + rela->offset) = (u32)patch; + } else { + *(u64 *)(target_base + rela->offset) = patch; + } + } + } + } + + const elf64_sym *desc_sym = (const elf64_sym *)symbols->driver_desc; + const elf64_sym *init_sym = (const elf64_sym *)symbols->driver_init; + const dkm_driver_desc *desc = (const dkm_driver_desc *)(image + image_offset[desc_sym->shndx] + desc_sym->value); + if (desc->magic != DKM_DRIVER_MAGIC) { + log_error("[DKM] external driver_desc magic mismatch"); + return -1; + } + + log_info("[DKM] external driver loaded"); + log_hex64("[DKM] desc ptr=", (u64)desc); + log_hex64("[DKM] name ptr=", (u64)desc->name); + log_info(desc->name); + + dkm_driver_init_fn init = (dkm_driver_init_fn)(image + image_offset[init_sym->shndx] + init_sym->value); + + dkm_driver_handle handle; + handle.desc = desc; + handle.state = DKM_STATE_INITING; + handle.init_status = 0; + handle.load_stage = desc->stage; + handle.flags = desc->flags; + + int result = init(dkm_get_kernel_api(), &handle); + handle.init_status = result; + if (result != 0) { + handle.state = DKM_STATE_FAILED; + log_error("[DKM] external driver init failed"); + return result; + } + handle.state = DKM_STATE_ACTIVE; + log_info("[DKM] external driver active"); + return 0; +} + +void dkm_scan_boot_modules(void) { + struct limine_module_response *response = g_module_request.response; + if (!response) { + log_warn("[DKM] no Limine module response"); + return; + } + + log_info("[DKM] boot modules scan begin"); + log_hex64("[DKM] boot module count=", response->module_count); + + for (u64 i = 0; i < response->module_count; i++) { + struct limine_file *file = response->modules[i]; + if (!file) { + log_warn("[DKM] null module entry"); + continue; + } + + log_info("[DKM] module path"); + if (file->path) { + log_info(file->path); + } + if (file->cmdline) { + log_info(file->cmdline); + } + log_hex64("[DKM] module size=", file->size); + + int status = dkm_check_elf64(file->address, file->size); + if (status == 0) { + log_info("[DKM] module ELF64 x86_64 ok"); + struct dkm_symbol_scan symbols; + if (dkm_scan_symbols(file->address, file->size, &symbols) == 0) { + log_info("[DKM] loading external ELF driver"); + int load_result = dkm_load_elf_rel(file->address, file->size, &symbols); + if (load_result != 0) { + log_error("[DKM] external driver load failed"); + } + } + } else { + log_error("[DKM] module ELF64 rejected"); + log_hex64("[DKM] elf status=", (u64)(i64)status); + } + } + + log_info("[DKM] boot modules scan end"); +} diff --git a/CODE/UTSM/dkm/boot_modules.o b/CODE/UTSM/dkm/boot_modules.o new file mode 100644 index 0000000..fa70a40 Binary files /dev/null and b/CODE/UTSM/dkm/boot_modules.o differ diff --git a/CODE/UTSM/dkm/console_early.c b/CODE/UTSM/dkm/console_early.c new file mode 100644 index 0000000..fce97c8 --- /dev/null +++ b/CODE/UTSM/dkm/console_early.c @@ -0,0 +1,47 @@ +#include + +static const char *const console_early_provides[] = { + "console", + "early-log" +}; + +static const dkm_driver_desc console_early_desc = { + .magic = DKM_DRIVER_MAGIC, + .abi_version = DKM_ABI_VERSION, + .desc_size = sizeof(dkm_driver_desc), + .name = "console_early", + .version = "0.1.0", + .vendor = "Deshab", + .driver_class = DKM_CLASS_CONSOLE, + .stage = 0, + .flags = DKM_F_REQUIRED | DKM_F_BOOT_MODULE | DKM_F_NO_UNLOAD | DKM_F_EARLY_LOG, + .priority = 0, + .depends = 0, + .depends_count = 0, + .provides = console_early_provides, + .provides_count = 2, + .min_kernel_abi = DKM_KERNEL_API_VERSION, + .feature_bits = 0, + .reserved0 = 0, + .reserved1 = 0 +}; + +static int console_early_init(const dkm_kernel_api *api, dkm_driver_handle *handle) { + (void)handle; + if (!api || !api->log || !api->log->info) { + return -1; + } + api->log->info("[DKM:console_early] serial console online"); + return 0; +} + +static int console_early_exit(dkm_driver_handle *handle) { + (void)handle; + return 0; +} + +const dkm_builtin_driver dkm_builtin_console_early = { + .desc = &console_early_desc, + .init = console_early_init, + .exit = console_early_exit +}; diff --git a/CODE/UTSM/dkm/console_early.o b/CODE/UTSM/dkm/console_early.o new file mode 100644 index 0000000..932fe41 Binary files /dev/null and b/CODE/UTSM/dkm/console_early.o differ diff --git a/CODE/UTSM/dkm/dkm_core.c b/CODE/UTSM/dkm/dkm_core.c new file mode 100644 index 0000000..c70da5b --- /dev/null +++ b/CODE/UTSM/dkm/dkm_core.c @@ -0,0 +1,67 @@ +#include +#include + +static int dkm_validate_desc(const dkm_driver_desc *desc) { + if (!desc) { + return -1; + } + if (desc->magic != DKM_DRIVER_MAGIC) { + return -2; + } + if (desc->abi_version != DKM_ABI_VERSION) { + return -3; + } + if (desc->desc_size < sizeof(dkm_driver_desc)) { + return -4; + } + if (!desc->name || !desc->version) { + return -5; + } + return 0; +} + +void dkm_init(void) { + log_info("[DKM] init begin"); + int status = dkm_load_builtin(&dkm_builtin_console_early); + if (status == 0) { + log_info("[DKM] init ok"); + } else { + log_error("[DKM] init failed"); + } +} + +int dkm_load_builtin(const dkm_builtin_driver *driver) { + if (!driver || !driver->desc || !driver->init) { + log_error("[DKM] invalid builtin driver"); + return -1; + } + + int status = dkm_validate_desc(driver->desc); + if (status != 0) { + log_error("[DKM] driver_desc rejected"); + return status; + } + + dkm_driver_handle handle; + handle.desc = driver->desc; + handle.state = DKM_STATE_ABI_CHECKED; + handle.init_status = 0; + handle.load_stage = driver->desc->stage; + handle.flags = driver->desc->flags; + + log_info("[DKM] builtin driver init begin"); + log_info(driver->desc->name); + handle.state = DKM_STATE_INITING; + status = driver->init(dkm_get_kernel_api(), &handle); + handle.init_status = status; + + if (status != 0) { + handle.state = DKM_STATE_FAILED; + log_error("[DKM] builtin driver init failed"); + return status; + } + + handle.state = DKM_STATE_ACTIVE; + log_info("[DKM] builtin driver active"); + return 0; +} diff --git a/CODE/UTSM/dkm/dkm_core.o b/CODE/UTSM/dkm/dkm_core.o new file mode 100644 index 0000000..1752f48 Binary files /dev/null and b/CODE/UTSM/dkm/dkm_core.o differ diff --git a/CODE/UTSM/dkm/kernel_api.c b/CODE/UTSM/dkm/kernel_api.c new file mode 100644 index 0000000..be571ee --- /dev/null +++ b/CODE/UTSM/dkm/kernel_api.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +static void dkm_api_info(const char *msg) { + log_info(msg); +} + +static void dkm_api_warn(const char *msg) { + log_warn(msg); +} + +static void dkm_api_error(const char *msg) { + log_error(msg); +} + +static void dkm_api_panic(const char *msg) { + log_error(msg); + for (;;) { + __asm__ volatile("cli; hlt"); + } +} + +static const dkm_log_api g_log_api = { + .info = dkm_api_info, + .warn = dkm_api_warn, + .error = dkm_api_error, + .panic = dkm_api_panic +}; + +static dkm_kernel_api g_kernel_api = { + .version = DKM_KERNEL_API_VERSION, + .size = sizeof(dkm_kernel_api), + .feature_bits = 0, + .log = &g_log_api, + .mem = 0, + .utsm = 0, + .irq = 0, + .pci = 0, + .dma = 0, + .vfs = 0, + .net = 0, + .timer = 0, + .drr = 0, + .rsdp_address = 0, + .fb_address = 0, + .fb_width = 0, + .fb_height = 0, + .fb_pitch = 0, + .fb_bpp = 0, + .boot_modules_response = 0, + .irq_register = 0, + .hhdm_offset = 0, + .block = 0 +}; + +const dkm_kernel_api *dkm_get_kernel_api(void) { + return &g_kernel_api; +} + +#include "../arch/x86_64/limine.h" +extern volatile struct limine_rsdp_request g_rsdp_request; +extern volatile struct limine_framebuffer_request g_fb_request; +extern volatile struct limine_module_request g_module_request; +extern volatile struct limine_hhdm_request g_hhdm_request; + +void dkm_fill_platform_info(void) { + g_kernel_api.dma = dma_get_api(); + g_kernel_api.net = net_get_api(); + g_kernel_api.block = block_get_api(); + if (g_rsdp_request.response && g_rsdp_request.response->address) { + g_kernel_api.rsdp_address = g_rsdp_request.response->address; + } + if (g_fb_request.response && g_fb_request.response->framebuffer_count > 0) { + struct limine_framebuffer *fb = g_fb_request.response->framebuffers[0]; + g_kernel_api.fb_address = fb->address; + g_kernel_api.fb_width = fb->width; + g_kernel_api.fb_height = fb->height; + g_kernel_api.fb_pitch = fb->pitch; + g_kernel_api.fb_bpp = fb->bpp; + } + if (g_module_request.response) { + g_kernel_api.boot_modules_response = g_module_request.response; + } + g_kernel_api.irq_register = (int (*)(u8,void*))((u64)irq_register); + if (g_hhdm_request.response) { + g_kernel_api.hhdm_offset = g_hhdm_request.response->offset; + } +} diff --git a/CODE/UTSM/dkm/kernel_api.o b/CODE/UTSM/dkm/kernel_api.o new file mode 100644 index 0000000..771ca2f Binary files /dev/null and b/CODE/UTSM/dkm/kernel_api.o differ diff --git a/CODE/UTSM/dkm/manifest.c b/CODE/UTSM/dkm/manifest.c new file mode 100644 index 0000000..6d8adda --- /dev/null +++ b/CODE/UTSM/dkm/manifest.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include "../arch/x86_64/limine.h" + +#define DSM_MAX_DRIVERS_PER_STAGE 8 +#define DSM_MAX_STAGES 4 +#define DSM_MAX_DEPENDS 4 +#define DSM_MAX_PROVIDES 4 + +typedef struct dsm_driver_entry { + char name[32]; + char path[64]; + u32 class_id; + u32 required; + char depends[DSM_MAX_DEPENDS][32]; + u32 depend_count; + char provides[DSM_MAX_PROVIDES][32]; + u32 provide_count; + struct limine_file *boot_module; + int loaded; +} dsm_driver_entry; + +typedef struct dsm_stage_entry { + u32 id; + char name[16]; + u32 required; + dsm_driver_entry drivers[DSM_MAX_DRIVERS_PER_STAGE]; + u32 driver_count; +} dsm_stage_entry; + +static dsm_stage_entry g_stages[DSM_MAX_STAGES]; +static u32 g_stage_count; + +extern volatile struct limine_module_request g_module_request; + +static int dsm_skip_ws(const char **p) { + while (**p == ' ' || **p == '\t' || **p == '\n' || **p == '\r') { (*p)++; } + return 0; +} + +static int dsm_expect(const char **p, char c) { + dsm_skip_ws(p); + if (**p == c) { (*p)++; return 0; } + return -1; +} + +static int dsm_read_string(const char **p, char *out, u32 max_len) { + dsm_skip_ws(p); + if (**p != '"') return -1; + (*p)++; + u32 i = 0; + while (**p && **p != '"' && i + 1 < max_len) { + out[i++] = **p; + (*p)++; + } + out[i] = 0; + if (**p != '"') return -1; + (*p)++; + return 0; +} + +static int dsm_skip_value(const char **p) { + dsm_skip_ws(p); + if (**p == '"') { + char tmp[256]; + return dsm_read_string(p, tmp, sizeof(tmp)); + } + if (**p == '{') { + (*p)++; + int depth = 1; + while (depth > 0 && **p) { + if (**p == '{') depth++; + else if (**p == '}') depth--; + else if (**p == '"') { (*p)++; while (**p && **p != '"') (*p)++; if (**p) (*p)++; continue; } + if (depth > 0) (*p)++; + } + return 0; + } + if (**p == '[') { + (*p)++; + int depth = 1; + while (depth > 0 && **p) { + if (**p == '[') depth++; + else if (**p == ']') depth--; + else if (**p == '"') { (*p)++; while (**p && **p != '"') (*p)++; if (**p) (*p)++; continue; } + if (depth > 0) (*p)++; + } + return 0; + } + while (**p && **p != ',' && **p != '}' && **p != ']') (*p)++; + return 0; +} + +static struct limine_file *dsm_find_boot_module(const char *path) { + struct limine_module_response *rsp = g_module_request.response; + if (!rsp) return 0; + for (u64 i = 0; i < rsp->module_count; i++) { + struct limine_file *f = rsp->modules[i]; + if (f && f->path) { + const char *fp = f->path; + while (*fp == '/') fp++; + usize j = 0; + usize k = 0; + while (fp[j]) j++; + while (path[k]) k++; + if (j == k) { + int match = 1; + for (usize x = 0; x < j; x++) { + if (fp[x] != path[x]) { match = 0; break; } + } + if (match) return f; + } + } + } + return 0; +} + +static int streq_static(const char *a, const char *b) { + usize i = 0; + while (a[i] && b[i]) { + if (a[i] != b[i]) return 0; + i++; + } + return a[i] == b[i]; +} + +static void dsm_parse_driver(const char **p, dsm_driver_entry *drv) { + drv->depend_count = 0; + drv->provide_count = 0; + drv->required = 1; + drv->boot_module = 0; + drv->loaded = 0; + drv->class_id = 0; + + if (dsm_expect(p, '{') != 0) return; + while (**p && **p != '}') { + dsm_skip_ws(p); + if (**p == '}') break; + char key[32]; + if (dsm_read_string(p, key, sizeof(key)) != 0) return; + dsm_expect(p, ':'); + + if (streq_static(key, "name")) { + dsm_read_string(p, drv->name, sizeof(drv->name)); + } else if (streq_static(key, "path")) { + dsm_read_string(p, drv->path, sizeof(drv->path)); + } else if (streq_static(key, "class")) { + char cls[32]; + dsm_read_string(p, cls, sizeof(cls)); + if (streq_static(cls, "platform")) drv->class_id = 1; + else if (streq_static(cls, "bus")) drv->class_id = 2; + else if (streq_static(cls, "interrupt")) drv->class_id = 3; + else if (streq_static(cls, "timer")) drv->class_id = 4; + else if (streq_static(cls, "console")) drv->class_id = 5; + else if (streq_static(cls, "storage")) drv->class_id = 6; + else if (streq_static(cls, "fs")) drv->class_id = 7; + else if (streq_static(cls, "fs-core")) drv->class_id = 8; + else if (streq_static(cls, "net")) drv->class_id = 9; + else if (streq_static(cls, "input")) drv->class_id = 10; + } else if (streq_static(key, "required")) { + dsm_skip_ws(p); + if (**p == 't') { drv->required = 1; (*p) += 4; } + else if (**p == 'f') { drv->required = 0; (*p) += 5; } + } else if (streq_static(key, "depends")) { + if (dsm_expect(p, '[') != 0) return; + while (**p && **p != ']') { + dsm_skip_ws(p); + if (**p == ']') break; + if (drv->depend_count < DSM_MAX_DEPENDS) { + dsm_read_string(p, drv->depends[drv->depend_count], 32); + drv->depend_count++; + } else { dsm_skip_value(p); } + dsm_skip_ws(p); + if (**p == ',') { (*p)++; continue; } + if (**p == ']') break; + } + dsm_expect(p, ']'); + } else if (streq_static(key, "provides")) { + if (dsm_expect(p, '[') != 0) return; + while (**p && **p != ']') { + dsm_skip_ws(p); + if (**p == ']') break; + if (drv->provide_count < DSM_MAX_PROVIDES) { + dsm_read_string(p, drv->provides[drv->provide_count], 32); + drv->provide_count++; + } else { dsm_skip_value(p); } + dsm_skip_ws(p); + if (**p == ',') { (*p)++; continue; } + if (**p == ']') break; + } + dsm_expect(p, ']'); + } else { + dsm_skip_value(p); + } + dsm_skip_ws(p); + if (**p == ',') (*p)++; + } + dsm_expect(p, '}'); +} + +static void dsm_parse_stage(const char **p, dsm_stage_entry *stage) { + stage->driver_count = 0; + if (dsm_expect(p, '{') != 0) return; + while (**p && **p != '}') { + dsm_skip_ws(p); + if (**p == '}') break; + char key[32]; + if (dsm_read_string(p, key, sizeof(key)) != 0) return; + dsm_expect(p, ':'); + + if (streq_static(key, "id")) { + dsm_skip_ws(p); + u32 val = 0; + while (**p >= '0' && **p <= '9') { val = val * 10 + (u32)(**p - '0'); (*p)++; } + stage->id = val; + } else if (streq_static(key, "name")) { + dsm_read_string(p, stage->name, sizeof(stage->name)); + } else if (streq_static(key, "required")) { + dsm_skip_ws(p); + if (**p == 't') { stage->required = 1; (*p) += 4; } + else if (**p == 'f') { stage->required = 0; (*p) += 5; } + } else if (streq_static(key, "drivers")) { + if (dsm_expect(p, '[') != 0) return; + while (**p && **p != ']') { + dsm_skip_ws(p); + if (**p == ']') break; + if (stage->driver_count < DSM_MAX_DRIVERS_PER_STAGE) { + dsm_parse_driver(p, &stage->drivers[stage->driver_count]); + stage->driver_count++; + } else { + dsm_skip_value(p); + } + dsm_skip_ws(p); + if (**p == ',') { (*p)++; continue; } + if (**p == ']') break; + } + dsm_expect(p, ']'); + } else { + dsm_skip_value(p); + } + dsm_skip_ws(p); + if (**p == ',') (*p)++; + } + dsm_expect(p, '}'); +} + +static int dsm_parse_manifest(const char *text, u64 size) { + g_stage_count = 0; + const char *p = text; + const char *end = text + size; + + while (p < end && *p) { + dsm_skip_ws(&p); + if (p >= end || !*p) break; + if (*p == '{' || *p == '}') { p++; continue; } + if (*p == ',' || *p == ':') { p++; continue; } + if (*p == '[' || *p == ']') { p++; continue; } + + char key[32]; + if (dsm_read_string(&p, key, sizeof(key)) != 0) break; + dsm_expect(&p, ':'); + + if (streq_static(key, "stages")) { + if (dsm_expect(&p, '[') != 0) return -1; + while (p < end && *p) { + dsm_skip_ws(&p); + if (*p == ']') { p++; break; } + if (g_stage_count < DSM_MAX_STAGES) { + dsm_parse_stage(&p, &g_stages[g_stage_count]); + g_stage_count++; + } + dsm_skip_ws(&p); + if (*p == ',') p++; + } + } else { + dsm_skip_value(&p); + } + dsm_skip_ws(&p); + if (*p == ',' || *p == '}' || *p == ']') p++; + } + return 0; +} + +void dsm_load_by_manifest(void) { + struct limine_module_response *rsp = g_module_request.response; + if (!rsp) { + log_warn("[DSM] no Limine module response"); + return; + } + + struct limine_file *manifest_file = 0; + for (u64 i = 0; i < rsp->module_count; i++) { + struct limine_file *f = rsp->modules[i]; + if (f && f->path && f->cmdline && streq_static(f->cmdline, "dkm:manifest")) { + manifest_file = f; + break; + } + } + + if (!manifest_file) { + log_warn("[DSM] manifest not found in boot modules"); + return; + } + + log_info("[DSM] parsing manifest"); + dsm_parse_manifest((const char *)manifest_file->address, manifest_file->size); + log_info("[DSM] manifest parsed"); + log_hex64("[DSM] stages=", (u64)g_stage_count); + + for (u32 si = 0; si < g_stage_count; si++) { + dsm_stage_entry *stage = &g_stages[si]; + log_info("[DSM] stage begin"); + log_info(stage->name); + log_hex64("[DSM] driver count=", stage->driver_count); + + for (u32 di = 0; di < stage->driver_count; di++) { + dsm_driver_entry *drv = &stage->drivers[di]; + + drv->boot_module = dsm_find_boot_module(drv->path); + if (!drv->boot_module) { + log_info("[DSM] module not found"); + log_info(drv->name); + if (drv->required && stage->required) { + log_error("[DSM] required module missing, abort stage"); + break; + } + continue; + } + + struct limine_file *f = drv->boot_module; + log_info("[DSM] loading driver"); + log_info(drv->name); + + if (dkm_check_elf64(f->address, f->size) != 0) { + log_error("[DSM] ELF check failed"); + continue; + } + + struct dkm_symbol_scan symbols; + if (dkm_scan_symbols(f->address, f->size, &symbols) != 0) { + log_error("[DSM] symbol scan failed"); + continue; + } + + int result = dkm_load_elf_rel(f->address, f->size, &symbols); + if (result != 0) { + log_error("[DSM] load failed"); + if (drv->required && stage->required) { + log_error("[DSM] required driver failed, abort stage"); + break; + } + } else { + drv->loaded = 1; + } + } + log_info("[DSM] stage end"); + } +} diff --git a/CODE/UTSM/dkm/manifest.o b/CODE/UTSM/dkm/manifest.o new file mode 100644 index 0000000..3aa0324 Binary files /dev/null and b/CODE/UTSM/dkm/manifest.o differ diff --git a/CODE/UTSM/drr/drr_stub.c b/CODE/UTSM/drr/drr_stub.c new file mode 100644 index 0000000..f1860d1 --- /dev/null +++ b/CODE/UTSM/drr/drr_stub.c @@ -0,0 +1,37 @@ +#include +#include + +static const u64 g_root_key[4] = { + 0x4452525554534d31ULL, + 0xfeedfacecafebeefULL, + 0x0123456789abcdefULL, + 0xfedcba9876543210ULL +}; + +void drr_stub_init(void) { +} + +const u64 *drr_get_root_key(void) { + return g_root_key; +} + +u64 drr_recovery_generation(void) { + return 1; +} + +void drr_report_fault(const char *reason) { + log_error("[DRR] fault reported"); + log_error(reason); +} + +void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len) { + UTSM_UNUSED(segment_slot); + UTSM_UNUSED(offset); + UTSM_UNUSED(len); +} + +void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len) { + UTSM_UNUSED(segment_slot); + UTSM_UNUSED(offset); + UTSM_UNUSED(len); +} diff --git a/CODE/UTSM/drr/drr_stub.o b/CODE/UTSM/drr/drr_stub.o new file mode 100644 index 0000000..515d549 Binary files /dev/null and b/CODE/UTSM/drr/drr_stub.o differ diff --git a/CODE/UTSM/include/utsm/arena.h b/CODE/UTSM/include/utsm/arena.h new file mode 100644 index 0000000..61994b3 --- /dev/null +++ b/CODE/UTSM/include/utsm/arena.h @@ -0,0 +1,13 @@ +#ifndef UTSM_ARENA_H +#define UTSM_ARENA_H + +#include + +void arena_init(void); +void *kmem_alloc(u64 size); +void *kmem_alloc_aligned(u64 size, u64 alignment); +void *kmem_alloc_page(u64 page_count); +u64 kmem_arena_virtual_base(void); +u64 kmem_arena_virtual_end(void); + +#endif diff --git a/CODE/UTSM/include/utsm/assert.h b/CODE/UTSM/include/utsm/assert.h new file mode 100644 index 0000000..033f6a7 --- /dev/null +++ b/CODE/UTSM/include/utsm/assert.h @@ -0,0 +1,8 @@ +#ifndef UTSM_ASSERT_H +#define UTSM_ASSERT_H + +#include + +#define UTSM_ASSERT(cond) do { if (!(cond)) { panic("assertion failed: " #cond); } } while (0) + +#endif diff --git a/CODE/UTSM/include/utsm/block.h b/CODE/UTSM/include/utsm/block.h new file mode 100644 index 0000000..938552a --- /dev/null +++ b/CODE/UTSM/include/utsm/block.h @@ -0,0 +1,29 @@ +#ifndef UTSM_BLOCK_H +#define UTSM_BLOCK_H + +#include + +#define DKM_BLOCK_MAX_DEVICES 8u + +typedef int (*dkm_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer); + +typedef struct dkm_block_device_desc { + const char *name; + u64 sector_size; + u64 sector_count; + void *ctx; + dkm_block_read_fn read; +} dkm_block_device_desc; + +typedef struct dkm_block_api { + int (*register_device)(const dkm_block_device_desc *desc); + u32 (*device_count)(void); + int (*read)(u32 index, u64 lba, u32 count, void *buffer); + u64 (*sector_size)(u32 index); + const char *(*device_name)(u32 index); +} dkm_block_api; + +void block_init(void); +const dkm_block_api *block_get_api(void); + +#endif diff --git a/CODE/UTSM/include/utsm/capability.h b/CODE/UTSM/include/utsm/capability.h new file mode 100644 index 0000000..cb6947f --- /dev/null +++ b/CODE/UTSM/include/utsm/capability.h @@ -0,0 +1,26 @@ +#ifndef UTSM_CAPABILITY_H +#define UTSM_CAPABILITY_H + +#include +#include + +#define UTSM_RIGHT_READ (1U << 0) +#define UTSM_RIGHT_WRITE (1U << 1) +#define UTSM_RIGHT_EXEC (1U << 2) +#define UTSM_RIGHT_SHARE (1U << 3) +#define UTSM_RIGHT_DMA (1U << 4) + +struct utsm_segment_desc; + +typedef struct { + u32 segment_slot; + u32 generation; + u32 rights; + u32 epoch; + u64 uuid_digest; + u64 auth_tag; +} utsm_capability; + +int utsm_cap_validate(utsm_capability cap, u32 required_rights, u64 offset, u64 len, struct utsm_segment_desc **out_desc); + +#endif diff --git a/CODE/UTSM/include/utsm/config.h b/CODE/UTSM/include/utsm/config.h new file mode 100644 index 0000000..04ef524 --- /dev/null +++ b/CODE/UTSM/include/utsm/config.h @@ -0,0 +1,13 @@ +#ifndef UTSM_CONFIG_H +#define UTSM_CONFIG_H + +#define UTSM_CACHE_LINE_SIZE 64ULL +#define UTSM_PAGE_SIZE 4096ULL +#define UTSM_MAX_SEGMENTS 1024U +#define UTSM_MAX_CAPABILITIES 256U +#define UTSM_MAX_PCKC_KEYS 8U +#define UTSM_ARENA_SIZE (16ULL * 1024ULL * 1024ULL) +#define UTSM_DIRTY_SHARD_PAGES 1024ULL +#define UTSM_KERNEL_STACK_SIZE 16384ULL + +#endif diff --git a/CODE/UTSM/include/utsm/crypto.h b/CODE/UTSM/include/utsm/crypto.h new file mode 100644 index 0000000..533003c --- /dev/null +++ b/CODE/UTSM/include/utsm/crypto.h @@ -0,0 +1,17 @@ +#ifndef UTSM_CRYPTO_H +#define UTSM_CRYPTO_H + +#include +#include + +struct utsm_segment_desc; + +typedef struct { + u64 words[4]; +} utsm_key_material; + +void utsm_kdf_segment_key(uuid128_t process_uuid, uuid128_t segment_uuid, u64 key_epoch, utsm_key_material *out_key); +u64 utsm_make_tweak(u64 tweak_seed, u64 line_index, u64 key_epoch); +void utsm_crypt_line(const utsm_key_material *key, u64 tweak, const u8 *in, u8 *out); + +#endif diff --git a/CODE/UTSM/include/utsm/dirty.h b/CODE/UTSM/include/utsm/dirty.h new file mode 100644 index 0000000..af9a762 --- /dev/null +++ b/CODE/UTSM/include/utsm/dirty.h @@ -0,0 +1,20 @@ +#ifndef UTSM_DIRTY_H +#define UTSM_DIRTY_H + +#include + +typedef struct { + u32 dirty_count; + u32 shard_id; + u64 page_base; + u64 page_count; + u64 last_checkpoint_epoch; + u64 *bitmap; +} utsm_dirty_shard; + +struct utsm_segment_desc; + +void utsm_dirty_init(struct utsm_segment_desc *desc, u64 page_count); +void utsm_mark_dirty(struct utsm_segment_desc *desc, u64 offset, u64 len); + +#endif diff --git a/CODE/UTSM/include/utsm/dkm.h b/CODE/UTSM/include/utsm/dkm.h new file mode 100644 index 0000000..15cb750 --- /dev/null +++ b/CODE/UTSM/include/utsm/dkm.h @@ -0,0 +1,141 @@ +#ifndef UTSM_DKM_H +#define UTSM_DKM_H + +#include +#include +#include +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u +#define DKM_KERNEL_API_VERSION 1u + +#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 + +#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) + +struct dkm_kernel_api; +struct dkm_driver_handle; + +typedef 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; +} dkm_driver_desc; + +typedef int (*dkm_driver_init_fn)(const struct dkm_kernel_api *api, struct dkm_driver_handle *handle); +typedef int (*dkm_driver_exit_fn)(struct dkm_driver_handle *handle); + +typedef struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +} dkm_log_api; + +typedef struct dkm_kernel_api { + u32 version; + u32 size; + u64 feature_bits; + + const dkm_log_api *log; + const void *mem; + const void *utsm; + const void *irq; + const void *pci; + const dkm_dma_api *dma; + const void *vfs; + const dkm_net_api *net; + const void *timer; + const void *drr; + + const void *rsdp_address; /* ACPI RSDP physical address */ + const void *fb_address; /* framebuffer base */ + u64 fb_width; + u64 fb_height; + u64 fb_pitch; + u16 fb_bpp; + const void *boot_modules_response; /* limine_module_response* */ + int (*irq_register)(u8 irq, void *handler); /* kernel irq_register */ + u64 hhdm_offset; /* physical → virtual */ + const dkm_block_api *block; /* block provider registry */ +} dkm_kernel_api; + +typedef enum dkm_driver_state { + DKM_STATE_DISCOVERED = 0, + DKM_STATE_ABI_CHECKED, + DKM_STATE_INITING, + DKM_STATE_ACTIVE, + DKM_STATE_FAILED +} dkm_driver_state; + +typedef struct dkm_driver_handle { + const dkm_driver_desc *desc; + dkm_driver_state state; + i32 init_status; + u32 load_stage; + u32 flags; +} dkm_driver_handle; + +typedef struct dkm_builtin_driver { + const dkm_driver_desc *desc; + dkm_driver_init_fn init; + dkm_driver_exit_fn exit; +} dkm_builtin_driver; + +void dkm_init(void); +int dkm_load_builtin(const dkm_builtin_driver *driver); +void dkm_scan_boot_modules(void); +void dsm_load_by_manifest(void); +void dkm_fill_platform_info(void); +const dkm_kernel_api *dkm_get_kernel_api(void); + +/* exposed for manifest parser */ +struct dkm_symbol_scan { + const void *driver_desc; + const void *driver_init; + const void *driver_exit; +}; +int dkm_check_elf64(const void *address, u64 size); +int dkm_scan_symbols(const void *address, u64 size, struct dkm_symbol_scan *out); +int dkm_load_elf_rel(const void *address, u64 size, const struct dkm_symbol_scan *symbols); + +extern const dkm_builtin_driver dkm_builtin_console_early; + +#endif diff --git a/CODE/UTSM/include/utsm/dma.h b/CODE/UTSM/include/utsm/dma.h new file mode 100644 index 0000000..9757358 --- /dev/null +++ b/CODE/UTSM/include/utsm/dma.h @@ -0,0 +1,20 @@ +#ifndef UTSM_DMA_H +#define UTSM_DMA_H + +#include + +typedef struct dkm_dma_buffer { + void *virt; + u64 phys; + u64 size; +} dkm_dma_buffer; + +typedef struct dkm_dma_api { + int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out); +} dkm_dma_api; + +void dma_init(void); +int dma_alloc_pages(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out); +const dkm_dma_api *dma_get_api(void); + +#endif diff --git a/CODE/UTSM/include/utsm/dmp.h b/CODE/UTSM/include/utsm/dmp.h new file mode 100644 index 0000000..f0827ec --- /dev/null +++ b/CODE/UTSM/include/utsm/dmp.h @@ -0,0 +1,26 @@ +#ifndef UTSM_DMP_H +#define UTSM_DMP_H + +#include + +typedef struct { + u64 magic; + u32 version; + u32 segment_slot; + u64 fast_base; + u64 fast_limit; + u64 fast_tweak_seed; + u64 fast_key_epoch; + u64 dirty_bitmap_base; + u64 mac_table_base; + u64 last_writer_seq; + u32 last_cpu; + u32 flags; + u64 crc; +} utsm_dmp; + +struct utsm_segment_desc; + +void utsm_dmp_init(utsm_dmp *dmp, struct utsm_segment_desc *desc, u32 slot); + +#endif diff --git a/CODE/UTSM/include/utsm/drr.h b/CODE/UTSM/include/utsm/drr.h new file mode 100644 index 0000000..9bff1f3 --- /dev/null +++ b/CODE/UTSM/include/utsm/drr.h @@ -0,0 +1,13 @@ +#ifndef UTSM_DRR_H +#define UTSM_DRR_H + +#include + +void drr_stub_init(void); +const u64 *drr_get_root_key(void); +u64 drr_recovery_generation(void); +void drr_report_fault(const char *reason); +void drr_log_write_intent(u32 segment_slot, u64 offset, u64 len); +void drr_log_write_commit(u32 segment_slot, u64 offset, u64 len); + +#endif diff --git a/CODE/UTSM/include/utsm/dsk.h b/CODE/UTSM/include/utsm/dsk.h new file mode 100644 index 0000000..8dcc84f --- /dev/null +++ b/CODE/UTSM/include/utsm/dsk.h @@ -0,0 +1,51 @@ +#ifndef UTSM_DSK_H +#define UTSM_DSK_H + +#include + +#define DSK_BOOT_MAGIC 0x44534B31424F4F54ULL /* "DSK1BOOT" */ +#define DSK_BOOT_ABI_VERSION 1u + +#define DSK_BOOT_FLAG_FROM_UTSM (1ULL << 0) +#define DSK_BOOT_FLAG_DKM_READY (1ULL << 1) +#define DSK_BOOT_FLAG_FAT32_PATH (1ULL << 2) + +typedef struct dsk_boot_context { + u64 magic; + u32 abi_version; + u32 size; + + u64 flags; + + u64 hhdm_offset; + u64 rsdp_address; + + u64 framebuffer_address; + u64 framebuffer_width; + u64 framebuffer_height; + u64 framebuffer_pitch; + u32 framebuffer_bpp; + u32 reserved_fb; + + u64 boot_modules_response; + + u64 dkm_kernel_api; + u64 dkm_driver_table; + u64 dkm_driver_count; + + u64 utsm_state; + u64 drr_state; + + u64 memory_map; + u64 memory_map_count; + u64 memory_map_entry_size; + + u64 kernel_stack_top; + u64 reserved[8]; +} dsk_boot_context; + +typedef void (*dsk_entry_fn)(const dsk_boot_context *context); + +int dsk_load_and_jump(void); + +#endif diff --git a/CODE/UTSM/include/utsm/idt.h b/CODE/UTSM/include/utsm/idt.h new file mode 100644 index 0000000..d51642c --- /dev/null +++ b/CODE/UTSM/include/utsm/idt.h @@ -0,0 +1,12 @@ +#ifndef UTSM_IDT_H +#define UTSM_IDT_H + +#include + +void idt_init(void); + +/* IRQ handler: return 1 to suppress default ACK, 0 for normal EOI */ +typedef int (*irq_handler_t)(u8 irq); +int irq_register(u8 irq, irq_handler_t handler); + +#endif diff --git a/CODE/UTSM/include/utsm/log.h b/CODE/UTSM/include/utsm/log.h new file mode 100644 index 0000000..b0db11c --- /dev/null +++ b/CODE/UTSM/include/utsm/log.h @@ -0,0 +1,16 @@ +#ifndef UTSM_LOG_H +#define UTSM_LOG_H + +#include + +void serial_init(void); +void serial_putc(char c); +void serial_write(const char *s); + +void log_info(const char *msg); +void log_warn(const char *msg); +void log_error(const char *msg); +void log_hex64(const char *prefix, u64 value); +void panic(const char *msg); + +#endif diff --git a/CODE/UTSM/include/utsm/net.h b/CODE/UTSM/include/utsm/net.h new file mode 100644 index 0000000..1f5131f --- /dev/null +++ b/CODE/UTSM/include/utsm/net.h @@ -0,0 +1,42 @@ +#ifndef UTSM_NET_H +#define UTSM_NET_H + +#include + +#define DKM_NET_MAX_DEVICES 8u + +/* 网卡链路/能力标志。 */ +#define DKM_NET_F_LINK_UP (1u << 0) +#define DKM_NET_F_TX_READY (1u << 1) +#define DKM_NET_F_RX_READY (1u << 2) + +typedef int (*dkm_net_tx_fn)(void *ctx, const void *packet, u32 length); +typedef int (*dkm_net_rx_poll_fn)(void *ctx, void *buffer, u32 capacity, u32 *out_length); + +typedef struct dkm_net_device_desc { + const char *name; + u8 mac[6]; + u32 flags; + void *ctx; + dkm_net_tx_fn tx; + dkm_net_rx_poll_fn rx_poll; +} dkm_net_device_desc; + +typedef struct dkm_net_device_info { + const char *name; + u8 mac[6]; + u32 flags; +} dkm_net_device_info; + +typedef struct dkm_net_api { + int (*register_device)(const dkm_net_device_desc *desc); + u32 (*device_count)(void); + int (*device_info)(u32 index, dkm_net_device_info *out); + int (*tx)(u32 index, const void *packet, u32 length); + int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); +} dkm_net_api; + +void net_init(void); +const dkm_net_api *net_get_api(void); + +#endif diff --git a/CODE/UTSM/include/utsm/pckc.h b/CODE/UTSM/include/utsm/pckc.h new file mode 100644 index 0000000..b00d546 --- /dev/null +++ b/CODE/UTSM/include/utsm/pckc.h @@ -0,0 +1,29 @@ +#ifndef UTSM_PCKC_H +#define UTSM_PCKC_H + +#include +#include +#include +#include + +struct utsm_segment_desc; + +typedef struct { + bool valid; + u32 segment_slot; + u64 key_epoch; + utsm_key_material key; +} utsm_pckc_entry; + +typedef struct { + uuid128_t current_process_uuid; + utsm_pckc_entry entries[UTSM_MAX_PCKC_KEYS]; + u64 hit_count; + u64 miss_count; + u32 next_replace; +} utsm_pckc; + +void utsm_pckc_init(void); +int utsm_pckc_get_or_derive(u32 segment_slot, struct utsm_segment_desc *desc, utsm_key_material *out_key); + +#endif diff --git a/CODE/UTSM/include/utsm/process.h b/CODE/UTSM/include/utsm/process.h new file mode 100644 index 0000000..2b1243d --- /dev/null +++ b/CODE/UTSM/include/utsm/process.h @@ -0,0 +1,18 @@ +#ifndef UTSM_PROCESS_H +#define UTSM_PROCESS_H + +#include +#include + +typedef struct utsm_process_context { + uuid128_t process_uuid; + u64 crypto_epoch; + u64 recovery_generation; + u32 capability_table_slot; + u32 capability_count; + u32 flags; +} utsm_process_context; + +void utsm_process_create_test(utsm_process_context *process, u64 seed); + +#endif diff --git a/CODE/UTSM/include/utsm/segment.h b/CODE/UTSM/include/utsm/segment.h new file mode 100644 index 0000000..9ba2f80 --- /dev/null +++ b/CODE/UTSM/include/utsm/segment.h @@ -0,0 +1,50 @@ +#ifndef UTSM_SEGMENT_H +#define UTSM_SEGMENT_H + +#include +#include +#include +#include +#include + +typedef enum { + UTSM_SEG_FREE = 0, + UTSM_SEG_ACTIVE, + UTSM_SEG_CHECKPOINTING, + UTSM_SEG_SEALED, + UTSM_SEG_RECOVERING, + UTSM_SEG_POISONED, + UTSM_SEG_DESTROYED +} utsm_segment_state; + +#define UTSM_SEG_F_READ (1U << 0) +#define UTSM_SEG_F_WRITE (1U << 1) +#define UTSM_SEG_F_EXEC (1U << 2) +#define UTSM_SEG_F_DMA (1U << 3) +#define UTSM_SEG_F_STRONG_RECOVERY (1U << 4) + +typedef struct utsm_segment_desc { + uuid128_t segment_uuid; + uuid128_t owner_process_uuid; + u8 *cipher_base; + u64 cipher_length; + void *dmp_base; + u64 key_epoch; + u64 tweak_seed; + utsm_dirty_shard *dirty_shards; + u32 dirty_shard_count; + void *mac_table; + u32 generation; + u32 flags; + u32 state; + u32 last_cpu; + volatile u64 writer_seq; + u64 checkpoint_generation; +} utsm_segment_desc; + +struct utsm_process_context; + +int utsm_create_segment(struct utsm_process_context *process, u64 size, u32 flags, utsm_capability *out_cap); +utsm_segment_desc *utsm_get_segment(u32 slot); + +#endif diff --git a/CODE/UTSM/include/utsm/status.h b/CODE/UTSM/include/utsm/status.h new file mode 100644 index 0000000..7eaf2e6 --- /dev/null +++ b/CODE/UTSM/include/utsm/status.h @@ -0,0 +1,18 @@ +#ifndef UTSM_STATUS_H +#define UTSM_STATUS_H + +typedef enum { + UTSM_OK = 0, + UTSM_ERR_INVALID = -1, + UTSM_ERR_NO_MEMORY = -2, + UTSM_ERR_ACCESS = -3, + UTSM_ERR_BOUNDS = -4, + UTSM_ERR_STALE_CAP = -5, + UTSM_ERR_EPOCH = -6, + UTSM_ERR_KEY_MISS = -7, + UTSM_ERR_STATE = -8, + UTSM_ERR_POISONED = -9, + UTSM_ERR_RETRY = -10 +} utsm_status; + +#endif diff --git a/CODE/UTSM/include/utsm/types.h b/CODE/UTSM/include/utsm/types.h new file mode 100644 index 0000000..898039e --- /dev/null +++ b/CODE/UTSM/include/utsm/types.h @@ -0,0 +1,36 @@ +#ifndef UTSM_TYPES_H +#define 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 signed short i16; +typedef signed int i32; +typedef signed long long i64; + +typedef u64 usize; +typedef i64 isize; + +typedef int bool; +#define true 1 +#define false 0 + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define UTSM_UNUSED(x) ((void)(x)) +#define UTSM_ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0])) + +static inline u64 utsm_align_up_u64(u64 value, u64 alignment) { + return (value + alignment - 1ULL) & ~(alignment - 1ULL); +} + +static inline void *utsm_align_up_ptr(void *ptr, u64 alignment) { + return (void *)utsm_align_up_u64((u64)ptr, alignment); +} + +#endif diff --git a/CODE/UTSM/include/utsm/utrw.h b/CODE/UTSM/include/utsm/utrw.h new file mode 100644 index 0000000..0b18e25 --- /dev/null +++ b/CODE/UTSM/include/utsm/utrw.h @@ -0,0 +1,12 @@ +#ifndef UTSM_UTRW_H +#define UTSM_UTRW_H + +#include +#include +#include + +int utsm_read(utsm_capability cap, u64 offset, void *dst, u64 len); +int utsm_write(utsm_capability cap, u64 offset, const void *src, u64 len); +int utsm_slow_path(int reason); + +#endif diff --git a/CODE/UTSM/include/utsm/utsm.h b/CODE/UTSM/include/utsm/utsm.h new file mode 100644 index 0000000..1740c5c --- /dev/null +++ b/CODE/UTSM/include/utsm/utsm.h @@ -0,0 +1,16 @@ +#ifndef UTSM_H +#define UTSM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +void utsm_init(void); +int utsm_selftest_run(void); + +#endif diff --git a/CODE/UTSM/include/utsm/uuid.h b/CODE/UTSM/include/utsm/uuid.h new file mode 100644 index 0000000..75eb702 --- /dev/null +++ b/CODE/UTSM/include/utsm/uuid.h @@ -0,0 +1,22 @@ +#ifndef UTSM_UUID_H +#define UTSM_UUID_H + +#include + +typedef struct { + u64 hi; + u64 lo; +} uuid128_t; + +static inline uuid128_t uuid_make_test(u64 seed) { + uuid128_t id; + id.hi = 0x5554534d00000000ULL ^ (seed * 0x9e3779b97f4a7c15ULL); + id.lo = 0x4453484200000000ULL ^ (seed + 0xd1b54a32d192ed03ULL); + return id; +} + +static inline bool uuid_equal(uuid128_t a, uuid128_t b) { + return a.hi == b.hi && a.lo == b.lo; +} + +#endif diff --git a/CODE/UTSM/kernel/block.c b/CODE/UTSM/kernel/block.c new file mode 100644 index 0000000..5e36527 --- /dev/null +++ b/CODE/UTSM/kernel/block.c @@ -0,0 +1,58 @@ +#include +#include + +static dkm_block_device_desc g_devices[DKM_BLOCK_MAX_DEVICES]; +static u32 g_device_count; + +void block_init(void) { + g_device_count = 0; + log_info("[BLOCK] registry init"); +} + +static int block_register_device_impl(const dkm_block_device_desc *desc) { + if (!desc || !desc->read || !desc->sector_size) return -1; + if (g_device_count >= DKM_BLOCK_MAX_DEVICES) return -2; + + u32 index = g_device_count++; + g_devices[index] = *desc; + + log_info("[BLOCK] device registered"); + if (desc->name) log_info(desc->name); + log_hex64("[BLOCK] index=", index); + log_hex64("[BLOCK] sector_size=", desc->sector_size); + log_hex64("[BLOCK] sector_count=", desc->sector_count); + return (int)index; +} + +static u32 block_device_count_impl(void) { + return g_device_count; +} + +static int block_read_impl(u32 index, u64 lba, u32 count, void *buffer) { + if (index >= g_device_count || !buffer || count == 0) return -1; + dkm_block_device_desc *dev = &g_devices[index]; + if (!dev->read) return -2; + return dev->read(dev->ctx, lba, count, buffer); +} + +static u64 block_sector_size_impl(u32 index) { + if (index >= g_device_count) return 0; + return g_devices[index].sector_size; +} + +static const char *block_device_name_impl(u32 index) { + if (index >= g_device_count) return 0; + return g_devices[index].name; +} + +static const dkm_block_api g_block_api = { + .register_device = block_register_device_impl, + .device_count = block_device_count_impl, + .read = block_read_impl, + .sector_size = block_sector_size_impl, + .device_name = block_device_name_impl +}; + +const dkm_block_api *block_get_api(void) { + return &g_block_api; +} diff --git a/CODE/UTSM/kernel/block.o b/CODE/UTSM/kernel/block.o new file mode 100644 index 0000000..fc324ad Binary files /dev/null and b/CODE/UTSM/kernel/block.o differ diff --git a/CODE/UTSM/kernel/dsk_loader.c b/CODE/UTSM/kernel/dsk_loader.c new file mode 100644 index 0000000..ff6ea0f --- /dev/null +++ b/CODE/UTSM/kernel/dsk_loader.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include +#include +#include "../arch/x86_64/limine.h" + +#define DSK_PATH "/system/deshab64/deshab.elf" +#define DSK_CMDLINE "dsk:main" + +#define ELF_MAGIC0 0x7f +#define ELF_MAGIC1 'E' +#define ELF_MAGIC2 'L' +#define ELF_MAGIC3 'F' +#define ELFCLASS64 2 +#define ELFDATA2LSB 1 +#define ET_DYN 3 +#define ET_EXEC 2 +#define EM_X86_64 62 +#define PT_LOAD 1 + +void *memset(void *dst, int value, usize len); +void *memcpy(void *dst, const void *src, usize len); + +extern volatile struct limine_module_request g_module_request; +extern volatile struct limine_rsdp_request g_rsdp_request; +extern volatile struct limine_framebuffer_request g_fb_request; +extern volatile struct limine_hhdm_request g_hhdm_request; + +typedef struct elf64_ehdr { + u8 ident[16]; + u16 type; + u16 machine; + u32 version; + u64 entry; + u64 phoff; + u64 shoff; + u32 flags; + u16 ehsize; + u16 phentsize; + u16 phnum; + u16 shentsize; + u16 shnum; + u16 shstrndx; +} elf64_ehdr; + +typedef struct elf64_phdr { + u32 type; + u32 flags; + u64 offset; + u64 vaddr; + u64 paddr; + u64 filesz; + u64 memsz; + u64 align; +} elf64_phdr; + +/* ---- minimal FAT32 parser ---- */ +typedef struct __attribute__((packed)) { + u8 jmp[3]; + char oem[8]; + u16 bytes_per_sector; + u8 sectors_per_cluster; + u16 reserved_sector_count; + u8 fat_count; + u16 root_entry_count; + u16 total_sectors_16; + u8 media; + u16 sectors_per_fat_16; + u16 sectors_per_track; + u16 heads; + u32 hidden_sectors; + u32 total_sectors_32; + u32 sectors_per_fat; + u16 flags; + u16 version; + u32 root_cluster; + u16 fsinfo_sector; + u16 backup_boot; + u8 reserved[12]; + u8 drive; + u8 nt_flags; + u8 signature; + u32 serial; + char label[11]; + char type[8]; + u8 code[420]; + u16 boot_sig; +} fat32_bpb; + +typedef struct __attribute__((packed)) { + char name[11]; + u8 attr; + u8 nt_reserved; + u8 creation_tenth; + u16 creation_time; + u16 creation_date; + u16 access_date; + u16 cluster_high; + u16 write_time; + u16 write_date; + u16 cluster_low; + u32 file_size; +} fat32_dir_entry; + +static u32 fat32_read_u32(const u8 *p) { + return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24); +} + +static u16 fat32_read_u16(const u8 *p) { + return (u16)p[0] | ((u16)p[1] << 8); +} + +static int fat32_name11_eq(const char *n, const char *s) { + for (int i = 0; i < 11; i++) { + if (n[i] != s[i]) return 0; + } + return 1; +} + +/* core helpers reused from earlier loader */ +static int streq(const char *a, const char *b) { + if (!a || !b) return 0; + while (*a && *b) { + if (*a != *b) return 0; + a++; + b++; + } + return *a == *b; +} + +static int range_ok(u64 offset, u64 length, u64 size) { + if (offset > size) return 0; + if (length > size - offset) return 0; + return 1; +} + +static struct limine_file *dsk_find_module(void) { + struct limine_module_response *rsp = g_module_request.response; + if (!rsp) return 0; + + for (u64 i = 0; i < rsp->module_count; i++) { + struct limine_file *file = rsp->modules[i]; + if (!file) continue; + if (streq(file->path, DSK_PATH) || streq(file->cmdline, DSK_CMDLINE)) { + return file; + } + } + return 0; +} + +/* ---- FAT32 block-provider file reader ---- */ +static u8 g_dsk_fat32_disk[65536]; /* 128-sector BPB+FAT+root dir buffer */ +static u8 g_dsk_fat32_cluster[4096]; /* 8-sector cluster buffer */ +static u8 g_dsk_fat32_filedata[65536]; /* file data buffer */ + +static int dsk_load_from_block_fat32(const void **out_addr, u64 *out_size) { + const dkm_kernel_api *api = dkm_get_kernel_api(); + if (!api || !api->block || !api->block->device_count) return -1; + u32 count = api->block->device_count(); + if (count == 0 || api->block->sector_size(0) != 512) return -1; + + /* Read first 128 sectors: BPB + FAT + root dir */ + u8 *disk = g_dsk_fat32_disk; + int st = api->block->read(0, 0, 128, disk); + if (st != 0) return -1; + + const fat32_bpb *bpb = (const fat32_bpb *)disk; + if (bpb->boot_sig != 0xAA55 || bpb->bytes_per_sector != 512) return -1; + + u32 fat_start = bpb->reserved_sector_count * 512; + u32 data_start_sec = bpb->reserved_sector_count + (u32)bpb->fat_count * bpb->sectors_per_fat; + + u32 clus = bpb->root_cluster; + u32 found_clus = 0; + u32 found_size = 0; + const char *target = "DESHAB ELF"; + + while (clus >= 2 && clus < 0x0FFFFFF8) { + u32 clus_lba = data_start_sec + (clus - 2) * bpb->sectors_per_cluster; + u32 cls_sec = bpb->sectors_per_cluster; + u8 *cb = g_dsk_fat32_cluster; + st = api->block->read(0, clus_lba, cls_sec, cb); + if (st != 0) return -1; + + const fat32_dir_entry *dir = (const fat32_dir_entry *)cb; + for (u32 e = 0; e * 32 < cls_sec * 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 (fat32_name11_eq(dir[e].name, target)) { + found_size = dir[e].file_size; + found_clus = fat32_read_u16((const u8 *)&dir[e].cluster_low); + break; + } + } + if (found_clus) break; + u32 fat_ent = fat_start + clus * 4; + if (fat_ent + 4 > 65536) break; + clus = fat32_read_u32(disk + fat_ent) & 0x0FFFFFFF; + } + + if (!found_clus || found_size == 0) return -1; + + if (found_size > sizeof(g_dsk_fat32_filedata)) return -1; + u8 *dst = g_dsk_fat32_filedata; + u32 remaining = found_size; + u32 fc = found_clus; + + while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) { + u32 fc_lba = data_start_sec + (fc - 2) * bpb->sectors_per_cluster; + u32 fc_sec = bpb->sectors_per_cluster; + u32 fc_bytes = fc_sec * 512; + if (fc_bytes > remaining) fc_bytes = remaining; + + u8 *chunk = g_dsk_fat32_cluster; + st = api->block->read(0, fc_lba, fc_sec, chunk); + if (st != 0) return -1; + for (u32 b = 0; b < fc_bytes; b++) dst[b] = chunk[b]; + dst += fc_bytes; + remaining -= fc_bytes; + + u32 fat_ent = fat_start + fc * 4; + if (fat_ent + 4 > 65536) { fc = 0x0FFFFFFF; break; } + fc = fat32_read_u32(disk + fat_ent) & 0x0FFFFFFF; + } + + /* Copy to arena for ELF loader */ + u8 *file_buf = (u8 *)kmem_alloc_aligned(found_size, 16); + if (!file_buf) return -1; + for (u32 i = 0; i < found_size; i++) file_buf[i] = g_dsk_fat32_filedata[i]; + + *out_addr = file_buf; + *out_size = found_size; + log_hex64("[UTSM] FAT32 file size=", found_size); + return 0; +} + +/* ---- ELF checks ---- */ +static int dsk_check_elf(const void *address, u64 size) { + if (!address || size < sizeof(elf64_ehdr)) return -1; + const elf64_ehdr *eh = (const elf64_ehdr *)address; + if (eh->ident[0] != ELF_MAGIC0 || eh->ident[1] != ELF_MAGIC1 || + eh->ident[2] != ELF_MAGIC2 || eh->ident[3] != ELF_MAGIC3) return -2; + if (eh->ident[4] != ELFCLASS64 || eh->ident[5] != ELFDATA2LSB) return -3; + if (eh->machine != EM_X86_64) return -4; + if (eh->type != ET_DYN && eh->type != ET_EXEC) return -5; + if (eh->phoff == 0 || eh->phnum == 0 || eh->phentsize < sizeof(elf64_phdr)) return -6; + if (!range_ok(eh->phoff, (u64)eh->phnum * eh->phentsize, size)) return -7; + return 0; +} + +static int dsk_load_elf_image(const void *address, u64 size, dsk_entry_fn *entry_out) { + const elf64_ehdr *eh = (const elf64_ehdr *)address; + const u8 *base = (const u8 *)address; + + u64 min_vaddr = ~0ULL; + u64 max_vaddr = 0; + u64 max_align = 0x1000; + u32 load_count = 0; + + for (u16 i = 0; i < eh->phnum; i++) { + const elf64_phdr *ph = (const elf64_phdr *)(base + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != PT_LOAD) continue; + if (ph->filesz > ph->memsz) return -1; + if (!range_ok(ph->offset, ph->filesz, size)) return -2; + if (ph->vaddr < min_vaddr) min_vaddr = ph->vaddr; + if (ph->vaddr + ph->memsz > max_vaddr) max_vaddr = ph->vaddr + ph->memsz; + if (ph->align > max_align) max_align = ph->align; + load_count++; + } + + if (load_count == 0 || min_vaddr == ~0ULL || max_vaddr <= min_vaddr) return -3; + if (eh->entry < min_vaddr || eh->entry >= max_vaddr) return -4; + + u64 image_size = utsm_align_up_u64(max_vaddr - min_vaddr, 0x1000); + u8 *image = (u8 *)kmem_alloc_aligned(image_size, max_align); + if (!image) return -5; + memset(image, 0, image_size); + + for (u16 i = 0; i < eh->phnum; i++) { + const elf64_phdr *ph = (const elf64_phdr *)(base + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != PT_LOAD) continue; + u64 dst_off = ph->vaddr - min_vaddr; + if (dst_off + ph->memsz > image_size) return -6; + memcpy(image + dst_off, base + ph->offset, ph->filesz); + } + + *entry_out = (dsk_entry_fn)(image + (eh->entry - min_vaddr)); + log_hex64("[UTSM] DSK image base=", (u64)image); + log_hex64("[UTSM] DSK image size=", image_size); + log_hex64("[UTSM] DSK entry=", (u64)*entry_out); + return 0; +} + +static void dsk_fill_boot_context(dsk_boot_context *ctx) { + memset(ctx, 0, sizeof(*ctx)); + ctx->magic = DSK_BOOT_MAGIC; + ctx->abi_version = DSK_BOOT_ABI_VERSION; + ctx->size = sizeof(*ctx); + ctx->flags = DSK_BOOT_FLAG_FROM_UTSM | DSK_BOOT_FLAG_DKM_READY | DSK_BOOT_FLAG_FAT32_PATH; + + if (g_hhdm_request.response) { + ctx->hhdm_offset = g_hhdm_request.response->offset; + } + if (g_rsdp_request.response) { + ctx->rsdp_address = (u64)g_rsdp_request.response->address; + } + if (g_fb_request.response && g_fb_request.response->framebuffer_count > 0) { + struct limine_framebuffer *fb = g_fb_request.response->framebuffers[0]; + ctx->framebuffer_address = (u64)fb->address; + ctx->framebuffer_width = fb->width; + ctx->framebuffer_height = fb->height; + ctx->framebuffer_pitch = fb->pitch; + ctx->framebuffer_bpp = fb->bpp; + } + ctx->boot_modules_response = (u64)g_module_request.response; + ctx->dkm_kernel_api = (u64)dkm_get_kernel_api(); + + u64 rsp; + __asm__ volatile ("movq %%rsp, %0" : "=r"(rsp)); + ctx->kernel_stack_top = rsp; +} + +int dsk_load_and_jump(void) { + log_info("[UTSM] loading DSK"); + log_info(DSK_PATH); + + const void *data = 0; + u64 data_size = 0; + + /* 1) try FAT32 block provider */ + int fat32_ok = dsk_load_from_block_fat32(&data, &data_size); + if (fat32_ok == 0) { + log_info("[UTSM] DSK loaded from FAT32 block provider"); + } else { + /* 2) fallback: Limine boot module */ + log_info("[UTSM] FAT32 block path unavailable; trying Limine module"); + struct limine_file *file = dsk_find_module(); + if (!file) { + log_error("[UTSM] DSK module not found (neither FAT32 nor Limine)"); + return -1; + } + data = file->address; + data_size = file->size; + log_hex64("[UTSM] DSK module size=", data_size); + } + + int status = dsk_check_elf(data, data_size); + if (status != 0) { + log_error("[UTSM] DSK ELF rejected"); + log_hex64("[UTSM] DSK ELF status=", (u64)(i64)status); + return status; + } + + dsk_entry_fn entry = 0; + status = dsk_load_elf_image(data, data_size, &entry); + if (status != 0) { + log_error("[UTSM] DSK load failed"); + log_hex64("[UTSM] DSK load status=", (u64)(i64)status); + return status; + } + + dsk_boot_context *ctx = (dsk_boot_context *)kmem_alloc_aligned(sizeof(dsk_boot_context), 16); + if (!ctx) { + log_error("[UTSM] DSK context allocation failed"); + return -1; + } + dsk_fill_boot_context(ctx); + + log_info("[UTSM] jumping to DSK"); + entry(ctx); + + log_error("[UTSM] DSK returned unexpectedly"); + return -1; +} diff --git a/CODE/UTSM/kernel/dsk_loader.o b/CODE/UTSM/kernel/dsk_loader.o new file mode 100644 index 0000000..12e1f0a Binary files /dev/null and b/CODE/UTSM/kernel/dsk_loader.o differ diff --git a/CODE/UTSM/kernel/log.c b/CODE/UTSM/kernel/log.c new file mode 100644 index 0000000..7dcf7e5 --- /dev/null +++ b/CODE/UTSM/kernel/log.c @@ -0,0 +1,34 @@ +#include + +static void log_line(const char *level, const char *msg) { + serial_write(level); + serial_write(" "); + serial_write(msg); + serial_write("\n"); +} + +void log_info(const char *msg) { + log_line("[INFO]", msg); +} + +void log_warn(const char *msg) { + log_line("[WARN]", msg); +} + +void log_error(const char *msg) { + log_line("[ERR ]", msg); +} + +void log_hex64(const char *prefix, u64 value) { + static const char hex[] = "0123456789abcdef"; + char buf[19]; + buf[0] = '0'; + buf[1] = 'x'; + for (int i = 0; i < 16; i++) { + buf[2 + i] = hex[(value >> ((15 - i) * 4)) & 0xf]; + } + buf[18] = 0; + serial_write(prefix); + serial_write(buf); + serial_write("\n"); +} diff --git a/CODE/UTSM/kernel/log.o b/CODE/UTSM/kernel/log.o new file mode 100644 index 0000000..4a698ed Binary files /dev/null and b/CODE/UTSM/kernel/log.o differ diff --git a/CODE/UTSM/kernel/main.c b/CODE/UTSM/kernel/main.c new file mode 100644 index 0000000..ed1d63a --- /dev/null +++ b/CODE/UTSM/kernel/main.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void arch_halt_forever(void); + +void kernel_main(void) { + serial_init(); + log_info("[UTSM] boot"); + + idt_init(); + + log_info("[UTSM] arena init begin"); + arena_init(); + log_info("[UTSM] arena init ok"); + + log_info("[UTSM] dma init begin"); + dma_init(); + log_info("[UTSM] dma init ok"); + + log_info("[UTSM] net registry init begin"); + net_init(); + log_info("[UTSM] net registry init ok"); + + log_info("[UTSM] drr stub init begin"); + drr_stub_init(); + log_info("[UTSM] drr stub init ok"); + + log_info("[UTSM] core init begin"); + utsm_init(); + log_info("[UTSM] init ok"); + + dkm_init(); + dkm_fill_platform_info(); + dsm_load_by_manifest(); + + int result = utsm_selftest_run(); + if (result == 0) { + log_info("[UTSM] SELFTEST PASS"); + if (dsk_load_and_jump() != 0) { + log_error("[UTSM] DSK jump failed"); + } + } else { + log_error("[UTSM] SELFTEST FAIL"); + } + + arch_halt_forever(); +} diff --git a/CODE/UTSM/kernel/main.o b/CODE/UTSM/kernel/main.o new file mode 100644 index 0000000..88fb8e0 Binary files /dev/null and b/CODE/UTSM/kernel/main.o differ diff --git a/CODE/UTSM/kernel/net.c b/CODE/UTSM/kernel/net.c new file mode 100644 index 0000000..f67837d --- /dev/null +++ b/CODE/UTSM/kernel/net.c @@ -0,0 +1,73 @@ +#include +#include + +/* + * 最小 netdev 注册表: + * - 早期 DKM 驱动加载路径是单线程顺序执行,当前无需引入锁,避免在网络底座阶段扩大复杂度。 + * - 后续进入多核/热插拔后,应在 register/device_info 外围加自旋锁或 RCU 风格只读快照。 + */ +static dkm_net_device_desc g_net_devices[DKM_NET_MAX_DEVICES]; +static u32 g_net_device_count; + +void net_init(void) { + g_net_device_count = 0; + log_info("[NET] registry init"); +} + +static int net_register_device_impl(const dkm_net_device_desc *desc) { + if (!desc || !desc->name) return -1; + if (g_net_device_count >= DKM_NET_MAX_DEVICES) return -2; + + u32 index = g_net_device_count++; + g_net_devices[index] = *desc; + + log_info("[NET] device registered"); + log_info(desc->name); + log_hex64("[NET] index=", index); + log_hex64("[NET] flags=", desc->flags); + return (int)index; +} + +static u32 net_device_count_impl(void) { + return g_net_device_count; +} + +static int net_device_info_impl(u32 index, dkm_net_device_info *out) { + if (!out) return -1; + if (index >= g_net_device_count) return -2; + + const dkm_net_device_desc *dev = &g_net_devices[index]; + out->name = dev->name; + for (u32 i = 0; i < 6; i++) { + out->mac[i] = dev->mac[i]; + } + out->flags = dev->flags; + return 0; +} + +static int net_tx_impl(u32 index, const void *packet, u32 length) { + if (index >= g_net_device_count) return -2; + const dkm_net_device_desc *dev = &g_net_devices[index]; + if (!dev->tx) return -3; + log_hex64("[NET] tx dispatch fn=", (u64)dev->tx); + return dev->tx(dev->ctx, packet, length); +} + +static int net_rx_poll_impl(u32 index, void *buffer, u32 capacity, u32 *out_length) { + if (index >= g_net_device_count) return -2; + const dkm_net_device_desc *dev = &g_net_devices[index]; + if (!dev->rx_poll) return -3; + return dev->rx_poll(dev->ctx, buffer, capacity, out_length); +} + +static const dkm_net_api g_net_api = { + .register_device = net_register_device_impl, + .device_count = net_device_count_impl, + .device_info = net_device_info_impl, + .tx = net_tx_impl, + .rx_poll = net_rx_poll_impl +}; + +const dkm_net_api *net_get_api(void) { + return &g_net_api; +} diff --git a/CODE/UTSM/kernel/net.o b/CODE/UTSM/kernel/net.o new file mode 100644 index 0000000..7953ba8 Binary files /dev/null and b/CODE/UTSM/kernel/net.o differ diff --git a/CODE/UTSM/kernel/panic.c b/CODE/UTSM/kernel/panic.c new file mode 100644 index 0000000..0b03bfc --- /dev/null +++ b/CODE/UTSM/kernel/panic.c @@ -0,0 +1,9 @@ +#include + +void arch_halt_forever(void); + +void panic(const char *msg) { + log_error("[PANIC]"); + log_error(msg); + arch_halt_forever(); +} diff --git a/CODE/UTSM/kernel/panic.o b/CODE/UTSM/kernel/panic.o new file mode 100644 index 0000000..83cadd4 Binary files /dev/null and b/CODE/UTSM/kernel/panic.o differ diff --git a/CODE/UTSM/linker.ld b/CODE/UTSM/linker.ld new file mode 100644 index 0000000..a021a3f --- /dev/null +++ b/CODE/UTSM/linker.ld @@ -0,0 +1,30 @@ +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS(5); /* R-X */ + rodata PT_LOAD FLAGS(4); /* R-- */ + data PT_LOAD FLAGS(7); /* RWX (SAS-R0) */ +} + +SECTIONS +{ + . = 0xffffffff80000000; + + .text : ALIGN(4K) { + *(.text*) + } :text + + .rodata : ALIGN(4K) { + *(.limine_requests_start*) + *(.limine_requests*) + *(.limine_requests_end*) + *(.rodata*) + } :rodata + + .data : ALIGN(4K) { + *(.data*) + *(COMMON) + *(.bss*) + } :data +} diff --git a/CODE/UTSM/mm/arena.c b/CODE/UTSM/mm/arena.c new file mode 100644 index 0000000..8b9f7d2 --- /dev/null +++ b/CODE/UTSM/mm/arena.c @@ -0,0 +1,38 @@ +#include +#include +#include + +static u8 arena[UTSM_ARENA_SIZE] __attribute__((aligned(UTSM_PAGE_SIZE))); +static u64 arena_offset; + +void arena_init(void) { + arena_offset = 0; +} + +void *kmem_alloc_aligned(u64 size, u64 alignment) { + u64 current = (u64)&arena[0] + arena_offset; + u64 aligned = utsm_align_up_u64(current, alignment); + u64 new_offset = (aligned - (u64)&arena[0]) + size; + if (new_offset > UTSM_ARENA_SIZE) { + log_error("[UTSM] arena out of memory"); + return NULL; + } + arena_offset = new_offset; + return (void *)aligned; +} + +void *kmem_alloc(u64 size) { + return kmem_alloc_aligned(size, 16); +} + +void *kmem_alloc_page(u64 page_count) { + return kmem_alloc_aligned(page_count * UTSM_PAGE_SIZE, UTSM_PAGE_SIZE); +} + +u64 kmem_arena_virtual_base(void) { + return (u64)&arena[0]; +} + +u64 kmem_arena_virtual_end(void) { + return (u64)&arena[0] + UTSM_ARENA_SIZE; +} diff --git a/CODE/UTSM/mm/arena.o b/CODE/UTSM/mm/arena.o new file mode 100644 index 0000000..5863c36 Binary files /dev/null and b/CODE/UTSM/mm/arena.o differ diff --git a/CODE/UTSM/mm/dma.c b/CODE/UTSM/mm/dma.c new file mode 100644 index 0000000..01dae07 --- /dev/null +++ b/CODE/UTSM/mm/dma.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include "../arch/x86_64/limine.h" + +#define PAGE_SIZE 4096ULL +#define DMA_LOW_MAX_PHYS 0x100000000ULL /* 只在低 4G 物理内存做 DMA 分配 */ +#define DMA_BITMAP_PAGES (DMA_LOW_MAX_PHYS / PAGE_SIZE) /* 1,048,576 页 */ +#define DMA_BITMAP_WORDS (DMA_BITMAP_PAGES / 64ULL) /* 每 word 覆盖 64 页 */ + +extern volatile struct limine_memmap_request g_memmap_request; +extern volatile struct limine_hhdm_request g_hhdm_request; + +/* + * 真实物理页分配器: + * - 以 memmap 中低 4G 内的 USABLE 区域构建 page bitmap(bit=1 表示可用)。 + * - Limine 保证 USABLE 区域不含 kernel/modules/framebuffer/bootloader 结构, + * 因此无需手动避让,标记为可用的页一定是安全的独立物理页。 + * - alloc 返回的 virt 使用 HHDM 直映射(hhdm_offset + phys), + * 保证 CPU 访问的虚拟地址与设备 bus-master 使用的物理地址指向同一页。 + */ +static u64 g_hhdm_offset; +static u64 g_bitmap[DMA_BITMAP_WORDS]; /* 1 bit/页,1=free */ +static u64 g_free_pages; +static u64 g_lowest_free_index; /* 分配起点提示,减少扫描 */ +static int g_dma_ready; + +static u64 align_up(u64 value, u64 alignment) { + if (alignment < PAGE_SIZE) alignment = PAGE_SIZE; + return (value + alignment - 1ULL) & ~(alignment - 1ULL); +} + +static void bitmap_mark_free(u64 index) { + g_bitmap[index >> 6] |= (1ULL << (index & 63ULL)); +} + +static void bitmap_mark_used(u64 index) { + g_bitmap[index >> 6] &= ~(1ULL << (index & 63ULL)); +} + +static int bitmap_is_free(u64 index) { + return (g_bitmap[index >> 6] >> (index & 63ULL)) & 1ULL; +} + +void dma_init(void) { + g_hhdm_offset = 0; + g_free_pages = 0; + g_lowest_free_index = DMA_BITMAP_PAGES; + g_dma_ready = 0; + for (u64 i = 0; i < DMA_BITMAP_WORDS; i++) g_bitmap[i] = 0; + + if (g_hhdm_request.response) { + g_hhdm_offset = g_hhdm_request.response->offset; + } + if (!g_hhdm_offset || !g_memmap_request.response) { + log_warn("[DMA] missing HHDM or memmap response"); + return; + } + + struct limine_memmap_response *rsp = g_memmap_request.response; + for (u64 i = 0; i < rsp->entry_count; i++) { + struct limine_memmap_entry *e = rsp->entries[i]; + if (!e || e->type != LIMINE_MEMMAP_USABLE) continue; + + u64 base = align_up(e->base, PAGE_SIZE); + u64 end = e->base + e->length; + if (end <= base) continue; + + /* 保留最低 1MiB,避免 legacy/实模式结构;限制在低 4G 内。 */ + if (base < 0x100000ULL) base = 0x100000ULL; + if (end > DMA_LOW_MAX_PHYS) end = DMA_LOW_MAX_PHYS; + if (end <= base) continue; + + u64 start_index = base / PAGE_SIZE; + u64 end_index = end / PAGE_SIZE; + for (u64 idx = start_index; idx < end_index; idx++) { + if (!bitmap_is_free(idx)) { + bitmap_mark_free(idx); + g_free_pages++; + if (idx < g_lowest_free_index) g_lowest_free_index = idx; + } + } + } + + if (g_free_pages == 0) { + log_warn("[DMA] no usable low pages found"); + return; + } + + g_dma_ready = 1; + log_info("[DMA] init ok"); + log_hex64("[DMA] free pages=", g_free_pages); + log_hex64("[DMA] first free phys=", g_lowest_free_index * PAGE_SIZE); +} + +int dma_alloc_pages(u64 page_count, u64 alignment, u64 max_phys, dkm_dma_buffer *out) { + if (!out || page_count == 0 || !g_dma_ready) return -1; + if (page_count > DMA_BITMAP_PAGES) return -3; + if (max_phys == 0 || max_phys > DMA_LOW_MAX_PHYS) max_phys = DMA_LOW_MAX_PHYS; + + if (alignment < PAGE_SIZE) alignment = PAGE_SIZE; + u64 align_pages = alignment / PAGE_SIZE; + if (align_pages == 0) align_pages = 1; + + u64 max_index = max_phys / PAGE_SIZE; + u64 start = g_lowest_free_index; + start = (start / align_pages) * align_pages; + + for (u64 base = start; base + page_count <= max_index; base += align_pages) { + int ok = 1; + for (u64 j = 0; j < page_count; j++) { + if (!bitmap_is_free(base + j)) { ok = 0; break; } + } + if (!ok) continue; + + for (u64 j = 0; j < page_count; j++) bitmap_mark_used(base + j); + g_free_pages -= page_count; + + u64 phys = base * PAGE_SIZE; + out->phys = phys; + out->virt = (void *)(g_hhdm_offset + phys); + out->size = page_count * PAGE_SIZE; + + u8 *p = (u8 *)out->virt; + for (u64 i = 0; i < out->size; i++) p[i] = 0; + return 0; + } + + log_warn("[DMA] alloc failed"); + log_hex64("[DMA] req pages=", page_count); + log_hex64("[DMA] free pages=", g_free_pages); + return -2; +} + +static const dkm_dma_api g_dma_api = { + .alloc_pages = dma_alloc_pages +}; + +const dkm_dma_api *dma_get_api(void) { + return &g_dma_api; +} diff --git a/CODE/UTSM/mm/dma.o b/CODE/UTSM/mm/dma.o new file mode 100644 index 0000000..c249466 Binary files /dev/null and b/CODE/UTSM/mm/dma.o differ diff --git a/CODE/UTSM/pckc/pckc.c b/CODE/UTSM/pckc/pckc.c new file mode 100644 index 0000000..a65a012 --- /dev/null +++ b/CODE/UTSM/pckc/pckc.c @@ -0,0 +1,36 @@ +#include +#include +#include + +static utsm_pckc g_pckc; + +void utsm_pckc_init(void) { + g_pckc.current_process_uuid = uuid_make_test(0); + g_pckc.hit_count = 0; + g_pckc.miss_count = 0; + g_pckc.next_replace = 0; + for (u32 i = 0; i < UTSM_MAX_PCKC_KEYS; i++) { + g_pckc.entries[i].valid = false; + } +} + +int utsm_pckc_get_or_derive(u32 segment_slot, utsm_segment_desc *desc, utsm_key_material *out_key) { + for (u32 i = 0; i < UTSM_MAX_PCKC_KEYS; i++) { + utsm_pckc_entry *entry = &g_pckc.entries[i]; + if (entry->valid && entry->segment_slot == segment_slot && entry->key_epoch == desc->key_epoch) { + *out_key = entry->key; + g_pckc.hit_count++; + return UTSM_OK; + } + } + + g_pckc.miss_count++; + u32 idx = g_pckc.next_replace++ % UTSM_MAX_PCKC_KEYS; + utsm_pckc_entry *entry = &g_pckc.entries[idx]; + entry->valid = true; + entry->segment_slot = segment_slot; + entry->key_epoch = desc->key_epoch; + utsm_kdf_segment_key(desc->owner_process_uuid, desc->segment_uuid, desc->key_epoch, &entry->key); + *out_key = entry->key; + return UTSM_OK; +} diff --git a/CODE/UTSM/pckc/pckc.o b/CODE/UTSM/pckc/pckc.o new file mode 100644 index 0000000..c6acb97 Binary files /dev/null and b/CODE/UTSM/pckc/pckc.o differ diff --git a/CODE/UTSM/runtime/assert.c b/CODE/UTSM/runtime/assert.c new file mode 100644 index 0000000..f31e685 --- /dev/null +++ b/CODE/UTSM/runtime/assert.c @@ -0,0 +1 @@ +#include diff --git a/CODE/UTSM/runtime/assert.o b/CODE/UTSM/runtime/assert.o new file mode 100644 index 0000000..8dbdd50 Binary files /dev/null and b/CODE/UTSM/runtime/assert.o differ diff --git a/CODE/UTSM/runtime/string.c b/CODE/UTSM/runtime/string.c new file mode 100644 index 0000000..5ba86cb --- /dev/null +++ b/CODE/UTSM/runtime/string.c @@ -0,0 +1,37 @@ +#include + +void *memset(void *dst, int value, usize len) { + u8 *d = (u8 *)dst; + for (usize i = 0; i < len; i++) { + d[i] = (u8)value; + } + return dst; +} + +void *memcpy(void *dst, const void *src, usize len) { + u8 *d = (u8 *)dst; + const u8 *s = (const u8 *)src; + for (usize i = 0; i < len; i++) { + d[i] = s[i]; + } + return dst; +} + +int memcmp(const void *a, const void *b, usize len) { + const u8 *x = (const u8 *)a; + const u8 *y = (const u8 *)b; + for (usize i = 0; i < len; i++) { + if (x[i] != y[i]) { + return (int)x[i] - (int)y[i]; + } + } + return 0; +} + +usize strlen(const char *s) { + usize len = 0; + while (s[len]) { + len++; + } + return len; +} diff --git a/CODE/UTSM/runtime/string.o b/CODE/UTSM/runtime/string.o new file mode 100644 index 0000000..c0e8ee0 Binary files /dev/null and b/CODE/UTSM/runtime/string.o differ diff --git a/CODE/UTSM/test/utsm_selftest.c b/CODE/UTSM/test/utsm_selftest.c new file mode 100644 index 0000000..beec582 --- /dev/null +++ b/CODE/UTSM/test/utsm_selftest.c @@ -0,0 +1,50 @@ +#include +#include +#include + +int memcmp(const void *a, const void *b, usize len); +usize strlen(const char *s); + +int utsm_selftest_run(void) { + utsm_process_context process; + utsm_process_create_test(&process, 1); + + 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); + if (status != UTSM_OK) { + log_error("[UTSM] selftest create segment failed"); + return status; + } + + const char *message = "deshab utsm sealed memory selftest"; + u64 len = strlen(message) + 1; + status = utsm_write(cap, 128, message, len); + if (status != UTSM_OK) { + log_error("[UTSM] selftest write failed"); + return status; + } + log_info("[UTSM] selftest write ok"); + + utsm_segment_desc *desc = utsm_get_segment(cap.segment_slot); + if (!desc) { + return UTSM_ERR_INVALID; + } + if (memcmp(desc->cipher_base + 128, message, len) == 0) { + log_error("[UTSM] raw cipher equals plaintext"); + return UTSM_ERR_INVALID; + } + log_info("[UTSM] raw cipher check ok"); + + char readback[64]; + status = utsm_read(cap, 128, readback, len); + if (status != UTSM_OK) { + log_error("[UTSM] selftest read failed"); + return status; + } + if (memcmp(readback, message, len) != 0) { + log_error("[UTSM] selftest read mismatch"); + return UTSM_ERR_INVALID; + } + log_info("[UTSM] selftest read ok"); + return UTSM_OK; +} diff --git a/CODE/UTSM/test/utsm_selftest.o b/CODE/UTSM/test/utsm_selftest.o new file mode 100644 index 0000000..9242949 Binary files /dev/null and b/CODE/UTSM/test/utsm_selftest.o differ diff --git a/CODE/UTSM/utrw/read.c b/CODE/UTSM/utrw/read.c new file mode 100644 index 0000000..abcac6a --- /dev/null +++ b/CODE/UTSM/utrw/read.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +void *memcpy(void *dst, const void *src, usize len); + +int utsm_read(utsm_capability cap, u64 offset, void *dst, u64 len) { + if (!dst && len != 0) { + return UTSM_ERR_INVALID; + } + if (len == 0) { + return UTSM_OK; + } + + utsm_segment_desc *desc = NULL; + int status = utsm_cap_validate(cap, UTSM_RIGHT_READ, offset, len, &desc); + if (status != UTSM_OK) { + return status; + } + + utsm_key_material key; + status = utsm_pckc_get_or_derive(cap.segment_slot, desc, &key); + if (status != UTSM_OK) { + return status; + } + + u8 line_plain[UTSM_CACHE_LINE_SIZE]; + u64 seq_before = desc->writer_seq; + if (seq_before & 1ULL) { + return UTSM_ERR_RETRY; + } + + u64 copied = 0; + while (copied < len) { + u64 absolute = offset + copied; + u64 line_start = (absolute / UTSM_CACHE_LINE_SIZE) * UTSM_CACHE_LINE_SIZE; + u64 in_line = absolute - line_start; + u64 chunk = UTSM_CACHE_LINE_SIZE - in_line; + if (chunk > len - copied) { + chunk = len - copied; + } + u64 line_index = line_start / UTSM_CACHE_LINE_SIZE; + u64 tweak = utsm_make_tweak(desc->tweak_seed, line_index, desc->key_epoch); + utsm_crypt_line(&key, tweak, desc->cipher_base + line_start, line_plain); + memcpy((u8 *)dst + copied, line_plain + in_line, chunk); + copied += chunk; + } + + u64 seq_after = desc->writer_seq; + if (seq_before != seq_after) { + return UTSM_ERR_RETRY; + } + return UTSM_OK; +} diff --git a/CODE/UTSM/utrw/read.o b/CODE/UTSM/utrw/read.o new file mode 100644 index 0000000..97e1223 Binary files /dev/null and b/CODE/UTSM/utrw/read.o differ diff --git a/CODE/UTSM/utrw/slow_path.c b/CODE/UTSM/utrw/slow_path.c new file mode 100644 index 0000000..b473d1c --- /dev/null +++ b/CODE/UTSM/utrw/slow_path.c @@ -0,0 +1,5 @@ +#include + +int utsm_slow_path(int reason) { + return reason; +} diff --git a/CODE/UTSM/utrw/slow_path.o b/CODE/UTSM/utrw/slow_path.o new file mode 100644 index 0000000..728dc89 Binary files /dev/null and b/CODE/UTSM/utrw/slow_path.o differ diff --git a/CODE/UTSM/utrw/write.c b/CODE/UTSM/utrw/write.c new file mode 100644 index 0000000..5ddbd6f --- /dev/null +++ b/CODE/UTSM/utrw/write.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include + +void *memcpy(void *dst, const void *src, usize len); + +int utsm_write(utsm_capability cap, u64 offset, const void *src, u64 len) { + if (!src && len != 0) { + return UTSM_ERR_INVALID; + } + if (len == 0) { + return UTSM_OK; + } + + utsm_segment_desc *desc = NULL; + int status = utsm_cap_validate(cap, UTSM_RIGHT_WRITE, offset, len, &desc); + if (status != UTSM_OK) { + return status; + } + + utsm_key_material key; + status = utsm_pckc_get_or_derive(cap.segment_slot, desc, &key); + if (status != UTSM_OK) { + return status; + } + + drr_log_write_intent(cap.segment_slot, offset, len); + desc->writer_seq++; + + u8 line_plain[UTSM_CACHE_LINE_SIZE]; + u8 line_cipher[UTSM_CACHE_LINE_SIZE]; + u64 written = 0; + while (written < len) { + u64 absolute = offset + written; + u64 line_start = (absolute / UTSM_CACHE_LINE_SIZE) * UTSM_CACHE_LINE_SIZE; + u64 in_line = absolute - line_start; + u64 chunk = UTSM_CACHE_LINE_SIZE - in_line; + if (chunk > len - written) { + chunk = len - written; + } + u64 line_index = line_start / UTSM_CACHE_LINE_SIZE; + u64 tweak = utsm_make_tweak(desc->tweak_seed, line_index, desc->key_epoch); + + if (chunk == UTSM_CACHE_LINE_SIZE && in_line == 0) { + memcpy(line_plain, (const u8 *)src + written, UTSM_CACHE_LINE_SIZE); + } else { + utsm_crypt_line(&key, tweak, desc->cipher_base + line_start, line_plain); + memcpy(line_plain + in_line, (const u8 *)src + written, chunk); + } + + utsm_crypt_line(&key, tweak, line_plain, line_cipher); + memcpy(desc->cipher_base + line_start, line_cipher, UTSM_CACHE_LINE_SIZE); + written += chunk; + } + + utsm_mark_dirty(desc, offset, len); + desc->writer_seq++; + drr_log_write_commit(cap.segment_slot, offset, len); + return UTSM_OK; +} diff --git a/CODE/UTSM/utrw/write.o b/CODE/UTSM/utrw/write.o new file mode 100644 index 0000000..73e9c28 Binary files /dev/null and b/CODE/UTSM/utrw/write.o differ diff --git a/CODE/dsk/MAKEFILE b/CODE/dsk/MAKEFILE new file mode 100644 index 0000000..164a6cc --- /dev/null +++ b/CODE/dsk/MAKEFILE @@ -0,0 +1,25 @@ +CC := clang +LD := ld.lld + +ROOT := ../.. +OUT := $(ROOT)/SYSTEM/system/deshab64/deshab.elf + +CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2 +LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld + +C_SOURCES := $(wildcard *.c) +OBJECTS := $(C_SOURCES:.c=.o) + +.PHONY: all clean + +all: $(OUT) + +$(OUT): $(OBJECTS) linker.ld + @if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64" + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }" diff --git a/CODE/dsk/README b/CODE/dsk/README new file mode 100644 index 0000000..0c1e8eb --- /dev/null +++ b/CODE/dsk/README @@ -0,0 +1,366 @@ +# DSK 主内核说明 + +`CODE/dsk` 是 Deshab 的主内核工程目录,目标产物为: + +```text +CODE/dsk -> SYSTEM/system/deshab64/deshab.elf +``` + +当前启动链路分为两层: + +```text +bootloader + -> SYSTEM/boot/utsm.elf + UTSM 首阶段内核 / DKM 驱动加载 / 平台发现 + -> SYSTEM/system/deshab64/deshab.elf + DSK 主内核 +``` + +也就是说,`utsm.elf` 不是最终长期运行的完整主内核,而是当前已经具备启动、封缄内存、DKM 驱动加载、平台发现和早期系统服务能力的前置内核阶段。后续 UTSM 需要继续执行到 `deshab.elf`,把控制权交给 DSK 主内核。 + +--- + +## 1. 模块定位 + +DSK 是 Deshab 的主内核,负责承接 UTSM 阶段已经建立的系统基础,并进入完整内核运行期。 + +DSK 的职责: + +```text +1. 接收 UTSM 传入的 boot context / platform context。 +2. 接管主调度器、内存管理、系统服务和长期运行状态。 +3. 使用 UTSM/DKM 已完成的平台发现结果。 +4. 继续使用或接管 DKM 驱动状态。 +5. 推进真实块设备、网络、VFS 和用户任务模型。 +6. 与 DRR 恢复根协作,支持 checkpoint / recovery / rollback。 +``` + +DSK 不应重复做已经由 UTSM 阶段完成的早期工作,例如: + +```text +- Limine request 原始解析 +- early serial 初始化 +- 早期 arena 初始化 +- DKM manifest 的首轮 stage 加载 +- 纯 discovery 型平台枚举 +``` + +DSK 应关注从“可启动实验内核”进入“可长期运行主内核”。 + +--- + +## 2. 产物路径 + +主内核 ELF 固定输出到: + +```text +SYSTEM/system/deshab64/deshab.elf +``` + +建议目录语义: + +```text +SYSTEM/ + boot/ + utsm.elf # bootloader 直接加载的首阶段内核 + + system/ + deshab64/ + deshab.elf # DSK 主内核 + + driver/ + manifest.json + ... *.drv # DKM 驱动模块 +``` + +`deshab64` 表示当前主内核目标架构为 x86_64 long mode。 + +--- + +## 3. UTSM 到 DSK 的执行关系 + +当前 UTSM 已完成: + +```text +- long mode C 入口 +- serial log +- UTSM/PCKC/UTRW selftest +- IDT 0-47 +- PIC IRQ fallback +- irq_register +- Limine RSDP/framebuffer/module/HHDM request +- DKM/DSM 外部 .drv 加载 +- 14/14 真实 DKM 驱动 discovery/加载闭环 +``` + +下一步需要让 UTSM 在完成早期阶段后定位并加载: + +```text +/system/deshab64/deshab.elf +``` + +推荐执行链: + +```text +kernel_main() + -> serial_init() + -> idt_init() + -> utsm_init() + -> dkm_init() + -> dsm_load_from_manifest() + -> utsm_selftest() + -> dsk_load_main_kernel("/system/deshab64/deshab.elf") + -> dsk_build_boot_context() + -> dsk_jump_entry() +``` + +DSK 入口不应假设自己由 bootloader 直接加载,而应假设由 UTSM 传入上下文。 + +--- + +## 4. DSK Boot Context + +UTSM 跳转 DSK 时应传入一个明确版本化的上下文结构。 + +建议第一版结构: + +```c +#define DSK_BOOT_MAGIC 0x44534B31424F4F54ULL /* "DSK1BOOT" */ +#define DSK_BOOT_ABI_VERSION 1 + +struct dsk_boot_context { + uint64_t magic; + uint32_t abi_version; + uint32_t size; + + uint64_t flags; + + uint64_t hhdm_offset; + + uint64_t rsdp_address; + + uint64_t framebuffer_address; + uint64_t framebuffer_width; + uint64_t framebuffer_height; + uint64_t framebuffer_pitch; + uint32_t framebuffer_bpp; + uint32_t reserved_fb; + + uint64_t boot_modules_response; + + uint64_t dkm_kernel_api; + uint64_t dkm_driver_table; + uint64_t dkm_driver_count; + + uint64_t utsm_state; + uint64_t drr_state; + + uint64_t memory_map; + uint64_t memory_map_count; + uint64_t memory_map_entry_size; + + uint64_t kernel_stack_top; + uint64_t reserved[8]; +}; +``` + +传参建议: + +```text +RDI = struct dsk_boot_context * +RSI = 0 +RDX = 0 +interrupt state = disabled 或明确约定 +paging = UTSM 当前页表 +address model = higher-half + HHDM +``` + +第一版可以只填必要字段: + +```text +magic / abi_version / size +hhdm_offset +rsdp_address +framebuffer 信息 +boot_modules_response +dkm_kernel_api +``` + +其余字段后续逐步补齐。 + +--- + +## 5. DSK ELF 加载要求 + +`deshab.elf` 建议先采用 ELF64 executable 或 relocatable-friendly layout。 + +第一版 loader 可只支持最小集合: + +```text +ELFCLASS64 +ELFDATA2LSB +EM_X86_64 +ET_EXEC 或 ET_DYN(二选一先固定) +PT_LOAD segment +PF_R / PF_W / PF_X +入口 e_entry +``` + +推荐第一版限制: + +```text +1. 不支持动态链接。 +2. 不支持 TLS。 +3. 不支持用户态权限切换。 +4. 不支持 demand paging。 +5. 不支持 symbol resolution。 +6. 所有 PT_LOAD 一次性复制/清零。 +7. 入口地址由 e_entry 给出。 +``` + +加载路径: + +```text +1. 从 bootfs/VFS 或 Limine module 查找 /system/deshab64/deshab.elf。 +2. 校验 ELF header。 +3. 计算 PT_LOAD 覆盖范围。 +4. 分配或确认目标虚拟地址范围。 +5. 复制 file-backed bytes。 +6. 清零 memsz - filesz。 +7. 设置段权限(第一版可沿用当前 RWX 简化策略)。 +8. 构造 dsk_boot_context。 +9. 跳转 e_entry。 +``` + +--- + +## 6. DSK 初始功能目标 + +第一阶段 DSK 不追求完整功能,先做可验证闭环。 + +最小启动目标: + +```text +1. 接收 dsk_boot_context。 +2. 校验 magic / abi_version / size。 +3. 复用 serial log 或 framebuffer log。 +4. 打印 DSK boot banner。 +5. 打印 hhdm / rsdp / framebuffer / driver_count。 +6. 执行 DSK selftest。 +7. 返回或 halt。 +``` + +成功日志建议: + +```text +[DSK] boot +[DSK] context ok +[DSK] hhdm offset=... +[DSK] framebuffer=... +[DSK] dkm drivers=... +[DSK] SELFTEST PASS +``` + +--- + +## 7. 后续主内核路线 + +DSK 阶段建议按以下顺序推进: + +```text +P0: DSK ELF loader + dsk_boot_context + 跳转入口 +P1: 物理页分配器 + 页表/MMIO 映射接口 +P2: DMA buffer / cache barrier / IOMMU 预留接口 +P3: AHCI IDENTIFY/READ 已具备最小 DMA 验证路径;继续推进 AHCI block provider 与 NVMe admin queue/identify +P4: FAT32 接入真实 block provider,替代 test.fat32 +P5: IDT 256 vectors + APIC/IOAPIC/MSI IRQ backend +P6: e1000 / virtio_net RX-TX 数据路径 +P7: 主调度器 SAS-R0-PCQ 与任务模型 +P8: DRR checkpoint / recovery log / A-B rollback +``` + +优先级原则: + +```text +先让 UTSM 能加载并跳到 DSK。 +再补内存/MMIO/DMA。 +再做块设备真实读取。 +再迁移文件系统和网络数据路径。 +最后进入调度器和恢复系统深化。 +``` + +--- + +## 8. 与 UTSM 的边界 + +UTSM 保留职责: + +```text +- 封缄内存模型 +- UTRW 明文读写路径 +- DMP/PCKC +- DKM 首轮驱动加载 +- early recovery hook +- DSK 加载前的早期平台发现 +``` + +DSK 接管职责: + +```text +- 主内核生命周期 +- 完整内存管理 +- 完整 IRQ backend +- 块设备与文件系统运行期 +- 网络栈运行期 +- 调度器和任务模型 +- 系统调用或内核服务入口 +``` + +二者关系不是互斥,而是阶段化: + +```text +UTSM = boot/runtime substrate +DSK = main kernel runtime +``` + +--- + +## 9. 当前待办 + +短期需要实现: + +```text +1. 已完成:build.ps1 增加 CODE/dsk -> SYSTEM/system/deshab64/deshab.elf 构建目标。 +2. 已完成:SYSTEM/boot/limine.conf 预加载 /system/deshab64/deshab.elf。 +3. 已完成:UTSM 中实现 DSK ELF64 PT_LOAD loader,优先通过 `kernel_api.block` + 内嵌 FAT32 解析从真实块设备读取 `DESHAB.ELF`(SATA FAT32 盘已验证);无块设备时自动回退 Limine boot module 预加载。 +4. 已完成:定义 dsk_boot_context 头文件,UTSM 与 DSK 共用。 +5. 已完成:编写 DSK 最小入口,打印 boot banner 和 SELFTEST PASS。 +6. 已完成:QEMU 验证从 UTSM 继续执行到 DSK。 +7. 已完成:把 "Limine 预加载 deshab.elf" 替换为 UTSM 通过 FAT32 block provider 读取。 + 后续:在 FAT32 中支持子目录路径,统一 DSK 加载与 VFS 路径语义。 +8. 下一步:按“块设备优先”推进 AHCI/NVMe IDENTIFY/READ 和真实 FAT32 挂载。 +``` + +当前验证日志: + +```text +[UTSM] SELFTEST PASS +[UTSM] loading DSK +[UTSM] DSK module size=... +[UTSM] DSK image base=... +[UTSM] DSK entry=... +[UTSM] jumping to DSK +[DSK] boot +[DSK] context ok +[DSK] SELFTEST PASS +``` + +验证标准: + +```text +[UTSM] SELFTEST PASS +[UTSM] loading DSK +[DSK] boot +[DSK] context ok +[DSK] SELFTEST PASS +``` diff --git a/CODE/dsk/linker.ld b/CODE/dsk/linker.ld new file mode 100644 index 0000000..78c6294 --- /dev/null +++ b/CODE/dsk/linker.ld @@ -0,0 +1,27 @@ +ENTRY(dsk_entry) + +PHDRS +{ + text PT_LOAD FLAGS(5); /* R-X */ + rodata PT_LOAD FLAGS(4); /* R-- */ + data PT_LOAD FLAGS(6); /* RW- */ +} + +SECTIONS +{ + . = 0x1000; + + .text : ALIGN(4K) { + *(.text*) + } :text + + .rodata : ALIGN(4K) { + *(.rodata*) + } :rodata + + .data : ALIGN(4K) { + *(.data*) + *(COMMON) + *(.bss*) + } :data +} diff --git a/CODE/dsk/main.c b/CODE/dsk/main.c new file mode 100644 index 0000000..4dc430c --- /dev/null +++ b/CODE/dsk/main.c @@ -0,0 +1,391 @@ +/* DSK Main Kernel — boot entry, FAT32 firstInit check, ELF loader */ + +#include "../UTSM/include/utsm/dsk.h" +#include "port.h" + +#define COM1 0x3F8 + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; + +typedef int (*block_read_fn)(u32 index, u64 lba, u32 count, void *buf); + +static void sputc(char c) { for(unsigned i=0;i<100000;i++){if(inb(COM1+5)&0x20)break;} outb(COM1,(u8)c); } +static void swrite(const char *s) { while(*s){if(*s=='\n')sputc('\r');sputc(*s++);} } +static void logl(const char *s) { swrite(s); swrite("\n"); } +static void logh(const char *p, u64 v) { static const char h[]="0123456789abcdef"; char b[19]; int i=0; b[i++]='0';b[i++]='x'; + for(int j=15;j>=0;j--)b[i++]=h[(v>>(j*4))&0xf];b[i]=0; swrite(p);swrite(b);swrite("\n"); } + +static void *memset_dsk(void *d, int c, u64 n) { u8 *p=(u8*)d; while(n--)*p++=(u8)c; return d; } +static void *memcpy_dsk(void *d, const void *s, u64 n) { u8 *dd=(u8*)d; const u8 *ss=(const u8*)s; while(n--)*dd++=*ss++; return d; } + +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; +} 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; +} fat32_de; + +static u32 r32(const u8 *p) { return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24); } +static u16 r16(const u8 *p) { return (u16)p[0]|((u16)p[1]<<8); } +static int neq11(const char *a, const char *b) { for(int i=0;i<11;i++) if(a[i]!=b[i]) return 0; return 1; } + +#define EI_MAG0 0x7f +#define ELFCLASS64 2 +#define ELFDATA2LSB 1 +#define EM_X86_64 62 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define 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; } elf64_ehdr; +typedef struct { u32 type,flags; u64 offset,vaddr,paddr,filesz,memsz,align; } elf64_phdr; +typedef struct { i64 tag; u64 val; } elf64_dyn; +typedef struct { u64 offset; u64 info; i64 addend; } elf64_rela; + +#define BG_TOP 0xFFC8E0F0u +#define BG_BOTTOM 0xFF49306Fu +#define BG_MID 0xFF8888B0u + +static u64 g_fb_addr, g_fb_w, g_fb_h, g_fb_p; +static block_read_fn g_block_read; +static u8 g_disk[131072]; +static u8 g_cluster[4096]; +static u8 g_fdata[262144]; + +static int fat32_read_sectors(u32 lba, u32 count, u8 *out) { return g_block_read ? g_block_read(0,lba,count,out) : -1; } + +static u32 blend_color(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; +} + +static u32 dsk_bg_at_y(i64 y) { + if (g_fb_h <= 1) return BG_TOP; + if (y < 0) y = 0; + if ((u64)y >= g_fb_h) y = (i64)g_fb_h - 1; + return blend_color(BG_TOP, BG_BOTTOM, (u32)(((u64)y * 255ULL) / (g_fb_h - 1))); +} + +static void dsk_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 >= g_fb_h) continue; + u32 color = dsk_bg_at_y(yy); + u32 *line = (u32 *)((u8 *)fb + (u64)yy * g_fb_p); + for (i64 c = 0; c < w; c++) { + i64 xx = x + c; + if (xx < 0 || (u64)xx >= g_fb_w) continue; + line[(u64)xx] = color; + } + } +} + +static int fat32_find_entry(const u8 *clus, u32 clus_sectors, const char *target, u32 *out_clus, u32 *out_size) { + const fat32_de *dir = (const fat32_de *)clus; + for (u32 e=0; e*32 < clus_sectors*512; e++) { + 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 (neq11(dir[e].name, target)) { + *out_clus = r16((const u8*)&dir[e].clow); + *out_size = dir[e].fsize; + return 0; + } + } + return -1; +} + +/* find a single file in root directory by 11-char 8.3 name (avoids PIE pointer reloc) */ +static int fat32_read_root_file(const char *name11, u8 **out_data, u32 *out_size) { + u8 *disk = g_disk; + logl("[DSK] fat32 reading 256 sectors from LBA 0"); + int st = fat32_read_sectors(0, 256, disk); + logh("[DSK] fat32 read_sectors rc=", (u64)(i64)st); + if (st != 0) return -1; + const fat32_bpb *bpb = (const fat32_bpb *)disk; + if (bpb->boot_sig != 0xAA55 || bpb->bps != 512) return -2; + if (bpb->spf == 0 || bpb->root_clus < 2) return -3; + + u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf; + u32 fat_byte_off = bpb->rsvd * 512; + u32 spc = bpb->spc; + logh("[DSK] fat32 data_lba=", data_lba); + + /* walk root directory cluster chain */ + 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 = disk + (u64)lba * 512; + else { + if (fat32_read_sectors(lba, 8, g_cluster) != 0) return -4; + cb = g_cluster; + } + if (fat32_find_entry(cb, spc, name11, &found_clus, &found_size) == 0) { found = 1; break; } + u32 fo = fat_byte_off + clus * 4; + if (fo + 4 > sizeof(g_disk)) break; + clus = r32(disk + fo) & 0x0FFFFFFF; + } + if (!found) { logl("[DSK] fat32 entry not found"); return -5; } + logh("[DSK] fat32 found_clus=", found_clus); + logh("[DSK] fat32 found_size=", found_size); + + 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 = disk + (u64)fc_lba * 512; + else { + if (fat32_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 = r32(disk + fo) & 0x0FFFFFFF; + } + *out_data = g_fdata; *out_size = found_size; + logl("[DSK] fat32 file read done"); + return 0; +} + +static int dsk_check_firstinit(int *is_first) { + char name[12]; /* "FIRSTINTXT" on stack — no PIE reloc */ + name[0]='F';name[1]='I';name[2]='R';name[3]='S';name[4]='T'; + name[5]='I';name[6]='N';name[7]='T';name[8]='X';name[9]='T';name[10]=0; + u8 *data = 0; u32 size = 0; + logl("[DSK] fat32_read_path start"); + int rc = fat32_read_root_file(name, &data, &size); + logh("[DSK] fat32_read_path rc=", (u64)(i64)rc); + if (rc != 0) { logl("[DSK] firstInit.txt not found"); *is_first = 1; return 0; } + if (data[0] == '0') { logl("[DSK] firstInit=0"); *is_first = 1; return 0; } + logl("[DSK] firstInit != 0"); *is_first = 0; return 0; +} + +static int dsk_load_elf(u8 *data, u32 size, void **entry_out) { + (void)size; + const elf64_ehdr *eh = (const elf64_ehdr *)data; + if (eh->ident[0] != EI_MAG0 || eh->ident[4] != ELFCLASS64) return -1; + if (eh->machine != EM_X86_64) return -2; + u64 min_vaddr = ~0ULL, max_vaddr = 0; + u32 lc = 0; + for (u16 i = 0; i < eh->phnum; i++) { + const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != 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; + isize = (isize + 0xFFF) & ~0xFFFULL; + static u8 ibuf[262144]; + u8 *image = ibuf; + memset_dsk(image, 0, isize); + for (u16 i = 0; i < eh->phnum; i++) { + const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != PT_LOAD) continue; + u64 off = ph->vaddr - min_vaddr; + memcpy_dsk(image + off, data + ph->offset, ph->filesz); + } + *entry_out = image + (eh->entry - min_vaddr); + + /* apply R_X86_64_RELATIVE relocations from PT_DYNAMIC (PIE) */ + u64 load_bias = (u64)image - min_vaddr; + for (u16 i = 0; i < eh->phnum; i++) { + const elf64_phdr *ph = (const elf64_phdr *)(data + eh->phoff + (u64)i * eh->phentsize); + if (ph->type != PT_DYNAMIC) continue; + const elf64_dyn *dyn = (const 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 == DT_RELA) rela_off = dyn->val; + else if (dyn->tag == DT_RELASZ) rela_sz = dyn->val; + else if (dyn->tag == DT_RELAENT) rela_ent = dyn->val; + } + if (rela_off && rela_sz) { + u64 n = rela_sz / rela_ent; + logh("[DSK] elf relocs=", n); + for (u64 r = 0; r < n; r++) { + const elf64_rela *rel = (const elf64_rela *)(image + (rela_off - min_vaddr) + r * rela_ent); + u32 rtype = (u32)(rel->info & 0xFFFFFFFF); + if (rtype == R_X86_64_RELATIVE) { + u64 *slot = (u64 *)(image + (rel->offset - min_vaddr)); + *slot = load_bias + (u64)rel->addend; + } + } + } + } + + logh("[DSK] elf image base=", (u64)image); + logh("[DSK] elf entry=", (u64)*entry_out); + return 0; +} + +static void dsk_spinner_and_check(u32 *fb_arg, i64 cx, i64 cy, i64 r, i64 thk, + const u32 arc_colors[4], u32 bg, const void *boot_ctx_ptr) { + (void)bg; + (void)fb_arg; + const dsk_boot_context *ctx = (const dsk_boot_context *)boot_ctx_ptr; + { u64 api = ctx->dkm_kernel_api; + g_block_read = (block_read_fn)*(u64 *)(api + 0xA8); + if (g_block_read) g_block_read = (block_read_fn)*(u64 *)((u64)g_block_read + 16); } + +#define SPR 128 + static u32 sprite[SPR*SPR]; + i64 sp=SPR*4, scx=SPR/2, scy=SPR/2, bx=cx-scx, by=cy-scy; + i64 frame=0, base=0; int checked=0; void *fe_entry=0; + + g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width; + g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch; + u32 *sfb=(u32*)(u64)g_fb_addr; + dsk_fill_gradient_rect(sfb, 0, 0, (i64)g_fb_w, (i64)g_fb_h); + u64 sw=g_fb_w, sh=g_fb_h, spv=g_fb_p; + logh("[DSK] block_read ptr=", (u64)g_block_read); + logl("[DSK] entering spinner loop"); + + while (1) { + g_fb_w=SPR; g_fb_h=SPR; g_fb_p=sp; + /* per-row gradient so sprite interior matches background */ + static u32 row_bg[SPR]; + for (i64 y0=0; y0= sh) ry = (i64)sh - 1; + row_bg[y0] = blend_color(BG_TOP, BG_BOTTOM, (u32)(((u64)ry * 255ULL) / (sh - 1))); + u32 *ln=&sprite[y0*SPR]; for(i64 x0=0;x0=SPR)continue; + u32 sbg=row_bg[yy]; + for(i64 dx=-ro-1;dx<=ro+1;dx++){i64 xx=scx+dx; if(xx<0||xx>=SPR)continue; + i64 d2=dx*dx+dy*dy; if(d2(ro+1)*(ro+1)+2)continue; + i64 ang; if(dx==0)ang=dy<0?90:270; + else{i64 ax=dx<0?-dx:dx,ay=dy<0?-dy:dy,a0=ax>ay?ay*45/ax:90-ax*45/(ay?ay:1); + if(dx>=0&&dy<=0)ang=a0;else if(dx<0&&dy<=0)ang=180-a0; + else if(dx<0&&dy>0)ang=180+a0;else ang=360-a0;} + i64 a=ang-sa; if(a<0)a+=360; if((u64)a>(u64)sw2)continue; + u32 fade; if((u64)a<6)fade=(u32)(a*255/6);else if((u64)a>(u64)(sw2-6))fade=(u32)((sw2-a)*255/6);else fade=255; + u64 vv=(u64)d2; u64 xr=vv,yr=(xr+1)>>1; while(yr>1;} i64 dist=(i64)xr; + u32 eff=((sbg&0xFF)*(256-fade)+(clr&0xFF)*fade)>>8; + u32 ge=(((sbg>>8)&0xFF)*(256-fade)+((clr>>8)&0xFF)*fade)>>8; + u32 be=(((sbg>>16)&0xFF)*(256-fade)+((clr>>16)&0xFF)*fade)>>8; + eff=0xFF000000|(be<<16)|(ge<<8)|eff; + if(dist>=ri&&dist<=ro)sprite[(u64)yy*SPR+(u64)xx]=eff; + }} + } + i64 ri2=r-8; + for(i64 dy=-ri2-2;dy<=ri2+2;dy++){i64 yy2=scy+dy; if(yy2<0||yy2>=SPR)continue; + u32 sbg2=row_bg[yy2]; + for(i64 dx=-ri2-2;dx<=ri2+2;dx++){i64 xx2=scx+dx; if(xx2<0||xx2>=SPR)continue; + i64 d2=dx*dx+dy*dy; + if(d2<=ri2*ri2){ + u32 ic=((sbg2&0xFF)*176+0xD0*80)>>8; + u32 ig=(((sbg2>>8)&0xFF)*176+0xF0*80)>>8; + u32 ib=(((sbg2>>16)&0xFF)*176+0xFF*80)>>8; + sprite[(u64)yy2*SPR+(u64)xx2]=0xFF000000|(ib<<16)|(ig<<8)|ic; + } + }} + } + g_fb_w=sw; g_fb_h=sh; g_fb_p=spv; + dsk_fill_gradient_rect(sfb, bx, by, SPR, SPR); + for(i64 sr=0;sr=g_fb_h)continue; + u32 *dst=(u32*)((u8*)sfb+(u64)sy*g_fb_p)+bx; u32 *src=&sprite[sr*SPR]; + for(i64 sc=0;sc=g_fb_w)continue; dst[sc]=src[sc];}} + for(volatile u32 d=0;d<72000;d++)__asm__("pause"); + base-=2; if(base<0)base+=360; frame++; + + if(frame>=100&&!checked){checked=1; /* was 540, reduced for test */ + logl("[DSK] checking firstInit..."); + if(g_block_read){logl("[DSK] have block read, calling dsk_check_firstinit"); + int is_first=0; + if(dsk_check_firstinit(&is_first)==0&&is_first){ + logl("[DSK] first init detected; running system initializers"); + + /* DSK owns system component initialization. FirstInit is only user setup. */ + char mn[12]; /* MOUSE ELF */ + mn[0]='M';mn[1]='O';mn[2]='U';mn[3]='S';mn[4]='E';mn[5]=' ';mn[6]=' ';mn[7]=' ';mn[8]='E';mn[9]='L';mn[10]='F'; + u8 *md=0; u32 ms=0; void *me=0; + if(fat32_read_root_file(mn,&md,&ms)==0){ + if(dsk_load_elf(md,ms,&me)==0){ + logl("[DSK] jumping to mouseInit"); + void(*mentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))me; + mentry(ctx); + logl("[DSK] mouseInit returned"); + } + } + + char nn[12]; /* NETMAN ELF */ + nn[0]='N';nn[1]='E';nn[2]='T';nn[3]='M';nn[4]='A';nn[5]='N';nn[6]=' ';nn[7]=' ';nn[8]='E';nn[9]='L';nn[10]='F'; + u8 *nd=0; u32 ns=0; void *ne=0; + if(fat32_read_root_file(nn,&nd,&ns)==0){ + if(dsk_load_elf(nd,ns,&ne)==0){ + logl("[DSK] jumping to netman"); + void(*nentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))ne; + nentry(ctx); + logl("[DSK] netman returned"); + } + } + + logl("[DSK] loading FirstInit.elf"); + char en[12]; /* FIRSTINIT */ + en[0]='F';en[1]='I';en[2]='R';en[3]='S';en[4]='T'; + en[5]='I';en[6]='N';en[7]='I';en[8]='T';en[9]=' ';en[10]=0; + u8 *fd=0; u32 fs=0; + if(fat32_read_root_file(en,&fd,&fs)==0){ + if(dsk_load_elf(fd,fs,&fe_entry)==0) logl("[DSK] FirstInit.elf loaded"); + } + } + } + } + if(fe_entry&&frame>150){logl("[DSK] jumping to FirstInit"); /* was 600 */ + void(*entry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))fe_entry; + entry(ctx); 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"))) +void dsk_entry(const dsk_boot_context *ctx) { + __asm__ volatile("cli"); /* disable IRQs — ps2kbd/IRQ1 & mouse/IRQ12 handlers crash during spinner */ + logl("[DSK] boot"); + if(!ctx||ctx->magic!=DSK_BOOT_MAGIC){logl("[DSK] bad context");for(;;)__asm__("hlt");} + logl("[DSK] context ok"); + g_fb_addr=ctx->framebuffer_address; g_fb_w=ctx->framebuffer_width; + g_fb_h=ctx->framebuffer_height; g_fb_p=ctx->framebuffer_pitch; + u32 bg=BG_MID, arcs[4]={0xFF4488CC,0xFF2266AA,0xFF66AAEE,0xFF88CCFF}; + i64 cx=(i64)g_fb_w/2,cy=(i64)g_fb_h/2,r=48,thk=4; + dsk_spinner_and_check((u32*)(u64)g_fb_addr,cx,cy,r,thk,arcs,bg,ctx); + logl("[DSK] SELFTEST PASS"); + for(;;)__asm__("hlt"); +} diff --git a/CODE/dsk/main.o b/CODE/dsk/main.o new file mode 100644 index 0000000..405a8be Binary files /dev/null and b/CODE/dsk/main.o differ diff --git a/CODE/dsk/port.h b/CODE/dsk/port.h new file mode 100644 index 0000000..e3d9a4e --- /dev/null +++ b/CODE/dsk/port.h @@ -0,0 +1,14 @@ +#ifndef DSK_PORT_H +#define DSK_PORT_H + +static inline void outb(unsigned short port, unsigned char value) { + __asm__ volatile ("outb %0, %1" :: "a"(value), "Nd"(port)); +} + +static inline unsigned char inb(unsigned short port) { + unsigned char value; + __asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +#endif diff --git a/CODE/firstInit/MAKEFILE b/CODE/firstInit/MAKEFILE new file mode 100644 index 0000000..5d38b78 --- /dev/null +++ b/CODE/firstInit/MAKEFILE @@ -0,0 +1,25 @@ +CC := clang +LD := ld.lld + +ROOT := ../.. +OUT := $(ROOT)/SYSTEM/system/user/use/FirstInit.elf + +CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2 +LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld + +C_SOURCES := main.c +OBJECTS := $(C_SOURCES:.c=.o) + +.PHONY: all clean + +all: $(OUT) + +$(OUT): $(OBJECTS) linker.ld + @if not exist "$(ROOT)\SYSTEM\system\user\use" mkdir "$(ROOT)\SYSTEM\system\user\use" + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }" diff --git a/CODE/firstInit/README b/CODE/firstInit/README new file mode 100644 index 0000000..2e37387 --- /dev/null +++ b/CODE/firstInit/README @@ -0,0 +1,57 @@ +# firstInit 首次启动引导 + +`CODE/firstInit` 是 Deshab 首次使用时的用户设置向导,只负责引导用户设置计算机名、用户名、密码等用户级配置。硬件/系统组件初始化(例如鼠标初始化)由 DSK 直接调度对应初始化程序,不由 FirstInit 接管。编译目标: + +```text +CODE/firstInit -> SYSTEM/system/user/use/FirstInit.elf +``` + +## 触发条件 + +DSK 主内核启动后,从 FAT32 块设备加载 `SYSTEM/system/user/use/firstInit.txt`: + +- 若 `firstInit == 0`:系统判定为**首次启动**,将值改为 `1`,加载 `FirstInit.elf` 执行用户引导 +- 若 `firstInit != 0`(或文件不存在):跳过引导,进入正常启动 + +## 执行流程 + +```text +1. 接收 DSK 传入的 dsk_boot_context +2. 淡出加载环动画(~1秒渐变) +3. 屏幕中央显示:"欢迎使用 Deshab" +4. 等待 5 秒 +5. 渐隐切换为:"接下来让我们来引导你设置你的系统" +6. 引导用户设置:网络、用户名、密码等(待实现) +``` + +## 产物路径 + +```text +SYSTEM/system/user/use/FirstInit.elf — 引导程序 ELF +SYSTEM/system/user/use/firstInit.txt — 首次标志文件(内容为 '0' 或 '1') +``` + +## 构建 + +```bash +cd CODE/firstInit +make -f MAKEFILE CC=clang LD=ld.lld +``` + +## 文件结构 + +```text +CODE/firstInit/ + main.c — 引导程序源码 + linker.ld — 链接脚本 + MAKEFILE — 构建规则 + README — 本文件 +``` + +## 后续规划 + +1. 键盘输入响应 +2. 网络配置界面(IP/DHCP/网关) +3. 用户名密码设置 +4. 时区/语言选择 +5. 完成后写入配置并跳转 DSK 正常启动 diff --git a/CODE/firstInit/linker.ld b/CODE/firstInit/linker.ld new file mode 100644 index 0000000..825d5f1 --- /dev/null +++ b/CODE/firstInit/linker.ld @@ -0,0 +1,16 @@ +ENTRY(dsk_entry) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); +} + +SECTIONS +{ + . = 0x1000; + .text : ALIGN(4K) { *(.text*) } :text + .rodata : ALIGN(4K) { *(.rodata*) } :rodata + .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data +} diff --git a/CODE/firstInit/main.c b/CODE/firstInit/main.c new file mode 100644 index 0000000..3665067 --- /dev/null +++ b/CODE/firstInit/main.c @@ -0,0 +1,982 @@ +/* FirstInit — first boot setup wizard + * Fades out loading ring, shows welcome, 5s delay, transitions to setup guide. + */ + +#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; } + +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"); } + +/* ---- PS/2 controller / mouse helpers (timeout-protected) ---- */ +static int ps2_wait_write(void) { + for (volatile u32 i = 0; i < 100000; i++) { + if (!(inb(0x64) & 0x02)) return 0; /* input buffer empty */ + } + return -1; +} +static int ps2_wait_read(void) { + for (volatile u32 i = 0; i < 100000; i++) { + if (inb(0x64) & 0x01) return 0; /* output buffer full */ + } + return -1; +} +static void ps2_cmd(u8 cmd) { + if (ps2_wait_write() == 0) outb(0x64, cmd); +} +static void mouse_cmd(u8 cmd) { + if (ps2_wait_write() == 0) outb(0x64, 0xD4); /* next byte -> mouse */ + if (ps2_wait_write() == 0) outb(0x60, cmd); +} +static void mouse_init(void) { + /* flush any pending data left by firmware */ + for (int i = 0; i < 16; i++) { + if (!(inb(0x64) & 0x01)) break; + inb(0x60); + } + logl("[mouse] enable AUX port"); + ps2_cmd(0xA8); /* enable AUX (mouse) port */ + /* read controller config: enable AUX clock, keep scan-code translation enabled */ + ps2_cmd(0x20); + u8 cfg = 0; + if (ps2_wait_read() == 0) cfg = inb(0x60); + logl("[mouse] old cfg read"); + cfg &= (u8)~(1 << 5); /* bit5=1 disables AUX clock; clear it */ + cfg |= (1 << 1) | (1 << 6); /* AUX IRQ + Set2→Set1 translation */ + ps2_cmd(0x60); + if (ps2_wait_write() == 0) outb(0x60, cfg); + logl("[mouse] new cfg written"); + /* reset mouse, drain ACK + self-test + device ID */ + logl("[mouse] reset"); + mouse_cmd(0xFF); + for (int i = 0; i < 32; i++) { + if (ps2_wait_read() != 0) break; + u8 r = inb(0x60); + if (r == 0xFA) logl("[mouse] ACK"); + else if (r == 0xAA) logl("[mouse] self-test OK"); + else if (r == 0x00) logl("[mouse] device ID 0"); + } + /* enable streaming mode */ + logl("[mouse] enable streaming"); + mouse_cmd(0xF4); + for (int i = 0; i < 16; i++) { + if (ps2_wait_read() != 0) break; + inb(0x60); + } + logl("[mouse] init done"); +} + +#define BG_TOP 0xFFC8E0F0u +#define BG_BOTTOM 0xFF49306Fu +#define BG_MID 0xFF8888B0u +#define CARD_BG 0xFFEAF5FBu +#define INPUT_BG 0xFFF0F5FAu +#define TEXT_FG 0xFF5F7FA6u +#define WELCOME_FG 0xFFE8F2FCu + +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; +} + +/* 8x8 font for ASCII */ +static const u8 font[95][8] = { + {0,0,0,0,0,0,0,0},{0x18,0x3c,0x3c,0x18,0x18,0,0x18,0}, /* ! */ + {0x6c,0x6c,0,0,0,0,0,0},{0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0}, /* # */ + {0x18,0x7e,0xc0,0x7c,0x6,0xfc,0x18,0},{0,0xc6,0xcc,0x18,0x30,0x66,0xc6,0}, /* $ % */ + {0x38,0x6c,0x38,0x76,0xdc,0xcc,0x76,0},{0x18,0x18,0x30,0,0,0,0,0}, /* & ' */ + {0xc,0x18,0x30,0x30,0x30,0x18,0xc,0},{0x30,0x18,0xc,0xc,0xc,0x18,0x30,0}, /* ( ) */ + {0,0x66,0x3c,0xff,0x3c,0x66,0,0},{0,0x18,0x18,0x7e,0x18,0x18,0,0}, /* * + */ + {0,0,0,0,0,0x18,0x18,0x30},{0,0,0,0x7e,0,0,0,0},{0,0,0,0,0,0x18,0x18,0}, /* , - . */ + {0x6,0xc,0x18,0x30,0x60,0xc0,0x80,0}, /* / */ + {0x7c,0xc6,0xce,0xde,0xf6,0xe6,0x7c,0},{0x18,0x38,0x18,0x18,0x18,0x18,0x7e,0}, /* 0 1 */ + {0x7c,0xc6,0x6,0xc,0x30,0x60,0xfe,0},{0x7c,0xc6,0x6,0x3c,0x6,0xc6,0x7c,0}, /* 2 3 */ + {0x1c,0x3c,0x6c,0xcc,0xfe,0xc,0x1e,0},{0xfe,0xc0,0xfc,0x6,0x6,0xc6,0x7c,0}, /* 4 5 */ + {0x38,0x60,0xc0,0xfc,0xc6,0xc6,0x7c,0},{0xfe,0xc6,0xc,0x18,0x30,0x30,0x30,0}, /* 6 7 */ + {0x7c,0xc6,0xc6,0x7c,0xc6,0xc6,0x7c,0},{0x7c,0xc6,0xc6,0x7e,0x6,0xc,0x78,0}, /* 8 9 */ + {0,0x18,0x18,0,0,0x18,0x18,0},{0,0x18,0x18,0,0,0x18,0x18,0x30}, /* : ; */ + {0x6,0xc,0x18,0x30,0x18,0xc,0x6,0},{0,0,0x7e,0,0,0x7e,0,0}, /* < = */ + {0x60,0x30,0x18,0xc,0x18,0x30,0x60,0},{0x7c,0xc6,0xc,0x18,0x18,0,0x18,0}, /* > ? */ + {0x7c,0xc6,0xde,0xde,0xde,0xc0,0x78,0}, /* @ */ + {0x38,0x6c,0xc6,0xfe,0xc6,0xc6,0xc6,0},{0xfc,0x66,0x66,0x7c,0x66,0x66,0xfc,0}, /* A B */ + {0x3c,0x66,0xc0,0xc0,0xc0,0x66,0x3c,0},{0xf8,0x6c,0x66,0x66,0x66,0x6c,0xf8,0}, /* C D */ + {0xfe,0x62,0x68,0x78,0x68,0x62,0xfe,0},{0xfe,0x62,0x68,0x78,0x68,0x60,0xf0,0}, /* E F */ + {0x3c,0x66,0xc0,0xc0,0xce,0x66,0x3e,0},{0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0}, /* G H */ + {0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0},{0x1e,0xc,0xc,0xc,0xcc,0xcc,0x78,0}, /* I J */ + {0xe6,0x66,0x6c,0x78,0x6c,0x66,0xe6,0},{0xf0,0x60,0x60,0x60,0x62,0x66,0xfe,0}, /* K L */ + {0xc6,0xee,0xfe,0xfe,0xd6,0xc6,0xc6,0},{0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0}, /* M N */ + {0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0},{0xfc,0x66,0x66,0x7c,0x60,0x60,0xf0,0}, /* O P */ + {0x7c,0xc6,0xc6,0xc6,0xc6,0xce,0x7c,0xe},{0xfc,0x66,0x66,0x7c,0x6c,0x66,0xe6,0}, /* Q R */ + {0x7c,0xc6,0xe0,0x78,0xe,0xc6,0x7c,0},{0x7e,0x7e,0x5a,0x18,0x18,0x18,0x3c,0}, /* S T */ + {0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0},{0xc6,0xc6,0xc6,0xc6,0xc6,0x6c,0x38,0}, /* U V */ + {0xc6,0xc6,0xc6,0xd6,0xd6,0xfe,0x6c,0},{0xc6,0xc6,0x6c,0x38,0x38,0x6c,0xc6,0}, /* W X */ + {0x66,0x66,0x66,0x3c,0x18,0x18,0x3c,0},{0xfe,0xc6,0x8c,0x18,0x32,0x66,0xfe,0}, /* Y Z */ + {0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}, /* [ \ */ + {0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}, /* ] ^ */ + {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0}, /* _ */ + {0,0,0,0,0,0,0,0}, /* ` */ + /* a-z lowercase — full-height (no top blank rows) so they scale like uppercase */ + {0x7c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0}, /* a */ + {0xc6,0xc6,0xc6,0xfc,0xc6,0xc6,0xfc,0}, /* b */ + {0x7c,0xc6,0xc0,0xc0,0xc0,0xc6,0x7c,0}, /* c */ + {0x06,0x06,0x06,0xfc,0xc6,0xc6,0xfc,0}, /* d */ + {0x7c,0xc6,0xfe,0xc0,0xfc,0xc0,0xc0,0}, /* e */ + {0x7c,0xc6,0xc0,0xf8,0xc0,0xc0,0xc0,0}, /* f */ + {0x7c,0xc6,0xc0,0xde,0xc6,0xc6,0x7c,0}, /* g */ + {0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0}, /* h */ + {0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0}, /* i */ + {0x06,0x06,0x06,0xc6,0xc6,0xc6,0x7c,0}, /* j */ + {0xc6,0xcc,0xd8,0xf0,0xd8,0xcc,0xc6,0}, /* k */ + {0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xfe,0}, /* l */ + {0xc6,0xee,0xfe,0xfe,0xc6,0xc6,0xc6,0}, /* m */ + {0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0}, /* n */ + {0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0}, /* o */ + {0xc6,0xc6,0xc6,0xfc,0xc0,0xc0,0xc0,0}, /* p */ + {0x7c,0xc6,0xc6,0xce,0x7c,0x06,0x06,0}, /* q */ + {0xc6,0xc6,0xcc,0xf8,0xcc,0xc6,0xc6,0}, /* r */ + {0x7c,0xc0,0x7c,0x06,0xc6,0xc6,0x7c,0}, /* s */ + {0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0}, /* t */ + {0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0}, /* u */ + {0xc6,0xc6,0xc6,0xc6,0xc6,0x6c,0x38,0}, /* v */ + {0xc6,0xc6,0xd6,0xfe,0xee,0xc6,0xc6,0}, /* w */ + {0xc6,0xc6,0x6c,0x38,0x6c,0xc6,0xc6,0}, /* x */ + {0xc6,0xc6,0xc6,0x7c,0x06,0x06,0x7c,0}, /* y */ + {0xfe,0x0c,0x18,0x30,0x60,0xc0,0xfe,0}, /* z */ +}; + +/* ASCII text — thin 8x8 → 10x16, preserves case, draws foreground only (no bg fill) */ +#define ASCII_W 10 +#define ASCII_H 16 +#define ASCII_STEP 12 +static void fb_char(u32 *fb, u32 ch, i64 x, i64 y, u32 fg, u32 bg) { + u32 idx = ch >= ' ' && ch <= 'z' ? (u32)(ch-' ') : 0; + const u8 *g = font[idx]; + u8 pat[8][8]; + for (i64 r=0; r<8; r++) { + u8 row = g[r]; + for (i64 c=0; c<8; c++) { + pat[r][c] = (row & (0x80>>c)) ? 1 : 0; + } + } + for (i64 sy=0; sy= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + i64 src_y = sy * 7 / (ASCII_H - 1); + if (src_y > 7) src_y = 7; + for (i64 sx=0; sx 7) src_x = 7; + int on = pat[src_y][src_x]; + int edge = 0; + if (!on) { + if (src_x > 0 && pat[src_y][src_x-1]) edge = 1; + if (src_x < 7 && pat[src_y][src_x+1]) edge = 1; + if (src_y > 0 && pat[src_y-1][src_x]) edge = 1; + if (src_y < 7 && pat[src_y+1][src_x]) edge = 1; + if (!edge) continue; + } + i64 xx = x + sx; + if (xx < 0 || (u64)xx >= fb_w) continue; + line[(u64)xx] = on ? fg : blend(bg, fg, 72); + } + } +} + +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); +} + +/* draw pre-rendered grayscale bitmap – writes only, no framebuffer read */ +static void fb_bitmap_alpha(u32 *fb, const u8 *data, i64 w, i64 h, i64 x, i64 y, u32 fg, u32 bg, u32 global_alpha) { + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + u32 a = ((u32)data[r*(u64)w + (u64)c] * global_alpha) >> 8; + line[(u64)xx] = blend(bg, fg, a); + } + } +} + +static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) { + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = color; + } + } +} + +static u32 bg_at_y(i64 y) { + if (fb_h <= 1) return BG_TOP; + if (y < 0) y = 0; + if ((u64)y >= fb_h) y = (i64)fb_h - 1; + return blend(BG_TOP, BG_BOTTOM, (u32)(((u64)y * 255ULL) / (fb_h - 1))); +} + +static void fill_gradient_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h) { + for (i64 r=0; r= fb_h) continue; + u32 color = bg_at_y(yy); + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = color; + } + } +} + +static int rounded_rect_contains(i64 px, i64 py, i64 x, i64 y, i64 w, i64 h, i64 radius) { + if (px < x || py < y || px >= x + w || py >= y + h) return 0; + if (radius <= 0) return 1; + if (radius * 2 > w) radius = w / 2; + if (radius * 2 > h) radius = h / 2; + + i64 left = x + radius; + i64 right = x + w - radius - 1; + i64 top = y + radius; + i64 bottom = y + h - radius - 1; + + if (px >= left && px <= right) return 1; + if (py >= top && py <= bottom) return 1; + + i64 cx = px < left ? left : right; + i64 cy = py < top ? top : bottom; + i64 dx = px - cx; + i64 dy = py - cy; + return dx * dx + dy * dy <= radius * radius; +} + +static void fill_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, u32 color) { + if (w <= 0 || h <= 0) return; + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + if (rounded_rect_contains(xx, yy, x, y, w, h, radius)) line[(u64)xx] = color; + } + } +} + +static void stroke_rounded_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, i64 thickness, u32 color) { + if (w <= 0 || h <= 0 || thickness <= 0) return; + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + int outer = rounded_rect_contains(xx, yy, x, y, w, h, radius); + int inner = rounded_rect_contains(xx, yy, x + thickness, y + thickness, w - thickness * 2, h - thickness * 2, radius - thickness); + if (outer && !inner) line[(u64)xx] = color; + } + } +} + +static void delay_frame(void) { + for (volatile u32 d=0; d<300000; d++) __asm__("pause"); +} + +#include "text_bitmaps.c" + +static void fade_ring(u32 *fb, i64 cx, i64 cy, u32 bg) { + /* Smoothly erase a square sprite area with the same vertical gradient as DSK. */ + (void)bg; + i64 r = 72; + for (int step=0; step<12; step++) { + (void)step; + fill_gradient_rect(fb, cx-r, cy-r, r*2+1, r*2+1); + delay_frame(); + } +} + +static void draw_card(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 card, u32 bg_color) { + (void)bg_color; + fill_rounded_rect(fb, x, y, w, h, 12, card); +} + +/* Draw gradient background + card in a single pass — per-row blend eliminates flicker */ +static void draw_card_fade(u32 *fb, i64 x, i64 y, i64 w, i64 h, i64 radius, u32 alpha) { + for (i64 r=0; r= fb_h) continue; + u32 bg_color = bg_at_y(yy); + u32 card_color = blend(bg_color, CARD_BG, alpha); + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = rounded_rect_contains(xx, yy, x, y, w, h, radius) ? card_color : bg_color; + } + } +} + +static void draw_rounded_input(u32 *fb, i64 x, i64 y, i64 w, i64 h, const char *value, int mask, u32 fg, u32 card_bg, int active) { + u32 fill = active ? 0xFFFFFFFF : INPUT_BG; + fill_rounded_rect(fb, x, y, w, h, 8, fill); + stroke_rounded_rect(fb, x, y, w, h, 8, 1, active ? 0xFFB6C9DD : card_bg); + char out[64]; int i=0; + while(value[i] && i<60) { out[i] = mask ? '*' : value[i]; i++; } + out[i]=0; + if (out[0]) fb_text(fb, out, x+14, y+(h-16)/2, fg, fill); + if (active) { + i64 cx2 = x + 14 + i * ASCII_STEP; + fill_rect(fb, cx2, y+10, 2, h-20, fg); + } +} + +static char scan_to_ascii(u8 sc, int shift) { + /* PS/2 scan code set 1 (controller translation enabled) */ + 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]; +} + +/* ---- cursor save / restore (avoids full card redraw on mouse move) ---- */ +#define CUR_W 24 +#define CUR_H 24 +static u32 cursor_bg[CUR_W * CUR_H]; +static int cursor_bg_valid = 0; +static i64 cursor_cur_x = -100, cursor_cur_y = -100; + +static const char *cursor_shape[CUR_H] = { + "X ", + "XX ", + "XOX ", + "XOOX ", + "XOOOX ", + "XOOOOX ", + "XOOOOOX ", + "XOOOOOOX ", + "XOOOOOOOX ", + "XOOOOOOOOX ", + "XOOOOOOOOOX ", + "XOOOOOOOOOOX ", + "XOOOOOOOXXXXX ", + "XOOOXOOX ", + "XOOXXOOX ", + "XOXX XOOX ", + "XXX XOOX ", + " XOOX ", + " XOOX ", + " XOOX ", + " XX ", + " ", + " ", + " " +}; + +static void cursor_erase(u32 *fb) { + if (!cursor_bg_valid) return; + for (i64 y = 0; y < CUR_H; y++) { + i64 py = cursor_cur_y + y; + if (py < 0 || (u64)py >= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)py * fb_p); + for (i64 x = 0; x < CUR_W; x++) { + i64 px = cursor_cur_x + x; + if (px < 0 || (u64)px >= fb_w) continue; + line[(u64)px] = cursor_bg[y * CUR_W + x]; + } + } + cursor_bg_valid = 0; +} + +static void cursor_draw(u32 *fb, i64 mx, i64 my) { + if (mx < 0) mx = 0; + if (my < 0) my = 0; + if ((u64)(mx + CUR_W) >= fb_w) mx = (i64)fb_w - CUR_W - 1; + if ((u64)(my + CUR_H) >= fb_h) my = (i64)fb_h - CUR_H - 1; + if (mx < 0) mx = 0; + if (my < 0) my = 0; + /* save background under cursor */ + for (i64 y = 0; y < CUR_H; y++) { + i64 py = my + y; + for (i64 x = 0; x < CUR_W; x++) { + i64 px = mx + x; + if (py < 0 || (u64)py >= fb_h || px < 0 || (u64)px >= fb_w) { + cursor_bg[y * CUR_W + x] = 0; + } else { + u32 *line = (u32 *)((u8 *)fb + (u64)py * fb_p); + cursor_bg[y * CUR_W + x] = line[(u64)px]; + } + } + } + cursor_bg_valid = 1; + cursor_cur_x = mx; + cursor_cur_y = my; + /* draw cursor shape */ + u32 white = 0xFFFFFFFF, dark = 0xFF304760; + for (i64 y = 0; y < CUR_H; y++) { + i64 py = my + y; + if (py < 0 || (u64)py >= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)py * fb_p); + for (i64 x = 0; x < CUR_W; x++) { + char p = cursor_shape[y][x]; + if (p == ' ') continue; + i64 px = mx + x; + if (px < 0 || (u64)px >= fb_w) continue; + line[(u64)px] = (p == 'O') ? white : dark; + } + } +} + +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; + cursor_erase(fb); + fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); + draw_card(fb, card_x, card_y, 900, 600, card, bg); + fb_bitmap_alpha(fb, g_txt_title, g_txt_title_w, g_txt_title_h, card_x+40, card_y+40, fg, card, 255); + fb_bitmap_alpha(fb, g_txt_computer, g_txt_computer_w, g_txt_computer_h, card_x+40, card_y+130, fg, card, 255); + 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); + 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); + 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); + cursor_draw(fb, mx, my); +} + +/* Returns: 0-2 = switch to that field, 3 = all done. */ +static int read_field(u32 *fb, i64 card_x, i64 card_y, int field, char *pc, char *user, char *pass, i64 *mx, i64 *my, u32 bg, u32 fg) { + char *buf = field==0 ? pc : (field==1 ? user : pass); + int max = 31; + int len = 0; + while (buf[len]) len++; + int shift = 0; + int release = 0; + int mcnt = 0; + u8 mpkt[3]; + u8 prev_btns = 0; + int mouse_log_cnt = 0; /* limit mouse log to first 20 packets */ + redraw_setup_card(fb, card_x, card_y, pc, user, pass, field, bg, fg, *mx, *my); + for (;;) { + u8 st = inb(0x64); + if (!(st & 1)) { __asm__("pause"); continue; } + u8 data = inb(0x60); + if (st & 0x20) { + /* mouse data: assemble 3-byte packet, sync on 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 (mouse_log_cnt < 20 && (dx != 0 || dy != 0 || btns)) { + logl("[mouse] pkt"); + mouse_log_cnt++; + } + if (dx != 0 || dy != 0) { + cursor_erase(fb); + *mx += dx; + *my -= dy; /* invert Y: mouse up = screen up */ + 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); + } + /* left click (rising edge) — switch to clicked input field */ + 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) { + buf[len] = 0; + if (f != field) return f; + break; + } + } + } + prev_btns = btns; + continue; + } + /* keyboard data: PS/2 scan code set 1 (translation enabled) */ + u8 sc = data; + if (sc == 0xF0) { release = 1; continue; } /* tolerate untranslated Set2 release */ + if (release) { release = 0; continue; } + if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } + if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } + if (sc & 0x80) continue; + char c = scan_to_ascii(sc, shift); + if (!c) continue; + if (c == '\n') { + buf[len] = 0; + return field < 2 ? field + 1 : 3; + } + if (c == 8) { + if (len > 0) buf[--len] = 0; + } else if (len < max-1 && c >= 32 && c <= 126) { + buf[len++] = c; + buf[len] = 0; + } + redraw_setup_card(fb, card_x, card_y, pc, user, pass, field, bg, fg, *mx, *my); + } +} + +typedef struct { + int language; + int region; + int timezone; + int keyboard; + int theme; +} setup_prefs; + +static const char *pref_value(int row, int val) { + if (row == 0) return val ? "English" : "Simplified Chinese"; + if (row == 1) return val ? "Global" : "China Mainland"; + if (row == 2) return val ? "UTC" : "Asia/Shanghai"; + if (row == 3) return val ? "CN-QWERTY" : "US-QWERTY"; + if (row == 4) return val ? "Dark" : "Light"; + return ""; +} + +static int pref_get(const setup_prefs *p, int row) { + if (row == 0) return p->language; + if (row == 1) return p->region; + if (row == 2) return p->timezone; + if (row == 3) return p->keyboard; + if (row == 4) return p->theme; + return 0; +} + +static void pref_toggle(setup_prefs *p, int row) { + if (row == 0) p->language ^= 1; + else if (row == 1) p->region ^= 1; + else if (row == 2) p->timezone ^= 1; + else if (row == 3) p->keyboard ^= 1; + else if (row == 4) p->theme ^= 1; +} + +static void draw_option_row(u32 *fb, i64 x, i64 y, i64 w, i64 h, + const u8 *label, i64 lw, i64 lh, + const char *value, int active, u32 fg, u32 card) { + u32 fill = active ? 0xFFFFFFFF : INPUT_BG; + fb_bitmap_alpha(fb, label, lw, lh, x, y + 4, fg, card, 255); + fill_rounded_rect(fb, x + 220, y, w - 220, h, 8, fill); + stroke_rounded_rect(fb, x + 220, y, w - 220, h, 8, 1, active ? 0xFFB6C9DD : card); + fb_text(fb, value, x + 238, y + (h - ASCII_H) / 2, fg, fill); +} + +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) { + u32 card = CARD_BG; + cursor_erase(fb); + fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); + draw_card(fb, card_x, card_y, 900, 600, card, bg); + fb_bitmap_alpha(fb, g_txt_prefs_title, g_txt_prefs_title_w, g_txt_prefs_title_h, card_x+40, card_y+40, fg, card, 255); + draw_option_row(fb, card_x+40, card_y+120, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card); + draw_option_row(fb, card_x+40, card_y+200, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card); + draw_option_row(fb, card_x+40, card_y+280, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card); + draw_option_row(fb, card_x+40, card_y+360, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card); + draw_option_row(fb, card_x+40, card_y+440, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card); + fb_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); + cursor_draw(fb, mx, my); +} + +static void redraw_prefs_row(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, int row, int active, i64 mx, i64 my, u32 fg) { + u32 card = CARD_BG; + i64 ry = card_y + 120 + (i64)row * 80; + cursor_erase(fb); + fill_rect(fb, card_x + 36, ry - 8, 832, 58, card); + if (row == 0) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_language, g_txt_prefs_language_w, g_txt_prefs_language_h, pref_value(0, pref_get(prefs, 0)), active==0, fg, card); + else if (row == 1) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_region, g_txt_prefs_region_w, g_txt_prefs_region_h, pref_value(1, pref_get(prefs, 1)), active==1, fg, card); + else if (row == 2) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_timezone, g_txt_prefs_timezone_w, g_txt_prefs_timezone_h, pref_value(2, pref_get(prefs, 2)), active==2, fg, card); + else if (row == 3) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_keyboard, g_txt_prefs_keyboard_w, g_txt_prefs_keyboard_h, pref_value(3, pref_get(prefs, 3)), active==3, fg, card); + else if (row == 4) draw_option_row(fb, card_x+40, ry, 820, 42, g_txt_prefs_theme, g_txt_prefs_theme_w, g_txt_prefs_theme_h, pref_value(4, pref_get(prefs, 4)), active==4, fg, card); + cursor_draw(fb, mx, my); +} + +static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, i64 *mx, i64 *my, u32 bg, u32 fg) { + int active = 0; + int mcnt = 0; + u8 mpkt[3]; + u8 prev_btns = 0; + int e0 = 0; + redraw_prefs_card(fb, card_x, card_y, prefs, active, *mx, *my, bg, fg); + for (;;) { + u8 st = inb(0x64); + if (!(st & 1)) { __asm__("pause"); continue; } + u8 data = inb(0x60); + if (st & 0x20) { + 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 || dy) { + cursor_erase(fb); + *mx += dx; *my -= dy; + 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 r = 0; r < 5; r++) { + i64 ry = card_y + 120 + (i64)r * 80; + if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) { + int old = active; + active = r; + pref_toggle(prefs, active); + if (old != active) redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg); + redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg); + } + } + } + prev_btns = btns; + continue; + } + u8 sc = data; + if (sc == 0xE0) { e0 = 1; continue; } + if (sc & 0x80) { e0 = 0; continue; } + if (e0 && sc == 0x48) { + if (active > 0) { int old = active; active--; redraw_prefs_row(fb, card_x, card_y, prefs, old, active, *mx, *my, fg); redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg); } + e0 = 0; continue; + } + if (e0 && sc == 0x50) { + if (active < 4) { 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; + if (sc == 0x39 || sc == 0x4D || sc == 0x4B) { + pref_toggle(prefs, active); + redraw_prefs_row(fb, card_x, card_y, prefs, active, active, *mx, *my, fg); + continue; + } + if (sc == 0x1C) { + if (active < 4) { + 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); + } else { + return; + } + } + } +} + +typedef struct { + int mode; + int device; + int ip; + int dns; +} setup_net; + +static const char *net_value(int row, int val) { + if (row == 0) return val == 0 ? "Disabled" : (val == 1 ? "DHCP" : "Manual"); + if (row == 1) return val == 0 ? "Auto" : (val == 1 ? "e1000" : "virtio-net"); + if (row == 2) return val ? "Manual" : "Auto DHCP"; + if (row == 3) return val ? "Manual DNS" : "Auto DNS"; + return ""; +} + +static int net_get(const setup_net *n, int row) { + if (row == 0) return n->mode; + if (row == 1) return n->device; + if (row == 2) return n->ip; + if (row == 3) return n->dns; + return 0; +} + +static void net_toggle(setup_net *n, int row) { + if (row == 0) n->mode = (n->mode + 1) % 3; + else if (row == 1) n->device = (n->device + 1) % 3; + else if (row == 2) n->ip ^= 1; + else if (row == 3) n->dns ^= 1; +} + +static void draw_network_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int row, int active, u32 fg) { + u32 card = CARD_BG; + i64 ry = card_y + 150 + (i64)row * 88; + const u8 *label = g_txt_net_mode; i64 lw = g_txt_net_mode_w, lh = g_txt_net_mode_h; + if (row == 1) { label = g_txt_net_device; lw = g_txt_net_device_w; lh = g_txt_net_device_h; } + else if (row == 2) { label = g_txt_net_ip; lw = g_txt_net_ip_w; lh = g_txt_net_ip_h; } + else if (row == 3) { label = g_txt_net_dns; lw = g_txt_net_dns_w; lh = g_txt_net_dns_h; } + draw_option_row(fb, card_x+40, ry, 820, 42, label, lw, lh, net_value(row, net_get(net, row)), active==row, fg, card); +} + +static void redraw_network_card(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int active, i64 mx, i64 my, u32 bg, u32 fg) { + u32 card = CARD_BG; + cursor_erase(fb); + fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); + draw_card(fb, card_x, card_y, 900, 600, card, bg); + fb_bitmap_alpha(fb, g_txt_network_title, g_txt_network_title_w, g_txt_network_title_h, card_x+40, card_y+40, fg, card, 255); + for (int r=0; r<4; r++) draw_network_row(fb, card_x, card_y, net, r, active, fg); + fb_bitmap_alpha(fb, g_txt_net_hint, g_txt_net_hint_w, g_txt_net_hint_h, card_x+40, card_y+555, 0xFF7F97AC, card, 255); + cursor_draw(fb, mx, my); +} + +static void redraw_network_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int row, int active, i64 mx, i64 my, u32 fg) { + u32 card = CARD_BG; + i64 ry = card_y + 150 + (i64)row * 88; + cursor_erase(fb); + fill_rect(fb, card_x + 36, ry - 8, 832, 58, card); + draw_network_row(fb, card_x, card_y, net, row, active, fg); + cursor_draw(fb, mx, my); +} + +static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i64 *mx, i64 *my, u32 bg, u32 fg) { + int active = 0, mcnt = 0, e0 = 0; + u8 mpkt[3]; u8 prev_btns = 0; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + for (;;) { + u8 st = inb(0x64); + if (!(st & 1)) { __asm__("pause"); continue; } + u8 data = inb(0x60); + if (st & 0x20) { + if (mcnt == 0 && !(data & 0x08)) continue; + mpkt[mcnt++] = data; + if (mcnt < 3) continue; + mcnt = 0; + i64 dx = (i64)(i8)mpkt[1], dy = (i64)(i8)mpkt[2]; + u8 btns = mpkt[0] & 0x07; + if (dx || dy) { + cursor_erase(fb); + *mx += dx; *my -= dy; + 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 r=0; r<4; r++) { + i64 ry = card_y + 150 + (i64)r * 88; + if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) { + int old = active; active = r; net_toggle(net, active); + if (old != active) redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); + redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); + } + } + } + prev_btns = btns; + continue; + } + u8 sc = data; + if (sc == 0xE0) { e0 = 1; continue; } + if (sc & 0x80) { e0 = 0; continue; } + if (e0 && sc == 0x48) { if (active > 0) { int old=active; active--; redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); } e0=0; continue; } + if (e0 && sc == 0x50) { if (active < 3) { int old=active; active++; redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); } e0=0; continue; } + e0 = 0; + if (sc == 0x39 || sc == 0x4D || sc == 0x4B) { net_toggle(net, active); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); continue; } + if (sc == 0x1C) { + if (active < 3) { int old=active; active++; redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); } + else return; + } + } +} + +/* compact SHA-256 */ +#define ROR(x,n) (((x)>>(n))|((x)<<(32-(n)))) +static const u32 K256[64] = {0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2}; +static void sha256(const char *msg, u8 out[32]) { + u32 h[8]={0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19}; + u8 block[64]; u64 len=0; while(msg[len]) len++; + for(int i=0;i<64;i++) block[i]=0; + for(u64 i=0;i>(i*8)); + u32 w[64]; + for(int i=0;i<16;i++) w[i]=((u32)block[i*4]<<24)|((u32)block[i*4+1]<<16)|((u32)block[i*4+2]<<8)|block[i*4+3]; + for(int i=16;i<64;i++){u32 s0=ROR(w[i-15],7)^ROR(w[i-15],18)^(w[i-15]>>3);u32 s1=ROR(w[i-2],17)^ROR(w[i-2],19)^(w[i-2]>>10);w[i]=w[i-16]+s0+w[i-7]+s1;} + u32 a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7]; + for(int i=0;i<64;i++){u32 S1=ROR(e,6)^ROR(e,11)^ROR(e,25);u32 ch=(e&f)^((~e)&g);u32 t1=hh+S1+ch+K256[i]+w[i];u32 S0=ROR(a,2)^ROR(a,13)^ROR(a,22);u32 maj=(a&b)^(a&c)^(b&c);u32 t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;} + h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh; + for(int i=0;i<8;i++){out[i*4]=(u8)(h[i]>>24);out[i*4+1]=(u8)(h[i]>>16);out[i*4+2]=(u8)(h[i]>>8);out[i*4+3]=(u8)h[i];} +} + +static void build_user_conf(const char *pc, const char *user, const char *pass, const setup_prefs *prefs, const setup_net *net) { + u8 hash[32]; sha256(pass, hash); + static char conf[1024]; int p=0; + const char *pre="DESHAB_USERCONF_V1\ncomputer="; + for(int i=0;pre[i];i++) conf[p++]=pre[i]; + for(int i=0;pc[i]&&p<740;i++) conf[p++]=pc[i]; + const char *mid="\nusername="; for(int i=0;mid[i];i++) conf[p++]=mid[i]; + for(int i=0;user[i]&&p<740;i++) conf[p++]=user[i]; + const char *hs="\npasswordSha256="; for(int i=0;hs[i];i++) conf[p++]=hs[i]; + static const char hx[]="0123456789abcdef"; + for(int i=0;i<32;i++){conf[p++]=hx[hash[i]>>4];conf[p++]=hx[hash[i]&15];} + const char *pref="\nlocale="; for(int i=0;pref[i];i++) conf[p++]=pref[i]; + const char *v0 = prefs->language ? "en-US" : "zh-CN"; for(int i=0;v0[i];i++) conf[p++]=v0[i]; + const char *r0="\nregion="; for(int i=0;r0[i];i++) conf[p++]=r0[i]; + const char *v1 = prefs->region ? "GLOBAL" : "CN"; for(int i=0;v1[i];i++) conf[p++]=v1[i]; + const char *t0="\ntimezone="; for(int i=0;t0[i];i++) conf[p++]=t0[i]; + const char *v2 = prefs->timezone ? "UTC" : "Asia/Shanghai"; for(int i=0;v2[i];i++) conf[p++]=v2[i]; + const char *k0="\nkeyboard="; for(int i=0;k0[i];i++) conf[p++]=k0[i]; + const char *v3 = prefs->keyboard ? "cn-qwerty" : "us-qwerty"; for(int i=0;v3[i];i++) conf[p++]=v3[i]; + const char *th0="\ntheme="; for(int i=0;th0[i];i++) conf[p++]=th0[i]; + const char *v4 = prefs->theme ? "dark" : "light"; for(int i=0;v4[i];i++) conf[p++]=v4[i]; + const char *nm="\nnetwork.mode="; for(int i=0;nm[i];i++) conf[p++]=nm[i]; + const char *nv0 = net->mode == 0 ? "disabled" : (net->mode == 1 ? "dhcp" : "manual"); for(int i=0;nv0[i];i++) conf[p++]=nv0[i]; + const char *nd="\nnetwork.device="; for(int i=0;nd[i];i++) conf[p++]=nd[i]; + const char *nv1 = net->device == 0 ? "auto" : (net->device == 1 ? "e1000" : "virtio-net"); for(int i=0;nv1[i];i++) conf[p++]=nv1[i]; + const char *ni="\nnetwork.ip="; for(int i=0;ni[i];i++) conf[p++]=ni[i]; + const char *nv2 = net->ip ? "manual" : "dhcp"; for(int i=0;nv2[i];i++) conf[p++]=nv2[i]; + const char *dns="\nnetwork.dns="; for(int i=0;dns[i];i++) conf[p++]=dns[i]; + const char *nv3 = net->dns ? "manual" : "auto"; for(int i=0;nv3[i];i++) conf[p++]=nv3[i]; + conf[p++]='\n'; + /* XOR encrypt buffer with hash-derived stream; real disk write waits for FAT32 write support. */ + for(int i=0;imagic != 0x44534B31424F4F54ULL) { + logl("[FirstInit] bad context"); + for(;;) __asm__("hlt"); + } + + fb_a = ctx->framebuffer_address; + fb_w = ctx->framebuffer_width; + fb_h = ctx->framebuffer_height; + fb_p = ctx->framebuffer_pitch; + + logl("[FirstInit] framebuffer info:"); + swrite("[FirstInit] fb_addr="); { char b[19]; u64 v=fb_a; b[0]='0';b[1]='x'; + for(int j=15;j>=0;j--) b[2+15-j]="0123456789abcdef"[(v>>(j*4))&0xf]; b[18]=0; swrite(b); swrite("\n"); } + swrite("[FirstInit] fb_w="); { char b[19]; u64 v=fb_w; b[0]='0';b[1]='x'; + for(int j=15;j>=0;j--) b[2+15-j]="0123456789abcdef"[(v>>(j*4))&0xf]; b[18]=0; swrite(b); swrite("\n"); } + swrite("[FirstInit] fb_h="); { char b[19]; u64 v=fb_h; b[0]='0';b[1]='x'; + for(int j=15;j>=0;j--) b[2+15-j]="0123456789abcdef"[(v>>(j*4))&0xf]; b[18]=0; swrite(b); swrite("\n"); } + + u32 bg = BG_MID, fg = TEXT_FG; + i64 cx=(i64)fb_w/2, cy=(i64)fb_h/2; + u32 *fb = (u32 *)(u64)fb_a; + fill_gradient_rect(fb, 0, 0, (i64)fb_w, (i64)fb_h); + + /* fade out loading ring */ + logl("[FirstInit] fading out ring"); + fade_ring(fb, cx, cy, bg); + logl("[FirstInit] ring faded"); + + /* center text: Chinese bitmaps */ + i64 tw = g_txt_welcome_w, tx = cx - tw/2, ty = cy - g_txt_welcome_h/2; + + logl("[FirstInit] fading in welcome"); + for (u32 a=0; a<=255; a+=17) { + fill_gradient_rect(fb, tx-4, ty-4, tw+8, g_txt_welcome_h+8); + 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); + delay_frame(); + } + logl("[FirstInit] welcome drawn"); + + for (int i=0; i<90; i++) delay_frame(); + + logl("[FirstInit] cross-fading to setup message"); + i64 tw2 = g_txt_setup_w, tx2 = cx - tw2/2; + i64 area_x = tx2 < tx ? tx2 : tx; + i64 area_w = (tx2 + tw2 > tx + tw ? tx2 + tw2 : tx + tw) - area_x; + i64 area_h = (g_txt_setup_h > g_txt_welcome_h ? g_txt_setup_h : g_txt_welcome_h) + 8; + + for (u32 step=0; step<=15; step++) { + u32 a2 = step * 17; + u32 a1 = 255 - a2; + fill_gradient_rect(fb, area_x-4, ty-4, area_w+8, area_h); + fb_bitmap_alpha(fb, g_txt_welcome, tw, g_txt_welcome_h, tx, ty, WELCOME_FG, bg_at_y(ty + g_txt_welcome_h / 2), a1); + fb_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); + delay_frame(); + } + logl("[FirstInit] setup message drawn"); + + for (int i=0; i<35; i++) delay_frame(); + + logl("[FirstInit] drawing account setup card"); + char pc[32]; char user[32]; char pass[32]; + pc[0]=0; user[0]=0; pass[0]=0; + i64 card_x = cx - 450; + i64 card_y = cy - 300; + /* fade in card + title — per-row gradient blend eliminates flicker */ + logl("[FI] fadein start"); + /* Phase 1: fade in card background only (no title) — avoids title/card color mismatch flicker */ + for (u32 a=0; a<=255; a+=17) { + draw_card_fade(fb, card_x, card_y, 900, 600, 12, a); + delay_frame(); + } + /* Phase 2: draw title once on fully-rendered card */ + { + u32 title_bg = blend(bg_at_y(card_y + 40 + g_txt_title_h/2), CARD_BG, 255); + fb_bitmap_alpha(fb, g_txt_title, g_txt_title_w, g_txt_title_h, card_x+40, card_y+40, fg, title_bg, 255); + } + logl("[FI] fadein done"); + logl("[FirstInit] initializing PS/2 mouse"); + mouse_init(); + i64 mx = cx, my = cy; + { + int field = 0; + while (field < 3) { + field = read_field(fb, card_x, card_y, field, pc, user, pass, &mx, &my, bg, fg); + } + } + setup_prefs prefs; + prefs.language = 0; /* zh-CN */ + prefs.region = 0; /* CN */ + prefs.timezone = 0; /* Asia/Shanghai */ + prefs.keyboard = 0; /* US-QWERTY */ + prefs.theme = 0; /* Light */ + read_prefs_page(fb, card_x, card_y, &prefs, &mx, &my, bg, fg); + setup_net net; + net.mode = 0; /* Disabled */ + net.device = 0; /* Auto */ + net.ip = 0; /* Auto DHCP */ + net.dns = 0; /* Auto DNS */ + read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg); + build_user_conf(pc, user, pass, &prefs, &net); + + cursor_erase(fb); + fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); + fb_bitmap_alpha(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 20, fg, bg_at_y(cy), 255); + logl("[FirstInit] user setup finished"); + + for(;;) __asm__("hlt"); +} diff --git a/CODE/firstInit/main.o b/CODE/firstInit/main.o new file mode 100644 index 0000000..082e6ab Binary files /dev/null and b/CODE/firstInit/main.o differ diff --git a/CODE/firstInit/text_bitmaps.c b/CODE/firstInit/text_bitmaps.c new file mode 100644 index 0000000..2c693ee --- /dev/null +++ b/CODE/firstInit/text_bitmaps.c @@ -0,0 +1,753 @@ +/* Auto-generated */ + +static const int g_txt_welcome_w = 360; +static const int g_txt_welcome_h = 54; +static const unsigned char g_txt_welcome[360*54] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 61, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 202, 137, 76, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 123, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 166, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 207, 118, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 255, 255, 247, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 255, 255, 255, 252, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 218, 255, 255, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 144, 243, 255, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 255, 255, 250, 173, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 161, 0, 0, 0, 0, 0, 0, 0, 0, 20, 70, 132, 209, 255, 255, 255, 255, 255, 226, 27, 5, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 228, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 245, 255, 255, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 255, 255, 255, 242, 8, 0, 0, 0, 0, 0, 0, 198, 255, 255, 255, 255, 255, 255, 255, 255, 254, 215, 120, 26, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 255, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 255, 251, 198, 136, 74, 17, 0, 0, 17, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 239, 255, 255, 242, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 255, 255, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 255, 255, 255, 238, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 252, 255, 255, 252, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 246, 255, 255, 255, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 168, 0, 0, 0, 0, 0, 0, 207, 255, 255, 237, 72, 12, 0, 0, 0, 0, 0, 0, 8, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 199, 188, 176, 169, 165, 161, 160, 160, 160, 160, 160, 160, 160, 160, 160, 130, 0, 0, 0, 0, 157, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 255, 255, 244, 8, 0, 0, 0, 0, 0, 200, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 255, 255, 255, 54, 0, 47, 245, 232, 222, 215, 211, 209, 208, 208, 208, 208, 208, 208, 220, 255, 255, 255, 223, 208, 208, 208, 208, 208, 208, 211, 214, 217, 222, 232, 242, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 252, 236, 217, 196, 142, 77, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 169, 0, 0, 0, 0, 213, 255, 255, 248, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209, 255, 255, 255, 76, 0, 0, 0, 0, 0, 193, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 249, 255, 255, 215, 0, 0, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 137, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 121, 0, 0, 0, 16, 254, 255, 255, 234, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 152, 0, 0, 0, 0, 0, 185, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 129, 0, 0, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 35, 139, 131, 124, 116, 109, 101, 96, 96, 96, 96, 96, 114, 255, 255, 255, 72, 0, 0, 0, 71, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 231, 140, 0, 0, 0, 0, 0, 178, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 233, 255, 255, 255, 112, 0, 0, 17, 87, 73, 64, 57, 52, 50, 48, 48, 48, 48, 48, 48, 100, 255, 255, 255, 113, 48, 48, 48, 48, 48, 49, 52, 55, 58, 63, 74, 84, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 186, 144, 147, 167, 193, 248, 255, 255, 255, 255, 255, 251, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 24, 0, 0, 0, 136, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 134, 53, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 11, 91, 223, 255, 255, 255, 255, 251, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 230, 0, 0, 0, 0, 206, 255, 255, 214, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 212, 255, 255, 255, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 221, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 8, 176, 255, 255, 255, 255, 211, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 172, 0, 0, 0, 27, 254, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 224, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 121, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 226, 255, 255, 226, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 181, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 4, 187, 255, 255, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 0, 0, 210, 255, 255, 114, 0, 0, 0, 119, 255, 255, 253, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 220, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 84, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 196, 255, 255, 255, 201, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 32, 250, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 86, 239, 121, 0, 0, 0, 0, 0, 15, 253, 255, 255, 57, 0, 0, 0, 213, 255, 255, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 255, 255, 255, 250, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 143, 255, 255, 251, 61, 0, 0, 0, 0, 79, 255, 255, 248, 6, 0, 0, 75, 255, 255, 255, 94, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 237, 2, 0, 0, 0, 0, 0, 0, 0, 80, 96, 84, 74, 69, 65, 64, 64, 64, 27, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 11, 231, 255, 255, 208, 240, 255, 255, 112, 0, 0, 0, 0, 97, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 220, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 248, 255, 255, 248, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 236, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 255, 255, 255, 255, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 167, 255, 255, 255, 231, 24, 0, 0, 0, 153, 255, 255, 195, 0, 0, 0, 197, 255, 255, 243, 11, 0, 0, 0, 192, 235, 205, 170, 100, 0, 0, 0, 53, 255, 255, 255, 171, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 105, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 85, 240, 255, 255, 112, 0, 0, 0, 0, 90, 255, 255, 255, 88, 32, 32, 32, 32, 32, 32, 88, 255, 255, 255, 102, 32, 32, 32, 32, 32, 32, 32, 255, 255, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 43, 64, 72, 51, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 57, 72, 72, 55, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 4, 43, 69, 73, 55, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 58, 74, 69, 52, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 11, 77, 108, 118, 94, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 201, 255, 255, 255, 191, 3, 0, 0, 227, 255, 255, 128, 0, 0, 64, 255, 255, 255, 159, 0, 0, 0, 0, 194, 255, 255, 255, 154, 0, 0, 0, 53, 153, 198, 249, 107, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 97, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 36, 248, 255, 255, 214, 2, 240, 255, 255, 112, 0, 0, 0, 0, 86, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 255, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 179, 244, 255, 255, 255, 255, 250, 191, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 136, 225, 255, 255, 255, 255, 255, 255, 210, 112, 7, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 29, 155, 241, 255, 255, 255, 255, 250, 175, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 130, 222, 255, 255, 255, 255, 255, 253, 202, 100, 3, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 5, 137, 242, 255, 255, 255, 255, 255, 212, 79, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 229, 255, 255, 255, 130, 0, 49, 255, 255, 255, 61, 0, 0, 185, 255, 255, 255, 65, 0, 0, 0, 0, 197, 255, 255, 255, 120, 0, 0, 0, 0, 0, 0, 8, 11, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 250, 245, 248, 255, 255, 255, 89, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 2, 194, 255, 255, 255, 90, 0, 240, 255, 255, 112, 0, 0, 0, 0, 81, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 25, 191, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 181, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 217, 37, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 71, 238, 255, 255, 255, 255, 255, 255, 255, 255, 243, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 198, 21, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 12, 199, 255, 255, 255, 255, 255, 255, 255, 255, 255, 160, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 58, 250, 255, 255, 252, 57, 125, 255, 255, 234, 3, 0, 0, 22, 101, 201, 227, 2, 0, 0, 0, 0, 202, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 16, 3, 0, 0, 128, 255, 255, 255, 80, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 26, 223, 255, 255, 216, 2, 0, 240, 255, 255, 112, 0, 0, 0, 0, 87, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 214, 0, 0, 0, 0, 0, 0, 44, 229, 255, 255, 255, 255, 252, 230, 237, 255, 255, 255, 255, 255, 208, 9, 0, 0, 0, 0, 0, 0, 0, 0, 74, 252, 255, 255, 255, 255, 249, 231, 236, 254, 255, 255, 255, 255, 228, 18, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 87, 253, 255, 255, 245, 198, 185, 233, 255, 255, 255, 255, 233, 17, 0, 0, 0, 0, 0, 0, 0, 90, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 199, 3, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 175, 255, 255, 255, 250, 231, 249, 255, 255, 255, 255, 255, 173, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 220, 208, 255, 255, 153, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 210, 255, 255, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 74, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 17, 192, 255, 90, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 95, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 255, 228, 0, 0, 0, 0, 0, 19, 222, 255, 255, 255, 217, 89, 11, 0, 0, 26, 126, 247, 255, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 7, 231, 255, 255, 255, 194, 58, 4, 0, 0, 8, 82, 206, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 154, 247, 255, 251, 130, 15, 0, 0, 3, 117, 254, 255, 255, 255, 123, 0, 0, 0, 0, 0, 0, 29, 243, 255, 255, 255, 226, 104, 42, 20, 26, 55, 130, 248, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 231, 90, 6, 0, 2, 66, 200, 255, 255, 255, 255, 109, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 184, 255, 255, 255, 255, 255, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 255, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 71, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 21, 183, 2, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 103, 255, 255, 255, 112, 64, 64, 64, 64, 64, 64, 112, 255, 255, 255, 124, 64, 64, 64, 64, 64, 64, 64, 255, 255, 255, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 255, 255, 255, 235, 0, 0, 0, 0, 0, 164, 255, 255, 255, 193, 12, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 252, 29, 0, 0, 0, 0, 0, 0, 75, 255, 255, 255, 215, 7, 0, 0, 0, 0, 0, 0, 18, 241, 255, 255, 227, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 247, 71, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 201, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 231, 30, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 188, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 226, 29, 0, 0, 0, 0, 0, 4, 182, 255, 255, 255, 238, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 239, 255, 255, 255, 255, 241, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 255, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 68, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 111, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 236, 0, 0, 0, 0, 37, 252, 255, 255, 244, 25, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 129, 0, 0, 0, 0, 0, 0, 106, 255, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 164, 224, 224, 223, 4, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 255, 87, 0, 0, 0, 0, 0, 0, 0, 21, 253, 255, 255, 240, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 1, 224, 255, 255, 243, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 22, 243, 255, 255, 255, 103, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 255, 255, 255, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 65, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 160, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 119, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 255, 255, 255, 166, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 236, 255, 255, 236, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 206, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 255, 255, 229, 0, 0, 0, 0, 132, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 190, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 255, 255, 255, 0, 0, 0, 0, 0, 0, 62, 64, 64, 64, 11, 0, 0, 0, 0, 0, 0, 0, 0, 193, 255, 255, 255, 9, 0, 0, 0, 0, 0, 192, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 255, 176, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 226, 255, 255, 255, 239, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 255, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 0, 0, 9, 125, 16, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 170, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 94, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 220, 255, 255, 255, 199, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 255, 255, 217, 0, 0, 0, 0, 198, 255, 255, 255, 153, 112, 112, 112, 112, 112, 112, 112, 112, 112, 138, 255, 255, 255, 241, 1, 0, 0, 0, 0, 0, 35, 255, 255, 255, 252, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 254, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 194, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 140, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 255, 255, 255, 232, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 176, 255, 255, 224, 0, 0, 1, 90, 226, 255, 32, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 0, 210, 255, 255, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 9, 14, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 255, 255, 255, 194, 0, 0, 0, 0, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 184, 255, 255, 255, 254, 187, 115, 66, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 64, 108, 157, 219, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 255, 255, 255, 13, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 255, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 255, 255, 246, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 179, 255, 255, 224, 0, 65, 202, 255, 255, 255, 56, 0, 0, 240, 255, 255, 160, 0, 0, 0, 0, 0, 95, 255, 255, 255, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 18, 197, 175, 0, 0, 0, 0, 0, 0, 145, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255, 255, 199, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 244, 255, 255, 244, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 226, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 255, 255, 255, 165, 0, 0, 0, 12, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 29, 227, 255, 255, 255, 255, 255, 255, 255, 232, 181, 112, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 4, 83, 164, 226, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 253, 255, 255, 255, 30, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 254, 255, 255, 206, 255, 255, 255, 233, 16, 0, 0, 0, 0, 0, 0, 0, 0, 2, 237, 255, 255, 255, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 214, 255, 255, 238, 189, 255, 255, 255, 255, 255, 90, 0, 0, 240, 255, 255, 160, 0, 0, 99, 176, 219, 255, 255, 255, 255, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 55, 227, 255, 255, 119, 0, 0, 0, 0, 0, 175, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 252, 255, 255, 255, 124, 0, 0, 0, 27, 255, 255, 255, 255, 145, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 24, 0, 0, 0, 0, 0, 0, 0, 22, 160, 250, 255, 255, 255, 255, 255, 255, 255, 255, 254, 178, 35, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 73, 222, 255, 255, 255, 255, 255, 255, 255, 255, 234, 160, 209, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 255, 255, 255, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 255, 46, 179, 255, 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 231, 255, 255, 222, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 47, 255, 255, 255, 255, 255, 255, 255, 255, 222, 117, 22, 0, 0, 240, 255, 255, 160, 0, 0, 56, 255, 255, 255, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 19, 219, 255, 255, 254, 85, 0, 0, 0, 0, 210, 255, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 255, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 255, 69, 0, 0, 0, 14, 255, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 94, 154, 199, 242, 255, 255, 255, 255, 255, 255, 243, 55, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 102, 254, 255, 255, 255, 255, 230, 184, 143, 98, 47, 2, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 255, 255, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 52, 254, 255, 255, 185, 0, 42, 252, 255, 255, 251, 42, 0, 0, 0, 0, 0, 0, 0, 179, 255, 255, 252, 61, 254, 255, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 202, 255, 255, 255, 255, 255, 255, 212, 89, 3, 0, 0, 0, 0, 240, 255, 255, 160, 0, 0, 0, 203, 255, 255, 255, 255, 177, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 39, 239, 255, 255, 247, 66, 0, 0, 8, 250, 255, 255, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 255, 255, 252, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 247, 11, 0, 0, 0, 0, 250, 255, 255, 255, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 34, 95, 176, 252, 255, 255, 255, 225, 8, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 39, 251, 255, 255, 255, 168, 45, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 255, 255, 255, 15, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 67, 0, 0, 149, 255, 255, 255, 187, 0, 0, 0, 0, 0, 0, 36, 253, 255, 255, 175, 0, 182, 255, 255, 238, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 124, 255, 255, 255, 255, 217, 88, 2, 0, 0, 0, 0, 0, 0, 244, 255, 255, 166, 0, 0, 0, 118, 251, 208, 143, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 64, 249, 255, 255, 246, 69, 0, 60, 255, 255, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 17, 240, 255, 255, 255, 172, 0, 0, 0, 0, 0, 207, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 16, 16, 16, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 248, 255, 255, 255, 88, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 150, 255, 255, 255, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 255, 255, 255, 238, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 255, 255, 255, 191, 0, 0, 0, 19, 238, 255, 255, 255, 80, 0, 0, 0, 0, 0, 168, 255, 255, 255, 59, 0, 64, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 8, 222, 255, 228, 101, 3, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 173, 0, 0, 0, 7, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 88, 254, 255, 255, 249, 96, 134, 255, 255, 254, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 254, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 255, 255, 255, 64, 0, 0, 0, 0, 0, 141, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 255, 66, 0, 0, 0, 0, 0, 125, 128, 128, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 255, 255, 142, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 212, 255, 255, 255, 81, 0, 0, 0, 0, 0, 0, 0, 0, 12, 226, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 255, 255, 255, 182, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 228, 255, 255, 255, 55, 0, 0, 0, 0, 111, 255, 255, 255, 221, 7, 0, 0, 0, 69, 255, 255, 255, 189, 0, 0, 0, 189, 255, 255, 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 74, 121, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 255, 255, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 255, 255, 255, 255, 244, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 1, 145, 255, 255, 255, 255, 185, 0, 0, 0, 0, 0, 0, 50, 255, 255, 255, 247, 38, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 255, 249, 14, 0, 0, 0, 0, 0, 232, 255, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 255, 151, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 233, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 2, 169, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 253, 56, 0, 0, 0, 0, 0, 0, 0, 19, 238, 255, 255, 255, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 170, 255, 255, 255, 174, 0, 0, 0, 0, 0, 4, 217, 255, 245, 114, 2, 0, 0, 20, 228, 255, 255, 254, 55, 0, 0, 0, 51, 253, 255, 255, 250, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 255, 255, 255, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 96, 0, 0, 0, 0, 2, 68, 202, 255, 255, 255, 255, 236, 31, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 215, 33, 0, 0, 0, 0, 0, 0, 20, 216, 255, 255, 255, 164, 0, 0, 0, 0, 0, 0, 182, 255, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 169, 255, 255, 255, 122, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 218, 255, 255, 255, 142, 0, 0, 0, 0, 0, 0, 23, 178, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 223, 29, 0, 0, 0, 0, 0, 4, 179, 255, 255, 255, 243, 15, 0, 0, 0, + 0, 0, 0, 0, 0, 96, 255, 255, 255, 247, 34, 0, 0, 0, 0, 0, 0, 80, 188, 34, 0, 0, 0, 5, 191, 255, 255, 255, 158, 0, 0, 0, 0, 0, 154, 255, 255, 255, 245, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 255, 255, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 232, 255, 255, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 176, 128, 131, 149, 174, 236, 255, 255, 255, 255, 255, 234, 55, 0, 0, 0, 0, 0, 0, 0, 0, 32, 236, 255, 255, 255, 240, 128, 41, 1, 0, 19, 95, 225, 255, 255, 255, 242, 33, 0, 0, 0, 0, 0, 0, 87, 255, 255, 255, 242, 120, 33, 0, 0, 0, 0, 23, 150, 255, 255, 255, 255, 45, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 160, 255, 255, 255, 253, 128, 23, 0, 16, 63, 155, 243, 255, 255, 255, 255, 255, 255, 24, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 233, 100, 15, 0, 10, 77, 204, 255, 255, 255, 255, 120, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 244, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 167, 255, 255, 255, 229, 19, 0, 0, 0, 0, 0, 15, 221, 255, 255, 255, 248, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 255, 255, 255, 199, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 255, 255, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 247, 255, 255, 255, 255, 246, 102, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 255, 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 206, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 242, 255, 255, 255, 255, 255, 252, 245, 255, 255, 255, 255, 255, 249, 79, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, 255, 255, 255, 230, 207, 196, 217, 253, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 47, 251, 255, 255, 255, 255, 255, 248, 255, 255, 255, 255, 255, 244, 220, 255, 255, 255, 62, 0, 0, 0, 0, 0, 192, 255, 255, 255, 195, 255, 255, 255, 255, 246, 255, 255, 255, 255, 255, 255, 180, 1, 0, 0, 0, 0, + 0, 0, 0, 28, 229, 255, 255, 255, 220, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 166, 255, 255, 255, 253, 65, 0, 0, 0, 0, 0, 0, 0, 41, 236, 255, 255, 255, 255, 158, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 247, 255, 255, 255, 255, 253, 172, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 144, 144, 144, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245, 255, 255, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 235, 255, 255, 255, 255, 255, 255, 255, 198, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 255, 255, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 103, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 228, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 169, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 164, 7, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 93, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 229, 55, 152, 255, 255, 255, 152, 0, 0, 0, 0, 0, 192, 255, 255, 255, 21, 216, 255, 255, 255, 255, 255, 255, 255, 255, 255, 161, 6, 0, 0, 0, 0, 0, + 0, 0, 7, 208, 255, 255, 255, 254, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 194, 255, 255, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 233, 255, 255, 255, 255, 226, 100, 4, 0, 0, 0, 0, 0, 0, 0, 0, 120, 254, 255, 255, 255, 255, 255, 255, 255, 255, 216, 130, 58, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 241, 255, 255, 255, 160, 199, 255, 255, 255, 255, 255, 201, 102, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 254, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 255, 255, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 248, 255, 255, 245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 250, 232, 202, 138, 71, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 105, 211, 255, 255, 255, 255, 255, 255, 230, 127, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 176, 248, 255, 255, 255, 255, 255, 255, 253, 187, 72, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 46, 179, 252, 255, 255, 255, 255, 255, 222, 123, 10, 0, 73, 255, 255, 255, 250, 48, 0, 0, 0, 0, 192, 255, 255, 255, 0, 11, 147, 243, 255, 255, 255, 255, 255, 202, 74, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 77, 225, 255, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 99, 238, 255, 255, 255, 255, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 216, 255, 255, 255, 255, 255, 223, 132, 31, 0, 0, 0, 0, 1, 144, 255, 255, 255, 219, 85, 70, 173, 255, 255, 255, 255, 255, 255, 244, 194, 143, 104, 66, 36, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 26, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 31, 179, 255, 255, 255, 255, 136, 0, 3, 122, 245, 255, 255, 255, 255, 255, 251, 199, 135, 81, 38, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 255, 255, 242, 0, 0, 0, 0, 0, 0, 119, 229, 229, 242, 255, 255, 255, 255, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 16, 16, 16, 16, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 76, 97, 105, 86, 43, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 50, 86, 101, 108, 96, 66, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 16, 16, 16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 73, 101, 104, 81, 41, 0, 0, 0, 0, 0, 15, 16, 16, 16, 8, 0, 0, 0, 0, 12, 16, 16, 16, 0, 0, 0, 9, 67, 95, 103, 80, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 14, 169, 222, 12, 0, 0, 0, 0, 0, 0, 0, 0, 2, 80, 207, 255, 255, 255, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 178, 255, 255, 255, 255, 255, 227, 21, 0, 0, 0, 4, 163, 255, 255, 255, 222, 24, 0, 0, 0, 54, 175, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 230, 217, 205, 198, 191, 186, 182, 178, 176, 176, 176, 180, 184, 190, 198, 209, 223, 240, 254, 230, 10, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 129, 0, 0, 0, 0, 0, 36, 157, 250, 255, 255, 255, 249, 109, 0, 0, 0, 0, 29, 163, 252, 255, 255, 255, 255, 255, 255, 255, 255, 251, 228, 202, 183, 173, 69, 0, 0, 0, 0, 0, 63, 255, 255, 255, 239, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 255, 255, 252, 0, 0, 0, 0, 0, 0, 27, 253, 255, 255, 255, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 251, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 114, 249, 255, 255, 253, 57, 0, 0, 0, 7, 175, 255, 255, 255, 246, 48, 0, 0, 0, 0, 0, 0, 33, 132, 219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 255, 255, 255, 137, 0, 0, 15, 95, 184, 252, 255, 255, 255, 255, 219, 51, 0, 0, 0, 0, 0, 0, 0, 34, 148, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 190, 1, 0, 0, 0, 0, 8, 217, 255, 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 255, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 205, 255, 255, 255, 255, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 255, 255, 255, 236, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 210, 255, 152, 0, 0, 0, 0, 14, 184, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 120, 184, 231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 244, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 255, 255, 255, 145, 0, 12, 194, 255, 255, 255, 255, 255, 249, 141, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 85, 163, 235, 255, 255, 255, 255, 255, 255, 255, 54, 0, 0, 0, 0, 0, 25, 141, 229, 255, 246, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 112, 112, 112, 112, 5, 0, 0, 0, 0, 0, 0, 150, 255, 252, 207, 126, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 193, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 124, 20, 0, 0, 0, 0, 0, 3, 149, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 66, 99, 127, 152, 175, 195, 214, 231, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 255, 255, 255, 153, 0, 0, 5, 168, 255, 255, 254, 172, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 39, 92, 135, 171, 201, 219, 199, 0, 0, 0, 0, 0, 0, 0, 0, 8, 118, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 52, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 17, 29, 39, 47, 54, 61, 66, 70, 74, 77, 78, 79, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 80, 80, 80, 49, 0, 0, 0, 8, 210, 143, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_setup_w = 384; +static const int g_txt_setup_h = 30; +static const unsigned char g_txt_setup[384*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 56, 14, 0, 0, 0, 0, 0, 0, 0, 82, 158, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 56, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 47, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 130, 42, 0, 0, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 56, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 3, 0, 0, 0, 0, 0, 0, 1, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 115, 14, 0, 0, 0, 29, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 115, 14, 0, 0, 0, 29, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 17, 0, 0, 0, 0, 0, 0, 0, 40, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 51, 103, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 89, 111, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 172, 255, 44, 0, 0, 0, 0, 0, 0, 0, 140, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 161, 4, 0, 0, 0, 0, 0, 0, 0, 4, 192, 192, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 72, 0, 0, 220, 255, 32, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 234, 15, 13, 183, 173, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 33, 27, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 156, 255, 107, 0, 0, 0, 0, 0, 0, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 235, 15, 0, 0, 116, 255, 196, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 222, 198, 14, 0, 0, 0, 0, 60, 120, 120, 120, 120, 120, 120, 120, 43, 0, 0, 0, 0, 0, 0, 0, 0, 87, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 128, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 235, 15, 0, 0, 116, 255, 196, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 250, 147, 0, 0, 0, 0, 0, 0, 159, 245, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 34, 58, 91, 124, 157, 198, 247, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 88, 186, 79, 0, 0, 0, 0, 0, 0, 19, 237, 250, 39, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 153, 255, 25, 0, 0, 36, 46, 40, 40, 40, 103, 255, 202, 40, 40, 40, 46, 58, 19, 0, 0, 91, 177, 170, 164, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 164, 171, 177, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178, 255, 165, 1, 0, 0, 0, 0, 0, 0, 0, 254, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 72, 147, 232, 255, 93, 0, 215, 255, 11, 26, 218, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 110, 0, 19, 224, 255, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, 0, 0, 0, 0, 149, 255, 100, 0, 0, 0, 0, 0, 0, 0, 240, 255, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 201, 255, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 124, 0, 0, 0, 198, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 177, 255, 197, 9, 0, 0, 0, 130, 255, 255, 255, 255, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 190, 168, 168, 184, 255, 179, 168, 168, 179, 255, 201, 168, 168, 168, 253, 221, 0, 0, 0, 0, 0, 0, 0, 0, 158, 255, 124, 0, 0, 0, 198, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 255, 101, 0, 0, 0, 0, 0, 0, 220, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 203, 211, 215, 222, 229, 241, 253, 255, 255, 255, 255, 255, 237, 208, 184, 161, 145, 132, 32, 0, 0, 0, 0, 0, 0, 175, 255, 156, 0, 0, 0, 0, 0, 0, 0, 117, 255, 167, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 160, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 104, 0, 0, 0, 5, 46, 36, 32, 32, 32, 32, 32, 39, 255, 221, 32, 32, 32, 32, 32, 33, 42, 46, 0, 0, 0, 0, 0, 0, 13, 208, 255, 130, 0, 0, 0, 0, 0, 0, 0, 247, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 88, 116, 158, 208, 252, 255, 255, 233, 175, 94, 0, 211, 255, 3, 58, 240, 254, 107, 0, 0, 0, 0, 0, 0, 0, 0, 8, 236, 230, 8, 0, 0, 54, 250, 252, 57, 0, 108, 166, 157, 152, 152, 152, 112, 0, 0, 0, 0, 5, 46, 36, 32, 32, 32, 32, 32, 39, 255, 221, 32, 32, 32, 32, 32, 33, 42, 46, 0, 0, 0, 0, 0, 0, 149, 144, 138, 132, 128, 128, 128, 128, 248, 252, 0, 0, 0, 0, 0, 142, 255, 93, 0, 0, 0, 0, 0, 0, 0, 225, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 20, 245, 229, 10, 0, 0, 40, 255, 210, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 188, 255, 177, 2, 0, 0, 135, 255, 73, 8, 8, 8, 155, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 64, 0, 0, 48, 255, 32, 0, 0, 32, 255, 96, 0, 0, 0, 248, 211, 0, 0, 0, 0, 0, 0, 0, 20, 245, 229, 10, 0, 0, 40, 255, 210, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 252, 20, 0, 0, 0, 0, 0, 34, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 200, 183, 170, 157, 147, 137, 239, 246, 99, 35, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 252, 232, 15, 0, 0, 38, 45, 37, 32, 32, 46, 154, 48, 32, 32, 33, 41, 49, 17, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 67, 95, 89, 88, 88, 88, 88, 88, 100, 113, 88, 92, 103, 34, 0, 0, 31, 58, 51, 45, 40, 40, 40, 40, 40, 188, 255, 100, 40, 40, 40, 40, 40, 40, 43, 47, 56, 25, 0, 0, 0, 24, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0, 27, 228, 253, 42, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 255, 255, 218, 210, 255, 102, 0, 0, 0, 0, 207, 255, 0, 0, 48, 236, 255, 110, 0, 0, 0, 0, 0, 0, 0, 98, 255, 116, 0, 0, 0, 0, 125, 255, 150, 0, 160, 255, 255, 255, 255, 255, 181, 0, 0, 0, 0, 24, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 245, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 104, 0, 0, 0, 150, 255, 225, 192, 192, 192, 192, 192, 192, 192, 192, 192, 34, 0, 0, 0, 0, 0, 17, 220, 248, 58, 0, 0, 161, 255, 55, 0, 0, 0, 152, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 145, 255, 136, 96, 96, 126, 255, 116, 96, 96, 116, 255, 156, 96, 96, 96, 251, 211, 0, 0, 0, 0, 0, 0, 0, 134, 255, 104, 0, 0, 0, 150, 255, 225, 192, 192, 192, 192, 192, 192, 192, 192, 192, 34, 0, 0, 0, 0, 0, 0, 235, 195, 0, 0, 0, 0, 0, 0, 119, 255, 140, 48, 48, 48, 48, 48, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 254, 134, 0, 0, 0, 7, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 255, 94, 0, 0, 0, 184, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, + 0, 216, 230, 212, 236, 255, 214, 218, 99, 0, 0, 22, 145, 21, 0, 0, 0, 75, 246, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 151, 129, 121, 120, 120, 120, 120, 124, 255, 234, 120, 120, 120, 120, 120, 122, 141, 144, 0, 0, 0, 0, 0, 0, 0, 0, 48, 75, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 72, 18, 0, 112, 255, 88, 0, 0, 0, 0, 204, 255, 0, 0, 0, 64, 250, 177, 2, 0, 0, 0, 0, 0, 3, 218, 255, 28, 0, 3, 40, 38, 6, 98, 0, 0, 39, 42, 30, 24, 53, 255, 173, 0, 0, 0, 0, 15, 151, 129, 121, 120, 120, 120, 120, 124, 255, 234, 120, 120, 120, 120, 120, 122, 141, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 241, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 247, 255, 40, 0, 0, 0, 0, 0, 0, 0, 25, 241, 255, 5, 0, 0, 31, 249, 246, 216, 216, 216, 216, 216, 216, 216, 226, 255, 220, 0, 0, 0, 0, 0, 0, 0, 44, 60, 0, 0, 0, 195, 255, 24, 0, 0, 0, 152, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 131, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 188, 0, 0, 0, 0, 0, 0, 25, 241, 255, 5, 0, 0, 31, 249, 246, 216, 216, 216, 216, 216, 216, 216, 226, 255, 220, 0, 0, 0, 0, 153, 208, 208, 255, 236, 208, 208, 208, 158, 0, 0, 209, 255, 255, 255, 255, 255, 255, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 250, 232, 70, 0, 0, 0, 31, 203, 205, 59, 0, 0, 0, 0, 0, 0, 0, 31, 248, 198, 1, 0, 157, 85, 85, 107, 100, 96, 151, 193, 96, 96, 96, 96, 97, 105, 114, 37, 0, + 0, 192, 212, 202, 233, 255, 207, 211, 96, 0, 0, 150, 255, 156, 0, 0, 0, 135, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 160, 9, 0, 0, 8, 255, 216, 0, 0, 0, 79, 188, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 202, 255, 0, 0, 0, 0, 60, 3, 0, 0, 0, 0, 0, 0, 99, 255, 255, 24, 0, 14, 255, 235, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 169, 0, 0, 0, 0, 0, 0, 0, 30, 160, 9, 0, 0, 8, 255, 216, 0, 0, 0, 79, 188, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 248, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 35, 3, 0, 0, 0, 0, 0, 0, 174, 255, 255, 0, 0, 0, 158, 255, 120, 0, 0, 0, 0, 0, 0, 0, 125, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 250, 224, 0, 0, 0, 0, 152, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 186, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, 255, 0, 0, 0, 158, 255, 120, 0, 0, 0, 0, 0, 0, 0, 125, 255, 124, 0, 0, 0, 0, 180, 255, 207, 200, 200, 200, 205, 255, 186, 0, 61, 255, 204, 128, 128, 128, 128, 128, 236, 223, 0, 0, 0, 0, 0, 0, 0, 0, 5, 121, 253, 198, 25, 0, 0, 0, 81, 237, 255, 234, 120, 5, 0, 0, 0, 0, 0, 0, 180, 252, 48, 0, 49, 255, 254, 65, 0, 0, 0, 173, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 0, 19, 244, 253, 45, 0, 0, 214, 246, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 163, 0, 0, 8, 255, 216, 0, 0, 7, 222, 253, 51, 0, 0, 0, 0, 0, 0, 28, 61, 51, 48, 48, 31, 0, 0, 0, 0, 0, 0, 0, 240, 241, 24, 24, 24, 25, 30, 35, 0, 0, 0, 46, 84, 78, 74, 72, 152, 255, 135, 72, 72, 72, 72, 216, 255, 72, 72, 72, 72, 73, 77, 85, 46, 0, 0, 0, 12, 230, 255, 255, 24, 0, 5, 255, 226, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 123, 255, 163, 0, 0, 8, 255, 216, 0, 0, 7, 222, 253, 51, 0, 0, 0, 0, 0, 0, 0, 0, 151, 240, 240, 240, 240, 240, 240, 240, 255, 255, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 205, 74, 2, 0, 0, 0, 88, 255, 255, 255, 0, 0, 65, 253, 232, 11, 0, 0, 44, 48, 1, 0, 2, 225, 254, 30, 0, 0, 0, 41, 33, 25, 24, 24, 4, 0, 1, 170, 255, 125, 0, 0, 0, 0, 152, 255, 147, 39, 40, 55, 26, 0, 0, 0, 79, 136, 129, 122, 120, 120, 120, 120, 120, 232, 255, 138, 120, 120, 120, 120, 122, 127, 136, 40, 0, 0, 0, 0, 88, 255, 255, 255, 0, 0, 65, 253, 232, 11, 0, 0, 44, 48, 1, 0, 2, 225, 254, 30, 0, 0, 0, 0, 173, 255, 32, 0, 0, 0, 24, 255, 180, 0, 184, 255, 63, 0, 0, 0, 0, 0, 221, 219, 0, 0, 0, 0, 0, 0, 30, 146, 225, 255, 212, 78, 72, 72, 72, 156, 255, 240, 116, 9, 0, 0, 0, 0, 0, 0, 9, 149, 255, 179, 60, 68, 187, 255, 137, 0, 0, 0, 49, 252, 218, 18, 0, 0, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 45, 35, 16, 120, 98, 24, 16, 53, 255, 171, 16, 19, 38, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 200, 255, 76, 0, 8, 255, 216, 0, 0, 118, 255, 152, 0, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 216, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, 140, 255, 254, 255, 24, 0, 1, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 4, 200, 255, 76, 0, 8, 255, 216, 0, 0, 118, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 170, 144, 144, 144, 144, 144, 144, 144, 3, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 222, 255, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 255, 244, 14, 0, 0, 48, 244, 243, 225, 255, 0, 20, 229, 255, 99, 0, 0, 0, 229, 252, 0, 0, 64, 254, 189, 0, 0, 0, 0, 248, 255, 255, 255, 255, 41, 6, 155, 255, 207, 8, 0, 0, 0, 0, 134, 255, 255, 255, 255, 255, 86, 0, 0, 0, 144, 255, 250, 244, 240, 240, 240, 240, 240, 255, 251, 240, 240, 240, 240, 240, 243, 247, 255, 72, 0, 0, 0, 48, 244, 243, 225, 255, 0, 20, 229, 255, 99, 0, 0, 0, 229, 252, 0, 0, 64, 254, 189, 0, 0, 0, 0, 0, 170, 255, 32, 0, 0, 0, 24, 255, 177, 66, 255, 196, 0, 0, 0, 0, 0, 0, 226, 214, 0, 0, 0, 0, 0, 0, 14, 250, 255, 255, 255, 255, 255, 255, 255, 254, 160, 21, 0, 0, 0, 0, 0, 0, 0, 0, 11, 236, 255, 255, 255, 255, 255, 212, 6, 0, 0, 5, 206, 244, 41, 0, 0, 61, 227, 112, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 120, 179, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 253, 196, 0, 8, 255, 216, 0, 20, 240, 235, 18, 0, 0, 0, 0, 0, 0, 0, 41, 92, 84, 136, 255, 155, 0, 0, 0, 0, 0, 0, 0, 240, 246, 104, 104, 104, 105, 110, 102, 0, 0, 0, 38, 69, 62, 59, 57, 143, 255, 124, 56, 56, 56, 56, 187, 255, 81, 56, 56, 56, 57, 61, 70, 38, 0, 0, 56, 252, 218, 204, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 49, 253, 196, 0, 8, 255, 216, 0, 20, 240, 235, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 187, 255, 238, 182, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 192, 247, 255, 144, 0, 0, 0, 204, 255, 104, 200, 255, 0, 109, 244, 188, 0, 0, 0, 0, 220, 243, 0, 0, 0, 21, 33, 0, 0, 0, 0, 141, 135, 131, 216, 255, 31, 22, 172, 212, 24, 0, 0, 0, 0, 0, 16, 127, 151, 152, 152, 152, 57, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 29, 255, 171, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 204, 255, 104, 200, 255, 0, 109, 244, 188, 0, 0, 0, 0, 220, 243, 0, 0, 0, 21, 33, 0, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 199, 255, 59, 0, 0, 0, 0, 0, 0, 230, 207, 0, 0, 0, 0, 0, 0, 0, 97, 91, 61, 43, 57, 195, 255, 214, 62, 0, 0, 45, 150, 18, 0, 0, 0, 0, 0, 0, 96, 130, 91, 61, 216, 253, 55, 0, 0, 1, 157, 255, 90, 0, 0, 0, 41, 239, 253, 79, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 43, 128, 52, 106, 118, 112, 112, 141, 130, 112, 112, 112, 112, 112, 112, 119, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 24, 237, 255, 204, 21, 0, 0, 0, 0, 0, 0, 0, 18, 35, 27, 24, 24, 24, 101, 35, 24, 31, 255, 219, 24, 26, 99, 99, 24, 24, 24, 28, 37, 18, 0, 0, 0, 0, 0, 80, 255, 147, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 149, 255, 52, 0, 0, 0, 97, 69, 0, 0, 0, 5, 218, 255, 73, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 18, 35, 27, 24, 24, 24, 101, 35, 24, 31, 255, 219, 24, 26, 99, 99, 24, 24, 24, 28, 37, 18, 0, 0, 0, 0, 182, 255, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 47, 195, 230, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 222, 175, 17, 0, 0, 0, 23, 151, 1, 200, 255, 0, 0, 16, 24, 0, 0, 0, 0, 217, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 27, 0, 57, 122, 100, 97, 96, 96, 96, 96, 96, 96, 96, 96, 26, 0, 0, 0, 0, 0, 0, 0, 0, 123, 192, 192, 192, 192, 204, 255, 229, 192, 192, 192, 192, 192, 141, 0, 0, 0, 0, 0, 0, 23, 151, 1, 200, 255, 0, 0, 16, 24, 0, 0, 0, 0, 217, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 12, 106, 17, 142, 98, 0, 0, 0, 0, 237, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 104, 237, 246, 122, 5, 0, 0, 3, 167, 255, 222, 42, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 142, 0, 0, 44, 183, 255, 143, 0, 2, 18, 38, 62, 153, 255, 241, 36, 0, 0, + 0, 0, 0, 0, 159, 255, 247, 255, 76, 0, 0, 0, 0, 142, 248, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 44, 230, 255, 216, 27, 0, 0, 0, 0, 0, 0, 104, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 104, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 1, 0, 125, 255, 77, 0, 0, 3, 224, 255, 93, 0, 0, 0, 36, 131, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 104, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 104, 0, 0, 0, 0, 197, 255, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 160, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 5, 83, 2, 0, 216, 240, 0, 3, 111, 35, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 120, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 245, 13, 0, 0, 0, 0, 0, 0, 0, 0, 158, 249, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 238, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 5, 83, 2, 0, 216, 240, 0, 3, 111, 35, 0, 0, 0, 0, 0, 0, 168, 255, 151, 136, 136, 136, 147, 255, 176, 0, 0, 54, 254, 225, 6, 0, 0, 0, 242, 192, 0, 0, 0, 0, 0, 0, 0, 23, 111, 219, 255, 177, 33, 0, 0, 0, 0, 0, 0, 116, 253, 240, 66, 0, 0, 0, 0, 0, 0, 18, 233, 222, 10, 0, 0, 61, 255, 255, 237, 242, 254, 255, 255, 255, 255, 255, 255, 206, 7, 0, + 1, 50, 121, 204, 255, 255, 198, 81, 29, 53, 35, 32, 33, 231, 255, 84, 32, 32, 32, 32, 32, 38, 50, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 35, 225, 255, 220, 29, 0, 0, 0, 0, 0, 69, 153, 135, 128, 128, 128, 128, 138, 254, 247, 255, 254, 255, 209, 128, 128, 128, 128, 130, 138, 145, 61, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 121, 101, 172, 164, 0, 89, 255, 115, 0, 0, 100, 255, 197, 3, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 69, 153, 135, 128, 128, 128, 128, 138, 254, 247, 255, 254, 255, 209, 128, 128, 128, 128, 130, 138, 145, 61, 0, 0, 0, 0, 213, 255, 203, 200, 200, 200, 200, 200, 200, 200, 34, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 113, 255, 172, 0, 216, 240, 0, 83, 255, 199, 3, 0, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 15, 32, 212, 252, 44, 32, 32, 32, 32, 134, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 242, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 221, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 0, 113, 255, 172, 0, 216, 240, 0, 83, 255, 199, 3, 0, 0, 0, 0, 0, 168, 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 172, 255, 102, 0, 0, 0, 250, 185, 0, 0, 0, 0, 0, 98, 224, 254, 255, 255, 181, 122, 135, 147, 159, 171, 182, 193, 204, 215, 248, 255, 249, 87, 0, 0, 0, 0, 0, 167, 255, 68, 0, 0, 3, 10, 239, 235, 221, 255, 203, 122, 209, 255, 59, 23, 216, 176, 17, 0, + 5, 226, 255, 245, 226, 255, 24, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 32, 223, 255, 220, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 115, 255, 217, 206, 251, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 37, 85, 136, 211, 255, 255, 255, 251, 162, 0, 47, 255, 162, 0, 16, 229, 246, 36, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 255, 115, 255, 217, 206, 251, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162, 184, 184, 184, 184, 184, 184, 184, 252, 255, 16, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 97, 136, 130, 124, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 155, 255, 205, 120, 120, 123, 130, 122, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 21, 240, 254, 64, 0, 216, 240, 0, 0, 187, 255, 127, 0, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 0, 0, 115, 255, 88, 0, 0, 0, 1, 213, 255, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 21, 240, 254, 64, 0, 216, 240, 0, 0, 187, 255, 127, 0, 0, 0, 0, 0, 168, 255, 46, 16, 16, 16, 38, 255, 176, 0, 0, 0, 54, 255, 220, 2, 0, 2, 255, 177, 0, 0, 0, 0, 0, 59, 255, 255, 255, 244, 233, 223, 217, 219, 255, 235, 196, 183, 170, 159, 148, 158, 255, 254, 100, 0, 0, 18, 156, 255, 226, 147, 177, 212, 249, 40, 16, 0, 64, 255, 132, 0, 176, 255, 24, 0, 26, 1, 0, 0, + 0, 125, 148, 22, 152, 255, 24, 0, 43, 104, 97, 96, 237, 250, 107, 96, 96, 148, 255, 168, 96, 98, 108, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 35, 230, 255, 216, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 181, 9, 255, 216, 45, 245, 247, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 0, 0, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 247, 255, 255, 255, 255, 255, 169, 71, 12, 0, 0, 3, 241, 225, 1, 167, 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 181, 9, 255, 216, 45, 245, 247, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 255, 241, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 160, 255, 153, 0, 0, 216, 240, 0, 0, 33, 244, 251, 51, 0, 0, 0, 0, 0, 0, 176, 255, 24, 0, 0, 0, 17, 242, 207, 1, 0, 0, 88, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 241, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 219, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 0, 160, 255, 153, 0, 0, 216, 240, 0, 0, 33, 244, 251, 51, 0, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 203, 255, 79, 0, 12, 255, 166, 0, 0, 0, 0, 0, 1, 55, 25, 5, 0, 0, 0, 0, 48, 255, 152, 0, 0, 0, 0, 0, 0, 117, 167, 18, 0, 0, 51, 255, 255, 255, 255, 244, 212, 173, 0, 0, 0, 71, 255, 122, 0, 176, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 0, 85, 255, 127, 0, 0, 0, 174, 250, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 46, 237, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 219, 18, 8, 255, 216, 0, 78, 251, 255, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 0, 27, 61, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 255, 220, 149, 81, 127, 255, 88, 0, 0, 0, 0, 0, 174, 255, 178, 255, 186, 1, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 129, 255, 219, 18, 8, 255, 216, 0, 78, 251, 255, 133, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 255, 210, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 20, 25, 19, 14, 14, 24, 8, 8, 8, 8, 8, 8, 8, 8, 70, 255, 163, 8, 8, 12, 18, 24, 0, 0, 0, 0, 0, 200, 255, 0, 0, 105, 255, 221, 13, 0, 0, 216, 240, 0, 0, 0, 117, 255, 209, 5, 0, 0, 0, 0, 0, 176, 255, 24, 50, 69, 0, 0, 132, 255, 117, 0, 17, 230, 249, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 251, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 243, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 0, 105, 255, 221, 13, 0, 0, 216, 240, 0, 0, 0, 117, 255, 209, 5, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 105, 201, 56, 0, 22, 255, 152, 0, 0, 0, 0, 0, 0, 0, 7, 92, 1, 0, 0, 0, 48, 255, 152, 0, 0, 5, 115, 6, 0, 0, 0, 0, 0, 0, 0, 159, 121, 70, 27, 0, 0, 0, 0, 0, 0, 99, 255, 96, 0, 176, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 9, 224, 255, 154, 50, 0, 59, 254, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 8, 155, 255, 233, 36, 0, 8, 255, 216, 0, 0, 86, 249, 255, 207, 64, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 21, 214, 212, 3, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 39, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 0, 76, 255, 255, 217, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 0, 8, 155, 255, 233, 36, 0, 8, 255, 216, 0, 0, 86, 249, 255, 207, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 177, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 15, 183, 186, 3, 0, 0, 0, 0, 0, 0, 0, 64, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 61, 250, 251, 57, 0, 0, 0, 216, 240, 0, 0, 0, 6, 220, 255, 124, 0, 0, 0, 0, 0, 176, 255, 60, 229, 194, 0, 0, 10, 223, 252, 90, 186, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 251, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 244, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 61, 250, 251, 57, 0, 0, 0, 216, 240, 0, 0, 0, 6, 220, 255, 124, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 5, 1, 0, 0, 36, 255, 137, 0, 0, 0, 0, 0, 0, 0, 162, 255, 187, 10, 0, 0, 48, 255, 152, 0, 0, 160, 255, 205, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 160, 255, 48, 0, 176, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 2, 78, 187, 255, 255, 194, 214, 237, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 211, 255, 227, 40, 0, 0, 8, 255, 216, 0, 0, 0, 58, 232, 255, 255, 182, 81, 9, 0, 0, 0, 0, 0, 80, 255, 171, 213, 255, 138, 1, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 0, 0, 19, 178, 255, 255, 92, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 200, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 0, 0, 58, 211, 255, 227, 40, 0, 0, 8, 255, 216, 0, 0, 0, 58, 232, 255, 255, 182, 81, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 144, 0, 0, 0, 0, 0, 136, 255, 88, 0, 0, 0, 0, 0, 0, 0, 10, 213, 255, 133, 0, 0, 0, 0, 0, 0, 0, 64, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 97, 251, 111, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 81, 231, 83, 0, 0, 0, 0, 0, 177, 255, 238, 255, 167, 2, 0, 0, 56, 248, 255, 255, 188, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 245, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 229, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 255, 0, 97, 251, 111, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 81, 231, 83, 0, 0, 0, 168, 255, 32, 0, 0, 0, 24, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 55, 255, 122, 0, 0, 0, 0, 0, 0, 148, 255, 222, 52, 0, 0, 0, 48, 255, 152, 0, 0, 29, 192, 255, 244, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 108, 190, 140, 0, 26, 247, 230, 2, 0, 176, 255, 24, 0, 0, 5, 0, 0, + 0, 0, 0, 0, 152, 255, 24, 0, 0, 0, 0, 0, 0, 41, 191, 255, 255, 236, 113, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 168, 254, 255, 199, 24, 0, 0, 0, 10, 255, 216, 0, 0, 0, 0, 29, 194, 255, 255, 255, 140, 0, 0, 0, 0, 0, 88, 255, 255, 255, 119, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 88, 0, 0, 1, 90, 232, 255, 213, 251, 231, 33, 0, 0, 235, 175, 10, 0, 0, 0, 0, 201, 255, 24, 0, 0, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 0, 0, 0, 44, 168, 254, 255, 199, 24, 0, 0, 0, 10, 255, 216, 0, 0, 0, 0, 29, 194, 255, 255, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 108, 0, 0, 0, 0, 0, 138, 255, 91, 0, 0, 0, 0, 0, 0, 0, 0, 44, 247, 252, 56, 0, 0, 0, 0, 0, 0, 64, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 0, 0, 42, 0, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 205, 255, 255, 157, 2, 0, 0, 0, 41, 222, 255, 255, 140, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 254, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 0, 0, 42, 0, 0, 0, 0, 0, 216, 240, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 170, 255, 249, 248, 248, 248, 248, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 108, 0, 0, 0, 0, 7, 165, 255, 202, 22, 0, 0, 0, 0, 48, 255, 152, 0, 0, 0, 3, 137, 255, 255, 142, 1, 0, 0, 28, 73, 120, 173, 229, 255, 255, 244, 110, 0, 170, 255, 128, 0, 0, 176, 255, 24, 0, 6, 218, 106, 1, + 0, 0, 0, 0, 190, 255, 22, 0, 0, 0, 0, 0, 50, 159, 253, 242, 151, 242, 255, 233, 117, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 238, 254, 150, 7, 0, 0, 0, 0, 17, 255, 220, 0, 0, 0, 0, 0, 1, 93, 218, 191, 1, 0, 0, 0, 0, 0, 153, 255, 255, 116, 34, 83, 69, 54, 48, 48, 243, 243, 48, 48, 48, 50, 56, 70, 60, 0, 0, 0, 0, 0, 0, 0, 156, 255, 85, 7, 88, 207, 255, 251, 133, 5, 114, 255, 225, 52, 40, 255, 227, 1, 0, 0, 0, 0, 204, 255, 27, 0, 1, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 57, 255, 168, 0, 0, 0, 61, 238, 254, 150, 7, 0, 0, 0, 0, 17, 255, 220, 0, 0, 0, 0, 0, 1, 93, 218, 191, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 229, 255, 68, 0, 0, 0, 0, 0, 141, 255, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 164, 0, 0, 0, 0, 0, 0, 112, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 3, 0, 0, 0, 0, 0, 0, 23, 243, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 253, 255, 162, 1, 0, 0, 20, 139, 247, 251, 157, 221, 255, 229, 130, 42, 1, 0, 0, 0, 0, 0, 0, 0, 0, 144, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 255, 3, 0, 0, 0, 0, 0, 0, 23, 243, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 172, 160, 160, 160, 169, 255, 183, 0, 0, 0, 0, 0, 0, 0, 15, 186, 255, 83, 0, 0, 0, 43, 208, 255, 186, 12, 0, 0, 0, 0, 0, 95, 255, 148, 0, 0, 0, 0, 0, 99, 253, 252, 59, 0, 0, 169, 255, 255, 255, 206, 136, 65, 6, 0, 126, 255, 221, 12, 0, 0, 176, 255, 35, 0, 67, 255, 210, 0, + 0, 28, 206, 207, 255, 243, 1, 0, 47, 120, 169, 222, 255, 255, 179, 35, 0, 21, 151, 252, 255, 232, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 67, 0, 0, 0, 0, 0, 0, 32, 255, 227, 0, 0, 0, 0, 0, 0, 0, 2, 22, 0, 0, 0, 0, 0, 1, 192, 255, 133, 0, 96, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 0, 58, 190, 218, 255, 255, 49, 60, 245, 255, 204, 50, 0, 0, 0, 135, 255, 253, 240, 255, 131, 0, 0, 0, 0, 0, 207, 255, 30, 0, 4, 255, 224, 0, 0, 0, 0, 0, 0, 171, 201, 241, 255, 157, 0, 0, 0, 0, 61, 67, 0, 0, 0, 0, 0, 0, 32, 255, 227, 0, 0, 0, 0, 0, 0, 0, 2, 22, 0, 0, 0, 0, 0, 0, 0, 125, 225, 225, 233, 254, 255, 238, 15, 0, 0, 0, 0, 0, 148, 255, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 104, 2, 0, 0, 0, 72, 212, 231, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 255, 8, 0, 0, 0, 0, 131, 220, 250, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 199, 197, 7, 17, 78, 167, 248, 255, 180, 42, 0, 7, 115, 228, 255, 255, 237, 194, 145, 0, 0, 115, 225, 219, 213, 235, 253, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 248, 239, 209, 213, 221, 135, 0, 0, 0, 0, 0, 208, 255, 8, 0, 0, 0, 0, 131, 220, 250, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 255, 32, 0, 0, 0, 24, 255, 190, 0, 0, 0, 0, 123, 203, 216, 249, 255, 230, 14, 0, 0, 0, 16, 173, 168, 6, 0, 0, 0, 35, 232, 244, 255, 255, 116, 0, 0, 0, 0, 0, 0, 91, 90, 0, 0, 0, 38, 197, 107, 28, 0, 0, 0, 6, 146, 255, 241, 48, 0, 0, 0, 158, 255, 207, 160, 225, 255, 106, 0, + 0, 0, 195, 255, 241, 94, 0, 0, 48, 255, 255, 225, 143, 48, 0, 0, 0, 0, 0, 48, 202, 224, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 176, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 200, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 123, 1, 0, 38, 95, 85, 82, 80, 80, 80, 80, 80, 80, 80, 81, 84, 92, 69, 0, 0, 0, 0, 2, 219, 255, 226, 110, 0, 0, 93, 100, 2, 0, 0, 0, 0, 0, 94, 233, 255, 225, 17, 0, 0, 0, 0, 0, 215, 255, 38, 0, 12, 255, 224, 0, 0, 0, 0, 0, 0, 125, 255, 255, 223, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 200, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 255, 255, 255, 250, 192, 56, 0, 0, 0, 0, 0, 0, 156, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 242, 255, 228, 122, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 16, 0, 0, 0, 0, 49, 255, 253, 196, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 22, 8, 181, 255, 252, 174, 56, 0, 0, 0, 0, 0, 5, 84, 174, 245, 255, 82, 0, 0, 63, 122, 116, 109, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 107, 110, 118, 74, 0, 0, 0, 0, 0, 216, 255, 16, 0, 0, 0, 0, 49, 255, 253, 196, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 176, 22, 0, 0, 0, 12, 128, 98, 0, 0, 0, 0, 48, 255, 255, 229, 154, 32, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 189, 255, 223, 152, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 250, 238, 57, 0, 0, 0, 0, 47, 213, 247, 248, 245, 163, 4, 0, + 0, 0, 47, 52, 6, 0, 0, 0, 0, 79, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 54, 15, 0, 0, 0, 0, 0, 0, 117, 136, 24, 0, 9, 112, 98, 0, 0, 0, 0, 0, 0, 28, 85, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 80, 61, 35, 5, 0, 0, 0, 0, 0, 0, 0, 0, 10, 16, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 45, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 40, 3, 0, 0, 0, 0, 0, 63, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 101, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 58, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 40, 3, 0, 0, 0, 0, 0, 63, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 69, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_title_w = 240; +static const int g_txt_title_h = 30; +static const unsigned char g_txt_title[240*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 124, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 176, 77, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 104, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 152, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 255, 242, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 255, 100, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 48, 48, 48, 48, 48, 48, 48, 5, 0, 124, 255, 75, 0, 0, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 197, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 194, 255, 218, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 255, 107, 0, 0, 0, 3, 106, 98, 93, 89, 88, 54, 0, 96, 173, 165, 162, 190, 255, 196, 160, 160, 160, 160, 67, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 235, 255, 255, 255, 255, 255, 255, 255, 25, 0, 116, 255, 66, 0, 0, 0, 0, 138, 150, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 112, 255, 222, 245, 245, 111, 1, 0, 0, 0, 0, 0, 0, 0, 83, 255, 100, 0, 0, 0, 8, 255, 255, 255, 255, 255, 88, 0, 117, 214, 206, 203, 218, 255, 221, 200, 200, 219, 255, 97, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 128, 128, 122, 103, 54, 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, 88, 128, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 232, 104, 104, 104, 104, 203, 255, 18, 0, 112, 255, 64, 0, 0, 0, 86, 255, 248, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 225, 249, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 248, 245, 41, 43, 213, 255, 182, 15, 0, 78, 216, 124, 0, 0, 80, 255, 96, 0, 0, 0, 1, 34, 24, 19, 195, 231, 5, 0, 0, 0, 0, 0, 80, 255, 96, 0, 0, 88, 255, 91, 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, 0, 0, 0, 0, 248, 255, 255, 255, 255, 255, 223, 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, 176, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 216, 0, 0, 0, 0, 168, 255, 14, 0, 112, 255, 64, 0, 0, 59, 246, 248, 70, 0, 0, 0, 0, 0, 0, 0, 19, 48, 48, 48, 48, 48, 48, 48, 101, 61, 48, 48, 48, 48, 48, 48, 48, 31, 0, 0, + 0, 0, 0, 21, 220, 255, 97, 0, 0, 10, 180, 255, 210, 25, 86, 255, 136, 0, 0, 80, 255, 96, 0, 0, 0, 0, 0, 0, 32, 254, 128, 0, 99, 124, 116, 113, 112, 157, 255, 166, 112, 112, 161, 255, 162, 118, 89, 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, 0, 0, 0, 0, 0, 248, 255, 106, 79, 113, 207, 255, 254, 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, 176, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 216, 0, 165, 240, 32, 168, 255, 11, 0, 112, 255, 64, 0, 51, 240, 249, 75, 0, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 162, 0, 0, + 0, 0, 10, 201, 255, 152, 0, 0, 0, 0, 6, 185, 255, 179, 78, 255, 131, 0, 0, 80, 255, 96, 0, 0, 0, 0, 0, 0, 132, 250, 25, 0, 192, 255, 253, 250, 248, 250, 255, 251, 248, 248, 250, 255, 252, 253, 176, 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, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 2, 157, 255, 242, 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, 0, 0, 176, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 216, 0, 168, 255, 9, 168, 255, 8, 0, 112, 255, 64, 68, 241, 249, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 188, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 192, 255, 155, 0, 0, + 0, 22, 197, 255, 183, 4, 0, 0, 0, 0, 0, 12, 178, 22, 72, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 0, 0, 7, 231, 166, 0, 0, 9, 4, 0, 0, 0, 80, 255, 96, 0, 0, 88, 255, 89, 0, 7, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 10, 234, 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, 0, 176, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 216, 0, 168, 255, 3, 168, 255, 8, 0, 112, 255, 82, 238, 244, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 149, 0, 0, + 12, 221, 255, 220, 217, 208, 208, 208, 208, 208, 208, 166, 0, 0, 72, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 0, 0, 123, 255, 56, 0, 0, 0, 37, 78, 70, 66, 124, 255, 136, 64, 64, 130, 255, 93, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 0, 156, 255, 172, 0, 0, 0, 0, 0, 1, 26, 30, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 23, 23, 0, 0, 0, 0, 0, 0, 176, 255, 48, 0, 0, 22, 24, 0, 0, 0, 0, 0, 0, 0, 0, 1, 25, 30, 3, 0, 0, 0, 0, 0, 224, 255, 0, 2, 46, 53, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 216, 0, 168, 255, 7, 168, 255, 8, 0, 112, 255, 64, 75, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 147, 0, 0, + 0, 68, 197, 44, 255, 244, 224, 224, 224, 226, 255, 196, 0, 0, 72, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 0, 111, 254, 251, 176, 176, 161, 0, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 102, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 0, 108, 255, 214, 0, 0, 0, 6, 129, 233, 255, 255, 238, 128, 3, 0, 0, 0, 0, 20, 150, 246, 255, 255, 241, 136, 9, 0, 0, 0, 176, 255, 48, 84, 221, 255, 255, 229, 76, 0, 0, 0, 0, 18, 148, 245, 255, 255, 241, 139, 5, 0, 0, 0, 224, 255, 54, 222, 255, 255, 245, 123, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 216, 0, 168, 255, 8, 168, 255, 108, 133, 184, 255, 160, 128, 128, 129, 131, 133, 137, 143, 60, 0, 0, 0, 0, 0, 83, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 144, 0, 0, + 0, 0, 1, 20, 255, 160, 0, 0, 0, 15, 255, 190, 0, 0, 72, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 0, 128, 255, 233, 217, 255, 205, 0, 27, 55, 46, 43, 108, 255, 121, 40, 40, 40, 40, 17, 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, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 0, 84, 255, 233, 0, 0, 4, 188, 255, 208, 127, 134, 225, 255, 156, 0, 0, 0, 1, 203, 255, 195, 125, 125, 202, 255, 161, 0, 0, 0, 176, 255, 146, 254, 162, 98, 160, 255, 246, 30, 0, 0, 6, 200, 255, 214, 147, 154, 228, 255, 138, 0, 0, 0, 224, 255, 235, 208, 122, 144, 242, 255, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 216, 0, 169, 255, 7, 168, 255, 184, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 104, 0, 0, 0, 0, 0, 81, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 144, 0, 0, + 0, 0, 0, 16, 255, 160, 0, 0, 0, 15, 255, 185, 0, 0, 72, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 0, 20, 23, 0, 25, 255, 168, 0, 10, 4, 0, 0, 80, 255, 96, 0, 0, 0, 5, 3, 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, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 0, 75, 255, 244, 0, 0, 113, 255, 186, 4, 0, 0, 23, 237, 255, 39, 0, 0, 44, 255, 219, 3, 0, 0, 6, 225, 232, 1, 0, 0, 176, 255, 255, 101, 0, 0, 0, 173, 255, 101, 0, 0, 77, 255, 213, 7, 0, 0, 24, 248, 235, 0, 0, 0, 224, 255, 204, 7, 0, 0, 54, 255, 251, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 216, 0, 178, 252, 1, 168, 255, 39, 29, 126, 255, 82, 24, 181, 235, 25, 27, 31, 38, 18, 0, 0, 0, 0, 0, 91, 255, 217, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 220, 255, 145, 0, 0, + 0, 0, 0, 16, 255, 160, 0, 0, 0, 25, 255, 181, 0, 0, 72, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 0, 29, 49, 0, 65, 255, 126, 0, 232, 255, 253, 250, 250, 255, 251, 248, 250, 254, 255, 64, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 0, 83, 255, 233, 0, 0, 210, 255, 107, 56, 56, 56, 56, 189, 255, 108, 0, 0, 23, 255, 233, 18, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 180, 0, 0, 0, 0, 106, 255, 127, 0, 0, 15, 32, 18, 0, 0, 0, 47, 243, 255, 6, 0, 0, 224, 255, 78, 0, 0, 0, 0, 190, 255, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 216, 0, 188, 236, 0, 168, 255, 13, 0, 112, 255, 64, 0, 118, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 252, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 251, 255, 150, 0, 0, + 0, 0, 0, 16, 255, 160, 0, 0, 0, 55, 255, 169, 0, 0, 72, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 16, 247, 190, 0, 113, 255, 76, 0, 120, 124, 117, 115, 157, 255, 166, 113, 115, 119, 126, 33, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 0, 104, 255, 215, 0, 3, 253, 255, 255, 255, 255, 255, 255, 255, 255, 139, 0, 0, 0, 166, 255, 238, 173, 128, 73, 8, 0, 0, 0, 0, 176, 255, 87, 0, 0, 0, 0, 96, 255, 128, 0, 0, 0, 0, 62, 125, 171, 222, 255, 255, 255, 8, 0, 0, 224, 255, 24, 0, 0, 0, 0, 142, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 216, 0, 212, 218, 0, 168, 255, 17, 0, 112, 255, 64, 0, 60, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 157, 0, 0, + 0, 0, 0, 16, 255, 160, 0, 49, 89, 194, 255, 134, 0, 0, 74, 255, 128, 0, 0, 80, 255, 96, 0, 0, 0, 0, 163, 255, 60, 171, 252, 18, 0, 0, 0, 0, 0, 80, 255, 96, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 0, 149, 255, 169, 0, 10, 255, 255, 75, 72, 72, 72, 72, 72, 72, 42, 0, 0, 0, 5, 108, 190, 238, 255, 255, 236, 80, 0, 0, 0, 176, 255, 53, 0, 0, 0, 0, 96, 255, 128, 0, 0, 21, 201, 255, 252, 210, 163, 99, 224, 255, 8, 0, 0, 224, 255, 9, 0, 0, 0, 0, 128, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 216, 0, 241, 189, 0, 168, 255, 22, 0, 112, 255, 64, 0, 8, 249, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 96, 62, 0, 0, + 0, 0, 0, 16, 255, 160, 0, 130, 255, 255, 240, 38, 0, 0, 83, 255, 132, 0, 0, 80, 255, 96, 0, 0, 0, 0, 36, 249, 204, 239, 195, 0, 111, 191, 184, 177, 176, 201, 255, 206, 176, 176, 176, 180, 188, 92, 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, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 6, 227, 255, 102, 0, 0, 242, 255, 28, 0, 0, 0, 0, 3, 8, 5, 0, 0, 0, 0, 0, 0, 0, 32, 122, 254, 243, 20, 0, 0, 176, 255, 48, 0, 0, 0, 0, 96, 255, 128, 0, 0, 165, 255, 156, 13, 0, 0, 0, 216, 255, 8, 0, 0, 224, 255, 40, 0, 0, 0, 0, 155, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 236, 216, 36, 255, 149, 0, 131, 200, 22, 0, 112, 255, 64, 0, 0, 191, 247, 17, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 255, 160, 0, 37, 182, 122, 27, 0, 0, 0, 91, 255, 142, 0, 0, 80, 255, 96, 0, 0, 0, 0, 0, 125, 255, 255, 110, 0, 128, 220, 212, 208, 208, 223, 255, 226, 208, 208, 209, 212, 221, 107, 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, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 48, 0, 0, 0, 142, 255, 236, 12, 0, 0, 175, 255, 111, 0, 0, 0, 0, 143, 255, 146, 0, 0, 89, 192, 87, 0, 0, 0, 0, 186, 255, 68, 0, 0, 176, 255, 48, 0, 0, 0, 0, 96, 255, 128, 0, 0, 232, 255, 36, 0, 0, 0, 51, 249, 255, 8, 0, 0, 224, 255, 132, 0, 0, 0, 6, 231, 255, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 33, 102, 255, 92, 2, 0, 0, 0, 0, 112, 255, 64, 0, 0, 98, 255, 125, 0, 0, 0, 0, 0, 0, 0, 34, 255, 234, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 255, 160, 0, 0, 0, 0, 0, 7, 94, 5, 17, 48, 28, 0, 0, 80, 255, 96, 0, 0, 0, 0, 0, 25, 250, 255, 154, 6, 0, 0, 0, 0, 0, 83, 255, 98, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 100, 69, 103, 195, 255, 250, 74, 0, 0, 0, 53, 251, 246, 109, 18, 7, 90, 249, 252, 49, 0, 0, 67, 255, 212, 39, 0, 0, 48, 236, 255, 42, 0, 0, 176, 255, 48, 0, 0, 0, 0, 96, 255, 128, 0, 0, 223, 255, 130, 5, 20, 107, 240, 255, 255, 12, 0, 0, 224, 255, 248, 100, 10, 31, 171, 255, 219, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 207, 250, 41, 203, 67, 0, 0, 0, 112, 255, 64, 0, 0, 11, 231, 245, 48, 0, 0, 0, 0, 0, 0, 126, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 255, 162, 0, 0, 0, 0, 0, 50, 255, 207, 0, 0, 0, 0, 0, 80, 255, 96, 0, 0, 0, 0, 3, 188, 255, 209, 255, 208, 79, 1, 0, 0, 0, 89, 255, 105, 0, 0, 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, 170, 255, 249, 248, 248, 248, 248, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 255, 255, 209, 60, 0, 0, 0, 0, 0, 88, 246, 255, 255, 254, 255, 249, 99, 0, 0, 0, 0, 154, 255, 255, 237, 234, 255, 255, 146, 0, 0, 0, 176, 255, 48, 0, 0, 0, 0, 96, 255, 128, 0, 0, 98, 254, 255, 254, 255, 255, 196, 218, 255, 60, 0, 0, 224, 255, 172, 255, 254, 255, 255, 232, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 166, 82, 254, 242, 47, 0, 0, 113, 255, 64, 98, 154, 0, 92, 255, 228, 29, 0, 0, 0, 0, 16, 235, 255, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 255, 220, 40, 24, 24, 24, 28, 164, 255, 144, 0, 0, 0, 0, 0, 113, 255, 94, 0, 0, 0, 4, 156, 255, 168, 0, 98, 241, 255, 228, 157, 100, 60, 82, 147, 61, 0, 0, 1, 14, 30, 47, 56, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 136, 136, 130, 108, 52, 1, 0, 0, 0, 0, 0, 0, 0, 27, 123, 171, 176, 133, 35, 0, 0, 0, 0, 0, 0, 61, 140, 174, 173, 141, 54, 0, 0, 0, 0, 93, 136, 25, 0, 0, 0, 0, 51, 136, 68, 0, 0, 0, 48, 144, 176, 158, 86, 2, 83, 136, 84, 0, 0, 119, 136, 2, 100, 168, 174, 122, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 243, 247, 34, 0, 109, 255, 229, 29, 0, 134, 255, 218, 255, 239, 8, 0, 151, 255, 233, 68, 0, 0, 0, 161, 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, 218, 255, 255, 255, 255, 255, 255, 255, 248, 38, 0, 0, 0, 166, 239, 255, 255, 69, 0, 0, 0, 178, 255, 208, 13, 0, 0, 24, 132, 222, 255, 255, 255, 255, 255, 251, 247, 244, 252, 255, 255, 255, 146, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 230, 255, 103, 0, 0, 0, 136, 255, 126, 0, 216, 255, 255, 173, 32, 0, 0, 2, 150, 255, 119, 0, 0, 109, 255, 251, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 115, 128, 128, 128, 128, 128, 115, 42, 0, 0, 0, 0, 83, 255, 238, 150, 3, 0, 0, 0, 68, 205, 24, 0, 0, 0, 0, 0, 0, 37, 86, 127, 153, 175, 191, 201, 206, 208, 209, 207, 203, 60, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 202, 255, 142, 0, 0, 0, 0, 1, 86, 0, 0, 100, 241, 83, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 116, 236, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 38, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_computer_w = 96; +static const int g_txt_computer_h = 30; +static const unsigned char g_txt_computer[96*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 56, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 142, 51, 0, 0, 0, 0, 0, 0, 0, 103, 128, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 32, 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, 63, 87, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 75, 168, 7, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 243, 250, 72, 0, 0, 0, 0, 0, 0, 13, 233, 253, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 255, 220, 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, 208, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 190, 255, 180, 6, 0, 0, 0, 0, 0, 0, 0, 76, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 195, 255, 231, 184, 184, 184, 185, 191, 183, 0, 145, 255, 239, 184, 184, 184, 184, 188, 198, 64, 0, 0, 0, 0, 0, 42, 255, 200, 0, 0, 0, 0, 62, 232, 232, 232, 232, 232, 232, 232, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 211, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 201, 255, 174, 4, 0, 0, 0, 0, 0, 0, 73, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 207, 176, 230, 255, 200, 180, 185, 177, 81, 254, 218, 176, 182, 255, 234, 179, 182, 191, 62, 0, 0, 0, 0, 0, 34, 255, 193, 0, 0, 0, 0, 62, 255, 223, 176, 176, 176, 243, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 233, 255, 207, 176, 176, 176, 176, 176, 176, 176, 176, 176, 143, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 219, 255, 168, 3, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 227, 16, 0, 133, 255, 127, 0, 0, 59, 245, 247, 36, 0, 0, 228, 246, 24, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 192, 0, 0, 0, 0, 54, 255, 150, 0, 0, 0, 216, 255, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 183, 255, 220, 208, 208, 208, 208, 208, 208, 208, 217, 255, 253, 60, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 42, 244, 148, 2, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 50, 161, 54, 0, 0, 65, 187, 101, 0, 0, 19, 117, 105, 0, 0, 0, 98, 91, 20, 0, 0, 0, 0, 0, 78, 125, 115, 130, 255, 220, 112, 115, 126, 16, 52, 255, 149, 0, 0, 0, 216, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 188, 4, 0, 0, 0, 0, 0, 0, 0, 159, 255, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 69, 0, 0, 0, 0, 0, 152, 255, 255, 255, 255, 255, 255, 255, 255, 32, 50, 255, 147, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 0, 2, 148, 255, 210, 15, 114, 47, 0, 0, 0, 0, 0, 99, 255, 187, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 255, 223, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 214, 255, 92, 0, 0, 0, 0, 0, 35, 44, 26, 144, 255, 196, 20, 25, 34, 4, 49, 255, 145, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 24, 190, 255, 221, 25, 75, 255, 245, 86, 0, 0, 0, 72, 250, 226, 21, 0, 0, 0, 0, 0, 0, + 0, 217, 212, 208, 208, 208, 134, 0, 135, 172, 163, 160, 160, 160, 187, 255, 208, 160, 160, 160, 161, 171, 140, 0, 0, 0, 0, 0, 61, 255, 190, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 172, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 207, 255, 192, 0, 0, 0, 0, 52, 255, 142, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 76, 235, 217, 29, 0, 0, 108, 248, 255, 134, 2, 79, 248, 240, 44, 0, 0, 0, 0, 0, 0, 0, + 0, 210, 207, 202, 213, 255, 159, 0, 192, 255, 252, 248, 248, 248, 250, 255, 252, 248, 248, 248, 249, 254, 200, 0, 0, 0, 0, 0, 58, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 82, 0, 0, 0, 0, 0, 0, 0, 23, 254, 255, 192, 4, 7, 0, 0, 54, 255, 136, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 0, 31, 18, 0, 0, 0, 0, 58, 234, 255, 206, 253, 234, 50, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 152, 0, 9, 6, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 0, 56, 255, 173, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 151, 255, 80, 0, 0, 0, 0, 0, 0, 0, 114, 255, 255, 209, 194, 147, 0, 0, 62, 255, 129, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 210, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 146, 0, 0, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 255, 239, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 235, 255, 81, 0, 0, 0, 0, 0, 0, 1, 219, 252, 255, 199, 215, 255, 75, 0, 72, 255, 120, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 119, 243, 255, 153, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 144, 0, 0, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 255, 212, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 200, 255, 84, 0, 0, 0, 0, 0, 0, 75, 255, 181, 255, 192, 59, 254, 232, 20, 96, 255, 102, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 118, 230, 255, 255, 195, 128, 128, 128, 128, 128, 128, 128, 128, 78, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 144, 0, 0, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 255, 192, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 175, 255, 89, 0, 0, 0, 0, 0, 1, 204, 255, 82, 255, 192, 0, 167, 156, 7, 125, 255, 76, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 0, 4, 75, 168, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 151, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 144, 0, 1, 11, 0, 0, 0, 0, 72, 255, 128, 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, 98, 0, 0, 0, 0, 0, 94, 255, 203, 32, 255, 192, 0, 8, 0, 0, 153, 255, 50, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 89, 177, 240, 255, 255, 236, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 143, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 144, 0, 129, 186, 4, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 8, 8, 161, 255, 55, 8, 8, 8, 8, 186, 255, 46, 8, 3, 0, 0, 0, 0, 17, 236, 255, 75, 32, 255, 192, 0, 0, 0, 0, 199, 250, 9, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 54, 253, 247, 164, 60, 88, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 144, 92, 254, 240, 40, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 80, 82, 75, 72, 72, 72, 72, 202, 255, 98, 72, 72, 72, 72, 204, 255, 100, 72, 72, 74, 80, 90, 5, 0, 128, 184, 0, 32, 255, 192, 0, 0, 0, 17, 252, 202, 0, 0, 0, 0, 216, 255, 8, 0, 0, 0, 0, 0, 0, 68, 16, 0, 0, 88, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, + 0, 0, 0, 0, 57, 255, 208, 251, 240, 55, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 15, 0, 32, 255, 192, 0, 0, 0, 118, 255, 137, 0, 0, 0, 0, 216, 255, 8, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, + 0, 0, 0, 0, 74, 255, 255, 241, 55, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 45, 42, 36, 32, 32, 32, 123, 255, 183, 32, 32, 32, 32, 32, 195, 255, 68, 32, 32, 35, 40, 51, 3, 0, 0, 0, 0, 34, 255, 193, 0, 0, 19, 233, 254, 43, 0, 0, 0, 0, 216, 255, 8, 0, 215, 204, 15, 0, 0, 0, 0, 0, 0, 88, 255, 143, 56, 56, 56, 56, 56, 56, 56, 56, 56, 124, 255, 136, 0, 0, 0, + 0, 0, 0, 0, 154, 255, 244, 58, 0, 0, 0, 0, 0, 0, 72, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 123, 250, 238, 36, 0, 0, 0, 0, 0, 192, 255, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 197, 0, 3, 175, 255, 173, 0, 0, 0, 0, 0, 217, 255, 36, 9, 245, 234, 0, 0, 0, 0, 0, 0, 0, 88, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 137, 0, 0, 0, + 0, 0, 0, 0, 62, 241, 65, 0, 0, 0, 0, 0, 0, 0, 78, 255, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 70, 138, 232, 255, 230, 49, 0, 0, 0, 0, 0, 0, 198, 255, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 255, 204, 0, 140, 255, 243, 30, 0, 0, 0, 0, 0, 187, 255, 242, 234, 255, 159, 0, 0, 0, 0, 0, 0, 0, 92, 255, 152, 72, 72, 72, 72, 72, 72, 72, 72, 72, 135, 255, 145, 0, 0, 0, + 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 192, 255, 241, 139, 17, 0, 0, 0, 0, 0, 0, 0, 204, 255, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 248, 205, 0, 18, 196, 90, 0, 0, 0, 0, 0, 0, 34, 148, 175, 176, 156, 24, 0, 0, 0, 0, 0, 0, 0, 100, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 153, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 16, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 93, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 88, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 112, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 88, 54, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_username_w = 72; +static const int g_txt_username_h = 30; +static const unsigned char g_txt_username[72*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 152, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 87, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 197, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 208, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 251, 240, 240, 240, 240, 240, 255, 255, 240, 240, 240, 240, 240, 245, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 255, 211, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 27, 255, 184, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 225, 249, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 233, 255, 207, 176, 176, 176, 176, 176, 176, 176, 176, 176, 143, 0, 0, 0, 0, + 0, 0, 0, 22, 255, 184, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 140, 0, 0, 0, 0, 0, 0, 0, 19, 48, 48, 48, 48, 48, 48, 48, 101, 61, 48, 48, 48, 48, 48, 48, 48, 31, 0, 0, 0, 0, 0, 0, 0, 1, 183, 255, 220, 208, 208, 208, 208, 208, 208, 208, 217, 255, 253, 60, 0, 0, 0, 0, + 0, 0, 0, 20, 255, 184, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 137, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 162, 0, 0, 0, 0, 0, 0, 0, 149, 255, 188, 4, 0, 0, 0, 0, 0, 0, 0, 159, 255, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 255, 186, 8, 8, 8, 8, 8, 240, 240, 8, 8, 8, 8, 8, 93, 255, 136, 0, 0, 0, 0, 0, 0, 0, 96, 255, 188, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 192, 255, 155, 0, 0, 0, 0, 0, 2, 148, 255, 210, 15, 114, 47, 0, 0, 0, 0, 0, 99, 255, 187, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 17, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 136, 0, 0, 0, 0, 0, 0, 0, 91, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 149, 0, 0, 0, 0, 24, 190, 255, 221, 25, 75, 255, 245, 86, 0, 0, 0, 72, 250, 226, 21, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 255, 211, 96, 96, 96, 96, 96, 246, 246, 96, 96, 96, 96, 96, 151, 255, 136, 0, 0, 0, 0, 0, 0, 0, 86, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 147, 0, 0, 0, 0, 76, 235, 217, 29, 0, 0, 108, 248, 255, 134, 2, 79, 248, 240, 44, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 18, 255, 184, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 0, 83, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 144, 0, 0, 0, 0, 0, 31, 18, 0, 0, 0, 0, 58, 234, 255, 206, 253, 234, 50, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 255, 184, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 0, 81, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 255, 255, 210, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 24, 255, 184, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 0, 91, 255, 217, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 220, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 119, 243, 255, 153, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 31, 255, 233, 176, 176, 176, 176, 176, 251, 251, 176, 176, 176, 176, 176, 203, 255, 136, 0, 0, 0, 0, 0, 0, 0, 109, 255, 252, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 251, 255, 150, 0, 0, 0, 0, 0, 0, 0, 0, 17, 118, 230, 255, 255, 195, 128, 128, 128, 128, 128, 128, 128, 128, 78, 0, 0, 0, + 0, 0, 0, 42, 255, 241, 208, 208, 208, 208, 208, 253, 253, 208, 208, 208, 208, 208, 224, 255, 136, 0, 0, 0, 0, 0, 0, 0, 127, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 157, 0, 0, 0, 0, 0, 4, 75, 168, 247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 151, 0, 0, 0, + 0, 0, 0, 64, 255, 157, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 0, 156, 255, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 96, 62, 0, 0, 0, 89, 177, 240, 255, 255, 236, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 143, 0, 0, 0, + 0, 0, 0, 101, 255, 119, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 0, 212, 255, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 253, 247, 164, 60, 88, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, + 0, 0, 0, 156, 255, 63, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 34, 255, 234, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 16, 0, 0, 88, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, + 0, 0, 1, 226, 246, 7, 0, 0, 0, 0, 0, 240, 240, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 126, 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, 0, 88, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, + 0, 0, 70, 255, 170, 0, 0, 0, 0, 0, 0, 243, 242, 0, 0, 0, 0, 0, 93, 255, 134, 0, 0, 0, 0, 0, 16, 235, 255, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 143, 56, 56, 56, 56, 56, 56, 56, 56, 56, 124, 255, 136, 0, 0, 0, + 0, 0, 184, 255, 77, 0, 0, 0, 0, 0, 0, 247, 246, 0, 0, 0, 89, 117, 204, 255, 109, 0, 0, 0, 0, 0, 161, 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, 88, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 137, 0, 0, 0, + 0, 79, 255, 221, 3, 0, 0, 0, 0, 0, 1, 255, 254, 1, 0, 0, 125, 255, 255, 229, 25, 0, 0, 0, 0, 109, 255, 251, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 152, 72, 72, 72, 72, 72, 72, 72, 72, 72, 135, 255, 145, 0, 0, 0, + 0, 41, 152, 100, 0, 0, 0, 0, 0, 0, 1, 56, 56, 1, 0, 0, 50, 142, 83, 10, 0, 0, 0, 0, 0, 116, 236, 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, 0, 0, 100, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 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, 0, 0, 0, 0, 0, 0, 23, 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, 47, 112, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 88, 54, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_password_w = 48; +static const int g_txt_password_h = 30; +static const unsigned char g_txt_password[48*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 74, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 238, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 98, 84, 72, 72, 72, 72, 72, 72, 43, 0, 0, 0, + 0, 0, 29, 40, 40, 40, 40, 40, 40, 40, 52, 248, 255, 98, 40, 40, 40, 40, 40, 40, 40, 40, 8, 0, 0, 156, 218, 210, 208, 208, 208, 209, 217, 225, 50, 240, 255, 255, 255, 255, 255, 255, 255, 255, 124, 0, 0, 0, + 0, 0, 185, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 45, 0, 0, 119, 168, 161, 190, 255, 196, 152, 156, 167, 38, 49, 46, 38, 32, 32, 32, 32, 120, 255, 85, 0, 0, 0, + 0, 0, 174, 255, 157, 112, 112, 112, 112, 112, 133, 162, 112, 112, 112, 112, 112, 112, 112, 112, 233, 255, 34, 0, 0, 0, 0, 0, 122, 255, 71, 0, 0, 0, 0, 0, 62, 100, 37, 0, 0, 0, 130, 255, 48, 0, 0, 0, + 0, 0, 171, 255, 80, 0, 0, 0, 0, 24, 228, 245, 70, 0, 0, 0, 6, 0, 0, 0, 216, 255, 40, 0, 0, 0, 0, 0, 168, 255, 19, 0, 0, 0, 0, 0, 135, 255, 106, 0, 0, 0, 161, 255, 14, 0, 0, 0, + 0, 0, 126, 176, 55, 0, 0, 0, 0, 0, 107, 255, 247, 67, 0, 88, 211, 31, 0, 0, 128, 152, 30, 0, 0, 0, 0, 2, 233, 211, 0, 0, 0, 0, 0, 0, 157, 255, 45, 0, 0, 0, 192, 236, 0, 0, 0, 0, + 0, 0, 0, 5, 163, 111, 10, 110, 192, 103, 0, 127, 213, 40, 62, 247, 255, 187, 1, 8, 59, 0, 0, 0, 0, 0, 0, 58, 255, 139, 0, 0, 0, 0, 0, 0, 178, 252, 5, 0, 0, 0, 223, 205, 0, 0, 0, 0, + 0, 0, 0, 74, 255, 224, 2, 136, 255, 127, 0, 0, 9, 94, 248, 253, 132, 5, 14, 219, 249, 54, 0, 0, 0, 0, 0, 139, 255, 159, 112, 112, 112, 82, 0, 0, 202, 225, 0, 0, 0, 2, 251, 174, 0, 0, 0, 0, + 0, 0, 0, 170, 255, 125, 0, 129, 255, 120, 0, 15, 155, 255, 224, 61, 0, 0, 0, 122, 255, 224, 15, 0, 0, 0, 8, 236, 255, 255, 255, 255, 255, 180, 0, 0, 234, 194, 0, 0, 0, 29, 255, 144, 0, 0, 0, 0, + 0, 0, 51, 254, 248, 22, 0, 128, 255, 122, 93, 229, 254, 152, 12, 0, 1, 129, 19, 0, 185, 255, 153, 0, 0, 0, 99, 255, 255, 76, 16, 16, 241, 171, 0, 11, 255, 173, 24, 24, 24, 77, 255, 130, 24, 24, 9, 0, + 0, 0, 7, 63, 90, 0, 0, 128, 255, 247, 255, 201, 59, 0, 0, 0, 62, 255, 247, 30, 31, 124, 11, 0, 0, 5, 215, 255, 255, 64, 0, 0, 240, 168, 0, 43, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 81, 0, + 0, 0, 0, 0, 0, 46, 131, 228, 255, 255, 230, 153, 152, 152, 152, 168, 244, 255, 173, 0, 0, 0, 0, 0, 0, 124, 255, 222, 255, 64, 0, 0, 240, 168, 0, 26, 104, 104, 104, 104, 104, 104, 104, 104, 193, 255, 54, 0, + 0, 85, 121, 166, 226, 255, 255, 235, 223, 255, 255, 255, 255, 255, 255, 255, 241, 178, 20, 0, 0, 0, 0, 0, 20, 247, 225, 120, 255, 64, 0, 0, 240, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 255, 33, 0, + 0, 121, 255, 255, 229, 150, 67, 3, 0, 2, 1, 26, 40, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 91, 112, 255, 64, 0, 0, 240, 168, 5, 2, 0, 0, 0, 0, 0, 0, 3, 9, 185, 255, 16, 0, + 0, 5, 120, 52, 28, 56, 31, 0, 0, 0, 0, 152, 255, 117, 0, 0, 0, 0, 54, 128, 78, 0, 0, 0, 0, 0, 0, 112, 255, 64, 0, 0, 240, 168, 104, 255, 250, 248, 248, 248, 248, 252, 255, 200, 205, 250, 0, 0, + 0, 0, 0, 0, 120, 255, 133, 0, 0, 0, 0, 130, 255, 107, 0, 0, 0, 0, 97, 255, 148, 0, 0, 0, 0, 0, 0, 112, 255, 208, 192, 192, 252, 169, 63, 148, 139, 136, 136, 136, 139, 143, 150, 122, 224, 228, 0, 0, + 0, 0, 0, 0, 109, 255, 122, 0, 0, 0, 0, 120, 255, 104, 0, 0, 0, 0, 89, 255, 138, 0, 0, 0, 0, 0, 0, 115, 255, 208, 192, 192, 252, 174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 246, 195, 0, 0, + 0, 0, 0, 0, 104, 255, 120, 0, 0, 0, 0, 120, 255, 104, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 119, 255, 64, 0, 0, 240, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 255, 157, 0, 0, + 0, 0, 0, 0, 104, 255, 120, 0, 0, 0, 0, 120, 255, 104, 0, 0, 0, 0, 88, 255, 136, 0, 0, 0, 0, 0, 0, 128, 255, 64, 0, 0, 105, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 105, 0, 0, + 0, 0, 0, 0, 115, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 136, 0, 0, 0, 0, 0, 0, 63, 120, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 180, 198, 251, 247, 24, 0, 0, + 0, 0, 0, 0, 62, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 172, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 255, 255, 227, 85, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 160, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 70, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_hint_w = 232; +static const int g_txt_hint_h = 22; +static const unsigned char g_txt_hint[232*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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 0, 0, 0, 0, 0, + 0, 0, 7, 192, 32, 0, 0, 0, 0, 143, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 74, 4, 0, 0, 0, 0, 0, 20, 176, 29, 0, 0, 0, 0, 0, 28, 156, 46, 0, 0, 0, 0, 6, 239, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 112, 112, 112, 54, 55, 112, 112, 112, 5, 0, 0, 57, 177, 0, 0, 20, 32, 32, 32, 32, 7, 0, 0, 166, 196, 0, 0, 0, 0, 0, + 0, 0, 1, 255, 34, 0, 0, 0, 0, 63, 253, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 232, 18, 0, 43, 39, 71, 56, 23, 255, 34, 77, 101, 0, 0, 0, 128, 233, 17, 0, 34, 105, 97, 96, 250, 127, 96, 100, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 136, 208, 118, 120, 208, 136, 255, 7, 76, 62, 69, 228, 0, 0, 157, 242, 224, 234, 245, 13, 0, 41, 249, 237, 40, 0, 0, 0, 0, + 0, 0, 0, 255, 32, 0, 79, 144, 144, 144, 190, 144, 144, 144, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 255, 255, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 220, 101, 0, 0, 168, 154, 135, 177, 16, 255, 32, 181, 156, 0, 0, 2, 226, 113, 1, 0, 49, 154, 146, 144, 249, 165, 144, 149, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 255, 255, 234, 166, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 255, 16, 160, 114, 116, 160, 16, 255, 3, 168, 135, 64, 224, 0, 0, 151, 144, 0, 132, 162, 0, 1, 188, 147, 106, 213, 19, 0, 0, 0, + 43, 166, 161, 255, 176, 75, 133, 196, 96, 96, 96, 96, 96, 166, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 113, 48, 48, 48, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 211, 3, 65, 38, 161, 146, 39, 250, 34, 255, 66, 252, 50, 0, 0, 85, 235, 10, 135, 169, 18, 41, 34, 32, 242, 74, 32, 32, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 74, 48, 73, 219, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 16, 160, 112, 113, 160, 16, 255, 1, 160, 128, 64, 224, 0, 0, 145, 144, 0, 215, 69, 0, 126, 219, 12, 0, 165, 213, 47, 0, 0, + 23, 87, 82, 255, 106, 40, 134, 160, 0, 117, 144, 0, 0, 112, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 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, 0, 0, 0, 0, 20, 220, 70, 23, 211, 177, 160, 144, 0, 92, 23, 255, 44, 85, 0, 0, 16, 223, 124, 1, 223, 86, 83, 218, 210, 208, 208, 208, 208, 239, 157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 32, 0, 0, 109, 251, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 16, 160, 112, 113, 160, 16, 255, 0, 160, 128, 64, 224, 0, 0, 144, 144, 63, 229, 2, 102, 251, 91, 48, 48, 51, 204, 250, 156, 29, + 0, 0, 0, 255, 32, 0, 53, 60, 0, 212, 125, 0, 0, 35, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 80, 0, 0, 0, 0, 0, 10, 80, 10, 45, 102, 76, 1, 0, 45, 80, 157, 223, 80, 80, 30, 0, 0, 0, 24, 86, 91, 26, 0, 0, 0, 30, 70, 0, 19, 88, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 248, 246, 235, 22, 160, 144, 93, 117, 121, 255, 130, 114, 122, 0, 88, 255, 230, 227, 200, 0, 0, 55, 109, 0, 75, 86, 2, 227, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 32, 0, 0, 139, 240, 0, 0, 0, 36, 96, 92, 28, 0, 0, 0, 0, 27, 90, 90, 26, 0, 0, 16, 255, 48, 0, 0, 67, 57, 0, 0, 0, 37, 95, 95, 31, 0, 0, 20, 80, 12, 81, 89, 20, 0, 0, 0, 0, 36, 96, 92, 28, 0, 0, 0, 0, 27, 90, 90, 26, 0, 0, 0, 0, 24, 86, 91, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 70, 255, 76, 184, 148, 148, 184, 76, 255, 63, 160, 128, 64, 224, 0, 0, 144, 144, 157, 157, 53, 251, 144, 231, 232, 250, 226, 192, 115, 217, 18, + 0, 0, 0, 255, 34, 35, 35, 34, 73, 247, 47, 32, 32, 32, 38, 8, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 128, 128, 128, 72, 0, 32, 255, 140, 216, 158, 242, 155, 0, 99, 176, 211, 241, 176, 176, 66, 0, 0, 80, 238, 181, 189, 241, 70, 0, 0, 96, 224, 82, 245, 240, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 32, 24, 233, 84, 0, 160, 144, 129, 166, 210, 255, 172, 162, 172, 0, 7, 64, 32, 227, 61, 0, 0, 56, 228, 172, 144, 171, 12, 127, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 128, 159, 245, 97, 0, 0, 100, 250, 192, 198, 247, 57, 0, 0, 87, 244, 202, 201, 245, 75, 0, 16, 255, 48, 0, 127, 228, 38, 0, 0, 95, 246, 177, 187, 246, 69, 0, 64, 255, 212, 197, 218, 234, 49, 0, 0, 100, 250, 192, 198, 247, 57, 0, 0, 87, 244, 202, 201, 245, 75, 0, 0, 80, 238, 181, 189, 241, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 199, 255, 195, 232, 220, 220, 231, 196, 255, 175, 160, 128, 64, 224, 0, 0, 144, 144, 47, 237, 31, 28, 0, 0, 64, 208, 0, 0, 0, 4, 0, + 0, 0, 19, 255, 223, 151, 177, 210, 246, 229, 208, 208, 245, 231, 218, 41, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 201, 176, 176, 176, 99, 0, 32, 255, 203, 11, 0, 102, 243, 0, 0, 0, 112, 208, 0, 0, 0, 0, 16, 238, 102, 0, 0, 156, 208, 0, 0, 96, 242, 216, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 179, 159, 0, 0, 160, 144, 0, 1, 213, 255, 66, 24, 0, 0, 0, 0, 139, 173, 0, 0, 75, 170, 39, 59, 144, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 186, 177, 210, 225, 79, 0, 0, 169, 115, 0, 0, 172, 156, 0, 24, 243, 118, 0, 0, 156, 211, 0, 16, 255, 48, 127, 225, 34, 0, 0, 0, 194, 146, 0, 0, 102, 99, 0, 64, 255, 125, 0, 3, 189, 191, 0, 0, 169, 115, 0, 0, 172, 156, 0, 24, 243, 118, 0, 0, 156, 211, 0, 16, 238, 102, 0, 0, 156, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 7, 160, 112, 114, 158, 16, 255, 0, 160, 128, 64, 224, 0, 0, 144, 144, 0, 154, 153, 48, 71, 65, 112, 220, 64, 65, 71, 24, 0, + 44, 169, 244, 255, 80, 0, 0, 41, 244, 34, 0, 11, 244, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 78, 0, 0, 64, 255, 0, 0, 0, 112, 208, 0, 0, 0, 0, 68, 255, 212, 208, 208, 227, 254, 5, 0, 96, 255, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 144, 217, 39, 59, 13, 160, 144, 0, 95, 225, 255, 174, 193, 4, 0, 0, 112, 253, 101, 96, 54, 21, 198, 192, 0, 156, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 32, 0, 0, 120, 250, 36, 0, 0, 48, 108, 162, 244, 176, 0, 83, 254, 9, 0, 0, 0, 0, 0, 16, 255, 171, 252, 182, 0, 0, 0, 0, 132, 235, 129, 77, 16, 0, 0, 64, 255, 22, 0, 0, 100, 246, 1, 0, 0, 48, 108, 162, 244, 176, 0, 83, 254, 9, 0, 0, 0, 0, 0, 68, 255, 212, 208, 208, 227, 254, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 251, 0, 160, 112, 116, 155, 16, 255, 0, 160, 128, 64, 224, 0, 0, 144, 144, 0, 70, 212, 128, 200, 193, 208, 244, 192, 194, 202, 64, 0, + 14, 150, 32, 255, 32, 0, 0, 184, 142, 0, 0, 106, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 37, 0, 0, 64, 255, 0, 0, 0, 112, 208, 0, 0, 0, 0, 81, 255, 58, 48, 48, 48, 48, 2, 0, 96, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 234, 255, 249, 223, 22, 160, 144, 32, 237, 81, 255, 42, 222, 123, 0, 0, 195, 246, 199, 167, 53, 34, 69, 101, 48, 186, 145, 48, 53, 59, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 32, 0, 0, 43, 255, 88, 3, 177, 225, 149, 99, 165, 176, 0, 97, 249, 2, 0, 0, 8, 32, 3, 16, 255, 218, 49, 238, 84, 0, 0, 0, 1, 81, 149, 208, 247, 87, 0, 64, 255, 10, 0, 0, 89, 253, 6, 3, 177, 225, 149, 99, 165, 176, 0, 97, 249, 2, 0, 0, 8, 32, 3, 81, 255, 58, 48, 48, 48, 48, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 238, 0, 160, 112, 125, 147, 16, 255, 0, 167, 134, 64, 224, 0, 0, 144, 144, 36, 154, 220, 0, 9, 0, 64, 208, 0, 4, 0, 0, 0, + 0, 0, 0, 255, 32, 0, 10, 165, 234, 135, 44, 230, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 32, 0, 0, 64, 255, 0, 0, 0, 105, 217, 0, 0, 0, 0, 37, 255, 66, 0, 0, 64, 172, 6, 0, 96, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 29, 2, 0, 0, 160, 152, 212, 165, 17, 255, 33, 78, 126, 0, 0, 19, 4, 0, 0, 0, 116, 201, 194, 194, 255, 207, 192, 195, 202, 13, 0, 0, 78, 96, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 32, 0, 0, 94, 255, 65, 61, 255, 36, 0, 0, 181, 176, 0, 50, 255, 57, 0, 0, 95, 250, 6, 16, 255, 56, 0, 107, 229, 14, 0, 0, 121, 38, 0, 0, 146, 208, 0, 64, 255, 68, 0, 0, 140, 223, 0, 61, 255, 36, 0, 0, 181, 176, 0, 50, 255, 57, 0, 0, 95, 250, 6, 37, 255, 66, 0, 0, 64, 172, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 201, 0, 160, 112, 150, 130, 16, 255, 0, 130, 105, 64, 224, 0, 0, 144, 144, 181, 244, 81, 1, 191, 124, 64, 208, 31, 224, 46, 0, 0, + 0, 0, 0, 255, 32, 0, 0, 0, 35, 175, 255, 229, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 168, 128, 128, 128, 128, 32, 32, 255, 32, 0, 0, 64, 255, 0, 0, 0, 47, 252, 112, 94, 127, 0, 0, 157, 228, 108, 100, 224, 162, 0, 0, 96, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 53, 31, 160, 144, 78, 22, 25, 255, 40, 0, 0, 0, 4, 34, 61, 95, 133, 99, 0, 0, 0, 135, 225, 36, 52, 0, 0, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 144, 130, 162, 246, 204, 4, 41, 254, 145, 93, 183, 249, 185, 0, 0, 167, 227, 106, 106, 230, 148, 0, 16, 255, 48, 0, 3, 208, 146, 0, 0, 182, 194, 92, 79, 208, 169, 0, 64, 255, 216, 86, 112, 246, 111, 0, 41, 254, 145, 93, 183, 249, 185, 0, 0, 167, 227, 106, 106, 230, 148, 0, 0, 157, 228, 108, 100, 224, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 149, 0, 160, 112, 185, 107, 16, 255, 0, 0, 0, 64, 224, 0, 0, 146, 144, 29, 15, 0, 111, 233, 27, 64, 208, 0, 141, 225, 29, 0, + 0, 0, 11, 255, 31, 0, 0, 35, 131, 238, 152, 185, 243, 126, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 176, 176, 176, 176, 176, 176, 44, 22, 176, 22, 0, 0, 44, 176, 0, 0, 0, 0, 81, 185, 202, 141, 0, 0, 2, 107, 183, 190, 117, 6, 0, 0, 66, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 195, 223, 252, 228, 61, 166, 144, 0, 0, 11, 96, 17, 0, 0, 0, 18, 249, 248, 193, 143, 67, 5, 59, 168, 242, 59, 98, 249, 172, 39, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 176, 176, 175, 155, 99, 8, 0, 0, 85, 187, 186, 112, 84, 165, 7, 0, 3, 112, 186, 185, 107, 3, 0, 11, 176, 33, 0, 0, 57, 173, 20, 0, 17, 133, 190, 196, 138, 16, 0, 64, 255, 76, 202, 201, 108, 0, 0, 0, 85, 187, 186, 112, 84, 165, 7, 0, 3, 112, 186, 185, 107, 3, 0, 0, 2, 107, 183, 190, 117, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 222, 73, 0, 167, 130, 245, 55, 17, 255, 0, 0, 0, 66, 224, 0, 0, 147, 144, 0, 0, 77, 248, 62, 0, 72, 207, 0, 2, 189, 212, 7, + 0, 98, 234, 244, 12, 112, 218, 252, 175, 54, 0, 0, 99, 240, 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, 12, 133, 80, 26, 0, 0, 172, 235, 208, 208, 208, 208, 208, 209, 217, 83, 0, 47, 8, 0, 0, 57, 245, 249, 157, 30, 0, 0, 39, 189, 243, 23, 0, 0, 0, 182, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 226, 3, 187, 255, 117, 183, 19, 209, 238, 0, 0, 146, 228, 195, 0, 0, 153, 144, 0, 0, 13, 73, 0, 146, 237, 176, 0, 0, 28, 46, 0, + 0, 23, 99, 29, 0, 34, 100, 22, 0, 0, 0, 0, 0, 27, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 48, 48, 48, 48, 48, 48, 50, 58, 23, 0, 0, 0, 0, 0, 0, 75, 23, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 77, 215, 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, 64, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 35, 28, 0, 0, 0, 90, 31, 0, 0, 71, 119, 27, 0, 0, 89, 81, 0, 0, 0, 0, 0, 75, 119, 23, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_prefs_title_w = 144; +static const int g_txt_prefs_title_h = 30; +static const unsigned char g_txt_prefs_title[144*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 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, 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, 147, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 53, 3, 0, 0, 0, 0, 0, 0, 0, 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, 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, 2, 200, 104, 17, 0, 0, 0, 0, 0, 158, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 182, 255, 98, 0, 0, 0, 0, 0, 0, 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, 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, 64, 255, 230, 16, 71, 88, 88, 88, 88, 150, 255, 168, 88, 88, 88, 88, 23, 0, 0, 0, 0, 0, 0, 0, 219, 246, 9, 0, 0, 0, 47, 74, 66, 64, 64, 64, 64, 64, 64, 64, 64, 8, 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, 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, 164, 255, 105, 0, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 60, 0, 0, 0, 0, 0, 0, 8, 251, 184, 0, 0, 0, 0, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 32, 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, 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, 27, 249, 233, 8, 0, 196, 226, 16, 16, 16, 16, 16, 16, 16, 16, 113, 255, 51, 0, 0, 0, 0, 0, 0, 56, 255, 125, 0, 0, 0, 0, 47, 74, 66, 64, 64, 64, 64, 69, 213, 255, 221, 15, 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, 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, 148, 255, 130, 0, 0, 190, 224, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 48, 0, 0, 0, 30, 50, 43, 129, 255, 100, 40, 40, 40, 34, 0, 0, 0, 0, 0, 0, 16, 186, 255, 184, 17, 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, 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, 44, 252, 252, 28, 0, 0, 188, 237, 104, 104, 104, 104, 104, 104, 104, 104, 165, 255, 52, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255, 192, 0, 0, 0, 0, 0, 48, 220, 255, 139, 2, 0, 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, 0, 0, 30, 146, 225, 255, 212, 78, 72, 72, 72, 156, 255, 240, 116, 9, 0, 0, 0, 0, 0, 0, 9, 149, 255, 179, 60, 68, 187, 255, 137, 0, 0, 0, 49, 252, 218, 18, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 190, 255, 232, 0, 0, 0, 205, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 0, 0, 0, 54, 97, 92, 248, 224, 88, 88, 130, 255, 145, 0, 0, 0, 0, 48, 249, 247, 92, 0, 0, 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, 0, 0, 0, 14, 250, 255, 255, 255, 255, 255, 255, 255, 254, 160, 21, 0, 0, 0, 0, 0, 0, 0, 0, 11, 236, 255, 255, 255, 255, 255, 212, 6, 0, 0, 5, 206, 244, 41, 0, 0, 61, 227, 112, 0, 0, 0, 0, 0, 0, 100, 255, 255, 232, 0, 0, 0, 224, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 145, 0, 0, 106, 255, 97, 0, 0, 0, 0, 35, 255, 184, 0, 0, 0, 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, 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, 48, 247, 232, 216, 232, 0, 0, 0, 242, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, 71, 0, 0, 158, 255, 41, 0, 0, 0, 0, 21, 255, 184, 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, 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, 5, 217, 255, 83, 200, 232, 0, 0, 13, 255, 243, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 84, 0, 0, 0, 0, 192, 242, 7, 0, 0, 223, 238, 15, 42, 35, 28, 24, 39, 255, 190, 24, 24, 25, 30, 40, 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, 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, 53, 144, 0, 200, 232, 0, 0, 58, 255, 255, 182, 144, 210, 255, 144, 147, 255, 196, 144, 175, 255, 93, 0, 0, 0, 31, 254, 164, 0, 0, 37, 255, 167, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 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, 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, 200, 232, 0, 0, 112, 255, 255, 88, 0, 152, 255, 0, 8, 255, 120, 0, 72, 255, 86, 0, 0, 0, 126, 255, 90, 0, 0, 124, 255, 86, 47, 147, 141, 135, 129, 136, 255, 220, 128, 128, 129, 134, 141, 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, 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, 200, 232, 0, 0, 189, 255, 255, 88, 0, 152, 255, 0, 8, 255, 120, 0, 72, 255, 80, 0, 0, 0, 145, 255, 237, 88, 3, 221, 239, 11, 0, 0, 0, 0, 0, 16, 255, 184, 0, 0, 0, 0, 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, 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, 200, 232, 0, 25, 251, 220, 255, 145, 88, 187, 255, 88, 93, 255, 166, 88, 135, 255, 80, 0, 0, 0, 0, 81, 233, 255, 202, 255, 139, 0, 0, 0, 0, 0, 0, 16, 255, 184, 0, 0, 0, 0, 0, 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, 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, 200, 232, 0, 129, 255, 129, 255, 250, 248, 252, 255, 248, 248, 255, 251, 248, 250, 255, 80, 0, 0, 0, 0, 0, 24, 187, 255, 255, 59, 0, 0, 0, 0, 0, 0, 16, 255, 184, 0, 0, 0, 0, 0, 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, 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, 200, 232, 14, 235, 188, 88, 255, 88, 0, 153, 255, 0, 10, 255, 121, 0, 72, 255, 80, 0, 0, 0, 0, 0, 0, 155, 255, 255, 228, 45, 0, 0, 0, 0, 0, 16, 255, 184, 0, 0, 0, 0, 0, 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, 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, 200, 232, 134, 255, 66, 88, 255, 88, 0, 155, 255, 2, 13, 255, 123, 0, 72, 255, 80, 0, 0, 0, 0, 0, 112, 255, 192, 121, 255, 239, 58, 0, 0, 0, 0, 16, 255, 184, 0, 0, 0, 0, 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, 7, 165, 255, 202, 22, 0, 0, 0, 0, 48, 255, 152, 0, 0, 0, 3, 137, 255, 255, 142, 1, 0, 0, 28, 73, 120, 173, 229, 255, 255, 244, 110, 0, 170, 255, 128, 0, 0, 176, 255, 24, 0, 6, 218, 106, 1, 0, 0, 0, 0, 202, 233, 57, 161, 0, 88, 255, 88, 0, 159, 255, 6, 24, 255, 129, 0, 72, 255, 80, 0, 0, 0, 0, 117, 255, 213, 17, 0, 113, 255, 244, 56, 0, 0, 0, 20, 255, 184, 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, 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, 0, 0, 0, 204, 235, 0, 0, 0, 88, 255, 88, 0, 168, 255, 15, 43, 255, 139, 0, 77, 255, 80, 0, 0, 20, 177, 255, 216, 24, 0, 0, 0, 144, 185, 10, 0, 0, 0, 91, 255, 177, 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, 16, 173, 168, 6, 0, 0, 0, 35, 232, 244, 255, 255, 116, 0, 0, 0, 0, 0, 0, 91, 90, 0, 0, 0, 38, 197, 107, 28, 0, 0, 0, 6, 146, 255, 241, 48, 0, 0, 0, 158, 255, 207, 160, 225, 255, 106, 0, 0, 0, 0, 0, 209, 241, 0, 0, 0, 88, 255, 88, 0, 16, 24, 2, 0, 0, 27, 144, 222, 255, 70, 0, 12, 222, 255, 196, 19, 0, 0, 0, 0, 2, 6, 0, 7, 216, 247, 255, 255, 126, 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, 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, 0, 0, 218, 249, 0, 0, 0, 88, 255, 88, 0, 0, 0, 0, 0, 0, 0, 209, 255, 208, 15, 0, 0, 64, 165, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 222, 134, 9, 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, 0, 0, 0, 0, 0, 30, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 39, 0, 0, 0, 22, 64, 22, 0, 0, 0, 0, 0, 0, 0, 56, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_prefs_language_w = 44; +static const int g_txt_prefs_language_h = 28; +static const unsigned char g_txt_prefs_language[44*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 108, 14, 0, 0, 30, 110, 102, 95, 92, 92, 92, 92, 92, 92, 92, 100, 109, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 123, 198, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 62, 255, 185, 3, 0, 62, 229, 222, 220, 217, 237, 255, 223, 217, 218, 220, 223, 230, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 127, 255, 149, 0, 0, 0, 0, 0, 0, 160, 255, 18, 0, 0, 0, 0, 0, 0, 0, 0, 20, 67, 59, 56, 56, 56, 56, 56, 105, 255, 198, 56, 56, 56, 56, 56, 57, 62, 70, 8, 0, + 0, 0, 0, 1, 179, 199, 9, 0, 0, 0, 0, 0, 197, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, 0, + 0, 0, 0, 0, 17, 9, 0, 0, 123, 208, 202, 195, 246, 237, 172, 172, 172, 172, 172, 7, 0, 0, 0, 17, 56, 46, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 46, 57, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 102, 166, 152, 182, 255, 195, 160, 160, 160, 240, 243, 0, 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, 104, 94, 88, 88, 58, 0, 0, 0, 0, 0, 129, 255, 58, 0, 0, 0, 241, 215, 0, 0, 0, 0, 0, 0, 0, 188, 206, 201, 200, 200, 200, 200, 200, 200, 200, 200, 202, 209, 135, 0, 0, 0, 0, + 56, 255, 255, 255, 255, 167, 0, 0, 0, 0, 0, 179, 252, 10, 0, 0, 24, 255, 187, 0, 0, 0, 0, 0, 0, 0, 129, 138, 133, 132, 132, 132, 132, 132, 132, 132, 132, 138, 152, 105, 0, 0, 0, 0, + 11, 45, 35, 38, 255, 161, 0, 2, 0, 0, 0, 228, 212, 0, 0, 0, 63, 255, 159, 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, 12, 255, 155, 0, 219, 247, 242, 239, 255, 251, 240, 240, 240, 245, 255, 249, 248, 255, 36, 0, 0, 0, 0, 121, 117, 110, 108, 108, 108, 108, 108, 108, 108, 108, 111, 119, 68, 0, 0, 0, 0, + 0, 0, 0, 12, 255, 152, 0, 93, 102, 94, 88, 88, 88, 88, 88, 88, 88, 88, 88, 90, 101, 15, 0, 0, 0, 0, 228, 229, 223, 220, 220, 220, 220, 220, 220, 220, 220, 224, 232, 128, 0, 0, 0, 0, + 0, 0, 0, 12, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 255, 152, 0, 0, 0, 120, 172, 172, 172, 172, 172, 172, 172, 172, 172, 39, 0, 0, 0, 0, 0, 0, 31, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 28, 0, 0, 0, 0, + 0, 0, 0, 12, 255, 152, 47, 67, 0, 168, 255, 200, 200, 200, 200, 200, 200, 226, 255, 49, 0, 0, 0, 0, 0, 0, 197, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 176, 0, 0, 0, 0, + 0, 0, 0, 12, 255, 164, 213, 227, 9, 163, 255, 0, 0, 0, 0, 0, 0, 120, 255, 45, 0, 0, 0, 0, 0, 0, 191, 242, 32, 32, 32, 32, 32, 32, 32, 32, 32, 53, 255, 170, 0, 0, 0, 0, + 0, 0, 0, 12, 255, 253, 255, 130, 0, 160, 255, 0, 0, 0, 0, 0, 0, 120, 255, 44, 0, 0, 0, 0, 0, 0, 186, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 164, 0, 0, 0, 0, + 0, 0, 0, 36, 255, 255, 154, 0, 0, 160, 255, 0, 0, 0, 0, 0, 0, 120, 255, 44, 0, 0, 0, 0, 0, 0, 181, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 161, 0, 0, 0, 0, + 0, 0, 0, 166, 255, 197, 5, 0, 0, 160, 255, 132, 132, 132, 132, 132, 132, 190, 255, 44, 0, 0, 0, 0, 0, 0, 186, 252, 200, 200, 200, 200, 200, 200, 200, 200, 200, 205, 255, 166, 0, 0, 0, 0, + 0, 0, 5, 160, 240, 30, 0, 0, 0, 166, 255, 220, 220, 220, 220, 220, 220, 236, 255, 50, 0, 0, 0, 0, 0, 0, 192, 249, 156, 156, 156, 156, 156, 156, 156, 156, 156, 165, 255, 171, 0, 0, 0, 0, + 0, 0, 0, 0, 46, 0, 0, 0, 0, 177, 255, 0, 0, 0, 0, 0, 0, 120, 255, 58, 0, 0, 0, 0, 0, 0, 194, 236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 252, 174, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 60, 0, 0, 0, 0, 0, 0, 18, 40, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_prefs_region_w = 44; +static const int g_txt_prefs_region_h = 28; +static const unsigned char g_txt_prefs_region[44*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 116, 76, 0, 0, 0, 0, 0, 0, 0, 121, 248, 67, 0, 0, 0, 0, 0, 0, 0, 0, 23, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 3, 0, + 0, 0, 0, 49, 255, 160, 0, 0, 0, 0, 0, 0, 0, 118, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, 0, + 0, 0, 0, 40, 255, 154, 0, 0, 0, 100, 164, 13, 0, 111, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 200, 248, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 94, 97, 106, 12, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 151, 255, 15, 0, 108, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 144, 255, 8, 0, 108, 255, 56, 0, 0, 43, 78, 0, 0, 0, 0, 196, 244, 0, 0, 0, 20, 7, 0, 0, 0, 0, 0, 0, 53, 159, 28, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 141, 255, 1, 0, 108, 255, 103, 129, 213, 255, 160, 0, 0, 0, 0, 196, 244, 0, 0, 6, 186, 207, 39, 0, 0, 0, 0, 0, 180, 255, 201, 8, 0, 0, 0, + 0, 170, 204, 207, 255, 233, 203, 211, 17, 140, 255, 0, 53, 182, 255, 255, 255, 208, 255, 154, 0, 0, 0, 0, 196, 244, 0, 0, 22, 181, 255, 244, 96, 0, 0, 0, 75, 255, 219, 17, 0, 0, 0, 0, + 0, 152, 181, 187, 255, 223, 179, 188, 15, 147, 255, 215, 255, 255, 255, 134, 30, 8, 255, 152, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 97, 243, 255, 146, 3, 21, 230, 251, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 109, 209, 255, 255, 201, 108, 128, 255, 56, 0, 8, 255, 152, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 36, 210, 255, 186, 192, 255, 123, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 100, 219, 206, 255, 0, 0, 108, 255, 56, 0, 8, 255, 152, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 11, 177, 255, 255, 200, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 5, 3, 140, 255, 0, 0, 108, 255, 56, 0, 7, 255, 151, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 0, 102, 255, 255, 247, 86, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 140, 255, 0, 0, 108, 255, 56, 0, 18, 255, 148, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 72, 250, 228, 148, 255, 254, 108, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 140, 255, 0, 0, 108, 255, 70, 92, 176, 255, 111, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 66, 245, 229, 37, 0, 104, 253, 255, 112, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 1, 12, 140, 255, 0, 0, 109, 255, 62, 227, 255, 193, 16, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 101, 248, 231, 39, 0, 0, 0, 92, 253, 254, 93, 0, 0, 0, + 0, 0, 0, 32, 255, 178, 125, 221, 77, 140, 255, 0, 0, 114, 255, 60, 76, 59, 0, 0, 0, 0, 0, 0, 196, 244, 0, 0, 39, 180, 255, 214, 33, 0, 0, 0, 0, 0, 104, 255, 239, 16, 0, 0, + 0, 15, 84, 169, 255, 255, 254, 195, 36, 140, 255, 0, 0, 104, 216, 59, 0, 0, 0, 95, 28, 0, 0, 0, 196, 244, 0, 24, 220, 255, 179, 15, 0, 0, 0, 0, 0, 0, 0, 145, 60, 0, 0, 0, + 0, 205, 255, 255, 200, 112, 28, 0, 0, 139, 255, 10, 0, 0, 0, 0, 0, 0, 3, 236, 254, 25, 0, 0, 196, 244, 0, 0, 18, 102, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 83, 150, 38, 0, 0, 0, 0, 0, 119, 255, 130, 45, 44, 44, 44, 44, 46, 127, 255, 195, 0, 0, 0, 199, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 241, 255, 255, 255, 255, 255, 255, 255, 255, 240, 58, 0, 0, 0, 206, 252, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 189, 194, 148, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 71, 76, 76, 76, 76, 76, 76, 65, 17, 0, 0, 0, 0, 136, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 166, 170, 129, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_prefs_timezone_w = 44; +static const int g_txt_prefs_timezone_h = 28; +static const unsigned char g_txt_prefs_timezone[44*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 248, 136, 0, 0, 0, 0, 0, 23, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 134, 0, 0, 0, 0, 0, 209, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, 0, + 0, 66, 188, 188, 188, 188, 188, 188, 127, 0, 0, 0, 0, 0, 0, 0, 66, 255, 128, 0, 0, 0, 0, 0, 200, 248, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 94, 97, 106, 12, 0, + 0, 88, 255, 217, 188, 188, 196, 255, 167, 0, 0, 0, 0, 0, 0, 0, 64, 255, 126, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 83, 255, 112, 0, 0, 32, 255, 162, 26, 54, 49, 45, 43, 41, 40, 91, 255, 145, 47, 54, 13, 0, 0, 196, 244, 0, 0, 0, 20, 7, 0, 0, 0, 0, 0, 0, 53, 159, 28, 0, 0, 0, 0, + 0, 78, 255, 112, 0, 0, 32, 255, 158, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 60, 0, 0, 196, 244, 0, 0, 6, 186, 207, 39, 0, 0, 0, 0, 0, 180, 255, 201, 8, 0, 0, 0, + 0, 76, 255, 112, 0, 0, 32, 255, 155, 44, 95, 90, 86, 84, 82, 81, 121, 255, 166, 88, 95, 23, 0, 0, 196, 244, 0, 0, 22, 181, 255, 244, 96, 0, 0, 0, 75, 255, 219, 17, 0, 0, 0, 0, + 0, 75, 255, 145, 60, 60, 84, 255, 153, 0, 0, 0, 0, 0, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 97, 243, 255, 146, 3, 21, 230, 251, 51, 0, 0, 0, 0, 0, + 0, 74, 255, 255, 255, 255, 255, 255, 152, 0, 0, 20, 26, 0, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 36, 210, 255, 186, 192, 255, 123, 0, 0, 0, 0, 0, 0, + 0, 72, 255, 145, 60, 60, 84, 255, 152, 0, 47, 244, 176, 0, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 11, 177, 255, 255, 200, 2, 0, 0, 0, 0, 0, 0, + 0, 74, 255, 112, 0, 0, 32, 255, 152, 0, 0, 193, 255, 63, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 0, 102, 255, 255, 247, 86, 0, 0, 0, 0, 0, 0, + 0, 76, 255, 112, 0, 0, 32, 255, 152, 0, 0, 51, 255, 203, 1, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 0, 72, 250, 228, 148, 255, 254, 108, 0, 0, 0, 0, 0, + 0, 79, 255, 112, 0, 0, 32, 255, 154, 0, 0, 0, 198, 209, 33, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 0, 66, 245, 229, 37, 0, 104, 253, 255, 112, 0, 0, 0, 0, + 0, 81, 255, 179, 120, 120, 137, 255, 157, 0, 0, 0, 21, 0, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 0, 0, 101, 248, 231, 39, 0, 0, 0, 92, 253, 254, 93, 0, 0, 0, + 0, 84, 255, 253, 252, 252, 252, 255, 161, 0, 0, 0, 0, 0, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 39, 180, 255, 214, 33, 0, 0, 0, 0, 0, 104, 255, 239, 16, 0, 0, + 0, 86, 255, 112, 0, 0, 32, 255, 166, 0, 0, 0, 0, 0, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 24, 220, 255, 179, 15, 0, 0, 0, 0, 0, 0, 0, 145, 60, 0, 0, 0, + 0, 89, 255, 112, 0, 0, 20, 160, 108, 0, 0, 0, 0, 0, 0, 0, 60, 255, 124, 0, 0, 0, 0, 0, 196, 244, 0, 0, 18, 102, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 75, 212, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 255, 123, 0, 0, 0, 0, 0, 199, 244, 0, 0, 0, 0, 0, 0, 0, 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, 185, 229, 251, 255, 90, 0, 0, 0, 0, 0, 206, 252, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 189, 194, 148, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 225, 181, 106, 2, 0, 0, 0, 0, 0, 136, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 166, 170, 129, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_prefs_keyboard_w = 88; +static const int g_txt_prefs_keyboard_h = 28; +static const unsigned char g_txt_prefs_keyboard[88*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 50, 159, 42, 0, 0, 0, 0, 0, 0, 0, 0, 59, 224, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 238, 171, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 224, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 6, 0, 0, + 0, 0, 0, 154, 255, 82, 0, 0, 0, 0, 0, 0, 0, 0, 59, 255, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 244, 247, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 13, 0, 0, + 0, 0, 25, 246, 192, 0, 0, 155, 213, 208, 208, 48, 132, 242, 239, 255, 235, 232, 232, 144, 0, 0, 0, 0, 0, 0, 0, 210, 232, 232, 245, 255, 238, 232, 232, 232, 232, 232, 232, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 201, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 150, 255, 6, 0, 0, + 0, 0, 152, 255, 209, 184, 170, 109, 149, 212, 243, 7, 62, 107, 147, 255, 124, 100, 248, 140, 0, 0, 0, 0, 0, 0, 0, 224, 252, 144, 144, 144, 147, 144, 144, 144, 144, 154, 255, 125, 0, 0, 0, 0, 0, 95, 147, 140, 137, 136, 136, 136, 223, 255, 165, 136, 136, 136, 136, 136, 136, 138, 140, 146, 109, 0, 0, 0, 0, 0, 252, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 1, 0, 0, + 0, 60, 253, 207, 175, 180, 167, 0, 0, 201, 172, 0, 0, 0, 76, 255, 40, 0, 244, 133, 0, 0, 0, 0, 0, 0, 0, 216, 248, 0, 0, 39, 227, 120, 8, 0, 0, 24, 255, 118, 0, 0, 0, 0, 0, 160, 252, 245, 242, 240, 240, 250, 255, 245, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 172, 0, 0, 0, 0, 0, 247, 215, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 178, 255, 7, 0, 0, + 31, 229, 224, 15, 0, 0, 0, 0, 18, 252, 91, 119, 234, 228, 236, 255, 232, 228, 254, 243, 215, 0, 0, 0, 0, 0, 0, 216, 248, 0, 0, 34, 171, 255, 183, 0, 0, 24, 255, 116, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 14, 230, 239, 23, 0, 62, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 14, 0, 0, + 10, 190, 52, 0, 0, 0, 9, 1, 98, 251, 16, 58, 106, 100, 146, 255, 124, 100, 248, 193, 120, 0, 0, 35, 61, 54, 48, 222, 249, 44, 44, 44, 44, 121, 89, 44, 44, 63, 255, 140, 47, 53, 45, 0, 0, 0, 0, 0, 0, 0, 148, 255, 105, 0, 0, 202, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 204, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 0, 0, + 0, 1, 150, 245, 238, 245, 255, 12, 199, 189, 0, 0, 0, 0, 76, 255, 40, 0, 244, 132, 0, 0, 0, 136, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 70, 253, 189, 2, 0, 0, 192, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 65, 187, 221, 76, 89, 124, 255, 200, 140, 81, 91, 194, 207, 255, 195, 184, 252, 137, 0, 0, 0, 49, 83, 75, 79, 252, 202, 72, 72, 85, 72, 72, 72, 72, 72, 106, 255, 139, 76, 81, 66, 0, 0, 0, 0, 0, 38, 239, 248, 77, 52, 52, 52, 203, 234, 52, 52, 52, 52, 52, 46, 0, 0, 0, 0, 0, 0, 0, 248, 228, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 85, 0, + 0, 0, 0, 144, 208, 0, 0, 143, 237, 204, 252, 130, 83, 162, 182, 255, 164, 148, 148, 84, 0, 0, 0, 0, 0, 0, 73, 255, 125, 0, 16, 235, 166, 42, 0, 0, 0, 48, 255, 92, 0, 0, 0, 0, 0, 0, 0, 31, 221, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 227, 0, 0, 0, 0, 0, 0, 2, 255, 249, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 239, 255, 149, 0, + 0, 0, 0, 144, 208, 0, 5, 10, 3, 2, 252, 105, 0, 0, 76, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 11, 209, 252, 34, 0, 13, 125, 237, 254, 103, 6, 17, 96, 255, 89, 0, 0, 0, 0, 0, 0, 39, 226, 255, 205, 255, 109, 68, 68, 68, 206, 235, 68, 68, 68, 68, 220, 220, 0, 0, 0, 0, 0, 0, 23, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 139, 0, + 0, 110, 232, 242, 254, 253, 204, 0, 19, 19, 255, 81, 175, 213, 221, 255, 212, 204, 211, 93, 0, 0, 0, 0, 17, 194, 255, 141, 0, 0, 0, 0, 21, 161, 16, 52, 254, 255, 254, 47, 0, 0, 0, 0, 0, 77, 239, 255, 127, 104, 255, 56, 0, 0, 0, 188, 228, 0, 0, 0, 0, 208, 214, 0, 0, 0, 0, 0, 0, 51, 255, 129, 0, 89, 116, 116, 116, 116, 116, 116, 116, 53, 0, 0, 55, 255, 129, 0, + 0, 56, 113, 190, 223, 88, 78, 140, 224, 44, 255, 50, 115, 138, 168, 255, 148, 128, 131, 60, 0, 0, 0, 0, 1, 94, 178, 5, 0, 0, 0, 0, 0, 0, 0, 0, 150, 137, 61, 0, 0, 0, 0, 0, 18, 219, 255, 117, 0, 104, 255, 56, 0, 0, 0, 188, 228, 0, 0, 0, 0, 208, 212, 0, 0, 0, 0, 0, 0, 97, 255, 94, 0, 193, 255, 255, 255, 255, 255, 255, 255, 111, 0, 0, 61, 255, 119, 0, + 0, 0, 0, 144, 208, 0, 0, 89, 254, 104, 255, 12, 0, 0, 76, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 149, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 149, 0, 0, 0, 0, 19, 82, 0, 0, 104, 255, 56, 0, 0, 0, 188, 228, 0, 0, 0, 0, 208, 212, 0, 0, 0, 0, 0, 0, 150, 255, 54, 0, 186, 240, 4, 4, 4, 4, 43, 255, 107, 0, 0, 68, 255, 109, 0, + 0, 0, 0, 144, 208, 0, 38, 22, 250, 216, 216, 43, 192, 184, 203, 255, 191, 180, 181, 192, 6, 0, 0, 0, 0, 176, 255, 152, 144, 144, 255, 194, 144, 144, 203, 247, 144, 144, 151, 255, 177, 0, 0, 0, 0, 0, 0, 0, 0, 105, 255, 56, 0, 0, 0, 188, 228, 0, 0, 0, 0, 209, 212, 0, 0, 0, 0, 0, 0, 225, 249, 7, 0, 178, 240, 0, 0, 0, 0, 40, 255, 108, 0, 0, 74, 255, 99, 0, + 0, 0, 0, 144, 208, 102, 247, 57, 156, 255, 149, 37, 167, 160, 184, 255, 168, 152, 153, 163, 5, 0, 0, 0, 0, 167, 255, 20, 0, 0, 255, 116, 0, 0, 136, 236, 0, 0, 16, 255, 170, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 56, 0, 0, 0, 188, 228, 0, 0, 0, 13, 234, 208, 0, 0, 0, 0, 0, 58, 255, 188, 0, 0, 183, 241, 16, 16, 16, 16, 53, 255, 114, 0, 0, 81, 255, 89, 0, + 0, 0, 0, 150, 250, 255, 200, 24, 79, 255, 194, 22, 0, 0, 76, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 20, 0, 0, 255, 116, 0, 0, 136, 236, 0, 0, 16, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 118, 255, 56, 0, 0, 0, 189, 228, 0, 51, 234, 252, 255, 168, 0, 0, 0, 0, 0, 178, 255, 97, 0, 0, 198, 255, 255, 255, 255, 255, 255, 255, 118, 0, 0, 92, 255, 79, 0, + 0, 0, 17, 228, 255, 169, 7, 21, 226, 208, 212, 237, 131, 55, 79, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 20, 0, 0, 255, 116, 0, 0, 136, 236, 0, 0, 16, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 56, 0, 0, 0, 192, 232, 0, 0, 218, 229, 159, 28, 0, 0, 0, 0, 71, 254, 232, 7, 0, 0, 67, 80, 80, 80, 80, 80, 80, 80, 46, 31, 32, 182, 255, 56, 0, + 0, 0, 15, 206, 168, 3, 48, 221, 248, 45, 11, 144, 245, 255, 251, 255, 217, 205, 202, 219, 218, 5, 51, 157, 147, 215, 255, 152, 144, 144, 255, 194, 144, 144, 203, 247, 144, 144, 151, 255, 218, 151, 160, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 238, 0, 0, 7, 0, 0, 0, 0, 0, 0, 4, 212, 255, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 233, 255, 255, 231, 6, 0, + 0, 0, 0, 18, 3, 0, 41, 223, 78, 0, 0, 0, 11, 64, 109, 144, 169, 184, 190, 189, 128, 0, 70, 222, 214, 212, 210, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 209, 213, 217, 221, 225, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 207, 246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 118, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 222, 169, 52, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_prefs_theme_w = 44; +static const int g_txt_prefs_theme_h = 28; +static const unsigned char g_txt_prefs_theme[44*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 158, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 118, 255, 194, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 240, 240, 240, 240, 240, 240, 105, 88, 251, 243, 240, 240, 240, 240, 240, 243, 251, 72, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 170, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 224, 112, 112, 112, 136, 255, 107, 46, 128, 118, 115, 210, 236, 113, 115, 119, 127, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 215, 254, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 200, 0, 0, 0, 44, 255, 102, 0, 0, 0, 0, 201, 195, 0, 0, 0, 0, 0, 0, + 0, 0, 68, 74, 68, 61, 60, 60, 60, 60, 104, 118, 60, 60, 60, 60, 60, 64, 71, 61, 0, 0, 0, 0, 197, 251, 236, 236, 236, 239, 255, 97, 0, 0, 0, 0, 235, 161, 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, 196, 0, 0, 0, 0, 201, 225, 116, 116, 116, 140, 255, 99, 0, 180, 252, 252, 255, 254, 252, 252, 252, 93, 0, 0, + 0, 0, 65, 71, 64, 57, 56, 56, 56, 62, 255, 212, 56, 56, 56, 56, 57, 60, 68, 58, 0, 0, 0, 0, 206, 200, 0, 0, 0, 44, 255, 105, 0, 175, 223, 80, 80, 80, 80, 115, 255, 77, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211, 246, 212, 212, 212, 219, 255, 110, 0, 171, 208, 0, 28, 48, 9, 52, 255, 68, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 116, 116, 116, 116, 116, 116, 51, 0, 169, 208, 0, 93, 255, 93, 52, 255, 64, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 53, 45, 44, 44, 44, 44, 49, 57, 19, 168, 208, 0, 84, 255, 69, 52, 255, 64, 0, 0, + 0, 0, 0, 90, 134, 127, 124, 124, 124, 128, 255, 227, 124, 124, 124, 124, 128, 135, 68, 0, 0, 0, 0, 204, 255, 255, 255, 255, 255, 255, 255, 255, 80, 170, 208, 0, 81, 255, 42, 52, 255, 68, 0, 0, + 0, 0, 0, 164, 255, 255, 252, 252, 252, 252, 255, 255, 252, 252, 252, 253, 255, 255, 124, 0, 0, 0, 0, 52, 49, 40, 34, 123, 255, 45, 40, 57, 22, 172, 208, 0, 105, 254, 9, 52, 255, 77, 0, 0, + 0, 0, 0, 8, 7, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 8, 6, 0, 0, 0, 0, 0, 92, 53, 0, 104, 255, 12, 0, 0, 0, 177, 208, 0, 167, 224, 0, 52, 255, 92, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 181, 0, 104, 255, 169, 170, 145, 0, 161, 182, 27, 248, 149, 4, 9, 48, 19, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 135, 0, 104, 255, 193, 193, 162, 0, 0, 3, 185, 251, 38, 169, 158, 14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 200, 21, 104, 255, 12, 0, 0, 0, 15, 179, 255, 134, 6, 142, 254, 220, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 236, 229, 175, 255, 12, 0, 0, 30, 229, 255, 164, 2, 0, 0, 73, 238, 91, 0, 0, + 0, 134, 153, 140, 134, 132, 132, 132, 132, 135, 255, 228, 132, 132, 132, 132, 132, 132, 136, 143, 121, 0, 0, 166, 252, 24, 145, 253, 255, 126, 50, 4, 0, 97, 126, 1, 0, 0, 0, 0, 22, 11, 44, 0, + 0, 194, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 245, 248, 254, 204, 0, 8, 241, 195, 0, 0, 41, 163, 243, 255, 248, 212, 186, 170, 166, 168, 178, 192, 210, 236, 255, 194, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 32, 209, 106, 0, 0, 0, 0, 8, 66, 114, 152, 176, 196, 208, 217, 221, 221, 220, 217, 213, 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, 3, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_prefs_hint_w = 352; +static const int g_txt_prefs_hint_h = 22; +static const unsigned char g_txt_prefs_hint[352*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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 16, 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, 15, 128, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 53, 0, 0, 0, 151, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 58, 63, 0, 177, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 0, 0, 0, 0, 0, 3, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 227, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 181, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 178, 4, 0, 0, 0, 0, 0, 71, 198, 0, 0, 0, 0, 0, 0, 15, 128, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 53, 0, 0, 0, 151, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 0, 0, 0, 0, 0, 3, 11, 0, 0, 0, 23, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 101, 117, 145, 192, 105, 23, 121, 113, 112, 112, 112, 13, 0, 0, 0, 0, 0, 206, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 58, 63, 0, 177, 154, 0, 0, 0, 0, 0, 0, 0, 61, 255, 28, 0, 49, 155, 147, 144, 144, 144, 144, 141, 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, 23, 255, 22, 0, 31, 122, 115, 112, 112, 112, 112, 112, 82, 0, 0, 0, 0, 242, 35, 0, 0, 31, 248, 93, 32, 32, 32, 8, 0, 0, 0, 0, 191, 130, 0, 0, 181, 178, 0, 182, 149, 0, 0, 0, 0, 0, 20, 104, 97, 96, 100, 54, 205, 246, 240, 245, 255, 240, 240, 244, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 241, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 218, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 150, 0, 95, 130, 128, 23, 145, 175, 228, 144, 144, 40, 0, 0, 0, 23, 255, 22, 0, 31, 122, 115, 112, 112, 112, 112, 112, 82, 0, 0, 0, 0, 242, 35, 0, 0, 31, 248, 93, 32, 32, 32, 8, 0, 0, 20, 104, 97, 96, 100, 54, 205, 246, 240, 245, 255, 240, 240, 244, 252, 0, 0, 0, 91, 252, 192, 192, 192, 192, 192, 192, 192, 192, 252, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 104, 89, 77, 61, 26, 139, 130, 128, 136, 255, 23, 0, 0, 0, 0, 0, 212, 84, 0, 31, 250, 242, 240, 240, 240, 240, 245, 95, 0, 0, 0, 0, 0, 0, 0, 18, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 130, 0, 0, 181, 178, 0, 182, 149, 0, 0, 0, 0, 0, 0, 0, 56, 255, 23, 0, 35, 175, 211, 112, 112, 112, 226, 159, 0, 0, + 32, 255, 255, 255, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 39, 156, 150, 214, 213, 144, 144, 201, 177, 0, 0, 0, 0, 240, 32, 0, 0, 174, 218, 192, 192, 236, 215, 9, 0, 0, 0, 0, 112, 231, 4, 9, 244, 183, 144, 221, 208, 150, 155, 109, 0, 0, 29, 154, 194, 228, 147, 78, 0, 0, 0, 99, 224, 0, 0, 0, 0, 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, 91, 0, 0, 0, 0, 0, 0, 0, 0, 17, 96, 96, 96, 229, 177, 96, 96, 96, 96, 96, 96, 96, 59, 0, 0, 70, 255, 196, 187, 96, 156, 241, 5, 100, 148, 216, 96, 236, 55, 0, 0, 0, 16, 255, 16, 0, 39, 156, 150, 214, 213, 144, 144, 201, 177, 0, 0, 0, 0, 240, 32, 0, 0, 174, 218, 192, 192, 236, 215, 9, 0, 0, 29, 154, 194, 228, 147, 78, 0, 0, 0, 99, 224, 0, 0, 0, 0, 0, 0, 0, 84, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 32, 32, 34, 37, 2, 42, 34, 32, 46, 255, 16, 0, 0, 0, 0, 0, 208, 80, 0, 7, 54, 37, 32, 32, 32, 40, 53, 23, 0, 0, 0, 0, 0, 0, 0, 16, 255, 244, 240, 240, 240, 241, 249, 79, 0, 0, 0, 58, 57, 48, 48, 48, 178, 165, 48, 48, 48, 53, 49, 0, 0, 0, 0, 112, 231, 4, 9, 244, 183, 144, 221, 208, 150, 155, 109, 0, 0, 3, 23, 65, 255, 35, 23, 0, 28, 242, 29, 0, 77, 238, 24, 0, 0, + 32, 255, 113, 48, 48, 48, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 0, 0, 0, 161, 159, 0, 0, 140, 163, 0, 35, 135, 129, 248, 147, 105, 107, 226, 17, 0, 27, 241, 53, 0, 0, 0, 0, 0, 12, 233, 50, 106, 228, 96, 96, 206, 186, 98, 101, 74, 0, 0, 0, 0, 112, 192, 0, 0, 3, 16, 16, 137, 175, 16, 16, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 229, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 226, 233, 0, 0, 42, 255, 200, 192, 192, 192, 192, 192, 192, 192, 192, 192, 236, 153, 0, 19, 223, 136, 83, 91, 0, 98, 168, 66, 96, 146, 216, 96, 236, 131, 20, 0, 0, 16, 255, 16, 0, 0, 0, 0, 161, 159, 0, 0, 140, 163, 0, 35, 135, 129, 248, 147, 105, 107, 226, 17, 0, 27, 241, 53, 0, 0, 0, 0, 0, 112, 192, 0, 0, 3, 16, 16, 137, 175, 16, 16, 15, 0, 0, 0, 0, 82, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 208, 208, 213, 192, 14, 219, 210, 208, 211, 255, 16, 0, 0, 26, 138, 132, 232, 169, 136, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 88, 32, 32, 32, 34, 42, 14, 0, 0, 0, 190, 226, 224, 224, 224, 244, 242, 224, 224, 224, 230, 192, 0, 0, 0, 0, 12, 233, 50, 106, 228, 96, 96, 206, 186, 98, 101, 74, 0, 0, 32, 255, 255, 255, 255, 224, 0, 0, 130, 196, 82, 239, 72, 0, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 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, 0, 0, 0, 0, 0, 0, 21, 255, 76, 99, 102, 0, 0, 165, 153, 0, 0, 152, 151, 0, 31, 119, 114, 247, 133, 157, 253, 130, 80, 80, 176, 195, 80, 80, 0, 0, 0, 0, 0, 5, 0, 99, 111, 0, 0, 176, 144, 0, 0, 0, 0, 0, 0, 0, 112, 192, 0, 0, 44, 255, 226, 224, 224, 224, 230, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 67, 64, 64, 245, 123, 64, 64, 64, 64, 64, 64, 66, 74, 0, 0, 35, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 146, 0, 66, 170, 0, 0, 5, 1, 176, 88, 96, 144, 179, 228, 144, 242, 183, 32, 0, 0, 21, 255, 76, 99, 102, 0, 0, 165, 153, 0, 0, 152, 151, 0, 31, 119, 114, 247, 133, 157, 253, 130, 80, 80, 176, 195, 80, 80, 0, 0, 0, 0, 112, 192, 0, 0, 44, 255, 226, 224, 224, 224, 230, 251, 0, 0, 0, 0, 80, 248, 128, 128, 128, 128, 128, 128, 128, 128, 248, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 17, 0, 0, 32, 159, 166, 255, 181, 153, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 16, 16, 31, 255, 76, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 99, 111, 0, 0, 176, 144, 0, 0, 0, 0, 0, 1, 8, 49, 255, 20, 10, 0, 0, 18, 232, 255, 151, 0, 0, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 10, 80, 10, 45, 102, 76, 1, 0, 45, 80, 157, 223, 80, 80, 30, 0, 0, 0, 24, 86, 91, 26, 0, 0, 0, 30, 70, 0, 19, 88, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 239, 255, 255, 213, 183, 132, 0, 0, 180, 139, 0, 0, 164, 139, 0, 0, 0, 0, 240, 32, 3, 133, 232, 160, 179, 249, 160, 166, 255, 0, 0, 3, 58, 51, 49, 8, 102, 120, 112, 112, 211, 193, 112, 112, 118, 70, 0, 0, 0, 112, 192, 0, 0, 36, 255, 16, 0, 0, 0, 48, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 32, 0, 15, 16, 16, 16, 16, 16, 1, 0, 176, 144, 0, 0, 64, 248, 237, 247, 67, 246, 15, 0, 24, 93, 196, 16, 226, 49, 0, 59, 239, 255, 255, 213, 183, 132, 0, 0, 180, 139, 0, 0, 164, 139, 0, 0, 0, 0, 240, 32, 3, 133, 232, 160, 179, 249, 160, 166, 255, 0, 0, 0, 0, 112, 192, 0, 0, 36, 255, 16, 0, 0, 0, 48, 243, 0, 0, 0, 0, 80, 248, 128, 128, 128, 128, 128, 128, 128, 128, 248, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 192, 192, 192, 192, 192, 192, 192, 192, 196, 255, 24, 0, 0, 0, 0, 83, 255, 80, 0, 164, 211, 208, 208, 208, 208, 208, 209, 216, 27, 0, 0, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 144, 0, 0, 0, 0, 0, 0, 0, 3, 58, 51, 49, 8, 102, 120, 112, 112, 211, 193, 112, 112, 118, 70, 0, 0, 0, 48, 255, 16, 22, 100, 159, 239, 172, 108, 231, 227, 169, 134, 25, + 32, 255, 168, 128, 128, 128, 72, 0, 32, 255, 140, 216, 158, 242, 155, 0, 99, 176, 211, 241, 176, 176, 66, 0, 0, 80, 238, 181, 189, 241, 70, 0, 0, 96, 224, 82, 245, 240, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 57, 43, 255, 16, 0, 0, 0, 0, 207, 112, 0, 0, 177, 128, 0, 0, 0, 0, 240, 61, 71, 80, 192, 0, 61, 231, 0, 16, 255, 0, 0, 14, 220, 214, 255, 42, 117, 148, 146, 251, 169, 200, 221, 146, 150, 88, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 40, 177, 31, 48, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 254, 114, 64, 64, 64, 64, 64, 64, 14, 0, 0, 0, 32, 255, 32, 0, 234, 246, 240, 240, 244, 255, 26, 0, 176, 144, 0, 0, 3, 130, 128, 0, 167, 254, 240, 94, 211, 236, 248, 224, 224, 49, 0, 15, 57, 43, 255, 16, 0, 0, 0, 0, 207, 112, 0, 0, 177, 128, 0, 0, 0, 0, 240, 61, 71, 80, 192, 0, 61, 231, 0, 16, 255, 0, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 40, 177, 31, 48, 240, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 96, 53, 79, 50, 67, 106, 48, 74, 48, 59, 50, 5, 0, 0, 0, 0, 156, 255, 182, 115, 64, 70, 64, 64, 244, 112, 64, 66, 75, 9, 0, 0, 7, 255, 80, 0, 0, 0, 0, 0, 0, 64, 255, 6, 0, 0, 53, 133, 128, 128, 128, 128, 128, 208, 200, 128, 128, 128, 128, 128, 133, 53, 14, 220, 214, 255, 42, 117, 148, 146, 251, 169, 200, 221, 146, 150, 88, 0, 0, 0, 48, 255, 186, 91, 169, 139, 43, 86, 137, 7, 85, 158, 169, 0, + 32, 255, 201, 176, 176, 176, 99, 0, 32, 255, 203, 11, 0, 102, 243, 0, 0, 0, 112, 208, 0, 0, 0, 0, 16, 238, 102, 0, 0, 156, 208, 0, 0, 96, 242, 216, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 0, 0, 4, 244, 72, 0, 0, 189, 117, 0, 0, 0, 34, 245, 241, 103, 80, 192, 0, 76, 221, 0, 16, 255, 0, 0, 0, 0, 16, 255, 36, 0, 0, 6, 255, 39, 128, 176, 0, 0, 0, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 57, 255, 24, 48, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 255, 229, 224, 224, 224, 224, 230, 255, 39, 0, 0, 0, 32, 255, 32, 0, 225, 96, 0, 0, 64, 255, 17, 0, 176, 144, 0, 0, 8, 128, 137, 24, 30, 21, 187, 76, 0, 80, 192, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 0, 0, 4, 244, 72, 0, 0, 189, 117, 0, 0, 0, 34, 245, 241, 103, 80, 192, 0, 76, 221, 0, 16, 255, 0, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 57, 255, 24, 48, 240, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 9, 181, 167, 70, 216, 47, 236, 73, 154, 167, 0, 0, 0, 0, 11, 239, 252, 171, 241, 21, 0, 0, 0, 240, 64, 0, 1, 0, 0, 0, 0, 1, 255, 80, 0, 0, 0, 0, 0, 0, 64, 255, 1, 0, 0, 53, 134, 129, 128, 128, 128, 128, 208, 200, 128, 128, 128, 128, 129, 133, 53, 0, 0, 16, 255, 36, 0, 0, 6, 255, 39, 128, 176, 0, 0, 0, 0, 42, 148, 228, 255, 133, 19, 16, 22, 18, 130, 215, 19, 24, 23, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 78, 0, 0, 64, 255, 0, 0, 0, 112, 208, 0, 0, 0, 0, 68, 255, 212, 208, 208, 227, 254, 5, 0, 96, 255, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 33, 0, 63, 251, 16, 0, 0, 201, 105, 0, 31, 181, 242, 252, 47, 31, 103, 200, 32, 110, 217, 32, 46, 255, 37, 17, 0, 0, 16, 255, 32, 0, 0, 55, 245, 9, 128, 176, 0, 0, 0, 0, 0, 0, 112, 192, 32, 61, 32, 255, 16, 66, 238, 0, 48, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 255, 16, 0, 0, 0, 0, 63, 255, 14, 0, 0, 0, 32, 255, 32, 0, 224, 96, 0, 0, 64, 255, 16, 0, 176, 144, 0, 0, 218, 249, 241, 220, 56, 50, 205, 83, 250, 247, 252, 240, 247, 31, 0, 0, 0, 16, 255, 16, 0, 33, 0, 63, 251, 16, 0, 0, 201, 105, 0, 31, 181, 242, 252, 47, 31, 103, 200, 32, 110, 217, 32, 46, 255, 37, 17, 0, 0, 112, 192, 32, 61, 32, 255, 16, 66, 238, 0, 48, 240, 0, 0, 0, 0, 80, 251, 176, 176, 176, 176, 176, 176, 176, 176, 251, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 0, 17, 84, 64, 208, 0, 67, 36, 143, 153, 0, 0, 0, 0, 121, 213, 209, 80, 216, 96, 130, 149, 0, 240, 64, 141, 137, 0, 0, 0, 0, 6, 255, 80, 0, 0, 0, 0, 0, 0, 64, 255, 5, 0, 0, 0, 0, 11, 31, 0, 0, 0, 160, 144, 0, 0, 0, 47, 17, 0, 0, 0, 0, 16, 255, 32, 0, 0, 55, 245, 9, 128, 176, 0, 0, 0, 0, 39, 220, 131, 255, 16, 0, 141, 245, 241, 247, 253, 242, 246, 205, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 37, 0, 0, 64, 255, 0, 0, 0, 112, 208, 0, 0, 0, 0, 81, 255, 58, 48, 48, 48, 48, 2, 0, 96, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 26, 141, 241, 11, 159, 188, 0, 0, 0, 214, 94, 0, 4, 135, 25, 240, 32, 164, 209, 208, 208, 237, 255, 222, 208, 208, 213, 83, 0, 0, 16, 255, 32, 0, 0, 157, 164, 0, 128, 176, 0, 85, 114, 0, 9, 66, 174, 250, 252, 123, 34, 255, 16, 103, 185, 0, 48, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 215, 0, 0, 0, 0, 0, 87, 245, 0, 0, 0, 0, 32, 255, 32, 0, 229, 136, 64, 64, 112, 255, 20, 0, 176, 144, 0, 0, 0, 128, 128, 0, 127, 154, 238, 18, 11, 82, 192, 0, 3, 1, 0, 0, 0, 16, 255, 26, 141, 241, 11, 159, 188, 0, 0, 0, 214, 94, 0, 4, 135, 25, 240, 32, 164, 209, 208, 208, 237, 255, 222, 208, 208, 213, 83, 9, 66, 174, 250, 252, 123, 34, 255, 16, 103, 185, 0, 48, 241, 0, 0, 0, 0, 80, 245, 80, 80, 80, 80, 80, 80, 80, 80, 245, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 32, 223, 71, 64, 208, 78, 180, 21, 129, 159, 0, 0, 0, 45, 244, 87, 208, 80, 16, 0, 219, 135, 0, 240, 64, 112, 251, 47, 0, 0, 0, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, 0, 0, 0, 86, 245, 0, 0, 0, 160, 144, 0, 0, 0, 244, 84, 0, 0, 0, 0, 16, 255, 32, 0, 0, 157, 164, 0, 128, 176, 0, 85, 114, 0, 0, 4, 48, 255, 16, 0, 0, 0, 0, 112, 208, 0, 0, 0, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 32, 0, 0, 64, 255, 0, 0, 0, 105, 217, 0, 0, 0, 0, 37, 255, 66, 0, 0, 64, 172, 6, 0, 96, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 227, 206, 49, 42, 250, 82, 0, 0, 0, 226, 82, 0, 0, 0, 0, 240, 32, 0, 0, 0, 11, 230, 218, 163, 0, 0, 0, 0, 0, 0, 16, 255, 32, 0, 102, 243, 32, 0, 123, 227, 101, 201, 185, 0, 44, 255, 194, 96, 20, 0, 43, 255, 16, 193, 121, 37, 48, 248, 0, 0, 0, 0, 78, 96, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 228, 130, 0, 0, 0, 0, 0, 110, 217, 0, 0, 0, 0, 32, 255, 32, 0, 177, 192, 192, 192, 192, 192, 21, 0, 176, 144, 0, 0, 0, 128, 128, 49, 54, 232, 222, 82, 182, 201, 236, 176, 177, 105, 0, 0, 0, 38, 255, 227, 206, 49, 42, 250, 82, 0, 0, 0, 226, 82, 0, 0, 0, 0, 240, 32, 0, 0, 0, 11, 230, 218, 163, 0, 0, 0, 0, 44, 255, 194, 96, 20, 0, 43, 255, 16, 193, 121, 37, 48, 248, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 78, 96, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 0, 106, 210, 68, 208, 14, 176, 133, 100, 180, 0, 0, 0, 90, 172, 0, 208, 80, 0, 64, 251, 27, 0, 240, 64, 3, 209, 196, 0, 0, 0, 39, 21, 16, 16, 16, 16, 16, 17, 16, 16, 16, 5, 0, 0, 0, 0, 80, 240, 0, 0, 0, 160, 144, 0, 0, 0, 240, 80, 0, 0, 0, 0, 16, 255, 32, 0, 102, 243, 32, 0, 123, 227, 101, 201, 185, 0, 0, 0, 48, 255, 16, 48, 53, 50, 48, 139, 217, 50, 54, 58, 61, 0, + 32, 255, 168, 128, 128, 128, 128, 32, 32, 255, 32, 0, 0, 64, 255, 0, 0, 0, 47, 252, 112, 94, 127, 0, 0, 157, 228, 108, 100, 224, 162, 0, 0, 96, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 251, 128, 5, 13, 204, 192, 0, 0, 0, 0, 240, 71, 0, 0, 0, 0, 240, 32, 0, 0, 6, 183, 198, 22, 234, 99, 0, 0, 0, 0, 0, 32, 255, 71, 48, 236, 82, 0, 0, 38, 188, 208, 202, 59, 0, 0, 35, 0, 0, 0, 0, 8, 48, 119, 235, 49, 245, 166, 40, 0, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 241, 21, 0, 0, 0, 0, 0, 133, 189, 0, 0, 0, 0, 32, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 144, 0, 0, 0, 128, 195, 244, 63, 193, 176, 34, 73, 126, 208, 64, 66, 43, 0, 0, 0, 121, 251, 128, 5, 13, 204, 192, 0, 0, 0, 0, 240, 71, 0, 0, 0, 0, 240, 32, 0, 0, 6, 183, 198, 22, 234, 99, 0, 0, 0, 0, 35, 0, 0, 0, 0, 8, 48, 119, 235, 49, 245, 166, 40, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 0, 31, 35, 64, 208, 0, 27, 32, 62, 228, 1, 122, 22, 0, 3, 0, 208, 80, 0, 195, 166, 0, 0, 240, 64, 0, 87, 255, 71, 0, 5, 232, 138, 1, 164, 63, 0, 61, 204, 6, 0, 115, 179, 0, 0, 0, 0, 80, 240, 0, 0, 0, 160, 144, 0, 0, 0, 240, 80, 0, 0, 0, 0, 32, 255, 71, 48, 236, 82, 0, 0, 38, 188, 208, 202, 59, 0, 0, 0, 48, 255, 16, 178, 213, 211, 209, 229, 247, 210, 211, 214, 221, 0, + 22, 176, 176, 176, 176, 176, 176, 44, 22, 176, 22, 0, 0, 44, 176, 0, 0, 0, 0, 81, 185, 202, 141, 0, 0, 2, 107, 183, 190, 117, 6, 0, 0, 66, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 52, 0, 20, 196, 225, 23, 0, 34, 85, 132, 255, 46, 0, 0, 0, 3, 244, 31, 0, 49, 207, 208, 20, 0, 64, 247, 142, 11, 0, 0, 51, 227, 193, 225, 177, 105, 44, 16, 0, 0, 0, 4, 22, 50, 15, 0, 0, 0, 0, 0, 0, 35, 162, 236, 51, 0, 71, 236, 200, 21, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 254, 86, 0, 0, 0, 0, 0, 18, 212, 155, 0, 0, 0, 0, 40, 255, 32, 0, 0, 0, 0, 0, 0, 0, 23, 25, 196, 140, 0, 0, 0, 185, 248, 82, 33, 235, 210, 169, 63, 86, 192, 0, 0, 0, 0, 0, 0, 11, 52, 0, 20, 196, 225, 23, 0, 34, 85, 132, 255, 46, 0, 0, 0, 3, 244, 31, 0, 49, 207, 208, 20, 0, 64, 247, 142, 11, 0, 0, 0, 0, 0, 0, 0, 35, 162, 236, 51, 0, 71, 236, 200, 21, 0, 0, 0, 83, 252, 192, 192, 192, 192, 192, 192, 192, 192, 252, 99, 0, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 158, 246, 90, 90, 230, 173, 247, 112, 8, 239, 132, 254, 62, 0, 0, 0, 213, 84, 16, 167, 48, 14, 39, 250, 59, 0, 3, 70, 0, 0, 91, 249, 29, 0, 177, 175, 0, 17, 239, 115, 0, 48, 252, 87, 0, 0, 0, 86, 248, 128, 128, 128, 208, 200, 128, 128, 128, 248, 80, 0, 0, 0, 51, 227, 193, 225, 177, 105, 44, 16, 0, 0, 0, 4, 22, 50, 15, 0, 75, 141, 254, 10, 0, 0, 0, 0, 113, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 206, 226, 36, 0, 0, 32, 255, 252, 171, 0, 0, 0, 100, 214, 237, 14, 184, 250, 143, 8, 0, 0, 0, 66, 236, 231, 25, 3, 203, 156, 0, 6, 91, 169, 222, 251, 254, 251, 251, 255, 255, 228, 3, 0, 0, 0, 0, 22, 193, 254, 178, 25, 0, 0, 0, 33, 218, 171, 0, 0, 0, 0, 182, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 252, 105, 0, 0, 0, 0, 105, 226, 251, 241, 56, 0, 0, 0, 0, 46, 255, 32, 0, 0, 0, 0, 0, 0, 0, 138, 255, 253, 77, 0, 0, 0, 145, 71, 30, 230, 137, 6, 122, 214, 252, 247, 220, 218, 236, 55, 0, 0, 0, 0, 9, 206, 226, 36, 0, 0, 32, 255, 252, 171, 0, 0, 0, 100, 214, 237, 14, 184, 250, 143, 8, 0, 0, 0, 66, 236, 231, 25, 0, 0, 0, 0, 22, 193, 254, 178, 25, 0, 0, 0, 33, 218, 171, 0, 0, 0, 90, 244, 64, 64, 64, 64, 64, 64, 64, 64, 244, 105, 0, 0, 0, 0, 0, 182, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 167, 35, 0, 87, 244, 134, 28, 0, 0, 85, 253, 185, 0, 0, 0, 0, 219, 90, 0, 0, 0, 158, 255, 226, 15, 0, 0, 0, 0, 8, 221, 154, 0, 0, 80, 252, 23, 0, 128, 233, 5, 0, 153, 230, 9, 0, 0, 46, 128, 128, 128, 128, 128, 128, 128, 128, 128, 248, 87, 0, 0, 3, 203, 156, 0, 6, 91, 169, 222, 251, 254, 251, 251, 255, 255, 228, 3, 0, 148, 254, 165, 0, 0, 0, 0, 0, 120, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 31, 0, 0, 0, 0, 54, 12, 0, 0, 0, 0, 31, 94, 22, 0, 68, 34, 0, 0, 0, 0, 0, 0, 22, 72, 0, 0, 11, 6, 0, 0, 0, 0, 0, 3, 21, 31, 36, 37, 35, 23, 0, 0, 0, 0, 0, 0, 97, 60, 0, 0, 0, 0, 0, 0, 28, 17, 0, 0, 0, 77, 215, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 88, 0, 0, 0, 0, 0, 34, 119, 77, 15, 0, 0, 0, 0, 0, 12, 64, 8, 0, 0, 0, 0, 0, 0, 0, 23, 61, 19, 0, 0, 0, 0, 0, 0, 0, 81, 1, 0, 0, 0, 9, 40, 58, 66, 65, 2, 0, 0, 0, 0, 0, 21, 31, 0, 0, 0, 0, 54, 12, 0, 0, 0, 0, 31, 94, 22, 0, 68, 34, 0, 0, 0, 0, 0, 0, 22, 72, 0, 0, 0, 0, 0, 0, 97, 60, 0, 0, 0, 0, 0, 0, 28, 17, 0, 0, 0, 29, 75, 0, 0, 0, 0, 0, 0, 0, 0, 90, 41, 0, 0, 0, 0, 77, 215, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 78, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 35, 13, 0, 0, 0, 0, 97, 41, 0, 0, 0, 34, 58, 5, 0, 0, 0, 0, 0, 0, 73, 29, 0, 0, 11, 50, 0, 0, 18, 39, 0, 0, 29, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 41, 0, 0, 0, 11, 6, 0, 0, 0, 0, 0, 3, 21, 31, 36, 37, 35, 23, 0, 0, 25, 19, 0, 0, 0, 0, 0, 0, 92, 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, 39, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_network_title_w = 96; +static const int g_txt_network_title_h = 30; +static const unsigned char g_txt_network_title[96*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 74, 6, 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, 88, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 1, 0, 0, 0, 0, 0, 61, 87, 2, 0, 0, 0, 0, 0, 83, 255, 225, 8, 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, 223, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 156, 255, 148, 0, 0, 0, 0, 0, 159, 255, 102, 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, 205, 255, 81, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 62, 255, 224, 0, 0, 0, 0, 0, 5, 233, 245, 27, 0, 0, 0, 0, 13, 243, 255, 180, 176, 176, 176, 176, 176, 176, 67, 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, 197, 255, 32, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 9, 0, 8, 255, 217, 0, 0, 0, 0, 0, 89, 255, 136, 0, 0, 0, 0, 0, 124, 255, 243, 208, 208, 208, 208, 222, 255, 232, 10, 0, 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, 192, 255, 32, 0, 22, 0, 0, 0, 230, 194, 45, 0, 0, 0, 21, 235, 119, 11, 255, 216, 0, 0, 0, 0, 0, 200, 241, 18, 0, 132, 54, 0, 15, 239, 255, 242, 18, 0, 0, 0, 166, 255, 107, 0, 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, 192, 255, 53, 213, 220, 6, 0, 34, 255, 224, 6, 15, 0, 0, 80, 255, 195, 8, 255, 216, 0, 0, 0, 0, 74, 255, 129, 0, 17, 247, 250, 33, 144, 255, 202, 255, 128, 0, 0, 59, 255, 213, 4, 0, 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, 192, 255, 32, 146, 255, 112, 0, 100, 255, 121, 139, 244, 42, 0, 147, 255, 86, 8, 255, 216, 0, 0, 0, 16, 222, 233, 13, 0, 125, 255, 142, 73, 253, 203, 9, 223, 244, 35, 10, 212, 253, 60, 0, 0, 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, 192, 255, 32, 10, 222, 239, 22, 170, 254, 30, 108, 255, 174, 0, 216, 242, 9, 8, 255, 216, 0, 0, 10, 204, 255, 188, 112, 112, 236, 233, 14, 156, 246, 40, 0, 89, 255, 198, 169, 255, 133, 0, 0, 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, 0, 192, 255, 32, 0, 76, 255, 164, 240, 193, 0, 1, 193, 255, 91, 255, 163, 0, 8, 255, 216, 0, 0, 0, 220, 255, 255, 238, 247, 255, 100, 0, 0, 40, 0, 0, 0, 176, 255, 255, 182, 3, 0, 0, 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, 0, 192, 255, 32, 0, 0, 181, 255, 255, 100, 0, 0, 51, 254, 249, 255, 72, 0, 8, 255, 216, 0, 0, 0, 70, 57, 10, 15, 237, 207, 2, 0, 0, 0, 0, 0, 75, 227, 255, 255, 202, 58, 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, 0, 192, 255, 32, 0, 0, 39, 254, 253, 17, 0, 0, 0, 170, 255, 230, 3, 0, 8, 255, 216, 0, 0, 0, 0, 0, 0, 136, 255, 62, 0, 0, 0, 8, 89, 201, 255, 239, 106, 148, 252, 255, 203, 109, 31, 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, 192, 255, 32, 0, 0, 70, 255, 255, 103, 0, 0, 0, 117, 255, 223, 5, 0, 8, 255, 216, 0, 0, 0, 0, 0, 41, 249, 167, 0, 0, 17, 164, 240, 255, 252, 158, 23, 0, 0, 47, 180, 254, 255, 255, 217, 16, 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, 192, 255, 32, 0, 0, 200, 246, 245, 239, 22, 0, 16, 235, 255, 255, 109, 0, 8, 255, 216, 0, 0, 0, 0, 11, 206, 243, 27, 10, 50, 50, 173, 251, 223, 133, 96, 96, 96, 96, 96, 96, 138, 218, 246, 165, 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, 192, 255, 32, 0, 86, 255, 142, 131, 255, 159, 0, 150, 255, 150, 253, 234, 14, 8, 255, 216, 0, 0, 0, 24, 199, 255, 236, 222, 254, 255, 151, 29, 26, 158, 255, 255, 255, 255, 255, 255, 255, 255, 144, 12, 22, 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, 192, 255, 32, 10, 223, 243, 23, 16, 236, 118, 72, 254, 220, 7, 165, 255, 136, 8, 255, 216, 0, 0, 0, 12, 243, 255, 254, 220, 178, 142, 70, 0, 0, 148, 255, 60, 32, 32, 32, 32, 88, 255, 124, 0, 0, 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, 192, 255, 32, 143, 255, 123, 0, 0, 24, 39, 239, 254, 68, 0, 39, 253, 236, 21, 255, 216, 0, 0, 0, 0, 88, 69, 15, 0, 0, 0, 0, 0, 0, 144, 255, 32, 0, 0, 0, 0, 64, 255, 112, 0, 0, 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, 192, 255, 104, 254, 217, 7, 0, 0, 17, 224, 255, 145, 0, 0, 0, 71, 13, 8, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 115, 0, 0, 144, 255, 32, 0, 0, 0, 0, 64, 255, 112, 0, 0, 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, 192, 255, 35, 109, 55, 0, 0, 0, 0, 40, 158, 7, 0, 0, 0, 0, 0, 8, 255, 216, 0, 0, 0, 0, 1, 37, 94, 154, 223, 255, 255, 8, 0, 144, 255, 32, 0, 0, 0, 0, 64, 255, 112, 0, 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, 195, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 255, 216, 0, 0, 0, 53, 238, 255, 255, 255, 203, 122, 53, 1, 0, 144, 255, 221, 216, 216, 216, 216, 226, 255, 112, 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, 205, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 203, 240, 255, 198, 0, 0, 0, 3, 226, 229, 136, 38, 0, 0, 0, 0, 0, 149, 255, 179, 168, 168, 168, 168, 190, 255, 119, 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, 225, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 247, 200, 68, 0, 0, 0, 0, 34, 3, 0, 0, 0, 0, 0, 0, 0, 158, 255, 32, 0, 0, 0, 0, 64, 255, 128, 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, 59, 64, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 38, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 160, 20, 0, 0, 0, 0, 28, 112, 58, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_net_mode_w = 88; +static const int g_txt_net_mode_h = 28; +static const unsigned char g_txt_net_mode[88*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 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, 0, 0, 0, 0, 0, 0, 56, 176, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 148, 0, 0, 0, 0, 0, 110, 248, 29, 0, 91, 224, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 215, 57, 0, 18, 39, 0, 0, 0, 0, 0, + 0, 16, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 35, 0, 0, 0, 0, 0, 207, 162, 17, 0, 0, 0, 0, 129, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 235, 201, 0, 0, 9, 2, 0, 103, 255, 18, 0, 95, 255, 54, 0, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 57, 21, 224, 227, 28, 0, 0, 0, 0, + 0, 3, 254, 234, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 224, 255, 21, 0, 0, 0, 0, 34, 255, 199, 2, 0, 0, 0, 0, 219, 247, 83, 76, 76, 76, 76, 76, 52, 0, 0, 0, 0, 0, 0, 228, 196, 0, 0, 168, 255, 253, 253, 255, 252, 252, 253, 255, 252, 253, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 54, 0, 137, 255, 211, 13, 0, 0, 0, + 0, 0, 246, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 12, 0, 0, 0, 0, 133, 255, 57, 0, 0, 0, 0, 77, 255, 255, 255, 255, 255, 255, 255, 255, 91, 0, 0, 0, 0, 0, 0, 224, 196, 0, 0, 47, 62, 57, 134, 255, 68, 56, 127, 255, 93, 57, 67, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 55, 0, 0, 177, 202, 38, 0, 0, 0, + 0, 0, 240, 200, 0, 1, 0, 0, 0, 150, 139, 29, 0, 0, 0, 98, 126, 15, 176, 255, 8, 0, 0, 0, 7, 234, 179, 0, 42, 67, 0, 0, 205, 255, 201, 20, 20, 20, 46, 248, 211, 2, 0, 0, 0, 20, 19, 16, 226, 199, 19, 29, 6, 0, 0, 67, 172, 10, 0, 70, 196, 36, 0, 0, 0, 0, 0, 98, 115, 108, 105, 104, 104, 104, 104, 104, 104, 206, 255, 138, 104, 104, 117, 113, 116, 123, 3, 0, + 0, 0, 240, 200, 80, 222, 62, 0, 0, 234, 235, 15, 4, 0, 0, 172, 255, 70, 176, 255, 8, 0, 0, 0, 112, 254, 49, 0, 144, 255, 83, 90, 255, 222, 255, 60, 0, 0, 165, 255, 71, 0, 0, 0, 0, 160, 255, 255, 255, 255, 255, 255, 44, 86, 164, 164, 164, 164, 164, 164, 164, 164, 164, 94, 0, 0, 0, 204, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, + 0, 0, 240, 200, 29, 240, 201, 1, 45, 255, 136, 134, 223, 11, 1, 235, 215, 0, 176, 255, 8, 0, 0, 33, 241, 166, 0, 16, 242, 215, 39, 236, 210, 22, 240, 203, 4, 71, 255, 168, 0, 0, 0, 0, 0, 47, 66, 69, 254, 209, 64, 74, 13, 124, 255, 144, 144, 144, 144, 144, 144, 144, 240, 138, 0, 0, 0, 42, 45, 40, 38, 36, 36, 36, 36, 36, 36, 129, 255, 121, 36, 36, 36, 41, 47, 53, 1, 0, + 0, 0, 240, 200, 0, 99, 255, 97, 115, 255, 44, 112, 255, 124, 50, 255, 122, 0, 176, 255, 8, 0, 19, 223, 255, 154, 124, 173, 255, 70, 95, 241, 47, 0, 115, 255, 157, 235, 216, 14, 0, 0, 0, 0, 0, 0, 0, 76, 255, 196, 0, 0, 0, 119, 255, 0, 0, 0, 0, 0, 0, 0, 220, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 1, 197, 235, 203, 208, 0, 2, 198, 241, 145, 255, 34, 0, 176, 255, 8, 0, 2, 229, 247, 211, 203, 255, 181, 0, 0, 19, 0, 0, 3, 211, 255, 247, 42, 0, 0, 0, 0, 0, 0, 0, 0, 165, 255, 196, 76, 9, 0, 116, 255, 228, 228, 228, 228, 228, 228, 228, 252, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 255, 172, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 0, 51, 254, 255, 115, 0, 0, 58, 255, 255, 196, 0, 0, 176, 255, 8, 0, 0, 37, 5, 0, 131, 250, 39, 0, 0, 0, 0, 26, 153, 254, 253, 255, 183, 56, 0, 0, 0, 0, 0, 0, 17, 246, 255, 222, 249, 138, 0, 119, 255, 80, 80, 80, 80, 80, 80, 80, 231, 132, 0, 0, 0, 0, 140, 226, 221, 220, 220, 220, 224, 231, 196, 0, 235, 224, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 0, 0, 191, 255, 62, 0, 0, 0, 180, 255, 107, 0, 0, 176, 255, 8, 0, 0, 0, 0, 29, 244, 140, 0, 0, 4, 69, 164, 249, 252, 144, 21, 128, 247, 255, 213, 132, 62, 6, 0, 0, 133, 250, 240, 196, 132, 252, 51, 122, 255, 36, 36, 36, 36, 36, 36, 36, 225, 136, 0, 0, 0, 0, 102, 163, 159, 172, 255, 212, 162, 168, 143, 0, 168, 255, 27, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 0, 33, 250, 255, 196, 1, 0, 3, 213, 255, 175, 0, 0, 176, 255, 8, 0, 0, 0, 0, 180, 231, 14, 0, 0, 181, 255, 255, 175, 45, 0, 0, 0, 29, 143, 242, 255, 241, 19, 0, 30, 244, 160, 224, 196, 10, 231, 117, 133, 255, 255, 255, 255, 255, 255, 255, 255, 255, 144, 0, 0, 0, 0, 0, 0, 0, 40, 255, 144, 0, 0, 0, 0, 96, 255, 102, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 0, 161, 248, 165, 255, 97, 0, 107, 255, 232, 255, 59, 0, 176, 255, 8, 0, 0, 2, 137, 255, 161, 117, 158, 175, 55, 160, 172, 216, 216, 216, 216, 216, 216, 216, 205, 111, 109, 0, 8, 204, 248, 32, 224, 196, 0, 25, 0, 8, 16, 16, 16, 146, 253, 18, 16, 16, 16, 9, 0, 0, 0, 0, 0, 0, 0, 40, 255, 144, 0, 0, 0, 0, 24, 254, 186, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 51, 254, 144, 18, 242, 192, 31, 241, 215, 51, 254, 200, 1, 176, 255, 8, 0, 0, 77, 255, 255, 255, 255, 248, 191, 0, 0, 139, 255, 139, 136, 136, 136, 136, 233, 206, 0, 0, 0, 16, 186, 138, 0, 224, 196, 0, 0, 38, 41, 35, 32, 32, 173, 237, 32, 32, 32, 33, 38, 35, 0, 0, 0, 0, 0, 0, 40, 255, 144, 0, 0, 0, 0, 0, 202, 254, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 4, 204, 241, 20, 0, 77, 19, 203, 254, 65, 0, 175, 255, 90, 176, 255, 8, 0, 0, 7, 179, 131, 79, 34, 1, 0, 0, 0, 133, 255, 8, 0, 0, 0, 0, 208, 190, 0, 0, 0, 0, 10, 16, 0, 224, 196, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 40, 255, 144, 0, 0, 0, 0, 0, 98, 255, 152, 0, 0, 0, 14, 0, 0, + 0, 0, 240, 200, 136, 255, 110, 0, 0, 4, 178, 255, 148, 0, 0, 44, 112, 17, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 48, 18, 0, 132, 255, 8, 0, 0, 0, 0, 208, 188, 0, 0, 0, 0, 0, 0, 0, 224, 196, 0, 0, 27, 27, 23, 20, 92, 255, 149, 248, 154, 20, 22, 26, 25, 0, 0, 0, 0, 0, 0, 40, 255, 148, 54, 116, 181, 162, 0, 8, 226, 252, 46, 0, 1, 229, 111, 5, + 0, 0, 240, 200, 59, 156, 2, 0, 0, 2, 74, 180, 8, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 30, 88, 152, 223, 255, 69, 0, 132, 255, 8, 0, 0, 0, 0, 208, 188, 0, 0, 0, 0, 0, 0, 0, 224, 196, 0, 0, 0, 0, 0, 42, 227, 202, 2, 121, 255, 120, 0, 0, 0, 0, 0, 0, 0, 20, 69, 139, 255, 255, 255, 248, 199, 106, 0, 0, 89, 255, 218, 14, 24, 255, 250, 7, + 0, 0, 243, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 8, 0, 0, 76, 231, 255, 255, 240, 165, 89, 15, 0, 132, 255, 201, 200, 200, 200, 200, 245, 188, 0, 0, 0, 0, 0, 0, 0, 225, 196, 0, 0, 0, 9, 111, 241, 210, 24, 0, 0, 164, 255, 162, 20, 0, 0, 0, 61, 232, 255, 255, 255, 205, 128, 60, 7, 0, 0, 0, 0, 0, 166, 255, 199, 134, 255, 200, 0, + 0, 0, 252, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 204, 222, 255, 244, 1, 0, 0, 20, 246, 186, 89, 8, 0, 0, 0, 0, 137, 255, 159, 156, 156, 156, 156, 237, 195, 0, 0, 0, 0, 0, 0, 0, 228, 199, 0, 58, 162, 235, 255, 162, 17, 0, 0, 0, 3, 145, 255, 242, 150, 16, 0, 5, 231, 223, 126, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 187, 255, 255, 255, 115, 0, + 0, 16, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 226, 179, 81, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 145, 255, 8, 0, 0, 0, 0, 208, 204, 0, 0, 0, 0, 0, 0, 0, 212, 185, 0, 17, 221, 183, 61, 0, 0, 0, 0, 0, 0, 0, 79, 212, 143, 0, 0, 0, 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 125, 210, 177, 9, 0, + 0, 1, 16, 12, 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, 61, 104, 3, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_net_device_w = 88; +static const int g_txt_net_device_h = 28; +static const unsigned char g_txt_net_device[88*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 10, 0, 0, 0, 0, 0, 0, 56, 176, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 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, 148, 224, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 35, 0, 0, 0, 0, 0, 207, 162, 17, 0, 0, 0, 0, 129, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 255, 210, 19, 0, 0, 0, 90, 240, 240, 240, 240, 240, 240, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 251, 194, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 254, 234, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 224, 255, 21, 0, 0, 0, 0, 34, 255, 199, 2, 0, 0, 0, 0, 219, 247, 83, 76, 76, 76, 76, 76, 52, 0, 0, 0, 0, 0, 119, 255, 199, 9, 0, 0, 98, 255, 148, 112, 112, 112, 249, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 207, 255, 184, 164, 164, 164, 164, 164, 164, 164, 164, 69, 0, 0, 0, 0, + 0, 0, 246, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 255, 12, 0, 0, 0, 0, 133, 255, 57, 0, 0, 0, 0, 77, 255, 255, 255, 255, 255, 255, 255, 255, 91, 0, 0, 0, 0, 0, 0, 149, 255, 152, 0, 0, 118, 255, 57, 0, 0, 0, 244, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 182, 144, 144, 144, 144, 144, 144, 160, 255, 223, 10, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 1, 0, 0, 0, 150, 139, 29, 0, 0, 0, 98, 126, 15, 176, 255, 8, 0, 0, 0, 7, 234, 179, 0, 42, 67, 0, 0, 205, 255, 201, 20, 20, 20, 46, 248, 211, 2, 0, 0, 0, 0, 0, 0, 5, 147, 23, 0, 0, 149, 255, 31, 0, 0, 0, 244, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 254, 219, 37, 0, 0, 0, 0, 7, 191, 248, 52, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 80, 222, 62, 0, 0, 234, 235, 15, 4, 0, 0, 172, 255, 70, 176, 255, 8, 0, 0, 0, 112, 254, 49, 0, 144, 255, 83, 90, 255, 222, 255, 60, 0, 0, 165, 255, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 217, 235, 1, 0, 0, 0, 244, 196, 0, 0, 0, 0, 0, 0, 0, 3, 147, 255, 187, 50, 226, 235, 64, 0, 0, 10, 183, 252, 87, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 29, 240, 201, 1, 45, 255, 136, 134, 223, 11, 1, 235, 215, 0, 176, 255, 8, 0, 0, 33, 241, 166, 0, 16, 242, 215, 39, 236, 210, 22, 240, 203, 4, 71, 255, 168, 0, 0, 0, 0, 2, 38, 30, 24, 24, 16, 0, 0, 108, 255, 141, 0, 0, 0, 0, 244, 231, 50, 33, 47, 33, 0, 0, 0, 29, 201, 171, 7, 0, 30, 212, 252, 125, 36, 203, 250, 92, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 99, 255, 97, 115, 255, 44, 112, 255, 124, 50, 255, 122, 0, 176, 255, 8, 0, 19, 223, 255, 154, 124, 173, 255, 70, 95, 241, 47, 0, 115, 255, 157, 235, 216, 14, 0, 0, 0, 0, 12, 255, 255, 255, 255, 166, 0, 100, 252, 216, 13, 0, 0, 0, 0, 223, 255, 255, 255, 255, 124, 0, 0, 0, 0, 6, 2, 0, 0, 0, 15, 196, 255, 253, 237, 64, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 1, 197, 235, 203, 208, 0, 2, 198, 241, 145, 255, 34, 0, 176, 255, 8, 0, 2, 229, 247, 211, 203, 255, 181, 0, 0, 19, 0, 0, 3, 211, 255, 247, 42, 0, 0, 0, 0, 0, 5, 110, 100, 104, 255, 157, 0, 84, 206, 30, 0, 0, 0, 0, 0, 39, 110, 116, 116, 116, 60, 0, 0, 0, 0, 0, 0, 0, 10, 71, 167, 250, 254, 250, 253, 181, 101, 39, 2, 0, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 0, 51, 254, 255, 115, 0, 0, 58, 255, 255, 196, 0, 0, 176, 255, 8, 0, 0, 37, 5, 0, 131, 250, 39, 0, 0, 0, 0, 26, 153, 254, 253, 255, 183, 56, 0, 0, 0, 0, 0, 0, 0, 12, 255, 153, 0, 29, 168, 157, 153, 152, 152, 152, 152, 152, 152, 152, 82, 0, 0, 0, 2, 38, 67, 103, 148, 195, 249, 255, 230, 136, 34, 24, 130, 228, 255, 255, 245, 214, 186, 171, 172, 16, + 0, 0, 240, 200, 0, 0, 0, 191, 255, 62, 0, 0, 0, 180, 255, 107, 0, 0, 176, 255, 8, 0, 0, 0, 0, 29, 244, 140, 0, 0, 4, 69, 164, 249, 252, 144, 21, 128, 247, 255, 213, 132, 62, 6, 0, 0, 0, 12, 255, 152, 0, 34, 200, 242, 253, 200, 200, 200, 200, 200, 244, 255, 61, 0, 0, 0, 1, 182, 255, 255, 242, 188, 124, 51, 1, 0, 0, 0, 0, 0, 45, 108, 153, 192, 228, 253, 184, 0, + 0, 0, 240, 200, 0, 0, 33, 250, 255, 196, 1, 0, 3, 213, 255, 175, 0, 0, 176, 255, 8, 0, 0, 0, 0, 180, 231, 14, 0, 0, 181, 255, 255, 175, 45, 0, 0, 0, 29, 143, 242, 255, 241, 19, 0, 0, 0, 12, 255, 152, 0, 0, 0, 134, 255, 35, 0, 0, 0, 18, 247, 210, 0, 0, 0, 0, 0, 41, 102, 46, 107, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 126, 0, 5, 12, 0, + 0, 0, 240, 200, 0, 0, 161, 248, 165, 255, 97, 0, 107, 255, 232, 255, 59, 0, 176, 255, 8, 0, 0, 2, 137, 255, 161, 117, 158, 175, 55, 160, 172, 216, 216, 216, 216, 216, 216, 216, 205, 111, 109, 0, 0, 0, 0, 12, 255, 152, 0, 0, 0, 36, 253, 134, 0, 0, 0, 125, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 100, 255, 116, 84, 84, 84, 215, 218, 84, 84, 84, 100, 255, 123, 0, 0, 0, 0, + 0, 0, 240, 200, 0, 51, 254, 144, 18, 242, 192, 31, 241, 215, 51, 254, 200, 1, 176, 255, 8, 0, 0, 77, 255, 255, 255, 255, 248, 191, 0, 0, 139, 255, 139, 136, 136, 136, 136, 233, 206, 0, 0, 0, 0, 0, 0, 12, 255, 152, 0, 34, 0, 0, 168, 243, 32, 0, 28, 241, 208, 2, 0, 0, 0, 0, 0, 0, 0, 0, 96, 255, 48, 0, 0, 0, 196, 200, 0, 0, 0, 24, 255, 118, 0, 0, 0, 0, + 0, 0, 240, 200, 4, 204, 241, 20, 0, 77, 19, 203, 254, 65, 0, 175, 255, 90, 176, 255, 8, 0, 0, 7, 179, 131, 79, 34, 1, 0, 0, 0, 133, 255, 8, 0, 0, 0, 0, 208, 190, 0, 0, 0, 0, 0, 0, 12, 255, 152, 88, 220, 5, 0, 34, 246, 203, 20, 192, 253, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 174, 156, 156, 156, 232, 234, 156, 156, 156, 165, 255, 116, 0, 0, 0, 0, + 0, 0, 240, 200, 136, 255, 110, 0, 0, 4, 178, 255, 148, 0, 0, 44, 112, 17, 176, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 48, 18, 0, 132, 255, 8, 0, 0, 0, 0, 208, 188, 0, 0, 0, 0, 0, 0, 12, 255, 215, 251, 240, 35, 0, 0, 105, 255, 241, 255, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 187, 172, 172, 172, 236, 237, 172, 172, 172, 179, 255, 116, 0, 0, 0, 0, + 0, 0, 240, 200, 59, 156, 2, 0, 0, 2, 74, 180, 8, 0, 0, 0, 0, 0, 176, 255, 8, 0, 0, 0, 0, 30, 88, 152, 223, 255, 69, 0, 132, 255, 8, 0, 0, 0, 0, 208, 188, 0, 0, 0, 0, 0, 0, 34, 255, 255, 235, 49, 0, 0, 0, 31, 234, 255, 244, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 48, 0, 0, 0, 196, 200, 0, 0, 0, 24, 255, 116, 0, 0, 0, 0, + 0, 0, 243, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 8, 0, 0, 76, 231, 255, 255, 240, 165, 89, 15, 0, 132, 255, 201, 200, 200, 200, 200, 245, 188, 0, 0, 0, 0, 0, 0, 122, 255, 241, 45, 0, 0, 11, 120, 240, 245, 161, 245, 251, 163, 67, 6, 0, 0, 0, 0, 0, 0, 0, 96, 255, 119, 88, 88, 88, 216, 219, 88, 88, 88, 103, 255, 118, 0, 0, 0, 0, + 0, 0, 252, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 204, 222, 255, 244, 1, 0, 0, 20, 246, 186, 89, 8, 0, 0, 0, 0, 137, 255, 159, 156, 156, 156, 156, 237, 195, 0, 0, 0, 0, 0, 0, 49, 240, 75, 14, 73, 153, 241, 253, 161, 28, 0, 28, 151, 246, 255, 248, 205, 161, 3, 0, 0, 0, 0, 99, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 122, 0, 0, 0, 0, + 0, 16, 255, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 226, 179, 81, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 145, 255, 8, 0, 0, 0, 0, 208, 204, 0, 0, 0, 0, 0, 0, 0, 28, 0, 72, 246, 242, 155, 40, 0, 0, 0, 0, 0, 19, 106, 190, 248, 103, 0, 0, 0, 0, 0, 106, 255, 54, 8, 8, 8, 8, 8, 8, 8, 8, 31, 255, 129, 0, 0, 0, 0, + 0, 1, 16, 12, 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, 61, 104, 3, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 0, 0, 0, 0, 0, 17, 40, 7, 0, 0, 0, 0, 0, 0, 0, 0, 3, 40, 21, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_net_ip_w = 88; +static const int g_txt_net_ip_h = 28; +static const unsigned char g_txt_net_ip[88*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 116, 76, 0, 0, 0, 0, 0, 0, 0, 121, 248, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 92, 45, 0, 0, 0, 0, 0, 0, 0, 0, 144, 180, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 133, 0, 0, 0, 0, 164, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 49, 255, 160, 0, 0, 0, 0, 0, 0, 0, 118, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 121, 0, 0, 0, 0, 0, 0, 0, 0, 201, 255, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 201, 0, 0, 0, 0, 221, 205, 0, 0, 0, 0, 0, 0, 0, 0, 112, 203, 198, 196, 196, 196, 196, 196, 197, 202, 128, 14, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 40, 255, 154, 0, 0, 0, 100, 164, 13, 0, 111, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 194, 255, 3, 0, 0, 0, 0, 0, 0, 2, 158, 149, 145, 144, 144, 241, 230, 144, 144, 144, 144, 239, 231, 144, 144, 145, 150, 158, 5, 0, 0, 80, 141, 233, 224, 136, 136, 136, 173, 255, 168, 92, 248, 255, 254, 252, 252, 252, 252, 224, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 151, 255, 15, 0, 108, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 3, 226, 214, 209, 208, 208, 249, 244, 208, 208, 208, 208, 248, 245, 208, 208, 211, 220, 228, 7, 0, 0, 0, 0, 208, 188, 0, 0, 0, 80, 255, 60, 0, 137, 255, 145, 100, 100, 100, 238, 203, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 144, 255, 8, 0, 108, 255, 56, 0, 0, 43, 78, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 38, 104, 52, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 226, 205, 0, 0, 0, 0, 226, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 188, 0, 0, 0, 80, 255, 60, 0, 51, 255, 90, 0, 0, 0, 246, 175, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 141, 255, 1, 0, 108, 255, 103, 129, 213, 255, 160, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 86, 255, 118, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 136, 114, 0, 0, 0, 0, 125, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 188, 0, 0, 0, 80, 255, 60, 0, 32, 255, 111, 0, 0, 23, 255, 144, 0, 0, + 0, 170, 204, 207, 255, 233, 203, 211, 17, 140, 255, 0, 53, 182, 255, 255, 255, 208, 255, 154, 0, 0, 0, 226, 220, 230, 255, 235, 223, 234, 18, 76, 255, 105, 0, 0, 188, 252, 45, 47, 51, 60, 18, 0, 0, 0, 18, 213, 121, 1, 39, 223, 69, 0, 0, 0, 0, 179, 124, 0, 73, 94, 0, 0, 0, 0, 0, 0, 0, 208, 246, 220, 220, 220, 231, 255, 60, 0, 11, 255, 134, 0, 0, 60, 255, 106, 0, 0, + 0, 152, 181, 187, 255, 223, 179, 188, 15, 147, 255, 215, 255, 255, 255, 134, 30, 8, 255, 152, 0, 0, 0, 183, 176, 199, 255, 195, 155, 167, 13, 73, 255, 93, 0, 0, 188, 255, 255, 255, 255, 255, 72, 0, 0, 0, 20, 158, 255, 191, 235, 222, 62, 0, 0, 0, 0, 246, 196, 2, 195, 253, 75, 0, 0, 0, 0, 0, 0, 208, 216, 108, 108, 108, 154, 255, 60, 0, 0, 237, 158, 0, 0, 106, 255, 63, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 109, 209, 255, 255, 201, 108, 128, 255, 56, 0, 8, 255, 152, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 72, 255, 88, 0, 0, 188, 253, 74, 76, 80, 89, 26, 0, 0, 0, 0, 0, 176, 255, 227, 20, 0, 0, 0, 0, 0, 242, 184, 0, 18, 224, 238, 16, 0, 0, 0, 0, 0, 208, 188, 0, 0, 0, 80, 255, 60, 0, 0, 205, 189, 0, 0, 169, 254, 14, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 100, 219, 206, 255, 0, 0, 108, 255, 56, 0, 8, 255, 152, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 0, 29, 177, 255, 212, 253, 48, 2, 39, 31, 23, 20, 242, 186, 20, 20, 78, 107, 34, 1, 0, 0, 0, 0, 208, 188, 0, 0, 0, 80, 255, 60, 0, 0, 162, 239, 1, 1, 231, 201, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 5, 3, 140, 255, 0, 0, 108, 255, 56, 0, 7, 255, 151, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 89, 249, 228, 76, 16, 243, 156, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 0, 0, 0, 0, 208, 226, 144, 144, 144, 179, 255, 60, 0, 0, 99, 255, 47, 70, 255, 121, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 140, 255, 0, 0, 108, 255, 56, 0, 18, 255, 148, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 6, 114, 14, 0, 75, 252, 233, 6, 92, 86, 79, 107, 255, 236, 76, 76, 76, 79, 89, 3, 0, 0, 0, 0, 208, 243, 208, 208, 208, 223, 255, 60, 0, 0, 32, 254, 138, 175, 255, 39, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 0, 0, 140, 255, 0, 0, 108, 255, 70, 92, 176, 255, 111, 0, 0, 0, 0, 0, 76, 255, 108, 0, 0, 0, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 245, 209, 255, 25, 0, 0, 0, 99, 255, 255, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 188, 0, 0, 0, 80, 255, 60, 0, 0, 0, 185, 234, 252, 177, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 152, 0, 1, 12, 140, 255, 0, 0, 109, 255, 62, 227, 255, 193, 16, 0, 0, 0, 0, 0, 76, 255, 108, 8, 80, 29, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 249, 157, 71, 255, 58, 0, 0, 0, 194, 224, 242, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 188, 0, 0, 0, 80, 255, 63, 56, 39, 0, 73, 255, 255, 53, 0, 0, 0, 0, + 0, 0, 0, 32, 255, 178, 125, 221, 77, 140, 255, 0, 0, 114, 255, 60, 76, 59, 0, 0, 0, 0, 0, 0, 0, 90, 255, 212, 242, 255, 38, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 46, 185, 255, 138, 2, 67, 255, 59, 0, 0, 64, 255, 120, 141, 245, 35, 0, 0, 0, 0, 0, 0, 0, 0, 208, 188, 0, 2, 45, 143, 255, 248, 255, 95, 0, 92, 255, 254, 65, 0, 0, 0, 0, + 0, 15, 84, 169, 255, 255, 254, 195, 36, 140, 255, 0, 0, 104, 216, 59, 0, 0, 0, 95, 28, 0, 18, 149, 214, 255, 255, 255, 203, 115, 6, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 63, 233, 87, 0, 0, 102, 255, 49, 0, 2, 197, 236, 16, 25, 239, 204, 11, 0, 0, 0, 0, 0, 0, 27, 218, 229, 190, 243, 255, 243, 255, 140, 50, 3, 31, 239, 231, 233, 229, 34, 0, 0, 0, + 0, 205, 255, 255, 200, 112, 28, 0, 0, 139, 255, 10, 0, 0, 0, 0, 0, 0, 3, 236, 254, 25, 5, 242, 255, 221, 123, 36, 0, 0, 0, 72, 255, 88, 0, 0, 188, 252, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 166, 250, 12, 0, 135, 255, 99, 0, 0, 88, 255, 192, 15, 0, 0, 0, 0, 136, 255, 255, 248, 181, 101, 34, 81, 255, 60, 0, 13, 208, 254, 70, 66, 252, 225, 55, 0, 0, + 0, 83, 150, 38, 0, 0, 0, 0, 0, 119, 255, 130, 45, 44, 44, 44, 44, 46, 127, 255, 195, 0, 0, 116, 71, 1, 0, 0, 81, 169, 163, 185, 255, 190, 156, 156, 229, 254, 156, 156, 160, 166, 172, 24, 0, 0, 0, 0, 1, 109, 254, 166, 4, 140, 255, 171, 0, 0, 0, 0, 136, 255, 216, 53, 0, 0, 0, 57, 217, 108, 18, 0, 0, 0, 82, 255, 61, 21, 200, 255, 139, 0, 0, 102, 255, 251, 124, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 241, 255, 255, 255, 255, 255, 255, 255, 255, 240, 58, 0, 0, 0, 0, 0, 0, 0, 113, 234, 225, 222, 220, 220, 220, 220, 220, 220, 220, 220, 225, 232, 239, 34, 0, 0, 25, 159, 225, 255, 199, 35, 197, 255, 199, 11, 0, 0, 0, 0, 0, 140, 255, 238, 35, 0, 0, 0, 2, 0, 0, 0, 0, 0, 86, 255, 66, 182, 255, 165, 2, 0, 0, 0, 109, 254, 194, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 71, 76, 76, 76, 76, 76, 76, 65, 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, 203, 186, 92, 1, 0, 138, 176, 13, 0, 0, 0, 0, 0, 0, 0, 102, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 71, 7, 118, 3, 0, 0, 0, 0, 0, 66, 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, 1, 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, 41, 104, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_net_dns_w = 33; +static const int g_txt_net_dns_h = 28; +static const unsigned char g_txt_net_dns[33*28] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 32, 32, 31, 19, 2, 0, 0, 0, 0, 0, 4, 32, 32, 1, 0, 0, 0, 5, 32, 25, 0, 0, 0, 0, 3, 51, 69, 40, 0, 0, 0, 0, + 12, 255, 255, 255, 255, 244, 181, 61, 0, 0, 0, 36, 255, 255, 74, 0, 0, 0, 44, 255, 204, 0, 0, 0, 71, 225, 255, 255, 255, 207, 34, 0, 0, + 12, 255, 246, 128, 142, 201, 255, 254, 104, 0, 0, 36, 255, 255, 187, 0, 0, 0, 44, 255, 204, 0, 0, 55, 250, 247, 149, 121, 198, 255, 203, 1, 0, + 12, 255, 236, 0, 0, 0, 95, 254, 249, 37, 0, 36, 255, 255, 255, 45, 0, 0, 44, 255, 204, 0, 0, 174, 255, 94, 0, 0, 4, 202, 255, 71, 0, + 12, 255, 236, 0, 0, 0, 0, 164, 255, 138, 0, 36, 255, 253, 255, 158, 0, 0, 44, 255, 204, 0, 0, 216, 255, 34, 0, 0, 0, 111, 232, 109, 0, + 12, 255, 236, 0, 0, 0, 0, 75, 255, 203, 0, 36, 255, 216, 208, 248, 23, 0, 44, 255, 204, 0, 0, 188, 255, 102, 0, 0, 0, 0, 0, 0, 0, + 12, 255, 236, 0, 0, 0, 0, 29, 255, 244, 0, 36, 255, 216, 95, 255, 129, 0, 44, 255, 204, 0, 0, 76, 255, 250, 138, 23, 0, 0, 0, 0, 0, + 12, 255, 236, 0, 0, 0, 0, 9, 255, 255, 5, 36, 255, 216, 6, 231, 234, 8, 44, 255, 204, 0, 0, 0, 99, 242, 255, 250, 156, 31, 0, 0, 0, + 12, 255, 236, 0, 0, 0, 0, 3, 255, 255, 11, 36, 255, 216, 0, 124, 255, 100, 44, 255, 204, 0, 0, 0, 0, 20, 125, 232, 255, 246, 90, 0, 0, + 12, 255, 236, 0, 0, 0, 0, 15, 255, 252, 0, 36, 255, 216, 0, 19, 246, 212, 44, 255, 204, 0, 0, 0, 0, 0, 0, 9, 129, 253, 255, 82, 0, + 12, 255, 236, 0, 0, 0, 0, 47, 255, 223, 0, 36, 255, 216, 0, 0, 152, 255, 114, 255, 204, 0, 4, 20, 15, 0, 0, 0, 0, 110, 255, 207, 0, + 12, 255, 236, 0, 0, 0, 0, 111, 255, 168, 0, 36, 255, 216, 0, 0, 39, 254, 227, 255, 204, 0, 53, 255, 198, 0, 0, 0, 0, 14, 255, 242, 0, + 12, 255, 236, 0, 0, 0, 16, 224, 255, 72, 0, 36, 255, 216, 0, 0, 0, 181, 255, 255, 204, 0, 25, 255, 239, 9, 0, 0, 0, 49, 255, 215, 0, + 12, 255, 237, 16, 29, 86, 214, 255, 170, 0, 0, 36, 255, 216, 0, 0, 0, 67, 255, 255, 204, 0, 0, 190, 255, 169, 18, 0, 33, 201, 255, 127, 0, + 12, 255, 255, 255, 255, 255, 250, 144, 5, 0, 0, 36, 255, 216, 0, 0, 0, 0, 208, 255, 204, 0, 0, 32, 222, 255, 253, 239, 255, 255, 183, 7, 0, + 6, 144, 144, 143, 132, 97, 26, 0, 0, 0, 0, 20, 144, 121, 0, 0, 0, 0, 67, 144, 114, 0, 0, 0, 13, 111, 170, 183, 158, 83, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const int g_txt_net_hint_w = 352; +static const int g_txt_net_hint_h = 22; +static const unsigned char g_txt_net_hint[352*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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 16, 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, 15, 128, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 53, 0, 0, 0, 151, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 58, 63, 0, 177, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 0, 0, 0, 0, 0, 3, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 227, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 181, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 178, 4, 0, 0, 0, 0, 0, 71, 198, 0, 0, 0, 0, 0, 0, 15, 128, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 53, 0, 0, 0, 151, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 0, 0, 0, 0, 0, 3, 11, 0, 0, 0, 23, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 101, 117, 145, 192, 105, 23, 121, 113, 112, 112, 112, 13, 0, 0, 0, 0, 0, 206, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 58, 63, 0, 177, 154, 0, 0, 0, 0, 0, 0, 0, 61, 255, 28, 0, 49, 155, 147, 144, 144, 144, 144, 141, 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, 23, 255, 22, 0, 31, 122, 115, 112, 112, 112, 112, 112, 82, 0, 0, 0, 0, 242, 35, 0, 0, 31, 248, 93, 32, 32, 32, 8, 0, 0, 0, 0, 191, 130, 0, 0, 181, 178, 0, 182, 149, 0, 0, 0, 0, 0, 20, 104, 97, 96, 100, 54, 205, 246, 240, 245, 255, 240, 240, 244, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 241, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 218, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 150, 0, 95, 130, 128, 23, 145, 175, 228, 144, 144, 40, 0, 0, 0, 23, 255, 22, 0, 31, 122, 115, 112, 112, 112, 112, 112, 82, 0, 0, 0, 0, 242, 35, 0, 0, 31, 248, 93, 32, 32, 32, 8, 0, 0, 20, 104, 97, 96, 100, 54, 205, 246, 240, 245, 255, 240, 240, 244, 252, 0, 0, 0, 91, 252, 192, 192, 192, 192, 192, 192, 192, 192, 252, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 255, 104, 89, 77, 61, 26, 139, 130, 128, 136, 255, 23, 0, 0, 0, 0, 0, 212, 84, 0, 31, 250, 242, 240, 240, 240, 240, 245, 95, 0, 0, 0, 0, 0, 0, 0, 18, 255, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 130, 0, 0, 181, 178, 0, 182, 149, 0, 0, 0, 0, 0, 0, 0, 56, 255, 23, 0, 35, 175, 211, 112, 112, 112, 226, 159, 0, 0, + 32, 255, 255, 255, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 39, 156, 150, 214, 213, 144, 144, 201, 177, 0, 0, 0, 0, 240, 32, 0, 0, 174, 218, 192, 192, 236, 215, 9, 0, 0, 0, 0, 112, 231, 4, 9, 244, 183, 144, 221, 208, 150, 155, 109, 0, 0, 29, 154, 194, 228, 147, 78, 0, 0, 0, 99, 224, 0, 0, 0, 0, 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, 91, 0, 0, 0, 0, 0, 0, 0, 0, 17, 96, 96, 96, 229, 177, 96, 96, 96, 96, 96, 96, 96, 59, 0, 0, 70, 255, 196, 187, 96, 156, 241, 5, 100, 148, 216, 96, 236, 55, 0, 0, 0, 16, 255, 16, 0, 39, 156, 150, 214, 213, 144, 144, 201, 177, 0, 0, 0, 0, 240, 32, 0, 0, 174, 218, 192, 192, 236, 215, 9, 0, 0, 29, 154, 194, 228, 147, 78, 0, 0, 0, 99, 224, 0, 0, 0, 0, 0, 0, 0, 84, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 32, 32, 34, 37, 2, 42, 34, 32, 46, 255, 16, 0, 0, 0, 0, 0, 208, 80, 0, 7, 54, 37, 32, 32, 32, 40, 53, 23, 0, 0, 0, 0, 0, 0, 0, 16, 255, 244, 240, 240, 240, 241, 249, 79, 0, 0, 0, 58, 57, 48, 48, 48, 178, 165, 48, 48, 48, 53, 49, 0, 0, 0, 0, 112, 231, 4, 9, 244, 183, 144, 221, 208, 150, 155, 109, 0, 0, 3, 23, 65, 255, 35, 23, 0, 28, 242, 29, 0, 77, 238, 24, 0, 0, + 32, 255, 113, 48, 48, 48, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 0, 0, 0, 161, 159, 0, 0, 140, 163, 0, 35, 135, 129, 248, 147, 105, 107, 226, 17, 0, 27, 241, 53, 0, 0, 0, 0, 0, 12, 233, 50, 106, 228, 96, 96, 206, 186, 98, 101, 74, 0, 0, 0, 0, 112, 192, 0, 0, 3, 16, 16, 137, 175, 16, 16, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 229, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 226, 233, 0, 0, 42, 255, 200, 192, 192, 192, 192, 192, 192, 192, 192, 192, 236, 153, 0, 19, 223, 136, 83, 91, 0, 98, 168, 66, 96, 146, 216, 96, 236, 131, 20, 0, 0, 16, 255, 16, 0, 0, 0, 0, 161, 159, 0, 0, 140, 163, 0, 35, 135, 129, 248, 147, 105, 107, 226, 17, 0, 27, 241, 53, 0, 0, 0, 0, 0, 112, 192, 0, 0, 3, 16, 16, 137, 175, 16, 16, 15, 0, 0, 0, 0, 82, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 208, 208, 213, 192, 14, 219, 210, 208, 211, 255, 16, 0, 0, 26, 138, 132, 232, 169, 136, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 88, 32, 32, 32, 34, 42, 14, 0, 0, 0, 190, 226, 224, 224, 224, 244, 242, 224, 224, 224, 230, 192, 0, 0, 0, 0, 12, 233, 50, 106, 228, 96, 96, 206, 186, 98, 101, 74, 0, 0, 32, 255, 255, 255, 255, 224, 0, 0, 130, 196, 82, 239, 72, 0, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 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, 0, 0, 0, 0, 0, 0, 21, 255, 76, 99, 102, 0, 0, 165, 153, 0, 0, 152, 151, 0, 31, 119, 114, 247, 133, 157, 253, 130, 80, 80, 176, 195, 80, 80, 0, 0, 0, 0, 0, 5, 0, 99, 111, 0, 0, 176, 144, 0, 0, 0, 0, 0, 0, 0, 112, 192, 0, 0, 44, 255, 226, 224, 224, 224, 230, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 67, 64, 64, 245, 123, 64, 64, 64, 64, 64, 64, 66, 74, 0, 0, 35, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 146, 0, 66, 170, 0, 0, 5, 1, 176, 88, 96, 144, 179, 228, 144, 242, 183, 32, 0, 0, 21, 255, 76, 99, 102, 0, 0, 165, 153, 0, 0, 152, 151, 0, 31, 119, 114, 247, 133, 157, 253, 130, 80, 80, 176, 195, 80, 80, 0, 0, 0, 0, 112, 192, 0, 0, 44, 255, 226, 224, 224, 224, 230, 251, 0, 0, 0, 0, 80, 248, 128, 128, 128, 128, 128, 128, 128, 128, 248, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 17, 0, 0, 32, 159, 166, 255, 181, 153, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 16, 16, 31, 255, 76, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 99, 111, 0, 0, 176, 144, 0, 0, 0, 0, 0, 1, 8, 49, 255, 20, 10, 0, 0, 18, 232, 255, 151, 0, 0, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 10, 80, 10, 45, 102, 76, 1, 0, 45, 80, 157, 223, 80, 80, 30, 0, 0, 0, 24, 86, 91, 26, 0, 0, 0, 30, 70, 0, 19, 88, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 239, 255, 255, 213, 183, 132, 0, 0, 180, 139, 0, 0, 164, 139, 0, 0, 0, 0, 240, 32, 3, 133, 232, 160, 179, 249, 160, 166, 255, 0, 0, 3, 58, 51, 49, 8, 102, 120, 112, 112, 211, 193, 112, 112, 118, 70, 0, 0, 0, 112, 192, 0, 0, 36, 255, 16, 0, 0, 0, 48, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 32, 0, 15, 16, 16, 16, 16, 16, 1, 0, 176, 144, 0, 0, 64, 248, 237, 247, 67, 246, 15, 0, 24, 93, 196, 16, 226, 49, 0, 59, 239, 255, 255, 213, 183, 132, 0, 0, 180, 139, 0, 0, 164, 139, 0, 0, 0, 0, 240, 32, 3, 133, 232, 160, 179, 249, 160, 166, 255, 0, 0, 0, 0, 112, 192, 0, 0, 36, 255, 16, 0, 0, 0, 48, 243, 0, 0, 0, 0, 80, 248, 128, 128, 128, 128, 128, 128, 128, 128, 248, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 255, 192, 192, 192, 192, 192, 192, 192, 192, 196, 255, 24, 0, 0, 0, 0, 83, 255, 80, 0, 164, 211, 208, 208, 208, 208, 208, 209, 216, 27, 0, 0, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 144, 0, 0, 0, 0, 0, 0, 0, 3, 58, 51, 49, 8, 102, 120, 112, 112, 211, 193, 112, 112, 118, 70, 0, 0, 0, 48, 255, 16, 22, 100, 159, 239, 172, 108, 231, 227, 169, 134, 25, + 32, 255, 168, 128, 128, 128, 72, 0, 32, 255, 140, 216, 158, 242, 155, 0, 99, 176, 211, 241, 176, 176, 66, 0, 0, 80, 238, 181, 189, 241, 70, 0, 0, 96, 224, 82, 245, 240, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 57, 43, 255, 16, 0, 0, 0, 0, 207, 112, 0, 0, 177, 128, 0, 0, 0, 0, 240, 61, 71, 80, 192, 0, 61, 231, 0, 16, 255, 0, 0, 14, 220, 214, 255, 42, 117, 148, 146, 251, 169, 200, 221, 146, 150, 88, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 40, 177, 31, 48, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 254, 114, 64, 64, 64, 64, 64, 64, 14, 0, 0, 0, 32, 255, 32, 0, 234, 246, 240, 240, 244, 255, 26, 0, 176, 144, 0, 0, 3, 130, 128, 0, 167, 254, 240, 94, 211, 236, 248, 224, 224, 49, 0, 15, 57, 43, 255, 16, 0, 0, 0, 0, 207, 112, 0, 0, 177, 128, 0, 0, 0, 0, 240, 61, 71, 80, 192, 0, 61, 231, 0, 16, 255, 0, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 40, 177, 31, 48, 240, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 96, 53, 79, 50, 67, 106, 48, 74, 48, 59, 50, 5, 0, 0, 0, 0, 156, 255, 182, 115, 64, 70, 64, 64, 244, 112, 64, 66, 75, 9, 0, 0, 7, 255, 80, 0, 0, 0, 0, 0, 0, 64, 255, 6, 0, 0, 53, 133, 128, 128, 128, 128, 128, 208, 200, 128, 128, 128, 128, 128, 133, 53, 14, 220, 214, 255, 42, 117, 148, 146, 251, 169, 200, 221, 146, 150, 88, 0, 0, 0, 48, 255, 186, 91, 169, 139, 43, 86, 137, 7, 85, 158, 169, 0, + 32, 255, 201, 176, 176, 176, 99, 0, 32, 255, 203, 11, 0, 102, 243, 0, 0, 0, 112, 208, 0, 0, 0, 0, 16, 238, 102, 0, 0, 156, 208, 0, 0, 96, 242, 216, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 0, 0, 4, 244, 72, 0, 0, 189, 117, 0, 0, 0, 34, 245, 241, 103, 80, 192, 0, 76, 221, 0, 16, 255, 0, 0, 0, 0, 16, 255, 36, 0, 0, 6, 255, 39, 128, 176, 0, 0, 0, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 57, 255, 24, 48, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 255, 229, 224, 224, 224, 224, 230, 255, 39, 0, 0, 0, 32, 255, 32, 0, 225, 96, 0, 0, 64, 255, 17, 0, 176, 144, 0, 0, 8, 128, 137, 24, 30, 21, 187, 76, 0, 80, 192, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 0, 0, 4, 244, 72, 0, 0, 189, 117, 0, 0, 0, 34, 245, 241, 103, 80, 192, 0, 76, 221, 0, 16, 255, 0, 0, 0, 0, 112, 192, 0, 0, 32, 255, 16, 57, 255, 24, 48, 240, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 255, 9, 181, 167, 70, 216, 47, 236, 73, 154, 167, 0, 0, 0, 0, 11, 239, 252, 171, 241, 21, 0, 0, 0, 240, 64, 0, 1, 0, 0, 0, 0, 1, 255, 80, 0, 0, 0, 0, 0, 0, 64, 255, 1, 0, 0, 53, 134, 129, 128, 128, 128, 128, 208, 200, 128, 128, 128, 128, 129, 133, 53, 0, 0, 16, 255, 36, 0, 0, 6, 255, 39, 128, 176, 0, 0, 0, 0, 42, 148, 228, 255, 133, 19, 16, 22, 18, 130, 215, 19, 24, 23, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 78, 0, 0, 64, 255, 0, 0, 0, 112, 208, 0, 0, 0, 0, 68, 255, 212, 208, 208, 227, 254, 5, 0, 96, 255, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 16, 0, 33, 0, 63, 251, 16, 0, 0, 201, 105, 0, 31, 181, 242, 252, 47, 31, 103, 200, 32, 110, 217, 32, 46, 255, 37, 17, 0, 0, 16, 255, 32, 0, 0, 55, 245, 9, 128, 176, 0, 0, 0, 0, 0, 0, 112, 192, 32, 61, 32, 255, 16, 66, 238, 0, 48, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 255, 16, 0, 0, 0, 0, 63, 255, 14, 0, 0, 0, 32, 255, 32, 0, 224, 96, 0, 0, 64, 255, 16, 0, 176, 144, 0, 0, 218, 249, 241, 220, 56, 50, 205, 83, 250, 247, 252, 240, 247, 31, 0, 0, 0, 16, 255, 16, 0, 33, 0, 63, 251, 16, 0, 0, 201, 105, 0, 31, 181, 242, 252, 47, 31, 103, 200, 32, 110, 217, 32, 46, 255, 37, 17, 0, 0, 112, 192, 32, 61, 32, 255, 16, 66, 238, 0, 48, 240, 0, 0, 0, 0, 80, 251, 176, 176, 176, 176, 176, 176, 176, 176, 251, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 0, 17, 84, 64, 208, 0, 67, 36, 143, 153, 0, 0, 0, 0, 121, 213, 209, 80, 216, 96, 130, 149, 0, 240, 64, 141, 137, 0, 0, 0, 0, 6, 255, 80, 0, 0, 0, 0, 0, 0, 64, 255, 5, 0, 0, 0, 0, 11, 31, 0, 0, 0, 160, 144, 0, 0, 0, 47, 17, 0, 0, 0, 0, 16, 255, 32, 0, 0, 55, 245, 9, 128, 176, 0, 0, 0, 0, 39, 220, 131, 255, 16, 0, 141, 245, 241, 247, 253, 242, 246, 205, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 37, 0, 0, 64, 255, 0, 0, 0, 112, 208, 0, 0, 0, 0, 81, 255, 58, 48, 48, 48, 48, 2, 0, 96, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 26, 141, 241, 11, 159, 188, 0, 0, 0, 214, 94, 0, 4, 135, 25, 240, 32, 164, 209, 208, 208, 237, 255, 222, 208, 208, 213, 83, 0, 0, 16, 255, 32, 0, 0, 157, 164, 0, 128, 176, 0, 85, 114, 0, 9, 66, 174, 250, 252, 123, 34, 255, 16, 103, 185, 0, 48, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 215, 0, 0, 0, 0, 0, 87, 245, 0, 0, 0, 0, 32, 255, 32, 0, 229, 136, 64, 64, 112, 255, 20, 0, 176, 144, 0, 0, 0, 128, 128, 0, 127, 154, 238, 18, 11, 82, 192, 0, 3, 1, 0, 0, 0, 16, 255, 26, 141, 241, 11, 159, 188, 0, 0, 0, 214, 94, 0, 4, 135, 25, 240, 32, 164, 209, 208, 208, 237, 255, 222, 208, 208, 213, 83, 9, 66, 174, 250, 252, 123, 34, 255, 16, 103, 185, 0, 48, 241, 0, 0, 0, 0, 80, 245, 80, 80, 80, 80, 80, 80, 80, 80, 245, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 32, 223, 71, 64, 208, 78, 180, 21, 129, 159, 0, 0, 0, 45, 244, 87, 208, 80, 16, 0, 219, 135, 0, 240, 64, 112, 251, 47, 0, 0, 0, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, 0, 0, 0, 86, 245, 0, 0, 0, 160, 144, 0, 0, 0, 244, 84, 0, 0, 0, 0, 16, 255, 32, 0, 0, 157, 164, 0, 128, 176, 0, 85, 114, 0, 0, 4, 48, 255, 16, 0, 0, 0, 0, 112, 208, 0, 0, 0, 0, 0, + 32, 255, 80, 0, 0, 0, 0, 0, 32, 255, 32, 0, 0, 64, 255, 0, 0, 0, 105, 217, 0, 0, 0, 0, 37, 255, 66, 0, 0, 64, 172, 6, 0, 96, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 255, 227, 206, 49, 42, 250, 82, 0, 0, 0, 226, 82, 0, 0, 0, 0, 240, 32, 0, 0, 0, 11, 230, 218, 163, 0, 0, 0, 0, 0, 0, 16, 255, 32, 0, 102, 243, 32, 0, 123, 227, 101, 201, 185, 0, 44, 255, 194, 96, 20, 0, 43, 255, 16, 193, 121, 37, 48, 248, 0, 0, 0, 0, 78, 96, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 228, 130, 0, 0, 0, 0, 0, 110, 217, 0, 0, 0, 0, 32, 255, 32, 0, 177, 192, 192, 192, 192, 192, 21, 0, 176, 144, 0, 0, 0, 128, 128, 49, 54, 232, 222, 82, 182, 201, 236, 176, 177, 105, 0, 0, 0, 38, 255, 227, 206, 49, 42, 250, 82, 0, 0, 0, 226, 82, 0, 0, 0, 0, 240, 32, 0, 0, 0, 11, 230, 218, 163, 0, 0, 0, 0, 44, 255, 194, 96, 20, 0, 43, 255, 16, 193, 121, 37, 48, 248, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 78, 96, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 0, 106, 210, 68, 208, 14, 176, 133, 100, 180, 0, 0, 0, 90, 172, 0, 208, 80, 0, 64, 251, 27, 0, 240, 64, 3, 209, 196, 0, 0, 0, 39, 21, 16, 16, 16, 16, 16, 17, 16, 16, 16, 5, 0, 0, 0, 0, 80, 240, 0, 0, 0, 160, 144, 0, 0, 0, 240, 80, 0, 0, 0, 0, 16, 255, 32, 0, 102, 243, 32, 0, 123, 227, 101, 201, 185, 0, 0, 0, 48, 255, 16, 48, 53, 50, 48, 139, 217, 50, 54, 58, 61, 0, + 32, 255, 168, 128, 128, 128, 128, 32, 32, 255, 32, 0, 0, 64, 255, 0, 0, 0, 47, 252, 112, 94, 127, 0, 0, 157, 228, 108, 100, 224, 162, 0, 0, 96, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 251, 128, 5, 13, 204, 192, 0, 0, 0, 0, 240, 71, 0, 0, 0, 0, 240, 32, 0, 0, 6, 183, 198, 22, 234, 99, 0, 0, 0, 0, 0, 32, 255, 71, 48, 236, 82, 0, 0, 38, 188, 208, 202, 59, 0, 0, 35, 0, 0, 0, 0, 8, 48, 119, 235, 49, 245, 166, 40, 0, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 241, 21, 0, 0, 0, 0, 0, 133, 189, 0, 0, 0, 0, 32, 255, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 144, 0, 0, 0, 128, 195, 244, 63, 193, 176, 34, 73, 126, 208, 64, 66, 43, 0, 0, 0, 121, 251, 128, 5, 13, 204, 192, 0, 0, 0, 0, 240, 71, 0, 0, 0, 0, 240, 32, 0, 0, 6, 183, 198, 22, 234, 99, 0, 0, 0, 0, 35, 0, 0, 0, 0, 8, 48, 119, 235, 49, 245, 166, 40, 0, 0, 0, 0, 80, 240, 0, 0, 0, 0, 0, 0, 0, 0, 240, 96, 0, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 0, 31, 35, 64, 208, 0, 27, 32, 62, 228, 1, 122, 22, 0, 3, 0, 208, 80, 0, 195, 166, 0, 0, 240, 64, 0, 87, 255, 71, 0, 5, 232, 138, 1, 164, 63, 0, 61, 204, 6, 0, 115, 179, 0, 0, 0, 0, 80, 240, 0, 0, 0, 160, 144, 0, 0, 0, 240, 80, 0, 0, 0, 0, 32, 255, 71, 48, 236, 82, 0, 0, 38, 188, 208, 202, 59, 0, 0, 0, 48, 255, 16, 178, 213, 211, 209, 229, 247, 210, 211, 214, 221, 0, + 22, 176, 176, 176, 176, 176, 176, 44, 22, 176, 22, 0, 0, 44, 176, 0, 0, 0, 0, 81, 185, 202, 141, 0, 0, 2, 107, 183, 190, 117, 6, 0, 0, 66, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 52, 0, 20, 196, 225, 23, 0, 34, 85, 132, 255, 46, 0, 0, 0, 3, 244, 31, 0, 49, 207, 208, 20, 0, 64, 247, 142, 11, 0, 0, 51, 227, 193, 225, 177, 105, 44, 16, 0, 0, 0, 4, 22, 50, 15, 0, 0, 0, 0, 0, 0, 35, 162, 236, 51, 0, 71, 236, 200, 21, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 254, 86, 0, 0, 0, 0, 0, 18, 212, 155, 0, 0, 0, 0, 40, 255, 32, 0, 0, 0, 0, 0, 0, 0, 23, 25, 196, 140, 0, 0, 0, 185, 248, 82, 33, 235, 210, 169, 63, 86, 192, 0, 0, 0, 0, 0, 0, 11, 52, 0, 20, 196, 225, 23, 0, 34, 85, 132, 255, 46, 0, 0, 0, 3, 244, 31, 0, 49, 207, 208, 20, 0, 64, 247, 142, 11, 0, 0, 0, 0, 0, 0, 0, 35, 162, 236, 51, 0, 71, 236, 200, 21, 0, 0, 0, 83, 252, 192, 192, 192, 192, 192, 192, 192, 192, 252, 99, 0, 0, 0, 0, 208, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 255, 158, 246, 90, 90, 230, 173, 247, 112, 8, 239, 132, 254, 62, 0, 0, 0, 213, 84, 16, 167, 48, 14, 39, 250, 59, 0, 3, 70, 0, 0, 91, 249, 29, 0, 177, 175, 0, 17, 239, 115, 0, 48, 252, 87, 0, 0, 0, 86, 248, 128, 128, 128, 208, 200, 128, 128, 128, 248, 80, 0, 0, 0, 51, 227, 193, 225, 177, 105, 44, 16, 0, 0, 0, 4, 22, 50, 15, 0, 75, 141, 254, 10, 0, 0, 0, 0, 113, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 206, 226, 36, 0, 0, 32, 255, 252, 171, 0, 0, 0, 100, 214, 237, 14, 184, 250, 143, 8, 0, 0, 0, 66, 236, 231, 25, 3, 203, 156, 0, 6, 91, 169, 222, 251, 254, 251, 251, 255, 255, 228, 3, 0, 0, 0, 0, 22, 193, 254, 178, 25, 0, 0, 0, 33, 218, 171, 0, 0, 0, 0, 182, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 252, 105, 0, 0, 0, 0, 105, 226, 251, 241, 56, 0, 0, 0, 0, 46, 255, 32, 0, 0, 0, 0, 0, 0, 0, 138, 255, 253, 77, 0, 0, 0, 145, 71, 30, 230, 137, 6, 122, 214, 252, 247, 220, 218, 236, 55, 0, 0, 0, 0, 9, 206, 226, 36, 0, 0, 32, 255, 252, 171, 0, 0, 0, 100, 214, 237, 14, 184, 250, 143, 8, 0, 0, 0, 66, 236, 231, 25, 0, 0, 0, 0, 22, 193, 254, 178, 25, 0, 0, 0, 33, 218, 171, 0, 0, 0, 90, 244, 64, 64, 64, 64, 64, 64, 64, 64, 244, 105, 0, 0, 0, 0, 0, 182, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 255, 167, 35, 0, 87, 244, 134, 28, 0, 0, 85, 253, 185, 0, 0, 0, 0, 219, 90, 0, 0, 0, 158, 255, 226, 15, 0, 0, 0, 0, 8, 221, 154, 0, 0, 80, 252, 23, 0, 128, 233, 5, 0, 153, 230, 9, 0, 0, 46, 128, 128, 128, 128, 128, 128, 128, 128, 128, 248, 87, 0, 0, 3, 203, 156, 0, 6, 91, 169, 222, 251, 254, 251, 251, 255, 255, 228, 3, 0, 148, 254, 165, 0, 0, 0, 0, 0, 120, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 31, 0, 0, 0, 0, 54, 12, 0, 0, 0, 0, 31, 94, 22, 0, 68, 34, 0, 0, 0, 0, 0, 0, 22, 72, 0, 0, 11, 6, 0, 0, 0, 0, 0, 3, 21, 31, 36, 37, 35, 23, 0, 0, 0, 0, 0, 0, 97, 60, 0, 0, 0, 0, 0, 0, 28, 17, 0, 0, 0, 77, 215, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 88, 0, 0, 0, 0, 0, 34, 119, 77, 15, 0, 0, 0, 0, 0, 12, 64, 8, 0, 0, 0, 0, 0, 0, 0, 23, 61, 19, 0, 0, 0, 0, 0, 0, 0, 81, 1, 0, 0, 0, 9, 40, 58, 66, 65, 2, 0, 0, 0, 0, 0, 21, 31, 0, 0, 0, 0, 54, 12, 0, 0, 0, 0, 31, 94, 22, 0, 68, 34, 0, 0, 0, 0, 0, 0, 22, 72, 0, 0, 0, 0, 0, 0, 97, 60, 0, 0, 0, 0, 0, 0, 28, 17, 0, 0, 0, 29, 75, 0, 0, 0, 0, 0, 0, 0, 0, 90, 41, 0, 0, 0, 0, 77, 215, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 78, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 35, 13, 0, 0, 0, 0, 97, 41, 0, 0, 0, 34, 58, 5, 0, 0, 0, 0, 0, 0, 73, 29, 0, 0, 11, 50, 0, 0, 18, 39, 0, 0, 29, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 41, 0, 0, 0, 11, 6, 0, 0, 0, 0, 0, 3, 21, 31, 36, 37, 35, 23, 0, 0, 25, 19, 0, 0, 0, 0, 0, 0, 92, 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, 39, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_privacy_title_w = 120; +static const int g_txt_privacy_title_h = 30; +static const unsigned char g_txt_privacy_title[120*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 153, 61, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 96, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 54, 149, 73, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 80, 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, 1, 209, 255, 172, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 55, 95, 139, 188, 241, 255, 51, 0, 0, 0, 37, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 203, 184, 6, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 255, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 36, 0, 0, + 0, 68, 232, 232, 232, 232, 232, 232, 68, 0, 0, 90, 255, 253, 125, 112, 112, 112, 112, 62, 0, 0, 0, 0, 0, 14, 202, 237, 255, 255, 255, 255, 252, 218, 192, 111, 0, 0, 0, 113, 253, 189, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 218, 255, 169, 1, 0, 0, 0, 0, 0, 13, 244, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 12, 112, 93, 63, 162, 61, 12, 255, 171, 0, 93, 141, 29, 50, 75, 88, 117, 151, 199, 250, 206, 2, 0, + 0, 65, 255, 175, 104, 104, 237, 245, 9, 0, 16, 231, 255, 224, 224, 224, 229, 255, 248, 38, 0, 0, 0, 0, 0, 0, 182, 211, 165, 125, 255, 206, 6, 0, 0, 0, 0, 0, 0, 152, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179, 255, 58, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 11, 17, 20, 0, 0, 0, 0, 0, 33, 235, 255, 130, 0, 0, 0, 0, 0, 119, 255, 153, 220, 228, 12, 0, 0, 0, 0, 0, 0, 0, 20, 255, 206, 72, 255, 122, 8, 255, 168, 0, 185, 252, 52, 176, 255, 255, 243, 221, 200, 178, 161, 48, 0, + 0, 60, 255, 120, 0, 16, 254, 174, 0, 4, 185, 255, 137, 0, 0, 0, 117, 255, 118, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 255, 200, 0, 0, 0, 0, 0, 0, 0, 199, 255, 105, 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, 192, 0, 0, 0, 0, 0, 0, 70, 253, 200, 4, 0, 0, 0, 21, 237, 242, 23, 86, 255, 160, 0, 0, 0, 0, 0, 0, 0, 12, 255, 197, 20, 255, 164, 8, 255, 168, 33, 251, 165, 0, 161, 255, 14, 0, 0, 0, 0, 0, 0, 0, + 0, 58, 255, 120, 0, 72, 255, 94, 4, 164, 255, 187, 3, 0, 0, 7, 232, 221, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 200, 0, 0, 0, 0, 0, 0, 5, 247, 255, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 245, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 151, 159, 124, 0, 0, 0, 0, 0, 0, 0, 88, 12, 0, 0, 0, 0, 171, 255, 114, 0, 0, 167, 255, 144, 3, 0, 0, 0, 0, 0, 8, 255, 192, 0, 229, 207, 8, 255, 168, 157, 255, 54, 0, 152, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 255, 120, 0, 131, 254, 22, 134, 255, 236, 229, 208, 202, 200, 211, 255, 232, 200, 200, 200, 32, 0, 0, 0, 0, 0, 0, 0, 0, 255, 200, 0, 0, 0, 0, 0, 0, 59, 255, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 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, 121, 255, 208, 4, 0, 0, 12, 205, 255, 207, 55, 0, 0, 0, 0, 8, 255, 192, 0, 87, 69, 8, 255, 168, 47, 125, 0, 0, 146, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 255, 120, 0, 200, 209, 0, 3, 104, 63, 155, 148, 141, 136, 136, 136, 136, 136, 207, 255, 30, 0, 0, 0, 97, 152, 146, 144, 144, 255, 231, 144, 145, 149, 130, 0, 0, 125, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 255, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 255, 241, 41, 0, 21, 140, 4, 21, 201, 255, 254, 200, 111, 0, 0, 8, 255, 192, 18, 37, 28, 32, 255, 176, 26, 31, 39, 0, 144, 255, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 255, 120, 18, 253, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 25, 0, 0, 0, 152, 255, 255, 255, 255, 255, 255, 255, 255, 255, 208, 0, 0, 191, 255, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 250, 241, 233, 232, 232, 78, 28, 178, 255, 247, 66, 0, 2, 186, 255, 149, 0, 9, 148, 254, 255, 85, 0, 0, 8, 255, 192, 104, 255, 255, 255, 255, 255, 255, 255, 240, 0, 145, 255, 116, 112, 112, 112, 112, 116, 100, 0, + 0, 56, 255, 120, 46, 255, 188, 1, 0, 0, 148, 169, 157, 152, 152, 152, 152, 152, 152, 213, 255, 24, 0, 0, 0, 16, 16, 11, 8, 189, 255, 201, 8, 9, 13, 20, 0, 8, 248, 243, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 255, 153, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 20, 0, 0, 11, 171, 162, 153, 223, 255, 67, 115, 246, 244, 69, 0, 3, 164, 255, 206, 21, 0, 0, 0, 54, 134, 0, 0, 0, 8, 255, 192, 41, 94, 84, 238, 255, 196, 86, 95, 109, 0, 146, 255, 255, 255, 255, 255, 255, 255, 200, 0, + 0, 56, 255, 120, 0, 154, 255, 104, 0, 0, 158, 193, 188, 184, 184, 184, 184, 184, 184, 226, 255, 24, 0, 0, 0, 0, 0, 0, 21, 252, 255, 200, 0, 19, 0, 0, 0, 67, 255, 174, 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 172, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 39, 0, 0, 0, 0, 0, 0, 176, 255, 55, 0, 39, 51, 0, 17, 178, 255, 199, 16, 0, 73, 148, 2, 0, 0, 0, 0, 0, 8, 255, 192, 0, 0, 53, 255, 255, 168, 47, 46, 0, 0, 149, 255, 21, 16, 16, 248, 166, 22, 26, 0, + 0, 56, 255, 120, 0, 15, 240, 232, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 25, 0, 0, 0, 0, 0, 0, 120, 255, 255, 200, 148, 227, 21, 0, 0, 132, 255, 97, 0, 60, 209, 106, 0, 0, 0, 0, 0, 0, 0, 0, 36, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 238, 253, 6, 0, 0, 0, 0, 0, 0, 176, 255, 49, 0, 0, 0, 75, 225, 255, 182, 12, 0, 15, 223, 255, 130, 0, 0, 0, 0, 0, 8, 255, 192, 0, 0, 167, 248, 255, 175, 235, 207, 4, 0, 154, 255, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 56, 255, 120, 0, 0, 153, 255, 65, 0, 114, 136, 130, 128, 128, 128, 128, 128, 128, 204, 255, 30, 0, 0, 0, 0, 0, 10, 234, 245, 255, 200, 134, 255, 187, 2, 0, 205, 253, 22, 0, 57, 255, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 254, 226, 0, 0, 0, 0, 0, 0, 0, 176, 255, 48, 0, 0, 83, 255, 255, 135, 2, 0, 8, 190, 255, 205, 16, 0, 0, 0, 0, 0, 8, 255, 192, 0, 47, 253, 156, 255, 168, 113, 255, 118, 0, 161, 248, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 56, 255, 120, 0, 0, 108, 255, 98, 0, 196, 241, 236, 232, 232, 232, 232, 232, 232, 232, 232, 37, 0, 0, 0, 0, 0, 125, 255, 145, 255, 200, 6, 215, 255, 126, 25, 254, 197, 0, 0, 0, 209, 255, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 196, 0, 0, 0, 0, 0, 0, 0, 176, 255, 48, 0, 0, 0, 138, 85, 0, 0, 19, 193, 255, 213, 20, 0, 17, 0, 0, 0, 0, 8, 255, 192, 3, 199, 251, 43, 255, 168, 4, 217, 246, 34, 175, 232, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 56, 255, 120, 0, 20, 174, 255, 87, 0, 0, 0, 0, 0, 0, 30, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 248, 241, 25, 255, 200, 0, 86, 209, 42, 103, 255, 118, 0, 0, 0, 107, 255, 174, 0, 0, 0, 0, 4, 35, 28, 21, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 23, 30, 0, 58, 255, 167, 0, 0, 0, 0, 0, 0, 0, 176, 255, 48, 0, 0, 0, 0, 0, 0, 70, 230, 255, 209, 22, 0, 16, 211, 132, 0, 0, 0, 8, 255, 192, 130, 255, 145, 14, 255, 168, 0, 93, 243, 70, 201, 213, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 56, 255, 120, 160, 255, 255, 231, 19, 2, 0, 0, 0, 0, 78, 253, 107, 0, 0, 8, 101, 4, 0, 0, 0, 7, 200, 255, 108, 0, 255, 200, 0, 1, 9, 0, 185, 255, 39, 0, 0, 0, 15, 246, 252, 29, 0, 0, 0, 32, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 0, 87, 255, 137, 0, 0, 0, 0, 0, 0, 0, 176, 255, 48, 92, 46, 0, 0, 48, 164, 254, 255, 181, 17, 0, 8, 194, 255, 249, 66, 0, 0, 8, 255, 192, 125, 222, 12, 20, 255, 171, 0, 3, 35, 0, 235, 182, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 56, 255, 120, 37, 219, 132, 24, 81, 224, 94, 83, 240, 113, 2, 227, 220, 1, 0, 117, 255, 111, 0, 0, 0, 160, 255, 190, 1, 0, 255, 200, 0, 0, 0, 23, 251, 216, 0, 0, 0, 0, 0, 164, 255, 132, 0, 0, 0, 23, 180, 173, 166, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 166, 156, 0, 117, 255, 108, 0, 0, 0, 0, 0, 0, 0, 176, 255, 122, 251, 218, 2, 95, 255, 255, 245, 114, 1, 0, 10, 184, 255, 234, 56, 0, 0, 0, 8, 255, 192, 0, 15, 0, 26, 255, 178, 0, 0, 0, 35, 255, 146, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 56, 255, 120, 0, 0, 0, 0, 174, 255, 61, 77, 255, 107, 0, 142, 211, 25, 0, 9, 225, 238, 18, 0, 0, 76, 237, 31, 0, 0, 255, 200, 0, 0, 0, 132, 255, 136, 0, 0, 0, 12, 45, 134, 255, 228, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 78, 0, 0, 0, 0, 0, 0, 0, 176, 255, 255, 255, 115, 0, 2, 187, 177, 34, 0, 0, 31, 202, 255, 222, 37, 0, 0, 0, 0, 12, 255, 192, 0, 0, 0, 14, 120, 88, 0, 0, 0, 101, 255, 96, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 57, 255, 120, 0, 0, 0, 33, 251, 190, 0, 72, 255, 104, 0, 6, 0, 60, 108, 22, 89, 255, 146, 0, 0, 0, 29, 0, 0, 0, 255, 200, 0, 0, 85, 250, 255, 175, 169, 202, 235, 255, 255, 255, 255, 255, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 255, 48, 0, 0, 0, 0, 0, 0, 0, 184, 255, 255, 150, 0, 0, 0, 5, 0, 0, 1, 100, 238, 255, 207, 25, 0, 0, 0, 0, 0, 18, 255, 242, 200, 200, 200, 200, 200, 200, 207, 216, 83, 199, 254, 29, 0, 0, 0, 248, 160, 0, 0, 0, + 0, 60, 255, 120, 0, 0, 0, 167, 255, 63, 0, 72, 255, 118, 0, 0, 0, 120, 255, 136, 1, 209, 208, 6, 0, 0, 0, 0, 0, 2, 255, 200, 0, 0, 90, 255, 255, 255, 255, 226, 184, 143, 105, 70, 158, 255, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 245, 254, 17, 0, 0, 0, 0, 0, 0, 31, 242, 255, 176, 2, 0, 0, 0, 0, 6, 92, 208, 255, 252, 146, 10, 0, 0, 0, 0, 0, 0, 20, 184, 184, 184, 184, 184, 184, 184, 185, 188, 196, 154, 255, 174, 0, 0, 0, 2, 254, 162, 0, 0, 0, + 0, 67, 255, 120, 0, 0, 29, 232, 191, 0, 0, 62, 255, 239, 148, 136, 156, 243, 255, 47, 0, 25, 1, 0, 0, 0, 0, 0, 0, 5, 255, 204, 0, 0, 6, 191, 130, 69, 17, 0, 0, 0, 0, 0, 70, 208, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 218, 225, 235, 255, 255, 185, 0, 0, 0, 0, 0, 0, 0, 91, 249, 212, 10, 0, 0, 49, 115, 171, 242, 255, 252, 169, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 226, 251, 41, 0, 0, 0, 17, 255, 171, 0, 0, 0, + 0, 73, 248, 116, 0, 0, 0, 7, 26, 0, 0, 11, 196, 249, 255, 255, 245, 216, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 255, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 155, 255, 255, 219, 142, 16, 0, 0, 0, 0, 0, 0, 0, 0, 52, 42, 0, 0, 0, 37, 243, 255, 231, 135, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 228, 134, 0, 0, 0, 0, 32, 255, 193, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 88, 75, 0, 0, 0, 0, 0, 0, 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, 43, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 70, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 7, 0, 0, 0, 0, 2, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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_done_w = 240; +static const int g_txt_done_h = 30; +static const unsigned char g_txt_done[240*30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 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, 0, 0, 0, 0, 0, 53, 127, 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, 139, 117, 7, 0, 70, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 104, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 166, 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, 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, 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, 0, 0, 0, 0, 16, 241, 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, 4, 255, 255, 3, 40, 249, 229, 50, 0, 0, 0, 0, 0, 0, 0, 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, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 38, 68, 105, 147, 199, 192, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 206, 255, 136, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 107, 255, 230, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 249, 0, 0, 120, 255, 241, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 232, 247, 76, 0, 0, 25, 127, 120, 116, 113, 112, 194, 255, 145, 112, 113, 116, 120, 128, 16, 0, 0, 0, 0, 82, 143, 150, 160, 172, 185, 201, 219, 241, 255, 255, 255, 255, 255, 255, 247, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 255, 247, 26, 0, 0, 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, 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, 66, 224, 224, 224, 224, 224, 224, 224, 224, 226, 255, 255, 232, 224, 224, 224, 224, 224, 224, 224, 224, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, 248, 0, 0, 0, 118, 255, 204, 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, 42, 233, 250, 68, 0, 46, 241, 234, 229, 226, 225, 242, 255, 231, 225, 226, 229, 234, 241, 30, 0, 0, 0, 0, 60, 255, 245, 226, 209, 191, 174, 162, 255, 235, 111, 87, 62, 39, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 152, 152, 152, 152, 152, 152, 152, 246, 255, 196, 152, 152, 152, 152, 152, 50, 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, 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, 65, 255, 244, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 251, 255, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 253, 0, 0, 0, 1, 118, 30, 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, 0, 0, 0, 0, 0, 0, 57, 250, 241, 25, 0, 0, 0, 0, 0, 0, 144, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 8, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 238, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 249, 255, 76, 0, 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, 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, 59, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 245, 0, 0, 0, 0, 0, 159, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 109, 0, 0, 127, 195, 189, 185, 184, 224, 255, 199, 184, 185, 189, 195, 114, 0, 0, 0, 78, 148, 140, 136, 136, 136, 136, 136, 136, 139, 255, 237, 136, 136, 136, 136, 136, 136, 136, 140, 149, 78, 0, 0, 0, 0, 0, 86, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 68, 0, 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, 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, 69, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 6, 0, 0, 0, 0, 157, 255, 178, 152, 152, 152, 152, 152, 152, 152, 222, 255, 162, 152, 152, 154, 157, 164, 172, 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, 92, 140, 134, 131, 129, 200, 255, 156, 129, 131, 134, 141, 83, 0, 0, 0, 128, 255, 252, 248, 248, 248, 248, 248, 248, 248, 255, 254, 248, 248, 248, 248, 248, 248, 249, 253, 255, 128, 0, 0, 0, 0, 0, 83, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 64, 0, 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, 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, 48, 160, 135, 0, 124, 183, 176, 170, 168, 168, 168, 168, 168, 168, 173, 180, 187, 0, 0, 150, 160, 15, 0, 0, 0, 0, 155, 255, 64, 0, 0, 0, 0, 0, 0, 0, 159, 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, 144, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 0, 0, 0, 0, 0, 0, 0, 8, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 81, 255, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 255, 66, 0, 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, 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, 0, 0, 0, 0, 168, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 64, 0, 0, 0, 0, 0, 0, 0, 140, 255, 55, 0, 0, 14, 157, 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, 217, 211, 208, 208, 145, 0, 0, 189, 194, 189, 186, 184, 184, 224, 255, 199, 184, 184, 186, 189, 194, 196, 0, 0, 0, 0, 0, 34, 112, 112, 112, 112, 112, 116, 255, 233, 112, 112, 112, 112, 112, 112, 33, 0, 0, 0, 0, 0, 0, 0, 0, 83, 255, 118, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 202, 255, 71, 0, 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, 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, 0, 0, 0, 0, 19, 24, 17, 10, 8, 8, 8, 8, 8, 8, 14, 21, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 88, 32, 32, 32, 32, 32, 10, 0, 114, 255, 86, 0, 0, 99, 255, 234, 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, 190, 188, 184, 255, 169, 0, 0, 160, 164, 159, 156, 154, 154, 153, 152, 153, 153, 154, 156, 158, 163, 165, 0, 0, 0, 0, 0, 71, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 68, 0, 0, 0, 0, 0, 0, 0, 0, 101, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 97, 91, 61, 43, 57, 195, 255, 214, 62, 0, 0, 45, 150, 18, 0, 0, 0, 0, 0, 0, 96, 130, 91, 61, 216, 253, 55, 0, 0, 1, 157, 255, 90, 0, 0, 0, 41, 239, 253, 79, 0, 0, 0, + 0, 0, 0, 0, 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, 0, 0, 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, 255, 255, 255, 255, 255, 74, 0, 77, 255, 123, 0, 0, 192, 255, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 163, 0, 0, 0, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 38, 0, 0, 0, 0, 0, 0, 0, 62, 255, 151, 16, 16, 16, 23, 255, 218, 16, 16, 16, 16, 151, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 120, 255, 130, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 104, 237, 246, 122, 5, 0, 0, 3, 167, 255, 222, 42, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 142, 0, 0, 44, 183, 255, 143, 0, 2, 18, 38, 62, 153, 255, 241, 36, 0, 0, + 0, 0, 0, 0, 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, 90, 122, 116, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, 120, 127, 79, 0, 0, 0, 0, 153, 255, 171, 144, 144, 144, 215, 255, 53, 0, 39, 255, 169, 0, 37, 254, 233, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 160, 0, 0, 0, 10, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 202, 0, 0, 0, 0, 0, 0, 0, 58, 255, 214, 160, 160, 160, 163, 255, 241, 160, 160, 160, 160, 214, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 176, 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, 161, 255, 58, 0, 0, 0, 172, 255, 33, 0, 2, 241, 220, 0, 171, 255, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 160, 0, 0, 0, 1, 255, 218, 56, 56, 56, 56, 56, 56, 68, 255, 195, 0, 0, 0, 0, 0, 0, 0, 57, 255, 242, 224, 224, 224, 225, 255, 251, 224, 224, 224, 224, 245, 255, 57, 0, 0, 0, 0, 0, 0, 0, 0, 185, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 41, 51, 44, 41, 40, 40, 99, 255, 211, 40, 40, 40, 208, 255, 80, 40, 40, 40, 42, 49, 56, 36, 0, 0, 0, 0, 175, 255, 51, 0, 0, 0, 185, 255, 12, 0, 0, 189, 255, 80, 255, 225, 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, 16, 255, 160, 0, 0, 0, 0, 252, 217, 48, 48, 48, 48, 48, 48, 61, 255, 187, 0, 0, 0, 0, 0, 0, 0, 62, 255, 144, 0, 0, 0, 8, 255, 216, 0, 0, 0, 0, 168, 255, 60, 0, 0, 0, 0, 0, 0, 0, 0, 236, 248, 34, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 144, 0, 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, 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, 0, 0, 0, 105, 255, 174, 0, 0, 0, 200, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 255, 34, 0, 0, 0, 198, 249, 0, 0, 0, 123, 255, 241, 255, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 160, 0, 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 0, 70, 255, 245, 232, 232, 232, 232, 255, 252, 232, 232, 232, 232, 247, 255, 67, 0, 0, 0, 0, 0, 0, 0, 34, 255, 209, 26, 255, 245, 144, 144, 144, 144, 144, 144, 144, 144, 144, 193, 255, 145, 0, 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, 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, 0, 0, 0, 156, 255, 133, 0, 0, 0, 200, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 255, 10, 0, 0, 0, 212, 230, 0, 0, 0, 44, 255, 255, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 160, 0, 60, 25, 0, 248, 214, 32, 32, 32, 32, 32, 32, 46, 255, 184, 0, 0, 0, 0, 0, 0, 0, 46, 152, 152, 152, 152, 152, 155, 255, 239, 152, 152, 152, 152, 152, 152, 45, 0, 0, 0, 0, 0, 0, 0, 92, 255, 154, 20, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 139, 0, 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, 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, 0, 0, 3, 230, 255, 59, 0, 0, 0, 200, 255, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 255, 230, 0, 0, 0, 1, 240, 210, 0, 0, 0, 94, 255, 255, 81, 0, 0, 0, 28, 0, 0, 0, 0, 0, 103, 144, 144, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 255, 160, 53, 239, 170, 0, 248, 212, 24, 24, 24, 24, 24, 24, 38, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 84, 17, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 136, 0, 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, 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, 0, 0, 118, 255, 223, 2, 0, 0, 0, 200, 255, 48, 0, 0, 0, 0, 67, 11, 0, 0, 0, 0, 97, 255, 181, 0, 57, 65, 127, 255, 186, 0, 0, 76, 249, 251, 255, 208, 3, 0, 8, 243, 130, 2, 0, 0, 0, 184, 255, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 255, 209, 243, 245, 84, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 184, 0, 0, 0, 0, 0, 0, 100, 224, 216, 211, 208, 208, 208, 209, 255, 248, 208, 208, 208, 208, 210, 217, 224, 143, 0, 0, 0, 0, 0, 17, 245, 243, 12, 16, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 136, 0, 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, 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, 0, 87, 249, 255, 86, 0, 0, 0, 0, 200, 255, 48, 0, 0, 0, 0, 208, 239, 106, 0, 0, 0, 174, 255, 102, 0, 168, 255, 255, 255, 116, 0, 101, 252, 249, 78, 180, 255, 143, 0, 48, 255, 233, 0, 0, 0, 0, 184, 255, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 227, 50, 0, 0, 249, 218, 56, 56, 56, 56, 56, 56, 68, 255, 184, 0, 0, 0, 0, 0, 0, 65, 144, 135, 131, 128, 128, 128, 132, 255, 236, 128, 128, 128, 128, 130, 137, 144, 93, 0, 0, 0, 0, 0, 122, 255, 152, 0, 19, 255, 237, 56, 56, 56, 56, 56, 56, 56, 56, 56, 143, 255, 136, 0, 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, 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, 18, 144, 254, 255, 138, 0, 0, 0, 0, 0, 199, 255, 99, 5, 1, 11, 76, 254, 255, 74, 0, 0, 42, 253, 251, 20, 0, 72, 187, 150, 86, 17, 166, 255, 240, 68, 0, 36, 243, 255, 137, 132, 255, 180, 0, 0, 0, 0, 184, 255, 255, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 255, 214, 28, 0, 0, 0, 254, 208, 0, 0, 0, 0, 0, 0, 16, 255, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 243, 255, 46, 0, 21, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 138, 0, 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, 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, 62, 152, 243, 255, 248, 113, 0, 0, 0, 0, 0, 0, 167, 255, 255, 255, 255, 255, 255, 255, 191, 1, 0, 4, 202, 255, 152, 0, 0, 0, 0, 4, 109, 234, 255, 213, 41, 0, 0, 0, 84, 252, 255, 255, 255, 110, 0, 0, 0, 0, 0, 4, 252, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 204, 21, 0, 0, 0, 5, 255, 208, 0, 0, 0, 0, 0, 61, 140, 255, 182, 0, 0, 0, 0, 72, 226, 220, 213, 208, 208, 208, 208, 208, 209, 255, 248, 208, 208, 208, 208, 208, 208, 209, 213, 222, 93, 0, 0, 195, 255, 179, 0, 0, 25, 255, 241, 96, 96, 96, 96, 96, 96, 96, 96, 96, 166, 255, 143, 0, 0, 0, 0, 16, 173, 168, 6, 0, 0, 0, 35, 232, 244, 255, 255, 116, 0, 0, 0, 0, 0, 0, 91, 90, 0, 0, 0, 38, 197, 107, 28, 0, 0, 0, 6, 146, 255, 241, 48, 0, 0, 0, 158, 255, 207, 160, 225, 255, 106, 0, + 0, 0, 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, 128, 255, 255, 193, 40, 0, 0, 0, 0, 0, 0, 0, 23, 141, 175, 176, 176, 176, 175, 124, 9, 0, 0, 10, 121, 226, 20, 0, 0, 0, 0, 1, 142, 254, 151, 10, 0, 0, 0, 0, 0, 75, 225, 255, 236, 19, 0, 0, 0, 0, 0, 83, 255, 72, 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, 12, 255, 208, 0, 0, 0, 0, 0, 79, 255, 247, 103, 0, 0, 0, 0, 54, 171, 165, 159, 153, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, 158, 167, 70, 0, 0, 32, 201, 44, 0, 0, 34, 255, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 255, 150, 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, 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, 101, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 1, 53, 0, 0, 0, 0, 0, 0, 0, 0, 3, 42, 14, 0, 0, 0, 0, 0, 60, 238, 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, 2, 40, 32, 0, 0, 0, 0, 0, 5, 61, 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, 4, 0, 0, 0, 9, 64, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 88, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 114, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/CODE/firstInit/text_bitmaps.o b/CODE/firstInit/text_bitmaps.o new file mode 100644 index 0000000..44883d4 Binary files /dev/null and b/CODE/firstInit/text_bitmaps.o differ diff --git a/CODE/font/README b/CODE/font/README new file mode 100644 index 0000000..b086e0e --- /dev/null +++ b/CODE/font/README @@ -0,0 +1,68 @@ +# Deshab Bitmap Font (.dbf) + +`CODE/font` 包含 Deshab 自研位图字体工具和规范。 + +## 文件格式 + +```text +Offset Size Field +0 4 magic "DBF\x10" (version 16) +4 4 u32 glyph_count +8 2 u16 default_width +10 2 u16 default_height +12 2 u16 bits_per_pixel (8=grayscale) +14 N×12 index table (N entries) +... ... glyph bitmap data +``` + +每条索引: +```text +[0..1] u16 codepoint (Unicode BMP) +[2..3] u16 actual_width (pixels) +[4..7] u32 data_offset +[8..11] u32 data_size +``` + +索引按 codepoint 升序排列,可用二分查找。 + +## 字体文件 + +| 文件 | 大小 | 字形数 | 精度 | +|------|------|--------|------| +| [simhei_16.dbf](file:///d:/Code/Deshab/SYSTEM/system/font/simhei_16.dbf) | 1.8MB | 6858 | 16×16 8bpp | +| [simhei_24.dbf](file:///d:/Code/Deshab/SYSTEM/system/font/simhei_24.dbf) | 3.9MB | 6858 | 24×24 8bpp | +| [simhei_32.dbf](file:///d:/Code/Deshab/SYSTEM/system/font/simhei_32.dbf) | 6.9MB | 6858 | 32×32 8bpp | + +字形覆盖:GB2312 全部 6763 个简体汉字 + ASCII 32–126 可打印字符。 + +## 生成工具 + +```bash +python CODE/font/mkfont.py +``` + +依赖:Python 3 + Pillow (`pip install Pillow`) + +## C 头文件 + +[dbf.h](file:///d:/Code/Deshab/CODE/font/dbf.h) 定义数据结构和二分查找函数: + +```c +#include "dbf.h" +const u8 *font = ...; // 加载 .dbf 到内存 +dbf_header *hdr = (dbf_header *)font; +dbf_index_entry *idx = (dbf_index_entry *)(font + 14); +int entry = dbf_lookup(idx, hdr->count, codepoint); +if (entry >= 0) { + u8 *glyph = font + idx[entry].offset; + u16 w = idx[entry].width; + u16 h = hdr->height; + // 逐行渲染 8bpp 灰度位图 +} +``` + +## 使用方式 + +1. 启动时将 `.dbf` 文件通过 boot module 或 FAT32 加载到内存 +2. GBK/GB2312 双字节序列转为 Unicode codepoint 后查表 +3. 8bpp 灰度可直接 alpha-blend 到 framebuffer diff --git a/CODE/font/dbf.h b/CODE/font/dbf.h new file mode 100644 index 0000000..d0610e2 --- /dev/null +++ b/CODE/font/dbf.h @@ -0,0 +1,57 @@ +/* dbf.h — Deshab Bitmap Font format reader + * + * DBF File Layout: + * [0..3] magic "DBF\x10" (version 16) + * [4..7] u32 glyph_count + * [8..9] u16 default_glyph_width + * [10..11] u16 default_glyph_height + * [12..13] u16 bits_per_pixel (8 = grayscale, 1 = BW) + * [14..] index[] array of glyph_count entries: + * [0..1] u16 codepoint (Unicode BMP) + * [2..3] u16 actual_width (pixels) + * [4..7] u32 data_offset (from file start) + * [8..11] u32 data_size (bytes) + * Each index entry is 12 bytes. + * [14 + index*12 ..] glyph_bitmap[] raw 8bpp row-major data + * + * Usage: + * const u8 *font_data = ...; // mmap or load entire .dbf + * dbf_header *hdr = (dbf_header *)font_data; + * dbf_index_entry *idx = (dbf_index_entry *)(font_data + 14); + * // binary search for codepoint in idx[0..hdr->count-1] + * // glyph data at font_data + idx[i].offset, size idx[i].size + */ + +#ifndef DBF_H +#define DBF_H + +#include + +typedef struct { + uint8_t magic[4]; /* "DBF\x10" */ + uint32_t count; + uint16_t width; + uint16_t height; + uint16_t bpp; +} __attribute__((packed)) dbf_header; + +typedef struct { + uint16_t codepoint; + uint16_t width; + uint32_t offset; + uint32_t size; +} __attribute__((packed)) dbf_index_entry; + +/* Binary search for codepoint. Returns entry index or -1. */ +static inline int dbf_lookup(dbf_index_entry *idx, uint32_t count, uint16_t cp) { + int lo = 0, hi = (int)count - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (idx[mid].codepoint == cp) return mid; + if (idx[mid].codepoint < cp) lo = mid + 1; + else hi = mid - 1; + } + return -1; +} + +#endif diff --git a/CODE/font/mkfont.py b/CODE/font/mkfont.py new file mode 100644 index 0000000..9208870 --- /dev/null +++ b/CODE/font/mkfont.py @@ -0,0 +1,106 @@ +"""mkfont.py — Deshab Bitmap Font generator +Usage: python mkfont.py +Renders GB2312 charset + ASCII to a packed bitmap font file (.dbf) +""" + +import struct +import sys +from PIL import Image, ImageFont, ImageDraw + +DBF_MAGIC = b"DBF\x10" + +# GB2312 codepoints for simplified Chinese (3755 level-1 + 3008 level-2) +def gb2312_codepoints(): + cps = [] + # ASCII 32-126 + for c in range(32, 127): + cps.append(c) + # GB2312 level 1: 0xB0A1 - 0xB0FE, ... , 0xD7F9 + for hi in range(0xB0, 0xD8): + for lo in range(0xA1, 0xFF): + cps.append((hi << 8) | lo) + # GB2312 level 2: skip duplicates, 0xD8A1 - 0xF7FE + for hi in range(0xD8, 0xF8): + for lo in range(0xA1, 0xFF): + if hi == 0xD7 and lo > 0xF9: + break + cps.append((hi << 8) | lo) + return cps + + +def render_font(ttf_path, size, output_path, bpp=8): + font = ImageFont.truetype(ttf_path, size) + cps = gb2312_codepoints() + + # Build index and data + glyph_w = size + glyph_h = size + data_parts = [] + index_entries = [] + + ascii_w = size * 3 // 4 # narrower for ASCII + + for cp in cps: + if cp < 128: + # ASCII — use narrower glyph + ch = chr(cp) + w = ascii_w + else: + # GB2312 byte pair → Unicode + hi = (cp >> 8) & 0xFF + lo = cp & 0xFF + try: + ch = bytes([hi, lo]).decode("gb2312") + except: + continue + w = glyph_w + + img = Image.new("L", (w, glyph_h), 0) + draw = ImageDraw.Draw(img) + draw.text((0, 0), ch, font=font, fill=255) + img = img.resize((w, glyph_h), Image.LANCZOS) + + raw = img.tobytes() + if bpp == 1: + # pack to 1bpp + packed = bytearray() + for y in range(glyph_h): + row = 0 + for x in range(w): + if raw[y * w + x] >= 128: + row |= (1 << (7 - (x & 7))) + if (x & 7) == 7: + packed.append(row) + row = 0 + if w & 7: + packed.append(row) + glyph_data = bytes(packed) + else: + glyph_data = raw + + offset = sum(len(d) for d in data_parts) + data_parts.append(glyph_data) + index_entries.append((cp, w, offset, len(glyph_data))) + + # Write file + total_data = sum(len(d) for d in data_parts) + print(f"Glyphs: {len(index_entries)}, Data: {total_data} bytes") + + with open(output_path, "wb") as f: + f.write(DBF_MAGIC) + f.write(struct.pack(" ") + sys.exit(1) + ttf = sys.argv[1] + sz = int(sys.argv[2]) + out = sys.argv[3] + render_font(ttf, sz, out) diff --git a/CODE/font/render_embedded.py b/CODE/font/render_embedded.py new file mode 100644 index 0000000..87ac964 --- /dev/null +++ b/CODE/font/render_embedded.py @@ -0,0 +1,57 @@ +"""render_embedded.py — render FirstInit Chinese strings at varied sizes""" +from PIL import Image, ImageFont, ImageDraw + +ttf = "SYSTEM/system/font/simhei.ttf" + +texts = [ + ("welcome", "欢迎使用 Deshab", 48), # only welcome stays large + ("setup", "接下来让我们来引导你设置你的系统", 24), # 32 -> 24 + ("title", "创建你的 Deshab 账户", 24), + ("computer", "计算机名", 24), + ("username", "用户名", 24), + ("password", "密码", 24), + ("hint", "按 Enter 继续,Backspace 删除", 16), # 20 -> 16 + ("prefs_title", "设置系统偏好", 24), + ("prefs_language", "语言", 22), + ("prefs_region", "地区", 22), + ("prefs_timezone", "时区", 22), + ("prefs_keyboard", "键盘布局", 22), + ("prefs_theme", "主题", 22), + ("prefs_hint", "Enter 切换选项,方向键切换项目,鼠标点击选择", 16), + ("network_title", "网络设置", 24), + ("net_mode", "网络模式", 22), + ("net_device", "网络设备", 22), + ("net_ip", "地址获取", 22), + ("net_dns", "DNS", 22), + ("net_hint", "Enter 切换选项,方向键切换项目,鼠标点击选择", 16), + ("privacy_title", "隐私与诊断", 24), + ("done", "设置完成,请重启系统", 24), # final status +] + +lines = [] +lines.append("/* Auto-generated */") +lines.append("") + +for name, text, size in texts: + font = ImageFont.truetype(ttf, size) + bbox = font.getbbox(text) + w = bbox[2] + h = size + 6 + img = Image.new("L", (w, h), 0) + draw = ImageDraw.Draw(img) + draw.text((0, 3), text, font=font, fill=255) + pixels = list(img.tobytes()) + + lines.append(f"static const int g_txt_{name}_w = {w};") + lines.append(f"static const int g_txt_{name}_h = {h};") + lines.append(f"static const unsigned char g_txt_{name}[{w}*{h}] = {{") + for y in range(h): + row = ", ".join(str(pixels[y*w + x]) for x in range(w)) + lines.append(f" {row},") + lines.append("};") + lines.append("") + +with open("CODE/firstInit/text_bitmaps.c", "w", encoding="ascii") as f: + f.write("\n".join(lines)) + +print("Generated CODE/firstInit/text_bitmaps.c") diff --git a/CODE/font/render_text.py b/CODE/font/render_text.py new file mode 100644 index 0000000..19cf8c8 --- /dev/null +++ b/CODE/font/render_text.py @@ -0,0 +1,31 @@ +"""render_text.py — pre-render specific Chinese strings to C arrays""" +from PIL import Image, ImageFont, ImageDraw + +def render(ttf, texts, size, var_prefix): + font = ImageFont.truetype(ttf, size) + for name, text in texts: + w = font.getbbox(text)[2] + h = size + 4 + img = Image.new("L", (w, h), 0) + draw = ImageDraw.Draw(img) + draw.text((0, 2), text, font=font, fill=255) + + pixels = list(img.tobytes()) + print(f"/* {text} — {w}x{h} */") + print(f"static const int {var_prefix}_{name}_w = {w};") + print(f"static const int {var_prefix}_{name}_h = {h};") + print(f"static const unsigned char {var_prefix}_{name}_data[{w}*{h}] = {{") + for y in range(h): + row = ", ".join(str(pixels[y*w + x]) for x in range(w)) + print(f" {row},") + print("};") + print() + +texts = [ + ("welcome", "欢迎使用 Deshab"), + ("setup", "接下来让我们来引导你设置你的系统"), +] +# Generate for multiple sizes +for sz in [24]: + print(f"// ===== size={sz}px =====") + render("SYSTEM/system/font/simhei.ttf", texts, sz, f"txt{sz}") diff --git a/CODE/mouse/MAKEFILE b/CODE/mouse/MAKEFILE new file mode 100644 index 0000000..6749ecf --- /dev/null +++ b/CODE/mouse/MAKEFILE @@ -0,0 +1,25 @@ +CC := clang +LD := ld.lld + +ROOT := ../.. +OUT := $(ROOT)/SYSTEM/system/deshab64/mouse/mouseInit.elf + +CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2 +LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld + +C_SOURCES := main.c +OBJECTS := $(C_SOURCES:.c=.o) + +.PHONY: all clean + +all: $(OUT) + +$(OUT): $(OBJECTS) linker.ld + @if not exist "$(ROOT)\SYSTEM\system\deshab64\mouse" mkdir "$(ROOT)\SYSTEM\system\deshab64\mouse" + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }" diff --git a/CODE/mouse/linker.ld b/CODE/mouse/linker.ld new file mode 100644 index 0000000..825d5f1 --- /dev/null +++ b/CODE/mouse/linker.ld @@ -0,0 +1,16 @@ +ENTRY(dsk_entry) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); +} + +SECTIONS +{ + . = 0x1000; + .text : ALIGN(4K) { *(.text*) } :text + .rodata : ALIGN(4K) { *(.rodata*) } :rodata + .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data +} diff --git a/CODE/mouse/main.c b/CODE/mouse/main.c new file mode 100644 index 0000000..3a7f26b --- /dev/null +++ b/CODE/mouse/main.c @@ -0,0 +1,38 @@ +/* mouseInit — safe mouse initializer stub + * DSK-owned system component initializer. + * Current version must never block or crash first boot. + */ + +#include "../UTSM/include/utsm/dsk.h" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +#define COM1 0x3F8 +#define PS2_STATUS 0x64 + +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 void sputc(char c) { + for (u32 i=0;i<10000;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 logl(const char *s) { swrite(s); swrite("\n"); } + +__attribute__((visibility("default"))) +void dsk_entry(const dsk_boot_context *ctx) { + (void)ctx; + logl("[mouseInit] boot"); + + /* Safe first version: do not touch PS/2 controller command path yet. + The previous implementation could block/reboot on some firmware states. + Full IRQ12 + packet decoding will be implemented as a later input driver. */ + u8 status = inb(PS2_STATUS); + (void)status; + + logl("[mouseInit] safe init complete"); + return; +} diff --git a/CODE/mouse/main.o b/CODE/mouse/main.o new file mode 100644 index 0000000..4e44e37 Binary files /dev/null and b/CODE/mouse/main.o differ diff --git a/CODE/mouse/readme b/CODE/mouse/readme new file mode 100644 index 0000000..ca0fdbd --- /dev/null +++ b/CODE/mouse/readme @@ -0,0 +1,41 @@ +# mouseInit 鼠标初始化器 + +`CODE/mouse` 生成早期鼠标初始化 ELF: + +```text +CODE/mouse -> SYSTEM/system/deshab64/mouse/mouseInit.elf +``` + +## 目标 + +`mouseInit.elf` 是 DSK 直接调度的系统输入初始化器,不属于 FirstInit 用户向导。DSK 在检测到首次启动后先加载并调用它,初始化完成后返回 DSK,再由 DSK 启动 FirstInit 用户设置向导。 + +第一版职责: + +1. 初始化 PS/2 Controller 的辅助设备通道(mouse / AUX)。 +2. 开启 IRQ12 配置位。 +3. 向鼠标发送默认配置 `0xF6`。 +4. 启用数据报告 `0xF4`。 +5. 输出串口日志并返回 DSK。 + +## 当前限制 + +- 当前只做 PS/2 鼠标初始化,不消费鼠标包。 +- 鼠标 IRQ12 的实际数据读取应由后续 DKM 输入驱动或 DSK 输入子系统接管。 +- USB 鼠标需要 xHCI/HID 支持,暂不在第一版范围。 + +## 构建 + +```powershell +cd CODE\mouse +make -f MAKEFILE CC=clang LD=ld.lld +``` + +## 启动链路 + +```text +DSK 检测 firstInit=0 + -> 加载并调用 mouseInit.elf + -> mouseInit 初始化 PS/2 鼠标后返回 + -> DSK 继续加载 FirstInit.elf +``` diff --git a/CODE/netman/MAKEFILE b/CODE/netman/MAKEFILE new file mode 100644 index 0000000..4130935 --- /dev/null +++ b/CODE/netman/MAKEFILE @@ -0,0 +1,25 @@ +CC := clang +LD := ld.lld + +ROOT := ../.. +OUT := $(ROOT)/SYSTEM/system/deshab64/network/netman.elf + +CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2 +LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld + +C_SOURCES := main.c +OBJECTS := $(C_SOURCES:.c=.o) + +.PHONY: all clean + +all: $(OUT) + +$(OUT): $(OBJECTS) linker.ld + @if not exist "$(ROOT)\SYSTEM\system\deshab64\network" mkdir "$(ROOT)\SYSTEM\system\deshab64\network" + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }" diff --git a/CODE/netman/linker.ld b/CODE/netman/linker.ld new file mode 100644 index 0000000..825d5f1 --- /dev/null +++ b/CODE/netman/linker.ld @@ -0,0 +1,16 @@ +ENTRY(dsk_entry) + +PHDRS +{ + text PT_LOAD FLAGS(5); + rodata PT_LOAD FLAGS(4); + data PT_LOAD FLAGS(6); +} + +SECTIONS +{ + . = 0x1000; + .text : ALIGN(4K) { *(.text*) } :text + .rodata : ALIGN(4K) { *(.rodata*) } :rodata + .data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data +} diff --git a/CODE/netman/main.c b/CODE/netman/main.c new file mode 100644 index 0000000..3e02cd5 --- /dev/null +++ b/CODE/netman/main.c @@ -0,0 +1,471 @@ +/* netman — Deshab network manager initializer + * 读取 /system/deshab64/network/conf/conf.conf(SATA 早期镜像里为 root 目录 NETCONF.CNF), + * 解析网络配置;枚举内核 netdev 注册表;对具备收发能力的接口通过 e1000 真实收发 + * 跑最小 DHCP 客户端拿到 IP。 + */ + +#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 int (*block_read_fn)(u32 index, u64 lba, u32 count, void *buf); +typedef u32 (*net_device_count_fn)(void); +typedef int (*net_device_info_fn)(u32 index, void *out); +typedef int (*net_tx_fn)(u32 index, const void *packet, u32 length); +typedef int (*net_rx_poll_fn)(u32 index, void *buffer, u32 capacity, u32 *out_length); + +#define DKM_NET_F_LINK_UP (1u << 0) +#define DKM_NET_F_TX_READY (1u << 1) +#define DKM_NET_F_RX_READY (1u << 2) + +typedef struct { + const char *name; + u8 mac[6]; + u32 flags; +} nm_net_device_info; + +#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; } + +static void sputc(char c) { + for (u32 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 logl(const char *s) { swrite(s); swrite("\n"); } + +static void log_hex(const char *p, u64 v) { + static const char h[]="0123456789abcdef"; + char b[19]; int i=0; b[i++]='0'; b[i++]='x'; + for(int j=15;j>=0;j--) b[i++]=h[(v>>(j*4))&0xf]; + b[i]=0; swrite(p); swrite(b); swrite("\n"); +} + +static void *memset_nm(void *d, int c, u64 n) { u8 *p=(u8*)d; while(n--)*p++=(u8)c; return d; } +static int streqn(const char *a, const char *b, u32 n) { for(u32 i=0;iboot_sig != 0xAA55 || bpb->bps != 512 || bpb->spf == 0) return -2; + 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 (read_sectors(lba, spc, g_cluster) != 0) return -3; cb = g_cluster; } + if (find_entry(cb, spc, name11, &found_clus, &found_size) == 0) { found=1; break; } + u32 fo = fat_byte_off + clus * 4; + if (fo + 4 > sizeof(g_disk)) break; + clus = r32(g_disk + fo) & 0x0FFFFFFF; + } + if (!found) return -4; + if (found_size + 1 > out_cap) return -5; + u32 remaining = found_size; + u32 fc = found_clus; + char *dst = out; + while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) { + u32 lba = data_lba + (fc - 2) * spc; + u32 bytes = spc * 512; + if (bytes > remaining) bytes = remaining; + if (read_sectors(lba, spc, g_cluster) != 0) return -6; + for (u32 i=0;i sizeof(g_disk)) break; + fc = r32(g_disk + fo) & 0x0FFFFFFF; + } + out[found_size] = 0; + *out_size = found_size; + return 0; +} + +typedef struct { + char mode[16]; + char device[16]; + char name[64]; + char password[64]; + char ip[16]; + char dns[16]; +} net_conf; + +static void copy_value(char *dst, u32 cap, const char *src, u32 len) { + if (!cap) return; + u32 n = len < cap-1 ? len : cap-1; + for (u32 i=0;imode, sizeof(cfg->mode), "dhcp", 4); + copy_value(cfg->device, sizeof(cfg->device), "auto", 4); + copy_value(cfg->ip, sizeof(cfg->ip), "dhcp", 4); + copy_value(cfg->dns, sizeof(cfg->dns), "auto", 4); + cfg->name[0]=0; cfg->password[0]=0; + const char *p = txt; + while (*p) { + const char *line = p; + u32 len = 0; + while (p[len] && p[len]!='\n' && p[len]!='\r') len++; + const char *eq = 0; + for (u32 i=0;imode,sizeof(cfg->mode),v,vlen); + else if (klen==14 && streqn(line,"network.device",14)) copy_value(cfg->device,sizeof(cfg->device),v,vlen); + else if (klen==12 && streqn(line,"network.name",12)) copy_value(cfg->name,sizeof(cfg->name),v,vlen); + else if (klen==16 && streqn(line,"network.password",16)) copy_value(cfg->password,sizeof(cfg->password),v,vlen); + else if (klen==10 && streqn(line,"network.ip",10)) copy_value(cfg->ip,sizeof(cfg->ip),v,vlen); + else if (klen==11 && streqn(line,"network.dns",11)) copy_value(cfg->dns,sizeof(cfg->dns),v,vlen); + } + p += len; + while (*p=='\n' || *p=='\r') p++; + } +} + +static void log_cfg(const net_conf *cfg) { + logl("[netman] config loaded"); + swrite("[netman] mode="); logl(cfg->mode); + swrite("[netman] device="); logl(cfg->device); + swrite("[netman] name="); logl(cfg->name[0] ? cfg->name : ""); + swrite("[netman] ip="); logl(cfg->ip); + swrite("[netman] dns="); logl(cfg->dns); + u32 pwlen=0; while(cfg->password[pwlen]) pwlen++; + log_hex("[netman] password length=", pwlen); +} + +static void log_mac(const u8 mac[6]) { + static const char h[]="0123456789abcdef"; + char b[18]; + int p=0; + for (int i=0;i<6;i++) { + if (i) b[p++]=':'; + b[p++]=h[(mac[i]>>4)&0xf]; + b[p++]=h[mac[i]&0xf]; + } + b[p]=0; + swrite(" mac="); + swrite(b); +} + +/* ---- 最小 DHCP 客户端:Ethernet + IPv4 + UDP,通过 e1000 真实收发 ---- */ + +static u8 g_pktbuf[2048]; +static u8 g_rxbuf[2048]; +static u8 g_local_mac[6]; +static u32 g_lease_ip; +static u32 g_server_ip; +static u32 g_dhcp_xid = 0x21030201u; + +/* 累加和,用于 IPv4 头部校验 */ +static u16 ip_checksum(const u8 *data, u32 len) { + u32 sum = 0; + for (u32 i=0;i+1> 16) sum = (sum & 0xffff) + (sum >> 16); + return (u16)(~sum); +} + +static void delay_spin(u32 loops) { + for (volatile u32 i=0;i>24); dhcp[5]=(u8)(g_dhcp_xid>>16); + dhcp[6]=(u8)(g_dhcp_xid>>8); dhcp[7]=(u8)(g_dhcp_xid); + dhcp[10]=0x80; dhcp[11]=0x00; /* broadcast flag */ + for (int i=0;i<6;i++) dhcp[28+i]=g_local_mac[i]; /* chaddr */ + dhcp[236]=99; dhcp[237]=130; dhcp[238]=83; dhcp[239]=99; /* magic cookie */ + + u32 opt = 240; + dhcp[opt++]=53; dhcp[opt++]=1; dhcp[opt++]=type; + if (type == 3) { + dhcp[opt++]=50; dhcp[opt++]=4; + dhcp[opt++]=(u8)(requested_ip>>24); dhcp[opt++]=(u8)(requested_ip>>16); + dhcp[opt++]=(u8)(requested_ip>>8); dhcp[opt++]=(u8)(requested_ip); + dhcp[opt++]=54; dhcp[opt++]=4; + dhcp[opt++]=(u8)(server_ip>>24); dhcp[opt++]=(u8)(server_ip>>16); + dhcp[opt++]=(u8)(server_ip>>8); dhcp[opt++]=(u8)(server_ip); + } + dhcp[opt++]=55; dhcp[opt++]=3; dhcp[opt++]=1; dhcp[opt++]=3; dhcp[opt++]=6; + dhcp[opt++]=255; + + u32 dhcp_len = opt; + u32 udp_len = 8 + dhcp_len; + u32 ip_len = 20 + udp_len; + + /* UDP (8) */ + udp[0]=0x00; udp[1]=68; udp[2]=0x00; udp[3]=67; + udp[4]=(u8)(udp_len>>8); udp[5]=(u8)(udp_len); + udp[6]=0; udp[7]=0; + + /* IPv4 (20) */ + ip[0]=0x45; ip[1]=0x00; + ip[2]=(u8)(ip_len>>8); ip[3]=(u8)(ip_len); + ip[4]=0; ip[5]=0; ip[6]=0x00; ip[7]=0x00; + ip[8]=64; ip[9]=17; ip[10]=0; ip[11]=0; + ip[12]=0;ip[13]=0;ip[14]=0;ip[15]=0; + ip[16]=255;ip[17]=255;ip[18]=255;ip[19]=255; + u16 ipck = ip_checksum(ip, 20); + ip[10]=(u8)(ipck>>8); ip[11]=(u8)(ipck); + + u32 frame_len = 14 + ip_len; + if (frame_len < 60) frame_len = 60; + return g_net_tx(index, g_pktbuf, frame_len); +} + +/* 解析收到的帧,若为本机 xid 的 DHCP 回复则返回 message type。 */ +static int dhcp_parse(const u8 *frame, u32 len, u32 *out_yiaddr, u32 *out_server) { + if (len < 14+20+8+240) return -1; + if (frame[12]!=0x08 || frame[13]!=0x00) return -1; + const u8 *ip = frame + 14; + if ((ip[0]>>4)!=4) return -1; + u32 ihl = (ip[0]&0xf)*4; + if (ip[9]!=17) return -1; + const u8 *udp = ip + ihl; + u16 dport = (u16)((udp[2]<<8)|udp[3]); + if (dport != 68) return -1; + const u8 *dhcp = udp + 8; + u32 xid = ((u32)dhcp[4]<<24)|((u32)dhcp[5]<<16)|((u32)dhcp[6]<<8)|dhcp[7]; + if (xid != g_dhcp_xid) return -1; + u32 yi = ((u32)dhcp[16]<<24)|((u32)dhcp[17]<<16)|((u32)dhcp[18]<<8)|dhcp[19]; + if (!(dhcp[236]==99&&dhcp[237]==130&&dhcp[238]==83&&dhcp[239]==99)) return -1; + u32 o = 240; + u8 msg_type = 0; + u32 server = 0; + u32 cap = len - 14; + while (o < cap) { + u8 code = dhcp[o++]; + if (code == 255) break; + if (code == 0) continue; + if (o >= cap) break; + u8 l = dhcp[o++]; + if (o + l > cap) break; + if (code == 53 && l >= 1) msg_type = dhcp[o]; + if (code == 54 && l >= 4) + server = ((u32)dhcp[o]<<24)|((u32)dhcp[o+1]<<16)|((u32)dhcp[o+2]<<8)|dhcp[o+3]; + o += l; + } + *out_yiaddr = yi; + *out_server = server; + return msg_type; +} + +static void log_ipv4(const char *pre, u32 ip_hostorder) { + char b[16]; int p=0; + for (int s=3;s>=0;s--) { + u32 oct = (ip_hostorder >> (s*8)) & 0xff; + if (oct==0) { b[p++]='0'; } + else { + int started=0; + for (u32 d=100; d>=1; d/=10) { + u32 dig=(oct/d)%10; + if (dig||started){ b[p++]=(char)('0'+dig); started=1; } + if (d==1) break; + } + } + if (s) b[p++]='.'; + } + b[p]=0; + swrite(pre); swrite(b); swrite("\n"); +} + +/* 对指定 netdev 跑一次 DHCP DISCOVER/OFFER/REQUEST/ACK。成功返回 0。 */ +static int dhcp_run(u32 index, const u8 mac[6]) { + if (!g_net_tx || !g_net_rx_poll) { logl("[netman] DHCP: net tx/rx api missing"); return -1; } + for (int i=0;i<6;i++) g_local_mac[i]=mac[i]; + g_lease_ip = 0; g_server_ip = 0; + + logl("[netman] DHCP: sending DISCOVER"); + int txrc = dhcp_send(index, 1, 0, 0); + log_hex("[netman] DHCP DISCOVER tx rc=", (u64)(i8)txrc); + if (txrc != 0) { logl("[netman] DHCP: DISCOVER tx failed"); return -1; } + + u32 offer_ip = 0, offer_srv = 0; + int got_offer = 0; + u32 rx_frames = 0; + for (u32 attempt=0; attempt<40000 && !got_offer; attempt++) { + u32 rlen = 0; + int rc = g_net_rx_poll(index, g_rxbuf, sizeof(g_rxbuf), &rlen); + if (rc == 0 && rlen > 0) { + rx_frames++; + int t = dhcp_parse(g_rxbuf, rlen, &offer_ip, &offer_srv); + if (t == 2) { got_offer = 1; break; } /* OFFER */ + } + delay_spin(200); + } + log_hex("[netman] DHCP rx frames seen=", rx_frames); + if (!got_offer) { logl("[netman] DHCP: no OFFER received"); return -2; } + log_ipv4("[netman] DHCP OFFER ip=", offer_ip); + + logl("[netman] DHCP: sending REQUEST"); + if (dhcp_send(index, 3, offer_ip, offer_srv) != 0) { logl("[netman] DHCP: REQUEST tx failed"); return -3; } + + u32 ack_ip=0, ack_srv=0; + int got_ack=0; + for (u32 attempt=0; attempt<40000 && !got_ack; attempt++) { + u32 rlen=0; + int rc = g_net_rx_poll(index, g_rxbuf, sizeof(g_rxbuf), &rlen); + if (rc == 0 && rlen > 0) { + int t = dhcp_parse(g_rxbuf, rlen, &ack_ip, &ack_srv); + if (t == 5) { got_ack=1; break; } /* ACK */ + } + delay_spin(200); + } + if (!got_ack) { logl("[netman] DHCP: no ACK received"); return -4; } + + g_lease_ip = ack_ip; g_server_ip = ack_srv; + log_ipv4("[netman] DHCP ACK, leased ip=", ack_ip); + log_ipv4("[netman] DHCP server=", ack_srv); + logl("[netman] DHCP: connected"); + return 0; +} + +static void query_netdevs(void) { + if (!g_net_device_count || !g_net_device_info) { + logl("[netman] net API not ready; configuration staged only"); + return; + } + + u32 count = g_net_device_count(); + log_hex("[netman] netdev_count=", count); + for (u32 i=0;i"); + log_mac(info.mac); + swrite(" flags="); + log_hex("", info.flags); + if (info.flags & DKM_NET_F_LINK_UP) logl("[netman] link=up"); + else logl("[netman] link=down"); + if (info.flags & DKM_NET_F_TX_READY) logl("[netman] tx=ready"); + if (info.flags & DKM_NET_F_RX_READY) logl("[netman] rx=ready"); + + /* 对第一个具备收发能力且 link up 的接口尝试真实 DHCP 连接 */ + if ((info.flags & (DKM_NET_F_LINK_UP|DKM_NET_F_TX_READY|DKM_NET_F_RX_READY)) + == (DKM_NET_F_LINK_UP|DKM_NET_F_TX_READY|DKM_NET_F_RX_READY)) { + dhcp_run(i, info.mac); + } + } +} + +__attribute__((visibility("default"))) +void dsk_entry(const dsk_boot_context *ctx) { + logl("[netman] boot"); + if (!ctx || ctx->magic != DSK_BOOT_MAGIC) { logl("[netman] bad context"); return; } + u64 api = ctx->dkm_kernel_api; + if (api) { + u64 block_api = *(u64 *)(api + 0xA8); + if (block_api) g_block_read = (block_read_fn)*(u64 *)(block_api + 16); + u64 net_api = *(u64 *)(api + 0x48); + log_hex("[netman] kernel_api.net=", net_api); + if (net_api) { + g_net_device_count = (net_device_count_fn)*(u64 *)(net_api + 8); + g_net_device_info = (net_device_info_fn)*(u64 *)(net_api + 16); + g_net_tx = (net_tx_fn)*(u64 *)(net_api + 24); + g_net_rx_poll = (net_rx_poll_fn)*(u64 *)(net_api + 32); + log_hex("[netman] net.tx ptr=", (u64)g_net_tx); + log_hex("[netman] net.rx ptr=", (u64)g_net_rx_poll); + } + } + u32 size=0; + int rc = fat32_read_root_file("NETCONF CNF", g_conf, sizeof(g_conf), &size); + log_hex("[netman] conf read rc=", (u64)(i8)rc); + if (rc != 0) { + logl("[netman] using built-in defaults"); + g_conf[0]=0; + } else { + log_hex("[netman] conf size=", size); + } + net_conf cfg; + memset_nm(&cfg, 0, sizeof(cfg)); + parse_conf(g_conf, &cfg); + log_cfg(&cfg); + if (cfg.mode[0]=='d' && cfg.mode[1]=='i') logl("[netman] networking disabled by config"); + query_netdevs(); + logl("[netman] done"); +} diff --git a/CODE/netman/main.o b/CODE/netman/main.o new file mode 100644 index 0000000..fea675f Binary files /dev/null and b/CODE/netman/main.o differ diff --git a/ISO/run_qemu.bat b/ISO/run_qemu.bat new file mode 100644 index 0000000..007935b --- /dev/null +++ b/ISO/run_qemu.bat @@ -0,0 +1,75 @@ +@echo off +setlocal + +set "SCRIPT_DIR=%~dp0" +set "IMG=%SCRIPT_DIR%deshab.img" +set "SATA_IMG=%SCRIPT_DIR%..\.build_tmp\sata_fat32_dsk.img" + +if not exist "%IMG%" ( + echo [qemu] Image not found: %IMG% + echo [qemu] Run ..\build.bat first. + exit /b 1 +) + +set "QEMU=" +for %%Q in (qemu-system-x86_64.exe qemu-system-x86_64) do ( + for /f "delims=" %%P in ('where %%Q 2^>nul') do ( + if not defined QEMU set "QEMU=%%P" + ) +) + +if not defined QEMU if exist "C:\Program Files\qemu\qemu-system-x86_64.exe" set "QEMU=C:\Program Files\qemu\qemu-system-x86_64.exe" +if not defined QEMU if exist "C:\Program Files (x86)\qemu\qemu-system-x86_64.exe" set "QEMU=C:\Program Files (x86)\qemu\qemu-system-x86_64.exe" +if not defined QEMU if exist "C:\msys64\mingw64\bin\qemu-system-x86_64.exe" set "QEMU=C:\msys64\mingw64\bin\qemu-system-x86_64.exe" +if not defined QEMU if exist "C:\msys64\ucrt64\bin\qemu-system-x86_64.exe" set "QEMU=C:\msys64\ucrt64\bin\qemu-system-x86_64.exe" + +if not defined QEMU ( + echo [qemu] qemu-system-x86_64 was not found. + echo [qemu] Install QEMU or add it to PATH. + exit /b 1 +) + +set "OVMF=" +if exist "C:\Program Files\qemu\share\edk2-x86_64-code.fd" set "OVMF=C:\Program Files\qemu\share\edk2-x86_64-code.fd" +if not defined OVMF if exist "C:\Program Files\qemu\share\OVMF_CODE.fd" set "OVMF=C:\Program Files\qemu\share\OVMF_CODE.fd" +if not defined OVMF if exist "C:\msys64\mingw64\share\edk2\ovmf\OVMF_CODE.fd" set "OVMF=C:\msys64\mingw64\share\edk2\ovmf\OVMF_CODE.fd" +if not defined OVMF if exist "C:\msys64\ucrt64\share\edk2\ovmf\OVMF_CODE.fd" set "OVMF=C:\msys64\ucrt64\share\edk2\ovmf\OVMF_CODE.fd" + +if not defined OVMF ( + echo [qemu] UEFI firmware was not found. + echo [qemu] Expected edk2-x86_64-code.fd or OVMF_CODE.fd under QEMU install directory. + exit /b 1 +) + +echo [qemu] Using: %QEMU% +echo [qemu] UEFI: %OVMF% +echo [qemu] Image: %IMG% +if exist "%SATA_IMG%" ( + echo [qemu] SATA: %SATA_IMG% + "%QEMU%" ^ + -machine q35 ^ + -m 512M ^ + -cpu max ^ + -serial stdio ^ + -drive if=pflash,format=raw,readonly=on,file="%OVMF%" ^ + -drive format=raw,file="%IMG%",if=virtio ^ + -drive id=sata0,format=raw,file="%SATA_IMG%",if=none ^ + -device ide-hd,drive=sata0,bus=ide.0 ^ + -netdev user,id=net0 ^ + -device e1000,netdev=net0,mac=52:54:00:12:34:56 ^ + -boot menu=on +) else ( + echo [qemu] SATA image not found, booting without block device + "%QEMU%" ^ + -machine q35 ^ + -m 512M ^ + -cpu max ^ + -serial stdio ^ + -drive if=pflash,format=raw,readonly=on,file="%OVMF%" ^ + -drive format=raw,file="%IMG%",if=virtio ^ + -netdev user,id=net0 ^ + -device e1000,netdev=net0 ^ + -boot menu=on +) + +endlocal diff --git a/LESSONS_LEARNED.md b/LESSONS_LEARNED.md new file mode 100644 index 0000000..772e719 --- /dev/null +++ b/LESSONS_LEARNED.md @@ -0,0 +1,532 @@ +# 经验记录 + +本文件记录项目开发和架构设计过程中的经验与教训。 + +## 2026-06-29 — 记录 UTSM 架构与算法设计 + +### 经验 + +1. **架构分层**: 将 UTSM 拆成 UTSM、UTRW、DMP、PCKC,并和 SAS-R0-PCQ、DRR 明确边界,能避免调度、加密、恢复逻辑互相污染。 +2. **热路径约束**: 将段定位设计为 `segment_slot + generation + epoch` 的 O(1) 路径,能保留 Per-CPU O(1) 调度器的性能目标。 +3. **文档同步**: 同时维护模块 README、算法记录和架构记录,方便后续实现时按模块拆分代码。 + +### 教训 + +1. **避免破坏连续内存**: UUID 不应嵌入数据区形成孔洞,否则会破坏 cache line、DMA、checkpoint 和批量复制路径。 +2. **恢复根要独立**: 看门狗如果只是普通内核线程,无法在调度器损坏或关中断卡死时可靠恢复,必须作为 DRR 独立设计。 +3. **不要在调度路径做重活**: 调度切换中不能扫描 capability、计算 MAC 或重加密,否则会破坏 O(1) 调度目标。 + +## 2026-06-29 — 优化 UTSM 恢复与 checkpoint 设计 + +### 经验 + +1. **先补恢复底座**: DRR Emergency Pool、A/B checkpoint metadata、Dirty Shard 是 UTSM 进入实现前最关键的三项稳定性优化。 +2. **分片降低抖动**: Dirty Shard 让 checkpoint 从扫描大 bitmap 变为处理 dirty shard,符合“不扫描全内存”的约束。 +3. **双槽保证稳定点**: A/B metadata 通过先写 inactive slot、CRC 校验、最后原子切换 active slot,能避免半写 checkpoint 破坏旧稳定点。 + +### 教训 + +1. **恢复路径不能依赖普通堆**: 普通 allocator OOM 时,如果 DRR 也依赖它,系统会失去最后恢复能力。 +2. **checkpoint 提交顺序必须固定**: metadata、dirty index、MAC root、CRC、flush、active slot 切换的顺序不能随意调整。 +3. **Dirty 计数要避免共享争用**: Dirty Shard 的 dirty_count 应结合 per-CPU dirty list,避免多核写入时形成 cache line 热点。 + +## 2026-06-29 — 添加 SYSTEM 驱动 manifest + +### 经验 + +1. **根目录语义明确**: SYSTEM 打包成 IMG 后就是系统根目录,启动路径和驱动路径应统一以 SYSTEM 为根来描述。 +2. **JSON 清单更适合扩展**: manifest.json 能直接表达 stage、required、depends、provides、driver class 等加载信息。 +3. **分阶段加载清晰**: platform、boot、filesystem、optional 四阶段能区分启动关键驱动和普通可选驱动。 + +### 教训 + +1. **不要只依赖目录扫描**: 启动驱动顺序需要 manifest 控制,否则依赖和失败策略不稳定。 +2. **required 标志必须明确**: 启动关键驱动失败应进入 DRR/panic,可选驱动失败只标记 FAILED。 +3. **stage0/stage1 需要 boot module 兜底**: 文件系统未就绪前,早期驱动不能完全依赖 SYSTEM/driver 的普通遍历。 + +## 2026-06-29 — 设计 DKM 驱动模块 ABI + +### 经验 + +1. **ABI 固定入口**: 每个驱动统一导出 `driver_desc`、`driver_init`、`driver_exit`,能让 DSM loader 简化加载和错误处理。 +2. **kernel_api 降低耦合**: 驱动通过 `dkm_kernel_api` 调用 log、mem、UTSM、IRQ、PCI、DMA、VFS、NET、DRR 等服务,避免直接绑定任意内核符号。 +3. **早期模块双路径**: stage0/stage1 优先从 boot module table 加载,VFS 可用后再从 SYSTEM 路径加载,能覆盖文件系统未启动阶段。 + +### 教训 + +1. **ELF 支持范围要收窄**: 第一版只支持必要 x86_64 relocation,禁止 TLS、IFUNC、lazy binding 和外部动态库,避免 loader 复杂度失控。 +2. **状态机要显式**: DISCOVERED 到 ACTIVE 以及各类 FAILED 状态必须明确,DRR 才能做恢复和归因。 +3. **manifest 和 driver_desc 都要校验**: manifest 描述启动策略,driver_desc 描述模块自声明,二者不一致时应拒绝或降级加载。 + +## 2026-06-29 — 编写 CODE/DKM/README.md + +### 经验 + +1. **模块 README 要可实现**: DKM README 不只描述概念,还应列出 loader、parser、relocation、state table 等第一版实现清单。 +2. **运行路径要闭环**: 从 `SYSTEM/driver/manifest.json` 到 `driver_init` 再到 DRR recovery 的链路需要在模块文档中完整串起来。 +3. **文档分层有利于实现**: `RE/驱动模块ABI设计.md` 作为完整规范,`CODE/DKM/README.md` 作为模块落地说明,职责清晰。 + +### 教训 + +1. **不要把 DKM 写成泛泛说明**: 驱动模块系统涉及启动顺序、ELF、ABI、UTSM、DRR,README 必须明确边界和第一版范围。 +2. **早期加载限制要写清**: stage0/stage1 不能依赖 VFS 和普通 heap,否则启动链会循环依赖。 +3. **实现清单要保留**: 后续写代码时应按 manifest parser、boot module lookup、ELF loader、relocation、driver state table 的顺序推进。 + +## 2026-06-29 — 接入 DKM 内置驱动最小链路 + +### 经验 + +1. **先内置再外置**: 在实现外部 ELF `.drv` loader 前,先用内置 `console_early` 驱动验证 `driver_desc -> driver_init -> ACTIVE`,能降低 DKM 首次接入风险。 +2. **kernel_api 是驱动边界**: 即使内置驱动也通过 `dkm_kernel_api` 调用 log 服务,能保持未来外部驱动 ABI 一致。 +3. **验证点要清晰**: 串口输出 `[DKM] builtin driver active` 和 `[DKM:console_early] serial console online`,说明 ABI 校验、handle 创建和 init 调用路径已跑通。 + +### 教训 + +1. **不要直接跳到外部 ELF loader**: manifest parser、boot module、relocation、符号解析同时实现会放大调试范围,应先固定 ABI 和状态机。 +2. **外部 `.drv` 仍未执行**: 当前 `SYSTEM/driver` 下的 `.drv` 只是镜像内容,必须实现 DSM/ELF loader 后才会被加载。 +3. **内置驱动也应走同一状态机**: 避免未来外部加载器和内置路径行为不一致。 + +## 2026-06-29 — 接入 Limine boot module 与 DKM ELF 符号扫描 + +### 经验 + +1. **先识别再加载**: 外部 `.drv` 应先通过 boot module 枚举和 ELF64 header 校验,再解析 section table / symtab / strtab,最后定位 `driver_desc` / `driver_init` / `driver_exit`。 +2. **Limine request 需要 linker 保留**: module request 必须放入 `.limine_requests*` 区段,并在 linker script 中显式保留 start/request/end,否则 bootloader 不会返回 response。 +3. **.drv 是 ET_REL 可重定位文件**: 当前 stub 驱动编译为 `-r` 可重定位目标文件,附带了 `.symtab` / `.strtab` 和完整符号表,内核可以直接遍历 symtab 找到目标符号的 section 和 offset。 +4. **streq 足够**: 内核内联 `streq("driver_desc")` 已足够定位所需符号,不需要 hash 或完整的字符串匹配库。 + +### 教训 + +1. **module 配置要在 limine.conf 中显式声明**: 仅把 `.drv` 放进 `SYSTEM/driver` 不会被 bootloader 自动预加载。 +2. **有符号表 ≠ 可调用**: 符号找到了,但 ET_REL 没有 load address / vaddr,relocation 未完成前不能直接跳转。 +3. **boot module 适合 stage0/stage1**: 文件系统未就绪前,早期驱动应优先通过 boot module 进入内核。 + +## 2026-06-29 — 实现 ET_REL 外部驱动装载与 relocation + +### 经验 + +1. **section offset + image base 才能得到正确地址**: ET_REL relocation 中符号值为 section 内偏移,重定位写入的目标地址必须是 `image_base + section_offset + symbol_value + addend`,只算 offset 会导致低地址指针。 +2. **NX 是早期加载器的隐性障碍**: arena 位于 `.bss`,默认只有 PF_R|PF_W,调用 `driver_init` 需要可执行内存。SAS-R0 下通过 linker script PHDRS 给 data 段加 PF_X 是最简方案。 +3. **Et_REL 只需 R_X86_64_64 即可起步**: 当前 stub driver 只有三种字符串指针重定位,全部是 R_X86_64_64,loader 复杂度极低。 + +### 教训 + +1. **不能只看 ELF dump 输出验证重定位**: 必须用 hex 指针输出确认运行时地址落在正确区间,否则空白输出难以区分是空字符串还是 NULL 指针。 +2. **arena 分配的内存在真实硬件上可能不可执行**: QEMU 模拟器不严格检查 NX 时不会暴露问题,但 Limine 的 page table 会强制 NX,未来应在 loader 中分配单独的可执行区域。 +3. **PHDRS 合并 .data/.bss 前要确认 startup 代码没有依赖 .bss 零初始化边界**: 当前 boot.S 没有依赖,合并安全。 + +## 2026-06-29 — 实现 manifest.json 解析与分阶段驱动加载 + +### 经验 + +1. **最小 JSON parser 足以处理 manifest**: 只需要 tokenizer + 字符串读取 + 递归对象/数组跳过,无需完整 JSON 库。 +2. **Limine 路径有前导 `/`**: `boot():/driver/...` 返回的路径以 `/` 开头,manifest 中的路径 `driver/...` 不含前导 `/`,匹配时需要 strip。 +3. **按 stage 分阶段加载天然隔离故障**: platform → boot → filesystem → optional,每 stage 内部 required 驱动失败只终止当前 stage,不阻断后续 stage 的 optional 驱动。 + +### 教训 + +1. **JSON value skipper 必须处理所有类型**: 对象 `{}`、数组 `[]`、字符串、数字、布尔,缺一不可,否则 parser 在遇到未知 value 类型时 break。 +2. **PowerShell Set-Content 会写 BOM**: 对 C 源文件应避免使用,否则 BOM 字符 `U+FEFF` 在文件开头导致 clang 报错 `extraneous closing brace`。 +3. **arena 不够 14 个驱动**: 当前 4MB arena 在加载全部 14 个驱动后已接近容量上限,后续需要实现真正的物理页分配器。 + +## 2026-06-29 — 实现第一个真实 DKM 驱动:PIT timer + +### 经验 + +1. **真实驱动需要 BSS**: 驱动中声明 `static const struct dkm_log_api *g_log` 会产生 `.bss` section(SHT_NOBITS + SHF_ALLOC),loader 必须为其分配零填充空间。 +2. **R_X86_64_PC32 和 R_X86_64_32S 是代码重定位主流**: inline asm 的 `outb/inb` 使用立即数端口,编译器生成 PC32 对 `.bss` 的引用和 32S 对 `.rodata.str1.1` 的引用。 +3. **PIT 校准可在无 IRQ 下工作**: 通过 latch command 读取 PIT 当前计数值,在 mode 0 terminal count 后计数值回绕,可做精确的 busy-wait 延迟。 + +### 教训 + +1. **driver_desc 不能 static**: ELF 符号 `driver_desc` 必须是 global(`visibility("default")`),否则 symtab 中符号为 LOCAL,loader 找不到。 +2. **真实驱动的 .text 有 `R_X86_64_PC32` 重定位**: stub 驱动的 `driver_init` 只是 `xor eax,eax; ret` 没有重定位,但真实的 `outb/inb` 内联汇编会产生 PC32 对 g_log 全局变量的引用。 + +## 2026-06-29 — 修复 UTSM 早期启动崩溃 + +### 经验 + +1. **先验证崩溃阶段**: 串口只到 `[UTSM] boot` 时,实际还未进入 DKM 驱动加载,不能把早期 runtime 崩溃误判成缺真实驱动。 +2. **早期日志不能无限等待硬件**: `serial_putc()` 对 COM1 ready 位无限 busy-wait 会让内核卡死,early serial 必须有超时或降级策略。 +3. **未初始化 FPU/SSE 前禁止编译器生成 SIMD 指令**: Clang 即使在 freestanding 下也可能生成 `xorps/movups`,早期内核未启用 SSE 会触发异常,应使用 `-mno-sse -mno-sse2 -mno-mmx -msoft-float`。 + +### 教训 + +1. **进入 C 前要固定 CPU 状态**: `_start` 应先 `cli` 和 `cld`,否则未建 IDT 前被中断或方向标志异常会导致三重故障。 +2. **驱动不能直接下载复用**: Linux/Windows 已编译驱动不符合 DKM ABI,真实驱动必须按 Deshab 的 `driver_desc/driver_init/kernel_api` 原生实现。 +3. **stub `.drv` 只是镜像样本**: 在 DSM manifest parser、ELF loader、relocation 和 `dkm_kernel_api` 实现前,`SYSTEM/driver` 下的 `.drv` 不会被内核加载执行。 + +## 2026-06-29 — 编写 UTSM 内核代码首阶段骨架 + +### 经验 + +1. **先闭环再扩展**: 先实现 Limine 入口、serial 日志、UTSM init、segment 创建、UTRW read/write 和 selftest,可以快速把设计变成可验证路径。 +2. **保持 64B line 接口**: 即使第一版使用占位 XOR stream,也保留 cache line 级加解密接口,后续替换真实加密不会影响 UTRW 主路径。 +3. **构建脚本要先检查工具链**: build.ps1 明确检查 clang、ld.lld、make,能快速定位环境缺失,而不是让 make 报模糊错误。 + +### 教训 + +1. **Windows make 会受环境影响**: MinGW make 会把 `CC` 默认为 `cc`,Makefile 中应强制指定 `CC := clang`,不能只用 `?=`。 +2. **当前环境缺少 LLVM 工具链**: 本机 PATH 中没有 clang 和 ld.lld,当前无法生成 `SYSTEM/boot/utsm.elf`,需要先安装 LLVM/Clang 或提供交叉工具链。 +3. **第一版不要引入完整系统复杂度**: DRR、DKM、真实 checkpoint、buddy/slab 和多 CPU 调度都应在 selftest 闭环后逐步接入。 + +## 2026-06-29 — 自动发现 LLVM 并成功构建 UTSM + +### 经验 + +1. **工具链可自动发现**: LLVM 安装在 `C:\Program Files\LLVM\bin` 但不在 PATH 时,build.ps1 可以扫描常见目录并把绝对路径传给 Makefile。 +2. **高半区内核要使用 kernel code model**: 链接地址位于 `0xffffffff80000000` 时,CFLAGS/ASFLAGS 需要 `-mcmodel=kernel`,否则会出现 `R_X86_64_32 out of range`。 +3. **清理旧 object 很关键**: 修改编译参数后必须先 clean,否则旧 object 仍按旧 code model 生成,链接错误会继续存在。 + +### 教训 + +1. **不要只检查 PATH**: Windows 上 LLVM 可能已安装但没写入 PATH,构建脚本应扫描常见安装目录。 +2. **PowerShell 路径含空格要传绝对参数**: `C:\Program Files\LLVM\bin\clang.exe` 这类路径应通过 make 变量传递,避免 shell 展开错误。 +3. **MinGW make 的中文临时目录警告可忽略但需关注**: 当前 `TMP/TEMP Illegal byte sequence` 未阻断构建,但后续若引发临时文件问题,应在 build.ps1 中设置 ASCII 临时目录。 + +## 2026-06-29 — 打包 SYSTEM 为 IMG 并添加 QEMU 启动脚本 + +### 经验 + +1. **不依赖外部镜像工具也能先闭环**: build.ps1 直接生成 FAT16 raw IMG,可以在没有 mtools/oscdimg/xorriso 的环境下把 SYSTEM 打包出来。 +2. **构建脚本应负责完整产物链**: 现在 `build.bat -> build.ps1 -> utsm.elf -> ISO/deshab.img` 形成了连续流程。 +3. **QEMU 启动脚本应自动找程序**: `ISO/run_qemu.bat` 会从 PATH 和常见安装目录找 `qemu-system-x86_64.exe`,使用 `-serial stdio` 方便看内核串口日志。 + +### 教训 + +1. **IMG 命名不等于 ISO 文件系统**: 当前产物是 FAT16 raw 磁盘镜像 `deshab.img`,目录名叫 ISO 只是输出目录,后续若需要真正 ISO9660 应单独实现。 +2. **Limine 是否能从该 IMG 启动仍需 QEMU 验证**: 镜像已包含 EFI/BOOT/BOOTX64.EFI、limine.conf 和 utsm.elf,但最终启动链需要实际运行 QEMU 检查。 +3. **临时构建文件需要过滤**: 打包 SYSTEM 时应排除 `*.tmp*`,避免旧的 `utsm.elf.tmp...` 被写入镜像。 + +## 2026-06-29 — 修正 UEFI 镜像为 GPT + FAT32 ESP + +### 经验 + +1. **UEFI 磁盘镜像应有 GPT**: 仅裸 FAT 分区不等于可启动 UEFI 磁盘,镜像需要保护 MBR、GPT 主/备表和 ESP 分区。 +2. **ESP 应使用 FAT32**: QEMU/UEFI 固件对 ESP 的预期是 FAT32 文件系统,`EFI/BOOT/BOOTX64.EFI` 应位于该分区内。 +3. **可直接验证关键签名**: 构建后检查 MBR `AA55`、保护分区类型 `EE`、GPT 签名 `EFI PART`、FAT32 标记,能快速确认镜像结构正确。 + +### 教训 + +1. **裸文件系统镜像不够**: 之前的 FAT16 raw 镜像缺少分区表,UEFI 固件不能按磁盘启动语义识别。 +2. **PowerShell 数字字面量要注意有符号转换**: `0xffffffff` 在某些上下文会变成 `-1`,应使用 `4294967295` 或显式 UInt64。 +3. **run_qemu 必须显式使用 UEFI 固件**: 仅 `qemu-system-x86_64 -drive ...` 不保证走 UEFI,需要加载 `edk2-x86_64-code.fd` 或 `OVMF_CODE.fd`。 + +## 2026-06-29 — 实现 6 个真实 DKM 驱动(timer/acpi/pci/console_fb/bootfs/vfs) + +### 经验 + +1. **kernel_api 渐进暴露平台能力**: rsdp_address → fb_address → boot_modules_response,按需添加字段避免了 ABI 大范围修改。 +2. **驱动独立编译必须保持 struct 布局一致**: 驱动本地定义的 `dkm_kernel_api` 必须包含所有字段(含 padding),否则偏移量错位导致读取错误。 +3. **Limine framebuffer 让早期图形可行**: 通过 Limine framebuffer request 获取线性帧缓冲,在无 GPU 驱动下即可实现 8x8 字体渲染。 + +### 教训 + +1. **PCI 扫描不需要 IRQ**: 纯配置空间枚举完全独立于中断系统,pci 驱动可以第一个实现。 +2. **bootfs/vfs 是软件驱动,不依赖硬件**: 利用 Limine boot module 可直接构造内存文件系统,无需块设备。 +3. **console_fb 的 bpp/pitch 必须从 bootloader 读取**: 帧缓冲格式随 QEMU/固件变化,硬编码会导致花屏。 + +## 2026-06-30 — 记录 devfs/fat32/IDT/IRQ/ps2kbd/e1000 阶段成果 + +### 经验 + +1. **先做内核 IRQ 底座再写中断驱动**: IDT 0–47 stub、PIC remap、统一 `idt_handler` 和 `irq_register` 先闭环后,`ps2kbd` 与 `e1000` 都能通过同一 IRQ 注册路径接入。 +2. **HHDM 是早期 MMIO 的最短路径**: 通过 Limine HHDM request 暴露 `kernel_api.hhdm_offset`,e1000 可直接把 PCI BAR0 物理地址映射到高半区虚拟地址,避免在页表/MMIO allocator 未完善前阻塞网卡初始化。 +3. **软件文件系统可先用 boot module 验证**: devfs 不依赖块设备,fat32 可用 `driver/test.fat32` 合成镜像验证 BPB/FAT/目录项/文件读取,为后续 AHCI/NVMe 块设备接入保留接口经验。 +4. **当前真实驱动进入 10/14 阶段**: 已完成 timer、acpi、pci、console_fb、bootfs、vfs、devfs、fat32、ps2kbd、e1000,剩余真实驱动集中在 APIC、块设备和 virtio_net。 + +### 教训 + +1. **汇编文件命名会影响 object 冲突**: `idt.S` 和 `idt.c` 同名会同时输出 `idt.o`,应将汇编入口拆成 `idt_stubs.S`,避免覆盖或重复符号。 +2. **中断 stub 宏要显式区分 error code**: x86_64 异常中部分 vector 自动压入 error code,stub 生成不正确会破坏统一栈帧,导致 `iretq` 返回失败或异常连锁。 +3. **驱动本地 ABI struct 必须同步**: `irq_register`、`hhdm_offset` 等字段加入后,所有真实驱动本地 `dkm_kernel_api` 定义都必须保持相同偏移,否则会读取到错误函数指针或平台地址。 +4. **QEMU 过滤日志可能隐藏有效输出**: FAT32 README 内容、VFS dump、e1000 reset warning 等行如果没有统一前缀,使用 Select-String 时可能被漏看;驱动日志应带稳定模块前缀。 +5. **e1000 reset timeout 暂不等于驱动失败**: 当前已能完成 PCI 探测、MAC 读取、IRQ 注册和 link setup,reset timeout 需要后续结合硬件状态轮询/RX-TX 初始化继续收敛。 + +## 2026-06-30 — 实现 APIC discovery 驱动 + +### 经验 + +1. **APIC 应先 discovery 后接管**: 当前内核 IRQ ack 仍走 PIC EOI,stage0 `apic` 先做 CPUID、`IA32_APIC_BASE`、MADT、LAPIC MMIO 只读探测,并继续保留 PIC 路由,能避免破坏现有 `ps2kbd/e1000` IRQ 路径。 +2. **MADT 可不依赖 ACPI 驱动**: `apic` 在 manifest 中早于 `acpi`,因此直接使用 `kernel_api.rsdp_address` 解析 RSDP/XSDT/MADT,枚举 LAPIC、IOAPIC 和 interrupt source override。 +3. **HHDM 适合只读 LAPIC 验证**: 通过 `kernel_api.hhdm_offset + lapic_phys` 可读取 LAPIC ID/version/SVR,验证 MMIO direct map 可用于平台控制器探测。 +4. **QEMU 验证链路明确**: 日志显示 `MADT found`、`IOAPIC addr=0xfec00000`、`LAPIC version reg=0x50014`、`discovery complete; PIC routing retained`,随后 `pci` 继续加载并 `UTSM SELFTEST PASS`。 + +### 教训 + +1. **不要提前切换 IOAPIC**: 未扩展 IDT/vector allocator/APIC EOI 前,不能禁用 PIC、重编 IOAPIC RTE 或启用 LAPIC timer,否则中断投递和 EOI 后端会不一致。 +2. **驱动也要用 kernel code model**: APIC 驱动代码较大时,Clang 会生成更多字符串地址 relocation;使用普通 code model 会让部分高半区字符串日志异常,需用 `-mcmodel=kernel` 编译真实 `.drv`。 +3. **APIC required 驱动不能误失败**: `apic` 是 stage0 required 且为 `pci` 提供 `irq` 依赖,除非 `api/log/irq_register` 缺失,否则 discovery 不完整也应返回 0 并保持 PIC fallback。 + +## 2026-06-30 — 实现 AHCI discovery 驱动 + +### 经验 + +1. **块设备先枚举再 DMA**: AHCI 第一版只做 PCI class 探测、BAR5/ABAR HHDM 映射、HBA CAP/GHC/PI/VS 和 port 寄存器枚举,不发命令,能在无 DMA allocator/页固定接口前安全验证控制器路径。 +2. **AHCI 可作为 block provider 先接入**: `ahci` 是 stage1 optional,依赖 `pci/irq` 并提供 `block`,即使还未提供真实读写,也能让 manifest/VFS/fat32 依赖链进入下一阶段测试。 +3. **QEMU ICH9 AHCI 验证稳定**: QEMU 下探测到 `8086:2922`,ABAR 位于 `0x81084000`,PI 为 `0x3f`,6 个 implemented ports 可读,port 2 显示 ATAPI signature `0xeb140101`。 + +### 教训 + +1. **PCI class dword 位移容易错**: 读取 offset `0x08` 时,prog_if 在 bits 15:8,subclass 在 bits 23:16,class_code 在 bits 31:24;按 byte offset 直觉解析会导致 AHCI 控制器找不到。 +2. **不要提前清端口/发命令**: 未建立 command list/FIS/PRDT DMA 内存、cache flush 和中断完成路径前,不能贸然 stop/start port 或写 PxCI。 +3. **HHDM MMIO 只读仍要保守**: 写 PCI command 只启用 MEM/BUSM,AHCI HBA 寄存器阶段保持只读,避免破坏固件/仿真器当前端口状态。 + +## 2026-06-30 — 实现 NVMe discovery 驱动 + +### 经验 + +1. **NVMe 也应先 discovery**: 第一版只做 PCI class `01/08/02` 探测、BAR0/BAR1 解析、IRQ line 读取,不创建 admin queue、不写 CC、不提交命令,能安全接入 stage1 storage 链路。 +2. **测试设备需显式加入 QEMU**: 默认启动脚本只挂 virtio 磁盘,不会出现 NVMe 控制器;临时加入 `-device nvme` 后可探测到 QEMU NVMe `1b36:0010`。 +3. **高位 BAR 要主动保护**: QEMU 将 NVMe BAR0 放到 `0x000000c000004000`,当前 HHDM/页表路径不能安全访问该 4G 以上 PCI MMIO,因此驱动记录 BAR 后跳过 register read,系统仍能 SELFTEST PASS。 + +### 教训 + +1. **不能假设所有 BAR 都在 4G 以下**: e1000/AHCI 的低位 BAR 可直接 HHDM 读取,但 NVMe 常见为 64-bit BAR,需要后续实现高位 PCI MMIO 映射或页表映射接口。 +2. **MMIO 读前必须检查映射能力**: 直接读未映射高位 BAR 会导致异常或 QEMU 退出,真实驱动必须在访问寄存器前验证地址范围和映射策略。 +3. **NVMe admin queue 不能提前做**: 未准备 contiguous DMA、doorbell stride、completion polling/IRQ 和 cache coherency 前,不应写 CC.EN 或 ASQ/ACQ。 + +## 2026-06-30 — 实现 virtio_net discovery 驱动并完成 14/14 真实驱动 + +### 经验 + +1. **virtio-net 可先做 capability discovery**: 第一版只识别 PCI vendor/device、BAR、IRQ line 和 modern virtio PCI capabilities,不做 feature negotiation、不建 virtqueue,能安全完成网络驱动入口闭环。 +2. **兼容 transitional/modern 识别**: QEMU `virtio-net-pci` 以 `1af4:1001` 形式出现,按 virtio transitional ID 计算 virtio_id=1 即可识别 net device,同时仍能枚举 modern capability 链。 +3. **14 个外部 `.drv` 全部真实化**: manifest 中 timer、apic、acpi、pci、console_fb、ahci、nvme、bootfs、vfs、fat32、devfs、e1000、virtio_net、ps2kbd 均已替换为可执行 DKM 驱动。 + +### 教训 + +1. **测试设备要显式挂载**: 默认 QEMU 脚本没有 virtio-net,需要临时添加 `-netdev user` 和 `-device virtio-net-pci` 才能验证驱动路径。 +2. **virtio capability 链只应只读枚举**: 未建立 DMA ring 和设备状态机前,不应写 common_cfg/device_status 或 queue_select/queue_enable。 +3. **多个网卡测试会改变 PCI 拓扑**: 加入 virtio-net 后 e1000 可能不存在或位置变化,驱动必须把“设备未找到”视为 optional 正常路径。 + +## 2026-06-30 — 规划 14/14 驱动完成后的下一阶段路线 + +### 经验 + +1. **先补底座再做数据路径**: 14 个驱动已完成 discovery/加载闭环,下一步最优先不是继续堆功能,而是补物理页分配、页表/MMIO 映射、DMA buffer 和屏障约定。 +2. **块设备应先于文件系统深化**: AHCI/NVMe 的 IDENTIFY/READ 能把 FAT32 从 `test.fat32` 迁移到真实 block provider,是后续 VFS 能力扩展的关键路径。 +3. **IRQ 后端需要独立升级**: 现有 PIC fallback 已稳定,APIC/IOAPIC/MSI 应作为单独阶段推进,避免在设备数据路径中同时改变中断后端。 + +### 教训 + +1. **不要在 discovery 驱动里提前发命令**: AHCI/NVMe/virtio/e1000 都需要 DMA、队列、IRQ 完成路径;缺少底座时发命令会把问题混在一起。 +2. **高位 PCI MMIO 是必须解决的问题**: NVMe BAR0 已暴露 4G 以上映射缺口,后续不能继续依赖简单 HHDM 假设覆盖所有设备 MMIO。 +3. **文档状态要及时修正**: CLAUDE.md 曾残留旧的“待实现 nvme/virtio_net”尾部,完成阶段性目标后必须同步路线,避免后续开发误判当前状态。 + +## 2026-06-30 — 编写 DSK 主内核 README + +### 经验 + +1. **明确两阶段内核边界**: `utsm.elf` 是 bootloader 加载的首阶段内核,`deshab.elf` 是 DSK 主内核;文档中必须明确 UTSM 后续要继续执行到 DSK。 +2. **先定义 boot context**: UTSM 跳转 DSK 前应通过版本化 `dsk_boot_context` 传递 HHDM、RSDP、framebuffer、boot modules、DKM API/driver table、UTSM/DRR 状态等信息。 +3. **先做最小 DSK 闭环**: DSK 第一版只需接收 context、打印 boot banner、执行 selftest,即可验证 `CODE/dsk -> SYSTEM/system/deshab64/deshab.elf` 的链路。 + +### 教训 + +1. **不要让 DSK 重复早期启动职责**: Limine request、early serial、DKM 首轮加载和 discovery 应保留在 UTSM 阶段,DSK 接管长期运行期。 +2. **ELF loader 第一版要收窄范围**: DSK loader 应先只支持 ELF64、PT_LOAD、入口跳转,不引入动态链接、TLS、demand paging 或符号解析。 +3. **构建路径要固定**: 主内核产物固定为 `SYSTEM/system/deshab64/deshab.elf`,后续 build.ps1 和 limine/bootfs 逻辑都应围绕该路径实现。 + +## 2026-06-30 — 实现 UTSM 到 DSK 最小跳转闭环 + +### 经验 + +1. **先用 FAT32 路径预加载闭环**: `deshab.elf` 已放入 `SYSTEM/system/deshab64/`,Limine 从 FAT32 ESP 预加载,UTSM 按 `/system/deshab64/deshab.elf` 定位模块并加载,符合后续替换为真实 FAT32/block provider 的路径语义。 +2. **最小 ELF loader 足够验证架构**: 第一版只支持 ELF64、ET_DYN/ET_EXEC、PT_LOAD、一次性复制/清零和入口跳转,已经能从 UTSM 进入 DSK 并传递 context。 +3. **DSK 不返回符合主内核语义**: DSK 校验 `dsk_boot_context` 后输出 HHDM/RSDP/framebuffer/boot modules/DKM API 等信息,`SELFTEST PASS` 后 halt,完成主内核接管模型验证。 + +### 教训 + +1. **构建脚本要显式集成 DSK**: build.ps1 必须同时构建 `CODE/UTSM` 和 `CODE/dsk`,并检查 `SYSTEM/system/deshab64/deshab.elf` 是否存在,否则镜像中不会包含主内核。 +2. **PowerShell/MinGW make 路径状态要谨慎**: `Push-Location`/`make -C` 在当前环境中出现路径解析问题,最终采用显式 `$DskDir` 与 make 工作目录方式稳定构建。 +3. **当前还不是真实 FAT32 自读取**: 现在 UTSM 通过 Limine module 拿到 `deshab.elf`,下一步要把来源替换为 AHCI/NVMe block provider + FAT32 读取。 + +## 2026-06-30 — 建立 DMA API 并验证 AHCI IDENTIFY/READ + +### 经验 + +1. **DMA 底座先从低位页开始**: 通过 Limine memmap 选取 4G 以下 usable 区间,配合 HHDM 形成 `phys/virt/size` 三元组,足以支撑 AHCI CLB/FIS/command table/data buffer。 +2. **kernel_api.dma 是块设备公共入口**: 将 `dkm_dma_api.alloc_pages` 暴露给 DKM 后,AHCI 不再依赖私有内存假设,后续 NVMe/e1000/virtio-net 也可以复用。 +3. **AHCI 最小数据路径已跑通**: 挂临时 SATA 测试盘时,AHCI 能完成 SATA disk IDENTIFY,并执行 READ DMA EXT 读取 LBA0;默认无 SATA 盘时会跳过命令并继续进入 DSK。 + +### 教训 + +1. **AHCI command header 布局必须严格按规范**: 初版把 `prdtl/prdbc` 字段布局写错,导致 HBA 看到错误 PRDT 信息,IDENTIFY 一直超时。 +2. **不要对 ATAPI/空端口发 ATA IDENTIFY**: 默认 QEMU 场景只有 ATAPI/空端口时应跳过命令,否则会产生误导性 timeout。 +3. **测试盘场景要和默认启动分开**: 默认 `run_qemu.bat` 保持稳定启动;SATA 测试盘用于验证 AHCI 数据路径,后续可整理成独立自动化场景。 + +## 2026-06-30 — 接入 block provider 并让 FAT32 优先读真实块设备 + +### 经验 + +1. **block provider registry 解耦驱动和文件系统**: UTSM 提供 `kernel_api.block.register_device/read` 后,AHCI 只负责注册 `ahci0` 和实现 LBA read,FAT32 不需要知道 AHCI 细节。 +2. **保留 boot module 回退很关键**: 默认 QEMU 没有 SATA disk 时,FAT32 会继续读取 `driver/test.fat32`,保证启动链和 DSK 跳转不受真实块设备实验影响。 +3. **SATA FAT32 镜像验证了真实路径**: 把 `test.fat32` 扩展成 SATA 测试盘后,AHCI 注册 block provider,FAT32 通过 block provider 读取 BPB/root/README.TXT,并继续进入 DSK。 + +### 教训 + +1. **ABI 字段只能追加不能插入**: `dkm_kernel_api.block` 必须追加到结构尾部,不能插在 `dma` 后面,否则会改变已有驱动本地 ABI 字段偏移。 +2. **block read 需要支持多扇区分块**: AHCI 当前 command table 只有一个 PRDT entry,公共 read 回调内部按最多 8 sector 分块,避免一次请求过大。 +3. **FAT32 应先校验 BPB 再切换来源**: block provider 读取成功但 BPB 无效时必须回退 boot module,否则普通测试盘会让 FAT32 误判失败。 + +## 2026-06-30 — DSK 加载:优先 FAT32 block provider,回退 Limine module + +### 经验 + +1. **两阶段加载优先级清晰**: DSK loader 先尝试 `kernel_api.block` + 内嵌 FAT32 解析,读取根目录 `DESHAB ELF` 文件;无块设备或文件不存在时自动回退 Limine boot module,保证默认场景稳定。 +2. **FAT32 data_start 必须乘以 fat_count**: `data_start_sec = rsvd + fat_count * spf`,漏掉第二份 FAT 会导致 root directory cluster 错位。 +3. **静态 BSS 缓冲区比 arena 分配更可靠**: DSK loader 的 64KB BPB/FAT 缓冲区、4KB cluster buffer、64KB file data buffer 使用 BSS 静态数组,绕过了 UTSM arena 4MB 限制,并已扩大 arena 到 16MB。 + +### 教训 + +1. **arena 容量需提前评估**: 原 4MB arena 在加载 14 个 DKM 驱动后剩余空间不足,导致 `kmem_alloc_aligned(64KB)` 失败,已调整为 16MB。 +2. **AHCI 单扇区 block read 在重入时可能不稳定**: batch read 128 扇区总是成功,但单扇区连续读偶尔超时;DSK loader 最终全部改用 cluster 级批量读取解决。 +3. **调试时 hex dump 比 ASCII 日志可靠**: FAT32 目录项包含空格,ASCII 日志容易遗漏;用 `log_hex64` 输出原始字节准确定位了 `data_start_sec` 偏移错误。 + + +## 2026-06-30 — DSK 首次启动链路 + FirstInit 用户设置向导 + mouseInit + +### 经验 + +1. **PIE ELF 必须处理 R_X86_64_RELATIVE**: DSK 用 `-pie` 编译 FirstInit/mouseInit,加载后所有全局指针/数组引用需要重定位。在 `dsk_load_elf` 中解析 `PT_DYNAMIC` → `DT_RELA/DT_RELASZ` → 应用 `R_X86_64_RELATIVE`(`*slot = load_bias + addend`),否则解引用全局数据会 page fault。 +2. **PIE 指针数组需改用栈上字符数组**: DSK 自身的 `static const char *fi_txt_path[]` 含 `R_X86_64_RELATIVE` 重定位项,但 UTSM loader 不处理 `.rela.dyn`。改用栈上 `char[]` 逐字节赋值避免重定位依赖。 +3. **`__attribute__((unused))` 在 -O2 下可能导致函数被优化掉**: `fb_char`/`fb_text` 标记为 unused 后,编译器可能内联消除或改变调用约定,导致按键时崩溃。去掉 unused 属性即可修复。 +4. **FAT32 文件缓冲区必须足够大**: FirstInit.elf 从 26KB 增长到 88KB 后超过 DSK 的 `g_fdata[65536]`,导致加载失败。扩大到 256KB 解决。 +5. **PS/2 控制器写命令在 QEMU 早期阶段可能阻塞**: mouseInit 的 `ps2_write_cmd` 写 `0x64` 端口后 `wait_input_clear` 死循环。安全 stub 版本只读状态寄存器,不写控制器。 +6. **预渲染中文位图是最简字体方案**: 用 Python/Pillow 从 simhei.ttf 渲染指定中文字符串为 8bpp 灰度 C 数组,嵌入 ELF,避免运行时 TTF 解析。 + +### 教训 + +1. **ELF 文件大小监控**: 新增功能后 ELF 体积可能超过静态缓冲区。应在 `dsk_load_elf` 中校验 `found_size <= sizeof(g_fdata)` 并打印日志。 +2. **圆角矩形绘制需完整边界检查**: `draw_card` 的圆角填充如果缺少 `sq <= 0` 检查,`isqrt_int` 会收到负数转无符号的大值,导致越界写入。 +3. **帧缓冲 blend 不应读取 framebuffer**: `blend(line[c], fg, a)` 读取帧缓冲可能触发 page fault 或性能问题。改为 `blend(bg, fg, a)` 纯写入。 +4. **block API 偏移量必须精确**: `dkm_kernel_api` 中 `block` 字段在 `0xA8`(168),之前误用 `0x60`(96 = `rsdp_address`),导致 `g_block_read` 永远为 NULL。 +5. **AHCI 单扇区读不稳定**: 批量读 256 扇区成功,但回退到单扇区读会挂死。DSK 的 `fat32_read_root_file` 应尽量在 256 扇区缓冲内完成所有操作。 + +### 当前已完成 + +```text +启动链路: + Limine → utsm.elf → DKM 14 驱动加载 → DSK (deshab.elf) + → 旋转加载动画 (双缓冲, 顺时针, comet-tail) + → FAT32 读取 firstInit.txt + → firstInit=0: + DSK → mouseInit.elf (安全 stub) → 返回 DSK + DSK → FirstInit.elf + → 环淡出 → "欢迎使用 Deshab" → 设置提示 + → 账户设置卡片 (圆角矩形, 白色输入框, 18px 中文) + → 键盘输入: 计算机名/用户名/密码 + → SHA256 密码 + XOR 加密配置缓冲 + → "设置完成" 提示 + → firstInit≠0: 正常启动 (待实现) + +字体系统: + CODE/font/mkfont.py → simhei_16/24/32.dbf (DBF 格式) + CODE/font/render_embedded.py → text_bitmaps.c (预渲染位图) + SYSTEM/system/font/simhei.ttf (原始 TTF) + +初始化程序架构: + DSK (deshab.elf) 负责调度所有系统初始化程序 + mouseInit.elf — DSK 直接加载调用, 返回 DSK + FirstInit.elf — DSK 加载跳转, 只做用户设置向导 +``` + +### 下一步计划 + +1. **修复 FirstInit 按键崩溃**: 定位 `fb_text` 在 `-O2` 下的优化问题(已移除 unused 属性,待验证) +2. **实现鼠标光标移动**: 从 PS/2 IRQ12 读取鼠标包,更新光标位置 +3. **user.conf 写盘**: 需要 `dkm_block_api.write` + AHCI write DMA + FAT32 文件覆写 +4. **网络配置界面**: FirstInit 第二步,IP/DHCP/网关设置 +5. **FirstInit 完成后跳转**: 设置完成后写 firstInit=1,跳转正常启动路径 +6. **block write API**: 在 `dkm_block_api` 中增加 `write` 函数,AHCI 实现 WRITE DMA +7. **FAT32 write 支持**: 文件创建、目录项写入、FAT 链更新 +8. **内存与 MMIO 基础设施**: 物理页分配器、页表映射、高位 PCI MMIO +9. **IRQ 后端升级**: APIC EOI/IOAPIC/MSI-X +10. **网络数据路径**: e1000 RX/TX, virtio-net virtqueue + +## 2026-06-30 — 优化 FirstInit framebuffer 首次启动 UI + +### 经验 + +1. **渐变背景需要统一擦除路径**: DSK spinner 与 FirstInit 共享 `BG_TOP/BG_BOTTOM` 语义,并通过按 y 计算背景色的局部擦除函数维护一致背景,可避免动画/转场留下纯色块。 +2. **圆角矩形应只绘制有效区域**: 直接逐像素判断 rounded-rect coverage,只写圆角范围内的像素,比“先填满矩形再擦角”更适合卡片叠层和输入框,能避免角落脏色块。 +3. **嵌入字体尺寸可独立调优**: 将 `render_embedded.py` 的字号从 32px 降到 24px 后,`text_bitmaps.c` 中 welcome 位图从 240x38 缩到 180x30,卡片在 1280x800 内更轻盈。 + +### 教训 + +1. **帧缓冲 sprite 也要匹配底层背景**: DSK spinner 的离屏 sprite 若仍用纯色 bg 初始化,即使屏幕先画了渐变,也会在 spinner 方块区域破坏渐变连续性。 +2. **构建产物可能被 QEMU 锁定**: `ISO/deshab.img` 被正在运行的 `qemu-system-x86_64.exe` 占用时,build.ps1 可完成编译/链接但会在 GPT IMG 打包阶段失败;重新打包前需关闭 QEMU。 +3. **Python 环境不等于有 Pillow**: 系统只有 WindowsApps python stub,Blender/WeCom 自带 Python 也缺 PIL;可临时使用 Blender Python + `.build_tmp/pydeps` 安装 Pillow 生成字体位图,避免污染项目源码。 + +## 2026-07-01 — 添加 netman 联网器与网络配置加载 + +### 经验 + +1. **系统初始化器应由 DSK 调度**: `netman.elf` 与 `mouseInit.elf` 一样作为系统组件,由 `deshab.elf` 直接从 FAT32 加载调用,避免 FirstInit 接管系统初始化职责。 +2. **早期配置要有扁平 8.3 兜底**: 用户路径保留为 `SYSTEM/system/deshab64/network/conf/conf.conf`,但 SATA 测试镜像额外写入 root 目录 `NETCONF.CNF`,方便早期 loader 在没有 VFS 路径解析时读取。 +3. **先做配置管理闭环**: 在 `kernel_api.net=0`、e1000/virtio-net 仍缺 RX/TX 数据路径时,先实现 netman 配置解析、状态输出和启动调度,为后续 net API/协议栈接入留下稳定入口。 + +### 教训 + +1. **完整联网不能只写 UI**: FirstInit 的网络设置页只产生配置,真正联网还必须补 `kernel_api.net`、netdev 注册、NIC RX/TX、ARP/IP/UDP/DHCP/DNS。 +2. **打包工具也要同步编译**: 修改 `.build_tmp/mkfat32.c` 后必须重新生成 `mkfat32.exe`,否则 build.ps1 会继续使用旧打包逻辑,导致新 ELF/配置未进入 SATA 镜像。 +3. **早期读取文件要限制格式**: netman 第一版读取 root 8.3 文件,避免在没有完整 VFS/FAT32 路径遍历前引入复杂目录解析和写盘风险。 + +## 2026-07-01 — 实现最小 net API 与 netdev 注册查询闭环 + +### 经验 + +1. **新增 ABI 能力优先复用保留槽位**: `dkm_kernel_api.net` 原本就是占位字段,只把类型从 `const void *` 收窄为 `const dkm_net_api *` 并赋值 `net_get_api()`,不在其前方插入字段,可保持 net 偏移稳定。 +2. **网络底座先用固定表**: 早期驱动按 stage 顺序单线程加载,固定 8 槽 netdev registry 具备 O(1) 注册与查询,避免在无协议栈阶段引入锁、队列和 DMA 生命周期复杂度。 +3. **discovery-only 驱动也能形成管理闭环**: e1000/virtio_net 先注册 name/MAC/flags,netman 即可枚举设备,为后续 RX/TX ring、virtqueue 和协议栈提供稳定入口。 + +### 教训 + +1. **本地驱动 ABI 镜像要同步**: DKM 驱动当前各自内联 `dkm_kernel_api` 布局,新增 typed net API 时必须同步 e1000/virtio_net 的本地结构定义,否则字段访问容易退化成裸偏移错误。 +2. **初始化器指定字段要按真实结构位置**: `block` 位于 `dkm_kernel_api` 尾部,不能因阅读局部字段时把 `.block` 放到 `.dma` 后导致重复初始化 warning。 +3. **build.bat 不会自动重编外部 DKM 源**: 修改 `CODE/DKM/*/*.c` 后需确保对应 `.drv` 产物更新到 `SYSTEM/driver`,否则镜像可能仍打包旧驱动。 + +## 2026-07-01 — e1000 最小 RX/TX ring 与构建接入 + +### 经验 + +1. **先暴露能力位再接协议栈**: e1000 注册 netdev 时增加 `TX_READY/RX_READY` 标志,netman 可以明确区分“只发现设备”和“具备收发 ring”的设备。 +2. **DMA ring 使用低 4G 连续页更稳**: 当前 DMA allocator 已提供 HHDM 虚拟地址与低 4G 物理地址,适合先搭 e1000 descriptor ring 和 2KB buffer 的最小闭环。 +3. **网络设备应纳入主构建**: 把 e1000/virtio_net `.drv` 编译加入 build.ps1,避免手动编译驱动后忘记重新打包,导致 QEMU 仍加载旧驱动。 + +### 教训 + +1. **QEMU 测试脚本要提供真实 NIC**: 不加 `-device e1000e` 时 PCI 里没有 `8086:10d3`,e1000 驱动只能走 not found 路径,无法验证 RX/TX ring。 +2. **网卡中断早期开启容易刷屏或阻塞**: 在 IOAPIC/MSI 路由完善前,e1000 中断保持 masked,收发先走 polling 路径更安全。 +3. **串口过滤会掩盖启动卡点**: 管道过滤日志可避免 100MB 输出限制,但若启动阶段卡住,只看到少量匹配行;需要结合未过滤日志或关键阶段日志判断。 + +## 2026-07-02 — 修 DMA 与 e1000 buffer 卡死 + +### 经验 + +1. **DMA 分配本身已可用**: 通过逐页 `alloc_pages(1)` 加分段 raw_log 定位,确认 RX/TX descriptor ring 和每个 2KB buffer 页都能成功分配返回,卡点不在 allocator 的分配/清零逻辑。 +2. **静态 BSS DMA pool 是临时方案**: 当前 `dma.c` 用内核 BSS 里的 16MiB `g_dma_static_pool` 加 `virt_to_phys()` 反推物理地址,作为 bump allocator 可分配,但网卡 bus-master 真实写入时的物理地址可靠性没有保证。 +3. **稳定边界要固化**: e1000 现在在分配 descriptor ring 后直接返回 -6,只注册 `LINK_UP`,保证系统稳定启动到 netman/FirstInit,避免反复试错阻塞其他开发。 + +### 教训 + +1. **能靠日志“跑过”的 bug 往往是内存/时序问题**: e1000 buffer/ring enable 阶段“加 raw_log 能多跑几步、去掉就更早卡”,是典型的 DMA 目标内存被网卡写坏或时序竞争,不能靠加日志掩盖。 +2. **bump allocator 不足以支撑真实 bus-master DMA**: 需要一个真正的物理页分配器(memmap USABLE → page bitmap,显式标记 kernel/module/framebuffer/arena/stack/DMA pool 已用),才能根治 e1000 RX/TX、AHCI PRDT、virtqueue 的连续物理内存需求。 +3. **不要无限试错**: 反复缩小 ring 深度、搬动日志无法根治底层物理内存问题,应固化到已知稳定状态并明确记录卡点,把真正的物理页分配器作为下一步。 + +## 2026-07-02 — 物理页 bitmap 分配器根治 e1000 DMA + +### 经验 + +1. **HHDM 直映射是 DMA 地址一致性的关键**: 把 `dma.c` 从"BSS 静态 pool + virt_to_phys 反推"改为"memmap USABLE → 物理页 bitmap"后,分配返回 `virt = hhdm_offset + phys`,保证 CPU 访问的虚拟地址与网卡 bus-master 使用的物理地址指向同一页,e1000 完整 RX/TX ring 启用不再卡死。 +2. **依赖 Limine USABLE 语义可省掉手动避让**: Limine 的 USABLE 区域已排除 kernel/modules/framebuffer/bootloader 结构,直接用它建 bitmap 就不需要再手动 trim kernel/arena/stack,代码更简单也更不易错。 +3. **验证要看能力位闭环**: netdev flags 从 `0x1`(仅 LINK_UP)变成 `0x7`(LINK_UP|TX_READY|RX_READY),netman 打印 `tx=ready rx=ready`,说明底层 DMA + ring enable 全链路打通。 + +### 教训 + +1. **反推物理地址不可靠**: 用内核高半虚拟地址(BSS)反推物理地址喂给设备 DMA,物理页与 CPU 映射不保证一致,是之前 e1000 一启用 RCTL/TCTL 就卡死的根因;DMA 缓冲必须来自明确的物理页分配。 +2. **低 4G 约束要显式**: e1000 等 32 位 BAR 设备的 DMA 描述符/缓冲需落在低 4G,bitmap 分配器直接把上界钳到 `0x100000000` 并保留最低 1MiB,避免 legacy 区。 + + + diff --git a/RE/UTSM_算法记录.md b/RE/UTSM_算法记录.md new file mode 100644 index 0000000..b932e68 --- /dev/null +++ b/RE/UTSM_算法记录.md @@ -0,0 +1,504 @@ +# UTSM 算法记录 + +```text +UTSM = UUID-Tuned Sealed Memory +中文名:UUID 调谐封缄内存 +``` + +本文记录 UTSM v1 的核心算法。 + +--- + +## 1. 段创建算法 + +目标:为任务创建一个只存密文的连续内存段,并返回 capability。 + +```text +utsm_create_segment(process, size, flags) + 1. size 按 64B / 4KB 对齐。 + 2. 从 buddy/slab 分配 cipher data pages。 + 3. 从 DMP pool 分配 DMP。 + 4. 生成 segment_uuid。 + 5. key_epoch = process.crypto_epoch。 + 6. tweak_seed = hash(process_uuid, segment_uuid, key_epoch)。 + 7. 初始化 dirty_bitmap。 + 8. 初始化 mac_table。 + 9. 填写 utsm_segment_desc。 + 10. 填写 utsm_dmp。 + 11. generation++。 + 12. 生成 utsm_capability。 + 13. 将 capability 写入任务 capability table。 + 14. 返回 capability。 +``` + +复杂度: + +```text +段元数据初始化:O(1) +数据页分配:O(page_count) +热路径访问定位:O(1) +``` + +--- + +## 2. 密钥派生算法 + +UUID 不作为明文密钥,而是作为调谐因子。 + +```text +segment_key = KDF( + DRR_root_key, + process_uuid, + segment_uuid, + key_epoch +) +``` + +每 64B cache line 生成 tweak: + +```text +line_index = offset / 64 +tweak = hash(tweak_seed, line_index, key_epoch) +``` + +加密和解密对称: + +```text +ciphertext = plaintext XOR stream(segment_key, tweak) +plaintext = ciphertext XOR stream(segment_key, tweak) +``` + +设计理由: + +```text +1. 避免十进制 UUID 解析。 +2. 避免 UUID 孔洞破坏连续内存。 +3. 以 64B cache line 为单位,适合 CPU cache 和 SIMD。 +4. key_epoch 可用于快速作废旧段。 +``` + +--- + +## 3. UTRW Fast Read 算法 + +```text +UTRW_READ_FAST(cap, offset, len, dst) + 1. desc = SEGMENT_TABLE[cap.segment_slot]。 + 2. 检查 desc.generation == cap.generation。 + 3. 检查 cap.rights 包含 READ。 + 4. 检查 cap.epoch == desc.key_epoch。 + 5. 检查 offset + len <= desc.cipher_length。 + 6. 检查 desc.state == ACTIVE。 + 7. key = PCKC_LOOKUP(desc.segment_slot, desc.key_epoch)。 + 8. 如果 key miss,进入 slow path。 + 9. 读取 writer_seq_before。 + 10. 如果 writer_seq_before 为奇数,重试或 slow path。 + 11. 对访问范围涉及的每个 64B line: + a. 计算 line_index。 + b. 生成 tweak。 + c. 读取密文 line。 + d. 解密成明文 line。 + e. 拷贝目标字节到 dst。 + 12. 读取 writer_seq_after。 + 13. 如果 before != after,重试。 + 14. 返回 OK。 +``` + +复杂度: + +```text +段定位:O(1) +读写长度 n:O(n / 64) +``` + +--- + +## 4. UTRW Fast Write 算法 + +```text +UTRW_WRITE_FAST(cap, offset, len, src) + 1. desc = SEGMENT_TABLE[cap.segment_slot]。 + 2. 检查 desc.generation == cap.generation。 + 3. 检查 cap.rights 包含 WRITE。 + 4. 检查 cap.epoch == desc.key_epoch。 + 5. 检查 offset + len <= desc.cipher_length。 + 6. 检查 desc.state == ACTIVE。 + 7. key = PCKC_LOOKUP(desc.segment_slot, desc.key_epoch)。 + 8. 如果 key miss,进入 slow path。 + 9. 如果是强恢复段,写 WRITE_INTENT log。 + 10. writer_seq++,进入奇数写入状态。 + 11. 对访问范围涉及的每个 64B line: + a. 计算 line_index。 + b. 生成 tweak。 + c. 如果覆盖完整 line,直接使用 src 明文。 + d. 如果部分覆盖 line,读取旧密文,解密旧明文,合并新明文。 + e. 加密成新密文。 + f. 写回 cipher data。 + 12. 标记 dirty_bitmap。 + 13. writer_seq++,回到偶数稳定状态。 + 14. 如果是强恢复段,写 WRITE_COMMIT log。 + 15. 返回 OK。 +``` + +部分写规则: + +```text +UTSM 的加密单位是 64B line。 +如果只写 line 的一部分,必须读旧密文、解密、合并、重新加密整条 line。 +``` + +--- + +## 5. UTRW Slow Path 算法 + +Slow Path 触发条件: + +```text +PCKC key miss +generation mismatch +epoch mismatch +state != ACTIVE +checkpoint busy +recovery busy +MAC failed +segment poisoned +out of bounds +access denied +``` + +处理策略: + +```text +KEY_MISS + -> 派生 segment_key + -> 生成 key_schedule + -> 写入 PCKC + -> 回到 fast path + +STALE_CAPABILITY + -> 尝试从 capability table 刷新 + -> 失败则返回错误 + +EPOCH_EXPIRED + -> 刷新 cap.epoch 和 auth_tag + -> 清除旧 PCKC key + -> 回到 fast path + +CHECKPOINT_BUSY + -> 短暂等待或写 side log + +RECOVERING + -> 阻塞任务,等待 DRR 完成恢复 + +MAC_FAILED + -> 标记 suspicious page + -> 通知 DRR + -> 尝试 page rollback + +SEGMENT_POISONED + -> 拒绝访问 + -> 交给 DRR 决定回滚、重建或 kill task +``` + +--- + +## 6. PCKC 查找算法 + +每 CPU 保存少量热段 key。 + +```text +PCKC_LOOKUP(slot, epoch) + 1. 检查 current hot slot。 + 2. 检查 hot_segment_slot[0..N]。 + 3. 如果 slot 和 epoch 均匹配,返回 key_schedule。 + 4. 否则返回 MISS。 +``` + +替换策略: + +```text +第一版使用小型 LRU 或替换最冷 slot。 +``` + +建议槽数: + +```text +8 个 key slot 起步。 +高并发或服务型任务可扩展到 16 个。 +``` + +--- + +## 7. Checkpoint 算法 + +### 7.1 Dirty Shard 标记算法 + +Dirty Bitmap 分片为 Dirty Shard,避免 checkpoint 扫描整块大 bitmap。 + +```text +UTSM_MARK_DIRTY(desc, page_index) + 1. shard = desc.dirty_shards[page_index / PAGES_PER_SHARD]。 + 2. bit = page_index % PAGES_PER_SHARD。 + 3. 如果 bit 原来是 0: + a. 设置 bitmap bit。 + b. shard.dirty_count++。 + c. 如果 shard.dirty_count 从 0 变为 1,加入 per-cpu dirty_shard_list。 + 4. 返回。 +``` + +复杂度: + +```text +Dirty 标记:O(1) +Checkpoint 扫描:O(number_of_dirty_shards + number_of_dirty_pages) +``` + +### 7.2 A/B Checkpoint Metadata 提交算法 + +```text +CHECKPOINT_METADATA_COMMIT(gen) + 1. active = checkpoint.active_slot。 + 2. inactive = opposite(active)。 + 3. 清理 inactive slot 的临时状态。 + 4. 写 inactive.metadata_header。 + 5. 写 segment desc snapshot。 + 6. 写 DMP snapshot。 + 7. 写 dirty shard summary。 + 8. 写 MAC root。 + 9. 写 generation。 + 10. 计算并写 CRC。 + 11. flush inactive slot。 + 12. 原子切换 checkpoint.active_slot = inactive。 + 13. 写 CHECKPOINT_COMMIT log。 +``` + +恢复时: + +```text +1. 读取 active slot。 +2. 校验 generation 和 CRC。 +3. 如果 active 有效,使用 active。 +4. 如果 active 无效,校验 fallback slot。 +5. 如果 fallback 有效,使用 fallback。 +6. 如果双槽都无效,进入 system rollback。 +``` + +### 7.3 Dirty Page Checkpoint 算法 + +```text +DRR_CHECKPOINT() + 1. checkpoint_generation++。 + 2. 写 CHECKPOINT_BEGIN log。 + 3. inactive_slot = opposite(active_slot)。 + 4. 遍历 per-cpu dirty_shard_list。 + 5. 跳过 dirty_count == 0 的 shard。 + 6. 对每个 dirty shard: + a. 找到关联 segment。 + b. desc.state = CHECKPOINTING。 + c. 冻结 writer。 + d. flush PWC / per-CPU dirty buffer。 + e. 只扫描 shard 内 dirty bit。 + f. 复制 dirty ciphertext page 到 checkpoint region。 + g. 计算 page MAC。 + h. 写 dirty shard summary。 + i. 清 shard bitmap。 + j. shard.dirty_count = 0。 + k. desc.state = ACTIVE。 + 7. 写 segment desc snapshot。 + 8. 写 DMP snapshot。 + 9. CHECKPOINT_METADATA_COMMIT(checkpoint_generation)。 +``` + +提交规则: + +```text +只有存在 CHECKPOINT_COMMIT 且 CRC 正确的 checkpoint 才是稳定点。 +中途崩溃的 checkpoint 直接丢弃。 +A/B metadata 至少保留一个旧稳定点。 +``` + +### 7.4 DRR Emergency Pool 分配算法 + +DRR Recovery 路径不能依赖普通 allocator。 + +```text +DRR_EMERGENCY_ALLOC(size, class) + 1. 从 DRR emergency pool 的 class freelist 取对象。 + 2. 如果 freelist 非空,返回对象。 + 3. 如果 freelist 为空,从 emergency pages 切分。 + 4. 如果 emergency pages 不足: + a. 写 EMERGENCY_POOL_EXHAUSTED。 + b. 进入 system rollback。 +``` + +Emergency Pool 包含: + +```text +DRR private stack +recovery log buffer +checkpoint metadata buffer +crash buffer +metadata slab +emergency pages +``` + +规则: + +```text +普通 allocator 不可使用 Emergency Pool。 +Emergency Pool 不参与普通内存回收。 +DRR OOM 直接视为不可恢复故障。 +``` + +--- + +## 8. Rollback 算法 + +分三级。 + +### 8.1 Page Rollback + +```text +1. 定位最近稳定 checkpoint。 +2. 校验 checkpoint page MAC。 +3. 将旧密文页复制回 cipher data。 +4. 恢复 page MAC。 +5. 清 dirty bit。 +6. 段保持 ACTIVE。 +``` + +### 8.2 Segment Rollback + +```text +1. desc.state = RECOVERING。 +2. 恢复 segment desc snapshot。 +3. 恢复 DMP snapshot。 +4. 恢复 key_epoch。 +5. 恢复 dirty bitmap。 +6. 恢复 mac_table。 +7. 清除对应 PCKC key。 +8. desc.state = ACTIVE。 +``` + +### 8.3 System Rollback + +```text +1. DRR 写 crash reason。 +2. 标记 boot attempt failed。 +3. 触发 reboot。 +4. bootloader 读取 BCB。 +5. 如果失败次数超限,切换 last_good_slot。 +``` + +--- + +## 9. 任务创建算法 + +```text +task_create(entry, priority, flags) + 1. 分配 TCB。 + 2. 生成 tid。 + 3. 生成 process_uuid。 + 4. 创建 utsm_process_context。 + 5. 创建 stack segment。 + 6. 创建 heap segment。 + 7. 可选创建 ipc segment。 + 8. 初始化 capability table。 + 9. cap[0] = stack。 + 10. cap[1] = heap。 + 11. 初始化 CPU context。 + 12. 通过 UTRW_WRITE 写入 task_start_packet。 + 13. hot_segment_hint = stack.segment_slot。 + 14. state = READY。 + 15. enqueue_task(current_cpu)。 +``` + +--- + +## 10. 调度切换算法 + +```text +schedule() + 1. old = current。 + 2. utsm_switch_out(old)。 + 3. next = pick_next_task()。 + 4. utsm_switch_in(next)。 + 5. context_switch(old, next)。 +``` + +`utsm_switch_out`: + +```text +1. 如果 old 有 PWC dirty,登记 flush pending。 +2. 保存 old.hot_segment_hint。 +3. 更新 old.recovery_generation。 +4. 更新 heartbeat。 +``` + +`utsm_switch_in`: + +```text +1. 如果 next.state == RECOVERING,跳过。 +2. PCKC.current_process_uuid = next.crypto.process_uuid。 +3. PCKC.current_crypto_epoch = next.crypto.crypto_epoch。 +4. 预热 next.hot_segment_hint。 +``` + +调度路径禁止: + +```text +扫描段 +扫描 capability table +计算 MAC +写 checkpoint +重加密整段 +``` + +--- + +## 11. 任务异常处理算法 + +```text +UTRW_DETECT_FAULT(task, segment, error) + 1. task.fault_segment_slot = segment.slot。 + 2. task.state = FAULTED。 + 3. 从 runqueue 移除。 + 4. 写 DRR fault event。 + 5. DRR 判断: + a. 刷新 capability。 + b. page rollback。 + c. segment rollback。 + d. kill task。 + e. system rollback。 +``` + +任务 kill: + +```text +task_kill(task) + 1. task.state = ZOMBIE。 + 2. 从 runqueue 移除。 + 3. revoke capability table。 + 4. owned segment key_epoch++。 + 5. desc.state = DESTROYED。 + 6. 加入 deferred free。 + 7. 写 TASK_KILL recovery log。 +``` + +--- + +## 12. 复杂度总结 + +```text +段定位:O(1) +UTRW 读写:O(n / 64) +PCKC 命中:O(1) +PCKC miss:O(KDF + key schedule) +Dirty 标记:O(1) +Checkpoint:O(number_of_dirty_shards + number_of_dirty_pages) +A/B metadata 切换:O(1) +DRR emergency alloc:O(1) +Page rollback:O(1 page) +Segment rollback:O(segment_dirty_pages + metadata) +调度切换额外成本:O(1) +``` diff --git a/RE/UTSM_设计架构.md b/RE/UTSM_设计架构.md new file mode 100644 index 0000000..f9cc187 --- /dev/null +++ b/RE/UTSM_设计架构.md @@ -0,0 +1,485 @@ +# UTSM 设计架构记录 + +本文记录 Deshab 内核当前 UTSM 相关架构定稿。 + +--- + +## 1. 架构目标 + +UTSM 的目标不是传统强安全隔离,而是在极高权限、极高效率的单地址空间 Ring0 内核中提供一种封缄内存模型。 + +核心目标: + +```text +所有普通内存可读,但默认是密文。 +明文读写必须通过 UTRW。 +UUID 作为进程和内存段的调谐因子。 +数据区保持连续,避免 UUID 孔洞。 +DRR 提供 root key、checkpoint、recovery log 和回滚。 +调度器保持 O(1)。 +``` + +--- + +## 2. 总架构模块 + +```text +Deshab Kernel Core + +SAS-R0-PCQ + 单地址空间 Ring0 任务模型 + Per-CPU O(1) 位图调度器 + +DRR + Dedicated Recovery Root + 专用恢复根 + watchdog / root key / checkpoint / recovery log / A-B rollback + +UTSM + UUID-Tuned Sealed Memory + UUID 调谐封缄内存 + +UTRW + UTSM Tagged Reader/Writer + UTSM 带标记读写器 + +DMP + Descriptor Meta Page + 段描述符元页 + +PCKC + Per-CPU Key Cache + 每 CPU 密钥缓存 +``` + +--- + +## 3. UTSM 内存模型 + +每个 UTSM 段由两部分组成: + +```text +DMP + Cipher Data +``` + +布局: + +```text ++-------------------------------+ +| DMP: Descriptor Meta Page | ++-------------------------------+ +| Cipher Data Page 0 | ++-------------------------------+ +| Cipher Data Page 1 | ++-------------------------------+ +| Cipher Data Page 2 | ++-------------------------------+ +| ... | ++-------------------------------+ +``` + +规则: + +```text +DMP 不属于用户数据。 +Cipher Data 只存密文。 +UUID 不插入 Cipher Data。 +数据区保持连续。 +``` + +--- + +## 4. 关键设计替代 + +从原始构想到 UTSM v1 的替代关系: + +```text +十进制 UUID + -> 128-bit 二进制 UUID + +UUID 数字间隔插入内存 + -> 独立 DMP 元数据页 + +UUID 数字位置表 + -> segment_slot + generation + tweak_seed + +十进制凯撒 + -> UUID 调谐的 cache-line 流式加密 + +每次传统 syscall + -> Ring0 fast accessor + PCKC + +写入时避开 UUID + -> 连续密文数据区直接写 +``` + +--- + +## 5. 数据结构架构 + +### 5.1 uuid128_t + +```c +typedef struct { + uint64_t hi; + uint64_t lo; +} uuid128_t; +``` + +### 5.2 utsm_process_context + +```c +struct utsm_process_context { + uuid128_t process_uuid; + uint64_t crypto_epoch; + uint64_t recovery_generation; + uint32_t capability_table_slot; + uint32_t capability_count; + uint32_t flags; +}; +``` + +### 5.3 utsm_segment_desc + +```c +struct utsm_segment_desc { + uuid128_t segment_uuid; + uuid128_t owner_process_uuid; + void *cipher_base; + uint64_t cipher_length; + void *dmp_base; + uint64_t key_epoch; + uint64_t tweak_seed; + void *dirty_bitmap; + void *mac_table; + uint32_t generation; + uint32_t flags; + uint32_t state; + uint32_t last_cpu; + uint64_t writer_seq; + uint64_t checkpoint_generation; +}; +``` + +### 5.4 utsm_dmp + +```c +struct utsm_dmp { + uint64_t magic; + uint32_t version; + uint32_t segment_slot; + uint64_t fast_base; + uint64_t fast_limit; + uint64_t fast_tweak_seed; + uint64_t fast_key_epoch; + uint64_t dirty_bitmap_base; + uint64_t mac_table_base; + uint64_t last_writer_seq; + uint32_t last_cpu; + uint32_t flags; + uint64_t crc; +}; +``` + +### 5.5 utsm_capability + +```c +struct utsm_capability { + uint32_t segment_slot; + uint32_t generation; + uint32_t rights; + uint32_t epoch; + uint64_t uuid_digest; + uint64_t auth_tag; +}; +``` + +--- + +## 6. UTRW 读写器架构 + +UTRW 分三层: + +```text +Level 0: Raw Access + 直接访问 cipher_base,读到密文。 + +Level 1: Fast Path + capability O(1) 定位段。 + PCKC 命中。 + 无全局锁。 + 无 trap。 + +Level 2: Slow Path + key miss。 + epoch mismatch。 + checkpoint/recovery。 + MAC fail。 + poisoned segment。 +``` + +读写基本流程: + +```text +Read: + cap -> segment desc -> PCKC key -> 64B line 解密 -> 返回明文 + +Write: + cap -> segment desc -> PCKC key -> 64B line 加密 -> 写密文 -> dirty bitmap -> recovery log +``` + +--- + +## 7. DRR 协作架构 + +DRR 保存: + +```text +root_key 或 root_key handle +global_crypto_epoch +segment table snapshot +DMP snapshot +dirty shard summary +dirty bitmap +MAC root +recovery log +poisoned segment list +Boot Control Block +DRR Emergency Pool +A/B checkpoint metadata slot +``` + +DRR 负责: + +```text +watchdog 检测 +freeze-all +checkpoint +page rollback +segment rollback +system rollback +A/B 镜像回滚 +``` + +--- + +## 8. Checkpoint / Rollback 架构 + +Checkpoint 类型: + +```text +Light Checkpoint + 控制面快照。 + +Dirty Page Checkpoint + dirty ciphertext page 快照。 + +Boot Checkpoint + 启动状态和 A/B slot 信息。 +``` + +### 8.1 Dirty Shard + +Dirty Bitmap 采用分片结构。 + +```text +Dirty Shard + dirty_count + shard_id + page_base + page_count + last_checkpoint_epoch + bitmap +``` + +checkpoint 时: + +```text +dirty_count == 0 的 shard 直接跳过。 +dirty_count > 0 的 shard 才扫描 dirty bit。 +``` + +这样 checkpoint 成本为: + +```text +O(number_of_dirty_shards + number_of_dirty_pages) +``` + +### 8.2 A/B Checkpoint Metadata + +checkpoint metadata 使用双槽: + +```text +Checkpoint Metadata Slot A +Checkpoint Metadata Slot B +``` + +写入规则: + +```text +写 inactive slot +写 metadata / dirty index / MAC root / CRC +flush +原子切换 active slot +旧 slot 作为 fallback +``` + +恢复规则: + +```text +active slot 有效 -> 使用 active +active slot 无效 -> fallback 另一个 slot +双槽都无效 -> system rollback +``` + +### 8.3 DRR Emergency Pool + +DRR 独立预留紧急内存池。 + +```text +DRR Emergency Pool + DRR private stack + recovery log buffer + checkpoint metadata buffer + crash buffer + metadata slab + emergency pages +``` + +约束: + +```text +普通 allocator 不可使用。 +普通 OOM 不影响 DRR recovery。 +DRR recovery 路径禁止依赖普通堆分配器。 +Emergency Pool 耗尽时进入 system rollback。 +``` + +Rollback 级别: + +```text +Page Rollback + 单页恢复。 + +Segment Rollback + 整段恢复。 + +System Rollback + 重启并由 bootloader 切换 last_good_slot。 +``` + +--- + +## 9. 与 SAS-R0-PCQ 的整合 + +UTSM Task 是: + +```text +Ring0 调度实体 ++ process_uuid ++ crypto context ++ capability table ++ encrypted stack/heap ++ PCKC hot hint ++ DRR recovery generation +``` + +调度切换: + +```text +schedule() + -> utsm_switch_out(old) + -> pick_next_task() + -> utsm_switch_in(next) + -> context_switch(old, next) +``` + +调度路径只允许: + +```text +设置 crypto 指针 +加载 process_uuid +预热 hot_segment_hint +标记 PWC dirty flush +``` + +禁止: + +```text +扫描段 +扫描 capability table +计算 MAC +写 checkpoint +重加密整段 +``` + +--- + +## 10. 启动顺序 + +```text +Bootloader + -> 读取 Boot Control Block + -> 选择 active_slot / last_good_slot + -> 加载 SYSTEM/boot/utsm.elf + -> 传入 memory map 和 DRR region + +DRR Init + -> 初始化 watchdog + -> 建立 root key handle + -> 初始化 recovery log + -> 初始化 checkpoint region + -> 初始化 heartbeat page + -> 初始化 DRR Emergency Pool + -> 初始化 A/B checkpoint metadata slot + +UTSM Init + -> 初始化 global crypto epoch + -> 初始化 segment table + -> 初始化 DMP pool + -> 初始化 PCKC area + -> 初始化 dirty shard pool + -> 初始化 dirty bitmap / MAC table pool + -> 注册 UTRW fast path / slow path + +Scheduler Init + -> 初始化 per-CPU runqueue + -> 初始化 per-CPU heartbeat + -> 初始化每 CPU PCKC + -> 创建 bootstrap task + -> 创建核心服务 task +``` + +--- + +## 11. 当前定稿边界 + +UTSM v1 确定: + +```text +加密粒度:64B cache line +Dirty 粒度:4KB page +Dirty 分片:Dirty Shard +MAC 粒度:4KB page +段定位:O(1) +调度额外成本:O(1) +checkpoint 成本:O(dirty shards + dirty pages) +checkpoint metadata:A/B 双槽,CRC 校验,原子切换 +DRR recovery 内存:Emergency Pool 独立预留 +``` + +暂不追求: + +```text +完整 RAM 原地回滚 +对恶意 Ring0 代码的强安全隔离 +设备外部副作用回滚 +``` + +设备恢复策略: + +```text +内存恢复后,设备 reset/reinit。 +磁盘依赖文件系统 journal。 +网卡 TX 已发包不回滚,RX/TX ring 重建。 +``` diff --git a/RE/驱动模块ABI设计.md b/RE/驱动模块ABI设计.md new file mode 100644 index 0000000..79a6821 --- /dev/null +++ b/RE/驱动模块ABI设计.md @@ -0,0 +1,627 @@ +# Deshab 驱动模块 ABI 设计 + +本文记录 Deshab 内核启动驱动模块系统的 ABI、manifest、ELF loader、加载状态机和 stage0/stage1 boot module 方案。 + +--- + +## 1. 总体定义 + +驱动模块系统命名: + +```text +DKM = Deshab Kernel Module +DSM = Driver Startup Manager +``` + +当前规则: + +```text +驱动目录:SYSTEM/driver +驱动清单:SYSTEM/driver/manifest.json +驱动格式:ELF64 .drv +加载模式:module loading +启动顺序:staged loading +内存模型:UTSM +``` + +--- + +## 2. driver_desc ABI + +每个驱动必须导出三个符号: + +```text +driver_desc +driver_init +driver_exit +``` + +### 2.1 driver_desc + +```c +#define DKM_DRIVER_MAGIC 0x444B4D31u /* "DKM1" */ +#define DKM_ABI_VERSION 1 + +struct dkm_driver_desc { + uint32_t magic; + uint16_t abi_version; + uint16_t desc_size; + + const char *name; + const char *version; + const char *vendor; + + uint32_t driver_class; + uint32_t stage; + uint32_t flags; + uint32_t priority; + + const char *const *depends; + uint32_t depends_count; + + const char *const *provides; + uint32_t provides_count; + + uint64_t min_kernel_abi; + uint64_t feature_bits; + + uint64_t reserved0; + uint64_t reserved1; +}; +``` + +### 2.2 driver_class + +```text +DKM_CLASS_PLATFORM +DKM_CLASS_BUS +DKM_CLASS_INTERRUPT +DKM_CLASS_TIMER +DKM_CLASS_CONSOLE +DKM_CLASS_STORAGE +DKM_CLASS_FS +DKM_CLASS_NET +DKM_CLASS_INPUT +DKM_CLASS_GPU +DKM_CLASS_MISC +``` + +### 2.3 flags + +```text +DKM_F_REQUIRED + 当前 stage 中失败则进入 DRR recovery 或 panic。 + +DKM_F_BOOT_MODULE + 可由 bootloader 预加载。 + +DKM_F_NO_UNLOAD + 不允许卸载。 + +DKM_F_STRONG_RECOVERY + 驱动 data 段使用 UTSM 强恢复策略。 + +DKM_F_DMA_REQUIRED + 驱动需要 DMA API。 + +DKM_F_EARLY_LOG + 驱动 init 期间允许使用 early log。 +``` + +### 2.4 driver_init / driver_exit + +```c +typedef int (*dkm_driver_init_fn)(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle); + +typedef int (*dkm_driver_exit_fn)(struct dkm_driver_handle *handle); +``` + +约定: + +```text +driver_init 返回 0 表示成功。 +driver_init 返回负数表示失败。 +driver_exit 只用于非 NO_UNLOAD 驱动。 +required 驱动 init 失败会触发 DRR recovery 或 panic。 +optional 驱动 init 失败会被标记为 FAILED。 +``` + +--- + +## 3. kernel_api ABI + +驱动不直接依赖任意内核符号,默认通过 `dkm_kernel_api` 调用内核服务。 + +### 3.1 API 总结构 + +```c +#define DKM_KERNEL_API_VERSION 1 + +struct dkm_kernel_api { + uint32_t version; + uint32_t size; + uint64_t feature_bits; + + const struct dkm_log_api *log; + const struct dkm_mem_api *mem; + const struct dkm_utsm_api *utsm; + const struct dkm_irq_api *irq; + const struct dkm_pci_api *pci; + const struct dkm_dma_api *dma; + const struct dkm_vfs_api *vfs; + const struct dkm_net_api *net; + const struct dkm_timer_api *timer; + const struct dkm_drr_api *drr; +}; +``` + +### 3.2 log API + +```c +struct dkm_log_api { + void (*info)(const char *fmt, ...); + void (*warn)(const char *fmt, ...); + void (*error)(const char *fmt, ...); + void (*panic)(const char *fmt, ...); +}; +``` + +### 3.3 memory API + +```c +struct dkm_mem_api { + void *(*alloc)(uint64_t size, uint32_t flags); + void (*free)(void *ptr); + + void *(*alloc_pages)(uint32_t order, uint32_t flags); + void (*free_pages)(void *ptr, uint32_t order); + + void *(*mmio_map)(uint64_t phys, uint64_t size, uint32_t flags); + void (*mmio_unmap)(void *virt, uint64_t size); +}; +``` + +### 3.4 UTSM API + +```c +struct dkm_utsm_api { + int (*create_segment)(uint64_t size, uint32_t flags, struct utsm_capability *out); + int (*read)(struct utsm_capability cap, uint64_t off, void *dst, uint64_t len); + int (*write)(struct utsm_capability cap, uint64_t off, const void *src, uint64_t len); + int (*pin)(struct utsm_capability cap, uint64_t off, uint64_t len, void **window); + int (*unpin)(void *window, uint32_t flags); +}; +``` + +### 3.5 IRQ API + +```c +struct dkm_irq_api { + int (*register_irq)(uint32_t vector, int (*handler)(void *ctx), void *ctx, uint32_t flags); + int (*unregister_irq)(uint32_t vector); + void (*ack)(uint32_t vector); +}; +``` + +### 3.6 PCI API + +```c +struct dkm_pci_api { + int (*scan)(int (*cb)(const struct pci_device *dev, void *ctx), void *ctx); + int (*enable_device)(const struct pci_device *dev); + uint64_t (*bar_phys)(const struct pci_device *dev, uint32_t bar); + uint64_t (*bar_size)(const struct pci_device *dev, uint32_t bar); +}; +``` + +### 3.7 DMA API + +```c +struct dkm_dma_api { + int (*alloc)(uint64_t size, uint32_t flags, struct dkm_dma_buffer *out); + void (*free)(struct dkm_dma_buffer *buf); + int (*sync_for_device)(struct dkm_dma_buffer *buf); + int (*sync_for_cpu)(struct dkm_dma_buffer *buf); +}; +``` + +### 3.8 VFS / NET / TIMER / DRR API + +第一版只定义边界: + +```text +vfs: register_fs / mount / open / read / write / close +net: register_netdev / submit_tx / poll_rx +timer: sleep_ns / call_later / monotonic_ns +drr: register_recovery_ops / report_fault / mark_critical +``` + +详细结构后续随对应子系统实现细化。 + +--- + +## 4. ELF Loader 支持的 relocation + +第一版仅支持 x86_64 ELF64 小型内核模型。 + +### 4.1 必须支持 + +```text +R_X86_64_64 + 绝对 64-bit 地址重定位。 + +R_X86_64_RELATIVE + base + addend。 + +R_X86_64_GLOB_DAT + GOT 符号地址。 + +R_X86_64_JUMP_SLOT + PLT 函数地址。 +``` + +### 4.2 建议支持 + +```text +R_X86_64_PC32 + 32-bit PC-relative 调用。 + +R_X86_64_PLT32 + PLT-relative 调用。 + +R_X86_64_32 / R_X86_64_32S + 仅允许确认地址范围安全时使用。 +``` + +### 4.3 第一版禁止 + +```text +TLS relocation +IFUNC +lazy binding +外部动态库依赖 +用户态 libc 依赖 +异常展开表强依赖 +``` + +### 4.4 加载流程 + +```text +DKM_ELF_LOAD(path_or_boot_module) + 1. 读取 ELF header。 + 2. 校验 ELFCLASS64 / EM_X86_64 / endian / ABI。 + 3. 读取 program headers 或 section headers。 + 4. 计算模块内存大小和对齐。 + 5. 通过 UTSM 创建 code/data 段。 + 6. 加载 PT_LOAD 或 SHF_ALLOC section。 + 7. 清零 .bss。 + 8. 解析 symbol table。 + 9. 解析 relocation section。 + 10. 将内核导出符号或 kernel_api 符号填入。 + 11. 执行 relocation。 + 12. 查找 driver_desc / driver_init / driver_exit。 + 13. 设置 code 段 READ|EXEC|SEALED。 + 14. 设置 data 段 READ|WRITE|STRONG_RECOVERY。 + 15. 返回 module handle。 +``` + +--- + +## 5. manifest.json 格式 + +当前使用 JSON,不再使用 ini 格式的 manifest.drv。 + +路径: + +```text +SYSTEM/driver/manifest.json +``` + +### 5.1 顶层字段 + +```json +{ + "schema": "deshab.driver.manifest.v1", + "root": "SYSTEM", + "driverRoot": "SYSTEM/driver", + "format": "elf64-drv", + "abi": {}, + "policy": {}, + "stages": [] +} +``` + +### 5.2 abi 字段 + +```json +{ + "name": "DKM", + "version": 1, + "entry": "driver_init", + "exit": "driver_exit", + "descriptor": "driver_desc" +} +``` + +### 5.3 policy 字段 + +```json +{ + "loadModel": "module", + "loadOrder": "staged", + "requiredFailure": "drr_recovery_or_panic", + "optionalFailure": "mark_failed_and_continue", + "memoryModel": "UTSM", + "codeSegment": "READ|EXEC|SEALED", + "dataSegment": "READ|WRITE|STRONG_RECOVERY", + "dmaSegment": "DMA|PINNED" +} +``` + +### 5.4 stage 字段 + +```json +{ + "id": 0, + "name": "platform", + "required": true, + "description": "平台发现、中断、总线、基础计时。", + "drivers": [] +} +``` + +### 5.5 driver 字段 + +```json +{ + "name": "pci", + "path": "driver/bus/pci.drv", + "class": "bus", + "required": true, + "depends": ["irq"], + "provides": ["pci"] +} +``` + +### 5.6 路径规则 + +```text +manifest 中的 path 相对 SYSTEM 根目录。 +SYSTEM/driver/manifest.json 内的 driver/bus/pci.drv 指向 SYSTEM/driver/bus/pci.drv。 +stage0/stage1 驱动允许来自 boot module,不要求文件系统已挂载。 +stage2/stage3 驱动默认从 SYSTEM/driver 加载。 +``` + +--- + +## 6. 驱动加载状态机 + +```text +DISCOVERED + -> QUEUED + -> DEP_WAIT + -> LOADING + -> ELF_CHECKED + -> MEMORY_ALLOCATED + -> RELOCATED + -> ABI_CHECKED + -> INITING + -> ACTIVE +``` + +失败状态: + +```text +FAILED_ELF +FAILED_RELOCATION +FAILED_DEPENDENCY +FAILED_ABI +FAILED_INIT +FAILED_REQUIRED +``` + +卸载状态: + +```text +ACTIVE + -> QUIESCING + -> EXITING + -> UNLOADING + -> UNLOADED +``` + +恢复状态: + +```text +ACTIVE + -> SUSPENDED_BY_DRR + -> RECOVERING + -> REINITING + -> ACTIVE +``` + +### 6.1 加载策略 + +```text +1. DSM 读取 manifest.json。 +2. 按 stage 顺序加载。 +3. 每个 stage 内按 priority 排序。 +4. 检查 depends 是否已由 provides 满足。 +5. 未满足依赖的驱动进入 DEP_WAIT。 +6. 每轮加载成功后更新 provides set。 +7. 一轮后仍 DEP_WAIT 的 required 驱动进入 FAILED_REQUIRED。 +8. optional 驱动失败只记录状态。 +``` + +### 6.2 required 失败策略 + +```text +stage0/stage1 required 失败: + -> DRR report_fault + -> 尝试 boot fallback + -> 不可恢复则 panic / system rollback + +stage2 required 失败: + -> DRR recovery + -> fallback 到 bootfs 或只读模式 + +stage3 optional 失败: + -> mark FAILED + -> continue boot +``` + +--- + +## 7. stage0/stage1 boot module 方案 + +问题: + +```text +stage0/stage1 负责 timer、irq、pci、bootfs、block 等早期能力。 +此时 VFS 和普通文件系统可能还没准备好。 +所以不能完全依赖 SYSTEM/driver 的普通文件遍历。 +``` + +### 7.1 Bootloader 预加载 + +bootloader 负责把关键文件作为 boot module 交给内核: + +```text +SYSTEM/driver/manifest.json +SYSTEM/driver/platform/timer.drv +SYSTEM/driver/platform/apic.drv +SYSTEM/driver/bus/pci.drv +SYSTEM/driver/fs/bootfs.drv +``` + +可选: + +```text +SYSTEM/driver/console/console_fb.drv +SYSTEM/driver/block/ahci.drv +SYSTEM/driver/block/nvme.drv +``` + +### 7.2 Boot Module 表 + +内核早期接收 boot module 表: + +```c +struct dkm_boot_module { + const char *path; + void *base; + uint64_t size; + uint32_t type; + uint32_t flags; + uint64_t checksum; +}; + +struct dkm_boot_module_table { + uint32_t count; + struct dkm_boot_module modules[]; +}; +``` + +### 7.3 查找规则 + +```text +DSM_LOAD_DRIVER(path) + 1. 如果 path 在 boot module table 中,直接从内存加载。 + 2. 否则如果 VFS 已可用,从 SYSTEM/path 加载。 + 3. 否则返回 BOOT_MODULE_NOT_FOUND。 +``` + +### 7.4 stage0/stage1 限制 + +```text +1. 不依赖普通 VFS。 +2. 不依赖普通 heap。 +3. 优先使用 early allocator 或 DRR emergency-safe allocator。 +4. 日志使用 early log。 +5. 不启动复杂后台任务。 +6. 只注册最小服务能力。 +``` + +### 7.5 stage 切换点 + +```text +stage0 完成: + timer / irq / pci 基础能力可用。 + +stage1 完成: + bootfs 或 block + fs 基础能力可用。 + +stage2 开始: + VFS 可用,可以从 SYSTEM/driver 正常读取更多驱动。 + +stage3 开始: + 普通调度、网络、输入、图形等可异步加载。 +``` + +--- + +## 8. 与 UTSM / DRR 的关系 + +### 8.1 UTSM 内存策略 + +```text +驱动 text:READ | EXEC | SEALED +驱动 rodata:READ | SEALED +驱动 data:READ | WRITE | STRONG_RECOVERY +驱动 bss:READ | WRITE | STRONG_RECOVERY +驱动 DMA:DMA | PINNED,必要时 CIPHERTEXT_DMA 或 NO_ENCRYPT +``` + +### 8.2 DRR 记录项 + +DRR 为每个驱动记录: + +```text +driver name +stage +state +module memory range +provided services +recovery ops +init result +last fault +``` + +### 8.3 驱动 recovery ops + +驱动可注册: + +```c +struct dkm_driver_recovery_ops { + int (*quiesce)(void *ctx); + int (*reset)(void *ctx); + int (*reinit)(void *ctx); + int (*dump_state)(void *ctx, void *buf, uint64_t len); +}; +``` + +DRR recovery 顺序: + +```text +quiesce -> UTSM rollback -> device reset -> driver reinit -> resume +``` + +--- + +## 9. 第一版定稿 + +第一版驱动系统确定: + +```text +1. SYSTEM 是 IMG 内系统根目录。 +2. 驱动清单为 SYSTEM/driver/manifest.json。 +3. 驱动文件为 ELF64 .drv。 +4. 驱动必须导出 driver_desc / driver_init / driver_exit。 +5. 驱动通过 dkm_kernel_api 调用内核服务。 +6. ELF loader 第一版支持常用 x86_64 relocation。 +7. 驱动按 stage 分阶段加载。 +8. stage0/stage1 支持 bootloader 预加载 boot module。 +9. stage2/stage3 从 SYSTEM/driver 加载。 +10. 驱动内存纳入 UTSM,驱动恢复纳入 DRR。 +``` diff --git a/SYSTEM/EFI/BOOT/BOOTX64.EFI b/SYSTEM/EFI/BOOT/BOOTX64.EFI new file mode 100644 index 0000000..adec196 Binary files /dev/null and b/SYSTEM/EFI/BOOT/BOOTX64.EFI differ diff --git a/SYSTEM/boot/limine.conf b/SYSTEM/boot/limine.conf new file mode 100644 index 0000000..6f6ebf5 --- /dev/null +++ b/SYSTEM/boot/limine.conf @@ -0,0 +1,42 @@ +timeout: 0 +serial: yes +verbose: yes + +/UTSM boot + protocol: limine + kernel_path: boot():/boot/utsm.elf + module_path: boot():/driver/manifest.json + module_cmdline: dkm:manifest + + module_path: boot():/driver/platform/timer.drv + module_cmdline: dkm:timer + module_path: boot():/driver/platform/apic.drv + module_cmdline: dkm:apic + module_path: boot():/driver/platform/acpi.drv + module_cmdline: dkm:acpi + module_path: boot():/driver/bus/pci.drv + module_cmdline: dkm:pci + module_path: boot():/driver/console/console_fb.drv + module_cmdline: dkm:console_fb + module_path: boot():/driver/block/ahci.drv + module_cmdline: dkm:ahci + module_path: boot():/driver/block/nvme.drv + module_cmdline: dkm:nvme + module_path: boot():/driver/fs/bootfs.drv + module_cmdline: dkm:bootfs + module_path: boot():/driver/fs/vfs.drv + module_cmdline: dkm:vfs + module_path: boot():/driver/fs/fat32.drv + module_cmdline: dkm:fat32 + module_path: boot():/driver/fs/devfs.drv + module_cmdline: dkm:devfs + module_path: boot():/driver/net/e1000.drv + module_cmdline: dkm:e1000 + module_path: boot():/driver/net/virtio_net.drv + module_cmdline: dkm:virtio_net + module_path: boot():/driver/input/ps2kbd.drv + module_cmdline: dkm:ps2kbd + module_path: boot():/system/deshab64/deshab.elf + module_cmdline: dsk:main + module_path: boot():/driver/test.fat32 + module_cmdline: dkm:test_fat32 diff --git a/SYSTEM/boot/utsm.elf b/SYSTEM/boot/utsm.elf new file mode 100644 index 0000000..d8e30e3 Binary files /dev/null and b/SYSTEM/boot/utsm.elf differ diff --git a/SYSTEM/driver/block/ahci.drv b/SYSTEM/driver/block/ahci.drv new file mode 100644 index 0000000..4db0c32 Binary files /dev/null and b/SYSTEM/driver/block/ahci.drv differ diff --git a/SYSTEM/driver/block/nvme.drv b/SYSTEM/driver/block/nvme.drv new file mode 100644 index 0000000..e7bebae Binary files /dev/null and b/SYSTEM/driver/block/nvme.drv differ diff --git a/SYSTEM/driver/bus/pci.drv b/SYSTEM/driver/bus/pci.drv new file mode 100644 index 0000000..42487c1 Binary files /dev/null and b/SYSTEM/driver/bus/pci.drv differ diff --git a/SYSTEM/driver/console/console_fb.drv b/SYSTEM/driver/console/console_fb.drv new file mode 100644 index 0000000..51cf902 Binary files /dev/null and b/SYSTEM/driver/console/console_fb.drv differ diff --git a/SYSTEM/driver/fs/bootfs.drv b/SYSTEM/driver/fs/bootfs.drv new file mode 100644 index 0000000..ff1190a Binary files /dev/null and b/SYSTEM/driver/fs/bootfs.drv differ diff --git a/SYSTEM/driver/fs/devfs.drv b/SYSTEM/driver/fs/devfs.drv new file mode 100644 index 0000000..aab7a1e Binary files /dev/null and b/SYSTEM/driver/fs/devfs.drv differ diff --git a/SYSTEM/driver/fs/fat32.drv b/SYSTEM/driver/fs/fat32.drv new file mode 100644 index 0000000..be17425 Binary files /dev/null and b/SYSTEM/driver/fs/fat32.drv differ diff --git a/SYSTEM/driver/fs/vfs.drv b/SYSTEM/driver/fs/vfs.drv new file mode 100644 index 0000000..b68aafb Binary files /dev/null and b/SYSTEM/driver/fs/vfs.drv differ diff --git a/SYSTEM/driver/input/ps2kbd.drv b/SYSTEM/driver/input/ps2kbd.drv new file mode 100644 index 0000000..9040f4c Binary files /dev/null and b/SYSTEM/driver/input/ps2kbd.drv differ diff --git a/SYSTEM/driver/manifest.json b/SYSTEM/driver/manifest.json new file mode 100644 index 0000000..d65002f --- /dev/null +++ b/SYSTEM/driver/manifest.json @@ -0,0 +1,169 @@ +{ + "schema": "deshab.driver.manifest.v1", + "root": "SYSTEM", + "driverRoot": "SYSTEM/driver", + "format": "elf64-drv", + "abi": { + "name": "DKM", + "version": 1, + "entry": "driver_init", + "exit": "driver_exit", + "descriptor": "driver_desc" + }, + "policy": { + "loadModel": "module", + "loadOrder": "staged", + "requiredFailure": "drr_recovery_or_panic", + "optionalFailure": "mark_failed_and_continue", + "memoryModel": "UTSM", + "codeSegment": "READ|EXEC|SEALED", + "dataSegment": "READ|WRITE|STRONG_RECOVERY", + "dmaSegment": "DMA|PINNED" + }, + "stages": [ + { + "id": 0, + "name": "platform", + "required": true, + "description": "平台发现、中断、总线、基础计时。", + "drivers": [ + { + "name": "timer", + "path": "driver/platform/timer.drv", + "class": "timer", + "required": true, + "depends": [], + "provides": ["timer"] + }, + { + "name": "apic", + "path": "driver/platform/apic.drv", + "class": "interrupt", + "required": true, + "depends": ["timer"], + "provides": ["irq", "apic"] + }, + { + "name": "acpi", + "path": "driver/platform/acpi.drv", + "class": "platform", + "required": false, + "depends": [], + "provides": ["acpi"] + }, + { + "name": "pci", + "path": "driver/bus/pci.drv", + "class": "bus", + "required": true, + "depends": ["irq"], + "provides": ["pci"] + } + ] + }, + { + "id": 1, + "name": "boot", + "required": true, + "description": "启动关键驱动:控制台、块设备、启动文件系统。", + "drivers": [ + { + "name": "console_fb", + "path": "driver/console/console_fb.drv", + "class": "console", + "required": false, + "depends": [], + "provides": ["console"] + }, + { + "name": "ahci", + "path": "driver/block/ahci.drv", + "class": "storage", + "required": false, + "depends": ["pci", "irq"], + "provides": ["block"] + }, + { + "name": "nvme", + "path": "driver/block/nvme.drv", + "class": "storage", + "required": false, + "depends": ["pci", "irq"], + "provides": ["block"] + }, + { + "name": "bootfs", + "path": "driver/fs/bootfs.drv", + "class": "fs", + "required": true, + "depends": [], + "provides": ["bootfs"] + } + ] + }, + { + "id": 2, + "name": "filesystem", + "required": true, + "description": "VFS 和可挂载文件系统。", + "drivers": [ + { + "name": "vfs", + "path": "driver/fs/vfs.drv", + "class": "fs-core", + "required": true, + "depends": ["bootfs"], + "provides": ["vfs"] + }, + { + "name": "fat32", + "path": "driver/fs/fat32.drv", + "class": "fs", + "required": false, + "depends": ["vfs", "block"], + "provides": ["fat32"] + }, + { + "name": "devfs", + "path": "driver/fs/devfs.drv", + "class": "fs", + "required": false, + "depends": ["vfs"], + "provides": ["devfs"] + } + ] + }, + { + "id": 3, + "name": "optional", + "required": false, + "description": "网络、输入、图形等非启动关键驱动。", + "drivers": [ + { + "name": "e1000", + "path": "driver/net/e1000.drv", + "class": "net", + "required": false, + "depends": ["pci", "irq"], + "provides": ["netdev"] + }, + { + "name": "virtio_net", + "path": "driver/net/virtio_net.drv", + "class": "net", + "required": false, + "depends": ["pci", "irq"], + "provides": ["netdev"] + }, + { + "name": "ps2kbd", + "path": "driver/input/ps2kbd.drv", + "class": "input", + "required": false, + "depends": ["irq"], + "provides": ["keyboard"] + } + ] + } + ] +} diff --git a/SYSTEM/driver/net/e1000.drv b/SYSTEM/driver/net/e1000.drv new file mode 100644 index 0000000..53162fe Binary files /dev/null and b/SYSTEM/driver/net/e1000.drv differ diff --git a/SYSTEM/driver/net/virtio_net.drv b/SYSTEM/driver/net/virtio_net.drv new file mode 100644 index 0000000..dd4cf4a Binary files /dev/null and b/SYSTEM/driver/net/virtio_net.drv differ diff --git a/SYSTEM/driver/platform/acpi.drv b/SYSTEM/driver/platform/acpi.drv new file mode 100644 index 0000000..784df1e Binary files /dev/null and b/SYSTEM/driver/platform/acpi.drv differ diff --git a/SYSTEM/driver/platform/apic.drv b/SYSTEM/driver/platform/apic.drv new file mode 100644 index 0000000..961620e Binary files /dev/null and b/SYSTEM/driver/platform/apic.drv differ diff --git a/SYSTEM/driver/platform/timer.drv b/SYSTEM/driver/platform/timer.drv new file mode 100644 index 0000000..26571d1 Binary files /dev/null and b/SYSTEM/driver/platform/timer.drv differ diff --git a/SYSTEM/driver/test.fat32 b/SYSTEM/driver/test.fat32 new file mode 100644 index 0000000..58640ab Binary files /dev/null and b/SYSTEM/driver/test.fat32 differ diff --git a/SYSTEM/system/deshab64/deshab.elf b/SYSTEM/system/deshab64/deshab.elf new file mode 100644 index 0000000..124c5e8 Binary files /dev/null and b/SYSTEM/system/deshab64/deshab.elf differ diff --git a/SYSTEM/system/deshab64/mouse/mouseInit.elf b/SYSTEM/system/deshab64/mouse/mouseInit.elf new file mode 100644 index 0000000..145385a Binary files /dev/null and b/SYSTEM/system/deshab64/mouse/mouseInit.elf differ diff --git a/SYSTEM/system/deshab64/network/conf/conf.conf b/SYSTEM/system/deshab64/network/conf/conf.conf new file mode 100644 index 0000000..eeff9b9 --- /dev/null +++ b/SYSTEM/system/deshab64/network/conf/conf.conf @@ -0,0 +1,6 @@ +network.mode=dhcp +network.device=auto +network.name= +network.password= +network.ip=dhcp +network.dns=auto diff --git a/SYSTEM/system/deshab64/network/netman.elf b/SYSTEM/system/deshab64/network/netman.elf new file mode 100644 index 0000000..775219f Binary files /dev/null and b/SYSTEM/system/deshab64/network/netman.elf differ diff --git a/SYSTEM/system/font/simhei.ttf b/SYSTEM/system/font/simhei.ttf new file mode 100644 index 0000000..1f1bdf8 Binary files /dev/null and b/SYSTEM/system/font/simhei.ttf differ diff --git a/SYSTEM/system/font/simhei_16.dbf b/SYSTEM/system/font/simhei_16.dbf new file mode 100644 index 0000000..90a4e16 Binary files /dev/null and b/SYSTEM/system/font/simhei_16.dbf differ diff --git a/SYSTEM/system/font/simhei_24.dbf b/SYSTEM/system/font/simhei_24.dbf new file mode 100644 index 0000000..f90a45e Binary files /dev/null and b/SYSTEM/system/font/simhei_24.dbf differ diff --git a/SYSTEM/system/font/simhei_32.dbf b/SYSTEM/system/font/simhei_32.dbf new file mode 100644 index 0000000..7108bd7 Binary files /dev/null and b/SYSTEM/system/font/simhei_32.dbf differ diff --git a/SYSTEM/system/user/deshab/user.conf b/SYSTEM/system/user/deshab/user.conf new file mode 100644 index 0000000..623296b --- /dev/null +++ b/SYSTEM/system/user/deshab/user.conf @@ -0,0 +1,7 @@ +# Deshab user configuration placeholder +# Runtime write is pending FAT32 write support. +version=1 +computerName= +username= +passwordSha256= +configEncrypted=0 diff --git a/SYSTEM/system/user/use/FirstInit.elf b/SYSTEM/system/user/use/FirstInit.elf new file mode 100644 index 0000000..ba2a0cf Binary files /dev/null and b/SYSTEM/system/user/use/FirstInit.elf differ diff --git a/SYSTEM/system/user/use/firstInit.txt b/SYSTEM/system/user/use/firstInit.txt new file mode 100644 index 0000000..1874828 --- /dev/null +++ b/SYSTEM/system/user/use/firstInit.txt @@ -0,0 +1 @@ +0 diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..1183241 --- /dev/null +++ b/build.bat @@ -0,0 +1,2 @@ +@echo off +powershell -ExecutionPolicy Bypass -File "%~dp0build.ps1" diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..d8082f8 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,580 @@ +$ErrorActionPreference = 'Stop' + +$Root = Split-Path -Parent $MyInvocation.MyCommand.Path +$UtsmDir = Join-Path $Root 'CODE\UTSM' +$DskDir = Join-Path $Root 'CODE\dsk' +$SystemDir = Join-Path $Root 'SYSTEM' +$IsoDir = Join-Path $Root 'ISO' +$Output = Join-Path $SystemDir 'boot\utsm.elf' +$DskOutput = Join-Path $SystemDir 'system\deshab64\deshab.elf' +$ImagePath = Join-Path $IsoDir 'deshab.img' +$BuildTmp = Join-Path $Root '.build_tmp' + +function Find-Tool($Name, $ExtraNames = @()) { + $names = @($Name) + $ExtraNames + foreach ($n in $names) { + $cmd = Get-Command $n -ErrorAction SilentlyContinue + if ($cmd) { return $cmd.Source } + } + + $dirs = @( + 'C:\Program Files\LLVM\bin', + 'C:\Program Files (x86)\LLVM\bin', + 'C:\LLVM\bin', + 'D:\LLVM\bin', + 'D:\Program Files\LLVM\bin', + 'D:\Code\LLVM\bin', + 'C:\msys64\clang64\bin', + 'C:\msys64\mingw64\bin', + 'C:\mingw64\bin' + ) + + foreach ($dir in $dirs) { + foreach ($n in $names) { + $candidate = Join-Path $dir $n + if (Test-Path $candidate) { return $candidate } + if (-not $n.EndsWith('.exe')) { + $candidateExe = Join-Path $dir "$n.exe" + if (Test-Path $candidateExe) { return $candidateExe } + } + } + } + + return $null +} + +function Build-DkmDriver([string]$SourcePath, [string]$OutputPath) { + $obj = Join-Path $BuildTmp (([System.IO.Path]::GetFileNameWithoutExtension($SourcePath)) + '.o') + $outDir = Split-Path -Parent $OutputPath + if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Force -Path $outDir | Out-Null } + & $clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -O2 -c $SourcePath -o $obj + if ($LASTEXITCODE -ne 0) { throw "failed to compile DKM driver: $SourcePath" } + & $lld -r -o $OutputPath $obj + if ($LASTEXITCODE -ne 0) { throw "failed to link DKM driver: $OutputPath" } + Write-Host "[build] DKM driver Output: $OutputPath" +} + +function Set-Le16([byte[]]$Buffer, [int]$Offset, [int]$Value) { + $Buffer[$Offset] = [byte]($Value -band 0xff) + $Buffer[$Offset + 1] = [byte](($Value -shr 8) -band 0xff) +} + +function Set-Le32([byte[]]$Buffer, [int]$Offset, [UInt64]$Value) { + $Buffer[$Offset] = [byte]($Value -band 0xff) + $Buffer[$Offset + 1] = [byte](($Value -shr 8) -band 0xff) + $Buffer[$Offset + 2] = [byte](($Value -shr 16) -band 0xff) + $Buffer[$Offset + 3] = [byte](($Value -shr 24) -band 0xff) +} + +function Set-Le64([byte[]]$Buffer, [int]$Offset, [UInt64]$Value) { + for ($i = 0; $i -lt 8; $i++) { + $Buffer[$Offset + $i] = [byte](($Value -shr ($i * 8)) -band 0xff) + } +} + +function Write-At($Stream, [Int64]$Offset, [byte[]]$Data) { + [void]$Stream.Seek($Offset, [System.IO.SeekOrigin]::Begin) + $Stream.Write($Data, 0, $Data.Length) +} + +function Get-Crc32([byte[]]$Data) { + $crc = [uint32]4294967295 + foreach ($b in $Data) { + $crc = $crc -bxor [uint32]$b + for ($i = 0; $i -lt 8; $i++) { + if (($crc -band 1) -ne 0) { + $crc = [uint32](($crc -shr 1) -bxor 0xedb88320) + } else { + $crc = [uint32]($crc -shr 1) + } + } + } + return [uint32]($crc -bxor [uint32]4294967295) +} + +function Test-ShortFatName([string]$Name) { + if ($Name -match '[a-z]') { return $false } + $parts = $Name.Split('.') + if ($parts.Count -gt 2) { return $false } + if ($parts[0].Length -lt 1 -or $parts[0].Length -gt 8) { return $false } + if ($parts.Count -eq 2 -and $parts[1].Length -gt 3) { return $false } + return $Name -match '^[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+(\.[A-Z0-9_\$%''\-@~`!\(\)\{\}\^#&]+)?$' +} + +function New-ShortFatName([string]$LongName, $Used) { + $upper = $LongName.ToUpperInvariant() + if ((Test-ShortFatName $upper) -and -not $Used.ContainsKey($upper)) { + $Used[$upper] = $true + $parts = $upper.Split('.') + $base = $parts[0].PadRight(8, ' ') + $ext = '' + if ($parts.Count -eq 2) { $ext = $parts[1] } + return ($base + $ext.PadRight(3, ' ')) + } + + $dot = $upper.LastIndexOf('.') + if ($dot -ge 0) { + $baseRaw = $upper.Substring(0, $dot) + $extRaw = $upper.Substring($dot + 1) + } else { + $baseRaw = $upper + $extRaw = '' + } + + $baseClean = ($baseRaw -replace '[^A-Z0-9]', '') + $extClean = ($extRaw -replace '[^A-Z0-9]', '') + if ($baseClean.Length -eq 0) { $baseClean = 'FILE' } + if ($extClean.Length -gt 3) { $extClean = $extClean.Substring(0, 3) } + + for ($i = 1; $i -lt 1000; $i++) { + $suffix = "~$i" + $take = [Math]::Min(8 - $suffix.Length, $baseClean.Length) + $candidateBase = $baseClean.Substring(0, $take) + $suffix + $display = $candidateBase + if ($extClean.Length -gt 0) { $display = "$candidateBase.$extClean" } + if (-not $Used.ContainsKey($display)) { + $Used[$display] = $true + return ($candidateBase.PadRight(8, ' ') + $extClean.PadRight(3, ' ')) + } + } + + throw "Unable to generate FAT short name for $LongName" +} + +function Get-ShortNameBytes([string]$ShortName) { + return [System.Text.Encoding]::ASCII.GetBytes($ShortName) +} + +function Get-LfnChecksum([byte[]]$ShortBytes) { + $sum = 0 + foreach ($b in $ShortBytes) { + $sum = (((($sum -band 1) * 0x80) + ($sum -shr 1) + $b) -band 0xff) + } + return [byte]$sum +} + +function New-LfnEntry([string]$Chunk, [int]$Order, [byte]$Checksum) { + $entry = [byte[]]::new(32) + for ($i = 0; $i -lt 32; $i++) { $entry[$i] = 0xff } + $entry[0] = [byte]$Order + $entry[11] = 0x0f + $entry[12] = 0x00 + $entry[13] = $Checksum + $entry[26] = 0x00 + $entry[27] = 0x00 + + $positions = @(1,3,5,7,9,14,16,18,20,22,24,28,30) + $chars = $Chunk.ToCharArray() + for ($i = 0; $i -lt $positions.Count; $i++) { + $pos = $positions[$i] + if ($i -lt $chars.Length) { + $code = [int][char]$chars[$i] + $entry[$pos] = [byte]($code -band 0xff) + $entry[$pos + 1] = [byte](($code -shr 8) -band 0xff) + } elseif ($i -eq $chars.Length) { + $entry[$pos] = 0x00 + $entry[$pos + 1] = 0x00 + } + } + return $entry +} + +function New-ShortEntry([string]$ShortName, [byte]$Attr, [int]$Cluster, [uint32]$Size) { + $entry = [byte[]]::new(32) + $nameBytes = Get-ShortNameBytes $ShortName + [Array]::Copy($nameBytes, 0, $entry, 0, 11) + $entry[11] = $Attr + Set-Le16 $entry 20 (($Cluster -shr 16) -band 0xffff) + Set-Le16 $entry 26 ($Cluster -band 0xffff) + Set-Le32 $entry 28 $Size + return $entry +} + +function Add-DirectoryEntry($Entries, [string]$Name, [byte]$Attr, [int]$Cluster, [uint32]$Size, $UsedNames) { + $shortName = New-ShortFatName $Name $UsedNames + $shortBytes = Get-ShortNameBytes $shortName + $upper = $Name.ToUpperInvariant() + $needsLfn = -not (Test-ShortFatName $upper) -or (($shortName.Trim() -replace ' +', '') -ne ($upper -replace '\.', '')) + + if ($needsLfn) { + $checksum = Get-LfnChecksum $shortBytes + $chunks = @() + for ($i = 0; $i -lt $Name.Length; $i += 13) { + $len = [Math]::Min(13, $Name.Length - $i) + $chunks += $Name.Substring($i, $len) + } + for ($i = $chunks.Count; $i -ge 1; $i--) { + $order = $i + if ($i -eq $chunks.Count) { $order = $order -bor 0x40 } + [void]$Entries.Add((New-LfnEntry $chunks[$i - 1] $order $checksum)) + } + } + + [void]$Entries.Add((New-ShortEntry $shortName $Attr $Cluster $Size)) +} + +function Get-GuidBytes([string]$GuidString) { + return ([Guid]$GuidString).ToByteArray() +} + +function New-GptHeader([UInt64]$CurrentLba, [UInt64]$BackupLba, [UInt64]$FirstUsable, [UInt64]$LastUsable, [byte[]]$DiskGuid, [UInt64]$EntriesLba, [uint32]$EntryCount, [uint32]$EntrySize, [uint32]$EntriesCrc) { + $header = [byte[]]::new(512) + [Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('EFI PART'), 0, $header, 0, 8) + Set-Le32 $header 8 0x00010000 + Set-Le32 $header 12 92 + Set-Le64 $header 24 $CurrentLba + Set-Le64 $header 32 $BackupLba + Set-Le64 $header 40 $FirstUsable + Set-Le64 $header 48 $LastUsable + [Array]::Copy($DiskGuid, 0, $header, 56, 16) + Set-Le64 $header 72 $EntriesLba + Set-Le32 $header 80 $EntryCount + Set-Le32 $header 84 $EntrySize + Set-Le32 $header 88 $EntriesCrc + + $crcData = [byte[]]::new(92) + [Array]::Copy($header, 0, $crcData, 0, 92) + Set-Le32 $crcData 16 0 + $crc = Get-Crc32 $crcData + Set-Le32 $header 16 $crc + return $header +} + +function New-GptFat32Image([string]$SourceDir, [string]$ImagePath, [int]$SizeMB = 128) { + $bytesPerSector = 512 + $totalSectors = [UInt64](($SizeMB * 1024 * 1024) / $bytesPerSector) + $lastLba = $totalSectors - 1 + $entryCount = [uint32]128 + $entrySize = [uint32]128 + $entrySectors = [UInt64](($entryCount * $entrySize) / $bytesPerSector) + $firstUsable = [UInt64](2 + $entrySectors) + $lastUsable = [UInt64]($lastLba - $entrySectors - 1) + $partStart = [UInt64]2048 + $partEnd = $lastUsable + $partSectors = [UInt64]($partEnd - $partStart + 1) + $partitionOffset = [Int64]($partStart * $bytesPerSector) + + $sectorsPerCluster = 1 + $reservedSectors = 32 + $numFats = 2 + $fatSectors = 1 + do { + $dataSectors = [Int64]$partSectors - $reservedSectors - ($numFats * $fatSectors) + $clusterCount = [int]($dataSectors / $sectorsPerCluster) + $neededFatSectors = [int]((($clusterCount + 2) * 4 + $bytesPerSector - 1) / $bytesPerSector) + if ($neededFatSectors -eq $fatSectors) { break } + $fatSectors = $neededFatSectors + } while ($true) + + if ($clusterCount -lt 65525) { + throw "Invalid FAT32 cluster count: $clusterCount" + } + + $fatOffset = [Int64]($partitionOffset + $reservedSectors * $bytesPerSector) + $dataOffset = [Int64]($partitionOffset + ($reservedSectors + $numFats * $fatSectors) * $bytesPerSector) + $clusterSize = $sectorsPerCluster * $bytesPerSector + $fat = [UInt32[]]::new($clusterCount + 2) + $fat[0] = 0x0ffffff8 + $fat[1] = 0x0fffffff + $fat[2] = 0x0fffffff + $script:fatNextCluster = 3 + + function Allocate-Chain([Int64]$ByteCount) { + $needed = [Math]::Max(1, [int](($ByteCount + $clusterSize - 1) / $clusterSize)) + $first = $script:fatNextCluster + for ($i = 0; $i -lt $needed; $i++) { + $cluster = $script:fatNextCluster + $script:fatNextCluster++ + if ($script:fatNextCluster -ge $fat.Length) { throw 'FAT32 image is too small' } + if ($i -eq $needed - 1) { + $fat[$cluster] = 0x0fffffff + } else { + $fat[$cluster] = [UInt32]($cluster + 1) + } + } + return $first + } + + function Get-ClusterOffset([int]$Cluster) { + return [Int64]($dataOffset + (($Cluster - 2) * $clusterSize)) + } + + function Write-FileContent($Stream, [string]$Path, [int]$FirstCluster) { + $bytes = [System.IO.File]::ReadAllBytes($Path) + $remaining = $bytes.Length + $srcOffset = 0 + $cluster = $FirstCluster + while ($remaining -gt 0) { + $chunk = [Math]::Min($clusterSize, $remaining) + [void]$Stream.Seek((Get-ClusterOffset $cluster), [System.IO.SeekOrigin]::Begin) + $Stream.Write($bytes, $srcOffset, $chunk) + $srcOffset += $chunk + $remaining -= $chunk + if ($remaining -gt 0) { $cluster = $fat[$cluster] } + } + } + + function Write-Directory($Stream, [string]$Path, [int]$SelfCluster, [int]$ParentCluster, [bool]$IsRoot) { + $entries = [System.Collections.ArrayList]::new() + $used = @{} + + if (-not $IsRoot) { + [void]$entries.Add((New-ShortEntry '. ' 0x10 $SelfCluster 0)) + [void]$entries.Add((New-ShortEntry '.. ' 0x10 $ParentCluster 0)) + } + + $items = Get-ChildItem -LiteralPath $Path -Force | Where-Object { $_.Name -notlike '*.tmp*' } | Sort-Object @{Expression={$_.PSIsContainer};Descending=$true}, Name + foreach ($item in $items) { + if ($item.PSIsContainer) { + $childCluster = Allocate-Chain $clusterSize + Add-DirectoryEntry $entries $item.Name 0x10 $childCluster 0 $used + Write-Directory $Stream $item.FullName $childCluster $SelfCluster $false + } else { + $fileSize = [uint32]$item.Length + $fileCluster = Allocate-Chain $item.Length + Add-DirectoryEntry $entries $item.Name 0x20 $fileCluster $fileSize $used + Write-FileContent $Stream $item.FullName $fileCluster + } + } + + $entryBytes = $entries.Count * 32 + $neededBytes = [Math]::Max($clusterSize, [int](($entryBytes + $clusterSize - 1) / $clusterSize) * $clusterSize) + $dirBytes = [byte[]]::new($neededBytes) + $offset = 0 + foreach ($entry in $entries) { + [Array]::Copy($entry, 0, $dirBytes, $offset, 32) + $offset += 32 + } + Write-At $Stream (Get-ClusterOffset $SelfCluster) $dirBytes + } + + New-Item -ItemType Directory -Force -Path (Split-Path -Parent $ImagePath) | Out-Null + $fs = [System.IO.File]::Open($ImagePath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) + try { + $fs.SetLength($SizeMB * 1024 * 1024) + + $mbr = [byte[]]::new(512) + $mbr[446 + 4] = 0xee + Set-Le32 $mbr (446 + 8) 1 + $protectiveSectors = [UInt64]($totalSectors - 1) + if ($protectiveSectors -gt [UInt64]4294967295) { $protectiveSectors = [UInt64]4294967295 } + Set-Le32 $mbr (446 + 12) $protectiveSectors + $mbr[510] = 0x55 + $mbr[511] = 0xaa + Write-At $fs 0 $mbr + + $partitionEntries = [byte[]]::new($entryCount * $entrySize) + $espType = Get-GuidBytes 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b' + $partGuid = ([Guid]::NewGuid()).ToByteArray() + [Array]::Copy($espType, 0, $partitionEntries, 0, 16) + [Array]::Copy($partGuid, 0, $partitionEntries, 16, 16) + Set-Le64 $partitionEntries 32 $partStart + Set-Le64 $partitionEntries 40 $partEnd + $nameBytes = [System.Text.Encoding]::Unicode.GetBytes('Deshab ESP') + [Array]::Copy($nameBytes, 0, $partitionEntries, 56, $nameBytes.Length) + $entriesCrc = Get-Crc32 $partitionEntries + $diskGuid = ([Guid]::NewGuid()).ToByteArray() + + $primaryHeader = New-GptHeader 1 $lastLba $firstUsable $lastUsable $diskGuid 2 $entryCount $entrySize $entriesCrc + $backupEntriesLba = [UInt64]($lastLba - $entrySectors) + $backupHeader = New-GptHeader $lastLba 1 $firstUsable $lastUsable $diskGuid $backupEntriesLba $entryCount $entrySize $entriesCrc + + Write-At $fs ($bytesPerSector * 1) $primaryHeader + Write-At $fs ($bytesPerSector * 2) $partitionEntries + Write-At $fs ([Int64]($backupEntriesLba * $bytesPerSector)) $partitionEntries + Write-At $fs ([Int64]($lastLba * $bytesPerSector)) $backupHeader + + $boot = [byte[]]::new(512) + $boot[0] = 0xeb; $boot[1] = 0x58; $boot[2] = 0x90 + [Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('DESHAB '), 0, $boot, 3, 8) + Set-Le16 $boot 11 $bytesPerSector + $boot[13] = [byte]$sectorsPerCluster + Set-Le16 $boot 14 $reservedSectors + $boot[16] = [byte]$numFats + Set-Le16 $boot 17 0 + Set-Le16 $boot 19 0 + $boot[21] = 0xf8 + Set-Le16 $boot 22 0 + Set-Le16 $boot 24 63 + Set-Le16 $boot 26 255 + Set-Le32 $boot 28 $partStart + Set-Le32 $boot 32 $partSectors + Set-Le32 $boot 36 $fatSectors + Set-Le16 $boot 40 0 + Set-Le16 $boot 42 0 + Set-Le32 $boot 44 2 + Set-Le16 $boot 48 1 + Set-Le16 $boot 50 6 + $boot[64] = 0x80 + $boot[66] = 0x29 + Set-Le32 $boot 67 0x44534842 + [Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('DESHAB '), 0, $boot, 71, 11) + [Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('FAT32 '), 0, $boot, 82, 8) + $boot[510] = 0x55; $boot[511] = 0xaa + Write-At $fs $partitionOffset $boot + Write-At $fs ($partitionOffset + 6 * $bytesPerSector) $boot + + $fsinfo = [byte[]]::new(512) + Set-Le32 $fsinfo 0 0x41615252 + Set-Le32 $fsinfo 484 0x61417272 + Set-Le32 $fsinfo 488 4294967295 + Set-Le32 $fsinfo 492 3 + $fsinfo[508] = 0x00; $fsinfo[509] = 0x00; $fsinfo[510] = 0x55; $fsinfo[511] = 0xaa + Write-At $fs ($partitionOffset + $bytesPerSector) $fsinfo + Write-At $fs ($partitionOffset + 7 * $bytesPerSector) $fsinfo + + Write-Directory $fs $SourceDir 2 2 $true + + $fatBytes = [byte[]]::new($fatSectors * $bytesPerSector) + for ($i = 0; $i -lt $fat.Length; $i++) { + $off = $i * 4 + if ($off + 3 -ge $fatBytes.Length) { break } + Set-Le32 $fatBytes $off $fat[$i] + } + Write-At $fs $fatOffset $fatBytes + Write-At $fs ($fatOffset + $fatSectors * $bytesPerSector) $fatBytes + } + finally { + $fs.Close() + } + + Write-Host "[build] GPT + FAT32 ESP IMG: $ImagePath" +} + +Write-Host '[build] Building UTSM kernel...' + +$clang = Find-Tool 'clang' +$lld = Find-Tool 'ld.lld' +$make = Find-Tool 'make' @('mingw32-make') + +if (-not $clang) { + throw 'clang was not found. Install LLVM/Clang or provide a freestanding x86_64 ELF toolchain.' +} + +if (-not $lld) { + throw 'ld.lld was not found. Install LLVM lld or provide a compatible linker.' +} + +if (-not $make) { + throw 'make was not found. Install make.' +} + +Write-Host "[build] clang: $clang" +Write-Host "[build] ld.lld: $lld" +Write-Host "[build] make: $make" + +New-Item -ItemType Directory -Force -Path $BuildTmp | Out-Null +$env:TMP = $BuildTmp +$env:TEMP = $BuildTmp + +Push-Location $UtsmDir +try { + & $make -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make UTSM failed with exit code $LASTEXITCODE" + } +} +finally { + Pop-Location +} + +Write-Host '[build] Building DSK main kernel...' +try { + & $make -C "$DskDir" -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make DSK failed with exit code $LASTEXITCODE" + } +} +finally { +} + +if (-not (Test-Path $Output)) { + throw "UTSM build did not produce $Output" +} +if (-not (Test-Path $DskOutput)) { + throw "DSK build did not produce $DskOutput" +} + +$FirstInitDir = Join-Path $Root 'CODE\firstInit' +$FirstInitOutput = Join-Path $SystemDir 'system\user\use\FirstInit.elf' + +Write-Host '[build] Building FirstInit.elf...' +try { + & $make -C "$FirstInitDir" -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make FirstInit failed with exit code $LASTEXITCODE" + } +} +finally { +} + +if (-not (Test-Path $FirstInitOutput)) { + throw "FirstInit build did not produce $FirstInitOutput" +} +Write-Host "[build] FirstInit Output: $FirstInitOutput" + +$MouseDir = Join-Path $Root 'CODE\mouse' +$MouseOutput = Join-Path $SystemDir 'system\deshab64\mouse\mouseInit.elf' + +Write-Host '[build] Building mouseInit.elf...' +try { + & $make -C "$MouseDir" -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make mouseInit failed with exit code $LASTEXITCODE" + } +} +finally { +} + +if (-not (Test-Path $MouseOutput)) { + throw "mouseInit build did not produce $MouseOutput" +} +Write-Host "[build] mouseInit Output: $MouseOutput" + +$NetmanDir = Join-Path $Root 'CODE\netman' +$NetmanOutput = Join-Path $SystemDir 'system\deshab64\network\netman.elf' + +Write-Host '[build] Building netman.elf...' +try { + & $make -C "$NetmanDir" -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make netman failed with exit code $LASTEXITCODE" + } +} +finally { +} + +if (-not (Test-Path $NetmanOutput)) { + throw "netman build did not produce $NetmanOutput" +} +Write-Host "[build] netman Output: $NetmanOutput" + +Write-Host '[build] Building DKM network drivers...' +Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv') +Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv') + +Write-Host "[build] Output: $Output" +Write-Host "[build] DSK Output: $DskOutput" +Write-Host '[build] Packaging SYSTEM to GPT + FAT32 IMG...' +New-GptFat32Image $SystemDir $ImagePath 128 +Write-Host "[build] Done: $ImagePath" + +# Rebuild SATA FAT32 disk image (DSK reads deshab.elf/FirstInit.elf/mouseInit.elf from here) +Write-Host '[build] Rebuilding SATA FAT32 disk image...' +$BuildTmp = Join-Path $Root '.build_tmp' +$MkFat32 = Join-Path $BuildTmp 'mkfat32.exe' +if (Test-Path $MkFat32) { + Push-Location $BuildTmp + try { + & $MkFat32 + if ($LASTEXITCODE -ne 0) { + Write-Host "[build] WARNING: mkfat32 exited with $LASTEXITCODE" + } + } + finally { + Pop-Location + } + Write-Host "[build] SATA IMG: $(Join-Path $BuildTmp 'sata_fat32_dsk.img')" +} else { + Write-Host '[build] WARNING: mkfat32.exe not found, SATA image not rebuilt' +}