;;-*-Lisp-*-
(in-package goal)
(require "engine/ui/progress/progress.gc")

#| Code for the progress menu in the PC port. The original code is still loaded, this just has some overriden functions. |#

;;---------------------------
;;---------------------------
;; pc menu extra stuff

(defconstant PROGRESS_PC_PAGE_HEIGHT 7)

(defconstant PROGRESS_SCROLL_DIR_UP -1)

(defconstant PROGRESS_SCROLL_DIR_DOWN 1)

(defconstant GAME_MIN_RES_MULT 0.5)

(deftype progress-scroll (structure)
  ((transition  float)
   (start-index int16)
   (real-index  int16)
   (direction   int8)
   (last-screen progress-screen)))

(define *progress-scroll* (new 'static 'progress-scroll))

(defmacro progress-scrolling? ()
  `(< (-> *progress-scroll* transition) 1.0))

(defmacro progress-scrolling-up? ()
  `(and (progress-scrolling?) (= (-> *progress-scroll* direction) PROGRESS_SCROLL_DIR_UP)))

(defmacro progress-scrolling-down? ()
  `(and (progress-scrolling?) (= (-> *progress-scroll* direction) PROGRESS_SCROLL_DIR_DOWN)))

(defconstant *progress-scroll-start* (-> *progress-scroll* start-index))

(defconstant *progress-scroll-end* (+ -1 PROGRESS_PC_PAGE_HEIGHT (-> *progress-scroll* start-index)))

(defmacro progress-scroll-reset ()
  "resets scroll. nothing will be scrolling, as if it had finished."
  `(begin
    (set! (-> *progress-scroll* transition) 1.0)
    (set! (-> *progress-scroll* direction) 0)
    (set! (-> *progress-scroll* start-index) 0)))

(defmacro progress-scroll-up! ()
  `(begin
    (set! (-> *progress-scroll* transition) 0.0)
    (set! (-> *progress-scroll* direction) PROGRESS_SCROLL_DIR_UP)
    (1-! (-> *progress-scroll* start-index))))

(defmacro progress-scroll-down! ()
  `(begin
    (set! (-> *progress-scroll* transition) 0.0)
    (set! (-> *progress-scroll* direction) PROGRESS_SCROLL_DIR_DOWN)
    (1+! (-> *progress-scroll* start-index))))

(defun progress-fast-save-and-start-speedrun ((category speedrun-category))
  "saves to first slot, updates last-slot-saved for cursor, then starts speedrun"
  (set! (-> *progress-state* last-slot-saved) 0)
  (auto-save-command 'save 0 0 *default-pool*)
  (start-speedrun category))

;; used in title logo
(define *quit-to-title* #f)

;; ############################
;; CAROUSELL STUFF
;; ############################

(deftype progress-carousell-state (structure)
  ((int-backup           int)
   (symbol-backup        symbol)
   (subtitle-backup      pc-language)
   (aspect-native-choice symbol)
   (current-carousell    (array text-id))
   (selection            int)
   (direction            symbol)
   (transition           symbol)
   (x-offset             int32)))

(define *progress-carousell* (new 'static 'progress-carousell-state))

(defmacro def-progress-carousell (name texts)
  `(define ,name (new 'static 'boxed-array :type text-id ,@(apply (lambda (x) `(text-id ,x)) texts))))

;; all carousells
(def-progress-carousell *carousell-display-mode* (windowed fullscreen borderless))

(def-progress-carousell *carousell-msaa* (off 2-times 4-times 8-times 16-times))

(def-progress-carousell *carousell-lod-bg* (lod-high lod-low))

(def-progress-carousell *carousell-lod-fg* (lod-high lod-low lod-ps2))

(def-progress-carousell *carousell-speaker* (speaker-always speaker-never speaker-auto))

(def-progress-carousell *carousell-frame-rate-150fps* (60fps 100fps 150fps))

(def-progress-carousell *carousell-frame-rate-100fps* (60fps 100fps))

(define *carousell-frame-rate* *carousell-frame-rate-150fps*)

;; pad remap stuff
(define *progress-pad-timeout* (the-as time-frame 0)) ;; timeout for remap buttons

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  MENU OPTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define *main-options-pc*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id game-options)
            :scale #t
            :param3 (game-option-menu game-settings))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id graphic-options)
            :scale #t
            :param3 (game-option-menu graphic-settings))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id sound-options)
            :scale #t
            :param3 (game-option-menu sound-settings))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id load-game)
            :scale #t
            :param3 (game-option-menu load-game))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id save-game)
            :scale #t
            :param3 (game-option-menu save-game))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id disable-auto-save)
            :scale #t
            :param3 (game-option-menu memcard-disable-auto-save))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id quit-game)
            :scale #t
            :param3 (game-option-menu quit))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *main-options-secrets*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id game-options)
            :scale #t
            :param3 (game-option-menu game-settings))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id graphic-options)
            :scale #t
            :param3 (game-option-menu graphic-settings))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id sound-options)
            :scale #t
            :param3 (game-option-menu sound-settings))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id load-game)
            :scale #t
            :param3 (game-option-menu load-game))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id save-game)
            :scale #t
            :param3 (game-option-menu save-game))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id disable-auto-save)
            :scale #t
            :param3 (game-option-menu memcard-disable-auto-save))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id secrets)
            :scale #t
            :param3 (game-option-menu secrets))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id quit-game)
            :scale #t
            :param3 (game-option-menu quit))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *game-options-pc*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-options)
            :scale #t
            :param3 (game-option-menu input-options))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id play-hints)
            :scale #t
            :on-confirm
            (lambda ()
              (set! (-> *setting-control* default play-hints) (-> *pc-settings* memcard-play-hints?))))
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id subtitles) :scale #t)
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id hinttitles) :scale #t)
       (new 'static 'game-option :option-type (game-option-type language) :name (text-id language) :scale #t)
       (new 'static 'game-option :option-type (game-option-type language-subtitles) :name (text-id subtitles-language) :scale #t)
       (new 'static 'game-option :option-type (game-option-type language-text) :name (text-id text-language) :scale #t)
       (new 'static 'game-option :option-type (game-option-type speaker) :name (text-id subtitles-speaker) :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id misc-options)
            :scale #t
            :param3 (game-option-menu misc-options))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *graphic-options-pc*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id game-resolution)
            :scale #t
            :param3 (game-option-menu resolution))
       (new 'static 'game-option :option-type (game-option-type display-mode) :name (text-id display-mode) :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id display)
            :scale #t
            :param3 (game-option-menu monitor)
            :option-disabled-func
            (lambda ()
              (= (pc-get-display-mode) 'windowed)))
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id vsync) :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id aspect-ratio)
            :scale #t
            :param3 (game-option-menu aspect-ratio))
       (new 'static 'game-option :option-type (game-option-type msaa) :name (text-id msaa) :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type frame-rate)
            :name (text-id frame-rate)
            :scale #t
            :option-disabled-func
            (lambda ()
              (or (-> *pc-settings* speedrunner-mode?) (<= (pc-get-active-display-refresh-rate) 60))))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id ps2-options)
            :scale #t
            :param3 (game-option-menu gfx-ps2-options))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *sound-options-pc*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type slider)
            :name (text-id sfx-volume)
            :scale #t
            :param1 0.0
            :param2 100.0
            :slider-step-size 1.0
            :on-confirm
            (lambda ((val object))
              (set! (-> *setting-control* default sfx-volume) (-> *pc-settings* memcard-volume-sfx))
              (none)))
       (new 'static
            'game-option
            :option-type (game-option-type slider)
            :name (text-id music-volume)
            :scale #t
            :param1 0.0
            :param2 100.0
            :slider-step-size 1.0
            :on-confirm
            (lambda ((val object))
              (set! (-> *setting-control* default music-volume) (-> *pc-settings* memcard-volume-music))
              (none)))
       (new 'static
            'game-option
            :option-type (game-option-type slider)
            :name (text-id speech-volume)
            :scale #t
            :param1 0.0
            :param2 100.0
            :slider-step-size 1.0
            :on-confirm
            (lambda ((val object))
              (set! (-> *setting-control* default dialog-volume) (-> *pc-settings* memcard-volume-dialog))
              (none)))
       ;(new 'static 'game-option :option-type (game-option-type on-off) :name (text-id music-fadein) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *camera-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type normal-inverted)
            :name (text-id camera-controls-first-horz)
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type normal-inverted)
            :name (text-id camera-controls-first-vert)
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type normal-inverted)
            :name (text-id camera-controls-third-horz)
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type normal-inverted)
            :name (text-id camera-controls-third-vert)
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type confirmation)
            :name (text-id restore-defaults)
            :scale #t
            :on-confirm
            (lambda ()
              (reset-camera *pc-settings* #t)
              (none)))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *controller-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-opts-select-controller)
            :scale #t
            :param3 (game-option-menu select-controller))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id vibrations)
            :scale #t
            :option-disabled-func
            (lambda ()
              (not (pc-current-controller-has-rumble?)))
            :on-confirm
            (lambda ()
              (set! (-> *setting-control* default vibration) (-> *pc-settings* memcard-vibration?))))
       (new 'static
            'game-option
            :option-type (game-option-type slider)
            :name (text-id input-opts-analog-deadzone)
            :scale #t
            :param1 0.0
            :param2 1.0
            :slider-step-size 0.01
            :slider-show-decimal? #t)
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-ignore-controller-win-focus)
            :scale #t
            :on-change
            (lambda ((val object))
              (let ((val-sym (the-as symbol val))) (set-ignore-controller-in-bg! *pc-settings* val-sym) (none))))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-controller-led-reflect-hp)
            :scale #t
            :option-disabled-func
            (lambda ()
              (not (pc-current-controller-has-led?))))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-controller-led-reflect-eco)
            :scale #t
            :option-disabled-func
            (lambda ()
              (not (pc-current-controller-has-led?))))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-controller-led-reflect-heat)
            :scale #t
            :option-disabled-func
            (lambda ()
              (not (pc-current-controller-has-led?))))
       (new 'static
            'game-option
            :option-type (game-option-type confirmation)
            :name (text-id restore-defaults)
            :scale #t
            :on-confirm
            (lambda ()
              (reset-input *pc-settings* 'controller #t)
              (set-ignore-controller-in-bg! *pc-settings* (-> *pc-settings* ignore-controller-win-unfocused?))
              (none)))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *mouse-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-mouse-enable-camera)
            :scale #t
            :on-change
            (lambda ((val object))
              (let ((val-sym (the-as symbol val)))
                (set! (-> *pc-settings* mouse-camera?) val-sym)
                (update-mouse-controls! *pc-settings*)
                (none))))
       (new 'static
            'game-option
            :option-type (game-option-type slider)
            :name (text-id input-opts-mouse-horizontal-sens)
            :scale #t
            :param1 -30.0
            :param2 30.0
            :slider-step-size 0.25
            :slider-show-decimal? #t
            :on-confirm
            (lambda ()
              (update-mouse-controls! *pc-settings*)
              (none))
            :option-disabled-func
            (lambda ()
              (not (-> *pc-settings* mouse-camera?))))
       (new 'static
            'game-option
            :option-type (game-option-type slider)
            :name (text-id input-opts-mouse-vertical-sens)
            :scale #t
            :param1 -30.0
            :param2 30.0
            :slider-step-size 0.25
            :slider-show-decimal? #t
            :on-confirm
            (lambda ()
              (update-mouse-controls! *pc-settings*)
              (none))
            :option-disabled-func
            (lambda ()
              (not (-> *pc-settings* mouse-camera?))))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-mouse-enable-movement)
            :scale #t
            :on-change
            (lambda ((val object))
              (let ((val-sym (the-as symbol val)))
                (set! (-> *pc-settings* mouse-movement?) val-sym)
                (update-mouse-controls! *pc-settings*)
                (none))))
       (new 'static
            'game-option
            :option-type (game-option-type confirmation)
            :name (text-id restore-defaults)
            :scale #t
            :on-confirm
            (lambda ()
              (reset-input *pc-settings* 'mouse #t)
              (update-mouse-controls! *pc-settings*)
              (none)))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *reassign-binds-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-opts-binds-controller)
            :scale #t
            :param3 (game-option-menu controller-binds)
            :option-disabled-func
            (lambda ()
              (<= (pc-get-controller-count) 0)))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-opts-binds-keyboard)
            :scale #t
            :param3 (game-option-menu keyboard-binds)
            :option-disabled-func
            (lambda ()
              (not (pc-get-keyboard-enabled?))))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-opts-binds-mouse)
            :scale #t
            :param3 (game-option-menu mouse-binds)
            :option-disabled-func
            (lambda ()
              (not (-> *pc-settings* mouse-enabled?))))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *input-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id camera-options)
            :scale #t
            :param3 (game-option-menu camera-options))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-opts-controller-opts)
            :scale #t
            :param3 (game-option-menu controller-options)
            :option-disabled-func
            (lambda ()
              (<= (pc-get-controller-count) 0)))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-enable-kb)
            :scale #t
            :on-change
            (lambda ((val object))
              (let ((val-sym (the-as symbol val)))
                (pc-set-keyboard-enabled! val-sym)
                (set! (-> *pc-settings* keyboard-enabled?) val-sym)
                (none))))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-enable-mouse)
            :scale #t
            :on-change
            (lambda ((val object))
              (let ((val-sym (the-as symbol val)))
                (set! (-> *pc-settings* mouse-enabled?) val-sym)
                (update-mouse-controls! *pc-settings*)
                (none))))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-opts-mouse-opts)
            :scale #t
            :param3 (game-option-menu mouse-options)
            :option-disabled-func
            (lambda ()
              (not (-> *pc-settings* mouse-enabled?))))
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id input-opts-auto-hide-cursor)
            :scale #t
            :on-change
            (lambda ((val object))
              (let ((val-sym (the-as symbol val)))
                (set! (-> *pc-settings* auto-hide-cursor?) val-sym)
                (update-mouse-controls! *pc-settings*)
                (none))))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id input-opts-reassign-binds)
            :scale #t
            :param3 (game-option-menu reassign-binds-options))
       (new 'static
            'game-option
            :option-type (game-option-type confirmation)
            :name (text-id restore-defaults)
            :scale #t
            :on-confirm
            (lambda ()
              (reset-input *pc-settings* 'input #t)
              (pc-set-keyboard-enabled! (-> *pc-settings* keyboard-enabled?))
              (update-mouse-controls! *pc-settings*)
              (none)))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *misc-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id money-starburst) :scale #t)
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id discord-rpc) :scale #t)
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id cutscene-skips) :scale #t)
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id speedrunner-mode) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *gfx-ps2-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type lod-bg) :name (text-id lod-bg) :scale #t)
       (new 'static 'game-option :option-type (game-option-type lod-fg) :name (text-id lod-fg) :scale #t)
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id ps2-parts) :scale #t)
       (new 'static 'game-option :option-type (game-option-type on-off) :name (text-id force-envmap) :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type on-off)
            :name (text-id force-actors)
            :scale #t
            :option-disabled-func
            (lambda ()
              (-> *pc-settings* speedrunner-mode?)))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

;; TODO - aspect ratios are hard-coded
(define *aspect-ratio-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type aspect-new) :name (text-id fit-to-screen) :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type aspect-new)
            :name (text-id aspect-fmt)
            :param1 4.0
            :param2 3.0
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type aspect-new)
            :name (text-id aspect-fmt)
            :param1 16.0
            :param2 9.0
            :scale #t)
       (new 'static 'game-option :option-type (game-option-type aspect-new) :name (text-id aspect4x3-ps2) :param1 -1.0 :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type aspect-new)
            :name (text-id aspect16x9-ps2)
            :param1 -2.0
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type aspect-new)
            :name (text-id aspect-fmt)
            :param1 16.0
            :param2 10.0
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type aspect-new)
            :name (text-id aspect-fmt)
            :param1 21.0
            :param2 9.0
            :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type aspect-new)
            :name (text-id aspect-fmt)
            :param1 64.0
            :param2 27.0
            :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *title-pc*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id new-game)
            :scale #t
            :param3 (game-option-menu save-game-title))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id load-game)
            :scale #t
            :param3 (game-option-menu load-game))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id options)
            :scale #t
            :param3 (game-option-menu settings-title))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id secrets)
            :scale #t
            :param3 (game-option-menu secrets))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id quit-game)
            :scale #t
            :param3 (game-option-menu quit-title))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *speedrun-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-reset-current-run) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-new-full-game) :scale #t)
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id speedrun-new-individual-level)
            :scale #t
            :param3 (game-option-menu speedrun-il-options))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id speedrun-new-cat-ext)
            :scale #t
            :param3 (game-option-menu speedrun-cat-ext-options))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *speedrun-il-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-training) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-village1) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-beach) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-jungle) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-misty) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-firecanyon) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-village2) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-sunken) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-swamp) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-rolling) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-ogre) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-village3) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-snow) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-cave) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-lavatube) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-il-citadel) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *speedrun-cat-ext-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-new-full-game) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-new-game-plus) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-hub1-100) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-hub2-100) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-hub3-100) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id speedrun-all-cutscenes) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *secrets-title*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id music-player)
            :scale #t
            :param3 (game-option-menu music-player))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id cheats)
            :scale #t
            :param3 (game-option-menu cheats))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *secrets*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id cheats)
            :scale #t
            :param3 (game-option-menu cheats))
       (new 'static
            'game-option
            :option-type (game-option-type menu)
            :name (text-id checkpoint-select)
            :scale #t
            :param3 (game-option-menu checkpoint-select)
            :option-disabled-func
            (lambda ()
              (not (>= (calculate-completion (the progress #f)) 100.0))))
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *checkpoint-select-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id training-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id village1-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id beach-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id jungle-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id misty-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id firecanyon-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id village2-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id sunken-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id swamp-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id rolling-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id ogre-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id village3-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id snow-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id cave-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id lavatube-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id citadel-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id finalboss-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *cheats*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-sidekick-alt)
            :scale #t
            :param1 5.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-big-head)
            :scale #t
            :param1 9.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-small-head)
            :scale #t
            :param1 10.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-big-fist)
            :scale #t
            :param1 11.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-big-head-npc)
            :scale #t
            :param1 16.0)
       (new 'static 'game-option :option-type (game-option-type cheat-toggle) :name (text-id cheat-tunes) :scale #t :param1 6.0)
       (new 'static 'game-option :option-type (game-option-type cheat-toggle) :name (text-id cheat-sky) :scale #t :param1 7.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-huge-head)
            :scale #t
            :param1 15.0)
       (new 'static 'game-option :option-type (game-option-type cheat-toggle) :name (text-id cheat-mirror) :scale #t :param1 8.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-eco-yellow)
            :scale #t
            :param1 3.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-eco-blue)
            :scale #t
            :param1 2.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-eco-red)
            :scale #t
            :param1 1.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-eco-green)
            :scale #t
            :param1 0.0)
       (new 'static 'game-option :option-type (game-option-type cheat-toggle) :name (text-id cheat-invinc) :scale #t :param1 4.0)
       (new 'static
            'game-option
            :option-type (game-option-type cheat-toggle)
            :name (text-id cheat-no-tex)
            :scale #t
            :param1 12.0)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

(define *music-player-options*
  (new 'static
       'boxed-array
       :type
       game-option
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id village1-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id beach-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id jungle-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id fishgame) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id jungleb-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id misty-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id firecanyon-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id village2-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id rolling-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id swamp-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id sunken-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id ogre-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id ogreboss) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id village3-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id snow-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id cave-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id lavatube-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id citadel-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id finalboss-level-name) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id credits) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button-music) :name (text-id danger-mus) :scale #t)
       (new 'static 'game-option :option-type (game-option-type button) :name (text-id back) :scale #t)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  DATA LISTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define *music-list*
  '(village1 beach
            jungle
            fishgame
            jungleb
            misty
            firecanyon
            village2
            rolling
            swamp
            sunken
            ogre
            ogreboss
            village3
            snow
            maincave
            lavatube
            citadel
            finalboss
            credits
            danger))

