This should let Xenia load in Account files from an actual 360, one step closer to fully supporting 360 profiles!
Only decrypts/reads atm, but the code is there for re-encrypting, maybe once new UI is ready we can make use of that for creating new 360-compatible profiles?
Also added more error checking, and changed XELOG statements that use %ws to use %s & xe::to_string instead, seems to_string handles some non-latin strings better than %ws does.
For example if a user applies a TU that adds achievements after already running the non-patched game, this should add any new achievements it finds.
Will also update the last_played time of a title when the SPA gets loaded in (on game launch), and also set the unlock_time when unlocking achievements.
Unlocking also only updates the title GPD (+ dash GPD) now, instead of needing to rewrite every GPD.
Velocity should now load Xenia-created GPDs fine :D
To try it just make a new profile package in Velocity, inject GPDs from Xenia, and inject random Account file from a 360 profile.
Then close created package, use Tools -> Profile Tools -> Profile Editor to open the package you just created, and if all goes well the profile should load up fine.
Seems to work fine, tested with Tetris TGM ACE which uses achievements to track progression & unlock things in the game, and unlocking achievements in the GPDs does unlock things in game as intended.
Haven't actually tested unlocking the achievements in-game and then checking though (my tetris skills are awful :P), but I think it should work fine.
Strings aren't copied into it yet since I didn't want to alloc guest memory for every CreateEnumerator call...
Not many games actually have in-game achievement lists though, but I've added a placeholder string for any that might.
Like said in the comment, maybe we should alloc those strings in guest mem when the game GPD/SPA is first loaded in?
XGIUserWriteAchievements seems to be the main (only?) way achievements are unlocked, when used that'll now try getting the GPD from UserProfile and unlock the achievements passed to it.
Dash GPD info is recalced and GPDs get written to disk whenever achievements are unlocked, letting achievements persist across instances.
TODO:
- "Achievement Unlocked!" UI, writing to log isn't much of an indicator :P
- XamUserCreateAchievementEnumerator, needs to return XACHIEVEMENT_DETAILS structs, which also contain pointers to strings in guest memory...
- Copy over more data from SPA? (need to check what data 360 copies to it)
- Optimize GPD writing, atm every GPD gets rewritten even if it hasn't been changed...
- Change profile folder path? Maybe allow using 360 profiles eventually?
- GPD settings