diff --git a/build.gradle.kts b/build.gradle.kts index 6f4202f..4d7ed67 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } group = "io.github.gaming32" -version = "1.0.2" +version = "1.0.3" application { mainClass.set("io.github.gaming32.prcraftinstaller.PrcraftInstaller") @@ -21,6 +21,7 @@ repositories { } dependencies { + api("commons-io:commons-io:2.13.0") api("com.nitorcreations:javaxdelta:1.5") api("net.fabricmc:tiny-remapper:0.8.7") api("org.apache.commons:commons-compress:1.23.0") diff --git a/src/main/java/io/github/gaming32/prcraftinstaller/PrcraftInstaller.java b/src/main/java/io/github/gaming32/prcraftinstaller/PrcraftInstaller.java index 680da5a..f1fc512 100644 --- a/src/main/java/io/github/gaming32/prcraftinstaller/PrcraftInstaller.java +++ b/src/main/java/io/github/gaming32/prcraftinstaller/PrcraftInstaller.java @@ -4,59 +4,96 @@ import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; import net.fabricmc.tinyremapper.TinyUtils; +import org.apache.commons.compress.archivers.zip.Zip64Mode; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; +import org.apache.commons.io.input.SequenceReader; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; import javax.swing.*; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import java.awt.*; import java.io.*; import java.net.URI; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.SeekableByteChannel; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.*; import java.util.Collections; public class PrcraftInstaller { - public static final String DATA_ROOT = "https://github.com/Gaming32/prcraft-installer-data/raw/main"; + public static final String ARTIFACT_ROOT = "https://maven.jemnetworks.com/releases/io/github/gaming32/prcraft"; public static void runInstaller(Path outputFile) throws IOException { - final Path mappingsZipPath = Files.createTempFile("prcraft", ".zip"); final Path unmappedClientPath = Files.createTempFile("prcraft", ".jar"); + try { + downloadFile(unmappedClientPath, "https://launcher.mojang.com/v1/objects/4a2fac7504182a97dcbcd7560c6392d7c8139928/client.jar"); + final String latestVersion = getLatestVersion(); + final SeekableByteChannel patchChannel = new SeekableInMemoryByteChannel(); + downloadFile( + Channels.newOutputStream(patchChannel), + ARTIFACT_ROOT + '/' + latestVersion + "/prcraft-" + latestVersion + ".zip" + ); + runInstaller( + patchChannel, + Integer.parseInt(latestVersion.substring(latestVersion.lastIndexOf('.') + 1)), + unmappedClientPath, outputFile + ); + } finally { + Files.deleteIfExists(unmappedClientPath); + } + } + + public static String getLatestVersion() throws IOException { + try (InputStream is = new URL(ARTIFACT_ROOT + "/maven-metadata.xml").openStream()) { + final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); + return document.getElementsByTagName("latest").item(0).getTextContent(); + } catch (ParserConfigurationException | SAXException e) { + throw new RuntimeException(e); + } + } + + public static void runInstaller(SeekableByteChannel patchChannel, int buildNumber, Path vanillaJar, Path outputFile) throws IOException { + final Path mappingsZipPath = Files.createTempFile("prcraft", ".zip"); final Path mappedClientPath = Files.createTempFile("prcraft", ".jar"); Files.deleteIfExists(mappedClientPath); try { downloadFile(mappingsZipPath, "https://mcphackers.org/versions/1.2.5.zip"); - downloadFile(unmappedClientPath, "https://launcher.mojang.com/v1/objects/4a2fac7504182a97dcbcd7560c6392d7c8139928/client.jar"); - remapClient(mappingsZipPath, unmappedClientPath, mappedClientPath); - final SeekableByteChannel patchChannel = new SeekableInMemoryByteChannel(); - downloadFile(Channels.newOutputStream(patchChannel), DATA_ROOT + "/prcraft-patch.zip"); - try ( - ZipFile patch = new ZipFile(patchChannel); - ZipFile input = new ZipFile(mappedClientPath); - ZipArchiveOutputStream output = new ZipArchiveOutputStream(outputFile); - BufferedReader patchList = new BufferedReader(new InputStreamReader(patch.getInputStream(patch.getEntry("META-INF/file.list")))) - ) { - output.putArchiveEntry(new ZipArchiveEntry("track.txt")); - downloadFile(output, DATA_ROOT + "/buildnumber"); - output.flush(); - output.closeArchiveEntry(); - - patchList.readLine(); - patchList.readLine(); - new JarPatcher("prcraft-patch.zip", "1.2.5-mapped.jar") - .applyDelta(patch, input, output, patchList, ""); + try (ZipFile patch = new ZipFile(patchChannel)) { + try (InputStream extraMappings = patch.getInputStream(patch.getEntry("extramappings.tiny"))) { + remapClient(mappingsZipPath, vanillaJar, mappedClientPath, extraMappings); + } + + try ( + ZipFile input = new ZipFile(mappedClientPath); + ZipArchiveOutputStream output = new ZipArchiveOutputStream(outputFile); + BufferedReader patchList = new BufferedReader(new InputStreamReader(patch.getInputStream(patch.getEntry("META-INF/file.list")))) + ) { + output.setUseZip64(Zip64Mode.Never); + + output.putArchiveEntry(new ZipArchiveEntry("track.txt")); + output.write(Integer.toString(buildNumber).getBytes(StandardCharsets.UTF_8)); + output.flush(); + output.closeArchiveEntry(); + + patchList.readLine(); + patchList.readLine(); + new JarPatcher("prcraft-patch.zip", "1.2.5-mapped.jar") + .applyDelta(patch, input, output, patchList, ""); + } } } finally { Files.deleteIfExists(mappingsZipPath); - Files.deleteIfExists(unmappedClientPath); Files.deleteIfExists(mappedClientPath); } } @@ -73,21 +110,14 @@ private static void downloadFile(OutputStream dest, String url) throws IOExcepti } } - private static byte[] downloadByteArray(String url) throws IOException { - try (InputStream is = new URL(url).openStream()) { - return IOUtils.toByteArray(is); - } - } - - private static void remapClient(Path mappingsZipPath, Path unmappedClientPath, Path destPath) throws IOException { + private static void remapClient(Path mappingsZipPath, Path unmappedClientPath, Path destPath, InputStream extraMappings) throws IOException { try (FileSystem zipFs = FileSystems.newFileSystem(mappingsZipPath, null)) { + final BufferedReader extraMappingsReader = new BufferedReader(new InputStreamReader(extraMappings)); + extraMappingsReader.readLine(); final TinyRemapper remapper = TinyRemapper.newRemapper() .withMappings(TinyUtils.createTinyMappingProvider( - Files.newBufferedReader(zipFs.getPath("/client.tiny")), "official", "named" - )) - .withMappings(TinyUtils.createTinyMappingProvider( - new BufferedReader(new InputStreamReader( - new ByteArrayInputStream(downloadByteArray(DATA_ROOT + "/extramappings.tiny")) + new BufferedReader(new SequenceReader( + Files.newBufferedReader(zipFs.getPath("/client.tiny")), extraMappingsReader )), "official", "named" )) .build();