Skip to content
This repository has been archived by the owner on Jan 6, 2022. It is now read-only.

Commit

Permalink
Addd manifest listing from both folders and crab files
Browse files Browse the repository at this point in the history
  • Loading branch information
REHERC committed May 14, 2021
1 parent 3fa86a2 commit a4a9838
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 19 deletions.
4 changes: 2 additions & 2 deletions App.AdventureMaker.Core/Tasks/ExportProjectTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Distance.AdventureMaker.Common.Models;
using Distance.AdventureMaker.Common.Models.Resources;
using Newtonsoft.Json;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -78,7 +77,8 @@ void hash(string resource)
archive.AddEntry("readme.txt", Resources.GetText("archive_readme.txt").GetStream());
progress.Value++;
archive.AddEntry("$campaign", string.Empty.GetStream());
progress.Value++;
progress.Value++;
editor.Document.Metadata.Version = DateTime.Now.TimeOfDay.Ticks;
archive.AddEntry("project.json", JsonConvert.SerializeObject(editor.Document).GetStream());
progress.Value++;
archive.AddEntry("hashes.json", JsonConvert.SerializeObject(hashes).GetStream());
Expand Down
2 changes: 1 addition & 1 deletion App.AdventureMaker.Core/Tasks/ImportProjectTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ string hash(string entry)
}
}

foreach (var hashEntry in hashes)
foreach (KeyValuePair<string, string> hashEntry in hashes)
{
progress.Status = $"Checking data validity... ({progress.Value + 1}/{progress.Maximum})";

Expand Down
5 changes: 2 additions & 3 deletions Distance.AdventureMaker/Loader/CampaignLoaderLogic.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Centrifuge.Distance.Game;
using Distance.AdventureMaker.Loader.Steps;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
Expand Down Expand Up @@ -45,7 +44,7 @@ public sealed class CampaignLoader : IEnumerable<LoaderTask>
{
private readonly Queue<LoaderTask> tasks;

public CampaignWorkspaceSetup WorkspaceSetup { get; }
public CampaignWorkspaceSetup Workspace { get; }

public CampaignListing Listing { get; }

Expand All @@ -57,7 +56,7 @@ public CampaignLoader()
{
tasks = new Queue<LoaderTask>();

tasks.Enqueue(WorkspaceSetup = new CampaignWorkspaceSetup(this));
tasks.Enqueue(Workspace = new CampaignWorkspaceSetup(this));
tasks.Enqueue(Listing = new CampaignListing(this));
tasks.Enqueue(Extractor = new CampaignExtractor(this));
tasks.Enqueue(Importer = new CampaignImporter(this));
Expand Down
5 changes: 5 additions & 0 deletions Distance.AdventureMaker/Loader/Steps/CampaignExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public CampaignExtractor(CampaignLoader loader) : base(loader)

public override IEnumerator Run(Task.Status status)
{
foreach (var item in loader.Listing)
{
Mod.Instance.Logger.Info($"[{item.Value.source}]\t {item.Key} : {item.Value.path.FullName}");
}

yield break;
}
}
Expand Down
4 changes: 4 additions & 0 deletions Distance.AdventureMaker/Loader/Steps/CampaignImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public CampaignImporter(CampaignLoader loader) : base(loader)

public override IEnumerator Run(Task.Status status)
{
yield break;

/*
DiscordRpc.RichPresence rpc = new DiscordRpc.RichPresence();
rpc.details = "Waiting...";
rpc.state = "In Main Menu";
Expand All @@ -39,6 +42,7 @@ public override IEnumerator Run(Task.Status status)
DiscordRpc.UpdatePresence(ref rpc);
yield return null;
}
*/
}
}
}
230 changes: 220 additions & 10 deletions Distance.AdventureMaker/Loader/Steps/CampaignListing.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,246 @@
using Centrifuge.Distance.Game;
using Distance.AdventureMaker.Common.Models;
using Newtonsoft.Json;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using static Distance.AdventureMaker.Loader.CampaignLoaderLogic;

