103 lines
5.3 KiB
FSharp
103 lines
5.3 KiB
FSharp
open System
|
|
open System.IO
|
|
open System.IO.Compression
|
|
open System.Security.Cryptography
|
|
open System.Text
|
|
open System.Text.Json
|
|
open System.Text.Json.Nodes
|
|
|
|
let args = fsi.CommandLineArgs |> Array.skip 1
|
|
if args.Length <> 3 then failwith "Usage: pack.fsx SOURCE.mrpack CLIENT_DIR OUTPUT_DIR"
|
|
let source, client, output = args[0], args[1], args[2]
|
|
let digest algorithm path =
|
|
use stream = File.OpenRead path
|
|
(match algorithm with
|
|
| "sha512" -> SHA512.HashData stream
|
|
| "sha1" -> SHA1.HashData stream
|
|
| _ -> invalidArg "algorithm" algorithm)
|
|
|> Convert.ToHexString
|
|
|> _.ToLowerInvariant()
|
|
let bytes (entry: ZipArchiveEntry) =
|
|
use stream = entry.Open()
|
|
use memory = new MemoryStream()
|
|
stream.CopyTo memory
|
|
memory.ToArray()
|
|
let text value = Encoding.UTF8.GetBytes(value: string)
|
|
let json value = JsonValue.Create(value: string) :> JsonNode
|
|
let shaderMods =
|
|
set [ "iris-neoforge-1.8.12+mc1.21.1.jar"
|
|
"monocle-0.2.3.ms.jar"
|
|
"colorwheel-neoforge-1.3.1-mc1.21.1.jar" ]
|
|
let shaderConfig =
|
|
set [ "overrides/config/iris.properties"
|
|
"overrides/config/iris-excluded.json"
|
|
"overrides/config/monocle.properties"
|
|
"overrides/config/colorwheel-client.toml" ]
|
|
let restores =
|
|
[ "ftb-essentials-neoforge-2101.1.10.jar", "https://edge.forgecdn.net/files/8442/866/ftb-essentials-neoforge-2101.1.10.jar"
|
|
"AdvancedBackups-neoforge-1.21-3.7.1.jar", "https://cdn.modrinth.com/data/Jrmoreqs/versions/ufIaRDFo/AdvancedBackups-neoforge-1.21-3.7.1.jar" ]
|
|
let patcher = "EuphoriaPatcher-1.10.5-r5.9.3-neoforge.jar"
|
|
let shader = "ComplementaryReimagined_r5.9.3.zip"
|
|
let patched = "ComplementaryReimagined_r5.9.3 + EuphoriaPatches_1.10.5"
|
|
let file path url =
|
|
let local = Path.Combine(client, path)
|
|
let hashes = JsonObject()
|
|
hashes["sha512"] <- json (digest "sha512" local)
|
|
hashes["sha1"] <- json (digest "sha1" local)
|
|
let env = JsonObject()
|
|
env["client"] <- json "required"
|
|
env["server"] <- json "unsupported"
|
|
let item = JsonObject()
|
|
item["path"] <- json path
|
|
item["hashes"] <- hashes
|
|
item["env"] <- env
|
|
item["downloads"] <- JsonArray(json url)
|
|
item["fileSize"] <- JsonValue.Create(FileInfo(local).Length)
|
|
item :> JsonNode
|
|
|
|
let build shaders =
|
|
use original = ZipFile.OpenRead source
|
|
let index = original.GetEntry("modrinth.index.json") |> bytes |> Encoding.UTF8.GetString |> JsonNode.Parse
|
|
let files = index["files"].AsArray()
|
|
for item in files do
|
|
let path = item["path"].GetValue<string>()
|
|
let hashes = item["hashes"]
|
|
if digest "sha512" (Path.Combine(client, path)) <> hashes["sha512"].GetValue<string>() then
|
|
failwithf "Source mod differs: %s" path
|
|
let replaced = restores |> List.map (fun (name, _) -> "mods/" + name) |> Set.ofList |> Set.add ("mods/" + patcher)
|
|
let selected = JsonArray()
|
|
for item in files do
|
|
let path = item["path"].GetValue<string>()
|
|
if not (replaced.Contains path) && (shaders || not (shaderMods.Contains(Path.GetFileName path))) then
|
|
selected.Add(item.DeepClone())
|
|
for name, url in restores do selected.Add(file ("mods/" + name) url)
|
|
if shaders then
|
|
selected.Add(file ("mods/" + patcher) "https://cdn.modrinth.com/data/4H6sumDB/versions/QMsRXtSJ/EuphoriaPatcher-1.10.5-r5.9.3-neoforge.jar")
|
|
selected.Add(file ("shaderpacks/" + shader) "https://cdn.modrinth.com/data/HVnmMxH1/versions/Bqen1mJX/ComplementaryReimagined_r5.9.3.zip")
|
|
index["files"] <- selected
|
|
index["name"] <- json (if shaders then "Enigmatica 10 - Custom (Shaders)" else "Enigmatica 10 - Custom (Minimal)")
|
|
index["versionId"] <- json (if shaders then "1.33.0-shaders.2" else "1.33.0-minimal.2")
|
|
index["summary"] <- json (if shaders then "Multiplayer pack with Complementary Reimagined and Euphoria Patches." else "Multiplayer pack without the shader renderer or administrative tools.")
|
|
let entries = System.Collections.Generic.SortedDictionary<string, byte[]>(StringComparer.Ordinal)
|
|
for entry in original.Entries do
|
|
if [".jar"; ".zip"; ".mrpack"; ".glsl"; ".fsh"; ".vsh"; ".csh"] |> List.exists (fun suffix -> entry.FullName.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) then
|
|
failwithf "Embedded payload must be a manifest download: %s" entry.FullName
|
|
if entry.FullName <> "modrinth.index.json" && not (shaderConfig.Contains entry.FullName) then
|
|
entries.Add(entry.FullName, bytes entry)
|
|
entries.Add("modrinth.index.json", text (index.ToJsonString(JsonSerializerOptions(WriteIndented = true))))
|
|
if shaders then
|
|
entries["overrides/config/iris.properties"] <- text ("shaderPack=" + patched + "\n")
|
|
let name = if shaders then "e10c-shaders.mrpack" else "e10c.mrpack"
|
|
let path = Path.Combine(output, name)
|
|
use destination = File.Open(path, FileMode.CreateNew)
|
|
use archive = new ZipArchive(destination, ZipArchiveMode.Create)
|
|
for KeyValue(name, data) in entries do
|
|
let entry = archive.CreateEntry(name, CompressionLevel.Optimal)
|
|
entry.LastWriteTime <- DateTimeOffset(1980, 1, 1, 0, 0, 0, TimeSpan.Zero)
|
|
use stream = entry.Open()
|
|
stream.Write data
|
|
printfn "%s files=%d mods=%d" path selected.Count (selected |> Seq.filter (fun item -> item["path"].GetValue<string>().StartsWith("mods/")) |> Seq.length)
|
|
|
|
Directory.CreateDirectory output |> ignore
|
|
build false
|
|
build true
|