diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8a0a60d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# Project exclude paths
+/.gradle/
+/build/
+/build/classes/java/main/
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b589d56
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/discord.xml b/.idea/discord.xml
new file mode 100644
index 0000000..d8e9561
--- /dev/null
+++ b/.idea/discord.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..0be8e21
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..fdc392f
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..87a20fc
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..789399f
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,27 @@
+plugins {
+ id 'java'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation('commons-io:commons-io:2.11.0')
+ implementation('net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01')
+ implementation('net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01')
+ implementation('org.apache.commons:commons-compress:1.21')
+ implementation('com.twelvemonkeys.imageio:imageio-tga:3.9.3')
+ implementation('org.jsoup:jsoup:1.15.4')
+ implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13'
+}
+
+task fatJar(type: Jar) {
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ manifest {
+ attributes 'Main-Class': 'swim.porter.Main'
+ }
+ baseName = project.name + '-all'
+ from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
+ with jar
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..41d9927
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..41dfb87
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..1b6c787
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..946a54d
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'PorterEngine'
+
diff --git a/src/main/java/swim/porter/Main.java b/src/main/java/swim/porter/Main.java
new file mode 100644
index 0000000..4869b3b
--- /dev/null
+++ b/src/main/java/swim/porter/Main.java
@@ -0,0 +1,70 @@
+package swim.porter;
+
+import org.apache.commons.io.FileUtils;
+import swim.porter.engine.PortFileProcessor;
+import swim.porter.engine.ZipUtil;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Paths;
+
+public class Main {
+
+ public static String portDir;
+
+ static {
+ try {
+ File jarFile = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
+ portDir = Paths.get(jarFile.getParent(), "Pack Jar").toString();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void main(String[] args) {
+ setUpAssets();
+ String returnValue = ""; // The string value you want to return
+
+ if (args.length > 0 && args[0].equalsIgnoreCase("port") && args.length > 1) {
+ String arg = args[1];
+ String portedPackPath = PortFileProcessor.handlePortCommand(arg);
+ returnValue = portedPackPath; // Set the return value
+ } else {
+ System.out.println("Proper command syntax is 'port url'\n Examples:\n port \"mediafire.com/file.zip\"\n port \"C:\\file.zip\"");
+ }
+
+ // Set the exit code
+ // our server will have to parse the hash code back into a string and upload file back to client
+ System.exit(returnValue.hashCode());
+ }
+
+ private static void setUpAssets() {
+ try {
+ File portBotDirFile = new File(portDir);
+ File assetsDir = new File(portBotDirFile, "assets");
+ // Check if we don't have an assets folder
+ if (!assetsDir.exists()) {
+ portBotDirFile.mkdirs();
+ // If running from the JAR, copy the assets.zip to a temporary file and extract from there
+ if (Main.class.getResource("/assets.zip").toString().startsWith("jar:")) {
+ try (InputStream inputStream = Main.class.getResourceAsStream("/assets.zip")) {
+ File tempAssetsZip = File.createTempFile("assets", ".zip");
+ FileUtils.copyInputStreamToFile(inputStream, tempAssetsZip);
+ ZipUtil.lightUnzip(tempAssetsZip, assetsDir);
+ tempAssetsZip.delete();
+ }
+ } else {
+ // If running from IDE or directly from the extracted JAR, extract assets.zip directly
+ File assetsZipFile = new File(Main.class.getResource("/assets.zip").toURI());
+ ZipUtil.lightUnzip(assetsZipFile, assetsDir);
+ }
+ }
+ // need to clean up from any past uses
+ PortFileProcessor.cleanPackDir();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/CubeMapper.java b/src/main/java/swim/porter/engine/CubeMapper.java
new file mode 100644
index 0000000..e5226ac
--- /dev/null
+++ b/src/main/java/swim/porter/engine/CubeMapper.java
@@ -0,0 +1,41 @@
+package swim.porter.engine;
+
+import javax.imageio.ImageIO;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+public class CubeMapper {
+
+ public static void CubeMapBuild(BufferedImage skyMap, String cubeMapPath) throws IOException {
+ int x = skyMap.getWidth() / 3;
+ int y = skyMap.getHeight() / 2;
+ // top right
+ writeCubeMapImage(skyMap.getSubimage(skyMap.getHeight(), 0, x, y), cubeMapPath + "\\cubemap_0.png", false);
+ // bottom left
+ writeCubeMapImage(skyMap.getSubimage(0, skyMap.getHeight() / 2, x, y), cubeMapPath + "\\cubemap_1.png", false);
+ // bottom middle
+ writeCubeMapImage(skyMap.getSubimage(skyMap.getWidth() / 3, skyMap.getHeight() / 2, x, y), cubeMapPath + "\\cubemap_2.png", false);
+ // bottom right
+ writeCubeMapImage(skyMap.getSubimage(skyMap.getHeight(), skyMap.getHeight() / 2, x, y), cubeMapPath + "\\cubemap_3.png", false);
+ // top middle, does 180 degree flip
+ writeCubeMapImage(skyMap.getSubimage(skyMap.getWidth() / 3, 0, x, y), cubeMapPath + "\\cubemap_4.png", true);
+ // top left, does 180 degree flip
+ writeCubeMapImage(skyMap.getSubimage(0, 0, x, y), cubeMapPath + "\\cubemap_5.png", true);
+ }
+
+ private static void writeCubeMapImage(BufferedImage image, String filePath, boolean flip) throws IOException {
+ if (flip) {
+ AffineTransform tx = AffineTransform.getScaleInstance(-1, -1);
+ tx.translate(image.getWidth(null) * -1, image.getHeight(null) * -1);
+ AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+ BufferedImage flippedImage = op.filter(image, null);
+ ImageIO.write(flippedImage, "png", new File(filePath));
+ } else {
+ ImageIO.write(image, "png", new File(filePath));
+ }
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/Downloader.java b/src/main/java/swim/porter/engine/Downloader.java
new file mode 100644
index 0000000..af26970
--- /dev/null
+++ b/src/main/java/swim/porter/engine/Downloader.java
@@ -0,0 +1,101 @@
+package swim.porter.engine;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.http.client.utils.URIBuilder;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.DataNode;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import swim.porter.Main;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class Downloader {
+
+ final static String fs = File.separator;
+
+ // returns the path of the downloaded pvprp file
+ public static File downloadPVPRP(String url) {
+ try {
+ assert url != null;
+ URL checkSite = new URL(url);
+ HttpURLConnection huc = (HttpURLConnection) checkSite.openConnection();
+ int responseCode = huc.getResponseCode();
+ if (url.contains("pvprp.com/pack?p=") && responseCode == 200) {
+ Document document = Jsoup.connect(url).get();
+ Elements scriptElements = document.getElementsByTag("script");
+ String funct = "";
+ for (Element element : scriptElements) {
+ for (DataNode node : element.dataNodes()) {
+ funct = node.getWholeData();
+ }
+ }
+ String[] assetSplit = funct.split("assets/packs");
+ assetSplit = assetSplit[1].split("\"");
+ String downloadURL = assetSplit[0];
+ String downloadURLBase = downloadURL.substring(0, downloadURL.indexOf('?'));
+ String formattedURL = new URIBuilder().setPath("pvprp.com/assets/packs" + downloadURLBase).toString().substring(1);
+ File portBotDir = new File(Main.portDir); // important
+ Elements fileName = document.getElementsByClass("f-mc extra-large shadow");
+ String name = "placeHolderName";
+ for (Element el : fileName) {
+ name = el.text();
+ }
+ File export = new File(portBotDir + fs + name + ".zip");
+ FileUtils.copyURLToFile(new URL("https://" + formattedURL), export, 30000, 30000);
+ return export;
+ } else {
+ System.out.println("Error : Unable to download pack from PVPRP");
+ }
+ } catch (Exception e) {
+ System.out.println("Error downloading pvprp pack, check the pack for errors and remove any illegal characters in file paths!");
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ // returns the path of the downloaded mediafire file
+ public static File downloadMediafire(String originalURL, boolean isRar) {
+ // we take advantage of corsproxy to bypass mediafire's cloudflare protection
+ // String url = "https://corsproxy.io/?" + URLEncoder.encode(originalURL, "UTF-8"); // jsoup 1.15.4 or something else random made encoding not needed
+ String url = "https://corsproxy.io/?" + originalURL;
+ Document document = null;
+ try {
+ document = Jsoup.connect(url).get();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Elements buttons = document.getElementsByClass("input popsok"); // get a list of the element class we care about
+ if (!buttons.isEmpty()) {
+ Element directDownload = document.getElementById("downloadButton"); // can cause an exception at times
+ assert directDownload != null;
+ String[] hrefSplit = directDownload.outerHtml().split("href=\"");
+ String[] href = hrefSplit[1].split("\"");
+ try {
+ Elements fileName = document.getElementsByClass("dl-btn-label");
+ String name = "placeHolderName";
+ for (Element el : fileName) {
+ name = el.text();
+ }
+ assert originalURL != null;
+ File portBotDir = new File(Main.portDir); // important
+ File export = new File(portBotDir + fs + name + ".zip");
+ if (isRar) {
+ export = new File(portBotDir + fs + name + ".rar");
+ }
+ FileUtils.copyURLToFile(new URL(href[0]), export, 30000, 30000);
+ return export; // return the path of the file we just downloaded
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ System.out.println("mediafire link error");
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/FileImageUtils.java b/src/main/java/swim/porter/engine/FileImageUtils.java
new file mode 100644
index 0000000..c84374e
--- /dev/null
+++ b/src/main/java/swim/porter/engine/FileImageUtils.java
@@ -0,0 +1,40 @@
+package swim.porter.engine;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+public class FileImageUtils {
+
+ // rescales an image file and writes it instantly to the disk
+ public static void resizeImageWrite(File originalImagePath, File resizedImagePath, int rescaleWidth, int rescaleHeight, String format) {
+ try {
+ if (originalImagePath.exists()) {
+ BufferedImage original = ImageIO.read(originalImagePath);
+ BufferedImage resized = Rescale.nearestNeighborRescale(original, rescaleWidth, rescaleHeight);
+ ImageIO.write(resized, format, resizedImagePath);
+ }
+ } catch (IOException e) {
+ System.out.println("failed to resize image");
+ }
+ }
+
+ // removes the useless alpha pixels in an image that interfere with directX's sprite rendering
+ public static BufferedImage imageTransparencyFix(BufferedImage raw) {
+ int WIDTH = raw.getWidth();
+ int HEIGHT = raw.getHeight();
+ BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
+ int pixels[] = new int[WIDTH * HEIGHT];
+ raw.getRGB(0, 0, WIDTH, HEIGHT, pixels, 0, WIDTH);
+ for (int i = 0; i < pixels.length; i++) {
+ int alpha = (pixels[i] & 0xff000000) >>> 24;
+ if (pixels[i] >= alpha) {
+ pixels[i] = 0x00ffffff;
+ }
+ }
+ image.setRGB(0, 0, WIDTH, HEIGHT, pixels, 0, WIDTH);
+ return image;
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/Port.java b/src/main/java/swim/porter/engine/Port.java
new file mode 100644
index 0000000..f420a48
--- /dev/null
+++ b/src/main/java/swim/porter/engine/Port.java
@@ -0,0 +1,936 @@
+package swim.porter.engine;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import swim.porter.Main;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.*;
+import java.util.*;
+import java.util.regex.Matcher;
+
+public class Port {
+
+ final static String fs = File.separator;
+
+ public static File port(File texturePackFile) {
+ String packPath = (Main.portDir + fs);
+ String filePath = texturePackFile.getAbsolutePath();
+ boolean failed = false;
+ String originalDescription = "";
+ try {
+ File mcmeta = new File(texturePackFile + fs + "pack.mcmeta");
+ Scanner reader = new Scanner(mcmeta);
+ // this gets the packs description from the mcmeta file
+ try {
+ while (reader.hasNextLine()) {
+ String data = reader.nextLine();
+ if (data.contains("description")) {
+ String[] splitName = data.split("\"", 4);
+ if (splitName.length >= 4) { // hopefully this if check prevents [1]
+ String desc = splitName[3]; // [1] POTENTIALLY HUGE ERROR this causes packs to screw up sometimes when they really shouldn't
+ originalDescription = desc.replaceAll("\\\\", " "); // filter out escape chars
+ originalDescription = originalDescription.replaceAll("\"", " "); // filter out quotes
+ // there could possibly be more escape chars to worry about having to filter, might want to write regex for this
+ }
+ }
+ }
+ reader.close();
+ } catch (Exception e) {
+ System.out.println("failed to read pack data");
+ e.printStackTrace();
+ PortFileProcessor.cleanPackDir();
+ }
+ } catch (FileNotFoundException ex) {
+ System.out.println(" Failed to port: " + filePath + "\n could not locate pack.mcmeta");
+ ex.printStackTrace();
+ PortFileProcessor.cleanPackDir();
+ }
+
+ String rawName = FilenameUtils.removeExtension(texturePackFile.getName());
+ String packName = FilenameUtils.getName(rawName + " PORT");
+ String description = originalDescription + "\\ndiscord.gg/swim | Ported with Swimfan72's Auto Port ";
+
+ if (!failed) {
+ String texturePackPath = packPath + packName;
+ new File(texturePackPath).mkdirs(); // folder of pack
+ new File(texturePackPath + fs + "textures").mkdirs(); // textures
+ new File(texturePackPath + fs + "textures" + fs + "ui").mkdirs(); // ui
+
+ createManifest(packName, rawName, description, packPath); // creates the manifest for the ported pack
+ icon(texturePackFile, texturePackPath); // copies over the pack icon
+ textures(texturePackFile, texturePackPath); // copies over textures folder
+ blockItemFix(texturePackPath); // renames block directory to blocks if needed, same for items
+ lowerCaseAll(new File(texturePackPath)); // lower cases all file and directory names recursively in the pack
+ armor(texturePackPath); // ports over the armor for the pack
+ painting(texturePackPath); // copies and renames over the paintings
+ colorMap(texturePackFile, texturePackPath); // copies over the colormap
+ font(texturePackFile, texturePackPath); // folder move + rename ascii to default8
+ gui(texturePackPath);
+ fire(texturePackPath); // copies over the fire sprite sheets
+ environment(texturePackFile, texturePackPath); // copies over the environment folder
+ sky(texturePackFile, texturePackPath); // ports the java sky to a cubemap
+ xp(texturePackPath); // ports over the xp bar from the gui
+ guiFix(texturePackPath); // resizes accordingly
+ crossHairFix(texturePackPath); // gets the crosshair from the gui and cleans the background
+ chestFix(texturePackPath); // renames chests
+ potionEffectsUI(texturePackPath); // extracts out the potion effect UI
+ panorama(texturePackFile, texturePackPath); // if possible set a menu background for the pack
+ sounds(texturePackFile, texturePackPath); // copy over the sounds files
+ itemsFix(texturePackPath); // delete the weird alpha pixels from every item
+ potions(texturePackPath); // iteratively create the potions
+ containerUI(texturePackFile, packPath, packName); // ports containers UI
+ grassSide(texturePackPath); // tga handling (broken)
+ mobileButtons(texturePackPath); // creates default mobile buttons for the pack
+
+ // finally we will need to zip the pack and turn its file extension to a .mcpack
+ File mcpack = new File(texturePackPath + ".mcpack");
+ finalizePort(texturePackPath);
+ return (mcpack);
+ }
+ return null;
+ }
+
+ private static void createManifest(String PackName, String rawName, String description, String packPath) {
+ try {
+ FileWriter manifest = new FileWriter(packPath + PackName + fs + "manifest.json");
+ // create the UUIDs for the manifest file
+ String uuid = UUID.randomUUID().toString();
+ String uuid2 = UUID.randomUUID().toString();
+ manifest.write("{\n");
+ manifest.write(" \"format_version\": 1,\n");
+ manifest.write(" \"header\": {\n");
+ manifest.write(" \"description\": \"" + description + "\",\n");
+ manifest.write(" \"name\": \"" + rawName + "\",\n");
+ manifest.write(" \"uuid\": \"" + uuid + "\",\n");
+ manifest.write(" \"version\": [1, 0, 0],\n");
+ manifest.write(" \"min_engine_version\": [1, 12, 1]\n");
+ manifest.write(" },\n");
+ manifest.write(" \"modules\": [\n");
+ manifest.write(" {\n");
+ manifest.write(" \"description\": \"" + description + "\",\n");
+ manifest.write(" \"type\": \"resources\",\n");
+ manifest.write(" \"uuid\": \"" + uuid2 + "\",\n");
+ manifest.write(" \"version\": [1, 0, 0]\n");
+ manifest.write(" }\n");
+ manifest.write(" ]\n");
+ manifest.write("}\n");
+ manifest.close();
+ } catch (IOException e) {
+ System.out.println("An error occurred creating the manifest.json");
+ e.printStackTrace();
+ PortFileProcessor.cleanPackDir();
+ }
+ }
+
+ private static void guiFix(String packPath) {
+ try {
+ File gui = new File(packPath + fs + "textures" + fs + "gui" + fs + "gui.png");
+ if (gui.exists()) {
+ BufferedImage guiIMG = ImageIO.read(gui);
+ if (guiIMG.getHeight() != 256) {
+ FileImageUtils.resizeImageWrite(gui, gui, 256, 256, "png");
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("failed to scale gui");
+ }
+ }
+
+ private static void icon(File file, String packPath) {
+ File source = new File(file + fs + "pack.png");
+ if (source.exists()) {
+ File dest = new File(packPath + fs + "pack.png");
+ try {
+ FileUtils.moveFile(source, dest); // was originally copy
+ File newIconName = new File(packPath + fs + "pack.png");
+ File fixedName = new File(packPath + fs + "pack_icon.png");
+ newIconName.renameTo(fixedName);
+ } catch (IOException e) {
+ System.out.println("icon error " + e.getMessage());
+ }
+ }
+ }
+
+ private static void chestFix(String packPath) {
+ File chestOld = new File(packPath + fs + "textures" + fs + "entity" + fs + "chest" + fs + "normal_double.png");
+ if (chestOld.exists()) {
+ File chestNew = new File(packPath + fs + "textures" + fs + "entity" + fs + "chest" + fs + "double_normal.png");
+ try {
+ chestOld.renameTo(chestNew);
+ } catch (Exception e) {
+ System.out.println("failed to port chests: " + e.getMessage());
+ }
+ }
+ }
+
+ private static void textures(File file, String packPath) {
+ File source = new File(file + fs + "assets" + fs + "minecraft" + fs + "textures");
+ if (source.exists()) {
+ File dest = new File(packPath + fs + "textures");
+ try {
+ FileUtils.copyDirectory(source, dest);
+ //FileUtils.moveDirectory(source, dest);
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("failed to port textures: " + e.getMessage());
+ }
+ }
+ }
+
+ private static void mobileButtons(String packPath) {
+ try {
+ File guiURL = new File(packPath + fs + "textures" + fs + "gui" + fs + "gui.png");
+ if (guiURL.exists()) {
+ File mobileURL = new File(Main.portDir + fs + "assets" + fs + "mobile" + fs + "mobile_buttons.png");
+ BufferedImage guiOriginal = ImageIO.read(guiURL);
+ BufferedImage mobileImage = ImageIO.read(mobileURL);
+ Graphics g = guiOriginal.getGraphics();
+ g.drawImage(mobileImage, 0, 0, null);
+ g.dispose();
+ ImageIO.write(guiOriginal, "png", guiURL);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void grassSide(String packPath) {
+ // we can only do this if we have the dirt block in the pack
+ File dirt = new File(packPath + fs + "textures" + fs + "blocks" + fs + "dirt.png");
+ if (dirt.exists()) {
+ // get the grass side overlay
+ File grassSide = new File(packPath + fs + "textures" + fs + "blocks" + fs + "grass_side_overlay.png");
+ // we can only do this if grass side exists
+ if (grassSide.exists()) {
+ // make the grass side overlay
+ sideOverlayTGA(packPath + fs + "textures" + fs + "blocks" + fs + "grass_side.tga", grassSide, dirt);
+ // remove the old one
+ File grass_side = new File(packPath + fs + "textures" + fs + "blocks" + fs + "grass_side.png");
+ if (grass_side.exists()) {
+ grass_side.delete();
+ }
+ // make the snow side overlay
+ sideOverlayTGA(packPath + fs + "textures" + fs + "blocks" + fs + "grass_side_snow.tga", grassSide, dirt);
+ // remove the old one
+ File snowSide = new File(packPath + fs + "textures" + fs + "blocks" + fs + "grass_side_snowed.png");
+ if (snowSide.exists()) {
+ snowSide.delete();
+ }
+ }
+ }
+ }
+
+ private static void sideOverlayTGA(String exportPath, File overlay, File base) {
+ try {
+ BufferedImage overlayImage = ImageIO.read(overlay);
+ BufferedImage baseImage = ImageIO.read(base);
+ if (overlayImage != null && baseImage != null) {
+ int width = baseImage.getWidth();
+ int height = baseImage.getHeight();
+ // draw the images on top of each other with the baseImage having 50 alpha value
+ BufferedImage canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = canvas.createGraphics();
+ // Set the alpha value for the base image to be 20% opaque
+ graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
+ graphics.drawImage(baseImage, 0, 0, null);
+ // Scale and draw the grass overlay image on top of the base image
+ BufferedImage scaledGrassSideImage = Rescale.nearestNeighborRescale(overlayImage, width, height);
+ graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); // set transparency back to full
+ graphics.drawImage(scaledGrassSideImage, 0, 0, null);
+ // now write it to Targa format
+ File outputImageFile = new File(exportPath);
+ ImageIO.write(canvas, "tga", outputImageFile); // 12monkeys dependency should handle this
+ graphics.dispose(); // close resources after use!
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // works recursively
+ private static void lowerCaseAll(File master) {
+ try {
+ File[] directoryListing = master.listFiles();
+ if (directoryListing != null) {
+ for (File currentFile : directoryListing) {
+ String lowerCasedName = FilenameUtils.getName(currentFile.toString().toLowerCase());
+ File newName = new File(currentFile.getParentFile() + fs + lowerCasedName);
+ currentFile.renameTo(newName);
+ if (currentFile.isDirectory()) {
+ lowerCaseAll(currentFile);
+ }
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("failed to lower case files: " + e.getMessage());
+ }
+ }
+
+ private static void blockItemFix(String packPath) {
+ try {
+ File blockPath = new File(packPath + fs + "textures" + fs + "block" + fs);
+ File blockPathFixed = new File(packPath + fs + "textures" + fs + "blocks" + fs);
+ if (blockPath.exists()) {
+ blockPath.renameTo(blockPathFixed);
+ }
+ File itemPath = new File(packPath + fs + "textures" + fs + "item" + fs);
+ File itemPathFixed = new File(packPath + fs + "textures" + fs + "items" + fs);
+ if (itemPath.exists()) {
+ itemPath.renameTo(itemPathFixed);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("directory renaming not required for this pack?");
+ }
+ }
+
+ private static void potionEffectsUI(String packPath) {
+ try {
+ File inventory = new File(packPath + fs + "textures" + fs + "gui" + fs + "container" + fs + "inventory.png");
+ if (inventory.exists()) {
+ String path = packPath + fs + "textures" + fs + "ui" + fs;
+ BufferedImage spriteSheet = ImageIO.read(inventory);
+ double sin = spriteSheet.getHeight() / 4.41379310345;
+ int epilsonFlat = (int) Math.round(sin);
+ int startingY = spriteSheet.getHeight() - epilsonFlat;
+ double hypo = spriteSheet.getHeight() / 14.2222222222;
+ int cellChangeFactor = (int) Math.round(hypo);
+ String[] effects = {"speed", "slowness", "haste", "mining_fatigue", "strength", "weakness", "poison", "regeneration", "invisibility", "saturation", "jump_boost", "nausea", "night_vision", "blindness", "resistance", "fire_resistance", "water_breathing", "wither", "absorption"};
+ // now that we have all our variables, loop through and sub image
+ int sheetRow = 0;
+ int x;
+ for (int i = 0; i < 19; i++) {
+ if (i % 8 == 0 && i != 0) {
+ x = 0;
+ sheetRow = 1;
+ startingY = startingY + cellChangeFactor;
+ } else {
+ x = sheetRow * cellChangeFactor;
+ sheetRow++;
+ }
+ ImageIO.write(spriteSheet.getSubimage(x, startingY, cellChangeFactor, cellChangeFactor), "png", new File(path + effects[i] + "_effect.png"));
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static final Map PotionColorMap = new HashMap<>();
+
+ static {
+ PotionColorMap.put("blindness", Color.darkGray);
+ PotionColorMap.put("damageBoost", new Color(139, 0, 0));
+ PotionColorMap.put("fireResistance", Color.orange);
+ PotionColorMap.put("harm", new Color(139, 0, 0));
+ PotionColorMap.put("heal", Color.RED);
+ PotionColorMap.put("invisibility", Color.lightGray);
+ PotionColorMap.put("jump", Color.green);
+ PotionColorMap.put("luck", Color.GREEN);
+ PotionColorMap.put("moveSlowdown", Color.lightGray);
+ PotionColorMap.put("moveSpeed", Color.CYAN);
+ PotionColorMap.put("nightVision", Color.blue);
+ PotionColorMap.put("poison", Color.green);
+ PotionColorMap.put("regeneration", Color.PINK);
+ PotionColorMap.put("slowFall", Color.LIGHT_GRAY);
+ PotionColorMap.put("turtleMaster", Color.lightGray);
+ PotionColorMap.put("waterBreathing", Color.lightGray);
+ PotionColorMap.put("weakness", Color.black);
+ PotionColorMap.put("haste", Color.yellow);
+ PotionColorMap.put("wither", new Color(61, 43, 31));
+ }
+
+ private static void tintPots(String packPath, File potBlank, File overlay, boolean splash) {
+ try {
+ BufferedImage overImage = ImageIO.read(overlay);
+ BufferedImage sourceImage = ImageIO.read(potBlank);
+ // iterate the color map for each potion
+ for (Map.Entry entry : PotionColorMap.entrySet()) {
+ String effect = entry.getKey();
+ Color color = entry.getValue();
+ // we need to copy in a new image buffer
+ BufferedImage source = new BufferedImage(sourceImage.getColorModel(), sourceImage.copyData(null), sourceImage.isAlphaPremultiplied(), null);
+ BufferedImage over = new BufferedImage(overImage.getColorModel(), overImage.copyData(null), overImage.isAlphaPremultiplied(), null);
+ // tint it and draw it in graphics image overlayed on top
+ over = Recolor.tint(over, color);
+ Graphics g = source.getGraphics();
+ g.drawImage(over, 0, 0, null);
+ g.dispose();
+ // write to disk
+ File path = new File(packPath + fs + "textures" + fs + "items" + fs + "potion_bottle_" + (splash ? "splash_" : "") + effect + ".png");
+ ImageIO.write(source, "png", path);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void potions(String packPath) {
+ try {
+ File drinkBlank = new File(packPath + fs + "textures" + fs + "items" + fs + "potion_bottle_drinkable.png");
+ File splashBlank = new File(packPath + fs + "textures" + fs + "items" + fs + "potion_bottle_splash.png");
+ File overlay = new File(packPath + fs + "textures" + fs + "items" + fs + "potion_overlay.png");
+ // check if our overlay png exists
+ if (overlay.exists()) {
+ // tint the drink pots
+ if (drinkBlank.exists()) {
+ BufferedImage overlayCompare = ImageIO.read(overlay);
+ BufferedImage drinkCompare = ImageIO.read(drinkBlank);
+ if (overlayCompare.getHeight() != drinkCompare.getHeight()) {
+ // correcting the potion overlay size (drink)
+ FileImageUtils.resizeImageWrite(overlay, overlay, drinkCompare.getHeight(), drinkCompare.getWidth(), "png");
+ }
+ tintPots(packPath, drinkBlank, overlay, false);
+ }
+ // tint the splash pots
+ if (splashBlank.exists()) {
+ BufferedImage overlayCompare = ImageIO.read(overlay);
+ BufferedImage splashCompare = ImageIO.read(splashBlank);
+ if (overlayCompare.getHeight() != splashCompare.getHeight()) {
+ // correcting the potion overlay size (splash)
+ FileImageUtils.resizeImageWrite(overlay, overlay, splashCompare.getWidth(), splashCompare.getHeight(), "png");
+ }
+ tintPots(packPath, splashBlank, overlay, true);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void itemsFix(String packPath) {
+ try {
+ File items = new File(packPath + fs + "textures" + fs + "items");
+ if (items.exists()) {
+ String[] images = items.list();
+ assert images != null;
+ for (String currentImage : images) {
+ try {
+ if (currentImage.toLowerCase().endsWith(".png")) {
+ File img = new File(packPath + fs + "textures" + fs + "items" + fs + currentImage);
+ BufferedImage item = ImageIO.read(img);
+ BufferedImage clean = FileImageUtils.imageTransparencyFix(item);
+ ImageIO.write(clean, "png", img);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ System.out.println("failed to fix image " + currentImage + " (might not be a .png file?)");
+ }
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("failed to find items folder");
+ }
+ }
+
+ private static void xp(String packPath) {
+ try {
+ String path = packPath + fs + "textures" + fs + "ui" + fs;
+ File iconsFile = new File(packPath + fs + "textures" + fs + "gui" + fs + "icons.png");
+ if (iconsFile.exists()) {
+ BufferedImage icons = ImageIO.read(iconsFile);
+ int x = 0;
+ int y = icons.getHeight() / 4;
+ double padding = icons.getWidth() / 3.45945945946; // calculate extra space between the xp bar and img width
+ int extra = (int) Math.round(padding); // round it to a whole integer
+ int xpBarLength = icons.getWidth() - extra; // subtract the extra white space to find the xp bar's length
+ // 256x256 is probably the smallest icons.png can be
+ int xpBarWidth = 5 * (icons.getWidth() / 256); // height doubles as a multiple of 5, factor of width
+
+ ImageIO.write(icons.getSubimage(x, y, xpBarLength, xpBarWidth), "png", new File(path + "experiencebarempty.png"));
+ ImageIO.write(icons.getSubimage(x, y + xpBarWidth, xpBarLength, xpBarWidth), "png", new File(path + "experiencebarfull.png"));
+
+ File source = new File(packPath + fs + "textures" + fs + "ui");
+ File dest = new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements");
+
+ FileUtils.copyDirectory(source, dest);
+ File xpBarEmpty2 = new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "experiencebarempty.png");
+ File xpBarFull2 = new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "experiencebarfull.png");
+
+ FileUtils.copyFile(xpBarEmpty2, new File(packPath + fs + "textures" + fs + "ui" + fs + "empty_progress_bar.png")); // new
+ FileUtils.copyFile(xpBarFull2, new File(packPath + fs + "textures" + fs + "ui" + fs + "filled_progress_bar.png")); // new
+
+ File hotdogempty = new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "hotdogempty.png");
+ File hotdogfull = new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "hotdogfull.png");
+
+ xpBarEmpty2.renameTo(hotdogempty);
+ xpBarFull2.renameTo(hotdogfull);
+
+ // now we need to put in notnub.png and nub.png into the achievements folder
+ BufferedImage nubImage = ImageIO.read(new File(Main.portDir + fs + "assets" + fs + "nub" + fs + "nub.png"));
+ File nubPath = new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "nub.png");
+
+ BufferedImage experienceNubImage = ImageIO.read(new File
+ (Main.portDir + fs + "assets" + fs + "nub" + fs + "experiencenub.png"));
+ File xpNubPath = new File(packPath + fs + "textures" + fs + "ui" + fs + "experiencenub.png");
+
+ BufferedImage experienceBarBlueNubImage = ImageIO.read(new File(Main.portDir + fs + "assets" + fs + "nub" + fs + "experience_bar_nub_blue.png"));
+ File xpBarNubPath = new File(packPath + fs + "textures" + fs + "ui" + fs + "experience_bar_nub_blue.png");
+ if (new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements").exists()) {
+ ImageIO.write(nubImage, "png", nubPath);
+ ImageIO.write(experienceNubImage, "png", xpNubPath);
+ ImageIO.write(experienceBarBlueNubImage, "png", xpBarNubPath);
+ }
+
+ // a ghost achievements folder gets generated *sometimes* so I guess just delete it
+ File ghost = new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "achievements");
+ if (ghost.exists()) {
+ ghost.delete();
+ }
+ // now we need to make the xp bar json files
+ // which will be done in barJson();
+ barJson(packPath);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void barJson(String packPath) {
+ try {
+ FileWriter experiencebarempty = new FileWriter(packPath + fs + "textures" + fs + "ui" + fs + "experiencebarempty.json");
+ experiencebarempty.write("{\n");
+ experiencebarempty.write(" \"nineslice_size\": [\n");
+ experiencebarempty.write(" 6,\n");
+ experiencebarempty.write(" 1,\n");
+ experiencebarempty.write(" 6,\n");
+ experiencebarempty.write(" 1\n");
+ experiencebarempty.write(" ],\n");
+ experiencebarempty.write(" \"base_size\": [\n");
+ experiencebarempty.write(" " + 182 + ",\n");
+ experiencebarempty.write(" " + 5 + "\n");
+ experiencebarempty.write(" ]\n");
+ experiencebarempty.write("}\n");
+ experiencebarempty.close();
+ } catch (IOException e) {
+ // System.out.println("An error occurred creating the experiencebarempty.json");
+ }
+ try {
+ FileWriter experiencebarfull = new FileWriter(packPath + fs + "textures" + fs + "ui" + fs + "experiencebarfull.json");
+ experiencebarfull.write("{\n");
+ experiencebarfull.write(" \"nineslice_size\": [\n");
+ experiencebarfull.write(" 1,\n");
+ experiencebarfull.write(" 0,\n");
+ experiencebarfull.write(" 1,\n");
+ experiencebarfull.write(" 0\n");
+ experiencebarfull.write(" ],\n");
+ experiencebarfull.write(" \"base_size\": [\n");
+ experiencebarfull.write(" " + 182 + ",\n");
+ experiencebarfull.write(" " + 5 + "\n");
+ experiencebarfull.write(" ]\n");
+ experiencebarfull.write("}\n");
+ experiencebarfull.close();
+ // new
+ File xpJSON = new File(packPath + fs + "textures" + fs + "ui" + fs + "experiencebarfull.json");
+ FileUtils.copyFile(xpJSON, new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "hotdogempty.json"));
+ FileUtils.copyFile(xpJSON, new File(packPath + fs + "textures" + fs + "gui" + fs + "achievements" + fs + "hotdogfull.json"));
+ FileUtils.copyFile(xpJSON, new File(packPath + fs + "textures" + fs + "ui" + fs + "empty_progress_bar.json"));
+ FileUtils.copyFile(xpJSON, new File(packPath + fs + "textures" + fs + "ui" + fs + "filled_progress_bar.json"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("An error occurred creating the experiencebarfull.json");
+ }
+ }
+
+ private static void sky(File file, String packPath) {
+ String[] skyboxNames = {"cloud1", "cloud2", "starfield03", "starfield", "skybox", "skybox2"};
+ BufferedImage skyMap = null;
+ for (String skyboxName : skyboxNames) {
+ File skyPath = new File(file + fs + "assets" + fs + "minecraft" + fs + "mcpatcher" + fs + "sky" + fs + "world0" + fs + skyboxName + ".png");
+ if (skyPath.exists()) {
+ try {
+ skyMap = ImageIO.read(skyPath);
+ break;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ // hopefully that managed to grab a sky box
+ if (skyMap != null) {
+ String path = packPath + fs + "textures" + fs + "environment" + fs + "overworld_cubemap" + fs;
+ try {
+ CubeMapper.CubeMapBuild(skyMap, path);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static void panorama(File file, String packPath) {
+ try {
+ File panorama = new File(file + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "title" + fs + "background" + fs);
+ if (panorama.exists()) {
+ FileUtils.moveDirectoryToDirectory(panorama, new File(packPath + fs + "textures" + fs + "gui" + fs), true);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void sounds(File file, String packPath) {
+ try {
+ File soundsDir = new File(file + fs + "assets" + fs + "minecraft" + fs + "sounds");
+ if (soundsDir.exists()) {
+ File dest = new File(packPath);
+ // FileUtils.copyDirectoryToDirectory(soundsDir, dest);
+ FileUtils.moveDirectoryToDirectory(soundsDir, dest, true);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static HashMap armorNames = new HashMap<>();
+
+ static {
+ armorNames.put("diamond_layer_1", "diamond_1");
+ armorNames.put("diamond_layer_2", "diamond_2");
+ armorNames.put("chainmail_layer_1", "chain_1");
+ armorNames.put("chainmail_layer_2", "chain_2");
+ armorNames.put("gold_layer_1", "gold_1");
+ armorNames.put("gold_layer_2", "gold_2");
+ armorNames.put("iron_layer_1", "iron_1");
+ armorNames.put("iron_layer_2", "iron_2");
+ armorNames.put("leather_layer_1", "cloth_1");
+ armorNames.put("leather_layer_2", "cloth_2");
+ }
+
+ private static void armor(String packPath) {
+ String path = packPath + fs + "textures" + fs + "models" + fs + "armor" + fs;
+ if (new File(path).exists()) {
+ for (Map.Entry entry : armorNames.entrySet()) {
+ try {
+ File original = new File(path + entry.getKey() + ".png");
+ if (original.exists()) {
+ File newName = new File(path + entry.getValue() + ".png");
+ original.renameTo(newName);
+ }
+ } catch (Exception e) {
+ System.out.println("error renaming armor");
+ }
+ }
+ }
+ }
+
+ private static void colorMap(File file, String packPath) {
+ try {
+ File source = new File(file + fs + "assets" + fs + "minecraft" + fs + "mcpatcher" + fs + "colormap");
+ if (source.exists()) {
+ File dest = new File(packPath + fs + "textures" + fs + "colormap");
+ FileUtils.moveDirectory(source, dest); // was originally copy
+ }
+ } catch (Exception e) {
+ System.out.println("failed to locate colormap");
+ }
+ }
+
+ private static void environment(File file, String packPath) {
+ try {
+ new File(packPath + fs + "textures" + fs + "environment" + fs + "overworld_cubemap").mkdirs();
+ for (int i = 0; i < 10; i++) {
+ File source = new File(file + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "blocks" + fs + "destroy_stage_" + i + ".png");
+ if (source.exists()) {
+ File dest = new File(packPath + fs + "textures" + fs + "environment" + fs + "destroy_stage_" + i + ".png");
+ FileUtils.moveFile(source, dest); // was originally copy
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("failed to port environment folder");
+ }
+ }
+
+ private static void font(File file, String packPath) {
+ try {
+ File source1 = new File(file + fs + "assets" + fs + "minecraft" + fs + "mcpatcher" + fs + "font"); // copy the font
+ File source2 = new File(file + fs + "assets" + fs + "minecraft" + fs + "font");
+ File source3 = new File(file + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "font");
+ File dest = new File(packPath + fs + "font");
+ // refactored this greatly to use move instead of copy
+ if (source1.exists()) {
+ FileUtils.moveDirectory(source1, dest);
+ } else if (source2.exists()) {
+ FileUtils.moveDirectory(source2, dest);
+ } else if (source3.exists()) {
+ FileUtils.moveDirectory(source3, dest);
+ }
+ // now rename the font from ascii to default8, so it works on bedrock
+ String path = dest + fs;
+ File original = new File(path + "ascii.png");
+ File newName = new File(path + "default8.png");
+ original.renameTo(newName);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("failed to port font");
+ }
+ }
+
+ private static void gui(String packPath) {
+ try {
+ String path = packPath + fs + "textures" + fs + "gui" + fs;
+ File original = new File(path + "widgets.png");
+ if (original.exists()) {
+ File newName = new File(path + "gui.png");
+ original.renameTo(newName);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("failed to port gui");
+ }
+ }
+
+ private static void crossHairFix(String packPath) {
+ try {
+ String path = packPath + fs + "textures" + fs + "gui" + fs;
+ File icons = new File(path + "icons.png");
+ if (icons.exists()) {
+ BufferedImage readIcons = ImageIO.read(icons);
+ // crosshair boxes are base 16 according to icons.png size
+ int crossHairSize = readIcons.getWidth() / 16;
+ // sub image out the crosshair from the top left of icons.png
+ BufferedImage crosshair = readIcons.getSubimage(0, 0, crossHairSize, crossHairSize);
+ // create a canvas of the crosshair size
+ BufferedImage canvas = new BufferedImage(crossHairSize, crossHairSize, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = canvas.getGraphics();
+ // fill the canvas with a black rectangle
+ g.setColor(Color.black);
+ g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
+ // now draw the crosshair
+ g.drawImage(crosshair, 0, 0, null);
+ g.dispose();
+ ImageIO.write(canvas, "png", new File(packPath + fs + "textures" + fs + "ui" + fs + "cross_hair.png"));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("failed to port cross hair");
+ }
+ }
+
+ private static void fire(String packPath) {
+ try {
+ String path = packPath + fs + "textures" + fs + "blocks" + fs;
+ File original = new File(path + "fire_layer_1.png");
+ if (original.exists()) {
+ File newName = new File(path + "fire_1.png");
+ original.renameTo(newName);
+ }
+ File original2 = new File(path + "fire_layer_0.png");
+ if (original2.exists()) {
+ File newName2 = new File(path + "fire_0.png");
+ original2.renameTo(newName2);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("failed to port fire");
+ }
+ }
+
+ private static void painting(String packPath) {
+ try {
+ String path = packPath + fs + "textures" + fs + "painting" + fs;
+ File original = new File(path + "paintings_kristoffer_zetterstrand.png");
+ if (original.exists()) {
+ File newName = new File(path + "kz.png");
+ original.renameTo(newName);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("failed to port painting");
+ }
+ }
+
+ // entire gui container UI port as well (chests and that stuff)
+ // a lot of potentially redundant code, could maybe get be improved
+ // this part of the process is fairly slow due to having to copy over folders from assets
+ private static void containerUI(File file, String packPath, String packName) {
+ String packPathFull = packPath + packName;
+ File checkInventory = new File(packPathFull + fs + "textures" + fs + "gui" + fs + "container" + fs + "inventory.png");
+ File checkChest = new File(packPathFull + fs + "textures" + fs + "entity" + fs + "chest" + fs + "double_normal.png");
+ // we only port containers if we know an inventory or a chest in the pack exists
+ if (checkInventory.exists() || checkChest.exists()) {
+ try {
+ String path = packPathFull + fs;
+ // First thing to do is to put in the UI folder
+ Path destUI = Paths.get(path.replaceAll("\\\\|\\/", Matcher.quoteReplacement(File.separator)));
+ File UI = new File(Main.portDir + fs + "assets" + fs + "ui");
+ FileUtils.copyDirectoryToDirectory(UI, new File(destUI.toString()));
+ // uidx
+ Path destUIDX = destUI;
+ File UIDX = new File(Main.portDir + fs + "assets" + fs + "uidx");
+ FileUtils.copyDirectoryToDirectory(UIDX, new File(destUIDX.toString()));
+ // uidx textures folder
+ Path texturesUIDXdest = Paths.get(path.replaceAll("\\\\|\\/", Matcher.quoteReplacement(File.separator)) + "textures");
+ File texturesUIDX = new File(Main.portDir + fs + "assets" + fs + "textures_uidx" + fs + "uidx");
+ FileUtils.copyDirectoryToDirectory(texturesUIDX, new File(texturesUIDXdest.toString()));
+ // java assets
+ File assets = new File(file + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container");
+ File assetsDest = new File(packPathFull + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container" + fs);
+ try {
+ FileUtils.copyDirectory(assets, assetsDest);
+ } catch (Exception ex) {
+ // System.out.println("pack being ported does not have a container");
+ }
+ // recipe book
+ File recipeBook = new File(Main.portDir + fs + "assets" + fs + "recipe_book");
+ FileUtils.copyDirectoryToDirectory(recipeBook, new File(path + "assets/uidx/textures/gui/container"));
+ FileUtils.copyDirectoryToDirectory(recipeBook, new File(texturesUIDXdest.toString()));
+ // container
+ File containerDest = new File(packPathFull + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container" + fs);
+ File container = new File(file + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container");
+ try {
+ FileUtils.copyDirectory(container, containerDest);
+ } catch (Exception ex) {
+ // System.out.println("pack being ported does not have a container for assets");
+ }
+ // chest and end chest
+ File generic54 = new File(packPathFull + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container" + fs + "generic_54.png");
+ File newGeneric = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container" + fs + "generic_54.png");
+
+ try {
+ FileUtils.copyFile(generic54, newGeneric);
+ File renameEndChest = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container" + fs + "generic_54.png");
+ File ender_chest = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container" + fs + "ender_chest.png");
+ renameEndChest.renameTo(ender_chest);
+ } catch (Exception e) {
+ // System.out.println("generic54.png does not exist");
+ }
+
+ try {
+ FileUtils.copyFile(generic54, newGeneric);
+ File renameChest = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container" + fs + "generic_54.png");
+ File small_chest = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container" + fs + "small_chest.png");
+ renameChest.renameTo(small_chest);
+ } catch (Exception e) {
+ // System.out.println("generic54.png does not exist");
+ }
+
+ File inventoryCreativeCheck = new File(packPathFull + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container" + fs + "creative_inventory");
+ if (!inventoryCreativeCheck.exists()) {
+ File creative = new File(Main.portDir + fs + "assets" + fs + "container" + fs + "creative_inventory");
+ FileUtils.copyDirectoryToDirectory(creative, inventoryCreativeCheck.getParentFile());
+ }
+
+ try {
+ // handle this later in check catch if it fails
+ FileUtils.copyFile(generic54, newGeneric);
+ File renameChest = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container" + fs + "generic_54.png");
+ File small_chest = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container" + fs + "small_chest.png");
+ renameChest.renameTo(small_chest);
+ } catch (Exception e) {
+ // System.out.println("can not find generic54, copying over default container assets");
+ File containerDir = new File(Main.portDir + fs + "assets" + fs + "container" + fs);
+ new File(Main.portDir + fs + packName + fs + "tempIMGS").mkdirs();
+ File tempIMGS = new File(Main.portDir + fs + packName + fs + "tempIMGS");
+ FileUtils.copyDirectoryToDirectory(containerDir, tempIMGS);
+ File temps = new File(tempIMGS + fs + "container");
+ File[] imgs = temps.listFiles();
+ for (File currentGUI : imgs) {
+ File checker = new File(packPathFull + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container" + fs + currentGUI.getName());
+ if (!checker.exists() && !currentGUI.isDirectory()) {
+ // System.out.println("Importing default container image " + currentGUI);
+ try {
+ FileUtils.copyFile(currentGUI, checker);
+ } catch (Exception ex) {
+ // System.out.println("save location does not exist (no container)");
+ }
+ }
+ }
+ try {
+ // small_chest handling from here
+ File uidxContainer = new File(packPathFull + fs + "assets" + fs + "uidx" + fs + "textures" + fs + "gui" + fs + "container");
+ File generic = new File(packPathFull + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container" + fs + "generic_54.png");
+ File genDest = new File(uidxContainer + fs + "generic_54.png");
+ FileUtils.copyFile(generic, genDest);
+ File smallChest = new File(uidxContainer + fs + "small_chest.png");
+ genDest.renameTo(smallChest);
+ FileUtils.copyFile(generic, genDest);
+ File eChest = new File(uidxContainer + fs + "ender_chest.png");
+ genDest.renameTo(eChest);
+ FileUtils.copyFile(generic, genDest);
+ } catch (Exception e1) {
+ // System.out.println("failed to port small_chest.png");
+ }
+ }
+ } catch (Exception e) {
+ System.out.println("failed to port container");
+ }
+
+ // scale method via global_variables.json editing
+ try {
+ Path global_variables = Paths.get(packPathFull + fs + "ui" + fs + "_global_variables.json");
+ Charset charset = StandardCharsets.UTF_8;
+ String content = Files.readString(global_variables, charset);
+ String[] containerNames = {"inventory", "generic_54", "brewing_stand", "crafting_table", "furnace", "blast_furnace", "smoker", "enchanting_table", "anvil", "hopper", "dispenser", "beacon", "horse"};
+ // iterate through the containers and fill in the resolutions
+ for (String containerName : containerNames) {
+ String currentName = containerName; // this is used for file look up only
+ // blast furnace and smoker share the furnace UI
+ if (containerName.equals("blast_furnace") || containerName.equals("smoker")) {
+ currentName = "furnace";
+ }
+ File containerFile = new File(packPathFull + fs + "assets" + fs + "minecraft" + fs + "textures" + fs + "gui" + fs + "container" + fs + currentName + ".png"); // file look up
+ if (containerFile.exists()) {
+ BufferedImage containerImage = ImageIO.read(containerFile);
+ int height = containerImage.getHeight();
+ int width = containerImage.getWidth();
+ // System.out.println(containerName + " | width: " + width + " | height: " + height);
+ int dimension = height;
+ // Valid height and width values
+ int[] validDimensions = {256, 512, 1024, 2048, 4096, 8192};
+ // Check if the height and width are valid dimensions
+ boolean validHeight = Arrays.stream(validDimensions).anyMatch(x -> x == height);
+ boolean validWidth = Arrays.stream(validDimensions).anyMatch(x -> x == width);
+ // If height and width are not equal or not valid, rescale to the closest valid width value
+ if (height != width || !validHeight || !validWidth) {
+ dimension = findClosestDimension(height, width, validDimensions);
+ // System.out.println(containerName + " | " + dimension);
+ FileImageUtils.resizeImageWrite(containerFile, containerFile, dimension, dimension, "png");
+ }
+ content = content.replaceAll("\"\\$" + containerName + "_resolution\": \"256x\",", "\"\\$" + containerName + "_resolution\"\\: \"" + dimension + "x\",");
+ }
+ }
+ // once we have updated the content we can write its new data to the file
+ Files.writeString(global_variables, content, charset);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static int findClosestDimension(int height, int width, int[] validDimensions) {
+ int averageDimension = (height + width) / 2;
+ int closestDimension = validDimensions[0];
+ int closestDifference = Math.abs(validDimensions[0] - averageDimension);
+
+ for (int dimension : validDimensions) {
+ int difference = Math.abs(dimension - averageDimension);
+ if (difference < closestDifference) {
+ closestDimension = dimension;
+ closestDifference = difference;
+ }
+ }
+
+ return closestDimension;
+ }
+
+ // compresses to an mcpack file
+ private static void finalizePort(String packPath) {
+ try {
+ File pack = new File(packPath);
+ ZipUtil.LightZip(pack.getAbsolutePath()); // this copies the file to a zip
+ File zippedPack = new File(packPath + ".zip");
+ File mcpack = new File(packPath + ".mcpack");
+ zippedPack.renameTo(mcpack);
+ System.out.println(" Successfully Ported Pack: \n " + mcpack);
+ } catch (Exception e) {
+ System.out.println("failed in final port stage clean up");
+ }
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/PortFileProcessor.java b/src/main/java/swim/porter/engine/PortFileProcessor.java
new file mode 100644
index 0000000..9a7f9f1
--- /dev/null
+++ b/src/main/java/swim/porter/engine/PortFileProcessor.java
@@ -0,0 +1,105 @@
+package swim.porter.engine;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import swim.porter.Main;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+
+public class PortFileProcessor {
+
+ final static String fs = File.separator;
+
+ public static String handlePortCommand(String arg) {
+ File pack = null;
+ // Check if arg is a file path
+ File fileCheck = new File(URLDecoder.decode(arg, StandardCharsets.UTF_8));
+ if (fileCheck.exists()) {
+ pack = fileCheck;
+ } else if (arg.contains("mediafire.com")) { // else wise check if arg was a website url
+ boolean isRar = arg.contains(".rar");
+ pack = Downloader.downloadMediafire(arg, isRar);
+ } else if (arg.contains("pvprp.com")) {
+ pack = Downloader.downloadPVPRP(arg);
+ }
+
+ // now port it
+ if (pack != null) {
+ File portedPack = processPort(pack);
+ if (portedPack != null) {
+ return portedPack.getAbsolutePath();
+ }
+ }
+
+ return "failed";
+ }
+
+ // takes in file for pack to port, returns path to the ported pack
+ private static File processPort(File export) {
+ File pack;
+
+ if (!export.isDirectory()) {
+ String ext = FilenameUtils.getExtension(export.getName());
+ String extractionPath = Paths.get(Main.portDir, FilenameUtils.removeExtension(export.getName())).toString();
+ if (ext.equalsIgnoreCase("zip")) {
+ ZipUtil.lightUnzip(export, new File(extractionPath));
+ } else {
+ ZipUtil.SevenExtract(export.getAbsolutePath(), extractionPath);
+ }
+ pack = new File(FilenameUtils.removeExtension(extractionPath));
+ } else {
+ pack = export;
+ }
+
+ // find the pack.mcmeta, the folder it is located is the root dir of the pack
+ File manifest = findManifestFile(pack);
+ if (manifest.exists()) {
+ return Port.port(new File(manifest.getParentFile().getAbsolutePath()));
+ } else {
+ System.out.println("Error: uploaded pack does not contain pack.mcmeta");
+ }
+
+ return null;
+ }
+
+
+ // iteratively searches the pack for the mcmeta file
+ private static File findManifestFile(File dir) {
+ File manifest = new File(dir, "pack.mcmeta");
+ if (manifest.exists()) {
+ return manifest;
+ }
+ File[] files = dir.listFiles();
+ assert files != null;
+ for (File file : files) {
+ if (file.isDirectory()) {
+ manifest = findManifestFile(file);
+ if (manifest.exists()) {
+ return manifest;
+ }
+ }
+ }
+ return manifest;
+ }
+
+ // delete everything in the pack dir except for the assets folder
+ public static void cleanPackDir() {
+ File swimServicesDir = new File(Main.portDir);
+ File[] swimFiles = swimServicesDir.listFiles();
+ assert swimFiles != null;
+ for (File file : swimFiles) {
+ if (!file.getName().equalsIgnoreCase("assets")) {
+ try {
+ FileUtils.forceDelete(file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/Recolor.java b/src/main/java/swim/porter/engine/Recolor.java
new file mode 100644
index 0000000..bbdfc32
--- /dev/null
+++ b/src/main/java/swim/porter/engine/Recolor.java
@@ -0,0 +1,24 @@
+package swim.porter.engine;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+public class Recolor {
+
+ // tints a BufferedImage to a color
+ public static BufferedImage tint(BufferedImage image, Color color) {
+ for (int x = 0; x < image.getWidth(); x++) {
+ for (int y = 0; y < image.getHeight(); y++) {
+ Color pixelColor = new Color(image.getRGB(x, y), true);
+ int r = (pixelColor.getRed() + color.getRed()) / 2;
+ int g = (pixelColor.getGreen() + color.getGreen()) / 2;
+ int b = (pixelColor.getBlue() + color.getBlue()) / 2;
+ int a = pixelColor.getAlpha();
+ int rgba = (a << 24) | (r << 16) | (g << 8) | b;
+ image.setRGB(x, y, rgba);
+ }
+ }
+ return image;
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/Rescale.java b/src/main/java/swim/porter/engine/Rescale.java
new file mode 100644
index 0000000..a5daead
--- /dev/null
+++ b/src/main/java/swim/porter/engine/Rescale.java
@@ -0,0 +1,20 @@
+package swim.porter.engine;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
+
+public class Rescale {
+
+ // this is just the cheapest and easiest method to implement for quick rescaling, if users really want more customization they can download the software
+ public static BufferedImage nearestNeighborRescale(BufferedImage originalImage, int targetWidth, int targetHeight) {
+ BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, TYPE_INT_ARGB);
+ Graphics2D g = resizedImage.createGraphics();
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+ g.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null);
+ g.dispose();
+ return resizedImage;
+ }
+
+}
diff --git a/src/main/java/swim/porter/engine/ZipUtil.java b/src/main/java/swim/porter/engine/ZipUtil.java
new file mode 100644
index 0000000..d9f4429
--- /dev/null
+++ b/src/main/java/swim/porter/engine/ZipUtil.java
@@ -0,0 +1,123 @@
+package swim.porter.engine;
+
+import net.sf.sevenzipjbinding.ExtractOperationResult;
+import net.sf.sevenzipjbinding.IInArchive;
+import net.sf.sevenzipjbinding.SevenZip;
+import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
+import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+
+import java.io.*;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class ZipUtil {
+
+ // lightweight method for zipping a directory relatively quick
+ // not sure if using 7zip binding would have any benefit beyond speed, which would be worth it if that is the case
+ public static void LightZip(String dirPath) {
+ final Path sourceDir = Paths.get(dirPath);
+ String zipFileName = dirPath.concat(".zip");
+ try {
+ final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFileName));
+ Files.walkFileTree(sourceDir, new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
+ try {
+ Path targetFile = sourceDir.relativize(file);
+ outputStream.putNextEntry(new ZipEntry(targetFile.toString()));
+ byte[] bytes = Files.readAllBytes(file);
+ outputStream.write(bytes, 0, bytes.length);
+ outputStream.closeEntry();
+ } catch (IOException e) {
+ System.out.println("file compression error");
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ outputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // a lightweight unzip method that ignores null data
+ public static void lightUnzip(File zipFile, File outputDir) {
+ try (ZipFile zf = new ZipFile(zipFile)) {
+ Enumeration entries = zf.getEntries();
+ while (entries.hasMoreElements()) {
+ ZipArchiveEntry entry = entries.nextElement();
+ try {
+ File outputFile = new File(outputDir, entry.getName());
+ if (entry.isDirectory()) {
+ Files.createDirectories(Paths.get(outputFile.getAbsolutePath()));
+ } else {
+ Files.createDirectories(Paths.get(outputFile.getParent()));
+ try (InputStream inputStream = zf.getInputStream(entry); FileOutputStream outputStream = new FileOutputStream(outputFile)) {
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error processing entry: " + entry.getName() + ". Skipping it.");
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // we use 7zip binding to extract any type of archive possible, but using lightUnzip is a lot faster for normal zip archives
+ public static void SevenExtract(String archiveFilePath, String outputDirectory) {
+ try {
+ RandomAccessFile randomAccessFile = new RandomAccessFile(archiveFilePath, "r");
+ RandomAccessFileInStream randomAccessFileStream = new RandomAccessFileInStream(randomAccessFile);
+ IInArchive inArchive = SevenZip.openInArchive(null, randomAccessFileStream);
+ try {
+ for (ISimpleInArchiveItem item : inArchive.getSimpleInterface().getArchiveItems()) {
+ if (!item.isFolder()) {
+ try {
+ File outputFile = new File(outputDirectory, item.getPath());
+ if (!outputFile.isDirectory()) {
+ Files.createDirectories(Paths.get(outputFile.getParent()));
+ try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
+ ExtractOperationResult result = item.extractSlow(data -> {
+ if (data == null) {
+ return 0;
+ }
+ try {
+ outputStream.write(data);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return data.length;
+ });
+ if (result != ExtractOperationResult.OK) {
+ System.err.println(String.format("Error extracting archive item %s. Extracting error: %s", item.getPath(), result));
+ }
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("Error processing item: " + item.getPath() + ". Skipping it.");
+ e.printStackTrace();
+ }
+ }
+ }
+ } finally {
+ inArchive.close();
+ randomAccessFileStream.close();
+ randomAccessFile.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/resources/assets.zip b/src/main/resources/assets.zip
new file mode 100644
index 0000000..f719a68
Binary files /dev/null and b/src/main/resources/assets.zip differ