package org.histidine.modlistextractor;
import java.awt.FileDialog;
import java.awt.Frame;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class main {
public static void main(String[] args) {
FileDialog loadDialog = new FileDialog((Frame)null, "Select save descriptor.xml");
loadDialog.setMode(FileDialog.LOAD);
//dialog.setFilenameFilter(new XMLFileFilter());
loadDialog.setFile("descriptor.xml");
loadDialog.setVisible(true);
if (loadDialog.getFile() == null) {
System.out.println("No file selected");
System.exit(0);
return;
}
Path path = Paths.get(loadDialog.getDirectory(), loadDialog.getFile());
System.out.println("Loading file " + path);
List<ModEntry> mods;
mods = getModList(path.toString());
String str = createEnabledModsJsonAsString(mods);
//System.out.println(str);
FileDialog saveDialog = new FileDialog((Frame)null, "Select enabled_mods.json");
saveDialog.setMode(FileDialog.SAVE);
String modPath = Paths.get(loadDialog.getDirectory(), "..", "..", "mods").toString();
saveDialog.setDirectory(modPath);
saveDialog.setFile("enabled_mods.json");
saveDialog.setVisible(true);
if (saveDialog.getFile() == null) {
System.exit(0);
return;
}
path = Paths.get(saveDialog.getDirectory(), saveDialog.getFile());
File json = path.toFile();
try (Writer output = new BufferedWriter(new FileWriter(json))) {
output.write(str);
output.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
System.out.println("Done, press Enter to exit");
try {
System.in.read();
} catch (IOException ex) {}
System.exit(0);
}
/**
* Gets the mod list from a Starsector save's descriptor.xml.
* @param path
* @return
*/
public static List<ModEntry> getModList(String path)
{
Map<Integer, ModEntry> allMods = new HashMap<>();
List<ModEntry> modlist = new ArrayList<>();
try {
File xml = new File(path);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xml);
doc.getDocumentElement().normalize();
// Load mod data from all mods ever enabled
Node everMods = doc.getElementsByTagName("allModsEverEnabled").item(0);
NodeList nlEverMods = everMods.getChildNodes();
for (int i=0; i<nlEverMods.getLength(); i++) {
Node node = nlEverMods.item(i);
String nodeName = node.getNodeName();
if (!nodeName.equals("com.fs.starfarer.campaign.ModAndPluginData_-EnabledModData"))
continue;
Element el = (Element)node;
int xmlId = Integer.parseInt(((Element)getElement(el, "spec")).getAttribute("z"));
String id = getElement(el, "id").getTextContent();
String name = getElement(el, "name").getTextContent();
String versionStr = "";
NodeList version = el.getElementsByTagName("versionInfo");
for (int j=0; j<version.getLength(); j++) {
Node versionNode = version.item(j);
if (versionNode.getNodeName().equals("string")) {
versionStr = versionNode.getTextContent();
}
}
// Store only the mod's folder name, not the full path
// This is because someone else's saves may come from a Starsector copy installed to a different directory
String modPath = getElement(el, "path").getTextContent();
String folderName = FileSystems.getDefault().getPath(modPath).getFileName().toString();
ModEntry entry = new ModEntry(id, name, versionStr, folderName);
allMods.put(xmlId, entry);
}
// Process mods last saved with
// Go up three levels: save folder -> saves/ -> starsector root
Path modFolderPath = FileSystems.getDefault().getPath(path, "..", "..", "..", "mods").normalize();
Node lastMods = doc.getElementsByTagName("enabledMods").item(0);
NodeList nlLastMods = lastMods.getChildNodes();
for (int i=0; i<nlEverMods.getLength(); i++) {
Node node = nlLastMods.item(i);
if (node == null) {
continue;
}
String nodeName = node.getNodeName();
if (!nodeName.equals("com.fs.starfarer.campaign.ModAndPluginData_-EnabledModData"))
continue;
Element el = (Element)node;
int xmlId = Integer.parseInt(((Element)getElement(el, "spec")).getAttribute("ref"));
ModEntry entry = allMods.get(xmlId);
Path modPath = FileSystems.getDefault().getPath(modFolderPath.toString(), entry.folderName);
//System.out.println(modPath.toString() + ", " + entry.folderName);
if (!Files.exists(modPath)) {
System.err.println("Warning, mod not found: " + entry.name + " " +
entry.version + " (" + modPath.toString() + ")");
}
//System.out.println(entry);
modlist.add(entry);
}
Collections.sort(modlist);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return modlist;
}
/**
* Creates a string ready to write to {@code enabled_mods.json}.
* @param mods
* @return
*/
public static String createEnabledModsJsonAsString(List<ModEntry> mods) {
StringBuilder sb = new StringBuilder();
sb.append("{\"enabledMods\": [\r\n");
for (ModEntry mod : mods) {
sb.append(" \"").append(mod.id).append("\",\r\n");
}
sb.append("]}");
return sb.toString();
}
public static Node getElement(Element el, String id) {
return el.getElementsByTagName(id).item(0);
}
public static class ModEntry implements Comparable<ModEntry> {
public String id;
public String name;
public String version;
public String folderName;
public ModEntry(String id, String name, String version, String folderName) {
this.id = id;
this.name = name;
this.version = version;
this.folderName = folderName;
}
@Override
public String toString() {
String str = String.format("%s v%s (id %s, path %s)", name, version, id, folderName);
return str;
}
@Override
public int compareTo(ModEntry other) {
return this.name.compareTo(other.name);
}
}
}