Merge branch 'master' of https://github.com/GPUCode/citra into vulkan-2
This commit is contained in:
@ -12,6 +12,7 @@ import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SubMenu;
|
||||
@ -19,11 +20,13 @@ import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
@ -74,6 +77,7 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 15;
|
||||
public static final int MENU_ACTION_DPAD_SLIDE_ENABLE = 16;
|
||||
public static final int MENU_ACTION_OPEN_CHEATS = 17;
|
||||
public static final int MENU_ACTION_CLOSE_GAME = 18;
|
||||
|
||||
public static final int REQUEST_SELECT_AMIIBO = 2;
|
||||
private static final int EMULATION_RUNNING_NOTIFICATION = 0x1000;
|
||||
@ -114,6 +118,8 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
EmulationActivity.MENU_ACTION_DPAD_SLIDE_ENABLE);
|
||||
buttonsActionsMap
|
||||
.append(R.id.menu_emulation_open_cheats, EmulationActivity.MENU_ACTION_OPEN_CHEATS);
|
||||
buttonsActionsMap
|
||||
.append(R.id.menu_emulation_close_game, EmulationActivity.MENU_ACTION_CLOSE_GAME);
|
||||
}
|
||||
|
||||
private View mDecorView;
|
||||
@ -223,21 +229,12 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
NativeLibrary.PauseEmulation();
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.emulation_close_game)
|
||||
.setMessage(R.string.emulation_close_game_message)
|
||||
.setPositiveButton(android.R.string.yes, (dialogInterface, i) ->
|
||||
{
|
||||
mEmulationFragment.stopEmulation();
|
||||
finish();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) ->
|
||||
NativeLibrary.UnPauseEmulation())
|
||||
.setOnCancelListener(dialogInterface ->
|
||||
NativeLibrary.UnPauseEmulation())
|
||||
.create()
|
||||
.show();
|
||||
View anchor = findViewById(R.id.menu_anchor);
|
||||
PopupMenu popupMenu = new PopupMenu(this, anchor);
|
||||
onCreateOptionsMenu(popupMenu.getMenu(), popupMenu.getMenuInflater());
|
||||
updateSavestateMenuOptions(popupMenu.getMenu());
|
||||
popupMenu.setOnMenuItemClickListener(this::onOptionsItemSelected);
|
||||
popupMenu.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -271,6 +268,10 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
public void onEmulationStarted() {
|
||||
Toast.makeText(this, getString(R.string.emulation_menu_help), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void enableFullscreenImmersive() {
|
||||
// It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
|
||||
mDecorView.setSystemUiVisibility(
|
||||
@ -285,7 +286,12 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.menu_emulation, menu);
|
||||
onCreateOptionsMenu(menu, getMenuInflater());
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_emulation, menu);
|
||||
|
||||
int layoutOptionMenuItem = R.id.menu_screen_layout_landscape;
|
||||
switch (EmulationMenuSettings.getLandscapeScreenLayout()) {
|
||||
@ -306,8 +312,6 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
menu.findItem(R.id.menu_emulation_show_fps).setChecked(EmulationMenuSettings.getShowFps());
|
||||
menu.findItem(R.id.menu_emulation_swap_screens).setChecked(EmulationMenuSettings.getSwapScreens());
|
||||
menu.findItem(R.id.menu_emulation_show_overlay).setChecked(EmulationMenuSettings.getShowOverlay());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DisplaySavestateWarning() {
|
||||
@ -333,12 +337,16 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
updateSavestateMenuOptions(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateSavestateMenuOptions(Menu menu) {
|
||||
final NativeLibrary.SavestateInfo[] savestates = NativeLibrary.GetSavestateInfo();
|
||||
if (savestates == null) {
|
||||
menu.findItem(R.id.menu_emulation_save_state).setVisible(false);
|
||||
menu.findItem(R.id.menu_emulation_load_state).setVisible(false);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
menu.findItem(R.id.menu_emulation_save_state).setVisible(true);
|
||||
menu.findItem(R.id.menu_emulation_load_state).setVisible(true);
|
||||
@ -367,7 +375,6 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
saveStateMenu.getItem(info.slot - 1).setTitle(text);
|
||||
loadStateMenu.getItem(info.slot - 1).setTitle(text).setEnabled(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WrongConstant")
|
||||
@ -480,6 +487,24 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
case MENU_ACTION_OPEN_CHEATS:
|
||||
CheatsActivity.launch(this);
|
||||
break;
|
||||
|
||||
case MENU_ACTION_CLOSE_GAME:
|
||||
NativeLibrary.PauseEmulation();
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.emulation_close_game)
|
||||
.setMessage(R.string.emulation_close_game_message)
|
||||
.setPositiveButton(android.R.string.yes, (dialogInterface, i) ->
|
||||
{
|
||||
mEmulationFragment.stopEmulation();
|
||||
finish();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) ->
|
||||
NativeLibrary.UnPauseEmulation())
|
||||
.setOnCancelListener(dialogInterface ->
|
||||
NativeLibrary.UnPauseEmulation())
|
||||
.create()
|
||||
.show();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -133,6 +133,8 @@ public class DiskShaderCacheProgress {
|
||||
case Complete:
|
||||
// Workaround for when dialog is dismissed when the app is in the background
|
||||
fragment.dismissAllowingStateLoss();
|
||||
|
||||
emulationActivity.runOnUiThread(emulationActivity::onEmulationStarted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -14,4 +14,10 @@
|
||||
android:layout_height="match_parent"
|
||||
android:transitionName="image_game_icon" />
|
||||
|
||||
</FrameLayout>
|
||||
<View
|
||||
android:id="@+id/menu_anchor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="top|end" />
|
||||
|
||||
</FrameLayout>
|
||||
|
@ -115,4 +115,9 @@
|
||||
app:showAsAction="never"
|
||||
android:title="@string/emulation_open_settings" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_emulation_close_game"
|
||||
app:showAsAction="never"
|
||||
android:title="@string/emulation_close_game" />
|
||||
|
||||
</menu>
|
||||
|
@ -164,6 +164,7 @@
|
||||
<string name="loader_error_invalid_format">Invalid ROM format</string>
|
||||
|
||||
<!-- Emulation Menu -->
|
||||
<string name="emulation_menu_help">Press Back to access the menu.</string>
|
||||
<string name="emulation_save_state">Save State</string>
|
||||
<string name="emulation_load_state">Load State</string>
|
||||
<string name="emulation_empty_state_slot">Slot %1$d</string>
|
||||
|
@ -17,7 +17,6 @@ CheatDialog::CheatDialog(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::CheatDialog>()) {
|
||||
// Setup gui control settings
|
||||
ui->setupUi(this);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
ui->tableCheats->setColumnWidth(0, 30);
|
||||
ui->tableCheats->setColumnWidth(2, 85);
|
||||
ui->tableCheats->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
|
||||
|
@ -20,8 +20,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, bool
|
||||
|
||||
PopulateSelectionList();
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
|
||||
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
|
||||
&ConfigureDialog::UpdateVisibleTabs);
|
||||
|
@ -41,8 +41,6 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
|
||||
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
setWindowTitle(tr("Properties"));
|
||||
// remove Help question mark button from the title bar
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
scene = new QGraphicsScene;
|
||||
ui->icon_view->setScene(scene);
|
||||
|
@ -50,9 +50,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Di
|
||||
setObjectName(QStringLiteral("MicroProfile"));
|
||||
setWindowTitle(tr("MicroProfile"));
|
||||
resize(1000, 600);
|
||||
// Remove the "?" button from the titlebar and enable the maximize button
|
||||
setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) |
|
||||
Qt::WindowMaximizeButtonHint);
|
||||
// Enable the maximize button
|
||||
setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
|
||||
|
||||
#if MICROPROFILE_ENABLED
|
||||
|
||||
|
@ -1019,6 +1019,11 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
"titles</a>."));
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorGbaTitle:
|
||||
QMessageBox::critical(this, tr("Unsupported ROM"),
|
||||
tr("GBA Virtual Console ROMs are not supported by Citra."));
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorVideoCore:
|
||||
QMessageBox::critical(
|
||||
this, tr("Video Core Error"),
|
||||
@ -2659,6 +2664,10 @@ int main(int argc, char* argv[]) {
|
||||
#ifdef __APPLE__
|
||||
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
|
||||
chdir(bin_path.c_str());
|
||||
#endif
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
// Disables the "?" button on all dialogs. Disabled by default on Qt6.
|
||||
QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
|
||||
#endif
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
|
||||
setWindowTitle(tr("Enter a hotkey"));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
key_sequence = new QKeySequenceEdit;
|
||||
|
||||
|
@ -20,6 +20,10 @@
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
#define EMU_DATA_DIR "Citra"
|
||||
#elif defined(__APPLE__)
|
||||
#define MACOS_EMU_DATA_DIR "Library" DIR_SEP "Application Support" DIR_SEP "Citra"
|
||||
// For compatibility with XDG paths.
|
||||
#define EMU_DATA_DIR "citra-emu"
|
||||
#elif ANDROID
|
||||
// On Android internal storage is mounted as "/sdcard"
|
||||
#define SDCARD_DIR "sdcard"
|
||||
|
@ -709,13 +709,26 @@ void SetUserPath(const std::string& path) {
|
||||
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
|
||||
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
|
||||
} else {
|
||||
std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
|
||||
std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
|
||||
std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
|
||||
std::string data_dir = GetUserDirectory("XDG_DATA_HOME") + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
std::string config_dir =
|
||||
GetUserDirectory("XDG_CONFIG_HOME") + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
std::string cache_dir =
|
||||
GetUserDirectory("XDG_CACHE_HOME") + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
|
||||
user_path = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
|
||||
g_paths.emplace(UserPath::ConfigDir, config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
|
||||
g_paths.emplace(UserPath::CacheDir, cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
|
||||
#if defined(__APPLE__)
|
||||
// If XDG directories don't already exist from a previous setup, use standard macOS
|
||||
// paths.
|
||||
if (!FileUtil::Exists(data_dir) && !FileUtil::Exists(config_dir) &&
|
||||
!FileUtil::Exists(cache_dir)) {
|
||||
data_dir = GetHomeDirectory() + DIR_SEP MACOS_EMU_DATA_DIR DIR_SEP;
|
||||
config_dir = data_dir + CONFIG_DIR DIR_SEP;
|
||||
cache_dir = data_dir + CACHE_DIR DIR_SEP;
|
||||
}
|
||||
#endif
|
||||
|
||||
user_path = data_dir;
|
||||
g_paths.emplace(UserPath::ConfigDir, config_dir);
|
||||
g_paths.emplace(UserPath::CacheDir, cache_dir);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -268,6 +268,8 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
return ResultStatus::ErrorLoader_ErrorEncrypted;
|
||||
case Loader::ResultStatus::ErrorInvalidFormat:
|
||||
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
||||
case Loader::ResultStatus::ErrorGbaTitle:
|
||||
return ResultStatus::ErrorLoader_ErrorGbaTitle;
|
||||
default:
|
||||
return ResultStatus::ErrorSystemMode;
|
||||
}
|
||||
@ -292,7 +294,6 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
telemetry_session->AddInitialInfo(*app_loader);
|
||||
std::shared_ptr<Kernel::Process> process;
|
||||
const Loader::ResultStatus load_result{app_loader->Load(process)};
|
||||
kernel->SetCurrentProcess(process);
|
||||
if (Loader::ResultStatus::Success != load_result) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
|
||||
System::Shutdown();
|
||||
@ -302,10 +303,13 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
return ResultStatus::ErrorLoader_ErrorEncrypted;
|
||||
case Loader::ResultStatus::ErrorInvalidFormat:
|
||||
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
||||
case Loader::ResultStatus::ErrorGbaTitle:
|
||||
return ResultStatus::ErrorLoader_ErrorGbaTitle;
|
||||
default:
|
||||
return ResultStatus::ErrorLoader;
|
||||
}
|
||||
}
|
||||
kernel->SetCurrentProcess(process);
|
||||
cheat_engine = std::make_unique<Cheats::CheatEngine>(*this);
|
||||
title_id = 0;
|
||||
if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
|
||||
@ -537,7 +541,8 @@ void System::Shutdown(bool is_deserializing) {
|
||||
perf_results.emulation_speed * 100.0);
|
||||
telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps);
|
||||
telemetry_session->AddField(performance, "Shutdown_Frametime", perf_results.frametime * 1000.0);
|
||||
telemetry_session->AddField(performance, "Mean_Frametime_MS", perf_stats->GetMeanFrametime());
|
||||
telemetry_session->AddField(performance, "Mean_Frametime_MS",
|
||||
perf_stats ? perf_stats->GetMeanFrametime() : 0);
|
||||
|
||||
// Shutdown emulation session
|
||||
VideoCore::Shutdown();
|
||||
|
@ -82,10 +82,12 @@ public:
|
||||
ErrorSystemMode, ///< Error determining the system mode
|
||||
ErrorLoader, ///< Error loading the specified application
|
||||
ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption
|
||||
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
|
||||
/// invalid format
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
|
||||
/// invalid format
|
||||
ErrorLoader_ErrorGbaTitle, ///< Error loading the specified application as it is GBA Virtual
|
||||
///< Console
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having
|
||||
/// generic drivers installed
|
||||
ErrorSavestate, ///< Error saving or loading
|
||||
|
@ -26,7 +26,8 @@
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
static std::string ReadTextInfo(FileUtil::IOFile& file, std::size_t offset, std::size_t max_size) {
|
||||
if (max_size > 0x400) { // Limit read string size to 0x400 bytes, just in case
|
||||
if (offset == 0 || max_size == 0 ||
|
||||
max_size > 0x400) { // Limit read string size to 0x400 bytes, just in case
|
||||
return "";
|
||||
}
|
||||
std::vector<char> char_data(max_size);
|
||||
|
@ -75,6 +75,7 @@ enum class ResultStatus {
|
||||
ErrorAlreadyLoaded,
|
||||
ErrorMemoryAllocationFailed,
|
||||
ErrorEncrypted,
|
||||
ErrorGbaTitle,
|
||||
};
|
||||
|
||||
constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||
|
@ -85,6 +85,11 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
u64_le program_id;
|
||||
if (ResultStatus::Success == ReadCode(code) &&
|
||||
ResultStatus::Success == ReadProgramId(program_id)) {
|
||||
if (IsGbaVirtualConsole(code)) {
|
||||
LOG_ERROR(Loader, "Encountered unsupported GBA Virtual Console code section.");
|
||||
return ResultStatus::ErrorGbaTitle;
|
||||
}
|
||||
|
||||
std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
(const char*)overlay_ncch->exheader_header.codeset_info.name, 8);
|
||||
|
||||
@ -177,6 +182,12 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
bool AppLoader_NCCH::IsGbaVirtualConsole(const std::vector<u8>& code) {
|
||||
const u32* gbaVcHeader = reinterpret_cast<const u32*>(code.data() + code.size() - 0x10);
|
||||
return code.size() >= 0x10 && gbaVcHeader[0] == MakeMagic('.', 'C', 'A', 'A') &&
|
||||
gbaVcHeader[1] == 1;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCCH::Load(std::shared_ptr<Kernel::Process>& process) {
|
||||
u64_le ncch_program_id;
|
||||
|
||||
|
@ -78,6 +78,9 @@ private:
|
||||
/// Reads the region lockout info in the SMDH and send it to CFG service
|
||||
void ParseRegionLockoutInfo();
|
||||
|
||||
/// Detects whether the NCCH contains GBA Virtual Console.
|
||||
bool IsGbaVirtualConsole(const std::vector<u8>& code);
|
||||
|
||||
FileSys::NCCHContainer base_ncch;
|
||||
FileSys::NCCHContainer update_ncch;
|
||||
FileSys::NCCHContainer* overlay_ncch;
|
||||
|
Reference in New Issue
Block a user