local bl_target_arch = {
	["risv32"] = {
		define = "-D__riscv=1 -D__riscv_mul=1 -D__riscv_div=1 -D__riscv_muldiv=1 -D__riscv_xlen=32 -D__riscv_compressed=1 -D__riscv_cmodel_medlow=1 -D__riscv_float_abi_soft=1";
		gdb_type = "riscv64-unknown-elf-gdb";
	};
};

local bl_target_map = {
	["bl602"] = table.clone(bl_target_arch["risv32"]);
	["bl616"] = table.clone(bl_target_arch["risv32"]);
	["bl702"] = table.clone(bl_target_arch["risv32"]);
	["bl808"] = table.clone(bl_target_arch["risv32"]);
};

local function find_bouffalo_toolsets(root)
	local tool_env = nil;
	local bin = nil;
	local openocd_root = nil;
	local cmake_path = nil;
	local cmake_ver = nil;
	local ninja_path = nil;
	local bl_targets = {};
	local toolchain_dir = utils:list_dir(make_path(root, "tools/toolchain*"));
	if root == nil or toolchain_dir == nil or
		utils:file_time(root) == nil or
		utils:file_time(make_path(root, "components")) == nil or
		utils:file_time(make_path(root, "cmake")) == nil or
		utils:file_time(make_path(root, "drivers")) == nil or
		utils:file_time(make_path(root, "tools")) == nil or
		utils:file_time(make_path(root, "bsp/board")) == nil
	then
		return nil;
	end;
	local bl602dk = make_path(root, "bsp/board/bl602dk");
	local bl616dk = make_path(root, "bsp/board/bl616dk");
	local bl702dk = make_path(root, "bsp/board/bl702dk");
	local bl808dk = make_path(root, "bsp/board/bl808dk");
	local toolchain_root = make_path(root, "tools", toolchain_dir);
	bl602dk = utils:file_time(bl602dk) and bl602dk;
	bl616dk = utils:file_time(bl616dk) and bl616dk;
	bl702dk = utils:file_time(bl702dk) and bl702dk;
	bl808dk = utils:file_time(bl808dk) and bl808dk;
	if not (bl602dk or bl616dk or bl702dk or bl808dk) then
		-- find no develop kits
		return nil;
	end;

	local bl_gpp = make_path(toolchain_root, "bin/riscv64-unknown-elf-g++.exe");
	local bl_gcc = make_path(toolchain_root, "bin/riscv64-unknown-elf-gcc.exe");
	local bl_gcc_ver = utils:list_dir(make_path(toolchain_root, "libexec/gcc/riscv64-unknown-elf/*"));
	local bl_gdb = make_path(toolchain_root, "bin/riscv64-unknown-elf-gdb.exe");
	local bl_openocd = make_path(root, "tools/openocd/openocd.exe");
	local bl_ninja = make_path(root, "tools/ninja/ninja.exe");
	local bl_make = make_path(root, "tools/make/make.exe");
	local bl_cmake = make_path(root, "tools/cmake/bin/cmake.exe");
	local bl_BLFlashCommand = make_path(root, "tools/bflb_tools/bouffalo_flash_cube/BLFlashCommand.exe");
	local bl_fw_post_proc = make_path(root, "tools/bflb_tools/bflb_fw_post_proc/bflb_fw_post_proc.exe");
	
	if utils:file_time(bl_gpp) == nil then
		print("Can't find G++ for Bouffalo SDK!",bl_gpp);
		return nil;
	end;
	
	if utils:file_time(bl_gcc) == nil then
		print("Can't find GCC for Bouffalo SDK!");
		return nil;
	end;
	
	if utils:file_time(bl_gdb) == nil then
		print("Can't find GDB for Bouffalo SDK!");
		return nil;
	end;
	
	if utils:file_time(bl_ninja) == nil then
		print("Can't find Ninja for Bouffalo SDK!");
	end;
	
	if utils:file_time(bl_make) == nil then
		print("Can't find Make for Bouffalo SDK!");
		return nil;
	end;
	
	if utils:file_time(bl_cmake) == nil then
		print("Can't find CMake for Bouffalo SDK!");
		return nil;
	end;
	
	if utils:file_time(bl_BLFlashCommand) == nil then
		print("Can't find BLFlashCommand tool for Bouffalo SDK!");
		return nil;
	end;
	
	if utils:file_time(bl_fw_post_proc) == nil then
		print("Can't find fw_post_proc tool for Bouffalo SDK!");
		return nil;
	end;

	local base_include = {
		make_path(toolchain_root, "lib/gcc/riscv64-unknown-elf/"..bl_gcc_ver.."/include-fixed");
		make_path(toolchain_root, "lib/gcc/riscv64-unknown-elf/"..bl_gcc_ver.."/include");
		make_path(toolchain_root, "riscv64-unknown-elf/include/c++/"..bl_gcc_ver);
		make_path(toolchain_root, "riscv64-unknown-elf/include");
	};
	if bl602dk then
		bl_targets["bl602"] = table.clone(
			{
				["root"] = root;
				["name"] = "bl602";
				["version"] = "";
				["include"] = table.clone(base_include,{bl602dk});
			},
			bl_target_map["bl602"]
		);
	end;
	if bl616dk then
		bl_targets["bl616"] = table.clone(
			{
				["root"] = root;
				["name"] = "bl616";
				["version"] = "";
				["include"] = table.clone(base_include,{bl616dk});
			},
			bl_target_map["bl616"]
		);
	end;
	if bl702dk then
		bl_targets["bl702"] = table.clone(
			{
				["root"] = root;
				["name"] = "bl702";
				["version"] = "";
				["include"] = table.clone(base_include,{bl702dk});
			},
			bl_target_map["bl702"]
		);
	end;
	if bl808dk then
		bl_targets["bl808"] = table.clone(
			{
				["root"] = root;
				["name"] = "bl808";
				["version"] = "";
				["include"] = table.clone(base_include,{bl808dk});
			},
			bl_target_map["bl808"]
		);
	end;

	tool_env = {
		["BL_SDK_BASE"] = root;
	};
	local debug_adapter = toolset_make_debug_adapter({"T-Head DebugServer"});
	local bouffalo_toolset = {
		["type"] = "bouffalo_sdk";
		["sdk_type"] = "bouffalo_sdk";
		["name"] = "bouffalo_sdk";
		-- ["version"] = string.format("%u.%u.%u", esp_ver_major, esp_ver_minor, esp_ver_patch);
		-- ["version_number"] = esp_ver_major*0x100000 + esp_ver_minor*0x1000 + esp_ver_patch;
		["home"] = make_path(root);
		["repo"] = repo;
		["envs"] = tool_env;
		["bin"] = {
			make_path(toolchain_root, "bin");
			make_path(bl_cmake, "..");
			make_path(bl_make, "..");
			make_path(bl_ninja, "..");
			make_path(bl_openocd, "..");
			make_path(bl_BLFlashCommand, "..");
			make_path(bl_fw_post_proc, "..");
		};
		["include"] = {};
		["lib"] = {};
		["targets"] = table.keys(bl_targets);
		["tools"] = {
			["debugger"] = "t-head";
			["openocd"] = bl_openocd;
			["cmake"] = bl_cmake;
			["make"] = bl_make;
			["ninja"] = ninja_path;
			["gcc"] = true;
			["g++"] = true;
			-- ["cxx"] = bl_gpp;
			-- ["cc"] = bl_gcc;
			["gdb"] = bl_gdb;
			["gcc-pretty-print"] = make_path(toolchain_root, "share/gcc-"..bl_gcc_ver.."/python");
			["flash_cmd"] = bl_BLFlashCommand;
			["fw_post_proc"] = bl_fw_post_proc;
			["vcpkg"] = "DISABLED";
		};
		["target_info"] = bl_targets;
		["debug_adapter"] = debug_adapter;
		encoding = "utf-8";
	};
	table.sort(bouffalo_toolset.targets);
	return bouffalo_toolset;
