skadistats.clarity.io.bitstream.BitStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of clarity Show documentation
Show all versions of clarity Show documentation
Clarity is an open source replay parser for Dota 2, CSGO, CS2 and Deadlock written in Java.
package skadistats.clarity.io.bitstream;
import com.google.protobuf.ByteString;
import com.google.protobuf.ZeroCopy;
import skadistats.clarity.io.s2.FieldOpType;
import skadistats.clarity.platform.ClarityPlatform;
import java.nio.charset.StandardCharsets;
public abstract class BitStream {
private static final int COORD_INTEGER_BITS = 14;
private static final int COORD_FRACTIONAL_BITS = 5;
private static final float COORD_RESOLUTION = (1.0f / (1 << COORD_FRACTIONAL_BITS));
private static final int COORD_INTEGER_BITS_MP = 11;
private static final int COORD_FRACTIONAL_BITS_MP_LOWPRECISION = 3;
private static final int COORD_DENOMINATOR_LOWPRECISION = (1 << COORD_FRACTIONAL_BITS_MP_LOWPRECISION);
private static final float COORD_RESOLUTION_LOWPRECISION = (1.0f / COORD_DENOMINATOR_LOWPRECISION);
private static final int NORMAL_FRACTIONAL_BITS = 11;
private static final float NORMAL_FRACTIONAL_RESOLUTION = (1.0f / ((1 << NORMAL_FRACTIONAL_BITS) - 1));
public static final long[] MASKS = {
0x0L, 0x1L, 0x3L, 0x7L,
0xfL, 0x1fL, 0x3fL, 0x7fL,
0xffL, 0x1ffL, 0x3ffL, 0x7ffL,
0xfffL, 0x1fffL, 0x3fffL, 0x7fffL,
0xffffL, 0x1ffffL, 0x3ffffL, 0x7ffffL,
0xfffffL, 0x1fffffL, 0x3fffffL, 0x7fffffL,
0xffffffL, 0x1ffffffL, 0x3ffffffL, 0x7ffffffL,
0xfffffffL, 0x1fffffffL, 0x3fffffffL, 0x7fffffffL,
0xffffffffL, 0x1ffffffffL, 0x3ffffffffL, 0x7ffffffffL,
0xfffffffffL, 0x1fffffffffL, 0x3fffffffffL, 0x7fffffffffL,
0xffffffffffL, 0x1ffffffffffL, 0x3ffffffffffL, 0x7ffffffffffL,
0xfffffffffffL, 0x1fffffffffffL, 0x3fffffffffffL, 0x7fffffffffffL,
0xffffffffffffL, 0x1ffffffffffffL, 0x3ffffffffffffL, 0x7ffffffffffffL,
0xfffffffffffffL, 0x1fffffffffffffL, 0x3fffffffffffffL, 0x7fffffffffffffL,
0xffffffffffffffL, 0x1ffffffffffffffL, 0x3ffffffffffffffL, 0x7ffffffffffffffL,
0xfffffffffffffffL, 0x1fffffffffffffffL, 0x3fffffffffffffffL, 0x7fffffffffffffffL,
0xffffffffffffffffL
};
private static final int[] UBV_COUNT = {0, 4, 8, 28};
private static final int[] UBVFP_COUNT = {2, 4, 10, 17, 31};
protected int len;
protected int pos;
private final byte[] stringTemp = new byte[32768];
public static BitStream createBitStream(ByteString input) {
var bs = ClarityPlatform.createBitStream(ZeroCopy.extract(input));
bs.len = input.size() * 8;
return bs;
}
protected abstract int peekBit(int pos);
public abstract int readUBitInt(int n);
public abstract long readUBitLong(int n);
public abstract void readBitsIntoByteArray(byte[] dest, int n);
public abstract FieldOpType readFieldOp();
public int len() {
return len;
}
public int pos() {
return pos;
}
public void pos(int pos) {
if (pos >= len) {
throw new UnsupportedOperationException("pos >= len");
}
this.pos = pos;
}
public int remaining() {
return len - pos;
}
public void skip(int n) {
pos = pos + n;
}
public int readBit() {
return peekBit(pos++);
}
public boolean readBitFlag() {
return peekBit(pos++) != 0;
}
public long readSBitLong(int n) {
var v = readUBitLong(n);
return (v & (1L << (n - 1))) == 0 ? v : v | (MASKS[64 - n] << n);
}
public int readSBitInt(int n) {
var v = readUBitInt(n);
return (v & (1 << (n - 1))) == 0 ? v : v | ((int)MASKS[32 - n] << n);
}
public String readString(int n) {
var o = 0;
while (o < n) {
var c = (byte) readUBitInt(8);
if (c == 0) {
break;
}
stringTemp[o] = c;
o++;
}
return new String(stringTemp, 0, o, StandardCharsets.UTF_8);
}
public long readVarU(int max) {
var m = ((max + 6) / 7) * 7;
var s = 0;
var v = 0L;
long b;
while (true) {
b = readUBitInt(8);
v |= (b & 0x7FL) << s;
s += 7;
if ((b & 0x80L) == 0L || s == m) {
return v;
}
}
}
public long readVarS(int max) {
var v = readVarU(max);
return (v >>> 1) ^ -(v & 1L);
}
public long readVarULong() {
return readVarU(64);
}
public long readVarSLong() {
return readVarS(64);
}
public int readVarUInt() {
return (int) readVarU(32);
}
public int readVarSInt() {
return (int) readVarS(32);
}
public int readUBitVar() {
// Thanks to Robin Dietrich for providing a clean version of this code :-)
// The header looks like this: [XY00001111222233333333333333333333] where everything > 0 is optional.
// The first 2 bits (X and Y) tell us how much (if any) to read other than the 6 initial bits:
// Y set -> read 4
// X set -> read 8
// X + Y set -> read 28
var v = readUBitInt(6);
var a = v >> 4;
if (a == 0) {
return v;
} else {
return (v & 15) | (readUBitInt(UBV_COUNT[a]) << 4);
}
}
public int readUBitVarFieldPath() {
var i = -1;
while (++i < 4) {
if (readBitFlag()) break;
}
return readUBitInt(UBVFP_COUNT[i]);
}
public float readBitCoord() {
var i = readBitFlag(); // integer component present?
var f = readBitFlag(); // fractional component present?
var v = 0.0f;
if (!(i || f)) return v;
var s = readBitFlag();
if (i) v = (float)(readUBitInt(COORD_INTEGER_BITS) + 1);
if (f) v += readUBitInt(COORD_FRACTIONAL_BITS) * COORD_RESOLUTION;
return s ? -v : v;
}
public float readCellCoord(int n, boolean integral, boolean lowPrecision) {
var v = (float)(readUBitInt(n));
if (integral) {
// TODO: something weird is going on here in alice, we might need to adjust the sign?
return v;
}
if (lowPrecision) {
throw new UnsupportedOperationException();
}
return v + readUBitInt(COORD_FRACTIONAL_BITS) * COORD_RESOLUTION;
}
public float readCoordMp(BitStream stream, boolean integral, boolean lowPrecision) {
var i = 0;
var f = 0;
var sign = false;
var value = 0.0f;
var inBounds = stream.readBitFlag();
if (integral) {
if (readBitFlag()) {
sign = stream.readBitFlag();
value = stream.readUBitInt(inBounds ? COORD_INTEGER_BITS_MP : COORD_INTEGER_BITS) + 1;
}
} else {
if (readBitFlag()) {
sign = stream.readBitFlag();
i = stream.readUBitInt(inBounds ? COORD_INTEGER_BITS_MP : COORD_INTEGER_BITS) + 1;
} else {
sign = stream.readBitFlag();
}
f = stream.readUBitInt(lowPrecision ? COORD_FRACTIONAL_BITS_MP_LOWPRECISION : COORD_FRACTIONAL_BITS);
value = i + ((float) f * (lowPrecision ? COORD_RESOLUTION_LOWPRECISION : COORD_RESOLUTION));
}
return sign ? -value : value;
}
public float readBitAngle(int n) {
return readUBitInt(n) * 360.0f / (1 << n);
}
public float readBitNormal() {
var s = readBitFlag();
var v = (float) readUBitInt(NORMAL_FRACTIONAL_BITS) * NORMAL_FRACTIONAL_RESOLUTION;
return s ? -v : v;
}
public float[] read3BitNormal() {
var v = new float[3];
var x = readBitFlag();
var y = readBitFlag();
if (x) v[0] = readBitNormal();
if (y) v[1] = readBitNormal();
var s = readBitFlag();
var p = v[0] * v[0] + v[1] * v[1];
if (p < 1.0f) v[2] = (float) Math.sqrt(1.0f - p);
if (s) v[2] = -v[2];
return v;
}
public String toString() {
var buf = new StringBuilder();
buf.append('[');
buf.append(pos);
buf.append('/');
buf.append(len);
buf.append(']');
buf.append(' ');
var prefixLen = buf.length();
var min = Math.max(0, (pos - 32));
var max = Math.min(len - 1, pos + 64);
for (var i = min; i <= max; i++) {
buf.append(peekBit(i));
}
buf.insert(pos - min + prefixLen, '*');
return buf.toString();
}
public String toString(int from, int to) {
var buf = new StringBuilder();
for (var i = from; i < to; i++) {
buf.append(peekBit(i));
}
return buf.toString();
}
}