Merge pull request #18442 from hrydgard/time-tracking

Track time-played per game
This commit is contained in:
Henrik Rydgård 2023-11-27 22:12:45 +01:00 committed by GitHub
commit 04c6b1ac99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 368 additions and 24 deletions

View file

@ -189,6 +189,15 @@ void Section::Clear() {
lines_.clear();
}
bool Section::GetKeys(std::vector<std::string> &keys) const {
keys.clear();
for (auto liter = lines_.begin(); liter != lines_.end(); ++liter) {
if (!liter->Key().empty())
keys.push_back(std::string(liter->Key()));
}
return true;
}
ParsedIniLine *Section::GetLine(const char *key) {
for (auto &line : lines_) {
if (equalsNoCase(line.Key(), key))
@ -482,12 +491,7 @@ bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) c
const Section *section = GetSection(sectionName);
if (!section)
return false;
keys.clear();
for (auto liter = section->lines_.begin(); liter != section->lines_.end(); ++liter) {
if (!liter->Key().empty())
keys.push_back(std::string(liter->Key()));
}
return true;
return section->GetKeys(keys);
}
void IniFile::SortSections()

View file

@ -105,6 +105,9 @@ public:
bool Get(const char* key, double* value, double defaultValue = false) const;
bool Get(const char* key, std::vector<std::string>& values) const;
// Return a list of all keys in this section
bool GetKeys(std::vector<std::string> &keys) const;
bool operator < (const Section& other) const {
return name_ < other.name_;
}

View file

