Skip to content

Commit 05c8f89

Browse files
committed
Allow loading resource packs from the mods folder.
1 parent db400bf commit 05c8f89

File tree

8 files changed

+173
-5
lines changed

8 files changed

+173
-5
lines changed

loader/src/main/java/com/fox2code/foxloader/client/gui/GuiModMenuContainer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,11 @@ protected void drawSlot(Minecraft mc, int index, float x, float y, int iconHeigh
145145
RenderSystem.disableBlend();
146146
RenderSystem.color(1F, 1F, 1F, 1F);
147147

148-
String name = UpdateManager.getInstance().getUpdateState(modInfo.id).colorPrefix +
149-
modInfo.name + " " + modInfo.version;
148+
String name = UpdateManager.getInstance()
149+
.getUpdateState(modInfo.id).colorPrefix + modInfo.name;
150+
if (!ModInfo.VERSION_NOT_APPLICABLE.equals(modInfo.version)) {
151+
name += " " + modInfo.version;
152+
}
150153
if (modInfo.unofficial) {
151154
name += ChatColors.GRAY + " (Unofficial)";
152155
}

loader/src/main/java/com/fox2code/foxloader/launcher/FoxClassLoader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,8 +554,8 @@ public static boolean isGamePath(String path) {
554554
path.startsWith("com/indigo3d/") ||
555555
path.startsWith("paulscode/sound/") ||
556556
path.startsWith("com/jcraft/") ||
557-
// font.txt is a protected game file
558-
path.equals("font.txt");
557+
// pack.png & font.txt are protected game files
558+
path.equals("pack.png") || path.equals("font.txt");
559559
}
560560

561561
public Collection<FileInfo> loadingClassPath() {

loader/src/main/java/com/fox2code/foxloader/loader/ModInfo.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class ModInfo extends FileInfo implements IMixinConfigSource {
4848
private static final Attributes.Name MOD_WEBSITE = new Attributes.Name("ModWebsite");
4949
private static final Attributes.Name UNOFFICIAL = new Attributes.Name("Unofficial");
5050
private static final Attributes.Name LOAD_ORDER_PRIORITY = new Attributes.Name("LoadOrderPriority");
51+
public static final String VERSION_NOT_APPLICABLE = "N/A";
5152

5253
@NotNull public final String id;
5354
@NotNull public final String name;

loader/src/main/java/com/fox2code/foxloader/loader/ModLoaderInit.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.fox2code.foxloader.loader.early.EarlyLoader;
3232
import com.fox2code.foxloader.loader.java.JavaLoadingPlugin;
3333
import com.fox2code.foxloader.loader.java.JavaModInfo;
34+
import com.fox2code.foxloader.loader.resource.ResourceLoadingPlugin;
3435
import com.fox2code.foxloader.patching.PreLoader;
3536
import com.fox2code.foxloader.patching.mixin.MixinModLoader;
3637
import com.fox2code.foxloader.updater.FoxLoaderUpdater;
@@ -300,7 +301,7 @@ private static Collection<LoadingPlugin> loadModContainersWithLoaders() throws E
300301
LoadingPlugin loadingPlugin = Class.forName(javaModInfo.loadingPlugin)
301302
.asSubclass(LoadingPlugin.class).newInstance();
302303
final String loadingPluginId = loadingPlugin.getPluginId();
303-
if (loaders.containsKey(loadingPluginId)) {
304+
if (loaders.containsKey(loadingPluginId) || "resource".equals(loadingPluginId)) {
304305
throw new RuntimeException("Duplicate loader with id " +
305306
javaModInfo.id + " between \"" +
306307
loaders.get(javaModInfo.id).javaModInfo.file.getName() +
@@ -309,6 +310,7 @@ private static Collection<LoadingPlugin> loadModContainersWithLoaders() throws E
309310
loadingPlugin.javaModInfo = javaModInfo;
310311
loaders.put(loadingPluginId, loadingPlugin);
311312
}
313+
loaders.put("resource", ResourceLoadingPlugin.RESOURCE_LOADING_PLUGIN); // Allow resource pack loading
312314
// Load non-foxloader mods
313315
loaders.remove("java"); // Remove Java plugin when loading mods outside of the java plugin
314316
fileIterator = files.iterator();

loader/src/main/java/com/fox2code/foxloader/loader/java/JavaLoadingPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
import java.util.List;
4646
import java.util.logging.Level;
4747

48+
/**
49+
* Loading plugin to load most FoxLoader mods, including FoxLoader loading plugins.
50+
*/
4851
public final class JavaLoadingPlugin extends LoadingPlugin {
4952
public static final JavaLoadingPlugin JAVA_LOADING_PLUGIN = new JavaLoadingPlugin();
5053
private static final boolean DISABLE_SPARK = Boolean.getBoolean("foxloader.disable-spark");
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023-2025 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.fox2code.foxloader.loader.resource;
25+
26+
import com.fox2code.foxloader.launcher.FoxClassLoader;
27+
import com.fox2code.foxloader.launcher.FoxLauncher;
28+
import com.fox2code.foxloader.loader.LoadingPlugin;
29+
import com.fox2code.foxloader.loader.Mod;
30+
import com.fox2code.foxloader.loader.ModContainer;
31+
import com.fox2code.foxloader.loader.ModInfo;
32+
import com.fox2code.foxloader.utils.io.IOUtils;
33+
import com.fox2code.foxloader.utils.io.URLUtils;
34+
import org.jetbrains.annotations.NotNull;
35+
import org.jetbrains.annotations.Nullable;
36+
37+
import java.io.File;
38+
import java.io.IOException;
39+
import java.math.BigInteger;
40+
import java.net.URL;
41+
import java.nio.charset.StandardCharsets;
42+
import java.util.Enumeration;
43+
import java.util.zip.ZipEntry;
44+
import java.util.zip.ZipFile;
45+
46+
/**
47+
* Loading plugin to load resource packs from the mods folder.
48+
*/
49+
public final class ResourceLoadingPlugin extends LoadingPlugin {
50+
public static final ResourceLoadingPlugin RESOURCE_LOADING_PLUGIN = new ResourceLoadingPlugin();
51+
52+
private ResourceLoadingPlugin() {
53+
super("resource");
54+
}
55+
56+
@Override
57+
public @Nullable ModInfo getModInfo(@NotNull File mod, @Nullable String jarPath) throws Exception {
58+
if (jarPath != null || !mod.getName().endsWith(".zip")) {
59+
return null;
60+
}
61+
String description;
62+
URL packIcon = null;
63+
String sha256;
64+
try (ZipFile zipFile = new ZipFile(mod)) {
65+
Enumeration<? extends ZipEntry> zipEntryEnumeration = zipFile.entries();
66+
while (zipEntryEnumeration.hasMoreElements()) {
67+
String entryName = zipEntryEnumeration.nextElement().getName();
68+
if (entryName.startsWith("META-INF/") || entryName.endsWith(".mixins.json") ||
69+
entryName.endsWith(".class") || entryName.endsWith(".class/")) {
70+
return null; // Skip potential java mods.
71+
}
72+
}
73+
ZipEntry descriptionEntry;
74+
if ((descriptionEntry = zipFile.getEntry("pack.txt")) == null) {
75+
return null;
76+
}
77+
description = new String(IOUtils.readAllBytes(
78+
zipFile.getInputStream(descriptionEntry)),
79+
StandardCharsets.UTF_8);
80+
if (zipFile.getEntry("pack.png") != null) {
81+
packIcon = URLUtils.getEntryURLOf(mod, "pack.png");
82+
}
83+
sha256 = new BigInteger(1, IOUtils.sha256Of(mod)).toString(16);
84+
} catch (IOException ioe) {
85+
return null;
86+
}
87+
final String packId = "resource_" + sha256.substring(0, 16);
88+
final String iconPath = "assets/" + packId + "/icon.png";
89+
if (packIcon != null) {
90+
// Inject resource pack icon as a resource
91+
FoxClassLoader foxClassLoader = FoxLauncher.getFoxClassLoader();
92+
foxClassLoader.injectResource(iconPath, packIcon);
93+
if (foxClassLoader.getResource(iconPath) == null) {
94+
packIcon = null;
95+
}
96+
}
97+
return new ResourcePackInfo(mod, packId, mod.getName(),
98+
description, packIcon == null ? null : iconPath);
99+
}
100+
101+
@Override
102+
public void preLoadModContainer(@NotNull ModContainer modContainer) throws Exception {
103+
104+
}
105+
106+
@Override
107+
public @Nullable Mod loadModContainer(@NotNull ModContainer modContainer) throws Exception {
108+
return null;
109+
}
110+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023-2025 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.fox2code.foxloader.loader.resource;
25+
26+
import com.fox2code.foxloader.loader.ModInfo;
27+
import org.jetbrains.annotations.NotNull;
28+
import org.jetbrains.annotations.Nullable;
29+
30+
import java.io.File;
31+
import java.io.IOException;
32+
33+
public final class ResourcePackInfo extends ModInfo {
34+
ResourcePackInfo(
35+
@NotNull File file, @NotNull String id, @Nullable String name,
36+
@Nullable String description, @Nullable String iconPath) throws IOException {
37+
super(file, null, id, name, VERSION_NOT_APPLICABLE, description, null, iconPath, "client", null, false, 0);
38+
}
39+
40+
@Override
41+
public boolean isJavaArchive() {
42+
return true; // Force archive to be loaded into the java class loader.
43+
}
44+
}

patching/src/main/java/com/fox2code/foxloader/utils/io/URLUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import org.jetbrains.annotations.Nullable;
2727

28+
import java.io.File;
2829
import java.net.MalformedURLException;
2930
import java.net.URISyntaxException;
3031
import java.net.URL;
@@ -53,4 +54,8 @@ public static boolean isValidHttpURL(@Nullable String url) {
5354
return false;
5455
}
5556
}
57+
58+
public static URL getEntryURLOf(File file, String entryPath) throws MalformedURLException {
59+
return new URL("jar:" + file.toURI().toURL().toExternalForm() + "!/" + entryPath);
60+
}
5661
}

0 commit comments

Comments
 (0)