mirror of
https://github.com/Wack0/IFPSTools.NET.git
synced 2025-06-19 03:05:40 -04:00
lib: allow record element names to be specified
asm: allow record element names to be specified asm: fix some errors being thrown by the wrong parser element asm: add light obfuscation, when enabled all exported functions will be renamed to "A"
This commit is contained in:
parent
f99cb21c5d
commit
b85cd7770a
@ -225,14 +225,19 @@ namespace IFPSAsmLib
|
|||||||
break;
|
break;
|
||||||
// record(types...)
|
// record(types...)
|
||||||
case Constants.TYPE_RECORD:
|
case Constants.TYPE_RECORD:
|
||||||
if (next != null) next.ThrowInvalid();
|
// next is optional record element name, there can be no more elements after that
|
||||||
|
if (next != null && (next.Next != null || next.NextChild != null)) next.ThrowInvalid();
|
||||||
var typeList = new List<IType>();
|
var typeList = new List<IType>();
|
||||||
|
var nameList = new List<string>();
|
||||||
for (next = child; next != null; next = next.NextChild)
|
for (next = child; next != null; next = next.NextChild)
|
||||||
{
|
{
|
||||||
|
var childName = string.Empty;
|
||||||
|
if (next.Next != null) childName = next.Next.Value;
|
||||||
if (!types.TryGetValue(next.Value, out childType)) next.ThrowInvalid("Invalid record element type");
|
if (!types.TryGetValue(next.Value, out childType)) next.ThrowInvalid("Invalid record element type");
|
||||||
typeList.Add(childType);
|
typeList.Add(childType);
|
||||||
|
nameList.Add(childName);
|
||||||
}
|
}
|
||||||
ret = new RecordType(typeList);
|
ret = new RecordType(typeList, nameList);
|
||||||
break;
|
break;
|
||||||
// set(bitsize)
|
// set(bitsize)
|
||||||
case Constants.TYPE_SET:
|
case Constants.TYPE_SET:
|
||||||
@ -536,6 +541,8 @@ namespace IFPSAsmLib
|
|||||||
return LocalVariable.Create(idx - 1);
|
return LocalVariable.Create(idx - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly char[] SPLIT_DOT = new char[] { '.' };
|
||||||
|
|
||||||
private static Operand ParseOperandValue(
|
private static Operand ParseOperandValue(
|
||||||
ParserElement value,
|
ParserElement value,
|
||||||
ScriptFunction function,
|
ScriptFunction function,
|
||||||
@ -554,6 +561,7 @@ namespace IFPSAsmLib
|
|||||||
// variable: name
|
// variable: name
|
||||||
// immediate index: name[int_elem]
|
// immediate index: name[int_elem]
|
||||||
// variable index: name[elem_var]
|
// variable index: name[elem_var]
|
||||||
|
// immediate index for record: name[elem_name]
|
||||||
if (value.NextChild != null)
|
if (value.NextChild != null)
|
||||||
{
|
{
|
||||||
// immediate
|
// immediate
|
||||||
@ -573,11 +581,32 @@ namespace IFPSAsmLib
|
|||||||
return new Operand(variable);
|
return new Operand(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// indexed variable
|
||||||
var baseName = val.Substring(0, indexOfStart);
|
var baseName = val.Substring(0, indexOfStart);
|
||||||
string element = val.Substring(indexOfStart + 1, val.Length - indexOfStart - 2);
|
string element = val.Substring(indexOfStart + 1, val.Length - indexOfStart - 2);
|
||||||
var baseVar = ParseVariable(baseName, function, globals, aliases);
|
var baseVar = ParseVariable(baseName, function, globals, aliases);
|
||||||
if (baseVar == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name));
|
if (baseVar == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name));
|
||||||
|
// check for immediate index
|
||||||
if (uint.TryParse(element, out var elementIdx)) return new Operand(baseVar, elementIdx);
|
if (uint.TryParse(element, out var elementIdx)) return new Operand(baseVar, elementIdx);
|
||||||
|
// not immediate index. if the base is a record, then check the element names
|
||||||
|
var baseRecord = baseVar.Type as RecordType;
|
||||||
|
if (baseRecord != null && !string.IsNullOrEmpty(element))
|
||||||
|
{
|
||||||
|
elementIdx = (uint)baseRecord.ElementNames.IndexOf(element);
|
||||||
|
if ((int)elementIdx != -1) return new Operand(baseVar, elementIdx);
|
||||||
|
}
|
||||||
|
// might be type.element
|
||||||
|
var elemSplit = element.Split(SPLIT_DOT, 2);
|
||||||
|
if (elemSplit.Length == 2 && types.TryGetValue(elemSplit[0], out var wantedType))
|
||||||
|
{
|
||||||
|
baseRecord = wantedType as RecordType;
|
||||||
|
if (baseRecord != null)
|
||||||
|
{
|
||||||
|
elementIdx = (uint)baseRecord.ElementNames.IndexOf(elemSplit[1]);
|
||||||
|
if ((int)elementIdx != -1) return new Operand(baseVar, elementIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// must be a variable
|
||||||
var elemVar = ParseVariable(element, function, globals, aliases);
|
var elemVar = ParseVariable(element, function, globals, aliases);
|
||||||
if (elemVar == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name));
|
if (elemVar == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name));
|
||||||
return new Operand(baseVar, elemVar);
|
return new Operand(baseVar, elemVar);
|
||||||
@ -627,7 +656,7 @@ namespace IFPSAsmLib
|
|||||||
if (next == null) insn.ThrowInvalid();
|
if (next == null) insn.ThrowInvalid();
|
||||||
next.ExpectValidName();
|
next.ExpectValidName();
|
||||||
next.EnsureNoNextChild();
|
next.EnsureNoNextChild();
|
||||||
if (!functions.TryGetValue(next.Value, out var funcOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown function", function.Name));
|
if (!functions.TryGetValue(next.Value, out var funcOp)) next.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown function", function.Name));
|
||||||
if (next.Next != null) next.Next.ThrowInvalid();
|
if (next.Next != null) next.Next.ThrowInvalid();
|
||||||
return Instruction.Create(opcode, funcOp);
|
return Instruction.Create(opcode, funcOp);
|
||||||
case OperandType.InlineType:
|
case OperandType.InlineType:
|
||||||
@ -636,7 +665,7 @@ namespace IFPSAsmLib
|
|||||||
if (next == null) insn.ThrowInvalid();
|
if (next == null) insn.ThrowInvalid();
|
||||||
next.ExpectValidName();
|
next.ExpectValidName();
|
||||||
next.EnsureNoNextChild();
|
next.EnsureNoNextChild();
|
||||||
if (!types.TryGetValue(next.Value, out var typeOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name));
|
if (!types.TryGetValue(next.Value, out var typeOp)) next.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name));
|
||||||
if (next.Next != null) next.Next.ThrowInvalid();
|
if (next.Next != null) next.Next.ThrowInvalid();
|
||||||
return Instruction.Create(opcode, typeOp);
|
return Instruction.Create(opcode, typeOp);
|
||||||
}
|
}
|
||||||
@ -666,7 +695,7 @@ namespace IFPSAsmLib
|
|||||||
next = next.Next;
|
next = next.Next;
|
||||||
next.ExpectValidName();
|
next.ExpectValidName();
|
||||||
next.EnsureNoNextChild();
|
next.EnsureNoNextChild();
|
||||||
if (!types.TryGetValue(next.Value, out var typeOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name));
|
if (!types.TryGetValue(next.Value, out var typeOp)) next.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name));
|
||||||
if (next.Next != null) next.Next.ThrowInvalid();
|
if (next.Next != null) next.Next.ThrowInvalid();
|
||||||
return Instruction.Create(opcode, op0, op1, typeOp);
|
return Instruction.Create(opcode, op0, op1, typeOp);
|
||||||
}
|
}
|
||||||
@ -676,7 +705,7 @@ namespace IFPSAsmLib
|
|||||||
next = next.Next;
|
next = next.Next;
|
||||||
next.ExpectValidName();
|
next.ExpectValidName();
|
||||||
next.EnsureNoNextChild();
|
next.EnsureNoNextChild();
|
||||||
if (!types.TryGetValue(next.Value, out var typeOp)) insn.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name));
|
if (!types.TryGetValue(next.Value, out var typeOp)) next.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name));
|
||||||
next = next.Next;
|
next = next.Next;
|
||||||
next.ExpectValidName();
|
next.ExpectValidName();
|
||||||
var variable = ParseVariable(next.Value, function, globals, aliases);
|
var variable = ParseVariable(next.Value, function, globals, aliases);
|
||||||
@ -884,6 +913,20 @@ namespace IFPSAsmLib
|
|||||||
|
|
||||||
if (entryPoint != null) ret.EntryPoint = functionsTable[entryPoint];
|
if (entryPoint != null) ret.EntryPoint = functionsTable[entryPoint];
|
||||||
|
|
||||||
|
// if obfuscation is required then rename all exported functions to "A".
|
||||||
|
if (parsed.OfType(ElementParentType.Obfuscate).Any())
|
||||||
|
{
|
||||||
|
foreach (var func in ret.Functions.OfType<ScriptFunction>())
|
||||||
|
{
|
||||||
|
if (func.Exported)
|
||||||
|
{
|
||||||
|
// assumption: script executor only care about InitializeUninstall
|
||||||
|
if (func.Name.ToLower() == "initializeuninstall") continue;
|
||||||
|
func.Name = "A";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ namespace IFPSAsmLib
|
|||||||
internal const char BINARY_FALSE = '0';
|
internal const char BINARY_FALSE = '0';
|
||||||
internal const char BINARY_TRUE = '1';
|
internal const char BINARY_TRUE = '1';
|
||||||
|
|
||||||
|
internal const string ELEMENT_OBFUSCATE = ".obfuscate";
|
||||||
internal const string ELEMENT_VERSION = ".version";
|
internal const string ELEMENT_VERSION = ".version";
|
||||||
internal const string ELEMENT_ENTRYPOINT = ".entry";
|
internal const string ELEMENT_ENTRYPOINT = ".entry";
|
||||||
internal const string ELEMENT_TYPE = ".type";
|
internal const string ELEMENT_TYPE = ".type";
|
||||||
|
@ -386,6 +386,7 @@ namespace IFPSAsmLib
|
|||||||
public enum ElementParentType : byte
|
public enum ElementParentType : byte
|
||||||
{
|
{
|
||||||
Unknown,
|
Unknown,
|
||||||
|
Obfuscate,
|
||||||
FileVersion,
|
FileVersion,
|
||||||
EntryPoint,
|
EntryPoint,
|
||||||
Attribute,
|
Attribute,
|
||||||
@ -526,12 +527,12 @@ namespace IFPSAsmLib
|
|||||||
bool baseType
|
bool baseType
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ParserElement currentChild = null, nextChild = null;
|
ParserElement currentChild, nextChild;
|
||||||
str.AdvanceWhiteSpace(ref location, true);
|
str.AdvanceWhiteSpace(ref location, true);
|
||||||
var typeNext = str.GetEntity(ref location, ParserSeparatorType.StartBody, false);
|
var typeNext = str.GetEntity(ref location, ParserSeparatorType.StartBody, false);
|
||||||
typeNext.ExpectValidName();
|
typeNext.ExpectValidName();
|
||||||
current.Next = next = new ParserElement(typeNext, parentType);
|
current.Next = next = new ParserElement(typeNext, parentType);
|
||||||
if (baseType && parentType == ElementParentType.Type && next.Value == "funcptr")
|
if (baseType && parentType == ElementParentType.Type && next.Value == Constants.TYPE_FUNCTION_POINTER)
|
||||||
{
|
{
|
||||||
ParseTokenWithUnknownBody(str, ref location, parentType, next, ref next, false);
|
ParseTokenWithUnknownBody(str, ref location, parentType, next, ref next, false);
|
||||||
typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false);
|
typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false);
|
||||||
@ -580,6 +581,13 @@ namespace IFPSAsmLib
|
|||||||
var typeString = type.ToString();
|
var typeString = type.ToString();
|
||||||
switch (typeString)
|
switch (typeString)
|
||||||
{
|
{
|
||||||
|
case Constants.ELEMENT_OBFUSCATE:
|
||||||
|
// .obfuscate
|
||||||
|
if (type.FoundSeparator) str.EnsureBodyEmpty(ref location);
|
||||||
|
parentType = ElementParentType.Obfuscate;
|
||||||
|
current = new ParserElement(type, parentType);
|
||||||
|
if (!str.AdvanceWhiteSpaceUntilNewLine(ref location)) current.Next.ThrowInvalid();
|
||||||
|
return current;
|
||||||
case Constants.ELEMENT_VERSION:
|
case Constants.ELEMENT_VERSION:
|
||||||
// .version int
|
// .version int
|
||||||
if (type.FoundSeparator) str.EnsureBodyEmpty(ref location);
|
if (type.FoundSeparator) str.EnsureBodyEmpty(ref location);
|
||||||
|
@ -9,7 +9,8 @@ namespace IFPSLib.Types
|
|||||||
/// Defines a record.
|
/// Defines a record.
|
||||||
/// A record in PascalScript (and Pascal) is equivalent to a struct in C-like languages.
|
/// A record in PascalScript (and Pascal) is equivalent to a struct in C-like languages.
|
||||||
/// In PascalScript, it's usually used for FFI.
|
/// In PascalScript, it's usually used for FFI.
|
||||||
/// Elements are typed but have no name; in bytecode they are referenced by index using array-index operands.
|
/// Elements are typed but have no name internally; in bytecode they are referenced by index using array-index operands.
|
||||||
|
/// For quality of life purposes, we allow names to be specified for elements, but they will not be retained when saving out bytecode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RecordType : TypeBase
|
public class RecordType : TypeBase
|
||||||
{
|
{
|
||||||
@ -20,11 +21,22 @@ namespace IFPSLib.Types
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<IType> Elements { get; internal set; } = new List<IType>();
|
public IList<IType> Elements { get; internal set; } = new List<IType>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Names of the record's elements.
|
||||||
|
/// </summary>
|
||||||
|
public IList<string> ElementNames { get; internal set; } = null;
|
||||||
|
|
||||||
private RecordType() { }
|
private RecordType() { }
|
||||||
|
|
||||||
public RecordType(IList<IType> elements)
|
public RecordType(IList<IType> elements)
|
||||||
{
|
{
|
||||||
Elements = elements;
|
Elements = elements;
|
||||||
|
ElementNames = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordType(IList<IType> elements, IList<string> elementNames) : this(elements)
|
||||||
|
{
|
||||||
|
ElementNames = elementNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static new RecordType Load(BinaryReader br, Script script)
|
internal static new RecordType Load(BinaryReader br, Script script)
|
||||||
@ -48,13 +60,26 @@ namespace IFPSLib.Types
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly string[] INVALID_CHARS_FOR_NAME = { " ", ",", "(", ")" };
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder(base.ToString() + "record(");
|
var sb = new StringBuilder(base.ToString() + "record(");
|
||||||
|
var nameCount = ElementNames == null ? 0 : ElementNames.Count;
|
||||||
for (int i = 0; i < Elements.Count; i++)
|
for (int i = 0; i < Elements.Count; i++)
|
||||||
{
|
{
|
||||||
sb.Append(i == 0 ? "" : ",");
|
sb.Append(i == 0 ? "" : ",");
|
||||||
sb.Append(Elements[i].Name);
|
sb.Append(Elements[i].Name);
|
||||||
|
if (i < nameCount)
|
||||||
|
{
|
||||||
|
var name = new StringBuilder(ElementNames[i]);
|
||||||
|
foreach (var chr in INVALID_CHARS_FOR_NAME) name = name.Replace(chr, string.Empty);
|
||||||
|
if (name.Length != 0)
|
||||||
|
{
|
||||||
|
sb.Append(' ');
|
||||||
|
sb.Append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sb.AppendFormat(") {0}", Name);
|
sb.AppendFormat(") {0}", Name);
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
|
Loading…
Reference in New Issue
Block a user