Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import java.nio.charset.Charset;
- import java.util.List;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.regex.Pattern;
- import java.util.regex.Matcher;
- import java.io.ByteArrayOutputStream;
- import java.io.DataOutput;
- import java.io.DataOutputStream;
- import java.nio.ByteBuffer;
- import java.nio.charset.StandardCharsets;
- import java.io.File;
- import java.nio.file.Files;
- public class J2SCC {
- public static final Charset JIS_X0201 = Charset.forName("JIS_X0201");
- public static byte[] compile (List<String> input) {
- List<String> components = new ArrayList<String>();
- for (int i = 0; i < input.size(); i++) {
- String s = input.get(i);
- if (s.matches("(//.*)?")) continue;
- else if (s.matches("struct (\\w+) \\{")) {
- StringBuilder b = new StringBuilder();
- while (!input.get(i).matches(".*\\}")) {
- b.append(input.get(i));
- i++;
- }
- components.add("s" + b.toString() + input.get(i));
- } else if (s.matches("(\\p{Lower}+ )*\\w+ \\w+ ?\\(((\\w+ \\w+, )*\\w+ \\w+)?\\) ?\\{")) {
- int scope = 0;
- StringBuilder b = new StringBuilder();
- do {
- b.append(input.get(i));
- if (input.get(i).matches(".*\\}")) scope--;
- if (input.get(i).matches(".*\\{")) scope++;
- i++;
- } while (scope > 0);
- components.add("s" + b.toString() + input.get(i));
- } else if (s.matches("(final )?\\w+ \\w+( ?= ?.+)?;")) {
- components.add("f" + s);
- } else if (s.matches("@(\\w+)\\(((?:\\w+, ?)*\\w+)\\)")) {
- components.add("a" + s);
- } else if (s.matches("/\\*.*")) {
- while (!input.get(i).matches(".*?\\*/")) i++;
- } else if (s.matches("#\\p{Alpha}+.*")) {
- components.add("i" + s);
- } else {
- throw new IllegalArgumentException("could not match line " + i + ". If this line includes comments that are not full lines, J2SCC v1.0 does not support such comments");
- }
- }
- List<JFStruct> structs = new ArrayList<JFStruct>();
- for (String s : components) if (s.startsWith("s")) structs.add(JFStruct.compile(s.substring(1)));
- List<JFField> fields = new ArrayList<JFField>();
- for (String s : components) if (s.startsWith("f")) fields.add(JFField.compile(s.substring(1)));
- List<JFComponent> components2 = new ArrayList<JFComponent>();
- for (String s : components) {
- if (s.startsWith("i")) components2.add(JFInjection.compile(s.substring(1)));
- if (s.startsWith("m")) components2.add(JFMethod.compile(s.substring(1)));
- }
- HashMap<String, Object> cartHeader = new HashMap<String, Object>();
- ByteArrayOutputStream bo = new ByteArrayOutputStream();
- DataOutput o = new DataOutputStream(bo);
- Iterator<JFComponent> si = components2.iterator();
- List<JFAnnotationTemplate> annotations = new ArrayList<JFAnnotationTemplate>();
- for (String s : components) {
- if (s.startsWith("a"))
- annotations.add(JFAnnotationTemplate.compile(s.substring(1)));
- else {
- if (s.startsWith("f") && annotations.size() > 0) throw new IllegalArgumentException("attempted to annotate a field");
- else if (s.startsWith("s") && annotations.size() > 0) throw new IllegalArgumentException("attempted to annotate a struct");
- else if (s.startsWith("i") && annotations.size() > 0) throw new IllegalArgumentException("attempted to annotate an injection");
- else if (s.startsWith("m") || s.startsWith("i")) {
- JFComponent next = si.next();
- if (next instanceof JFMethod m) annotations.forEach(m::annotate);
- next.write(cartHeader);
- }
- }
- }
- ByteBuffer buf = ByteBuffer.wrap(bo.toByteArray());
- buf.put(0xFFAE, (byte) 0xD00000DB);
- buf.put(0xFFAF, (byte) 0x40);
- buf.put(0xFFB0, "JFjfgm".getBytes(StandardCharsets.US_ASCII));
- buf.put(0xFFB6, new byte[10]);
- buf.put(0xFFC0, getName(cartHeader.get("name")));
- buf.put(0xFFD5, (byte) 0x31);
- buf.put(0xFFD6, getChipset(cartHeader));
- buf.put(0xFFD7, (byte) Integer.bitCount((Integer.highestOneBit(bo.toByteArray().length) << 1) - 1));
- buf.put(0xFFD8, (byte) 19);
- buf.put(0xFFD9, getRegion(cartHeader));
- buf.put(0xFFDA, (byte) 0x33);
- buf.put(0xFFDB, getVersion(cartHeader));
- buf.putInt(0xFFDC, 0xFFFF0000);
- buf.putInt(0xFFE0, 0);
- buf.putInt(0xFFE4, 0xAFFFAFFF);
- buf.putShort(0xFFE8, (short) 0);
- buf.putShort(0xFFEA, getPauseVector(cartHeader));
- buf.putShort(0xFFEC, (short) 0);
- buf.putShort(0xFFEE, getInterruptVector(cartHeader));
- buf.putInt(0xFFF0, 0);
- buf.putInt(0xFFF4, 0xAFFFAFFF);
- buf.putShort(0xFFF8, (short) 0);
- buf.putShort(0xFFFA, (short) 0xA000AEFF);
- buf.putShort(0xFFFC, getResetVector(cartHeader));
- buf.putShort(0xFFFE, (short) 0xA000AEFF);
- byte[] data = buf.array();
- int checksum = 0;
- for (byte b : data)
- checksum += Byte.toUnsignedInt(b);
- checksum &= 0xFFFF;
- buf.putShort(0xFFDC, (short) Integer.reverseBytes(checksum - 0xFFFF));
- buf.putShort(0xFFDE, (short) Integer.reverseBytes(checksum));
- return buf.array();
- }
- private static byte[] getName (Object name) {
- if (name == null || !(name instanceof String)) name = "";
- if (((String) name).length() > 21) throw new IllegalArgumentException("maximum name length of 21 characters");
- return (((String) name) + " ".substring(((String) name).length())).getBytes(JIS_X0201);
- }
- private static byte getChipset (HashMap<String, Object> map) {
- boolean saveRAM = map.containsKey("save ram") ? (map.get("save ram") instanceof Boolean ? (Boolean) map.get("save ram") : false) : false;
- boolean coprocessor = map.containsKey("coprocessor");
- int cop = 0;
- if (coprocessor) cop = getCoprocessor(map.get("coprocessor"));
- return (byte) ((saveRAM ? 2 : 0) + cop);
- }
- private static int getCoprocessor (Object cc) {
- if (cc == null || !(cc instanceof String)) return 0;
- String cop = (String) cc;
- if (cop.matches("DSP-[1-4]")) return 0x03;
- if (cop.equals("GSU") || cop.equals("SuperFX")) return 0x13;
- if (cop.equals("OBC1")) return 0x23;
- if (cop.equals("SA-1")) return 0x33;
- if (cop.equals("S-DD1")) return 0x43;
- if (cop.equals("S-RTC")) return 0x53;
- return 0;
- }
- private static byte getRegion (HashMap<String, Object> map) {
- if (map.get("region") == null || !(map.get("region") instanceof String)) return (byte) 0;
- if (((String) map.get("region")).equalsIgnoreCase("US")) return (byte) 0;
- if (((String) map.get("region")).equalsIgnoreCase("USA")) return (byte) 0;
- if (((String) map.get("region")).equalsIgnoreCase("America")) return (byte) 0;
- if (((String) map.get("region")).equalsIgnoreCase("NTSC-U")) return (byte) 0;
- if (((String) map.get("region")).equalsIgnoreCase("Japan")) return (byte) 1;
- if (((String) map.get("region")).equalsIgnoreCase("JP")) return (byte) 1;
- if (((String) map.get("region")).equalsIgnoreCase("NTSC-J")) return (byte) 1;
- if (((String) map.get("region")).equalsIgnoreCase("France")) return (byte) 2;
- if (((String) map.get("region")).equalsIgnoreCase("Germany")) return (byte) 2;
- if (((String) map.get("region")).equalsIgnoreCase("United Kingdom")) return (byte) 2;
- if (((String) map.get("region")).equalsIgnoreCase("UK")) return (byte) 2;
- if (((String) map.get("region")).equalsIgnoreCase("Great Britain")) return (byte) 2;
- if (((String) map.get("region")).equalsIgnoreCase("Britain")) return (byte) 2;
- if (((String) map.get("region")).equalsIgnoreCase("EU")) return (byte) 2;
- if (((String) map.get("region")).equalsIgnoreCase("PAL")) return (byte) 2;
- return (byte) 0;
- }
- private static byte getVersion (HashMap<String, Object> map) {
- if (map.get("version") == null || !(map.get("version") instanceof Number)) return 0;
- return ((Number) map.get("version")).byteValue();
- }
- private static short getPauseVector (HashMap<String, Object> map) {
- if (map.get("pause") == null || !(map.get("pause") instanceof Number)) return (short) 0xA000AFFF;
- return Short.reverseBytes(((Number) map.get("pause")).shortValue());
- }
- private static short getInterruptVector (HashMap<String, Object> map) {
- if (map.get("interrupt") == null || !(map.get("interrupt") instanceof Number)) return (short) 0xA000AFFF;
- return Short.reverseBytes(((Number) map.get("interrupt")).shortValue());
- }
- private static short getResetVector (HashMap<String, Object> map) {
- if (map.get("pause") == null || !(map.get("pause") instanceof Number)) throw new IllegalStateException("no defined reset vector");
- return Short.reverseBytes(((Number) map.get("pause")).shortValue());
- }
- }
- public class JFROMLayout {
- private static int[] rom = new int[64];
- private static ByteBuffer romData = ByteBuffer.allocate(0x400000);
- private static int codeBanks = 0;
- private static int dataBanks = 0;
- public static int allocateCode (int size) {
- for (int i = 0; i < rom.length; i++) {
- int max = (i == 0) ? 0xFFAE : 0x010000;
- if (i > codeBanks) {
- if (codeBanks + dataBanks >= 0xFF) throw new IllegalStateException("code and data clash");
- codeBanks++;
- }
- if (rom[i] + size < max) {
- int ret = rom[i] + 0xC00000 + (i << 16);
- rom[i] += size;
- return ret;
- }
- }
- throw new IllegalStateException("rom overflow");
- }
- public static int allocateData (int size) {
- for (int i = rom.length - 1; i >= 0; i--) {
- int max = (i == 0) ? 0xFFAE : 0x010000;
- if (64 - i > dataBanks) {
- if (codeBanks + dataBanks >= 0xFF) throw new IllegalStateException("code and data clash");
- dataBanks++;
- }
- if (rom[i] + size < max) {
- int ret = rom[i] + 0xC00000 + (i << 16);
- rom[i] += size;
- return ret;
- }
- }
- throw new IllegalStateException("rom overflow");
- }
- public static void write (int address, byte[] data) {
- romData.put(address, data);
- }
- }
- public class JFStruct extends JFType {
- public final String name;
- public final int size;
- public final String[] names;
- public final JFType[] types;
- public JFStruct (String name, String[] names, JFType[] types) {
- this.name = name;
- int s = 0;
- for (JFType t : types)
- s += t.getSize();
- this.size = s;
- this.names = names;
- this.types = types;
- JFType.add(this.name, this);
- }
- public static JFStruct compile (String string) {
- Matcher m = Pattern.compile("struct (\\w+) \\{\\R((?:\\p{Blank}*\\w+ \\w+;\\R)*)\\}").matcher(string);
- if (!m.matches()) throw new IllegalArgumentException("illegal struct definition");
- String name = m.group(1);
- String[] ss = m.group(2).split(";\n");
- HashMap<String, JFType> map = new HashMap<String, JFType>();
- Pattern p = Pattern.compile("\\p{Blank}*(\\w+) (\\w+);");
- for (String s : ss) {
- m = p.matcher(s);
- if (!m.matches()) throw new IllegalArgumentException("illegal struct component definition");
- String name2 = m.group(1);
- if (map.containsKey(name2)) throw new IllegalArgumentException("struct component name in use");
- JFType type = JFType.getPrimitive(m.group(2));
- map.put(name2, type);
- }
- return new JFStruct(name, map.keySet().toArray(new String[map.size()]), map.values().toArray(new String[map.size()]));
- }
- public int getSize () {
- return this.size;
- }
- }
- public abstract class JFType {
- private static HashMap<String, JFType> primitives = new HashMap<String, JFType>();
- private static HashMap<String, JFType> structs = new HashMap<String, JFType>();
- static {
- primitives.put("byte", JFPrimitiveType.BYTE);
- primitives.put("word", JFPrimitiveType.WORD);
- primitives.put("dword", JFPrimitiveType.DWORD);
- }
- public static void add (String name, JFStruct struct) {
- structs.put(name, struct);
- }
- public static JFType getPrimitive (String name) {
- if (!primitives.containsKey(name)) throw new IllegalArgumentException("no primitive type named " + name);
- return primitives.get(name);
- }
- public static JFType getStruct (String name) {
- if (!structs.containsKey(name)) throw new IllegalArgumentException("no struct named " + name);
- return structs.get(name);
- }
- public static JFType getType (String name) {
- if (!structs.containsKey(name)) return getPrimitive(name);
- return structs.get(name);
- }
- public abstract int getSize ();
- }
- public class JFPrimitiveType extends JFType {
- public static final JFPrimitiveType BYTE = new JFPrimitiveType(1);
- public static final JFPrimitiveType WORD = new JFPrimitiveType(2);
- public static final JFPrimitiveType DWORD = new JFPrimitiveType(4);
- public final int size;
- public JFPrimitiveType (int size) {
- this.size = size;
- }
- public int getSize () {
- return this.size;
- }
- }
- public sealed interface JFField permits JFFinalField, JFNonFinalField {
- public static JFField compile (String f) {
- if (f.matches("final (\\w+) (\\w+)( = .*)?;"))
- return compileFinal(f);
- else
- return compileNonFinal(f);
- }
- private static JFFinalField compileFinal (String f) {
- Matcher m = Pattern.compile("final (\\w+) (\\w+) = (.*);").matcher(f);
- if (m.matches())
- throw new IllegalArgumentException("final field was not assigned");
- JFType type = JFType.getType(m.group(1));
- String name = m.group(2);
- String a = m.group(3);
- m = Pattern.compile("new (\\w+) ?\\(((?:\\w+ \\w+, ?)+\\w+ \\w+)\\)").matcher(a);
- if (m.matches()) {
- if (!type.equals(JFType.getType(m.group(1)))) throw new IllegalArgumentException("struct constructor type does not match struct definition");
- ByteBuffer b = ByteBuffer.allocate(type.getSize());
- String[] array = m.group(2).split(",");
- Pattern p = Pattern.compile(" ?(\\w+) (\\w+)");
- for (int i = 0; i < array.length; i++) {
- m = p.matcher(array[i]);
- b.put(JFValue.parse(JFType.getPrimitive(m.group(1)), m.group(2)));
- }
- return new JFFinalField(name, type, b.array());
- } else {
- return new JFFinalField(name, type, JFValue.parse(type, a));
- }
- }
- private static JFNonFinalField compileNonFinal (String f) {
- Matcher m = Pattern.compile("(?<type>\\w+) (?<assign>(?<name>\\w+) = (?:.*);)").matcher(f);
- return new JFNonFinalField(m.group("name"), JFType.getType(m.group("type")), m.group("assign"));
- }
- }
- public final class JFNonFinalField implements JFField {
- private static int staticMemory = 0;
- public final String name;
- public final JFType type;
- public final int address;
- public final String assignment;
- public JFNonFinalField (String name, JFType type) {
- this.name = name;
- this.type = type;
- this.address = staticMemory + 0x7E1000;
- this.assignment = assignment;
- staticMemory += type.getSize();
- if (staticMemory > 0xF000) throw new IllegalStateException("too many statically-allocated fields");
- }
- }
- public final class JFFinalField implements JFField {
- public final String name;
- public final JFType type;
- public final byte[] data;
- public JFFinalField (String name, JFType type, byte[] data) {
- this.name = name;
- this.type = type;
- this.data = data;
- }
- }
- public interface JFComponent {
- void write (HashMap<String, Object> header);
- }
- public interface JFInjection extends JFComponent {
- public static JFInjection compile (String string) {
- Matcher m = Pattern.compile("#(\\p{Alpha}+).*").matcher(string);
- String type = m.group(1);
- if (type.equals("import"))
- return _import(string.substring(7));
- else if (type.equals("title")) {
- m = Pattern.compile("#title (.*)").matcher(string);
- String title = m.group(1);
- return new JFInjection () {
- public void write (HashMap<String, Object> header) {
- header.put("name", title);
- }
- };
- } else if (type.equals("region")) {
- m = Pattern.compile("#region (.*)").matcher(string);
- String region = m.group(1);
- return new JFInjection () {
- public void write (HashMap<String, Object> header) {
- header.put("region", region);
- }
- };
- } else if (type.equals("coprocessor")) {
- m = Pattern.compile("#coprocessor (.*)").matcher(string);
- String region = m.group(1);
- return new JFInjection () {
- public void write (HashMap<String, Object> header) {
- header.put("coprocessor", region);
- }
- };
- } else if (type.equals("table")) {
- m = Pattern.compile("#table (\\w+) [((?:\\w+ )*\\w+)]").matcher(string);
- String name = m.group(1);
- if (m.group(2).equals("")) throw new IllegalArgumentException("empty data table");
- String[] params = m.group(2).split(" ");
- byte[] data = new byte[params.length];
- for (int i = 0; i < params.length; i++) data[i] = Byte.parseByte(params[i]);
- int address = JFROMLayout.allocateData(data.length);
- return new JFInjection () {
- public void write (HashMap<String, Object> header) {
- JFROMLayout.write(address, data);
- header.put("table " + name, Integer.valueOf(address));
- }
- };
- }
- }
- public static JFInjection _import (String sub) {
- Matcher m = Pattern.compile(" (.+) as (\\w+)(?:, (\\w+))").matcher(sub);
- byte[] data = Files.readAllBytes(new File(m.group(1)).toPath());
- String name = m.group(2);
- String type = m.group(3);
- if (m.group(2) == null) type = "binary";
- data = JFDataConverters.get(type).convert(data);
- int address = JFROMLayout.allocateData(data.length);
- return new JFInjection () {
- public void write (HashMap<String, Object> header) {
- JFROMLayout.write(address, data);
- header.put("import " + name, Integer.valueOf(address));
- }
- };
- }
- }
- public class JFAnnotationTemplate {
- public final String type;
- public final String[] params;
- public JFAnnotationTemplate (String type, String[] params) {
- this.type = type;
- this.params = params;
- }
- public static JFAnnotationTemplate compile (String annotation) {
- Matcher m = Pattern.compile("@(\\w+)\\(((?:\\w+, ?)*\\w+)\\)").matcher(annotation);
- String type = m.group(1);
- String[] params = m.group(2).split(",");
- for (int i = 0; i < params.length; i++) params[i] = params[i].stripLeading();
- return new JFAnnotationTemplate(type, params);
- }
- }
- public class JFValue {
- public static byte[] parse (JFType type, String value) {
- if (!(type instanceof JFPrimitiveType)) throw new IllegalArgumentException("attempted to parse an invalid type");
- if (type == JFPrimitiveType.BYTE) return parseByte(value);
- else if (type == JFPrimitiveType.WORD) return parseWord(value);
- else if (type == JFPrimitiveType.DWORD) return parseDWord(value);
- else System.exit(177);
- }
- private static byte[] parseByte (String value) {
- Matcher m = Pattern.compile("(['\"])(.)\\1").matcher(value);
- if (m.matches())
- return m.group(2).getBytes(StandardCharsets.US_ASCII);
- m = Pattern.compile("0x([0-9a-fA-F]{1,2})").matcher(value);
- if (m.matches())
- return new byte[] { Byte.parseByte(m.group(1), 16) };
- m = Pattern.compile("0o?([0-3]?[0-7]{1,2})").matcher(value);
- if (m.matches())
- return new byte[] { Byte.parseByte(m.group(1), 8) };
- m = Pattern.compile("0b?([01]{1,8})").matcher(value);
- if (m.matches())
- return new byte[] { Byte.parseByte(m.group(1), 2) };
- return new byte[] { Byte.parseByte(value, 10) };
- }
- private static byte[] parseWord (String value) {
- Matcher m = Pattern.compile("(['\"])(..)\\1").matcher(value);
- if (m.matches())
- return m.group(2).getBytes(StandardCharsets.US_ASCII);
- ByteBuffer b = ByteBuffer.allocate(2);
- m = Pattern.compile("0x([0-9a-fA-F]{1,4})").matcher(value);
- if (m.matches())
- return b.putShort(Short.parseShort(m.group(1), 16)).array();
- m = Pattern.compile("0o?([01]?[0-7]{1,5})").matcher(value);
- if (m.matches())
- return b.putShort(Short.parseShort(m.group(1), 8)).array();
- m = Pattern.compile("0b?([01]{1,16})").matcher(value);
- if (m.matches())
- return b.putShort(Short.parseShort(m.group(1), 2)).array();
- return b.putShort(Short.parseShort(m.group(1), 10)).array();
- }
- private static byte[] parseDWord (String value) {
- Matcher m = Pattern.compile("(['\"])(....)\\1").matcher(value);
- if (m.matches())
- return m.group(2).getBytes(StandardCharsets.US_ASCII);
- ByteBuffer b = ByteBuffer.allocate(4);
- m = Pattern.compile("0x([0-9a-fA-F]{1,8})").matcher(value);
- if (m.matches())
- return b.putInt(Integer.parseInt(m.group(1), 16)).array();
- m = Pattern.compile("0o?([0-3]?[0-7]{1,10})").matcher(value);
- if (m.matches())
- return b.putInt(Integer.parseInt(m.group(1), 8)).array();
- m = Pattern.compile("0b?([01]{1,32})").matcher(value);
- if (m.matches())
- return b.putInt(Integer.parseInt(m.group(1), 2)).array();
- return b.putInt(Integer.parseInt(m.group(1), 10)).array();
- }
- }
- public class JFDataConverters {
- private static final HashMap<String, JFDataConverter> REGISTRY = new HashMap<String, JFDataConverter>();
- static {
- REGISTRY.put("binary", (d, m) -> d);
- }
- public static JFDataConverter get (String s) {
- return REGISTRY.get(s);
- }
- }
- public interface JFDataConverter {
- byte[] convert (byte[] input, HashMap<String, Object> map);
- }
Advertisement
Add Comment
Please, Sign In to add comment