namespace Distance.AdventureMaker.Loader.Steps
{
public class CampaignListing : LoaderTask
public class CampaignListing : LoaderTask, IEnumerable<KeyValuePair<string, CampaignListing.CampaignItem>>
{
private readonly CampaignItem.Collection Manifests;

public CampaignListing(CampaignLoader loader) : base(loader)
{
Manifests = new CampaignItem.Collection();
}

public override IEnumerator Run(Task.Status status)
{
const int max = 100;
/*
List all .crab files and extracted folders
For each folder, check project.json and fill the map
Proceed to scan every .crab file
If
.crab file cid is not in map
or cid is in map but version is higher
Then update extraction map with higher version
For each item in extraction map
If update was made, set flag to true
Later: if flag is true: ask if the mod should destroy the source .crab file
*/

status.SetText("Scanning workspace...");
status.SetProgress(0, 1);

foreach (DirectoryInfo workspace in loader.Workspace)
{
// Scan already extracted campaigns first
foreach (DirectoryInfo directory in workspace.GetDirectories())
{
FileInfo projectFile = new FileInfo(Path.Combine(directory.FullName, "project.json"));

if (projectFile.Exists)
{
CampaignFile manifest = Json.Load<CampaignFile>(projectFile, null);

if (manifest != null)
{
Manifests.Add(manifest, directory);
}
}
}

// Scan archive files
foreach (FileInfo file in workspace.GetFiles("*.crab"))
{
using (Stream archiveStream = File.OpenRead(file.FullName))
{
if (!ZipArchive.IsZipFile(archiveStream))
{
continue;
}

using (IArchive archive = ZipArchive.Open(archiveStream))
{
Dictionary<string, IArchiveEntry> entries = archive.GetFileEntries();

bool required_entries_present =
entries.ContainsKey("$campaign")
&& entries.ContainsKey("project.json")
&& entries.ContainsKey("hashes.json");

if (!required_entries_present)
{
continue;
}

Dictionary<string, string> hashes = JsonConvert.DeserializeObject<Dictionary<string, string>>(entries["hashes.json"].GetText());

string hash(string entry)
{
using (HashAlgorithm ha = SHA512.Create())
{
byte[] hashed = ha.ComputeHash(entries[$"resources/{entry.Replace("\\", "/")}"].OpenEntryStream());
return BitConverter.ToString(hashed).Replace("-", "");
}
}

if (!hashes.All(hashEntry => string.Equals(hashEntry.Value, hash(hashEntry.Key))))
{
continue;
}

status.SetText("Setting up...");
try
{
CampaignFile manifest = JsonConvert.DeserializeObject<CampaignFile>(entries["project.json"].GetText());

if (manifest != null)
{
Manifests.Add(manifest, file);
}
}
catch (Exception)
{
continue;
}
}
}
}
}

status.SetText("");
status.SetProgress(0, 1);

yield return Task.Wait(1.5f);
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public IEnumerator<KeyValuePair<string, CampaignItem>> GetEnumerator()
{
return Manifests.GetEnumerator();
}

public struct CampaignItem : IEquatable<CampaignItem>, IComparable<CampaignItem>
{
public enum Source : byte
{
Archive = 0,
Folder = 1
}

public class Collection : Dictionary<string, CampaignItem>
{
public void Add(CampaignFile manifest, DirectoryInfo directory)
{
Add(manifest, directory, Source.Folder);
}

for (uint i = 1; i <= max; ++i)
public void Add(CampaignFile manifest, FileInfo file)
{
Add(manifest, file, Source.Archive);
}

public void Add(CampaignFile manifest, FileSystemInfo fsi, Source source)
{
CampaignMetadata metadata = manifest.Metadata;
CampaignItem item = new CampaignItem(source, fsi, metadata.Version);

if (!ContainsKey(metadata.Guid))
{
this[metadata.Guid] = item;
}
else
{
if (item > this[metadata.Guid])
{
this[metadata.Guid] = item;
}
}
}
}

public Source source;
public FileSystemInfo path;
public long version;

public CampaignItem(Source source, FileSystemInfo path, long version)
{
this.source = source;
this.path = path;
this.version = version;
}

public int CompareTo(CampaignItem other)
{
int versionCompare = version.CompareTo(other.version);
if (versionCompare == 0)
{
return source.CompareTo(other.source);
}
return versionCompare;
}

public override bool Equals(object obj)
{
return obj is CampaignItem info && Equals(info);
}

public bool Equals(CampaignItem other)
{
return version == other.version && source == other.source;
}

public override int GetHashCode()
{
int hashCode = -1235800969;
hashCode = (hashCode * -1521134295) + version.GetHashCode();
hashCode = (hashCode * -1521134295) + source.GetHashCode();
return hashCode;
}

public static bool operator ==(CampaignItem left, CampaignItem right)
{
return left.Equals(right);
}

public static bool operator !=(CampaignItem left, CampaignItem right)
{
return !(left == right);
}

public static bool operator >(CampaignItem left, CampaignItem right)
{
status.SetText($"Running task {i} of {max}...");
status.SetProgress(i, max);
return left.CompareTo(right) == 1;
}

yield return Task.Wait(0.05f);
public static bool operator <(CampaignItem left, CampaignItem right)
{
return right.CompareTo(left) == 1;
}

status.SetText("Finishing...");
status.SetProgress(1, 1);
public static bool operator >=(CampaignItem left, CampaignItem right)
{
return left > right || left == right;
}

yield return Task.Wait(2.0f);
public static bool operator <=(CampaignItem left, CampaignItem right)
{
return left < right || left == right;
}
}
}
}
18 changes: 15 additions & 3 deletions Distance.AdventureMaker/Loader/Steps/CampaignWorkspaceSetup.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
using Centrifuge.Distance.Game;
using Reactor.API.Storage;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using static Distance.AdventureMaker.Loader.CampaignLoaderLogic;

namespace Distance.AdventureMaker.Loader.Steps
{
public class CampaignWorkspaceSetup : LoaderTask
public class CampaignWorkspaceSetup : LoaderTask, IEnumerable<DirectoryInfo>
{
DirectoryInfo LocalCampaignsFolder;
DirectoryInfo DocumentsCampaignsFolder;
private DirectoryInfo LocalCampaignsFolder;
private DirectoryInfo DocumentsCampaignsFolder;

public CampaignWorkspaceSetup(CampaignLoader loader) : base(loader)
{
}

public IEnumerator<DirectoryInfo> GetEnumerator()
{
yield return LocalCampaignsFolder;
yield return DocumentsCampaignsFolder;
}

public override IEnumerator Run(Task.Status status)
{
status.SetText("Creating directory structure...");
Expand All @@ -29,5 +36,10 @@ public override IEnumerator Run(Task.Status status)

yield break;
}

IEnumerator IEnumerable.GetEnumerator()
{
yield return GetEnumerator();
}
}
}

0 comments on commit a4a9838

Please sign in to comment.