Add mis parser

This commit is contained in:
RandomityGuy 2021-06-12 22:06:10 +05:30
parent 4f9a0066f0
commit bfae8b91d8
5 changed files with 384 additions and 9 deletions

View file

@ -1,5 +1,7 @@
package;
import mis.MisParser;
import sys.io.File;
import shapes.EndPad;
import shapes.LandMine;
import shapes.StartPad;
@ -57,6 +59,10 @@ class Main extends hxd.App {
tform.setPosition(new Vector(0, 0, 0));
db.setTransform(tform);
var ltr = File.getContent("data/missions/beginner/finale.mis");
var mfp = new MisParser(ltr);
var mis = mfp.parse();
// var pi = new PathedInterior();
// DifBuilder.loadDif("data/interiors/addon/smallplatform.dif", loader, pi);
// var pim = pi.getTransform();

View file

@ -139,4 +139,21 @@ class Util {
}
return -1;
}
public static function indexIsInStringLiteral(str:String, index:Int, strLiteralToken = '"') {
var inString = false;
for (i in 0...str.length) {
var c = str.charAt(i);
if (inString) {
if (i == index)
return true;
if (c == strLiteralToken && str.charAt(i - 1) != '\\')
inString = false;
continue;
}
if (c == strLiteralToken)
inString = true;
}
return false;
}
}

View file

@ -1,3 +1,15 @@
package mis;
class MisFile {}
import mis.MissionElement.MissionElementSimGroup;
@:publicFields
class MisFile {
var root:MissionElementSimGroup;
/** The custom marble attributes overrides specified in the file. */
var marbleAttributes:Map<String, String>;
var activatedPackages:Array<String>;
public function new() {}
}

View file

