diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..149cb3c
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..692ff10
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/*
+!src/
+!.settings/
+!.gitignore
+!.project
+!.classpath
+!pom.xml
diff --git a/.project b/.project
new file mode 100644
index 0000000..809bdfa
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ MaiCrypt
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..672496e
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..80f31b6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,46 @@
+
+ 4.0.0
+ MaiCrypt
+ MaiCrypt
+ 0.0.1-SNAPSHOT
+
+ src
+
+
+ maven-compiler-plugin
+ 3.5.1
+
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.4.3
+
+
+
+ me.despawningbone.maicrypt.Main
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+ org.apache.commons
+ commons-compress
+ 1.18
+
+
+
\ No newline at end of file
diff --git a/src/me/despawningbone/maicrypt/Main.java b/src/me/despawningbone/maicrypt/Main.java
new file mode 100644
index 0000000..6106637
--- /dev/null
+++ b/src/me/despawningbone/maicrypt/Main.java
@@ -0,0 +1,123 @@
+package me.despawningbone.maicrypt;
+
+import java.awt.GraphicsEnvironment;
+import java.io.Console;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
+import org.apache.commons.compress.compressors.gzip.GzipParameters;
+import org.apache.commons.compress.utils.IOUtils;
+
+public class Main {
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+ Console console = System.console();
+ if(console == null && !GraphicsEnvironment.isHeadless()){
+ String filename = Main.class.getProtectionDomain().getCodeSource().getLocation().toString().substring(6);
+ if(System.getProperty("os.name").contains("Win")) Runtime.getRuntime().exec(new String[]{"cmd", "/c", "start", "cmd", "/c", "java -jar \"" + filename + "\""});
+ }else{
+ System.out.println("-= maimai .bin data file decryption tool =-\n");
+ Boolean encrypt = null;
+ while (encrypt == null) {
+ System.out.print("Encrypt/decrypt (e/d)? ");
+ String line = System.console().readLine();
+ if (line.equalsIgnoreCase("e") || line.equalsIgnoreCase("d"))
+ encrypt = line.equalsIgnoreCase("e");
+ }
+ System.out.println("Selected " + (encrypt ? "encrypt" : "decrypt") + " mode.");
+ System.out.println("Please put the files in the same directory as the jar now (including a \"Keyfile.txt\" with only the key in hex form).");
+ String key = Files.readAllLines(new File("Keyfile.txt").toPath()).get(0).replaceAll("\\s+","");
+ String base, out;
+ if(encrypt) {
+ System.out.print("Output file name (the input file name should be the same as output but without file extension): ");
+ out = System.console().readLine();
+ base = out.substring(0, out.lastIndexOf("."));
+ compressGzip(base, base + "_EDITED.gz");
+ processFile(true, new File(base + "_EDITED.gz"), DatatypeConverter.parseHexBinary(key), new File(out));
+ } else {
+ System.out.print("Input file name: ");
+ String in = System.console().readLine();
+ base = in.substring(0, in.lastIndexOf("."));
+ processFile(false, new File(in), DatatypeConverter.parseHexBinary(key),
+ new File(base + ".gz"));
+ decompressGzip(base + ".gz", base);
+ out = base;
+ }
+ System.out.print("Done. Check the directory for the " + base + (encrypt ? "_EDITED" : "") + ".gz archive and the " + out + " file!\nPress any key to exit...");
+ System.console().readLine();
+ }
+ }
+
+ private static void processFile(boolean encrypt, File input, byte[] byteKey, File output) {
+ try (FileInputStream fileInputStream = new FileInputStream(input);
+ FileOutputStream fileOutputStream = new FileOutputStream(output)) {
+ Key key = new SecretKeySpec(byteKey, "AES");
+
+ byte[] inputBytes = new byte[(int) input.length()];
+ fileInputStream.read(inputBytes);
+ if(encrypt) { //append 16 bytes before encrypting
+ ByteBuffer buf = ByteBuffer.allocate(inputBytes.length + 16);
+ buf.put(new byte[16]);
+ buf.put(inputBytes);
+ inputBytes = buf.array();
+ }
+
+ byte[] iv = Arrays.copyOfRange(inputBytes, 0, 16);
+ IvParameterSpec ivspec = new IvParameterSpec(iv);
+
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+
+ cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key, ivspec);
+
+ byte[] outputBytes = cipher.doFinal(inputBytes);
+
+ if(!encrypt) outputBytes = Arrays.copyOfRange(outputBytes, 16, outputBytes.length);
+
+ fileOutputStream.write(outputBytes);
+
+ fileInputStream.close();
+ fileOutputStream.close();
+
+ } catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void compressGzip(String file, String gzipFile) {
+ GzipParameters param = new GzipParameters();
+ param.setFilename(file);
+ try (GzipCompressorOutputStream out = new GzipCompressorOutputStream(new FileOutputStream(gzipFile), param); FileInputStream fi = new FileInputStream(file)) {
+ IOUtils.copy(fi, out);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void decompressGzip(String gzipFile, String file) {
+ try (GzipCompressorInputStream in = new GzipCompressorInputStream(new FileInputStream(gzipFile)); FileOutputStream fo = new FileOutputStream(file)) {
+ IOUtils.copy(in, fo);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}