Skip to content

Commit

Permalink
🎉 Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul1365972 committed Feb 9, 2020
1 parent 5982cc1 commit 1039481
Show file tree
Hide file tree
Showing 28 changed files with 1,311 additions and 0 deletions.
16 changes: 16 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id 'java'
}

group 'com.github.paul1365972'
version '1.0.0'

sourceCompatibility = 1.8

repositories {
mavenCentral()
}

dependencies {
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'LoRDeckCoder'
59 changes: 59 additions & 0 deletions src/main/java/io/github/paul1365972/lordeckcoder/LoRDeckCoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.github.paul1365972.lordeckcoder;

import io.github.paul1365972.lordeckcoder.coder.FormatVersion;
import io.github.paul1365972.lordeckcoder.coder.LoRCoder;
import io.github.paul1365972.lordeckcoder.coder.LoRDecoder;
import io.github.paul1365972.lordeckcoder.exceptions.LoRCodingException;
import io.github.paul1365972.lordeckcoder.objects.impl.LoRCard;
import io.github.paul1365972.lordeckcoder.objects.impl.LoRDeck;
import io.github.paul1365972.lordeckcoder.util.Base32;

import java.io.ByteArrayOutputStream;
import java.util.function.Supplier;

public class LoRDeckCoder {

private static final LoRDecoder<LoRDeck> BASIC_DECODER = LoRDecoder.from(LoRDeck::new,
(deck, set, factionId, cardNumber, amount) ->
deck.addCards(new LoRCard(set, factionId, cardNumber), amount)
);

public static LoRDeck toDeck(String code) throws LoRCodingException {
return toDeck(code, BASIC_DECODER);
}

public static <T> T toDeck(String code, LoRDecoder<T> decoder) throws LoRCodingException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Base32.decode(code, baos::write);
return toDeck(baos.toByteArray(), decoder);
}

public static LoRDeck toDeck(byte[] bytes) throws LoRCodingException {
return toDeck(bytes, BASIC_DECODER);
}

public static <T> T toDeck(byte[] bytes, LoRDecoder<T> decoder) throws LoRCodingException {
return LoRCoder.decode(bytesToSupplier(bytes), decoder);
}

public static String toCode(LoRDeck deck, FormatVersion formatVersion) throws LoRCodingException {
return Base32.encode(bytesToSupplier(toCodeBytes(deck, formatVersion)));
}

public static byte[] toCodeBytes(LoRDeck deck, FormatVersion formatVersion) throws LoRCodingException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
LoRCoder.encode(deck, formatVersion, baos::write);
return baos.toByteArray();
}