@ -1,5 +1,22 @@
package mis;
import haxe.Exception;
import mis.MissionElement.MissionElementPathedInterior;
import mis.MissionElement.MissionElementPath;
import mis.MissionElement.MissionElementSimGroup;
import mis.MissionElement.MissionElementBase;
import mis.MissionElement.MissionElementParticleEmitterNode;
import mis.MissionElement.MissionElementTSStatic;
import mis.MissionElement.MissionElementMessageVector;
import mis.MissionElement.MissionElementAudioProfile;
import mis.MissionElement.MissionElementTrigger;
import mis.MissionElement.MissionElementMarker;
import mis.MissionElement.MissionElementItem;
import mis.MissionElement.MissionElementStaticShape;
import mis.MissionElement.MissionElementInteriorInstance;
import mis.MissionElement.MissionElementSun;
import mis.MissionElement.MissionElementSky;
import mis.MissionElement.MissionElementMissionArea;
import mis.MissionElement.MissionElementType;
import mis.MissionElement.MissionElementScriptObject;
import src.Util;
@ -23,7 +40,174 @@ class MisParser {
this.text = text;
}
public function parse() {}
public function parse() {
var objectWriteBeginIndex = this.text.indexOf("//--- OBJECT WRITE BEGIN ---");
var objectWriteEndIndex = this.text.lastIndexOf("//--- OBJECT WRITE END ---");
var outsideText = this.text.substring(0, objectWriteBeginIndex) + this.text.substring(objectWriteEndIndex);
// Find all specified variables
this.variables = ["$usermods" => '""']; // Just make $usermods point to nothing
var startText = outsideText;
while (assignmentRegEx.match(startText)) {
if (!this.variables.exists(assignmentRegEx.matched(1)))
this.variables.set(assignmentRegEx.matched(1), assignmentRegEx.matched(2));
startText = assignmentRegEx.matchedRight();
}
var marbleAttributes = new Map();
startText = outsideText;
while (marbleAttributesRegEx.match(startText)) {
marbleAttributes.set(marbleAttributesRegEx.matched(1), this.resolveExpression(marbleAttributesRegEx.matched(2)));
startText = marbleAttributesRegEx.matchedRight();
}
var activatedPackages = [];
startText = outsideText;
while (activatePackageRegEx.match(startText)) {
activatedPackages.push(this.resolveExpression(activatePackageRegEx.matched(1)));
startText = marbleAttributesRegEx.matchedRight();
}
if (objectWriteBeginIndex != -1 && objectWriteEndIndex != -1) {
this.text = this.text.substring(objectWriteBeginIndex, objectWriteEndIndex);
}
var currentIndex = 0;
while (true) {
// blockCommentRegEx.lastIndex = currentIndex;
// lineCommentRegEx.lastIndex = currentIndex;
var blockMatch = blockCommentRegEx.matchSub(this.text, currentIndex);
var lineMatch = lineCommentRegEx.matchSub(this.text, currentIndex);
// The detected "comment" might be inside a string literal, in which case we ignore it 'cause it ain't no comment.
if (blockMatch && Util.indexIsInStringLiteral(this.text, blockCommentRegEx.matchedPos().pos))
blockMatch = false;
if (lineMatch && Util.indexIsInStringLiteral(this.text, lineCommentRegEx.matchedPos().pos))
lineMatch = false;
if (!blockMatch && !lineMatch)
break;
else if (!lineMatch || (blockMatch && lineMatch && blockCommentRegEx.matchedPos().pos < lineCommentRegEx.matchedPos().pos)) {
this.text = this.text.substring(0, blockCommentRegEx.matchedPos().pos)
+ this.text.substring(blockCommentRegEx.matchedPos().pos + blockCommentRegEx.matchedPos().len);
currentIndex += blockCommentRegEx.matchedPos().pos;
} else {
this.text = this.text.substring(0, lineCommentRegEx.matchedPos().pos)
+ this.text.substring(lineCommentRegEx.matchedPos().pos + lineCommentRegEx.matchedPos().len);
currentIndex += lineCommentRegEx.matchedPos().pos;
}
}
var elements = [];
while (this.hasNextElement()) {
var element = this.readElement();
if (element == null)
continue;
elements.push(element);
}
if (elements.length != 1) {
// We expect there to be only one outer element; the MissionGroup SimGroup.
trace(elements);
throw new Exception("Mission file doesn't have exactly 1 outer element!");
}
var mf = new MisFile();
mf.root = cast elements[0];
mf.marbleAttributes = marbleAttributes;
mf.activatedPackages = activatedPackages;
return mf;
}
function readElement() {
// Get information about the head
// elementHeadRegEx.lastIndex = this.index;
var head = elementHeadRegEx.match(this.text.substring(this.index));
this.index += elementHeadRegEx.matchedPos().pos + elementHeadRegEx.matchedPos().len;
var type = elementHeadRegEx.matched(1);
var name = elementHeadRegEx.matched(2);
var element:MissionElementBase = null;
switch (type) {
case "SimGroup":
element = this.readSimGroup(name);
case "ScriptObject":
element = this.readScriptObject(name);
case "MissionArea":
element = this.readMissionArea(name);
case "Sky":
element = this.readSky(name);
case "Sun":
element = this.readSun(name);
case "InteriorInstance":
element = this.readInteriorInstance(name);
case "StaticShape":
element = this.readStaticShape(name);
case "Item":
element = this.readItem(name);
case "Path":
element = this.readPath(name);
case "Marker":
element = this.readMarker(name);
case "PathedInterior":
element = this.readPathedInterior(name);
case "Trigger":
element = this.readTrigger(name);
case "AudioProfile":
element = this.readAudioProfile(name);
case "MessageVector":
element = this.readMessageVector(name);
case "TSStatic":
element = this.readTSStatic(name);
case "ParticleEmitterNode":
element = this.readParticleEmitterNode(name);
default:
trace("Unknown element type! " + type);
// Still advance the index
var endingBraceIndex = Util.indexOfIgnoreStringLiterals(this.text, '};', this.index);
if (endingBraceIndex == -1)
endingBraceIndex = this.text.length;
this.index = endingBraceIndex + 2;
}
if (element != null)
element._id = this.currentElementId++;
return element;
}
function hasNextElement() {
if (!elementHeadRegEx.match(this.text.substring(this.index)))
return false;
if (Util.indexOfIgnoreStringLiterals(this.text.substring(this.index, this.index + elementHeadRegEx.matchedPos().pos), '}') != -1)
return false;
return true;
}
function readSimGroup(name:String) {
var elements = [];
// Read in all elements
while (this.hasNextElement()) {
var element = this.readElement();
if (element == null)
continue;
elements.push(element);
}
var endingBraceIndex = Util.indexOfIgnoreStringLiterals(this.text, '};', this.index);
if (endingBraceIndex == -1)
endingBraceIndex = this.text.length;
this.index = endingBraceIndex + 2;
var sg = new MissionElementSimGroup();
sg._name = name;
sg._type = MissionElementType.SimGroup;
sg.elements = elements;
return sg;
}
function readValues() {
// Values are either strings or string arrays.
@ -65,21 +249,175 @@ class MisParser {
return obj;
}
function readScriptObject(name:String) {
var obj = new MissionElementScriptObject();
obj._type = MissionElementType.ScriptObject;
obj._name = name;
function copyFields(obj:Dynamic) {
var values = this.readValues();
var objfields = Type.getInstanceFields(Type.getClass(obj));
for (key => value in values) {
if (value.length > 1) {
for (i in 0...value.length) {
Reflect.setField(obj, '${key}${i}', value[i]);
var fname = '${key}${i}';
if (objfields.contains(fname))
Reflect.setField(obj, fname, value[i]);
}
} else {
Reflect.setField(obj, key, value[0]);
if (key == "static")
key = "isStatic";
if (objfields.contains(key))
Reflect.setField(obj, key, value[0]);
}
}
}
function readScriptObject(name:String) {
var obj = new MissionElementScriptObject();
obj._type = MissionElementType.ScriptObject;
obj._name = name;
copyFields(obj);
return obj;
}
function readMissionArea(name:String) {
var obj = new MissionElementMissionArea();
obj._type = MissionElementType.MissionArea;
obj._name = name;
copyFields(obj);
return obj;
}
function readSky(name:String) {
var obj = new MissionElementSky();
obj._type = MissionElementType.Sky;
obj._name = name;
copyFields(obj);
return obj;
}
function readSun(name:String) {
var obj = new MissionElementSun();
obj._type = MissionElementType.Sun;
obj._name = name;
copyFields(obj);
return obj;
}
function readInteriorInstance(name:String) {
var obj = new MissionElementInteriorInstance();
obj._type = MissionElementType.InteriorInstance;
obj._name = name;
copyFields(obj);
return obj;
}
function readStaticShape(name:String) {
var obj = new MissionElementStaticShape();
obj._type = MissionElementType.StaticShape;
obj._name = name;
copyFields(obj);
return obj;
}
function readItem(name:String) {
var obj = new MissionElementItem();
obj._type = MissionElementType.Item;
obj._name = name;
copyFields(obj);
return obj;
}
function readPath(name:String) {
var sg:MissionElementSimGroup = cast this.readSimGroup(name);
var obj = new MissionElementPath();
obj._type = MissionElementType.Path;
obj._name = name;
obj.markers = sg.elements.map(x -> cast x);
obj.markers.sort((a, b) -> cast MisParser.parseNumber(a.seqnum) - MisParser.parseNumber(b.seqnum));
copyFields(obj);
return obj;
}
function readMarker(name:String) {
var obj = new MissionElementMarker();
obj._type = MissionElementType.Marker;
obj._name = name;
copyFields(obj);
return obj;
}
function readPathedInterior(name:String) {
var obj = new MissionElementPathedInterior();
obj._type = MissionElementType.PathedInterior;
obj._name = name;
copyFields(obj);
return obj;
}
function readTrigger(name:String) {
var obj = new MissionElementTrigger();
obj._type = MissionElementType.Trigger;
obj._name = name;
copyFields(obj);
return obj;
}
function readAudioProfile(name:String) {
var obj = new MissionElementAudioProfile();
obj._type = MissionElementType.AudioProfile;
obj._name = name;
copyFields(obj);
return obj;
}
function readMessageVector(name:String) {
var obj = new MissionElementMessageVector();
obj._type = MissionElementType.MessageVector;
obj._name = name;
copyFields(obj);
return obj;
}
function readTSStatic(name:String) {
var obj = new MissionElementTSStatic();
obj._type = MissionElementType.TSStatic;
obj._name = name;
copyFields(obj);
return obj;
}
function readParticleEmitterNode(name:String) {
var obj = new MissionElementParticleEmitterNode();
obj._type = MissionElementType.ParticleEmitterNode;
obj._name = name;
copyFields(obj);
return obj;
}

View file

@ -75,7 +75,9 @@ class MissionElementSky extends MissionElementBase {
var position:String;
var rotation:String;
var scale:String;
var cloudheightper:Array<String>;
var cloudheightper0:String;
var cloudheightper1:String;
var cloudheightper2:String;
var cloudspeed1:String;
var cloudspeed2:String;
var cloudspeed3:String;