diff --git a/jni/NOTES b/jni/NOTES
new file mode 100644
index 0000000000000000000000000000000000000000..2e6e5bb2775a2d67fda59ab164ff77106b3423f0
--- /dev/null
+++ b/jni/NOTES
@@ -0,0 +1,12 @@
+Compilation:
+javac com/upstandinghackers/hammer/*.java
+
+Conversion to JNI headers:
+find -name "*.class" | sed -e 's/.class$//' | tr '/' '.' | cut -c 3- | xargs javah
+
+Not working:
+enums aren't converted at all, no idea why
+
+TODO:
+Implement the entire JNI side
+Testing
diff --git a/jni/com/upstandinghackers/hammer/HAction.java b/jni/com/upstandinghackers/hammer/HAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..09aeaaff7f4cc0344facfd002a3152e63eb137ff
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/HAction.java
@@ -0,0 +1,8 @@
+package com.upstandinghackers.hammer;
+
+import java.util.List;
+
+public interface HAction
+{
+    public List<HParsedToken> execute(HParseResult p);
+}
diff --git a/jni/com/upstandinghackers/hammer/HParseResult.java b/jni/com/upstandinghackers/hammer/HParseResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed5c85beb11b374bca28a1541a06044ea5f43277
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/HParseResult.java
@@ -0,0 +1,9 @@
+package com.upstandinghackers.hammer;
+
+import java.util.List;
+
+public class HParseResult
+{
+    public native List<HParsedToken> getAst();
+    public native long getBitLength();
+}
diff --git a/jni/com/upstandinghackers/hammer/HParsedToken.java b/jni/com/upstandinghackers/hammer/HParsedToken.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8a70d6a670ae96ebe974f3c235f509f63933a51
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/HParsedToken.java
@@ -0,0 +1,28 @@
+package com.upstandinghackers.hammer;
+
+import java.util.List;
+
+public class HParsedToken
+{
+    public native Hammer.HTokenType getTokenType();
+    public native int getIndex();
+    public native byte getBitOffset();
+    public native byte[] getBytesValue();
+    public native long getSIntValue();
+    public native long getUIntValue();
+    public native double getDoubleValue();
+    public native float getFloatValue();
+    public native List<HParsedToken> getSeqValue();
+    public native Object getUserValue();
+
+    native void setTokenType(Hammer.HTokenType type);
+    native void setIndex(int index);
+    native void setBitOffset(byte offset);
+    native void setBytesValue(byte[] value);
+    native void setSIntValue(long value);
+    native void setUIntValue(long value);
+    native void setDoubleValue(double value);
+    native void setFloatValue(float value);
+    native void setSeqValue(List<HParsedToken> value);
+    native void setUserValue(Object value);
+}
diff --git a/jni/com/upstandinghackers/hammer/HParser.java b/jni/com/upstandinghackers/hammer/HParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f2387aadceb3f7a0751f2f8e143196ee83a4974
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/HParser.java
@@ -0,0 +1,6 @@
+package com.upstandinghackers.hammer;
+
+public class HParser
+{
+    public native void bindIndirect(HParser inner);
+}
diff --git a/jni/com/upstandinghackers/hammer/HPredicate.java b/jni/com/upstandinghackers/hammer/HPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c87875e2f1544472e8b80368fcd461eb22b1f99
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/HPredicate.java
@@ -0,0 +1,6 @@
+package com.upstandinghackers.hammer;
+
+public interface HPredicate
+{
+    public boolean apply(HParseResult p);
+}
diff --git a/jni/com/upstandinghackers/hammer/Hammer.java b/jni/com/upstandinghackers/hammer/Hammer.java
new file mode 100644
index 0000000000000000000000000000000000000000..08b75b1efcab93d945a20ebdb8bbb5c82c58a143
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/Hammer.java
@@ -0,0 +1,66 @@
+package com.upstandinghackers.hammer;
+
+public class Hammer
+{
+    public final static byte BYTE_BIG_ENDIAN = 0x1;
+    public final static byte BIT_BIG_ENDIAN = 0x2;
+    public final static byte BYTE_LITTLE_ENDIAN = 0x0;
+    public final static byte BIT_LITTLE_ENDIAN = 0x0;
+   
+    public enum HTokenType
+    {
+        TT_NONE(1),
+        TT_BYTES(2),
+        TT_SINT(4),
+        TT_UINT(8),
+        TT_SEQUENCE(16),
+        TT_ERR(32),
+        TT_USER(64),
+        TT_MAX(128);
+
+        private int value;
+        public int getValue() { return this.value; }
+        private HTokenType(int value) { this.value = value; }
+    }
+
+    public static native HParseResult hParse(HParser parser, byte[] input, int length);
+    public static native HParser hToken(byte[] str, int length);
+    public static native HParser hCh(byte c);
+    public static native HParser hChRange(byte from, byte to);
+    public static native HParser hIntRange(HParser p, int lower, int upper);
+    public static native HParser hBits(int len, boolean sign);
+    public static native HParser hInt64();
+    public static native HParser hInt32();
+    public static native HParser hInt16();
+    public static native HParser hInt8();
+    public static native HParser hUInt64();
+    public static native HParser hUInt32();
+    public static native HParser hUInt16();
+    public static native HParser hUInt8();
+    public static native HParser hWhitespace(HParser p);
+    public static native HParser hLeft(HParser p, HParser q);
+    public static native HParser hRight(HParser p, HParser q);
+    public static native HParser hMiddle(HParser p, HParser x, HParser q);
+    public static native HParser hAction(HParser p, HAction a);
+    public static native HParser hIn(byte[] charset, int length);
+    public static native HParser hEndP();
+    public static native HParser hNothingP();
+    public static native HParser hSequence(HParser[] parsers);
+    public static native HParser hChoice(HParser[] parsers);
+    public static native HParser hButNot(HParser p1, HParser p2);
+    public static native HParser hDifference(HParser p1, HParser p2);
+    public static native HParser hXor(HParser p1, HParser p2);
+    public static native HParser hMany(HParser p);
+    public static native HParser hMany1(HParser p);
+    public static native HParser hRepeatN(HParser p, int n);
+    public static native HParser hOptional(HParser p);
+    public static native HParser hIgnore(HParser p);
+    public static native HParser hSepBy(HParser p, HParser sep);
+    public static native HParser hSepBy1(HParser p, HParser sep);
+    public static native HParser hEpsilonP();
+    public static native HParser hLengthValue(HParser length, HParser value);
+    public static native HParser hAttrBool(HParser p, HPredicate pred);
+    public static native HParser hAnd(HParser p);
+    public static native HParser hNot(HParser p);
+    public static native HParser hIndirect();
+}