// Copyright 2021 faddenSoft. All Rights Reserved.
// See the LICENSE.txt file for distribution terms (Apache 2.0).
using System;
using System.Collections.Generic;
using PluginCommon;
namespace ExtensionScriptSample {
///
/// Formats three different kinds of inline functions:
///
/// PrintInlineL1String: format following data as L1 string
/// PrintInlineNullString*: format following data as null-term string.
/// PrintInlineAddrString*: format following data as 16-bit pointer to null-term string.
///
public class InlineMultiData : MarshalByRefObject, IPlugin, IPlugin_SymbolList,
IPlugin_InlineJsr {
private IApplication mAppRef;
private byte[] mFileData;
private AddressTranslate mAddrTrans;
private const string L1STR_NAME = "PrintInlineL1String";
private const string NULLSTR_PREFIX = "PrintInlineNullString";
private const string ADDRSTR_PREFIX = "PrintInlineAddrString";
private enum InlineKind { Unknown = 0, L1Str, NullStr, AddrStr };
private Dictionary mInlineLabels = new Dictionary();
public string Identifier {
get {
return "Inline multi-data formatter";
}
}
public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
mAppRef = appRef;
mFileData = fileData;
mAddrTrans = addrTrans;
mAppRef.DebugLog("InlineMultiData(id=" +
AppDomain.CurrentDomain.Id + "): prepare()");
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
mAddrTrans = null;
}
public void UpdateSymbolList(List plSyms) {
mInlineLabels.Clear();
// Find matching symbols. Save the symbol's value (its address) and the type.
// We want an exact match on L1STR_NAME, and prefix matches on the other two.
foreach (PlSymbol sym in plSyms) {
if (sym.Label.Equals(L1STR_NAME)) {
mInlineLabels.Add(sym.Value, InlineKind.L1Str);
} else if (sym.Label.StartsWith(NULLSTR_PREFIX)) {
mInlineLabels.Add(sym.Value, InlineKind.NullStr);
} else if (sym.Label.StartsWith(ADDRSTR_PREFIX)) {
mInlineLabels.Add(sym.Value, InlineKind.AddrStr);
}
}
mAppRef.DebugLog("Found matches for " + mInlineLabels.Count + " labels");
}
public bool IsLabelSignificant(string beforeLabel, string afterLabel) {
return DoesLabelMatch(beforeLabel) || DoesLabelMatch(afterLabel);
}
private static bool DoesLabelMatch(string label) {
return (label.Equals(L1STR_NAME) ||
label.StartsWith(NULLSTR_PREFIX) ||
label.StartsWith(ADDRSTR_PREFIX));
}
public void CheckJsr(int offset, int operand, out bool noContinue) {
noContinue = false;
InlineKind kind;
if (!mInlineLabels.TryGetValue(operand, out kind)) {
return;
}
switch (kind) {
case InlineKind.L1Str:
// Length-delimited ASCII string
FormatL1String(offset + 3);
break;
case InlineKind.NullStr:
// Null-terminated ASCII string.
FormatNullTermString(offset + 3);
break;
case InlineKind.AddrStr:
// Pointer to data. Format as address. Start by confirming next two
// bytes are inside the file bounds.
if (!Util.IsInBounds(mFileData, offset + 3, 2)) {
return;
}
// Format 16-bit value as an address.
mAppRef.SetInlineDataFormat(offset + 3, 2,
DataType.NumericLE, DataSubType.Address, null);
// Now format the string that the address points to. Extract the
// address from the operand.
int strAddr = Util.GetWord(mFileData, offset + 3, 2, false);
// Convert the address to a file offset. Returns -1 if not in file bounds.
int strOff = mAddrTrans.AddressToOffset(offset, strAddr);
// Format it if it's in bounds.
FormatNullTermString(strOff);
break;
}
}
private void FormatL1String(int offset) {
if (offset < 0 || offset >= mFileData.Length) {
return; // length byte is not inside file
}
int len = mFileData[offset];
if (offset + 1 + len > mFileData.Length) {
mAppRef.DebugLog("L1 string ran off end of file at +" +
(offset + 1).ToString("x6"));
return;
}
// Assuming ASCII.
mAppRef.SetInlineDataFormat(offset, len + 1,
DataType.StringL8, DataSubType.Ascii, null);
}
private void FormatNullTermString(int offset) {
if (offset < 0 || offset >= mFileData.Length) {
return; // start is not inside file
}
// search for the terminating null byte
int nullOff = offset;
while (nullOff < mFileData.Length) {
if (mFileData[nullOff] == 0x00) {
break;
}
nullOff++;
}
if (nullOff == mFileData.Length) {
mAppRef.DebugLog("Unable to find end of null-terminated string at +" +
offset.ToString("x6"));
return;
}
// Assuming ASCII.
mAppRef.SetInlineDataFormat(offset, nullOff - offset + 1,
DataType.StringNullTerm, DataSubType.Ascii, null);
}
}
}