# The Legend of Zelda: Breath of the Wild 1.8.2
# BID: DE0B6AC4EFC1DDA7

DECLARATIONS:
  -
    type: variable
    name: game_speed
    value_type: float
    default_value: 1.0
  -
    type: variable
    name: min_delta
    value_type: float
    default_value: 1.0
    evaluate: 30 / FPS_TARGET
  -
    type: variable
    name: dr_factor
    value_type: float
    default_value: 100.0
    evaluate: (FPS_TARGET / 30) * 100
  -
    type: variable
    name: frame_tick
    value_type: uint64
    default_value: 0
  -
    type: const
    name: default_frame_nanoseconds
    value: 33333333
  -
    type: code
    name: dynamicSpeed
    instructions: [
      [stp, x29, x30, [sp, -16], "!"],
      [mov, x29, sp],
      [blr, x8],
      [mrs, x2, cntpct_el0],
      [adrp, x8, $frame_tick],
      [ldr, x1, [x8, $frame_tick]],
      [cmp, x1, xzr],
      [str, x2, [x8, $frame_tick]],
      [b.eq, :goto4],
      [sub, x0, x2, x1],
      [bl, _convertTickToTimeSpan()],
      [ucvtf, s0, x0],
      [mov, w0, $default_frame_nanoseconds],
      [movk, w0, $default_frame_nanoseconds, 16],
      [ucvtf, s1, w0],
      [fdiv, s0, s0, s1],
      [fmov, s1, 2.0],
      [fcmp, s0, s1],
      [b.lt, :goto2],
      [fmov, s0, s1],
      :goto2, [adrp, x8, $min_delta],
      [ldr, s1, [x8, $min_delta]],
      [fcmp, s0, s1],
      [b.gt, :goto3],
      [fmov, s0, s1],
      :goto3, [adrp, x8, $game_speed],
      [str, s0, [x8, $game_speed]],
      :goto4, [ldp, x29, x30, [sp], 16],
      [ret]
    ]
  -
    type: variable
    name: cutscene_state
    value_type: uint8
    default_value: 1
  -
    type: code
    name: signalCutscene
    instructions: [
      [adrp, x7, $cutscene_state],
      [strb, w1, [x7, $cutscene_state]],
      [b, 0xbf7f78]
    ]
MASTER_WRITE:
  # Remove double buffer
  ## REF: 61 F6 42 39
  -
    type: asm_a64
    main_offset: 0xD80748
    instructions: [
      [mov, w1, 1]
    ]
  # Change pointer of time factor for DR calculations to $dr_factor
  ## REF: 01 01 27 1E 08 59 A8 52 03 08 21 1E, replace hardcoded 100.0 float
  -
    type: asm_a64
    main_offset: 0x1153314
    instructions: [
      [adrp, x8, $dr_factor],
      [fmul, s3, s0, s1],
      [ldr, s1, [x8, $dr_factor]]
    ]
  # Dynamic speed
  ## Replace nvnQueuePresentTexture call to code cave _dynamicSpeed()
  -
    type: asm_a64
    main_offset: 0xD874FC
    instructions: [
      [bl, _dynamicSpeed()]
    ]
  ## Patch game speed function to use speed from $game_speed
  -
    type: asm_a64
    main_offset: 0x14092A8
    instructions: [
      [b, +12]
    ]
  -
    type: asm_a64
    main_offset: 0x14092CC
    instructions: [
      [adrp, x9, $game_speed],
      [ldr, s1, [x9, $game_speed]]
    ]
  ## Patch UI speed function to use speeed from $game_speed
  -
    type: asm_a64
    main_offset: 0x116D0D8
    instructions: [
      [adrp, x7, $game_speed]
    ]
  -
    type: asm_a64
    main_offset: 0x116D0E4
    instructions: [
      [ldr, s0, [x7, $game_speed]]
    ]
  # Signal Cutscenes playing
  -
    type: asm_a64
    main_offset: 0xC00680
    instructions: [
      [bl, _signalCutscene()]
    ]
  -
    type: asm_a64
    main_offset: 0xC00E40
    instructions: [
      [bl, _signalCutscene()]
    ]
ALL_FPS:
  # Scrolling speed
  ## REF: 68 16 41 B9 D8 22 D5 1A
  ## ADRP + LDR above it
  -
    type: evaluate_write
    address: [MAIN, 0x1C4BA40]
    value_type: float
    value: "30 / FPS_TARGET"
  # Prerendered cutscenes must be played at 30 FPS
  -
    type: compare
    compare_address: [VARIABLE, cutscene_state]
    compare_type: "=="
    compare_value: 0
    value_type: refresh_rate
    value: 30
