Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ Three singletons manage core state. All run on the main thread. Access via `Clas
### PS1 formats (static) and runtime extraction (experimental)

- **Static parsers** (`src/PS1/`): `PS1TMD`, `PS1TIM`, `PS1RSD`, `PS1PLY`, `PS1MAT` for known PlayStation mesh/texture formats.
- **Runtime extraction** (`src/PS1/runtime/`, epic #412): `ENABLE_PS1_RIP` (OFF by default). When ON, `PS1RipManager` singleton coordinates emulator embedding, GPU/GTE capture, VRAM dumps, and reconstruction into Ogre meshes. Design doc: `src/PS1/PS1_RIP_DESIGN.md`. CI enables the flag on Linux test builds only. Sentry breadcrumbs use category `ps1.rip`.
- **Runtime extraction** (`src/PS1/runtime/`, epic #412): `ENABLE_PS1_RIP` (OFF by default). When ON, `PS1RipManager` runs an `EmuCore` plugin from `<app>/PS1Cores/` on a worker thread; stub core `qtmesh_ps1core_stub` ships for CI/dev. Session UI: **Tools → Experimental → PS1 Runtime Ripper…** (`PS1RipSessionWindow`, `EmuViewport`). Design doc: `src/PS1/PS1_RIP_DESIGN.md`. CI enables the flag on Linux test builds only. Sentry breadcrumbs use category `ps1.rip`.

### Mesh Import/Export

Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ option(ENABLE_PS1_RIP "Enable experimental PS1 runtime geometry extraction" OFF)
if(ENABLE_PS1_RIP)
add_definitions(-DENABLE_PS1_RIP)
message(STATUS "PS1 runtime geometry extraction enabled (experimental)")
add_subdirectory(plugins/ps1core_stub)
endif()
##############################################################
# Sentry crash reporting and analytics
Expand Down
32 changes: 32 additions & 0 deletions plugins/ps1core_stub/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
if(NOT ENABLE_PS1_RIP)
return()
endif()

set(PS1_RUNTIME_INCLUDE "${CMAKE_SOURCE_DIR}/src/PS1/runtime")

add_library(qtmesh_ps1core_stub MODULE
StubEmuCore.cpp
StubEmuCorePlugin.cpp
)

target_include_directories(qtmesh_ps1core_stub PRIVATE
${PS1_RUNTIME_INCLUDE}
${CMAKE_CURRENT_SOURCE_DIR}
)

target_link_libraries(qtmesh_ps1core_stub PRIVATE Qt6::Core)

set_target_properties(qtmesh_ps1core_stub PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/PS1Cores"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/PS1Cores"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/PS1Cores"
)

if(BUILD_TESTS AND TARGET UnitTests)
add_dependencies(UnitTests qtmesh_ps1core_stub)
endif()

if(BUILD_QT_MESH_EDITOR AND TARGET ${CMAKE_PROJECT_NAME})
add_dependencies(${CMAKE_PROJECT_NAME} qtmesh_ps1core_stub)
endif()
83 changes: 83 additions & 0 deletions plugins/ps1core_stub/StubEmuCore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include "StubEmuCore.h"

#include <QFileInfo>

StubEmuCore::StubEmuCore()
{
for (EmuFramebuffer &buf : m_buffers) {
buf.width = kWidth;
buf.height = kHeight;
buf.rgb24.resize(kWidth * kHeight * 3);
}
fillTestPattern(m_buffers[m_writeIndex]);
}

QString StubEmuCore::coreId() const
{
return QStringLiteral("stub");
}

bool StubEmuCore::loadBios(const QString &biosPath)
{
const QFileInfo info(biosPath);
if (!info.exists() || !info.isFile())
return false;
m_biosPath = info.absoluteFilePath();
return true;
}

bool StubEmuCore::loadIso(const QString &isoPath)
{
const QFileInfo info(isoPath);
if (!info.exists() || !info.isFile())
return false;
m_isoPath = info.absoluteFilePath();
return true;
}

void StubEmuCore::runFrame()
{
if (m_biosPath.isEmpty() || m_isoPath.isEmpty())
return;

EmuFramebuffer &buf = m_buffers[static_cast<size_t>(m_writeIndex)];
buf.frameIndex = ++m_frameIndex;
fillTestPattern(buf);

m_readIndex = m_writeIndex;
m_writeIndex = (m_writeIndex + 1) % 3;
}

void StubEmuCore::reset()
{
m_frameIndex = 0;
fillTestPattern(m_buffers[m_writeIndex]);
m_readIndex = m_writeIndex;
Comment on lines +53 to +55
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset leaves stale phase state in the first post-reset frame.

Line [54] regenerates pixels from buf.frameIndex, but reset() only clears m_frameIndex. If the selected write buffer was previously used, the displayed reset frame can start from an old phase.

Suggested fix
 void StubEmuCore::reset()
 {
     m_frameIndex = 0;
-    fillTestPattern(m_buffers[m_writeIndex]);
+    EmuFramebuffer &buf = m_buffers[static_cast<size_t>(m_writeIndex)];
+    buf.frameIndex = m_frameIndex;
+    fillTestPattern(buf);
     m_readIndex = m_writeIndex;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
m_frameIndex = 0;
fillTestPattern(m_buffers[m_writeIndex]);
m_readIndex = m_writeIndex;
m_frameIndex = 0;
EmuFramebuffer &buf = m_buffers[static_cast<size_t>(m_writeIndex)];
buf.frameIndex = m_frameIndex;
fillTestPattern(buf);
m_readIndex = m_writeIndex;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ps1core_stub/StubEmuCore.cpp` around lines 53 - 55, The reset leaves
stale per-buffer phase because fillTestPattern reads buf.frameIndex while
reset() only resets m_frameIndex; fix by initializing the selected write
buffer's frame state before regenerating pixels: set the write buffer's
frameIndex (or equivalent phase/state in m_buffers[m_writeIndex]) to
m_frameIndex (or 0) prior to calling fillTestPattern, and then set m_readIndex =
m_writeIndex so the displayed frame uses the new phase; update
StubEmuCore::reset() to adjust m_buffers[m_writeIndex]'s phase field (or call a
helper to sync buffer state) so no old phase remains.

}

