Files
jak-project/goal_src/jak2/pc/progress/progress-generic-pc.gc
2023-12-19 18:56:49 -05:00

589 lines
28 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
(defmethod call-on-load ((this generic-progress-state-entry))
(when (and (nonzero? (-> this on-load)) (!= (-> this on-load) #f))
((-> this on-load)))
(none))
(defmethod call-get-value-fn ((this menu-generic-boolean-option))
(if (and (nonzero? (-> this get-value-fn)) (!= (-> this get-value-fn) #f))
((-> this get-value-fn))
#f))
(defmethod call-on-confirm ((this menu-generic-boolean-option) (val symbol))
(when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f))
((-> this on-confirm) val))
(none))
(defmethod call-get-item-index-fn ((this menu-generic-carousel-option))
(if (and (nonzero? (-> this get-item-index-fn)) (!= (-> this get-item-index-fn) #f))
((-> this get-item-index-fn))
0))
(defmethod call-on-confirm ((this menu-generic-carousel-option) (val int) (the-progress progress))
(when (and (nonzero? (-> this on-confirm)) (-> this on-confirm))
((-> this on-confirm) val the-progress))
(none))
(defmethod call-on-confirm ((this menu-generic-button-option) (the-progress progress))
(when (and (nonzero? (-> this on-confirm)) (-> this on-confirm))
((-> this on-confirm) the-progress))
(none))
(defmethod call-on-confirm ((this menu-generic-confirm-option))
(when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f))
((-> this on-confirm)))
(none))
(defmethod call-get-value-fn ((this menu-generic-slider-option))
(if (and (nonzero? (-> this get-value-fn)) (!= (-> this get-value-fn) #f))
((-> this get-value-fn))
0.0))
(defmethod call-on-confirm ((this menu-generic-slider-option) (val float))
(when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f))
((-> this on-confirm) val))
(none))
(defmethod init! ((this progress-pc-generic-store))
(set! (-> this clear-screen?) #f)
(set! (-> this keybind-select-time) 0)
(set! (-> this current-highscore-page-index) 0)
(none))
(defmethod call-on-confirm ((this menu-generic-details-confirm-entry))
(when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f))
((-> this on-confirm)))
(none))
(defmethod navigate! ((this progress-pc-generic-store) (progress progress) (target menu-option-list) (on-load (function none)))
(when (< (-> this history-stack-index) 9) ;; hard-coded length
;; if this is the first history entry, we push the current page as well
;; if it's not, then we update the current entry before proceeding to the next page
(if (has-history? this)
(let ((stack-entry (-> this history-stack (-> this history-stack-index))))
(set! (-> stack-entry hover-index) (-> this current-menu-hover-index))
(set! (-> stack-entry scroll-index) (the int (-> this current-menu-scroll-index)))
(set! (-> stack-entry progress-id) (-> progress current)))
(let ((stack-entry (-> this history-stack (-> this history-stack-index))))
(set! (-> stack-entry ref) (-> progress current-options))
(set! (-> stack-entry on-load) on-load)
(set! (-> stack-entry hover-index) (-> this current-menu-hover-index))
(set! (-> stack-entry scroll-index) (the int (-> this current-menu-scroll-index)))
(set! (-> stack-entry progress-id) (-> progress current))))
;; push the target history entry now
(inc! (-> this history-stack-index))
;; Create a new stack entry
(let ((stack-entry (-> this history-stack (-> this history-stack-index))))
(set! (-> stack-entry ref) target)
(set! (-> stack-entry on-load) on-load)
(set! (-> stack-entry hover-index) 0)
(set! (-> stack-entry progress-id) 'generic-menu))
;; Update global state
(set! (-> this clear-screen?) #t)
(set! (-> progress next) 'generic-menu)
(set! (-> progress selected-option) #f))
(none))
(defmethod back! ((this progress-pc-generic-store) (progress progress))
(set! (-> this history-stack-index) (max 0 (dec (-> this history-stack-index))))
(set! (-> this clear-screen?) #t)
(set! (-> progress next) 'generic-menu)
(set! (-> progress selected-option) #f)
(none))
(defmethod has-history? ((this progress-pc-generic-store))
(> (-> this history-stack-index) 0))
(defmethod clear-history-if-empty! ((this progress-pc-generic-store))
"if we have no history, just reset the entry. This is so we don't preserve state after completely exiting the menu"
(when (not (has-history? this))
(set! (-> this history-stack-index) 0)
(set! (-> this clear-screen?) #f)
(set! (-> this current-menu-hover-index) 0)
(set! (-> this current-menu-scroll-index) 0.0)
(let ((stack-entry (-> this history-stack (-> this history-stack-index))))
(set! (-> stack-entry hover-index) 0)
(set! (-> stack-entry ref) #f)
(set! (-> stack-entry on-load) #f)
(set! (-> stack-entry progress-id) #f)))
(none))
(defmethod clear-history! ((this progress-pc-generic-store))
(set! (-> this history-stack-index) 0)
(set! (-> this clear-screen?) #t)
(set! (-> this current-menu-hover-index) 0)
(set! (-> this current-menu-scroll-index) 0.0)
(none))
(defmethod on-mount! ((this menu-generic-option))
(set! (-> this mounted?) #t)
(none))
;; TODO - call the parent methods, when there is an elegant way to do so (not having to know the ID)
(defmethod on-mount! ((this menu-generic-carousel-option))
(set! (-> this mounted?) #t)
(set! (-> this item-index) 0)
(none))
(defmethod on-mount! ((this menu-generic-scrolling-page))
(set! (-> this mounted?) #t)
(set! (-> this selected-option-index) -1)
(none))
(defmethod on-mount! ((this menu-generic-details-page))
(set! (-> this mounted?) #t)
(set! (-> this selected-entry-index) -1)
(none))
(defmethod on-mount! ((this menu-generic-confirm-option))
(set! (-> this mounted?) #t)
(set! (-> this confirmed?) #f)
(none))
(defun controller-keybind->input-index ((bind controller-keybind))
(case bind
(((controller-keybind l-analog-up)) 1)
(((controller-keybind l-analog-down)) 1)
(((controller-keybind l-analog-left)) 0)
(((controller-keybind l-analog-right)) 0)
(((controller-keybind r-analog-up)) 3)
(((controller-keybind r-analog-down)) 3)
(((controller-keybind r-analog-left)) 2)
(((controller-keybind r-analog-right)) 2)
(else (the int bind))))
(defmethod on-mount! ((this menu-generic-details-keybind-entry))
(set! (-> this mounted?) #t)
(set! (-> this bind-info port) 0) ;; always port 0 for now
(set! (-> this bind-info device-type) (the int (-> this device-type)))
(set! (-> this bind-info for-buttons?) #t)
(set! (-> this bind-info analog-min-range?) #f)
(set! (-> this bind-info input-idx) (controller-keybind->input-index (-> this keybind)))
(case (-> this keybind)
(((controller-keybind l-analog-up) (controller-keybind l-analog-down) (controller-keybind l-analog-left) (controller-keybind l-analog-right)
(controller-keybind r-analog-up) (controller-keybind r-analog-down) (controller-keybind r-analog-left) (controller-keybind r-analog-right))
(set! (-> this bind-info for-buttons?) #f)
(case (-> this keybind)
(((controller-keybind l-analog-up) (controller-keybind l-analog-left) (controller-keybind r-analog-up) (controller-keybind r-analog-left))
(set! (-> this bind-info analog-min-range?) #t)))))
(none))
;; Progress Code Overrides
(defmethod respond-to-cpad ((this progress))
(mc-get-slot-info 0 *progress-save-info*)
;; ND originally did this...called this every frame in the game options, looked like a hack
;; there's probably a way to consistently do it synchronously
(load-level-text-files (the-as int (-> *pc-settings* text-language)))
(when (-> this current-options)
(let ((option-array (-> this current-options options))
(has-generic-history? (has-history? *progress-pc-generic-store*)))
(when (and option-array (= (-> this menu-transition) 0.0))
(let ((in-generic-page? (and (= (-> option-array length) 1)
(or (type? (-> option-array 0) menu-generic-scrolling-page)
(type? (-> option-array 0) menu-generic-details-page)))))
(cond
;; If it's a scrolling menu, we have to pull from it's list of items
(in-generic-page?
;; update the scrolling list
(respond-progress
(the-as menu-option (-> option-array 0))
this
(and (= (-> this menu-transition) 0.0) (-> this selected-option))))
;; Do a bounds check to avoid crashing
((>= (-> this option-index) (-> option-array length))
(format 0 "respond-to-cpad:progress: Option index ~D out of bounds~%" (-> this option-index)))
(else
(respond-progress
(the-as menu-option (-> option-array (-> this option-index)))
this
(and (= (-> this menu-transition) 0.0) (-> this selected-option)))))
(cond
((-> this selected-option)
(cond
((cpad-pressed? 0 confirm)
(set! (-> this selected-option) #f)
)
((cpad-pressed? 0 triangle)
(when (not in-generic-page?)
(if (= (-> this current-options) *main-options*)
(sound-play "window-contract")
(sound-play "generic-beep")))
(set! (-> this selected-option) #f))))
(else
(cond
((cpad-pressed? 0 up l-analog-up)
(cond
((= (-> this current-options) *main-options*)
(sound-play "ring-select"))
((!= (length option-array) 1)
(sound-play "roll-over")))
;; skip over hidden secrets menu
(if (and (= *title-pc* (-> this current-options)) (not (memcard-unlocked-secrets? #f)) (= (-> this option-index) 4))
(set! (-> this option-index) 3))
(cond
((> (-> this want-option-index) 0)
(set! (-> this want-option-index) -1))
((< -2 (-> this want-option-index))
(+! (-> this want-option-index) -1))))
((cpad-pressed? 0 down l-analog-down)
(cond
((= (-> this current-options) *main-options*)
(sound-play "ring-select"))
((!= (length option-array) 1)
(sound-play "roll-over")))
;; skip over hidden secrets menu
(if (and (= *title-pc* (-> this current-options)) (not (memcard-unlocked-secrets? #f)) (= (-> this option-index) 2))
(set! (-> this option-index) 3))
(cond
((< (-> this want-option-index) 0)
(set! (-> this want-option-index) 1))
((< (-> this want-option-index) 2)
(+! (-> this want-option-index) 1))))
((cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(when (and (not in-generic-page?)
(not (-> *progress-state* clear-screen)))
(sound-play "generic-beep"))
;; don't "select" the page link
(when (!= (-> this next) 'generic-menu)
(set! (-> this selected-option) #t)))
((cpad-pressed? 0 triangle)
;; if we are within a generic managed menu option, handle the stack elsewhere
;; TODO - this likely has issues if we have a nested-non-generic menu, but deal with that use-case if it's ever
;; actually employed
(when (and (not has-generic-history?) (can-go-back? this))
(cpad-clear! 0 triangle)
(if (and (= (-> *progress-state* starting-state) 'main) (!= (-> this current-options) *main-options*))
(sound-play "window-contract")
(sound-play "generic-beep"))
(clear-history-if-empty! *progress-pc-generic-store*)
(pop-state this))))))))))
(none))
;; Component implementation
;; - input handling
(defmethod respond-progress ((this menu-generic-scrolling-page) (progress progress) (selected? symbol))
"Handle progress menu navigation logic."
(let ((selected-item? #f))
(if (= (-> this selected-option-index) -1)
(cond
((or (cpad-pressed? 0 down l-analog-down)
(and (cpad-hold? 0 down l-analog-down)
(>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2))))
(set! (-> this last-move) (current-time))
(cond
((< (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this menu-options length)))
(+! (-> *progress-pc-generic-store* current-menu-hover-index) 1)
(sound-play "roll-over"))
(else
(set! (-> *progress-pc-generic-store* current-menu-hover-index) 0)
(sound-play "roll-over"))))
((or (cpad-pressed? 0 up l-analog-up)
(and (cpad-hold? 0 up l-analog-up)
(>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2))))
(set! (-> this last-move) (current-time))
(+! (-> *progress-pc-generic-store* current-menu-hover-index) -1)
(sound-play "roll-over")
(when (< (-> *progress-pc-generic-store* current-menu-hover-index) 0)
(set! (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this menu-options length)))))
((cpad-pressed? 0 confirm)
(when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this menu-options length))
(let ((menu-option (-> this menu-options (-> *progress-pc-generic-store* current-menu-hover-index))))
;; if the option is disabled, return early
(when (and (type? menu-option menu-generic-option)
(nonzero? (-> (the-as menu-generic-option menu-option) should-disable?))
(!= (-> (the-as menu-generic-option menu-option) should-disable?) #f)
((-> (the-as menu-generic-option menu-option) should-disable?)))
(sound-play "roll-over")
(cpad-clear! 0 confirm)
(return 0))
;; ignore confirm if it's a link
(cond
;; TODO the condition for menu-generic-to-resolutions-option shouldn't be required if it
;; followed the existing pattern of being a page that contains options, instead of a single option that is a page
;; There are already components that do a similar thing (see menu-generic-details-page)
((or (type? menu-option menu-generic-link-option) (type? menu-option menu-generic-to-resolutions-option))
(set! selected-item? #t))
(else
(set! (-> this selected-option-index) (-> *progress-pc-generic-store* current-menu-hover-index))
(sound-play "generic-beep"))))))
((cpad-pressed? 0 triangle)
;; we are in a sub-page, time to go back
(when (has-history? *progress-pc-generic-store*)
(back! *progress-pc-generic-store* progress)
(sound-play "generic-beep"))))
;; menu option already selected
(cond
((cpad-pressed? 0 confirm)
(set! (-> this selected-option-index) -1))
((cpad-pressed? 0 triangle)
(set! (-> this selected-option-index) -1))))
;; propagate event to menu-option
(when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this menu-options length))
(respond-progress (-> this menu-options (-> *progress-pc-generic-store* current-menu-hover-index)) progress (or selected? selected-item?))))
0)
(defmethod respond-progress ((this menu-generic-boolean-option) (progress progress) (selected? symbol))
(if selected?
(cond
((cpad-pressed? 0 left l-analog-left right l-analog-right)
(if (-> this value)
(set! (-> this value) #f)
(set! (-> this value) #t))
(sound-play "generic-beep"))
((cpad-pressed? 0 confirm)
(call-on-confirm this (-> this value))
(sound-play "generic-beep")))
(cond
((cpad-pressed? 0 confirm)
;; set the value, this is so we edit the component's state and not the actual underlying value
;; in other words, don't change the setting until the user has actually confirmed the change!
(set! (-> this value) (call-get-value-fn this)))))
0)
(defmethod respond-progress ((this menu-generic-carousel-option) (progress progress) (selected? symbol))
(if selected?
(cond
((cpad-pressed? 0 left l-analog-left)
(dec! (-> this item-index))
(when (< (-> this item-index) 0)
(set! (-> this item-index) (dec (num-items this))))
(sound-play "generic-beep"))
((cpad-pressed? 0 right l-analog-right)
(inc! (-> this item-index))
(when (>= (-> this item-index) (num-items this))
(set! (-> this item-index) 0))
(sound-play "generic-beep"))
((cpad-pressed? 0 confirm)
(call-on-confirm this (-> this item-index) progress)
(sound-play "generic-beep")))
(cond
((cpad-pressed? 0 confirm)
;; set the value, this is so we edit the component's state and not the actual underlying value
;; in other words, don't change the setting until the user has actually confirmed the change!
(set! (-> this item-index) (call-get-item-index-fn this)))))
0)
(defmethod respond-progress ((this menu-generic-link-option) (progress progress) (selected? symbol))
(when (and selected? (cpad-pressed? 0 confirm))
(navigate! *progress-pc-generic-store* progress (-> this target) (-> this on-load))
(set! (-> progress selected-option) #f)
(sound-play "generic-beep"))
0)
(defmethod respond-progress ((this menu-generic-button-option) (progress progress) (selected? symbol))
(cond
((cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(call-on-confirm this progress)
(sound-play "generic-beep")))
0)
(defmethod respond-progress ((this menu-generic-confirm-option) (progress progress) (selected? symbol))
(if selected?
(cond
((cpad-pressed? 0 left l-analog-left right l-analog-right)
(if (-> this confirmed?)
(set! (-> this confirmed?) #f)
(set! (-> this confirmed?) #t))
(sound-play "generic-beep"))
((cpad-pressed? 0 confirm)
(call-on-confirm this)
(sound-play "generic-beep")))
(cond
((cpad-pressed? 0 confirm)
(set! (-> this confirmed?) #f))))
0)
(defmethod respond-progress ((this menu-generic-slider-option) (progress progress) (selected? symbol))
(if selected?
(cond
((and (> (-> this value) (-> this min-value))
(cpad-hold? 0 left l-analog-left))
;; TODO - might not work well on higher frame-rates
(set! (-> this value) (fmax (- (-> this value) (-> this step)) (-> this min-value)))
(when (< (seconds 0.03) (- (current-time) (-> this last-sound-played)))
(set! (-> this last-sound-played) (current-time))
(sound-play-by-name (static-sound-name "menu-slide") (new-sound-id) 512 0 0 (sound-group sfx) #t)))
((and (< (-> this value) (-> this max-value))
(cpad-hold? 0 right l-analog-right))
;; TODO - might not work well on higher frame-rates
(set! (-> this value) (fmin (+ (-> this value) (-> this step)) (-> this max-value)))
(when (< (seconds 0.03) (- (current-time) (-> this last-sound-played)))
(set! (-> this last-sound-played) (current-time))
(sound-play-by-name (static-sound-name "menu-slide") (new-sound-id) 512 0 0 (sound-group sfx) #t)))
((cpad-pressed? 0 confirm)
(call-on-confirm this (-> this value))
(sound-play "generic-beep")))
(cond
((cpad-pressed? 0 confirm)
;; set the value, this is so we edit the component's state and not the actual underlying value
;; in other words, don't change the setting until the user has actually confirmed the change!
(set! (-> this value) (call-get-value-fn this)))))
0)
(defmethod respond-progress ((this menu-generic-details-page) (progress progress) (selected? symbol))
(if (= (-> this selected-entry-index) -1)
(cond
((or (cpad-pressed? 0 down l-analog-down)
(and (cpad-hold? 0 down l-analog-down)
(>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2))))
(set! (-> this last-move) (current-time))
(cond
((< (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this entries length)))
(+! (-> *progress-pc-generic-store* current-menu-hover-index) 1)
(sound-play "roll-over"))
(else
(set! (-> *progress-pc-generic-store* current-menu-hover-index) 0)
(sound-play "roll-over"))))
((or (cpad-pressed? 0 up l-analog-up)
(and (cpad-hold? 0 up l-analog-up)
(>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2))))
(set! (-> this last-move) (current-time))
(+! (-> *progress-pc-generic-store* current-menu-hover-index) -1)
(sound-play "roll-over")
(when (< (-> *progress-pc-generic-store* current-menu-hover-index) 0)
(set! (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this entries length)))))
((cpad-pressed? 0 confirm)
(when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this entries length))
(set! (-> this selected-entry-index) (-> *progress-pc-generic-store* current-menu-hover-index))
(sound-play "generic-beep")))
((cpad-pressed? 0 triangle)
;; we are in a sub-page, time to go back
(when (has-history? *progress-pc-generic-store*)
(back! *progress-pc-generic-store* progress)
(sound-play "score-slide"))))
;; menu option already selected
(cond
((cpad-pressed? 0 confirm)
(set! (-> this selected-entry-index) -1))
((cpad-pressed? 0 triangle)
(set! (-> this selected-entry-index) -1))
;; keybinds
((nonzero? (-> *progress-pc-generic-store* keybind-select-time))
;; when the bind has been set, or it's expired
(when (or (not (pc-waiting-for-bind?))
(>= (- (current-time) (the-as int (-> *progress-pc-generic-store* keybind-select-time))) (seconds 5.0)))
(set! (-> *progress-pc-generic-store* keybind-select-time) 0)
(set! (-> this selected-entry-index) -1)
(set! (-> progress selected-option) #f)
(cond
((not (pc-waiting-for-bind?))
(sound-play "generic-beep"))
(else
(pc-stop-waiting-for-bind!)
(sound-play "roll-over")))))))
;; propagate event to menu-option
(when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this entries length))
(respond-progress (-> this entries (-> *progress-pc-generic-store* current-menu-hover-index)) progress selected?))
0)
(defmethod respond-progress ((this menu-generic-details-keybind-entry) (progress progress) (selected? symbol))
(when (not selected?)
(cond
((cpad-pressed? 0 confirm)
(let ((bind-info (-> this bind-info)))
(pc-set-waiting-for-bind! (-> bind-info device-type) (not (-> bind-info for-buttons?)) (-> bind-info analog-min-range?) (-> bind-info input-idx)))
(set! (-> *progress-pc-generic-store* keybind-select-time) (current-time)))))
0)
(defmethod respond-progress ((this menu-generic-details-confirm-entry) (progress progress) (selected? symbol))
(if selected?
(cond
((cpad-pressed? 0 left l-analog-left right l-analog-right)
(if (-> this confirmed?)
(set! (-> this confirmed?) #f)
(set! (-> this confirmed?) #t))
(sound-play "generic-beep"))
((cpad-pressed? 0 confirm)
(call-on-confirm this)
(sound-play "generic-beep")))
(cond
((cpad-pressed? 0 confirm)
(set! (-> this confirmed?) #f))))
0)
;; - rest of component logic
(defmethod num-items ((this menu-generic-carousel-option))
"Get the number of items in the carousel"
(if (and (nonzero? (-> this items)) (!= (-> this items) #f))
(-> this items length)
(if (and (and (nonzero? (-> this get-max-size-fn)) (!= (-> this get-max-size-fn) #f))
(and (nonzero? (-> this get-item-label-fn)) (!= (-> this get-max-size-fn) #f)))
((-> this get-max-size-fn))
0)))
(defmethod get-item-label ((this menu-generic-carousel-option) (item-index int))
"Gets the string label of the currently choosen item, preferring the `items` array if it exists"
(if (and (nonzero? (-> this items)) (!= (-> this items) #f))
(lookup-text! *common-text* (-> this items item-index) #f)
(if (and (nonzero? (-> this get-item-label-fn)) (!= (-> this get-max-size-fn) #f))
((-> this get-item-label-fn) item-index)
"#ERROR")))
;; TODO - it could be possible to map certain controllers to buttons (ie. dualshock controllers could have nice mappings instead of SDL named ones)
(defmethod get-keybind-string ((this menu-generic-details-keybind-entry))
(case (-> this keybind)
(((controller-keybind cross))
;; the PS2 TRC says it should be pronounced "ecks", hence why every game that speaks the buttons out loud says X, plus at least 80% of people also already say X (i assume in part because of that).
;; the official spelling is probably "cross" (see PS2 development reference manuals), but that has less weight in the context of displaying the name to the user, I think?
;; "cross" seems to be mainly a UK thing (those weirdos)
(if (language? uk-english)
"~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H Cross"
"~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H X"))
(((controller-keybind square))
"~Y~22L<~Z~Y~24L#~Z~Y~1L>~Z~Y~23L[~Z~+26H S~+7Vq~-7Vuare")
(((controller-keybind circle))
"~Y~22L<~Z~Y~25L@~Z~Y~1L>~Z~Y~23L[~Z~+26H Circle")
(((controller-keybind triangle))
"~Y~22L<~Z~Y~26L;~Z~Y~1L>~Z~Y~23L[~Z~+26H Trian~+7Vg~-7Vle")
(((controller-keybind dpad-up))
"~Y~0L\ca1~Z~3L~+17H~-13V\ca2~Z~0L~+17H~+14V\ca0~Z~0L~+32H\ca3~Z~+56H D-Pad U~+7Vp~-7V")
(((controller-keybind dpad-down))
"~Y~0L\ca1~Z~0L~+17H~-13V\ca2~Z~3L~+17H~+14V\ca0~Z~0L~+32H\ca3~Z~+56H D-Pad Down")
(((controller-keybind dpad-right))
"~Y~0L\ca1~Z~0L~+17H~-13V\ca2~Z~0L~+17H~+14V\ca0~Z~3L~+32H\ca3~Z~+56H D-Pad Ri~+7Vg~-7Vht")
(((controller-keybind dpad-left))
"~Y~3L\ca1~Z~0L~+17H~-13V\ca2~Z~0L~+17H~+14V\ca0~Z~0L~+32H\ca3~Z~+56H D-Pad Left")
(((controller-keybind l1))
"~Y~22L~-2H~-12V\ca6\ca7~Z~22L~-2H~+17V\cb0\cb1~Z~1L~+4H~+3V\c95~Z~+38H L1")
(((controller-keybind l2))
"~Y~22L~-2H~-6V\ca8\ca9~Z~22L~-2H~+16V\cb2\cb3~Z~1L~+5H~-2V\c97~Z~+38H L2")
(((controller-keybind l3))
"\c91 L3")
(((controller-keybind r1))
"~Y~22L~-2H~-12V\ca6\ca7~Z~22L~-2H~+17V\cb0\cb1~Z~1L~+6H~+3V\c94~Z~+38H R1")
(((controller-keybind r2))
"~Y~22L~-2H~-6V\ca8\ca9~Z~22L~-2H~+16V\cb2\cb3~Z~1L~+5H~-2V\c96~Z~+38H R2")
(((controller-keybind r3))
"\c91 R3")
(((controller-keybind select))
"\c92 Select")
(((controller-keybind start))
"\c92 Start")
(((controller-keybind l-analog-up))
"~Y~1L\c91~Z~-17V~6L\c98~Z~+26H Left Analo~+7Vg~-7V U~+7Vp~-7V")
(((controller-keybind l-analog-down))
"~Y~1L\c91~Z~+15V~6L\c9c~Z~+26H Left Analo~+7Vg~-7V Down")
(((controller-keybind l-analog-left))
"~Y~1L\c91~Z~-17H~6L\c9e~Z~+26H Left Analo~+7Vg~-7V Left")
(((controller-keybind l-analog-right))
"~Y~1L\c91~Z~+17H~6L\ca4~Z~+26H Left Analo~+7Vg~-7V Ri~+7Vg~-7Vht")
(((controller-keybind r-analog-up))
"~Y~1L\c91~Z~-17V~6L\c98~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V U~+7Vp~-7V")
(((controller-keybind r-analog-down))
"~Y~1L\c91~Z~+15V~6L\c9c~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V Down")
(((controller-keybind r-analog-left))
"~Y~1L\c91~Z~-17H~6L\c9e~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V Left")
(((controller-keybind r-analog-right))
"~Y~1L\c91~Z~+17H~6L\ca4~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V Ri~+7Vg~-7Vht")
(else
"Unknown Bind")))