├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── installer │ │ ├── logo.png │ │ └── local_info.json │ ├── lets-encrypt-r3.pem │ ├── letsencryptauthorityx3.pem │ └── langs │ │ ├── EN.lang │ │ └── FR.lang │ └── java │ └── fr │ └── minecraftforgefrance │ ├── common │ ├── IInstallRunner.java │ ├── Logger.java │ ├── Localization.java │ ├── LibEntry.java │ ├── EnumOS.java │ ├── FileEntry.java │ ├── InstallFrame.java │ ├── FileChecker.java │ ├── RemoteInfoReader.java │ ├── DownloadUtils.java │ └── ProcessInstall.java │ ├── installer │ ├── Installer.java │ ├── LocalInfoReader.java │ ├── CreditFrame.java │ ├── SuccessFrame.java │ ├── OptionFrame.java │ └── InstallerFrame.java │ ├── plusplus │ └── PlusPlusGame.java │ └── updater │ └── Updater.java ├── .gitignore ├── README.md ├── proguard.pro ├── LICENCE.txt ├── php ├── index_sync.php └── index_preset.php ├── gradlew.bat └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FFMT/ModPackInstaller/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/installer/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FFMT/ModPackInstaller/HEAD/src/main/resources/installer/logo.png -------------------------------------------------------------------------------- /src/main/resources/installer/local_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "remoteUrl": "http://dl.mcnanotech.fr/FFMT/installer/demo/remote_info.json" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | .gradle/ 3 | repos/ 4 | build/ 5 | .classpath 6 | .project 7 | .settings/ 8 | /repo/ 9 | 10 | Thumbs.db 11 | *~ 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ModPackInstaller 2 | ================ 3 | 4 | A customisable, simple to use, modpack installer and updater ! 5 | 6 | Un installateur et un système de mise à jour facile à utiliser et à modifier ! -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/IInstallRunner.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | public interface IInstallRunner 4 | { 5 | void onFinish(); 6 | 7 | boolean shouldDownloadLib(); 8 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip 6 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/Logger.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | public class Logger 4 | { 5 | public final static boolean DEBUG = Boolean.valueOf(System.getProperty("fr.minecraftforgefrance.installer.debug", "false")); 6 | 7 | public static void info(String info) 8 | { 9 | if(DEBUG) 10 | { 11 | System.out.println(info); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /proguard.pro: -------------------------------------------------------------------------------- 1 | -libraryjars /lib/rt.jar 2 | 3 | -dontoptimize 4 | -dontobfuscate 5 | -dontpreverify 6 | -dontwarn javax.annotation.** 7 | -dontwarn javax.inject.** 8 | -dontwarn ** 9 | 10 | -keep class fr.minecraftforgefrance.installer.Installer { 11 | public static void main(java.lang.String[]); 12 | } 13 | 14 | -keepclassmembers class * { 15 | static final % *; 16 | static final java.lang.String *; 17 | } 18 | 19 | -keep public class fr.minecraftforgefrance.**.** { 20 | public protected ; 21 | } 22 | 23 | -keepclassmembers enum * { 24 | public static **[] values(); 25 | public static ** valueOf(java.lang.String); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/Localization.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import java.util.Locale; 4 | 5 | import utybo.minkj.locale.MinkJ; 6 | 7 | public class Localization 8 | { 9 | public static final MinkJ LANG = new MinkJ(); 10 | 11 | public static void init() 12 | { 13 | if(!Logger.DEBUG) 14 | { 15 | LANG.mute(); 16 | } 17 | 18 | try 19 | { 20 | LANG.loadTranslationsFromFile(Locale.FRENCH, Localization.class.getResourceAsStream("/langs/FR.lang")); 21 | LANG.loadTranslationsFromFile(Locale.ENGLISH, Localization.class.getResourceAsStream("/langs/EN.lang")); 22 | } 23 | catch(Exception ex) 24 | { 25 | ex.printStackTrace(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | English : 2 | 3 | This software has been created by robin4002 and kevin_68 to allow 4 | its users to install a minecraft modpack and keep it up-to-date. 5 | You may use, modify, share our work only if you give the credit to 6 | the authors, don't modify the "credits" button and of course the 7 | software should not be used commercially (that includes adf.ly). 8 | If you contribute to the software your name will be mentioned in 9 | the credits. 10 | 11 | 12 | French : 13 | 14 | Ce logiciel a été créé par robin4002 et kevin_68 afin de fournir 15 | une solution pour installer facilement un modpack minecraft et le 16 | garder à jour. Vous pouvez l'utiliser, le modifier et le distribuer 17 | à condition de citer les auteurs et de ne pas supprimer ou modifier 18 | le bouton de crédit et également, de ne pas l'utiliser commercialement 19 | (cela inclut adf.ly). Si vous contribuez au logiciel, votre nom sera 20 | ajouté dans la liste des crédits. -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/installer/Installer.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.installer; 2 | 3 | import javax.swing.UIManager; 4 | 5 | import fr.minecraftforgefrance.common.Localization; 6 | import fr.minecraftforgefrance.common.RemoteInfoReader; 7 | 8 | public class Installer 9 | { 10 | public static InstallerFrame frame; 11 | 12 | public static void main(String[] args) 13 | { 14 | try 15 | { 16 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 17 | } 18 | catch(Exception e) 19 | { 20 | e.printStackTrace(); 21 | } 22 | Localization.init(); 23 | RemoteInfoReader.instance = new RemoteInfoReader(LocalInfoReader.instance().getRemoteUrl()); 24 | if(!RemoteInfoReader.instance().init()) 25 | { 26 | return; 27 | } 28 | frame = new InstallerFrame(); 29 | frame.run(); 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/LibEntry.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import java.io.File; 4 | 5 | public class LibEntry 6 | { 7 | private final String url; 8 | private final String name; 9 | private final File dest; 10 | private final File packDest; 11 | private final long size; 12 | private final boolean isXZ; 13 | 14 | public LibEntry(String url, String name, File dest, File packDest, long size, boolean isXZ) 15 | { 16 | this.url = url; 17 | this.name = name; 18 | this.dest = dest; 19 | this.packDest = packDest; 20 | this.size = size; 21 | this.isXZ = isXZ; 22 | } 23 | 24 | public String getName() 25 | { 26 | return name; 27 | } 28 | 29 | public String getUrl() 30 | { 31 | return url; 32 | } 33 | 34 | public File getDest() 35 | { 36 | return dest; 37 | } 38 | 39 | public File getPackDest() 40 | { 41 | return packDest; 42 | } 43 | 44 | public long getSize() 45 | { 46 | return size; 47 | } 48 | 49 | public boolean isXZ() 50 | { 51 | return isXZ; 52 | } 53 | } -------------------------------------------------------------------------------- /php/index_sync.php: -------------------------------------------------------------------------------- 1 | $value) 28 | { 29 | $stat = stat($value); 30 | if($index != 0) 31 | { 32 | echo ", "."\n"; 33 | } 34 | echo ' {'."\n"; 35 | echo ' "name":"'.htmlentities($value).'",'."\n"; 36 | if(is_dir($value)){ 37 | echo ' "md5":"'.md5($value).'",'."\n"; 38 | echo ' "size":"0"'."\n"; 39 | }else{ 40 | echo ' "md5":"'.md5_file($value).'",'."\n"; 41 | echo ' "size":"'.$stat['size'].'"'."\n"; 42 | } 43 | echo ' }'; 44 | $index++; 45 | } 46 | echo "\n".']'; 47 | 48 | ?> 49 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/installer/LocalInfoReader.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.installer; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | 8 | import javax.swing.JOptionPane; 9 | 10 | import argo.jdom.JdomParser; 11 | import argo.jdom.JsonRootNode; 12 | import argo.saj.InvalidSyntaxException; 13 | 14 | import com.google.common.base.Charsets; 15 | import com.google.common.base.Throwables; 16 | 17 | public class LocalInfoReader 18 | { 19 | private static final LocalInfoReader INSTANCE = new LocalInfoReader(); 20 | public final JsonRootNode data; 21 | 22 | public LocalInfoReader() 23 | { 24 | JdomParser parser = new JdomParser(); 25 | InputStream in = this.getClass().getResourceAsStream("/installer/local_info.json"); 26 | try 27 | { 28 | data = parser.parse(new InputStreamReader(in, Charsets.UTF_8)); 29 | } 30 | catch(IOException e) 31 | { 32 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotfindlocalinfo"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 33 | throw Throwables.propagate(e); 34 | } 35 | catch(InvalidSyntaxException e) 36 | { 37 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.invalidjson"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 38 | throw Throwables.propagate(e); 39 | } 40 | } 41 | 42 | public String getRemoteUrl() 43 | { 44 | return data.getStringValue("remoteUrl"); 45 | } 46 | 47 | public static LocalInfoReader instance() 48 | { 49 | return INSTANCE; 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/resources/lets-encrypt-r3.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw 3 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 4 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw 5 | WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg 6 | RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 7 | AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP 8 | R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx 9 | sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm 10 | NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg 11 | Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG 12 | /kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC 13 | AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB 14 | Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA 15 | FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw 16 | AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw 17 | Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB 18 | gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W 19 | PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl 20 | ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz 21 | CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm 22 | lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 23 | avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 24 | yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O 25 | yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids 26 | hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ 27 | HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv 28 | MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX 29 | nLRbwHOoq7hHwg== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/EnumOS.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import java.io.File; 4 | import java.util.Locale; 5 | 6 | public enum EnumOS 7 | { 8 | WINDOWS, 9 | MACOS, 10 | UNIX, 11 | UNKNOWN; 12 | 13 | public static EnumOS getPlatform() 14 | { 15 | String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); 16 | if(osName.contains("win")) 17 | { 18 | return WINDOWS; 19 | } 20 | if(osName.contains("mac")) 21 | { 22 | return MACOS; 23 | } 24 | if(osName.contains("solaris") || osName.contains("sunos") || osName.contains("linux") || osName.contains("unix")) 25 | { 26 | return UNIX; 27 | } 28 | return UNKNOWN; 29 | } 30 | 31 | public static File getMinecraftDefaultDir() 32 | { 33 | String userHome = System.getProperty("user.home", "."); 34 | switch(getPlatform()) 35 | { 36 | case UNIX: 37 | return new File(userHome, ".minecraft"); 38 | case WINDOWS: 39 | String applicationData = System.getenv("APPDATA"); 40 | String folder = applicationData != null ? applicationData : userHome; 41 | return new File(folder, ".minecraft"); 42 | case MACOS: 43 | return new File(new File(new File(userHome, "Library"), "Application Support"), "minecraft"); 44 | default: 45 | return new File(userHome, "minecraft"); 46 | } 47 | } 48 | 49 | public static String getJavaExecutable() 50 | { 51 | String separator = System.getProperty("file.separator"); 52 | String path = System.getProperty("java.home") + separator + "bin" + separator; 53 | if((getPlatform() == WINDOWS) && (new File(path + "javaw.exe").isFile())) 54 | { 55 | return path + "javaw.exe"; 56 | } 57 | return path + "java"; 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/FileEntry.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import java.net.URL; 4 | 5 | public class FileEntry 6 | { 7 | private final URL url; 8 | private final String md5; 9 | private final String path; 10 | private final long size; 11 | 12 | public FileEntry(URL url, String md5, String path, long size) 13 | { 14 | this.url = url; 15 | this.md5 = md5; 16 | this.path = path; 17 | this.size = size; 18 | } 19 | 20 | public FileEntry(String md5, String path, long size) 21 | { 22 | this.url = null; 23 | this.md5 = md5; 24 | this.path = path.replace("\\", "/"); 25 | this.size = size; 26 | } 27 | 28 | public URL getUrl() 29 | { 30 | return url; 31 | } 32 | 33 | public String getMd5() 34 | { 35 | return md5; 36 | } 37 | 38 | public String getPath() 39 | { 40 | return path; 41 | } 42 | 43 | public long getSize() 44 | { 45 | return size; 46 | } 47 | 48 | @Override 49 | public int hashCode() 50 | { 51 | final int prime = 31; 52 | int result = 1; 53 | result = prime * result + ((md5 == null) ? 0 : md5.hashCode()); 54 | return result; 55 | } 56 | 57 | @Override 58 | public boolean equals(Object obj) 59 | { 60 | if(this == obj) 61 | { 62 | return true; 63 | } 64 | if(obj == null) 65 | { 66 | return false; 67 | } 68 | if(getClass() != obj.getClass()) 69 | { 70 | return false; 71 | } 72 | FileEntry other = (FileEntry)obj; 73 | if(md5 == null) 74 | { 75 | if(other.md5 != null) 76 | { 77 | return false; 78 | } 79 | } 80 | else if(!md5.equals(other.md5)) 81 | { 82 | return false; 83 | } 84 | return true; 85 | } 86 | } -------------------------------------------------------------------------------- /php/index_preset.php: -------------------------------------------------------------------------------- 1 | $value) 42 | { 43 | if(!is_dir($value)) 44 | { 45 | $currentCategory = substr($value, 0, strpos($value,"/")); 46 | if($currentCategory != $lastCategory) 47 | { 48 | $lastCategory = $currentCategory; 49 | if($index != 0) 50 | { 51 | echo "\n".' ],'."\n"; 52 | } 53 | echo ' "'.$currentCategory.'": ['."\n"; 54 | $index = 0; 55 | } 56 | if($index != 0) 57 | { 58 | echo ","."\n"; 59 | } 60 | echo ' '.'"'.htmlentities(substr($value, strpos($value,"/") + 1, strlen($value))).'"'; 61 | 62 | $index++; 63 | } 64 | } 65 | echo "\n"; 66 | echo ' ]'."\n"; 67 | echo '}'; 68 | 69 | ?> 70 | -------------------------------------------------------------------------------- /src/main/resources/letsencryptauthorityx3.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw 3 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 4 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1 5 | WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg 6 | RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi 7 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX 8 | NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf 9 | 89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl 10 | Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc 11 | Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz 12 | uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB 13 | AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU 14 | BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB 15 | FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo 16 | SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js 17 | LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF 18 | BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG 19 | AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD 20 | VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB 21 | ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx 22 | A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM 23 | UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2 24 | DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1 25 | eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu 26 | OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw 27 | p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY 28 | 2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0 29 | ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR 30 | PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b 31 | rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/installer/CreditFrame.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.installer; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.awt.Desktop; 6 | import java.awt.Frame; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.net.URI; 10 | 11 | import javax.swing.BoxLayout; 12 | import javax.swing.JButton; 13 | import javax.swing.JDialog; 14 | import javax.swing.JFrame; 15 | import javax.swing.JLabel; 16 | import javax.swing.JOptionPane; 17 | import javax.swing.JPanel; 18 | 19 | import fr.minecraftforgefrance.common.RemoteInfoReader; 20 | 21 | public class CreditFrame extends JDialog 22 | { 23 | private static final long serialVersionUID = 1L; 24 | 25 | public CreditFrame(Frame parent) 26 | { 27 | super(parent); 28 | this.setTitle(LANG.getTranslation("title.credits")); 29 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 30 | this.setResizable(false); 31 | this.setModalityType(ModalityType.APPLICATION_MODAL); 32 | 33 | JPanel panel = new JPanel(); 34 | panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 35 | 36 | JButton sponsorButton = new JButton(LANG.getTranslation("scr.btn.mffwebsite")); 37 | sponsorButton.setAlignmentX(CENTER_ALIGNMENT); 38 | sponsorButton.setAlignmentY(CENTER_ALIGNMENT); 39 | sponsorButton.addActionListener(new ActionListener() 40 | { 41 | @Override 42 | public void actionPerformed(ActionEvent e) 43 | { 44 | try 45 | { 46 | Desktop.getDesktop().browse(new URI("http://www.minecraftforgefrance.fr")); 47 | } 48 | catch(Exception ex) 49 | { 50 | JOptionPane.showMessageDialog(CreditFrame.this, String.format(LANG.getTranslation("err.cannotopenurl"), "http://www.minecraftforgefrance.fr"), "Error", JOptionPane.ERROR_MESSAGE); 51 | } 52 | } 53 | }); 54 | 55 | JPanel sponsorPanel = new JPanel(); 56 | sponsorPanel.setLayout(new BoxLayout(sponsorPanel, BoxLayout.X_AXIS)); 57 | sponsorPanel.setAlignmentX(CENTER_ALIGNMENT); 58 | sponsorPanel.setAlignmentY(CENTER_ALIGNMENT); 59 | sponsorPanel.add(sponsorButton); 60 | 61 | JLabel text = new JLabel(); 62 | String creditText = "
"; 63 | if(RemoteInfoReader.instance().hasCredits()) 64 | { 65 | creditText += RemoteInfoReader.instance().getCredits() + "

"; 66 | } 67 | creditText += String.format(LANG.getTranslation("scr.credits.html"), "robin4002", "kevin_68", "utybo", "cpw") + "
"; 68 | text.setText(creditText); 69 | text.setAlignmentX(CENTER_ALIGNMENT); 70 | text.setAlignmentY(CENTER_ALIGNMENT); 71 | 72 | panel.add(text); 73 | panel.add(sponsorPanel); 74 | this.add(panel); 75 | this.pack(); 76 | this.setLocationRelativeTo(parent); 77 | } 78 | } -------------------------------------------------------------------------------- /src/main/resources/langs/EN.lang: -------------------------------------------------------------------------------- 1 | ################################################# 2 | # ------------------ CREDITS ------------------ # 3 | # # 4 | # English translations by utybo & the FFMT # 5 | # This file is used by MinkJ (Localization API) # 6 | # # 7 | # # 8 | # # 9 | # ------------------- MODEL ------------------- # 10 | # # 11 | # Comments start with a # # 12 | # The model is key.here=Translation here # 13 | # Do not put any space beside the = # 14 | ################################################# 15 | 16 | 17 | ############################### 18 | # CONSOLE/SCREEN MESSAGES # 19 | ############################### 20 | # Errors 21 | err.invalidurl=Invalid URL : %s 22 | err.mcdirmissing=The Minecraft dir is missing, please run the official Minecraft launcher 23 | err.mcprofilemissing=Minecraft launcher profile not found, please run the official Minecraft launcher 24 | err.mcprofilecorrupted=The launcher profile file is corrupted. Please re-run the minecraft launcher to fix it! 25 | err.networkerror=Network error, please check your Internet connection 26 | err.noFile=There are no file to download ! 27 | err.cannotdeletefile=Could not delete file 28 | err.cannotdownload=Could not download 29 | err.cannotwriteversion=There was a problem while writing the launcher version. Is it write protected? 30 | err.cannotwriteprofile=There was a problem while writing the launcher profile. Is it write protected? 31 | err.cannotreadremote=Could not read remote info, please check your Internet connection 32 | err.jsoninvalid=The file remote_profile.json isn't valid ! 33 | err.cannotopenurl=An error occurred while opening URL : %s 34 | err.bigimage=The image is too big! 35 | err.cannotfindlocalinfo=Local information not found, file /installer/local_info.json is missing in the jar 36 | err.invalidjson=The local json is not valid, please check it with this website : http://jsonlint.com/ 37 | err.erroredprofile=Errored profile, please re-install it! 38 | err.runminecraft=can't run the game, run it manually 39 | 40 | # Tasks done 41 | file.unpacked.success=Successfully unpacked %s 42 | installation.success=Installation finished ! 43 | update.finished.success=Update finished successfully, please restart the game ! 44 | 45 | # Processes running 46 | proc.unpackingfile=Unpacking packed file 47 | proc.downloadinglib=Downloading library %s 48 | proc.downloadingmods=Downloading mods and config files... 49 | proc.startingupdater=Starting the Updater... 50 | proc.verifyfiles=Checking files ... 51 | 52 | 53 | 54 | #################### 55 | # SCREEN STUFF # 56 | #################### 57 | # Titles 58 | title.credits=Credits 59 | title.libs=Downloading and extracting libraries... 60 | title.installer=%s Installer 61 | title.options=Options 62 | title.preset=Downloading presets... 63 | 64 | 65 | # Buttons 66 | scr.btn.mffwebsite=MFF website 67 | scr.btn.install=Install 68 | scr.btn.credits=Credits 69 | scr.btn.webSite=Web site 70 | scr.btn.options=Options 71 | scr.btn.exit=Exit 72 | scr.btn.run=Run Minecraft 73 | 74 | # CREDITS 75 | scr.credits.html=Installer created by %s with the help of %s (GUI) and %s (translation system)
Thank to %s, creator of the Forge installer that served as an inspiration 76 | 77 | # Options 78 | option.mcDir.info=Minecraft folder : 79 | option.mcDir.select=Select an alternative minecraft directory 80 | option.modpackDir.info=The modpack will be installed in the following folder : 81 | option.confirm=Confirm 82 | option.folder.notValid=This folder ins't valid ! 83 | option.preset=Preset : 84 | 85 | 86 | 87 | ##################### 88 | # MISCELLANEOUS # 89 | ##################### 90 | misc.finishing=Finishing... 91 | misc.speed.ko=Speed : %s ko/s 92 | misc.speed.mo=Speed : %s mo/s 93 | misc.missing=Missing 94 | misc.failed=Failed 95 | misc.error=Error 96 | misc.cancel=Cancel 97 | misc.success=Success 98 | 99 | 100 | 101 | ############# 102 | # ????? # 103 | ############# 104 | egg.name=The ++ Game 105 | egg.level=Level 106 | egg.score=Score 107 | egg.untilnextlevel=Clicks until next level -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/InstallFrame.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.io.File; 6 | 7 | import javax.swing.BoxLayout; 8 | import javax.swing.JFrame; 9 | import javax.swing.JLabel; 10 | import javax.swing.JPanel; 11 | import javax.swing.JProgressBar; 12 | import javax.swing.JTextArea; 13 | 14 | import com.google.common.base.Charsets; 15 | import com.google.common.io.Files; 16 | 17 | import argo.jdom.JdomParser; 18 | import argo.jdom.JsonField; 19 | import argo.jdom.JsonRootNode; 20 | 21 | public class InstallFrame extends JFrame 22 | { 23 | private static final long serialVersionUID = 1L; 24 | 25 | public JProgressBar fileProgressBar; 26 | public JProgressBar fullProgressBar; 27 | private JPanel panel; 28 | public JLabel downloadSpeedLabel; 29 | public JLabel currentDownload; 30 | 31 | private final ProcessInstall installThread; 32 | 33 | public InstallFrame(ProcessInstall install) 34 | { 35 | this.installThread = install; 36 | } 37 | 38 | public void run() 39 | { 40 | this.setTitle(LANG.getTranslation("proc.verifyfiles")); 41 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 42 | this.setResizable(false); 43 | this.setSize(500, 100); 44 | this.setLocationRelativeTo(null); 45 | 46 | this.fileProgressBar = new JProgressBar(0, 10); 47 | this.fileProgressBar.setValue(0); 48 | this.fileProgressBar.setStringPainted(true); 49 | this.fileProgressBar.setIndeterminate(true); 50 | 51 | this.fullProgressBar = new JProgressBar(0, 10); 52 | this.fullProgressBar.setValue(0); 53 | this.fullProgressBar.setStringPainted(true); 54 | this.fullProgressBar.setIndeterminate(true); 55 | 56 | this.currentDownload = new JLabel(" "); 57 | this.downloadSpeedLabel = new JLabel(" "); 58 | 59 | this.panel = new JPanel(); 60 | this.panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 61 | this.panel.add(this.currentDownload); 62 | this.panel.add(this.fileProgressBar); 63 | this.panel.add(this.fullProgressBar); 64 | this.panel.add(this.downloadSpeedLabel); 65 | 66 | if(RemoteInfoReader.instance().hasChangeLog()) 67 | { 68 | JTextArea area = new JTextArea(); 69 | area.setLineWrap(true); 70 | area.setWrapStyleWord(true); 71 | area.setBounds(4, 2, 492, 150); 72 | this.getChangeLog(area); 73 | if(!area.getText().isEmpty()) 74 | { 75 | this.setSize(500, 250); 76 | this.panel.add(area); 77 | } 78 | } 79 | this.setContentPane(this.panel); 80 | this.setVisible(true); 81 | 82 | new Thread(this.installThread).start(); 83 | } 84 | 85 | private void getChangeLog(JTextArea area) 86 | { 87 | String currentVersion = null; 88 | File modpackInfo = new File(installThread.modPackDir, RemoteInfoReader.instance().getModPackName() + ".json"); 89 | if(modpackInfo.exists()) 90 | { 91 | JdomParser jsonParser = new JdomParser(); 92 | try 93 | { 94 | JsonRootNode jsonProfileData = jsonParser.parse(Files.newReader(modpackInfo, Charsets.UTF_8)); 95 | currentVersion = jsonProfileData.getStringValue("currentVersion"); 96 | } 97 | catch(Exception e) 98 | { 99 | 100 | } 101 | } 102 | if(RemoteInfoReader.instance().getChangeLog() != null) 103 | { 104 | for(JsonField field : RemoteInfoReader.instance().getChangeLog().getFieldList()) 105 | { 106 | if(field.getName().getText().equals(currentVersion)) 107 | { 108 | break; 109 | } 110 | area.append(field.getName().getText() + ":\n"); 111 | String[] changes = field.getValue().getText().split("\n"); 112 | for(String change : changes) 113 | { 114 | area.append("- " + change + "\n"); 115 | } 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/installer/SuccessFrame.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.installer; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.awt.BorderLayout; 6 | import java.awt.event.ActionEvent; 7 | import java.awt.event.ActionListener; 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | import javax.swing.JButton; 12 | import javax.swing.JDialog; 13 | import javax.swing.JFrame; 14 | import javax.swing.JLabel; 15 | import javax.swing.JOptionPane; 16 | import javax.swing.JPanel; 17 | 18 | import fr.minecraftforgefrance.common.EnumOS; 19 | 20 | public class SuccessFrame extends JDialog 21 | { 22 | private static final long serialVersionUID = 1L; 23 | private File launcherFile; 24 | 25 | public SuccessFrame() 26 | { 27 | this.setTitle(LANG.getTranslation("misc.success")); 28 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 29 | this.setResizable(false); 30 | this.setSize(300, 100); 31 | this.setLayout(new BorderLayout()); 32 | this.setModalityType(ModalityType.APPLICATION_MODAL); 33 | 34 | JPanel panel = new JPanel(); 35 | JLabel label = new JLabel(LANG.getTranslation("installation.success")); 36 | label.setAlignmentX(CENTER_ALIGNMENT); 37 | label.setAlignmentY(TOP_ALIGNMENT); 38 | panel.add(label); 39 | this.getContentPane().add(panel, BorderLayout.CENTER); 40 | 41 | JPanel buttonPanel = new JPanel(); 42 | JButton exit = new JButton(LANG.getTranslation("scr.btn.exit")); 43 | exit.addActionListener(new ActionListener() 44 | { 45 | @Override 46 | public void actionPerformed(ActionEvent e) 47 | { 48 | SuccessFrame.this.dispose(); 49 | } 50 | }); 51 | buttonPanel.add(exit); 52 | 53 | if(EnumOS.getPlatform() == EnumOS.WINDOWS) 54 | { 55 | if(System.getenv("ProgramW6432").isEmpty()) 56 | { 57 | // 32 bits system 58 | this.launcherFile = new File("C:\\Program Files\\Minecraft\\MinecraftLauncher.exe"); 59 | } 60 | else 61 | { 62 | // 64 bits system 63 | this.launcherFile = new File("C:\\Program Files (x86)\\Minecraft\\MinecraftLauncher.exe"); 64 | } 65 | } 66 | else 67 | { 68 | this.launcherFile = new File(Installer.frame.mcDir.getPath(), "launcher.jar"); 69 | } 70 | 71 | JButton runGame = new JButton(LANG.getTranslation("scr.btn.run")); 72 | runGame.addActionListener(new ActionListener() 73 | { 74 | @Override 75 | public void actionPerformed(ActionEvent e) 76 | { 77 | if(EnumOS.getPlatform() == EnumOS.WINDOWS) 78 | { 79 | try 80 | { 81 | Runtime.getRuntime().exec(SuccessFrame.this.launcherFile.getAbsolutePath()); 82 | } 83 | catch(IOException ex2) 84 | { 85 | ex2.printStackTrace(); 86 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.runminecraft"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 87 | } 88 | } 89 | else 90 | { 91 | try 92 | { 93 | Runtime.getRuntime().exec(EnumOS.getJavaExecutable() + " -jar " + SuccessFrame.this.launcherFile.getAbsolutePath()); 94 | } 95 | catch(IOException ex) 96 | { 97 | ex.printStackTrace(); 98 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.runminecraft"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 99 | } 100 | } 101 | SuccessFrame.this.dispose(); 102 | } 103 | }); 104 | if(SuccessFrame.this.launcherFile.exists()) 105 | { 106 | buttonPanel.add(runGame); 107 | } 108 | this.getContentPane().add(buttonPanel, BorderLayout.SOUTH); 109 | 110 | this.setLocationRelativeTo(null); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/resources/langs/FR.lang: -------------------------------------------------------------------------------- 1 | ################################################# 2 | # ------------------ CRÉDITS ------------------ # 3 | # # 4 | # Traductions françaises par utybo # 5 | # Ce fichier est utilisé par MinkJ (API de lo- # 6 | # calisaton) # 7 | # # 8 | # # 9 | # ------------------ MODÈLE ------------------- # 10 | # # 11 | # Les commentaires commencent par un # # 12 | # Le modèle est cle.ici=Traduction ici # 13 | # Ne pas mettre d'espace à coté du = # 14 | ################################################# 15 | 16 | 17 | ######################################## 18 | # MESSAGES DE LA CONSOLE/À L'ÉCRAN # 19 | ######################################## 20 | # Erreurs 21 | err.invalidurl=URL invalide : %s 22 | err.mcdirmissing=Le dossier de Minecraft est introuvable, veuillez lancer le launcher officiel! 23 | err.mcprofilemissing=Le profile du launcher officiel est introuvable, veuillez lancer le launcher officiel! 24 | err.mcprofilecorrupted=Le fichier du profil est corrompu. Veuillez relancer le launcher officiel pour le réparer! 25 | err.networkerror=Erreur réseau, veuillez vérifier votre connexion internet 26 | err.noFile=Il n'y a aucun fichier à télécharger ! 27 | err.cannotdeletefile=Impossible de supprimer le fichier 28 | err.cannotdownload=Impossible de télécharger 29 | err.cannotwriteversion=Il y a eu un problème lors de l'écriture de la version du launcher. Les permissions en écriture sont-elles accordées? 30 | err.cannotwriteprofile=Il y a eu un problème lors de l'écriture du profil. Les permissions en écriture sont-elles accordées? 31 | err.cannotreadremote=Impossible d'obtenir les informations de l'hôte distant, veuillez vérifier votre connexion Internet 32 | err.jsoninvalid=Le fichier remote_profile.json est invalide ! 33 | err.cannotopenurl=Problème lors de l'ouverture de l'URL : %s 34 | err.bigimage=L'image est trop grande 35 | err.cannotfindlocalinfo=Informations locales introuvables, le fichier /installer/local_info.json est manquant dans le .jar 36 | err.invalidjson=Json local invalide, veuillez le vérifiez avec http://jsonlint.com/ 37 | err.erroredprofile=Profil érroné, veuiller utiliser le launcher officiel pour corriger cela! 38 | err.runminecraft=Impossible de lancer Minecraft, lancez-le manuellement 39 | 40 | # Tâches effectuées 41 | file.unpacked.success=%s à été décompressé 42 | jar.validated.success=Contenu du .jar vérifié avec succès 43 | installation.success=Installation terminée ! 44 | update.finished.success=Mise à jour terminée avec succès, veuillez relancer Minecraft ! 45 | 46 | # Processus 47 | proc.unpackingfile=Décompression du fichier 48 | proc.downloadinglib=Téléchargement de la bibliothèque %s 49 | proc.downloadingmods=Téléchargement des mods et des fichiers de configuration... 50 | proc.startingupdater=Démarrage de l'utilitaire de mise à jour.. 51 | proc.verifyfiles=Vérification des fichiers ... 52 | 53 | 54 | 55 | ######################## 56 | # AFFICHAGE DU GUI # 57 | ######################## 58 | # Nom des fenêtres 59 | title.credits=Crédits 60 | title.libs=Téléchargement et décompression des bibliothèques... 61 | title.installer=Installeur de %s 62 | title.options=Options 63 | title.preset=Téléchargement des préconfigurations... 64 | 65 | # Boutons 66 | scr.btn.mffwebsite=Site web de MFF 67 | scr.btn.install=Installer 68 | scr.btn.credits=Crédits 69 | scr.btn.webSite=Site web 70 | scr.btn.options=Options 71 | scr.btn.exit=Fermer 72 | scr.btn.run=Lancer Minecraft 73 | 74 | # CRÉDITS 75 | scr.credits.html=Installateur créé par %s avec la contribution de %s (interface) et %s (système de traduction)
Remerciement à %s, créateur de l'installateur de Forge qui a servi de source d'inspiration 76 | 77 | # Options 78 | option.mcDir.info=Dossier de Minecraft : 79 | option.mcDir.select=Sélectionner un dossier de jeu alternatif 80 | option.modpackDir.info=Le modpack va être installé dans le dossier suivant : 81 | option.confirm=Confirmer 82 | option.folder.notValid=Ce dossier n'est pas valide ! 83 | option.preset=Préconfiguration : 84 | 85 | 86 | 87 | ############## 88 | # DIVERS # 89 | ############## 90 | misc.finishing=Finitions en cours... 91 | misc.speed.ko=Vitesse : %s ko/s 92 | misc.speed.mo=Vitesse : %s mo/s 93 | misc.missing=Manquant 94 | misc.failed=Échoué 95 | misc.error=Erreur 96 | misc.cancel=Annuler 97 | misc.success=Succès 98 | 99 | 100 | 101 | ############# 102 | # ????? # 103 | ############# 104 | egg.name=The ++ Game 105 | egg.level=Niveau 106 | egg.score=Score 107 | egg.untilnextlevel=Clic avant le prochain niveau 108 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/plusplus/PlusPlusGame.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.plusplus; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.awt.Color; 6 | import java.awt.Dimension; 7 | import java.awt.Toolkit; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | import java.awt.event.WindowEvent; 11 | import java.util.Random; 12 | 13 | import javax.swing.BoxLayout; 14 | import javax.swing.JButton; 15 | import javax.swing.JFrame; 16 | import javax.swing.JPanel; 17 | import javax.swing.JProgressBar; 18 | import javax.swing.JTextField; 19 | 20 | public class PlusPlusGame extends JFrame 21 | { 22 | private static final long serialVersionUID = 1L; 23 | 24 | public static boolean isRunning; 25 | private Random rand = new Random(); 26 | private JProgressBar progressBar; 27 | private int level = 1, currentClick, totalClick = 0, score; 28 | private JTextField field, field2; 29 | private JButton button, button2, button3; 30 | private JPanel pan, lab; 31 | 32 | public PlusPlusGame() 33 | { 34 | isRunning = true; 35 | 36 | this.setTitle(LANG.getTranslation("egg.name")); 37 | this.setSize(400, 200); 38 | this.setLocationRelativeTo(null); 39 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 40 | this.setResizable(false); 41 | 42 | progressBar = new JProgressBar(0, 10); 43 | progressBar.setValue(0); 44 | progressBar.setStringPainted(true); 45 | 46 | button = new JButton(" ++ "); 47 | button.addActionListener(new ActionListener() 48 | { 49 | @Override 50 | public void actionPerformed(ActionEvent e) 51 | { 52 | progress(progressBar.getValue() + 1); 53 | } 54 | }); 55 | 56 | button2 = new JButton(" +++ "); 57 | button2.setEnabled(false); 58 | button2.addActionListener(new ActionListener() 59 | { 60 | @Override 61 | public void actionPerformed(ActionEvent e) 62 | { 63 | progress(progressBar.getValue() + 50); 64 | } 65 | }); 66 | 67 | button3 = new JButton(" ++++ "); 68 | button3.setEnabled(false); 69 | button3.addActionListener(new ActionListener() 70 | { 71 | @Override 72 | public void actionPerformed(ActionEvent e) 73 | { 74 | progress(progressBar.getValue() + 1000); 75 | } 76 | }); 77 | 78 | field = new JTextField(20); 79 | field.setText(LANG.getTranslation("egg.level") + " " + 1); 80 | field.setEditable(false); 81 | 82 | field2 = new JTextField(20); 83 | field2.setText(LANG.getTranslation("egg.untilnextlevel") + " : " + 10); 84 | field2.setEditable(false); 85 | 86 | pan = new JPanel(); 87 | pan.setLayout(new BoxLayout(pan, BoxLayout.PAGE_AXIS)); 88 | pan.setBackground(Color.ORANGE); 89 | pan.add(progressBar); 90 | lab = new JPanel(); 91 | lab.setBackground(Color.ORANGE); 92 | lab.add(button); 93 | lab.add(button2); 94 | lab.add(button3); 95 | pan.add(lab); 96 | pan.add(field); 97 | pan.add(field2); 98 | this.setContentPane(pan); 99 | 100 | this.setVisible(true); 101 | } 102 | 103 | public void progress(int i) 104 | { 105 | if(progressBar.getValue() == progressBar.getMaximum()) 106 | { 107 | progressBar.setValue(0); 108 | progressBar.setMaximum(progressBar.getMaximum() * 2); 109 | level++; 110 | currentClick = 0; 111 | Color color = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)); 112 | pan.setBackground(color); 113 | lab.setBackground(color); 114 | if(level == 10) 115 | { 116 | button2.setEnabled(true);; 117 | } 118 | if(level == 20) 119 | { 120 | button3.setEnabled(true);; 121 | } 122 | } 123 | else 124 | { 125 | currentClick += i - progressBar.getValue(); 126 | score += i - progressBar.getValue(); 127 | progressBar.setValue(i); 128 | totalClick++; 129 | } 130 | 131 | field.setText(LANG.getTranslation("egg.level") + " " + level + " | " + LANG.getTranslation("egg.score") + " : " + score); 132 | field2.setText(LANG.getTranslation("egg.untilnextlevel") + " : " + (int)(10 * Math.pow(2, level - 1)) + " | Current : " + currentClick + " | Total clicks : " + totalClick); 133 | Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); 134 | int x = rand.nextInt(dim.width - 400); 135 | int y = rand.nextInt(dim.height - 200); 136 | this.setLocation(x, y); 137 | } 138 | 139 | protected void processWindowEvent(WindowEvent e) 140 | { 141 | if(e.getID() == WindowEvent.WINDOW_CLOSING) 142 | { 143 | isRunning = false; 144 | } 145 | super.processWindowEvent(e); 146 | } 147 | } -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/FileChecker.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.math.BigInteger; 7 | import java.security.DigestInputStream; 8 | import java.security.MessageDigest; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | import argo.jdom.JsonField; 14 | 15 | public class FileChecker 16 | { 17 | public List remoteList = Collections.synchronizedList(new ArrayList()); 18 | public List syncList = new ArrayList(); 19 | public List checkDir = new ArrayList(); 20 | public List localList = new ArrayList(); 21 | 22 | public List missingList; 23 | public List outdatedList; 24 | 25 | private final File modPackDir; 26 | 27 | public FileChecker(File modpackDir) 28 | { 29 | this.modPackDir = modpackDir; 30 | DownloadUtils.readRemoteList(this.remoteList, this.checkDir); 31 | this.getLocalFile(); 32 | this.compare(); 33 | } 34 | 35 | private void getLocalFile() 36 | { 37 | if(!this.modPackDir.isDirectory()) 38 | { 39 | this.modPackDir.delete(); 40 | } 41 | 42 | if(!this.modPackDir.exists()) 43 | { 44 | this.modPackDir.mkdirs(); 45 | return; // no need to check files as the folder is empty 46 | } 47 | 48 | for(String dirName : this.checkDir) 49 | { 50 | File dir = new File(this.modPackDir, dirName); 51 | if(dir.exists() && dir.isDirectory()) 52 | { 53 | if(RemoteInfoReader.instance().getSyncDir().contains(dirName)) 54 | { 55 | this.addFiles(this.localList, this.syncList, dir, this.modPackDir.getAbsolutePath(), true); 56 | } 57 | else 58 | { 59 | this.addFiles(this.localList, this.syncList, dir, this.modPackDir.getAbsolutePath(), false); 60 | } 61 | } 62 | } 63 | } 64 | 65 | private void compare() 66 | { 67 | this.missingList = new ArrayList(this.remoteList); 68 | this.missingList.removeAll(this.localList); 69 | 70 | this.outdatedList = new ArrayList(this.syncList); 71 | this.outdatedList.removeAll(this.remoteList); 72 | 73 | if(RemoteInfoReader.instance().hasWhiteList() && !this.outdatedList.isEmpty()) 74 | { 75 | for(JsonField field : RemoteInfoReader.instance().getWhileList().getFieldList()) 76 | { 77 | for(FileEntry file : this.outdatedList) 78 | { 79 | if(file.getMd5().equals(field.getValue().getText())) 80 | { 81 | this.outdatedList.remove(file); 82 | break; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | private void addFiles(List list, List syncList, File dir, String modpackPath, boolean syncDir) 90 | { 91 | for(File file : dir.listFiles()) 92 | { 93 | if(file.isDirectory()) 94 | { 95 | if(!RemoteInfoReader.instance().enableSubFolder()) 96 | { 97 | // only use recursive mode if sub folder option is disabled 98 | addFiles(list, syncList, file, modpackPath, syncDir); 99 | } 100 | } 101 | else 102 | { 103 | if(syncDir) 104 | { 105 | syncList.add(new FileEntry(getMd5(file), file.getAbsolutePath().replace(modpackPath + File.separator, ""), file.length())); 106 | } 107 | list.add(new FileEntry(getMd5(file), file.getAbsolutePath().replace(modpackPath + File.separator, ""), file.length())); 108 | } 109 | } 110 | } 111 | 112 | public String getMd5(final File file) 113 | { 114 | DigestInputStream stream = null; 115 | try 116 | { 117 | stream = new DigestInputStream(new FileInputStream(file), MessageDigest.getInstance("MD5")); 118 | final byte[] buffer = new byte[65536]; 119 | 120 | int read = stream.read(buffer); 121 | while(read >= 1) 122 | { 123 | read = stream.read(buffer); 124 | } 125 | } 126 | catch(final Exception ignored) 127 | { 128 | return null; 129 | } 130 | finally 131 | { 132 | if(stream != null) 133 | { 134 | try 135 | { 136 | stream.close(); 137 | } 138 | catch(final IOException localIOException) 139 | { 140 | 141 | } 142 | } 143 | } 144 | return String.format("%1$032x", new Object[] {new BigInteger(1, stream.getMessageDigest().digest())}); 145 | } 146 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/RemoteInfoReader.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.net.MalformedURLException; 8 | import java.net.URI; 9 | import java.net.URISyntaxException; 10 | import java.net.URLConnection; 11 | import java.util.ArrayList; 12 | import java.util.zip.GZIPInputStream; 13 | 14 | import javax.swing.JOptionPane; 15 | 16 | import com.google.common.base.Charsets; 17 | import com.google.common.base.Splitter; 18 | import com.google.common.collect.Lists; 19 | 20 | import argo.jdom.JdomParser; 21 | import argo.jdom.JsonNode; 22 | import argo.jdom.JsonRootNode; 23 | import argo.saj.InvalidSyntaxException; 24 | 25 | public class RemoteInfoReader 26 | { 27 | public static RemoteInfoReader instance; 28 | public JsonRootNode data; 29 | public final String remoteUrl; 30 | private final JdomParser parser = new JdomParser(); 31 | 32 | public RemoteInfoReader(String url) 33 | { 34 | this.remoteUrl = url; 35 | } 36 | 37 | public boolean init() 38 | { 39 | try 40 | { 41 | InputStreamReader reader = getRemoteStream(this.remoteUrl); 42 | this.data = this.parser.parse(reader); 43 | return true; 44 | } 45 | catch(InvalidSyntaxException e) 46 | { 47 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.jsoninvalid"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 48 | e.printStackTrace(); 49 | return false; 50 | } 51 | catch(IOException e) 52 | { 53 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotreadremote"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 54 | e.printStackTrace(); 55 | return false; 56 | } 57 | catch(URISyntaxException e) 58 | { 59 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotreadremote"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 60 | e.printStackTrace(); 61 | return false; 62 | } 63 | } 64 | 65 | public static RemoteInfoReader instance() 66 | { 67 | return instance; 68 | } 69 | 70 | public String getModPackName() 71 | { 72 | return this.data.getStringValue("profile", "id"); 73 | } 74 | 75 | public String getModPackDisplayName() 76 | { 77 | return this.data.getStringValue("install", "name"); 78 | } 79 | 80 | public String getMinecraftVersion() 81 | { 82 | return this.data.getStringValue("install", "minecraft"); 83 | } 84 | 85 | public String getForgeVersion() 86 | { 87 | return this.data.getStringValue("install", "forge"); 88 | } 89 | 90 | /** 91 | * An option to manage manually sub-folders 92 | * By default the check of files is recursive, if you put "mods" in the list all folder inside 93 | * "mods" will also be check. If sub-folder is enabled, is it will not be the case and you 94 | * need to add "mods/subfolder" in the syncDir to make the installer checking it 95 | * @return true if sub-folder is enabled 96 | */ 97 | public boolean enableSubFolder() 98 | { 99 | return this.data.isBooleanValue("install", "subfolder") && this.data.getBooleanValue("install", "subfolder"); 100 | } 101 | 102 | public ArrayList getSyncDir() 103 | { 104 | return Lists.newArrayList(Splitter.on(',').trimResults().omitEmptyStrings().split(this.data.getStringValue("install", "syncDir"))); 105 | } 106 | 107 | public String getSyncUrl() 108 | { 109 | return this.data.getStringValue("install", "syncUrl"); 110 | } 111 | 112 | public String getVersionTarget() 113 | { 114 | return this.data.getStringValue("install", "target"); 115 | } 116 | 117 | public JsonNode getProfileInfo() 118 | { 119 | return this.data.getNode("profile"); 120 | } 121 | 122 | public String getWelcome() 123 | { 124 | return this.data.getStringValue("install", "welcome"); 125 | } 126 | 127 | public boolean hasArgument() 128 | { 129 | return this.data.isStringValue("install", "JVMarg"); 130 | } 131 | 132 | public String getArgument() 133 | { 134 | return this.data.getStringValue("install", "JVMarg"); 135 | } 136 | 137 | public boolean hasWhiteList() 138 | { 139 | return this.data.isStringValue("install", "whiteList"); 140 | } 141 | 142 | public JsonRootNode getWhileList() 143 | { 144 | try 145 | { 146 | InputStreamReader reader = getRemoteStream(this.data.getStringValue("install", "whiteList")); 147 | return this.parser.parse(reader); 148 | } 149 | catch(Exception e) 150 | { 151 | e.printStackTrace(); 152 | return null; 153 | } 154 | } 155 | 156 | public boolean hasWebSite() 157 | { 158 | return this.data.isStringValue("install", "webSite"); 159 | } 160 | 161 | public String getWebSite() 162 | { 163 | return this.data.getStringValue("install", "webSite"); 164 | } 165 | 166 | public boolean hasCredits() 167 | { 168 | return this.data.isStringValue("install", "credits"); 169 | } 170 | 171 | public String getCredits() 172 | { 173 | return this.data.getStringValue("install", "credits"); 174 | } 175 | 176 | public boolean hasChangeLog() 177 | { 178 | return this.data.isStringValue("install", "changeLog"); 179 | } 180 | 181 | public JsonRootNode getChangeLog() 182 | { 183 | try 184 | { 185 | InputStreamReader reader = getRemoteStream(this.data.getStringValue("install", "changeLog")); 186 | return this.parser.parse(reader); 187 | } 188 | catch(Exception e) 189 | { 190 | e.printStackTrace(); 191 | return null; 192 | } 193 | } 194 | 195 | public boolean hasPreset() 196 | { 197 | return this.data.isStringValue("install", "preset"); 198 | } 199 | 200 | public JsonRootNode getPreset() 201 | { 202 | try 203 | { 204 | InputStreamReader reader = getRemoteStream(this.data.getStringValue("install", "preset")); 205 | return this.parser.parse(reader); 206 | } 207 | catch(Exception e) 208 | { 209 | e.printStackTrace(); 210 | return null; 211 | } 212 | } 213 | 214 | public String getPresetUrl() 215 | { 216 | return this.data.getStringValue("install", "preset"); 217 | } 218 | 219 | private InputStreamReader getRemoteStream(String str) throws MalformedURLException, IOException, URISyntaxException 220 | { 221 | URI uri = new URI(str); 222 | URLConnection connection = uri.toURL().openConnection(); 223 | connection.setRequestProperty("Accept-Encoding", "gzip"); 224 | connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:10.0) Gecko/20100101 Firefox/55.0"); 225 | InputStreamReader reader = null; 226 | if("gzip".equals(connection.getContentEncoding())) 227 | { 228 | reader = new InputStreamReader(new GZIPInputStream(connection.getInputStream()), Charsets.UTF_8); 229 | } 230 | else 231 | { 232 | reader = new InputStreamReader(connection.getInputStream(), Charsets.UTF_8); 233 | } 234 | return reader; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/installer/OptionFrame.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.installer; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.awt.Color; 6 | import java.awt.Frame; 7 | import java.awt.event.ActionEvent; 8 | import java.awt.event.ActionListener; 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import javax.swing.AbstractAction; 13 | import javax.swing.BoxLayout; 14 | import javax.swing.ButtonGroup; 15 | import javax.swing.JButton; 16 | import javax.swing.JDialog; 17 | import javax.swing.JFileChooser; 18 | import javax.swing.JFrame; 19 | import javax.swing.JLabel; 20 | import javax.swing.JPanel; 21 | import javax.swing.JRadioButton; 22 | import javax.swing.JTextField; 23 | 24 | import argo.jdom.JsonField; 25 | import argo.jdom.JsonRootNode; 26 | import fr.minecraftforgefrance.common.EnumOS; 27 | import fr.minecraftforgefrance.common.RemoteInfoReader; 28 | 29 | public class OptionFrame extends JDialog 30 | { 31 | private static final long serialVersionUID = 1L; 32 | public JLabel modpackFolder; 33 | public JLabel infoLabel; 34 | private JTextField selectedDirText; 35 | 36 | public OptionFrame(Frame parent) 37 | { 38 | this.setTitle(LANG.getTranslation("title.options")); 39 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 40 | this.setResizable(false); 41 | this.setModalityType(ModalityType.APPLICATION_MODAL); 42 | 43 | JPanel mainPanel = new JPanel(); 44 | mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS)); 45 | 46 | JLabel gameFolder = new JLabel(LANG.getTranslation("option.mcDir.info")); 47 | gameFolder.setAlignmentX(CENTER_ALIGNMENT); 48 | mainPanel.add(gameFolder); 49 | 50 | JPanel panel1 = new JPanel(); 51 | panel1.setLayout(new BoxLayout(panel1, BoxLayout.X_AXIS)); 52 | this.selectedDirText = new JTextField(); 53 | this.selectedDirText.setEditable(false); 54 | this.selectedDirText.setColumns(35); 55 | panel1.add(this.selectedDirText); 56 | 57 | JButton dirSelect = new JButton(); 58 | dirSelect.setAction(new AbstractAction() 59 | { 60 | private static final long serialVersionUID = 1L; 61 | 62 | @Override 63 | public void actionPerformed(ActionEvent e) 64 | { 65 | JFileChooser dirChooser = new JFileChooser(); 66 | dirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 67 | dirChooser.setFileHidingEnabled(false); 68 | dirChooser.ensureFileIsVisible(EnumOS.getMinecraftDefaultDir()); 69 | dirChooser.setSelectedFile(EnumOS.getMinecraftDefaultDir()); 70 | int response = dirChooser.showOpenDialog(OptionFrame.this); 71 | switch(response) 72 | { 73 | case JFileChooser.APPROVE_OPTION: 74 | try 75 | { 76 | OptionFrame.this.updateMinecraftDir(dirChooser.getSelectedFile().getCanonicalFile()); 77 | } 78 | catch(IOException e1) 79 | { 80 | e1.printStackTrace(); 81 | } 82 | break; 83 | default: 84 | break; 85 | } 86 | } 87 | 88 | }); 89 | dirSelect.setText("..."); 90 | dirSelect.setToolTipText(LANG.getTranslation("option.mcDir.select")); 91 | panel1.add(dirSelect); 92 | panel1.setAlignmentY(TOP_ALIGNMENT); 93 | mainPanel.add(panel1); 94 | 95 | this.infoLabel = new JLabel(); 96 | this.infoLabel.setForeground(Color.RED); 97 | this.infoLabel.setVisible(false); 98 | this.infoLabel.setAlignmentX(CENTER_ALIGNMENT); 99 | mainPanel.add(this.infoLabel); 100 | 101 | JLabel label = new JLabel(LANG.getTranslation("option.modpackDir.info")); 102 | label.setAlignmentX(CENTER_ALIGNMENT); 103 | label.setAlignmentY(TOP_ALIGNMENT); 104 | mainPanel.add(label); 105 | 106 | this.modpackFolder = new JLabel(); 107 | this.modpackFolder.setAlignmentX(CENTER_ALIGNMENT); 108 | this.modpackFolder.setAlignmentY(BOTTOM_ALIGNMENT); 109 | mainPanel.add(this.modpackFolder); 110 | 111 | if(RemoteInfoReader.instance().hasPreset()) 112 | { 113 | try 114 | { 115 | final JsonRootNode json = RemoteInfoReader.instance().getPreset(); 116 | 117 | JPanel choicePanel = new JPanel(); 118 | JLabel preConfig = new JLabel(LANG.getTranslation("option.preset")); 119 | choicePanel.add(preConfig); 120 | 121 | final ButtonGroup choiceButtonGroup = new ButtonGroup(); 122 | class PreSetAction extends AbstractAction 123 | { 124 | private static final long serialVersionUID = 1L; 125 | private final String name; 126 | 127 | public PreSetAction(String name) 128 | { 129 | this.name = name; 130 | } 131 | 132 | @Override 133 | public void actionPerformed(ActionEvent e) 134 | { 135 | Installer.frame.preSet = this.name; 136 | } 137 | }; 138 | 139 | for(JsonField field : json.getFieldList()) 140 | { 141 | String presetName = field.getName().getText(); 142 | if(presetName.equals("default")) 143 | { 144 | continue; 145 | } 146 | JRadioButton button = new JRadioButton(presetName); 147 | button.setAction(new PreSetAction(presetName)); 148 | button.setText(presetName); 149 | button.setSelected(presetName.equals(Installer.frame.preSet)); 150 | button.setAlignmentX(LEFT_ALIGNMENT); 151 | button.setAlignmentY(CENTER_ALIGNMENT); 152 | choiceButtonGroup.add(button); 153 | choicePanel.add(button); 154 | } 155 | choicePanel.setAlignmentY(CENTER_ALIGNMENT); 156 | mainPanel.add(choicePanel); 157 | 158 | } 159 | catch(Exception e) 160 | { 161 | e.printStackTrace(); 162 | } 163 | } 164 | 165 | JPanel buttonPanel = new JPanel(); 166 | JButton confirm = new JButton(LANG.getTranslation("option.confirm")); 167 | confirm.addActionListener(new ActionListener() 168 | { 169 | @Override 170 | public void actionPerformed(ActionEvent e) 171 | { 172 | OptionFrame.this.dispose(); 173 | } 174 | }); 175 | buttonPanel.add(confirm); 176 | mainPanel.add(buttonPanel); 177 | 178 | this.add(mainPanel); 179 | this.pack(); 180 | this.setLocationRelativeTo(parent); 181 | this.updateMinecraftDir(EnumOS.getMinecraftDefaultDir()); 182 | } 183 | 184 | public void updateMinecraftDir(File newMCDir) 185 | { 186 | this.selectedDirText.setText(newMCDir.getPath()); 187 | this.modpackFolder.setText(newMCDir.getPath() + File.separator + "modpack" + File.separator + RemoteInfoReader.instance().getModPackName()); 188 | 189 | File launcherProfiles = new File(newMCDir, "launcher_profiles.json"); 190 | if(!launcherProfiles.exists()) 191 | { 192 | this.infoLabel.setText(LANG.getTranslation("option.folder.notValid")); 193 | this.infoLabel.setVisible(true); 194 | } 195 | else 196 | { 197 | Installer.frame.mcDir = newMCDir; 198 | this.infoLabel.setVisible(false); 199 | } 200 | this.pack(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/updater/Updater.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.updater; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.io.BufferedInputStream; 6 | import java.io.File; 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.security.KeyStore; 12 | import java.security.SecureRandom; 13 | import java.security.cert.Certificate; 14 | import java.security.cert.CertificateFactory; 15 | import java.security.cert.X509Certificate; 16 | 17 | import javax.net.ssl.SSLContext; 18 | import javax.net.ssl.TrustManagerFactory; 19 | import javax.swing.JOptionPane; 20 | import javax.swing.UIManager; 21 | 22 | import com.google.common.base.Charsets; 23 | import com.google.common.base.Throwables; 24 | 25 | import argo.jdom.JdomParser; 26 | import argo.jdom.JsonRootNode; 27 | import argo.saj.InvalidSyntaxException; 28 | import fr.minecraftforgefrance.common.FileChecker; 29 | import fr.minecraftforgefrance.common.IInstallRunner; 30 | import fr.minecraftforgefrance.common.Localization; 31 | import fr.minecraftforgefrance.common.Logger; 32 | import fr.minecraftforgefrance.common.ProcessInstall; 33 | import fr.minecraftforgefrance.common.RemoteInfoReader; 34 | import joptsimple.OptionParser; 35 | import joptsimple.OptionSet; 36 | import joptsimple.OptionSpec; 37 | import net.minecraft.launchwrapper.Launch; 38 | 39 | public class Updater implements IInstallRunner 40 | { 41 | final private String[] arguments; 42 | private boolean forgeUpdate; 43 | 44 | public static void main(String[] args) 45 | { 46 | Localization.init(); 47 | new Updater(args); 48 | } 49 | 50 | public Updater(String[] args) 51 | { 52 | long start = System.currentTimeMillis(); 53 | Logger.info("Starting updater !"); 54 | final OptionParser parser = new OptionParser(); 55 | parser.allowsUnrecognizedOptions(); 56 | final OptionSpec gameDirOption = parser.accepts("gameDir", "The game directory").withRequiredArg().ofType(File.class); 57 | final OptionSpec modpackOption = parser.accepts("version", "The version used").withRequiredArg(); 58 | 59 | final OptionSet options = parser.parse(args); 60 | File gameDir = options.valueOf(gameDirOption); 61 | String modpackName = options.valueOf(modpackOption); 62 | File modPackDir; 63 | File mcDir; 64 | 65 | if(!gameDir.getAbsoluteFile().getPath().endsWith(modpackName)) 66 | { 67 | mcDir = gameDir; 68 | modPackDir = new File(new File(gameDir, "modpacks"), modpackName); 69 | for(int i = 0; i < args.length; i++) 70 | { 71 | if("--gameDir".equals(args[i])) 72 | { 73 | args[i + 1] = modPackDir.getAbsolutePath(); 74 | } 75 | } 76 | } 77 | else 78 | { 79 | modPackDir = gameDir; 80 | mcDir = gameDir.getParentFile().getParentFile(); 81 | } 82 | Logger.info(String.format("Running installer in folder: %s", gameDir.getPath())); 83 | this.arguments = args; 84 | 85 | File modpackInfo = new File(modPackDir, modpackName + ".json"); 86 | if(!modpackInfo.exists()) 87 | { 88 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.erroredprofile"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 89 | return; 90 | } 91 | 92 | JdomParser jsonParser = new JdomParser(); 93 | JsonRootNode jsonProfileData; 94 | 95 | try 96 | { 97 | jsonProfileData = jsonParser.parse(com.google.common.io.Files.newReader(modpackInfo, Charsets.UTF_8)); 98 | } 99 | catch(InvalidSyntaxException e) 100 | { 101 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.erroredprofile"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 102 | throw Throwables.propagate(e); 103 | } 104 | catch(Exception e) 105 | { 106 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.erroredprofile"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 107 | throw Throwables.propagate(e); 108 | } 109 | 110 | this.injectLECert(); 111 | 112 | RemoteInfoReader.instance = new RemoteInfoReader(jsonProfileData.getStringValue("remote")); 113 | if(!RemoteInfoReader.instance().init()) 114 | { 115 | runMinecraft(args); 116 | } 117 | FileChecker checker = new FileChecker(modPackDir); 118 | if(!shouldUpdate(jsonProfileData.getStringValue("forge"), checker)) 119 | { 120 | Logger.info("No update found, launching Minecraft !"); 121 | runMinecraft(args); 122 | } 123 | else 124 | { 125 | try 126 | { 127 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 128 | } 129 | catch(Exception e) 130 | { 131 | e.printStackTrace(); 132 | } 133 | ProcessInstall install = new ProcessInstall(checker, this, mcDir, null); 134 | install.createFrame(); 135 | } 136 | long end = System.currentTimeMillis(); 137 | Logger.info(String.format("Update checked in %d ms", (end - start))); 138 | 139 | } 140 | 141 | public boolean shouldUpdate(String forgeVersion, FileChecker checker) 142 | { 143 | if(checker.remoteList.isEmpty()) 144 | { 145 | return false; 146 | } 147 | if(!RemoteInfoReader.instance().getForgeVersion().equals(forgeVersion)) 148 | { 149 | this.forgeUpdate = true; 150 | return true; 151 | } 152 | return !checker.missingList.isEmpty() || !checker.outdatedList.isEmpty(); 153 | } 154 | 155 | public void runMinecraft(String[] args) 156 | { 157 | Logger.info("Lauching Minecraft ..."); 158 | Launch.main(args); 159 | } 160 | 161 | @Override 162 | public void onFinish() 163 | { 164 | if(this.forgeUpdate) 165 | { 166 | JOptionPane.showMessageDialog(null, LANG.getTranslation("update.finished.success"), LANG.getTranslation("misc.success"), JOptionPane.INFORMATION_MESSAGE); 167 | } 168 | else 169 | { 170 | runMinecraft(this.arguments); 171 | } 172 | } 173 | 174 | @Override 175 | public boolean shouldDownloadLib() 176 | { 177 | return forgeUpdate; 178 | } 179 | 180 | // Minecraft launcher use Java 8u51, so let's encrypt certificate isn't recognized. This code add the let's encrypt root cert in trust certs list. 181 | public void injectLECert() 182 | { 183 | try 184 | { 185 | KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 186 | Path ksPath = Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts"); 187 | keyStore.load(Files.newInputStream(ksPath), null); 188 | 189 | CertificateFactory cf = CertificateFactory.getInstance("X.509"); 190 | InputStream caInput = new BufferedInputStream(Updater.class.getResourceAsStream("/letsencryptauthorityx3.pem")); 191 | Certificate crt = cf.generateCertificate(caInput); 192 | keyStore.setCertificateEntry("letsencryptauthorityx3", crt); 193 | Logger.info("Added Cert for " + ((X509Certificate)crt).getSubjectDN()); 194 | 195 | InputStream caInput2 = new BufferedInputStream(Updater.class.getResourceAsStream("/lets-encrypt-r3.pem")); 196 | Certificate crt2 = cf.generateCertificate(caInput2); 197 | keyStore.setCertificateEntry("letsencryptauthorityx3", crt2); 198 | Logger.info("Added Cert for " + ((X509Certificate)crt2).getSubjectDN()); 199 | 200 | TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 201 | tmf.init(keyStore); 202 | SSLContext sslContext = SSLContext.getInstance("TLS"); 203 | sslContext.init(null, tmf.getTrustManagers(), new SecureRandom()); 204 | SSLContext.setDefault(sslContext); 205 | } 206 | catch(Exception e) 207 | { 208 | System.err.println("Failed to import LE root cert:"); 209 | e.printStackTrace(); 210 | } 211 | } 212 | 213 | } -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/installer/InstallerFrame.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.installer; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.awt.Desktop; 6 | import java.awt.Dimension; 7 | import java.awt.Toolkit; 8 | import java.awt.event.ActionEvent; 9 | import java.awt.event.ActionListener; 10 | import java.awt.event.KeyEvent; 11 | import java.awt.event.KeyListener; 12 | import java.awt.event.WindowAdapter; 13 | import java.awt.event.WindowEvent; 14 | import java.awt.image.BufferedImage; 15 | import java.io.File; 16 | import java.net.URI; 17 | 18 | import javax.imageio.ImageIO; 19 | import javax.swing.BoxLayout; 20 | import javax.swing.ImageIcon; 21 | import javax.swing.JButton; 22 | import javax.swing.JFrame; 23 | import javax.swing.JLabel; 24 | import javax.swing.JOptionPane; 25 | import javax.swing.JPanel; 26 | 27 | import argo.jdom.JsonRootNode; 28 | import fr.minecraftforgefrance.common.EnumOS; 29 | import fr.minecraftforgefrance.common.FileChecker; 30 | import fr.minecraftforgefrance.common.IInstallRunner; 31 | import fr.minecraftforgefrance.common.ProcessInstall; 32 | import fr.minecraftforgefrance.common.RemoteInfoReader; 33 | import fr.minecraftforgefrance.plusplus.PlusPlusGame; 34 | 35 | public class InstallerFrame extends JFrame implements IInstallRunner 36 | { 37 | private static final long serialVersionUID = 1L; 38 | public File mcDir = EnumOS.getMinecraftDefaultDir(); 39 | public String preSet = null; 40 | 41 | public InstallerFrame() 42 | { 43 | this.setTitle(String.format(LANG.getTranslation("title.installer"), RemoteInfoReader.instance().getModPackDisplayName())); 44 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 45 | this.setResizable(false); 46 | 47 | if(RemoteInfoReader.instance().hasPreset()) 48 | { 49 | try 50 | { 51 | JsonRootNode json = RemoteInfoReader.instance().getPreset(); 52 | this.preSet = json.getStringValue("default"); 53 | } 54 | catch(Exception e) 55 | { 56 | System.err.println("Cannot find default preset"); 57 | e.printStackTrace(); 58 | } 59 | } 60 | 61 | BufferedImage image = null; 62 | try 63 | { 64 | image = ImageIO.read(this.getClass().getResourceAsStream("/installer/logo.png")); 65 | } 66 | catch(Exception e) 67 | { 68 | 69 | } 70 | 71 | final Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); 72 | 73 | JPanel panel = new JPanel(); 74 | panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 75 | if(image != null) 76 | { 77 | ImageIcon icon = new ImageIcon(image); 78 | JLabel logoLabel = new JLabel(icon); 79 | logoLabel.setAlignmentX(CENTER_ALIGNMENT); 80 | logoLabel.setAlignmentY(CENTER_ALIGNMENT); 81 | if(image.getWidth() > dim.width || image.getHeight() + 10 > dim.height) 82 | { 83 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.bigimage"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 84 | this.dispose(); 85 | } 86 | else 87 | { 88 | logoLabel.setSize(image.getWidth(), image.getHeight()); 89 | panel.add(logoLabel); 90 | } 91 | } 92 | 93 | JPanel buttonPanel = new JPanel(); 94 | 95 | JButton install = new JButton(LANG.getTranslation("scr.btn.install")); 96 | install.addActionListener(new ActionListener() 97 | { 98 | @Override 99 | public void actionPerformed(ActionEvent e) 100 | { 101 | InstallerFrame.this.dispose(); 102 | if(!InstallerFrame.this.mcDir.exists() || !InstallerFrame.this.mcDir.isDirectory()) 103 | { 104 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.mcdirmissing"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 105 | return; 106 | } 107 | FileChecker checker = new FileChecker(new File(new File(InstallerFrame.this.mcDir, "modpacks"), RemoteInfoReader.instance().getModPackName())); 108 | ProcessInstall install = new ProcessInstall(checker, InstallerFrame.this, InstallerFrame.this.mcDir, InstallerFrame.this.preSet); 109 | install.createFrame(); 110 | } 111 | }); 112 | buttonPanel.add(install); 113 | 114 | if(RemoteInfoReader.instance().hasWebSite()) 115 | { 116 | JButton webSite = new JButton(LANG.getTranslation("scr.btn.webSite")); 117 | webSite.addActionListener(new ActionListener() 118 | { 119 | @Override 120 | public void actionPerformed(ActionEvent e) 121 | { 122 | try 123 | { 124 | Desktop.getDesktop().browse(new URI(RemoteInfoReader.instance().getWebSite())); 125 | } 126 | catch(Exception ex) 127 | { 128 | JOptionPane.showMessageDialog(InstallerFrame.this, String.format(LANG.getTranslation("err.cannotopenurl"), RemoteInfoReader.instance().getWebSite()), "Error", JOptionPane.ERROR_MESSAGE); 129 | } 130 | } 131 | }); 132 | buttonPanel.add(webSite); 133 | } 134 | 135 | JButton credit = new JButton(LANG.getTranslation("scr.btn.credits")); 136 | credit.addActionListener(new ActionListener() 137 | { 138 | @Override 139 | public void actionPerformed(ActionEvent e) 140 | { 141 | CreditFrame credit = new CreditFrame(InstallerFrame.this); 142 | credit.setVisible(true); 143 | } 144 | }); 145 | buttonPanel.add(credit); 146 | 147 | JButton option = new JButton(LANG.getTranslation("scr.btn.options")); 148 | option.addActionListener(new ActionListener() 149 | { 150 | @Override 151 | public void actionPerformed(ActionEvent e) 152 | { 153 | OptionFrame credit = new OptionFrame(InstallerFrame.this); 154 | credit.setVisible(true); 155 | } 156 | }); 157 | buttonPanel.add(option); 158 | 159 | JButton cancel = new JButton(LANG.getTranslation("misc.cancel")); 160 | cancel.addActionListener(new ActionListener() 161 | { 162 | @Override 163 | public void actionPerformed(ActionEvent e) 164 | { 165 | InstallerFrame.this.dispose(); 166 | } 167 | }); 168 | buttonPanel.add(cancel); 169 | 170 | JLabel welcome = new JLabel(RemoteInfoReader.instance().getWelcome()); 171 | welcome.setAlignmentX(CENTER_ALIGNMENT); 172 | welcome.setAlignmentY(CENTER_ALIGNMENT); 173 | 174 | JLabel mc = new JLabel("Minecraft : " + RemoteInfoReader.instance().getMinecraftVersion()); 175 | mc.setAlignmentX(CENTER_ALIGNMENT); 176 | mc.setAlignmentY(CENTER_ALIGNMENT); 177 | 178 | JLabel forge = new JLabel("Forge : " + RemoteInfoReader.instance().getForgeVersion()); 179 | forge.setAlignmentX(CENTER_ALIGNMENT); 180 | forge.setAlignmentY(CENTER_ALIGNMENT); 181 | 182 | panel.add(welcome); 183 | panel.add(mc); 184 | panel.add(forge); 185 | panel.add(buttonPanel); 186 | 187 | this.add(panel); 188 | addWindowListener(new WindowAdapter() 189 | { 190 | public void windowOpened(WindowEvent e) 191 | { 192 | requestFocus(); 193 | } 194 | }); 195 | this.pack(); 196 | this.setLocationRelativeTo(null); 197 | 198 | addKeyListener(new KeyListener() 199 | { 200 | @Override 201 | public void keyTyped(KeyEvent e) 202 | { 203 | if(e.getKeyChar() == '+' && !PlusPlusGame.isRunning) 204 | { 205 | new PlusPlusGame(); 206 | } 207 | } 208 | 209 | @Override 210 | public void keyReleased(KeyEvent e) 211 | { 212 | 213 | } 214 | 215 | @Override 216 | public void keyPressed(KeyEvent e) 217 | { 218 | 219 | } 220 | }); 221 | } 222 | 223 | public void run() 224 | { 225 | this.setVisible(true); 226 | } 227 | 228 | @Override 229 | public void onFinish() 230 | { 231 | SuccessFrame successFrame = new SuccessFrame(); 232 | successFrame.setVisible(true); 233 | } 234 | 235 | @Override 236 | public boolean shouldDownloadLib() 237 | { 238 | return true; 239 | } 240 | } -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/DownloadUtils.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import static fr.minecraftforgefrance.common.Localization.LANG; 4 | 5 | import java.awt.EventQueue; 6 | import java.io.BufferedReader; 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.File; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.InputStreamReader; 14 | import java.net.URL; 15 | import java.net.URLConnection; 16 | import java.nio.charset.Charset; 17 | import java.text.DecimalFormat; 18 | import java.util.Arrays; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.jar.JarEntry; 22 | import java.util.jar.JarInputStream; 23 | import java.util.jar.JarOutputStream; 24 | import java.util.jar.Pack200; 25 | import java.util.zip.GZIPInputStream; 26 | 27 | import javax.swing.JOptionPane; 28 | 29 | import org.tukaani.xz.XZInputStream; 30 | 31 | import com.google.common.base.Charsets; 32 | import com.google.common.hash.Hashing; 33 | import com.google.common.io.Files; 34 | 35 | import argo.jdom.JdomParser; 36 | import argo.jdom.JsonNode; 37 | import argo.jdom.JsonRootNode; 38 | 39 | public class DownloadUtils 40 | { 41 | public static final String LIBRARIES_URL = "https://libraries.minecraft.net/"; 42 | public static final String PACK_NAME = ".pack.xz"; 43 | 44 | public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat(); 45 | { 46 | DECIMAL_FORMAT.setMaximumFractionDigits(2); 47 | } 48 | 49 | /** 50 | * Fill the list of all files and the list of directory by reading the remote json file 51 | */ 52 | public static void readRemoteList(List files, List dirs) 53 | { 54 | try 55 | { 56 | URL resourceUrl = new URL(RemoteInfoReader.instance().getSyncUrl()); 57 | URLConnection connection = resourceUrl.openConnection(); 58 | connection.setRequestProperty("Accept-Encoding", "gzip"); 59 | connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:10.0) Gecko/20100101 Firefox/55.0"); 60 | 61 | JdomParser parser = new JdomParser(); 62 | InputStreamReader reader = null; 63 | if("gzip".equals(connection.getContentEncoding())) 64 | { 65 | reader = new InputStreamReader(new GZIPInputStream(connection.getInputStream()), Charsets.UTF_8); 66 | } 67 | else 68 | { 69 | reader = new InputStreamReader(connection.getInputStream(), Charsets.UTF_8); 70 | } 71 | 72 | JsonRootNode data = parser.parse(reader); 73 | 74 | for(int i = 0; i < data.getElements().size(); i++) 75 | { 76 | JsonNode node = data.getElements().get(i); 77 | String key = node.getStringValue("name"); 78 | long size = Long.parseLong(node.getStringValue("size")); 79 | String md5 = node.getStringValue("md5"); 80 | 81 | if(size > 0L) 82 | { 83 | String link = RemoteInfoReader.instance().getSyncUrl() + DownloadUtils.escapeURIPathParam(key); 84 | files.add(new FileEntry(new URL(link), md5, key, size)); 85 | } 86 | else if(RemoteInfoReader.instance().enableSubFolder()) 87 | { 88 | // add all folders if sub folder is enabled 89 | dirs.add(key.substring(0, key.length() - 1)); 90 | } 91 | else if(key.split("/").length == 1) 92 | { 93 | // only add the folder if it's in modpack root folder 94 | dirs.add(key.substring(0, key.length() - 1)); 95 | } 96 | } 97 | } 98 | catch(Exception ex) 99 | { 100 | ex.printStackTrace(); 101 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.networkerror"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 102 | } 103 | } 104 | 105 | public static boolean downloadFile(final URL url, final File dest, final InstallFrame installFrame) 106 | { 107 | EventQueue.invokeLater(new Runnable() 108 | { 109 | public void run() 110 | { 111 | installFrame.fileProgressBar.setIndeterminate(true); 112 | } 113 | }); 114 | 115 | FileOutputStream fos = null; 116 | BufferedReader reader = null; 117 | 118 | try 119 | { 120 | URLConnection connection = url.openConnection(); 121 | connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:10.0) Gecko/20100101 Firefox/55.0"); 122 | 123 | final int fileLength = connection.getContentLength(); 124 | 125 | EventQueue.invokeLater(new Runnable() 126 | { 127 | public void run() 128 | { 129 | installFrame.fileProgressBar.setMaximum(fileLength); 130 | installFrame.fileProgressBar.setValue(0); 131 | installFrame.fileProgressBar.setIndeterminate(false); 132 | } 133 | }); 134 | 135 | InputStream in = connection.getInputStream(); 136 | reader = new BufferedReader(new InputStreamReader(in)); 137 | fos = new FileOutputStream(dest); 138 | 139 | long downloadStartTime = System.currentTimeMillis(); 140 | int downloadedAmount = 0; 141 | byte[] buff = new byte[1024]; 142 | 143 | int progress = 0; 144 | while((progress = in.read(buff)) != -1) 145 | { 146 | fos.write(buff, 0, progress); 147 | addProgress(installFrame, progress); 148 | downloadedAmount += progress; 149 | long timeLapse = System.currentTimeMillis() - downloadStartTime; 150 | if(timeLapse >= 250L) 151 | { 152 | final float downloadSpeed = downloadedAmount / (float)timeLapse; 153 | 154 | downloadedAmount = 0; 155 | downloadStartTime += 250L; 156 | if(downloadSpeed > 1024F) 157 | { 158 | changeDownloadSpeed(installFrame, String.format(LANG.getTranslation("misc.speed.mo"), String.valueOf(DECIMAL_FORMAT.format(downloadSpeed / 1024F)))); 159 | 160 | } 161 | else 162 | { 163 | changeDownloadSpeed(installFrame, String.format(LANG.getTranslation("misc.speed.ko"), String.valueOf(DECIMAL_FORMAT.format(downloadSpeed)))); 164 | } 165 | } 166 | } 167 | } 168 | catch(Exception e) 169 | { 170 | e.printStackTrace(); 171 | System.err.println(String.format(LANG.getTranslation("err.invalidurl"), url.toString())); 172 | return false; 173 | } 174 | finally 175 | { 176 | try 177 | { 178 | if(fos != null) 179 | { 180 | fos.flush(); 181 | fos.close(); 182 | } 183 | if(reader != null) 184 | { 185 | reader.close(); 186 | } 187 | } 188 | catch(IOException e) 189 | { 190 | e.printStackTrace(); 191 | return false; 192 | } 193 | } 194 | return true; 195 | } 196 | 197 | private static void addProgress(final InstallFrame installFrame, final int progress) 198 | { 199 | EventQueue.invokeLater(new Runnable() 200 | { 201 | public void run() 202 | { 203 | installFrame.fileProgressBar.setValue(installFrame.fileProgressBar.getValue() + progress); 204 | installFrame.fullProgressBar.setValue(installFrame.fullProgressBar.getValue() + progress); 205 | } 206 | }); 207 | } 208 | 209 | private static void changeDownloadSpeed(final InstallFrame installFrame, final String text) 210 | { 211 | EventQueue.invokeLater(new Runnable() 212 | { 213 | public void run() 214 | { 215 | installFrame.downloadSpeedLabel.setText(text); 216 | } 217 | }); 218 | } 219 | 220 | public static boolean checksumValid(File libPath, List checksums) 221 | { 222 | try 223 | { 224 | byte[] fileData = Files.toByteArray(libPath); 225 | boolean valid = checksums == null || checksums.isEmpty() || checksums.contains(Hashing.sha1().hashBytes(fileData).toString()); 226 | if(!valid && libPath.getName().endsWith(".jar")) 227 | { 228 | valid = validateJar(libPath, fileData, checksums); 229 | } 230 | return valid; 231 | } 232 | catch(IOException e) 233 | { 234 | e.printStackTrace(); 235 | return false; 236 | } 237 | } 238 | 239 | public static boolean validateJar(File libPath, byte[] data, List checksums) throws IOException 240 | { 241 | Logger.info(String.format("Checking %s internal checksums", libPath.getAbsolutePath())); 242 | 243 | HashMap files = new HashMap(); 244 | String[] hashes = null; 245 | JarInputStream jar = new JarInputStream(new ByteArrayInputStream(data)); 246 | JarEntry entry = jar.getNextJarEntry(); 247 | while(entry != null) 248 | { 249 | byte[] eData = readFully(jar); 250 | 251 | if(entry.getName().equals("checksums.sha1")) 252 | { 253 | hashes = new String(eData, Charset.forName("UTF-8")).split("\n"); 254 | } 255 | 256 | if(!entry.isDirectory()) 257 | { 258 | files.put(entry.getName(), Hashing.sha1().hashBytes(eData).toString()); 259 | } 260 | entry = jar.getNextJarEntry(); 261 | } 262 | jar.close(); 263 | 264 | if(hashes != null) 265 | { 266 | boolean failed = !checksums.contains(files.get("checksums.sha1")); 267 | if(failed) 268 | { 269 | System.err.println("Failed checksums.sha1 validation!"); 270 | } 271 | else 272 | { 273 | Logger.info("Successfully validated checksums.sha1"); 274 | for(String hash : hashes) 275 | { 276 | if(hash.trim().equals("") || !hash.contains(" ")) 277 | continue; 278 | String[] e = hash.split(" "); 279 | String validChecksum = e[0]; 280 | String target = e[1]; 281 | String checksum = files.get(target); 282 | 283 | if(!files.containsKey(target) || checksum == null) 284 | { 285 | System.err.println(" " + target + " : " + LANG.getTranslation("misc.missing").toLowerCase()); 286 | failed = true; 287 | } 288 | else if(!checksum.equals(validChecksum)) 289 | { 290 | System.err.println(" " + target + " : " + LANG.getTranslation("misc.failed").toLowerCase() + " (" + checksum + ", " + validChecksum + ")"); 291 | failed = true; 292 | } 293 | } 294 | } 295 | 296 | if(!failed) 297 | { 298 | Logger.info("Jar contents validated successfully"); 299 | } 300 | 301 | return !failed; 302 | } 303 | else 304 | { 305 | Logger.info("checksums.sha1 was not found, validation failed"); 306 | return false; // Missing checksums 307 | } 308 | } 309 | 310 | public static byte[] readFully(InputStream stream) throws IOException 311 | { 312 | byte[] data = new byte[4096]; 313 | ByteArrayOutputStream entryBuffer = new ByteArrayOutputStream(); 314 | int len; 315 | do 316 | { 317 | len = stream.read(data); 318 | if(len > 0) 319 | { 320 | entryBuffer.write(data, 0, len); 321 | } 322 | } 323 | while(len != -1); 324 | 325 | return entryBuffer.toByteArray(); 326 | } 327 | 328 | public static void unpackLibrary(File output, byte[] data) throws IOException 329 | { 330 | if(output.exists()) 331 | { 332 | output.delete(); 333 | } 334 | 335 | byte[] decompressed = readFully(new XZInputStream(new ByteArrayInputStream(data))); 336 | 337 | // Snag the checksum signature 338 | String end = new String(decompressed, decompressed.length - 4, 4); 339 | if(!end.equals("SIGN")) 340 | { 341 | System.err.println("Unpacking failed, missing signature : " + end); 342 | return; 343 | } 344 | 345 | int x = decompressed.length; 346 | int len = ((decompressed[x - 8] & 0xFF)) | ((decompressed[x - 7] & 0xFF) << 8) | ((decompressed[x - 6] & 0xFF) << 16) | ((decompressed[x - 5] & 0xFF) << 24); 347 | byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8); 348 | 349 | FileOutputStream jarBytes = new FileOutputStream(output); 350 | JarOutputStream jos = new JarOutputStream(jarBytes); 351 | 352 | Pack200.newUnpacker().unpack(new ByteArrayInputStream(decompressed), jos); 353 | 354 | jos.putNextEntry(new JarEntry("checksums.sha1")); 355 | jos.write(checksums); 356 | jos.closeEntry(); 357 | 358 | jos.close(); 359 | jarBytes.close(); 360 | } 361 | 362 | public static String escapeURIPathParam(String input) 363 | { 364 | StringBuilder resultStr = new StringBuilder(); 365 | for(char ch : input.toCharArray()) 366 | { 367 | if(isUnsafe(ch)) 368 | { 369 | resultStr.append('%'); 370 | resultStr.append(toHex(ch / 16)); 371 | resultStr.append(toHex(ch % 16)); 372 | } 373 | else 374 | { 375 | resultStr.append(ch); 376 | } 377 | } 378 | return resultStr.toString(); 379 | } 380 | 381 | private static char toHex(int ch) 382 | { 383 | return (char)(ch < 10 ? '0' + ch : 'A' + ch - 10); 384 | } 385 | 386 | private static boolean isUnsafe(char ch) 387 | { 388 | if(ch > 128 || ch < 0) 389 | return true; 390 | return " %$&+,:;=?@<>#%".indexOf(ch) >= 0; 391 | } 392 | } -------------------------------------------------------------------------------- /src/main/java/fr/minecraftforgefrance/common/ProcessInstall.java: -------------------------------------------------------------------------------- 1 | package fr.minecraftforgefrance.common; 2 | 3 | import static argo.jdom.JsonNodeBuilders.aStringBuilder; 4 | import static fr.minecraftforgefrance.common.Localization.LANG; 5 | 6 | import java.awt.EventQueue; 7 | import java.awt.HeadlessException; 8 | import java.io.BufferedWriter; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.net.MalformedURLException; 12 | import java.net.URL; 13 | import java.net.URLConnection; 14 | import java.util.AbstractList; 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | 19 | import javax.swing.JOptionPane; 20 | 21 | import com.google.common.base.Charsets; 22 | import com.google.common.base.Function; 23 | import com.google.common.base.Splitter; 24 | import com.google.common.base.Throwables; 25 | import com.google.common.collect.Iterables; 26 | import com.google.common.collect.Lists; 27 | import com.google.common.collect.Maps; 28 | import com.google.common.io.Files; 29 | 30 | import argo.format.JsonFormatter; 31 | import argo.format.PrettyJsonFormatter; 32 | import argo.jdom.JdomParser; 33 | import argo.jdom.JsonField; 34 | import argo.jdom.JsonNode; 35 | import argo.jdom.JsonNodeBuilders; 36 | import argo.jdom.JsonNodeFactories; 37 | import argo.jdom.JsonNodeSelector; 38 | import argo.jdom.JsonNodeSelectors; 39 | import argo.jdom.JsonObjectNodeBuilder; 40 | import argo.jdom.JsonRootNode; 41 | import argo.jdom.JsonStringNode; 42 | import argo.saj.InvalidSyntaxException; 43 | 44 | public class ProcessInstall implements Runnable 45 | { 46 | private final InstallFrame installFrame; 47 | private List missingLibs = new ArrayList(); 48 | 49 | private static final JsonFormatter JSON_FORMATTER = new PrettyJsonFormatter(); 50 | 51 | private final FileChecker fileChecker; 52 | private final IInstallRunner runner; 53 | private final String preset; 54 | 55 | public final File mcDir; 56 | public final File modPackDir; 57 | 58 | private boolean error = false; 59 | 60 | public ProcessInstall(FileChecker file, IInstallRunner runner, File mcDir, String preset) 61 | { 62 | this.installFrame = new InstallFrame(this); 63 | this.fileChecker = file; 64 | this.runner = runner; 65 | this.mcDir = mcDir; 66 | this.modPackDir = new File(new File(mcDir, "modpacks"), RemoteInfoReader.instance().getModPackName()); 67 | this.preset = preset; 68 | } 69 | 70 | public void createFrame() 71 | { 72 | this.installFrame.run(); 73 | } 74 | 75 | @Override 76 | public void run() 77 | { 78 | if(!this.fileChecker.remoteList.isEmpty()) 79 | { 80 | this.deleteDeprecated(); 81 | } 82 | else 83 | { 84 | this.installFrame.dispose(); 85 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.noFile"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 86 | return; 87 | } 88 | 89 | final int totalSize = this.getTotalDownloadSize(); 90 | EventQueue.invokeLater(new Runnable() 91 | { 92 | public void run() 93 | { 94 | ProcessInstall.this.installFrame.fullProgressBar.setMaximum(totalSize); 95 | ProcessInstall.this.installFrame.fullProgressBar.setIndeterminate(false); 96 | } 97 | }); 98 | 99 | this.downloadMod(); 100 | if(this.runner.shouldDownloadLib()) 101 | { 102 | this.downloadLib(); 103 | } 104 | if(this.preset != null) 105 | { 106 | this.downloadPreset(); 107 | } 108 | if(!error) 109 | { 110 | this.finish(); 111 | } 112 | } 113 | 114 | public void deleteDeprecated() 115 | { 116 | for(FileEntry entry : this.fileChecker.outdatedList) 117 | { 118 | File f = new File(this.modPackDir, entry.getPath()); 119 | if(f.delete()) 120 | { 121 | Logger.info(String.format("%1$s was removed. Its md5 was : %2$s", f.getPath(), entry.getMd5())); 122 | } 123 | else 124 | { 125 | this.installFrame.dispose(); 126 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotdeletefile") + " : " + f.getPath(), "Error", JOptionPane.ERROR_MESSAGE); 127 | } 128 | } 129 | } 130 | 131 | /** 132 | * Check for missing libraries 133 | * 134 | * @return the sum of all missing libs's size 135 | */ 136 | private int checkMissingLibs() 137 | { 138 | File librariesDir = new File(this.mcDir, "libraries"); 139 | final List libraries = RemoteInfoReader.instance().getProfileInfo().getArrayNode("libraries"); 140 | EventQueue.invokeLater(new Runnable() 141 | { 142 | public void run() 143 | { 144 | ProcessInstall.this.installFrame.fileProgressBar.setMaximum(libraries.size()); 145 | ProcessInstall.this.installFrame.fileProgressBar.setIndeterminate(false); 146 | } 147 | }); 148 | int max = 0; 149 | for(final JsonNode library : libraries) 150 | { 151 | ProcessInstall.this.changeCurrentDownloadText(library.getStringValue("name")); 152 | EventQueue.invokeLater(new Runnable() 153 | { 154 | public void run() 155 | { 156 | ProcessInstall.this.installFrame.fileProgressBar.setValue(ProcessInstall.this.installFrame.fileProgressBar.getValue() + 1); 157 | } 158 | }); 159 | 160 | List checksums = null; 161 | String libName = library.getStringValue("name"); 162 | if(library.isBooleanValue("required") && library.getBooleanValue("required")) 163 | { 164 | if(library.isArrayNode("checksums")) 165 | { 166 | checksums = Lists.newArrayList(Lists.transform(library.getArrayNode("checksums"), new Function() 167 | { 168 | public String apply(JsonNode node) 169 | { 170 | return node.getText(); 171 | } 172 | })); 173 | } 174 | 175 | Logger.info(String.format("Considering library %s", libName)); 176 | String[] nameparts = Iterables.toArray(Splitter.on(':').split(libName), String.class); 177 | nameparts[0] = nameparts[0].replace('.', '/'); 178 | String jarName = nameparts[1] + '-' + nameparts[2] + ".jar"; 179 | String pathName = nameparts[0] + '/' + nameparts[1] + '/' + nameparts[2] + '/' + jarName; 180 | File libPath = new File(librariesDir, pathName.replace('/', File.separatorChar)); 181 | String libURL = DownloadUtils.LIBRARIES_URL; 182 | if(library.isStringValue("url")) 183 | { 184 | libURL = library.getStringValue("url") + (!library.getStringValue("url").endsWith("/") ? "/" : ""); 185 | } 186 | if(libPath.exists() && DownloadUtils.checksumValid(libPath, checksums)) 187 | { 188 | continue; 189 | } 190 | 191 | libPath.getParentFile().mkdirs(); 192 | libURL += pathName; 193 | File pack = null; 194 | boolean xz = false; 195 | if(library.isBooleanValue("xz") && library.getBooleanValue("xz")) 196 | { 197 | xz = true; 198 | pack = new File(libPath.getParentFile(), libPath.getName() + DownloadUtils.PACK_NAME); 199 | libURL += DownloadUtils.PACK_NAME; 200 | } 201 | if(library.isStringValue("directURL")) 202 | { 203 | libURL = library.getStringValue("directURL"); 204 | } 205 | try 206 | { 207 | URL url = new URL(libURL); 208 | URLConnection connection = url.openConnection(); 209 | connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:10.0) Gecko/20100101 Firefox/55.0"); 210 | int fileLength = connection.getContentLength(); 211 | max += fileLength; 212 | this.missingLibs.add(new LibEntry(libURL, libName, libPath, pack, fileLength, xz)); 213 | } 214 | catch(IOException e) 215 | { 216 | e.printStackTrace(); 217 | } 218 | } 219 | } 220 | return max; 221 | } 222 | 223 | /** 224 | * Get the sum of all files's size to download 225 | * 226 | * @return the size 227 | */ 228 | private int getTotalDownloadSize() 229 | { 230 | int size = 0; 231 | for(FileEntry entry : this.fileChecker.missingList) 232 | { 233 | size += entry.getSize(); 234 | } 235 | if(this.runner.shouldDownloadLib()) 236 | { 237 | size += this.checkMissingLibs(); 238 | } 239 | return size; 240 | } 241 | 242 | public void downloadMod() 243 | { 244 | this.installFrame.setTitle(LANG.getTranslation("proc.downloadingmods")); 245 | 246 | for(FileEntry entry : this.fileChecker.missingList) 247 | { 248 | File f = new File(this.modPackDir, entry.getPath()); 249 | if(f.getParentFile() != null && !f.getParentFile().isDirectory()) 250 | { 251 | f.getParentFile().mkdirs(); 252 | } 253 | this.changeCurrentDownloadText(entry.getPath()); 254 | Logger.info(String.format("Downloading file %1$s to %2$s (its md5 is %3$s)", entry.getUrl().toString(), f.getPath(), entry.getMd5())); 255 | if(!DownloadUtils.downloadFile(entry.getUrl(), f, this.installFrame)) 256 | { 257 | this.installFrame.dispose(); 258 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotdownload") + " : " + entry.getUrl().toString(), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 259 | Thread.currentThread().interrupt(); 260 | this.error = true; 261 | return; 262 | } 263 | } 264 | } 265 | 266 | public void downloadLib() 267 | { 268 | this.installFrame.setTitle(LANG.getTranslation("title.libs")); 269 | 270 | for(LibEntry entry : this.missingLibs) 271 | { 272 | this.changeCurrentDownloadText(String.format(LANG.getTranslation("proc.downloadinglib"), entry.getName())); 273 | try 274 | { 275 | File filePath = entry.isXZ() ? entry.getPackDest() : entry.getDest(); 276 | if(!DownloadUtils.downloadFile(new URL(entry.getUrl()), filePath, this.installFrame)) 277 | { 278 | this.installFrame.dispose(); 279 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotdownload") + " : " + entry.getUrl().toString() + (entry.isXZ() ? DownloadUtils.PACK_NAME : ""), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 280 | Thread.currentThread().interrupt(); 281 | this.error = true; 282 | return; 283 | } 284 | else if(entry.isXZ()) 285 | { 286 | try 287 | { 288 | this.changeCurrentDownloadText(LANG.getTranslation("proc.unpackingfile") + " : " + entry.getPackDest().toString()); 289 | DownloadUtils.unpackLibrary(entry.getDest(), Files.toByteArray(entry.getPackDest())); 290 | this.changeCurrentDownloadText(String.format(LANG.getTranslation("file.unpacked.success"), entry.getPackDest().toString())); 291 | entry.getPackDest().delete(); 292 | } 293 | catch(Exception e) 294 | { 295 | e.printStackTrace(); 296 | } 297 | } 298 | } 299 | catch(HeadlessException e) 300 | { 301 | e.printStackTrace(); 302 | } 303 | catch(MalformedURLException e) 304 | { 305 | e.printStackTrace(); 306 | } 307 | } 308 | } 309 | 310 | public void downloadPreset() 311 | { 312 | this.installFrame.setTitle(LANG.getTranslation("title.preset")); 313 | final JsonRootNode data = RemoteInfoReader.instance().getPreset(); 314 | final JsonNodeSelector> preSet = JsonNodeSelectors.anArrayNode(this.preset); 315 | final JsonNodeSelector preSetName = JsonNodeSelectors.aStringNode(); 316 | List files = new AbstractList() 317 | { 318 | public String get(int index) 319 | { 320 | return preSetName.getValue(preSet.getValue(data).get(index)); 321 | } 322 | 323 | public int size() 324 | { 325 | return preSet.getValue(data).size(); 326 | } 327 | }; 328 | for(String file : files) 329 | { 330 | File destFile = new File(this.modPackDir, file); 331 | if(!destFile.exists()) 332 | { 333 | if(!destFile.getParentFile().exists()) 334 | { 335 | destFile.getParentFile().mkdirs(); 336 | } 337 | this.changeCurrentDownloadText(file); 338 | try 339 | { 340 | if(!DownloadUtils.downloadFile(new URL(RemoteInfoReader.instance().getPresetUrl() + this.preset + "/" + DownloadUtils.escapeURIPathParam(file)), destFile, this.installFrame)) 341 | { 342 | this.installFrame.dispose(); 343 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotdownload") + " : " + RemoteInfoReader.instance().getPresetUrl() + this.preset + "/" + DownloadUtils.escapeURIPathParam(file), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 344 | Thread.currentThread().interrupt(); 345 | this.error = true; 346 | return; 347 | } 348 | } 349 | catch(HeadlessException e) 350 | { 351 | e.printStackTrace(); 352 | } 353 | catch(MalformedURLException e) 354 | { 355 | e.printStackTrace(); 356 | } 357 | } 358 | } 359 | } 360 | 361 | private void changeCurrentDownloadText(final String text) 362 | { 363 | EventQueue.invokeLater(new Runnable() 364 | { 365 | public void run() 366 | { 367 | ProcessInstall.this.installFrame.currentDownload.setText(text); 368 | } 369 | }); 370 | } 371 | 372 | public void finish() 373 | { 374 | EventQueue.invokeLater(new Runnable() 375 | { 376 | public void run() 377 | { 378 | ProcessInstall.this.installFrame.fullProgressBar.setMaximum(100); 379 | ProcessInstall.this.installFrame.fullProgressBar.setValue(100); 380 | } 381 | }); 382 | this.installFrame.setTitle(LANG.getTranslation("misc.finishing")); 383 | this.createOrUpdateProfile(); 384 | this.writeModPackInfo(); 385 | this.addToProfileList(); 386 | this.installFrame.dispose(); 387 | this.runner.onFinish(); 388 | } 389 | 390 | /** 391 | * create or update the file modpackName.json in the folder .minecraft/profile/modpackName 392 | */ 393 | private void createOrUpdateProfile() 394 | { 395 | String modpackName = RemoteInfoReader.instance().getModPackName(); 396 | File versionRootDir = new File(this.mcDir, "versions"); 397 | File modpackVersionDir = new File(versionRootDir, modpackName); 398 | if(!modpackVersionDir.exists()) 399 | { 400 | modpackVersionDir.mkdirs(); 401 | } 402 | File modpackJson = new File(modpackVersionDir, modpackName + ".json"); 403 | 404 | JsonRootNode versionJson = JsonNodeFactories.object(RemoteInfoReader.instance().getProfileInfo().getFields()); 405 | try 406 | { 407 | BufferedWriter newWriter = Files.newWriter(modpackJson, Charsets.UTF_8); 408 | JSON_FORMATTER.format(versionJson, newWriter); 409 | newWriter.close(); 410 | } 411 | catch(Exception e) 412 | { 413 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotwriteversion"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 414 | } 415 | } 416 | 417 | /** 418 | * determine if the profile exist and if it is valid in the file launcher_profiles.json 419 | * 420 | * @param profiles 421 | * JsonRootNode of the file launcher_profiles.json 422 | * @param modpackName 423 | * name of the modpack 424 | * @param displayName 425 | * display name of the modpack 426 | * @return true if the profile exist and is valid 427 | */ 428 | private boolean isProfileValid(JsonRootNode profiles, String modpackName, String displayName) 429 | { 430 | if(profiles.isObjectNode("profiles", displayName)) 431 | { 432 | if(profiles.isStringValue("profiles", displayName, "name") && profiles.getStringValue("profiles", displayName, "name").equals(displayName)) 433 | { 434 | return profiles.getStringValue("profiles", displayName, "lastVersionId").equals(modpackName); 435 | } 436 | } 437 | return false; 438 | } 439 | 440 | /** 441 | * add the profile in the file launcher_profiles.json 442 | */ 443 | private void addToProfileList() 444 | { 445 | String modpackName = RemoteInfoReader.instance().getModPackName(); 446 | String displayName = RemoteInfoReader.instance().getModPackDisplayName(); 447 | File launcherProfiles = new File(this.mcDir, "launcher_profiles.json"); 448 | JdomParser parser = new JdomParser(); 449 | JsonRootNode jsonProfileData; 450 | if(!launcherProfiles.exists()) 451 | { 452 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.mcprofilemissing"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 453 | this.installFrame.dispose(); 454 | return; 455 | } 456 | try 457 | { 458 | jsonProfileData = parser.parse(Files.newReader(launcherProfiles, Charsets.UTF_8)); 459 | } 460 | catch(InvalidSyntaxException e) 461 | { 462 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.mcprofilecorrupted"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 463 | throw Throwables.propagate(e); 464 | } 465 | catch(Exception e) 466 | { 467 | throw Throwables.propagate(e); 468 | } 469 | 470 | if(!isProfileValid(jsonProfileData, modpackName, displayName)) 471 | { 472 | JsonField[] fields = new JsonField[RemoteInfoReader.instance().hasArgument() ? 5 : 4]; 473 | fields[0] = JsonNodeFactories.field("name", JsonNodeFactories.string(displayName)); 474 | fields[1] = JsonNodeFactories.field("lastVersionId", JsonNodeFactories.string(modpackName)); 475 | fields[2] = JsonNodeFactories.field("type", JsonNodeFactories.string("custom")); 476 | fields[3] = JsonNodeFactories.field("gameDir", JsonNodeFactories.string(this.modPackDir.getAbsoluteFile().toString())); 477 | if(RemoteInfoReader.instance().hasArgument()) 478 | { 479 | fields[4] = JsonNodeFactories.field("javaArgs", JsonNodeFactories.string(RemoteInfoReader.instance().getArgument())); 480 | } 481 | 482 | HashMap profileCopy = Maps.newHashMap(jsonProfileData.getNode("profiles").getFields()); 483 | HashMap rootCopy = Maps.newHashMap(jsonProfileData.getFields()); 484 | profileCopy.put(JsonNodeFactories.string(displayName), JsonNodeFactories.object(fields)); 485 | JsonRootNode profileJsonCopy = JsonNodeFactories.object(profileCopy); 486 | 487 | rootCopy.put(JsonNodeFactories.string("profiles"), profileJsonCopy); 488 | 489 | jsonProfileData = JsonNodeFactories.object(rootCopy); 490 | 491 | try 492 | { 493 | BufferedWriter newWriter = Files.newWriter(launcherProfiles, Charsets.UTF_8); 494 | JSON_FORMATTER.format(jsonProfileData, newWriter); 495 | newWriter.close(); 496 | } 497 | catch(Exception e) 498 | { 499 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotwriteprofile"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 500 | } 501 | } 502 | } 503 | 504 | public void writeModPackInfo() 505 | { 506 | File info = new File(this.modPackDir, RemoteInfoReader.instance().getModPackName() + ".json"); 507 | if(!info.exists()) 508 | { 509 | try 510 | { 511 | info.createNewFile(); 512 | } 513 | catch(IOException e) 514 | { 515 | throw Throwables.propagate(e); 516 | } 517 | } 518 | 519 | JsonObjectNodeBuilder jsonBuilder = JsonNodeBuilders.anObjectBuilder().withField("forge", aStringBuilder(RemoteInfoReader.instance().getForgeVersion())); 520 | jsonBuilder.withField("remote", aStringBuilder(RemoteInfoReader.instance().remoteUrl)); 521 | if(RemoteInfoReader.instance().hasChangeLog()) 522 | { 523 | JsonRootNode changeLog = RemoteInfoReader.instance().getChangeLog(); 524 | if(changeLog != null && changeLog.hasFields()) 525 | jsonBuilder.withField("currentVersion", aStringBuilder(changeLog.getFieldList().get(0).getName().getText())); 526 | } 527 | JsonRootNode json = jsonBuilder.build(); 528 | try 529 | { 530 | BufferedWriter writer = Files.newWriter(info, Charsets.UTF_8); 531 | JSON_FORMATTER.format(json, writer); 532 | writer.close(); 533 | } 534 | catch(Exception e) 535 | { 536 | JOptionPane.showMessageDialog(null, LANG.getTranslation("err.cannotwriteversion"), LANG.getTranslation("misc.error"), JOptionPane.ERROR_MESSAGE); 537 | } 538 | } 539 | } --------------------------------------------------------------------------------