Skip to content

Commit

Permalink
TEMP
Browse files Browse the repository at this point in the history
  • Loading branch information
Foxtrot47 committed Jan 1, 2024
1 parent 0422f99 commit 1027852
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 87 deletions.
3 changes: 3 additions & 0 deletions Crimson/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<x:String x:Key="AppTitleName">Crimson</x:String>
<x:Double x:Key="ContentDialogMaxWidth">1200</x:Double>
<x:Double x:Key="ContentDialogWidth">1200</x:Double>
<Style TargetType="MenuFlyoutItem">
<Setter Property="FontSize" Value="17"/>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
215 changes: 138 additions & 77 deletions Crimson/Core/InstallManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class InstallManager

private readonly ConcurrentQueue<DownloadTask> _downloadQueue = new();
private readonly ConcurrentQueue<IoTask> _ioQueue = new();
private readonly CancellationTokenSource _cancellationTokenSource = new();
private CancellationTokenSource _cancellationTokenSource = new();

private readonly ConcurrentDictionary<string, object> _fileLocksConcurrentDictionary = new();

Expand Down Expand Up @@ -130,13 +130,6 @@ private async void ProcessNext()

// TODO Handle stats if game is installed

// create CurrentInstall.folder if it doesn't exist
if (!Directory.Exists(CurrentInstall.Location))
{
Directory.CreateDirectory(CurrentInstall.Location);
_log.Debug("Folder created at: {location}", CurrentInstall.Location);
}

if (!HasFolderWritePermissions(CurrentInstall.Location))
{
_log.Error("ProcessNext: No write permissions to {Location}", CurrentInstall.Location);
Expand All @@ -146,44 +139,39 @@ private async void ProcessNext()
ProcessNext();
}

var chunkDownloadList = new List<ChunkInfo>();

foreach (var fileManifest in data.FileManifestList.Elements)
if (CurrentInstall.Action == ActionType.Install)
{
foreach (var chunkPart in fileManifest.ChunkParts)
// create CurrentInstall.folder if it doesn't exist
if (!Directory.Exists(CurrentInstall.Location))
{
if (_chunkToFileManifestsDictionary.TryGetValue(chunkPart.GuidStr, out var fileManifests))
{
fileManifests.Add(fileManifest);
_chunkToFileManifestsDictionary[chunkPart.GuidStr] = fileManifests;
}
else
{
_ = _chunkToFileManifestsDictionary.TryAdd(chunkPart.GuidStr,
new List<FileManifest>() { fileManifest });
}
Directory.CreateDirectory(CurrentInstall.Location);
_log.Debug("Folder created at: {location}", CurrentInstall.Location);
}

if (chunkDownloadList.FirstOrDefault(chunk => chunk.GuidStr == chunkPart.GuidStr) != null) continue;
GetChunksToDownload(manifestData, data);
}
else if (CurrentInstall.Action == ActionType.Uninstall)
{
foreach (var fileManifest in data.FileManifestList.Elements)
{
CurrentInstall.TotalWriteSizeMb += fileManifest.FileSize / 1000000.0;

var chunkInfo = data.CDL.GetChunkByGuid(chunkPart.GuidStr);
var newTask = new DownloadTask()
var task = new IoTask()
{
Url = manifestData.BaseUrls.FirstOrDefault() + "/" + chunkInfo.Path,
TempPath = Path.Combine(CurrentInstall.Location, ".temp", (chunkInfo.GuidStr + ".chunk")),
Guid = chunkInfo.GuidStr,
ChunkInfo = chunkInfo
DestinationFilePath = Path.Combine(CurrentInstall.Location, fileManifest.Filename),
TaskType = IoTaskType.Delete,
Size = fileManifest.FileSize,
};
_log.Debug("ProcessNext: Adding new download task {@task}", newTask);
chunkDownloadList.Add(chunkInfo);
_downloadQueue.Enqueue(newTask);

CurrentInstall.TotalDownloadSizeMb += chunkInfo.FileSize / 1000000.0;
_log.Debug("ProcessNext: Adding ioTask: {task}", task);
_ioQueue.Enqueue(task);
}
CurrentInstall.TotalWriteSizeMb += fileManifest.FileSize / 1000000.0;
}

CurrentInstall!.Status = ActionStatus.Processing;
InstallationStatusChanged?.Invoke(CurrentInstall);

// Reset cancellation token
_cancellationTokenSource = new CancellationTokenSource();
_installStopWatch.Start();

for (var i = 0; i < _numberOfThreads; i++)
Expand Down Expand Up @@ -448,6 +436,24 @@ private async Task ProcessIoQueue()
}
}
break;
case IoTaskType.Delete:
File.Delete(ioTask.DestinationFilePath);
lock (_installItemLock)
{
CurrentInstall.WrittenSize += ioTask.Size / 1000000.0;
CurrentInstall.WriteSpeed = _installStopWatch.IsRunning && _installStopWatch.Elapsed.TotalSeconds > 0
? Math.Round(CurrentInstall.WrittenSize / _installStopWatch.Elapsed.TotalSeconds, 2)
: 0;
CurrentInstall.ProgressPercentage = Convert.ToInt32((CurrentInstall.WrittenSize / CurrentInstall.TotalWriteSizeMb) * 100);

// Limit firing progress update events
if ((DateTime.Now - _lastUpdateTime).TotalMilliseconds >= _progressUpdateIntervalInMS)
{
_lastUpdateTime = DateTime.Now;
InstallProgressUpdate?.Invoke(CurrentInstall);
}
}
break;
}
}
catch (Exception ex)
Expand All @@ -460,7 +466,6 @@ private async Task ProcessIoQueue()
InstallationStatusChanged?.Invoke(CurrentInstall);
CurrentInstall = null;
}