private static Supplier<Byte> bytesToSupplier(byte[] bytes) {
return new Supplier<Byte>() {
int index = 0;

@Override
public Byte get() {
return index < bytes.length ? bytes[index++] : null;
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.github.paul1365972.lordeckcoder.coder;

import java.util.Objects;

public class FormatVersion {

public static final FormatVersion F01V01 = new FormatVersion(1, 1);
public static final FormatVersion LIVE = F01V01, PBE = F01V01, EXPERIMENTAL = F01V01;


private byte formatVersion;

public FormatVersion(byte formatVersion) {
this.formatVersion = formatVersion;
}

public FormatVersion(int format, int version) {
this((byte) ((version << 4) | format));
}

public byte getPacked() {
return formatVersion;
}

public int getFormat() {
return formatVersion & 0xF;
}

public int getVersion() {
return (formatVersion >> 4) & 0xF;
}

@Override
public String toString() {
return "FormatVersion{" +
"format=" + getFormat() +
", version=" + getVersion() +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FormatVersion that = (FormatVersion) o;
return getPacked() == that.getPacked();
}

@Override
public int hashCode() {
return Objects.hash(getPacked());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.github.paul1365972.lordeckcoder.coder;

import io.github.paul1365972.lordeckcoder.exceptions.LoRCodingException;
import io.github.paul1365972.lordeckcoder.exceptions.LoRUnsupportedFormatException;
import io.github.paul1365972.lordeckcoder.objects.LoRCardI;
import io.github.paul1365972.lordeckcoder.objects.LoRDeckI;
import io.github.paul1365972.lordeckcoder.objects.LoRFactionI;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class LoRCoder {

private static final Map<FormatVersion, LoRFormatter> FORMATS = new HashMap<>();

static {
register(new LoRFormat01Ver01());
}

public static <T> T decode(Supplier<Byte> bytes, LoRDecoder<T> decoder) throws LoRCodingException {
FormatVersion formatVersion = new FormatVersion(bytes.get());
return get(formatVersion).decode(bytes, decoder);
}

public static <T extends LoRCardI<U>, U extends LoRFactionI> void encode(LoRDeckI<T, U> deck, FormatVersion formatVersion, Consumer<Byte> builder) throws LoRUnsupportedFormatException {
builder.accept(formatVersion.getPacked());
get(formatVersion).encode(deck, builder);
}

public static LoRFormatter get(FormatVersion formatVersion) throws LoRUnsupportedFormatException {
LoRFormatter formatter = FORMATS.get(formatVersion);
if (formatter == null)
throw new LoRUnsupportedFormatException("Unsupported Deck Format/Version " + formatVersion.getFormat() + "/" + formatVersion.getVersion());
return formatter;
}

public static void register(LoRFormatter formatter) {
FORMATS.put(formatter.getFormatVersion(), formatter);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.github.paul1365972.lordeckcoder.coder;

import io.github.paul1365972.lordeckcoder.exceptions.LoRCodingException;

import java.util.function.Function;

public interface LoRDecoder<T> {

static <T> LoRDecoder<T> from(Function<FormatVersion, T> deckFactory, CardAccumulator<T> accumulator) {
return new LoRDecoder<T>() {
@Override
public T createDeck(FormatVersion formatVersion) {
return deckFactory.apply(formatVersion);
}

@Override
public void addCard(T deck, int set, int factionId, int cardNumber, int amount) throws LoRCodingException {
accumulator.accept(deck, set, factionId, cardNumber, amount);
}
};
}

T createDeck(FormatVersion formatVersion) throws LoRCodingException;

void addCard(T deck, int set, int factionId, int cardNumber, int amount) throws LoRCodingException;

@FunctionalInterface
interface CardAccumulator<T> {
void accept(T deck, int set, int factionId, int cardNumber, int amount) throws LoRCodingException;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package io.github.paul1365972.lordeckcoder.coder;

import io.github.paul1365972.lordeckcoder.exceptions.LoRCodingException;
import io.github.paul1365972.lordeckcoder.exceptions.LoRIllegalFormatException;
import io.github.paul1365972.lordeckcoder.exceptions.LoRUnsupportedFormatException;
import io.github.paul1365972.lordeckcoder.objects.LoRCardI;
import io.github.paul1365972.lordeckcoder.objects.LoRDeckI;
import io.github.paul1365972.lordeckcoder.objects.LoRFactionI;
import io.github.paul1365972.lordeckcoder.util.CheckedSupplier;
import io.github.paul1365972.lordeckcoder.util.VarInt;

import java.math.BigInteger;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class LoRFormat01Ver01 implements LoRFormatter {

public static final FormatVersion FORMAT_VERSION = new FormatVersion(1, 1);

public <T, U> T decode(Supplier<Byte> bytes, LoRDecoder<T> deserializer) throws LoRCodingException {
CheckedSupplier<BigInteger, LoRCodingException> supplier = () -> Optional
.ofNullable(VarInt.toBigInt(bytes))
.orElseThrow(() -> new LoRIllegalFormatException("Reached end of stream unexpectedly"));

T deck = deserializer.createDeck(FORMAT_VERSION);
try {
for (int cards = 3; cards > 0; cards--) {
int setFactionGroups = supplier.get().intValueExact();
for (int i = 0; i < setFactionGroups; i++) {
int setFactionLength = supplier.get().intValueExact();
int set = supplier.get().intValueExact();
int faction = supplier.get().intValueExact();
for (int j = 0; j < setFactionLength; j++) {
int cardNumber = supplier.get().intValueExact();
deserializer.addCard(deck, set, faction, cardNumber, cards);
}
}
}
} catch (ArithmeticException e) {
throw new LoRUnsupportedFormatException("VarInt-Optimization overflowed");
}
return deck;
}

@Override
public <T extends LoRCardI<U>, U extends LoRFactionI> void encode(LoRDeckI<T, U> deck, Consumer<Byte> builder) {
Consumer<BigInteger> consumer = varInt -> VarInt.toBytes(varInt, builder);

Map<Integer, List<T>> amountCardsMap = new HashMap<>();
deck.getCards().forEach((card, amount) -> amountCardsMap.computeIfAbsent(amount, k -> new ArrayList<>()).add(card));
for (int amount = 3; amount >= 1; amount--) {
List<T> cards = amountCardsMap.getOrDefault(amount, Collections.emptyList());
Map<SetFaction, List<T>> setFactionCardsMap = new HashMap<>();
cards.forEach(card -> setFactionCardsMap.computeIfAbsent(new SetFaction(card.getSet(), card.getFaction()), k -> new ArrayList<>()).add(card));
consumer.accept(BigInteger.valueOf(setFactionCardsMap.size()));
setFactionCardsMap.entrySet().stream()
.sorted(Comparator.comparing(entry -> entry.getValue().size()))
.forEach(entry -> {
consumer.accept(BigInteger.valueOf(entry.getValue().size()));
consumer.accept(BigInteger.valueOf(entry.getKey().getSet()));
consumer.accept(BigInteger.valueOf(entry.getKey().getFaction().getId()));
entry.getValue().stream()
.sorted(Comparator.comparing(LoRCardI::getCardCode))
.forEach(card -> consumer.accept(BigInteger.valueOf(card.getCardNumber())));
});
}
}

@Override
public FormatVersion getFormatVersion() {
return FORMAT_VERSION;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.paul1365972.lordeckcoder.coder;

import io.github.paul1365972.lordeckcoder.exceptions.LoRCodingException;
import io.github.paul1365972.lordeckcoder.objects.LoRCardI;
import io.github.paul1365972.lordeckcoder.objects.LoRDeckI;
import io.github.paul1365972.lordeckcoder.objects.LoRFactionI;

import java.util.function.Consumer;
import java.util.function.Supplier;

public interface LoRFormatter {

<T, U> T decode(Supplier<Byte> bytes, LoRDecoder<T> decoder) throws LoRCodingException;

<T extends LoRCardI<U>, U extends LoRFactionI> void encode(LoRDeckI<T, U> deck, Consumer<Byte> builder);

FormatVersion getFormatVersion();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.github.paul1365972.lordeckcoder.coder;

import io.github.paul1365972.lordeckcoder.objects.LoRFactionI;

import java.util.Objects;

class SetFaction {
private int set;
private LoRFactionI faction;

public SetFaction(int set, LoRFactionI faction) {
this.set = set;
this.faction = Objects.requireNonNull(faction);
}

public int getSet() {
return set;
}

public LoRFactionI getFaction() {
return faction;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SetFaction that = (SetFaction) o;
return getSet() == that.getSet() &&
getFaction() == that.getFaction();
}

@Override
public int hashCode() {
return Objects.hash(getSet(), getFaction());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.paul1365972.lordeckcoder.exceptions;

public class LoRCodingException extends Exception {

public LoRCodingException() {
super();
}

public LoRCodingException(String message) {
super(message);
}

public LoRCodingException(String message, Throwable cause) {
super(message, cause);
}

public LoRCodingException(Throwable cause) {
super(cause);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.paul1365972.lordeckcoder.exceptions;

public class LoRIllegalFormatException extends LoRCodingException {

public LoRIllegalFormatException() {
super();
}

public LoRIllegalFormatException(String message) {
super(message);
}

public LoRIllegalFormatException(String message, Throwable cause) {
super(message, cause);
}

public LoRIllegalFormatException(Throwable cause) {
super(cause);
}

}
Loading

0 comments on commit 1039481

Please sign in to comment.