Redo hashing sytem, pack file reading, write json

This commit is contained in:
comp500 2019-06-12 14:28:38 +01:00
parent 8be5cb8e60
commit fbd54b4604
No known key found for this signature in database
GPG Key ID: 214C822FFEC586B5
9 changed files with 243 additions and 99 deletions

View File

@ -2,19 +2,21 @@ package link.infra.packwiz.installer;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.net.URI;
import java.nio.file.Paths;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.moandjiezana.toml.Toml;
import link.infra.packwiz.installer.metadata.HashInputStream;
import link.infra.packwiz.installer.metadata.HashTypeAdapter;
import link.infra.packwiz.installer.metadata.ManifestFile;
import link.infra.packwiz.installer.metadata.PackFile;
import link.infra.packwiz.installer.metadata.hash.Hash;
import link.infra.packwiz.installer.request.HandlerManager;
import link.infra.packwiz.installer.ui.IUserInterface;
import link.infra.packwiz.installer.ui.InstallProgress;
@ -87,7 +89,7 @@ public class UpdateManager {
this.checkOptions();
ui.submitProgress(new InstallProgress("Loading manifest file..."));
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new HashTypeAdapter()).create();
Gson gson = new Gson();
ManifestFile manifest;
try {
manifest = gson.fromJson(new FileReader(Paths.get(opts.packFolder, opts.manifestFile).toString()),
@ -100,31 +102,47 @@ public class UpdateManager {
}
ui.submitProgress(new InstallProgress("Loading pack file..."));
HashInputStream packFileStream;
Hash.HashInputStream packFileStream;
try {
InputStream stream = HandlerManager.getFileInputStream(opts.downloadURI);
if (stream == null) {
throw new Exception("Pack file URI is invalid, is it supported?");
}
packFileStream = new HashInputStream(stream);
packFileStream = new Hash.HashInputStream(stream, "sha256");
} catch (Exception e) {
// TODO: still launch the game if updating doesn't work?
ui.handleExceptionAndExit(e);
return;
}
// TODO: read file
PackFile pf;
try {
packFileStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
pf = new Toml().read(packFileStream).to(PackFile.class);
} catch (IllegalStateException e) {
ui.handleExceptionAndExit(e);
return;
}
byte[] packFileHash = packFileStream.getHashValue();
Hash packFileHash = packFileStream.get();
if (packFileHash.equals(manifest.packFileHash)) {
System.out.println("Hash already up to date!");
// WOOO it's already up to date
// todo: --force?
}
// TODO: save manifest file
System.out.println(pf.name);
// When successfully updated
manifest.packFileHash = packFileHash;
// update other hashes
// TODO: don't do this on failure?
try (Writer writer = new FileWriter(Paths.get(opts.packFolder, opts.manifestFile).toString())) {
gson.toJson(manifest, writer);
} catch (IOException e) {
// TODO: add message?
ui.handleException(e);
}
}
protected void checkOptions() {

View File

@ -1,60 +0,0 @@
package link.infra.packwiz.installer.metadata;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HashInputStream extends FilterInputStream {
private MessageDigest md;
private byte[] output;
public HashInputStream(InputStream in) throws NoSuchAlgorithmException {
super(in);
md = MessageDigest.getInstance("SHA-256");
}
@Override
public int read() throws IOException {
int value = super.read();
if (value == -1) {
return value;
}
md.update((byte) value);
return value;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = super.read(b, off, len);
if (bytesRead > 0) {
md.update(b, off, len);
}
return bytesRead;
}
@Override
public void reset() throws IOException {
throw new IOException("HashInputStream doesn't support reset()");
}
@Override
public boolean markSupported() {
return false;
}
@Override
public void mark(int readlimit) {
// Do nothing
}
public byte[] getHashValue() {
if (output == null) {
output = md.digest();
}
return output;
}
}

View File

@ -0,0 +1,5 @@
package link.infra.packwiz.installer.metadata;
public class IndexFile {
}

View File

@ -1,5 +1,13 @@
package link.infra.packwiz.installer.metadata;
import link.infra.packwiz.installer.metadata.hash.Hash;
public class ManifestFile {
public byte[] packFileHash = null;
public Hash packFileHash = null;
public Hash indexFileHash = null;
public static class File {
public Hash hash = null;
}
}

View File

@ -1,5 +1,21 @@
package link.infra.packwiz.installer.metadata;
public class PackFile {
import java.util.Map;
import com.google.gson.annotations.SerializedName;
public class PackFile {
public String name;
public IndexFileLoc index;
public static class IndexFileLoc {
public String file;
@SerializedName("hash-format")
public String hashFormat;
public String hash;
}
public Map<String, String> versions;
public Map<String, Object> client;
public Map<String, Object> server;
}

View File

@ -0,0 +1,115 @@
package link.infra.packwiz.installer.metadata.hash;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
public class Hash {
public final String value;
public final String type;
public Hash(String value, String type) {
this.value = value;
this.type = type;
}
private static final Map<String, IHasher> hashTypeConversion = new HashMap<String, IHasher>();
static {
try {
hashTypeConversion.put("sha256", new HasherMessageDigest("SHA-256"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public IHasher getHasher() {
return hashTypeConversion.get(type);
}
public static IHasher getHasher(String type) {
return hashTypeConversion.get(type);
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Hash)) {
return false;
}
Hash hash = (Hash)obj;
return type.equals(hash.type) && getHasher().equalValues(value, hash.value);
}
public static class HashInputStream extends FilterInputStream {
private IHasher md;
private Hash output;
private final String hashType;
private Hash compare = null;
public HashInputStream(InputStream in, String hashType) throws NoSuchAlgorithmException {
super(in);
this.hashType = hashType;
md = hashTypeConversion.get(hashType);
}
public HashInputStream(InputStream in, Hash compare) throws NoSuchAlgorithmException {
this(in, compare.type);
this.compare = compare;
}
@Override
public int read() throws IOException {
int value = super.read();
if (value == -1) {
return value;
}
md.update((byte) value);
return value;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = super.read(b, off, len);
if (bytesRead > 0) {
md.update(b, off, len);
}
return bytesRead;
}
@Override
public void reset() throws IOException {
throw new IOException("HashInputStream doesn't support reset()");
}
@Override
public boolean markSupported() {
return false;
}
@Override
public void mark(int readlimit) {
// Do nothing
}
public Hash get() {
if (output == null) {
String value = md.get();
if (value != null) {
output = new Hash(value, hashType);
}
}
return output;
}
public boolean isNew() {
if (output != null) {
return !output.equals(compare);
}
return false;
}
}
}

View File

@ -1,27 +1,7 @@
package link.infra.packwiz.installer.metadata;
package link.infra.packwiz.installer.metadata.hash;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public class HashTypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
@Override
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(printHexBinary(src));
}
@Override
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return parseHexBinary(json.getAsString());
}
public class HashUtils {
private HashUtils() {}
// Why did Java remove this in 1.9????!
public static byte[] parseHexBinary(String s) {
@ -52,7 +32,7 @@ public class HashTypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer
return -1;
}
private static final char[] hexCode = "0123456789ABCDEF".toCharArray();
private static final char[] hexCode = "0123456789abcdef".toCharArray();
public static String printHexBinary(byte[] data) {
StringBuilder r = new StringBuilder(data.length*2);

View File

@ -0,0 +1,45 @@
package link.infra.packwiz.installer.metadata.hash;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HasherMessageDigest implements IHasher {
MessageDigest md;
public HasherMessageDigest(String hashType) throws NoSuchAlgorithmException {
md = MessageDigest.getInstance(hashType);
}
@Override
public void update(byte[] data) {
md.update(data);
}
@Override
public void update(byte[] data, int offset, int length) {
md.update(data, offset, length);
}
@Override
public void update(byte data) {
md.update(data);
}
@Override
public String get() {
return HashUtils.printHexBinary(md.digest());
}
// Enforce case insensitivity
@Override
public boolean equalValues(String a, String b) {
if (a == null) {
if (b == null) {
return true;
}
return false;
}
return a.equalsIgnoreCase(b);
}
}

View File

@ -0,0 +1,17 @@
package link.infra.packwiz.installer.metadata.hash;
public interface IHasher {
public void update(byte[] data);
public void update(byte[] data, int offset, int length);
public void update(byte data);
public String get();
public default boolean equalValues(String a, String b) {
if (a == null) {
if (b == null) {
return true;
}
return false;
}
return a.equals(b);
}
}