diff --git a/3rdparty/libossia b/3rdparty/libossia
index 879e5c3666..09d8744c7c 160000
--- a/3rdparty/libossia
+++ b/3rdparty/libossia
@@ -1 +1 @@
-Subproject commit 879e5c36660039015ae0c6ae223b7fe9f88dbda8
+Subproject commit 09d8744c7c2a2cd2c12b60baa7fc3a2cae0f0668
diff --git a/cmake/ScoreConfiguration.cmake b/cmake/ScoreConfiguration.cmake
index 2ca900f383..38b44558cc 100644
--- a/cmake/ScoreConfiguration.cmake
+++ b/cmake/ScoreConfiguration.cmake
@@ -304,3 +304,56 @@ install(
DESTINATION include/score
COMPONENT Devel
OPTIONAL)
+
+# Win32 codepage stuff
+if(WIN32)
+ file(CONFIGURE
+ OUTPUT
+ "${PROJECT_BINARY_DIR}/score.exe.manifest"
+ CONTENT
+ [=[
+
+
+ true/PM
+ PerMonitorV2,PerMonitor
+ true
+ UTF-8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]=]
+ )
+
+ if(MINGW)
+ enable_language(RC)
+ # 1 = CREATEPROCESS_MANIFEST_RESOURCE_ID, 24 = RT_MANIFEST (no includes needed)
+ file(WRITE "${PROJECT_BINARY_DIR}/score.exe.rc"
+ "1 24 \"${PROJECT_BINARY_DIR}/score.exe.manifest\"\n")
+ endif()
+endif()
+
+function(target_enable_utf8 tgt)
+ if(NOT SCORE_MSSTORE_DEPLOYMENT) # c.f. ScoreDeploymentWindowsStore
+ if(MSVC)
+ target_link_options(${tgt} PRIVATE
+ /MANIFEST:EMBED
+ "/MANIFESTINPUT:${CMAKE_BINARY_DIR}/score.exe.manifest")
+ elseif(MINGW)
+ target_sources(${tgt} PRIVATE "${CMAKE_BINARY_DIR}/score.exe.rc")
+ endif()
+ endif()
+endfunction()
\ No newline at end of file
diff --git a/cmake/ScoreDeploymentWindowsStore.cmake b/cmake/ScoreDeploymentWindowsStore.cmake
index c85d7d414e..393a68ec43 100644
--- a/cmake/ScoreDeploymentWindowsStore.cmake
+++ b/cmake/ScoreDeploymentWindowsStore.cmake
@@ -2,36 +2,6 @@ if(NOT SCORE_MSSTORE_DEPLOYMENT)
return()
endif()
-file(CONFIGURE
- OUTPUT
- "${PROJECT_BINARY_DIR}/score.exe.manifest"
- CONTENT
- [=[
-
-
- true/PM
- PerMonitorV2,PerMonitor
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ]=]
-)
-
install(
FILES
"${PROJECT_BINARY_DIR}/score.exe.manifest"
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index bde903cb1b..b53230695d 100755
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -165,6 +165,7 @@ if(APPLE)
endif()
if(WIN32)
+ target_enable_utf8(${APPNAME})
target_link_libraries(${APPNAME} PRIVATE ntdll RuntimeObject)
endif()
diff --git a/src/lib/score/tools/RecursiveWatch.cpp b/src/lib/score/tools/RecursiveWatch.cpp
index 905791e67e..1acdc35aef 100644
--- a/src/lib/score/tools/RecursiveWatch.cpp
+++ b/src/lib/score/tools/RecursiveWatch.cpp
@@ -71,8 +71,11 @@ void for_all_files(std::string_view root, std::function
{
if(auto res = algorithm::contents(pp.value()))
{
+ try
+ {
for(auto& p : res.value())
{
+ try {
switch(p.second.st_type)
{
#if !defined(_WIN32)
@@ -87,15 +90,46 @@ void for_all_files(std::string_view root, std::function
#if !defined(_WIN32)
f(r.native());
#else
- f(r.generic_string());
+ try {
+ f(r.generic_string());
+ }
+ catch(...)
+ {
+ // Very inefficient but r.generic_string() sometimes throws
+ // filesystem error: in __wide_to_char: Illegal byte sequence
+ // on windows
+ auto s = QString::fromStdWString(r.native()).toStdString();
+ std::replace(s.begin(), s.end(), '\\', '/');
+ f(s);
+ }
#endif
break;
}
default:
break;
}
+ }
+ catch(const std::exception& e)
+ {
+ }
+ catch(...)
+ {
+ }
+ }
+ }
+ catch(const std::exception& e)
+ {
+ }
+ catch(...)
+ {
}
}
+ else
+ {
+ }
+ }
+ catch(const std::exception& e)
+ {
}
catch(...)
{
@@ -318,4 +352,11 @@ void RecursiveWatch::scanAsync(QObject* context)
send_to_main_thread();
});
}
+
+void RecursiveWatch::reset()
+{
+ m_root.clear();
+ m_watched.clear();
+ m_asyncWatched.clear();
+}
}
diff --git a/src/lib/score/tools/RecursiveWatch.hpp b/src/lib/score/tools/RecursiveWatch.hpp
index ce977feb1f..abd34696ba 100644
--- a/src/lib/score/tools/RecursiveWatch.hpp
+++ b/src/lib/score/tools/RecursiveWatch.hpp
@@ -69,12 +69,7 @@ class SCORE_LIB_BASE_EXPORT RecursiveWatch
/// on the GUI thread via \a context's event loop.
void scanAsync(QObject* context);
- void reset()
- {
- m_root.clear();
- m_watched.clear();
- m_asyncWatched.clear();
- }
+ void reset();
private:
std::string m_root;
diff --git a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp
index 5fe6d31a59..12360c6d98 100644
--- a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp
+++ b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.cpp
@@ -94,7 +94,7 @@ void ProcessModel::setInitialScript(const QString& script)
fx.reset(ysfx_new(config.get()), ysfx_u_deleter{});
if(!ysfx_load_file(fx.get(), path_to_jsfx.c_str(), 0))
{
- qDebug() << "ysfx: could not load " << script;
+ qDebug() << "ysfx: setInitialScript: could not load " << script;
return;
}
@@ -108,7 +108,11 @@ void ProcessModel::setInitialScript(const QString& script)
uint32_t compile_opts = 0;
if(!ysfx_compile(fx.get(), compile_opts))
+ {
+ qDebug() << "ysfx: setInitialScript: could not compile " << script;
return;
+ }
+
ysfx_init(fx.get());
ysfx_process_double(fx.get(), 0, 0, 0, 0, 0);
@@ -123,6 +127,7 @@ void ProcessModel::setInitialScript(const QString& script)
}
programChanged();
+ flagsChanged(); // For UI
}
void ProcessModel::recreatePorts()
@@ -313,7 +318,7 @@ Process::ScriptChangeResult ProcessModel::reload()
QFile tempFile{tempFilePath};
if(!tempFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
- qDebug() << "ysfx: could not write temp file" << tempFilePath;
+ qDebug() << "ysfx: reload: could not write temp file" << tempFilePath;
return res;
}
tempFile.write(m_text.toUtf8());
@@ -324,7 +329,10 @@ Process::ScriptChangeResult ProcessModel::reload()
ysfx_register_builtin_audio_formats(config.get());
// Set import and data roots from the original file
- ysfx_guess_file_roots(config.get(), m_jsfx_path.toStdString().c_str());
+ const auto& c = config.get();
+ ysfx_guess_file_roots(c, m_jsfx_path.toStdString().c_str());
+ if(auto import = ysfx_get_import_root(c); !import || strlen(import) == 0)
+ ysfx_set_import_root(c, QFileInfo{m_jsfx_path}.dir().canonicalPath().toStdString().c_str());
// Create a new fx instance
auto new_fx = std::shared_ptr(ysfx_new(config.get()), ysfx_u_deleter{});
@@ -332,14 +340,14 @@ Process::ScriptChangeResult ProcessModel::reload()
auto tempPath = tempFilePath.toStdString();
if(!ysfx_load_file(new_fx.get(), tempPath.c_str(), 0))
{
- qDebug() << "ysfx: could not load temp file" << tempFilePath;
+ qDebug() << "ysfx: reload: could not load temp file" << tempFilePath;
return res;
}
uint32_t compile_opts = 0;
if(!ysfx_compile(new_fx.get(), compile_opts))
{
- qDebug() << "ysfx: compilation failed";
+ qDebug() << "ysfx: reload: compilation failed";
return res;
}
@@ -363,7 +371,13 @@ Process::ScriptChangeResult ProcessModel::reload()
recreatePorts();
+ if(auto bank = ysfx_get_bank_path(this->fx.get()))
+ {
+ m_bank = ysfx_load_bank(bank);
+ }
+
programChanged();
+ flagsChanged(); // For UI
return res;
}
diff --git a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp
index e6be61601b..a2a7c95396 100644
--- a/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp
+++ b/src/plugins/score-plugin-ysfx/YSFX/ProcessModel.hpp
@@ -81,6 +81,9 @@ class SCORE_PLUGIN_YSFX_EXPORT ProcessModel final : public Process::ProcessModel
QString m_jsfx_path;
QString m_text;
+ // If we reload something, to preserve state when the object doesn't create
+ QByteArray m_saved_state;
+
std::bitset m_sliderBeingChanged{};
};
diff --git a/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp b/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp
index b59f978e06..4ae3bc91d3 100644
--- a/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp
+++ b/src/plugins/score-plugin-ysfx/YSFX/ProcessModelSerialization.cpp
@@ -74,7 +74,11 @@ static void loadYSFXState(ysfx_t& fx, const QByteArray& state)
template <>
void DataStreamReader::read(const YSFX::ProcessModel& proc)
{
- m_stream << proc.m_jsfx_path << proc.m_text << readYSFXState(*proc.fx.get());
+ m_stream << proc.m_jsfx_path << proc.m_text;
+ if(proc.fx)
+ m_stream << readYSFXState(*proc.fx.get());
+ else
+ m_stream << proc.m_saved_state;
readPorts(*this, proc.m_inlets, proc.m_outlets);
@@ -94,19 +98,21 @@ void DataStreamWriter::write(YSFX::ProcessModel& proc)
{
proc.m_jsfx_path = script;
proc.m_text = text;
+ proc.reload();
// If we also have a file path, we may need to reload from it
// But for now, we prefer the stored text (live-coded version)
- if(proc.fx)
- loadYSFXState(*proc.fx.get(), dat);
}
else
{
proc.setInitialScript(script);
- if(proc.fx)
- loadYSFXState(*proc.fx.get(), dat);
}
+ if(proc.fx)
+ loadYSFXState(*proc.fx.get(), dat);
+ else
+ proc.m_saved_state = dat;
+
writePorts(
*this, components.interfaces(), proc.m_inlets,
proc.m_outlets, &proc);
@@ -147,6 +153,8 @@ void JSONWriter::write(YSFX::ProcessModel& proc)
auto dat = QByteArray::fromBase64(obj["Chunk"].toByteArray());
if(proc.fx)
loadYSFXState(*proc.fx.get(), dat);
+ else
+ proc.m_saved_state = dat;
writePorts(
*this, components.interfaces(), proc.m_inlets,