diff --git a/src/backend/generators.h b/src/backend/generators.h index f4bbcd59..1ff06662 100644 --- a/src/backend/generators.h +++ b/src/backend/generators.h @@ -129,11 +129,11 @@ namespace boss { node = head.append_child(); node.set_name("link"); node.append_attribute("rel").set_value("stylesheet"); - node.append_attribute("href").set_value("../resources/style.css"); + node.append_attribute("href").set_value(ToFileURL(css_path).c_str()); node = head.append_child(); node.set_name("script"); - node.append_attribute("src").set_value("../resources/polyfill.js"); + node.append_attribute("src").set_value(ToFileURL(polyfill_path).c_str()); node.text().set(" "); } @@ -448,7 +448,7 @@ namespace boss { node = body.append_child(); node.set_name("script"); - node.append_attribute("src").set_value("../resources/script.js"); + node.append_attribute("src").set_value(ToFileURL(js_path).c_str()); node.text().set(" "); } diff --git a/src/backend/globals.cpp b/src/backend/globals.cpp index 16ade454..e7d31fda 100644 --- a/src/backend/globals.cpp +++ b/src/backend/globals.cpp @@ -49,10 +49,13 @@ namespace boss { const unsigned int VERSION_PATCH = 0; //Common paths. - const boost::filesystem::path readme_path = "Docs/BOSS Readme.html"; + const boost::filesystem::path readme_path = "docs/BOSS Readme.html"; const boost::filesystem::path libespm_options_path = "resources/libespm.yaml"; const boost::filesystem::path svn_path = "resources/svn/svn.exe"; const boost::filesystem::path local_path = GetLocalAppDataPath() / "BOSS"; + const boost::filesystem::path css_path = boost::filesystem::current_path() / "resources" / "style.css"; + const boost::filesystem::path js_path = boost::filesystem::current_path() / "resources" / "script.js"; + const boost::filesystem::path polyfill_path = boost::filesystem::current_path() / "resources" / "polyfill.js"; const boost::filesystem::path settings_path = local_path / "settings.yaml"; const boost::filesystem::path log_path = local_path / "BOSSDebugLog.txt"; } diff --git a/src/backend/globals.h b/src/backend/globals.h index fa7492b3..712ff914 100644 --- a/src/backend/globals.h +++ b/src/backend/globals.h @@ -54,6 +54,9 @@ namespace boss { extern const boost::filesystem::path readme_path; extern const boost::filesystem::path settings_path; extern const boost::filesystem::path libespm_options_path; + extern const boost::filesystem::path css_path; + extern const boost::filesystem::path js_path; + extern const boost::filesystem::path polyfill_path; extern const boost::filesystem::path svn_path; extern const boost::filesystem::path log_path; } diff --git a/src/backend/helpers.cpp b/src/backend/helpers.cpp index 4db36650..e0af18ae 100644 --- a/src/backend/helpers.cpp +++ b/src/backend/helpers.cpp @@ -220,6 +220,31 @@ namespace boss { return fs::path(""); #endif } + + //Turns an absolute filesystem path into a valid file:// URL. + std::string ToFileURL(const fs::path& file) { + //URLs are UTF-8 encoded then any characters (equiv. their corresponding bytes) not in the unreserved set (equiv. their corresponding bytes) are replaced by a percentage sign followed by the hex representation of their binary value. + string unreserved = "-.0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"; //Unreserved in byte value order, plus the colon character since that's allowed for drive paths. + + string url = "file://"; + for (boost::filesystem::path::const_iterator it=file.begin(), endit=file.end(); it != endit; ++it) { + string part = it->string(); + if (part == "/") //Skip exta backslash after drive path. + continue; + //String iterator is byte-by-byte, not character-by-character, which is good. + for (string::const_iterator jt=part.begin(), endjt=part.end(); jt != endjt; ++jt) { + if (!binary_search(unreserved.begin(), unreserved.end(), *jt)) + //Replace with percentage-hex value. + url += '%' + IntToHexString(*jt); + else + url += *jt; + } + url += '/'; + } + url.resize(url.length()-1); //Get rid of trailing forward slash. + + return url; + } ////////////////////////////// diff --git a/src/backend/helpers.h b/src/backend/helpers.h index df8a073f..83beeaec 100644 --- a/src/backend/helpers.h +++ b/src/backend/helpers.h @@ -66,6 +66,9 @@ namespace boss { //Get the local application data path. boost::filesystem::path GetLocalAppDataPath(); + //Turns an absolute filesystem path into a valid file:// URL. + std::string ToFileURL(const boost::filesystem::path& file); + //Version class for more robust version comparisons. class Version { private: diff --git a/src/backend/network.cpp b/src/backend/network.cpp index 27bc19df..f231a593 100644 --- a/src/backend/network.cpp +++ b/src/backend/network.cpp @@ -73,7 +73,8 @@ namespace boss { siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = consoleWrite; siStartInfo.hStdOutput = consoleWrite; - siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + siStartInfo.wShowWindow = SW_HIDE; const int utf16Len = MultiByteToWideChar(CP_UTF8, 0, command.c_str(), -1, NULL, 0); wchar_t * cmdLine = new wchar_t[utf16Len]; @@ -126,36 +127,37 @@ namespace boss { if (!RunCommand(command, output)) throw error(ERROR_SUBVERSION_ERROR, "Subversion could not update the masterlist. Details: " + output); - //Now test masterlist to see if it parses OK. - bool good = false; - while (!good) { + + string revision; + while (true) { try { + + //Now get the masterlist revision. + command = svn_path.string() + " info \"" + game.MasterlistPath().string() + "\""; + if (!RunCommand(command, output)) + throw error(ERROR_SUBVERSION_ERROR, "Subversion could not read the masterlist revision number. Details: " + output); + + size_t pos1 = output.rfind("Revision: "); + size_t pos2 = output.find('\n', pos1); + + revision = output.substr(pos1+10, pos2-pos1-10); + + pos1 = output.find("Last Changed Date: ", pos2); + pos2 = output.find(' ', pos1+19); + + string date = output.substr(pos1+19, pos2-pos1-19); + + //Now test masterlist to see if it parses OK. YAML::Node mlist = YAML::LoadFile(game.MasterlistPath().string()); - good = true; + + return revision + " (" + date + ")"; } catch (YAML::Exception& e) { //Roll back one revision if there's an error. - parsingErrors.push_back(e.what()); + parsingErrors.push_back("Masterlist revision " + revision + ": " + e.what()); command = svn_path.string() + " update --revision PREV \"" + game.MasterlistPath().string() + "\""; if (!RunCommand(command, output)) throw error(ERROR_SUBVERSION_ERROR, "Subversion could not update the masterlist. Details: " + output); } } - - //Now get the masterlist revision. Can either create a pipe using the Win32 API (http://msdn.microsoft.com/en-us/library/ms682499.aspx), or output to a file, read it, then delete it. - command = svn_path.string() + " info \"" + game.MasterlistPath().string() + "\""; - if (!RunCommand(command, output)) - throw error(ERROR_SUBVERSION_ERROR, "Subversion could not read the masterlist revision number. Details: " + output); - - size_t pos1 = output.rfind("Revision: "); - size_t pos2 = output.find('\n', pos1); - - string revision = output.substr(pos1+10, pos2-pos1-10); - - pos1 = output.find("Last Changed Date: ", pos2); - pos2 = output.find(' ', pos1+19); - - string date = output.substr(pos1+19, pos2-pos1-19); - - return revision + " (" + date + ")"; } } diff --git a/src/gui/main.cpp b/src/gui/main.cpp index df50ce72..3f682c54 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -79,19 +79,7 @@ bool BossGUI::OnInit() { } //Load settings. - if (fs::exists(settings_path)) { - try { - _settings = YAML::LoadFile(settings_path.string()); - } catch (YAML::ParserException& e) { - //LOG_ERROR("Error: %s", e.getString().c_str()); - wxMessageBox( - FromUTF8(format(loc::translate("Error: Settings parsing failed. %1%")) % e.what()), - translate("BOSS: Error"), - wxOK | wxICON_ERROR, - NULL); - return false; - } - } else { + if (!fs::exists(settings_path)) { try { if (!fs::exists(settings_path.parent_path())) fs::create_directory(settings_path.parent_path()); @@ -105,6 +93,19 @@ bool BossGUI::OnInit() { } GenerateDefaultSettingsFile(settings_path.string()); } + if (fs::exists(settings_path)) { + try { + _settings = YAML::LoadFile(settings_path.string()); + } catch (YAML::ParserException& e) { + //LOG_ERROR("Error: %s", e.getString().c_str()); + wxMessageBox( + FromUTF8(format(loc::translate("Error: Settings parsing failed. %1%")) % e.what()), + translate("BOSS: Error"), + wxOK | wxICON_ERROR, + NULL); + return false; + } + } //Skip logging initialisation for tester. /* if (gl_log_debug_output) @@ -327,6 +328,11 @@ void Launcher::OnClose(wxCloseEvent& event) { void Launcher::OnSortPlugins(wxCommandEvent& event) { + YAML::Node mlist, ulist; + list messages, mlist_messages, ulist_messages; + list mlist_plugins, ulist_plugins; + time_t start, end; + wxProgressDialog *progDia = new wxProgressDialog(translate("BOSS: Working..."),translate("BOSS working..."), 1000, this, wxPD_APP_MODAL|wxPD_AUTO_HIDE|wxPD_ELAPSED_TIME); time_t t0 = time(NULL); @@ -334,7 +340,6 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { ofstream out((local_path / "out.txt").string().c_str()); out << "Updating masterlist..." << endl; - time_t start, end; start = time(NULL); vector parsingErrors; @@ -342,13 +347,10 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { try { revision = UpdateMasterlist(_game, parsingErrors); } catch (boss::error& e) { - //LOG_ERROR("Error: %s", e.what()); - wxMessageBox( - FromUTF8(format(loc::translate("Error: Condition evaluation failed. %1%")) % e.what()), - translate("BOSS: Error"), - wxOK | wxICON_ERROR, - this); - return; + messages.push_back(boss::Message(boss::MESSAGE_ERROR, string("Masterlist update failed. Details: ") + e.what())); + } + for (vector::const_iterator it=parsingErrors.begin(), endit=parsingErrors.end(); it != endit; ++it) { + messages.push_back(boss::Message(boss::MESSAGE_ERROR, *it)); } end = time(NULL); @@ -376,10 +378,6 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { out << "Time taken to read plugins: " << (end - start) << " seconds." << endl; start = time(NULL); - YAML::Node mlist, ulist; - list messages, mlist_messages, ulist_messages; - list mlist_plugins, ulist_plugins; - if (fs::exists(_game.MasterlistPath())) { out << "Parsing masterlist..." << endl; @@ -392,6 +390,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { translate("BOSS: Error"), wxOK | wxICON_ERROR, this); + progDia->Destroy(); return; } if (mlist["globals"]) @@ -416,6 +415,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { translate("BOSS: Error"), wxOK | wxICON_ERROR, this); + progDia->Destroy(); return; } if (ulist["plugins"]) @@ -461,13 +461,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { try { it->EvalAllConditions(_game, lang); } catch (boss::error& e) { - //LOG_ERROR("Error: %s", e.what()); - wxMessageBox( - FromUTF8(format(loc::translate("Error: Condition evaluation failed. %1%")) % e.what()), - translate("BOSS: Error"), - wxOK | wxICON_ERROR, - this); - return; + messages.push_back(boss::Message(boss::MESSAGE_ERROR, "\"" + it->Name() + "\" contains a condition that could not be evaluated. Details: " + e.what())); } progDia->Pulse(); @@ -652,6 +646,7 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) { translate("BOSS: Error"), wxOK | wxICON_ERROR, this); + progDia->Destroy(); return; } diff --git a/src/installer.nsi b/src/installer.nsi index 6e1ff83b..359eca62 100644 --- a/src/installer.nsi +++ b/src/installer.nsi @@ -262,14 +262,14 @@ LangString TEXT_USERFILES ${LANG_SIMPCHINESE} "BOSS的userlist和BOSS.ini文件 File "..\build\libboss64.dll" ;Now install readme files. - SetOutPath "$INSTDIR\Docs" + SetOutPath "$INSTDIR\docs" File "..\docs\BOSS API Readme.html" File "..\docs\BOSS Metadata Syntax.html" File "..\docs\BOSS Readme.html" File "..\docs\Licenses.txt" ;Now install readme images. - SetOutPath "$INSTDIR\Docs\images" + SetOutPath "$INSTDIR\docs\images" File "..\docs\images\editor.png" File "..\docs\images\main.png" File "..\docs\images\settings.png" @@ -383,19 +383,19 @@ LangString TEXT_USERFILES ${LANG_SIMPCHINESE} "BOSS的userlist和BOSS.ini文件 RMDir "$INSTDIR\API" ;Remove readme files. - Delete "$INSTDIR\Docs\BOSS API Readme.html" - Delete "$INSTDIR\Docs\BOSS Metadata Syntax.html" - Delete "$INSTDIR\Docs\BOSS Readme.html" - Delete "$INSTDIR\Docs\Licenses.txt" - RMDir "$INSTDIR\Docs" + Delete "$INSTDIR\docs\BOSS API Readme.html" + Delete "$INSTDIR\docs\BOSS Metadata Syntax.html" + Delete "$INSTDIR\docs\BOSS Readme.html" + Delete "$INSTDIR\docs\Licenses.txt" + RMDir "$INSTDIR\docs" ;Remove readme images. - Delete "$INSTDIR\Docs\images\editor.png" - Delete "$INSTDIR\Docs\images\main.png" - Delete "$INSTDIR\Docs\images\settings.png" - Delete "$INSTDIR\Docs\images\viewer-1.png" - Delete "$INSTDIR\Docs\images\viewer-2.png" - RMDir "$INSTDIR\Docs\images" + Delete "$INSTDIR\docs\images\editor.png" + Delete "$INSTDIR\docs\images\main.png" + Delete "$INSTDIR\docs\images\settings.png" + Delete "$INSTDIR\docs\images\viewer-1.png" + Delete "$INSTDIR\docs\images\viewer-2.png" + RMDir "$INSTDIR\docs\images" ;Remove resource files. Delete "$INSTDIR\resources\libespm.yaml"