239 lines
7.6 KiB
C#
239 lines
7.6 KiB
C#
using Newtonsoft.Json;
|
|
using SM64Lib.ASM;
|
|
using SM64Lib.Behaviors.Script;
|
|
using SM64Lib.Data;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Z.Collections.Extensions;
|
|
using static SM64Lib.Extensions.ObjectExtensions;
|
|
|
|
namespace SM64Lib.Behaviors;
|
|
|
|
public class Behavior
|
|
{
|
|
private readonly Dictionary<CustomAsmAreaConfig, BehaviorscriptCommand> knownCustomAsmCommands = [];
|
|
|
|
[JsonProperty]
|
|
public BehaviorConfig Config { get; private set; }
|
|
[JsonProperty]
|
|
public Behaviorscript Script { get; private set; }
|
|
public int CollisionPointer { get; set; }
|
|
public bool EnableCollisionPointer { get; set; }
|
|
public List<int> BehaviorAddressDestinations { get; set; } = [];
|
|
|
|
[JsonIgnore]
|
|
public long Length
|
|
{
|
|
get
|
|
{
|
|
if (Config.FixedLength == -1)
|
|
return Script.Length;
|
|
else
|
|
return Config.FixedLength;
|
|
}
|
|
}
|
|
|
|
public Behavior() : this(new BehaviorConfig())
|
|
{
|
|
}
|
|
|
|
public Behavior(BehaviorConfig config)
|
|
{
|
|
CreateNewBehaviorscript();
|
|
Config = config;
|
|
}
|
|
|
|
public Behavior(BehaviorCreationTypes behaviorCreationType) : this()
|
|
{
|
|
Create(behaviorCreationType);
|
|
}
|
|
|
|
public void Create(BehaviorCreationTypes type)
|
|
{
|
|
CreateNewBehaviorscript();
|
|
|
|
switch (type)
|
|
{
|
|
case BehaviorCreationTypes.SolidObject:
|
|
Script.Add(new BehaviorscriptCommand("00 09 00 00"));
|
|
Script.Add(new BehaviorscriptCommand("11 01 00 01"));
|
|
Script.Add(new BehaviorscriptCommand("2a 00 00 00 00 00 00 00"));
|
|
Script.Add(new BehaviorscriptCommand("08 00 00 00"));
|
|
Script.Add(new BehaviorscriptCommand("0c 00 00 00 80 38 39 cc"));
|
|
Script.Add(new BehaviorscriptCommand("09 00 00 00"));
|
|
break;
|
|
}
|
|
|
|
ParseScript();
|
|
}
|
|
|
|
private void CreateNewBehaviorscript()
|
|
{
|
|
if (Script != null)
|
|
Script.Close();
|
|
Script = [];
|
|
}
|
|
|
|
public bool Read(BinaryData data, int address)
|
|
{
|
|
CreateNewBehaviorscript();
|
|
var success = Script.Read(data, address, Config.IsVanilla, Config.ExpectedLength != -1, Config.ExpectedLength);
|
|
if (Config.IsVanilla)
|
|
Config.FixedLength = (int)Script.Length;
|
|
Config.ExpectedLength = (int)Script.Length;
|
|
ParseScript();
|
|
return success;
|
|
}
|
|
|
|
public void Write(BinaryData data, int address)
|
|
{
|
|
//if (!Config.IsVanilla)
|
|
TakeoverSettingsToScript();
|
|
|
|
var length = Script.Write(data, address);
|
|
|
|
if (Config.FixedLength != -1 && length != Config.FixedLength)
|
|
data.Position -= length - Config.FixedLength;
|
|
Config.ExpectedLength = length;
|
|
}
|
|
|
|
public void ParseScript()
|
|
{
|
|
var dicCustomAsmFuncs = new Dictionary<int, CustomAsmAreaLinkOptions>();
|
|
EnableCollisionPointer = false;
|
|
knownCustomAsmCommands.Clear();
|
|
|
|
foreach (var link in Config.CustomAsmLinks)
|
|
dicCustomAsmFuncs.AddOrUpdate(link.CustomAsmAreaConfig.RamAddress | unchecked((int)0x80000000), link);
|
|
|
|
foreach (BehaviorscriptCommand cmd in Script)
|
|
{
|
|
switch (cmd.CommandType)
|
|
{
|
|
case BehaviorscriptCommandTypes.x2A_SetCollision:
|
|
CollisionPointer = BehaviorscriptCommandFunctions.X2A.GetCollisionPointer(cmd);
|
|
EnableCollisionPointer = true;
|
|
break;
|
|
case BehaviorscriptCommandTypes.x0C_CallFunction:
|
|
var ptr = BehaviorscriptCommandFunctions.X0C.GetAsmPointer(cmd);
|
|
if (dicCustomAsmFuncs.ContainsKey(ptr))
|
|
knownCustomAsmCommands.AddOrUpdate(dicCustomAsmFuncs[ptr].CustomAsmAreaConfig, cmd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void TakeoverSettingsToScript()
|
|
{
|
|
// Update collision pointer
|
|
AddUpdateRemoveCmd(
|
|
BehaviorscriptCommandTypes.x2A_SetCollision,
|
|
EnableCollisionPointer,
|
|
() => BehaviorscriptCommandFactory.Build_x2A(CollisionPointer),
|
|
(cmd) => BehaviorscriptCommandFunctions.X2A.SetCollisionPointer(cmd, CollisionPointer));
|
|
|
|
// Insert Custom Asm Links
|
|
{
|
|
foreach (var link in Config.CustomAsmLinks)
|
|
{
|
|
var asmPointer = link.CustomAsmAreaConfig.RamAddress | unchecked((int)0x80000000);
|
|
var cmdStartLoop = Script.FirstOfType(BehaviorscriptCommandTypes.x08_LoopStart);
|
|
var cmdStartLoopIndex = Script.IndexOf(cmdStartLoop);
|
|
int iInsert;
|
|
|
|
if (link.Loop)
|
|
{
|
|
if (cmdStartLoop is not null)
|
|
iInsert = cmdStartLoopIndex + 1;
|
|
else
|
|
iInsert = -1;
|
|
}
|
|
else
|
|
{
|
|
if (cmdStartLoop is not null)
|
|
iInsert = cmdStartLoopIndex;
|
|
else
|
|
iInsert = Script.Count - 2;
|
|
}
|
|
|
|
if (knownCustomAsmCommands.ContainsKey(link.CustomAsmAreaConfig))
|
|
{
|
|
var cmd = knownCustomAsmCommands[link.CustomAsmAreaConfig];
|
|
var cmdIndex = Script.IndexOf(cmd);
|
|
BehaviorscriptCommandFunctions.X0C.SetAsmPointer(cmd, asmPointer);
|
|
|
|
if (cmdIndex != -1)
|
|
{
|
|
var reinsert = false;
|
|
|
|
if (link.Loop && cmdIndex < cmdStartLoopIndex)
|
|
{
|
|
reinsert = true;
|
|
iInsert -= 1;
|
|
}
|
|
else if (!link.Loop && cmdIndex > cmdStartLoopIndex)
|
|
reinsert = true;
|
|
|
|
if (reinsert)
|
|
{
|
|
Script.Remove(cmd);
|
|
Script.Insert(iInsert, cmd);
|
|
}
|
|
}
|
|
}
|
|
else if (iInsert != -1)
|
|
{
|
|
var cmd = BehaviorscriptCommandFactory.Build_x0C(asmPointer);
|
|
Script.Insert(iInsert, cmd);
|
|
knownCustomAsmCommands.Add(link.CustomAsmAreaConfig, cmd);
|
|
}
|
|
}
|
|
|
|
foreach (var kvp in knownCustomAsmCommands.ToArray())
|
|
{
|
|
if (!Config.CustomAsmLinks.Where(n => n.CustomAsmAreaConfig == kvp.Key).Any())
|
|
{
|
|
knownCustomAsmCommands.Remove(kvp.Key);
|
|
Script.RemoveIfContains(kvp.Value);
|
|
kvp.Value.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddUpdateRemoveCmd(BehaviorscriptCommandTypes cmdType, bool conditionAddUpdate, Func<BehaviorscriptCommand> createCmd, Action<BehaviorscriptCommand> updateCmd)
|
|
{
|
|
var cmd = Script.FirstOfType(cmdType);
|
|
if (cmd is not null)
|
|
{
|
|
if (conditionAddUpdate)
|
|
updateCmd(cmd);
|
|
else
|
|
{
|
|
Script.Remove(cmd);
|
|
cmd.Close();
|
|
}
|
|
}
|
|
else if (conditionAddUpdate)
|
|
{
|
|
cmd = createCmd();
|
|
Script.Insert(1, cmd);
|
|
}
|
|
}
|
|
|
|
public void CopyPropertiesTo(Behavior dest)
|
|
{
|
|
// Copy Script
|
|
TakeoverSettingsToScript();
|
|
dest.Script.Close();
|
|
dest.Script.AddRange(Script);
|
|
dest.ParseScript();
|
|
|
|
// Copy Configs
|
|
dest.Config.Name = Config.Name;
|
|
Config.ParamsInfo.CloneTo(dest.Config.ParamsInfo);
|
|
}
|
|
|
|
}
|