end;

local function find_bl_sdks()
	local bl_sdks = {};

	local EDX_TOOLSETS_ROOT = (function()
			local edx_exe_path = edx.make_config_path("edx.exe", CFG_PATH_CWD);
			local toolsets_root = make_path(edx_exe_path, "../toolsets");
			local root = {};
			if utils:is_dir(toolsets_root) then
				table.insert(root, toolsets_root);
			end;
			local app_toolset_root = make_path(edx.app_path, [[toolsets]]);
			if utils:is_dir(app_toolset_root) and app_toolset_root ~= toolsets_root then
				table.insert(root, app_toolset_root);
			end;
			return root;
	end)();

	local bl_roots = {};
	for idx, root in ipairs(EDX_TOOLSETS_ROOT) do
		local bl_root = utils:list_dir(make_path(root, "bouffalo_sdk*"));
		if bl_root then
			bl_root = make_path(root, bl_root);
			if not bl_roots[bl_root] then
				bl_roots[bl_root] = true;
				local bl_sdk = find_bouffalo_toolsets(bl_root);
				table.insert(bl_sdks, bl_sdk);
			end;
		end;
	end;
	local selected_sdk = bl_sdks[1];
	if selected_sdk == nil then
		return;
	end;

	print("Found Bouffalo SDK(s)!");
	for idx, info in ipairs(bl_sdks) do
		print("    => ", info.home, " support [", table.concat(info.targets or {}, ","), "]");
	end;
	return bl_sdks, selected_sdk;