const EmuFramebuffer &StubEmuCore::framebuffer() const
{
return m_buffers[static_cast<size_t>(m_readIndex)];
}

void StubEmuCore::setHooks(EmuHooks *hooks)
{
m_hooks = hooks;
}

void StubEmuCore::fillTestPattern(EmuFramebuffer &buf)
{
const int w = buf.width;
const int h = buf.height;
auto *pixels = reinterpret_cast<uchar *>(buf.rgb24.data());
const quint8 phase = static_cast<quint8>(buf.frameIndex % 256);

for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
const int i = (y * w + x) * 3;
pixels[i + 0] = static_cast<uchar>((x + phase) & 0xFF);
pixels[i + 1] = static_cast<uchar>((y + phase) & 0xFF);
pixels[i + 2] = static_cast<uchar>(((x ^ y) + phase) & 0xFF);
}
}
}
38 changes: 38 additions & 0 deletions plugins/ps1core_stub/StubEmuCore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef STUBEMUCORE_H
#define STUBEMUCORE_H

#include "EmuCore.h"

#include <QString>
#include <array>

/** Reference / CI core: validates BIOS+ISO paths and renders a test pattern (#415). */
class StubEmuCore final : public EmuCore
{
public:
StubEmuCore();

QString coreId() const override;
bool loadBios(const QString &biosPath) override;
bool loadIso(const QString &isoPath) override;
void runFrame() override;
void reset() override;
const EmuFramebuffer &framebuffer() const override;
void setHooks(EmuHooks *hooks) override;

private:
void fillTestPattern(EmuFramebuffer &buf);

static constexpr int kWidth = 320;
static constexpr int kHeight = 240;

QString m_biosPath;
QString m_isoPath;
EmuHooks *m_hooks = nullptr;
quint64 m_frameIndex = 0;
std::array<EmuFramebuffer, 3> m_buffers{};
int m_readIndex = 0;
int m_writeIndex = 1;
};

