/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;
class NIDInfo {
public String prx;
public String prxName;
public String libraryName;
public String functionName;
public int functionNID;
public boolean resolved;
public String firmwareVersion;
public NIDInfo(int nid) {
functionNID = nid;
resolved = false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("prx=" + prx + " ");
sb.append("prxName=" + prxName + " ");
sb.append("libraryName=" + libraryName + " ");
sb.append("functionName=" + functionName + " ");
sb.append("functionNID=" + String.format("0x%08X", functionNID));
return sb.toString();
}
}
class Module {
private final String m_name;
private Hashtable<String, String> m_functions;
public Module(String name) {
m_name = new String(name);
m_functions = new Hashtable<String, String>();
}
public String getName() {
return m_name;
}
public String getFunctionFromModule(String nid) {
try {
return m_functions.get(nid);
} catch (NullPointerException dummy) {
return null;
}
}
public int countFunctions() {
return m_functions.size();
}
public void addFunction(String nid, String name) {
//System.out.println(nid + "/" + name);
m_functions.put(nid, name);
}
}
class Firmware {
private String m_version;
private int m_moduleCount;
private int m_functionCount;
private Hashtable<String, Module> m_moduleTable;
// psplibdoc parser based on hlide's FunctionLibrary.java
public Firmware(String version, String psplibdoc_filename) throws Exception {
m_version = version;
m_moduleCount = 0;
m_functionCount = 0;
m_moduleTable = new Hashtable<String, Module>();
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(new File(psplibdoc_filename));
XPath modules = XPath.newInstance("/PSPLIBDOC/PRXFILES/PRXFILE/LIBRARIES/LIBRARY");
List<?> LibList = modules.selectNodes(doc);
//m_moduleList = modules.selectNodes(doc, "//NAME");
Iterator<?> i = LibList.iterator();
while (i.hasNext()) {
Element curEl = (Element) i.next();
String modName = curEl.getChild("NAME").getText();
Module newMod = new Module(modName);
List<?> FunctionList = curEl.getChild("FUNCTIONS").getChildren("FUNCTION");
Iterator<?> j = FunctionList.iterator();
while (j.hasNext()) {
Element funcEl = (Element) j.next();
newMod.addFunction(funcEl.getChild("NID").getText(), funcEl.getChild("NAME").getText());
m_functionCount++;
}
m_moduleCount++;
m_moduleTable.put(modName, newMod);
}
System.out.println("filename: " + psplibdoc_filename + " modules: " + m_moduleCount + " functions: " + m_functionCount);
}
public String getVersion() {
return m_version;
}
public int getModuleCount() {
return m_moduleCount;
}
public int getFunctionCount() {
return m_functionCount;
}
/** @return true if resolved */
public boolean resolveNID(NIDInfo info) {
for (Module module : m_moduleTable.values()) {
String functionName = module.getFunctionFromModule(String.format("0x%08X", info.functionNID));
if (functionName != null) {
info.functionName = functionName;
info.libraryName = module.getName();
if (!info.resolved) {
info.firmwareVersion = m_version;
}
info.resolved = true;
return true;
}
}
return false;
}
}
public class ResolveUnmappedNIDs {
public static HashMap<Integer, NIDInfo> parseLog(String filename) {
LinkedHashMap<Integer, NIDInfo> nids = new LinkedHashMap<Integer, NIDInfo>();
try {
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
String line;
while((line = br.readLine()) != null) {
String[] parts = line.split(" ");
for (int i = 10; i < parts.length; i++) {
if (parts[i].startsWith("[0x")) {
try {
int nid = (int)Long.parseLong(parts[i].substring(3, 11), 16);
NIDInfo info = new NIDInfo(nid);
nids.put(nid, info);
} catch(Exception e) {
//System.err.println(e.getMessage());
//System.err.println("error near '" + parts[i] + "'");
}
break;
}
}
}
br.close();
fr.close();
} catch(FileNotFoundException e) {
System.err.println("parseLog: File not found: " + filename);
} catch(Exception e) {
System.err.println("parseLog: " + filename);
e.printStackTrace();
}
return nids;
}
public static void processNIDs(LinkedList<Firmware> firmware, HashMap<Integer, NIDInfo> nids) {
for (NIDInfo info : nids.values()) {
for (Firmware fw : firmware) {
fw.resolveNID(info);
// keep resolving in newer fw, since later libdoc have nids decoded to function names
//if (found)
// break;
}
}
}
public static void printResults(HashMap<Integer, NIDInfo> nids, int syscallCode) {
for (NIDInfo info : nids.values()) {
//System.out.println(info);
// sample:
// sceUmdReplaceProhibit(0x3007, 0x87533940), // 2.00+
String functionName = (info.functionName == null) ? String.format("unknown_%08X", info.functionNID) : info.functionName;
String firmwareVersion = (info.firmwareVersion == null) ? "" : " // " + info.firmwareVersion + "+";
String msg = String.format("%s(0x%04x, 0x%08X),%s",
functionName, syscallCode++, info.functionNID, firmwareVersion);
System.out.println(msg);
}
}
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("parameters: <logfile> [syscallCodeStart]");
System.err.println("example: fragment.txt 0x3000");
System.exit(1);
}
// Load psplibdocs
String prefixPath = "psplibdoc/";
LinkedList<Firmware> firmware = new LinkedList<Firmware>();
try {
firmware.add(new Firmware("1.00", prefixPath + "100_psplibdoc_230808.xml"));
firmware.add(new Firmware("1.50", prefixPath + "150_psplibdoc_190808.xml"));
firmware.add(new Firmware("2.00", prefixPath + "200_psplibdoc_260808.xml"));
firmware.add(new Firmware("2.50", prefixPath + "250_psplibdoc_270808.xml"));
firmware.add(new Firmware("2.71", prefixPath + "271_psplibdoc_280808.xml"));
firmware.add(new Firmware("3.52", prefixPath + "352_psplibdoc_190808.xml"));
firmware.add(new Firmware("3.95", prefixPath + "395_psplibdoc_020508.xml"));
firmware.add(new Firmware("4.05", prefixPath + "405_psplibdoc_190808.xml"));
firmware.add(new Firmware("5.00", prefixPath + "500_psplibdoc_191008.xml"));
} catch(Exception e) {
e.printStackTrace();
}
// Load log file fragment
HashMap<Integer, NIDInfo> nids = parseLog(args[0]);
// Resolve nids
processNIDs(firmware, nids);
// Output
int syscallCode = 0x3000;
if (args.length >= 2) {
try {
syscallCode = Integer.parseInt(args[1], 16);
} catch(Exception e) {
System.err.println("bad syscall parameter, defaulting to 0x3000");
}
}
printResults(nids, syscallCode);
}
}