commit 060da45ee03d8bb6db04a37c553503987d808d51 Author: CekisSWG Date: Thu Feb 21 11:39:09 2019 -0800 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..c76a33d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +"# Snapshot Reader" diff --git a/ShapshotReader.fxml b/ShapshotReader.fxml new file mode 100644 index 0000000..bb7f446 --- /dev/null +++ b/ShapshotReader.fxml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SnapshotReader.java b/SnapshotReader.java new file mode 100644 index 0000000..560ea79 --- /dev/null +++ b/SnapshotReader.java @@ -0,0 +1,77 @@ +/** + * Created by Cekis on 2/20/2017. + */ + +import javafx.application.Application; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.Scene; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Stage; +import swg.WSFile; + +import java.io.IOException; + +public class SnapshotReader extends Application { + + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) throws IOException { + primaryStage.setTitle("SWG Snapshot Viewer"); + + TabPane tabView = new TabPane(); + Tab tab = new Tab(); + tab.closableProperty().setValue(false); + tab.setText("lok.ws"); + TableView dataTable = new TableView(); + + ObservableList cols = dataTable.getColumns(); + + cols.add(makeColumn("Object ID", "id",75)); + cols.add(makeColumn("Parent ID", "parentId", 75)); + cols.add(makeColumn("Template", "template", dataTable, 550d)); + cols.add(makeColumn("Index", "nodeIndex", 50)); + cols.add(makeColumn("X", "x", 50)); + cols.add(makeColumn("Y", "y", 50)); + cols.add(makeColumn("Z", "z", 50)); + cols.add(makeColumn("QW", "objW",50)); + cols.add(makeColumn("QX", "objX",50)); + cols.add(makeColumn("QY", "objY",50)); + cols.add(makeColumn("QZ", "objZ",50)); + + tab.setContent(dataTable); + tabView.getTabs().add(tab); + + WSFile wsFile = new WSFile(); + wsFile.readFile("snapshot/lok.ws"); + tab.setText(wsFile.getAreaName() + ".ws"); + + ObservableList data = FXCollections.observableArrayList(wsFile.getAllNodes().toArray()); + dataTable.setItems(data); + + Scene scene = new Scene(tabView, 300, 275); + primaryStage.setScene(scene); + primaryStage.show(); + } + private TableColumn makeColumn(String name, String property, double width){ + TableColumn newCol = new TableColumn(); + newCol.setPrefWidth(width); + newCol.setText(name); + newCol.setCellValueFactory(new PropertyValueFactory(property)); + return newCol; + } + private TableColumn makeColumn(String name, String property, TableView dt, double diff){ + TableColumn newCol = new TableColumn(); + newCol.prefWidthProperty().bind(dt.widthProperty().subtract(diff)); + newCol.setText(name); + newCol.setCellValueFactory(new PropertyValueFactory(property)); + return newCol; + } +} diff --git a/components/SnapshotTab.fxml b/components/SnapshotTab.fxml new file mode 100644 index 0000000..59f0d37 --- /dev/null +++ b/components/SnapshotTab.fxml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main.java b/main.java new file mode 100644 index 0000000..0b78434 --- /dev/null +++ b/main.java @@ -0,0 +1,29 @@ +import swg.WSFile; + +import java.io.File; + +/** + * Created by Cekis on 2/18/2017. + */ +public class main { + public static void main(String[] args) + { + if(args != null){ + switch(args.length){ + case 0: + File f = new File("snapshot"); + for(File file : f.listFiles()){ + WSFile snapshot = new WSFile(); + snapshot.readFile("snapshot/" + file.getName()); + } + break; + case 1: + WSFile snapshot = new WSFile(); + snapshot.readFile(args[0]); + break; + default: + break; + } + } + } +} diff --git a/swg/WSFile.java b/swg/WSFile.java new file mode 100644 index 0000000..6f1736d --- /dev/null +++ b/swg/WSFile.java @@ -0,0 +1,291 @@ +package swg; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Cekis on 2/18/2017. + */ +public class WSFile { + private List _nodes; + private String[] _types; + private String _fileName; + private String _areaName; + + public List getNodes() { + if(_nodes == null){ + _nodes = new ArrayList<>(); + } + return _nodes; + } + + public void setNodes(List _nodes) { + this._nodes = _nodes; + } + + public String[] getTypes() { + return _types; + } + + public void setTypes(String[] _types) { + this._types = _types; + } + + public String getFileName() { + return _fileName; + } + + public void setFileName(String fileName) { + this._fileName = fileName; + } + + public String getAreaName() { + return _areaName; + } + + public void setAreaName(String area) { + this._areaName = area; + } + + public void readFile(String filename){ + try{ + _areaName = filename.replaceAll("snapshot/","").replaceAll(".ws",""); + parseFile(new File(filename)); + } + catch(Exception e){ + e.printStackTrace(); + } + } + + private void parseFile(File file) throws IOException { + FileInputStream fis = null; + BufferedInputStream bi = null; + try { + byte[] FORM = new byte[4]; + byte[] WSNPFORM = new byte[8]; + byte[] t0001FORM = new byte[8]; + byte[] lengthBuff = new byte[4]; + byte[] OTNL = new byte[4]; + + fis = new FileInputStream(file); + bi = new BufferedInputStream(fis); + bi.read(FORM); + bi.read(new byte[4]); // length of form + bi.read(WSNPFORM); + bi.read(new byte[4]); // length of wsnpform + bi.read(t0001FORM); + bi.read(lengthBuff); // length of t0001form + + int buffLength = ByteBuffer.wrap(lengthBuff).getInt(); + + byte[] nodesBuffer = new byte[buffLength]; + + bi.read(nodesBuffer); + + bi.read(OTNL); // OTNL + bi.read(new byte[4]); // length of OTNL + bi.read(new byte[4]); // count (string) + + List listTypes = new ArrayList<>(); + String type = ""; + while(bi.available() > 0){ + byte[] ba = new byte[1]; + bi.read(ba); + while(ba[0] != 0){ + type += (char) ba[0]; + bi.read(ba); + } + listTypes.add(type); + type = ""; + } + _types = listTypes.toArray(new String[listTypes.size()]); + + parseNodes(nodesBuffer); + + //System.out.println("objid\tcontainer\tserver_template_crc\tcell_index\tpx\tpy\tpz\tqw\tqx\tqy\tqz\tscripts\tobjvars"); + //System.out.println("i\ti\th\ti\tf\tf\tf\tf\tf\tf\tf\ts\tp"); + //printNodes(_nodes); + //saveNodesToFiles(_nodes); + } + catch(Exception e){ + e.printStackTrace(); + } + finally{ + if(bi != null){ + bi.close(); + } + if(fis != null){ + fis.close(); + } + } + } + private void parseNodes(byte[] data) throws IOException { + int remaining = 0; + byte[] FORM = new byte[4]; + byte[] formBuffer = new byte[4]; + byte[] NODEFORM = new byte[8]; + byte[] t0000DATA = new byte[8]; + byte[] value = new byte[4]; + WSNode tempNode = null; + + BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(data)); + + try { + while (remaining < data.length) { + // header data + bis.read(FORM); + bis.read(formBuffer); // length + if(WSFile.IsAlpha(formBuffer)){ + bis.read(new byte[4]); + remaining += 4; + } + bis.read(NODEFORM); + bis.read(new byte[4]); // length + bis.read(t0000DATA); + bis.read(new byte[4]); // length + + remaining += 32; // total of header data + + // start reading actual node + WSNode node = new WSNode(); + bis.read(value); + node.setId(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getInt()); + bis.read(value); + node.setParentId(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getInt()); + bis.read(value); + node.setObjIndex(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getInt()); + node.setTemplate(getTypes()[node.getObjIndex()]); + bis.read(value); // no idea what this value is supposed to be. + bis.read(value); + node.setObjW(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setObjX(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setObjY(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setObjZ(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setX(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setY(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setZ(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setType(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getFloat()); + bis.read(value); + node.setPOBCRC(ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getInt()); + + // 13 * 4 is 13 values read * 4 bytes each. + remaining += (13 * 4); + + // add the node to our list of nodes. + if(node.getParentId() == 0) { + getNodes().add(node); + } + else{ + tempNode = FindNodeById(node.getParentId()); + if(tempNode == null){ + throw new Exception("Parent Node " + node.getParentId() + " could not be found!"); + } + tempNode.getNodes().add(node); + } + } + } + catch(Exception e){ + System.out.println("EXCEPTION: " + e.getMessage()); + e.printStackTrace(); + } + finally { + bis.close(); + } + } + // IsAlpha identifies if this is the first node or not. If so, we need to offset the read 4 bytes to make sure stuff lines up. + private static boolean IsAlpha(byte[] data) { + for (byte character : data) { + if ((((character <= 0x2f) || (character >= 0x3a)) && ((character <= 0x40) || (character >= 0x5c))) && (character != 0x20)) { + return false; + } + } + return true; + } + private WSNode FindNodeById(int parentId){ + if(getNodes().size() > 0){ + return FindChildren(parentId, getNodes().toArray(new WSNode[getNodes().size()])); + } + return null; + } + private WSNode FindChildren(int id, WSNode[] nodes){ + for (WSNode node : nodes){ + if(node == null) continue; + if(node.getId() == id){ + return node; + } + else{ + if(node.hasChildNodes()){ + WSNode returnVal = FindChildren(id, node.getNodes().toArray(new WSNode[getNodes().size()])); + if(returnVal != null){ + return returnVal; + } + } + } + } + return null; + } + private void saveNodesToFiles(List nodes) throws IOException { + String area = getAreaName(); + String oldDir = "output/" + area; + File rmDir = new File(oldDir); + if(rmDir.exists()){ + System.out.print("Removing files from folder " + area + "... "); + boolean result = false; + for(File f : rmDir.listFiles()){ + result = f.delete(); + if(!result) break; + } + System.out.println((result ? "removed." : "not removed.")); + if(!result) System.exit(1); + System.out.print("Removing folder " + area + "... "); + System.out.println((rmDir.delete() ? "removed." : "not removed.")); + } + boolean newFile = false; + for (WSNode node : nodes) { + int x = (int) Math.floor((double) (node.getX() + 8192) / 2048) + 1; + int y = (int) Math.floor((double) (node.getZ() + 8192) / 2048) + 1; + + String fileName = "output/" + area + "/" + area + "_" + x + "_" + y + "_ws.tab"; + + File outFile = new File(fileName); + if(!outFile.exists()){ + outFile.getParentFile().mkdirs(); + newFile = true; + } + Writer writer; + try { + FileWriter fw = new FileWriter(outFile, true); + if(newFile){ + fw.write("objid\tcontainer\tserver_template_crc\tcell_index\tpx\tpy\tpz\tqw\tqx\tqy\tqz\tscripts\tobjvars\n"); + fw.write("i\ti\th\ti\tf\tf\tf\tf\tf\tf\tf\ts\tp\n"); + } + fw.write(node.serialize(0,true)); + fw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + newFile = false; + } + } + + public List getAllNodes(){ + List tmp = new ArrayList<>(); + for (WSNode node : _nodes) { + tmp.add(node); + if(node.hasChildNodes()){ + tmp.addAll(node.getAllNodes()); + } + } + return tmp; + } +} diff --git a/swg/WSNode.java b/swg/WSNode.java new file mode 100644 index 0000000..f00f9b3 --- /dev/null +++ b/swg/WSNode.java @@ -0,0 +1,228 @@ +package swg; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Cekis on 2/18/2017. + */ +public class WSNode { + private int _id; + private int _parentId; + private int _nodeIndex; + private int _objIndex; + private float _objX; + private float _objY; + private float _objZ; + private float _objW; + private float _objScale; + private float _x; + private float _y; + private float _z; + private float _type; + private int _POBCRC; + private WSNode _parent; + private List _childNodes; + private String template; + + public WSNode(){} + + public WSNode(int id, int parentId, int objIndex, byte ox, byte oy, byte oz, byte ow, byte scale, byte x, byte y, byte z, byte type, int unknown){ + this._id = id; + this._parentId = parentId; + this._objIndex = objIndex; + this._objX = ox; + this._objY = oy; + this._objZ = oz; + this._objW = ow; + this._objScale = scale; + this._type = type; + this._x = x; + this._y = y; + this._z = z; + this._POBCRC = unknown; + } + + public int getId(){ + return _id; + } + public void setId(int id){ + _id = id; + } + + public int getParentId() { + return _parentId; + } + + public void setParentId(int _parentId) { + this._parentId = _parentId; + } + + public int getObjIndex() { + return _objIndex; + } + + public void setObjIndex(int _oIndex) { + this._objIndex = _oIndex; + } + + public float getObjX() { + return _objX; + } + + public void setObjX(float _oX) { + this._objX = _oX; + } + + public float getObjY() { + return _objY; + } + + public void setObjY(float _objY) { + this._objY = _objY; + } + + public float getObjZ() { + return _objZ; + } + + public void setObjZ(float _objZ) { + this._objZ = _objZ; + } + + public float getObjScale() { + return _objScale; + } + + public void setObjScale(float _objScale) { + this._objScale = _objScale; + } + + public float getX() { + return _x; + } + + public void setX(float _x) { + this._x = _x; + } + + public float getY() { + return _y; + } + + public void setY(float _y) { + this._y = _y; + } + + public float getZ() { + return _z; + } + + public void setZ(float _z) { + this._z = _z; + } + + public float getObjW() { + return _objW; + } + + public void setObjW(float _objW) { + this._objW = _objW; + } + + public float getType() { + return _type; + } + + public void setType(float _type) { + this._type = _type; + } + + public int getPOBCRC() { + return _POBCRC; + } + + public void setPOBCRC(int _POBCRC) { + this._POBCRC = _POBCRC; + } + + public WSNode getParent() { + return _parent; + } + + public void setParent(WSNode _parent) { + this._parent = _parent; + } + + public List getNodes() { + if(_childNodes == null){ + _childNodes = new ArrayList<>(); + } + return _childNodes; + } + + public void setNodes(List _nodes) { + this._childNodes = _nodes; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(String template) { + this.template = template; + } + + public int getNodeIndex() { + return _nodeIndex; + } + + public void setNodeIndex(int _nodeIndex) { + this._nodeIndex = _nodeIndex; + } + + public boolean hasChildNodes(){ + return getNodes().size() > 0; + } + + public String serialize(int index, boolean relative){ + String outLine = ""; + outLine += getId() + "\t"; + outLine += getParentId() + "\t"; + outLine += getTemplate() + "\t"; + outLine += index + "\t"; + outLine += (relative ? ((getX() + 8192) % 2048) : getX()) + "\t"; + outLine += getY() + "\t"; + outLine += (relative ? ((getZ() + 8192) % 2048) : getZ()) + "\t"; + outLine += getObjW() + "\t"; + outLine += getObjX() + "\t"; + outLine += getObjY() + "\t"; + outLine += getObjZ() + "\t"; + outLine += "\t"; + outLine += (getPOBCRC() != 0 ? "portalProperty.crc|0|" + getPOBCRC() + "|" : ""); + outLine += "$|\n"; + + if(hasChildNodes()){ + int nSize = _childNodes.size(); + for(int j = 0; j < nSize; j++){ + WSNode node = _childNodes.get(j); + outLine += node.serialize(j + 1, false); + } + } + + return outLine; + } + + public List getAllNodes(){ + List tmp = new ArrayList<>(); + int i = 1; + for (WSNode node : _childNodes) { + node.setNodeIndex(i++); + tmp.add(node); + if(node.hasChildNodes()){ + tmp.addAll(node.getAllNodes()); + } + } + return tmp; + } +}