#endif // STUBEMUCORE_H
12 changes: 12 additions & 0 deletions plugins/ps1core_stub/StubEmuCorePlugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "StubEmuCorePlugin.h"
#include "StubEmuCore.h"

QString StubEmuCorePlugin::pluginId() const
{
return QStringLiteral("stub");
}

std::unique_ptr<EmuCore> StubEmuCorePlugin::createCore()
{
return std::make_unique<StubEmuCore>();
}
18 changes: 18 additions & 0 deletions plugins/ps1core_stub/StubEmuCorePlugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef STUBEMUCOREPLUGIN_H
#define STUBEMUCOREPLUGIN_H

#include <QObject>
#include "IEmuCorePlugin.h"

class StubEmuCorePlugin : public QObject, public IEmuCorePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID IEmuCorePlugin_iid FILE "ps1core_stub.json")
Q_INTERFACES(IEmuCorePlugin)

public:
QString pluginId() const override;
std::unique_ptr<EmuCore> createCore() override;
};

#endif // STUBEMUCOREPLUGIN_H
6 changes: 6 additions & 0 deletions plugins/ps1core_stub/ps1core_stub.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Keys": [],
"MetaData": {
"description": "QtMeshEditor PS1 stub emulator core (test pattern)"
}
}
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,10 @@ if(BUILD_TESTS)

ADD_DEPENDENCIES(UnitTests ui)

if(ENABLE_PS1_RIP)
add_dependencies(UnitTests qtmesh_ps1core_stub)
endif()

# On macOS, create symlinks from Ogre plugin .framework bundles into the
# UnitTests binary directory so that plugins.cfg (PluginFolder=../) can
# find them. The app bundle has these in Contents/MacOS/ via INSTALL
Expand Down
12 changes: 12 additions & 0 deletions src/PS1/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,23 @@ ${CMAKE_CURRENT_SOURCE_DIR}/PS1TIM.h

if(ENABLE_PS1_RIP)
list(APPEND SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/runtime/EmuCoreLoader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/runtime/EmuViewport.cpp
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipLegalityDialog.cpp
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipManager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipSessionWindow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipWorker.cpp
)
list(APPEND HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/runtime/EmuCore.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/EmuCoreLoader.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/EmuFramebuffer.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/EmuHooks.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/EmuViewport.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/IEmuCorePlugin.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipLegalityDialog.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipManager.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipSessionWindow.h
${CMAKE_CURRENT_SOURCE_DIR}/runtime/PS1RipWorker.h
)
endif()
Expand Down
8 changes: 7 additions & 1 deletion src/PS1/PS1_RIP_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,13 @@ option(ENABLE_PS1_RIP "Enable experimental PS1 runtime geometry extraction" OFF)

See epic #412 for phased issues (#413–#431).

## Phase 1 status

