mirror of
https://github.com/Wack0/IFPSTools.NET.git
synced 2025-06-18 10:45:36 -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;
|
||||
// record(types...)
|
||||
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 nameList = new List<string>();
|
||||
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");
|
||||
typeList.Add(childType);
|
||||
nameList.Add(childName);
|
||||
}
|
||||
ret = new RecordType(typeList);
|
||||
ret = new RecordType(typeList, nameList);
|
||||
break;
|
||||
// set(bitsize)
|
||||
case Constants.TYPE_SET:
|
||||
@ -536,6 +541,8 @@ namespace IFPSAsmLib
|
||||
return LocalVariable.Create(idx - 1);
|
||||
}
|
||||
|
||||
private static readonly char[] SPLIT_DOT = new char[] { '.' };
|
||||
|
||||
private static Operand ParseOperandValue(
|
||||
ParserElement value,
|
||||
ScriptFunction function,
|
||||
@ -554,6 +561,7 @@ namespace IFPSAsmLib
|
||||
// variable: name
|
||||
// immediate index: name[int_elem]
|
||||
// variable index: name[elem_var]
|
||||
// immediate index for record: name[elem_name]
|
||||
if (value.NextChild != null)
|
||||
{
|
||||
// immediate
|
||||
@ -573,11 +581,32 @@ namespace IFPSAsmLib
|
||||
return new Operand(variable);
|
||||
}
|
||||
|
||||
// indexed variable
|
||||
var baseName = val.Substring(0, indexOfStart);
|
||||
string element = val.Substring(indexOfStart + 1, val.Length - indexOfStart - 2);
|
||||
var baseVar = ParseVariable(baseName, function, globals, aliases);
|
||||
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);
|
||||
// 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);
|
||||
if (elemVar == null) value.ThrowInvalid(string.Format("In function {0}: Used nonexistant variable", function.Name));
|
||||
return new Operand(baseVar, elemVar);
|
||||
@ -627,7 +656,7 @@ namespace IFPSAsmLib
|
||||
if (next == null) insn.ThrowInvalid();
|
||||
next.ExpectValidName();
|
||||
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();
|
||||
return Instruction.Create(opcode, funcOp);
|
||||
case OperandType.InlineType:
|
||||
@ -636,7 +665,7 @@ namespace IFPSAsmLib
|
||||
if (next == null) insn.ThrowInvalid();
|
||||
next.ExpectValidName();
|
||||
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();
|
||||
return Instruction.Create(opcode, typeOp);
|
||||
}
|
||||
@ -666,7 +695,7 @@ namespace IFPSAsmLib
|
||||
next = next.Next;
|
||||
next.ExpectValidName();
|
||||
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();
|
||||
return Instruction.Create(opcode, op0, op1, typeOp);
|
||||
}
|
||||
@ -676,7 +705,7 @@ namespace IFPSAsmLib
|
||||
next = next.Next;
|
||||
next.ExpectValidName();
|
||||
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.ExpectValidName();
|
||||
var variable = ParseVariable(next.Value, function, globals, aliases);
|
||||
@ -884,6 +913,20 @@ namespace IFPSAsmLib
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ namespace IFPSAsmLib
|
||||
internal const char BINARY_FALSE = '0';
|
||||
internal const char BINARY_TRUE = '1';
|
||||
|
||||
internal const string ELEMENT_OBFUSCATE = ".obfuscate";
|
||||
internal const string ELEMENT_VERSION = ".version";
|
||||
internal const string ELEMENT_ENTRYPOINT = ".entry";
|
||||
internal const string ELEMENT_TYPE = ".type";
|
||||
|
@ -386,6 +386,7 @@ namespace IFPSAsmLib
|
||||
public enum ElementParentType : byte
|
||||
{
|
||||
Unknown,
|
||||
Obfuscate,
|
||||
FileVersion,
|
||||
EntryPoint,
|
||||
Attribute,
|
||||
@ -526,12 +527,12 @@ namespace IFPSAsmLib
|
||||
bool baseType
|
||||
)
|
||||
{
|
||||
ParserElement currentChild = null, nextChild = null;
|
||||
ParserElement currentChild, nextChild;
|
||||
str.AdvanceWhiteSpace(ref location, true);
|
||||
var typeNext = str.GetEntity(ref location, ParserSeparatorType.StartBody, false);
|
||||
typeNext.ExpectValidName();
|
||||
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);
|
||||
typeNext = str.GetEntity(ref location, ParserSeparatorType.NextOrEndBody, false);
|
||||
@ -580,6 +581,13 @@ namespace IFPSAsmLib
|
||||
var typeString = type.ToString();
|
||||
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:
|
||||
// .version int
|
||||
if (type.FoundSeparator) str.EnsureBodyEmpty(ref location);
|
||||
|
@ -9,7 +9,8 @@ namespace IFPSLib.Types
|
||||
/// Defines a record.
|
||||
/// A record in PascalScript (and Pascal) is equivalent to a struct in C-like languages.
|
||||
/// 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>
|
||||
public class RecordType : TypeBase
|
||||
{
|
||||
@ -20,11 +21,22 @@ namespace IFPSLib.Types
|
||||
/// </summary>
|
||||
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() { }
|
||||
|
||||
public RecordType(IList<IType> 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)
|
||||
@ -48,13 +60,26 @@ namespace IFPSLib.Types
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string[] INVALID_CHARS_FOR_NAME = { " ", ",", "(", ")" };
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder(base.ToString() + "record(");
|
||||
var nameCount = ElementNames == null ? 0 : ElementNames.Count;
|
||||
for (int i = 0; i < Elements.Count; i++)
|
||||
{
|
||||
sb.Append(i == 0 ? "" : ",");
|
||||
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);
|
||||
return sb.ToString();
|
||||
|
Loading…
Reference in New Issue
Block a user