user-manager: squash all commits for this feature.

This commit is contained in:
mpm11011 2018-03-17 02:17:27 -04:00 committed by Ivan
parent c5c0f68b3e
commit 948bd3673e
21 changed files with 654 additions and 56 deletions

View File

@ -4,6 +4,7 @@
#include "utils.h"
#include "unself.h"
#include "Emu/VFS.h"
#include "Emu/System.h"
#include <algorithm>
#include <zlib.h>
@ -1294,7 +1295,7 @@ bool SELFDecrypter::GetKeyFromRap(u8* content_id, u8* npdrm_key)
// Try to find a matching RAP file under exdata folder.
const std::string ci_str = reinterpret_cast<const char*>(content_id);
const std::string rap_path = "/dev_hdd0/home/00000001/exdata/" + ci_str + ".rap";
const std::string rap_path = fmt::format("/dev_hdd0/home/%s/exdata/%s.rap", Emu.GetUsr(), ci_str);
// Open the RAP file and read the key.
const fs::file rap_file(vfs::get(rap_path));

View File

@ -85,8 +85,8 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
vm::ptr<CellSaveDataFileSet> fileSet = g_savedata_context.ptr(&savedata_context::fileSet);
vm::ptr<CellSaveDataDoneGet> doneGet = g_savedata_context.ptr(&savedata_context::doneGet);
//TODO: get current user ID
// userId(0) = CELL_SYSUTIL_USERID_CURRENT
// TODO: now that we have Emu.GetUsrId(), do we need "userId ? userId : 1u"? Replace with error check?
// userId(0) = CELL_SYSUTIL_USERID_CURRENT;
// path of the specified user (00000001 by default)
const std::string base_dir = vfs::get(fmt::format("/dev_hdd0/home/%08u/savedata/", userId ? userId : 1u));
@ -843,10 +843,9 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
static NEVER_INLINE s32 savedata_get_list_item(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB, u32 userId)
{
//TODO: accurately get the current user
if (userId == 0)
{
userId = 1u;
userId = Emu.GetUsrId();
}
std::string save_path = vfs::get(fmt::format("/dev_hdd0/home/%08u/savedata/%s/", userId, dirName.get_ptr()));
std::string sfo = save_path + "PARAM.SFO";
@ -910,7 +909,7 @@ s32 cellSaveDataListSave2(ppu_thread& ppu, u32 version, PSetList setList, PSetBu
cellSaveData.warning("cellSaveDataListSave2(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, setList, setBuf, funcList, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_LIST_SAVE, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_LIST_SAVE, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataListLoad2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
@ -919,7 +918,7 @@ s32 cellSaveDataListLoad2(ppu_thread& ppu, u32 version, PSetList setList, PSetBu
cellSaveData.warning("cellSaveDataListLoad2(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, setList, setBuf, funcList, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_LIST_LOAD, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_LIST_LOAD, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataListSave(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
@ -928,7 +927,7 @@ s32 cellSaveDataListSave(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf
cellSaveData.warning("cellSaveDataListSave(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
version, setList, setBuf, funcList, funcStat, funcFile, container);
return savedata_op(ppu, SAVEDATA_OP_LIST_SAVE, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_LIST_SAVE, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, vm::null, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataListLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
@ -937,7 +936,7 @@ s32 cellSaveDataListLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf
cellSaveData.warning("cellSaveDataListLoad(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
version, setList, setBuf, funcList, funcStat, funcFile, container);
return savedata_op(ppu, SAVEDATA_OP_LIST_LOAD, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_LIST_LOAD, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, vm::null, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataFixedSave2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
@ -946,7 +945,7 @@ s32 cellSaveDataFixedSave2(ppu_thread& ppu, u32 version, PSetList setList, PSetB
cellSaveData.warning("cellSaveDataFixedSave2(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_FIXED_SAVE, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_FIXED_SAVE, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataFixedLoad2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
@ -955,7 +954,7 @@ s32 cellSaveDataFixedLoad2(ppu_thread& ppu, u32 version, PSetList setList, PSetB
cellSaveData.warning("cellSaveDataFixedLoad2(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_FIXED_LOAD, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_FIXED_LOAD, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataFixedSave(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
@ -964,7 +963,7 @@ s32 cellSaveDataFixedSave(ppu_thread& ppu, u32 version, PSetList setList, PSetBu
cellSaveData.warning("cellSaveDataFixedSave(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
version, setList, setBuf, funcFixed, funcStat, funcFile, container);
return savedata_op(ppu, SAVEDATA_OP_FIXED_SAVE, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_FIXED_SAVE, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, vm::null, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataFixedLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
@ -973,7 +972,7 @@ s32 cellSaveDataFixedLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBu
cellSaveData.warning("cellSaveDataFixedLoad(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
version, setList, setBuf, funcFixed, funcStat, funcFile, container);
return savedata_op(ppu, SAVEDATA_OP_FIXED_LOAD, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_FIXED_LOAD, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, vm::null, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataAutoSave2(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
@ -982,7 +981,7 @@ s32 cellSaveDataAutoSave2(ppu_thread& ppu, u32 version, vm::cptr<char> dirName,
cellSaveData.warning("cellSaveDataAutoSave2(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, dirName, errDialog, setBuf, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_AUTO_SAVE, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_AUTO_SAVE, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataAutoLoad2(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
@ -991,7 +990,7 @@ s32 cellSaveDataAutoLoad2(ppu_thread& ppu, u32 version, vm::cptr<char> dirName,
cellSaveData.warning("cellSaveDataAutoLoad2(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, dirName, errDialog, setBuf, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_AUTO_LOAD, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_AUTO_LOAD, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataAutoSave(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
@ -1000,7 +999,7 @@ s32 cellSaveDataAutoSave(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u
cellSaveData.warning("cellSaveDataAutoSave(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
version, dirName, errDialog, setBuf, funcStat, funcFile, container);
return savedata_op(ppu, SAVEDATA_OP_AUTO_SAVE, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_AUTO_SAVE, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, vm::null, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataAutoLoad(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
@ -1009,7 +1008,7 @@ s32 cellSaveDataAutoLoad(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u
cellSaveData.warning("cellSaveDataAutoLoad(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
version, dirName, errDialog, setBuf, funcStat, funcFile, container);
return savedata_op(ppu, SAVEDATA_OP_AUTO_LOAD, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_AUTO_LOAD, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, vm::null, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataListAutoSave(ppu_thread& ppu, u32 version, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
@ -1017,7 +1016,7 @@ s32 cellSaveDataListAutoSave(ppu_thread& ppu, u32 version, u32 errDialog, PSetLi
cellSaveData.warning("cellSaveDataListAutoSave(version=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, errDialog, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_LIST_AUTO_SAVE, version, vm::null, errDialog, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 0, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_LIST_AUTO_SAVE, version, vm::null, errDialog, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 0, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataListAutoLoad(ppu_thread& ppu, u32 version, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
@ -1025,7 +1024,7 @@ s32 cellSaveDataListAutoLoad(ppu_thread& ppu, u32 version, u32 errDialog, PSetLi
cellSaveData.warning("cellSaveDataListAutoLoad(version=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, errDialog, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_LIST_AUTO_LOAD, version, vm::null, errDialog, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 0, userdata, 0, vm::null);
return savedata_op(ppu, SAVEDATA_OP_LIST_AUTO_LOAD, version, vm::null, errDialog, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 0, userdata, Emu.GetUsrId(), vm::null);
}
s32 cellSaveDataDelete2(u32 container)
@ -1047,7 +1046,7 @@ s32 cellSaveDataFixedDelete(ppu_thread& ppu, PSetList setList, PSetBuf setBuf, P
cellSaveData.warning("cellSaveDataFixedDelete(setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)",
setList, setBuf, funcFixed, funcDone, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_FIXED_DELETE, 0, vm::null, 1, setList, setBuf, vm::null, funcFixed, vm::null, vm::null, container, 2, userdata, 0, funcDone);
return savedata_op(ppu, SAVEDATA_OP_FIXED_DELETE, 0, vm::null, 1, setList, setBuf, vm::null, funcFixed, vm::null, vm::null, container, 2, userdata, Emu.GetUsrId(), funcDone);
}
s32 cellSaveDataUserListSave(ppu_thread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
@ -1133,7 +1132,7 @@ s32 cellSaveDataListDelete(ppu_thread& ppu, PSetList setList, PSetBuf setBuf, PF
{
cellSaveData.warning("cellSaveDataListDelete(setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)", setList, setBuf, funcList, funcDone, container, userdata);
return savedata_op(ppu, SAVEDATA_OP_LIST_DELETE, 0, vm::null, 0, setList, setBuf, funcList, vm::null, vm::null, vm::null, container, 0x40, userdata, 0, funcDone);
return savedata_op(ppu, SAVEDATA_OP_LIST_DELETE, 0, vm::null, 0, setList, setBuf, funcList, vm::null, vm::null, vm::null, container, 0x40, userdata, Emu.GetUsrId(), funcDone);
}
s32 cellSaveDataListImport(ppu_thread& ppu, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
@ -1166,7 +1165,7 @@ s32 cellSaveDataFixedExport(ppu_thread& ppu, vm::cptr<char> dirName, u32 maxSize
s32 cellSaveDataGetListItem(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB)
{
cellSaveData.warning("cellSavaDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB);
cellSaveData.warning("cellSaveDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB);
return savedata_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, 0);
}
@ -1208,7 +1207,7 @@ s32 cellSaveDataUserFixedExport(ppu_thread& ppu, u32 userId, vm::cptr<char> dirN
s32 cellSaveDataUserGetListItem(u32 userId, vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB)
{
cellSaveData.warning("cellSavaDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x, userID=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB, userId);
cellSaveData.warning("cellSaveDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x, userID=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB, userId);
return savedata_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, userId);
}

View File

@ -37,8 +37,8 @@ error_code cellUserInfoGetStat(u32 id, vm::ptr<CellUserInfoUserStat> stat)
if (id == CELL_SYSUTIL_USERID_CURRENT)
{
// TODO: Return current user/profile when that is implemented
id = 1;
// We want the int value, not the string.
id = Emu.GetUsrId();
}
if (!stat)
@ -121,8 +121,8 @@ error_code cellUserInfoGetList(vm::ptr<u32> listNum, vm::ptr<CellUserInfoUserLis
if (currentUserId)
{
// TODO: Properly set the current user ID here, once implemented
*currentUserId = 1;
// We want the int value, not the string.
*currentUserId = Emu.GetUsrId();
}
return CELL_OK;

View File

@ -65,8 +65,7 @@ s32 npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
npdrmkeys->devKlic.fill(0);
npdrmkeys->rifKey.fill(0);
// todo: profile for rap_dir_path
std::string rap_dir_path = "/dev_hdd0/home/00000001/exdata/";
std::string rap_dir_path = fmt::format("/dev_hdd0/home/%s/exdata/", Emu.GetUsr());
const std::string& enc_drm_path_local = vfs::get(enc_drm_path);
const fs::file enc_file(enc_drm_path_local);
@ -145,7 +144,7 @@ s32 sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
return SCE_NP_DRM_ERROR_INVALID_PARAM;
}
if (!fs::is_file(vfs::get("/dev_hdd0/home/00000001/exdata/") + content_id.get_ptr() + ".rap"))
if (!fs::is_file(vfs::get(fmt::format("/dev_hdd0/home/%s/exdata/%s.rap", Emu.GetUsr(), content_id.get_ptr()))))
{
// Game hasn't been purchased therefore no RAP file present
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
@ -164,7 +163,7 @@ s32 sceNpDrmVerifyUpgradeLicense2(vm::cptr<char> content_id)
return SCE_NP_DRM_ERROR_INVALID_PARAM;
}
if (!fs::is_file(vfs::get("/dev_hdd0/home/00000001/exdata/") + content_id.get_ptr() + ".rap"))
if (!fs::is_file(vfs::get(fmt::format("/dev_hdd0/home/%s/exdata/%s.rap", Emu.GetUsr(), content_id.get_ptr()))))
{
// Game hasn't been purchased therefore no RAP file present
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;

View File

@ -296,8 +296,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
}
}
// TODO: Get the path of the current user
std::string trophyPath = "/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name;
std::string trophyPath = fmt::format("/dev_hdd0/home/%s/trophy/%s", Emu.GetUsr(), ctxt->trp_name);
if (!trp.Install(trophyPath))
{
return SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE;
@ -346,7 +345,7 @@ error_code sceNpTrophyGetRequiredDiskSpace(u32 context, u32 handle, vm::ptr<u64>
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
if (!fs::is_dir(vfs::get("/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name)))
if (!fs::is_dir(vfs::get(fmt::format("/dev_hdd0/home/%s/trophy/%s", Emu.GetUsr(), ctxt->trp_name))))
{
TRPLoader trp(ctxt->trp_stream);
@ -405,8 +404,7 @@ error_code sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptr<SceNpTrophyGa
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
// TODO: Get the path of the current user
fs::file config(vfs::get("/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/TROPCONF.SFM"));
fs::file config(vfs::get(fmt::format("/dev_hdd0/home/%s/trophy/%s/TROPCONF.SFM", Emu.GetUsr(), ctxt->trp_name)));
if (!config)
{
@ -505,7 +503,7 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
return SCE_NP_TROPHY_ERROR_ALREADY_UNLOCKED;
ctxt->tropusr->UnlockTrophy(trophyId, 0, 0); // TODO
std::string trophyPath = "/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/TROPUSR.DAT";
std::string trophyPath = fmt::format("/dev_hdd0/home/%s/trophy/%s/TROPUSR.DAT", Emu.GetUsr(), ctxt->trp_name);
ctxt->tropusr->Save(trophyPath);
if (platinumId)
@ -527,7 +525,8 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
}
// Get icon for the notification.
std::string trophyIconPath = "/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/TROP" + padding + std::to_string(trophyId) + ".PNG";
std::string paddedTrophyId = padding + std::to_string(trophyId);
std::string trophyIconPath = fmt::format("/dev_hdd0/home/%s/trophy/%s/TROP%s.PNG", Emu.GetUsr(), ctxt->trp_name, paddedTrophyId);
fs::file trophyIconFile = fs::file(vfs::get(trophyIconPath));
size_t iconSize = trophyIconFile.size();
std::vector<uchar> trophyIconData;
@ -612,8 +611,7 @@ error_code sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::p
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
// TODO: Get the path of the current user
fs::file config(vfs::get("/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/TROPCONF.SFM"));
fs::file config(vfs::get(fmt::format("/dev_hdd0/home/%s/trophy/%s/TROPCONF.SFM", Emu.GetUsr(), ctxt->trp_name)));
if (!config)
{
@ -756,7 +754,7 @@ error_code sceNpTrophyGetGameIcon(u32 context, u32 handle, vm::ptr<void> buffer,
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
fs::file icon_file(vfs::get("/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/ICON0.PNG"));
fs::file icon_file(vfs::get(fmt::format("/dev_hdd0/home/%s/trophy/%s/ICON0.PNG", Emu.GetUsr(), ctxt->trp_name)));
if (!icon_file)
{
@ -809,7 +807,7 @@ error_code sceNpTrophyGetTrophyIcon(u32 context, u32 handle, s32 trophyId, vm::p
return hidden ? SCE_NP_TROPHY_ERROR_HIDDEN : SCE_NP_TROPHY_ERROR_LOCKED;
}
fs::file icon_file(vfs::get("/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + fmt::format("/TROP%03d.PNG", trophyId)));
fs::file icon_file(vfs::get(fmt::format("/dev_hdd0/home/%s/trophy/%s/TROP%03d.PNG", Emu.GetUsr(), ctxt->trp_name, trophyId)));
if (!icon_file)
{

View File

@ -278,10 +278,14 @@ void Emulator::Init()
// Create directories
const std::string emu_dir = GetEmuDir();
const std::string dev_hdd0 = fmt::replace_all(g_cfg.vfs.dev_hdd0, "$(EmulatorDir)", emu_dir);
const std::string dev_hdd0 = GetHddDir();
const std::string dev_hdd1 = fmt::replace_all(g_cfg.vfs.dev_hdd1, "$(EmulatorDir)", emu_dir);
const std::string dev_usb = fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir);
// Set selected user.
m_usr = g_cfg.usr.selected_usr;
m_usrid = static_cast<u32>(std::stoul(m_usr));
fs::create_path(dev_hdd0);
fs::create_path(dev_hdd1);
fs::create_path(dev_usb);
@ -290,11 +294,11 @@ void Emulator::Init()
fs::create_dir(dev_hdd0 + "game/TEST12345/USRDIR/");
fs::create_dir(dev_hdd0 + "game/.locks/");
fs::create_dir(dev_hdd0 + "home/");
fs::create_dir(dev_hdd0 + "home/00000001/");
fs::create_dir(dev_hdd0 + "home/00000001/exdata/");
fs::create_dir(dev_hdd0 + "home/00000001/savedata/");
fs::create_dir(dev_hdd0 + "home/00000001/trophy/");
fs::write_file(dev_hdd0 + "home/00000001/localusername", fs::create + fs::excl + fs::write, "User"s);
fs::create_dir(dev_hdd0 + "home/" + m_usr + "/");
fs::create_dir(dev_hdd0 + "home/" + m_usr + "/exdata/");
fs::create_dir(dev_hdd0 + "home/" + m_usr + "/savedata/");
fs::create_dir(dev_hdd0 + "home/" + m_usr + "/trophy/");
fs::write_file(dev_hdd0 + "home/" + m_usr + "/localusername", fs::create + fs::excl + fs::write, "User"s);
fs::create_dir(dev_hdd0 + "disc/");
fs::create_dir(dev_hdd0 + "savedata/");
fs::create_dir(dev_hdd0 + "savedata/vmc/");

View File

@ -209,6 +209,8 @@ class Emulator final
std::string m_title;
std::string m_cat;
std::string m_dir;
std::string m_usr;
u32 m_usrid;
bool m_force_boot = false;
@ -277,6 +279,18 @@ public:
return m_dir;
}
// String for GUI dialogs.
const std::string& GetUsr() const
{
return m_usr;
}
// u32 for cell.
const u32 GetUsrId() const
{
return m_usrid;
}
u64 GetPauseTime()
{
return m_pause_amend_time;
@ -491,6 +505,15 @@ struct cfg_root : cfg::node
} net{this};
struct node_usr : cfg::node
{
node_usr(cfg::node* _this) : cfg::node(_this, "User") {}
// This is only a default, will get read from existing config.yml.
cfg::string selected_usr{ this, "Selected User", "00000001" };
} usr{this};
struct node_misc : cfg::node
{
node_misc(cfg::node* _this) : cfg::node(_this, "Miscellaneous") {}

View File

@ -481,6 +481,11 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_user_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_vfs_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@ -636,6 +641,11 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_user_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_vfs_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@ -811,6 +821,11 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release - LLVM\moc_user_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release - LLVM\moc_vfs_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@ -966,6 +981,11 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_user_manager_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_vfs_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@ -998,6 +1018,8 @@
<ClCompile Include="rpcs3qt\trophy_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\trophy_notification_frame.cpp" />
<ClCompile Include="rpcs3qt\trophy_notification_helper.cpp" />
<ClCompile Include="rpcs3qt\user_account.cpp" />
<ClCompile Include="rpcs3qt\user_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\vfs_dialog.cpp" />
<ClCompile Include="rpcs3qt\vfs_dialog_tab.cpp" />
<ClCompile Include="rpcs3qt\welcome_dialog.cpp" />
@ -1605,6 +1627,24 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I$(VULKAN_SDK)\Include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\user_manager_dialog.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DBRANCH= -DLLVM_AVAILABLE -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DLLVM_AVAILABLE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE "-I.\..\Vulkan\Vulkan-LoaderAndValidationLayers\include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtQuick" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)\." "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<ClInclude Include="rpcs3qt\table_item_delegate.h" />
<CustomBuild Include="rpcs3qt\welcome_dialog.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">Moc%27ing welcome_dialog.h...</Message>
@ -1663,6 +1703,7 @@
<ClInclude Include="rpcs3qt\trophy_manager_dialog.h" />
<ClInclude Include="rpcs3qt\trophy_notification_frame.h" />
<ClInclude Include="rpcs3qt\trophy_notification_helper.h" />
<ClInclude Include="rpcs3qt\user_account.h" />
<ClInclude Include="xinput_pad_handler.h" />
<ClInclude Include="\rpcs3qt\*.h" />
</ItemGroup>

View File

@ -102,6 +102,9 @@
<Filter Include="Gui\utils">
<UniqueIdentifier>{77ca7382-c296-43af-adb4-fb32f5ab4571}</UniqueIdentifier>
</Filter>
<Filter Include="Gui\user accounts">
<UniqueIdentifier>{bebec4fb-789c-4456-a838-5ba7e158af5c}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="\rpcs3qt\*.cpp">
@ -599,6 +602,24 @@
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_register_editor_dialog.cpp">
<Filter>Generated Files\Debug - LLVM</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\user_manager_dialog.cpp">
<Filter>Gui\user accounts</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\user_account.cpp">
<Filter>Gui\user accounts</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release - LLVM\moc_user_manager_dialog.cpp">
<Filter>Generated Files\Release - LLVM</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_user_manager_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_user_manager_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug - LLVM\moc_user_manager_dialog.cpp">
<Filter>Generated Files\Debug - LLVM</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\breakpoint_handler.cpp">
<Filter>Gui\debugger</Filter>
</ClCompile>
@ -718,6 +739,9 @@
<ClInclude Include="rpcs3qt\custom_dock_widget.h">
<Filter>Gui</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\user_account.h">
<Filter>Gui\user accounts</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\_discord_utils.h">
<Filter>Gui\utils</Filter>
</ClInclude>
@ -840,6 +864,9 @@
<CustomBuild Include="rpcs3qt\register_editor_dialog.h">
<Filter>Gui\debugger</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\user_manager_dialog.h">
<Filter>Gui\user accounts</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\debugger_list.h">
<Filter>Gui\debugger</Filter>
</CustomBuild>

View File

@ -110,6 +110,9 @@ public:
// Network
ConnectionStatus,
// User
SelectedUser,
// Language
Language,
EnableHostRoot,
@ -287,6 +290,9 @@ private:
// Networking
{ ConnectionStatus, { "Net", "Connection status"}},
// User
{SelectedUser, {"User", "Selected User"}},
// System
{ Language, { "System", "Language"}},
{ EnableHostRoot, { "VFS", "Enable /host_root/"}},

View File

@ -126,6 +126,7 @@ namespace gui
const QString gs_frame = "GSFrame";
const QString trophy = "Trophy";
const QString savedata = "SaveData";
const QString users = "Users";
const QString notes = "Notes";
const QColor gl_icon_color = QColor(36, 36, 36, 255);
@ -216,6 +217,8 @@ namespace gui
const gui_save tr_trophy_state = gui_save(trophy, "trophy_state", QByteArray());
const gui_save sd_geometry = gui_save(savedata, "geometry", QByteArray());
const gui_save um_geometry = gui_save(users, "geometry", QByteArray());
}
/** Class for GUI settings..

View File

@ -16,6 +16,7 @@
#include "vfs_dialog.h"
#include "save_manager_dialog.h"
#include "trophy_manager_dialog.h"
#include "user_manager_dialog.h"
#include "kernel_explorer.h"
#include "game_list_frame.h"
#include "debugger_frame.h"
@ -891,6 +892,8 @@ void main_window::OnEmuStop()
ui->toolbar_start->setText(tr("Play"));
ui->toolbar_start->setToolTip(Emu.IsReady() ? tr("Start emulation") : tr("Resume emulation"));
}
ui->actionManage_Users->setEnabled(true);
#ifdef WITH_DISCORD_RPC
// Discord Rich Presence Integration
if (guiSettings->GetValue(gui::m_richPresence).toBool())
@ -913,6 +916,8 @@ void main_window::OnEmuReady()
ui->toolbar_start->setText(tr("Play"));
ui->toolbar_start->setToolTip(Emu.IsReady() ? tr("Start emulation") : tr("Resume emulation"));
EnableMenus(true);
ui->actionManage_Users->setEnabled(false);
}
void main_window::EnableMenus(bool enabled)
@ -1262,6 +1267,12 @@ void main_window::CreateConnects()
trop_manager->show();
});
connect(ui->actionManage_Users, &QAction::triggered, [=]
{
user_manager_dialog* user_manager = new user_manager_dialog(guiSettings, emuSettings, "", this);
user_manager->show();
});
connect(ui->toolsCgDisasmAct, &QAction::triggered, [=]
{
cg_disasm_window* cgdw = new cg_disasm_window(guiSettings);
@ -1778,8 +1789,7 @@ void main_window::dropEvent(QDropEvent* event)
{
const std::string rapname = sstr(QFileInfo(rap).fileName());
// TODO: use correct user ID once User Manager is implemented
if (!fs::copy_file(sstr(rap), fmt::format("%s/home/%s/exdata/%s", Emu.GetHddDir(), "00000001", rapname), false))
if (!fs::copy_file(sstr(rap), fmt::format("%s/home/%s/exdata/%s", Emu.GetHddDir(), Emu.GetUsr(), rapname), false))
{
LOG_WARNING(GENERAL, "Could not copy rap file by drop: %s", rapname);
}

View File

@ -502,7 +502,7 @@
</action>
<action name="actionManage_Users">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>User Accounts</string>

View File

@ -3,6 +3,7 @@
#include <QPushButton>
#include <QHBoxLayout>
#include <QHeaderView>
#include "Emu/System.h"
constexpr auto qstr = QString::fromStdString;
@ -60,7 +61,7 @@ void save_data_info_dialog::UpdateData()
//Maybe there should be more details of save data.
m_list->setItem(0, 0, new QTableWidgetItem(tr("User ID")));
m_list->setItem(0, 1, new QTableWidgetItem("00000001 (Default)"));
m_list->setItem(0, 1, new QTableWidgetItem(qstr(Emu.GetUsr())));
m_list->setItem(1, 0, new QTableWidgetItem(tr("Title")));
m_list->setItem(1, 1, new QTableWidgetItem(qstr(m_entry.title)));

View File

@ -146,7 +146,8 @@ void save_manager_dialog::UpdateList()
{
if (m_dir == "")
{
m_dir = Emu.GetHddDir() + "home/00000001/savedata/";
// fmt::format(%shome ... is harder to read than straight concatenation.
m_dir = Emu.GetHddDir() + "home/" + Emu.GetUsr() + "/savedata/";
}
m_save_entries = GetSaveEntries(m_dir);

View File

@ -29,8 +29,6 @@
#include <QScrollBar>
#include <QWheelEvent>
static const char* m_TROPHY_DIR = "/dev_hdd0/home/00000001/trophy/";
namespace
{
constexpr auto qstr = QString::fromStdString;
@ -58,6 +56,10 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
// HACK: dev_hdd0 must be mounted for vfs to work for loading trophies.
vfs::mount("dev_hdd0", Emu.GetHddDir());
// Get the currently selected user's trophy path.
m_trophy_dir = fmt::format("/dev_hdd0/home/%s/trophy/", Emu.GetUsr());
// Game chooser combo box
m_game_combo = new QComboBox();
m_game_combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
@ -320,7 +322,7 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name)
{
std::string trophyPath = m_TROPHY_DIR + trop_name;
std::string trophyPath = m_trophy_dir + trop_name;
// Populate GameTrophiesData
std::unique_ptr<GameTrophiesData> game_trophy_data = std::make_unique<GameTrophiesData>();

View File

@ -106,6 +106,7 @@ private:
bool m_show_silver_trophies = true;
bool m_show_gold_trophies = true;
bool m_show_platinum_trophies = true;
std::string m_trophy_dir;
int m_icon_height = 75;
bool m_save_icon_height = false;

View File

@ -0,0 +1,27 @@
#include "user_account.h"
UserAccount::UserAccount(const std::string& userId)
{
// Setting userId.
m_userId = userId;
// Setting userDir.
m_userDir = Emu.GetHddDir() + "home/" + m_userId + "/";
// Setting userName.
fs::file file;
if (file.open(m_userDir + "localusername", fs::read))
{
file.read(m_userName, 16*sizeof(char)); //max of 16 chars on real PS3
file.close();
}
else
{
LOG_WARNING(GENERAL, "UserAccount: localusername file read error (userId=%s, userDir=%s).", m_userId, m_userDir);
}
}
UserAccount::~UserAccount()
{
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "Emu/System.h"
#include "Utilities/File.h"
#include "Utilities/StrFmt.h"
// Do not confuse this with the "user" in Emu/System.h.
// That user is read from config.yml, and it only represents the currently "logged in" user.
// The UserAccount class will represent all users in the home directory for the User Manager dialog.
// Selecting a user account in this dialog and saving writes it to config.yml.
class UserAccount
{
public:
explicit UserAccount(const std::string& userId);
std::string GetUserId() { return m_userId; };
std::string GetUserDir() { return m_userDir; };
std::string GetUserName() { return m_userName; };
~UserAccount();
private:
std::string m_userId;
std::string m_userDir;
std::string m_userName;
};

View File

@ -0,0 +1,371 @@
#include "user_manager_dialog.h"
#include "Utilities/StrUtil.h"
#include <QRegExpValidator>
#include <QInputDialog>
namespace
{
std::vector<UserAccount*> GetUserAccounts(const std::string& base_dir)
{
std::vector<UserAccount*> user_list;
// I believe this gets the folder list sorted alphabetically by default,
// but I can't find proof of this always being true.
for (const auto& userFolder : fs::dir(base_dir))
{
if (!userFolder.is_directory)
{
continue;
}
// Is the folder name exactly 8 all-numerical characters long?
// We use strtol to find any non-numeric characters in folder name.
char* nonNumericChar;
std::strtol(userFolder.name.c_str(), &nonNumericChar, 10);
if (userFolder.name.length() != 8 || *nonNumericChar != '\0')
{
continue;
}
// Does the localusername file exist?
if (!fs::is_file(fmt::format("%s/%s/localusername", base_dir, userFolder.name)))
{
continue;
}
UserAccount* user_entry = new UserAccount(userFolder.name);
user_list.emplace_back(user_entry);
}
return user_list;
}
}
user_manager_dialog::user_manager_dialog(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, const std::string& dir, QWidget* parent)
: QDialog(parent), m_user_list(), m_dir(dir), m_sort_column(1), m_sort_ascending(true), m_gui_settings(gui_settings), m_emu_settings(emu_settings)
{
setWindowTitle(tr("User Manager"));
setMinimumSize(QSize(400, 400));
setModal(true);
m_emu_settings->LoadSettings();
Init(dir);
}
void user_manager_dialog::Init(const std::string& dir)
{
// Table
m_table = new QTableWidget(this);
//m_table->setItemDelegate(new table_item_delegate(this)); // to get rid of cell selection rectangles include "table_item_delegate.h"
m_table->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
m_table->setContextMenuPolicy(Qt::CustomContextMenu);
m_table->setColumnCount(2);
m_table->setHorizontalHeaderLabels(QStringList() << tr("User ID") << tr("User Name"));
QPushButton* push_remove_user = new QPushButton(tr("Delete User"), this);
QPushButton* push_create_user = new QPushButton(tr("Create User"), this);
QPushButton* push_login_user = new QPushButton(tr("Log In User"), this);
QPushButton* push_rename_user = new QPushButton(tr("Rename User"), this);
QPushButton* push_close = new QPushButton(tr("&Close"), this);
push_close->setAutoDefault(true);
// Button Layout
QHBoxLayout* hbox_buttons = new QHBoxLayout();
hbox_buttons->addWidget(push_create_user);
hbox_buttons->addWidget(push_login_user);
hbox_buttons->addWidget(push_rename_user);
hbox_buttons->addWidget(push_remove_user);
hbox_buttons->addStretch();
hbox_buttons->addWidget(push_close);
// main layout
QVBoxLayout* vbox_main = new QVBoxLayout();
vbox_main->setAlignment(Qt::AlignCenter);
vbox_main->addWidget(m_table);
vbox_main->addLayout(hbox_buttons);
setLayout(vbox_main);
m_selected_user = m_emu_settings->GetSetting(emu_settings::SelectedUser);
UpdateTable();
restoreGeometry(m_gui_settings->GetValue(gui::um_geometry).toByteArray());
// Use this in multiple connects to protect the current user from deletion/rename.
auto enableButtons = [=]()
{
int idx = m_table->currentRow();
if (idx < 0)
{
return;
}
int idx_real = m_table->item(idx, 0)->data(Qt::UserRole).toInt();
std::string idx_user = m_user_list[idx_real]->GetUserId();
bool enable = idx_user != m_selected_user;
push_rename_user->setEnabled(enable);
push_remove_user->setEnabled(enable);
};
// Connects and events
connect(push_close, &QAbstractButton::clicked, this, &user_manager_dialog::close);
connect(push_remove_user, &QAbstractButton::clicked, this, &user_manager_dialog::OnUserRemove);
connect(push_rename_user, &QAbstractButton::clicked, this, &user_manager_dialog::OnUserRename);
connect(push_create_user, &QAbstractButton::clicked, this, &user_manager_dialog::OnUserCreate);
connect(push_login_user, &QAbstractButton::clicked, this, &user_manager_dialog::OnUserLogin);
connect(this, &user_manager_dialog::OnUserLoginSuccess, this, enableButtons);
connect(m_table, &QTableWidget::itemDoubleClicked, this, &user_manager_dialog::OnUserLogin);
connect(m_table->horizontalHeader(), &QHeaderView::sectionClicked, this, &user_manager_dialog::OnSort);
connect(m_table, &QTableWidget::customContextMenuRequested, this, &user_manager_dialog::ShowContextMenu);
connect(m_table, &QTableWidget::itemClicked, this, enableButtons);
connect(m_table, &QTableWidget::itemSelectionChanged, this, enableButtons);
}
void user_manager_dialog::UpdateTable()
{
if (m_dir == "")
{
// fmt::format(%shome ... is harder to read than straight concatenation.
m_dir = Emu.GetHddDir() + "home";
}
// Get the user folders in the home directory and the currently logged in user.
m_user_list.clear();
m_user_list = GetUserAccounts(m_dir);
// Clear and then repopulate the table with the list gathered above.
m_table->setRowCount(static_cast<int>(m_user_list.size()));
// For indicating logged-in user.
QFont boldFont;
boldFont.setBold(true);
int row = 0;
for (UserAccount* user : m_user_list)
{
QString userId = qstr(user->GetUserId());
QString userName = qstr(user->GetUserName());
QTableWidgetItem* userIdItem = new QTableWidgetItem(userId);
userIdItem->setData(Qt::UserRole, row); // For sorting to work properly
userIdItem->setFlags(userIdItem->flags() & ~Qt::ItemIsEditable);
m_table->setItem(row, 0, userIdItem);
QTableWidgetItem* userNameItem = new QTableWidgetItem(userName);
userNameItem->setData(Qt::UserRole, row); // For sorting to work properly
userNameItem->setFlags(userNameItem->flags() & ~Qt::ItemIsEditable);
m_table->setItem(row, 1, userNameItem);
// Compare current config value with the one in this user (only 8 digits in userId)
if (m_selected_user.compare(0, 8, user->GetUserId()) == 0)
{
userIdItem->setFont(boldFont);
userNameItem->setFont(boldFont);
}
++row;
}
// GUI resizing
m_table->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
m_table->verticalHeader()->resizeSections(QHeaderView::ResizeToContents);
QSize tableSize = QSize(
m_table->verticalHeader()->width() + m_table->horizontalHeader()->length() + m_table->frameWidth() * 2,
m_table->horizontalHeader()->height() + m_table->verticalHeader()->length() + m_table->frameWidth() * 2);
QSize preferredSize = minimumSize().expandedTo(sizeHint() - m_table->sizeHint() + tableSize).expandedTo(size());
QSize maxSize = QSize(preferredSize.width(), static_cast<int>(QApplication::desktop()->screenGeometry().height()*.6));
resize(preferredSize.boundedTo(maxSize));
}
//Remove a user folder, needs to be confirmed.
void user_manager_dialog::OnUserRemove()
{
int idx = m_table->currentRow();
int idx_real = m_table->item(idx, 0)->data(Qt::UserRole).toInt();
if (QMessageBox::question(this, tr("Delete Confirmation"), tr("Are you sure you want to delete:\n%1?").arg(qstr(m_user_list[idx_real]->GetUserName())), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
{
fs::remove_all(m_user_list[idx_real]->GetUserDir());
UpdateTable();
}
}
void user_manager_dialog::GenerateUser(const std::string& username)
{
// If the user list is sorted by defult from fs::dir, then we just need the last one in the list.
std::string largestUserId = m_user_list[m_user_list.size() - 1]->GetUserId();
// Add one to the largest user id, then reformat the result into an 8-digit string.
u8 nextLargest = static_cast<u8>(std::stoul(largestUserId) + 1u);
char* buf;
sprintf(buf, "%08d", nextLargest);
std::string nextUserId(buf);
// Create user folders and such.
const std::string homeDir = Emu.GetHddDir() + "home/";
const std::string userDir = homeDir + nextUserId;
fs::create_dir(homeDir);
fs::create_dir(fmt::format("%s/", userDir));
fs::create_dir(fmt::format("%s/exdata/", userDir));
fs::create_dir(fmt::format("%s/savedata/", userDir));
fs::create_dir(fmt::format("%s/trophy/", userDir));
fs::write_file(fmt::format("%s/localusername", userDir), fs::create + fs::excl + fs::write, username);
}
bool user_manager_dialog::ValidateUsername(const QString& textToValidate)
{
// "Entire string (^...$) must be between 3 and 16 characters
// and only consist of letters, numbers, underscores, and hyphens."
QRegExpValidator validator(QRegExp("^[A-Za-z0-9_-]{3,16}$"));
int pos = 0;
QString text = textToValidate;
return (validator.validate(text, pos) == QValidator::Acceptable);
}
void user_manager_dialog::OnUserRename()
{
QInputDialog* dialog = new QInputDialog(this);
dialog->setWindowTitle(tr("Rename User"));
dialog->setLabelText(tr("New Username: "));
dialog->resize(200, 100);
while (dialog->exec() != QDialog::Rejected)
{
dialog->resize(200, 100);
QString textToValidate = dialog->textValue();
if (!ValidateUsername(textToValidate))
{
QMessageBox::warning(this, tr("Error"), tr("Name must be between 3 and 16 characters and only consist of letters, numbers, underscores, and hyphens."));
continue;
}
int idx = m_table->currentRow();
int idx_real = m_table->item(idx, 0)->data(Qt::UserRole).toInt();
const std::string userId = m_user_list[idx_real]->GetUserId();
const std::string usernameFile = Emu.GetHddDir() + "home/" + userId + "/localusername";
fs::write_file(usernameFile, fs::rewrite, textToValidate.toStdString());
UpdateTable();
break;
}
}
void user_manager_dialog::OnUserCreate()
{
QInputDialog* dialog = new QInputDialog(this);
dialog->setWindowTitle(tr("New User"));
dialog->setLabelText(tr("New Username: "));
dialog->resize(200, 100);
while (dialog->exec() != QDialog::Rejected)
{
dialog->resize(200, 100);
QString textToValidate = dialog->textValue();
if (!ValidateUsername(textToValidate))
{
QMessageBox::warning(this, tr("Error"), tr("Name must be between 3 and 16 characters and only consist of letters, numbers, underscores, and hyphens."));
continue;
}
GenerateUser(textToValidate.toStdString());
UpdateTable();
break;
}
}
void user_manager_dialog::OnUserLogin()
{
int idx = m_table->currentRow();
int idx_real = m_table->item(idx, 0)->data(Qt::UserRole).toInt();
std::string selectedUserId = m_user_list[idx_real]->GetUserId();
m_selected_user = selectedUserId;
m_emu_settings->SetSetting(emu_settings::SelectedUser, m_selected_user);
m_emu_settings->SaveSettings();
UpdateTable();
Q_EMIT OnUserLoginSuccess();
}
void user_manager_dialog::OnSort(int logicalIndex)
{
if (logicalIndex < 0)
{
return;
}
else if (logicalIndex == m_sort_column)
{
m_sort_ascending ^= true;
}
else
{
m_sort_ascending = true;
}
Qt::SortOrder sort_order = m_sort_ascending ? Qt::AscendingOrder : Qt::DescendingOrder;
m_table->sortByColumn(m_sort_column, sort_order);
m_sort_column = logicalIndex;
}
void user_manager_dialog::ShowContextMenu(const QPoint &pos)
{
int idx = m_table->currentRow();
if (idx < 0)
{
return;
}
QPoint globalPos = m_table->mapToGlobal(pos);
QMenu* menu = new QMenu();
// Create all the actions before adding them to the menu/submenus.
QAction* userIdAct = new QAction(tr("User ID"), this);
QAction* userNameAct = new QAction(tr("User Name"), this);
QAction* removeAct = new QAction(tr("&Remove"), this);
QAction* renameAct = new QAction(tr("&Rename"), this);
QAction* loginAct = new QAction(tr("&Login"), this);
QAction* showDirAct = new QAction(tr("&Open User Directory"), this);
//Create submenu for sort options.
m_sort_options = new QMenu(tr("&Sort By"));
m_sort_options->addAction(userIdAct);
m_sort_options->addAction(userNameAct);
// Add all options and submenus to the context menu.
menu->addMenu(m_sort_options);
menu->addSeparator();
menu->addAction(removeAct);
menu->addAction(renameAct);
menu->addAction(loginAct);
menu->addAction(showDirAct);
// Only enable actions if selected user is not logged in user.
int idx_real = m_table->item(idx, 0)->data(Qt::UserRole).toInt();
std::string idx_user = m_user_list[idx_real]->GetUserId();
bool enable = idx_user != m_selected_user;
removeAct->setEnabled(enable);
renameAct->setEnabled(enable);
// Connects and Events
connect(removeAct, &QAction::triggered, this, &user_manager_dialog::OnUserRemove);
connect(renameAct, &QAction::triggered, this, &user_manager_dialog::OnUserRename);
connect(loginAct, &QAction::triggered, this, &user_manager_dialog::OnUserLogin);
connect(showDirAct, &QAction::triggered, [=]()
{
int idx_real = m_table->item(idx, 0)->data(Qt::UserRole).toInt();
QString path = qstr(m_user_list[idx_real]->GetUserDir());
QDesktopServices::openUrl(QUrl("file:///" + path));
});
connect(userIdAct, &QAction::triggered, this, [=] {OnSort(0); });
connect(userNameAct, &QAction::triggered, this, [=] {OnSort(1); });
menu->exec(globalPos);
}
void user_manager_dialog::closeEvent(QCloseEvent *event)
{
m_gui_settings->SetValue(gui::um_geometry, saveGeometry());
QDialog::closeEvent(event);
}

View File

@ -0,0 +1,58 @@
#pragma once
#include "stdafx.h"
#include "gui_settings.h"
#include "emu_settings.h"
#include "Emu/System.h"
#include "Utilities/File.h"
#include "user_account.h"
#include <QDialog>
#include <QTableWidget>
#include <QHBoxLayout>
#include <QPushButton>
#include <QHeaderView>
#include <QMenu>
#include <QMessageBox>
#include <QDesktopWidget>
#include <QApplication>
#include <QUrl>
#include <QDesktopServices>
class user_manager_dialog : public QDialog
{
Q_OBJECT
public:
explicit user_manager_dialog(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, const std::string& dir = "", QWidget* parent = nullptr);
Q_SIGNALS:
void OnUserLoginSuccess();
private Q_SLOTS:
void OnUserLogin();
void OnUserCreate();
void OnUserRemove();
void OnUserRename();
void OnSort(int logicalIndex);
private:
void Init(const std::string& dir);
void UpdateTable();
void GenerateUser(const std::string& username);
bool ValidateUsername(const QString& textToValidate);
void ShowContextMenu(const QPoint &pos);
void closeEvent(QCloseEvent* event) override;
QTableWidget* m_table;
std::string m_dir;
std::string m_selected_user;
std::vector<UserAccount*> m_user_list;
std::shared_ptr<gui_settings> m_gui_settings;
std::shared_ptr<emu_settings> m_emu_settings;
QMenu* m_sort_options;
int m_sort_column;
bool m_sort_ascending;
};