- `EmuCore` + `IEmuCorePlugin` + `EmuCoreLoader` landed (#415).
- Stub plugin `plugins/ps1core_stub/` renders a 320×240 test pattern when BIOS+ISO paths validate.
- `PS1RipSessionWindow` + `EmuViewport` + legality dialog (#416–#417) — keyboard/gamepad input still TODO.

## Open questions

- Exact plugin ABI for `EmuCore` (#415).
- mednafen-psx plugin build/integration (replace stub for real emulation).
- BIOS / ISO first-run legality dialog copy (#417) — requires legal review before release.
28 changes: 28 additions & 0 deletions src/PS1/runtime/EmuCore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef EMUCORE_H
#define EMUCORE_H

#include "EmuFramebuffer.h"
#include "EmuHooks.h"

#include <QString>
#include <memory>

/**
* Abstract PS1 emulator core (#415). Implemented by dynamically loaded plugins
* under <app>/PS1Cores/ — never linked into the main QtMeshEditor binary.
*/
class EmuCore
{
public:
virtual ~EmuCore() = default;

virtual QString coreId() const = 0;
virtual bool loadBios(const QString &biosPath) = 0;
virtual bool loadIso(const QString &isoPath) = 0;
virtual void runFrame() = 0;
virtual void reset() = 0;
virtual const EmuFramebuffer &framebuffer() const = 0;
virtual void setHooks(EmuHooks *hooks) = 0;
};

#endif // EMUCORE_H
79 changes: 79 additions & 0 deletions src/PS1/runtime/EmuCoreLoader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "EmuCoreLoader.h"
#include "IEmuCorePlugin.h"

#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QPluginLoader>

namespace {

QString bundleOrAppDir()
{
return QCoreApplication::applicationDirPath();
}
Comment on lines +11 to +14
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use bundle-root path resolution on macOS for PS1Cores.

applicationDirPath() points to .../Contents/MacOS in app bundles, not the bundle root expected by the PR contract.

As per coding guidelines "Use macBundlePath() to get .app bundle root on macOS; Ogre resource paths in resources.cfg resolve relative to bundle root, not Contents/MacOS/".

Also applies to: 31-37

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/PS1/runtime/EmuCoreLoader.cpp` around lines 11 - 14, bundleOrAppDir()
currently returns QCoreApplication::applicationDirPath(), which points to
Contents/MacOS inside .app bundles; change it to return the bundle root on macOS
by using QCoreApplication::applicationDirPath() fallback to
QCoreApplication::applicationDirPath() only on non-macOS and use
QCoreApplication::applicationDirPath() replacement
QCoreApplication::macBundlePath() under a Q_OS_MAC (or `#ifdef` Q_OS_MAC) guard so
PS1Cores and any code referencing bundleOrAppDir() (also update the similar
logic around the other occurrence at the 31-37 region) resolve resources
relative to the .app bundle root as required by resources.cfg.


QStringList pluginFileNames()
{
#if defined(Q_OS_WIN)
return {QStringLiteral("qtmesh_ps1core_stub.dll")};
#elif defined(Q_OS_MACOS)
return {QStringLiteral("libqtmesh_ps1core_stub.dylib"),
QStringLiteral("qtmesh_ps1core_stub.dylib")};
#else
return {QStringLiteral("libqtmesh_ps1core_stub.so"),
QStringLiteral("qtmesh_ps1core_stub.so")};
#endif
Comment on lines +16 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Loader discovery is hardcoded to the stub plugin name.

This only loads qtmesh_ps1core_stub* and prevents discovering any real core plugin with a different filename.

Proposed fix
-QStringList pluginFileNames()
-{
-#if defined(Q_OS_WIN)
-    return {QStringLiteral("qtmesh_ps1core_stub.dll")};
-#elif defined(Q_OS_MACOS)
-    return {QStringLiteral("libqtmesh_ps1core_stub.dylib"),
-            QStringLiteral("qtmesh_ps1core_stub.dylib")};
-#else
-    return {QStringLiteral("libqtmesh_ps1core_stub.so"),
-            QStringLiteral("qtmesh_ps1core_stub.so")};
-#endif
-}
+QStringList pluginFileNames(const QDir &dir)
+{
+    QStringList out;
+    const QFileInfoList entries = dir.entryInfoList(QDir::Files);
+    for (const QFileInfo &fi : entries) {
+        if (QLibrary::isLibrary(fi.fileName()))
+            out << fi.fileName();
+    }
+    return out;
+}
...
-    const QStringList names = pluginFileNames();
     for (const QString &dirPath : coreSearchPaths()) {
         const QDir dir(dirPath);
         if (!dir.exists())
             continue;
 
-        for (const QString &fileName : names) {
+        for (const QString &fileName : pluginFileNames(dir)) {
             const QString pluginPath = dir.filePath(fileName);

Also applies to: 43-51

🧰 Tools
🪛 Cppcheck (2.20.0)

[error] 22-22: There is an unknown macro here somewhere. Configuration is required. If Q_DECLARE_INTERFACE is a macro then please configure it.

(unknownMacro)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/PS1/runtime/EmuCoreLoader.cpp` around lines 16 - 26, The plugin discovery
currently hardcodes only the "qtmesh_ps1core_stub" filenames in the
pluginFileNames() implementation, preventing loading of real core plugins;
change pluginFileNames() (and the duplicate implementation further down) to
either return broader platform-specific name patterns like "qtmesh_ps1core*" or
replace the hardcoded list with a small discovery routine that uses
QDir/QDir::entryList name filters (e.g.,
"qtmesh_ps1core*.dll"/"qtmesh_ps1core*.dylib"/"qtmesh_ps1core*.so") to enumerate
all matching files in the plugin directory so real core plugins are found in
addition to the stub.

}

} // namespace

QStringList EmuCoreLoader::coreSearchPaths()
{
QStringList paths;
const QString base = bundleOrAppDir();
paths << QDir(base).filePath(QStringLiteral("PS1Cores"));
paths << QDir(QCoreApplication::applicationDirPath()).filePath(QStringLiteral("PS1Cores"));
paths.removeDuplicates();
return paths;
}

std::unique_ptr<EmuCore> EmuCoreLoader::loadCore(QString *errorOut)
{
const QStringList names = pluginFileNames();
for (const QString &dirPath : coreSearchPaths()) {
const QDir dir(dirPath);
if (!dir.exists())
continue;

for (const QString &fileName : names) {
const QString pluginPath = dir.filePath(fileName);
if (!QFileInfo::exists(pluginPath))
continue;

QPluginLoader loader(pluginPath);
QObject *instance = loader.instance();
if (!instance) {
if (errorOut)

Check failure on line 57 in src/PS1/runtime/EmuCoreLoader.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ43hKPbL63nvjzR2nYR&open=AZ43hKPbL63nvjzR2nYR&pullRequest=577
*errorOut = loader.errorString();
continue;
}

auto *plugin = qobject_cast<IEmuCorePlugin *>(instance);
if (!plugin) {
if (errorOut)

Check failure on line 64 in src/PS1/runtime/EmuCoreLoader.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ43hKPcL63nvjzR2nYS&open=AZ43hKPcL63nvjzR2nYS&pullRequest=577
*errorOut = QObject::tr("Plugin does not implement IEmuCorePlugin: %1").arg(pluginPath);
continue;
}

return plugin->createCore();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle null createCore() and keep searching candidates.

If a plugin loads but returns nullptr, the loader exits early instead of trying other plugins and returning a useful error.

Proposed fix
-            return plugin->createCore();
+            if (std::unique_ptr<EmuCore> core = plugin->createCore())
+                return core;
+            if (errorOut)
+                *errorOut = QObject::tr("Plugin loaded but failed to create core: %1").arg(pluginPath);
+            continue;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/PS1/runtime/EmuCoreLoader.cpp` at line 69, The current loader returns
immediately whatever plugin->createCore() yields, so if createCore() returns
nullptr the loader exits without trying other candidates; change the logic in
EmuCoreLoader.cpp where plugin->createCore() is invoked to call and store the
result in a variable (e.g., auto core = plugin->createCore()), check if core !=
nullptr and return it only then, otherwise log/ignore that plugin and continue
iterating over remaining plugins; after the loop, return nullptr (or produce the
existing error path) to indicate no plugin produced a valid core.

}
}

if (errorOut) {
*errorOut = QObject::tr(
"No PS1 emulator core found in PS1Cores/. "
"Build with ENABLE_PS1_RIP=ON to install the stub core.");
}
return nullptr;
}
19 changes: 19 additions & 0 deletions src/PS1/runtime/EmuCoreLoader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef EMUCORELOADER_H
#define EMUCORELOADER_H

#include "EmuCore.h"

#include <QString>
#include <memory>

/**
* Loads an EmuCore implementation from <app>/PS1Cores/*.so at runtime.

Check warning on line 10 in src/PS1/runtime/EmuCoreLoader.h

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the misleading "/*" characters.

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ43hKZvL63nvjzR2nYc&open=AZ43hKZvL63nvjzR2nYc&pullRequest=577
*/
class EmuCoreLoader
{
public:
static QStringList coreSearchPaths();
static std::unique_ptr<EmuCore> loadCore(QString *errorOut = nullptr);
};

#endif // EMUCORELOADER_H
Loading
Loading