@ -19,7 +19,7 @@ void NiceSizeFormat(uint64_t size, char *out, size_t bufSize) {
if (s == 0)
snprintf(out, bufSize, "%d B", (int)size);
else
snprintf(out, bufSize, "%3.1f %s", f, sizes[s]);
snprintf(out, bufSize, "%3.2f %s", f, sizes[s]);
}
std::string NiceSizeFormat(uint64_t size) {

View file

@ -337,7 +337,7 @@ void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLC
std::vector<std::string_view> lines;
SplitString(errorString, '\n', lines);
for (auto line : lines) {
ERROR_LOG(G3D, "%.*s", line.size(), line.data());
ERROR_LOG(G3D, "%.*s", (int)line.size(), line.data());
}
if (errorCallback_) {
std::string desc = StringFromFormat("Shader compilation failed: %s", step.create_shader.stage == GL_VERTEX_SHADER ? "vertex" : "fragment");

View file

@ -31,7 +31,7 @@ bool RequestHeader::GetParamValue(const char *param_name, std::string *value) co
for (size_t i = 0; i < v.size(); i++) {
std::vector<std::string_view> parts;
SplitString(v[i], '=', parts);
DEBUG_LOG(IO, "Param: %.*s Value: %.*s", parts[0].size(), parts[0].data(), parts[1].size(), parts[1].data());
DEBUG_LOG(IO, "Param: %.*s Value: %.*s", (int)parts[0].size(), parts[0].data(), (int)parts[1].size(), parts[1].data());
if (parts[0] == param_name) {
*value = parts[1];
return true;

View file

@ -68,6 +68,20 @@ double from_time_raw_relative(uint64_t raw_time) {
return from_time_raw(raw_time);
}
double time_now_unix_utc() {
const int64_t UNIX_TIME_START = 0x019DB1DED53E8000; //January 1, 1970 (start of Unix epoch) in "ticks"
const double TICKS_PER_SECOND = 10000000; //a tick is 100ns
FILETIME ft;
GetSystemTimeAsFileTime(&ft); //returns ticks in UTC
// Copy the low and high parts of FILETIME into a LARGE_INTEGER
LARGE_INTEGER li;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
//Convert ticks since 1/1/1970 into seconds
return (double)(li.QuadPart - UNIX_TIME_START) / TICKS_PER_SECOND;
}
void yield() {
YieldProcessor();
}
@ -99,6 +113,12 @@ double from_time_raw_relative(uint64_t raw_time) {
return (double)raw_time * (1.0 / nanos);
}
double time_now_unix_utc() {
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
return tp.tv_sec * 1000000000ULL + tp.tv_nsec;
}
void yield() {
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
_mm_pause();
@ -120,9 +140,14 @@ double time_now_d() {
return (double)(tv.tv_sec - start) + (double)tv.tv_usec * (1.0 / micros);
}
// Fake, but usable in a pinch. Don't, though.
uint64_t time_now_raw() {
return (uint64_t)(time_now_d() * nanos);
static time_t start;
struct timeval tv;
gettimeofday(&tv, nullptr);
if (start == 0) {
start = tv.tv_sec;
}
return (double)tv.tv_sec + (double)tv.tv_usec * (1.0 / micros);
}
double from_time_raw(uint64_t raw_time) {
@ -135,6 +160,10 @@ double from_time_raw_relative(uint64_t raw_time) {
void yield() {}
double time_now_unix_utc() {
return time_now_raw();
}
#endif
void sleep_ms(int ms) {

View file

@ -13,6 +13,9 @@ uint64_t time_now_raw();
double from_time_raw(uint64_t raw_time);
double from_time_raw_relative(uint64_t raw_time);
// Seconds, Unix UTC time
double time_now_unix_utc();
// Sleep. Does not necessarily have millisecond granularity, especially on Windows.
void sleep_ms(int ms);

View file

@ -13,6 +13,15 @@
#include "Core/KeyMap.h"
void Screen::focusChanged(ScreenFocusChange focusChange) {
char *eventName = "";
switch (focusChange) {
case ScreenFocusChange::FOCUS_LOST_TOP: eventName = "FOCUS_LOST_TOP"; break;
case ScreenFocusChange::FOCUS_BECAME_TOP: eventName = "FOCUS_BECAME_TOP"; break;
}
DEBUG_LOG(SYSTEM, "Screen %s got %s", this->tag(), eventName);
}
ScreenManager::~ScreenManager() {
shutdown();
}
@ -68,14 +77,17 @@ void ScreenManager::switchToNext() {
Layer temp = {nullptr, 0};
if (!stack_.empty()) {
temp = stack_.back();
temp.screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
stack_.pop_back();
}
stack_.push_back(nextStack_.front());
nextStack_.front().screen->focusChanged(ScreenFocusChange::FOCUS_BECAME_TOP);
if (temp.screen) {
delete temp.screen;
}
UI::SetFocusedView(nullptr);
// When will this ever happen? Should handle focus here too?
for (size_t i = 1; i < nextStack_.size(); ++i) {
stack_.push_back(nextStack_[i]);
}
@ -264,17 +276,30 @@ void ScreenManager::push(Screen *screen, int layerFlags) {
touch(input);
Layer layer = {screen, layerFlags};
if (nextStack_.empty())
if (!stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
}
if (nextStack_.empty()) {
layer.screen->focusChanged(ScreenFocusChange::FOCUS_BECAME_TOP);
stack_.push_back(layer);
else
} else {
nextStack_.push_back(layer);
}
}
void ScreenManager::pop() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (stack_.size()) {
if (!stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
delete stack_.back().screen;
stack_.pop_back();
if (!stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
}
} else {
ERROR_LOG(SYSTEM, "Can't pop when stack empty");
}
@ -318,12 +343,19 @@ void ScreenManager::processFinishDialog() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
// Another dialog may have been pushed before the render, so search for it.
Screen *caller = dialogParent(dialogFinished_);
bool erased = false;
for (size_t i = 0; i < stack_.size(); ++i) {
if (stack_[i].screen == dialogFinished_) {
stack_[i].screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
stack_.erase(stack_.begin() + i);
erased = true;
}
}
if (erased && !stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_BECAME_TOP);
}
if (!caller) {
ERROR_LOG(SYSTEM, "ERROR: no top screen when finishing dialog");
} else if (caller != topScreen()) {

View file

@ -42,6 +42,11 @@ namespace Draw {
class DrawContext;
}
enum class ScreenFocusChange {
FOCUS_LOST_TOP, // Another screen was pushed on top
FOCUS_BECAME_TOP, // Became the top screen again
};
class Screen {
public:
Screen() : screenManager_(nullptr) { }
@ -60,6 +65,8 @@ public:
virtual void deviceLost() {}
virtual void deviceRestored() {}
virtual void focusChanged(ScreenFocusChange focusChange);
// Return value of UnsyncTouch is only used to let the overlay screen block touches.
virtual bool UnsyncTouch(const TouchInput &touch) = 0;
// Return value of UnsyncKey is used to not block certain system keys like volume when unhandled, on Android.

View file

@ -1157,6 +1157,10 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
}
}
// Time tracking
Section *playTime = iniFile.GetOrCreateSection("PlayTime");
playTimeTracker_.Load(playTime);
auto pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths")->ToMap();
vPinnedPaths.clear();
for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) {
@ -1317,6 +1321,10 @@ bool Config::Save(const char *saveReason) {
if (LogManager::GetInstance())
LogManager::GetInstance()->SaveConfig(log);
// Time tracking
Section *playTime = iniFile.GetOrCreateSection("PlayTime");
playTimeTracker_.Save(playTime);
if (!iniFile.Save(iniFilename_)) {
ERROR_LOG(LOADER, "Error saving config (%s)- can't write ini '%s'", saveReason, iniFilename_.c_str());
return false;
@ -1827,3 +1835,89 @@ int Config::GetPSPLanguage() {
return g_Config.iLanguage;
}
}
void PlayTimeTracker::Start(std::string gameId) {
INFO_LOG(SYSTEM, "GameTimeTracker::Start(%s)", gameId.c_str());
if (gameId.empty()) {
return;
}
auto iter = tracker_.find(std::string(gameId));
if (iter != tracker_.end()) {
if (iter->second.startTime == 0.0) {
iter->second.lastTimePlayed = time_now_unix_utc();
iter->second.startTime = time_now_d();
}
return;
}
PlayTime playTime;
playTime.lastTimePlayed = time_now_unix_utc();
playTime.totalTimePlayed = 0.0;
playTime.startTime = time_now_d();
tracker_[gameId] = playTime;
}
void PlayTimeTracker::Stop(std::string gameId) {
INFO_LOG(SYSTEM, "GameTimeTracker::Stop(%s)", gameId.c_str());
_dbg_assert_(!gameId.empty());
auto iter = tracker_.find(std::string(gameId));
if (iter != tracker_.end()) {
if (iter->second.startTime != 0.0) {
iter->second.totalTimePlayed += time_now_d() - iter->second.startTime;
iter->second.startTime = 0.0;
}
iter->second.lastTimePlayed = time_now_unix_utc();
return;
}
// Shouldn't happen, ignore this case.
WARN_LOG(SYSTEM, "GameTimeTracker::Stop called without corresponding GameTimeTracker::Start");
}
void PlayTimeTracker::Load(const Section *section) {
tracker_.clear();
std::vector<std::string> keys;
section->GetKeys(keys);
for (auto key : keys) {
std::string value;
if (!section->Get(key.c_str(), &value, nullptr)) {
continue;
}
// Parse the string.
PlayTime gameTime{};
if (2 == sscanf(value.c_str(), "%d,%llu", &gameTime.totalTimePlayed, &gameTime.lastTimePlayed)) {
tracker_[key] = gameTime;
}
}
}
void PlayTimeTracker::Save(Section *section) {
for (auto iter : tracker_) {
std::string formatted = StringFromFormat("%d,%llu", iter.second.totalTimePlayed, iter.second.lastTimePlayed);
section->Set(iter.first.c_str(), formatted);
}
}
bool PlayTimeTracker::GetPlayedTimeString(const std::string &gameId, std::string *str) const {
auto ga = GetI18NCategory(I18NCat::GAME);
auto iter = tracker_.find(gameId);
if (iter == tracker_.end()) {
return false;
}
int totalSeconds = iter->second.totalTimePlayed;
int seconds = totalSeconds % 60;
totalSeconds /= 60;
int minutes = totalSeconds % 60;
totalSeconds /= 60;
int hours = totalSeconds;
*str = ApplySafeSubstitutions(ga->T("Time Played: %1h %2m %3s"), hours, minutes, seconds);
return true;
}

View file

@ -37,6 +37,29 @@ namespace http {
struct UrlEncoder;
struct ConfigPrivate;
class Section;
class PlayTimeTracker {
public:
struct PlayTime {
int totalTimePlayed;
double startTime; // time_now_d() time
uint64_t lastTimePlayed; // UTC Unix time for portability.
};
// It's OK to call these redundantly.
void Start(std::string gameId);
void Stop(std::string gameId);
void Load(const Section *section);
void Save(Section *section);
bool GetPlayedTimeString(const std::string &gameId, std::string *str) const;
private:
std::map<std::string, PlayTime> tracker_;
};
struct Config {
public:
Config();
@ -578,6 +601,8 @@ public:
// Applies the Auto setting if set. Returns an enum value from PSP_SYSTEMPARAM_LANGUAGE_*.
int GetPSPLanguage();
PlayTimeTracker &TimeTracker() { return playTimeTracker_; }
protected:
void LoadStandardControllerIni();
void LoadLangValuesMapping();
@ -592,6 +617,7 @@ private:
std::string gameIdTitle_;
std::vector<std::string> recentIsos;
std::map<std::string, std::pair<std::string, int>> langValuesMapping_;
PlayTimeTracker playTimeTracker_;
Path iniFilename_;
Path controllerIniFilename_;
Path searchPath_;

View file

@ -148,6 +148,7 @@ bool IsBlockingExecution() {
return g_isLoggingIn || g_isIdentifying;
}
// This is the RetroAchievements game ID, rather than the PSP game ID.
static u32 GetGameID() {
if (!g_rcClient) {
return 0;
@ -243,6 +244,7 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t *
// TODO: Translation?
std::string title = ApplySafeSubstitutions(ac->T("Mastered %1"), gameInfo->title);
rc_client_user_game_summary_t summary;
rc_client_get_user_game_summary(g_rcClient, &summary);

View file

@ -409,13 +409,20 @@ void EmuScreen::bootComplete() {
loadingViewColor_->Divert(0x00FFFFFF, 0.2f);
loadingViewVisible_->Divert(UI::V_INVISIBLE, 0.2f);
std::string gameID = g_paramSFO.GetValueString("DISC_ID");
g_Config.TimeTracker().Start(gameID);
}
EmuScreen::~EmuScreen() {
std::string gameID = g_paramSFO.GetValueString("DISC_ID");
g_Config.TimeTracker().Stop(gameID);
// If we were invalid, it would already be shutdown.
if (!invalid_ || bootPending_) {
PSP_Shutdown();
}
g_OSD.ClearAchievementStuff();
SetExtraAssertInfo(nullptr);
@ -461,6 +468,24 @@ static void AfterStateBoot(SaveState::Status status, const std::string &message,
System_Notify(SystemNotification::DISASSEMBLY);
}
void EmuScreen::focusChanged(ScreenFocusChange focusChange) {
Screen::focusChanged(focusChange);
std::string gameID = g_paramSFO.GetValueString("DISC_ID");
if (gameID.empty()) {
// startup or shutdown
return;
}
switch (focusChange) {
case ScreenFocusChange::FOCUS_LOST_TOP:
g_Config.TimeTracker().Stop(gameID);
break;
case ScreenFocusChange::FOCUS_BECAME_TOP:
g_Config.TimeTracker().Start(gameID);
break;
}
}
void EmuScreen::sendMessage(UIMessage message, const char *value) {
// External commands, like from the Windows UI.
if (message == UIMessage::REQUEST_GAME_PAUSE && screenManager()->topScreen() == this) {

View file

@ -58,6 +58,10 @@ public:
// We also need to do some special handling of queued UI events to handle closing the chat window.
bool key(const KeyInput &key) override;
protected:
void focusChanged(ScreenFocusChange focusChange) override;
private:
void CreateViews() override;
UI::EventReturn OnDevTools(UI::EventParams &params);

View file

@ -25,6 +25,7 @@
#include "Common/UI/ViewGroup.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/File/FileUtil.h"
#include "Common/StringUtils.h"
@ -107,26 +108,39 @@ void GameScreen::CreateViews() {
leftColumn->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &GameScreen::OnSwitchBack);
if (info) {
leftColumn->Add(new GameIconView(gamePath_, 2.0f, new AnchorLayoutParams(144 * 2, 80 * 2, 10, 10, NONE, NONE)));
ViewGroup *badgeHolder = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(10, 10, 110, NONE));
LinearLayout *mainGameInfo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
mainGameInfo->SetSpacing(3.0f);
// Need an explicit size here because homebrew uses screenshots as icons.
badgeHolder->Add(new GameIconView(gamePath_, 2.0f, new LinearLayoutParams(144 * 2, 80 * 2, UI::Margins(0))));
badgeHolder->Add(mainGameInfo);
leftColumn->Add(badgeHolder);
LinearLayout *infoLayout = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(10, 200, NONE, NONE));
leftColumn->Add(infoLayout);
tvTitle_ = infoLayout->Add(new TextView(info->GetTitle(), ALIGN_LEFT | FLAG_WRAP_TEXT, false, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
// TODO: Add non-translated title here if available in gameDB.
tvTitle_ = mainGameInfo->Add(new TextView(info->GetTitle(), ALIGN_LEFT | FLAG_WRAP_TEXT, false, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvTitle_->SetShadow(true);
tvID_ = infoLayout->Add(new TextView("", ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvID_ = mainGameInfo->Add(new TextView("", ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvID_->SetShadow(true);
tvRegion_ = mainGameInfo->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvRegion_->SetShadow(true);
tvGameSize_ = mainGameInfo->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvGameSize_->SetShadow(true);
// This one doesn't need to be updated.
infoLayout->Add(new TextView(gamePath_.ToVisualString(), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetShadow(true);
tvGameSize_ = infoLayout->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvGameSize_->SetShadow(true);
tvSaveDataSize_ = infoLayout->Add(new TextView("...", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvSaveDataSize_->SetShadow(true);
tvInstallDataSize_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvInstallDataSize_->SetShadow(true);
tvInstallDataSize_->SetVisibility(V_GONE);
tvRegion_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvRegion_->SetShadow(true);
tvPlayTime_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvPlayTime_->SetShadow(true);
tvPlayTime_->SetVisibility(V_GONE);
tvCRC_ = infoLayout->Add(new TextView("", ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
tvCRC_->SetShadow(true);
tvCRC_->SetVisibility(Reporting::HasCRC(gamePath_) ? V_VISIBLE : V_GONE);
@ -138,6 +152,7 @@ void GameScreen::CreateViews() {
tvSaveDataSize_ = nullptr;
tvInstallDataSize_ = nullptr;
tvRegion_ = nullptr;
tvPlayTime_ = nullptr;
tvCRC_ = nullptr;
tvID_ = nullptr;
}
@ -275,11 +290,15 @@ void GameScreen::render() {
if (info->gameSizeOnDisk) {
char temp[256];
if (tvGameSize_) {
snprintf(temp, sizeof(temp), "%s: %1.1f %s", ga->T("Game"), (float)(info->gameSizeOnDisk) / 1024.f / 1024.f, ga->T("MB"));
snprintf(temp, sizeof(temp), "%s: %s", ga->T("Game"), NiceSizeFormat(info->gameSizeOnDisk).c_str());
if (info->gameSizeUncompressed != info->gameSizeOnDisk) {
size_t len = strlen(temp);
snprintf(temp + len, sizeof(temp) - len, " (%s: %s)", ga->T("Uncompressed"), NiceSizeFormat(info->gameSizeUncompressed).c_str());
}
tvGameSize_->SetText(temp);
}
if (tvSaveDataSize_) {
snprintf(temp, sizeof(temp), "%s: %1.2f %s", ga->T("SaveData"), (float)(info->saveDataSize) / 1024.f / 1024.f, ga->T("MB"));
snprintf(temp, sizeof(temp), "%s: %s", ga->T("SaveData"), NiceSizeFormat(info->saveDataSize).c_str());
tvSaveDataSize_->SetText(temp);
}
if (info->installDataSize > 0 && tvInstallDataSize_) {
@ -305,6 +324,14 @@ void GameScreen::render() {
}
}
if (tvPlayTime_) {
std::string str;
if (g_Config.TimeTracker().GetPlayedTimeString(info->id, &str)) {
tvPlayTime_->SetText(str);
tvPlayTime_->SetVisibility(UI::V_VISIBLE);
}
}
if (tvCRC_ && Reporting::HasCRC(gamePath_)) {
auto rp = GetI18NCategory(I18NCat::REPORTING);
uint32_t crcVal = Reporting::RetrieveCRC(gamePath_);

View file

@ -72,6 +72,7 @@ private:
UI::TextView *tvSaveDataSize_ = nullptr;
UI::TextView *tvInstallDataSize_ = nullptr;
UI::TextView *tvRegion_ = nullptr;
UI::TextView *tvPlayTime_ = nullptr;
UI::TextView *tvCRC_ = nullptr;
UI::TextView *tvID_ = nullptr;
NoticeView *tvVerified_ = nullptr;

View file

@ -1217,12 +1217,13 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_keyUp(JNIEnv *, jclass, jin
return NativeKey(keyInput);
}
// TODO: Make a batched interface, since we get these in batches on the Android side.
// TODO: Make a batched interface, since we get these in batches on the Java side.
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_joystickAxis(
JNIEnv *env, jclass, jint deviceId, jint axisId, jfloat value) {
if (!renderer_inited)
return;
// These are dirty-filtered on the Java side.
AxisInput axis;
axis.deviceId = (InputDeviceID)deviceId;
axis.axisId = (InputAxis)axisId;

View file

@ -535,6 +535,8 @@ Remove From Recent = ‎مسح من "الحالي"
SaveData = ‎بيانات الحفظ
Setting Background = ‎إعدادات الخلفية
Show In Folder = ‎أظهر في المجلد
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = ‎أمريكا
Use UI background = ‎إستخدم خلفية الواجهة

View file

@ -527,6 +527,8 @@ Remove From Recent = Remove from "Recent"
SaveData = Savedata
Setting Background = Setting background
Show In Folder = Show in folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Премахни от „Скорошни“
SaveData = Savedata
Setting Background = Setting background
Show In Folder = Покажи в папка
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Remove from "Recent"
SaveData = Dades guardades
Setting Background = Imatge de fons
Show In Folder = Mostrar en carpeta
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = EEUU
Use UI background = Utilitzar imatge de fons de l'interfície d'usuari

View file

@ -527,6 +527,8 @@ Remove From Recent = Odstranit z "Nedávné"
SaveData = Uložená data
Setting Background = Setting background
Show In Folder = Zobrazit ve složce
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Fjern fra "Nylige"
SaveData = Gem data
Setting Background = Setting background
Show In Folder = Vis i katalog
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Brug UI baggrund

View file

@ -527,6 +527,8 @@ Remove From Recent = Aus der Liste entfernen
SaveData = Speicherdaten
Setting Background = Einstellungshintergrund
Show In Folder = Ordner öffnen
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Benutze UI Hintergrund

View file

@ -527,6 +527,8 @@ Remove From Recent = Remove from "Recent"
SaveData = Pangsave
Setting Background = Setting background
Show In Folder = Show in folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -551,6 +551,8 @@ Remove From Recent = Remove from "Recent"
SaveData = Savedata
Setting Background = Setting background
Show In Folder = Show in folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Borrar de recientes
SaveData = Datos
Setting Background = Ajustar imagen de fondo
Show In Folder = Mostrar en carpeta
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = América
Use UI background = Usar imagen de fondo

View file

@ -527,6 +527,8 @@ Remove From Recent = Borrar de recientes
SaveData = Datos
Setting Background = Configurar fondo
Show In Folder = Mostrar en carpeta
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = América
Use UI background = Usar fondo como interfaz

View file

@ -527,6 +527,8 @@ Remove From Recent = Remove from "Recent"
SaveData = ‎دیتای ذخیره
Setting Background = تنظیمات پس زمینا
Show In Folder = نمایش پوشه
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = آمریکا
Use UI background = استفاده به عنوان پس زمینه؟

View file

@ -527,6 +527,8 @@ Remove From Recent = Poista "äskettäin" listalta...
SaveData = Tallennustieto
Setting Background = Setting background
Show In Folder = Näytä kansiossa
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Supprimer de "Récemment"
SaveData = Sauvegarde 
Setting Background = Paramétrage du fond d'écran
Show In Folder = Montrer dans dossier
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Utiliser le fond d'écran

View file

@ -527,6 +527,8 @@ Remove From Recent = Borrar de recentes
SaveData = Datos
Setting Background = Setting background
Show In Folder = Mostrar en carpeta
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Κατάργηση από τα πρόσφατα
SaveData = Δεδομένα Αποθήκευσης
Setting Background = Ρύθμιση φόντου
Show In Folder = Εμφάνιση σε Φάκελο
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = ΗΠΑ
Use UI background = Χρήση φόντου UI

View file

@ -527,6 +527,8 @@ Remove From Recent = Remove from "Recent"
SaveData = Savedata
Setting Background = Setting background
Show In Folder = Show in folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Remove from "Recent"
SaveData = Savedata
Setting Background = Setting background
Show In Folder = Show in folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Makni iz "Nedavno"
SaveData = Savedata
Setting Background = Postavljanje pozadine
Show In Folder = Prikaži u mapi
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = SAD
Use UI background = Koristi UI pozadinu

View file

@ -527,6 +527,8 @@ Remove From Recent = Törlés a listából
SaveData = Mentések
Setting Background = Háttér beállítás
Show In Folder = Mutatás mappában
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Alapértelmezett háttér használata

View file

@ -527,6 +527,8 @@ Remove From Recent = Hapus dari "Terbaru"
SaveData = Simpanan data
Setting Background = Atur latar belakang
Show In Folder = Tampilkan di berkas
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = Amerika Serikat
Use UI background = Gunakan latar belakang UI

View file

@ -527,6 +527,8 @@ Remove From Recent = Rimuovi dai Recenti
SaveData = Dati salvataggio
Setting Background = Imposta sfondo
Show In Folder = Mostra nella Cartella
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Usa sfondo interfaccia

View file

@ -527,6 +527,8 @@ Remove From Recent = 履歴から削除する
SaveData = セーブデータ
Setting Background = 背景を設定
Show In Folder = フォルダに表示する
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = UIの背景を使う

View file

@ -527,6 +527,8 @@ Remove From Recent = Mbusek soko "Terakher"
SaveData = Simpen Data
Setting Background = Setting background
Show In Folder = Tampilno Nang Folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = "최근"에서 제거
SaveData = 저장데이터
Setting Background = 배경 설정
Show In Folder = 폴더에 표기
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = 미국
Use UI background = UI 배경 사용

View file

@ -527,6 +527,8 @@ Remove From Recent = ເອົາອອກຈາກໜ້າ "ຫຼ້າສຸ
SaveData = ບັນທຶກຂໍ້ມູນ
Setting Background = Setting background
Show In Folder = ສະແດງໃນໂຟນເດີ້
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = ໃຊ້ເປັນພາບພື້ນຫຼັງ

View file

@ -527,6 +527,8 @@ Remove From Recent = Ištrinti iš "Paskutiniai" sąrašo
SaveData = Išsaugojimai
Setting Background = Setting background
Show In Folder = Rodyti aplanke
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Hapuskan dari "Semasa"
SaveData = Data disimpan
Setting Background = Setting background
Show In Folder = Papar dalam folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Wissen uit "Recent"
SaveData = Opgeslagen data
Setting Background = Achtergrondinstelling
Show In Folder = Weergeven in map
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = VS
Use UI background = UI-achtergrond gebruiken

View file

@ -527,6 +527,8 @@ Remove From Recent = Remove from "Recent"
SaveData = Savedata
Setting Background = Setting background
Show In Folder = Show in folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -532,6 +532,8 @@ Remove From Recent = Usuń z "Ostatnio uruchamianych tytułów"
SaveData = Zapisy gier
Setting Background = Ustawianie tła
Show In Folder = Pokaż w folderze
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Użyj tego tła

View file

@ -551,6 +551,8 @@ Remove From Recent = Remover dos "Recentes"
SaveData = Dados do save
Setting Background = Configurando o cenário de fundo
Show In Folder = Mostrar na pasta
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = EUA
Use UI background = Usar cenário de fundo da interface do usuário

View file

@ -551,6 +551,8 @@ Remove From Recent = Remover de "Recente"
SaveData = Dados Salvos
Setting Background = Definindo o cenário de fundo
Show In Folder = Mostrar na pasta
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = EUA
Use UI background = Usar cenário de fundo da interface do usuário.

View file

@ -528,6 +528,8 @@ Remove From Recent = Ștergere de la "Recent"
SaveData = Salvare
Setting Background = Setting background
Show In Folder = Arată în folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = Удалить из недавних
SaveData = Сохранения
Setting Background = Установка фона
Show In Folder = Показать в папке
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = США
Use UI background = Использовать фон интерфейса

View file

@ -528,6 +528,8 @@ Remove From Recent = Ta bort från "Senast spelat"
SaveData = Sparad data
Setting Background = Sätter UI-bakgrund
Show In Folder = Visa i mapp
Time Played: %1h %2m %3s = Spelad tid: %1h %2m %3s
Uncompressed = Okomprimerat
USA = USA
Use UI background = Använd UI-bakgrund

View file

@ -527,6 +527,8 @@ Remove From Recent = Alisin mula sa "Recent"
SaveData = SaveData
Setting Background = Setting background
Show In Folder = Ipakita sa folder
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Use UI background

View file

@ -527,6 +527,8 @@ Remove From Recent = ลบออกจากในหน้า "ล่าสุ
SaveData = ขนาดข้อมูลเซฟ
Setting Background = การตั้งค่าพื้นหลัง
Show In Folder = แสดงในโฟลเดอร์
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA (โซนอเมริกา)
Use UI background = ใช้เป็นภาพพื้นหลัง

View file

@ -529,6 +529,8 @@ Remove From Recent = Geçmişten sil
SaveData = Kayıt
Setting Background = Arkaplan ayarlanıyor
Show In Folder = Dosyada göster
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = ABD
Use UI background = Arayüz arkaplanı olarak kullan

View file

@ -527,6 +527,8 @@ Remove From Recent = Видалити з останніх
SaveData = Збереження
Setting Background = Налаштування фону
Show In Folder = Показати в теці
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = США
Use UI background = Використати фон інтерфейсу

View file

@ -527,6 +527,8 @@ Remove From Recent = Xóa danh sách trò chơi gần đây
SaveData = Dữ liệu save
Setting Background = Cài đặt nền
Show In Folder = Hiện trong thư mục
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = USA
Use UI background = Sử dụng nền UI

View file

@ -527,6 +527,8 @@ Remove From Recent = 从最近游玩中删除
SaveData = 存档
Setting Background = 设置背景
Show In Folder = 在文件夹中显示
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = 美国
Use UI background = 使用背景为壁纸

View file

@ -527,6 +527,8 @@ Remove From Recent = 從「最近遊玩」中移除
SaveData = 儲存資料
Setting Background = 設定背景
Show In Folder = 在資料夾中顯示
Time Played: %1h %2m %3s = Time Played: %1h %2m %3s
Uncompressed = Uncompressed
USA = 美國
Use UI background = 使用 UI 背景