mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e38698cdb | ||
|
|
9aac7e8426 | ||
|
|
96284205a1 | ||
|
|
4a1d9d31d0 | ||
|
|
12d6087f2a | ||
|
|
251962c415 | ||
|
|
1bdd7d2352 | ||
|
|
7b98259ea1 | ||
|
|
ee8166d1fe | ||
|
|
43e073a18d | ||
|
|
bc41666d53 | ||
|
|
5b85d6a758 | ||
|
|
1fdc000815 | ||
|
|
3a57bb46ab | ||
|
|
2cb75e60b3 | ||
|
|
600ac6ec4f | ||
|
|
ed0cd628f8 | ||
|
|
33a825c17f | ||
|
|
660a165533 | ||
|
|
bfe2d5abb2 | ||
|
|
d5b36da6b0 | ||
|
|
328cebd5fc |
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
|
||||
|
||||
|
||||
14
.github/workflows/release_cut_new.yml
vendored
14
.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
|
||||
|
||||
|
||||
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"
|
||||
@@ -16672,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"
|
||||
@@ -17826,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"
|
||||
@@ -19949,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"
|
||||
@@ -20119,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"
|
||||
@@ -20883,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"
|
||||
@@ -25053,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"
|
||||
@@ -30130,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"
|
||||
@@ -66750,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"
|
||||
@@ -66973,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"
|
||||
@@ -74178,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"
|
||||
@@ -74977,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"
|
||||
|
||||
@@ -850,6 +850,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 +896,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,
|
||||
|
||||
@@ -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;
|
||||
@@ -1307,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);
|
||||
@@ -1347,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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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,9 @@
|
||||
|
||||
std::vector<BreakPoint> CBreakPoints::breakPoints_;
|
||||
u32 CBreakPoints::breakSkipFirstAtEE_ = 0;
|
||||
u64 CBreakPoints::breakSkipFirstTicksEE_ = 0;
|
||||
u32 CBreakPoints::breakSkipFirstAtIop_ = 0;
|
||||
u64 CBreakPoints::breakSkipFirstTicksIop_ = 0;
|
||||
bool CBreakPoints::pendingClearSkipFirstAtEE_ = false;
|
||||
bool CBreakPoints::pendingClearSkipFirstAtIop_ = false;
|
||||
std::vector<MemCheck> CBreakPoints::memChecks_;
|
||||
std::vector<MemCheck*> CBreakPoints::cleanupMemChecks_;
|
||||
bool CBreakPoints::breakpointTriggered_ = false;
|
||||
@@ -393,30 +393,47 @@ void CBreakPoints::SetSkipFirst(BreakPointCpu cpu, u32 pc)
|
||||
if (cpu == BREAKPOINT_EE)
|
||||
{
|
||||
breakSkipFirstAtEE_ = standardizeBreakpointAddress(pc);
|
||||
breakSkipFirstTicksEE_ = r5900Debug.getCycles();
|
||||
pendingClearSkipFirstAtEE_ = false;
|
||||
}
|
||||
else if (cpu == BREAKPOINT_IOP)
|
||||
{
|
||||
breakSkipFirstAtIop_ = pc;
|
||||
breakSkipFirstTicksIop_ = r3000Debug.getCycles();
|
||||
pendingClearSkipFirstAtIop_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void CBreakPoints::ClearSkipFirst()
|
||||
void CBreakPoints::ClearSkipFirst(BreakPointCpu cpu)
|
||||
{
|
||||
breakSkipFirstAtEE_ = 0;
|
||||
breakSkipFirstTicksEE_ = 0;
|
||||
breakSkipFirstAtIop_ = 0;
|
||||
breakSkipFirstTicksIop_ = 0;
|
||||
if((cpu & BREAKPOINT_EE) != 0)
|
||||
pendingClearSkipFirstAtEE_ = true;
|
||||
else if ((cpu & BREAKPOINT_IOP) != 0)
|
||||
pendingClearSkipFirstAtIop_ = true;
|
||||
|
||||
if(cpu == BREAKPOINT_IOP_AND_EE)
|
||||
CommitClearSkipFirst(BREAKPOINT_IOP_AND_EE);
|
||||
}
|
||||
|
||||
void CBreakPoints::CommitClearSkipFirst(BreakPointCpu cpu)
|
||||
{
|
||||
if((cpu & BREAKPOINT_EE) != 0 && pendingClearSkipFirstAtEE_)
|
||||
{
|
||||
pendingClearSkipFirstAtEE_ = false;
|
||||
breakSkipFirstAtEE_ = 0;
|
||||
}
|
||||
else if ((cpu & BREAKPOINT_IOP) != 0 && pendingClearSkipFirstAtIop_)
|
||||
{
|
||||
pendingClearSkipFirstAtIop_ = true;
|
||||
breakSkipFirstAtIop_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()
|
||||
|
||||
@@ -135,7 +135,8 @@ public:
|
||||
|
||||
static void SetSkipFirst(BreakPointCpu cpu, u32 pc);
|
||||
static u32 CheckSkipFirst(BreakPointCpu cpu, u32 pc);
|
||||
static void ClearSkipFirst();
|
||||
static void ClearSkipFirst(BreakPointCpu cpu = BREAKPOINT_IOP_AND_EE);
|
||||
static void CommitClearSkipFirst(BreakPointCpu cpu);
|
||||
|
||||
// Includes uncached addresses.
|
||||
static const std::vector<MemCheck> GetMemCheckRanges();
|
||||
@@ -169,9 +170,9 @@ private:
|
||||
|
||||
static std::vector<BreakPoint> breakPoints_;
|
||||
static u32 breakSkipFirstAtEE_;
|
||||
static u64 breakSkipFirstTicksEE_;
|
||||
static bool pendingClearSkipFirstAtEE_;
|
||||
static u32 breakSkipFirstAtIop_;
|
||||
static u64 breakSkipFirstTicksIop_;
|
||||
static bool pendingClearSkipFirstAtIop_;
|
||||
|
||||
static bool breakpointTriggered_;
|
||||
static BreakPointCpu breakpointTriggeredCpu_;
|
||||
|
||||
@@ -343,12 +343,14 @@ bool GSDevice12::CreateCommandLists()
|
||||
|
||||
void GSDevice12::MoveToNextCommandList()
|
||||
{
|
||||
m_current_command_list = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
|
||||
m_current_fence_value++;
|
||||
const int next_command_list = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
|
||||
|
||||
// We may have to wait if this command list hasn't finished on the GPU.
|
||||
CommandListResources& res = m_command_lists[m_current_command_list];
|
||||
CommandListResources& res = m_command_lists[next_command_list];
|
||||
WaitForFence(res.ready_fence_value, false);
|
||||
|
||||
m_current_command_list = next_command_list;
|
||||
m_current_fence_value++;
|
||||
res.ready_fence_value = m_current_fence_value;
|
||||
res.init_command_list_used = false;
|
||||
|
||||
@@ -2179,13 +2181,13 @@ void GSDevice12::IASetIndexBuffer(const void* index, size_t count)
|
||||
m_index_stream_buffer.CommitMemory(size);
|
||||
}
|
||||
|
||||
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor)
|
||||
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, bool depth_read)
|
||||
{
|
||||
GSTexture12* vkRt = static_cast<GSTexture12*>(rt);
|
||||
GSTexture12* vkDs = static_cast<GSTexture12*>(ds);
|
||||
pxAssert(vkRt || vkDs);
|
||||
|
||||
if (m_current_render_target != vkRt || m_current_depth_target != vkDs)
|
||||
if (m_current_render_target != vkRt || m_current_depth_target != vkDs || m_current_depth_read_only != depth_read)
|
||||
{
|
||||
// framebuffer change
|
||||
EndRenderPass();
|
||||
@@ -2212,13 +2214,14 @@ void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
|
||||
|
||||
m_current_render_target = vkRt;
|
||||
m_current_depth_target = vkDs;
|
||||
m_current_depth_read_only = depth_read;
|
||||
|
||||
if (!InRenderPass())
|
||||
{
|
||||
if (vkRt)
|
||||
vkRt->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
if (vkDs)
|
||||
vkDs->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
vkDs->TransitionToState(depth_read ? (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) : D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
}
|
||||
|
||||
// This is used to set/initialize the framebuffer for tfx rendering.
|
||||
@@ -3328,6 +3331,7 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
|
||||
{
|
||||
EndRenderPass();
|
||||
m_current_depth_target = nullptr;
|
||||
m_current_depth_read_only = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3448,7 +3452,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
D3D12_RENDER_PASS_DEPTH_STENCIL_DESC ds = {};
|
||||
if (m_current_depth_target)
|
||||
{
|
||||
ds.cpuDescriptor = m_current_depth_target->GetWriteDescriptor();
|
||||
ds.cpuDescriptor = m_current_depth_read_only ? m_current_depth_target->GetReadDepthViewDescriptor() : m_current_depth_target->GetWriteDescriptor();
|
||||
ds.DepthEndingAccess.Type = depth_end;
|
||||
ds.DepthBeginningAccess.Type = depth_begin;
|
||||
if (depth_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
|
||||
@@ -3468,7 +3472,8 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
}
|
||||
|
||||
GetCommandList()->BeginRenderPass(m_current_render_target ? 1 : 0,
|
||||
m_current_render_target ? &rt : nullptr, m_current_depth_target ? &ds : nullptr, D3D12_RENDER_PASS_FLAG_NONE);
|
||||
m_current_render_target ? &rt : nullptr, m_current_depth_target ? &ds : nullptr,
|
||||
(m_current_depth_target && m_current_depth_read_only) ? (D3D12_RENDER_PASS_FLAG_BIND_READ_ONLY_DEPTH) : D3D12_RENDER_PASS_FLAG_NONE);
|
||||
}
|
||||
|
||||
void GSDevice12::EndRenderPass()
|
||||
@@ -3550,11 +3555,13 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
|
||||
if (m_current_render_target)
|
||||
{
|
||||
cmdlist->OMSetRenderTargets(1, &m_current_render_target->GetWriteDescriptor().cpu_handle, FALSE,
|
||||
m_current_depth_target ? &m_current_depth_target->GetWriteDescriptor().cpu_handle : nullptr);
|
||||
m_current_depth_target ?
|
||||
(m_current_depth_read_only ? &m_current_depth_target->GetReadDepthViewDescriptor().cpu_handle : &m_current_depth_target->GetWriteDescriptor().cpu_handle) :
|
||||
nullptr);
|
||||
}
|
||||
else if (m_current_depth_target)
|
||||
{
|
||||
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, &m_current_depth_target->GetWriteDescriptor().cpu_handle);
|
||||
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, m_current_depth_read_only ? &m_current_depth_target->GetReadDepthViewDescriptor().cpu_handle : &m_current_depth_target->GetWriteDescriptor().cpu_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3855,6 +3862,18 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
// figure out the pipeline
|
||||
UpdateHWPipelineSelector(config);
|
||||
|
||||
// Handle RT hazard when no barrier was requested
|
||||
if (m_features.texture_barrier && config.tex && (config.tex == config.rt) && !(config.require_one_barrier || config.require_full_barrier))
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {draw_rt->GetResource(), nullptr};
|
||||
GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
// now blit the colclip texture back to the original target
|
||||
if (colclip_rt)
|
||||
{
|
||||
@@ -3910,15 +3929,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
if (config.blend.constant_enable)
|
||||
SetBlendConstants(config.blend.constant);
|
||||
|
||||
// Depth testing and sampling, bind resource as dsv read only and srv at the same time without the need of a copy.
|
||||
if (config.tex && config.tex == config.ds)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
// Transition dsv as read only.
|
||||
draw_ds->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_READ);
|
||||
}
|
||||
|
||||
// Primitive ID tracking DATE setup.
|
||||
GSTexture12* date_image = nullptr;
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
|
||||
@@ -4020,7 +4030,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
Console.Warning("D3D12: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
|
||||
// For depth testing and sampling, use a read only dsv, otherwise use a write dsv
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, config.tex && config.tex == config.ds);
|
||||
|
||||
// Begin render pass if new target or out of the area.
|
||||
if (!m_in_render_pass)
|
||||
|
||||
@@ -459,7 +459,7 @@ public:
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback = false);
|
||||
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
|
||||
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, bool depth_read = false);
|
||||
|
||||
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
|
||||
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
|
||||
@@ -580,6 +580,7 @@ private:
|
||||
|
||||
GSTexture12* m_current_render_target = nullptr;
|
||||
GSTexture12* m_current_depth_target = nullptr;
|
||||
bool m_current_depth_read_only = false;
|
||||
|
||||
D3D12_VIEWPORT m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||
GSVector4i m_scissor = GSVector4i::zero();
|
||||
|
||||
@@ -17,13 +17,15 @@
|
||||
GSTexture12::GSTexture12(Type type, Format format, int width, int height, int levels, DXGI_FORMAT dxgi_format,
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<ID3D12Resource> resource_fbl,
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& uav_descriptor,
|
||||
const D3D12DescriptorHandle& fbl_descriptor, WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& ro_dsv_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, const D3D12DescriptorHandle& fbl_descriptor,
|
||||
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
|
||||
: m_resource(std::move(resource))
|
||||
, m_resource_fbl(std::move(resource_fbl))
|
||||
, m_allocation(std::move(allocation))
|
||||
, m_srv_descriptor(srv_descriptor)
|
||||
, m_write_descriptor(write_descriptor)
|
||||
, m_read_dsv_descriptor(ro_dsv_descriptor)
|
||||
, m_uav_descriptor(uav_descriptor)
|
||||
, m_fbl_descriptor(fbl_descriptor)
|
||||
, m_write_descriptor_type(wdtype)
|
||||
@@ -58,6 +60,7 @@ void GSTexture12::Destroy(bool defer)
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->DeferDescriptorDestruction(dev->GetDSVHeapManager(), &m_write_descriptor);
|
||||
dev->DeferDescriptorDestruction(dev->GetDSVHeapManager(), &m_read_dsv_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::None:
|
||||
default:
|
||||
@@ -87,6 +90,7 @@ void GSTexture12::Destroy(bool defer)
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->GetDSVHeapManager().Free(&m_write_descriptor);
|
||||
dev->GetDSVHeapManager().Free(&m_read_dsv_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::None:
|
||||
default:
|
||||
@@ -237,7 +241,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
}
|
||||
}
|
||||
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor, fbl_descriptor;
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor, fbl_descriptor;
|
||||
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
|
||||
if (srv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
@@ -252,7 +256,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
write_descriptor_type = WriteDescriptorType::RTV;
|
||||
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
|
||||
{
|
||||
dev->GetRTVHeapManager().Free(&srv_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -261,9 +265,15 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
case Type::DepthStencil:
|
||||
{
|
||||
write_descriptor_type = WriteDescriptorType::DSV;
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor, false))
|
||||
{
|
||||
dev->GetDSVHeapManager().Free(&srv_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &ro_dsv_descriptor, true))
|
||||
{
|
||||
dev->GetDSVHeapManager().Free(&write_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -275,7 +285,18 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), dsv_format, &uav_descriptor))
|
||||
{
|
||||
dev->GetDescriptorHeapManager().Free(&write_descriptor);
|
||||
switch (write_descriptor_type)
|
||||
{
|
||||
case WriteDescriptorType::RTV:
|
||||
dev->GetRTVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->GetDSVHeapManager().Free(&ro_dsv_descriptor);
|
||||
dev->GetDSVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
@@ -285,7 +306,18 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
if (!CreateSRVDescriptor(resource_fbl.get(), levels, srv_format, &fbl_descriptor))
|
||||
{
|
||||
dev->GetDescriptorHeapManager().Free(&uav_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&write_descriptor);
|
||||
switch (write_descriptor_type)
|
||||
{
|
||||
case WriteDescriptorType::RTV:
|
||||
dev->GetRTVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->GetDSVHeapManager().Free(&ro_dsv_descriptor);
|
||||
dev->GetDSVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
@@ -293,7 +325,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
|
||||
return std::unique_ptr<GSTexture12>(
|
||||
new GSTexture12(type, format, width, height, levels, dxgi_format, std::move(resource), std::move(resource_fbl), std::move(allocation),
|
||||
srv_descriptor, write_descriptor, uav_descriptor, fbl_descriptor, write_descriptor_type, state));
|
||||
srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor, fbl_descriptor, write_descriptor_type, state));
|
||||
}
|
||||
|
||||
std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resource> resource, Type type, Format format,
|
||||
@@ -302,7 +334,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
{
|
||||
const D3D12_RESOURCE_DESC desc = resource->GetDesc();
|
||||
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor;
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor;
|
||||
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
|
||||
if (srv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
@@ -315,16 +347,22 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
write_descriptor_type = WriteDescriptorType::RTV;
|
||||
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetRTVHeapManager().Free(&srv_descriptor);
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (type == Type::DepthStencil)
|
||||
{
|
||||
write_descriptor_type = WriteDescriptorType::DSV;
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor, false))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&srv_descriptor);
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &ro_dsv_descriptor, true))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&write_descriptor);
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -333,14 +371,26 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
{
|
||||
if (!CreateUAVDescriptor(resource.get(), srv_format, &uav_descriptor))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&write_descriptor);
|
||||
switch (write_descriptor_type)
|
||||
{
|
||||
case WriteDescriptorType::RTV:
|
||||
GSDevice12::GetInstance()->GetRTVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&ro_dsv_descriptor);
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<GSTexture12>(new GSTexture12(type, format, static_cast<u32>(desc.Width), desc.Height,
|
||||
desc.MipLevels, desc.Format, std::move(resource), {}, {}, srv_descriptor, write_descriptor, uav_descriptor,
|
||||
desc.MipLevels, desc.Format, std::move(resource), {}, {}, srv_descriptor, write_descriptor, {}, uav_descriptor,
|
||||
{}, write_descriptor_type, resource_state));
|
||||
}
|
||||
|
||||
@@ -365,7 +415,7 @@ bool GSTexture12::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT form
|
||||
{
|
||||
if (!GSDevice12::GetInstance()->GetRTVHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate SRV descriptor");
|
||||
Console.Error("Failed to allocate RTV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -374,15 +424,15 @@ bool GSTexture12::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT form
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSTexture12::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
|
||||
bool GSTexture12::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh, bool read_only)
|
||||
{
|
||||
if (!GSDevice12::GetInstance()->GetDSVHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate SRV descriptor");
|
||||
Console.Error("Failed to allocate DSV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE};
|
||||
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, read_only ? D3D12_DSV_FLAG_READ_ONLY_DEPTH : D3D12_DSV_FLAG_NONE };
|
||||
GSDevice12::GetInstance()->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
|
||||
return true;
|
||||
}
|
||||
@@ -677,7 +727,36 @@ void GSTexture12::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RE
|
||||
if (m_resource_state == state)
|
||||
return;
|
||||
|
||||
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_resource_state, state);
|
||||
// Read only depth requires special handling as we might want to write stencil.
|
||||
// Also batch the transition barriers as per recommendation from docs.
|
||||
if (state == (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
// Transition to read depth/write stencil
|
||||
const D3D12_RESOURCE_BARRIER barriers[2] = {
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 0, m_resource_state, (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)}}},
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 1, m_resource_state, D3D12_RESOURCE_STATE_DEPTH_WRITE}}},
|
||||
};
|
||||
GSDevice12::GetInstance()->GetCommandList()->ResourceBarrier(m_resource_state == D3D12_RESOURCE_STATE_DEPTH_WRITE ? 1 : 2, barriers);
|
||||
}
|
||||
else if (m_resource_state == (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
// Transition from read depth/write stencil
|
||||
const D3D12_RESOURCE_BARRIER barriers[2] = {
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 0, (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE), state}}},
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 1, D3D12_RESOURCE_STATE_DEPTH_WRITE, state}}},
|
||||
};
|
||||
GSDevice12::GetInstance()->GetCommandList()->ResourceBarrier(state == D3D12_RESOURCE_STATE_DEPTH_WRITE ? 1 : 2, barriers);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal transition
|
||||
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_resource_state, state);
|
||||
}
|
||||
|
||||
m_resource_state = state;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
|
||||
__fi const D3D12DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetReadDepthViewDescriptor() const { return m_read_dsv_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetUAVDescriptor() const { return m_uav_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetFBLDescriptor() const { return m_fbl_descriptor; }
|
||||
__fi D3D12_RESOURCE_STATES GetResourceState() const { return m_resource_state; }
|
||||
@@ -72,13 +73,14 @@ private:
|
||||
GSTexture12(Type type, Format format, int width, int height, int levels, DXGI_FORMAT dxgi_format,
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<ID3D12Resource> resource_fbl,
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& uav_descriptor,
|
||||
const D3D12DescriptorHandle& fbl_descriptor, WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state);
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& ro_dsv_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, const D3D12DescriptorHandle& fbl_descriptor,
|
||||
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state);
|
||||
|
||||
static bool CreateSRVDescriptor(
|
||||
ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh, bool read_only);
|
||||
static bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
|
||||
ID3D12GraphicsCommandList* GetCommandBufferForUpdate();
|
||||
@@ -91,6 +93,7 @@ private:
|
||||
|
||||
D3D12DescriptorHandle m_srv_descriptor = {};
|
||||
D3D12DescriptorHandle m_write_descriptor = {};
|
||||
D3D12DescriptorHandle m_read_dsv_descriptor = {};
|
||||
D3D12DescriptorHandle m_uav_descriptor = {};
|
||||
D3D12DescriptorHandle m_fbl_descriptor = {};
|
||||
WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None;
|
||||
|
||||
@@ -8037,6 +8037,14 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
m_conf.drawarea = m_channel_shuffle ? scissor : scissor.rintersect(ComputeBoundingBox(rtsize, rtscale));
|
||||
m_conf.scissor = (DATE && !DATE_BARRIER) ? m_conf.drawarea : scissor;
|
||||
|
||||
// ComputeDrawlistGetSize expects the original index layout, so needs to be called before we modify it via HandleProvokingVertexFirst/SetupIA.
|
||||
if (m_conf.require_full_barrier && (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy))
|
||||
{
|
||||
ComputeDrawlistGetSize(rt->m_scale);
|
||||
m_conf.drawlist = &m_drawlist;
|
||||
m_conf.drawlist_bbox = &m_drawlist_bbox;
|
||||
}
|
||||
|
||||
HandleProvokingVertexFirst();
|
||||
|
||||
SetupIA(rtscale, sx, sy, m_channel_shuffle_width != 0);
|
||||
@@ -8125,13 +8133,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
m_conf.alpha_second_pass.enable = false;
|
||||
}
|
||||
|
||||
if (m_conf.require_full_barrier && (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy))
|
||||
{
|
||||
ComputeDrawlistGetSize(rt->m_scale);
|
||||
m_conf.drawlist = &m_drawlist;
|
||||
m_conf.drawlist_bbox = &m_drawlist_bbox;
|
||||
}
|
||||
|
||||
if (!m_channel_shuffle_width)
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
else
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Host
|
||||
const std::string_view context, const std::string_view msg);
|
||||
|
||||
static std::mutex s_settings_mutex;
|
||||
static std::mutex s_secrets_settings_mutex;
|
||||
static LayeredSettingsInterface s_layered_settings_interface;
|
||||
|
||||
static constexpr u32 TRANSLATION_STRING_CACHE_SIZE = 4 * 1024 * 1024;
|
||||
@@ -164,6 +165,11 @@ std::unique_lock<std::mutex> Host::GetSettingsLock()
|
||||
return std::unique_lock<std::mutex>(s_settings_mutex);
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> Host::GetSecretsSettingsLock()
|
||||
{
|
||||
return std::unique_lock<std::mutex>(s_secrets_settings_mutex);
|
||||
}
|
||||
|
||||
SettingsInterface* Host::GetSettingsInterface()
|
||||
{
|
||||
return &s_layered_settings_interface;
|
||||
@@ -352,6 +358,11 @@ SettingsInterface* Host::Internal::GetBaseSettingsLayer()
|
||||
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE);
|
||||
}
|
||||
|
||||
SettingsInterface* Host::Internal::GetSecretsSettingsLayer()
|
||||
{
|
||||
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_SECRETS);
|
||||
}
|
||||
|
||||
SettingsInterface* Host::Internal::GetGameSettingsLayer()
|
||||
{
|
||||
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_GAME);
|
||||
@@ -365,10 +376,17 @@ SettingsInterface* Host::Internal::GetInputSettingsLayer()
|
||||
void Host::Internal::SetBaseSettingsLayer(SettingsInterface* sif)
|
||||
{
|
||||
pxAssertRel(s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE) == nullptr,
|
||||
"Base layer has not been set");
|
||||
"Base layer has already been set");
|
||||
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_BASE, sif);
|
||||
}
|
||||
|
||||
void Host::Internal::SetSecretsSettingsLayer(SettingsInterface* sif)
|
||||
{
|
||||
pxAssertRel(s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_SECRETS) == nullptr,
|
||||
"Secrets layer has already been set");
|
||||
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_SECRETS, sif);
|
||||
}
|
||||
|
||||
void Host::Internal::SetGameSettingsLayer(SettingsInterface* sif, std::unique_lock<std::mutex>& settings_lock)
|
||||
{
|
||||
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_GAME, sif);
|
||||
|
||||
@@ -134,6 +134,8 @@ namespace Host
|
||||
|
||||
/// Direct access to settings interface. Must hold the lock when calling GetSettingsInterface() and while using it.
|
||||
std::unique_lock<std::mutex> GetSettingsLock();
|
||||
/// Ditto for secrets file.
|
||||
std::unique_lock<std::mutex> GetSecretsSettingsLock();
|
||||
SettingsInterface* GetSettingsInterface();
|
||||
|
||||
/// Sets host-specific default settings.
|
||||
@@ -147,6 +149,9 @@ namespace Host
|
||||
/// Retrieves the base settings layer. Must call with lock held.
|
||||
SettingsInterface* GetBaseSettingsLayer();
|
||||
|
||||
/// Retrieves the base settings layer. Must call with lock held.
|
||||
SettingsInterface* GetSecretsSettingsLayer();
|
||||
|
||||
/// Retrieves the game settings layer, if present. Must call with lock held.
|
||||
SettingsInterface* GetGameSettingsLayer();
|
||||
|
||||
@@ -156,6 +161,9 @@ namespace Host
|
||||
/// Sets the base settings layer. Should be called by the host at initialization time.
|
||||
void SetBaseSettingsLayer(SettingsInterface* sif);
|
||||
|
||||
/// Sets the secrets settings layer. Should follow call to SetBaseSettingsLayer.
|
||||
void SetSecretsSettingsLayer(SettingsInterface* sif);
|
||||
|
||||
/// Sets the game settings layer. Called by VMManager when the game changes.
|
||||
void SetGameSettingsLayer(SettingsInterface* sif, std::unique_lock<std::mutex>& settings_lock);
|
||||
|
||||
|
||||
@@ -3458,8 +3458,11 @@ void FullscreenUI::StartAutomaticBinding(u32 port)
|
||||
names.reserve(devices.size());
|
||||
for (auto& [name, display_name] : devices)
|
||||
{
|
||||
if(!StringUtil::compareNoCase(name, display_name))
|
||||
options.emplace_back(fmt::format("{}: {}", name, display_name), false);
|
||||
else
|
||||
options.emplace_back(std::move(display_name), false);
|
||||
names.push_back(std::move(name));
|
||||
options.emplace_back(std::move(display_name), false);
|
||||
}
|
||||
OpenChoiceDialog(FSUI_CSTR("Select Device"), false, std::move(options),
|
||||
[port, names = std::move(names)](s32 index, const std::string& title, bool checked) {
|
||||
|
||||
@@ -67,7 +67,10 @@ void intBreakpoint(bool memcheck)
|
||||
{
|
||||
const u32 pc = cpuRegs.pc;
|
||||
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0)
|
||||
{
|
||||
CBreakPoints::ClearSkipFirst(BREAKPOINT_EE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!memcheck)
|
||||
{
|
||||
@@ -161,6 +164,8 @@ static void execI()
|
||||
intBreakpoint(false);
|
||||
|
||||
intCheckMemcheck();
|
||||
|
||||
CBreakPoints::CommitClearSkipFirst(BREAKPOINT_EE);
|
||||
#endif
|
||||
|
||||
const u32 pc = cpuRegs.pc;
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
LAYER_CMDLINE,
|
||||
LAYER_GAME,
|
||||
LAYER_INPUT,
|
||||
LAYER_SECRETS,
|
||||
LAYER_BASE,
|
||||
NUM_LAYERS
|
||||
};
|
||||
|
||||
@@ -120,7 +120,10 @@ void psxBreakpoint(bool memcheck)
|
||||
{
|
||||
u32 pc = psxRegs.pc;
|
||||
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) != 0)
|
||||
{
|
||||
CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!memcheck)
|
||||
{
|
||||
@@ -208,6 +211,8 @@ static __fi void execI()
|
||||
psxBreakpoint(false);
|
||||
|
||||
psxCheckMemcheck();
|
||||
|
||||
CBreakPoints::CommitClearSkipFirst(BREAKPOINT_IOP);
|
||||
#endif
|
||||
|
||||
// Inject IRX hack
|
||||
|
||||
@@ -1259,7 +1259,10 @@ static bool psxDynarecCheckBreakpoint()
|
||||
{
|
||||
u32 pc = psxRegs.pc;
|
||||
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc)
|
||||
{
|
||||
CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP);
|
||||
return false;
|
||||
}
|
||||
|
||||
int bpFlags = psxIsBreakpointNeeded(pc);
|
||||
bool hit = false;
|
||||
@@ -1299,8 +1302,10 @@ static bool psxDynarecMemcheck(size_t i)
|
||||
auto mc = CBreakPoints::GetMemChecks(BREAKPOINT_IOP)[i];
|
||||
|
||||
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc)
|
||||
{
|
||||
CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP);
|
||||
return false;
|
||||
|
||||
}
|
||||
if (mc.hasCond)
|
||||
{
|
||||
if (!mc.cond.Evaluate())
|
||||
@@ -1373,7 +1378,7 @@ static void psxRecMemcheck(u32 op, u32 bits, bool store)
|
||||
}
|
||||
}
|
||||
|
||||
static void psxEncodeBreakpoint()
|
||||
static bool psxEncodeBreakpoint()
|
||||
{
|
||||
if (psxIsBreakpointNeeded(psxpc) != 0)
|
||||
{
|
||||
@@ -1381,14 +1386,17 @@ static void psxEncodeBreakpoint()
|
||||
xFastCall((void*)psxDynarecCheckBreakpoint);
|
||||
xTEST(al, al);
|
||||
xJNZ(iopExitRecompiledCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void psxEncodeMemcheck()
|
||||
static bool psxEncodeMemcheck()
|
||||
{
|
||||
int needed = psxIsMemcheckNeeded(psxpc);
|
||||
if (needed == 0)
|
||||
return;
|
||||
return false;
|
||||
|
||||
u32 op = iopMemRead32(needed == 2 ? psxpc + 4 : psxpc);
|
||||
const R5900::OPCODE& opcode = R5900::GetInstruction(op);
|
||||
@@ -1409,6 +1417,7 @@ static void psxEncodeMemcheck()
|
||||
psxRecMemcheck(op, 64, store);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void psxRecompileNextInstruction(bool delayslot, bool swapped_delayslot)
|
||||
@@ -1437,12 +1446,10 @@ void psxRecompileNextInstruction(bool delayslot, bool swapped_delayslot)
|
||||
EEINST* old_inst_info = g_pCurInstInfo;
|
||||
s_recompilingDelaySlot = delayslot;
|
||||
|
||||
// add breakpoint
|
||||
if (!delayslot)
|
||||
{
|
||||
// Broken on x64
|
||||
psxEncodeBreakpoint();
|
||||
psxEncodeMemcheck();
|
||||
if(psxEncodeBreakpoint() || psxEncodeMemcheck())
|
||||
xFastCall((void*)CBreakPoints::CommitClearSkipFirst, BREAKPOINT_IOP);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1498,7 +1498,10 @@ void dynarecCheckBreakpoint()
|
||||
{
|
||||
u32 pc = cpuRegs.pc;
|
||||
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0)
|
||||
{
|
||||
CBreakPoints::ClearSkipFirst(BREAKPOINT_EE);
|
||||
return;
|
||||
}
|
||||
|
||||
const int bpFlags = isBreakpointNeeded(pc);
|
||||
bool hit = false;
|
||||
@@ -1532,7 +1535,10 @@ void dynarecMemcheck(size_t i)
|
||||
const u32 op = memRead32(cpuRegs.pc);
|
||||
const OPCODE& opcode = GetInstruction(op);
|
||||
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0)
|
||||
{
|
||||
CBreakPoints::ClearSkipFirst(BREAKPOINT_EE);
|
||||
return;
|
||||
}
|
||||
|
||||
auto mc = CBreakPoints::GetMemChecks(BREAKPOINT_EE)[i];
|
||||
|
||||
@@ -1606,20 +1612,22 @@ void recMemcheck(u32 op, u32 bits, bool store)
|
||||
}
|
||||
}
|
||||
|
||||
void encodeBreakpoint()
|
||||
bool encodeBreakpoint()
|
||||
{
|
||||
if (isBreakpointNeeded(pc) != 0)
|
||||
{
|
||||
iFlushCall(FLUSH_EVERYTHING | FLUSH_PC);
|
||||
xFastCall((void*)dynarecCheckBreakpoint);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void encodeMemcheck()
|
||||
bool encodeMemcheck()
|
||||
{
|
||||
const int needed = isMemcheckNeeded(pc);
|
||||
if (needed == 0)
|
||||
return;
|
||||
return false;
|
||||
|
||||
const u32 op = memRead32(needed == 2 ? pc + 4 : pc);
|
||||
const OPCODE& opcode = GetInstruction(op);
|
||||
@@ -1643,6 +1651,7 @@ void encodeMemcheck()
|
||||
recMemcheck(op, 128, store);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void recompileNextInstruction(bool delayslot, bool swapped_delay_slot)
|
||||
@@ -1653,8 +1662,8 @@ void recompileNextInstruction(bool delayslot, bool swapped_delay_slot)
|
||||
// add breakpoint
|
||||
if (!delayslot)
|
||||
{
|
||||
encodeBreakpoint();
|
||||
encodeMemcheck();
|
||||
if(encodeBreakpoint() || encodeMemcheck())
|
||||
xFastCall((void*)CBreakPoints::CommitClearSkipFirst, BREAKPOINT_EE);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user