(define *progress-music* -1)

(defmacro progress-get-music-by-index (idx)
  `(the symbol (ref *music-list* ,idx)))

(defmacro progress-reset-music-parms ()
  `(begin
    (set! *progress-music* -1)
    (set! *progress-flava* -1)))

(defmacro static-text-list-array (&rest texts)
  `(new 'static 'boxed-array :type text-id ,@(apply (lambda (x) `(text-id ,x)) texts)))

(define *music-flava-name-list*
  (new 'static
       'boxed-array
       :type (array text-id)
       (static-text-list-array village1-level-name
                               flava-vi1-unused0
                               flava-sage
                               flava-sage-hut
                               flava-birdlady
                               flava-farmer
                               flava-assistant
                               flava-mayor
                               flava-sculptor
                               flava-explorer
                               flava-dock)
       (static-text-list-array beach-level-name flava-bea-sentinel flava-bea-cannon flava-bea-grotto flava-birdlady)
       (static-text-list-array jungle-level-name flava-jun-temple-exit flava-jun-lurkerm flava-jun-temple-top)
       (static-text-list-array fishgame)
       (static-text-list-array jungleb-level-name flava-jub-eggtop flava-jub-plant-boss)
       (static-text-list-array misty-level-name flava-mis-battle flava-mis-boat flava-racer flava-mis-unused0)
       (static-text-list-array firecanyon-level-name flava-racer flava-fic-unused0)
       (static-text-list-array village2-level-name
                               flava-sage
                               flava-assistant
                               flava-warrior
                               flava-geologist
                               flava-gambler
                               flava-levitator)
       (static-text-list-array rolling-level-name flava-rol-gorge)
       (static-text-list-array swamp-level-name flava-swa-game flava-swa-launcher flava-swa-battle flava-flutflut)
       (static-text-list-array sunken-level-name)
       (static-text-list-array ogre-level-name flava-ogr-middle flava-ogr-end)
       (static-text-list-array ogreboss)
       (static-text-list-array village3-level-name flava-vi3-miners flava-sage flava-assistant flava-vi3-mai flava-vi3-sno)
       (static-text-list-array snow-level-name flava-sno-battle flava-flutflut flava-sno-cave flava-sno-fort flava-sno-balls)
       (static-text-list-array cave-level-name flava-mai-rob flava-mai-rob-top flava-mai-mai flava-mai-dar)
       (static-text-list-array zero lavatube-level-name flava-lav-middle flava-lav-end)
       (static-text-list-array citadel-level-name
                               flava-sage
                               flava-assistant
                               flava-cit-yellowsage
                               flava-cit-redsage
                               flava-cit-bluesage
                               flava-cit-hub)
       (static-text-list-array finalboss-level-name flava-fin-middle flava-fin-end)
       (static-text-list-array credits flava-credits-middle flava-credits-end)
       (static-text-list-array danger-mus)))

(defmacro def-language-remap-info (name langs)
  `(define ,name (quote ,(apply (lambda (x) `((the binteger (text-id ,x)) (the binteger (pc-language ,x)))) langs))))

(def-language-remap-info *language-remap-info-pc*
                         (english uk-english
                                  french
                                  german
                                  spanish
                                  italian
                                  japanese
                                  portuguese
                                  br-portuguese
                                  swedish
                                  finnish
                                  danish
                                  norwegian
                                  dutch
                                  hungarian
                                  catalan
                                  icelandic
                                  polish
                                  lithuanian
                                  czech
                                  croatian
                                  galician))

(define *text-languages*
  (static-text-list-array english
                          uk-english
                          french
                          german
                          spanish
                          italian
                          japanese
                          portuguese
                          br-portuguese
                          swedish
                          finnish
                          danish
                          norwegian
                          dutch
                          hungarian
                          polish
                          lithuanian
                          czech
                          croatian
                          galician))

(define *subtitle-languages*
  (static-text-list-array english
                          uk-english
                          french
                          german
                          spanish
                          italian
                          br-portuguese
                          swedish
                          finnish
                          danish
                          dutch
                          polish
                          lithuanian
                          czech
                          croatian
                          galician))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  DYNAMIC MENU THINGS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define-perm *temp-options-alloced* symbol #f)

