From e6ea5984c76d6bf2972a5202ae7f3dce7394d8bd Mon Sep 17 00:00:00 2001
From: apple502j <33279053+apple502j@users.noreply.github.com>
Date: Sun, 7 Aug 2022 03:02:42 +0900
Subject: [PATCH] ModResourcePackUtil: Properly handle special chars in mod
 name (#2407)

* ModResourcePackUtil: Properly handle special chars in mod name

* Add tests

* Fix NPE in testmod
---
 .../resource/loader/ModResourcePackUtil.java  | 27 ++++++++++-------
 .../loader/BuiltinResourcePackTestMod.java    | 30 ++++++++++++++++++-
 2 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java
index d5866f9c5..ee5b0987b 100644
--- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java
+++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModResourcePackUtil.java
@@ -19,8 +19,11 @@ package net.fabricmc.fabric.impl.resource.loader;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 import com.google.common.base.Charsets;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
 import org.apache.commons.io.IOUtils;
 import org.jetbrains.annotations.Nullable;
 
@@ -40,6 +43,8 @@ import net.fabricmc.loader.api.metadata.ModMetadata;
  * Internal utilities for managing resource packs.
  */
 public final class ModResourcePackUtil {
+	private static final Gson GSON = new Gson();
+
 	private ModResourcePackUtil() {
 	}
 
@@ -71,21 +76,23 @@ public final class ModResourcePackUtil {
 	public static InputStream openDefault(ModMetadata info, ResourceType type, String filename) {
 		switch (filename) {
 		case "pack.mcmeta":
-			String description = info.getName();
-
-			if (description == null) {
-				description = "";
-			} else {
-				description = description.replaceAll("\"", "\\\"");
-			}
-
-			String pack = String.format("{\"pack\":{\"pack_format\":" + type.getPackVersion(SharedConstants.getGameVersion()) + ",\"description\":\"%s\"}}", description);
-			return IOUtils.toInputStream(pack, Charsets.UTF_8);
+			String description = Objects.requireNonNullElse(info.getName(), "");
+			String metadata = serializeMetadata(type.getPackVersion(SharedConstants.getGameVersion()), description);
+			return IOUtils.toInputStream(metadata, Charsets.UTF_8);
 		default:
 			return null;
 		}
 	}
 
+	public static String serializeMetadata(int packVersion, String description) {
+		JsonObject pack = new JsonObject();
+		pack.addProperty("pack_format", packVersion);
+		pack.addProperty("description", description);
+		JsonObject metadata = new JsonObject();
+		metadata.add("pack", pack);
+		return GSON.toJson(metadata);
+	}
+
 	public static String getName(ModMetadata info) {
 		if (info.getName() != null) {
 			return info.getName();
diff --git a/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/BuiltinResourcePackTestMod.java b/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/BuiltinResourcePackTestMod.java
index 40481fc6e..c5e9ccc4b 100644
--- a/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/BuiltinResourcePackTestMod.java
+++ b/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/BuiltinResourcePackTestMod.java
@@ -16,14 +16,18 @@
 
 package net.fabricmc.fabric.test.resource.loader;
 
-import org.slf4j.LoggerFactory;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import net.minecraft.util.Identifier;
 
 import net.fabricmc.api.ModInitializer;
 import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
 import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
+import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
 import net.fabricmc.loader.api.FabricLoader;
 
 public class BuiltinResourcePackTestMod implements ModInitializer {
@@ -31,6 +35,8 @@ public class BuiltinResourcePackTestMod implements ModInitializer {
 
 	private static final Logger LOGGER = LoggerFactory.getLogger(BuiltinResourcePackTestMod.class);
 
+	private static final Gson GSON = new Gson();
+
 	@Override
 	public void onInitialize() {
 		// Should always be present as it's **this** mod.
@@ -42,5 +48,27 @@ public class BuiltinResourcePackTestMod implements ModInitializer {
 				.map(container -> ResourceManagerHelper.registerBuiltinResourcePack(new Identifier(MODID, "test2"),
 						container, ResourcePackActivationType.NORMAL))
 				.filter(success -> !success).ifPresent(success -> LOGGER.warn("Could not register built-in resource pack."));
+
+		// Test various metadata serialization issues (#2407)
+		testMetadataSerialization("");
+		testMetadataSerialization("Quotes: \"\" \"");
+		testMetadataSerialization("Backslash: \\ \\\\");
+	}
+
+	private void testMetadataSerialization(String description) {
+		String metadata = ModResourcePackUtil.serializeMetadata(1, description);
+		JsonObject json;
+
+		try {
+			json = GSON.fromJson(metadata, JsonObject.class);
+		} catch (JsonParseException exc) {
+			throw new AssertionError("Metadata parsing test for description \"%s\" failed".formatted(description), exc);
+		}
+
+		String parsedDescription = json.get("pack").getAsJsonObject().get("description").getAsString();
+
+		if (!description.equals(parsedDescription)) {
+			throw new AssertionError("Metadata parsing test for description failed: expected \"%s\", got \"%s\"".formatted(description, parsedDescription));
+		}
 	}
 }