jak3: support mirror mode (#4170)

Similar changes as have been done in the previous two games. Test the
following things:
- normal gameplay
- defend spargus turret
- satellite
- that desert mission where you shoot the gun
- that turret mission in haven forest

At a glance, i think that's everything...?

~~Didn't debug the crash yet either, hoping this is sufficient~~ (fixed,
thanks hatkid, needed to disable some blit related code)

Closes #4160
This commit is contained in:
Tyler Wilding
2026-04-05 17:52:15 -04:00
committed by GitHub
parent dc203502fc
commit 1f1fdf834b
17 changed files with 136 additions and 31 deletions
+1 -1
View File
@@ -2880,7 +2880,7 @@
(shutdown sound-rpc-shutdown :offset 0)
(list-sounds sound-rpc-list-sounds :offset 0)
(unload-music sound-rpc-unload-music :offset 0)
(mirror-mode sound-rpc-set-mirror-mode :overlay-at (-> data 0))
(mirror-mode sound-rpc-set-mirror-mode :offset 0)
)
:method-count-assert 9
:size-assert #x50
+14
View File
@@ -2616,6 +2616,8 @@
(iop-mem)
(cancel-dgo)
(set-stereo-mode)
;; og:preserve-this mirror mode
(set-mirror 201)
)
;; +++gsound-h:sound-group
@@ -2953,6 +2955,17 @@
:flag-assert #x900000004
)
; added
(defenum sound-mirror-mode
:type uint8
(normal)
(mirrored)
)
; added for mirror mode
(deftype sound-rpc-set-mirror-mode (sound-rpc-cmd)
((mirror sound-mirror-mode)))
(deftype sound-rpc-union (structure)
((data uint32 20 :offset-assert 0) ;; guessed by decompiler
(load-bank sound-rpc-load-bank :offset-assert 0 :overlay-at data)
@@ -2976,6 +2989,7 @@
(shutdown sound-rpc-shutdown :offset-assert 0 :overlay-at data)
(list-sounds sound-rpc-list-sounds :offset-assert 0 :overlay-at data)
(unload-music sound-rpc-unload-music :offset-assert 0 :overlay-at data)
(mirror-mode sound-rpc-set-mirror-mode :offset-assert 0 :overlay-at data)
)
:method-count-assert 9
:size-assert #x50
+7
View File
@@ -171,6 +171,8 @@ enum class SoundCommand : u16 {
// LINVEL_NOM = 37,
CANCEL_DGO = 49,
SET_STEREO_MODE = 50,
// custom below
MIRROR_MODE = 201,
};
struct SoundPlayParams {
@@ -274,6 +276,11 @@ struct Rpc_Loader_Set_Stereo_Mode : public Rpc_Player_Base_Cmd {
};
static_assert(sizeof(Rpc_Loader_Set_Stereo_Mode) == 8);
struct Rpc_Loader_Set_Mirror_Mode : public Rpc_Player_Base_Cmd {
u8 mode;
};
static_assert(sizeof(Rpc_Loader_Set_Mirror_Mode) == 6);
constexpr int kPlayerCommandStride = 0x50;
constexpr int kLoaderCommandStride = 0x50;
+9
View File
@@ -584,6 +584,15 @@ void* RPC_Loader(unsigned int, void* msg, int size) {
PrintSounds();
} break;
case SoundCommand::MIRROR_MODE: {
auto* cmd = (Rpc_Loader_Set_Mirror_Mode*)m_ptr;
if (cmd->mode == 0) {
g_CameraInvert = false;
} else {
g_CameraInvert = true;
}
} break;
default:
ovrld_log(LogCategory::WARN, "[RPC Loader] Unsupported Loader {}",
(int)((const Rpc_Player_Base_Cmd*)m_ptr)->command);
+19 -2
View File
@@ -1890,10 +1890,27 @@
(if (and (not (paused?)) (zero? (-> this number)) (-> *setting-control* user-current player-control-override))
(override-player-controls this)
)
(when (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode flag
(when (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
*PC-MIRROR-MODE-SET*)
(set! (-> this leftx) (- 255 (the-as int (-> this leftx))))
(set! (-> this rightx) (- 255 (the-as int (-> this rightx))))
)
;; while in the progress menu, invert left/right as well
(when *progress-process*
;; right = 5th bit and 1st index for pressure
;; left = 7th bit and 2nd index for pressure
(let ((right-pressed? (logtest? (-> this button0) (pad-buttons right)))
(right-pressure (-> this abutton 0))
(left-pressed? (logtest? (-> this button0) (pad-buttons left)))
(left-pressure (-> this abutton 1)))
(when right-pressed?
(logclear! (-> this button0) (pad-buttons right))
(logior! (-> this button0) (pad-buttons left)))
(when left-pressed?
(logclear! (-> this button0) (pad-buttons left))
(logior! (-> this button0) (pad-buttons right)))
(set! (-> this abutton 0) left-pressure)
(set! (-> this abutton 1) right-pressure))))
0
)
+3 -1
View File
@@ -1792,9 +1792,11 @@
(set! (-> s5-0 screeny) (-> s4-0 screeny))
(set! (-> *video-params* display-dy) (* (/ (-> s4-0 screeny) 2) 2))
)
;; og:preserve-this If this flag worked end-to-end (i believe it doesn't), mirror mode would come a bit easier
;; atleast in jak2 (havn't checked fully yet for jak3, assuming its the same)
(set-horizontal-flip-flag
*blit-displays-work*
(logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
(or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
)
(set! (-> s5-0 letterbox-speed) (-> s4-0 letterbox-speed))
(seek!
+11 -9
View File
@@ -711,15 +711,17 @@
(when (and (-> *setting-control* user-current render) (>= (the-as uint 1) (-> this count-down)))
;; handle horizontal flip by copying to fx buf, then copying back.
;; this is done before any HUD/text rendering.
(when (and (get-horizontal-flip-flag this) (not (get-menu-mode this)))
(with-dma-buffer-add-bucket ((s4-2 (-> *display* frames (-> *display* on-screen) global-buf))
(bucket-id debug-no-zbuf1)
)
(fx-copy-buf s4-2)
(fx-to-raster-flipped this s4-2)
(reset-display-gs-state *display* s4-2)
)
)
;; og:preserve-this Disabled right now to get mirror mode work (but technically be somewhat inaccurate)
;; can be fixed later when mirror mode is properly supported via the flag end-to-end by the gfx pipeline
;; (when (and (get-horizontal-flip-flag this) (not (get-menu-mode this)))
;; (with-dma-buffer-add-bucket ((s4-2 (-> *display* frames (-> *display* on-screen) global-buf))
;; (bucket-id debug-no-zbuf1)
;; )
;; (fx-copy-buf s4-2)
;; (fx-to-raster-flipped this s4-2)
;; (reset-display-gs-state *display* s4-2)
;; )
;; )
;; draw "depth-cue" or a simple attempt at anti-aliasing. This happens before particles, and
+5 -3
View File
@@ -209,9 +209,11 @@ Without this corrector, the fogginess of the world would change as the FOV chang
;; #xffffffff, which overflows the 24-bit z buffer.
;; cheating this by 1 bit seems to fix it.
(#when PC_PORT
;; #x4b002032 -> #x4b002031
(set! (-> arg0 isometric vector 3 z) (the-as float (- (the-as int (-> arg0 isometric vector 3 z)) 1)))
)
;; #x4b002032 -> #x4b002031
(set! (-> arg0 isometric vector 3 z) (the-as float (- (the-as int (-> arg0 isometric vector 3 z)) 1)))
;; also do mirror game check here.
(when *PC-MIRROR-MODE-SET*
(*! (-> arg0 perspective vector 0 x) -1.)))
)
(set! (-> arg0 isometric trans w) f30-0)
(let ((f1-28 (-> arg0 perspective rvec x))
+13
View File
@@ -58,6 +58,8 @@
(iop-mem)
(cancel-dgo)
(set-stereo-mode)
;; og:preserve-this mirror mode
(set-mirror 201)
)
(defenum sound-bank-mode
@@ -111,6 +113,12 @@
(unk2)
)
;; og:preserve-this mirror mode
(defenum sound-mirror-mode
:type uint8
(normal)
(mirrored))
(defenum stream-status
:type uint32
:bitfield #t
@@ -415,6 +423,9 @@
()
)
;; og:preserve-this added for mirror mode
(deftype sound-rpc-set-mirror-mode (sound-rpc-cmd)
((mirror sound-mirror-mode)))
(deftype sound-rpc-union (structure)
((data uint32 20)
@@ -439,6 +450,8 @@
(shutdown sound-rpc-shutdown :overlay-at (-> data 0))
(list-sounds sound-rpc-list-sounds :overlay-at (-> data 0))
(unload-music sound-rpc-unload-music :overlay-at (-> data 0))
;; og:preserve-this mirror mode
(mirror-mode sound-rpc-set-mirror-mode :overlay-at (-> data 0))
)
)
+14 -1
View File
@@ -482,6 +482,19 @@
0
)
;; og:preserve-this mirror mode
(define *sound-current-mirror* (sound-mirror-mode normal))
;; og:preserve-this mirror mode
(defun sound-set-mirror-mode ((mode sound-mirror-mode))
(when (!= mode *sound-current-mirror*)
(let ((cmd (the sound-rpc-set-mirror-mode (add-element *sound-loader-rpc*))))
(set! (-> cmd command) (sound-command set-mirror))
(set! (-> cmd mirror) mode))
(call *sound-loader-rpc* (the-as uint 0) (the-as pointer 0) (the-as uint 0))
(set! *sound-current-mirror* mode))
(none))
(define *sound-player-enable* #t)
(defun swap-sound-buffers ((arg0 vector) (arg1 vector) (arg2 vector) (arg3 vector) (arg4 vector) (arg5 float))
@@ -646,7 +659,7 @@
(sound-unit-vector-convert (-> gp-0 cam-forward) fwd)
(sound-unit-vector-convert (-> gp-0 cam-left) left)
(set! (-> gp-0 cam-scale) (the int (* 65536.0 scale)))
(set! (-> gp-0 cam-inverted) (if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
(set! (-> gp-0 cam-inverted) (if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
1
0
)
@@ -56,7 +56,9 @@
(set! (-> s5-0 x) (/ (-> s5-0 x) 2))
(set! (-> s5-0 z) (/ (-> s5-0 z) 2))
(set! (-> arg0 leftx) (the-as uint (the int (* 255.0 (-> s5-0 x)))))
(if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
*PC-MIRROR-MODE-SET*)
(set! (-> arg0 leftx) (the-as uint (* (the-as uint -1) (-> arg0 leftx))))
)
(set! (-> arg0 lefty) (the-as uint (the int (* 255.0 (-> s5-0 z)))))
@@ -592,14 +592,16 @@
0
)
(('off-to-left)
(logior! (-> this offscreen) (if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(logior! (-> this offscreen) (if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
2
1
)
)
)
(('off-to-right)
(logior! (-> this offscreen) (if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(logior! (-> this offscreen) (if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
1
2
)
+3 -1
View File
@@ -244,7 +244,9 @@
(f30-0 (vector-vector-angle-safe sv-140 (vector-! (new 'stack-no-clear 'vector) sv-128 sv-136)))
)
(set! sv-240 (atan (-> s2-4 x) (-> s2-4 y)))
(if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
*PC-MIRROR-MODE-SET*)
(set! sv-240 (* -1.0 sv-240))
)
(let ((v1-105 (new 'stack-no-clear 'vector)))
@@ -2278,7 +2278,8 @@
)
(set! s3-0 (logior s3-0 (ash 1 s1-0)))
(let ((v1-50 (cond
((logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
((or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
(case s1-0
((1)
3
@@ -3013,7 +3014,8 @@
(v1-6 (new 'stack-no-clear 'vector))
)
(let ((a0-7 (cond
((logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
((or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
(case (-> self index)
((1)
3
@@ -3075,7 +3077,8 @@
(v1-6 (new 'stack-no-clear 'vector))
)
(let ((a0-7 (cond
((logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
((or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
(case (-> self index)
((1)
3
+15 -7
View File
@@ -15,9 +15,12 @@
)
(when (wascity-turret-get-reticle-fire-pos s4-0)
(transform-point-vector! s5-0 s4-0)
(if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
(set! (-> s5-0 x) (- (-> s5-0 x)))
)
;; og:preserve-this mirror mode
;; Since we don't properly flip the screen, don't mess with the hud (or the reticule moves in the opposite direction)
;; (if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; *PC-MIRROR-MODE-SET*)
;; (set! (-> s5-0 x) (- (-> s5-0 x)))
;; )
(+! (-> s5-0 x) -1792.0)
(+! (-> s5-0 y) -1840.0)
(set-hud-piece-position! (-> this sprites 0) (the int (-> s5-0 x)) (the int (-> s5-0 y)))
@@ -62,7 +65,8 @@
(let ((s3-0 (the-as object (new 'stack-no-clear 'vector4w))))
(set! (-> (the-as vector4w s3-0) quad) (the-as uint128 0))
(when (transform-point-qword! (the-as vector4w s3-0) (-> this position s5-2))
(when (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(when (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
(+! (-> (the-as (pointer uint32) s3-0) 0) -32768)
(set! (-> (the-as (pointer uint32) s3-0) 0)
(the-as uint (- (the-as int (-> (the-as (pointer uint32) s3-0) 0))))
@@ -130,7 +134,9 @@
)
(b! (< (-> this minfo s5-3 pos y) (+ -20480.0 (get-base-height *ocean-map*))) cfg-53)
(transform-point-vector! s3-3 (-> this minfo s5-3 pos))
(if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
*PC-MIRROR-MODE-SET*)
(set! (-> s3-3 x) (- (-> s3-3 x)))
)
(+! (-> s3-3 x) -2048.0)
@@ -275,14 +281,16 @@
0
)
(('off-to-left)
(logior! (-> this offscreen) (if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(logior! (-> this offscreen) (if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
2
1
)
)
)
(('off-to-right)
(logior! (-> this offscreen) (if (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
;; og:preserve-this mirror mode
(logior! (-> this offscreen) (if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)) *PC-MIRROR-MODE-SET*)
1
2
)
+8
View File
@@ -496,6 +496,9 @@
(defmethod update-cheats ((obj pc-settings-jak3))
"run cheats."
;; sync mirror mode cheat
(set! *PC-MIRROR-MODE-SET* (logtest? (-> *game-info* secrets) (game-secrets hflip-screen)))
;; run cheats here.
;;;;;;;;;;;;;;;;;;;
@@ -537,6 +540,11 @@
(pc-set-gfx-hack (pc-gfx-hack no-tex) (pc-cheats? (-> obj cheats) no-textures))
(if (or (logtest? (-> *game-info* secrets) (game-secrets hflip-screen))
*PC-MIRROR-MODE-SET*)
(sound-set-mirror-mode (sound-mirror-mode mirrored))
(sound-set-mirror-mode (sound-mirror-mode normal)))
;; run cheats end!!!
;;;;;;;;;;;;;;;;;;;;
+1
View File
@@ -718,6 +718,7 @@
(shutdown sound-rpc-shutdown :overlay-at (-> data 0))
(list-sounds sound-rpc-list-sounds :overlay-at (-> data 0))
(unload-music sound-rpc-unload-music :overlay-at (-> data 0))
(mirror-mode sound-rpc-set-mirror-mode :overlay-at (-> data 0))
)
)