diff --git a/modules/libSceUserService/CMakeLists.txt b/modules/libSceUserService/CMakeLists.txt index 8a7b036..bdfbdb4 100644 --- a/modules/libSceUserService/CMakeLists.txt +++ b/modules/libSceUserService/CMakeLists.txt @@ -5,4 +5,12 @@ project(${libName}) add_library(${libName} SHARED entry.cpp) -setupModule(${libName}) \ No newline at end of file +target_link_directories(${libName} PRIVATE + ${CMAKE_BINARY_DIR}/third_party/install/lib +) + +add_dependencies(${libName} boost config_emu) +target_compile_definitions(${libName} PUBLIC BOOST_ALL_NO_LIB) +target_link_libraries(${libName} PUBLIC libboost_thread config_emu.lib) + +setupModule(${libName}) diff --git a/modules/libSceUserService/errorcodes.h b/modules/libSceUserService/codes.h similarity index 91% rename from modules/libSceUserService/errorcodes.h rename to modules/libSceUserService/codes.h index e175154..1d79c46 100644 --- a/modules/libSceUserService/errorcodes.h +++ b/modules/libSceUserService/codes.h @@ -10,4 +10,6 @@ constexpr int USER_SERVICE_ERROR_OPERATION_NOT_SUPPORTED = -2137653242; /* 0x809 constexpr int USER_SERVICE_ERROR_NO_EVENT = -2137653241; /* 0x80960007 */ constexpr int USER_SERVICE_ERROR_NOT_LOGGED_IN = -2137653239; /* 0x80960009 */ constexpr int USER_SERVICE_ERROR_BUFFER_TOO_SHORT = -2137653238; /* 0x8096000A */ -} // namespace Err \ No newline at end of file +} // namespace Err + +constexpr size_t USER_SERVICE_MAX_USER_NAME_LENGTH = 16; diff --git a/modules/libSceUserService/entry.cpp b/modules/libSceUserService/entry.cpp index 3d47923..7fe45b0 100644 --- a/modules/libSceUserService/entry.cpp +++ b/modules/libSceUserService/entry.cpp @@ -1,7 +1,10 @@ +#include "codes.h" #include "common.h" -#include "errorcodes.h" +#include "config_emu.h" #include "logging.h" +#include + LOG_DEFINE_MODULE(libSceUserService); namespace { @@ -17,6 +20,20 @@ struct UserServiceEvent { }; enum UserServiceUserColor { USER_COLOR_BLUE, USER_COLOR_RED, USER_COLOR_GREEN, USER_COLOR_PINK }; + +static std::unordered_map colors = { + {"blue", USER_COLOR_BLUE}, + {"red", USER_COLOR_RED}, + {"green", USER_COLOR_GREEN}, + {"pink", USER_COLOR_PINK}, +}; + +static UserServiceUserColor map_user_color(std::string_view str) { + auto it = colors.find(str); + if (it != colors.end()) return it->second; + return USER_COLOR_BLUE; +} + } // namespace extern "C" { @@ -36,8 +53,8 @@ EXPORT SYSV_ABI int32_t sceUserServiceTerminate() { } EXPORT SYSV_ABI int sceUserServiceGetInitialUser(int* userId) { - *userId = 1; - + auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL); + if (!getJsonParam(jData, "userIndex", *userId) || (*userId < 1 || *userId > 3)) *userId = 1; return Ok; } @@ -47,7 +64,7 @@ EXPORT SYSV_ABI int sceUserServiceGetEvent(UserServiceEvent* event) { if (!logged_in) { logged_in = true; event->eventType = UserServiceEventTypeLogin; - event->userId = 1; + sceUserServiceGetInitialUser(&event->userId); return Ok; } @@ -55,7 +72,7 @@ EXPORT SYSV_ABI int sceUserServiceGetEvent(UserServiceEvent* event) { } EXPORT SYSV_ABI int sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* userId_list) { - userId_list->userId[0] = 1; + sceUserServiceGetInitialUser(&userId_list->userId[0]); userId_list->userId[1] = -1; userId_list->userId[2] = -1; userId_list->userId[3] = -1; @@ -64,14 +81,36 @@ EXPORT SYSV_ABI int sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* } EXPORT SYSV_ABI int sceUserServiceGetUserName(int userId, char* name, size_t size) { + if (userId < 1 || userId > 3) return Err::USER_SERVICE_ERROR_INVALID_ARGUMENT; + std::string username = "Anon"; - auto const count = username.copy(name, size - 1); - name[count] = '\0'; + auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL); + + try { + auto& profiles = (*jData)["profiles"]; + auto& cprofile = profiles[userId - 1]; + cprofile["name"].get_to(username); + } catch (json::exception& ex) { + } + + auto const count = username.copy(name, std::min(USER_SERVICE_MAX_USER_NAME_LENGTH, size - 1)); + name[count] = '\0'; return Ok; } EXPORT SYSV_ABI int32_t sceUserServiceGetUserColor(int userId, UserServiceUserColor* color) { - *color = USER_COLOR_BLUE; + auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL); + + std::string _scolor; + + try { + auto& profiles = (*jData)["profiles"]; + auto& cprofile = profiles[userId - 1]; + cprofile["color"].get_to(_scolor); + } catch (json::exception& ex) { + } + + *color = map_user_color(_scolor); return Ok; } -} \ No newline at end of file +} diff --git a/tools/config_emu/config_emu.cpp b/tools/config_emu/config_emu.cpp index f12c453..0242913 100644 --- a/tools/config_emu/config_emu.cpp +++ b/tools/config_emu/config_emu.cpp @@ -106,21 +106,81 @@ Config::Config() { should_backup = true; } - for (auto& [dkey, dval]: defaults.items()) { - if ((item->_json)[dkey].is_null() && !dval.is_null()) { - (item->_json)[dkey] = dval; - should_resave = true; - printf("%s: missing parameter \"%s\" has been added!\n", item->_name.data(), dkey.c_str()); + std::function fixMissing; + std::function removeUnused; + + /** + * @brief This function is necessary since json.items() + * translates every key to string. Even the array indexes, + * so we have to translate it back to number for arrays. + * + */ + auto getVal = [](json& _o, std::string_view key) -> json& { + if (_o.is_array()) { + int _tempi; + auto res = std::from_chars(key.data(), key.data() + key.size(), _tempi); + if (res.ec == std::errc::invalid_argument) { + printf("Unreachable scenario!\n"); // At least it should be + exit(1); + } + return _o[_tempi]; } + + return _o[key]; + }; + + fixMissing = [&should_backup, &getVal, &fixMissing](json& obj, json& def) -> bool { + bool missing = false; + + for (auto& [dkey, dval]: def.items()) { + json& cval = getVal(obj, dkey); + + if (cval.is_null() && !dval.is_null()) { + cval = dval; + missing = true; + } else if (dval.is_structured()) { + if (cval.is_structured()) { // Function calls itself if json element is array or object + missing |= fixMissing(cval, dval); + continue; + } + + should_backup = true; + cval = dval; + missing = true; + } + } + + return missing; + }; + + if (fixMissing(item->_json, defaults)) { + should_resave = true; + printf("%s: some missing parameters has been added!\n", item->_name.data()); } - for (auto& [ckey, cval]: item->_json.items()) { - if (defaults[ckey].is_null()) { - item->_json.erase(ckey); - should_backup = true; - should_resave = true; - printf("%s: unused parameter \"%s\" has been removed!\n", item->_name.data(), ckey.c_str()); + // Just the same thing as above, but for removing unused keys this time + removeUnused = [&getVal, &removeUnused](json& obj, json& def) -> bool { + bool unused = false; + + for (auto& [ckey, cval]: obj.items()) { + json& dval = getVal(def, ckey); + + if (dval.is_null()) { + obj.erase(ckey); + unused = true; + } else if (dval.is_structured()) { + unused |= removeUnused(cval, dval); + continue; + } } + + return unused; + }; + + if (removeUnused(item->_json, defaults)) { + should_backup = true; + should_resave = true; + printf("%s: some unused parameters has been removed!\n", item->_name.data()); } if (should_backup == true && std::filesystem::exists(path)) { @@ -136,7 +196,8 @@ Config::Config() { if (should_resave && dflag != ConfigModFlag::NONE) this->save((uint32_t)dflag); }; - const json defaultpad = {{"type", "gamepad"}, {"deadzones", {{"left_stick", {{"x", 0.0f}, {"y", 0.0f}}}, {"right_stick", {{"x", 0.0f}, {"y", 0.0f}}}}}}; + const json defaultpad = {{"type", "gamepad"}, {"deadzones", {{"left_stick", {{"x", 0.0f}, {"y", 0.0f}}}, {"right_stick", {{"x", 0.0f}, {"y", 0.0f}}}}}}; + const json defaultprof = {{"name", "Anon"}, {"color", "blue"}}; m_logging._future = std::async(std::launch::async | std::launch::deferred, load, &m_logging, json({{"sink", "FileBin"}, {"verbosity", 1}}), ConfigModFlag::LOGGING); @@ -160,5 +221,7 @@ Config::Config() { }}}), ConfigModFlag::CONTROLS); - m_general._future = std::async(std::launch::async, load, &m_general, json({{"systemlang", 1}}), ConfigModFlag::GENERAL); + m_general._future = + std::async(std::launch::async, load, &m_general, + json({{"systemlang", 1}, {"userIndex", 1}, {"profiles", json::array({defaultprof, defaultprof, defaultprof})}}), ConfigModFlag::GENERAL); }