mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd9dcbe441 | ||
|
|
2a1f29c641 | ||
|
|
38883e8df4 | ||
|
|
f971040912 | ||
|
|
9aac7e8426 | ||
|
|
96284205a1 | ||
|
|
4a1d9d31d0 | ||
|
|
12d6087f2a | ||
|
|
251962c415 | ||
|
|
1bdd7d2352 | ||
|
|
7b98259ea1 | ||
|
|
ee8166d1fe | ||
|
|
43e073a18d | ||
|
|
bc41666d53 | ||
|
|
5b85d6a758 | ||
|
|
1fdc000815 | ||
|
|
3a57bb46ab | ||
|
|
2cb75e60b3 | ||
|
|
600ac6ec4f | ||
|
|
ed0cd628f8 | ||
|
|
33a825c17f | ||
|
|
660a165533 | ||
|
|
bfe2d5abb2 | ||
|
|
d5b36da6b0 | ||
|
|
328cebd5fc | ||
|
|
579cb7bd27 | ||
|
|
aab889535f | ||
|
|
e0362f7879 | ||
|
|
009ae1fb02 | ||
|
|
d40289e977 | ||
|
|
c2fd4af163 | ||
|
|
5bdee3a611 | ||
|
|
cb026a6946 | ||
|
|
cb5124da4b | ||
|
|
7c88af9c73 | ||
|
|
465a31bbd5 | ||
|
|
6deb43bde2 | ||
|
|
8e7dcb83a8 | ||
|
|
51ead1e00f | ||
|
|
27bcb7c29a | ||
|
|
aaed4a4983 | ||
|
|
748f232976 | ||
|
|
a33612cf7d | ||
|
|
5c123f3183 | ||
|
|
d30a7fb991 | ||
|
|
d4f661c27c | ||
|
|
6d05d0220d | ||
|
|
7fab935c2d | ||
|
|
cd120c3cfd | ||
|
|
0180ec060b | ||
|
|
cf4412ecbe | ||
|
|
743b0b11d8 | ||
|
|
e8c2cfa843 | ||
|
|
764875ddbf | ||
|
|
92c7eaa383 | ||
|
|
4d2c1a82c9 | ||
|
|
eb50aaea35 |
15
.github/workflows/cron_publish_flatpak.yml
vendored
15
.github/workflows/cron_publish_flatpak.yml
vendored
@@ -4,6 +4,17 @@ on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Every day at 12am UTC.
|
||||
workflow_dispatch: # As well as manually.
|
||||
inputs:
|
||||
stableBuild:
|
||||
description: 'Build stable version'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
publish:
|
||||
description: 'Publish to Flathub'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -52,8 +63,8 @@ jobs:
|
||||
artifactPrefixName: "PCSX2-linux-Qt-x64-flatpak"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
publish: true
|
||||
publish: ${{ inputs.publish || true }}
|
||||
fetchTags: true
|
||||
stableBuild: false
|
||||
stableBuild: ${{ inputs.stableBuild || false }}
|
||||
secrets: inherit
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
with:
|
||||
title: "Qt: Update Base Translation"
|
||||
commit-message: "[ci skip] Qt: Update Base Translation."
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
mv ./game_controller_db.txt ${{github.workspace}}/bin/resources/game_controller_db.txt
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
with:
|
||||
title: "PAD: Update to latest controller database"
|
||||
commit-message: "[ci skip] PAD: Update to latest controller database."
|
||||
|
||||
2
.github/workflows/linux_build_flatpak.yml
vendored
2
.github/workflows/linux_build_flatpak.yml
vendored
@@ -153,7 +153,7 @@ jobs:
|
||||
mv "./${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak" "$GITHUB_WORKSPACE"/ci-artifacts/
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: ci-artifacts
|
||||
|
||||
6
.github/workflows/linux_build_qt.yml
vendored
6
.github/workflows/linux_build_qt.yml
vendored
@@ -92,7 +92,7 @@ jobs:
|
||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh', '.github/workflows/scripts/common/*.patch') }}
|
||||
@@ -174,7 +174,7 @@ jobs:
|
||||
|
||||
- name: Upload artifact
|
||||
if: inputs.buildAppImage == true
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: ci-artifacts
|
||||
|
||||
6
.github/workflows/macos_build.yml
vendored
6
.github/workflows/macos_build.yml
vendored
@@ -91,7 +91,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/*', '.github/workflows/scripts/common/*.patch') }}
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache ccache cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: "*.tar.xz"
|
||||
|
||||
16
.github/workflows/release_cut_new.yml
vendored
16
.github/workflows/release_cut_new.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
is_prelease:
|
||||
is_prerelease:
|
||||
description: 'Should be a pre-release?'
|
||||
required: true
|
||||
default: 'true'
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
with:
|
||||
body_path: ./release-notes.md
|
||||
draft: true
|
||||
prerelease: ${{ github.event_name != 'workflow_dispatch' || inputs.is_prelease == 'true' }}
|
||||
prerelease: ${{ github.event_name != 'workflow_dispatch' || inputs.is_prerelease == 'true' }}
|
||||
tag_name: ${{ steps.tag_version.outputs.new_tag }}
|
||||
|
||||
- name: Create a GitHub Release (Push)
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
cmakeflags: ""
|
||||
buildAppImage: true
|
||||
fetchTags: true
|
||||
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
|
||||
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prerelease == 'false' }}
|
||||
secrets: inherit
|
||||
|
||||
build_linux_flatpak:
|
||||
@@ -114,9 +114,9 @@ jobs:
|
||||
artifactPrefixName: "PCSX2-linux-Qt-x64-flatpak"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
publish: false
|
||||
publish: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prerelease == 'false' }} # prerelease builds are published by the cron job
|
||||
fetchTags: true
|
||||
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
|
||||
stableBuild: ${{ inputs.is_prerelease == 'false' }}
|
||||
secrets: inherit
|
||||
|
||||
# Windows
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
buildSystem: cmake
|
||||
cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
|
||||
fetchTags: true
|
||||
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
|
||||
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prerelease == 'false' }}
|
||||
secrets: inherit
|
||||
|
||||
# MacOS
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
jobName: "MacOS Build"
|
||||
artifactPrefixName: "PCSX2-macos-Qt"
|
||||
fetchTags: true
|
||||
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prelease == 'false' }}
|
||||
stableBuild: ${{ github.event_name == 'workflow_dispatch' && inputs.is_prerelease == 'false' }}
|
||||
sign_and_notarize: true
|
||||
secrets: inherit
|
||||
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
- name: Prepare Artifact Folder
|
||||
run: mkdir ./ci-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
name: Download all Artifacts
|
||||
with:
|
||||
path: ./ci-artifacts/
|
||||
|
||||
6
.github/workflows/windows_build_qt.yml
vendored
6
.github/workflows/windows_build_qt.yml
vendored
@@ -115,7 +115,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat', '.github/workflows/scripts/common/*.patch') }}
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
cmake --build build --config Release --target unittests
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: |
|
||||
@@ -186,7 +186,7 @@ jobs:
|
||||
}
|
||||
|
||||
- name: Upload artifact - with symbols
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
|
||||
path: |
|
||||
|
||||
15
3rdparty/rcheevos/CHANGELOG.md
vendored
15
3rdparty/rcheevos/CHANGELOG.md
vendored
@@ -1,3 +1,18 @@
|
||||
# v12.2.0
|
||||
* add rc_client_create_subset_list
|
||||
* add rc_client_begin_fetch_game_titles
|
||||
* greatly improve performance parsing long AddSource chains
|
||||
* don't send pings if not processing frames; allows server to suspend session while emulator is paused
|
||||
* modify validation logic to return most severe error instead of first error found
|
||||
* improve validation warning when 'PauseIf {recall}' attempts to use non-PauseIf Remember
|
||||
* fix rounding error when subtracting floats from integers
|
||||
* fix infinite loop processing 'Remember {recall}' with no modifiers
|
||||
* fix measured value jumping to 0 if all measured-generating alts are paused
|
||||
* fix buffer overflow converting long user names between rc_client_external versions
|
||||
* fix validation warning when adding differently sized values
|
||||
* fix validation warning when ResetIf only applies to hit count inside an AndNext chain
|
||||
* fix validation warning when only last node of modified memref chain differs
|
||||
|
||||
# v12.1.0
|
||||
* add rc_client_get_user_subset_summary
|
||||
* add validation warning for using MeasuredIf without Measured
|
||||
|
||||
2
3rdparty/rcheevos/include/rc_api_info.h
vendored
2
3rdparty/rcheevos/include/rc_api_info.h
vendored
@@ -211,6 +211,8 @@ typedef struct rc_api_game_title_entry_t {
|
||||
const char* title;
|
||||
/* The image name for the game badge */
|
||||
const char* image_name;
|
||||
/* The URL for the game badge image */
|
||||
const char* image_url;
|
||||
}
|
||||
rc_api_game_title_entry_t;
|
||||
|
||||
|
||||
54
3rdparty/rcheevos/include/rc_client.h
vendored
54
3rdparty/rcheevos/include/rc_client.h
vendored
@@ -364,6 +364,22 @@ RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client
|
||||
|
||||
RC_EXPORT void RC_CCONV rc_client_get_user_subset_summary(const rc_client_t* client, uint32_t subset_id, rc_client_user_game_summary_t* summary);
|
||||
|
||||
typedef struct rc_client_subset_list_t {
|
||||
const rc_client_subset_t** subsets;
|
||||
uint32_t num_subsets;
|
||||
} rc_client_subset_list_t;
|
||||
|
||||
/**
|
||||
* Creates a list of subsets for the currently loaded game.
|
||||
* Returns an allocated list that must be free'd by calling rc_client_destroy_subset_list.
|
||||
*/
|
||||
RC_EXPORT rc_client_subset_list_t* RC_CCONV rc_client_create_subset_list(rc_client_t* client);
|
||||
|
||||
/**
|
||||
* Destroys a list allocated by rc_client_create_subset_list_list.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_subset_list(rc_client_subset_list_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Fetch Game Hashes |
|
||||
\*****************************************************************************/
|
||||
@@ -398,6 +414,42 @@ RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_hash_library(
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_hash_library(rc_client_hash_library_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Fetch Game Titles |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_client_game_title_entry_t {
|
||||
uint32_t game_id;
|
||||
const char* title;
|
||||
char badge_name[16];
|
||||
const char* badge_url;
|
||||
} rc_client_game_title_entry_t;
|
||||
|
||||
typedef struct rc_client_game_title_list_t {
|
||||
rc_client_game_title_entry_t* entries;
|
||||
uint32_t num_entries;
|
||||
} rc_client_game_title_list_t;
|
||||
|
||||
/**
|
||||
* Callback that is fired when a game titles request completes. list may be null if the query failed.
|
||||
*/
|
||||
typedef void(RC_CCONV* rc_client_fetch_game_titles_callback_t)(int result, const char* error_message,
|
||||
rc_client_game_title_list_t* list, rc_client_t* client,
|
||||
void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Starts an asynchronous request for titles and badge names for the specified games.
|
||||
* The caller must provide an array of game IDs and the number of IDs in the array.
|
||||
*/
|
||||
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_game_titles(
|
||||
rc_client_t* client, const uint32_t* game_ids, uint32_t num_game_ids,
|
||||
rc_client_fetch_game_titles_callback_t callback, void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Destroys a previously-allocated result from the rc_client_begin_fetch_game_titles() callback.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_game_title_list(rc_client_game_title_list_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Achievements |
|
||||
\*****************************************************************************/
|
||||
@@ -503,7 +555,7 @@ enum {
|
||||
RC_EXPORT rc_client_achievement_list_t* RC_CCONV rc_client_create_achievement_list(rc_client_t* client, int category, int grouping);
|
||||
|
||||
/**
|
||||
* Destroys a list allocated by rc_client_get_achievement_list.
|
||||
* Destroys a list allocated by rc_client_create_achievement_list.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_achievement_list(rc_client_achievement_list_t* list);
|
||||
|
||||
|
||||
2
3rdparty/rcheevos/include/rc_runtime_types.h
vendored
2
3rdparty/rcheevos/include/rc_runtime_types.h
vendored
@@ -173,6 +173,8 @@ enum {
|
||||
RC_OPERATOR_SUB,
|
||||
|
||||
RC_OPERATOR_SUB_PARENT, /* internal use */
|
||||
RC_OPERATOR_ADD_ACCUMULATOR, /* internal use */
|
||||
RC_OPERATOR_SUB_ACCUMULATOR, /* internal use */
|
||||
RC_OPERATOR_INDIRECT_READ /* internal use */
|
||||
};
|
||||
|
||||
|
||||
7
3rdparty/rcheevos/src/rapi/rc_api_info.c
vendored
7
3rdparty/rcheevos/src/rapi/rc_api_info.c
vendored
@@ -448,7 +448,8 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re
|
||||
rc_json_field_t entry_fields[] = {
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("ImageIcon")
|
||||
RC_JSON_NEW_FIELD("ImageIcon"),
|
||||
RC_JSON_NEW_FIELD("ImageUrl")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@@ -482,6 +483,10 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re
|
||||
if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
rc_json_get_optional_string(&entry->image_url, &response->response, &entry_fields[3], "ImageUrl", "");
|
||||
if (!entry->image_url[0])
|
||||
entry->image_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_GAME, entry->image_name);
|
||||
|
||||
++entry;
|
||||
}
|
||||
}
|
||||
|
||||
325
3rdparty/rcheevos/src/rc_client.c
vendored
325
3rdparty/rcheevos/src/rc_client.c
vendored
@@ -1686,6 +1686,67 @@ static void rc_client_free_pending_media(rc_client_pending_media_t* pending_medi
|
||||
free(pending_media);
|
||||
}
|
||||
|
||||
static void rc_client_log_active_assets(rc_client_t* client)
|
||||
{
|
||||
uint32_t num_achievements;
|
||||
uint32_t num_active_achievements;
|
||||
uint32_t num_unsupported_achievements;
|
||||
uint32_t num_leaderboards;
|
||||
uint32_t num_unsupported_leaderboards;
|
||||
const rc_client_achievement_info_t* ach;
|
||||
const rc_client_achievement_info_t* ach_stop;
|
||||
const rc_client_leaderboard_info_t* lbd;
|
||||
const rc_client_leaderboard_info_t* lbd_stop;
|
||||
|
||||
const rc_client_subset_info_t* subset = client->game->subsets;
|
||||
for (; subset; subset = subset->next) {
|
||||
num_achievements = 0;
|
||||
num_active_achievements = 0;
|
||||
num_unsupported_achievements = 0;
|
||||
num_leaderboards = 0;
|
||||
num_unsupported_leaderboards = 0;
|
||||
|
||||
ach = subset->achievements;
|
||||
ach_stop = ach + subset->public_.num_achievements;
|
||||
for (; ach < ach_stop; ++ach) {
|
||||
if (ach->public_.category == RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE) {
|
||||
++num_achievements;
|
||||
if (ach->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE)
|
||||
++num_active_achievements;
|
||||
else if (ach->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_DISABLED)
|
||||
++num_unsupported_achievements;
|
||||
}
|
||||
}
|
||||
|
||||
lbd = subset->leaderboards;
|
||||
lbd_stop = lbd + subset->public_.num_leaderboards;
|
||||
for (; lbd < lbd_stop; ++lbd) {
|
||||
++num_leaderboards;
|
||||
if (lbd->public_.state == RC_CLIENT_LEADERBOARD_STATE_DISABLED)
|
||||
++num_unsupported_leaderboards;
|
||||
}
|
||||
|
||||
if (num_unsupported_achievements) {
|
||||
if (num_unsupported_leaderboards) {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active (%u unsupported), %u leaderboards (%u unsupported)",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_unsupported_achievements, num_leaderboards, num_unsupported_leaderboards);
|
||||
}
|
||||
else {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active (%u unsupported), %u leaderboards",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_unsupported_achievements, num_leaderboards);
|
||||
}
|
||||
}
|
||||
else if (num_unsupported_leaderboards) {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active, %u leaderboards (%u unsupported)",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_leaderboards, num_unsupported_leaderboards);
|
||||
}
|
||||
else {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active, %u leaderboards",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_leaderboards);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: address validation uses the read_memory callback to make sure the client
|
||||
* will return data for the requested address. As such, this function must
|
||||
* respect the `client->state.allow_background_memory_reads setting. Use
|
||||
@@ -1720,10 +1781,13 @@ static void rc_client_activate_game(rc_client_load_state_t* load_state, rc_api_s
|
||||
|
||||
/* make the loaded game active if another game is not aleady being loaded. */
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
if (client->state.load == load_state)
|
||||
if (client->state.load == load_state) {
|
||||
client->game = load_state->game;
|
||||
else
|
||||
client->state.frames_processed = client->state.frames_at_last_ping = 0;
|
||||
}
|
||||
else {
|
||||
load_state->progress = RC_CLIENT_LOAD_GAME_STATE_ABORTED;
|
||||
}
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (load_state->progress != RC_CLIENT_LOAD_GAME_STATE_ABORTED) {
|
||||
@@ -1807,6 +1871,9 @@ static void rc_client_activate_game(rc_client_load_state_t* load_state, rc_api_s
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Game %u loaded, hardcore %s%s", load_state->game->public_.id,
|
||||
client->state.hardcore ? "enabled" : "disabled",
|
||||
(client->state.spectator_mode != RC_CLIENT_SPECTATOR_MODE_OFF) ? ", spectating" : "");
|
||||
|
||||
if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO)
|
||||
rc_client_log_active_assets(client);
|
||||
}
|
||||
else {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Subset %u loaded", load_state->subset->public_.id);
|
||||
@@ -2352,6 +2419,7 @@ static int rc_client_attach_load_state(rc_client_t* client, rc_client_load_state
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
client->state.load = load_state;
|
||||
client->state.frames_processed = client->state.frames_at_last_ping = 0;
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
}
|
||||
else if (client->state.load != load_state) {
|
||||
@@ -3483,6 +3551,58 @@ const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc_client_subset_list_t* rc_client_create_subset_list(rc_client_t* client)
|
||||
{
|
||||
rc_client_subset_list_info_t* list;
|
||||
const rc_client_subset_info_t* subset;
|
||||
const rc_client_subset_t** subset_ptr;
|
||||
const uint32_t list_size = RC_ALIGN(sizeof(*list));
|
||||
uint32_t num_subsets = 0;
|
||||
|
||||
if (!client)
|
||||
return (rc_client_subset_list_t*)calloc(1, list_size);
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->create_subset_list)
|
||||
return (rc_client_subset_list_t*)client->state.external_client->create_subset_list();
|
||||
#endif
|
||||
|
||||
if (!client->game)
|
||||
return (rc_client_subset_list_t*)calloc(1, list_size);
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
|
||||
subset = client->game->subsets;
|
||||
for (; subset; subset = subset->next) {
|
||||
if (subset->active)
|
||||
num_subsets++;
|
||||
}
|
||||
|
||||
list = (rc_client_subset_list_info_t*)malloc(list_size + num_subsets * sizeof(rc_client_subset_t*));
|
||||
list->public_.subsets = subset_ptr = (const rc_client_subset_t**)((uint8_t*)list + list_size);
|
||||
|
||||
subset = client->game->subsets;
|
||||
for (; subset; subset = subset->next) {
|
||||
if (subset->active)
|
||||
*subset_ptr++ = &subset->public_;
|
||||
}
|
||||
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
list->destroy_func = NULL;
|
||||
list->public_.num_subsets = (uint32_t)(subset_ptr - list->public_.subsets);
|
||||
return &list->public_;
|
||||
}
|
||||
|
||||
void rc_client_destroy_subset_list(rc_client_subset_list_t* list)
|
||||
{
|
||||
rc_client_subset_list_info_t* info = (rc_client_subset_list_info_t*)list;
|
||||
if (info->destroy_func)
|
||||
info->destroy_func(info);
|
||||
else
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Fetch Game Hashes ===== */
|
||||
|
||||
typedef struct rc_client_fetch_hash_library_callback_data_t {
|
||||
@@ -3595,6 +3715,158 @@ void rc_client_destroy_hash_library(rc_client_hash_library_t* list)
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Fetch Game Titles ===== */
|
||||
|
||||
typedef struct rc_client_fetch_game_titles_callback_data_t {
|
||||
rc_client_t* client;
|
||||
rc_client_fetch_game_titles_callback_t callback;
|
||||
void* callback_userdata;
|
||||
rc_client_async_handle_t async_handle;
|
||||
} rc_client_fetch_game_titles_callback_data_t;
|
||||
|
||||
static void rc_client_fetch_game_titles_callback(const rc_api_server_response_t* server_response, void* callback_data)
|
||||
{
|
||||
rc_client_fetch_game_titles_callback_data_t* titles_callback_data =
|
||||
(rc_client_fetch_game_titles_callback_data_t*)callback_data;
|
||||
rc_client_t* client = titles_callback_data->client;
|
||||
rc_api_fetch_game_titles_response_t titles_response;
|
||||
const char* error_message;
|
||||
int result;
|
||||
|
||||
result = rc_client_end_async(client, &titles_callback_data->async_handle);
|
||||
if (result) {
|
||||
if (result != RC_CLIENT_ASYNC_DESTROYED)
|
||||
RC_CLIENT_LOG_VERBOSE(client, "Fetch game titles aborted");
|
||||
|
||||
free(titles_callback_data);
|
||||
return;
|
||||
}
|
||||
|
||||
result = rc_api_process_fetch_game_titles_server_response(&titles_response, server_response);
|
||||
error_message =
|
||||
rc_client_server_error_message(&result, server_response->http_status_code, &titles_response.response);
|
||||
if (error_message) {
|
||||
RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch game titles failed: %s", error_message);
|
||||
titles_callback_data->callback(result, error_message, NULL, client, titles_callback_data->callback_userdata);
|
||||
} else {
|
||||
rc_client_game_title_list_t* list;
|
||||
size_t strings_size = 0;
|
||||
const rc_api_game_title_entry_t* src;
|
||||
const rc_api_game_title_entry_t* stop;
|
||||
size_t list_size;
|
||||
|
||||
/* calculate string buffer size */
|
||||
for (src = titles_response.entries, stop = src + titles_response.num_entries; src < stop; ++src) {
|
||||
if (src->title)
|
||||
strings_size += strlen(src->title) + 1;
|
||||
if (src->image_url)
|
||||
strings_size += strlen(src->image_url) + 1;
|
||||
}
|
||||
|
||||
list_size = sizeof(*list) + sizeof(rc_client_game_title_entry_t) * titles_response.num_entries + strings_size;
|
||||
list = (rc_client_game_title_list_t*)malloc(list_size);
|
||||
if (!list) {
|
||||
titles_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client,
|
||||
titles_callback_data->callback_userdata);
|
||||
} else {
|
||||
rc_client_game_title_entry_t* entry = list->entries =
|
||||
(rc_client_game_title_entry_t*)((uint8_t*)list + sizeof(*list));
|
||||
char* strings = (char*)((uint8_t*)list + sizeof(*list) +
|
||||
sizeof(rc_client_game_title_entry_t) * titles_response.num_entries);
|
||||
|
||||
for (src = titles_response.entries, stop = src + titles_response.num_entries; src < stop; ++src, ++entry) {
|
||||
entry->game_id = src->id;
|
||||
|
||||
if (src->title) {
|
||||
const size_t len = strlen(src->title) + 1;
|
||||
entry->title = strings;
|
||||
memcpy(strings, src->title, len);
|
||||
strings += len;
|
||||
} else {
|
||||
entry->title = NULL;
|
||||
}
|
||||
|
||||
if (src->image_name)
|
||||
snprintf(entry->badge_name, sizeof(entry->badge_name), "%s", src->image_name);
|
||||
else
|
||||
entry->badge_name[0] = '\0';
|
||||
|
||||
if (src->image_url) {
|
||||
const size_t len = strlen(src->image_url) + 1;
|
||||
entry->badge_url = strings;
|
||||
memcpy(strings, src->image_url, len);
|
||||
strings += len;
|
||||
}
|
||||
else {
|
||||
entry->badge_url = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
list->num_entries = titles_response.num_entries;
|
||||
|
||||
titles_callback_data->callback(RC_OK, NULL, list, client, titles_callback_data->callback_userdata);
|
||||
}
|
||||
}
|
||||
|
||||
rc_api_destroy_fetch_game_titles_response(&titles_response);
|
||||
free(titles_callback_data);
|
||||
}
|
||||
|
||||
rc_client_async_handle_t* rc_client_begin_fetch_game_titles(rc_client_t* client, const uint32_t* game_ids,
|
||||
uint32_t num_game_ids,
|
||||
rc_client_fetch_game_titles_callback_t callback,
|
||||
void* callback_userdata)
|
||||
{
|
||||
rc_api_fetch_game_titles_request_t api_params;
|
||||
rc_client_fetch_game_titles_callback_data_t* callback_data;
|
||||
rc_client_async_handle_t* async_handle;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
const char* error_message;
|
||||
|
||||
if (!client) {
|
||||
callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!game_ids || num_game_ids == 0) {
|
||||
callback(RC_INVALID_STATE, "game_ids is required", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
api_params.game_ids = game_ids;
|
||||
api_params.num_game_ids = num_game_ids;
|
||||
result = rc_api_init_fetch_game_titles_request_hosted(&request, &api_params, &client->state.host);
|
||||
|
||||
if (result != RC_OK) {
|
||||
error_message = rc_error_str(result);
|
||||
callback(result, error_message, NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data = (rc_client_fetch_game_titles_callback_data_t*)calloc(1, sizeof(*callback_data));
|
||||
if (!callback_data) {
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data->client = client;
|
||||
callback_data->callback = callback;
|
||||
callback_data->callback_userdata = callback_userdata;
|
||||
|
||||
async_handle = &callback_data->async_handle;
|
||||
rc_client_begin_async(client, async_handle);
|
||||
client->callbacks.server_call(&request, rc_client_fetch_game_titles_callback, callback_data, client);
|
||||
rc_api_destroy_request(&request);
|
||||
|
||||
return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL;
|
||||
}
|
||||
|
||||
void rc_client_destroy_game_title_list(rc_client_game_title_list_t* list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Achievements ===== */
|
||||
|
||||
static void rc_client_update_achievement_display_information(rc_client_t* client, rc_client_achievement_info_t* achievement, time_t recent_unlock_time)
|
||||
@@ -5160,30 +5432,37 @@ static void rc_client_ping(rc_client_scheduled_callback_data_t* callback_data, r
|
||||
char buffer[256];
|
||||
int result;
|
||||
|
||||
if (!client->callbacks.rich_presence_override ||
|
||||
!client->callbacks.rich_presence_override(client, buffer, sizeof(buffer))) {
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
/* if no frames have been processed since the last ping, the emulator is idle. let the
|
||||
* server session expire. it will be resumed/restarted once frames start getting
|
||||
* processed again. */
|
||||
if (client->state.frames_processed != client->state.frames_at_last_ping) {
|
||||
client->state.frames_at_last_ping = client->state.frames_processed;
|
||||
|
||||
rc_runtime_get_richpresence(&client->game->runtime, buffer, sizeof(buffer),
|
||||
client->state.legacy_peek, client, NULL);
|
||||
if (!client->callbacks.rich_presence_override ||
|
||||
!client->callbacks.rich_presence_override(client, buffer, sizeof(buffer))) {
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
}
|
||||
rc_runtime_get_richpresence(&client->game->runtime, buffer, sizeof(buffer),
|
||||
client->state.legacy_peek, client, NULL);
|
||||
|
||||
memset(&api_params, 0, sizeof(api_params));
|
||||
api_params.username = client->user.username;
|
||||
api_params.api_token = client->user.token;
|
||||
api_params.game_id = client->game->public_.id;
|
||||
api_params.rich_presence = buffer;
|
||||
api_params.game_hash = client->game->public_.hash;
|
||||
api_params.hardcore = client->state.hardcore;
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
}
|
||||
|
||||
result = rc_api_init_ping_request_hosted(&request, &api_params, &client->state.host);
|
||||
if (result != RC_OK) {
|
||||
RC_CLIENT_LOG_WARN_FORMATTED(client, "Error generating ping request: %s", rc_error_str(result));
|
||||
}
|
||||
else {
|
||||
client->callbacks.server_call(&request, rc_client_ping_callback, client, client);
|
||||
memset(&api_params, 0, sizeof(api_params));
|
||||
api_params.username = client->user.username;
|
||||
api_params.api_token = client->user.token;
|
||||
api_params.game_id = client->game->public_.id;
|
||||
api_params.rich_presence = buffer;
|
||||
api_params.game_hash = client->game->public_.hash;
|
||||
api_params.hardcore = client->state.hardcore;
|
||||
|
||||
result = rc_api_init_ping_request_hosted(&request, &api_params, &client->state.host);
|
||||
if (result != RC_OK) {
|
||||
RC_CLIENT_LOG_WARN_FORMATTED(client, "Error generating ping request: %s", rc_error_str(result));
|
||||
}
|
||||
else {
|
||||
client->callbacks.server_call(&request, rc_client_ping_callback, client, client);
|
||||
}
|
||||
}
|
||||
|
||||
callback_data->when = now + 120 * 1000;
|
||||
@@ -5885,6 +6164,8 @@ void rc_client_do_frame(rc_client_t* client)
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
rc_client_raise_pending_events(client, client->game);
|
||||
|
||||
++client->state.frames_processed;
|
||||
}
|
||||
|
||||
/* we've processed a frame. if there's a pause delay in effect, process it */
|
||||
|
||||
36
3rdparty/rcheevos/src/rc_client_external.c
vendored
36
3rdparty/rcheevos/src/rc_client_external.c
vendored
@@ -9,13 +9,15 @@
|
||||
|
||||
/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */
|
||||
#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 64
|
||||
/* https://media.retroachievements.org/UserPic/TwentyCharUserNameXX.png is 69 with null terminator */
|
||||
#define RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE 80
|
||||
|
||||
typedef struct rc_client_external_conversions_t {
|
||||
rc_client_user_t user;
|
||||
rc_client_game_t game;
|
||||
rc_client_subset_t subsets[4];
|
||||
rc_client_achievement_t achievements[16];
|
||||
char user_avatar_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char user_avatar_url[RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE];
|
||||
char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
@@ -24,7 +26,7 @@ typedef struct rc_client_external_conversions_t {
|
||||
uint32_t next_achievement_index;
|
||||
} rc_client_external_conversions_t;
|
||||
|
||||
static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t image_type, const char* image_name)
|
||||
static const char* rc_client_external_build_avatar_url(char buffer[], size_t buffer_size, uint32_t image_type, const char* image_name)
|
||||
{
|
||||
rc_api_fetch_image_request_t image_request;
|
||||
rc_api_request_t request;
|
||||
@@ -38,7 +40,7 @@ static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t i
|
||||
if (result != RC_OK)
|
||||
return NULL;
|
||||
|
||||
strcpy_s(buffer, RC_CLIENT_IMAGE_URL_BUFFER_SIZE, request.url);
|
||||
snprintf(buffer, buffer_size, "%s", request.url);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -69,7 +71,9 @@ const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* cl
|
||||
RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);
|
||||
|
||||
converted->avatar_url = rc_client_external_build_avatar_url(
|
||||
client->state.external_client_conversions->user_avatar_url, RC_IMAGE_TYPE_USER, v1_user->username);
|
||||
client->state.external_client_conversions->user_avatar_url,
|
||||
sizeof(client->state.external_client_conversions->user_avatar_url),
|
||||
RC_IMAGE_TYPE_USER, v1_user->username);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -88,7 +92,9 @@ const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* cl
|
||||
RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(
|
||||
client->state.external_client_conversions->game_badge_url, RC_IMAGE_TYPE_GAME, v1_game->badge_name);
|
||||
client->state.external_client_conversions->game_badge_url,
|
||||
sizeof(client->state.external_client_conversions->game_badge_url),
|
||||
RC_IMAGE_TYPE_GAME, v1_game->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -123,7 +129,9 @@ const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t
|
||||
memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->subset_badge_url[0]),
|
||||
RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -161,8 +169,12 @@ const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const r
|
||||
memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
|
||||
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
|
||||
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -246,9 +258,13 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con
|
||||
*achievement = &new_list->achievements[num_achievements++];
|
||||
memcpy(*achievement, *src_achievement, sizeof(**src_achievement));
|
||||
|
||||
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
|
||||
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
|
||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
|
||||
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
|
||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
8
3rdparty/rcheevos/src/rc_client_external.h
vendored
8
3rdparty/rcheevos/src/rc_client_external.h
vendored
@@ -61,6 +61,11 @@ typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_lead
|
||||
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t)(rc_client_t* client,
|
||||
uint32_t leaderboard_id, uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata);
|
||||
|
||||
/* NOTE: rc_client_external_create_subset_list_func_t returns an internal wrapper structure which contains the public list
|
||||
* and a destructor function. */
|
||||
struct rc_client_subset_list_info_t;
|
||||
typedef struct rc_client_subset_list_info_t* (RC_CCONV* rc_client_external_create_subset_list_func_t)();
|
||||
|
||||
|
||||
typedef size_t (RC_CCONV *rc_client_external_progress_size_func_t)(void);
|
||||
typedef int (RC_CCONV *rc_client_external_serialize_progress_func_t)(uint8_t* buffer, size_t buffer_size);
|
||||
@@ -144,6 +149,9 @@ typedef struct rc_client_external_t
|
||||
rc_client_external_get_user_game_summary_func_t get_user_game_summary_v5;
|
||||
rc_client_external_get_user_subset_summary_func_t get_user_subset_summary;
|
||||
|
||||
/* VERSION 6 */
|
||||
rc_client_external_create_subset_list_func_t create_subset_list;
|
||||
|
||||
} rc_client_external_t;
|
||||
|
||||
#define RC_CLIENT_EXTERNAL_VERSION 5
|
||||
|
||||
10
3rdparty/rcheevos/src/rc_client_internal.h
vendored
10
3rdparty/rcheevos/src/rc_client_internal.h
vendored
@@ -222,6 +222,14 @@ typedef struct rc_client_subset_info_t {
|
||||
uint8_t pending_events;
|
||||
} rc_client_subset_info_t;
|
||||
|
||||
struct rc_client_subset_list_info_t;
|
||||
typedef void (RC_CCONV* rc_client_destroy_subset_list_func_t)(struct rc_client_subset_list_info_t* list);
|
||||
|
||||
typedef struct rc_client_subset_list_info_t {
|
||||
rc_client_subset_list_t public_;
|
||||
rc_client_destroy_subset_list_func_t destroy_func;
|
||||
} rc_client_subset_list_info_t;
|
||||
|
||||
/*****************************************************************************\
|
||||
| Game |
|
||||
\*****************************************************************************/
|
||||
@@ -316,6 +324,8 @@ typedef struct rc_client_state_t {
|
||||
rc_client_raintegration_t* raintegration;
|
||||
#endif
|
||||
|
||||
uint32_t frames_processed;
|
||||
uint32_t frames_at_last_ping;
|
||||
uint16_t unpaused_frame_decay;
|
||||
uint16_t required_unpaused_frames;
|
||||
|
||||
|
||||
19
3rdparty/rcheevos/src/rc_libretro.c
vendored
19
3rdparty/rcheevos/src/rc_libretro.c
vendored
@@ -649,6 +649,7 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
||||
uint32_t i, j;
|
||||
rc_libretro_core_memory_info_t info;
|
||||
size_t offset;
|
||||
int found_aligning_padding = 0;
|
||||
|
||||
for (i = 0; i < console_regions->num_regions; ++i) {
|
||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||
@@ -656,6 +657,16 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
||||
const uint32_t type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
|
||||
uint32_t base_address = 0;
|
||||
|
||||
if (console_region->type == RC_MEMORY_TYPE_UNUSED && console_region_size >= 0x10000 && !found_aligning_padding) {
|
||||
if (console_regions->region[console_regions->num_regions - 1].end_address > 0x01000000) {
|
||||
/* assume anything exposing more than 16MB of regions with at least one 64KB+ UNUSED region
|
||||
* is padding so things align with real addresses. this indicates the memory is disjoint
|
||||
* in the system, so we cannot expect it to be contiguous in the RETRO_SYSTEM_RAM.
|
||||
* stop processing regions now, and just fill the remaining memory map with null filler. */
|
||||
found_aligning_padding = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j <= i; ++j) {
|
||||
const rc_memory_region_t* console_region2 = &console_regions->region[j];
|
||||
if (rc_libretro_memory_console_region_to_ram_type(console_region2->type) == type) {
|
||||
@@ -665,7 +676,13 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
||||
}
|
||||
offset = console_region->start_address - base_address;
|
||||
|
||||
get_core_memory_info(type, &info);
|
||||
if (!found_aligning_padding) {
|
||||
get_core_memory_info(type, &info);
|
||||
}
|
||||
else {
|
||||
info.data = NULL;
|
||||
info.size = console_region_size;
|
||||
}
|
||||
|
||||
if (offset < info.size) {
|
||||
info.size -= offset;
|
||||
|
||||
2
3rdparty/rcheevos/src/rc_version.h
vendored
2
3rdparty/rcheevos/src/rc_version.h
vendored
@@ -8,7 +8,7 @@
|
||||
RC_BEGIN_C_DECLS
|
||||
|
||||
#define RCHEEVOS_VERSION_MAJOR 12
|
||||
#define RCHEEVOS_VERSION_MINOR 1
|
||||
#define RCHEEVOS_VERSION_MINOR 2
|
||||
#define RCHEEVOS_VERSION_PATCH 0
|
||||
|
||||
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
|
||||
|
||||
21
3rdparty/rcheevos/src/rcheevos/condition.c
vendored
21
3rdparty/rcheevos/src/rcheevos/condition.c
vendored
@@ -374,7 +374,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
||||
}
|
||||
|
||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
||||
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
|
||||
parse->indirect_parent.type = RC_OPERAND_NONE;
|
||||
break;
|
||||
|
||||
@@ -388,13 +388,13 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
/* type determined by parent */
|
||||
const uint8_t new_size = rc_operand_is_float(&parse->addsource_parent) ? RC_MEMSIZE_FLOAT : RC_MEMSIZE_32_BITS;
|
||||
|
||||
if (parse->addsource_oper == RC_OPERATOR_ADD && !rc_operand_is_memref(&parse->addsource_parent)) {
|
||||
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR && !rc_operand_is_memref(&parse->addsource_parent)) {
|
||||
/* if the previous element was a constant we have to turn it into a memref by adding zero */
|
||||
rc_modified_memref_t* memref;
|
||||
rc_operand_t zero;
|
||||
rc_operand_set_const(&zero, 0);
|
||||
memref = rc_alloc_modified_memref(parse,
|
||||
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD, &zero);
|
||||
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD_ACCUMULATOR, &zero);
|
||||
parse->addsource_parent.value.memref = (rc_memref_t*)memref;
|
||||
parse->addsource_parent.type = RC_OPERAND_ADDRESS;
|
||||
}
|
||||
@@ -414,19 +414,27 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
}
|
||||
|
||||
/* subtract the condition from the chain */
|
||||
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB : RC_OPERATOR_SUB_PARENT;
|
||||
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB_ACCUMULATOR : RC_OPERATOR_SUB_PARENT;
|
||||
rc_condition_convert_to_operand(condition, &cond_operand, parse);
|
||||
rc_operand_addsource(&cond_operand, parse, new_size);
|
||||
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
||||
|
||||
/* indicate the next value can be added to the chain */
|
||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
||||
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
|
||||
}
|
||||
|
||||
parse->indirect_parent.type = RC_OPERAND_NONE;
|
||||
break;
|
||||
|
||||
case RC_CONDITION_REMEMBER:
|
||||
if (condition->operand1.type == RC_OPERAND_RECALL &&
|
||||
condition->oper == RC_OPERATOR_NONE &&
|
||||
parse->addsource_parent.type == RC_OPERAND_NONE &&
|
||||
parse->indirect_parent.type == RC_OPERAND_NONE) {
|
||||
/* Remembering {recall} without any modifications is a no-op */
|
||||
break;
|
||||
}
|
||||
|
||||
rc_condition_convert_to_operand(condition, &condition->operand1, parse);
|
||||
|
||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||
@@ -465,6 +473,9 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
default:
|
||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||
/* type determined by leaf */
|
||||
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR)
|
||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
||||
|
||||
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
|
||||
condition->operand1.is_combining = 1;
|
||||
|
||||
|
||||
8
3rdparty/rcheevos/src/rcheevos/condset.c
vendored
8
3rdparty/rcheevos/src/rcheevos/condset.c
vendored
@@ -62,8 +62,10 @@ static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr, c
|
||||
do {
|
||||
rc_parse_condition_internal(&condition, &memaddr, &parse);
|
||||
|
||||
if (parse.offset < 0)
|
||||
if (parse.offset < 0) {
|
||||
rc_destroy_parse_state(&parse);
|
||||
return parse.offset;
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
@@ -106,7 +108,9 @@ static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr, c
|
||||
* logic in rc_find_next_classification */
|
||||
self->num_other_conditions += chain_length - 1;
|
||||
|
||||
return index;
|
||||
rc_destroy_parse_state(&parse);
|
||||
|
||||
return (int32_t)index;
|
||||
}
|
||||
|
||||
static int rc_find_next_classification(const char* memaddr) {
|
||||
|
||||
29
3rdparty/rcheevos/src/rcheevos/memref.c
vendored
29
3rdparty/rcheevos/src/rcheevos/memref.c
vendored
@@ -154,8 +154,14 @@ rc_modified_memref_t* rc_alloc_modified_memref(rc_parse_state_t* parse, uint8_t
|
||||
memcpy(&modified_memref->parent, parent, sizeof(modified_memref->parent));
|
||||
memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier));
|
||||
modified_memref->modifier_type = modifier_type;
|
||||
modified_memref->depth = 0;
|
||||
modified_memref->memref.address = rc_operand_is_memref(modifier) ? modifier->value.memref->address : modifier->value.num;
|
||||
|
||||
if (rc_operand_is_memref(parent) && parent->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
|
||||
const rc_modified_memref_t* parent_modified_memref = (rc_modified_memref_t*)parent->value.memref;
|
||||
modified_memref->depth = parent_modified_memref->depth + 1;
|
||||
}
|
||||
|
||||
return modified_memref;
|
||||
}
|
||||
|
||||
@@ -729,11 +735,34 @@ uint32_t rc_get_modified_memref_value(const rc_modified_memref_t* memref, rc_pee
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_SUB_PARENT:
|
||||
/* sub parent is "-parent + modifier" */
|
||||
rc_typed_value_negate(&value);
|
||||
rc_typed_value_add(&value, &modifier);
|
||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_SUB_ACCUMULATOR:
|
||||
rc_typed_value_negate(&modifier);
|
||||
/* fallthrough */ /* to case RC_OPERATOR_SUB_ACCUMULATOR */
|
||||
|
||||
case RC_OPERATOR_ADD_ACCUMULATOR:
|
||||
/* when modifying the accumulator, force the modifier to match the accumulator
|
||||
* type instead of promoting them both to the less restrictive type.
|
||||
*
|
||||
* 18 - 17.5 will result in an integer. should it be 0 or 1?
|
||||
*
|
||||
* default: float is less restrictive, convert both to float for combine,
|
||||
* then convert to the memref type.
|
||||
* (int)((float)18 - 17.5) -> (int)(0.5) -> 0
|
||||
*
|
||||
* accumulator is integer: force modifier to be integer before combining
|
||||
* (int)(18 - (int)17.5) -> (int)(18 - 17) -> 1
|
||||
*/
|
||||
rc_typed_value_convert(&modifier, value.type);
|
||||
rc_typed_value_add(&value, &modifier);
|
||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc_typed_value_combine(&value, &modifier, memref->modifier_type);
|
||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||
|
||||
5
3rdparty/rcheevos/src/rcheevos/operand.c
vendored
5
3rdparty/rcheevos/src/rcheevos/operand.c
vendored
@@ -334,8 +334,11 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) {
|
||||
const rc_modified_memref_t* left_memref = (const rc_modified_memref_t*)left->value.memref;
|
||||
const rc_modified_memref_t* right_memref = (const rc_modified_memref_t*)right->value.memref;
|
||||
return (left_memref->modifier_type == right_memref->modifier_type &&
|
||||
left_memref->depth == right_memref->depth &&
|
||||
rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier) &&
|
||||
rc_operands_are_equal(&left_memref->parent, &right_memref->parent) &&
|
||||
rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier));
|
||||
1 == 1
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
4
3rdparty/rcheevos/src/rcheevos/rc_internal.h
vendored
4
3rdparty/rcheevos/src/rcheevos/rc_internal.h
vendored
@@ -14,10 +14,11 @@ typedef struct rc_scratch_string {
|
||||
rc_scratch_string_t;
|
||||
|
||||
typedef struct rc_modified_memref_t {
|
||||
rc_memref_t memref; /* for compatibility with rc_operand_t.value.memref */
|
||||
rc_memref_t memref; /* For compatibility with rc_operand_t.value.memref */
|
||||
rc_operand_t parent; /* The parent memref this memref is derived from (type will always be a memref type) */
|
||||
rc_operand_t modifier; /* The modifier to apply to the parent. */
|
||||
uint8_t modifier_type; /* How to apply the modifier to the parent. (RC_OPERATOR_*) */
|
||||
uint16_t depth; /* The number of parents this memref has. */
|
||||
}
|
||||
rc_modified_memref_t;
|
||||
|
||||
@@ -382,7 +383,6 @@ rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self);
|
||||
void rc_reset_richpresence_triggers(rc_richpresence_t* self);
|
||||
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud);
|
||||
|
||||
int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address);
|
||||
int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id);
|
||||
|
||||
RC_END_C_DECLS
|
||||
|
||||
829
3rdparty/rcheevos/src/rcheevos/rc_validate.c
vendored
829
3rdparty/rcheevos/src/rcheevos/rc_validate.c
vendored
File diff suppressed because it is too large
Load Diff
10
3rdparty/rcheevos/src/rcheevos/trigger.c
vendored
10
3rdparty/rcheevos/src/rcheevos/trigger.c
vendored
@@ -235,8 +235,14 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, void* unus
|
||||
is_paused |= sub_paused;
|
||||
}
|
||||
|
||||
/* if paused, the measured value may not be captured, keep the old value */
|
||||
if (!is_paused) {
|
||||
if (is_paused) {
|
||||
/* if the trigger is fully paused, ignore any updates to the measured value */
|
||||
}
|
||||
else if (measured_value.type == RC_VALUE_TYPE_NONE) {
|
||||
/* if a measured value was not captured, keep the old value (it's possible to pause
|
||||
* an alt that is generating the measured value without fully pausing the trigger) */
|
||||
}
|
||||
else {
|
||||
rc_typed_value_convert(&measured_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
self->measured_value = measured_value.value.u32;
|
||||
}
|
||||
|
||||
36
3rdparty/rcheevos/src/rcheevos/value.c
vendored
36
3rdparty/rcheevos/src/rcheevos/value.c
vendored
@@ -67,6 +67,7 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
next_clause = &self->conditions;
|
||||
do {
|
||||
/* count the number of joiners and add one to determine the number of clauses. */
|
||||
buffer[0] = 'A'; /* reset to AddSource */
|
||||
done = 0;
|
||||
num_measured_conditions = 1;
|
||||
buffer_ptr = *memaddr;
|
||||
@@ -97,8 +98,8 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
/* if last condition is SubSource, we'll need to add a dummy condition for the Measured */
|
||||
if (buffer[0] == 'B')
|
||||
/* if last condition is not AddSource, we'll need to add a dummy condition for the Measured */
|
||||
if (buffer[0] != 'A')
|
||||
++num_measured_conditions;
|
||||
|
||||
condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t,
|
||||
@@ -121,10 +122,18 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
for (;; ++(*memaddr)) {
|
||||
switch (**memaddr) {
|
||||
case '_': /* add next */
|
||||
*ptr = '\0';
|
||||
break;
|
||||
|
||||
case '$': /* maximum of */
|
||||
case '\0': /* end of string */
|
||||
case ':': /* end of leaderboard clause */
|
||||
case ')': /* end of rich presence macro */
|
||||
/* the last condition needs to be Measured - AddSource can be changed here,
|
||||
* SubSource will be handled later */
|
||||
if (buffer[0] == 'A')
|
||||
buffer[0] = 'M';
|
||||
|
||||
*ptr = '\0';
|
||||
break;
|
||||
|
||||
@@ -176,33 +185,34 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
return;
|
||||
}
|
||||
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
|
||||
*next = cond;
|
||||
next = &cond->next;
|
||||
|
||||
if (**memaddr != '_') /* add next */
|
||||
break;
|
||||
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
++cond;
|
||||
}
|
||||
|
||||
/* end of clause */
|
||||
if (cond->type == RC_CONDITION_SUB_SOURCE) {
|
||||
/* cannot change SubSource to Measured. add a dummy condition */
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
if (parse->buffer)
|
||||
/* -- end of clause -- */
|
||||
|
||||
/* clause must end in a Measured. if it doesn't, append one */
|
||||
if (cond->type != RC_CONDITION_MEASURED) {
|
||||
if (!parse->buffer)
|
||||
cond = &local_cond;
|
||||
else
|
||||
++cond;
|
||||
|
||||
buffer_ptr = "A:0";
|
||||
buffer_ptr = "M:0";
|
||||
rc_parse_condition_internal(cond, &buffer_ptr, parse);
|
||||
*next = cond;
|
||||
next = &cond->next;
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
}
|
||||
|
||||
/* convert final AddSource condition to Measured */
|
||||
cond->type = RC_CONDITION_MEASURED;
|
||||
cond->next = NULL;
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
*next = NULL;
|
||||
|
||||
/* finalize clause */
|
||||
*next_clause = condset;
|
||||
|
||||
22
3rdparty/rcheevos/src/rhash/hash.c
vendored
22
3rdparty/rcheevos/src/rhash/hash.c
vendored
@@ -1300,7 +1300,18 @@ static void rc_hash_initialize_iterator_from_path(rc_hash_iterator_t* iterator,
|
||||
bsearch(&search, handlers, num_handlers, sizeof(*handler), rc_hash_iterator_find_handler);
|
||||
if (handler) {
|
||||
handler->handler(iterator, handler->data);
|
||||
} else {
|
||||
|
||||
if (iterator->callbacks.verbose_message) {
|
||||
int count = 0;
|
||||
while (iterator->consoles[count])
|
||||
++count;
|
||||
|
||||
rc_hash_iterator_verbose_formatted(iterator, "Found %d potential consoles for %s file extension", count, ext);
|
||||
}
|
||||
}
|
||||
else {
|
||||
rc_hash_iterator_error_formatted(iterator, "No console mapping specified for %s file extension - trying full file hash", ext);
|
||||
|
||||
/* if we didn't match the extension, default to something that does a whole file hash */
|
||||
if (!iterator->consoles[0])
|
||||
iterator->consoles[0] = RC_CONSOLE_GAMEBOY;
|
||||
@@ -1332,15 +1343,6 @@ int rc_hash_iterate(char hash[33], rc_hash_iterator_t* iterator) {
|
||||
|
||||
if (iterator->index == -1) {
|
||||
rc_hash_initialize_iterator_from_path(iterator, iterator->path);
|
||||
|
||||
if (iterator->callbacks.verbose_message) {
|
||||
int count = 0;
|
||||
while (iterator->consoles[count])
|
||||
++count;
|
||||
|
||||
rc_hash_iterator_verbose_formatted(iterator, "Found %d potential consoles for %s file extension", count, rc_path_get_extension(iterator->path));
|
||||
}
|
||||
|
||||
iterator->index = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13722,6 +13722,8 @@ SLED-51676:
|
||||
region: "PAL"
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLED-51807:
|
||||
name: "Summer Heat Beach Volleyball"
|
||||
region: "PAL-Unk"
|
||||
@@ -13996,6 +13998,7 @@ SLED-53109:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLED-53137:
|
||||
name: "Stolen [Demo]"
|
||||
@@ -16671,6 +16674,8 @@ SLES-50958:
|
||||
region: "PAL-M4"
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLES-50963:
|
||||
name: "Riding Spirits"
|
||||
region: "PAL-M5"
|
||||
@@ -17825,6 +17830,8 @@ SLES-51393:
|
||||
SLES-51397:
|
||||
name: "IndyCar Series"
|
||||
region: "PAL-M5"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLES-51398:
|
||||
name: "World Championship Snooker 2003"
|
||||
region: "PAL-E"
|
||||
@@ -19948,6 +19955,8 @@ SLES-52298:
|
||||
name: "IndyCar Series 2005"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLES-52308:
|
||||
name: "Karaoke Stage"
|
||||
region: "PAL-M5"
|
||||
@@ -20118,6 +20127,9 @@ SLES-52378:
|
||||
name: "Euro Rally Champion"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
textureInsideRT: 1 # Fixes broken fog rendering.
|
||||
SLES-52379:
|
||||
name: "Shrek 2"
|
||||
region: "PAL-E"
|
||||
@@ -20882,10 +20894,8 @@ SLES-52636:
|
||||
gameFixes:
|
||||
- FullVU0SyncHack # Fixes in-game timer.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 3 # Fixes missing lighting and car reflections.
|
||||
halfPixelOffset: 1 # Fixes 4 split lines in stage intros.
|
||||
autoFlush: 1 # Fixes incorrect colors.
|
||||
alignSprite: 1 # Fixes vertical lines such as in FMVs.
|
||||
minimumBlendingLevel: 3 # Fixes missing lighting and car reflections.
|
||||
halfPixelOffset: 4 # Fixes lines in game and FMVs.
|
||||
SLES-52637:
|
||||
name: "TOCA Race Driver 2"
|
||||
region: "PAL-M5"
|
||||
@@ -22269,6 +22279,7 @@ SLES-53044:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLES-53045:
|
||||
name: "Street Racing Syndicate"
|
||||
@@ -22592,6 +22603,7 @@ SLES-53151:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLES-53152:
|
||||
name: "Mashed Fully Loaded"
|
||||
@@ -25050,6 +25062,8 @@ SLES-53957:
|
||||
SLES-53958:
|
||||
name: "Noble Racing"
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLES-53959:
|
||||
name: "Pac-Man World 3"
|
||||
region: "PAL-M5"
|
||||
@@ -30127,6 +30141,11 @@ SLES-55544:
|
||||
region: "PAL-M5"
|
||||
roundModes:
|
||||
vu1RoundMode: 0 # Fixes VU size spam and potential graphical issues with GH3 engine.
|
||||
gsHWFixes:
|
||||
cpuCLUTRender: 1 # Fixes broken rainbow rendering.
|
||||
halfPixelOffset: 4 # Mostly aligns post-processing.
|
||||
nativeScaling: 1 # Fixes post-processing smoothness and position.
|
||||
autoFlush: 1 # Fixes edge garbage and shadow definition.
|
||||
SLES-55545:
|
||||
name: "WWE SmackDown! vs. Raw 2010"
|
||||
region: "PAL-M5"
|
||||
@@ -32394,6 +32413,7 @@ SLKA-25283:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLKA-25284:
|
||||
name: "사쿠라 대전 3 ~파리는 불타고 있는가~"
|
||||
@@ -48603,6 +48623,7 @@ SLPM-66277:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLPM-66278:
|
||||
name: "新・豪血寺一族 -煩悩解放-"
|
||||
@@ -66745,6 +66766,8 @@ SLUS-20597:
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLUS-20598:
|
||||
name: "Everblue 2"
|
||||
region: "NTSC-U"
|
||||
@@ -66968,6 +66991,8 @@ SLUS-20641:
|
||||
name: "IndyCar Series"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLUS-20642:
|
||||
name: "Auto Modellista"
|
||||
region: "NTSC-U"
|
||||
@@ -68225,6 +68250,7 @@ SLUS-20872:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLUS-20873:
|
||||
name: "Silent Hill 4 - The Room"
|
||||
@@ -74172,8 +74198,10 @@ SLUS-21866:
|
||||
roundModes:
|
||||
vu1RoundMode: 0 # Fixes VU size spam and potential graphical issues with GH3 engine.
|
||||
gsHWFixes:
|
||||
cpuCLUTRender: 1 # Fixes broken rainbow rendering.
|
||||
halfPixelOffset: 4 # Mostly aligns post-processing.
|
||||
nativeScaling: 1 # Fixes post-processing smoothness and position.
|
||||
autoFlush: 1 # Fixes edge garbage and shadow definition.
|
||||
SLUS-21867:
|
||||
name: "Guitar Hero - Van Halen"
|
||||
region: "NTSC-U"
|
||||
@@ -74971,6 +74999,8 @@ SLUS-29049:
|
||||
region: "NTSC-U"
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLUS-29050:
|
||||
name: "EverQuest Online Adventures [Regular Demo]"
|
||||
region: "NTSC-U"
|
||||
@@ -75330,6 +75360,7 @@ SLUS-29147:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLUS-29148:
|
||||
name: "The Incredible Hulk - Ultimate Destruction [Demo]"
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001260000000000000,8BitDo Ultimate 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001c30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,paddle1:b2,paddle2:b5,platform:Windows,
|
||||
03000000c82d00001c30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
@@ -245,7 +245,8 @@
|
||||
03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows,
|
||||
030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000c9110000f055000000000000,GC100XF,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
03000000c21100000791000000000000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000c9110000f055000000000000,Be1 GC100XF Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
@@ -850,6 +851,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
|
||||
# Mac OS X
|
||||
030000008f0e00000300000009010000,2 In 1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
03000000c82d00001930000000000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00001930000000020000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00001930000001000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00000031000001000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
@@ -895,7 +897,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
@@ -985,6 +987,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000019c2000000020000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
@@ -993,7 +996,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000019c2000000020000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
|
||||
@@ -1395,6 +1397,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
||||
03000000d80400004bea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
03000000d80400004aea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
030000008a2e0000d910000011010000,icedragon.io STAC2 Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
030000008a2e0000e910000011010000,icedragon.io STAC2 Dance Pad,a:b8,b:b9,x:b10,y:b11,back:b12,platform:Linux,
|
||||
030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,
|
||||
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
|
||||
@@ -1591,6 +1597,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux,
|
||||
03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000120c0000160e000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
|
||||
030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
|
||||
@@ -1701,7 +1708,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,
|
||||
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
|
||||
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
|
||||
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000632500002605000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
@@ -1858,4 +1864,5 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000120c0000182e000011010000,Zeroplus PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000790000002201000011010000,ZhiXu GuliKit D,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "RedtapeWindows.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <io.h>
|
||||
#include <malloc.h>
|
||||
#include <pathcch.h>
|
||||
|
||||
@@ -1148,7 +1148,7 @@ bool BMPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (width > 65536 || height > 65536)
|
||||
if (width >= 65536 || height >= 65536)
|
||||
{
|
||||
Console.Error("BMP dimensions too large: %ux%u", width, height);
|
||||
return false;
|
||||
|
||||
@@ -133,7 +133,7 @@ void DisplaySurface::handleCloseEvent(QCloseEvent* event)
|
||||
// In the latter case, it's going to destroy us, so don't let Qt do it first.
|
||||
// Treat a close event while fullscreen as an exit, that way ALT+F4 closes PCSX2,
|
||||
// rather than just the game.
|
||||
if (QtHost::IsVMValid() && !isActuallyFullscreen())
|
||||
if (QtHost::IsVMValid() && !isFullScreen())
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window, "requestShutdown", Q_ARG(bool, true),
|
||||
Q_ARG(bool, true), Q_ARG(bool, false));
|
||||
@@ -147,10 +147,44 @@ void DisplaySurface::handleCloseEvent(QCloseEvent* event)
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
bool DisplaySurface::isActuallyFullscreen() const
|
||||
bool DisplaySurface::isFullScreen() const
|
||||
{
|
||||
// DisplaySurface is always in a container, so we need to check parent window
|
||||
return parent()->windowState() & Qt::WindowFullScreen;
|
||||
// DisplaySurface may be in a container
|
||||
return (parent() ? parent()->windowState() : windowState()) & Qt::WindowFullScreen;
|
||||
}
|
||||
|
||||
void DisplaySurface::setFocus()
|
||||
{
|
||||
if (m_container)
|
||||
m_container->setFocus();
|
||||
else
|
||||
requestActivate();
|
||||
}
|
||||
|
||||
QByteArray DisplaySurface::saveGeometry() const
|
||||
{
|
||||
if (m_container)
|
||||
return m_container->saveGeometry();
|
||||
else
|
||||
{
|
||||
// QWindow lacks saveGeometry, so create a dummy widget and copy geometry across.
|
||||
QWidget dummy = QWidget();
|
||||
dummy.setGeometry(geometry());
|
||||
return dummy.saveGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaySurface::restoreGeometry(const QByteArray& geometry)
|
||||
{
|
||||
if (m_container)
|
||||
m_container->restoreGeometry(geometry);
|
||||
else
|
||||
{
|
||||
// QWindow lacks restoreGeometry, so create a dummy widget and copy geometry across.
|
||||
QWidget dummy = QWidget();
|
||||
dummy.restoreGeometry(geometry);
|
||||
setGeometry(dummy.geometry());
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaySurface::updateCenterPos()
|
||||
@@ -393,10 +427,18 @@ bool DisplaySurface::event(QEvent* event)
|
||||
return event->isAccepted();
|
||||
|
||||
case QEvent::Move:
|
||||
{
|
||||
updateCenterPos();
|
||||
return true;
|
||||
}
|
||||
|
||||
// These events only work on the top level control.
|
||||
// Which is this container when render to seperate or fullscreen is active (Windows).
|
||||
case QEvent::Close:
|
||||
handleCloseEvent(static_cast<QCloseEvent*>(event));
|
||||
return true;
|
||||
case QEvent::WindowStateChange:
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit windowRestoredEvent();
|
||||
return false;
|
||||
|
||||
default:
|
||||
return QWindow::event(event);
|
||||
@@ -418,7 +460,7 @@ bool DisplaySurface::eventFilter(QObject* object, QEvent* event)
|
||||
return true;
|
||||
|
||||
// These events only work on the top level control.
|
||||
// Which is this container when render to seperate or fullscreen is active.
|
||||
// Which is this container when render to seperate or fullscreen is active (Non-Windows).
|
||||
case QEvent::Close:
|
||||
handleCloseEvent(static_cast<QCloseEvent*>(event));
|
||||
return true;
|
||||
@@ -427,8 +469,8 @@ bool DisplaySurface::eventFilter(QObject* object, QEvent* event)
|
||||
emit windowRestoredEvent();
|
||||
return false;
|
||||
|
||||
case QEvent::ChildRemoved:
|
||||
if (static_cast<QChildEvent*>(event)->child() == m_container)
|
||||
case QEvent::ChildWindowRemoved:
|
||||
if (static_cast<QChildWindowEvent*>(event)->child() == this)
|
||||
{
|
||||
object->removeEventFilter(this);
|
||||
m_container = nullptr;
|
||||
|
||||
@@ -30,6 +30,12 @@ public:
|
||||
void updateRelativeMode(bool enabled);
|
||||
void updateCursor(bool hidden);
|
||||
|
||||
bool isFullScreen() const;
|
||||
void setFocus();
|
||||
|
||||
QByteArray saveGeometry() const;
|
||||
void restoreGeometry(const QByteArray& geometry);
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowResizedEvent(int width, int height, float scale);
|
||||
void windowRestoredEvent();
|
||||
@@ -47,7 +53,6 @@ private Q_SLOTS:
|
||||
void onResizeDebounceTimer();
|
||||
|
||||
private:
|
||||
bool isActuallyFullscreen() const;
|
||||
void updateCenterPos();
|
||||
|
||||
QPoint m_relative_mouse_start_pos{};
|
||||
|
||||
@@ -220,6 +220,8 @@ void GameListWidget::initialize()
|
||||
m_sort_model->setSourceModel(m_model);
|
||||
|
||||
m_ui.setupUi(this);
|
||||
m_ui.stack->installEventFilter(this);
|
||||
m_ui.stack->setAutoFillBackground(false);
|
||||
|
||||
for (u32 type = 0; type < static_cast<u32>(GameList::EntryType::Count); type++)
|
||||
{
|
||||
@@ -353,6 +355,7 @@ void GameListWidget::setCustomBackground()
|
||||
m_background_movie = nullptr;
|
||||
}
|
||||
|
||||
// Get the path to the custom background
|
||||
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
||||
if (!Path::IsAbsolute(path))
|
||||
path = Path::Combine(EmuFolders::DataRoot, path);
|
||||
@@ -360,27 +363,26 @@ void GameListWidget::setCustomBackground()
|
||||
// Only try to create background if path are valid
|
||||
if (!path.empty() && FileSystem::FileExists(path.c_str()))
|
||||
{
|
||||
QMovie* new_movie;
|
||||
QString img_path = QString::fromStdString(path);
|
||||
if (img_path.endsWith(".png", Qt::CaseInsensitive))
|
||||
// Use apng plugin
|
||||
new_movie = new QMovie(img_path, "apng", this);
|
||||
else
|
||||
new_movie = new QMovie(img_path, QByteArray(), this);
|
||||
|
||||
if (new_movie->isValid())
|
||||
m_background_movie = new_movie;
|
||||
else
|
||||
const QByteArray format = (img_path.endsWith(".png", Qt::CaseInsensitive)) ? QByteArray("apng") : QByteArray();
|
||||
m_background_movie = new QMovie(img_path, format, this);
|
||||
if (!m_background_movie->isValid())
|
||||
{
|
||||
Console.Warning("Failed to load background movie from: %s", path.c_str());
|
||||
delete new_movie;
|
||||
delete m_background_movie;
|
||||
m_background_movie = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no valid background then reset fallback to default UI state
|
||||
if (!m_background_movie)
|
||||
{
|
||||
m_ui.stack->setPalette(QApplication::palette());
|
||||
m_background_pixmap = QPixmap();
|
||||
m_ui.stack->setAutoFillBackground(true);
|
||||
m_table_view->viewport()->setAutoFillBackground(true);
|
||||
m_list_view->viewport()->setAutoFillBackground(true);
|
||||
|
||||
m_ui.stack->update();
|
||||
m_table_view->setAlternatingRowColors(true);
|
||||
return;
|
||||
}
|
||||
@@ -390,7 +392,7 @@ void GameListWidget::setCustomBackground()
|
||||
const std::string ar_value = Host::GetBaseStringSettingValue("UI", "GameListBackgroundMode", InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[static_cast<u8>(QtUtils::ScalingMode::Fit)]);
|
||||
for (u8 i = 0; i < static_cast<u8>(QtUtils::ScalingMode::MaxCount); i++)
|
||||
{
|
||||
if (!(InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[i] == nullptr))
|
||||
if (InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[i] != nullptr)
|
||||
{
|
||||
if (ar_value == InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[i])
|
||||
{
|
||||
@@ -405,8 +407,13 @@ void GameListWidget::setCustomBackground()
|
||||
|
||||
// Selected Custom background is valid, connect the signals and start animation in gamelist
|
||||
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames, Qt::UniqueConnection);
|
||||
m_ui.stack->setAutoFillBackground(false);
|
||||
|
||||
m_table_view->viewport()->setAutoFillBackground(false);
|
||||
m_list_view->viewport()->setAutoFillBackground(false);
|
||||
updateCustomBackgroundState(true);
|
||||
m_table_view->setAlternatingRowColors(false);
|
||||
processBackgroundFrames();
|
||||
}
|
||||
|
||||
void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
||||
@@ -422,7 +429,7 @@ void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
||||
|
||||
void GameListWidget::processBackgroundFrames()
|
||||
{
|
||||
if (m_background_movie && m_background_movie->isValid())
|
||||
if (m_background_movie && m_background_movie->isValid() && isVisible())
|
||||
{
|
||||
const int widget_width = m_ui.stack->width();
|
||||
const int widget_height = m_ui.stack->height();
|
||||
@@ -435,9 +442,8 @@ void GameListWidget::processBackgroundFrames()
|
||||
|
||||
QtUtils::resizeAndScalePixmap(&pm, widget_width, widget_height, dpr, m_background_scaling, m_background_opacity);
|
||||
|
||||
QPalette bg_palette(m_ui.stack->palette());
|
||||
bg_palette.setBrush(QPalette::Base, pm);
|
||||
m_ui.stack->setPalette(bg_palette);
|
||||
m_background_pixmap = std::move(pm);
|
||||
m_ui.stack->update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,6 +731,7 @@ bool GameListWidget::event(QEvent* event)
|
||||
if (event->type() == QEvent::DevicePixelRatioChange)
|
||||
{
|
||||
m_model->setDevicePixelRatio(devicePixelRatioF());
|
||||
processBackgroundFrames();
|
||||
QWidget::event(event);
|
||||
return true;
|
||||
}
|
||||
@@ -732,6 +739,25 @@ bool GameListWidget::event(QEvent* event)
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
bool GameListWidget::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (watched == m_ui.stack && event->type() == QEvent::Paint)
|
||||
{
|
||||
if (!m_background_pixmap.isNull())
|
||||
{
|
||||
QPainter painter(m_ui.stack);
|
||||
const auto* paint_event = static_cast<QPaintEvent*>(event);
|
||||
painter.save();
|
||||
painter.setClipRect(paint_event->rect());
|
||||
painter.drawTiledPixmap(m_ui.stack->rect(), m_background_pixmap);
|
||||
painter.restore();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void GameListWidget::resizeTableViewColumnsToFit()
|
||||
{
|
||||
QtUtils::ResizeColumnsForTableView(m_table_view, {
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include <QtGui/QMovie>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtWidgets/QListView>
|
||||
#include <QtWidgets/QStackedWidget>
|
||||
#include <QtWidgets/QTableView>
|
||||
|
||||
Q_DECLARE_METATYPE(const GameList::Entry*);
|
||||
@@ -101,6 +103,7 @@ protected:
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
bool event(QEvent* event) override;
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
private:
|
||||
void loadTableHeaderState();
|
||||
@@ -123,6 +126,7 @@ private:
|
||||
GameListRefreshThread* m_refresh_thread = nullptr;
|
||||
|
||||
QMovie* m_background_movie = nullptr;
|
||||
QPixmap m_background_pixmap;
|
||||
QtUtils::ScalingMode m_background_scaling = QtUtils::ScalingMode::Fit;
|
||||
float m_background_opacity = 100.0f;
|
||||
};
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
#include "pcsx2/Recording/InputRecordingControls.h"
|
||||
#include "pcsx2/SaveState.h"
|
||||
#include "pcsx2/SIO/Sio.h"
|
||||
#include "pcsx2/GS/GSExtra.h"
|
||||
|
||||
@@ -99,6 +100,13 @@ static quint32 s_current_running_crc;
|
||||
static bool s_record_on_start = false;
|
||||
static QString s_path_to_recording_for_record_on_start;
|
||||
|
||||
// DX cannot fullscreen when the display surface is in a container.
|
||||
// QWindow, however, seems to lack CSD under wayland, so needs the container.
|
||||
// MAC is unknown
|
||||
#ifdef _WIN32
|
||||
#define DISPLAY_SURFACE_WINDOW
|
||||
#endif
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
pxAssert(!g_main_window);
|
||||
@@ -1007,10 +1015,15 @@ void MainWindow::updateWindowTitle()
|
||||
if (windowTitle() != main_title)
|
||||
setWindowTitle(main_title);
|
||||
|
||||
if (m_display_container && !isRenderingToMain())
|
||||
if (m_display_surface && !isRenderingToMain())
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (m_display_surface->title() != display_title)
|
||||
m_display_surface->setTitle(display_title);
|
||||
#else
|
||||
if (m_display_container->windowTitle() != display_title)
|
||||
m_display_container->setWindowTitle(display_title);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (g_log_window)
|
||||
@@ -1039,7 +1052,13 @@ void MainWindow::updateWindowState(bool force_visible)
|
||||
|
||||
// Update the display widget too if rendering separately.
|
||||
if (m_display_surface && !isRenderingToMain())
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
QtUtils::SetWindowResizeable(m_display_surface, resizeable);
|
||||
#else
|
||||
QtUtils::SetWindowResizeable(m_display_container, resizeable);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setProgressBar(int current, int total)
|
||||
@@ -1074,12 +1093,12 @@ bool MainWindow::isRenderingFullscreen() const
|
||||
if (!MTGS::IsOpen() || !m_display_surface)
|
||||
return false;
|
||||
|
||||
return m_display_container->isFullScreen();
|
||||
return m_display_surface->isFullScreen();
|
||||
}
|
||||
|
||||
bool MainWindow::isRenderingToMain() const
|
||||
{
|
||||
return (m_display_surface && m_ui.mainContainer->indexOf(m_display_container) == 1);
|
||||
return (m_display_container && m_ui.mainContainer->indexOf(m_display_container) == 1);
|
||||
}
|
||||
|
||||
bool MainWindow::shouldHideMouseCursor() const
|
||||
@@ -1107,16 +1126,25 @@ bool MainWindow::shouldMouseLock() const
|
||||
if (m_display_created == false || m_display_surface == nullptr)
|
||||
return false;
|
||||
|
||||
bool windowsHidden = (!g_debugger_window || g_debugger_window->isHidden()) &&
|
||||
(!m_controller_settings_window || m_controller_settings_window->isHidden()) &&
|
||||
(!m_settings_window || m_settings_window->isHidden());
|
||||
const bool windowsHidden = (!g_debugger_window || g_debugger_window->isHidden()) &&
|
||||
(!m_controller_settings_window || m_controller_settings_window->isHidden()) &&
|
||||
(!m_settings_window || m_settings_window->isHidden());
|
||||
|
||||
auto* displayWindow = isRenderingToMain() ? window() : m_display_container->window();
|
||||
|
||||
if (displayWindow == nullptr)
|
||||
if (!windowsHidden)
|
||||
return false;
|
||||
|
||||
return windowsHidden && (displayWindow->isActiveWindow() || displayWindow->isFullScreen());
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (isRenderingToMain())
|
||||
{
|
||||
const auto* displayWindow = window();
|
||||
return displayWindow ? (displayWindow->isActiveWindow() || displayWindow->isFullScreen()) : false;
|
||||
}
|
||||
else
|
||||
return m_display_surface->isActive() || m_display_surface->isFullScreen();
|
||||
#else
|
||||
const auto* displayWindow = isRenderingToMain() ? window() : m_display_container->window();
|
||||
return displayWindow ? (displayWindow->isActiveWindow() || displayWindow->isFullScreen()) : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MainWindow::shouldAbortForMemcardBusy(const VMLock& lock)
|
||||
@@ -1179,7 +1207,7 @@ void MainWindow::switchToEmulationView()
|
||||
g_emu_thread->setVMPaused(false);
|
||||
|
||||
if (m_display_surface)
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
}
|
||||
|
||||
void MainWindow::refreshGameList(bool invalidate_cache, bool popup_on_error)
|
||||
@@ -1192,6 +1220,12 @@ void MainWindow::cancelGameListRefresh()
|
||||
m_game_list_widget->cancelRefresh();
|
||||
}
|
||||
|
||||
void MainWindow::updateGameListBackground()
|
||||
{
|
||||
if (m_game_list_widget)
|
||||
m_game_list_widget->setCustomBackground();
|
||||
}
|
||||
|
||||
void MainWindow::reportInfo(const QString& title, const QString& message)
|
||||
{
|
||||
QMessageBox::information(this, title, message);
|
||||
@@ -1213,6 +1247,103 @@ void MainWindow::onStatusMessage(const QString& message)
|
||||
m_ui.statusBar->showMessage(message);
|
||||
}
|
||||
|
||||
void MainWindow::reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup)
|
||||
{
|
||||
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||
if (!prompt_on_error)
|
||||
{
|
||||
SaveState_ReportLoadErrorOSD(message.toStdString(), slot, backup);
|
||||
return;
|
||||
}
|
||||
|
||||
QString title;
|
||||
if (slot.has_value())
|
||||
{
|
||||
if (backup)
|
||||
title = tr("Failed to Load State From Backup Slot %1").arg(*slot);
|
||||
else
|
||||
title = tr("Failed to Load State From Slot %1").arg(*slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
title = tr("Failed to Load State");
|
||||
}
|
||||
|
||||
VMLock lock(pauseAndLockVM());
|
||||
|
||||
QCheckBox* do_not_show_again = new QCheckBox(tr("Do not show again"));
|
||||
|
||||
QPointer<QMessageBox> message_box = new QMessageBox(this);
|
||||
message_box->setWindowTitle(title);
|
||||
message_box->setText(message);
|
||||
message_box->setIcon(QMessageBox::Critical);
|
||||
message_box->addButton(QMessageBox::Ok);
|
||||
message_box->setDefaultButton(QMessageBox::Ok);
|
||||
message_box->setCheckBox(do_not_show_again);
|
||||
|
||||
message_box->exec();
|
||||
if (message_box.isNull())
|
||||
return;
|
||||
|
||||
if (do_not_show_again->isChecked())
|
||||
{
|
||||
Host::SetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", false);
|
||||
Host::CommitBaseSettingChanges();
|
||||
if (m_settings_window)
|
||||
{
|
||||
InterfaceSettingsWidget* interface_settings = m_settings_window->getInterfaceSettingsWidget();
|
||||
interface_settings->updatePromptOnStateLoadSaveFailureCheckbox(Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
delete message_box;
|
||||
}
|
||||
|
||||
void MainWindow::reportStateSaveError(const QString& message, std::optional<s32> slot)
|
||||
{
|
||||
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||
if (!prompt_on_error)
|
||||
{
|
||||
SaveState_ReportSaveErrorOSD(message.toStdString(), slot);
|
||||
return;
|
||||
}
|
||||
|
||||
QString title;
|
||||
if (slot.has_value())
|
||||
title = tr("Failed to Save State To Slot %1").arg(*slot);
|
||||
else
|
||||
title = tr("Failed to Save State");
|
||||
|
||||
VMLock lock(pauseAndLockVM());
|
||||
|
||||
QCheckBox* do_not_show_again = new QCheckBox(tr("Do not show again"));
|
||||
|
||||
QPointer<QMessageBox> message_box = new QMessageBox(this);
|
||||
message_box->setWindowTitle(title);
|
||||
message_box->setText(message);
|
||||
message_box->setIcon(QMessageBox::Critical);
|
||||
message_box->addButton(QMessageBox::Ok);
|
||||
message_box->setDefaultButton(QMessageBox::Ok);
|
||||
message_box->setCheckBox(do_not_show_again);
|
||||
|
||||
message_box->exec();
|
||||
if (message_box.isNull())
|
||||
return;
|
||||
|
||||
if (do_not_show_again->isChecked())
|
||||
{
|
||||
Host::SetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", false);
|
||||
Host::CommitBaseSettingChanges();
|
||||
if (m_settings_window)
|
||||
{
|
||||
InterfaceSettingsWidget* interface_settings = m_settings_window->getInterfaceSettingsWidget();
|
||||
interface_settings->updatePromptOnStateLoadSaveFailureCheckbox(Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
delete message_box;
|
||||
}
|
||||
|
||||
void MainWindow::runOnUIThread(const std::function<void()>& func)
|
||||
{
|
||||
func();
|
||||
@@ -1734,8 +1865,8 @@ void MainWindow::onCreateGameShortcutTriggered()
|
||||
|
||||
const QString title = QString::fromStdString(entry->GetTitle());
|
||||
const QString path = QString::fromStdString(entry->path);
|
||||
VMLock lock(pauseAndLockVM());
|
||||
ShortcutCreationDialog dlg(lock.getDialogParent(), title, path);
|
||||
|
||||
ShortcutCreationDialog dlg(this, title, path);
|
||||
dlg.exec();
|
||||
}
|
||||
#endif
|
||||
@@ -2025,7 +2156,7 @@ void MainWindow::onVMResumed()
|
||||
if (m_display_surface)
|
||||
{
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2345,21 +2476,25 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
if (!is_fullscreen && !is_rendering_to_main)
|
||||
saveDisplayWindowGeometryToConfig();
|
||||
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
auto* displayWindow = m_display_surface;
|
||||
#else
|
||||
auto* displayWindow = m_display_container;
|
||||
#endif
|
||||
if (fullscreen)
|
||||
{
|
||||
m_display_container->showFullScreen();
|
||||
}
|
||||
displayWindow->showFullScreen();
|
||||
else
|
||||
{
|
||||
// Needs to exit fullscreen before resizing
|
||||
displayWindow->showNormal();
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
m_display_container->setGeometry(geometry());
|
||||
displayWindow->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_container->showNormal();
|
||||
}
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
updateWindowState();
|
||||
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
@@ -2396,7 +2531,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
updateWindowState();
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
return wi;
|
||||
}
|
||||
|
||||
@@ -2413,9 +2548,14 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
|
||||
m_display_surface = new DisplaySurface();
|
||||
if (fullscreen || !render_to_main)
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
m_display_surface->setTitle(windowTitle());
|
||||
m_display_surface->setIcon(windowIcon());
|
||||
#else
|
||||
m_display_container = m_display_surface->createWindowContainer();
|
||||
m_display_container->setWindowTitle(windowTitle());
|
||||
m_display_container->setWindowIcon(windowIcon());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2424,20 +2564,38 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
// On Wayland, while move/restoreGeometry can't position the window, it can influence which screen they show up on
|
||||
// On Wayland, while move/restoreGeometry can't position the window, it can influence which screen they show up on.
|
||||
// Other platforms can position windows fine, but the only thing that matters here is the screen.
|
||||
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (isVisible() && g_emu_thread->shouldRenderToMain())
|
||||
m_display_surface->setFramePosition(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_surface->showFullScreen();
|
||||
#else
|
||||
if (isVisible() && g_emu_thread->shouldRenderToMain())
|
||||
m_display_container->move(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_container->showFullScreen();
|
||||
#endif
|
||||
}
|
||||
else if (!render_to_main)
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
m_display_surface->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_surface->showNormal();
|
||||
#else
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
m_display_container->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_container->showNormal();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2466,12 +2624,21 @@ void MainWindow::displayResizeRequested(qint32 width, qint32 height)
|
||||
width = static_cast<qint32>(std::max(static_cast<int>(std::lroundf(static_cast<float>(width) / dpr)), 1));
|
||||
height = static_cast<qint32>(std::max(static_cast<int>(std::lroundf(static_cast<float>(height) / dpr)), 1));
|
||||
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (!m_display_container)
|
||||
{
|
||||
// no parent - rendering to separate window. easy.
|
||||
QtUtils::ResizePotentiallyFixedSizeWindow(m_display_surface, width, height);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (!m_display_container->parent())
|
||||
{
|
||||
// no parent - rendering to separate window. easy.
|
||||
QtUtils::ResizePotentiallyFixedSizeWindow(m_display_container, width, height);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// we are rendering to the main window. we have to add in the extra height from the toolbar/status bar.
|
||||
const s32 extra_height = this->height() - m_display_container->height();
|
||||
@@ -2504,7 +2671,7 @@ void MainWindow::destroyDisplayWidget(bool show_game_list)
|
||||
if (!m_display_surface)
|
||||
return;
|
||||
|
||||
if (!isRenderingFullscreen() && !isRenderingToMain())
|
||||
if (!m_display_surface->isFullScreen() && !isRenderingToMain())
|
||||
saveDisplayWindowGeometryToConfig();
|
||||
|
||||
if (isRenderingToMain())
|
||||
@@ -2518,12 +2685,18 @@ void MainWindow::destroyDisplayWidget(bool show_game_list)
|
||||
}
|
||||
}
|
||||
|
||||
// displau surface is always in a container
|
||||
pxAssert(m_display_container != nullptr);
|
||||
m_display_container->deleteLater();
|
||||
m_display_container = nullptr;
|
||||
// m_display_surface will be destroyed by the container's dtor
|
||||
m_display_surface = nullptr;
|
||||
if (m_display_container)
|
||||
{
|
||||
m_display_container->deleteLater();
|
||||
m_display_container = nullptr;
|
||||
// m_display_surface will be destroyed by the container's dtor
|
||||
m_display_surface = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_display_surface->deleteLater();
|
||||
m_display_surface = nullptr;
|
||||
}
|
||||
|
||||
updateDisplayRelatedActions(false, false, false);
|
||||
}
|
||||
@@ -2535,14 +2708,6 @@ void MainWindow::updateDisplayWidgetCursor()
|
||||
m_display_surface->updateCursor(s_vm_valid && !s_vm_paused && shouldHideMouseCursor());
|
||||
}
|
||||
|
||||
void MainWindow::focusDisplayWidget()
|
||||
{
|
||||
if (!m_display_surface || centralWidget() != m_display_container)
|
||||
return;
|
||||
|
||||
m_display_container->setFocus();
|
||||
}
|
||||
|
||||
void MainWindow::setupMouseMoveHandler()
|
||||
{
|
||||
auto mouse_cb_fn = [](int x, int y) {
|
||||
@@ -2573,7 +2738,11 @@ void MainWindow::checkMousePosition(int x, int y)
|
||||
|
||||
// logical (DIP) frame rect
|
||||
const QSize logicalSize = displayWindow->size();
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
const QPoint logicalPosition = isRenderingToMain() ? (displayWindow->position() + displayWindow->parent()->position()) : displayWindow->position();
|
||||
#else
|
||||
const QPoint logicalPosition = displayWindow->position() + displayWindow->parent()->position();
|
||||
#endif
|
||||
|
||||
// The offset to the origin of the current screen is in device-independent pixels while the origin itself is native!
|
||||
// The logicalPosition is the sum of these two values, so we need to separate them and only scale the offset
|
||||
@@ -2604,13 +2773,13 @@ void MainWindow::checkMousePosition(int x, int y)
|
||||
|
||||
void MainWindow::saveDisplayWindowGeometryToConfig()
|
||||
{
|
||||
if (m_display_container->windowState() & Qt::WindowFullScreen)
|
||||
if (m_display_surface->isFullScreen())
|
||||
{
|
||||
// if we somehow ended up here, don't save the fullscreen state to the config
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray geometry = m_display_container->saveGeometry();
|
||||
const QByteArray geometry = m_display_surface->saveGeometry();
|
||||
const QByteArray geometry_b64 = geometry.toBase64();
|
||||
const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry");
|
||||
if (old_geometry_b64 != geometry_b64.constData())
|
||||
@@ -2626,15 +2795,23 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig()
|
||||
const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64));
|
||||
if (!geometry.isEmpty())
|
||||
{
|
||||
m_display_container->restoreGeometry(geometry);
|
||||
m_display_surface->restoreGeometry(geometry);
|
||||
|
||||
// make sure we're not loading a dodgy config which had fullscreen set...
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
m_display_surface->setWindowStates(m_display_surface->windowStates() & ~(Qt::WindowFullScreen | Qt::WindowActive));
|
||||
#else
|
||||
m_display_container->setWindowState(m_display_container->windowState() & ~(Qt::WindowFullScreen | Qt::WindowActive));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// default size
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
m_display_surface->resize(640, 480);
|
||||
#else
|
||||
m_display_container->resize(640, 480);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3204,7 +3381,7 @@ MainWindow::VMLock MainWindow::pauseAndLockVM()
|
||||
g_emu_thread->setFullscreen(false, false);
|
||||
|
||||
// Process events untill both EmuThread and Qt have finished exiting fullscreen
|
||||
while (QtHost::IsVMValid() && (g_emu_thread->isFullscreen() || m_display_container->isFullScreen()))
|
||||
while (QtHost::IsVMValid() && (g_emu_thread->isFullscreen() || m_display_surface->isFullScreen()))
|
||||
{
|
||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
}
|
||||
@@ -3216,7 +3393,27 @@ MainWindow::VMLock MainWindow::pauseAndLockVM()
|
||||
g_main_window->raise();
|
||||
g_main_window->activateWindow();
|
||||
|
||||
return VMLock(m_display_container, was_paused, was_fullscreen);
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (!m_display_container)
|
||||
{
|
||||
// Create a temporary parent for the dialog.
|
||||
QWidget* dialog_parent = new QWidget();
|
||||
dialog_parent->setAttribute(Qt::WA_NativeWindow);
|
||||
QWindow* window_handle = dialog_parent->windowHandle();
|
||||
|
||||
// Set the transient parent to the display surface.
|
||||
// This will position the dialog_parent over the display surface (and thus so will any dialogs)
|
||||
// and also enforces the focus lock of modal dialogs against the display surface.
|
||||
// This works even without showing the dialog_parent window.
|
||||
window_handle->setTransientParent(m_display_surface);
|
||||
|
||||
return VMLock(dialog_parent, was_paused, was_fullscreen, true);
|
||||
}
|
||||
else
|
||||
return VMLock(m_display_container, was_paused, was_fullscreen, false);
|
||||
#else
|
||||
return VMLock(m_display_container, was_paused, was_fullscreen, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::rescanFile(const std::string& path)
|
||||
@@ -3224,11 +3421,12 @@ void MainWindow::rescanFile(const std::string& path)
|
||||
m_game_list_widget->rescanFile(path);
|
||||
}
|
||||
|
||||
MainWindow::VMLock::VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen)
|
||||
MainWindow::VMLock::VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen, bool owns_parent)
|
||||
: m_dialog_parent(dialog_parent)
|
||||
, m_has_lock(true)
|
||||
, m_was_paused(was_paused)
|
||||
, m_was_fullscreen(was_fullscreen)
|
||||
, m_owns_dialog_parent(owns_parent)
|
||||
{
|
||||
QtHost::LockVMWithDialog();
|
||||
}
|
||||
@@ -3238,11 +3436,13 @@ MainWindow::VMLock::VMLock(VMLock&& lock)
|
||||
, m_has_lock(lock.m_has_lock)
|
||||
, m_was_paused(lock.m_was_paused)
|
||||
, m_was_fullscreen(lock.m_was_fullscreen)
|
||||
, m_owns_dialog_parent(lock.m_owns_dialog_parent)
|
||||
{
|
||||
lock.m_dialog_parent = nullptr;
|
||||
lock.m_has_lock = false;
|
||||
lock.m_was_paused = true;
|
||||
lock.m_was_fullscreen = false;
|
||||
lock.m_owns_dialog_parent = false;
|
||||
}
|
||||
|
||||
MainWindow::VMLock::~VMLock()
|
||||
@@ -3250,6 +3450,12 @@ MainWindow::VMLock::~VMLock()
|
||||
if (m_has_lock)
|
||||
QtHost::UnlockVMWithDialog();
|
||||
|
||||
if (m_owns_dialog_parent && m_dialog_parent)
|
||||
{
|
||||
m_dialog_parent->deleteLater();
|
||||
m_dialog_parent = nullptr;
|
||||
}
|
||||
|
||||
if (m_was_fullscreen)
|
||||
{
|
||||
g_main_window->m_is_temporarily_windowed = false;
|
||||
|
||||
@@ -65,13 +65,14 @@ public:
|
||||
void cancelResume();
|
||||
|
||||
private:
|
||||
VMLock(QWidget* dialog_parent, bool was_paused, bool was_exclusive_fullscreen);
|
||||
VMLock(QWidget* dialog_parent, bool was_paused, bool was_exclusive_fullscreen, bool owns_parent);
|
||||
friend MainWindow;
|
||||
|
||||
QWidget* m_dialog_parent;
|
||||
bool m_has_lock;
|
||||
bool m_was_paused;
|
||||
bool m_was_fullscreen;
|
||||
bool m_owns_dialog_parent;
|
||||
};
|
||||
|
||||
/// Default filter for opening a file.
|
||||
@@ -116,10 +117,13 @@ public Q_SLOTS:
|
||||
void checkForUpdates(bool display_message, bool force_check);
|
||||
void refreshGameList(bool invalidate_cache, bool popup_on_error);
|
||||
void cancelGameListRefresh();
|
||||
void updateGameListBackground();
|
||||
void reportInfo(const QString& title, const QString& message);
|
||||
void reportError(const QString& title, const QString& message);
|
||||
bool confirmMessage(const QString& title, const QString& message);
|
||||
void onStatusMessage(const QString& message);
|
||||
void reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup);
|
||||
void reportStateSaveError(const QString& message, std::optional<s32> slot);
|
||||
|
||||
void runOnUIThread(const std::function<void()>& func);
|
||||
void requestReset();
|
||||
@@ -135,7 +139,6 @@ private Q_SLOTS:
|
||||
void displayResizeRequested(qint32 width, qint32 height);
|
||||
void mouseModeRequested(bool relative_mode, bool hide_cursor);
|
||||
void releaseRenderWindow();
|
||||
void focusDisplayWidget();
|
||||
void setupMouseMoveHandler();
|
||||
void onGameListRefreshComplete();
|
||||
void onGameListRefreshProgress(const QString& status, int current, int total);
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace QtHost
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static QTimer* s_settings_save_timer = nullptr;
|
||||
static std::unique_ptr<INISettingsInterface> s_base_settings_interface;
|
||||
static std::unique_ptr<INISettingsInterface> s_secrets_settings_interface;
|
||||
static bool s_batch_mode = false;
|
||||
static bool s_nogui_mode = false;
|
||||
static bool s_start_big_picture_mode = false;
|
||||
@@ -194,6 +195,9 @@ void EmuThread::stopFullscreenUI()
|
||||
{
|
||||
m_run_fullscreen_ui.store(false, std::memory_order_release);
|
||||
emit onFullscreenUIStateChange(false);
|
||||
|
||||
// Resume and refresh background when FullscreenUI exits
|
||||
QMetaObject::invokeMethod(g_main_window, "updateGameListBackground", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +303,11 @@ void EmuThread::loadState(const QString& filename)
|
||||
|
||||
Error error;
|
||||
if (!VMManager::LoadState(filename.toUtf8().constData(), &error))
|
||||
Host::ReportErrorAsync(TRANSLATE_SV("QtHost", "Failed to Load State"), error.GetDescription());
|
||||
{
|
||||
QtHost::RunOnUIThread([message = QString::fromStdString(error.GetDescription())]() {
|
||||
g_main_window->reportStateLoadError(message, std::nullopt, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EmuThread::loadStateFromSlot(qint32 slot, bool load_backup)
|
||||
@@ -315,7 +323,11 @@ void EmuThread::loadStateFromSlot(qint32 slot, bool load_backup)
|
||||
|
||||
Error error;
|
||||
if (!VMManager::LoadStateFromSlot(slot, load_backup, &error))
|
||||
Host::ReportErrorAsync(TRANSLATE_SV("QtHost", "Failed to Load State"), error.GetDescription());
|
||||
{
|
||||
QtHost::RunOnUIThread([message = QString::fromStdString(error.GetDescription()), slot, load_backup]() {
|
||||
g_main_window->reportStateLoadError(message, slot, load_backup);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EmuThread::saveState(const QString& filename)
|
||||
@@ -329,11 +341,11 @@ void EmuThread::saveState(const QString& filename)
|
||||
if (!VMManager::HasValidVM())
|
||||
return;
|
||||
|
||||
if (!VMManager::SaveState(filename.toUtf8().constData()))
|
||||
{
|
||||
// this one is usually the result of a user-chosen path, so we can display a message box safely here
|
||||
Console.Error("Failed to save state");
|
||||
}
|
||||
VMManager::SaveState(filename.toUtf8().constData(), true, false, [](const std::string& error) {
|
||||
QtHost::RunOnUIThread([message = QString::fromStdString(error)]() {
|
||||
g_main_window->reportStateSaveError(message, std::nullopt);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void EmuThread::saveStateToSlot(qint32 slot)
|
||||
@@ -347,7 +359,11 @@ void EmuThread::saveStateToSlot(qint32 slot)
|
||||
if (!VMManager::HasValidVM())
|
||||
return;
|
||||
|
||||
VMManager::SaveStateToSlot(slot);
|
||||
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||
QtHost::RunOnUIThread([message = QString::fromStdString(error), slot]() {
|
||||
g_main_window->reportStateSaveError(message, slot);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void EmuThread::run()
|
||||
@@ -1292,6 +1308,7 @@ bool QtHost::InitializeConfig()
|
||||
// Write crash dumps to the data directory, since that'll be accessible for certain.
|
||||
CrashHandler::SetWriteDirectory(EmuFolders::DataRoot);
|
||||
|
||||
// Load main settings ini
|
||||
const std::string path = Path::Combine(EmuFolders::Settings, "PCSX2.ini");
|
||||
const bool settings_exists = FileSystem::FileExists(path.c_str());
|
||||
Console.WriteLnFmt("Loading config from {}.", path);
|
||||
@@ -1332,6 +1349,29 @@ bool QtHost::InitializeConfig()
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
// Layer secrets ini on top
|
||||
const std::string secrets_path = Path::Combine(EmuFolders::Settings, "secrets.ini");
|
||||
const bool secrets_settings_exists = FileSystem::FileExists(secrets_path.c_str());
|
||||
Console.WriteLnFmt("Loading secrets from {}.", secrets_path);
|
||||
|
||||
s_secrets_settings_interface = std::make_unique<INISettingsInterface>(std::move(secrets_path));
|
||||
Host::Internal::SetSecretsSettingsLayer(s_secrets_settings_interface.get());
|
||||
if (!secrets_settings_exists || !s_secrets_settings_interface->Load())
|
||||
{
|
||||
if (!s_base_settings_interface->Save(&error))
|
||||
{
|
||||
QMessageBox::critical(
|
||||
nullptr, QStringLiteral("PCSX2"),
|
||||
QStringLiteral(
|
||||
"Failed to save secrets to\n\n%1\n\nThe error was: %2\n\nPlease ensure this directory is writable. You "
|
||||
"can also try portable mode by creating portable.txt in the same directory you installed PCSX2 into.")
|
||||
.arg(QString::fromStdString(s_secrets_settings_interface->GetFileName()))
|
||||
.arg(QString::fromStdString(error.GetDescription())));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Setup wizard was incomplete last time?
|
||||
s_run_setup_wizard =
|
||||
s_run_setup_wizard || s_base_settings_interface->GetBoolValue("UI", "SetupWizardIncomplete", false);
|
||||
|
||||
@@ -141,9 +141,16 @@ namespace QtUtils
|
||||
|
||||
void resizeAndScalePixmap(QPixmap* pm, const int expected_width, const int expected_height, const qreal dpr, const ScalingMode scaling_mode, const float opacity)
|
||||
{
|
||||
if (!pm || pm->isNull() || pm->width() <= 0 || pm->height() <= 0)
|
||||
if (!pm || pm->width() <= 0 || pm->height() <= 0)
|
||||
return;
|
||||
|
||||
if (dpr <= 0.0)
|
||||
{
|
||||
Console.ErrorFmt("resizeAndScalePixmap: Invalid device pixel ratio ({}) - pixmap will be null", dpr);
|
||||
*pm = QPixmap();
|
||||
return;
|
||||
}
|
||||
|
||||
const int dpr_expected_width = qRound(expected_width * dpr);
|
||||
const int dpr_expected_height = qRound(expected_height * dpr);
|
||||
|
||||
@@ -196,8 +203,7 @@ namespace QtUtils
|
||||
qRound(scaledSize.width()),
|
||||
qRound(scaledSize.height()),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation
|
||||
);
|
||||
Qt::SmoothTransformation);
|
||||
|
||||
const QRectF scaledSrcRect(0, 0, pm->width(), pm->height());
|
||||
|
||||
@@ -211,16 +217,7 @@ namespace QtUtils
|
||||
}
|
||||
case ScalingMode::Stretch:
|
||||
{
|
||||
*pm = pm->scaled(
|
||||
dpr_expected_width,
|
||||
dpr_expected_height,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation
|
||||
);
|
||||
|
||||
const QRectF scaledSrcRect(0, 0, pm->width(), pm->height());
|
||||
|
||||
painter.drawPixmap(painterRect, *pm, scaledSrcRect);
|
||||
painter.drawPixmap(painterRect, *pm, srcRect);
|
||||
break;
|
||||
}
|
||||
case ScalingMode::Center:
|
||||
@@ -229,7 +226,6 @@ namespace QtUtils
|
||||
const qreal pmHeight = pm->height() / dpr;
|
||||
|
||||
QRectF destRect(0, 0, pmWidth, pmHeight);
|
||||
|
||||
destRect.moveCenter(painterRect.center());
|
||||
|
||||
painter.drawPixmap(destRect, *pm, srcRect);
|
||||
@@ -243,13 +239,19 @@ namespace QtUtils
|
||||
if (tileWidth <= 0 || tileHeight <= 0)
|
||||
break;
|
||||
|
||||
QPixmap tileSource = pm->scaled(tileWidth, tileHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
tileSource.setDevicePixelRatio(dpr);
|
||||
|
||||
QBrush tileBrush(tileSource);
|
||||
tileBrush.setTextureImage(tileSource.toImage());
|
||||
|
||||
painter.fillRect(painterRect, tileBrush);
|
||||
if (pm->devicePixelRatio() == dpr)
|
||||
{
|
||||
QBrush tileBrush(*pm);
|
||||
painter.fillRect(painterRect, tileBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
QPixmap tileSource = pm->scaled(tileWidth, tileHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
tileSource.setDevicePixelRatio(dpr);
|
||||
QBrush tileBrush(tileSource);
|
||||
tileBrush.setTextureImage(tileSource.toImage());
|
||||
painter.fillRect(painterRect, tileBrush);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -364,6 +366,25 @@ namespace QtUtils
|
||||
}
|
||||
}
|
||||
|
||||
void SetWindowResizeable(QWindow* window, bool resizeable)
|
||||
{
|
||||
if (resizeable)
|
||||
{
|
||||
// Min/max numbers come from uic.
|
||||
window->setMinimumWidth(1);
|
||||
window->setMinimumHeight(1);
|
||||
window->setMaximumWidth(16777215);
|
||||
window->setMaximumHeight(16777215);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->setMinimumWidth(window->width());
|
||||
window->setMinimumHeight(window->height());
|
||||
window->setMaximumWidth(window->width());
|
||||
window->setMaximumHeight(window->height());
|
||||
}
|
||||
}
|
||||
|
||||
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height)
|
||||
{
|
||||
width = std::max(width, 1);
|
||||
@@ -374,6 +395,22 @@ namespace QtUtils
|
||||
widget->resize(width, height);
|
||||
}
|
||||
|
||||
void ResizePotentiallyFixedSizeWindow(QWindow* window, int width, int height)
|
||||
{
|
||||
width = std::max(width, 1);
|
||||
height = std::max(height, 1);
|
||||
|
||||
if (window->minimumHeight() == window->maximumHeight())
|
||||
{
|
||||
window->setMinimumWidth(width);
|
||||
window->setMinimumHeight(height);
|
||||
window->setMaximumWidth(width);
|
||||
window->setMaximumHeight(height);
|
||||
}
|
||||
|
||||
window->resize(width, height);
|
||||
}
|
||||
|
||||
QString AbstractItemModelToCSV(QAbstractItemModel* model, int role, bool useQuotes)
|
||||
{
|
||||
QString csv;
|
||||
|
||||
@@ -102,9 +102,11 @@ namespace QtUtils
|
||||
|
||||
/// Changes whether a window is resizable.
|
||||
void SetWindowResizeable(QWidget* widget, bool resizeable);
|
||||
void SetWindowResizeable(QWindow* window, bool resizeable);
|
||||
|
||||
/// Adjusts the fixed size for a window if it's not resizeable.
|
||||
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height);
|
||||
void ResizePotentiallyFixedSizeWindow(QWindow* window, int width, int height);
|
||||
|
||||
/// Returns the common window info structure for a Qt Window/Widget.
|
||||
template <class T>
|
||||
|
||||
@@ -17,7 +17,8 @@ AchievementLoginDialog::AchievementLoginDialog(QWidget* parent, Achievements::Lo
|
||||
, m_reason(reason)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
QtUtils::SetScalableIcon(m_ui.loginIcon, QIcon::fromTheme(QStringLiteral("login-box-line")), QSize(32, 32));
|
||||
const QString base_path(QtHost::GetResourcesBasePath());
|
||||
QtUtils::SetScalableIcon(m_ui.loginIcon, QIcon(QStringLiteral("%1/icons/ra-icon.svg").arg(base_path)), QSize(50, 50));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
// Adjust text if needed based on reason.
|
||||
|
||||
@@ -39,6 +39,8 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsWindow* settings_dialog, QWidget*
|
||||
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
|
||||
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
|
||||
connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &BIOSSettingsWidget::fastBootChanged);
|
||||
|
||||
fastBootChanged();
|
||||
}
|
||||
|
||||
BIOSSettingsWidget::~BIOSSettingsWidget() = default;
|
||||
|
||||
@@ -202,7 +202,11 @@ void ControllerBindingWidget::onAutomaticBindingClicked()
|
||||
for (const QPair<QString, QString>& dev : m_dialog->getDeviceList())
|
||||
{
|
||||
// we set it as data, because the device list could get invalidated while the menu is up
|
||||
QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.first).arg(dev.second));
|
||||
QAction* action;
|
||||
if(dev.first.compare(dev.second, Qt::CaseInsensitive) == 0)
|
||||
action = menu.addAction(dev.first);
|
||||
else
|
||||
action = menu.addAction(QStringLiteral("%1: %2").arg(dev.first).arg(dev.second));
|
||||
action->setData(dev.first);
|
||||
connect(action, &QAction::triggered, this, [this, action]() { doDeviceAutomaticBinding(action->data().toString()); });
|
||||
added = true;
|
||||
@@ -1152,7 +1156,11 @@ void USBDeviceWidget::onAutomaticBindingClicked()
|
||||
for (const QPair<QString, QString>& dev : m_dialog->getDeviceList())
|
||||
{
|
||||
// we set it as data, because the device list could get invalidated while the menu is up
|
||||
QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.first).arg(dev.second));
|
||||
QAction* action;
|
||||
if(dev.first.compare(dev.second, Qt::CaseInsensitive) == 0)
|
||||
action = menu.addAction(dev.first);
|
||||
else
|
||||
action = menu.addAction(QStringLiteral("%1: %2").arg(dev.first).arg(dev.second));
|
||||
action->setData(dev.first);
|
||||
connect(action, &QAction::triggered, this, [this, action]() { doDeviceAutomaticBinding(action->data().toString()); });
|
||||
added = true;
|
||||
|
||||
@@ -88,7 +88,11 @@ ControllerGlobalSettingsWidget::~ControllerGlobalSettingsWidget() = default;
|
||||
void ControllerGlobalSettingsWidget::addDeviceToList(const QString& identifier, const QString& name)
|
||||
{
|
||||
QListWidgetItem* item = new QListWidgetItem();
|
||||
item->setText(QStringLiteral("%1: %2").arg(identifier).arg(name));
|
||||
if(identifier.compare(name,Qt::CaseInsensitive) == 0)
|
||||
item->setText(identifier);
|
||||
else
|
||||
item->setText(QStringLiteral("%1: %2").arg(identifier).arg(name));
|
||||
|
||||
item->setData(Qt::UserRole, identifier);
|
||||
m_ui.deviceList->addItem(item);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/HeterogeneousContainers.h"
|
||||
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QStandardItemModel>
|
||||
|
||||
GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent)
|
||||
@@ -40,6 +41,9 @@ GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* settings_dialog
|
||||
|
||||
m_ui.cheatList->expandAll();
|
||||
|
||||
m_ui.cheatList->viewport()->installEventFilter(this);
|
||||
m_ui.cheatList->viewport()->setMouseTracking(true);
|
||||
|
||||
SettingsInterface* sif = dialog()->getSettingsInterface();
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "EmuCore", "EnableCheats", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.allCRCsCheckbox, "EmuCore", "ShowCheatsForAllCRCs", false);
|
||||
@@ -83,7 +87,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (!data.isValid())
|
||||
return;
|
||||
|
||||
@@ -95,7 +99,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
||||
|
||||
void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
||||
{
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (!data.isValid())
|
||||
return;
|
||||
|
||||
@@ -109,6 +113,31 @@ void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
||||
setCheatEnabled(std::move(cheat_name), current_checked, true);
|
||||
}
|
||||
|
||||
void GameCheatSettingsWidget::onCheatListItemHovered(const QModelIndex& index)
|
||||
{
|
||||
const QModelIndex source_index = m_model_proxy->mapToSource(index);
|
||||
const QModelIndex sibling_index = source_index.siblingAtColumn(0);
|
||||
QStandardItem* item = m_model->itemFromIndex(sibling_index);
|
||||
if (!item)
|
||||
{
|
||||
// No item is selected.
|
||||
m_ui.appliedLabel->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<Patch::patch_place_type> place;
|
||||
|
||||
bool ok;
|
||||
int place_value = item->data(PLACE_ROLE).toInt(&ok);
|
||||
if (ok)
|
||||
{
|
||||
// The patch commands in the group are all applied at the same time.
|
||||
place = static_cast<Patch::patch_place_type>(place_value);
|
||||
}
|
||||
|
||||
m_ui.appliedLabel->setText(tr("<strong>Applied:</strong> %1").arg(Patch::PlaceToString(place)));
|
||||
}
|
||||
|
||||
void GameCheatSettingsWidget::onReloadClicked()
|
||||
{
|
||||
reloadList();
|
||||
@@ -136,6 +165,32 @@ void GameCheatSettingsWidget::disableAllCheats()
|
||||
si->Save();
|
||||
}
|
||||
|
||||
bool GameCheatSettingsWidget::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (watched == m_ui.cheatList->viewport())
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||||
onCheatListItemHovered(m_ui.cheatList->indexAt(mouse_event->position().toPoint()));
|
||||
return true;
|
||||
}
|
||||
case QEvent::Leave:
|
||||
{
|
||||
onCheatListItemHovered(QModelIndex());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SettingsWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void GameCheatSettingsWidget::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
@@ -185,7 +240,7 @@ void GameCheatSettingsWidget::setStateRecursively(QStandardItem* parent, bool en
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
QStandardItem* item = parent ? parent->child(i, 0) : m_model->item(i, 0);
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (data.isValid())
|
||||
{
|
||||
if ((item->checkState() == Qt::Checked) != enabled)
|
||||
@@ -277,7 +332,9 @@ QList<QStandardItem*> GameCheatSettingsWidget::populateTreeViewRow(const Patch::
|
||||
const std::string_view name_part = pi.GetNamePart();
|
||||
nameItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren | Qt::ItemIsEnabled);
|
||||
nameItem->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
|
||||
nameItem->setData(QString::fromStdString(pi.name), Qt::UserRole);
|
||||
nameItem->setData(QString::fromStdString(pi.name), NAME_ROLE);
|
||||
if (pi.place.has_value())
|
||||
nameItem->setData(static_cast<int>(*pi.place), PLACE_ROLE);
|
||||
if (!name_part.empty())
|
||||
nameItem->setText(QString::fromUtf8(name_part.data(), name_part.length()));
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
~GameCheatSettingsWidget();
|
||||
|
||||
void disableAllCheats();
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
@@ -39,6 +40,7 @@ protected:
|
||||
private Q_SLOTS:
|
||||
void onCheatListItemDoubleClicked(const QModelIndex& index);
|
||||
void onCheatListItemChanged(QStandardItem* item);
|
||||
void onCheatListItemHovered(const QModelIndex& index);
|
||||
void onReloadClicked();
|
||||
void updateListEnabled();
|
||||
void reloadList();
|
||||
@@ -50,6 +52,12 @@ private:
|
||||
void setStateForAll(bool enabled);
|
||||
void setStateRecursively(QStandardItem* parent, bool enabled);
|
||||
|
||||
enum Roles
|
||||
{
|
||||
NAME_ROLE = Qt::UserRole,
|
||||
PLACE_ROLE = Qt::UserRole + 1
|
||||
};
|
||||
|
||||
Ui::GameCheatSettingsWidget m_ui;
|
||||
QStandardItemModel* m_model = nullptr;
|
||||
QSortFilterProxyModel* m_model_proxy = nullptr;
|
||||
|
||||
@@ -90,6 +90,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="appliedLabel">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -15,19 +15,23 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
GamePatchDetailsWidget::GamePatchDetailsWidget(std::string name, const std::string& author,
|
||||
const std::string& description, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
||||
GamePatchDetailsWidget::GamePatchDetailsWidget(const Patch::PatchInfo& info, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_dialog(dialog)
|
||||
, m_name(name)
|
||||
, m_name(info.name)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_ui.name->setText(QString::fromStdString(name));
|
||||
const QString name = QString::fromStdString(info.name);
|
||||
const QString author = !info.author.empty() ? QString::fromStdString(info.author) : tr("Unknown");
|
||||
const QString place = QString::fromUtf8(PlaceToString(info.place));
|
||||
const QString description = !info.description.empty() ? QString::fromStdString(info.description) : tr("No description provided.");
|
||||
m_ui.name->setText(name);
|
||||
m_ui.description->setText(
|
||||
tr("<strong>Author: </strong>%1<br>%2")
|
||||
.arg(author.empty() ? tr("Unknown") : QString::fromStdString(author))
|
||||
.arg(description.empty() ? tr("No description provided.") : QString::fromStdString(description)));
|
||||
tr("<strong>Author:</strong> %1<br><strong>Applied:</strong> %2<br>%3")
|
||||
.arg(author)
|
||||
.arg(place)
|
||||
.arg(description));
|
||||
|
||||
pxAssert(dialog->getSettingsInterface());
|
||||
m_ui.enabled->setTristate(tristate);
|
||||
@@ -178,7 +182,7 @@ void GamePatchSettingsWidget::reloadList()
|
||||
}
|
||||
|
||||
GamePatchDetailsWidget* it =
|
||||
new GamePatchDetailsWidget(std::move(pi.name), pi.author, pi.description, globally_toggleable_option, check_state, dialog(), container);
|
||||
new GamePatchDetailsWidget(pi, globally_toggleable_option, check_state, dialog(), container);
|
||||
layout->addWidget(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class GamePatchDetailsWidget : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GamePatchDetailsWidget(std::string name, const std::string& author, const std::string& description, bool tristate, Qt::CheckState checkState,
|
||||
GamePatchDetailsWidget(const Patch::PatchInfo& info, bool tristate, Qt::CheckState checkState,
|
||||
SettingsWindow* dialog, QWidget* parent);
|
||||
~GamePatchDetailsWidget();
|
||||
|
||||
|
||||
@@ -97,15 +97,16 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmShutdown, "UI", "ConfirmShutdown", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnControllerDisconnection, "UI", "PauseOnControllerDisconnection", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.promptOnStateLoadSaveFailure, "UI", "PromptOnStateLoadSaveFailure", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "EmuCore", "EnableDiscordPresence", false);
|
||||
|
||||
#ifdef __linux__ // Mouse locking is only supported on X11
|
||||
#ifdef __linux__ // Mouse locking is only supported on X11
|
||||
const bool mouse_lock_supported = QGuiApplication::platformName().toLower() == "xcb";
|
||||
#else
|
||||
const bool mouse_lock_supported = true;
|
||||
#endif
|
||||
|
||||
if(mouse_lock_supported)
|
||||
if (mouse_lock_supported)
|
||||
{
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mouseLock, "EmuCore", "EnableMouseLock", false);
|
||||
connect(m_ui.mouseLock, &QCheckBox::checkStateChanged, [](Qt::CheckState state) {
|
||||
@@ -196,6 +197,8 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
"and unpauses when you switch back."));
|
||||
dialog()->registerWidgetHelp(m_ui.pauseOnControllerDisconnection, tr("Pause On Controller Disconnection"),
|
||||
tr("Unchecked"), tr("Pauses the emulator when a controller with bindings is disconnected."));
|
||||
dialog()->registerWidgetHelp(m_ui.promptOnStateLoadSaveFailure, tr("Pause On State Load/Save Failure"),
|
||||
tr("Checked"), tr("Display a modal dialog when a save state load/save operation fails."));
|
||||
dialog()->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"),
|
||||
tr("Automatically switches to fullscreen mode when a game is started."));
|
||||
dialog()->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Unchecked"),
|
||||
@@ -224,7 +227,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
dialog()->registerWidgetHelp(
|
||||
m_ui.backgroundBrowse, tr("Game List Background"), tr("None"),
|
||||
tr("Enable an animated/static background on the game list (where you launch your games).<br>"
|
||||
"This background is only visible in the library and will be hidden once a game is launched. It will also be paused when it's not in focus."));
|
||||
"This background is only visible in the library and will be hidden once a game is launched. It will also be paused when it's not in focus."));
|
||||
dialog()->registerWidgetHelp(
|
||||
m_ui.backgroundReset, tr("Disable/Reset Game List Background"), tr("None"),
|
||||
tr("Disable and reset the currently applied game list background."));
|
||||
@@ -241,6 +244,12 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
|
||||
InterfaceSettingsWidget::~InterfaceSettingsWidget() = default;
|
||||
|
||||
void InterfaceSettingsWidget::updatePromptOnStateLoadSaveFailureCheckbox(Qt::CheckState state)
|
||||
{
|
||||
QSignalBlocker blocker(m_ui.promptOnStateLoadSaveFailure);
|
||||
m_ui.promptOnStateLoadSaveFailure->setCheckState(state);
|
||||
}
|
||||
|
||||
void InterfaceSettingsWidget::onRenderToSeparateWindowChanged()
|
||||
{
|
||||
m_ui.hideMainWindow->setEnabled(m_ui.renderToSeparateWindow->isChecked());
|
||||
@@ -266,7 +275,7 @@ void InterfaceSettingsWidget::onSetGameListBackgroundTriggered()
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
std::string relative_path = Path::MakeRelative(QDir::toNativeSeparators(path).toStdString(), EmuFolders::DataRoot);
|
||||
std::string relative_path = Path::MakeRelative(path.toStdString(), EmuFolders::DataRoot);
|
||||
Host::SetBaseStringSettingValue("UI", "GameListBackgroundPath", relative_path.c_str());
|
||||
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
@@ -15,6 +15,8 @@ public:
|
||||
InterfaceSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent);
|
||||
~InterfaceSettingsWidget();
|
||||
|
||||
void updatePromptOnStateLoadSaveFailureCheckbox(Qt::CheckState state);
|
||||
|
||||
Q_SIGNALS:
|
||||
void themeChanged();
|
||||
void languageChanged();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>725</width>
|
||||
<height>625</height>
|
||||
<height>637</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -78,6 +78,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="promptOnStateLoadSaveFailure">
|
||||
<property name="text">
|
||||
<string>Prompt On State Load/Save Failure</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -380,6 +387,7 @@
|
||||
<tabstop>discordPresence</tabstop>
|
||||
<tabstop>pauseOnControllerDisconnection</tabstop>
|
||||
<tabstop>mouseLock</tabstop>
|
||||
<tabstop>promptOnStateLoadSaveFailure</tabstop>
|
||||
<tabstop>startFullscreen</tabstop>
|
||||
<tabstop>doubleClickTogglesFullscreen</tabstop>
|
||||
<tabstop>renderToSeparateWindow</tabstop>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "VMManager.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <Windows.h>
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <shlobj.h>
|
||||
#include <winnls.h>
|
||||
#include <shobjidl.h>
|
||||
@@ -61,16 +61,6 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
||||
connect(m_ui.bootOptionToggle, &QCheckBox::toggled, m_ui.bootOptionDropdown, &QPushButton::setEnabled);
|
||||
connect(m_ui.fullscreenMode, &QCheckBox::toggled, m_ui.fullscreenModeDropdown, &QPushButton::setEnabled);
|
||||
|
||||
m_ui.shortcutDesktop->setChecked(true);
|
||||
m_ui.overrideBootELFPath->setEnabled(false);
|
||||
m_ui.overrideBootELFButton->setEnabled(false);
|
||||
m_ui.gameArgs->setEnabled(false);
|
||||
m_ui.bootOptionDropdown->setEnabled(false);
|
||||
m_ui.fullscreenModeDropdown->setEnabled(false);
|
||||
m_ui.loadStateIndex->setEnabled(false);
|
||||
m_ui.loadStateFileBrowse->setEnabled(false);
|
||||
m_ui.loadStateFilePath->setEnabled(false);
|
||||
|
||||
m_ui.loadStateIndex->setMaximum(VMManager::NUM_SAVE_STATE_SLOTS);
|
||||
|
||||
if (std::getenv("container"))
|
||||
@@ -80,6 +70,7 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
||||
m_ui.shortcutStartMenu->setEnabled(false);
|
||||
}
|
||||
|
||||
connect(m_ui.dialogButtons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(m_ui.dialogButtons, &QDialogButtonBox::accepted, this, [&]() {
|
||||
std::vector<std::string> args;
|
||||
|
||||
@@ -111,21 +102,24 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ui.loadStateFileToggle->isChecked() && !m_ui.loadStateFilePath->text().isEmpty())
|
||||
{
|
||||
args.push_back("-statefile");
|
||||
args.push_back(m_ui.loadStateFilePath->text().toStdString());
|
||||
}
|
||||
|
||||
if (m_ui.fullscreenMode->isChecked())
|
||||
args.push_back(m_ui.fullscreenModeDropdown->currentIndex() ? "-nofullscreen" : "-fullscreen");
|
||||
|
||||
if (m_ui.bigPictureModeToggle->isChecked())
|
||||
args.push_back("-bigpicture");
|
||||
|
||||
m_desktop = m_ui.shortcutDesktop->isChecked();
|
||||
|
||||
std::string custom_args = m_ui.customArgsInput->text().toStdString();
|
||||
|
||||
ShortcutCreationDialog::CreateShortcut(title.toStdString(), path.toStdString(), args, custom_args, m_desktop);
|
||||
ShortcutCreationDialog::CreateShortcut(title.toStdString(), path.toStdString(), args, custom_args, m_ui.shortcutDesktop->isChecked());
|
||||
|
||||
accept();
|
||||
});
|
||||
connect(m_ui.dialogButtons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::string game_path, std::vector<std::string> passed_cli_args, std::string custom_args, bool is_desktop)
|
||||
@@ -183,7 +177,10 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
||||
|
||||
if (!lossless)
|
||||
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s). The resulting shortcut may not work."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
ShortcutCreationDialog::EscapeShortcutCommandLine(&clean_path);
|
||||
std::string combined_args = StringUtil::JoinString(passed_cli_args.begin(), passed_cli_args.end(), " ");
|
||||
@@ -313,9 +310,6 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_flatpak) // Flatpak
|
||||
executable_path = "flatpak run net.pcsx2.PCSX2";
|
||||
|
||||
// Find home directory
|
||||
std::string link_path;
|
||||
const char* home = std::getenv("HOME");
|
||||
@@ -340,15 +334,30 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Home path is empty."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Path to the Home directory is empty."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
// Checks if a shortcut already exist
|
||||
if (FileSystem::FileExists(link_path.c_str()))
|
||||
// Copy PCSX2 icon
|
||||
std::string icon_dest;
|
||||
if (xdg_data_home)
|
||||
icon_dest = fmt::format("{}/icons/hicolor/512x512/apps/", xdg_data_home);
|
||||
else
|
||||
icon_dest = fmt::format("{}/.local/share/icons/hicolor/512x512/apps/", home);
|
||||
|
||||
std::string icon_name;
|
||||
if (is_flatpak) // Flatpak
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("A shortcut with the same name already exists."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
executable_path = "flatpak run net.pcsx2.PCSX2";
|
||||
icon_name = "net.pcsx2.PCSX2";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
icon_name = "PCSX2";
|
||||
std::string icon_path = fmt::format("{}/{}.png", icon_dest, icon_name).c_str();
|
||||
if (FileSystem::EnsureDirectoryExists(icon_dest.c_str(), true))
|
||||
FileSystem::CopyFilePath(Path::Combine(EmuFolders::Resources, "icons/AppIconLarge.png").c_str(), icon_path.c_str(), false);
|
||||
}
|
||||
|
||||
// Shortcut CmdLine Args
|
||||
@@ -357,25 +366,13 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
||||
|
||||
if (!lossless)
|
||||
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s). The resulting shortcut may not work."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cmdline = StringUtil::JoinString(passed_cli_args.begin(), passed_cli_args.end(), " ");
|
||||
|
||||
if (!is_flatpak)
|
||||
{
|
||||
// Copy PCSX2 icon
|
||||
std::string icon_dest;
|
||||
if (xdg_data_home)
|
||||
icon_dest = fmt::format("{}/icons/hicolor/512x512/apps/", xdg_data_home);
|
||||
else
|
||||
icon_dest = fmt::format("{}/.local/share/icons/hicolor/512x512/apps/", home);
|
||||
|
||||
std::string icon_name = "PCSX2.png";
|
||||
std::string icon_path = fmt::format("{}/{}", icon_dest, icon_name).c_str();
|
||||
if (FileSystem::EnsureDirectoryExists(icon_dest.c_str(), true))
|
||||
FileSystem::CopyFilePath(Path::Combine(EmuFolders::Resources, "icons/AppIconLarge.png").c_str(), icon_path.c_str(), false);
|
||||
}
|
||||
|
||||
// Further string sanitization
|
||||
if (!is_flatpak)
|
||||
ShortcutCreationDialog::EscapeShortcutCommandLine(&executable_path);
|
||||
@@ -393,7 +390,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
"StartupWMClass=PCSX2\n"
|
||||
"Exec=" + final_args + "\n"
|
||||
"Name=" + clean_name + "\n"
|
||||
"Icon=net.pcsx2.PCSX2\n"
|
||||
"Icon=" + icon_name + "\n"
|
||||
"Categories=Game;Emulator;\n";
|
||||
std::string_view sv(file_content);
|
||||
|
||||
|
||||
@@ -22,8 +22,9 @@ public:
|
||||
bool EscapeShortcutCommandLine(std::string* cmdline);
|
||||
|
||||
protected:
|
||||
QString m_title;
|
||||
QString m_path;
|
||||
bool m_desktop;
|
||||
const QString m_title;
|
||||
const QString m_path;
|
||||
|
||||
private:
|
||||
Ui::ShortcutCreationDialog m_ui;
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>685</height>
|
||||
<height>700</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="shortcutLayout">
|
||||
@@ -59,6 +59,9 @@
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="fullscreenModeDropdown">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Force Enable</string>
|
||||
@@ -82,10 +85,17 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" colspan="2">
|
||||
<widget class="QLineEdit" name="gameArgs"/>
|
||||
<widget class="QLineEdit" name="gameArgs">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2" colspan="2">
|
||||
<widget class="QComboBox" name="bootOptionDropdown">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Fast Boot</string>
|
||||
@@ -107,13 +117,20 @@
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QPushButton" name="overrideBootELFButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLineEdit" name="overrideBootELFPath"/>
|
||||
<widget class="QLineEdit" name="overrideBootELFPath">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="4">
|
||||
<widget class="QGroupBox" name="saveStateGroup">
|
||||
@@ -123,6 +140,9 @@
|
||||
<layout class="QGridLayout" name="savestateGridLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="loadStateIndex">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="showGroupSeparator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@@ -132,7 +152,11 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="loadStateFilePath"/>
|
||||
<widget class="QLineEdit" name="loadStateFilePath">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="loadStateNone">
|
||||
@@ -160,6 +184,9 @@
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="loadStateFileBrowse">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
@@ -194,7 +221,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="customArgsInstruction">
|
||||
<property name="text">
|
||||
<string>You may add additional (space-separated) <a href="https://pcsx2.net/docs/post/cli/">custom arguments</a> that are not listed above here:</string>
|
||||
<string>You may add additional (space-separated) <a href="https://pcsx2.net/docs/advanced/cli/">custom arguments</a> that are not listed above here:</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::TextFormat::RichText</enum>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@
|
||||
#include "common/MD5Digest.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/SettingsInterface.h"
|
||||
#include "common/SmallString.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
@@ -439,7 +440,25 @@ bool Achievements::Initialize()
|
||||
IdentifyGame(VMManager::GetDiscCRC(), VMManager::GetCurrentCRC());
|
||||
|
||||
const std::string username = Host::GetBaseStringSettingValue("Achievements", "Username");
|
||||
const std::string api_token = Host::GetBaseStringSettingValue("Achievements", "Token");
|
||||
|
||||
// Check the base settings file to see if the token is defined inside. Move if found.
|
||||
std::string oldToken = Host::GetBaseStringSettingValue("Achievements", "Token");
|
||||
if (!oldToken.empty())
|
||||
{
|
||||
auto secretsLock = Host::GetSecretsSettingsLock();
|
||||
SettingsInterface* secretsInterface = Host::Internal::GetSecretsSettingsLayer();
|
||||
secretsInterface->SetStringValue("Achievements", "Token", oldToken.c_str());
|
||||
secretsInterface->Save();
|
||||
|
||||
oldToken.clear();
|
||||
|
||||
auto baseLock = Host::GetSettingsLock();
|
||||
SettingsInterface* baseInterface = Host::Internal::GetBaseSettingsLayer();
|
||||
baseInterface->DeleteValue("Achievements", "Token");
|
||||
baseInterface->Save();
|
||||
}
|
||||
|
||||
const std::string api_token = Host::GetStringSettingValue("Achievements", "Token");
|
||||
if (!username.empty() && !api_token.empty())
|
||||
{
|
||||
Console.WriteLn("Achievements: Attempting login with user '%s'...", username.c_str());
|
||||
@@ -1785,9 +1804,12 @@ void Achievements::ClientLoginWithPasswordCallback(int result, const char* error
|
||||
|
||||
// Store configuration.
|
||||
Host::SetBaseStringSettingValue("Achievements", "Username", params->username);
|
||||
Host::SetBaseStringSettingValue("Achievements", "Token", user->token);
|
||||
Host::SetBaseStringSettingValue("Achievements", "LoginTimestamp", fmt::format("{}", std::time(nullptr)).c_str());
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
SettingsInterface* secretsInterface = Host::Internal::GetSecretsSettingsLayer();
|
||||
secretsInterface->SetStringValue("Achievements", "Token", user->token);
|
||||
secretsInterface->Save();
|
||||
|
||||
ShowLoginSuccess(client);
|
||||
}
|
||||
@@ -1887,9 +1909,13 @@ void Achievements::Logout()
|
||||
|
||||
Console.WriteLn("Achievements: Clearing credentials...");
|
||||
Host::RemoveBaseSettingValue("Achievements", "Username");
|
||||
Host::RemoveBaseSettingValue("Achievements", "Token");
|
||||
Host::RemoveBaseSettingValue("Achievements", "LoginTimestamp");
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
auto secretsLock = Host::GetSecretsSettingsLock();
|
||||
SettingsInterface* secretsInterface = Host::Internal::GetSecretsSettingsLayer();
|
||||
secretsInterface->DeleteValue("Achievements", "Token");
|
||||
secretsInterface->Save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
|
||||
std::vector<BreakPoint> CBreakPoints::breakPoints_;
|
||||
u32 CBreakPoints::breakSkipFirstAtEE_ = 0;
|
||||
u64 CBreakPoints::breakSkipFirstTicksEE_ = 0;
|
||||
u32 CBreakPoints::breakSkipFirstAtIop_ = 0;
|
||||
u64 CBreakPoints::breakSkipFirstTicksIop_ = 0;
|
||||
std::vector<MemCheck> CBreakPoints::memChecks_;
|
||||
std::vector<MemCheck*> CBreakPoints::cleanupMemChecks_;
|
||||
bool CBreakPoints::breakpointTriggered_ = false;
|
||||
@@ -393,20 +391,18 @@ void CBreakPoints::SetSkipFirst(BreakPointCpu cpu, u32 pc)
|
||||
if (cpu == BREAKPOINT_EE)
|
||||
{
|
||||
breakSkipFirstAtEE_ = standardizeBreakpointAddress(pc);
|
||||
breakSkipFirstTicksEE_ = r5900Debug.getCycles();
|
||||
}
|
||||
else if (cpu == BREAKPOINT_IOP)
|
||||
{
|
||||
breakSkipFirstAtIop_ = pc;
|
||||
breakSkipFirstTicksIop_ = r3000Debug.getCycles();
|
||||
}
|
||||
}
|
||||
|
||||
u32 CBreakPoints::CheckSkipFirst(BreakPointCpu cpu, u32 cmpPc)
|
||||
{
|
||||
if (cpu == BREAKPOINT_EE && breakSkipFirstTicksEE_ == r5900Debug.getCycles())
|
||||
if (cpu == BREAKPOINT_EE && breakSkipFirstAtEE_ == r5900Debug.getPC())
|
||||
return breakSkipFirstAtEE_;
|
||||
else if (cpu == BREAKPOINT_IOP && breakSkipFirstTicksIop_ == r3000Debug.getCycles())
|
||||
else if (cpu == BREAKPOINT_IOP && breakSkipFirstAtIop_ == r3000Debug.getPC())
|
||||
return breakSkipFirstAtIop_;
|
||||
return 0;
|
||||
}
|
||||
@@ -414,9 +410,7 @@ u32 CBreakPoints::CheckSkipFirst(BreakPointCpu cpu, u32 cmpPc)
|
||||
void CBreakPoints::ClearSkipFirst()
|
||||
{
|
||||
breakSkipFirstAtEE_ = 0;
|
||||
breakSkipFirstTicksEE_ = 0;
|
||||
breakSkipFirstAtIop_ = 0;
|
||||
breakSkipFirstTicksIop_ = 0;
|
||||
}
|
||||
|
||||
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()
|
||||
|
||||
@@ -439,7 +439,9 @@ GSRendererType D3D::GetPreferredRenderer()
|
||||
if (!feature_level.has_value())
|
||||
return GSRendererType::DX11;
|
||||
else if (feature_level == D3D_FEATURE_LEVEL_12_0)
|
||||
return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::DX11;
|
||||
return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::DX12;
|
||||
else if (feature_level == D3D_FEATURE_LEVEL_11_1)
|
||||
return GSRendererType::DX12;
|
||||
else
|
||||
return GSRendererType::DX11;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user