├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── kotlinc.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── PAL_LAUNCHER └── PAL_Launcher.cs ├── PoE-Addon-Launcher-Core.iml ├── README.md ├── pom.xml └── src ├── main ├── java │ ├── Data │ │ └── PALsettings.java │ └── GUI │ │ └── PopUp │ │ ├── Updated_HTML_Popup.java │ │ └── Updated_HTML_Popup_Controller.java ├── kotlin │ └── PAL2 │ │ ├── Addons │ │ └── Externals.kt │ │ ├── Database │ │ └── DataBaseHandler.kt │ │ ├── Filters │ │ ├── FilterBlast.kt │ │ ├── FilterContainer.kt │ │ └── FilterDownloader.kt │ │ ├── FunStuff │ │ └── FunStuff.kt │ │ ├── GUI │ │ ├── AddonAnchor.kt │ │ ├── Configurator │ │ │ └── Configurator.kt │ │ ├── CoreApplication.kt │ │ ├── CoreController.kt │ │ ├── CustomAddonAnchor.kt │ │ ├── DownloadsAnchor.kt │ │ ├── FilterAnchor.kt │ │ ├── InstalledAnchor.kt │ │ ├── LightningEffects.kt │ │ ├── Loader │ │ │ └── Loader.kt │ │ └── UtilityAnchor.kt │ │ ├── Github │ │ ├── GH_Connector.kt │ │ ├── ReadMeConverter.kt │ │ └── UpdateGrabber.kt │ │ ├── GlobalData.kt │ │ ├── Launcher.kt │ │ ├── PAL_DataClasses │ │ └── PALData.kt │ │ └── SystemHandling │ │ ├── AddonRemover.kt │ │ ├── AddonUpdater.kt │ │ ├── FileHandling.kt │ │ ├── Init.kt │ │ ├── InstallHandler.kt │ │ ├── LaunchHandling.kt │ │ └── WebHandling.kt └── resources │ ├── Configurator.fxml │ ├── CoreUI.fxml │ ├── FiltersTab.fxml │ ├── Loader.fxml │ ├── Settings.fxml │ ├── bk │ ├── CoreUI.bk │ └── CoreUIPREPAL2.bk │ ├── config.fxml │ ├── downloadAsker.fxml │ ├── fxml_previews │ ├── addonDisplay.fxml │ ├── downloadsAnchor.fxml │ └── installedList.fxml │ ├── icons │ ├── NoIcon.png │ ├── Web.pdn │ ├── Web.png │ ├── ahk.png │ ├── cancel.png │ ├── cancel0.png │ ├── colors │ ├── cross.png │ ├── down.png │ ├── downArrow.pdn │ ├── downArrow.png │ ├── down_hl.png │ ├── download.png │ ├── download_all.pdn │ ├── download_all.png │ ├── download_darker.png │ ├── edit.png │ ├── emptyStar.png │ ├── filledStar.png │ ├── gggAprove.png │ ├── gggAproveQ.png │ ├── gggMark.pdn │ ├── info.png │ ├── main_quest_item.png │ ├── minimize.png │ ├── minimize_hl.png │ ├── minus.png │ ├── part.png │ ├── play.png │ ├── q.png │ ├── refresh_icon.png │ ├── remove.png │ ├── respecbook.png │ ├── settings.png │ ├── settings_hl.png │ ├── skillbook.png │ ├── star25.png │ ├── star50.png │ ├── star75.png │ ├── thumbsDown.png │ ├── thumbsUp.png │ ├── trash.png │ ├── trayIcon.png │ ├── trial.png │ ├── upArrow.pdn │ ├── upArrow.png │ └── waypoint.png │ ├── legion.png │ ├── log4j2.xml │ ├── new_in_this_release.fxml │ ├── overlay │ ├── GridDisplay.fxml │ └── WebAddonUI.fxml │ ├── overlayidea.png │ ├── popup.fxml │ ├── popup_import.fxml │ ├── settings │ ├── Twitch_icon.png │ ├── YouTube-icon.png │ ├── discord.jpg │ ├── discord.png │ ├── github-512.png │ ├── patreon.png │ ├── patreon_small.png │ ├── reddit.png │ ├── twitchsmall.png │ ├── twitter.png │ ├── twittersmaller.png │ └── utubesmall.png │ ├── test.fxml │ ├── tray │ ├── animation.gif │ ├── baseTray.png │ └── paused.png │ ├── update_notes.fxml │ ├── update_notes.html │ ├── updatepls.fxml │ └── witch.png └── test └── kotlin └── PAL2 ├── Filters └── FilterBlastTest.kt └── Github └── ReadMeConverterTest.kt /.gitignore: -------------------------------------------------------------------------------- 1 | src/main/kotlin/PAL2/Client/* 2 | 3 | #Ignore CSS files 4 | src/main/resources/*.css 5 | 6 | # Compiled class file 7 | *.class 8 | 9 | # Log file 10 | *.log 11 | 12 | # BlueJ files 13 | *.ctxt 14 | 15 | # Mobile Tools for Java (J2ME) 16 | .mtj.tmp/ 17 | 18 | # Package Files # 19 | *.jar 20 | *.war 21 | *.nar 22 | *.ear 23 | *.zip 24 | *.tar.gz 25 | *.rar 26 | 27 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 28 | hs_err_pid* 29 | 30 | 31 | # Created by https://www.gitignore.io/api/java,maven,intellij 32 | 33 | ### Intellij ### 34 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 35 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 36 | 37 | # User-specific stuff 38 | .idea/**/workspace.xml 39 | .idea/**/tasks.xml 40 | .idea/**/usage.statistics.xml 41 | .idea/**/dictionaries 42 | .idea/**/shelf 43 | 44 | # Generated files 45 | .idea/**/contentModel.xml 46 | 47 | # Sensitive or high-churn files 48 | .idea/**/dataSources/ 49 | .idea/**/dataSources.ids 50 | .idea/**/dataSources.local.xml 51 | .idea/**/sqlDataSources.xml 52 | .idea/**/dynamic.xml 53 | .idea/**/uiDesigner.xml 54 | .idea/**/dbnavigator.xml 55 | 56 | # Gradle 57 | .idea/**/gradle.xml 58 | .idea/**/libraries 59 | 60 | # Gradle and Maven with auto-import 61 | # When using Gradle or Maven with auto-import, you should exclude module files, 62 | # since they will be recreated, and may cause churn. Uncomment if using 63 | # auto-import. 64 | # .idea/modules.xml 65 | # .idea/*.iml 66 | # .idea/modules 67 | 68 | # CMake 69 | cmake-build-*/ 70 | 71 | # Mongo Explorer plugin 72 | .idea/**/mongoSettings.xml 73 | 74 | # File-based project format 75 | *.iws 76 | 77 | # IntelliJ 78 | out/ 79 | 80 | # mpeltonen/sbt-idea plugin 81 | .idea_modules/ 82 | 83 | # JIRA plugin 84 | atlassian-ide-plugin.xml 85 | 86 | # Cursive Clojure plugin 87 | .idea/replstate.xml 88 | 89 | # Crashlytics plugin (for Android Studio and IntelliJ) 90 | com_crashlytics_export_strings.xml 91 | crashlytics.properties 92 | crashlytics-build.properties 93 | fabric.properties 94 | 95 | # Editor-based Rest Client 96 | .idea/httpRequests 97 | 98 | ### Intellij Patch ### 99 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 100 | 101 | # *.iml 102 | # modules.xml 103 | # .idea/misc.xml 104 | # *.ipr 105 | 106 | # Sonarlint plugin 107 | .idea/sonarlint 108 | 109 | ### Java ### 110 | # Compiled class file 111 | *.class 112 | 113 | # Log file 114 | *.log 115 | 116 | # BlueJ files 117 | *.ctxt 118 | 119 | # Mobile Tools for Java (J2ME) 120 | .mtj.tmp/ 121 | 122 | # Package Files # 123 | *.jar 124 | *.war 125 | *.nar 126 | *.ear 127 | *.zip 128 | *.tar.gz 129 | *.rar 130 | 131 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 132 | hs_err_pid* 133 | 134 | ### Maven ### 135 | target/ 136 | pom.xml.tag 137 | pom.xml.releaseBackup 138 | pom.xml.versionsBackup 139 | pom.xml.next 140 | release.properties 141 | dependency-reduced-pom.xml 142 | buildNumber.properties 143 | .mvn/timing.properties 144 | .mvn/wrapper/maven-wrapper.jar 145 | 146 | 147 | # End of https://www.gitignore.io/api/java,maven,intellij 148 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | PoE-Addon-Launcher-Core -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PAL_LAUNCHER/PAL_Launcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Threading; 8 | using System.Runtime.InteropServices; 9 | using System.Net; 10 | using System.Windows; 11 | using System.IO; 12 | using System.IO.Compression; 13 | 14 | namespace PAL_Launcher 15 | { 16 | class Program 17 | { 18 | [DllImport("kernel32.dll")] 19 | static extern IntPtr GetConsoleWindow(); 20 | 21 | [DllImport("user32.dll")] 22 | static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); 23 | 24 | const int SW_HIDE = 0; 25 | const int SW_SHOW = 5; 26 | 27 | static void Main(string[] args) 28 | { 29 | // Check if the folder "new" exists 30 | // If new exists replace the current JAR with the one in "new" 31 | 32 | applyUpdate(); 33 | 34 | 35 | Console.WriteLine("==========================="); 36 | Console.WriteLine("Welcome to the PAL Launcher"); 37 | Console.WriteLine("==========================="); 38 | Console.WriteLine(""); 39 | Console.WriteLine("Preparing to launch PAL2\n"); 40 | 41 | Console.WriteLine("Detecting PAL2..."); 42 | if (checkPAL()) 43 | { 44 | Console.WriteLine("PAL2 found! Attempting to launch..."); 45 | } 46 | else 47 | { 48 | Console.WriteLine("PAL2 not found!"); 49 | Console.WriteLine("Downloading PAL2.jar!"); 50 | FileDownloader.DownloadFile("https://github.com/POE-Addon-Launcher/PALRelease/raw/master/PAL2.jar", Path.Combine(Environment.CurrentDirectory, "PAL2.jar"), int.MaxValue); 51 | } 52 | 53 | Console.WriteLine("Searching for Java..."); 54 | 55 | if (checkJava()) 56 | { 57 | Thread t = new Thread(() => 58 | { 59 | var handle = GetConsoleWindow(); 60 | Console.WriteLine("Java found! Launching PAL2"); 61 | Process process = new Process(); 62 | process.StartInfo.FileName = "cmd.exe"; 63 | process.StartInfo.Arguments = "/c java -jar PAL2.jar"; 64 | process.StartInfo.UseShellExecute = false; 65 | process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 66 | ShowWindow(handle, SW_HIDE); 67 | process.Start(); 68 | }); 69 | t.Start(); 70 | } 71 | else 72 | { 73 | // if ojdk folder exists, launch instead! 74 | string cdir = Path.Combine(Environment.CurrentDirectory, "jre"); 75 | if (Directory.Exists(cdir)) 76 | { 77 | Console.WriteLine("Launching PAL2"); 78 | launchPAL2(); 79 | } 80 | else 81 | { 82 | Console.Clear(); 83 | Console.WriteLine("Java not found, downloading the Java Runtime..."); 84 | 85 | string jreZip = Path.Combine(Environment.CurrentDirectory, "jre.zip"); 86 | 87 | var success = FileDownloader.DownloadFile("https://github.com/POE-Addon-Launcher/PALRelease/releases/download/jre/jre.zip", jreZip, int.MaxValue); 88 | 89 | if (success) 90 | { 91 | ZipStorer zip = ZipStorer.Open(jreZip, FileAccess.Read); 92 | List files = zip.ReadCentralDir(); 93 | 94 | Console.WriteLine("Extracting Zip Archive this may take some time...\nDO NOT CLOSE THE PROGRAM IT IS NOT STUCK!"); 95 | 96 | foreach (ZipStorer.ZipFileEntry entry in files) 97 | { 98 | zip.ExtractFile(entry, Path.Combine(Environment.CurrentDirectory, entry.FilenameInZip)); 99 | }; 100 | zip.Close(); 101 | Console.WriteLine("Deleting Archive..."); 102 | // TODO Delete zip 103 | if (File.Exists(jreZip)) 104 | { 105 | File.Delete(jreZip); 106 | } 107 | 108 | Console.WriteLine("Extracting Completed, launching PAL2"); 109 | launchPAL2(); 110 | } 111 | else 112 | { 113 | Console.WriteLine("Download failed for some reason! Download java yourself instead."); 114 | } 115 | } 116 | } 117 | 118 | } 119 | 120 | static void launchPAL2() 121 | { 122 | Thread t = new Thread(() => 123 | { 124 | Console.WriteLine("Launching PAL2"); 125 | var javaEXE = Path.Combine(Environment.CurrentDirectory, @"jre\bin\java.exe"); 126 | string cmd = "/c "; 127 | cmd += javaEXE; 128 | cmd += " -jar PAL2.jar"; 129 | var handle = GetConsoleWindow(); 130 | Process process = new Process(); 131 | process.StartInfo.FileName = "cmd.exe"; 132 | process.StartInfo.Arguments = cmd; 133 | process.StartInfo.UseShellExecute = false; 134 | process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 135 | ShowWindow(handle, SW_HIDE); 136 | process.Start(); 137 | }); 138 | t.Start(); 139 | } 140 | 141 | static void killJava() 142 | { 143 | Process.Start("taskkill", "/F /IM java.exe"); 144 | Process.Start("taskkill", "/F /IM javaw.exe"); 145 | Console.WriteLine("Waiting for java to exit..."); 146 | Thread.Sleep(3000); 147 | } 148 | 149 | /** 150 | * Overwrites PAL2.jar with the one in the "new" folder. 151 | */ 152 | static void applyUpdate() 153 | { 154 | var newest = Path.Combine(@Environment.CurrentDirectory, @"new\PAL2.jar"); 155 | if (File.Exists(newest)) 156 | { 157 | // Taskkill java & javaw 158 | killJava(); 159 | 160 | string pal2jar = Path.Combine(Environment.CurrentDirectory, "PAL2.jar"); 161 | if (checkPAL()) 162 | { 163 | File.Delete(pal2jar); 164 | } 165 | File.Move(newest, pal2jar); 166 | 167 | string pal2log = Path.Combine(Environment.CurrentDirectory, @"new\PAL_Logger.log"); 168 | if (File.Exists(pal2log)) 169 | { 170 | File.Delete(pal2log); 171 | } 172 | 173 | 174 | string dir = Path.Combine(Environment.CurrentDirectory, @"new"); 175 | if (Directory.Exists(dir)) 176 | { 177 | Directory.Delete(dir); 178 | } 179 | } 180 | } 181 | 182 | /** 183 | * Checks if PAL2.jar is in our root directory. 184 | */ 185 | static bool checkPAL() 186 | { 187 | string pal2jar = Path.Combine(Environment.CurrentDirectory, "PAL2.jar"); 188 | return File.Exists(pal2jar); 189 | } 190 | 191 | static Process process = new Process(); 192 | static bool foundJava = false; 193 | 194 | /** 195 | * Checks if Java is installed. 196 | */ 197 | static bool checkJava() 198 | { 199 | process.EnableRaisingEvents = true; 200 | process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived); 201 | process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived); 202 | process.Exited += new EventHandler(process_Exited); 203 | 204 | process.StartInfo.FileName = "cmd.exe"; 205 | process.StartInfo.Arguments = "/c java -version"; 206 | process.StartInfo.UseShellExecute = false; 207 | process.StartInfo.RedirectStandardError = true; 208 | process.StartInfo.RedirectStandardOutput = true; 209 | 210 | process.Start(); 211 | process.BeginErrorReadLine(); 212 | process.BeginOutputReadLine(); 213 | 214 | process.WaitForExit(); 215 | 216 | return foundJava; 217 | } 218 | 219 | static void process_Exited(object sender, EventArgs e) 220 | { 221 | //Console.WriteLine(string.Format("process exited with code {0}\n", process.ExitCode.ToString())); 222 | } 223 | 224 | static void process_ErrorDataReceived(object sender, DataReceivedEventArgs e) 225 | { 226 | if (e.Data != null) 227 | { 228 | if (e.Data.Contains("java version")) 229 | foundJava = true; 230 | } 231 | 232 | } 233 | 234 | static void process_OutputDataReceived(object sender, DataReceivedEventArgs e) 235 | { 236 | //Console.WriteLine("Output: " + e.Data + "\n"); 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /PoE-Addon-Launcher-Core.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PoE Addon Launcher 2 (PAL2) 2 | 3 | ![](https://i.imgur.com/QvZYFkM.png "") 4 | 5 | # HOW DO I DOWNLOAD THIS? 6 | # Go to the releases tab or click on this useful link: [v1.12.2](https://github.com/POE-Addon-Launcher/PAL2/releases/download/1.12.2/PAL2.exe) 7 | 8 | # HOW DO I USE THIS? 9 | # [User-Guide v1.11](https://docs.google.com/document/d/1iV6BpndAaENhWc6hcCYI3hINjxi1FAa8bdbeSUXrKFo/edit?usp=sharing) 10 | 11 | 12 | PAL2 is PAL but completely rewritten from the ground up in Kotlin, it also no longer requires you to install the java runtime for it to be used. 13 | 14 | It's to be used to eliminate manual tracking of programs and their updates, and also launches all your addons prior to launching Path of Exile! 15 | 16 | Stay up to date with all your favourite addons! 17 | 18 | ![](https://i.imgur.com/y5G4eJf.png "") 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | RizlimDev 8 | PoE-Addon-Launcher-Core 9 | 1.12 10 | 11 | 12 | 1.3.0 13 | 14 | 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-jar-plugin 21 | 3.0.2 22 | 23 | 24 | 25 | true 26 | Launcher2 27 | 28 | 29 | . 30 | 31 | 32 | 33 | 34 | 35 | maven-assembly-plugin 36 | 37 | 38 | 39 | true 40 | Launcher2 41 | 42 | 43 | . 44 | 45 | 46 | 47 | jar-with-dependencies 48 | 49 | 50 | 51 | 52 | org.jetbrains.kotlin 53 | kotlin-maven-plugin 54 | ${kotlin.version} 55 | 56 | 57 | compile 58 | compile 59 | 60 | compile 61 | 62 | 63 | 64 | test-compile 65 | test-compile 66 | 67 | test-compile 68 | 69 | 70 | 71 | 72 | 1.8 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-compiler-plugin 78 | 3.8.0 79 | 80 | 81 | compile 82 | compile 83 | 84 | compile 85 | 86 | 87 | 88 | testCompile 89 | test-compile 90 | 91 | testCompile 92 | 93 | 94 | 95 | 96 | 8 97 | 8 98 | 99 | 100 | 101 | org.jetbrains.kotlin 102 | kotlin-maven-plugin 103 | ${kotlin.version} 104 | 105 | 106 | compile 107 | process-sources 108 | 109 | compile 110 | 111 | 112 | 113 | src/main/java 114 | src/main/kotlin 115 | src/main/resources 116 | 117 | 118 | 119 | 120 | test-compile 121 | process-test-sources 122 | 123 | test-compile 124 | 125 | 126 | 127 | src/test/java 128 | src/test/kotlin 129 | src/test/resources 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | com.atlassian.commonmark 141 | commonmark 142 | 0.12.1 143 | 144 | 145 | com.fasterxml.jackson.core 146 | jackson-databind 147 | 2.9.10.1 148 | 149 | 150 | com.fasterxml.jackson.module 151 | jackson-module-kotlin 152 | 2.9.4.1 153 | 154 | 155 | junit 156 | junit 157 | 4.12 158 | 159 | 160 | net.lingala.zip4j 161 | zip4j 162 | 1.3.2 163 | 164 | 165 | org.kohsuke 166 | github-api 167 | 1.93 168 | 169 | 170 | org.jetbrains.kotlin 171 | kotlin-stdlib-jdk8 172 | ${kotlin.version} 173 | 174 | 175 | org.jetbrains.kotlin 176 | kotlin-test-junit 177 | ${kotlin.version} 178 | test 179 | 180 | 181 | org.jetbrains.kotlinx 182 | kotlinx-coroutines-core 183 | 1.0.1 184 | 185 | 186 | org.jetbrains.kotlinx 187 | kotlinx-coroutines-javafx 188 | 1.0.1 189 | 190 | 191 | org.jetbrains.kotlin 192 | kotlin-stdlib-jdk8 193 | ${kotlin.version} 194 | 195 | 196 | org.jetbrains.kotlin 197 | kotlin-test 198 | ${kotlin.version} 199 | test 200 | 201 | 202 | org.jetbrains.kotlin 203 | kotlin-reflect 204 | 1.3.0 205 | 206 | 207 | com.fasterxml.jackson.datatype 208 | jackson-datatype-jsr310 209 | 2.9.9 210 | 211 | 212 | io.github.microutils 213 | kotlin-logging 214 | 1.6.22 215 | 216 | 217 | org.slf4j 218 | slf4j-api 219 | 1.7.25 220 | 221 | 222 | org.apache.logging.log4j 223 | log4j-slf4j-impl 224 | 2.9.1 225 | 226 | 227 | org.apache.logging.log4j 228 | log4j-api 229 | 2.9.1 230 | 231 | 232 | org.apache.logging.log4j 233 | log4j-core 234 | 2.9.1 235 | 236 | 237 | org.xerial 238 | sqlite-jdbc 239 | 3.25.2 240 | 241 | 242 | commons-io 243 | commons-io 244 | 2.6 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /src/main/java/Data/PALsettings.java: -------------------------------------------------------------------------------- 1 | package Data; 2 | 3 | /** 4 | * 5 | */ 6 | public class PALsettings 7 | { 8 | private boolean wait_for_updates = false; 9 | private String loot_filter_dir = ""; 10 | private boolean github_api_enabled = true; 11 | private String pref_version = ""; 12 | private boolean filterblast_api = true; 13 | private boolean down_on_launch = false; 14 | private String AHK_Folder = ""; 15 | private String github_token = ""; 16 | private boolean github_api_token_enabled = false; 17 | private boolean run_poe_on_launch = false; 18 | 19 | private PALsettings() 20 | {} 21 | 22 | public PALsettings(boolean wait_for_updates, String loot_filter_dir, boolean github_api_enabled, String pref_version, boolean filterblast_api, boolean down_on_launch, String AHK_Folder, String github_token, boolean github_api_token_enabled, boolean run_poe_on_launch) 23 | { 24 | this.wait_for_updates = wait_for_updates; 25 | this.loot_filter_dir = loot_filter_dir; 26 | this.github_api_enabled = github_api_enabled; 27 | this.pref_version = pref_version; 28 | this.filterblast_api = filterblast_api; 29 | this.down_on_launch = down_on_launch; 30 | this.AHK_Folder = AHK_Folder; 31 | this.github_token = github_token; 32 | this.github_api_token_enabled = github_api_token_enabled; 33 | this.run_poe_on_launch = run_poe_on_launch; 34 | } 35 | 36 | public boolean isWait_for_updates() 37 | { 38 | return wait_for_updates; 39 | } 40 | 41 | public void setWait_for_updates(boolean wait_for_updates) 42 | { 43 | this.wait_for_updates = wait_for_updates; 44 | } 45 | 46 | public String getLoot_filter_dir() 47 | { 48 | return loot_filter_dir; 49 | } 50 | 51 | public void setLoot_filter_dir(String loot_filter_dir) 52 | { 53 | this.loot_filter_dir = loot_filter_dir; 54 | } 55 | 56 | public boolean isGithub_api_enabled() 57 | { 58 | return github_api_enabled; 59 | } 60 | 61 | public void setGithub_api_enabled(boolean github_api_enabled) 62 | { 63 | this.github_api_enabled = github_api_enabled; 64 | } 65 | 66 | public String getPref_version() 67 | { 68 | return pref_version; 69 | } 70 | 71 | public void setPref_version(String pref_version) 72 | { 73 | this.pref_version = pref_version; 74 | } 75 | 76 | public boolean isFilterblast_api() 77 | { 78 | return filterblast_api; 79 | } 80 | 81 | public void setFilterblast_api(boolean filterblast_api) 82 | { 83 | this.filterblast_api = filterblast_api; 84 | } 85 | 86 | public boolean isDown_on_launch() 87 | { 88 | return down_on_launch; 89 | } 90 | 91 | public void setDown_on_launch(boolean down_on_launch) 92 | { 93 | this.down_on_launch = down_on_launch; 94 | } 95 | 96 | public String getAHK_Folder() 97 | { 98 | return AHK_Folder; 99 | } 100 | 101 | public void setAHK_Folder(String AHK_Folder) 102 | { 103 | this.AHK_Folder = AHK_Folder; 104 | } 105 | 106 | public String getGithub_token() 107 | { 108 | return github_token; 109 | } 110 | 111 | public void setGithub_token(String github_token) 112 | { 113 | this.github_token = github_token; 114 | } 115 | 116 | public boolean isGithub_api_token_enabled() 117 | { 118 | return github_api_token_enabled; 119 | } 120 | 121 | public void setGithub_api_token_enabled(boolean github_api_token_enabled) 122 | { 123 | this.github_api_token_enabled = github_api_token_enabled; 124 | } 125 | 126 | public boolean isRun_poe_on_launch() 127 | { 128 | return run_poe_on_launch; 129 | } 130 | 131 | public void setRun_poe_on_launch(boolean run_poe_on_launch) 132 | { 133 | this.run_poe_on_launch = run_poe_on_launch; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/GUI/PopUp/Updated_HTML_Popup.java: -------------------------------------------------------------------------------- 1 | package GUI.PopUp; 2 | 3 | import javafx.application.Application; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.scene.Parent; 6 | import javafx.scene.Scene; 7 | import javafx.scene.image.Image; 8 | import javafx.stage.Stage; 9 | import javafx.stage.StageStyle; 10 | 11 | /** 12 | * 13 | */ 14 | public class Updated_HTML_Popup 15 | extends Application 16 | { 17 | public static Stage stage; 18 | 19 | public void start(Stage primaryStage) throws Exception 20 | { 21 | primaryStage = new Stage(); 22 | FXMLLoader fxmlLoader = new FXMLLoader(); 23 | primaryStage.initStyle(StageStyle.UNDECORATED); 24 | primaryStage.setAlwaysOnTop(true); 25 | Parent root = fxmlLoader.load(getClass().getResource("/update_notes.fxml")); 26 | primaryStage.setTitle("PAL: NEW!"); 27 | primaryStage.getIcons().add(new Image(getClass().getResource("/witch.png").toString())); 28 | Scene scene = new Scene(root, 499, 300); 29 | primaryStage.setScene(scene); 30 | stage = primaryStage; 31 | primaryStage.show(); 32 | } 33 | 34 | public void activate(String[] args) 35 | { 36 | launch(args); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/GUI/PopUp/Updated_HTML_Popup_Controller.java: -------------------------------------------------------------------------------- 1 | package GUI.PopUp; 2 | 3 | import javafx.application.Platform; 4 | import javafx.fxml.FXML; 5 | import javafx.fxml.Initializable; 6 | import javafx.scene.image.Image; 7 | import javafx.scene.image.ImageView; 8 | import javafx.scene.input.MouseEvent; 9 | import javafx.scene.web.WebView; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.net.MalformedURLException; 15 | import java.net.URL; 16 | import java.nio.file.Files; 17 | import java.nio.file.Paths; 18 | import java.util.ResourceBundle; 19 | 20 | public class Updated_HTML_Popup_Controller 21 | implements Initializable 22 | { 23 | @FXML 24 | private ImageView topbar_image_closeWindow; 25 | 26 | @FXML 27 | private WebView HTML_Viewer; 28 | 29 | 30 | private double xOffset = 0; 31 | private double yOffset = 0; 32 | 33 | public void onMouseDragged(MouseEvent mouseEvent) 34 | { 35 | Updated_HTML_Popup.stage.setX(mouseEvent.getScreenX() + xOffset); 36 | Updated_HTML_Popup.stage.setY(mouseEvent.getScreenY() + yOffset); 37 | } 38 | public void onMousePressed(MouseEvent mouseEvent) 39 | { 40 | xOffset = Updated_HTML_Popup.stage.getX() - mouseEvent.getScreenX(); 41 | yOffset = Updated_HTML_Popup.stage.getY() - mouseEvent.getScreenY(); 42 | } 43 | 44 | public void topbar_closeWIndow_onMouseClicked() 45 | { 46 | Updated_HTML_Popup.stage.close(); 47 | } 48 | 49 | public void topbar_closeWindow_onMouseEntered() 50 | { 51 | changeImage(topbar_image_closeWindow, "/icons/cancel.png"); 52 | } 53 | 54 | public void topbar_closeWindow_onMouseExited() 55 | { 56 | changeImage(topbar_image_closeWindow, "/icons/cancel0.png"); 57 | } 58 | 59 | private void changeImage(ImageView imageView, String s) 60 | { 61 | Platform.runLater(() -> imageView.setImage(new Image(getClass().getResource(s).toString()))); 62 | } 63 | 64 | @Override 65 | public void initialize(URL location, ResourceBundle resources) 66 | { 67 | try 68 | { 69 | String folder = System.getenv("LOCALAPPDATA"); 70 | 71 | File f = new File(folder + File.separator + "PAL" + File.separator + "update_notes.html"); 72 | 73 | if (f.exists()) 74 | f.delete(); 75 | 76 | InputStream inputStream = new URL("https://raw.githubusercontent.com/POE-Addon-Launcher/PAL2/master/src/main/resources/update_notes.html").openStream(); 77 | Files.copy(inputStream, Paths.get(f.getPath() )); 78 | Platform.runLater(() -> 79 | { 80 | try 81 | { 82 | HTML_Viewer.getEngine().load(f.toURI().toURL().toString()); 83 | } 84 | catch (MalformedURLException e) 85 | { 86 | e.printStackTrace(); 87 | } 88 | }); 89 | 90 | } 91 | catch (IOException e) 92 | { 93 | e.printStackTrace(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Addons/Externals.kt: -------------------------------------------------------------------------------- 1 | package PAL2.Addons 2 | 3 | import GlobalData 4 | import PAL2.Database.countAHKs 5 | import PAL2.Database.countExternalAddon 6 | import PAL2.Database.getAHKScriptsArray 7 | import PAL2.Database.nukeAHK 8 | import PAL2.GUI.CoreApplication 9 | import PAL2.SystemHandling.FileDownloader 10 | import PAL2.SystemHandling.InstallHandlerHelpers 11 | import PAL_DataClasses.PAL_External_Addon 12 | import SystemHandling.deleteFile 13 | import java.io.File 14 | import java.net.URL 15 | import java.nio.file.Files 16 | import java.nio.file.Paths 17 | import java.util.zip.CRC32 18 | 19 | /** 20 | * 21 | */ 22 | object Externals 23 | { 24 | val LUTBOT_DL = "http://lutbot.com/ahk/macro.ahk" 25 | val SIC_DL = "https://synthesisparser.herokuapp.com/SynthesisParser.ahk" 26 | 27 | fun calcCRC32(bytes: ByteArray): String 28 | { 29 | val hash = CRC32() 30 | hash.update(bytes) 31 | return hash.value.toString(16) 32 | } 33 | 34 | fun calcCRC32(file: File): String 35 | { 36 | val bytes = Files.readAllBytes(Paths.get(file.toURI())) 37 | val hash = CRC32() 38 | hash.update(bytes) 39 | return hash.value.toString(16) 40 | } 41 | 42 | fun determineCMD(file: File): String 43 | { 44 | return when (file.extension.toLowerCase()) 45 | { 46 | "exe" -> InstallHandlerHelpers.createExeLaunchCommandWithElevation(file.path) 47 | "ahk" -> InstallHandlerHelpers.createAHKLaunchCommand(file.path) 48 | "jar" -> InstallHandlerHelpers.createJARLaunchCommand(file.path) 49 | else -> "" 50 | } 51 | } 52 | 53 | // Checks if Externals Table Exists, if it doesn't creates it. 54 | fun determineDBID(): Int 55 | { 56 | return countExternalAddon()+1 57 | } 58 | 59 | fun syncAHKsWithExternals() 60 | { 61 | val ahks = countAHKs() 62 | 63 | if (ahks == 0) 64 | return 65 | 66 | // Retrieve AHKs 67 | val scripts = getAHKScriptsArray() 68 | 69 | for (ahk in scripts) 70 | { 71 | val file = File(ahk.location) 72 | if (file.exists() && !file.isDirectory) 73 | { 74 | val crc32 = Externals.calcCRC32(file) 75 | val cmd = Externals.determineCMD(file) 76 | val dbid = Externals.determineDBID() 77 | val name = file.nameWithoutExtension 78 | val path = file.path 79 | val ea = PAL_External_Addon(dbid, name, crc32, "", "", "", "", cmd, path, ahk.runOnLaunch) 80 | CoreApplication.controller.saveExternal(ea) 81 | 82 | } 83 | } 84 | 85 | // Delete AHKs from DB 86 | nukeAHK() 87 | } 88 | 89 | fun checkForUpdatesAndUpdateExternals() 90 | { 91 | //TODO: This 92 | // Check for updates 93 | 94 | 95 | // If update found back up current file 96 | 97 | 98 | // 99 | } 100 | 101 | /** 102 | * Returns AID for setting installed, matches based on "website_source" 103 | */ 104 | fun isMajorAddon(string: String): Int 105 | { 106 | return when (string) 107 | { 108 | LUTBOT_DL -> 9 109 | SIC_DL -> 17 110 | else -> 0 111 | } 112 | } 113 | 114 | fun isExternal(aid: Int): Boolean 115 | { 116 | return when (aid) 117 | { 118 | 17 -> true // Synthesized-Implicit-Calculator 119 | 9 -> true // Lutbot 120 | else -> false 121 | } 122 | } 123 | 124 | fun addLutBot() 125 | { 126 | if (!GlobalData.addonFolder.exists()) 127 | GlobalData.addonFolder.mkdir() 128 | 129 | var install = GlobalData.addonFolder.path + File.separator + "lutbot" 130 | 131 | if (!File(install).exists()) 132 | File(install).mkdir() 133 | 134 | install += File.separator + "macro.ahk" 135 | 136 | val ea = PAL_External_Addon(-1, "Lutbot", "", "", null, "", LUTBOT_DL, determineCMD(File(install)), install, false) 137 | 138 | // Download 139 | CoreApplication.controller.showDownloadPopup(File(ea.webSource).name) 140 | val location = FileDownloader().downloadFile(URL(ea.webSource), GlobalData.temp_down_folder, 1024, GlobalData.noIcon) 141 | val dest = File(ea.path) 142 | deleteFile(dest) 143 | Files.copy(location.toPath(), dest.toPath()) 144 | ea.checksum = calcCRC32(dest) 145 | 146 | CoreApplication.controller.saveExternal(ea) 147 | } 148 | 149 | fun addSIC() 150 | { 151 | if (!GlobalData.addonFolder.exists()) 152 | GlobalData.addonFolder.mkdir() 153 | 154 | var install = GlobalData.addonFolder.path + File.separator + "Synthesized-Implicit-Calculator" 155 | 156 | if (!File(install).exists()) 157 | File(install).mkdir() 158 | 159 | install += File.separator + "SynthesisParser.ahk" 160 | 161 | val ea = PAL_External_Addon(-1, "Synthesized Implicit Calculator", "", "", null, "", SIC_DL, determineCMD(File(install)), install, false) 162 | 163 | CoreApplication.controller.showDownloadPopup(File(ea.webSource).name) 164 | val location = FileDownloader().downloadFile(URL(ea.webSource), GlobalData.temp_down_folder, 1024, GlobalData.noIcon) 165 | val dest = File(ea.path) 166 | deleteFile(dest) 167 | Files.copy(location.toPath(), dest.toPath()) 168 | ea.checksum = calcCRC32(dest) 169 | 170 | CoreApplication.controller.saveExternal(ea) 171 | } 172 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Filters/FilterBlast.kt: -------------------------------------------------------------------------------- 1 | package PAL2.Filters 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import java.io.IOException 5 | import java.io.InputStream 6 | import java.net.HttpURLConnection 7 | import java.net.URL 8 | import java.util.ArrayList 9 | 10 | /** 11 | * 12 | */ 13 | data class FilterBlastFilter( 14 | val name: String, 15 | val version: String, 16 | val lastUpdate: String, 17 | val poe_version: String, 18 | val forumThread: String, 19 | val variations: ArrayList 20 | ) 21 | { 22 | override fun toString(): String 23 | { 24 | return name 25 | } 26 | } 27 | 28 | data class FilterBlastVariation(val key: String, val fileName: String) 29 | 30 | 31 | object FilterBlast 32 | { 33 | private val FILTER_LIST = "https://filterblast.oversoul.xyz/api/ListFilters/" 34 | 35 | private fun create(input: String): FilterBlastVariation 36 | { 37 | var process = input.replace("{", "") 38 | process = process.replace("}", "") 39 | process = process.replace("\"", "") 40 | val splits = process.split(":") 41 | return FilterBlastVariation(splits[0], splits[1]) 42 | } 43 | 44 | fun downloadListOfFilters(): ArrayList 45 | { 46 | val list = ArrayList() 47 | 48 | val url = URL(FILTER_LIST) 49 | 50 | val httpcon = url.openConnection() as HttpURLConnection 51 | httpcon.addRequestProperty("User-Agent", "Mozilla/4.0") 52 | 53 | var foo = convertStreamToString(httpcon.inputStream) 54 | // Remove hidden character from the start. 55 | foo = findValidString(foo) 56 | 57 | //System.out.println(foo); 58 | 59 | val objectMapper = ObjectMapper() 60 | val node = objectMapper.readTree(foo) 61 | val elements = node.elements() 62 | while (elements.hasNext()) 63 | { 64 | val variations = ArrayList() 65 | val n = elements.next() 66 | val parts = n.get("Presets").toString().split(",") 67 | val arr = ArrayList() 68 | 69 | for (str in parts) 70 | { 71 | arr.add(create(str)) 72 | } 73 | 74 | 75 | val presets = n.get("Presets").elements() 76 | while (presets.hasNext()) 77 | { 78 | val no = presets.next() 79 | variations.add(no.toString().replace("\"", "")) 80 | } 81 | 82 | val f = FilterBlastFilter(findKey(n.get("Name").toString().replace("\"", "")), n.get("Version").toString(), n.get("LastUpdate").toString(), n.get("PoEVersion").toString(), n.get("ForumThread").toString(), arr) 83 | list.add(f) 84 | } 85 | return list 86 | } 87 | 88 | private fun findKey(name: String): String 89 | { 90 | return if (name.split("'")[0] == "Highwind") 91 | "ffhighwind" 92 | else if (name.split("'")[0] == "Lumpa") 93 | "Lumpaa" 94 | else if (name.split("'")[0] == "Dsgreat") 95 | "Dsgreat3" 96 | else if (name.split("'")[0] == "Ment") 97 | "ment2008" 98 | else if (name.split("'")[0] == "Vexi") 99 | "Vexivian" 100 | else if (name == "Sayk Loot Filters") 101 | "Sayk" 102 | else if (name == "PoE Default Filter") 103 | "Default" 104 | else 105 | name.split("'")[0] 106 | } 107 | 108 | /** 109 | * Filterblast has some weird things where it adds some weird characters we're gonna try to attempt to remove them. 110 | * @return 111 | */ 112 | fun findValidString(`in`: String): String 113 | { 114 | var count = 0 115 | for (c in 0 until `in`.length) 116 | { 117 | if (`in`[c] == '{') 118 | { 119 | break 120 | } 121 | else 122 | { 123 | count++ 124 | } 125 | } 126 | return `in`.substring(count) 127 | } 128 | 129 | fun convertStreamToString(`is`: java.io.InputStream): String 130 | { 131 | val s = java.util.Scanner(`is`).useDelimiter("\\A") 132 | return if (s.hasNext()) s.next() else "" 133 | } 134 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Filters/FilterContainer.kt: -------------------------------------------------------------------------------- 1 | package PAL2.Filters 2 | 3 | import PAL2.Addons.Externals 4 | import PAL2.Database.addFilter 5 | import PAL_DataClasses.Filter 6 | import PAL_DataClasses.PAL_External_Addon 7 | import java.io.File 8 | 9 | /** 10 | * 11 | */ 12 | object FilterContainer 13 | { 14 | val filters = ArrayList() 15 | 16 | fun makeAnchorData(f: FilterBlastFilter, location: File, web: String, variation: String): Filter 17 | { 18 | val crc32 = Externals.calcCRC32(location) 19 | val db_filter = addFilter(Filter(-1, f.name, crc32, web, location.path, variation)) 20 | 21 | return db_filter 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Filters/FilterDownloader.kt: -------------------------------------------------------------------------------- 1 | package PAL2.Filters 2 | 3 | import PAL_DataClasses.Filter 4 | import SystemHandling.deleteFile 5 | import mu.KotlinLogging 6 | import java.io.File 7 | import java.net.HttpURLConnection 8 | import java.net.URL 9 | import java.nio.file.Files 10 | 11 | /** 12 | * 13 | */ 14 | private val logger = KotlinLogging.logger {} 15 | 16 | object FilterDownloader 17 | { 18 | val URL_FB_API = "https://filterblast.xyz/api/FilterFile/?filter=!K&preset=!P" 19 | 20 | fun updateFilter(filter: Filter): File? 21 | { 22 | val url = URL(filter.webSource) 23 | val httpConnection = url.openConnection() as HttpURLConnection 24 | httpConnection.addRequestProperty("User-Agent", "Mozilla/4.0") 25 | val f = File(filter.path) 26 | 27 | if (f.exists()) 28 | { 29 | deleteFile(f) 30 | Files.copy(httpConnection.inputStream, f.toPath()) 31 | return f 32 | } 33 | logger.error {"Attempting to update a filter that doesn't exist. ${filter.name}"} 34 | return null 35 | } 36 | 37 | fun downloadFilter(filterVar: String, name: String, filterBlastFilter: FilterBlastFilter): Filter 38 | { 39 | val url_safe = filterVar.replace(" ", "%20") 40 | val sub_url = URL_FB_API.replace("!P", url_safe).replace("!K", name) 41 | val url = URL(sub_url) 42 | val httpConnection = url.openConnection() as HttpURLConnection 43 | httpConnection.addRequestProperty("User-Agent", "Mozilla/4.0") 44 | val f = File(GlobalData.loot_filter_path + File.separator + buildFileName(name, filterVar)) 45 | 46 | if (f.exists()) 47 | deleteFile(f) 48 | 49 | Files.copy(httpConnection.inputStream, f.toPath()) 50 | return FilterContainer.makeAnchorData(filterBlastFilter, f, sub_url, filterVar) 51 | } 52 | 53 | fun buildFileName(name: String, key: String): String 54 | { 55 | return "$name - $key.filter" 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/FunStuff/FunStuff.kt: -------------------------------------------------------------------------------- 1 | package PAL2.FunStuff 2 | 3 | import java.time.LocalDateTime 4 | import java.time.ZoneId 5 | import java.time.ZonedDateTime 6 | import java.time.temporal.ChronoUnit 7 | 8 | /** 9 | * 10 | */ 11 | object FunStuff 12 | { 13 | val europe_release = LocalDateTime.of(2019, 6, 7, 22, 0) 14 | val zdt = ZonedDateTime.of(europe_release, ZoneId.of("UTC+2")) 15 | val local_release = zdt.toLocalDateTime() 16 | 17 | fun legionCountDownTimer() 18 | { 19 | // Add Legion Countdowner if it's not yet at the date of the release! 20 | val now = LocalDateTime.now() 21 | 22 | println("days: ${now.until(local_release, ChronoUnit.DAYS)}") 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/AddonAnchor.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI 2 | 3 | import GlobalData 4 | import PAL2.Addons.Externals 5 | import PAL_DataClasses.PAL_AddonFullData 6 | import SystemHandling.checkForUseableDownloads 7 | import javafx.event.EventHandler 8 | import javafx.scene.image.Image 9 | import javafx.scene.image.ImageView 10 | import javafx.scene.input.MouseButton 11 | import javafx.scene.layout.AnchorPane 12 | import javafx.scene.paint.Color 13 | import javafx.scene.shape.Rectangle 14 | import javafx.scene.text.Text 15 | import javafx.scene.text.TextAlignment 16 | import mu.KotlinLogging 17 | import java.net.URL 18 | 19 | /** 20 | * 21 | */ 22 | private val logger = KotlinLogging.logger {} 23 | 24 | class AddonAnchor(var name: String, var version: String, var repo: String, var iconUrl: String, var addonID: Int) 25 | { 26 | lateinit var anchorPane: AnchorPane 27 | lateinit var anchorButtonRight: AnchorPane 28 | lateinit var anchorButtonLeft: AnchorPane 29 | lateinit var icon: ImageView 30 | lateinit var versionText: Text 31 | lateinit var addonText: Text 32 | var rectButtonRight = Rectangle(75.0, 25.0) 33 | var buttonRightText = Text() 34 | var buttonLeftText = Text() 35 | var rectButtonLeft = Rectangle(75.0, 25.0) 36 | 37 | lateinit var repoText: Text 38 | 39 | var activeIcon = "" 40 | var isWebIcon = true 41 | 42 | 43 | init 44 | { 45 | createAnchor() 46 | initImage() 47 | initCreatorText() 48 | initAddonText() 49 | initVersionText() 50 | initButton("Download", anchorButtonLeft, buttonLeftText, rectButtonLeft) 51 | initButton("Info", anchorButtonRight, buttonRightText, rectButtonRight) 52 | 53 | // Attach Listeners 54 | setListners() 55 | 56 | 57 | rectButtonLeft.id = "buttonRect" 58 | buttonLeftText.id = "buttonText" 59 | } 60 | 61 | private fun initButton(arg: String, anchor: AnchorPane, text: Text, rect: Rectangle) 62 | { 63 | text.text = arg 64 | anchor.children.add(text) 65 | text.textAlignment = TextAlignment.CENTER 66 | text.layoutX = 0.0 67 | text.layoutY = 30.0 68 | text.wrappingWidth = 75.0 69 | text.fill = Color.WHITE 70 | 71 | anchor.children.add(rect) 72 | rect.layoutX = 0.0 73 | rect.layoutY = 12.5 74 | rect.stroke = Color.WHITE 75 | rect.fill = Color(1.0, 1.0, 1.0, 0.0) 76 | } 77 | 78 | private fun initVersionText() 79 | { 80 | versionText = Text() 81 | versionText.text = version 82 | anchorPane.children.add(versionText) 83 | versionText.wrappingWidth = 200.0 84 | versionText.fill = Color.WHITE 85 | versionText.layoutY = 38.5 86 | versionText.layoutX = 50.0 87 | } 88 | 89 | private fun initAddonText() 90 | { 91 | addonText = Text() 92 | addonText.text = name 93 | anchorPane.children.add(addonText) 94 | addonText.wrappingWidth = 200.0 95 | addonText.layoutX = 50.0 96 | addonText.layoutY = 22.5 97 | addonText.fill = Color.WHITE 98 | addonText.style = "-fx-font-weight: Bold" 99 | } 100 | 101 | private fun initCreatorText() 102 | { 103 | repoText = Text() 104 | repoText.text = repo 105 | anchorPane.children.add(repoText) 106 | repoText.wrappingWidth = 175.0 107 | repoText.layoutX = 325.0 108 | repoText.layoutY = 22.5 109 | repoText.textAlignment = TextAlignment.CENTER 110 | repoText.fill = Color.WHITE 111 | } 112 | 113 | private fun initImage() 114 | { 115 | icon = ImageView() 116 | anchorPane.children.add(icon) 117 | icon.fitWidth = 40.0 118 | icon.fitHeight = 40.0 119 | icon.layoutX = 4.0 120 | icon.layoutY = 7.5 121 | 122 | 123 | if (iconUrl == "") 124 | { 125 | setImage(icon, "/icons/NoIcon.png") 126 | isWebIcon = false 127 | } 128 | else 129 | { 130 | setURLImage(icon, iconUrl) 131 | isWebIcon = true 132 | } 133 | } 134 | 135 | private fun createAnchor() 136 | { 137 | anchorPane = AnchorPane() 138 | anchorPane.id = "$addonID" 139 | anchorPane.prefWidth = 575.0 140 | anchorButtonRight = AnchorPane() 141 | anchorButtonRight.prefWidth = 75.0 142 | anchorButtonRight.prefHeight = 50.0 143 | anchorButtonRight.layoutX = 500.0 144 | anchorButtonRight.layoutY = 0.0 145 | 146 | anchorButtonLeft = AnchorPane() 147 | anchorButtonLeft.prefHeight = 50.0 148 | anchorButtonLeft.prefWidth = 75.0 149 | anchorButtonLeft.layoutX = 250.0 150 | anchorButtonLeft.layoutY = 0.0 151 | 152 | anchorPane.children.add(anchorButtonLeft) 153 | anchorPane.children.add(anchorButtonRight) 154 | } 155 | 156 | private fun setImage(imageView: ImageView, s: String) 157 | { 158 | activeIcon = s 159 | imageView.image = Image(javaClass.getResource(s).toString()) 160 | } 161 | 162 | private fun setURLImage(imageView: ImageView, s: String) 163 | { 164 | imageView.image = Image(s) 165 | } 166 | 167 | private fun setListners() 168 | { 169 | leftButtonListners() 170 | rightButtonListners() 171 | } 172 | 173 | 174 | 175 | fun leftButtonListners() 176 | { 177 | rectButtonLeft.onMouseClicked = EventHandler() 178 | { 179 | if(it.button == MouseButton.PRIMARY) 180 | { 181 | rectButtonLeft.isVisible = false 182 | buttonLeftText.text = "Downloading" 183 | if (Externals.isExternal(addonID)) 184 | { 185 | // TODO: Hide download button 186 | when (addonID) 187 | { 188 | 17 -> Externals.addSIC() 189 | 9 -> Externals.addLutBot() 190 | } 191 | buttonLeftText.text = "Installed" 192 | } 193 | else 194 | { 195 | val a = GlobalData.getAddonByID(addonID) 196 | if (a != null) 197 | { 198 | val download_urls = checkForUseableDownloads(a.download_urls, addonID) 199 | if (download_urls.size == 1) 200 | { 201 | CoreApplication.controller.startDownload(download_urls[0], a.aid, icon.image) 202 | } 203 | else 204 | { 205 | var desc = "No description has been set, sorry." 206 | 207 | if (a.description != null) 208 | { 209 | if (a.description is String) 210 | { 211 | desc = a.description!! 212 | } 213 | 214 | } 215 | 216 | CoreApplication.controller.setDescInfo(icon.image, name, desc, addonID) 217 | CoreApplication.controller.showDownloadsPage(download_urls) 218 | } 219 | } 220 | } 221 | } 222 | } 223 | 224 | rectButtonLeft.onMouseEntered = EventHandler() 225 | { 226 | rectButtonLeft.stroke = Color(1.0, 0.0, 1.0, 1.0) 227 | } 228 | 229 | rectButtonLeft.onMouseExited = EventHandler() 230 | { 231 | rectButtonLeft.stroke = Color.WHITE 232 | } 233 | } 234 | 235 | fun rightButtonListners() 236 | { 237 | rectButtonRight.onMouseClicked = EventHandler() 238 | { 239 | if (it.button == MouseButton.PRIMARY) 240 | { 241 | val a = GlobalData.getAddonByID(addonID) 242 | var desc = "No description has been set, sorry." 243 | if (a is PAL_AddonFullData) 244 | { 245 | if (a.description != null) 246 | { 247 | if (a.description is String) 248 | { 249 | desc = a.description!! 250 | } 251 | 252 | } 253 | } 254 | CoreApplication.controller.setDescInfo(icon.image, name, desc, addonID) 255 | } 256 | } 257 | 258 | rectButtonRight.onMouseEntered = EventHandler() 259 | { 260 | rectButtonRight.stroke = Color(1.0, 0.0, 1.0, 1.0) 261 | } 262 | 263 | rectButtonRight.onMouseExited = EventHandler() 264 | { 265 | rectButtonRight.stroke = Color.WHITE 266 | } 267 | } 268 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/CoreApplication.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI 2 | 3 | import javafx.application.Application 4 | import javafx.fxml.FXMLLoader 5 | import javafx.scene.Parent 6 | import javafx.scene.Scene 7 | import javafx.scene.image.Image 8 | import javafx.stage.Stage 9 | import javafx.stage.StageStyle 10 | 11 | /** 12 | * 13 | */ 14 | class CoreApplication : Application() 15 | { 16 | companion object 17 | { 18 | lateinit var stage: Stage 19 | lateinit var controller: CoreController 20 | } 21 | 22 | override fun start(primaryStage: Stage?) 23 | { 24 | stage = Stage() 25 | val fxmlLoader = FXMLLoader() 26 | stage.initStyle(StageStyle.UNDECORATED) 27 | val root = fxmlLoader.load(javaClass.getResource("/CoreUI.fxml").openStream()) 28 | 29 | controller = fxmlLoader.getController() as CoreController 30 | 31 | stage.title = "PAL: Core" 32 | stage.icons.add(Image(javaClass.getResource("/witch.png").toString())) 33 | val scene = Scene(root, 600.0, 500.0) 34 | scene.stylesheets.add("layout_settings.css") 35 | stage.scene = scene 36 | stage.show() 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/CustomAddonAnchor.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI 2 | 3 | import GlobalData 4 | import PAL2.Addons.Externals 5 | import PAL2.Database.hideExternalAddon 6 | import PAL2.Database.updateExternalAddon 7 | import PAL2.Database.updateRunOnLaunchExternal 8 | import PAL2.SystemHandling.FileDownloader 9 | import PAL2.SystemHandling.taskKill 10 | import PAL_DataClasses.PAL_External_Addon 11 | import SystemHandling.deleteFile 12 | import javafx.application.Platform 13 | import javafx.event.EventHandler 14 | import javafx.scene.control.CheckBox 15 | import javafx.scene.control.ContextMenu 16 | import javafx.scene.control.MenuItem 17 | import javafx.scene.effect.Lighting 18 | import javafx.scene.image.Image 19 | import javafx.scene.image.ImageView 20 | import javafx.scene.input.MouseButton 21 | import javafx.scene.layout.AnchorPane 22 | import javafx.scene.paint.Color 23 | import javafx.scene.shape.Rectangle 24 | import javafx.scene.text.Text 25 | import javafx.scene.text.TextAlignment 26 | import kotlinx.coroutines.GlobalScope 27 | import kotlinx.coroutines.launch 28 | import mu.KotlinLogging 29 | import java.io.BufferedInputStream 30 | import java.io.File 31 | import java.net.HttpURLConnection 32 | import java.net.URL 33 | import java.nio.file.Files 34 | 35 | /** 36 | * 37 | */ 38 | private val logger = KotlinLogging.logger {} 39 | 40 | class ExternalAnchor(val externalAddon: PAL_External_Addon) 41 | { 42 | var anchorPane = AnchorPane() 43 | var displayImage = ImageView() 44 | lateinit var textAddonName: Text 45 | lateinit var textNewestVersion: Text 46 | lateinit var textLastCheck: Text 47 | lateinit var textEnabled: Text 48 | 49 | lateinit var bTextVersion: Text 50 | lateinit var bTextLastCheck: Text 51 | lateinit var bTextNewVersion: Text 52 | 53 | lateinit var checkBox: CheckBox 54 | 55 | //var imageViewInfo = ImageView() 56 | 57 | var anchorButton = AnchorPane() 58 | var rectButton = Rectangle(75.0, 25.0) 59 | var textButton = Text() 60 | 61 | // Context Menu 62 | val cUpdate = MenuItem("Check for update") 63 | val update = MenuItem("Update") 64 | val settings = MenuItem("Settings") 65 | val remove = MenuItem("Remove") 66 | val editMenu = ContextMenu(cUpdate, update, settings, remove) 67 | 68 | init 69 | { 70 | anchorPane.id = externalAddon.eid.toString() 71 | initImg() 72 | initTopText() 73 | initBottomText() 74 | initButton() 75 | initCheckBox() 76 | initEditMenu() 77 | 78 | setListners() 79 | updateChecker() 80 | } 81 | 82 | private fun initEditMenu() 83 | { 84 | editMenu.onHidden = EventHandler{GlobalData.contextMenuOpen = false} 85 | cUpdate.onAction = EventHandler{ GlobalScope.launch { updateChecker() } } 86 | settings.onAction = EventHandler { CoreApplication.controller.showSettingsOfExternal(externalAddon) } 87 | update.onAction = EventHandler { updateExternal() } 88 | remove.onAction = EventHandler { 89 | val aid = Externals.isMajorAddon(externalAddon.webSource) 90 | if (aid != 0) 91 | CoreApplication.controller.setDownloadableAddon(aid) 92 | hideExternalAddon(externalAddon.eid) 93 | CoreApplication.controller.removeExternalSelected() 94 | } 95 | } 96 | 97 | private fun updateExternal() 98 | { 99 | // TaskKill 100 | val ext = File(externalAddon.path).extension 101 | 102 | when (ext) 103 | { 104 | "ahk" -> taskKill("autohotkey.exe").waitFor() 105 | "jar" -> taskKill("java").waitFor() 106 | "exe" -> taskKill(File(externalAddon.path).name).waitFor() 107 | } 108 | 109 | 110 | GlobalScope.launch { 111 | CoreApplication.controller.showDownloadPopup(File(externalAddon.webSource).name) 112 | val location = FileDownloader().downloadFile(URL(externalAddon.webSource), GlobalData.temp_down_folder, 1024, GlobalData.noIcon) 113 | val dest = File(externalAddon.path) 114 | deleteFile(dest) 115 | Files.copy(location.toPath(), dest.toPath()) 116 | 117 | // Set new CRC32 118 | GlobalScope.launch { 119 | val crc32 = Externals.calcCRC32(dest) 120 | externalAddon.checksum = crc32 121 | bTextNewVersion.text = crc32 122 | updateExternalAddon(externalAddon) 123 | } 124 | 125 | 126 | // Set Icon to green 127 | Platform.runLater { displayImage.effect = LightningEffects.noUpdateLighting() } 128 | } 129 | } 130 | 131 | private fun updateChecker() 132 | { 133 | if (externalAddon.webSource.isEmpty()) 134 | return 135 | 136 | Platform.runLater { displayImage.effect = Lighting() } 137 | 138 | val httpConnection = URL(externalAddon.webSource).openConnection() as HttpURLConnection 139 | httpConnection.addRequestProperty("User-Agent", "Mozilla/4.0") 140 | val input = BufferedInputStream(httpConnection.inputStream) 141 | val crc32 = Externals.calcCRC32(input.readBytes()) 142 | 143 | if (crc32 == externalAddon.checksum) 144 | { 145 | Platform.runLater { displayImage.effect = LightningEffects.noUpdateLighting() } 146 | } 147 | else 148 | { 149 | Platform.runLater { displayImage.effect = LightningEffects.updateAvailLighting() } 150 | } 151 | } 152 | 153 | private fun initCheckBox() 154 | { 155 | checkBox = CheckBox("") 156 | anchorPane.children.add(checkBox) 157 | checkBox.layoutX = 542.5 158 | checkBox.layoutY = 20.0 159 | checkBox.isSelected = externalAddon.runOnLaunch 160 | } 161 | 162 | private fun setListners() 163 | { 164 | anchorListner() 165 | setDownloadUpdateListner() 166 | checkBoxListener() 167 | } 168 | 169 | private fun checkBoxListener() 170 | { 171 | checkBox.onMouseClicked = EventHandler() 172 | { 173 | updateRunOnLaunchExternal(externalAddon.eid, checkBox.isSelected) 174 | } 175 | } 176 | 177 | 178 | fun anchorListner() 179 | { 180 | // TODO: Create Rollbock Update Option 181 | 182 | anchorPane.onMouseClicked = EventHandler() 183 | { 184 | if (it.button == MouseButton.PRIMARY) 185 | { 186 | if (it.clickCount == 2) 187 | { 188 | // TODO: If no launch command show a popup that says that there is no launch command. 189 | GlobalScope.launch { 190 | Runtime.getRuntime().exec(externalAddon.launchCMD) 191 | } 192 | } 193 | } 194 | } 195 | anchorPane.onContextMenuRequested = EventHandler() 196 | { 197 | if (!GlobalData.contextMenuOpen) 198 | { 199 | GlobalData.contextMenuOpen = true 200 | editMenu.show(anchorPane, it.screenX, it.screenY) 201 | } 202 | } 203 | } 204 | 205 | fun setDownloadUpdateListner() 206 | { 207 | 208 | rectButton.onMouseEntered = EventHandler() 209 | { 210 | rectButton.stroke = Color(1.0, 0.0, 1.0, 1.0) 211 | } 212 | rectButton.onMouseExited = EventHandler() 213 | { 214 | rectButton.stroke = Color.WHITE 215 | } 216 | rectButton.onMouseClicked = EventHandler() 217 | { 218 | Platform.runLater { 219 | CoreApplication.controller.showSettingsOfExternal(externalAddon) 220 | } 221 | } 222 | } 223 | 224 | 225 | private fun initButton() 226 | { 227 | textButton.id = "textButton" 228 | rectButton.id = "rectButton" 229 | initButton("Settings", anchorButton, textButton, rectButton) 230 | anchorPane.children.add(anchorButton) 231 | } 232 | 233 | private fun initButton(arg: String, anchor: AnchorPane, text: Text, rect: Rectangle) 234 | { 235 | anchor.layoutX = 240.0 236 | anchor.layoutY = 0.0 237 | 238 | text.text = arg 239 | anchor.children.add(text) 240 | text.textAlignment = TextAlignment.CENTER 241 | text.layoutX = 0.0 242 | text.layoutY = 25.0 243 | text.wrappingWidth = 75.0 244 | text.fill = Color.WHITE 245 | 246 | anchor.children.add(rect) 247 | rect.layoutX = 0.0 248 | rect.layoutY = 7.5 249 | rect.stroke = Color.WHITE 250 | rect.fill = Color(1.0, 1.0, 1.0, 0.0) 251 | } 252 | 253 | // Drive letter :/ FileName.ext 254 | fun shortner(maxChars: Int, string: String): String 255 | { 256 | if (string.length < maxChars) 257 | return string 258 | 259 | val f = File(string) 260 | val splits = string.split(File.separator) 261 | 262 | return if (splits[0].length + splits[splits.size-1].length < maxChars - 5) 263 | { 264 | "${splits[0]}${File.separator}...${File.separator}${splits[splits.size-1]}" 265 | } 266 | else 267 | { 268 | when 269 | { 270 | f.name.length < maxChars -> f.name 271 | f.nameWithoutExtension.length < maxChars -> f.nameWithoutExtension 272 | else -> "" 273 | } 274 | } 275 | 276 | } 277 | 278 | private fun initBottomText() 279 | { 280 | // TODO: middle ... for x size 281 | bTextVersion = bottomTextFactory(shortner(50, externalAddon.path), 40.0, 200.0) 282 | bTextVersion.textAlignment = TextAlignment.LEFT 283 | bTextVersion.id = "bTextVersion" 284 | bTextLastCheck = bottomTextFactory(monthToNum(externalAddon.eid.toString()), 425.0, 100.0) 285 | bTextLastCheck.id = "bTextLastCheck" 286 | 287 | bTextNewVersion = bottomTextFactory(externalAddon.checksum, 325.0, 100.0) 288 | 289 | anchorPane.children.addAll(bTextVersion, bTextLastCheck, bTextNewVersion) 290 | } 291 | 292 | private fun monthToNum(arg: String): String 293 | { 294 | when (true) 295 | { 296 | arg.toLowerCase().contains("january") -> return arg.replace("JANUARY", "01") 297 | arg.toLowerCase().contains("february") -> return arg.replace("FEBRUARY", "02") 298 | arg.toLowerCase().contains("march") -> return arg.replace("MARCH", "03") 299 | arg.toLowerCase().contains("april") -> return arg.replace("APRIL", "04") 300 | arg.toLowerCase().contains("may") -> return arg.replace("MAY", "05") 301 | arg.toLowerCase().contains("june") -> return arg.replace("JUNE", "06") 302 | arg.toLowerCase().contains("july") -> return arg.replace("july", "07") 303 | arg.toLowerCase().contains("august") -> return arg.replace("AUGUST", "08") 304 | arg.toLowerCase().contains("september") -> return arg.replace("SEPTEMBER", "09") 305 | arg.toLowerCase().contains("october") -> return arg.replace("OCTOBER", "10") 306 | arg.toLowerCase().contains("november") -> return arg.replace("NOVEMBER", "11") 307 | arg.toLowerCase().contains("december") -> return arg.replace("DECEMBER", "12") 308 | else -> return arg 309 | } 310 | } 311 | 312 | 313 | private fun initTopText() 314 | { 315 | textAddonName = textTopFactory(externalAddon.name, 40.0, 200.0) 316 | textAddonName.id = "textAddonName" 317 | textAddonName.textAlignment = TextAlignment.LEFT 318 | textNewestVersion = textTopFactory("CRC32", 325.0, 100.0) 319 | textLastCheck = textTopFactory("EID", 425.0, 100.0) 320 | textEnabled = textTopFactory("Enabled", 525.0, 50.0) 321 | 322 | anchorPane.children.addAll(textAddonName, textNewestVersion, textLastCheck, textEnabled) 323 | } 324 | 325 | fun bottomTextFactory(arg: String, x: Double, ww: Double): Text 326 | { 327 | val text = Text() 328 | text.layoutX = x 329 | text.layoutY = 32.5 330 | text.wrappingWidth = ww 331 | text.textAlignment = TextAlignment.CENTER 332 | text.text = arg 333 | text.fill = Color.WHITE 334 | return text 335 | } 336 | 337 | fun textTopFactory(arg: String, x: Double, ww: Double): Text 338 | { 339 | val text = Text() 340 | text.fill = Color.WHITE 341 | text.layoutX = x 342 | text.layoutY = 15.0 343 | text.style = "-fx-font-weight: Bold;" 344 | text.text = arg 345 | text.wrappingWidth = ww 346 | text.textAlignment = TextAlignment.CENTER 347 | return text 348 | } 349 | 350 | fun initImg() 351 | { 352 | /* 353 | imageViewInfo.layoutX = 530.0 354 | imageViewInfo.layoutY = 2.5 355 | imageViewInfo.fitHeight = 35.0 356 | imageViewInfo.fitWidth = 35.0 357 | imageViewInfo.image = Image(javaClass.getResource("/icons/gggAproveQ.png").openStream()) 358 | anchorPane.children.add(imageViewInfo)*/ 359 | 360 | displayImage.layoutX = 2.5 361 | displayImage.layoutY = 2.5 362 | displayImage.fitWidth = 35.0 363 | displayImage.fitHeight = 35.0 364 | displayImage.id = "imageInfo" 365 | 366 | logger.debug{"${externalAddon.name} | ${externalAddon.iconUrl}"} 367 | 368 | // TODO: Use IconURL 369 | 370 | displayImage.image = Image(javaClass.getResource("/icons/NoIcon.png").openStream()) 371 | anchorPane.children.add(displayImage) 372 | } 373 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/DownloadsAnchor.kt: -------------------------------------------------------------------------------- 1 | package GUI 2 | 3 | import PAL2.GUI.CoreApplication 4 | import javafx.application.Application 5 | import javafx.application.Platform 6 | import javafx.fxml.FXML 7 | import javafx.fxml.FXMLLoader 8 | import javafx.fxml.Initializable 9 | import javafx.scene.Parent 10 | import javafx.scene.Scene 11 | import javafx.scene.control.ListView 12 | import javafx.scene.control.ProgressBar 13 | import javafx.scene.image.Image 14 | import javafx.scene.image.ImageView 15 | import javafx.scene.layout.AnchorPane 16 | import javafx.scene.paint.Color 17 | import javafx.scene.text.Font 18 | import javafx.scene.text.Text 19 | import javafx.stage.Stage 20 | import javafx.stage.StageStyle 21 | import mu.KotlinLogging 22 | import java.net.URL 23 | import java.util.* 24 | 25 | private val logger = KotlinLogging.logger {} 26 | /** 27 | * 28 | */ 29 | class DownloadsAnchor(val text: String) 30 | { 31 | init 32 | { 33 | createAnchor() 34 | createImageView() 35 | createProgressBar() 36 | createAddonText() 37 | createSideImages() 38 | } 39 | 40 | private fun createSideImages() 41 | { 42 | upArrowImg = ImageView() 43 | hideImg = ImageView() 44 | downArrowImg = ImageView() 45 | 46 | anchorPane.children.addAll(upArrowImg, hideImg, downArrowImg) 47 | 48 | upArrowImg.layoutY = 2.5 49 | upArrowImg.layoutX = 2.0 50 | upArrowImg.fitHeight = 15.0 51 | upArrowImg.fitWidth = 15.0 52 | 53 | downArrowImg.layoutY = 37.5 54 | downArrowImg.layoutX = 2.0 55 | downArrowImg.fitHeight = 15.0 56 | downArrowImg.fitWidth = 15.0 57 | 58 | hideImg.layoutY = 20.0 59 | hideImg.layoutX = 2.0 60 | hideImg.fitHeight = 15.0 61 | hideImg.fitWidth = 15.0 62 | 63 | upArrowImg.image = Image(javaClass.getResource("/icons/upArrow.png").openStream()) 64 | downArrowImg.image = Image(javaClass.getResource("/icons/downArrow.png").openStream()) 65 | hideImg.image = Image(javaClass.getResource("/icons/minus.png").openStream()) 66 | } 67 | 68 | lateinit var anchorPane: AnchorPane 69 | lateinit var imageView: ImageView 70 | lateinit var progressBar: ProgressBar 71 | lateinit var addonText: Text 72 | lateinit var upArrowImg: ImageView 73 | lateinit var downArrowImg: ImageView 74 | lateinit var hideImg: ImageView 75 | val template = "%n | %p" 76 | val font = Font("System Bold", 14.0) 77 | 78 | fun createAnchor() 79 | { 80 | anchorPane = AnchorPane() 81 | } 82 | 83 | fun createAddonText() 84 | { 85 | addonText = Text() 86 | addonText.style = "-fx-font-size: 14; -fx-font-weight: Bold;" 87 | anchorPane.children.add(addonText) 88 | addonText.text = text 89 | addonText.layoutX = 75.0 90 | addonText.layoutY = 22.0 91 | addonText.wrappingWidth = 520.0 92 | addonText.fill = Color.WHITE 93 | } 94 | 95 | fun createProgressBar() 96 | { 97 | progressBar = ProgressBar() 98 | anchorPane.children.add(progressBar) 99 | progressBar.layoutX = 75.0 100 | progressBar.layoutY = 32.0 101 | progressBar.prefWidth = 500.0 102 | progressBar.prefHeight = 18.0 103 | } 104 | 105 | fun createImageView() 106 | { 107 | imageView = ImageView() 108 | anchorPane.children.add(imageView) 109 | imageView.fitWidth = 50.0 110 | imageView.fitHeight = 50.0 111 | imageView.layoutX = 20.0 112 | imageView.layoutY = 2.5 113 | } 114 | 115 | fun setProgress(d: Double) 116 | { 117 | Platform.runLater { progressBar.progress = d } 118 | } 119 | 120 | fun addDownloadText(str: String) 121 | { 122 | Platform.runLater { addonText.text = template.replace("%n", text).replace("%p", str) } 123 | } 124 | 125 | fun setImg(image: Image) 126 | { 127 | Platform.runLater { imageView.image = image } 128 | } 129 | 130 | fun attachToListView() 131 | { 132 | CoreApplication.controller.addActiveDownload(this) 133 | } 134 | 135 | fun setName(str: String) 136 | { 137 | Platform.runLater { addonText.text = str } 138 | } 139 | } 140 | 141 | class DownloadTemplateController: Initializable 142 | { 143 | override fun initialize(location: URL?, resources: ResourceBundle?) 144 | { 145 | 146 | } 147 | 148 | fun updateProgressbar(d: Double) 149 | { 150 | Platform.runLater { progressBarDownload.progress = d } 151 | } 152 | 153 | @FXML 154 | private lateinit var anchor: AnchorPane 155 | 156 | @FXML 157 | private lateinit var addonIcon: ImageView 158 | 159 | @FXML 160 | private lateinit var addonName: Text 161 | 162 | @FXML 163 | private lateinit var progressBarDownload: ProgressBar 164 | } 165 | 166 | class AnchorTest : Application() 167 | { 168 | companion object 169 | { 170 | lateinit var stage: Stage 171 | lateinit var controller: DownloadTemplateController 172 | lateinit var listView: ListView 173 | } 174 | override fun start(primaryStage: Stage?) 175 | { 176 | val fxmlLoader = FXMLLoader() 177 | controller = DownloadTemplateController() 178 | 179 | /* 180 | val arrayList = ArrayList() 181 | fxmlLoader.setController(controller) 182 | for (c in 0..4) 183 | { 184 | arrayList.add(fxmlLoader.load(javaClass.getResource("/downloadTemplate.fxml").openStream())) 185 | }*/ 186 | 187 | var anchorPane = AnchorPane() 188 | listView = ListView() 189 | 190 | anchorPane.children.add(listView) 191 | listView.prefWidth = 600.0 192 | 193 | stage = Stage() 194 | stage.initStyle(StageStyle.UNDECORATED) 195 | stage.icons.add(Image(javaClass.getResource("/witch.png").toString())) 196 | val scene = Scene(anchorPane, 600.0, 500.0) 197 | scene.stylesheets.add("layout_settings.css") 198 | stage.initStyle(StageStyle.TRANSPARENT) 199 | scene.fill = Color.TRANSPARENT 200 | stage.scene = scene 201 | stage.isAlwaysOnTop = true 202 | stage.x = 0.0 203 | stage.show() 204 | } 205 | 206 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/FilterAnchor.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI 2 | 3 | import PAL2.Addons.Externals 4 | import PAL2.Database.* 5 | import PAL2.Filters.FilterDownloader 6 | import PAL2.SystemHandling.FileDownloader 7 | import PAL2.SystemHandling.taskKill 8 | import PAL_DataClasses.Filter 9 | import PAL_DataClasses.PAL_External_Addon 10 | import SystemHandling.deleteFile 11 | import javafx.application.Platform 12 | import javafx.event.EventHandler 13 | import javafx.scene.control.CheckBox 14 | import javafx.scene.control.ContextMenu 15 | import javafx.scene.control.MenuItem 16 | import javafx.scene.effect.Lighting 17 | import javafx.scene.image.Image 18 | import javafx.scene.image.ImageView 19 | import javafx.scene.input.MouseButton 20 | import javafx.scene.layout.AnchorPane 21 | import javafx.scene.paint.Color 22 | import javafx.scene.shape.Rectangle 23 | import javafx.scene.text.Text 24 | import javafx.scene.text.TextAlignment 25 | import kotlinx.coroutines.GlobalScope 26 | import kotlinx.coroutines.launch 27 | import mu.KotlinLogging 28 | import java.io.BufferedInputStream 29 | import java.io.File 30 | import java.net.HttpURLConnection 31 | import java.net.URL 32 | import java.nio.file.Files 33 | 34 | /** 35 | * 36 | */ 37 | private val logger = KotlinLogging.logger {} 38 | 39 | class FilterAnchor(val filter: Filter) 40 | { 41 | var anchorPane = AnchorPane() 42 | var displayImage = ImageView() 43 | lateinit var textAddonName: Text 44 | lateinit var textNewestVersion: Text 45 | lateinit var textLastCheck: Text 46 | 47 | lateinit var bTextVersion: Text 48 | lateinit var bTextLastCheck: Text 49 | lateinit var bTextNewVersion: Text 50 | 51 | //var imageViewInfo = ImageView() 52 | 53 | var anchorButton = AnchorPane() 54 | var rectButton = Rectangle(75.0, 25.0) 55 | var textButton = Text() 56 | 57 | // Context Menu 58 | val cUpdate = MenuItem("Check for update") 59 | val update = MenuItem("Update") 60 | val remove = MenuItem("Remove") 61 | val editMenu = ContextMenu(cUpdate, update, remove) 62 | 63 | init 64 | { 65 | anchorPane.id = filter.fid.toString() 66 | initImg() 67 | initTopText() 68 | initBottomText() 69 | initButton() 70 | initEditMenu() 71 | 72 | setListners() 73 | updateChecker() 74 | } 75 | 76 | private fun initEditMenu() 77 | { 78 | editMenu.onHidden = EventHandler{GlobalData.contextMenuOpen = false} 79 | cUpdate.onAction = EventHandler{ GlobalScope.launch { updateChecker() } } 80 | update.onAction = EventHandler { updateExternal() } 81 | remove.onAction = EventHandler { removeFilter() } 82 | } 83 | 84 | fun updateExternal() 85 | { 86 | logger.debug { "Syncing ${filter.name} - ${filter.variation}" } 87 | GlobalScope.launch { 88 | CoreApplication.controller.showDownloadPopup(File(filter.path).name) 89 | 90 | val dest = FilterDownloader.updateFilter(filter) ?: return@launch 91 | 92 | // Set new CRC32 93 | GlobalScope.launch { 94 | val crc32 = Externals.calcCRC32(dest) 95 | filter.crc32 = crc32 96 | updateFilter(filter) 97 | } 98 | 99 | 100 | // Set Icon to green 101 | Platform.runLater { displayImage.effect = LightningEffects.noUpdateLighting() } 102 | } 103 | } 104 | 105 | private fun updateChecker() 106 | { 107 | if (filter.webSource.isEmpty()) 108 | return 109 | 110 | Platform.runLater { displayImage.effect = Lighting() } 111 | 112 | val httpConnection = URL(filter.webSource).openConnection() as HttpURLConnection 113 | httpConnection.addRequestProperty("User-Agent", "Mozilla/4.0") 114 | val input = BufferedInputStream(httpConnection.inputStream) 115 | val crc32 = Externals.calcCRC32(input.readBytes()) 116 | 117 | if (crc32 == filter.crc32) 118 | { 119 | Platform.runLater { displayImage.effect = LightningEffects.noUpdateLighting() } 120 | } 121 | else 122 | { 123 | Platform.runLater { displayImage.effect = LightningEffects.updateAvailLighting() } 124 | } 125 | } 126 | 127 | 128 | private fun setListners() 129 | { 130 | anchorListner() 131 | setDownloadUpdateListner() 132 | } 133 | 134 | 135 | fun anchorListner() 136 | { 137 | /* 138 | anchorPane.onMouseClicked = EventHandler() 139 | { 140 | if (it.button == MouseButton.PRIMARY) 141 | { 142 | if (it.clickCount == 2) 143 | { 144 | // TODO: If no launch command show a popup that says that there is no launch command. 145 | GlobalScope.launch { 146 | Runtime.getRuntime().exec(externalAddon.launchCMD) 147 | } 148 | } 149 | } 150 | }*/ 151 | anchorPane.onContextMenuRequested = EventHandler() 152 | { 153 | if (!GlobalData.contextMenuOpen) 154 | { 155 | GlobalData.contextMenuOpen = true 156 | editMenu.show(anchorPane, it.screenX, it.screenY) 157 | } 158 | } 159 | } 160 | 161 | fun setDownloadUpdateListner() 162 | { 163 | 164 | rectButton.onMouseEntered = EventHandler() 165 | { 166 | rectButton.stroke = Color(1.0, 0.0, 1.0, 1.0) 167 | } 168 | rectButton.onMouseExited = EventHandler() 169 | { 170 | rectButton.stroke = Color.WHITE 171 | } 172 | rectButton.onMouseClicked = EventHandler() 173 | { 174 | removeFilter() 175 | } 176 | } 177 | 178 | private fun removeFilter() 179 | { 180 | // Delete from DB 181 | deleteFilterFromDB(filter.fid) 182 | 183 | // Delete from Harddrive 184 | val file = File(filter.path) 185 | if (file.isDirectory) 186 | logger.error { "SEVERE ERROR, FILTER PATH IS A DIRECTORY!" } 187 | else if (file.isFile) 188 | deleteFile(File(filter.path)) 189 | 190 | // Delete from UI 191 | CoreApplication.controller.removeFilterAnchor() 192 | } 193 | 194 | 195 | private fun initButton() 196 | { 197 | textButton.id = "textButton" 198 | rectButton.id = "rectButton" 199 | initButton("Remove", anchorButton, textButton, rectButton) 200 | anchorPane.children.add(anchorButton) 201 | } 202 | 203 | private fun initButton(arg: String, anchor: AnchorPane, text: Text, rect: Rectangle) 204 | { 205 | anchor.layoutX = 490.0 206 | anchor.layoutY = 0.0 207 | 208 | text.text = arg 209 | anchor.children.add(text) 210 | text.textAlignment = TextAlignment.CENTER 211 | text.layoutX = 0.0 212 | text.layoutY = 25.0 213 | text.wrappingWidth = 75.0 214 | text.fill = Color.WHITE 215 | 216 | anchor.children.add(rect) 217 | rect.layoutX = 0.0 218 | rect.layoutY = 7.5 219 | rect.stroke = Color.WHITE 220 | rect.fill = Color(1.0, 1.0, 1.0, 0.0) 221 | } 222 | 223 | // Drive letter :/ FileName.ext 224 | fun shortner(maxChars: Int, string: String): String 225 | { 226 | if (string.length < maxChars) 227 | return string 228 | 229 | val f = File(string) 230 | val splits = string.split(File.separator) 231 | 232 | return if (splits[0].length + splits[splits.size-1].length < maxChars - 5) 233 | { 234 | "${splits[0]}${File.separator}...${File.separator}${splits[splits.size-1]}" 235 | } 236 | else 237 | { 238 | when 239 | { 240 | f.name.length < maxChars -> f.name 241 | f.nameWithoutExtension.length < maxChars -> f.nameWithoutExtension 242 | else -> "" 243 | } 244 | } 245 | 246 | } 247 | 248 | private fun initBottomText() 249 | { 250 | // TODO: middle ... for x size 251 | bTextVersion = bottomTextFactory(shortner(50, filter.path), 40.0, 300.0) 252 | bTextVersion.textAlignment = TextAlignment.LEFT 253 | bTextVersion.id = "bTextVersion" 254 | bTextLastCheck = bottomTextFactory(monthToNum(filter.fid.toString()), 395.0, 100.0) 255 | bTextLastCheck.id = "bTextLastCheck" 256 | 257 | bTextNewVersion = bottomTextFactory(filter.crc32, 325.0, 100.0) 258 | 259 | anchorPane.children.addAll(bTextVersion, bTextLastCheck, bTextNewVersion) 260 | } 261 | 262 | private fun monthToNum(arg: String): String 263 | { 264 | when (true) 265 | { 266 | arg.toLowerCase().contains("january") -> return arg.replace("JANUARY", "01") 267 | arg.toLowerCase().contains("february") -> return arg.replace("FEBRUARY", "02") 268 | arg.toLowerCase().contains("march") -> return arg.replace("MARCH", "03") 269 | arg.toLowerCase().contains("april") -> return arg.replace("APRIL", "04") 270 | arg.toLowerCase().contains("may") -> return arg.replace("MAY", "05") 271 | arg.toLowerCase().contains("june") -> return arg.replace("JUNE", "06") 272 | arg.toLowerCase().contains("july") -> return arg.replace("july", "07") 273 | arg.toLowerCase().contains("august") -> return arg.replace("AUGUST", "08") 274 | arg.toLowerCase().contains("september") -> return arg.replace("SEPTEMBER", "09") 275 | arg.toLowerCase().contains("october") -> return arg.replace("OCTOBER", "10") 276 | arg.toLowerCase().contains("november") -> return arg.replace("NOVEMBER", "11") 277 | arg.toLowerCase().contains("december") -> return arg.replace("DECEMBER", "12") 278 | else -> return arg 279 | } 280 | } 281 | 282 | 283 | private fun initTopText() 284 | { 285 | textAddonName = textTopFactory(filter.name + " - " + filter.variation, 40.0, 300.0) 286 | textAddonName.id = "textAddonName" 287 | textAddonName.textAlignment = TextAlignment.LEFT 288 | textNewestVersion = textTopFactory("CRC32", 325.0, 100.0) 289 | textLastCheck = textTopFactory("FID", 395.0, 100.0) 290 | 291 | anchorPane.children.addAll(textAddonName, textNewestVersion, textLastCheck) 292 | } 293 | 294 | fun bottomTextFactory(arg: String, x: Double, ww: Double): Text 295 | { 296 | val text = Text() 297 | text.layoutX = x 298 | text.layoutY = 32.5 299 | text.wrappingWidth = ww 300 | text.textAlignment = TextAlignment.CENTER 301 | text.text = arg 302 | text.fill = Color.WHITE 303 | return text 304 | } 305 | 306 | fun textTopFactory(arg: String, x: Double, ww: Double): Text 307 | { 308 | val text = Text() 309 | text.fill = Color.WHITE 310 | text.layoutX = x 311 | text.layoutY = 15.0 312 | text.style = "-fx-font-weight: Bold;" 313 | text.text = arg 314 | text.wrappingWidth = ww 315 | text.textAlignment = TextAlignment.CENTER 316 | return text 317 | } 318 | 319 | fun initImg() 320 | { 321 | /* 322 | imageViewInfo.layoutX = 530.0 323 | imageViewInfo.layoutY = 2.5 324 | imageViewInfo.fitHeight = 35.0 325 | imageViewInfo.fitWidth = 35.0 326 | imageViewInfo.image = Image(javaClass.getResource("/icons/gggAproveQ.png").openStream()) 327 | anchorPane.children.add(imageViewInfo)*/ 328 | 329 | displayImage.layoutX = 2.5 330 | displayImage.layoutY = 2.5 331 | displayImage.fitWidth = 35.0 332 | displayImage.fitHeight = 35.0 333 | displayImage.id = "imageInfo" 334 | 335 | displayImage.image = Image(javaClass.getResource("/icons/NoIcon.png").openStream()) 336 | anchorPane.children.add(displayImage) 337 | } 338 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/InstalledAnchor.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI 2 | 3 | import GlobalData 4 | import PAL2.Database.getRunAddonOnLaunch 5 | import PAL2.Database.updateRunAddonWhenLaunching 6 | import PAL2.SystemHandling.launchAddon 7 | import PAL2.SystemHandling.updateAddon 8 | import javafx.application.Platform 9 | import javafx.event.EventHandler 10 | import javafx.scene.control.CheckBox 11 | import javafx.scene.image.Image 12 | import javafx.scene.image.ImageView 13 | import javafx.scene.input.MouseButton 14 | import javafx.scene.layout.AnchorPane 15 | import javafx.scene.paint.Color 16 | import javafx.scene.shape.Rectangle 17 | import javafx.scene.text.Text 18 | import javafx.scene.text.TextAlignment 19 | import mu.KotlinLogging 20 | 21 | /** 22 | * 23 | */ 24 | private val logger = KotlinLogging.logger {} 25 | 26 | class InstalledAnchor(var aid: Int, var iconUrl: String?, var addonName: String, var version: String, var newestVersion: String, var lastCheck: String, var website: String) 27 | { 28 | var anchorPane = AnchorPane() 29 | var displayImage = ImageView() 30 | lateinit var textAddonName: Text 31 | lateinit var textNewestVersion: Text 32 | lateinit var textLastCheck: Text 33 | lateinit var textEnabled: Text 34 | 35 | lateinit var bTextVersion: Text 36 | lateinit var bTextLastCheck: Text 37 | lateinit var bTextNewVersion: Text 38 | 39 | lateinit var checkBox: CheckBox 40 | 41 | //var imageViewInfo = ImageView() 42 | 43 | var anchorButton = AnchorPane() 44 | var rectButton = Rectangle(75.0, 25.0) 45 | var textButton = Text() 46 | 47 | init 48 | { 49 | anchorPane.id = aid.toString() 50 | initImg() 51 | initTopText() 52 | initBottomText() 53 | initButton() 54 | initCheckBox() 55 | 56 | // Check up to date 57 | if (version == newestVersion) 58 | { 59 | this.isUpToDate() 60 | } 61 | else 62 | { 63 | this.canBeUpdated() 64 | } 65 | 66 | setListners() 67 | } 68 | 69 | private fun initCheckBox() 70 | { 71 | checkBox = CheckBox("") 72 | anchorPane.children.add(checkBox) 73 | checkBox.layoutX = 542.5 74 | checkBox.layoutY = 20.0 75 | checkBox.isSelected = getRunAddonOnLaunch(aid) 76 | } 77 | 78 | private fun setListners() 79 | { 80 | anchorListner() 81 | setDownloadUpdateListner() 82 | checkBoxListener() 83 | } 84 | 85 | private fun checkBoxListener() 86 | { 87 | checkBox.onMouseClicked = EventHandler() 88 | { 89 | updateRunAddonWhenLaunching(checkBox.isSelected, aid) 90 | } 91 | } 92 | 93 | fun anchorListner() 94 | { 95 | anchorPane.onMouseClicked = EventHandler() 96 | { 97 | if (it.button == MouseButton.PRIMARY) 98 | { 99 | if (it.clickCount == 2) 100 | { 101 | launchAddon(aid) 102 | } 103 | } 104 | } 105 | } 106 | 107 | fun setDownloadUpdateListner() 108 | { 109 | rectButton.onMouseEntered = EventHandler() 110 | { 111 | rectButton.stroke = Color(1.0, 0.0, 1.0, 1.0) 112 | } 113 | rectButton.onMouseExited = EventHandler() 114 | { 115 | rectButton.stroke = Color.WHITE 116 | } 117 | rectButton.onMouseClicked = EventHandler() 118 | { 119 | Platform.runLater { 120 | rectButton.isVisible = false 121 | textButton.text = "Downloading" 122 | updateAddon(aid, displayImage.image) 123 | } 124 | } 125 | } 126 | 127 | fun checkUpdateAble() 128 | { 129 | val most_recent = GlobalData.getAddonByID(aid)?: return 130 | if (most_recent.version_text != version) 131 | { 132 | canBeUpdated() 133 | } 134 | else 135 | { 136 | isUpToDate() 137 | } 138 | } 139 | 140 | fun isUpToDate() 141 | { 142 | Platform.runLater { 143 | rectButton.isVisible = false 144 | textButton.text = "Up-to-date" 145 | textButton.textAlignment = TextAlignment.CENTER 146 | textButton.layoutY = 25.0 147 | } 148 | } 149 | 150 | fun canBeUpdated() 151 | { 152 | Platform.runLater { 153 | rectButton.isVisible = true 154 | textButton.text = "Update" 155 | textButton.layoutY = 25.0 156 | } 157 | } 158 | /* 159 | fun isGGGAproved() 160 | { 161 | Platform.runLater { 162 | imageViewInfo.image = Image(javaClass.getResource("/icons/gggAprove.png").openStream()) 163 | } 164 | } 165 | 166 | fun isGGGunknown() 167 | { 168 | Platform.runLater { 169 | imageViewInfo.image = Image(javaClass.getResource("/icons/gggAproveQ.png").openStream()) 170 | } 171 | }*/ 172 | 173 | private fun initButton() 174 | { 175 | textButton.id = "textButton" 176 | rectButton.id = "rectButton" 177 | initButton("Update", anchorButton, textButton, rectButton) 178 | anchorPane.children.add(anchorButton) 179 | } 180 | 181 | private fun initButton(arg: String, anchor: AnchorPane, text: Text, rect: Rectangle) 182 | { 183 | anchor.layoutX = 240.0 184 | anchor.layoutY = 0.0 185 | 186 | text.text = arg 187 | anchor.children.add(text) 188 | text.textAlignment = TextAlignment.CENTER 189 | text.layoutX = 0.0 190 | text.layoutY = 25.0 191 | text.wrappingWidth = 75.0 192 | text.fill = Color.WHITE 193 | 194 | anchor.children.add(rect) 195 | rect.layoutX = 0.0 196 | rect.layoutY = 7.5 197 | rect.stroke = Color.WHITE 198 | rect.fill = Color(1.0, 1.0, 1.0, 0.0) 199 | } 200 | 201 | private fun initBottomText() 202 | { 203 | bTextVersion = bottomTextFactory(version, 40.0, 200.0) 204 | bTextVersion.textAlignment = TextAlignment.LEFT 205 | bTextVersion.id = "bTextVersion" 206 | bTextLastCheck = bottomTextFactory(monthToNum(lastCheck), 425.0, 100.0) 207 | bTextLastCheck.id = "bTextLastCheck" 208 | 209 | bTextNewVersion = bottomTextFactory(newestVersion, 325.0, 100.0) 210 | 211 | anchorPane.children.addAll(bTextVersion, bTextLastCheck, bTextNewVersion) 212 | } 213 | 214 | private fun monthToNum(arg: String): String 215 | { 216 | when (true) 217 | { 218 | arg.toLowerCase().contains("january") -> return arg.replace("JANUARY", "01") 219 | arg.toLowerCase().contains("february") -> return arg.replace("FEBRUARY", "02") 220 | arg.toLowerCase().contains("march") -> return arg.replace("MARCH", "03") 221 | arg.toLowerCase().contains("april") -> return arg.replace("APRIL", "04") 222 | arg.toLowerCase().contains("may") -> return arg.replace("MAY", "05") 223 | arg.toLowerCase().contains("june") -> return arg.replace("JUNE", "06") 224 | arg.toLowerCase().contains("july") -> return arg.replace("july", "07") 225 | arg.toLowerCase().contains("august") -> return arg.replace("AUGUST", "08") 226 | arg.toLowerCase().contains("september") -> return arg.replace("SEPTEMBER", "09") 227 | arg.toLowerCase().contains("october") -> return arg.replace("OCTOBER", "10") 228 | arg.toLowerCase().contains("november") -> return arg.replace("NOVEMBER", "11") 229 | arg.toLowerCase().contains("december") -> return arg.replace("DECEMBER", "12") 230 | else -> return arg 231 | } 232 | } 233 | 234 | 235 | private fun initTopText() 236 | { 237 | textAddonName = textTopFactory(addonName, 40.0, 200.0) 238 | textAddonName.id = "textAddonName" 239 | textAddonName.textAlignment = TextAlignment.LEFT 240 | textNewestVersion = textTopFactory("Newest Version", 325.0, 100.0) 241 | textLastCheck = textTopFactory("Last Check", 425.0, 100.0) 242 | textEnabled = textTopFactory("Enabled", 525.0, 50.0) 243 | 244 | anchorPane.children.addAll(textAddonName, textNewestVersion, textLastCheck, textEnabled) 245 | } 246 | 247 | fun bottomTextFactory(arg: String, x: Double, ww: Double): Text 248 | { 249 | val text = Text() 250 | text.layoutX = x 251 | text.layoutY = 32.5 252 | text.wrappingWidth = ww 253 | text.textAlignment = TextAlignment.CENTER 254 | text.text = arg 255 | text.fill = Color.WHITE 256 | return text 257 | } 258 | 259 | fun textTopFactory(arg: String, x: Double, ww: Double): Text 260 | { 261 | val text = Text() 262 | text.fill = Color.WHITE 263 | text.layoutX = x 264 | text.layoutY = 15.0 265 | text.style = "-fx-font-weight: Bold;" 266 | text.text = arg 267 | text.wrappingWidth = ww 268 | text.textAlignment = TextAlignment.CENTER 269 | return text 270 | } 271 | 272 | fun initImg() 273 | { 274 | /* 275 | imageViewInfo.layoutX = 530.0 276 | imageViewInfo.layoutY = 2.5 277 | imageViewInfo.fitHeight = 35.0 278 | imageViewInfo.fitWidth = 35.0 279 | imageViewInfo.image = Image(javaClass.getResource("/icons/gggAproveQ.png").openStream()) 280 | anchorPane.children.add(imageViewInfo)*/ 281 | 282 | displayImage.layoutX = 2.5 283 | displayImage.layoutY = 2.5 284 | displayImage.fitWidth = 35.0 285 | displayImage.fitHeight = 35.0 286 | displayImage.id = "imageInfo" 287 | 288 | logger.debug{"$addonName | $iconUrl"} 289 | 290 | if (iconUrl == null) 291 | { 292 | displayImage.image = Image(javaClass.getResource("/icons/NoIcon.png").openStream()) 293 | } 294 | else 295 | { 296 | if (iconUrl == "") 297 | { 298 | displayImage.image = Image(javaClass.getResource("/icons/NoIcon.png").openStream()) 299 | } 300 | else 301 | { 302 | displayImage.image = Image(iconUrl) 303 | } 304 | } 305 | anchorPane.children.add(displayImage) 306 | } 307 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/LightningEffects.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI 2 | 3 | import javafx.scene.effect.Light 4 | import javafx.scene.effect.Lighting 5 | import javafx.scene.paint.Color 6 | 7 | /** 8 | * 9 | */ 10 | object LightningEffects 11 | { 12 | fun noUpdateLighting(): Lighting 13 | { 14 | val l = Lighting() 15 | l.diffuseConstant = 2.0 16 | l.specularConstant = 0.0 17 | l.specularExponent = 0.0 18 | l.surfaceScale = 0.0 19 | l.light = Light.Distant() 20 | l.light.color = Color.color(0.0/255.0, 119.0/255.0, 42.0/255.0) 21 | return l 22 | } 23 | 24 | fun updateAvailLighting(): Lighting 25 | { 26 | val l = Lighting() 27 | l.diffuseConstant = 2.0 28 | l.specularConstant = 0.0 29 | l.specularExponent = 0.0 30 | l.surfaceScale = 0.0 31 | l.light = Light.Distant() 32 | l.light.color = Color.color(141.0/255.0, 100.0/255.0, 47.0/255.0) 33 | return l 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/Loader/Loader.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI.Loader 2 | 3 | import javafx.application.Application 4 | import javafx.application.Platform 5 | import javafx.fxml.FXML 6 | import javafx.fxml.FXMLLoader 7 | import javafx.scene.Parent 8 | import javafx.scene.Scene 9 | import javafx.scene.control.ProgressBar 10 | import javafx.scene.image.Image 11 | import javafx.scene.text.Text 12 | import javafx.stage.Stage 13 | import javafx.stage.StageStyle 14 | 15 | /** 16 | * 17 | */ 18 | class Loader : Application() 19 | { 20 | companion object 21 | { 22 | lateinit var stage: Stage 23 | lateinit var controller: LoaderController 24 | } 25 | 26 | override fun start(primaryStage: Stage?) 27 | { 28 | stage = Stage() 29 | val fxmlLoader = FXMLLoader() 30 | stage.initStyle(StageStyle.UNDECORATED) 31 | val root = fxmlLoader.load(javaClass.getResource("/Loader.fxml").openStream()) 32 | 33 | controller = fxmlLoader.getController() as LoaderController 34 | 35 | stage.title = "PAL: Loader" 36 | stage.icons.add(Image(javaClass.getResource("/witch.png").toString())) 37 | val scene = Scene(root, 400.0, 80.0) 38 | scene.stylesheets.add("layout_settings.css") 39 | stage.scene = scene 40 | stage.show() 41 | } 42 | } 43 | 44 | class LoaderController 45 | { 46 | @FXML 47 | lateinit var loggerText: Text 48 | 49 | @FXML 50 | lateinit var loadingProgress: ProgressBar 51 | 52 | fun updateProgressbar(d: Double) 53 | { 54 | Platform.runLater { loadingProgress.progress = d } 55 | } 56 | 57 | fun setText(str: String) 58 | { 59 | Platform.runLater { loggerText.text = str } 60 | } 61 | 62 | fun close() 63 | { 64 | Platform.runLater { Loader.stage.close() } 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GUI/UtilityAnchor.kt: -------------------------------------------------------------------------------- 1 | package PAL2.GUI 2 | 3 | import javafx.event.EventHandler 4 | import javafx.scene.image.Image 5 | import javafx.scene.image.ImageView 6 | import javafx.scene.layout.AnchorPane 7 | import javafx.scene.paint.Color 8 | import javafx.scene.text.Text 9 | 10 | 11 | /** 12 | * 13 | */ 14 | class UtilityAnchor 15 | { 16 | lateinit var anchorPane: AnchorPane 17 | 18 | var refresh = Text() 19 | var update= Text() 20 | var updateAll = Text() 21 | var editConfig = Text() 22 | var removeAddon = Text() 23 | 24 | var _refresh = Text() 25 | var _update = Text() 26 | var _updateAll = Text() 27 | var _editConfig = Text() 28 | var _removeAddon = Text() 29 | 30 | init 31 | { 32 | createAnchor() 33 | createImages() 34 | createTexts() 35 | 36 | setListners() 37 | } 38 | 39 | private fun setListners() 40 | { 41 | baseTextListner(refresh) 42 | baseTextListner(updateAll) 43 | baseTextListner(update) 44 | baseTextListner(editConfig) 45 | baseTextListner(removeAddon) 46 | baseTextListner(_refresh) 47 | baseTextListner(_updateAll) 48 | baseTextListner(_update) 49 | baseTextListner(_editConfig) 50 | baseTextListner(_removeAddon) 51 | } 52 | 53 | private fun baseTextListner(text: Text) 54 | { 55 | text.onMouseEntered = EventHandler() 56 | { 57 | text.isVisible = !text.isVisible 58 | } 59 | 60 | text.onMouseExited = EventHandler() 61 | { 62 | text.isVisible = !text.isVisible 63 | } 64 | } 65 | 66 | 67 | private fun createTexts() 68 | { 69 | shadowedText(refresh, _refresh, "Refresh", 32.0, 20.0, Color.WHITE, Color(0.192, 0.671, 0.776, 1.0)) 70 | shadowedText(updateAll, _updateAll, "Update All", 220.0, 20.0, Color.WHITE, Color(0.192, 0.671, 0.776, 1.0)) 71 | shadowedText(update, _update, "Update", 130.0, 20.0, Color.WHITE, Color(0.192, 0.671, 0.776, 1.0)) 72 | shadowedText(editConfig, _editConfig, "Edit Configuration", 340.0, 20.0, Color.WHITE, Color(0.192, 0.671, 0.776, 1.0)) 73 | shadowedText(removeAddon, _removeAddon, "Remove Addon", 500.0, 20.0, Color((196.0/255.0), (49.0/255.0), (49.0/255.0), 1.0), Color.WHITE) 74 | 75 | anchorPane.children.addAll(refresh, update, updateAll, editConfig, removeAddon, 76 | _refresh, _update, _updateAll, _editConfig, _removeAddon) 77 | } 78 | 79 | fun shadowedText(text: Text, _text: Text, s: String, x: Double, y: Double, c: Color, _c: Color) 80 | { 81 | subShadow(text, s, x, y) 82 | subShadow(_text, s, x, y) 83 | text.fill = c 84 | _text.fill = _c 85 | _text.isVisible = false 86 | } 87 | 88 | fun subShadow(text: Text, s:String, x: Double, y: Double) 89 | { 90 | text.text = s 91 | text.style = "-fx-font-weight: Bold" 92 | text.layoutY = y 93 | text.layoutX = x 94 | } 95 | 96 | private fun createImages() 97 | { 98 | val refresh = ImageView() 99 | val update = ImageView() 100 | val updateAll = ImageView() 101 | val editConfig = ImageView() 102 | val removeAddon = ImageView() 103 | 104 | anchorPane.children.addAll(refresh, update, updateAll, editConfig, removeAddon) 105 | 106 | setWidthHeight(refresh) 107 | setWidthHeight(update) 108 | setWidthHeight(updateAll) 109 | setWidthHeight(editConfig) 110 | setWidthHeight(removeAddon) 111 | 112 | setXY(refresh, 2.5, 2.5) 113 | setXY(update, 100.0, 2.5) 114 | setXY(updateAll, 190.0, 2.5) 115 | setXY(editConfig, 310.0, 2.5) 116 | setXY(removeAddon, 470.0, 2.5) 117 | 118 | setImg(refresh, "/icons/refresh_icon.png") 119 | setImg(update, "/icons/down.png") 120 | setImg(updateAll, "/icons/download_all.png") 121 | setImg(editConfig, "/icons/edit.png") 122 | setImg(removeAddon, "/icons/remove.png") 123 | } 124 | 125 | private fun setImg(imageView: ImageView, s: String) 126 | { 127 | imageView.image = Image(javaClass.getResource(s).toString()) 128 | } 129 | 130 | private fun setXY(imageView: ImageView, x: Double, y: Double) 131 | { 132 | imageView.layoutY = y 133 | imageView.layoutX = x 134 | } 135 | 136 | private fun setWidthHeight(imageView: ImageView) 137 | { 138 | imageView.fitWidth = 25.0 139 | imageView.fitHeight = 25.0 140 | } 141 | 142 | private fun createAnchor() 143 | { 144 | anchorPane = AnchorPane() 145 | } 146 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Github/GH_Connector.kt: -------------------------------------------------------------------------------- 1 | package Github 2 | 3 | import GlobalData 4 | import PAL_DataClasses.PAL_AddonFullData 5 | import PAL_DataClasses.initObjectMapper 6 | import mu.KotlinLogging 7 | import org.kohsuke.github.GHRelease 8 | import org.kohsuke.github.GitHub 9 | import java.io.BufferedInputStream 10 | import java.net.HttpURLConnection 11 | import java.net.URL 12 | 13 | /** 14 | * 15 | */ 16 | private val logger = KotlinLogging.logger {} 17 | var attempts = 0 18 | 19 | fun connect(): GitHub 20 | { 21 | val token = GlobalData.github_token 22 | if (token != "") 23 | { 24 | return try 25 | { 26 | GitHub.connectUsingOAuth(token) 27 | } 28 | catch (ex: Exception) 29 | { 30 | if (attempts == 3) 31 | { 32 | logger.error { "GitHub API is offline" } 33 | // TODO: Use fallback 34 | } 35 | attempts++ 36 | logger.error { "Github API is likely offline, retrying in 5 seconds!" } 37 | Thread.sleep(5 * 1000) 38 | return connect() 39 | } 40 | } 41 | return try 42 | { 43 | GitHub.connectAnonymously() 44 | } 45 | catch (ex: Exception) 46 | { 47 | if (attempts == 3) 48 | { 49 | logger.error { "GitHub API is offline" } 50 | // TODO: Use fallback 51 | } 52 | attempts++ 53 | logger.error { "Github API is likely offline, retrying in 5 seconds!" } 54 | Thread.sleep(5 * 1000) 55 | return connect() 56 | } 57 | } 58 | 59 | /** 60 | * Checks Repo for an addons.json and parses it. 61 | * Returns null if no addons.json is found. 62 | */ 63 | fun getAddonsFromGHRepo(repo: String): Array? 64 | { 65 | val connection = connect() 66 | val repository = connection.getRepository(repo) 67 | for (a in repository.latestRelease.assets) 68 | { 69 | if (a.name == "addons.json") 70 | { 71 | val httpConnection = URL(a.browserDownloadUrl).openConnection() as HttpURLConnection 72 | val input = BufferedInputStream(httpConnection.inputStream) 73 | 74 | val om = initObjectMapper() 75 | return om.readValue(input, Array::class.java) 76 | } 77 | } 78 | return null 79 | } 80 | 81 | /** 82 | * Checking greater than 5 to keep the user with some GitHub requests in case of special cases. 83 | */ 84 | fun canRequest(gitHub: GitHub): Boolean 85 | { 86 | return if (gitHub.rateLimit.remaining > 5) 87 | { 88 | true 89 | } 90 | else 91 | { 92 | logger.error { "Only ${gitHub.rateLimit.remaining} requests remaining; which reset at: ${gitHub.rateLimit.resetDate}" } 93 | false 94 | } 95 | } 96 | 97 | fun getLatestRelease(gitHub: GitHub, username:String, reponame: String): GHRelease? 98 | { 99 | if (canRequest(gitHub)) 100 | { 101 | return gitHub.getRepository("$username/$reponame").latestRelease 102 | } 103 | return null 104 | } 105 | -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Github/ReadMeConverter.kt: -------------------------------------------------------------------------------- 1 | package PAL2.Github 2 | 3 | import PAL_DataClasses.PAL_AddonFullData 4 | import org.commonmark.parser.Parser 5 | import org.commonmark.renderer.html.HtmlRenderer 6 | import java.io.BufferedInputStream 7 | import java.net.HttpURLConnection 8 | import java.net.URL 9 | 10 | /** 11 | * 12 | */ 13 | object ReadMeConverter 14 | { 15 | fun exceptions(a: PAL_AddonFullData): String 16 | { 17 | return when (a.aid) 18 | { 19 | 6 -> "https://raw.githubusercontent.com/PoE-TradeMacro/POE-TradeMacro/master/README.markdown" 20 | 8 -> "https://raw.githubusercontent.com/klayveR/xenontrade/master/readme.md" 21 | 9 -> "https://gist.githubusercontent.com/POE-Addon-Launcher/2687293a2e255d394e16895a5bcd180d/raw/685db0260292a175270f1ca3922ee07acc73ab6c/lutbot.md" 22 | 12 -> "https://gist.githubusercontent.com/POE-Addon-Launcher/0db7512cdfc44afdca3a66b6c8c240d8/raw/dc463bc87006fe5913c8ddd94c384b5be1bf9298/nope.md" 23 | 17 -> "https://gist.githubusercontent.com/POE-Addon-Launcher/0db7512cdfc44afdca3a66b6c8c240d8/raw/dc463bc87006fe5913c8ddd94c384b5be1bf9298/nope.md" 24 | else -> "https://raw.githubusercontent.com/${a.gh_username}/${a.gh_reponame}/master/README.md" 25 | } 26 | } 27 | 28 | fun convert(a: PAL_AddonFullData): String 29 | { 30 | val url = exceptions(a) 31 | val httpConnection = URL(url).openConnection() as HttpURLConnection 32 | httpConnection.addRequestProperty("User-Agent", "Mozilla/4.0") 33 | val input = BufferedInputStream(httpConnection.inputStream) 34 | val str = String(input.readBytes()) 35 | val parser = Parser.builder().build() 36 | val doc = parser.parse(str) 37 | val renderer = HtmlRenderer.builder().build() 38 | val html = renderer.render(doc) 39 | return html 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Github/UpdateGrabber.kt: -------------------------------------------------------------------------------- 1 | package PAL2.Github 2 | 3 | import Github.connect 4 | import SystemHandling.deleteFile 5 | import SystemHandling.unzip 6 | import SystemHandling.verifyFolder 7 | import mu.KotlinLogging 8 | import org.apache.commons.io.FileUtils 9 | import java.io.File 10 | import java.net.URL 11 | 12 | private val logger = KotlinLogging.logger {} 13 | 14 | /** 15 | * 16 | */ 17 | fun checkUpdate() 18 | { 19 | if (!GlobalData.debugging) 20 | { 21 | val gh = connect() 22 | if (gh.rateLimit.remaining > 3) 23 | { 24 | val repo = gh.getRepository("") 25 | } 26 | } 27 | } 28 | 29 | fun getUpdate() 30 | { 31 | // Get Token from DB if DB exists and if token is != "" 32 | if (!GlobalData.debugging) 33 | { 34 | val gh = connect() 35 | if (gh.rateLimit.remaining > 3) 36 | { 37 | val repo = gh.getRepository("POE-Addon-Launcher/PALRelease") 38 | val latest = repo.latestRelease 39 | val latest_tag = latest.tagName 40 | if(latest_tag != GlobalData.version) 41 | { 42 | val down_folder = File("${GlobalData.pal_folder}") 43 | verifyFolder(down_folder) 44 | 45 | val dest = File("${GlobalData.install_dir}${File.separator}new${File.separator}PAL2.jar") 46 | val destFolder = File("${GlobalData.install_dir}${File.separator}new") 47 | if (dest.exists()) 48 | deleteFile(dest) 49 | 50 | if (!destFolder.exists()) 51 | destFolder.mkdir() 52 | 53 | val download_url = latest.assets[0].browserDownloadUrl 54 | FileUtils.copyURLToFile(URL(download_url), dest) 55 | 56 | logger.debug { "Downloaded update [$latest_tag] to: $dest" } 57 | 58 | //rundll32 url.dll,FileProtocolHandler 59 | println(">>> \"${GlobalData.install_dir}${File.separator}PAL2.exe\" <<") 60 | Runtime.getRuntime().exec("\"${GlobalData.install_dir}${File.separator}PAL2.exe\"") 61 | 62 | System.exit(99) 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/GlobalData.kt: -------------------------------------------------------------------------------- 1 | import PAL_DataClasses.PAL_AddonFullData 2 | import javafx.scene.image.Image 3 | import java.io.File 4 | import javax.swing.filechooser.FileSystemView 5 | 6 | /** 7 | * 8 | */ 9 | class GlobalData 10 | { 11 | companion object 12 | { 13 | var version = "1.12.2" 14 | var debugging = false 15 | var install_dir = "" 16 | var github_token = "" 17 | var list_of_addons = ArrayList() 18 | var set_of_addons = HashSet() 19 | var list_of_repos = ArrayList() 20 | var pal_folder = File("${System.getenv("LOCALAPPDATA")}${File.separator}PAL") 21 | var pal_data_folder = File("${pal_folder.path}${File.separator}Data") 22 | var db_file = File("${pal_data_folder}${File.separator}PALData.db") 23 | var server_enabled = false 24 | var first_launch = true 25 | var first_launch_after_update = true 26 | var primaryPoEFile: File? = null 27 | var poeLocations = ArrayList() 28 | var logFile = "" 29 | val noIcon = Image(GlobalData::class.java.getResource("/icons/NoIcon.png").openStream()) 30 | 31 | var loot_filter_path = FileSystemView.getFileSystemView().defaultDirectory.path + File.separator + "My Games" + File.separator + "Path of Exile" 32 | var show_update_note = false 33 | var launch_addons = true 34 | var contextMenuOpen = false 35 | var filterblastApiInProgress = true 36 | 37 | /** 38 | * DB Data 39 | */ 40 | //var poePaths = HashSet() 41 | var addonFolder = File("${pal_folder}${File.separator}Addons") 42 | var ahkFolder = File("") 43 | var launchPOEonLaunch = false 44 | var gitHubAPIEnabled = true 45 | var showUpdateNotesOnUpdate = true 46 | var ahk_scripts = HashSet() 47 | 48 | var temp_down_folder = File("${pal_folder.path}${File.separator}temp_downloads") 49 | var launchList = ArrayList() 50 | var steam_poe = false 51 | var launch_externals = true 52 | var allowTaskKill = true 53 | 54 | fun addToListOfAddons(arr: Array) 55 | { 56 | for (a in arr) 57 | { 58 | if (!list_of_addons.contains(a)) 59 | { 60 | list_of_addons.add(a) 61 | } 62 | } 63 | } 64 | 65 | fun getAddonByID(aid: Int): PAL_AddonFullData? 66 | { 67 | for (a in set_of_addons) 68 | { 69 | if (a.aid == aid) 70 | { 71 | return a 72 | } 73 | } 74 | return null 75 | } 76 | 77 | fun checkAddonListForDuplicates() 78 | { 79 | set_of_addons.addAll(list_of_addons) 80 | } 81 | 82 | fun addToRepoList(repo: String) 83 | { 84 | if (!list_of_repos.contains(repo)) 85 | { 86 | list_of_repos.add(repo) 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/Launcher.kt: -------------------------------------------------------------------------------- 1 | import PAL2.GUI.CoreApplication 2 | import SystemHandling.deleteFile 3 | import javafx.application.Application 4 | import org.apache.commons.io.FileUtils 5 | import java.io.File 6 | import java.util.* 7 | 8 | /** 9 | * Reads Data 10 | * Creates Data 11 | * Launches everything 12 | */ 13 | class Launcher2 14 | { 15 | companion object 16 | { 17 | @JvmStatic 18 | fun main(args: Array) 19 | { 20 | // TODO: Allow users to add existing addons 21 | 22 | // Set Locale to US to avoid any locale issues. 23 | Locale.setDefault(Locale.US) 24 | 25 | if (!GlobalData.debugging) 26 | { 27 | var fInstall = File(Launcher2::class.java.protectionDomain.codeSource.location.toURI()) 28 | 29 | if (fInstall.isFile) 30 | { 31 | fInstall = fInstall.parentFile 32 | } 33 | GlobalData.install_dir = fInstall.path 34 | 35 | 36 | } 37 | 38 | 39 | /* 40 | if (!GlobalData.debugging) 41 | { 42 | var fInstall = File(Launcher2::class.java.protectionDomain.codeSource.location.toURI()) 43 | 44 | if (fInstall.isFile) 45 | { 46 | fInstall = fInstall.parentFile 47 | } 48 | GlobalData.install_dir = fInstall.path 49 | 50 | val stateONE = File("${GlobalData.pal_folder.path}${File.separator}1.state") 51 | val stateTWO = File("${GlobalData.pal_folder.path}${File.separator}2.state") 52 | val newest_update = File("${GlobalData.install_dir}${File.separator}latest") 53 | 54 | if (stateONE.exists()) 55 | { 56 | val install_dir = File(GlobalData.install_dir) 57 | FileUtils.copyDirectory(install_dir, install_dir.parentFile) 58 | 59 | deleteFile(stateONE) 60 | stateTWO.createNewFile() 61 | // Launch PAL2.exe in the parent file. 62 | Runtime.getRuntime().exec("\"${install_dir.parentFile.path}${File.separator}PAL2.exe\"") 63 | return 64 | } 65 | else if (stateTWO.exists()) 66 | { 67 | deleteFile(newest_update) 68 | deleteFile(stateTWO) 69 | } 70 | }*/ 71 | 72 | try 73 | { 74 | Application.launch(CoreApplication::class.java, *args) 75 | } 76 | catch (ex: Exception) 77 | { 78 | ex.printStackTrace() 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/PAL_DataClasses/PALData.kt: -------------------------------------------------------------------------------- 1 | package PAL_DataClasses 2 | 3 | import com.fasterxml.jackson.core.JsonFactory 4 | import com.fasterxml.jackson.core.JsonGenerator 5 | import com.fasterxml.jackson.core.JsonParser 6 | import com.fasterxml.jackson.databind.ObjectMapper 7 | import com.fasterxml.jackson.module.kotlin.KotlinModule 8 | import javafx.beans.property.SimpleStringProperty 9 | import java.time.LocalDate 10 | import java.util.* 11 | 12 | /** 13 | * PAL Data Classes; mimic database entries. 14 | */ 15 | data class FilterSettings 16 | ( 17 | var updateEvery: Int, 18 | var dontCheckWhenPoERunning: Boolean, 19 | var autoUpdateFilters: Boolean 20 | ) 21 | 22 | 23 | data class Filter 24 | ( 25 | var fid: Int, 26 | val name: String, 27 | var crc32: String, 28 | val webSource: String, 29 | val path: String, 30 | val variation: String 31 | ) 32 | 33 | data class PAL_External_Addon 34 | ( 35 | var eid: Int, 36 | var name: String, 37 | var checksum: String, 38 | var newCheckSum: String, 39 | var iconUrl: String?, 40 | var lastCheck: String, 41 | var webSource: String, 42 | var launchCMD: String, 43 | var path: String, 44 | var runOnLaunch: Boolean 45 | ) 46 | 47 | data class PAL_Addon 48 | ( 49 | var id: Int, 50 | var name: String, 51 | var gh_username: String?, 52 | var gh_reponame: String?, 53 | var icon: String?, 54 | var description: String? 55 | ) 56 | 57 | data class PAL_DownloadInfo 58 | ( 59 | var id: Int, 60 | var download_url: String 61 | ) 62 | 63 | data class PAL_AddonVersion 64 | ( 65 | var id: Int, 66 | var version_text: String, 67 | var addon: PAL_Addon, 68 | var downloadInfo: PAL_DownloadInfo, 69 | var launch_command: String, 70 | var last_update: LocalDate, 71 | var html_description: String? 72 | ) 73 | 74 | data class PAL_AddonFullData 75 | ( 76 | var name: String, 77 | var aid: Int, 78 | var gh_username: String?, 79 | var gh_reponame: String?, 80 | var icon_url: String?, 81 | var description: String?, 82 | var download_urls: Array, 83 | var avid: Int, 84 | var version_text: String, 85 | var last_update: String, 86 | var html_description: String?, 87 | var extra_flags: Array 88 | ) 89 | { 90 | 91 | 92 | override fun hashCode(): Int 93 | { 94 | var result = name.hashCode() 95 | result = 31 * result + (gh_username?.hashCode() ?: 0) 96 | result = 31 * result + (gh_reponame?.hashCode() ?: 0) 97 | result = 31 * result + (icon_url?.hashCode() ?: 0) 98 | result = 31 * result + (description?.hashCode() ?: 0) 99 | result = 31 * result + download_urls.contentHashCode() 100 | result = 31 * result + version_text.hashCode() 101 | result = 31 * result + last_update.hashCode() 102 | result = 31 * result + (html_description?.hashCode() ?: 0) 103 | result = 31 * result + extra_flags.contentHashCode() 104 | return result 105 | } 106 | 107 | override fun equals(other: Any?): Boolean 108 | { 109 | if (this === other) return true 110 | if (javaClass != other?.javaClass) return false 111 | 112 | other as PAL_AddonFullData 113 | 114 | if (name != other.name) return false 115 | if (aid != other.aid) return false 116 | if (gh_username != other.gh_username) return false 117 | if (gh_reponame != other.gh_reponame) return false 118 | if (icon_url != other.icon_url) return false 119 | if (description != other.description) return false 120 | if (!download_urls.contentEquals(other.download_urls)) return false 121 | if (avid != other.avid) return false 122 | if (version_text != other.version_text) return false 123 | if (last_update != other.last_update) return false 124 | if (html_description != other.html_description) return false 125 | if (!extra_flags.contentEquals(other.extra_flags)) return false 126 | 127 | return true 128 | } 129 | 130 | fun toAddonTableRow(): PAL_AddonTableRow 131 | { 132 | return PAL_AddonTableRow(SimpleStringProperty(name), SimpleStringProperty(version_text), SimpleStringProperty(last_update), SimpleStringProperty(gh_reponame), SimpleStringProperty(gh_reponame), SimpleStringProperty(description)) 133 | } 134 | } 135 | 136 | data class PAL_Creator 137 | ( 138 | var id: Int, 139 | var name: String 140 | ) 141 | 142 | data class PAL_Ownership 143 | ( 144 | var creator: PAL_Creator, 145 | var addon: PAL_Addon 146 | ) 147 | 148 | data class PAL_FilterVariation 149 | ( 150 | var id: Int, 151 | var key: String, 152 | var name: String, 153 | var filter: PAL_Filter 154 | ) 155 | 156 | data class PAL_Filter 157 | ( 158 | var name: String, 159 | var version: String, 160 | var PoE_version: String, 161 | var forumthread: String, 162 | var description: String, 163 | var html_description: String, 164 | var variations: Array, 165 | var key: String 166 | ) 167 | { 168 | override fun toString(): String 169 | { 170 | return "PAL_Filter(name='$name', version='$version', PoE_version='$PoE_version', forumthread='$forumthread', description='$description', html_description='$html_description', variations=${Arrays.toString(variations)}, key='$key')" 171 | } 172 | } 173 | 174 | data class PAL_GH_Info 175 | ( 176 | var id: Int, 177 | var username: String, 178 | var reponame: String, 179 | var addon: PAL_Addon 180 | ) 181 | 182 | data class PAL_InstalledAddon 183 | ( 184 | var id: Int, 185 | var install_location: String, 186 | var local_version: String, 187 | var install_date: LocalDate, 188 | var executable_location: String, 189 | var addonVersion: PAL_AddonVersion 190 | ) 191 | 192 | data class PAL_InstalledFilter 193 | ( 194 | var id: Int, 195 | var filter: PAL_Filter, 196 | var install_date: LocalDate, 197 | var local_version: String 198 | ) 199 | 200 | data class PAL_User(val username: String, val poe_account_name: String, val displayName: String, val email:String, val registeredAt: String, 201 | var account_desc: String) 202 | 203 | data class PAL_AddonMetaData 204 | ( 205 | var aid: Int, 206 | var likes: Int, 207 | var dislikes: Int, 208 | var downloads: Int, 209 | var description: String, 210 | var html_description: String, 211 | var icon_url: String, 212 | var ggg_aproved: Boolean, 213 | var creators: String 214 | ) 215 | 216 | 217 | fun initObjectMapper(): ObjectMapper 218 | { 219 | return ObjectMapper() 220 | .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) 221 | .registerModule(KotlinModule()) 222 | } 223 | 224 | fun initObjectMapperWithoutStreamClose(): ObjectMapper 225 | { 226 | val mpf = JsonFactory() 227 | mpf.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false) 228 | return ObjectMapper(mpf) 229 | .enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES) 230 | .registerModule(KotlinModule()) 231 | } 232 | 233 | /** 234 | * Addon Data 235 | */ 236 | data class Releases 237 | ( 238 | var name: String, 239 | var release_list: Array<*>, 240 | var release_date: LocalDate, 241 | var tag: String 242 | ) 243 | { 244 | override fun toString(): String 245 | { 246 | val str = StringBuilder() 247 | str.append("$name | $tag | $release_date\n") 248 | 249 | for (r in release_list) 250 | { 251 | if (r != null) 252 | { 253 | str.append("\t> $r\n") 254 | } 255 | else 256 | { 257 | str.append("\t> NULL\n") 258 | } 259 | } 260 | 261 | return str.toString() 262 | } 263 | } 264 | 265 | data class Release 266 | ( 267 | var name: String, 268 | var dl_link: String, 269 | var content_type: String, 270 | var download_size: Long 271 | ) 272 | { 273 | override fun toString(): String 274 | { 275 | // Magic Number to convert download size to MB: 1048576 276 | return "$name $content_type ${converToMB(download_size)}" 277 | } 278 | 279 | fun converToMB(bits: Long): String 280 | { 281 | return "${String.format("%.1f", bits/1048576.0)} MB" 282 | } 283 | } 284 | 285 | fun createAFD(a: PAL_Addon, di: Array, av: PAL_AddonVersion, ef: Array): PAL_AddonFullData 286 | { 287 | return PAL_AddonFullData( 288 | a.name, 289 | a.id, 290 | a.gh_username, 291 | a.gh_reponame, 292 | a.icon, 293 | a.description, 294 | di, 295 | av.id, 296 | av.version_text, 297 | createDate(av.last_update), 298 | av.html_description, 299 | ef 300 | ) 301 | } 302 | 303 | fun createDate(arg:LocalDate):String 304 | { 305 | return "${arg.year}-${arg.month}-${arg.dayOfMonth}" 306 | } 307 | 308 | /** 309 | * DEPRECATED DATA CLASSES 310 | */ 311 | 312 | @Deprecated("Used for compatibility with old PAL") 313 | data class PAL_AddonJson 314 | ( 315 | var name :String, 316 | var version :String, 317 | var creators :String, 318 | var gh_username :String, 319 | var gh_reponame :String, 320 | var download_url :String, 321 | var icon_url :String, 322 | var description :String, 323 | var file_launch :String, 324 | var programming_language :String 325 | ) 326 | 327 | /** 328 | * FXML 329 | */ 330 | data class PAL_AddonTableRow 331 | ( 332 | var name: SimpleStringProperty, 333 | var version: SimpleStringProperty, 334 | var last_update: SimpleStringProperty, 335 | var gh_reponame: SimpleStringProperty, 336 | var gh_username: SimpleStringProperty, 337 | var description: SimpleStringProperty 338 | ) -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/SystemHandling/AddonRemover.kt: -------------------------------------------------------------------------------- 1 | package PAL2.SystemHandling 2 | 3 | import PAL2.Database.getInstallDir 4 | import PAL2.Database.removeInstalledAddon 5 | import SystemHandling.deleteFile 6 | import kotlinx.coroutines.GlobalScope 7 | import kotlinx.coroutines.launch 8 | import java.io.File 9 | 10 | /** 11 | * 12 | */ 13 | fun removeAddon(aid: Int) 14 | { 15 | val install_dir = findInstall(aid) 16 | if (install_dir != null) 17 | { 18 | if (install_dir.exists() && install_dir.isDirectory) 19 | { 20 | GlobalScope.launch { 21 | deleteFile(install_dir) 22 | } 23 | removeInstalledAddon(aid) 24 | } 25 | } 26 | } 27 | 28 | fun findInstall(aid :Int): File? 29 | { 30 | return getInstallDir(aid) 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/SystemHandling/AddonUpdater.kt: -------------------------------------------------------------------------------- 1 | package PAL2.SystemHandling 2 | 3 | import GlobalData 4 | import SystemHandling.checkForUseableDownloads 5 | import javafx.scene.image.Image 6 | import kotlinx.coroutines.GlobalScope 7 | import kotlinx.coroutines.launch 8 | import java.net.URL 9 | 10 | /** 11 | * 12 | */ 13 | fun updateAddon(aid: Int, image: Image) 14 | { 15 | closeAllAddons() 16 | GlobalScope.launch { 17 | val addon = GlobalData.getAddonByID(aid) ?: return@launch 18 | val download_urls = checkForUseableDownloads(addon.download_urls, addon.aid) 19 | 20 | if (download_urls.size == 1) 21 | { 22 | val fd = FileDownloader() 23 | fd.downloadFileAndInstall(URL(download_urls[0]), GlobalData.temp_down_folder, 1024, image, aid) 24 | } 25 | } 26 | } 27 | 28 | fun closeAllAddons() 29 | { 30 | if (GlobalData.allowTaskKill) 31 | { 32 | taskKill("autohotkey.exe /F") 33 | taskKill("Path of Maps Client.exe") 34 | taskKill("TraderForPoe.exe") 35 | taskKill("javaw.exe") 36 | taskKill("java.exe") 37 | taskKill("POE-Trades-Companion.exe") 38 | taskKill("XenonTrade.exe") 39 | taskKill("LabCompass.exe") 40 | taskKill("Exilence.exe") 41 | taskKill("CurrencyCop.exe") 42 | taskKill("PoE Custom Soundtrack.exe") 43 | } 44 | } 45 | 46 | fun taskKill(name: String): Process 47 | { 48 | if (name == "java") 49 | { 50 | taskKill("javaw.exe").waitFor() 51 | return taskKill("java.exe") 52 | } 53 | return Runtime.getRuntime().exec("taskkill /IM \"$name\"") 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/SystemHandling/FileHandling.kt: -------------------------------------------------------------------------------- 1 | package SystemHandling 2 | 3 | import GlobalData 4 | import mu.KotlinLogging 5 | import net.lingala.zip4j.core.ZipFile 6 | import java.io.File 7 | 8 | private val logger = KotlinLogging.logger {} 9 | 10 | /** 11 | * Verify if a folder exists if it doesn't deletes it. 12 | */ 13 | fun verifyFolder(file: File) 14 | { 15 | if (!file.exists()) 16 | { 17 | logger.debug { "Creating folder: \"${file.path}\"" } 18 | file.mkdir() 19 | } 20 | } 21 | 22 | /** 23 | * WARNING: Recursively deletes all underlying folders aswell! 24 | */ 25 | fun deleteFile(file: File) 26 | { 27 | if (file.isDirectory) 28 | logger.debug { "Deleting: ${file.path} and all ${file.listFiles().size} files within" } 29 | else 30 | logger.debug { "Deleting: ${file.path}"} 31 | if (file.exists()) 32 | { 33 | if (file.isDirectory) 34 | { 35 | for (f in file.listFiles()) 36 | { 37 | deleteFile(f) 38 | } 39 | } 40 | file.delete() 41 | } 42 | } 43 | 44 | fun checkForUseableDownloads(download_urls: Array, aid: Int): Array 45 | { 46 | when (aid) 47 | { 48 | 2 -> return usableDownloadsWithExtension(download_urls, "zip") 49 | 5 -> return usableDownloadsWithExtension(download_urls, "zip") 50 | 8 -> return usableDownloadsWithExtension(download_urls, "exe") 51 | 10 -> return usableDownloadsWithExtension(download_urls, "exe") 52 | 11 -> return usableDownloadsWithExtension(download_urls, "exe") 53 | else -> return defaultUsableDownloads(download_urls) 54 | } 55 | } 56 | 57 | fun removeTempDownloads() 58 | { 59 | if (GlobalData.temp_down_folder.exists() && GlobalData.temp_down_folder.isDirectory) 60 | { 61 | deleteFile(GlobalData.temp_down_folder) 62 | GlobalData.temp_down_folder.mkdir() 63 | } 64 | } 65 | 66 | fun usableDownloadsWithExtension(download_urls: Array, extension_format: String): Array 67 | { 68 | val arr = ArrayList() 69 | 70 | for (dl in download_urls) 71 | { 72 | val splits = dl.split(".") 73 | val extension = splits[splits.size-1] 74 | 75 | when (extension) 76 | { 77 | extension_format -> arr.add(dl) 78 | } 79 | 80 | } 81 | return arr.toTypedArray() 82 | } 83 | 84 | fun unzip(archive: File, destination: File) 85 | { 86 | val zip = ZipFile(archive) 87 | zip.extractAll(destination.path) 88 | deleteFile(archive) 89 | } 90 | 91 | fun defaultUsableDownloads(download_urls: Array): Array 92 | { 93 | val arr = ArrayList() 94 | 95 | for (dl in download_urls) 96 | { 97 | val splits = dl.split(".") 98 | val extension = splits[splits.size-1] 99 | 100 | when (extension) 101 | { 102 | "exe" -> arr.add(dl) 103 | "zip" -> arr.add(dl) 104 | "jar" -> arr.add(dl) 105 | "ahk" -> arr.add(dl) 106 | "msi" -> arr.add(dl) 107 | } 108 | 109 | } 110 | return arr.toTypedArray() 111 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/SystemHandling/Init.kt: -------------------------------------------------------------------------------- 1 | package SystemHandling 2 | 3 | import Github.getAddonsFromGHRepo 4 | import GlobalData 5 | import PAL2.Database.* 6 | import PAL2.GUI.Configurator.ConfiguratorApplication 7 | import PAL2.GUI.CoreApplication 8 | import PAL2.GUI.Loader.Loader 9 | import PAL2.Github.checkUpdate 10 | import PAL2.Github.getUpdate 11 | import PAL_DataClasses.PAL_AddonFullData 12 | import PAL_DataClasses.initObjectMapper 13 | import javafx.application.Platform 14 | import javafx.stage.Stage 15 | import mu.KotlinLogging 16 | import org.apache.commons.io.FileUtils 17 | import java.io.File 18 | import java.net.URL 19 | 20 | private val logger = KotlinLogging.logger {} 21 | /** 22 | * 23 | */ 24 | var configurating = false 25 | 26 | fun loader(arg: String, d: Double) 27 | { 28 | Loader.controller.setText(arg) 29 | Loader.controller.updateProgressbar(d) 30 | } 31 | 32 | fun init() 33 | { 34 | Platform.runLater { CoreApplication.stage.opacity = 0.01 } 35 | 36 | loader("Initializing Folders", 0.0) 37 | logger.debug { "Init: Folders" } 38 | initFolders() 39 | logger.debug { "Init: DB" } 40 | loader("Initializing Database", 0.1) 41 | initDB() 42 | 43 | logger.debug { "Init: Misc" } 44 | loader("Initializing User Data", 0.33) 45 | moreInit() 46 | 47 | 48 | //CoreApplication.controller.addInstalledAnchorTEST() 49 | } 50 | 51 | fun initFolders() 52 | { 53 | verifyFolder(GlobalData.pal_folder) 54 | verifyFolder(GlobalData.pal_data_folder) 55 | } 56 | 57 | fun initDB() 58 | { 59 | if (!GlobalData.db_file.exists()) 60 | { 61 | configurating = true 62 | createDB() 63 | 64 | // Ask user to enter information we need. 65 | // Launch simple UI to ask for it. 66 | Platform.runLater { 67 | val c = ConfiguratorApplication() 68 | c.start(Stage()) 69 | } 70 | 71 | } 72 | GlobalData.first_launch = false 73 | } 74 | 75 | // TODO: MORE INIT STUFF, IN TERMS OF DB SETTINGS AND FINISH CONFIGURATOR 76 | 77 | fun moreInit() 78 | { 79 | loader("Waiting on configurator", 0.34) 80 | while (configurating) 81 | { 82 | Thread.sleep(250) 83 | } 84 | //TODO: Read data from database 85 | if (!GlobalData.first_launch) 86 | { 87 | if (GlobalData.showUpdateNotesOnUpdate) 88 | { 89 | if (getLastPALVersion() != GlobalData.version) 90 | { 91 | setPALVersion() 92 | GlobalData.show_update_note = true 93 | } 94 | } 95 | 96 | loader("Getting previous configuration", 0.4) 97 | retreiveConfig() 98 | //Get AHK Scripts 99 | loader("Getting AHK Scripts", 0.5) 100 | getAHKScripts() 101 | 102 | val f = getPrimaryPoE() 103 | if (f != "") 104 | { 105 | GlobalData.primaryPoEFile = File(f) 106 | } 107 | } 108 | 109 | loader("Checking for Updates to PAL", 0.5) 110 | //getUpdate(GlobalData.install_dir) 111 | 112 | getUpdate() 113 | 114 | loader("No updates to PAL!", 0.6) 115 | 116 | 117 | loader("Downloading Addons from Repository", 0.6) 118 | // Check if a Token has been set 119 | if (GlobalData.github_token == "" || !GlobalData.gitHubAPIEnabled) 120 | { 121 | staticRepoAddons() 122 | } 123 | else 124 | { 125 | // TODO: Parse repos from DB 126 | // Use GitHub repo 127 | val addons = getAddonsFromGHRepo("POE-Addon-Launcher/server") 128 | if (addons == null) 129 | { 130 | staticRepoAddons() 131 | } 132 | else 133 | { 134 | GlobalData.set_of_addons.addAll(addons) 135 | } 136 | } 137 | loader("Initializing: Addon List", 0.8) 138 | CoreApplication.controller.initAddonListView() 139 | 140 | // Get Installed Addons 141 | loader("Checking for your installed addons", 0.9) 142 | getInstalledAddons() 143 | 144 | loader("Initialization done!", 1.0) 145 | Loader.controller.close() 146 | Platform.runLater { CoreApplication.stage.opacity = 1.0 } 147 | } 148 | 149 | fun staticRepoAddons() 150 | { 151 | logger.debug { "Getting addons from static file!" } 152 | // Use static repo 153 | // https://raw.githubusercontent.com/POE-Addon-Launcher/server/master/addons.json 154 | val addonsJson = File("${GlobalData.pal_folder}${File.separator}addons.json") 155 | deleteFile(addonsJson) 156 | 157 | FileUtils.copyURLToFile(URL("https://raw.githubusercontent.com/POE-Addon-Launcher/server/master/addons.json"), addonsJson) 158 | 159 | val objectMapper = initObjectMapper() 160 | GlobalData.addToListOfAddons(objectMapper.readValue(addonsJson, Array::class.java)) 161 | deleteFile(addonsJson) 162 | 163 | // Extra sanity check! 164 | GlobalData.checkAddonListForDuplicates() 165 | } -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/SystemHandling/LaunchHandling.kt: -------------------------------------------------------------------------------- 1 | package PAL2.SystemHandling 2 | 3 | import GlobalData 4 | import PAL2.Database.getExternalsOnLaunchCommands 5 | import PAL2.Database.getInstallDir 6 | import PAL2.Database.getLaunchCommand 7 | import PAL2.Database.getRunOnLaunchCommands 8 | import kotlinx.coroutines.GlobalScope 9 | import kotlinx.coroutines.launch 10 | import mu.KotlinLogging 11 | import java.awt.Desktop.getDesktop 12 | import java.io.File 13 | import java.io.IOException 14 | import java.net.URI 15 | 16 | 17 | private val logger = KotlinLogging.logger {} 18 | /** 19 | * 20 | */ 21 | fun launchAddon(aid: Int) 22 | { 23 | // Grab launch command from DB 24 | val lc = getLaunchCommand(aid) 25 | 26 | when (aid) 27 | { 28 | 14 -> procurementHandler(aid) 29 | 12 -> runPathOfMaps(lc) 30 | 15 -> leagueOverlayHandler(aid, lc) 31 | else -> defaultHandler(aid, lc) 32 | } 33 | } 34 | 35 | fun defaultHandler(aid: Int, lc: String) 36 | { 37 | GlobalScope.launch { 38 | // Check for exceptions: ? "SET_AHK_FOLDER" etc 39 | if (lc == "?") 40 | { 41 | logger.error { "NO LAUNCH COMMAND SET!" } 42 | } 43 | else if (lc == "SET_AHK_FOLDER") 44 | { 45 | logger.error { "SET AHK FOLDER!" } 46 | } 47 | else 48 | { 49 | val dir = getInstallDir(aid) 50 | if (dir != null) 51 | { 52 | Runtime.getRuntime().exec(lc, null, dir) 53 | } 54 | else 55 | { 56 | Runtime.getRuntime().exec(lc) 57 | } 58 | 59 | } 60 | } 61 | 62 | 63 | } 64 | 65 | fun runPathOfMaps(lc: String) 66 | { 67 | GlobalScope.launch { 68 | Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler $lc") 69 | } 70 | 71 | } 72 | 73 | fun leagueOverlayHandler(aid: Int, launch_command: String) 74 | { 75 | GlobalScope.launch { 76 | val dir = getInstallDir(aid) 77 | if (dir != null) 78 | { 79 | Runtime.getRuntime().exec(launch_command, null, dir) 80 | } 81 | } 82 | } 83 | 84 | fun procurementHandler(aid: Int) 85 | { 86 | GlobalScope.launch { 87 | val f = getInstallDir(aid) 88 | 89 | if (f != null) 90 | { 91 | var exe = "" 92 | for (_f in f.listFiles()) 93 | { 94 | if (_f.name == "Procurement.exe") 95 | { 96 | exe = _f.path 97 | } 98 | } 99 | 100 | if (exe != "") 101 | Runtime.getRuntime().exec("\"$exe\"", null, f) 102 | } 103 | 104 | } 105 | } 106 | 107 | private fun launch_poe(exe: String) 108 | { 109 | logger.debug { "CALLED: LAUNCH_POE(exe) | exe = \'$exe\'" } 110 | 111 | if (exe.contains("PathOfExileSteam.exe") || exe.contains("PathOfExile_x64Steam.exe")) 112 | { 113 | logger.debug { "Attempting to run Steam PoE" } 114 | runSteamPoE() 115 | } 116 | else if (exe.contains("PathOfExile.exe") || exe.contains("PathOfExile_x64.exe")) 117 | { 118 | val dir: String 119 | val executable: String 120 | if (exe.contains("PathOfExile_x64.exe")) 121 | { 122 | logger.debug { "Found PathOfExile_x64.exe" } 123 | executable = "PathOfExile_x64.exe" 124 | dir = exe.replace(executable, "") 125 | logger.debug { "dir = $dir" } 126 | } 127 | else 128 | { 129 | logger.debug { "ELSE case" } 130 | executable = "PathOfExile.exe" 131 | dir = exe.replace(executable, "") 132 | logger.debug { "dir = $dir" } 133 | } 134 | try 135 | { 136 | logger.debug { "Runtime command: \"$exe\", NULL, ${File(dir)}" } 137 | logger.debug { "dir = $dir" } 138 | Runtime.getRuntime().exec("\"$exe\"", null, File(dir)) 139 | } 140 | catch (e: IOException) 141 | { 142 | logger.error { e.printStackTrace() } 143 | } 144 | 145 | } 146 | } 147 | 148 | fun runSteamPoE() 149 | { 150 | val desktop = getDesktop() 151 | val steamProtocol = URI("steam://run/238960") 152 | desktop.browse(steamProtocol) 153 | } 154 | 155 | fun launchAddons() 156 | { 157 | if (GlobalData.launch_addons) 158 | { 159 | val arr = getRunOnLaunchCommands() 160 | if (arr != null) 161 | { 162 | for (lc in arr) 163 | { 164 | GlobalScope.launch { 165 | Runtime.getRuntime().exec(lc) 166 | } 167 | } 168 | } 169 | GlobalData.launch_addons = false 170 | } 171 | } 172 | 173 | @Deprecated("Uses different method to launch AHKs now.") 174 | fun launchAHKScripts() 175 | { 176 | var arr = GlobalData.ahk_scripts 177 | for (ahk in arr) 178 | { 179 | GlobalScope.launch { 180 | Runtime.getRuntime().exec(createAHKLaunchCommand(ahk)) 181 | } 182 | } 183 | } 184 | 185 | fun createAHKLaunchCommand(ahk_file: String): String 186 | { 187 | val ahk_folder = GlobalData.ahkFolder 188 | if (ahk_folder.path != "") 189 | { 190 | if (ahk_folder.exists()) 191 | { 192 | val stringBuilder = StringBuilder() 193 | stringBuilder.append("\"") 194 | stringBuilder.append(ahk_folder.path) 195 | stringBuilder.append(File.separator) 196 | stringBuilder.append("autohotkey.exe") 197 | stringBuilder.append("\" ") 198 | 199 | stringBuilder.append("\"") 200 | stringBuilder.append(ahk_file) 201 | stringBuilder.append("\" ") 202 | 203 | logger.debug { stringBuilder.toString() } 204 | return stringBuilder.toString() 205 | } 206 | else 207 | { 208 | return "SET_AHK_FOLDER" 209 | } 210 | } 211 | return "SET_AHK_FOLDER" 212 | } 213 | 214 | fun launchPoE() 215 | { 216 | if (GlobalData.steam_poe) 217 | { 218 | GlobalScope.launch { 219 | GlobalScope.launch { 220 | launchAddons() 221 | } 222 | GlobalScope.launch { 223 | launchExternals() 224 | } 225 | GlobalScope.launch { 226 | runSteamPoE() 227 | } 228 | } 229 | return 230 | } 231 | 232 | if (GlobalData.poeLocations.size == 0) 233 | { 234 | logger.error { "No path of exile directories are known." } 235 | } 236 | else if (GlobalData.poeLocations.size == 1) 237 | { 238 | GlobalScope.launch { 239 | GlobalScope.launch { 240 | launchAddons() 241 | } 242 | GlobalScope.launch { 243 | launchExternals() 244 | } 245 | GlobalScope.launch { 246 | launch_poe(GlobalData.poeLocations[0]) 247 | } 248 | } 249 | } 250 | else 251 | { 252 | val f = GlobalData.primaryPoEFile 253 | if (f != null) 254 | { 255 | if (f.exists()) 256 | { 257 | GlobalScope.launch { 258 | GlobalScope.launch { 259 | launchAddons() 260 | } 261 | GlobalScope.launch { 262 | launchExternals() 263 | } 264 | GlobalScope.launch { 265 | launch_poe(f.path) 266 | } 267 | } 268 | } 269 | } 270 | } 271 | } 272 | 273 | fun launchExternals() 274 | { 275 | if (!GlobalData.launch_externals) 276 | return 277 | 278 | val arr = getExternalsOnLaunchCommands() ?: return 279 | 280 | if (arr.isEmpty()) 281 | return 282 | 283 | for (str in arr) 284 | { 285 | GlobalScope.launch { Runtime.getRuntime().exec(str) } 286 | } 287 | 288 | GlobalData.launch_externals = false 289 | } 290 | -------------------------------------------------------------------------------- /src/main/kotlin/PAL2/SystemHandling/WebHandling.kt: -------------------------------------------------------------------------------- 1 | package PAL2.SystemHandling 2 | 3 | import GUI.DownloadsAnchor 4 | import GlobalData 5 | import SystemHandling.deleteFile 6 | import javafx.scene.image.Image 7 | import kotlinx.coroutines.GlobalScope 8 | import kotlinx.coroutines.delay 9 | import kotlinx.coroutines.launch 10 | import java.io.BufferedInputStream 11 | import java.io.BufferedOutputStream 12 | import java.io.File 13 | import java.io.FileOutputStream 14 | import java.net.HttpURLConnection 15 | import java.net.URL 16 | 17 | /** 18 | * 19 | */ 20 | class FileDownloader 21 | { 22 | private var maxSize = 0 23 | private var currSize = 0 24 | private lateinit var downloadBar: DownloadsAnchor 25 | private var blockSize = 128 26 | 27 | fun downloadFile(url: URL, temp_download_dir: File, block: Int, image: Image): File 28 | { 29 | var store_location = temp_download_dir 30 | if (store_location.isDirectory) 31 | { 32 | val splits = url.file.split("/") 33 | store_location = File("${store_location.path}${File.separator}${splits[splits.size-1]}") 34 | } 35 | 36 | if (store_location.exists()) 37 | deleteFile(store_location) 38 | 39 | blockSize = block 40 | 41 | val httpConnection = url.openConnection() as HttpURLConnection 42 | httpConnection.addRequestProperty("User-Agent", "Mozilla/4.0") 43 | val filesize = httpConnection.contentLength 44 | maxSize = filesize 45 | 46 | val input = BufferedInputStream(httpConnection.inputStream) 47 | val output = FileOutputStream(store_location) 48 | 49 | val buff = BufferedOutputStream(output, blockSize) 50 | 51 | val data = ByteArray(blockSize) 52 | var downloadedFileSize = 0 53 | var x: Int 54 | 55 | val splits = url.file.split("/") 56 | downloadBar = DownloadsAnchor(splits[splits.size-1]) 57 | downloadBar.setImg(image) 58 | downloadBar.attachToListView() 59 | updateProgress(0, maxSize, 0.0) 60 | functionCallerOnDelay() 61 | 62 | while (true) 63 | { 64 | x = input.read(data, 0, blockSize) 65 | 66 | if (x < 0) 67 | break 68 | 69 | downloadedFileSize += x 70 | currSize = downloadedFileSize 71 | 72 | buff.write(data, 0, x) 73 | } 74 | 75 | input.close() 76 | buff.close() 77 | output.close() 78 | 79 | return store_location 80 | } 81 | 82 | fun downloadFileAndInstall(url: URL, temp_download_dir: File, block: Int, image: Image, aid: Int) 83 | { 84 | var store_location = temp_download_dir 85 | if (store_location.isDirectory) 86 | { 87 | val splits = url.file.split("/") 88 | store_location = File("${store_location.path}${File.separator}${splits[splits.size-1]}") 89 | } 90 | 91 | if (store_location.exists()) 92 | deleteFile(store_location) 93 | 94 | blockSize = block 95 | 96 | val httpConnection = url.openConnection() as HttpURLConnection 97 | httpConnection.addRequestProperty("User-Agent", "Mozilla/4.0") 98 | val filesize = httpConnection.contentLength 99 | maxSize = filesize 100 | 101 | val input = BufferedInputStream(httpConnection.inputStream) 102 | val output = FileOutputStream(store_location) 103 | 104 | val buff = BufferedOutputStream(output, blockSize) 105 | 106 | val data = ByteArray(blockSize) 107 | var downloadedFileSize = 0 108 | var x: Int 109 | 110 | val splits = url.file.split("/") 111 | downloadBar = DownloadsAnchor(splits[splits.size-1]) 112 | downloadBar.setImg(image) 113 | downloadBar.attachToListView() 114 | updateProgress(0, maxSize, 0.0) 115 | functionCallerOnDelay() 116 | 117 | while (true) 118 | { 119 | x = input.read(data, 0, blockSize) 120 | 121 | if (x < 0) 122 | break 123 | 124 | downloadedFileSize += x 125 | currSize = downloadedFileSize 126 | 127 | buff.write(data, 0, x) 128 | } 129 | 130 | input.close() 131 | buff.close() 132 | output.close() 133 | 134 | 135 | // Download finished run installation. 136 | val addon = GlobalData.getAddonByID(aid) 137 | if (addon != null) 138 | { 139 | val installer = InstallHandler(addon, store_location) 140 | } 141 | } 142 | 143 | fun functionCallerOnDelay() 144 | { 145 | GlobalScope.launch { 146 | var timeSpent = 0.0 147 | while (currSize < maxSize) 148 | { 149 | val down_speed = (currSize/1024/1024/timeSpent) 150 | updateProgress(currSize, maxSize, Math.round(down_speed * 100.0) / 100.0) 151 | delay(250) 152 | timeSpent += 0.25 153 | } 154 | updateProgress(maxSize, maxSize, 0.0) 155 | } 156 | } 157 | 158 | fun updateProgress(min_size: Int, maxSize: Int, downSpeed: Double) 159 | { 160 | //AnchorTest.controller.updateProgressbar(d / 100.0) 161 | downloadBar.setProgress((min_size.toDouble() / maxSize.toDouble())) 162 | downloadBar.addDownloadText("[${min_size/1024/1024}MB / ${maxSize/1024/1024}MB] ~${downSpeed}MB/s") 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/resources/Configurator.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |