FUCK3
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
/* pe_dll_manager.c — PE 真实 DLL 加载管理器实现。
|
||||
*
|
||||
* 加载流程:
|
||||
* 1. pe_dll_resolve(dll, func) 被 resolve_imports 调用(shim miss 后)
|
||||
* 2. 若 DLL 未缓存,pe_dll_load 读取 DLL 文件 → 复制到 arena → pe_load_image
|
||||
* 3. pe_load_image 内部 resolve_imports 递归解析该 DLL 的依赖
|
||||
* 4. DLL 加载完成后,解析导出表按名称查找函数地址
|
||||
*
|
||||
* 内存安全:fat32_read_path 返回 static 缓冲(g_dsk_fat32_filedata),
|
||||
* 递归加载会覆盖该缓冲。因此 pe_dll_load 先将文件数据复制到 arena,
|
||||
* 再调 pe_load_image,确保递归不破坏外层 pe_data。
|
||||
*/
|
||||
|
||||
#include "pe_dll_manager.h"
|
||||
#include "pe_loader.h"
|
||||
#include "pe_shim.h"
|
||||
#include <utsm/arena.h>
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/pe.h>
|
||||
|
||||
/* ---- PE 导出目录结构(packed,与 pe_loader.h 风格一致) ---- */
|
||||
typedef struct __attribute__((packed)) {
|
||||
u32 characteristics;
|
||||
u32 time_date_stamp;
|
||||
u16 major_version;
|
||||
u16 minor_version;
|
||||
u32 name_rva;
|
||||
u32 ordinal_base;
|
||||
u32 number_of_functions;
|
||||
u32 number_of_names;
|
||||
u32 address_of_functions; /* EAT RVA */
|
||||
u32 address_of_names; /* ENPT RVA */
|
||||
u32 address_of_name_ordinals; /* EOT RVA */
|
||||
} image_export_directory;
|
||||
|
||||
/* ---- DLL 缓存 ---- */
|
||||
#define PE_DLL_CACHE_MAX 32
|
||||
#define PE_DLL_NAME_MAX 64
|
||||
|
||||
enum {
|
||||
DLL_STATE_NONE = 0,
|
||||
DLL_STATE_LOADING = 1,
|
||||
DLL_STATE_LOADED = 2,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char name[PE_DLL_NAME_MAX]; /* 小写 DLL 名 */
|
||||
u64 image_base; /* pe_load_image 返回基址 */
|
||||
u32 image_size; /* SizeOfImage */
|
||||
u32 export_dir_rva; /* 导出表 RVA(0=无导出) */
|
||||
u32 export_dir_size;
|
||||
int state; /* DLL_STATE_* */
|
||||
} pe_dll_entry;
|
||||
|
||||
static pe_dll_entry g_dll_cache[PE_DLL_CACHE_MAX];
|
||||
static pe_dll_reader_fn g_dll_reader = 0;
|
||||
int g_pe_dll_manager_ready = 0;
|
||||
|
||||
/* ---- 辅助函数 ---- */
|
||||
|
||||
/* ASCII 大小写不敏感字符串比较(freestanding,无 strcmp) */
|
||||
static int dll_streq_ci(const char *a, const char *b) {
|
||||
if (!a || !b) return 0;
|
||||
while (*a && *b) {
|
||||
char ca = *a, cb = *b;
|
||||
if (ca >= 'a' && ca <= 'z') ca = (char)(ca - 32);
|
||||
if (cb >= 'a' && cb <= 'z') cb = (char)(cb - 32);
|
||||
if (ca != cb) return 0;
|
||||
a++; b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
/* 将 DLL 名转为小写到 dst(最多 n-1 字符 + NUL) */
|
||||
static void dll_name_lower(const char *src, char *dst, int n) {
|
||||
int i = 0;
|
||||
while (src[i] && i < n - 1) {
|
||||
char c = src[i];
|
||||
if (c >= 'A' && c <= 'Z') c = (char)(c + 32);
|
||||
dst[i] = c;
|
||||
i++;
|
||||
}
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
/* 在缓存中查找 DLL 名(大小写不敏感)。返回索引,-1 未找到。 */
|
||||
static int dll_cache_find(const char *dll_name) {
|
||||
char lower[PE_DLL_NAME_MAX];
|
||||
dll_name_lower(dll_name, lower, sizeof(lower));
|
||||
for (int i = 0; i < PE_DLL_CACHE_MAX; i++) {
|
||||
if (g_dll_cache[i].state == DLL_STATE_NONE) continue;
|
||||
if (dll_streq_ci(g_dll_cache[i].name, lower)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 在缓存中分配一个空闲槽。返回索引,-1 缓存满。 */
|
||||
static int dll_cache_alloc(void) {
|
||||
for (int i = 0; i < PE_DLL_CACHE_MAX; i++) {
|
||||
if (g_dll_cache[i].state == DLL_STATE_NONE) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 从已加载的 PE 镜像中提取导出表 RVA 和 size。
|
||||
* image 指向 pe_load_image 分配的镜像基址(headers 已映射)。 */
|
||||
static void extract_export_dir(u64 image_base, u32 *out_rva, u32 *out_size) {
|
||||
*out_rva = 0;
|
||||
*out_size = 0;
|
||||
const u8 *img = (const u8 *)image_base;
|
||||
|
||||
/* DOS header */
|
||||
const image_dos_header *dos = (const image_dos_header *)img;
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return;
|
||||
|
||||
/* NT headers */
|
||||
const u8 *nt = img + dos->e_lfanew;
|
||||
if (*(const u32 *)nt != IMAGE_NT_SIGNATURE) return;
|
||||
const image_file_header *fh = (const image_file_header *)(nt + 4);
|
||||
const void *opt = (const void *)(fh + 1);
|
||||
|
||||
u16 magic = *(const u16 *)opt;
|
||||
const image_data_directory *data_dir;
|
||||
if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||
const image_optional_header64 *oh = (const image_optional_header64 *)opt;
|
||||
data_dir = oh->data_directory;
|
||||
} else {
|
||||
const image_optional_header32 *oh = (const image_optional_header32 *)opt;
|
||||
data_dir = oh->data_directory;
|
||||
}
|
||||
|
||||
*out_rva = data_dir[IMAGE_DIRECTORY_ENTRY_EXPORT].virtual_address;
|
||||
*out_size = data_dir[IMAGE_DIRECTORY_ENTRY_EXPORT].size;
|
||||
}
|
||||
|
||||
/* 在已加载 DLL 的导出表中按名称查找函数。
|
||||
* 返回绝对地址(image_base + func_rva),0=未找到/转发导出。 */
|
||||
static u64 resolve_export_by_name(u64 image_base, u32 export_dir_rva,
|
||||
const char *func_name) {
|
||||
if (export_dir_rva == 0) return 0;
|
||||
const u8 *img = (const u8 *)image_base;
|
||||
const image_export_directory *exp =
|
||||
(const image_export_directory *)(img + export_dir_rva);
|
||||
if (exp->number_of_names == 0) return 0;
|
||||
|
||||
const u32 *names = (const u32 *)(img + exp->address_of_names);
|
||||
const u16 *ordinals = (const u16 *)(img + exp->address_of_name_ordinals);
|
||||
const u32 *functions = (const u32 *)(img + exp->address_of_functions);
|
||||
|
||||
for (u32 i = 0; i < exp->number_of_names; i++) {
|
||||
const char *name = (const char *)(img + names[i]);
|
||||
if (dll_streq_ci(name, func_name)) {
|
||||
u16 eat_index = ordinals[i];
|
||||
if (eat_index >= exp->number_of_functions) return 0;
|
||||
u32 func_rva = functions[eat_index];
|
||||
|
||||
/* 转发导出:func_rva 落在导出表范围内 → 是转发字符串而非代码地址 */
|
||||
if (func_rva >= export_dir_rva &&
|
||||
func_rva < export_dir_rva + exp->number_of_functions * 4) {
|
||||
/* 粗略范围检查(更精确应使用 export_dir_size,但此处足够) */
|
||||
log_info("[PE] forwarded export (not supported)");
|
||||
return 0;
|
||||
}
|
||||
return image_base + func_rva;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 公共接口 ---- */
|
||||
|
||||
void pe_dll_manager_init(pe_dll_reader_fn reader) {
|
||||
g_dll_reader = reader;
|
||||
for (int i = 0; i < PE_DLL_CACHE_MAX; i++) {
|
||||
g_dll_cache[i].state = DLL_STATE_NONE;
|
||||
}
|
||||
g_pe_dll_manager_ready = 1;
|
||||
log_info("[PE] DLL manager initialized (SYSTEM/lib)");
|
||||
}
|
||||
|
||||
int pe_dll_load(const char *dll_name, u64 *out_base) {
|
||||
if (!g_pe_dll_manager_ready || !g_dll_reader || !dll_name) {
|
||||
if (out_base) *out_base = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 查缓存 */
|
||||
int idx = dll_cache_find(dll_name);
|
||||
if (idx >= 0) {
|
||||
pe_dll_entry *e = &g_dll_cache[idx];
|
||||
if (e->state == DLL_STATE_LOADED) {
|
||||
if (out_base) *out_base = e->image_base;
|
||||
return 0;
|
||||
}
|
||||
if (e->state == DLL_STATE_LOADING) {
|
||||
/* 循环依赖:返回 -1,调用方填 unimpl stub */
|
||||
if (out_base) *out_base = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 分配缓存槽 */
|
||||
idx = dll_cache_alloc();
|
||||
if (idx < 0) {
|
||||
log_warn("[PE] DLL cache full, cannot load");
|
||||
if (out_base) *out_base = 0;
|
||||
return -2;
|
||||
}
|
||||
|
||||
pe_dll_entry *entry = &g_dll_cache[idx];
|
||||
dll_name_lower(dll_name, entry->name, PE_DLL_NAME_MAX);
|
||||
entry->state = DLL_STATE_LOADING;
|
||||
entry->image_base = 0;
|
||||
entry->export_dir_rva = 0;
|
||||
entry->export_dir_size = 0;
|
||||
|
||||
/* 读取 DLL 文件 */
|
||||
u8 *file_data = 0;
|
||||
u32 file_size = 0;
|
||||
if (g_dll_reader(dll_name, &file_data, &file_size) != 0 || !file_data || file_size == 0) {
|
||||
entry->state = DLL_STATE_NONE; /* 回收槽 */
|
||||
if (out_base) *out_base = 0;
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* 复制文件数据到 arena(防止递归加载覆盖 fat32_read_path 的 static 缓冲) */
|
||||
u8 *file_copy = (u8 *)kmem_alloc_aligned(file_size, 16);
|
||||
if (!file_copy) {
|
||||
entry->state = DLL_STATE_NONE;
|
||||
if (out_base) *out_base = 0;
|
||||
return -4;
|
||||
}
|
||||
for (u32 i = 0; i < file_size; i++) file_copy[i] = file_data[i];
|
||||
|
||||
/* 加载 PE 镜像(内部会递归 resolve_imports → 可能再次 pe_dll_load) */
|
||||
pe_image_info info;
|
||||
if (pe_load_image(file_copy, file_size, &info) != 0) {
|
||||
log_warn("[PE] DLL load failed: pe_load_image error");
|
||||
log_info(dll_name);
|
||||
entry->state = DLL_STATE_NONE;
|
||||
if (out_base) *out_base = 0;
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* 提取导出表信息 */
|
||||
extract_export_dir(info.image_base, &entry->export_dir_rva,
|
||||
&entry->export_dir_size);
|
||||
entry->image_base = info.image_base;
|
||||
entry->image_size = (u32)info.image_size;
|
||||
entry->state = DLL_STATE_LOADED;
|
||||
|
||||
log_info("[PE] loaded DLL:");
|
||||
log_info(dll_name);
|
||||
log_hex64("[PE] DLL base=", info.image_base);
|
||||
|
||||
if (out_base) *out_base = info.image_base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 pe_dll_resolve(const char *dll_name, const char *func_name) {
|
||||
if (!g_pe_dll_manager_ready || !dll_name || !func_name) return 0;
|
||||
|
||||
/* 确保 DLL 已加载 */
|
||||
int idx = dll_cache_find(dll_name);
|
||||
if (idx < 0 || g_dll_cache[idx].state != DLL_STATE_LOADED) {
|
||||
u64 base = 0;
|
||||
if (pe_dll_load(dll_name, &base) != 0) return 0;
|
||||
idx = dll_cache_find(dll_name);
|
||||
if (idx < 0 || g_dll_cache[idx].state != DLL_STATE_LOADED) return 0;
|
||||
}
|
||||
|
||||
pe_dll_entry *e = &g_dll_cache[idx];
|
||||
return resolve_export_by_name(e->image_base, e->export_dir_rva, func_name);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
pe/pe_dll_manager.o: pe\pe_dll_manager.c pe\pe_dll_manager.h \
|
||||
include\utsm\types.h pe\pe_loader.h include\utsm\pe.h pe\pe_shim.h \
|
||||
include\utsm\arena.h include\utsm\log.h
|
||||
pe\pe_dll_manager.h:
|
||||
include\utsm\types.h:
|
||||
pe\pe_loader.h:
|
||||
include\utsm\pe.h:
|
||||
pe\pe_shim.h:
|
||||
include\utsm\arena.h:
|
||||
include\utsm\log.h:
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef PE_DLL_MANAGER_H
|
||||
#define PE_DLL_MANAGER_H
|
||||
|
||||
/* pe_dll_manager.h — PE 真实 DLL 加载管理器(SYSTEM/lib 兼容层)。
|
||||
*
|
||||
* PE 兼容层默认使用内置 shim 表解析 imports。shim 未命中的函数,
|
||||
* 由 DLL 管理器从 SYSTEM/lib/ 加载真实 DLL 并解析导出表。
|
||||
*
|
||||
* 架构决策:
|
||||
* - 仅 PE32+(64 位原生)启用真实 DLL 加载
|
||||
* - PE32(32 位解释器)不加载真实 DLL(x86emu32 不能调 64 位代码)
|
||||
* - 不修改 pe_service 结构体 ABI(通过全局注册模式)
|
||||
* - 递归依赖:pe_dll_load → pe_load_image → resolve_imports → pe_dll_resolve → pe_dll_load
|
||||
* - 循环检测:DLL_LOADING 状态遇到则跳过(填 unimpl stub,安全降级)
|
||||
* - 内存:每个 DLL 用 kmem_alloc_aligned 分配 SizeOfImage + 文件副本(arena 不释放)
|
||||
* - 缓存上限 32 个 DLL
|
||||
*/
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
/* DLL 文件读取回调:从 SYSTEM/lib/ 读取 DLL 到内存。
|
||||
* name : DLL 名(如 "foo.dll",原样来自 PE import 表)
|
||||
* out_data : 返回内存数据指针(调用方不负责释放)
|
||||
* out_size : 返回字节大小
|
||||
* 返回 0 成功,负值失败。 */
|
||||
typedef int (*pe_dll_reader_fn)(const char *name, u8 **out_data, u32 *out_size);
|
||||
|
||||
/* 全局就绪标志(1=DLL 管理器已初始化,可解析真实 DLL)。
|
||||
* pe_loader.c resolve_imports 检查此标志决定是否尝试真实 DLL。 */
|
||||
extern int g_pe_dll_manager_ready;
|
||||
|
||||
/* 初始化 DLL 管理器(注册文件读取回调)。
|
||||
* 在 DKM 驱动加载完成、block provider 注册后调用。 */
|
||||
void pe_dll_manager_init(pe_dll_reader_fn reader);
|
||||
|
||||
/* 加载 DLL(含递归解析其自身 import)。
|
||||
* 已加载返回缓存基址。循环依赖检测:LOADING 状态遇到则返回 -1。
|
||||
* dll_name : DLL 名(如 "foo.dll",大小写不敏感)
|
||||
* out_base : 输出加载基址
|
||||
* 返回 0 成功,负值失败。 */
|
||||
int pe_dll_load(const char *dll_name, u64 *out_base);
|
||||
|
||||
/* 按名称解析导出函数。
|
||||
* 若 DLL 未加载,自动调用 pe_dll_load。
|
||||
* 管理器未就绪时返回 0。
|
||||
* dll_name : DLL 名(大小写不敏感)
|
||||
* func_name : 函数名(大小写不敏感,PE 导出表规范)
|
||||
* 返回绝对地址,0=未找到。 */
|
||||
u64 pe_dll_resolve(const char *dll_name, const char *func_name);
|
||||
|
||||
#endif /* PE_DLL_MANAGER_H */
|
||||
@@ -0,0 +1,44 @@
|
||||
# Makefile.linux — Deshab 桌面 Linux SDL2 宿主构建
|
||||
#
|
||||
# 用法(在 Linux 下,CODE/desktop/ 目录):
|
||||
# make -f Makefile.linux # 编译 desktop_host
|
||||
# make -f Makefile.linux run # 编译并运行(默认读 ../../ISO/deshab.img)
|
||||
# make -f Makefile.linux clean
|
||||
#
|
||||
# 依赖:gcc/clang、libsdl2-dev(apt install libsdl2-dev)
|
||||
# 产物:desktop_host(普通 Linux 可执行文件)
|
||||
#
|
||||
# 与裸机 MAKEFILE 完全独立:不带 -ffreestanding/-mno-sse/-target,
|
||||
# 不使用 linker.ld;通过 -DDESHAB_HOST 启用 host_main.c + main.c 中的宿主分支。
|
||||
|
||||
CC ?= gcc
|
||||
SDL_CFLAGS := $(shell sdl2-config --cflags 2>/dev/null)
|
||||
SDL_LIBS := $(shell sdl2-config --libs 2>/dev/null)
|
||||
|
||||
ROOT := ../..
|
||||
IMG := $(ROOT)/ISO/deshab.img
|
||||
|
||||
CFLAGS := -DDESHAB_HOST -O2 -Wall -Wextra -g \
|
||||
-I. -I../UTSM/include -I../tools -I../firstInit \
|
||||
$(SDL_CFLAGS)
|
||||
LDFLAGS := $(SDL_LIBS) -lm
|
||||
|
||||
TARGET := desktop_host
|
||||
SRCS := main.c host_main.c
|
||||
OBJS := $(SRCS:.c=.o)
|
||||
|
||||
.PHONY: all run clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
%.o: %.c host_main.h
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
run: $(TARGET)
|
||||
./$(TARGET) --img $(IMG)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET)
|
||||
@@ -0,0 +1,376 @@
|
||||
/* host_main.c — Deshab 桌面 Linux SDL2 宿主
|
||||
*
|
||||
* 仅在 -DDESHAB_HOST 编译时链接。提供:
|
||||
* - SDL2 窗口 + streaming texture(模拟 framebuffer,g_host_backbuf)
|
||||
* - 鼠标/键盘事件队列(模拟 PS/2,供 host_mouse_poll/host_kb_poll 消费)
|
||||
* - deshab.img GPT ESP 文件 block 后端(模拟 kernel_api block provider)
|
||||
* - 单调时钟 / 当天秒数(替代 rdtsc / RTC CMOS)
|
||||
*
|
||||
* main() 构造 dsk_boot_context 后调用 dsk_entry(),进入桌面主循环。
|
||||
* 主循环每帧末尾调用 host_pump_and_present() 上传 back buffer 并 pump 事件。
|
||||
*/
|
||||
#include "host_main.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utsm/dsk.h>
|
||||
|
||||
/* ---- 全局缓冲(供 desktop/main.c 经 extern 使用) ---- */
|
||||
u8 *g_host_sprite = NULL;
|
||||
u8 *g_host_backbuf = NULL;
|
||||
|
||||
/* ---- SDL 状态 ---- */
|
||||
static SDL_Window *g_win = NULL;
|
||||
static SDL_Renderer *g_rend = NULL;
|
||||
static SDL_Texture *g_tex = NULL;
|
||||
static int g_fb_w = 1280, g_fb_h = 720;
|
||||
|
||||
/* ---- 鼠标状态(SDL 事件累积,host_mouse_poll 消费) ---- */
|
||||
static struct {
|
||||
int x, y; /* 绝对坐标 */
|
||||
int btn; /* bit0=左 bit1=右 */
|
||||
int prev_btn; /* 上次 poll 时的按键状态 */
|
||||
int changed; /* 自上次 poll 以来有变化 */
|
||||
} g_mse = { 400, 300, 0, 0, 0 };
|
||||
|
||||
/* ---- 键盘扫描码队列(SDL 键事件 → set-1 字节序列) ---- */
|
||||
#define KB_QSZ 64
|
||||
static u8 g_kb_q[KB_QSZ];
|
||||
static int g_kb_head = 0, g_kb_tail = 0;
|
||||
|
||||
static void kb_push(u8 b) {
|
||||
int nxt = (g_kb_tail + 1) % KB_QSZ;
|
||||
if (nxt == g_kb_head) return; /* 满,丢弃 */
|
||||
g_kb_q[g_kb_tail] = b;
|
||||
g_kb_tail = nxt;
|
||||
}
|
||||
|
||||
/* ---- 日志 ---- */
|
||||
void host_log(const char *s) { fputs(s, stderr); fflush(stderr); }
|
||||
void host_log_char(char c) { fputc(c, stderr); fflush(stderr); }
|
||||
|
||||
/* ---- 时间 ---- */
|
||||
u64 host_now_us(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (u64)ts.tv_sec * 1000000ULL + (u64)ts.tv_nsec / 1000ULL;
|
||||
}
|
||||
|
||||
int host_rtc_seconds(void) {
|
||||
time_t t = time(NULL);
|
||||
struct tm *lt = localtime(&t);
|
||||
return lt->tm_hour * 3600 + lt->tm_min * 60 + lt->tm_sec;
|
||||
}
|
||||
|
||||
/* ---- 鼠标 ---- */
|
||||
int host_mouse_poll(int *px, int *py, int *pbtn,
|
||||
int *p_left_pressed, int *p_left_released,
|
||||
int *p_right_pressed, int max_x, int max_y) {
|
||||
if (!g_mse.changed && g_mse.btn == g_mse.prev_btn) return 0;
|
||||
g_mse.changed = 0;
|
||||
int btn = g_mse.btn;
|
||||
int prev = g_mse.prev_btn;
|
||||
g_mse.prev_btn = btn;
|
||||
int x = g_mse.x, y = g_mse.y;
|
||||
if (x < 0) x = 0; if (y < 0) y = 0;
|
||||
if (x > max_x) x = max_x; if (y > max_y) y = max_y;
|
||||
*px = x; *py = y; *pbtn = btn;
|
||||
*p_left_pressed = (btn & 1) && !(prev & 1);
|
||||
*p_left_released = !(btn & 1) && (prev & 1);
|
||||
*p_right_pressed = (btn & 2) && !(prev & 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- 键盘 ---- */
|
||||
int host_kb_poll(u8 *out_sc) {
|
||||
if (g_kb_head == g_kb_tail) return 0;
|
||||
*out_sc = g_kb_q[g_kb_head];
|
||||
g_kb_head = (g_kb_head + 1) % KB_QSZ;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SDL_Scancode → PS/2 set-1 make 码(单字节;E0 前缀键返回 0xFF 表示需 E0) */
|
||||
static u8 sdl_to_set1(SDL_Scancode sc, int *e0) {
|
||||
*e0 = 0;
|
||||
switch (sc) {
|
||||
case SDL_SCANCODE_A: return 0x1E; case SDL_SCANCODE_B: return 0x30;
|
||||
case SDL_SCANCODE_C: return 0x2E; case SDL_SCANCODE_D: return 0x20;
|
||||
case SDL_SCANCODE_E: return 0x12; case SDL_SCANCODE_F: return 0x21;
|
||||
case SDL_SCANCODE_G: return 0x22; case SDL_SCANCODE_H: return 0x23;
|
||||
case SDL_SCANCODE_I: return 0x17; case SDL_SCANCODE_J: return 0x24;
|
||||
case SDL_SCANCODE_K: return 0x25; case SDL_SCANCODE_L: return 0x26;
|
||||
case SDL_SCANCODE_M: return 0x32; case SDL_SCANCODE_N: return 0x31;
|
||||
case SDL_SCANCODE_O: return 0x18; case SDL_SCANCODE_P: return 0x19;
|
||||
case SDL_SCANCODE_Q: return 0x10; case SDL_SCANCODE_R: return 0x13;
|
||||
case SDL_SCANCODE_S: return 0x1F; case SDL_SCANCODE_T: return 0x14;
|
||||
case SDL_SCANCODE_U: return 0x16; case SDL_SCANCODE_V: return 0x2F;
|
||||
case SDL_SCANCODE_W: return 0x11; case SDL_SCANCODE_X: return 0x2D;
|
||||
case SDL_SCANCODE_Y: return 0x15; case SDL_SCANCODE_Z: return 0x2C;
|
||||
case SDL_SCANCODE_1: return 0x02; case SDL_SCANCODE_2: return 0x03;
|
||||
case SDL_SCANCODE_3: return 0x04; case SDL_SCANCODE_4: return 0x05;
|
||||
case SDL_SCANCODE_5: return 0x06; case SDL_SCANCODE_6: return 0x07;
|
||||
case SDL_SCANCODE_7: return 0x08; case SDL_SCANCODE_8: return 0x09;
|
||||
case SDL_SCANCODE_9: return 0x0A; case SDL_SCANCODE_0: return 0x0B;
|
||||
case SDL_SCANCODE_RETURN: return 0x1C;
|
||||
case SDL_SCANCODE_BACKSPACE: return 0x0E;
|
||||
case SDL_SCANCODE_TAB: return 0x0F;
|
||||
case SDL_SCANCODE_SPACE: return 0x39;
|
||||
case SDL_SCANCODE_ESCAPE: return 0x01;
|
||||
case SDL_SCANCODE_LSHIFT: return 0x2A; case SDL_SCANCODE_RSHIFT: return 0x36;
|
||||
case SDL_SCANCODE_LCTRL: return 0x1D; case SDL_SCANCODE_RCTRL: return 0x1D;
|
||||
case SDL_SCANCODE_LALT: return 0x38; case SDL_SCANCODE_RALT: return 0x38;
|
||||
case SDL_SCANCODE_CAPSLOCK: return 0x3A;
|
||||
case SDL_SCANCODE_MINUS: return 0x0C;
|
||||
case SDL_SCANCODE_EQUALS: return 0x0D;
|
||||
case SDL_SCANCODE_LEFTBRACKET: return 0x1A;
|
||||
case SDL_SCANCODE_RIGHTBRACKET: return 0x1B;
|
||||
case SDL_SCANCODE_SEMICOLON: return 0x27;
|
||||
case SDL_SCANCODE_APOSTROPHE: return 0x28;
|
||||
case SDL_SCANCODE_GRAVE: return 0x29;
|
||||
case SDL_SCANCODE_BACKSLASH: return 0x2B;
|
||||
case SDL_SCANCODE_COMMA: return 0x33;
|
||||
case SDL_SCANCODE_PERIOD: return 0x34;
|
||||
case SDL_SCANCODE_SLASH: return 0x35;
|
||||
case SDL_SCANCODE_LEFT: *e0 = 1; return 0x4B;
|
||||
case SDL_SCANCODE_RIGHT: *e0 = 1; return 0x4D;
|
||||
case SDL_SCANCODE_UP: *e0 = 1; return 0x48;
|
||||
case SDL_SCANCODE_DOWN: *e0 = 1; return 0x50;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_key_event(SDL_Scancode sc, int down) {
|
||||
int e0 = 0;
|
||||
u8 code = sdl_to_set1(sc, &e0);
|
||||
if (!code) return;
|
||||
if (e0) kb_push(0xE0);
|
||||
kb_push(down ? code : (u8)(code | 0x80));
|
||||
}
|
||||
|
||||
/* ---- 帧末 pump + present ---- */
|
||||
int host_pump_and_present(void) {
|
||||
int quit = 0;
|
||||
/* 上传 back buffer 到 SDL texture 并 present */
|
||||
if (g_tex && g_host_backbuf) {
|
||||
SDL_UpdateTexture(g_tex, NULL, g_host_backbuf, g_fb_w * 4);
|
||||
SDL_RenderClear(g_rend);
|
||||
SDL_RenderCopy(g_rend, g_tex, NULL, NULL);
|
||||
SDL_RenderPresent(g_rend);
|
||||
}
|
||||
/* pump 事件 */
|
||||
SDL_Event ev;
|
||||
while (SDL_PollEvent(&ev)) {
|
||||
switch (ev.type) {
|
||||
case SDL_QUIT:
|
||||
quit = 1;
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
g_mse.x = ev.motion.x;
|
||||
g_mse.y = ev.motion.y;
|
||||
g_mse.changed = 1;
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
int down = (ev.type == SDL_MOUSEBUTTONDOWN);
|
||||
if (ev.button.button == SDL_BUTTON_LEFT) {
|
||||
if (down) g_mse.btn |= 1; else g_mse.btn &= ~1;
|
||||
g_mse.changed = 1;
|
||||
} else if (ev.button.button == SDL_BUTTON_RIGHT) {
|
||||
if (down) g_mse.btn |= 2; else g_mse.btn &= ~2;
|
||||
g_mse.changed = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP: {
|
||||
/* 忽略重复事件(SDL_KEYDOWN 带 repeat>0) */
|
||||
if (ev.key.repeat) break;
|
||||
handle_key_event(ev.key.keysym.scancode, ev.type == SDL_KEYDOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return quit;
|
||||
}
|
||||
|
||||
/* ---- block 后端:deshab.img GPT ESP 文件 ---- */
|
||||
static int g_img_fd = -1;
|
||||
static u64 g_esp_byte_off = 0; /* ESP 分区起始字节偏移 */
|
||||
|
||||
/* 读取 img LBA1 GPT header,定位 ESP 分区起始 LBA。失败回退 2048。 */
|
||||
static u64 find_esp_offset(int fd) {
|
||||
u8 hdr[512];
|
||||
if (pread(fd, hdr, 512, 512) != 512) return 2048 * 512;
|
||||
/* GPT signature "EFI PART" */
|
||||
if (memcmp(hdr, "EFI PART", 8) != 0) return 2048 * 512;
|
||||
u64 entries_lba = 0;
|
||||
u32 entry_count = 0, entry_size = 0;
|
||||
memcpy(&entries_lba, hdr + 72, 8);
|
||||
memcpy(&entry_count, hdr + 80, 4);
|
||||
memcpy(&entry_size, hdr + 84, 4);
|
||||
if (entry_size < 56 || entry_count == 0 || entry_count > 256) return 2048 * 512;
|
||||
/* ESP 类型 GUID(混合字节序):c12a7328-f81f-11d2-ba4b-00a0c93ec93b */
|
||||
static const u8 esp_guid[16] = {
|
||||
0x28,0x73,0x2a,0xc1, 0x1f,0xf8, 0xd2,0x11,
|
||||
0xba,0x4b, 0x00,0xa0,0xc9,0x3e,0xc9,0x3b
|
||||
};
|
||||
u8 *entries = (u8 *)malloc((u64)entry_count * entry_size);
|
||||
if (!entries) return 2048 * 512;
|
||||
u64 entries_byte = entries_lba * 512;
|
||||
ssize_t got = pread(fd, entries, (u64)entry_count * entry_size, entries_byte);
|
||||
if (got <= 0) { free(entries); return 2048 * 512; }
|
||||
u64 esp_lba = 0;
|
||||
int found = 0;
|
||||
for (u32 i = 0; i < entry_count; i++) {
|
||||
u8 *e = entries + (u64)i * entry_size;
|
||||
if (memcmp(e, esp_guid, 16) == 0) {
|
||||
memcpy(&esp_lba, e + 32, 8);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(entries);
|
||||
return found ? (esp_lba * 512) : (2048 * 512);
|
||||
}
|
||||
|
||||
/* block_read/write:desktop 期望 int (u32 index, u64 lba, u32 count, void* buf)
|
||||
* index 固定 0;扇区 512 字节;偏移 = esp_byte_off + lba*512。 */
|
||||
static int host_block_read(u32 index, u64 lba, u32 count, void *buf) {
|
||||
(void)index;
|
||||
if (g_img_fd < 0) return -1;
|
||||
u64 off = g_esp_byte_off + lba * 512ULL;
|
||||
u64 total = (u64)count * 512ULL;
|
||||
u8 *p = (u8 *)buf;
|
||||
while (total > 0) {
|
||||
ssize_t n = pread(g_img_fd, p, total, off);
|
||||
if (n <= 0) return -1;
|
||||
p += n; off += (u64)n; total -= (u64)n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host_block_write(u32 index, u64 lba, u32 count, const void *buf) {
|
||||
(void)index;
|
||||
if (g_img_fd < 0) return -1;
|
||||
u64 off = g_esp_byte_off + lba * 512ULL;
|
||||
u64 total = (u64)count * 512ULL;
|
||||
const u8 *p = (const u8 *)buf;
|
||||
while (total > 0) {
|
||||
ssize_t n = pwrite(g_img_fd, p, total, off);
|
||||
if (n <= 0) return -1;
|
||||
p += n; off += (u64)n; total -= (u64)n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 伪造 kernel_api:让 dsk_entry 现有 block 获取路径零改动 ----
|
||||
* desktop/main.c: u64 blk = *(u64*)(ctx->dkm_kernel_api + 0xA8);
|
||||
* g_block_read = *(u64*)(blk + 16);
|
||||
* g_block_write = *(u64*)(blk + 24);
|
||||
* 故 host_kernel_api 偏移 0xA8 处填 &host_block_api;
|
||||
* host_block_api[2] = read, [3] = write(偏移 16/24)。 */
|
||||
static u64 host_block_api[8];
|
||||
static u8 host_kernel_api[0xB0 + 8];
|
||||
|
||||
static void build_fake_kernel_api(void) {
|
||||
memset(host_kernel_api, 0, sizeof(host_kernel_api));
|
||||
memset(host_block_api, 0, sizeof(host_block_api));
|
||||
host_block_api[2] = (u64)(uintptr_t)host_block_read;
|
||||
host_block_api[3] = (u64)(uintptr_t)host_block_write;
|
||||
*(u64 *)(host_kernel_api + 0xA8) = (u64)(uintptr_t)host_block_api;
|
||||
}
|
||||
|
||||
/* ---- main ---- */
|
||||
int main(int argc, char **argv) {
|
||||
const char *img_path = "../../ISO/deshab.img";
|
||||
int width = 1280, height = 720;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--img") == 0 && i + 1 < argc) { img_path = argv[++i]; continue; }
|
||||
if (strcmp(argv[i], "--width") == 0 && i + 1 < argc) { width = atoi(argv[++i]); continue; }
|
||||
if (strcmp(argv[i], "--height") == 0 && i + 1 < argc) { height = atoi(argv[++i]); continue; }
|
||||
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
||||
fprintf(stderr, "usage: desktop_host [--img PATH] [--width W] [--height H]\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
g_fb_w = width; g_fb_h = height;
|
||||
|
||||
/* 打开镜像 */
|
||||
g_img_fd = open(img_path, O_RDWR);
|
||||
if (g_img_fd < 0) {
|
||||
/* 只读回退 */
|
||||
g_img_fd = open(img_path, O_RDONLY);
|
||||
if (g_img_fd < 0) {
|
||||
fprintf(stderr, "[host] cannot open %s: ", img_path);
|
||||
perror("");
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "[host] warning: %s opened read-only, FAT32 writes will fail\n", img_path);
|
||||
}
|
||||
g_esp_byte_off = find_esp_offset(g_img_fd);
|
||||
fprintf(stderr, "[host] img=%s esp_off=%llu width=%d height=%d\n",
|
||||
img_path, (unsigned long long)g_esp_byte_off, width, height);
|
||||
|
||||
/* SDL */
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
fprintf(stderr, "[host] SDL_Init: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
g_win = SDL_CreateWindow("Deshab Desktop (Linux host)",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
width, height, SDL_WINDOW_SHOWN);
|
||||
if (!g_win) { fprintf(stderr, "[host] SDL_CreateWindow: %s\n", SDL_GetError()); return 1; }
|
||||
g_rend = SDL_CreateRenderer(g_win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!g_rend) g_rend = SDL_CreateRenderer(g_win, -1, 0);
|
||||
if (!g_rend) { fprintf(stderr, "[host] SDL_CreateRenderer: %s\n", SDL_GetError()); return 1; }
|
||||
g_tex = SDL_CreateTexture(g_rend, SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
if (!g_tex) { fprintf(stderr, "[host] SDL_CreateTexture: %s\n", SDL_GetError()); return 1; }
|
||||
|
||||
/* back buffer + sprite buffer */
|
||||
size_t fbsz = (size_t)width * height * 4;
|
||||
g_host_backbuf = (u8 *)malloc(fbsz);
|
||||
g_host_sprite = (u8 *)malloc(fbsz);
|
||||
if (!g_host_backbuf || !g_host_sprite) {
|
||||
fprintf(stderr, "[host] alloc buffer failed\n");
|
||||
return 1;
|
||||
}
|
||||
memset(g_host_backbuf, 0, fbsz);
|
||||
memset(g_host_sprite, 0, fbsz);
|
||||
|
||||
build_fake_kernel_api();
|
||||
|
||||
/* 构造 dsk_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.framebuffer_address = (u64)(uintptr_t)g_host_backbuf;
|
||||
ctx.framebuffer_width = (u64)width;
|
||||
ctx.framebuffer_height = (u64)height;
|
||||
ctx.framebuffer_pitch = (u64)width * 4;
|
||||
ctx.framebuffer_bpp = 32;
|
||||
ctx.dkm_kernel_api = (u64)(uintptr_t)host_kernel_api;
|
||||
/* reserved[5]=0:无 linux_compat 服务,桌面侧 g_lxc_svc 保持 NULL */
|
||||
|
||||
fprintf(stderr, "[host] entering dsk_entry\n");
|
||||
dsk_entry(&ctx);
|
||||
fprintf(stderr, "[host] dsk_entry returned, exiting\n");
|
||||
|
||||
SDL_DestroyTexture(g_tex);
|
||||
SDL_DestroyRenderer(g_rend);
|
||||
SDL_DestroyWindow(g_win);
|
||||
SDL_Quit();
|
||||
if (g_img_fd >= 0) close(g_img_fd);
|
||||
free(g_host_backbuf);
|
||||
free(g_host_sprite);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/* host_main.h — Deshab 桌面 Linux 宿主接口
|
||||
*
|
||||
* 仅在 -DDESHAB_HOST 编译时使用。提供 SDL2 窗口(模拟 framebuffer)、
|
||||
* 鼠标/键盘事件(模拟 PS/2)、deshab.img 文件 block 后端、单调时钟
|
||||
* (替代 rDTSC)与当天秒数(替代 RTC CMOS)。
|
||||
*
|
||||
* desktop/main.c 通过 #ifdef DESHAB_HOST 调用本接口;裸机构建不包含本头。
|
||||
*/
|
||||
#ifndef DESHAB_HOST_MAIN_H
|
||||
#define DESHAB_HOST_MAIN_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
/* dsk_entry 由 desktop/main.c 提供,host_main.c 构造好 ctx 后调用。 */
|
||||
struct dsk_boot_context;
|
||||
void dsk_entry(const struct dsk_boot_context *ctx);
|
||||
|
||||
/* ---- 日志(替代 COM1 串口 sputc/swrite/slog) ---- */
|
||||
void host_log(const char *s); /* 输出字符串到 stderr */
|
||||
void host_log_char(char c); /* 输出单字符到 stderr */
|
||||
|
||||
/* ---- 时间 ---- */
|
||||
/* 单调时钟微秒,替代 rdtsc。供 tsc_calibrate/g_last_click_tsc 使用。
|
||||
* g_tsc_per_sec 应设为 1000000。 */
|
||||
u64 host_now_us(void);
|
||||
/* 当天秒数(0..86399),替代 rtc_day_sec。 */
|
||||
int host_rtc_seconds(void);
|
||||
|
||||
/* ---- 鼠标(替代 ps2_mouse_poll) ----
|
||||
* 从 SDL 事件累积的鼠标状态生成一次快照。
|
||||
* px/py : 输出当前绝对坐标(已钳位到 [0,max])
|
||||
* pbtn : 输出按键位(bit0=左 bit1=右)
|
||||
* p_left_pressed / p_left_released / p_right_pressed : 边沿事件(1 表示本次发生)
|
||||
* max_x/max_y : 坐标上界(g_fb_w - CURSOR_SIZE 等)
|
||||
* 返回 1 表示有移动或按键变化,0 表示无事件。 */
|
||||
int host_mouse_poll(int *px, int *py, int *pbtn,
|
||||
int *p_left_pressed, int *p_left_released,
|
||||
int *p_right_pressed, int max_x, int max_y);
|
||||
|
||||
/* ---- 键盘(替代 inb(0x64)/inb(0x60) 扫描码轮询) ----
|
||||
* 取一个 PS/2 set-1 扫描码(make 码;break 码为 |0x80)。
|
||||
* 返回 1 表示取到,0 表示队列空。 */
|
||||
int host_kb_poll(u8 *out_sc);
|
||||
|
||||
/* ---- 帧末:pump SDL 事件 + present back buffer ----
|
||||
* 在主循环末尾调用(替代 __asm__("pause"))。
|
||||
* 1. 把 back buffer(g_host_backbuf)上传到 SDL texture 并 RenderPresent
|
||||
* 2. SDL_PollEvent 排空:鼠标/键盘事件入内部状态,SDL_QUIT 返回非 0
|
||||
* 返回非 0 表示收到退出请求(窗口关闭),调用方应置 g_quit。 */
|
||||
int host_pump_and_present(void);
|
||||
|
||||
/* ---- host 分配的缓冲(替代裸机硬编码物理地址) ----
|
||||
* g_host_sprite : sprite 渲染缓冲(替代 SPRITE_BUF_ADDR 0x7000000)
|
||||
* g_host_backbuf: 真实 framebuffer 对应的 back buffer(替代 g_real_fb 指向的显存)
|
||||
* 大小均为 width*height*4 字节,由 host_main.c 在初始化时分配。 */
|
||||
extern u8 *g_host_sprite;
|
||||
extern u8 *g_host_backbuf;
|
||||
|
||||
#endif /* DESHAB_HOST_MAIN_H */
|
||||
@@ -0,0 +1,231 @@
|
||||
/* fat32_lfn.h — Deshab FAT32 VFAT 长文件名(LFN)解析共享库(static inline,无库)
|
||||
*
|
||||
* 前置条件:包含者已定义 u8/u16/u32 基础类型(与 fat32_io.h 一致)。
|
||||
*
|
||||
* FAT32 LFN 原理:
|
||||
* 目录项中 attr==0x0F 的是 LFN 片段项。每项含 13 个 UTF-16 LE 字符
|
||||
* (name1[5] + name2[6] + name3[2]),按逆序存储——序号最高(带 0x40
|
||||
* 标志)的项最先出现,序号递减,最后紧跟一个 8.3 短目录项作为"主项"。
|
||||
* 短名项的 checksum(由 name11 计算)用于校验整条 LFN 链完整性。
|
||||
*
|
||||
* 用法(在目录遍历循环中):
|
||||
* fat32_lfn_buf lfn;
|
||||
* fat32_lfn_init(&lfn);
|
||||
* // 外层簇遍历循环——lfn 必须跨簇存活(LFN 链可能跨簇边界)
|
||||
* while (遍历目录簇) {
|
||||
* for (每个 32 字节目录项 e) {
|
||||
* if (name[0]==0) 目录结束;
|
||||
* if (name[0]==0xE5) { fat32_lfn_init(&lfn); continue; }
|
||||
* int is_short = fat32_lfn_process(&lfn, entry_ptr);
|
||||
* if (is_short) {
|
||||
* // lfn.valid==1 时有完整 LFN,可 fat32_lfn_to_ascii 取名
|
||||
* // 用 fat32_lfn_match 匹配长名或 8.3 短名
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 注意:fat32_lfn_buf 必须在簇遍历循环**外**声明,跨簇切换时不清空,
|
||||
* 以支持跨簇边界的 LFN 链。
|
||||
*/
|
||||
|
||||
#ifndef DESHAB_FAT32_LFN_H
|
||||
#define DESHAB_FAT32_LFN_H
|
||||
|
||||
/* LFN 最大字符数(FAT32 规范最多 20 个 LFN 项 = 260 字符) */
|
||||
#define FAT32_LFN_MAX 260
|
||||
|
||||
/* LFN 收集缓冲(栈/局部变量分配) */
|
||||
typedef struct {
|
||||
u16 chars[FAT32_LFN_MAX]; /* UTF-16 LE 累积字符(正序) */
|
||||
int count; /* 有效字符数(不含 0x0000 终止符/0xFFFF 填充) */
|
||||
u8 checksum; /* LFN 链 checksum(来自 0x40 标志项) */
|
||||
int valid; /* 1=LFN 链完整且 checksum 与短名项匹配 */
|
||||
} fat32_lfn_buf;
|
||||
|
||||
/* 初始化/清空 LFN 缓冲 */
|
||||
static inline void fat32_lfn_init(fat32_lfn_buf *buf) {
|
||||
buf->count = 0;
|
||||
buf->valid = 0;
|
||||
buf->checksum = 0;
|
||||
for (int i = 0; i < FAT32_LFN_MAX; i++) buf->chars[i] = 0;
|
||||
}
|
||||
|
||||
/* 计算 8.3 短名(11 字节)的 LFN checksum。
|
||||
* 标准 FAT32 算法:cksum = ((cksum & 1) << 7) + (cksum >> 1) + name[i] */
|
||||
static inline u8 fat32_lfn_checksum(const u8 name11[11]) {
|
||||
u8 cksum = 0;
|
||||
for (int i = 0; i < 11; i++) {
|
||||
cksum = (u8)(((cksum & 1) << 7) + (cksum >> 1) + name11[i]);
|
||||
}
|
||||
return cksum;
|
||||
}
|
||||
|
||||
/* 处理一个 32 字节目录项。自动判断 LFN 项 / 短名项 / 已删除项。
|
||||
*
|
||||
* 调用方对目录遍历中的每个条目(跳过 name[0]==0 结束符后)调用此函数。
|
||||
* - LFN 项(attr==0x0F):收集 13 个 UTF-16 字符到 buf,返回 0
|
||||
* - 已删除项(name[0]==0xE5):清空 buf,返回 0
|
||||
* - 短名项:校验 checksum,设 buf->valid,返回 1
|
||||
*
|
||||
* 返回 1=刚处理的是短名项(此时可检查 buf->valid 判断是否有有效 LFN),
|
||||
* 0=LFN 项或已删除项。
|
||||
*
|
||||
* 注意:遇到带 0x40 标志的 LFN 项(链开始)时自动重置 buf。
|
||||
* 遇到已删除项时自动清空 buf(防止残留 LFN 污染下一个文件)。 */
|
||||
static inline int fat32_lfn_process(fat32_lfn_buf *buf, const u8 *entry) {
|
||||
u8 attr = entry[11];
|
||||
|
||||
/* 已删除项:清空 LFN 缓冲 */
|
||||
if (entry[0] == 0xE5) {
|
||||
fat32_lfn_init(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LFN 项(attr == 0x0F) */
|
||||
if (attr == 0x0F) {
|
||||
u8 seq = entry[0];
|
||||
u8 ord = (u8)(seq & 0x3F); /* 序号 1-20 */
|
||||
if (ord == 0 || ord > 20) {
|
||||
fat32_lfn_init(buf);
|
||||
return 0;
|
||||
}
|
||||
/* 带 0x40 标志 = LFN 链开始(最高序号项),重置缓冲 */
|
||||
if (seq & 0x40) {
|
||||
int prev_cksum = buf->checksum;
|
||||
fat32_lfn_init(buf);
|
||||
buf->checksum = entry[13];
|
||||
(void)prev_cksum;
|
||||
}
|
||||
/* 提取 13 个 UTF-16 LE 字符,放到 (ord-1)*13 位置(正序) */
|
||||
int base = (ord - 1) * 13;
|
||||
if (base + 13 > FAT32_LFN_MAX) {
|
||||
fat32_lfn_init(buf);
|
||||
return 0;
|
||||
}
|
||||
/* name1: entry[1..10] = 5 个 UTF-16 LE 字符 */
|
||||
for (int i = 0; i < 5; i++) {
|
||||
u16 c = (u16)entry[1 + i * 2] | ((u16)entry[2 + i * 2] << 8);
|
||||
buf->chars[base + i] = c;
|
||||
}
|
||||
/* name2: entry[14..25] = 6 个 UTF-16 LE 字符 */
|
||||
for (int i = 0; i < 6; i++) {
|
||||
u16 c = (u16)entry[14 + i * 2] | ((u16)entry[15 + i * 2] << 8);
|
||||
buf->chars[base + 5 + i] = c;
|
||||
}
|
||||
/* name3: entry[28..31] = 2 个 UTF-16 LE 字符 */
|
||||
for (int i = 0; i < 2; i++) {
|
||||
u16 c = (u16)entry[28 + i * 2] | ((u16)entry[29 + i * 2] << 8);
|
||||
buf->chars[base + 11 + i] = c;
|
||||
}
|
||||
return 0; /* LFN 项,还不是短名项 */
|
||||
}
|
||||
|
||||
/* 短名项:校验 checksum */
|
||||
u8 cksum = fat32_lfn_checksum(entry);
|
||||
if (buf->checksum != 0 && buf->checksum == cksum) {
|
||||
/* LFN 链有效——计算实际字符数(截断于 0x0000 终止符或 0xFFFF 填充符) */
|
||||
int n = 0;
|
||||
for (int i = 0; i < FAT32_LFN_MAX; i++) {
|
||||
if (buf->chars[i] == 0x0000 || buf->chars[i] == 0xFFFF) break;
|
||||
n++;
|
||||
}
|
||||
buf->count = n;
|
||||
buf->valid = 1;
|
||||
} else {
|
||||
/* checksum 不匹配或无 LFN 链:清空,回退 8.3 */
|
||||
fat32_lfn_init(buf);
|
||||
}
|
||||
return 1; /* 短名项处理完毕 */
|
||||
}
|
||||
|
||||
/* 从 LFN 缓冲提取 ASCII 名(UTF-16→ASCII 缩窄,仅 BMP U+0000..U+007F)。
|
||||
* out_cap 为 out 缓冲容量(含 NUL)。
|
||||
* 返回字符串长度(不含 NUL),-1=无有效 LFN 或含非 ASCII 字符。 */
|
||||
static inline int fat32_lfn_to_ascii(const fat32_lfn_buf *buf, char *out, int out_cap) {
|
||||
if (!buf->valid || buf->count == 0 || out_cap <= 0) return -1;
|
||||
int n = buf->count;
|
||||
if (n >= out_cap) n = out_cap - 1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
u16 c = buf->chars[i];
|
||||
if (c > 0x7F) return -1; /* 含非 ASCII 字符 */
|
||||
out[i] = (char)(u8)c;
|
||||
}
|
||||
out[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ASCII 大小写不敏感字符串比较 */
|
||||
static inline int fat32_lfn_streq_ci(const char *a, const char *b) {
|
||||
if (!a || !b) return 0;
|
||||
while (*a && *b) {
|
||||
char ca = *a, cb = *b;
|
||||
if (ca >= 'a' && ca <= 'z') ca = (char)(ca - 32);
|
||||
if (cb >= 'a' && cb <= 'z') cb = (char)(cb - 32);
|
||||
if (ca != cb) return 0;
|
||||
a++; b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
/* 判断 ASCII 字符串是否以指定后缀结尾(大小写不敏感)。
|
||||
* 例如 fat32_lfn_ends_with_ci("libtest.so", ".so") → 1 */
|
||||
static inline int fat32_lfn_ends_with_ci(const char *s, const char *suffix) {
|
||||
if (!s || !suffix) return 0;
|
||||
int sl = 0, xl = 0;
|
||||
while (s[sl]) sl++;
|
||||
while (suffix[xl]) xl++;
|
||||
if (xl > sl || xl == 0) return 0;
|
||||
for (int i = 0; i < xl; i++) {
|
||||
char a = s[sl - xl + i];
|
||||
char b = suffix[i];
|
||||
if (a >= 'a' && a <= 'z') a = (char)(a - 32);
|
||||
if (b >= 'a' && b <= 'z') b = (char)(b - 32);
|
||||
if (a != b) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 匹配目录项:优先匹配 LFN 长名(大小写不敏感),回退 8.3 短名。
|
||||
* buf : LFN 收集缓冲(短名项处理后)
|
||||
* name11 : 短名项的 11 字节 8.3 名
|
||||
* target_long: 目标长名(ASCII,可为 NULL=不匹配长名)
|
||||
* target83 : 目标 8.3 名(11 字节,可为 NULL=不匹配 8.3)
|
||||
* 返回 1=匹配,0=不匹配。
|
||||
* FAT32 规范:文件名查找大小写不敏感。 */
|
||||
static inline int fat32_lfn_match(const fat32_lfn_buf *buf, const u8 name11[11],
|
||||
const char *target_long, const char *target83) {
|
||||
/* 1. 优先匹配 LFN 长名 */
|
||||
if (buf->valid && buf->count > 0 && target_long) {
|
||||
char ascii[FAT32_LFN_MAX];
|
||||
int n = fat32_lfn_to_ascii(buf, ascii, sizeof(ascii));
|
||||
if (n >= 0 && fat32_lfn_streq_ci(ascii, target_long)) return 1;
|
||||
}
|
||||
/* 2. 回退 8.3 短名匹配(精确比较 11 字节) */
|
||||
if (target83 && name11) {
|
||||
for (int i = 0; i < 11; i++) {
|
||||
if (name11[i] != (u8)target83[i]) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 从 8.3 短名(11 字节)生成可显示 ASCII 字符串(如 "README.TXT")。
|
||||
* 与 fat32_io.h 的 f32_name_from_83 功能相同,独立提供以避免循环依赖。 */
|
||||
static inline void fat32_lfn_short_to_str(const u8 name11[11], char out[13]) {
|
||||
int p = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (name11[j] == ' ') break;
|
||||
out[p++] = (char)name11[j];
|
||||
}
|
||||
if (name11[8] != ' ') {
|
||||
out[p++] = '.';
|
||||
for (int j = 8; j < 11; j++) {
|
||||
if (name11[j] == ' ') break;
|
||||
out[p++] = (char)name11[j];
|
||||
}
|
||||
}
|
||||
out[p] = 0;
|
||||
}
|
||||
|
||||
#endif /* DESHAB_FAT32_LFN_H */
|
||||
Reference in New Issue
Block a user