diff --git a/src/Snapmaker_Orca.cpp b/src/Snapmaker_Orca.cpp index 2e2f4421e3e..e6690467500 100644 --- a/src/Snapmaker_Orca.cpp +++ b/src/Snapmaker_Orca.cpp @@ -460,7 +460,7 @@ static int decode_png_to_thumbnail(std::string png_file, ThumbnailData& thumbnai std::string png_buffer(size, '\0'); png_buffer.reserve(size); - boost::filesystem::ifstream ifs(png_file, std::ios::binary); + boost::nowide::ifstream ifs(png_file, std::ios::binary); ifs.read(png_buffer.data(), png_buffer.size()); ifs.close(); @@ -6109,11 +6109,20 @@ bool CLI::setup(int argc, char **argv) } #endif +#ifdef WIN32 + // path::string() returns ACP on Windows; use nowide::narrow(wstring()) for UTF-8. + set_resources_dir(boost::nowide::narrow(path_resources.wstring())); + set_var_dir(boost::nowide::narrow((path_resources / "images").wstring())); + set_local_dir(boost::nowide::narrow((path_resources / "i18n").wstring())); + set_sys_shapes_dir(boost::nowide::narrow((path_resources / "shapes").wstring())); + set_custom_gcodes_dir(boost::nowide::narrow((path_resources / "custom_gcodes").wstring())); +#else set_resources_dir(path_resources.string()); set_var_dir((path_resources / "images").string()); set_local_dir((path_resources / "i18n").string()); set_sys_shapes_dir((path_resources / "shapes").string()); set_custom_gcodes_dir((path_resources / "custom_gcodes").string()); +#endif // Parse all command line options into a DynamicConfig. // If any option is unsupported, print usage and abort immediately. diff --git a/src/dev-utils/BaseException.cpp b/src/dev-utils/BaseException.cpp index 9ca16080e3d..3b68a9b5e41 100644 --- a/src/dev-utils/BaseException.cpp +++ b/src/dev-utils/BaseException.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 440ac33387c..427fcfd7594 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -1430,13 +1431,25 @@ void AppConfig::reset_selections() std::string AppConfig::config_path() { #ifdef USE_JSON_CONFIG +#ifdef WIN32 + std::string path = (m_mode == EAppMode::Editor) ? + boost::nowide::narrow((Slic3r::data_dir_path() / (SLIC3R_APP_KEY ".conf")).make_preferred().wstring()) : + boost::nowide::narrow((Slic3r::data_dir_path() / (GCODEVIEWER_APP_KEY ".conf")).make_preferred().wstring()); +#else + std::string path = (m_mode == EAppMode::Editor) ? + (Slic3r::data_dir_path() / (SLIC3R_APP_KEY ".conf")).make_preferred().string() : + (Slic3r::data_dir_path() / (GCODEVIEWER_APP_KEY ".conf")).make_preferred().string(); +#endif +#else +#ifdef WIN32 std::string path = (m_mode == EAppMode::Editor) ? - (boost::filesystem::path(Slic3r::data_dir()) / (SLIC3R_APP_KEY ".conf")).make_preferred().string() : - (boost::filesystem::path(Slic3r::data_dir()) / (GCODEVIEWER_APP_KEY ".conf")).make_preferred().string(); + boost::nowide::narrow((Slic3r::data_dir_path() / (SLIC3R_APP_KEY ".ini")).make_preferred().wstring()) : + boost::nowide::narrow((Slic3r::data_dir_path() / (GCODEVIEWER_APP_KEY ".ini")).make_preferred().wstring()); #else std::string path = (m_mode == EAppMode::Editor) ? - (boost::filesystem::path(Slic3r::data_dir()) / (SLIC3R_APP_KEY ".ini")).make_preferred().string() : - (boost::filesystem::path(Slic3r::data_dir()) / (GCODEVIEWER_APP_KEY ".ini")).make_preferred().string(); + (Slic3r::data_dir_path() / (SLIC3R_APP_KEY ".ini")).make_preferred().string() : + (Slic3r::data_dir_path() / (GCODEVIEWER_APP_KEY ".ini")).make_preferred().string(); +#endif #endif return path; diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index e25eba2ec15..452dbddcf2b 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -801,9 +802,12 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex CNumericLocalesSetter locales_setter; try { - boost::nowide::ifstream ifs(file); - ifs >> j; - ifs.close(); +#ifdef WIN32 + // `file` is UTF-8 (wx / data_dir); boost::filesystem::path(std::string) uses ACP on Windows. + j = json::parse(read_text_file_for_json_parse(boost::filesystem::path(boost::nowide::widen(file)))); +#else + j = json::parse(read_text_file_for_json_parse(boost::filesystem::path(file))); +#endif const ConfigDef* config_def = this->def(); if (config_def == nullptr) { diff --git a/src/libslic3r/FilamentColorLibrary.cpp b/src/libslic3r/FilamentColorLibrary.cpp index 98e5d82c8dc..8ddbc5c2f9d 100644 --- a/src/libslic3r/FilamentColorLibrary.cpp +++ b/src/libslic3r/FilamentColorLibrary.cpp @@ -2,6 +2,7 @@ #include "Utils.hpp" #include +#include #include #include #include @@ -86,7 +87,7 @@ std::vector NormalizeColorList(const std::vector& colo boost::filesystem::path FilamentsColoursPath() { // Prefer the system copy, fall back to bundled resources for a fresh install. - boost::filesystem::path path = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path path = Slic3r::data_dir_path(); path /= "system"; path /= "Snapmaker"; path /= "filament"; @@ -94,7 +95,7 @@ boost::filesystem::path FilamentsColoursPath() if (boost::filesystem::exists(path)) return path.make_preferred(); - path = boost::filesystem::path(Slic3r::resources_dir()); + path = Slic3r::resources_dir_path(); path /= "profiles"; path /= "Snapmaker"; path /= "filament"; @@ -162,7 +163,7 @@ std::string NormalizeJsonColor(const std::string& color, bool& hasInvalidColor) bool LoadJsonFile(const boost::filesystem::path& path, nlohmann::json& out) { - boost::nowide::ifstream ifs(path.string()); + boost::nowide::ifstream ifs(path); if (!ifs.is_open()) { BOOST_LOG_TRIVIAL(warning) << "Failed to open official filament color file: " << path.string(); diff --git a/src/libslic3r/FilamentHotBedNozzleRules.cpp b/src/libslic3r/FilamentHotBedNozzleRules.cpp index 03b51a7fe74..1518c3db1cb 100644 --- a/src/libslic3r/FilamentHotBedNozzleRules.cpp +++ b/src/libslic3r/FilamentHotBedNozzleRules.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -21,17 +23,30 @@ namespace pt = boost::property_tree; namespace { // Prefer user data dir (installed system profile) so rules can be patched without reinstalling the app. +// Windows: data_dir/resources_dir are UTF-8; return UTF-8 path for boost::nowide streams, build fs::path via widen for lookups. static std::string filament_hot_bed_nozzles_json_path() { namespace fs = boost::filesystem; - const fs::path user_path = (fs::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR / PresetBundle::SM_BUNDLE / "filament" / - "filament_hot_bed_nozzles.json") - .make_preferred(); - if (fs::exists(user_path)) - return user_path.string(); - return (fs::path(Slic3r::resources_dir()) / "profiles" / PresetBundle::SM_BUNDLE / "filament" / "filament_hot_bed_nozzles.json") - .make_preferred() - .string(); +#ifdef _WIN32 + const fs::path user = (fs::path(boost::nowide::widen(Slic3r::data_dir())) / PRESET_SYSTEM_DIR / PresetBundle::SM_BUNDLE / "filament" / + "filament_hot_bed_nozzles.json") + .make_preferred(); + + if (fs::exists(user)) + return boost::nowide::narrow(user.wstring()); + const fs::path res = (fs::path(boost::nowide::widen(Slic3r::resources_dir())) / "profiles" / PresetBundle::SM_BUNDLE / "filament" / + "filament_hot_bed_nozzles.json") + .make_preferred(); + + return boost::nowide::narrow(res.wstring()); +#else + const fs::path user = (fs::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR / PresetBundle::SM_BUNDLE / "filament" / "filament_hot_bed_nozzles.json").make_preferred(); + + if (fs::exists(user)) + return user.string(); + + return (fs::path(Slic3r::resources_dir()) / "profiles" / PresetBundle::SM_BUNDLE / "filament" / "filament_hot_bed_nozzles.json").make_preferred().string(); +#endif } std::string to_upper_ascii(std::string s) @@ -354,14 +369,24 @@ void FilamentHotBedNozzleRules::load() m_loaded = false; const std::string file_path = filament_hot_bed_nozzles_json_path(); - if (!boost::filesystem::exists(file_path)) { +#ifdef _WIN32 + if (!boost::filesystem::exists(boost::filesystem::path(boost::nowide::widen(file_path)))) +#else + if (!boost::filesystem::exists(boost::filesystem::path(file_path))) +#endif + { BOOST_LOG_TRIVIAL(warning) << "filament_hot_bed_nozzles.json not found: " << file_path; return; } try { pt::ptree root; - pt::read_json(file_path, root); + boost::nowide::ifstream ifs(file_path, std::ios::binary); + if (!ifs) { + BOOST_LOG_TRIVIAL(warning) << "filament_hot_bed_nozzles.json could not be opened: " << file_path; + return; + } + pt::read_json(ifs, root); for (const auto& kv : root) { const std::string rule_key = boost::trim_copy(kv.first); diff --git a/src/libslic3r/FilamentHotBedNozzleRules.hpp b/src/libslic3r/FilamentHotBedNozzleRules.hpp index 85338175fe2..e241de6db1b 100644 --- a/src/libslic3r/FilamentHotBedNozzleRules.hpp +++ b/src/libslic3r/FilamentHotBedNozzleRules.hpp @@ -21,14 +21,6 @@ struct NozzleFilamentRuleMismatch { std::string filament_preset_name; // resolved preset display name (may be empty) }; -// JSON: %AppData%/.../system/Snapmaker/filament/filament_hot_bed_nozzles.json (preferred), else bundled resources path. -// Keys: bed ids (btPEI, btGESP), nozzle ids ("0.2mm" …) with support/warning. -// 喷嘴规则(键名以 "mm" 结尾),任选其一: -// 1) 单对象 { "type":"all"|材质, "forbidden":[...] } -// 2) 数组 [ { "type", "forbidden" }, ... ] -// 3) 按材质分键对象:键为 all / undefine / hardened_steel / stainless_steel / brass(可只写其中任意几种), -// 值为 forbidden 数组、{ "forbidden": [...] }、warning 数组、{ "warning": [...] },或同时含 forbidden / warning 的对象; -// "all" 表示任意喷嘴材质共用该条规则。warning 为提示级(不拦截切片),与热床 warning 语义类似。 class FilamentHotBedNozzleRules { public: diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 3f434753727..b85dcb72c96 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -6152,7 +6152,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) MD5_CTX ctx; MD5_Init(&ctx); auto src_gcode_file = plate_data->gcode_file; - boost::filesystem::ifstream ifs(src_gcode_file, std::ios::binary); + boost::nowide::ifstream ifs(src_gcode_file, std::ios::binary); std::string buf(64 * 1024, 0); const std::size_t & size = boost::filesystem::file_size(src_gcode_file); std::size_t left_size = size; @@ -7996,7 +7996,7 @@ bool _BBS_3MF_Exporter::_add_gcode_file_to_archive(mz_zip_archive& archive, cons BOOST_LOG_TRIVIAL(error) << "Gcode is missing, filename = " << src_gcode_file; result = false; } - boost::filesystem::ifstream ifs(src_gcode_file, std::ios::binary); + boost::nowide::ifstream ifs(src_gcode_file, std::ios::binary); std::string buf(64 * 1024, 0); while (ifs) { ifs.read(buf.data(), buf.size()); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 8882ce9a3af..85b0fb19632 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -177,7 +177,7 @@ ConfigFileType guess_config_file_type(const ptree &tree) VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool load_all) { ptree tree; - boost::filesystem::ifstream ifs(path); + boost::nowide::ifstream ifs(path); boost::property_tree::read_ini(ifs, tree); return VendorProfile::from_ini(tree, path, load_all); } @@ -3911,9 +3911,15 @@ namespace PresetUtils { std::string out; const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset); if (pm != nullptr && !pm->bed_model.empty()) { - out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_model; - if (!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model; + const auto vendor_p = boost::filesystem::path(boost::nowide::widen(preset.vendor->id)); + auto user_path = (Slic3r::data_dir_path() / "vendor" / vendor_p / pm->bed_model).make_preferred(); + if (!boost::filesystem::exists(user_path)) + user_path = (Slic3r::resources_dir_path() / "profiles" / vendor_p / pm->bed_model).make_preferred(); +#ifdef WIN32 + out = boost::nowide::narrow(user_path.wstring()); +#else + out = user_path.string(); +#endif } return out; } @@ -3923,9 +3929,15 @@ namespace PresetUtils { std::string out; const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset); if (pm != nullptr && !pm->bed_texture.empty()) { - out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->bed_texture; - if (!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture; + const auto vendor_p = boost::filesystem::path(boost::nowide::widen(preset.vendor->id)); + auto user_path = (Slic3r::data_dir_path() / "vendor" / vendor_p / pm->bed_texture).make_preferred(); + if (!boost::filesystem::exists(user_path)) + user_path = (Slic3r::resources_dir_path() / "profiles" / vendor_p / pm->bed_texture).make_preferred(); +#ifdef WIN32 + out = boost::nowide::narrow(user_path.wstring()); +#else + out = user_path.string(); +#endif } return out; } @@ -3935,13 +3947,30 @@ namespace PresetUtils { std::string out; const VendorProfile::PrinterModel* pm = PresetUtils::system_printer_model(preset); if (pm != nullptr && !pm->hotend_model.empty()) { - out = Slic3r::data_dir() + "/vendor/" + preset.vendor->id + "/" + pm->hotend_model; - if (!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->hotend_model; + const auto vendor_p = boost::filesystem::path(boost::nowide::widen(preset.vendor->id)); + auto user_path = (Slic3r::data_dir_path() / "vendor" / vendor_p / pm->hotend_model).make_preferred(); + if (!boost::filesystem::exists(user_path)) + user_path = (Slic3r::resources_dir_path() / "profiles" / vendor_p / pm->hotend_model).make_preferred(); +#ifdef WIN32 + out = boost::nowide::narrow(user_path.wstring()); +#else + out = user_path.string(); +#endif + } + + if (out.empty() +#ifdef WIN32 + || !boost::filesystem::exists(boost::filesystem::path(boost::nowide::widen(out))) +#else + || !boost::filesystem::exists(boost::filesystem::path(out)) +#endif + ) { +#ifdef WIN32 + out = boost::nowide::narrow((Slic3r::resources_dir_path() / "profiles" / "hotend.stl").make_preferred().wstring()); +#else + out = (Slic3r::resources_dir_path() / "profiles" / "hotend.stl").make_preferred().string(); +#endif } - - if (out.empty() ||!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/hotend.stl"; return out; } } // namespace PresetUtils diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index f97f468fe47..88dbfddbe80 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -334,7 +335,7 @@ void PresetBundle::reset(bool delete_files) void PresetBundle::setup_directories() { - boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path data_dir = Slic3r::data_dir_path(); //BBS: change directoties by design std::initializer_list paths = { data_dir, @@ -372,7 +373,15 @@ static void copy_dir(const boost::filesystem::path& from_dir, const boost::files for (auto& dir_entry : boost::filesystem::directory_iterator(from_dir)) { if (!boost::filesystem::is_directory(dir_entry.path())) { std::string em; +#ifdef _WIN32 + // path::string() returns ACP on Windows; copy_file expects UTF-8 (uses CP_UTF8 internally). + CopyFileResult cfr = copy_file( + boost::nowide::narrow(dir_entry.path().wstring()), + boost::nowide::narrow((to_dir / dir_entry.path().filename()).wstring()), + em, false); +#else CopyFileResult cfr = copy_file(dir_entry.path().string(), (to_dir / dir_entry.path().filename()).string(), em, false); +#endif if (cfr != SUCCESS) { BOOST_LOG_TRIVIAL(error) << "Error when copying files from " << from_dir << " to " << to_dir << ": " << em; } @@ -384,7 +393,7 @@ static void copy_dir(const boost::filesystem::path& from_dir, const boost::files void PresetBundle::copy_files(const std::string& from) { - boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path data_dir = Slic3r::data_dir_path(); // list of searched paths based on current directory system in setup_directories() // do not copy cache and snapshots boost::filesystem::path from_data_dir = boost::filesystem::path(from); @@ -567,23 +576,24 @@ bool PresetBundle::use_bbl_device_tab() { bool PresetBundle::backup_user_folder() const { - const std::string backup_folderpath = data_dir() + "/" + (boost::format("user_backup-v%1%") % Snapmaker_VERSION).str(); + const boost::filesystem::path backup_folderpath = + Slic3r::data_dir_path() / (boost::format("user_backup-v%1%") % Snapmaker_VERSION).str(); // Check if backup file already exists - if (boost::filesystem::exists(boost::filesystem::path(backup_folderpath))) + if (boost::filesystem::exists(backup_folderpath)) return false; BOOST_LOG_TRIVIAL(info) << "Backing up user folder to: " << backup_folderpath; try { // Copy the user folder to the backup folder - boost::filesystem::copy(data_dir() + "/" + PRESET_USER_DIR, backup_folderpath, boost::filesystem::copy_options::recursive); + boost::filesystem::copy(Slic3r::data_dir_path() / PRESET_USER_DIR, backup_folderpath, boost::filesystem::copy_options::recursive); BOOST_LOG_TRIVIAL(info) << "User folder backup completed successfully"; return true; } catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "Exception during user folder backup: " << ex.what(); // Try to clean up partially copied backup folder - if (boost::filesystem::exists(boost::filesystem::path(backup_folderpath))) - boost::filesystem::remove_all(boost::filesystem::path(backup_folderpath)); + if (boost::filesystem::exists(backup_folderpath)) + boost::filesystem::remove_all(backup_folderpath); return false; } } @@ -719,9 +729,16 @@ std::string PresetBundle::get_texture_for_printer_model(std::string model_name) if (!texture_name.empty()) { - out = Slic3r::data_dir() + "/vendor/" + vendor_name + "/" + texture_name; - if (!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/" + vendor_name + "/" + texture_name; + // vendor_name may be UTF-8; construct path safely via wide string on Windows. + const auto vendor_p = boost::filesystem::path(boost::nowide::widen(vendor_name)); + auto user_path = (Slic3r::data_dir_path() / "vendor" / vendor_p / texture_name).make_preferred(); + if (!boost::filesystem::exists(user_path)) + user_path = (Slic3r::resources_dir_path() / "profiles" / vendor_p / texture_name).make_preferred(); +#ifdef WIN32 + out = boost::nowide::narrow(user_path.wstring()); +#else + out = user_path.string(); +#endif } return out; @@ -747,9 +764,15 @@ std::string PresetBundle::get_stl_model_for_printer_model(std::string model_name if (!stl_name.empty()) { - out = Slic3r::data_dir() + "/vendor/" + vendor_name + "/" + stl_name; - if (!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/" + vendor_name + "/" + stl_name; + const auto vendor_p = boost::filesystem::path(boost::nowide::widen(vendor_name)); + auto user_path = (Slic3r::data_dir_path() / "vendor" / vendor_p / stl_name).make_preferred(); + if (!boost::filesystem::exists(user_path)) + user_path = (Slic3r::resources_dir_path() / "profiles" / vendor_p / stl_name).make_preferred(); +#ifdef WIN32 + out = boost::nowide::narrow(user_path.wstring()); +#else + out = user_path.string(); +#endif } return out; @@ -774,13 +797,30 @@ std::string PresetBundle::get_hotend_model_for_printer_model(std::string model_n if (!hotend_stl.empty()) { - out = Slic3r::data_dir() + "/vendor/" + vendor_name + "/" + hotend_stl; - if (!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/" + vendor_name + "/" + hotend_stl; + const auto vendor_p = boost::filesystem::path(boost::nowide::widen(vendor_name)); + auto user_path = (Slic3r::data_dir_path() / "vendor" / vendor_p / hotend_stl).make_preferred(); + if (!boost::filesystem::exists(user_path)) + user_path = (Slic3r::resources_dir_path() / "profiles" / vendor_p / hotend_stl).make_preferred(); +#ifdef WIN32 + out = boost::nowide::narrow(user_path.wstring()); +#else + out = user_path.string(); +#endif } - if (out.empty() ||!boost::filesystem::exists(boost::filesystem::path(out))) - out = Slic3r::resources_dir() + "/profiles/hotend.stl"; + if (out.empty() +#ifdef WIN32 + || !boost::filesystem::exists(boost::filesystem::path(boost::nowide::widen(out))) +#else + || !boost::filesystem::exists(boost::filesystem::path(out)) +#endif + ) { +#ifdef WIN32 + out = boost::nowide::narrow((Slic3r::resources_dir_path() / "profiles" / "hotend.stl").make_preferred().wstring()); +#else + out = (Slic3r::resources_dir_path() / "profiles" / "hotend.stl").make_preferred().string(); +#endif + } return out; } @@ -791,13 +831,17 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, For PresetsConfigSubstitutions substitutions; std::string errors_cummulative; - fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR); + fs::path user_folder = Slic3r::data_dir_path() / PRESET_USER_DIR; if (!fs::exists(user_folder)) fs::create_directory(user_folder); - std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + user; - fs::path folder(user_folder / user); + fs::path folder = user_folder / user; if (!fs::exists(folder)) fs::create_directory(folder); + // dir_user_presets is passed to load_presets() which internally does + // boost::filesystem::path(string) — that ctor uses ACP on Windows. + // Use path::string() (ACP) for compatibility with the callee's expectation. + std::string dir_user_presets = folder.make_preferred().string(); + // BBS do not load sla_print // BBS: change directoties by design try { @@ -925,7 +969,7 @@ PresetsConfigSubstitutions PresetBundle::import_presets(std::vector if (boost::iends_with(file, ".orca_printer") || boost::iends_with(file, ".orca_filament") || boost::iends_with(file, ".zip")) { boost::system::error_code ec; // create user folder - fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR); + fs::path user_folder = Slic3r::data_dir_path() / PRESET_USER_DIR; if (!fs::exists(user_folder)) fs::create_directory(user_folder, ec); if (ec) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << ec.message(); // create default folder @@ -1103,11 +1147,10 @@ void PresetBundle::save_user_presets(AppConfig& config, std::vector if (!config.get("preset_folder").empty()) user_sub_folder = config.get("preset_folder"); //BBS: change directory by design - const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/"+ user_sub_folder; + const boost::filesystem::path user_folder = Slic3r::data_dir_path() / PRESET_USER_DIR; + const std::string dir_user_presets = (user_folder / user_sub_folder).make_preferred().string(); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, save to %1%")%dir_user_presets; - - fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR); if (!fs::exists(user_folder)) fs::create_directory(user_folder); @@ -1125,11 +1168,10 @@ void PresetBundle::save_user_presets(AppConfig& config, std::vector void PresetBundle::update_user_presets_directory(const std::string preset_folder) { //BBS: change directory by design - const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/"+ preset_folder; + const boost::filesystem::path user_folder = Slic3r::data_dir_path() / PRESET_USER_DIR; + const std::string dir_user_presets = (user_folder / preset_folder).make_preferred().string(); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, update directory to %1%")%dir_user_presets; - - fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR); if (!fs::exists(user_folder)) fs::create_directory(user_folder); @@ -1145,14 +1187,14 @@ void PresetBundle::update_user_presets_directory(const std::string preset_folder void PresetBundle::remove_user_presets_directory(const std::string preset_folder) { - const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + preset_folder; + const boost::filesystem::path folder = (Slic3r::data_dir_path() / PRESET_USER_DIR / preset_folder).make_preferred(); + const std::string dir_user_presets = folder.string(); if (preset_folder.empty()) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": preset_folder is empty, no need to remove directory : %1%") % dir_user_presets; return; } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, delete directory : %1%") % dir_user_presets; - fs::path folder(dir_user_presets); if (fs::exists(folder)) { fs::remove_all(folder); } @@ -1403,9 +1445,9 @@ std::pair PresetBundle::load_system_pre // Here the vendor specific read only Config Bundles are stored. //BBS: change directory by design - boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / PRESET_SYSTEM_DIR).make_preferred(); + boost::filesystem::path dir = (Slic3r::data_dir_path() / PRESET_SYSTEM_DIR).make_preferred(); if (validation_mode) - dir = (boost::filesystem::path(data_dir())).make_preferred(); + dir = Slic3r::data_dir_path().make_preferred(); PresetsConfigSubstitutions substitutions; std::string errors_cummulative; @@ -1413,14 +1455,14 @@ std::pair PresetBundle::load_system_pre std::vector vendor_names; // store all vendor names in vendor_names for (auto& dir_entry : boost::filesystem::directory_iterator(dir)) { - std::string vendor_file = dir_entry.path().string(); - if (!Slic3r::is_json_file(vendor_file)) + if (!boost::iequals(dir_entry.path().extension().string(), ".json")) continue; - std::string vendor_name = dir_entry.path().filename().string(); - - // Remove the .json suffix. - vendor_name.erase(vendor_name.size() - 5); +#ifdef WIN32 + std::string vendor_name = boost::nowide::narrow(dir_entry.path().stem().wstring()); +#else + std::string vendor_name = dir_entry.path().stem().string(); +#endif vendor_names.push_back(vendor_name); } // Move ORCA_FILAMENT_LIBRARY to the beginning of the list @@ -1441,13 +1483,13 @@ std::pair PresetBundle::load_system_pre // Load the config bundle, flatten it. if (first) { // Reset this PresetBundle and load the first vendor config. - append(substitutions, this->load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem, compatibility_rule).first); + append(substitutions, this->load_vendor_configs_from_json(dir, vendor_name, PresetBundle::LoadSystem, compatibility_rule).first); first = false; } else { // Load the other vendor configs, merge them with this PresetBundle. // Report duplicate profiles. PresetBundle other; - append(substitutions, other.load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem, compatibility_rule, this).first); + append(substitutions, other.load_vendor_configs_from_json(dir, vendor_name, PresetBundle::LoadSystem, compatibility_rule, this).first); std::vector duplicates = this->merge_presets(std::move(other)); if (!duplicates.empty()) { errors_cummulative += "Found duplicated settings in vendor " + vendor_name + "'s json file lists: "; @@ -1503,24 +1545,22 @@ std::pair PresetBundle::load_system_mod compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable; // Here the vendor specific read only Config Bundles are stored. - boost::filesystem::path dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + boost::filesystem::path dir = (Slic3r::resources_dir_path() / "profiles").make_preferred(); PresetsConfigSubstitutions substitutions; std::string errors_cummulative; for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) { - std::string vendor_file = dir_entry.path().string(); - if (Slic3r::is_json_file(vendor_file)) { - std::string vendor_name = dir_entry.path().filename().string(); - // Remove the .json suffix. - vendor_name.erase(vendor_name.size() - 5); + if (!boost::iequals(dir_entry.path().extension().string(), ".json")) + continue; + + std::string vendor_name = dir_entry.path().stem().string(); try { // Load the config bundle, flatten it. - append(substitutions, load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadVendorOnly, compatibility_rule).first); + append(substitutions, load_vendor_configs_from_json(dir, vendor_name, PresetBundle::LoadVendorOnly, compatibility_rule).first); } catch (const std::runtime_error &err) { errors_cummulative += err.what(); errors_cummulative += "\n"; } } - } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%") % errors_cummulative; return std::make_pair(std::move(substitutions), errors_cummulative); @@ -1537,26 +1577,25 @@ std::pair PresetBundle::load_system_fil compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable; // Here the vendor specific read only Config Bundles are stored. - boost::filesystem::path dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + boost::filesystem::path dir = (Slic3r::resources_dir_path() / "profiles").make_preferred(); PresetsConfigSubstitutions substitutions; std::string errors_cummulative; bool first = true; for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) { - std::string vendor_file = dir_entry.path().string(); - if (Slic3r::is_json_file(vendor_file)) { - std::string vendor_name = dir_entry.path().filename().string(); - // Remove the .json suffix. - vendor_name.erase(vendor_name.size() - 5); + if (!boost::iequals(dir_entry.path().extension().string(), ".json")) + continue; + + std::string vendor_name = dir_entry.path().stem().string(); try { if (first) { // Reset this PresetBundle and load the first vendor config. - append(substitutions, this->load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first); + append(substitutions, this->load_vendor_configs_from_json(dir, vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first); first = false; } else { // Load the other vendor configs, merge them with this PresetBundle. // Report duplicate profiles. PresetBundle other; - append(substitutions, other.load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first); + append(substitutions, other.load_vendor_configs_from_json(dir, vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first); std::vector duplicates = this->merge_presets(std::move(other)); if (!duplicates.empty()) { errors_cummulative += "Found duplicated settings in vendor " + vendor_name + "'s json file lists: "; @@ -1571,7 +1610,6 @@ std::pair PresetBundle::load_system_fil errors_cummulative += "\n"; } } - } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%") % errors_cummulative; return std::make_pair(std::move(substitutions), errors_cummulative); @@ -3013,7 +3051,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool //BBS: Load a config bundle file from json std::pair PresetBundle::load_vendor_configs_from_json( - const std::string &path, const std::string &vendor_name, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule, const PresetBundle* base_bundle) + const boost::filesystem::path &root_dir, const std::string &vendor_name, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule, const PresetBundle* base_bundle) { const bool startup_profile = startup_profile_enabled(); const auto total_start = std::chrono::steady_clock::now(); @@ -3022,15 +3060,16 @@ std::pair PresetBundle::load_vendor_configs_ ConfigSubstitutionContext substitution_context { compatibility_rule }; PresetsConfigSubstitutions substitutions; + const std::string root_dir_log = root_dir.generic_string(); //BBS: add config related logs - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, path %1%, compatibility_rule %2%")%path.c_str()%compatibility_rule; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, path %1%, compatibility_rule %2%")%root_dir_log.c_str()%compatibility_rule; if (flags.has(LoadConfigBundleAttribute::ResetUserProfile) || flags.has(LoadConfigBundleAttribute::LoadSystem)) // Reset this bundle, delete user profile files if SaveImported. this->reset(flags.has(LoadConfigBundleAttribute::SaveImported)); // 1) load the vroot json and construct the vendor profile VendorProfile vendor_profile(vendor_name); - std::string root_file = path + "/" + vendor_name + ".json"; + const boost::filesystem::path root_file = root_dir / (vendor_name + ".json"); std::vector> machine_model_subfiles; std::vector> process_subfiles; std::vector> filament_subfiles; @@ -3106,9 +3145,7 @@ std::pair PresetBundle::load_vendor_configs_ subfile_map.emplace_back(file.stem().string(), file.lexically_relative(vendor_dir).generic_string()); }; try { - boost::nowide::ifstream ifs(root_file); - json j; - ifs >> j; + json j = json::parse(read_text_file_for_json_parse(root_file)); //parse the json elements for (auto it = j.begin(); it != j.end(); it++) { if (boost::iequals(it.key(), BBL_JSON_KEY_VERSION)) { @@ -3117,7 +3154,7 @@ std::pair PresetBundle::load_vendor_configs_ auto config_version = Semver::parse(version_str); if (! config_version) { throw ConfigurationError((boost::format("vendor %1%'s config version: %2% invalid\nSuggest cleaning the directory %3% firstly") - % vendor_name % version_str % path).str()); + % vendor_name % version_str % root_dir_log).str()); } else { vendor_profile.config_version = std::move(*config_version); } @@ -3128,7 +3165,7 @@ std::pair PresetBundle::load_vendor_configs_ } else if (boost::iequals(it.key(), BBL_JSON_KEY_DESCRIPTION)) { //get description - BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ": parse "< PresetBundle::load_vendor_configs_ } } catch(nlohmann::detail::parse_error &err) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "< PresetBundle::load_vendor_configs_ //2) paste the machine model for (auto& machine_model : machine_model_subfiles) { - std::string subfile = path + "/" + vendor_name + "/" + machine_model.second; + const boost::filesystem::path subfile_path = root_dir / vendor_name / machine_model.second; VendorProfile::PrinterModel model; model.id = machine_model.first; try { - boost::nowide::ifstream ifs(subfile); - json j; - ifs >> j; + json j = json::parse(read_text_file_for_json_parse(subfile_path)); //parse the json elements for (auto it = j.begin(); it != j.end(); it++) { if (boost::iequals(it.key(), BBL_JSON_KEY_VERSION)) { @@ -3274,9 +3309,9 @@ std::pair PresetBundle::load_vendor_configs_ } } catch(nlohmann::detail::parse_error &err) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<< subfile <<" got a nlohmann::detail::parse_error, reason = " << err.what(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<< subfile_path.generic_string() <<" got a nlohmann::detail::parse_error, reason = " << err.what(); throw ConfigurationError((boost::format("Failed loading configuration file %1%: %2%\nSuggest cleaning the directory %3% firstly") - %subfile %err.what() % path).str()); + %subfile_path.generic_string() %err.what() % root_dir_log).str()); } if (! model.id.empty() && ! model.variants.empty()) @@ -3296,7 +3331,7 @@ std::pair PresetBundle::load_vendor_configs_ PresetCollection *presets = nullptr; size_t presets_loaded = 0; - auto parse_subfile = [this, path, vendor_name, presets_loaded, current_vendor_profile, base_bundle]( + auto parse_subfile = [this, root_dir, root_dir_log, vendor_name, presets_loaded, current_vendor_profile, base_bundle]( ConfigSubstitutionContext& substitution_context, PresetsConfigSubstitutions& substitutions, LoadConfigBundleAttributes& flags, @@ -3306,7 +3341,12 @@ std::pair PresetBundle::load_vendor_configs_ PresetCollection* presets_collection, size_t& count, bool is_from_lib = false) -> std::string { - std::string subfile = path + "/" + vendor_name + "/" + subfile_iter.second; + const boost::filesystem::path subfile_path = root_dir / vendor_name / subfile_iter.second; +#ifdef WIN32 + const std::string subfile = boost::nowide::narrow(subfile_path.wstring()); +#else + const std::string subfile = subfile_path.string(); +#endif // Load the print, filament or printer preset. std::string preset_name; DynamicPrintConfig config; @@ -3411,7 +3451,7 @@ std::pair PresetBundle::load_vendor_configs_ if (key_values.find(ORCA_JSON_KEY_RENAMED_FROM) != key_values.end()) { if (!unescape_strings_cstyle(key_values[ORCA_JSON_KEY_RENAMED_FROM], renamed_from)) { - BOOST_LOG_TRIVIAL(error) << "Error in a Config \"" << path << "\": The preset \"" << preset_name + BOOST_LOG_TRIVIAL(error) << "Error in a Config \"" << root_dir_log << "\": The preset \"" << preset_name << "\" contains invalid \"renamed_from\" key, which is being ignored."; } } @@ -3438,7 +3478,7 @@ std::pair PresetBundle::load_vendor_configs_ auto printer_model = config.opt_string("printer_model"); if (printer_model.empty()) { ++m_errors; - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << root_dir_log << "\": The printer preset \"" << preset_name << "\" defines no printer model, it will be ignored."; reason = std::string("can not find printer_model"); return reason; @@ -3446,7 +3486,7 @@ std::pair PresetBundle::load_vendor_configs_ auto printer_variant = config.opt_string("printer_variant"); if (printer_variant.empty()) { ++m_errors; - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << root_dir_log << "\": The printer preset \"" << preset_name << "\" defines no printer variant, it will be ignored."; reason = std::string("can not find printer_variant"); return reason; @@ -3456,7 +3496,7 @@ std::pair PresetBundle::load_vendor_configs_ ); if (it_model == current_vendor_profile->models.end()) { ++m_errors; - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << root_dir_log << "\": The printer preset \"" << preset_name << "\" defines invalid printer model \"" << printer_model << "\", it will be ignored."; reason = std::string("can not find printer model in vendor profile"); return reason; @@ -3464,7 +3504,7 @@ std::pair PresetBundle::load_vendor_configs_ auto it_variant = it_model->variant(printer_variant); if (it_variant == nullptr) { ++m_errors; - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << root_dir_log << "\": The printer preset \"" << preset_name << "\" defines invalid printer variant \"" << printer_variant << "\", it will be ignored."; reason = std::string("can not find printer_variant in vendor profile"); return reason; @@ -3473,18 +3513,22 @@ std::pair PresetBundle::load_vendor_configs_ const Preset *preset_existing = presets_collection->find_preset(preset_name, false); if (preset_existing != nullptr) { ++m_errors; - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << root_dir_log << "\": The printer preset \"" << preset_name << "\" has already been loaded from another Config Bundle."; reason = std::string("duplicated defines"); return reason; } - auto file_path = (boost::filesystem::path(data_dir()) /PRESET_SYSTEM_DIR/ vendor_name / subfile_iter.second).make_preferred(); + boost::filesystem::path file_path = root_dir / vendor_name / subfile_iter.second; if(validation_mode) - file_path = (boost::filesystem::path(data_dir()) / vendor_name / subfile_iter.second).make_preferred(); + file_path = (Slic3r::data_dir_path() / vendor_name / subfile_iter.second).make_preferred(); // Load the preset into the list of presets, save it to disk. +#ifdef WIN32 + Preset &loaded = presets_collection->load_preset(boost::nowide::narrow(file_path.wstring()), preset_name, std::move(config), false); +#else Preset &loaded = presets_collection->load_preset(file_path.string(), preset_name, std::move(config), false); +#endif if (flags.has(LoadConfigBundleAttribute::LoadSystem)) { loaded.is_system = true; loaded.vendor = current_vendor_profile; @@ -3550,9 +3594,10 @@ std::pair PresetBundle::load_vendor_configs_ if (!reason.empty()) { ++m_errors; //parse error - std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second; + const boost::filesystem::path subfile_p = root_dir / vendor_name / subfile.second; + const std::string subfile_path = subfile_p.generic_string(); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse process setting from %1%") % subfile_path; - throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); + throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % root_dir_log).str()); } } if (startup_profile) { @@ -3574,9 +3619,10 @@ std::pair PresetBundle::load_vendor_configs_ if (!reason.empty()) { ++m_errors; //parse error - std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second; + const boost::filesystem::path subfile_p = root_dir / vendor_name / subfile.second; + const std::string subfile_path = subfile_p.generic_string(); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse filament setting from %1%") % subfile_path; - throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); + throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % root_dir_log).str()); } } if (startup_profile) { @@ -3600,9 +3646,10 @@ std::pair PresetBundle::load_vendor_configs_ if (!reason.empty()) { ++m_errors; //parse error - std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second; + const boost::filesystem::path subfile_p = root_dir / vendor_name / subfile.second; + const std::string subfile_path = subfile_p.generic_string(); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse printer setting from %1%") % subfile_path; - throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str()); + throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % root_dir_log).str()); } } if (startup_profile) { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 8e08a9efd14..d42755970d1 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -247,7 +247,7 @@ class PresetBundle const std::string &path, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule);*/ //Orca: load config bundle from json, pass the base bundle to support cross vendor inheritance std::pair load_vendor_configs_from_json( - const std::string &path, const std::string &vendor_name, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule, const PresetBundle* base_bundle = nullptr); + const boost::filesystem::path &root_dir, const std::string &vendor_name, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule, const PresetBundle* base_bundle = nullptr); // Export a config bundle file containing all the presets and the names of the active presets. //void export_configbundle(const std::string &path, bool export_system_settings = false, bool export_physical_printers = false); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 703e8ed32db..a2f8ca78de3 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -3008,8 +3009,8 @@ FilamentTempType Print::get_filament_temp_type(const std::string& filament_type) static std::unordered_map>filament_temp_type_map; if (filament_temp_type_map.empty()) { - fs::path file_path = fs::path(resources_dir()) / "info" / "filament_info.json"; - std::ifstream in(file_path.string()); + fs::path file_path = Slic3r::resources_dir_path() / "info" / "filament_info.json"; + boost::nowide::ifstream in(file_path); json j; try{ j = json::parse(in); @@ -3043,8 +3044,8 @@ int Print::get_hrc_by_nozzle_type(const NozzleType&type) { static std::mapnozzle_type_to_hrc; if (nozzle_type_to_hrc.empty()) { - fs::path file_path = fs::path(resources_dir()) / "info" / "nozzle_info.json"; - boost::nowide::ifstream in(file_path.string()); + fs::path file_path = Slic3r::resources_dir_path() / "info" / "nozzle_info.json"; + boost::nowide::ifstream in(file_path); //std::ifstream in(file_path.string()); json j; try { diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 0421b657b0d..7d7055d9e13 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -100,6 +100,8 @@ std::string var(const std::string &file_name); void set_resources_dir(const std::string &path); // Return a full path to the resources directory. const std::string& resources_dir(); +// UTF-8 resources_dir() as a boost::filesystem::path (Windows: native wide path; use for all file I/O under resources_dir). +boost::filesystem::path resources_dir_path(); //BBS: add temp dir void set_temporary_dir(const std::string &path); @@ -151,6 +153,8 @@ const std::string& custom_gcodes_dir(); void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. const std::string& data_dir(); +// UTF-8 data_dir() as a boost::filesystem::path (Windows: native wide path; use for all file I/O under data_dir). +boost::filesystem::path data_dir_path(); // BBL: true: succeed create or dir exists; false: fail to create bool makedir(const std::string path); @@ -695,6 +699,10 @@ bool atomic_replace_directory( void save_string_file(const boost::filesystem::path& p, const std::string& str); void load_string_file(const boost::filesystem::path& p, std::string& str); +// Read raw file bytes, strip UTF-8 BOM, normalize to UTF-8 for nlohmann::json::parse. +// On Windows (non-UTF-8 ACP), if the payload is not valid UTF-8 — e.g. profiles saved as Notepad "ANSI" under %AppData%/.../system — reinterpret from the system code page to UTF-8. +std::string read_text_file_for_json_parse(const boost::filesystem::path& path); + bool check_layer_id_pattern(const std::string& pattern, int layer_id); } // namespace Slic3r diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 01aaf09f560..b0b55cca422 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -2,6 +2,7 @@ #include "I18N.hpp" #include +#include #include #include #include @@ -9,6 +10,8 @@ #include #include #include +#include +#include #include "format.hpp" #include "Platform.hpp" @@ -60,11 +63,14 @@ #include #include #include +#include #include #include #include #include +#include "nlohmann/json.hpp" + // We are using quite an old TBB 2017 U7, which does not support global control API officially. // Before we update our build servers, let's use the old API, which is deprecated in up to date TBB. #include @@ -220,7 +226,13 @@ static std::string g_resources_dir; void set_resources_dir(const std::string &dir) { +#ifdef WIN32 + // `dir` is UTF-8 from wx/path conversion. Store as-is; do not round-trip + // through path(wstring).string() which uses the process narrow code page. + g_resources_dir = dir; +#else g_resources_dir = dir; +#endif } const std::string& resources_dir() @@ -228,6 +240,17 @@ const std::string& resources_dir() return g_resources_dir; } +boost::filesystem::path resources_dir_path() +{ +#ifdef WIN32 + if (g_resources_dir.empty()) + return {}; + return boost::filesystem::path(boost::nowide::widen(g_resources_dir)); +#else + return boost::filesystem::path(g_resources_dir); +#endif +} + //BBS: add temporary dir static std::string g_temporary_dir; void set_temporary_dir(const std::string &dir) @@ -282,10 +305,22 @@ static std::string g_data_dir; void set_data_dir(const std::string &dir) { +#ifdef WIN32 + // `dir` is UTF-8 from wx (e.g. GetUserDataDir().ToUTF8()). Do not round-trip through + // path(wstring).string() - that uses the process narrow code page and corrupts CJK profile paths. + g_data_dir = dir; + if (!g_data_dir.empty()) { + const boost::filesystem::path p(boost::nowide::widen(g_data_dir)); + if (!boost::filesystem::exists(p)) + boost::filesystem::create_directories(p); + } +#else + // On non-Windows platforms, dir is already in the native encoding (UTF-8). g_data_dir = dir; if (!g_data_dir.empty() && !boost::filesystem::exists(g_data_dir)) { boost::filesystem::create_directory(g_data_dir); } +#endif } const std::string& data_dir() @@ -293,9 +328,22 @@ const std::string& data_dir() return g_data_dir; } +boost::filesystem::path data_dir_path() +{ +#ifdef WIN32 + if (g_data_dir.empty()) + return {}; + return boost::filesystem::path(boost::nowide::widen(g_data_dir)); +#else + return boost::filesystem::path(g_data_dir); +#endif +} + std::string custom_shapes_dir() { - return (boost::filesystem::path(g_data_dir) / "shapes").string(); + if (g_data_dir.empty()) + return {}; + return g_data_dir + "/shapes"; } static std::atomic debug_out_path_called(false); @@ -303,7 +351,7 @@ static std::atomic debug_out_path_called(false); std::string debug_out_path(const char *name, ...) { //static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/"; - auto svg_folder = boost::filesystem::path(g_data_dir) / "SVG/"; + auto svg_folder = data_dir_path() / "SVG/"; if (! debug_out_path_called.exchange(true)) { if (!boost::filesystem::exists(svg_folder)) { boost::filesystem::create_directory(svg_folder); @@ -320,9 +368,13 @@ std::string debug_out_path(const char *name, ...) std::string buf(buffer); if (size_t pos = buf.find_first_of('/'); pos != std::string::npos) { std::string sub_dir = buf.substr(0, pos); - std::filesystem::create_directory(svg_folder.string() + sub_dir); + boost::filesystem::create_directories(svg_folder / sub_dir); } - return svg_folder.string() + std::string(buffer); +#ifdef WIN32 + return boost::nowide::narrow((svg_folder / std::string(buffer)).wstring()); +#else + return (svg_folder / std::string(buffer)).string(); +#endif } namespace logging = boost::log; @@ -341,14 +393,20 @@ void set_log_path_and_level(const std::string& file, unsigned int level) #endif //BBS log file at C:\\Users\\[yourname]\\AppData\\Roaming\\Snapmaker_Orca\\log\\[log_filename].log - auto log_folder = boost::filesystem::path(g_data_dir) / "log"; + auto log_folder = data_dir_path() / "log"; if (!boost::filesystem::exists(log_folder)) { boost::filesystem::create_directories(log_folder); } auto full_path = (log_folder / file).make_preferred(); +#ifdef WIN32 + const std::string log_file_pattern = boost::nowide::narrow(full_path.wstring()) + ".%N"; +#else + const std::string log_file_pattern = full_path.string() + ".%N"; +#endif + g_log_sink = boost::log::add_file_log( - keywords::file_name = full_path.string() + ".%N", + keywords::file_name = log_file_pattern, keywords::rotation_size = 100 * 1024 * 1024, keywords::format = ( @@ -1535,17 +1593,27 @@ bool copy_directory_recursively(const boost::filesystem::path &source, const boo std::string error_message; for (auto &dir_entry : boost::filesystem::directory_iterator(source)) { + const boost::filesystem::path dest_child = target / dir_entry.path().filename(); +#ifdef WIN32 + const std::string name = boost::nowide::narrow(dir_entry.path().filename().wstring()); +#else const std::string name = dir_entry.path().filename().string(); +#endif if (boost::filesystem::is_directory(dir_entry)) { - if (!copy_directory_recursively(dir_entry, target / name, filter)) + if (!copy_directory_recursively(dir_entry, dest_child, filter)) return false; } else { if (filter && filter(name)) continue; +#ifdef WIN32 + const std::string source_file = boost::nowide::narrow(dir_entry.path().wstring()); + const std::string target_file = boost::nowide::narrow(dest_child.wstring()); +#else const std::string source_file = dir_entry.path().string(); - const std::string target_file = (target / name).string(); - const CopyFileResult cfr = copy_file(source_file, target_file, error_message, false); + const std::string target_file = dest_child.string(); +#endif + const CopyFileResult cfr = copy_file(source_file, target_file, error_message, false); if (cfr != CopyFileResult::SUCCESS) { BOOST_LOG_TRIVIAL(error) << "Copying failed(" << static_cast(cfr) << "): " << error_message << " (" << source_file << " -> " << target_file << ")"; @@ -1608,43 +1676,157 @@ bool atomic_replace_directory( return false; } - { + // Try to rename target -> backup, with retry and fallback for Windows file locks. + bool target_existed = fs::exists(target); + bool backup_done = false; + + auto try_rename_with_retry = [](const fs::path &from, const fs::path &to, const char *operation) -> bool { boost::system::error_code ec; - fs::rename(target, backup, ec); - if (ec && ec != boost::system::errc::no_such_file_or_directory) { - BOOST_LOG_TRIVIAL(error) << Slic3r::format("atomic_replace_directory: failed to backup %1%: %2%", target, ec.message()); - remove_path(staging); - return false; + fs::rename(from, to, ec); + if (!ec) + return true; + if (ec == boost::system::errc::no_such_file_or_directory) + return true; // source gone = nothing to rename, not a failure + + // On Windows, rename can fail with ACCESS_DENIED when files inside the + // directory are locked by another process (e.g. HTTP server serving + // flutter_web). Retry a few times with increasing backoff — the lock + // may be transient. + BOOST_LOG_TRIVIAL(warning) << Slic3r::format( + "atomic_replace_directory: %1 %2% -> %3% failed (%4%), retrying...", + operation, from, to, ec.message()); + + for (int retry = 0; retry < 5; ++retry) { + std::this_thread::sleep_for(std::chrono::milliseconds(100 * (1 << retry))); + ec.clear(); + fs::rename(from, to, ec); + if (!ec) { + BOOST_LOG_TRIVIAL(info) << Slic3r::format( + "atomic_replace_directory: %1 succeeded after %2% retry(ies)", + operation, retry + 1); + return true; + } + if (ec == boost::system::errc::no_such_file_or_directory) + return true; } + BOOST_LOG_TRIVIAL(warning) << Slic3r::format( + "atomic_replace_directory: %1 %2% -> %3% failed after retries: %4%", + operation, from, to, ec.message()); + return false; + }; + + if (target_existed) { + backup_done = try_rename_with_retry(target, backup, "backup"); + if (!backup_done) { + // Rename failed even after retries (likely locked files on Windows). + // Fallback: copy target to backup file-by-file, then delete target. + BOOST_LOG_TRIVIAL(warning) << Slic3r::format( + "atomic_replace_directory: rename backup failed, falling back to copy+delete for %1%", target); + if (copy_directory_recursively(target, backup)) { + boost::system::error_code ec; + fs::remove_all(target, ec); + if (!ec || !fs::exists(target)) { + backup_done = true; + BOOST_LOG_TRIVIAL(info) << "atomic_replace_directory: copy+delete backup succeeded"; + } else { + // Target still exists after copy — can't proceed with rename activation. + // Fall through to direct-overwrite strategy below. + BOOST_LOG_TRIVIAL(warning) << Slic3r::format( + "atomic_replace_directory: could not delete target after copy backup: %1%", ec.message()); + remove_path(backup); + } + } else { + BOOST_LOG_TRIVIAL(warning) << "atomic_replace_directory: copy backup also failed"; + } + } + } else { + backup_done = true; // no existing target to backup + } + + if (backup_done) { + // Normal path: activate staging by renaming it to target. + if (try_rename_with_retry(staging, target, "activate")) { + remove_path(backup); + BOOST_LOG_TRIVIAL(info) << Slic3r::format("atomic_replace_directory: replaced %1%", target); + return true; + } + + // Activate rename failed — try to restore from backup. + BOOST_LOG_TRIVIAL(error) << Slic3r::format("atomic_replace_directory: failed to activate %1%", target); + if (fs::exists(backup)) { + boost::system::error_code ec2; + fs::rename(backup, target, ec2); + if (ec2) + BOOST_LOG_TRIVIAL(error) << Slic3r::format("atomic_replace_directory: failed to restore %1% from backup: %2%", + target, ec2.message()); + } + remove_path(staging); + return false; } + // Last-resort fallback: we could not backup the target (files are locked). + // Copy staging content directly into target, overwriting file-by-file. + // We cannot use copy_directory_recursively() here because it calls + // remove_all() on the target first, which would also fail on locked files. + // Instead, iterate staging and copy each file individually into the target. + BOOST_LOG_TRIVIAL(warning) << Slic3r::format( + "atomic_replace_directory: attempting direct overwrite from staging to %1%", target); + { - boost::system::error_code ec; - fs::rename(staging, target, ec); - if (ec) { - BOOST_LOG_TRIVIAL(error) << Slic3r::format("atomic_replace_directory: failed to activate %1%: %2%", target, ec.message()); - if (fs::exists(backup)) { - boost::system::error_code ec2; - fs::rename(backup, target, ec2); - if (ec2) - BOOST_LOG_TRIVIAL(error) << Slic3r::format("atomic_replace_directory: failed to restore %1% from backup: %2%", - target, ec2.message()); + bool direct_ok = true; + if (!fs::exists(target)) + fs::create_directories(target); + + for (auto &dir_entry : fs::recursive_directory_iterator(staging)) { + const fs::path rel_path = fs::relative(dir_entry.path(), staging); + const fs::path dest_path = target / rel_path; + + if (fs::is_directory(dir_entry)) { + boost::system::error_code ec; + fs::create_directories(dest_path, ec); + if (ec) { + BOOST_LOG_TRIVIAL(warning) << Slic3r::format( + "atomic_replace_directory: failed to create directory %1%: %2%", + dest_path, ec.message()); + direct_ok = false; + } + } else { + // Skip filtered files + std::string filename = dir_entry.path().filename().string(); + if (filter && filter(filename)) + continue; + + boost::system::error_code ec; + fs::copy_file(dir_entry.path(), dest_path, + fs::copy_option::overwrite_if_exists, ec); + if (ec) { + BOOST_LOG_TRIVIAL(warning) << Slic3r::format( + "atomic_replace_directory: failed to copy %1% -> %2%: %3%", + dir_entry.path(), dest_path, ec.message()); + direct_ok = false; + } } + } + + if (direct_ok) { remove_path(staging); - return false; + BOOST_LOG_TRIVIAL(info) << Slic3r::format( + "atomic_replace_directory: direct-overwrite replaced %1%", target); + return true; } } - remove_path(backup); - BOOST_LOG_TRIVIAL(info) << Slic3r::format("atomic_replace_directory: replaced %1%", target); - return true; + BOOST_LOG_TRIVIAL(error) << Slic3r::format( + "atomic_replace_directory: direct overwrite also failed for %1%", target); + remove_path(staging); + return false; } void save_string_file(const boost::filesystem::path& p, const std::string& str) { boost::nowide::ofstream file; file.exceptions(std::ios_base::failbit | std::ios_base::badbit); - file.open(p.generic_string(), std::ios_base::binary); + file.open(p, std::ios_base::binary); file.write(str.c_str(), str.size()); } @@ -1652,10 +1834,94 @@ void load_string_file(const boost::filesystem::path& p, std::string& str) { boost::nowide::ifstream file; file.exceptions(std::ios_base::failbit | std::ios_base::badbit); - file.open(p.generic_string(), std::ios_base::binary); - std::size_t sz = static_cast(boost::filesystem::file_size(p)); - str.resize(sz, '\0'); - file.read(&str[0], sz); + file.open(p, std::ios_base::binary); + // Size from the opened stream (matches handle; avoids a separate file_size vs. open race). + file.seekg(0, std::ios_base::end); + const std::streamoff ssize = static_cast(file.tellg()); + if (ssize < 0) + throw std::runtime_error("load_string_file: could not determine file size"); + file.seekg(0, std::ios_base::beg); + const std::size_t sz = static_cast(ssize); + str.resize(sz); + // Do not call read(&str[0], 0): subscript on empty string is undefined behavior. + if (sz > 0) + file.read(str.data(), static_cast(sz)); +} + +#ifdef WIN32 +namespace { +static bool slic3r_buffer_is_strict_utf8(const char *data, size_t len) +{ + if (len == 0) + return true; + const int n = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, data, static_cast(len), nullptr, 0); + return n > 0; +} + +static std::string slic3r_codepage_bytes_to_utf8(const std::string &s, UINT codepage) +{ + if (s.empty()) + return s; + const int wlen = ::MultiByteToWideChar(codepage, 0, s.data(), static_cast(s.size()), nullptr, 0); + if (wlen <= 0) + return s; + std::wstring w(static_cast(wlen), 0); + if (::MultiByteToWideChar(codepage, 0, s.data(), static_cast(s.size()), w.data(), wlen) <= 0) + return s; + const int u8len = ::WideCharToMultiByte(CP_UTF8, 0, w.data(), wlen, nullptr, 0, nullptr, nullptr); + if (u8len <= 0) + return s; + std::string out(static_cast(u8len), 0); + if (::WideCharToMultiByte(CP_UTF8, 0, w.data(), wlen, out.data(), u8len, nullptr, nullptr) <= 0) + return s; + return out; +} + +// Preset/vendor JSON is often UTF-8, but Windows "ANSI" saves may be GBK (CP936) or the current ACP. +// nlohmann::json requires UTF-8. If bytes are already strict UTF-8, return as-is (do not re-decode). +// Otherwise try GBK then ACP and pick the first strict UTF-8 string that json::accept agrees with. +static std::string resolve_windows_json_payload(std::string raw) +{ + if (slic3r_buffer_is_strict_utf8(raw.data(), raw.size())) + return raw; + + const auto utf8_json_ok = [](const std::string &s) { + return slic3r_buffer_is_strict_utf8(s.data(), s.size()) && nlohmann::json::accept(s); + }; + + const std::string as_gbk = slic3r_codepage_bytes_to_utf8(raw, 936); + if (utf8_json_ok(as_gbk)) + return as_gbk; + + const std::string as_acp = slic3r_codepage_bytes_to_utf8(raw, CP_ACP); + if (utf8_json_ok(as_acp)) + return as_acp; + + // No candidate is both valid UTF-8 and JSON; avoid passing invalid UTF-8 into json::parse. + if (slic3r_buffer_is_strict_utf8(as_gbk.data(), as_gbk.size())) + return as_gbk; + if (slic3r_buffer_is_strict_utf8(as_acp.data(), as_acp.size())) + return as_acp; + BOOST_LOG_TRIVIAL(warning) << "resolve_windows_json_payload: could not decode file as UTF-8, GBK, or ACP; passing raw bytes to json::parse"; + return raw; +} +} // namespace +#endif + +std::string read_text_file_for_json_parse(const boost::filesystem::path &path) +{ + std::string str; + load_string_file(path, str); + if (str.size() >= 3 && + static_cast(str[0]) == 0xEF && + static_cast(str[1]) == 0xBB && + static_cast(str[2]) == 0xBF) { + str.erase(0, 3); + } +#ifdef WIN32 + str = resolve_windows_json_payload(std::move(str)); +#endif + return str; } // pattern string supprt these pattern: " diff --git a/src/sentry_wrapper/SentryWrapper.cpp b/src/sentry_wrapper/SentryWrapper.cpp index 3299b6ae4d8..8b2ff7ea48d 100644 --- a/src/sentry_wrapper/SentryWrapper.cpp +++ b/src/sentry_wrapper/SentryWrapper.cpp @@ -117,6 +117,31 @@ void initSentryEx() wchar_t exeDir[MAX_PATH_EXTENDED]; DWORD pathLen = ::GetModuleFileNameW(nullptr, exeDir, MAX_PATH_EXTENDED); + + auto wstringTostring = [](const std::wstring& wTmpStr) -> std::string { + if (wTmpStr.empty()) + return std::string(); + + int len = WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, nullptr, 0, nullptr, nullptr); + if (len <= 0) { + std::cout << "WideCharToMultiByte failed, error: " << GetLastError(); + return std::string(); + } + + // len includes the null terminator, so buffer must be at least len bytes. + std::string desStr(static_cast(len), '\0'); + int result = WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, desStr.data(), len, nullptr, nullptr); + if (result != len) { + std::cout << "WideCharToMultiByte conversion failed, error: " << GetLastError(); + return std::string(); + } + + // Keep std::string content without trailing null terminator. + desStr.resize(static_cast(len - 1)); + + return desStr; + }; + // GetModuleFileNameW returns 0 on error, or the number of characters written (excluding null terminator) // If return value equals buffer size, the path was truncated if (pathLen == 0) { @@ -153,58 +178,21 @@ void initSentryEx() std::wstring desDir = wsDmpDir + L"crashpad_handler.exe"; wsDmpDir += L"dump"; - auto wstringTostring = [](const std::wstring& wTmpStr) -> std::string { - if (wTmpStr.empty()) - return std::string(); - - int len = WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, nullptr, 0, nullptr, nullptr); - if (len <= 0) { - std::cout<< "WideCharToMultiByte failed, error: " << GetLastError(); - return std::string(); - } - - // Allocate buffer with size len (includes null terminator) - std::string desStr; - desStr.resize(len - 1); // Reserve space excluding null terminator - int result = WideCharToMultiByte(CP_UTF8, 0, wTmpStr.c_str(), -1, &desStr[0], len, nullptr, nullptr); - if (result == 0 || result != len) { - std::cout<< "WideCharToMultiByte conversion failed, error: " << GetLastError(); - return std::string(); - } - - // Remove null terminator if present (safely check before accessing) - if (!desStr.empty() && desStr.back() == '\0') - desStr.pop_back(); - - return desStr; - }; - handlerDir = wstringTostring(desDir); } // Get LocalAppData folder path PWSTR pszPath = nullptr; - char* path = nullptr; - size_t pathLength = 0; HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &pszPath); if (SUCCEEDED(hr) && pszPath != nullptr) { - // Calculate required buffer size first size_t wcsLen = wcslen(pszPath); - if (wcsLen > 0 && wcsLen < SIZE_MAX / 3) { // Check for overflow - // Allocate buffer with extra space for safety - size_t requiredSize = wcsLen * 3 + 1; // UTF-8 can be up to 3 bytes per wchar - path = new (std::nothrow) char[requiredSize](); - - if (path != nullptr) { - errno_t err = wcstombs_s(&pathLength, path, requiredSize, pszPath, _TRUNCATE); - if (err != 0) { - std::cout<< "wcstombs_s failed, error: " << err; - delete[] path; - path = nullptr; - } + if (wcsLen > 0 && wcsLen < SIZE_MAX / 3) { + std::string localAppDataPath = wstringTostring(pszPath); + if (!localAppDataPath.empty()) { + dataBaseDir = localAppDataPath + "\\Snapmaker_Orca\\"; } else { - std::cout<< "Failed to allocate memory for path conversion"; + std::cout << "Failed to convert FOLDERID_LocalAppData path to UTF-8"; } } else if (wcsLen == 0) { std::cout<< "SHGetKnownFolderPath returned empty path"; @@ -223,17 +211,11 @@ void initSentryEx() } } - if (path != nullptr) { - std::string filePath = path; - std::string appName = "\\" + std::string("Snapmaker_Orca\\"); - dataBaseDir = filePath + appName; - delete[] path; - path = nullptr; - } else { + if (dataBaseDir.empty()) { // Fallback: use temp directory - char tempPath[MAX_PATH]; - if (GetTempPathA(MAX_PATH, tempPath) != 0) { - dataBaseDir = std::string(tempPath) + "Snapmaker_Orca\\"; + WCHAR tempPath[MAX_PATH]; + if (GetTempPathW(MAX_PATH, tempPath) != 0) { + dataBaseDir = wstringTostring(tempPath) + "Snapmaker_Orca\\"; std::cout<< "Using temp directory as fallback for Sentry data: " << dataBaseDir; } else { dataBaseDir = ""; diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 3617a731d46..4f8264a7405 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -272,8 +273,8 @@ bool Snapshot::equal_to_active(const AppConfig &app_config) const } // 2) Check, whether this snapshot references the same set of ini files as the current state. - boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); - boost::filesystem::path snapshot_dir = boost::filesystem::path(Slic3r::data_dir()) / SLIC3R_SNAPSHOTS_DIR / this->id; + boost::filesystem::path data_dir = Slic3r::data_dir_path(); + boost::filesystem::path snapshot_dir = Slic3r::data_dir_path() / SLIC3R_SNAPSHOTS_DIR / this->id; for (const char *subdir : snapshot_subdirs) { boost::filesystem::path path1 = data_dir / subdir; boost::filesystem::path path2 = snapshot_dir / subdir; @@ -291,8 +292,14 @@ bool Snapshot::equal_to_active(const AppConfig &app_config) const if (files1 != files2) return false; for (const std::string &filename : files1) { +#ifdef WIN32 + // path::string() returns ACP on Windows; use nowide::narrow(wstring()) for UTF-8. + FILE *f1 = boost::nowide::fopen(boost::nowide::narrow((path1 / filename).wstring()).c_str(), "rb"); + FILE *f2 = boost::nowide::fopen(boost::nowide::narrow((path2 / filename).wstring()).c_str(), "rb"); +#else FILE *f1 = boost::nowide::fopen((path1 / filename).string().c_str(), "rb"); FILE *f2 = boost::nowide::fopen((path2 / filename).string().c_str(), "rb"); +#endif bool same = true; if (f1 && f2) { char buf1[4096]; @@ -397,7 +404,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path) const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment) { - boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path data_dir = Slic3r::data_dir_path(); boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir(); // 1) Prepare the snapshot structure. @@ -436,7 +443,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: PresetBundle bundle; //BBS: change directoties by design //bundle.load_configbundle((data_dir / PRESET_SYSTEM_DIR / (cfg.name + ".ini")).string(), PresetBundle::LoadConfigBundleAttribute::LoadVendorOnly, ForwardCompatibilitySubstitutionRule::EnableSilent); - bundle.load_vendor_configs_from_json((data_dir/PRESET_SYSTEM_DIR).string(), cfg.name, PresetBundle::LoadConfigBundleAttribute::LoadVendorOnly, ForwardCompatibilitySubstitutionRule::EnableSilent); + bundle.load_vendor_configs_from_json((data_dir/PRESET_SYSTEM_DIR), cfg.name, PresetBundle::LoadConfigBundleAttribute::LoadVendorOnly, ForwardCompatibilitySubstitutionRule::EnableSilent); for (const auto &vp : bundle.vendors) if (vp.second.id == cfg.name) cfg.version.config_version = vp.second.config_version; @@ -480,7 +487,7 @@ const Snapshot& SnapshotDB::restore_snapshot(const std::string &id, AppConfig &a void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_config) { - boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path data_dir = Slic3r::data_dir_path(); boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir(); boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; // Remove existing ini files and restore the ini files from the snapshot. @@ -543,7 +550,7 @@ SnapshotDB::const_iterator SnapshotDB::snapshot(const std::string &id) const boost::filesystem::path SnapshotDB::create_db_dir() { - boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path data_dir = Slic3r::data_dir_path(); boost::filesystem::path snapshots_dir = data_dir / SLIC3R_SNAPSHOTS_DIR; for (const boost::filesystem::path &path : { data_dir, snapshots_dir }) { boost::filesystem::path subdir = path; diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 092594ce517..997c5faab21 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -196,7 +196,7 @@ size_t Index::load(const boost::filesystem::path &path) m_vendor = path.stem().string(); m_path = path; - boost::nowide::ifstream ifs(path.string()); + boost::nowide::ifstream ifs(path); std::string line; size_t idx_line = 0; Version ver; @@ -306,7 +306,7 @@ Index::const_iterator Index::recommended() const std::vector Index::load_db() { - boost::filesystem::path cache_dir = boost::filesystem::path(Slic3r::data_dir()) / "ota"; + boost::filesystem::path cache_dir = Slic3r::data_dir_path() / "ota"; std::vector index_db; std::string errors_cummulative; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index db62413c9ce..4f10611a261 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -72,16 +72,18 @@ bool Bundle::load(fs::path source_path, bool ais_in_resources, bool ais_sm_bundl this->is_in_resources = ais_in_resources; this->is_sm_bundle = ais_sm_bundle; + // path::string() returns ACP on Windows; for non-ASCII-safe checks use + // boost::filesystem::path operations and boost::nowide::narrow for name extraction. std::string path_string = source_path.string(); - std::string parent_path = source_path.parent_path().string(); + const boost::filesystem::path parent_path = source_path.parent_path(); //BBS: add json logic for vendor bundles - std::string vendor_name = source_path.filename().string(); - if (Slic3r::is_json_file(path_string)) { - // Remove the .json suffix. - vendor_name.erase(vendor_name.size() - 5); - } - else + if (!boost::iequals(source_path.extension().string(), ".json")) return false; +#ifdef WIN32 + std::string vendor_name = boost::nowide::narrow(source_path.stem().wstring()); +#else + std::string vendor_name = source_path.stem().string(); +#endif // Throw when parsing invalid configuration. Only valid configuration is supposed to be provided over the air. //BBS: add json logic for vendor bundles @@ -120,8 +122,8 @@ BundleMap BundleMap::load() BundleMap res; //BBS: change directories by design - const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR).make_preferred(); - const auto rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + const auto vendor_dir = (Slic3r::data_dir_path() / PRESET_SYSTEM_DIR).make_preferred(); + const auto rsrc_vendor_dir = (Slic3r::resources_dir_path() / "profiles").make_preferred(); // SM_FEATURE auto sm_bundle_path = (vendor_dir / PresetBundle::SM_BUNDLE).replace_extension(".json"); @@ -143,8 +145,12 @@ BundleMap BundleMap::load() for (auto dir : { &vendor_dir, &rsrc_vendor_dir }) { for (const auto &dir_entry : boost::filesystem::directory_iterator(*dir)) { //BBS: add json logic for vendor bundle - if (Slic3r::is_json_file(dir_entry.path().string())) { + if (boost::iequals(dir_entry.path().extension().string(), ".json")) { +#ifdef WIN32 + std::string id = boost::nowide::narrow(dir_entry.path().stem().wstring()); // stem() = filename() without the trailing ".json" part +#else std::string id = dir_entry.path().stem().string(); // stem() = filename() without the trailing ".json" part +#endif // Don't load this bundle if we've already loaded it. if (res.find(id) != res.end()) { continue; } @@ -1857,10 +1863,15 @@ void ConfigWizard::priv::load_vendors() } else { // In case of legacy datadir, try to guess the preference based on the printer preset files that are present //BBS: change directories by design - const auto printer_dir = fs::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR / PRESET_PRINTER_NAME; + const auto printer_dir = Slic3r::data_dir_path() / PRESET_SYSTEM_DIR / PRESET_PRINTER_NAME; for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir)) if (Slic3r::is_ini_file(dir_entry)) { - auto needle = legacy_preset_map.find(dir_entry.path().filename().string()); + auto needle = legacy_preset_map.find( +#ifdef WIN32 + boost::nowide::narrow(dir_entry.path().filename().wstring())); +#else + dir_entry.path().filename().string()); +#endif if (needle == legacy_preset_map.end()) { continue; } const auto &model = needle->second.first; diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp index 78e14f1bed7..c7faf8d3aae 100644 --- a/src/slic3r/GUI/CreatePresetsDialog.cpp +++ b/src/slic3r/GUI/CreatePresetsDialog.cpp @@ -8,8 +8,10 @@ #include #include #include +#include #include #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Utils.hpp" #include "I18N.hpp" #include "GUI_App.hpp" #include "MsgDialog.hpp" @@ -2089,7 +2091,7 @@ bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(Pre { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " is load template: "<< just_template; std::string selected_vendor_id; - std::string preset_path; + boost::filesystem::path preset_dir; if (m_printer_preset) { delete m_printer_preset; m_printer_preset = nullptr; @@ -2119,13 +2121,13 @@ bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(Pre } else { selected_vendor_id = m_printer_preset_vendor_selected.id; - if (boost::filesystem::exists(boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR / selected_vendor_id)) { - preset_path = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR).string(); - } else if (boost::filesystem::exists(boost::filesystem::path(Slic3r::resources_dir()) / "profiles" / selected_vendor_id)) { - preset_path = (boost::filesystem::path(Slic3r::resources_dir()) / "profiles").string(); + if (boost::filesystem::exists(Slic3r::data_dir_path() / PRESET_SYSTEM_DIR / selected_vendor_id)) { + preset_dir = Slic3r::data_dir_path() / PRESET_SYSTEM_DIR; + } else if (boost::filesystem::exists(Slic3r::resources_dir_path() / "profiles" / selected_vendor_id)) { + preset_dir = Slic3r::resources_dir_path() / "profiles"; } - if (preset_path.empty()) { + if (preset_dir.empty()) { BOOST_LOG_TRIVIAL(info) << "Preset path was not found"; MessageDialog dlg(this, _L("Preset path was not found, please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); @@ -2134,7 +2136,7 @@ bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(Pre } try { - temp_preset_bundle.load_vendor_configs_from_json(preset_path, selected_vendor_id, PresetBundle::LoadConfigBundleAttribute::LoadSystem, + temp_preset_bundle.load_vendor_configs_from_json(preset_dir, selected_vendor_id, PresetBundle::LoadConfigBundleAttribute::LoadSystem, ForwardCompatibilitySubstitutionRule::EnableSilent); } catch (...) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "load vendor fonfigs form json failed"; @@ -2185,11 +2187,11 @@ bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(Pre temp_preset_bundle.update_compatible(PresetSelectCompatibleType::Always); } else { selected_vendor_id = PRESET_TEMPLATE_DIR; - preset_path.clear(); - if (boost::filesystem::exists(boost::filesystem::path(Slic3r::resources_dir()) / PRESET_PROFILES_TEMOLATE_DIR / selected_vendor_id)) { - preset_path = (boost::filesystem::path(Slic3r::resources_dir()) / PRESET_PROFILES_TEMOLATE_DIR).string(); + preset_dir.clear(); + if (boost::filesystem::exists(Slic3r::resources_dir_path() / PRESET_PROFILES_TEMOLATE_DIR / selected_vendor_id)) { + preset_dir = Slic3r::resources_dir_path() / PRESET_PROFILES_TEMOLATE_DIR; } - if (preset_path.empty()) { + if (preset_dir.empty()) { BOOST_LOG_TRIVIAL(info) << "Preset path was not found"; MessageDialog dlg(this, _L("Preset path was not found, please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES_NO | wxYES_DEFAULT | wxCENTRE); @@ -2197,7 +2199,7 @@ bool CreatePrinterPresetDialog::load_system_and_user_presets_with_curr_model(Pre return false; } try { - temp_preset_bundle.load_vendor_configs_from_json(preset_path, selected_vendor_id, PresetBundle::LoadConfigBundleAttribute::LoadSystem, + temp_preset_bundle.load_vendor_configs_from_json(preset_dir, selected_vendor_id, PresetBundle::LoadConfigBundleAttribute::LoadSystem, ForwardCompatibilitySubstitutionRule::EnableSilent); } catch (...) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "load template vendor configs form json failed"; @@ -3362,7 +3364,7 @@ ExportConfigsDialog::~ExportConfigsDialog() } // Delete the Temp folder - boost::filesystem::path temp_folder(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp"); + const boost::filesystem::path temp_folder = Slic3r::data_dir_path() / PRESET_USER_DIR / "Temp"; if (boost::filesystem::exists(temp_folder)) boost::filesystem::remove_all(temp_folder); } @@ -3417,8 +3419,12 @@ bool ExportConfigsDialog::earse_preset_fields_for_safe(Preset *preset) { if (preset->type != Preset::Type::TYPE_PRINTER) return true; - boost::filesystem::path file_path(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp" + "/" + (preset->name + ".json")); + boost::filesystem::path file_path = Slic3r::data_dir_path() / PRESET_USER_DIR / "Temp" / (preset->name + ".json"); +#ifdef WIN32 + preset->file = boost::nowide::narrow(file_path.make_preferred().wstring()); +#else preset->file = file_path.make_preferred().string(); +#endif DynamicPrintConfig &config = preset->config; config.erase("print_host"); @@ -4091,11 +4097,11 @@ wxBoxSizer *ExportConfigsDialog::create_select_printer(wxWindow *parent) void ExportConfigsDialog::data_init() { // Delete the Temp folder - boost::filesystem::path folder(data_dir() + "/" + PRESET_USER_DIR + "/" + "Temp"); + const boost::filesystem::path folder = Slic3r::data_dir_path() / PRESET_USER_DIR / "Temp"; if (boost::filesystem::exists(folder)) boost::filesystem::remove_all(folder); boost::system::error_code ec; - boost::filesystem::path user_folder(data_dir() + "/" + PRESET_USER_DIR); + const boost::filesystem::path user_folder = Slic3r::data_dir_path() / PRESET_USER_DIR; bool temp_folder_exist = true; if (!boost::filesystem::exists(user_folder)) { if (!boost::filesystem::create_directories(user_folder, ec)) { diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index d4ece5ebbe0..daa48a79521 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "fast_float/fast_float.h" #include @@ -457,8 +458,13 @@ std::string MachineObject::get_printer_thumbnail_img_str() std::string img_url; if (!img_str.empty()) { - img_url = Slic3r::resources_dir() + "\\printers\\image\\" + img_str; - if (fs::exists(img_url + ".svg")) { + auto img_path = Slic3r::resources_dir_path() / "printers" / "image" / img_str; + if (fs::exists(Slic3r::resources_dir_path() / "printers" / "image" / (img_str + ".svg"))) { +#ifdef WIN32 + img_url = boost::nowide::narrow(img_path.wstring()); +#else + img_url = img_path.string(); +#endif return img_url; } else { @@ -6815,8 +6821,8 @@ std::vector DeviceManager::get_resolution_supported(std::string typ { std::vector resolution_supported; - std::string config_file = Slic3r::resources_dir() + "/printers/" + type_str + ".json"; - boost::nowide::ifstream json_file(config_file.c_str()); + auto config_path = Slic3r::resources_dir_path() / "printers" / (type_str + ".json"); + boost::nowide::ifstream json_file(config_path); try { json jj; if (json_file.is_open()) { @@ -6837,8 +6843,8 @@ std::vector DeviceManager::get_resolution_supported(std::string typ std::vector DeviceManager::get_compatible_machine(std::string type_str) { std::vector compatible_machine; - std::string config_file = Slic3r::resources_dir() + "/printers/" + type_str + ".json"; - boost::nowide::ifstream json_file(config_file.c_str()); + auto config_path = Slic3r::resources_dir_path() / "printers" / (type_str + ".json"); + boost::nowide::ifstream json_file(config_path); try { json jj; if (json_file.is_open()) { @@ -6861,7 +6867,8 @@ boost::bimaps::bimap DeviceManager::get_all_model_id_w boost::bimaps::bimap models; std::vector m_files; - wxDir dir(Slic3r::resources_dir() + "/printers/"); + auto printers_dir = Slic3r::resources_dir_path() / "printers"; + wxDir dir(printers_dir.wstring()); if (!dir.IsOpened()) { return models; } @@ -6876,8 +6883,8 @@ boost::bimaps::bimap DeviceManager::get_all_model_id_w for (wxString file : m_files) { if (!file.Lower().ends_with(".json")) continue; - std::string config_file = Slic3r::resources_dir() + "/printers/" + file.ToStdString(); - boost::nowide::ifstream json_file(config_file.c_str()); + auto config_path = Slic3r::resources_dir_path() / "printers" / file.ToStdString(); + boost::nowide::ifstream json_file(config_path); try { json jj; @@ -6906,8 +6913,8 @@ bool DeviceManager::load_filaments_blacklist_config() { filaments_blacklist = json::object(); - std::string config_file = Slic3r::resources_dir() + "/printers/filaments_blacklist.json"; - boost::nowide::ifstream json_file(config_file.c_str()); + auto config_path = Slic3r::resources_dir_path() / "printers" / "filaments_blacklist.json"; + boost::nowide::ifstream json_file(config_path); try { if (json_file.is_open()) { @@ -6915,11 +6922,11 @@ bool DeviceManager::load_filaments_blacklist_config() return true; } else { - BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_file; + BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_path; } } catch (...) { - BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_file; + BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_path; return false; } return true; @@ -6993,8 +7000,8 @@ void DeviceManager::check_filaments_in_blacklist(std::string tag_vendor, std::st std::string DeviceManager::load_gcode(std::string type_str, std::string gcode_file) { - std::string gcode_full_path = Slic3r::resources_dir() + "/printers/" + gcode_file; - std::ifstream gcode(encode_path(gcode_full_path.c_str()).c_str()); + auto gcode_path = Slic3r::resources_dir_path() / "printers" / gcode_file; + boost::nowide::ifstream gcode(gcode_path); try { std::stringstream gcode_str; if (gcode.is_open()) { @@ -7003,7 +7010,7 @@ std::string DeviceManager::load_gcode(std::string type_str, std::string gcode_fi return gcode_str.str(); } } catch(...) { - BOOST_LOG_TRIVIAL(error) << "load gcode file failed, file = " << gcode_file << ", path = " << gcode_full_path; + BOOST_LOG_TRIVIAL(error) << "load gcode file failed, file = " << gcode_file << ", path = " << gcode_path; } diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 85263899e81..2a84e48e21c 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -1172,8 +1172,8 @@ class DeviceManager template static T get_value_from_config(std::string type_str, std::string item){ - std::string config_file = Slic3r::resources_dir() + "/printers/" + type_str + ".json"; - boost::nowide::ifstream json_file(config_file.c_str()); + auto config_path = Slic3r::resources_dir_path() / "printers" / (type_str + ".json"); + boost::nowide::ifstream json_file(config_path); try { json jj; if (json_file.is_open()) { diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e9f4cceddfe..7feb8553c5a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -2188,8 +2189,8 @@ void GUI_App::init_app_config() #ifndef __linux__ std::string data_dir = wxStandardPaths::Get().GetUserDataDir().ToUTF8().data(); //BBS create folder if not exists - data_dir_path = boost::filesystem::path(data_dir); set_data_dir(data_dir); + data_dir_path = Slic3r::data_dir_path(); #else // Since version 2.3, config dir on Linux is in ${XDG_CONFIG_HOME}. // https://github.com/prusa3d/PrusaSlicer/issues/2911 @@ -2197,7 +2198,7 @@ void GUI_App::init_app_config() if (! wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() ) dir = wxFileName::GetHomeDir() + wxS("/.config"); set_data_dir((dir + "/" + GetAppName()).ToUTF8().data()); - data_dir_path = boost::filesystem::path(data_dir()); + data_dir_path = Slic3r::data_dir_path(); #endif if (!boost::filesystem::exists(data_dir_path)){ boost::filesystem::create_directory(data_dir_path); @@ -2205,7 +2206,11 @@ void GUI_App::init_app_config() } // Change current dirtory of application +#ifdef WIN32 + [[maybe_unused]] auto unused_result = ::_wchdir((Slic3r::data_dir_path() / "log").make_preferred().wstring().c_str()); +#else [[maybe_unused]] auto unused_result = chdir(encode_path((Slic3r::data_dir() + "/log").c_str()).c_str()); +#endif } else { m_datadir_redefined = true; } @@ -2270,22 +2275,27 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup) void GUI_App::copy_web_resources() { StartupProfiler profiler("GUI_App::copy_web_resources"); - auto data_web_path = boost::filesystem::path(data_dir()) / "web"; + auto data_web_path = Slic3r::data_dir_path() / "web"; if (!boost::filesystem::exists(data_web_path / "flutter_web")) { copy_bundled_flutter_web(false); profiler.mark("copy flutter_web (missing target)"); } else { - auto source_version_file = boost::filesystem::path(resources_dir()) / "web" / "flutter_web" / "version.json"; + auto source_version_file = Slic3r::resources_dir_path() / "web" / "flutter_web" / "version.json"; auto target_version_file = data_web_path / "flutter_web" / "version.json"; try { boost::property_tree::ptree source_config, target_config; - boost::property_tree::read_json(source_version_file.string(), source_config); - boost::property_tree::read_json(target_version_file.string(), target_config); - std::string source_build_number_str = source_config.get("build_number", "0"); - std::string target_build_number_str = target_config.get("build_number", "0"); - - if (source_build_number_str > target_build_number_str) { + // Use path-aware stream open on Windows to avoid ANSI codepage path corruption. + boost::nowide::ifstream source_ifs(source_version_file); + boost::nowide::ifstream target_ifs(target_version_file); + boost::property_tree::read_json(source_ifs, source_config); + boost::property_tree::read_json(target_ifs, target_config); + const std::string source_version_str = source_config.get("version", "0"); + const std::string target_version_str = target_config.get("version", "0"); + const Semver source_ver = Semver::parse(source_version_str).get_value_or(Semver::zero()); + const Semver target_ver = Semver::parse(target_version_str).get_value_or(Semver::zero()); + + if (target_ver < source_ver) { copy_bundled_flutter_web(true); profiler.mark("copy flutter_web (version upgrade)"); } else { @@ -2300,8 +2310,8 @@ void GUI_App::copy_web_resources() { bool GUI_App::copy_bundled_flutter_web(bool upgrade) { - auto source_path = boost::filesystem::path(resources_dir()) / "web" / "flutter_web"; - auto target_path = boost::filesystem::path(data_dir()) / "web" / "flutter_web"; + auto source_path = Slic3r::resources_dir_path() / "web" / "flutter_web"; + auto target_path = Slic3r::data_dir_path() / "web" / "flutter_web"; if (copy_directory_recursively(source_path, target_path)) return true; @@ -2443,7 +2453,7 @@ std::string GUI_App::get_local_models_path() return local_path; } - auto models_folder = (boost::filesystem::path(data_dir()) / "models"); + auto models_folder = (Slic3r::data_dir_path() / "models"); local_path = models_folder.string(); if (!fs::exists(models_folder)) { @@ -2498,7 +2508,7 @@ int GUI_App::OnExit() // Orca: clean up encrypted bbl network log file if plugin is used // No point to keep them as they are encrypted and can't be used for debugging try { - auto log_folder = boost::filesystem::path(data_dir()) / "log"; + auto log_folder = Slic3r::data_dir_path() / "log"; const std::string filePattern = R"(debug_network_.*\.log\.enc)"; std::regex pattern(filePattern); if (boost::filesystem::exists(log_folder)) { @@ -3317,7 +3327,11 @@ bool GUI_App::on_init_network(bool try_backup) //BBS set cert dir if (m_agent) - m_agent->set_cert_file(resources_dir() + "/cert", "slicer_base64.cer"); +#ifdef WIN32 + m_agent->set_cert_file(boost::nowide::narrow((Slic3r::resources_dir_path() / "cert").make_preferred().wstring()), "slicer_base64.cer"); +#else + m_agent->set_cert_file((Slic3r::resources_dir_path() / "cert").make_preferred().string(), "slicer_base64.cer"); +#endif init_http_extra_header(); @@ -4570,7 +4584,11 @@ std::string GUI_App::handle_web_request(std::string cmd) pt::ptree data_node = root.get_child("data"); boost::optional path = data_node.get_optional("file"); if (path.has_value()) { - std::string Fullpath = resources_dir() + "/web/homepage/model/" + path.value(); +#ifdef WIN32 + std::string Fullpath = boost::nowide::narrow((Slic3r::resources_dir_path() / "web" / "homepage" / "model" / path.value()).make_preferred().wstring()); +#else + std::string Fullpath = (Slic3r::resources_dir_path() / "web" / "homepage" / "model" / path.value()).make_preferred().string(); +#endif this->request_open_project(Fullpath); } diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 87a6d909cb5..72de4fdb2fa 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -598,7 +598,11 @@ wxMenu* MenuFactory::append_submenu_add_handy_model(wxMenu* menu, ModelVolumeTyp is_stringhell = true; } else return; - input_files.push_back((boost::filesystem::path(Slic3r::resources_dir()) / "handy_models" / file_name)); +#ifdef WIN32 + input_files.push_back(boost::nowide::narrow((Slic3r::resources_dir_path() / "handy_models" / file_name).make_preferred().wstring())); +#else + input_files.push_back((Slic3r::resources_dir_path() / "handy_models" / file_name).string()); +#endif plater()->load_files(input_files, LoadStrategy::LoadModel); // Suggest to change settings for stringhell diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index e2ad64f621e..01ac86e2773 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3351,12 +3351,11 @@ std::string concat(std::vector data) { } boost::filesystem::path get_fontlist_cache_path(){ - return boost::filesystem::path(data_dir()) / "cache" / "fonts.cereal"; + return Slic3r::data_dir_path() / "cache" / "fonts.cereal"; } bool store(const Facenames &facenames) { - std::string cache_path = get_fontlist_cache_path().string(); - boost::nowide::ofstream file(cache_path, std::ios::binary); + boost::nowide::ofstream file(get_fontlist_cache_path(), std::ios::binary); ::cereal::BinaryOutputArchive archive(file); std::vector good; good.reserve(facenames.faces.size()); @@ -3369,7 +3368,7 @@ bool store(const Facenames &facenames) { try { archive(data); } catch (const std::exception &ex) { - BOOST_LOG_TRIVIAL(error) << "Failed to write fontlist cache - " << cache_path << ex.what(); + BOOST_LOG_TRIVIAL(error) << "Failed to write fontlist cache - " << get_fontlist_cache_path() << ex.what(); return false; } return true; @@ -3382,7 +3381,7 @@ bool load(Facenames &facenames) { BOOST_LOG_TRIVIAL(warning) << "Fontlist cache - '" << path_str << "' does not exists."; return false; } - boost::nowide::ifstream file(path_str, std::ios::binary); + boost::nowide::ifstream file(path, std::ios::binary); cereal::BinaryInputArchive archive(file); FacenamesSerializer data; diff --git a/src/slic3r/GUI/HMS.cpp b/src/slic3r/GUI/HMS.cpp index f98b9fbb8c7..c94bd78e51e 100644 --- a/src/slic3r/GUI/HMS.cpp +++ b/src/slic3r/GUI/HMS.cpp @@ -1,5 +1,7 @@ #include "HMS.hpp" +#include +#include #include @@ -107,12 +109,12 @@ int HMSQuery::load_from_local(std::string& version_info, std::string hms_type, j return -1; } std::string filename = get_hms_file(hms_type, HMSQuery::hms_language_code()); - auto hms_folder = (boost::filesystem::path(data_dir()) / "hms"); + auto hms_folder = Slic3r::data_dir_path() / "hms"; if (!fs::exists(hms_folder)) fs::create_directory(hms_folder); - std::string dir_str = (hms_folder / filename).make_preferred().string(); - std::ifstream json_file(encode_path(dir_str.c_str())); + auto hms_file = hms_folder / filename; + boost::nowide::ifstream json_file(hms_file); try { if (json_file.is_open()) { json_file >> (*load_json); @@ -140,11 +142,11 @@ int HMSQuery::save_to_local(std::string lang, std::string hms_type, json save_js return -1; } std::string filename = get_hms_file(hms_type,lang); - auto hms_folder = (boost::filesystem::path(data_dir()) / "hms"); + auto hms_folder = Slic3r::data_dir_path() / "hms"; if (!fs::exists(hms_folder)) fs::create_directory(hms_folder); - std::string dir_str = (hms_folder / filename).make_preferred().string(); - std::ofstream json_file(encode_path(dir_str.c_str())); + auto hms_file = hms_folder / filename; + boost::nowide::ofstream json_file(hms_file); if (json_file.is_open()) { json_file << std::setw(4) << save_json << std::endl; json_file.close(); diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index fa26cd17a2f..f6349ce303a 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -67,7 +67,7 @@ namespace { void write_used_binary(const std::vector& ids) { - boost::nowide::ofstream file((boost::filesystem::path(data_dir()) / "user" / "hints.cereal").string(), std::ios::binary); + boost::nowide::ofstream file(Slic3r::data_dir_path() / "user" / "hints.cereal", std::ios::binary); cereal::BinaryOutputArchive archive(file); HintsCerealData cd{ ids }; try @@ -81,12 +81,12 @@ namespace { } void read_used_binary(std::vector& ids) { - boost::filesystem::path path(boost::filesystem::path(data_dir()) / "user" / "hints.cereal"); + boost::filesystem::path path(Slic3r::data_dir_path() / "user" / "hints.cereal"); if (!boost::filesystem::exists(path)) { BOOST_LOG_TRIVIAL(warning) << "Failed to load to hints.cereal. File does not exists. " << path.string(); return; } - boost::nowide::ifstream file(path.string()); + boost::nowide::ifstream file(path); cereal::BinaryInputArchive archive(file); HintsCerealData cd; try @@ -311,7 +311,7 @@ void HintDatabase::reinit() } void HintDatabase::init() { - load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini")); + load_hints_from_file(std::move(Slic3r::resources_dir_path() / "data" / "hints.ini")); m_initialized = true; init_random_hint_id(); } @@ -324,7 +324,7 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) { namespace pt = boost::property_tree; pt::ptree tree; - boost::nowide::ifstream ifs(path.string()); + boost::nowide::ifstream ifs(path); try { pt::read_ini(ifs, tree); } diff --git a/src/slic3r/GUI/HttpServer.cpp b/src/slic3r/GUI/HttpServer.cpp index 187404a2de0..af25ec2d312 100644 --- a/src/slic3r/GUI/HttpServer.cpp +++ b/src/slic3r/GUI/HttpServer.cpp @@ -769,7 +769,7 @@ std::string HttpServer::map_url_to_file_path(const std::string& url) return decoded; } - auto data_web_path = boost::filesystem::path(data_dir()) / "web"; + auto data_web_path = Slic3r::data_dir_path() / "web"; if (!boost::filesystem::exists(data_web_path / "flutter_web")) { if (!GUI::wxGetApp().copy_bundled_flutter_web(false)) GUI::wxGetApp().try_notify_flutter_web_copy_failure(); diff --git a/src/slic3r/GUI/InstanceCheck.cpp b/src/slic3r/GUI/InstanceCheck.cpp index fb6b86327fe..111d59b7791 100644 --- a/src/slic3r/GUI/InstanceCheck.cpp +++ b/src/slic3r/GUI/InstanceCheck.cpp @@ -194,8 +194,10 @@ namespace instance_check_internal //BOOST_LOG_TRIVIAL(debug) << "shuting down with lockfile: " << l_created_lockfile; if (s_created_lockfile) { - std::string path = data_dir() + "/cache/" + GUI::wxGetApp().get_instance_hash_string() + ".lock"; - if( remove( path.c_str() ) != 0 ) + boost::filesystem::path path = Slic3r::data_dir_path() / "cache" / (GUI::wxGetApp().get_instance_hash_string() + ".lock"); + boost::system::error_code ec; + boost::filesystem::remove(path, ec); + if (ec) BOOST_LOG_TRIVIAL(error) << "Failed to delete lockfile " << path; //else // BOOST_LOG_TRIVIAL(error) << "success delete lockfile " << path; @@ -346,11 +348,11 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance) if (! cla.should_send.has_value()) cla.should_send = app_config_single_instance; #ifdef _WIN32 - GUI::wxGetApp().init_single_instance_checker(lock_name + ".lock", data_dir() + "\\cache\\"); + GUI::wxGetApp().init_single_instance_checker(lock_name + ".lock", boost::nowide::narrow((Slic3r::data_dir_path() / "cache").make_preferred().wstring()) + "\\"); if (cla.should_send.value() && GUI::wxGetApp().single_instance_checker()->IsAnotherRunning()) { #else // mac & linx // get_lock() creates the lockfile therefore *cla.should_send is checked after - if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && *cla.should_send) { + if (instance_check_internal::get_lock(lock_name + ".lock", (Slic3r::data_dir_path() / "cache").string() + "/") && *cla.should_send) { #endif instance_check_internal::send_message(cla.cl_string, lock_name); BOOST_LOG_TRIVIAL(error) << "Instance check: Another instance found. This instance will terminate. Lock file of current running instance is located at " << data_dir() << @@ -529,7 +531,7 @@ void OtherInstanceMessageHandler::handle_message(const std::string& message) #ifdef __APPLE__ void OtherInstanceMessageHandler::handle_message_other_closed() { - instance_check_internal::get_lock(wxGetApp().get_instance_hash_string() + ".lock", data_dir() + "/cache/"); + instance_check_internal::get_lock(wxGetApp().get_instance_hash_string() + ".lock", (Slic3r::data_dir_path() / "cache").string() + "/"); } #endif //__APPLE__ diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 26e37d0325b..b5eaaf8a93f 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -502,7 +502,7 @@ void ArrangeJob::check_unprintable() if (it->poly.area() < 0.001 || it->height>params.printable_height) { #if SAVE_ARRANGE_POLY - SVG svg(data_dir() + "/SVG/arrange_unprintable_"+it->name+".svg", get_extents(it->poly)); + SVG svg((data_dir_path() / "SVG" / ("arrange_unprintable_" + it->name + ".svg")).string(), get_extents(it->poly)); if (svg.is_opened()) svg.draw_outline(it->poly); #endif diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index ba317d2be97..02f46232936 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -4001,7 +4001,7 @@ void MainFrame::show_sync_dialog() void MainFrame::export_logs() { // 1. Get log folder path - auto log_folder = boost::filesystem::path(data_dir()) / "log"; + auto log_folder = Slic3r::data_dir_path() / "log"; // 2. Check if log folder exists if (!boost::filesystem::exists(log_folder) || boost::filesystem::is_empty(log_folder)) { diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index b1edf8c1f28..df3eaf6a1f2 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #undef pid_t #include #ifdef __WIN32__ @@ -461,7 +462,7 @@ void MediaPlayCtrl::TogglePlay() void MediaPlayCtrl::ToggleStream() { - std::string file_url = data_dir() + "/cameratools/url.txt"; + auto file_url = Slic3r::data_dir_path() / "cameratools" / "url.txt"; if (m_streaming) { boost::nowide::ofstream file(file_url); file.close(); @@ -526,7 +527,7 @@ void MediaPlayCtrl::ToggleStream() url += "&device=" + into_u8(m_machine); url += "&dev_ver=" + m_dev_ver; BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); - std::string file_url = data_dir() + "/cameratools/url.txt"; + auto file_url = Slic3r::data_dir_path() / "cameratools" / "url.txt"; boost::nowide::ofstream file(file_url); auto url2 = encode_path(url.c_str()); file.write(url2.c_str(), url2.size()); @@ -555,7 +556,7 @@ void MediaPlayCtrl::ToggleStream() .ShowModal(); return; } - std::string file_url = data_dir() + "/cameratools/url.txt"; + auto file_url = Slic3r::data_dir_path() / "cameratools" / "url.txt"; boost::nowide::ofstream file(file_url); auto url2 = encode_path(url.c_str()); file.write(url2.c_str(), url2.size()); @@ -652,12 +653,17 @@ void MediaPlayCtrl::load() m_last_state = MEDIASTATE_LOADING; SetStatus(_L("Loading...")); if (wxGetApp().app_config->get("internal_developer_mode") == "true") { - std::string file_h264 = data_dir() + "/video.h264"; - std::string file_info = data_dir() + "/video.info"; + auto file_h264 = Slic3r::data_dir_path() / "video.h264"; + auto file_info = Slic3r::data_dir_path() / "video.info"; BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl dump video to " << file_h264; // closed by BambuSource - FILE *dump_h264_file = boost::nowide::fopen(file_h264.c_str(), "wb"); - FILE *dump_info_file = boost::nowide::fopen(file_info.c_str(), "wb"); +#ifdef WIN32 + FILE *dump_h264_file = boost::nowide::fopen(boost::nowide::narrow(file_h264.wstring()).c_str(), "wb"); + FILE *dump_info_file = boost::nowide::fopen(boost::nowide::narrow(file_info.wstring()).c_str(), "wb"); +#else + FILE *dump_h264_file = boost::nowide::fopen(file_h264.string().c_str(), "wb"); + FILE *dump_info_file = boost::nowide::fopen(file_info.string().c_str(), "wb"); +#endif m_url = m_url + "&dump_h264=" + boost::lexical_cast(dump_h264_file); m_url = m_url + "&dump_info=" + boost::lexical_cast(dump_info_file); } @@ -721,12 +727,12 @@ void MediaPlayCtrl::media_proc() bool MediaPlayCtrl::start_stream_service(bool *need_install) { #ifdef __WIN32__ - auto tools_dir = boost::nowide::widen(data_dir()) + L"\\cameratools\\"; + auto tools_dir = (Slic3r::data_dir_path() / "cameratools").wstring() + L"\\"; auto file_source = tools_dir + L"bambu_source.exe"; auto file_ffmpeg = tools_dir + L"ffmpeg.exe"; auto file_ff_cfg = tools_dir + L"ffmpeg.cfg"; #else - auto tools_dir = data_dir() + "/cameratools/"; + auto tools_dir = (Slic3r::data_dir_path() / "cameratools").string() + "/"; auto file_source = tools_dir + "bambu_source"; auto file_ffmpeg = tools_dir + "ffmpeg"; auto file_ff_cfg = tools_dir + "ffmpeg.cfg"; @@ -735,12 +741,16 @@ bool MediaPlayCtrl::start_stream_service(bool *need_install) if (need_install) *need_install = true; return false; } - std::string file_url = data_dir() + "/cameratools/url.txt"; + auto file_url = Slic3r::data_dir_path() / "cameratools" / "url.txt"; if (!boost::filesystem::exists(file_url)) { boost::nowide::ofstream file(file_url); file.close(); } - wxString file_url2 = L"bambu:///camera/" + from_u8(file_url); +#ifdef WIN32 + wxString file_url2 = L"bambu:///camera/" + from_u8(boost::nowide::narrow(file_url.wstring())); +#else + wxString file_url2 = L"bambu:///camera/" + from_u8(file_url.string()); +#endif file_url2.Replace("\\", "/"); file_url2 = wxURI(file_url2).BuildURI(); try { @@ -750,9 +760,9 @@ bool MediaPlayCtrl::start_stream_service(bool *need_install) boost::algorithm::split(configss, configs, boost::algorithm::is_any_of("\r\n")); configss.erase(std::remove(configss.begin(), configss.end(), std::string()), configss.end()); boost::process::pipe intermediate; - boost::filesystem::path start_dir(boost::filesystem::path(data_dir()) / "plugins"); + boost::filesystem::path start_dir(Slic3r::data_dir_path() / "plugins"); #ifdef __WXMSW__ - auto plugins_dir = boost::nowide::widen(data_dir()) + L"\\plugins\\"; + auto plugins_dir = (Slic3r::data_dir_path() / "plugins").wstring() + L"\\"; for (auto dll : {L"BambuSource.dll", L"live555.dll"}) { auto file_dll = tools_dir + dll; auto file_dll2 = plugins_dir + dll; @@ -795,8 +805,8 @@ bool MediaPlayCtrl::get_stream_url(std::string *url) } CloseHandle(shm); #else - std::string file_url = data_dir() + "/cameratools/url.txt"; - key_t key = ::ftok(file_url.c_str(), 1000); + auto file_url = Slic3r::data_dir_path() / "cameratools" / "url.txt"; + key_t key = ::ftok(file_url.string().c_str(), 1000); int shm = ::shmget(key, 1024, 0); if (shm == -1) return false; struct shmid_ds ds; diff --git a/src/slic3r/GUI/MixedColorMatchHelpers.cpp b/src/slic3r/GUI/MixedColorMatchHelpers.cpp index 5b59f1edb41..eb01b9339ac 100644 --- a/src/slic3r/GUI/MixedColorMatchHelpers.cpp +++ b/src/slic3r/GUI/MixedColorMatchHelpers.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "nlohmann/json.hpp" #include "libslic3r/Utils.hpp" @@ -904,13 +905,17 @@ static void load_filament_compatibility() try { // Prefer user data dir (where PresetUpdater deploys updates), fall back to bundled resources - const boost::filesystem::path user_path = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR + const boost::filesystem::path user_path = (Slic3r::data_dir_path() / PRESET_SYSTEM_DIR / PresetBundle::SM_BUNDLE / "filament" / "filament_compatibility.json").make_preferred(); - const boost::filesystem::path rsrc_path = (boost::filesystem::path(Slic3r::resources_dir()) / "profiles" + const boost::filesystem::path rsrc_path = (Slic3r::resources_dir_path() / "profiles" / PresetBundle::SM_BUNDLE / "filament" / "filament_compatibility.json").make_preferred(); +#ifdef WIN32 + const std::string path = boost::nowide::narrow((boost::filesystem::exists(user_path) ? user_path : rsrc_path).wstring()); +#else const std::string path = (boost::filesystem::exists(user_path) ? user_path : rsrc_path).string(); +#endif std::ifstream ifs(path); if (!ifs.is_open()) { diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 435b72d0933..4e91821e590 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cc0e8ecef11..36c87b28f34 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -8646,7 +8646,7 @@ void Sidebar::update_printer_thumbnail() } png_name += "_cover.png"; - boost::filesystem::path(resources_dir()) / "profile" / vendor / png_name; + Slic3r::resources_dir_path() / "profile" / vendor / png_name; std::string printer_type = selected_preset.get_current_printer_type(preset_bundle); try { @@ -17183,18 +17183,38 @@ void Plater::calib_flowrate(bool is_linear, int pass) { if (is_linear) { if (pass == 1) +#ifdef WIN32 + add_model(false, + boost::nowide::narrow((Slic3r::resources_dir_path() / "calib" / "filament_flow" / "Orca-LinearFlow.3mf").wstring())); +#else add_model(false, - (boost::filesystem::path(Slic3r::resources_dir()) / "calib" / "filament_flow" / "Orca-LinearFlow.3mf").string()); + (Slic3r::resources_dir_path() / "calib" / "filament_flow" / "Orca-LinearFlow.3mf").string()); +#endif else +#ifdef WIN32 + add_model(false, + boost::nowide::narrow((Slic3r::resources_dir_path() / "calib" / "filament_flow" / "Orca-LinearFlow_fine.3mf").wstring())); +#else add_model(false, - (boost::filesystem::path(Slic3r::resources_dir()) / "calib" / "filament_flow" / "Orca-LinearFlow_fine.3mf").string()); + (Slic3r::resources_dir_path() / "calib" / "filament_flow" / "Orca-LinearFlow_fine.3mf").string()); +#endif } else { if (pass == 1) +#ifdef WIN32 + add_model(false, + boost::nowide::narrow((Slic3r::resources_dir_path() / "calib" / "filament_flow" / "flowrate-test-pass1.3mf").wstring())); +#else add_model(false, - (boost::filesystem::path(Slic3r::resources_dir()) / "calib" / "filament_flow" / "flowrate-test-pass1.3mf").string()); + (Slic3r::resources_dir_path() / "calib" / "filament_flow" / "flowrate-test-pass1.3mf").string()); +#endif else +#ifdef WIN32 + add_model(false, + boost::nowide::narrow((Slic3r::resources_dir_path() / "calib" / "filament_flow" / "flowrate-test-pass2.3mf").wstring())); +#else add_model(false, - (boost::filesystem::path(Slic3r::resources_dir()) / "calib" / "filament_flow" / "flowrate-test-pass2.3mf").string()); + (Slic3r::resources_dir_path() / "calib" / "filament_flow" / "flowrate-test-pass2.3mf").string()); +#endif } adjust_settings_for_flowrate_calib(model().objects, is_linear, pass); diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index f97ea156bcc..399f1b99d63 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -244,7 +245,7 @@ struct PrinterFileSystem::Download : Progress std::string path; std::string local_path; std::string error; - boost::filesystem::ofstream ofs; + boost::nowide::ofstream ofs; boost::uuids::detail::md5 boost_md5; }; diff --git a/src/slic3r/GUI/ReleaseNote.cpp b/src/slic3r/GUI/ReleaseNote.cpp index c0b6fc6bd05..3cdef8cef56 100644 --- a/src/slic3r/GUI/ReleaseNote.cpp +++ b/src/slic3r/GUI/ReleaseNote.cpp @@ -2,6 +2,7 @@ #include "I18N.hpp" #include "libslic3r/Utils.hpp" +#include #include "libslic3r/Thread.hpp" #include "GUI.hpp" #include "GUI_App.hpp" @@ -298,14 +299,18 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent) }); - fs::path ph(data_dir()); + fs::path ph(Slic3r::data_dir_path()); ph /= "resources/tooltip/releasenote.html"; if (!fs::exists(ph)) { - ph = fs::path(resources_dir()) / "tooltip/releasenote.html"; + ph = Slic3r::resources_dir_path() / "tooltip/releasenote.html"; } // Make absolute to get proper "C:/..." prefix for file:// URL ph = fs::absolute(ph); +#ifdef WIN32 + auto url = boost::nowide::narrow(ph.wstring()); +#else auto url = ph.string(); +#endif std::replace(url.begin(), url.end(), '\\', '/'); url = "file:///" + url; m_vebview_release_note->LoadURL(from_u8(url)); diff --git a/src/slic3r/GUI/SSWCP.cpp b/src/slic3r/GUI/SSWCP.cpp index 9874436c235..7ae06e30d49 100644 --- a/src/slic3r/GUI/SSWCP.cpp +++ b/src/slic3r/GUI/SSWCP.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index f33ac56d4a1..dd4c9baaad2 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "Plater.hpp" #include "Notebook.hpp" @@ -2947,9 +2948,8 @@ bool SelectMachineDialog::is_show_timelapse() PartPlate *plate = m_plater->get_partplate_list().get_curr_plate(); fs::path gcode_path = plate->get_tmp_gcode_path(); - std::string line; - std::ifstream gcode_file; - gcode_file.open(gcode_path.string()); + std::string line; + boost::nowide::ifstream gcode_file(gcode_path); if (gcode_file.fail()) { } else { bool is_version = false; diff --git a/src/slic3r/GUI/WebDownPluginDlg.cpp b/src/slic3r/GUI/WebDownPluginDlg.cpp index a1141ab48ca..6e8556402cf 100644 --- a/src/slic3r/GUI/WebDownPluginDlg.cpp +++ b/src/slic3r/GUI/WebDownPluginDlg.cpp @@ -38,7 +38,11 @@ DownPluginFrame::DownPluginFrame(GUI_App *pGUI) : wxDialog((wxWindow *) (pGUI->m // set the frame icon wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL); - wxString TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/6/index.html").make_preferred().string()); +#ifdef WIN32 + wxString TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/6/index.html").make_preferred().wstring())); +#else + wxString TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/6/index.html").make_preferred().string()); +#endif TargetUrl = "file://" + TargetUrl; diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index 785f16ee6c0..73b654b96d9 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -216,19 +216,35 @@ wxString GuideFrame::SetStartPage(GuidePage startpage, bool load) { m_page = startpage; BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(" enter, load=%1%, start_page=%2%")%load%int(startpage); - //wxLogMessage("GUIDE: webpage_1 %s", (boost::filesystem::path(resources_dir()) / "web\\guide\\1\\index.html").make_preferred().string().c_str() ); - wxString TargetUrl = from_u8( (boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=1").make_preferred().string() ); + //wxLogMessage("GUIDE: webpage_1 %s", (Slic3r::resources_dir_path() / "web\\guide\\1\\index.html").make_preferred().string().c_str() ); +#ifdef WIN32 + wxString TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=1").make_preferred().wstring())); +#else + wxString TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=1").make_preferred().string()); +#endif //wxLogMessage("GUIDE: webpage_2 %s", TargetUrl.mb_str()); if (startpage == BBL_WELCOME){ SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=1").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=1").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=1").make_preferred().string()); +#endif } else if (startpage == BBL_REGION) { SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=11").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=11").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=11").make_preferred().string()); +#endif } else if (startpage == BBL_MODELS) { SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=21").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=21").make_preferred().string()); +#endif } else if (startpage == BBL_FILAMENTS) { SetTitle(_L("Setup Wizard")); @@ -239,19 +255,39 @@ wxString GuideFrame::SetStartPage(GuidePage startpage, bool load) } if (nSize>0) - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=22").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=22").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=22").make_preferred().string()); +#endif else - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=21").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=21").make_preferred().string()); +#endif } else if (startpage == BBL_FILAMENT_ONLY) { SetTitle(""); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=23").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=23").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=23").make_preferred().string()); +#endif } else if (startpage == BBL_MODELS_ONLY) { SetTitle(""); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=24").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=24").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=24").make_preferred().string()); +#endif } else { SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=21").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/0/index.html?target=21").make_preferred().string()); +#endif } wxString strlang = wxGetApp().current_language_code_safe(); @@ -753,7 +789,7 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle bool check_unsaved_preset_changes = false; std::vector install_bundles; std::vector remove_bundles; - const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR).make_preferred(); + const auto vendor_dir = (Slic3r::data_dir_path() / PRESET_SYSTEM_DIR).make_preferred(); for (const auto &it : enabled_vendors) { if (it.second.size() > 0) { auto vendor_file = vendor_dir/(it.first + ".json"); @@ -944,14 +980,17 @@ bool GuideFrame::run() return false; } -int GuideFrame::GetFilamentInfo( std::string VendorDirectory, json & pFilaList, std::string filepath, std::string &sVendor, std::string &sType) +int GuideFrame::GetFilamentInfo(const boost::filesystem::path &vendor_root, json &pFilaList, const boost::filesystem::path &filepath, + std::string &sVendor, std::string &sType) { - //GetStardardFilePath(filepath); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " GetFilamentInfo:VendorDirectory - " << VendorDirectory << ", Filepath - "< loaded_vendors; auto filament_library_name = boost::filesystem::path(PresetBundle::ORCA_FILAMENT_LIBRARY).replace_extension(".json"); if (boost::filesystem::exists(vendor_dir / filament_library_name)) { - m_OrcaFilaLibPath = (vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string(); - LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, (vendor_dir / filament_library_name).string()); + m_OrcaFilaLibRoot = (vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).make_preferred(); + LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, vendor_dir / filament_library_name); } else { - m_OrcaFilaLibPath = (rsrc_vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string(); - LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, (rsrc_vendor_dir / filament_library_name).string()); + m_OrcaFilaLibRoot = (rsrc_vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).make_preferred(); + LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, rsrc_vendor_dir / filament_library_name); } loaded_vendors.insert(PresetBundle::ORCA_FILAMENT_LIBRARY); // Load Snapmaker vendor first to ensure it appears at the top of the machine list auto sm_bundle_name = boost::filesystem::path(PresetBundle::SM_BUNDLE).replace_extension(".json"); if (boost::filesystem::exists(vendor_dir / sm_bundle_name)) { - LoadProfileFamily(PresetBundle::SM_BUNDLE, (vendor_dir / sm_bundle_name).string()); + LoadProfileFamily(PresetBundle::SM_BUNDLE, vendor_dir / sm_bundle_name); loaded_vendors.insert(PresetBundle::SM_BUNDLE); } else if (boost::filesystem::exists(rsrc_vendor_dir / sm_bundle_name)) { - LoadProfileFamily(PresetBundle::SM_BUNDLE, (rsrc_vendor_dir / sm_bundle_name).string()); + LoadProfileFamily(PresetBundle::SM_BUNDLE, rsrc_vendor_dir / sm_bundle_name); loaded_vendors.insert(PresetBundle::SM_BUNDLE); } if (m_destroy) @@ -1075,36 +1105,35 @@ int GuideFrame::LoadProfileData() //load custom bundle from user data path boost::filesystem::directory_iterator endIter; - for (boost::filesystem::directory_iterator iter(vendor_dir); iter != endIter; iter++) { - if (!boost::filesystem::is_directory(*iter)) { - wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); - strVendor = strVendor.AfterLast('\\'); - strVendor = strVendor.AfterLast('/'); - - wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower(); - if(strExtension.CmpNoCase("json") != 0 || loaded_vendors.find(w2s(strVendor)) != loaded_vendors.end()) - continue; + for (boost::filesystem::directory_iterator iter(vendor_dir); iter != endIter; ++iter) { + const boost::filesystem::path &fp = iter->path(); + if (boost::filesystem::is_directory(fp)) + continue; + if (!boost::iequals(fp.extension().string(), ".json")) + continue; + const std::string vendor_id = fp.stem().string(); + if (loaded_vendors.find(vendor_id) != loaded_vendors.end()) + continue; - LoadProfileFamily(w2s(strVendor), iter->path().string()); - loaded_vendors.insert(w2s(strVendor)); - } + LoadProfileFamily(vendor_id, fp); + loaded_vendors.insert(vendor_id); if (m_destroy) return 0; } boost::filesystem::directory_iterator others_endIter; - for (boost::filesystem::directory_iterator iter(rsrc_vendor_dir); iter != others_endIter; iter++) { - if (!boost::filesystem::is_directory(*iter)) { - wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); - strVendor = strVendor.AfterLast('\\'); - strVendor = strVendor.AfterLast('/'); - wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower(); - if (strExtension.CmpNoCase("json") != 0 || loaded_vendors.find(w2s(strVendor)) != loaded_vendors.end()) - continue; + for (boost::filesystem::directory_iterator iter(rsrc_vendor_dir); iter != others_endIter; ++iter) { + const boost::filesystem::path &fp = iter->path(); + if (boost::filesystem::is_directory(fp)) + continue; + if (!boost::iequals(fp.extension().string(), ".json")) + continue; + const std::string vendor_id = fp.stem().string(); + if (loaded_vendors.find(vendor_id) != loaded_vendors.end()) + continue; - LoadProfileFamily(w2s(strVendor), iter->path().string()); - loaded_vendors.insert(w2s(strVendor)); - } + LoadProfileFamily(vendor_id, fp); + loaded_vendors.insert(vendor_id); if (m_destroy) return 0; } @@ -1217,17 +1246,14 @@ void StringReplace(string &strBase, string strSrc, string strDes) } -int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath) +int GuideFrame::LoadProfileFamily(std::string strVendor, const boost::filesystem::path &json_path) { - // wxString strFolder = strFilePath.BeforeLast(boost::filesystem::path::preferred_separator); - boost::filesystem::path file_path(strFilePath); - boost::filesystem::path vendor_dir = boost::filesystem::absolute(file_path.parent_path() / strVendor).make_preferred(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", vendor path %1%.") % vendor_dir.string(); + const boost::filesystem::path file_path = json_path; + const boost::filesystem::path vendor_root = boost::filesystem::absolute(file_path.parent_path() / strVendor).make_preferred(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", vendor path %1%.") % vendor_root; try { - // wxLogMessage("GUIDE: json_path1 %s", w2s(strFilePath)); - std::string contents; - LoadFile(strFilePath, contents); + LoadFile(json_path, contents); // wxLogMessage("GUIDE: json_path1 content: %s", contents); json jLocal = json::parse(contents); // wxLogMessage("GUIDE: json_path1 Loaded"); @@ -1247,13 +1273,10 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath std::string s1 = OneModel["model"]; std::string s2 = OneModel["sub_path"]; - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); if (!boost::filesystem::exists(sub_path)) continue; - std::string sub_file = sub_path.string(); - - // wxLogMessage("GUIDE: json_path2 %s", w2s(ModelFilePath)); - LoadFile(sub_file, contents); + LoadFile(sub_path, contents); // wxLogMessage("GUIDE: json_path2 content: %s", contents); json pm = json::parse(contents); // wxLogMessage("GUIDE: json_path2 loaded"); @@ -1267,14 +1290,18 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath // wxString strCoverPath = wxString::Format("%s\\%s\\%s_cover.png", strFolder, strVendor, std::string(s1.mb_str())); std::string cover_file = s1 + "_cover.png"; - boost::filesystem::path cover_path = boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/profiles/" / strVendor / cover_file).make_preferred(); + boost::filesystem::path cover_path = boost::filesystem::absolute(Slic3r::resources_dir_path() / "/profiles/" / strVendor / cover_file).make_preferred(); if (!boost::filesystem::exists(cover_path)) { cover_path = - (boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/web/image/printer/") / + (boost::filesystem::absolute(Slic3r::resources_dir_path() / "/web/image/printer/") / cover_file) .make_preferred(); } +#ifdef WIN32 + OneModel["cover"] = boost::nowide::narrow(cover_path.wstring()); +#else OneModel["cover"] = cover_path.string(); +#endif OneModel["nozzle_selected"] = ""; @@ -1292,11 +1319,10 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath std::string s2 = OneMachine["sub_path"]; // wxString ModelFilePath = wxString::Format("%s\\%s\\%s", strFolder, strVendor, s2); - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); if (!boost::filesystem::exists(sub_path)) continue; - std::string sub_file = sub_path.string(); - LoadFile(sub_file, contents); + LoadFile(sub_path, contents); json pm = json::parse(contents); std::string strInstant = pm["instantiation"]; @@ -1335,24 +1361,23 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath if (!m_ProfileJson["filament"].contains(s1)) { // wxString ModelFilePath = wxString::Format("%s\\%s\\%s", strFolder, strVendor, s2); - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); if (!boost::filesystem::exists(sub_path)) continue; - std::string sub_file = sub_path.string(); - LoadFile(sub_file, contents); + LoadFile(sub_path, contents); if (contents == "") { continue; } json pm = json::parse(contents); std::string strInstant = pm["instantiation"]; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",Path:" << sub_file << ",instantiation?" << strInstant; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",Path:" << sub_path << ",instantiation?" << strInstant; if (strInstant == "true") { std::string sV; std::string sT; - int nRet = GetFilamentInfo(vendor_dir.string(),tFilaList, sub_file, sV, sT); + int nRet = GetFilamentInfo(vendor_root, tFilaList, sub_path, sV, sT); if (nRet != 0) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",GetFilamentInfo Failed, Vendor:" << sV << ",Type:"<< sT; continue; @@ -1400,11 +1425,10 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath std::string s2 = OneProcess["sub_path"]; // wxString ModelFilePath = wxString::Format("%s\\%s\\%s", strFolder, strVendor, s2); - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); if (!boost::filesystem::exists(sub_path)) continue; - std::string sub_file = sub_path.string(); - LoadFile(sub_file, contents); + LoadFile(sub_path, contents); json pm = json::parse(contents); std::string bInstall = pm["instantiation"]; @@ -1412,12 +1436,10 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath } } catch (nlohmann::detail::parse_error &err) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << strFilePath << " got a nlohmann::detail::parse_error, reason = " << err.what(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << json_path << " got a nlohmann::detail::parse_error, reason = " << err.what(); return -1; } catch (std::exception &e) { - // wxMessageBox(e.what(), "", MB_OK); - // wxLogMessage("GUIDE: LoadFamily Error: %s", e.what()); - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << strFilePath << " got exception: " << e.what(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << json_path << " got exception: " << e.what(); return -1; } @@ -1447,21 +1469,16 @@ void GuideFrame::GetStardardFilePath(std::string &FilePath) { StrReplace(FilePath, "/" , w2s(wxString::Format("%c", boost::filesystem::path::preferred_separator))); } -bool GuideFrame::LoadFile(std::string jPath, std::string &sContent) +bool GuideFrame::LoadFile(const boost::filesystem::path &p, std::string &sContent) { try { - boost::nowide::ifstream t(jPath); - std::stringstream buffer; - buffer << t.rdbuf(); - sContent=buffer.str(); - BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format(", load %1% into buffer")% jPath; - } - catch (std::exception &e) - { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", got exception: "< 0) -// TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/22/index.html").make_preferred().string()); +// TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/22/index.html").make_preferred().string()); // else -// TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/21/index.html").make_preferred().string()); +// TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/21/index.html").make_preferred().string()); // } else if (startpage == BBL_FILAMENT_ONLY) { // SetTitle(""); -// TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/23/index.html").make_preferred().string()); +// TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/23/index.html").make_preferred().string()); // } else if (startpage == BBL_MODELS_ONLY) { // SetTitle(""); -// TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/24/index.html").make_preferred().string()); +// TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/24/index.html").make_preferred().string()); // } else { // SetTitle(_L("Setup Wizard")); -// TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/21/index.html").make_preferred().string()); +// TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/21/index.html").make_preferred().string()); // } // // wxString strlang = wxGetApp().current_language_code_safe(); @@ -274,13 +274,21 @@ wxString WebPresetDialog::SetStartPage(GuidePage startpage, bool load) { m_page = startpage; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, load=%1%, start_page=%2%") % load % int(startpage); - // wxLogMessage("GUIDE: webpage_1 %s", (boost::filesystem::path(resources_dir()) / + // wxLogMessage("GUIDE: webpage_1 %s", (Slic3r::resources_dir_path() / // "web\\guide\\1\\index.html").make_preferred().string().c_str() ); - wxString TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/1/index.html").make_preferred().string()); +#ifdef WIN32 + wxString TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/guide/1/index.html").make_preferred().wstring())); +#else + wxString TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/1/index.html").make_preferred().string()); +#endif // wxLogMessage("GUIDE: webpage_2 %s", TargetUrl.mb_str()); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/preset_bind/24/index.html").make_preferred().string()); - // TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/24/index.html").make_preferred().string()); +#ifdef WIN32 + TargetUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web/preset_bind/24/index.html").make_preferred().wstring())); +#else + TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/preset_bind/24/index.html").make_preferred().string()); +#endif + // TargetUrl = from_u8((Slic3r::resources_dir_path() / "web/guide/24/index.html").make_preferred().string()); wxString strlang = wxGetApp().current_language_code_safe(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", strlang=%1%") % into_u8(strlang); @@ -887,7 +895,7 @@ bool WebPresetDialog::apply_config(AppConfig* app_config, PresetBundle* preset_b bool check_unsaved_preset_changes = false; std::vector install_bundles; std::vector remove_bundles; - const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR).make_preferred(); + const auto vendor_dir = (Slic3r::data_dir_path() / PRESET_SYSTEM_DIR).make_preferred(); for (const auto& it : enabled_vendors) { if (it.second.size() > 0) { auto vendor_file = vendor_dir / (it.first + ".json"); @@ -1080,14 +1088,17 @@ bool WebPresetDialog::run() return false; } -int WebPresetDialog::GetFilamentInfo(std::string VendorDirectory, json& pFilaList, std::string filepath, std::string& sVendor, std::string& sType) +int WebPresetDialog::GetFilamentInfo(const boost::filesystem::path &vendor_root, json &pFilaList, const boost::filesystem::path &filepath, + std::string &sVendor, std::string &sType) { - // GetStardardFilePath(filepath); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " GetFilamentInfo:VendorDirectory - " << VendorDirectory << ", Filepath - " << filepath; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " vendor_root=" << vendor_root << " filepath=" << filepath; try { std::string contents; - LoadFile(filepath, contents); + if (!LoadFile(filepath, contents) || contents.empty()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to read " << filepath; + return -1; + } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Json Contents: " << contents; json jLocal = json::parse(contents); @@ -1117,15 +1128,11 @@ int WebPresetDialog::GetFilamentInfo(std::string VendorDirectory, json& pFilaLis } std::string FPath = pFilaList[FName]["sub_path"]; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Before Format Inherits Path: VendorDirectory - " << VendorDirectory - << ", sub_path - " << FPath; - wxString strNewFile = wxString::Format("%s%c%s", wxString(VendorDirectory.c_str(), wxConvUTF8), - boost::filesystem::path::preferred_separator, FPath); - boost::filesystem::path inherits_path(w2s(strNewFile)); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inherits sub_path=" << FPath; + const boost::filesystem::path inherits_path = (vendor_root / FPath).make_preferred(); - // boost::filesystem::path nf(strNewFile.c_str()); if (boost::filesystem::exists(inherits_path)) - return GetFilamentInfo(VendorDirectory, pFilaList, inherits_path.string(), sVendor, sType); + return GetFilamentInfo(vendor_root, pFilaList, inherits_path, sVendor, sType); else { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inherits File Not Exist: " << inherits_path; return -1; @@ -1135,19 +1142,17 @@ int WebPresetDialog::GetFilamentInfo(std::string VendorDirectory, json& pFilaLis if (sType == "") { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "sType is Empty"; return -1; - } else + } else { sVendor = "Generic"; - return 0; + return 0; + } } } else return 0; - } catch (nlohmann::detail::parse_error& err) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << filepath - << " got a nlohmann::detail::parse_error, reason = " << err.what(); + } catch (nlohmann::detail::parse_error &err) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << filepath << " got a nlohmann::detail::parse_error, reason = " << err.what(); return -1; - } catch (std::exception& e) { - // wxLogMessage("GUIDE: load_profile_error %s ", e.what()); - // wxMessageBox(e.what(), "", MB_OK); + } catch (std::exception &e) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << filepath << " got exception: " << e.what(); return -1; } @@ -1189,8 +1194,8 @@ int WebPresetDialog::LoadProfile() m_ProfileJson["filament"] = json::object(); m_ProfileJson["process"] = json::array(); - vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR).make_preferred(); - rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + vendor_dir = (Slic3r::data_dir_path() / PRESET_SYSTEM_DIR).make_preferred(); + rsrc_vendor_dir = (Slic3r::resources_dir_path() / "profiles").make_preferred(); // BBS: add BBL as default // BBS: add json logic for vendor bundle @@ -1218,45 +1223,27 @@ int WebPresetDialog::LoadProfile() //} while (_findnext(handle, &findData) == 0); // ??????????? - // load BBL bundle from user data path - string targetPath = sm_bundle_path.make_preferred().string(); - boost::filesystem::path myPath(targetPath); boost::filesystem::directory_iterator endIter; - for (boost::filesystem::directory_iterator iter(myPath); iter != endIter; iter++) { - if (boost::filesystem::is_directory(*iter)) { - // cout << "is dir" << endl; - // cout << iter->path().string() << endl; - } else { - // cout << "is a file" << endl; - // cout << iter->path().string() << endl; - - wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); - strVendor = strVendor.AfterLast('\\'); - strVendor = strVendor.AfterLast('/'); - wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower(); - - if (w2s(strVendor) == PresetBundle::SM_BUNDLE && strExtension.CmpNoCase("json") == 0) - LoadProfileFamily(w2s(strVendor), iter->path().string()); - } + for (boost::filesystem::directory_iterator iter(sm_bundle_path); iter != endIter; ++iter) { + const boost::filesystem::path &fp = iter->path(); + if (boost::filesystem::is_directory(fp)) + continue; + if (!boost::iequals(fp.extension().string(), ".json")) + continue; + if (fp.stem().string() == PresetBundle::SM_BUNDLE) + LoadProfileFamily(PresetBundle::SM_BUNDLE, fp); } - // string others_targetPath = rsrc_vendor_dir.string(); boost::filesystem::directory_iterator others_endIter; - for (boost::filesystem::directory_iterator iter(rsrc_vendor_dir); iter != others_endIter; iter++) { - if (boost::filesystem::is_directory(*iter)) { - // cout << "is dir" << endl; - // cout << iter->path().string() << endl; - } else { - // cout << "is a file" << endl; - // cout << iter->path().string() << endl; - wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); - strVendor = strVendor.AfterLast('\\'); - strVendor = strVendor.AfterLast('/'); - wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower(); - - if (w2s(strVendor) != PresetBundle::SM_BUNDLE && strExtension.CmpNoCase("json") == 0) - LoadProfileFamily(w2s(strVendor), iter->path().string()); - } + for (boost::filesystem::directory_iterator iter(rsrc_vendor_dir); iter != others_endIter; ++iter) { + const boost::filesystem::path &fp = iter->path(); + if (boost::filesystem::is_directory(fp)) + continue; + if (!boost::iequals(fp.extension().string(), ".json")) + continue; + const std::string vid = fp.stem().string(); + if (vid != PresetBundle::SM_BUNDLE) + LoadProfileFamily(vid, fp); } // LoadProfileFamily(PresetBundle::BBL_BUNDLE, bbl_bundle_path.string()); @@ -1339,17 +1326,14 @@ int WebPresetDialog::LoadProfile() return 0; } -int WebPresetDialog::LoadProfileFamily(std::string strVendor, std::string strFilePath) +int WebPresetDialog::LoadProfileFamily(std::string strVendor, const boost::filesystem::path &json_path) { - // wxString strFolder = strFilePath.BeforeLast(boost::filesystem::path::preferred_separator); - boost::filesystem::path file_path(strFilePath); - boost::filesystem::path vendor_dir = boost::filesystem::absolute(file_path.parent_path() / strVendor).make_preferred(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", vendor path %1%.") % vendor_dir.string(); + const boost::filesystem::path file_path = json_path; + const boost::filesystem::path vendor_root = boost::filesystem::absolute(file_path.parent_path() / strVendor).make_preferred(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", vendor path %1%.") % vendor_root; try { - // wxLogMessage("GUIDE: json_path1 %s", w2s(strFilePath)); - std::string contents; - LoadFile(strFilePath, contents); + LoadFile(json_path, contents); // wxLogMessage("GUIDE: json_path1 content: %s", contents); json jLocal = json::parse(contents); // wxLogMessage("GUIDE: json_path1 Loaded"); @@ -1369,11 +1353,9 @@ int WebPresetDialog::LoadProfileFamily(std::string strVendor, std::string strFil std::string s1 = OneModel["model"]; std::string s2 = OneModel["sub_path"]; - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); - std::string sub_file = sub_path.string(); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); - // wxLogMessage("GUIDE: json_path2 %s", w2s(ModelFilePath)); - LoadFile(sub_file, contents); + LoadFile(sub_path, contents); // wxLogMessage("GUIDE: json_path2 content: %s", contents); json pm = json::parse(contents); // wxLogMessage("GUIDE: json_path2 loaded"); @@ -1386,14 +1368,18 @@ int WebPresetDialog::LoadProfileFamily(std::string strVendor, std::string strFil // wxString strCoverPath = wxString::Format("%s\\%s\\%s_cover.png", strFolder, strVendor, std::string(s1.mb_str())); std::string cover_file = s1 + "_cover.png"; - boost::filesystem::path cover_path = boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/profiles/" / + boost::filesystem::path cover_path = boost::filesystem::absolute(Slic3r::resources_dir_path() / "/profiles/" / strVendor / cover_file) .make_preferred(); if (!boost::filesystem::exists(cover_path)) { - cover_path = (boost::filesystem::absolute(boost::filesystem::path(resources_dir()) / "/web/image/printer/") / cover_file) + cover_path = (boost::filesystem::absolute(Slic3r::resources_dir_path() / "/web/image/printer/") / cover_file) .make_preferred(); } +#ifdef WIN32 + OneModel["cover"] = boost::nowide::narrow(cover_path.wstring()); +#else OneModel["cover"] = cover_path.string(); +#endif OneModel["nozzle_selected"] = ""; @@ -1411,9 +1397,8 @@ int WebPresetDialog::LoadProfileFamily(std::string strVendor, std::string strFil std::string s2 = OneMachine["sub_path"]; // wxString ModelFilePath = wxString::Format("%s\\%s\\%s", strFolder, strVendor, s2); - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); - std::string sub_file = sub_path.string(); - LoadFile(sub_file, contents); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); + LoadFile(sub_path, contents); json pm = json::parse(contents); std::string strInstant = pm["instantiation"]; @@ -1452,20 +1437,19 @@ int WebPresetDialog::LoadProfileFamily(std::string strVendor, std::string strFil if (!m_ProfileJson["filament"].contains(s1)) { // wxString ModelFilePath = wxString::Format("%s\\%s\\%s", strFolder, strVendor, s2); - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); - std::string sub_file = sub_path.string(); - LoadFile(sub_file, contents); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); + LoadFile(sub_path, contents); json pm = json::parse(contents); std::string strInstant = pm["instantiation"]; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",Path:" << sub_file << ",instantiation?" + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",Path:" << sub_path << ",instantiation?" << strInstant; if (strInstant == "true") { std::string sV; std::string sT; - int nRet = GetFilamentInfo(vendor_dir.string(), tFilaList, sub_file, sV, sT); + int nRet = GetFilamentInfo(vendor_root, tFilaList, sub_path, sV, sT); if (nRet != 0) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",GetFilamentInfo Failed, Vendor:" << sV << ",Type:" << sT; @@ -1509,9 +1493,8 @@ int WebPresetDialog::LoadProfileFamily(std::string strVendor, std::string strFil std::string s2 = OneProcess["sub_path"]; // wxString ModelFilePath = wxString::Format("%s\\%s\\%s", strFolder, strVendor, s2); - boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_dir / s2).make_preferred(); - std::string sub_file = sub_path.string(); - LoadFile(sub_file, contents); + boost::filesystem::path sub_path = boost::filesystem::absolute(vendor_root / s2).make_preferred(); + LoadFile(sub_path, contents); json pm = json::parse(contents); std::string bInstall = pm["instantiation"]; @@ -1521,13 +1504,10 @@ int WebPresetDialog::LoadProfileFamily(std::string strVendor, std::string strFil } } catch (nlohmann::detail::parse_error& err) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << strFilePath - << " got a nlohmann::detail::parse_error, reason = " << err.what(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << json_path << " got a nlohmann::detail::parse_error, reason = " << err.what(); return -1; } catch (std::exception& e) { - // wxMessageBox(e.what(), "", MB_OK); - // wxLogMessage("GUIDE: LoadFamily Error: %s", e.what()); - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << strFilePath << " got exception: " << e.what(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << json_path << " got exception: " << e.what(); return -1; } @@ -1554,19 +1534,16 @@ void WebPresetDialog::GetStardardFilePath(std::string& FilePath) StrReplace(FilePath, "/", w2s(wxString::Format("%c", boost::filesystem::path::preferred_separator))); } -bool WebPresetDialog::LoadFile(std::string jPath, std::string& sContent) +bool WebPresetDialog::LoadFile(const boost::filesystem::path& p, std::string& sContent) { try { - boost::nowide::ifstream t(jPath); - std::stringstream buffer; - buffer << t.rdbuf(); - sContent = buffer.str(); - BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format(", load %1% into buffer") % jPath; + Slic3r::load_string_file(p, sContent); + BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format(", load %1% into buffer") % p; } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", got exception: " << e.what(); + sContent.clear(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", path " << p << ", exception: " << e.what(); return false; } - return true; } diff --git a/src/slic3r/GUI/WebPresetDialog.hpp b/src/slic3r/GUI/WebPresetDialog.hpp index 37ba3b905dc..7129244421e 100644 --- a/src/slic3r/GUI/WebPresetDialog.hpp +++ b/src/slic3r/GUI/WebPresetDialog.hpp @@ -70,9 +70,10 @@ class WebPresetDialog : public DPIDialog // Model - Machine - Filaments int LoadProfile(); - int LoadProfileFamily(std::string strVendor, std::string strFilePath); + int LoadProfileFamily(std::string strVendor, const boost::filesystem::path &json_path); int SaveProfile(); - int GetFilamentInfo(std::string VendorDirectory, json& pFilaList, std::string filepath, std::string& sVendor, std::string& sType); + int GetFilamentInfo(const boost::filesystem::path &vendor_root, json &pFilaList, const boost::filesystem::path &filepath, + std::string &sVendor, std::string &sType); bool apply_config(AppConfig* app_config, PresetBundle* preset_bundle, const PresetUpdater* updater, bool& apply_keeped_changes); bool run(); @@ -80,7 +81,7 @@ class WebPresetDialog : public DPIDialog void StrReplace(std::string& strBase, std::string strSrc, std::string strDes); std::string w2s(wxString sSrc); void GetStardardFilePath(std::string& FilePath); - bool LoadFile(std::string jPath, std::string& sContent); + bool LoadFile(const boost::filesystem::path &p, std::string &sContent); // install plugin int DownloadPlugin(); diff --git a/src/slic3r/GUI/WebSMUserLoginDialog.cpp b/src/slic3r/GUI/WebSMUserLoginDialog.cpp index 9b23e17535b..0c10fe2ae68 100644 --- a/src/slic3r/GUI/WebSMUserLoginDialog.cpp +++ b/src/slic3r/GUI/WebSMUserLoginDialog.cpp @@ -416,10 +416,18 @@ void SMUserLogin::OnScriptResponseMessage(wxCommandEvent &WXUNUSED(evt)) bool SMUserLogin::ShowErrorPage() { - wxString ErrorUrl = from_u8((boost::filesystem::path(resources_dir()) / "web\\login\\error.html").make_preferred().string()); +#ifdef WIN32 + wxString ErrorUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web\\login\\error.html").make_preferred().wstring())); +#else + wxString ErrorUrl = from_u8((Slic3r::resources_dir_path() / "web\\login\\error.html").make_preferred().string()); +#endif wxString strlang = wxGetApp().current_language_code_safe(); if (strlang != "") - ErrorUrl = wxString::Format("file://%s/web/login/error.html?lang=%s", from_u8(resources_dir()), strlang); +#ifdef WIN32 + ErrorUrl = wxString::Format("file://%s/web/login/error.html?lang=%s", from_u8(boost::nowide::narrow(Slic3r::resources_dir_path().wstring())), strlang); +#else + ErrorUrl = wxString::Format("file://%s/web/login/error.html?lang=%s", from_u8(Slic3r::resources_dir_path().string()), strlang); +#endif load_url(ErrorUrl); return true; diff --git a/src/slic3r/GUI/WebUserLoginDialog.cpp b/src/slic3r/GUI/WebUserLoginDialog.cpp index 17d1ad3f5b1..ec5df638a9c 100644 --- a/src/slic3r/GUI/WebUserLoginDialog.cpp +++ b/src/slic3r/GUI/WebUserLoginDialog.cpp @@ -390,7 +390,11 @@ void ZUserLogin::OnScriptResponseMessage(wxCommandEvent &WXUNUSED(evt)) bool ZUserLogin::ShowErrorPage() { - wxString ErrortUrl = from_u8((boost::filesystem::path(resources_dir()) / "web\\login\\error.html").make_preferred().string()); +#ifdef WIN32 + wxString ErrortUrl = from_u8(boost::nowide::narrow((Slic3r::resources_dir_path() / "web\\login\\error.html").make_preferred().wstring())); +#else + wxString ErrortUrl = from_u8((Slic3r::resources_dir_path() / "web\\login\\error.html").make_preferred().string()); +#endif load_url(ErrortUrl); return true; diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index dc802449d7c..2dd941ef5cb 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -89,11 +90,11 @@ std::mutex g_mutex; struct form_file { - fs::ifstream ifs; - boost::filesystem::ifstream::off_type init_offset; + boost::nowide::ifstream ifs; + boost::nowide::ifstream::off_type init_offset; size_t content_length; - form_file(fs::path const& p, const boost::filesystem::ifstream::off_type offset, const size_t content_length) + form_file(fs::path const& p, const boost::nowide::ifstream::off_type offset, const size_t content_length) : ifs(p, std::ios::in | std::ios::binary), init_offset(offset), content_length(content_length) {} }; @@ -142,7 +143,7 @@ struct Http::priv void set_timeout_connect(long timeout); void set_timeout_max(long timeout); - void form_add_file(const char *name, const fs::path &path, const char* filename, boost::filesystem::ifstream::off_type offset, size_t length); + void form_add_file(const char *name, const fs::path &path, const char* filename, boost::nowide::ifstream::off_type offset, size_t length); /* mime */ void mime_form_add_text(const char* name, const char* value); void mime_form_add_file(const char* name, const char* path); @@ -331,7 +332,7 @@ void Http::priv::set_timeout_max(long timeout) ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); } -void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename, boost::filesystem::ifstream::off_type offset, size_t length) +void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename, boost::nowide::ifstream::off_type offset, size_t length) { // We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows // and so we use CURLFORM_STREAM with boost ifstream to read the file. @@ -650,7 +651,7 @@ Http& Http::form_add(const std::string &name, const std::string &contents) return *this; } -Http& Http::form_add_file(const std::string &name, const fs::path &path, boost::filesystem::ifstream::off_type offset, size_t length) +Http& Http::form_add_file(const std::string &name, const fs::path &path, boost::nowide::ifstream::off_type offset, size_t length) { if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr, offset, length); } return *this; @@ -670,13 +671,13 @@ Http& Http::mime_form_add_file(std::string &name, const char* path) } -Http& Http::form_add_file(const std::wstring& name, const fs::path& path, boost::filesystem::ifstream::off_type offset, size_t length) +Http& Http::form_add_file(const std::wstring& name, const fs::path& path, boost::nowide::ifstream::off_type offset, size_t length) { if (p) { p->form_add_file((char*)name.c_str(), path.c_str(), nullptr, offset, length); } return *this; } -Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename, boost::filesystem::ifstream::off_type offset, size_t length) +Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename, boost::nowide::ifstream::off_type offset, size_t length) { if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str(), offset, length); } return *this; diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index 7b2c05b9083..a8e3207fc80 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "libslic3r/Exception.hpp" #include "common_func/common_func.hpp" @@ -124,15 +125,15 @@ class Http : public std::enable_shared_from_this { // Add a HTTP multipart form field Http& form_add(const std::string &name, const std::string &contents); // Add a HTTP multipart form file data contents, `name` is the name of the part - Http& form_add_file(const std::string &name, const boost::filesystem::path &path, boost::filesystem::ifstream::off_type offset = 0, size_t length = 0); + Http& form_add_file(const std::string &name, const boost::filesystem::path &path, boost::nowide::ifstream::off_type offset = 0, size_t length = 0); // Add a HTTP mime form field Http& mime_form_add_text(std::string& name, std::string& value); // Add a HTTP mime form file Http& mime_form_add_file(std::string& name, const char* path); // Same as above except also override the file's filename with a wstring type - Http& form_add_file(const std::wstring& name, const boost::filesystem::path& path, boost::filesystem::ifstream::off_type offset = 0, size_t length = 0); + Http& form_add_file(const std::wstring& name, const boost::filesystem::path& path, boost::nowide::ifstream::off_type offset = 0, size_t length = 0); // Same as above except also override the file's filename with a custom one - Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename, boost::filesystem::ifstream::off_type offset = 0, size_t length = 0); + Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename, boost::nowide::ifstream::off_type offset = 0, size_t length = 0); #ifdef WIN32 // Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present. diff --git a/src/slic3r/Utils/InstanceID.cpp b/src/slic3r/Utils/InstanceID.cpp index 9010c09d800..83b432ed2fb 100644 --- a/src/slic3r/Utils/InstanceID.cpp +++ b/src/slic3r/Utils/InstanceID.cpp @@ -108,7 +108,7 @@ std::optional read_storage_file() if (path.empty() || !boost::filesystem::exists(path)) return std::nullopt; - boost::nowide::ifstream file(path.string()); + boost::nowide::ifstream file(path); if (!file) return std::nullopt; @@ -136,7 +136,7 @@ bool write_storage_file(const std::string& value) if (ec) return false; - boost::nowide::ofstream file(path.string(), std::ios::trunc); + boost::nowide::ofstream file(path, std::ios::trunc); if (!file) return false; diff --git a/src/slic3r/Utils/MQTT.cpp b/src/slic3r/Utils/MQTT.cpp index dd169db3083..44bb3fcdc40 100644 --- a/src/slic3r/Utils/MQTT.cpp +++ b/src/slic3r/Utils/MQTT.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -65,7 +66,7 @@ MqttClient::MqttClient(const std::string& server_address, // CA证书临时文件 boost::filesystem::path ca_path = temp_dir / ("ca_" + client_id + std::to_string(int64_t(this)) + ".pem"); if (!ca_content.empty()) { - boost::filesystem::ofstream ca_file(ca_path); + boost::nowide::ofstream ca_file(ca_path); ca_file << ca_content; ca_file.close(); if (!ca_file) { @@ -78,7 +79,7 @@ MqttClient::MqttClient(const std::string& server_address, // 客户端证书临时文件 boost::filesystem::path cert_path = temp_dir / ("cert_" + client_id + std::to_string(int64_t(this)) + ".pem"); if (!cert_content.empty()) { - boost::filesystem::ofstream cert_file(cert_path); + boost::nowide::ofstream cert_file(cert_path); cert_file << cert_content; cert_file.close(); if (!cert_file) { @@ -91,7 +92,7 @@ MqttClient::MqttClient(const std::string& server_address, // 私钥临时文件 boost::filesystem::path key_path = temp_dir / ("key_" + client_id + std::to_string(int64_t(this)) + ".pem"); if (!key_content.empty()) { - boost::filesystem::ofstream key_file(key_path); + boost::nowide::ofstream key_file(key_path); key_file << key_content; key_file.close(); if (!key_file) { diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index eb32cd9c809..e7468acbc7b 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -100,8 +101,15 @@ void copy_file_fix(const fs::path &source, const fs::path &target) { BOOST_LOG_TRIVIAL(debug) << format("PresetUpdater: Copying %1% -> %2%", source, target); std::string error_message; - //CopyFileResult cfr = Slic3r::GUI::copy_file_gui(source.string(), target.string(), error_message, false); + // copy_file() on Windows expects UTF-8; path::string() is not UTF-8 when path holds wchar internals. +#ifdef _WIN32 + const std::string from_u8 = boost::nowide::narrow(source.wstring()); + const std::string to_u8 = boost::nowide::narrow(target.wstring()); + CopyFileResult cfr = copy_file(from_u8, to_u8, error_message, false); +#else CopyFileResult cfr = copy_file(source.string(), target.string(), error_message, false); +#endif + if (cfr != CopyFileResult::SUCCESS) { BOOST_LOG_TRIVIAL(error) << "Copying failed(" << cfr << "): " << error_message; throw Slic3r::CriticalException(GUI::format( @@ -317,9 +325,15 @@ struct PresetUpdater::priv //BBS: change directories by design PresetUpdater::priv::priv() +#ifdef _WIN32 + : cache_path(fs::path(boost::nowide::widen(Slic3r::data_dir())) / "ota") + , rsrc_path(fs::path(boost::nowide::widen(Slic3r::resources_dir())) / "profiles") + , vendor_path(fs::path(boost::nowide::widen(Slic3r::data_dir())) / PRESET_SYSTEM_DIR) +#else : cache_path(fs::path(Slic3r::data_dir()) / "ota") - , rsrc_path(fs::path(resources_dir()) / "profiles") + , rsrc_path(Slic3r::resources_dir_path() / "profiles") , vendor_path(fs::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR) +#endif , cancel(false) { //BBS: refine preset updater logic @@ -328,8 +342,7 @@ PresetUpdater::priv::priv() // Install indicies from resources. Only installs those that are either missing or older than in resources. check_installed_vendor_profiles(); perform_updates(get_printer_config_updates(), false); - // Load indices from the cache directory. - //index_db = Index::load_db(); + } // Pull relevant preferences from AppConfig @@ -387,7 +400,12 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe bool PresetUpdater::priv::extract_file(const fs::path &source_path, const fs::path &dest_path) { bool res = true; +#ifdef _WIN32 + // source_path.string() returns ACP on Windows; boost::nowide::fopen expects UTF-8. + std::string file_path = boost::nowide::narrow(source_path.wstring()); +#else std::string file_path = source_path.string(); +#endif fs::path parent_path = !dest_path.empty() ? dest_path : source_path.parent_path(); mz_zip_archive archive; mz_zip_zero_struct(&archive); @@ -430,13 +448,13 @@ bool PresetUpdater::priv::extract_file(const fs::path &source_path, const fs::pa if (!parent_dir.empty() && !fs::exists(parent_dir)) fs::create_directories(parent_dir); +#ifdef _WIN32 + // Use wide API directly to avoid ACP round-trip through encode_path. + std::wstring dest_file_w = full_dest.wstring(); + res = mz_zip_reader_extract_to_file_w(&archive, stat.m_file_index, dest_file_w.c_str(), 0); +#else std::string dest_file_encoded = encode_path(full_dest.string().c_str()); res = mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_file_encoded.c_str(), 0); -#ifdef _WIN32 - if (!res) { - std::wstring dest_file_w = boost::nowide::widen(full_dest.generic_string()); - res = mz_zip_reader_extract_to_file_w(&archive, stat.m_file_index, dest_file_w.c_str(), 0); - } #endif if (!res) { mz_zip_error zip_err = mz_zip_get_last_error(&archive); @@ -713,9 +731,17 @@ void PresetUpdater::priv::sync_resources(std::string http_url, std::mapget_vendor_profile_version(PresetBundle::SM_BUNDLE); else - currentPresetVersion = get_version_from_json(data_dir() + "/system/Snapmaker.json"); +#ifdef _WIN32 + currentPresetVersion = get_version_from_json(boost::nowide::narrow((Slic3r::data_dir_path() / "system/Snapmaker.json").wstring())); +#else + currentPresetVersion = get_version_from_json((Slic3r::data_dir_path() / "system/Snapmaker.json").string()); +#endif std::regex matcher("[0-9]+\\.[0-9]+(\\.[0-9]+)*(-[A-Za-z0-9]+)?(\\+[A-Za-z0-9]+)?"); @@ -1232,7 +1266,7 @@ void PresetUpdater::priv::sync_tooltip(std::string http_url, std::string languag try { std::string common_version = "00.00.00.00"; std::string language_version = "00.00.00.00"; - fs::path cache_root = fs::path(data_dir()) / "resources/tooltip"; + fs::path cache_root = Slic3r::data_dir_path() / "resources" / "tooltip"; try { auto vf = cache_root / "common" / "version"; if (fs::exists(vf)) Slic3r::load_string_file(vf, common_version); @@ -1260,8 +1294,7 @@ void PresetUpdater::priv::sync_tooltip(std::string http_url, std::string languag // return true means there are plugins files bool PresetUpdater::priv::get_cached_plugins_version(std::string& cached_version, bool &force) { - std::string data_dir_str = data_dir(); - boost::filesystem::path data_dir_path(data_dir_str); + const boost::filesystem::path data_dir_path = Slic3r::data_dir_path(); auto cache_folder = data_dir_path / "ota"; std::string network_library, player_library, live555_library; bool has_plugins = false; @@ -1346,8 +1379,7 @@ void PresetUpdater::priv::sync_plugins(std::string http_url, std::string plugin_ } if (need_delete_cache) { - std::string data_dir_str = data_dir(); - boost::filesystem::path data_dir_path(data_dir_str); + const boost::filesystem::path data_dir_path = Slic3r::data_dir_path(); auto cache_folder = data_dir_path / "ota"; #if defined(_MSC_VER) || defined(_WIN32) @@ -1460,8 +1492,7 @@ void PresetUpdater::priv::sync_printer_config(std::string http_url) std::string using_version = curr_version.substr(0, 6) + "00.00"; std::string cached_version; - std::string data_dir_str = data_dir(); - boost::filesystem::path data_dir_path(data_dir_str); + const boost::filesystem::path data_dir_path = Slic3r::data_dir_path(); auto config_folder = data_dir_path / "printers"; auto cache_folder = data_dir_path / "ota" / "printers"; @@ -1649,20 +1680,25 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const Updates PresetUpdater::priv::get_printer_config_updates(bool update) const { - std::string data_dir_str = data_dir(); - boost::filesystem::path data_dir_path(data_dir_str); - boost::filesystem::path resc_dir_path(resources_dir()); + const boost::filesystem::path data_dir_path = Slic3r::data_dir_path(); + const boost::filesystem::path resc_dir_path = Slic3r::resources_dir_path(); auto config_folder = data_dir_path / "printers"; auto resc_folder = (update ? cache_path : resc_dir_path) / "printers"; std::string curr_version; std::string resc_version; try { - Slic3r::load_string_file(resc_folder / "version.txt", resc_version); - boost::algorithm::trim(resc_version); + const auto resc_version_file = resc_folder / "version.txt"; + if (boost::filesystem::exists(resc_version_file)) { + Slic3r::load_string_file(resc_version_file, resc_version); + boost::algorithm::trim(resc_version); + } } catch (...) {} try { - Slic3r::load_string_file(config_folder / "version.txt", curr_version); - boost::algorithm::trim(curr_version); + const auto curr_version_file = config_folder / "version.txt"; + if (boost::filesystem::exists(curr_version_file)) { + Slic3r::load_string_file(curr_version_file, curr_version); + boost::algorithm::trim(curr_version); + } } catch (...) {} if (!curr_version.empty()) { @@ -1682,7 +1718,11 @@ Updates PresetUpdater::priv::get_printer_config_updates(bool update) const version.config_version = resc_version; std::string change_log; if (update) { +#ifdef _WIN32 + std::string changelog_file = boost::nowide::narrow((resc_folder / "printer.json").wstring()); +#else std::string changelog_file = (resc_folder / "printer.json").string(); +#endif try { boost::nowide::ifstream ifs(changelog_file); json j; @@ -2191,16 +2231,18 @@ void PresetUpdater::load_flutter_web(const std::string& resource_path, bool serv std::string ori_version_str = "0"; std::string ori_build_number_str = "0"; - auto ori_version_file = boost::filesystem::path(data_dir()) / "web" / "flutter_web" / "version.json"; + auto ori_version_file = Slic3r::data_dir_path() / "web" / "flutter_web" / "version.json"; boost::property_tree::ptree ori_config; - boost::property_tree::read_json(ori_version_file.string(), ori_config); + boost::nowide::ifstream ifs_ori(ori_version_file); + boost::property_tree::read_json(ifs_ori, ori_config); ori_version_str = ori_config.get("version", "0"); ori_build_number_str = ori_config.get("build_number", "0"); { const fs::path version_path = flutter_root / "version.json"; boost::property_tree::ptree config; - boost::property_tree::read_json(version_path.string(), config); + boost::nowide::ifstream ifs_ver(version_path); + boost::property_tree::read_json(ifs_ver, config); std::string version_str = config.get("version", "0"); std::string build_number_str = config.get("build_number", "0"); @@ -2209,7 +2251,7 @@ void PresetUpdater::load_flutter_web(const std::string& resource_path, bool serv if (current_version < online_version) { auto source_folder_path = flutter_root; - auto target_folder_path = (boost::filesystem::path(data_dir()) / "web" / "flutter_web"); + auto target_folder_path = (Slic3r::data_dir_path() / "web" / "flutter_web"); Version version; version.config_version = online_version; @@ -2373,7 +2415,8 @@ void PresetUpdater::import_system_profile() if (dir_entry.path().extension() == ".json") { { boost::property_tree::ptree config; - boost::property_tree::read_json(dir_entry.path().string(), config); + boost::nowide::ifstream ifs(dir_entry.path()); + boost::property_tree::read_json(ifs, config); std::string version_str = config.get("version", "0"); std::string vendor = dir_entry.path().stem().string(); diff --git a/src/slic3r/Utils/SimplyPrint.cpp b/src/slic3r/Utils/SimplyPrint.cpp index 212d79f0afe..92715b74423 100644 --- a/src/slic3r/Utils/SimplyPrint.cpp +++ b/src/slic3r/Utils/SimplyPrint.cpp @@ -102,7 +102,11 @@ static bool should_open_in_external_browser() SimplyPrint::SimplyPrint(DynamicPrintConfig* config) { - cred_file = (boost::filesystem::path(data_dir()) / OAUTH_CREDENTIAL_PATH).make_preferred().string(); +#ifdef WIN32 + cred_file = boost::nowide::narrow((Slic3r::data_dir_path() / OAUTH_CREDENTIAL_PATH).make_preferred().wstring()); +#else + cred_file = (Slic3r::data_dir_path() / OAUTH_CREDENTIAL_PATH).make_preferred().string(); +#endif load_oauth_credential(); } @@ -405,7 +409,7 @@ bool SimplyPrint::do_chunk_upload(const boost::filesystem::path& file_path, cons } // Calculate the offset and length of current chunk - const boost::filesystem::ifstream::off_type offset = i * buffer_size; + const boost::nowide::ifstream::off_type offset = i * buffer_size; const size_t length = i == (chunk_amount - 1) ? file_size - offset : buffer_size; const bool succ = do_api_call( diff --git a/src/slic3r/Utils/json_diff.cpp b/src/slic3r/Utils/json_diff.cpp index 171a44c63da..109f2c0e041 100644 --- a/src/slic3r/Utils/json_diff.cpp +++ b/src/slic3r/Utils/json_diff.cpp @@ -15,6 +15,7 @@ #include #include #include +#include using namespace std; using json = nlohmann::json; @@ -89,8 +90,8 @@ bool json_diff::load_compatible_settings(std::string const &type, std::string co printer_version = version2; } settings_base.clear(); - std::string config_file = Slic3r::data_dir() + "/printers/" + printer_type + ".json"; - boost::nowide::ifstream json_file(config_file.c_str()); + boost::filesystem::path config_file = Slic3r::data_dir_path() / "printers" / (printer_type + ".json"); + boost::nowide::ifstream json_file(config_file); try { json versions; if (json_file.is_open()) { @@ -104,10 +105,10 @@ bool json_diff::load_compatible_settings(std::string const &type, std::string co diff2all_base_reset(full_message); return true; } else { - BOOST_LOG_TRIVIAL(error) << "load_compatible_settings failed, file = " << config_file; + BOOST_LOG_TRIVIAL(error) << "load_compatible_settings failed, file = " << config_file.string(); } } catch (...) { - BOOST_LOG_TRIVIAL(error) << "load_compatible_settings failed, file = " << config_file; + BOOST_LOG_TRIVIAL(error) << "load_compatible_settings failed, file = " << config_file.string(); } return false; }