# Xenoblade Chronicles 2 2.1.0
# BID: F77F1559371C0EC6

DECLARATIONS:
  -
    type: variable
    name: last_tick
    value_type: uint64
    default_value: 0
  -
    type: variable
    name: game_speed
    value_type: float
    default_value: 0.03333333333
  -
    type: variable
    name: min_delta
    value_type: float
    default_value: 0.03333333333
    evaluate: "1 / FPS_TARGET"
  -
    type: variable
    name: max_delta
    value_type: float
    default_value: 30
    evaluate: "FPS_TARGET / 2"
  -
    type: variable
    name: dr_target
    value_type: float
    default_value: 0.0166666666
    evaluate: "(1 / FPS_TARGET) / VSYNC_TARGET"
  -
    type: const
    name: microseconds_in_sec
    value: 1000000
  -
    type: const
    name: nano_in_micro
    value: 1000
  -
    type: code
    name: dynamicSpeed
    instructions: [
      [stp, x29, x30, [sp, -16], "!"],
      [mov, x29, sp],
      [mrs, x0, cntpct_el0],
      [adrp, x2, $last_tick],
      [ldr, x1, [x2, $last_tick]],
      [str, x0, [x2, $last_tick]],
      [cbz, x1, :goto3],
      [sub, x0, x0, x1],
      [bl, _convertTickToTimeSpan()],
      [adrp, x1, $max_delta],
      [ldr, s2, [x1, $max_delta]],
      [mov, w1, $nano_in_micro],
      [udiv, x0, x0, x1],
      [ucvtf, s0, x0],
      [mov, w1, $microseconds_in_sec],
      [movk, w1, $microseconds_in_sec, 16],
      [ucvtf, s1, w1],
      [b, :goto2],
      :goto4, [adrp, x0, 0xb8f000],
      [ldr, x0, [x0, 0xd0]],
      [cbz, x0, :goto3],
      [str, s0, [x0, 0x78]],
      [str, s0, [x0, 0x7c]],
      [adrp, x0, 0xc22000],
      [ldr, x0, [x0, 0x2c8]],
      [str, s0, [x0, 0x80]],
      [adrp, x0, $game_speed],
      [str, s0, [x0, $game_speed]],
      [b, :goto3],
      :goto5, [fmov, s1, 1.0],
      [fdiv, s2, s1, s2],
      [fcmp, s0, s2],
      [b.lt, :goto6],
      [fmov, s0, s2],
      [b, :goto4],
      :goto6, [adrp, x0, $min_delta],
      [ldr, s2, [x0, $min_delta]],
      [fcmp, s0, s2],
      [b.hi, :goto4],
      [fmov, s0, s2],
      [b, :goto4],
      :goto2, [fdiv, s0, s0, s1],
      [b, :goto5],
      :goto3, [mov, w8, 0x21a0],
      [ldp, x29, x30, [sp], 16],
      [ret],
    ]
MASTER_WRITE:
  # Dynamic speed
  # Jump before pushing frame
  -
    type: asm_a64
    main_offset: 0x700164
    instructions: [
      [bl, _dynamicSpeed()]
    ]
  # Patch wind speed to take game speed
  -
    type: asm_a64
    main_offset: 0x747DD8
    instructions: [
      [adrp, x8, $game_speed],
      [ldr, s6, [x8, $game_speed]]
    ]
  # Redirect DR timing
  -
    type: asm_a64
    main_offset: 0x6AA14C
    instructions: [
      [adrp, x8, $dr_target],
      [ldr, s0, [x8, $dr_target]]
    ]
ALL_FPS:
  -
    type: evaluate_write
    address: [MAIN, 0xB74BF0]
    value_type: uint32
    value: ["VSYNC_TARGET", "VSYNC_TARGET"]
  # Cutscenes in 30 FPS
  -
    type: compare
    compare_type: "=="
    compare_value_type: uint8
    compare_value: 1
    compare_address: [MAIN, 0xBD7F40]
    value_type: refresh_rate
    value: 60
  -
    type: compare
    compare_type: "=="
    compare_value_type: uint8
    compare_value: 1
    compare_address: [MAIN, 0xBD7F40]
    address: [VARIABLE, min_delta]
    value: 0.0333333333
  -
    type: compare
    compare_type: "=="
    compare_value_type: uint8
    compare_value: 1
    compare_address: [MAIN, 0xBD7F40]
    address: [VARIABLE, max_delta]
    value: 30
  -
    type: compare
    compare_type: "=="
    compare_value_type: uint8
    compare_value: 1
    compare_address: [MAIN, 0xBD7F40]
    address: [MAIN, 0xB74BF0]
    value_type: uint32
    value: [2, 2]
  -
    type: compare
    compare_type: "=="
    compare_value_type: uint8
    compare_value: 1
    compare_address: [MAIN, 0xBD7F40]
    address: [VARIABLE, dr_target]
    value: 0.0166666666
