diff --git a/dialog.jai b/dialog.jai index 95b74bc..72d7e6d 100644 --- a/dialog.jai +++ b/dialog.jai @@ -493,6 +493,8 @@ Download_Dialog_State :: struct { thread: Thread; thread_data: Download_Thread_Data; static_hwnd: HWND; + file_size: u64; + is_update: bool; } download_dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #c_call { @@ -508,12 +510,15 @@ download_dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) - hFont := GetStockObject(DEFAULT_GUI_FONT); + operation := ifx state.is_update then "update" else "installation"; + size_text := format_file_size(state.file_size); + status_text := tprint("Downloading Sing-box core %...\nDownload size: %\nThis may take a moment, please wait.", operation, size_text); state.static_hwnd = CreateWindowExW( 0, utf8_to_wide("STATIC"), - utf8_to_wide("Downloading Sing-box core executable...\nThis may take a moment, please wait."), + utf8_to_wide(status_text), WS_CHILD | WS_VISIBLE | SS_LEFT, - 30, 30, 400, 50, + 30, 25, 400, 65, hwnd, null, null, @@ -566,7 +571,7 @@ download_dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) - } } -show_download_progress_dialog :: (parent_hwnd: HWND, url: string, dest_path: string) -> bool, string { +show_download_progress_dialog :: (parent_hwnd: HWND, url: string, dest_path: string, file_size: u64, is_update: bool) -> bool, string { hInstance := GetModuleHandleW(null); class_name := utf8_to_wide("Sing-boxDownloadDialogClass"); @@ -592,11 +597,15 @@ show_download_progress_dialog :: (parent_hwnd: HWND, url: string, dest_path: str state: Download_Dialog_State; state.thread_data.url = copy_string(url); state.thread_data.dest_path = copy_string(dest_path); + state.file_size = file_size; + state.is_update = is_update; + + dialog_title := ifx is_update then "Updating Sing-box Core" else "Installing Sing-box Core"; dialog_hwnd := CreateWindowExW( WS_EX_DLGMODALFRAME, class_name, - utf8_to_wide("Downloading Sing-box Core"), + utf8_to_wide(dialog_title), WS_POPUP | WS_CAPTION, dialog_x, dialog_y, dialog_w, dialog_h, parent_hwnd, @@ -638,4 +647,3 @@ show_download_progress_dialog :: (parent_hwnd: HWND, url: string, dest_path: str return state.thread_data.success, state.thread_data.error_msg; } - diff --git a/main.jai b/main.jai index 8bcae37..f8dcf29 100644 --- a/main.jai +++ b/main.jai @@ -727,8 +727,23 @@ install_or_update_singbox_core :: (app: *App_State, is_update: bool) -> bool { prompt_title := ifx is_update then "Update Sing-box" else "Sing-box Core Installation"; success_msg := tprint("Sing-box core % successfully!", ifx is_update then "updated" else "installed"); + file_size, size_ok, size_err := get_remote_file_size(download_url); + if !size_ok { + msg := tprint("Could not determine the Sing-box download size.\nError: %\n\nPlease ensure you have an active internet connection.", size_err); + MessageBoxW(app.hwnd, utf8_to_wide(msg), utf8_to_wide(prompt_title), MB_ICONERROR); + return false; + } + + size_text := format_file_size(file_size); + action := ifx is_update then "update Sing-box" else "download and install Sing-box"; + prompt := tprint("Version: %\nDownload size: %\n\nWould you like to % now?", version_to_download, size_text, action); + response := MessageBoxW(app.hwnd, utf8_to_wide(prompt), utf8_to_wide(prompt_title), MB_YESNO | MB_ICONQUESTION); + if response != IDYES { + return false; + } + // 1. Download while VPN is still active - success, err_msg := show_download_progress_dialog(app.hwnd, download_url, temp_zip); + success, err_msg := show_download_progress_dialog(app.hwnd, download_url, temp_zip, file_size, is_update); if !success { msg := tprint("Failed to download Sing-box core.\nError: %\n\nPlease ensure you have an active internet connection.", err_msg); MessageBoxW(app.hwnd, utf8_to_wide(msg), utf8_to_wide("Sing-box Tray"), MB_ICONERROR); @@ -926,13 +941,8 @@ start_singbox :: (app: *App_State) -> bool { exe_path := tprint("%\\sing-box.exe", global_userspace_dir); if !file_exists(exe_path) { - response := MessageBoxW(app.hwnd, utf8_to_wide("Sing-box core executable (sing-box.exe) was not found in your userspace folder.\n\nWould you like to download and install it now?"), utf8_to_wide("Sing-box Tray"), MB_YESNO | MB_ICONQUESTION); - if response == IDYES { - installed := install_or_update_singbox_core(app, false); - return installed; // install_or_update_singbox_core handles starting sing-box - } else { - return false; - } + installed := install_or_update_singbox_core(app, false); + return installed; // install_or_update_singbox_core handles prompting and starting sing-box } diff --git a/updater.jai b/updater.jai index bbd32ab..72a8174 100644 --- a/updater.jai +++ b/updater.jai @@ -12,6 +12,76 @@ Updater_Thread_Data :: struct { is_test_mode: bool; } +get_remote_file_size :: (url: string) -> u64, bool, string { + hInternet := InternetOpenW( + utf8_to_wide("Sing-boxTrayDownloader"), + INTERNET_OPEN_TYPE_PRECONFIG, + null, + null, + 0 + ); + if !hInternet { + return 0, false, "Failed to initialize WinINet session"; + } + defer InternetCloseHandle(hInternet); + + flags: DWORD = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE; + if starts_with(url, "https") { + flags |= INTERNET_FLAG_SECURE; + } + + headers := "User-Agent: Sing-boxTray\r\n"; + hUrl := InternetOpenUrlW( + hInternet, + utf8_to_wide(url), + utf8_to_wide(headers), + cast(u32) headers.count, + flags, + 0 + ); + if !hUrl { + err := GetLastError(); + return 0, false, tprint("Failed to open URL (Win32 Error: %)", err); + } + defer InternetCloseHandle(hUrl); + + response_code: DWORD; + response_code_size: DWORD = size_of(type_of(response_code)); + if !HttpQueryInfoW(hUrl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, *response_code, *response_code_size, null) { + err := GetLastError(); + return 0, false, tprint("Failed to query HTTP status code (Win32 Error: %)", err); + } + if response_code != 200 { + return 0, false, tprint("HTTP request failed with status code %", response_code); + } + + content_length: DWORD; + content_length_size: DWORD = size_of(type_of(content_length)); + if !HttpQueryInfoW(hUrl, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, *content_length, *content_length_size, null) { + err := GetLastError(); + return 0, false, tprint("Failed to query download size (Win32 Error: %)", err); + } + + return cast(u64) content_length, true, ""; +} + +format_file_size :: (bytes: u64) -> string { + mib: u64 = 1024 * 1024; + kib: u64 = 1024; + + if bytes >= mib { + whole := bytes / mib; + tenths := (bytes % mib) * 10 / mib; + return tprint("%.% MiB", whole, tenths); + } + if bytes >= kib { + whole := bytes / kib; + tenths := (bytes % kib) * 10 / kib; + return tprint("%.% KiB", whole, tenths); + } + return tprint("% bytes", bytes); +} + download_url :: (url: string) -> string, bool, string { log_info("Downloading config from URL: %\n", url); @@ -432,4 +502,3 @@ download_file :: (url: string, dest_path: string) -> bool, string { return true, ""; } - diff --git a/win32.jai b/win32.jai index b4db5c7..2cefaaa 100644 --- a/win32.jai +++ b/win32.jai @@ -259,6 +259,7 @@ INTERNET_FLAG_SECURE :: 0x00800000; INTERNET_FLAG_NO_CACHE_WRITE :: 0x04000000; HTTP_QUERY_STATUS_CODE :: 19; +HTTP_QUERY_CONTENT_LENGTH :: 5; HTTP_QUERY_FLAG_NUMBER :: 0x20000000; InternetOpenW :: ( @@ -376,4 +377,3 @@ RGB :: (r: u32, g: u32, b: u32) -> u32 { -