Merge branch 'melonDS-emu:master' into mem9_timings
This commit is contained in:
commit
9ad2ff3675
|
@ -1,55 +0,0 @@
|
|||
name: AppImage
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
||||
sudo apt update
|
||||
sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libqt5multimedia5-plugins qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades
|
||||
- name: Create build environment
|
||||
run: mkdir ${{runner.workspace}}/build
|
||||
- name: Configure
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE
|
||||
- name: Make
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
make -j$(nproc --all)
|
||||
- name: Prepare AppDir for AppImage
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
make install DESTDIR=AppDir
|
||||
mv ./AppDir/usr/local/bin ./AppDir/usr/bin
|
||||
mv ./AppDir/usr/local/share ./AppDir/usr/share
|
||||
rm -rf ./AppDir/usr/local
|
||||
- name: Prepare necessary Tools for building the AppImage
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
- name: Build the AppImage
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: |
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
|
||||
mkdir dist
|
||||
cp ./melonDS*.AppImage ./dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: melonDS-appimage-x86_64
|
||||
path: ${{runner.workspace}}/build/dist
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
arch: [x86_64, arm64]
|
||||
|
||||
name: ${{ matrix.arch }}
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Check out sources
|
||||
uses: actions/checkout@v3
|
||||
|
@ -27,7 +27,7 @@ jobs:
|
|||
- name: Set up vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
vcpkgGitCommitId: c8696863d371ab7f46e213d8f5ca923c4aef2a00
|
||||
vcpkgGitCommitId: 53bef8994c541b6561884a8395ea35715ece75db
|
||||
- name: Build
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
|
@ -48,6 +48,7 @@ jobs:
|
|||
name: Universal binary
|
||||
needs: [build-macos]
|
||||
runs-on: macos-13
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Download x86_64
|
||||
uses: actions/download-artifact@v4
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
name: Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: aarch64
|
||||
runs-on: ubuntu-20.04
|
||||
container: ubuntu:20.04
|
||||
|
||||
steps:
|
||||
- name: Prepare system
|
||||
shell: bash
|
||||
run: |
|
||||
apt update
|
||||
apt -y full-upgrade
|
||||
apt -y install git
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v1
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
dpkg --add-architecture arm64
|
||||
sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
|
||||
rm /etc/apt/sources.list
|
||||
mv /etc/apt/sources.list{.new,}
|
||||
apt update
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu {libsdl2,qtbase5,qtbase5-private,qtmultimedia5,libslirp,libarchive,libzstd}-dev:arm64 zstd:arm64 cmake extra-cmake-modules dpkg-dev
|
||||
- name: Configure
|
||||
shell: bash
|
||||
run: |
|
||||
CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -B build
|
||||
- name: Make
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build build -j$(nproc --all)
|
||||
mkdir dist
|
||||
cp build/melonDS dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: melonDS-ubuntu-aarch64
|
||||
path: dist
|
|
@ -9,30 +9,77 @@ on:
|
|||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
build-x86_64:
|
||||
name: x86_64
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
name: Check out sources
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
||||
sudo apt update
|
||||
sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades
|
||||
- name: Create build environment
|
||||
run: mkdir ${{runner.workspace}}/build
|
||||
sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev \
|
||||
qt6-{base,base-private,multimedia}-dev libslirp0 libslirp-dev libarchive-dev libzstd-dev libfuse2
|
||||
- name: Configure
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE
|
||||
- name: Make
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- name: Build
|
||||
run: |
|
||||
make -j$(nproc --all)
|
||||
mkdir dist
|
||||
cp melonDS dist
|
||||
- uses: actions/upload-artifact@v1
|
||||
cmake --build build
|
||||
DESTDIR=AppDir cmake --install build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-ubuntu-x86_64
|
||||
path: ${{runner.workspace}}/build/dist
|
||||
path: AppDir/usr/bin/melonDS
|
||||
- name: Fetch AppImage tools
|
||||
run: |
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod a+x linuxdeploy-*.AppImage
|
||||
- name: Build the AppImage
|
||||
env:
|
||||
QMAKE: /usr/lib/qt6/bin/qmake
|
||||
run: |
|
||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-appimage-x86_64
|
||||
path: melonDS*.AppImage
|
||||
|
||||
build-aarch64:
|
||||
name: aarch64
|
||||
runs-on: ubuntu-latest
|
||||
container: ubuntu:22.04
|
||||
|
||||
steps:
|
||||
- name: Prepare system
|
||||
shell: bash
|
||||
run: |
|
||||
dpkg --add-architecture arm64
|
||||
sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
|
||||
rm /etc/apt/sources.list
|
||||
mv /etc/apt/sources.list{.new,}
|
||||
apt update
|
||||
apt -y full-upgrade
|
||||
apt -y install git {gcc-12,g++-12}-aarch64-linux-gnu cmake ninja-build extra-cmake-modules \
|
||||
{libsdl2,qt6-{base,base-private,multimedia},libslirp,libarchive,libzstd}-dev:arm64 \
|
||||
pkg-config dpkg-dev
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
- name: Configure
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \
|
||||
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \
|
||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \
|
||||
-DUSE_QT6=ON
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: melonDS-ubuntu-aarch64
|
||||
path: build/melonDS
|
||||
|
|
|
@ -7,7 +7,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
|
|||
file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE)
|
||||
FetchContent_Declare(vcpkg
|
||||
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
|
||||
GIT_TAG 2023.12.12
|
||||
GIT_TAG 2024.01.12
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||
FetchContent_MakeAvailable(vcpkg)
|
||||
endif()
|
||||
|
|
|
@ -154,11 +154,13 @@ endif()
|
|||
|
||||
if (WIN32)
|
||||
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
|
||||
elseif(NOT APPLE)
|
||||
elseif(NOT APPLE AND NOT HAIKU)
|
||||
check_library_exists(rt shm_open "" NEED_LIBRT)
|
||||
if (NEED_LIBRT)
|
||||
target_link_libraries(core PRIVATE rt)
|
||||
endif()
|
||||
elseif(HAIKU)
|
||||
target_link_libraries(core PRIVATE network)
|
||||
endif()
|
||||
|
||||
if (ENABLE_JIT_PROFILING)
|
||||
|
|
|
@ -136,6 +136,11 @@ enum FileMode : unsigned {
|
|||
*/
|
||||
Text = 0b01'00'00,
|
||||
|
||||
/**
|
||||
* Opens a file in append mode.
|
||||
*/
|
||||
Append = 0b10'00'00,
|
||||
|
||||
/**
|
||||
* Opens a file for reading and writing.
|
||||
* Equivalent to <tt>Read | Write</tt>.
|
||||
|
@ -201,6 +206,13 @@ FileHandle* OpenLocalFile(const std::string& path, FileMode mode);
|
|||
bool FileExists(const std::string& name);
|
||||
bool LocalFileExists(const std::string& name);
|
||||
|
||||
// Returns true if we have permission to write to the file.
|
||||
// Warning: Also creates the file if not present!
|
||||
bool CheckFileWritable(const std::string& filepath);
|
||||
|
||||
// Same as above (CheckFileWritable()) but for local files.
|
||||
bool CheckLocalFileWritable(const std::string& filepath);
|
||||
|
||||
/** Close a file opened with \c OpenFile.
|
||||
* @returns \c true if the file was closed successfully, false otherwise.
|
||||
* @post \c file is no longer valid and should not be used.
|
||||
|
|
|
@ -142,6 +142,7 @@ bool MouseHide;
|
|||
int MouseHideSeconds;
|
||||
|
||||
bool PauseLostFocus;
|
||||
std::string UITheme;
|
||||
|
||||
int64_t RTCOffset;
|
||||
|
||||
|
@ -344,6 +345,7 @@ ConfigEntry ConfigFile[] =
|
|||
{"MouseHide", 1, &MouseHide, false, false},
|
||||
{"MouseHideSeconds", 0, &MouseHideSeconds, 5, false},
|
||||
{"PauseLostFocus", 1, &PauseLostFocus, false, false},
|
||||
{"UITheme", 2, &UITheme, (std::string)"", false},
|
||||
|
||||
{"RTCOffset", 3, &RTCOffset, (int64_t)0, true},
|
||||
|
||||
|
@ -376,7 +378,7 @@ ConfigEntry ConfigFile[] =
|
|||
};
|
||||
|
||||
|
||||
void LoadFile(int inst)
|
||||
bool LoadFile(int inst, int actualinst)
|
||||
{
|
||||
Platform::FileHandle* f;
|
||||
if (inst > 0)
|
||||
|
@ -384,11 +386,17 @@ void LoadFile(int inst)
|
|||
char name[100] = {0};
|
||||
snprintf(name, 99, kUniqueConfigFile, inst+1);
|
||||
f = Platform::OpenLocalFile(name, Platform::FileMode::ReadText);
|
||||
|
||||
if (!Platform::CheckLocalFileWritable(name)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
f = Platform::OpenLocalFile(kConfigFile, Platform::FileMode::ReadText);
|
||||
|
||||
if (!f) return;
|
||||
if (actualinst == 0 && !Platform::CheckLocalFileWritable(kConfigFile)) return false;
|
||||
}
|
||||
|
||||
if (!f) return true;
|
||||
|
||||
char linebuf[1024];
|
||||
char entryname[32];
|
||||
|
@ -423,9 +431,10 @@ void LoadFile(int inst)
|
|||
}
|
||||
|
||||
CloseFile(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Load()
|
||||
bool Load()
|
||||
{
|
||||
|
||||
for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
|
||||
|
@ -438,12 +447,14 @@ void Load()
|
|||
case 3: *(int64_t*)entry->Value = std::get<int64_t>(entry->Default); break;
|
||||
}
|
||||
}
|
||||
|
||||
LoadFile(0);
|
||||
|
||||
|
||||
int inst = Platform::InstanceID();
|
||||
|
||||
bool ret = LoadFile(0, inst);
|
||||
if (inst > 0)
|
||||
LoadFile(inst);
|
||||
ret = LoadFile(inst, inst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Save()
|
||||
|
|
|
@ -185,6 +185,7 @@ extern bool EnableCheats;
|
|||
extern bool MouseHide;
|
||||
extern int MouseHideSeconds;
|
||||
extern bool PauseLostFocus;
|
||||
extern std::string UITheme;
|
||||
|
||||
extern int64_t RTCOffset;
|
||||
|
||||
|
@ -203,7 +204,7 @@ extern bool GdbARM7BreakOnStartup;
|
|||
extern bool GdbARM9BreakOnStartup;
|
||||
|
||||
|
||||
void Load();
|
||||
bool Load();
|
||||
void Save();
|
||||
|
||||
}
|
||||
|
|
|
@ -380,6 +380,12 @@ void EmuSettingsDialog::on_btnFirmwareBrowse_clicked()
|
|||
|
||||
if (file.isEmpty()) return;
|
||||
|
||||
if (!Platform::CheckFileWritable(file.toStdString()))
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", "Unable to write to firmware file.\nPlease check file/folder write permissions.");
|
||||
return;
|
||||
}
|
||||
|
||||
updateLastBIOSFolder(file);
|
||||
|
||||
ui->txtFirmwarePath->setText(file);
|
||||
|
@ -436,6 +442,12 @@ void EmuSettingsDialog::on_btnDLDISDBrowse_clicked()
|
|||
|
||||
if (file.isEmpty()) return;
|
||||
|
||||
if (!Platform::CheckFileWritable(file.toStdString()))
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", "Unable to write to DLDI SD image.\nPlease check file/folder write permissions.");
|
||||
return;
|
||||
}
|
||||
|
||||
updateLastBIOSFolder(file);
|
||||
|
||||
ui->txtDLDISDPath->setText(file);
|
||||
|
@ -468,6 +480,13 @@ void EmuSettingsDialog::on_btnDSiFirmwareBrowse_clicked()
|
|||
|
||||
if (file.isEmpty()) return;
|
||||
|
||||
if (!Platform::CheckFileWritable(file.toStdString()))
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", "Unable to write to DSi firmware file.\nPlease check file/folder write permissions.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
updateLastBIOSFolder(file);
|
||||
|
||||
ui->txtDSiFirmwarePath->setText(file);
|
||||
|
@ -482,6 +501,13 @@ void EmuSettingsDialog::on_btnDSiNANDBrowse_clicked()
|
|||
|
||||
if (file.isEmpty()) return;
|
||||
|
||||
if (!Platform::CheckFileWritable(file.toStdString()))
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", "Unable to write to DSi NAND image.\nPlease check file/folder write permissions.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
updateLastBIOSFolder(file);
|
||||
|
||||
ui->txtDSiNANDPath->setText(file);
|
||||
|
@ -510,6 +536,12 @@ void EmuSettingsDialog::on_btnDSiSDBrowse_clicked()
|
|||
|
||||
if (file.isEmpty()) return;
|
||||
|
||||
if (!Platform::CheckFileWritable(file.toStdString()))
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", "Unable to write to DSi SD image.\nPlease check file/folder write permissions.");
|
||||
return;
|
||||
}
|
||||
|
||||
updateLastBIOSFolder(file);
|
||||
|
||||
ui->txtDSiSDPath->setText(file);
|
||||
|
|
|
@ -16,15 +16,16 @@
|
|||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include <QStyleFactory>
|
||||
#include "InterfaceSettingsDialog.h"
|
||||
#include "ui_InterfaceSettingsDialog.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "Platform.h"
|
||||
#include "Config.h"
|
||||
#include "main.h"
|
||||
|
||||
InterfaceSettingsDialog* InterfaceSettingsDialog::currentDlg = nullptr;
|
||||
|
||||
InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InterfaceSettingsDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
@ -35,6 +36,18 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare
|
|||
ui->spinMouseHideSeconds->setValue(Config::MouseHideSeconds);
|
||||
ui->cbPauseLostFocus->setChecked(Config::PauseLostFocus != 0);
|
||||
ui->spinMaxFPS->setValue(Config::MaxFPS);
|
||||
|
||||
const QList<QString> themeKeys = QStyleFactory::keys();
|
||||
const QString currentTheme = qApp->style()->objectName();
|
||||
|
||||
ui->cbxUITheme->addItem("System default", "");
|
||||
|
||||
for (int i = 0; i < themeKeys.length(); i++)
|
||||
{
|
||||
ui->cbxUITheme->addItem(themeKeys[i], themeKeys[i]);
|
||||
if (!Config::UITheme.empty() && themeKeys[i].compare(currentTheme, Qt::CaseInsensitive) == 0)
|
||||
ui->cbxUITheme->setCurrentIndex(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceSettingsDialog::~InterfaceSettingsDialog()
|
||||
|
@ -63,8 +76,16 @@ void InterfaceSettingsDialog::done(int r)
|
|||
Config::PauseLostFocus = ui->cbPauseLostFocus->isChecked() ? 1:0;
|
||||
Config::MaxFPS = ui->spinMaxFPS->value();
|
||||
|
||||
QString themeName = ui->cbxUITheme->currentData().toString();
|
||||
Config::UITheme = themeName.toStdString();
|
||||
|
||||
Config::Save();
|
||||
|
||||
if (!Config::UITheme.empty())
|
||||
qApp->setStyle(themeName);
|
||||
else
|
||||
qApp->setStyle(*systemThemeName);
|
||||
|
||||
emit updateMouseTimer();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>337</width>
|
||||
<height>233</height>
|
||||
<height>275</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -20,12 +20,29 @@
|
|||
<string>Interface settings - melonDS</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
|
||||
<item alignment="Qt::AlignTop">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Main window</string>
|
||||
<string>User interface</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Theme</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>cbxUITheme</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cbxUITheme"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbMouseHide">
|
||||
<property name="text">
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <stdio.h>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include "types.h"
|
||||
#include "Config.h"
|
||||
|
@ -37,6 +38,7 @@ extern bool RunningSomething;
|
|||
|
||||
bool PathSettingsDialog::needsReset = false;
|
||||
|
||||
constexpr char errordialog[] = "melonDS cannot write to that directory.";
|
||||
|
||||
PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PathSettingsDialog)
|
||||
{
|
||||
|
@ -101,6 +103,12 @@ void PathSettingsDialog::on_btnSaveFileBrowse_clicked()
|
|||
QString::fromStdString(EmuDirectory));
|
||||
|
||||
if (dir.isEmpty()) return;
|
||||
|
||||
if (!QTemporaryFile(dir).open())
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", errordialog);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->txtSaveFilePath->setText(dir);
|
||||
}
|
||||
|
@ -112,6 +120,12 @@ void PathSettingsDialog::on_btnSavestateBrowse_clicked()
|
|||
QString::fromStdString(EmuDirectory));
|
||||
|
||||
if (dir.isEmpty()) return;
|
||||
|
||||
if (!QTemporaryFile(dir).open())
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", errordialog);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->txtSavestatePath->setText(dir);
|
||||
}
|
||||
|
@ -123,6 +137,12 @@ void PathSettingsDialog::on_btnCheatFileBrowse_clicked()
|
|||
QString::fromStdString(EmuDirectory));
|
||||
|
||||
if (dir.isEmpty()) return;
|
||||
|
||||
if (!QTemporaryFile(dir).open())
|
||||
{
|
||||
QMessageBox::critical(this, "melonDS", errordialog);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->txtCheatFilePath->setText(dir);
|
||||
}
|
||||
|
|
|
@ -217,6 +217,10 @@ std::string InstanceFileSuffix()
|
|||
|
||||
constexpr char AccessMode(FileMode mode, bool file_exists)
|
||||
{
|
||||
|
||||
if (mode & FileMode::Append)
|
||||
return 'a';
|
||||
|
||||
if (!(mode & FileMode::Write))
|
||||
// If we're only opening the file for reading...
|
||||
return 'r';
|
||||
|
@ -255,7 +259,7 @@ static std::string GetModeString(FileMode mode, bool file_exists)
|
|||
|
||||
FileHandle* OpenFile(const std::string& path, FileMode mode)
|
||||
{
|
||||
if ((mode & FileMode::ReadWrite) == FileMode::None)
|
||||
if ((mode & (FileMode::ReadWrite | FileMode::Append)) == FileMode::None)
|
||||
{ // If we aren't reading or writing, then we can't open the file
|
||||
Log(LogLevel::Error, "Attempted to open \"%s\" in neither read nor write mode (FileMode 0x%x)\n", path.c_str(), mode);
|
||||
return nullptr;
|
||||
|
@ -327,6 +331,28 @@ bool LocalFileExists(const std::string& name)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CheckFileWritable(const std::string& filepath)
|
||||
{
|
||||
FileHandle* file = Platform::OpenFile(filepath.c_str(), FileMode::Append);
|
||||
if (file)
|
||||
{
|
||||
Platform::CloseFile(file);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool CheckLocalFileWritable(const std::string& name)
|
||||
{
|
||||
FileHandle* file = Platform::OpenLocalFile(name.c_str(), FileMode::Append);
|
||||
if (file)
|
||||
{
|
||||
Platform::CloseFile(file);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool FileSeek(FileHandle* file, s64 offset, FileSeekOrigin origin)
|
||||
{
|
||||
int stdorigin;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <fstream>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <zstd.h>
|
||||
#ifdef ARCHIVE_SUPPORT_ENABLED
|
||||
|
@ -210,6 +211,9 @@ QString VerifyDSFirmware()
|
|||
f = Platform::OpenLocalFile(Config::FirmwarePath, FileMode::Read);
|
||||
if (!f) return "DS firmware was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
if (!Platform::CheckFileWritable(Config::FirmwarePath))
|
||||
return "DS firmware is unable to be written to.\nPlease check file/folder write permissions.";
|
||||
|
||||
len = FileLength(f);
|
||||
if (len == 0x20000)
|
||||
{
|
||||
|
@ -237,6 +241,9 @@ QString VerifyDSiFirmware()
|
|||
f = Platform::OpenLocalFile(Config::DSiFirmwarePath, FileMode::Read);
|
||||
if (!f) return "DSi firmware was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
if (!Platform::CheckFileWritable(Config::FirmwarePath))
|
||||
return "DSi firmware is unable to be written to.\nPlease check file/folder write permissions.";
|
||||
|
||||
len = FileLength(f);
|
||||
if (len != 0x20000)
|
||||
{
|
||||
|
@ -259,6 +266,9 @@ QString VerifyDSiNAND()
|
|||
f = Platform::OpenLocalFile(Config::DSiNANDPath, FileMode::ReadWriteExisting);
|
||||
if (!f) return "DSi NAND was not found or could not be accessed. Check your emu settings.";
|
||||
|
||||
if (!Platform::CheckFileWritable(Config::FirmwarePath))
|
||||
return "DSi NAND is unable to be written to.\nPlease check file/folder write permissions.";
|
||||
|
||||
// TODO: some basic checks
|
||||
// check that it has the nocash footer, and all
|
||||
|
||||
|
@ -1276,7 +1286,18 @@ bool LoadROMData(const QStringList& filepath, std::unique_ptr<u8[]>& filedata, u
|
|||
return false;
|
||||
}
|
||||
|
||||
bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
|
||||
QString GetSavErrorString(std::string& filepath, bool gba)
|
||||
{
|
||||
std::string console = gba ? "GBA" : "DS";
|
||||
std::string err1 = "Unable to write to ";
|
||||
std::string err2 = " save.\nPlease check file/folder write permissions.\n\nAttempted to Access:\n";
|
||||
|
||||
err1 += console + err2 + filepath;
|
||||
|
||||
return QString::fromStdString(err1);
|
||||
}
|
||||
|
||||
bool LoadROM(QMainWindow* mainWindow, EmuThread* emuthread, QStringList filepath, bool reset)
|
||||
{
|
||||
unique_ptr<u8[]> filedata = nullptr;
|
||||
u32 filelen;
|
||||
|
@ -1284,7 +1305,10 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
|
|||
std::string romname;
|
||||
|
||||
if (!LoadROMData(filepath, filedata, filelen, basepath, romname))
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
|
||||
return false;
|
||||
}
|
||||
|
||||
NDSSave = nullptr;
|
||||
|
||||
|
@ -1300,7 +1324,22 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
|
|||
savname += Platform::InstanceFileSuffix();
|
||||
|
||||
FileHandle* sav = Platform::OpenFile(savname, FileMode::Read);
|
||||
if (!sav) sav = Platform::OpenFile(origsav, FileMode::Read);
|
||||
if (!sav)
|
||||
{
|
||||
if (!Platform::CheckFileWritable(origsav))
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(origsav, false));
|
||||
return false;
|
||||
}
|
||||
|
||||
sav = Platform::OpenFile(origsav, FileMode::Read);
|
||||
}
|
||||
else if (!Platform::CheckFileWritable(savname))
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(savname, false));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sav)
|
||||
{
|
||||
savelen = (u32)Platform::FileLength(sav);
|
||||
|
@ -1322,13 +1361,19 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
|
|||
|
||||
auto cart = NDSCart::ParseROM(std::move(filedata), filelen, std::move(cartargs));
|
||||
if (!cart)
|
||||
{
|
||||
// If we couldn't parse the ROM...
|
||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reset)
|
||||
{
|
||||
if (!emuthread->UpdateConsole(std::move(cart), Keep {}))
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
|
||||
return false;
|
||||
}
|
||||
|
||||
InitFirmwareSaveManager(emuthread);
|
||||
emuthread->NDS->Reset();
|
||||
|
@ -1351,7 +1396,7 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
|
|||
NDSSave = std::make_unique<SaveManager>(savname);
|
||||
LoadCheats(*emuthread->NDS);
|
||||
|
||||
return true;
|
||||
return true; // success
|
||||
}
|
||||
|
||||
void EjectCart(NDS& nds)
|
||||
|
@ -1388,9 +1433,13 @@ QString CartLabel()
|
|||
}
|
||||
|
||||
|
||||
bool LoadGBAROM(NDS& nds, QStringList filepath)
|
||||
bool LoadGBAROM(QMainWindow* mainWindow, NDS& nds, QStringList filepath)
|
||||
{
|
||||
if (nds.ConsoleType == 1) return false; // DSi doesn't have a GBA slot
|
||||
if (nds.ConsoleType == 1)
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", "The DSi doesn't have a GBA slot.");
|
||||
return false;
|
||||
}
|
||||
|
||||
unique_ptr<u8[]> filedata = nullptr;
|
||||
u32 filelen;
|
||||
|
@ -1398,7 +1447,10 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
|
|||
std::string romname;
|
||||
|
||||
if (!LoadROMData(filepath, filedata, filelen, basepath, romname))
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM.");
|
||||
return false;
|
||||
}
|
||||
|
||||
GBASave = nullptr;
|
||||
|
||||
|
@ -1414,7 +1466,22 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
|
|||
savname += Platform::InstanceFileSuffix();
|
||||
|
||||
FileHandle* sav = Platform::OpenFile(savname, FileMode::Read);
|
||||
if (!sav) sav = Platform::OpenFile(origsav, FileMode::Read);
|
||||
if (!sav)
|
||||
{
|
||||
if (!Platform::CheckFileWritable(origsav))
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(origsav, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
sav = Platform::OpenFile(origsav, FileMode::Read);
|
||||
}
|
||||
else if (!Platform::CheckFileWritable(savname))
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(savname, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sav)
|
||||
{
|
||||
savelen = (u32)FileLength(sav);
|
||||
|
@ -1430,7 +1497,10 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
|
|||
|
||||
auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen);
|
||||
if (!cart)
|
||||
{
|
||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM.");
|
||||
return false;
|
||||
}
|
||||
|
||||
nds.SetGBACart(std::move(cart));
|
||||
GBACartType = 0;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "SaveManager.h"
|
||||
#include "AREngine.h"
|
||||
#include "DSi_NAND.h"
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "MemConstants.h"
|
||||
#include <optional>
|
||||
|
@ -72,12 +73,12 @@ std::optional<Firmware> LoadFirmware(int type) noexcept;
|
|||
std::optional<DSi_NAND::NANDImage> LoadNAND(const std::array<u8, DSiBIOSSize>& arm7ibios) noexcept;
|
||||
|
||||
/// Inserts a ROM into the emulated console.
|
||||
bool LoadROM(EmuThread*, QStringList filepath, bool reset);
|
||||
bool LoadROM(QMainWindow* mainWindow, EmuThread*, QStringList filepath, bool reset);
|
||||
void EjectCart(NDS& nds);
|
||||
bool CartInserted();
|
||||
QString CartLabel();
|
||||
|
||||
bool LoadGBAROM(NDS& nds, QStringList filepath);
|
||||
bool LoadGBAROM(QMainWindow* mainWindow, NDS& nds, QStringList filepath);
|
||||
void LoadGBAAddon(NDS& nds, int type);
|
||||
void EjectGBACart(NDS& nds);
|
||||
bool GBACartInserted();
|
||||
|
|
|
@ -865,10 +865,8 @@ void MainWindow::dropEvent(QDropEvent* event)
|
|||
|
||||
if (isNdsRom)
|
||||
{
|
||||
if (!ROMManager::LoadROM(emuThread, file, true))
|
||||
if (!ROMManager::LoadROM(mainWindow, emuThread, file, true))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the DS ROM.");
|
||||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
@ -886,10 +884,8 @@ void MainWindow::dropEvent(QDropEvent* event)
|
|||
}
|
||||
else if (isGbaRom)
|
||||
{
|
||||
if (!ROMManager::LoadGBAROM(*emuThread->NDS, file))
|
||||
if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, file))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM.");
|
||||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
@ -952,12 +948,7 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
|
|||
bool gbaloaded = false;
|
||||
if (!gbafile.isEmpty())
|
||||
{
|
||||
if (!ROMManager::LoadGBAROM(*emuThread->NDS, gbafile))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM.");
|
||||
return false;
|
||||
}
|
||||
if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, gbafile)) return false;
|
||||
|
||||
gbaloaded = true;
|
||||
}
|
||||
|
@ -965,12 +956,8 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
|
|||
bool ndsloaded = false;
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
if (!ROMManager::LoadROM(emuThread, file, true))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
|
||||
return false;
|
||||
}
|
||||
if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) return false;
|
||||
|
||||
recentFileList.removeAll(file.join("|"));
|
||||
recentFileList.prepend(file.join("|"));
|
||||
updateRecentFilesMenu();
|
||||
|
@ -1173,11 +1160,9 @@ void MainWindow::onOpenFile()
|
|||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ROMManager::LoadROM(emuThread, file, true))
|
||||
|
||||
if (!ROMManager::LoadROM(mainWindow, emuThread, file, true))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
|
||||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
@ -1272,11 +1257,9 @@ void MainWindow::onClickRecentFile()
|
|||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ROMManager::LoadROM(emuThread, file, true))
|
||||
|
||||
if (!ROMManager::LoadROM(mainWindow, emuThread, file, true))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
|
||||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
@ -1326,10 +1309,8 @@ void MainWindow::onInsertCart()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!ROMManager::LoadROM(emuThread, file, false))
|
||||
if (!ROMManager::LoadROM(mainWindow, emuThread, file, false))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
|
||||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
@ -1361,10 +1342,8 @@ void MainWindow::onInsertGBACart()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!ROMManager::LoadGBAROM(*emuThread->NDS, file))
|
||||
if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, file))
|
||||
{
|
||||
// TODO: better error reporting?
|
||||
QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
|
||||
emuThread->emuUnpause();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" };
|
|||
QString GbaRomMimeType = "application/x-gba-rom";
|
||||
QStringList GbaRomExtensions { ".gba", ".agb" };
|
||||
|
||||
QString* systemThemeName;
|
||||
|
||||
// This list of supported archive formats is based on libarchive(3) version 3.6.2 (2022-12-09).
|
||||
QStringList ArchiveMimeTypes
|
||||
|
@ -292,6 +293,11 @@ int main(int argc, char** argv)
|
|||
|
||||
qputenv("QT_SCALE_FACTOR", "1");
|
||||
|
||||
#if QT_VERSION_MAJOR == 6 && defined(__WIN32__)
|
||||
// Allow using the system dark theme palette on Windows
|
||||
qputenv("QT_QPA_PLATFORM", "windows:darkmode=2");
|
||||
#endif
|
||||
|
||||
printf("melonDS " MELONDS_VERSION "\n");
|
||||
printf(MELONDS_URL "\n");
|
||||
|
||||
|
@ -331,7 +337,7 @@ int main(int argc, char** argv)
|
|||
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
||||
SDL_EnableScreenSaver(); SDL_DisableScreenSaver();
|
||||
|
||||
Config::Load();
|
||||
if (!Config::Load()) QMessageBox::critical(NULL, "melonDS", "Unable to write to config.\nPlease check the write permissions of the folder you placed melonDS in.");
|
||||
|
||||
#define SANITIZE(var, min, max) { var = std::clamp(var, min, max); }
|
||||
SANITIZE(Config::ConsoleType, 0, 1);
|
||||
|
@ -360,6 +366,12 @@ int main(int argc, char** argv)
|
|||
camManager[0]->setXFlip(Config::Camera[0].XFlip);
|
||||
camManager[1]->setXFlip(Config::Camera[1].XFlip);
|
||||
|
||||
systemThemeName = new QString(QApplication::style()->objectName());
|
||||
|
||||
if (!Config::UITheme.empty())
|
||||
{
|
||||
QApplication::setStyle(QString::fromStdString(Config::UITheme));
|
||||
}
|
||||
|
||||
Input::JoystickID = Config::JoystickID;
|
||||
Input::OpenJoystick();
|
||||
|
|
|
@ -44,4 +44,6 @@ public:
|
|||
bool event(QEvent* event) override;
|
||||
};
|
||||
|
||||
extern QString* systemThemeName;
|
||||
|
||||
#endif // MAIN_H
|
||||
|
|
|
@ -27,6 +27,9 @@ A million repetitions of "a"
|
|||
#if defined(__sun)
|
||||
#include "solarisfixes.h"
|
||||
#endif
|
||||
#if defined(__HAIKU__)
|
||||
#include <ByteOrder.h>
|
||||
#endif
|
||||
#include "sha1.h"
|
||||
|
||||
#ifndef BYTE_ORDER
|
||||
|
|
Loading…
Reference in New Issue