;; this is to avoid changing the value of *temp-options* or reallocating the options when reloading file
(unless *temp-options-alloced*
  (define *temp-options* (new 'static 'boxed-array :type game-option :length 200 :allocated-length 200))
  (dotimes (i (-> *temp-options* length))
    (set! (-> *temp-options* i) (new 'global 'game-option))
    (set! (-> *temp-options* i name-override) (new 'global 'string 128 (the-as string #f))))
  (true! *temp-options-alloced*))

(defmacro add-flava-player-option (text-id flava)
  "add a music player w/ flava button to *temp-options* with specified size"
  `(let ((option (-> *temp-options* (length *temp-options*))))
    (set! (-> option option-type) (game-option-type button-flava))
    (set! (-> option name) ,text-id)
    (set! (-> option param1) (the float ,flava))
    (set! (-> option scale) #t)
    (1+! (-> *temp-options* length))))

(defmacro add-back-option ()
  "add *back-button* to *temp-options*"
  `(let ((option (-> *temp-options* (length *temp-options*))))
    (set! (-> option option-type) (game-option-type button))
    (set! (-> option name) (text-id back))
    (set! (-> option scale) #t)
    (1+! (-> *temp-options* length))))

(defmacro add-message-option (text)
  "adds an option that isn't selectable but displays some text"
  `(let ((option (-> *temp-options* (length *temp-options*))))
    (set! (-> option option-type) (game-option-type button))
    (set! (-> option option-disabled-func) (lambda () #t))
    (set! (-> option name) ,text)
    (set! (-> option scale) #t)
    (1+! (-> *temp-options* length))))

(defmacro add-resolution-option (x y)
  "add a resolution button to *temp-options* with specified size"
  `(let ((option (-> *temp-options* (length *temp-options*))))
    (set! (-> option option-type) (game-option-type resolution))
    (set! (-> option option-disabled-func) #f)
    (set! (-> option name) (text-id resolution-fmt))
    (set! (-> option param1) (the float ,x))
    (set! (-> option param2) (the float ,y))
    (set! (-> option scale) #t)
    (1+! (-> *temp-options* length))))

(defun build-resolution-options ()
  (set! (-> *temp-options* length) 0)
  ;; Enumerate the available resolutions from C++
  (let ((window-width 0)
        (window-height 0)
        (window-aspect-ratio 0.0)
        (num-resolutions (pc-get-num-resolutions (= (pc-get-display-mode) 'windowed)))
        (num-resolutions-added 0))
    (when (> num-resolutions 0)
      (pc-get-window-size (& window-width) (& window-height))
      (set! window-aspect-ratio (/ (the float window-width) (the float window-height)))
      (dotimes (i num-resolutions)
        (let ((res-width 0)
              (res-height 0)
              (res-aspect-ratio 0.0))
          (pc-get-resolution i (= (pc-get-display-mode) 'windowed) (& res-width) (& res-height))
          (set! res-aspect-ratio (/ (the float res-width) (the float res-height)))
          ;; Ignore those that aren't relevant to the current window's aspect ratio
          ;; we only do this when we aren't in windowed mode, because in windowed mode
          ;; you are using the resolution menu to change it...so it doesn't make sense to base it on the size
          (when (or (= (pc-get-display-mode) 'windowed) (< (fabs (- window-aspect-ratio res-aspect-ratio)) 0.05))
            (add-resolution-option res-width res-height)
            (1+! num-resolutions-added)))))
    ;; if we didn't add any options, inform them they have a weird aspect ratio
    (cond
      ((zero? num-resolutions-added) (add-message-option (text-id progress-no-other-resolution-options)))
      ((fullscreen?)
       ;; for the weird people that want a more "PS2-like" experience
       (add-resolution-option 512 448)
       (add-resolution-option 512 224)))
    (add-back-option)))

(defun build-flava-player-options ((mus-idx int))
  (set! (-> *temp-options* length) 0)
  (dotimes (i (-> *music-flava-name-list* mus-idx length))
    (if (nonzero? (-> *music-flava-name-list* mus-idx i)) (add-flava-player-option (-> *music-flava-name-list* mus-idx i) i)))
  (add-back-option)
  *temp-options*)

(defmacro add-monitor-option (text-id display-id)
  "add a display button to *temp-options* with specified size"
  `(let ((option (-> *temp-options* (length *temp-options*)))
        (name (pc-get-display-name ,display-id *pc-cpp-temp-string*)))
    (set! (-> option option-type) (game-option-type monitor))
    ;; Use the name of the display if it's available
    (if name (copy-string<-string (-> option name-override) *pc-cpp-temp-string*) (set! (-> option name) ,text-id))
    (set! (-> option param1) (the float ,display-id))
    (set! (-> option scale) #t)
    (1+! (-> *temp-options* length))))

(defun build-monitor-options ()
  (set! (-> *temp-options* length) 0)
  (dotimes (i (pc-get-display-count))
    (add-monitor-option (text-id display-fmt) i))
  (add-back-option)
  *temp-options*)

(defmacro add-controller-option (text-id controller-id)
  "add a controller option to *temp-options* with specified size"
  `(let ((option (-> *temp-options* (length *temp-options*)))
        (name (pc-get-controller-name ,controller-id *pc-cpp-temp-string*)))
    (set! (-> option option-type) (game-option-type controller))
    ;; Use the name of the controller if it's available
    (if name (copy-string<-string (-> option name-override) *pc-cpp-temp-string*) (set! (-> option name) ,text-id))
    (set! (-> option param1) (the float ,controller-id))
    (set! (-> option scale) #t)
    (1+! (-> *temp-options* length))))

(defun build-controller-options ()
  "Dynamically creates menu options for selecting your controller.
   Will attempt to use the name of the controller, but will fallback to a generic number otherwise"
  (set! (-> *temp-options* length) 0)
  (dotimes (i (pc-get-controller-count))
    (add-controller-option (text-id input-opts-generic-controller) i))
  (add-back-option)
  *temp-options*)

(define *analog-bind-names*
  (new 'static
       'boxed-array
       :type
       string
       "L ANALOG LEFT"
       "L ANALOG RIGHT"
       "L ANALOG UP"
       "L ANALOG DOWN"
       "R ANALOG LEFT"
       "R ANALOG RIGHT"
       "R ANALOG UP"
       "R ANALOG DOWN"))

(define *button-bind-names*
  (new 'static
       'boxed-array
       :type
       string
       "SELECT"
       "L3"
       "R3"
       "START"
       "DPAD UP"
       "DPAD RIGHT"
       "DPAD DOWN"
       "DPAD LEFT"
       "L2"
       "R2"
       "L1"
       "R1"
       "TRIANGLE"
       "CIRCLE"
       "CROSS"
       "SQUARE"))

;; TODO - make a common function for these dynamic menus instead of the duplication / macros
(defun build-binding-reassignment-options ((screen progress-screen))
  "Dynamically creates menu options for reassigning bindings for controllers/mice/keyboard"
  (set! (-> *temp-options* length) 0)
  ;; initialize binding options
  ;; for controllers and mice, dont allow analog rebindings
  ;; for keyboards, allow analog binding
  (when (= screen (progress-screen keyboard-binds))
    ;; Add analog bindings
    (dotimes (i 4)
      ;; two for each direction (low/high)
      (let ((option (-> *temp-options* (length *temp-options*))))
        (set! (-> option bind-info port) 0)
        ;; only for keyboards
        (set! (-> option bind-info device-type) 1)
        (set! (-> option bind-info for-buttons?) #f)
        (set! (-> option bind-info input-idx) i)
        (set! (-> option bind-info analog-min-range?) #t)
        (set! (-> option option-type) (game-option-type binding-assignment))
        (set! (-> option name-override) (copy-string<-string (-> option name-override) (-> *analog-bind-names* (* i 2))))
        (set! (-> option scale) #t)
        (1+! (-> *temp-options* length)))
      (let ((option (-> *temp-options* (length *temp-options*))))
        (set! (-> option bind-info port) 0)
        ;; only for keyboards
        (set! (-> option bind-info device-type) 1)
        (set! (-> option bind-info for-buttons?) #f)
        (set! (-> option bind-info input-idx) i)
        (set! (-> option bind-info analog-min-range?) #f)
        (set! (-> option option-type) (game-option-type binding-assignment))
        (set! (-> option name-override) (copy-string<-string (-> option name-override) (-> *analog-bind-names* (+ 1 (* i 2)))))
        (set! (-> option scale) #t)
        (1+! (-> *temp-options* length)))))
  ;; Add Button bindings
  (dotimes (i 16)
    (let ((option (-> *temp-options* (length *temp-options*))))
      (set! (-> option bind-info port) 0)
      (case screen
        (((progress-screen controller-binds)) (set! (-> option bind-info device-type) 0))
        (((progress-screen keyboard-binds)) (set! (-> option bind-info device-type) 1))
        (((progress-screen mouse-binds)) (set! (-> option bind-info device-type) 2)))
      (set! (-> option bind-info for-buttons?) #t)
      (set! (-> option bind-info input-idx) i)
      (set! (-> option bind-info analog-min-range?) #f)
      (set! (-> option option-type) (game-option-type binding-assignment))
      (set! (-> option name-override) (copy-string<-string (-> option name-override) (-> *button-bind-names* i)))
      (set! (-> option scale) #t)
      (1+! (-> *temp-options* length))))
  ;; We add a restore defaults button at the bottom of each binding menu
  (let ((option (-> *temp-options* (length *temp-options*))))
    (set! (-> option option-type) (game-option-type confirmation))
    (set! (-> option name) (text-id restore-defaults))
    (set! (-> option scale) #t)
    ;; No closures :(
    (case screen
      ;; NOTE - assumes only port 0
      (((progress-screen controller-binds))
       (set! (-> option on-confirm) (lambda () (pc-reset-bindings-to-defaults! 0 0) (none))))
      (((progress-screen keyboard-binds))
       (set! (-> option on-confirm) (lambda () (pc-reset-bindings-to-defaults! 0 1) (none))))
      (((progress-screen mouse-binds)) (set! (-> option on-confirm) (lambda () (pc-reset-bindings-to-defaults! 0 2) (none)))))
    (1+! (-> *temp-options* length)))
  (add-back-option)
  *temp-options*)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  CAROUSELL FUNCTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun get-language-name ((lang pc-language))
  "get the text-id from a lang"
  (dolist (item *language-remap-info-pc*)
    (when (= lang (the pc-language (/ (the int (cadr (car item))) 8)))
      (return (the text-id (/ (the int (car (car item))) 8)))))
  (text-id zero))

(defun print-string-in-carousell ((arg0 text-id) (arg1 font-context) (arg2 int) (arg3 symbol))
  (let ((s5-0 (if arg3 arg2 (- arg2))))
    (+! (-> arg1 origin x) (the float s5-0))
    (let ((f30-0 (- 1.0 (* 0.0033333334 (the float arg2)))))
      (print-game-text-scaled (lookup-text! *common-text* arg0 #f) f30-0 arg1 (the int (* 128.0 f30-0))))
    (set! (-> arg1 origin x) (- (-> arg1 origin x) (the float s5-0))))
  (set! (-> arg1 color) (font-color default))
  arg1)

(defun progress-draw-carousell-from-string-list ((options (array text-id)) (font font-context) (y-off int) (new-val int))
  "yep."
  (let ((old-lang (-> *progress-carousell* selection))
        (new-lang new-val)
        (max-lang (length options)))
    (if (-> *progress-carousell* transition)
      (seekl! (-> *progress-carousell* x-offset) 200 (the int (* 10.0 (-> *display* time-adjust-ratio)))))
    (when (>= (-> *progress-carousell* x-offset) 100)
      (set! (-> *progress-carousell* selection) new-lang)
      (set! old-lang new-lang)
      (set! (-> *progress-carousell* transition) #f)
      (set! (-> *progress-carousell* x-offset) 0))
    (set! (-> font origin y) (the float (+ y-off 3)))
    (set-color! font (font-color progress-blue))
    0
    (let ((next-lang (mod (+ old-lang 1) max-lang))
          (prev-lang (mod (+ max-lang -1 old-lang) max-lang))
          ;; these are used during the transition since it technically allows you to see 4 langs at once.
          (next2-lang (mod (+ old-lang 2) max-lang))
          (prev2-lang (mod (+ max-lang -2 old-lang) max-lang)))
      (cond
        ((-> *progress-carousell* direction)
         (let ((a2-22 (- 200 (+ (-> *progress-carousell* x-offset) 100))))
           (print-string-in-carousell (-> options prev-lang) font a2-22 #f))
         (set-color! font (font-color progress-blue))
         (let ((a2-23 (+ (-> *progress-carousell* x-offset) 100)))
           (cond
             ((< a2-23 150) (print-string-in-carousell (-> options next-lang) font a2-23 #t))
             (else
              (let ((a2-24 (- 200 (-> *progress-carousell* x-offset))))
                (print-string-in-carousell (-> options prev2-lang) font a2-24 #f))))))
        (else
         (let ((a2-25 (+ (-> *progress-carousell* x-offset) 100)))
           (cond
             ((< a2-25 150) (print-string-in-carousell (-> options prev-lang) font a2-25 #f))
             (else
              (let ((a2-26 (- 200 (-> *progress-carousell* x-offset))))
                (print-string-in-carousell (-> options next2-lang) font a2-26 #t)))))
         (set-color! font (font-color progress-blue))
         (let ((a2-27 (- 200 (+ (-> *progress-carousell* x-offset) 100))))
           (print-string-in-carousell (-> options next-lang) font a2-27 #t)))))
    (if (not (-> *progress-carousell* transition)) (set-color! font (font-color progress-selected)))
    (print-string-in-carousell (-> options old-lang)
                               font
                               (-> *progress-carousell* x-offset)
                               (-> *progress-carousell* direction))))

;;---------------------------
;;---------------------------
;; function overrides

;; NOTE - the following is generated from ./scripts/gsrc/jak1-sprite-adjust.py
;; They are polynomials fit with manually adjusted values for all common aspect ratios
;; so they can make a decent approximation interpolating between

(defun pc-sprite-adjust-left ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 20.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 4.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) -6.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) -10.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) -14.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) -16.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) -25.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) -26.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) -37.0)
    (else
     (+ 121.37227965381875
        (* -145.13874571806738 aspect-ratio)
        (* 48.695824268929236 aspect-ratio aspect-ratio)
        (* -5.737940185600177 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-right ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) -33.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) -6.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 12.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 18.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 26.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 29.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 45.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 46.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 65.0)
    (else
     (+ -205.4294994422315
        (* 245.07283711809043 aspect-ratio)
        (* -81.04347133064093 aspect-ratio aspect-ratio)
        (* 9.423126530695802 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-cross-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) -20.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) -5.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 5.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 8.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 17.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 30.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 30.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 45.0)
    (else
     (+ -98.75041386103345
        (* 104.4004528706783 aspect-ratio)
        (* -27.27525895954647 aspect-ratio aspect-ratio)
        (* 2.6112419651912617 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-cross-y ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) -20.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 5.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 20.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 20.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 25.0)
    (else
     (+ -92.55990871923132
        (* 104.71302956908977 aspect-ratio)
        (* -32.46993312399963 aspect-ratio aspect-ratio)
        (* 3.4672700107735115 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-square-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) -5.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 5.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 5.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 8.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 8.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 17.0)
    (else
     (+ -22.034620779370684
        (* 15.66980500404166 aspect-ratio)
        (* 2.6395010789749374 aspect-ratio aspect-ratio)
        (* -1.1122480459486384 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-square-y ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) -10.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 15.0)
    (else
     (+ -35.887413062108386
        (* 28.768844269882614 aspect-ratio)
        (* -0.854904339304392 aspect-ratio aspect-ratio)
        (* -0.9020647898186882 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-triangle-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 2.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 2.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 3.0)
    (else
     (+ 9.074933988821043
        (* -16.012653345486786 aspect-ratio)
        (* 8.413647131791468 aspect-ratio aspect-ratio)
        (* -1.2341491172835792 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-triangle-y ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 5.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 5.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 5.0)
    (else
     (+ 24.319264777517994
        (* -43.26989970602368 aspect-ratio)
        (* 23.07604741978599 aspect-ratio aspect-ratio)
        (* -3.494974989657488 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-circle-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 2.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 2.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 5.0)
    (else
     (+ 7.769390144448719
        (* -13.422040271641388 aspect-ratio)
        (* 6.780103459545601 aspect-ratio aspect-ratio)
        (* -0.9064673601247457 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-circle-y ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) -7.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) -7.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) -10.0)
    (else
     (+ -32.088654921966715
        (* 56.69193997766507 aspect-ratio)
        (* -29.856150879331594 aspect-ratio aspect-ratio)
        (* 4.401442349782235 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-percent-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 12.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 12.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 18.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 18.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 25.0)
    (else
     (+ -46.209527361482614
        (* 59.19334582683605 aspect-ratio)
        (* -17.978708435875035 aspect-ratio aspect-ratio)
        (* 1.9546083242648873 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-autosave-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 10.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 15.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 18.0)
    (else
     (+ -25.454818080453748
        (* 28.58764000115764 aspect-ratio)
        (* -5.122397663089315 aspect-ratio aspect-ratio)
        (* 0.1450488213340339 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-orb-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 5.0)
    (else
     (+ -3.26385961093079
        (* 6.476532684613467 aspect-ratio)
        (* -4.083859180614655 aspect-ratio aspect-ratio)
        (* 0.819204392897082 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-orb-glow-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 4.0)
    (else
     (+ -2.6110876887446324
        (* 5.181226147690773 aspect-ratio)
        (* -3.267087344491723 aspect-ratio aspect-ratio)
        (* 0.6553635143176653 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-orb-text-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 10.0)
    (else
     (+ -6.52771922186158
        (* 12.953065369226934 aspect-ratio)
        (* -8.16771836122931 aspect-ratio aspect-ratio)
        (* 1.638408785794164 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-cell-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) -10.0)
    (else
     (+ 6.52771922186158
        (* -12.953065369226934 aspect-ratio)
        (* 8.16771836122931 aspect-ratio aspect-ratio)
        (* -1.638408785794164 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-cell-text-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 2.0)
    (else
     (+ -1.3055438443723162
        (* 2.5906130738453865 aspect-ratio)
        (* -1.6335436722458616 aspect-ratio aspect-ratio)
        (* 0.32768175715883263 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-buzzer-text-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) -5.0)
    (else
     (+ 3.26385961093079
        (* -6.476532684613467 aspect-ratio)
        (* 4.083859180614655 aspect-ratio aspect-ratio)
        (* -0.819204392897082 aspect-ratio aspect-ratio aspect-ratio)))))

(defun pc-sprite-adjust-options-text-x ((aspect-ratio float))
  (cond
    ((fequal-epsilon? aspect-ratio 1.0 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.25 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.5 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.6 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.78 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 1.85 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.33 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 2.35 0.01) 0.0)
    ((fequal-epsilon? aspect-ratio 3.56 0.01) 3.0)
    (else
     (+ -1.9583157665584714
        (* 3.885919610768078 aspect-ratio)
        (* -2.450315508368792 aspect-ratio aspect-ratio)
        (* 0.491522635738249 aspect-ratio aspect-ratio aspect-ratio)))))

(define *PC-CROSS-X-ADJUST* 0.0)

(define *PC-CROSS-Y-ADJUST* 0.0)

(define *PC-SQUARE-X-ADJUST* 0.0)

(define *PC-SQUARE-Y-ADJUST* 0.0)

(define *PC-TRIANGLE-X-ADJUST* 0.0)

(define *PC-TRIANGLE-Y-ADJUST* 0.0)

(define *PC-CIRCLE-X-ADJUST* 0.0)

(define *PC-CIRCLE-Y-ADJUST* 0.0)

(define *PC-PERCENT-X-ADJUST* 0.0)

(define *PC-AUTOSAVE-X-ADJUST* 0.0)

(define *PC-ORB-X-ADJUST* 0.0)

(define *PC-ORB-GLOW-X-ADJUST* 0.0)

(define *PC-ORB-TEXT-X-ADJUST* 0.0)

(define *PC-CELL-X-ADJUST* 0.0)

(define *PC-CELL-TEXT-X-ADJUST* 0.0)

(define *PC-BUZZER-TEXT-X-ADJUST* 0.0)

(define *PC-OPTIONS-TEXT-X-ADJUST* 0.0)

(defmethod adjust-ratios ((this progress) (aspect symbol) (video-mode symbol))
  (case aspect
    (('aspect4x3)
     (set! (-> this sides-x-scale) 1.0)
     (set! (-> this sides-y-scale) 13.0)
     (set! (-> this left-x-offset) 0)
     (set! (-> this right-x-offset) 0)
     (set! (-> this button-scale) 1.0)
     (set! (-> this slot-scale) 8192.0)
     (set! (-> this small-orb-y-offset) 58)
     (set! (-> this icons 5 scale-x) 0.008)
     (set! (-> this icons 5 scale-y) -0.009)
     (set! (-> this big-orb-y-offset) 243)
     (set! (-> this icons 4 scale-x) 0.013)
     (set! (-> this icons 4 scale-y) -0.015))
    (('aspect16x9)
     (set! (-> this sides-x-scale) 1.2)
     (set! (-> this sides-y-scale) 9.8)
     (set! (-> this left-x-offset) -10)
     (set! (-> this right-x-offset) 17)
     (set! (-> this button-scale) 1.05)
     (set! (-> this slot-scale) 6144.0)
     (set! (-> this small-orb-y-offset) 59)
     (set! (-> this icons 5 scale-x) 0.008)
     (set! (-> this icons 5 scale-y) -0.0098)
     (set! (-> this big-orb-y-offset) 255)
     (set! (-> this icons 4 scale-x) 0.017)
     (set! (-> this icons 4 scale-y) -0.0205)))
  (when (not (-> *pc-settings* use-vis?))
    (format 0 "[PC] Adjusting sprite positioning, aspect-ratio: ~f~%" (-> *pc-settings* aspect-ratio))
    (set! (-> this left-x-offset) (the int (pc-sprite-adjust-left (-> *pc-settings* aspect-ratio))))
    (set! (-> this right-x-offset) (the int (pc-sprite-adjust-right (-> *pc-settings* aspect-ratio))))
    (set! *PC-CROSS-X-ADJUST* (pc-sprite-adjust-cross-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-CROSS-Y-ADJUST* (pc-sprite-adjust-cross-y (-> *pc-settings* aspect-ratio)))
    (set! *PC-SQUARE-X-ADJUST* (pc-sprite-adjust-square-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-SQUARE-Y-ADJUST* (pc-sprite-adjust-square-y (-> *pc-settings* aspect-ratio)))
    (set! *PC-TRIANGLE-X-ADJUST* (pc-sprite-adjust-triangle-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-TRIANGLE-Y-ADJUST* (pc-sprite-adjust-triangle-y (-> *pc-settings* aspect-ratio)))
    (set! *PC-CIRCLE-X-ADJUST* (pc-sprite-adjust-circle-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-CIRCLE-Y-ADJUST* (pc-sprite-adjust-circle-y (-> *pc-settings* aspect-ratio)))
    (set! *PC-PERCENT-X-ADJUST* (pc-sprite-adjust-percent-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-AUTOSAVE-X-ADJUST* (pc-sprite-adjust-autosave-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-ORB-X-ADJUST* (pc-sprite-adjust-orb-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-ORB-GLOW-X-ADJUST* (pc-sprite-adjust-orb-glow-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-ORB-TEXT-X-ADJUST* (pc-sprite-adjust-orb-text-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-CELL-X-ADJUST* (pc-sprite-adjust-cell-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-CELL-TEXT-X-ADJUST* (pc-sprite-adjust-cell-text-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-BUZZER-TEXT-X-ADJUST* (pc-sprite-adjust-buzzer-text-x (-> *pc-settings* aspect-ratio)))
    (set! *PC-OPTIONS-TEXT-X-ADJUST* (pc-sprite-adjust-options-text-x (-> *pc-settings* aspect-ratio))))
  (when (= video-mode 'pal)
    (set! (-> this icons 5 scale-y) (* 1.15 (-> this icons 5 scale-y)))
    (set! (-> this icons 4 scale-x) (* 1.05 (-> this icons 4 scale-x)))
    (set! (-> this icons 4 scale-y) (* (-> this icons 4 scale-y) (the-as float (if (= aspect 'aspect16x9) 1.18 1.15))))
    (+! (-> this big-orb-y-offset) (if (= aspect 'aspect16x9) 3 2)))
  0
  (none))

(defmethod draw-progress ((this progress))
  (let ((f30-0 (+ -409.0 (-> this particles 2 init-pos x) (* 0.8 (the float (-> this left-x-offset)))))
        (s5-0 (if (or (-> this stat-transition) (nonzero? (-> this level-transition))) 0 (-> this transition-offset))))
    (let ((f28-0 (if (or (-> this stat-transition) (nonzero? (-> this level-transition))) 1.0 (-> this transition-percentage-invert))))
      (let* ((s3-0 (-> *display* frames (-> *display* on-screen) frame global-buf))
             (s4-0 (-> s3-0 base)))
        (let ((s2-0 draw-string-xy))
          (format (clear *temp-string*) "~D" (the int (+ 0.5 (-> *target* game money))))
          (s2-0 *temp-string*
                s3-0
                (the int (+ *PC-ORB-TEXT-X-ADJUST* 428.0 (the float s5-0) f30-0))
                (- 12 (the int (* 0.16666667 f30-0)))
                (font-color default)
                (font-flags shadow kerning large)))
        (let ((s2-1 draw-string-xy))
          (format (clear *temp-string*) "~D" (the int (+ 0.5 (-> *target* game fuel))))
          (s2-1 *temp-string*
                s3-0
                (the int (+ *PC-CELL-TEXT-X-ADJUST* 456.0 (the float (adjust-pos s5-0 50)) f30-0))
                (- 48 (the int (* 0.125 f30-0)))
                (font-color default)
                (font-flags shadow kerning large)))
        (let ((s2-2 draw-string-xy))
          (format (clear *temp-string*) "~D" (the int (+ 0.5 (-> *target* fact buzzer))))
          (s2-2 *temp-string*
                s3-0
                (the int (+ *PC-BUZZER-TEXT-X-ADJUST* 469.0 (the float (adjust-pos s5-0 100)) f30-0))
                89
                (font-color default)
                (font-flags shadow kerning large)))
        (let ((a3-4 (-> s3-0 base)))
          (let ((v1-20 (the-as object (-> s3-0 base))))
            (set! (-> (the-as dma-packet v1-20) dma) (new 'static 'dma-tag :id (dma-tag-id next)))
            (set! (-> (the-as dma-packet v1-20) vif0) (new 'static 'vif-tag))
            (set! (-> (the-as dma-packet v1-20) vif1) (new 'static 'vif-tag))
            (set! (-> s3-0 base) (&+ (the-as pointer v1-20) 16)))
          (dma-bucket-insert-tag (-> *display* frames (-> *display* on-screen) frame bucket-group)
                                 (bucket-id debug)
                                 s4-0
                                 (the-as (pointer dma-tag) a3-4))))
      (let ((s4-2 (new 'stack
                       'font-context
                       *font-default-matrix*
                       (the int
                            (+ *PC-OPTIONS-TEXT-X-ADJUST*
                               (- 423.0 (the float (/ (-> this left-x-offset) 2)))
                               f30-0
                               (the float (adjust-pos s5-0 150))))
                       131
                       0.0
                       (font-color default)
                       (font-flags shadow kerning))))
        (let ((v1-29 s4-2)) (set! (-> v1-29 width) (the float 100)))
        (let ((v1-30 s4-2)) (set! (-> v1-30 height) (the float 15)))
        (let ((v1-31 s4-2)) (set! (-> v1-31 scale) 0.5))
        (set! (-> s4-2 flags) (font-flags shadow kerning large))
        (print-game-text (lookup-text! *common-text* (text-id options) #f) s4-2 #f 128 22)
        (let ((v1-34 s4-2)) (set! (-> v1-34 width) (the float 160)))
        (let ((v1-35 s4-2)) (set! (-> v1-35 height) (the float 22)))
        (let ((v1-36 s4-2)) (set! (-> v1-36 scale) 1.3))
        (let ((a0-31 s4-2)) (set! (-> a0-31 color) (font-color progress-percent)))
        (set! (-> s4-2 origin x)
              (+ *PC-PERCENT-X-ADJUST*
                 (- 435.0 (the float (if (< (-> *progress-process* 0 completion-percentage) 10.0) 93 80)))
                 f30-0))
        (set! (-> s4-2 origin y) 180.0)
        (set! (-> s4-2 flags) (font-flags shadow kerning middle middle-vert large))
        (let ((s3-3 print-game-text))
          (format (clear *temp-string*) "~2D%" (the int (-> *progress-process* 0 completion-percentage)))
          (s3-3 *temp-string* s4-2 #f (the int (* 128.0 f28-0)) 22))))
    0.0
    (let ((f28-1 (+ -94.0 (-> this particles 2 init-pos x)))
          (s3-4 90)
          (s4-3 224)
          (s2-5 (/ s5-0 5))
          (f26-3 (-> this button-scale)))
      ;; CROSS
      (let ((f24-0 (* 182.04445 (- (/ -36.0 f26-3) (the float s2-5)))))
        (set! (-> this particles 27 init-pos x) (+ *PC-CROSS-X-ADJUST* (the float (+ s3-4 (the int (* f28-1 (cos f24-0)))))))
        (set! (-> this particles 27 init-pos y) (+ *PC-CROSS-Y-ADJUST* (the float (+ s4-3 (the int (* f28-1 (sin f24-0))))))))
      ;; SQUARE
      (let ((f24-2 (* 182.04445 (- (/ -21.0 f26-3) (the float (adjust-pos s2-5 10))))))
        (set! (-> this particles 28 init-pos x) (+ *PC-SQUARE-X-ADJUST* (the float (+ s3-4 (the int (* f28-1 (cos f24-2)))))))
        (set! (-> this particles 28 init-pos y) (+ *PC-SQUARE-Y-ADJUST* (the float (+ s4-3 (the int (* f28-1 (sin f24-2))))))))
      ;; TRIANGLE
      (let ((f24-4 (* 182.04445 (- (/ -6.0 f26-3) (the float (adjust-pos s2-5 15))))))
        (set! (-> this particles 29 init-pos x) (+ *PC-TRIANGLE-X-ADJUST* (the float (+ s3-4 (the int (* f28-1 (cos f24-4)))))))
        (set! (-> this particles 29 init-pos y) (+ *PC-TRIANGLE-Y-ADJUST* (the float (+ s4-3 (the int (* f28-1 (sin f24-4))))))))
      ;; CIRCLE
      (let ((f26-5 (* 182.04445 (- (/ 9.0 f26-3) (the float (adjust-pos s2-5 20))))))
        (set! (-> this particles 30 init-pos x) (+ *PC-CIRCLE-X-ADJUST* (the float (+ s3-4 (the int (* f28-1 (cos f26-5)))))))
        (set! (-> this particles 30 init-pos y) (+ *PC-CIRCLE-Y-ADJUST* (the float (+ s4-3 (the int (* f28-1 (sin f26-5)))))))))
    (when *cheat-mode*
      (let ((a0-46 "AUTO SAVE OFF"))
        (if (-> *setting-control* current auto-save) (set! a0-46 "AUTO SAVE ON"))
        (let* ((s3-5 (-> *display* frames (-> *display* on-screen) frame global-buf))
               (s4-4 (-> s3-5 base)))
          (draw-string-xy a0-46
                          s3-5
                          (the int (+ *PC-AUTOSAVE-X-ADJUST* 430.0 f30-0))
                          200
                          (font-color progress-memcard)
                          (font-flags shadow kerning middle))
          (let ((a3-9 (-> s3-5 base)))
            (let ((v1-81 (the-as object (-> s3-5 base))))
              (set! (-> (the-as dma-packet v1-81) dma) (new 'static 'dma-tag :id (dma-tag-id next)))
              (set! (-> (the-as dma-packet v1-81) vif0) (new 'static 'vif-tag))
              (set! (-> (the-as dma-packet v1-81) vif1) (new 'static 'vif-tag))
              (set! (-> s3-5 base) (&+ (the-as pointer v1-81) 16)))
            (dma-bucket-insert-tag (-> *display* frames (-> *display* on-screen) frame bucket-group)
                                   (bucket-id debug)
                                   s4-4
                                   (the-as (pointer dma-tag) a3-9))))))
    (let ((a0-52 (-> this icons 5 icon 0 root)))
      (set-yaw-angle-clear-roll-pitch! a0-52 (- (y-angle a0-52) (* 182.04445 (* 4.0 (-> *display* time-adjust-ratio))))))
    (let* ((f28-2 (* 0.00024414062 (the float (-> *progress-process* 0 in-out-position))))
           (f30-1 (* 300.0 f28-2)))
      (set! (-> this particles 18 init-pos x)
            (+ *PC-ORB-GLOW-X-ADJUST*
               (the float (+ (the int (the float (adjust-pos s5-0 50))) 394 (the int f30-1) (-> this right-x-offset)))))
      (set! (-> this particles 18 init-pos y) (the float (- 40 (the int (* 80.0 f28-2)))))
      (set! (-> this icons 5 icon-x)
            (+ (the int *PC-ORB-X-ADJUST*) (the int (the float (adjust-pos s5-0 50))) 393 (the int f30-1) (-> this right-x-offset)))
      (set! (-> this icons 5 icon-y) (- (-> this small-orb-y-offset) (the int (* 80.0 f28-2))))
      (set! (-> this particles 16 init-pos x)
            (+ *PC-CELL-X-ADJUST*
               (the float (+ (the int (the float (adjust-pos s5-0 100))) 425 (the int f30-1) (-> this right-x-offset)))))
      (set! (-> this particles 16 init-pos y) (the float (- 112 (the int (* 60.0 f28-2)))))
      (set! (-> this particles 17 init-pos x)
            (the float
                 (+ (the int (the float (adjust-pos s5-0 150)))
                    442
                    (the int f30-1)
                    (the int (* 0.7 (the float (-> this right-x-offset)))))))))
  (set! (-> this particles 17 init-pos y) 193.0)
  0
  (none))

(defun init-game-options ((obj progress))
  "Set the options for all of the menus."
  ;; start off by making them all invalid
  (dotimes (i (progress-screen max))
    (set! (-> *options-remap* i) #f))
  ;; give cheats
  (when (task-closed? (game-task finalboss-movies) (task-status need-reminder))
    (logior! (-> *pc-settings* cheats-known) (pc-cheats sidekick-blue tunes sky big-head small-head big-fist big-head-npc))
    (when (>= (calculate-completion (the progress #f)) 100.0)
      (logior! (-> *pc-settings* cheats-known)
               (pc-cheats invinc eco-blue eco-red eco-green eco-yellow huge-head mirror no-tex))))
  ;; set up options for each screen
  (set! (-> *options-remap* (progress-screen settings))
        (if (or *cheat-mode* (nonzero? (-> *pc-settings* cheats-known))) *main-options-secrets* *main-options-pc*))
  (set! (-> *options-remap* (progress-screen game-settings)) *game-options-pc*)
  (set! (-> *options-remap* (progress-screen graphic-settings)) *graphic-options-pc*)
  (let ((max-refresh-rate (pc-get-active-display-refresh-rate)))
    (cond
      ((> max-refresh-rate 100) (set! *carousell-frame-rate* *carousell-frame-rate-150fps*))
      ((> max-refresh-rate 60) (set! *carousell-frame-rate* *carousell-frame-rate-100fps*))))
  (set! (-> *options-remap* (progress-screen sound-settings)) *sound-options-pc*)
  (set! (-> *options-remap* (progress-screen memcard-no-space)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-not-inserted)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-not-formatted)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-format)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen memcard-data-exists)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen memcard-insert)) *ok-options*)
  (set! (-> *options-remap* (progress-screen load-game)) *load-options*)
  (set! (-> *options-remap* (progress-screen save-game)) *save-options*)
  (set! (-> *options-remap* (progress-screen save-game-title)) *save-options-title*)
  (set! (-> *options-remap* (progress-screen memcard-error-loading)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-error-saving)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-error-formatting)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-error-creating)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-auto-save-error)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-removed)) *ok-options*)
  (set! (-> *options-remap* (progress-screen memcard-no-data)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen title)) *title-pc*)
  (set! (-> *options-remap* (progress-screen settings-title)) *options*)
  (set! (-> *options-remap* (progress-screen auto-save)) *ok-options*)
  (set! (-> *options-remap* (progress-screen pal-change-to-60hz)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen pal-now-60hz)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen no-disc)) *ok-options*)
  (set! (-> *options-remap* (progress-screen bad-disc)) *ok-options*)
  (set! (-> *options-remap* (progress-screen quit)) *yes-no-options*)
  ;; our screens!
  (set! (-> *options-remap* (progress-screen aspect-msg)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen input-options)) *input-options*)
  (set! (-> *options-remap* (progress-screen camera-options)) *camera-options*)
  (set! (-> *options-remap* (progress-screen controller-options)) *controller-options*)
  (set! (-> *options-remap* (progress-screen mouse-options)) *mouse-options*)
  (set! (-> *options-remap* (progress-screen reassign-binds-options)) *reassign-binds-options*)
  (set! (-> *options-remap* (progress-screen select-controller)) *temp-options*)
  (set! (-> *options-remap* (progress-screen controller-binds)) *temp-options*)
  (set! (-> *options-remap* (progress-screen keyboard-binds)) *temp-options*)
  (set! (-> *options-remap* (progress-screen mouse-binds)) *temp-options*)
  (set! (-> *options-remap* (progress-screen misc-options)) *misc-options*)
  (set! (-> *options-remap* (progress-screen gfx-ps2-options)) *gfx-ps2-options*)
  (set! (-> *options-remap* (progress-screen resolution)) *temp-options*)
  (set! (-> *options-remap* (progress-screen aspect-ratio)) *aspect-ratio-options*)
  (set! (-> *options-remap* (progress-screen quit-title)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen secrets)) *secrets-title*)
  (set! (-> *options-remap* (progress-screen cheats)) *cheats*)
  (set! (-> *options-remap* (progress-screen checkpoint-select)) *checkpoint-select-options*)
  (set! (-> *options-remap* (progress-screen music-player)) *music-player-options*)
  (set! (-> *options-remap* (progress-screen flava-player)) *temp-options*)
  (set! (-> *options-remap* (progress-screen memcard-disable-auto-save)) *yes-no-options*)
  (set! (-> *options-remap* (progress-screen memcard-auto-save-disabled)) *ok-options*)
  (set! (-> *options-remap* (progress-screen monitor)) *temp-options*)
  (set! (-> *options-remap* (progress-screen speedrun-options)) *speedrun-options*)
  (set! (-> *options-remap* (progress-screen speedrun-il-options)) *speedrun-il-options*)
  (set! (-> *options-remap* (progress-screen speedrun-cat-ext-options)) *speedrun-cat-ext-options*)
  ;; set default params
  (set! (-> *progress-state* aspect-ratio-choice) (get-aspect-ratio))
  (set! (-> *progress-state* video-mode-choice) (get-video-mode))
  (set! (-> *progress-state* yes-no-choice) #f)
  ;; set variable pointers
  (set! (-> *game-options* 1 value-to-modify) (&-> *pc-settings* memcard-play-hints?))
  (set! (-> *game-options* 2 value-to-modify) (&-> *setting-control* default language))
  (set! (-> *game-options-japan* 0 value-to-modify) (&-> *pc-settings* memcard-vibration?))
  (set! (-> *game-options-japan* 1 value-to-modify) (&-> *pc-settings* memcard-play-hints?))
  (set! (-> *game-options-demo* 0 value-to-modify) (&-> *pc-settings* memcard-vibration?))
  (set! (-> *game-options-demo* 1 value-to-modify) (&-> *pc-settings* memcard-play-hints?))
  (set! (-> *graphic-options* 1 value-to-modify) (&-> *progress-state* aspect-ratio-choice))
  (set! (-> *sound-options* 0 value-to-modify) (&-> *setting-control* default sfx-volume))
  (set! (-> *sound-options* 1 value-to-modify) (&-> *setting-control* default music-volume))
  (set! (-> *sound-options* 2 value-to-modify) (&-> *setting-control* default dialog-volume))
  (set! (-> *yes-no-options* 0 value-to-modify) (&-> *progress-state* yes-no-choice))
  ;; our options!
  (set! (-> *game-options-pc* 1 value-to-modify) (&-> *pc-settings* memcard-play-hints?))
  (set! (-> *game-options-pc* 2 value-to-modify) (&-> *pc-settings* subtitles?))
  (set! (-> *game-options-pc* 3 value-to-modify) (&-> *pc-settings* hinttitles?))
  (set! (-> *game-options-pc* 4 value-to-modify) (&-> *setting-control* default language))
  (set! (-> *game-options-pc* 5 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *game-options-pc* 6 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *game-options-pc* 7 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *graphic-options-pc* 1 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *graphic-options-pc* 3 value-to-modify) (&-> *pc-settings* vsync?))
  (set! (-> *graphic-options-pc* 5 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *graphic-options-pc* 6 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *misc-options* 0 value-to-modify) (&-> *pc-settings* money-starburst?))
  (set! (-> *misc-options* 1 value-to-modify) (&-> *pc-settings* discord-rpc?))
  (set! (-> *misc-options* 2 value-to-modify) (&-> *pc-settings* skip-movies?))
  (set! (-> *misc-options* 3 value-to-modify) (&-> *pc-settings* speedrunner-mode?))
  (set! (-> *camera-options* 0 value-to-modify) (&-> *pc-settings* first-camera-h-inverted?))
  (set! (-> *camera-options* 1 value-to-modify) (&-> *pc-settings* first-camera-v-inverted?))
  (set! (-> *camera-options* 2 value-to-modify) (&-> *pc-settings* third-camera-h-inverted?))
  (set! (-> *camera-options* 3 value-to-modify) (&-> *pc-settings* third-camera-v-inverted?))
  ;; input options
  (set! (-> *controller-options* 1 value-to-modify) (&-> *pc-settings* memcard-vibration?))
  (set! (-> *controller-options* 2 value-to-modify) (&-> *pc-settings* stick-deadzone))
  (set! (-> *controller-options* 3 value-to-modify) (&-> *pc-settings* ignore-controller-win-unfocused?))
  (set! (-> *controller-options* 4 value-to-modify) (&-> *pc-settings* controller-led-hp?))
  (set! (-> *controller-options* 5 value-to-modify) (&-> *pc-settings* controller-led-eco?))
  (set! (-> *controller-options* 6 value-to-modify) (&-> *pc-settings* controller-led-heat?))
  (set! (-> *mouse-options* 0 value-to-modify) (&-> *pc-settings* mouse-camera?))
  (set! (-> *mouse-options* 1 value-to-modify) (&-> *pc-settings* mouse-xsens))
  (set! (-> *mouse-options* 2 value-to-modify) (&-> *pc-settings* mouse-ysens))
  (set! (-> *mouse-options* 3 value-to-modify) (&-> *pc-settings* mouse-movement?))
  (set! (-> *input-options* 2 value-to-modify) (&-> *pc-settings* keyboard-enabled?))
  (set! (-> *input-options* 3 value-to-modify) (&-> *pc-settings* mouse-enabled?))
  (set! (-> *input-options* 5 value-to-modify) (&-> *pc-settings* auto-hide-cursor?))
  (set! (-> *gfx-ps2-options* 0 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *gfx-ps2-options* 1 value-to-modify) (&-> *progress-carousell* int-backup))
  (set! (-> *gfx-ps2-options* 2 value-to-modify) (&-> *pc-settings* ps2-parts?))
  (set! (-> *gfx-ps2-options* 3 value-to-modify) (&-> *pc-settings* force-envmap?))
  (set! (-> *gfx-ps2-options* 4 value-to-modify) (&-> *pc-settings* ps2-actor-vis?))
  (set! (-> *sound-options-pc* 0 value-to-modify) (&-> *pc-settings* memcard-volume-sfx))
  (set! (-> *sound-options-pc* 1 value-to-modify) (&-> *pc-settings* memcard-volume-music))
  (set! (-> *sound-options-pc* 2 value-to-modify) (&-> *pc-settings* memcard-volume-dialog))
  (dotimes (i (1- (-> *cheats* length)))
    (set! (-> *cheats* i value-to-modify) (&-> *progress-carousell* symbol-backup)))
  ;(set! (-> *sound-options-pc* 3 value-to-modify) (&-> *pc-settings* music-fadein?))
  (set! (-> *progress-carousell* aspect-native-choice) (-> *pc-settings* use-vis?))
  ;; scroll stuff!
  (progress-scroll-reset)
  (none))

(defmethod respond-common ((obj progress))
  "common logic for navigating the progress menu.
   this is the overriden version, purged of no longer necessary code and with additional new code."
  ;; read memcard
  (mc-get-slot-info 0 *progress-save-info*)
  (set! (-> obj card-info) *progress-save-info*)
  ;; build custom dynamic menus
  (case (-> obj display-state)
    (((progress-screen resolution))
     ;; TODO infinite scrolling
     (build-resolution-options))
    (((progress-screen flava-player)) (build-flava-player-options *progress-music*))
    (((progress-screen secrets))
     (if (= (-> obj display-state-stack 0) (progress-screen title))
       (set! (-> *options-remap* (-> obj display-state)) *secrets-title*)
       (set! (-> *options-remap* (-> obj display-state)) *secrets*)))
    (((progress-screen monitor)) (build-monitor-options))
    (((progress-screen select-controller)) (build-controller-options))
    (((progress-screen controller-binds) (progress-screen keyboard-binds) (progress-screen mouse-binds))
     (build-binding-reassignment-options (-> obj display-state))))
  ;; run nav code
  (let ((options (-> *options-remap* (-> obj display-state))))
    ;; snap scroll if oob
    (when (> (length options) PROGRESS_PC_PAGE_HEIGHT)
      (set! (-> *progress-scroll* start-index)
            (max (-> *progress-scroll* start-index) (- (-> obj option-index) (+ PROGRESS_PC_PAGE_HEIGHT -2)))))
    (when (and options (not (or (progress-scrolling?) (-> obj in-transition))))
      ;; only respond to inputs when transition is done (and also there's options at all)
      (cond
        ((cpad-pressed? 0 up)
         ;; pressed up
         ;; original code checked hold and then press, because hold can be used during center screen option. which we don't use.
         (when (not (-> obj selected-option))
           (if (!= (length options) 1) (sound-play "cursor-up-down"))
           (set! (-> obj last-option-index-change) (-> *display* real-frame-counter))
           (cond
             ((> (-> obj option-index) 0)
              (1-! (-> obj option-index))
              (when (and (> (length options) PROGRESS_PC_PAGE_HEIGHT) (< (-> obj option-index) *progress-scroll-start*))
                (progress-scroll-up!)))
             (else
              (set! (-> obj option-index) (1- (length options)))
              (set! (-> *progress-scroll* start-index) (max 0 (- (length options) PROGRESS_PC_PAGE_HEIGHT -1)))))))
        ((cpad-pressed? 0 down)
         ;; pressed down.
         (when (not (-> obj selected-option))
           (if (!= (length options) 1) (sound-play "cursor-up-down"))
           (set! (-> obj last-option-index-change) (-> *display* real-frame-counter))
           (cond
             ((< (-> obj option-index) (1- (length options)))
              (1+! (-> obj option-index))
              (when (and (> (length options) PROGRESS_PC_PAGE_HEIGHT) (>= (-> obj option-index) *progress-scroll-end*))
                (progress-scroll-down!)))
             (else (set! (-> obj option-index) 0) (set! (-> *progress-scroll* start-index) 0)))))
        ((cpad-hold? 0 left)
         ;; holding left. sliders use hold.
         (cond
           ((cpad-pressed? 0 left)
            ;; navigate left.
            (when (or (-> obj selected-option) (= (-> options (-> obj option-index) option-type) (game-option-type yes-no)))
              (let ((sound? #f))
                (case (-> options (-> obj option-index) option-type)
                  (((game-option-type on-off)
                    (game-option-type yes-no)
                    (game-option-type normal-inverted)
                    (game-option-type aspect-native)
                    (game-option-type cheat-toggle))
                   ;; pressed left on an on/off yes/no option
                   (when (not (-> (the-as (pointer uint32) (-> options (-> obj option-index) value-to-modify))))
                     ;; it was on 'off' or 'no'
                     (set! sound? #t)
                     ;; vibrate if this toggles vibration.
                     ;; TODO - get this working
                     (when (= (-> options (-> obj option-index) name) (text-id vibrations))
                       (cpad-set-buzz! (-> *cpad-list* cpads 0) 1 255 (seconds 0.3))))
                   ;; if the option has an explicit on-change function, call it
                   (if (nonzero? (-> options (-> obj option-index) on-change))
                     ((-> options (-> obj option-index) on-change) #t)
                     ;; set to yes
                     (set! (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))) #t)))
                  (((game-option-type aspect-ratio))
                   (set! sound? (= (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))) 'aspect16x9))
                   (set! (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))) 'aspect4x3))
                  (((game-option-type language))
                   ;; language selection. if not on first language, go back. if on first language, go to last.
                   (if (> (the-as int (-> (the-as (pointer uint64) (-> options (-> obj option-index) value-to-modify)))) 0)
                     (+! (-> (the-as (pointer uint64) (-> options (-> obj option-index) value-to-modify))) -1)
                     (set! (-> (the-as (pointer int64) (-> options (-> obj option-index) value-to-modify))) 5))
                   ;; language was updated.
                   (set! (-> obj language-transition) #t)
                   (set! (-> obj language-direction) #t)
                   (set! sound? #t))
                  (((game-option-type confirmation))
                   (set! sound? (!= (-> *progress-state* on-off-backup) #t))
                   (set! (-> *progress-state* on-off-backup) #t))
                  (((game-option-type display-mode)
                    (game-option-type msaa)
                    (game-option-type lod-bg)
                    (game-option-type lod-fg)
                    (game-option-type speaker)
                    (game-option-type frame-rate)
                    (game-option-type language-subtitles)
                    (game-option-type language-text))
                   ;; a carousell like language
                   (if (> (-> (the-as (pointer int) (-> options (-> obj option-index) value-to-modify))) 0)
                     (+! (-> (the-as (pointer int) (-> options (-> obj option-index) value-to-modify))) -1)
                     (set! (-> (the-as (pointer int) (-> options (-> obj option-index) value-to-modify)))
                           (1- (length (-> *progress-carousell* current-carousell)))))
                   ;; updated.
                   (set! (-> *progress-carousell* transition) #t)
                   (set! (-> *progress-carousell* direction) #t)
                   (set! sound? #t)))
                (if sound? (sound-play "cursor-l-r")))))
           (else
            ;; holding left
            (when (-> obj selected-option)
              (let ((sound? #f))
                (case (-> options (-> obj option-index) option-type)
                  (((game-option-type slider))
                   ;; slider is selected
                   (cond
                     ((>= (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                          (+ (-> options (-> obj option-index) slider-step-size) (-> options (-> obj option-index) param1)))
                      ;; we're 1 above minimum, so reduce by 1
                      (set! (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                            (- (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                               (-> options (-> obj option-index) slider-step-size)))
                      (set! sound? #t))
                     ((< (-> options (-> obj option-index) param1)
                         ;; not at least 1 above minimum, just set to minimum (why not just use max or something!!)
                         (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify))))
                      (set! (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                            (-> options (-> obj option-index) param1))
                      (set! sound? #t)))))
                ;; play sound
                (when sound?
                  (let ((vol 100.0))
                    (case (-> options (-> obj option-index) name)
                      (((text-id music-volume) (text-id speech-volume))
                       (set! vol (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify))))))
                    (when (< (seconds 0.3) (- (-> *display* real-frame-counter) (-> *progress-state* last-slider-sound)))
                      (set! (-> *progress-state* last-slider-sound) (-> *display* real-frame-counter))
                      (sound-play "slider2001" :vol vol)))))))))
        ((cpad-hold? 0 right)
         ;; holding right
         (cond
           ((cpad-pressed? 0 right)
            ;; pressed right
            (when (or (-> obj selected-option) (= (-> options (-> obj option-index) option-type) (game-option-type yes-no)))
              (let ((sound? #f))
                (case (-> options (-> obj option-index) option-type)
                  (((game-option-type on-off)
                    (game-option-type yes-no)
                    (game-option-type normal-inverted)
                    (game-option-type aspect-native)
                    (game-option-type cheat-toggle))
                   ;; play sound if it was on 'yes' because we're going to 'no' now
                   (set! sound? (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))))
                   ;; if the option has an explicit on-change function, call it
                   (if (nonzero? (-> options (-> obj option-index) on-change))
                     ((-> options (-> obj option-index) on-change) #f)
                     ;; set to no
                     (set! (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))) #f)))
                  (((game-option-type aspect-ratio))
                   ;; same shit different toilet
                   (set! sound? (= (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))) 'aspect4x3))
                   (set! (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))) 'aspect16x9))
                  (((game-option-type language))
                   ;; same thing as before. if at last, go to first. otherwise, keep going forward.
                   (if (< (the-as int (-> (the-as (pointer uint64) (-> options (-> obj option-index) value-to-modify)))) 5)
                     (1+! (-> (the-as (pointer uint64) (-> options (-> obj option-index) value-to-modify))))
                     (set! (-> (the-as (pointer int64) (-> options (-> obj option-index) value-to-modify))) 0))
                   (set! (-> obj language-transition) #t)
                   (set! (-> obj language-direction) #f)
                   (set! sound? #t))
                  (((game-option-type confirmation))
                   (set! sound? (!= (-> *progress-state* on-off-backup) #f))
                   (set! (-> *progress-state* on-off-backup) #f))
                  (((game-option-type display-mode)
                    (game-option-type msaa)
                    (game-option-type lod-bg)
                    (game-option-type lod-fg)
                    (game-option-type speaker)
                    (game-option-type frame-rate)
                    (game-option-type language-subtitles)
                    (game-option-type language-text))
                   ;; same thing as before. if at last, go to first. otherwise, keep going forward.
                   (if (< (-> (the-as (pointer int) (-> options (-> obj option-index) value-to-modify)))
                          (1- (length (-> *progress-carousell* current-carousell))))
                     (1+! (-> (the-as (pointer int) (-> options (-> obj option-index) value-to-modify))))
                     (set! (-> (the-as (pointer int) (-> options (-> obj option-index) value-to-modify))) 0))
                   (set! (-> *progress-carousell* transition) #t)
                   (set! (-> *progress-carousell* direction) #f)
                   (set! sound? #t)))
                ;; play sound if desired
                (if sound? (sound-play "cursor-l-r")))))
           (else
            ;; holding right, but didnt just press it. same slider stuff as before
            (when (-> obj selected-option)
              (let ((sound? #f))
                (case (-> options (-> obj option-index) option-type)
                  (((game-option-type slider))
                   (cond
                     ((>= (- (-> options (-> obj option-index) param2) (-> options (-> obj option-index) slider-step-size))
                          (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify))))
                      (set! (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                            (+ (-> options (-> obj option-index) slider-step-size)
                               (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))))
                      (set! sound? #t))
                     ((< (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                         (-> options (-> obj option-index) param2))
                      (set! (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                            (-> options (-> obj option-index) param2))
                      (set! sound? #t)))))
                (when sound?
                  (let ((vol 100.0))
                    (case (-> options (-> obj option-index) name)
                      (((text-id music-volume) (text-id speech-volume))
                       (set! vol (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify))))))
                    (when (< (seconds 0.3) (- (-> *display* real-frame-counter) (-> *progress-state* last-slider-sound)))
                      (set! (-> *progress-state* last-slider-sound) (-> *display* real-frame-counter))
                      (sound-play "slider2001" :vol vol)))))))))
        ((or (cpad-pressed? 0 square) (cpad-pressed? 0 triangle))
         ;; pressed square or triangle, cancel out!
         (cond
           ((-> obj selected-option)
            ;; an option is selected. AHHH!!! just restore to whatever was on the backup
            (case (-> options (-> obj option-index) option-type)
              (((game-option-type slider))
               (set! (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))
                     (-> *progress-state* slider-backup)))
              (((game-option-type language))
               (set! (-> (the-as (pointer language-enum) (-> options (-> obj option-index) value-to-modify)))
                     (-> *progress-state* language-backup)))
              (((game-option-type on-off) (game-option-type normal-inverted))
               (set! (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify)))
                     (-> *progress-state* on-off-backup))))
            ;; ding
            (sound-play "cursor-options")
            (set! (-> obj selected-option) #f))
           ((or (can-go-back? obj)
                (= (-> obj display-state) (progress-screen load-game))
                (= (-> obj display-state) (progress-screen save-game))
                (= (-> obj display-state) (progress-screen save-game-title)))
            ;; no option selected, go back
            (cpad-clear! 0 square)
            (cpad-clear! 0 triangle)
            (if (= (-> obj display-state) (progress-screen settings)) (sound-play "menu-stats") (sound-play "cursor-options"))
            (when (= (-> obj display-state) (progress-screen music-player))
              (sound-group-pause (sound-group music))
              (progress-reset-music-parms))
            (load-level-text-files (-> *level-task-data* (-> obj display-level-index) text-group-index))
            (set! (-> obj next-display-state) (progress-screen invalid)))))
        ((or (cpad-pressed? 0 x) (cpad-pressed? 0 circle))
         ;; pressed x or circle. advance!
         ;; first check if the option is disabled, if so leave early and do nothing
         (when (and (nonzero? (-> options (-> obj option-index) option-disabled-func))
                    (!= (-> options (-> obj option-index) option-disabled-func) #f)
                    ((-> options (-> obj option-index) option-disabled-func)))
           (sound-play "cursor-options")
           (return #f))
         (cond
           ((not (-> obj selected-option))
            ;; no option already selected.
            (cond
              ((= (-> options (-> obj option-index) option-type) (game-option-type menu))
               ;; go to a menu
               (cpad-clear! 0 x)
               (cpad-clear! 0 circle)
               (cond
                 ((and (not (-> *setting-control* default auto-save))
                       (= (text-id disable-auto-save) (-> options (-> obj option-index) name)))
                  (sound-play "cursor-options"))
                 (else
                  (push! obj)
                  (sound-play "select-option")
                  (set! (-> obj next-display-state) (the-as progress-screen (-> options (-> obj option-index) param3)))
                  (case (-> obj next-display-state)
                    (((progress-screen load-game) (progress-screen save-game) (progress-screen save-game-title))
                     (set! (-> obj next-display-state) (set-memcard-screen obj (-> obj next-display-state))))))))
              ((= (-> options (-> obj option-index) option-type) (game-option-type button))
               ;; a button. what?
               (case (-> options (-> obj option-index) name)
                 (((text-id exit-demo))
                  ;; exit demo!
                  (set! *master-exit* 'force)
                  (set-master-mode 'game))
                 (((text-id back))
                  ;; go back!
                  (if (= (-> obj display-state) (progress-screen settings)) (sound-play "menu-stats") (sound-play "cursor-options"))
                  (when (= (-> obj display-state) (progress-screen music-player))
                    (sound-group-pause (sound-group music))
                    (progress-reset-music-parms))
                  (load-level-text-files (-> *level-task-data* (-> obj display-level-index) text-group-index))
                  (set! (-> obj next-display-state) (progress-screen invalid)))
                 ;; LEVEL SELECT
                 (((text-id training-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "game-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id village1-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "village1-warp"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id beach-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "beach-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id jungle-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "jungle-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id misty-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "misty-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id firecanyon-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "firecanyon-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id village2-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "village2-warp"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id sunken-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "sunken-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id swamp-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "swamp-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id rolling-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "rolling-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id ogre-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "ogre-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id village3-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "village3-warp"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id snow-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "snow-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id cave-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "maincave-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id lavatube-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "lavatube-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id citadel-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "citadel-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 (((text-id finalboss-level-name))
                  (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "finalboss-start"))
                  (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f)))
                 ;; SPEEDRUNS
                 ;; Restart current run
                 (((text-id speedrun-reset-current-run)) (progress-fast-save-and-start-speedrun (the-as speedrun-category #f)))
                 ;; Full game runs
                 (((text-id speedrun-new-full-game)) (progress-fast-save-and-start-speedrun (speedrun-category full-game)))
                 ;; Individual Levels
                 (((text-id speedrun-il-training)) (progress-fast-save-and-start-speedrun (speedrun-category il-training)))
                 (((text-id speedrun-il-village1)) (progress-fast-save-and-start-speedrun (speedrun-category il-village1)))
                 (((text-id speedrun-il-beach)) (progress-fast-save-and-start-speedrun (speedrun-category il-beach)))
                 (((text-id speedrun-il-jungle)) (progress-fast-save-and-start-speedrun (speedrun-category il-jungle)))
                 (((text-id speedrun-il-misty)) (progress-fast-save-and-start-speedrun (speedrun-category il-misty)))
                 (((text-id speedrun-il-firecanyon)) (progress-fast-save-and-start-speedrun (speedrun-category il-firecanyon)))
                 (((text-id speedrun-il-village2)) (progress-fast-save-and-start-speedrun (speedrun-category il-village2)))
                 (((text-id speedrun-il-sunken)) (progress-fast-save-and-start-speedrun (speedrun-category il-sunken)))
                 (((text-id speedrun-il-swamp)) (progress-fast-save-and-start-speedrun (speedrun-category il-swamp)))
                 (((text-id speedrun-il-rolling)) (progress-fast-save-and-start-speedrun (speedrun-category il-rolling)))
                 (((text-id speedrun-il-ogre)) (progress-fast-save-and-start-speedrun (speedrun-category il-ogre)))
                 (((text-id speedrun-il-village3)) (progress-fast-save-and-start-speedrun (speedrun-category il-village3)))
                 (((text-id speedrun-il-snow)) (progress-fast-save-and-start-speedrun (speedrun-category il-snow)))
                 (((text-id speedrun-il-cave)) (progress-fast-save-and-start-speedrun (speedrun-category il-cave)))
                 (((text-id speedrun-il-lavatube)) (progress-fast-save-and-start-speedrun (speedrun-category il-lavatube)))
                 (((text-id speedrun-il-citadel)) (progress-fast-save-and-start-speedrun (speedrun-category il-citadel)))
                 (((text-id speedrun-new-game-plus)) (progress-fast-save-and-start-speedrun (speedrun-category new-game-plus)))
                 (((text-id speedrun-hub1-100)) (progress-fast-save-and-start-speedrun (speedrun-category hub1-100)))
                 (((text-id speedrun-hub2-100)) (progress-fast-save-and-start-speedrun (speedrun-category hub2-100)))
                 (((text-id speedrun-hub3-100)) (progress-fast-save-and-start-speedrun (speedrun-category hub3-100)))
                 (((text-id speedrun-all-cutscenes)) (progress-fast-save-and-start-speedrun (speedrun-category all-cutscenes))))
               (commit-to-file *pc-settings*)
               ;; other behaviors are hardcoded elsewhere because screw you.
               )
              ((= (-> options (-> obj option-index) option-type) (game-option-type button-music))
               ;; go to the music+flava player
               (when (find-music-log (progress-get-music-by-index (-> obj option-index)))
                 (cpad-clear! 0 x)
                 (cpad-clear! 0 circle)
                 (set! *progress-music* (-> obj option-index))
                 (push! obj)
                 (sound-play "select-option")
                 (set! (-> obj next-display-state) (progress-screen flava-player))))
              ((= (-> options (-> obj option-index) option-type) (game-option-type button-flava))
               ;; set flava
               (when (find-flava-log (progress-get-music-by-index *progress-music*) (the int (-> options (-> obj option-index) param1)))
                 (cpad-clear! 0 x)
                 (cpad-clear! 0 circle)
                 (set-setting *setting-control* obj 'music (progress-get-music-by-index *progress-music*) 0.0 0)
                 (set! *progress-flava* (the int (-> options (-> obj option-index) param1)))
                 (sound-group-continue (sound-group music))
                 (sound-play "select-option")))
              ((= (-> options (-> obj option-index) option-type) (game-option-type resolution))
               ;; resolution button. change resolution!
               (let ((newx (the int (-> options (-> obj option-index) param1)))
                     (newy (the int (-> options (-> obj option-index) param2))))
                 (set-window-size! *pc-settings* newx newy))
               (cpad-clear! 0 x)
               (cpad-clear! 0 circle)
               (sound-play "cursor-options")
               (set! (-> obj next-display-state) (progress-screen invalid))
               (commit-to-file *pc-settings*))
              ;; TODO - might be able to simplify this with the new on-change func
              ((= (-> options (-> obj option-index) option-type) (game-option-type controller))
               (let ((controller-id (the int (-> options (-> obj option-index) param1))))
                 ;; NOTE - for now, we only manage port 0 in the progress menu
                 (pc-set-controller! controller-id 0))
               (cpad-clear! 0 x)
               (cpad-clear! 0 circle)
               (sound-play "cursor-options")
               (set! (-> obj next-display-state) (progress-screen invalid))
               (commit-to-file *pc-settings*))
              ((= (-> options (-> obj option-index) option-type) (game-option-type monitor))
               ;; monitor button
               (let ((monitor (the int (-> options (-> obj option-index) param1)))) (pc-set-display-id! monitor))
               (cpad-clear! 0 x)
               (cpad-clear! 0 circle)
               (sound-play "cursor-options")
               (set! (-> obj next-display-state) (progress-screen invalid))
               (commit-to-file *pc-settings*))
              ((= (-> options (-> obj option-index) option-type) (game-option-type aspect-new))
               ;; aspect ratio button.
               (let ((newx (the int (-> options (-> obj option-index) param1)))
                     (newy (the int (-> options (-> obj option-index) param2))))
                 (cond
                   ((= (-> options (-> obj option-index) name) (text-id fit-to-screen))
                    (set-aspect! *pc-settings* 4 3)
                    (false! (-> *pc-settings* use-vis?))
                    (set-aspect-ratio 'aspect4x3)
                    (true! (-> *pc-settings* aspect-ratio-auto?)))
                   ((= newx -1.0)
                    (set-aspect! *pc-settings* 4 3)
                    (true! (-> *pc-settings* use-vis?))
                    (set! (-> *setting-control* default aspect-ratio) 'aspect4x3)
                    (set-aspect-ratio 'aspect4x3))
                   ((= newx -2.0)
                    (set-aspect! *pc-settings* 4 3)
                    (true! (-> *pc-settings* use-vis?))
                    (set! (-> *setting-control* default aspect-ratio) 'aspect16x9)
                    (set-aspect-ratio 'aspect16x9))
                   (else (set-aspect! *pc-settings* newx newy))))
               (cpad-clear! 0 x)
               (cpad-clear! 0 circle)
               (sound-play "cursor-options")
               (commit-to-file *pc-settings*)
               (set! (-> obj next-display-state) (progress-screen invalid)))
              ((and (= (-> options (-> obj option-index) option-type) (game-option-type cheat-toggle))
                    (not (logtest? (-> *pc-settings* cheats-known) (ash 1 (the int (-> options (-> obj option-index) param1))))))
               (sound-play "cursor-options")
               (if (and (= *cheat-mode* 'debug) (cpad-hold? 0 l1) (cpad-hold? 0 r1))
                 (logior! (-> *pc-settings* cheats-known) (ash 1 (the int (-> options (-> obj option-index) param1))))))
              ((!= (-> options (-> obj option-index) option-type) (game-option-type yes-no))
               ;; not yes-no
               ;; set backups! we're entering some toggle or whatever
               (case (-> options (-> obj option-index) option-type)
                 (((game-option-type slider))
                  (set! (-> *progress-state* slider-backup)
                        (-> (the-as (pointer float) (-> options (-> obj option-index) value-to-modify)))))
                 (((game-option-type language))
                  (set! (-> *progress-state* language-backup)
                        (-> (the-as (pointer language-enum) (-> options (-> obj option-index) value-to-modify)))))
                 (((game-option-type on-off) (game-option-type normal-inverted))
                  (set! (-> *progress-state* on-off-backup)
                        (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify)))))
                 (((game-option-type confirmation)) (set! (-> *progress-state* on-off-backup) #f))
                 (((game-option-type binding-assignment))
                  (set! *progress-pad-timeout* (-> *display* real-frame-counter))
                  ;; Inform the C++ side to start listening for input
                  (pc-set-waiting-for-bind! (-> options (-> obj option-index) bind-info device-type)
                                            (not (-> options (-> obj option-index) bind-info for-buttons?))
                                            (-> options (-> obj option-index) bind-info analog-min-range?)
                                            (-> options (-> obj option-index) bind-info input-idx)))
                 (((game-option-type cheat-toggle))
                  (set! (-> *progress-state* on-off-backup)
                        (logtest? (-> *pc-settings* cheats) (ash 1 (the int (-> options (-> obj option-index) param1)))))
                  (set! (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify)))
                        (-> *progress-state* on-off-backup)))
                 (((game-option-type display-mode))
                  ;; display-mode just reuses language stuff
                  (case (pc-get-display-mode)
                    (('windowed #f) (set! (-> *progress-carousell* int-backup) 0))
                    (('fullscreen #t) (set! (-> *progress-carousell* int-backup) 1))
                    (('borderless) (set! (-> *progress-carousell* int-backup) 2))))
                 (((game-option-type msaa))
                  (case (-> *pc-settings* gfx-msaa)
                    ((2) (set! (-> *progress-carousell* int-backup) 1))
                    ((4) (set! (-> *progress-carousell* int-backup) 2))
                    ((8) (set! (-> *progress-carousell* int-backup) 3))
                    ((16) (set! (-> *progress-carousell* int-backup) 4))
                    (else (set! (-> *progress-carousell* int-backup) 0))))
                 (((game-option-type lod-bg))
                  (case (-> *pc-settings* lod-force-tfrag)
                    ((0) (set! (-> *progress-carousell* int-backup) 0))
                    ((1 2) (set! (-> *progress-carousell* int-backup) 1))
                    (else (set! (-> *progress-carousell* int-backup) 2))))
                 (((game-option-type lod-fg))
                  (cond
                    ((-> *pc-settings* ps2-lod-dist?) (set! (-> *progress-carousell* int-backup) 1))
                    (else (set! (-> *progress-carousell* int-backup) 0))))
                 (((game-option-type speaker))
                  (case (-> *pc-settings* subtitle-speaker?)
                    ((#t) (set! (-> *progress-carousell* int-backup) 0))
                    ((#f) (set! (-> *progress-carousell* int-backup) 1))
                    (('auto) (set! (-> *progress-carousell* int-backup) 2))))
                 (((game-option-type frame-rate))
                  (case (-> *pc-settings* target-fps)
                    ((60) (set! (-> *progress-carousell* int-backup) 0))
                    ((100) (set! (-> *progress-carousell* int-backup) 1))
                    ((150) (set! (-> *progress-carousell* int-backup) 2))))
                 (((game-option-type language-subtitles))
                  (let ((cur-lang (get-language-name (-> *pc-settings* subtitle-language))))
                    (dotimes (i (-> *subtitle-languages* length))
                      (if (= (-> *subtitle-languages* i) cur-lang) (set! (-> *progress-carousell* int-backup) i)))))
                 (((game-option-type language-text))
                  (let ((cur-lang (get-language-name (-> *pc-settings* text-language))))
                    (dotimes (i (-> *text-languages* length))
                      (if (= (-> *text-languages* i) cur-lang) (set! (-> *progress-carousell* int-backup) i))))))
               (sound-play "select-option")
               (cpad-clear! 0 x)
               (cpad-clear! 0 circle)
               (set! (-> obj selected-option) #t)
               (case (-> options (-> obj option-index) option-type)
                 (((game-option-type language))
                  (set! (-> obj language-selection) (-> *setting-control* current language))
                  (set! (-> obj language-direction) #t)
                  (set! (-> obj language-transition) #f)
                  (set! (-> obj language-x-offset) 0))
                 (else
                  (set! (-> *progress-carousell* selection) (-> *progress-carousell* int-backup))
                  (set! (-> *progress-carousell* direction) #t)
                  (set! (-> *progress-carousell* transition) #f)
                  (set! (-> *progress-carousell* x-offset) 0)
                  (case (-> options (-> obj option-index) option-type)
                    (((game-option-type display-mode)) (set! (-> *progress-carousell* current-carousell) *carousell-display-mode*))
                    (((game-option-type msaa)) (set! (-> *progress-carousell* current-carousell) *carousell-msaa*))
                    (((game-option-type lod-bg)) (set! (-> *progress-carousell* current-carousell) *carousell-lod-bg*))
                    (((game-option-type lod-fg)) (set! (-> *progress-carousell* current-carousell) *carousell-lod-bg*))
                    (((game-option-type speaker)) (set! (-> *progress-carousell* current-carousell) *carousell-speaker*))
                    (((game-option-type frame-rate)) (set! (-> *progress-carousell* current-carousell) *carousell-frame-rate*))
                    (((game-option-type language-subtitles)) (set! (-> *progress-carousell* current-carousell) *subtitle-languages*))
                    (((game-option-type language-text)) (set! (-> *progress-carousell* current-carousell) *text-languages*))))))))
           (else
            ;; an option was selected. write stuff!
            (if (= (-> options (-> obj option-index) option-type) (game-option-type confirmation))
              (if (-> *progress-state* on-off-backup) (sound-play "menu-stats") (sound-play "cursor-options"))
              (sound-play "start-options"))
            (set! (-> obj selected-option) #f)
            (case (-> options (-> obj option-index) option-type)
              (((game-option-type cheat-toggle))
               (let ((the-cheat (ash 1 (the int (-> options (-> obj option-index) param1)))))
                 (cond
                   ((-> (the (pointer symbol) (-> options (-> obj option-index) value-to-modify)))
                    (logior! (-> *pc-settings* cheats) the-cheat))
                   (else
                    (logclear! (-> *pc-settings* cheats) the-cheat)
                    (when (and *target* (= the-cheat (pc-cheats invinc)))
                      (logclear! (-> *target* state-flags) (state-flags invulnerable)))))))
              (((game-option-type aspect-ratio))
               ;; aspect ratio is first written to the backup. so this is for applying the change if we went through with it.
               (set! (-> *setting-control* default aspect-ratio)
                     (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify)))))
              (((game-option-type aspect-native))
               (set! (-> *pc-settings* use-vis?) (-> (the-as (pointer symbol) (-> options (-> obj option-index) value-to-modify))))
               (if (-> *pc-settings* use-vis?)
                 (set! (-> *setting-control* current aspect-ratio) #f)
                 (set-aspect! *pc-settings* (-> *pc-settings* aspect-custom-x) (-> *pc-settings* aspect-custom-y))))
              (((game-option-type display-mode))
               ;; same thing.
               (case (-> *progress-carousell* int-backup)
                 ((0) (pc-set-display-mode! 'windowed))
                 ((1) (pc-set-display-mode! 'fullscreen))
                 ((2) (pc-set-display-mode! 'borderless))))
              (((game-option-type msaa))
               (case (-> *progress-carousell* int-backup)
                 ((0) (set! (-> *pc-settings* gfx-msaa) 1))
                 ((1) (set! (-> *pc-settings* gfx-msaa) 2))
                 ((2) (set! (-> *pc-settings* gfx-msaa) 4))
                 ((3) (set! (-> *pc-settings* gfx-msaa) 8))
                 ((4) (set! (-> *pc-settings* gfx-msaa) 16))))
              (((game-option-type lod-bg))
               (case (-> *progress-carousell* int-backup)
                 ((0) (set! (-> *pc-settings* lod-force-tfrag) 0) (set! (-> *pc-settings* lod-force-tie) 0))
                 ((1) (set! (-> *pc-settings* lod-force-tfrag) 2) (set! (-> *pc-settings* lod-force-tie) 2))
                 ((2) (set! (-> *pc-settings* lod-force-tfrag) 2) (set! (-> *pc-settings* lod-force-tie) 3))))
              (((game-option-type lod-fg))
               (case (-> *progress-carousell* int-backup)
                 ((0) (set! (-> *pc-settings* lod-force-actor) 0) (set! (-> *pc-settings* ps2-lod-dist?) #f))
                 ((1) (set! (-> *pc-settings* lod-force-actor) 0) (set! (-> *pc-settings* ps2-lod-dist?) #t))))
              (((game-option-type language-subtitles))
               (let ((lang (assoc (the binteger (-> *subtitle-languages* (-> *progress-carousell* int-backup))) *language-remap-info-pc*)))
                 (set! (-> *pc-settings* subtitle-language) (the pc-language (/ (the int (cadr lang)) 8)))))
              (((game-option-type language-text))
               (let ((lang (assoc (the binteger (-> *text-languages* (-> *progress-carousell* int-backup))) *language-remap-info-pc*)))
                 (set! (-> *pc-settings* text-language) (the pc-language (/ (the int (cadr lang)) 8))))
               (if (not (-> *progress-carousell* transition)) (load-level-text-files (-> obj display-level-index))))
              (((game-option-type speaker))
               ;; same thing.
               (case (-> *progress-carousell* int-backup)
                 ((0) (set! (-> *pc-settings* subtitle-speaker?) #t))
                 ((1) (set! (-> *pc-settings* subtitle-speaker?) #f))
                 ((2) (set! (-> *pc-settings* subtitle-speaker?) 'auto))))
              (((game-option-type frame-rate))
               (case (-> *progress-carousell* int-backup)
                 ((0) (set-frame-rate! *pc-settings* 60 #t))
                 ((1) (set-frame-rate! *pc-settings* 100 #t))
                 ((2) (set-frame-rate! *pc-settings* 150 #t)))
               (set-video-mode (-> *setting-control* default video-mode))))
            ;; if the option has a `on-confirm` function, run it
            (when (nonzero? (-> options (-> obj option-index) on-confirm))
              ((-> options (-> obj option-index) on-confirm)))
            (commit-to-file *pc-settings*)))))))
  (none))

(defmethod respond-memcard ((obj progress))
  "logic for handling input in memcard screens."
  (let ((s5-0 (-> obj card-info)))
    (when (and s5-0 (not (-> obj in-transition)))
      (when (or (cpad-pressed? 0 x) (cpad-pressed? 0 circle))
        (cpad-clear! 0 x)
        (cpad-clear! 0 circle)
        (case (-> obj display-state)
          (((progress-screen load-game))
           (cond
             ((< (-> obj option-index) 4)
              (when (nonzero? (-> s5-0 file (-> obj option-index) present))
                (sound-play "start-options")
                (set! (-> *progress-state* which) (-> obj option-index))
                (set! (-> obj next-display-state) (progress-screen memcard-loading))))
             (else (sound-play "cursor-options") (set! (-> obj next-display-state) (progress-screen invalid)))))
          (((progress-screen save-game) (progress-screen save-game-title))
           (cond
             ((< (-> obj option-index) 4)
              (sound-play "start-options")
              (set! (-> *progress-state* which) (-> obj option-index))
              ;; save slot selected
              (if (zero? (-> s5-0 file (-> obj option-index) present))
                (set! (-> obj next-display-state) (progress-screen memcard-saving))
                (set! (-> obj next-display-state) (progress-screen memcard-data-exists))))
             ;; continue without saving
             ((and (= (-> obj display-state) (progress-screen save-game-title)) (= (-> obj option-index) 4))
              (sound-play "starts-options")
              (sound-volume-off)
              (set! (-> *game-info* mode) 'play)
              (if (= (-> *pc-settings* speedrunner-mode?) #t)
                ;; Start a new game differently if speedrunning mode is active
                (speedrun-start-full-game-run)
                ;; start the game normally
                (initialize! *game-info* 'game (the-as game-save #f) "intro-start"))
              (set-master-mode 'game))
             (else (sound-play "cursor-options") (set! (-> obj next-display-state) (progress-screen invalid)))))
          (((progress-screen memcard-insert))
           (sound-play "cursor-options")
           (set! (-> obj next-display-state) (progress-screen invalid)))
          (((progress-screen memcard-data-exists))
           (cond
             ((-> *progress-state* yes-no-choice)
              (sound-play "start-options")
              (set! (-> obj next-display-state) (progress-screen memcard-saving)))
             (else
              (sound-play "cursor-options")
              (if (= (-> obj display-state-stack 0) (progress-screen title))
                (set! (-> obj next-display-state) (progress-screen save-game-title))
                (set! (-> obj next-display-state) (progress-screen save-game))))))
          (((progress-screen memcard-no-data))
           (cond
             ((-> *progress-state* yes-no-choice)
              (sound-play "start-options")
              (set! (-> obj next-display-state) (progress-screen memcard-creating)))
             (else
              (sound-play "cursor-options")
              (sound-volume-off)
              (set! (-> *game-info* mode) 'play)
              ;; Start a new game differently if speedrunning mode is active
              (cond
                ((-> *pc-settings* speedrunner-mode?) (speedrun-start-full-game-run))
                ;; start the game normally
                (else (initialize! *game-info* 'game (the-as game-save #f) "intro-start")))
              (set-master-mode 'game))))
          (((progress-screen memcard-no-space) (progress-screen memcard-not-inserted) (progress-screen memcard-not-formatted))
           (cond
             ((= (-> obj display-state-stack 0) (progress-screen title))
              (sound-play "start-options")
              (sound-volume-off)
              (set! (-> *game-info* mode) 'play)
              ;; Start a new game differently if speedrunning mode is active
              (cond
                ((-> *pc-settings* speedrunner-mode?) (speedrun-start-full-game-run))
                ;; start the game normally
                (else (initialize! *game-info* 'game (the-as game-save #f) "intro-start")))
              (set-master-mode 'game))
             ((nonzero? (-> obj display-state-stack 0)) (sound-play "start-options") (set-master-mode 'game))
             (else (sound-play "cursor-options") (set! (-> obj next-display-state) (progress-screen invalid)))))
          (((progress-screen memcard-error-loading)
            (progress-screen memcard-error-saving)
            (progress-screen memcard-error-formatting)
            (progress-screen memcard-error-creating)
            (progress-screen memcard-auto-save-error)
            (progress-screen memcard-removed)
            (progress-screen auto-save)
            (progress-screen memcard-auto-save-disabled))
           (sound-play "cursor-options")
           (set! (-> obj next-display-state) (progress-screen invalid)))
          (((progress-screen pal-change-to-60hz))
           (cond
             ((-> *progress-state* yes-no-choice)
              (sound-play "start-options")
              (set! (-> *setting-control* default video-mode) (-> *progress-state* video-mode-choice))
              (set! (-> obj video-mode-timeout) (-> *display* real-frame-counter))
              (set! (-> obj next-display-state) (progress-screen pal-now-60hz)))
             (else
              (sound-play "cursor-options")
              (set! (-> *progress-state* video-mode-choice) 'pal)
              (set! (-> obj next-display-state) (progress-screen invalid)))))
          (((progress-screen pal-now-60hz))
           (cond
             ((not (-> *progress-state* yes-no-choice))
              (set! (-> *progress-state* video-mode-choice) 'pal)
              (set! (-> *setting-control* default video-mode) (-> *progress-state* video-mode-choice))
              (sound-play "cursor-options"))
             (else (sound-play "start-options")))
           (set! (-> obj next-display-state) (progress-screen invalid)))
          (((progress-screen no-disc) (progress-screen bad-disc))
           (when (is-cd-in?)
             (sound-play "cursor-options")
             (set! (-> obj next-display-state) (progress-screen invalid))))
          (((progress-screen quit))
           (cond
             ((-> *progress-state* yes-no-choice)
              (set! *quit-to-title* #t)
              (sound-play "start-options")
              (sound-volume-off)
              (set! (-> *game-info* mode) 'play)
              (initialize! *game-info* 'game (the-as game-save #f) "title-start"))
             (else (sound-play "cursor-options") (set! (-> obj next-display-state) (progress-screen invalid)))))
          (((progress-screen memcard-format))
           (cond
             ((-> *progress-state* yes-no-choice)
              (sound-play "start-options")
              (set! (-> obj next-display-state) (progress-screen memcard-formatting)))
             ((= (-> obj display-state-stack 0) (progress-screen title))
              (sound-play "start-options")
              (sound-volume-off)
              (set! (-> *game-info* mode) 'play)
              ;; Start a new game differently if speedrunning mode is active
              (cond
                ((-> *pc-settings* speedrunner-mode?) (speedrun-start-full-game-run))
                ;; start the game normally
                (else (initialize! *game-info* 'game (the-as game-save #f) "intro-start")))
              (set-master-mode 'game))
             (else (sound-play "cursor-options") (set! (-> obj next-display-state) (progress-screen invalid)))))
          (((progress-screen quit-title))
           (cond
             ((-> *progress-state* yes-no-choice)
              (sound-play "start-options")
              (commit-to-file *pc-settings*)
              (sound-volume-off)
              (kernel-shutdown))
             (else (sound-play "cursor-options") (set! (-> obj next-display-state) (progress-screen invalid)))))
          (((progress-screen memcard-disable-auto-save))
           (cond
             ((-> *progress-state* yes-no-choice)
              (sound-play "start-options")
              (set! (-> *setting-control* default auto-save) #f)
              (set! (-> obj next-display-state) (progress-screen memcard-auto-save-disabled)))
             (else (sound-play "cursor-options") (set! (-> obj next-display-state) (progress-screen invalid)))))))))
  0
  (none))

(defmethod draw-options ((obj progress) (arg0 int) (arg1 int) (arg2 float))
  "common logic for drawing options menus."
  (let ((options (-> *options-remap* (-> obj display-state))))
    (when options
      ;; this menu has options to draw omg
      (let* ((line-amt (if (> (length options) PROGRESS_PC_PAGE_HEIGHT) (1- PROGRESS_PC_PAGE_HEIGHT) (length options)))
             (y-off (- arg0 (/ (* arg1 line-amt) 2)))
             (option-count 0)
             (unkx 27)
             (unk2 0)
             (font (new 'stack 'font-context *font-default-matrix* 0 0 0.0 (font-color default) (font-flags shadow kerning)))
             (locked-str (lookup-text! *common-text* (text-id locked) #f)))
        ;; set the common params for the text drawing
        (set-width! font 370)
        (set-height! font 25)
        (set! (-> font flags) (font-flags shadow kerning middle middle-vert large))
        ;; set scroll arrow sprite vertical position right now
        (set! (-> obj particles 32 init-pos y) (the float (* 2 (- y-off 20))))
        ;; when scrolling we draw an extra line
        (cond
          ((progress-scrolling-down?)
           (set! y-off (+ (- y-off arg1) (* (the float arg1) (- 1.0 (-> *progress-scroll* transition))))))
          ((progress-scrolling-up?) (set! y-off (+ (- y-off arg1) (* (the float arg1) (-> *progress-scroll* transition))))))
        (let ((draw-arrows (and (not (-> obj in-transition))
                                (= (-> obj next-state name) 'progress-normal)
                                (> (length options) PROGRESS_PC_PAGE_HEIGHT)
                                (< (mod (-> *display* real-frame-counter) (seconds 0.2)) (seconds 0.1))))
              (draw-prev (< 0 *progress-scroll-start*))
              (draw-next (> (length options) *progress-scroll-end*)))
          (set! (-> obj particles 32 init-pos x)
                (the float (if (and draw-arrows draw-prev) (- 195 (-> *progress-process* 0 left-x-offset)) -320)))
          (set! (-> obj particles 33 init-pos x)
                (the float (if (and draw-arrows draw-next) (- 195 (-> *progress-process* 0 left-x-offset)) -320))))
        ;; cheating a bit and putting some screen-specific code here. it's not a big deal.
        (case (-> obj display-state)
          (((progress-screen music-player))
           (set-color! font (font-color default))
           (set-origin! font (- 17 (-> obj left-x-offset)) 14)
           (set-scale! font (* 0.6 (-> obj transition-percentage-invert)))
           (print-game-text (lookup-text! *common-text* (text-id select-level) #f)
                            font
                            #f
                            (the int (* 128.0 (-> obj transition-percentage-invert)))
                            22))
          (((progress-screen flava-player))
           (set-color! font (font-color default))
           (set-origin! font (- 17 (-> obj left-x-offset)) 14)
           (set-scale! font (* 0.6 (-> obj transition-percentage-invert)))
           (print-game-text (lookup-text! *common-text* (text-id select-flava) #f)
                            font
                            #f
                            (the int (* 128.0 (-> obj transition-percentage-invert)))
                            22)))
        (dotimes (index (length options))
          (let ((option-str (the string #f)) ;; the option text
                (option-x 17)
                (option-y y-off)
                (locked? #f))
            ;; Check if the option should be disabled
            (when (and (nonzero? (-> options index option-disabled-func))
                       (!= (-> options index option-disabled-func) #f)
                       ((-> options index option-disabled-func)))
              (set! locked? #t))
            (case (-> options index option-type)
              (((game-option-type yes-no))
               ;; yes-no option. text is either '->YES<- NO' or 'YES ->NO<-', not the most robust but this option is a strange hack anyway.
               (if (-> (the-as (pointer uint32) (-> options index value-to-modify)))
                 (set! option-str
                       (string-format "~30L~S~0L ~S"
                                      (lookup-text! *common-text* (text-id yes) #f)
                                      (lookup-text! *common-text* (text-id no) #f)))
                 (set! option-str
                       (string-format "~0L~S ~30L~S"
                                      (lookup-text! *common-text* (text-id yes) #f)
                                      (lookup-text! *common-text* (text-id no) #f)))))
              ;; menu option or simple button. just draw its text!
              (((game-option-type menu) (game-option-type button))
               (if (nonzero? (-> options index name))
                 (set! option-str (lookup-text! *common-text* (-> options index name) #f))
                 (set! option-str #f))
               ;; if autosave disabled, update text and lock it
               (when (and (not (-> *setting-control* default auto-save)) (= (text-id disable-auto-save) (-> options index name)))
                 (set! option-str (lookup-text! *common-text* (text-id auto-save-disabled) #f))
                 (true! locked?))
               ;; checkpoint-select only unlocked after beating finalboss
               (when (and (not (task-closed? (game-task finalboss-movies) (task-status need-reminder)))
                          (= (text-id checkpoint-select) (-> options index name)))
                 (set! option-str (lookup-text! *common-text* (text-id cheat-req-beat-game) #f))
                 (true! locked?)))
              (((game-option-type button-music))
               ;; a button for the music player. has the text "locked" sometimes!
               (if (and (nonzero? (-> options index name)) (find-music-log (progress-get-music-by-index index)))
                 (set! option-str (lookup-text! *common-text* (-> options index name) #f))
                 (set! option-str locked-str)))
              (((game-option-type button-flava))
               ;; a button for the flava player. has the text "locked" sometimes!
               (if (and (nonzero? (-> options index name))
                        (find-flava-log (progress-get-music-by-index *progress-music*) (the int (-> options index param1))))
                 (set! option-str (lookup-text! *common-text* (-> options index name) #f))
                 (set! option-str locked-str)))
              (((game-option-type resolution) (game-option-type aspect-new))
               ;; resolution settings
               (set! option-str
                     (string-format (lookup-text! *common-text* (-> options index name) #f)
                                    (the int (-> options index param1))
                                    (the int (-> options index param2)))))
              (((game-option-type monitor))
               ;; monitor list
               (if (nonzero? (-> options index name-override))
                 (set! option-str (string-format "~S (~D)" (-> options index name-override) (+ 1 (the int (-> options index param1)))))
                 (set! option-str
                       (string-format (lookup-text! *common-text* (-> options index name) #f) (+ 1 (the int (-> options index param1)))))))
              (((game-option-type controller))
               ;; controller list
               (if (nonzero? (-> options index name-override))
                 (set! option-str (string-format "~S (~D)" (-> options index name-override) (+ 1 (the int (-> options index param1)))))
                 (set! option-str
                       (string-format (lookup-text! *common-text* (-> options index name) #f) (+ 1 (the int (-> options index param1)))))))
              (else
               (cond
                 ((and (-> obj selected-option) (= (-> obj option-index) index))
                  ;; this option is SELECTED!
                  (set-color! font (font-color default))
                  (set! (-> font origin x) (the float (- option-x (-> obj left-x-offset))))
                  (set! (-> font origin y) (the float (+ y-off -8)))
                  (set-scale! font 0.6)
                  ;; we use the binding name for the selected text header
                  (if (= (-> options index option-type) (game-option-type binding-assignment))
                    ;; display timeout in seconds
                    (let ((time-left (the int (+ 11.0 (* (/ 1.0 TICKS_PER_SECOND) (* -1.0 (- (-> *display* real-frame-counter) *progress-pad-timeout*)))))))
                      ;; if the timeout is over, unselect the option
                      (when (<= time-left 0)
                        (set! (-> obj selected-option) #f)
                        (sound-play "menu-stats")
                        (pc-stop-waiting-for-bind!))
                      (when (not (pc-waiting-for-bind?))
                        (set! (-> obj selected-option) #f)
                        (sound-play "cursor-options"))
                      (print-game-text (string-format "~S (~4L~D~0L)" (-> options index name-override) time-left) font #f 128 22))
                    (print-game-text (lookup-text! *common-text* (-> options index name) #f) font #f 128 22))
                  (set! option-y (+ y-off 7))
                  (case (-> options index option-type)
                    (((game-option-type slider))
                     ;; draw a slider and its text.
                     ;; this ugliness is just decompiler stuff. all it does is fade the alpha according to value.
                     (let* ((v1-82 (the-as uint #x8000ffff))
                            ;; calculate the percentage of the bar based on the actual range, instead of assuming 0-100
                            ;; low = param1 / high = param2
                            ;; (actual + abs(low)) / (high + abs(low)) = percentage
                            (percentage (/ (+ (-> (the-as (pointer float) (-> options index value-to-modify))) (fabs (-> options index param1)))
                                           (+ (-> options index param2) (fabs (-> options index param1)))))
                            (a0-34 (logior (logand v1-82 -256) (shr (shl (the int (+ 64.0 (* 191.0 percentage))) 56) 56)))
                            (color (logior (logand a0-34 -65281) (shr (shl (shr (shl a0-34 56) 56) 56) 48))))
                       (draw-percent-bar (- 75 (-> obj left-x-offset)) (+ y-off 8) percentage (the-as rgba color))
                       (set! option-x (+ (the int (* 2.5 (* 100.0 percentage))) -100)))
                     (if (= (-> options index slider-show-decimal?) #t)
                       (set! option-str (string-format "~,,1f" (-> (the-as (pointer float) (-> options index value-to-modify)))))
                       (set! option-str (string-format "~D" (the int (-> (the-as (pointer float) (-> options index value-to-modify))))))))
                    (((game-option-type on-off) (game-option-type normal-inverted) (game-option-type aspect-native))
                     ;; on-off option or some other toggle. same logic as yes-no. changed to cut down code duping.
                     (let ((on-str (case (-> options index option-type)
                                     (((game-option-type on-off) (game-option-type aspect-native)) (lookup-text! *common-text* (text-id on) #f))
                                     (((game-option-type normal-inverted)) (lookup-text! *common-text* (text-id inverted) #f))))
                           (off-str (case (-> options index option-type)
                                      (((game-option-type on-off) (game-option-type aspect-native)) (lookup-text! *common-text* (text-id off) #f))
                                      (((game-option-type normal-inverted)) (lookup-text! *common-text* (text-id normal) #f)))))
                       (if (-> (the-as (pointer symbol) (-> options index value-to-modify)))
                         (set! option-str (string-format "~30L~S~0L ~S" on-str off-str))
                         (set! option-str (string-format "~0L~S ~30L~S" on-str off-str)))))
                    ;; the same as yes-no, but we aren't changing an underlying value.
                    (((game-option-type confirmation))
                     (if (-> *progress-state* on-off-backup)
                       (set! option-str
                             (string-format "~30L~S~0L ~S"
                                            (lookup-text! *common-text* (text-id yes) #f)
                                            (lookup-text! *common-text* (text-id no) #f)))
                       (set! option-str
                             (string-format "~0L~S ~30L~S"
                                            (lookup-text! *common-text* (text-id yes) #f)
                                            (lookup-text! *common-text* (text-id no) #f)))))
                    (((game-option-type cheat-toggle))
                     (if (-> (the-as (pointer symbol) (-> options index value-to-modify)))
                       (set! option-str
                             (string-format "~30L~S~0L ~S"
                                            (lookup-text! *common-text* (text-id on) #f)
                                            (lookup-text! *common-text* (text-id off) #f)))
                       (set! option-str
                             (string-format "~0L~S ~30L~S"
                                            (lookup-text! *common-text* (text-id on) #f)
                                            (lookup-text! *common-text* (text-id off) #f)))))
                    (((game-option-type binding-assignment))
                     (let ((bind (pc-get-current-bind (-> options index bind-info) *pc-cpp-temp-string*)))
                       (cond
                         ((= bind #f) (set! option-str (string-format "~S" (lookup-text! *common-text* (text-id input-opts-binds-unset) #f))))
                         ((string= *pc-cpp-temp-string* "unknown")
                          (set! option-str (string-format "~S" (lookup-text! *common-text* (text-id input-opts-binds-unknown) #f))))
                         (else (set! option-str (string-format "~S" *pc-cpp-temp-string*))))))
                    (((game-option-type display-mode)
                      (game-option-type msaa)
                      (game-option-type lod-bg)
                      (game-option-type lod-fg)
                      (game-option-type speaker)
                      (game-option-type frame-rate)
                      (game-option-type language-subtitles)
                      (game-option-type language-text))
                     ;; crunched down to one generic function.
                     (progress-draw-carousell-from-string-list (-> *progress-carousell* current-carousell)
                                                               font
                                                               y-off
                                                               (-> (the-as (pointer int) (-> options index value-to-modify)))))
                    (((game-option-type language))
                     ;; language carousell. who knew this could be so complicated.
                     (let ((old-lang (-> obj language-selection))
                           (new-lang (-> (the-as (pointer language-enum) (-> options index value-to-modify))))
                           (max-lang 6))
                       (if (-> obj language-transition)
                         (seekl! (-> obj language-x-offset) 200 (the int (* 10.0 (-> *display* time-adjust-ratio)))))
                       (when (>= (-> obj language-x-offset) 100)
                         (set! (-> obj language-selection) new-lang)
                         (set! old-lang new-lang)
                         (set! (-> obj language-transition) #f)
                         (set! (-> obj language-x-offset) 0))
                       (set! (-> font origin y) (the float (+ y-off 3)))
                       (set-color! font (font-color progress-blue))
                       0
                       (let ((next-lang (mod (+ old-lang 1) max-lang))
                             (prev-lang (mod (+ max-lang -1 old-lang) max-lang))
                             ;; these are used during the transition since it technically allows you to see 4 langs at once.
                             (next2-lang (mod (+ old-lang 2) max-lang))
                             (prev2-lang (mod (+ max-lang -2 old-lang) max-lang)))
                         (cond
                           ((-> obj language-direction)
                            (let ((a2-22 (- 200 (+ (-> obj language-x-offset) 100)))) (print-language-name prev-lang font a2-22 #f))
                            (set-color! font (font-color progress-blue))
                            (let ((a2-23 (+ (-> obj language-x-offset) 100)))
                              (cond
                                ((< a2-23 150) (print-language-name (the int next-lang) font a2-23 #t))
                                (else (let ((a2-24 (- 200 (-> obj language-x-offset)))) (print-language-name prev2-lang font a2-24 #f))))))
                           (else
                            (let ((a2-25 (+ (-> obj language-x-offset) 100)))
                              (cond
                                ((< a2-25 150) (print-language-name prev-lang font a2-25 #f))
                                (else (let ((a2-26 (- 200 (-> obj language-x-offset)))) (print-language-name (the int next2-lang) font a2-26 #t)))))
                            (set-color! font (font-color progress-blue))
                            (let ((a2-27 (- 200 (+ (-> obj language-x-offset) 100)))) (print-language-name (the int next-lang) font a2-27 #t)))))
                       (if (not (-> obj language-transition)) (set-color! font (font-color progress-selected)))
                       (print-language-name (the-as int old-lang) font (-> obj language-x-offset) (-> obj language-direction))))
                    (((game-option-type aspect-ratio))
                     ;; same as on-off but checks a different symbol
                     (if (= (-> (the-as (pointer symbol) (-> options index value-to-modify))) 'aspect4x3)
                       (set! option-str
                             (string-format "~30L~S~0L ~S"
                                            (lookup-text! *common-text* (text-id 4x3) #f)
                                            (lookup-text! *common-text* (text-id 16x9) #f)))
                       (set! option-str
                             (string-format "~0L~S ~30L~S"
                                            (lookup-text! *common-text* (text-id 4x3) #f)
                                            (lookup-text! *common-text* (text-id 16x9) #f)))))))
                 (else
                  ;; this option is not selected
                  (case (-> options index option-type)
                    (((game-option-type slider)
                      (game-option-type aspect-ratio)
                      (game-option-type display-mode)
                      (game-option-type msaa)
                      (game-option-type lod-bg)
                      (game-option-type lod-fg)
                      (game-option-type speaker)
                      (game-option-type frame-rate)
                      (game-option-type normal-inverted)
                      (game-option-type confirmation))
                     ;; slider and aspect ratio options just show their text
                     (set! option-str (lookup-text! *common-text* (-> options index name) #f)))
                    (((game-option-type on-off) (game-option-type aspect-native))
                     ;; on-off options show their text + on or off
                     (set! option-str
                           (string-format "~S: ~S"
                                          (lookup-text! *common-text* (-> options index name) #f)
                                          (if (-> (the-as (pointer uint32) (-> options index value-to-modify)))
                                            (lookup-text! *common-text* (text-id on) #f)
                                            (lookup-text! *common-text* (text-id off) #f)))))
                    (((game-option-type binding-assignment))
                     ;; binding assignment options show their input + the current binding
                     (let ((bind (pc-get-current-bind (-> options index bind-info) *pc-cpp-temp-string*)))
                       (cond
                         ((= bind #f)
                          (set! option-str
                                (string-format "~S: ~10L~S~0L"
                                               (-> options index name-override)
                                               (lookup-text! *common-text* (text-id input-opts-binds-unset) #f))))
                         ((string= *pc-cpp-temp-string* "unknown")
                          (set! option-str
                                (string-format "~S: ~10L~S~0L"
                                               (-> options index name-override)
                                               (lookup-text! *common-text* (text-id input-opts-binds-unknown) #f))))
                         (else (set! option-str (string-format "~S: ~10L~S~0L" (-> options index name-override) *pc-cpp-temp-string*))))))
                    (((game-option-type cheat-toggle))
                     (cond
                       ((not (logtest? (-> *pc-settings* cheats-known) (ash 1 (the int (-> options index param1)))))
                        (true! locked?)
                        (case (ash 1 (the int (-> options index param1)))
                          (((pc-cheats eco-blue)
                            (pc-cheats eco-green)
                            (pc-cheats eco-red)
                            (pc-cheats eco-yellow)
                            (pc-cheats invinc)
                            (pc-cheats huge-head)
                            (pc-cheats mirror)
                            (pc-cheats no-tex))
                           (set! option-str (lookup-text! *common-text* (text-id cheat-req-100-percent) #f)))
                          (((pc-cheats sky)
                            (pc-cheats sidekick-blue)
                            (pc-cheats tunes)
                            (pc-cheats big-head)
                            (pc-cheats small-head)
                            (pc-cheats big-fist)
                            (pc-cheats big-head-npc))
                           (set! option-str (lookup-text! *common-text* (text-id cheat-req-beat-game) #f)))
                          (else
                           ;; (format 0 "weird case ~f #x~x~%" (-> options index param1) (ash 1 (the int (-> options index param1))))
                           (set! option-str locked-str))))
                       (else
                        (set! option-str
                              (string-format "~S: ~S"
                                             (lookup-text! *common-text* (-> options index name) #f)
                                             (if (logtest? (-> *pc-settings* cheats) (ash 1 (the int (-> options index param1))))
                                               (lookup-text! *common-text* (text-id on) #f)
                                               (lookup-text! *common-text* (text-id off) #f)))))))
                    (((game-option-type language))
                     ;; language options show their text + language name
                     (set! option-str
                           (string-format "~S: ~S"
                                          (lookup-text! *common-text* (-> options index name) #f)
                                          (lookup-text! *common-text*
                                                        (-> *language-name-remap* (-> (the-as (pointer uint64) (-> options index value-to-modify))))
                                                        #f))))
                    (((game-option-type language-subtitles))
                     (set! option-str
                           (string-format "~S: ~S"
                                          (lookup-text! *common-text* (-> options index name) #f)
                                          (lookup-text! *common-text* (get-language-name (-> *pc-settings* subtitle-language)) #f))))
                    (((game-option-type language-text))
                     (set! option-str
                           (string-format "~S: ~S"
                                          (lookup-text! *common-text* (-> options index name) #f)
                                          (lookup-text! *common-text* (get-language-name (-> *pc-settings* text-language)) #f)))))))))
            (when (or (<= (length options) PROGRESS_PC_PAGE_HEIGHT)
                      (and (not (progress-scrolling?)) (>= index *progress-scroll-start*) (< index *progress-scroll-end*))
                      (and (progress-scrolling-down?) (>= index (1- *progress-scroll-start*)) (< index *progress-scroll-end*))
                      (and (progress-scrolling-up?) (>= index *progress-scroll-start*) (< index (1+ *progress-scroll-end*))))
              (when option-str
                ;; draw the actual text!
                (let ((f0-23 (-> obj transition-percentage-invert))
                      (scroll-amt (-> *progress-scroll* transition)))
                  (cond
                    ((or (and (progress-scrolling-up?) (= index *progress-scroll-start*))
                         (and (progress-scrolling-down?) (= index (1- *progress-scroll-end*)))))
                    ((or (and (progress-scrolling-down?) (= index (1- *progress-scroll-start*)))
                         (and (progress-scrolling-up?) (= index *progress-scroll-end*)))
                     (set! scroll-amt (- 1.0 scroll-amt)))
                    (else (set! scroll-amt 1.0)))
                  (set-color! font
                              (cond
                                ((and (!= index (-> obj option-index)) (or locked? (= locked-str option-str)))
                                 ;; for "locked" options!
                                 (font-color menu-invalid))
                                ((and (= index (-> obj option-index)) (not (or (progress-scrolling?) (-> obj in-transition))))
                                 (font-color progress-selected))
                                (else (font-color default))))
                  (set! (-> font origin x) (the float (- option-x (-> obj left-x-offset))))
                  (set! (-> font origin y) (the float (the int (* (the float option-y) (if (-> options index scale) f0-23 1.0)))))
                  (set-scale! font (* arg2 f0-23 scroll-amt))
                  (print-game-text option-str font #f (the int (* 128.0 f0-23 scroll-amt)) 22)))
              (+! y-off arg1)
              (+! option-count 1)))))))
  0
  (none))

(defun draw-memcard-speedrun-options ((obj progress) (arg0 font-context))
  (set-scale! arg0 0.85)
  (set-flags! arg0 (font-flags shadow kerning middle middle-vert large))
  (set-origin! arg0 (- 20 (-> obj left-x-offset)) 30)
  (set-width! arg0 360)
  (set-color! arg0 (font-color progress-blue))
  (set-height! arg0 20)
  (print-game-text-scaled (lookup-text! *common-text* (text-id speedrun-options-heading) #f)
                          (-> obj transition-percentage-invert)
                          arg0
                          128)
  (set-scale! arg0 0.6)
  (set-flags! arg0 (font-flags shadow kerning middle middle-vert large))
  (set-origin! arg0 (- 20 (-> obj left-x-offset)) 60)
  (set-width! arg0 360)
  (set-height! arg0 20)
  (set-color! arg0 (font-color progress-yellow))
  (print-game-text-scaled (lookup-text! *common-text* (text-id speedrun-options-warning) #f)
                          (-> obj transition-percentage-invert)
                          arg0
                          128)
  0)

(defun draw-memcard-auto-save-disabled ((obj progress) (arg0 font-context))
  (set-scale! arg0 0.6)
  (set-flags! arg0 (font-flags shadow kerning middle middle-vert large))
  (set-origin! arg0 (- 20 (-> obj left-x-offset)) 38)
  (set-width! arg0 360)
  (set-height! arg0 20)
  (print-game-text-scaled (lookup-text! *common-text* (text-id autosave-disabled-title) #f)
                          (-> obj transition-percentage-invert)
                          arg0
                          128)
  (set! (-> arg0 origin y) 72.0)
  (set-width! arg0 360)
  (set-height! arg0 60)
  (print-game-text-scaled (lookup-text! *common-text* (text-id autosave-disabled-msg) #f)
                          (-> obj transition-percentage-invert)
                          arg0
                          128)
  (set-scale! arg0 0.65)
  (set! (-> arg0 origin y) 120.0)
  (set-height! arg0 60)
  (print-game-text-scaled (lookup-text! *common-text* (text-id continue?) #f)
                          (-> obj transition-percentage-invert)
                          arg0
                          128)
  0)

(defun draw-memcard-disable-auto-save ((obj progress) (arg0 font-context))
  (set-origin! arg0 (- 50 (-> obj left-x-offset)) 70)
  (set-scale! arg0 0.6)
  (set-width! arg0 300)
  (set-height! arg0 40)
  (set-flags! arg0 (font-flags shadow kerning middle middle-vert large))
  (print-game-text-scaled (lookup-text! *common-text* (text-id disable-auto-save-query) #f)
                          (-> obj transition-percentage-invert)
                          arg0
                          128)
  0)

(defmethod draw-notice-screen ((obj progress))
  (hide-progress-icons)
  (when *common-text*
    (let ((a1-1 (new 'stack
                     'font-context
                     *font-default-matrix*
                     (- 70 (-> obj left-x-offset))
                     10
                     0.0
                     (font-color default)
                     (font-flags shadow kerning))))
      (case (-> obj display-state)
        (((progress-screen memcard-format)) (draw-memcard-format obj a1-1))
        (((progress-screen memcard-no-space) (progress-screen memcard-not-inserted) (progress-screen memcard-not-formatted))
         (draw-memcard-storage-error obj a1-1))
        (((progress-screen memcard-data-exists)) (draw-memcard-data-exists obj a1-1))
        (((progress-screen memcard-no-data)) (draw-memcard-no-data obj a1-1))
        (((progress-screen memcard-loading)
          (progress-screen memcard-saving)
          (progress-screen memcard-formatting)
          (progress-screen memcard-creating))
         (draw-memcard-accessing obj a1-1))
        (((progress-screen memcard-insert)) (draw-memcard-insert obj a1-1))
        (((progress-screen load-game) (progress-screen save-game) (progress-screen save-game-title))
         (draw-memcard-file-select obj a1-1))
        (((progress-screen memcard-auto-save-error)) (draw-memcard-auto-save-error obj a1-1))
        (((progress-screen memcard-removed)) (draw-memcard-removed obj a1-1))
        (((progress-screen memcard-error-loading)
          (progress-screen memcard-error-saving)
          (progress-screen memcard-error-formatting)
          (progress-screen memcard-error-creating))
         (draw-memcard-error obj a1-1))
        (((progress-screen auto-save)) (draw-auto-save obj a1-1))
        (((progress-screen pal-change-to-60hz)) (draw-pal-change-to-60hz obj a1-1))
        (((progress-screen pal-now-60hz)) (draw-pal-now-60hz obj a1-1))
        (((progress-screen no-disc)) (draw-no-disc obj a1-1))
        (((progress-screen bad-disc)) (draw-bad-disc obj a1-1))
        (((progress-screen quit) (progress-screen quit-title)) (draw-quit obj a1-1))
        (((progress-screen memcard-disable-auto-save)) (draw-memcard-disable-auto-save obj a1-1))
        (((progress-screen memcard-auto-save-disabled)) (draw-memcard-auto-save-disabled obj a1-1))
        (((progress-screen speedrun-options)) (draw-memcard-speedrun-options obj a1-1)))))
  0
  (none))

;; override the post handler for progress-normal
(set! (-> progress-normal post)
      (lambda :behavior progress ()
        (when (progress-scrolling?)
          (seek! (-> *progress-scroll* transition)
                 1.0
                 (* (/ 1.75 512) (-> self transition-speed) (-> *display* time-adjust-ratio))))
        (when (!= (-> self display-state) (-> *progress-scroll* last-screen))
          (progress-scroll-reset)
          (set! (-> *progress-scroll* last-screen) (-> self display-state)))
        ;; draw the menus!!
        (let* ((a1-0 (-> self display-level-index))
               (gp-0 (-> *level-task-data* a1-0))
               (unk #t)
               (stats? #f))
          (case (-> self display-state)
            (((progress-screen fuel-cell)) (set! stats? #t) (draw-fuel-cell-screen self a1-0))
            (((progress-screen money)) (set! stats? #t) (draw-money-screen self a1-0))
            (((progress-screen buzzer)) (set! stats? #t) (draw-buzzer-screen self a1-0))
            (((progress-screen graphic-settings)
              (progress-screen settings-title)
              (progress-screen title)
              (progress-screen sound-settings)
              (progress-screen game-ps2-options)
              (progress-screen resolution)
              (progress-screen aspect-ratio)
              (progress-screen secrets)
              (progress-screen cheats)
              (progress-screen monitor))
             (hide-progress-icons)
             (draw-options self 115 25 0.82))
            (((progress-screen settings) (progress-screen settings-title)) (hide-progress-icons) (draw-options self 115 25 0.78))
            (((progress-screen misc-options)) (hide-progress-icons) (draw-options self 115 25 0.79))
            (((progress-screen music-player)) (hide-progress-icons) (draw-options self 128 21 0.82))
            (((progress-screen flava-player)) (hide-progress-icons) (draw-options self 128 21 0.82))
            (((progress-screen speedrun-options)) (hide-progress-icons) (draw-notice-screen self) (draw-options self 155 25 0.65))
            (((progress-screen camera-options)
              (progress-screen checkpoint-select)
              (progress-screen speedrun-il-options)
              (progress-screen speedrun-cat-ext-options))
             ;; camera options lines are a bit too big
             (hide-progress-icons)
             (draw-options self 115 25 0.65))
            (((progress-screen input-options)
              (progress-screen select-controller)
              (progress-screen controller-binds)
              (progress-screen keyboard-binds)
              (progress-screen mouse-binds)
              (progress-screen controller-options)
              (progress-screen mouse-options)
              (progress-screen reassign-binds-options))
             (hide-progress-icons)
             (draw-options self 115 25 0.65))
            (((progress-screen gfx-ps2-options)) (hide-progress-icons) (draw-options self 115 25 0.67))
            (((progress-screen game-settings)) (hide-progress-icons) (draw-options self 115 25 0.70))
            (((progress-screen memcard-removed) (progress-screen memcard-auto-save-error))
             (draw-notice-screen self)
             (draw-options self 192 0 0.82))
            (((progress-screen memcard-no-data) (progress-screen memcard-auto-save-disabled))
             (draw-notice-screen self)
             (draw-options self 165 0 0.82))
            (((progress-screen memcard-format)) (draw-notice-screen self) (draw-options self 172 0 0.82))
            (((progress-screen memcard-no-space) (progress-screen memcard-not-inserted) (progress-screen memcard-not-formatted))
             (draw-notice-screen self)
             (draw-options self 195 0 0.82))
            (((progress-screen memcard-error-loading)
              (progress-screen memcard-error-saving)
              (progress-screen memcard-error-formatting)
              (progress-screen memcard-error-creating)
              (progress-screen memcard-auto-save-error)
              (progress-screen auto-save)
              (progress-screen load-game)
              (progress-screen save-game))
             (draw-notice-screen self)
             (draw-options self 190 0 0.82))
            (((progress-screen no-disc) (progress-screen bad-disc))
             (draw-notice-screen self)
             (if (is-cd-in?) (draw-options self 170 0 0.82)))
            (((progress-screen quit) (progress-screen quit-title) (progress-screen memcard-disable-auto-save))
             (draw-notice-screen self)
             (draw-options self 110 0 0.82))
            (((progress-screen memcard-insert)) (draw-notice-screen self) (draw-options self 165 0 0.82))
            (((progress-screen memcard-data-exists)) (draw-notice-screen self) (draw-options self 168 0 0.82))
            (((progress-screen memcard-loading)
              (progress-screen memcard-saving)
              (progress-screen memcard-formatting)
              (progress-screen memcard-creating))
             (draw-notice-screen self))
            (((progress-screen save-game-title)) (draw-notice-screen self) (draw-options self 169 15 0.6))
            (else (format 0 "PROGRESS - You forgot to add a `progress-screen` case to draw options!")))
          (when stats?
            (let* ((v1-98 (cond
                            ((-> self stat-transition) 0)
                            ((= (-> self level-transition) 1) (- (-> self transition-offset)))
                            (else (-> self transition-offset))))
                   (f30-0 (the-as float (if (-> self stat-transition) 1.0 (-> self transition-percentage-invert))))
                   (s5-1 (new 'stack
                              'font-context
                              *font-default-matrix*
                              (- 32 (-> self left-x-offset))
                              (the int (* (+ 42.0 (the float (/ v1-98 2))) f30-0))
                              8325000.0
                              (font-color progress-blue)
                              (font-flags shadow kerning))))
              (set-width! s5-1 328)
              (set-height! s5-1 45)
              (set! (-> s5-1 flags) (font-flags shadow kerning middle middle-vert large))
              (print-game-text-scaled (lookup-text! *common-text* (-> gp-0 level-name-id) #f) f30-0 s5-1 (the int (* 128.0 f30-0))))))
        (case (-> self display-state)
          (((progress-screen fuel-cell) (progress-screen money) (progress-screen buzzer)) (draw-progress self)))
        (adjust-sprites self)
        (adjust-icons self)
        (none)))

;; override the enter handler for progress-going-out
(set! (-> progress-going-out enter)
      (lambda :behavior progress ()
        (sound-play "menu-close")
        (hide-progress-icons)
        (remove-setting! 'music)
        (apply-settings *setting-control*)
        (progress-reset-music-parms)
        (commit-to-file *pc-settings*)
        (set! (-> self particles 3 init-pos x) -320.0)
        (set! (-> self particles 4 init-pos x) -320.0)
        (set! (-> self particles 32 init-pos x) -320.0)
        (set! (-> self particles 33 init-pos x) -320.0)
        (case (-> self display-state)
          (((progress-screen load-game) (progress-screen save-game) (progress-screen save-game-title))
           (set! (-> self transition-speed) 30.0)))
        (none)))

;; these need to be overridden as well
(set! (-> progress-going-out post) (-> progress-normal post))

(set! (-> progress-coming-in post) (-> progress-normal post))

;; override this as well...
(set! (-> progress-normal code)
      (lambda :behavior progress ()
        (loop
          (when (and (cpad-hold? 0 l1) (cpad-hold? 0 r1) *cheat-mode*)
            (when (and (< (-> self task-index) (-> *level-task-data* (-> self display-level-index) nb-of-tasks))
                       (>= (-> self task-index) 0))
              (let ((gp-0 (-> *level-task-data* (-> self display-level-index) task-info (-> self task-index) task-id)))
                (close-specific-task! gp-0 (task-status need-resolution))
                (send-event *target* 'get-pickup (pickup-type fuel-cell) (the float gp-0)))))
          (if (and (= (-> self display-state) (-> self next-display-state))
                   (= (-> self display-level-index) (-> self next-level-index)))
            (seekl! (-> self transition-offset)
                    0
                    (* (the int (* (-> self transition-speed) (-> *display* time-adjust-ratio)))
                       (if (or (-> self stat-transition) (nonzero? (-> self level-transition))) 2 1)))
            (seekl! (-> self transition-offset)
                    512
                    (* (the int (* (-> self transition-speed) (-> *display* time-adjust-ratio)))
                       (if (or (-> self stat-transition) (nonzero? (-> self level-transition))) 2 1))))
          (set-transition-progress! self (-> self transition-offset))
          (set! (-> self in-transition) (or (-> self force-transition) (nonzero? (-> self transition-offset))))
          (when (and (not (handle->process (-> *game-info* auto-save-proc)))
                     (or (-> self force-transition) (-> self in-transition))
                     (>= (-> self transition-offset)
                         (if (and (zero? (-> self level-transition))
                                  (nonzero? (-> self next-display-state))
                                  (!= (-> self next-display-state) 1)
                                  (!= (-> self next-display-state) 2))
                           512
                           256)))
            (if (>= (the-as int (-> self next-display-state)) 0) (enter! self (-> self next-display-state) 0) (pop! self))
            (set! (-> self display-level-index) (-> self next-level-index))
            (when (nonzero? (-> self level-transition))
              (set! (-> self task-index) (get-next-task-up -1 (-> self display-level-index)))
              (case (-> self level-transition)
                ((1) (set! (-> self level-transition) 2))
                ((2) (set! (-> self level-transition) 1))))
            (set! (-> self force-transition) #f))
          (when (zero? (-> self transition-offset))
            (set! (-> self stat-transition) #f)
            (set! (-> self level-transition) 0)
            0)
          (let ((gp-1 #f))
            (let ((v1-62 #f))
              (case (-> self display-state)
                (((progress-screen fuel-cell) (progress-screen money) (progress-screen buzzer))
                 (let ((s5-0 (-> self display-level-index)))
                   (when (and (< (mod (-> *display* real-frame-counter) (seconds 0.2)) (seconds 0.1))
                              (zero? (-> *progress-process* 0 in-out-position))
                              (not (-> self in-transition))
                              (zero? (-> self transition-offset)))
                     (set! gp-1 (!= s5-0 (get-next-level-up s5-0)))
                     (set! v1-62 (!= s5-0 (get-next-level-down s5-0)))))))
              (set! (-> self particles 3 init-pos x) (the float (if v1-62 (- 195 (-> *progress-process* 0 left-x-offset)) -320))))
            (set! (-> self particles 4 init-pos x) (the float (if gp-1 (- 195 (-> *progress-process* 0 left-x-offset)) -320))))
          (respond-common self)
          (set! (-> self next-display-state) (set-memcard-screen self (-> self next-display-state)))
          (let ((v1-74 (-> self display-state)))
            (cond
              ((or (= v1-74 (progress-screen fuel-cell)) (or (= v1-74 (progress-screen money)) (= v1-74 (progress-screen buzzer))))
               (respond-progress self))
              ((or (= v1-74 (progress-screen memcard-no-space))
                   (= v1-74 (progress-screen memcard-format))
                   (= v1-74 (progress-screen memcard-data-exists))
                   (= v1-74 (progress-screen memcard-insert))
                   (= v1-74 (progress-screen load-game))
                   (= v1-74 (progress-screen save-game))
                   (= v1-74 (progress-screen save-game-title))
                   (= v1-74 (progress-screen memcard-error-loading))
                   (= v1-74 (progress-screen memcard-error-saving))
                   (= v1-74 (progress-screen memcard-error-formatting))
                   (= v1-74 (progress-screen memcard-error-creating))
                   (= v1-74 (progress-screen memcard-auto-save-error))
                   (= v1-74 (progress-screen memcard-removed))
                   (= v1-74 (progress-screen memcard-no-data))
                   (= v1-74 (progress-screen memcard-not-inserted))
                   (= v1-74 (progress-screen memcard-not-formatted))
                   (= v1-74 (progress-screen auto-save))
                   (= v1-74 (progress-screen pal-change-to-60hz))
                   (= v1-74 (progress-screen pal-now-60hz))
                   (= v1-74 (progress-screen no-disc))
                   (= v1-74 (progress-screen bad-disc))
                   (= v1-74 (progress-screen quit))
                   (= v1-74 (progress-screen quit-title))
                   (= v1-74 (progress-screen memcard-auto-save-disabled))
                   (= v1-74 (progress-screen memcard-disable-auto-save)))
               (respond-memcard self))))
          (suspend))
        (none)))