ProcessNext();
}
}
Expand Down Expand Up @@ -511,64 +516,76 @@ private async Task UpdateInstalledGameStatus()
CurrentInstall.AppName);
throw new Exception("Invalid game data");
}
var installedGamesDictionary = _storage.InstalledGamesDictionary;
installedGamesDictionary ??= [];

if (!_storage.InstalledGamesDictionary.TryGetValue(CurrentInstall.AppName, out InstalledGame installedGame))
if (installedGamesDictionary.Count > 0 && CurrentInstall.Action == ActionType.Uninstall)
{
installedGame = new InstalledGame();
installedGamesDictionary.Remove(CurrentInstall.AppName);
_log.Information("UpdateInstalledGameStatus: Removing entry: {appName} from installed games list", CurrentInstall.AppName);
}
else
{
if (!installedGamesDictionary.TryGetValue(CurrentInstall.AppName, out var installedGame))
{
installedGame = new InstalledGame();
}

var manifestDataBytes = await _repository.GetGameManifest(gameData.AssetInfos.Windows.Namespace,
gameData.AssetInfos.Windows.CatalogItemId, gameData.AppName);
var manifestData = Manifest.ReadAll(manifestDataBytes.ManifestBytes);
var manifestDataBytes = await _repository.GetGameManifest(gameData.AssetInfos.Windows.Namespace,
gameData.AssetInfos.Windows.CatalogItemId, gameData.AppName);
var manifestData = Manifest.ReadAll(manifestDataBytes.ManifestBytes);

// Verify all the files
CurrentInstall.Action = ActionType.Verify;
InstallationStatusChanged?.Invoke(CurrentInstall);
var invalidFilesList = await VerifyFiles(CurrentInstall.Location, manifestData.FileManifestList.Elements);
// Verify all the files
CurrentInstall.Action = ActionType.Verify;
InstallationStatusChanged?.Invoke(CurrentInstall);
var invalidFilesList = await VerifyFiles(CurrentInstall.Location, manifestData.FileManifestList.Elements);

if (invalidFilesList.Count > 0)
{
// We will handle this later
// For now fail install
throw new Exception("UpdateInstalledGameStatus: Verification failed");
}
_log.Information("UpdateInstalledGameStatus: Verification successful for {appName}", CurrentInstall.AppName);
if (invalidFilesList.Count > 0)
{
// We will handle this later
// For now fail install
throw new Exception("UpdateInstalledGameStatus: Verification failed");
}
_log.Information("UpdateInstalledGameStatus: Verification successful for {appName}", CurrentInstall.AppName);

await File.WriteAllBytesAsync(CurrentInstall.Location + "/.temp/manifest", manifestDataBytes.ManifestBytes);
await File.WriteAllBytesAsync(CurrentInstall.Location + "/.temp/manifest", manifestDataBytes.ManifestBytes);

var canRunOffLine = gameData.Metadata.CustomAttributes.CanRunOffline.Value == "true";
var requireOwnerShipToken = gameData.Metadata.CustomAttributes?.OwnershipToken?.Value == "true";
var canRunOffLine = gameData.Metadata.CustomAttributes.CanRunOffline.Value == "true";
var requireOwnerShipToken = gameData.Metadata.CustomAttributes?.OwnershipToken?.Value == "true";

if (installedGame?.AppName == null)
{
installedGame = new InstalledGame()
if (installedGame?.AppName == null)
{
AppName = CurrentInstall.AppName,
IsDlc = gameData.IsDlc()
};
}
installedGame = new InstalledGame()
{
AppName = CurrentInstall.AppName,
IsDlc = gameData.IsDlc()
};
}

installedGame.BaseUrls = gameData.BaseUrls;
installedGame.CanRunOffline = canRunOffLine;
installedGame.Executable = manifestData.ManifestMeta.LaunchExe;
installedGame.InstallPath = CurrentInstall.Location;
installedGame.LaunchParameters = manifestData.ManifestMeta.LaunchCommand;
installedGame.RequiresOt = requireOwnerShipToken;
installedGame.Version = manifestData.ManifestMeta.BuildVersion;
installedGame.Title = gameData.AppTitle;
installedGame.BaseUrls = gameData.BaseUrls;
installedGame.CanRunOffline = canRunOffLine;
installedGame.Executable = manifestData.ManifestMeta.LaunchExe;
installedGame.InstallPath = CurrentInstall.Location;
installedGame.LaunchParameters = manifestData.ManifestMeta.LaunchCommand;
installedGame.RequiresOt = requireOwnerShipToken;
installedGame.Version = manifestData.ManifestMeta.BuildVersion;
installedGame.Title = gameData.AppTitle;

