Last active
October 17, 2024 06:41
-
-
Save BenjaminUrquhart/1de2d69c48c92dca3045c35458a74d98 to your computer and use it in GitHub Desktop.
Java utility class to properly read/write decompressed RPGMaker MV/MZ save files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.concurrent.atomic.AtomicInteger; | |
import org.json.JSONArray; | |
import org.json.JSONObject; | |
public class JsonEx { | |
// Listen this is not how you use Java but | |
// I don't care. | |
record Reference(Object base, Object key, int id) {} | |
public static Object deserialize(String json) { | |
return deserialize(new JSONObject(json)); | |
} | |
// This modifies the input field btw I'm too lazy to change it | |
public static Object deserialize(JSONObject data) { | |
Map<Integer, Object> objects = new HashMap<>(); | |
Set<Reference> references = new HashSet<>(); | |
// It's possible that this is a JSONArray and not a JSONObject. | |
// Given the intended use case for this little project is RPGMaker | |
// save file parsing, that shouldn't happen, but who knows? | |
Object result = deserializeInternal(data, objects, references, "root"); | |
Object obj; | |
for(Reference ref : references) { | |
if(objects.containsKey(ref.id)) { | |
obj = objects.get(ref.id); | |
} | |
else { | |
System.err.println("Failed to find reference " + ref.id); | |
obj = null; | |
} | |
if(ref.base instanceof JSONArray arr) { | |
arr.put((int)ref.key, obj); | |
} | |
else if(ref.base instanceof JSONObject o) { | |
o.put((String)ref.key, obj); | |
} | |
else { | |
throw new IllegalArgumentException(String.format("Invalid type %s for reference %d", ref.base.getClass().getName(), ref.id)); | |
} | |
} | |
return result; | |
} | |
private static Object deserializeInternal(JSONObject data, Map<Integer, Object> objects, Set<Reference> references, String path) { | |
if(data.has("@r")) { | |
throw new IllegalStateException(String.format("Cannot deserialize reference at %s", path)); | |
} | |
boolean hasType = data.has("@"); | |
if(hasType) { | |
System.out.printf("%s is type %s\n", path, data.get("@")); | |
//data.remove("@"); | |
} | |
if(data.has("@c")) { | |
if(data.has("@a")) { | |
if(hasType) { | |
throw new IllegalStateException(String.format("Array entry at %s contains a type entry ('@') which is not allowed", path)); | |
} | |
if(data.keySet().size() > 2) { | |
throw new IllegalStateException(String.format("Array entry at %s contains extra keys (expected [@a, @c], got %s)", path, data.keySet())); | |
} | |
JSONArray arr = data.getJSONArray("@a"); | |
for(int i = 0; i < arr.length(); i++) { | |
if(arr.get(i) instanceof JSONObject element) { | |
if(element.has("@r")) { | |
references.add(new Reference(arr, i, element.getInt("@r"))); | |
} | |
else { | |
arr.put(i, deserializeInternal(element, objects, references, path + "[" + i + "]")); | |
} | |
} | |
} | |
objects.put(data.getInt("@c"), arr); | |
data.remove("@c"); | |
data.remove("@a"); | |
return arr; | |
} | |
else { | |
objects.put(data.getInt("@c"), data); | |
data.remove("@c"); | |
} | |
} | |
for(String key : data.keySet()) { | |
if(data.get(key) instanceof JSONObject obj) { | |
if(obj.has("@r")) { | |
references.add(new Reference(data, key, obj.getInt("@r"))); | |
} | |
else { | |
data.put(key, deserializeInternal(obj, objects, references, path + "." + key)); | |
} | |
} | |
} | |
return data; | |
} | |
public static JSONObject serialize(JSONObject data) { | |
Map<Object, Integer> ids = new HashMap<>(); | |
AtomicInteger id = new AtomicInteger(1); | |
return serializeInternal(data, ids, id); | |
} | |
public static JSONObject serialize(JSONArray data) { | |
Map<Object, Integer> ids = new HashMap<>(); | |
AtomicInteger id = new AtomicInteger(1); | |
return serializeInternal(data, ids, id); | |
} | |
private static JSONObject serializeInternal(JSONObject data, Map<Object, Integer> ids, AtomicInteger id) { | |
if(ids.containsKey(data)) { | |
return new JSONObject().put("@r", ids.get(data)); | |
} | |
if(data.has("@c")) { | |
throw new IllegalStateException("Illegal key: @c"); | |
} | |
if(data.has("@a")) { | |
throw new IllegalStateException("Illegal key: @a"); | |
} | |
int c = id.getAndIncrement(); | |
JSONObject out = new JSONObject().put("@c", c); | |
ids.put(data, c); | |
for(String key : data.keySet()) { | |
if(data.get(key) instanceof JSONObject obj) { | |
out.put(key, serializeInternal(obj, ids, id)); | |
} | |
else if(data.get(key) instanceof JSONArray arr) { | |
out.put(key, serializeInternal(arr, ids, id)); | |
} | |
else { | |
out.put(key, data.get(key)); | |
} | |
} | |
return out; | |
} | |
private static JSONObject serializeInternal(JSONArray data, Map<Object, Integer> ids, AtomicInteger id) { | |
JSONObject out = new JSONObject(); | |
if(ids.containsKey(data)) { | |
return out.put("@r", ids.get(data)); | |
} | |
JSONArray array = new JSONArray(data.length()); | |
int c = id.getAndIncrement(); | |
out.put("@a", array).put("@c", c); | |
ids.put(data, c); | |
for(int i = 0; i < data.length(); i++) { | |
if(data.get(i) instanceof JSONObject obj) { | |
array.put(i, serializeInternal(obj, ids, id)); | |
} | |
else if(data.get(i) instanceof JSONArray arr) { | |
array.put(i, serializeInternal(arr, ids, id)); | |
} | |
else { | |
array.put(i, data.get(i)); | |
} | |
} | |
return out; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment