diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java index 5296be3af..ec25f1ed3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java @@ -25,6 +25,7 @@ import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jetbrains.annotations.Unmodifiable; @@ -38,7 +39,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public final class ModManager { @FunctionalInterface private interface ModMetadataReader { - LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException; + LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException; } private static final Map>> READERS; @@ -125,10 +126,10 @@ public final class ModManager { LocalModFile modInfo = null; List exceptions = new ArrayList<>(); - try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(file)) { + try (ZipFileTree tree = CompressingUtils.openZipTree(file)) { for (ModMetadataReader reader : supportedReaders) { try { - modInfo = reader.fromFile(this, file, fs); + modInfo = reader.fromFile(this, file, tree); break; } catch (Exception e) { exceptions.add(e); @@ -138,7 +139,7 @@ public final class ModManager { if (modInfo == null) { for (ModMetadataReader reader : unsupportedReaders) { try { - modInfo = reader.fromFile(this, file, fs); + modInfo = reader.fromFile(this, file, tree); break; } catch (Exception ignored) { } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/FabricModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/FabricModMetadata.java index b69fd8755..dd9b6c4d8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/FabricModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/FabricModMetadata.java @@ -19,16 +19,16 @@ package org.jackhuang.hmcl.mod.modinfo; import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; +import kala.compress.archivers.zip.ZipArchiveEntry; import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import java.io.IOException; import java.lang.reflect.Type; -import java.nio.file.FileSystem; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -59,11 +59,11 @@ public final class FabricModMetadata { this.contact = contact; } - public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException { - Path mcmod = fs.getPath("fabric.mod.json"); - if (Files.notExists(mcmod)) + public static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException { + ZipArchiveEntry mcmod = tree.getEntry("fabric.mod.json"); + if (mcmod == null) throw new IOException("File " + modFile + " is not a Fabric mod."); - FabricModMetadata metadata = JsonUtils.fromNonNullJson(Files.readString(mcmod), FabricModMetadata.class); + FabricModMetadata metadata = JsonUtils.fromNonNullJsonFully(tree.getInputStream(mcmod), FabricModMetadata.class); String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", ")); return new LocalModFile(modManager, modManager.getLocalMod(metadata.id, ModLoaderType.FABRIC), modFile, metadata.name, new LocalModFile.Description(metadata.description), authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeNewModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeNewModMetadata.java index a5c220c82..9a0f815de 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeNewModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeNewModMetadata.java @@ -8,6 +8,7 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.annotations.JsonAdapter; import com.moandjiezana.toml.Toml; +import kala.compress.archivers.zip.ZipArchiveEntry; import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.ModManager; @@ -16,14 +17,13 @@ import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.Validation; import org.jackhuang.hmcl.util.io.CompressingUtils; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; -import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -149,33 +149,33 @@ public final class ForgeNewModMetadata { } } - public static LocalModFile fromForgeFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException { - return fromFile(modManager, modFile, fs, ModLoaderType.FORGE); + public static LocalModFile fromForgeFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException { + return fromFile(modManager, modFile, tree, ModLoaderType.FORGE); } - public static LocalModFile fromNeoForgeFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException { - return fromFile(modManager, modFile, fs, ModLoaderType.NEO_FORGED); + public static LocalModFile fromNeoForgeFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException { + return fromFile(modManager, modFile, tree, ModLoaderType.NEO_FORGED); } - private static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs, ModLoaderType modLoaderType) throws IOException { + private static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree, ModLoaderType modLoaderType) throws IOException { if (modLoaderType != ModLoaderType.FORGE && modLoaderType != ModLoaderType.NEO_FORGED) { throw new IOException("Invalid mod loader: " + modLoaderType); } if (modLoaderType == ModLoaderType.NEO_FORGED) { try { - return fromFile0("META-INF/neoforge.mods.toml", modLoaderType, modManager, modFile, fs); + return fromFile0("META-INF/neoforge.mods.toml", modLoaderType, modManager, modFile, tree); } catch (Exception ignored) { } } try { - return fromFile0("META-INF/mods.toml", modLoaderType, modManager, modFile, fs); + return fromFile0("META-INF/mods.toml", modLoaderType, modManager, modFile, tree); } catch (Exception ignored) { } try { - return fromEmbeddedMod(modManager, modFile, fs, modLoaderType); + return fromEmbeddedMod(modManager, modFile, tree, modLoaderType); } catch (Exception ignored) { } @@ -187,19 +187,19 @@ public final class ForgeNewModMetadata { ModLoaderType modLoaderType, ModManager modManager, Path modFile, - FileSystem fs) throws IOException, JsonParseException { - Path modToml = fs.getPath(tomlPath); - if (Files.notExists(modToml)) + ZipFileTree tree) throws IOException, JsonParseException { + ZipArchiveEntry modToml = tree.getEntry(tomlPath); + if (modToml == null) throw new IOException("File " + modFile + " is not a Forge 1.13+ or NeoForge mod."); - Toml toml = new Toml().read(Files.readString(modToml)); + Toml toml = new Toml().read(tree.readTextEntry(modToml)); ForgeNewModMetadata metadata = toml.to(ForgeNewModMetadata.class); if (metadata == null || metadata.getMods().isEmpty()) throw new IOException("Mod " + modFile + " `mods.toml` is malformed.."); Mod mod = metadata.getMods().get(0); - Path manifestMF = fs.getPath("META-INF/MANIFEST.MF"); + ZipArchiveEntry manifestMF = tree.getEntry("META-INF/MANIFEST.MF"); String jarVersion = ""; - if (Files.exists(manifestMF)) { - try (InputStream is = Files.newInputStream(manifestMF)) { + if (manifestMF != null) { + try (InputStream is = tree.getInputStream(manifestMF)) { Manifest manifest = new Manifest(is); jarVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); } catch (IOException e) { @@ -215,30 +215,30 @@ public final class ForgeNewModMetadata { metadata.getLogoFile()); } - private static LocalModFile fromEmbeddedMod(ModManager modManager, Path modFile, FileSystem fs, ModLoaderType modLoaderType) throws IOException { - Path manifestFile = fs.getPath("META-INF/MANIFEST.MF"); - if (!Files.isRegularFile(manifestFile)) - throw new IOException("Missing MANIFEST.MF in file " + manifestFile); + private static LocalModFile fromEmbeddedMod(ModManager modManager, Path modFile, ZipFileTree tree, ModLoaderType modLoaderType) throws IOException { + ZipArchiveEntry manifestFile = tree.getEntry("META-INF/MANIFEST.MF"); + if (manifestFile == null) + throw new IOException("Missing MANIFEST.MF in file " + modFile); Manifest manifest; - try (InputStream input = Files.newInputStream(manifestFile)) { + try (InputStream input = tree.getInputStream(manifestFile)) { manifest = new Manifest(input); } - List embeddedModFiles = List.of(); + List embeddedModFiles = List.of(); String embeddedDependenciesMod = manifest.getMainAttributes().getValue("Embedded-Dependencies-Mod"); if (embeddedDependenciesMod != null) { - Path embeddedModFile = fs.getPath(embeddedDependenciesMod); - if (!Files.isRegularFile(embeddedModFile)) { - LOG.warning("Missing embedded-dependencies-mod: " + embeddedModFile); + ZipArchiveEntry embeddedModFile = tree.getEntry(embeddedDependenciesMod); + if (embeddedModFile == null) { + LOG.warning("Missing embedded-dependencies-mod: " + embeddedDependenciesMod); throw new IOException(); } embeddedModFiles = List.of(embeddedModFile); } else { - Path jarInJarMetadata = fs.getPath("META-INF/jarjar/metadata.json"); - if (Files.isRegularFile(jarInJarMetadata)) { - JarInJarMetadata metadata = JsonUtils.fromJsonFile(jarInJarMetadata, JarInJarMetadata.class); + ZipArchiveEntry jarInJarMetadata = tree.getEntry("META-INF/jarjar/metadata.json"); + if (jarInJarMetadata != null) { + JarInJarMetadata metadata = JsonUtils.fromJsonFully(tree.getInputStream(jarInJarMetadata), JarInJarMetadata.class); if (metadata == null) throw new IOException("Invalid metadata file: " + jarInJarMetadata); @@ -246,11 +246,11 @@ public final class ForgeNewModMetadata { embeddedModFiles = new ArrayList<>(); for (EmbeddedJarMetadata jar : metadata.jars) { - Path path = fs.getPath(jar.path); - if (Files.isRegularFile(path)) { + ZipArchiveEntry path = tree.getEntry(jar.path); + if (path != null) { embeddedModFiles.add(path); } else { - LOG.warning("Missing embedded-dependencies-mod: " + path); + LOG.warning("Missing embedded-dependencies-mod: " + jar.path); } } } @@ -262,10 +262,10 @@ public final class ForgeNewModMetadata { Path tempFile = Files.createTempFile("hmcl-", ".zip"); try { - for (Path embeddedModFile : embeddedModFiles) { - Files.copy(embeddedModFile, tempFile, StandardCopyOption.REPLACE_EXISTING); - try (FileSystem embeddedFs = CompressingUtils.createReadOnlyZipFileSystem(tempFile)) { - return fromFile(modManager, modFile, embeddedFs, modLoaderType); + for (ZipArchiveEntry embeddedModFile : embeddedModFiles) { + tree.extractTo(embeddedModFile, tempFile); + try (ZipFileTree embeddedTree = CompressingUtils.openZipTree(tempFile)) { + return fromFile(modManager, modFile, embeddedTree, modLoaderType); } catch (Exception ignored) { } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadata.java index 5deee4f9e..53c6b0925 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/ForgeOldModMetadata.java @@ -21,16 +21,16 @@ import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; +import kala.compress.archivers.zip.ZipArchiveEntry; import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -123,14 +123,14 @@ public final class ForgeOldModMetadata { return authors; } - public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException { - Path mcmod = fs.getPath("mcmod.info"); - if (Files.notExists(mcmod)) + public static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException { + ZipArchiveEntry mcmod = tree.getEntry("mcmod.info"); + if (mcmod == null) throw new IOException("File " + modFile + " is not a Forge mod."); List modList; - try (var reader = Files.newBufferedReader(mcmod); + try (var reader = tree.getBufferedReader(mcmod); var jsonReader = new JsonReader(reader)) { JsonToken firstToken = jsonReader.peek(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/LiteModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/LiteModMetadata.java index d29f43dfc..8a59a711b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/LiteModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/LiteModMetadata.java @@ -18,17 +18,16 @@ package org.jackhuang.hmcl.mod.modinfo; import com.google.gson.JsonParseException; +import kala.compress.archivers.zip.ZipArchiveEntry; import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import java.io.IOException; -import java.nio.file.FileSystem; import java.nio.file.Path; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; /** * @@ -109,18 +108,16 @@ public final class LiteModMetadata { public String getUpdateURI() { return updateURI; } - - public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException { - try (ZipFile zipFile = new ZipFile(modFile.toFile())) { - ZipEntry entry = zipFile.getEntry("litemod.json"); - if (entry == null) - throw new IOException("File " + modFile + "is not a LiteLoader mod."); - LiteModMetadata metadata = JsonUtils.fromJsonFully(zipFile.getInputStream(entry), LiteModMetadata.class); - if (metadata == null) - throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); - return new LocalModFile(modManager, modManager.getLocalMod(metadata.getName(), ModLoaderType.LITE_LOADER), modFile, metadata.getName(), new LocalModFile.Description(metadata.getDescription()), metadata.getAuthor(), - metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), ""); - } + + public static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException { + ZipArchiveEntry entry = tree.getEntry("litemod.json"); + if (entry == null) + throw new IOException("File " + modFile + " is not a LiteLoader mod."); + LiteModMetadata metadata = JsonUtils.fromJsonFully(tree.getInputStream(entry), LiteModMetadata.class); + if (metadata == null) + throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); + return new LocalModFile(modManager, modManager.getLocalMod(metadata.getName(), ModLoaderType.LITE_LOADER), modFile, metadata.getName(), new LocalModFile.Description(metadata.getDescription()), metadata.getAuthor(), + metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), ""); } - + } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java index 80b89d259..024a0102e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java @@ -20,6 +20,7 @@ package org.jackhuang.hmcl.mod.modinfo; import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import kala.compress.archivers.zip.ZipArchiveEntry; import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.ModManager; @@ -29,11 +30,10 @@ import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.Validation; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import java.io.IOException; import java.lang.reflect.Type; -import java.nio.file.FileSystem; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -183,11 +183,11 @@ public record PackMcMeta(@SerializedName("pack") PackInfo pack) implements Valid } } - public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException { - Path mcmod = fs.getPath("pack.mcmeta"); - if (Files.notExists(mcmod)) + public static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException { + ZipArchiveEntry mcmod = tree.getEntry("pack.mcmeta"); + if (mcmod == null) throw new IOException("File " + modFile + " is not a resource pack."); - PackMcMeta metadata = JsonUtils.fromNonNullJson(Files.readString(mcmod), PackMcMeta.class); + PackMcMeta metadata = JsonUtils.fromNonNullJsonFully(tree.getInputStream(mcmod), PackMcMeta.class); return new LocalModFile( modManager, modManager.getLocalMod(FileUtils.getNameWithoutExtension(modFile), ModLoaderType.PACK), diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/QuiltModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/QuiltModMetadata.java index d8f2bb4f4..b9e7ebd56 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/QuiltModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/QuiltModMetadata.java @@ -2,15 +2,15 @@ package org.jackhuang.hmcl.mod.modinfo; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import kala.compress.archivers.zip.ZipArchiveEntry; import org.jackhuang.hmcl.mod.LocalModFile; import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.ModManager; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; import java.util.stream.Collectors; @@ -53,13 +53,13 @@ public final class QuiltModMetadata { this.quilt_loader = quiltLoader; } - public static LocalModFile fromFile(ModManager modManager, Path modFile, FileSystem fs) throws IOException, JsonParseException { - Path path = fs.getPath("quilt.mod.json"); - if (Files.notExists(path)) { + public static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException { + ZipArchiveEntry path = tree.getEntry("quilt.mod.json"); + if (path == null) { throw new IOException("File " + modFile + " is not a Quilt mod."); } - QuiltModMetadata root = JsonUtils.fromNonNullJson(Files.readString(path), QuiltModMetadata.class); + QuiltModMetadata root = JsonUtils.fromNonNullJsonFully(tree.getInputStream(path), QuiltModMetadata.class); if (root.schema_version != 1) { throw new IOException("File " + modFile + " is not a supported Quilt mod."); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java index aca03c64e..675f2f9a2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CompressingUtils.java @@ -21,6 +21,7 @@ import kala.compress.archivers.zip.ZipArchiveEntry; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.platform.OperatingSystem; +import org.jackhuang.hmcl.util.tree.ZipFileTree; import java.io.IOException; import java.nio.ByteBuffer; @@ -127,6 +128,10 @@ public final class CompressingUtils { throw new IOException("Cannot find suitable encoding for the zip."); } + public static ZipFileTree openZipTree(Path zipFile) throws IOException { + return new ZipFileTree(openZipFile(zipFile)); + } + public static ZipArchiveReader openZipFile(Path zipFile) throws IOException { ZipArchiveReader zipReader = new ZipArchiveReader(Files.newByteChannel(zipFile)); Charset suitableEncoding; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/tree/ArchiveFileTree.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/tree/ArchiveFileTree.java index 741621c70..239b7358a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/tree/ArchiveFileTree.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/tree/ArchiveFileTree.java @@ -23,10 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -152,6 +149,17 @@ public abstract class ArchiveFileTree implements Clos return getInputStream(entry); } + public BufferedReader getBufferedReader(@NotNull E entry) throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream(entry), StandardCharsets.UTF_8)); + } + + public @NotNull BufferedReader getBufferedReader(String entryPath) throws IOException { + E entry = getEntry(entryPath); + if (entry == null) + throw new FileNotFoundException("Entry not found: " + entryPath); + return getBufferedReader(entry); + } + public byte[] readBinaryEntry(@NotNull E entry) throws IOException { try (InputStream input = getInputStream(entry)) { return input.readAllBytes();