-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmain.cpp
More file actions
683 lines (610 loc) · 20.9 KB
/
main.cpp
File metadata and controls
683 lines (610 loc) · 20.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
/*
* [sfd_tool] - Low-level device communication module
*
* This module contains code that works with certain UNISOC bootrom behaviors.
* The implementation is based on publicly disclosed information from 2025.
*
* USE RESTRICTION: This tool is designed for legal device maintenance only.
* The author does not authorize its use for any illegal purpose, including
* but not limited to unlocking stolen devices or circumventing paid services.
*
* Users assume all legal responsibility for their usage.
*/
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include "common.h"
#include "main.h"
#include "ui/GtkWidgetHelper.hpp"
#include "i18n.h"
#include "ui/ui_common.h"
#include "pages/page_connect.h"
#include "pages/page_partition.h"
#include "pages/page_manual.h"
#include "pages/page_advanced_op.h"
#include "pages/page_advanced_set.h"
#include "pages/page_debug.h"
#include "pages/page_about.h"
#include "pages/page_log.h"
#include "pages/page_pac_flash.h"
#include <thread>
#include <chrono>
#include <gtk/gtk.h>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include "version.h"
#include "core/config_service.h"
#ifdef __linux__
#include <unistd.h>
#include <execinfo.h>
#include <limits.h>
#include <sys/stat.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
#include <dbghelp.h>
#include <sys/stat.h>
#endif
#ifdef _WIN32
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#endif
std::string g_about_text;
namespace {
static std::string get_effective_lc_all_from_ui_language(const std::string& ui_language) {
if (ui_language.empty() || ui_language == "auto") {
return std::string();
}
if (ui_language == "zh_CN") {
#ifndef _WIN32
return "zh_CN.UTF-8";
#else
return "zh_CN";
#endif
}
if (ui_language == "en_US") {
#ifndef _WIN32
return "en_US.UTF-8";
#else
return "en_US";
#endif
}
// 未知值:退回系统默认
return std::string();
}
static std::string get_executable_dir() {
#if defined(__linux__)
char path[PATH_MAX] = {0};
ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
if (len <= 0) {
return std::string();
}
path[len] = '\0';
std::string p(path);
auto pos = p.find_last_of('/');
if (pos == std::string::npos) {
return std::string();
}
return p.substr(0, pos);
#elif defined(__APPLE__)
char path[PATH_MAX] = {0};
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) != 0) {
return std::string();
}
std::string p(path);
auto pos = p.find_last_of('/');
if (pos == std::string::npos) {
return std::string();
}
return p.substr(0, pos);
#elif defined(_WIN32)
wchar_t path[MAX_PATH] = {0};
DWORD len = GetModuleFileNameW(NULL, path, MAX_PATH);
if (len == 0 || len == MAX_PATH) {
return std::string();
}
char utf8_path[MAX_PATH] = {0};
int utf8_len = WideCharToMultiByte(CP_UTF8, 0, path, len,
utf8_path, sizeof(utf8_path) - 1,
NULL, NULL);
if (utf8_len <= 0) {
return std::string();
}
utf8_path[utf8_len] = '\0'; // 确保终止
std::string p(utf8_path);
// 支持 \ 和 / 两种分隔符
size_t pos = p.find_last_of("\\/");
if (pos == std::string::npos) {
return std::string();
}
return p.substr(0, pos);
#else
return std::string();
#endif
}
static bool dir_exists(const std::string& path) {
#if defined(__linux__) || defined(__APPLE__)
struct stat st;
return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
#elif defined(_WIN32)
// 将 UTF-8 路径转换为 UTF-16
int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, nullptr, 0);
if (wlen <= 0) return false;
std::vector<wchar_t> wpath(wlen);
MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, wpath.data(), wlen);
DWORD attr = GetFileAttributesW(wpath.data());
return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
#else
(void)path;
return false;
#endif
}
static std::string choose_locale_dir() {
std::string exe_dir = get_executable_dir();
#if defined(__APPLE__)
g_is_macos_bundle = (!exe_dir.empty() && exe_dir.find(".app/Contents/MacOS") != std::string::npos);
#endif
#if defined(__APPLE__)
// macOS .app Bundle: 优先从 Contents/Resources/locale 查找
if (!exe_dir.empty()) {
// exe_dir 形如 /.../SFD Tool.app/Contents/MacOS
std::string bundle_locale = exe_dir + "/../Resources/locale";
if (dir_exists(bundle_locale)) {
return bundle_locale;
}
}
#endif
if (!exe_dir.empty()) {
std::string exe_locale = exe_dir + "/locale";
if (dir_exists(exe_locale)) {
return exe_locale;
}
}
if (dir_exists("./locale")) {
return "./locale";
}
// 都不存在时,返回空字符串,让 gettext 使用系统默认路径
return std::string();
}
static std::string load_about_from_path(const std::string& path) {
std::ifstream in(path);
if (!in) {
return std::string();
}
std::ostringstream ss;
ss << in.rdbuf();
return ss.str();
}
static std::string load_installed_about_text() {
// 1) 优先使用编译期指定的文档目录(Linux 安装包)
#ifdef SFD_TOOL_DOC_DIR
{
std::string doc_path = std::string(SFD_TOOL_DOC_DIR) + "/VERSION_LOG.md";
std::string content = load_about_from_path(doc_path);
if (!content.empty()) {
return content;
}
}
#endif
// 2) 其次使用可执行文件所在目录(macOS DMG、Windows 目录运行等)
std::string exe_dir = get_executable_dir();
#if defined(__APPLE__)
g_is_macos_bundle = (!exe_dir.empty() && exe_dir.find(".app/Contents/MacOS") != std::string::npos);
#endif
if (!exe_dir.empty()) {
{
std::string path = exe_dir + "/VERSION_LOG.md";
std::string content = load_about_from_path(path);
if (!content.empty()) {
return content;
}
}
{
std::string path = exe_dir + "/docs/VERSION_LOG.md";
std::string content = load_about_from_path(path);
if (!content.empty()) {
return content;
}
}
#if defined(__APPLE__)
// 预留给 .app Bundle 的资源路径:Contents/MacOS/../Resources
{
std::string path = exe_dir + "/../Resources/VERSION_LOG.md";
std::string content = load_about_from_path(path);
if (!content.empty()) {
return content;
}
}
#endif
}
return std::string();
}
} // namespace
std::string load_about_text() {
// 1) 开发环境:当前工作目录中的相对路径
const char* candidates[] = {
"docs/VERSION_LOG.md",
"VERSION_LOG.md",
};
for (auto path : candidates) {
std::ifstream in(path);
if (in) {
std::ostringstream ss;
ss << in.rdbuf();
return ss.str();
}
}
// 2) 已安装环境:使用编译期/可执行文件路径推导出的版本记录文件
std::string installed = load_installed_about_text();
if (!installed.empty()) {
return installed;
}
// 3) 仍然找不到时,回退到原有提示
return "SFD Tool GUI\n\nBy Ryan Crepa\n\nAbout information file missing.\n";
}
const char *Version = "[1.2.3.0@_250726]";
AppState g_app_state; // 全局应用状态实例
int& m_bOpened = g_app_state.device.m_bOpened;
int fdl1_loaded = 0;
int fdl2_executed = 0;
int isKickMode = 0;
int& selected_ab = g_app_state.flash.selected_ab;
int no_fdl_mode = 0;
uint64_t fblk_size = 0;
uint64_t g_spl_size;
extern bool isUseCptable;
const char* o_exception;
int init_stage = -1;
int& device_stage = g_app_state.device.device_stage;
int& device_mode = g_app_state.device.device_mode;
//sfd_tool protocol
char** str2;
char mode_str[256];
int in_quote;
char* temp;
char str1[(ARGC_MAX - 1) * ARGV_LEN];
spdio_t*& io = g_app_state.transport.io;
int ret;
int conn_wait = 30 * REOPEN_FREQ;
int keep_charge = 1, end_data = 0, blk_size = 0, skip_confirm = 1, highspeed = 0, cve_v2 = 0;
int g_default_blk_size = 0;
int nand_info[3];
int argcount = 0, stage = -1, nand_id = DEFAULT_NAND_ID;
unsigned exec_addr = 0, baudrate = 0;
int bootmode = -1, at = 0, async = 1;
int waitFDL1 = -1;
//Set up environment
#if !USE_LIBUSB
extern DWORD curPort;
DWORD* ports;
//Channel9 init(Windows platform)
#else
//libsub init(Linux/Android-termux)
extern libusb_device* curPort;
libusb_device** ports;
#endif
// Moved initialization into gtk_kmain()
#ifdef __linux__
void check_root_permission(GtkWidgetHelper helper) {
if (geteuid() != 0) {
// not root
showWarningDialog(GTK_WINDOW(helper.getWidget("main_window")), _(_(_("Warning"))), _("You are running this tool without root permission!\nIt may cause device connecting issue\nRecommanded to open this tool with root permission!\n\nsudo -E /path/to/sfd_tool"));
}
}
#endif
bool isCrashed = false;
void crash_handler(int sig) {
(void)sig;
if (isCrashed) return;
isCrashed = true;
#ifdef __linux__
void* array[20];
size_t size;
// 获取回溯信息
size = backtrace(array, 20);
// 打印错误信息
fprintf(stderr, "Error: signal %d:\n", sig);
// 打印堆栈
backtrace_symbols_fd(array, size, STDERR_FILENO);
#elif defined(_WIN32)
fprintf(stderr, "Error: signal %d:\n", sig);
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for (int i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)stack[i], 0, symbol);
printf("%i: %s - 0x%0llX\n", frames - i - 1, symbol->Name, symbol->Address);
}
free(symbol);
#endif
if (isHelperInit) {
// 检测当前是否在主线程
bool is_main_thread = g_main_context_is_owner(g_main_context_default());
if (is_main_thread) {
// 主线程中直接执行,无需等待(因为没有异步)
// 等待窗口拖动结束
while (isWindowDragging(helper.getWidget("main_window") ? GTK_WINDOW(helper.getWidget("main_window")) : nullptr)) {
g_main_context_iteration(g_main_context_default(), FALSE);
g_usleep(10000); // 10ms
}
// 禁用控件并显示对话框
DisableWidgets(helper);
GtkWidget* main_window = helper.getWidget("main_window");
if (main_window) {
showErrorDialog(GTK_WINDOW(main_window),
_("Program Crash"),
_("The program encountered an unhandled exception, which may be caused by device connection issues or a bug in the program.\n\nIt is recommended to check the device connection, ensure the correct options are used, and try running the tool again."));
}
} else {
gui_idle_call_wait_drag([](){ DisableWidgets(helper); }, GTK_WINDOW(helper.getWidget("main_window")));
showErrorDialogSyncInThread(GTK_WINDOW(helper.getWidget("main_window")),
_("Program Crash"),
_("The program encountered an unhandled exception, which may be caused by device connection issues or a bug in the program.\n\nIt is recommended to check the device connection, ensure the correct options are used, and try running the tool again."));
}
} else {
// 命令行模式
fprintf(stderr, "Program crashed. Exiting...\n");
#ifndef _WIN32
std::this_thread::sleep_for(std::chrono::seconds(3));
std::exit(EXIT_FAILURE);
#else
system("pause");
std::exit(EXIT_FAILURE);
#endif
}
// 4. 等待用户阅读错误信息
std::this_thread::sleep_for(std::chrono::seconds(3));
// 5. 强制退出
std::exit(EXIT_FAILURE);
}
// 全局快捷键处理:在 macOS 上支持 Command+Q,其他平台支持 Ctrl+Q 退出
static gboolean on_main_window_key_press(GtkWidget* widget, GdkEventKey* event, gpointer user_data) {
(void)widget;
(void)user_data;
#if defined(__APPLE__)
if ((event->state & GDK_META_MASK) && event->keyval == GDK_KEY_q) {
gtk_main_quit();
return TRUE;
}
#else
if ((event->state & GDK_CONTROL_MASK) && event->keyval == GDK_KEY_q) {
gtk_main_quit();
return TRUE;
}
#endif
return FALSE;
}
//fdl exec
std::string fdl1_path_json;
std::string fdl2_path_json;
uint32_t fdl1_addr_json;
uint32_t fdl2_addr_json;
int gtk_kmain(int argc, char** argv) {
DEG_LOG(I, "Starting GUI mode...");
gtk_init(&argc, &argv);
g_about_text = load_about_text();
// Initialization previously at file scope
io = spdio_init(0);
#if USE_LIBUSB
ret = libusb_init(nullptr);
if (ret < 0) ERR_EXIT("libusb_init failed: %s\n", libusb_error_name(ret));
#else
io->handle = createClass();
call_Initialize(io->handle);
#endif
snprintf(fn_partlist, sizeof(fn_partlist), "partition_%lld.xml", (long long)time(nullptr));
#if defined(__APPLE__)
// macOS: 如果通过 .app Bundle 启动,默认将备份文件保存到 ~/Documents/sfd_tool
{
std::string exe_dir = get_executable_dir();
#if defined(__APPLE__)
g_is_macos_bundle = (!exe_dir.empty() && exe_dir.find(".app/Contents/MacOS") != std::string::npos);
#endif
if (!exe_dir.empty() && exe_dir.find(".app/Contents/MacOS") != std::string::npos) {
const char* home = std::getenv("HOME");
if (home && *home) {
std::string docs_dir = std::string(home) + "/Documents/sfd_tool";
struct stat st{};
if (stat(docs_dir.c_str(), &st) != 0) {
// 目录不存在则尝试创建,失败时静默忽略,退回到当前工作目录策略
mkdir(docs_dir.c_str(), 0755);
}
if (stat(docs_dir.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
if (docs_dir.size() < sizeof(savepath)) {
std::snprintf(savepath, sizeof(savepath), "%s", docs_dir.c_str());
DEG_LOG(I, "macOS bundle detected, savepath set to %s", savepath);
}
}
}
}
}
#endif
// Window Setup
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "SFD Tool GUI By Ryan Crepa");
// 根据屏幕分辨率自适应默认窗口大小,给小屏幕留出边距
GdkScreen* screen = gdk_screen_get_default();
int screen_w = 0;
int screen_h = 0;
#if GTK_CHECK_VERSION(3, 22, 0)
GdkDisplay* display = gdk_display_get_default();
if (display) {
GdkMonitor* primary = gdk_display_get_primary_monitor(display);
if (primary) {
GdkRectangle geometry{};
gdk_monitor_get_geometry(primary, &geometry);
screen_w = geometry.width;
screen_h = geometry.height;
}
}
#else
if (screen) {
screen_w = gdk_screen_get_width(screen);
screen_h = gdk_screen_get_height(screen);
}
#endif
const int target_w = 1174;
const int target_h = 820;
const int margin_w = 100;
int win_w = target_w;
int win_h = target_h;
if (screen_w > 0) {
win_w = std::min(target_w, screen_w - margin_w);
}
if (screen_h > 0) {
win_h = (screen_h < target_h) ? screen_h : target_h;
}
// 再兜底确保窗口不会太小
if (win_w < 800) win_w = 800;
if (win_h < 600) win_h = 600;
gtk_window_set_default_size(GTK_WINDOW(window), win_w, win_h);
// 启用键盘事件(用于快捷键)
gtk_widget_add_events(window, GDK_KEY_PRESS_MASK);
g_signal_connect(window, "key-press-event", G_CALLBACK(on_main_window_key_press), NULL);
// 设置关闭信号
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 创建主网格布局
GtkWidget* mainGrid = gtk_grid_new();
// 创建 GtkWidgetHelper
helper = GtkWidgetHelper(window);
isHelperInit = true;
helper.setParent(window, LayoutType::GRID);
helper.addWidget("main_window", window);
initDragDetection(GTK_WINDOW(window));
// 创建Notebook(标签页控件)
GtkWidget* notebook = helper.createNotebook("main_notebook", 0, 0, 1174, 0);
{
// ========== 模块化页面创建 ==========
create_connect_page(helper, notebook);
create_partition_page(helper, notebook);
create_manual_page(helper, notebook);
create_advanced_op_page(helper, notebook);
create_pac_flash_page(helper, notebook);
create_advanced_set_page(helper, notebook);
create_debug_page(helper, notebook);
create_log_page(helper, notebook);
create_about_page(helper, notebook);
// ========== 底部控制栏 ==========
GtkWidget* bottomContainer = create_bottom_controls(helper);
// Add notebook and bottom container to main grid
gtk_grid_attach(GTK_GRID(mainGrid), notebook, 0, 0, 10, 1);
gtk_grid_attach(GTK_GRID(mainGrid), bottomContainer, 0, 1, 10, 1);
// 创建CSS样式
GtkCssProvider* provider = gtk_css_provider_new();
const gchar* css =
"label.big-label { font-size: 20px; }"
"progressbar { min-height: 9px; }"
"#wait_con_no_arrow button { min-width: 0px; padding: 0px; border: none; background: transparent; -gtk-icon-source: none; color: transparent; opacity: 0; }"
"#wait_con_no_arrow entry, #wait_con_no_arrow spinbutton, #wait_con_no_arrow text, #wait_con_no_arrow * { background: transparent; box-shadow: none; border: none; }";
gtk_css_provider_load_from_data(provider, css, -1, NULL);
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
gtk_container_add(GTK_CONTAINER(window), mainGrid);
// 显示所有组件
gtk_widget_show_all(window);
// 强制默认选中第一个标签页(“连接”页)
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
// ========== 模块化信号绑定 ==========
bind_connect_signals(helper, argc, argv);
bind_partition_signals(helper);
bind_manual_signals(helper);
bind_advanced_op_signals(helper);
bind_pac_flash_signals(helper);
bind_advanced_set_signals(helper);
bind_debug_signals(helper);
bind_log_signals(helper);
bind_bottom_signals(helper, bottomContainer);
}
DisableWidgets(helper);
// 启动GTK主循环
gtk_main();
return 0;
}
int main(int argc, char** argv) {
// 读取配置并根据 ui_language 设置 gettext 语言
sfd::AppConfig cfg;
sfd::loadAppConfigOrDefault(cfg); // 即使失败也会填充默认值(含 ui_language)
// 用于调试:记录当前配置语言
LOG_INFO("ui_language at startup: %s", cfg.ui_language.c_str());
bool locale_from_config = false;
#if defined(__linux__) || defined(__APPLE__)
if (cfg.ui_language == "zh_CN") {
setenv("LANGUAGE", "zh_CN", 1);
} else if (cfg.ui_language == "en_US") {
setenv("LANGUAGE", "en_US", 1);
}
// "auto" 或空字符串:不设置 LANGUAGE,沿用环境/系统默认
#elif defined(_WIN32)
// Windows 上也设置 LANGUAGE 环境变量,libintl 会读取它来决定翻译语言
if (cfg.ui_language == "zh_CN") {
_putenv_s("LANGUAGE", "zh_CN");
} else if (cfg.ui_language == "en_US") {
_putenv_s("LANGUAGE", "en_US");
}
LOG_INFO("LANGUAGE on Windows after config: %s",
std::getenv("LANGUAGE") ? std::getenv("LANGUAGE") : "(null)");
// 同时设置 C 运行时 locale,便于 std::locale / C API 使用
std::string lc_all = get_effective_lc_all_from_ui_language(cfg.ui_language);
if (!lc_all.empty() && lc_all != "auto") {
LOG_INFO("setlocale(LC_ALL, %s) on Windows", lc_all.c_str());
setlocale(LC_ALL, lc_all.c_str());
locale_from_config = true;
} else {
LOG_INFO("ui_language is auto/empty, using system default locale");
}
#endif
// 如果没有通过配置显式设置 locale,则调用一次 setlocale(LC_ALL, ""),
// 让 C 库从环境变量或系统默认解析 locale。
if (!locale_from_config) {
setlocale(LC_ALL, "");
LOG_INFO("effective locale after setlocale(\"\"): %s", setlocale(LC_ALL, nullptr));
} else {
LOG_INFO("effective locale after config locale: %s", setlocale(LC_ALL, nullptr));
}
// 根据可执行文件路径选择 locale 目录
std::string locale_dir = choose_locale_dir();
LOG_INFO("chosen locale_dir: %s", locale_dir.c_str());
if (!locale_dir.empty()) {
// 开发 / 便携包:使用 exe 同目录或 ./locale
bindtextdomain("sfd_tool", locale_dir.c_str());
}
// 如果 locale_dir 为空:不调用 bindtextdomain,
// 让 gettext 使用系统默认路径(通常是 /usr/share/locale)
textdomain("sfd_tool");
bind_textdomain_codeset("sfd_tool", "UTF-8");
signal(SIGSEGV, crash_handler); // 段错误
signal(SIGABRT, crash_handler); // 断言失败
signal(SIGFPE, crash_handler); // 浮点异常
signal(SIGILL, crash_handler); // 非法指令
// 解决Windows OneDrive的按需同步导致的文件(夹)访问错误
#ifdef _WIN32
g_setenv("GTK_USE_PORTAL", "1", TRUE);
g_setenv("GIO_USE_VFS", "local", TRUE);
g_setenv("GIO_USE_VOLUME_MONITOR", "win32", TRUE);
#endif
if (argc > 1 && !strcmp(argv[1], "--no-gui")) {
// Call the console version of main
return main_console(argc - 1, argv + 1); // Skip the first argument
} else {
return gtk_kmain(argc, argv);
}
}