fix compressingutils

This commit is contained in:
Tungstend 2022-10-20 19:07:27 +08:00
parent 92b1df630e
commit 60ab40cc57
27 changed files with 561 additions and 18 deletions

View File

@ -27,6 +27,7 @@ android {
}
dependencies {
implementation 'com.github.marschall:zipfilesystem-standalone:1.0.1'
implementation 'org.nanohttpd:nanohttpd:2.3.1'
implementation 'com.github.steveice10:opennbt:1.4'
implementation 'org.apache.commons:commons-lang3:3.12.0'

View File

@ -0,0 +1,4 @@
package com.tungsten.fclcore.constant;
public class RequestCodes {
}

View File

@ -13,6 +13,7 @@ import com.tungsten.fclcore.game.Version;
import com.tungsten.fclcore.task.FileDownloadTask;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import com.tungsten.fclcore.util.versioning.VersionNumber;
@ -25,7 +26,6 @@ import java.util.Collections;
import java.util.Map;
import java.util.Optional;
// Todo : fix
public final class ForgeInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;

View File

@ -4,6 +4,7 @@ import com.tungsten.fclcore.download.DefaultDependencyManager;
import com.tungsten.fclcore.download.LibraryAnalyzer;
import com.tungsten.fclcore.game.Version;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.versioning.VersionNumber;
import java.io.File;
@ -18,7 +19,6 @@ import java.util.List;
/**
* Remove class digital verification file in game jar
*/
// Todo : fix
public final class GameVerificationFixTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;

View File

@ -15,6 +15,7 @@ import com.tungsten.fclcore.game.LibraryDownloadInfo;
import com.tungsten.fclcore.game.Version;
import com.tungsten.fclcore.task.FileDownloadTask;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import com.tungsten.fclcore.util.platform.CommandBuilder;
import com.tungsten.fclcore.util.versioning.VersionNumber;

View File

@ -5,6 +5,7 @@ import static com.tungsten.fclcore.util.Logging.LOG;
import com.google.gson.JsonParseException;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import org.jenkinsci.constant_pool_scanner.ConstantPool;
@ -24,7 +25,6 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
// Todo : fix
public final class GameVersion {
private GameVersion() {
}

View File

@ -1,6 +1,15 @@
package com.tungsten.fclcore.game;
import com.github.steveice10.opennbt.NBTIO;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.LongTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import com.tungsten.fclcore.util.io.Unzipper;
import com.tungsten.fclcore.util.io.Zipper;
import java.io.IOException;
import java.io.InputStream;
@ -13,7 +22,6 @@ import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
// Todo : fix
public class World {
private final Path file;

View File

@ -3,6 +3,7 @@ package com.tungsten.fclcore.mod;
import com.google.gson.JsonParseException;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.IOException;

View File

@ -3,6 +3,7 @@ package com.tungsten.fclcore.mod;
import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.IOException;
@ -15,7 +16,6 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
// Todo : fix
public final class FabricModMetadata {
private final String id;
private final String name;

View File

@ -4,6 +4,7 @@ import static com.tungsten.fclcore.util.Logging.LOG;
import com.google.gson.JsonParseException;
import com.moandjiezana.toml.Toml;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.IOException;
@ -16,7 +17,6 @@ import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.logging.Level;
// Todo : fix
public final class ForgeNewModMetadata {
private final String modLoader;

View File

@ -5,6 +5,7 @@ import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import com.tungsten.fclcore.util.StringUtils;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.IOException;
@ -13,7 +14,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
// Todo : fix
public final class ForgeOldModMetadata {
@SerializedName("modid")

View File

@ -5,6 +5,7 @@ import static com.tungsten.fclcore.util.Hex.encodeHex;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.File;
@ -16,7 +17,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
// Todo : fix
public final class MinecraftInstanceTask<T> extends Task<ModpackConfiguration<T>> {
private final File zipFile;

View File

@ -2,6 +2,7 @@ package com.tungsten.fclcore.mod;
import com.tungsten.fclcore.game.GameRepository;
import com.tungsten.fclcore.util.StringUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import com.tungsten.fclcore.util.versioning.VersionNumber;
@ -12,7 +13,6 @@ import java.util.HashMap;
import java.util.Objects;
import java.util.TreeSet;
// Todo : fix
public final class ModManager {
private final GameRepository repository;
private final String id;

View File

@ -5,6 +5,7 @@ import static com.tungsten.fclcore.util.Hex.encodeHex;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.io.FileUtils;
import com.tungsten.fclcore.util.io.Unzipper;
import java.io.File;
import java.io.IOException;
@ -13,7 +14,6 @@ import java.nio.file.Files;
import java.util.*;
import java.util.function.Predicate;
// Todo : fix
public class ModpackInstallTask<T> extends Task<Void> {
private final File modpackFile;

View File

@ -5,6 +5,7 @@ import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.gson.Validation;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.IOException;
@ -16,7 +17,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Todo : fix
public class PackMcMeta implements Validation {
@SerializedName("pack")

View File

@ -8,6 +8,7 @@ import com.tungsten.fclcore.mod.ModpackProvider;
import com.tungsten.fclcore.mod.ModpackUpdateTask;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.IOUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
@ -18,7 +19,6 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
// Todo : fix
public final class CurseModpackProvider implements ModpackProvider {
public static final CurseModpackProvider INSTANCE = new CurseModpackProvider();

View File

@ -15,10 +15,13 @@ import com.tungsten.fclcore.mod.ModAdviser;
import com.tungsten.fclcore.mod.Modpack;
import com.tungsten.fclcore.mod.ModpackExportInfo;
import com.tungsten.fclcore.mod.curse.CurseManifest;
import com.tungsten.fclcore.mod.curse.CurseManifestMinecraft;
import com.tungsten.fclcore.mod.curse.CurseManifestModLoader;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.StringUtils;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.Zipper;
import java.io.File;
import java.io.IOException;
@ -28,7 +31,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Todo : fix
public class McbbsModpackExportTask extends Task<Void> {
private final DefaultGameRepository repository;
private final String version;

View File

@ -99,7 +99,6 @@ public class McbbsModpackLocalInstallTask extends Task<Void> {
dependencies.add(repository.saveAsync(version.addPatch(patch)));
} else {
// This mcbbs modpack was installed by other launchers.
// TODO: maintain libraries.
}
dependencies.add(new McbbsModpackCompletionTask(dependencyManager, name, instanceTask.getResult()));

View File

@ -8,6 +8,7 @@ import com.tungsten.fclcore.mod.ModpackProvider;
import com.tungsten.fclcore.mod.ModpackUpdateTask;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import org.apache.commons.compress.archivers.zip.ZipFile;
@ -16,7 +17,6 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
// Todo : fix
public final class ModrinthModpackProvider implements ModpackProvider {
public static final ModrinthModpackProvider INSTANCE = new ModrinthModpackProvider();

View File

@ -7,10 +7,12 @@ import static com.tungsten.fclcore.download.LibraryAnalyzer.LibraryType.LITELOAD
import com.tungsten.fclcore.download.LibraryAnalyzer;
import com.tungsten.fclcore.game.DefaultGameRepository;
import com.tungsten.fclcore.mod.ModAdviser;
import com.tungsten.fclcore.mod.Modpack;
import com.tungsten.fclcore.mod.ModpackExportInfo;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.Zipper;
import java.io.File;
import java.io.IOException;
@ -21,7 +23,6 @@ import java.util.List;
/**
* Export the game to a mod pack file.
*/
// Todo : fix
public class MultiMCModpackExportTask extends Task<Void> {
private final DefaultGameRepository repository;
private final String versionId;

View File

@ -13,6 +13,7 @@ import com.tungsten.fclcore.mod.ModpackConfiguration;
import com.tungsten.fclcore.mod.ModpackInstallTask;
import com.tungsten.fclcore.task.Task;
import com.tungsten.fclcore.util.gson.JsonUtils;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.File;
@ -26,7 +27,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
// Todo : fix
public final class MultiMCModpackInstallTask extends Task<Void> {
private final File zipFile;

View File

@ -19,12 +19,12 @@ import static java.util.Objects.requireNonNull;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.io.ChecksumMismatchException;
import com.tungsten.fclcore.util.io.CompressingUtils;
import com.tungsten.fclcore.util.io.FileUtils;
/**
* A task that can download a file online.
*/
// Todo : fix
public class FileDownloadTask extends FetchTask<Void> {
public static class IntegrityCheck {

View File

@ -0,0 +1,260 @@
package com.tungsten.fclcore.util.io;
import com.github.marschall.com.sun.nio.zipfs.ZipFileSystemProvider;
import com.tungsten.fclcore.util.platform.OperatingSystem;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import java.io.File;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.*;
import java.nio.file.*;
import java.nio.file.spi.FileSystemProvider;
import java.util.*;
import java.util.zip.ZipError;
import java.util.zip.ZipException;
/**
* Utilities of compressing
*/
public final class CompressingUtils {
private static final FileSystemProvider ZIPFS_PROVIDER = new ZipFileSystemProvider();
private CompressingUtils() {
}
private static CharsetDecoder newCharsetDecoder(Charset charset) {
return charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT);
}
public static boolean testEncoding(Path zipFile, Charset encoding) throws IOException {
try (ZipFile zf = openZipFile(zipFile, encoding)) {
return testEncoding(zf, encoding);
}
}
public static boolean testEncoding(ZipFile zipFile, Charset encoding) throws IOException {
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
CharsetDecoder cd = newCharsetDecoder(encoding);
CharBuffer cb = CharBuffer.allocate(32);
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
if (entry.getGeneralPurposeBit().usesUTF8ForNames()) continue;
cd.reset();
byte[] ba = entry.getRawName();
int clen = (int)(ba.length * cd.maxCharsPerByte());
if (clen == 0) continue;
if (clen <= cb.capacity())
((Buffer) cb).clear(); // cast to prevent "java.lang.NoSuchMethodError: java.nio.CharBuffer.clear()Ljava/nio/CharBuffer;" when compiling with Java 9+
else
cb = CharBuffer.allocate(clen);
ByteBuffer bb = ByteBuffer.wrap(ba, 0, ba.length);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow()) return false;
cr = cd.flush(cb);
if (!cr.isUnderflow()) return false;
}
return true;
}
public static Charset findSuitableEncoding(Path zipFile) throws IOException {
return findSuitableEncoding(zipFile, Charset.availableCharsets().values());
}
public static Charset findSuitableEncoding(Path zipFile, Collection<Charset> candidates) throws IOException {
try (ZipFile zf = openZipFile(zipFile, StandardCharsets.UTF_8)) {
return findSuitableEncoding(zf, candidates);
}
}
public static Charset findSuitableEncoding(ZipFile zipFile) throws IOException {
return findSuitableEncoding(zipFile, Charset.availableCharsets().values());
}
public static Charset findSuitableEncoding(ZipFile zipFile, Collection<Charset> candidates) throws IOException {
if (testEncoding(zipFile, StandardCharsets.UTF_8)) return StandardCharsets.UTF_8;
if (OperatingSystem.NATIVE_CHARSET != StandardCharsets.UTF_8 && testEncoding(zipFile, OperatingSystem.NATIVE_CHARSET))
return OperatingSystem.NATIVE_CHARSET;
for (Charset charset : candidates)
if (charset != null && testEncoding(zipFile, charset))
return charset;
throw new IOException("Cannot find suitable encoding for the zip.");
}
public static ZipFile openZipFile(Path zipFile) throws IOException {
return new ZipFile(Files.newByteChannel(zipFile));
}
public static ZipFile openZipFile(Path zipFile, Charset charset) throws IOException {
return new ZipFile(Files.newByteChannel(zipFile), charset.name());
}
public static final class Builder {
private boolean autoDetectEncoding = false;
private Collection<Charset> charsetCandidates;
private Charset encoding = StandardCharsets.UTF_8;
private boolean useTempFile = false;
private final boolean create;
private final Path zip;
public Builder(Path zip, boolean create) {
this.zip = zip;
this.create = create;
}
public Builder setAutoDetectEncoding(boolean autoDetectEncoding) {
this.autoDetectEncoding = autoDetectEncoding;
return this;
}
public Builder setCharsetCandidates(Collection<Charset> charsetCandidates) {
this.charsetCandidates = charsetCandidates;
return this;
}
public Builder setEncoding(Charset encoding) {
this.encoding = encoding;
return this;
}
public Builder setUseTempFile(boolean useTempFile) {
this.useTempFile = useTempFile;
return this;
}
public FileSystem build() throws IOException {
if (autoDetectEncoding) {
if (!testEncoding(zip, encoding)) {
if (charsetCandidates == null)
charsetCandidates = Charset.availableCharsets().values();
encoding = findSuitableEncoding(zip, charsetCandidates);
}
}
return createZipFileSystem(zip, create, useTempFile, encoding);
}
}
public static Builder readonly(Path zipFile) {
return new Builder(zipFile, false);
}
public static Builder writable(Path zipFile) {
return new Builder(zipFile, true).setUseTempFile(true);
}
public static FileSystem createReadOnlyZipFileSystem(Path zipFile) throws IOException {
return createReadOnlyZipFileSystem(zipFile, null);
}
public static FileSystem createReadOnlyZipFileSystem(Path zipFile, Charset charset) throws IOException {
return createZipFileSystem(zipFile, false, false, charset);
}
public static FileSystem createWritableZipFileSystem(Path zipFile) throws IOException {
return createWritableZipFileSystem(zipFile, null);
}
public static FileSystem createWritableZipFileSystem(Path zipFile, Charset charset) throws IOException {
return createZipFileSystem(zipFile, true, true, charset);
}
public static FileSystem createZipFileSystem(Path zipFile, boolean create, boolean useTempFile, Charset encoding) throws IOException {
Map<String, Object> env = new HashMap<>();
if (create)
env.put("create", "true");
if (encoding != null)
env.put("encoding", encoding.name());
if (useTempFile)
env.put("useTempFile", true);
try {
return ZIPFS_PROVIDER.newFileSystem(zipFile, env);
} catch (ZipError error) {
// Since Java 8 throws ZipError stupidly
throw new ZipException(error.getMessage());
} catch (UnsupportedOperationException ex) {
throw new ZipException("Not a zip file");
} catch (FileSystemNotFoundException ex) {
throw new ZipException("Java Environment is broken");
}
}
/**
* Read the text content of a file in zip.
*
* @param zipFile the zip file
* @param name the location of the text in zip file, something like A/B/C/D.txt
* @throws IOException if the file is not a valid zip file.
* @return the plain text content of given file.
*/
public static String readTextZipEntry(File zipFile, String name) throws IOException {
try (ZipFile s = new ZipFile(zipFile)) {
return readTextZipEntry(s, name);
}
}
/**
* Read the text content of a file in zip.
*
* @param zipFile the zip file
* @param name the location of the text in zip file, something like A/B/C/D.txt
* @throws IOException if the file is not a valid zip file.
* @return the plain text content of given file.
*/
public static String readTextZipEntry(ZipFile zipFile, String name) throws IOException {
return IOUtils.readFullyAsString(zipFile.getInputStream(zipFile.getEntry(name)), StandardCharsets.UTF_8);
}
/**
* Read the text content of a file in zip.
*
* @param zipFile the zip file
* @param name the location of the text in zip file, something like A/B/C/D.txt
* @throws IOException if the file is not a valid zip file.
* @return the plain text content of given file.
*/
public static String readTextZipEntry(Path zipFile, String name, Charset encoding) throws IOException {
try (ZipFile s = openZipFile(zipFile, encoding)) {
return IOUtils.readFullyAsString(s.getInputStream(s.getEntry(name)), StandardCharsets.UTF_8);
}
}
/**
* Read the text content of a file in zip.
*
* @param file the zip file
* @param name the location of the text in zip file, something like A/B/C/D.txt
* @return the plain text content of given file.
*/
public static Optional<String> readTextZipEntryQuietly(File file, String name) {
try {
return Optional.of(readTextZipEntry(file, name));
} catch (IOException | NullPointerException e) {
return Optional.empty();
}
}
/**
* Read the text content of a file in zip.
*
* @param file the zip file
* @param name the location of the text in zip file, something like A/B/C/D.txt
* @return the plain text content of given file.
*/
public static Optional<String> readTextZipEntryQuietly(Path file, String name, Charset encoding) {
try {
return Optional.of(readTextZipEntry(file, name, encoding));
} catch (IOException | NullPointerException e) {
return Optional.empty();
}
}
}

View File

@ -0,0 +1,129 @@
package com.tungsten.fclcore.util.io;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
public class Unzipper {
private final Path zipFile, dest;
private boolean replaceExistentFile = false;
private boolean terminateIfSubDirectoryNotExists = false;
private String subDirectory = "/";
private FileFilter filter = null;
private Charset encoding = StandardCharsets.UTF_8;
/**
* Decompress the given zip file to a directory.
*
* @param zipFile the input zip file to be uncompressed
* @param destDir the dest directory to hold uncompressed files
*/
public Unzipper(Path zipFile, Path destDir) {
this.zipFile = zipFile;
this.dest = destDir;
}
/**
* Decompress the given zip file to a directory.
*
* @param zipFile the input zip file to be uncompressed
* @param destDir the dest directory to hold uncompressed files
*/
public Unzipper(File zipFile, File destDir) {
this(zipFile.toPath(), destDir.toPath());
}
/**
* True if replace the existent files in destination directory,
* otherwise those conflict files will be ignored.
*/
public Unzipper setReplaceExistentFile(boolean replaceExistentFile) {
this.replaceExistentFile = replaceExistentFile;
return this;
}
/**
* Will be called for every entry in the zip file.
* Callback returns false if you want leave the specific file uncompressed.
*/
public Unzipper setFilter(FileFilter filter) {
this.filter = filter;
return this;
}
/**
* Will only uncompress files in the "subDirectory", their path will be also affected.
*
* For example, if you set subDirectory to /META-INF, files in /META-INF/ will be
* uncompressed to the destination directory without creating META-INF folder.
*
* Default value: "/"
*/
public Unzipper setSubDirectory(String subDirectory) {
this.subDirectory = FileUtils.normalizePath(subDirectory);
return this;
}
public Unzipper setEncoding(Charset encoding) {
this.encoding = encoding;
return this;
}
public Unzipper setTerminateIfSubDirectoryNotExists() {
this.terminateIfSubDirectoryNotExists = true;
return this;
}
/**
* Decompress the given zip file to a directory.
*
* @throws IOException if zip file is malformed or filesystem error.
*/
public void unzip() throws IOException {
Files.createDirectories(dest);
try (FileSystem fs = CompressingUtils.readonly(zipFile).setEncoding(encoding).setAutoDetectEncoding(true).build()) {
Path root = fs.getPath(subDirectory);
if (!root.isAbsolute() || (subDirectory.length() > 1 && subDirectory.endsWith("/")))
throw new IllegalArgumentException("Subdirectory for unzipper must be absolute");
if (terminateIfSubDirectoryNotExists && Files.notExists(root))
return;
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
String relativePath = root.relativize(file).toString();
Path destFile = dest.resolve(relativePath);
if (filter != null && !filter.accept(file, false, destFile, relativePath))
return FileVisitResult.CONTINUE;
try {
Files.copy(file, destFile, replaceExistentFile ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[]{});
} catch (FileAlreadyExistsException e) {
if (replaceExistentFile)
throw e;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
String relativePath = root.relativize(dir).toString();
Path dirToCreate = dest.resolve(relativePath);
if (filter != null && !filter.accept(dir, true, dirToCreate, relativePath))
return FileVisitResult.SKIP_SUBTREE;
Files.createDirectories(dirToCreate);
return FileVisitResult.CONTINUE;
}
});
}
}
public interface FileFilter {
boolean accept(Path zipEntry, boolean isDirectory, Path destFile, String entryPath) throws IOException;
}
}

View File

@ -0,0 +1,103 @@
package com.tungsten.fclcore.util.io;
import com.tungsten.fclcore.util.function.ExceptionalPredicate;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
/**
* Non thread-safe
*/
public final class Zipper implements Closeable {
private final FileSystem fs;
public Zipper(Path zipFile) throws IOException {
this(zipFile, null);
}
public Zipper(Path zipFile, Charset encoding) throws IOException {
Files.deleteIfExists(zipFile);
fs = CompressingUtils.createWritableZipFileSystem(zipFile, encoding);
}
@Override
public void close() throws IOException {
fs.close();
}
/**
* Compress all the files in sourceDir
*
* @param source the file in basePath to be compressed
* @param rootDir the path of the directory in this zip file.
*/
public void putDirectory(Path source, String rootDir) throws IOException {
putDirectory(source, rootDir, null);
}
/**
* Compress all the files in sourceDir
*
* @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
*/
public void putDirectory(Path source, String targetDir, ExceptionalPredicate<String, IOException> filter) throws IOException {
Path root = fs.getPath(targetDir);
Files.createDirectories(root);
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (".DS_Store".equals(file.getFileName().toString())) {
return FileVisitResult.SKIP_SUBTREE;
}
String relativePath = source.relativize(file).normalize().toString();
if (filter != null && !filter.test(relativePath.replace('\\', '/'))) {
return FileVisitResult.SKIP_SUBTREE;
}
Files.copy(file, root.resolve(relativePath), StandardCopyOption.COPY_ATTRIBUTES);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String relativePath = source.relativize(dir).normalize().toString();
if (filter != null && !filter.test(relativePath.replace('\\', '/'))) {
return FileVisitResult.SKIP_SUBTREE;
}
Path path = root.resolve(relativePath);
if (Files.notExists(path)) {
Files.createDirectory(path);
}
return FileVisitResult.CONTINUE;
}
});
}
public void putFile(File file, String path) throws IOException {
putFile(file.toPath(), path);
}
public void putFile(Path file, String path) throws IOException {
Files.copy(file, fs.getPath(path), StandardCopyOption.COPY_ATTRIBUTES);
}
public void putStream(InputStream in, String path) throws IOException {
Files.copy(in, fs.getPath(path), StandardCopyOption.COPY_ATTRIBUTES);
}
public void putTextFile(String text, String path) throws IOException {
putTextFile(text, "UTF-8", path);
}
public void putTextFile(String text, String encoding, String pathName) throws IOException {
Files.write(fs.getPath(pathName), text.getBytes(encoding));
}
}

View File

@ -1,5 +1,9 @@
package com.tungsten.fclcore.util.platform;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
public enum OperatingSystem {
/**
* Microsoft Windows.
@ -28,4 +32,31 @@ public enum OperatingSystem {
return checkedName;
}
public static final Charset NATIVE_CHARSET;
static {
String nativeEncoding = System.getProperty("native.encoding");
String hmclNativeEncoding = System.getProperty("fcl.native.encoding");
Charset nativeCharset = Charset.defaultCharset();
try {
if (hmclNativeEncoding != null) {
nativeCharset = Charset.forName(hmclNativeEncoding);
} else {
if (nativeEncoding != null && !nativeEncoding.equalsIgnoreCase(nativeCharset.name())) {
nativeCharset = Charset.forName(nativeEncoding);
}
if (nativeCharset == StandardCharsets.UTF_8 || nativeCharset == StandardCharsets.US_ASCII) {
nativeCharset = StandardCharsets.UTF_8;
} else if ("GBK".equalsIgnoreCase(nativeCharset.name()) || "GB2312".equalsIgnoreCase(nativeCharset.name())) {
nativeCharset = Charset.forName("GB18030");
}
}
} catch (UnsupportedCharsetException e) {
e.printStackTrace();
}
NATIVE_CHARSET = nativeCharset;
}
}

View File

@ -10,6 +10,9 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url "https://jitpack.io"
}
}
}
rootProject.name = "Fold Craft Launcher"