Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static CompletableFuture<Void> exportLogs(Path zipFile, DefaultGameReposi
}

return CompletableFuture.runAsync(() -> {
try (Zipper zipper = new Zipper(zipFile)) {
try (Zipper zipper = new Zipper(zipFile, true)) {
processLogs(runDirectory.resolve("liteconfig"), "*.log", "liteconfig", zipper);
processLogs(runDirectory.resolve("logs"), "*.log", "logs", zipper);
processLogs(runDirectory, "*.log", "runDirectory", zipper);
Expand Down
47 changes: 31 additions & 16 deletions HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
Expand All @@ -39,13 +41,15 @@ public final class Zipper implements Closeable {

private final ZipOutputStream zos;
private final byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
private final Set<String> entryNames;

public Zipper(Path zipFile) throws IOException {
this(zipFile, StandardCharsets.UTF_8);
this(zipFile, false);
}

public Zipper(Path zipFile, Charset encoding) throws IOException {
this.zos = new ZipOutputStream(Files.newOutputStream(zipFile), encoding);
public Zipper(Path zipFile, boolean allowDuplicateEntry) throws IOException {
this.zos = new ZipOutputStream(Files.newOutputStream(zipFile), StandardCharsets.UTF_8);
this.entryNames = allowDuplicateEntry ? new HashSet<>() : null;
}

private static String normalize(String path) {
Expand All @@ -57,6 +61,20 @@ private static String normalize(String path) {
return path;
}

private ZipEntry newEntry(String name) throws IOException {
if (entryNames == null || entryNames.add(name))
return new ZipEntry(name);

for (int i = 1; i < 10; i++) {
String newName = name + "." + i;
if (entryNames.add(newName)) {
return new ZipEntry(newName);
}
}

throw new ZipException("duplicate entry: " + name);
}

private static String resolve(String dir, String file) {
if (dir.isEmpty()) return file;
if (file.isEmpty()) return dir;
Expand All @@ -71,7 +89,7 @@ public void close() throws IOException {
/**
* Compress all the files in sourceDir
*
* @param source the file in basePath to be compressed
* @param source the file in basePath to be compressed
* @param targetDir the path of the directory in this zip file.
*/
public void putDirectory(Path source, String targetDir) throws IOException {
Expand All @@ -81,9 +99,9 @@ public void putDirectory(Path source, String targetDir) throws IOException {
/**
* Compress all the files in sourceDir
*
* @param source the file in basePath to be compressed
* @param source the file in basePath to be compressed
* @param targetDir the path of the directory in this zip file.
* @param filter returns false if you do not want that file or directory
* @param filter returns false if you do not want that file or directory
*/
public void putDirectory(Path source, String targetDir, ExceptionalPredicate<String, IOException> filter) throws IOException {
String root = normalize(targetDir);
Expand All @@ -108,7 +126,7 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th
return FileVisitResult.SKIP_SUBTREE;
}
try {
zos.putNextEntry(new ZipEntry(resolve(root, relativePath) + "/"));
zos.putNextEntry(newEntry(resolve(root, relativePath) + "/"));
zos.closeEntry();
} catch (ZipException ignored) {
// Directory already exists
Expand All @@ -118,23 +136,20 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th
});
}

public void putFile(File file, String path) throws IOException {
putFile(file.toPath(), path);
}

public void putFile(Path file, String path) throws IOException {
path = normalize(path);

BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);

ZipEntry entry = new ZipEntry(attrs.isDirectory() ? path + "/" : path);
ZipEntry entry = newEntry(attrs.isDirectory() ? path + "/" : path);
entry.setCreationTime(attrs.creationTime());
entry.setLastAccessTime(attrs.lastAccessTime());
entry.setLastModifiedTime(attrs.lastModifiedTime());

if (attrs.isDirectory()) {
try {
zos.putNextEntry(entry);
entryNames.add(entry.getName());
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential NullPointerException when allowDuplicateEntry is false. The code attempts to call entryNames.add() but entryNames is null when allowDuplicateEntry is false. This line should be guarded with a null check or only execute when entryNames is not null.

Suggested change
entryNames.add(entry.getName());
if (entryNames != null) {
entryNames.add(entry.getName());
}

Copilot uses AI. Check for mistakes.
zos.closeEntry();
} catch (ZipException ignored) {
// Directory already exists
Expand All @@ -149,13 +164,13 @@ public void putFile(Path file, String path) throws IOException {
}

public void putStream(InputStream in, String path) throws IOException {
zos.putNextEntry(new ZipEntry(normalize(path)));
zos.putNextEntry(newEntry(normalize(path)));
IOUtils.copyTo(in, zos, buffer);
zos.closeEntry();
}

public OutputStream putStream(String path) throws IOException {
zos.putNextEntry(new ZipEntry(normalize(path)));
zos.putNextEntry(newEntry(normalize(path)));
return new OutputStream() {
public void write(int b) throws IOException {
zos.write(b);
Expand All @@ -180,7 +195,7 @@ public void close() throws IOException {
}

public void putLines(Stream<String> lines, String path) throws IOException {
zos.putNextEntry(new ZipEntry(normalize(path)));
zos.putNextEntry(newEntry(normalize(path)));

try {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos));
Expand All @@ -205,7 +220,7 @@ public void putTextFile(String text, String path) throws IOException {
}

public void putTextFile(String text, Charset encoding, String path) throws IOException {
zos.putNextEntry(new ZipEntry(normalize(path)));
zos.putNextEntry(newEntry(normalize(path)));
zos.write(text.getBytes(encoding));
zos.closeEntry();
}
Expand Down