mirror of
https://github.com/open-goal/jak-project
synced 2026-08-06 18:03:30 -04:00
d3cc739e43
This attempts to get into master whatever work was done in this PR / it's earlier PR https://github.com/open-goal/jak-project/pull/3965 I don't want this work to be lost / floating around in massive PRs. However the changes are: - switch to ntsc_v1 instead of PAL as the development target, as we have done for all other games - remove most of the copied-from-jak2/3 changes as they need to be confirmed during the decompilation process not just assumed - avoids committing any changes to `game/kernel/common` as it was not clear to me if these were changes made in jak x's kernel that were not properly broken out into it's own functions. We don't want to accidentally introduce bugs into jak1-3's kernel code. - in other words, if the change in the kernel only happens in jak x...it should likely be specific to jak x's kernel, not common. --------- Co-authored-by: VodBox <dillon@vodbox.io> Co-authored-by: yodah <greenboyyodah@gmail.com>
1787 lines
60 KiB
C++
1787 lines
60 KiB
C++
#include "iso.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include "common/util/Assert.h"
|
|
|
|
#include "game/overlord/jakx/dma.h"
|
|
#include "game/overlord/jakx/iso_api.h"
|
|
#include "game/overlord/jakx/iso_cd.h"
|
|
#include "game/overlord/jakx/iso_queue.h"
|
|
#include "game/overlord/jakx/overlord.h"
|
|
#include "game/overlord/jakx/rpc_interface.h"
|
|
#include "game/overlord/jakx/sbank.h"
|
|
#include "game/overlord/jakx/spustreams.h"
|
|
#include "game/overlord/jakx/srpc.h"
|
|
#include "game/overlord/jakx/ssound.h"
|
|
#include "game/overlord/jakx/stream.h"
|
|
#include "game/overlord/jakx/streamlist.h"
|
|
#include "game/overlord/jakx/vag.h"
|
|
#include "game/sce/iop.h"
|
|
#include "game/sound/sndshim.h"
|
|
|
|
using namespace iop;
|
|
|
|
namespace jakx {
|
|
int g_nISOInitFlag = 0;
|
|
u32 s_MsgPacket_NotOnStackSync = 0;
|
|
int g_nSyncMbx = -1;
|
|
int g_nISOMbx = -1;
|
|
int g_nDGOMbx = -1;
|
|
int g_nDGOThread = -1;
|
|
int g_nSTRThreadID = -1;
|
|
int g_nISOThreadID = -1;
|
|
int g_nPlayThreadID = -1;
|
|
int g_nMusicFadeDir = 0;
|
|
int g_nMusicFade = 0;
|
|
int g_nMusicTweak = 0;
|
|
int g_nMusicSemaphore = 0;
|
|
bool g_bMusicIsPaused = false;
|
|
bool g_bMusicPause = false;
|
|
bool g_bAnotherMusicPauseFlag = false;
|
|
char g_szCurrentMusicName[0x30];
|
|
char g_szTargetMusicName[0x30];
|
|
int g_nActiveMusicStreams = 0;
|
|
bool g_bVagCmdsInitialized = false;
|
|
u32 time_of_last_unknown_rate_drive_op = 0;
|
|
ISO_DGOCommand sLoadDGO;
|
|
RPC_Dgo_Cmd sRPCBuff[1];
|
|
constexpr int kRpcBuffSize = sizeof(RPC_Dgo_Cmd);
|
|
|
|
void jakx_overlord_init_globals_iso() {
|
|
g_nISOInitFlag = 0;
|
|
s_MsgPacket_NotOnStackSync = 0;
|
|
g_nSyncMbx = -1;
|
|
g_nISOMbx = -1;
|
|
g_nDGOMbx = -1;
|
|
g_nDGOThread = -1;
|
|
g_nSTRThreadID = -1;
|
|
g_nISOThreadID = -1;
|
|
g_nPlayThreadID = -1;
|
|
g_nMusicFadeDir = 0;
|
|
g_nMusicFade = 0;
|
|
g_nMusicTweak = 0;
|
|
g_nMusicSemaphore = 0;
|
|
g_bMusicIsPaused = false;
|
|
g_bMusicPause = false;
|
|
g_szCurrentMusicName[0] = 0;
|
|
g_szTargetMusicName[0] = 0;
|
|
sLoadDGO = {};
|
|
g_nActiveMusicStreams = 0;
|
|
g_bVagCmdsInitialized = false;
|
|
time_of_last_unknown_rate_drive_op = 0;
|
|
sRPCBuff[0] = {};
|
|
}
|
|
|
|
/*!
|
|
* Initialize the filesystem driver, then send a message to the sync messagebox
|
|
*/
|
|
void InitDriver() {
|
|
// init the filesystem
|
|
if (get_file_system()->Init() == 0) {
|
|
g_nISOInitFlag = 0;
|
|
}
|
|
lg::info("sending mbox for init driver");
|
|
SendMbx(g_nSyncMbx, &s_MsgPacket_NotOnStackSync);
|
|
}
|
|
|
|
/*!
|
|
* Check if there is anything in the sync mbox of the iso thread.
|
|
*/
|
|
u32 LookSyncMbx() {
|
|
MsgPacket* msg_packet;
|
|
auto poll_result = PollMbx(&msg_packet, g_nSyncMbx);
|
|
if (poll_result != KE_MBOX_NOMSG) {
|
|
sLoadDGO.sync_mbox_wait_count++;
|
|
}
|
|
return poll_result != KE_MBOX_NOMSG;
|
|
}
|
|
|
|
/*!
|
|
* Wait on the message box to have a message. Unlike older versions, they finally figured out
|
|
* how to call ReceiveMbx instead of polling in a loop!
|
|
*/
|
|
u32 WaitMbx(s32 box) {
|
|
MsgPacket* msg_packet;
|
|
return ReceiveMbx(&msg_packet, box);
|
|
}
|
|
|
|
u32 ISOThread();
|
|
u32 DGOThread();
|
|
|
|
/*!
|
|
* Initialize the Filesystem and ISO Thread.
|
|
*/
|
|
void InitISOFS() {
|
|
// memset(&sLoadDGO,0,0x110);
|
|
sLoadDGO = {};
|
|
sLoadDGO.last_id = -1;
|
|
g_nISOInitFlag = 1;
|
|
sLoadDGO.acked_cancel_id = -1;
|
|
sLoadDGO.selected_id = -1;
|
|
sLoadDGO.nosync_cancel_pending_flag = 0;
|
|
// g_pFileSystem = &g_ISOCDFileSystem;
|
|
sLoadDGO.request_cancel_id = -1;
|
|
sLoadDGO.nosync_cancel_ack = 0;
|
|
sLoadDGO.sync_sent_count = 0;
|
|
sLoadDGO.sync_mbox_wait_count = 0;
|
|
sLoadDGO.sync_ret_count = 0;
|
|
|
|
MbxParam mbx_param;
|
|
mbx_param.option = 0;
|
|
mbx_param.attr = 0;
|
|
|
|
g_nISOMbx = CreateMbx(&mbx_param);
|
|
ASSERT(g_nISOMbx >= 0);
|
|
g_nDGOMbx = CreateMbx(&mbx_param);
|
|
ASSERT(g_nDGOMbx >= 0);
|
|
g_nSyncMbx = CreateMbx(&mbx_param);
|
|
ASSERT(g_nSyncMbx >= 0);
|
|
|
|
ThreadParam thread_param;
|
|
|
|
thread_param.stackSize = 0x1100;
|
|
thread_param.entry = ISOThread;
|
|
thread_param.initPriority = 0x80; // changed to be lower priority
|
|
thread_param.attr = TH_C;
|
|
thread_param.option = 0;
|
|
strcpy(thread_param.name, "ISO");
|
|
g_nISOThreadID = CreateThread(&thread_param);
|
|
ASSERT(g_nISOThreadID >= 0);
|
|
|
|
thread_param.entry = DGOThread;
|
|
thread_param.initPriority = 0x38;
|
|
thread_param.attr = TH_C;
|
|
thread_param.stackSize = 0x900;
|
|
thread_param.option = 0;
|
|
strcpy(thread_param.name, "DGO");
|
|
g_nDGOThread = CreateThread(&thread_param);
|
|
ASSERT(g_nDGOThread >= 0);
|
|
|
|
thread_param.entry = STRThread;
|
|
thread_param.initPriority = 0x39;
|
|
thread_param.attr = TH_C;
|
|
thread_param.stackSize = 0x900;
|
|
thread_param.option = 0;
|
|
strcpy(thread_param.name, "STR");
|
|
g_nSTRThreadID = CreateThread(&thread_param);
|
|
ASSERT(g_nSTRThreadID >= 0);
|
|
|
|
thread_param.attr = TH_C;
|
|
thread_param.entry = PLAYThread;
|
|
thread_param.initPriority = 0x35;
|
|
thread_param.stackSize = 0x900;
|
|
thread_param.option = 0;
|
|
strcpy(thread_param.name, "Play");
|
|
g_nPlayThreadID = CreateThread(&thread_param);
|
|
ASSERT(g_nPlayThreadID >= 0);
|
|
|
|
StartThread(g_nISOThreadID, 0);
|
|
StartThread(g_nDGOThread, 0);
|
|
StartThread(g_nSTRThreadID, 0);
|
|
StartThread(g_nPlayThreadID, 0);
|
|
WaitMbx(g_nSyncMbx);
|
|
|
|
const ISOFileDef* vagdir_file = FindISOFile("VAGDIR.AYB");
|
|
if (vagdir_file) {
|
|
int load_status = LoadISOFileToIOP(vagdir_file, &g_VagDir, sizeof(g_VagDir));
|
|
if (load_status) {
|
|
ASSERT(g_VagDir.vag_magic_1 == 0x41574756);
|
|
ASSERT(g_VagDir.vag_magic_2 == 0x52494444);
|
|
} else {
|
|
lg::warn("Failed to load vagdir file");
|
|
g_VagDir.num_entries = 0;
|
|
}
|
|
} else {
|
|
lg::warn("Failed to find vagdir file");
|
|
g_VagDir.num_entries = 0;
|
|
}
|
|
|
|
// splash screen load was here...
|
|
ASSERT(g_nISOInitFlag == 0);
|
|
}
|
|
|
|
const ISOFileDef* FindISOFile(const char* name) {
|
|
return get_file_system()->Find(name);
|
|
}
|
|
|
|
s32 GetISOFileLength(const ISOFileDef* def) {
|
|
return get_file_system()->GetLength(def);
|
|
}
|
|
|
|
void SetVagClock(ISO_VAGCommand* cmd) {
|
|
cmd->clockc = 0;
|
|
cmd->clocka = 0;
|
|
cmd->clockb = 0;
|
|
cmd->clockd = 0;
|
|
if (!cmd->m_pBaseFile) {
|
|
cmd->flags.file_disappeared = 1;
|
|
} else {
|
|
cmd->flags.clocks_set = 1;
|
|
if (cmd->stereo_sibling) {
|
|
cmd->stereo_sibling->flags.clocks_set = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Start playing music, from an "external" command (from the ISO system).
|
|
*/
|
|
void IsoPlayMusicStream(ISO_VAGCommand* user_cmd) {
|
|
// must be flagged as music in the request.
|
|
ASSERT(user_cmd->music_flag);
|
|
|
|
const char* name = user_cmd->name;
|
|
|
|
// first, let's try to find an existing internal command that's streaming this music
|
|
ISO_VAGCommand* internal_cmd = FindMusicStreamName(name);
|
|
ISO_VAGCommand* stereo_internal_cmd = nullptr;
|
|
|
|
if (!internal_cmd) {
|
|
// no existing command, so allocate one.
|
|
internal_cmd = SmartAllocMusicVagCommand(user_cmd, 0);
|
|
|
|
if (!internal_cmd) {
|
|
ASSERT_NOT_REACHED(); // was only a warning
|
|
return;
|
|
}
|
|
|
|
// set the active flags to false:
|
|
set_active_a(internal_cmd, 0);
|
|
set_active_b(internal_cmd, 0);
|
|
set_active_c(internal_cmd, 0);
|
|
set_active_a(user_cmd, 0);
|
|
set_active_b(user_cmd, 0);
|
|
set_active_c(user_cmd, 0);
|
|
|
|
// clear flags related to actual streaming, since we're starting (or restarting) the stream
|
|
// from nothing.
|
|
user_cmd->flags.saw_chunks1 = 0;
|
|
user_cmd->flags.running = 0;
|
|
|
|
// if we're reusing an already started stream, need to stop that one first.
|
|
// if ((*(uint*)&internal_cmd->bit_running & 0xffff00) != 0) {
|
|
if (internal_cmd->flags.clocks_set || internal_cmd->flags.file_disappeared) {
|
|
IsoStopVagStream(internal_cmd);
|
|
}
|
|
|
|
// copy entire header
|
|
internal_cmd->unka = user_cmd->unka;
|
|
internal_cmd->unkb = user_cmd->unkb;
|
|
internal_cmd->status = user_cmd->status;
|
|
internal_cmd->active_a = user_cmd->active_a;
|
|
internal_cmd->active_b = user_cmd->active_b;
|
|
internal_cmd->active_c = user_cmd->active_c;
|
|
internal_cmd->pad = user_cmd->pad;
|
|
|
|
internal_cmd->msg_type = user_cmd->msg_type;
|
|
internal_cmd->mbox_reply = user_cmd->mbox_reply;
|
|
internal_cmd->thread_to_wake = user_cmd->thread_to_wake;
|
|
internal_cmd->callback = user_cmd->callback;
|
|
|
|
internal_cmd->m_pBaseFile = user_cmd->m_pBaseFile;
|
|
internal_cmd->priority = user_cmd->priority;
|
|
internal_cmd->file_def = user_cmd->file_def;
|
|
|
|
// copy part of the command set by the user
|
|
internal_cmd->vag_file_def = user_cmd->vag_file_def;
|
|
internal_cmd->vag_dir_entry = user_cmd->vag_dir_entry;
|
|
ASSERT(strlen(name) < 0x30);
|
|
strncpy(internal_cmd->name, name, 0x30);
|
|
internal_cmd->play_volume = user_cmd->play_volume;
|
|
internal_cmd->id = user_cmd->id;
|
|
internal_cmd->plugin_id = user_cmd->plugin_id;
|
|
internal_cmd->maybe_sound_handler = user_cmd->maybe_sound_handler;
|
|
internal_cmd->oog = user_cmd->oog;
|
|
internal_cmd->dolby_pan_angle = user_cmd->dolby_pan_angle;
|
|
internal_cmd->art_flag = user_cmd->art_flag;
|
|
internal_cmd->movie_flag = user_cmd->movie_flag;
|
|
|
|
//
|
|
InitVAGCmd(internal_cmd, 1);
|
|
|
|
// check for stereo bit:
|
|
if ((internal_cmd->vag_dir_entry->words[1] & 0x400U) != 0) {
|
|
// allocate stereo command
|
|
stereo_internal_cmd = SmartAllocMusicVagCommand(user_cmd, 1);
|
|
if (!stereo_internal_cmd) {
|
|
// it failed - give up on this message!
|
|
ReleaseMessage(internal_cmd);
|
|
FreeVagCmd(internal_cmd);
|
|
internal_cmd = nullptr;
|
|
ASSERT_NOT_REACHED();
|
|
} else {
|
|
// stop existing stereo command
|
|
// if ((*(uint*)&stereo_internal_cmd->bit_running & 0xffff00) != 0) {
|
|
if (stereo_internal_cmd->flags.clocks_set || stereo_internal_cmd->flags.file_disappeared) {
|
|
IsoStopVagStream(stereo_internal_cmd);
|
|
}
|
|
|
|
// set up as a stereo secondary
|
|
stereo_internal_cmd->flags.stereo_secondary = 1;
|
|
internal_cmd->stereo_sibling = stereo_internal_cmd;
|
|
stereo_internal_cmd->stereo_sibling = internal_cmd;
|
|
|
|
auto name_len = strlen(internal_cmd->name);
|
|
strncpyz(stereo_internal_cmd->name, internal_cmd->name, 0x31);
|
|
if (name_len < 0x30) {
|
|
strncpyz(stereo_internal_cmd->name + name_len, " (stereo)", 0x31 - name_len);
|
|
}
|
|
stereo_internal_cmd->id = ~internal_cmd->id;
|
|
stereo_internal_cmd->vag_dir_entry = internal_cmd->vag_dir_entry;
|
|
}
|
|
}
|
|
|
|
// abort if no command
|
|
if (!internal_cmd) {
|
|
return;
|
|
}
|
|
|
|
// start us out as paused
|
|
internal_cmd->flags.paused = 1;
|
|
if (stereo_internal_cmd) {
|
|
stereo_internal_cmd->flags.paused = 1;
|
|
}
|
|
|
|
if (QueueMessage(internal_cmd, 5) == 0) {
|
|
// failed to queue, clear our commands and release the message.
|
|
FreeVagCmd(internal_cmd);
|
|
if ((internal_cmd->vag_dir_entry->words[1] & 0x400U) != 0) {
|
|
FreeVagCmd(stereo_internal_cmd);
|
|
}
|
|
ReleaseMessage(internal_cmd);
|
|
} else {
|
|
auto* vag_dir_entry = internal_cmd->vag_dir_entry;
|
|
if (!vag_dir_entry) {
|
|
// not really sure how this can happen...
|
|
internal_cmd->m_pBaseFile = nullptr;
|
|
ASSERT_NOT_REACHED(); // just so I can learn when this happens.
|
|
} else {
|
|
// need to understand this better, but it seems like we can pick between two different files
|
|
// to actually load from...
|
|
const ISOFileDef* filedef = nullptr;
|
|
if (((u32)vag_dir_entry->words[1] >> 0xb & 1) == 0) {
|
|
filedef = internal_cmd->file_def;
|
|
} else {
|
|
filedef = internal_cmd->vag_file_def;
|
|
}
|
|
|
|
// open the file!!
|
|
auto* base_file = get_file_system()->OpenWAD(filedef, vag_dir_entry->words[1] >> 16);
|
|
internal_cmd->m_pBaseFile = base_file;
|
|
|
|
// determine the reading rate of the music
|
|
if (base_file) {
|
|
u32 rate_idx = 0x3c & (internal_cmd->vag_dir_entry->words[1] >> 10);
|
|
ASSERT((rate_idx % 4) == 0);
|
|
constexpr int rates[16] = {0xFA00, 0x1F40, 0x3E80, 0x5DC0, 0x7D00, 0x9C40,
|
|
0xBB80, 0xDAC0, 0xAC44, 0x1589, 0x2B11, 0x409A,
|
|
0x5622, 0x6BAB, 0x8133, 0x96BC};
|
|
|
|
int rate = rates[rate_idx];
|
|
if (((internal_cmd->vag_dir_entry->words[1] >> 10) & 1) == 0) {
|
|
rate = rate << 2;
|
|
} else {
|
|
rate = rate << 3;
|
|
}
|
|
base_file->m_ReadRate = rate / 7;
|
|
}
|
|
}
|
|
|
|
SetVagStreamName(internal_cmd, 0x30);
|
|
if (stereo_internal_cmd) {
|
|
SetVagStreamName(stereo_internal_cmd, 0x30);
|
|
}
|
|
|
|
internal_cmd->callback = ProcessVAGData;
|
|
internal_cmd->status = EIsoStatus::OK_2;
|
|
internal_cmd->flags.paused = 0;
|
|
|
|
if (stereo_internal_cmd) {
|
|
stereo_internal_cmd->flags.paused = 0;
|
|
}
|
|
internal_cmd->flags.running = 1;
|
|
if (stereo_internal_cmd) {
|
|
stereo_internal_cmd->flags.running = 1;
|
|
}
|
|
internal_cmd->status = EIsoStatus::OK_2;
|
|
set_active_a(internal_cmd, 1);
|
|
set_active_b(internal_cmd, 1);
|
|
|
|
// load tweak value
|
|
g_nMusicFadeDir = 0;
|
|
g_nMusicFade = 0x10000;
|
|
g_nMusicTweak = 0x80;
|
|
for (u32 i = 0; i < gMusicTweakInfo.TweakCount; i++) {
|
|
if (strcmp(gMusicTweakInfo.MusicTweak[i].MusicName, name) == 0) {
|
|
g_nMusicTweak = gMusicTweakInfo.MusicTweak[i].VolumeAdjust;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!internal_cmd) {
|
|
return;
|
|
}
|
|
}
|
|
SetVagClock(internal_cmd);
|
|
}
|
|
|
|
/*!
|
|
* Get a VAG command ready for playback, but don't actually start the audio yet.
|
|
*/
|
|
void IsoQueueVagStream(ISO_VAGCommand* user_cmd) {
|
|
ASSERT(user_cmd);
|
|
ASSERT(!user_cmd->music_flag); // can't use music commands with this function
|
|
ASSERT(user_cmd->id);
|
|
ISO_VAGCommand* internal_stereo_cmd = nullptr;
|
|
|
|
// mysterious case to reject a command
|
|
if (user_cmd->vag_dir_entry && (user_cmd->vag_dir_entry->words[1] & 0x400U) != 0 &&
|
|
HowManyBelowThisPriority(user_cmd->priority_pq) < 2) {
|
|
ovrld_log(LogCategory::WARN, "mysterious rejection of a queued vag stream: {}", user_cmd->name);
|
|
return;
|
|
}
|
|
|
|
// see if we already have a command for this
|
|
ISO_VAGCommand* internal_cmd = FindThisVagStream(user_cmd->name, user_cmd->id);
|
|
if (!internal_cmd) {
|
|
// try allocating one
|
|
internal_cmd = SmartAllocVagCmd(user_cmd);
|
|
if (!internal_cmd) {
|
|
// no more commands!
|
|
return;
|
|
}
|
|
|
|
ovrld_log(LogCategory::VAG_SETUP, "IsoQueueVagStream allocating for {} {}", user_cmd->name,
|
|
user_cmd->id);
|
|
// clear active flags
|
|
set_active_a(internal_cmd, 0);
|
|
set_active_b(internal_cmd, 0);
|
|
set_active_c(internal_cmd, 0);
|
|
set_active_a(user_cmd, 0);
|
|
set_active_b(user_cmd, 0);
|
|
set_active_c(user_cmd, 0);
|
|
user_cmd->flags.saw_chunks1 = 0;
|
|
user_cmd->flags.running = 0;
|
|
// if ((*(uint*)&internal_cmd->bit_running & 0xffff00) != 0) {
|
|
// if we're playing it, stop it.
|
|
if (internal_cmd->flags.clocks_set || internal_cmd->flags.file_disappeared) {
|
|
IsoStopVagStream(internal_cmd);
|
|
}
|
|
|
|
// copy entire header
|
|
internal_cmd->unka = user_cmd->unka;
|
|
internal_cmd->unkb = user_cmd->unkb;
|
|
internal_cmd->status = user_cmd->status;
|
|
internal_cmd->active_a = user_cmd->active_a;
|
|
internal_cmd->active_b = user_cmd->active_b;
|
|
internal_cmd->active_c = user_cmd->active_c;
|
|
internal_cmd->pad = user_cmd->pad;
|
|
|
|
internal_cmd->msg_type = user_cmd->msg_type;
|
|
internal_cmd->mbox_reply = user_cmd->mbox_reply;
|
|
internal_cmd->thread_to_wake = user_cmd->thread_to_wake;
|
|
internal_cmd->callback = user_cmd->callback;
|
|
|
|
internal_cmd->m_pBaseFile = user_cmd->m_pBaseFile;
|
|
internal_cmd->priority = user_cmd->priority;
|
|
internal_cmd->file_def = user_cmd->file_def;
|
|
|
|
internal_cmd->vag_file_def = user_cmd->vag_file_def;
|
|
internal_cmd->vag_dir_entry = user_cmd->vag_dir_entry;
|
|
strncpy(internal_cmd->name, user_cmd->name, 0x30);
|
|
internal_cmd->id = user_cmd->id;
|
|
internal_cmd->play_volume = user_cmd->play_volume;
|
|
internal_cmd->plugin_id = user_cmd->plugin_id;
|
|
internal_cmd->maybe_sound_handler = user_cmd->maybe_sound_handler;
|
|
internal_cmd->oog = user_cmd->oog;
|
|
internal_cmd->dolby_pan_angle = user_cmd->dolby_pan_angle;
|
|
internal_cmd->art_flag = user_cmd->art_flag;
|
|
internal_cmd->movie_flag = user_cmd->movie_flag;
|
|
|
|
InitVAGCmd(internal_cmd, 1);
|
|
|
|
internal_cmd->flags.scanned = 1;
|
|
|
|
// check if we're a stereo command
|
|
if ((internal_cmd->vag_dir_entry) && ((internal_cmd->vag_dir_entry->words[1] & 0x400U) != 0)) {
|
|
internal_stereo_cmd = SmartAllocVagCmd(user_cmd);
|
|
if (!internal_stereo_cmd) {
|
|
// allocating stereo failed, give up.
|
|
internal_cmd->flags.scanned = 0;
|
|
// ASSERT_NOT_REACHED();
|
|
ReleaseMessage(internal_cmd);
|
|
RemoveVagCmd(internal_cmd);
|
|
FreeVagCmd(internal_cmd);
|
|
internal_cmd = nullptr;
|
|
} else {
|
|
// if ((*(uint*)&internal_stereo_cmd->bit_running & 0xffff00) != 0) {
|
|
if (internal_stereo_cmd->flags.clocks_set || internal_stereo_cmd->flags.file_disappeared) {
|
|
IsoStopVagStream(internal_stereo_cmd);
|
|
}
|
|
internal_cmd->stereo_sibling = internal_stereo_cmd;
|
|
internal_stereo_cmd->flags.stereo_secondary = 1;
|
|
internal_stereo_cmd->stereo_sibling = internal_cmd;
|
|
auto name_len = strlen(internal_cmd->name);
|
|
strncpyz(internal_stereo_cmd->name, internal_cmd->name, 0x31);
|
|
if (name_len < 0x30) {
|
|
strncpyz(internal_stereo_cmd->name + name_len, " (stereo)", 0x31 - name_len);
|
|
}
|
|
internal_stereo_cmd->id = ~internal_cmd->id;
|
|
internal_stereo_cmd->vag_dir_entry = internal_cmd->vag_dir_entry;
|
|
internal_stereo_cmd->flags.scanned = 1;
|
|
}
|
|
}
|
|
|
|
// return if alloc failed
|
|
if (!internal_cmd) {
|
|
return;
|
|
}
|
|
|
|
if (QueueMessage(internal_cmd, 5) == 0) {
|
|
// queueing failed.
|
|
internal_cmd->flags.scanned = 0;
|
|
ASSERT_NOT_REACHED();
|
|
RemoveVagCmd(internal_cmd);
|
|
FreeVagCmd(internal_cmd);
|
|
if ((internal_cmd->vag_dir_entry->words[1] & 0x400U) != 0) {
|
|
internal_stereo_cmd->flags.scanned = 0;
|
|
RemoveVagCmd(internal_stereo_cmd);
|
|
FreeVagCmd(internal_stereo_cmd);
|
|
}
|
|
ReleaseMessage(internal_cmd);
|
|
} else {
|
|
auto* vag_dir_entry = internal_cmd->vag_dir_entry;
|
|
if (!vag_dir_entry) {
|
|
// not really sure how this can happen...
|
|
internal_cmd->m_pBaseFile = nullptr;
|
|
// ASSERT_NOT_REACHED(); // just so I can learn when this happens.
|
|
} else {
|
|
// need to understand this better, but it seems like we can pick between two different files
|
|
// to actually load from...
|
|
const ISOFileDef* filedef = nullptr;
|
|
if (((u32)vag_dir_entry->words[1] >> 0xb & 1) == 0) {
|
|
filedef = internal_cmd->file_def;
|
|
} else {
|
|
filedef = internal_cmd->vag_file_def;
|
|
}
|
|
auto* base_file = get_file_system()->OpenWAD(filedef, vag_dir_entry->words[1] >> 16);
|
|
internal_cmd->m_pBaseFile = base_file;
|
|
if (!base_file) {
|
|
u32 rate_idx = 0x3c & (internal_cmd->vag_dir_entry->words[1] >> 10);
|
|
ASSERT((rate_idx % 4) == 0);
|
|
constexpr int rates[16] = {0xFA00, 0x1F40, 0x3E80, 0x5DC0, 0x7D00, 0x9C40,
|
|
0xBB80, 0xDAC0, 0xAC44, 0x1589, 0x2B11, 0x409A,
|
|
0x5622, 0x6BAB, 0x8133, 0x96BC};
|
|
|
|
int rate = rates[rate_idx];
|
|
if (((internal_cmd->vag_dir_entry->words[1] >> 10) & 1) == 0) {
|
|
rate = rate << 2;
|
|
} else {
|
|
rate = rate << 3;
|
|
}
|
|
base_file->m_ReadRate = rate / 7;
|
|
}
|
|
}
|
|
if (user_cmd->art_flag != 0) {
|
|
internal_cmd->flags.art = 1;
|
|
}
|
|
if (user_cmd->movie_flag != 0) {
|
|
internal_cmd->flags.movie = 1;
|
|
}
|
|
|
|
internal_cmd->flags.paused = 1;
|
|
SetNewVagCmdPri(internal_cmd, user_cmd->priority_pq);
|
|
if (internal_stereo_cmd) {
|
|
internal_stereo_cmd->flags.paused = 1;
|
|
internal_stereo_cmd->flags.scanned = 1;
|
|
SetNewVagCmdPri(internal_stereo_cmd, 10);
|
|
}
|
|
SetVagStreamName(internal_cmd, 0x30);
|
|
if (internal_stereo_cmd) {
|
|
SetVagStreamName(internal_stereo_cmd, 0x30);
|
|
}
|
|
internal_cmd->status = EIsoStatus::OK_2;
|
|
internal_cmd->callback = ProcessVAGData;
|
|
}
|
|
if (!internal_cmd) {
|
|
return;
|
|
}
|
|
}
|
|
SetVagClock(internal_cmd);
|
|
}
|
|
|
|
/*!
|
|
* Actually start playback of audio, requested from ISO thread functions.
|
|
*/
|
|
void IsoPlayVagStream(ISO_VAGCommand* user_cmd) {
|
|
ASSERT(user_cmd);
|
|
ASSERT(!user_cmd->music_flag);
|
|
ISO_VAGCommand* stereo_cmd = user_cmd->stereo_sibling;
|
|
ISO_VAGCommand* internal_cmd = FindThisVagStream(user_cmd->name, user_cmd->id);
|
|
|
|
// update to running only if we aren't already.
|
|
if (internal_cmd && (internal_cmd->flags.running == 0)) {
|
|
internal_cmd->play_volume = user_cmd->play_volume;
|
|
if (internal_cmd->flags.paused != 0) {
|
|
if (g_bExtPause) {
|
|
g_bExtResume = true;
|
|
}
|
|
if (internal_cmd->flags.saw_chunks1 == 0) {
|
|
internal_cmd->flags.paused = 0;
|
|
if (stereo_cmd) {
|
|
stereo_cmd->flags.paused = 0;
|
|
}
|
|
} else {
|
|
ovrld_log(LogCategory::VAG_SETUP, "IsoPlayVagStream is unpausing {}", internal_cmd->name);
|
|
UnPauseVAG(internal_cmd);
|
|
}
|
|
if (user_cmd->priority_pq < 3) {
|
|
// this printed a "ruins fix" message, seems like this is a total hack!!
|
|
SetNewVagCmdPri(user_cmd, 7);
|
|
}
|
|
}
|
|
internal_cmd->flags.running = 1;
|
|
if (stereo_cmd) {
|
|
stereo_cmd->flags.running = 1;
|
|
}
|
|
if (internal_cmd) {
|
|
SetVagClock(internal_cmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
void IsoStopVagStream(ISO_VAGCommand* cmd) {
|
|
auto id = cmd->id;
|
|
ISO_VAGCommand* internal_cmd;
|
|
|
|
// handle music/audio separately here
|
|
if (cmd->music_flag != 0) {
|
|
if (id == 0) { // no id, must use name
|
|
if (cmd->name[0] == 0) {
|
|
ASSERT_NOT_REACHED(); // shouldn't happen
|
|
return;
|
|
}
|
|
|
|
// terminate all with this name.
|
|
while (internal_cmd = FindMusicStreamName(cmd->name), internal_cmd) {
|
|
ovrld_log(LogCategory::VAG_SETUP, "IsoStopVagStream is terminating {} (1)",
|
|
internal_cmd->name);
|
|
TerminateVAG(internal_cmd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// we have an id, just terminate that.
|
|
internal_cmd = FindThisMusicStream(cmd->name, id);
|
|
if (!internal_cmd) {
|
|
return;
|
|
}
|
|
ovrld_log(LogCategory::VAG_SETUP, "IsoStopVagStream is terminating {} (2)", internal_cmd->name);
|
|
TerminateVAG(internal_cmd);
|
|
return;
|
|
}
|
|
|
|
// flag - this controls if we do the AnyVagRunning check or not.
|
|
// not really sure why...
|
|
bool flag = false;
|
|
|
|
if (id == 0) {
|
|
if (cmd->name[0] == 0) {
|
|
return;
|
|
}
|
|
while (internal_cmd = FindVagStreamName(cmd->name), internal_cmd) {
|
|
flag = true;
|
|
ovrld_log(LogCategory::VAG_SETUP, "IsoStopVagStream is terminating {} (3)",
|
|
internal_cmd->name);
|
|
TerminateVAG(internal_cmd);
|
|
}
|
|
|
|
if (!flag) {
|
|
return;
|
|
}
|
|
} else {
|
|
internal_cmd = FindThisVagStream(cmd->name, id);
|
|
if (!internal_cmd) {
|
|
return;
|
|
}
|
|
ovrld_log(LogCategory::VAG_SETUP, "IsoStopVagStream is terminating {} (4)", internal_cmd->name);
|
|
TerminateVAG(internal_cmd);
|
|
}
|
|
|
|
if (AnyVagRunning() == 0) {
|
|
g_bExtPause = false;
|
|
g_bExtResume = false;
|
|
}
|
|
}
|
|
|
|
void ProcessMusic() {
|
|
ISO_VAGCommand* cmd = nullptr;
|
|
WaitSema(g_nMusicSemaphore);
|
|
|
|
// handle unpausing request
|
|
if (g_bMusicIsPaused && !g_bMusicPause && !g_bAnotherMusicPauseFlag) {
|
|
cmd = FindMusicStreamName(g_szCurrentMusicName);
|
|
if (cmd && cmd->id && !cmd->flags.stop) { // can't unpause if stopped.
|
|
UnPauseVAG(cmd);
|
|
}
|
|
g_bMusicIsPaused = false;
|
|
}
|
|
|
|
// handle pausing request.
|
|
if (!g_bMusicIsPaused && g_bMusicPause) {
|
|
cmd = FindMusicStreamName(g_szCurrentMusicName);
|
|
if (cmd && cmd->id && !cmd->flags.stop) {
|
|
PauseVAG(cmd);
|
|
}
|
|
g_bMusicIsPaused = true;
|
|
}
|
|
|
|
// handle playing music updates.
|
|
if (g_bMusicIsPaused == 0) {
|
|
// first, see if we're even playing the right music:
|
|
if (strncmp(g_szCurrentMusicName, g_szTargetMusicName, 0xf) == 0) {
|
|
// we are! fade in the music, if it's active:
|
|
if (0 < g_nActiveMusicStreams && g_nMusicFadeDir < 0) {
|
|
g_nMusicFadeDir = 1;
|
|
}
|
|
|
|
if ((g_szCurrentMusicName[0] == 0) || (g_nActiveMusicStreams != 0)) {
|
|
SignalSema(g_nMusicSemaphore);
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
// we aren't. fade out music if the target is null, or we're currently playing music.
|
|
if ((g_szTargetMusicName[0] == 0) || (g_szCurrentMusicName[0] != 0)) {
|
|
if (g_nMusicFade < 1) {
|
|
cmd = g_aVagCmds + 4;
|
|
int i = 1;
|
|
do {
|
|
i--;
|
|
// stop any non-stereo, but real music.
|
|
if (cmd->music_flag && !cmd->flags.stereo_secondary && cmd->id) {
|
|
IsoStopVagStream(cmd);
|
|
}
|
|
cmd = cmd + 1;
|
|
} while (-1 < i);
|
|
} else {
|
|
g_nMusicFadeDir = -1;
|
|
}
|
|
}
|
|
if (g_nMusicFade != 0) {
|
|
SignalSema(g_nMusicSemaphore);
|
|
return;
|
|
}
|
|
|
|
// if we made it through that, we're done playing old stuff.
|
|
strncpyz(g_szCurrentMusicName, g_szTargetMusicName, 0x10);
|
|
}
|
|
|
|
if (g_szCurrentMusicName[0] != 0) {
|
|
VagStreamData vsd;
|
|
strncpy(vsd.name, g_szCurrentMusicName, 0x30);
|
|
vsd.id = 0x29a;
|
|
vsd.priority = 9;
|
|
vsd.art_load = 0;
|
|
vsd.movie_art_load = 0;
|
|
vsd.sound_handler = 0;
|
|
ovrld_log(LogCategory::VAG_SETUP, "ProcessMusic is changing the music to {}", vsd.name);
|
|
PlayMusicStream(&vsd);
|
|
}
|
|
}
|
|
|
|
SignalSema(g_nMusicSemaphore);
|
|
}
|
|
|
|
u32 ISOThread() {
|
|
int priority = -1;
|
|
g_szCurrentMusicName[0] = 0;
|
|
g_szTargetMusicName[0] = 0;
|
|
g_bMusicIsPaused = false;
|
|
|
|
lg::info("top of ISO Thread");
|
|
|
|
// file = (CISOCDFile*)0x0;
|
|
InitBuffers();
|
|
// bVar1 = false;
|
|
InitVagCmds();
|
|
g_bVagCmdsInitialized = true;
|
|
InitDriver();
|
|
|
|
ISO_Hdr* mbx_cmd = nullptr;
|
|
ISO_LoadSoundbank* load_sbk_cmd = nullptr;
|
|
ISO_LoadCommon* load_cmd = nullptr;
|
|
char local_name[32];
|
|
ISO_VAGCommand* vag_cmd = nullptr;
|
|
ISO_VAGCommand* internal_vag_cmd = nullptr;
|
|
|
|
// ISOFileDef* file_def = nullptr;
|
|
|
|
while (true) {
|
|
// dma_intr_hack();
|
|
// Part 1: Handle incoming messages from the user:
|
|
|
|
int poll_result = PollMbx((MsgPacket**)&mbx_cmd, g_nISOMbx);
|
|
if (poll_result == KE_OK) {
|
|
if (mbx_cmd->msg_type == ISO_Hdr::MsgType::ABADBABE) {
|
|
// what is this garbage
|
|
ASSERT_NOT_REACHED();
|
|
ReleaseMessage(mbx_cmd);
|
|
} else {
|
|
set_active_a(mbx_cmd, 0);
|
|
set_active_b(mbx_cmd, 0);
|
|
set_active_c(mbx_cmd, 0);
|
|
|
|
// iVar3 = (cmd->header).kind;
|
|
mbx_cmd->callback = NullCallback;
|
|
mbx_cmd->m_pBaseFile = nullptr;
|
|
auto msg_kind = mbx_cmd->msg_type;
|
|
|
|
// ovrld_log(LogCategory::ISO_QUEUE, "Incoming message to the ISO Queue with type 0x{:x}",
|
|
// (int)msg_kind);
|
|
|
|
// if we're a simple file loading command:
|
|
if (msg_kind == ISO_Hdr::MsgType::LOAD_EE || msg_kind == ISO_Hdr::MsgType::LOAD_EE_CHUNK ||
|
|
msg_kind == ISO_Hdr::MsgType::LOAD_IOP ||
|
|
msg_kind == ISO_Hdr::MsgType::LOAD_SOUNDBANK) {
|
|
priority = 3; // default priority for file loads is 3... unless we're loading a soundbank
|
|
// in which case there's a bizarre special case here:
|
|
load_cmd = (ISO_LoadCommon*)mbx_cmd;
|
|
|
|
if (msg_kind == ISO_Hdr::MsgType::LOAD_SOUNDBANK) {
|
|
load_sbk_cmd = (ISO_LoadSoundbank*)mbx_cmd;
|
|
priority = 0;
|
|
if (load_sbk_cmd->priority == 2) {
|
|
priority = 2;
|
|
} else {
|
|
if (load_sbk_cmd->priority == 10) {
|
|
priority = 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (QueueMessage(mbx_cmd, priority) == 0) {
|
|
ovrld_log(LogCategory::WARN, "Failed to queue incoming iso message");
|
|
goto LAB_00006b18;
|
|
}
|
|
|
|
// iVar3 = (cmd->header).kind;
|
|
// handle opening the file:
|
|
switch (msg_kind) {
|
|
case ISO_Hdr::MsgType::LOAD_EE_CHUNK: {
|
|
// ovrld_log(LogCategory::ISO_QUEUE, "Opening File {} for EE Chunk Load offset {}",
|
|
// mbx_cmd->file_def->name.data, ((ISO_LoadSingle*)mbx_cmd)->sector_offset);
|
|
mbx_cmd->m_pBaseFile = get_file_system()->Open(
|
|
mbx_cmd->file_def, ((ISO_LoadSingle*)mbx_cmd)->sector_offset, 1);
|
|
} break;
|
|
case ISO_Hdr::MsgType::LOAD_IOP:
|
|
case ISO_Hdr::MsgType::LOAD_EE:
|
|
// ovrld_log(LogCategory::ISO_QUEUE, "Opening File {} for Load {}",
|
|
// msg_kind == ISO_Hdr::MsgType::LOAD_EE ? "EE" : "IOP",
|
|
// mbx_cmd->file_def->name.data);
|
|
mbx_cmd->m_pBaseFile = get_file_system()->Open(mbx_cmd->file_def, -1, 1);
|
|
break;
|
|
case ISO_Hdr::MsgType::LOAD_SOUNDBANK: {
|
|
// ovrld_log(LogCategory::ISO_QUEUE, "Opening for LOAD_SOUNDBANK {} ",
|
|
// load_sbk_cmd->name);
|
|
// build name
|
|
ASSERT(load_sbk_cmd->name);
|
|
strncpy(local_name, load_sbk_cmd->name, 0xc);
|
|
local_name[8] = 0;
|
|
strcat(local_name, ".sbk");
|
|
mbx_cmd->file_def = get_file_system()->Find(local_name);
|
|
ASSERT(mbx_cmd->file_def);
|
|
mbx_cmd->m_pBaseFile = get_file_system()->Open(mbx_cmd->file_def, -1, 1);
|
|
ASSERT(mbx_cmd->m_pBaseFile);
|
|
} break;
|
|
default:
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
// if we failed to open, bail
|
|
if (!mbx_cmd->m_pBaseFile) {
|
|
ASSERT_NOT_REACHED();
|
|
mbx_cmd->status = EIsoStatus::ERROR_OPENING_FILE_8;
|
|
UnqueueMessage(mbx_cmd);
|
|
ReturnMessage(mbx_cmd);
|
|
// this goes somewhere else...
|
|
}
|
|
|
|
// set up lengths based on the actual file length on disc.
|
|
load_cmd->dest_ptr = load_cmd->addr;
|
|
load_cmd->progress_bytes = 0;
|
|
load_cmd->length_to_copy = get_file_system()->GetLength(mbx_cmd->file_def);
|
|
// pIVar5 = length
|
|
if (msg_kind == ISO_Hdr::MsgType::LOAD_SOUNDBANK) {
|
|
load_cmd->maxlen = load_cmd->length_to_copy;
|
|
} else {
|
|
ASSERT(load_cmd->length_to_copy);
|
|
// trim copy size to the max buffer length given.
|
|
if (load_cmd->length_to_copy > load_cmd->maxlen) {
|
|
load_cmd->length_to_copy = load_cmd->maxlen;
|
|
}
|
|
}
|
|
|
|
// set up callback
|
|
switch (msg_kind) {
|
|
case ISO_Hdr::MsgType::LOAD_IOP:
|
|
mbx_cmd->callback = CopyDataToIOP;
|
|
break;
|
|
case ISO_Hdr::MsgType::LOAD_SOUNDBANK:
|
|
mbx_cmd->callback = CopyDataSbkLoad;
|
|
break;
|
|
case ISO_Hdr::MsgType::LOAD_EE:
|
|
case ISO_Hdr::MsgType::LOAD_EE_CHUNK:
|
|
mbx_cmd->callback = CopyDataToEE;
|
|
break;
|
|
default:
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
mbx_cmd->status = EIsoStatus::OK_2;
|
|
set_active_a(mbx_cmd, 1);
|
|
} else {
|
|
switch (msg_kind) {
|
|
case ISO_Hdr::MsgType::DGO_LOAD:
|
|
if (QueueMessage(mbx_cmd, 1) != 0) {
|
|
// modified for non compressed dgos
|
|
ovrld_log(LogCategory::ISO_QUEUE, "Opening {} for DGO Load",
|
|
mbx_cmd->file_def->name.data);
|
|
mbx_cmd->m_pBaseFile = get_file_system()->Open(mbx_cmd->file_def, -1, 1);
|
|
if (mbx_cmd->m_pBaseFile) {
|
|
mbx_cmd->callback = RunDGOStateMachine;
|
|
mbx_cmd->status = EIsoStatus::OK_2;
|
|
((ISO_DGOCommand*)mbx_cmd)->state = ISO_DGOCommand::State::INIT;
|
|
set_active_a(mbx_cmd, 1);
|
|
} else {
|
|
ASSERT_NOT_REACHED();
|
|
UnqueueMessage(mbx_cmd);
|
|
ASSERT(sLoadDGO.msg_type != ISO_Hdr::MsgType::MSG_0);
|
|
SendMbx(g_nISOMbx, &sLoadDGO);
|
|
}
|
|
}
|
|
break;
|
|
case ISO_Hdr::MsgType::VAG_PAUSE:
|
|
ovrld_log(LogCategory::ISO_QUEUE, "VagPause (all of them)");
|
|
if (g_bExtPause == 0) {
|
|
SetVagStreamsNoStart(1);
|
|
int iVar3 = AnyVagRunning();
|
|
if (iVar3 != 0) {
|
|
PauseVagStreams(0);
|
|
}
|
|
g_bExtPause = true;
|
|
g_bExtResume = iVar3 != 0;
|
|
}
|
|
ReturnMessage(mbx_cmd);
|
|
break;
|
|
case ISO_Hdr::MsgType::VAG_UNPAUSE:
|
|
ovrld_log(LogCategory::ISO_QUEUE, "VagUnPause (all of them)");
|
|
if (g_bExtPause != 0) {
|
|
if (g_bExtResume != false) {
|
|
UnPauseVagStreams(0);
|
|
}
|
|
g_bExtPause = false;
|
|
g_bExtResume = false;
|
|
}
|
|
SetVagStreamsNoStart(0);
|
|
ReturnMessage(mbx_cmd);
|
|
break;
|
|
case ISO_Hdr::MsgType::VAG_SET_PITCH_VOL:
|
|
vag_cmd = (ISO_VAGCommand*)mbx_cmd;
|
|
internal_vag_cmd = FindVagStreamId(vag_cmd->id);
|
|
if (internal_vag_cmd) {
|
|
internal_vag_cmd->pitch_cmd = vag_cmd->pitch_cmd;
|
|
SetVAGVol(internal_vag_cmd);
|
|
}
|
|
ReturnMessage(vag_cmd);
|
|
break;
|
|
case ISO_Hdr::MsgType::ADEADBEE:
|
|
ReturnMessage(vag_cmd);
|
|
ExitThread();
|
|
goto LAB_00006b18;
|
|
break;
|
|
default:
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (poll_result == -0x1a9) {
|
|
// messagebox was deleted - this means we're shutting down
|
|
return 0;
|
|
}
|
|
if (poll_result != -0x1a8) {
|
|
// unknown messagebox error
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
}
|
|
LAB_00006b18:
|
|
// Part 2: music update
|
|
// Poll is called here... but we don't use it.
|
|
// (**(code**)(*g_pFileSystem + 4))();
|
|
ProcessMusic();
|
|
|
|
// Part 3: service in-progress messages
|
|
// get the top priority message
|
|
bool buffer_ok = false;
|
|
auto* cmd = GetMessage();
|
|
CBaseFile* file = nullptr;
|
|
bool known_read_rate = false;
|
|
|
|
if (cmd) {
|
|
// handle the buffering
|
|
// ovrld_log(LogCategory::ISO_QUEUE, "Processing Command 0x{:x} - allocating buffer\n",
|
|
// (int)cmd->msg_type);
|
|
|
|
// check if we need to initialize a buffer, or if we just need to realloc pages
|
|
file = cmd->m_pBaseFile;
|
|
bool needs_buffer_init = false;
|
|
if (!file) {
|
|
needs_buffer_init = true;
|
|
// we'd need to set buffer_ok = false later on if this is the case,
|
|
// but I dont think this can happen.
|
|
ASSERT_NOT_REACHED();
|
|
} else {
|
|
if (file->m_Buffer.m_eBufferType == CBuffer::BufferType::EBT_FREE) {
|
|
needs_buffer_init = true;
|
|
}
|
|
}
|
|
|
|
// set up buffer
|
|
if (needs_buffer_init) {
|
|
buffer_ok =
|
|
file->InitBuffer(cmd->callback == ProcessVAGData ? CBuffer::BufferType::REQUEST_VAG
|
|
: CBuffer::BufferType::REQUEST_NORMAL,
|
|
cmd);
|
|
} else {
|
|
file->AllocPages();
|
|
buffer_ok = true;
|
|
}
|
|
|
|
file = nullptr;
|
|
if (buffer_ok == 0) {
|
|
cmd = nullptr;
|
|
known_read_rate = false;
|
|
} else {
|
|
file = cmd->m_pBaseFile;
|
|
known_read_rate = false;
|
|
if (file && file->m_ReadRate) {
|
|
known_read_rate = true;
|
|
}
|
|
// iVar3 = (**(code**)(file->base).vtable)(file);
|
|
// ovrld_log(LogCategory::ISO_QUEUE, "Processing Command 0x{:x} - starting read!\n",
|
|
// (int)cmd->msg_type);
|
|
|
|
// lg::info("ISO - BeginRead");
|
|
cmd->status = file->BeginRead();
|
|
if (cmd->status != EIsoStatus::OK_2) {
|
|
buffer_ok = false;
|
|
if (cmd->m_pBaseFile) {
|
|
cmd->m_pBaseFile->TerminateBuffer();
|
|
}
|
|
cmd = nullptr;
|
|
file = nullptr;
|
|
}
|
|
if (!known_read_rate) {
|
|
time_of_last_unknown_rate_drive_op = GetSystemTimeLow();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ovrld_log(LogCategory::ISO_QUEUE, "Processing Command 0x{:x} - handling message data\n",
|
|
// (int)cmd->msg_type);
|
|
|
|
if (ProcessMessageData(cmd) == 0) {
|
|
cmd = nullptr;
|
|
}
|
|
|
|
if (buffer_ok && cmd) {
|
|
EIsoStatus status = EIsoStatus::ERROR_b;
|
|
if (file) {
|
|
status = file->SyncRead();
|
|
}
|
|
if (!known_read_rate) {
|
|
time_of_last_unknown_rate_drive_op = GetSystemTimeLow();
|
|
}
|
|
if (status == EIsoStatus::ERROR_b) {
|
|
if (cmd->m_pBaseFile && cmd->m_pBaseFile->m_Status != EIsoStatus::NONE_0) {
|
|
cmd->status = EIsoStatus::OK_2;
|
|
}
|
|
} else {
|
|
cmd->status = status;
|
|
if (!cmd->active_c) {
|
|
set_active_c(cmd, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
WaitSema(g_RequestedStreamsList.sema);
|
|
if (g_RequestedStreamsList.pending_data == 1) {
|
|
QueueNewStreamsFromList(&g_RequestedStreamsList);
|
|
auto* vag_info = g_NewStreamsList.next;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (vag_info->id) {
|
|
ovrld_log(LogCategory::ISO_QUEUE, "ISO thread: queueing VAG {}", vag_info->name);
|
|
QueueVAGStream(vag_info);
|
|
}
|
|
vag_info = vag_info->next;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
ISO_VAGCommand* vc = &g_aVagCmds[i];
|
|
if (!vc->music_flag && !vc->flags.stereo_secondary && !vc->flags.scanned && vc->id) {
|
|
ovrld_log(LogCategory::ISO_QUEUE, "ISO thread: stopping {} since it is no longer requested",
|
|
vc->name);
|
|
IsoStopVagStream(vc);
|
|
}
|
|
}
|
|
|
|
SignalSema(g_RequestedStreamsList.sema);
|
|
g_RequestedStreamsList.pending_data = 0;
|
|
|
|
for (int i = 4; i < 6; i++) {
|
|
ISO_VAGCommand* vc = &g_aVagCmds[i];
|
|
if (vc->music_flag && !vc->flags.stereo_secondary && vc->flags.stop && vc->id) {
|
|
ovrld_log(LogCategory::ISO_QUEUE, "ISO thread: stopping music {}", vc->name);
|
|
IsoStopVagStream(vc);
|
|
}
|
|
}
|
|
|
|
// this logic was changed so that the iso thread doesn't sleep when loading a DGO: otherwise
|
|
// we'd spent most of our time letting the thread sleep.
|
|
// instead, if there's an in progress DGO command, we yield.
|
|
// this yield allows DGO RPCs to run. Additionally, the priority of the ISO thread was lowered
|
|
// to allow the RPCs to run during this time.
|
|
bool sleep = !DgoCmdWaiting();
|
|
if (sleep) {
|
|
if (buffer_ok) {
|
|
DelayThread(4000);
|
|
} else {
|
|
// DelayThread(200);
|
|
DelayThread(2000);
|
|
}
|
|
} else {
|
|
YieldThread();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* This function runs the state machine for the double-buffered DGO loading system.
|
|
* There are a few tricks here:
|
|
* - Each DGO file contains a number of objects.
|
|
* - The object loading is double buffered - this state machine toggles between loading to two
|
|
* different buffers. While one buffer is being written, the GOAL linker is processing the other.
|
|
* - The final object is not double buffered. Instead, it is loaded directly to the top of the heap.
|
|
* - New! for jak 2, there is an option to not use the double buffering.
|
|
* - New! for jak 3, there is a very complicated load cancel system
|
|
*/
|
|
EIsoStatus RunDGOStateMachine(ISO_Hdr* m) {
|
|
auto* cmd = (ISO_DGOCommand*)m;
|
|
// lg::info("ISO - DGO state machine");
|
|
int send_count, receive_count;
|
|
|
|
CBaseFile* file = cmd->m_pBaseFile;
|
|
EIsoStatus ret_status = EIsoStatus::OK_2;
|
|
if (!file) {
|
|
return EIsoStatus::OK_2;
|
|
}
|
|
ASSERT(file->m_Buffer.m_pPageList);
|
|
|
|
// handle page boundary crossings - after this call, our CBuffer will be set up properly for
|
|
// processing.
|
|
file->CheckPageBoundary();
|
|
CBuffer* buffer = &file->m_Buffer;
|
|
|
|
int buffer_len = (file->m_Buffer).m_nDataLength;
|
|
ASSERT(buffer_len >= 0);
|
|
|
|
if (cmd->state == ISO_DGOCommand::State::INIT) {
|
|
// these counters are used for debugging the DGO sync stuff.
|
|
cmd->sync_mbox_wait_count = 1;
|
|
cmd->sync_ret_count = 0;
|
|
}
|
|
// CpuSuspendIntr(local_30);
|
|
|
|
// process this DGO as normal, unless we've been asked to cancel this.
|
|
if (cmd->nosync_cancel_pending_flag == 0 || cmd->selected_id != cmd->request_cancel_id) {
|
|
// CpuResumeIntr();
|
|
if (buffer_len == 0) {
|
|
// nothing we can do with no data...
|
|
goto out_of_data;
|
|
}
|
|
do {
|
|
switch (cmd->state) {
|
|
case ISO_DGOCommand::State::INIT:
|
|
ovrld_log(LogCategory::DGO, "DGO: Starting state machine");
|
|
cmd->state = ISO_DGOCommand::State::READ_DGO_HEADER;
|
|
cmd->bytes_processed = 0;
|
|
cmd->finished_first_object = 0;
|
|
cmd->want_abort = 0;
|
|
break;
|
|
case ISO_DGOCommand::State::READ_DGO_HEADER: {
|
|
// here, we work on reading the DGO file's header into our command.
|
|
// first, compute how many bytes we want to read right now, as the max of
|
|
// the remaining header size, and what's buffered
|
|
int bytes_needed = sizeof(DgoHeader) - cmd->bytes_processed;
|
|
if (buffer_len < bytes_needed) {
|
|
bytes_needed = buffer_len;
|
|
}
|
|
|
|
// loop over pages - the header may span multiple pages that aren't adjacent in memory.
|
|
while (bytes_needed) {
|
|
// determine how many bytes to copy from this page
|
|
int bytes_from_this_page = buffer->m_pPageList->m_pCurrentActivePage->m_pPageMemEnd -
|
|
file->m_Buffer.m_pCurrentData + 1;
|
|
if (bytes_needed <= bytes_from_this_page) {
|
|
bytes_from_this_page = bytes_needed;
|
|
}
|
|
ovrld_log(LogCategory::DGO, "DGO: reading {} bytes of dgo header",
|
|
bytes_from_this_page);
|
|
// copy data from buffer into command
|
|
memcpy(((u8*)&cmd->dgo_header) + cmd->bytes_processed, file->m_Buffer.m_pCurrentData,
|
|
bytes_from_this_page);
|
|
|
|
// advance buffer and page
|
|
buffer->AdvanceCurrentData(bytes_from_this_page);
|
|
file->CheckPageBoundary();
|
|
|
|
// advance progress
|
|
cmd->bytes_processed = bytes_from_this_page + cmd->bytes_processed;
|
|
buffer_len = buffer_len - bytes_from_this_page;
|
|
bytes_needed = bytes_needed - bytes_from_this_page;
|
|
}
|
|
|
|
// check if we got the whole header
|
|
if (cmd->bytes_processed == sizeof(DgoHeader)) {
|
|
ovrld_log(LogCategory::DGO, "DGO: got dgo header: {} with {} objects",
|
|
cmd->dgo_header.name, cmd->dgo_header.object_count);
|
|
cmd->bytes_processed = 0;
|
|
cmd->objects_loaded = 0;
|
|
if (cmd->dgo_header.object_count == 1) {
|
|
// if we have only 1 object, go directly to loading to the top buffer
|
|
cmd->ee_dest_buffer = cmd->buffer_top;
|
|
cmd->state = ISO_DGOCommand::State::READ_OBJ_HEADER;
|
|
cmd->buffer_toggle = 0;
|
|
} else {
|
|
// otherwise, start with buffer!
|
|
cmd->buffer_toggle = 1;
|
|
cmd->ee_dest_buffer = cmd->buffer1;
|
|
cmd->state = ISO_DGOCommand::State::READ_OBJ_HEADER;
|
|
}
|
|
}
|
|
} break;
|
|
case ISO_DGOCommand::State::FINISH_OBJ:
|
|
|
|
// sync with EE - if we're loading double-buffered, wait on the EE
|
|
// note that we don't wait on the first object, since both buffers start empty,
|
|
// and we can safely fill both with no syncs.
|
|
// the order of synchronization is a little bit strange. The EE must tell us that it's
|
|
// finished processing buffer A before we tell the EE the location of buffer B.
|
|
// This is needed to get the sync right for the last object - we want the EE to run
|
|
// through all the buffers, then we load the final object, then we notify it. If the EE
|
|
// wouldn't tell us it was done until it got the next object, we'd be unable to do this.
|
|
if (cmd->finished_first_object != 0 && cmd->buffer1 != cmd->buffer2) {
|
|
if (LookSyncMbx() == 0)
|
|
goto exit_no_sync;
|
|
|
|
ovrld_log(LogCategory::DGO,
|
|
"DGO: finished object (2buffer), and got sync message from EE or cancel");
|
|
// iVar3 = 6;
|
|
if (cmd->want_abort != 0) {
|
|
ovrld_log(LogCategory::DGO, "DGO: cancel!! (1)");
|
|
cmd->state = ISO_DGOCommand::State::FINISH_DGO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// for double buffer, notify the EE that we've finished loading.
|
|
if (cmd->buffer1 != cmd->buffer2) {
|
|
cmd->status = EIsoStatus::OK_2;
|
|
cmd->selected_buffer = cmd->buffer_toggle != 1 ? cmd->buffer2 : cmd->buffer1;
|
|
ovrld_log(LogCategory::DGO,
|
|
"DGO: finished object (2buffer) - notifying EE of location");
|
|
ReturnMessage(cmd);
|
|
sLoadDGO.sync_ret_count = sLoadDGO.sync_ret_count + 1;
|
|
}
|
|
|
|
// for single buffer, sync with EE so we know the next location to load.
|
|
// note that we've already returned the message for the single buffer case
|
|
if ((cmd->buffer1 == cmd->buffer2) &&
|
|
(cmd->objects_loaded + 1 < (s32)cmd->dgo_header.object_count)) {
|
|
if (LookSyncMbx() == 0)
|
|
goto exit_no_sync;
|
|
ovrld_log(LogCategory::DGO,
|
|
"DGO: finished object (1buffer), and got sync message from EE or cancel");
|
|
if (cmd->want_abort != 0) {
|
|
ovrld_log(LogCategory::DGO, "DGO: cancel!! (2)");
|
|
cmd->state = ISO_DGOCommand::State::FINISH_DGO;
|
|
break;
|
|
}
|
|
}
|
|
cmd->finished_first_object = 1;
|
|
if (cmd->buffer_toggle == 1) {
|
|
cmd->ee_dest_buffer = cmd->buffer2;
|
|
cmd->buffer_toggle = 2;
|
|
} else {
|
|
cmd->buffer_toggle = 1;
|
|
cmd->ee_dest_buffer = cmd->buffer1;
|
|
}
|
|
|
|
if (cmd->objects_loaded + 1 == (int)cmd->dgo_header.object_count) {
|
|
cmd->state = ISO_DGOCommand::State::READ_LAST_OBJ;
|
|
} else {
|
|
cmd->state = ISO_DGOCommand::State::READ_OBJ_HEADER;
|
|
}
|
|
// LAB_000073f8:
|
|
// cmd->state = iVar3;
|
|
break;
|
|
case ISO_DGOCommand::State::READ_LAST_OBJ:
|
|
// do an extra sync here to wait for the EE to finish processing both temporary buffers.
|
|
// the next load will be to the heap top, which may overlap the temp buffers.
|
|
// lg::warn("in read last obj!");
|
|
if (LookSyncMbx() == 0)
|
|
goto exit_no_sync;
|
|
ovrld_log(LogCategory::DGO,
|
|
"DGO: got final object sync message - can start running that now");
|
|
if (cmd->want_abort != 0) {
|
|
cmd->state = ISO_DGOCommand::State::FINISH_DGO;
|
|
ovrld_log(LogCategory::DGO, "DGO: cancel!! (3)");
|
|
} else {
|
|
cmd->ee_dest_buffer = cmd->buffer_top;
|
|
cmd->state = ISO_DGOCommand::State::READ_OBJ_HEADER;
|
|
cmd->buffer_toggle = 0;
|
|
}
|
|
|
|
break;
|
|
case ISO_DGOCommand::State::READ_OBJ_HEADER: {
|
|
int bytes_needed = sizeof(ObjectHeader) - cmd->bytes_processed;
|
|
if (buffer_len < bytes_needed) {
|
|
bytes_needed = buffer_len;
|
|
}
|
|
while (bytes_needed) {
|
|
int bytes_from_this_page = buffer->m_pPageList->m_pCurrentActivePage->m_pPageMemEnd -
|
|
file->m_Buffer.m_pCurrentData + 1;
|
|
if (bytes_needed <= bytes_from_this_page) {
|
|
bytes_from_this_page = bytes_needed;
|
|
}
|
|
ASSERT(bytes_from_this_page >= 0);
|
|
ovrld_log(LogCategory::DGO, "DGO: reading {} bytes of object header",
|
|
bytes_from_this_page);
|
|
memcpy(((u8*)&cmd->obj_header) + cmd->bytes_processed, (file->m_Buffer).m_pCurrentData,
|
|
bytes_from_this_page);
|
|
buffer->AdvanceCurrentData(bytes_from_this_page);
|
|
file->CheckPageBoundary();
|
|
cmd->bytes_processed = bytes_from_this_page + cmd->bytes_processed;
|
|
buffer_len = buffer_len - bytes_from_this_page;
|
|
bytes_needed = bytes_needed - bytes_from_this_page;
|
|
}
|
|
if (cmd->bytes_processed == sizeof(ObjectHeader)) {
|
|
ovrld_log(LogCategory::DGO, "DGO: got object header {} {}", cmd->obj_header.name,
|
|
cmd->obj_header.size);
|
|
cmd->obj_header.size = (cmd->obj_header.size + 0xf) & 0xfffffff0;
|
|
DMA_SendToEE(cmd->ee_dest_buffer, &cmd->obj_header, sizeof(ObjectHeader), nullptr,
|
|
nullptr);
|
|
cmd->ee_dest_buffer = cmd->ee_dest_buffer + sizeof(ObjectHeader);
|
|
cmd->state = ISO_DGOCommand::State::READ_OBJ_DATA;
|
|
cmd->bytes_processed = 0;
|
|
}
|
|
} break;
|
|
case ISO_DGOCommand::State::READ_OBJ_DATA: {
|
|
int bytes_needed = cmd->obj_header.size - cmd->bytes_processed;
|
|
if (buffer_len < bytes_needed) {
|
|
bytes_needed = buffer_len;
|
|
}
|
|
|
|
while (bytes_needed) {
|
|
auto* page = buffer->m_pPageList->m_pCurrentActivePage;
|
|
int bytes_from_this_page = page->m_pPageMemEnd - file->m_Buffer.m_pCurrentData + 1;
|
|
if (bytes_needed <= bytes_from_this_page) {
|
|
bytes_from_this_page = bytes_needed;
|
|
}
|
|
int ret = page->AddDmaRef();
|
|
ASSERT(ret >= 0);
|
|
|
|
DMA_SendToEE(cmd->ee_dest_buffer, (file->m_Buffer).m_pCurrentData, bytes_from_this_page,
|
|
CopyDataDmaCallback, page);
|
|
buffer->AdvanceCurrentData(bytes_from_this_page);
|
|
file->CheckPageBoundary();
|
|
cmd->ee_dest_buffer = bytes_from_this_page + cmd->ee_dest_buffer;
|
|
cmd->bytes_processed = bytes_from_this_page + cmd->bytes_processed;
|
|
buffer_len = buffer_len - bytes_from_this_page;
|
|
bytes_needed = bytes_needed - bytes_from_this_page;
|
|
|
|
if (!file->m_Buffer.m_pCurrentData) {
|
|
buffer_len = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cmd->bytes_processed == (int)cmd->obj_header.size) {
|
|
cmd->objects_loaded = cmd->objects_loaded + 1;
|
|
if (cmd->objects_loaded < (int)cmd->dgo_header.object_count) {
|
|
if (cmd->buffer1 == cmd->buffer2) {
|
|
cmd->state = ISO_DGOCommand::State::FINISH_OBJ_SINGLE_BUFFER;
|
|
} else {
|
|
cmd->state = ISO_DGOCommand::State::FINISH_OBJ;
|
|
}
|
|
cmd->bytes_processed = 0;
|
|
} else {
|
|
ret_status = EIsoStatus::NONE_0;
|
|
cmd->state = ISO_DGOCommand::State::FINISH_DGO;
|
|
}
|
|
}
|
|
} break;
|
|
case ISO_DGOCommand::State::FINISH_DGO:
|
|
ret_status = EIsoStatus::NONE_0;
|
|
file->m_Buffer.m_pCurrentData = nullptr;
|
|
file->m_Buffer.m_pCurrentPageStart = nullptr;
|
|
goto out_of_data;
|
|
case ISO_DGOCommand::State::FINISH_OBJ_SINGLE_BUFFER:
|
|
cmd->status = EIsoStatus::OK_2;
|
|
if (cmd->buffer_toggle == 1) {
|
|
cmd->selected_buffer = cmd->buffer1;
|
|
} else {
|
|
cmd->selected_buffer = cmd->buffer2;
|
|
}
|
|
ReturnMessage((ISO_VAGCommand*)cmd);
|
|
sLoadDGO.sync_ret_count = sLoadDGO.sync_ret_count + 1;
|
|
cmd->state = ISO_DGOCommand::State::FINISH_OBJ;
|
|
}
|
|
} while (buffer_len);
|
|
exit_no_sync:
|
|
if (ret_status != EIsoStatus::NONE_0)
|
|
goto LAB_0000743c;
|
|
} else {
|
|
cmd->nosync_cancel_ack = 1;
|
|
cmd->nosync_cancel_pending_flag = 0;
|
|
cmd->acked_cancel_id = cmd->request_cancel_id;
|
|
send_count = sLoadDGO.sync_sent_count - sLoadDGO.sync_mbox_wait_count;
|
|
receive_count = sLoadDGO.sync_ret_count - sLoadDGO.sync_mbox_wait_count;
|
|
// CpuResumeIntr(local_30[0]);
|
|
if (0 < send_count) {
|
|
receive_count = receive_count + -1;
|
|
WaitMbx(g_nSyncMbx);
|
|
sLoadDGO.sync_mbox_wait_count = sLoadDGO.sync_mbox_wait_count + 1;
|
|
}
|
|
ret_status = EIsoStatus::IDLE_1;
|
|
if (-1 < receive_count) {
|
|
LAB_0000743c:
|
|
if (buffer_len) {
|
|
file->m_Buffer.m_nDataLength = buffer_len;
|
|
return ret_status;
|
|
}
|
|
goto out_of_data;
|
|
}
|
|
ret_status = EIsoStatus::NONE_0;
|
|
}
|
|
(file->m_Buffer).m_pCurrentData = nullptr;
|
|
(file->m_Buffer).m_pCurrentPageStart = nullptr;
|
|
out_of_data:
|
|
(file->m_Buffer).m_nDataLength = 0;
|
|
return ret_status;
|
|
}
|
|
|
|
u32 DGOThread() {
|
|
sceSifQueueData dq;
|
|
sceSifServeData serve;
|
|
|
|
// setup RPC.
|
|
CpuDisableIntr();
|
|
sceSifInitRpc(0);
|
|
sceSifSetRpcQueue(&dq, GetThreadId());
|
|
sceSifRegisterRpc(&serve, RpcId::DGO, RPC_DGO, sRPCBuff, kRpcBuffSize, nullptr, nullptr, &dq);
|
|
CpuEnableIntr();
|
|
sceSifRpcLoop(&dq);
|
|
return 0;
|
|
}
|
|
|
|
void* RPC_DGO(unsigned int fno, void* msg_ptr, int) {
|
|
RPC_Dgo_Cmd* cmd = (RPC_Dgo_Cmd*)msg_ptr;
|
|
switch (fno) {
|
|
case DgoFno::LOAD:
|
|
LoadDGO(cmd);
|
|
break;
|
|
case DgoFno::LOAD_NEXT:
|
|
LoadNextDGO(cmd);
|
|
break;
|
|
case DgoFno::CANCEL:
|
|
CancelDGO(cmd);
|
|
break;
|
|
default:
|
|
cmd->status = 1;
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
return msg_ptr;
|
|
}
|
|
|
|
void* foo = 0;
|
|
|
|
/*!
|
|
* Send a sync message to unblock the DGO thread when doing an async cancel.
|
|
*/
|
|
bool NotifyDGO() {
|
|
// CpuSuspendIntr(local_10);
|
|
bool pending_cancel = sLoadDGO.nosync_cancel_ack == 0;
|
|
if (pending_cancel) {
|
|
sLoadDGO.sync_sent_count = sLoadDGO.sync_sent_count + 1;
|
|
}
|
|
// CpuResumeIntr(local_10[0]);
|
|
if (pending_cancel) {
|
|
SendMbx(g_nSyncMbx, &foo);
|
|
}
|
|
return pending_cancel;
|
|
}
|
|
|
|
void LoadDGO(RPC_Dgo_Cmd* cmd) {
|
|
ISOFileDef* file = get_file_system()->Find(cmd->name);
|
|
|
|
if (!file) {
|
|
ovrld_log(LogCategory::WARN, "DGO RPC: LoadDGO {} file not found\n", cmd->name);
|
|
cmd->status = 1;
|
|
return;
|
|
}
|
|
if (sLoadDGO.last_id < cmd->cgo_id) {
|
|
ovrld_log(LogCategory::RPC, "DGO RPC: new command ID, starting a load for {}", cmd->name);
|
|
CancelDGO(nullptr);
|
|
sLoadDGO.msg_type = ISO_Hdr::MsgType::DGO_LOAD;
|
|
sLoadDGO.selected_id = cmd->cgo_id;
|
|
sLoadDGO.mbox_reply = g_nDGOMbx;
|
|
sLoadDGO.thread_to_wake = 0;
|
|
sLoadDGO.buffer1 = (u8*)(u64)cmd->buffer1;
|
|
sLoadDGO.buffer2 = (u8*)(u64)cmd->buffer2;
|
|
sLoadDGO.buffer_top = (u8*)(u64)cmd->buffer_heap_top;
|
|
sLoadDGO.file_def = file;
|
|
// CpuSuspendIntr(local_18);
|
|
if (0 < cmd->cgo_id - sLoadDGO.last_id) {
|
|
sLoadDGO.last_id = cmd->cgo_id;
|
|
}
|
|
sLoadDGO.sync_sent_count = 1;
|
|
sLoadDGO.nosync_cancel_ack = 0;
|
|
// CpuResumeIntr(local_18[0]);
|
|
ASSERT(sLoadDGO.msg_type != ISO_Hdr::MsgType::MSG_0);
|
|
SendMbx(g_nISOMbx, &sLoadDGO);
|
|
WaitMbx(g_nDGOMbx);
|
|
if (sLoadDGO.status == EIsoStatus::OK_2) {
|
|
cmd->status = 2;
|
|
return;
|
|
}
|
|
if (sLoadDGO.status != EIsoStatus::NONE_0) {
|
|
cmd->status = 1;
|
|
sLoadDGO.msg_type = ISO_Hdr::MsgType::MSG_0;
|
|
sLoadDGO.selected_id = -1;
|
|
return;
|
|
}
|
|
} else {
|
|
ovrld_log(LogCategory::WARN, "DGO RPC: old command ID seen for {} (got {}, saw {}), ignoring\n",
|
|
cmd->name, cmd->cgo_id, sLoadDGO.last_id);
|
|
}
|
|
cmd->buffer1 = cmd->buffer_heap_top;
|
|
cmd->status = 0;
|
|
sLoadDGO.msg_type = ISO_Hdr::MsgType::MSG_0;
|
|
sLoadDGO.selected_id = -1;
|
|
}
|
|
|
|
void LoadNextDGO(RPC_Dgo_Cmd* cmd) {
|
|
if (sLoadDGO.msg_type == ISO_Hdr::MsgType::MSG_0) {
|
|
ovrld_log(LogCategory::WARN, "DGO RPC: LoadNextDGO {} load not running! Ignoring\n", cmd->name);
|
|
cmd->status = 1;
|
|
return;
|
|
}
|
|
|
|
sLoadDGO.buffer_top = (u8*)(u64)cmd->buffer_heap_top;
|
|
sLoadDGO.buffer1 = (u8*)(u64)cmd->buffer1;
|
|
sLoadDGO.buffer2 = (u8*)(u64)cmd->buffer2;
|
|
bool unblocked = NotifyDGO(); // tell dgo state machine to run
|
|
int status = 3;
|
|
if (unblocked != 0) {
|
|
WaitMbx(g_nDGOMbx); // wait for it to finish, and return to EE
|
|
if (sLoadDGO.status == EIsoStatus::OK_2) {
|
|
cmd->status = 2;
|
|
cmd->buffer1 = (u32)(u64)sLoadDGO.selected_buffer;
|
|
return;
|
|
}
|
|
status = 11;
|
|
if (sLoadDGO.status == EIsoStatus::NONE_0) {
|
|
cmd->status = 0;
|
|
cmd->buffer1 = cmd->buffer_heap_top;
|
|
sLoadDGO.msg_type = ISO_Hdr::MsgType::MSG_0;
|
|
sLoadDGO.selected_id = -1;
|
|
return;
|
|
}
|
|
} else {
|
|
ovrld_log(LogCategory::WARN, "DGO RPC: LoadNextDGO {} already cancelled! Ignoring\n",
|
|
cmd->name);
|
|
}
|
|
cmd->status = status;
|
|
sLoadDGO.msg_type = ISO_Hdr::MsgType::MSG_0;
|
|
sLoadDGO.selected_id = -1;
|
|
}
|
|
|
|
void CancelDGO(RPC_Dgo_Cmd* param_1) {
|
|
ovrld_log(LogCategory::WARN, "DGO RPC: CancelDGO {}", param_1 ? param_1->name : "NO CMD");
|
|
if (sLoadDGO.msg_type != ISO_Hdr::MsgType::MSG_0) {
|
|
sLoadDGO.want_abort = 1;
|
|
if (NotifyDGO()) {
|
|
WaitMbx(g_nDGOMbx);
|
|
}
|
|
if (param_1) {
|
|
param_1->status = 3;
|
|
}
|
|
sLoadDGO.selected_id = -1;
|
|
sLoadDGO.msg_type = ISO_Hdr::MsgType::MSG_0;
|
|
}
|
|
}
|
|
|
|
void CancelDGONoSync(int id) {
|
|
ovrld_log(LogCategory::WARN, "DGO RPC: CancelDGONoSync {}", id);
|
|
// CpuSuspendIntr(local_10);
|
|
sLoadDGO.nosync_cancel_pending_flag = 1;
|
|
if (0 < id - sLoadDGO.last_id) {
|
|
sLoadDGO.last_id = id;
|
|
}
|
|
sLoadDGO.request_cancel_id = id;
|
|
// CpuResumeIntr(local_10[0]);
|
|
}
|
|
|
|
EIsoStatus CopyDataToEE(ISO_Hdr* msg) {
|
|
return CopyData((ISO_LoadSingle*)msg, CopyKind::EE);
|
|
}
|
|
|
|
EIsoStatus CopyDataToIOP(ISO_Hdr* msg) {
|
|
return CopyData((ISO_LoadSingle*)msg, CopyKind::IOP);
|
|
}
|
|
|
|
EIsoStatus CopyDataSbkLoad(ISO_Hdr* msg) {
|
|
return CopyData((ISO_LoadSingle*)msg, CopyKind::SBK);
|
|
}
|
|
|
|
void CopyDataDmaCallback(void* in) {
|
|
((CPage*)in)->ReleaseDmaRef();
|
|
}
|
|
|
|
EIsoStatus CopyData(ISO_LoadCommon* cmd, CopyKind kind) {
|
|
ASSERT(cmd);
|
|
auto* file = cmd->m_pBaseFile;
|
|
if (file == (CISOCDFile*)0x0) {
|
|
return EIsoStatus::ERROR_NO_FILE;
|
|
}
|
|
|
|
EIsoStatus status = EIsoStatus::OK_2;
|
|
if (file->m_Buffer.m_eBufferType != CBuffer::BufferType::NORMAL) {
|
|
CBuffer* buffer = &file->m_Buffer;
|
|
CPage* page = nullptr;
|
|
if (buffer->m_pPageList && buffer->m_nDataLength) {
|
|
if (file->CheckPageBoundary()) {
|
|
page = file->m_Buffer.m_pPageList->m_pCurrentActivePage;
|
|
}
|
|
if (page && cmd->progress_bytes < cmd->length_to_copy) {
|
|
int len;
|
|
do {
|
|
// length we want
|
|
len = cmd->length_to_copy - cmd->progress_bytes;
|
|
|
|
// trim to buffered
|
|
if (buffer->m_nDataLength < len) {
|
|
len = buffer->m_nDataLength;
|
|
}
|
|
|
|
// trim to page
|
|
if (page->m_pPageMemEnd - buffer->m_pCurrentData + 1 < len) {
|
|
len = page->m_pPageMemEnd - buffer->m_pCurrentData + 1;
|
|
}
|
|
|
|
if (0 < len) {
|
|
switch (kind) {
|
|
case CopyKind::IOP: {
|
|
if (page->AddRef() < 1) {
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
memcpy(cmd->dest_ptr, buffer->m_pCurrentData, len);
|
|
if (page->ReleaseRef() < 0)
|
|
ASSERT_NOT_REACHED();
|
|
} break;
|
|
case CopyKind::EE: {
|
|
if (page->AddDmaRef() < 1) {
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
DMA_SendToEE(cmd->dest_ptr, buffer->m_pCurrentData, len, CopyDataDmaCallback, page);
|
|
} break;
|
|
case CopyKind::SBK: {
|
|
WaitSema(g_n989Semaphore);
|
|
if (g_bSoundEnable == 0) {
|
|
SignalSema(g_n989Semaphore);
|
|
return EIsoStatus::ERROR_NO_SOUND;
|
|
}
|
|
if (page->AddRef() < 1) {
|
|
ASSERT_NOT_REACHED();
|
|
SignalSema(g_n989Semaphore);
|
|
return EIsoStatus::OK_2;
|
|
}
|
|
auto* bank_info = ((ISO_LoadSoundbank*)cmd)->bank_info;
|
|
|
|
// hack: added
|
|
if (cmd->progress_bytes == 0) {
|
|
snd_BankLoadFromIOPPartialEx_Start();
|
|
}
|
|
snd_BankLoadFromIOPPartialEx(buffer->m_pCurrentData, len, bank_info->m_nSpuMemLoc,
|
|
bank_info->m_nSpuMemSize);
|
|
if (cmd->progress_bytes + len == cmd->length_to_copy) {
|
|
bank_info->snd_handle = snd_BankLoadFromIOPPartialEx_Completion();
|
|
snd_ResolveBankXREFS();
|
|
// TODO: this also set field_0x28... is that needed??
|
|
}
|
|
|
|
if (page->ReleaseRef() < 0) {
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
SignalSema(g_n989Semaphore);
|
|
} break;
|
|
default:
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
cmd->dest_ptr = cmd->dest_ptr + len;
|
|
cmd->progress_bytes = len + cmd->progress_bytes;
|
|
buffer->AdvanceCurrentData(len);
|
|
if (!file->CheckPageBoundary())
|
|
break;
|
|
page = buffer->m_pPageList->m_pCurrentActivePage;
|
|
}
|
|
|
|
if (!page || len < 1)
|
|
break;
|
|
} while (true);
|
|
}
|
|
}
|
|
if ((u32)cmd->progress_bytes < (u32)cmd->length_to_copy) {
|
|
return status;
|
|
}
|
|
buffer->m_pPageList->CancelActivePages();
|
|
if (status != EIsoStatus::OK_2) {
|
|
return status;
|
|
}
|
|
return EIsoStatus::NONE_0;
|
|
}
|
|
return EIsoStatus::ERROR_NO_FILE;
|
|
}
|
|
|
|
EIsoStatus NullCallback(ISO_Hdr*) {
|
|
return EIsoStatus::NULL_CALLBACK;
|
|
}
|
|
|
|
void set_active_a(ISO_Hdr* cmd, int val) {
|
|
cmd->active_a = val;
|
|
}
|
|
|
|
void set_active_b(ISO_Hdr* cmd, int val) {
|
|
cmd->active_b = val;
|
|
}
|
|
|
|
void set_active_c(ISO_Hdr* cmd, int val) {
|
|
cmd->active_c = val;
|
|
}
|
|
|
|
} // namespace jakx
|