Jsm.LabelBuilder.cs 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. using OpenVIII.Fields.Scripts.Instructions;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace OpenVIII.Fields.Scripts
  5. {
  6. public static partial class Jsm
  7. {
  8. public sealed class LabelBuilder
  9. {
  10. private readonly Dictionary<Int32, List<IJumpToOpcode>> _targets = new Dictionary<Int32, List<IJumpToOpcode>>();
  11. private Dictionary<Int32, IndexedInstruction> _candidates = new Dictionary<Int32, IndexedInstruction>();
  12. private readonly UInt16 _opcodeCount;
  13. public LabelBuilder(UInt16 opcodeCount)
  14. {
  15. _opcodeCount = opcodeCount;
  16. }
  17. public void TraceInstruction(Int32 position, Int32 label, IndexedInstruction instruction)
  18. {
  19. _candidates.Add(label, instruction);
  20. if (!(instruction.Instruction is IJumpToOpcode jump))
  21. return;
  22. Int32 target = position + jump.Offset;
  23. if (target < 0)
  24. throw new InvalidProgramException($"Trying to jump out of script ({position} -> {target}). The field \"test3.jsm\" isn't supported.");
  25. if (target >= _opcodeCount)
  26. {
  27. if (target == 74) // escouse1.jsm (Lunar Gate - Concourse)
  28. target = _opcodeCount - 1;
  29. else
  30. throw new InvalidProgramException($"Trying to jump out of script ({position} -> {target}).");
  31. }
  32. if (!_targets.TryGetValue(target, out var list))
  33. {
  34. list = new List<IJumpToOpcode>();
  35. _targets.Add(target, list);
  36. }
  37. list.Add(jump);
  38. }
  39. public HashSet<Int32> Commit()
  40. {
  41. HashSet<Int32> result = new HashSet<Int32>();
  42. foreach (var pair in _targets)
  43. {
  44. var offset = pair.Key;
  45. if (!_candidates.TryGetValue(offset, out IndexedInstruction target))
  46. throw new InvalidProgramException($"Invalid jump target: {pair.Key}");
  47. foreach (var jump in pair.Value)
  48. jump.Index = target.Index;
  49. result.Add(target.Index);
  50. }
  51. return result;
  52. }
  53. }
  54. }
  55. }