From 849dfb463d5788f483668d2abe886e4a2b628172 Mon Sep 17 00:00:00 2001 From: longpanda Date: Sat, 8 May 2021 14:22:37 +0800 Subject: [PATCH] 1. Change the UTF-16 languages.ini to UTF-8 languages.json 2. The update button is available even if data corrupted in VTOYEFI partition. 3. Set the default focus to No when you click Install button in Ventoy2Disk.exe. 4. Fix a BUG when booting Windows VHD(x) with the latest ventoy_vhdboot.img. 5. Support boot Windows VHD(x) files in local disk. --- .../grub-2.04/grub-core/Makefile.core.def | 1 + .../grub-2.04/grub-core/ventoy/ventoy.c | 4464 +--------------- .../grub-2.04/grub-core/ventoy/ventoy_cmd.c | 4497 +++++++++++++++++ .../grub-2.04/grub-core/ventoy/ventoy_def.h | 16 +- .../grub-2.04/grub-core/ventoy/ventoy_unix.c | 2 +- .../grub-2.04/grub-core/ventoy/ventoy_vhd.c | 179 +- INSTALL/Ventoy2Disk.exe | Bin 324096 -> 327168 bytes INSTALL/grub/grub.cfg | 46 +- INSTALL/tool/VentoyWorker.sh | 14 +- INSTALL/ventoy_pack.sh | 2 +- LANGUAGES/README | 17 + LANGUAGES/check.sh | 17 + LANGUAGES/languages.ini | Bin 178284 -> 0 bytes LANGUAGES/languages.json | 1741 +++++++ LinuxGUI/language.sh | 36 +- Ventoy2Disk/Ventoy2Disk/Language.h | 8 +- Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c | 6 + Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h | 2 +- Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj | 2 + .../Ventoy2Disk/Ventoy2Disk.vcxproj.filters | 6 + Ventoy2Disk/Ventoy2Disk/VentoyJson.c | 771 +++ Ventoy2Disk/Ventoy2Disk/VentoyJson.h | 164 + Ventoy2Disk/Ventoy2Disk/WinDialog.c | Bin 64752 -> 70408 bytes VtoyTool/vtoydump.c | 38 +- VtoyTool/vtoytool/00/vtoytool_32 | Bin 65772 -> 65772 bytes VtoyTool/vtoytool/00/vtoytool_64 | Bin 65456 -> 65456 bytes VtoyTool/vtoytool/00/vtoytool_aa64 | Bin 153824 -> 153824 bytes VtoyTool/vtoytool/00/vtoytool_m64e | Bin 155376 -> 155632 bytes 28 files changed, 7497 insertions(+), 4532 deletions(-) create mode 100644 GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c create mode 100644 LANGUAGES/README create mode 100644 LANGUAGES/check.sh delete mode 100644 LANGUAGES/languages.ini create mode 100644 LANGUAGES/languages.json create mode 100644 Ventoy2Disk/Ventoy2Disk/VentoyJson.c create mode 100644 Ventoy2Disk/Ventoy2Disk/VentoyJson.h diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def b/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def index 9ffa5f81..f7351891 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def @@ -1603,6 +1603,7 @@ module = { module = { name = ventoy; common = ventoy/ventoy.c; + common = ventoy/ventoy_cmd.c; common = ventoy/ventoy_linux.c; common = ventoy/ventoy_unix.c; common = ventoy/ventoy_windows.c; diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c index e10985e8..14dae6e0 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c @@ -31,113 +31,18 @@ #include #include #include -#include #include #include #include -#ifdef GRUB_MACHINE_EFI -#include -#include -#endif #include -#include -#include -#include -#include -#include #include #include "ventoy_def.h" -#include "miniz.h" GRUB_MOD_LICENSE ("GPLv3+"); int g_ventoy_debug = 0; static int g_efi_os = 0xFF; -initrd_info *g_initrd_img_list = NULL; -initrd_info *g_initrd_img_tail = NULL; -int g_initrd_img_count = 0; -int g_valid_initrd_count = 0; -int g_default_menu_mode = 0; -int g_filt_dot_underscore_file = 0; -int g_sort_case_sensitive = 0; -int g_tree_view_menu_style = 0; -static grub_file_t g_old_file; -static int g_ventoy_last_entry_back; -static grub_uint32_t g_ventoy_plat_data; - -char g_iso_path[256]; -char g_img_swap_tmp_buf[1024]; -img_info g_img_swap_tmp; -img_info *g_ventoy_img_list = NULL; - -int g_ventoy_img_count = 0; - -grub_device_t g_enum_dev = NULL; -grub_fs_t g_enum_fs = NULL; -int g_img_max_search_level = -1; -img_iterator_node g_img_iterator_head; -img_iterator_node *g_img_iterator_tail = NULL; - -grub_uint8_t g_ventoy_break_level = 0; -grub_uint8_t g_ventoy_debug_level = 0; -grub_uint8_t g_ventoy_chain_type = 0; - -grub_uint8_t *g_ventoy_cpio_buf = NULL; -grub_uint32_t g_ventoy_cpio_size = 0; -cpio_newc_header *g_ventoy_initrd_head = NULL; -grub_uint8_t *g_ventoy_runtime_buf = NULL; - -int g_plugin_image_list = 0; - -ventoy_grub_param *g_grub_param = NULL; - -ventoy_guid g_ventoy_guid = VENTOY_GUID; - -ventoy_img_chunk_list g_img_chunk_list; - -int g_wimboot_enable = 0; -ventoy_img_chunk_list g_wimiso_chunk_list; -char *g_wimiso_path = NULL; - -int g_vhdboot_enable = 0; - -grub_uint64_t g_conf_replace_offset = 0; -grub_uint64_t g_svd_replace_offset = 0; -conf_replace *g_conf_replace_node = NULL; -grub_uint8_t *g_conf_replace_new_buf = NULL; -int g_conf_replace_new_len = 0; -int g_conf_replace_new_len_align = 0; - -ventoy_gpt_info *g_ventoy_part_info = NULL; -grub_uint64_t g_ventoy_disk_size = 0; - -static char *g_tree_script_buf = NULL; -static int g_tree_script_pos = 0; - -static char *g_list_script_buf = NULL; -static int g_list_script_pos = 0; - -static char *g_part_list_buf = NULL; -static int g_part_list_pos = 0; - -static int g_video_mode_max = 0; -static int g_video_mode_num = 0; -static ventoy_video_mode *g_video_mode_list = NULL; - -static int g_enumerate_time_checked = 0; -static grub_uint64_t g_enumerate_start_time_ms; -static grub_uint64_t g_enumerate_finish_time_ms; -static int g_vtoy_file_flt[VTOY_FILE_FLT_BUTT] = {0}; - -static const char *g_menu_class[] = -{ - "vtoyiso", "vtoywim", "vtoyefi", "vtoyimg", "vtoyvhd", "vtoyvtoy" -}; - -static const char *g_menu_prefix[] = -{ - "iso", "wim", "efi", "img", "vhd", "vtoy" -}; +grub_uint32_t g_ventoy_plat_data; void ventoy_debug(const char *fmt, ...) { @@ -179,7 +84,6 @@ int ventoy_strncmp (const char *pattern, const char *str, grub_size_t n) return (int)(grub_uint8_t)*pattern - (int)(grub_uint8_t)*str; } - void ventoy_debug_dump_guid(const char *prefix, grub_uint8_t *guid) { int i; @@ -207,4366 +111,46 @@ int ventoy_is_efi_os(void) return g_efi_os; } -static int ventoy_get_fs_type(const char *fs) -{ - if (NULL == fs) - { - return ventoy_fs_max; - } - else if (grub_strncmp(fs, "exfat", 5) == 0) - { - return ventoy_fs_exfat; - } - else if (grub_strncmp(fs, "ntfs", 4) == 0) - { - return ventoy_fs_ntfs; - } - else if (grub_strncmp(fs, "ext", 3) == 0) - { - return ventoy_fs_ext; - } - else if (grub_strncmp(fs, "xfs", 3) == 0) - { - return ventoy_fs_xfs; - } - else if (grub_strncmp(fs, "udf", 3) == 0) - { - return ventoy_fs_udf; - } - else if (grub_strncmp(fs, "fat", 3) == 0) - { - return ventoy_fs_fat; - } - - return ventoy_fs_max; -} - -static int ventoy_string_check(const char *str, grub_char_check_func check) -{ - if (!str) - { - return 0; - } - - for ( ; *str; str++) - { - if (!check(*str)) - { - return 0; - } - } - - return 1; -} - - -static grub_ssize_t ventoy_fs_read(grub_file_t file, char *buf, grub_size_t len) -{ - grub_memcpy(buf, (char *)file->data + file->offset, len); - return len; -} - -static int ventoy_control_get_flag(const char *key) -{ - const char *val = ventoy_get_env(key); - - if (val && val[0] == '1' && val[1] == 0) - { - return 1; - } - return 0; -} - -static grub_err_t ventoy_fs_close(grub_file_t file) -{ - grub_file_close(g_old_file); - grub_free(file->data); - - file->device = 0; - file->name = 0; - - return 0; -} - -static int ventoy_video_hook(const struct grub_video_mode_info *info, void *hook_arg) -{ - int i; - - (void)hook_arg; - - if (info->mode_type & GRUB_VIDEO_MODE_TYPE_PURE_TEXT) - { - return 0; - } - - for (i = 0; i < g_video_mode_num; i++) - { - if (g_video_mode_list[i].width == info->width && - g_video_mode_list[i].height == info->height && - g_video_mode_list[i].bpp == info->bpp) - { - return 0; - } - } - - g_video_mode_list[g_video_mode_num].width = info->width; - g_video_mode_list[g_video_mode_num].height = info->height; - g_video_mode_list[g_video_mode_num].bpp = info->bpp; - g_video_mode_num++; - - if (g_video_mode_num == g_video_mode_max) - { - g_video_mode_max *= 2; - g_video_mode_list = grub_realloc(g_video_mode_list, g_video_mode_max * sizeof(ventoy_video_mode)); - } - - return 0; -} - -static int ventoy_video_mode_cmp(ventoy_video_mode *v1, ventoy_video_mode *v2) -{ - if (v1->bpp == v2->bpp) - { - if (v1->width == v2->width) - { - if (v1->height == v2->height) - { - return 0; - } - else - { - return (v1->height < v2->height) ? -1 : 1; - } - } - else - { - return (v1->width < v2->width) ? -1 : 1; - } - } - else - { - return (v1->bpp < v2->bpp) ? -1 : 1; - } -} - -static int ventoy_enum_video_mode(void) -{ - int i, j; - grub_video_adapter_t adapter; - grub_video_driver_id_t id; - ventoy_video_mode mode; - - g_video_mode_num = 0; - g_video_mode_max = 1024; - g_video_mode_list = grub_malloc(sizeof(ventoy_video_mode) * g_video_mode_max); - if (!g_video_mode_list) - { - return 0; - } - - #ifdef GRUB_MACHINE_PCBIOS - grub_dl_load ("vbe"); - #endif - - id = grub_video_get_driver_id (); - - FOR_VIDEO_ADAPTERS (adapter) - { - if (!adapter->iterate || - (adapter->id != id && (id != GRUB_VIDEO_DRIVER_NONE || - adapter->init() != GRUB_ERR_NONE))) - { - continue; - } - - adapter->iterate(ventoy_video_hook, NULL); - - if (adapter->id != id) - { - adapter->fini(); - } - } - - /* sort video mode */ - for (i = 0; i < g_video_mode_num; i++) - for (j = i + 1; j < g_video_mode_num; j++) - { - if (ventoy_video_mode_cmp(g_video_mode_list + i, g_video_mode_list + j) < 0) - { - grub_memcpy(&mode, g_video_mode_list + i, sizeof(ventoy_video_mode)); - grub_memcpy(g_video_mode_list + i, g_video_mode_list + j, sizeof(ventoy_video_mode)); - grub_memcpy(g_video_mode_list + j, &mode, sizeof(ventoy_video_mode)); - } - } - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_file_t ventoy_wrapper_open(grub_file_t rawFile, enum grub_file_type type) -{ - int len; - grub_file_t file; - static struct grub_fs vtoy_fs = - { - .name = "vtoy", - .fs_dir = 0, - .fs_open = 0, - .fs_read = ventoy_fs_read, - .fs_close = ventoy_fs_close, - .fs_label = 0, - .next = 0 - }; - - if (type != 52) - { - return rawFile; - } - - file = (grub_file_t)grub_zalloc(sizeof (*file)); - if (!file) - { - return 0; - } - - file->data = grub_malloc(rawFile->size + 4096); - if (!file->data) - { - return 0; - } - - grub_file_read(rawFile, file->data, rawFile->size); - len = ventoy_fill_data(4096, (char *)file->data + rawFile->size); - - g_old_file = rawFile; - - file->size = rawFile->size + len; - file->device = rawFile->device; - file->fs = &vtoy_fs; - file->not_easily_seekable = 1; - - return file; -} - -static int ventoy_check_decimal_var(const char *name, long *value) +static int ventoy_arch_mode_init(void) { - const char *value_str = NULL; - - value_str = grub_env_get(name); - if (NULL == value_str) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s not found", name); - } - - if (!ventoy_is_decimal(value_str)) + #ifdef GRUB_MACHINE_EFI + if (grub_strcmp(GRUB_TARGET_CPU, "i386") == 0) { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s value '%s' is not an integer", name, value_str); + g_ventoy_plat_data = VTOY_PLAT_I386_UEFI; + grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "ia32"); } - - *value = grub_strtol(value_str, NULL, 10); - - return GRUB_ERR_NONE; -} - -static grub_err_t ventoy_cmd_debug(grub_extcmd_context_t ctxt, int argc, char **args) -{ - if (argc != 1) + else if (grub_strcmp(GRUB_TARGET_CPU, "arm64") == 0) { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {on|off}", cmd_raw_name); + g_ventoy_plat_data = VTOY_PLAT_ARM64_UEFI; + grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "aa64"); } - - if (0 == grub_strcmp(args[0], "on")) + else if (grub_strcmp(GRUB_TARGET_CPU, "mips64el") == 0) { - g_ventoy_debug = 1; - grub_env_set("vtdebug_flag", "debug"); + g_ventoy_plat_data = VTOY_PLAT_MIPS_UEFI; + grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "mips"); } else { - g_ventoy_debug = 0; - grub_env_set("vtdebug_flag", ""); - } - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_break(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - - if (argc < 1 || (args[0][0] != '0' && args[0][0] != '1')) - { - grub_printf("Usage: %s {level} [debug]\r\n", cmd_raw_name); - grub_printf(" level:\r\n"); - grub_printf(" 01/11: busybox / (+cat log)\r\n"); - grub_printf(" 02/12: initrd / (+cat log)\r\n"); - grub_printf(" 03/13: hook / (+cat log)\r\n"); - grub_printf("\r\n"); - grub_printf(" debug:\r\n"); - grub_printf(" 0: debug is off\r\n"); - grub_printf(" 1: debug is on\r\n"); - grub_printf("\r\n"); - VENTOY_CMD_RETURN(GRUB_ERR_NONE); - } - - g_ventoy_break_level = (grub_uint8_t)grub_strtoul(args[0], NULL, 16); - - if (argc > 1 && grub_strtoul(args[1], NULL, 10) > 0) - { - g_ventoy_debug_level = 1; - } - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_strstr(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - - if (argc != 2) - { - return 1; - } - - return (grub_strstr(args[0], args[1])) ? 0 : 1; -} - -static grub_err_t ventoy_cmd_strbegin(grub_extcmd_context_t ctxt, int argc, char **args) -{ - char *c0, *c1; - - (void)ctxt; - - if (argc != 2) - { - return 1; - } - - c0 = args[0]; - c1 = args[1]; - - while (*c0 && *c1) - { - if (*c0 != *c1) - { - return 1; - } - c0++; - c1++; - } - - if (*c1) - { - return 1; + g_ventoy_plat_data = VTOY_PLAT_X86_64_UEFI; + grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "uefi"); } +#else + g_ventoy_plat_data = VTOY_PLAT_X86_LEGACY; + grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "legacy"); +#endif return 0; } -static grub_err_t ventoy_cmd_incr(grub_extcmd_context_t ctxt, int argc, char **args) -{ - long value_long = 0; - char buf[32]; - - if ((argc != 2) || (!ventoy_is_decimal(args[1]))) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Variable} {Int}", cmd_raw_name); - } - - if (GRUB_ERR_NONE != ventoy_check_decimal_var(args[0], &value_long)) - { - return grub_errno; - } - - value_long += grub_strtol(args[1], NULL, 10); - - grub_snprintf(buf, sizeof(buf), "%ld", value_long); - grub_env_set(args[0], buf); - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_mod(grub_extcmd_context_t ctxt, int argc, char **args) -{ - ulonglong value1 = 0; - ulonglong value2 = 0; - char buf[32]; - - if (argc != 3) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int} {Int} {Variable}", cmd_raw_name); - } - - value1 = grub_strtoull(args[0], NULL, 10); - value2 = grub_strtoull(args[1], NULL, 10); - - grub_snprintf(buf, sizeof(buf), "%llu", (value1 & (value2 - 1))); - grub_env_set(args[2], buf); - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_file_size(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc = 1; - char buf[32]; - grub_file_t file; - - (void)ctxt; - (void)argc; - (void)args; - - if (argc != 2) - { - return rc; - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (file == NULL) - { - debug("failed to open file <%s> for udf check\n", args[0]); - return 1; - } - - grub_snprintf(buf, sizeof(buf), "%llu", (unsigned long long)file->size); - - grub_env_set(args[1], buf); - - grub_file_close(file); - rc = 0; - - return rc; -} - -static grub_err_t ventoy_cmd_load_wimboot(grub_extcmd_context_t ctxt, int argc, char **args) +GRUB_MOD_INIT(ventoy) { - grub_file_t file; - - (void)ctxt; - (void)argc; - (void)args; - - g_wimboot_enable = 0; - grub_check_free(g_wimiso_path); - grub_check_free(g_wimiso_chunk_list.chunk); - - file = grub_file_open(args[0], VENTOY_FILE_TYPE); - if (!file) - { - return 0; - } - - grub_memset(&g_wimiso_chunk_list, 0, sizeof(g_wimiso_chunk_list)); - g_wimiso_chunk_list.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); - if (NULL == g_wimiso_chunk_list.chunk) - { - return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); - } - - g_wimiso_chunk_list.max_chunk = DEFAULT_CHUNK_NUM; - g_wimiso_chunk_list.cur_chunk = 0; - - ventoy_get_block_list(file, &g_wimiso_chunk_list, file->device->disk->partition->start); - - g_wimboot_enable = 1; - g_wimiso_path = grub_strdup(args[0]); - - grub_file_close(file); - - return 0; + ventoy_env_init(); + ventoy_arch_mode_init(); + ventoy_register_all_cmd(); } -static int ventoy_load_efiboot_template(char **buf, int *datalen, int *direntoff) +GRUB_MOD_FINI(ventoy) { - int len; - grub_file_t file; - char exec[128]; - char *data = NULL; - grub_uint32_t offset; - - file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s/ventoy/ventoy_efiboot.img.xz", ventoy_get_env("vtoy_efi_part")); - if (file == NULL) - { - debug("failed to open file <%s>\n", "ventoy_efiboot.img.xz"); - return 1; - } - - len = (int)file->size; - - data = (char *)grub_malloc(file->size); - if (!data) - { - return 1; - } - - grub_file_read(file, data, file->size); - grub_file_close(file); - - grub_snprintf(exec, sizeof(exec), "loopback efiboot mem:0x%llx:size:%d", (ulonglong)(ulong)data, len); - grub_script_execute_sourcecode(exec); - - file = grub_file_open("(efiboot)/EFI/BOOT/BOOTX64.EFI", GRUB_FILE_TYPE_LINUX_INITRD); - offset = (grub_uint32_t)grub_iso9660_get_last_file_dirent_pos(file); - grub_file_close(file); - - grub_script_execute_sourcecode("loopback -d efiboot"); - - *buf = data; - *datalen = len; - *direntoff = offset + 2; - - return 0; -} - -static grub_err_t ventoy_cmd_concat_efi_iso(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int len = 0; - int totlen = 0; - int offset = 0; - grub_file_t file; - char name[32]; - char value[32]; - char *buf = NULL; - char *data = NULL; - ventoy_iso9660_override *dirent; - - (void)ctxt; - - if (argc != 2) - { - return 1; - } - - totlen = sizeof(ventoy_chain_head); - - if (ventoy_load_efiboot_template(&buf, &len, &offset)) - { - debug("failed to load efiboot template %d\n", len); - return 1; - } - - totlen += len; - - debug("efiboot template len:%d offset:%d\n", len, offset); - - file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s", args[0]); - if (file == NULL) - { - debug("failed to open file <%s>\n", args[0]); - return 1; - } - - totlen += ventoy_align_2k(file->size); - - dirent = (ventoy_iso9660_override *)(buf + offset); - dirent->first_sector = len / 2048; - dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector); - dirent->size = (grub_uint32_t)file->size; - dirent->size_be = grub_swap_bytes32(dirent->size); - - debug("rawiso len:%d efilen:%d total:%d\n", len, (int)file->size, totlen); - -#ifdef GRUB_MACHINE_EFI - data = (char *)grub_efi_allocate_iso_buf(totlen); -#else - data = (char *)grub_malloc(totlen); -#endif - - ventoy_fill_os_param(file, (ventoy_os_param *)data); - - grub_memcpy(data + sizeof(ventoy_chain_head), buf, len); - grub_check_free(buf); - - grub_file_read(file, data + sizeof(ventoy_chain_head) + len, file->size); - grub_file_close(file); - - grub_snprintf(name, sizeof(name), "%s_addr", args[1]); - grub_snprintf(value, sizeof(value), "0x%llx", (ulonglong)(ulong)data); - grub_env_set(name, value); - - grub_snprintf(name, sizeof(name), "%s_size", args[1]); - grub_snprintf(value, sizeof(value), "%d", (int)(totlen)); - grub_env_set(name, value); - - return 0; -} - -static grub_err_t ventoy_cmd_load_file_to_mem(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc = 1; - char name[32]; - char value[32]; - char *buf = NULL; - grub_file_t file; - - (void)ctxt; - (void)argc; - (void)args; - - if (argc != 2) - { - return rc; - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (file == NULL) - { - debug("failed to open file <%s>\n", args[0]); - return 1; - } - -#ifdef GRUB_MACHINE_EFI - buf = (char *)grub_efi_allocate_iso_buf(file->size); -#else - buf = (char *)grub_malloc(file->size); -#endif - - grub_file_read(file, buf, file->size); - - grub_snprintf(name, sizeof(name), "%s_addr", args[1]); - grub_snprintf(value, sizeof(value), "0x%llx", (unsigned long long)(unsigned long)buf); - grub_env_set(name, value); - - grub_snprintf(name, sizeof(name), "%s_size", args[1]); - grub_snprintf(value, sizeof(value), "%llu", (unsigned long long)file->size); - grub_env_set(name, value); - - grub_file_close(file); - rc = 0; - - return rc; -} - -static grub_err_t ventoy_cmd_load_img_memdisk(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc = 1; - int headlen; - char name[32]; - char value[32]; - char *buf = NULL; - grub_file_t file; - - (void)ctxt; - (void)argc; - (void)args; - - if (argc != 2) - { - return rc; - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (file == NULL) - { - debug("failed to open file <%s> for udf check\n", args[0]); - return 1; - } - - headlen = sizeof(ventoy_chain_head); - -#ifdef GRUB_MACHINE_EFI - buf = (char *)grub_efi_allocate_iso_buf(headlen + file->size); -#else - buf = (char *)grub_malloc(headlen + file->size); -#endif - - ventoy_fill_os_param(file, (ventoy_os_param *)buf); - - grub_file_read(file, buf + headlen, file->size); - - grub_snprintf(name, sizeof(name), "%s_addr", args[1]); - grub_snprintf(value, sizeof(value), "0x%llx", (unsigned long long)(unsigned long)buf); - grub_env_set(name, value); - - grub_snprintf(name, sizeof(name), "%s_size", args[1]); - grub_snprintf(value, sizeof(value), "%llu", (unsigned long long)file->size); - grub_env_set(name, value); - - grub_file_close(file); - rc = 0; - - return rc; -} - -static grub_err_t ventoy_cmd_iso9660_nojoliet(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - - if (argc != 1) - { - return 1; - } - - if (args[0][0] == '1') - { - grub_iso9660_set_nojoliet(1); - } - else - { - grub_iso9660_set_nojoliet(0); - } - - return 0; -} - -static grub_err_t ventoy_cmd_is_udf(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i; - int rc = 1; - grub_file_t file; - grub_uint8_t buf[32]; - - (void)ctxt; - (void)argc; - (void)args; - - if (argc != 1) - { - return rc; - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (file == NULL) - { - debug("failed to open file <%s> for udf check\n", args[0]); - return 1; - } - - for (i = 16; i < 32; i++) - { - grub_file_seek(file, i * 2048); - grub_file_read(file, buf, sizeof(buf)); - if (buf[0] == 255) - { - break; - } - } - - i++; - grub_file_seek(file, i * 2048); - grub_file_read(file, buf, sizeof(buf)); - - if (grub_memcmp(buf + 1, "BEA01", 5) == 0) - { - i++; - grub_file_seek(file, i * 2048); - grub_file_read(file, buf, sizeof(buf)); - - if (grub_memcmp(buf + 1, "NSR02", 5) == 0 || - grub_memcmp(buf + 1, "NSR03", 5) == 0) - { - rc = 0; - } - } - - grub_file_close(file); - - debug("ISO UDF: %s\n", rc ? "NO" : "YES"); - - return rc; -} - -static grub_err_t ventoy_cmd_cmp(grub_extcmd_context_t ctxt, int argc, char **args) -{ - long value_long1 = 0; - long value_long2 = 0; - - if ((argc != 3) || (!ventoy_is_decimal(args[0])) || (!ventoy_is_decimal(args[2]))) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq|ne|gt|lt|ge|le } {Int2}", cmd_raw_name); - } - - value_long1 = grub_strtol(args[0], NULL, 10); - value_long2 = grub_strtol(args[2], NULL, 10); - - if (0 == grub_strcmp(args[1], "eq")) - { - grub_errno = (value_long1 == value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; - } - else if (0 == grub_strcmp(args[1], "ne")) - { - grub_errno = (value_long1 != value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; - } - else if (0 == grub_strcmp(args[1], "gt")) - { - grub_errno = (value_long1 > value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; - } - else if (0 == grub_strcmp(args[1], "lt")) - { - grub_errno = (value_long1 < value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; - } - else if (0 == grub_strcmp(args[1], "ge")) - { - grub_errno = (value_long1 >= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; - } - else if (0 == grub_strcmp(args[1], "le")) - { - grub_errno = (value_long1 <= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; - } - else - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq ne gt lt ge le } {Int2}", cmd_raw_name); - } - - return grub_errno; -} - -static grub_err_t ventoy_cmd_device(grub_extcmd_context_t ctxt, int argc, char **args) -{ - char *pos = NULL; - char buf[128] = {0}; - - if (argc != 2) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s path var", cmd_raw_name); - } - - grub_strncpy(buf, (args[0][0] == '(') ? args[0] + 1 : args[0], sizeof(buf) - 1); - pos = grub_strstr(buf, ","); - if (pos) - { - *pos = 0; - } - - grub_env_set(args[1], buf); - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_check_compatible(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i; - char buf[256]; - grub_disk_t disk; - char *pos = NULL; - const char *files[] = { "ventoy.dat", "VENTOY.DAT" }; - - (void)ctxt; - - if (argc != 1) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s (loop)", cmd_raw_name); - } - - for (i = 0; i < (int)ARRAY_SIZE(files); i++) - { - grub_snprintf(buf, sizeof(buf) - 1, "[ -e \"%s/%s\" ]", args[0], files[i]); - if (0 == grub_script_execute_sourcecode(buf)) - { - debug("file %s exist, ventoy_compatible YES\n", buf); - grub_env_set("ventoy_compatible", "YES"); - VENTOY_CMD_RETURN(GRUB_ERR_NONE); - } - else - { - debug("file %s NOT exist\n", buf); - } - } - - grub_snprintf(buf, sizeof(buf) - 1, "%s", args[0][0] == '(' ? (args[0] + 1) : args[0]); - pos = grub_strstr(buf, ")"); - if (pos) - { - *pos = 0; - } - - disk = grub_disk_open(buf); - if (disk) - { - grub_disk_read(disk, 16 << 2, 0, 1024, g_img_swap_tmp_buf); - grub_disk_close(disk); - - g_img_swap_tmp_buf[703] = 0; - for (i = 318; i < 703; i++) - { - if (g_img_swap_tmp_buf[i] == 'V' && - 0 == grub_strncmp(g_img_swap_tmp_buf + i, VENTOY_COMPATIBLE_STR, VENTOY_COMPATIBLE_STR_LEN)) - { - debug("Ventoy compatible string exist at %d, ventoy_compatible YES\n", i); - grub_env_set("ventoy_compatible", "YES"); - VENTOY_CMD_RETURN(GRUB_ERR_NONE); - } - } - } - else - { - debug("failed to open disk <%s>\n", buf); - } - - grub_env_set("ventoy_compatible", "NO"); - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -int ventoy_cmp_img(img_info *img1, img_info *img2) -{ - char *s1, *s2; - int c1 = 0; - int c2 = 0; - - if (g_plugin_image_list == VENTOY_IMG_WHITE_LIST) - { - return (img1->plugin_list_index - img2->plugin_list_index); - } - - for (s1 = img1->name, s2 = img2->name; *s1 && *s2; s1++, s2++) - { - c1 = *s1; - c2 = *s2; - - if (0 == g_sort_case_sensitive) - { - if (grub_islower(c1)) - { - c1 = c1 - 'a' + 'A'; - } - - if (grub_islower(c2)) - { - c2 = c2 - 'a' + 'A'; - } - } - - if (c1 != c2) - { - break; - } - } - - return (c1 - c2); -} - -static int ventoy_cmp_subdir(img_iterator_node *node1, img_iterator_node *node2) -{ - char *s1, *s2; - int c1 = 0; - int c2 = 0; - - if (g_plugin_image_list == VENTOY_IMG_WHITE_LIST) - { - return (node1->plugin_list_index - node2->plugin_list_index); - } - - for (s1 = node1->dir, s2 = node2->dir; *s1 && *s2; s1++, s2++) - { - c1 = *s1; - c2 = *s2; - - if (0 == g_sort_case_sensitive) - { - if (grub_islower(c1)) - { - c1 = c1 - 'a' + 'A'; - } - - if (grub_islower(c2)) - { - c2 = c2 - 'a' + 'A'; - } - } - - if (c1 != c2) - { - break; - } - } - - return (c1 - c2); -} - -void ventoy_swap_img(img_info *img1, img_info *img2) -{ - grub_memcpy(&g_img_swap_tmp, img1, sizeof(img_info)); - - grub_memcpy(img1, img2, sizeof(img_info)); - img1->next = g_img_swap_tmp.next; - img1->prev = g_img_swap_tmp.prev; - - g_img_swap_tmp.next = img2->next; - g_img_swap_tmp.prev = img2->prev; - grub_memcpy(img2, &g_img_swap_tmp, sizeof(img_info)); -} - -static int ventoy_img_name_valid(const char *filename, grub_size_t namelen) -{ - (void)namelen; - - if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_') - { - return 0; - } - - return 1; -} - -static int ventoy_check_ignore_flag(const char *filename, const struct grub_dirhook_info *info, void *data) -{ - if (0 == info->dir) - { - if (filename && filename[0] == '.' && 0 == grub_strncmp(filename, ".ventoyignore", 13)) - { - *((int *)data) = 1; - return 0; - } - } - - return 0; -} - -static int ventoy_collect_img_files(const char *filename, const struct grub_dirhook_info *info, void *data) -{ - //int i = 0; - int type = 0; - int ignore = 0; - int index = 0; - grub_size_t len; - img_info *img; - img_info *tail; - img_iterator_node *tmp; - img_iterator_node *new_node; - img_iterator_node *node = (img_iterator_node *)data; - - if (g_enumerate_time_checked == 0) - { - g_enumerate_finish_time_ms = grub_get_time_ms(); - if ((g_enumerate_finish_time_ms - g_enumerate_start_time_ms) >= 3000) - { - grub_cls(); - grub_printf("\n\n Ventoy scanning files, please wait...\n"); - grub_refresh(); - g_enumerate_time_checked = 1; - } - } - - len = grub_strlen(filename); - - if (info->dir) - { - if (node->level + 1 > g_img_max_search_level) - { - return 0; - } - - if ((len == 1 && filename[0] == '.') || - (len == 2 && filename[0] == '.' && filename[1] == '.')) - { - return 0; - } - - if (!ventoy_img_name_valid(filename, len)) - { - return 0; - } - - if (filename[0] == '$' && 0 == grub_strncmp(filename, "$RECYCLE.BIN", 12)) - { - return 0; - } - - if (g_plugin_image_list == VENTOY_IMG_WHITE_LIST) - { - grub_snprintf(g_img_swap_tmp_buf, sizeof(g_img_swap_tmp_buf), "%s%s/", node->dir, filename); - index = ventoy_plugin_get_image_list_index(vtoy_class_directory, g_img_swap_tmp_buf); - if (index == 0) - { - debug("Directory %s not found in image_list plugin config...\n", g_img_swap_tmp_buf); - return 0; - } - } - - new_node = grub_zalloc(sizeof(img_iterator_node)); - if (new_node) - { - new_node->level = node->level + 1; - new_node->plugin_list_index = index; - new_node->dirlen = grub_snprintf(new_node->dir, sizeof(new_node->dir), "%s%s/", node->dir, filename); - - g_enum_fs->fs_dir(g_enum_dev, new_node->dir, ventoy_check_ignore_flag, &ignore); - if (ignore) - { - debug("Directory %s ignored...\n", new_node->dir); - grub_free(new_node); - return 0; - } - - new_node->tail = node->tail; - - new_node->parent = node; - if (!node->firstchild) - { - node->firstchild = new_node; - } - - if (g_img_iterator_tail) - { - g_img_iterator_tail->next = new_node; - g_img_iterator_tail = new_node; - } - else - { - g_img_iterator_head.next = new_node; - g_img_iterator_tail = new_node; - } - } - } - else - { - debug("Find a file %s\n", filename); - if (len < 4) - { - return 0; - } - - if (FILE_FLT(ISO) && 0 == grub_strcasecmp(filename + len - 4, ".iso")) - { - type = img_type_iso; - } - else if (FILE_FLT(WIM) && g_wimboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".wim"))) - { - type = img_type_wim; - } - else if (FILE_FLT(VHD) && g_vhdboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".vhd") || - (len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vhdx")))) - { - type = img_type_vhd; - } - #ifdef GRUB_MACHINE_EFI - else if (FILE_FLT(EFI) && 0 == grub_strcasecmp(filename + len - 4, ".efi")) - { - type = img_type_efi; - } - #endif - else if (FILE_FLT(IMG) && 0 == grub_strcasecmp(filename + len - 4, ".img")) - { - if (len == 18 && grub_strncmp(filename, "ventoy_", 7) == 0) - { - if (grub_strncmp(filename + 7, "wimboot", 7) == 0 || - grub_strncmp(filename + 7, "vhdboot", 7) == 0) - { - return 0; - } - } - type = img_type_img; - } - else if (FILE_FLT(VTOY) && len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vtoy")) - { - type = img_type_vtoy; - } - else if (len >= 9 && 0 == grub_strcasecmp(filename + len - 5, ".vcfg")) - { - if (filename[len - 9] == '.' || (len >= 10 && filename[len - 10] == '.')) - { - grub_snprintf(g_img_swap_tmp_buf, sizeof(g_img_swap_tmp_buf), "%s%s", node->dir, filename); - ventoy_plugin_add_custom_boot(g_img_swap_tmp_buf); - } - return 0; - } - else - { - return 0; - } - - if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_') - { - return 0; - } - - if (g_plugin_image_list) - { - grub_snprintf(g_img_swap_tmp_buf, sizeof(g_img_swap_tmp_buf), "%s%s", node->dir, filename); - index = ventoy_plugin_get_image_list_index(vtoy_class_image_file, g_img_swap_tmp_buf); - if (VENTOY_IMG_WHITE_LIST == g_plugin_image_list && index == 0) - { - debug("File %s not found in image_list plugin config...\n", g_img_swap_tmp_buf); - return 0; - } - else if (VENTOY_IMG_BLACK_LIST == g_plugin_image_list && index > 0) - { - debug("File %s found in image_blacklist plugin config %d ...\n", g_img_swap_tmp_buf, index); - return 0; - } - } - - img = grub_zalloc(sizeof(img_info)); - if (img) - { - img->type = type; - img->plugin_list_index = index; - grub_snprintf(img->name, sizeof(img->name), "%s", filename); - - img->pathlen = grub_snprintf(img->path, sizeof(img->path), "%s%s", node->dir, img->name); - - img->size = info->size; - if (0 == img->size) - { - img->size = ventoy_grub_get_file_size("%s/%s%s", g_iso_path, node->dir, filename); - } - - if (img->size < VTOY_FILT_MIN_FILE_SIZE) - { - debug("img <%s> size too small %llu\n", img->name, (ulonglong)img->size); - grub_free(img); - return 0; - } - - if (g_ventoy_img_list) - { - tail = *(node->tail); - img->prev = tail; - tail->next = img; - } - else - { - g_ventoy_img_list = img; - } - - img->id = g_ventoy_img_count; - img->parent = node; - if (node && NULL == node->firstiso) - { - node->firstiso = img; - } - - node->isocnt++; - tmp = node->parent; - while (tmp) - { - tmp->isocnt++; - tmp = tmp->parent; - } - - *((img_info **)(node->tail)) = img; - g_ventoy_img_count++; - - img->alias = ventoy_plugin_get_menu_alias(vtoy_alias_image_file, img->path); - img->class = ventoy_plugin_get_menu_class(vtoy_class_image_file, img->name, img->path); - if (!img->class) - { - img->class = g_menu_class[type]; - } - img->menu_prefix = g_menu_prefix[type]; - - if (img_type_iso == type) - { - if (ventoy_plugin_check_memdisk(img->path)) - { - img->menu_prefix = "miso"; - } - } - - debug("Add %s%s to list %d\n", node->dir, filename, g_ventoy_img_count); - } - } - - return 0; -} - -static int ventoy_arch_mode_init(void) -{ - #ifdef GRUB_MACHINE_EFI - if (grub_strcmp(GRUB_TARGET_CPU, "i386") == 0) - { - g_ventoy_plat_data = VTOY_PLAT_I386_UEFI; - grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "ia32"); - } - else if (grub_strcmp(GRUB_TARGET_CPU, "arm64") == 0) - { - g_ventoy_plat_data = VTOY_PLAT_ARM64_UEFI; - grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "aa64"); - } - else if (grub_strcmp(GRUB_TARGET_CPU, "mips64el") == 0) - { - g_ventoy_plat_data = VTOY_PLAT_MIPS_UEFI; - grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "mips"); - } - else - { - g_ventoy_plat_data = VTOY_PLAT_X86_64_UEFI; - grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "uefi"); - } -#else - g_ventoy_plat_data = VTOY_PLAT_X86_LEGACY; - grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "legacy"); -#endif - - return 0; -} - -int ventoy_fill_data(grub_uint32_t buflen, char *buffer) -{ - int len = GRUB_UINT_MAX; - const char *value = NULL; - char name[32] = {0}; - char plat[32] = {0}; - char guidstr[32] = {0}; - ventoy_guid guid = VENTOY_GUID; - const char *fmt1 = NULL; - const char *fmt2 = NULL; - const char *fmt3 = NULL; - grub_uint32_t *puint = (grub_uint32_t *)name; - grub_uint32_t *puint2 = (grub_uint32_t *)plat; - const char fmtdata[]={ 0x39, 0x35, 0x25, 0x00, 0x35, 0x00, 0x23, 0x30, 0x30, 0x30, 0x30, 0x66, 0x66, 0x00 }; - const char fmtcode[]={ - 0x22, 0x0A, 0x2B, 0x20, 0x68, 0x62, 0x6F, 0x78, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x74, 0x6F, 0x70, - 0x20, 0x3D, 0x20, 0x25, 0x73, 0x0A, 0x20, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x3D, 0x20, 0x25, - 0x73, 0x0A, 0x20, 0x20, 0x2B, 0x20, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x7B, 0x74, 0x65, 0x78, - 0x74, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x20, 0x25, 0x73, 0x25, 0x73, 0x22, 0x20, 0x63, 0x6F, - 0x6C, 0x6F, 0x72, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x22, 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E, - 0x20, 0x3D, 0x20, 0x22, 0x6C, 0x65, 0x66, 0x74, 0x22, 0x7D, 0x0A, 0x7D, 0x0A, 0x22, 0x00 - }; - - grub_memset(name, 0, sizeof(name)); - puint[0] = grub_swap_bytes32(0x56454e54); - puint[3] = grub_swap_bytes32(0x4f4e0000); - puint[2] = grub_swap_bytes32(0x45525349); - puint[1] = grub_swap_bytes32(0x4f595f56); - value = ventoy_get_env(name); - - grub_memset(name, 0, sizeof(name)); - puint[1] = grub_swap_bytes32(0x5f544f50); - puint[0] = grub_swap_bytes32(0x56544c45); - fmt1 = ventoy_get_env(name); - if (!fmt1) - { - fmt1 = fmtdata; - } - - grub_memset(name, 0, sizeof(name)); - puint[1] = grub_swap_bytes32(0x5f4c4654); - puint[0] = grub_swap_bytes32(0x56544c45); - fmt2 = ventoy_get_env(name); - - grub_memset(name, 0, sizeof(name)); - puint[1] = grub_swap_bytes32(0x5f434c52); - puint[0] = grub_swap_bytes32(0x56544c45); - fmt3 = ventoy_get_env(name); - - grub_memcpy(guidstr, &guid, sizeof(guid)); - - puint2[0] = grub_swap_bytes32(g_ventoy_plat_data); - - /* Easter egg :) It will be appreciated if you reserve it, but NOT mandatory. */ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wformat-nonliteral" - len = grub_snprintf(buffer, buflen, fmtcode, - fmt1 ? fmt1 : fmtdata, - fmt2 ? fmt2 : fmtdata + 4, - value ? value : "", plat, guidstr, - fmt3 ? fmt3 : fmtdata + 6); - #pragma GCC diagnostic pop - - grub_memset(name, 0, sizeof(name)); - puint[0] = grub_swap_bytes32(0x76746f79); - puint[2] = grub_swap_bytes32(0x656e7365); - puint[1] = grub_swap_bytes32(0x5f6c6963); - ventoy_set_env(name, guidstr); - - return len; -} - -int ventoy_check_password(const vtoy_password *pwd, int retry) -{ - int offset; - char input[256]; - grub_uint8_t md5[16]; - - while (retry--) - { - grub_memset(input, 0, sizeof(input)); - - grub_printf("Enter password: "); - grub_refresh(); - - if (pwd->type == VTOY_PASSWORD_TXT) - { - grub_password_get(input, 128); - if (grub_strcmp(pwd->text, input) == 0) - { - return 0; - } - } - else if (pwd->type == VTOY_PASSWORD_MD5) - { - grub_password_get(input, 128); - grub_crypto_hash(GRUB_MD_MD5, md5, input, grub_strlen(input)); - if (grub_memcmp(pwd->md5, md5, 16) == 0) - { - return 0; - } - } - else if (pwd->type == VTOY_PASSWORD_SALT_MD5) - { - offset = (int)grub_snprintf(input, 128, "%s", pwd->salt); - grub_password_get(input + offset, 128); - - grub_crypto_hash(GRUB_MD_MD5, md5, input, grub_strlen(input)); - if (grub_memcmp(pwd->md5, md5, 16) == 0) - { - return 0; - } - } - - grub_printf("Invalid password!\n\n"); - grub_refresh(); - } - - return 1; -} - -static img_info * ventoy_get_min_iso(img_iterator_node *node) -{ - img_info *minimg = NULL; - img_info *img = (img_info *)(node->firstiso); - - while (img && (img_iterator_node *)(img->parent) == node) - { - if (img->select == 0 && (NULL == minimg || ventoy_cmp_img(img, minimg) < 0)) - { - minimg = img; - } - img = img->next; - } - - if (minimg) - { - minimg->select = 1; - } - - return minimg; -} - -static img_iterator_node * ventoy_get_min_child(img_iterator_node *node) -{ - img_iterator_node *Minchild = NULL; - img_iterator_node *child = node->firstchild; - - while (child && child->parent == node) - { - if (child->select == 0 && (NULL == Minchild || ventoy_cmp_subdir(child, Minchild) < 0)) - { - Minchild = child; - } - child = child->next; - } - - if (Minchild) - { - Minchild->select = 1; - } - - return Minchild; -} - -static int ventoy_dynamic_tree_menu(img_iterator_node *node) -{ - int offset = 1; - img_info *img = NULL; - const char *dir_class = NULL; - const char *dir_alias = NULL; - img_iterator_node *child = NULL; - - if (node->isocnt == 0 || node->done == 1) - { - return 0; - } - - if (node->parent && node->parent->dirlen < node->dirlen) - { - offset = node->parent->dirlen; - } - - if (node == &g_img_iterator_head) - { - if (g_default_menu_mode == 0) - { - if (g_tree_view_menu_style == 0) - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "menuentry \"%-10s [Return to ListView]\" --class=\"vtoyret\" VTOY_RET {\n " - " echo 'return ...' \n" - "}\n", "<--"); - } - else - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "menuentry \"[Return to ListView]\" --class=\"vtoyret\" VTOY_RET {\n " - " echo '%s ...' \n" - "}\n", "return"); - } - } - } - else - { - node->dir[node->dirlen - 1] = 0; - dir_class = ventoy_plugin_get_menu_class(vtoy_class_directory, node->dir, node->dir); - if (!dir_class) - { - dir_class = "vtoydir"; - } - - dir_alias = ventoy_plugin_get_menu_alias(vtoy_alias_directory, node->dir); - if (dir_alias) - { - if (g_tree_view_menu_style == 0) - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "submenu \"%-10s %s\" --class=\"%s\" --id=\"DIR_%s\" {\n", - "DIR", dir_alias, dir_class, node->dir + offset); - } - else - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "submenu \"%s\" --class=\"%s\" --id=\"DIR_%s\" {\n", - dir_alias, dir_class, node->dir + offset); - } - } - else - { - dir_alias = node->dir + offset; - - if (g_tree_view_menu_style == 0) - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "submenu \"%-10s [%s]\" --class=\"%s\" --id=\"DIR_%s\" {\n", - "DIR", dir_alias, dir_class, node->dir + offset); - } - else - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "submenu \"[%s]\" --class=\"%s\" --id=\"DIR_%s\" {\n", - dir_alias, dir_class, node->dir + offset); - } - } - - if (g_tree_view_menu_style == 0) - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n " - " echo 'return ...' \n" - "}\n", "<--"); - } - else - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n " - " echo '%s ...' \n" - "}\n", "return"); - } - } - - while ((child = ventoy_get_min_child(node)) != NULL) - { - ventoy_dynamic_tree_menu(child); - } - - while ((img = ventoy_get_min_iso(node)) != NULL) - { - if (g_tree_view_menu_style == 0) - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "menuentry \"%-10s %s%s\" --class=\"%s\" --id=\"VID_%d\" {\n" - " %s_%s \n" - "}\n", - grub_get_human_size(img->size, GRUB_HUMAN_SIZE_SHORT), - img->unsupport ? "[***********] " : "", - img->alias ? img->alias : img->name, img->class, img->id, - img->menu_prefix, - img->unsupport ? "unsupport_menuentry" : "common_menuentry"); - } - else - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, - "menuentry \"%s%s\" --class=\"%s\" --id=\"VID_%d\" {\n" - " %s_%s \n" - "}\n", - img->unsupport ? "[***********] " : "", - img->alias ? img->alias : img->name, img->class, img->id, - img->menu_prefix, - img->unsupport ? "unsupport_menuentry" : "common_menuentry"); - } - } - - if (node != &g_img_iterator_head) - { - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "%s", "}\n"); - } - - node->done = 1; - return 0; -} - -int ventoy_check_device_result(int ret) -{ - char buf[32]; - - grub_snprintf(buf, sizeof(buf), "%d", (ret & 0x7FFF)); - grub_env_set("VTOY_CHKDEV_RESULT_STRING", buf); - grub_env_export("VTOY_CHKDEV_RESULT_STRING"); - - if (ret) - { - grub_printf(VTOY_WARNING"\n"); - grub_printf(VTOY_WARNING"\n"); - grub_printf(VTOY_WARNING"\n\n\n"); - - grub_printf("This is NOT a standard Ventoy device and is NOT supported (0x%x).\n\n", ret); - grub_printf("You should follow the instructions in https://www.ventoy.net to use Ventoy.\n"); - - grub_printf("\n\nWill exit after 10 seconds ...... "); - grub_refresh(); - grub_sleep(10); - } - - return ret; -} - -int ventoy_check_device(grub_device_t dev) -{ - int workaround = 0; - grub_file_t file; - grub_uint64_t offset; - char devname[64]; - grub_fs_t fs; - grub_device_t dev2; - char *label = NULL; - struct grub_partition *partition; - - if (dev->disk == NULL || dev->disk->partition == NULL) - { - return ventoy_check_device_result(1 | 0x1000); - } - - if (0 == ventoy_check_file_exist("(%s,2)/ventoy/ventoy.cpio", dev->disk->name) || - 0 == ventoy_check_file_exist("(%s,2)/grub/localboot.cfg", dev->disk->name) || - 0 == ventoy_check_file_exist("(%s,2)/tool/mount.exfat-fuse_aarch64", dev->disk->name)) - { - #ifndef GRUB_MACHINE_EFI - if (0 == ventoy_check_file_exist("(ventoydisk)/ventoy/ventoy.cpio", dev->disk->name) || - 0 == ventoy_check_file_exist("(ventoydisk)/grub/localboot.cfg", dev->disk->name) || - 0 == ventoy_check_file_exist("(ventoydisk)/tool/mount.exfat-fuse_aarch64", dev->disk->name)) - { - return ventoy_check_device_result(2 | 0x1000); - } - else - { - workaround = 1; - } - #endif - } - - /* We must have partition 2 */ - if (workaround) - { - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", "(ventoydisk)/ventoy/ventoy.cpio"); - } - else - { - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(%s,2)/ventoy/ventoy.cpio", dev->disk->name); - } - if (!file) - { - return ventoy_check_device_result(3 | 0x1000); - } - - if (NULL == grub_strstr(file->fs->name, "fat")) - { - grub_file_close(file); - return ventoy_check_device_result(4 | 0x1000); - } - - partition = dev->disk->partition; - if (partition->number != 0 || partition->start != 2048) - { - return ventoy_check_device_result(5); - } - - if (workaround) - { - if (grub_strncmp(g_ventoy_part_info->Head.Signature, "EFI PART", 8) == 0) - { - ventoy_gpt_part_tbl *PartTbl = g_ventoy_part_info->PartTbl; - if (PartTbl[1].StartLBA != PartTbl[0].LastLBA + 1 || - (PartTbl[1].LastLBA + 1 - PartTbl[1].StartLBA) != 65536) - { - grub_file_close(file); - return ventoy_check_device_result(6); - } - } - else - { - ventoy_part_table *PartTbl = g_ventoy_part_info->MBR.PartTbl; - if (PartTbl[1].StartSectorId != PartTbl[0].StartSectorId + PartTbl[0].SectorCount || - PartTbl[1].SectorCount != 65536) - { - grub_file_close(file); - return ventoy_check_device_result(6); - } - } - } - else - { - offset = partition->start + partition->len; - partition = file->device->disk->partition; - if ((partition->number != 1) || (partition->len != 65536) || (offset != partition->start)) - { - grub_file_close(file); - return ventoy_check_device_result(7); - } - } - - grub_file_close(file); - - if (workaround == 0) - { - grub_snprintf(devname, sizeof(devname), "%s,2", dev->disk->name); - dev2 = grub_device_open(devname); - if (!dev2) - { - return ventoy_check_device_result(8); - } - - fs = grub_fs_probe(dev2); - if (!fs) - { - grub_device_close(dev2); - return ventoy_check_device_result(9); - } - - fs->fs_label(dev2, &label); - if ((!label) || grub_strncmp("VTOYEFI", label, 7)) - { - grub_device_close(dev2); - return ventoy_check_device_result(10); - } - - grub_device_close(dev2); - } - - return ventoy_check_device_result(0); -} - -static int ventoy_set_default_menu(void) -{ - int img_len = 0; - char *pos = NULL; - char *end = NULL; - char *def = NULL; - const char *strdata = NULL; - img_info *cur = NULL; - img_info *default_node = NULL; - const char *default_image = NULL; - - default_image = ventoy_get_env("VTOY_DEFAULT_IMAGE"); - if (default_image && default_image[0] == '/') - { - img_len = grub_strlen(default_image); - - for (cur = g_ventoy_img_list; cur; cur = cur->next) - { - if (img_len == cur->pathlen && grub_strcmp(default_image, cur->path) == 0) - { - default_node = cur; - break; - } - } - - if (!default_node) - { - return 1; - } - - if (0 == g_default_menu_mode) - { - vtoy_ssprintf(g_list_script_buf, g_list_script_pos, "set default='VID_%d'\n", default_node->id); - } - else - { - def = grub_strdup(default_image); - if (!def) - { - return 1; - } - - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "set default=%c", '\''); - - strdata = ventoy_get_env("VTOY_DEFAULT_SEARCH_ROOT"); - if (strdata && strdata[0] == '/') - { - pos = def + grub_strlen(strdata); - if (*pos == '/') - { - pos++; - } - } - else - { - pos = def + 1; - } - - while ((end = grub_strchr(pos, '/')) != NULL) - { - *end = 0; - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "DIR_%s>", pos); - pos = end + 1; - } - - vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "VID_%d'\n", default_node->id); - grub_free(def); - } - } - - return 0; -} - -static grub_err_t ventoy_cmd_list_img(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int len; - grub_fs_t fs; - grub_device_t dev = NULL; - img_info *cur = NULL; - img_info *tail = NULL; - const char *strdata = NULL; - char *device_name = NULL; - char buf[32]; - img_iterator_node *node = NULL; - img_iterator_node *tmp = NULL; - - (void)ctxt; - - if (argc != 2) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {device} {cntvar}", cmd_raw_name); - } - - if (g_ventoy_img_list || g_ventoy_img_count) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Must clear image before list"); - } - - g_enumerate_time_checked = 0; - g_enumerate_start_time_ms = grub_get_time_ms(); - - strdata = ventoy_get_env("VTOY_FILT_DOT_UNDERSCORE_FILE"); - if (strdata && strdata[0] == '1' && strdata[1] == 0) - { - g_filt_dot_underscore_file = 1; - } - - strdata = ventoy_get_env("VTOY_SORT_CASE_SENSITIVE"); - if (strdata && strdata[0] == '1' && strdata[1] == 0) - { - g_sort_case_sensitive = 1; - } - - device_name = grub_file_get_device_name(args[0]); - if (!device_name) - { - goto fail; - } - - g_enum_dev = dev = grub_device_open(device_name); - if (!dev) - { - goto fail; - } - - g_enum_fs = fs = grub_fs_probe(dev); - if (!fs) - { - goto fail; - } - - if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max) - { - debug("unsupported fs:<%s>\n", fs->name); - ventoy_set_env("VTOY_NO_ISO_TIP", "unsupported file system"); - goto fail; - } - - ventoy_set_env("vtoy_iso_fs", fs->name); - - strdata = ventoy_get_env("VTOY_DEFAULT_MENU_MODE"); - if (strdata && strdata[0] == '1') - { - g_default_menu_mode = 1; - } - - grub_memset(&g_img_iterator_head, 0, sizeof(g_img_iterator_head)); - - grub_snprintf(g_iso_path, sizeof(g_iso_path), "%s", args[0]); - - strdata = ventoy_get_env("VTOY_DEFAULT_SEARCH_ROOT"); - if (strdata && strdata[0] == '/') - { - len = grub_snprintf(g_img_iterator_head.dir, sizeof(g_img_iterator_head.dir) - 1, "%s", strdata); - if (g_img_iterator_head.dir[len - 1] != '/') - { - g_img_iterator_head.dir[len++] = '/'; - } - g_img_iterator_head.dirlen = len; - } - else - { - g_img_iterator_head.dirlen = 1; - grub_strcpy(g_img_iterator_head.dir, "/"); - } - - g_img_iterator_head.tail = &tail; - - if (g_img_max_search_level < 0) - { - g_img_max_search_level = GRUB_INT_MAX; - strdata = ventoy_get_env("VTOY_MAX_SEARCH_LEVEL"); - if (strdata && ventoy_is_decimal(strdata)) - { - g_img_max_search_level = (int)grub_strtoul(strdata, NULL, 10); - } - } - - g_vtoy_file_flt[VTOY_FILE_FLT_ISO] = ventoy_control_get_flag("VTOY_FILE_FLT_ISO"); - g_vtoy_file_flt[VTOY_FILE_FLT_WIM] = ventoy_control_get_flag("VTOY_FILE_FLT_WIM"); - g_vtoy_file_flt[VTOY_FILE_FLT_EFI] = ventoy_control_get_flag("VTOY_FILE_FLT_EFI"); - g_vtoy_file_flt[VTOY_FILE_FLT_IMG] = ventoy_control_get_flag("VTOY_FILE_FLT_IMG"); - g_vtoy_file_flt[VTOY_FILE_FLT_VHD] = ventoy_control_get_flag("VTOY_FILE_FLT_VHD"); - g_vtoy_file_flt[VTOY_FILE_FLT_VTOY] = ventoy_control_get_flag("VTOY_FILE_FLT_VTOY"); - - for (node = &g_img_iterator_head; node; node = node->next) - { - fs->fs_dir(dev, node->dir, ventoy_collect_img_files, node); - } - - strdata = ventoy_get_env("VTOY_TREE_VIEW_MENU_STYLE"); - if (strdata && strdata[0] == '1' && strdata[1] == 0) - { - g_tree_view_menu_style = 1; - } - - ventoy_set_default_menu(); - - for (node = &g_img_iterator_head; node; node = node->next) - { - ventoy_dynamic_tree_menu(node); - } - - /* free node */ - node = g_img_iterator_head.next; - while (node) - { - tmp = node->next; - grub_free(node); - node = tmp; - } - - /* sort image list by image name */ - for (cur = g_ventoy_img_list; cur; cur = cur->next) - { - for (tail = cur->next; tail; tail = tail->next) - { - if (ventoy_cmp_img(cur, tail) > 0) - { - ventoy_swap_img(cur, tail); - } - } - } - - if (g_default_menu_mode == 1) - { - vtoy_ssprintf(g_list_script_buf, g_list_script_pos, - "menuentry \"%s [Return to TreeView]\" --class=\"vtoyret\" VTOY_RET {\n " - " echo 'return ...' \n" - "}\n", "<--"); - } - - for (cur = g_ventoy_img_list; cur; cur = cur->next) - { - vtoy_ssprintf(g_list_script_buf, g_list_script_pos, - "menuentry \"%s%s\" --class=\"%s\" --id=\"VID_%d\" {\n" - " %s_%s \n" - "}\n", - cur->unsupport ? "[***********] " : "", - cur->alias ? cur->alias : cur->name, cur->class, cur->id, - cur->menu_prefix, - cur->unsupport ? "unsupport_menuentry" : "common_menuentry"); - } - - g_tree_script_buf[g_tree_script_pos] = 0; - g_list_script_buf[g_list_script_pos] = 0; - - grub_snprintf(buf, sizeof(buf), "%d", g_ventoy_img_count); - grub_env_set(args[1], buf); - -fail: - - check_free(device_name, grub_free); - check_free(dev, grub_device_close); - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - - -static grub_err_t ventoy_cmd_clear_img(grub_extcmd_context_t ctxt, int argc, char **args) -{ - img_info *next = NULL; - img_info *cur = g_ventoy_img_list; - - (void)ctxt; - (void)argc; - (void)args; - - while (cur) - { - next = cur->next; - grub_free(cur); - cur = next; - } - - g_ventoy_img_list = NULL; - g_ventoy_img_count = 0; - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_img_name(grub_extcmd_context_t ctxt, int argc, char **args) -{ - long img_id = 0; - img_info *cur = g_ventoy_img_list; - - (void)ctxt; - - if (argc != 2 || (!ventoy_is_decimal(args[0]))) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {imageID} {var}", cmd_raw_name); - } - - img_id = grub_strtol(args[0], NULL, 10); - if (img_id >= g_ventoy_img_count) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images %ld %ld", img_id, g_ventoy_img_count); - } - - debug("Find image %ld name \n", img_id); - - while (cur && img_id > 0) - { - img_id--; - cur = cur->next; - } - - if (!cur) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images"); - } - - debug("image name is %s\n", cur->name); - - grub_env_set(args[1], cur->name); - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_ext_select_img_path(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int len = 0; - char id[32] = {0}; - img_info *cur = g_ventoy_img_list; - - (void)ctxt; - - if (argc != 1) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {var}", cmd_raw_name); - } - - len = (int)grub_strlen(args[0]); - - while (cur) - { - if (len == cur->pathlen && 0 == grub_strcmp(args[0], cur->path)) - { - break; - } - cur = cur->next; - } - - if (!cur) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such image"); - } - - grub_snprintf(id, sizeof(id), "VID_%d", cur->id); - grub_env_set("chosen", id); - grub_env_export("chosen"); - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_chosen_img_path(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int img_id = 0; - char value[32]; - char *pos = NULL; - const char *id = NULL; - img_info *cur = g_ventoy_img_list; - - (void)ctxt; - - if (argc < 1 || argc > 2) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {var}", cmd_raw_name); - } - - id = grub_env_get("chosen"); - - pos = grub_strstr(id, "VID_"); - if (pos) - { - img_id = (int)grub_strtoul(pos + 4, NULL, 10); - } - else - { - img_id = (int)grub_strtoul(id, NULL, 10); - } - - while (cur) - { - if (img_id == cur->id) - { - break; - } - cur = cur->next; - } - - if (!cur) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such image"); - } - - grub_env_set(args[0], cur->path); - - if (argc > 1) - { - grub_snprintf(value, sizeof(value), "%llu", (ulonglong)(cur->size)); - grub_env_set(args[1], value); - } - - g_svd_replace_offset = 0; - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -int ventoy_get_disk_guid(const char *filename, grub_uint8_t *guid, grub_uint8_t *signature) -{ - grub_disk_t disk; - char *device_name; - char *pos; - char *pos2; - - device_name = grub_file_get_device_name(filename); - if (!device_name) - { - return 1; - } - - pos = device_name; - if (pos[0] == '(') - { - pos++; - } - - pos2 = grub_strstr(pos, ","); - if (!pos2) - { - pos2 = grub_strstr(pos, ")"); - } - - if (pos2) - { - *pos2 = 0; - } - - disk = grub_disk_open(pos); - if (disk) - { - grub_disk_read(disk, 0, 0x180, 16, guid); - grub_disk_read(disk, 0, 0x1b8, 4, signature); - grub_disk_close(disk); - } - else - { - return 1; - } - - grub_free(device_name); - return 0; -} - -grub_uint32_t ventoy_get_iso_boot_catlog(grub_file_t file) -{ - eltorito_descriptor desc; - - grub_memset(&desc, 0, sizeof(desc)); - grub_file_seek(file, 17 * 2048); - grub_file_read(file, &desc, sizeof(desc)); - - if (desc.type != 0 || desc.version != 1) - { - return 0; - } - - if (grub_strncmp((char *)desc.id, "CD001", 5) != 0 || - grub_strncmp((char *)desc.system_id, "EL TORITO SPECIFICATION", 23) != 0) - { - return 0; - } - - return desc.sector; -} - -int ventoy_has_efi_eltorito(grub_file_t file, grub_uint32_t sector) -{ - int i; - int x86count = 0; - grub_uint8_t buf[512]; - grub_uint8_t parttype[] = { 0x04, 0x06, 0x0B, 0x0C }; - - grub_file_seek(file, sector * 2048); - grub_file_read(file, buf, sizeof(buf)); - - if (buf[0] == 0x01 && buf[1] == 0xEF) - { - debug("%s efi eltorito in Validation Entry\n", file->name); - return 1; - } - - if (buf[0] == 0x01 && buf[1] == 0x00) - { - x86count++; - } - - for (i = 64; i < (int)sizeof(buf); i += 32) - { - if ((buf[i] == 0x90 || buf[i] == 0x91) && buf[i + 1] == 0xEF) - { - debug("%s efi eltorito offset %d 0x%02x\n", file->name, i, buf[i]); - return 1; - } - - if ((buf[i] == 0x90 || buf[i] == 0x91) && buf[i + 1] == 0x00 && x86count == 1) - { - debug("0x9100 assume %s efi eltorito offset %d 0x%02x\n", file->name, i, buf[i]); - return 1; - } - } - - if (x86count && buf[32] == 0x88 && buf[33] == 0x04) - { - for (i = 0; i < (int)(ARRAY_SIZE(parttype)); i++) - { - if (buf[36] == parttype[i]) - { - debug("hard disk image assume %s efi eltorito, part type 0x%x\n", file->name, buf[36]); - return 1; - } - } - } - - debug("%s does not contain efi eltorito\n", file->name); - return 0; -} - -void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param) -{ - char *pos; - const char *fs = NULL; - const char *cdprompt = NULL; - grub_uint32_t i; - grub_uint8_t chksum = 0; - grub_disk_t disk; - - disk = file->device->disk; - grub_memcpy(¶m->guid, &g_ventoy_guid, sizeof(ventoy_guid)); - - param->vtoy_disk_size = disk->total_sectors * (1 << disk->log_sector_size); - param->vtoy_disk_part_id = disk->partition->number + 1; - param->vtoy_disk_part_type = ventoy_get_fs_type(file->fs->name); - - pos = grub_strstr(file->name, "/"); - if (!pos) - { - pos = file->name; - } - - grub_snprintf(param->vtoy_img_path, sizeof(param->vtoy_img_path), "%s", pos); - - ventoy_get_disk_guid(file->name, param->vtoy_disk_guid, param->vtoy_disk_signature); - - param->vtoy_img_size = file->size; - - param->vtoy_reserved[0] = g_ventoy_break_level; - param->vtoy_reserved[1] = g_ventoy_debug_level; - - param->vtoy_reserved[2] = g_ventoy_chain_type; - - /* Windows CD/DVD prompt 0:suppress 1:reserved */ - param->vtoy_reserved[4] = 0; - if (g_ventoy_chain_type == 1) /* Windows */ - { - cdprompt = ventoy_get_env("VTOY_WINDOWS_CD_PROMPT"); - if (cdprompt && cdprompt[0] == '1' && cdprompt[1] == 0) - { - param->vtoy_reserved[4] = 1; - } - } - - fs = ventoy_get_env("ventoy_fs_probe"); - if (fs && grub_strcmp(fs, "udf") == 0) - { - param->vtoy_reserved[3] = 1; - } - - /* calculate checksum */ - for (i = 0; i < sizeof(ventoy_os_param); i++) - { - chksum += *((grub_uint8_t *)param + i); - } - param->chksum = (grub_uint8_t)(0x100 - chksum); - - return; -} - -int ventoy_check_block_list(grub_file_t file, ventoy_img_chunk_list *chunklist, grub_disk_addr_t start) -{ - grub_uint32_t i = 0; - grub_uint64_t total = 0; - grub_uint64_t fileblk = 0; - ventoy_img_chunk *chunk = NULL; - - for (i = 0; i < chunklist->cur_chunk; i++) - { - chunk = chunklist->chunk + i; - - if (chunk->disk_start_sector <= start) - { - debug("%u disk start invalid %lu\n", i, (ulong)start); - return 1; - } - - total += chunk->disk_end_sector + 1 - chunk->disk_start_sector; - } - - fileblk = (file->size + 511) / 512; - - if (total != fileblk) - { - debug("Invalid total: %llu %llu\n", (ulonglong)total, (ulonglong)fileblk); - if ((file->size % 512) && (total + 1 == fileblk)) - { - debug("maybe img file to be processed.\n"); - return 0; - } - - return 1; - } - - return 0; -} - -int ventoy_get_block_list(grub_file_t file, ventoy_img_chunk_list *chunklist, grub_disk_addr_t start) -{ - int fs_type; - int len; - grub_uint32_t i = 0; - grub_uint32_t sector = 0; - grub_uint32_t count = 0; - grub_off_t size = 0; - grub_off_t read = 0; - - fs_type = ventoy_get_fs_type(file->fs->name); - if (fs_type == ventoy_fs_exfat) - { - grub_fat_get_file_chunk(start, file, chunklist); - } - else if (fs_type == ventoy_fs_ext) - { - grub_ext_get_file_chunk(start, file, chunklist); - } - else - { - file->read_hook = (grub_disk_read_hook_t)grub_disk_blocklist_read; - file->read_hook_data = chunklist; - - for (size = file->size; size > 0; size -= read) - { - read = (size > VTOY_SIZE_1GB) ? VTOY_SIZE_1GB : size; - grub_file_read(file, NULL, read); - } - - for (i = 0; start > 0 && i < chunklist->cur_chunk; i++) - { - chunklist->chunk[i].disk_start_sector += start; - chunklist->chunk[i].disk_end_sector += start; - } - - if (ventoy_fs_udf == fs_type) - { - for (i = 0; i < chunklist->cur_chunk; i++) - { - count = (chunklist->chunk[i].disk_end_sector + 1 - chunklist->chunk[i].disk_start_sector) >> 2; - chunklist->chunk[i].img_start_sector = sector; - chunklist->chunk[i].img_end_sector = sector + count - 1; - sector += count; - } - } - } - - len = (int)grub_strlen(file->name); - if ((len > 4 && grub_strncasecmp(file->name + len - 4, ".img", 4) == 0) || - (len > 4 && grub_strncasecmp(file->name + len - 4, ".vhd", 4) == 0) || - (len > 5 && grub_strncasecmp(file->name + len - 5, ".vhdx", 5) == 0) || - (len > 5 && grub_strncasecmp(file->name + len - 5, ".vtoy", 5) == 0)) - { - for (i = 0; i < chunklist->cur_chunk; i++) - { - count = chunklist->chunk[i].disk_end_sector + 1 - chunklist->chunk[i].disk_start_sector; - if (count < 4) - { - count = 1; - } - else - { - count >>= 2; - } - - chunklist->chunk[i].img_start_sector = sector; - chunklist->chunk[i].img_end_sector = sector + count - 1; - sector += count; - } - } - - return 0; -} - -static grub_err_t ventoy_cmd_img_sector(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc; - grub_file_t file; - grub_disk_addr_t start; - - (void)ctxt; - (void)argc; - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (!file) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); - } - - g_conf_replace_node = NULL; - g_conf_replace_offset = 0; - - if (g_img_chunk_list.chunk) - { - grub_free(g_img_chunk_list.chunk); - } - - if (ventoy_get_fs_type(file->fs->name) >= ventoy_fs_max) - { - grub_file_close(file); - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Unsupported filesystem %s\n", file->fs->name); - } - - /* get image chunk data */ - grub_memset(&g_img_chunk_list, 0, sizeof(g_img_chunk_list)); - g_img_chunk_list.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); - if (NULL == g_img_chunk_list.chunk) - { - return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); - } - - g_img_chunk_list.max_chunk = DEFAULT_CHUNK_NUM; - g_img_chunk_list.cur_chunk = 0; - - start = file->device->disk->partition->start; - - ventoy_get_block_list(file, &g_img_chunk_list, start); - - rc = ventoy_check_block_list(file, &g_img_chunk_list, start); - grub_file_close(file); - - if (rc) - { - return grub_error(GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported chunk list.\n"); - } - - grub_memset(&g_grub_param->file_replace, 0, sizeof(g_grub_param->file_replace)); - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_select_conf_replace(grub_extcmd_context_t ctxt, int argc, char **args) -{ - grub_uint64_t offset = 0; - grub_uint32_t align = 0; - grub_file_t file = NULL; - conf_replace *node = NULL; - - (void)ctxt; - (void)argc; - (void)args; - - debug("select conf replace argc:%d\n", argc); - - if (argc < 2) - { - return 0; - } - - node = ventoy_plugin_find_conf_replace(args[1]); - if (!node) - { - debug("Conf replace not found for %s\n", args[1]); - goto end; - } - - debug("Find conf replace for %s\n", args[1]); - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", node->orgconf); - if (!file) - { - debug("<(loop)%s> NOT exist\n", node->orgconf); - goto end; - } - - offset = grub_iso9660_get_last_file_dirent_pos(file); - grub_file_close(file); - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[0], node->newconf); - if (!file) - { - debug("New config file <%s%s> NOT exist\n", args[0], node->newconf); - goto end; - } - - align = ((int)file->size + 2047) / 2048 * 2048; - - if (align > vtoy_max_replace_file_size) - { - debug("New config file <%s%s> too big\n", args[0], node->newconf); - goto end; - } - - grub_file_read(file, g_conf_replace_new_buf, file->size); - g_conf_replace_new_len = (int)file->size; - g_conf_replace_new_len_align = align; - - g_conf_replace_node = node; - g_conf_replace_offset = offset + 2; - - debug("conf_replace OK: newlen: %d\n", g_conf_replace_new_len); - -end: - if (file) - { - grub_file_close(file); - } - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_sel_auto_install(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i = 0; - int pos = 0; - char *buf = NULL; - char configfile[128]; - install_template *node = NULL; - - (void)ctxt; - (void)argc; - (void)args; - - debug("select auto installation argc:%d\n", argc); - - if (argc < 1) - { - return 0; - } - - node = ventoy_plugin_find_install_template(args[0]); - if (!node) - { - debug("Auto install template not found for %s\n", args[0]); - return 0; - } - - if (node->autosel >= 0 && node->autosel <= node->templatenum) - { - node->cursel = node->autosel - 1; - debug("Auto install template auto select %d\n", node->autosel); - return 0; - } - - buf = (char *)grub_malloc(VTOY_MAX_SCRIPT_BUF); - if (!buf) - { - return 0; - } - - vtoy_ssprintf(buf, pos, "menuentry \"Boot without auto installation template\" {\n" - " echo %s\n}\n", "123"); - - for (i = 0; i < node->templatenum; i++) - { - vtoy_ssprintf(buf, pos, "menuentry \"Boot with %s\" {\n" - " echo 123\n}\n", - node->templatepath[i].path); - } - - g_ventoy_menu_esc = 1; - g_ventoy_suppress_esc = 1; - - grub_snprintf(configfile, sizeof(configfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)buf, pos); - grub_script_execute_sourcecode(configfile); - - g_ventoy_menu_esc = 0; - g_ventoy_suppress_esc = 0; - - grub_free(buf); - - node->cursel = g_ventoy_last_entry - 1; - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_sel_persistence(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i = 0; - int pos = 0; - char *buf = NULL; - char configfile[128]; - persistence_config *node; - - (void)ctxt; - (void)argc; - (void)args; - - debug("select persistence argc:%d\n", argc); - - if (argc < 1) - { - return 0; - } - - node = ventoy_plugin_find_persistent(args[0]); - if (!node) - { - debug("Persistence image not found for %s\n", args[0]); - return 0; - } - - if (node->autosel >= 0 && node->autosel <= node->backendnum) - { - node->cursel = node->autosel - 1; - debug("Persistence image auto select %d\n", node->autosel); - return 0; - } - - buf = (char *)grub_malloc(VTOY_MAX_SCRIPT_BUF); - if (!buf) - { - return 0; - } - - vtoy_ssprintf(buf, pos, "menuentry \"Boot without persistence\" {\n" - " echo %s\n}\n", "123"); - - for (i = 0; i < node->backendnum; i++) - { - vtoy_ssprintf(buf, pos, "menuentry \"Boot with %s\" {\n" - " echo 123\n}\n", - node->backendpath[i].path); - - } - - g_ventoy_menu_esc = 1; - g_ventoy_suppress_esc = 1; - - grub_snprintf(configfile, sizeof(configfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)buf, pos); - grub_script_execute_sourcecode(configfile); - - g_ventoy_menu_esc = 0; - g_ventoy_suppress_esc = 0; - - grub_free(buf); - - node->cursel = g_ventoy_last_entry - 1; - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_dump_img_sector(grub_extcmd_context_t ctxt, int argc, char **args) -{ - grub_uint32_t i; - ventoy_img_chunk *cur; - - (void)ctxt; - (void)argc; - (void)args; - - for (i = 0; i < g_img_chunk_list.cur_chunk; i++) - { - cur = g_img_chunk_list.chunk + i; - grub_printf("image:[%u - %u] <==> disk:[%llu - %llu]\n", - cur->img_start_sector, cur->img_end_sector, - (unsigned long long)cur->disk_start_sector, (unsigned long long)cur->disk_end_sector - ); - } - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_test_block_list(grub_extcmd_context_t ctxt, int argc, char **args) -{ - grub_uint32_t i; - grub_file_t file; - ventoy_img_chunk_list chunklist; - - (void)ctxt; - (void)argc; - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (!file) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); - } - - /* get image chunk data */ - grub_memset(&chunklist, 0, sizeof(chunklist)); - chunklist.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); - if (NULL == chunklist.chunk) - { - return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); - } - - chunklist.max_chunk = DEFAULT_CHUNK_NUM; - chunklist.cur_chunk = 0; - - ventoy_get_block_list(file, &chunklist, 0); - - if (0 != ventoy_check_block_list(file, &chunklist, 0)) - { - grub_printf("########## UNSUPPORTED ###############\n"); - } - - grub_printf("filesystem: <%s> entry number:<%u>\n", file->fs->name, chunklist.cur_chunk); - - for (i = 0; i < chunklist.cur_chunk; i++) - { - grub_printf("%llu+%llu,", (ulonglong)chunklist.chunk[i].disk_start_sector, - (ulonglong)(chunklist.chunk[i].disk_end_sector + 1 - chunklist.chunk[i].disk_start_sector)); - } - - grub_printf("\n==================================\n"); - - for (i = 0; i < chunklist.cur_chunk; i++) - { - grub_printf("%2u: [%llu %llu] - [%llu %llu]\n", i, - (ulonglong)chunklist.chunk[i].img_start_sector, - (ulonglong)chunklist.chunk[i].img_end_sector, - (ulonglong)chunklist.chunk[i].disk_start_sector, - (ulonglong)chunklist.chunk[i].disk_end_sector - ); - } - - grub_free(chunklist.chunk); - grub_file_close(file); - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_add_replace_file(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i; - ventoy_grub_param_file_replace *replace = NULL; - - (void)ctxt; - (void)argc; - (void)args; - - if (argc >= 2) - { - replace = &(g_grub_param->file_replace); - replace->magic = GRUB_FILE_REPLACE_MAGIC; - - replace->old_name_cnt = 0; - for (i = 0; i < 4 && i + 1 < argc; i++) - { - replace->old_name_cnt++; - grub_snprintf(replace->old_file_name[i], sizeof(replace->old_file_name[i]), "%s", args[i + 1]); - } - - replace->new_file_virtual_id = (grub_uint32_t)grub_strtoul(args[0], NULL, 10); - } - - VENTOY_CMD_RETURN(GRUB_ERR_NONE); -} - -static grub_err_t ventoy_cmd_dump_menu(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - if (argc == 0) - { - grub_printf("List Mode: CurLen:%d MaxLen:%u\n", g_list_script_pos, VTOY_MAX_SCRIPT_BUF); - grub_printf("%s", g_list_script_buf); - } - else - { - grub_printf("Tree Mode: CurLen:%d MaxLen:%u\n", g_tree_script_pos, VTOY_MAX_SCRIPT_BUF); - grub_printf("%s", g_tree_script_buf); - } - - return 0; -} - -static grub_err_t ventoy_cmd_dump_img_list(grub_extcmd_context_t ctxt, int argc, char **args) -{ - img_info *cur = g_ventoy_img_list; - - (void)ctxt; - (void)argc; - (void)args; - - while (cur) - { - grub_printf("path:<%s> id=%d list_index=%d\n", cur->path, cur->id, cur->plugin_list_index); - grub_printf("name:<%s>\n\n", cur->name); - cur = cur->next; - } - - return 0; -} - -static grub_err_t ventoy_cmd_dump_injection(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - ventoy_plugin_dump_injection(); - - return 0; -} - -static grub_err_t ventoy_cmd_dump_auto_install(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - ventoy_plugin_dump_auto_install(); - - return 0; -} - -static grub_err_t ventoy_cmd_dump_persistence(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - ventoy_plugin_dump_persistence(); - - return 0; -} - -static grub_err_t ventoy_cmd_check_mode(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - if (argc != 1) - { - return 1; - } - - if (args[0][0] == '0') - { - return g_ventoy_memdisk_mode ? 0 : 1; - } - else if (args[0][0] == '1') - { - return g_ventoy_iso_raw ? 0 : 1; - } - else if (args[0][0] == '2') - { - return g_ventoy_iso_uefi_drv ? 0 : 1; - } - else if (args[0][0] == '3') - { - return g_ventoy_grub2_mode ? 0 : 1; - } - - return 1; -} - -static grub_err_t ventoy_cmd_dynamic_menu(grub_extcmd_context_t ctxt, int argc, char **args) -{ - static int configfile_mode = 0; - char memfile[128] = {0}; - - (void)ctxt; - (void)argc; - (void)args; - - /* - * args[0]: 0:normal 1:configfile - * args[1]: 0:list_buf 1:tree_buf - */ - - if (argc != 2) - { - debug("Invalid argc %d\n", argc); - return 0; - } - - if (args[0][0] == '0') - { - if (args[1][0] == '0') - { - grub_script_execute_sourcecode(g_list_script_buf); - } - else - { - grub_script_execute_sourcecode(g_tree_script_buf); - } - } - else - { - if (configfile_mode) - { - debug("Now already in F3 mode %d\n", configfile_mode); - return 0; - } - - if (args[1][0] == '0') - { - grub_snprintf(memfile, sizeof(memfile), "configfile mem:0x%llx:size:%d", - (ulonglong)(ulong)g_list_script_buf, g_list_script_pos); - } - else - { - g_ventoy_last_entry = -1; - grub_snprintf(memfile, sizeof(memfile), "configfile mem:0x%llx:size:%d", - (ulonglong)(ulong)g_tree_script_buf, g_tree_script_pos); - } - - configfile_mode = 1; - grub_script_execute_sourcecode(memfile); - configfile_mode = 0; - } - - return 0; -} - -static grub_err_t ventoy_cmd_file_exist_nocase(grub_extcmd_context_t ctxt, int argc, char **args) -{ - grub_file_t file; - - (void)ctxt; - - if (argc != 1) - { - return 1; - } - - g_ventoy_case_insensitive = 1; - file = grub_file_open(args[0], VENTOY_FILE_TYPE); - g_ventoy_case_insensitive = 0; - - grub_errno = 0; - - if (file) - { - grub_file_close(file); - return 0; - } - return 1; -} - -static grub_err_t ventoy_cmd_find_bootable_hdd(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int id = 0; - int find = 0; - grub_disk_t disk; - const char *isopath = NULL; - char hdname[32]; - ventoy_mbr_head mbr; - - (void)ctxt; - (void)argc; - - if (argc != 1) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s variable\n", cmd_raw_name); - } - - isopath = grub_env_get("vtoy_iso_part"); - if (!isopath) - { - debug("isopath is null %p\n", isopath); - return 0; - } - - debug("isopath is %s\n", isopath); - - for (id = 0; id < 30 && (find == 0); id++) - { - grub_snprintf(hdname, sizeof(hdname), "hd%d,", id); - if (grub_strstr(isopath, hdname)) - { - debug("skip %s ...\n", hdname); - continue; - } - - grub_snprintf(hdname, sizeof(hdname), "hd%d", id); - - disk = grub_disk_open(hdname); - if (!disk) - { - debug("%s not exist\n", hdname); - break; - } - - grub_memset(&mbr, 0, sizeof(mbr)); - if (0 == grub_disk_read(disk, 0, 0, 512, &mbr)) - { - if (mbr.Byte55 == 0x55 && mbr.ByteAA == 0xAA) - { - if (mbr.PartTbl[0].Active == 0x80 || mbr.PartTbl[1].Active == 0x80 || - mbr.PartTbl[2].Active == 0x80 || mbr.PartTbl[3].Active == 0x80) - { - - grub_env_set(args[0], hdname); - find = 1; - } - } - debug("%s is %s\n", hdname, find ? "bootable" : "NOT bootable"); - } - else - { - debug("read %s failed\n", hdname); - } - - grub_disk_close(disk); - } - - return 0; -} - -static grub_err_t ventoy_cmd_read_1st_line(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int len = 1024; - grub_file_t file; - char *buf = NULL; - - (void)ctxt; - (void)argc; - - if (argc != 2) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s file var \n", cmd_raw_name); - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (!file) - { - debug("failed to open file %s\n", args[0]); - return 0; - } - - buf = grub_malloc(len); - if (!buf) - { - goto end; - } - - buf[len - 1] = 0; - grub_file_read(file, buf, len - 1); - - ventoy_get_line(buf); - ventoy_set_env(args[1], buf); - -end: - - grub_check_free(buf); - grub_file_close(file); - - return 0; -} - -static int ventoy_img_partition_callback (struct grub_disk *disk, const grub_partition_t partition, void *data) -{ - (void)disk; - (void)data; - - g_part_list_pos += grub_snprintf(g_part_list_buf + g_part_list_pos, VTOY_MAX_SCRIPT_BUF - g_part_list_pos, - "0 %llu linear /dev/ventoy %llu\n", - (ulonglong)partition->len, (ulonglong)partition->start); - - return 0; -} - -static grub_err_t ventoy_cmd_img_part_info(grub_extcmd_context_t ctxt, int argc, char **args) -{ - char *device_name = NULL; - grub_device_t dev = NULL; - char buf[64]; - - (void)ctxt; - - g_part_list_pos = 0; - grub_env_unset("vtoy_img_part_file"); - - if (argc != 1) - { - return 1; - } - - device_name = grub_file_get_device_name(args[0]); - if (!device_name) - { - debug("ventoy_cmd_img_part_info failed, %s\n", args[0]); - goto end; - } - - dev = grub_device_open(device_name); - if (!dev) - { - debug("grub_device_open failed, %s\n", device_name); - goto end; - } - - grub_partition_iterate(dev->disk, ventoy_img_partition_callback, NULL); - - grub_snprintf(buf, sizeof(buf), "newc:vtoy_dm_table:mem:0x%llx:size:%d", (ulonglong)(ulong)g_part_list_buf, g_part_list_pos); - grub_env_set("vtoy_img_part_file", buf); - -end: - - check_free(device_name, grub_free); - check_free(dev, grub_device_close); - - return 0; -} - - -static grub_err_t ventoy_cmd_file_strstr(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc = 1; - grub_file_t file; - char *buf = NULL; - - (void)ctxt; - (void)argc; - - if (argc != 2) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s file str \n", cmd_raw_name); - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (!file) - { - debug("failed to open file %s\n", args[0]); - return 1; - } - - buf = grub_malloc(file->size + 1); - if (!buf) - { - goto end; - } - - buf[file->size] = 0; - grub_file_read(file, buf, file->size); - - if (grub_strstr(buf, args[1])) - { - rc = 0; - } - -end: - - grub_check_free(buf); - grub_file_close(file); - - return rc; -} - -static grub_err_t ventoy_cmd_parse_volume(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int len; - grub_file_t file; - char buf[64]; - grub_uint64_t size; - ventoy_iso9660_vd pvd; - - (void)ctxt; - (void)argc; - - if (argc != 4) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s sysid volid space \n", cmd_raw_name); - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (!file) - { - debug("failed to open file %s\n", args[0]); - return 0; - } - - grub_file_seek(file, 16 * 2048); - len = (int)grub_file_read(file, &pvd, sizeof(pvd)); - if (len != sizeof(pvd)) - { - debug("failed to read pvd %d\n", len); - goto end; - } - - grub_memset(buf, 0, sizeof(buf)); - grub_memcpy(buf, pvd.sys, sizeof(pvd.sys)); - ventoy_set_env(args[1], buf); - - grub_memset(buf, 0, sizeof(buf)); - grub_memcpy(buf, pvd.vol, sizeof(pvd.vol)); - ventoy_set_env(args[2], buf); - - size = pvd.space; - size *= 2048; - grub_snprintf(buf, sizeof(buf), "%llu", (ulonglong)size); - ventoy_set_env(args[3], buf); - -end: - grub_file_close(file); - - return 0; -} - -static grub_err_t ventoy_cmd_parse_create_date(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int len; - grub_file_t file; - char buf[64]; - - (void)ctxt; - (void)argc; - - if (argc != 2) - { - return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s var \n", cmd_raw_name); - } - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (!file) - { - debug("failed to open file %s\n", args[0]); - return 0; - } - - grub_memset(buf, 0, sizeof(buf)); - grub_file_seek(file, 16 * 2048 + 813); - len = (int)grub_file_read(file, buf, 17); - if (len != 17) - { - debug("failed to read create date %d\n", len); - goto end; - } - - ventoy_set_env(args[1], buf); - -end: - grub_file_close(file); - - return 0; -} - -static grub_err_t ventoy_cmd_img_hook_root(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - ventoy_env_hook_root(1); - - return 0; -} - -static grub_err_t ventoy_cmd_img_unhook_root(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - ventoy_env_hook_root(0); - - return 0; -} - -#ifdef GRUB_MACHINE_EFI -static grub_err_t ventoy_cmd_check_secureboot_var(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int ret = 1; - grub_uint8_t *var; - grub_size_t size; - grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; - - (void)ctxt; - (void)argc; - (void)args; - - var = grub_efi_get_variable("SecureBoot", &global, &size); - if (var && *var == 1) - { - return 0; - } - - return ret; -} -#else -static grub_err_t ventoy_cmd_check_secureboot_var(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - return 1; -} -#endif - -static grub_err_t ventoy_cmd_img_check_range(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i; - int ret = 1; - grub_file_t file; - grub_uint64_t FileSectors = 0; - ventoy_gpt_info *gpt = NULL; - ventoy_part_table *pt = NULL; - grub_uint8_t zeroguid[16] = {0}; - - (void)ctxt; - (void)argc; - - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); - if (!file) - { - debug("failed to open file %s\n", args[0]); - return 1; - } - - if (file->size % 512) - { - debug("unaligned file size: %llu\n", (ulonglong)file->size); - goto out; - } - - gpt = grub_zalloc(sizeof(ventoy_gpt_info)); - if (!gpt) - { - goto out; - } - - FileSectors = file->size / 512; - - grub_file_read(file, gpt, sizeof(ventoy_gpt_info)); - if (grub_strncmp(gpt->Head.Signature, "EFI PART", 8) == 0) - { - debug("This is EFI partition table\n"); - - for (i = 0; i < 128; i++) - { - if (grub_memcmp(gpt->PartTbl[i].PartGuid, zeroguid, 16)) - { - if (FileSectors < gpt->PartTbl[i].LastLBA) - { - debug("out of range: part[%d] LastLBA:%llu FileSectors:%llu\n", i, - (ulonglong)gpt->PartTbl[i].LastLBA, (ulonglong)FileSectors); - goto out; - } - } - } - } - else - { - debug("This is MBR partition table\n"); - - for (i = 0; i < 4; i++) - { - pt = gpt->MBR.PartTbl + i; - if (FileSectors < pt->StartSectorId + pt->SectorCount) - { - debug("out of range: part[%d] LastLBA:%llu FileSectors:%llu\n", i, - (ulonglong)(pt->StartSectorId + pt->SectorCount), - (ulonglong)FileSectors); - goto out; - } - } - } - - ret = 0; - -out: - grub_file_close(file); - grub_check_free(gpt); - grub_errno = GRUB_ERR_NONE; - return ret; -} - -static grub_err_t ventoy_cmd_clear_key(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i; - int ret; - - (void)ctxt; - (void)argc; - (void)args; - - for (i = 0; i < 500; i++) - { - ret = grub_getkey_noblock(); - if (ret == GRUB_TERM_NO_KEY) - { - break; - } - } - - if (i >= 500) - { - grub_cls(); - grub_printf("\n\n Still have key input after clear.\n"); - grub_refresh(); - grub_sleep(5); - } - - return 0; -} - -static grub_err_t ventoy_cmd_acpi_param(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i; - int buflen; - int datalen; - int loclen; - int img_chunk_num; - int image_sector_size; - char cmd[64]; - ventoy_chain_head *chain; - ventoy_img_chunk *chunk; - ventoy_os_param *osparam; - ventoy_image_location *location; - ventoy_image_disk_region *region; - struct grub_acpi_table_header *acpi; - - (void)ctxt; - - if (argc != 2) - { - return 1; - } - - debug("ventoy_cmd_acpi_param %s %s\n", args[0], args[1]); - - chain = (ventoy_chain_head *)(ulong)grub_strtoul(args[0], NULL, 16); - if (!chain) - { - return 1; - } - - image_sector_size = (int)grub_strtol(args[1], NULL, 10); - - if (grub_memcmp(&g_ventoy_guid, &(chain->os_param.guid), 16)) - { - debug("Invalid ventoy guid 0x%x\n", chain->os_param.guid.data1); - return 1; - } - - img_chunk_num = chain->img_chunk_num; - - loclen = sizeof(ventoy_image_location) + (img_chunk_num - 1) * sizeof(ventoy_image_disk_region); - datalen = sizeof(ventoy_os_param) + loclen; - - buflen = sizeof(struct grub_acpi_table_header) + datalen; - acpi = grub_zalloc(buflen); - if (!acpi) - { - return 1; - } - - /* Step1: Fill acpi table header */ - grub_memcpy(acpi->signature, "VTOY", 4); - acpi->length = buflen; - acpi->revision = 1; - grub_memcpy(acpi->oemid, "VENTOY", 6); - grub_memcpy(acpi->oemtable, "OSPARAMS", 8); - acpi->oemrev = 1; - acpi->creator_id[0] = 1; - acpi->creator_rev = 1; - - /* Step2: Fill data */ - osparam = (ventoy_os_param *)(acpi + 1); - grub_memcpy(osparam, &chain->os_param, sizeof(ventoy_os_param)); - osparam->vtoy_img_location_addr = 0; - osparam->vtoy_img_location_len = loclen; - osparam->chksum = 0; - osparam->chksum = 0x100 - grub_byte_checksum(osparam, sizeof(ventoy_os_param)); - - location = (ventoy_image_location *)(osparam + 1); - grub_memcpy(&location->guid, &osparam->guid, sizeof(ventoy_guid)); - location->image_sector_size = image_sector_size; - location->disk_sector_size = chain->disk_sector_size; - location->region_count = img_chunk_num; - - region = location->regions; - chunk = (ventoy_img_chunk *)((char *)chain + chain->img_chunk_offset); - if (512 == image_sector_size) - { - for (i = 0; i < img_chunk_num; i++) - { - region->image_sector_count = chunk->disk_end_sector - chunk->disk_start_sector + 1; - region->image_start_sector = chunk->img_start_sector * 4; - region->disk_start_sector = chunk->disk_start_sector; - region++; - chunk++; - } - } - else - { - for (i = 0; i < img_chunk_num; i++) - { - region->image_sector_count = chunk->img_end_sector - chunk->img_start_sector + 1; - region->image_start_sector = chunk->img_start_sector; - region->disk_start_sector = chunk->disk_start_sector; - region++; - chunk++; - } - } - - /* Step3: Fill acpi checksum */ - acpi->checksum = 0; - acpi->checksum = 0x100 - grub_byte_checksum(acpi, acpi->length); - - /* load acpi table */ - grub_snprintf(cmd, sizeof(cmd), "acpi mem:0x%lx:size:%d", (ulong)acpi, acpi->length); - grub_script_execute_sourcecode(cmd); - - grub_free(acpi); - - VENTOY_CMD_RETURN(0); -} - -static grub_err_t ventoy_cmd_push_last_entry(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - g_ventoy_last_entry_back = g_ventoy_last_entry; - g_ventoy_last_entry = -1; - - return 0; -} - -static grub_err_t ventoy_cmd_pop_last_entry(grub_extcmd_context_t ctxt, int argc, char **args) -{ - (void)ctxt; - (void)argc; - (void)args; - - g_ventoy_last_entry = g_ventoy_last_entry_back; - - return 0; -} - -grub_uint64_t ventoy_get_part1_size(ventoy_gpt_info *gpt) -{ - grub_uint64_t sectors; - - if (grub_strncmp(gpt->Head.Signature, "EFI PART", 8) == 0) - { - sectors = gpt->PartTbl[0].LastLBA + 1 - gpt->PartTbl[0].StartLBA; - } - else - { - sectors = gpt->MBR.PartTbl[0].SectorCount; - } - - return sectors * 512; -} - -static int ventoy_lib_module_callback(const char *filename, const struct grub_dirhook_info *info, void *data) -{ - const char *pos = filename + 1; - - if (info->dir) - { - while (*pos) - { - if (*pos == '.') - { - if ((*(pos - 1) >= '0' && *(pos - 1) <= '9') && (*(pos + 1) >= '0' && *(pos + 1) <= '9')) - { - grub_strncpy((char *)data, filename, 128); - return 1; - } - } - pos++; - } - } - - return 0; -} - -static grub_err_t ventoy_cmd_lib_module_ver(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc = 1; - char *device_name = NULL; - grub_device_t dev = NULL; - grub_fs_t fs = NULL; - char buf[128] = {0}; - - (void)ctxt; - - if (argc != 3) - { - debug("ventoy_cmd_lib_module_ver, invalid param num %d\n", argc); - return 1; - } - - debug("ventoy_cmd_lib_module_ver %s %s %s\n", args[0], args[1], args[2]); - - device_name = grub_file_get_device_name(args[0]); - if (!device_name) - { - debug("grub_file_get_device_name failed, %s\n", args[0]); - goto end; - } - - dev = grub_device_open(device_name); - if (!dev) - { - debug("grub_device_open failed, %s\n", device_name); - goto end; - } - - fs = grub_fs_probe(dev); - if (!fs) - { - debug("grub_fs_probe failed, %s\n", device_name); - goto end; - } - - fs->fs_dir(dev, args[1], ventoy_lib_module_callback, buf); - - if (buf[0]) - { - ventoy_set_env(args[2], buf); - } - - rc = 0; - -end: - - check_free(device_name, grub_free); - check_free(dev, grub_device_close); - - return rc; -} - -static grub_err_t ventoy_cmd_load_part_table(grub_extcmd_context_t ctxt, int argc, char **args) -{ - char name[64]; - int ret; - grub_disk_t disk; - grub_device_t dev; - - (void)argc; - (void)ctxt; - - g_ventoy_part_info = grub_zalloc(sizeof(ventoy_gpt_info)); - if (!g_ventoy_part_info) - { - return 1; - } - - disk = grub_disk_open(args[0]); - if (!disk) - { - debug("Failed to open disk %s\n", args[0]); - return 1; - } - - g_ventoy_disk_size = disk->total_sectors * (1U << disk->log_sector_size); - - grub_disk_read(disk, 0, 0, sizeof(ventoy_gpt_info), g_ventoy_part_info); - grub_disk_close(disk); - - grub_snprintf(name, sizeof(name), "%s,1", args[0]); - dev = grub_device_open(name); - if (dev) - { - /* make sure that we are running in a correct Ventoy device */ - ret = ventoy_check_device(dev); - grub_device_close(dev); - - if (ret) - { - grub_exit(); - } - } - - return 0; -} - -static grub_err_t ventoy_cmd_check_custom_boot(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int ret = 1; - const char *vcfg = NULL; - - (void)argc; - (void)ctxt; - - vcfg = ventoy_plugin_get_custom_boot(args[0]); - if (vcfg) - { - debug("custom boot <%s>:<%s>\n", args[0], vcfg); - grub_env_set(args[1], vcfg); - ret = 0; - } - else - { - debug("custom boot <%s>:\n", args[0]); - } - - grub_errno = 0; - return ret; -} - - -static grub_err_t ventoy_cmd_part_exist(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int id; - grub_uint8_t zeroguid[16] = {0}; - - (void)argc; - (void)ctxt; - - id = (int)grub_strtoul(args[0], NULL, 10); - grub_errno = 0; - - if (grub_memcmp(g_ventoy_part_info->Head.Signature, "EFI PART", 8) == 0) - { - if (id >= 1 && id <= 128) - { - if (grub_memcmp(g_ventoy_part_info->PartTbl[id - 1].PartGuid, zeroguid, 16)) - { - return 0; - } - } - } - else - { - if (id >= 1 && id <= 4) - { - if (g_ventoy_part_info->MBR.PartTbl[id - 1].FsFlag) - { - return 0; - } - } - } - - return 1; -} - -static grub_err_t ventoy_cmd_get_fs_label(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc = 1; - char *device_name = NULL; - grub_device_t dev = NULL; - grub_fs_t fs = NULL; - char *label = NULL; - - (void)ctxt; - - debug("get fs label for %s\n", args[0]); - - if (argc != 2) - { - debug("ventoy_cmd_get_fs_label, invalid param num %d\n", argc); - return 1; - } - - device_name = grub_file_get_device_name(args[0]); - if (!device_name) - { - debug("grub_file_get_device_name failed, %s\n", args[0]); - goto end; - } - - dev = grub_device_open(device_name); - if (!dev) - { - debug("grub_device_open failed, %s\n", device_name); - goto end; - } - - fs = grub_fs_probe(dev); - if (NULL == fs || NULL == fs->fs_label) - { - debug("grub_fs_probe failed, %s %p %p\n", device_name, fs, fs->fs_label); - goto end; - } - - fs->fs_label(dev, &label); - if (label) - { - debug("label=<%s>\n", label); - ventoy_set_env(args[1], label); - grub_free(label); - } - - rc = 0; - -end: - - check_free(device_name, grub_free); - check_free(dev, grub_device_close); - - return rc; -} - -static int ventoy_fs_enum_1st_file(const char *filename, const struct grub_dirhook_info *info, void *data) -{ - if (!info->dir) - { - grub_snprintf((char *)data, 256, "%s", filename); - return 1; - } - - return 0; -} - - -static grub_err_t ventoy_cmd_fs_enum_1st_file(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int rc = 1; - char *device_name = NULL; - grub_device_t dev = NULL; - grub_fs_t fs = NULL; - char name[256] ={0}; - - (void)ctxt; - - if (argc != 3) - { - debug("ventoy_cmd_fs_enum_1st_file, invalid param num %d\n", argc); - return 1; - } - - device_name = grub_file_get_device_name(args[0]); - if (!device_name) - { - debug("grub_file_get_device_name failed, %s\n", args[0]); - goto end; - } - - dev = grub_device_open(device_name); - if (!dev) - { - debug("grub_device_open failed, %s\n", device_name); - goto end; - } - - fs = grub_fs_probe(dev); - if (!fs) - { - debug("grub_fs_probe failed, %s\n", device_name); - goto end; - } - - fs->fs_dir(dev, args[1], ventoy_fs_enum_1st_file, name); - if (name[0]) - { - ventoy_set_env(args[2], name); - } - - rc = 0; - -end: - - check_free(device_name, grub_free); - check_free(dev, grub_device_close); - - return rc; -} - -static grub_err_t ventoy_cmd_basename(grub_extcmd_context_t ctxt, int argc, char **args) -{ - char c; - char *pos = NULL; - char *end = NULL; - - (void)ctxt; - - if (argc != 2) - { - debug("ventoy_cmd_basename, invalid param num %d\n", argc); - return 1; - } - - for (pos = args[0]; *pos; pos++) - { - if (*pos == '.') - { - end = pos; - } - } - - if (end) - { - c = *end; - *end = 0; - } - - grub_env_set(args[1], args[0]); - - if (end) - { - *end = c; - } - - return 0; -} - -static grub_err_t ventoy_cmd_basefile(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int i; - int len; - const char *buf; - - (void)ctxt; - - if (argc != 2) - { - debug("ventoy_cmd_basefile, invalid param num %d\n", argc); - return 1; - } - - buf = args[0]; - len = (int)grub_strlen(buf); - for (i = len; i > 0; i--) - { - if (buf[i - 1] == '/') - { - grub_env_set(args[1], buf + i); - return 0; - } - } - - grub_env_set(args[1], buf); - - return 0; -} - -static grub_err_t ventoy_cmd_enum_video_mode(grub_extcmd_context_t ctxt, int argc, char **args) -{ - struct grub_video_mode_info info; - char buf[32]; - - (void)ctxt; - (void)argc; - (void)args; - - if (!g_video_mode_list) - { - ventoy_enum_video_mode(); - } - - if (grub_video_get_info(&info) == GRUB_ERR_NONE) - { - grub_snprintf(buf, sizeof(buf), "Resolution (%ux%u)", info.width, info.height); - } - else - { - grub_snprintf(buf, sizeof(buf), "Resolution (0x0)"); - } - - grub_env_set("VTOY_CUR_VIDEO_MODE", buf); - - grub_snprintf(buf, sizeof(buf), "%d", g_video_mode_num); - grub_env_set("VTOY_VIDEO_MODE_NUM", buf); - - VENTOY_CMD_RETURN(0); -} - -static grub_err_t vt_cmd_update_cur_video_mode(grub_extcmd_context_t ctxt, int argc, char **args) -{ - struct grub_video_mode_info info; - char buf[32]; - - (void)ctxt; - (void)argc; - (void)args; - - if (grub_video_get_info(&info) == GRUB_ERR_NONE) - { - grub_snprintf(buf, sizeof(buf), "%ux%ux%u", info.width, info.height, info.bpp); - } - else - { - grub_snprintf(buf, sizeof(buf), "0x0x0"); - } - - grub_env_set(args[0], buf); - - VENTOY_CMD_RETURN(0); -} - -static grub_err_t ventoy_cmd_get_video_mode(grub_extcmd_context_t ctxt, int argc, char **args) -{ - int id; - char buf[32]; - - (void)ctxt; - (void)argc; - - if (!g_video_mode_list) - { - return 0; - } - - id = (int)grub_strtoul(args[0], NULL, 10); - if (id < g_video_mode_num) - { - grub_snprintf(buf, sizeof(buf), "%ux%ux%u", - g_video_mode_list[id].width, g_video_mode_list[id].height, g_video_mode_list[id].bpp); - } - - grub_env_set(args[1], buf); - - VENTOY_CMD_RETURN(0); -} - -grub_uint64_t ventoy_grub_get_file_size(const char *fmt, ...) -{ - grub_uint64_t size = 0; - grub_file_t file; - va_list ap; - char fullpath[256] = {0}; - - va_start (ap, fmt); - grub_vsnprintf(fullpath, 255, fmt, ap); - va_end (ap); - - file = grub_file_open(fullpath, VENTOY_FILE_TYPE); - if (!file) - { - debug("grub_file_open failed <%s>\n", fullpath); - grub_errno = 0; - return 0; - } - - size = file->size; - grub_file_close(file); - return size; -} - -grub_file_t ventoy_grub_file_open(enum grub_file_type type, const char *fmt, ...) -{ - va_list ap; - grub_file_t file; - char fullpath[256] = {0}; - - va_start (ap, fmt); - grub_vsnprintf(fullpath, 255, fmt, ap); - va_end (ap); - - file = grub_file_open(fullpath, type); - if (!file) - { - debug("grub_file_open failed <%s> %d\n", fullpath, grub_errno); - grub_errno = 0; - } - - return file; -} - -int ventoy_is_file_exist(const char *fmt, ...) -{ - va_list ap; - int len; - char *pos = NULL; - char buf[256] = {0}; - - grub_snprintf(buf, sizeof(buf), "%s", "[ -f \""); - pos = buf + 6; - - va_start (ap, fmt); - len = grub_vsnprintf(pos, 255, fmt, ap); - va_end (ap); - - grub_strncpy(pos + len, "\" ]", 3); - - debug("script exec %s\n", buf); - - if (0 == grub_script_execute_sourcecode(buf)) - { - return 1; - } - - return 0; -} - -int ventoy_is_dir_exist(const char *fmt, ...) -{ - va_list ap; - int len; - char *pos = NULL; - char buf[256] = {0}; - - grub_snprintf(buf, sizeof(buf), "%s", "[ -d \""); - pos = buf + 6; - - va_start (ap, fmt); - len = grub_vsnprintf(pos, 255, fmt, ap); - va_end (ap); - - grub_strncpy(pos + len, "\" ]", 3); - - debug("script exec %s\n", buf); - - if (0 == grub_script_execute_sourcecode(buf)) - { - return 1; - } - - return 0; -} - -int ventoy_gzip_compress(void *mem_in, int mem_in_len, void *mem_out, int mem_out_len) -{ - mz_stream s; - grub_uint8_t *outbuf; - grub_uint8_t gzHdr[10] = - { - 0x1F, 0x8B, /* magic */ - 8, /* z method */ - 0, /* flags */ - 0,0,0,0, /* mtime */ - 4, /* xfl */ - 3, /* OS */ - }; - - grub_memset(&s, 0, sizeof(mz_stream)); - - mz_deflateInit2(&s, 1, MZ_DEFLATED, -MZ_DEFAULT_WINDOW_BITS, 6, MZ_DEFAULT_STRATEGY); - - outbuf = (grub_uint8_t *)mem_out; - - mem_out_len -= sizeof(gzHdr) + 8; - grub_memcpy(outbuf, gzHdr, sizeof(gzHdr)); - outbuf += sizeof(gzHdr); - - s.avail_in = mem_in_len; - s.next_in = mem_in; - - s.avail_out = mem_out_len; - s.next_out = outbuf; - - mz_deflate(&s, MZ_FINISH); - - mz_deflateEnd(&s); - - outbuf += s.total_out; - *(grub_uint32_t *)outbuf = grub_getcrc32c(0, outbuf, s.total_out); - *(grub_uint32_t *)(outbuf + 4) = (grub_uint32_t)(s.total_out); - - return s.total_out + sizeof(gzHdr) + 8; -} - -static int ventoy_env_init(void) -{ - char buf[64]; - - grub_env_set("vtdebug_flag", ""); - - g_part_list_buf = grub_malloc(VTOY_PART_BUF_LEN); - g_tree_script_buf = grub_malloc(VTOY_MAX_SCRIPT_BUF); - g_list_script_buf = grub_malloc(VTOY_MAX_SCRIPT_BUF); - g_conf_replace_new_buf = grub_malloc(vtoy_max_replace_file_size); - - ventoy_filt_register(0, ventoy_wrapper_open); - - g_grub_param = (ventoy_grub_param *)grub_zalloc(sizeof(ventoy_grub_param)); - if (g_grub_param) - { - g_grub_param->grub_env_get = grub_env_get; - g_grub_param->grub_env_set = (grub_env_set_pf)grub_env_set; - g_grub_param->grub_env_printf = (grub_env_printf_pf)grub_printf; - grub_snprintf(buf, sizeof(buf), "%p", g_grub_param); - grub_env_set("env_param", buf); - grub_env_set("ventoy_env_param", buf); - - grub_env_export("env_param"); - grub_env_export("ventoy_env_param"); - } - - return 0; -} - -static cmd_para ventoy_cmds[] = -{ - { "vt_incr", ventoy_cmd_incr, 0, NULL, "{Var} {INT}", "Increase integer variable", NULL }, - { "vt_mod", ventoy_cmd_mod, 0, NULL, "{Int} {Int} {Var}", "mod integer variable", NULL }, - { "vt_strstr", ventoy_cmd_strstr, 0, NULL, "", "", NULL }, - { "vt_str_begin", ventoy_cmd_strbegin, 0, NULL, "", "", NULL }, - { "vt_debug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, - { "vtdebug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, - { "vtbreak", ventoy_cmd_break, 0, NULL, "{level}", "set debug break", NULL }, - { "vt_cmp", ventoy_cmd_cmp, 0, NULL, "{Int1} { eq|ne|gt|lt|ge|le } {Int2}", "Comare two integers", NULL }, - { "vt_device", ventoy_cmd_device, 0, NULL, "path var", "", NULL }, - { "vt_check_compatible", ventoy_cmd_check_compatible, 0, NULL, "", "", NULL }, - { "vt_list_img", ventoy_cmd_list_img, 0, NULL, "{device} {cntvar}", "find all iso file in device", NULL }, - { "vt_clear_img", ventoy_cmd_clear_img, 0, NULL, "", "clear image list", NULL }, - { "vt_img_name", ventoy_cmd_img_name, 0, NULL, "{imageID} {var}", "get image name", NULL }, - { "vt_chosen_img_path", ventoy_cmd_chosen_img_path, 0, NULL, "{var}", "get chosen img path", NULL }, - { "vt_ext_select_img_path", ventoy_cmd_ext_select_img_path, 0, NULL, "{var}", "select chosen img path", NULL }, - { "vt_img_sector", ventoy_cmd_img_sector, 0, NULL, "{imageName}", "", NULL }, - { "vt_dump_img_sector", ventoy_cmd_dump_img_sector, 0, NULL, "", "", NULL }, - { "vt_load_wimboot", ventoy_cmd_load_wimboot, 0, NULL, "", "", NULL }, - { "vt_load_vhdboot", ventoy_cmd_load_vhdboot, 0, NULL, "", "", NULL }, - { "vt_patch_vhdboot", ventoy_cmd_patch_vhdboot, 0, NULL, "", "", NULL }, - { "vt_raw_chain_data", ventoy_cmd_raw_chain_data, 0, NULL, "", "", NULL }, - { "vt_get_vtoy_type", ventoy_cmd_get_vtoy_type, 0, NULL, "", "", NULL }, - { "vt_check_custom_boot", ventoy_cmd_check_custom_boot, 0, NULL, "", "", NULL }, - { "vt_dump_custom_boot", ventoy_cmd_dump_custom_boot, 0, NULL, "", "", NULL }, - - { "vt_skip_svd", ventoy_cmd_skip_svd, 0, NULL, "", "", NULL }, - { "vt_cpio_busybox64", ventoy_cmd_cpio_busybox_64, 0, NULL, "", "", NULL }, - { "vt_load_cpio", ventoy_cmd_load_cpio, 0, NULL, "", "", NULL }, - { "vt_trailer_cpio", ventoy_cmd_trailer_cpio, 0, NULL, "", "", NULL }, - { "vt_push_last_entry", ventoy_cmd_push_last_entry, 0, NULL, "", "", NULL }, - { "vt_pop_last_entry", ventoy_cmd_pop_last_entry, 0, NULL, "", "", NULL }, - { "vt_get_lib_module_ver", ventoy_cmd_lib_module_ver, 0, NULL, "", "", NULL }, - - { "vt_load_part_table", ventoy_cmd_load_part_table, 0, NULL, "", "", NULL }, - { "vt_check_part_exist", ventoy_cmd_part_exist, 0, NULL, "", "", NULL }, - { "vt_get_fs_label", ventoy_cmd_get_fs_label, 0, NULL, "", "", NULL }, - { "vt_fs_enum_1st_file", ventoy_cmd_fs_enum_1st_file, 0, NULL, "", "", NULL }, - { "vt_file_basename", ventoy_cmd_basename, 0, NULL, "", "", NULL }, - { "vt_file_basefile", ventoy_cmd_basefile, 0, NULL, "", "", NULL }, - { "vt_enum_video_mode", ventoy_cmd_enum_video_mode, 0, NULL, "", "", NULL }, - { "vt_get_video_mode", ventoy_cmd_get_video_mode, 0, NULL, "", "", NULL }, - { "vt_update_cur_video_mode", vt_cmd_update_cur_video_mode, 0, NULL, "", "", NULL }, - - - { "vt_find_first_bootable_hd", ventoy_cmd_find_bootable_hdd, 0, NULL, "", "", NULL }, - { "vt_dump_menu", ventoy_cmd_dump_menu, 0, NULL, "", "", NULL }, - { "vt_dynamic_menu", ventoy_cmd_dynamic_menu, 0, NULL, "", "", NULL }, - { "vt_check_mode", ventoy_cmd_check_mode, 0, NULL, "", "", NULL }, - { "vt_dump_img_list", ventoy_cmd_dump_img_list, 0, NULL, "", "", NULL }, - { "vt_dump_injection", ventoy_cmd_dump_injection, 0, NULL, "", "", NULL }, - { "vt_dump_auto_install", ventoy_cmd_dump_auto_install, 0, NULL, "", "", NULL }, - { "vt_dump_persistence", ventoy_cmd_dump_persistence, 0, NULL, "", "", NULL }, - { "vt_select_auto_install", ventoy_cmd_sel_auto_install, 0, NULL, "", "", NULL }, - { "vt_select_persistence", ventoy_cmd_sel_persistence, 0, NULL, "", "", NULL }, - { "vt_select_conf_replace", ventoy_select_conf_replace, 0, NULL, "", "", NULL }, - - { "vt_iso9660_nojoliet", ventoy_cmd_iso9660_nojoliet, 0, NULL, "", "", NULL }, - { "vt_is_udf", ventoy_cmd_is_udf, 0, NULL, "", "", NULL }, - { "vt_file_size", ventoy_cmd_file_size, 0, NULL, "", "", NULL }, - { "vt_load_file_to_mem", ventoy_cmd_load_file_to_mem, 0, NULL, "", "", NULL }, - { "vt_load_img_memdisk", ventoy_cmd_load_img_memdisk, 0, NULL, "", "", NULL }, - { "vt_concat_efi_iso", ventoy_cmd_concat_efi_iso, 0, NULL, "", "", NULL }, - - { "vt_linux_parse_initrd_isolinux", ventoy_cmd_isolinux_initrd_collect, 0, NULL, "{cfgfile}", "", NULL }, - { "vt_linux_parse_initrd_grub", ventoy_cmd_grub_initrd_collect, 0, NULL, "{cfgfile}", "", NULL }, - { "vt_linux_specify_initrd_file", ventoy_cmd_specify_initrd_file, 0, NULL, "", "", NULL }, - { "vt_linux_clear_initrd", ventoy_cmd_clear_initrd_list, 0, NULL, "", "", NULL }, - { "vt_linux_dump_initrd", ventoy_cmd_dump_initrd_list, 0, NULL, "", "", NULL }, - { "vt_linux_initrd_count", ventoy_cmd_initrd_count, 0, NULL, "", "", NULL }, - { "vt_linux_valid_initrd_count", ventoy_cmd_valid_initrd_count, 0, NULL, "", "", NULL }, - { "vt_linux_locate_initrd", ventoy_cmd_linux_locate_initrd, 0, NULL, "", "", NULL }, - { "vt_linux_chain_data", ventoy_cmd_linux_chain_data, 0, NULL, "", "", NULL }, - { "vt_linux_get_main_initrd_index", ventoy_cmd_linux_get_main_initrd_index, 0, NULL, "", "", NULL }, - - { "vt_windows_reset", ventoy_cmd_wimdows_reset, 0, NULL, "", "", NULL }, - { "vt_windows_chain_data", ventoy_cmd_windows_chain_data, 0, NULL, "", "", NULL }, - { "vt_windows_collect_wim_patch", ventoy_cmd_collect_wim_patch, 0, NULL, "", "", NULL }, - { "vt_windows_locate_wim_patch", ventoy_cmd_locate_wim_patch, 0, NULL, "", "", NULL }, - { "vt_windows_count_wim_patch", ventoy_cmd_wim_patch_count, 0, NULL, "", "", NULL }, - { "vt_dump_wim_patch", ventoy_cmd_dump_wim_patch, 0, NULL, "", "", NULL }, - { "vt_wim_check_bootable", ventoy_cmd_wim_check_bootable, 0, NULL, "", "", NULL }, - { "vt_wim_chain_data", ventoy_cmd_wim_chain_data, 0, NULL, "", "", NULL }, - - { "vt_add_replace_file", ventoy_cmd_add_replace_file, 0, NULL, "", "", NULL }, - { "vt_test_block_list", ventoy_cmd_test_block_list, 0, NULL, "", "", NULL }, - { "vt_file_exist_nocase", ventoy_cmd_file_exist_nocase, 0, NULL, "", "", NULL }, - - - { "vt_load_plugin", ventoy_cmd_load_plugin, 0, NULL, "", "", NULL }, - { "vt_check_plugin_json", ventoy_cmd_plugin_check_json, 0, NULL, "", "", NULL }, - { "vt_check_password", ventoy_cmd_check_password, 0, NULL, "", "", NULL }, - - { "vt_1st_line", ventoy_cmd_read_1st_line, 0, NULL, "", "", NULL }, - { "vt_file_strstr", ventoy_cmd_file_strstr, 0, NULL, "", "", NULL }, - { "vt_img_part_info", ventoy_cmd_img_part_info, 0, NULL, "", "", NULL }, - - - { "vt_parse_iso_volume", ventoy_cmd_parse_volume, 0, NULL, "", "", NULL }, - { "vt_parse_iso_create_date", ventoy_cmd_parse_create_date, 0, NULL, "", "", NULL }, - { "vt_parse_freenas_ver", ventoy_cmd_parse_freenas_ver, 0, NULL, "", "", NULL }, - { "vt_unix_parse_freebsd_ver", ventoy_cmd_unix_freebsd_ver, 0, NULL, "", "", NULL }, - { "vt_unix_parse_freebsd_ver_elf", ventoy_cmd_unix_freebsd_ver_elf, 0, NULL, "", "", NULL }, - { "vt_unix_reset", ventoy_cmd_unix_reset, 0, NULL, "", "", NULL }, - { "vt_unix_replace_conf", ventoy_cmd_unix_replace_conf, 0, NULL, "", "", NULL }, - { "vt_unix_replace_ko", ventoy_cmd_unix_replace_ko, 0, NULL, "", "", NULL }, - { "vt_unix_fill_image_desc", ventoy_cmd_unix_fill_image_desc, 0, NULL, "", "", NULL }, - { "vt_unix_gzip_new_ko", ventoy_cmd_unix_gzip_newko, 0, NULL, "", "", NULL }, - { "vt_unix_chain_data", ventoy_cmd_unix_chain_data, 0, NULL, "", "", NULL }, - - { "vt_img_hook_root", ventoy_cmd_img_hook_root, 0, NULL, "", "", NULL }, - { "vt_img_unhook_root", ventoy_cmd_img_unhook_root, 0, NULL, "", "", NULL }, - { "vt_acpi_param", ventoy_cmd_acpi_param, 0, NULL, "", "", NULL }, - { "vt_check_secureboot_var", ventoy_cmd_check_secureboot_var, 0, NULL, "", "", NULL }, - { "vt_clear_key", ventoy_cmd_clear_key, 0, NULL, "", "", NULL }, - { "vt_img_check_range", ventoy_cmd_img_check_range, 0, NULL, "", "", NULL }, - -}; - - - -GRUB_MOD_INIT(ventoy) -{ - grub_uint32_t i; - cmd_para *cur = NULL; - - ventoy_env_init(); - - ventoy_arch_mode_init(); - - for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++) - { - cur = ventoy_cmds + i; - cur->cmd = grub_register_extcmd(cur->name, cur->func, cur->flags, - cur->summary, cur->description, cur->parser); - } -} - -GRUB_MOD_FINI(ventoy) -{ - grub_uint32_t i; - - for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++) - { - grub_unregister_extcmd(ventoy_cmds[i].cmd); - } + ventoy_unregister_all_cmd(); } diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c new file mode 100644 index 00000000..9f0a8b8f --- /dev/null +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_cmd.c @@ -0,0 +1,4497 @@ +/****************************************************************************** + * ventoy_cmd.c + * + * Copyright (c) 2021, longpanda + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef GRUB_MACHINE_EFI +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "ventoy_def.h" +#include "miniz.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +initrd_info *g_initrd_img_list = NULL; +initrd_info *g_initrd_img_tail = NULL; +int g_initrd_img_count = 0; +int g_valid_initrd_count = 0; +int g_default_menu_mode = 0; +int g_filt_dot_underscore_file = 0; +int g_sort_case_sensitive = 0; +int g_tree_view_menu_style = 0; +static grub_file_t g_old_file; +static int g_ventoy_last_entry_back; + +char g_iso_path[256]; +char g_img_swap_tmp_buf[1024]; +img_info g_img_swap_tmp; +img_info *g_ventoy_img_list = NULL; + +int g_ventoy_img_count = 0; + +grub_device_t g_enum_dev = NULL; +grub_fs_t g_enum_fs = NULL; +int g_img_max_search_level = -1; +img_iterator_node g_img_iterator_head; +img_iterator_node *g_img_iterator_tail = NULL; + +grub_uint8_t g_ventoy_break_level = 0; +grub_uint8_t g_ventoy_debug_level = 0; +grub_uint8_t g_ventoy_chain_type = 0; + +grub_uint8_t *g_ventoy_cpio_buf = NULL; +grub_uint32_t g_ventoy_cpio_size = 0; +cpio_newc_header *g_ventoy_initrd_head = NULL; +grub_uint8_t *g_ventoy_runtime_buf = NULL; + +int g_plugin_image_list = 0; + +ventoy_grub_param *g_grub_param = NULL; + +ventoy_guid g_ventoy_guid = VENTOY_GUID; + +ventoy_img_chunk_list g_img_chunk_list; + +int g_wimboot_enable = 0; +ventoy_img_chunk_list g_wimiso_chunk_list; +char *g_wimiso_path = NULL; + +int g_vhdboot_enable = 0; + +grub_uint64_t g_conf_replace_offset = 0; +grub_uint64_t g_svd_replace_offset = 0; +conf_replace *g_conf_replace_node = NULL; +grub_uint8_t *g_conf_replace_new_buf = NULL; +int g_conf_replace_new_len = 0; +int g_conf_replace_new_len_align = 0; + +ventoy_gpt_info *g_ventoy_part_info = NULL; +grub_uint64_t g_ventoy_disk_size = 0; +grub_uint64_t g_ventoy_disk_part_size[2]; + +static char *g_tree_script_buf = NULL; +static int g_tree_script_pos = 0; + +static char *g_list_script_buf = NULL; +static int g_list_script_pos = 0; + +static char *g_part_list_buf = NULL; +static int g_part_list_pos = 0; + +static int g_video_mode_max = 0; +static int g_video_mode_num = 0; +static ventoy_video_mode *g_video_mode_list = NULL; + +static int g_enumerate_time_checked = 0; +static grub_uint64_t g_enumerate_start_time_ms; +static grub_uint64_t g_enumerate_finish_time_ms; +static int g_vtoy_file_flt[VTOY_FILE_FLT_BUTT] = {0}; + +static const char *g_menu_class[] = +{ + "vtoyiso", "vtoywim", "vtoyefi", "vtoyimg", "vtoyvhd", "vtoyvtoy" +}; + +static const char *g_menu_prefix[] = +{ + "iso", "wim", "efi", "img", "vhd", "vtoy" +}; + +static int ventoy_get_fs_type(const char *fs) +{ + if (NULL == fs) + { + return ventoy_fs_max; + } + else if (grub_strncmp(fs, "exfat", 5) == 0) + { + return ventoy_fs_exfat; + } + else if (grub_strncmp(fs, "ntfs", 4) == 0) + { + return ventoy_fs_ntfs; + } + else if (grub_strncmp(fs, "ext", 3) == 0) + { + return ventoy_fs_ext; + } + else if (grub_strncmp(fs, "xfs", 3) == 0) + { + return ventoy_fs_xfs; + } + else if (grub_strncmp(fs, "udf", 3) == 0) + { + return ventoy_fs_udf; + } + else if (grub_strncmp(fs, "fat", 3) == 0) + { + return ventoy_fs_fat; + } + + return ventoy_fs_max; +} + +static int ventoy_string_check(const char *str, grub_char_check_func check) +{ + if (!str) + { + return 0; + } + + for ( ; *str; str++) + { + if (!check(*str)) + { + return 0; + } + } + + return 1; +} + + +static grub_ssize_t ventoy_fs_read(grub_file_t file, char *buf, grub_size_t len) +{ + grub_memcpy(buf, (char *)file->data + file->offset, len); + return len; +} + +static int ventoy_control_get_flag(const char *key) +{ + const char *val = ventoy_get_env(key); + + if (val && val[0] == '1' && val[1] == 0) + { + return 1; + } + return 0; +} + +static grub_err_t ventoy_fs_close(grub_file_t file) +{ + grub_file_close(g_old_file); + grub_free(file->data); + + file->device = 0; + file->name = 0; + + return 0; +} + +static int ventoy_video_hook(const struct grub_video_mode_info *info, void *hook_arg) +{ + int i; + + (void)hook_arg; + + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_PURE_TEXT) + { + return 0; + } + + for (i = 0; i < g_video_mode_num; i++) + { + if (g_video_mode_list[i].width == info->width && + g_video_mode_list[i].height == info->height && + g_video_mode_list[i].bpp == info->bpp) + { + return 0; + } + } + + g_video_mode_list[g_video_mode_num].width = info->width; + g_video_mode_list[g_video_mode_num].height = info->height; + g_video_mode_list[g_video_mode_num].bpp = info->bpp; + g_video_mode_num++; + + if (g_video_mode_num == g_video_mode_max) + { + g_video_mode_max *= 2; + g_video_mode_list = grub_realloc(g_video_mode_list, g_video_mode_max * sizeof(ventoy_video_mode)); + } + + return 0; +} + +static int ventoy_video_mode_cmp(ventoy_video_mode *v1, ventoy_video_mode *v2) +{ + if (v1->bpp == v2->bpp) + { + if (v1->width == v2->width) + { + if (v1->height == v2->height) + { + return 0; + } + else + { + return (v1->height < v2->height) ? -1 : 1; + } + } + else + { + return (v1->width < v2->width) ? -1 : 1; + } + } + else + { + return (v1->bpp < v2->bpp) ? -1 : 1; + } +} + +static int ventoy_enum_video_mode(void) +{ + int i, j; + grub_video_adapter_t adapter; + grub_video_driver_id_t id; + ventoy_video_mode mode; + + g_video_mode_num = 0; + g_video_mode_max = 1024; + g_video_mode_list = grub_malloc(sizeof(ventoy_video_mode) * g_video_mode_max); + if (!g_video_mode_list) + { + return 0; + } + + #ifdef GRUB_MACHINE_PCBIOS + grub_dl_load ("vbe"); + #endif + + id = grub_video_get_driver_id (); + + FOR_VIDEO_ADAPTERS (adapter) + { + if (!adapter->iterate || + (adapter->id != id && (id != GRUB_VIDEO_DRIVER_NONE || + adapter->init() != GRUB_ERR_NONE))) + { + continue; + } + + adapter->iterate(ventoy_video_hook, NULL); + + if (adapter->id != id) + { + adapter->fini(); + } + } + + /* sort video mode */ + for (i = 0; i < g_video_mode_num; i++) + for (j = i + 1; j < g_video_mode_num; j++) + { + if (ventoy_video_mode_cmp(g_video_mode_list + i, g_video_mode_list + j) < 0) + { + grub_memcpy(&mode, g_video_mode_list + i, sizeof(ventoy_video_mode)); + grub_memcpy(g_video_mode_list + i, g_video_mode_list + j, sizeof(ventoy_video_mode)); + grub_memcpy(g_video_mode_list + j, &mode, sizeof(ventoy_video_mode)); + } + } + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_file_t ventoy_wrapper_open(grub_file_t rawFile, enum grub_file_type type) +{ + int len; + grub_file_t file; + static struct grub_fs vtoy_fs = + { + .name = "vtoy", + .fs_dir = 0, + .fs_open = 0, + .fs_read = ventoy_fs_read, + .fs_close = ventoy_fs_close, + .fs_label = 0, + .next = 0 + }; + + if (type != 52) + { + return rawFile; + } + + file = (grub_file_t)grub_zalloc(sizeof (*file)); + if (!file) + { + return 0; + } + + file->data = grub_malloc(rawFile->size + 4096); + if (!file->data) + { + return 0; + } + + grub_file_read(rawFile, file->data, rawFile->size); + len = ventoy_fill_data(4096, (char *)file->data + rawFile->size); + + g_old_file = rawFile; + + file->size = rawFile->size + len; + file->device = rawFile->device; + file->fs = &vtoy_fs; + file->not_easily_seekable = 1; + + return file; +} + +static int ventoy_check_decimal_var(const char *name, long *value) +{ + const char *value_str = NULL; + + value_str = grub_env_get(name); + if (NULL == value_str) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s not found", name); + } + + if (!ventoy_is_decimal(value_str)) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s value '%s' is not an integer", name, value_str); + } + + *value = grub_strtol(value_str, NULL, 10); + + return GRUB_ERR_NONE; +} + +grub_uint64_t ventoy_get_vtoy_partsize(int part) +{ + grub_uint64_t sectors; + + if (grub_strncmp(g_ventoy_part_info->Head.Signature, "EFI PART", 8) == 0) + { + sectors = g_ventoy_part_info->PartTbl[part].LastLBA + 1 - g_ventoy_part_info->PartTbl[part].StartLBA; + } + else + { + sectors = g_ventoy_part_info->MBR.PartTbl[part].SectorCount; + } + + return sectors * 512; +} + +static int ventoy_load_efiboot_template(char **buf, int *datalen, int *direntoff) +{ + int len; + grub_file_t file; + char exec[128]; + char *data = NULL; + grub_uint32_t offset; + + file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s/ventoy/ventoy_efiboot.img.xz", ventoy_get_env("vtoy_efi_part")); + if (file == NULL) + { + debug("failed to open file <%s>\n", "ventoy_efiboot.img.xz"); + return 1; + } + + len = (int)file->size; + + data = (char *)grub_malloc(file->size); + if (!data) + { + return 1; + } + + grub_file_read(file, data, file->size); + grub_file_close(file); + + grub_snprintf(exec, sizeof(exec), "loopback efiboot mem:0x%llx:size:%d", (ulonglong)(ulong)data, len); + grub_script_execute_sourcecode(exec); + + file = grub_file_open("(efiboot)/EFI/BOOT/BOOTX64.EFI", GRUB_FILE_TYPE_LINUX_INITRD); + offset = (grub_uint32_t)grub_iso9660_get_last_file_dirent_pos(file); + grub_file_close(file); + + grub_script_execute_sourcecode("loopback -d efiboot"); + + *buf = data; + *datalen = len; + *direntoff = offset + 2; + + return 0; +} + +static int ventoy_set_check_result(int ret) +{ + char buf[32]; + + grub_snprintf(buf, sizeof(buf), "%d", (ret & 0x7FFF)); + grub_env_set("VTOY_CHKDEV_RESULT_STRING", buf); + grub_env_export("VTOY_CHKDEV_RESULT_STRING"); + + if (ret) + { + grub_printf(VTOY_WARNING"\n"); + grub_printf(VTOY_WARNING"\n"); + grub_printf(VTOY_WARNING"\n\n\n"); + + grub_printf("This is NOT a standard Ventoy device and is NOT supported (0x%x).\n\n", ret); + grub_printf("You should follow the instructions in https://www.ventoy.net to use Ventoy.\n"); + + grub_printf("\n\nWill exit after 10 seconds ...... "); + grub_refresh(); + grub_sleep(10); + } + + return ret; +} + +static int ventoy_check_official_device(grub_device_t dev) +{ + int workaround = 0; + grub_file_t file; + grub_uint64_t offset; + char devname[64]; + grub_fs_t fs; + grub_device_t dev2; + char *label = NULL; + struct grub_partition *partition; + + if (dev->disk == NULL || dev->disk->partition == NULL) + { + return ventoy_set_check_result(1 | 0x1000); + } + + if (0 == ventoy_check_file_exist("(%s,2)/ventoy/ventoy.cpio", dev->disk->name) || + 0 == ventoy_check_file_exist("(%s,2)/grub/localboot.cfg", dev->disk->name) || + 0 == ventoy_check_file_exist("(%s,2)/tool/mount.exfat-fuse_aarch64", dev->disk->name)) + { + #ifndef GRUB_MACHINE_EFI + if (0 == ventoy_check_file_exist("(ventoydisk)/ventoy/ventoy.cpio", dev->disk->name) || + 0 == ventoy_check_file_exist("(ventoydisk)/grub/localboot.cfg", dev->disk->name) || + 0 == ventoy_check_file_exist("(ventoydisk)/tool/mount.exfat-fuse_aarch64", dev->disk->name)) + { + return ventoy_set_check_result(2 | 0x1000); + } + else + { + workaround = 1; + } + #endif + } + + /* We must have partition 2 */ + if (workaround) + { + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", "(ventoydisk)/ventoy/ventoy.cpio"); + } + else + { + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(%s,2)/ventoy/ventoy.cpio", dev->disk->name); + } + if (!file) + { + return ventoy_set_check_result(3 | 0x1000); + } + + if (NULL == grub_strstr(file->fs->name, "fat")) + { + grub_file_close(file); + return ventoy_set_check_result(4 | 0x1000); + } + + partition = dev->disk->partition; + if (partition->number != 0 || partition->start != 2048) + { + return ventoy_set_check_result(5); + } + + if (workaround) + { + if (grub_strncmp(g_ventoy_part_info->Head.Signature, "EFI PART", 8) == 0) + { + ventoy_gpt_part_tbl *PartTbl = g_ventoy_part_info->PartTbl; + if (PartTbl[1].StartLBA != PartTbl[0].LastLBA + 1 || + (PartTbl[1].LastLBA + 1 - PartTbl[1].StartLBA) != 65536) + { + grub_file_close(file); + return ventoy_set_check_result(6); + } + } + else + { + ventoy_part_table *PartTbl = g_ventoy_part_info->MBR.PartTbl; + if (PartTbl[1].StartSectorId != PartTbl[0].StartSectorId + PartTbl[0].SectorCount || + PartTbl[1].SectorCount != 65536) + { + grub_file_close(file); + return ventoy_set_check_result(6); + } + } + } + else + { + offset = partition->start + partition->len; + partition = file->device->disk->partition; + if ((partition->number != 1) || (partition->len != 65536) || (offset != partition->start)) + { + grub_file_close(file); + return ventoy_set_check_result(7); + } + } + + grub_file_close(file); + + if (workaround == 0) + { + grub_snprintf(devname, sizeof(devname), "%s,2", dev->disk->name); + dev2 = grub_device_open(devname); + if (!dev2) + { + return ventoy_set_check_result(8); + } + + fs = grub_fs_probe(dev2); + if (!fs) + { + grub_device_close(dev2); + return ventoy_set_check_result(9); + } + + fs->fs_label(dev2, &label); + if ((!label) || grub_strncmp("VTOYEFI", label, 7)) + { + grub_device_close(dev2); + return ventoy_set_check_result(10); + } + + grub_device_close(dev2); + } + + return ventoy_set_check_result(0); +} + +static int ventoy_check_ignore_flag(const char *filename, const struct grub_dirhook_info *info, void *data) +{ + if (0 == info->dir) + { + if (filename && filename[0] == '.' && 0 == grub_strncmp(filename, ".ventoyignore", 13)) + { + *((int *)data) = 1; + return 0; + } + } + + return 0; +} + +grub_uint64_t ventoy_grub_get_file_size(const char *fmt, ...) +{ + grub_uint64_t size = 0; + grub_file_t file; + va_list ap; + char fullpath[256] = {0}; + + va_start (ap, fmt); + grub_vsnprintf(fullpath, 255, fmt, ap); + va_end (ap); + + file = grub_file_open(fullpath, VENTOY_FILE_TYPE); + if (!file) + { + debug("grub_file_open failed <%s>\n", fullpath); + grub_errno = 0; + return 0; + } + + size = file->size; + grub_file_close(file); + return size; +} + +grub_file_t ventoy_grub_file_open(enum grub_file_type type, const char *fmt, ...) +{ + va_list ap; + grub_file_t file; + char fullpath[256] = {0}; + + va_start (ap, fmt); + grub_vsnprintf(fullpath, 255, fmt, ap); + va_end (ap); + + file = grub_file_open(fullpath, type); + if (!file) + { + debug("grub_file_open failed <%s> %d\n", fullpath, grub_errno); + grub_errno = 0; + } + + return file; +} + +int ventoy_is_file_exist(const char *fmt, ...) +{ + va_list ap; + int len; + char *pos = NULL; + char buf[256] = {0}; + + grub_snprintf(buf, sizeof(buf), "%s", "[ -f \""); + pos = buf + 6; + + va_start (ap, fmt); + len = grub_vsnprintf(pos, 255, fmt, ap); + va_end (ap); + + grub_strncpy(pos + len, "\" ]", 3); + + debug("script exec %s\n", buf); + + if (0 == grub_script_execute_sourcecode(buf)) + { + return 1; + } + + return 0; +} + +int ventoy_is_dir_exist(const char *fmt, ...) +{ + va_list ap; + int len; + char *pos = NULL; + char buf[256] = {0}; + + grub_snprintf(buf, sizeof(buf), "%s", "[ -d \""); + pos = buf + 6; + + va_start (ap, fmt); + len = grub_vsnprintf(pos, 255, fmt, ap); + va_end (ap); + + grub_strncpy(pos + len, "\" ]", 3); + + debug("script exec %s\n", buf); + + if (0 == grub_script_execute_sourcecode(buf)) + { + return 1; + } + + return 0; +} + +int ventoy_gzip_compress(void *mem_in, int mem_in_len, void *mem_out, int mem_out_len) +{ + mz_stream s; + grub_uint8_t *outbuf; + grub_uint8_t gzHdr[10] = + { + 0x1F, 0x8B, /* magic */ + 8, /* z method */ + 0, /* flags */ + 0,0,0,0, /* mtime */ + 4, /* xfl */ + 3, /* OS */ + }; + + grub_memset(&s, 0, sizeof(mz_stream)); + + mz_deflateInit2(&s, 1, MZ_DEFLATED, -MZ_DEFAULT_WINDOW_BITS, 6, MZ_DEFAULT_STRATEGY); + + outbuf = (grub_uint8_t *)mem_out; + + mem_out_len -= sizeof(gzHdr) + 8; + grub_memcpy(outbuf, gzHdr, sizeof(gzHdr)); + outbuf += sizeof(gzHdr); + + s.avail_in = mem_in_len; + s.next_in = mem_in; + + s.avail_out = mem_out_len; + s.next_out = outbuf; + + mz_deflate(&s, MZ_FINISH); + + mz_deflateEnd(&s); + + outbuf += s.total_out; + *(grub_uint32_t *)outbuf = grub_getcrc32c(0, outbuf, s.total_out); + *(grub_uint32_t *)(outbuf + 4) = (grub_uint32_t)(s.total_out); + + return s.total_out + sizeof(gzHdr) + 8; +} + + +#if 0 +ventoy grub cmds +#endif + +static grub_err_t ventoy_cmd_debug(grub_extcmd_context_t ctxt, int argc, char **args) +{ + if (argc != 1) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {on|off}", cmd_raw_name); + } + + if (0 == grub_strcmp(args[0], "on")) + { + g_ventoy_debug = 1; + grub_env_set("vtdebug_flag", "debug"); + } + else + { + g_ventoy_debug = 0; + grub_env_set("vtdebug_flag", ""); + } + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_break(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + + if (argc < 1 || (args[0][0] != '0' && args[0][0] != '1')) + { + grub_printf("Usage: %s {level} [debug]\r\n", cmd_raw_name); + grub_printf(" level:\r\n"); + grub_printf(" 01/11: busybox / (+cat log)\r\n"); + grub_printf(" 02/12: initrd / (+cat log)\r\n"); + grub_printf(" 03/13: hook / (+cat log)\r\n"); + grub_printf("\r\n"); + grub_printf(" debug:\r\n"); + grub_printf(" 0: debug is off\r\n"); + grub_printf(" 1: debug is on\r\n"); + grub_printf("\r\n"); + VENTOY_CMD_RETURN(GRUB_ERR_NONE); + } + + g_ventoy_break_level = (grub_uint8_t)grub_strtoul(args[0], NULL, 16); + + if (argc > 1 && grub_strtoul(args[1], NULL, 10) > 0) + { + g_ventoy_debug_level = 1; + } + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_strstr(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + + if (argc != 2) + { + return 1; + } + + return (grub_strstr(args[0], args[1])) ? 0 : 1; +} + +static grub_err_t ventoy_cmd_strbegin(grub_extcmd_context_t ctxt, int argc, char **args) +{ + char *c0, *c1; + + (void)ctxt; + + if (argc != 2) + { + return 1; + } + + c0 = args[0]; + c1 = args[1]; + + while (*c0 && *c1) + { + if (*c0 != *c1) + { + return 1; + } + c0++; + c1++; + } + + if (*c1) + { + return 1; + } + + return 0; +} + +static grub_err_t ventoy_cmd_incr(grub_extcmd_context_t ctxt, int argc, char **args) +{ + long value_long = 0; + char buf[32]; + + if ((argc != 2) || (!ventoy_is_decimal(args[1]))) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Variable} {Int}", cmd_raw_name); + } + + if (GRUB_ERR_NONE != ventoy_check_decimal_var(args[0], &value_long)) + { + return grub_errno; + } + + value_long += grub_strtol(args[1], NULL, 10); + + grub_snprintf(buf, sizeof(buf), "%ld", value_long); + grub_env_set(args[0], buf); + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_mod(grub_extcmd_context_t ctxt, int argc, char **args) +{ + ulonglong value1 = 0; + ulonglong value2 = 0; + char buf[32]; + + if (argc != 3) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int} {Int} {Variable}", cmd_raw_name); + } + + value1 = grub_strtoull(args[0], NULL, 10); + value2 = grub_strtoull(args[1], NULL, 10); + + grub_snprintf(buf, sizeof(buf), "%llu", (value1 & (value2 - 1))); + grub_env_set(args[2], buf); + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_file_size(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc = 1; + char buf[32]; + grub_file_t file; + + (void)ctxt; + (void)argc; + (void)args; + + if (argc != 2) + { + return rc; + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (file == NULL) + { + debug("failed to open file <%s> for udf check\n", args[0]); + return 1; + } + + grub_snprintf(buf, sizeof(buf), "%llu", (unsigned long long)file->size); + + grub_env_set(args[1], buf); + + grub_file_close(file); + rc = 0; + + return rc; +} + +static grub_err_t ventoy_cmd_load_wimboot(grub_extcmd_context_t ctxt, int argc, char **args) +{ + grub_file_t file; + + (void)ctxt; + (void)argc; + (void)args; + + g_wimboot_enable = 0; + grub_check_free(g_wimiso_path); + grub_check_free(g_wimiso_chunk_list.chunk); + + file = grub_file_open(args[0], VENTOY_FILE_TYPE); + if (!file) + { + return 0; + } + + grub_memset(&g_wimiso_chunk_list, 0, sizeof(g_wimiso_chunk_list)); + g_wimiso_chunk_list.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); + if (NULL == g_wimiso_chunk_list.chunk) + { + return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); + } + + g_wimiso_chunk_list.max_chunk = DEFAULT_CHUNK_NUM; + g_wimiso_chunk_list.cur_chunk = 0; + + ventoy_get_block_list(file, &g_wimiso_chunk_list, file->device->disk->partition->start); + + g_wimboot_enable = 1; + g_wimiso_path = grub_strdup(args[0]); + + grub_file_close(file); + + return 0; +} + +static grub_err_t ventoy_cmd_concat_efi_iso(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int len = 0; + int totlen = 0; + int offset = 0; + grub_file_t file; + char name[32]; + char value[32]; + char *buf = NULL; + char *data = NULL; + ventoy_iso9660_override *dirent; + + (void)ctxt; + + if (argc != 2) + { + return 1; + } + + totlen = sizeof(ventoy_chain_head); + + if (ventoy_load_efiboot_template(&buf, &len, &offset)) + { + debug("failed to load efiboot template %d\n", len); + return 1; + } + + totlen += len; + + debug("efiboot template len:%d offset:%d\n", len, offset); + + file = ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD, "%s", args[0]); + if (file == NULL) + { + debug("failed to open file <%s>\n", args[0]); + return 1; + } + + totlen += ventoy_align_2k(file->size); + + dirent = (ventoy_iso9660_override *)(buf + offset); + dirent->first_sector = len / 2048; + dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector); + dirent->size = (grub_uint32_t)file->size; + dirent->size_be = grub_swap_bytes32(dirent->size); + + debug("rawiso len:%d efilen:%d total:%d\n", len, (int)file->size, totlen); + +#ifdef GRUB_MACHINE_EFI + data = (char *)grub_efi_allocate_iso_buf(totlen); +#else + data = (char *)grub_malloc(totlen); +#endif + + ventoy_fill_os_param(file, (ventoy_os_param *)data); + + grub_memcpy(data + sizeof(ventoy_chain_head), buf, len); + grub_check_free(buf); + + grub_file_read(file, data + sizeof(ventoy_chain_head) + len, file->size); + grub_file_close(file); + + grub_snprintf(name, sizeof(name), "%s_addr", args[1]); + grub_snprintf(value, sizeof(value), "0x%llx", (ulonglong)(ulong)data); + grub_env_set(name, value); + + grub_snprintf(name, sizeof(name), "%s_size", args[1]); + grub_snprintf(value, sizeof(value), "%d", (int)(totlen)); + grub_env_set(name, value); + + return 0; +} + +static grub_err_t ventoy_cmd_load_file_to_mem(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc = 1; + char name[32]; + char value[32]; + char *buf = NULL; + grub_file_t file; + + (void)ctxt; + (void)argc; + (void)args; + + if (argc != 2) + { + return rc; + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (file == NULL) + { + debug("failed to open file <%s>\n", args[0]); + return 1; + } + +#ifdef GRUB_MACHINE_EFI + buf = (char *)grub_efi_allocate_iso_buf(file->size); +#else + buf = (char *)grub_malloc(file->size); +#endif + + grub_file_read(file, buf, file->size); + + grub_snprintf(name, sizeof(name), "%s_addr", args[1]); + grub_snprintf(value, sizeof(value), "0x%llx", (unsigned long long)(unsigned long)buf); + grub_env_set(name, value); + + grub_snprintf(name, sizeof(name), "%s_size", args[1]); + grub_snprintf(value, sizeof(value), "%llu", (unsigned long long)file->size); + grub_env_set(name, value); + + grub_file_close(file); + rc = 0; + + return rc; +} + +static grub_err_t ventoy_cmd_load_img_memdisk(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc = 1; + int headlen; + char name[32]; + char value[32]; + char *buf = NULL; + grub_file_t file; + + (void)ctxt; + (void)argc; + (void)args; + + if (argc != 2) + { + return rc; + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (file == NULL) + { + debug("failed to open file <%s> for udf check\n", args[0]); + return 1; + } + + headlen = sizeof(ventoy_chain_head); + +#ifdef GRUB_MACHINE_EFI + buf = (char *)grub_efi_allocate_iso_buf(headlen + file->size); +#else + buf = (char *)grub_malloc(headlen + file->size); +#endif + + ventoy_fill_os_param(file, (ventoy_os_param *)buf); + + grub_file_read(file, buf + headlen, file->size); + + grub_snprintf(name, sizeof(name), "%s_addr", args[1]); + grub_snprintf(value, sizeof(value), "0x%llx", (unsigned long long)(unsigned long)buf); + grub_env_set(name, value); + + grub_snprintf(name, sizeof(name), "%s_size", args[1]); + grub_snprintf(value, sizeof(value), "%llu", (unsigned long long)file->size); + grub_env_set(name, value); + + grub_file_close(file); + rc = 0; + + return rc; +} + +static grub_err_t ventoy_cmd_iso9660_nojoliet(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + + if (argc != 1) + { + return 1; + } + + if (args[0][0] == '1') + { + grub_iso9660_set_nojoliet(1); + } + else + { + grub_iso9660_set_nojoliet(0); + } + + return 0; +} + +static grub_err_t ventoy_cmd_is_udf(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i; + int rc = 1; + grub_file_t file; + grub_uint8_t buf[32]; + + (void)ctxt; + (void)argc; + (void)args; + + if (argc != 1) + { + return rc; + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (file == NULL) + { + debug("failed to open file <%s> for udf check\n", args[0]); + return 1; + } + + for (i = 16; i < 32; i++) + { + grub_file_seek(file, i * 2048); + grub_file_read(file, buf, sizeof(buf)); + if (buf[0] == 255) + { + break; + } + } + + i++; + grub_file_seek(file, i * 2048); + grub_file_read(file, buf, sizeof(buf)); + + if (grub_memcmp(buf + 1, "BEA01", 5) == 0) + { + i++; + grub_file_seek(file, i * 2048); + grub_file_read(file, buf, sizeof(buf)); + + if (grub_memcmp(buf + 1, "NSR02", 5) == 0 || + grub_memcmp(buf + 1, "NSR03", 5) == 0) + { + rc = 0; + } + } + + grub_file_close(file); + + debug("ISO UDF: %s\n", rc ? "NO" : "YES"); + + return rc; +} + +static grub_err_t ventoy_cmd_cmp(grub_extcmd_context_t ctxt, int argc, char **args) +{ + long value_long1 = 0; + long value_long2 = 0; + + if ((argc != 3) || (!ventoy_is_decimal(args[0])) || (!ventoy_is_decimal(args[2]))) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq|ne|gt|lt|ge|le } {Int2}", cmd_raw_name); + } + + value_long1 = grub_strtol(args[0], NULL, 10); + value_long2 = grub_strtol(args[2], NULL, 10); + + if (0 == grub_strcmp(args[1], "eq")) + { + grub_errno = (value_long1 == value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; + } + else if (0 == grub_strcmp(args[1], "ne")) + { + grub_errno = (value_long1 != value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; + } + else if (0 == grub_strcmp(args[1], "gt")) + { + grub_errno = (value_long1 > value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; + } + else if (0 == grub_strcmp(args[1], "lt")) + { + grub_errno = (value_long1 < value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; + } + else if (0 == grub_strcmp(args[1], "ge")) + { + grub_errno = (value_long1 >= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; + } + else if (0 == grub_strcmp(args[1], "le")) + { + grub_errno = (value_long1 <= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE; + } + else + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq ne gt lt ge le } {Int2}", cmd_raw_name); + } + + return grub_errno; +} + +static grub_err_t ventoy_cmd_device(grub_extcmd_context_t ctxt, int argc, char **args) +{ + char *pos = NULL; + char buf[128] = {0}; + + if (argc != 2) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s path var", cmd_raw_name); + } + + grub_strncpy(buf, (args[0][0] == '(') ? args[0] + 1 : args[0], sizeof(buf) - 1); + pos = grub_strstr(buf, ","); + if (pos) + { + *pos = 0; + } + + grub_env_set(args[1], buf); + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_check_compatible(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i; + char buf[256]; + grub_disk_t disk; + char *pos = NULL; + const char *files[] = { "ventoy.dat", "VENTOY.DAT" }; + + (void)ctxt; + + if (argc != 1) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s (loop)", cmd_raw_name); + } + + for (i = 0; i < (int)ARRAY_SIZE(files); i++) + { + grub_snprintf(buf, sizeof(buf) - 1, "[ -e \"%s/%s\" ]", args[0], files[i]); + if (0 == grub_script_execute_sourcecode(buf)) + { + debug("file %s exist, ventoy_compatible YES\n", buf); + grub_env_set("ventoy_compatible", "YES"); + VENTOY_CMD_RETURN(GRUB_ERR_NONE); + } + else + { + debug("file %s NOT exist\n", buf); + } + } + + grub_snprintf(buf, sizeof(buf) - 1, "%s", args[0][0] == '(' ? (args[0] + 1) : args[0]); + pos = grub_strstr(buf, ")"); + if (pos) + { + *pos = 0; + } + + disk = grub_disk_open(buf); + if (disk) + { + grub_disk_read(disk, 16 << 2, 0, 1024, g_img_swap_tmp_buf); + grub_disk_close(disk); + + g_img_swap_tmp_buf[703] = 0; + for (i = 318; i < 703; i++) + { + if (g_img_swap_tmp_buf[i] == 'V' && + 0 == grub_strncmp(g_img_swap_tmp_buf + i, VENTOY_COMPATIBLE_STR, VENTOY_COMPATIBLE_STR_LEN)) + { + debug("Ventoy compatible string exist at %d, ventoy_compatible YES\n", i); + grub_env_set("ventoy_compatible", "YES"); + VENTOY_CMD_RETURN(GRUB_ERR_NONE); + } + } + } + else + { + debug("failed to open disk <%s>\n", buf); + } + + grub_env_set("ventoy_compatible", "NO"); + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +int ventoy_cmp_img(img_info *img1, img_info *img2) +{ + char *s1, *s2; + int c1 = 0; + int c2 = 0; + + if (g_plugin_image_list == VENTOY_IMG_WHITE_LIST) + { + return (img1->plugin_list_index - img2->plugin_list_index); + } + + for (s1 = img1->name, s2 = img2->name; *s1 && *s2; s1++, s2++) + { + c1 = *s1; + c2 = *s2; + + if (0 == g_sort_case_sensitive) + { + if (grub_islower(c1)) + { + c1 = c1 - 'a' + 'A'; + } + + if (grub_islower(c2)) + { + c2 = c2 - 'a' + 'A'; + } + } + + if (c1 != c2) + { + break; + } + } + + return (c1 - c2); +} + +static int ventoy_cmp_subdir(img_iterator_node *node1, img_iterator_node *node2) +{ + char *s1, *s2; + int c1 = 0; + int c2 = 0; + + if (g_plugin_image_list == VENTOY_IMG_WHITE_LIST) + { + return (node1->plugin_list_index - node2->plugin_list_index); + } + + for (s1 = node1->dir, s2 = node2->dir; *s1 && *s2; s1++, s2++) + { + c1 = *s1; + c2 = *s2; + + if (0 == g_sort_case_sensitive) + { + if (grub_islower(c1)) + { + c1 = c1 - 'a' + 'A'; + } + + if (grub_islower(c2)) + { + c2 = c2 - 'a' + 'A'; + } + } + + if (c1 != c2) + { + break; + } + } + + return (c1 - c2); +} + +void ventoy_swap_img(img_info *img1, img_info *img2) +{ + grub_memcpy(&g_img_swap_tmp, img1, sizeof(img_info)); + + grub_memcpy(img1, img2, sizeof(img_info)); + img1->next = g_img_swap_tmp.next; + img1->prev = g_img_swap_tmp.prev; + + g_img_swap_tmp.next = img2->next; + g_img_swap_tmp.prev = img2->prev; + grub_memcpy(img2, &g_img_swap_tmp, sizeof(img_info)); +} + +static int ventoy_img_name_valid(const char *filename, grub_size_t namelen) +{ + (void)namelen; + + if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_') + { + return 0; + } + + return 1; +} + +static int ventoy_collect_img_files(const char *filename, const struct grub_dirhook_info *info, void *data) +{ + //int i = 0; + int type = 0; + int ignore = 0; + int index = 0; + grub_size_t len; + img_info *img; + img_info *tail; + img_iterator_node *tmp; + img_iterator_node *new_node; + img_iterator_node *node = (img_iterator_node *)data; + + if (g_enumerate_time_checked == 0) + { + g_enumerate_finish_time_ms = grub_get_time_ms(); + if ((g_enumerate_finish_time_ms - g_enumerate_start_time_ms) >= 3000) + { + grub_cls(); + grub_printf("\n\n Ventoy scanning files, please wait...\n"); + grub_refresh(); + g_enumerate_time_checked = 1; + } + } + + len = grub_strlen(filename); + + if (info->dir) + { + if (node->level + 1 > g_img_max_search_level) + { + return 0; + } + + if ((len == 1 && filename[0] == '.') || + (len == 2 && filename[0] == '.' && filename[1] == '.')) + { + return 0; + } + + if (!ventoy_img_name_valid(filename, len)) + { + return 0; + } + + if (filename[0] == '$' && 0 == grub_strncmp(filename, "$RECYCLE.BIN", 12)) + { + return 0; + } + + if (g_plugin_image_list == VENTOY_IMG_WHITE_LIST) + { + grub_snprintf(g_img_swap_tmp_buf, sizeof(g_img_swap_tmp_buf), "%s%s/", node->dir, filename); + index = ventoy_plugin_get_image_list_index(vtoy_class_directory, g_img_swap_tmp_buf); + if (index == 0) + { + debug("Directory %s not found in image_list plugin config...\n", g_img_swap_tmp_buf); + return 0; + } + } + + new_node = grub_zalloc(sizeof(img_iterator_node)); + if (new_node) + { + new_node->level = node->level + 1; + new_node->plugin_list_index = index; + new_node->dirlen = grub_snprintf(new_node->dir, sizeof(new_node->dir), "%s%s/", node->dir, filename); + + g_enum_fs->fs_dir(g_enum_dev, new_node->dir, ventoy_check_ignore_flag, &ignore); + if (ignore) + { + debug("Directory %s ignored...\n", new_node->dir); + grub_free(new_node); + return 0; + } + + new_node->tail = node->tail; + + new_node->parent = node; + if (!node->firstchild) + { + node->firstchild = new_node; + } + + if (g_img_iterator_tail) + { + g_img_iterator_tail->next = new_node; + g_img_iterator_tail = new_node; + } + else + { + g_img_iterator_head.next = new_node; + g_img_iterator_tail = new_node; + } + } + } + else + { + debug("Find a file %s\n", filename); + if (len < 4) + { + return 0; + } + + if (FILE_FLT(ISO) && 0 == grub_strcasecmp(filename + len - 4, ".iso")) + { + type = img_type_iso; + } + else if (FILE_FLT(WIM) && g_wimboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".wim"))) + { + type = img_type_wim; + } + else if (FILE_FLT(VHD) && g_vhdboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".vhd") || + (len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vhdx")))) + { + type = img_type_vhd; + } + #ifdef GRUB_MACHINE_EFI + else if (FILE_FLT(EFI) && 0 == grub_strcasecmp(filename + len - 4, ".efi")) + { + type = img_type_efi; + } + #endif + else if (FILE_FLT(IMG) && 0 == grub_strcasecmp(filename + len - 4, ".img")) + { + if (len == 18 && grub_strncmp(filename, "ventoy_", 7) == 0) + { + if (grub_strncmp(filename + 7, "wimboot", 7) == 0 || + grub_strncmp(filename + 7, "vhdboot", 7) == 0) + { + return 0; + } + } + type = img_type_img; + } + else if (FILE_FLT(VTOY) && len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vtoy")) + { + type = img_type_vtoy; + } + else if (len >= 9 && 0 == grub_strcasecmp(filename + len - 5, ".vcfg")) + { + if (filename[len - 9] == '.' || (len >= 10 && filename[len - 10] == '.')) + { + grub_snprintf(g_img_swap_tmp_buf, sizeof(g_img_swap_tmp_buf), "%s%s", node->dir, filename); + ventoy_plugin_add_custom_boot(g_img_swap_tmp_buf); + } + return 0; + } + else + { + return 0; + } + + if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_') + { + return 0; + } + + if (g_plugin_image_list) + { + grub_snprintf(g_img_swap_tmp_buf, sizeof(g_img_swap_tmp_buf), "%s%s", node->dir, filename); + index = ventoy_plugin_get_image_list_index(vtoy_class_image_file, g_img_swap_tmp_buf); + if (VENTOY_IMG_WHITE_LIST == g_plugin_image_list && index == 0) + { + debug("File %s not found in image_list plugin config...\n", g_img_swap_tmp_buf); + return 0; + } + else if (VENTOY_IMG_BLACK_LIST == g_plugin_image_list && index > 0) + { + debug("File %s found in image_blacklist plugin config %d ...\n", g_img_swap_tmp_buf, index); + return 0; + } + } + + img = grub_zalloc(sizeof(img_info)); + if (img) + { + img->type = type; + img->plugin_list_index = index; + grub_snprintf(img->name, sizeof(img->name), "%s", filename); + + img->pathlen = grub_snprintf(img->path, sizeof(img->path), "%s%s", node->dir, img->name); + + img->size = info->size; + if (0 == img->size) + { + img->size = ventoy_grub_get_file_size("%s/%s%s", g_iso_path, node->dir, filename); + } + + if (img->size < VTOY_FILT_MIN_FILE_SIZE) + { + debug("img <%s> size too small %llu\n", img->name, (ulonglong)img->size); + grub_free(img); + return 0; + } + + if (g_ventoy_img_list) + { + tail = *(node->tail); + img->prev = tail; + tail->next = img; + } + else + { + g_ventoy_img_list = img; + } + + img->id = g_ventoy_img_count; + img->parent = node; + if (node && NULL == node->firstiso) + { + node->firstiso = img; + } + + node->isocnt++; + tmp = node->parent; + while (tmp) + { + tmp->isocnt++; + tmp = tmp->parent; + } + + *((img_info **)(node->tail)) = img; + g_ventoy_img_count++; + + img->alias = ventoy_plugin_get_menu_alias(vtoy_alias_image_file, img->path); + img->class = ventoy_plugin_get_menu_class(vtoy_class_image_file, img->name, img->path); + if (!img->class) + { + img->class = g_menu_class[type]; + } + img->menu_prefix = g_menu_prefix[type]; + + if (img_type_iso == type) + { + if (ventoy_plugin_check_memdisk(img->path)) + { + img->menu_prefix = "miso"; + } + } + + debug("Add %s%s to list %d\n", node->dir, filename, g_ventoy_img_count); + } + } + + return 0; +} + +int ventoy_fill_data(grub_uint32_t buflen, char *buffer) +{ + int len = GRUB_UINT_MAX; + const char *value = NULL; + char name[32] = {0}; + char plat[32] = {0}; + char guidstr[32] = {0}; + ventoy_guid guid = VENTOY_GUID; + const char *fmt1 = NULL; + const char *fmt2 = NULL; + const char *fmt3 = NULL; + grub_uint32_t *puint = (grub_uint32_t *)name; + grub_uint32_t *puint2 = (grub_uint32_t *)plat; + const char fmtdata[]={ 0x39, 0x35, 0x25, 0x00, 0x35, 0x00, 0x23, 0x30, 0x30, 0x30, 0x30, 0x66, 0x66, 0x00 }; + const char fmtcode[]={ + 0x22, 0x0A, 0x2B, 0x20, 0x68, 0x62, 0x6F, 0x78, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x74, 0x6F, 0x70, + 0x20, 0x3D, 0x20, 0x25, 0x73, 0x0A, 0x20, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x3D, 0x20, 0x25, + 0x73, 0x0A, 0x20, 0x20, 0x2B, 0x20, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x7B, 0x74, 0x65, 0x78, + 0x74, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x20, 0x25, 0x73, 0x25, 0x73, 0x22, 0x20, 0x63, 0x6F, + 0x6C, 0x6F, 0x72, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x22, 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E, + 0x20, 0x3D, 0x20, 0x22, 0x6C, 0x65, 0x66, 0x74, 0x22, 0x7D, 0x0A, 0x7D, 0x0A, 0x22, 0x00 + }; + + grub_memset(name, 0, sizeof(name)); + puint[0] = grub_swap_bytes32(0x56454e54); + puint[3] = grub_swap_bytes32(0x4f4e0000); + puint[2] = grub_swap_bytes32(0x45525349); + puint[1] = grub_swap_bytes32(0x4f595f56); + value = ventoy_get_env(name); + + grub_memset(name, 0, sizeof(name)); + puint[1] = grub_swap_bytes32(0x5f544f50); + puint[0] = grub_swap_bytes32(0x56544c45); + fmt1 = ventoy_get_env(name); + if (!fmt1) + { + fmt1 = fmtdata; + } + + grub_memset(name, 0, sizeof(name)); + puint[1] = grub_swap_bytes32(0x5f4c4654); + puint[0] = grub_swap_bytes32(0x56544c45); + fmt2 = ventoy_get_env(name); + + grub_memset(name, 0, sizeof(name)); + puint[1] = grub_swap_bytes32(0x5f434c52); + puint[0] = grub_swap_bytes32(0x56544c45); + fmt3 = ventoy_get_env(name); + + grub_memcpy(guidstr, &guid, sizeof(guid)); + + puint2[0] = grub_swap_bytes32(g_ventoy_plat_data); + + /* Easter egg :) It will be appreciated if you reserve it, but NOT mandatory. */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-nonliteral" + len = grub_snprintf(buffer, buflen, fmtcode, + fmt1 ? fmt1 : fmtdata, + fmt2 ? fmt2 : fmtdata + 4, + value ? value : "", plat, guidstr, + fmt3 ? fmt3 : fmtdata + 6); + #pragma GCC diagnostic pop + + grub_memset(name, 0, sizeof(name)); + puint[0] = grub_swap_bytes32(0x76746f79); + puint[2] = grub_swap_bytes32(0x656e7365); + puint[1] = grub_swap_bytes32(0x5f6c6963); + ventoy_set_env(name, guidstr); + + return len; +} + +int ventoy_check_password(const vtoy_password *pwd, int retry) +{ + int offset; + char input[256]; + grub_uint8_t md5[16]; + + while (retry--) + { + grub_memset(input, 0, sizeof(input)); + + grub_printf("Enter password: "); + grub_refresh(); + + if (pwd->type == VTOY_PASSWORD_TXT) + { + grub_password_get(input, 128); + if (grub_strcmp(pwd->text, input) == 0) + { + return 0; + } + } + else if (pwd->type == VTOY_PASSWORD_MD5) + { + grub_password_get(input, 128); + grub_crypto_hash(GRUB_MD_MD5, md5, input, grub_strlen(input)); + if (grub_memcmp(pwd->md5, md5, 16) == 0) + { + return 0; + } + } + else if (pwd->type == VTOY_PASSWORD_SALT_MD5) + { + offset = (int)grub_snprintf(input, 128, "%s", pwd->salt); + grub_password_get(input + offset, 128); + + grub_crypto_hash(GRUB_MD_MD5, md5, input, grub_strlen(input)); + if (grub_memcmp(pwd->md5, md5, 16) == 0) + { + return 0; + } + } + + grub_printf("Invalid password!\n\n"); + grub_refresh(); + } + + return 1; +} + +static img_info * ventoy_get_min_iso(img_iterator_node *node) +{ + img_info *minimg = NULL; + img_info *img = (img_info *)(node->firstiso); + + while (img && (img_iterator_node *)(img->parent) == node) + { + if (img->select == 0 && (NULL == minimg || ventoy_cmp_img(img, minimg) < 0)) + { + minimg = img; + } + img = img->next; + } + + if (minimg) + { + minimg->select = 1; + } + + return minimg; +} + +static img_iterator_node * ventoy_get_min_child(img_iterator_node *node) +{ + img_iterator_node *Minchild = NULL; + img_iterator_node *child = node->firstchild; + + while (child && child->parent == node) + { + if (child->select == 0 && (NULL == Minchild || ventoy_cmp_subdir(child, Minchild) < 0)) + { + Minchild = child; + } + child = child->next; + } + + if (Minchild) + { + Minchild->select = 1; + } + + return Minchild; +} + +static int ventoy_dynamic_tree_menu(img_iterator_node *node) +{ + int offset = 1; + img_info *img = NULL; + const char *dir_class = NULL; + const char *dir_alias = NULL; + img_iterator_node *child = NULL; + + if (node->isocnt == 0 || node->done == 1) + { + return 0; + } + + if (node->parent && node->parent->dirlen < node->dirlen) + { + offset = node->parent->dirlen; + } + + if (node == &g_img_iterator_head) + { + if (g_default_menu_mode == 0) + { + if (g_tree_view_menu_style == 0) + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "menuentry \"%-10s [Return to ListView]\" --class=\"vtoyret\" VTOY_RET {\n " + " echo 'return ...' \n" + "}\n", "<--"); + } + else + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "menuentry \"[Return to ListView]\" --class=\"vtoyret\" VTOY_RET {\n " + " echo '%s ...' \n" + "}\n", "return"); + } + } + } + else + { + node->dir[node->dirlen - 1] = 0; + dir_class = ventoy_plugin_get_menu_class(vtoy_class_directory, node->dir, node->dir); + if (!dir_class) + { + dir_class = "vtoydir"; + } + + dir_alias = ventoy_plugin_get_menu_alias(vtoy_alias_directory, node->dir); + if (dir_alias) + { + if (g_tree_view_menu_style == 0) + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "submenu \"%-10s %s\" --class=\"%s\" --id=\"DIR_%s\" {\n", + "DIR", dir_alias, dir_class, node->dir + offset); + } + else + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "submenu \"%s\" --class=\"%s\" --id=\"DIR_%s\" {\n", + dir_alias, dir_class, node->dir + offset); + } + } + else + { + dir_alias = node->dir + offset; + + if (g_tree_view_menu_style == 0) + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "submenu \"%-10s [%s]\" --class=\"%s\" --id=\"DIR_%s\" {\n", + "DIR", dir_alias, dir_class, node->dir + offset); + } + else + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "submenu \"[%s]\" --class=\"%s\" --id=\"DIR_%s\" {\n", + dir_alias, dir_class, node->dir + offset); + } + } + + if (g_tree_view_menu_style == 0) + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n " + " echo 'return ...' \n" + "}\n", "<--"); + } + else + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n " + " echo '%s ...' \n" + "}\n", "return"); + } + } + + while ((child = ventoy_get_min_child(node)) != NULL) + { + ventoy_dynamic_tree_menu(child); + } + + while ((img = ventoy_get_min_iso(node)) != NULL) + { + if (g_tree_view_menu_style == 0) + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "menuentry \"%-10s %s%s\" --class=\"%s\" --id=\"VID_%d\" {\n" + " %s_%s \n" + "}\n", + grub_get_human_size(img->size, GRUB_HUMAN_SIZE_SHORT), + img->unsupport ? "[***********] " : "", + img->alias ? img->alias : img->name, img->class, img->id, + img->menu_prefix, + img->unsupport ? "unsupport_menuentry" : "common_menuentry"); + } + else + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, + "menuentry \"%s%s\" --class=\"%s\" --id=\"VID_%d\" {\n" + " %s_%s \n" + "}\n", + img->unsupport ? "[***********] " : "", + img->alias ? img->alias : img->name, img->class, img->id, + img->menu_prefix, + img->unsupport ? "unsupport_menuentry" : "common_menuentry"); + } + } + + if (node != &g_img_iterator_head) + { + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "%s", "}\n"); + } + + node->done = 1; + return 0; +} + +static int ventoy_set_default_menu(void) +{ + int img_len = 0; + char *pos = NULL; + char *end = NULL; + char *def = NULL; + const char *strdata = NULL; + img_info *cur = NULL; + img_info *default_node = NULL; + const char *default_image = NULL; + + default_image = ventoy_get_env("VTOY_DEFAULT_IMAGE"); + if (default_image && default_image[0] == '/') + { + img_len = grub_strlen(default_image); + + for (cur = g_ventoy_img_list; cur; cur = cur->next) + { + if (img_len == cur->pathlen && grub_strcmp(default_image, cur->path) == 0) + { + default_node = cur; + break; + } + } + + if (!default_node) + { + return 1; + } + + if (0 == g_default_menu_mode) + { + vtoy_ssprintf(g_list_script_buf, g_list_script_pos, "set default='VID_%d'\n", default_node->id); + } + else + { + def = grub_strdup(default_image); + if (!def) + { + return 1; + } + + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "set default=%c", '\''); + + strdata = ventoy_get_env("VTOY_DEFAULT_SEARCH_ROOT"); + if (strdata && strdata[0] == '/') + { + pos = def + grub_strlen(strdata); + if (*pos == '/') + { + pos++; + } + } + else + { + pos = def + 1; + } + + while ((end = grub_strchr(pos, '/')) != NULL) + { + *end = 0; + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "DIR_%s>", pos); + pos = end + 1; + } + + vtoy_ssprintf(g_tree_script_buf, g_tree_script_pos, "VID_%d'\n", default_node->id); + grub_free(def); + } + } + + return 0; +} + +static grub_err_t ventoy_cmd_clear_img(grub_extcmd_context_t ctxt, int argc, char **args) +{ + img_info *next = NULL; + img_info *cur = g_ventoy_img_list; + + (void)ctxt; + (void)argc; + (void)args; + + while (cur) + { + next = cur->next; + grub_free(cur); + cur = next; + } + + g_ventoy_img_list = NULL; + g_ventoy_img_count = 0; + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_img_name(grub_extcmd_context_t ctxt, int argc, char **args) +{ + long img_id = 0; + img_info *cur = g_ventoy_img_list; + + (void)ctxt; + + if (argc != 2 || (!ventoy_is_decimal(args[0]))) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {imageID} {var}", cmd_raw_name); + } + + img_id = grub_strtol(args[0], NULL, 10); + if (img_id >= g_ventoy_img_count) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images %ld %ld", img_id, g_ventoy_img_count); + } + + debug("Find image %ld name \n", img_id); + + while (cur && img_id > 0) + { + img_id--; + cur = cur->next; + } + + if (!cur) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images"); + } + + debug("image name is %s\n", cur->name); + + grub_env_set(args[1], cur->name); + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_ext_select_img_path(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int len = 0; + char id[32] = {0}; + img_info *cur = g_ventoy_img_list; + + (void)ctxt; + + if (argc != 1) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {var}", cmd_raw_name); + } + + len = (int)grub_strlen(args[0]); + + while (cur) + { + if (len == cur->pathlen && 0 == grub_strcmp(args[0], cur->path)) + { + break; + } + cur = cur->next; + } + + if (!cur) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such image"); + } + + grub_snprintf(id, sizeof(id), "VID_%d", cur->id); + grub_env_set("chosen", id); + grub_env_export("chosen"); + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_chosen_img_path(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int img_id = 0; + char value[32]; + char *pos = NULL; + const char *id = NULL; + img_info *cur = g_ventoy_img_list; + + (void)ctxt; + + if (argc < 1 || argc > 2) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {var}", cmd_raw_name); + } + + id = grub_env_get("chosen"); + + pos = grub_strstr(id, "VID_"); + if (pos) + { + img_id = (int)grub_strtoul(pos + 4, NULL, 10); + } + else + { + img_id = (int)grub_strtoul(id, NULL, 10); + } + + while (cur) + { + if (img_id == cur->id) + { + break; + } + cur = cur->next; + } + + if (!cur) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such image"); + } + + grub_env_set(args[0], cur->path); + + if (argc > 1) + { + grub_snprintf(value, sizeof(value), "%llu", (ulonglong)(cur->size)); + grub_env_set(args[1], value); + } + + g_svd_replace_offset = 0; + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + + +static grub_err_t ventoy_cmd_list_img(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int len; + grub_fs_t fs; + grub_device_t dev = NULL; + img_info *cur = NULL; + img_info *tail = NULL; + const char *strdata = NULL; + char *device_name = NULL; + char buf[32]; + img_iterator_node *node = NULL; + img_iterator_node *tmp = NULL; + + (void)ctxt; + + if (argc != 2) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {device} {cntvar}", cmd_raw_name); + } + + if (g_ventoy_img_list || g_ventoy_img_count) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Must clear image before list"); + } + + VTOY_CMD_CHECK(1); + + g_enumerate_time_checked = 0; + g_enumerate_start_time_ms = grub_get_time_ms(); + + strdata = ventoy_get_env("VTOY_FILT_DOT_UNDERSCORE_FILE"); + if (strdata && strdata[0] == '1' && strdata[1] == 0) + { + g_filt_dot_underscore_file = 1; + } + + strdata = ventoy_get_env("VTOY_SORT_CASE_SENSITIVE"); + if (strdata && strdata[0] == '1' && strdata[1] == 0) + { + g_sort_case_sensitive = 1; + } + + device_name = grub_file_get_device_name(args[0]); + if (!device_name) + { + goto fail; + } + + g_enum_dev = dev = grub_device_open(device_name); + if (!dev) + { + goto fail; + } + + g_enum_fs = fs = grub_fs_probe(dev); + if (!fs) + { + goto fail; + } + + if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max) + { + debug("unsupported fs:<%s>\n", fs->name); + ventoy_set_env("VTOY_NO_ISO_TIP", "unsupported file system"); + goto fail; + } + + ventoy_set_env("vtoy_iso_fs", fs->name); + + strdata = ventoy_get_env("VTOY_DEFAULT_MENU_MODE"); + if (strdata && strdata[0] == '1') + { + g_default_menu_mode = 1; + } + + grub_memset(&g_img_iterator_head, 0, sizeof(g_img_iterator_head)); + + grub_snprintf(g_iso_path, sizeof(g_iso_path), "%s", args[0]); + + strdata = ventoy_get_env("VTOY_DEFAULT_SEARCH_ROOT"); + if (strdata && strdata[0] == '/') + { + len = grub_snprintf(g_img_iterator_head.dir, sizeof(g_img_iterator_head.dir) - 1, "%s", strdata); + if (g_img_iterator_head.dir[len - 1] != '/') + { + g_img_iterator_head.dir[len++] = '/'; + } + g_img_iterator_head.dirlen = len; + } + else + { + g_img_iterator_head.dirlen = 1; + grub_strcpy(g_img_iterator_head.dir, "/"); + } + + g_img_iterator_head.tail = &tail; + + if (g_img_max_search_level < 0) + { + g_img_max_search_level = GRUB_INT_MAX; + strdata = ventoy_get_env("VTOY_MAX_SEARCH_LEVEL"); + if (strdata && ventoy_is_decimal(strdata)) + { + g_img_max_search_level = (int)grub_strtoul(strdata, NULL, 10); + } + } + + g_vtoy_file_flt[VTOY_FILE_FLT_ISO] = ventoy_control_get_flag("VTOY_FILE_FLT_ISO"); + g_vtoy_file_flt[VTOY_FILE_FLT_WIM] = ventoy_control_get_flag("VTOY_FILE_FLT_WIM"); + g_vtoy_file_flt[VTOY_FILE_FLT_EFI] = ventoy_control_get_flag("VTOY_FILE_FLT_EFI"); + g_vtoy_file_flt[VTOY_FILE_FLT_IMG] = ventoy_control_get_flag("VTOY_FILE_FLT_IMG"); + g_vtoy_file_flt[VTOY_FILE_FLT_VHD] = ventoy_control_get_flag("VTOY_FILE_FLT_VHD"); + g_vtoy_file_flt[VTOY_FILE_FLT_VTOY] = ventoy_control_get_flag("VTOY_FILE_FLT_VTOY"); + + for (node = &g_img_iterator_head; node; node = node->next) + { + fs->fs_dir(dev, node->dir, ventoy_collect_img_files, node); + } + + strdata = ventoy_get_env("VTOY_TREE_VIEW_MENU_STYLE"); + if (strdata && strdata[0] == '1' && strdata[1] == 0) + { + g_tree_view_menu_style = 1; + } + + ventoy_set_default_menu(); + + for (node = &g_img_iterator_head; node; node = node->next) + { + ventoy_dynamic_tree_menu(node); + } + + /* free node */ + node = g_img_iterator_head.next; + while (node) + { + tmp = node->next; + grub_free(node); + node = tmp; + } + + /* sort image list by image name */ + for (cur = g_ventoy_img_list; cur; cur = cur->next) + { + for (tail = cur->next; tail; tail = tail->next) + { + if (ventoy_cmp_img(cur, tail) > 0) + { + ventoy_swap_img(cur, tail); + } + } + } + + if (g_default_menu_mode == 1) + { + vtoy_ssprintf(g_list_script_buf, g_list_script_pos, + "menuentry \"%s [Return to TreeView]\" --class=\"vtoyret\" VTOY_RET {\n " + " echo 'return ...' \n" + "}\n", "<--"); + } + + for (cur = g_ventoy_img_list; cur; cur = cur->next) + { + vtoy_ssprintf(g_list_script_buf, g_list_script_pos, + "menuentry \"%s%s\" --class=\"%s\" --id=\"VID_%d\" {\n" + " %s_%s \n" + "}\n", + cur->unsupport ? "[***********] " : "", + cur->alias ? cur->alias : cur->name, cur->class, cur->id, + cur->menu_prefix, + cur->unsupport ? "unsupport_menuentry" : "common_menuentry"); + } + + g_tree_script_buf[g_tree_script_pos] = 0; + g_list_script_buf[g_list_script_pos] = 0; + + grub_snprintf(buf, sizeof(buf), "%d", g_ventoy_img_count); + grub_env_set(args[1], buf); + +fail: + + check_free(device_name, grub_free); + check_free(dev, grub_device_close); + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +int ventoy_get_disk_guid(const char *filename, grub_uint8_t *guid, grub_uint8_t *signature) +{ + grub_disk_t disk; + char *device_name; + char *pos; + char *pos2; + + device_name = grub_file_get_device_name(filename); + if (!device_name) + { + return 1; + } + + pos = device_name; + if (pos[0] == '(') + { + pos++; + } + + pos2 = grub_strstr(pos, ","); + if (!pos2) + { + pos2 = grub_strstr(pos, ")"); + } + + if (pos2) + { + *pos2 = 0; + } + + disk = grub_disk_open(pos); + if (disk) + { + grub_disk_read(disk, 0, 0x180, 16, guid); + grub_disk_read(disk, 0, 0x1b8, 4, signature); + grub_disk_close(disk); + } + else + { + return 1; + } + + grub_free(device_name); + return 0; +} + +grub_uint32_t ventoy_get_iso_boot_catlog(grub_file_t file) +{ + eltorito_descriptor desc; + + grub_memset(&desc, 0, sizeof(desc)); + grub_file_seek(file, 17 * 2048); + grub_file_read(file, &desc, sizeof(desc)); + + if (desc.type != 0 || desc.version != 1) + { + return 0; + } + + if (grub_strncmp((char *)desc.id, "CD001", 5) != 0 || + grub_strncmp((char *)desc.system_id, "EL TORITO SPECIFICATION", 23) != 0) + { + return 0; + } + + return desc.sector; +} + +int ventoy_has_efi_eltorito(grub_file_t file, grub_uint32_t sector) +{ + int i; + int x86count = 0; + grub_uint8_t buf[512]; + grub_uint8_t parttype[] = { 0x04, 0x06, 0x0B, 0x0C }; + + grub_file_seek(file, sector * 2048); + grub_file_read(file, buf, sizeof(buf)); + + if (buf[0] == 0x01 && buf[1] == 0xEF) + { + debug("%s efi eltorito in Validation Entry\n", file->name); + return 1; + } + + if (buf[0] == 0x01 && buf[1] == 0x00) + { + x86count++; + } + + for (i = 64; i < (int)sizeof(buf); i += 32) + { + if ((buf[i] == 0x90 || buf[i] == 0x91) && buf[i + 1] == 0xEF) + { + debug("%s efi eltorito offset %d 0x%02x\n", file->name, i, buf[i]); + return 1; + } + + if ((buf[i] == 0x90 || buf[i] == 0x91) && buf[i + 1] == 0x00 && x86count == 1) + { + debug("0x9100 assume %s efi eltorito offset %d 0x%02x\n", file->name, i, buf[i]); + return 1; + } + } + + if (x86count && buf[32] == 0x88 && buf[33] == 0x04) + { + for (i = 0; i < (int)(ARRAY_SIZE(parttype)); i++) + { + if (buf[36] == parttype[i]) + { + debug("hard disk image assume %s efi eltorito, part type 0x%x\n", file->name, buf[36]); + return 1; + } + } + } + + debug("%s does not contain efi eltorito\n", file->name); + return 0; +} + +void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param) +{ + char *pos; + const char *fs = NULL; + const char *cdprompt = NULL; + grub_uint32_t i; + grub_uint8_t chksum = 0; + grub_disk_t disk; + + disk = file->device->disk; + grub_memcpy(¶m->guid, &g_ventoy_guid, sizeof(ventoy_guid)); + + param->vtoy_disk_size = disk->total_sectors * (1 << disk->log_sector_size); + param->vtoy_disk_part_id = disk->partition->number + 1; + param->vtoy_disk_part_type = ventoy_get_fs_type(file->fs->name); + + pos = grub_strstr(file->name, "/"); + if (!pos) + { + pos = file->name; + } + + grub_snprintf(param->vtoy_img_path, sizeof(param->vtoy_img_path), "%s", pos); + + ventoy_get_disk_guid(file->name, param->vtoy_disk_guid, param->vtoy_disk_signature); + + param->vtoy_img_size = file->size; + + param->vtoy_reserved[0] = g_ventoy_break_level; + param->vtoy_reserved[1] = g_ventoy_debug_level; + + param->vtoy_reserved[2] = g_ventoy_chain_type; + + /* Windows CD/DVD prompt 0:suppress 1:reserved */ + param->vtoy_reserved[4] = 0; + if (g_ventoy_chain_type == 1) /* Windows */ + { + cdprompt = ventoy_get_env("VTOY_WINDOWS_CD_PROMPT"); + if (cdprompt && cdprompt[0] == '1' && cdprompt[1] == 0) + { + param->vtoy_reserved[4] = 1; + } + } + + fs = ventoy_get_env("ventoy_fs_probe"); + if (fs && grub_strcmp(fs, "udf") == 0) + { + param->vtoy_reserved[3] = 1; + } + + /* calculate checksum */ + for (i = 0; i < sizeof(ventoy_os_param); i++) + { + chksum += *((grub_uint8_t *)param + i); + } + param->chksum = (grub_uint8_t)(0x100 - chksum); + + return; +} + +int ventoy_check_block_list(grub_file_t file, ventoy_img_chunk_list *chunklist, grub_disk_addr_t start) +{ + grub_uint32_t i = 0; + grub_uint64_t total = 0; + grub_uint64_t fileblk = 0; + ventoy_img_chunk *chunk = NULL; + + for (i = 0; i < chunklist->cur_chunk; i++) + { + chunk = chunklist->chunk + i; + + if (chunk->disk_start_sector <= start) + { + debug("%u disk start invalid %lu\n", i, (ulong)start); + return 1; + } + + total += chunk->disk_end_sector + 1 - chunk->disk_start_sector; + } + + fileblk = (file->size + 511) / 512; + + if (total != fileblk) + { + debug("Invalid total: %llu %llu\n", (ulonglong)total, (ulonglong)fileblk); + if ((file->size % 512) && (total + 1 == fileblk)) + { + debug("maybe img file to be processed.\n"); + return 0; + } + + return 1; + } + + return 0; +} + +int ventoy_get_block_list(grub_file_t file, ventoy_img_chunk_list *chunklist, grub_disk_addr_t start) +{ + int fs_type; + int len; + grub_uint32_t i = 0; + grub_uint32_t sector = 0; + grub_uint32_t count = 0; + grub_off_t size = 0; + grub_off_t read = 0; + + fs_type = ventoy_get_fs_type(file->fs->name); + if (fs_type == ventoy_fs_exfat) + { + grub_fat_get_file_chunk(start, file, chunklist); + } + else if (fs_type == ventoy_fs_ext) + { + grub_ext_get_file_chunk(start, file, chunklist); + } + else + { + file->read_hook = (grub_disk_read_hook_t)grub_disk_blocklist_read; + file->read_hook_data = chunklist; + + for (size = file->size; size > 0; size -= read) + { + read = (size > VTOY_SIZE_1GB) ? VTOY_SIZE_1GB : size; + grub_file_read(file, NULL, read); + } + + for (i = 0; start > 0 && i < chunklist->cur_chunk; i++) + { + chunklist->chunk[i].disk_start_sector += start; + chunklist->chunk[i].disk_end_sector += start; + } + + if (ventoy_fs_udf == fs_type) + { + for (i = 0; i < chunklist->cur_chunk; i++) + { + count = (chunklist->chunk[i].disk_end_sector + 1 - chunklist->chunk[i].disk_start_sector) >> 2; + chunklist->chunk[i].img_start_sector = sector; + chunklist->chunk[i].img_end_sector = sector + count - 1; + sector += count; + } + } + } + + len = (int)grub_strlen(file->name); + if ((len > 4 && grub_strncasecmp(file->name + len - 4, ".img", 4) == 0) || + (len > 4 && grub_strncasecmp(file->name + len - 4, ".vhd", 4) == 0) || + (len > 5 && grub_strncasecmp(file->name + len - 5, ".vhdx", 5) == 0) || + (len > 5 && grub_strncasecmp(file->name + len - 5, ".vtoy", 5) == 0)) + { + for (i = 0; i < chunklist->cur_chunk; i++) + { + count = chunklist->chunk[i].disk_end_sector + 1 - chunklist->chunk[i].disk_start_sector; + if (count < 4) + { + count = 1; + } + else + { + count >>= 2; + } + + chunklist->chunk[i].img_start_sector = sector; + chunklist->chunk[i].img_end_sector = sector + count - 1; + sector += count; + } + } + + return 0; +} + +static grub_err_t ventoy_cmd_img_sector(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc; + grub_file_t file; + grub_disk_addr_t start; + + (void)ctxt; + (void)argc; + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); + } + + g_conf_replace_node = NULL; + g_conf_replace_offset = 0; + + if (g_img_chunk_list.chunk) + { + grub_free(g_img_chunk_list.chunk); + } + + if (ventoy_get_fs_type(file->fs->name) >= ventoy_fs_max) + { + grub_file_close(file); + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Unsupported filesystem %s\n", file->fs->name); + } + + /* get image chunk data */ + grub_memset(&g_img_chunk_list, 0, sizeof(g_img_chunk_list)); + g_img_chunk_list.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); + if (NULL == g_img_chunk_list.chunk) + { + return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); + } + + g_img_chunk_list.max_chunk = DEFAULT_CHUNK_NUM; + g_img_chunk_list.cur_chunk = 0; + + start = file->device->disk->partition->start; + + ventoy_get_block_list(file, &g_img_chunk_list, start); + + rc = ventoy_check_block_list(file, &g_img_chunk_list, start); + grub_file_close(file); + + if (rc) + { + return grub_error(GRUB_ERR_NOT_IMPLEMENTED_YET, "Unsupported chunk list.\n"); + } + + grub_memset(&g_grub_param->file_replace, 0, sizeof(g_grub_param->file_replace)); + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_select_conf_replace(grub_extcmd_context_t ctxt, int argc, char **args) +{ + grub_uint64_t offset = 0; + grub_uint32_t align = 0; + grub_file_t file = NULL; + conf_replace *node = NULL; + + (void)ctxt; + (void)argc; + (void)args; + + debug("select conf replace argc:%d\n", argc); + + if (argc < 2) + { + return 0; + } + + node = ventoy_plugin_find_conf_replace(args[1]); + if (!node) + { + debug("Conf replace not found for %s\n", args[1]); + goto end; + } + + debug("Find conf replace for %s\n", args[1]); + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", node->orgconf); + if (!file) + { + debug("<(loop)%s> NOT exist\n", node->orgconf); + goto end; + } + + offset = grub_iso9660_get_last_file_dirent_pos(file); + grub_file_close(file); + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", args[0], node->newconf); + if (!file) + { + debug("New config file <%s%s> NOT exist\n", args[0], node->newconf); + goto end; + } + + align = ((int)file->size + 2047) / 2048 * 2048; + + if (align > vtoy_max_replace_file_size) + { + debug("New config file <%s%s> too big\n", args[0], node->newconf); + goto end; + } + + grub_file_read(file, g_conf_replace_new_buf, file->size); + g_conf_replace_new_len = (int)file->size; + g_conf_replace_new_len_align = align; + + g_conf_replace_node = node; + g_conf_replace_offset = offset + 2; + + debug("conf_replace OK: newlen: %d\n", g_conf_replace_new_len); + +end: + if (file) + { + grub_file_close(file); + } + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_sel_auto_install(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i = 0; + int pos = 0; + char *buf = NULL; + char configfile[128]; + install_template *node = NULL; + + (void)ctxt; + (void)argc; + (void)args; + + debug("select auto installation argc:%d\n", argc); + + if (argc < 1) + { + return 0; + } + + node = ventoy_plugin_find_install_template(args[0]); + if (!node) + { + debug("Auto install template not found for %s\n", args[0]); + return 0; + } + + if (node->autosel >= 0 && node->autosel <= node->templatenum) + { + node->cursel = node->autosel - 1; + debug("Auto install template auto select %d\n", node->autosel); + return 0; + } + + buf = (char *)grub_malloc(VTOY_MAX_SCRIPT_BUF); + if (!buf) + { + return 0; + } + + vtoy_ssprintf(buf, pos, "menuentry \"Boot without auto installation template\" {\n" + " echo %s\n}\n", "123"); + + for (i = 0; i < node->templatenum; i++) + { + vtoy_ssprintf(buf, pos, "menuentry \"Boot with %s\" {\n" + " echo 123\n}\n", + node->templatepath[i].path); + } + + g_ventoy_menu_esc = 1; + g_ventoy_suppress_esc = 1; + + grub_snprintf(configfile, sizeof(configfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)buf, pos); + grub_script_execute_sourcecode(configfile); + + g_ventoy_menu_esc = 0; + g_ventoy_suppress_esc = 0; + + grub_free(buf); + + node->cursel = g_ventoy_last_entry - 1; + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_sel_persistence(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i = 0; + int pos = 0; + char *buf = NULL; + char configfile[128]; + persistence_config *node; + + (void)ctxt; + (void)argc; + (void)args; + + debug("select persistence argc:%d\n", argc); + + if (argc < 1) + { + return 0; + } + + node = ventoy_plugin_find_persistent(args[0]); + if (!node) + { + debug("Persistence image not found for %s\n", args[0]); + return 0; + } + + if (node->autosel >= 0 && node->autosel <= node->backendnum) + { + node->cursel = node->autosel - 1; + debug("Persistence image auto select %d\n", node->autosel); + return 0; + } + + buf = (char *)grub_malloc(VTOY_MAX_SCRIPT_BUF); + if (!buf) + { + return 0; + } + + vtoy_ssprintf(buf, pos, "menuentry \"Boot without persistence\" {\n" + " echo %s\n}\n", "123"); + + for (i = 0; i < node->backendnum; i++) + { + vtoy_ssprintf(buf, pos, "menuentry \"Boot with %s\" {\n" + " echo 123\n}\n", + node->backendpath[i].path); + + } + + g_ventoy_menu_esc = 1; + g_ventoy_suppress_esc = 1; + + grub_snprintf(configfile, sizeof(configfile), "configfile mem:0x%llx:size:%d", (ulonglong)(ulong)buf, pos); + grub_script_execute_sourcecode(configfile); + + g_ventoy_menu_esc = 0; + g_ventoy_suppress_esc = 0; + + grub_free(buf); + + node->cursel = g_ventoy_last_entry - 1; + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_dump_img_sector(grub_extcmd_context_t ctxt, int argc, char **args) +{ + grub_uint32_t i; + ventoy_img_chunk *cur; + + (void)ctxt; + (void)argc; + (void)args; + + for (i = 0; i < g_img_chunk_list.cur_chunk; i++) + { + cur = g_img_chunk_list.chunk + i; + grub_printf("image:[%u - %u] <==> disk:[%llu - %llu]\n", + cur->img_start_sector, cur->img_end_sector, + (unsigned long long)cur->disk_start_sector, (unsigned long long)cur->disk_end_sector + ); + } + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_test_block_list(grub_extcmd_context_t ctxt, int argc, char **args) +{ + grub_uint32_t i; + grub_file_t file; + ventoy_img_chunk_list chunklist; + + (void)ctxt; + (void)argc; + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); + } + + /* get image chunk data */ + grub_memset(&chunklist, 0, sizeof(chunklist)); + chunklist.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM); + if (NULL == chunklist.chunk) + { + return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n"); + } + + chunklist.max_chunk = DEFAULT_CHUNK_NUM; + chunklist.cur_chunk = 0; + + ventoy_get_block_list(file, &chunklist, 0); + + if (0 != ventoy_check_block_list(file, &chunklist, 0)) + { + grub_printf("########## UNSUPPORTED ###############\n"); + } + + grub_printf("filesystem: <%s> entry number:<%u>\n", file->fs->name, chunklist.cur_chunk); + + for (i = 0; i < chunklist.cur_chunk; i++) + { + grub_printf("%llu+%llu,", (ulonglong)chunklist.chunk[i].disk_start_sector, + (ulonglong)(chunklist.chunk[i].disk_end_sector + 1 - chunklist.chunk[i].disk_start_sector)); + } + + grub_printf("\n==================================\n"); + + for (i = 0; i < chunklist.cur_chunk; i++) + { + grub_printf("%2u: [%llu %llu] - [%llu %llu]\n", i, + (ulonglong)chunklist.chunk[i].img_start_sector, + (ulonglong)chunklist.chunk[i].img_end_sector, + (ulonglong)chunklist.chunk[i].disk_start_sector, + (ulonglong)chunklist.chunk[i].disk_end_sector + ); + } + + grub_free(chunklist.chunk); + grub_file_close(file); + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_add_replace_file(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i; + ventoy_grub_param_file_replace *replace = NULL; + + (void)ctxt; + (void)argc; + (void)args; + + if (argc >= 2) + { + replace = &(g_grub_param->file_replace); + replace->magic = GRUB_FILE_REPLACE_MAGIC; + + replace->old_name_cnt = 0; + for (i = 0; i < 4 && i + 1 < argc; i++) + { + replace->old_name_cnt++; + grub_snprintf(replace->old_file_name[i], sizeof(replace->old_file_name[i]), "%s", args[i + 1]); + } + + replace->new_file_virtual_id = (grub_uint32_t)grub_strtoul(args[0], NULL, 10); + } + + VENTOY_CMD_RETURN(GRUB_ERR_NONE); +} + +static grub_err_t ventoy_cmd_dump_menu(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + if (argc == 0) + { + grub_printf("List Mode: CurLen:%d MaxLen:%u\n", g_list_script_pos, VTOY_MAX_SCRIPT_BUF); + grub_printf("%s", g_list_script_buf); + } + else + { + grub_printf("Tree Mode: CurLen:%d MaxLen:%u\n", g_tree_script_pos, VTOY_MAX_SCRIPT_BUF); + grub_printf("%s", g_tree_script_buf); + } + + return 0; +} + +static grub_err_t ventoy_cmd_dump_img_list(grub_extcmd_context_t ctxt, int argc, char **args) +{ + img_info *cur = g_ventoy_img_list; + + (void)ctxt; + (void)argc; + (void)args; + + while (cur) + { + grub_printf("path:<%s> id=%d list_index=%d\n", cur->path, cur->id, cur->plugin_list_index); + grub_printf("name:<%s>\n\n", cur->name); + cur = cur->next; + } + + return 0; +} + +static grub_err_t ventoy_cmd_dump_injection(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + ventoy_plugin_dump_injection(); + + return 0; +} + +static grub_err_t ventoy_cmd_dump_auto_install(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + ventoy_plugin_dump_auto_install(); + + return 0; +} + +static grub_err_t ventoy_cmd_dump_persistence(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + ventoy_plugin_dump_persistence(); + + return 0; +} + +static grub_err_t ventoy_cmd_check_mode(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + if (argc != 1) + { + return 1; + } + + if (args[0][0] == '0') + { + return g_ventoy_memdisk_mode ? 0 : 1; + } + else if (args[0][0] == '1') + { + return g_ventoy_iso_raw ? 0 : 1; + } + else if (args[0][0] == '2') + { + return g_ventoy_iso_uefi_drv ? 0 : 1; + } + else if (args[0][0] == '3') + { + return g_ventoy_grub2_mode ? 0 : 1; + } + + return 1; +} + +static grub_err_t ventoy_cmd_dynamic_menu(grub_extcmd_context_t ctxt, int argc, char **args) +{ + static int configfile_mode = 0; + char memfile[128] = {0}; + + (void)ctxt; + (void)argc; + (void)args; + + /* + * args[0]: 0:normal 1:configfile + * args[1]: 0:list_buf 1:tree_buf + */ + + if (argc != 2) + { + debug("Invalid argc %d\n", argc); + return 0; + } + + VTOY_CMD_CHECK(1); + + if (args[0][0] == '0') + { + if (args[1][0] == '0') + { + grub_script_execute_sourcecode(g_list_script_buf); + } + else + { + grub_script_execute_sourcecode(g_tree_script_buf); + } + } + else + { + if (configfile_mode) + { + debug("Now already in F3 mode %d\n", configfile_mode); + return 0; + } + + if (args[1][0] == '0') + { + grub_snprintf(memfile, sizeof(memfile), "configfile mem:0x%llx:size:%d", + (ulonglong)(ulong)g_list_script_buf, g_list_script_pos); + } + else + { + g_ventoy_last_entry = -1; + grub_snprintf(memfile, sizeof(memfile), "configfile mem:0x%llx:size:%d", + (ulonglong)(ulong)g_tree_script_buf, g_tree_script_pos); + } + + configfile_mode = 1; + grub_script_execute_sourcecode(memfile); + configfile_mode = 0; + } + + return 0; +} + +static grub_err_t ventoy_cmd_file_exist_nocase(grub_extcmd_context_t ctxt, int argc, char **args) +{ + grub_file_t file; + + (void)ctxt; + + if (argc != 1) + { + return 1; + } + + g_ventoy_case_insensitive = 1; + file = grub_file_open(args[0], VENTOY_FILE_TYPE); + g_ventoy_case_insensitive = 0; + + grub_errno = 0; + + if (file) + { + grub_file_close(file); + return 0; + } + return 1; +} + +static grub_err_t ventoy_cmd_find_bootable_hdd(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int id = 0; + int find = 0; + grub_disk_t disk; + const char *isopath = NULL; + char hdname[32]; + ventoy_mbr_head mbr; + + (void)ctxt; + (void)argc; + + if (argc != 1) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s variable\n", cmd_raw_name); + } + + isopath = grub_env_get("vtoy_iso_part"); + if (!isopath) + { + debug("isopath is null %p\n", isopath); + return 0; + } + + debug("isopath is %s\n", isopath); + + for (id = 0; id < 30 && (find == 0); id++) + { + grub_snprintf(hdname, sizeof(hdname), "hd%d,", id); + if (grub_strstr(isopath, hdname)) + { + debug("skip %s ...\n", hdname); + continue; + } + + grub_snprintf(hdname, sizeof(hdname), "hd%d", id); + + disk = grub_disk_open(hdname); + if (!disk) + { + debug("%s not exist\n", hdname); + break; + } + + grub_memset(&mbr, 0, sizeof(mbr)); + if (0 == grub_disk_read(disk, 0, 0, 512, &mbr)) + { + if (mbr.Byte55 == 0x55 && mbr.ByteAA == 0xAA) + { + if (mbr.PartTbl[0].Active == 0x80 || mbr.PartTbl[1].Active == 0x80 || + mbr.PartTbl[2].Active == 0x80 || mbr.PartTbl[3].Active == 0x80) + { + + grub_env_set(args[0], hdname); + find = 1; + } + } + debug("%s is %s\n", hdname, find ? "bootable" : "NOT bootable"); + } + else + { + debug("read %s failed\n", hdname); + } + + grub_disk_close(disk); + } + + return 0; +} + +static grub_err_t ventoy_cmd_read_1st_line(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int len = 1024; + grub_file_t file; + char *buf = NULL; + + (void)ctxt; + (void)argc; + + if (argc != 2) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s file var \n", cmd_raw_name); + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + debug("failed to open file %s\n", args[0]); + return 0; + } + + buf = grub_malloc(len); + if (!buf) + { + goto end; + } + + buf[len - 1] = 0; + grub_file_read(file, buf, len - 1); + + ventoy_get_line(buf); + ventoy_set_env(args[1], buf); + +end: + + grub_check_free(buf); + grub_file_close(file); + + return 0; +} + +static int ventoy_img_partition_callback (struct grub_disk *disk, const grub_partition_t partition, void *data) +{ + (void)disk; + (void)data; + + g_part_list_pos += grub_snprintf(g_part_list_buf + g_part_list_pos, VTOY_MAX_SCRIPT_BUF - g_part_list_pos, + "0 %llu linear /dev/ventoy %llu\n", + (ulonglong)partition->len, (ulonglong)partition->start); + + return 0; +} + +static grub_err_t ventoy_cmd_img_part_info(grub_extcmd_context_t ctxt, int argc, char **args) +{ + char *device_name = NULL; + grub_device_t dev = NULL; + char buf[64]; + + (void)ctxt; + + g_part_list_pos = 0; + grub_env_unset("vtoy_img_part_file"); + + if (argc != 1) + { + return 1; + } + + device_name = grub_file_get_device_name(args[0]); + if (!device_name) + { + debug("ventoy_cmd_img_part_info failed, %s\n", args[0]); + goto end; + } + + dev = grub_device_open(device_name); + if (!dev) + { + debug("grub_device_open failed, %s\n", device_name); + goto end; + } + + grub_partition_iterate(dev->disk, ventoy_img_partition_callback, NULL); + + grub_snprintf(buf, sizeof(buf), "newc:vtoy_dm_table:mem:0x%llx:size:%d", (ulonglong)(ulong)g_part_list_buf, g_part_list_pos); + grub_env_set("vtoy_img_part_file", buf); + +end: + + check_free(device_name, grub_free); + check_free(dev, grub_device_close); + + return 0; +} + + +static grub_err_t ventoy_cmd_file_strstr(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc = 1; + grub_file_t file; + char *buf = NULL; + + (void)ctxt; + (void)argc; + + if (argc != 2) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s file str \n", cmd_raw_name); + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + debug("failed to open file %s\n", args[0]); + return 1; + } + + buf = grub_malloc(file->size + 1); + if (!buf) + { + goto end; + } + + buf[file->size] = 0; + grub_file_read(file, buf, file->size); + + if (grub_strstr(buf, args[1])) + { + rc = 0; + } + +end: + + grub_check_free(buf); + grub_file_close(file); + + return rc; +} + +static grub_err_t ventoy_cmd_parse_volume(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int len; + grub_file_t file; + char buf[64]; + grub_uint64_t size; + ventoy_iso9660_vd pvd; + + (void)ctxt; + (void)argc; + + if (argc != 4) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s sysid volid space \n", cmd_raw_name); + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + debug("failed to open file %s\n", args[0]); + return 0; + } + + grub_file_seek(file, 16 * 2048); + len = (int)grub_file_read(file, &pvd, sizeof(pvd)); + if (len != sizeof(pvd)) + { + debug("failed to read pvd %d\n", len); + goto end; + } + + grub_memset(buf, 0, sizeof(buf)); + grub_memcpy(buf, pvd.sys, sizeof(pvd.sys)); + ventoy_set_env(args[1], buf); + + grub_memset(buf, 0, sizeof(buf)); + grub_memcpy(buf, pvd.vol, sizeof(pvd.vol)); + ventoy_set_env(args[2], buf); + + size = pvd.space; + size *= 2048; + grub_snprintf(buf, sizeof(buf), "%llu", (ulonglong)size); + ventoy_set_env(args[3], buf); + +end: + grub_file_close(file); + + return 0; +} + +static grub_err_t ventoy_cmd_parse_create_date(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int len; + grub_file_t file; + char buf[64]; + + (void)ctxt; + (void)argc; + + if (argc != 2) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s var \n", cmd_raw_name); + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + debug("failed to open file %s\n", args[0]); + return 0; + } + + grub_memset(buf, 0, sizeof(buf)); + grub_file_seek(file, 16 * 2048 + 813); + len = (int)grub_file_read(file, buf, 17); + if (len != 17) + { + debug("failed to read create date %d\n", len); + goto end; + } + + ventoy_set_env(args[1], buf); + +end: + grub_file_close(file); + + return 0; +} + +static grub_err_t ventoy_cmd_img_hook_root(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + ventoy_env_hook_root(1); + + return 0; +} + +static grub_err_t ventoy_cmd_img_unhook_root(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + ventoy_env_hook_root(0); + + return 0; +} + +#ifdef GRUB_MACHINE_EFI +static grub_err_t ventoy_cmd_check_secureboot_var(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int ret = 1; + grub_uint8_t *var; + grub_size_t size; + grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; + + (void)ctxt; + (void)argc; + (void)args; + + var = grub_efi_get_variable("SecureBoot", &global, &size); + if (var && *var == 1) + { + return 0; + } + + return ret; +} +#else +static grub_err_t ventoy_cmd_check_secureboot_var(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + return 1; +} +#endif + +static grub_err_t ventoy_cmd_img_check_range(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i; + int ret = 1; + grub_file_t file; + grub_uint64_t FileSectors = 0; + ventoy_gpt_info *gpt = NULL; + ventoy_part_table *pt = NULL; + grub_uint8_t zeroguid[16] = {0}; + + (void)ctxt; + (void)argc; + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + debug("failed to open file %s\n", args[0]); + return 1; + } + + if (file->size % 512) + { + debug("unaligned file size: %llu\n", (ulonglong)file->size); + goto out; + } + + gpt = grub_zalloc(sizeof(ventoy_gpt_info)); + if (!gpt) + { + goto out; + } + + FileSectors = file->size / 512; + + grub_file_read(file, gpt, sizeof(ventoy_gpt_info)); + if (grub_strncmp(gpt->Head.Signature, "EFI PART", 8) == 0) + { + debug("This is EFI partition table\n"); + + for (i = 0; i < 128; i++) + { + if (grub_memcmp(gpt->PartTbl[i].PartGuid, zeroguid, 16)) + { + if (FileSectors < gpt->PartTbl[i].LastLBA) + { + debug("out of range: part[%d] LastLBA:%llu FileSectors:%llu\n", i, + (ulonglong)gpt->PartTbl[i].LastLBA, (ulonglong)FileSectors); + goto out; + } + } + } + } + else + { + debug("This is MBR partition table\n"); + + for (i = 0; i < 4; i++) + { + pt = gpt->MBR.PartTbl + i; + if (FileSectors < pt->StartSectorId + pt->SectorCount) + { + debug("out of range: part[%d] LastLBA:%llu FileSectors:%llu\n", i, + (ulonglong)(pt->StartSectorId + pt->SectorCount), + (ulonglong)FileSectors); + goto out; + } + } + } + + ret = 0; + +out: + grub_file_close(file); + grub_check_free(gpt); + grub_errno = GRUB_ERR_NONE; + return ret; +} + +static grub_err_t ventoy_cmd_clear_key(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i; + int ret; + + (void)ctxt; + (void)argc; + (void)args; + + for (i = 0; i < 500; i++) + { + ret = grub_getkey_noblock(); + if (ret == GRUB_TERM_NO_KEY) + { + break; + } + } + + if (i >= 500) + { + grub_cls(); + grub_printf("\n\n Still have key input after clear.\n"); + grub_refresh(); + grub_sleep(5); + } + + return 0; +} + +static grub_err_t ventoy_cmd_acpi_param(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i; + int buflen; + int datalen; + int loclen; + int img_chunk_num; + int image_sector_size; + char cmd[64]; + ventoy_chain_head *chain; + ventoy_img_chunk *chunk; + ventoy_os_param *osparam; + ventoy_image_location *location; + ventoy_image_disk_region *region; + struct grub_acpi_table_header *acpi; + + (void)ctxt; + + if (argc != 2) + { + return 1; + } + + debug("ventoy_cmd_acpi_param %s %s\n", args[0], args[1]); + + chain = (ventoy_chain_head *)(ulong)grub_strtoul(args[0], NULL, 16); + if (!chain) + { + return 1; + } + + image_sector_size = (int)grub_strtol(args[1], NULL, 10); + + if (grub_memcmp(&g_ventoy_guid, &(chain->os_param.guid), 16)) + { + debug("Invalid ventoy guid 0x%x\n", chain->os_param.guid.data1); + return 1; + } + + img_chunk_num = chain->img_chunk_num; + + loclen = sizeof(ventoy_image_location) + (img_chunk_num - 1) * sizeof(ventoy_image_disk_region); + datalen = sizeof(ventoy_os_param) + loclen; + + buflen = sizeof(struct grub_acpi_table_header) + datalen; + acpi = grub_zalloc(buflen); + if (!acpi) + { + return 1; + } + + /* Step1: Fill acpi table header */ + grub_memcpy(acpi->signature, "VTOY", 4); + acpi->length = buflen; + acpi->revision = 1; + grub_memcpy(acpi->oemid, "VENTOY", 6); + grub_memcpy(acpi->oemtable, "OSPARAMS", 8); + acpi->oemrev = 1; + acpi->creator_id[0] = 1; + acpi->creator_rev = 1; + + /* Step2: Fill data */ + osparam = (ventoy_os_param *)(acpi + 1); + grub_memcpy(osparam, &chain->os_param, sizeof(ventoy_os_param)); + osparam->vtoy_img_location_addr = 0; + osparam->vtoy_img_location_len = loclen; + osparam->chksum = 0; + osparam->chksum = 0x100 - grub_byte_checksum(osparam, sizeof(ventoy_os_param)); + + location = (ventoy_image_location *)(osparam + 1); + grub_memcpy(&location->guid, &osparam->guid, sizeof(ventoy_guid)); + location->image_sector_size = image_sector_size; + location->disk_sector_size = chain->disk_sector_size; + location->region_count = img_chunk_num; + + region = location->regions; + chunk = (ventoy_img_chunk *)((char *)chain + chain->img_chunk_offset); + if (512 == image_sector_size) + { + for (i = 0; i < img_chunk_num; i++) + { + region->image_sector_count = chunk->disk_end_sector - chunk->disk_start_sector + 1; + region->image_start_sector = chunk->img_start_sector * 4; + region->disk_start_sector = chunk->disk_start_sector; + region++; + chunk++; + } + } + else + { + for (i = 0; i < img_chunk_num; i++) + { + region->image_sector_count = chunk->img_end_sector - chunk->img_start_sector + 1; + region->image_start_sector = chunk->img_start_sector; + region->disk_start_sector = chunk->disk_start_sector; + region++; + chunk++; + } + } + + /* Step3: Fill acpi checksum */ + acpi->checksum = 0; + acpi->checksum = 0x100 - grub_byte_checksum(acpi, acpi->length); + + /* load acpi table */ + grub_snprintf(cmd, sizeof(cmd), "acpi mem:0x%lx:size:%d", (ulong)acpi, acpi->length); + grub_script_execute_sourcecode(cmd); + + grub_free(acpi); + + VENTOY_CMD_RETURN(0); +} + +static grub_err_t ventoy_cmd_push_last_entry(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + g_ventoy_last_entry_back = g_ventoy_last_entry; + g_ventoy_last_entry = -1; + + return 0; +} + +static grub_err_t ventoy_cmd_pop_last_entry(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + (void)argc; + (void)args; + + g_ventoy_last_entry = g_ventoy_last_entry_back; + + return 0; +} + +static int ventoy_lib_module_callback(const char *filename, const struct grub_dirhook_info *info, void *data) +{ + const char *pos = filename + 1; + + if (info->dir) + { + while (*pos) + { + if (*pos == '.') + { + if ((*(pos - 1) >= '0' && *(pos - 1) <= '9') && (*(pos + 1) >= '0' && *(pos + 1) <= '9')) + { + grub_strncpy((char *)data, filename, 128); + return 1; + } + } + pos++; + } + } + + return 0; +} + +static grub_err_t ventoy_cmd_lib_module_ver(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc = 1; + char *device_name = NULL; + grub_device_t dev = NULL; + grub_fs_t fs = NULL; + char buf[128] = {0}; + + (void)ctxt; + + if (argc != 3) + { + debug("ventoy_cmd_lib_module_ver, invalid param num %d\n", argc); + return 1; + } + + debug("ventoy_cmd_lib_module_ver %s %s %s\n", args[0], args[1], args[2]); + + device_name = grub_file_get_device_name(args[0]); + if (!device_name) + { + debug("grub_file_get_device_name failed, %s\n", args[0]); + goto end; + } + + dev = grub_device_open(device_name); + if (!dev) + { + debug("grub_device_open failed, %s\n", device_name); + goto end; + } + + fs = grub_fs_probe(dev); + if (!fs) + { + debug("grub_fs_probe failed, %s\n", device_name); + goto end; + } + + fs->fs_dir(dev, args[1], ventoy_lib_module_callback, buf); + + if (buf[0]) + { + ventoy_set_env(args[2], buf); + } + + rc = 0; + +end: + + check_free(device_name, grub_free); + check_free(dev, grub_device_close); + + return rc; +} + +int ventoy_load_part_table(const char *diskname) +{ + char name[64]; + int ret; + grub_disk_t disk; + grub_device_t dev; + + g_ventoy_part_info = grub_zalloc(sizeof(ventoy_gpt_info)); + if (!g_ventoy_part_info) + { + return 1; + } + + disk = grub_disk_open(diskname); + if (!disk) + { + debug("Failed to open disk %s\n", diskname); + return 1; + } + + g_ventoy_disk_size = disk->total_sectors * (1U << disk->log_sector_size); + + grub_disk_read(disk, 0, 0, sizeof(ventoy_gpt_info), g_ventoy_part_info); + grub_disk_close(disk); + + grub_snprintf(name, sizeof(name), "%s,1", diskname); + dev = grub_device_open(name); + if (dev) + { + /* Check for official Ventoy device */ + ret = ventoy_check_official_device(dev); + grub_device_close(dev); + + if (ret) + { + return 1; + } + } + + g_ventoy_disk_part_size[0] = ventoy_get_vtoy_partsize(0); + g_ventoy_disk_part_size[1] = ventoy_get_vtoy_partsize(1); + + return 0; +} + +static grub_err_t ventoy_cmd_load_part_table(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int ret; + + (void)argc; + (void)ctxt; + + ret = ventoy_load_part_table(args[0]); + if (ret) + { + grub_exit(); + } + + g_ventoy_disk_part_size[0] = ventoy_get_vtoy_partsize(0); + g_ventoy_disk_part_size[1] = ventoy_get_vtoy_partsize(1); + + return 0; +} + +static grub_err_t ventoy_cmd_check_custom_boot(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int ret = 1; + const char *vcfg = NULL; + + (void)argc; + (void)ctxt; + + vcfg = ventoy_plugin_get_custom_boot(args[0]); + if (vcfg) + { + debug("custom boot <%s>:<%s>\n", args[0], vcfg); + grub_env_set(args[1], vcfg); + ret = 0; + } + else + { + debug("custom boot <%s>:\n", args[0]); + } + + grub_errno = 0; + return ret; +} + + +static grub_err_t ventoy_cmd_part_exist(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int id; + grub_uint8_t zeroguid[16] = {0}; + + (void)argc; + (void)ctxt; + + id = (int)grub_strtoul(args[0], NULL, 10); + grub_errno = 0; + + if (grub_memcmp(g_ventoy_part_info->Head.Signature, "EFI PART", 8) == 0) + { + if (id >= 1 && id <= 128) + { + if (grub_memcmp(g_ventoy_part_info->PartTbl[id - 1].PartGuid, zeroguid, 16)) + { + return 0; + } + } + } + else + { + if (id >= 1 && id <= 4) + { + if (g_ventoy_part_info->MBR.PartTbl[id - 1].FsFlag) + { + return 0; + } + } + } + + return 1; +} + +static grub_err_t ventoy_cmd_get_fs_label(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc = 1; + char *device_name = NULL; + grub_device_t dev = NULL; + grub_fs_t fs = NULL; + char *label = NULL; + + (void)ctxt; + + debug("get fs label for %s\n", args[0]); + + if (argc != 2) + { + debug("ventoy_cmd_get_fs_label, invalid param num %d\n", argc); + return 1; + } + + device_name = grub_file_get_device_name(args[0]); + if (!device_name) + { + debug("grub_file_get_device_name failed, %s\n", args[0]); + goto end; + } + + dev = grub_device_open(device_name); + if (!dev) + { + debug("grub_device_open failed, %s\n", device_name); + goto end; + } + + fs = grub_fs_probe(dev); + if (NULL == fs || NULL == fs->fs_label) + { + debug("grub_fs_probe failed, %s %p %p\n", device_name, fs, fs->fs_label); + goto end; + } + + fs->fs_label(dev, &label); + if (label) + { + debug("label=<%s>\n", label); + ventoy_set_env(args[1], label); + grub_free(label); + } + + rc = 0; + +end: + + check_free(device_name, grub_free); + check_free(dev, grub_device_close); + + return rc; +} + +static int ventoy_fs_enum_1st_file(const char *filename, const struct grub_dirhook_info *info, void *data) +{ + if (!info->dir) + { + grub_snprintf((char *)data, 256, "%s", filename); + return 1; + } + + return 0; +} + + +static grub_err_t ventoy_cmd_fs_enum_1st_file(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int rc = 1; + char *device_name = NULL; + grub_device_t dev = NULL; + grub_fs_t fs = NULL; + char name[256] ={0}; + + (void)ctxt; + + if (argc != 3) + { + debug("ventoy_cmd_fs_enum_1st_file, invalid param num %d\n", argc); + return 1; + } + + device_name = grub_file_get_device_name(args[0]); + if (!device_name) + { + debug("grub_file_get_device_name failed, %s\n", args[0]); + goto end; + } + + dev = grub_device_open(device_name); + if (!dev) + { + debug("grub_device_open failed, %s\n", device_name); + goto end; + } + + fs = grub_fs_probe(dev); + if (!fs) + { + debug("grub_fs_probe failed, %s\n", device_name); + goto end; + } + + fs->fs_dir(dev, args[1], ventoy_fs_enum_1st_file, name); + if (name[0]) + { + ventoy_set_env(args[2], name); + } + + rc = 0; + +end: + + check_free(device_name, grub_free); + check_free(dev, grub_device_close); + + return rc; +} + +static grub_err_t ventoy_cmd_basename(grub_extcmd_context_t ctxt, int argc, char **args) +{ + char c; + char *pos = NULL; + char *end = NULL; + + (void)ctxt; + + if (argc != 2) + { + debug("ventoy_cmd_basename, invalid param num %d\n", argc); + return 1; + } + + for (pos = args[0]; *pos; pos++) + { + if (*pos == '.') + { + end = pos; + } + } + + if (end) + { + c = *end; + *end = 0; + } + + grub_env_set(args[1], args[0]); + + if (end) + { + *end = c; + } + + return 0; +} + +static grub_err_t ventoy_cmd_basefile(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int i; + int len; + const char *buf; + + (void)ctxt; + + if (argc != 2) + { + debug("ventoy_cmd_basefile, invalid param num %d\n", argc); + return 1; + } + + buf = args[0]; + len = (int)grub_strlen(buf); + for (i = len; i > 0; i--) + { + if (buf[i - 1] == '/') + { + grub_env_set(args[1], buf + i); + return 0; + } + } + + grub_env_set(args[1], buf); + + return 0; +} + +static grub_err_t ventoy_cmd_enum_video_mode(grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_video_mode_info info; + char buf[32]; + + (void)ctxt; + (void)argc; + (void)args; + + if (!g_video_mode_list) + { + ventoy_enum_video_mode(); + } + + if (grub_video_get_info(&info) == GRUB_ERR_NONE) + { + grub_snprintf(buf, sizeof(buf), "Resolution (%ux%u)", info.width, info.height); + } + else + { + grub_snprintf(buf, sizeof(buf), "Resolution (0x0)"); + } + + grub_env_set("VTOY_CUR_VIDEO_MODE", buf); + + grub_snprintf(buf, sizeof(buf), "%d", g_video_mode_num); + grub_env_set("VTOY_VIDEO_MODE_NUM", buf); + + VENTOY_CMD_RETURN(0); +} + +static grub_err_t vt_cmd_update_cur_video_mode(grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_video_mode_info info; + char buf[32]; + + (void)ctxt; + (void)argc; + (void)args; + + if (grub_video_get_info(&info) == GRUB_ERR_NONE) + { + grub_snprintf(buf, sizeof(buf), "%ux%ux%u", info.width, info.height, info.bpp); + } + else + { + grub_snprintf(buf, sizeof(buf), "0x0x0"); + } + + grub_env_set(args[0], buf); + + VENTOY_CMD_RETURN(0); +} + +static grub_err_t ventoy_cmd_get_video_mode(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int id; + char buf[32]; + + (void)ctxt; + (void)argc; + + if (!g_video_mode_list) + { + return 0; + } + + id = (int)grub_strtoul(args[0], NULL, 10); + if (id < g_video_mode_num) + { + grub_snprintf(buf, sizeof(buf), "%ux%ux%u", + g_video_mode_list[id].width, g_video_mode_list[id].height, g_video_mode_list[id].bpp); + } + + grub_env_set(args[1], buf); + + VENTOY_CMD_RETURN(0); +} + +int ventoy_env_init(void) +{ + char buf[64]; + + grub_env_set("vtdebug_flag", ""); + + g_part_list_buf = grub_malloc(VTOY_PART_BUF_LEN); + g_tree_script_buf = grub_malloc(VTOY_MAX_SCRIPT_BUF); + g_list_script_buf = grub_malloc(VTOY_MAX_SCRIPT_BUF); + g_conf_replace_new_buf = grub_malloc(vtoy_max_replace_file_size); + + ventoy_filt_register(0, ventoy_wrapper_open); + + g_grub_param = (ventoy_grub_param *)grub_zalloc(sizeof(ventoy_grub_param)); + if (g_grub_param) + { + g_grub_param->grub_env_get = grub_env_get; + g_grub_param->grub_env_set = (grub_env_set_pf)grub_env_set; + g_grub_param->grub_env_printf = (grub_env_printf_pf)grub_printf; + grub_snprintf(buf, sizeof(buf), "%p", g_grub_param); + grub_env_set("env_param", buf); + grub_env_set("ventoy_env_param", buf); + + grub_env_export("env_param"); + grub_env_export("ventoy_env_param"); + } + + return 0; +} + +static cmd_para ventoy_cmds[] = +{ + { "vt_incr", ventoy_cmd_incr, 0, NULL, "{Var} {INT}", "Increase integer variable", NULL }, + { "vt_mod", ventoy_cmd_mod, 0, NULL, "{Int} {Int} {Var}", "mod integer variable", NULL }, + { "vt_strstr", ventoy_cmd_strstr, 0, NULL, "", "", NULL }, + { "vt_str_begin", ventoy_cmd_strbegin, 0, NULL, "", "", NULL }, + { "vt_debug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, + { "vtdebug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, + { "vtbreak", ventoy_cmd_break, 0, NULL, "{level}", "set debug break", NULL }, + { "vt_cmp", ventoy_cmd_cmp, 0, NULL, "{Int1} { eq|ne|gt|lt|ge|le } {Int2}", "Comare two integers", NULL }, + { "vt_device", ventoy_cmd_device, 0, NULL, "path var", "", NULL }, + { "vt_check_compatible", ventoy_cmd_check_compatible, 0, NULL, "", "", NULL }, + { "vt_list_img", ventoy_cmd_list_img, 0, NULL, "{device} {cntvar}", "find all iso file in device", NULL }, + { "vt_clear_img", ventoy_cmd_clear_img, 0, NULL, "", "clear image list", NULL }, + { "vt_img_name", ventoy_cmd_img_name, 0, NULL, "{imageID} {var}", "get image name", NULL }, + { "vt_chosen_img_path", ventoy_cmd_chosen_img_path, 0, NULL, "{var}", "get chosen img path", NULL }, + { "vt_ext_select_img_path", ventoy_cmd_ext_select_img_path, 0, NULL, "{var}", "select chosen img path", NULL }, + { "vt_img_sector", ventoy_cmd_img_sector, 0, NULL, "{imageName}", "", NULL }, + { "vt_dump_img_sector", ventoy_cmd_dump_img_sector, 0, NULL, "", "", NULL }, + { "vt_load_wimboot", ventoy_cmd_load_wimboot, 0, NULL, "", "", NULL }, + { "vt_load_vhdboot", ventoy_cmd_load_vhdboot, 0, NULL, "", "", NULL }, + { "vt_patch_vhdboot", ventoy_cmd_patch_vhdboot, 0, NULL, "", "", NULL }, + { "vt_raw_chain_data", ventoy_cmd_raw_chain_data, 0, NULL, "", "", NULL }, + { "vt_get_vtoy_type", ventoy_cmd_get_vtoy_type, 0, NULL, "", "", NULL }, + { "vt_check_custom_boot", ventoy_cmd_check_custom_boot, 0, NULL, "", "", NULL }, + { "vt_dump_custom_boot", ventoy_cmd_dump_custom_boot, 0, NULL, "", "", NULL }, + + { "vt_skip_svd", ventoy_cmd_skip_svd, 0, NULL, "", "", NULL }, + { "vt_cpio_busybox64", ventoy_cmd_cpio_busybox_64, 0, NULL, "", "", NULL }, + { "vt_load_cpio", ventoy_cmd_load_cpio, 0, NULL, "", "", NULL }, + { "vt_trailer_cpio", ventoy_cmd_trailer_cpio, 0, NULL, "", "", NULL }, + { "vt_push_last_entry", ventoy_cmd_push_last_entry, 0, NULL, "", "", NULL }, + { "vt_pop_last_entry", ventoy_cmd_pop_last_entry, 0, NULL, "", "", NULL }, + { "vt_get_lib_module_ver", ventoy_cmd_lib_module_ver, 0, NULL, "", "", NULL }, + + { "vt_load_part_table", ventoy_cmd_load_part_table, 0, NULL, "", "", NULL }, + { "vt_check_part_exist", ventoy_cmd_part_exist, 0, NULL, "", "", NULL }, + { "vt_get_fs_label", ventoy_cmd_get_fs_label, 0, NULL, "", "", NULL }, + { "vt_fs_enum_1st_file", ventoy_cmd_fs_enum_1st_file, 0, NULL, "", "", NULL }, + { "vt_file_basename", ventoy_cmd_basename, 0, NULL, "", "", NULL }, + { "vt_file_basefile", ventoy_cmd_basefile, 0, NULL, "", "", NULL }, + { "vt_enum_video_mode", ventoy_cmd_enum_video_mode, 0, NULL, "", "", NULL }, + { "vt_get_video_mode", ventoy_cmd_get_video_mode, 0, NULL, "", "", NULL }, + { "vt_update_cur_video_mode", vt_cmd_update_cur_video_mode, 0, NULL, "", "", NULL }, + + + { "vt_find_first_bootable_hd", ventoy_cmd_find_bootable_hdd, 0, NULL, "", "", NULL }, + { "vt_dump_menu", ventoy_cmd_dump_menu, 0, NULL, "", "", NULL }, + { "vt_dynamic_menu", ventoy_cmd_dynamic_menu, 0, NULL, "", "", NULL }, + { "vt_check_mode", ventoy_cmd_check_mode, 0, NULL, "", "", NULL }, + { "vt_dump_img_list", ventoy_cmd_dump_img_list, 0, NULL, "", "", NULL }, + { "vt_dump_injection", ventoy_cmd_dump_injection, 0, NULL, "", "", NULL }, + { "vt_dump_auto_install", ventoy_cmd_dump_auto_install, 0, NULL, "", "", NULL }, + { "vt_dump_persistence", ventoy_cmd_dump_persistence, 0, NULL, "", "", NULL }, + { "vt_select_auto_install", ventoy_cmd_sel_auto_install, 0, NULL, "", "", NULL }, + { "vt_select_persistence", ventoy_cmd_sel_persistence, 0, NULL, "", "", NULL }, + { "vt_select_conf_replace", ventoy_select_conf_replace, 0, NULL, "", "", NULL }, + + { "vt_iso9660_nojoliet", ventoy_cmd_iso9660_nojoliet, 0, NULL, "", "", NULL }, + { "vt_is_udf", ventoy_cmd_is_udf, 0, NULL, "", "", NULL }, + { "vt_file_size", ventoy_cmd_file_size, 0, NULL, "", "", NULL }, + { "vt_load_file_to_mem", ventoy_cmd_load_file_to_mem, 0, NULL, "", "", NULL }, + { "vt_load_img_memdisk", ventoy_cmd_load_img_memdisk, 0, NULL, "", "", NULL }, + { "vt_concat_efi_iso", ventoy_cmd_concat_efi_iso, 0, NULL, "", "", NULL }, + + { "vt_linux_parse_initrd_isolinux", ventoy_cmd_isolinux_initrd_collect, 0, NULL, "{cfgfile}", "", NULL }, + { "vt_linux_parse_initrd_grub", ventoy_cmd_grub_initrd_collect, 0, NULL, "{cfgfile}", "", NULL }, + { "vt_linux_specify_initrd_file", ventoy_cmd_specify_initrd_file, 0, NULL, "", "", NULL }, + { "vt_linux_clear_initrd", ventoy_cmd_clear_initrd_list, 0, NULL, "", "", NULL }, + { "vt_linux_dump_initrd", ventoy_cmd_dump_initrd_list, 0, NULL, "", "", NULL }, + { "vt_linux_initrd_count", ventoy_cmd_initrd_count, 0, NULL, "", "", NULL }, + { "vt_linux_valid_initrd_count", ventoy_cmd_valid_initrd_count, 0, NULL, "", "", NULL }, + { "vt_linux_locate_initrd", ventoy_cmd_linux_locate_initrd, 0, NULL, "", "", NULL }, + { "vt_linux_chain_data", ventoy_cmd_linux_chain_data, 0, NULL, "", "", NULL }, + { "vt_linux_get_main_initrd_index", ventoy_cmd_linux_get_main_initrd_index, 0, NULL, "", "", NULL }, + + { "vt_windows_reset", ventoy_cmd_wimdows_reset, 0, NULL, "", "", NULL }, + { "vt_windows_chain_data", ventoy_cmd_windows_chain_data, 0, NULL, "", "", NULL }, + { "vt_windows_collect_wim_patch", ventoy_cmd_collect_wim_patch, 0, NULL, "", "", NULL }, + { "vt_windows_locate_wim_patch", ventoy_cmd_locate_wim_patch, 0, NULL, "", "", NULL }, + { "vt_windows_count_wim_patch", ventoy_cmd_wim_patch_count, 0, NULL, "", "", NULL }, + { "vt_dump_wim_patch", ventoy_cmd_dump_wim_patch, 0, NULL, "", "", NULL }, + { "vt_wim_check_bootable", ventoy_cmd_wim_check_bootable, 0, NULL, "", "", NULL }, + { "vt_wim_chain_data", ventoy_cmd_wim_chain_data, 0, NULL, "", "", NULL }, + + { "vt_add_replace_file", ventoy_cmd_add_replace_file, 0, NULL, "", "", NULL }, + { "vt_test_block_list", ventoy_cmd_test_block_list, 0, NULL, "", "", NULL }, + { "vt_file_exist_nocase", ventoy_cmd_file_exist_nocase, 0, NULL, "", "", NULL }, + + + { "vt_load_plugin", ventoy_cmd_load_plugin, 0, NULL, "", "", NULL }, + { "vt_check_plugin_json", ventoy_cmd_plugin_check_json, 0, NULL, "", "", NULL }, + { "vt_check_password", ventoy_cmd_check_password, 0, NULL, "", "", NULL }, + + { "vt_1st_line", ventoy_cmd_read_1st_line, 0, NULL, "", "", NULL }, + { "vt_file_strstr", ventoy_cmd_file_strstr, 0, NULL, "", "", NULL }, + { "vt_img_part_info", ventoy_cmd_img_part_info, 0, NULL, "", "", NULL }, + + + { "vt_parse_iso_volume", ventoy_cmd_parse_volume, 0, NULL, "", "", NULL }, + { "vt_parse_iso_create_date", ventoy_cmd_parse_create_date, 0, NULL, "", "", NULL }, + { "vt_parse_freenas_ver", ventoy_cmd_parse_freenas_ver, 0, NULL, "", "", NULL }, + { "vt_unix_parse_freebsd_ver", ventoy_cmd_unix_freebsd_ver, 0, NULL, "", "", NULL }, + { "vt_unix_parse_freebsd_ver_elf", ventoy_cmd_unix_freebsd_ver_elf, 0, NULL, "", "", NULL }, + { "vt_unix_reset", ventoy_cmd_unix_reset, 0, NULL, "", "", NULL }, + { "vt_unix_replace_conf", ventoy_cmd_unix_replace_conf, 0, NULL, "", "", NULL }, + { "vt_unix_replace_ko", ventoy_cmd_unix_replace_ko, 0, NULL, "", "", NULL }, + { "vt_unix_fill_image_desc", ventoy_cmd_unix_fill_image_desc, 0, NULL, "", "", NULL }, + { "vt_unix_gzip_new_ko", ventoy_cmd_unix_gzip_newko, 0, NULL, "", "", NULL }, + { "vt_unix_chain_data", ventoy_cmd_unix_chain_data, 0, NULL, "", "", NULL }, + + { "vt_img_hook_root", ventoy_cmd_img_hook_root, 0, NULL, "", "", NULL }, + { "vt_img_unhook_root", ventoy_cmd_img_unhook_root, 0, NULL, "", "", NULL }, + { "vt_acpi_param", ventoy_cmd_acpi_param, 0, NULL, "", "", NULL }, + { "vt_check_secureboot_var", ventoy_cmd_check_secureboot_var, 0, NULL, "", "", NULL }, + { "vt_clear_key", ventoy_cmd_clear_key, 0, NULL, "", "", NULL }, + { "vt_img_check_range", ventoy_cmd_img_check_range, 0, NULL, "", "", NULL }, + +}; + +int ventoy_register_all_cmd(void) +{ + grub_uint32_t i; + cmd_para *cur = NULL; + + for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++) + { + cur = ventoy_cmds + i; + cur->cmd = grub_register_extcmd(cur->name, cur->func, cur->flags, + cur->summary, cur->description, cur->parser); + } + return 0; +} + +int ventoy_unregister_all_cmd(void) +{ + grub_uint32_t i; + + for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++) + { + grub_unregister_extcmd(ventoy_cmds[i].cmd); + } + + return 0; +} + + diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h index 45f823f9..5e7e11d2 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h @@ -140,6 +140,7 @@ typedef struct cpio_newc_header typedef int (*grub_char_check_func)(int c); #define ventoy_is_decimal(str) ventoy_string_check(str, grub_isdigit) +#define OFFSET_OF(TYPE, MEMBER) ((grub_size_t) &((TYPE *)0)->MEMBER) #pragma pack(1) typedef struct ventoy_patch_vhd @@ -932,6 +933,8 @@ extern grub_uint8_t *g_conf_replace_new_buf; extern int g_conf_replace_new_len; extern int g_conf_replace_new_len_align; extern grub_uint64_t g_ventoy_disk_size; +extern grub_uint64_t g_ventoy_disk_part_size[2]; +extern grub_uint32_t g_ventoy_plat_data; #define ventoy_unix_fill_virt(new_data, new_len) \ { \ @@ -949,12 +952,16 @@ extern grub_uint64_t g_ventoy_disk_size; chain->virt_img_size_in_bytes += data_secs * 2048; \ } +#define ventoy_syscall0(name) grub_##name() +#define ventoy_syscall1(name, a) grub_##name(a) + char * ventoy_get_line(char *start); int ventoy_cmp_img(img_info *img1, img_info *img2); void ventoy_swap_img(img_info *img1, img_info *img2); char * ventoy_plugin_get_cur_install_template(const char *isopath); install_template * ventoy_plugin_find_install_template(const char *isopath); persistence_config * ventoy_plugin_find_persistent(const char *isopath); +grub_uint64_t ventoy_get_vtoy_partsize(int part); void ventoy_plugin_dump_injection(void); void ventoy_plugin_dump_auto_install(void); int ventoy_fill_windows_rtdata(void *buf, char *isopath); @@ -994,11 +1001,16 @@ grub_err_t ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt, int argc, char * grub_err_t ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt, int argc, char **args); grub_err_t ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt, int argc, char **args); int ventoy_check_password(const vtoy_password *pwd, int retry); -int ventoy_gzip_compress(void *mem_in, int mem_in_len, void *mem_out, int mem_out_len); -grub_uint64_t ventoy_get_part1_size(ventoy_gpt_info *gpt); int ventoy_plugin_add_custom_boot(const char *vcfgpath); const char * ventoy_plugin_get_custom_boot(const char *isopath); grub_err_t ventoy_cmd_dump_custom_boot(grub_extcmd_context_t ctxt, int argc, char **args); +int ventoy_gzip_compress(void *mem_in, int mem_in_len, void *mem_out, int mem_out_len); +int ventoy_load_part_table(const char *diskname); +int ventoy_env_init(void); +int ventoy_register_all_cmd(void); +int ventoy_unregister_all_cmd(void); + +#define VTOY_CMD_CHECK(a) if (33554432 != g_ventoy_disk_part_size[a]) ventoy_syscall0(exit) #endif /* __VENTOY_DEF_H__ */ diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_unix.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_unix.c index e74e7389..179739b2 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_unix.c +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_unix.c @@ -720,7 +720,7 @@ grub_err_t ventoy_cmd_unix_fill_image_desc(grub_extcmd_context_t ctxt, int argc, desc = (ventoy_image_desc *)(byte + i); desc->disk_size = g_ventoy_disk_size; - desc->part1_size = ventoy_get_part1_size(g_ventoy_part_info); + desc->part1_size = g_ventoy_disk_part_size[0]; grub_memcpy(desc->disk_uuid, g_ventoy_part_info->MBR.BootCode + 0x180, 16); grub_memcpy(desc->disk_signature, g_ventoy_part_info->MBR.BootCode + 0x1B8, 4); diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_vhd.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_vhd.c index 0b8b00f5..a5203e2f 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_vhd.c +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_vhd.c @@ -44,14 +44,12 @@ GRUB_MOD_LICENSE ("GPLv3+"); -static int g_vhdboot_bcd_offset = 0; -static int g_vhdboot_bcd_len = 0; static int g_vhdboot_isolen = 0; static char *g_vhdboot_totbuf = NULL; static char *g_vhdboot_isobuf = NULL; static grub_uint64_t g_img_trim_head_secnum = 0; -static int ventoy_vhd_find_bcd(int *bcdoffset, int *bcdlen) +static int ventoy_vhd_find_bcd(int *bcdoffset, int *bcdlen, const char *path) { grub_uint32_t offset; grub_file_t file; @@ -61,10 +59,9 @@ static int ventoy_vhd_find_bcd(int *bcdoffset, int *bcdlen) grub_script_execute_sourcecode(cmdbuf); - file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", "(vhdiso)/boot/bcd"); + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(vhdiso)%s", path); if (!file) { - grub_printf("Failed to open bcd file in the image file\n"); return 1; } @@ -83,13 +80,15 @@ static int ventoy_vhd_find_bcd(int *bcdoffset, int *bcdlen) return 0; } -static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2) +static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2, + int bcdoffset, int bcdlen) { int i; int cnt = 0; char *pos; grub_size_t pathlen; const char *plat; + char *newpath = NULL; grub_uint16_t *unicode_path; const grub_uint8_t winloadexe[] = { @@ -97,6 +96,11 @@ static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy 0x65, 0x00, 0x78, 0x00, 0x65, 0x00 }; + while ((*vhdpath) != '/') + { + vhdpath++; + } + pathlen = sizeof(grub_uint16_t) * (grub_strlen(vhdpath) + 1); debug("unicode path for <%s> len:%d\n", vhdpath, (int)pathlen); @@ -110,10 +114,10 @@ static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy if (plat && (plat[0] == 'e')) /* UEFI */ { - pos = g_vhdboot_isobuf + g_vhdboot_bcd_offset; + pos = g_vhdboot_isobuf + bcdoffset; /* winload.exe ==> winload.efi */ - for (i = 0; i + (int)sizeof(winloadexe) < g_vhdboot_bcd_len; i++) + for (i = 0; i + (int)sizeof(winloadexe) < bcdlen; i++) { if (*((grub_uint32_t *)(pos + i)) == 0x00690077 && grub_memcmp(pos + i, winloadexe, sizeof(winloadexe)) == 0) @@ -127,7 +131,8 @@ static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy debug("winload patch %d times\n", cnt); } - for (pos = vhdpath; *pos; pos++) + newpath = grub_strdup(vhdpath); + for (pos = newpath; *pos; pos++) { if (*pos == '/') { @@ -135,40 +140,127 @@ static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy } } - grub_utf8_to_utf16(unicode_path, pathlen, (grub_uint8_t *)vhdpath, -1, NULL); + grub_utf8_to_utf16(unicode_path, pathlen, (grub_uint8_t *)newpath, -1, NULL); grub_memcpy(patch1->vhd_file_path, unicode_path, pathlen); grub_memcpy(patch2->vhd_file_path, unicode_path, pathlen); + grub_free(newpath); return 0; } -static int ventoy_vhd_patch_disk(ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2) +static int ventoy_vhd_read_parttbl(const char *filename, ventoy_gpt_info *gpt, int *index) { + int ret = 1; + grub_file_t file = NULL; + grub_disk_t disk = NULL; + + file = grub_file_open(filename, VENTOY_FILE_TYPE); + if (!file) + { + goto end; + } + + disk = grub_disk_open(file->device->disk->name); + if (!disk) + { + goto end; + } + + *index = file->device->disk->partition->index; + grub_disk_read(disk, 0, 0, sizeof(ventoy_gpt_info), gpt); + + ret = 0; + +end: + check_free(file, grub_file_close); + check_free(disk, grub_disk_close); + + return ret; +} + +static int ventoy_vhd_patch_disk(const char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2) +{ + int partIndex = 0; + grub_uint64_t offset = 0; char efipart[16] = {0}; + ventoy_gpt_info *gpt = NULL; - grub_memcpy(efipart, g_ventoy_part_info->Head.Signature, sizeof(g_ventoy_part_info->Head.Signature)); + if (vhdpath[0] == '/') + { + gpt = g_ventoy_part_info; + partIndex = 0; + debug("This is Ventoy ISO partIndex %d %s\n", partIndex, vhdpath); + } + else + { + gpt = grub_zalloc(sizeof(ventoy_gpt_info)); + ventoy_vhd_read_parttbl(vhdpath, gpt, &partIndex); + debug("This is HDD partIndex %d %s\n", partIndex, vhdpath); + } - debug("part1 type: 0x%x <%s>\n", g_ventoy_part_info->MBR.PartTbl[0].FsFlag, efipart); + grub_memcpy(efipart, gpt->Head.Signature, sizeof(gpt->Head.Signature)); + + grub_memset(patch1, 0, OFFSET_OF(ventoy_patch_vhd, vhd_file_path)); + grub_memset(patch2, 0, OFFSET_OF(ventoy_patch_vhd, vhd_file_path)); if (grub_strncmp(efipart, "EFI PART", 8) == 0) { - ventoy_debug_dump_guid("GPT disk GUID: ", g_ventoy_part_info->Head.DiskGuid); - ventoy_debug_dump_guid("GPT part GUID: ", g_ventoy_part_info->PartTbl[0].PartGuid); + ventoy_debug_dump_guid("GPT disk GUID: ", gpt->Head.DiskGuid); + ventoy_debug_dump_guid("GPT partIndex GUID: ", gpt->PartTbl[partIndex].PartGuid); - grub_memcpy(patch1->disk_signature_or_guid, g_ventoy_part_info->Head.DiskGuid, 16); - grub_memcpy(patch1->part_offset_or_guid, g_ventoy_part_info->PartTbl[0].PartGuid, 16); - grub_memcpy(patch2->disk_signature_or_guid, g_ventoy_part_info->Head.DiskGuid, 16); - grub_memcpy(patch2->part_offset_or_guid, g_ventoy_part_info->PartTbl[0].PartGuid, 16); + grub_memcpy(patch1->disk_signature_or_guid, gpt->Head.DiskGuid, 16); + grub_memcpy(patch1->part_offset_or_guid, gpt->PartTbl[partIndex].PartGuid, 16); + grub_memcpy(patch2->disk_signature_or_guid, gpt->Head.DiskGuid, 16); + grub_memcpy(patch2->part_offset_or_guid, gpt->PartTbl[partIndex].PartGuid, 16); patch1->part_type = patch2->part_type = 0; } else { - debug("MBR disk signature: %02x%02x%02x%02x\n", - g_ventoy_part_info->MBR.BootCode[0x1b8 + 0], g_ventoy_part_info->MBR.BootCode[0x1b8 + 1], - g_ventoy_part_info->MBR.BootCode[0x1b8 + 2], g_ventoy_part_info->MBR.BootCode[0x1b8 + 3]); - grub_memcpy(patch1->disk_signature_or_guid, g_ventoy_part_info->MBR.BootCode + 0x1b8, 4); - grub_memcpy(patch2->disk_signature_or_guid, g_ventoy_part_info->MBR.BootCode + 0x1b8, 4); + offset = gpt->MBR.PartTbl[partIndex].StartSectorId; + offset *= 512; + + debug("MBR disk signature: %02x%02x%02x%02x Part(%d) offset:%llu\n", + gpt->MBR.BootCode[0x1b8 + 0], gpt->MBR.BootCode[0x1b8 + 1], + gpt->MBR.BootCode[0x1b8 + 2], gpt->MBR.BootCode[0x1b8 + 3], + partIndex + 1, offset); + + grub_memcpy(patch1->part_offset_or_guid, &offset, 8); + grub_memcpy(patch2->part_offset_or_guid, &offset, 8); + + grub_memcpy(patch1->disk_signature_or_guid, gpt->MBR.BootCode + 0x1b8, 4); + grub_memcpy(patch2->disk_signature_or_guid, gpt->MBR.BootCode + 0x1b8, 4); + + patch1->part_type = patch2->part_type = 1; + } + + if (gpt != g_ventoy_part_info) + { + grub_free(gpt); + } + + return 0; +} + +static int ventoy_find_vhdpatch_offset(int bcdoffset, int bcdlen, int *offset) +{ + int i; + int cnt = 0; + grub_uint8_t *buf = (grub_uint8_t *)(g_vhdboot_isobuf + bcdoffset); + grub_uint8_t magic[16] = { + 0x5C, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00 + }; + + for (i = 0; i < bcdlen - 16 && cnt < 2; i++) + { + if (*(grub_uint32_t *)(buf + i) == 0x0058005C) + { + if (grub_memcmp(magic, buf + i, 16) == 0) + { + *offset++ = i - (int)OFFSET_OF(ventoy_patch_vhd, vhd_file_path); + cnt++; + } + } } return 0; @@ -177,6 +269,8 @@ static int ventoy_vhd_patch_disk(ventoy_patch_vhd *patch1, ventoy_patch_vhd *pat grub_err_t ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args) { int rc; + int bcdoffset, bcdlen; + int patchoffset[2]; ventoy_patch_vhd *patch1; ventoy_patch_vhd *patch2; char envbuf[64]; @@ -194,18 +288,39 @@ grub_err_t ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt, int argc, char * return 0; } - rc = ventoy_vhd_find_bcd(&g_vhdboot_bcd_offset, &g_vhdboot_bcd_len); + rc = ventoy_vhd_find_bcd(&bcdoffset, &bcdlen, "/boot/bcd"); if (rc) { debug("failed to get bcd location %d\n", rc); - return 0; + } + else + { + ventoy_find_vhdpatch_offset(bcdoffset, bcdlen, patchoffset); + patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[0]); + patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[1]); + + debug("Find /boot/bcd (%d %d) now patch it (offset: 0x%x 0x%x) ...\n", + bcdoffset, bcdlen, patchoffset[0], patchoffset[1]); + ventoy_vhd_patch_disk(args[0], patch1, patch2); + ventoy_vhd_patch_path(args[0], patch1, patch2, bcdoffset, bcdlen); } - patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + g_vhdboot_bcd_offset + 0x495a); - patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + g_vhdboot_bcd_offset + 0x50aa); - - ventoy_vhd_patch_disk(patch1, patch2); - ventoy_vhd_patch_path(args[0], patch1, patch2); + rc = ventoy_vhd_find_bcd(&bcdoffset, &bcdlen, "/boot/BCD"); + if (rc) + { + debug("No file /boot/BCD \n"); + } + else + { + ventoy_find_vhdpatch_offset(bcdoffset, bcdlen, patchoffset); + patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[0]); + patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[1]); + + debug("Find /boot/BCD (%d %d) now patch it (offset: 0x%x 0x%x) ...\n", + bcdoffset, bcdlen, patchoffset[0], patchoffset[1]); + ventoy_vhd_patch_disk(args[0], patch1, patch2); + ventoy_vhd_patch_path(args[0], patch1, patch2, bcdoffset, bcdlen); + } /* set buffer and size */ #ifdef GRUB_MACHINE_EFI @@ -220,7 +335,7 @@ grub_err_t ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt, int argc, char * grub_env_set("vtoy_vhd_buf_size", envbuf); #endif - return 0; + VENTOY_CMD_RETURN(GRUB_ERR_NONE); } grub_err_t ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args) diff --git a/INSTALL/Ventoy2Disk.exe b/INSTALL/Ventoy2Disk.exe index d5a1798d14e0d4f70bf02f54ead1b82f1a527a9e..da7243aed4ab1cc2b22f54c998c66b9d5e6aeaca 100644 GIT binary patch delta 59237 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 delta 55881 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

P@fn;bki$(waln{NgL5B&EB4&;9hR8DS_ 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 diff --git a/INSTALL/grub/grub.cfg b/INSTALL/grub/grub.cfg index 8990ad5f..9c4d6dfd 100644 --- a/INSTALL/grub/grub.cfg +++ b/INSTALL/grub/grub.cfg @@ -131,7 +131,7 @@ function get_os_type { function vt_check_compatible_pe { #Check for PE without external tools if [ -f "$1/HBCD_PE.ini" ]; then - set ventoy_compatible=YES + set ventoy_compatible=YES fi } @@ -1230,6 +1230,30 @@ function efi_unsupport_menuentry { common_unsupport_menuentry } +function vhdboot_common_func { + vt_patch_vhdboot "$1" + + ventoy_debug_pause + + if [ -n "$vtoy_vhd_buf_addr" ]; then + if [ "$grub_platform" = "pc" ]; then + ventoy_cli_console + linux16 $vtoy_path/memdisk iso raw + initrd16 mem:${vtoy_vhd_buf_addr}:size:${vtoy_vhd_buf_size} + boot + ventoy_gui_console + else + ventoy_cli_console + chainloader ${vtoy_path}/ventoy_${VTOY_EFI_ARCH}.efi memdisk env_param=${env_param} isoefi=${LoadIsoEfiDriver} ${vtdebug_flag} mem:${vtoy_vhd_buf_addr}:size:${vtoy_vhd_buf_size} + boot + ventoy_gui_console + fi + else + echo "Please put the right ventoy_vhdboot.img file to the 1st partition" + ventoy_pause + fi +} + function vhd_common_menuentry { if [ "$VTOY_VHD_NO_WARNING" != "1" ]; then @@ -1252,25 +1276,7 @@ function vhd_common_menuentry { return fi - vt_patch_vhdboot "$vt_chosen_path" - - ventoy_debug_pause - - if [ -n "$vtoy_vhd_buf_addr" ]; then - if [ "$grub_platform" = "pc" ]; then - linux16 $vtoy_path/memdisk iso raw - initrd16 mem:${vtoy_vhd_buf_addr}:size:${vtoy_vhd_buf_size} - boot - else - ventoy_cli_console - chainloader ${vtoy_path}/ventoy_${VTOY_EFI_ARCH}.efi memdisk env_param=${env_param} isoefi=${LoadIsoEfiDriver} ${vtdebug_flag} mem:${vtoy_vhd_buf_addr}:size:${vtoy_vhd_buf_size} - boot - ventoy_gui_console - fi - else - echo "Please put the right ventoy_vhdboot.img file to the 1st partition" - ventoy_pause - fi + vhdboot_common_func "${vt_chosen_path}" } function vhd_unsupport_menuentry { diff --git a/INSTALL/tool/VentoyWorker.sh b/INSTALL/tool/VentoyWorker.sh index c820ffc4..543ac697 100644 --- a/INSTALL/tool/VentoyWorker.sh +++ b/INSTALL/tool/VentoyWorker.sh @@ -365,11 +365,15 @@ else oldver=$(get_disk_ventoy_version $DISK) if [ $? -ne 0 ]; then - vtwarn "$DISK does not contain Ventoy or data corrupted" - echo "" - vtwarn "Please use -i option if you want to install ventoy to $DISK" - echo "" - exit 1 + if is_disk_contains_ventoy $DISK; then + oldver="Unknown" + else + vtwarn "$DISK does not contain Ventoy or data corrupted" + echo "" + vtwarn "Please use -i option if you want to install ventoy to $DISK" + echo "" + exit 1 + fi fi #reserve secure boot option diff --git a/INSTALL/ventoy_pack.sh b/INSTALL/ventoy_pack.sh index 577a23a0..11b2a289 100644 --- a/INSTALL/ventoy_pack.sh +++ b/INSTALL/ventoy_pack.sh @@ -151,7 +151,7 @@ tar -czvf ventoy-${curver}-linux.tar.gz $tmpdir rm -f ventoy-${curver}-windows.zip cp $OPT Ventoy2Disk*.exe $tmpdir/ -cp $OPT $LANG_DIR/languages.ini $tmpdir/ventoy/ +cp $OPT $LANG_DIR/languages.json $tmpdir/ventoy/ rm -rf $tmpdir/tool rm -f $tmpdir/*.sh rm -rf $tmpdir/WebUI diff --git a/LANGUAGES/README b/LANGUAGES/README new file mode 100644 index 00000000..7a5b4f25 --- /dev/null +++ b/LANGUAGES/README @@ -0,0 +1,17 @@ + +File encoding: UTF-8 + +language name must in the format: "Language-XXX (YYY)" + 1. Language- fixed 9 characters + 2. XXX: name in English + 3. a space (ASCII: 0x20) + 4. a left brace (ASCII: 0x28) + 5. YYY: name in the specified language + 6. a right brace (ASCII: 0x29) + +string translation: + all the String Define + #@ will be replaced with \r\n + +All the languages will be sorted by the name + diff --git a/LANGUAGES/check.sh b/LANGUAGES/check.sh new file mode 100644 index 00000000..a18c1a8d --- /dev/null +++ b/LANGUAGES/check.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +VTOY_PATH=$1 + +if [ ! -f $VTOY_PATH/LANGUAGES/languages.json ]; then + exit 1 +fi + +gcc -DFOR_VTOY_JSON_CHECK $VTOY_PATH/Ventoy2Disk/Ventoy2Disk/VentoyJson.c -I $VTOY_PATH/Ventoy2Disk/Ventoy2Disk/ -o checkjson + +./checkjson $VTOY_PATH/LANGUAGES/languages.json +ret=$? + +rm -f ./checkjson +[ $ret -eq 0 ] + + diff --git a/LANGUAGES/languages.ini b/LANGUAGES/languages.ini deleted file mode 100644 index c40c7521616bb67d2b0e72ed827b383b1225debf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178284 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 2TB aus.", + "STR_SHOW_ALL_DEV":"Alle Geräte anzeigen", + "STR_PART_ALIGN_4KB":"Richten Sie Partitionen mit 4KB aus", + "STR_WEB_COMMUNICATION_ERR":"Kommunikationsfehler:", + "STR_WEB_REMOTE_ABNORMAL":"Kommunikationsfehler: unnormales Verhalten", + "STR_WEB_REQUEST_TIMEOUT":"Kommunikationsfehler: Zeitüberschreitung der Anforderung", + "STR_WEB_SERVICE_UNAVAILABLE":"Kommunikationsfehler: Dienst nicht verfügbar", + "STR_WEB_TOKEN_MISMATCH":"Daemon-Status aktualisiert, bitte später erneut versuchen.", + "STR_WEB_SERVICE_BUSY":"Dienst ist ausgelastet, bitte später erneut versuchen.", + "STRXXX":"" + }, + { + "name":"Occitan (Occitan)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"quentin", + + "STR_ERROR":"Error", + "STR_WARNING":"Avertiment", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Aqueste programa deu s’executar dins lo bon repertòri!", + "STR_INCORRECT_TREE_DIR":"Lancetz pas aquò, mercés de telecargar lo programa d’installacion e relançatz-lo.", + "STR_DEVICE":"Periferic", + "STR_LOCAL_VER":"Ventoy en local", + "STR_DISK_VER":"Ventoy sul periferic", + "STR_STATUS":"Estat - prèst", + "STR_INSTALL":"Installacion", + "STR_UPDATE":"Mesa a jorn", + "STR_UPDATE_TIP":"La mesa a jorn es segura, los fichièrs ISO seràn pas modificats.#@Continhar ?", + "STR_INSTALL_TIP":"Lo disc serà formatat e totas sas donadas seràn perdudas.#@Contunhar ?", + "STR_INSTALL_TIP2":"Lo disc serà formatat e totas sas donadas seràn perdudas.#@Contunhar ? (confirmacion)", + "STR_INSTALL_SUCCESS":"Felicitacions !#@Ventoy es estat corrèctament installat sul periferic.", + "STR_INSTALL_FAILED":"Una error s’es producha pendent l’installacion. Podètz tornar brancar lo periferic USB e tornar ensajar. Agachatz lo fichièr log.txt per ne saber mai.", + "STR_UPDATE_SUCCESS":"Felicitacions !#@Ventoy es estat corrèctament mes a jorn sul periferic.", + "STR_UPDATE_FAILED":"Una error s’es producha pendent la mesa a jorn. Podètz tornar brancar lo periferic USB e tornar ensajar. Agachatz lo fichièr log.txt per ne saber mai.", + "STR_WAIT_PROCESS":"Una operacion es en cors, esperatz...", + "STR_MENU_OPTION":"Opcion", + "STR_MENU_SECURE_BOOT":"Secure Boot", + "STR_MENU_PART_CFG":"Configuracion de particion", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Anullar", + "STR_PRESERVE_SPACE":"Preserve some space at the end of the disk", + "STR_SPACE_VAL_INVALID":"Valor invalida per l’espaci reservat", + "STR_MENU_CLEAR":"Escafar Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy es estat corrèctament tirat del periferic.", + "STR_CLEAR_FAILED":"Una error s’es producha pendent l’esfaçament de Ventoy del disc. Podètz tornar brancar l’USB e tornar ensajar. Vejatz log.txt pels detalhs.", + "STR_MENU_PART_STYLE":"Estil de particion", + "STR_DISK_2TB_MBR_ERROR":"Pels disques de mai de 2To seleccionatz GPT", + "STR_SHOW_ALL_DEV":"Mostrar totes los periferics", + "STR_PART_ALIGN_4KB":"Alinhar las particions sus 4Ko", + "STR_WEB_COMMUNICATION_ERR":"Error de comunicacion :", + "STR_WEB_REMOTE_ABNORMAL":"Error de comunicacion : remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Error de comunicacion: requèsta tardièra", + "STR_WEB_SERVICE_UNAVAILABLE":"Error de comunicacion : servici pas disponible", + "STR_WEB_TOKEN_MISMATCH":"Estat del Daemon actualizat, tornatz ensajar mai tard.", + "STR_WEB_SERVICE_BUSY":"Lo servici es ocupat, tornatz ensajar mai tard.", + "STRXXX":"" + }, + { + "name":"French (Français)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"vboucard", + + "STR_ERROR":"Erreur", + "STR_WARNING":"Avertissement", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Ce programme doit s'exécuter dans le bon répertoire !", + "STR_INCORRECT_TREE_DIR":"Ne me lancez pas d'ici, veuillez télécharger le programme d'installation et relancez-le.", + "STR_DEVICE":"Périphérique", + "STR_LOCAL_VER":"Ventoy en local", + "STR_DISK_VER":"Ventoy sur le périphérique", + "STR_STATUS":"Etat - prêt", + "STR_INSTALL":"Installation", + "STR_UPDATE":"Mise à jour", + "STR_UPDATE_TIP":"La mise à jour est sûre, les fichiers ISO ne seront pas modifiés.#@Continuer?", + "STR_INSTALL_TIP":"Le disque va être formaté et toutes ses données seront perdues.#@Continuer?", + "STR_INSTALL_TIP2":"Le disque va être formaté et toutes ses données seront perdues.#@Continuer? (confirmation)", + "STR_INSTALL_SUCCESS":"Félicitations !#@Ventoy a été correctement installé sur le périphérique.", + "STR_INSTALL_FAILED":"Une erreur est survenue durant l'installation. Vous pouvez rebrancher le périphérique USB et réessayer. Vérifiez le fichier log.txt pour plus de détails.", + "STR_UPDATE_SUCCESS":"Félicitations !#@Ventoy a été correctement mis à jour sur le périphérique.", + "STR_UPDATE_FAILED":"Une erreur est survenue durant la mise à jour. Vous pouvez rebrancher le périphérique USB et réessayer. Vérifiez le fichier log.txt pour plus de détails.", + "STR_WAIT_PROCESS":"Une opération est en cours, veuillez patienter...", + "STR_MENU_OPTION":"Option", + "STR_MENU_SECURE_BOOT":"Secure Boot", + "STR_MENU_PART_CFG":"Configuration de partition", + "STR_BTN_OK":"D'accord", + "STR_BTN_CANCEL":"Annuler", + "STR_PRESERVE_SPACE":"Préservez de l'espace au bas du disque", + "STR_SPACE_VAL_INVALID":"Valeur non valide pour l'espace réservé", + "STR_MENU_CLEAR":"Effacer Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy a été enlevé du périphérique avec succès.", + "STR_CLEAR_FAILED":"Une erreur est survenue pendant la suppression de Ventoy. Vous pouvez rebrancher le périphérique USB. Vérifiez le fichier log.txt pour plus de détails.", + "STR_MENU_PART_STYLE":"Type de partition", + "STR_DISK_2TB_MBR_ERROR":"Sélectionnez le type GPT pour les disques de plus de 2TO", + "STR_SHOW_ALL_DEV":"Afficher tous les appareils", + "STR_PART_ALIGN_4KB":"Alignez les partitions avec 4 KO", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Czech (Čeština)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"vavanade", + + "STR_ERROR":"Chyba", + "STR_WARNING":"Varování", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Spusťte prosím ve správném adresáři!", + "STR_INCORRECT_TREE_DIR":"Nespouštějte mě zde, stáhněte si prosím vydaný instalační balík a spusťte v něm.", + "STR_DEVICE":"Zařízení", + "STR_LOCAL_VER":"Ventoy v balíčku", + "STR_DISK_VER":"Ventoy v zařízení", + "STR_STATUS":"Status - PŘIPRAVENO", + "STR_INSTALL":"Instalovat", + "STR_UPDATE":"Aktualizovat", + "STR_UPDATE_TIP":"Operace aktualizace je bezpečná, ISO soubory nebudou změněny.#@Pokračovat?", + "STR_INSTALL_TIP":"Disk bude zformátován a všechna data budou ztracena!#@Pokračovat?", + "STR_INSTALL_TIP2":"Disk bude zformátován a všechna data budou ztracena!#@Pokračovat? (druhá kontrola)", + "STR_INSTALL_SUCCESS":"Gratulujeme!#@Ventoy byla na zařízení úspěšně nainstalována.", + "STR_INSTALL_FAILED":"V průběhu instalace se vyskytla chyba. Můžete vyjmout a znovu zastrčit USB zařízení a zkusit to znovu. Pro podrobnosti se podívejte do souboru log.txt.", + "STR_UPDATE_SUCCESS":"Gratulujeme!#@Ventoy byla na zařízení úspěšně aktualizována.", + "STR_UPDATE_FAILED":"V průběhu aktualizace se vyskytla chyba. Můžete vyjmout a znovu zastrčit USB zařízení a zkusit to znovu. Pro podrobnosti se podívejte do souboru log.txt.", + "STR_WAIT_PROCESS":"Vlákno běží, prosíme vyčkejte...", + "STR_MENU_OPTION":"Možnosti", + "STR_MENU_SECURE_BOOT":"Secure Boot", + "STR_MENU_PART_CFG":"Konfigurace oddílu", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"zrušení", + "STR_PRESERVE_SPACE":"Zachovejte místo na spodní straně disku", + "STR_SPACE_VAL_INVALID":"Neplatná hodnota pro vyhrazený prostor", + "STR_MENU_CLEAR":"Vymazat Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy has been successfully removed from the device.", + "STR_CLEAR_FAILED":"An error occurred when clear Ventoy from disk. You can replug the USB and try again. Check log.txt for detail.", + "STR_MENU_PART_STYLE":"Styl oddílu", + "STR_DISK_2TB_MBR_ERROR":"Vyberte GPT pro disk přes 2TB", + "STR_SHOW_ALL_DEV":"Zobrazit všechna zařízení", + "STR_PART_ALIGN_4KB":"Zarovnejte oddíly s 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Spanish (Español)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Carlos Sánchez, MELERIX", + + "STR_ERROR":"Error", + "STR_WARNING":"Advertencia", + "STR_INFO":"Información", + "STR_INCORRECT_DIR":"¡Por favor, ejecuta bajo el directorio correcto!", + "STR_INCORRECT_TREE_DIR":"No me ejecute aquí, por favor descarga el paquete de instalacion lanzado, y ejecutalo allí.", + "STR_DEVICE":"Dispositivo", + "STR_LOCAL_VER":"Ventoy En Paquete", + "STR_DISK_VER":"Ventoy En Dispositivo", + "STR_STATUS":"Estado - LISTO", + "STR_INSTALL":"Instalar", + "STR_UPDATE":"Actualizar", + "STR_UPDATE_TIP":"La operación de actualización es segura, Los archivo ISO no se modificarán.#@¿Continuar?", + "STR_INSTALL_TIP":"El dispositivo será formateado y todos los datos se perderán.#@¿Continuar?", + "STR_INSTALL_TIP2":"El dispositivo será formateado y todos los datos se perderán.#@¿Continuar? (Doble Comprobación)", + "STR_INSTALL_SUCCESS":"¡Felicitaciones!#@Ventoy ha sido instalado exitosamente en el dispositivo.", + "STR_INSTALL_FAILED":"Ocurrío un error durante la instalación. Puedes reconectar el USB e intentar de nuevo. Comprueba log.txt para detalles.", + "STR_UPDATE_SUCCESS":"¡Felicitaciones!#@Ventoy ha sido actualizado exitosamente en el dispositivo.", + "STR_UPDATE_FAILED":"Ocurrío un error durante la actualización. Puedes reconectar el USB e intentar de nuevo. Comprueba log.txt para detalles.", + "STR_WAIT_PROCESS":"Un hilo está ejecutandose, por favor espera...", + "STR_MENU_OPTION":"Opción", + "STR_MENU_SECURE_BOOT":"Soporte de Arranque Seguro", + "STR_MENU_PART_CFG":"Confirguración de Partición", + "STR_BTN_OK":"ACEPTAR", + "STR_BTN_CANCEL":"Cancelar", + "STR_PRESERVE_SPACE":"Preservar algo de espacio en la parte inferior del dispositivo", + "STR_SPACE_VAL_INVALID":"Valor inválido para espacio preservado", + "STR_MENU_CLEAR":"Limpiar Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy ha sido removido exotosamente desde el dispositivo.", + "STR_CLEAR_FAILED":"Ocurrío un error al limpiar Ventoy desde el dispositivo. Puedes reconectar el USB e intentar de nuevo. Comprueba log.txt para detalle.", + "STR_MENU_PART_STYLE":"Estilo de Partición", + "STR_DISK_2TB_MBR_ERROR":"Por favor selecciona GPT para dispositivos sobre 2TB", + "STR_SHOW_ALL_DEV":"Mostrar todos los dispositivos", + "STR_PART_ALIGN_4KB":"Alinear particiones con 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Russian (Pусский)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"BL4CKH47H4CK3R", + + "STR_ERROR":"Ошибка", + "STR_WARNING":"Предупреждение", + "STR_INFO":"Информация", + "STR_INCORRECT_DIR":"Пожалуйста, запустите в другом каталоге!", + "STR_INCORRECT_TREE_DIR":"Не запускайте меня здесь, пожалуйста, загрузите выпущенный установочный пакет и запустите его в другом месте.", + "STR_DEVICE":"Устройство", + "STR_LOCAL_VER":"Ventoy в пакете", + "STR_DISK_VER":"Ventoy на устройстве", + "STR_STATUS":"Статус - ГОТОВ", + "STR_INSTALL":"Установить", + "STR_UPDATE":"Обновить", + "STR_UPDATE_TIP":"Обновление безопасно, ISO-файлы останутся без изменений.#@Продолжить?", + "STR_INSTALL_TIP":"Диск будет отформатирован и все данные будут потеряны.#@Продолжить?", + "STR_INSTALL_TIP2":"Диск будет отформатирован и все данные будут потеряны.#@Вы ДЕЙСТВИТЕЛЬНО хотите продолжить?", + "STR_INSTALL_SUCCESS":"Поздравляем!#@Ventoy был успешно установлен на устройство.", + "STR_INSTALL_FAILED":"Во время установки Ventoy произошла ошибка. Переподключите устройство и попробуйте снова. Проверьте log.txt на ошибки.", + "STR_UPDATE_SUCCESS":"Поздравляем!#@Ventoy был успешно обновлен на устройстве.", + "STR_UPDATE_FAILED":"Во время обновления Ventoy произошла ошибка. Переподключите устройство и попробуйте снова. Проверьте log.txt на ошибки.", + "STR_WAIT_PROCESS":"Процесс запущен, пожалуйста подождите...", + "STR_MENU_OPTION":"Опции", + "STR_MENU_SECURE_BOOT":"Поддержка Secure Boot", + "STR_MENU_PART_CFG":"Дополнительный раздел", + "STR_BTN_OK":"ОК", + "STR_BTN_CANCEL":"Отмена", + "STR_PRESERVE_SPACE":"Создать дополнительный раздел в конце диска", + "STR_SPACE_VAL_INVALID":"Неверное значение размера раздела", + "STR_MENU_CLEAR":"Удалить Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy был успешно удалён с устройства.", + "STR_CLEAR_FAILED":"Во время удаления Ventoy произошла ошибка. Переподключите устройство и попробуйте снова. Проверьте log.txt на ошибки.", + "STR_MENU_PART_STYLE":"Стиль разметки разделов", + "STR_DISK_2TB_MBR_ERROR":"Пожалуйста, выберите GPT для дисков более 2ТБ", + "STR_SHOW_ALL_DEV":"Показать все устройства", + "STR_PART_ALIGN_4KB":"Выровнять разделы с размером 4КБ", + "STR_WEB_COMMUNICATION_ERR":"Ошибка связи:", + "STR_WEB_REMOTE_ABNORMAL":"Ошибка связи: Удаленное соединение недействительно", + "STR_WEB_REQUEST_TIMEOUT":"Ошибка связи: Истекло время ожидания запроса", + "STR_WEB_SERVICE_UNAVAILABLE":"Ошибка связи: Служба недоступна", + "STR_WEB_TOKEN_MISMATCH":"Статус демона обновлен. Повторите попытку позже.", + "STR_WEB_SERVICE_BUSY":"Служба занята, повторите попытку позже.", + "STRXXX":"" + }, + { + "name":"Bengali (বাংলা)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"BL4CKH47H4CK3R", + + "STR_ERROR":"ত্রুটি", + "STR_WARNING":"সতর্কতা", + "STR_INFO":"তথ্য", + "STR_INCORRECT_DIR":"দয়া করে সঠিক ডিরেক্টরিতে চালান!", + "STR_INCORRECT_TREE_DIR":"আমাকে এখানে চালাবেন না, দয়া করে প্রকাশিত ইনস্টল প্যাকেজটি ডাউনলোড করুন এবং সেখানে চালান।", + "STR_DEVICE":"ডিভাইস", + "STR_LOCAL_VER":"Ventoy প্যাকেজে আছে", + "STR_DISK_VER":"Ventoy ডিভাইসে আছে", + "STR_STATUS":"স্থিতি - প্রস্তুত", + "STR_INSTALL":"ইনস্টল করুন", + "STR_UPDATE":"আপডেট করুন", + "STR_UPDATE_TIP":"আপগ্রেড অপারেশন নিরাপদ, ISO ফাইলগুলি অপরিবর্তিত থাকবে।#@চালিয়ে যাবেন?", + "STR_INSTALL_TIP":"ডিস্কটি ফর্ম্যাট করা হবে এবং সমস্ত ডেটা হারিয়ে যাবে।#@চালিয়ে যাবেন?", + "STR_INSTALL_TIP2":"ডিস্কটি ফর্ম্যাট করা হবে এবং সমস্ত ডেটা হারিয়ে যাবে।#@চালিয়ে যাবেন? (পুনঃনিরীক্ষণ)", + "STR_INSTALL_SUCCESS":"অভিনন্দন! #@Ventoy সফলভাবে ডিভাইসে ইনস্টল করা হয়েছে।", + "STR_INSTALL_FAILED":"ইনস্টলেশন চলাকালীন একটি ত্রুটি ঘটেছে। আপনি USB পুনরায় প্লাগ করতে পারেন এবং আবার চেষ্টা করতে পারেন। বিস্তারিত জানার জন্য log.txt পরীক্ষা করুন।", + "STR_UPDATE_SUCCESS":"অভিনন্দন! #@Ventoy সফলভাবে ডিভাইসে আপডেট করা হয়েছে।", + "STR_UPDATE_FAILED":"আপডেটের সময় একটি ত্রুটি ঘটেছে। আপনি USB পুনরায় প্লাগ করতে পারেন এবং আবার চেষ্টা করতে পারেন। বিস্তারিত জানার জন্য log.txt পরীক্ষা করুন।", + "STR_WAIT_PROCESS":"একটি থ্রেড চলছে, দয়া করে অপেক্ষা করুন ...", + "STR_MENU_OPTION":"অপসন", + "STR_MENU_SECURE_BOOT":"নিরাপদ বুট", + "STR_MENU_PART_CFG":"পার্টিশন কনফিগারেশন", + "STR_BTN_OK":"ঠিক আছে", + "STR_BTN_CANCEL":"বাতিল", + "STR_PRESERVE_SPACE":"ডিস্কের নীচে কিছু স্থান সংরক্ষণ করুন", + "STR_SPACE_VAL_INVALID":"সংরক্ষিত জায়গার জন্য অবৈধ মান", + "STR_MENU_CLEAR":"Ventoy সাফ", + "STR_CLEAR_SUCCESS":"Ventoy সফলভাবে ডিভাইস থেকে সরানো হয়েছে।", + "STR_CLEAR_FAILED":"ডিস্ক থেকে Ventoy সাফ করার সময় একটি ত্রুটি ঘটেছে। আপনি USB পুনরায় প্লাগ করতে পারেন এবং আবার চেষ্টা করতে পারেন। বিস্তারিত জানার জন্য log.txt পরীক্ষা করুন।", + "STR_MENU_PART_STYLE":"পার্টিশন স্টাইল", + "STR_DISK_2TB_MBR_ERROR":"2TB এর বেশি ডিস্কের জন্য দয়া করে GPT নির্বাচন করুন", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Hindi (हिन्दी)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"BL4CKH47H4CK3R", + + "STR_ERROR":"त्रुटि", + "STR_WARNING":"चेतावनी", + "STR_INFO":"जानकारी", + "STR_INCORRECT_DIR":"कृपया सही निर्देशिका के तहत चलाएं!", + "STR_INCORRECT_TREE_DIR":"मुझे यहां न चलाएं, कृपया जारी किए गए इंस्टॉल पैकेज को डाउनलोड करें, और वहां चलाएं।", + "STR_DEVICE":"डिवाइस", + "STR_LOCAL_VER":"पैकेज में Ventoy", + "STR_DISK_VER":"डिवाइस में Ventoy", + "STR_STATUS":"स्थिति - तैयार", + "STR_INSTALL":"इंस्टॉल", + "STR_UPDATE":"अपडेट करें", + "STR_UPDATE_TIP":"नवीनीकरण ऑपरेशन सुरक्षित है, ISO फाइल अपरिवर्तित रहेंगी।#@जारी रखें?", + "STR_INSTALL_TIP":"डिस्क को स्वरूपित किया जाएगा और सभी डेटा खो जाएगा।#@जारी रखें?", + "STR_INSTALL_TIP2":"डिस्क को स्वरूपित किया जाएगा और सभी डेटा खो जाएगा।#@जारी रखें? (दोहरी जाँच)", + "STR_INSTALL_SUCCESS":"बधाई! #@Ventoy डिवाइस में सफलतापूर्वक स्थापित किया गया है।", + "STR_INSTALL_FAILED":"स्थापना के दौरान एक त्रुटि हुई। आप USB को पुन: स्थापित कर सकते हैं और पुनः प्रयास कर सकते हैं। विस्तार के लिए log.txt की जाँच करें।", + "STR_UPDATE_SUCCESS":"बधाई! #@Ventoy डिवाइस में सफलतापूर्वक अपडेट हो गई है।", + "STR_UPDATE_FAILED":"अपडेट के दौरान एक त्रुटि हुई। आप USB को पुन: भर सकते हैं और पुनः प्रयास कर सकते हैं। विस्तार के लिए log.txt की जाँच करें।", + "STR_WAIT_PROCESS":"एक धागा चल रहा है, कृपया प्रतीक्षा करें ...", + "STR_MENU_OPTION":"विकल्प", + "STR_MENU_SECURE_BOOT":"सुरक्षित बूट", + "STR_MENU_PART_CFG":"विभाजन विन्यास", + "STR_BTN_OK":"ठीक है", + "STR_BTN_CANCEL":"रद्द करना", + "STR_PRESERVE_SPACE":"डिस्क के निचले भाग में कुछ स्थान सुरक्षित रखें", + "STR_SPACE_VAL_INVALID":"आरक्षित स्थान के लिए अमान्य मान", + "STR_MENU_CLEAR":"Ventoy को हटा दें", + "STR_CLEAR_SUCCESS":"डिवाइस से Ventoy को सफलतापूर्वक हटा दिया गया है।", + "STR_CLEAR_FAILED":"डिस्क से Ventoy को साफ़ करते समय एक त्रुटि हुई। आप USB को पुन: भर सकते हैं और पुनः प्रयास कर सकते हैं। विस्तार के लिए log.txt की जाँच करें।", + "STR_MENU_PART_STYLE":"विभाजन शैली", + "STR_DISK_2TB_MBR_ERROR":"कृपया 2TB से अधिक डिस्क के लिए GPT का चयन करें", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Dutch (Nederlands)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"UmitCanbolat", + + "STR_ERROR":"Fout", + "STR_WARNING":"Waarschuwing", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Voer Ventoy uit in de juiste directory!", + "STR_INCORRECT_TREE_DIR":"Ventoy kan hier niet worden uitgevoerd. Download het installatiepakket en probeer Ventoy daarmee te starten.", + "STR_DEVICE":"Apparaat", + "STR_LOCAL_VER":"Ventoy in pakket", + "STR_DISK_VER":"Ventoy op apparaat", + "STR_STATUS":"Status - GEREED", + "STR_INSTALL":"Installeren", + "STR_UPDATE":"Bijwerken", + "STR_UPDATE_TIP":"Upgraden is veilig: ISO-bestanden blijven ongewijzigd.#@Doorgaan?", + "STR_INSTALL_TIP":"De schijf wordt geformatteerd en alle gegevens gaan verloren.#@Doorgaan?", + "STR_INSTALL_TIP2":"De schijf wordt geformatteerd en alle gegevens gaan verloren.#@Doorgaan? (Dubbelcheck)", + "STR_INSTALL_SUCCESS":"Gefeliciteerd!#@Ventoy is met succes op het apparaat geïnstalleerd.", + "STR_INSTALL_FAILED":"Er is een fout opgetreden tijdens de installatie. U kunt het apparaat opnieuw aansluiten en het nogmaals proberen. Controleer log.txt voor details.", + "STR_UPDATE_SUCCESS":"Gefeliciteerd!#@Ventoy is succesvol bijgewerkt op het apparaat.", + "STR_UPDATE_FAILED":"Er is een fout opgetreden tijdens de update. U kunt het apparaat opnieuw aansluiten en het nogmaals proberen. Controleer log.txt voor details.", + "STR_WAIT_PROCESS":"Ventoy is nog bezig, even geduld...", + "STR_MENU_OPTION":"Opties", + "STR_MENU_SECURE_BOOT":"Secure Boot", + "STR_MENU_PART_CFG":"Partitieconfiguratie", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Annuleren", + "STR_PRESERVE_SPACE":"Ruimte aan het einde van de schijf reserveren", + "STR_SPACE_VAL_INVALID":"Ongeldige waarde voor gereserveerde ruimte", + "STR_MENU_CLEAR":"Ventoy verwijderen", + "STR_CLEAR_SUCCESS":"Ventoy is succesvol verwijderd van het apparaat.", + "STR_CLEAR_FAILED":"Er is een fout opgetreden bij het verwijderen van Ventoy. U kunt het apparaat opnieuw aansluiten en het nogmaals proberen. Controleer log.txt voor details.", + "STR_MENU_PART_STYLE":"Partitietabel", + "STR_DISK_2TB_MBR_ERROR":"Selecteer GPT als partitietabel voor schijven groter dan 2TB", + "STR_SHOW_ALL_DEV":"Toon alle apparaten", + "STR_PART_ALIGN_4KB":"Lijn partities uit met 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communicatie fout:", + "STR_WEB_REMOTE_ABNORMAL":"Communicatiefout: abnormaal op afstand", + "STR_WEB_REQUEST_TIMEOUT":"Communicatiefout: time-out van verzoek", + "STR_WEB_SERVICE_UNAVAILABLE":"Communicatiefout: service niet beschikbaar", + "STR_WEB_TOKEN_MISMATCH":"Daemon-status bijgewerkt, probeer het later opnieuw.", + "STR_WEB_SERVICE_BUSY":"Service is bezet, probeer het later opnieuw.", + "STRXXX":"" + }, + { + "name":"Romanian (Română)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"DorinMol", + + "STR_ERROR":"Eroare", + "STR_WARNING":"Avertisment", + "STR_INFO":"Informare", + "STR_INCORRECT_DIR":"Vă rugăm executați în directorul corect!", + "STR_INCORRECT_TREE_DIR":"Nu rulați aici, vă rugăm să descărcați pachetul de instalare și executati acolo.", + "STR_DEVICE":"Dispozitiv", + "STR_LOCAL_VER":"Ventoy În Pachet", + "STR_DISK_VER":"Ventoy În Dispozitiv", + "STR_STATUS":"Stare - PREGĂTIT", + "STR_INSTALL":"Instalare", + "STR_UPDATE":"Actualizare", + "STR_UPDATE_TIP":"Operația de actualizare este sigură, fișierele ISO nu vor fi alterate / modificate.#@Continuați?", + "STR_INSTALL_TIP":"Unitatea disc va fi formatată și toate datele vor fi pierdute.#@Continuați?", + "STR_INSTALL_TIP2":"Unitatea disc va fi formatată și toate datele vor fi pierdute.#@Continuați? (Verificare Dublă)", + "STR_INSTALL_SUCCESS":"Felicitări!#@Ventoy a fost instalat cu succes pe dispozitiv.", + "STR_INSTALL_FAILED":"A apărut o eroare în timpul instalării. Reintroduceți dispozitivul USB și încercați din nou. Verificați log.txt pentru detalii.", + "STR_UPDATE_SUCCESS":"Felicitări!#@Ventoy a fost actualizat cu succes pe dispozitiv.", + "STR_UPDATE_FAILED":"A apărut o eroare în timpul actualizării. Reintroduceți dispozitivul USB și încercați din nou. Verificați log.txt pentru detalii.", + "STR_WAIT_PROCESS":"Rulează un fir de execuție, vă rugăm așteptați...", + "STR_MENU_OPTION":"Opțiune", + "STR_MENU_SECURE_BOOT":"Încărcare Sigură", + "STR_MENU_PART_CFG":"Configurare partiție", + "STR_BTN_OK":"O.K", + "STR_BTN_CANCEL":"Anulare", + "STR_PRESERVE_SPACE":"Păstrați puțin spațiu în partea de jos a discului", + "STR_SPACE_VAL_INVALID":"Valoare nevalidă pentru spațiul rezervat", + "STR_MENU_CLEAR":"Clear Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy has been successfully removed from the device.", + "STR_CLEAR_FAILED":"An error occurred when clear Ventoy from disk. You can replug the USB and try again. Check log.txt for detail.", + "STR_MENU_PART_STYLE":"Partition Style", + "STR_DISK_2TB_MBR_ERROR":"Please select GPT for disk over 2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Japanese (日本語)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"taichi eto,Bentnand", + + "STR_ERROR":"エラー", + "STR_WARNING":"警告", + "STR_INFO":"情報", + "STR_INCORRECT_DIR":"正しいディレクトリーで実行してください", + "STR_INCORRECT_TREE_DIR":"ここで実行するには、パッケージをインストールしてください。.", + "STR_DEVICE":"デバイス", + "STR_LOCAL_VER":"Ventoy In Package", + "STR_DISK_VER":"Ventoy In Device", + "STR_STATUS":"Status - 準備完了", + "STR_INSTALL":"インストール", + "STR_UPDATE":"更新", + "STR_UPDATE_TIP":"アップグレード可能です、ISOファイルは改変されません。.#@続行?", + "STR_INSTALL_TIP":"選択されたディスクは完全に初期化され、保存されたデータは二度と復元できません。#@続行?", + "STR_INSTALL_TIP2":"選択されたディスクは完全に初期化され、保存されたデータは二度と復元できません。#@続行? (再確認)", + "STR_INSTALL_SUCCESS":"Congratulations!#@Ventoy は正常にインストールされました", + "STR_INSTALL_FAILED":"インストール中にエラーが発生しました。デバイスを再接続してもう一度やり直してください。 詳細ログ log.txt ", + "STR_UPDATE_SUCCESS":"Congratulations!#@Ventoy は正常にアップデートされました.", + "STR_UPDATE_FAILED":"更新中にエラーが発生しました。デバイスを再接続してもう一度やり直してください。. 詳細ログ log.txt", + "STR_WAIT_PROCESS":"処理中...", + "STR_MENU_OPTION":"設定", + "STR_MENU_SECURE_BOOT":"Secure Boot", + "STR_MENU_PART_CFG":"パーティション構成", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"キャンセル", + "STR_PRESERVE_SPACE":"ディスクの下部にある程度のスペースを確保する", + "STR_SPACE_VAL_INVALID":"予約スペースの無効な値", + "STR_MENU_CLEAR":"Ventoyを削除", + "STR_CLEAR_SUCCESS":"Ventoyがディスクから削除されました。", + "STR_CLEAR_FAILED":"Ventoyをディスクから削除できませんでした。USBを再挿入してみてください。 詳細ログ log.txt", + "STR_MENU_PART_STYLE":"パーティションスタイル", + "STR_DISK_2TB_MBR_ERROR":"2TB以上のディスクにはGPTを使用してください。", + "STR_SHOW_ALL_DEV":"すべてのデバイスを表示", + "STR_PART_ALIGN_4KB":"パーティションを4KBに揃える", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"デーモンのステータスが更新されました。しばらくしてから再試行してください。", + "STR_WEB_SERVICE_BUSY":"サービスがビジーです。後で再試行してください。", + "STRXXX":"" + }, + { + "name":"Italian (Italiano)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"AverageUser2", + + "STR_ERROR":"Errore", + "STR_WARNING":"Attenzione", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Si prega di eseguire nella cartella corretta", + "STR_INCORRECT_TREE_DIR":"Non eseguire qui, scarica il pacchetto di installazione ed avvialo", + "STR_DEVICE":"Dispositivo", + "STR_LOCAL_VER":"Versione Ventoy locale", + "STR_DISK_VER":"Ventoy nel dispositivo", + "STR_STATUS":"Stato - PRONTO", + "STR_INSTALL":"Installa", + "STR_UPDATE":"Aggiorna", + "STR_UPDATE_TIP":"L'aggiornamento è sicuro, i file presenti nel dispositivo rimarranno invariati.#@Continue?", + "STR_INSTALL_TIP":"Il disco verrà formattato e tutti i dati saranno persi.#@Continue?", + "STR_INSTALL_TIP2":"Il disco verrà formattato e tutti i dati saranno persi.#@Continue?' (Seconda Verifica)", + "STR_INSTALL_SUCCESS":"Congratulazioni!#@Ventoy è stato installato con successo nel dispositivo", + "STR_INSTALL_FAILED":"Si è verificato un errore durante l'installazione. Reinserisci il dispostivo e riprova. Controlla il file log.txt per i dettagli.", + "STR_UPDATE_SUCCESS":"Congratulazioni!#@Ventoy è stato aggiornato con successo nel dispositivo", + "STR_UPDATE_FAILED":"Si è verificato un errore durante l'aggiornamento. Reinserisci il dispostivo e riprova. Controlla il file log.txt per i dettagli.", + "STR_WAIT_PROCESS":"Un processo è in esecuzione, attendere prego...", + "STR_MENU_OPTION":"Opzioni", + "STR_MENU_SECURE_BOOT":"Avvio protetto (secure boot)", + "STR_MENU_PART_CFG":"Configurazione della partizione", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Annulla", + "STR_PRESERVE_SPACE":"Conserva spazio nella parte finale del disco", + "STR_SPACE_VAL_INVALID":"Quantità di spazio da riservare non valida", + "STR_MENU_CLEAR":"Rimuovi Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy è stato rimosso con successo dal dispositivo.", + "STR_CLEAR_FAILED":"Si è verificato un errore durante la rimozione di Ventoy dal dispositivo. Reinserisci il dispositivo e riprova. Controlla il file log.txt per maggiori dettagli", + "STR_MENU_PART_STYLE":"Stile Tabella delle partizioni", + "STR_DISK_2TB_MBR_ERROR":"Seleziona GPT per dischi con dimensioni maggiori di 2TB", + "STR_SHOW_ALL_DEV":"Mostra tutti i dispositivi", + "STR_PART_ALIGN_4KB":"Allinea le partizioni con 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Croatian (Hrvatski)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Valnjes", + + "STR_ERROR":"Pogreška", + "STR_WARNING":"Upozorenje", + "STR_INFO":"Informacija", + "STR_INCORRECT_DIR":"Molim vas pokrenite unutar pravog direktorija!", + "STR_INCORRECT_TREE_DIR":"Ne pokrećite me ovdje, molim vas downloadajte poslijednju verziju, te ponovno pokrenite!", + "STR_DEVICE":"Uređaj", + "STR_LOCAL_VER":"Ventoy (lokalni)", + "STR_DISK_VER":"Ventoy (na uređaju)", + "STR_STATUS":"Status - SPREMAN", + "STR_INSTALL":"Instaliraj", + "STR_UPDATE":"Ažuriraj", + "STR_UPDATE_TIP":"Nadogradnja je sigurna, ISO datoteke neće biti promjenjene.#@Nastaviti?", + "STR_INSTALL_TIP":"USB disk će biti formatiran i svi podatci će biti izgubljeni!#@Nastaviti?", + "STR_INSTALL_TIP2":"USB disk će biti formatiran i svi podatci će biti izgubljeni!#@Nastaviti? (Dodatna provjera)", + "STR_INSTALL_SUCCESS":"Čestitam!#@Ventoy je uspješno instaliran na vaš uređaj.", + "STR_INSTALL_FAILED":"Dogodila se pogreška tokom instalacije. Pokušajte ponovno spojiti USB i pokušati ponovno. Provjerite log.txt za više detalja o nastaloj pogrešci.", + "STR_UPDATE_SUCCESS":"Čestitam!#@Ventoy je uspješno ažuriran na vašem uređaju.", + "STR_UPDATE_FAILED":"Dogodila se pogreška tokom ažuriranja. Pokušajte ponovno spojiti USB i pokušati ponovno. Provjerite log.txt za više detalja o nastaloj pogrešci.", + "STR_WAIT_PROCESS":"Instanca je vec pokrenuta, molimo vas pričekajte...", + "STR_MENU_OPTION":"Opcije", + "STR_MENU_SECURE_BOOT":"Secure Boot", + "STR_MENU_PART_CFG":"Konfiguracija particije", + "STR_BTN_OK":"u redu", + "STR_BTN_CANCEL":"Otkazati", + "STR_PRESERVE_SPACE":"Sačuvajte malo prostora na dnu diska", + "STR_SPACE_VAL_INVALID":"Nevažeća vrijednost rezerviranog prostora", + "STR_MENU_CLEAR":"Obriši Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy je uspesno obrisan sa uređaja", + "STR_CLEAR_FAILED":"Dogodila se greška tokom brisanja Ventoy sa diska. Možeš ponovo ubacit USB i pokušati opet. Pogledaj log.txt za još detalja.", + "STR_MENU_PART_STYLE":"Stil particija", + "STR_DISK_2TB_MBR_ERROR":"Molim te, koristi GPT za diskove preko 2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Hungarian (Magyar)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Bitfarago", + + "STR_ERROR":"Hiba", + "STR_WARNING":"Figyelem", + "STR_INFO":"Infó", + "STR_INCORRECT_DIR":"Kérjük, futtasd a megfelelő könyvtárból!", + "STR_INCORRECT_TREE_DIR":"Ne futtasd innen. Töltsd le a kiadott telepítőcsomagot és onnan futtasd.", + "STR_DEVICE":"Eszköz", + "STR_LOCAL_VER":"Ventoy a csomagban", + "STR_DISK_VER":"Ventoy az eszközön", + "STR_STATUS":"Állapot - KÉSZ", + "STR_INSTALL":"Telepítés", + "STR_UPDATE":"Frissítés", + "STR_UPDATE_TIP":"A frissítés biztonságos, az ISO fájlok nem változnak.#@Folytatod?", + "STR_INSTALL_TIP":"A meghajtó formázva lesz és minden rajta lévő adat elveszik.#@Folytatod?", + "STR_INSTALL_TIP2":"A meghajtó formázva lesz és minden rajta lévő adat elveszik.#@Folytatod? (Második jóváhagyás)", + "STR_INSTALL_SUCCESS":"Gratulálunk!#@A Ventoy sikeresen telepítve lett az eszközön.", + "STR_INSTALL_FAILED":"Hiba történt a telepítés során. Csatlakoztasd újra az USB eszközt, és próbáld újra.#@A részleteket lásd a log.txt fájlban.", + "STR_UPDATE_SUCCESS":"Gratulálunk!#@A Ventoy sikeresen frissítve lett az eszközön.", + "STR_UPDATE_FAILED":"Hiba történt a frissítés során. Csatlakoztasd újra az USB eszközt, és próbáld újra.#@A részleteket lásd a log.txt fájlban.", + "STR_WAIT_PROCESS":"A feldolgozás még folyamatban van, kérlek várj...", + "STR_MENU_OPTION":"Opció", + "STR_MENU_SECURE_BOOT":"Biztonsági mód (Secure Boot)", + "STR_MENU_PART_CFG":"Partíciókonfiguráció", + "STR_BTN_OK":"rendben", + "STR_BTN_CANCEL":"Megszünteti", + "STR_PRESERVE_SPACE":"Tartson szabad helyet a lemez alján", + "STR_SPACE_VAL_INVALID":"A fenntartott hely érvénytelen értéke", + "STR_MENU_CLEAR":"Clear Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy has been successfully removed from the device.", + "STR_CLEAR_FAILED":"An error occurred when clear Ventoy from disk. You can replug the USB and try again. Check log.txt for detail.", + "STR_MENU_PART_STYLE":"Partition Style", + "STR_DISK_2TB_MBR_ERROR":"Please select GPT for disk over 2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Chinese Traditional (繁體中文)", + "FontFamily":"新細明體", + "FontSize":14, + "Author":"penut85420", + + "STR_ERROR":"錯誤", + "STR_WARNING":"警告", + "STR_INFO":"提醒", + "STR_INCORRECT_DIR":"請在正確的資料夾下開啟!", + "STR_INCORRECT_TREE_DIR":"請下載並使用發行版本的安裝包", + "STR_DEVICE":"裝置", + "STR_LOCAL_VER":"當前 Ventoy 版本", + "STR_DISK_VER":"裝置內部的 Ventoy 版本", + "STR_STATUS":"狀態 - 準備就緒", + "STR_INSTALL":"安裝", + "STR_UPDATE":"升級", + "STR_UPDATE_TIP":"升級操作是安全的,磁碟內的 ISO 文件不會被清除#@是否繼續?", + "STR_INSTALL_TIP":"磁碟將會被格式化,所有內容將會被清除!#@是否繼續?", + "STR_INSTALL_TIP2":"磁碟將會被格式化,所有內容將會被清除!#@再次確認是否繼續?", + "STR_INSTALL_SUCCESS":"恭喜,Ventoy 已經成功安裝到此裝置中!", + "STR_INSTALL_FAILED":"安裝 Ventoy 的過程中發生錯誤,請重新插入磁碟並重試一次,詳細訊息請調閱 log.txt 文件。", + "STR_UPDATE_SUCCESS":"恭喜,新版本的 Ventoy 已經成功更新到此裝置中!", + "STR_UPDATE_FAILED":"更新 Ventoy 的過程中發生錯誤,請重新插入磁碟並重試一次,詳細訊息請調閱 log.txt 文件。", + "STR_WAIT_PROCESS":"目前有執行緒正在運作中,請稍候", + "STR_MENU_OPTION":"選項", + "STR_MENU_SECURE_BOOT":"支援 Secure Boot", + "STR_MENU_PART_CFG":"分區配置", + "STR_BTN_OK":"確定", + "STR_BTN_CANCEL":"取消", + "STR_PRESERVE_SPACE":"在磁盤最後保留一部分空間", + "STR_SPACE_VAL_INVALID":"保留空間的大小不合法", + "STR_MENU_CLEAR":"清除Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy已成功從設備中清除", + "STR_CLEAR_FAILED":"清除 Ventoy 的過程中發生錯誤,請重新插入磁碟並重試一次,詳細訊息請調閱 log.txt 文件。", + "STR_MENU_PART_STYLE":"分區格式", + "STR_DISK_2TB_MBR_ERROR":"對於超過2TB的磁片請選擇GPT分區格式", + "STR_SHOW_ALL_DEV":"顯示所有設備", + "STR_PART_ALIGN_4KB":"分區按照4KB對齊", + "STR_WEB_COMMUNICATION_ERR":"通信錯誤:", + "STR_WEB_REMOTE_ABNORMAL":"通信錯誤: 服務端異常", + "STR_WEB_REQUEST_TIMEOUT":"通信錯誤: 請求超時", + "STR_WEB_SERVICE_UNAVAILABLE":"通信錯誤: 服務不可用", + "STR_WEB_TOKEN_MISMATCH":"服務狀態已更新", + "STR_WEB_SERVICE_BUSY":"服務正忙,請稍後重試", + "STRXXX":"" + }, + { + "name":"Serbian Latin (Srpski)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Bojan Maksimović, panickingkernel, Zoran Jankov", + + "STR_ERROR":"Greška", + "STR_WARNING":"Upozorenje", + "STR_INFO":"Informacija", + "STR_INCORRECT_DIR":"Molim Vas, pokrenite ме u pravom direktorijumu!", + "STR_INCORRECT_TREE_DIR":"Nemojте me pokretati ovde, molim Vas preuzmite objavljeni instalacioni paket i pokrenite me tamo.", + "STR_DEVICE":"Uređaj", + "STR_LOCAL_VER":"Ventoy u paketu", + "STR_DISK_VER":"Ventoy u uređaju", + "STR_STATUS":"Status - SPREMAN", + "STR_INSTALL":"Instaliraj", + "STR_UPDATE":"Nadogradi", + "STR_UPDATE_TIP":"Nadogradnja je sigurna, ISO fajlovi neće biti promenjeni.#@Nastavi?", + "STR_INSTALL_TIP":"Disk će biti formatiran i svi podaci će biti izgubljeni.#@Nastavi?", + "STR_INSTALL_TIP2":"Disk će biti formatiran i svi podaci će biti izgubljeni.#@Nastavi? (Dvostruka provera)", + "STR_INSTALL_SUCCESS":"Čestitam!#@Ventoy je uspešno instaliran na ovom uređaju.", + "STR_INSTALL_FAILED":"Dogodila se greška tokom instalacije. Možete izvaditi i ponovo ubaciti USB, pa pokušati opet. Pogledajte log.txt za još detalja.", + "STR_UPDATE_SUCCESS":"Čestitam!#@Ventoy je uspešno nadograđen na ovom uređaju.", + "STR_UPDATE_FAILED":"Dogodila se greška prilikom nadogradnje. Možeте izvaditi i ponovo ubaciti USB, pa pokušati opet. Pogledajte log.txt za još detalja.", + "STR_WAIT_PROCESS":"Odvija se operacija, molim Vas sačekajte...", + "STR_MENU_OPTION":"Opcije", + "STR_MENU_SECURE_BOOT":"Sigurno uključivanje je podržano", + "STR_MENU_PART_CFG":"Konfiguracija particija", + "STR_BTN_OK":"U redu", + "STR_BTN_CANCEL":"Otkaži", + "STR_PRESERVE_SPACE":"Sačuvaj nešto prostora na kraju diska", + "STR_SPACE_VAL_INVALID":"Neispravna vrednost za prezervirani prostor", + "STR_MENU_CLEAR":"Obriši Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy je uspešno obrisan sa uređaja", + "STR_CLEAR_FAILED":"Dogodila se greška tokom brisanja Ventoy sa diska. Možete ponovo ubaciti USB i pokušati opet. Pogledajte log.txt za još detalja.", + "STR_MENU_PART_STYLE":"Stil particija", + "STR_DISK_2TB_MBR_ERROR":"Molim Vas, izaberite GPT za diskove preko 2TB", + "STR_SHOW_ALL_DEV":"Pokaži sve uređaje", + "STR_PART_ALIGN_4KB":"Poravnajte sve particije sa 4KB", + "STR_WEB_COMMUNICATION_ERR":"Greška u komunikaciji:", + "STR_WEB_REMOTE_ABNORMAL":"Greška u komunikaciji: nenormalno daljinsko upravljanje", + "STR_WEB_REQUEST_TIMEOUT":"Greška u komunikaciji: Zahtev je istekao", + "STR_WEB_SERVICE_UNAVAILABLE":"Greška u komunikaciji: Servis nije dostupan", + "STR_WEB_TOKEN_MISMATCH":"Status demona ažuriran, pokušajte ponovo kasnije.", + "STR_WEB_SERVICE_BUSY":"Servis je zauzet, pokušajte ponovo kasnije.", + "STRXXX":"" + }, + { + "name":"Serbian Cyrillic (Српски)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Bojan Maksimović, panickingkernel, Zoran Jankov", + + "STR_ERROR":"Грешка", + "STR_WARNING":"Упозорење", + "STR_INFO":"Информација", + "STR_INCORRECT_DIR":"Молим Вас, покрените ме у правом директоријуму!", + "STR_INCORRECT_TREE_DIR":"Немојте ме покретати овде, молим Вас преузмите објављени инсталациони пакет и покрените ме тамо.", + "STR_DEVICE":"Уређај", + "STR_LOCAL_VER":"Ventoy у пакету", + "STR_DISK_VER":"Ventoy у уређају", + "STR_STATUS":"Статус - СПРЕМАН", + "STR_INSTALL":"Инсталирај", + "STR_UPDATE":"Надогради", + "STR_UPDATE_TIP":"Надоградња је сигурна, ISO фајлови неће бити промењени.#@Настави?", + "STR_INSTALL_TIP":"Диск ће бити форматиран и сви подаци ће бити изгубљени.#@Настави?", + "STR_INSTALL_TIP2":"Диск ће бити форматиран и сви подаци ће бити изгубљени.#@Настави? (Двострука провера)", + "STR_INSTALL_SUCCESS":"Честитам!#@Ventoy је успешно инсталиран на овом уређају.", + "STR_INSTALL_FAILED":"Догодила се грешка током инсталације. Можете извадити и поново убацити USB, па покушати опет. Погледајте log.txt за још детаља.", + "STR_UPDATE_SUCCESS":"Честитам!#@Ventoy је био успешно надограђен на овом уређају.", + "STR_UPDATE_FAILED":"Догодила се грешка током надоградње. Можете извадити и поново убацити USB, па покушати опет. Погледајте log.txt за још детаља.", + "STR_WAIT_PROCESS":"Одвија се операција, молим Вас сачекајте...", + "STR_MENU_OPTION":"Опције", + "STR_MENU_SECURE_BOOT":"Сигурно уклјучиванје је подржано", + "STR_MENU_PART_CFG":"Конфигурација партиција", + "STR_BTN_OK":"У реду", + "STR_BTN_CANCEL":"Откажи", + "STR_PRESERVE_SPACE":"Саћувај нешто простора на крају диска", + "STR_SPACE_VAL_INVALID":"Неисправна вредност за презервирани простор", + "STR_MENU_CLEAR":"Обриши Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy је успешно обрисан са уређаја", + "STR_CLEAR_FAILED":"Можете поново убацити USB и покушати опет. Погледајте log.txt за још детаља.", + "STR_MENU_PART_STYLE":"Стил партиција", + "STR_DISK_2TB_MBR_ERROR":"Молим Вас, изаберите GPT за дискове преко 2ТB", + "STR_SHOW_ALL_DEV":"Прикажи све уређаје", + "STR_PART_ALIGN_4KB":"Поравнајте партиције са 4KB", + "STR_WEB_COMMUNICATION_ERR":"Комуникациона грешка:", + "STR_WEB_REMOTE_ABNORMAL":"Комуникациона грешка: ненормално даљинско управљање", + "STR_WEB_REQUEST_TIMEOUT":"Комуникациона грешка: Захтев је истекао", + "STR_WEB_SERVICE_UNAVAILABLE":"Комуникациона грешка: Сервис је недоступан", + "STR_WEB_TOKEN_MISMATCH":"Статус демона ажуриран, покушајте поново касније.", + "STR_WEB_SERVICE_BUSY":"Севис је заузет, покушајте поново касније.", + "STRXXX":"" + }, + { + "name":"Thai (ไทย)", + "FontFamily":"Courier New", + "FontSize":17, + "Author":"longpanda", + + "STR_ERROR":"ผิดพลาด", + "STR_WARNING":"คำเตือน", + "STR_INFO":"ข้อมูล", + "STR_INCORRECT_DIR":"กรุณาเรียกใช้ในอุปกรณ์ที่ถูกต้อง!", + "STR_INCORRECT_TREE_DIR":"อย่าติดตั้งในนี้, โปรดดาวน์โหลดและใช้แพ็คเกจการติดตั้งของรุ่นที่วางจำหน่าย", + "STR_DEVICE":"อุปกรณ์", + "STR_LOCAL_VER":"เวอร์ชั่น Ventoy ", + "STR_DISK_VER":"ลง Ventoy ในอุปกรณ์", + "STR_STATUS":"สถานะ - พร้อม", + "STR_INSTALL":"ติดตั้ง", + "STR_UPDATE":"ปรับปรุง", + "STR_UPDATE_TIP":"การดำเนินการอัปเกรดปลอดภัย, ไฟล์ ISO จะไม่มีการเปลี่ยนแปลง.#@ต่อเนื่อง?", + "STR_INSTALL_TIP":"ดิสก์จะถูกฟอร์แมตและข้อมูลทั้งหมดจะสูญหาย.#@ต่อเนื่อง?", + "STR_INSTALL_TIP2":"ดิสก์จะถูกฟอร์แมตและข้อมูลทั้งหมดจะสูญหาย#@ต่อเนื่อง? (ตรวจสอบอีกครั้ง)", + "STR_INSTALL_SUCCESS":"ขอแสดงความยินดี!#@Ventoy ติดตั้งไปยังอุปกรณ์สำเร็จแล้ว", + "STR_INSTALL_FAILED":"เกิดข้อผิดพลาดระหว่างการติดตั้ง. คุณสามารถถอด USB และลองอีกครั้ง. ตรวจสอบรายละเอียดได้ที่ log.txt.", + "STR_UPDATE_SUCCESS":"ขอแสดงความยินดี!#@Ventoy อัปเดตไปยังอุปกรณ์สำเร็จแล้ว.", + "STR_UPDATE_FAILED":"เกิดข้อผิดพลาดระหว่างการอัพเดต. คุณสามารถถอด USB และลองอีกครั้ง. ตรวจสอบรายละเอียดได้ที่ log.txt.", + "STR_WAIT_PROCESS":"CPU กำลังทำงานอยู่,ได้โปรดรอ...", + "STR_MENU_OPTION":"ตัวเลือก", + "STR_MENU_SECURE_BOOT":"สนับสนุนการบูตที่ปลอดภัย", + "STR_MENU_PART_CFG":"กำหนดค่าพาร์ติชัน", + "STR_BTN_OK":"ตกลง", + "STR_BTN_CANCEL":"ยกเลิก", + "STR_PRESERVE_SPACE":"เก็บพื้นที่บางส่วนของอุปกรณ์ไว้", + "STR_SPACE_VAL_INVALID":"มีปัญหาสำหรับพื้นที่ที่สงวนไว้(กรุณา ฟอร์แมต และติดตั้งใหม่)", + "STR_MENU_CLEAR":"Clear Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy has been successfully removed from the device.", + "STR_CLEAR_FAILED":"An error occurred when clear Ventoy from disk. You can replug the USB and try again. Check log.txt for detail.", + "STR_MENU_PART_STYLE":"Partition Style", + "STR_DISK_2TB_MBR_ERROR":"Please select GPT for disk over 2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Norwegian (Norsk)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Stein-Ove Bøthun", + + "STR_ERROR":"Feil", + "STR_WARNING":"Advarsel", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Vennligst kjør fra den riktige mappen!", + "STR_INCORRECT_TREE_DIR":"Ikke kjør meg her, vennligst last ned den utgitte installasjonspakken og kjør den der.", + "STR_DEVICE":"Enhet", + "STR_LOCAL_VER":"Ventoy i Pakken", + "STR_DISK_VER":"Ventoy på Enheten", + "STR_STATUS":"Status - KLAR", + "STR_INSTALL":"Installer", + "STR_UPDATE":"Oppdater", + "STR_UPDATE_TIP":"Oppgraderingen er trygg, ISO-filene vil være uendret.#@Fortsette?", + "STR_INSTALL_TIP":"Disken blir formatert og alle dataene vil gå tapt.#@Fortsette?", + "STR_INSTALL_TIP2":"Disken blir formatert og alle dataene vil gå tapt.#@Fortsette? (Dobbelsjekk)", + "STR_INSTALL_SUCCESS":"Gratulerer!#@Ventoy er blitt installert på enheten.", + "STR_INSTALL_FAILED":"Det oppstod en feil under installasjonen. Du kan koble til USB-en på nytt og prøve igjen. Check log.txt for flere detaljer.", + "STR_UPDATE_SUCCESS":"Gratulerer!#@Ventoy har blitt oppdatert på enheten.", + "STR_UPDATE_FAILED":"Det oppstod en feil under oppdateringen. Du kan koble til USB-en på nytt og prøve igjen. Se log.txt for flere detaljer.", + "STR_WAIT_PROCESS":"En prosess kjører, vennligst vent...", + "STR_MENU_OPTION":"Alternativ", + "STR_MENU_SECURE_BOOT":"Støtte for sikker oppstart", + "STR_MENU_PART_CFG":"Partisjonskonfigirasjon", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Avbryt", + "STR_PRESERVE_SPACE":"Bevar litt plass på bunnen av disken", + "STR_SPACE_VAL_INVALID":"Ugyldig verdi for reservert plass", + "STR_MENU_CLEAR":"Fjern Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy er blitt fjernet fra enheten.", + "STR_CLEAR_FAILED":"Det oppstod en feil da Ventoy bler fjernet fra disken. Du kan koble til USB-en på nytt og prøve igjen. Sjekk log.txt for flere detaljer.", + "STR_MENU_PART_STYLE":"Partisjonsstil", + "STR_DISK_2TB_MBR_ERROR":"Velg GPT for disker over 2 TB", + "STR_SHOW_ALL_DEV":"Vis alle enheter", + "STR_PART_ALIGN_4KB":"Juster partisjoner med 4KB", + "STR_WEB_COMMUNICATION_ERR":"Kommunikasjonsfeil:", + "STR_WEB_REMOTE_ABNORMAL":"Kommunokasjonsfeil: unormalt fjerntliggende", + "STR_WEB_REQUEST_TIMEOUT":"Kommunikasjonsfeil: Forespørselen ble tidsavbrutt", + "STR_WEB_SERVICE_UNAVAILABLE":"Kommunikasjonsfeil: Tjeneste utilgjengelig", + "STR_WEB_TOKEN_MISMATCH":"Daemon status er oppdatert, vennligst prøv igjen senere.", + "STR_WEB_SERVICE_BUSY":"Tjenesten er opptatt, vennligst prøv igjen senere.", + "STRXXX":"" + }, + { + "name":"Vietnamese (Tiếng Việt)", + "FontFamily":"Tahoma", + "FontSize":14, + "Author":"Nguyen Quoc Hoang - cuumay.com", + + "STR_ERROR":"Lỗi", + "STR_WARNING":"Cảnh báo", + "STR_INFO":"Thông tin", + "STR_INCORRECT_DIR":"Vui lòng chạy Ventoy2Disk tại đúng thư mục của nó !", + "STR_INCORRECT_TREE_DIR":"Không được chạy Ventoy2Disk ở đây, vui lòng tải gói cài đặt đã phát hành và chạy ở đó.", + "STR_DEVICE":"Thiết bị", + "STR_LOCAL_VER":"Phiên bản Ventoy", + "STR_DISK_VER":"Phiên bản Ventoy ở thiết bị", + "STR_STATUS":"Trạng thái - SẴN SÀNG", + "STR_INSTALL":"Cài đặt", + "STR_UPDATE":"Cập nhật", + "STR_UPDATE_TIP":"Việc cập nhật Ventoy là an toàn, các tập tin ISO của bạn sẽ được giữ nguyên trạng.#@ Bạn thật sự muốn tiếp tục ?.", + "STR_INSTALL_TIP":"Thiết bị sẽ được định dạng và do đó TẤT CẢ DỮ LIỆU trên thiết bị sẽ bị mất.#@ Bạn thật sự muốn tiếp tục ?.", + "STR_INSTALL_TIP2":"Thiết bị sẽ được định dạng và do đó TẤT CẢ DỮ LIỆU trên thiết bị sẽ bị mất.#@ Bạn thật sự muốn tiếp tục ?. (Xác nhận lần hai)", + "STR_INSTALL_SUCCESS":"Chúc mừng bạn !.#@ Thiết bị đã được cài Ventoy thành công.", + "STR_INSTALL_FAILED":"Đã xảy ra lỗi trong quá trình cài đặt Ventoy. Bạn có thể rút thiết bị ra và thử lại. Xem chi tiết ở tệp log.txt.", + "STR_UPDATE_SUCCESS":"Chúc mừng bạn !.#@ Thiết bị đã được cập nhật Ventoy thành công.", + "STR_UPDATE_FAILED":"Đã xảy ra lỗi trong quá trình cập nhật Ventoy. Bạn có thể rút thiết bị ra và thử lại. Xem chi tiết ở tệp log.txt.", + "STR_WAIT_PROCESS":"Một luồng xử lý đang chạy, vui lòng chờ...", + "STR_MENU_OPTION":"Tùy chọn", + "STR_MENU_SECURE_BOOT":"Bật hỗ trợ Secure Boot", + "STR_MENU_PART_CFG":"Cấu hình phân vùng", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Hủy", + "STR_PRESERVE_SPACE":"Giữ lại phần dung lượng ở cuối thiết bị", + "STR_SPACE_VAL_INVALID":"Giá trị dung lượng giữ lại không hợp lệ.", + "STR_MENU_CLEAR":"Gỡ bỏ Ventoy", + "STR_CLEAR_SUCCESS":"Chúc mừng bạn !.#@ Thiết bị đã được gỡ bỏ Ventoy thành công.", + "STR_CLEAR_FAILED":"Đã xảy ra lỗi trong quá trình gỡ bỏ Ventoy. Bạn có thể rút thiết bị ra và thử lại. Xem chi tiết ở tệp log.txt.", + "STR_MENU_PART_STYLE":"Kiểu phân vùng", + "STR_DISK_2TB_MBR_ERROR":"Thiết bị có dung lượng lớn hơn 2TB. Vui lòng chọn Kiểu phân vùng là GPT.", + "STR_SHOW_ALL_DEV":"Hiện tất cả Thiết bị", + "STR_PART_ALIGN_4KB":"Căn chỉnh phân vùng với 4KB", + "STR_WEB_COMMUNICATION_ERR":"Lỗi giao tiếp:", + "STR_WEB_REMOTE_ABNORMAL":"Lỗi giao tiếp: Remote bất thường", + "STR_WEB_REQUEST_TIMEOUT":"Lỗi giao tiếp: Yêu cầu đã hết thời gian chờ", + "STR_WEB_SERVICE_UNAVAILABLE":"Lỗi giao tiếp: Dịch vụ không sẵn có", + "STR_WEB_TOKEN_MISMATCH":"Đã cập nhật trạng thái Daemon, vui lòng thử lại sau.", + "STR_WEB_SERVICE_BUSY":"Dịch vụ bận, vui lòng thử lại sau.", + "STRXXX":"" + }, + { + "name":"Lithuanian (Lietuvių)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"r0manas", + + "STR_ERROR ":"Klaida", + "STR_WARNING ":"Įspėjimas", + "STR_INFO ":"Informacija", + "STR_INCORRECT_DIR ":"Prašome paleisti teisingame kataloge!", + "STR_INCORRECT_TREE_DIR ":"Nepaleiskite manęs čia, atsisiųskite išleistą diegimo paketą ir paleiskite ten.", + "STR_DEVICE ":"Įrenginys", + "STR_LOCAL_VER ":"„Ventoy“ versija pakuotėje", + "STR_DISK_VER ":"„Ventoy“ versija įrenginyje", + "STR_STATUS ":"Būsena - PASIRENGĘS", + "STR_INSTALL ":"Įdiegti", + "STR_UPDATE ":"Atnaujinti", + "STR_UPDATE_TIP ":"Atnaujinti - saugu, ISO failai liks nepažeisti.#@Tęsti?", + "STR_INSTALL_TIP ":"Įrenginys bus suformatuotas ir visi duomenys bus prarasti.#@Tęsti?", + "STR_INSTALL_TIP2 ":"Įrenginys bus suformatuotas ir visi duomenys bus prarasti.#@Tęsti? (TIKRAI TĘSTI?)", + "STR_INSTALL_SUCCESS ":"Sveikinu!#@Ventoy sėkmingai įdiegtas įrenginyje.", + "STR_INSTALL_FAILED ":"Diegimo metu įvyko klaida. Galite iš naujo prijungti USB ir bandyti dar kartą. Patikrinkite log.txt, jei norite sužinoti daugiau.", + "STR_UPDATE_SUCCESS ":"Sveikinu!#@Ventoy sėkmingai atnaujintas įrenginyje.", + "STR_UPDATE_FAILED ":"Atnaujinant įvyko klaida. Galite iš naujo prijungti USB ir bandyti dar kartą. Patikrinkite log.txt, jei norite sužinoti daugiau.", + "STR_WAIT_PROCESS ":"Pradėtas procesas, palaukite ...", + "STR_MENU_OPTION ":"Nustatymai", + "STR_MENU_SECURE_BOOT ":"Secure Boot palaikymas", + "STR_MENU_PART_CFG ":"Skirsnio konfigūracija", + "STR_BTN_OK ":"Gerai", + "STR_BTN_CANCEL ":"Atšaukti", + "STR_PRESERVE_SPACE ":"Rezervuoti vietos įrenginyje", + "STR_SPACE_VAL_INVALID ":"Neteisinga rezervuotos vietos vertė", + "STR_MENU_CLEAR ":"Pašalinti „Ventoy“", + "STR_CLEAR_SUCCESS ":"„Ventoy“ sėkmingai pašalintas iš įrenginio.", + "STR_CLEAR_FAILED ":"Įvyko klaida pašalinant „Ventoy“ iš įrenginio. USB ir bandyti dar kartą. Patikrinkite log.txt, jei norite sužinoti daugiau.", + "STR_MENU_PART_STYLE ":"Skirsnio formatas", + "STR_DISK_2TB_MBR_ERROR ":"Prašome pasirinkti GPT, jei įrenginys didesnis nei 2 TB.", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Macedonian (Македонски)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Влатко Стојанов", + + "STR_ERROR":"Грешка", + "STR_WARNING":"Предупредување", + "STR_INFO":"Инфо", + "STR_INCORRECT_DIR":"Ве молам покренете ме коректниот фолдер!", + "STR_INCORRECT_TREE_DIR":"Не покренувајте ме овде, ве молам превземете ја последната верзија и покренете ме таму.", + "STR_DEVICE":"Уред", + "STR_LOCAL_VER":"Ventoy Во Пакетот", + "STR_DISK_VER":"Ventoy Во Уредот", + "STR_STATUS":"Статус - Спремен", + "STR_INSTALL":"Инсталирај", + "STR_UPDATE":"Ажурирај", + "STR_UPDATE_TIP":"Операцијата ажурирање е безбедна, ISO датотеките нема да бидат променети.#@Продолжи?", + "STR_INSTALL_TIP":"УСБ дискот ќе биде форматиран и сите податоци ќе бидат уништени.#@Продолжи?", + "STR_INSTALL_TIP2":"УСБ дискот ќе биде форматиран и сите податоци ќе бидат уништени.#@Продолжи? (Двојна проверка)", + "STR_INSTALL_SUCCESS":"Честитки!#@Ventoy е успешно инсталиран на уредот.", + "STR_INSTALL_FAILED":"Се појави грешка при инсталацијата. Отстранете го УСБ драјвот и пробајте повторно. Проверете го log.txt за детали.", + "STR_UPDATE_SUCCESS":"Честитки!#@Ventoy е успешно ажуриран на уредот.", + "STR_UPDATE_FAILED":"Се појави грешка при ажурирањето. Отстранете го УСБ драјвот и пробајте повторно. Проверете го log.txt за детали.", + "STR_WAIT_PROCESS":"Процесот е вклучен, ве молиме почекајте...", + "STR_MENU_OPTION":"Опции", + "STR_MENU_SECURE_BOOT":"Secure Boot Поддршка", + "STR_MENU_PART_CFG":"Конфигурирање на партиции", + "STR_BTN_OK":"ОК", + "STR_BTN_CANCEL":"Излез", + "STR_PRESERVE_SPACE":"Резервирај место на крајот на дискот", + "STR_SPACE_VAL_INVALID":"Невалидна вредност за резервираното место", + "STR_MENU_CLEAR":"Отстрани го Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy е успешно отстранет од уредот.", + "STR_CLEAR_FAILED":"Се појави грешка при отстранувањето на Ventoy од уредот. Отстранете го УСБ драјвот и пробајте повторно. Проверете го log.txt за детали.", + "STR_MENU_PART_STYLE":"Тип на партиција", + "STR_DISK_2TB_MBR_ERROR":"Ве молиме изберете GPT за дискови поголеми од 2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Hebrew (עברית)", + "FontFamily":"tahoma", + "FontSize":16, + "Author":"chaim-chv", + + "STR_ERROR":"תקלה", + "STR_WARNING":"אזהרה", + "STR_INFO":"מידע", + "STR_INCORRECT_DIR":"הרץ בבקשה בתיקייה הנכונה!", + "STR_INCORRECT_TREE_DIR":"אל תריץ אותי כאן. בבקשה הורד את חבילת ההתקנה ששוחררה, ותריץ אותי שם.", + "STR_DEVICE":"התקן", + "STR_LOCAL_VER":"גרסת Ventoy מקומית", + "STR_DISK_VER":"גרסת Ventoy בהתקן", + "STR_STATUS":"סטטוס - מוכן", + "STR_INSTALL":"התקנה", + "STR_UPDATE":"עדכון", + "STR_UPDATE_TIP":"אופציית העדכון היא בטוחה לשימוש. קבצי ה-ISO לא ייפגעו.#@להמשיך?", + "STR_INSTALL_TIP":"הדיסק יפורמט וכל המידע שבו ייעלם.#@להמשיך?", + "STR_INSTALL_TIP2":"הדיסק יפורמט וכל המידע שבו ייעלפ ויימחק לחלוטין.#@להמשיך? (בדיקה לווידוא)", + "STR_INSTALL_SUCCESS":"הצלחה!#@Ventoy הותקנה בהצלחה על הדיסק הנבחר", + "STR_INSTALL_FAILED":"אירעה תקלה בניסיון ההתקנה. אפשר לנסות לחבר את הדיסק מחדש ולבצע ניסיון התקנה נוסף. עיין בקובץ ה-log.txt לפרטים נוספים.", + "STR_UPDATE_SUCCESS":"הצלחה!#@Ventoy הותקנה בהצלחה על הדיסק הנבחר", + "STR_UPDATE_FAILED":"אירעה תקלה בניסיון העדכון. אפשר לנסות לחבר את הדיסק מחדש ולבצע ניסיון נוסף לעדכון. עיין בקובץ ה-log.txt לפרטים נוספים.", + "STR_WAIT_PROCESS":"עובד... נא המתן", + "STR_MENU_OPTION":"אפשרויות", + "STR_MENU_SECURE_BOOT":"תמיכה ב-Secure Boot", + "STR_MENU_PART_CFG":"הגדרת מחיצות", + "STR_BTN_OK":"אישור", + "STR_BTN_CANCEL":"ביטול", + "STR_PRESERVE_SPACE":"הגדר מקום לשמירה בדיסק", + "STR_SPACE_VAL_INVALID":"ערך לא חוקי עבור שטח לשמירה", + "STR_MENU_CLEAR":"נקה את Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy נמחקה בהצלחה מהדיסק הנבחר.", + "STR_CLEAR_FAILED":"אירעה תקלה בניסיון למחוק את Ventoy מהדיסק הנבחר. אפשר לנסות לחבר את הדיסק מחדש ולבצע שוב מחיקה. עיין בקובץ ה-log.txt לפרטים נוספים.", + "STR_MENU_PART_STYLE":"סוג מחיצה", + "STR_DISK_2TB_MBR_ERROR":"בחר ב-GPT עבור דיסק שגדול מ-2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Portuguese (Português de Portugal)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Eskiso", + + "STR_ERROR":"Erro", + "STR_WARNING":"Aviso", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Por favor, execute na pasta correta!", + "STR_INCORRECT_TREE_DIR":"Não me execute aqui, por favor transfira o pacote de instalação, e execute lá.", + "STR_DEVICE":"Dispositivo", + "STR_LOCAL_VER":"Ventoy em pacote", + "STR_DISK_VER":"Ventoy no dispositivo", + "STR_STATUS":"Estado - PRONTO", + "STR_INSTALL":"Instalar", + "STR_UPDATE":"Atualizar", + "STR_UPDATE_TIP":"A operação de atualização é segura, os ficheiros ISO não serão alterados.#@Continuar?", + "STR_INSTALL_TIP":"O disco será formatado e todos os dados serão perdidos.#@Continuar?", + "STR_INSTALL_TIP2":"O disco será formatado e todos os dados serão perdidos.#@Continuar? (Confirmação)", + "STR_INSTALL_SUCCESS":"Parabéns!#@Ventoy foi instalado com sucesso no dispositivo.", + "STR_INSTALL_FAILED":"Um erro ocorreu durante a instalação. Pode reconectar o dispositivo USB e tentar novamente. Verifique o ficheiro log.txt para mais detalhes.", + "STR_UPDATE_SUCCESS":"Parabéns!#@Ventoy foi atualizado com sucesso no dispositivo.", + "STR_UPDATE_FAILED":"Um erro ocorreu durante a atualização. Pode reconectar o dispositivo USB e tentar novamente. Verifique o ficheiro log.txt para mais detalhes.", + "STR_WAIT_PROCESS":"Uma thread está em execução, por favor espere...", + "STR_MENU_OPTION":"Opção", + "STR_MENU_SECURE_BOOT":"Boot seguro", + "STR_MENU_PART_CFG":"Configuração da Partição", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Cancelar", + "STR_PRESERVE_SPACE":"Preservar algum espaço no final do disco", + "STR_SPACE_VAL_INVALID":"Valor invalido para o espaço reservado", + "STR_MENU_CLEAR":"Remover o Ventoy", + "STR_CLEAR_SUCCESS":"O Ventoy foi removido deste dispositivo com sucesso.", + "STR_CLEAR_FAILED":"Um erro ocorreu ao remover o Ventoy do disco. Pode reconectar o dispositivo USB e tentar novamente. Verifique o ficheiro log.txt para mais detalhes.", + "STR_MENU_PART_STYLE":"Estilo de Partição", + "STR_DISK_2TB_MBR_ERROR":"Por favor selecione GPT para discos maiores que 2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Indonesian (Bahasa Indonesia)", + "FontFamily":"Comic Sans MS", + "FontSize":16, + "Author":"Ida Bagus Anom Sanjaya", + + "STR_ERROR":"Kesalahan", + "STR_WARNING":"Peringatan", + "STR_INFO":"Informasi", + "STR_INCORRECT_DIR":"Silakan jalankan pada direktori yang benar!", + "STR_INCORRECT_TREE_DIR":"Jangan jalankan di sini, silakan unduh paket pemasangan yang diliris, dan jalankan di sana.", + "STR_DEVICE":"Perangkat", + "STR_LOCAL_VER":"Ventoy pada Paket", + "STR_DISK_VER":"Ventoy pada Perangkat", + "STR_STATUS":"Status - SIAP", + "STR_INSTALL":"Pasang", + "STR_UPDATE":"Perbarui", + "STR_UPDATE_TIP":"Operasi pembaruan ini aman, Tidak akan ada perubahan berkas ISO.#@Lanjutkan?", + "STR_INSTALL_TIP":"Disk akan di-format dan semua data akan hilang.#@Lanjutkan?", + "STR_INSTALL_TIP2":"Disk akan di-format dan semua data akan hilang.#@Lanjutkan? (YAKIN)", + "STR_INSTALL_SUCCESS":"Selamat!#@Ventoy telah berhasil terpasang di perangkat ini.", + "STR_INSTALL_FAILED":"Terjadi kesalahan ketika pemasangan berlangsung. Anda perlu mencabut-pasang ulang USB dan coba lagi. Cek log.txt untuk detil.", + "STR_UPDATE_SUCCESS":"Selamat!#@Ventoy telah berhasil diperbarui di perangkat ini.", + "STR_UPDATE_FAILED":"Terjadi kesalahan ketika pembaruan berlangsung. Anda perlu mencabut-pasang ulang USB dan coba lagi. Cek log.txt untuk detil.", + "STR_WAIT_PROCESS":"Tugas sedang berjalan, silakan tunggu...", + "STR_MENU_OPTION":"Pilihan", + "STR_MENU_SECURE_BOOT":"Dukungan Secure Boot", + "STR_MENU_PART_CFG":"Konfigurasi Partisi", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Batal", + "STR_PRESERVE_SPACE":"Mempertahankan sejumlah ruang penyimpanan pada disk di bawah ini", + "STR_SPACE_VAL_INVALID":"Nilai menpertahankan ruang tidak valid", + "STR_MENU_CLEAR":"Bersihkan Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy telah berhasil dihapus pada perangkat ini.", + "STR_CLEAR_FAILED":"Terjadi kesalahan ketika penghapusan berlangsung. Anda perlu mencabut-pasang ulang USB dan coba lagi. Cek log.txt untuk detil.", + "STR_MENU_PART_STYLE":"Gaya Partisi", + "STR_DISK_2TB_MBR_ERROR":"Silakan pilih GPT untuk disk yang lebih dari 2TB", + "STR_SHOW_ALL_DEV":"Show All Devices", + "STR_PART_ALIGN_4KB":"Align partitions with 4KB", + "STR_WEB_COMMUNICATION_ERR":"Communication error:", + "STR_WEB_REMOTE_ABNORMAL":"Communication error: remote abnormal", + "STR_WEB_REQUEST_TIMEOUT":"Communication error: Request timed out", + "STR_WEB_SERVICE_UNAVAILABLE":"Communication error: Service Unavailable", + "STR_WEB_TOKEN_MISMATCH":"Daemon status updated, please retry later.", + "STR_WEB_SERVICE_BUSY":"Service is busy, please retry later.", + "STRXXX":"" + }, + { + "name":"Ukrainian (Українська)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Teraskull", + + "STR_ERROR":"Помилка", + "STR_WARNING":"Попередження", + "STR_INFO":"Інформація", + "STR_INCORRECT_DIR":"Будь ласка, запустіть у правильному каталозі!", + "STR_INCORRECT_TREE_DIR":"Не запускайте мене тут, завантажте реліз інсталяційного пакету і запустіть там.", + "STR_DEVICE":"Пристрій", + "STR_LOCAL_VER":"Ventoy в пакеті", + "STR_DISK_VER":"Ventoy на пристрої", + "STR_STATUS":"Стан - ГОТОВИЙ", + "STR_INSTALL":"Встановити", + "STR_UPDATE":"Оновити", + "STR_UPDATE_TIP":"Процес оновлення безпечний, файли ISO залишаться незмінними.#@Продовжити?", + "STR_INSTALL_TIP":"Диск буде відформатовано, і всі дані будуть втрачені.#@Продовжити?", + "STR_INSTALL_TIP2":"Диск буде відформатовано, і всі дані будуть втрачені.#@Продовжити? (Подвійна перевірка)", + "STR_INSTALL_SUCCESS":"Вітаємо!#@Ventoy успішно встановлено на пристрій.", + "STR_INSTALL_FAILED":"Під час встановлення сталася помилка. Ви можете ще раз підключити USB і повторити спробу. Перевірте log.txt для деталей.", + "STR_UPDATE_SUCCESS":"Вітаємо!#@Ventoy на пристрої успішно оновлено.", + "STR_UPDATE_FAILED":"Під час оновлення сталася помилка. Ви можете ще раз підключити USB і повторити спробу. Перевірте log.txt для деталей.", + "STR_WAIT_PROCESS":"Потік запущено, зачекайте...", + "STR_MENU_OPTION":"Опції", + "STR_MENU_SECURE_BOOT":"Підтримка Secure Boot", + "STR_MENU_PART_CFG":"Конфігурація розділів", + "STR_BTN_OK":"ОК", + "STR_BTN_CANCEL":"Скасувати", + "STR_PRESERVE_SPACE":"Зарезервувати простір в кінці диска", + "STR_SPACE_VAL_INVALID":"Недійсне значення для зарезервованого простору", + "STR_MENU_CLEAR":"Видалити Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy успішно видалено з пристрою.", + "STR_CLEAR_FAILED":"Під час видалення Ventoy сталася помилка. Ви можете ще раз підключити USB і повторити спробу. Перевірте log.txt для деталей.", + "STR_MENU_PART_STYLE":"Стиль розмітки розділів", + "STR_DISK_2TB_MBR_ERROR":"Будь ласка, виберіть GPT для дисків понад 2TB", + "STR_SHOW_ALL_DEV":"Показати всі пристрої", + "STR_PART_ALIGN_4KB":"Вирівняти розділи з розміром 4КБ", + "STR_WEB_COMMUNICATION_ERR":"Помилка зв'язку: ", + "STR_WEB_REMOTE_ABNORMAL":"Помилка зв'язку: віддалене з'єднання недійсне", + "STR_WEB_REQUEST_TIMEOUT":"Помилка зв'язку: Час очікування запиту минув", + "STR_WEB_SERVICE_UNAVAILABLE":"Помилка зв'язку: Служба недоступна", + "STR_WEB_TOKEN_MISMATCH":"Статус демона оновлено. Повторіть спробу пізніше.", + "STR_WEB_SERVICE_BUSY":"Служба зайнята, повторіть спробу пізніше.", + "STRXXX":"" + }, + { + "name":"Greek (Ελληνικά)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"grmasa, Vasilis Kosmidis", + + "STR_ERROR":"Σφάλμα", + "STR_WARNING":"Προειδοποίηση", + "STR_INFO":"Πληροφορίες", + "STR_INCORRECT_DIR":"Παρακαλώ εκτελέστε στον σωστό κατάλογο!", + "STR_INCORRECT_TREE_DIR":"Μην με εκτελείτε εδώ, κατεβάστε το πακέτο εγκατάστασης και εκτελέστε εκεί.", + "STR_DEVICE":"Συσκευή", + "STR_LOCAL_VER":"Ventoy στο πρόγραμμα", + "STR_DISK_VER":"Ventoy στη συσκευή", + "STR_STATUS":"Κατάσταση - ΕΤΟΙΜΟ", + "STR_INSTALL":"Εγκατάσταση", + "STR_UPDATE":"Αναβάθμιση", + "STR_UPDATE_TIP":"Η λειτουργία αναβάθμισης είναι ασφαλής, τα αρχεία ISO δεν θα αλλάξουν.#@Συνέχεια;", + "STR_INSTALL_TIP":"Ο δίσκος θα μορφοποιηθεί και όλα τα δεδομένα θα χαθούν.#@Συνέχεια;", + "STR_INSTALL_TIP2":"Ο δίσκος θα μορφοποιηθεί και όλα τα δεδομένα θα χαθούν.#@Συνέχεια; (Επανελέγξτε)", + "STR_INSTALL_SUCCESS":"Συγχαρητήρια!#Το @Ventoy έχει εγκατασταθεί με επιτυχία στη συσκευή.", + "STR_INSTALL_FAILED":"Παρουσιάστηκε σφάλμα κατά την εγκατάσταση. Μπορείτε να επανασυνδέσετε το USB και να δοκιμάσετε ξανά. Ελέγξτε το αρχείο log.txt για λεπτομέρειες.", + "STR_UPDATE_SUCCESS":"Συγχαρητήρια!#Το @Ventoy ενημερώθηκε με επιτυχία στη συσκευή.", + "STR_UPDATE_FAILED":"Παρουσιάστηκε σφάλμα κατά την ενημέρωση. Μπορείτε να επανασυνδέσετε το USB και να δοκιμάσετε ξανά. Ελέγξτε το αρχείο log.txt για λεπτομέρειες.", + "STR_WAIT_PROCESS":"Ένα νήμα εκτελείται, παρακαλώ περιμένετε...", + "STR_MENU_OPTION":"Επιλογές", + "STR_MENU_SECURE_BOOT":"Υποστήριξη ασφαλούς εκκίνησης (Secure Boot)", + "STR_MENU_PART_CFG":"Διαμόρφωση κατατμήσεων", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Ακύρωση", + "STR_PRESERVE_SPACE":"Διατηρήστε λίγο χώρο στο κάτω μέρος του δίσκου", + "STR_SPACE_VAL_INVALID":"Μη έγκυρη τιμή για τον δεσμευμένο χώρο", + "STR_MENU_CLEAR":"Απεγκατάσταση του Ventoy", + "STR_CLEAR_SUCCESS":"Το Ventoy καταργήθηκε με επιτυχία από τη συσκευή.", + "STR_CLEAR_FAILED":"Παρουσιάστηκε σφάλμα κατά την εκκαθάριση του Ventoy από το δίσκο. Μπορείτε να επανασυνδέσετε το USB και να δοκιμάσετε ξανά. Ελέγξτε το αρχείο log.txt για λεπτομέρειες.", + "STR_MENU_PART_STYLE":"Στυλ κατατμήσεων", + "STR_DISK_2TB_MBR_ERROR":"Παρακαλώ επιλέξτε GPT για δίσκο άνω των 2 TB", + "STR_SHOW_ALL_DEV":"Προβολή όλων των συσκευών", + "STR_PART_ALIGN_4KB":"Ευθυγράμμιση κατατμήσεων με 4KB", + "STR_WEB_COMMUNICATION_ERR":"Σφάλμα επικοινωνίας:", + "STR_WEB_REMOTE_ABNORMAL":"Σφάλμα επικοινωνίας: απομακρυσμένη ανωμαλία", + "STR_WEB_REQUEST_TIMEOUT":"Σφάλμα επικοινωνίας: χρονική λήξη αιτήματος", + "STR_WEB_SERVICE_UNAVAILABLE":"Σφάλμα επικοινωνίας: υπηρεσία μη διαθέσιμη", + "STR_WEB_TOKEN_MISMATCH":"Η κατάσταση του δαίμονα επικαιροποιήθηκε. Παρακαλώ δοκιμάστε αργότερα.", + "STR_WEB_SERVICE_BUSY":"Η υπηρεσία είναι απασχολημένη. Παρακαλώ δοκιμάστε αργότερα.", + "STRXXX":"" + }, + { + "name":"Swedish (Svenska)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Sopor", + + "STR_ERROR":"Fel", + "STR_WARNING":"Varning", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Kör programmet från korrekt katalog!", + "STR_INCORRECT_TREE_DIR":"Kör inte programmet här. Ladda ner installationspaketet och kör programmet där.", + "STR_DEVICE":"Enhet", + "STR_LOCAL_VER":"Ventoy i paketet", + "STR_DISK_VER":"Ventoy på enheten", + "STR_STATUS":"Status - REDO", + "STR_INSTALL":"Installera", + "STR_UPDATE":"Uppdatera", + "STR_UPDATE_TIP":"Uppdateringen är säker, ISO-filerna kommer att vara oförändrade.#@Fortsätta?", + "STR_INSTALL_TIP":"Enheten kommer att formateras och all data kommer att gå förlorad.#@Fortsätta?", + "STR_INSTALL_TIP2":"Enheten kommer att formateras och all data kommer att gå förlorad.#@Fortsätta? (Sista varningen!)", + "STR_INSTALL_SUCCESS":"Gratulerar!#@Ventoy har installerats på enheten.", + "STR_INSTALL_FAILED":"Ett fel inträffade under installationen. Prova att återansluta USB-enheten och försök igen. Läs i log.txt för mer information.", + "STR_UPDATE_SUCCESS":"Gratulerar!#@Ventoy har uppdaterats på enheten.", + "STR_UPDATE_FAILED":"Ett fel inträffade under uppdateringen. Prova att återansluta USB-enheten och försök igen. Läs i log.txt för mer information.", + "STR_WAIT_PROCESS":"En tråd körs redan, vänta...", + "STR_MENU_OPTION":"Alternativ", + "STR_MENU_SECURE_BOOT":"Stöd för säker start", + "STR_MENU_PART_CFG":"Partitionskonfiguration", + "STR_BTN_OK":"OK", + "STR_BTN_CANCEL":"Avbryt", + "STR_PRESERVE_SPACE":"Spara lite utrymme i slutet av enheten", + "STR_SPACE_VAL_INVALID":"Ogiltigt värde för reserverat utrymme", + "STR_MENU_CLEAR":"Ta bort Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy har tagits bort från enheten.", + "STR_CLEAR_FAILED":"Ett fel inträffade när Ventoy skulle tas bort från enheten. Prova att återansluta USB-enheten och försök igen. Läs i log.txt för mer information.", + "STR_MENU_PART_STYLE":"Partitionsstil", + "STR_DISK_2TB_MBR_ERROR":"Välj GPT för enhet över 2 TB", + "STR_SHOW_ALL_DEV":"Visa alla enheter", + "STR_PART_ALIGN_4KB":"Justera partitioner med 4KB", + "STR_WEB_COMMUNICATION_ERR":"Kommunikationsfel:", + "STR_WEB_REMOTE_ABNORMAL":"Kommunikationsfel: onormal fjärr", + "STR_WEB_REQUEST_TIMEOUT":"Kommunikationsfel: Begäran tog för lång tid", + "STR_WEB_SERVICE_UNAVAILABLE":"Kommunikationsfel: Tjänsten är inte tillgänglig", + "STR_WEB_TOKEN_MISMATCH":"Daemon-status uppdaterad. Försök igen senare.", + "STR_WEB_SERVICE_BUSY":"Tjänster är upptagen. Försök igen senare.", + "STRXXX":"" + }, + { + "name":"Slovenian (Slovenski)", + "FontFamily":"Courier New", + "FontSize":16, + "Author":"Urajmal", + + "STR_ERROR":"Napaka", + "STR_WARNING":"Opozorilo", + "STR_INFO":"Info", + "STR_INCORRECT_DIR":"Prosim izberite pravilno pot!", + "STR_INCORRECT_TREE_DIR":"Ne zaganjajte tukaj. Prosim prenesite posodobljeno verzijo in jo zaženite.", + "STR_DEVICE":"Naprava", + "STR_LOCAL_VER":"Ventoy verzija", + "STR_DISK_VER":"Ventoy na napravi", + "STR_STATUS":"Status - PRIPRAVLJEN", + "STR_INSTALL":"Namesti", + "STR_UPDATE":"Nadgradi", + "STR_UPDATE_TIP":"Varna nadgradnja. ISO datoteke ne bodo spremenjene.#@Nadaljujem?", + "STR_INSTALL_TIP":"Ta disk bo formatiran in vsi podatki bodo izbrisani.#@Nadaljujem?", + "STR_INSTALL_TIP2":"Ta disk bo formatiran in vsi podatki bodo izbrisani.#@Nadaljujem? (Druga potrditev)", + "STR_INSTALL_SUCCESS":"Čestitamo!#@ventoy je bil uspešno nameščen na napravo.", + "STR_INSTALL_FAILED":"Med namestitvijo je prišlo do napake. Iztaknite in ponovno priklopite USB. Preverite log.txt za podrobnosti.", + "STR_UPDATE_SUCCESS":"Čestitamo!#@ventoy na napravi je bil uspešno posodobljen.", + "STR_UPDATE_FAILED":"Prišlo je do napake med nadgradnjo. Iztaknite in ponovno priklopite USB. Preverite log.txt za podrobnosti.", + "STR_WAIT_PROCESS":"Proces teče, prosimo počakajte, ...", + "STR_MENU_OPTION":"Nastavitve", + "STR_MENU_SECURE_BOOT":"Podpora Secure Boot", + "STR_MENU_PART_CFG":"Konfiguracija particij", + "STR_BTN_OK":"V redu", + "STR_BTN_CANCEL":"Prekliči", + "STR_PRESERVE_SPACE":"Ohrani nekaj prostora na koncu particije", + "STR_SPACE_VAL_INVALID":"Nepravilna vrednost za rezerviran prostor", + "STR_MENU_CLEAR":"Odstrani Ventoy", + "STR_CLEAR_SUCCESS":"Ventoy je bil uspešno odstranjen.", + "STR_CLEAR_FAILED":"Prišlo je do napake pri odstranjevanju Ventoy-a iz diska. Iztaknite in ponovno priklopite USB. Preverite log.txt za podrobnosti.", + "STR_MENU_PART_STYLE":"Vrsta particije", + "STR_DISK_2TB_MBR_ERROR":"Prosim izberite GPT za particije nad 2TB", + "STR_SHOW_ALL_DEV":"Pokaži vse naprave", + "STR_PART_ALIGN_4KB":"Poravnaj particije na 4KB", + "STR_WEB_COMMUNICATION_ERR":"Napaka v komunikaciji:", + "STR_WEB_REMOTE_ABNORMAL":"Napaka v komunikaciji: oddaljena težava", + "STR_WEB_REQUEST_TIMEOUT":"Napaka v komunikaciji: čas je potekel", + "STR_WEB_SERVICE_UNAVAILABLE":"Napaka v komunikaciji: storitev ni na voljo", + "STR_WEB_TOKEN_MISMATCH":"Zahteva storitve je potekla, prosim poskusite kasneje.", + "STR_WEB_SERVICE_BUSY":"Storitev je zasedena, prosim poskusite kasneje.", + "STRXXX":"" + } +] \ No newline at end of file diff --git a/LinuxGUI/language.sh b/LinuxGUI/language.sh index 2a0b6df8..07d8fe28 100644 --- a/LinuxGUI/language.sh +++ b/LinuxGUI/language.sh @@ -1,37 +1,19 @@ #!/bin/bash -echo "generating languages.js ..." +VTOY_PATH=$PWD/../ -iconv -f utf-16 -t utf-8 ../LANGUAGES/languages.ini | egrep -v '=STR|^;' | egrep 'Language-|STR_' > languages.js +echo "checking languages.json ..." +sh $VTOY_PATH/LANGUAGES/check.sh $VTOY_PATH || exit 1 -dos2unix languages.js - -sed 's/\(STR_.*\)=/"\1":/g' -i languages.js - -sed "s/: *'/:\"/g" -i languages.js - -sed "s/'\s*$/\",/g" -i languages.js - -sed 's/\[Language-\(.*\)\].*/"STR_XXX":""},{"name":"\1",/g' -i languages.js +echo "generating languages.json ..." -sed "1s/.*\},/var vtoy_language_data = \[/" -i languages.js +echo "var vtoy_language_data = " > languages.js +cat $VTOY_PATH/LANGUAGES/languages.json >> languages.js +echo ";" >> languages.js -sed 's/\("STR_WEB_COMMUNICATION_ERR"[^,]*\)/\1,/g' -i languages.js -sed 's/,,/,/g' -i languages.js - -CNT=$(grep -v -c ',$' languages.js) - -if [ $CNT -gt 0 ]; then - echo "====== FAILED =========" - grep -v -n ',$' languages.js - exit 1 -fi - - -echo '"STR_XXX":""}' >> languages.js -echo '];' >> languages.js +dos2unix languages.js rm -f WebUI/static/js/languages.js mv languages.js WebUI/static/js/ -echo "====== SUCCESS ==========" \ No newline at end of file +echo "====== SUCCESS ==========" diff --git a/Ventoy2Disk/Ventoy2Disk/Language.h b/Ventoy2Disk/Ventoy2Disk/Language.h index 144a9b23..aa1f4c0f 100644 --- a/Ventoy2Disk/Ventoy2Disk/Language.h +++ b/Ventoy2Disk/Ventoy2Disk/Language.h @@ -101,13 +101,19 @@ typedef enum OPT_SUBMENU #define VTOY_MENU_LANGUAGE_BEGIN 0xB000 -#define VENTOY_LANGUAGE_INI TEXT(".\\ventoy\\languages.ini") +#define VENTOY_LANGUAGE_INI TEXT(".\\ventoy\\languages.ini") +#define VENTOY_LANGUAGE_JSON TEXT(".\\ventoy\\languages.json") +#define VENTOY_LANGUAGE_INI_A ".\\ventoy\\languages.ini" +#define VENTOY_LANGUAGE_JSON_A ".\\ventoy\\languages.json" + #define VENTOY_CFG_INI TEXT(".\\Ventoy2Disk.ini") #define VENTOY_CFG_INI_A ".\\Ventoy2Disk.ini" #define VENTOY_MAX_LANGUAGE 200 #define GET_INI_STRING(Section, Key, Buf) GetPrivateProfileString(Section, Key, TEXT("#"), Buf, sizeof(Buf), VENTOY_LANGUAGE_INI) + + typedef struct VENTOY_LANGUAGE { WCHAR Name[128]; diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c index 43c4ea0c..4c9fdf4d 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c @@ -283,6 +283,12 @@ static int FilterPhysicalDrive(PHY_DRIVE_INFO *pDriveList, DWORD DriveCount) CurDrive->PartStyle = (MBR.PartTbl[0].FsFlag == 0xEE) ? 1 : 0; GetVentoyVerInPhyDrive(CurDrive, Part2StartSector, CurDrive->VentoyVersion, sizeof(CurDrive->VentoyVersion), &(CurDrive->SecureBootSupport)); Log("PhyDrive %d is Ventoy Disk ver:%s SecureBoot:%u", CurDrive->PhyDrive, CurDrive->VentoyVersion, CurDrive->SecureBootSupport); + + if (CurDrive->VentoyVersion[0] == 0) + { + CurDrive->VentoyVersion[0] = '?'; + Log("Unknown Ventoy Version"); + } } } diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h index b9a84db4..8ec38ab8 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h @@ -148,7 +148,7 @@ typedef struct PHY_DRIVE_INFO STORAGE_BUS_TYPE BusType; CHAR DriveLetters[64]; - + CHAR VentoyVersion[32]; BOOL SecureBootSupport; diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj index 7824269f..e1276f2a 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj @@ -110,6 +110,7 @@ + @@ -133,6 +134,7 @@ + diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters index a4dede7c..45f86ffe 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters @@ -78,6 +78,9 @@ 源文件 + + 源文件 + @@ -137,6 +140,9 @@ 头文件 + + 头文件 + diff --git a/Ventoy2Disk/Ventoy2Disk/VentoyJson.c b/Ventoy2Disk/Ventoy2Disk/VentoyJson.c new file mode 100644 index 00000000..1bfca570 --- /dev/null +++ b/Ventoy2Disk/Ventoy2Disk/VentoyJson.c @@ -0,0 +1,771 @@ +/****************************************************************************** + * VentoyJson.c + * + * Copyright (c) 2021, longpanda + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#ifdef FOR_VTOY_JSON_CHECK +#include +#include +#include +#else +#include +#include "Ventoy2Disk.h" +#endif + +#include "VentoyJson.h" + +static void vtoy_json_free(VTOY_JSON *pstJsonHead) +{ + VTOY_JSON *pstNext = NULL; + + while (NULL != pstJsonHead) + { + pstNext = pstJsonHead->pstNext; + if ((pstJsonHead->enDataType < JSON_TYPE_BUTT) && (NULL != pstJsonHead->pstChild)) + { + vtoy_json_free(pstJsonHead->pstChild); + } + + free(pstJsonHead); + pstJsonHead = pstNext; + } + + return; +} + +static char *vtoy_json_skip(const char *pcData) +{ + while ((NULL != pcData) && ('\0' != *pcData) && (*pcData <= 32)) + { + pcData++; + } + + return (char *)pcData; +} + +VTOY_JSON *vtoy_json_find_item +( + VTOY_JSON *pstJson, + JSON_TYPE enDataType, + const char *szKey +) +{ + while (NULL != pstJson) + { + if ((enDataType == pstJson->enDataType) && + (0 == strcmp(szKey, pstJson->pcName))) + { + return pstJson; + } + pstJson = pstJson->pstNext; + } + + return NULL; +} + +static int vtoy_json_parse_number +( + VTOY_JSON *pstJson, + const char *pcData, + const char **ppcEnd +) +{ + unsigned long Value; + + Value = strtoul(pcData, (char **)ppcEnd, 10); + if (*ppcEnd == pcData) + { + Log("Failed to parse json number %s.", pcData); + return JSON_FAILED; + } + + pstJson->enDataType = JSON_TYPE_NUMBER; + pstJson->unData.lValue = Value; + + return JSON_SUCCESS; +} + +static int vtoy_json_parse_string +( + char *pcNewStart, + char *pcRawStart, + VTOY_JSON *pstJson, + const char *pcData, + const char **ppcEnd +) +{ + UINT32 uiLen = 0; + const char *pcPos = NULL; + const char *pcTmp = pcData + 1; + + *ppcEnd = pcData; + + if ('\"' != *pcData) + { + return JSON_FAILED; + } + + pcPos = strchr(pcTmp, '\"'); + if ((NULL == pcPos) || (pcPos < pcTmp)) + { + Log("Invalid string %s.", pcData); + return JSON_FAILED; + } + + *ppcEnd = pcPos + 1; + uiLen = (UINT32)(unsigned long)(pcPos - pcTmp); + + pstJson->enDataType = JSON_TYPE_STRING; + pstJson->unData.pcStrVal = pcNewStart + (pcTmp - pcRawStart); + pstJson->unData.pcStrVal[uiLen] = '\0'; + + return JSON_SUCCESS; +} + +static int vtoy_json_parse_array +( + char *pcNewStart, + char *pcRawStart, + VTOY_JSON *pstJson, + const char *pcData, + const char **ppcEnd +) +{ + int Ret = JSON_SUCCESS; + VTOY_JSON *pstJsonChild = NULL; + VTOY_JSON *pstJsonItem = NULL; + const char *pcTmp = pcData + 1; + + *ppcEnd = pcData; + pstJson->enDataType = JSON_TYPE_ARRAY; + + if ('[' != *pcData) + { + return JSON_FAILED; + } + + pcTmp = vtoy_json_skip(pcTmp); + + if (']' == *pcTmp) + { + *ppcEnd = pcTmp + 1; + return JSON_SUCCESS; + } + + JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED); + + Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd); + if (JSON_SUCCESS != Ret) + { + Log("Failed to parse array child."); + return JSON_FAILED; + } + + pstJsonChild = pstJson->pstChild; + pcTmp = vtoy_json_skip(*ppcEnd); + while ((NULL != pcTmp) && (',' == *pcTmp)) + { + JSON_NEW_ITEM(pstJsonItem, JSON_FAILED); + pstJsonChild->pstNext = pstJsonItem; + pstJsonItem->pstPrev = pstJsonChild; + pstJsonChild = pstJsonItem; + + Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd); + if (JSON_SUCCESS != Ret) + { + Log("Failed to parse array child."); + return JSON_FAILED; + } + pcTmp = vtoy_json_skip(*ppcEnd); + } + + if ((NULL != pcTmp) && (']' == *pcTmp)) + { + *ppcEnd = pcTmp + 1; + return JSON_SUCCESS; + } + else + { + *ppcEnd = pcTmp; + return JSON_FAILED; + } +} + +static int vtoy_json_parse_object +( + char *pcNewStart, + char *pcRawStart, + VTOY_JSON *pstJson, + const char *pcData, + const char **ppcEnd +) +{ + int Ret = JSON_SUCCESS; + VTOY_JSON *pstJsonChild = NULL; + VTOY_JSON *pstJsonItem = NULL; + const char *pcTmp = pcData + 1; + + *ppcEnd = pcData; + pstJson->enDataType = JSON_TYPE_OBJECT; + + if ('{' != *pcData) + { + return JSON_FAILED; + } + + pcTmp = vtoy_json_skip(pcTmp); + if ('}' == *pcTmp) + { + *ppcEnd = pcTmp + 1; + return JSON_SUCCESS; + } + + JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED); + + Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd); + if (JSON_SUCCESS != Ret) + { + Log("Failed to parse array child."); + return JSON_FAILED; + } + + pstJsonChild = pstJson->pstChild; + pstJsonChild->pcName = pstJsonChild->unData.pcStrVal; + pstJsonChild->unData.pcStrVal = NULL; + + pcTmp = vtoy_json_skip(*ppcEnd); + if ((NULL == pcTmp) || (':' != *pcTmp)) + { + *ppcEnd = pcTmp; + return JSON_FAILED; + } + + Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd); + if (JSON_SUCCESS != Ret) + { + Log("Failed to parse array child."); + return JSON_FAILED; + } + + pcTmp = vtoy_json_skip(*ppcEnd); + while ((NULL != pcTmp) && (',' == *pcTmp)) + { + JSON_NEW_ITEM(pstJsonItem, JSON_FAILED); + pstJsonChild->pstNext = pstJsonItem; + pstJsonItem->pstPrev = pstJsonChild; + pstJsonChild = pstJsonItem; + + Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd); + if (JSON_SUCCESS != Ret) + { + Log("Failed to parse array child."); + return JSON_FAILED; + } + + pcTmp = vtoy_json_skip(*ppcEnd); + pstJsonChild->pcName = pstJsonChild->unData.pcStrVal; + pstJsonChild->unData.pcStrVal = NULL; + if ((NULL == pcTmp) || (':' != *pcTmp)) + { + *ppcEnd = pcTmp; + return JSON_FAILED; + } + + Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd); + if (JSON_SUCCESS != Ret) + { + Log("Failed to parse array child."); + return JSON_FAILED; + } + + pcTmp = vtoy_json_skip(*ppcEnd); + } + + if ((NULL != pcTmp) && ('}' == *pcTmp)) + { + *ppcEnd = pcTmp + 1; + return JSON_SUCCESS; + } + else + { + *ppcEnd = pcTmp; + return JSON_FAILED; + } +} + +int vtoy_json_parse_value +( + char *pcNewStart, + char *pcRawStart, + VTOY_JSON *pstJson, + const char *pcData, + const char **ppcEnd +) +{ + pcData = vtoy_json_skip(pcData); + + switch (*pcData) + { + case 'n': + { + if (0 == strncmp(pcData, "null", 4)) + { + pstJson->enDataType = JSON_TYPE_NULL; + *ppcEnd = pcData + 4; + return JSON_SUCCESS; + } + break; + } + case 'f': + { + if (0 == strncmp(pcData, "false", 5)) + { + pstJson->enDataType = JSON_TYPE_BOOL; + pstJson->unData.lValue = 0; + *ppcEnd = pcData + 5; + return JSON_SUCCESS; + } + break; + } + case 't': + { + if (0 == strncmp(pcData, "true", 4)) + { + pstJson->enDataType = JSON_TYPE_BOOL; + pstJson->unData.lValue = 1; + *ppcEnd = pcData + 4; + return JSON_SUCCESS; + } + break; + } + case '\"': + { + return vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd); + } + case '[': + { + return vtoy_json_parse_array(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd); + } + case '{': + { + return vtoy_json_parse_object(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd); + } + case '-': + { + return vtoy_json_parse_number(pstJson, pcData, ppcEnd); + } + default : + { + if (*pcData >= '0' && *pcData <= '9') + { + return vtoy_json_parse_number(pstJson, pcData, ppcEnd); + } + } + } + + *ppcEnd = pcData; + Log("Invalid json data %u.", (UINT8)(*pcData)); + return JSON_FAILED; +} + +VTOY_JSON * vtoy_json_create(void) +{ + VTOY_JSON *pstJson = NULL; + + pstJson = (VTOY_JSON *)malloc(sizeof(VTOY_JSON)); + if (NULL == pstJson) + { + return NULL; + } + memset(pstJson, 0, sizeof(VTOY_JSON)); + return pstJson; +} + +int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData) +{ + UINT32 uiMemSize = 0; + int Ret = JSON_SUCCESS; + char *pcNewBuf = NULL; + const char *pcEnd = NULL; + + uiMemSize = strlen(szJsonData) + 1; + pcNewBuf = (char *)malloc(uiMemSize); + if (NULL == pcNewBuf) + { + Log("Failed to alloc new buf."); + return JSON_FAILED; + } + memcpy(pcNewBuf, szJsonData, uiMemSize); + pcNewBuf[uiMemSize - 1] = 0; + + Ret = vtoy_json_parse_value(pcNewBuf, (char *)szJsonData, pstJson, szJsonData, &pcEnd); + if (JSON_SUCCESS != Ret) + { + Log("Failed to parse json data start=%p, end=%p", szJsonData, pcEnd); + return JSON_FAILED; + } + + return JSON_SUCCESS; +} + +int vtoy_json_scan_parse +( + const VTOY_JSON *pstJson, + UINT32 uiParseNum, + JSON_PARSE *pstJsonParse +) +{ + UINT32 i = 0; + const VTOY_JSON *pstJsonCur = NULL; + JSON_PARSE *pstCurParse = NULL; + + for (pstJsonCur = pstJson; NULL != pstJsonCur; pstJsonCur = pstJsonCur->pstNext) + { + if ((JSON_TYPE_OBJECT == pstJsonCur->enDataType) || + (JSON_TYPE_ARRAY == pstJsonCur->enDataType)) + { + continue; + } + + for (i = 0, pstCurParse = NULL; i < uiParseNum; i++) + { + if (0 == strcmp(pstJsonParse[i].pcKey, pstJsonCur->pcName)) + { + pstCurParse = pstJsonParse + i; + break; + } + } + + if (NULL == pstCurParse) + { + continue; + } + + switch (pstJsonCur->enDataType) + { + case JSON_TYPE_NUMBER: + { + if (sizeof(UINT32) == pstCurParse->uiBufSize) + { + *(UINT32 *)(pstCurParse->pDataBuf) = (UINT32)pstJsonCur->unData.lValue; + } + else if (sizeof(UINT16) == pstCurParse->uiBufSize) + { + *(UINT16 *)(pstCurParse->pDataBuf) = (UINT16)pstJsonCur->unData.lValue; + } + else if (sizeof(UINT8) == pstCurParse->uiBufSize) + { + *(UINT8 *)(pstCurParse->pDataBuf) = (UINT8)pstJsonCur->unData.lValue; + } + else if ((pstCurParse->uiBufSize > sizeof(UINT64))) + { + sprintf_s((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, "%llu", + (unsigned long long)(pstJsonCur->unData.lValue)); + } + else + { + Log("Invalid number data buf size %u.", pstCurParse->uiBufSize); + } + break; + } + case JSON_TYPE_STRING: + { + strcpy_s((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, pstJsonCur->unData.pcStrVal); + break; + } + case JSON_TYPE_BOOL: + { + *(UINT8 *)(pstCurParse->pDataBuf) = (pstJsonCur->unData.lValue) > 0 ? 1 : 0; + break; + } + default : + { + break; + } + } + } + + return JSON_SUCCESS; +} + +int vtoy_json_scan_array +( + VTOY_JSON *pstJson, + const char *szKey, + VTOY_JSON **ppstArrayItem +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + *ppstArrayItem = pstJsonItem; + + return JSON_SUCCESS; +} + +int vtoy_json_scan_array_ex +( + VTOY_JSON *pstJson, + const char *szKey, + VTOY_JSON **ppstArrayItem +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + *ppstArrayItem = pstJsonItem->pstChild; + + return JSON_SUCCESS; +} + +int vtoy_json_scan_object +( + VTOY_JSON *pstJson, + const char *szKey, + VTOY_JSON **ppstObjectItem +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_OBJECT, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + *ppstObjectItem = pstJsonItem; + + return JSON_SUCCESS; +} + +int vtoy_json_get_int +( + VTOY_JSON *pstJson, + const char *szKey, + int *piValue +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + *piValue = (int)pstJsonItem->unData.lValue; + + return JSON_SUCCESS; +} + +int vtoy_json_get_uint +( + VTOY_JSON *pstJson, + const char *szKey, + UINT32 *puiValue +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + *puiValue = (UINT32)pstJsonItem->unData.lValue; + + return JSON_SUCCESS; +} + +int vtoy_json_get_uint64 +( + VTOY_JSON *pstJson, + const char *szKey, + UINT64 *pui64Value +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + *pui64Value = (UINT64)pstJsonItem->unData.lValue; + + return JSON_SUCCESS; +} + +int vtoy_json_get_bool +( + VTOY_JSON *pstJson, + const char *szKey, + UINT8 *pbValue +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_BOOL, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + *pbValue = pstJsonItem->unData.lValue > 0 ? 1 : 0; + + return JSON_SUCCESS; +} + +int vtoy_json_get_string +( + VTOY_JSON *pstJson, + const char *szKey, + UINT32 uiBufLen, + char *pcBuf +) +{ + VTOY_JSON *pstJsonItem = NULL; + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return JSON_NOT_FOUND; + } + + strcpy_s(pcBuf, uiBufLen, pstJsonItem->unData.pcStrVal); + + return JSON_SUCCESS; +} + +const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey) +{ + VTOY_JSON *pstJsonItem = NULL; + + if ((NULL == pstJson) || (NULL == szKey)) + { + return NULL; + } + + pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey); + if (NULL == pstJsonItem) + { + Log("Key %s is not found in json data.", szKey); + return NULL; + } + + return pstJsonItem->unData.pcStrVal; +} + +int vtoy_json_destroy(VTOY_JSON *pstJson) +{ + if (NULL == pstJson) + { + return JSON_SUCCESS; + } + + if (NULL != pstJson->pstChild) + { + vtoy_json_free(pstJson->pstChild); + } + + if (NULL != pstJson->pstNext) + { + vtoy_json_free(pstJson->pstNext); + } + + free(pstJson); + + return JSON_SUCCESS; +} + + +#ifdef FOR_VTOY_JSON_CHECK + +int main(int argc, char**argv) +{ + int ret = 1; + int FileSize; + FILE *fp; + void *Data = NULL; + VTOY_JSON *json = NULL; + + fp = fopen(argv[1], "rb"); + if (!fp) + { + Log("Failed to open %s\n", argv[1]); + goto out; + } + + fseek(fp, 0, SEEK_END); + FileSize = (int)ftell(fp); + fseek(fp, 0, SEEK_SET); + + Data = malloc(FileSize + 4); + if (!Data) + { + Log("Failed to malloc %d\n", FileSize + 4); + goto out; + } + *((char *)Data + FileSize) = 0; + + fread(Data, 1, FileSize, fp); + + json = vtoy_json_create(); + if (!json) + { + Log("Failed vtoy_json_create\n"); + goto out; + } + + if (vtoy_json_parse(json, (char *)Data) != JSON_SUCCESS) + { + goto out; + } + + ret = 0; + +out: + if (fp) fclose(fp); + if (Data) free(Data); + if (json) vtoy_json_destroy(json); + + printf("\n"); + return ret; +} + +#endif diff --git a/Ventoy2Disk/Ventoy2Disk/VentoyJson.h b/Ventoy2Disk/Ventoy2Disk/VentoyJson.h new file mode 100644 index 00000000..6c2cf3b3 --- /dev/null +++ b/Ventoy2Disk/Ventoy2Disk/VentoyJson.h @@ -0,0 +1,164 @@ +/****************************************************************************** + * VentoyJson.h + * + * Copyright (c) 2021, longpanda + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#ifndef __VENTOY_JSON_H__ +#define __VENTOY_JSON_H__ + +#ifdef FOR_VTOY_JSON_CHECK +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef unsigned long long UINT64; + +#define Log printf +#define strcpy_s(a, b, c) strncpy(a, c, b) +#define sprintf_s snprintf +#endif + +#define JSON_SUCCESS 0 +#define JSON_FAILED 1 +#define JSON_NOT_FOUND 2 + +typedef enum _JSON_TYPE +{ + JSON_TYPE_NUMBER = 0, + JSON_TYPE_STRING, + JSON_TYPE_BOOL, + JSON_TYPE_ARRAY, + JSON_TYPE_OBJECT, + JSON_TYPE_NULL, + JSON_TYPE_BUTT +}JSON_TYPE; + + +typedef struct _VTOY_JSON +{ + struct _VTOY_JSON *pstPrev; + struct _VTOY_JSON *pstNext; + struct _VTOY_JSON *pstChild; + + JSON_TYPE enDataType; + union + { + char *pcStrVal; + int iNumVal; + UINT64 lValue; + }unData; + + char *pcName; +}VTOY_JSON; + +typedef struct _JSON_PARSE +{ + char *pcKey; + void *pDataBuf; + UINT32 uiBufSize; +}JSON_PARSE; + +#define JSON_NEW_ITEM(pstJson, ret) \ +{ \ + (pstJson) = (VTOY_JSON *)malloc(sizeof(VTOY_JSON)); \ + if (NULL == (pstJson)) \ + { \ + Log("Failed to alloc memory for json.\n"); \ + return (ret); \ + } \ + memset((pstJson), 0, sizeof(VTOY_JSON));\ +} + +VTOY_JSON *vtoy_json_find_item +( + VTOY_JSON *pstJson, + JSON_TYPE enDataType, + const char *szKey +); +int vtoy_json_parse_value +( + char *pcNewStart, + char *pcRawStart, + VTOY_JSON *pstJson, + const char *pcData, + const char **ppcEnd +); +VTOY_JSON * vtoy_json_create(void); +int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData); + +int vtoy_json_scan_parse +( + const VTOY_JSON *pstJson, + UINT32 uiParseNum, + JSON_PARSE *pstJsonParse +); + +int vtoy_json_scan_array +( + VTOY_JSON *pstJson, + const char *szKey, + VTOY_JSON **ppstArrayItem +); + +int vtoy_json_scan_array_ex +( + VTOY_JSON *pstJson, + const char *szKey, + VTOY_JSON **ppstArrayItem +); +int vtoy_json_scan_object +( + VTOY_JSON *pstJson, + const char *szKey, + VTOY_JSON **ppstObjectItem +); +int vtoy_json_get_int +( + VTOY_JSON *pstJson, + const char *szKey, + int *piValue +); +int vtoy_json_get_uint +( + VTOY_JSON *pstJson, + const char *szKey, + UINT32 *puiValue +); +int vtoy_json_get_uint64 +( + VTOY_JSON *pstJson, + const char *szKey, + UINT64 *pui64Value +); +int vtoy_json_get_bool +( + VTOY_JSON *pstJson, + const char *szKey, + UINT8 *pbValue +); +int vtoy_json_get_string +( + VTOY_JSON *pstJson, + const char *szKey, + UINT32 uiBufLen, + char *pcBuf +); +const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey); +int vtoy_json_destroy(VTOY_JSON *pstJson); + +#endif /* __VENTOY_JSON_H__ */ + diff --git a/Ventoy2Disk/Ventoy2Disk/WinDialog.c b/Ventoy2Disk/Ventoy2Disk/WinDialog.c index bec810e8cc5bcea12f86cd4574367a229fe9a688..20f6f66c0473aa1f9286e86b5789348057646fcb 100644 GIT binary patch delta 2900 zcmcImU2Kz87(NYPKT9`1y1$P7m14V&&8`wuT*G9WAd=FN4Tm^z-MV#lTmL(L3<8yq zcwva{NlYY4BoTuFkx;^gkqf;raS}4U5U%EoB8wO^Zirs#d(Qd3wv!>4V48m4`FYR# zKIeVjpKtEZMQ?nx_}oz=nvA808qLPdEX86hnmug%>BX8D>t}<^N6#o5X5;i8W^#kC}PDQqwP3qQE)zP$ID|M(Ab2YcrnyS&Q8`FS!y6<&TJ^U*npWcDxxtPc9*D_ zi1lFdWGQK_#bjbR+%K82HtrLaw#BRQVf-AfoK*44K?_C_t8wU%3fm{On*5t+y>7+M zNADpGQs zu}W)v$4_!T=EE{)F~MqtBM!Cd+74GtOe#ynb@*giP;)|_f*&u`)bx;}I@u0#%4Q<4lP=BoM`R*`B&rsch!V&_ z{BWV}F8e_uB+1WH)^)AuB1rs7+X+LAh~jx17H=Lp?Q9(1_O0yBW9Xw`;S{V9Pp1qS zZ7d0DU|v?lOe7>!f`cREf#c#K zHgWc$r=*I7#N#5GQd~Hvgu|7Q%_Ed^I83{C0yMvX@nR*a9La|?C)hODEzChqDwN1u zl6!XHgnW!j<};QGz2F?w9TFl0Nz&z_h#;y6tGthH{V~?c;v%Pbc5pHV$)&cObm3T@ z)b->~`bq0va#};43KX5LtQX{K_U=5Dvxl}L^CPydWRHf0qjeE2JcyQMDlm7`>6JMe zrPT-_*LImi6QscHr}ZJ>A3NPb&ZNKAAhE9m)7PkQ&bmAF?7yRG&C@wfI-VshY5Kd# zUQnxVJv1LfXS@;@dsVbN*$V$vQ-j2IAIR`&N14I?Kj3*n$5MZvSnyZ80_lCK?iZ@| z)!uH3gjaL1;-Xnp@GBYRzMk)wF(&*$t3yc4cK#=hx~cSZQSvRo(S`@W5o=B=PB&$z8wP(>v##YXpNx9nq$~2~VMFHom6+?Ya|F-m zXYYl8(-an%Wc#vl>(ahM*Ks%d&-1e|JX!*eYR>K-Z*8x$vHhfM3w6LgDmELaE4GV| e6W){kY&9~MBbfT;{JrF|nakcqS;DEIam~j7 delta 402 zcmeBJ#`57O^M*e5%?H?TL`;rr(3#w4$20j!g3#ne4L+0aCCW{H(C~|qbMoH=Q+5Rg zUIs1(&dIik_LFzSs7zj#s4{thB+q04HLuMEO&aV#W%q@|C;KHiO>T=-n!F=PW%8+5 zp2-h{#3lYNwFV|PPM&dDV6sb^$YiDz51@{~l*2$$I@Owyb8~DeH{)b8ABpJ) zI2buL_ocBgf;BBnR|L}+)5E!ehCo~aHcWnUR7Me4?qRy<0xpHuk+R54p(yWj`VC;~b{GgX>vR^%yK1fG0Ln%WMLp(zcLn1>S zLpp;t5L!c>S_`GqfGScMQYYJ|Nls4769U?Iu|9RPU$=|X=9-5QESt41-E#l{Jp_3o diff --git a/VtoyTool/vtoydump.c b/VtoyTool/vtoydump.c index 34c1ee7e..72b78151 100644 --- a/VtoyTool/vtoydump.c +++ b/VtoyTool/vtoydump.c @@ -464,10 +464,13 @@ static int vtoy_check_device(ventoy_os_param *param, const char *device) static int vtoy_print_os_param(ventoy_os_param *param, char *diskname) { - int cnt = 0; + int fd, size; + int cnt = 0; char *path = param->vtoy_img_path; const char *fs; - + char diskpath[256] = {0}; + char sizebuf[64] = {0}; + cnt = vtoy_find_disk_by_size(param->vtoy_disk_size, diskname); debug("find disk by size %llu, cnt=%d...\n", (unsigned long long)param->vtoy_disk_size, cnt); if (1 == cnt) @@ -494,6 +497,37 @@ static int vtoy_print_os_param(ventoy_os_param *param, char *diskname) if (1 == cnt) { + if (strstr(diskname, "nvme") || strstr(diskname, "mmc") || strstr(diskname, "nbd")) + { + snprintf(diskpath, sizeof(diskpath) - 1, "/sys/class/block/%sp2/size", diskname); + } + else + { + snprintf(diskpath, sizeof(diskpath) - 1, "/sys/class/block/%s2/size", diskname); + } + + if (access(diskpath, F_OK) >= 0) + { + debug("get part size from sysfs for %s\n", diskpath); + + fd = open(diskpath, O_RDONLY | O_BINARY); + if (fd >= 0) + { + read(fd, sizebuf, sizeof(sizebuf)); + size = (int)strtoull(sizebuf, NULL, 10); + close(fd); + if ((size != (64 * 1024)) && (size != (8 * 1024))) + { + debug("sizebuf=<%s> size=%d\n", sizebuf, size); + return 1; + } + } + } + else + { + debug("%s not exist \n", diskpath); + } + printf("/dev/%s#%s#%s\n", diskname, fs, path); return 0; } diff --git a/VtoyTool/vtoytool/00/vtoytool_32 b/VtoyTool/vtoytool/00/vtoytool_32 index 6cda86be93f1ce56ab9f7b42c1e1f80f1757bf88..97f553f4eae818f708f339b79ee919227a800089 100644 GIT binary patch delta 11457 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 RUSk*9z^-k!f2sq;{{h&&;zs}g diff --git a/VtoyTool/vtoytool/00/vtoytool_64 b/VtoyTool/vtoytool/00/vtoytool_64 index 1acd940fbd504b3d96245cad850ed9324a800993..53a951e60fcac7ac6e8c1d4fca265c746cfeff26 100644 GIT binary patch delta 15036 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 diff --git a/VtoyTool/vtoytool/00/vtoytool_aa64 b/VtoyTool/vtoytool/00/vtoytool_aa64 index 9a429bc5ffe74bcac6b965e0f08577041e62a41d..11833dce190e2f2dcea9a284f5a2d492aafe364b 100644 GIT binary patch delta 13018 zcmZX43tZGi*Z<7yaupER8~5GiCIWJEML@*mlA5AHnn%(pGZkp0-J^U=YkM8C1nn5A^%i0};>?M@ckh)a9SQAeV@XeZNhEBaEGdEeuizc<# zHN;66*;hx&9>d(?_Aia3RzvI!+X-p01|=((Ghbz5>qfgy?_3WS7vl!YaP7*7uD3;$Wmg&SHe3+cBPXDmuG|BFENZY%Z)S72O$$ z8KDWxwYLcx=ppBo6uDr%^9Cba=MznP@JydlO^R|Kf6OO8sHbAbc)}cKgM0FKee9;5 zN(+RzQ~^5Y^=|g|+si)KRmH=6`-k*YBEUR`kUpG|c0RXI&*%CcxArrT+8Y{@>Y#18 zP4mkdU_LL~2{HoeoO&Nee9JuLfkt zq$$ec%xueJp88RwVx;pwOJAG?!Av3<1(iJ3>6LrJHBJNAY2S^JpeN6xfR; zCGg(@jbY{lQhN#hDOH2jQ`@o7rs5)6L6BUGs#uooZ^agNncz9^1X7V3PzakT1EGrc)MwMeoohBE9@o*E{C9}O<9lyMfI$kGvfcq3%dD!i?7Q6Fc%@e1RGopI=1X<4 znq1{+{-Vwtdj%t<8=b?*DTtCzwdLq~{)MhTIA7-meRf1H^19LaqRij}rK#XU(6T(9 zKd3jx4n(qsC-(UqVq`GOw5N))_f^(%MQ`=%3x)K~6}&;8+3Pc?UCG*V;kPw1WHxz~ z@PvaJGL6=hJY^~rdYT&yeR~X37cNtePBl&t=Cf;vZ9gPLl-gw2H|GB{k1sGJv4KXu z*^sC$PzsIw14CK2d?gi1%cM1*hZ!TQ^A#)l-(k<(n$R;W&T;nkGP3)&hA7uZ)7?m> zsn<2s*;J}kw@IGj`yhSa%zlbI&FeAa%@Ui^K1;jXh?V}iKn6kRZo5U!;jc(}G6>Ae z$?m37f29C;ceOuw0`Ljk?dGo)!t9x1-V-@Um z+LNSe2xO`l(s?3$ce^EzN_f8|l|EoA;mRf#=c^@aUK%4@()8|CeX4p62Jx93k8V$ZLt z9%#?sCW+4v@-QC-@!3K7Q`Qa2uUa!Ge-mg`;U#Sl)I!!1rB?QzFIl4YwN$B*{dc^5 z2UXU~{=bL}!xo}t%3AbSo}J(szl15PWq;H}PrsEZ)eeUEE7L`48W!@sFPT^6 zLn@jP9a72pP|{4uyjIh;D7j?=^f<^z1bR<$M}T6wP}Q~#gDQcuLF1!XjlNvw9KCb? zZMPsuG!~7$;qaesXjc9BFrBdRf%^E9ZSl z*kqh)CsfV|StE|Rh$YD7Lh@eKR&zYdYy}=BP?tHabD8bxtf}g(dwi8&ap+1}MpZ+j zvkV$Gp+tm9k{gS`;JVE|)_o-Ur zrgOS)9{zOmbb;~~eyh=3j`9V|yMb-3^prV`R$1l% zC!-l|B1@k}t1NSX`vo{;oxM0eTS^lBs+Avr^qlTNN-I>ANvuf|q-^Ytj4#uGwyiG& zcq+*@Kcs>nSaY1;*Yv%{7V{wa>iG3a+ zt4AFTpJRXAE(?83RQbExCVfW=QH{LZUr{ho0amJNTjQrH`-Nxao&&E{Rs})IQ{bD5 zNW28Ul>VlUKj*Kk1U?-6GV{ydjsFwKXS?wqVsb*ScEcn_rxC)4&R^NXlVUAFSwTuSiC}eSFYuAEiI(F* zFtE;P0aB)127j1g(-Rn*~~}-(n8~e#o@fSHG}OB=Tof-x?~stw+hS{ z4JK)fEdHW(Eju306XJVP4IdW&Fx}|d7oW~Tvvf)#4!HloQcVeE`&;<6#1TP21_3EY zmhxdqDa=>T7bgv8wL1QBQY^if|Cp4=V)WdcoKF|<@yY3ILN+i9+so)ABXq4!awa@N}uOpwn?nq$miIK*a0K|r!9&Vn)xTT zk@QC%+hA`p++wRud|B@M?4pVH>pPR#V_mQJeT@cb z^-9xIMA-qap3TSSUkI8M0ar4h$akFW&HEQv5*OY6Y+I zaf-a#+>9peNYynrGtB&#(t+WvaSD#Q+9{({x2qw8&Aha1DO~4ZSvi|w=H2dj8~4nE z_tdiEWTTH=xap-`pnc?{1R`pA>x7@p}h9Ngw2&4mQ&t zT)z&UO6g;KpxE%CW_pDGI@FZl53W{Ud%iq?`i@s$;3ELt9<3SQrf_ejeCPW!sm@Q(={$-d>W-j zesF?`_TgVlxDUxOY2us!7jE~`@!#TGCz`_E@;)8B?#+p>@?#UzpzO7YJq!a;;#7u^ zc8hwD=$!IIc!u|yR7!v5^Ck_VfxKbTePL^R$XM}QHk+R$%3&V)KmxtU`#&%-AV-}` z?@Z_0ABd(!{NMuvXf(h5z=r`>)a$Czd5NERFfOXG#FB*bPZs_LXE2ir>gCJDsINJl z9HniXYPWLp_NH&@cJMO6hp}Do5jWGob1dLv65pDkA#7d0Q+>691_x!4Lw3sQ2bDer!rBE3xwTrVf$7Z`#aob);idNeWT^ z4VPX8^pn&6V3qN%#SbOZz>0X~5DHdnN<4pkdOVAW=bucE3rUGrTEM(g7F8AT{O0uO zY)3qw{P0Su<;@S9=^;*M?2@QAW_(Le@~0n(Msj)ek?V{m@Q~+?e9z1oc*={M<&q&b zD~;~v?X$k8^ITue{*K{McmJFjbeqdMx0KT1u7~EC7#+r+Sn#A2yD$MFk``9e75vqO zgXnDj>%xyCKS5z=X>r@eKqq|MW2vjdlI_m>-2^zb=ZVw|T(g(e!`$ z1B+j0ubEY4VwTj>Yy9mcrATHsmW-w?{NAOdbTHqrbg|(`fafi-GcIMwCh^F}pP`Ju z`nbS5TmNM?7L>pjEPH~E=hv3$=n9w5@`7ckT236uYG~QFjPvF)S zI(mWkSy4*Y@}(;>=`_B3#UPUgk$ejt@go+|G^@DbB_x$)*WOt+Ljs3n* zUr|F*_{^x>zYWLppBV5B22|o1j4!E9pf!9;bvb>T|5V)@Ni5c3rdxP_M?b>?A9*7s z?Uix%2BIY+E8|E*GvDAy&|OD0jQjZ86txpJw8lCq!S$hICS^?tuFO?WF#3t>t<^Tl z#wYS0{&$h_@kAwo*z>Cin^mkQ6$^Zc^1 z2I`k<;v~4aCYb?ge(FK=T-8qnGx{3e@U)w5;~Umx(jENZx~cSgZhhuUc!c#C9dB75 z%460argLS_)v{hsQL*|w`z7}1+h^mfdycFrZWy4&jS_dP#V|qBs^SJS+;tfGqh$=^}otrkalB_b4CJL|5?hg5sU(Rh}8}FivlB%e}9a zXZMJvWxSmWdWI{t1Lzn048E`Pu#N9gC;xP#8ENR(jiX(KF9ZiMlFWzhn8}KgdHs%+ zG{cquN`#(W*74Ex^Vv?F>*M+fRNtag$bmuat2Fj@LhS87^tHEVyZSV&rP>*Kn-Z^V-$4|HRK5^e*LFm>W}RF| zSxO|o`N=j-q;iX&snEIJJavMyvPi!8bR3%$$;Ih-RvXFRI~~cVeG}@3Mb%ifQLbN3 z2T-jA8}DlSY=uvxe8!{lXgwR(;l@LSy zS!kSV&<}w=;h)7R>f>6e<;+BE&@$8(Q?KN*H{)C{UGZh?T%7BbpF3#$V6%eqRlX=( zYiH(gJI)b{#t~IFhcwt_wXnqO`tWL~%&6ynONiZL!paGFB0e0{eu$Z7?FyB*PGYml zwB~vDI1~Zn-5J=owQh@#;ht~Z0!LS>479iK z*9>_U{#zW>(x;gFT>U^Fn#$-kG0%_QN52(E{Ae{jSYPZjyPhlA)z z33~_AkJ#Y^@n0X{fP&s*&#Ur;Q@WOjG=>82SHv(xK`S}5G>=X9~nO5@mtcyY*zDZh@d|HVp^HIkr9 zq8Z^orz`tH@FI)otV4}|{^#`i(MgmtT3kP-2fgJ-_tcNbq{k?8^b)3Q`Z7D*OVnr6 z7nnUm+?RvJlw^p{a_AIBGDPp*z>~!KPkPf{lopA08(kY(o1sQ)XHJQ#SNc$!Hprf- zrY9K}f%Rwd=_JPDvqWYg-OTc`>OU!@|MCeLk)>3Aq?JLihg`|5f3AeC31@XZ>rG>5 zK{sYet;gS)`dX?NEM1T}b#~Q)1({Q3&zU+aGikxxoXiD}JpK^L{J$hG<>7}GTIN>G zUucnhmg)27{Er2^(-&B#&zWyYS`bJie#&Fh`xhoHC{mgFCrt|^nbRIx1o;W-zd-WP zlIc|o#a0K^We~KoRfs*bAzpZ&7gl)TI4>Nys{U&ST||d``lM%o!(vwle(&wujGq3L zPI{oHf5F=?kM;C__x4-N^_y2wUrqJ+H6DRgUO3kapYXz3FWlvY2fXl%7yjghpl#ngA#EL+c z!AATk{t?Iu*toyM&w-G&_qK@Z#!8Gqnhy72@0b+8C5({$8nLJw!R2RzwGj_8^wV-uy!( z2eT47Lp&192GByWJDByOdhug0c+VMyF$BD`#h4Ju8zEMOuw-<1hv0u52S+YvPqZ~K!|@ATnun!z-dKsI5Sg+s0zp6 zwc;`KSY3*EA)MtJDtdOfgQNsB8$OpYpjxJ19Y@2-8E% za*?EG=8-!y?hKbYOu~L`0S-#5P2%PPR|A~B3vRU+*SUHNXq$j@h>d!dz-lwZyLy&O zE#jJ<4JdBO`tOwvhQ?Qc8|;-RB~Jp*lHKvQM@y2NeIf9bVxEENiY9? zOBb9JCuLRuzgCPiGM%vwXNsI}5~+NS4c*3dE{N1d&bbj)WMy_|yZgMY0~OuAhjCLOe9} z6D5F%mPeuxY2$6AUaGe7_y%Py2@q2;40(__SBw6B5dGTH4$k{Ff?=PA$n<262j=RbI z55i^LA_8Kth%%8KgIHQ9M#jJ#O+{i=4C1m-yaghAbAULHE^8YgSS;4IL}bS@6PWLf z#n9zqo&*l@Vl2xvT(oz%>mXY;J3dX&ZHD+Zmf<2PtZ~d7Su?Q1eHkgavz6h;Bm+KT zd>l5?Cbq}1zLDv}?)anRluK*{SA@70hq<*Pzzju?4-*Au7Duzhcr(;rEtUeX3&X`` zGd5_PIAR8Gx%f)*ZW;_L6H;BlA|s^*x^2klQc0DV9}gegGhI02u_moJ5f6Q)iFOHy=$XL! z(l;gO?Xx8c|IJLiC^6;YOakl2F3u7miHMSPac?5F?5{_~{6v_*B3?`cQ=vGM$TG7V z=XSU+bTPd&Y7ElpCj8A0*TcV?#M^*hFS3)EDboH}hr9J}qGvCrfgB^MBsptQhg&WJ z=V0j25RXA?K`s|c5+*&c~kfDw$=*w=MIg!{U$;TLWJX82H_{(25Z0@_WJ7woJT}4BM26ufWb4 zmy5t2NF-&VR}WaOW`!8rgXI{@93Aetau}K&pSn1F9OC63*wTw@#ibrB!&>`vhr0;DSJxP4d-Qa4qY?CwFx( za7VlF0p-aa!0!iMj_Yg|{J3l(8eF`@rJgK)l$@aJq%l^cox5;Zz#YB|R|ec(;G_bw z$&DDN;>6fg#MTuqR-_`Yh6wQv$!ekk&^D>d6iP)FP?nl8HoP{N3iF>lxAl7zRJe!6687vNFv3@?QKj?7J z7TG?;3|ZY zjneF|W8!2s%dM{a=#IBYHaRq=CyGYkyLw<2a67x;q$`vGC&x?ColW8=0e=AaPE{pM z;uiwn2z;JIc$!(Aa6JeuU4%nTQU~1SkHpJ4I2~g|a}Lw_Tm~vs{xn5tKfV)zQj(lX zpNeh#NQC!>0T+tm-tfO^VAD!Enq-*`mJ0DmZ;${scvVJx+P+|<-*zri(Ue9XV6KPE>}tS_F?9nI*>ZO zT#lCdt_FE0@KQ==lR9nze#=R*xexNCR-EaBYf*&=%Y}FE`c&lPBCUst@wsYQvN9LM zCE|z#m4fz_0nsQ@`?BQhnll~l8J%JQ<&3j&8E*pK-UVNV->ZT57c2XsHYpb;0E~^_ z-0?!GQ%WY1>Ng@e4>~Or59A?^CW-BNvfjTeKFVY9k$+w8a7$U8P0pvqKBowp4@+MC zw}{GT1KIJbVoE;L+y5)>0NwzVLnQl7uwVHNV!Ghv`>;mM+x#HdeaMfPBvk1v4w{AVSR*R=WW?Qa_cM4b$JJl-wC_q}f+$y5_ zv4Ph8ZQhdFd$^Wy|60G&jlLEPU!5a!+RA<`!C-H{BVBIo0g!B>p&v8%JPOhlpk$M~ z+X7PgtvkLWRoC{G^&l-oL4Ev|$S8y&JO31q7UC5E@YzBXIu>!F5CL=iuMT%5Mszm0 zSUv25>s)_S5h=I-JD%i(Wgy!{aeoBNBr&Ex=BT|bR`o~PTqut9X9@3Zt9|Vf@$P*rSyTJEU7UHD z_7b5Z*xH=6_j;%!hO10uueU$C+|$n*>Y)!G=INihSDYLHw<;eZ0!Olefup@v7`3*3 z!boOh(Vb?w8#aAxSFRaE-DsAj0ZsnS8p$lezk-EER=n=Cku)bg{dyV~n DMO>4A delta 12555 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 gn9V!Z diff --git a/VtoyTool/vtoytool/00/vtoytool_m64e b/VtoyTool/vtoytool/00/vtoytool_m64e index 3fd69195dfbd4e489fa295081ca061da9f6c6ed5..249e1a0c33b90b0e71760feca61ff2ebbe6ca5e0 100644 GIT binary patch delta 21383 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 bJ6VL5X`oPoQn3jjq?BpaHi;K;5&wSy$GLH{ delta 20888 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 G@&6a1h=NQ2