Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
|
21b474ec30 | ||
|
8966998868 | ||
|
8b8f8c7a3c | ||
|
24844a6fb6 | ||
|
40d0448ef1 | ||
|
f947e64f26 | ||
|
a81a6d4b21 | ||
|
a902189c56 | ||
|
1abe1f703e | ||
|
041d0ee717 | ||
|
4d11665646 | ||
|
f7faa08705 | ||
|
afdaf5a12e | ||
|
abbd24581e | ||
|
31a4ffff0e | ||
|
290631a28e | ||
|
4b4cddd769 | ||
|
634286945b | ||
|
70ac433c23 |
40 changed files with 1457 additions and 2286 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -46,4 +46,4 @@
|
|||
url = https://github.com/google/cpu_features.git
|
||||
[submodule "ext/rcheevos"]
|
||||
path = ext/rcheevos
|
||||
url = https://github.com/RetroAchievements/rcheevos.git
|
||||
url = https://github.com/Jamiras/rcheevos.git
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Data/Collections/FastVec.h"
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/GPU/GPUBackendCommon.h"
|
||||
|
||||
|
@ -205,4 +206,3 @@ private:
|
|||
uint32_t usage_ = 0;
|
||||
bool grow_;
|
||||
};
|
||||
|
||||
|
|
|
@ -69,6 +69,9 @@ struct Bounds {
|
|||
Bounds Expand(float xAmount, float yAmount) const {
|
||||
return Bounds(x - xAmount, y - yAmount, w + xAmount * 2, h + yAmount * 2);
|
||||
}
|
||||
Bounds Expand(float left, float top, float right, float bottom) const {
|
||||
return Bounds(x - left, y - top, w + left + right, h + top + bottom);
|
||||
}
|
||||
Bounds Offset(float xAmount, float yAmount) const {
|
||||
return Bounds(x + xAmount, y + yAmount, w, h);
|
||||
}
|
||||
|
|
|
@ -147,6 +147,11 @@ public:
|
|||
|
||||
void DoMarker(const char *prevName, u32 arbitraryNumber = 0x42);
|
||||
|
||||
void SkipBytes(size_t bytes) {
|
||||
// Should work in all modes.
|
||||
*ptr += bytes;
|
||||
}
|
||||
|
||||
size_t Offset() const { return *ptr - ptrStart_; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "Common/System/OSD.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
OnScreenDisplay g_OSD;
|
||||
|
||||
|
@ -17,6 +18,14 @@ void OnScreenDisplay::Update() {
|
|||
}
|
||||
}
|
||||
|
||||
for (auto iter = sideEntries_.begin(); iter != sideEntries_.end(); ) {
|
||||
if (now >= iter->endTime) {
|
||||
iter = sideEntries_.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = bars_.begin(); iter != bars_.end(); ) {
|
||||
if (now >= iter->endTime) {
|
||||
iter = bars_.erase(iter);
|
||||
|
@ -31,6 +40,11 @@ std::vector<OnScreenDisplay::Entry> OnScreenDisplay::Entries() {
|
|||
return entries_; // makes a copy.
|
||||
}
|
||||
|
||||
std::vector<OnScreenDisplay::Entry> OnScreenDisplay::SideEntries() {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return sideEntries_; // makes a copy.
|
||||
}
|
||||
|
||||
std::vector<OnScreenDisplay::ProgressBar> OnScreenDisplay::ProgressBars() {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return bars_; // makes a copy.
|
||||
|
@ -99,6 +113,88 @@ void OnScreenDisplay::ShowAchievementUnlocked(int achievementID) {
|
|||
entries_.insert(entries_.begin(), msg);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::ShowAchievementProgress(int achievementID, float duration_s) {
|
||||
double now = time_now_d();
|
||||
|
||||
for (auto &entry : sideEntries_) {
|
||||
if (entry.numericID == achievementID && entry.type == OSDType::ACHIEVEMENT_PROGRESS) {
|
||||
// Duplicate, let's just bump the timer.
|
||||
entry.startTime = now;
|
||||
entry.endTime = now + (double)duration_s;
|
||||
// We're done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// OK, let's make a new side-entry.
|
||||
Entry entry;
|
||||
entry.numericID = achievementID;
|
||||
entry.type = OSDType::ACHIEVEMENT_PROGRESS;
|
||||
entry.startTime = now;
|
||||
entry.endTime = now + (double)duration_s;
|
||||
sideEntries_.insert(sideEntries_.begin(), entry);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::ShowChallengeIndicator(int achievementID, bool show) {
|
||||
double now = time_now_d();
|
||||
|
||||
for (auto &entry : sideEntries_) {
|
||||
if (entry.numericID == achievementID && entry.type == OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR && !show) {
|
||||
// Hide and eventually delete it.
|
||||
entry.endTime = now + (double)FadeoutTime();
|
||||
// Found it, we're done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!show) {
|
||||
// Sanity check
|
||||
return;
|
||||
}
|
||||
|
||||
// OK, let's make a new side-entry.
|
||||
Entry entry;
|
||||
entry.numericID = achievementID;
|
||||
entry.type = OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR;
|
||||
entry.startTime = now;
|
||||
entry.endTime = now + 10000000.0; // Don't auto-fadeout.
|
||||
sideEntries_.insert(sideEntries_.begin(), entry);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::ShowLeaderboardTracker(int leaderboardTrackerID, const char *trackerText, bool show) { // show=true is used both for create and update.
|
||||
double now = time_now_d();
|
||||
|
||||
for (auto &entry : sideEntries_) {
|
||||
if (entry.numericID == leaderboardTrackerID && entry.type == OSDType::LEADERBOARD_TRACKER) {
|
||||
if (show) {
|
||||
// Just an update.
|
||||
entry.text = trackerText;
|
||||
} else {
|
||||
// Keep the current text, hide and eventually delete it.
|
||||
entry.endTime = now + (double)FadeoutTime();
|
||||
}
|
||||
// Found it, we're done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!show) {
|
||||
// Sanity check
|
||||
return;
|
||||
}
|
||||
|
||||
// OK, let's make a new side-entry.
|
||||
Entry entry;
|
||||
entry.numericID = leaderboardTrackerID;
|
||||
entry.type = OSDType::LEADERBOARD_TRACKER;
|
||||
entry.startTime = now;
|
||||
entry.endTime = now + 10000000.0; // Don't auto-fadeout
|
||||
if (trackerText) {
|
||||
entry.text = trackerText;
|
||||
}
|
||||
sideEntries_.insert(sideEntries_.begin(), entry);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::ShowOnOff(const std::string &message, bool on, float duration_s) {
|
||||
// TODO: translate "on" and "off"? Or just get rid of this whole thing?
|
||||
Show(OSDType::MESSAGE_INFO, message + ": " + (on ? "on" : "off"), duration_s);
|
||||
|
@ -129,12 +225,12 @@ void OnScreenDisplay::SetProgressBar(std::string id, std::string &&message, int
|
|||
bars_.push_back(bar);
|
||||
}
|
||||
|
||||
void OnScreenDisplay::RemoveProgressBar(std::string id, float fadeout_s) {
|
||||
void OnScreenDisplay::RemoveProgressBar(std::string id) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
for (auto iter = bars_.begin(); iter != bars_.end(); iter++) {
|
||||
if (iter->id == id) {
|
||||
iter->progress = iter->maxValue;
|
||||
iter->endTime = time_now_d() + (double)fadeout_s;
|
||||
iter->endTime = time_now_d() + FadeoutTime();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@ enum class OSDType {
|
|||
|
||||
ACHIEVEMENT_UNLOCKED,
|
||||
|
||||
// PROGRESS_BAR,
|
||||
// PROGRESS_INDETERMINATE,
|
||||
// Side entries
|
||||
ACHIEVEMENT_PROGRESS, // Achievement icon + "measured_progress" text, auto-hide after 2s
|
||||
ACHIEVEMENT_CHALLENGE_INDICATOR, // Achievement icon ONLY, no auto-hide
|
||||
LEADERBOARD_TRACKER,
|
||||
};
|
||||
|
||||
// Data holder for on-screen messages.
|
||||
|
@ -31,7 +33,6 @@ public:
|
|||
Show(type, text, text2, "", duration_s, id);
|
||||
}
|
||||
void Show(OSDType type, const std::string &text, const std::string &text2, const std::string &icon, float duration_s = 0.0f, const char *id = nullptr);
|
||||
void ShowAchievementUnlocked(int achievementID);
|
||||
|
||||
void ShowOnOff(const std::string &message, bool on, float duration_s = 0.0f);
|
||||
|
||||
|
@ -40,10 +41,17 @@ public:
|
|||
// Call this every frame, cleans up old entries.
|
||||
void Update();
|
||||
|
||||
// Specialized achievement-related types. These go to the side notifications, not the top-middle.
|
||||
void ShowAchievementUnlocked(int achievementID);
|
||||
void ShowAchievementProgress(int achievementID, float duration_s);
|
||||
void ShowChallengeIndicator(int achievementID, bool show); // call with show=false to hide.
|
||||
void ShowLeaderboardTracker(int leaderboardTrackerID, const char *trackerText, bool show); // show=true is used both for create and update.
|
||||
|
||||
// Progress bar controls
|
||||
// Set is both create and update.
|
||||
// Set is both create and update. If you set maxValue <= minValue, you'll create an "indeterminate" progress
|
||||
// bar that doesn't show a specific amount of progress.
|
||||
void SetProgressBar(std::string id, std::string &&message, int minValue, int maxValue, int progress);
|
||||
void RemoveProgressBar(std::string id, float fadeout_s);
|
||||
void RemoveProgressBar(std::string id);
|
||||
|
||||
struct Entry {
|
||||
OSDType type;
|
||||
|
@ -66,10 +74,14 @@ public:
|
|||
};
|
||||
|
||||
std::vector<Entry> Entries();
|
||||
std::vector<Entry> SideEntries();
|
||||
std::vector<ProgressBar> ProgressBars();
|
||||
|
||||
static float FadeoutTime() { return 0.25f; }
|
||||
|
||||
private:
|
||||
std::vector<Entry> entries_;
|
||||
std::vector<Entry> sideEntries_;
|
||||
std::vector<ProgressBar> bars_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
|
|
@ -313,6 +313,17 @@ void UIContext::FillRect(const UI::Drawable &drawable, const Bounds &bounds) {
|
|||
}
|
||||
}
|
||||
|
||||
void UIContext::DrawRectDropShadow(const Bounds &bounds, float radius, float alpha, uint32_t color) {
|
||||
if (alpha <= 0.0f)
|
||||
return;
|
||||
|
||||
color = colorAlpha(color, alpha);
|
||||
|
||||
// Bias the shadow downwards a bit.
|
||||
Bounds shadowBounds = bounds.Expand(radius, 0.5 * radius, radius, 1.5 * radius);
|
||||
Draw()->DrawImage4Grid(theme->dropShadow4Grid, shadowBounds.x, shadowBounds.y, shadowBounds.x2(), shadowBounds.y2(), color, radius * (1.0f / 24.0f) * 2.0f);
|
||||
}
|
||||
|
||||
void UIContext::DrawImageVGradient(ImageID image, uint32_t color1, uint32_t color2, const Bounds &bounds) {
|
||||
uidrawbuffer_->DrawImageStretchVGradient(image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), color1, color2);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,8 @@ public:
|
|||
|
||||
void SetTintSaturation(float tint, float sat);
|
||||
|
||||
// High level drawing functions. They generally assume the default texture to be bounds.
|
||||
|
||||
void SetFontStyle(const UI::FontStyle &style);
|
||||
const UI::FontStyle &GetFontStyle() { return *fontStyle_; }
|
||||
void SetFontScale(float scaleX, float scaleY);
|
||||
|
@ -88,6 +90,7 @@ public:
|
|||
void DrawTextRect(const char *str, const Bounds &bounds, uint32_t color, int align = 0);
|
||||
void DrawTextShadowRect(const char *str, const Bounds &bounds, uint32_t color, int align = 0);
|
||||
void FillRect(const UI::Drawable &drawable, const Bounds &bounds);
|
||||
void DrawRectDropShadow(const Bounds &bounds, float radius, float alpha, uint32_t color = 0);
|
||||
void DrawImageVGradient(ImageID image, uint32_t color1, uint32_t color2, const Bounds &bounds);
|
||||
|
||||
// in dps, like dp_xres and dp_yres
|
||||
|
|
|
@ -266,20 +266,17 @@ static bool DefaultSasThread() {
|
|||
|
||||
static const ConfigSetting achievementSettings[] = {
|
||||
ConfigSetting("AchievementsEnable", &g_Config.bAchievementsEnable, true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsLeaderboards", &g_Config.bAchievementsLeaderboards, false, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsTestMode", &g_Config.bAchievementsTestMode, false, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsUnofficialTestMode", &g_Config.bAchievementsUnofficialTestMode, false, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsRichPresence", &g_Config.bAchievementsRichPresence, true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsChallengeMode", &g_Config.bAchievementsChallengeMode, false, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsEncoreMode", &g_Config.bAchievementsEncoreMode, false, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsUnofficial", &g_Config.bAchievementsUnofficial, false, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsSoundEffects", &g_Config.bAchievementsSoundEffects, true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsNotifications", &g_Config.bAchievementsNotifications, true, CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsLogBadMemReads", &g_Config.bAchievementsLogBadMemReads, false, CfgFlag::DEFAULT),
|
||||
|
||||
// Achievements login info. Note that password is NOT stored, only a login token.
|
||||
// And that login token is stored separately from the ini, see NativeSaveSecret.
|
||||
ConfigSetting("AchievementsUserName", &g_Config.sAchievementsUserName, "", CfgFlag::DEFAULT),
|
||||
// And that login token is stored separately from the ini, see NativeSaveSecret, but it can also be loaded
|
||||
// from the ini if manually entered (useful when testing various builds on Android).
|
||||
ConfigSetting("AchievementsToken", &g_Config.sAchievementsToken, "", CfgFlag::DONT_SAVE),
|
||||
ConfigSetting("AchievementsLoginTimestamp", &g_Config.sAchievementsLoginTimestamp, "", CfgFlag::DEFAULT),
|
||||
ConfigSetting("AchievementsUserName", &g_Config.sAchievementsUserName, "", CfgFlag::DEFAULT),
|
||||
};
|
||||
|
||||
static const ConfigSetting cpuSettings[] = {
|
||||
|
|
|
@ -485,20 +485,16 @@ public:
|
|||
// Retro Achievement settings
|
||||
// Copied from Duckstation, we might want to remove some.
|
||||
bool bAchievementsEnable;
|
||||
bool bAchievementsLeaderboards;
|
||||
bool bAchievementsTestMode;
|
||||
bool bAchievementsUnofficialTestMode;
|
||||
bool bAchievementsRichPresence;
|
||||
bool bAchievementsChallengeMode;
|
||||
bool bAchievementsEncoreMode;
|
||||
bool bAchievementsUnofficial;
|
||||
bool bAchievementsSoundEffects;
|
||||
bool bAchievementsNotifications;
|
||||
bool bAchievementsLogBadMemReads;
|
||||
|
||||
// Achivements login info. Note that password is NOT stored, only a login token.
|
||||
// Still, we may wanna store it more securely than in PPSSPP.ini, especially on Android.
|
||||
std::string sAchievementsUserName;
|
||||
std::string sAchievementsToken; // Not saved, to be used if you want to manually make your RA login persistent. See Native_SaveSecret for the normal case.
|
||||
std::string sAchievementsLoginTimestamp;
|
||||
|
||||
// Various directories. Autoconfigured, not read from ini.
|
||||
Path currentDirectory; // The directory selected in the game browsing window.
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
#include "Core/HW/Display.h"
|
||||
#include "Core/Util/PPGeDraw.h"
|
||||
#include "Core/RetroAchievements.h"
|
||||
|
||||
#include "GPU/GPU.h"
|
||||
#include "GPU/GPUState.h"
|
||||
|
@ -348,12 +349,15 @@ void __DisplaySetWasPaused() {
|
|||
}
|
||||
|
||||
static int FrameTimingLimit() {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)
|
||||
return g_Config.iFpsLimit1;
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)
|
||||
return g_Config.iFpsLimit2;
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::ANALOG)
|
||||
return PSP_CoreParameter().analogFpsLimit;
|
||||
if (!Achievements::ChallengeModeActive()) {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1)
|
||||
return g_Config.iFpsLimit1;
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2)
|
||||
return g_Config.iFpsLimit2;
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::ANALOG)
|
||||
return PSP_CoreParameter().analogFpsLimit;
|
||||
}
|
||||
// Note: Fast-forward is OK in challenge mode.
|
||||
if (PSP_CoreParameter().fastForward)
|
||||
return 0;
|
||||
return framerate;
|
||||
|
|
|
@ -85,8 +85,7 @@
|
|||
#include "sceDmac.h"
|
||||
#include "sceMp4.h"
|
||||
#include "sceOpenPSID.h"
|
||||
|
||||
#include "../Util/PPGeDraw.h"
|
||||
#include "Core/Util/PPGeDraw.h"
|
||||
|
||||
/*
|
||||
17: [MIPS32 R4K 00000000 ]: Loader: Type: 1 Vaddr: 00000000 Filesz: 2856816 Memsz: 2856816
|
||||
|
@ -286,8 +285,6 @@ void __KernelDoState(PointerWrap &p)
|
|||
__AACDoState(p);
|
||||
__UsbGpsDoState(p);
|
||||
__UsbMicDoState(p);
|
||||
|
||||
// IMPORTANT! Add new sections last!
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
#include "Core/HLE/sceKernelMemory.h"
|
||||
#include "Core/HLE/KernelWaitHelpers.h"
|
||||
#include "Core/RetroAchievements.h"
|
||||
|
||||
#include "Core/FileSystems/BlockDevices.h"
|
||||
#include "Core/FileSystems/MetaFileSystem.h"
|
||||
|
@ -483,13 +484,15 @@ static u32 sceUmdGetErrorStat()
|
|||
return umdErrorStat;
|
||||
}
|
||||
|
||||
void __UmdReplace(Path filepath) {
|
||||
void __UmdReplace(const Path &filepath) {
|
||||
std::string error = "";
|
||||
if (!UmdReplace(filepath, error)) {
|
||||
ERROR_LOG(SCEIO, "UMD Replace failed: %s", error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Achievements::ChangeUMD(filepath);
|
||||
|
||||
UMDInserted = false;
|
||||
// Wake any threads waiting for the disc to be removed.
|
||||
UmdWakeThreads();
|
||||
|
|
|
@ -43,7 +43,7 @@ enum pspUmdType {
|
|||
void __UmdInit();
|
||||
void __UmdDoState(PointerWrap &p);
|
||||
|
||||
void __UmdReplace(Path filepath);
|
||||
void __UmdReplace(const Path &filepath);
|
||||
bool getUMDReplacePermit();
|
||||
|
||||
void Register_sceUmdUser();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,9 +9,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
@ -19,54 +16,17 @@
|
|||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#include "ext/rcheevos/include/rc_client.h"
|
||||
|
||||
class Path;
|
||||
class PointerWrap;
|
||||
|
||||
namespace Achievements {
|
||||
|
||||
enum class AchievementCategory : u8
|
||||
{
|
||||
Local = 0,
|
||||
Core = 3,
|
||||
Unofficial = 5
|
||||
};
|
||||
|
||||
struct Achievement
|
||||
{
|
||||
u32 id;
|
||||
std::string title;
|
||||
std::string description;
|
||||
std::string memaddr;
|
||||
std::string badge_name;
|
||||
|
||||
u32 points;
|
||||
AchievementCategory category;
|
||||
bool locked;
|
||||
bool active;
|
||||
bool primed;
|
||||
bool disabled; // due to bad memory access, presumably
|
||||
};
|
||||
|
||||
struct Leaderboard
|
||||
{
|
||||
u32 id;
|
||||
std::string title;
|
||||
std::string description;
|
||||
int format;
|
||||
bool hidden;
|
||||
};
|
||||
|
||||
struct LeaderboardEntry
|
||||
{
|
||||
std::string user;
|
||||
std::string formatted_score;
|
||||
time_t submitted;
|
||||
u32 rank;
|
||||
bool is_self;
|
||||
};
|
||||
|
||||
struct Statistics
|
||||
{
|
||||
struct Statistics {
|
||||
// Debug stats
|
||||
int badMemoryAccessCount;
|
||||
};
|
||||
|
@ -85,101 +45,64 @@ static inline bool IsUsingRAIntegration()
|
|||
|
||||
#endif
|
||||
|
||||
bool IsActive();
|
||||
// Returns true if the user is logged in properly, and everything is set up for playing games with achievements.
|
||||
bool IsLoggedIn();
|
||||
|
||||
// Returns true if in a game, and achievements are active in the current game.
|
||||
bool IsActive();
|
||||
|
||||
// Returns true if unofficial achievements are enabled.
|
||||
bool UnofficialEnabled();
|
||||
|
||||
// Returns true if the emulator should hold off on executing game code, such as during game identification.
|
||||
bool IsBlockingExecution();
|
||||
|
||||
// Returns true if features such as save states should be disabled.
|
||||
// This should only be used for controlling functionality of the following things, which are banned in Challenge/Hardcore mode:
|
||||
//
|
||||
// * Savestates
|
||||
// * Slowdown time (though hard to fully prevent, could use crazy post shaders or software rendering...)
|
||||
bool ChallengeModeActive();
|
||||
bool LeaderboardsActive();
|
||||
bool IsTestModeActive();
|
||||
bool IsUnofficialTestModeActive();
|
||||
bool IsRichPresenceEnabled();
|
||||
bool HasActiveGame();
|
||||
|
||||
u32 GetGameID();
|
||||
// Same as ChallengeModeActive but comes with a convenient user message. Don't use for every-frame checks or UI enablement,
|
||||
// only for shortcut keys and commands. You should look up the message in I18NCat::ACHIEVEMENTS.
|
||||
// If no message is specified, a standard "This feature is not available in Challenge Mode" message will be shown.
|
||||
// Also returns true if challenge mode is active.
|
||||
bool WarnUserIfChallengeModeActive(const char *message = nullptr);
|
||||
|
||||
/// Acquires the achievements lock. Must be held when accessing any achievement state from another thread.
|
||||
std::unique_lock<std::recursive_mutex> GetLock();
|
||||
// The new API is so much nicer that we can use it directly instead of wrapping it. So let's expose the client.
|
||||
// Will of course return nullptr if not active.
|
||||
rc_client_t *GetClient();
|
||||
|
||||
void Initialize();
|
||||
void UpdateSettings();
|
||||
|
||||
/// Called when the system is being reset. If it returns false, the reset should be aborted.
|
||||
bool ConfirmSystemReset();
|
||||
|
||||
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted if possible.
|
||||
bool Shutdown();
|
||||
|
||||
/// Called once a frame at vsync time on the CPU thread.
|
||||
void DownloadImageIfMissing(const std::string &cache_key, std::string &&url);
|
||||
|
||||
/// Called once a frame at vsync time on the CPU thread, during gameplay.
|
||||
void FrameUpdate();
|
||||
|
||||
/// Called when the system is paused, because FrameUpdate() won't be getting called.
|
||||
void ProcessPendingHTTPRequests();
|
||||
/// Called every frame to let background processing happen.
|
||||
void Idle();
|
||||
|
||||
/// Saves/loads state.
|
||||
bool DoState(PointerWrap &sw);
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
/// Returns true if the current game has any achievements or leaderboards.
|
||||
/// Does not need to have the lock held.
|
||||
bool SafeHasAchievementsOrLeaderboards();
|
||||
|
||||
const std::string &GetUsername();
|
||||
const std::string &GetRichPresenceString();
|
||||
bool HasAchievementsOrLeaderboards();
|
||||
|
||||
bool LoginAsync(const char *username, const char *password);
|
||||
void Logout();
|
||||
|
||||
void GameChanged(const Path &path);
|
||||
void LeftGame();
|
||||
|
||||
/// Re-enables hardcode mode if it is enabled in the settings.
|
||||
bool ResetChallengeMode();
|
||||
|
||||
/// Forces hardcore mode off until next reset.
|
||||
void DisableChallengeMode();
|
||||
|
||||
/// Prompts the user to disable hardcore mode, if they agree, returns true.
|
||||
bool ConfirmChallengeModeDisable(const char *trigger);
|
||||
|
||||
/// Returns true if features such as save states should be disabled.
|
||||
bool ChallengeModeActive();
|
||||
|
||||
const std::string &GetGameTitle();
|
||||
const std::string &GetGameIcon();
|
||||
|
||||
bool EnumerateAchievements(std::function<bool(const Achievement &)> callback);
|
||||
|
||||
// TODO: Make these support multiple games, not just the current games, with cached info.
|
||||
u32 GetUnlockedAchiementCount();
|
||||
u32 GetAchievementCount();
|
||||
u32 GetMaximumPointsForGame();
|
||||
u32 GetCurrentPointsForGame();
|
||||
void SetGame(const Path &path);
|
||||
void ChangeUMD(const Path &path); // for in-game UMD change
|
||||
void UnloadGame(); // Call when leaving a game.
|
||||
|
||||
Statistics GetStatistics();
|
||||
|
||||
bool EnumerateLeaderboards(std::function<bool(const Leaderboard &)> callback);
|
||||
|
||||
// Unlike most other functions here, this you're supposed to poll until you get a valid std::optional.
|
||||
std::optional<bool> TryEnumerateLeaderboardEntries(u32 id, std::function<bool(const LeaderboardEntry &)> callback);
|
||||
const Leaderboard *GetLeaderboardByID(u32 id);
|
||||
u32 GetLeaderboardCount();
|
||||
bool IsLeaderboardTimeType(const Leaderboard &leaderboard);
|
||||
u32 GetPrimedAchievementCount();
|
||||
|
||||
const Achievement *GetAchievementByID(u32 id);
|
||||
std::pair<u32, u32> GetAchievementProgress(const Achievement &achievement);
|
||||
std::string GetGameAchievementSummary();
|
||||
std::string GetAchievementProgressText(const Achievement &achievement);
|
||||
std::string GetAchievementBadgePath(const Achievement &achievement, bool download_if_missing = true,
|
||||
bool force_unlocked_icon = false);
|
||||
std::string GetAchievementBadgeURL(const Achievement &achievement);
|
||||
|
||||
#ifdef WITH_RAINTEGRATION
|
||||
void SwitchToRAIntegration();
|
||||
|
||||
namespace RAIntegration {
|
||||
void MainWindowChanged(void *new_handle);
|
||||
void GameChanged();
|
||||
std::vector<std::tuple<int, std::string, bool>> GetMenuItems();
|
||||
void ActivateMenuItem(int item);
|
||||
} // namespace RAIntegration
|
||||
#endif
|
||||
} // namespace Achievements
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
||||
#include "Core/RetroAchievements.h"
|
||||
#include "HW/MemoryStick.h"
|
||||
#include "GPU/GPUState.h"
|
||||
|
||||
|
@ -391,12 +392,18 @@ namespace SaveState
|
|||
currentMIPS->DoState(p);
|
||||
HLEDoState(p);
|
||||
__KernelDoState(p);
|
||||
Achievements::DoState(p);
|
||||
// Kernel object destructors might close open files, so do the filesystem last.
|
||||
pspFileSystem.DoState(p);
|
||||
}
|
||||
|
||||
void Enqueue(SaveState::Operation op)
|
||||
{
|
||||
if (Achievements::ChallengeModeActive()) {
|
||||
// No savestate operations are permitted, let's just ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
pending.push_back(op);
|
||||
|
||||
|
|
|
@ -830,10 +830,22 @@ void SystemInfoScreen::CreateTabs() {
|
|||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Clear")))->OnClick.Add([&](UI::EventParams &) {
|
||||
g_OSD.RemoveProgressBar("testprogress", 0.25f);
|
||||
g_OSD.RemoveProgressBar("testprogress");
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new ItemHeader(si->T("Achievement tests")));
|
||||
internals->Add(new Choice(si->T("Leaderboard tracker: Show")))->OnClick.Add([=](UI::EventParams &) {
|
||||
g_OSD.ShowLeaderboardTracker(1, "My leaderboard tracker", true);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Leaderboard tracker: Update")))->OnClick.Add([=](UI::EventParams &) {
|
||||
g_OSD.ShowLeaderboardTracker(1, "Updated tracker", true);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
internals->Add(new Choice(si->T("Leaderboard tracker: Hide")))->OnClick.Add([=](UI::EventParams &) {
|
||||
g_OSD.ShowLeaderboardTracker(1, nullptr, false);
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
|
|
151
UI/EmuScreen.cpp
151
UI/EmuScreen.cpp
|
@ -348,7 +348,7 @@ void EmuScreen::bootGame(const Path &filename) {
|
|||
g_OSD.Show(OSDType::MESSAGE_WARNING, gr->T("DefaultCPUClockRequired", "Warning: This game requires the CPU clock to be set to default."), 10.0f);
|
||||
}
|
||||
|
||||
Achievements::GameChanged(filename);
|
||||
Achievements::SetGame(filename);
|
||||
|
||||
loadingViewColor_->Divert(0xFFFFFFFF, 0.75f);
|
||||
loadingViewVisible_->Divert(UI::V_VISIBLE, 0.75f);
|
||||
|
@ -405,6 +405,7 @@ void EmuScreen::bootComplete() {
|
|||
EmuScreen::~EmuScreen() {
|
||||
if (!invalid_ || bootPending_) {
|
||||
// If we were invalid, it would already be shutdown.
|
||||
Achievements::UnloadGame();
|
||||
PSP_Shutdown();
|
||||
}
|
||||
|
||||
|
@ -581,7 +582,7 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) {
|
|||
break;
|
||||
|
||||
case VIRTKEY_SPEED_TOGGLE:
|
||||
if (down) {
|
||||
if (down && !Achievements::WarnUserIfChallengeModeActive()) {
|
||||
// Cycle through enabled speeds.
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1;
|
||||
|
@ -597,28 +598,32 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) {
|
|||
break;
|
||||
|
||||
case VIRTKEY_SPEED_CUSTOM1:
|
||||
if (down) {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("fixed", "Speed: alternate"), 1.0);
|
||||
}
|
||||
} else {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("standard", "Speed: standard"), 1.0);
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (down) {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("fixed", "Speed: alternate"), 1.0);
|
||||
}
|
||||
} else {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("standard", "Speed: standard"), 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_SPEED_CUSTOM2:
|
||||
if (down) {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0);
|
||||
}
|
||||
} else {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("standard", "Speed: standard"), 1.0);
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (down) {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0);
|
||||
}
|
||||
} else {
|
||||
if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) {
|
||||
PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL;
|
||||
g_OSD.Show(OSDType::MESSAGE_INFO, sc->T("standard", "Speed: standard"), 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -634,13 +639,15 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) {
|
|||
break;
|
||||
|
||||
case VIRTKEY_FRAME_ADVANCE:
|
||||
if (down) {
|
||||
// If game is running, pause emulation immediately. Otherwise, advance a single frame.
|
||||
if (Core_IsStepping()) {
|
||||
frameStep_ = true;
|
||||
Core_EnableStepping(false);
|
||||
} else if (!frameStep_) {
|
||||
Core_EnableStepping(true, "ui.frameAdvance", 0);
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (down) {
|
||||
// If game is running, pause emulation immediately. Otherwise, advance a single frame.
|
||||
if (Core_IsStepping()) {
|
||||
frameStep_ = true;
|
||||
Core_EnableStepping(false);
|
||||
} else if (!frameStep_) {
|
||||
Core_EnableStepping(true, "ui.frameAdvance", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -690,7 +697,7 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) {
|
|||
#endif
|
||||
|
||||
case VIRTKEY_REWIND:
|
||||
if (down) {
|
||||
if (down && !Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (SaveState::CanRewind()) {
|
||||
SaveState::Rewind(&AfterSaveStateAction);
|
||||
} else {
|
||||
|
@ -699,21 +706,21 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) {
|
|||
}
|
||||
break;
|
||||
case VIRTKEY_SAVE_STATE:
|
||||
if (down)
|
||||
if (down && !Achievements::WarnUserIfChallengeModeActive())
|
||||
SaveState::SaveSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction);
|
||||
break;
|
||||
case VIRTKEY_LOAD_STATE:
|
||||
if (down)
|
||||
if (down && !Achievements::WarnUserIfChallengeModeActive())
|
||||
SaveState::LoadSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction);
|
||||
break;
|
||||
case VIRTKEY_PREVIOUS_SLOT:
|
||||
if (down) {
|
||||
if (down && !Achievements::WarnUserIfChallengeModeActive()) {
|
||||
SaveState::PrevSlot();
|
||||
NativeMessageReceived("savestate_displayslot", "");
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_NEXT_SLOT:
|
||||
if (down) {
|
||||
if (down && !Achievements::WarnUserIfChallengeModeActive()) {
|
||||
SaveState::NextSlot();
|
||||
NativeMessageReceived("savestate_displayslot", "");
|
||||
}
|
||||
|
@ -1494,50 +1501,52 @@ void EmuScreen::render() {
|
|||
|
||||
Core_UpdateDebugStats(g_Config.bShowDebugStats || g_Config.bLogFrameDrops);
|
||||
|
||||
PSP_BeginHostFrame();
|
||||
bool blockedExecution = Achievements::IsBlockingExecution();
|
||||
if (!blockedExecution) {
|
||||
PSP_BeginHostFrame();
|
||||
PSP_RunLoopWhileState();
|
||||
|
||||
PSP_RunLoopWhileState();
|
||||
|
||||
// Hopefully coreState is now CORE_NEXTFRAME
|
||||
switch (coreState) {
|
||||
case CORE_NEXTFRAME:
|
||||
// Reached the end of the frame, all good. Set back to running for the next frame
|
||||
coreState = CORE_RUNNING;
|
||||
break;
|
||||
case CORE_STEPPING:
|
||||
case CORE_RUNTIME_ERROR:
|
||||
{
|
||||
// If there's an exception, display information.
|
||||
const MIPSExceptionInfo &info = Core_GetExceptionInfo();
|
||||
if (info.type != MIPSExceptionType::NONE) {
|
||||
// Clear to blue background screen
|
||||
bool dangerousSettings = !Reporting::IsSupported();
|
||||
uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000;
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError");
|
||||
// The info is drawn later in renderUI
|
||||
} else {
|
||||
// If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output.
|
||||
// This won't work in non-buffered, but that's fine.
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
|
||||
// Just to make sure.
|
||||
if (PSP_IsInited()) {
|
||||
gpu->CopyDisplayToOutput(true);
|
||||
// Hopefully coreState is now CORE_NEXTFRAME
|
||||
switch (coreState) {
|
||||
case CORE_NEXTFRAME:
|
||||
// Reached the end of the frame, all good. Set back to running for the next frame
|
||||
coreState = CORE_RUNNING;
|
||||
break;
|
||||
case CORE_STEPPING:
|
||||
case CORE_RUNTIME_ERROR:
|
||||
{
|
||||
// If there's an exception, display information.
|
||||
const MIPSExceptionInfo &info = Core_GetExceptionInfo();
|
||||
if (info.type != MIPSExceptionType::NONE) {
|
||||
// Clear to blue background screen
|
||||
bool dangerousSettings = !Reporting::IsSupported();
|
||||
uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000;
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError");
|
||||
// The info is drawn later in renderUI
|
||||
} else {
|
||||
// If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output.
|
||||
// This won't work in non-buffered, but that's fine.
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
|
||||
// Just to make sure.
|
||||
if (PSP_IsInited()) {
|
||||
gpu->CopyDisplayToOutput(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Didn't actually reach the end of the frame, ran out of the blockTicks cycles.
|
||||
// In this case we need to bind and wipe the backbuffer, at least.
|
||||
// It's possible we never ended up outputted anything - make sure we have the backbuffer cleared
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Didn't actually reach the end of the frame, ran out of the blockTicks cycles.
|
||||
// In this case we need to bind and wipe the backbuffer, at least.
|
||||
// It's possible we never ended up outputted anything - make sure we have the backbuffer cleared
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
|
||||
break;
|
||||
}
|
||||
|
||||
PSP_EndHostFrame();
|
||||
PSP_EndHostFrame();
|
||||
|
||||
// This must happen after PSP_EndHostFrame so that things like push buffers are end-frame'd before we start destroying stuff.
|
||||
checkPowerDown();
|
||||
// This must happen after PSP_EndHostFrame so that things like push buffers are end-frame'd before we start destroying stuff.
|
||||
checkPowerDown();
|
||||
}
|
||||
|
||||
if (invalid_)
|
||||
return;
|
||||
|
|
|
@ -1214,7 +1214,8 @@ void NativeUpdate() {
|
|||
|
||||
g_requestManager.ProcessRequests();
|
||||
|
||||
Achievements::ProcessPendingHTTPRequests();
|
||||
// it's ok to call this redundantly with DoFrame from EmuScreen
|
||||
Achievements::Idle();
|
||||
|
||||
g_DownloadManager.Update();
|
||||
g_screenManager->update();
|
||||
|
|
|
@ -46,14 +46,6 @@ static const float extraTextScale = 0.7f;
|
|||
|
||||
// Align only matters here for the ASCII-only flag.
|
||||
static void MeasureOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, int align, float *width, float *height, float *height1) {
|
||||
if (entry.type == OSDType::ACHIEVEMENT_UNLOCKED) {
|
||||
const Achievements::Achievement *achievement = Achievements::GetAchievementByID(entry.numericID);
|
||||
MeasureAchievement(dc, *achievement, width, height);
|
||||
*width = 550.0f;
|
||||
*height1 = *height;
|
||||
return;
|
||||
}
|
||||
|
||||
dc.SetFontStyle(dc.theme->uiFont);
|
||||
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, entry.text.c_str(), width, height, align);
|
||||
*height1 = *height;
|
||||
|
@ -85,8 +77,10 @@ static void MeasureOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry,
|
|||
|
||||
static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, Bounds bounds, float height1, int align, float alpha) {
|
||||
if (entry.type == OSDType::ACHIEVEMENT_UNLOCKED) {
|
||||
const Achievements::Achievement *achievement = Achievements::GetAchievementByID(entry.numericID);
|
||||
RenderAchievement(dc, *achievement, AchievementRenderStyle::UNLOCKED, bounds, alpha, entry.startTime, time_now_d());
|
||||
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
|
||||
if (achievement) {
|
||||
RenderAchievement(dc, achievement, AchievementRenderStyle::UNLOCKED, bounds, alpha, entry.startTime, time_now_d());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -94,10 +88,7 @@ static void RenderOSDEntry(UIContext &dc, const OnScreenDisplay::Entry &entry, B
|
|||
|
||||
uint32_t foreGround = whiteAlpha(alpha);
|
||||
|
||||
Bounds shadowBounds = bounds.Expand(10.0f);
|
||||
|
||||
dc.Draw()->DrawImage4Grid(dc.theme->dropShadow4Grid, shadowBounds.x, shadowBounds.y + 4.0f, shadowBounds.x2(), shadowBounds.y2(), alphaMul(0xFF000000, 0.9f * alpha), 1.0f);
|
||||
|
||||
dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha);
|
||||
dc.FillRect(background, bounds);
|
||||
|
||||
ImageID iconID = GetOSDIcon(entry.type);
|
||||
|
@ -144,9 +135,7 @@ static void MeasureOSDProgressBar(UIContext &dc, const OnScreenDisplay::Progress
|
|||
static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressBar &entry, Bounds bounds, int align, float alpha) {
|
||||
uint32_t foreGround = whiteAlpha(alpha);
|
||||
|
||||
Bounds shadowBounds = bounds.Expand(10.0f);
|
||||
|
||||
dc.Draw()->DrawImage4Grid(dc.theme->dropShadow4Grid, shadowBounds.x, shadowBounds.y + 4.0f, shadowBounds.x2(), shadowBounds.y2(), alphaMul(0xFF000000, 0.9f * alpha), 1.0f);
|
||||
dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha);
|
||||
|
||||
uint32_t backgroundColor = colorAlpha(0x806050, alpha);
|
||||
uint32_t progressBackgroundColor = colorAlpha(0xa08070, alpha);
|
||||
|
@ -182,6 +171,23 @@ static void RenderOSDProgressBar(UIContext &dc, const OnScreenDisplay::ProgressB
|
|||
dc.DrawTextShadowRect(entry.message.c_str(), bounds, colorAlpha(0xFFFFFFFF, alpha), (align & FLAG_DYNAMIC_ASCII) | ALIGN_CENTER);
|
||||
}
|
||||
|
||||
static void MeasureLeaderboardTracker(UIContext &dc, const std::string &text, float *width, float *height) {
|
||||
dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text.c_str(), width, height);
|
||||
*width += 10.0f;
|
||||
*height += 10.0f;
|
||||
}
|
||||
|
||||
static void RenderLeaderboardTracker(UIContext &dc, const Bounds &bounds, const std::string &text, float alpha) {
|
||||
// TODO: Awful color.
|
||||
uint32_t backgroundColor = colorAlpha(0x806050, alpha);
|
||||
UI::Drawable background = UI::Drawable(backgroundColor);
|
||||
dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha);
|
||||
dc.FillRect(background, bounds);
|
||||
dc.SetFontStyle(dc.theme->uiFont);
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextShadowRect(text.c_str(), bounds.Inset(5.0f, 5.0f), colorAlpha(0xFFFFFFFF, alpha), ALIGN_VCENTER | ALIGN_LEFT);
|
||||
}
|
||||
|
||||
void OnScreenMessagesView::Draw(UIContext &dc) {
|
||||
if (!g_Config.bShowOnScreenMessages) {
|
||||
return;
|
||||
|
@ -191,11 +197,73 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
|||
|
||||
double now = time_now_d();
|
||||
|
||||
float y = 10.0f;
|
||||
|
||||
const float fadeoutCoef = 1.0f / OnScreenDisplay::FadeoutTime();
|
||||
|
||||
// Draw side entries. Top entries should apply on top of them if there's a collision, so drawing
|
||||
// these first makes sense.
|
||||
const std::vector<OnScreenDisplay::Entry> sideEntries = g_OSD.SideEntries();
|
||||
for (auto &entry : sideEntries) {
|
||||
float tw, th;
|
||||
|
||||
const rc_client_achievement_t *achievement = nullptr;
|
||||
AchievementRenderStyle style;
|
||||
|
||||
switch (entry.type) {
|
||||
case OSDType::ACHIEVEMENT_PROGRESS:
|
||||
{
|
||||
achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
|
||||
if (!achievement)
|
||||
continue;
|
||||
style = AchievementRenderStyle::PROGRESS_INDICATOR;
|
||||
MeasureAchievement(dc, achievement, style, &tw, &th);
|
||||
break;
|
||||
}
|
||||
case OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR:
|
||||
{
|
||||
achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
|
||||
if (!achievement)
|
||||
continue;
|
||||
style = AchievementRenderStyle::CHALLENGE_INDICATOR;
|
||||
MeasureAchievement(dc, achievement, style, &tw, &th);
|
||||
break;
|
||||
}
|
||||
case OSDType::LEADERBOARD_TRACKER:
|
||||
{
|
||||
MeasureLeaderboardTracker(dc, entry.text, &tw, &th);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
Bounds b(10.0f, y, tw, th);
|
||||
float alpha = Clamp((float)(entry.endTime - now) * fadeoutCoef, 0.0f, 1.0f);
|
||||
// OK, render the thing.
|
||||
|
||||
switch (entry.type) {
|
||||
case OSDType::ACHIEVEMENT_PROGRESS:
|
||||
case OSDType::ACHIEVEMENT_CHALLENGE_INDICATOR:
|
||||
{
|
||||
RenderAchievement(dc, achievement, style, b, alpha, entry.startTime, now);
|
||||
break;
|
||||
}
|
||||
case OSDType::LEADERBOARD_TRACKER:
|
||||
RenderLeaderboardTracker(dc, b, entry.text, alpha);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
y += (b.h + 4.0f) * alpha; // including alpha here gets us smooth animations.
|
||||
}
|
||||
|
||||
// Get height
|
||||
float w, h;
|
||||
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &w, &h);
|
||||
|
||||
float y = 10.0f;
|
||||
y = 10.0f;
|
||||
|
||||
// Then draw them all.
|
||||
const std::vector<OnScreenDisplay::ProgressBar> bars = g_OSD.ProgressBars();
|
||||
for (auto &bar : bars) {
|
||||
|
@ -210,19 +278,34 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
|||
}
|
||||
|
||||
const std::vector<OnScreenDisplay::Entry> entries = g_OSD.Entries();
|
||||
for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
|
||||
for (const auto &entry : entries) {
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
// Messages that are wider than the screen are left-aligned instead of centered.
|
||||
|
||||
int align = 0;
|
||||
// If we have newlines, we may be looking at ASCII debug output. But let's verify.
|
||||
if (iter->text.find('\n') != 0) {
|
||||
if (!UTF8StringHasNonASCII(iter->text.c_str()))
|
||||
if (entry.text.find('\n') != 0) {
|
||||
if (!UTF8StringHasNonASCII(entry.text.c_str()))
|
||||
align |= FLAG_DYNAMIC_ASCII;
|
||||
}
|
||||
|
||||
float tw, th, h1;
|
||||
MeasureOSDEntry(dc, *iter, align, &tw, &th, &h1);
|
||||
float tw, th = 0.0f, h1 = 0.0f;
|
||||
|
||||
switch (entry.type) {
|
||||
case OSDType::ACHIEVEMENT_UNLOCKED:
|
||||
{
|
||||
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
|
||||
if (achievement) {
|
||||
MeasureAchievement(dc, achievement, AchievementRenderStyle::UNLOCKED, &tw, &th);
|
||||
h1 = th;
|
||||
}
|
||||
tw = 550.0f;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MeasureOSDEntry(dc, entry, align, &tw, &th, &h1);
|
||||
break;
|
||||
}
|
||||
|
||||
Bounds b(0.0f, y, tw, th);
|
||||
|
||||
|
@ -244,8 +327,8 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
|
|||
b.h *= scale;
|
||||
}
|
||||
|
||||
float alpha = Clamp((float)(iter->endTime - now) * 4.0f, 0.0f, 1.0f);
|
||||
RenderOSDEntry(dc, *iter, b, h1, align, alpha);
|
||||
float alpha = Clamp((float)(entry.endTime - now) * 4.0f, 0.0f, 1.0f);
|
||||
RenderOSDEntry(dc, entry, b, h1, align, alpha);
|
||||
y += (b.h * scale + 4.0f) * alpha; // including alpha here gets us smooth animations.
|
||||
}
|
||||
|
||||
|
|
|
@ -265,27 +265,13 @@ GamePauseScreen::~GamePauseScreen() {
|
|||
__DisplaySetWasPaused();
|
||||
}
|
||||
|
||||
void GamePauseScreen::CreateViews() {
|
||||
void GamePauseScreen::CreateSavestateControls(UI::LinearLayout *leftColumnItems, bool vertical) {
|
||||
auto pa = GetI18NCategory(I18NCat::PAUSE);
|
||||
|
||||
static const int NUM_SAVESLOTS = 5;
|
||||
|
||||
using namespace UI;
|
||||
|
||||
bool vertical = UseVerticalLayout();
|
||||
|
||||
Margins scrollMargins(0, 20, 0, 0);
|
||||
Margins actionMenuMargins(0, 20, 15, 0);
|
||||
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
|
||||
auto pa = GetI18NCategory(I18NCat::PAUSE);
|
||||
|
||||
root_ = new LinearLayout(ORIENT_HORIZONTAL);
|
||||
|
||||
ViewGroup *leftColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0, scrollMargins));
|
||||
root_->Add(leftColumn);
|
||||
|
||||
LinearLayout *leftColumnItems = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
leftColumn->Add(leftColumnItems);
|
||||
|
||||
leftColumnItems->Add(new Spacer(0.0));
|
||||
leftColumnItems->SetSpacing(10.0);
|
||||
for (int i = 0; i < NUM_SAVESLOTS; i++) {
|
||||
SaveSlotView *slot = leftColumnItems->Add(new SaveSlotView(gamePath_, i, vertical, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
|
||||
|
@ -311,6 +297,36 @@ void GamePauseScreen::CreateViews() {
|
|||
rewindButton->SetEnabled(SaveState::CanRewind());
|
||||
rewindButton->OnClick.Handle(this, &GamePauseScreen::OnRewind);
|
||||
}
|
||||
}
|
||||
|
||||
void GamePauseScreen::CreateViews() {
|
||||
using namespace UI;
|
||||
|
||||
bool vertical = UseVerticalLayout();
|
||||
|
||||
Margins scrollMargins(0, 10, 0, 0);
|
||||
Margins actionMenuMargins(0, 10, 15, 0);
|
||||
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
|
||||
auto pa = GetI18NCategory(I18NCat::PAUSE);
|
||||
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
|
||||
|
||||
root_ = new LinearLayout(ORIENT_HORIZONTAL);
|
||||
|
||||
ViewGroup *leftColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0, scrollMargins));
|
||||
root_->Add(leftColumn);
|
||||
|
||||
LinearLayout *leftColumnItems = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
leftColumn->Add(leftColumnItems);
|
||||
|
||||
leftColumnItems->Add(new Spacer(0.0));
|
||||
if (Achievements::IsActive()) {
|
||||
leftColumnItems->Add(new GameAchievementSummaryView());
|
||||
leftColumnItems->Add(new Spacer(5.0));
|
||||
}
|
||||
|
||||
if (!Achievements::ChallengeModeActive()) {
|
||||
CreateSavestateControls(leftColumnItems, vertical);
|
||||
}
|
||||
|
||||
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(vertical ? 200 : 300, FILL_PARENT, actionMenuMargins));
|
||||
root_->Add(rightColumn);
|
||||
|
@ -347,7 +363,7 @@ void GamePauseScreen::CreateViews() {
|
|||
return UI::EVENT_DONE;
|
||||
});
|
||||
}
|
||||
if (g_Config.bAchievementsEnable && Achievements::SafeHasAchievementsOrLeaderboards()) {
|
||||
if (g_Config.bAchievementsEnable && Achievements::HasAchievementsOrLeaderboards()) {
|
||||
rightColumnItems->Add(new Choice(pa->T("Achievements")))->OnClick.Add([&](UI::EventParams &e) {
|
||||
screenManager()->push(new RetroAchievementsListScreen(gamePath_));
|
||||
return UI::EVENT_DONE;
|
||||
|
|
|
@ -45,6 +45,8 @@ protected:
|
|||
void CallbackDeleteConfig(bool yes);
|
||||
|
||||
private:
|
||||
void CreateSavestateControls(UI::LinearLayout *viewGroup, bool vertical);
|
||||
|
||||
UI::EventReturn OnGameSettings(UI::EventParams &e);
|
||||
UI::EventReturn OnExitToMenu(UI::EventParams &e);
|
||||
UI::EventReturn OnReportFeedback(UI::EventParams &e);
|
||||
|
|
|
@ -17,11 +17,9 @@ void RetroAchievementsListScreen::CreateTabs() {
|
|||
achievements->SetSpacing(5.0f);
|
||||
CreateAchievementsTab(achievements);
|
||||
|
||||
if (Achievements::GetLeaderboardCount() > 0) {
|
||||
UI::LinearLayout *leaderboards = AddTab("Leaderboards", ac->T("Leaderboards"));
|
||||
leaderboards->SetSpacing(5.0f);
|
||||
CreateLeaderboardsTab(leaderboards);
|
||||
}
|
||||
UI::LinearLayout *leaderboards = AddTab("Leaderboards", ac->T("Leaderboards"));
|
||||
leaderboards->SetSpacing(5.0f);
|
||||
CreateLeaderboardsTab(leaderboards);
|
||||
|
||||
#ifdef _DEBUG
|
||||
CreateStatisticsTab(AddTab("AchievementsStatistics", ac->T("Statistics")));
|
||||
|
@ -34,29 +32,50 @@ void RetroAchievementsListScreen::CreateAchievementsTab(UI::ViewGroup *achieveme
|
|||
|
||||
using namespace UI;
|
||||
|
||||
std::vector<Achievements::Achievement> unlockedAchievements;
|
||||
std::vector<Achievements::Achievement> lockedAchievements;
|
||||
int filter = RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE;
|
||||
if (Achievements::UnofficialEnabled()) {
|
||||
filter = RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL;
|
||||
}
|
||||
|
||||
rc_client_achievement_list_t *list = rc_client_create_achievement_list(Achievements::GetClient(),
|
||||
filter, RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_LOCK_STATE);
|
||||
|
||||
std::vector<const rc_client_achievement_t *> unlockedAchievements;
|
||||
std::vector<const rc_client_achievement_t *> lockedAchievements;
|
||||
std::vector<const rc_client_achievement_t *> otherAchievements;
|
||||
|
||||
for (uint32_t i = 0; i < list->num_buckets; i++) {
|
||||
const rc_client_achievement_bucket_t &bucket = list->buckets[i];
|
||||
for (uint32_t j = 0; j < bucket.num_achievements; j++) {
|
||||
switch (bucket.bucket_type) {
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED:
|
||||
lockedAchievements.push_back(bucket.achievements[j]);
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED:
|
||||
unlockedAchievements.push_back(bucket.achievements[j]);
|
||||
break;
|
||||
default:
|
||||
otherAchievements.push_back(bucket.achievements[j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
achievements->Add(new ItemHeader(ac->T("Achievements")));
|
||||
|
||||
achievements->Add(new GameAchievementSummaryView(Achievements::GetGameID()));
|
||||
|
||||
Achievements::EnumerateAchievements([&](const Achievements::Achievement &achievement) {
|
||||
if (achievement.locked) {
|
||||
lockedAchievements.push_back(achievement);
|
||||
} else {
|
||||
unlockedAchievements.push_back(achievement);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
achievements->Add(new GameAchievementSummaryView());
|
||||
|
||||
achievements->Add(new ItemHeader(ac->T("Unlocked achievements")));
|
||||
for (auto &achievement : unlockedAchievements) {
|
||||
achievements->Add(new AchievementView(std::move(achievement)));
|
||||
achievements->Add(new AchievementView(achievement));
|
||||
}
|
||||
achievements->Add(new ItemHeader(ac->T("Locked achievements")));
|
||||
for (auto &achievement : lockedAchievements) {
|
||||
achievements->Add(new AchievementView(std::move(achievement)));
|
||||
achievements->Add(new AchievementView(achievement));
|
||||
}
|
||||
achievements->Add(new ItemHeader(ac->T("Other achievements")));
|
||||
for (auto &achievement : otherAchievements) {
|
||||
achievements->Add(new AchievementView(achievement));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,25 +85,25 @@ void RetroAchievementsListScreen::CreateLeaderboardsTab(UI::ViewGroup *viewGroup
|
|||
|
||||
using namespace UI;
|
||||
|
||||
viewGroup->Add(new GameAchievementSummaryView(Achievements::GetGameID()));
|
||||
viewGroup->Add(new GameAchievementSummaryView());
|
||||
|
||||
viewGroup->Add(new ItemHeader(ac->T("Leaderboards")));
|
||||
|
||||
std::vector<Achievements::Leaderboard> leaderboards;
|
||||
|
||||
Achievements::EnumerateLeaderboards([&](const Achievements::Leaderboard &leaderboard) {
|
||||
leaderboards.push_back(leaderboard);
|
||||
return true;
|
||||
});
|
||||
std::vector<rc_client_leaderboard_t *> leaderboards;
|
||||
rc_client_leaderboard_list_t *list = rc_client_create_leaderboard_list(Achievements::GetClient(), RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
|
||||
for (uint32_t i = 0; i < list->num_buckets; i++) {
|
||||
const rc_client_leaderboard_bucket_t &bucket = list->buckets[i];
|
||||
for (uint32_t j = 0; j < bucket.num_leaderboards; j++) {
|
||||
leaderboards.push_back(bucket.leaderboards[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &leaderboard : leaderboards) {
|
||||
if (!leaderboard.hidden) {
|
||||
int leaderboardID = leaderboard.id;
|
||||
viewGroup->Add(new LeaderboardSummaryView(std::move(leaderboard)))->OnClick.Add([=](UI::EventParams &e) -> UI::EventReturn {
|
||||
screenManager()->push(new RetroAchievementsLeaderboardScreen(gamePath_, leaderboardID));
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
}
|
||||
int leaderboardID = leaderboard->id;
|
||||
viewGroup->Add(new LeaderboardSummaryView(leaderboard))->OnClick.Add([=](UI::EventParams &e) -> UI::EventReturn {
|
||||
screenManager()->push(new RetroAchievementsLeaderboardScreen(gamePath_, leaderboardID));
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,35 +117,60 @@ void RetroAchievementsListScreen::CreateStatisticsTab(UI::ViewGroup *viewGroup)
|
|||
viewGroup->Add(new InfoItem(ac->T("Bad memory accesses"), StringFromFormat("%d", stats.badMemoryAccessCount)));
|
||||
}
|
||||
|
||||
RetroAchievementsLeaderboardScreen::~RetroAchievementsLeaderboardScreen() {
|
||||
if (pendingAsyncCall_) {
|
||||
rc_client_abort_async(Achievements::GetClient(), pendingAsyncCall_);
|
||||
}
|
||||
Poll(); // Gets rid of pendingEntryList_.
|
||||
if (entryList_) {
|
||||
rc_client_destroy_leaderboard_entry_list(entryList_);
|
||||
}
|
||||
}
|
||||
|
||||
RetroAchievementsLeaderboardScreen::RetroAchievementsLeaderboardScreen(const Path &gamePath, int leaderboardID)
|
||||
: TabbedUIDialogScreenWithGameBackground(gamePath), leaderboardID_(leaderboardID) {
|
||||
rc_client_begin_fetch_leaderboard_entries(Achievements::GetClient(), leaderboardID_, 0, 20, [](int result, const char *error_message, rc_client_leaderboard_entry_list_t *list, rc_client_t *client, void *userdata) {
|
||||
if (result != RC_OK) {
|
||||
g_OSD.Show(OSDType::MESSAGE_ERROR, error_message, 10.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
RetroAchievementsLeaderboardScreen *thiz = (RetroAchievementsLeaderboardScreen *)userdata;
|
||||
thiz->pendingEntryList_ = list;
|
||||
thiz->pendingAsyncCall_ = nullptr;
|
||||
}, this);
|
||||
}
|
||||
|
||||
void RetroAchievementsLeaderboardScreen::CreateTabs() {
|
||||
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
|
||||
const Achievements::Leaderboard *leaderboard = Achievements::GetLeaderboardByID(leaderboardID_);
|
||||
const rc_client_leaderboard_t *leaderboard = rc_client_get_leaderboard_info(Achievements::GetClient(), leaderboardID_);
|
||||
|
||||
using namespace UI;
|
||||
|
||||
UI::LinearLayout *layout = AddTab("AchievementsLeaderboard", leaderboard->title.c_str());
|
||||
UI::LinearLayout *layout = AddTab("AchievementsLeaderboard", leaderboard->title);
|
||||
layout->Add(new TextView(leaderboard->description));
|
||||
|
||||
layout->Add(new ItemHeader(ac->T("Leaderboard")));
|
||||
|
||||
Poll();
|
||||
if (entryList_) {
|
||||
for (uint32_t i = 0; i < entryList_->num_entries; i++) {
|
||||
bool is_self = (i == entryList_->user_index);
|
||||
// Should highlight somehow.
|
||||
const rc_client_leaderboard_entry_t &entry = entryList_->entries[i];
|
||||
|
||||
// TODO: Make it pretty.
|
||||
for (auto &entry : entries_) {
|
||||
layout->Add(new TextView(StringFromFormat(" %d: %s: %s%s", entry.rank, entry.user.c_str(), entry.formatted_score.c_str(), entry.is_self ? " <<<<< " : "")));
|
||||
char buffer[512];
|
||||
rc_client_leaderboard_entry_get_user_image_url(&entryList_->entries[i], buffer, sizeof(buffer));
|
||||
// Can also show entry.submitted, which is a time_t. And maybe highlight recent ones?
|
||||
layout->Add(new LeaderboardEntryView(&entryList_->entries[i], is_self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RetroAchievementsLeaderboardScreen::Poll() {
|
||||
if (done_)
|
||||
return;
|
||||
|
||||
std::optional<bool> result = Achievements::TryEnumerateLeaderboardEntries(leaderboardID_, [&](const Achievements::LeaderboardEntry &entry) -> bool {
|
||||
entries_.push_back(entry);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (result.has_value()) {
|
||||
done_ = true;
|
||||
if (pendingEntryList_) {
|
||||
if (entryList_) {
|
||||
rc_client_destroy_leaderboard_entry_list(entryList_);
|
||||
}
|
||||
entryList_ = pendingEntryList_;
|
||||
pendingEntryList_ = nullptr;
|
||||
RecreateViews();
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +180,8 @@ void RetroAchievementsLeaderboardScreen::update() {
|
|||
Poll();
|
||||
}
|
||||
|
||||
RetroAchievementsSettingsScreen::~RetroAchievementsSettingsScreen() {}
|
||||
|
||||
void RetroAchievementsSettingsScreen::CreateTabs() {
|
||||
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
|
@ -160,8 +206,17 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup)
|
|||
|
||||
using namespace UI;
|
||||
|
||||
if (Achievements::IsLoggedIn()) {
|
||||
viewGroup->Add(new InfoItem(ac->T("Username"), Achievements::GetUsername()));
|
||||
if (!g_Config.bAchievementsEnable) {
|
||||
viewGroup->Add(new TextView(ac->T("Achievements are disabled")));
|
||||
} else if (Achievements::IsLoggedIn()) {
|
||||
const rc_client_user_t *info = rc_client_get_user_info(Achievements::GetClient());
|
||||
|
||||
// In the future, RetroAchievements will support display names. Prepare for that.
|
||||
if (strcmp(info->display_name, info->username) != 0) {
|
||||
viewGroup->Add(new InfoItem(ac->T("Name"), info->display_name));
|
||||
}
|
||||
viewGroup->Add(new InfoItem(ac->T("Username"), info->username));
|
||||
// viewGroup->Add(new InfoItem(ac->T("Unread messages"), info.numUnreadMessages));
|
||||
viewGroup->Add(new Choice(di->T("Log out")))->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn {
|
||||
Achievements::Logout();
|
||||
return UI::EVENT_DONE;
|
||||
|
@ -212,85 +267,165 @@ void RetroAchievementsSettingsScreen::CreateSettingsTab(UI::ViewGroup *viewGroup
|
|||
|
||||
using namespace UI;
|
||||
viewGroup->Add(new ItemHeader(ac->T("Settings")));
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsRichPresence, ac->T("Rich Presence")));
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsSoundEffects, ac->T("Sound Effects"))); // not yet implemented
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsLogBadMemReads, ac->T("Log bad memory accesses")));
|
||||
|
||||
// Not yet fully implemented
|
||||
// viewGroup->Add(new CheckBox(&g_Config.bAchievementsChallengeMode, ac->T("Challenge Mode (no savestates)")));
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsEnable, ac->T("Achievements enabled")))->OnClick.Add([&](UI::EventParams &e) -> UI::EventReturn {
|
||||
Achievements::UpdateSettings();
|
||||
RecreateViews();
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsChallengeMode, ac->T("Challenge Mode (no savestates)")));
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsEncoreMode, ac->T("Encore Mode")));
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsUnofficial, ac->T("Unofficial achievements")));
|
||||
// viewGroup->Add(new CheckBox(&g_Config.bAchievementsSoundEffects, ac->T("Sound Effects")))->SetEnabledPtr(&g_Config.bAchievementsEnable); // not yet implemented
|
||||
viewGroup->Add(new CheckBox(&g_Config.bAchievementsLogBadMemReads, ac->T("Log bad memory accesses")))->SetEnabledPtr(&g_Config.bAchievementsEnable);
|
||||
|
||||
// TODO: What are these for?
|
||||
// viewGroup->Add(new CheckBox(&g_Config.bAchievementsTestMode, ac->T("Test Mode")));
|
||||
// viewGroup->Add(new CheckBox(&g_Config.bAchievementsUnofficialTestMode, ac->T("Unofficial Test Mode")));
|
||||
}
|
||||
|
||||
void MeasureAchievement(const UIContext &dc, const Achievements::Achievement &achievement, float *w, float *h) {
|
||||
void MeasureAchievement(const UIContext &dc, const rc_client_achievement_t *achievement, AchievementRenderStyle style, float *w, float *h) {
|
||||
*w = 0.0f;
|
||||
switch (style) {
|
||||
case AchievementRenderStyle::PROGRESS_INDICATOR:
|
||||
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, achievement->measured_progress, w, h);
|
||||
*w += 44.0f + 4.0f * 3.0f;
|
||||
*h = 44.0f;
|
||||
break;
|
||||
case AchievementRenderStyle::CHALLENGE_INDICATOR:
|
||||
// ONLY the icon.
|
||||
*w = 60.0f;
|
||||
*h = 60.0f;
|
||||
break;
|
||||
default:
|
||||
*h = 72.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MeasureGameAchievementSummary(const UIContext &dc, float *w, float *h) {
|
||||
std::string description = Achievements::GetGameAchievementSummary();
|
||||
|
||||
float tw, th;
|
||||
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, "Wg", &tw, &th);
|
||||
|
||||
dc.MeasureText(dc.theme->uiFont, 0.66f, 0.66f, description.c_str(), w, h);
|
||||
*h += 12.0f + th;
|
||||
*w += 8.0f;
|
||||
}
|
||||
|
||||
void MeasureLeaderboardSummary(const UIContext &dc, const rc_client_leaderboard_t *leaderboard, float *w, float *h) {
|
||||
*w = 0.0f;
|
||||
*h = 72.0f;
|
||||
}
|
||||
|
||||
void MeasureGameAchievementSummary(const UIContext &dc, int gameID, float *w, float *h) {
|
||||
*w = 0.0f;
|
||||
*h = 72.0f;
|
||||
}
|
||||
|
||||
void MeasureLeaderboardSummary(const UIContext &dc, const Achievements::Leaderboard &achievement, float *w, float *h) {
|
||||
void MeasureLeaderboardEntry(const UIContext &dc, const rc_client_leaderboard_entry_t *entry, float *w, float *h) {
|
||||
*w = 0.0f;
|
||||
*h = 72.0f;
|
||||
}
|
||||
|
||||
// Graphical
|
||||
void RenderAchievement(UIContext &dc, const Achievements::Achievement &achievement, AchievementRenderStyle style, const Bounds &bounds, float alpha, float startTime, float time_s) {
|
||||
void RenderAchievement(UIContext &dc, const rc_client_achievement_t *achievement, AchievementRenderStyle style, const Bounds &bounds, float alpha, float startTime, float time_s) {
|
||||
using namespace UI;
|
||||
UI::Drawable background = UI::Drawable(dc.theme->backgroundColor);
|
||||
if (achievement.locked) {
|
||||
background.color = 0x706060;
|
||||
|
||||
// Set some alpha, if displayed in list.
|
||||
if (style == AchievementRenderStyle::LISTED) {
|
||||
background.color = colorAlpha(background.color, 0.6f);
|
||||
}
|
||||
background.color = colorAlpha(background.color, alpha);
|
||||
uint32_t fgColor = colorAlpha(dc.theme->itemStyle.fgColor, alpha);
|
||||
|
||||
if (!achievement->unlocked) {
|
||||
// Make the background color gray.
|
||||
// TODO: Different colors in challenge mode, or even in the "re-take achievements" mode when we add that?
|
||||
background.color = (background.color & 0xFF000000) | 0x706060;
|
||||
}
|
||||
|
||||
int iconState = achievement->state;
|
||||
|
||||
background.color = alphaMul(background.color, alpha);
|
||||
uint32_t fgColor = alphaMul(dc.theme->itemStyle.fgColor, alpha);
|
||||
|
||||
if (style == AchievementRenderStyle::UNLOCKED) {
|
||||
float mixWhite = pow(Clamp((float)(1.0f - (time_s - startTime)), 0.0f, 1.0f), 3.0f);
|
||||
background.color = colorBlend(0xFFE0FFFF, background.color, mixWhite);
|
||||
}
|
||||
|
||||
float iconSpace = 64.0f;
|
||||
float padding = 4.0f;
|
||||
|
||||
float iconSpace = bounds.h - padding * 2.0f;
|
||||
dc.Flush();
|
||||
dc.RebindTexture();
|
||||
|
||||
dc.Begin();
|
||||
|
||||
if (style != AchievementRenderStyle::LISTED) {
|
||||
dc.DrawRectDropShadow(bounds, 12.0f, 0.7f * alpha);
|
||||
}
|
||||
|
||||
dc.FillRect(background, bounds);
|
||||
|
||||
dc.Flush();
|
||||
dc.Begin();
|
||||
|
||||
dc.SetFontStyle(dc.theme->uiFont);
|
||||
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextRect(achievement.title.c_str(), bounds.Inset(iconSpace + 12.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
char temp[512];
|
||||
|
||||
dc.SetFontScale(0.66f, 0.66f);
|
||||
dc.DrawTextRect(achievement.description.c_str(), bounds.Inset(iconSpace + 12.0f, 39.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
switch (style) {
|
||||
case AchievementRenderStyle::LISTED:
|
||||
case AchievementRenderStyle::UNLOCKED:
|
||||
{
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextRect(achievement->title, bounds.Inset(iconSpace + 12.0f, 2.0f, padding, padding), fgColor, ALIGN_TOPLEFT);
|
||||
|
||||
char temp[64];
|
||||
snprintf(temp, sizeof(temp), "%d", achievement.points);
|
||||
dc.SetFontScale(0.66f, 0.66f);
|
||||
dc.DrawTextRect(achievement->description, bounds.Inset(iconSpace + 12.0f, 39.0f, padding, padding), fgColor, ALIGN_TOPLEFT);
|
||||
|
||||
dc.SetFontScale(1.5f, 1.5f);
|
||||
dc.DrawTextRect(temp, bounds.Expand(-5.0f, -5.0f), fgColor, ALIGN_RIGHT | ALIGN_VCENTER);
|
||||
if (style == AchievementRenderStyle::LISTED && strlen(achievement->measured_progress) > 0) {
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextRect(achievement->measured_progress, bounds.Inset(iconSpace + 12.0f, padding, padding + 100.0f, padding), fgColor, ALIGN_VCENTER | ALIGN_RIGHT);
|
||||
}
|
||||
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.Flush();
|
||||
// TODO: Draw measured_progress / measured_percent in a cute way
|
||||
snprintf(temp, sizeof(temp), "%d", achievement->points);
|
||||
|
||||
std::string name = Achievements::GetAchievementBadgePath(achievement);
|
||||
if (g_iconCache.BindIconTexture(&dc, name)) {
|
||||
dc.Draw()->DrawTexRect(Bounds(bounds.x + 4.0f, bounds.y + 4.0f, iconSpace, iconSpace), 0.0f, 0.0f, 1.0f, 1.0f, whiteAlpha(alpha));
|
||||
dc.SetFontScale(1.5f, 1.5f);
|
||||
dc.DrawTextRect(temp, bounds.Expand(-5.0f, -5.0f), fgColor, ALIGN_RIGHT | ALIGN_VCENTER);
|
||||
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.Flush();
|
||||
break;
|
||||
}
|
||||
case AchievementRenderStyle::PROGRESS_INDICATOR:
|
||||
// TODO: Also render a progress bar.
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextRect(achievement->measured_progress, bounds.Inset(iconSpace + padding * 2.0f, padding, padding, padding), fgColor, ALIGN_LEFT | ALIGN_VCENTER);
|
||||
// Show the unlocked icon.
|
||||
iconState = RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED;
|
||||
break;
|
||||
case AchievementRenderStyle::CHALLENGE_INDICATOR:
|
||||
// Nothing but the icon, unlocked.
|
||||
iconState = RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED;
|
||||
break;
|
||||
}
|
||||
|
||||
dc.Flush();
|
||||
dc.RebindTexture();
|
||||
// Download and display the image.
|
||||
char cacheKey[256];
|
||||
snprintf(cacheKey, sizeof(cacheKey), "ai:%s:%s", achievement->badge_name, iconState == RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED ? "unlocked" : "locked");
|
||||
if (RC_OK == rc_client_achievement_get_image_url(achievement, iconState, temp, sizeof(temp))) {
|
||||
Achievements::DownloadImageIfMissing(cacheKey, std::move(std::string(temp)));
|
||||
if (g_iconCache.BindIconTexture(&dc, cacheKey)) {
|
||||
dc.Draw()->DrawTexRect(Bounds(bounds.x + padding, bounds.y + padding, iconSpace, iconSpace), 0.0f, 0.0f, 1.0f, 1.0f, whiteAlpha(alpha));
|
||||
}
|
||||
dc.Flush();
|
||||
dc.RebindTexture();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderGameAchievementSummary(UIContext &dc, int gameID, const Bounds &bounds, float alpha) {
|
||||
void RenderGameAchievementSummary(UIContext &dc, const Bounds &bounds, float alpha) {
|
||||
using namespace UI;
|
||||
UI::Drawable background = dc.theme->itemStyle.background;
|
||||
|
||||
background.color = colorAlpha(background.color, alpha);
|
||||
background.color = alphaMul(background.color, alpha);
|
||||
uint32_t fgColor = colorAlpha(dc.theme->itemStyle.fgColor, alpha);
|
||||
|
||||
float iconSpace = 64.0f;
|
||||
|
@ -301,11 +436,12 @@ void RenderGameAchievementSummary(UIContext &dc, int gameID, const Bounds &bound
|
|||
|
||||
dc.SetFontStyle(dc.theme->uiFont);
|
||||
|
||||
const rc_client_game_t *gameInfo = rc_client_get_game_info(Achievements::GetClient());
|
||||
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextRect(Achievements::GetGameTitle().c_str(), bounds.Inset(iconSpace + 5.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
dc.DrawTextRect(gameInfo->title, bounds.Inset(iconSpace + 5.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
|
||||
std::string description = Achievements::GetGameAchievementSummary();
|
||||
std::string icon = Achievements::GetGameIcon();
|
||||
|
||||
dc.SetFontScale(0.66f, 0.66f);
|
||||
dc.DrawTextRect(description.c_str(), bounds.Inset(iconSpace + 5.0f, 38.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
|
@ -313,16 +449,21 @@ void RenderGameAchievementSummary(UIContext &dc, int gameID, const Bounds &bound
|
|||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.Flush();
|
||||
|
||||
std::string name = icon;
|
||||
if (g_iconCache.BindIconTexture(&dc, name)) {
|
||||
dc.Draw()->DrawTexRect(Bounds(bounds.x, bounds.y, iconSpace, iconSpace), 0.0f, 0.0f, 1.0f, 1.0f, whiteAlpha(alpha));
|
||||
char url[512];
|
||||
char cacheKey[256];
|
||||
snprintf(cacheKey, sizeof(cacheKey), "gi:%s", gameInfo->badge_name);
|
||||
if (RC_OK == rc_client_game_get_image_url(gameInfo, url, sizeof(url))) {
|
||||
Achievements::DownloadImageIfMissing(cacheKey, std::move(std::string(url)));
|
||||
if (g_iconCache.BindIconTexture(&dc, cacheKey)) {
|
||||
dc.Draw()->DrawTexRect(Bounds(bounds.x, bounds.y, iconSpace, iconSpace), 0.0f, 0.0f, 1.0f, 1.0f, whiteAlpha(alpha));
|
||||
}
|
||||
}
|
||||
|
||||
dc.Flush();
|
||||
dc.RebindTexture();
|
||||
}
|
||||
|
||||
void RenderLeaderboardSummary(UIContext &dc, const Achievements::Leaderboard &leaderboard, AchievementRenderStyle style, const Bounds &bounds, float alpha, float startTime, float time_s) {
|
||||
void RenderLeaderboardSummary(UIContext &dc, const rc_client_leaderboard_t *leaderboard, AchievementRenderStyle style, const Bounds &bounds, float alpha, float startTime, float time_s) {
|
||||
using namespace UI;
|
||||
UI::Drawable background = UI::Drawable(dc.theme->backgroundColor);
|
||||
background.color = colorAlpha(background.color, alpha);
|
||||
|
@ -333,7 +474,6 @@ void RenderLeaderboardSummary(UIContext &dc, const Achievements::Leaderboard &le
|
|||
background.color = colorBlend(0xFFE0FFFF, background.color, mixWhite);
|
||||
}
|
||||
|
||||
float iconSpace = 64.0f;
|
||||
dc.Flush();
|
||||
|
||||
dc.Begin();
|
||||
|
@ -342,10 +482,10 @@ void RenderLeaderboardSummary(UIContext &dc, const Achievements::Leaderboard &le
|
|||
dc.SetFontStyle(dc.theme->uiFont);
|
||||
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextRect(leaderboard.title.c_str(), bounds.Inset(iconSpace + 12.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
dc.DrawTextRect(leaderboard->title, bounds.Inset(12.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
|
||||
dc.SetFontScale(0.66f, 0.66f);
|
||||
dc.DrawTextRect(leaderboard.description.c_str(), bounds.Inset(iconSpace + 12.0f, 39.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
dc.DrawTextRect(leaderboard->description, bounds.Inset(12.0f, 39.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
|
||||
/*
|
||||
char temp[64];
|
||||
|
@ -362,21 +502,84 @@ void RenderLeaderboardSummary(UIContext &dc, const Achievements::Leaderboard &le
|
|||
dc.RebindTexture();
|
||||
}
|
||||
|
||||
void RenderLeaderboardEntry(UIContext &dc, const rc_client_leaderboard_entry_t *entry, const Bounds &bounds, float alpha) {
|
||||
using namespace UI;
|
||||
UI::Drawable background = dc.theme->itemStyle.background;
|
||||
|
||||
background.color = alphaMul(background.color, alpha);
|
||||
uint32_t fgColor = alphaMul(dc.theme->itemStyle.fgColor, alpha);
|
||||
|
||||
float iconSize = 64.0f;
|
||||
float numberSpace = 128.0f;
|
||||
float iconLeft = numberSpace + 5.0f;
|
||||
float iconSpace = numberSpace + 5.0f + iconSize;
|
||||
|
||||
dc.Flush();
|
||||
|
||||
dc.Begin();
|
||||
dc.FillRect(background, bounds);
|
||||
|
||||
dc.SetFontStyle(dc.theme->uiFont);
|
||||
|
||||
dc.SetFontScale(1.5f, 1.5f);
|
||||
dc.DrawTextRect(StringFromFormat("%d", entry->rank).c_str(), Bounds(bounds.x + 4.0f, bounds.y + 4.0f, numberSpace - 10.0f, bounds.h - 4.0f * 2.0f), fgColor, ALIGN_TOPRIGHT);
|
||||
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.DrawTextRect(entry->user, bounds.Inset(iconSpace + 5.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
|
||||
dc.SetFontScale(0.66f, 0.66f);
|
||||
dc.DrawTextRect(entry->display, bounds.Inset(iconSpace + 5.0f, 38.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
|
||||
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
dc.Flush();
|
||||
|
||||
// Come up with a unique name for the icon entry.
|
||||
char cacheKey[256];
|
||||
snprintf(cacheKey, sizeof(cacheKey), "lbe:%s", entry->user);
|
||||
char temp[512];
|
||||
if (RC_OK == rc_client_leaderboard_entry_get_user_image_url(entry, temp, sizeof(temp))) {
|
||||
Achievements::DownloadImageIfMissing(cacheKey, std::move(std::string(temp)));
|
||||
if (g_iconCache.BindIconTexture(&dc, cacheKey)) {
|
||||
dc.Draw()->DrawTexRect(Bounds(bounds.x + iconLeft, bounds.y + 4.0f, 64.0f, 64.0f), 0.0f, 0.0f, 1.0f, 1.0f, whiteAlpha(alpha));
|
||||
}
|
||||
}
|
||||
|
||||
dc.Flush();
|
||||
dc.RebindTexture();
|
||||
}
|
||||
|
||||
void AchievementView::Draw(UIContext &dc) {
|
||||
RenderAchievement(dc, achievement_, AchievementRenderStyle::LISTED, bounds_, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void AchievementView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
|
||||
MeasureAchievement(dc, achievement_, &w, &h);
|
||||
MeasureAchievement(dc, achievement_, AchievementRenderStyle::LISTED, &w, &h);
|
||||
}
|
||||
|
||||
void AchievementView::Click() {
|
||||
// In debug builds, clicking achievements will show them being unlocked (which may be a lie).
|
||||
#ifdef _DEBUG
|
||||
g_OSD.ShowAchievementUnlocked(achievement_.id);
|
||||
static int type = 0;
|
||||
type++;
|
||||
type = type % 4;
|
||||
switch (type) {
|
||||
case 0: g_OSD.ShowAchievementUnlocked(achievement_->id); break;
|
||||
case 1: g_OSD.ShowAchievementProgress(achievement_->id, 2.0f); break;
|
||||
case 2: g_OSD.ShowChallengeIndicator(achievement_->id, true); break;
|
||||
case 3: g_OSD.ShowChallengeIndicator(achievement_->id, false); break;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void GameAchievementSummaryView::Draw(UIContext &dc) {
|
||||
RenderGameAchievementSummary(dc, bounds_, 1.0f);
|
||||
}
|
||||
|
||||
void GameAchievementSummaryView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
|
||||
MeasureGameAchievementSummary(dc, &w, &h);
|
||||
}
|
||||
|
||||
void LeaderboardSummaryView::Draw(UIContext &dc) {
|
||||
RenderLeaderboardSummary(dc, leaderboard_, AchievementRenderStyle::LISTED, bounds_, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
@ -385,10 +588,11 @@ void LeaderboardSummaryView::GetContentDimensions(const UIContext &dc, float &w,
|
|||
MeasureLeaderboardSummary(dc, leaderboard_, &w, &h);
|
||||
}
|
||||
|
||||
void GameAchievementSummaryView::Draw(UIContext &dc) {
|
||||
RenderGameAchievementSummary(dc, gameID_, bounds_, 1.0f);
|
||||
void LeaderboardEntryView::Draw(UIContext &dc) {
|
||||
RenderLeaderboardEntry(dc, entry_, bounds_, 1.0f);
|
||||
}
|
||||
|
||||
void GameAchievementSummaryView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
|
||||
MeasureGameAchievementSummary(dc, gameID_, &w, &h);
|
||||
void LeaderboardEntryView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
|
||||
MeasureLeaderboardEntry(dc, entry_, &w, &h);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common/File/Path.h"
|
||||
#include "Common/UI/View.h"
|
||||
#include "Common/UI/UIScreen.h"
|
||||
|
@ -29,6 +31,7 @@ private:
|
|||
class RetroAchievementsSettingsScreen : public TabbedUIDialogScreenWithGameBackground {
|
||||
public:
|
||||
RetroAchievementsSettingsScreen(const Path &gamePath) : TabbedUIDialogScreenWithGameBackground(gamePath) {}
|
||||
~RetroAchievementsSettingsScreen();
|
||||
const char *tag() const override { return "RetroAchievementsSettingsScreen"; }
|
||||
|
||||
void CreateTabs() override;
|
||||
|
@ -47,20 +50,29 @@ private:
|
|||
|
||||
class RetroAchievementsLeaderboardScreen : public TabbedUIDialogScreenWithGameBackground {
|
||||
public:
|
||||
RetroAchievementsLeaderboardScreen(const Path &gamePath, int leaderboardID) : TabbedUIDialogScreenWithGameBackground(gamePath), leaderboardID_(leaderboardID) {}
|
||||
RetroAchievementsLeaderboardScreen(const Path &gamePath, int leaderboardID);
|
||||
~RetroAchievementsLeaderboardScreen();
|
||||
|
||||
const char *tag() const override { return "RetroAchievementsLeaderboardScreen"; }
|
||||
|
||||
void CreateTabs() override;
|
||||
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
bool ShowSearchControls() const override { return false; }
|
||||
|
||||
private:
|
||||
void Poll();
|
||||
|
||||
int leaderboardID_;
|
||||
bool done_ = false;
|
||||
std::vector<Achievements::LeaderboardEntry> entries_;
|
||||
|
||||
// Keep the fetched list alive and destroy in destructor.
|
||||
rc_client_leaderboard_entry_list_t *entryList_ = nullptr;
|
||||
|
||||
rc_client_leaderboard_entry_list_t *pendingEntryList_ = nullptr;
|
||||
|
||||
rc_client_async_handle_t *pendingAsyncCall_ = nullptr;
|
||||
};
|
||||
|
||||
class UIContext;
|
||||
|
@ -68,41 +80,66 @@ class UIContext;
|
|||
enum class AchievementRenderStyle {
|
||||
LISTED,
|
||||
UNLOCKED,
|
||||
PROGRESS_INDICATOR,
|
||||
CHALLENGE_INDICATOR,
|
||||
};
|
||||
|
||||
void MeasureAchievement(const UIContext &dc, const Achievements::Achievement &achievement, float *w, float *h);
|
||||
void RenderAchievement(UIContext &dc, const Achievements::Achievement &achievement, AchievementRenderStyle style, const Bounds &bounds, float alpha, float startTime, float time_s);
|
||||
void MeasureGameAchievementSummary(const UIContext &dc, int gameID, float *w, float *h);
|
||||
void RenderGameAchievementSummary(UIContext &dc, int gameID, const Bounds &bounds, float alpha);
|
||||
void MeasureAchievement(const UIContext &dc, const rc_client_achievement_t *achievement, AchievementRenderStyle style, float *w, float *h);
|
||||
void RenderAchievement(UIContext &dc, const rc_client_achievement_t *achievement, AchievementRenderStyle style, const Bounds &bounds, float alpha, float startTime, float time_s);
|
||||
|
||||
void MeasureGameAchievementSummary(const UIContext &dc, float *w, float *h);
|
||||
void RenderGameAchievementSummary(UIContext &dc, const Bounds &bounds, float alpha);
|
||||
|
||||
void MeasureLeaderboardEntry(const UIContext &dc, const rc_client_leaderboard_entry_t *entry, float *w, float *h);
|
||||
void RenderLeaderboardEntry(UIContext &dc, const rc_client_leaderboard_entry_t *entry, const Bounds &bounds, float alpha);
|
||||
|
||||
class AchievementView : public UI::ClickableItem {
|
||||
public:
|
||||
AchievementView(const Achievements::Achievement &&achievement, UI::LayoutParams *layoutParams = nullptr) : UI::ClickableItem(layoutParams), achievement_(achievement) {}
|
||||
AchievementView(const rc_client_achievement_t *achievement, UI::LayoutParams *layoutParams = nullptr) : UI::ClickableItem(layoutParams), achievement_(achievement) {
|
||||
layoutParams_->height = UI::WRAP_CONTENT; // Override the standard Item fixed height.
|
||||
}
|
||||
|
||||
void Click() override;
|
||||
void Draw(UIContext &dc) override;
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
private:
|
||||
Achievements::Achievement achievement_;
|
||||
};
|
||||
|
||||
class LeaderboardSummaryView : public UI::ClickableItem {
|
||||
public:
|
||||
LeaderboardSummaryView(const Achievements::Leaderboard &&leaderboard, UI::LayoutParams *layoutParams = nullptr) : UI::ClickableItem(layoutParams), leaderboard_(leaderboard) {}
|
||||
|
||||
void Draw(UIContext &dc) override;
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
|
||||
private:
|
||||
Achievements::Leaderboard leaderboard_;
|
||||
const rc_client_achievement_t *achievement_;
|
||||
};
|
||||
|
||||
class GameAchievementSummaryView : public UI::Item {
|
||||
public:
|
||||
GameAchievementSummaryView(int gameID, UI::LayoutParams *layoutParams = nullptr) : UI::Item(layoutParams), gameID_(gameID) {}
|
||||
GameAchievementSummaryView(UI::LayoutParams *layoutParams = nullptr) : UI::Item(layoutParams) {
|
||||
layoutParams_->height = UI::WRAP_CONTENT; // Override the standard Item fixed height.
|
||||
}
|
||||
|
||||
void Draw(UIContext &dc) override;
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
private:
|
||||
int gameID_;
|
||||
};
|
||||
|
||||
class LeaderboardSummaryView : public UI::ClickableItem {
|
||||
public:
|
||||
LeaderboardSummaryView(const rc_client_leaderboard_t *leaderboard, UI::LayoutParams *layoutParams = nullptr) : UI::ClickableItem(layoutParams), leaderboard_(leaderboard) {
|
||||
layoutParams_->height = UI::WRAP_CONTENT; // Override the standard Item fixed height.
|
||||
}
|
||||
|
||||
void Draw(UIContext &dc) override;
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
|
||||
private:
|
||||
const rc_client_leaderboard_t *leaderboard_;
|
||||
};
|
||||
|
||||
class LeaderboardEntryView : public UI::Item {
|
||||
public:
|
||||
LeaderboardEntryView(const rc_client_leaderboard_entry_t *entry, bool isCurrentUser, UI::LayoutParams *layoutParams = nullptr)
|
||||
: UI::Item(layoutParams), entry_(entry), isCurrentUser_(isCurrentUser) {
|
||||
layoutParams_->height = UI::WRAP_CONTENT; // Override the standard Item fixed height.
|
||||
}
|
||||
|
||||
void Draw(UIContext &dc) override;
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
|
||||
private:
|
||||
const rc_client_leaderboard_entry_t *entry_;
|
||||
bool isCurrentUser_;
|
||||
};
|
||||
|
|
|
@ -106,6 +106,9 @@
|
|||
<ClInclude Include="Theme.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.vcxproj">
|
||||
<Project>{533f1d30-d04d-47cc-ad71-20f658907e36}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ext\discord-rpc-build\discord-rpc.vcxproj">
|
||||
<Project>{beb0a821-3c7f-410f-a525-63afbc69bf8f}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<ClInclude Include="..\..\ext\rcheevos\include\rc_api_request.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_api_runtime.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_api_user.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_client.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_consoles.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_error.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_hash.h" />
|
||||
|
@ -48,6 +49,7 @@
|
|||
<ClInclude Include="..\..\ext\rcheevos\include\rc_runtime_types.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_url.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\src\rapi\rc_api_common.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\src\rcheevos\rc_client_internal.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\src\rcheevos\rc_compat.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\src\rcheevos\rc_internal.h" />
|
||||
<ClInclude Include="..\..\ext\rcheevos\src\rcheevos\rc_validate.h" />
|
||||
|
@ -69,6 +71,7 @@
|
|||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\lboard.c" />
|
||||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\memref.c" />
|
||||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\operand.c" />
|
||||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\rc_client.c" />
|
||||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\rc_validate.c" />
|
||||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\richpresence.c" />
|
||||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\runtime.c" />
|
||||
|
@ -103,8 +106,10 @@
|
|||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings"></ImportGroup>
|
||||
<ImportGroup Label="Shared"></ImportGroup>
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
|
|
@ -55,6 +55,12 @@
|
|||
<ClInclude Include="..\..\ext\rcheevos\src\rhash\md5.h">
|
||||
<Filter>rhash</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\rcheevos\src\rcheevos\rc_client_internal.h">
|
||||
<Filter>rcheevos</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\rcheevos\include\rc_client.h">
|
||||
<Filter>include</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="include">
|
||||
|
@ -140,5 +146,8 @@
|
|||
<ClCompile Include="..\..\ext\rcheevos\src\rhash\md5.c">
|
||||
<Filter>rhash</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\ext\rcheevos\src\rcheevos\rc_client.c">
|
||||
<Filter>rcheevos</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -51,6 +51,7 @@
|
|||
#include "Core/HLE/sceUmd.h"
|
||||
#include "Core/SaveState.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/RetroAchievements.h"
|
||||
|
||||
extern bool g_TakeScreenshot;
|
||||
|
||||
|
@ -69,14 +70,23 @@ namespace MainWindow {
|
|||
LRESULT CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
void SetIngameMenuItemStates(HMENU menu, const GlobalUIState state) {
|
||||
UINT menuEnable = state == UISTATE_INGAME || state == UISTATE_EXCEPTION ? MF_ENABLED : MF_GRAYED;
|
||||
bool menuEnableBool = state == UISTATE_INGAME || state == UISTATE_EXCEPTION;
|
||||
|
||||
bool saveStateEnableBool = menuEnableBool;
|
||||
if (Achievements::ChallengeModeActive()) {
|
||||
saveStateEnableBool = false;
|
||||
}
|
||||
|
||||
UINT menuEnable = menuEnableBool ? MF_ENABLED : MF_GRAYED;
|
||||
UINT saveStateEnable = saveStateEnableBool ? MF_ENABLED : MF_GRAYED;
|
||||
UINT menuInGameEnable = state == UISTATE_INGAME ? MF_ENABLED : MF_GRAYED;
|
||||
UINT umdSwitchEnable = state == UISTATE_INGAME && getUMDReplacePermit() ? MF_ENABLED : MF_GRAYED;
|
||||
|
||||
EnableMenuItem(menu, ID_FILE_SAVESTATEFILE, menuEnable);
|
||||
EnableMenuItem(menu, ID_FILE_LOADSTATEFILE, menuEnable);
|
||||
EnableMenuItem(menu, ID_FILE_QUICKSAVESTATE, menuEnable);
|
||||
EnableMenuItem(menu, ID_FILE_QUICKLOADSTATE, menuEnable);
|
||||
EnableMenuItem(menu, ID_FILE_SAVESTATE_SLOT_MENU, saveStateEnable);
|
||||
EnableMenuItem(menu, ID_FILE_SAVESTATEFILE, saveStateEnable);
|
||||
EnableMenuItem(menu, ID_FILE_LOADSTATEFILE, saveStateEnable);
|
||||
EnableMenuItem(menu, ID_FILE_QUICKSAVESTATE, saveStateEnable);
|
||||
EnableMenuItem(menu, ID_FILE_QUICKLOADSTATE, saveStateEnable);
|
||||
EnableMenuItem(menu, ID_EMULATION_PAUSE, menuEnable);
|
||||
EnableMenuItem(menu, ID_EMULATION_STOP, menuEnable);
|
||||
EnableMenuItem(menu, ID_EMULATION_RESET, menuEnable);
|
||||
|
@ -517,70 +527,87 @@ namespace MainWindow {
|
|||
}
|
||||
break;
|
||||
case ID_FILE_LOADSTATEFILE:
|
||||
if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::Load(Path(fn), -1, SaveStateActionFinished);
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::Load(Path(fn), -1, SaveStateActionFinished);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ID_FILE_SAVESTATEFILE:
|
||||
if (W32Util::BrowseForFileName(false, hWnd, L"Save state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::Save(Path(fn), -1, SaveStateActionFinished);
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (W32Util::BrowseForFileName(false, hWnd, L"Save state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::Save(Path(fn), -1, SaveStateActionFinished);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ID_FILE_SAVESTATE_NEXT_SLOT:
|
||||
{
|
||||
SaveState::NextSlot();
|
||||
NativeMessageReceived("savestate_displayslot", "");
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_FILE_SAVESTATE_NEXT_SLOT_HC:
|
||||
{
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_NEXT_SLOT)) {
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
SaveState::NextSlot();
|
||||
NativeMessageReceived("savestate_displayslot", "");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_FILE_SAVESTATE_SLOT_1: g_Config.iCurrentStateSlot = 0; break;
|
||||
case ID_FILE_SAVESTATE_SLOT_2: g_Config.iCurrentStateSlot = 1; break;
|
||||
case ID_FILE_SAVESTATE_SLOT_3: g_Config.iCurrentStateSlot = 2; break;
|
||||
case ID_FILE_SAVESTATE_SLOT_4: g_Config.iCurrentStateSlot = 3; break;
|
||||
case ID_FILE_SAVESTATE_SLOT_5: g_Config.iCurrentStateSlot = 4; break;
|
||||
|
||||
case ID_FILE_QUICKLOADSTATE:
|
||||
case ID_FILE_SAVESTATE_NEXT_SLOT_HC:
|
||||
{
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_NEXT_SLOT)) {
|
||||
SaveState::NextSlot();
|
||||
NativeMessageReceived("savestate_displayslot", "");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_FILE_QUICKLOADSTATE_HC:
|
||||
{
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_LOAD_STATE))
|
||||
{
|
||||
case ID_FILE_SAVESTATE_SLOT_1:
|
||||
case ID_FILE_SAVESTATE_SLOT_2:
|
||||
case ID_FILE_SAVESTATE_SLOT_3:
|
||||
case ID_FILE_SAVESTATE_SLOT_4:
|
||||
case ID_FILE_SAVESTATE_SLOT_5:
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
g_Config.iCurrentStateSlot = wmId - ID_FILE_SAVESTATE_SLOT_1;
|
||||
}
|
||||
break;
|
||||
|
||||
case ID_FILE_QUICKLOADSTATE:
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
}
|
||||
break;
|
||||
|
||||
case ID_FILE_QUICKLOADSTATE_HC:
|
||||
{
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_LOAD_STATE)) {
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ID_FILE_QUICKSAVESTATE:
|
||||
{
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_FILE_QUICKSAVESTATE_HC:
|
||||
{
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_SAVE_STATE))
|
||||
{
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
break;
|
||||
if (!Achievements::WarnUserIfChallengeModeActive()) {
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_SAVE_STATE))
|
||||
{
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ RCHEEVOS_FILES := \
|
|||
${SRC}/ext/rcheevos/src/rcheevos/lboard.c \
|
||||
${SRC}/ext/rcheevos/src/rcheevos/memref.c \
|
||||
${SRC}/ext/rcheevos/src/rcheevos/operand.c \
|
||||
${SRC}/ext/rcheevos/src/rcheevos/rc_client.c \
|
||||
${SRC}/ext/rcheevos/src/rcheevos/rc_validate.c \
|
||||
${SRC}/ext/rcheevos/src/rcheevos/richpresence.c \
|
||||
${SRC}/ext/rcheevos/src/rcheevos/runtime.c \
|
||||
|
@ -103,8 +104,7 @@ RCHEEVOS_FILES := \
|
|||
${SRC}/ext/rcheevos/src/rcheevos/value.c \
|
||||
${SRC}/ext/rcheevos/src/rhash/cdreader.c \
|
||||
${SRC}/ext/rcheevos/src/rhash/hash.c \
|
||||
${SRC}/ext/rcheevos/src/rhash/md5.c \
|
||||
${SRC}/ext/rcheevos/src/rurl/url.c
|
||||
${SRC}/ext/rcheevos/src/rhash/md5.c
|
||||
|
||||
VR_FILES := \
|
||||
$(SRC)/Common/VR/OpenXRLoader.cpp \
|
||||
|
|
|
@ -24,23 +24,30 @@
|
|||
|
||||
[Achievements]
|
||||
%d achievements, %d points = %d achievements, %d points
|
||||
%1: Leaderboard attempt started = %1: Leaderboard attempt started
|
||||
%1: Leaderboard attempt failed = %1: Leaderboard attempt failed
|
||||
%1: Submitting leaderboard score: %2! = %1: Submitting leaderboard score: %2!
|
||||
Account = Account
|
||||
Achievements = Achievements
|
||||
Achievements are disabled = Achievements are disabled
|
||||
Challenge Mode = Challenge Mode
|
||||
Challenge Mode (no savestates) = Challenge Mode (no savestates)
|
||||
Earned = You have earned %d of %d achievements, and %d of %d points
|
||||
Encore Mode = Encore Mode
|
||||
How to use RetroAchievements = How to use RetroAchievements
|
||||
Leaderboard submission is enabled = Leaderboard submission is enabled
|
||||
Leaderboards = Leaderboards
|
||||
Links = Links
|
||||
Local = Local
|
||||
Locked achievements = Locked achievements
|
||||
Log bad memory accesses = Log bad memory accesses
|
||||
Mastered %1 = Mastered %1
|
||||
This feature is not available in Challenge Mode = This feature is not available in Challenge Mode
|
||||
Save states not available in Challenge Mode = Save states not available in Challenge Mode
|
||||
Register on www.retroachievements.org = Register on www.retroachievements.org
|
||||
RetroAchievements are not available for this game = RetroAchievements are not available for this game
|
||||
RetroAchievements website = RetroAchievements website
|
||||
Rich Presence = Rich Presence
|
||||
Save state loaded without achievement data = Save state loaded without achievement data
|
||||
Sound Effects = Sound Effects
|
||||
Statistics = Statistics
|
||||
Submitted Score = Submitted Score
|
||||
|
@ -49,7 +56,7 @@ Syncing achievements data... = Syncing achievements data...
|
|||
Test Mode = Test Mode
|
||||
This game has no achievements = This game has no achievements
|
||||
Unlocked achievements = Unlocked achievements
|
||||
Unofficial = Unofficial
|
||||
Unofficial achievements = Unofficial achievements
|
||||
|
||||
[Audio]
|
||||
Alternate speed volume = Alternate speed volume
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d176b4bcb42488da84f55991fd10f8ff36183384
|
||||
Subproject commit 86374e5731bb2f671a535a585b06afb03a17e047
|
|
@ -23,6 +23,8 @@ set(ALL_SOURCE_FILES
|
|||
${SRC_DIR}/rcheevos/memref.c
|
||||
${SRC_DIR}/rcheevos/operand.c
|
||||
${SRC_DIR}/rcheevos/rc_compat.h
|
||||
${SRC_DIR}/rcheevos/rc_client.c
|
||||
${SRC_DIR}/rcheevos/rc_client_internal.h
|
||||
${SRC_DIR}/rcheevos/rc_internal.h
|
||||
${SRC_DIR}/rcheevos/rc_validate.h
|
||||
${SRC_DIR}/rcheevos/rc_validate.c
|
||||
|
@ -37,8 +39,6 @@ set(ALL_SOURCE_FILES
|
|||
${SRC_DIR}/rhash/hash.c
|
||||
${SRC_DIR}/rhash/md5.c
|
||||
${SRC_DIR}/rhash/md5.h
|
||||
# rurl
|
||||
${SRC_DIR}/rurl/url.c
|
||||
)
|
||||
|
||||
add_library(rcheevos STATIC ${ALL_SOURCE_FILES})
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<ClCompile Include="..\rcheevos\src\rcheevos\lboard.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rcheevos\memref.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rcheevos\operand.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rcheevos\rc_client.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rcheevos\rc_validate.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rcheevos\richpresence.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rcheevos\runtime.c" />
|
||||
|
@ -58,7 +59,6 @@
|
|||
<ClCompile Include="..\rcheevos\src\rhash\cdreader.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rhash\hash.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rhash\md5.c" />
|
||||
<ClCompile Include="..\rcheevos\src\rurl\url.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\rcheevos\include\rcheevos.h" />
|
||||
|
@ -67,6 +67,7 @@
|
|||
<ClInclude Include="..\rcheevos\include\rc_api_request.h" />
|
||||
<ClInclude Include="..\rcheevos\include\rc_api_runtime.h" />
|
||||
<ClInclude Include="..\rcheevos\include\rc_api_user.h" />
|
||||
<ClInclude Include="..\rcheevos\include\rc_client.h" />
|
||||
<ClInclude Include="..\rcheevos\include\rc_consoles.h" />
|
||||
<ClInclude Include="..\rcheevos\include\rc_error.h" />
|
||||
<ClInclude Include="..\rcheevos\include\rc_hash.h" />
|
||||
|
@ -74,6 +75,7 @@
|
|||
<ClInclude Include="..\rcheevos\include\rc_runtime_types.h" />
|
||||
<ClInclude Include="..\rcheevos\include\rc_url.h" />
|
||||
<ClInclude Include="..\rcheevos\src\rapi\rc_api_common.h" />
|
||||
<ClInclude Include="..\rcheevos\src\rcheevos\rc_client_internal.h" />
|
||||
<ClInclude Include="..\rcheevos\src\rcheevos\rc_compat.h" />
|
||||
<ClInclude Include="..\rcheevos\src\rcheevos\rc_internal.h" />
|
||||
<ClInclude Include="..\rcheevos\src\rcheevos\rc_validate.h" />
|
||||
|
|
|
@ -10,9 +10,6 @@
|
|||
<Filter Include="rhash">
|
||||
<UniqueIdentifier>{55ec06aa-6d34-4edf-8b0b-f93f93a064b4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="rurl">
|
||||
<UniqueIdentifier>{990047ac-f0d4-44f5-8463-54f898557d02}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="include">
|
||||
<UniqueIdentifier>{9e049d1f-4b83-4aa5-89f3-01a42e1773e2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -87,8 +84,8 @@
|
|||
<ClCompile Include="..\rcheevos\src\rhash\md5.c">
|
||||
<Filter>rhash</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\rcheevos\src\rurl\url.c">
|
||||
<Filter>rurl</Filter>
|
||||
<ClCompile Include="..\rcheevos\src\rcheevos\rc_client.c">
|
||||
<Filter>rcheevos</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -146,5 +143,11 @@
|
|||
<ClInclude Include="..\rcheevos\src\rhash\md5.h">
|
||||
<Filter>rhash</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\rcheevos\include\rc_client.h">
|
||||
<Filter>include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\rcheevos\src\rcheevos\rc_client_internal.h">
|
||||
<Filter>rcheevos</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -143,6 +143,12 @@ void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\
|
|||
void System_AudioClear() {}
|
||||
void System_AudioPushSamples(const s32 *audio, int numSamples) {}
|
||||
|
||||
// TODO: To avoid having to define these here, these should probably be turned into system "requests".
|
||||
void NativeSaveSecret(const char *nameOfSecret, const std::string &data) {}
|
||||
std::string NativeLoadSecret(const char *nameOfSecret) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int printUsage(const char *progname, const char *reason)
|
||||
{
|
||||
if (reason != NULL)
|
||||
|
|
|
@ -508,6 +508,9 @@
|
|||
<ProjectReference Include="..\ext\libzstd.vcxproj">
|
||||
<Project>{8bfd8150-94d5-4bf9-8a50-7bd9929a0850}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ext\rcheevos-build\rcheevos.vcxproj">
|
||||
<Project>{31694510-a8c0-40f6-b09b-e8df825adefa}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GPU\GPU.vcxproj">
|
||||
<Project>{457f45d2-556f-47bc-a31d-aff0d15beaed}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -212,6 +212,7 @@ SOURCES_C += \
|
|||
$(EXTDIR)/rcheevos/src/rcheevos/lboard.c \
|
||||
$(EXTDIR)/rcheevos/src/rcheevos/memref.c \
|
||||
$(EXTDIR)/rcheevos/src/rcheevos/operand.c \
|
||||
$(EXTDIR)/rcheevos/src/rcheevos/rc_client.c \
|
||||
$(EXTDIR)/rcheevos/src/rcheevos/rc_validate.c \
|
||||
$(EXTDIR)/rcheevos/src/rcheevos/richpresence.c \
|
||||
$(EXTDIR)/rcheevos/src/rcheevos/runtime.c \
|
||||
|
|
|
@ -103,6 +103,12 @@ void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\
|
|||
void System_AudioClear() {}
|
||||
void System_AudioPushSamples(const s32 *audio, int numSamples) {}
|
||||
|
||||
// TODO: To avoid having to define these here, these should probably be turned into system "requests".
|
||||
void NativeSaveSecret(const char *nameOfSecret, const std::string &data) {}
|
||||
std::string NativeLoadSecret(const char *nameOfSecret) {
|
||||
return "";
|
||||
}
|
||||
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
JNIEnv *getEnv() {
|
||||
return nullptr;
|
||||
|
|
|
@ -426,15 +426,15 @@
|
|||
<ProjectReference Include="..\ext\libzstd.vcxproj">
|
||||
<Project>{8bfd8150-94d5-4bf9-8a50-7bd9929a0850}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ext\rcheevos-build\rcheevos.vcxproj">
|
||||
<Project>{31694510-a8c0-40f6-b09b-e8df825adefa}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ext\zlib\zlib.vcxproj">
|
||||
<Project>{f761046e-6c38-4428-a5f1-38391a37bb34}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GPU\GPU.vcxproj">
|
||||
<Project>{457f45d2-556f-47bc-a31d-aff0d15beaed}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\UI\UI.vcxproj">
|
||||
<Project>{004b8d11-2be3-4bd9-ab40-2be04cf2096f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="JitHarness.h" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue