#include "iso.h" #include #include #include "common/util/Assert.h" #include "game/common/dgo_rpc_types.h" #include "game/overlord/common/dma.h" #include "game/overlord/common/iso.h" #include "game/overlord/common/srpc.h" #include "game/overlord/jak2/iso_api.h" #include "game/overlord/jak2/iso_cd.h" #include "game/overlord/jak2/iso_queue.h" #include "game/overlord/jak2/spustreams.h" #include "game/overlord/jak2/srpc.h" #include "game/overlord/jak2/ssound.h" #include "game/overlord/jak2/stream.h" #include "game/overlord/jak2/streamlist.h" #include "game/overlord/jak2/vag.h" #include "game/runtime.h" #include "game/sce/iop.h" using namespace iop; namespace jak2 { void spu_dma_hack(); u32 DGOThread(); FileRecord* FindISOFile(const char* name); int LoadISOFileToIOP(FileRecord* fr, uint8_t* dest, int length); int RunDGOStateMachine(CmdHeader* param_1, Buffer* param_2); void CancelDGO(RPC_Dgo_Cmd* cmd); int CopyDataToIOP(CmdHeader* param_1, Buffer* param_2); int CopyDataToEE(CmdHeader* param_1, Buffer* param_2); IsoFs* isofs; int iso_init_flag = 0; int iso_mbx = 0; int dgo_mbx = 0; int sync_mbx = 0; int iso_thread = 0; int dgo_thread = 0; int str_thread = 0; int play_thread = 0; int ext_pause = 0; int ext_resume = 0; CmdDgo sLoadDgo; // renamed from scmd to sLoadDGO in Jak 2 static RPC_Dgo_Cmd sRPCBuff[1]; constexpr int kRpcBuffSize = sizeof(RPC_Dgo_Cmd); VagDir gVagDir; /// The main buffer used for reading data and doing blzo decompression. LargeBuffer* SpLargeBuffer = nullptr; void iso_init_globals() { isofs = nullptr; iso_init_flag = 0; iso_mbx = 0; dgo_mbx = 0; sync_mbx = 0; iso_thread = 0; dgo_thread = 0; str_thread = 0; play_thread = 0; ext_pause = 0; ext_resume = 0; memset(&sLoadDgo, 0, sizeof(sLoadDgo)); memset(&gVagDir, 0, sizeof(gVagDir)); memset(sRPCBuff, 0, sizeof(RPC_Dgo_Cmd)); SpLargeBuffer = nullptr; } // The "ISO Thread" is responsible managing "messages" - both starting reads for messages that need // data, and calling callbacks on messages that have pending data. /*! * Set up messageboxes/threads for the ISO thread. */ u32 InitISOFS() { isofs = &iso_cd; iso_init_flag = 1; SpLargeBuffer = (LargeBuffer*)ScratchPadMemory; ScratchPadMemory += sizeof(LargeBuffer); MbxParam mbx_param; mbx_param.attr = 0; mbx_param.option = 0; iso_mbx = CreateMbx(&mbx_param); if (iso_mbx < 1) { printf("IOP: ======================================================================\n"); printf("IOP: iso InitISOFS: Cannot create ISO mbx\n"); printf("IOP: ======================================================================\n"); return 1; } mbx_param.attr = 0; mbx_param.option = 0; dgo_mbx = CreateMbx(&mbx_param); if (dgo_mbx < 1) { printf("IOP: ======================================================================\n"); printf("IOP: iso InitISOFS: Cannot create DGO mbx\n"); printf("IOP: ======================================================================\n"); return 1; } mbx_param.attr = 0; mbx_param.option = 0; sync_mbx = CreateMbx(&mbx_param); if (sync_mbx < 1) { printf("IOP: ======================================================================\n"); printf("IOP: iso InitISOFS: Cannot create sync mbx\n"); printf("IOP: ======================================================================\n"); return 1; } ThreadParam thread_param; thread_param.entry = ISOThread; thread_param.initPriority = 0x6e; thread_param.attr = TH_C; thread_param.stackSize = 0x1000; thread_param.option = 0; iso_thread = CreateThread(&thread_param); if (iso_thread < 1) { printf("IOP: ======================================================================\n"); printf("IOP: iso InitISOFS: Cannot create ISO thread\n"); printf("IOP: ======================================================================\n"); return 1; } thread_param.entry = DGOThread; thread_param.attr = TH_C; thread_param.initPriority = 0x6f; thread_param.stackSize = 0x800; thread_param.option = 0; dgo_thread = CreateThread(&thread_param); if (dgo_thread < 1) { printf("IOP: ======================================================================\n"); printf("IOP: iso InitISOFS: Cannot create DGO thread\n"); printf("IOP: ======================================================================\n"); return 1; } thread_param.entry = STRThread; thread_param.attr = TH_C; thread_param.initPriority = 0x72; thread_param.stackSize = 0x800; thread_param.option = 0; str_thread = CreateThread(&thread_param); if (str_thread < 1) { printf("IOP: ======================================================================\n"); printf("IOP: iso InitISOFS: Cannot create STR thread\n"); printf("IOP: ======================================================================\n"); return 1; } thread_param.entry = PLAYThread; thread_param.attr = 0x2000000; thread_param.initPriority = 0x3c; thread_param.stackSize = 0x800; thread_param.option = 0; play_thread = CreateThread(&thread_param); if (play_thread < 1) { printf("IOP: ======================================================================\n"); printf("IOP: iso InitISOFS: Cannot create PLAY thread\n"); printf("IOP: ======================================================================\n"); return 1; } StartThread(iso_thread, 0); StartThread(dgo_thread, 0); StartThread(str_thread, 0); StartThread(play_thread, 0); // wait for ISO Thread to initialize WaitMbx(sync_mbx); FileRecord* vagdir_file = FindISOFile("VAGDIR.AYB"); if (vagdir_file) { LoadISOFileToIOP(vagdir_file, (u8*)&gVagDir, sizeof(gVagDir)); } else { printf("IOP: ======================================================================\n"); printf("IOP : iso InitISOFS : cannot load VAG directory\n"); printf("IOP: ======================================================================\n"); } // loading screen thing. /* iVar2 = FindISOFile(param_2); if (iVar2 == 0) { return iso_init_flag; } LoadISOFileToEE(iVar2, 0x1000000, 0x800000); */ return iso_init_flag; } void IsoQueueVagStream(VagCmd* cmd, int param_2) { VagCmd* new_cmd; LoadStackEntry* pLVar5; VagCmd* pVVar7; VagCmd* new_stereo_cmd; // we added a few null checks on vag_dir_entry. // as far as I can tell, it's "okay" that some animations (like Jak's idle animation) // don't have a corresponding audio. There's checks later on in this function that skip // trying to open the VAG file when this happens, but the stereo/mono checks don't have this // check. So we added them. if (param_2 == 1) { // CpuSuspendIntr(local_20); } new_stereo_cmd = nullptr; // allocate/find a vag cmd to hold this stream command. We don't own the incoming command. if ((cmd->id == 0) || (((cmd->vag_dir_entry && cmd->vag_dir_entry->flag & 1U) != 0 && // added null check (HowManyBelowThisPriority(cmd->priority, 0) < 2)))) goto LAB_000049dc; new_cmd = FindThisVagStream(cmd->name, cmd->id); if (!new_cmd) { new_cmd = SmartAllocVagCmd(cmd); if (!new_cmd) goto LAB_000049dc; if ((*(u32*)&new_cmd->status_bytes[BYTE4] & 0xffff00) != 0) { IsoStopVagStream(new_cmd, 0); } // copy data from the other command to us. new_cmd->header = cmd->header; new_cmd->file_record = cmd->file_record; new_cmd->vag_dir_entry = cmd->vag_dir_entry; strncpy(new_cmd->name, cmd->name, 0x30); new_cmd->unk_196 = cmd->unk_196; new_cmd->num_processed_chunks = cmd->num_processed_chunks; new_cmd->xfer_size = cmd->xfer_size; new_cmd->sample_rate = cmd->sample_rate; new_cmd->unk_260 = cmd->unk_260; new_cmd->unk_264 = cmd->unk_264; new_cmd->unk_268 = cmd->unk_268; new_cmd->vol_multiplier = cmd->vol_multiplier; new_cmd->unk_256_pitch2 = cmd->unk_256_pitch2; new_cmd->id = cmd->id; new_cmd->plugin_id = cmd->plugin_id; new_cmd->sound_handler = cmd->sound_handler; new_cmd->unk_176 = cmd->unk_176; new_cmd->unk_288 = cmd->unk_288; new_cmd->unk_292 = cmd->unk_292; new_cmd->unk_296 = cmd->unk_296; new_cmd->vec3.x = cmd->vec3.x; new_cmd->vec3.y = cmd->vec3.y; new_cmd->vec3.z = cmd->vec3.z; InitVAGCmd(new_cmd, 1); new_cmd->sb_scanned = true; // check for stereo if (cmd->vag_dir_entry && (new_cmd->vag_dir_entry->flag & 1U) != 0) { // added null check // try allocating it new_stereo_cmd = SmartAllocVagCmd(cmd); if (!new_stereo_cmd) { // no room for stereo, give up new_cmd->sb_scanned = false; ReleaseMessage(&new_cmd->header, 0); RemoveVagCmd(new_cmd, 0); FreeVagCmd(new_cmd, 0); new_cmd = nullptr; } else { // set up stereo command. // if ((*(u32*)&new_stereo_cmd->status_bytes[BYTE4] & 0xffff00) != 0) { if (new_stereo_cmd->byte5 || new_stereo_cmd->byte6) { IsoStopVagStream(new_stereo_cmd, 0); } // ADDED this line: seems random if this is set or not and other code relies on it not // being set. I don't understand why this doesn't happen on the real game, but it could just // be dma timing differences. new_stereo_cmd->byte21 = 0; new_stereo_cmd->status_bytes[BYTE11] = true; new_cmd->stereo_sibling = new_stereo_cmd; new_stereo_cmd->stereo_sibling = new_cmd; strncpy(new_stereo_cmd->name, "Stereo", 0x30); new_stereo_cmd->id = ~new_cmd->id; new_stereo_cmd->sb_scanned = true; new_stereo_cmd->vag_dir_entry = new_cmd->vag_dir_entry; } } if (new_cmd == nullptr) goto LAB_000049dc; // queue the command. if (QueueMessage(&new_cmd->header, 3, "QueueVAGStream", 0) == 0) { // queue failed, give up. new_cmd->sb_scanned = false; RemoveVagCmd(new_cmd, 0); FreeVagCmd(new_cmd, 0); if (cmd->vag_dir_entry && (new_cmd->vag_dir_entry->flag & 1U) != 0) { // added null check new_stereo_cmd->sb_scanned = false; RemoveVagCmd(new_stereo_cmd, 0); FreeVagCmd(new_stereo_cmd, 0); } ReleaseMessage(&new_cmd->header, 0); } else { // queue succeeded! open the file if (new_cmd->vag_dir_entry == nullptr) { (new_cmd->header).lse = nullptr; } else { pLVar5 = (isofs->open_wad)(new_cmd->file_record, new_cmd->vag_dir_entry->offset); (new_cmd->header).lse = pLVar5; } if (cmd->unk_288 != 0) { new_cmd->status_bytes[BYTE10] = true; } if (cmd->unk_292 != 0) { new_cmd->unk_232 = true; } new_cmd->sb_paused = true; // set up name/priority SetNewVagCmdPri(new_cmd, cmd->priority, 0); if (new_stereo_cmd != nullptr) { new_stereo_cmd->sb_scanned = true; new_stereo_cmd->sb_paused = true; SetNewVagCmdPri(new_stereo_cmd, 10, 0); } SetVagStreamName(new_cmd, 0x30, 0); if (new_stereo_cmd != nullptr) { SetVagStreamName(new_stereo_cmd, 0x30, 0); } // set up buffer (new_cmd->header).status = -1; (new_cmd->header).callback = ProcessVAGData; } if (new_cmd == nullptr) goto LAB_000049dc; } pLVar5 = (new_cmd->header).lse; pVVar7 = new_cmd->stereo_sibling; new_cmd->unk_188 = 0; new_cmd->unk_180 = 0; new_cmd->unk_184 = 0; new_cmd->unk_192 = 0; if (pLVar5 == nullptr) { new_cmd->status_bytes[BYTE6] = true; } else { new_cmd->status_bytes[BYTE5] = true; if (pVVar7 != nullptr) { pVVar7->status_bytes[BYTE5] = true; } } LAB_000049dc: if (param_2 == 1) { // CpuResumeIntr(local_20[0]); } } void IsoPlayVagStream(VagCmd* param_1, int param_2) { VagCmd* iVar1; LoadStackEntry* pLVar1; VagCmd* pVVar1; VagCmd* pRVar2; if (param_2 == 1) { // CpuSuspendIntr(local_20); } pRVar2 = param_1->stereo_sibling; iVar1 = FindThisVagStream(param_1->name, param_1->id); if (iVar1 != nullptr) { if (iVar1->status_bytes[BYTE4] == false) { iVar1->vol_multiplier = param_1->vol_multiplier; if (iVar1->sb_paused != false) { if (ext_pause != 0) { ext_resume = 1; } if (iVar1->sb_playing == false) { iVar1->sb_paused = false; if (pRVar2 != nullptr) { pRVar2->sb_paused = false; } } else { UnPauseVAG(iVar1, 0); } if ((u32)param_1->priority < 3) { SetNewVagCmdPri(param_1, 7, 0); } } iVar1->status_bytes[BYTE4] = true; if (pRVar2 != nullptr) { pRVar2->status_bytes[BYTE4] = true; } } else { iVar1 = nullptr; } if (iVar1 != nullptr) { pLVar1 = (iVar1->header).lse; pVVar1 = iVar1->stereo_sibling; iVar1->unk_188 = 0; iVar1->unk_180 = 0; iVar1->unk_184 = 0; iVar1->unk_192 = 0; if (pLVar1 == nullptr) { iVar1->status_bytes[BYTE6] = true; } else { iVar1->status_bytes[BYTE5] = true; if (pVVar1 != nullptr) { pVVar1->status_bytes[BYTE5] = true; } } } } if (param_2 == 1) { // CpuResumeIntr(local_20[0]); } } u32 _not_on_stack_sync = 0; u32 IsoThreadCounter = 0; u32 ISOThread() { bool bVar1; CmdLoadSingleIop* inasdf; Buffer* pBVar3; int iVar4; LoadStackEntry* pLVar5; Page* pages; VagCmd* pRVar7; Buffer* pBVar8; Buffer* pBVar9; VagCmd* pRVar10; int iVar11; FileRecord* pFVar12; VagStrListNode* pLVar14; CmdLoadSingleIop* local_30; InitBuffers(); InitVagCmds(); InitSpuStreamsThread(); pBVar3 = AllocateBuffer(1, nullptr, 1); iVar4 = (isofs->init)(/*pBVar3->unk_12*/); if (iVar4 == 0) { iso_init_flag = 0; } SendMbx(sync_mbx, &_not_on_stack_sync); FreeBuffer(pBVar3, 1); do { spu_dma_hack(); IsoThreadCounter = IsoThreadCounter + 1; // iVar4 = PollMbx(&local_30, iso_mbx); iVar4 = PollMbx((MsgPacket**)(&local_30), iso_mbx); inasdf = local_30; auto* in_dgo = (CmdDgo*)local_30; auto* in_sbk = (CmdLoadSoundBank*)local_30; auto* in_music = (CmdLoadMusic*)local_30; auto* in_vag = (VagCmd*)local_30; if (iVar4 == 0) { iVar4 = (local_30->header).cmd_kind; (local_30->header).callback_buffer = (Buffer*)nullptr; (local_30->header).ready_for_data = 1; (local_30->header).callback = NullCallback; (local_30->header).lse = (LoadStackEntry*)nullptr; if (iVar4 - 0x100U < 3) { iVar4 = QueueMessage(&local_30->header, 2, "LoadSingle", 1); if (iVar4 != 0) { if ((inasdf->header).cmd_kind == 0x102) { pFVar12 = inasdf->file_record; iVar4 = inasdf->offset; } else { iVar4 = -1; pFVar12 = inasdf->file_record; } pLVar5 = (isofs->open)(pFVar12, iVar4, OpenMode::KNOWN_NOT_BLZO); (inasdf->header).lse = pLVar5; if ((inasdf->header).lse == (LoadStackEntry*)nullptr) { (inasdf->header).status = 6; UnqueueMessage(&inasdf->header, 1); LAB_00005120: ReturnMessage(&inasdf->header); } else { inasdf->unk_64 = 0; inasdf->ptr = inasdf->dest_addr; iVar4 = (isofs->get_length)(inasdf->file_record); inasdf->length_to_copy = iVar4; if (iVar4 == 0) { inasdf->length_to_copy = inasdf->length; } else if (inasdf->length < iVar4) { inasdf->length_to_copy = inasdf->length; } iVar4 = (inasdf->header).cmd_kind; if (iVar4 == 0x101) { (inasdf->header).callback = CopyDataToIOP; } else { if (iVar4 < 0x102) { if (iVar4 != 0x100) { (inasdf->header).status = -1; goto LAB_00005144; } } else if (iVar4 != 0x102) goto LAB_00004e50; (inasdf->header).callback = CopyDataToEE; } LAB_00004e50: (inasdf->header).status = -1; } } } else if (iVar4 == 0x200) { iVar4 = QueueMessage(&local_30->header, 0, "LoadDGO", 1); if (iVar4 != 0) { pLVar5 = (LoadStackEntry*)(isofs->open)(in_dgo->fr, -1, OpenMode::MODE1); (in_dgo->header).lse = pLVar5; if (pLVar5 != (LoadStackEntry*)nullptr) { in_dgo->dgo_state = DgoState::Init; (in_dgo->header).callback = RunDGOStateMachine; goto LAB_00004e50; } UnqueueMessage(&in_dgo->header, 1); SendMbx(iso_mbx, &sLoadDgo); } } else { if (iVar4 == 0x300) { gSoundInUse = gSoundInUse + 1; if (gSoundEnable != 0) { pages = (Page*)AllocPages(SpMemoryBuffers, 1); if (pages == (Page*)nullptr) { LAB_00004f28: SendMbx(iso_mbx, inasdf); } else { SetBufferMem(pages->buffer, SpMemoryBuffers->page_size); // iVar4 = inasdf->maybe_offset; // pcVar6 = (code*)isofs->load_sound_bank; iVar4 = isofs->load_sound_bank(in_sbk->bank_name, in_sbk->bank); LAB_00004f6c: // iVar4 = (*pcVar6)(&inasdf->file_record, iVar4); (inasdf->header).status = iVar4; ReleaseBufferMem(); FreePagesList(SpMemoryBuffers, pages); } } } else { if (iVar4 != 0x380) { if (iVar4 == 0x403) { if (ext_pause == 0) { SetVagStreamsNoStart(1, 1); iVar4 = AnyVagRunning(); if (iVar4 != 0) { PauseVagStreams(); } ext_resume = (u32)(iVar4 != 0); ext_pause = 1; } } else if (iVar4 == 0x404) { if (ext_pause != 0) { if (ext_resume != 0) { UnPauseVagStreams(); // 0? } ext_pause = 0; ext_resume = 0; } SetVagStreamsNoStart(0, 1); } else if (iVar4 == 0x405) { pRVar7 = FindVagStreamId(in_vag->id); if (pRVar7 != nullptr) { pRVar7->vol_multiplier = in_vag->vol_multiplier; SetVAGVol(pRVar7, 1); } } else if (iVar4 == 0x406) { pRVar7 = FindVagStreamId(in_vag->id); if (pRVar7 != nullptr) { pRVar7->unk_256_pitch2 = in_vag->unk_256_pitch2; SetVAGVol(pRVar7, 1); } } else if (iVar4 == 0x407) { MasterVolume[2] = in_vag->vol_multiplier; SetAllVagsVol(-1); } else { if ((local_30->header).cmd_kind == 0x666) { ReturnMessage(&local_30->header); ASSERT_NOT_REACHED(); } (local_30->header).status = 4; } goto LAB_00005120; } gSoundInUse = gSoundInUse + 1; if (gSoundEnable != 0) { pages = (Page*)AllocPages(SpMemoryBuffers, 1); if (pages == (Page*)nullptr) goto LAB_00004f28; SetBufferMem(pages->buffer, SpMemoryBuffers->page_size); iVar4 = inasdf->offset; // pcVar6 = (code*)isofs->load_music; iVar4 = isofs->load_music(in_music->name, in_music->handle); goto LAB_00004f6c; } } gSoundInUse = gSoundInUse + -1; ReturnMessage(&inasdf->header); } } else if (iVar4 == -0x1a9) { return 0; } LAB_00005144: pBVar3 = (Buffer*)nullptr; pRVar7 = (VagCmd*)GetMessage(); if (pRVar7 == nullptr) { LAB_00005208:; // (isofs->poll_drive)(); } else { u32 uVar13 = 1; if ((pRVar7->header).callback == ProcessVAGData) { if (pRVar7->xfer_size != 0) { LAB_000051ac: uVar13 = 2; goto LAB_000051b0; } // no idea what happens here.. i think this is a bug and the callback_buffer == 0 should // flip. if (((pRVar7->header).callback_buffer == (Buffer*)nullptr) && (uVar13 = 2, /*DAT_00000008 == 0*/ true)) goto LAB_000051b0; if (pRVar7->xfer_size != 0) goto LAB_000051ac; } else { LAB_000051b0: pBVar3 = AllocateBuffer(uVar13, pRVar7, 1); } if (pBVar3 == (Buffer*)nullptr) { LAB_000051fc: pRVar7 = nullptr; } else { iVar4 = (isofs->page_begin_read)((pRVar7->header).lse, pBVar3); (pRVar7->header).status = iVar4; if (iVar4 != -1) { FreeBuffer(pBVar3, 1); pBVar3 = (Buffer*)nullptr; goto LAB_000051fc; } } if (pRVar7 == nullptr) goto LAB_00005208; } ProcessMessageData(); if (pBVar3 != (Buffer*)nullptr) { iVar4 = (isofs->sync_read)(/*pBVar3*/); if (iVar4 == 8) { FreeBuffer(pBVar3, 1); pBVar3 = (Buffer*)nullptr; } else { (pRVar7->header).status = iVar4; pBVar3->decomp_buffer = (uint8_t*)pBVar3->unk_12; pBVar8 = (pRVar7->header).callback_buffer; if (pBVar8 == (Buffer*)nullptr) { (pRVar7->header).callback_buffer = pBVar3; } else { pBVar9 = pBVar8->next; while (pBVar9 != (Buffer*)nullptr) { pBVar8 = pBVar8->next; pBVar9 = pBVar8->next; } pBVar8->next = pBVar3; } pBVar3 = (Buffer*)nullptr; } } WaitSema(RequestedStreamsList.sema); if (RequestedStreamsList.unk2_init0 == 1) { QueueNewStreamsFromList(&RequestedStreamsList); iVar4 = 0; pLVar14 = (VagStrListNode*)NewStreamsList.next; do { if (pLVar14->id != 0) { if (g_game_version != GameVersion::Jak3) { // doesn't work. printf("jak3 skipping vag stream\n"); QueueVAGStream(pLVar14); } } pLVar14 = (VagStrListNode*)pLVar14->list.next; iVar4 = iVar4 + 1; } while (iVar4 < 4); } pRVar7 = VagCmds; iVar4 = 0; // pcVar15 = VagCmds[0].name; auto* cmd_iter = VagCmds; do { if ((((cmd_iter->byte11 == false) && (cmd_iter->sb_scanned == false)) && (cmd_iter->id != 0)) || ((StopPluginStreams == 1 && (cmd_iter->sound_handler) != 0))) { // CpuSuspendIntr(&local_2c); bVar1 = false; if (cmd_iter->id == 0) { if (cmd_iter->name[0] != false) { while (pRVar10 = FindVagStreamName(pRVar7->name), pRVar10 != nullptr) { TerminateVAG(pRVar10, 0); bVar1 = true; } } } else { pRVar10 = FindThisVagStream(cmd_iter->name, cmd_iter->id); if (pRVar10 != nullptr) { TerminateVAG(pRVar10, 0); bVar1 = true; } } if ((bVar1) && (iVar11 = AnyVagRunning(), iVar11 == 0)) { ext_pause = 0; ext_resume = 0; } // CpuResumeIntr(local_2c); } // pcVar15 = pcVar15 + 0x144; cmd_iter++; iVar4 = iVar4 + 1; pRVar7 = pRVar7 + 1; } while (iVar4 < 4); SignalSema(RequestedStreamsList.sema); RequestedStreamsList.unk2_init0 = 0; if (pBVar3 == (Buffer*)nullptr) { bool should_sleep = true; if (PeekMbx(iso_mbx)) { should_sleep = false; } auto* msg = GetMessage(); if (msg && msg->callback != ProcessVAGData) { should_sleep = false; } if (should_sleep) { DelayThread(1000); } } } while (true); } int RunDGOStateMachine(CmdHeader* param_1_in, Buffer* param_2) { auto* param_1 = (CmdDgo*)param_1_in; uint8_t* puVar1; size_t bytes_to_read; size_t sVar2; int iVar3; int iVar4; size_t sVar5; size_t bytes_left; uint8_t* unprocessed_data; int return_value; return_value = -1; bytes_left = param_2->decompressed_size; unprocessed_data = param_2->decomp_buffer; do { if (bytes_left == 0) goto LAB_000059b4; switch (param_1->dgo_state) { case DgoState::Init: param_1->bytes_processed = 0; param_1->dgo_state = DgoState::Read_Header; param_1->finished_first_object = 0; param_1->want_abort = 0; break; case DgoState::Read_Header: bytes_to_read = 0x40 - param_1->bytes_processed; if ((int)bytes_left < (int)bytes_to_read) { bytes_to_read = bytes_left; } for (; bytes_to_read != 0; bytes_to_read = bytes_to_read - sVar5) { sVar2 = (param_2->page->ptr - unprocessed_data) + 1; sVar5 = bytes_to_read; if ((int)sVar2 < (int)bytes_to_read) { sVar5 = sVar2; } bytes_left = bytes_left - sVar5; memcpy((param_1->dgo_header).name + param_1->bytes_processed + -4, unprocessed_data, sVar5); param_2->decomp_buffer = param_2->decomp_buffer + sVar5; param_2->decompressed_size = param_2->decompressed_size - sVar5; unprocessed_data = (uint8_t*)CheckForIsoPageBoundaryCrossing(param_2); param_1->bytes_processed = param_1->bytes_processed + sVar5; } if (param_1->bytes_processed == 0x40) { iVar4 = (param_1->dgo_header).object_count; param_1->bytes_processed = 0; param_1->objects_loaded = 0; if (iVar4 == 1) { LAB_00005990: puVar1 = param_1->buffer_heaptop; param_1->buffer_toggle = 0; } else { puVar1 = param_1->buffer1; param_1->buffer_toggle = 1; } param_1->dgo_state = DgoState::Read_Obj_Header; param_1->ee_dest_buffer = puVar1; } break; case DgoState::Finish_Obj: if (param_1->finished_first_object == 0) { LAB_00005870: if (param_1->buffer1 == param_1->buffer2) goto LAB_000058c4; iVar4 = param_1->buffer_toggle; (param_1->header).status = -1; if (iVar4 == 1) { puVar1 = param_1->buffer1; } else { puVar1 = param_1->buffer2; } param_1->selected_buffer = puVar1; ReturnMessage(¶m_1->header); if (param_1->buffer1 == param_1->buffer2) goto LAB_000058c4; } else { if (param_1->buffer1 != param_1->buffer2) { if (LookMbx(sync_mbx)) { if (param_1->want_abort != 0) goto LAB_00005988; goto LAB_00005870; } goto LAB_000059b4; } LAB_000058c4: if (param_1->objects_loaded + 1 < (param_1->dgo_header).object_count) { if (!LookMbx(sync_mbx)) goto LAB_000059b4; if (param_1->want_abort != 0) goto LAB_00005988; } } param_1->finished_first_object = 1; if (param_1->buffer_toggle == 1) { param_1->buffer_toggle = 2; param_1->ee_dest_buffer = param_1->buffer2; } else { param_1->buffer_toggle = 1; param_1->ee_dest_buffer = param_1->buffer1; } if (param_1->objects_loaded + 1 == (param_1->dgo_header).object_count) { param_1->dgo_state = DgoState::Read_Last_Obj; } else { param_1->dgo_state = DgoState::Read_Obj_Header; } break; case DgoState::Read_Last_Obj: if (!LookMbx(sync_mbx)) goto LAB_000059b4; if (param_1->want_abort == 0) goto LAB_00005990; LAB_00005988: param_1->dgo_state = DgoState::Finish_Dgo; break; case DgoState::Read_Obj_Header: bytes_to_read = 0x40 - param_1->bytes_processed; if ((int)bytes_left < (int)bytes_to_read) { bytes_to_read = bytes_left; } for (; bytes_to_read != 0; bytes_to_read = bytes_to_read - sVar5) { sVar2 = (param_2->page->ptr - unprocessed_data) + 1; sVar5 = bytes_to_read; if ((int)sVar2 < (int)bytes_to_read) { sVar5 = sVar2; } bytes_left = bytes_left - sVar5; memcpy((param_1->obj_header).name + param_1->bytes_processed + -4, unprocessed_data, sVar5); param_2->decomp_buffer = param_2->decomp_buffer + sVar5; param_2->decompressed_size = param_2->decompressed_size - sVar5; unprocessed_data = (uint8_t*)CheckForIsoPageBoundaryCrossing(param_2); param_1->bytes_processed = param_1->bytes_processed + sVar5; } if (param_1->bytes_processed == 0x40) { DMA_SendToEE(¶m_1->obj_header, 0x40, param_1->ee_dest_buffer); param_1->dgo_state = DgoState::Read_Obj_data; iVar4 = (param_1->obj_header).size; param_1->bytes_processed = 0; param_1->ee_dest_buffer = param_1->ee_dest_buffer + 0x40; (param_1->obj_header).size = iVar4 + 0xfU & 0xfffffff0; } break; case DgoState::Read_Obj_data: bytes_to_read = (param_1->obj_header).size - param_1->bytes_processed; if ((int)bytes_left < (int)bytes_to_read) { bytes_to_read = bytes_left; } for (; bytes_to_read != 0; bytes_to_read = bytes_to_read - sVar5) { sVar2 = (param_2->page->ptr - unprocessed_data) + 1; sVar5 = bytes_to_read; if ((int)sVar2 < (int)bytes_to_read) { sVar5 = sVar2; } bytes_left = bytes_left - sVar5; DMA_SendToEE(unprocessed_data, sVar5, param_1->ee_dest_buffer); param_2->decomp_buffer = param_2->decomp_buffer + sVar5; param_2->decompressed_size = param_2->decompressed_size - sVar5; unprocessed_data = (uint8_t*)CheckForIsoPageBoundaryCrossing(param_2); param_1->ee_dest_buffer = param_1->ee_dest_buffer + sVar5; param_1->bytes_processed = param_1->bytes_processed + sVar5; } if (param_1->bytes_processed == (param_1->obj_header).size) { iVar3 = (param_1->dgo_header).object_count; iVar4 = param_1->objects_loaded + 1; param_1->objects_loaded = iVar4; if (iVar4 < iVar3) { DgoState nstate = DgoState::Finish_Obj_NoDoubleBuffer; if (param_1->buffer1 != param_1->buffer2) { nstate = DgoState::Finish_Obj; } param_1->dgo_state = nstate; param_1->bytes_processed = 0; } else { param_1->dgo_state = DgoState::Finish_Dgo; return_value = 0; } } break; case DgoState::Finish_Dgo: return_value = 0; LAB_000059b4: if ((return_value == 0) || (bytes_left == 0)) { param_2->decomp_buffer = (uint8_t*)nullptr; param_2->decompressed_size = 0; } else { param_2->decomp_buffer = unprocessed_data; param_2->decompressed_size = bytes_left; } return return_value; case DgoState::Finish_Obj_NoDoubleBuffer: iVar4 = param_1->buffer_toggle; (param_1->header).status = -1; if (iVar4 == 1) { puVar1 = param_1->buffer1; } else { puVar1 = param_1->buffer2; } param_1->selected_buffer = puVar1; ReturnMessage(¶m_1->header); param_1->dgo_state = DgoState::Finish_Obj; } } while (true); } void LoadDGO(RPC_Dgo_Cmd* param_1) { FileRecord* iVar1; iVar1 = (isofs->find)(param_1->name); if (iVar1 == 0) { printf("overlord couldn't find dgo: %s\n", param_1->name); param_1->result = 1; } else { CancelDGO(0); sLoadDgo.header.cmd_kind = 0x200; sLoadDgo.header.thread_id = 0; sLoadDgo.header.mbx_to_reply = dgo_mbx; sLoadDgo.buffer1 = (uint8_t*)(u64)param_1->buffer1; sLoadDgo.buffer2 = (uint8_t*)(u64)param_1->buffer2; sLoadDgo.buffer_heaptop = (uint8_t*)(u64)param_1->buffer_heap_top; sLoadDgo.fr = iVar1; SendMbx(iso_mbx, &sLoadDgo); // wait for the ReturnMessage in the DGO callback state machine. // this happens when the first file is loaded WaitMbx(dgo_mbx); if (sLoadDgo.header.status == -1) { param_1->result = 2; } else { if (sLoadDgo.header.status == 0) { param_1->result = 0; param_1->buffer1 = param_1->buffer_heap_top; } else { param_1->result = 1; } sLoadDgo.header.cmd_kind = 0; } } } int CopyData(CmdLoadSingleIop* param_1, Buffer* param_2, int param_3) { size_t sVar1; Page* pPVar2; uint8_t* src; size_t n; pPVar2 = param_2->page; if (param_2->decompressed_size != 0) { n = param_1->length_to_copy - param_1->unk_64; if (pPVar2 != (Page*)nullptr) { while (0 < (int)n) { n = param_1->length_to_copy - param_1->unk_64; if (param_2->decompressed_size < (int)n) { n = param_2->decompressed_size; } src = param_2->decomp_buffer; sVar1 = (pPVar2->ptr - src) + 1; if ((int)sVar1 < (int)n) { n = sVar1; } if (param_3 == 0) { DMA_SendToEE(src, n, param_1->ptr); } else if (param_3 == 1) { memcpy(param_1->ptr, src, n); } param_1->ptr = param_1->ptr + n; param_1->unk_64 = param_1->unk_64 + n; param_2->decomp_buffer = param_2->decomp_buffer + n; param_2->decompressed_size = param_2->decompressed_size - n; CheckForIsoPageBoundaryCrossing(param_2); pPVar2 = param_2->page; if ((u32)param_1->length_to_copy <= (u32)param_1->unk_64) { if (pPVar2 != (Page*)nullptr) { pPVar2->state = PageState::SIX; pPVar2 = (Page*)StepTopPage(param_2->plist, pPVar2); param_2->page = pPVar2; param_2->free_pages = param_2->free_pages + -1; } break; } if (pPVar2 == (Page*)nullptr) break; } } if ((u32)param_1->length_to_copy <= (u32)param_1->unk_64) { return 0; } } return -1; } /*! * Find a file by name. Return nullptr if it fails. */ FileRecord* FindISOFile(const char* name) { return isofs->find(name); } // FindVAGFile in common u32 GetISOFileLength(FileRecord* fr) { return isofs->get_length(fr); } int NullCallback(CmdHeader* cmd, Buffer* buff) { (void)cmd; buff->decompressed_size = 0; return CMD_STATUS_NULL_CB; } void IsoStopVagStream(VagCmd* param_1, int param_2) { bool bVar1; VagCmd* pRVar2; int iVar3; if (param_2 == 1) { // CpuSuspendIntr(local_18); } bVar1 = false; if (param_1->id == 0) { if (param_1->name[0] != false) { while (pRVar2 = FindVagStreamName(param_1->name), pRVar2 != nullptr) { TerminateVAG(pRVar2, 0); bVar1 = true; } } } else { pRVar2 = FindThisVagStream(param_1->name, param_1->id); if (pRVar2 != nullptr) { TerminateVAG(pRVar2, 0); bVar1 = true; } } if ((bVar1) && (iVar3 = AnyVagRunning(), iVar3 == 0)) { ext_pause = 0; ext_resume = 0; } if (param_2 == 1) { // CpuResumeIntr(local_18[0]); } } int CopyDataToIOP(CmdHeader* param_1, Buffer* param_2) { return CopyData((CmdLoadSingleIop*)param_1, param_2, 1); } int CopyDataToEE(CmdHeader* param_1, Buffer* param_2) { return CopyData((CmdLoadSingleIop*)param_1, param_2, 0); } void* RPC_DGO(unsigned int fno, void* _cmd, int y); void LoadNextDGO(RPC_Dgo_Cmd* cmd); u32 DGOThread() { sceSifQueueData dq; sceSifServeData serve; // setup RPC. CpuDisableIntr(); sceSifInitRpc(0); sceSifSetRpcQueue(&dq, GetThreadId()); sceSifRegisterRpc(&serve, DGO_RPC_ID[g_game_version], RPC_DGO, sRPCBuff, kRpcBuffSize, nullptr, nullptr, &dq); CpuEnableIntr(); sceSifRpcLoop(&dq); return 0; } void* RPC_DGO(unsigned int param_1, void* param_2, int) { if (param_1 == 0) { LoadDGO((RPC_Dgo_Cmd*)param_2); } else if (param_1 == 1) { LoadNextDGO((RPC_Dgo_Cmd*)param_2); } else if (param_1 == 2) { CancelDGO((RPC_Dgo_Cmd*)param_2); } else { ((RPC_Dgo_Cmd*)param_2)->result = 1; } return param_2; } void LoadNextDGO(RPC_Dgo_Cmd* param_1) { if (sLoadDgo.header.cmd_kind == 0) { param_1->result = 1; } else { sLoadDgo.buffer_heaptop = (uint8_t*)(u64)param_1->buffer_heap_top; sLoadDgo.buffer1 = (uint8_t*)(u64)param_1->buffer1; sLoadDgo.buffer2 = (uint8_t*)(u64)param_1->buffer2; SendMbx(sync_mbx, nullptr /*&a*/); // no idea why they put an address here.. WaitMbx(dgo_mbx); if (sLoadDgo.header.status == -1) { param_1->result = 2; param_1->buffer1 = (u32)(u64)sLoadDgo.selected_buffer; } else { if (sLoadDgo.header.status == 0) { param_1->result = 0; param_1->buffer1 = param_1->buffer_heap_top; } else { param_1->result = 1; } sLoadDgo.header.cmd_kind = 0; } } } void CancelDGO(RPC_Dgo_Cmd* param_1) { if (sLoadDgo.header.cmd_kind != 0) { sLoadDgo.want_abort = 1; SendMbx(sync_mbx, nullptr); // was some stack addr... WaitMbx(dgo_mbx); if (param_1 != (RPC_Dgo_Cmd*)nullptr) { param_1->result = 3; } sLoadDgo.header.cmd_kind = 0; } } void InitDriver() { int iVar1; iVar1 = (isofs->init)(); if (iVar1 == 0) { iso_init_flag = 0; } SendMbx(sync_mbx, &_not_on_stack_sync); } void SetVagClock(VagCmd* param_1, int param_2) { LoadStackEntry* pLVar1; if (param_2 == 1) { // CpuSuspendIntr(local_18); } pLVar1 = (param_1->header).lse; param_1->unk_188 = 0; param_1->unk_180 = 0; param_1->unk_184 = 0; param_1->unk_192 = 0; if (pLVar1 == (LoadStackEntry*)nullptr) { param_1->byte6 = true; } else { param_1->byte5 = true; if (param_1->stereo_sibling != (VagCmd*)nullptr) { param_1->stereo_sibling->byte5 = true; } } if (param_2 == 1) { // CpuResumeIntr(local_18[0]); } } /*! * Find VAG file by "name", where name is 8 bytes (chars with spaces at the end, treated as two * s32's). Returns pointer to name in the VAGDIR file data. */ VagDirEntry* FindVAGFile(const char* name) { VagDirEntry* entry = gVagDir.vag; for (u32 idx = 0; idx < gVagDir.count; idx++) { // check if matching name if (memcmp(entry->name, name, 8) == 0) { return entry; } entry++; } return nullptr; } } // namespace jak2