Compare commits

..

4 Commits

16 changed files with 297 additions and 4 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "libraries/cfcoreapi"]
path = libraries/cfcoreapi
url = https://sys.reslate.net/lambda/git/ydeng/cfcoreapi.git

26
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/CFUtils/bin/Debug/net6.0/CFUtils.dll",
"args": [],
"cwd": "${workspaceFolder}/CFUtils",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"cSpell.words": [
"CFUtils"
]
}

41
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/CFUtils/CFUtils.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/CFUtils/CFUtils.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/CFUtils/CFUtils.csproj"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@ -3,8 +3,12 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<ImplicitUsings>false</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta3.22114.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,55 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.CommandLine;
using System.Threading.Tasks;
namespace CFUtils.Commands
{
internal class Install : Command
{
const string NAME = "install";
const string DESC = "Install the profile described by the manifest.";
public Install(DirectoryInfo workspaceDir) : base(NAME, DESC)
{
Argument<FileInfo> profileFile = new Argument<FileInfo>("profile", "The .zip file describing the profile to install.");
Add(profileFile);
Option<DirectoryInfo?> destination = new Option<DirectoryInfo?>("--destination", "Where to install the profile.");
destination.AddAlias("-d");
Add(destination);
Option<bool> server = new Option<bool>("--server", "Install server files.");
server.AddAlias("-s");
Add(server);
Option<DirectoryInfo?> client = new Option<DirectoryInfo?>("--client", "Install files in local client.");
client.AddAlias("-c");
Add(client);
Option<FileInfo?> fileExclude = new Option<FileInfo?>("--exclude-file", "Excludes files with IDs listed in a given file.");
fileExclude.AddAlias("-E");
Add(fileExclude);
Option<FileInfo?> fileInclude = new Option<FileInfo?>("--include-file", "Includes files with IDs listed in a given file.");
fileInclude.AddAlias("-I");
Add(fileInclude);
Option<IEnumerable<FileInfo>> excludeIds = new Option<IEnumerable<FileInfo>>
("--exclude-id", "Excludes the files with the given IDs.");
excludeIds.AllowMultipleArgumentsPerToken = true;
excludeIds.AddAlias("-e");
Add(excludeIds);
Option<IEnumerable<FileInfo>> includeIds = new Option<IEnumerable<FileInfo>>("--include-id", "Include the files with the given IDs");
includeIds.AllowMultipleArgumentsPerToken = true;
includeIds.AddAlias("-i");
Add(includeIds);
this.SetHandler(async (FileInfo profileFile, DirectoryInfo? dest, bool server, bool client, FileInfo? fileExclude, FileInfo? fileInclude, IEnumerable<FileInfo> excludeIds, IEnumerable<FileInfo> includeIds) =>
{
}, profileFile, destination, client, fileExclude, fileInclude, excludeIds, includeIds);
}
}
}

21
CFUtils/Program.cs Normal file
View File

@ -0,0 +1,21 @@
using System.IO;
using System;
using System.CommandLine;
using System.Threading.Tasks;
using CFUtils.Commands;
namespace CFUtils
{
internal class Program
{
// See: https://docs.microsoft.com/en-us/dotnet/core/tutorials/top-level-templates
static async Task<int> Main(string[] args)
{
DirectoryInfo workspace = new DirectoryInfo(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
Directory.CreateDirectory(workspace.FullName);
RootCommand rootCmd = new RootCommand("CLI utility for creating, editing, and installing Curseforge profiles.");
rootCmd.Add(new Install(workspace));
return await rootCmd.InvokeAsync(args);
}
}
}

View File

@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using CFUtils.Serialization.Modpack;
namespace CFUtils.Serialization
{
internal class ManifestConverter : JsonConverter<Manifest>
{
PropertyInfo[] manifestProperties;
Type manifestType;
public ManifestConverter()
{
manifestType = typeof(Manifest);
manifestProperties = manifestType.GetProperties();
}
public override Manifest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string[] propertyNames = new string[manifestProperties.Length];
for (int i = 0; i < propertyNames.Length; i++) propertyNames[i] = manifestProperties[i].Name;
Manifest result = new Manifest();
HashSet<string> pendingProperties = new HashSet<string>(propertyNames);
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
if (pendingProperties.Count != 0)
{
throw new JsonException("Manifest is missing data. Will not proceed.");
}
return result;
}
if (reader.TokenType == JsonTokenType.PropertyName)
{
string propertyName = reader.GetString() ?? throw new JsonException();
if (propertyNames.Contains(propertyName))
{
PropertyInfo property = manifestProperties[Array.IndexOf(propertyNames, propertyName)];
pendingProperties.Remove(propertyName);
property.SetValue(result, JsonSerializer.Deserialize(ref reader, property.PropertyType, options));
}
else if (Enum.GetNames(typeof(ManifestGames)).Contains(propertyName, StringComparer.CurrentCultureIgnoreCase))
{
Game game = JsonSerializer.Deserialize<Game>(ref reader);
game.gameType = Enum.Parse<ManifestGames>(propertyName, true);
result.game = game;
}
}
else
{
throw new JsonException();
}
}
throw new JsonException(); // Reached end of stream and did not find ending token.
}
public override void Write(Utf8JsonWriter writer, Manifest value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName(value.game.gameType.ToString().ToLower());
JsonSerializer.Serialize(writer, value.game, typeof(Game), options);
for (int i = 0; i < manifestProperties.Length; i++)
{
PropertyInfo property = manifestProperties[i];
writer.WritePropertyName(property.Name);
JsonSerializer.Serialize(writer, property.GetValue(value), property.PropertyType, options);
}
writer.WriteEndObject();
}
}
}

View File

@ -0,0 +1,7 @@
namespace CFUtils.Serialization
{
public enum ManifestGames
{
Minecraft
}
}

View File

@ -0,0 +1,9 @@
namespace CFUtils.Serialization.Modpack
{
public struct File
{
public string ProjectId { get; set; }
public string FileId { get; set; }
public bool Required { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace CFUtils.Serialization.Modpack
{
public struct Game
{
[JsonIgnore]
public ManifestGames gameType;
public string Version { get; set; }
public IList<ModLoader> ModLoaders { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace CFUtils.Serialization.Modpack
{
public struct Manifest
{
[JsonIgnore]
public Game game;
public string ManifestType { get; set; }
public int ManifestVersion { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string Author { get; set; }
public IList<File> Files { get; set; }
public string Overrides { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace CFUtils.Serialization.Modpack
{
public struct ModLoader
{
public string Id { get; set; }
public bool Primary { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace CFUtils.Serialization
{
internal class ProfileReader
{
}
}

View File

@ -1,2 +0,0 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

1
libraries/cfcoreapi Submodule

@ -0,0 +1 @@
Subproject commit 55718048c73f59cc55704734344cd00c27db3c11