Merge branch 'master' of https://github.com/GPUCode/citra into vulkan-2

This commit is contained in:
GPUCode
2023-02-01 23:27:12 +02:00
19 changed files with 124 additions and 43 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -9,7 +9,6 @@
SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
setWindowTitle(tr("Enter a hotkey"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
key_sequence = new QKeySequenceEdit;

View File

@ -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"

View File

@ -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
}

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -75,6 +75,7 @@ enum class ResultStatus {
ErrorAlreadyLoaded,
ErrorMemoryAllocationFailed,
ErrorEncrypted,
ErrorGbaTitle,
};
constexpr u32 MakeMagic(char a, char b, char c, char d) {

View File

@ -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;

View File

@ -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;