Switch over to rc-client

This commit is contained in:
Henrik Rydgård 2023-06-27 23:31:15 +02:00
parent 0bf0a4a1ef
commit d0b42705a0
20 changed files with 698 additions and 1922 deletions

View file

@ -17,6 +17,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 +39,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 +112,28 @@ 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::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);

View file

@ -16,6 +16,10 @@ enum class OSDType {
ACHIEVEMENT_UNLOCKED,
// Side entries
ACHIEVEMENT_PROGRESS,
ACHIEVEMENT_CHALLENGE_INDICATOR,
// PROGRESS_BAR,
// PROGRESS_INDETERMINATE,
};
@ -33,6 +37,8 @@ public:
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 ShowAchievementProgress(int achievementID, float duration_s);
void ShowOnOff(const std::string &message, bool on, float duration_s = 0.0f);
bool IsEmpty() const { return entries_.empty(); } // Shortcut to skip rendering.
@ -66,10 +72,12 @@ public:
};
std::vector<Entry> Entries();
std::vector<Entry> SideEntries();
std::vector<ProgressBar> ProgressBars();
private:
std::vector<Entry> entries_;
std::vector<Entry> sideEntries_;
std::vector<ProgressBar> bars_;
std::mutex mutex_;
};

View file

@ -266,9 +266,6 @@ 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("AchievementsSoundEffects", &g_Config.bAchievementsSoundEffects, true, CfgFlag::DEFAULT),
@ -276,10 +273,10 @@ static const ConfigSetting achievementSettings[] = {
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[] = {

View file

@ -488,9 +488,6 @@ 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 bAchievementsSoundEffects;
@ -501,7 +498,6 @@ public:
// 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.

File diff suppressed because it is too large Load diff

View file

@ -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,46 @@ static inline bool IsUsingRAIntegration()
#endif
bool IsActive();
bool IsLoggedIn();
bool ChallengeModeActive();
bool LeaderboardsActive();
bool IsTestModeActive();
bool IsUnofficialTestModeActive();
bool IsRichPresenceEnabled();
bool HasActiveGame();
u32 GetGameID();
/// Acquires the achievements lock. Must be held when accessing any achievement state from another thread.
std::unique_lock<std::recursive_mutex> GetLock();
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 FrameUpdate();
/// Called when the system is paused, because FrameUpdate() won't be getting called.
void ProcessPendingHTTPRequests();
/// Saves/loads state.
bool DoState(PointerWrap &sw);
/// 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 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();
// 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();
bool EnumerateAchievements(std::function<bool(const Achievement &)> callback);
u32 GetGameID();
// TODO: Make these support multiple games, not just the current games, with cached info.
u32 GetUnlockedAchiementCount();
u32 GetAchievementCount();
u32 GetMaximumPointsForGame();
u32 GetCurrentPointsForGame();
void Initialize();
void UpdateSettings();
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted if possible.
bool Shutdown();
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 every frame to let background processing happen.
void Idle();
/// Saves/loads state.
void DoState(PointerWrap &p);
/// Returns true if the current game has any achievements or leaderboards.
bool HasAchievementsOrLeaderboards();
bool LoginAsync(const char *username, const char *password);
void Logout();
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

View file

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

View file

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

View file

@ -53,8 +53,10 @@ static NoticeLevel GetNoticeLevel(OSDType type) {
}
}
// Align only matters here for the ASCII-only flag.
static void MeasureNotice(const UIContext &dc, NoticeLevel level, const std::string &text, const std::string &details, const std::string &iconName, int align, float *width, float *height, float *height1) {
dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, text.c_str(), width, height, align);
*height1 = *height;
float width2 = 0.0f, height2 = 0.0f;
@ -85,8 +87,8 @@ static void MeasureNotice(const UIContext &dc, NoticeLevel level, const std::str
// Align only matters here for the ASCII-only flag.
static void MeasureOSDEntry(const 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);
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
MeasureAchievement(dc, achievement, AchievementRenderStyle::UNLOCKED, width, height);
*width = 550.0f;
*height1 = *height;
} else {
@ -141,12 +143,14 @@ static void RenderNotice(UIContext &dc, Bounds bounds, float height1, NoticeLeve
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;
} else {
RenderNotice(dc, bounds, height1, GetNoticeLevel(entry.type), entry.text, entry.text2, entry.iconName, align, alpha);
}
RenderNotice(dc, bounds, height1, GetNoticeLevel(entry.type), entry.text, entry.text2, entry.iconName, align, alpha);
}
static void MeasureOSDProgressBar(const UIContext &dc, const OnScreenDisplay::ProgressBar &bar, float *width, float *height) {
@ -204,11 +208,39 @@ void OnScreenMessagesView::Draw(UIContext &dc) {
double now = time_now_d();
float y = 10.0f;
// 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;
AchievementRenderStyle style = AchievementRenderStyle::PROGRESS_INDICATOR;
switch (entry.type) {
case OSDType::ACHIEVEMENT_PROGRESS:
{
const rc_client_achievement_t *achievement = rc_client_get_achievement_info(Achievements::GetClient(), entry.numericID);
style = AchievementRenderStyle::PROGRESS_INDICATOR;
MeasureAchievement(dc, achievement, style, &tw, &th);
break;
}
default:
continue;
}
Bounds b(10.0f, y, tw, th);
float alpha = Clamp((float)(entry.endTime - now) * 4.0f, 0.0f, 1.0f);
// OK, render the thing.
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) {
@ -223,19 +255,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);
@ -257,8 +304,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.
}

View file

@ -347,7 +347,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;

View file

@ -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,45 @@ void RetroAchievementsListScreen::CreateAchievementsTab(UI::ViewGroup *achieveme
using namespace UI;
std::vector<Achievements::Achievement> unlockedAchievements;
std::vector<Achievements::Achievement> lockedAchievements;
rc_client_achievement_list_t *list = rc_client_create_achievement_list(Achievements::GetClient(),
RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL, 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 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));
}
}
@ -70,21 +84,21 @@ void RetroAchievementsListScreen::CreateLeaderboardsTab(UI::ViewGroup *viewGroup
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 +112,57 @@ 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();
// 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 ? " <<<<< " : "")));
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];
// Can also show entry.submitted, which is a time_t. And maybe highlight recent ones?
layout->Add(new TextView(StringFromFormat(" %d: %s: %s%s", entry.rank, entry.user, entry.display, 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();
}
}
@ -161,7 +197,14 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup)
using namespace UI;
if (Achievements::IsLoggedIn()) {
viewGroup->Add(new InfoItem(ac->T("Username"), Achievements::GetUsername()));
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;
@ -224,9 +267,16 @@ void RetroAchievementsSettingsScreen::CreateSettingsTab(UI::ViewGroup *viewGroup
// 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;
*h = 72.0f;
switch (style) {
case AchievementRenderStyle::PROGRESS_INDICATOR:
*h = 36.0f;
break;
default:
*h = 72.0f;
break;
}
}
void MeasureGameAchievementSummary(const UIContext &dc, int gameID, float *w, float *h) {
@ -234,16 +284,18 @@ void MeasureGameAchievementSummary(const UIContext &dc, int gameID, float *w, fl
*h = 72.0f;
}
void MeasureLeaderboardSummary(const UIContext &dc, const Achievements::Leaderboard &achievement, float *w, float *h) {
void MeasureLeaderboardSummary(const UIContext &dc, const rc_client_leaderboard_t *leaderboard, 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) {
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 = 0x706060;
}
background.color = colorAlpha(background.color, alpha);
@ -263,13 +315,15 @@ void RenderAchievement(UIContext &dc, const Achievements::Achievement &achieveme
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);
dc.DrawTextRect(achievement->title, bounds.Inset(iconSpace + 12.0f, 2.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
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);
dc.DrawTextRect(achievement->description, bounds.Inset(iconSpace + 12.0f, 39.0f, 5.0f, 5.0f), fgColor, ALIGN_TOPLEFT);
char temp[64];
snprintf(temp, sizeof(temp), "%d", achievement.points);
// TODO: Draw measured_progress / measured_percent in a cute way
char temp[512];
snprintf(temp, sizeof(temp), "%d", achievement->points);
dc.SetFontScale(1.5f, 1.5f);
dc.DrawTextRect(temp, bounds.Expand(-5.0f, -5.0f), fgColor, ALIGN_RIGHT | ALIGN_VCENTER);
@ -277,9 +331,12 @@ void RenderAchievement(UIContext &dc, const Achievements::Achievement &achieveme
dc.SetFontScale(1.0f, 1.0f);
dc.Flush();
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));
// Download and display the image.
if (RC_OK == rc_client_achievement_get_image_url(achievement, achievement->state, temp, sizeof(temp))) {
Achievements::DownloadImageIfMissing(achievement->badge_name, std::move(std::string(temp)));
if (g_iconCache.BindIconTexture(&dc, achievement->badge_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.Flush();
@ -301,11 +358,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 +371,19 @@ 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 temp[512];
if (RC_OK == rc_client_game_get_image_url(gameInfo, temp, sizeof(temp))) {
Achievements::DownloadImageIfMissing(gameInfo->badge_name, std::move(std::string(temp)));
if (g_iconCache.BindIconTexture(&dc, gameInfo->badge_name)) {
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 +394,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 +402,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];
@ -367,13 +427,13 @@ void AchievementView::Draw(UIContext &dc) {
}
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);
g_OSD.ShowAchievementUnlocked(achievement_->id);
#endif
}

View file

@ -1,5 +1,7 @@
#pragma once
#include <cstdint>
#include "Common/File/Path.h"
#include "Common/UI/View.h"
#include "Common/UI/UIScreen.h"
@ -47,20 +49,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,33 +79,35 @@ class UIContext;
enum class AchievementRenderStyle {
LISTED,
UNLOCKED,
PROGRESS_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 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, int gameID, float *w, float *h);
void RenderGameAchievementSummary(UIContext &dc, int gameID, 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) {}
void Click() override;
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
private:
Achievements::Achievement achievement_;
const rc_client_achievement_t *achievement_;
};
class LeaderboardSummaryView : public UI::ClickableItem {
public:
LeaderboardSummaryView(const Achievements::Leaderboard &&leaderboard, UI::LayoutParams *layoutParams = nullptr) : UI::ClickableItem(layoutParams), leaderboard_(leaderboard) {}
LeaderboardSummaryView(const rc_client_leaderboard_t *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_leaderboard_t *leaderboard_;
};
class GameAchievementSummaryView : public UI::Item {

View file

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

View file

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

View file

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

@ -1 +1 @@
Subproject commit d176b4bcb42488da84f55991fd10f8ff36183384
Subproject commit 223df0761313c5c9d9f4d4dc4b5897cf41e7e036

View file

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

View file

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

View file

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

View file

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