Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retroachievements support (work-in-progress) #17589

Merged
merged 41 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c0f0c05
rcheevos initial build setup and basic scaffolding
hrydgard Jun 15, 2023
da1691e
Start sketching on a wrapper
hrydgard Jun 15, 2023
4745999
Copy-paste retroachievement support from DuckStation. No fixes yet!
hrydgard Jun 15, 2023
029b887
Initial batch of fixes. Not compiling yet, http will be the tricky one.
hrydgard Jun 15, 2023
c9c1796
Second batch of fixes. Doesn't link yet and much work remains.
hrydgard Jun 16, 2023
84e9a85
Things link now. Let's see if it works..
hrydgard Jun 16, 2023
66d9c24
More integration
hrydgard Jun 16, 2023
b84a6f8
You can now log into RetroAchievements with hardcoded user/pass
hrydgard Jun 16, 2023
a3b7e99
Attempt to implement the specified PSP game hashing method
hrydgard Jun 16, 2023
f6fdaa4
Get it to recognize the hash by using the rcheevos hash implementation
hrydgard Jun 16, 2023
8beca03
MacOSX buildfixes
hrydgard Jun 17, 2023
e054d66
Add an extremely basic achievement listing to the pause screen
hrydgard Jun 17, 2023
07e6543
Windows for ARM configuration fixes, Android.mk fix
hrydgard Jun 17, 2023
2ba8eca
Further UWP and Android fixes
hrydgard Jun 17, 2023
01dda6c
Final Android.mk fix hopefully.
hrydgard Jun 17, 2023
93de741
Add basic achievement rendering
hrydgard Jun 17, 2023
6326880
Properly include stenzek's DuckStation copyright notices in the retro…
hrydgard Jun 18, 2023
603dc74
Image caching work
hrydgard Jun 18, 2023
1d0bada
Achievements: Show achievement icons in list
hrydgard Jun 18, 2023
9fc6d12
Add RA logo image resource
hrydgard Jun 19, 2023
c379f6f
Implement retroachievements login (on platforms that support the logi…
hrydgard Jun 19, 2023
80ff3f8
Improve the pause screen achievement list, show a game banner at the top
hrydgard Jun 19, 2023
4e8c29d
Minor drawing issue
hrydgard Jun 20, 2023
54a378f
Fix bug in Achievements::SafeHasAchievementsOrLeaderboards()
hrydgard Jun 20, 2023
eaa1975
Improve logging on bad memory access from RA
hrydgard Jun 20, 2023
3b6989b
Hook up progress bar, minor fixes
hrydgard Jun 20, 2023
da36d78
De-spam the logs
hrydgard Jun 20, 2023
3218422
Attempt to add memory access validation through rc_runtime_invalidate…
hrydgard Jun 20, 2023
078002d
Show progress when downloading achievement data
hrydgard Jun 20, 2023
c442d1d
Implement rendering of unlocked achievements. Not final design.
hrydgard Jun 21, 2023
69a5ca0
A bunch of visual updates and fixes. Lots left to polish.
hrydgard Jun 21, 2023
9502d55
Fix
hrydgard Jun 21, 2023
d63415d
Set up a custom rc_hash_filereader to make things work on Android (lo…
hrydgard Jun 21, 2023
b0dc1db
Login: Support desktop platforms other than Windows.
hrydgard Jun 21, 2023
37fb3be
Visual improvements, notify if game unknown to RetroAchievements
hrydgard Jun 22, 2023
87846c5
Add simple facility for storing secret in app-private storage on Andr…
hrydgard Jun 25, 2023
864b2bb
Path: If a Path is empty, the slash operator will not add an addition…
hrydgard Jun 25, 2023
4134acc
Use the new "secret storage" to store the retroachievements token
hrydgard Jun 26, 2023
23e37b7
Achivement list rendering fix (wrong alpha)
hrydgard Jun 26, 2023
d4239e7
Keeping it simple - putting the token in memstick/PSP/SYSTEM for now
hrydgard Jun 26, 2023
81e1293
Token name and path fix
hrydgard Jun 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,5 @@ CMakeFiles
.cache/
build
libretro/obj/local

ppsspp_retroachievements.dat
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@
[submodule "cpu_features"]
path = ext/cpu_features
url = https://github.com/google/cpu_features.git
[submodule "ext/rcheevos"]
path = ext/rcheevos
url = https://github.com/RetroAchievements/rcheevos.git
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,10 @@ list(APPEND NativeAppSource
UI/CustomButtonMappingScreen.cpp
UI/Theme.h
UI/Theme.cpp
UI/RetroAchievements.cpp
UI/RetroAchievements.h
UI/RetroAchievementScreens.cpp
UI/RetroAchievementScreens.h
)

if(ANDROID)
Expand Down Expand Up @@ -2212,7 +2216,7 @@ else()
include_directories(ext/zstd/lib)
endif()

target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash ${GlslangLibs}
target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash rcheevos ${GlslangLibs}
${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS})

target_compile_features(${CoreLibName} PUBLIC cxx_std_17)
Expand Down
2 changes: 1 addition & 1 deletion Common/Data/Collections/TinySet.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ struct FixedTinyVec {
bool operator == (const FixedTinyVec<T, MaxSize> &other) const {
if (count_ != other.count_)
return false;
for (size_t i = 0; i < count_; i++) {
for (int i = 0; i < count_; i++) {
if (!(data_[i] == other.data_[i])) {
return false;
}
Expand Down
1 change: 1 addition & 0 deletions Common/Data/Text/I18n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ static const char * const g_categoryNames[(size_t)I18NCat::CATEGORY_COUNT] = {
"UI Elements",
"Upgrade",
"VR",
"Achievements",
};

I18NRepo g_i18nrepo;
Expand Down
1 change: 1 addition & 0 deletions Common/Data/Text/I18n.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ enum class I18NCat : uint8_t {
UI_ELEMENTS,
UPGRADE,
VR,
ACHIEVEMENTS,
CATEGORY_COUNT,
NONE = CATEGORY_COUNT,
};
Expand Down
13 changes: 12 additions & 1 deletion Common/File/Path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ Path Path::operator /(const std::string &subdir) const {
}

// Direct string manipulation.

if (path_.empty()) {
return Path(subdir);
}
if (subdir.empty()) {
return Path(path_);
}
Expand Down Expand Up @@ -258,6 +260,15 @@ std::wstring Path::ToWString() const {
}
return w;
}
std::string Path::ToCString() const {
std::string w = path_;
for (size_t i = 0; i < w.size(); i++) {
if (w[i] == '/') {
w[i] = '\\';
}
}
return w;
}
#endif

std::string Path::ToVisualString(const char *relativeRoot) const {
Expand Down
5 changes: 5 additions & 0 deletions Common/File/Path.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ class Path {

#if PPSSPP_PLATFORM(WINDOWS)
std::wstring ToWString() const;
std::string ToCString() const; // Flips the slashes back to Windows standard, but string still UTF-8.
#else
std::string ToCString() const {
return ToString();
}
#endif

// Pass in a relative root to turn the path into a relative path - if it is one!
Expand Down
3 changes: 3 additions & 0 deletions Common/Log.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ enum LOG_TYPE {
SCEMISC,

NUMBER_OF_LOGS, // Must be last

// Temporary aliases
ACHIEVEMENTS = HLE, // TODO: Make a real category
};

enum LOG_LEVELS : int {
Expand Down
7 changes: 7 additions & 0 deletions Common/Math/geom2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ struct Bounds {
return Bounds(x + left, y + top, w - left - right, h - bottom - top);
}

Bounds Inset(float xAmount, float yAmount) const {
return Bounds(x + xAmount, y + yAmount, w - xAmount * 2, h - yAmount * 2);
}
Bounds Inset(float left, float top, float right, float bottom) const {
return Bounds(x + left, y + top, w - left - right, h - top - bottom);
}

float x;
float y;
float w;
Expand Down
4 changes: 4 additions & 0 deletions Common/Net/HTTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ class Downloader {

std::vector<float> GetCurrentProgress();

size_t GetActiveCount() const {
return downloads_.size();
}

private:
std::vector<std::shared_ptr<Download>> downloads_;
// These get copied to downloads_ in Update(). It's so that callbacks can add new downloads
Expand Down
3 changes: 3 additions & 0 deletions Common/System/NativeApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,6 @@ void NativeShutdownGraphics();
void NativeShutdown();

void PostLoadConfig();

void NativeSaveSecret(const char *nameOfSecret, const std::string &data);
std::string NativeLoadSecret(const char *nameOfSecret);
18 changes: 17 additions & 1 deletion Common/System/Request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ std::vector<OnScreenDisplay::ProgressBar> OnScreenDisplay::ProgressBars() {
return bars_; // makes a copy.
}

void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::string &text2, float duration_s, const char *id) {
void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::string &text2, const std::string &icon, float duration_s, const char *id) {
// Automatic duration based on type.
if (duration_s <= 0.0f) {
switch (type) {
Expand Down Expand Up @@ -70,6 +70,7 @@ void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::str
msg.text = text;
msg.text2 = text2;
msg.type = type;
msg.iconName = icon;
// Move to top (should we? maybe not?)
entries_.erase(iter);
entries_.insert(entries_.begin(), msg);
Expand All @@ -81,12 +82,27 @@ void OnScreenDisplay::Show(OSDType type, const std::string &text, const std::str
Entry msg;
msg.text = text;
msg.text2 = text2;
msg.iconName = icon;
msg.startTime = now;
msg.endTime = now + duration_s;
msg.type = type;
msg.id = id;
entries_.insert(entries_.begin(), msg);
}

void OnScreenDisplay::ShowAchievementUnlocked(int achievementID) {
double now = time_now_d();

double duration_s = 5.0;

Entry msg;
msg.numericID = achievementID;
msg.type = OSDType::ACHIEVEMENT_UNLOCKED;
msg.startTime = now;
msg.endTime = now + duration_s;
entries_.insert(entries_.begin(), msg);
}

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);
Expand Down
14 changes: 12 additions & 2 deletions Common/System/System.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ enum class OSDType {
MESSAGE_ERROR,
MESSAGE_ERROR_DUMP, // displays lots of text (after the first line), small size
MESSAGE_FILE_LINK,

ACHIEVEMENT_UNLOCKED,

// PROGRESS_BAR,
// PROGRESS_INDETERMINATE,
};
Expand All @@ -234,7 +237,12 @@ class OnScreenDisplay {
void Show(OSDType type, const std::string &text, float duration_s = 0.0f, const char *id = nullptr) {
Show(type, text, "", duration_s, id);
}
void Show(OSDType type, const std::string &text, const std::string &text2, float duration_s = 0.0f, const char *id = nullptr);
void Show(OSDType type, const std::string &text, const std::string &text2, float duration_s = 0.0f, const char *id = nullptr) {
Show(type, text, "", "", 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);

bool IsEmpty() const { return entries_.empty(); } // Shortcut to skip rendering.
Expand All @@ -251,9 +259,11 @@ class OnScreenDisplay {
OSDType type;
std::string text;
std::string text2;
std::string iconName;
int numericID;
const char *id;
double startTime;
double endTime;
double duration;
};

struct ProgressBar {
Expand Down
10 changes: 10 additions & 0 deletions Common/UI/IconCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ class IconCache {

IconCacheStats GetStats();

// for testing
std::string GetFirstIconName() const {
Comment on lines +54 to +55
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what this is for specifically, but it should be using lock_ in case someone starts calling it later.

-[Unknown]

Copy link
Owner Author

@hrydgard hrydgard Jul 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never used it for anything, I'll just delete it. Was gonna be used to test achievement popups without getting achievements.

if (!cache_.empty()) {
return cache_.begin()->first;
} else if (!pending_.empty()) {
return *pending_.begin();
}
return "";
}

private:
struct Entry {
std::string data;
Expand Down
19 changes: 19 additions & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,24 @@ static bool DefaultSasThread() {
return cpu_info.num_cores > 1;
}

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("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),
ConfigSetting("AchievementsToken", &g_Config.sAchievementsToken, "", CfgFlag::DONT_SAVE),
ConfigSetting("AchievementsLoginTimestamp", &g_Config.sAchievementsLoginTimestamp, "", CfgFlag::DEFAULT),
};

static const ConfigSetting cpuSettings[] = {
ConfigSetting("CPUCore", &g_Config.iCpuCore, &DefaultCpuCore, CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("SeparateSASThread", &g_Config.bSeparateSASThread, &DefaultSasThread, CfgFlag::PER_GAME | CfgFlag::REPORT),
Expand Down Expand Up @@ -887,6 +905,7 @@ static const ConfigSectionSettings sections[] = {
{"Upgrade", upgradeSettings, ARRAY_SIZE(upgradeSettings)},
{"Theme", themeSettings, ARRAY_SIZE(themeSettings)},
{"VR", vrSettings, ARRAY_SIZE(vrSettings)},
{"Achievements", achievementSettings, ARRAY_SIZE(achievementSettings)},
};

const size_t numSections = ARRAY_SIZE(sections);
Expand Down
18 changes: 18 additions & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,24 @@ struct Config {
bool bShowFrameProfiler;
bool bGpuLogProfiler; // Controls the Vulkan logging profiler (profiles textures uploads etc).

// 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 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.
Path defaultCurrentDirectory; // Platform dependent, initialized at startup.
Expand Down
2 changes: 1 addition & 1 deletion Core/HLE/sceSas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ static u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int right

static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
return hleLogVerbose(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
}

if (size == 0 || ((u32)size & 0xF) != 0) {
Expand Down
2 changes: 1 addition & 1 deletion UI/DevScreens.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class SystemInfoScreen : public TabbedUIDialogScreenWithGameBackground {
void CreateTabs() override;

protected:
bool ShowSearchControls() override { return false; }
bool ShowSearchControls() const override { return false; }
};

class AddressPromptScreen : public PopupScreen {
Expand Down
5 changes: 5 additions & 0 deletions UI/EmuScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ using namespace std::placeholders;
#include "UI/ProfilerDraw.h"
#include "UI/DiscordIntegration.h"
#include "UI/ChatScreen.h"
#include "UI/RetroAchievements.h"

#include "Core/Reporting.h"

Expand Down Expand Up @@ -346,6 +347,8 @@ 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);

loadingViewColor_->Divert(0xFFFFFFFF, 0.75f);
loadingViewVisible_->Divert(UI::V_VISIBLE, 0.75f);

Expand Down Expand Up @@ -1126,6 +1129,8 @@ void EmuScreen::update() {
}
}
}

Achievements::FrameUpdate();
}

void EmuScreen::checkPowerDown() {
Expand Down
6 changes: 6 additions & 0 deletions UI/GameSettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "UI/GPUDriverTestScreen.h"
#include "UI/MemStickScreen.h"
#include "UI/Theme.h"
#include "UI/RetroAchievementScreens.h"

#include "Common/File/FileUtil.h"
#include "Common/File/AndroidContentURI.h"
Expand Down Expand Up @@ -852,6 +853,11 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {

systemSettings->Add(new ItemHeader(sy->T("UI")));

systemSettings->Add(new Choice(sy->T("RetroAchievements")))->OnClick.Add([&](UI::EventParams &) -> UI::EventReturn {
screenManager()->push(new RetroAchievementsSettingsScreen(gamePath_));
return UI::EVENT_DONE;
});

auto langCodeToName = [](const char *value) -> std::string {
auto &mapping = g_Config.GetLangValuesMapping();
auto iter = mapping.find(value);
Expand Down
1 change: 1 addition & 0 deletions UI/MiscScreens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,7 @@ void CreditsScreen::render() {
"adenovan",
"iota97",
"Lubos",
"stenzek", // For retroachievements integration
"",
cr->T("specialthanks", "Special thanks to:"),
specialthanksMaxim.c_str(),
Expand Down
Loading