end;

local BL_DEF_TOOLSET = nil;
BL_TOOLSETS, BL_DEF_TOOLSET = pinvoke(find_bl_sdks);
local BL_TOOLSET = BL_DEF_TOOLSET;
BL_CMAKE_PROJECT = false;

function bl_create_project(path, name, target, on_finished_handler)
	if #BL_TOOLSETS == 0 then
		return false;
	end;
	local toolset = BL_DEF_TOOLSET;
	if not (toolset and toolset.target_info and toolset.target_info[target]) then
		-- the toolset does not support the target type
		for idx, info in ipairs(BL_TOOLSETS) do
			if info.target_info[target] then
				-- find a valid toolset
				toolset = info;
				break;
			end;
		end;
	end;

	local project_name = name:gsub("%s", "-");
local cmake_list = [[cmake_minimum_required(VERSION 3.15)

include(proj.conf)

find_package(bouffalo_sdk REQUIRED HINTS $ENV{BL_SDK_BASE})

# sdk_add_compile_definitions(<MACRO_DEF>)

# sdk_add_compile_options(<COMPIER_OPTIONS>)

# sdk_add_link_options(<LINKER_OPTIONS>)

# sdk_add_include_directories(${CMAKE_CURRENT_SOURCE_DIR})

file(GLOB APP_FILES
	LIST_DIRECTORIES false
	RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
	${CMAKE_CURRENT_SOURCE_DIR}/*.c
	${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/*.cc
	${CMAKE_CURRENT_SOURCE_DIR}/*.cxx
)

list(FILTER APP_FILES EXCLUDE REGEX "main\\.c")

target_sources(app PRIVATE ${APP_FILES})

sdk_set_main_file(main.c)

project(%s)
]];

local proj_conf = [[option(CONFIG_BFLOG 					"" 						OFF)
option(CONFIG_BFLOG_DEBUG 				"" 						OFF)
option(CONFIG_BFLB_BLOCK_POOL_DEBUG		"" 						OFF)
option(CONFIG_COREDUMP					"" 						OFF)
option(CONFIG_PARTITION					"PARTITION"				OFF)
option(CONFIG_BFLB_MTD					"BLMTD"					OFF)
option(CONFIG_BFLB_OTA					"OTA" 					OFF)
option(CONFIG_CHERRYUSB 				"USB Support" 			OFF)
option(CONFIG_FREERTOS 					"FreeRTOS Support" 		OFF)
option(CONFIG_TLSF 						"Memory Management" 	ON)
option(CONFIG_NEWLIB 					"NewLib" 				OFF)
option(CONFIG_NEWLIB_STANDARD 			"NewLib standard" 		OFF)
option(CONFIG_LOG_DISABLE 				"" 						OFF)
option(CONFIG_ASSERT_DISABLE 			"" 						OFF)
option(CONFIG_LOG_NCOLOR 				"" 						OFF)
option(CONFIG_LVGL 						"LVGL Support"			OFF)
option(CONFIG_XZ 						"XZ Support"			OFF)
option(CONFIG_LWIP 						"LWIP Support"			OFF)
option(CONFIG_CLI_CMD_ENABLE			""						OFF)
#option(CONFIG_MEMTESTER				""						OFF)
#option(CONFIG_HYPERVISOR				""						OFF)
#option(CONFIG_EASYFLASH4				"EasyFlash4 Support"	OFF)
option(CONFIG_HIBOOSTER					""						OFF)
option(CONFIG_MULTIMEDIA				"Multimedia Support"	OFF)
#option(CONFIG_IPERF					""						OFF)
option(CONFIG_SHELL						""						OFF)

# multimedia
option(CONFIG_ALGRTC 					""					OFF)
option(CONFIG_AACDEC 					""					OFF)
option(CONFIG_AMRNB 					""					OFF)
option(CONFIG_AMRWB 					""					OFF)
option(CONFIG_FLAC 						""					OFF)
option(CONFIG_PVMP3DEC 					""					OFF)
option(CONFIG_OPUS 						""					OFF)
option(CONFIG_SPEEX 					""					OFF)
option(CONFIG_VORBIS 					""					OFF)
option(CONFIG_OGG 						""					OFF)
option(CONFIG_AUIDIO_FLOWCTRL_BRIDGE 	""					OFF)
option(CONFIG_BL616_BLIMPLS 			""					OFF)
option(CONFIG_SONIC 					""					OFF)
option(CONFIG_MINIALSA 					""					OFF)
option(CONFIG_SPEEXDSP 					""					OFF)
option(CONFIG_USERVICE 					""					OFF)
option(CONFIG_DRV_SND_BL616 			""					OFF)
option(CONFIG_SMART_AUDIO_BL616 		""					OFF)
option(CONFIG_AV 						""					OFF)
option(CONFIG_XCODEC 					""					OFF)
option(CONFIG_XPORT 					""					OFF)
option(CONFIG_XUTILS 					""					OFF)
option(CONFIG_XRECORD 					""					OFF)
option(CONFIG_XWEBSOCKET 				""					OFF)

#wireless
option(CONFIG_BLUETOOTH 				""					OFF)
option(CONFIG_WIFI6 					""					OFF)
option(CONFIG_LPFW 						""					OFF)
option(CONFIG_LMAC154 					""					OFF)

#fs
option(CONFIG_FATFS 					""					OFF)
option(CONFIG_LITTLEFS 					""					OFF)
option(CONFIG_ROMFS 					""					OFF)

#ai
option(CONFIG_TINYMAIX 					""					OFF)
option(CONFIG_TENSORFLOWLITE			""					OFF)

option(CONFIG_MBEDTLS 					"MbedTLS Support"	OFF)

set(CONFIG_VLIBC 					0		 			CACHE STRING "Enable VLIBC")
set(CONFIG_BFLOG					0		 			CACHE STRING "Enable BFLOG")
set(CONFIG_ETHERNET				 	0		 			CACHE STRING "Enable ETHERNET Support")
set(CONFIG_PSRAM 					1		 			CACHE STRING "Enable PSRAM Support")
set(CONFIG_SCANF_FLOAT	 			0		 			CACHE STRING "Enable scanf float")
set(CONFIG_SCANF_FLOAT_EX 			0		 			CACHE STRING "Enable scanf float ex")
set(CONFIG_VSNPRINTF_NANO 			0		 			CACHE STRING "Enable vsprintf nano")
set(CONFIG_VSNPRINTF_FLOAT 			1		 			CACHE STRING "Enable vsprintf float")
set(CONFIG_VSNPRINTF_FLOAT_EX 		1					CACHE STRING "Enable vsprintf float ex")
set(CONFIG_VSNPRINTF_LONG_LONG		1					CACHE STRING "Enable vsprintf long long")
set(CONFIG_LOG_LEVEL 				3					CACHE STRING "Current log level")
set(CONFIG_LOG_POOL_SIZE 			1024				CACHE STRING "Current log pool size")
set(CONFIG_BLE_USE_MAC2 			0					CACHE STRING "")
set(CONFIG_BFLB_MTD		 			1					CACHE STRING "")
set(CONFIG_BT_STACK_CLI 			1					CACHE STRING "")
set(CONFIG_BTBLECONTROLLER_LIB 		ble1m0s1bredr0		CACHE STRING "")
]];

local main_c = [[#include "bflb_mtimer.h"
#include "board.h"

#define DBG_TAG "MAIN"
#include "log.h"

int main(void) {
    board_init();
    while (1) {
        LOG_F("hello world fatal\r\n");
        LOG_E("hello world error\r\n");
        LOG_W("hello world warning\r\n");
        LOG_I("hello world information\r\n");
        LOG_D("hello world debug\r\n");
        LOG_T("hello world trace\r\n");
        LOG_RF("hello world fatal raw\r\n");
        LOG_RE("hello world error raw\r\n");
        LOG_RW("hello world warning raw\r\n");
        LOG_RI("hello world information raw\r\n");
        LOG_RD("hello world debug raw\r\n");
        LOG_RT("hello world trace raw\r\n");
        bflb_mtimer_delay_ms(1000);
		}
		return 0;
}
]];
		write_file(make_path(path, "CMakeLists.txt"), cmake_list:format(project_name));
		write_file(make_path(path, "proj.conf"), proj_conf);
		write_file(make_path(path, "main.c"), main_c);
		if on_finished_handler then
			local extras = "\t[\".extra\"] = {\n"..
			"\t\t[\"target_cpu\"] = [[%s]];\n"..
			"\t\t[\"com_baud\"] = [[2048000]];\n"..
			"\t\t[\"mon_baud\"] = [[2048000]];\n"..
			"\t}\n";
			on_finished_handler(true, toolset.name, "main.c", extras:format(target));
		end;
end;

local function bl_show_command_line(path)
	local toolset = BL_TOOLSET;
	if not path then
		path = project:home();
	end;
	local cmd_line = [[cmd /d /k]];
	local params = table.clone( toolset.envs, {
			PATH = table.concat(toolset.bin, ";")..";"..os.getenv("PATH");
	});
	params[1] = path;
	edx.shell2(cmd_line, "*", params);
end;

local old_project_target_filter = project._cmake_project_target_filter;
project._cmake_project_target_filter = function(self, target, cmd)
	if BL_CMAKE_PROJECT and cmd.toolset and cmd.toolset["type"] == "bouffalo_sdk" then
		return target["dir"] and (
			target["dir"]:sub(1,18) == "build_out/drivers/" or
			target["dir"]:sub(1,21) == "build_out/components/" or
			(target["name"] == "app" and target["dir"] == ".")
		);
	end;
	return old_project_target_filter(self, target, cmd);
end;

local old_project_filter = project._cmake_project_filter;
project._cmake_project_filter = function(self, targets, cmd)
	targets = old_project_filter(self, targets, cmd);
	if BL_CMAKE_PROJECT then
		local bl_build_path = make_path(cmd.build_path, "bouffalo_sdk");
		local bl_targets = {};
		for idx, target in ipairs(targets) do
			if target["type"] ~= 4 and not is_file_in_path(make_path(target.output), bl_build_path) then
				table.insert(bl_targets, target);
			end;
		end;
		-- add install target enable the install menu item
		table.insert(bl_targets, {
				["name"] = "install";
				["type"] = 4;
		});
		-- enable flash target
		table.insert(bl_targets, {
				["name"] = "flash";
				["type"] = 4;
		});
		return bl_targets;
	end;
	return targets;
end;

local old_cmake_find_build_command = project._cmake_find_build_command;
project._cmake_find_build_command = function(self, build_target)
	if not BL_CMAKE_PROJECT then
		return old_cmake_find_build_command(self, build_target);
	end;

	local config = menu_bar.get_cmake_config();
	local toolset = nil;
	local target = menu_bar.get_cmake_cpu() or "bl606";
	local cmake_extra_arguments = nil;

	local bl_toolset = BL_TOOLSET;
	-- is Bouffalo project
	local target_info = bl_toolset.target_info[target];
	toolset = table.clone(bl_toolset, {
			["name"] = bl_toolset.name.."-"..target;
			["target"] = target;
			["include"] = target_info.include;
			["define"] = target_info.define;
			["envs"] = table.clone(bl_toolset.envs, {
					["BLPORT"] = menu_bar.get_cmake_serial_port() or (({utils:list_serial_ports()})[1] or {})[1];
					["BLBAUD"] = menu_bar.get_cmake_serial_baud();
			});
			["tools"] = table.clone(bl_toolset.tools, {
					["gdb"] = target_info.gdb_path;
			});
	});
	if build_target == "install" then
		build_target = "flash";
	end;
	cmake_extra_arguments = " -DCPU_ID= -DBOARD_DIR= -DCONFIG_ROMAPI=y -DCONFIG_TLSF=y -DCHIP=\""..target.. "\" -DBOARD=\""..target.. "dk\" -DCROSS_COMPILE=\"riscv64-unknown-elf-\"";

	local build_cmd = self:_cmake_find_build_command_impl(build_target, toolset, config, cmake_extra_arguments);
	if build_cmd == nil then
		return nil;
	end;
	-- print_table(build_cmd);
	build_cmd.__bl_build_target = build_target;
	return build_cmd;
end;

local old_cmake_gen_do_build_params = project._cmake_gen_do_build_params;
project._cmake_gen_do_build_params = function(self, cmd, build_type, on_complete, prepend_cmds, apppend_cmds)
	if not BL_CMAKE_PROJECT then
		return old_cmake_gen_do_build_params(self, cmd, build_type, on_complete, prepend_cmds, apppend_cmds);
	end;
	local toolset = cmd.toolset;
	if cmd.__bl_build_target == "flash" then
		local com_port = toolset.envs["BLPORT"];
		local com_port_baud = toolset.envs["BLBAUD"];
		-- if the corresponding port was opened, trying to stop the serial port monitor
		if edx:set_serial_monitor(com_port, false) then
			-- the com port is closed successfully
			local old_on_complete = on_complete;
			on_complete = function(success)
				-- trying to reopen the com port
				edx:set_serial_monitor(com_port, true);
				if old_on_complete then
					old_on_complete(success);
				end;
			end;
		end;
		cmd["build_target"] = cmd["build"];
		build_type = "flash";
	end;
	local params = old_cmake_gen_do_build_params(self, cmd, build_type, on_complete, prepend_cmds, apppend_cmds);
	if build_type == "rebuild" or
		build_type == "rebuild_target" or
		build_type == "build_target" or
		build_type == "build" or
		build_type == "flash"
	then
		table.insert(params.cmds, {
				["cmd"] = cmd["build"].." --target combine";
				encoding = "utf-8";
		});
	end;
	if build_type == "flash" then
		local com_port = toolset.envs["BLPORT"];
		local com_port_baud = toolset.envs["BLBAUD"];
		local cpu_id = "";
		local target = menu_bar.get_cmake_cpu() or "bl606";
		local tools = toolset.tools;
		local flash_cfg = project:make_path("flash_prog_cfg.ini");
		local cmd_line = tools["flash_cmd"] .. [[ --interface=uart --baudrate=%s --port=%s --chipname=%s --cpu_id=%s --config="%s"]];
		table.insert(params.cmds, {
				["cmd"] = cmd_line:format(com_port_baud, com_port, target, cpu_id, flash_cfg);
				encoding = "utf-8";
				pre_run = function(self)
					-- prepare flash_prog_cfg.ini
					local def_target = menu_bar.get_cmake_default_target() or { name = ""; output = "" };
					if not def_target.output then
						print("Can NOT find output target!");
						return;
					end;
					local target_output = project:make_rel_path(make_path(def_target.output));
					target_output = target_output:gsub("/cmake/bouffalo_sdk%-bl%d+/", "/cmake/bouffalo_sdk-$(CHIPNAME)/");
					local cfg_file = io.u8_open(flash_cfg, "r");
					local new_cfg = {};
					local should_update_cfg = false;
					local file_dir_lines = {
						["FW"] = "filedir = "..(target_output:gsub("(_bl%d+).elf$", "_$(CHIPNAME).bin"));
						["boot2"] = "filedir = "..(target_output:gsub("/build_out/.-(_bl%d+).elf$", "/build_out/boot2_*.bin"));
						["partition"] = "filedir = "..(target_output:gsub("/build_out/.-(_bl%d+).elf$", "/build_out/partition*.bin"));
						["mfg"] = "filedir = "..(target_output:gsub("/build_out/.-(_bl%d+).elf$", "/build_out/mfg*.bin"));
					};

					if cfg_file then
						local current_section = "";
						for line in cfg_file:lines() do
							local section_name = line:match("%[(%w+)%]");
							if section_name then
								if file_dir_lines[current_section] and section_name ~= current_section then
									table.insert(new_cfg, file_dir_line);
									file_dir_lines[current_section] = nil;
									should_update_cfg = true;
								end;
								current_section = section_name;
								table.insert(new_cfg, line);
							elseif file_dir_lines[current_section] then
								local key, val = line:match("^%s*(%w+)%s*=%s*(.*)");
								if key == "filedir" then
									local file_dir_line = file_dir_lines[current_section];
									file_dir_lines[current_section] = nil;
									if file_dir_line ~= line then
										should_update_cfg = true;
									end;
									if file_dir_line then
										table.insert(new_cfg, file_dir_line);
									end;
								else
									table.insert(new_cfg, line);
								end;
							else
								table.insert(new_cfg, line);
							end;
						end;
						cfg_file:close();
					else
						new_cfg = {
							"[cfg]";
							"# 0: no erase, 1:programmed section erase, 2: chip erase";
							"erase = 1";
							"# skip mode set first para is skip addr, second para is skip len, multi-segment region with ; separated";
							"skip_mode = 0x0, 0x0";
							"# 0: not use isp mode, #1: isp mode";
							"boot2_isp_mode = 0";
							"";
							"[FW]";
							file_dir_lines["FW"];
							"address = 0x000000";
						};
						should_update_cfg = true;
					end;
					if should_update_cfg then
						write_file(flash_cfg, table.concat(new_cfg, "\n"));
					end;
				end;
		});
	end;
	return params;
end;

table.insert(project._toolset_changed_listeners,
	function(toolset)
		if BL_CMAKE_PROJECT then
			BL_TOOLSET = toolset;
			menu_bar.update_cmake_cpu(toolset.targets, menu_bar.get_cmake_cpu());
		end;
	end
);

local function bl_find_current_target(dir)
	if project_configs == nil then
		project:reload_settings(dir);
	end;
	return project:get_extra_setting("target_cpu", "bl606");
end;

table.insert(xws_mgr._pre_load_cmake_project,
	function(self, dir, cmake_lists, cmake_lists_data)
		BL_CMAKE_PROJECT = string.match(cmake_lists_data, [[find_package%(bouffalo_sdk%s.*%s%$ENV{BL_SDK_BASE}]]) ~= nil;
		if not BL_CMAKE_PROJECT then
			return false;
		end;
		if BL_TOOLSET == nil then
			BL_CMAKE_PROJECT = false;
			print("\x1b[33;41m", LANG("Bouffalo SDK/TOOLCHAIN not found!"),"\x1b[0m");
			print("\x1b[33;41m", LANG("Download SDK from https://github.com/bouffalolab/bouffalo_sdk.git"),"\x1b[0m");
			print("\x1b[33;41m", LANG("Download TOOLCHAIN from https://github.com/bouffalolab/toolchain_gcc_t-head_windows.git"),"\x1b[0m");
			return false;
		end;
		project.show_command_line = bl_show_command_line;
		local bl_toolset = BL_DEF_TOOLSET;
		local def_target = bl_find_current_target(dir);
		local def_buadrate = project:get_extra_setting("com_baud", SERIAL_DEFAULT_BAUDRATE);
		local mon_buadrate = project:get_extra_setting("mon_baud", SERIAL_DEFAULT_BAUDRATE);
		if bl_toolset.target_info[def_target] == nil then
			-- target was not supported by default toolset
			for idx, toolset in ipairs(BL_TOOLSETS) do
				if toolset.target_info[def_target] then
					bl_toolset = toolset;
					break;
				end;
			end;
		end;

		menu_bar.update_cmake_toolset(BL_TOOLSETS);
		menu_bar.set_cmake_toolset(bl_toolset.name);
		menu_bar.show_cmake_items(
			"toolset",
			"serial_ports",
			"serial_baud",
			"cpus"
			-- "boards",
		);
		menu_bar.set_cmake_text(LANG("menu_bar/cmake/bouffalo_sdk"));
		menu_bar.update_cmake_cpu(bl_toolset.targets);
		menu_bar.set_cmake_cpu(def_target);
		menu_bar.update_cmake_serial_baud(
			SERIAL_BAUDRATES_HI,
			def_buadrate
		);
		menu_bar.update_cmake_serial_port(
			function()
				local ports = {utils:list_serial_ports()};
				table.sort(ports, function(a, b)
						return tonumber(a[1]:sub(4)) < tonumber(b[1]:sub(4));
				end);
				return ports;
			end,
			nil,
			function(val) return val[1], table.concat(val, "\t"); end
		);
		edx:update_serial_baudrate(SERIAL_BAUDRATES, mon_buadrate);
		return true;
	end
);

table.insert(xws_mgr._pre_unload_dir,
	function(self, dir)
		if not BL_CMAKE_PROJECT then
			return false;
		end;
		project:set_extra_setting("target_cpu", menu_bar.get_cmake_cpu());
		return false;
	end
);

table.insert(xws_mgr._post_unload_dir,
	function(self, dir)
		if BL_CMAKE_PROJECT then
			menu_bar.hide_cmake_items("serial_ports", "serial_baud", "debug_adapters", "debug_adapter_speed", "debug_adapter_transport", "cpus", "boards");
			menu_bar.show_cmake_items("toolset");
			menu_bar.update_cmake_toolset(TOOLSETS);
			menu_bar.set_cmake_text(LANG("menu_bar/cmake"));
			project.show_command_line = nil;
		end;
		BL_CMAKE_PROJECT = false;
		return false;
	end
);
