mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Add support for Origin
This commit is contained in:
parent
b169ddb36d
commit
dea841136f
@ -1,5 +1,10 @@
|
|||||||
### Changelog
|
### 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
|
#### Version - 2.3.6.2 - 12/31/2020
|
||||||
* HOTFIX: Also apply the IPS4 changes to LL Meta lookups
|
* HOTFIX: Also apply the IPS4 changes to LL Meta lookups
|
||||||
|
|
||||||
|
@ -41,7 +41,8 @@ namespace Wabbajack.Common
|
|||||||
StardewValley,
|
StardewValley,
|
||||||
KingdomComeDeliverance,
|
KingdomComeDeliverance,
|
||||||
MechWarrior5Mercenaries,
|
MechWarrior5Mercenaries,
|
||||||
NoMansSky
|
NoMansSky,
|
||||||
|
DragonAgeOrigins
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GameExtensions
|
public static class GameExtensions
|
||||||
@ -71,6 +72,11 @@ namespace Wabbajack.Common
|
|||||||
// to get gog ids: https://www.gogdb.org
|
// to get gog ids: https://www.gogdb.org
|
||||||
public List<int>? GOGIDs { get; internal set; }
|
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>();
|
public List<string> EpicGameStoreIDs { get; internal set; } = new List<string>();
|
||||||
|
|
||||||
// to get BethNet IDs: check the registry
|
// to get BethNet IDs: check the registry
|
||||||
@ -548,7 +554,7 @@ namespace Wabbajack.Common
|
|||||||
Game = Game.NoMansSky,
|
Game = Game.NoMansSky,
|
||||||
NexusName = "nomanssky",
|
NexusName = "nomanssky",
|
||||||
NexusGameId = 1634,
|
NexusGameId = 1634,
|
||||||
MO2Name = "Mo Man's Sky",
|
MO2Name = "No Man's Sky",
|
||||||
SteamIDs = new List<int>{275850},
|
SteamIDs = new List<int>{275850},
|
||||||
GOGIDs = new List<int>{1446213994},
|
GOGIDs = new List<int>{1446213994},
|
||||||
RequiredFiles = new List<string>
|
RequiredFiles = new List<string>
|
||||||
@ -557,6 +563,22 @@ namespace Wabbajack.Common
|
|||||||
},
|
},
|
||||||
MainExecutable = @"Binaries\NMS.exe"
|
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 Game Game { get; internal set; }
|
||||||
public override StoreType Type { get; internal set; } = StoreType.BethNet;
|
public override StoreType Type { get; internal set; } = StoreType.BethNet;
|
||||||
|
|
||||||
|
public AbsolutePath InstallPath;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BethNetHandler : AStoreHandler
|
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,
|
STEAM,
|
||||||
GOG,
|
GOG,
|
||||||
BethNet,
|
BethNet,
|
||||||
EpicGameStore
|
EpicGameStore,
|
||||||
|
Origin
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StoreHandler
|
public class StoreHandler
|
||||||
@ -30,6 +31,9 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
private static readonly Lazy<EpicGameStoreHandler> _epicGameStoreHandler = new Lazy<EpicGameStoreHandler>(() => new EpicGameStoreHandler());
|
private static readonly Lazy<EpicGameStoreHandler> _epicGameStoreHandler = new Lazy<EpicGameStoreHandler>(() => new EpicGameStoreHandler());
|
||||||
public EpicGameStoreHandler EpicGameStoreHandler = _epicGameStoreHandler.Value;
|
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 List<AStoreGame> StoreGames;
|
||||||
|
|
||||||
public StoreHandler()
|
public StoreHandler()
|
||||||
@ -83,6 +87,18 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
{
|
{
|
||||||
Utils.Error(new StoreException("Could not Init the EpicGameStoreHandler, check previous error messages!"));
|
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)
|
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