From d1260a0c0bc1b8658e12c67d0b0ac4535102a2d8 Mon Sep 17 00:00:00 2001 From: ManDude <7569514+ManDude@users.noreply.github.com> Date: Sun, 19 Mar 2023 20:30:23 +0000 Subject: [PATCH] [jak2] double load dgo workaround (#2331) fixes #2282 --- game/overlord/iso.cpp | 103 ++++++++++++++++++-------- game/overlord/iso.h | 6 ++ game/overlord/iso_queue.cpp | 20 ++--- goal_src/jak2/engine/level/level-h.gc | 4 +- goal_src/jak2/engine/level/level.gc | 11 ++- goal_src/jak2/game.gp | 11 +++ 6 files changed, 109 insertions(+), 46 deletions(-) diff --git a/game/overlord/iso.cpp b/game/overlord/iso.cpp index 0f47305485..77c34dcf0e 100644 --- a/game/overlord/iso.cpp +++ b/game/overlord/iso.cpp @@ -70,10 +70,14 @@ s32 iso_thread; s32 dgo_thread; s32 str_thread; s32 play_thread; -VagDir gVagDir; +VagDirJak2 gVagDir; u32 gPlayPos; -static RPC_Dgo_Cmd sRPCBuff[1]; // todo move... -DgoCommand scmd; +// todo move... +static RPC_Dgo_Cmd sRPCBuff[1]; +DgoCommand scmd; // renamed to sLoadDGO in Jak 2 +// :-) +#define sLoadDGO scmd + static VagCommand vag_cmd; VagCommand* gVAGCMD = nullptr; s32 gDialogVolume = 0; @@ -108,9 +112,8 @@ void iso_init_globals() { * Initialize the ISO Driver. * Requires a buffer large enough to hold 3 sector (or 4 if you have DUP files) */ +static MsgPacket not_on_stack_sync; void InitDriver(u8* buffer) { - MsgPacket msg_packet; - if (!isofs->init(buffer)) { // succesful init! iso_init_flag = 0; @@ -118,7 +121,10 @@ void InitDriver(u8* buffer) { // you idiots, you're giving the kernel a pointer to a stack variable! // (this is fixed in Jak 1 Japan and NTSC Greatest Hits) - SendMbx(sync_mbx, &msg_packet); + // SendMbx(sync_mbx, &msg_packet); + + // whoever fixed that bug felt similarly about it + SendMbx(sync_mbx, ¬_on_stack_sync); } /*! @@ -161,8 +167,10 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { // mark us as NOT initialized. iso_init_flag = 1; - while (!DMA_SendToSPUAndSync(&VAG_SilentLoop, 0x30, gTrapSRAM)) { - DelayThread(1000); + if (g_game_version == GameVersion::Jak1) { + while (!DMA_SendToSPUAndSync(&VAG_SilentLoop, 0x30, gTrapSRAM)) { + DelayThread(1000); + } } // INITIALIZE MESSAGE BOXES @@ -171,6 +179,9 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { mbx_param.option = 0; iso_mbx = CreateMbx(&mbx_param); if (iso_mbx <= 0) { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : Cannot create ISO mbx\n"); + printf("IOP: ======================================================================\n"); return 1; } @@ -178,6 +189,9 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { mbx_param.option = 0; dgo_mbx = CreateMbx(&mbx_param); if (dgo_mbx <= 0) { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : Cannot create DGO mbx\n"); + printf("IOP: ======================================================================\n"); return 1; } @@ -185,6 +199,9 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { mbx_param.option = 0; sync_mbx = CreateMbx(&mbx_param); if (sync_mbx <= 0) { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : Cannot create sync mbx\n"); + printf("IOP: ======================================================================\n"); return 1; } @@ -198,6 +215,9 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { strcpy(thread_param.name, "ISOThread"); iso_thread = CreateThread(&thread_param); if (iso_thread <= 0) { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : Cannot create ISO thread\n"); + printf("IOP: ======================================================================\n"); return 1; } @@ -209,6 +229,9 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { strcpy(thread_param.name, "DGOThread"); dgo_thread = CreateThread(&thread_param); if (dgo_thread <= 0) { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : Cannot create DGO thread\n"); + printf("IOP: ======================================================================\n"); return 1; } @@ -220,6 +243,9 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { strcpy(thread_param.name, "STRThread"); str_thread = CreateThread(&thread_param); if (str_thread <= 0) { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : Cannot create STR thread\n"); + printf("IOP: ======================================================================\n"); return 1; } @@ -231,6 +257,9 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { strcpy(thread_param.name, "PLAYThread"); play_thread = CreateThread(&thread_param); if (play_thread <= 0) { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : Cannot create PLAY thread\n"); + printf("IOP: ======================================================================\n"); return 1; } @@ -246,7 +275,12 @@ u32 InitISOFS(const char* fs_mode, const char* loading_screen) { // LOAD VAGDIR file FileRecord* vagdir_file = FindISOFile("VAGDIR.AYB"); if (vagdir_file) { - LoadISOFileToIOP(vagdir_file, &gVagDir, sizeof(gVagDir)); + LoadISOFileToIOP(vagdir_file, &gVagDir, + g_game_version == GameVersion::Jak1 ? sizeof(VagDir) : sizeof(VagDirJak2)); + } else { + printf("IOP: ======================================================================\n"); + printf("IOP : iso InitISOFS : cannot load VAG directory\n"); + printf("IOP: ======================================================================\n"); } FileRecord* loading_screen_file = FindISOFile(loading_screen); if (loading_screen_file) { @@ -381,18 +415,22 @@ u32 ISOThread() { } // Got a DGO command. There is one LoadDGO command for the entire DGO. // queued successfully, open the file. - auto* load_single_cmd = (IsoCommandLoadSingle*)msg_from_mbx; - load_single_cmd->fd = isofs->open(load_single_cmd->file_record, -1); - if (!load_single_cmd->fd) { + auto* load_dgo_cmd = (DgoCommand*)msg_from_mbx; + load_dgo_cmd->fd = isofs->open(load_dgo_cmd->fr, -1); + if (!load_dgo_cmd->fd) { // failed to open, return error - load_single_cmd->status = CMD_STATUS_FAILED_TO_OPEN; - UnqueueMessage(load_single_cmd); - ReturnMessage(load_single_cmd); + load_dgo_cmd->status = CMD_STATUS_FAILED_TO_OPEN; + UnqueueMessage(load_dgo_cmd); + if (g_game_version == GameVersion::Jak1) { + ReturnMessage(load_dgo_cmd); + } else { + SendMbx(iso_mbx, &sLoadDGO); + } } else { // init DGO state machine and register as the callback. - load_single_cmd->status = CMD_STATUS_IN_PROGRESS; - ((DgoCommand*)load_single_cmd)->dgo_state = DgoState::Init; - load_single_cmd->callback_function = RunDGOStateMachine; + load_dgo_cmd->status = CMD_STATUS_IN_PROGRESS; + load_dgo_cmd->dgo_state = DgoState::Init; + load_dgo_cmd->callback_function = RunDGOStateMachine; } } break; @@ -598,17 +636,19 @@ u32 ISOThread() { // Handle Sound //////////////////////////// - if (in_progress_vag_command && !CheckVAGStreamProgress(in_progress_vag_command)) { - gVAGCMD = nullptr; - StopVAG(in_progress_vag_command); - ReleaseMessage(in_progress_vag_command); - in_progress_vag_command = nullptr; - // added. this variable seems to determine whether a vag stream is actually playing, and it is - // possible to get into a scenario where (for example) you want to unpause a vag stream but a - // different sound command hasn't run yet to correct this value, which makes the game either - // play the wrong sound or crash right away if no actual sound is to be played with the vag - // stream - unk = 0; + if (g_game_version == GameVersion::Jak1) { + if (in_progress_vag_command && !CheckVAGStreamProgress(in_progress_vag_command)) { + gVAGCMD = nullptr; + StopVAG(in_progress_vag_command); + ReleaseMessage(in_progress_vag_command); + in_progress_vag_command = nullptr; + // added. this variable seems to determine whether a vag stream is actually playing, and it + // is possible to get into a scenario where (for example) you want to unpause a vag stream + // but a different sound command hasn't run yet to correct this value, which makes the game + // either play the wrong sound or crash right away if no actual sound is to be played with + // the vag stream + unk = 0; + } } //////////////////////////// @@ -1456,11 +1496,10 @@ void CancelDGO(RPC_Dgo_Cmd* cmd) { // this will cause a crash if we cancel because we try to load 2 dgos at the same time. // this should succeed if it's an actual cancel because we changed which level we're trying to // load. - // I don't understand how this works in the real game. - // maybe the IOP doesn't crash on writing to 0x0? + // This is weird in the original game, the IOP doesn't crash on writing to 0 // or, we have some other bug. + // NOTE : actually got fixed in Jak 2 so who cares if (cmd) { - printf("null pointer case in CancelDGO hit!\n"); cmd->result = DGO_RPC_RESULT_ABORTED; } diff --git a/game/overlord/iso.h b/game/overlord/iso.h index 456b5fa221..66287704b5 100644 --- a/game/overlord/iso.h +++ b/game/overlord/iso.h @@ -27,6 +27,12 @@ struct VagDir { VagDirEntry vag[VAG_COUNT]; }; +static constexpr int VAG_COUNT_JAK2 = 2728; +struct VagDirJak2 { + u32 count; + VagDirEntry vag[VAG_COUNT_JAK2]; +}; + void iso_init_globals(); FileRecord* FindISOFile(const char* name); u32 GetISOFileLength(FileRecord* f); diff --git a/game/overlord/iso_queue.cpp b/game/overlord/iso_queue.cpp index 1f7dcfe7c6..5a6fcd820e 100644 --- a/game/overlord/iso_queue.cpp +++ b/game/overlord/iso_queue.cpp @@ -163,7 +163,8 @@ void FreeBuffer(IsoBufferHeader* buffer) { void DisplayQueue() { for (int pri = 0; pri < N_PRIORITIES; pri++) { for (int cmd = 0; cmd < (int)gPriStack[pri].n; cmd++) { - lg::debug(" PRI {} elt {} {}", pri, cmd, gPriStack[pri].names[cmd]); + lg::debug(" PRI {} elt {} {} @ #x{:X}", pri, cmd, gPriStack[pri].names[cmd], + (u64)gPriStack[pri].cmds[cmd]); } } } @@ -180,7 +181,7 @@ u32 QueueMessage(IsoMessage* cmd, int32_t priority, const char* name) { gPriStack[priority].names[gPriStack[priority].n] = name; gPriStack[priority].n++; lg::debug("[OVERLORD] Queue {} ({}/{}), {}", priority, gPriStack[priority].n, PRI_STACK_LENGTH, - gPriStack[priority].names[gPriStack[priority].n - 1].c_str()); + name); DisplayQueue(); } else { lg::warn("[OVERLORD ISO QUEUE] Failed to queue!"); @@ -200,24 +201,25 @@ void UnqueueMessage(IsoMessage* cmd) { // loop over priorities for (pri = 0; pri < N_PRIORITIES; pri++) { - pse = gPriStack + pri; + pse = &gPriStack[pri]; // loop over entries - for (idx = 0; idx < gPriStack[pri].n; idx++) { + for (idx = 0; idx < pse->n; idx++) { if (pse->cmds[idx] == cmd) { goto found; } } } lg::warn("[OVERLORD ISO QUEUE] Failed to unqueue!"); + return; found: - ASSERT(gPriStack[pri].cmds[idx] == cmd); + ASSERT(pse->cmds[idx] == cmd); // pop - gPriStack[pri].n--; + pse->n--; // and move other entries up. - while (idx < gPriStack[pri].n) { + while (idx < pse->n) { pse->cmds[idx] = pse->cmds[idx + 1]; idx++; } @@ -233,8 +235,8 @@ found: IsoMessage* GetMessage() { // loop over all priorities for (int pri = (N_PRIORITIES - 1); pri >= 0; pri--) { - auto pse = gPriStack + pri; - int idx = gPriStack[pri].n; + auto pse = &gPriStack[pri]; + int idx = pse->n; for (idx = idx - 1; idx >= 0; idx--) { if (pse->cmds[idx]->fd && pse->cmds[idx]->status == CMD_STATUS_IN_PROGRESS && pse->cmds[idx]->ready_for_data) { diff --git a/goal_src/jak2/engine/level/level-h.gc b/goal_src/jak2/engine/level/level-h.gc index f2bf14ecd0..89a44dd9b2 100644 --- a/goal_src/jak2/engine/level/level-h.gc +++ b/goal_src/jak2/engine/level/level-h.gc @@ -26,8 +26,8 @@ (define-extern *level-load-list* pair) (defmacro start-debug (str &rest args) - `(format 0 ,(string-append "[START] " str) ,@args) - ) + ;`(format 0 ,(string-append "[START] " str) ,@args) + 0) (defenum load-buffer-mode diff --git a/goal_src/jak2/engine/level/level.gc b/goal_src/jak2/engine/level/level.gc index 97ec9f8f98..ad6cbdd022 100644 --- a/goal_src/jak2/engine/level/level.gc +++ b/goal_src/jak2/engine/level/level.gc @@ -414,7 +414,7 @@ into 7 sections, which might explain the weird sizes in the center. the level object. This function is the way to transition from loaded to alive/active." - (start-debug "level-status-update trying to do ~A to ~A~%" (-> obj status) arg0) + (start-debug "level-status-update trying to do ~A to ~A for ~A~%" (-> obj status) arg0 (-> obj name)) (case arg0 (('inactive) ;; any request to go inactive should unload. @@ -634,7 +634,6 @@ into 7 sections, which might explain the weird sizes in the center. (set! sv-16 (the-as symbol #f)) (let ((s5-0 (dgo-load-get-next (& sv-16)))) (when s5-0 - (start-debug "dgo-load-get-next: #x~X~%" s5-0) ;; we got something! remember where and update stats (set! (-> obj load-buffer-last) (the-as uint s5-0)) (+! (-> *level* load-size) (-> (the-as (pointer uint32) s5-0))) @@ -1039,6 +1038,7 @@ into 7 sections, which might explain the weird sizes in the center. ;; - we can't allocate during login. Or if we allocate more than our link data, then bad things happen. (set! (-> obj load-buffer-mode) (load-buffer-mode borrow)) (let ((a3-19 (logand -64 (&+ (-> obj heap current) 63)))) + (start-debug "DGO-LOAD-BEGIN FOR BORROW: #x~x~%" a3-19) ;; start dgo loader! (dgo-load-begin *temp-string* a3-19 a3-19 a3-19) ) @@ -2457,7 +2457,12 @@ into 7 sections, which might explain the weird sizes in the center. ) ) - (when (>= unload-idx 0) ;; did we find one to unload? + ;; did we find one to unload? + ;; PC NOTE : added an extra check for DGO time. If you start a load and discard it on the next frame, + ;; you may attempt to start a new load right away before the ISO thread can properly stop the previous load + ;; which will just crash the game. Sadly this means loads may sometimes be delayed by one frame. The horror. + (when ;(>= unload-idx 0) + (and (>= unload-idx 0) (< 1 (- (-> *display* real-clock integral-frame-counter) *dgo-time*))) (let ((lev-to-unload (-> *level* level unload-idx))) (format 0 "Discarding level ~A~%" (-> lev-to-unload name)) (level-status-update! lev-to-unload 'inactive) ;; kill it. diff --git a/goal_src/jak2/game.gp b/goal_src/jak2/game.gp index 930595398b..d8b9cff3ee 100644 --- a/goal_src/jak2/game.gp +++ b/goal_src/jak2/game.gp @@ -61,6 +61,15 @@ (cgo-file "kernel.gd" ()) +;;;;;;;;;;;;;;;;;;;;; +;; misc files +;;;;;;;;;;;;;;;;;;;;; + +;; the VAGDIR file +(defstep :in "$ISO/VAG/VAGDIR.AYB" + :tool 'copy + :out '("$OUT/iso/VAGDIR.AYB")) + ;;;;;;;;;;;;;;;;;;;;; ;; DGOs ;;;;;;;;;;;;;;;;;;;;; @@ -324,6 +333,8 @@ (group-list "iso" `("$OUT/iso/0COMMON.TXT" + "$OUT/iso/TWEAKVAL.MUS" + "$OUT/iso/VAGDIR.AYB" ,@(reverse *all-vis*) ,@(reverse *all-str*) ,@(reverse *all-sbk*)