if (manifestData.ManifestMeta.UninstallActionPath != null)
{
installedGame.Uninstaller = new Dictionary<string, string>
if (manifestData.ManifestMeta.UninstallActionPath != null)
{
installedGame.Uninstaller = new Dictionary<string, string>
{
{ manifestData.ManifestMeta.UninstallActionPath, manifestData.ManifestMeta.UninstallActionArgs }
};
}
}

_log.Information("UpdateInstalledGameStatus: Adding new entry installed games list {@entry}",
installedGame);

_log.Information("UpdateInstalledGameStatus: Adding new entry installed games list {@entry}",
installedGame);
installedGamesDictionary.Add(gameData.AppName, installedGame);
}

_storage.SaveInstalledGamesList(installedGame);
_storage.UpdateInstalledGames(installedGamesDictionary);

gameData.InstallStatus = CurrentInstall.Action switch
{
Expand Down Expand Up @@ -626,6 +643,50 @@ await Parallel.ForEachAsync(fileManifestLists, options, async (manifest, token)
});
return invalidFilesList;
}

/// <summary>
/// Retrieves the chunks to download from the file manifest list
/// </summary>
/// <param name="manifestData"></param>
/// <param name="data"></param>
private void GetChunksToDownload(GetGameManifest manifestData, Manifest data)
{
var chunkDownloadList = new List<ChunkInfo>();

foreach (var fileManifest in data.FileManifestList.Elements)
{
foreach (var chunkPart in fileManifest.ChunkParts)
{
if (_chunkToFileManifestsDictionary.TryGetValue(chunkPart.GuidStr, out var fileManifests))
{
fileManifests.Add(fileManifest);
_chunkToFileManifestsDictionary[chunkPart.GuidStr] = fileManifests;
}
else
{
_ = _chunkToFileManifestsDictionary.TryAdd(chunkPart.GuidStr,
new List<FileManifest>() { fileManifest });
}

if (chunkDownloadList.FirstOrDefault(chunk => chunk.GuidStr == chunkPart.GuidStr) != null) continue;

var chunkInfo = data.CDL.GetChunkByGuid(chunkPart.GuidStr);
var newTask = new DownloadTask()
{
Url = manifestData.BaseUrls.FirstOrDefault() + "/" + chunkInfo.Path,
TempPath = Path.Combine(CurrentInstall.Location, ".temp", (chunkInfo.GuidStr + ".chunk")),
Guid = chunkInfo.GuidStr,
ChunkInfo = chunkInfo
};
_log.Debug("ProcessNext: Adding new download task {@task}", newTask);
chunkDownloadList.Add(chunkInfo);
_downloadQueue.Enqueue(newTask);

CurrentInstall.TotalDownloadSizeMb += chunkInfo.FileSize / 1000000.0;
}
CurrentInstall.TotalWriteSizeMb += fileManifest.FileSize / 1000000.0;
}
}
}

internal class InstallItemComparer : IEqualityComparer<InstallItem>
Expand Down
2 changes: 1 addition & 1 deletion Crimson/Crimson.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<UseWinUI>true</UseWinUI>
<AssemblyName>Crimson</AssemblyName>
<EnableMsixTooling>true</EnableMsixTooling>
<!--<WindowsPackageType>None</WindowsPackageType>-->
<WindowsPackageType>None</WindowsPackageType>
<UseRidGraph>true</UseRidGraph>
</PropertyGroup>

Expand Down
4 changes: 2 additions & 2 deletions Crimson/Utils/Storage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ public void SaveMetaData(Game game)
_gameMetaDataDictionary.TryAdd(game.AppName, game);
}

public void SaveInstalledGamesList(InstalledGame installedGame)
public void UpdateInstalledGames(Dictionary<string, InstalledGame> installedGamesDict)
{
_installedGamesDictionary.TryAdd(installedGame.AppName, installedGame);
_installedGamesDictionary = installedGamesDict;

var jsonString = JsonSerializer.Serialize(_installedGamesDictionary);

Expand Down
19 changes: 13 additions & 6 deletions Crimson/Views/GameInfoPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
</StackPanel>
</Button>

<DropDownButton IsEnabled="False">
<DropDownButton x:Name="DropDownSecondaryActions" IsEnabled="{x:Bind IsInstalled}">
<DropDownButton.Content>
<StackPanel Orientation="Horizontal" Spacing="10" Padding="10,5,10,5">
<FontIcon Glyph="&#xE713;" />
Expand All @@ -91,11 +91,18 @@
</DropDownButton.Content>
<DropDownButton.Flyout>
<MenuFlyout Placement="Bottom">
<MenuFlyoutItem Text="Uninstall">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="&#xE74D;" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="MinWidth" Value="170" />
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyout.Items>
<MenuFlyoutItem Text="Uninstall" Click="UninstallBtn_Click">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="&#xE74D;" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyout.Items>
</MenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
Expand Down
Loading

0 comments on commit 1027852

Please sign in to comment.