mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge pull request #1242 from wabbajack-tools/origin-support
Add support for Origin
This commit is contained in:
commit
a709d534d4
@ -1,5 +1,10 @@
|
||||
### Changelog
|
||||
|
||||
#### Version - 2.4.0.0 - ???
|
||||
* Wabbajack is now based on .NET 5.0 (does not require a runtime download by users)
|
||||
* Origin is now supported as a game source
|
||||
* Basic (mostly untested) support for Dragon Age : Origins
|
||||
|
||||
#### Version - 2.3.6.2 - 12/31/2020
|
||||
* HOTFIX: Also apply the IPS4 changes to LL Meta lookups
|
||||
|
||||
|
@ -41,7 +41,8 @@ namespace Wabbajack.Common
|
||||
StardewValley,
|
||||
KingdomComeDeliverance,
|
||||
MechWarrior5Mercenaries,
|
||||
NoMansSky
|
||||
NoMansSky,
|
||||
DragonAgeOrigins
|
||||
}
|
||||
|
||||
public static class GameExtensions
|
||||
@ -71,6 +72,11 @@ namespace Wabbajack.Common
|
||||
// to get gog ids: https://www.gogdb.org
|
||||
public List<int>? GOGIDs { get; internal set; }
|
||||
|
||||
// to get these ids, split the numbers from the letters in file names found in
|
||||
// C:\ProgramData\Origin\LocalContent\{game name)\*.mfst
|
||||
// So for DA:O this is "DR208591800.mfst" -> "DR:208591800"
|
||||
public List<string> OriginIDs { get; set; } = new();
|
||||
|
||||
public List<string> EpicGameStoreIDs { get; internal set; } = new List<string>();
|
||||
|
||||
// to get BethNet IDs: check the registry
|
||||
@ -548,7 +554,7 @@ namespace Wabbajack.Common
|
||||
Game = Game.NoMansSky,
|
||||
NexusName = "nomanssky",
|
||||
NexusGameId = 1634,
|
||||
MO2Name = "Mo Man's Sky",
|
||||
MO2Name = "No Man's Sky",
|
||||
SteamIDs = new List<int>{275850},
|
||||
GOGIDs = new List<int>{1446213994},
|
||||
RequiredFiles = new List<string>
|
||||
@ -557,6 +563,22 @@ namespace Wabbajack.Common
|
||||
},
|
||||
MainExecutable = @"Binaries\NMS.exe"
|
||||
}
|
||||
},
|
||||
{
|
||||
Game.DragonAgeOrigins, new GameMetaData
|
||||
{
|
||||
Game = Game.DragonAgeOrigins,
|
||||
NexusName = "dragonage",
|
||||
NexusGameId = 140,
|
||||
MO2Name = "Dragon Age: Origins", // Probably wrong
|
||||
SteamIDs = new List<int>{17450},
|
||||
OriginIDs = new List<string>{"DR:208591800"},
|
||||
RequiredFiles = new List<string>
|
||||
{
|
||||
@"bin_ship\daorigins.exe"
|
||||
},
|
||||
MainExecutable = @"bin_ship\daorigins.exe"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,9 @@ namespace Wabbajack.Common.StoreHandlers
|
||||
{
|
||||
public override Game Game { get; internal set; }
|
||||
public override StoreType Type { get; internal set; } = StoreType.BethNet;
|
||||
|
||||
public AbsolutePath InstallPath;
|
||||
|
||||
}
|
||||
|
||||
public class BethNetHandler : AStoreHandler
|
||||
|
210
Wabbajack.Common/StoreHandlers/OriginHandler.cs
Normal file
210
Wabbajack.Common/StoreHandlers/OriginHandler.cs
Normal file
@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Wabbajack.Common.StoreHandlers
|
||||
{
|
||||
public class OriginHandler : AStoreHandler
|
||||
{
|
||||
private AbsolutePath OriginDataPath = (AbsolutePath)@"C:\ProgramData\Origin\LocalContent";
|
||||
private Extension MFSTExtension = new Extension(".mfst");
|
||||
private HashSet<string> KnownMFSTs = new();
|
||||
|
||||
public override StoreType Type { get; internal set; } = StoreType.Origin;
|
||||
public override bool Init()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!OriginDataPath.Exists)
|
||||
return false;
|
||||
|
||||
KnownMFSTs = OriginDataPath.EnumerateFiles()
|
||||
.Where(f => f.Extension == MFSTExtension)
|
||||
.Select(f => f.FileNameWithoutExtension.ToString())
|
||||
.ToHashSet();
|
||||
|
||||
Utils.Log($"Found MFSTs from Origin: {string.Join(", ", KnownMFSTs)}");
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool LoadAllGames()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var game in GameRegistry.Games)
|
||||
{
|
||||
var mfst = game.Value.OriginIDs.FirstOrDefault(g => KnownMFSTs.Contains(g.Replace(":", "")));
|
||||
if (mfst == null)
|
||||
continue;
|
||||
|
||||
var ogame = new OriginGame(mfst, game.Key, game.Value);
|
||||
ogame.GetAndCacheManifestResponse();
|
||||
Games.Add(ogame);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Log(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class OriginGame : AStoreGame
|
||||
{
|
||||
private string _mfst;
|
||||
private GameMetaData _metaData;
|
||||
|
||||
public OriginGame(string mfst, Game game, GameMetaData metaData)
|
||||
{
|
||||
_mfst = mfst;
|
||||
Game = game;
|
||||
_metaData = metaData;
|
||||
}
|
||||
public override Game Game { get; internal set; }
|
||||
public override StoreType Type { get; internal set; } = StoreType.Origin;
|
||||
|
||||
public override AbsolutePath Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetGamePath();
|
||||
|
||||
}
|
||||
internal set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private AbsolutePath GetGamePath()
|
||||
{
|
||||
var manifestData = GetAndCacheManifestResponse().FromJsonString<GameLocalDataResponse>();
|
||||
var platform = manifestData!.publishing!.softwareList!.software!.FirstOrDefault(a => a.softwarePlatform == "PCWIN");
|
||||
|
||||
var installPath = GetPathFromPlatformPath(platform!.fulfillmentAttributes!.installCheckOverride!);
|
||||
return installPath;
|
||||
|
||||
}
|
||||
|
||||
internal AbsolutePath GetPathFromPlatformPath(string path, RegistryView platformView)
|
||||
{
|
||||
if (!path.StartsWith("["))
|
||||
{
|
||||
return (AbsolutePath)path;
|
||||
}
|
||||
|
||||
var matchPath = Regex.Match(path, @"\[(.*?)\\(.*)\\(.*)\](.*)");
|
||||
if (!matchPath.Success)
|
||||
{
|
||||
Utils.Log("Unknown path format " + path);
|
||||
return default;
|
||||
}
|
||||
|
||||
var root = matchPath.Groups[1].Value;
|
||||
|
||||
RegistryKey rootKey = root switch
|
||||
{
|
||||
"HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, platformView),
|
||||
"HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, platformView),
|
||||
_ => throw new Exception("Unknown registry root entry " + root)
|
||||
};
|
||||
|
||||
var subPath = matchPath.Groups[2].Value.Trim('\\');
|
||||
var key = matchPath.Groups[3].Value;
|
||||
var executable = matchPath.Groups[4].Value.Trim('\\');
|
||||
var subKey = rootKey.OpenSubKey(subPath);
|
||||
if (subKey == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var keyValue = rootKey!.OpenSubKey(subPath)!.GetValue(key);
|
||||
if (keyValue == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return (AbsolutePath)keyValue!.ToString()!;
|
||||
}
|
||||
|
||||
internal AbsolutePath GetPathFromPlatformPath(string path)
|
||||
{
|
||||
var resultPath = GetPathFromPlatformPath(path, RegistryView.Registry64);
|
||||
if (resultPath == default)
|
||||
{
|
||||
resultPath = GetPathFromPlatformPath(path, RegistryView.Registry32);
|
||||
}
|
||||
|
||||
return resultPath;
|
||||
}
|
||||
|
||||
private AbsolutePath ManifestCacheLocation =>
|
||||
Consts.LocalAppDataPath.Combine("OriginManifestCache", _mfst.Replace(":", ""));
|
||||
|
||||
internal string GetAndCacheManifestResponse()
|
||||
{
|
||||
if (ManifestCacheLocation.Exists)
|
||||
{
|
||||
return ManifestCacheLocation.ReadAllText();
|
||||
}
|
||||
|
||||
Utils.Log($"Getting Origin Manifest info for {_mfst}");
|
||||
var client = new HttpClient();
|
||||
var data = client.GetStringAsync($"https://api1.origin.com/ecommerce2/public/{_mfst}/en_US").Result;
|
||||
ManifestCacheLocation.Parent.CreateDirectory();
|
||||
ManifestCacheLocation.WriteAllTextAsync(data).Wait();
|
||||
return data;
|
||||
}
|
||||
|
||||
public class GameLocalDataResponse
|
||||
{
|
||||
public class LocalizableAttributes
|
||||
{
|
||||
public string? longDescription;
|
||||
public string? displayName;
|
||||
}
|
||||
|
||||
public class Publishing
|
||||
{
|
||||
public class Software
|
||||
{
|
||||
public class FulfillmentAttributes
|
||||
{
|
||||
public string? executePathOverride;
|
||||
public string? installationDirectory;
|
||||
public string? installCheckOverride;
|
||||
}
|
||||
|
||||
public string? softwareId;
|
||||
public string? softwarePlatform;
|
||||
public FulfillmentAttributes? fulfillmentAttributes;
|
||||
}
|
||||
|
||||
public class SoftwareList
|
||||
{
|
||||
public List<Software>? software;
|
||||
}
|
||||
|
||||
public SoftwareList? softwareList;
|
||||
}
|
||||
|
||||
public string? offerId;
|
||||
public string? offerType;
|
||||
public Publishing? publishing;
|
||||
public LocalizableAttributes? localizableAttributes;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,8 @@ namespace Wabbajack.Common.StoreHandlers
|
||||
STEAM,
|
||||
GOG,
|
||||
BethNet,
|
||||
EpicGameStore
|
||||
EpicGameStore,
|
||||
Origin
|
||||
}
|
||||
|
||||
public class StoreHandler
|
||||
@ -30,6 +31,9 @@ namespace Wabbajack.Common.StoreHandlers
|
||||
private static readonly Lazy<EpicGameStoreHandler> _epicGameStoreHandler = new Lazy<EpicGameStoreHandler>(() => new EpicGameStoreHandler());
|
||||
public EpicGameStoreHandler EpicGameStoreHandler = _epicGameStoreHandler.Value;
|
||||
|
||||
private static readonly Lazy<OriginHandler> _originHandler = new Lazy<OriginHandler>(() => new OriginHandler());
|
||||
public OriginHandler OriginHandler = _originHandler.Value;
|
||||
|
||||
public List<AStoreGame> StoreGames;
|
||||
|
||||
public StoreHandler()
|
||||
@ -83,6 +87,18 @@ namespace Wabbajack.Common.StoreHandlers
|
||||
{
|
||||
Utils.Error(new StoreException("Could not Init the EpicGameStoreHandler, check previous error messages!"));
|
||||
}
|
||||
|
||||
if (OriginHandler.Init())
|
||||
{
|
||||
if (OriginHandler.LoadAllGames())
|
||||
StoreGames.AddRange(OriginHandler.Games);
|
||||
else
|
||||
Utils.Error(new StoreException("Could not load all Games from the OriginHandler, check previous error messages!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.Error(new StoreException("Could not Init the OriginHandler, check previous error messages!"));
|
||||
}
|
||||
}
|
||||
|
||||
public AbsolutePath? TryGetGamePath(Game game)
|
||||
|
29
Wabbajack.Test/GameStoreTests.cs
Normal file
29
Wabbajack.Test/GameStoreTests.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Wabbajack.Test
|
||||
{
|
||||
public class GameStoreTests : ATestBase
|
||||
{
|
||||
public GameStoreTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Comment out this [Fact] when not testing by hand. It's too hard to have all games installed at all times
|
||||
/// so we only run this test as neede
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
//[Fact]
|
||||
public async Task OriginGameStoreTest()
|
||||
{
|
||||
Assert.True(Game.DragonAgeOrigins.MetaData().TryGetGameLocation(out var loc));
|
||||
Assert.NotEqual(default, loc);
|
||||
Assert.Equal((AbsolutePath)@"c:\Games\Dragon Age", loc);
|
||||
Assert.True(Game.DragonAgeOrigins.MetaData().IsInstalled);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user