Skip to content

Commit

Permalink
Add scan-simulator console command, which shows attributes and thei…
Browse files Browse the repository at this point in the history
…r file offsets of a simulator data file. This can help editing save game data.
  • Loading branch information
emd4600 committed Jan 6, 2024
1 parent e6b1e86 commit fd03900
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/sporemodder/HashManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public void initialize(Properties properties) {

UIManager.get().tryAction(() -> {
simulatorRegistry.read(PathManager.get().getProgramFile(simulatorRegistry.getFileName()));
simulatorRegistry.read(PathManager.get().getProgramFile("reg_simulator_stub.txt"));
}, "The simulator attributes registry (reg_simulator.txt) is corrupt or missing.");

CnvUnit.loadNameRegistry();
Expand Down
23 changes: 21 additions & 2 deletions src/sporemodder/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -19,8 +20,10 @@
import sporemodder.file.dbpf.DBPFPackingTask;
import sporemodder.file.dbpf.DBPFUnpackingTask;
import sporemodder.file.filestructures.FileStream;
import sporemodder.file.filestructures.MemoryStream;
import sporemodder.file.filestructures.StreamReader;
import sporemodder.file.filestructures.StreamWriter;
import sporemodder.file.simulator.SimulatorClass;
import sporemodder.file.spui.SporeUserInterface;
import sporemodder.util.NameRegistry;

Expand Down Expand Up @@ -60,7 +63,8 @@ public String[] getVersion() throws Exception {
EncodeCommand.class,
UnpackCommand.class,
PackCommand.class,
FindSpuiForControlId.class
FindSpuiForControlIdCommand.class,
ScanSimulatorCommand.class
})
public static class SMFXCommand implements Callable<Integer> {

Expand Down Expand Up @@ -393,7 +397,7 @@ public Integer call() throws Exception {
}

@Command(name = "find-spui", description = "Find all SPUIs that have a specific control ID", mixinStandardHelpOptions = true)
public static class FindSpuiForControlId implements Callable<Integer> {
public static class FindSpuiForControlIdCommand implements Callable<Integer> {
@Parameters(index = "0", description = "The program will look all .spui files in this folder and subfolders")
private File inputFolder;

Expand All @@ -407,4 +411,19 @@ public Integer call() throws Exception {
return 0;
}
}

@Command(name = "scan-simulator", description = "Scan file offsets of attributes in simulator data files", mixinStandardHelpOptions = true)
public static class ScanSimulatorCommand implements Callable<Integer> {
@Parameters(index = "0", description = "Input simulator data file")
private File inputFile;

@Override
public Integer call() throws Exception {
try (StreamReader stream = new MemoryStream(Files.readAllBytes(inputFile.toPath()))) {
SimulatorClass.scanClasses(stream);
}

return 0;
}
}
}
111 changes: 88 additions & 23 deletions src/sporemodder/file/simulator/SimulatorClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,37 @@
****************************************************************************/
package sporemodder.file.simulator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import sporemodder.file.filestructures.StreamReader;
import sporemodder.file.filestructures.StreamWriter;
import sporemodder.HashManager;
import sporemodder.file.simulator.attributes.SimulatorAttribute;
import sporemodder.util.IntPair;

import javax.management.Attribute;

public abstract class SimulatorClass {

public static int GENERIC_SERIALIZER_ID = 0x1A80D26;

public class AttributeHeader {
public int id;
public int size;

public AttributeHeader(int id, int size) {
this.id = id;
this.size = size;
}
}

protected final LinkedHashMap<String, SimulatorAttribute> attributes = new LinkedHashMap<String, SimulatorAttribute>();
protected int classID;
protected int classSize;

public SimulatorClass(int classID) {
this.classID = classID;
Expand All @@ -42,6 +61,9 @@ public SimulatorClass(int classID) {
public int getClassID() {
return classID;
}
public int getClassSize() {
return classSize;
}

/**
* Returns the correct attribute type for the given attribute name. This must be implemented in subclasses to
Expand All @@ -60,33 +82,31 @@ public int calculateSize() {

return size;
}

public void read(StreamReader stream) throws Exception {


public List<AttributeHeader> readHeader(StreamReader stream) throws IOException {
classID = stream.readInt();

// size
stream.readInt();

classSize = stream.readInt();

int count = stream.readInt();
int[] ids = new int[count];
int[] sizes = new int[count];

HashManager hasher = HashManager.get();

for (int i = 0; i < count; i++) {
ids[i] = stream.readInt();
sizes[i] = stream.readInt();
}

List<AttributeHeader> headers = new ArrayList<>();
for (int i = 0; i < count; i++) {
String name = hasher.getSimulatorName(ids[i]);

SimulatorAttribute attribute = createAttribute(name);
attribute.read(stream, sizes[i]);

setAttribute(name, attribute);
headers.add(new AttributeHeader(stream.readInt(), stream.readInt()));
}
return headers;
}

public void read(StreamReader stream) throws Exception {
List<AttributeHeader> headers = readHeader(stream);

HashManager hasher = HashManager.get();
for (AttributeHeader header : headers) {
String name = hasher.getSimulatorName(header.id);

SimulatorAttribute attribute = createAttribute(name);
attribute.read(stream, header.size);

setAttribute(name, attribute);
}
}

public void write(StreamWriter stream) throws Exception {
Expand Down Expand Up @@ -168,4 +188,49 @@ public void printXML(StringBuilder sb, String tabulation) {
// default: return null;
// }
// }

public static class UnknownSimulatorClass extends SimulatorClass {
public UnknownSimulatorClass(int classID) {
super(classID);
}

@Override
public SimulatorAttribute createAttribute(String name) {
return null;
}
}

public static void scanClasses(StreamReader stream) throws IOException {
while (stream.getFilePointer() + 4 < stream.length()) {
int testValue = stream.readInt();
if (testValue == GENERIC_SERIALIZER_ID) {
stream.seek(stream.getFilePointer() - 4);
SimulatorClass simulatorClass = new UnknownSimulatorClass(GENERIC_SERIALIZER_ID);
long classOffset = stream.getFilePointer();
List<AttributeHeader> headers = simulatorClass.readHeader(stream);
long offset = stream.getFilePointer();

System.out.println("Found class at offset " + classOffset + ", attributes:");
for (AttributeHeader header : headers) {
StringBuilder sb = new StringBuilder();
sb.append(" ");
sb.append(HashManager.get().hexToString(header.id));
String attributeName = HashManager.get().getSimulatorName(header.id);
if (attributeName != null) {
sb.append(" ");
sb.append(attributeName);
}
sb.append(": offset = ");
sb.append(offset);
sb.append(" size = ");
sb.append(header.size);
System.out.println(sb.toString());

offset += header.size;
}
System.out.println();
stream.seek(offset);
}
}
}
}

0 comments on commit fd03900

Please sign in to comment.