mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-26 16:00:52 +00:00
[wip] Jak 3 Overlord (#3567)
This commit is contained in:
parent
57772c59a0
commit
e81431bd21
@ -29,8 +29,11 @@ class Wrapper {
|
||||
Wrapper(const std::string& _username,
|
||||
const Config& config,
|
||||
const StartupFile& startup,
|
||||
bool nrepl_alive)
|
||||
: username(_username), repl_config(config), startup_file(startup), nrepl_alive(nrepl_alive) {}
|
||||
bool _nrepl_alive)
|
||||
: username(_username),
|
||||
repl_config(config),
|
||||
startup_file(startup),
|
||||
nrepl_alive(_nrepl_alive) {}
|
||||
replxx::Replxx& get_repl() { return repl; }
|
||||
void init_settings();
|
||||
void reload_startup_file();
|
||||
|
@ -27,11 +27,11 @@ Range<int> parse_json_optional_integer_range(const nlohmann::json& json);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void json_get_optional(const nlohmann::json& json,
|
||||
void json_get_optional(const nlohmann::json& j,
|
||||
const std::string& key,
|
||||
std::optional<T>& optionalValue) {
|
||||
if (json.contains(key) && !json[key].is_null()) {
|
||||
optionalValue = json[key].get<T>();
|
||||
if (j.contains(key) && !j[key].is_null()) {
|
||||
optionalValue = j[key].get<T>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ namespace decompiler {
|
||||
*/
|
||||
class LinkedObjectFile {
|
||||
public:
|
||||
LinkedObjectFile(GameVersion version) : version(version){};
|
||||
LinkedObjectFile(GameVersion _version) : version(_version) {}
|
||||
void set_segment_count(int n_segs);
|
||||
void push_back_word_to_segment(uint32_t word, int segment);
|
||||
int get_label_id_for(int seg, int offset);
|
||||
|
@ -138,7 +138,7 @@
|
||||
[7, "(function none)"],
|
||||
[3, "(function symbol :behavior process)"]
|
||||
],
|
||||
"scene": [[4, "(function none :behavior scene-player)"]],
|
||||
"scene": [[4, "(function symbol :behavior scene-player)"]],
|
||||
"pov-camera": [
|
||||
[
|
||||
7,
|
||||
|
@ -254,6 +254,29 @@ set(RUNTIME_SOURCE
|
||||
overlord/jak2/streamlfo.cpp
|
||||
overlord/jak2/streamlist.cpp
|
||||
overlord/jak2/vag.cpp
|
||||
overlord/jak3/overlord.cpp
|
||||
overlord/jak3/pagemanager.cpp
|
||||
overlord/jak3/iso_cd.cpp
|
||||
overlord/jak3/dma.cpp
|
||||
overlord/jak3/iso.cpp
|
||||
overlord/jak3/iso_queue.cpp
|
||||
overlord/jak3/srpc.cpp
|
||||
overlord/jak3/vag.cpp
|
||||
overlord/jak3/ssound.cpp
|
||||
overlord/jak3/iso_api.cpp
|
||||
overlord/jak3/spustreams.cpp
|
||||
overlord/jak3/list.cpp
|
||||
overlord/jak3/vblank_handler.cpp
|
||||
overlord/jak3/dvd_driver.cpp
|
||||
overlord/jak3/basefile.cpp
|
||||
overlord/jak3/basefilesystem.cpp
|
||||
overlord/jak3/ramdisk.cpp
|
||||
overlord/jak3/isocommon.cpp
|
||||
overlord/jak3/init.cpp
|
||||
overlord/jak3/stream.cpp
|
||||
overlord/jak3/sbank.cpp
|
||||
overlord/jak3/soundcommon.cpp
|
||||
overlord/jak3/streamlist.cpp
|
||||
runtime.cpp
|
||||
sce/deci2.cpp
|
||||
sce/iop.cpp
|
||||
|
@ -31,19 +31,3 @@ struct RPC_Dgo_Cmd {
|
||||
// a buffer.
|
||||
uint8_t pad[32];
|
||||
};
|
||||
|
||||
namespace jak3 {
|
||||
struct RPC_Dgo_Cmd {
|
||||
uint16_t rsvd;
|
||||
uint16_t result;
|
||||
uint32_t buffer1;
|
||||
uint32_t buffer2;
|
||||
uint32_t buffer_heap_top;
|
||||
char name[16];
|
||||
uint16_t cgo_id;
|
||||
uint8_t pad[30];
|
||||
};
|
||||
static_assert(sizeof(RPC_Dgo_Cmd) == 0x40);
|
||||
} // namespace jak3
|
||||
|
||||
static_assert(sizeof(RPC_Dgo_Cmd) == sizeof(jak3::RPC_Dgo_Cmd));
|
||||
|
@ -26,8 +26,8 @@ class EyeRenderer;
|
||||
struct SharedRenderState {
|
||||
explicit SharedRenderState(std::shared_ptr<TexturePool> _texture_pool,
|
||||
std::shared_ptr<Loader> _loader,
|
||||
GameVersion version)
|
||||
: shaders(version), texture_pool(_texture_pool), loader(_loader) {}
|
||||
GameVersion _version)
|
||||
: shaders(_version), texture_pool(_texture_pool), loader(_loader), version(_version) {}
|
||||
ShaderLibrary shaders;
|
||||
std::shared_ptr<TexturePool> texture_pool;
|
||||
std::shared_ptr<Loader> loader;
|
||||
|
@ -129,8 +129,8 @@ Hfrag::HfragLevel* Hfrag::get_hfrag_level(const std::string& name,
|
||||
// first, see if this level is loaded. If not, there's nothing we can do.
|
||||
auto lev_data = render_state->loader->get_tfrag3_level(name);
|
||||
if (!lev_data) {
|
||||
printf("[hfrag] can't display %s because it's not loaded.\n", name.c_str());
|
||||
render_state->loader->debug_print_loaded_levels();
|
||||
// printf("[hfrag] can't display %s because it's not loaded.\n", name.c_str());
|
||||
// render_state->loader->debug_print_loaded_levels();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,6 @@ struct link_control {
|
||||
}
|
||||
};
|
||||
|
||||
void klink_init_globals();
|
||||
Ptr<u8> c_symlink2(Ptr<u8> objData, Ptr<u8> linkObj, Ptr<u8> relocTable);
|
||||
|
||||
extern link_control saved_link_control;
|
||||
|
@ -16,7 +16,6 @@ void load_and_link_dgo_from_c_fast(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
u32 linkFlag,
|
||||
s32 bufferSize);
|
||||
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size);
|
||||
void kdgo_init_globals();
|
||||
extern RPC_Dgo_Cmd sMsg[2];
|
||||
extern RPC_Dgo_Cmd* sLastMsg;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "game/kernel/common/kdgo.h"
|
||||
#include "game/kernel/common/kmalloc.h"
|
||||
#include "game/kernel/jak3/klink.h"
|
||||
#include "game/overlord/jak3/rpc_interface.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
@ -39,7 +40,7 @@ void BeginLoadingDGO(const char* name, Ptr<u8> buffer1, Ptr<u8> buffer2, Ptr<u8>
|
||||
RpcSync(DGO_RPC_CHANNEL); // make sure old RPC is finished
|
||||
|
||||
// put a dummy value here just to make sure the IOP overwrites it.
|
||||
sMsg[msgID].result = DGO_RPC_RESULT_INIT; // !! this is 666
|
||||
sMsg[msgID].status = DGO_RPC_RESULT_INIT; // !! this is 666
|
||||
|
||||
// inform IOP of buffers
|
||||
sMsg[msgID].buffer1 = buffer1.offset;
|
||||
@ -78,13 +79,13 @@ Ptr<u8> GetNextDGO(u32* lastObjectFlag) {
|
||||
Ptr<u8> buffer(0);
|
||||
if (sLastMsg) {
|
||||
// if we got a good result, get pointer to object
|
||||
if ((sLastMsg->result == DGO_RPC_RESULT_MORE) || (sLastMsg->result == DGO_RPC_RESULT_DONE)) {
|
||||
if ((sLastMsg->status == DGO_RPC_RESULT_MORE) || (sLastMsg->status == DGO_RPC_RESULT_DONE)) {
|
||||
buffer.offset =
|
||||
sLastMsg->buffer1; // buffer 1 always contains location of most recently loaded object.
|
||||
}
|
||||
|
||||
// not the last one, so don't set the flag.
|
||||
if (sLastMsg->result == DGO_RPC_RESULT_MORE) {
|
||||
if (sLastMsg->status == DGO_RPC_RESULT_MORE) {
|
||||
*lastObjectFlag = 0;
|
||||
}
|
||||
|
||||
@ -111,7 +112,7 @@ void ContinueLoadingDGO(Ptr<u8> b1, Ptr<u8> b2, Ptr<u8> heapPtr) {
|
||||
u32 msgID = sMsgNum;
|
||||
jak3::RPC_Dgo_Cmd* sendBuff = sMsg + sMsgNum;
|
||||
sMsgNum = sMsgNum ^ 1;
|
||||
sendBuff->result = DGO_RPC_RESULT_INIT;
|
||||
sendBuff->status = DGO_RPC_RESULT_INIT;
|
||||
sMsg[msgID].buffer1 = b1.offset;
|
||||
sMsg[msgID].buffer2 = b2.offset;
|
||||
sMsg[msgID].buffer_heap_top = heapPtr.offset;
|
||||
|
@ -30,7 +30,7 @@ void StopVAG(VagCmd* cmd, int /*param_2*/);
|
||||
enum VolumeCategory {
|
||||
DIALOGUE = 2, // VAG streams. Copied "dialogue" name from jak 1.
|
||||
};
|
||||
int MasterVolume[17];
|
||||
int MasterVolume[32];
|
||||
|
||||
void vag_init_globals() {
|
||||
memset(VagCmds, 0, sizeof(VagCmds));
|
||||
|
@ -113,7 +113,7 @@ extern int TrapSRAM[N_VAG_CMDS];
|
||||
extern int StreamVoice[N_VAG_CMDS];
|
||||
|
||||
extern int ActiveVagStreams;
|
||||
extern int MasterVolume[17];
|
||||
extern int MasterVolume[32];
|
||||
|
||||
void vag_init_globals();
|
||||
|
||||
|
333
game/overlord/jak3/basefile.cpp
Normal file
333
game/overlord/jak3/basefile.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
#include "basefile.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/overlord/jak3/pagemanager.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_basefile() {}
|
||||
|
||||
/*!
|
||||
* Construct a CBaseFile in an unused state.
|
||||
*/
|
||||
CBaseFile::CBaseFile() {
|
||||
m_Buffer.m_pCurrentData = nullptr;
|
||||
m_Buffer.m_pCurrentPageStart = nullptr;
|
||||
m_Buffer.m_nMinNumPages = 1;
|
||||
m_Buffer.m_nMaxNumPages = kDefaultBufferPageCount;
|
||||
m_Buffer.m_nDataLength = 0;
|
||||
m_Buffer.m_pPageList = nullptr;
|
||||
m_Buffer.m_pIsoCmd = nullptr;
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
|
||||
|
||||
m_ProcessDataSemaphore = -1;
|
||||
m_FileDef = nullptr;
|
||||
m_FileKind = Kind::UNKNOWN;
|
||||
m_Status = EIsoStatus::NONE_0;
|
||||
m_ReadRate = 0;
|
||||
m_LengthPages = 0;
|
||||
m_PageOffset = 0;
|
||||
m_nNumPages = kDefaultBufferPageCount;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Construct a CBaseFile for a given file, but keep it in the "idle" state, with no buffer
|
||||
* allocated.
|
||||
*/
|
||||
CBaseFile::CBaseFile(const jak3::ISOFileDef* file, int semaphore) {
|
||||
m_Buffer.m_pCurrentData = nullptr;
|
||||
m_Buffer.m_nMaxNumPages = kDefaultBufferPageCount;
|
||||
m_Buffer.m_nDataLength = 0;
|
||||
m_Buffer.m_nMinNumPages = 1;
|
||||
m_Buffer.m_pPageList = nullptr;
|
||||
m_Buffer.m_pIsoCmd = nullptr;
|
||||
m_Buffer.m_pCurrentPageStart = nullptr;
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
|
||||
|
||||
m_nNumPages = kDefaultBufferPageCount;
|
||||
m_FileDef = file;
|
||||
m_ProcessDataSemaphore = semaphore;
|
||||
m_FileKind = Kind::UNKNOWN;
|
||||
m_Status = EIsoStatus::IDLE_1;
|
||||
m_ReadRate = 0;
|
||||
m_LengthPages = 0;
|
||||
m_PageOffset = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Update our buffer to handle crossing page boundaries, return pointer to next data.
|
||||
*/
|
||||
uint8_t* CBaseFile::CheckPageBoundary() {
|
||||
// Can't check page boundary if the buffer is not allocated.
|
||||
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
|
||||
|
||||
// Can't check page boundary if there is no manager
|
||||
ASSERT(m_Buffer.m_pPageList);
|
||||
|
||||
CPageList* page_list = m_Buffer.m_pPageList;
|
||||
CPage* page = page_list->m_pCurrentActivePage;
|
||||
|
||||
// can only return data if there's an active page
|
||||
if (!page || page_list->m_nNumActivePages <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// buffer doesn't know the current page, just reset it to the start of the active page.
|
||||
if (m_Buffer.m_pCurrentPageStart == nullptr) {
|
||||
m_Buffer.m_pCurrentData = page->m_pPageMemStart;
|
||||
m_Buffer.m_pCurrentPageStart = page->m_pPageMemStart;
|
||||
} else {
|
||||
uint8_t* end_ptr = page->m_pPageMemEnd;
|
||||
// check if our data pointer crossed the page boundary
|
||||
if (end_ptr <= m_Buffer.m_pCurrentData) {
|
||||
// it did!
|
||||
uint8_t* past_boundary_ptr = m_Buffer.m_pCurrentData;
|
||||
|
||||
// get the next active page
|
||||
CPage* next_page = page_list->StepActivePage();
|
||||
if (!next_page) {
|
||||
// no more active pages, no data to process
|
||||
m_Buffer.m_pCurrentPageStart = nullptr;
|
||||
m_Buffer.m_pCurrentData = nullptr;
|
||||
ovrld_log(LogCategory::PAGING, "File {} ran out of pages in CheckPageBoundary",
|
||||
m_FileDef->name.data);
|
||||
} else {
|
||||
// this is a little weird, but if we went past the end of the previous page, we actually
|
||||
// start at an offset into the next page - perhaps the user could know that pages are
|
||||
// consecutive in memory?
|
||||
uint8_t* new_page_mem = next_page->m_pPageMemStart;
|
||||
m_Buffer.m_pCurrentData = new_page_mem + (end_ptr + 1 - past_boundary_ptr);
|
||||
m_Buffer.m_pCurrentPageStart = new_page_mem;
|
||||
ovrld_log(LogCategory::PAGING, "File {} advanced to next page (wrapped {} bytes)",
|
||||
m_FileDef->name.data, (end_ptr + 1 - past_boundary_ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_Buffer.m_pCurrentData;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Allocate pages and set up the CBuffer for the given ISO Msg and type.
|
||||
*/
|
||||
int CBaseFile::InitBuffer(CBuffer::BufferType type, jak3::ISO_Hdr* msg) {
|
||||
ASSERT(msg);
|
||||
m_Buffer.m_pCurrentData = nullptr;
|
||||
m_Buffer.m_pPageList = nullptr;
|
||||
m_Buffer.m_pIsoCmd = msg;
|
||||
m_Buffer.m_pCurrentPageStart = nullptr;
|
||||
m_Buffer.m_nDataLength = 0;
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
|
||||
m_Buffer.m_nMinNumPages = 1;
|
||||
m_Buffer.m_nMaxNumPages = kDefaultBufferPageCount;
|
||||
m_nNumPages = 4;
|
||||
|
||||
// adjust size based on the request buffer type
|
||||
switch (type) {
|
||||
case CBuffer::BufferType::REQUEST_NORMAL: { // 3
|
||||
m_Buffer.m_pCurrentData = nullptr;
|
||||
m_Buffer.m_nMinNumPages = 1;
|
||||
m_Buffer.m_nMaxNumPages = 4;
|
||||
m_nNumPages = 4;
|
||||
m_Buffer.m_nDataLength = 0;
|
||||
m_Buffer.m_pCurrentPageStart = nullptr;
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::NORMAL; // 1
|
||||
|
||||
// and the iso msg request.
|
||||
switch (msg->msg_type) {
|
||||
case ISO_Hdr::MsgType::LOAD_EE:
|
||||
case ISO_Hdr::MsgType::LOAD_IOP:
|
||||
case ISO_Hdr::MsgType::LOAD_EE_CHUNK:
|
||||
m_Buffer.m_nMinNumPages = 1;
|
||||
m_nNumPages = 4;
|
||||
m_Buffer.m_nMaxNumPages = 4;
|
||||
break;
|
||||
case ISO_Hdr::MsgType::LOAD_SOUNDBANK:
|
||||
m_Buffer.m_nMinNumPages = 1;
|
||||
m_Buffer.m_nMaxNumPages = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
} break;
|
||||
case CBuffer::BufferType::REQUEST_VAG: {
|
||||
m_Buffer.m_pCurrentData = nullptr;
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::VAG;
|
||||
m_Buffer.m_nMinNumPages = 1;
|
||||
m_nNumPages = 0x10;
|
||||
m_Buffer.m_nDataLength = 0;
|
||||
m_Buffer.m_pCurrentPageStart = nullptr;
|
||||
m_Buffer.m_nMaxNumPages = 0x10;
|
||||
} break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED(); // bad buffer type
|
||||
}
|
||||
|
||||
ovrld_log(LogCategory::PAGING, "File {} initializing buffer ({} pages {} min {} max)",
|
||||
m_FileDef->name.data, m_nNumPages, m_Buffer.m_nMinNumPages, m_Buffer.m_nMaxNumPages);
|
||||
|
||||
// Actual allocation of page data
|
||||
CPageList* page_list = AllocPages();
|
||||
if (page_list) {
|
||||
// set up the current pointers of the buffer.
|
||||
m_Buffer.m_pCurrentPageStart = page_list->m_pFirstPage->m_pPageMemStart;
|
||||
m_Buffer.m_pCurrentData = m_Buffer.m_pCurrentPageStart;
|
||||
return 1;
|
||||
} else {
|
||||
ovrld_log(LogCategory::WARN, "File {} failed to allocate a page list.", m_FileDef->name.data);
|
||||
// if it failed, terminate the buffer
|
||||
TerminateBuffer();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Free page memory, clear event flags for cpages.
|
||||
*/
|
||||
void CBaseFile::TerminateBuffer() {
|
||||
ovrld_log(LogCategory::PAGING, "File {} terminating buffer.", m_FileDef->name.data);
|
||||
|
||||
auto buffer_type = m_Buffer.m_eBufferType;
|
||||
if (buffer_type != CBuffer::BufferType::EBT_FREE) {
|
||||
// clean up our pages
|
||||
auto* page_list = m_Buffer.m_pPageList;
|
||||
if (page_list) {
|
||||
// stop try to load
|
||||
page_list->CancelActivePages();
|
||||
}
|
||||
|
||||
switch (buffer_type) {
|
||||
case CBuffer::BufferType::EBT_FREE:
|
||||
case CBuffer::BufferType::NORMAL:
|
||||
case CBuffer::BufferType::VAG:
|
||||
case CBuffer::BufferType::REQUEST_NORMAL:
|
||||
case CBuffer::BufferType::REQUEST_VAG:
|
||||
// return memory
|
||||
FreePages();
|
||||
|
||||
// reset our buffer
|
||||
m_Buffer.m_nDataLength = 0;
|
||||
m_Buffer.m_pCurrentPageStart = 0;
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::EBT_FREE;
|
||||
|
||||
// reset our command
|
||||
if (buffer_type != CBuffer::BufferType::EBT_FREE) {
|
||||
ASSERT(m_Buffer.m_pIsoCmd);
|
||||
m_Buffer.m_pIsoCmd = nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED(); // Invalid buffer type
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED(); // Buffer already terminated, shouldn't happen again.
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Allocate CPage and CPageList as needed to reach the target number of pages.
|
||||
*/
|
||||
CPageList* CBaseFile::AllocPages() {
|
||||
// should have picked a buffer type
|
||||
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
|
||||
|
||||
if (m_Buffer.m_eBufferType == CBuffer::BufferType::EBT_FREE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We might have a PageList or not
|
||||
auto* old_plist = m_Buffer.m_pPageList;
|
||||
int old_unstepped_pages = 0;
|
||||
bool have_plist = old_plist != nullptr;
|
||||
CPageList* ret_plist = nullptr;
|
||||
|
||||
// if we do have a pagelist, we'll reuse it and any unstepped pages.
|
||||
if (have_plist) {
|
||||
old_unstepped_pages = old_plist->m_nNumUnsteppedPages;
|
||||
ret_plist = old_plist;
|
||||
}
|
||||
|
||||
int page_alloc_count = 0;
|
||||
|
||||
// to increase unstepped pages to the target m_nNumPages, we need to allocate this many more.
|
||||
page_alloc_count = m_nNumPages - old_unstepped_pages;
|
||||
// lg::warn("page counts in AllocPages: {} {}", m_nNumPages, old_unstepped_pages);
|
||||
if (old_plist) {
|
||||
// lg::warn(" {} {}", old_plist->m_nNumPages, old_plist->m_nNumActivePages);
|
||||
}
|
||||
|
||||
if (old_unstepped_pages < (int)m_nNumPages) {
|
||||
// alloc count will be positive.
|
||||
|
||||
// reduce allocation count to at most the number of free pages
|
||||
if (get_page_manager()->m_CCache.m_nNumFreePages < page_alloc_count) {
|
||||
page_alloc_count = get_page_manager()->m_CCache.m_nNumFreePages;
|
||||
}
|
||||
|
||||
} else {
|
||||
// alloc count would be negative, don't allocate
|
||||
page_alloc_count = 0;
|
||||
}
|
||||
|
||||
switch (m_Buffer.m_eBufferType) {
|
||||
case CBuffer::BufferType::NORMAL:
|
||||
case CBuffer::BufferType::REQUEST_NORMAL: {
|
||||
// don't do anything special
|
||||
} break;
|
||||
case CBuffer::BufferType::VAG:
|
||||
case CBuffer::BufferType::REQUEST_VAG: {
|
||||
// for VAG commands, we don't bother buffering more than the DMA transfer size
|
||||
int dma_xfer_size = ((ISO_VAGCommand*)m_Buffer.m_pIsoCmd)->xfer_size;
|
||||
if (dma_xfer_size) {
|
||||
ASSERT(dma_xfer_size > 0);
|
||||
int limit = (dma_xfer_size + 0x7fff) >> 0xf;
|
||||
if (page_alloc_count > limit) {
|
||||
ovrld_log(LogCategory::WARN, "File {} wants {} pages, but VAG DMA sizes limit us to ",
|
||||
m_FileDef->name.data, page_alloc_count, limit);
|
||||
lg::info("page count dma limit {} -> {}\n", page_alloc_count, limit);
|
||||
page_alloc_count = limit;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED(); // bad buffer type.
|
||||
}
|
||||
|
||||
if (page_alloc_count == 0) {
|
||||
return ret_plist;
|
||||
}
|
||||
|
||||
ovrld_log(LogCategory::PAGING, "File {} wants {} more pages in AllocPages.", m_FileDef->name.data,
|
||||
page_alloc_count);
|
||||
if (have_plist) {
|
||||
ret_plist = get_page_manager()->GrowPageList(m_Buffer.m_pPageList, page_alloc_count);
|
||||
} else {
|
||||
ret_plist = get_page_manager()->AllocPageList(page_alloc_count, 0);
|
||||
}
|
||||
|
||||
if (ret_plist) {
|
||||
m_Buffer.m_pPageList = ret_plist;
|
||||
if (!have_plist) {
|
||||
m_Buffer.m_pCurrentData = ret_plist->m_pFirstPage->m_pPageMemStart;
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED(); // might be ok.
|
||||
}
|
||||
return ret_plist;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Free all pages and the page list.
|
||||
*/
|
||||
void CBaseFile::FreePages() {
|
||||
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
|
||||
if (m_Buffer.m_pPageList) {
|
||||
get_page_manager()->FreePageList(m_Buffer.m_pPageList);
|
||||
}
|
||||
m_Buffer.m_pPageList = nullptr;
|
||||
}
|
||||
|
||||
} // namespace jak3
|
76
game/overlord/jak3/basefile.h
Normal file
76
game/overlord/jak3/basefile.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
#include "game/overlord/jak3/pagemanager.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_basefile();
|
||||
|
||||
struct CPageList;
|
||||
struct ISOFileDef;
|
||||
struct ISO_Hdr;
|
||||
|
||||
constexpr int kDefaultBufferPageCount = 4;
|
||||
|
||||
/*!
|
||||
* Base class for a file that the ISO system is processing.
|
||||
* This represents an "open" file, and contains references to the buffer holding this file's data
|
||||
*/
|
||||
struct CBaseFile {
|
||||
CBaseFile();
|
||||
CBaseFile(const ISOFileDef* file, int semaphore);
|
||||
virtual ~CBaseFile() = default;
|
||||
|
||||
uint8_t* CheckPageBoundary();
|
||||
int InitBuffer(CBuffer::BufferType type, ISO_Hdr* msg);
|
||||
CPageList* AllocPages();
|
||||
void TerminateBuffer();
|
||||
void FreePages();
|
||||
|
||||
// buffer that stores some contents of this file
|
||||
CBuffer m_Buffer;
|
||||
|
||||
// the number of pages that were allocated for reading this file.
|
||||
u32 m_nNumPages;
|
||||
|
||||
// Metadata about the file
|
||||
const ISOFileDef* m_FileDef;
|
||||
|
||||
// The compression format used on the file
|
||||
enum class Kind {
|
||||
UNKNOWN = 0,
|
||||
NORMAL = 1,
|
||||
LZO_COMPRESSED = 2,
|
||||
} m_FileKind = Kind::UNKNOWN;
|
||||
|
||||
EIsoStatus m_Status = EIsoStatus::NONE_0;
|
||||
|
||||
// The expected read rate for streaming, used to prioritize CD reads. Can be 0 if unknown/not
|
||||
// applicable.
|
||||
int m_ReadRate = 0;
|
||||
|
||||
// Number of sectors that we should read in total, decided based on the file size and request from
|
||||
// user when they opened this file.
|
||||
int m_LengthPages = 0; // really, in pages...
|
||||
|
||||
// The current offset. (todo: is this for data we read, processed?)
|
||||
int m_PageOffset = 0;
|
||||
|
||||
// Semaphore that we should wait on before handing new data to the process callback.
|
||||
// Set to -1 if there is no semaphore.
|
||||
// (this is a bit of hack, only used for VAG streaming).
|
||||
int m_ProcessDataSemaphore = 0;
|
||||
|
||||
// virtual methods
|
||||
virtual EIsoStatus BeginRead() = 0;
|
||||
virtual EIsoStatus SyncRead() = 0;
|
||||
virtual void Close() = 0;
|
||||
virtual int RecoverPages(int num_pages) = 0;
|
||||
virtual int GetSector() = 0;
|
||||
// ??
|
||||
// ??
|
||||
// ??
|
||||
};
|
||||
} // namespace jak3
|
26
game/overlord/jak3/basefilesystem.cpp
Normal file
26
game/overlord/jak3/basefilesystem.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "basefilesystem.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
using namespace iop;
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_basefilesystem() {}
|
||||
|
||||
CBaseFileSystem::CBaseFileSystem() {
|
||||
for (auto& sema : m_Sema) {
|
||||
sema = -1;
|
||||
|
||||
SemaParam param;
|
||||
param.max_count = 1;
|
||||
param.attr = 0;
|
||||
param.init_count = 1;
|
||||
param.option = 0;
|
||||
sema = CreateSema(¶m);
|
||||
if (sema < 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace jak3
|
33
game/overlord/jak3/basefilesystem.h
Normal file
33
game/overlord/jak3/basefilesystem.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_basefilesystem();
|
||||
|
||||
struct ISOFileDef;
|
||||
struct ISOName;
|
||||
struct CBaseFile;
|
||||
struct VagDirEntry;
|
||||
|
||||
constexpr int kMaxOpenFiles = 16;
|
||||
|
||||
/*!
|
||||
* Base class for "FileSystem", which supports finding and opening files.
|
||||
* The only implementation we have is CISOCDFileSystem
|
||||
*/
|
||||
struct CBaseFileSystem {
|
||||
CBaseFileSystem();
|
||||
virtual ~CBaseFileSystem() = default;
|
||||
|
||||
// semaphores for processing open files
|
||||
int m_Sema[kMaxOpenFiles];
|
||||
|
||||
virtual int Init() = 0;
|
||||
// polldrive
|
||||
virtual ISOFileDef* Find(const char* name) = 0;
|
||||
virtual ISOFileDef* FindIN(const ISOName* name) = 0;
|
||||
virtual int GetLength(const ISOFileDef* file) = 0;
|
||||
virtual CBaseFile* Open(const ISOFileDef* file_def, int sector_offset, int file_kind) = 0;
|
||||
virtual CBaseFile* OpenWAD(const ISOFileDef* file_def, int page_offset) = 0;
|
||||
virtual VagDirEntry* FindVAGFile(const char* name) = 0;
|
||||
};
|
||||
} // namespace jak3
|
526
game/overlord/jak3/dma.cpp
Normal file
526
game/overlord/jak3/dma.cpp
Normal file
@ -0,0 +1,526 @@
|
||||
#include "dma.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/basefile.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "game/sound/sdshim.h"
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
#define VOICE_BIT(voice) (1 << ((voice) >> 1))
|
||||
|
||||
namespace jak3 {
|
||||
using namespace iop;
|
||||
namespace {
|
||||
|
||||
// most recent call to voice_trans_wrapper's arguments
|
||||
u32 g_voiceTransMode = 0;
|
||||
u32 g_voiceTransSize = 0;
|
||||
s16 g_voiceTransChannel = 0;
|
||||
const void* g_voiceTransAddr = nullptr;
|
||||
u32 g_voiceTransSpuAddr = 0;
|
||||
|
||||
// if we've started a transfer recently
|
||||
bool g_voiceTransRunning = false;
|
||||
// when that transfer was started
|
||||
u32 g_voiceTransTime = 0;
|
||||
|
||||
// despite the name, this is really an indicator that the SPU streaming system is waiting
|
||||
// for a SPU interrupt on completion.
|
||||
bool g_bSpuDmaBusy = false;
|
||||
int g_nSpuDmaChannel = 0;
|
||||
|
||||
ISO_VAGCommand* g_pDmaVagCmd = nullptr;
|
||||
ISO_VAGCommand* g_pDmaStereoVagCmd = nullptr;
|
||||
|
||||
int g_nSpuDmaChunks = 0;
|
||||
|
||||
std::array<DmaQueueEntry, 16> g_aSpuDmaQueue;
|
||||
int g_nSpuDmaQueueHead = 0;
|
||||
int g_nSpuDmaQueueTail = 0;
|
||||
int g_nSpuDmaQueueCount = 0;
|
||||
|
||||
struct DmaInterruptHandlerHack {
|
||||
s32 chan = 0;
|
||||
sceSdTransIntrHandler cb = nullptr;
|
||||
void* data;
|
||||
int countdown = 0;
|
||||
} g_DmaInterruptHack;
|
||||
|
||||
} // namespace
|
||||
|
||||
void jak3_overlord_init_globals_dma() {
|
||||
g_voiceTransMode = 0;
|
||||
g_voiceTransSize = 0;
|
||||
g_voiceTransChannel = 0;
|
||||
g_voiceTransAddr = nullptr;
|
||||
g_voiceTransSpuAddr = 0;
|
||||
g_voiceTransRunning = false;
|
||||
g_voiceTransTime = 0;
|
||||
g_bSpuDmaBusy = false;
|
||||
g_nSpuDmaChannel = 0;
|
||||
g_pDmaVagCmd = nullptr;
|
||||
g_pDmaStereoVagCmd = nullptr;
|
||||
g_nSpuDmaChunks = 0;
|
||||
g_aSpuDmaQueue = {};
|
||||
g_nSpuDmaQueueHead = 0;
|
||||
g_nSpuDmaQueueCount = 0;
|
||||
g_nSpuDmaQueueTail = 0;
|
||||
g_DmaInterruptHack = {};
|
||||
}
|
||||
|
||||
// The DMA callback hack below is used to defer dma completion "interrupts" until the next run
|
||||
// of the ISO Thread. This avoids re-entry type problems where the original design would set off
|
||||
// a dma transfer in the completion handler of the previous transfer, and expect a few instructions
|
||||
// to run after.
|
||||
|
||||
void uninstall_dma_intr() {
|
||||
g_DmaInterruptHack = {};
|
||||
}
|
||||
|
||||
void set_dma_intr_handler_hack(s32 chan, sceSdTransIntrHandler cb, void* data) {
|
||||
ASSERT(!g_DmaInterruptHack.cb);
|
||||
g_DmaInterruptHack.chan = chan;
|
||||
g_DmaInterruptHack.cb = cb;
|
||||
g_DmaInterruptHack.data = data;
|
||||
g_DmaInterruptHack.countdown = 10;
|
||||
}
|
||||
|
||||
int SPUDmaIntr(int channel, void* userdata);
|
||||
|
||||
void dma_intr_hack() {
|
||||
if (g_DmaInterruptHack.countdown) {
|
||||
g_DmaInterruptHack.countdown--;
|
||||
if (g_DmaInterruptHack.countdown == 0) {
|
||||
int chan = g_DmaInterruptHack.chan;
|
||||
void* data = g_DmaInterruptHack.data;
|
||||
g_DmaInterruptHack = {};
|
||||
SPUDmaIntr(chan, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This function is used to set up a DMA transfer to SPU DMA.
|
||||
*
|
||||
* This wrapper was added very close to the end of Jak 3's development.
|
||||
*
|
||||
* I believe it basically checks for dma transfers that are somehow "dropped", and retries them.
|
||||
* Since I don't think our IOP framework will ever do this, we have an assert if the dropped logic
|
||||
* ever goes off.
|
||||
*/
|
||||
int voice_trans_wrapper(s16 chan, u32 mode, const void* iop_addr, u32 spu_addr, u32 size) {
|
||||
// remember the transfer settings. If there's a transfer in progress, so we can't start here,
|
||||
// we'll use these to start the transfer later.
|
||||
g_voiceTransMode = mode;
|
||||
g_voiceTransSize = size;
|
||||
g_voiceTransChannel = chan;
|
||||
g_voiceTransAddr = iop_addr;
|
||||
g_voiceTransSpuAddr = spu_addr;
|
||||
|
||||
if (g_voiceTransRunning) {
|
||||
// I claim this should never happen, and this is their workaround for a bug.
|
||||
ASSERT_NOT_REACHED();
|
||||
return -0xd2; // busy
|
||||
} else {
|
||||
g_voiceTransRunning = true;
|
||||
g_voiceTransTime = GetSystemTimeLow();
|
||||
return sceSdVoiceTrans(chan, mode, iop_addr, spu_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
u32 read_rate_calc(u32 pitch) {
|
||||
u64 pitch1 = (pitch >> 3);
|
||||
u64 mult_result = pitch1 * 0x2492'4925ull;
|
||||
return mult_result >> 32;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The worst function of all time - the SPU DMA completion interrupt.
|
||||
*/
|
||||
int SPUDmaIntr(int channel, void* userdata) {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr enter! {} 0x{:x}", channel, (u64)userdata);
|
||||
|
||||
if (!g_bSpuDmaBusy) {
|
||||
// we got an interrupt, but weren't expecting it, or no longer have the need for the data.
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr exit - not busy");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (channel != g_nSpuDmaChannel) {
|
||||
// interrupt was for the wrong channel, somehow.
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr exit - not our channel ??");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// since we're in the completion handler, we know that there is no voice trans (SPU DMA) running.
|
||||
g_voiceTransRunning = false;
|
||||
|
||||
// This next block will handle updating the playback command that triggered this dma:
|
||||
if (g_pDmaVagCmd) {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDma for cmd {}", g_pDmaVagCmd->name);
|
||||
if (!g_pDmaStereoVagCmd) {
|
||||
// non-stereo audio
|
||||
|
||||
// set a flag to indicate even/odd number of chunks have been dma'd
|
||||
if ((g_nSpuDmaChunks & 1) == 0) {
|
||||
g_pDmaVagCmd->flags.dma_complete_even_chunk_count = 1;
|
||||
} else {
|
||||
g_pDmaVagCmd->flags.dma_complete_odd_chunk_count = 1;
|
||||
}
|
||||
} else {
|
||||
// stereo audio. This requires two uploads, one for left/right audio. If we've finished the
|
||||
// first, start the second one here:
|
||||
if (g_pDmaStereoVagCmd->xfer_size) {
|
||||
// parameters for second upload
|
||||
int chan = g_pDmaVagCmd->dma_chan;
|
||||
const u8* iop_addr = g_pDmaStereoVagCmd->dma_iop_mem_ptr;
|
||||
int size = g_pDmaStereoVagCmd->xfer_size;
|
||||
|
||||
// SPU addr - toggle the buffer based on stereo side:
|
||||
// TODO: better explanation of why this picks the correct buffer.
|
||||
int spu_addr;
|
||||
if ((g_nSpuDmaChunks & 1) == 0) {
|
||||
spu_addr = g_pDmaStereoVagCmd->stream_sram;
|
||||
} else {
|
||||
spu_addr = g_pDmaStereoVagCmd->stream_sram + 0x2000;
|
||||
}
|
||||
|
||||
// these lines reordered to possibly support immediate dma completion callback??
|
||||
|
||||
// clear flag so we know not to transfer the next part
|
||||
g_pDmaStereoVagCmd->xfer_size = 0;
|
||||
g_pDmaStereoVagCmd->dma_iop_mem_ptr = nullptr;
|
||||
|
||||
// start next transfer
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr starting stereo sibling transfer");
|
||||
set_dma_intr_handler_hack(g_nSpuDmaChannel, SPUDmaIntr, userdata);
|
||||
voice_trans_wrapper(chan, 0, iop_addr, spu_addr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// second stereo upload completed - update double-buffering flags
|
||||
if ((g_nSpuDmaChunks & 1) == 0) {
|
||||
g_pDmaVagCmd->flags.dma_complete_even_chunk_count = 1;
|
||||
g_pDmaStereoVagCmd->flags.dma_complete_even_chunk_count = 1;
|
||||
} else {
|
||||
g_pDmaVagCmd->flags.dma_complete_odd_chunk_count = 1;
|
||||
g_pDmaStereoVagCmd->flags.dma_complete_odd_chunk_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// if this is the first chunk, we'll start the actual audio here:
|
||||
// lg::warn("----------> interrupt with chunks {}\n", g_nSpuDmaChunks);
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks count {}", g_nSpuDmaChunks);
|
||||
|
||||
if (g_nSpuDmaChunks == 0) {
|
||||
// compute pitch/playback rate
|
||||
int pitch = CalculateVAGPitch(g_pDmaVagCmd->pitch1, g_pDmaVagCmd->pitch_cmd);
|
||||
ASSERT(pitch == (pitch & 0xffff));
|
||||
|
||||
// inform the ISO system how fast we're reading
|
||||
if (g_pDmaVagCmd->m_pBaseFile) {
|
||||
// unlike actual playback, this is done with the pitch1 value from the file itself - so if
|
||||
// we speed up/slow down stuff in debug, it won't change streaming modes
|
||||
const int pitch_from_file =
|
||||
CalculateVAGPitch(g_pDmaVagCmd->pitch1_file, g_pDmaVagCmd->pitch_cmd);
|
||||
int rate = g_pDmaStereoVagCmd ? pitch_from_file * 0x2ee : pitch_from_file * 0x177;
|
||||
g_pDmaVagCmd->m_pBaseFile->m_ReadRate = read_rate_calc(rate);
|
||||
}
|
||||
|
||||
// start!
|
||||
u32 voice_mask = 0;
|
||||
if (!g_pDmaStereoVagCmd) {
|
||||
// forget any previous spu address
|
||||
g_pDmaVagCmd->current_spu_address = 0;
|
||||
|
||||
static_assert(SD_VA_SSA == 0x2040);
|
||||
static_assert(SD_S_KOFF == 0x1600);
|
||||
static_assert(SD_S_KON == 0x1500);
|
||||
|
||||
static_assert(SD_VP_ADSR1 == 0x300);
|
||||
static_assert(SD_VP_ADSR2 == 0x400);
|
||||
static_assert(SD_VP_PITCH == 0x200);
|
||||
|
||||
// before touching SPU2 hardware, wait for voice safety:
|
||||
BlockUntilVoiceSafe(g_pDmaVagCmd->voice, 0x900);
|
||||
|
||||
// set address and ADSR settings
|
||||
sceSdSetAddr(g_pDmaVagCmd->voice | SD_VA_SSA, g_pDmaVagCmd->stream_sram + 0x30);
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR1, 0xff);
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR2, 0x1fc0);
|
||||
if (g_pDmaVagCmd->flags.paused) {
|
||||
pitch = 0;
|
||||
}
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, pitch);
|
||||
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice);
|
||||
} else {
|
||||
// forget any previous spu address
|
||||
g_pDmaVagCmd->current_spu_address = 0;
|
||||
g_pDmaStereoVagCmd->current_spu_address = 0;
|
||||
|
||||
// wait for voices to be safe to adjust
|
||||
BlockUntilVoiceSafe(g_pDmaVagCmd->voice, 0x900);
|
||||
BlockUntilVoiceSafe(g_pDmaStereoVagCmd->voice, 0x900);
|
||||
|
||||
// set voice params
|
||||
sceSdSetAddr(g_pDmaVagCmd->voice | SD_VA_SSA, g_pDmaVagCmd->stream_sram + 0x30);
|
||||
sceSdSetAddr(g_pDmaStereoVagCmd->voice | SD_VA_SSA, g_pDmaStereoVagCmd->stream_sram + 0x30);
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR1, 0xff);
|
||||
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_ADSR1, 0xff);
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_ADSR2, 0x1fc0);
|
||||
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_ADSR2, 0x1fc0);
|
||||
if (g_pDmaVagCmd->flags.paused) {
|
||||
pitch = 0;
|
||||
}
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, pitch);
|
||||
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_PITCH, pitch);
|
||||
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice) | VOICE_BIT(g_pDmaStereoVagCmd->voice);
|
||||
}
|
||||
|
||||
// do key-on or key-off
|
||||
if (g_pDmaVagCmd->flags.paused) {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 0, key off");
|
||||
BlockUntilAllVoicesSafe();
|
||||
sceSdSetSwitch(SD_S_KOFF | (g_pDmaVagCmd->voice & 1), voice_mask);
|
||||
} else {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 0, key on");
|
||||
BlockUntilAllVoicesSafe();
|
||||
sceSdSetSwitch(SD_S_KON | (g_pDmaVagCmd->voice & 1), voice_mask);
|
||||
}
|
||||
|
||||
// remember the time of the key-on/off. This is used to avoid sending voice commands
|
||||
// quickly, which somehow confuses the sound hardware.
|
||||
auto sys_time = GetSystemTimeLow();
|
||||
MarkVoiceKeyedOnOff(g_pDmaVagCmd->voice, sys_time);
|
||||
if (g_pDmaStereoVagCmd) {
|
||||
MarkVoiceKeyedOnOff(g_pDmaStereoVagCmd->voice, sys_time);
|
||||
}
|
||||
} else if (g_nSpuDmaChunks == 1) {
|
||||
g_pDmaVagCmd->flags.saw_chunks1 = 1;
|
||||
if (g_pDmaStereoVagCmd) {
|
||||
g_pDmaStereoVagCmd->flags.saw_chunks1 = 1;
|
||||
}
|
||||
|
||||
if (g_pDmaVagCmd->flags.paused) {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 1, pausing");
|
||||
u32 voice_mask = 0;
|
||||
if (!g_pDmaStereoVagCmd) {
|
||||
// pause by setting pitches to 0
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, 0);
|
||||
BlockUntilVoiceSafe(VOICE_BIT(g_pDmaVagCmd->voice), 0x900);
|
||||
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice);
|
||||
} else {
|
||||
sceSdSetParam(g_pDmaStereoVagCmd->voice | SD_VP_PITCH, 0);
|
||||
sceSdSetParam(g_pDmaVagCmd->voice | SD_VP_PITCH, 0);
|
||||
BlockUntilVoiceSafe(VOICE_BIT(g_pDmaVagCmd->voice), 0x900);
|
||||
BlockUntilVoiceSafe(VOICE_BIT(g_pDmaStereoVagCmd->voice), 0x900);
|
||||
voice_mask = VOICE_BIT(g_pDmaVagCmd->voice) | VOICE_BIT(g_pDmaStereoVagCmd->voice);
|
||||
}
|
||||
|
||||
// switch off
|
||||
BlockUntilAllVoicesSafe();
|
||||
sceSdSetSwitch(SD_S_KOFF | (g_pDmaVagCmd->voice & 1), voice_mask);
|
||||
auto sys_time = GetSystemTimeLow();
|
||||
MarkVoiceKeyedOnOff(g_pDmaVagCmd->voice, sys_time);
|
||||
if (g_pDmaStereoVagCmd) {
|
||||
MarkVoiceKeyedOnOff(g_pDmaStereoVagCmd->voice, sys_time);
|
||||
}
|
||||
} else {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr chunks 1, unpausing by call to UnPauseVAG");
|
||||
g_pDmaVagCmd->flags.paused = 1;
|
||||
UnPauseVAG(g_pDmaVagCmd);
|
||||
}
|
||||
}
|
||||
|
||||
// now that we've processed the command from this interrupt, mark it as safe to modify
|
||||
g_pDmaVagCmd->safe_to_modify_dma = 1;
|
||||
if (g_pDmaStereoVagCmd) {
|
||||
g_pDmaStereoVagCmd->safe_to_modify_dma = 1;
|
||||
}
|
||||
// and forget it!
|
||||
g_pDmaVagCmd = nullptr;
|
||||
g_pDmaStereoVagCmd = nullptr;
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr dma handling of VAG cmd is complete");
|
||||
}
|
||||
|
||||
// release ref on this page. (interestingly, not a dma ref...)
|
||||
if (userdata) {
|
||||
CPage* page = (CPage*)userdata;
|
||||
int ret = page->ReleaseRef();
|
||||
ASSERT(ret >= 0);
|
||||
}
|
||||
|
||||
// now - see if we have another queued dma transfer
|
||||
ASSERT(g_nSpuDmaQueueCount >= 0);
|
||||
if (g_nSpuDmaQueueCount == 0) {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr dma queue is empty, disabling interrupt");
|
||||
// we're done!
|
||||
// set_dma_intr_handler_hack(channel, nullptr, nullptr);
|
||||
uninstall_dma_intr();
|
||||
// if (-1 < channel) {
|
||||
// snd_FreeSPUDMA(channel);
|
||||
// }
|
||||
g_bSpuDmaBusy = false;
|
||||
} else {
|
||||
ovrld_log(LogCategory::SPU_DMA_STR,
|
||||
"SPUDmaIntr dma queue is not empty, preparing to run {} ({} pending)",
|
||||
g_nSpuDmaQueueHead, g_nSpuDmaQueueCount);
|
||||
// nope, more dma to run
|
||||
auto* next_xfer = &g_aSpuDmaQueue[g_nSpuDmaQueueHead];
|
||||
|
||||
// set up the next interrupt handler
|
||||
set_dma_intr_handler_hack(channel, SPUDmaIntr, next_xfer->user_data);
|
||||
|
||||
// args for the dma transfer
|
||||
int next_chan = channel;
|
||||
int next_mode = 0;
|
||||
const void* next_iop = next_xfer->iop_mem;
|
||||
u32 next_spu = next_xfer->spu_addr;
|
||||
u32 next_length = next_xfer->length;
|
||||
|
||||
// load up the commands to handle
|
||||
g_pDmaVagCmd = next_xfer->command;
|
||||
g_pDmaStereoVagCmd = nullptr;
|
||||
if (g_pDmaVagCmd) {
|
||||
g_pDmaStereoVagCmd = g_pDmaVagCmd->stereo_sibling;
|
||||
}
|
||||
g_nSpuDmaChunks = next_xfer->num_isobuffered_chunks;
|
||||
|
||||
// advance the queue!
|
||||
g_nSpuDmaQueueCount = g_nSpuDmaQueueCount + -1;
|
||||
g_nSpuDmaQueueHead = g_nSpuDmaQueueHead + 1;
|
||||
if (0xf < g_nSpuDmaQueueHead) {
|
||||
g_nSpuDmaQueueHead = 0;
|
||||
}
|
||||
|
||||
// start the next one!
|
||||
// set_dma_intr_handler_hack(g_nSpuDmaChannel, SPUDmaIntr, userdata);
|
||||
voice_trans_wrapper(next_chan, next_mode, next_iop, next_spu, next_length);
|
||||
}
|
||||
|
||||
ovrld_log(LogCategory::SPU_DMA_STR, "SPUDmaIntr exit - end of function");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Start DMA to EE.
|
||||
*/
|
||||
void DMA_SendToEE(void* ee_dest,
|
||||
const void* iop_src,
|
||||
u32 length,
|
||||
void callback(void*),
|
||||
void* callback_arg) {
|
||||
ASSERT(iop_src);
|
||||
ASSERT(ee_dest);
|
||||
ASSERT(((uintptr_t)iop_src & 3) == 0);
|
||||
ASSERT(((uintptr_t)ee_dest & 0xf) == 0);
|
||||
ASSERT(length < 0xffff0);
|
||||
sceSifDmaData cmd; // DMA settings
|
||||
|
||||
// setup command
|
||||
cmd.mode = 0;
|
||||
cmd.data = iop_src;
|
||||
cmd.addr = ee_dest;
|
||||
cmd.size = length;
|
||||
|
||||
// instant DMA
|
||||
// ovrld_log(LogCategory::EE_DMA, "DMA_SendToEE: 0x{:x}, size {}", (u64)ee_dest, length);
|
||||
sceSifSetDma(&cmd, 1);
|
||||
|
||||
// for now, we'll do the callback here, but I bet it will cause problems
|
||||
if (callback) {
|
||||
callback(callback_arg);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Start DMA transfer to SPU. Despite the name, this does not actually "sync" - the transfer will
|
||||
* be ongoing. If there is an ongoing transfer when this is called, the transfer will be queued.
|
||||
*/
|
||||
int DMA_SendToSPUAndSync(const u8* iop_mem,
|
||||
int length,
|
||||
int spu_addr,
|
||||
ISO_VAGCommand* cmd,
|
||||
void* user_data) {
|
||||
// CpuSuspendIntr(local_28);
|
||||
int ret = 1;
|
||||
bool defer = false;
|
||||
|
||||
ovrld_log(LogCategory::SPU_DMA_STR,
|
||||
"DMA to SPU requested for {}, {} bytes to 0x{:x}, currently busy? {}",
|
||||
cmd ? cmd->name : "NO-CMD", length, spu_addr, g_bSpuDmaBusy);
|
||||
if (g_bSpuDmaBusy == 0) {
|
||||
// not busy, we can actually start dma now.
|
||||
g_nSpuDmaChannel = snd_GetFreeSPUDMA();
|
||||
if (g_nSpuDmaChannel == -1) {
|
||||
return 0;
|
||||
}
|
||||
// set globals for DMA processing
|
||||
if (cmd) {
|
||||
g_nSpuDmaChunks = cmd->num_isobuffered_chunks;
|
||||
g_pDmaStereoVagCmd = cmd->stereo_sibling;
|
||||
g_pDmaVagCmd = cmd;
|
||||
}
|
||||
|
||||
} else {
|
||||
// busy, need to queue the dma
|
||||
ASSERT(g_nSpuDmaQueueCount <= (int)g_aSpuDmaQueue.size());
|
||||
|
||||
// set values:
|
||||
g_aSpuDmaQueue[g_nSpuDmaQueueTail].length = length;
|
||||
g_aSpuDmaQueue[g_nSpuDmaQueueTail].spu_addr = spu_addr;
|
||||
g_aSpuDmaQueue[g_nSpuDmaQueueTail].user_data = user_data;
|
||||
g_aSpuDmaQueue[g_nSpuDmaQueueTail].num_isobuffered_chunks =
|
||||
cmd ? cmd->num_isobuffered_chunks : 0;
|
||||
g_aSpuDmaQueue[g_nSpuDmaQueueTail].command = cmd;
|
||||
g_aSpuDmaQueue[g_nSpuDmaQueueTail].iop_mem = iop_mem;
|
||||
g_nSpuDmaQueueCount = g_nSpuDmaQueueCount + 1;
|
||||
g_nSpuDmaQueueTail = g_nSpuDmaQueueTail + 1;
|
||||
if (0xf < g_nSpuDmaQueueTail) {
|
||||
g_nSpuDmaQueueTail = 0;
|
||||
}
|
||||
defer = true;
|
||||
}
|
||||
|
||||
// set up the stereo command
|
||||
if (cmd) {
|
||||
cmd->safe_to_modify_dma = 0;
|
||||
auto* stereo = cmd->stereo_sibling;
|
||||
if (stereo) {
|
||||
stereo->num_isobuffered_chunks = cmd->num_isobuffered_chunks;
|
||||
stereo->dma_iop_mem_ptr = iop_mem + length;
|
||||
cmd->dma_chan = g_nSpuDmaChannel;
|
||||
stereo->xfer_size = length;
|
||||
}
|
||||
}
|
||||
|
||||
// kick off dma, if we decided not to queue.
|
||||
if (!defer) {
|
||||
g_bSpuDmaBusy = true;
|
||||
set_dma_intr_handler_hack(g_nSpuDmaChannel, SPUDmaIntr, user_data);
|
||||
voice_trans_wrapper(g_nSpuDmaChannel, 0, iop_mem, spu_addr, length);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Run a dma transfer that was delayed or dropped.
|
||||
*/
|
||||
void RunDeferredVoiceTrans() {
|
||||
// only if there's a currently happening transfer.
|
||||
if (g_voiceTransRunning) {
|
||||
if (GetSystemTimeLow() - g_voiceTransTime > 0x384000) {
|
||||
ovrld_log(LogCategory::WARN, "DeferredVoiceTrans has detected hung dma... expect problems.");
|
||||
// original game also check sceSdVoiceTransStatus here, we'll possibly need to mess with this
|
||||
// if we delay dma completion interrupts...
|
||||
g_voiceTransRunning = false;
|
||||
voice_trans_wrapper(g_voiceTransChannel, g_voiceTransMode, g_voiceTransAddr,
|
||||
g_voiceTransSpuAddr, g_voiceTransSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace jak3
|
32
game/overlord/jak3/dma.h
Normal file
32
game/overlord/jak3/dma.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_dma();
|
||||
struct ISO_VAGCommand;
|
||||
|
||||
int voice_trans_wrapper(s16 chan, u32 mode, const void* iop_addr, u32 spu_addr, u32 size);
|
||||
void DMA_SendToEE(void* ee_dest,
|
||||
const void* iop_src,
|
||||
u32 length,
|
||||
void callback(void*),
|
||||
void* callback_arg);
|
||||
int DMA_SendToSPUAndSync(const u8* iop_mem,
|
||||
int length,
|
||||
int spu_addr,
|
||||
ISO_VAGCommand* cmd,
|
||||
void* user_data);
|
||||
void RunDeferredVoiceTrans();
|
||||
struct ISO_VAGCommand;
|
||||
|
||||
struct DmaQueueEntry {
|
||||
ISO_VAGCommand* command = nullptr;
|
||||
const void* iop_mem = nullptr;
|
||||
u32 spu_addr = 0;
|
||||
u32 length = 0;
|
||||
void* user_data = nullptr;
|
||||
u32 num_isobuffered_chunks = 0;
|
||||
};
|
||||
void dma_intr_hack();
|
||||
} // namespace jak3
|
582
game/overlord/jak3/dvd_driver.cpp
Normal file
582
game/overlord/jak3/dvd_driver.cpp
Normal file
@ -0,0 +1,582 @@
|
||||
#include "dvd_driver.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
using namespace iop;
|
||||
std::unique_ptr<CDvdDriver> g_DvdDriver;
|
||||
s32 g_nDvdDriverThread = -1;
|
||||
|
||||
void jak3_overlord_init_globals_dvd_driver() {
|
||||
g_DvdDriver = std::make_unique<CDvdDriver>();
|
||||
g_nDvdDriverThread = -1;
|
||||
}
|
||||
|
||||
CDvdDriver* get_driver() {
|
||||
return g_DvdDriver.get();
|
||||
}
|
||||
|
||||
CMsg::CMsg(jak3::CMsg::MsgKind msg) : m_msg(msg) {
|
||||
m_ret = 1;
|
||||
m_thread = GetThreadId();
|
||||
}
|
||||
|
||||
int CMsg::send() {
|
||||
// note: changed from passing data
|
||||
// and removed corresponding -4 to skip back past the vtable in DVD thread
|
||||
// (what were they thinking?)
|
||||
s32 ret = SendMbx(get_driver()->msgbox, this);
|
||||
if (ret == 0) {
|
||||
get_driver()->KickDvdThread();
|
||||
SleepThread();
|
||||
return m_ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// CMsgLock::CMsgLock() : CMsg(CMsg::MsgKind::LOCK) {}
|
||||
//
|
||||
// void CMsgLock::handler() {
|
||||
// get_driver()->Lock();
|
||||
// m_ret = 0;
|
||||
// }
|
||||
|
||||
// CMsgReadRaw::CMsgReadRaw(jak3::BlockParams* params) : CMsg(CMsg::MsgKind::READ_RAW) {
|
||||
// m_block_params = *params;
|
||||
// }
|
||||
//
|
||||
// void CMsgReadRaw::handler() {
|
||||
// m_ret = get_driver()->ReadDirect(&m_block_params);
|
||||
// }
|
||||
|
||||
CMsgCancelRead::CMsgCancelRead(jak3::CDescriptor* desc) : CMsg(CMsg::MsgKind::CANCEL_READ) {
|
||||
m_desc = desc;
|
||||
}
|
||||
|
||||
void CMsgCancelRead::handler() {
|
||||
get_driver()->CancelRead(m_desc);
|
||||
m_ret = 0;
|
||||
}
|
||||
|
||||
u32 DvdThread();
|
||||
|
||||
CDvdDriver::CDvdDriver() {
|
||||
fifo_entry_sema = -1;
|
||||
current_thread_priority = 0x13;
|
||||
disk_type = 5;
|
||||
tray_flag = 1;
|
||||
// m_nLockCount = 0;
|
||||
event_flag = -1;
|
||||
fifo_access_sema = -1;
|
||||
tray_flag2 = 1;
|
||||
initialized = 0;
|
||||
m_nNumFifoEntries = 0;
|
||||
ring_head = 0;
|
||||
ring_tail = 0;
|
||||
read_in_progress = 0;
|
||||
callback = nullptr;
|
||||
// locked = false;
|
||||
trayflag3 = 0;
|
||||
m_nDvdThreadAccessSemaCount = 0;
|
||||
memset(ring, 0, sizeof(Block) * 16);
|
||||
}
|
||||
|
||||
void CDvdDriver::Initialize() {
|
||||
if (!initialized) {
|
||||
*this = {};
|
||||
ThreadParam thread_param;
|
||||
thread_param.attr = 0x2000000;
|
||||
// mbox_param.option = gDvdDriverThreadOptions; // ???
|
||||
thread_param.entry = DvdThread;
|
||||
thread_param.stackSize = 0x800;
|
||||
thread_param.initPriority = 0x13;
|
||||
thread_param.option = 0;
|
||||
strcpy(thread_param.name, "dvd");
|
||||
// mbox_param.attr = (int)PTR_DvdThread_00015c98; // ???
|
||||
g_nDvdDriverThread = CreateThread(&thread_param);
|
||||
ASSERT(g_nDvdDriverThread >= 0);
|
||||
|
||||
SemaParam sema_param;
|
||||
sema_param.attr = 0;
|
||||
sema_param.init_count = 1;
|
||||
sema_param.max_count = 1;
|
||||
sema_param.option = 0;
|
||||
fifo_access_sema = CreateSema(&sema_param);
|
||||
ASSERT(fifo_access_sema >= 0);
|
||||
sema_param.max_count = 0x10;
|
||||
sema_param.attr = 0;
|
||||
sema_param.init_count = 0x10;
|
||||
sema_param.option = 0;
|
||||
fifo_entry_sema = CreateSema(&sema_param);
|
||||
ASSERT(fifo_entry_sema >= 0);
|
||||
MbxParam mbox_param;
|
||||
mbox_param.attr = 0;
|
||||
mbox_param.option = 0;
|
||||
msgbox = CreateMbx(&mbox_param);
|
||||
ASSERT(msgbox >= 0);
|
||||
|
||||
EventFlagParam param;
|
||||
param.attr = 0;
|
||||
param.option = 0;
|
||||
param.init_pattern = 0;
|
||||
event_flag = CreateEventFlag(¶m);
|
||||
ASSERT(event_flag >= 0);
|
||||
StartThread(g_nDvdDriverThread, 0); // this...
|
||||
}
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
void CDvdDriver::SetDriverCallback(std::function<void(int)> f) {
|
||||
callback = f;
|
||||
}
|
||||
|
||||
// GetDriveCallback
|
||||
|
||||
// Poll - would kick the thread...
|
||||
|
||||
// void CDvdDriver::Lock() {
|
||||
// ASSERT_NOT_REACHED();
|
||||
// if (GetThreadId() == g_nDvdDriverThread) {
|
||||
// m_nLockCount++;
|
||||
// locked = true;
|
||||
// // needs break HACK
|
||||
// needs_break = false;
|
||||
// } else {
|
||||
// CMsgLock lock;
|
||||
// lock.send();
|
||||
// }
|
||||
// }
|
||||
|
||||
// Read
|
||||
|
||||
int CDvdDriver::ReadMultiple(CDescriptor* descriptor,
|
||||
int* pages_read_out,
|
||||
BlockParams* params,
|
||||
int num_blocks,
|
||||
bool block_if_queue_full) {
|
||||
*pages_read_out = 0;
|
||||
s32 ret = 1;
|
||||
|
||||
// check block parameters are reasonable
|
||||
if (ValidateBlockParams(params, num_blocks) != 0) {
|
||||
bool from_dvd_thread = GetThreadId() == g_nDvdDriverThread;
|
||||
if (from_dvd_thread) {
|
||||
// there is a setting to control if this function should block if there are too many
|
||||
// queued reads. If the is called from the DVD thread, then this would deadlock.
|
||||
// the original game ignored the block argument, but I'm asserting
|
||||
block_if_queue_full = 0;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
ovrld_log(LogCategory::DRIVER, "[driver] ReadMultiple (from our thread? {}) num_blocks {}",
|
||||
from_dvd_thread, num_blocks);
|
||||
|
||||
ret = 0;
|
||||
if (0 < num_blocks) {
|
||||
// loop, until we've done all the requested reads.
|
||||
do {
|
||||
s32 acquired_slots = 0;
|
||||
if (0 < num_blocks) {
|
||||
// loop to try to get up to num_blocks slots in the fifo
|
||||
// but, if we get less, we'll take that too
|
||||
do {
|
||||
if (PollSema(this->fifo_entry_sema) == -0x1a3)
|
||||
break;
|
||||
acquired_slots = acquired_slots + 1;
|
||||
} while (acquired_slots < num_blocks);
|
||||
}
|
||||
ovrld_log(LogCategory::DRIVER, "[driver] ReadMultiple acquired {} slots in ring",
|
||||
acquired_slots);
|
||||
|
||||
// if we are blocking, and we acquired no slots, then we'll wait here until we get one slot.
|
||||
if ((block_if_queue_full != 0) && (acquired_slots < 1)) {
|
||||
ovrld_log(LogCategory::DRIVER, "[driver] ring is full, blocking!");
|
||||
acquired_slots = 1; // the one we'll get from the WaitSema below
|
||||
do {
|
||||
} while (WaitSema(fifo_entry_sema) != 0);
|
||||
}
|
||||
|
||||
// lock, now that we've gotten the slots
|
||||
AcquireFIFOSema(from_dvd_thread);
|
||||
num_blocks = num_blocks - acquired_slots;
|
||||
|
||||
// if we didn't get any slots, bail.
|
||||
if (acquired_slots < 1) {
|
||||
ovrld_log(LogCategory::DRIVER, "[driver] ring is full, bailing!");
|
||||
ReleaseFIFOSema(from_dvd_thread);
|
||||
if (0 < *pages_read_out) {
|
||||
KickDvdThread();
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
// loop, updating the ring for each slot we aquired
|
||||
do {
|
||||
auto* slot = ring + ring_tail;
|
||||
ovrld_log(LogCategory::DRIVER, "[driver] inserting in ring slot {}", ring_tail);
|
||||
|
||||
ring_tail++;
|
||||
if (0xf < ring_tail) {
|
||||
ring_tail = 0;
|
||||
}
|
||||
m_nNumFifoEntries++;
|
||||
|
||||
auto* tail = descriptor->m_pTail;
|
||||
slot->descriptor = descriptor;
|
||||
slot->params = params[*pages_read_out];
|
||||
*pages_read_out = (*pages_read_out) + 1;
|
||||
if (!tail) {
|
||||
descriptor->m_pHead = slot;
|
||||
} else {
|
||||
tail->next = slot;
|
||||
}
|
||||
acquired_slots = acquired_slots + -1;
|
||||
descriptor->m_pTail = slot;
|
||||
slot->next = nullptr;
|
||||
} while (acquired_slots != 0);
|
||||
ReleaseFIFOSema(from_dvd_thread);
|
||||
KickDvdThread();
|
||||
ret = 0;
|
||||
} while (0 < num_blocks);
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CDvdDriver::CancelRead(jak3::CDescriptor* desc) {
|
||||
if (GetThreadId() == g_nDvdDriverThread) {
|
||||
AcquireFIFOSema(true);
|
||||
if ((read_in_progress != 0) && (ring[ring_head].descriptor == desc)) {
|
||||
// while (iVar1 = sceCdBreak(), iVar1 == 0) {
|
||||
// DelayThread(8000);
|
||||
// sceCdSync(0);
|
||||
// }
|
||||
// sceCdSync(0);
|
||||
read_in_progress = 0;
|
||||
}
|
||||
|
||||
Block* iter = desc->m_pHead;
|
||||
Block* tail = desc->m_pTail;
|
||||
while (iter) {
|
||||
if (iter->descriptor) {
|
||||
CompletionHandler(iter, 6);
|
||||
}
|
||||
iter->descriptor = nullptr;
|
||||
iter->next = nullptr;
|
||||
if (iter == tail)
|
||||
break;
|
||||
iter = desc->m_pHead;
|
||||
}
|
||||
|
||||
if (desc->m_pTail == tail) {
|
||||
desc->m_pTail = nullptr;
|
||||
}
|
||||
ReleaseFIFOSema(true);
|
||||
} else {
|
||||
CMsgCancelRead msg(desc);
|
||||
msg.send();
|
||||
}
|
||||
}
|
||||
|
||||
s32 CDvdDriver::ValidateBlockParams(jak3::BlockParams* params, int num_params) {
|
||||
if (!params) {
|
||||
lg::die("Invalid BlockParams: nullptr");
|
||||
return 0;
|
||||
}
|
||||
if (num_params < 1) {
|
||||
lg::die("Invalid BlockParams: size == 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_params; i++) {
|
||||
auto& p = params[i];
|
||||
if (p.destination == nullptr) {
|
||||
lg::die("Invalid BlockParams: {} had nullptr dest", i);
|
||||
return 0;
|
||||
}
|
||||
int kMaxFileSize = 1024 * 1024 * 1024;
|
||||
if (p.sector_num > kMaxFileSize / 0x800) {
|
||||
lg::die("Invalid BlockParams: {} had sector num {}", i, p.sector_num);
|
||||
return 0;
|
||||
}
|
||||
if (p.num_sectors > 0x1d0) {
|
||||
lg::die("Invalid BlockParams: {} had sector count {}", i, p.num_sectors);
|
||||
return 0;
|
||||
}
|
||||
if (!p.file_def) {
|
||||
lg::die("Invalid BlockParams: {} had no file", i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CDvdDriver::KickDvdThread() {
|
||||
while (SetEventFlag(event_flag, 1)) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
int CDvdDriver::AcquireFIFOSema(bool from_dvd_thread) {
|
||||
int iVar1;
|
||||
int iVar2;
|
||||
|
||||
if ((from_dvd_thread != 0) &&
|
||||
(iVar2 = m_nDvdThreadAccessSemaCount, m_nDvdThreadAccessSemaCount = iVar2 + 1, 0 < iVar2)) {
|
||||
return 0;
|
||||
}
|
||||
iVar1 = WaitSema(this->fifo_access_sema);
|
||||
iVar2 = 0;
|
||||
if ((iVar1 != 0) && (iVar2 = iVar1, from_dvd_thread != 0)) {
|
||||
m_nDvdThreadAccessSemaCount = m_nDvdThreadAccessSemaCount + -1;
|
||||
iVar2 = iVar1;
|
||||
}
|
||||
return iVar2;
|
||||
}
|
||||
|
||||
int CDvdDriver::ReleaseFIFOSema(bool from_dvd_thread) {
|
||||
int iVar1;
|
||||
int iVar2;
|
||||
|
||||
if (from_dvd_thread != 0) {
|
||||
iVar2 = m_nDvdThreadAccessSemaCount + -1;
|
||||
if (m_nDvdThreadAccessSemaCount < 1) {
|
||||
return -0x1a4;
|
||||
}
|
||||
m_nDvdThreadAccessSemaCount = iVar2;
|
||||
if (0 < iVar2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
iVar1 = SignalSema(fifo_access_sema);
|
||||
iVar2 = 0;
|
||||
if ((iVar1 != 0) && (iVar2 = iVar1, from_dvd_thread != 0)) {
|
||||
m_nDvdThreadAccessSemaCount = m_nDvdThreadAccessSemaCount + 1;
|
||||
iVar2 = iVar1;
|
||||
}
|
||||
return iVar2;
|
||||
}
|
||||
|
||||
/*!
|
||||
* PC port added function to actually do a read of a block.
|
||||
*/
|
||||
void CDvdDriver::read_from_file(const jak3::Block* block) {
|
||||
const auto* fd = block->params.file_def;
|
||||
ASSERT(fd);
|
||||
FileCacheEntry* selected_entry = nullptr;
|
||||
|
||||
// get a cache entry
|
||||
for (auto& entry : m_file_cache) {
|
||||
// if we already opened this file, use that
|
||||
if (entry.def == fd) {
|
||||
selected_entry = &entry;
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise pick the least recently used
|
||||
if (!selected_entry) {
|
||||
selected_entry = &entry;
|
||||
} else {
|
||||
if (selected_entry->last_use_count > entry.last_use_count) {
|
||||
selected_entry = &entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// open a new file if needed
|
||||
if (selected_entry->def != fd) {
|
||||
lg::debug("CDvdDriver swapping files {} - > {}",
|
||||
selected_entry->def ? selected_entry->def->name.data : "NONE", fd->name.data);
|
||||
if (selected_entry->def) {
|
||||
fclose(selected_entry->fp);
|
||||
}
|
||||
|
||||
selected_entry->def = fd;
|
||||
selected_entry->fp = file_util::open_file(fd->full_path, "rb");
|
||||
if (!selected_entry->fp) {
|
||||
lg::die("Failed to open {} {}", fd->full_path, strerror(errno));
|
||||
}
|
||||
selected_entry->offset_in_file = 0;
|
||||
}
|
||||
|
||||
// increment use counter
|
||||
selected_entry->last_use_count = m_file_cache_counter++;
|
||||
|
||||
const u64 desired_offset = block->params.sector_num * 0x800;
|
||||
|
||||
// see if we're reading entirely past the end of the file
|
||||
if (desired_offset >= fd->length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected_entry->offset_in_file != desired_offset) {
|
||||
lg::debug("CDvdDriver jumping in file {}: {} -> {}", fd->name.data,
|
||||
selected_entry->offset_in_file, desired_offset);
|
||||
if (fseek(selected_entry->fp, desired_offset, SEEK_SET)) {
|
||||
ASSERT_NOT_REACHED_MSG("Failed to fseek");
|
||||
}
|
||||
selected_entry->offset_in_file = desired_offset;
|
||||
}
|
||||
|
||||
// read
|
||||
s64 read_length = block->params.num_sectors * 0x800;
|
||||
s64 extra_length = read_length + desired_offset - fd->length;
|
||||
if (extra_length > 0) {
|
||||
read_length -= extra_length;
|
||||
}
|
||||
auto ret = fread(block->params.destination, read_length, 1, selected_entry->fp);
|
||||
if (ret != 1) {
|
||||
lg::die("Failed to read {} {}, size {} of {} (ret {})", fd->full_path, strerror(errno),
|
||||
read_length, fd->length, ret);
|
||||
}
|
||||
selected_entry->offset_in_file += read_length;
|
||||
}
|
||||
|
||||
u32 DvdThread() {
|
||||
auto* driver = get_driver();
|
||||
|
||||
while (true) {
|
||||
// Poll for messages
|
||||
CMsg* msg = nullptr;
|
||||
while (true) {
|
||||
int poll_status = PollMbx((MsgPacket**)&msg, driver->msgbox);
|
||||
if (poll_status || !msg) {
|
||||
break;
|
||||
}
|
||||
// run message code
|
||||
msg->handler();
|
||||
// wake the waiting thread.
|
||||
WakeupThread(msg->m_thread);
|
||||
}
|
||||
|
||||
bool completed = false;
|
||||
|
||||
// if a read is in progress, wait for it to finish.
|
||||
if (driver->read_in_progress) {
|
||||
// TODO: if we switch to async reads, this is where we'd want to sync.
|
||||
// sceCdSync(0);
|
||||
completed = true;
|
||||
// error checking
|
||||
}
|
||||
|
||||
driver->AcquireFIFOSema(true);
|
||||
// error handling
|
||||
|
||||
// handle ring book-keeping.
|
||||
// note that these are somewhat double-buffered - we'll sync on read i-1, start read i, then
|
||||
// run the completion handler for read i - 1.
|
||||
// the completion is what actually removes stuff from the ring.
|
||||
|
||||
s32 fifo_slots_freed = completed ? 1 : 0;
|
||||
s32 ring_entry = driver->ring_head + (completed ? 1 : 0);
|
||||
if (ring_entry > 15) {
|
||||
ring_entry = 0;
|
||||
}
|
||||
Block* block = driver->ring + ring_entry;
|
||||
s32 fifo_entries = driver->m_nNumFifoEntries - fifo_slots_freed;
|
||||
|
||||
if (fifo_entries) {
|
||||
// start a new read.
|
||||
driver->read_in_progress = 1;
|
||||
ovrld_log(LogCategory::DRIVER, "[driver thread] Reading for slot {}", block - driver->ring);
|
||||
driver->read_from_file(block);
|
||||
|
||||
} else {
|
||||
driver->read_in_progress = 0;
|
||||
}
|
||||
|
||||
// run completion handler for the previous read - not the one we just started!
|
||||
Block sblock;
|
||||
if (completed) {
|
||||
auto* last_block = &driver->ring[driver->ring_head];
|
||||
ovrld_log(LogCategory::DRIVER, "[driver thread] Completion handler for {}",
|
||||
driver->ring_head);
|
||||
sblock = *last_block;
|
||||
|
||||
if (sblock.descriptor && sblock.descriptor->m_pHead) {
|
||||
ASSERT(sblock.descriptor->m_pHead == last_block);
|
||||
}
|
||||
if (((sblock.descriptor)->m_pHead == last_block) &&
|
||||
((sblock.descriptor)->m_pHead = &sblock, (sblock.descriptor)->m_pTail == last_block)) {
|
||||
(sblock.descriptor)->m_pTail = &sblock;
|
||||
}
|
||||
}
|
||||
driver->ring_head = ring_entry;
|
||||
driver->m_nNumFifoEntries = fifo_entries;
|
||||
while (0 < fifo_slots_freed) {
|
||||
fifo_slots_freed = fifo_slots_freed + -1;
|
||||
SignalSema(driver->fifo_entry_sema);
|
||||
}
|
||||
if (completed != 0) {
|
||||
driver->CompletionHandler(&sblock, 0);
|
||||
}
|
||||
driver->ReleaseFIFOSema(true);
|
||||
|
||||
// this logic here was modified - we go to waiting if we have no more reads.
|
||||
if (driver->m_nNumFifoEntries == 0) {
|
||||
ovrld_log(LogCategory::DRIVER, "[driver thread] No work, waiting.");
|
||||
WaitEventFlag(driver->event_flag, 1, 0x11);
|
||||
ovrld_log(LogCategory::DRIVER, "[driver thread] Woken up!");
|
||||
}
|
||||
|
||||
// s32 status;
|
||||
// if ((((driver->locked != false)) ||
|
||||
// ((driver->needs_break == 0 && (driver->m_nNumFifoEntries == 0)))) &&
|
||||
// ((status = WaitEventFlag(driver->event_flag, 1, 0x11),
|
||||
// status != 0 && (status != -0x1a2)))) {
|
||||
// if (status == -0x1a9) {
|
||||
// do {
|
||||
// SleepThread();
|
||||
// } while (true);
|
||||
// }
|
||||
// DelayThread(8000);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void CDvdDriver::CompletionHandler(jak3::Block* block, int code) {
|
||||
// there is some janky thread priority changes here,
|
||||
// but they do not seem to be needed with the changes to the ISO thread.
|
||||
|
||||
// PushPri(this,local_20,0x35);
|
||||
auto* desc = block->descriptor;
|
||||
if (desc && desc->m_pHead) {
|
||||
desc->m_status = code;
|
||||
int thread = desc->m_ThreadID;
|
||||
if (desc->m_Callback) {
|
||||
(desc->m_Callback)(desc->m_File, block, code);
|
||||
}
|
||||
if (0 < thread) {
|
||||
WakeupThread(thread);
|
||||
}
|
||||
|
||||
Block* next;
|
||||
if ((desc->m_pHead == block) && (next = block->next, desc->m_pHead = next, next == nullptr)) {
|
||||
desc->m_pTail = nullptr;
|
||||
}
|
||||
}
|
||||
block->next = nullptr;
|
||||
block->descriptor = nullptr;
|
||||
// PopPri(this, local_20[0]);
|
||||
}
|
||||
|
||||
CDvdDriver::~CDvdDriver() {
|
||||
for (auto& entry : m_file_cache) {
|
||||
if (entry.fp) {
|
||||
fclose(entry.fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
144
game/overlord/jak3/dvd_driver.h
Normal file
144
game/overlord/jak3/dvd_driver.h
Normal file
@ -0,0 +1,144 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_dvd_driver();
|
||||
|
||||
/*!
|
||||
* The DVD Driver is what actually called the Sony CD library functions.
|
||||
* It also had many special cases for handling errors, open trays, retries, etc that are not
|
||||
* recreated in this port.
|
||||
*
|
||||
* The original DVD driver thread didn't run the actual reads - the Sony CD library had its own
|
||||
* threads for async reads. However, they would call sceCdSync() to wait until a read finished.
|
||||
* It's not clear if sceCdSync would allow other threads to run while waiting on the read to finish.
|
||||
* but if it did, this is a bit of a difference in the blocking behavior.
|
||||
*
|
||||
* This is something that could be revisited, as freads will now essentially block the entire
|
||||
* overlord, including refilling sound RAM.
|
||||
*/
|
||||
|
||||
struct CDescriptor;
|
||||
struct BlockParams;
|
||||
struct CPage;
|
||||
struct ISOFileDef;
|
||||
|
||||
// The CMSG system is used to pass messages from external threads to the driver. It's mostly used
|
||||
// internally by the driver, when functions are called on it from outside threads. In these cases
|
||||
// the Cmsg will get send to the driver thread and handler will run on it here.
|
||||
|
||||
struct CMsg {
|
||||
enum class MsgKind {
|
||||
// LOCK = 0,
|
||||
READ_RAW = 1,
|
||||
CANCEL_READ = 2,
|
||||
};
|
||||
CMsg(MsgKind msg);
|
||||
virtual int send();
|
||||
virtual void handler() = 0;
|
||||
|
||||
u8 data[8];
|
||||
MsgKind m_msg;
|
||||
int m_thread;
|
||||
int m_ret;
|
||||
};
|
||||
|
||||
// struct CMsgLock : public CMsg {
|
||||
// CMsgLock();
|
||||
// void handler() override;
|
||||
// };
|
||||
|
||||
// struct CMsgReadRaw : public CMsg {
|
||||
// explicit CMsgReadRaw(BlockParams* params);
|
||||
// void handler() override;
|
||||
// BlockParams m_block_params;
|
||||
// };
|
||||
|
||||
struct CMsgCancelRead : public CMsg {
|
||||
explicit CMsgCancelRead(CDescriptor* desc);
|
||||
void handler() override;
|
||||
CDescriptor* m_desc;
|
||||
};
|
||||
|
||||
struct CISOCDFile;
|
||||
|
||||
/*!
|
||||
* Reference to an ongoing or requested read at the driver level, possibly made up of multiple
|
||||
* blocks. In this case, the head/tail pointers point to Blocks stored inside the CDvdDriver's
|
||||
* internal FIFO.
|
||||
*/
|
||||
struct CDescriptor {
|
||||
CDescriptor() = default;
|
||||
int m_unk0 = 0;
|
||||
void (*m_Callback)(CISOCDFile*, Block*, s32) = nullptr;
|
||||
int m_ThreadID = 0;
|
||||
int m_status = 0;
|
||||
CISOCDFile* m_File = nullptr;
|
||||
Block* m_pHead = nullptr;
|
||||
Block* m_pTail = nullptr;
|
||||
};
|
||||
|
||||
class CDvdDriver {
|
||||
public:
|
||||
CDvdDriver();
|
||||
~CDvdDriver();
|
||||
void Initialize();
|
||||
void CancelRead(CDescriptor* descriptor);
|
||||
int ReadMultiple(CDescriptor* descriptor,
|
||||
int* pages_read_out,
|
||||
BlockParams* params,
|
||||
int num_pages,
|
||||
bool block_if_queue_full);
|
||||
void SetDriverCallback(std::function<void(int)> f);
|
||||
// int ReadDirect(BlockParams* params);
|
||||
void KickDvdThread();
|
||||
// void Lock();
|
||||
int GetDiskType() const { return disk_type; }
|
||||
s32 ValidateBlockParams(BlockParams* params, int num_params);
|
||||
int ReleaseFIFOSema(bool from_dvd_thread);
|
||||
int AcquireFIFOSema(bool from_dvd_thread);
|
||||
void read_from_file(const Block* block);
|
||||
void CompletionHandler(Block* block, int code);
|
||||
|
||||
u8 initialized = 0;
|
||||
s32 event_flag = -1;
|
||||
s32 fifo_access_sema = -1;
|
||||
s32 fifo_entry_sema = -1;
|
||||
s32 msgbox = -1;
|
||||
s32 m_nNumFifoEntries = 0;
|
||||
s32 ring_head = 0;
|
||||
s32 ring_tail = 0;
|
||||
Block ring[16];
|
||||
u8 read_in_progress; // more likely: read in progress
|
||||
std::function<void(int)> callback;
|
||||
s32 current_thread_priority = 0;
|
||||
// s32 m_nLockCount = 0;
|
||||
// bool locked = false;
|
||||
//
|
||||
s32 disk_type;
|
||||
u8 tray_flag2;
|
||||
u8 trayflag3;
|
||||
u8 tray_flag;
|
||||
s32 m_nDvdThreadAccessSemaCount = 0;
|
||||
|
||||
private:
|
||||
struct FileCacheEntry {
|
||||
const ISOFileDef* def = nullptr;
|
||||
FILE* fp = nullptr;
|
||||
u32 last_use_count = 0;
|
||||
u64 offset_in_file = 0;
|
||||
};
|
||||
u32 m_file_cache_counter = 0;
|
||||
static constexpr int kNumFileCacheEntries = 6;
|
||||
FileCacheEntry m_file_cache[kNumFileCacheEntries];
|
||||
};
|
||||
|
||||
// replacement for g_DvdDriver
|
||||
CDvdDriver* get_driver();
|
||||
|
||||
} // namespace jak3
|
50
game/overlord/jak3/init.cpp
Normal file
50
game/overlord/jak3/init.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
#include "game/overlord/jak3/basefile.h"
|
||||
#include "game/overlord/jak3/basefilesystem.h"
|
||||
#include "game/overlord/jak3/dma.h"
|
||||
#include "game/overlord/jak3/dvd_driver.h"
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/iso_api.h"
|
||||
#include "game/overlord/jak3/iso_cd.h"
|
||||
#include "game/overlord/jak3/iso_queue.h"
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
#include "game/overlord/jak3/list.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/overlord/jak3/pagemanager.h"
|
||||
#include "game/overlord/jak3/ramdisk.h"
|
||||
#include "game/overlord/jak3/sbank.h"
|
||||
#include "game/overlord/jak3/soundcommon.h"
|
||||
#include "game/overlord/jak3/spustreams.h"
|
||||
#include "game/overlord/jak3/srpc.h"
|
||||
#include "game/overlord/jak3/ssound.h"
|
||||
#include "game/overlord/jak3/stream.h"
|
||||
#include "game/overlord/jak3/streamlist.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/overlord/jak3/vblank_handler.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_all() {
|
||||
jak3_overlord_init_globals_overlord();
|
||||
jak3_overlord_init_globals_pagemanager();
|
||||
jak3_overlord_init_globals_iso_cd();
|
||||
jak3_overlord_init_globals_dma();
|
||||
jak3_overlord_init_globals_iso();
|
||||
jak3_overlord_init_globals_iso_queue();
|
||||
jak3_overlord_init_globals_srpc();
|
||||
jak3_overlord_init_globals_vag();
|
||||
jak3_overlord_init_globals_ssound();
|
||||
jak3_overlord_init_globals_iso_api();
|
||||
jak3_overlord_init_globals_spustreams();
|
||||
jak3_overlord_init_globals_list();
|
||||
jak3_overlord_init_globals_vblank_handler();
|
||||
jak3_overlord_init_globals_dvd_driver();
|
||||
jak3_overlord_init_globals_basefile();
|
||||
jak3_overlord_init_globals_basefilesystem();
|
||||
jak3_overlord_init_globals_ramdisk();
|
||||
jak3_overlord_init_globals_isocommon();
|
||||
jak3_overlord_init_globals_stream();
|
||||
jak3_overlord_init_globals_sbank();
|
||||
jak3_overlord_init_globals_soundcommon();
|
||||
jak3_overlord_init_globals_streamlist();
|
||||
}
|
||||
} // namespace jak3
|
5
game/overlord/jak3/init.h
Normal file
5
game/overlord/jak3/init.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_all();
|
||||
}
|
1796
game/overlord/jak3/iso.cpp
Normal file
1796
game/overlord/jak3/iso.cpp
Normal file
File diff suppressed because it is too large
Load Diff
92
game/overlord/jak3/iso.h
Normal file
92
game/overlord/jak3/iso.h
Normal file
@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "common/link_types.h"
|
||||
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
|
||||
namespace jak3 {
|
||||
struct ISOFileDef;
|
||||
void jak3_overlord_init_globals_iso();
|
||||
void InitISOFS();
|
||||
|
||||
const ISOFileDef* FindISOFile(const char*);
|
||||
struct ISO_VAGCommand;
|
||||
struct VagStreamData;
|
||||
struct RPC_Dgo_Cmd;
|
||||
|
||||
struct ISO_DGOCommand : public ISO_Hdr {
|
||||
u8* buffer1 = nullptr;
|
||||
u8* buffer2 = nullptr;
|
||||
u8* buffer_top = nullptr;
|
||||
DgoHeader dgo_header; //
|
||||
ObjectHeader obj_header; //
|
||||
u8* ee_dest_buffer = nullptr; // 192
|
||||
int bytes_processed = 0; // 196
|
||||
int objects_loaded = 0; // 200
|
||||
enum class State {
|
||||
INIT = 0,
|
||||
READ_DGO_HEADER = 1,
|
||||
FINISH_OBJ = 2,
|
||||
READ_LAST_OBJ = 3,
|
||||
READ_OBJ_HEADER = 4,
|
||||
READ_OBJ_DATA = 5,
|
||||
FINISH_DGO = 6,
|
||||
FINISH_OBJ_SINGLE_BUFFER = 7,
|
||||
} state = State::INIT; // 204
|
||||
int finished_first_object = 0; // 208
|
||||
int buffer_toggle = 0; // 212
|
||||
u8* selected_buffer = nullptr; // 216
|
||||
int selected_id = 0; // 220
|
||||
int last_id = 0; // 224
|
||||
int acked_cancel_id = 0; // 228
|
||||
u8 nosync_cancel_pending_flag = 0; // 232
|
||||
int request_cancel_id = 0; // 236
|
||||
u8 nosync_cancel_ack = 0; // 240
|
||||
int sync_sent_count = 0; // 244
|
||||
|
||||
// the number of times we looked in the iso thread's sync mbox
|
||||
// I think just used for debugging/asserts.
|
||||
int sync_mbox_wait_count = 0; // 248
|
||||
|
||||
int sync_ret_count = 0; // 252
|
||||
int want_abort = 0; // 256
|
||||
};
|
||||
|
||||
enum class CopyKind {
|
||||
EE = 0,
|
||||
IOP = 1,
|
||||
SBK = 2,
|
||||
};
|
||||
|
||||
void set_active_a(ISO_Hdr* cmd, int val);
|
||||
void set_active_b(ISO_Hdr* cmd, int val);
|
||||
void set_active_c(ISO_Hdr* cmd, int val);
|
||||
void IsoStopVagStream(ISO_VAGCommand* cmd);
|
||||
void IsoPlayVagStream(ISO_VAGCommand* user_cmd);
|
||||
EIsoStatus NullCallback(ISO_Hdr* msg);
|
||||
EIsoStatus CopyDataToIOP(ISO_Hdr* msg);
|
||||
EIsoStatus CopyDataSbkLoad(ISO_Hdr* msg);
|
||||
EIsoStatus CopyDataToEE(ISO_Hdr* msg);
|
||||
EIsoStatus RunDGOStateMachine(ISO_Hdr* msg);
|
||||
void QueueVAGStream(VagStreamData* cmd);
|
||||
void CopyDataDmaCallback(void*);
|
||||
void* RPC_DGO(unsigned int fno, void* msg_ptr, int);
|
||||
void LoadDGO(RPC_Dgo_Cmd* cmd);
|
||||
void CancelDGO(RPC_Dgo_Cmd* cmd);
|
||||
void LoadNextDGO(RPC_Dgo_Cmd* cmd);
|
||||
EIsoStatus CopyData(ISO_LoadCommon* msg, CopyKind kind);
|
||||
void CancelDGONoSync(int id);
|
||||
void IsoPlayMusicStream(ISO_VAGCommand* user_cmd);
|
||||
void IsoQueueVagStream(ISO_VAGCommand* user_cmd);
|
||||
extern int g_nISOThreadID;
|
||||
extern int g_nISOMbx;
|
||||
extern bool g_bMusicPause;
|
||||
extern int g_nMusicSemaphore;
|
||||
extern char g_szTargetMusicName[0x30];
|
||||
extern int g_nActiveMusicStreams;
|
||||
extern bool g_bVagCmdsInitialized;
|
||||
extern bool g_bMusicIsPaused;
|
||||
extern bool g_bAnotherMusicPauseFlag;
|
||||
extern int g_nMusicFade;
|
||||
extern int g_nMusicTweak;
|
||||
extern int g_nMusicFadeDir;
|
||||
} // namespace jak3
|
229
game/overlord/jak3/iso_api.cpp
Normal file
229
game/overlord/jak3/iso_api.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
#include "iso_api.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/iso_cd.h"
|
||||
#include "game/overlord/jak3/iso_queue.h"
|
||||
#include "game/overlord/jak3/srpc.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
using namespace iop;
|
||||
void jak3_overlord_init_globals_iso_api() {}
|
||||
|
||||
/*!
|
||||
* This file is the "API" that implementations of RPCs or other code can use to submit things to the
|
||||
* ISO thread. Note that not all RPCs use this API - for example the DGO RPC just manually submits
|
||||
* messages. Generally, these functions will not return until the actual action is complete, like a
|
||||
* file is loaded.
|
||||
* - except for messages to pause/play audio, those functions will return immediately, but there may
|
||||
* be a delay until they are actually processed.
|
||||
*/
|
||||
|
||||
// PluginVagAndVagWad not ported.
|
||||
|
||||
u32 EEVagAndVagWad(ISO_VAGCommand* cmd, char* name) {
|
||||
ISOName name_buff;
|
||||
if (*name == '$' || strlen(name) < 9) {
|
||||
if (*name == '$') {
|
||||
name++;
|
||||
}
|
||||
MakeISOName(&name_buff, name);
|
||||
} else {
|
||||
file_util::ISONameFromAnimationName(name_buff.data, name);
|
||||
}
|
||||
|
||||
cmd->vag_dir_entry = get_file_system()->FindVAGFile(name_buff.data);
|
||||
if (cmd->vag_dir_entry) {
|
||||
memcpy(name_buff.data, "VAGWAD ", 8);
|
||||
strncpy(name_buff.data + 8, g_pszLanguage, 4);
|
||||
auto* file_def = get_file_system()->FindIN(&name_buff);
|
||||
// piVar1 = g_pFileSystem;
|
||||
cmd->file_def = file_def;
|
||||
|
||||
strncpy(name_buff.data + 8, "INT", 4);
|
||||
|
||||
cmd->vag_file_def = get_file_system()->FindIN(&name_buff);
|
||||
if (cmd->vag_dir_entry && cmd->file_def && cmd->vag_file_def) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
cmd->vag_dir_entry = nullptr;
|
||||
cmd->file_def = nullptr;
|
||||
cmd->vag_file_def = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LoadISOFileToIOP(const ISOFileDef* file_def, void* addr, int length) {
|
||||
ISO_LoadSingle cmd;
|
||||
cmd.msg_type = ISO_Hdr::MsgType::LOAD_IOP;
|
||||
cmd.mbox_reply = 0;
|
||||
cmd.thread_to_wake = GetThreadId();
|
||||
cmd.file_def = file_def;
|
||||
cmd.addr = (u8*)addr;
|
||||
cmd.maxlen = length;
|
||||
lg::warn("--------------- LoadISOFileToIOP START");
|
||||
SendMbx(g_nISOMbx, &cmd);
|
||||
SleepThread();
|
||||
lg::warn("--------------- LoadISOFileToIOP END");
|
||||
if (cmd.status == EIsoStatus::NONE_0) {
|
||||
return cmd.length_to_copy;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int LoadISOFileToEE(const ISOFileDef* file_def, u32 addr, int length) {
|
||||
ISO_LoadSingle cmd;
|
||||
cmd.msg_type = ISO_Hdr::MsgType::LOAD_EE;
|
||||
cmd.mbox_reply = 0;
|
||||
cmd.thread_to_wake = GetThreadId();
|
||||
cmd.file_def = file_def;
|
||||
cmd.addr = (u8*)(u64)addr;
|
||||
cmd.maxlen = length;
|
||||
lg::warn("--------------- LoadISOFileToEE START");
|
||||
SendMbx(g_nISOMbx, &cmd);
|
||||
SleepThread();
|
||||
lg::warn("--------------- LoadISOFileToEE END");
|
||||
if (cmd.status == EIsoStatus::NONE_0) {
|
||||
return cmd.length_to_copy;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LoadISOFileChunkToEE(const ISOFileDef* file_def, u32 addr, int max_len, int sector_offset) {
|
||||
ISO_LoadSingle cmd;
|
||||
cmd.msg_type = ISO_Hdr::MsgType::LOAD_EE_CHUNK;
|
||||
cmd.mbox_reply = 0;
|
||||
cmd.thread_to_wake = GetThreadId();
|
||||
cmd.file_def = file_def;
|
||||
cmd.addr = (u8*)(u64)addr;
|
||||
cmd.maxlen = max_len;
|
||||
cmd.sector_offset = sector_offset;
|
||||
lg::warn("--------------- LoadISOFileChunkToEE START");
|
||||
SendMbx(g_nISOMbx, &cmd);
|
||||
SleepThread();
|
||||
lg::warn("--------------- LoadISOFileChunkToEE END");
|
||||
if (cmd.status == EIsoStatus::NONE_0) {
|
||||
return cmd.length_to_copy;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 LoadSoundBankToIOP(const char* name, SoundBankInfo* bank, u32 mode) {
|
||||
ISO_LoadSoundbank cmd;
|
||||
cmd.msg_type = ISO_Hdr::MsgType::LOAD_SOUNDBANK;
|
||||
cmd.mbox_reply = 0;
|
||||
cmd.thread_to_wake = GetThreadId();
|
||||
cmd.bank_info = bank;
|
||||
cmd.name = name;
|
||||
cmd.priority = mode;
|
||||
lg::warn("--------------- LoadSoundBankToIOP START");
|
||||
SendMbx(g_nISOMbx, &cmd);
|
||||
SleepThread();
|
||||
lg::warn("--------------- LoadSoundBankToIOP END");
|
||||
|
||||
return (u32)cmd.status;
|
||||
}
|
||||
|
||||
void PlayMusicStream(VagStreamData* stream) {
|
||||
int iVar1;
|
||||
ISO_VAGCommand cmd;
|
||||
|
||||
cmd.msg_type = ISO_Hdr::MsgType::PLAY_MUSIC_STREAM;
|
||||
cmd.mbox_reply = 0;
|
||||
cmd.thread_to_wake = 0;
|
||||
iVar1 = EEVagAndVagWad(&cmd, stream->name);
|
||||
if (iVar1 == 0) {
|
||||
// if (bWarn == 0) {
|
||||
// bWarn = 1;
|
||||
// }
|
||||
} else {
|
||||
cmd.play_volume = 0x400;
|
||||
// bWarn = 0;
|
||||
strncpy(cmd.name, stream->name, 0x30);
|
||||
cmd.id = stream->id;
|
||||
cmd.priority_pq = 9;
|
||||
cmd.music_flag = 1;
|
||||
cmd.maybe_sound_handler = 0;
|
||||
cmd.plugin_id = 0;
|
||||
cmd.art_flag = 0;
|
||||
cmd.movie_flag = 0;
|
||||
cmd.updated_trans = 0;
|
||||
IsoPlayMusicStream(&cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueVAGStream(VagStreamData* stream) {
|
||||
bool bVar1;
|
||||
bool bVar2;
|
||||
ISO_VAGCommand cmd;
|
||||
|
||||
cmd.msg_type = ISO_Hdr::MsgType::VAG_QUEUE;
|
||||
cmd.mbox_reply = 0;
|
||||
cmd.thread_to_wake = 0;
|
||||
if (stream->sound_handler == 0) {
|
||||
EEVagAndVagWad(&cmd, stream->name);
|
||||
cmd.play_volume = 0x400;
|
||||
cmd.play_group = 2;
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
// PluginVagAndVagWad(&cmd,stream);
|
||||
// cmd.play_volume = stream->maybe_volume2;
|
||||
// cmd.oog = stream->maybe_volume_3;
|
||||
// cmd.play_group = stream->group;
|
||||
}
|
||||
strncpy(cmd.name, stream->name, 0x30);
|
||||
cmd.id = stream->id;
|
||||
cmd.plugin_id = stream->plugin_id;
|
||||
cmd.priority_pq = stream->priority;
|
||||
cmd.maybe_sound_handler = stream->sound_handler;
|
||||
bVar1 = stream->movie_art_load != 0;
|
||||
cmd.movie_flag = bVar1;
|
||||
bVar2 = stream->art_load != 0;
|
||||
cmd.art_flag = bVar2;
|
||||
cmd.music_flag = 0;
|
||||
if (bVar2) {
|
||||
cmd.flags.art = 1;
|
||||
}
|
||||
if (bVar1) {
|
||||
cmd.flags.movie = 1;
|
||||
}
|
||||
cmd.updated_trans = 0;
|
||||
IsoQueueVagStream(&cmd);
|
||||
}
|
||||
|
||||
void PauseVAGStreams() {
|
||||
auto* cmd = GetVAGCommand();
|
||||
cmd->msg_type = ISO_Hdr::MsgType::VAG_PAUSE;
|
||||
cmd->mbox_reply = 0;
|
||||
cmd->thread_to_wake = 0;
|
||||
SendMbx(g_nISOMbx, cmd);
|
||||
}
|
||||
|
||||
void UnpauseVAGStreams() {
|
||||
auto* cmd = GetVAGCommand();
|
||||
cmd->msg_type = ISO_Hdr::MsgType::VAG_UNPAUSE;
|
||||
cmd->mbox_reply = 0;
|
||||
cmd->thread_to_wake = 0;
|
||||
SendMbx(g_nISOMbx, cmd);
|
||||
}
|
||||
|
||||
void SetVAGStreamPitch(int id, int pitch) {
|
||||
auto* cmd = GetVAGCommand();
|
||||
cmd->msg_type = ISO_Hdr::MsgType::VAG_SET_PITCH_VOL;
|
||||
cmd->id = id;
|
||||
cmd->pitch_cmd = pitch;
|
||||
cmd->mbox_reply = 0;
|
||||
cmd->thread_to_wake = 0;
|
||||
SendMbx(g_nISOMbx, cmd);
|
||||
}
|
||||
|
||||
} // namespace jak3
|
19
game/overlord/jak3/iso_api.h
Normal file
19
game/overlord/jak3/iso_api.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_iso_api();
|
||||
|
||||
struct ISOFileDef;
|
||||
struct VagStreamData;
|
||||
struct SoundBankInfo;
|
||||
|
||||
int LoadISOFileToEE(const ISOFileDef* file_def, u32 addr, int length);
|
||||
int LoadISOFileToIOP(const ISOFileDef* file_def, void* addr, int length);
|
||||
void PlayMusicStream(VagStreamData* data);
|
||||
int LoadISOFileChunkToEE(const ISOFileDef* file_def, u32 addr, int max_len, int sector_offset);
|
||||
void SetVAGStreamPitch(s32 id, s32 pitch);
|
||||
void UnpauseVAGStreams();
|
||||
u32 LoadSoundBankToIOP(const char* name, SoundBankInfo* bank, u32 mode);
|
||||
} // namespace jak3
|
521
game/overlord/jak3/iso_cd.cpp
Normal file
521
game/overlord/jak3/iso_cd.cpp
Normal file
@ -0,0 +1,521 @@
|
||||
#include "iso_cd.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/overlord/jak3/spustreams.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
namespace jak3 {
|
||||
VagDir g_VagDir;
|
||||
MusicTweaks gMusicTweakInfo;
|
||||
CISOCDFile g_CISOCDFiles[kMaxOpenFiles];
|
||||
|
||||
namespace {
|
||||
CISOCDFile* g_pReadInfo = nullptr;
|
||||
std::vector<ISOFileDef> g_FileDefs;
|
||||
std::unique_ptr<CISOCDFileSystem> g_ISOCDFileSystem;
|
||||
} // namespace
|
||||
|
||||
void jak3_overlord_init_globals_iso_cd() {
|
||||
g_pReadInfo = nullptr;
|
||||
for (auto& f : g_CISOCDFiles) {
|
||||
f = CISOCDFile();
|
||||
}
|
||||
g_FileDefs.clear();
|
||||
g_VagDir = {};
|
||||
g_ISOCDFileSystem = std::make_unique<CISOCDFileSystem>();
|
||||
gMusicTweakInfo = {};
|
||||
}
|
||||
|
||||
CBaseFileSystem* get_file_system() {
|
||||
return g_ISOCDFileSystem.get();
|
||||
}
|
||||
|
||||
CISOCDFile::CISOCDFile() {
|
||||
m_nSector = -1;
|
||||
m_nLoaded = 0;
|
||||
m_nLength = 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void ReadPagesCallbackF(CISOCDFile* file, Block* block, s32 error) {
|
||||
file->ReadPagesCallback(block, error);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CISOCDFile::CISOCDFile(const jak3::ISOFileDef* filedef, s32 process_data_semaphore)
|
||||
: CBaseFile(filedef, process_data_semaphore) {
|
||||
m_nSector = -1;
|
||||
m_nLoaded = 0;
|
||||
m_nLength = 0;
|
||||
m_Descriptor.m_File = this;
|
||||
m_Descriptor.m_Callback = ReadPagesCallbackF;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Call ReadPages to read data from this file into its PageList. I believe that the read
|
||||
* is finished after this function returns. Note that this returns COMPLETE enum in many cases, both
|
||||
* if the read succeeds, or if the read is not attempted for some reasons.
|
||||
*/
|
||||
EIsoStatus CISOCDFile::BeginRead() {
|
||||
ASSERT(m_Buffer.m_pPageList);
|
||||
ASSERT(m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE);
|
||||
|
||||
if (m_Status == EIsoStatus::NONE_0) {
|
||||
return EIsoStatus::ERROR_b;
|
||||
}
|
||||
|
||||
if (m_Status == EIsoStatus::OK_2) {
|
||||
return EIsoStatus::OK_2;
|
||||
}
|
||||
|
||||
ASSERT(m_Status == EIsoStatus::IDLE_1);
|
||||
|
||||
auto* plist = m_Buffer.m_pPageList;
|
||||
|
||||
// how many pages are in our list, but not filled?
|
||||
int num_pages_desired = plist->m_nNumPages - plist->m_nNumActivePages;
|
||||
|
||||
if (!num_pages_desired) {
|
||||
// no space to read
|
||||
return EIsoStatus::OK_2;
|
||||
}
|
||||
|
||||
// convert pages to active, indicating that a read will attempt to fill them:
|
||||
auto* first_page = plist->AddActivePages(num_pages_desired);
|
||||
if (!first_page) {
|
||||
lg::warn("Failed to add {} active pages", num_pages_desired);
|
||||
}
|
||||
|
||||
// convert buffer type - ??
|
||||
switch (m_Buffer.m_eBufferType) {
|
||||
case CBuffer::BufferType::EBT_FREE:
|
||||
ASSERT_NOT_REACHED();
|
||||
case CBuffer::BufferType::NORMAL:
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::REQUEST_NORMAL;
|
||||
break;
|
||||
case CBuffer::BufferType::VAG:
|
||||
m_Buffer.m_eBufferType = CBuffer::BufferType::REQUEST_VAG;
|
||||
break;
|
||||
case CBuffer::BufferType::REQUEST_NORMAL:
|
||||
case CBuffer::BufferType::REQUEST_VAG:
|
||||
break;
|
||||
}
|
||||
|
||||
m_Status = EIsoStatus::OK_2;
|
||||
|
||||
// remember this as our currently reading file
|
||||
g_pReadInfo = this;
|
||||
if (m_FileKind != CBaseFile::Kind::LZO_COMPRESSED) {
|
||||
if (m_nLength == 0 || m_nLoaded < m_nLength) {
|
||||
ovrld_log(LogCategory::PAGING, "Calling ReadPages: sector {}, pages {}", m_nSector,
|
||||
num_pages_desired);
|
||||
ReadPages(m_nSector, first_page, num_pages_desired, nullptr, true);
|
||||
int bytes = 0x8000 * num_pages_desired;
|
||||
m_nLoaded += bytes;
|
||||
m_Buffer.AddData(bytes);
|
||||
m_nSector += bytes >> 0xb;
|
||||
}
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return EIsoStatus::OK_2;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Called by ?? to indicate that the read is done.
|
||||
*/
|
||||
EIsoStatus CISOCDFile::SyncRead() {
|
||||
if (m_Status != EIsoStatus::IDLE_1 && g_pReadInfo) {
|
||||
if (m_Status == EIsoStatus::OK_2) {
|
||||
m_Status = EIsoStatus::IDLE_1;
|
||||
}
|
||||
g_pReadInfo = nullptr;
|
||||
return EIsoStatus::OK_2;
|
||||
}
|
||||
return EIsoStatus::ERROR_b;
|
||||
}
|
||||
|
||||
/*!
|
||||
* As soon as possible, stop ongoing reads and free buffers.
|
||||
*/
|
||||
void CISOCDFile::Close() {
|
||||
// cancel ongoing reading in the driver
|
||||
get_driver()->CancelRead(&m_Descriptor);
|
||||
|
||||
ASSERT(m_FileKind != CBaseFile::Kind::LZO_COMPRESSED); // unsupported in pc
|
||||
if (this == g_pReadInfo) {
|
||||
g_pReadInfo = nullptr;
|
||||
}
|
||||
|
||||
if (m_Buffer.m_eBufferType != CBuffer::BufferType::EBT_FREE) {
|
||||
TerminateBuffer();
|
||||
}
|
||||
|
||||
// reset self
|
||||
*this = CISOCDFile();
|
||||
}
|
||||
|
||||
/*!
|
||||
* In the case where we have run out of page memory, take pages that we have loaded, but aren't yet
|
||||
* using, and discard them back to the pool.
|
||||
*/
|
||||
int CISOCDFile::RecoverPages(int num_pages_desired) {
|
||||
// we only allow ourselves to recover pages for VAG streaming.
|
||||
if (!m_Buffer.m_pIsoCmd || m_Buffer.m_pIsoCmd->callback != ProcessVAGData) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// lock semaphore for processing
|
||||
ASSERT(m_ProcessDataSemaphore != -1);
|
||||
WaitSema(m_ProcessDataSemaphore);
|
||||
auto* plist = m_Buffer.m_pPageList;
|
||||
int num_removed = 0;
|
||||
if (plist) {
|
||||
int pages_to_ask_for = plist->m_nNumUnsteppedPages;
|
||||
if (pages_to_ask_for > 1) {
|
||||
// don't ask for more than user asked for
|
||||
if (pages_to_ask_for > num_pages_desired) {
|
||||
pages_to_ask_for = num_pages_desired;
|
||||
}
|
||||
num_removed = plist->RemoveActivePages(pages_to_ask_for);
|
||||
if (num_removed) {
|
||||
m_nSector -= 0x10 * num_removed;
|
||||
m_nLoaded -= 0x8000 * num_removed;
|
||||
// suspend intr
|
||||
ASSERT(m_Buffer.m_nDataLength >= 0);
|
||||
if (m_Buffer.m_nDataLength > 0) {
|
||||
m_Buffer.AddData(-num_removed * 0x8000);
|
||||
}
|
||||
// resume intr
|
||||
ASSERT(m_nLoaded >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move our reading pointer back.
|
||||
m_PageOffset -= num_removed;
|
||||
// unlock processing
|
||||
SignalSema(m_ProcessDataSemaphore);
|
||||
return num_removed;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the next sector to read.
|
||||
*/
|
||||
int CISOCDFile::GetSector() {
|
||||
return m_nSector;
|
||||
}
|
||||
|
||||
// WaitForLZOPages - not ported.
|
||||
|
||||
void CISOCDFile::ReadPages(int sector,
|
||||
jak3::CPage* destination_page,
|
||||
int num_pages,
|
||||
char* done_flag_ptr,
|
||||
bool sleep_until_done) {
|
||||
ASSERT(destination_page);
|
||||
ASSERT(num_pages >= 1);
|
||||
|
||||
constexpr int kMaxPages = 24;
|
||||
// I _think_ this might have been an alloca...
|
||||
BlockParams params_array[kMaxPages];
|
||||
ASSERT(num_pages <= kMaxPages);
|
||||
|
||||
// iVar2 = -((num_pages + -1) * 0x14 + 0x1bU & 0xfffffff8);
|
||||
// params = (BlockParams*)((int)local_30 + iVar2);
|
||||
BlockParams* params = params_array;
|
||||
|
||||
// increment our progress in the file - the distributed update of the various progress integers
|
||||
// is somewhat confusing, especially since failures here don't seem to reset it properly.
|
||||
m_PageOffset += num_pages;
|
||||
|
||||
// Set up block params for each page to read
|
||||
CPage* page = destination_page;
|
||||
int pg_remaining = num_pages;
|
||||
do {
|
||||
page->input_state = CPage::State::READING;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~page->mask);
|
||||
|
||||
params->destination = page->m_pPageMemStart;
|
||||
params->num_sectors = 0x10;
|
||||
params->sector_num = sector;
|
||||
params->file_def = m_FileDef;
|
||||
params->page = page;
|
||||
|
||||
params->flag = nullptr;
|
||||
if (pg_remaining == 1) { // on the last page...
|
||||
params->flag = (char*)0xffffffff; // flag this
|
||||
if (!sleep_until_done) {
|
||||
params->flag = done_flag_ptr;
|
||||
}
|
||||
}
|
||||
ovrld_log(LogCategory::PAGING, "[paging] building block for driver: 0x{:x}, {}, {}, flag: {}",
|
||||
(u64)params->destination, params->sector_num, params->file_def->name.data,
|
||||
(u64)params->flag);
|
||||
page = page->m_pNextPage;
|
||||
pg_remaining = pg_remaining + -1;
|
||||
sector += 0x10;
|
||||
params++;
|
||||
} while (page && pg_remaining > 0);
|
||||
|
||||
if (pg_remaining == 0) {
|
||||
// we set up block params for all the requested pages.
|
||||
int pages_actually_read = -1;
|
||||
|
||||
ovrld_log(LogCategory::PAGING, "[paging] Submitting {} blocks to driver.\n", num_pages);
|
||||
int status = get_driver()->ReadMultiple(&m_Descriptor, &pages_actually_read, params_array,
|
||||
num_pages, true);
|
||||
if ((status == 0) && pages_actually_read == num_pages) {
|
||||
if (!sleep_until_done) {
|
||||
return;
|
||||
}
|
||||
// put us to sleep...
|
||||
ovrld_log(LogCategory::PAGING, "[paging] Sleeping, waiting for driver to read {}",
|
||||
pages_actually_read);
|
||||
SleepThread();
|
||||
ovrld_log(LogCategory::PAGING, "[paging] Driver woke us up!");
|
||||
return;
|
||||
}
|
||||
ASSERT_NOT_REACHED(); // unexpected failure to issue read
|
||||
} else {
|
||||
// ran out of pages....
|
||||
ASSERT_NOT_REACHED(); // this was just a warning.. but I think it shouldn't happen.
|
||||
if (destination_page) {
|
||||
do {
|
||||
destination_page->input_state = CPage::State::ACTIVE;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag,
|
||||
~destination_page->mask);
|
||||
destination_page = destination_page->m_pNextPage;
|
||||
} while (destination_page);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
// only get here is we failed.... set the done flag.
|
||||
if (sleep_until_done == 0 && done_flag_ptr) {
|
||||
*done_flag_ptr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Callback run by the dvd driver when a page is finished reading.
|
||||
*/
|
||||
void CISOCDFile::ReadPagesCallback(jak3::Block* block, int error) {
|
||||
if (error == 0) {
|
||||
ASSERT(block->params.page->m_nAllocState == 1);
|
||||
// flag page as done
|
||||
block->params.page->input_state = CPage::State::READ_DONE;
|
||||
// set flag
|
||||
SetEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, block->params.page->mask);
|
||||
|
||||
// if this block has a notification, process it:
|
||||
if (block->params.flag == (char*)0xffffffff) { // indicates we should wake caller
|
||||
// we assume the caller is the iso thread
|
||||
WakeupThread(g_nISOThreadID);
|
||||
} else if (block->params.flag) {
|
||||
// in this case, it's a pointer.
|
||||
*block->params.flag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DecompressBlock - not ported
|
||||
|
||||
/*!
|
||||
* Initialize the file system - find all files and set up their definitions.
|
||||
*/
|
||||
int CISOCDFileSystem::Init() {
|
||||
// drive ready event flag - not ported
|
||||
// get disc type - not ported
|
||||
|
||||
// this is mostly not needed... except for some vag pausing nonsense :(
|
||||
get_driver()->SetDriverCallback([&](int a) { DvdDriverCallback(a); });
|
||||
|
||||
// skipped a bunch of lzo pages crap
|
||||
ReadDirectory();
|
||||
LoadMusicTweaks();
|
||||
// skip load Disc ID
|
||||
|
||||
// should already be done in the constructor...
|
||||
for (auto& f : g_CISOCDFiles) {
|
||||
f.m_FileDef = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PollDrive - not ported
|
||||
|
||||
/*!
|
||||
* Find a file definition by name.
|
||||
*/
|
||||
ISOFileDef* CISOCDFileSystem::Find(const char* name) {
|
||||
ISOName iname;
|
||||
file_util::MakeISOName(iname.data, name);
|
||||
return FindIN(&iname);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Find a file definition by its "ISO Name", a 12-byte name.
|
||||
*/
|
||||
ISOFileDef* CISOCDFileSystem::FindIN(const jak3::ISOName* name) {
|
||||
for (auto& def : g_FileDefs) {
|
||||
if (def.name == *name) {
|
||||
return &def;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the length of a file, in bytes.
|
||||
*/
|
||||
int CISOCDFileSystem::GetLength(const jak3::ISOFileDef* file) {
|
||||
return file->length;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Open a file for reading.
|
||||
*/
|
||||
CBaseFile* CISOCDFileSystem::Open(const jak3::ISOFileDef* file_def,
|
||||
int sector_offset,
|
||||
int file_kind) {
|
||||
auto* file = AllocateFile(file_def);
|
||||
ASSERT(file);
|
||||
|
||||
// file kind must be known to be non-compressed (1). (TODO: remove arg)
|
||||
ASSERT(file_kind == 1);
|
||||
|
||||
file->m_FileKind = CBaseFile::Kind::NORMAL;
|
||||
file->m_nLength = file_def->length;
|
||||
file->m_LengthPages = (0x7fff + file->m_nLength) >> 0xf;
|
||||
if (file->m_LengthPages < 1) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
file->m_nLoaded = 0;
|
||||
file->m_PageOffset = 0;
|
||||
|
||||
// in the original game, this was the sector of the start of the file
|
||||
// we just make it 0, since we don't build up a whole iso file.
|
||||
file->m_nSector = 0;
|
||||
if (sector_offset != -1) {
|
||||
file->m_nSector += sector_offset;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Open a WAD file for reading, given the offset of the data to read, in pages.
|
||||
*/
|
||||
CBaseFile* CISOCDFileSystem::OpenWAD(const jak3::ISOFileDef* file_def, int page_offset) {
|
||||
auto* file = AllocateFile(file_def);
|
||||
ASSERT(file);
|
||||
|
||||
file->m_LengthPages = 1; // this is not really true..
|
||||
file->m_FileKind = CBaseFile::Kind::NORMAL;
|
||||
file->m_PageOffset = 0;
|
||||
file->m_nSector = page_offset * 0x10;
|
||||
file->m_nLoaded = 0;
|
||||
file->m_nLength = 0;
|
||||
return file;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Locate the entry for a VAG file in the VAG directory.
|
||||
*/
|
||||
VagDirEntry* CISOCDFileSystem::FindVAGFile(const char* name) {
|
||||
u32 packed_name[2];
|
||||
PackVAGFileName(packed_name, name);
|
||||
for (int i = 0; i < g_VagDir.num_entries; i++) {
|
||||
auto& entry = g_VagDir.entries[i];
|
||||
if (packed_name[0] == entry.words[0] && packed_name[1] == (entry.words[1] & 0x3ff)) {
|
||||
return &entry;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get a CISOCDFile* for a newly opened file.
|
||||
*/
|
||||
CISOCDFile* CISOCDFileSystem::AllocateFile(const jak3::ISOFileDef* file_def) {
|
||||
for (int i = 0; i < kMaxOpenFiles; i++) {
|
||||
auto* file = &g_CISOCDFiles[i];
|
||||
if (!file->m_FileDef) {
|
||||
*file = CISOCDFile(file_def, m_Sema[i]);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Callback from the DVD driver itself into the filesystem. This was originally used for notifying
|
||||
* when the tray is opened or closed.
|
||||
*/
|
||||
void CISOCDFileSystem::DvdDriverCallback(int) {
|
||||
// the only callbacks that do anything are tray open/close, which we don't care about
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// CheckDiscID - not ported
|
||||
// LoadDiscID - not ported
|
||||
// ReadSectorsNow - not ported
|
||||
|
||||
/*!
|
||||
* Find all the files on the disc and set up their information. This is modified for the PC port to
|
||||
* just search for files in the appropriate out folder.
|
||||
*/
|
||||
void CISOCDFileSystem::ReadDirectory() {
|
||||
for (const auto& f :
|
||||
fs::directory_iterator(file_util::get_jak_project_dir() / "out" / "jak3" / "iso")) {
|
||||
if (f.is_regular_file()) {
|
||||
auto& e = g_FileDefs.emplace_back();
|
||||
std::string file_name = f.path().filename().string();
|
||||
ASSERT(file_name.length() < 16); // should be 8.3.
|
||||
MakeISOName(&e.name, file_name.c_str());
|
||||
e.full_path =
|
||||
fmt::format("{}/out/jak3/iso/{}", file_util::get_jak_project_dir().string(), file_name);
|
||||
auto* fp = file_util::open_file(e.full_path, "rb");
|
||||
ASSERT(fp);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
e.length = ftell(fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Load the "Music Tweaks" file, which contains a volume setting per music track.
|
||||
*/
|
||||
void CISOCDFileSystem::LoadMusicTweaks() {
|
||||
ISOName tweakname;
|
||||
MakeISOName(&tweakname, "TWEAKVAL.MUS");
|
||||
auto file = g_ISOCDFileSystem->FindIN(&tweakname);
|
||||
if (file) {
|
||||
auto fp = file_util::open_file(file->full_path, "rb");
|
||||
ASSERT(fp);
|
||||
ASSERT(file->length <= sizeof(gMusicTweakInfo));
|
||||
auto ret = fread(&gMusicTweakInfo, file->length, 1, fp);
|
||||
ASSERT(ret == 1);
|
||||
fclose(fp);
|
||||
} else {
|
||||
lg::warn("Failed to open music tweak file.");
|
||||
gMusicTweakInfo.TweakCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Crc32 - not ported
|
||||
// ReadU32 - not ported
|
||||
|
||||
} // namespace jak3
|
58
game/overlord/jak3/iso_cd.h
Normal file
58
game/overlord/jak3/iso_cd.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "game/overlord/jak3/basefile.h"
|
||||
#include "game/overlord/jak3/basefilesystem.h"
|
||||
#include "game/overlord/jak3/dvd_driver.h"
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_iso_cd();
|
||||
|
||||
CBaseFileSystem* get_file_system();
|
||||
|
||||
extern VagDir g_VagDir;
|
||||
extern MusicTweaks gMusicTweakInfo;
|
||||
|
||||
struct CISOCDFile : public CBaseFile {
|
||||
CISOCDFile();
|
||||
CISOCDFile(const ISOFileDef* filedef, s32 process_data_semaphore);
|
||||
int m_nLoaded = 0; // bytes loaded so far
|
||||
int m_nSector = 0; // next sector to read.
|
||||
int m_nLength = 0; // bytes that we want to load. 0 for the whole file
|
||||
CDescriptor m_Descriptor;
|
||||
|
||||
EIsoStatus BeginRead() override;
|
||||
|
||||
void ReadPages(int sector,
|
||||
CPage* destination_page,
|
||||
int num_pages,
|
||||
char* done_flag_ptr,
|
||||
bool flag);
|
||||
EIsoStatus SyncRead() override;
|
||||
void Close() override;
|
||||
int RecoverPages(int num_pages) override;
|
||||
int GetSector() override;
|
||||
|
||||
void ReadPagesCallback(Block* block, int error);
|
||||
};
|
||||
|
||||
struct CISOCDFileSystem : public CBaseFileSystem {
|
||||
CISOCDFileSystem() = default;
|
||||
int Init() override;
|
||||
ISOFileDef* Find(const char* name) override;
|
||||
ISOFileDef* FindIN(const ISOName* name) override;
|
||||
int GetLength(const ISOFileDef* file) override;
|
||||
CBaseFile* Open(const ISOFileDef* file_def, int sector_offset, int file_kind) override;
|
||||
CBaseFile* OpenWAD(const ISOFileDef* file_def, int page_offset) override;
|
||||
VagDirEntry* FindVAGFile(const char* name) override;
|
||||
|
||||
void DvdDriverCallback(int a);
|
||||
void ReadDirectory();
|
||||
void LoadMusicTweaks();
|
||||
CISOCDFile* AllocateFile(const ISOFileDef* file);
|
||||
|
||||
// int m_drive_ready_event_flag = -1;
|
||||
};
|
||||
} // namespace jak3
|
732
game/overlord/jak3/iso_queue.cpp
Normal file
732
game/overlord/jak3/iso_queue.cpp
Normal file
@ -0,0 +1,732 @@
|
||||
#include "iso_queue.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/basefile.h"
|
||||
#include "game/overlord/jak3/dma.h"
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
#include "game/overlord/jak3/pagemanager.h"
|
||||
#include "game/overlord/jak3/spustreams.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
namespace jak3 {
|
||||
s32 g_nPriQueueSema = 0;
|
||||
s32 g_VagCmdSema = 0;
|
||||
u32 g_auStrmSRAM[6];
|
||||
u32 g_auTrapSRAM[6];
|
||||
PriStackEntry gPriStack[2];
|
||||
extern u32 time_of_last_unknown_rate_drive_op;
|
||||
u32 g_cmds_with_speed_total = 0;
|
||||
bool unk_time_mode_flag = false;
|
||||
ISO_Hdr* g_selected_cmd = nullptr;
|
||||
bool unk_time_mflag = 0;
|
||||
s32 unk_sector = 0;
|
||||
u32 vag_cmd_cnt = 0;
|
||||
u32 vag_cmd_used = 0;
|
||||
u32 max_vag_cmd_cnt = 0;
|
||||
ISO_VAGCommand vag_cmds[16];
|
||||
|
||||
static constexpr s32 LOOP_END = 1;
|
||||
static constexpr s32 LOOP_REPEAT = 2;
|
||||
static constexpr s32 LOOP_START = 4;
|
||||
|
||||
// Empty ADPCM block with loop flags
|
||||
|
||||
// clang-format off
|
||||
u8 VAG_SilentLoop[0x60] = {
|
||||
0x0, LOOP_START | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, LOOP_END | LOOP_REPEAT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void jak3_overlord_init_globals_iso_queue() {
|
||||
g_nPriQueueSema = 0;
|
||||
g_VagCmdSema = 0;
|
||||
for (auto& x : gPriStack) {
|
||||
x = {};
|
||||
}
|
||||
g_cmds_with_speed_total = 0;
|
||||
unk_time_mode_flag = false;
|
||||
g_selected_cmd = nullptr;
|
||||
unk_time_mflag = 0;
|
||||
unk_sector = 0;
|
||||
vag_cmd_cnt = 0;
|
||||
vag_cmd_used = 0;
|
||||
for (auto& x : vag_cmds) {
|
||||
x = {};
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Added function to check if there is a pending DGO load command.
|
||||
* On PC, DOG loads are really the only loading time that users see. If there is a pending
|
||||
* DGO load, we can modify logic to take advantage of PCs being dramatically faster than PS2 and
|
||||
* get much better load times.
|
||||
*/
|
||||
bool DgoCmdWaiting() {
|
||||
for (auto& level : gPriStack) {
|
||||
for (int i = 0; i < level.count; i++) {
|
||||
auto* cmd = level.cmds[i];
|
||||
|
||||
if (cmd && cmd->msg_type == ISO_Hdr::MsgType::DGO_LOAD) {
|
||||
if (cmd->m_pBaseFile) {
|
||||
auto* file = cmd->m_pBaseFile;
|
||||
if (file->m_Buffer.m_nDataLength) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InitBuffers() {
|
||||
SemaParam sema_param;
|
||||
sema_param.max_count = 1;
|
||||
sema_param.init_count = 1;
|
||||
sema_param.attr = 0;
|
||||
sema_param.option = 0;
|
||||
|
||||
g_nPriQueueSema = CreateSema(&sema_param);
|
||||
ASSERT(g_nPriQueueSema >= 0);
|
||||
get_page_manager()->Initialize();
|
||||
|
||||
g_auStrmSRAM[0] = 0x5040;
|
||||
g_auTrapSRAM[0] = 0x9040;
|
||||
snd_SRAMMarkUsed(0x5040, 0x4040);
|
||||
g_auStrmSRAM[1] = 0x9080;
|
||||
g_auTrapSRAM[1] = 0xd080;
|
||||
snd_SRAMMarkUsed(0x9080, 0x4040);
|
||||
g_auStrmSRAM[2] = 0xd0c0;
|
||||
g_auTrapSRAM[2] = 0x110c0;
|
||||
snd_SRAMMarkUsed(0xd0c0, 0x4040);
|
||||
g_auStrmSRAM[3] = 0x11100;
|
||||
g_auTrapSRAM[3] = 0x15100;
|
||||
snd_SRAMMarkUsed(0x11100, 0x4040);
|
||||
g_auStrmSRAM[4] = 0x15140; // 86384 - 48
|
||||
g_auTrapSRAM[4] = 0x19140;
|
||||
snd_SRAMMarkUsed(0x15140, 0x4040);
|
||||
g_auStrmSRAM[5] = 0x019180;
|
||||
g_auTrapSRAM[5] = 0x001d180;
|
||||
snd_SRAMMarkUsed(0x19180, 0x4040);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (!DMA_SendToSPUAndSync(VAG_SilentLoop, 0x30, g_auTrapSRAM[i], nullptr, nullptr)) {
|
||||
DelayThread(1000);
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sema_param.max_count = 1;
|
||||
sema_param.attr = 1;
|
||||
sema_param.init_count = 1;
|
||||
sema_param.option = 0;
|
||||
g_VagCmdSema = CreateSema(&sema_param);
|
||||
ASSERT(g_VagCmdSema >= 0);
|
||||
}
|
||||
|
||||
int QueueMessage(ISO_Hdr* msg, int pri) {
|
||||
msg->status = EIsoStatus::OK_2;
|
||||
msg->priority = pri;
|
||||
WaitSema(g_nPriQueueSema);
|
||||
int queue_idx = (pri == 5) ? 1 : 0;
|
||||
bool ok = gPriStack[queue_idx].count != 8;
|
||||
if (ok) {
|
||||
gPriStack[queue_idx].cmds[gPriStack[queue_idx].count] = msg;
|
||||
gPriStack[queue_idx].count++;
|
||||
SignalSema(g_nPriQueueSema);
|
||||
} else {
|
||||
msg->status = EIsoStatus::FAILED_TO_QUEUE_4;
|
||||
SignalSema(g_nPriQueueSema);
|
||||
ReturnMessage(msg);
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int UnqueueMessage(ISO_Hdr* msg) {
|
||||
WaitSema(g_nPriQueueSema);
|
||||
int iVar5 = 0;
|
||||
PriStackEntry* stack = gPriStack;
|
||||
do {
|
||||
int iVar4 = 0;
|
||||
ISO_Hdr** cmd = stack->cmds;
|
||||
if (0 < stack->count) {
|
||||
do {
|
||||
if (*cmd == msg)
|
||||
break;
|
||||
iVar4 = iVar4 + 1;
|
||||
cmd++;
|
||||
} while (iVar4 < stack->count);
|
||||
}
|
||||
iVar5 = iVar5 + 1;
|
||||
if (iVar4 < stack->count) {
|
||||
stack->count = stack->count + -1;
|
||||
if (iVar4 < stack->count) {
|
||||
ISO_Hdr** ppIVar3 = stack->cmds + iVar4;
|
||||
do {
|
||||
iVar4 = iVar4 + 1;
|
||||
*ppIVar3 = ppIVar3[1];
|
||||
ppIVar3 = ppIVar3 + 1;
|
||||
} while (iVar4 < stack->count);
|
||||
}
|
||||
return SignalSema(g_nPriQueueSema);
|
||||
}
|
||||
stack = stack + 1;
|
||||
if (1 < iVar5) {
|
||||
return SignalSema(g_nPriQueueSema);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Select which command to read for next
|
||||
* This function considers things like seeking time, reading rates of streamed files,
|
||||
* and buffer sizing. To be entirely honest, I don't understand it almost at all, and it's not
|
||||
* clear that it works as expected. It seems to work good enough, and no commands get entirely
|
||||
* starved of data while there are multiple streams.
|
||||
*/
|
||||
ISO_Hdr* GetMessage() {
|
||||
// bool been_a_while;
|
||||
// bool bVar2;
|
||||
// int now;
|
||||
// int iVar3;
|
||||
// int iVar4;
|
||||
// CISOCDFile *file;
|
||||
// CISOCDFile *pCVar5;
|
||||
// int iVar6;
|
||||
// CPageList *plist;
|
||||
// uint uVar7;
|
||||
// int iVar8;
|
||||
// int iVar9;
|
||||
// PriStack *local_t2_216;
|
||||
// PriStack *pri_level;
|
||||
// CISOCDFile *tfile4;
|
||||
// code *pcVar10;
|
||||
// CISOCDFile *tfile2;
|
||||
// CISOCDFile *tfile3;
|
||||
// uint uVar11;
|
||||
// ISO_VAGCommand *cmd;
|
||||
// int idx_on_level;
|
||||
// ISO_VAGCommand **ppIVar12;
|
||||
// int iVar13;
|
||||
// CBaseFile *tfile;
|
||||
// int cmds_total;
|
||||
// int iVar14;
|
||||
// uint uVar15;
|
||||
ISO_Hdr* cmds_array[16];
|
||||
u32 read_rates_array[16];
|
||||
int num_pages_array[16];
|
||||
int unstepped_pages_array[16];
|
||||
int remaining_pages_array[16];
|
||||
// uint its_been_a_while;
|
||||
// int pages_total;
|
||||
// int read_rate_total;
|
||||
// int min_nospeed_pages_total;
|
||||
// int max_pages_total;
|
||||
// int min_speed_pages_total;
|
||||
// ISO_VAGCommand *selected_cmd;
|
||||
// int cmds_with_speed_total;
|
||||
// uint cmd2_read_rate;
|
||||
// int selected_cmd_rem_sectors;
|
||||
// int pri_level_idx;
|
||||
|
||||
// simple logic to select which command to use next.
|
||||
|
||||
u32 now = GetSystemTimeLow();
|
||||
|
||||
bool been_a_while = false;
|
||||
if (unk_time_mode_flag == 0) {
|
||||
been_a_while = 0x384000 < (now - time_of_last_unknown_rate_drive_op);
|
||||
} else {
|
||||
unk_time_mode_flag = 0;
|
||||
time_of_last_unknown_rate_drive_op = now;
|
||||
}
|
||||
|
||||
s32 cmds_total = 0;
|
||||
get_page_manager()->GarbageCollect();
|
||||
s32 pages_total = 0;
|
||||
s32 read_rate_total = 0;
|
||||
s32 min_nospeed_pages_total = 0;
|
||||
s32 max_pages_total = 0;
|
||||
s32 min_speed_pages_total = 0;
|
||||
LAB_000080e4:
|
||||
s32 iVar9 = g_cmds_with_speed_total * 400 + 0x2ee;
|
||||
s32 iVar13 = 0x7fffffff;
|
||||
ISO_Hdr* selected_cmd = nullptr;
|
||||
s32 cmds_with_speed_total = 0;
|
||||
s32 cmd2_read_rate = 0;
|
||||
s32 selected_cmd_rem_sectors = -1;
|
||||
s32 pri_level_idx = 1;
|
||||
PriStackEntry* pri_level = gPriStack + 1;
|
||||
s32 iVar8 = iVar13;
|
||||
|
||||
// loop over priority levels
|
||||
do {
|
||||
s32 idx_on_level = pri_level->count + -1;
|
||||
|
||||
// if any exist on this level
|
||||
if (-1 < idx_on_level) {
|
||||
ISO_Hdr** ppIVar12 = pri_level->cmds + idx_on_level;
|
||||
// iVar14 = cmds_total << 2;
|
||||
// loop over commands on this level
|
||||
do {
|
||||
ISO_Hdr* cmd = *ppIVar12;
|
||||
CBaseFile* file = nullptr;
|
||||
// iVar4 = iVar14;
|
||||
|
||||
// basic check if this command is even valid:
|
||||
if (cmd && (file = cmd->m_pBaseFile, file) && cmd->status == EIsoStatus::OK_2 &&
|
||||
cmd->active_a != 0) {
|
||||
u32 read_rate = file->m_ReadRate;
|
||||
read_rate_total = read_rate_total + read_rate; // maybe this is an inverse rate...
|
||||
|
||||
// build up arrays of info for each command
|
||||
read_rates_array[cmds_total] = read_rate;
|
||||
cmds_array[cmds_total] = cmd;
|
||||
|
||||
if ((int)read_rate < 1) {
|
||||
min_nospeed_pages_total = min_nospeed_pages_total + file->m_Buffer.m_nMinNumPages;
|
||||
max_pages_total = max_pages_total + file->m_Buffer.m_nMaxNumPages;
|
||||
} else {
|
||||
min_speed_pages_total = min_speed_pages_total + file->m_Buffer.m_nMinNumPages;
|
||||
cmds_with_speed_total = cmds_with_speed_total + 1;
|
||||
}
|
||||
|
||||
CPageList* plist = file->m_Buffer.m_pPageList;
|
||||
|
||||
s32 npages = 0;
|
||||
if (plist != (CPageList*)0x0) {
|
||||
npages = plist->m_nNumPages;
|
||||
}
|
||||
pages_total = pages_total + npages;
|
||||
num_pages_array[cmds_total] = npages;
|
||||
|
||||
s32 n_untepped_pages = 0;
|
||||
if (plist != (CPageList*)0x0) {
|
||||
n_untepped_pages = plist->m_nNumUnsteppedPages;
|
||||
}
|
||||
unstepped_pages_array[cmds_total] = n_untepped_pages;
|
||||
|
||||
s32 n_remaining_pages = 4;
|
||||
if (cmd->callback != RunDGOStateMachine) {
|
||||
n_remaining_pages = n_untepped_pages + file->m_LengthPages - file->m_PageOffset;
|
||||
// lg::warn("remaining pages is {} = {} + {} - {}", n_remaining_pages, n_untepped_pages,
|
||||
// file->m_LengthPages, file->m_PageOffset);
|
||||
}
|
||||
remaining_pages_array[cmds_total] = n_remaining_pages;
|
||||
|
||||
if (remaining_pages_array[cmds_total] < 1) {
|
||||
remaining_pages_array[cmds_total] = 1;
|
||||
}
|
||||
|
||||
// careful, this increments in a weird spot.
|
||||
// I use cmds_total - 1 below...
|
||||
int old_cmds_total = cmds_total;
|
||||
cmds_total = cmds_total + 1;
|
||||
|
||||
// iVar4 = iVar14 + 4;
|
||||
|
||||
// next, we'll determine a desired page cutoff and discard commands where
|
||||
// we have enough pages.
|
||||
if (read_rate && plist) {
|
||||
// 3/4 * mNumPages, this is if our allocated buffer is 75% full.
|
||||
s32 desired_pages = (int)(file->m_nNumPages * 0x30) >> 6;
|
||||
|
||||
// but, we want at least min pages
|
||||
if (desired_pages < file->m_Buffer.m_nMinNumPages) {
|
||||
desired_pages = file->m_Buffer.m_nMinNumPages;
|
||||
}
|
||||
|
||||
// and we want at least 2
|
||||
if (desired_pages < 2) {
|
||||
desired_pages = 2;
|
||||
}
|
||||
|
||||
// but, we never want more pages than we plan to eventually read.
|
||||
if (remaining_pages_array[old_cmds_total] < desired_pages) {
|
||||
desired_pages = remaining_pages_array[old_cmds_total];
|
||||
}
|
||||
|
||||
// if we have that many, we can just forget the command - no point in filling it now.
|
||||
if (desired_pages <= unstepped_pages_array[old_cmds_total])
|
||||
goto LAB_00008420;
|
||||
}
|
||||
|
||||
s32 iVar14 = iVar9;
|
||||
if ((0 < (int)read_rate) &&
|
||||
(iVar14 = (int)(file->m_Buffer.m_nDataLength * 1000) / (int)read_rate,
|
||||
read_rate == 0)) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
s32 pri = cmd->priority;
|
||||
|
||||
// really not sure what this is...
|
||||
if (been_a_while) {
|
||||
if ((read_rate == 0) || (iVar14 < 0x2ee)) {
|
||||
been_a_while = false;
|
||||
goto LAB_000080e4;
|
||||
}
|
||||
|
||||
// if this isn't the command we said last time.
|
||||
if (g_selected_cmd != cmd) {
|
||||
s32 current_sector = file->GetSector();
|
||||
current_sector = current_sector - unk_sector;
|
||||
bool sector_ok = false;
|
||||
if (current_sector < 0) {
|
||||
current_sector = -current_sector;
|
||||
if (unk_time_mflag != 0) {
|
||||
LAB_000083ac:
|
||||
current_sector = current_sector + 10000000;
|
||||
}
|
||||
sector_ok = current_sector < iVar13;
|
||||
} else {
|
||||
sector_ok = current_sector < iVar13;
|
||||
if ((0 < current_sector) && (unk_time_mflag == 0))
|
||||
goto LAB_000083ac;
|
||||
}
|
||||
if (sector_ok) {
|
||||
iVar13 = current_sector;
|
||||
iVar8 = iVar14;
|
||||
selected_cmd = cmd;
|
||||
cmd2_read_rate = read_rate;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal selection logic???
|
||||
if ((((iVar14 == iVar8) && (selected_cmd_rem_sectors < pri)) || (iVar14 < iVar8)) &&
|
||||
(iVar14 <= iVar9)) {
|
||||
iVar8 = iVar14;
|
||||
selected_cmd = cmd;
|
||||
cmd2_read_rate = read_rate;
|
||||
selected_cmd_rem_sectors = pri;
|
||||
}
|
||||
}
|
||||
}
|
||||
LAB_00008420:
|
||||
idx_on_level = idx_on_level + -1;
|
||||
/* WARNING: ptrarith problems */
|
||||
ppIVar12 = ppIVar12 + -1;
|
||||
// iVar14 = iVar4;
|
||||
} while (-1 < idx_on_level);
|
||||
}
|
||||
pri_level--;
|
||||
pri_level_idx = pri_level_idx + -1;
|
||||
} while (-1 < pri_level_idx);
|
||||
|
||||
if (selected_cmd) {
|
||||
if (cmd2_read_rate == 0) {
|
||||
unk_time_mode_flag = 1;
|
||||
time_of_last_unknown_rate_drive_op = now;
|
||||
}
|
||||
iVar13 = unk_sector;
|
||||
if (selected_cmd->m_pBaseFile) {
|
||||
iVar13 = selected_cmd->m_pBaseFile->GetSector();
|
||||
}
|
||||
if (unk_sector < iVar13) {
|
||||
unk_time_mflag = 1;
|
||||
unk_sector = iVar13;
|
||||
} else {
|
||||
been_a_while = iVar13 != unk_sector;
|
||||
unk_sector = iVar13;
|
||||
if (been_a_while) {
|
||||
unk_time_mflag = 0;
|
||||
unk_sector = iVar13;
|
||||
}
|
||||
}
|
||||
}
|
||||
min_speed_pages_total = min_speed_pages_total + min_nospeed_pages_total;
|
||||
g_cmds_with_speed_total = cmds_with_speed_total;
|
||||
g_selected_cmd = selected_cmd;
|
||||
pages_total = get_page_manager()->m_CCache.m_nNumFreePages + pages_total;
|
||||
if ((0 < min_speed_pages_total) && (0 < pages_total)) {
|
||||
if (min_speed_pages_total == 0) {
|
||||
// trap(0x1c00);
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
min_speed_pages_total = (min_nospeed_pages_total * pages_total) / min_speed_pages_total;
|
||||
if (min_speed_pages_total < min_nospeed_pages_total) {
|
||||
min_speed_pages_total = min_nospeed_pages_total;
|
||||
}
|
||||
if (max_pages_total < min_speed_pages_total) {
|
||||
min_speed_pages_total = max_pages_total;
|
||||
}
|
||||
pages_total = pages_total - min_speed_pages_total;
|
||||
iVar9 = 0;
|
||||
iVar13 = min_speed_pages_total;
|
||||
iVar8 = pages_total;
|
||||
if (0 < cmds_total) {
|
||||
// iVar14 = 0;
|
||||
iVar13 = min_speed_pages_total;
|
||||
iVar8 = pages_total;
|
||||
do {
|
||||
CBaseFile* tfile = cmds_array[iVar9]->m_pBaseFile;
|
||||
if (read_rates_array[iVar9] < 1) {
|
||||
s32 uVar11 = (tfile->m_Buffer).m_nMaxNumPages;
|
||||
if (max_pages_total == 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
s32 uVar15 = (tfile->m_Buffer).m_nMinNumPages;
|
||||
s32 uVar7 = (int)(uVar11 * min_speed_pages_total) / max_pages_total;
|
||||
if ((int)uVar7 < (int)uVar15) {
|
||||
uVar7 = uVar15;
|
||||
}
|
||||
|
||||
tfile->m_nNumPages = uVar7;
|
||||
if ((int)uVar11 < (int)uVar7) {
|
||||
uVar7 = uVar11;
|
||||
}
|
||||
tfile->m_nNumPages = uVar7;
|
||||
if (remaining_pages_array[iVar9] < (int)uVar7) {
|
||||
uVar7 = remaining_pages_array[iVar9];
|
||||
}
|
||||
tfile->m_nNumPages = uVar7;
|
||||
// lg::warn("num pages mod {}", uVar7);
|
||||
iVar13 = iVar13 - uVar7;
|
||||
} else {
|
||||
s32 uVar11 = (tfile->m_Buffer).m_nMinNumPages;
|
||||
s32 uVar7 = (tfile->m_Buffer).m_nMaxNumPages;
|
||||
// lg::warn("taking else case: {} {} {}", uVar11, uVar7, tfile->m_nNumPages);
|
||||
|
||||
s32 uVar15 = -1;
|
||||
if (read_rate_total == 0) {
|
||||
LAB_000085e4:
|
||||
// lg::warn("going to min! (rrt is {})", read_rate_total);
|
||||
uVar15 = uVar11;
|
||||
} else {
|
||||
if (read_rate_total == 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
uVar15 = (read_rates_array[iVar9] * pages_total) / read_rate_total;
|
||||
// lg::warn("read rate math is {}", uVar15);
|
||||
if ((int)uVar15 < (int)uVar11)
|
||||
goto LAB_000085e4;
|
||||
}
|
||||
|
||||
// lg::warn("vals {} {}", uVar7, uVar15);
|
||||
if ((int)uVar7 < (int)uVar15) {
|
||||
uVar15 = uVar7;
|
||||
}
|
||||
// lg::warn("vals 2 {} {}", remaining_pages_array[iVar9], uVar15);
|
||||
if (remaining_pages_array[iVar9] < (int)uVar15) {
|
||||
uVar15 = remaining_pages_array[iVar9];
|
||||
}
|
||||
iVar8 = iVar8 - uVar15;
|
||||
tfile->m_nNumPages = uVar15;
|
||||
// lg::warn("num pages mod 2 {}", uVar15);
|
||||
}
|
||||
iVar9 = iVar9 + 1;
|
||||
// iVar14 = iVar9 * 4;
|
||||
} while (iVar9 < cmds_total);
|
||||
}
|
||||
while (0 < iVar13) {
|
||||
iVar9 = 0;
|
||||
s32 iVar14 = iVar13;
|
||||
if (0 < cmds_total) {
|
||||
s32 iVar4 = 0;
|
||||
iVar14 = iVar13;
|
||||
while (0 < iVar14) {
|
||||
CBaseFile* tfile2 = cmds_array[iVar4 / 4]->m_pBaseFile;
|
||||
iVar9 = iVar9 + 1;
|
||||
s32 uVar11;
|
||||
if (read_rates_array[iVar4 / 4] == 0 &&
|
||||
(uVar11 = tfile2->m_nNumPages, (int)uVar11 < remaining_pages_array[iVar4 / 4]) &&
|
||||
((int)uVar11 < tfile2->m_Buffer.m_nMaxNumPages)) {
|
||||
tfile2->m_nNumPages = uVar11 + 1;
|
||||
iVar14 = iVar14 + -1;
|
||||
}
|
||||
if (cmds_total <= iVar9)
|
||||
break;
|
||||
iVar4 = iVar9 * 4;
|
||||
}
|
||||
}
|
||||
been_a_while = iVar13 == iVar14;
|
||||
iVar13 = iVar14;
|
||||
if (been_a_while) {
|
||||
iVar8 = iVar8 + iVar14;
|
||||
iVar13 = 0;
|
||||
}
|
||||
}
|
||||
while (0 < iVar8) {
|
||||
iVar13 = 0;
|
||||
iVar9 = iVar8;
|
||||
if (0 < cmds_total) {
|
||||
s32 iVar14 = 0;
|
||||
iVar9 = iVar8;
|
||||
while (0 < iVar9) {
|
||||
CBaseFile* tfile4 = cmds_array[iVar14 / 4]->m_pBaseFile;
|
||||
iVar13 = iVar13 + 1;
|
||||
s32 uVar11;
|
||||
if (((0 < read_rates_array[iVar14 / 4]) &&
|
||||
(uVar11 = tfile4->m_nNumPages, (int)uVar11 < remaining_pages_array[iVar14 / 4])) &&
|
||||
((int)uVar11 < tfile4->m_Buffer.m_nMaxNumPages)) {
|
||||
tfile4->m_nNumPages = uVar11 + 1;
|
||||
iVar9 = iVar9 + -1;
|
||||
}
|
||||
if (cmds_total <= iVar13)
|
||||
break;
|
||||
iVar14 = iVar13 * 4;
|
||||
}
|
||||
}
|
||||
been_a_while = iVar8 == iVar9;
|
||||
iVar8 = iVar9;
|
||||
if (been_a_while) {
|
||||
iVar8 = 0;
|
||||
}
|
||||
}
|
||||
iVar13 = 0;
|
||||
if (0 < cmds_total) {
|
||||
iVar8 = 0;
|
||||
do {
|
||||
CBaseFile* tfile3 = cmds_array[iVar8 / 4]->m_pBaseFile;
|
||||
|
||||
iVar13 = iVar13 + 1;
|
||||
s32 uVar11 = tfile3->m_nNumPages;
|
||||
// lg::warn("mystery values: {} - {} = {} (from {})", num_pages_array[iVar8 / 4],
|
||||
// uVar11,
|
||||
// num_pages_array[iVar8 / 4] - uVar11,
|
||||
// cmds_array[iVar8 / 4]->m_pBaseFile->m_FileDef->name.data);
|
||||
if (((int)uVar11 < num_pages_array[iVar8 / 4]) &&
|
||||
(iVar8 = tfile3->RecoverPages(num_pages_array[iVar8 / 4] - uVar11), 0 < iVar8)) {
|
||||
get_page_manager()->GarbageCollectPageList(tfile3->m_Buffer.m_pPageList);
|
||||
}
|
||||
iVar8 = iVar13 * 4;
|
||||
} while (iVar13 < cmds_total);
|
||||
}
|
||||
}
|
||||
return selected_cmd;
|
||||
}
|
||||
|
||||
int ProcessMessageData(ISO_Hdr* tgt_msg) {
|
||||
EIsoStatus stat;
|
||||
s32 ret = 1;
|
||||
LAB_000088a4:
|
||||
s32 num_remaining = gPriStack[0].count + -1;
|
||||
if (num_remaining < 0) {
|
||||
return ret;
|
||||
}
|
||||
auto* cmd_array = gPriStack[0].cmds + num_remaining;
|
||||
ISO_Hdr* cmd = nullptr;
|
||||
do {
|
||||
EIsoStatus (*cb)(ISO_Hdr*) = nullptr;
|
||||
cmd = *cmd_array;
|
||||
if (cmd && cmd->active_a) {
|
||||
stat = cmd->status;
|
||||
if (stat == EIsoStatus::OK_2) {
|
||||
auto* file = cmd->m_pBaseFile;
|
||||
auto* buffer = &file->m_Buffer;
|
||||
if (!file) {
|
||||
buffer = nullptr;
|
||||
}
|
||||
if ((buffer && (buffer->m_eBufferType != CBuffer::BufferType::EBT_FREE)) &&
|
||||
(cb = cmd->callback, cb != ProcessVAGData)) {
|
||||
stat = (cb)(cmd);
|
||||
}
|
||||
}
|
||||
if (stat != EIsoStatus::OK_2)
|
||||
break;
|
||||
cmd->status = EIsoStatus::OK_2;
|
||||
}
|
||||
num_remaining = num_remaining + -1;
|
||||
cmd_array = cmd_array + -1;
|
||||
if (num_remaining < 0) {
|
||||
return ret;
|
||||
}
|
||||
} while (true);
|
||||
if (cmd == tgt_msg) {
|
||||
ret = 0;
|
||||
}
|
||||
ReleaseMessage(cmd);
|
||||
if (stat != EIsoStatus::IDLE_1) {
|
||||
cmd->status = stat;
|
||||
ReturnMessage(cmd);
|
||||
}
|
||||
goto LAB_000088a4;
|
||||
}
|
||||
|
||||
void ReturnMessage(ISO_Hdr* msg) {
|
||||
if (msg->mbox_reply == 0) {
|
||||
if (msg->thread_to_wake == 0) {
|
||||
FreeVAGCommand((ISO_VAGCommand*)msg);
|
||||
} else {
|
||||
WakeupThread(msg->thread_to_wake);
|
||||
}
|
||||
} else {
|
||||
SendMbx(msg->mbox_reply, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseMessage(ISO_Hdr* msg) {
|
||||
if (GetThreadId() == g_nISOThreadID) {
|
||||
auto* file = msg->m_pBaseFile;
|
||||
if (file && file->m_Status != EIsoStatus::NONE_0) {
|
||||
file->Close();
|
||||
}
|
||||
UnqueueMessage(msg);
|
||||
} else {
|
||||
if (msg->msg_type != ISO_Hdr::MsgType::ABADBABE) {
|
||||
set_active_a(msg, 0);
|
||||
set_active_c(msg, 0);
|
||||
msg->msg_type = ISO_Hdr::MsgType::ABADBABE;
|
||||
SendMbx(g_nISOMbx, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ISO_VAGCommand* GetVAGCommand() {
|
||||
int iVar1;
|
||||
u32 uVar2;
|
||||
u32 uVar3;
|
||||
|
||||
do {
|
||||
while (vag_cmd_cnt == 0x1f) {
|
||||
DelayThread(1000);
|
||||
}
|
||||
do {
|
||||
iVar1 = WaitSema(g_VagCmdSema);
|
||||
uVar2 = 0;
|
||||
} while (iVar1 != 0);
|
||||
do {
|
||||
uVar3 = uVar2 + 1;
|
||||
if (((vag_cmd_used >> (uVar2 & 0x1f) ^ 1U) & 1) != 0) {
|
||||
vag_cmd_cnt = vag_cmd_cnt + 1;
|
||||
vag_cmd_used = vag_cmd_used | 1 << (uVar2 & 0x1f);
|
||||
if (max_vag_cmd_cnt < vag_cmd_cnt) {
|
||||
max_vag_cmd_cnt = vag_cmd_cnt;
|
||||
}
|
||||
SignalSema(g_VagCmdSema);
|
||||
return vag_cmds + uVar2;
|
||||
}
|
||||
uVar2 = uVar3;
|
||||
} while ((int)uVar3 < 0x1f);
|
||||
SignalSema(g_VagCmdSema);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void FreeVAGCommand(ISO_VAGCommand* param_1) {
|
||||
u32 uVar1;
|
||||
int iVar2;
|
||||
|
||||
uVar1 = param_1 - vag_cmds;
|
||||
ASSERT(uVar1 < 16);
|
||||
if ((uVar1 < 0x1f) && (((vag_cmd_used >> (uVar1 & 0x1f) ^ 1U) & 1) == 0)) {
|
||||
do {
|
||||
iVar2 = WaitSema(g_VagCmdSema);
|
||||
} while (iVar2 != 0);
|
||||
vag_cmd_cnt = vag_cmd_cnt - 1;
|
||||
vag_cmd_used = vag_cmd_used & ~(1 << (uVar1 & 0x1f));
|
||||
SignalSema(g_VagCmdSema);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
30
game/overlord/jak3/iso_queue.h
Normal file
30
game/overlord/jak3/iso_queue.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_iso_queue();
|
||||
struct ISO_Hdr;
|
||||
struct ISO_VAGCommand;
|
||||
void ReleaseMessage(ISO_Hdr* msg);
|
||||
void FreeVagCmd(ISO_VAGCommand* msg);
|
||||
int QueueMessage(ISO_Hdr* msg, int pri);
|
||||
int UnqueueMessage(ISO_Hdr* msg);
|
||||
void ReturnMessage(ISO_Hdr* msg);
|
||||
void InitBuffers();
|
||||
bool DgoCmdWaiting();
|
||||
ISO_Hdr* GetMessage();
|
||||
int ProcessMessageData(ISO_Hdr* msg);
|
||||
void FreeVAGCommand(ISO_VAGCommand* msg);
|
||||
ISO_VAGCommand* GetVAGCommand();
|
||||
|
||||
struct PriStackEntry {
|
||||
ISO_Hdr* cmds[8];
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
extern u32 g_auTrapSRAM[6];
|
||||
extern u32 g_auStrmSRAM[6];
|
||||
extern s32 g_nPriQueueSema;
|
||||
extern PriStackEntry gPriStack[2];
|
||||
} // namespace jak3
|
118
game/overlord/jak3/isocommon.cpp
Normal file
118
game/overlord/jak3/isocommon.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "isocommon.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_isocommon() {}
|
||||
|
||||
/*!
|
||||
* Convert file name to "ISO Name"
|
||||
* ISO names are upper case and 12 bytes long.
|
||||
* xxxxxxxxyyy0
|
||||
*
|
||||
* x - uppercase letter of file name, or space
|
||||
* y - uppercase letter of file extension, or space
|
||||
* 0 - null terminator (\0, not the character zero)
|
||||
*/
|
||||
void MakeISOName(ISOName* dst, const char* src) {
|
||||
int i = 0;
|
||||
const char* src_ptr = src;
|
||||
char* dst_ptr = dst->data;
|
||||
|
||||
// copy name and upper case
|
||||
while ((i < 8) && (*src_ptr) && (*src_ptr != '.')) {
|
||||
char c = *src_ptr;
|
||||
src_ptr++;
|
||||
if (('`' < c) && (c < '{')) { // lower case
|
||||
c -= 0x20;
|
||||
}
|
||||
*dst_ptr = c;
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// pad out name with spaces
|
||||
while (i < 8) {
|
||||
*dst_ptr = ' ';
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// increment past period
|
||||
if (*src_ptr == '.')
|
||||
src_ptr++;
|
||||
|
||||
// same for extension
|
||||
while (i < 11 && (*src_ptr)) {
|
||||
char c = *src_ptr;
|
||||
src_ptr++;
|
||||
if (('`' < c) && (c < '{')) { // lower case
|
||||
c -= 0x20;
|
||||
}
|
||||
*dst_ptr = c;
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
|
||||
while (i < 11) {
|
||||
*dst_ptr = ' ';
|
||||
dst_ptr++;
|
||||
i++;
|
||||
}
|
||||
*dst_ptr = 0;
|
||||
}
|
||||
|
||||
// UnmakeISOName
|
||||
|
||||
/*!
|
||||
* Pack an 8-character (64-bits) file name into a packed vag file name (32 + 10 = 42 bit).
|
||||
*/
|
||||
int PackVAGFileName(u32* out, const char* name) {
|
||||
if (!out || !name) {
|
||||
return 0;
|
||||
}
|
||||
int ret = 1;
|
||||
|
||||
// accumulator of up to 4 packed characters
|
||||
u32 acc = 0;
|
||||
u32 first_four = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// start the second word:
|
||||
if (i == 4) {
|
||||
first_four = acc;
|
||||
acc = 0;
|
||||
}
|
||||
|
||||
// read character from input
|
||||
u32 name_char = *((const u8*)name);
|
||||
name++;
|
||||
|
||||
u32 remapped_char;
|
||||
|
||||
if (name_char - 'A' < 26) { // capital letter
|
||||
remapped_char = name_char - 'A' + 1; // so A becomes 1.
|
||||
} else if (name_char - 'a' < 26) { // lowercase letter
|
||||
remapped_char = name_char - 'a' + 1; // so a becomes 1.
|
||||
} else if (name_char - '0' < 10) { // digit
|
||||
remapped_char = name_char - '0' + 27; // so 0 becomes 27
|
||||
} else if (name_char == '-') {
|
||||
remapped_char = 37;
|
||||
} else if (name_char == ' ' || name_char == '\0') {
|
||||
remapped_char = 0;
|
||||
} else {
|
||||
ASSERT_NOT_REACHED(); // invalid char in input.
|
||||
}
|
||||
|
||||
ASSERT((remapped_char & 0xff) == remapped_char);
|
||||
acc = acc * 38 + remapped_char; // (null + alphabet + 10 + dash)
|
||||
}
|
||||
|
||||
out[0] = (first_four << 0x15) | acc;
|
||||
out[1] = first_four >> 0xb;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// UnpackVAGFileName - nobody uses it...
|
||||
|
||||
} // namespace jak3
|
152
game/overlord/jak3/isocommon.h
Normal file
152
game/overlord/jak3/isocommon.h
Normal file
@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_isocommon();
|
||||
|
||||
struct CBaseFile;
|
||||
|
||||
struct ISOFileDef;
|
||||
|
||||
enum class EIsoStatus {
|
||||
NONE_0 = 0,
|
||||
IDLE_1 = 0x1,
|
||||
OK_2 = 0x2, // already reading, or no need to read, or no mem to read.
|
||||
FAILED_TO_QUEUE_4 = 0x4,
|
||||
ERROR_OPENING_FILE_8 = 0x8,
|
||||
NULL_CALLBACK = 9,
|
||||
ERROR_NO_FILE = 0xa,
|
||||
ERROR_b = 0xb, // tried to do BeginRead on a file that's not allocated.
|
||||
ERROR_NO_SOUND = 0xc,
|
||||
};
|
||||
|
||||
struct SoundBankInfo;
|
||||
|
||||
struct ISO_Hdr {
|
||||
int unka;
|
||||
int unkb;
|
||||
EIsoStatus status;
|
||||
int8_t active_a;
|
||||
int8_t active_b;
|
||||
int8_t active_c;
|
||||
int8_t pad;
|
||||
|
||||
enum class MsgType : u32 {
|
||||
MSG_0 = 0,
|
||||
LOAD_EE = 0x100,
|
||||
LOAD_IOP = 0x101,
|
||||
LOAD_EE_CHUNK = 0x102,
|
||||
LOAD_SOUNDBANK = 0x103,
|
||||
DGO_LOAD = 0x200,
|
||||
VAG_QUEUE = 0x400,
|
||||
VAG_STOP = 0x402,
|
||||
VAG_PAUSE = 0x403,
|
||||
VAG_UNPAUSE = 0x404,
|
||||
VAG_SET_PITCH_VOL = 0x406,
|
||||
PLAY_MUSIC_STREAM = 0x408,
|
||||
ABADBABE = 0xabadbabe,
|
||||
ADEADBEE = 0xadeadbee,
|
||||
} msg_type = MsgType::MSG_0;
|
||||
|
||||
int mbox_reply;
|
||||
int thread_to_wake;
|
||||
|
||||
EIsoStatus (*callback)(ISO_Hdr*) = nullptr;
|
||||
CBaseFile* m_pBaseFile = nullptr;
|
||||
int priority = 0;
|
||||
const ISOFileDef* file_def = nullptr;
|
||||
};
|
||||
|
||||
struct ISO_LoadCommon : public ISO_Hdr {
|
||||
u8* addr = 0; // 44
|
||||
int maxlen = 0;
|
||||
int length_to_copy = 0; // 52
|
||||
u8* dest_ptr = 0; // 68 (not truly common??) what are they doing here.
|
||||
int progress_bytes = 0; // 72
|
||||
};
|
||||
|
||||
struct ISO_LoadSingle : public ISO_LoadCommon {
|
||||
// addr
|
||||
// maxlen
|
||||
// maybe size
|
||||
int sector_offset = 0; // 56
|
||||
// 60
|
||||
// 64
|
||||
// dest_ptr
|
||||
// maybe ofset
|
||||
};
|
||||
|
||||
struct ISO_LoadSoundbank : public ISO_LoadCommon {
|
||||
const char* name = nullptr; // 60
|
||||
int priority; // 64
|
||||
SoundBankInfo* bank_info = nullptr;
|
||||
};
|
||||
|
||||
struct CPage;
|
||||
|
||||
struct BlockParams {
|
||||
void* destination;
|
||||
int num_sectors;
|
||||
int sector_num;
|
||||
// ADDED
|
||||
const ISOFileDef* file_def;
|
||||
CPage* page;
|
||||
char* flag;
|
||||
};
|
||||
|
||||
struct CDescriptor;
|
||||
|
||||
struct Block {
|
||||
BlockParams params;
|
||||
CDescriptor* descriptor;
|
||||
Block* next;
|
||||
};
|
||||
|
||||
struct ISOName {
|
||||
char data[12];
|
||||
|
||||
bool operator==(const ISOName& other) {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (data[i] != other.data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct ISOFileDef {
|
||||
ISOName name;
|
||||
std::string full_path; // pc
|
||||
u32 length;
|
||||
};
|
||||
|
||||
struct VagDirEntry {
|
||||
u32 words[2];
|
||||
};
|
||||
|
||||
struct VagDir {
|
||||
int vag_magic_1 = 0;
|
||||
int vag_magic_2 = 0;
|
||||
int vag_version = 0;
|
||||
int num_entries = 0;
|
||||
VagDirEntry entries[4096];
|
||||
};
|
||||
static_assert(sizeof(VagDir) == 0x8010);
|
||||
|
||||
constexpr int MUSIC_TWEAK_COUNT = 0x40;
|
||||
struct MusicTweaks {
|
||||
u32 TweakCount = 0;
|
||||
struct {
|
||||
char MusicName[12] = "\0";
|
||||
s32 VolumeAdjust = 0;
|
||||
} MusicTweak[MUSIC_TWEAK_COUNT];
|
||||
};
|
||||
|
||||
int PackVAGFileName(u32* out, const char* name);
|
||||
void MakeISOName(ISOName* dst, const char* src);
|
||||
|
||||
} // namespace jak3
|
54
game/overlord/jak3/list.cpp
Normal file
54
game/overlord/jak3/list.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "list.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
using namespace iop;
|
||||
void jak3_overlord_init_globals_list() {}
|
||||
|
||||
void InitList(List* list, int count, int element_size) {
|
||||
VagStreamData* iter;
|
||||
int iVar1;
|
||||
SemaParam sema_params;
|
||||
|
||||
list->count = count;
|
||||
iter = (VagStreamData*)AllocSysMemory(0, count * element_size, 0);
|
||||
ASSERT(iter);
|
||||
ASSERT(element_size == sizeof(VagStreamData));
|
||||
list->buffer = (u8*)iter;
|
||||
if (iter != (VagStreamData*)0x0) {
|
||||
list->next = iter;
|
||||
iVar1 = 0;
|
||||
if (0 < count) {
|
||||
do {
|
||||
iter->in_use = 0;
|
||||
if (iVar1 < count + -1) {
|
||||
iter->next = (VagStreamData*)((u8*)iter + element_size);
|
||||
} else {
|
||||
iter->next = nullptr;
|
||||
}
|
||||
if (iVar1 == 0) {
|
||||
iter->prev = nullptr;
|
||||
} else {
|
||||
iter->prev = (VagStreamData*)((u8*)iter - element_size);
|
||||
}
|
||||
iVar1 = iVar1 + 1;
|
||||
iter = (VagStreamData*)((u8*)iter + element_size);
|
||||
} while (iVar1 < count);
|
||||
}
|
||||
list->unk_flag = 0;
|
||||
list->pending_data = 0;
|
||||
sema_params.max_count = 1;
|
||||
sema_params.attr = 1;
|
||||
sema_params.init_count = 1;
|
||||
sema_params.option = 0;
|
||||
iVar1 = CreateSema(&sema_params);
|
||||
list->sema = iVar1;
|
||||
ASSERT(list->sema >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
27
game/overlord/jak3/list.h
Normal file
27
game/overlord/jak3/list.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_list();
|
||||
|
||||
struct VagStreamData;
|
||||
|
||||
/*!
|
||||
* The List system is a linked list used to track requested and playing streams.
|
||||
* Originally, it supported multiple element types.
|
||||
* One of those types is related to plugin streams which are not supported in PC.
|
||||
* So, this is just hard-coded to use VagStreamData as the element type.
|
||||
*/
|
||||
struct List {
|
||||
char name[8];
|
||||
int sema = 0;
|
||||
int unk_flag = 0; // set when there's a free node??
|
||||
int count = 0;
|
||||
int pending_data = 0;
|
||||
VagStreamData* next = nullptr;
|
||||
u8* buffer;
|
||||
};
|
||||
|
||||
void InitList(List* list, int num_elements, int element_size);
|
||||
} // namespace jak3
|
151
game/overlord/jak3/overlord.cpp
Normal file
151
game/overlord/jak3/overlord.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
#include "overlord.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "game/overlord/jak3/dvd_driver.h"
|
||||
#include "game/overlord/jak3/init.h"
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/ramdisk.h"
|
||||
#include "game/overlord/jak3/sbank.h"
|
||||
#include "game/overlord/jak3/srpc.h"
|
||||
#include "game/overlord/jak3/ssound.h"
|
||||
#include "game/overlord/jak3/vblank_handler.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
using namespace iop;
|
||||
|
||||
int g_nServerThreadID = 0;
|
||||
int g_nPlayerThreadID = 0;
|
||||
int g_nLoaderThreadID = 0;
|
||||
|
||||
void jak3_overlord_init_globals_overlord() {
|
||||
g_nServerThreadID = 0;
|
||||
g_nPlayerThreadID = 0;
|
||||
g_nLoaderThreadID = 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*!
|
||||
* Start up overlord threads. After this returns, the overlord is initialized and
|
||||
* ready to run. (this might have been in start.cpp in the original, but this is close enough)
|
||||
*/
|
||||
int start_overlord() {
|
||||
// Initialize SIF to communicate with the EE. Does nothing in port
|
||||
if (!sceSifCheckInit()) {
|
||||
sceSifInit();
|
||||
}
|
||||
// Initialize RPC to EE
|
||||
sceSifInitRpc(0);
|
||||
|
||||
lg::info("======== overlrd2.irx startup ========");
|
||||
// Removed some prints related to IOP memory size
|
||||
|
||||
// C++ ctors ran here. In the port, we've added these "init_globals" function that
|
||||
// take care of resetting all the global/static variables and constructing C++ objects.
|
||||
// do_global_ctors();
|
||||
jak3_overlord_init_globals_all();
|
||||
InitBanks();
|
||||
InitSound();
|
||||
VBlank_Initialize();
|
||||
|
||||
// RPC thread to load data from game files to the game memory.
|
||||
ThreadParam thread_param;
|
||||
thread_param.initPriority = 0x3b;
|
||||
thread_param.stackSize = 0x800;
|
||||
thread_param.entry = LoadToEE_RPC_Thread;
|
||||
thread_param.attr = TH_C;
|
||||
thread_param.option = 0;
|
||||
strcpy(thread_param.name, "load_to_ee");
|
||||
g_nServerThreadID = CreateThread(&thread_param);
|
||||
if (g_nServerThreadID <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// thread to respond to sound play RPC's
|
||||
thread_param.entry = Thread_Player;
|
||||
thread_param.initPriority = 0x36;
|
||||
thread_param.stackSize = 0xb00;
|
||||
thread_param.attr = TH_C;
|
||||
thread_param.option = 0;
|
||||
strcpy(thread_param.name, "player");
|
||||
g_nPlayerThreadID = CreateThread(&thread_param);
|
||||
if (g_nPlayerThreadID <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// thread to respond to sound load RPC's
|
||||
thread_param.attr = TH_C;
|
||||
thread_param.entry = Thread_Loader;
|
||||
thread_param.initPriority = 0x3a;
|
||||
thread_param.stackSize = 0x900;
|
||||
thread_param.option = 0;
|
||||
strcpy(thread_param.name, "loader");
|
||||
g_nLoaderThreadID = CreateThread(&thread_param);
|
||||
if (g_nLoaderThreadID <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize the dvd driver that will be used to implement the "ISO FS" system
|
||||
get_driver()->Initialize();
|
||||
|
||||
// then, initialize ISO FS itself
|
||||
InitISOFS();
|
||||
|
||||
// start up RPC threads!
|
||||
StartThread(g_nServerThreadID, 0);
|
||||
StartThread(g_nPlayerThreadID, 0);
|
||||
StartThread(g_nLoaderThreadID, 0);
|
||||
|
||||
lg::info("[overlord2] Threads started");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool* init_complete;
|
||||
|
||||
/*!
|
||||
* Wrapper of start_overlord that can be run as an IOP thread. Sets init_complete flag to true.
|
||||
* This is a bit of hack to transition from the normal and sane game code to the world of IOP
|
||||
* threading.
|
||||
*/
|
||||
u32 call_start() {
|
||||
start_overlord();
|
||||
*init_complete = true;
|
||||
|
||||
// TODO: figure out how to quit this thread.
|
||||
while (true) {
|
||||
SleepThread();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int start_overlord_wrapper(bool* signal) {
|
||||
ThreadParam param = {};
|
||||
init_complete = signal;
|
||||
param.attr = TH_C;
|
||||
param.initPriority = 0;
|
||||
param.stackSize = 0x800;
|
||||
param.option = 0;
|
||||
strcpy(param.name, "start"); // added for debug
|
||||
param.entry = call_start;
|
||||
auto start_thread = CreateThread(¶m);
|
||||
StartThread(start_thread, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Copy null-terminated string to destination buffer with the given size.
|
||||
* If the string doesn't fit, it will be truncated and null terminated.
|
||||
*/
|
||||
char* strncpyz(char* dst, const char* src, size_t n) {
|
||||
if (n) {
|
||||
strncpy(dst, src, n);
|
||||
dst[n - 1] = 0;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
} // namespace jak3
|
61
game/overlord/jak3/overlord.h
Normal file
61
game/overlord/jak3/overlord.h
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/log/log.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
/*!
|
||||
* External entry point for the game to start Overlord. This assumes that the IOP Kernel
|
||||
* is at least initialized, then sets up all overlord threads/RPCs. Once this returns,
|
||||
* it's safe to call overlord functions.
|
||||
*/
|
||||
int start_overlord_wrapper(bool* signal);
|
||||
void jak3_overlord_init_globals_overlord();
|
||||
char* strncpyz(char* dst, const char* src, size_t n);
|
||||
|
||||
extern int g_nServerThreadID;
|
||||
extern int g_nPlayerThreadID;
|
||||
extern int g_nLoaderThreadID;
|
||||
|
||||
enum class LogCategory {
|
||||
PAGING,
|
||||
FILESYSTEM,
|
||||
WARN,
|
||||
SPU_DMA_STR,
|
||||
EE_DMA,
|
||||
ISO_QUEUE,
|
||||
VAG_SETUP,
|
||||
DGO,
|
||||
RPC,
|
||||
STR_RPC,
|
||||
PLAYER_RPC,
|
||||
DRIVER,
|
||||
NUM_CATETORIES
|
||||
};
|
||||
|
||||
constexpr bool g_OverlordLogEnable[(int)LogCategory::NUM_CATETORIES] = {
|
||||
false, // paging: cpage's, page manager, page crossing, etc
|
||||
true, // filesystem: opening/finding files
|
||||
true, // warning: something weird
|
||||
false, // spu dma streaming: vag streaming, clocks, spu, dma
|
||||
true, // ee dma: sending stuff to the ee (dgo, etc)
|
||||
true, // iso queue: message queuing
|
||||
true, // vag setup: creation of vag commands (lists, etc)
|
||||
false, // dgo
|
||||
true, // rpc in general
|
||||
true, // str rpc
|
||||
false, // PLAYER
|
||||
false, // driver
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
void ovrld_log(LogCategory category, const std::string& format, Args&&... args) {
|
||||
if (g_OverlordLogEnable[(int)category]) {
|
||||
lg::info(format, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
852
game/overlord/jak3/pagemanager.cpp
Normal file
852
game/overlord/jak3/pagemanager.cpp
Normal file
@ -0,0 +1,852 @@
|
||||
#include "pagemanager.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
namespace jak3 {
|
||||
namespace {
|
||||
std::unique_ptr<CPageManager> g_manager;
|
||||
}
|
||||
void jak3_overlord_init_globals_pagemanager() {
|
||||
g_manager = std::make_unique<CPageManager>();
|
||||
}
|
||||
|
||||
CPageManager* get_page_manager() {
|
||||
return g_manager.get();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add the given number of pages to the active list, returning the first in the active list.
|
||||
* This should be called before writing to the pages.
|
||||
* This grows the active list from the end and adds a reference count.
|
||||
*/
|
||||
CPage* CPageList::AddActivePages(int num_pages) {
|
||||
ASSERT(m_nAllocState == AllocState::EPLAS_ALLOCATED);
|
||||
ASSERT(num_pages > 0); // game warned on this, might be ok to just return null
|
||||
|
||||
if (m_nNumPages < num_pages + m_nNumActivePages) {
|
||||
// the original game just gave you no pages, but that seems sus, so abort for now.
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// pick the page to convert from non-active to active
|
||||
CPage* first_to_convert;
|
||||
if (m_pLastActivePage) {
|
||||
// if we have active pages, go to the page after that
|
||||
first_to_convert = m_pLastActivePage->m_pNextPage;
|
||||
} else {
|
||||
// otherwise, start at the beginning of the page list.
|
||||
first_to_convert = m_pFirstPage;
|
||||
}
|
||||
|
||||
if (first_to_convert) {
|
||||
// the first active page should stay the same if we have one
|
||||
CPage* new_first_active = m_pFirstActivePage;
|
||||
if (!new_first_active) {
|
||||
// but if we don't, it'll be the first we convert.
|
||||
new_first_active = first_to_convert;
|
||||
}
|
||||
|
||||
int page_idx = 0; // how many we've done
|
||||
CPage* last_converted = nullptr; // last one that was finished
|
||||
CPage* to_convert = first_to_convert; // next one to convert
|
||||
if (0 < num_pages) {
|
||||
// loop over pages and convert them!!
|
||||
do {
|
||||
ASSERT(to_convert->input_state == CPage::State::UNMAKRED);
|
||||
last_converted = to_convert;
|
||||
last_converted->input_state = CPage::State::ACTIVE;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~last_converted->mask);
|
||||
int status = last_converted->AddRef();
|
||||
ASSERT(status >= 1);
|
||||
page_idx = page_idx + 1;
|
||||
if (!last_converted->m_pNextPage)
|
||||
break;
|
||||
to_convert = last_converted->m_pNextPage;
|
||||
} while (page_idx < num_pages);
|
||||
}
|
||||
if (page_idx == num_pages) {
|
||||
// success! all converted.
|
||||
// CpuSuspendIntr(local_28);
|
||||
// update the active range and count
|
||||
m_pFirstActivePage = new_first_active;
|
||||
m_pLastActivePage = last_converted;
|
||||
m_nNumActivePages = m_nNumActivePages + page_idx;
|
||||
m_nNumUnsteppedPages = m_nNumUnsteppedPages + page_idx;
|
||||
if (!m_pCurrentActivePage) {
|
||||
m_pCurrentActivePage = first_to_convert;
|
||||
}
|
||||
// CpuResumeIntr(local_28[0]);
|
||||
return first_to_convert;
|
||||
} else {
|
||||
// darn, didn't have enough. undo what we did.
|
||||
// Not really sure that we need to clear the even flag again.
|
||||
while (0 < page_idx) {
|
||||
new_first_active->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag,
|
||||
~new_first_active->mask);
|
||||
new_first_active->ReleaseRef();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Try to remove count pages from the active list, starting at the end and working back.
|
||||
* This will remove buffered that the user has not yet read. The data will have to be read from the
|
||||
* DVD again, so this should only be used when more free pages are absolutely needed.
|
||||
*
|
||||
* This may remove fewer than requested. It will not remove the current active page, and always
|
||||
* leaves at least 1 active page to avoid removing a page that's currently being used.
|
||||
*/
|
||||
int CPageList::RemoveActivePages(int count) {
|
||||
// lg::error("Remove Active Pages {}", count);
|
||||
int num_removed = 0;
|
||||
ASSERT(m_nAllocState == AllocState::EPLAS_ALLOCATED);
|
||||
|
||||
// CpuSuspendIntr(local_28);
|
||||
num_removed = 0;
|
||||
|
||||
// only attempt if we have more than 1 active
|
||||
if (count > 0 && m_nNumActivePages > 1) {
|
||||
CPage* last_active = m_pLastActivePage;
|
||||
CPage* current_active = m_pCurrentActivePage;
|
||||
|
||||
// I'm not sure what the last->next = current check is actually doing.
|
||||
// this check is added - I have no idea how this could happen, or why the game avoids removing
|
||||
// pages in this case. Hopefully we don't hit it.
|
||||
if (last_active) {
|
||||
ASSERT(last_active->m_pNextPage != current_active);
|
||||
}
|
||||
|
||||
if (current_active && last_active && last_active->m_pNextPage != current_active) {
|
||||
// assume we remove them all except 1.
|
||||
num_removed = m_nNumActivePages + -1;
|
||||
// but if that's more than we asked for, reduce.
|
||||
if (count < num_removed) {
|
||||
num_removed = count;
|
||||
}
|
||||
|
||||
// iterate backward to find the first to remove, stopping if we reach current_active, or we
|
||||
// exceed the count to remove.
|
||||
int num_pages_back = 0;
|
||||
CPage* iter = last_active;
|
||||
if (last_active != current_active && 0 < num_removed) {
|
||||
do {
|
||||
iter = iter->m_pPrevPage;
|
||||
num_pages_back = num_pages_back + 1;
|
||||
if (!iter) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
} while (iter != current_active && num_pages_back < num_removed);
|
||||
|
||||
if (0 < num_pages_back) {
|
||||
// now, iterate forward and convert to inactive.
|
||||
// go one forward, since the last loop terminated on the page after the last one we want.
|
||||
CPage* fwd_iter = iter->m_pNextPage;
|
||||
|
||||
// the last one that stays active is one after that
|
||||
m_pLastActivePage = fwd_iter->m_pPrevPage;
|
||||
m_nNumActivePages = m_nNumActivePages - num_pages_back;
|
||||
m_nNumUnsteppedPages = m_nNumUnsteppedPages - num_pages_back;
|
||||
|
||||
// the end of the conversion loop
|
||||
CPage* end_iter = last_active->m_pNextPage;
|
||||
num_removed = num_pages_back;
|
||||
while (fwd_iter && fwd_iter != end_iter) {
|
||||
fwd_iter->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~fwd_iter->mask);
|
||||
fwd_iter->ReleaseRef();
|
||||
fwd_iter = fwd_iter->m_pNextPage;
|
||||
}
|
||||
} else {
|
||||
// in this case, I think we return the wrong number of removed pages.
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// CpuResumeIntr(local_28[0]);
|
||||
return num_removed;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Remove all active pages.
|
||||
*/
|
||||
void CPageList::CancelActivePages() {
|
||||
ASSERT(m_nAllocState == AllocState::EPLAS_ALLOCATED);
|
||||
// CpuSuspendIntr(local_18);
|
||||
CPage* last_active = m_pLastActivePage;
|
||||
CPage* iter = m_pCurrentActivePage;
|
||||
// note: keep the last active page pointing at the right point in the ring to allocate.
|
||||
m_pLastActivePage = m_pCurrentActivePage;
|
||||
m_pFirstActivePage = nullptr;
|
||||
m_pCurrentActivePage = nullptr;
|
||||
m_nNumActivePages = 0;
|
||||
m_nNumUnsteppedPages = 0;
|
||||
if (iter && last_active && last_active->m_pNextPage != iter) {
|
||||
CPage* end = last_active->m_pNextPage;
|
||||
do {
|
||||
if (iter == end)
|
||||
break;
|
||||
iter->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~iter->mask);
|
||||
iter->ReleaseRef();
|
||||
iter = iter->m_pNextPage;
|
||||
} while (iter);
|
||||
}
|
||||
// CpuResumeIntr(local_18[0]);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Step the current active page forward. This will release the reference count added when the page
|
||||
* became active. If no other references were added, this page may be Garbage Collected at any time.
|
||||
*/
|
||||
CPage* CPageList::StepActivePage() {
|
||||
ASSERT(m_nAllocState == AllocState::EPLAS_ALLOCATED);
|
||||
|
||||
CPage* new_current_active = nullptr;
|
||||
// CpuSuspendIntr(local_18);
|
||||
auto* current_active = m_pCurrentActivePage;
|
||||
if (current_active && m_pLastActivePage && m_pLastActivePage->m_pNextPage != current_active) {
|
||||
ASSERT(m_nNumActivePages > 0);
|
||||
m_nNumUnsteppedPages = m_nNumUnsteppedPages + -1;
|
||||
current_active->ReleaseRef();
|
||||
current_active->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~current_active->mask);
|
||||
new_current_active = current_active->m_pNextPage;
|
||||
ASSERT(new_current_active != m_pCurrentActivePage);
|
||||
m_pCurrentActivePage = new_current_active;
|
||||
} else {
|
||||
ASSERT_NOT_REACHED(); // step past end of active warning, seems bad.
|
||||
}
|
||||
// CpuResumeIntr(local_18[0]);
|
||||
return new_current_active;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Remove pages that are no longer needed. They will be sent back to the Page Manager.
|
||||
*/
|
||||
void CPageList::GarbageCollect() {
|
||||
ovrld_log(LogCategory::PAGING, "[paging] Garbage collecting, currently have {} pages, {} active",
|
||||
m_nNumPages, m_nNumActivePages);
|
||||
|
||||
// for (auto* p = m_pFirstPage; p; p = p->m_pNextPage) {
|
||||
// ovrld_log(LogCategory::PAGING,
|
||||
// "page 0x{:x}, first active? {} last active? {} current active? {} last? {}",
|
||||
// p->m_nPageIdx, p == m_pFirstActivePage, p == m_pLastActivePage,
|
||||
// p == m_pCurrentActivePage, p == m_pLastPage);
|
||||
// }
|
||||
// trim pages at the front. Anything unreferenced before the current active page is ok to clean.
|
||||
CPage* page = m_pFirstPage;
|
||||
if (page && page != m_pCurrentActivePage) {
|
||||
ASSERT(page->m_nAllocState == 1); // pages in CPageLists should always be allocated.
|
||||
|
||||
while (page->m_nPageRefCount == 0 && page->m_nDmaRefCount == 0) { // only unref'd pages.
|
||||
ASSERT(page->m_nAllocState == 1); // prior to active.
|
||||
CPage* next_page = page->m_pNextPage;
|
||||
// CpuSuspendIntr(&local_18);
|
||||
m_nNumPages = m_nNumPages + -1;
|
||||
// pop page from our normal list
|
||||
m_pFirstPage = next_page;
|
||||
if (m_pLastPage == page) {
|
||||
m_pLastPage = nullptr;
|
||||
// sanity check - we just killed our last page from the list, count should be 0
|
||||
ASSERT(m_nNumPages == 0);
|
||||
}
|
||||
|
||||
// maintain active list too
|
||||
if (m_pFirstActivePage == page) {
|
||||
m_nNumActivePages = m_nNumActivePages + -1;
|
||||
if (page == m_pLastActivePage) {
|
||||
// since we're getting rid of this page from our list, don't keep a ref to it.
|
||||
m_pFirstActivePage = nullptr;
|
||||
m_pLastActivePage = nullptr;
|
||||
ASSERT(m_nNumActivePages == 0); // sanity check count.
|
||||
} else {
|
||||
m_pFirstActivePage = next_page;
|
||||
}
|
||||
}
|
||||
if (m_pLastActivePage == page) {
|
||||
m_pLastActivePage = nullptr;
|
||||
}
|
||||
if (next_page) {
|
||||
next_page->m_pPrevPage = nullptr;
|
||||
}
|
||||
// CpuResumeIntr(local_18);
|
||||
|
||||
// now clean the page itself
|
||||
ovrld_log(LogCategory::PAGING, "[paging] GC took page 0x{:x} (fwd)", page->m_nPageIdx);
|
||||
page->m_pPageList = nullptr;
|
||||
page->m_pPrevPage = nullptr;
|
||||
page->m_pNextPage = nullptr;
|
||||
page->m_nAllocState = 0;
|
||||
page->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~page->mask);
|
||||
int page_idx = page - get_page_manager()->m_CCache.m_Pages;
|
||||
if (page_idx < 0x1d) {
|
||||
get_page_manager()->m_CCache.m_nAllocatedMask &= ~(1 << (page_idx & 0x1f));
|
||||
} else {
|
||||
ASSERT_NOT_REACHED(); // idk
|
||||
}
|
||||
get_page_manager()->m_CCache.m_nNumFreePages++;
|
||||
if (!next_page) {
|
||||
break;
|
||||
}
|
||||
page = next_page;
|
||||
if (page == m_pCurrentActivePage) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now, start at the end and work backward. We'll stop once we reach the last active page, since
|
||||
// we expect everything before that to have a nonzero ref count.
|
||||
page = m_pLastPage;
|
||||
if (page && page != m_pLastActivePage && page != m_pCurrentActivePage) {
|
||||
ASSERT(page->m_nAllocState == 1);
|
||||
while ((page->m_nPageRefCount == 0 && (page->m_nDmaRefCount == 0))) {
|
||||
ovrld_log(LogCategory::PAGING, "[paging] GC took page 0x{:x} (bwd)", page->m_nPageIdx);
|
||||
ASSERT(page->m_nAllocState == 1);
|
||||
CPage* prev = page->m_pPrevPage;
|
||||
// CpuSuspendIntr(&local_14);
|
||||
m_nNumPages = m_nNumPages + -1;
|
||||
m_pLastPage = prev;
|
||||
if (m_pFirstPage == page) {
|
||||
m_pFirstPage = nullptr;
|
||||
ASSERT(m_nNumPages == 0);
|
||||
}
|
||||
if (prev != (CPage*)0x0) {
|
||||
prev->m_pNextPage = (CPage*)0x0;
|
||||
}
|
||||
// CpuResumeIntr(local_14);
|
||||
page->m_pPageList = nullptr;
|
||||
page->m_pPrevPage = nullptr;
|
||||
page->m_pNextPage = nullptr;
|
||||
page->m_nAllocState = 0;
|
||||
page->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~page->mask);
|
||||
int page_idx = page - get_page_manager()->m_CCache.m_Pages;
|
||||
if (page_idx < 0x1d) {
|
||||
get_page_manager()->m_CCache.m_nAllocatedMask &= ~(1 << (page_idx & 0x1f));
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
get_page_manager()->m_CCache.m_nNumFreePages++;
|
||||
|
||||
if (!prev) {
|
||||
return;
|
||||
}
|
||||
if (prev == m_pLastActivePage) {
|
||||
return;
|
||||
}
|
||||
page = prev;
|
||||
if (prev == m_pCurrentActivePage) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ovrld_log(LogCategory::PAGING,
|
||||
"[paging] Done Garbage collecting, currently have {} pages, {} active in 0x{:x}",
|
||||
m_nNumPages, m_nNumActivePages, (u64)this);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wait on the pages indicated by the mask.
|
||||
*/
|
||||
void CPageManager::WaitForPagesFilled(u32 mask) {
|
||||
if ((mask & m_CCache.m_PendingMask) != 0) {
|
||||
WaitEventFlag(m_CCache.m_PagesFilledEventFlag, mask, 0);
|
||||
} else {
|
||||
// waiting for something that's not pending... might be ok. was a warning.
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
CPage::CPage(uint8_t* page_mem_start, uint8_t* page_mem_end, int page_idx) {
|
||||
m_pNextPage = nullptr;
|
||||
mask = 1 << (page_idx & 0x1f);
|
||||
m_pPrevPage = nullptr;
|
||||
m_pPageMemStart = page_mem_start;
|
||||
m_pPageList = nullptr;
|
||||
m_pPageMemEnd = page_mem_end;
|
||||
m_nPageRefCount = 0;
|
||||
m_nPageIdx = page_idx;
|
||||
m_nDmaRefCount = 0;
|
||||
m_nAllocState = 0;
|
||||
input_state = State::UNMAKRED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add one to this CPage's reference count, preventing it from being garbage collected
|
||||
*/
|
||||
int CPage::AddRef() {
|
||||
// CpuSuspendIntr(local_18);
|
||||
auto* page_list = m_pPageList;
|
||||
int ret = -1;
|
||||
ASSERT(page_list);
|
||||
ASSERT(m_nAllocState == 1);
|
||||
if (m_nAllocState == 1 && page_list) {
|
||||
page_list->m_nPageRefCnt = page_list->m_nPageRefCnt + 1;
|
||||
m_nPageRefCount = m_nPageRefCount + 1;
|
||||
ret = m_nPageRefCount;
|
||||
}
|
||||
// CpuResumeIntr(local_18[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Subtract one from this CPage's reference count.
|
||||
*/
|
||||
int CPage::ReleaseRef() {
|
||||
// CpuSuspendIntr(local_18);
|
||||
auto* page_list = m_pPageList;
|
||||
int ret = -1;
|
||||
ASSERT(page_list);
|
||||
ASSERT(m_nAllocState == 1);
|
||||
if (m_nAllocState == 1 && page_list) {
|
||||
page_list->m_nPageRefCnt = page_list->m_nPageRefCnt - 1;
|
||||
m_nPageRefCount = m_nPageRefCount - 1;
|
||||
ret = m_nPageRefCount;
|
||||
ASSERT(ret >= 0);
|
||||
}
|
||||
// CpuResumeIntr(local_18[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Add one to the DMA reference count of this page
|
||||
*/
|
||||
int CPage::AddDmaRef() {
|
||||
// CpuSuspendIntr(local_18);
|
||||
auto* page_list = m_pPageList;
|
||||
int ret = -1;
|
||||
ASSERT(page_list);
|
||||
ASSERT(m_nAllocState == 1);
|
||||
if (m_nAllocState == 1 && page_list) {
|
||||
page_list->m_nDmaRefCnt = page_list->m_nDmaRefCnt + 1;
|
||||
m_nDmaRefCount = m_nDmaRefCount + 1;
|
||||
ret = m_nPageRefCount;
|
||||
}
|
||||
// CpuResumeIntr(local_18[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int CPage::ReleaseDmaRef() {
|
||||
// CpuSuspendIntr(local_18);
|
||||
auto* page_list = m_pPageList;
|
||||
int ret = -1;
|
||||
ASSERT(page_list);
|
||||
ASSERT(m_nAllocState == 1);
|
||||
if (m_nAllocState == 1 && page_list) {
|
||||
page_list->m_nDmaRefCnt = page_list->m_nDmaRefCnt - 1;
|
||||
m_nDmaRefCount = m_nDmaRefCount - 1;
|
||||
ret = m_nPageRefCount;
|
||||
ASSERT(ret >= 0);
|
||||
}
|
||||
// CpuResumeIntr(local_18[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Copy data from this page to destination. This works with sizes that are greater than the page
|
||||
* size, and will look at future pages. However, it does not actually advance progress in the page.
|
||||
*/
|
||||
void CPage::FromPagesCopy(uint8_t* in, uint8_t* dest, s32 size) {
|
||||
ASSERT(in);
|
||||
ASSERT(dest);
|
||||
ASSERT(size >= 0);
|
||||
CPage* page = this;
|
||||
if (0 < size) {
|
||||
while (true) {
|
||||
s32 input_page_left = (page->m_pPageMemEnd - in) + 1;
|
||||
ASSERT(input_page_left >= 0);
|
||||
if (size < input_page_left)
|
||||
break;
|
||||
size -= input_page_left;
|
||||
memcpy(dest, in, input_page_left);
|
||||
dest += input_page_left;
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
ASSERT(size > 0);
|
||||
ASSERT(page->m_pNextPage);
|
||||
page = page->m_pNextPage;
|
||||
in = page->m_pPageMemStart;
|
||||
}
|
||||
memcpy(dest, in, size);
|
||||
}
|
||||
}
|
||||
|
||||
CCache::CCache() {
|
||||
m_PendingMask = kAllPagesMask;
|
||||
m_PagesFilledEventFlag = -1;
|
||||
m_nNumFreePages = 0;
|
||||
m_nAllocatedMask = 0;
|
||||
m_nPagelistAllocatedMask = 0;
|
||||
}
|
||||
|
||||
void CCache::Initialize() {
|
||||
static_assert(0xe81d0 == kPageStride * kNumPages);
|
||||
m_paCache = AllocSysMemory(0, kPageStride * kNumPages, nullptr);
|
||||
ASSERT(m_paCache);
|
||||
m_nNumFreePages = kNumPages;
|
||||
|
||||
// initialize pageslists
|
||||
for (auto& page_list : m_PageLists) {
|
||||
page_list.m_pFirstActivePage = nullptr;
|
||||
page_list.m_pLastActivePage = nullptr;
|
||||
page_list.m_pCurrentActivePage = nullptr;
|
||||
|
||||
page_list.m_pFirstPage = nullptr;
|
||||
page_list.m_pLastPage = nullptr;
|
||||
page_list.m_nNumActivePages = 0;
|
||||
page_list.m_nNumUnsteppedPages = 0;
|
||||
page_list.m_nPageRefCnt = 0;
|
||||
page_list.m_nDmaRefCnt = 0;
|
||||
page_list.m_nAllocState = CPageList::AllocState::EPLAS_FREE;
|
||||
}
|
||||
|
||||
u8* mem = (u8*)m_paCache;
|
||||
for (int i = 0; i < kNumPages; i++) {
|
||||
m_Pages[i] = CPage(mem, mem + kPageSize - 1, i);
|
||||
// interestingly, the stride is a bit longer.
|
||||
mem += kPageStride;
|
||||
}
|
||||
|
||||
m_nAllocatedMask = 0;
|
||||
m_nPagelistAllocatedMask = 0;
|
||||
|
||||
EventFlagParam param;
|
||||
param.attr = 2;
|
||||
param.option = 0;
|
||||
param.init_pattern = 0;
|
||||
m_PagesFilledEventFlag = CreateEventFlag(¶m); // TODO args here
|
||||
ASSERT(m_PagesFilledEventFlag >= 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Increase the length by the given amount. This is used for the DVD reading side to inform the
|
||||
* consumer that there is more data available.
|
||||
*/
|
||||
void CBuffer::AddData(int len) {
|
||||
// suspend interrupts
|
||||
m_nDataLength += len;
|
||||
// resume interrupts
|
||||
}
|
||||
|
||||
/*!
|
||||
* Advance the current point in the buffer. This is used by the consume to mark forward progress.
|
||||
*/
|
||||
void CBuffer::AdvanceCurrentData(int len) {
|
||||
// suspend interrupts
|
||||
m_nDataLength -= len;
|
||||
m_pCurrentData += len;
|
||||
// resume interrupts
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set up pages.
|
||||
*/
|
||||
void CPageManager::Initialize() {
|
||||
m_CCache.Initialize();
|
||||
}
|
||||
|
||||
CPageList* CPageManager::AllocPageListBytes(int bytes, bool flag) {
|
||||
return AllocPageList((bytes + kPageSize - 1) / kPageSize, flag);
|
||||
}
|
||||
|
||||
s32 alloc_bitmask(u32* mask, u32 length, u32 start = 0) {
|
||||
for (u32 i = start; i < length; i++) {
|
||||
if ((*mask & (1 << i)) == 0) {
|
||||
// it's free!
|
||||
(*mask) |= (1 << i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Allocate a PageList with the given number of pages.
|
||||
*/
|
||||
CPageList* CPageManager::AllocPageList(int count, bool consecutive_pages) {
|
||||
ASSERT(count > 0);
|
||||
ASSERT(count <= CCache::kNumPages);
|
||||
|
||||
if (count > m_CCache.m_nNumFreePages) {
|
||||
// if we're out of pages, use RecoverPages to discard pages that we've already read, but
|
||||
// nobody is using yet. We'll be able to read them from the DVD again.
|
||||
lg::warn("Recovering pages - {} requested in AllocPageList, but only {} available", count,
|
||||
m_CCache.m_nNumFreePages);
|
||||
RecoverPages(count);
|
||||
ASSERT(m_CCache.m_nNumFreePages >= count);
|
||||
}
|
||||
|
||||
// next, find a pagelist. the original game had some fancy bit magic here, but this is simpler
|
||||
int plist_idx = alloc_bitmask(&m_CCache.m_nPagelistAllocatedMask, CCache::kNumPageLists);
|
||||
ASSERT(plist_idx >= 0);
|
||||
CPageList* plist = &m_CCache.m_PageLists[plist_idx];
|
||||
|
||||
// Fill this array with allocated pages
|
||||
CPage* pages[CCache::kNumPages];
|
||||
int pages_allocated = 0;
|
||||
int last_page_allocated = -1;
|
||||
int next_page_to_check = 0;
|
||||
|
||||
while (pages_allocated < count) {
|
||||
if (next_page_to_check >= CCache::kNumPages) {
|
||||
break;
|
||||
}
|
||||
// grab the next page
|
||||
int page_idx = alloc_bitmask(&m_CCache.m_nAllocatedMask, CCache::kNumPages, next_page_to_check);
|
||||
ASSERT(page_idx >= 0);
|
||||
|
||||
// start after this page on the next search
|
||||
next_page_to_check = page_idx + 1;
|
||||
|
||||
pages[pages_allocated] = &m_CCache.m_Pages[page_idx];
|
||||
pages_allocated++;
|
||||
|
||||
// if we asked for consecutive pages, but didn't get them, we need to rewind our progress.
|
||||
// but, we shouldn't rewind next_page_to_check!
|
||||
if (consecutive_pages && last_page_allocated != -1 && last_page_allocated + 1 != page_idx) {
|
||||
page_idx = -1;
|
||||
while (pages_allocated) {
|
||||
pages_allocated--;
|
||||
|
||||
u32 i = pages[pages_allocated] - m_CCache.m_Pages;
|
||||
ASSERT(i >= 0 && i < CCache::kNumPages);
|
||||
m_CCache.m_nAllocatedMask &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
last_page_allocated = page_idx;
|
||||
}
|
||||
|
||||
if (pages_allocated != count) {
|
||||
// allocation failed
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_CCache.m_nNumFreePages -= count;
|
||||
ASSERT(m_CCache.m_nNumFreePages >= 0);
|
||||
|
||||
// zero everything
|
||||
plist->m_pFirstPage = nullptr;
|
||||
plist->m_pLastPage = nullptr;
|
||||
plist->m_pFirstActivePage = nullptr;
|
||||
plist->m_pLastActivePage = nullptr;
|
||||
plist->m_pCurrentActivePage = nullptr;
|
||||
plist->m_nNumPages = 0;
|
||||
plist->m_nNumActivePages = 0;
|
||||
plist->m_nNumUnsteppedPages = 0;
|
||||
ASSERT(plist->m_nPageRefCnt == 0);
|
||||
ASSERT(plist->m_nDmaRefCnt == 0);
|
||||
|
||||
plist->m_nAllocState = CPageList::AllocState::EPLAS_ALLOCATED;
|
||||
|
||||
// set up the pages
|
||||
CPage* prev = nullptr;
|
||||
CPage* page = nullptr;
|
||||
for (int i = 0; i < pages_allocated; i++) {
|
||||
page = pages[i];
|
||||
page->m_pPageList = plist;
|
||||
page->m_pPrevPage = prev;
|
||||
if (prev) {
|
||||
prev->m_pNextPage = page;
|
||||
}
|
||||
page->m_nAllocState = 1;
|
||||
page->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~page->mask);
|
||||
prev = page;
|
||||
}
|
||||
page->m_pNextPage = nullptr;
|
||||
plist->m_nNumPages = count;
|
||||
plist->m_pLastPage = page;
|
||||
plist->m_pFirstPage = pages[0];
|
||||
return plist;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Allocate page_count more pages, add them to the end of the list. This can be used to get more
|
||||
* pages for the DVD driver to write to.
|
||||
*/
|
||||
CPageList* CPageManager::GrowPageList(jak3::CPageList* in, int page_count) {
|
||||
ASSERT(in);
|
||||
ASSERT(in->m_nAllocState == CPageList::AllocState::EPLAS_ALLOCATED);
|
||||
ASSERT(page_count >= 0);
|
||||
|
||||
if (page_count > m_CCache.m_nNumFreePages) {
|
||||
// if we're out of pages, use RecoverPages to discard pages that we've already read, but
|
||||
// nobody is using yet. We'll be able to read them from the DVD again.
|
||||
lg::warn("Recovering pages - {} requested in AllocPageList, but only {} available", page_count,
|
||||
m_CCache.m_nNumFreePages);
|
||||
RecoverPages(page_count);
|
||||
ASSERT(m_CCache.m_nNumFreePages >= page_count);
|
||||
}
|
||||
|
||||
CPage* pages[CCache::kNumPages];
|
||||
int next_page_to_check = 0;
|
||||
int pages_allocated = 0;
|
||||
while (pages_allocated != page_count) {
|
||||
if (next_page_to_check >= CCache::kNumPages) {
|
||||
break;
|
||||
}
|
||||
int page_idx = alloc_bitmask(&m_CCache.m_nAllocatedMask, CCache::kNumPages, next_page_to_check);
|
||||
ASSERT(page_idx >= 0); // otherwise need to handle this better
|
||||
next_page_to_check = page_idx + 1;
|
||||
pages[pages_allocated] = &m_CCache.m_Pages[page_idx];
|
||||
pages_allocated++;
|
||||
}
|
||||
|
||||
if (pages_allocated != page_count) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_CCache.m_nNumFreePages -= pages_allocated;
|
||||
|
||||
// set up the pages
|
||||
CPage* prev = nullptr;
|
||||
CPage* page = nullptr;
|
||||
for (int i = 0; i < pages_allocated; i++) {
|
||||
page = pages[i];
|
||||
ASSERT(page);
|
||||
ASSERT(page->m_nAllocState == 0);
|
||||
page->m_pPageList = in;
|
||||
page->m_pPrevPage = prev;
|
||||
if (prev) {
|
||||
prev->m_pNextPage = page;
|
||||
}
|
||||
page->m_nAllocState = 1;
|
||||
page->input_state = CPage::State::UNMAKRED;
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~page->mask);
|
||||
prev = page;
|
||||
}
|
||||
page->m_pNextPage = nullptr;
|
||||
|
||||
// suspendintr
|
||||
if (in->m_nNumPages == 0) {
|
||||
ASSERT(!in->m_pFirstPage);
|
||||
ASSERT(!in->m_pLastPage);
|
||||
ASSERT(!in->m_pFirstActivePage);
|
||||
ASSERT(!in->m_pLastActivePage);
|
||||
ASSERT(!in->m_pCurrentActivePage);
|
||||
in->m_pFirstPage = pages[0];
|
||||
} else {
|
||||
auto* old_end = in->m_pLastPage;
|
||||
ASSERT(!old_end->m_pNextPage);
|
||||
old_end->m_pNextPage = pages[0];
|
||||
pages[0]->m_pPrevPage = old_end;
|
||||
}
|
||||
in->m_pLastPage = page;
|
||||
in->m_nNumPages += pages_allocated;
|
||||
// resume intr
|
||||
return in;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Return a PageList and its pages to the CCache. If the PageList is still referenced, the freeing
|
||||
* will be deferred until GC.
|
||||
*/
|
||||
void CPageManager::FreePageList(jak3::CPageList* list) {
|
||||
ASSERT(list);
|
||||
ASSERT(list->m_nAllocState != CPageList::AllocState::EPLAS_FREE);
|
||||
// suspend itr
|
||||
list->m_nAllocState = CPageList::AllocState::FREE_PENDING;
|
||||
// resume intr
|
||||
|
||||
if (list->m_nDmaRefCnt || list->m_nPageRefCnt) {
|
||||
lg::warn("Skipping free since list is referenced!");
|
||||
return;
|
||||
}
|
||||
|
||||
// loop over pages, clearing them.
|
||||
CPage* page = list->m_pFirstPage;
|
||||
int pages_count = 0;
|
||||
int pages[CCache::kNumPages];
|
||||
|
||||
while (page) {
|
||||
u32 page_slot = page - m_CCache.m_Pages;
|
||||
ASSERT(page_slot < CCache::kNumPages);
|
||||
pages[pages_count] = page_slot;
|
||||
pages_count++;
|
||||
auto* next = page->m_pNextPage;
|
||||
page->m_pPageList = nullptr;
|
||||
page->m_pPrevPage = nullptr;
|
||||
page->m_pNextPage = nullptr;
|
||||
page->m_nAllocState = 0;
|
||||
page->input_state = CPage::State::UNMAKRED;
|
||||
ASSERT(!page->m_nPageRefCount);
|
||||
ASSERT(!page->m_nDmaRefCount);
|
||||
ClearEventFlag(get_page_manager()->m_CCache.m_PagesFilledEventFlag, ~page->mask);
|
||||
page = next;
|
||||
}
|
||||
|
||||
// clear list
|
||||
if (pages_count != list->m_nNumPages) {
|
||||
lg::die("Paging error: found {} pages out of {} in FreePageList", pages_count,
|
||||
list->m_nNumPages);
|
||||
}
|
||||
ASSERT(pages_count == list->m_nNumPages);
|
||||
list->m_pFirstActivePage = nullptr;
|
||||
list->m_nNumPages = 0;
|
||||
list->m_pLastActivePage = nullptr;
|
||||
list->m_pCurrentActivePage = nullptr;
|
||||
list->m_nNumActivePages = 0;
|
||||
list->m_nNumUnsteppedPages = 0;
|
||||
list->m_nAllocState = CPageList::AllocState::EPLAS_FREE;
|
||||
m_CCache.m_nNumFreePages += pages_count;
|
||||
ASSERT(m_CCache.m_nNumFreePages <= CCache::kNumPages);
|
||||
list->m_pFirstPage = nullptr;
|
||||
list->m_pLastPage = nullptr;
|
||||
// unset bits in allocated mask
|
||||
while (0 < pages_count) {
|
||||
pages_count--;
|
||||
m_CCache.m_nAllocatedMask &= ~(1 << pages[pages_count]);
|
||||
}
|
||||
// unset bit for the pagelist itself
|
||||
u32 plist_idx = list - m_CCache.m_PageLists;
|
||||
ASSERT(plist_idx >= 0 && plist_idx < CCache::kNumPageLists);
|
||||
m_CCache.m_nPagelistAllocatedMask &= ~(1 << plist_idx);
|
||||
}
|
||||
|
||||
int CPageManager::RecoverPages(int) {
|
||||
ASSERT_NOT_REACHED(); // TODO, this looks at pristack
|
||||
}
|
||||
|
||||
/*!
|
||||
* Call GarbageCollect on all allocate CPageLists.
|
||||
*/
|
||||
void CPageManager::GarbageCollect() {
|
||||
for (u32 i = 0; i < CCache::kNumPageLists; i++) {
|
||||
if (m_CCache.m_nPagelistAllocatedMask & (1 << i)) {
|
||||
GarbageCollectPageList(&m_CCache.m_PageLists[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Do garbage collection of pages and page lists.
|
||||
*/
|
||||
void CPageManager::GarbageCollectPageList(jak3::CPageList* list) {
|
||||
ASSERT(list);
|
||||
list->GarbageCollect();
|
||||
// if we tried to free the list in the past, but failed, try to do it again now.
|
||||
if (list->m_nAllocState == CPageList::AllocState::FREE_PENDING) {
|
||||
FreePageList(list);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
181
game/overlord/jak3/pagemanager.h
Normal file
181
game/overlord/jak3/pagemanager.h
Normal file
@ -0,0 +1,181 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_pagemanager();
|
||||
|
||||
/*!
|
||||
* CPages Overview
|
||||
*
|
||||
* Data is read from the DVD driver into CPages. The CPages are then given to consumers.
|
||||
* Each file that's opened has an associated CPageList.
|
||||
*
|
||||
* The CPageList is a linked list of pages. Within this linked list, there is a section of "active
|
||||
* pages" which have been filled by the DVD driver. There is also the "current active page", which
|
||||
* is the page that the user will read from next. Note that pages before the current active page
|
||||
* and still be referenced by the user, but they should keep a nonzero reference count so the CPage
|
||||
* is not Garbage Collected.
|
||||
*
|
||||
* By default, each active page will have a ref count of 1.
|
||||
*
|
||||
* The CBuffer object owned by CBaseFile uses memory managed by this CPage system.
|
||||
*
|
||||
* The pages are preallocated by the CPageManager, which gives out pages as needed.
|
||||
*/
|
||||
|
||||
struct CPage;
|
||||
struct CPageList;
|
||||
struct ISO_Hdr;
|
||||
|
||||
constexpr int kPageSize = 0x8000;
|
||||
constexpr int kPageStride = 0x8010;
|
||||
|
||||
/*!
|
||||
* A CBuffer stores file data in pages, and tracks the progress through the page data as it is fed
|
||||
* into the ISO data handling system.
|
||||
*/
|
||||
struct CBuffer {
|
||||
// The pages in this buffer are managed by a PageList
|
||||
CPageList* m_pPageList = nullptr;
|
||||
|
||||
// The ISO command that requested us to load this data
|
||||
ISO_Hdr* m_pIsoCmd = nullptr;
|
||||
|
||||
// Current progress through the data (todo: load or process?)
|
||||
uint8_t* m_pCurrentData = nullptr;
|
||||
|
||||
// First address in the current page
|
||||
uint8_t* m_pCurrentPageStart = nullptr;
|
||||
|
||||
// todo
|
||||
int m_nDataLength = 0;
|
||||
|
||||
enum class BufferType {
|
||||
// this is a really confusing enum...
|
||||
EBT_FREE = 0, // there is no buffer allocate
|
||||
NORMAL = 1, // a buffer sized for non-VAG stream operations (several possible sizes)
|
||||
VAG = 2, // a buffer size for VAG streams (larger than normal sizes)
|
||||
REQUEST_NORMAL = 3, // argument passed to InitBuffer to get a normal buffer
|
||||
REQUEST_VAG = 4, // argument passed to InitBuffer to get a VAG size buffer
|
||||
} m_eBufferType = BufferType::EBT_FREE;
|
||||
|
||||
// Try to have at least this many pages filled
|
||||
int m_nMinNumPages = 0;
|
||||
|
||||
// Don't fill more than this number of pages
|
||||
int m_nMaxNumPages = 0;
|
||||
|
||||
void AddData(int len);
|
||||
void AdvanceCurrentData(int len);
|
||||
};
|
||||
|
||||
/*!
|
||||
* List of pages for a file.
|
||||
*/
|
||||
struct CPageList {
|
||||
CPageList() = default; // ???
|
||||
|
||||
// list of all pages
|
||||
CPage* m_pFirstPage = nullptr;
|
||||
CPage* m_pLastPage = nullptr;
|
||||
|
||||
// list of active pages. This is part of the all page list
|
||||
CPage* m_pFirstActivePage = nullptr;
|
||||
CPage* m_pLastActivePage = nullptr;
|
||||
|
||||
// page that the application is currently reading from
|
||||
CPage* m_pCurrentActivePage = nullptr;
|
||||
|
||||
// total number of CPage, including both active/inactive
|
||||
int m_nNumPages = 0;
|
||||
|
||||
// number of cpages in the active page list
|
||||
int m_nNumActivePages = 0;
|
||||
|
||||
// number of CPages remaining for the user, including the current active page
|
||||
int m_nNumUnsteppedPages = 0;
|
||||
|
||||
// Reference counters to know if this data is still needed or not.
|
||||
int m_nPageRefCnt = 0;
|
||||
int m_nDmaRefCnt = 0;
|
||||
|
||||
enum class AllocState {
|
||||
EPLAS_FREE = 0,
|
||||
EPLAS_ALLOCATED = 1,
|
||||
FREE_PENDING = 2, // FreePageList called, but no
|
||||
} m_nAllocState = AllocState::EPLAS_FREE;
|
||||
|
||||
CPage* StepActivePage();
|
||||
CPage* AddActivePages(int num_pages);
|
||||
int RemoveActivePages(int num_pages);
|
||||
void CancelActivePages();
|
||||
void GarbageCollect();
|
||||
};
|
||||
|
||||
/*!
|
||||
* A single page, pointing to a contiguous buffer of file data.
|
||||
*/
|
||||
struct CPage {
|
||||
CPage(uint8_t* page_mem_start, uint8_t* page_mem_end, int page_idx);
|
||||
CPage() = default; // ???
|
||||
CPage* m_pNextPage = nullptr;
|
||||
CPage* m_pPrevPage = nullptr;
|
||||
CPageList* m_pPageList = nullptr;
|
||||
int m_nPageRefCount = 0;
|
||||
int m_nDmaRefCount = 0;
|
||||
int m_nAllocState = 0;
|
||||
enum class State {
|
||||
UNMAKRED = 0,
|
||||
ACTIVE = 1,
|
||||
READING = 2,
|
||||
READ_DONE = 3
|
||||
} input_state = State::UNMAKRED;
|
||||
uint8_t* m_pPageMemStart = nullptr;
|
||||
uint8_t* m_pPageMemEnd = nullptr;
|
||||
u32 m_nPageIdx = 0;
|
||||
u32 mask = 0;
|
||||
|
||||
int AddRef();
|
||||
int ReleaseRef();
|
||||
int AddDmaRef();
|
||||
int ReleaseDmaRef();
|
||||
void FromPagesCopy(uint8_t* in, uint8_t* dest, s32 size);
|
||||
};
|
||||
|
||||
/*!
|
||||
* CCache contains the actual CPage/CPageList objects to be given out to files.
|
||||
*/
|
||||
struct CCache {
|
||||
static constexpr int kNumPages = 29;
|
||||
static constexpr int kNumPageLists = 29;
|
||||
static constexpr int kAllPagesMask = (1 << kNumPages) - 1;
|
||||
|
||||
CCache();
|
||||
void Initialize();
|
||||
void* m_paCache = nullptr;
|
||||
CPageList m_PageLists[kNumPageLists];
|
||||
CPage m_Pages[kNumPages];
|
||||
int m_nNumFreePages = 0;
|
||||
u32 m_nAllocatedMask;
|
||||
u32 m_nPagelistAllocatedMask;
|
||||
int m_PagesFilledEventFlag;
|
||||
int m_PendingMask;
|
||||
};
|
||||
|
||||
struct CPageManager {
|
||||
CCache m_CCache;
|
||||
|
||||
CPageList* GrowPageList(CPageList* in, int page_count);
|
||||
CPageList* AllocPageList(int count, bool flag);
|
||||
CPageList* AllocPageListBytes(int bytes, bool flag);
|
||||
void FreePageList(CPageList* list);
|
||||
void WaitForPagesFilled(u32 mask);
|
||||
void Initialize();
|
||||
int RecoverPages(int k);
|
||||
void GarbageCollect();
|
||||
void GarbageCollectPageList(CPageList* list);
|
||||
};
|
||||
|
||||
CPageManager* get_page_manager();
|
||||
} // namespace jak3
|
50
game/overlord/jak3/ramdisk.cpp
Normal file
50
game/overlord/jak3/ramdisk.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "ramdisk.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/iso_api.h"
|
||||
#include "game/overlord/jak3/rpc_interface.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
constexpr int kRamdiskBufSize = 512;
|
||||
u8 gRamdiskRpcBuf[kRamdiskBufSize];
|
||||
|
||||
void jak3_overlord_init_globals_ramdisk() {}
|
||||
|
||||
/*!
|
||||
* RPC Handler for "load to ee" rpc.
|
||||
*/
|
||||
void* RPC_LoadToEE(unsigned int fno, void* msg_ptr, int) {
|
||||
switch (fno) {
|
||||
case LoadToEEFno::LOAD_FILE: {
|
||||
RpcLoadToEEMsg* msg = (RpcLoadToEEMsg*)(msg_ptr);
|
||||
auto f = FindISOFile(msg->name);
|
||||
ASSERT(f);
|
||||
LoadISOFileToEE(f, msg->addr, msg->length);
|
||||
} break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 LoadToEE_RPC_Thread() {
|
||||
using namespace iop;
|
||||
|
||||
sceSifQueueData dq;
|
||||
sceSifServeData serve;
|
||||
|
||||
// set up RPC
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, RpcId::LoadToEE, RPC_LoadToEE, gRamdiskRpcBuf, kRamdiskBufSize, nullptr,
|
||||
nullptr, &dq);
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
} // namespace jak3
|
10
game/overlord/jak3/ramdisk.h
Normal file
10
game/overlord/jak3/ramdisk.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_ramdisk();
|
||||
|
||||
u32 LoadToEE_RPC_Thread();
|
||||
|
||||
} // namespace jak3
|
280
game/overlord/jak3/rpc_interface.h
Normal file
280
game/overlord/jak3/rpc_interface.h
Normal file
@ -0,0 +1,280 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
/*!
|
||||
* This file has structs that are shared between GOAL and Overlord.
|
||||
* The memory layout of these structs should not be changed.
|
||||
*/
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
struct SoundStreamName {
|
||||
char chars[48];
|
||||
};
|
||||
|
||||
// vblank message
|
||||
|
||||
struct SoundIOPInfo {
|
||||
u32 frame; // 0
|
||||
s32 strpos; // 4
|
||||
u32 str_id; // 8
|
||||
u32 freemem; // 12
|
||||
u8 chinfo[48]; // 16
|
||||
u32 freemem2; // 64
|
||||
u32 nocd; // 68
|
||||
u32 dirtycd; // 72
|
||||
u32 diskspeed[2]; // 76
|
||||
u32 lastspeed; // 84
|
||||
s32 dupseg; // 88
|
||||
s32 times[41]; // 92
|
||||
u32 times_seq; // 256
|
||||
u32 iop_ticks; // 260
|
||||
u32 pad0[2];
|
||||
u32 stream_position[4]; // 272
|
||||
u32 stream_status[4]; // 288
|
||||
SoundStreamName stream_name[4];
|
||||
u32 stream_id[4];
|
||||
u8 music_register[17];
|
||||
// s8 music_excite;
|
||||
char ramdisk_name[16];
|
||||
u32 pad[11];
|
||||
char sound_bank0[16];
|
||||
char sound_bank1[16];
|
||||
char sound_bank2[16];
|
||||
char sound_bank3[16];
|
||||
char sound_bank4[16];
|
||||
char sound_bank5[16];
|
||||
char sound_bank6[16];
|
||||
char sound_bank7[16];
|
||||
};
|
||||
// static_assert(offsetof(SoundIOPInfo, stream_name) == 304);
|
||||
// static_assert(offsetof(SoundIOPInfo, stream_id) == 496);
|
||||
// static_assert(offsetof(SoundIOPInfo, music_register) == 512);
|
||||
// static_assert(offsetof(SoundIOPInfo, ramdisk_name) == 529);
|
||||
// static_assert(offsetof(SoundIOPInfo, sound_bank0) == 592);
|
||||
static_assert(sizeof(SoundIOPInfo) == 0x2d0);
|
||||
|
||||
// static_assert(sizeof(SoundIOPInfo) == 288);
|
||||
|
||||
// Common
|
||||
|
||||
enum RpcId {
|
||||
Player = 0xfab0, // sound effects playback
|
||||
Loader = 0xfab1, // sound effects loading.
|
||||
LoadToEE = 0xfab2, // was ramdisk, now just a simple way to load a file to EE memory.
|
||||
DGO = 0xfab3, // level/engine .DGO loading
|
||||
STR = 0xfab4, // loading .str files of animations or other streamed data
|
||||
PLAY = 0xfab5, // playing and queueing vag streams
|
||||
};
|
||||
|
||||
// RAMDISK RPC (renamed to LoadToEE for jak 3, kinda)
|
||||
struct RpcLoadToEEMsg {
|
||||
u32 unk;
|
||||
u32 addr;
|
||||
u32 unk2;
|
||||
u32 length;
|
||||
char name[16];
|
||||
};
|
||||
static_assert(sizeof(RpcLoadToEEMsg) == 32);
|
||||
|
||||
enum LoadToEEFno {
|
||||
LOAD_FILE = 4,
|
||||
};
|
||||
|
||||
// DGO RPC
|
||||
|
||||
struct RPC_Dgo_Cmd {
|
||||
uint16_t rsvd;
|
||||
uint16_t status;
|
||||
uint32_t buffer1;
|
||||
uint32_t buffer2;
|
||||
uint32_t buffer_heap_top;
|
||||
char name[16];
|
||||
int32_t cgo_id;
|
||||
uint8_t pad[28];
|
||||
};
|
||||
static_assert(sizeof(RPC_Dgo_Cmd) == 0x40);
|
||||
|
||||
enum DgoFno {
|
||||
LOAD = 0,
|
||||
LOAD_NEXT = 1,
|
||||
CANCEL = 2,
|
||||
};
|
||||
|
||||
// STR RPC
|
||||
struct RPC_Str_Cmd {
|
||||
u16 rsvd;
|
||||
u16 result; // 2
|
||||
u32 address;
|
||||
s32 section; // 8
|
||||
u32 maxlen;
|
||||
u32 dummy[4];
|
||||
char basename[48]; // 32
|
||||
};
|
||||
|
||||
// PLAYER/LOADER RPCS
|
||||
|
||||
struct RPC_Play_Cmd {
|
||||
u16 rsvd;
|
||||
u16 result;
|
||||
u32 address;
|
||||
u32 section;
|
||||
u32 maxlen;
|
||||
u32 id[4];
|
||||
SoundStreamName names[4];
|
||||
u32 pad[8];
|
||||
};
|
||||
|
||||
struct SoundName {
|
||||
char data[16];
|
||||
};
|
||||
|
||||
enum class SoundCommand : u16 {
|
||||
// IOP_STORE = 0,
|
||||
// IOP_FREE = 1,
|
||||
LOAD_BANK = 2,
|
||||
// LOAD_BANK_FROM_IOP = 3,
|
||||
// LOAD_BANK_FROM_EE = 4,
|
||||
LOAD_MUSIC = 5,
|
||||
UNLOAD_BANK = 6,
|
||||
PLAY = 7,
|
||||
PAUSE_SOUND = 8,
|
||||
STOP_SOUND = 9,
|
||||
CONTINUE_SOUND = 10,
|
||||
SET_PARAM = 11,
|
||||
SET_MASTER_VOLUME = 12,
|
||||
PAUSE_GROUP = 13,
|
||||
STOP_GROUP = 14,
|
||||
CONTINUE_GROUP = 15,
|
||||
GET_IRX_VERSION = 16,
|
||||
// SET_FALLOFF_CURVE = 17,
|
||||
// SET_SOUND_FALLOFF = 18,
|
||||
// RELOAD_INFO = 19,
|
||||
SET_LANGUAGE = 20,
|
||||
// SET_FLAVA = 21,
|
||||
SET_MIDI_REG = 22,
|
||||
SET_REVERB = 23,
|
||||
SET_EAR_TRANS = 24,
|
||||
SHUTDOWN = 25,
|
||||
// LIST_SOUNDS = 26,
|
||||
UNLOAD_MUSIC = 27,
|
||||
SET_FPS = 28,
|
||||
// BOOT_LOAD = 29,
|
||||
// GAME_LOAD = 30,
|
||||
// NUM_TESTS = 31,
|
||||
// NUM_TESTRUNS = 32,
|
||||
// NUM_SECTORS = 33,
|
||||
// NUM_STREAMSECTORS = 34,
|
||||
// NUM_STREMBANKS = 35,
|
||||
// TRACK_PITCH = 36,
|
||||
// LINVEL_NOM = 37,
|
||||
CANCEL_DGO = 49,
|
||||
SET_STEREO_MODE = 50,
|
||||
};
|
||||
|
||||
struct SoundPlayParams {
|
||||
u16 mask;
|
||||
s16 pitch_mod;
|
||||
s16 bend;
|
||||
s16 fo_min;
|
||||
s16 fo_max;
|
||||
s8 fo_curve;
|
||||
s8 priority;
|
||||
s32 volume;
|
||||
s32 trans[3];
|
||||
u8 group;
|
||||
u8 reg[3];
|
||||
};
|
||||
|
||||
struct Rpc_Player_Base_Cmd {
|
||||
u16 rsvd1 = 0;
|
||||
SoundCommand command;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Base_Cmd) == 4);
|
||||
|
||||
struct Rpc_Player_Sound_Cmd : public Rpc_Player_Base_Cmd {
|
||||
s32 sound_id = 0;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Sound_Cmd) == 8);
|
||||
|
||||
struct Rpc_Player_Group_Cmd : public Rpc_Player_Base_Cmd {
|
||||
u32 group = 0;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Group_Cmd) == 8);
|
||||
|
||||
struct Rpc_Player_Play_Cmd : public Rpc_Player_Sound_Cmd {
|
||||
s32 pad[2];
|
||||
SoundName name;
|
||||
SoundPlayParams params;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Play_Cmd) == 0x40);
|
||||
|
||||
struct Rpc_Player_Set_Param_Cmd : public Rpc_Player_Sound_Cmd {
|
||||
SoundPlayParams params;
|
||||
s32 auto_time;
|
||||
s32 auto_from;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Set_Param_Cmd) == 0x30);
|
||||
|
||||
struct Rpc_Player_Set_Master_Volume_Cmd : public Rpc_Player_Group_Cmd {
|
||||
s32 volume;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Set_Master_Volume_Cmd) == 12);
|
||||
|
||||
struct Rpc_Player_Set_Ear_Trans_Cmd : public Rpc_Player_Base_Cmd {
|
||||
s32 ear_trans1[3];
|
||||
s32 ear_trans0[3];
|
||||
s32 ear_trans[3];
|
||||
s32 cam_forward[3];
|
||||
s32 cam_left[3];
|
||||
s32 cam_scale;
|
||||
s32 cam_inverted;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Set_Ear_Trans_Cmd) == 0x48);
|
||||
|
||||
struct Rpc_Player_Set_Fps_Cmd : public Rpc_Player_Base_Cmd {
|
||||
u8 fps;
|
||||
u8 pad;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Set_Fps_Cmd) == 5 + 1);
|
||||
|
||||
struct Rpc_Player_Cancel_Dgo_Cmd : public Rpc_Player_Group_Cmd {
|
||||
u32 id;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Player_Cancel_Dgo_Cmd) == 12);
|
||||
|
||||
struct Rpc_Loader_Bank_Cmd : public Rpc_Player_Base_Cmd {
|
||||
u32 pad[3];
|
||||
SoundName bank_name;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Loader_Bank_Cmd) == 32);
|
||||
|
||||
struct Rpc_Loader_Load_Bank_Cmd : public Rpc_Loader_Bank_Cmd {
|
||||
u32 ee_addr;
|
||||
u32 mode;
|
||||
u32 priority;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Loader_Load_Bank_Cmd) == 0x2c);
|
||||
|
||||
struct Rpc_Loader_Get_Irx_Version : public Rpc_Player_Base_Cmd {
|
||||
u32 major;
|
||||
u32 minor;
|
||||
u32 ee_addr;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Loader_Get_Irx_Version) == 16);
|
||||
|
||||
struct Rpc_Loader_Set_Language : public Rpc_Player_Base_Cmd {
|
||||
u32 lang;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Loader_Set_Language) == 8);
|
||||
|
||||
struct Rpc_Loader_Set_Stereo_Mode : public Rpc_Player_Base_Cmd {
|
||||
s32 mode;
|
||||
};
|
||||
static_assert(sizeof(Rpc_Loader_Set_Stereo_Mode) == 8);
|
||||
|
||||
constexpr int kPlayerCommandStride = 0x50;
|
||||
constexpr int kLoaderCommandStride = 0x50;
|
||||
|
||||
} // namespace jak3
|
167
game/overlord/jak3/sbank.cpp
Normal file
167
game/overlord/jak3/sbank.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
#include "sbank.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
constexpr int kNumBanks = 8;
|
||||
SoundBankInfo* gBanks[kNumBanks];
|
||||
|
||||
SoundBankInfo gCommonBank;
|
||||
SoundBankInfo gModeBank;
|
||||
SoundBankInfo gLevel0Bank, gLevel0hBank;
|
||||
SoundBankInfo gLevel1Bank, gLevel1hBank;
|
||||
SoundBankInfo gLevel2Bank, gLevel2hBank;
|
||||
|
||||
void jak3_overlord_init_globals_sbank() {
|
||||
gBanks[0] = &gCommonBank;
|
||||
gBanks[1] = &gModeBank;
|
||||
gBanks[2] = &gLevel0Bank;
|
||||
gBanks[3] = &gLevel0hBank;
|
||||
gBanks[4] = &gLevel1Bank;
|
||||
gBanks[5] = &gLevel1hBank;
|
||||
gBanks[6] = &gLevel2Bank;
|
||||
gBanks[7] = &gLevel2hBank;
|
||||
}
|
||||
|
||||
void InitBanks() {
|
||||
for (int i = 0; i < kNumBanks; i++) {
|
||||
auto* bank = gBanks[i];
|
||||
bank->in_use = 0;
|
||||
bank->snd_handle = nullptr;
|
||||
bank->loaded = false;
|
||||
bank->idx = i;
|
||||
bank->unk0 = 0;
|
||||
}
|
||||
|
||||
strncpyz(gBanks[0]->m_name2, "common", 0x10);
|
||||
gBanks[0]->m_nSpuMemSize = 0xbbe40;
|
||||
gBanks[0]->m_nSpuMemLoc = 0x1d1c0;
|
||||
|
||||
strncpyz(gBanks[1]->m_name2, "mode", 0x10);
|
||||
gBanks[1]->m_nSpuMemSize = 0x25400;
|
||||
gBanks[1]->m_nSpuMemLoc = 0xe0000;
|
||||
|
||||
strncpyz(gBanks[2]->m_name2, "level0", 0x10);
|
||||
gBanks[2]->m_nSpuMemLoc = 0x105400;
|
||||
gBanks[2]->m_nSpuMemSize = 0x51400;
|
||||
|
||||
strncpyz(gBanks[3]->m_name2, "level0h", 0x10);
|
||||
gBanks[3]->m_nSpuMemLoc = 0x12de00;
|
||||
gBanks[3]->m_nSpuMemSize = 0x28a00;
|
||||
|
||||
strncpyz(gBanks[4]->m_name2, "level1", 0x10);
|
||||
gBanks[4]->m_nSpuMemSize = 0x51400;
|
||||
gBanks[4]->m_nSpuMemLoc = 0x156800;
|
||||
|
||||
strncpyz(gBanks[5]->m_name2, "level1h", 0x10);
|
||||
gBanks[5]->m_nSpuMemSize = 0x28a00;
|
||||
gBanks[5]->m_nSpuMemLoc = 0x17f200;
|
||||
|
||||
strncpyz(gBanks[6]->m_name2, "level2", 0x10);
|
||||
gBanks[6]->m_nSpuMemSize = 0x51400;
|
||||
gBanks[6]->m_nSpuMemLoc = 0x1a7c00;
|
||||
|
||||
strncpyz(gBanks[7]->m_name2, "level2h", 0x10);
|
||||
gBanks[7]->m_nSpuMemSize = 0x28a00;
|
||||
gBanks[7]->m_nSpuMemLoc = 0x1d0600;
|
||||
}
|
||||
|
||||
SoundBankInfo* AllocateBankName(const char* name, u32 mode) {
|
||||
int iVar1;
|
||||
int mem_sz;
|
||||
SoundBankInfo** ppSVar2;
|
||||
int iVar3;
|
||||
int iVar4;
|
||||
SoundBankInfo* pSVar5;
|
||||
int iVar6;
|
||||
int iVar6_d4 = 2;
|
||||
SoundBankInfo* bank = nullptr;
|
||||
|
||||
// handle common case
|
||||
if (memcmp(name, "common", 7) == 0 || memcmp(name, "commonj", 8) == 0) {
|
||||
if (!gBanks[0]->in_use) {
|
||||
bank = gBanks[0];
|
||||
}
|
||||
} else if (memcmp(name, "mode", 4) == 0) {
|
||||
if (!gBanks[1]->in_use) {
|
||||
bank = gBanks[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == 4) {
|
||||
for (int bank_idx = 2; bank_idx < kNumBanks; bank_idx += 2) {
|
||||
if (!gBanks[bank_idx]->in_use && !gBanks[bank_idx + 1]->in_use) {
|
||||
bank = gBanks[bank_idx];
|
||||
bank->m_nSpuMemSize = 0x51400;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (mode > 3 && (mode - 6u < 3)) { // wtf
|
||||
iVar1 = 2;
|
||||
// iVar6 = 8;
|
||||
iVar6_d4 = 2;
|
||||
while (gBanks[iVar6_d4]->in_use == 0 || gBanks[iVar6_d4]->mode != mode) {
|
||||
auto* sbi = gBanks[iVar6_d4 + 1];
|
||||
iVar6 = iVar6 + 8;
|
||||
if (((sbi->in_use != 0) && (iVar4 = iVar1, sbi->mode == mode)) ||
|
||||
(iVar1 = iVar1 + 2, iVar4 = -1, 7 < iVar1))
|
||||
goto LAB_0000c2fc;
|
||||
}
|
||||
iVar4 = iVar1 + 1;
|
||||
LAB_0000c2fc:
|
||||
if (iVar4 < 0) {
|
||||
iVar1 = 2;
|
||||
ppSVar2 = gBanks;
|
||||
LAB_0000c36c:
|
||||
ppSVar2 = ppSVar2 + 2;
|
||||
pSVar5 = *ppSVar2;
|
||||
iVar1 = iVar1 + 2;
|
||||
if ((pSVar5->in_use != 0) || (ppSVar2[1]->in_use != 0))
|
||||
goto LAB_0000c39c;
|
||||
mem_sz = 0x28a00;
|
||||
pSVar5->m_nSpuMemSize = mem_sz;
|
||||
bank = pSVar5;
|
||||
goto LAB_0000c3a4;
|
||||
}
|
||||
pSVar5 = gBanks[iVar4];
|
||||
if (pSVar5->in_use == 0) {
|
||||
gBanks[iVar1]->m_nSpuMemSize = 0x28a00;
|
||||
bank = pSVar5;
|
||||
}
|
||||
}
|
||||
LAB_0000c3a4:
|
||||
if (bank) {
|
||||
bank->mode = mode;
|
||||
bank->snd_handle = nullptr;
|
||||
bank->unk0 = 0;
|
||||
}
|
||||
return bank;
|
||||
|
||||
LAB_0000c39c:
|
||||
if (7 < iVar1)
|
||||
goto LAB_0000c3a4;
|
||||
goto LAB_0000c36c;
|
||||
}
|
||||
|
||||
SoundBankInfo* LookupBank(const char* name) {
|
||||
for (int i = kNumBanks; i-- > 0;) {
|
||||
if (memcmp(name, gBanks[i]->m_name1, 16) == 0) {
|
||||
return gBanks[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int GetFalloffCurve(int x) {
|
||||
if (x < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (x == 0 || 0x28 < x) {
|
||||
x = 2;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
} // namespace jak3
|
29
game/overlord/jak3/sbank.h
Normal file
29
game/overlord/jak3/sbank.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
struct SoundBankInfo {
|
||||
// int m_name1[4];
|
||||
char m_name1[16];
|
||||
char m_name2[16];
|
||||
int m_nSpuMemLoc = 0;
|
||||
int m_nSpuMemSize = 0;
|
||||
// s32 snd_handle = 0;
|
||||
snd::BankHandle snd_handle = nullptr;
|
||||
u8 in_use = 0;
|
||||
u8 loaded = 0;
|
||||
u8 mode = 0;
|
||||
u8 idx = 0;
|
||||
int unk0 = 0;
|
||||
};
|
||||
|
||||
void jak3_overlord_init_globals_sbank();
|
||||
void InitBanks();
|
||||
SoundBankInfo* LookupBank(const char* name);
|
||||
SoundBankInfo* AllocateBankName(const char* name, u32 mode);
|
||||
extern SoundBankInfo* gBanks[8];
|
||||
} // namespace jak3
|
19
game/overlord/jak3/soundcommon.cpp
Normal file
19
game/overlord/jak3/soundcommon.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "soundcommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_soundcommon() {}
|
||||
|
||||
// Only for use with 16 character sound names!
|
||||
void strcpy_toupper(char* dest, const char* source) {
|
||||
// clear the dest string
|
||||
memset(dest, 0, 16);
|
||||
std::string string(source);
|
||||
std::transform(string.begin(), string.end(), string.begin(), ::toupper);
|
||||
std::replace(string.begin(), string.end(), '-', '_');
|
||||
string.copy(dest, 16);
|
||||
}
|
||||
} // namespace jak3
|
7
game/overlord/jak3/soundcommon.h
Normal file
7
game/overlord/jak3/soundcommon.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_soundcommon();
|
||||
|
||||
void strcpy_toupper(char* dest, const char* src);
|
||||
} // namespace jak3
|
1066
game/overlord/jak3/spustreams.cpp
Normal file
1066
game/overlord/jak3/spustreams.cpp
Normal file
File diff suppressed because it is too large
Load Diff
14
game/overlord/jak3/spustreams.h
Normal file
14
game/overlord/jak3/spustreams.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
|
||||
namespace jak3 {
|
||||
struct ISO_Hdr;
|
||||
struct ISO_VAGCommand;
|
||||
void jak3_overlord_init_globals_spustreams();
|
||||
EIsoStatus ProcessVAGData(ISO_Hdr* msg);
|
||||
void StopVagStream(ISO_VAGCommand* cmd);
|
||||
u32 GetSpuRamAddress(ISO_VAGCommand* cmd);
|
||||
} // namespace jak3
|
604
game/overlord/jak3/srpc.cpp
Normal file
604
game/overlord/jak3/srpc.cpp
Normal file
@ -0,0 +1,604 @@
|
||||
#include "srpc.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/iso_api.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/overlord/jak3/rpc_interface.h"
|
||||
#include "game/overlord/jak3/sbank.h"
|
||||
#include "game/overlord/jak3/soundcommon.h"
|
||||
#include "game/overlord/jak3/spustreams.h"
|
||||
#include "game/overlord/jak3/ssound.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/overlord/jak3/vblank_handler.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
using namespace iop;
|
||||
|
||||
// This file has two RPCs: PLAYER and LOADER
|
||||
// Generally, PLAYER receives commands to play/pause sound effects or streams, which complete
|
||||
// quickly.
|
||||
|
||||
// LOADER will load soundbanks, and can take some time to complete - likely why it is moved
|
||||
// into its own RPC, to avoid having soundbank loads block playback of other sounds.
|
||||
|
||||
constexpr int kPlayerBufSize = 0x50 * 128;
|
||||
static uint8_t s_anRPC_PlayerBuf[kPlayerBufSize];
|
||||
|
||||
constexpr int kLoaderBufSize = 0x50;
|
||||
static uint8_t s_anRPC_LoaderBuf[kLoaderBufSize];
|
||||
|
||||
constexpr u32 kNumLanguages = 12;
|
||||
static const char* languages[kNumLanguages] = {"ENG", "FRE", "GER", "SPA", "ITA", "COM",
|
||||
"JAP", "KOR", "RUS", "POR", "DUT", "UKE"};
|
||||
|
||||
const char* g_pszLanguage = languages[0];
|
||||
u8 g_nFPS = 60;
|
||||
SoundBankInfo* g_LoadingSoundBank = nullptr;
|
||||
|
||||
void jak3_overlord_init_globals_srpc() {
|
||||
g_nFPS = 60;
|
||||
g_LoadingSoundBank = nullptr;
|
||||
g_pszLanguage = languages[0];
|
||||
}
|
||||
|
||||
u32 Thread_Player() {
|
||||
sceSifQueueData dq;
|
||||
sceSifServeData serve;
|
||||
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, RpcId::Player, RPC_Player, &s_anRPC_PlayerBuf, kPlayerBufSize, nullptr,
|
||||
nullptr, &dq);
|
||||
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 Thread_Loader() {
|
||||
sceSifQueueData dq;
|
||||
sceSifServeData serve;
|
||||
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, RpcId::Loader, RPC_Loader, &s_anRPC_LoaderBuf, kLoaderBufSize, nullptr,
|
||||
nullptr, &dq);
|
||||
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* RPC_Player(unsigned int, void* msg, int size) {
|
||||
if (!g_bSoundEnable) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// const auto* cmd = (RPC_Player_Cmd*)msg;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "Got Player RPC with {} cmds", size / kPlayerCommandStride);
|
||||
const u8* m_ptr = (const u8*)msg;
|
||||
const u8* end = m_ptr + size;
|
||||
|
||||
for (; m_ptr < end; m_ptr += kPlayerCommandStride) {
|
||||
switch (((const Rpc_Player_Base_Cmd*)m_ptr)->command) {
|
||||
case SoundCommand::PLAY: {
|
||||
const auto* cmd = (const Rpc_Player_Play_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[Player RPC] command PLAY {} id {}", cmd->name.data,
|
||||
cmd->sound_id);
|
||||
s32 id = cmd->sound_id;
|
||||
if (id) {
|
||||
auto* sound = LookupSound(id);
|
||||
if (!sound) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[Player RPC] allocating a new one");
|
||||
sound = AllocateSound();
|
||||
if (sound) {
|
||||
SFXUserData user_val;
|
||||
strcpy_toupper(sound->name.data, cmd->name.data);
|
||||
sound->params = cmd->params;
|
||||
sound->auto_time = 0;
|
||||
s32 get_status =
|
||||
snd_GetSoundUserData(nullptr, nullptr, -1, sound->name.data, &user_val);
|
||||
s32 mask = sound->params.mask;
|
||||
if ((mask & 8) == 0) {
|
||||
sound->params.group = 0;
|
||||
}
|
||||
if ((mask & 0x40) == 0) {
|
||||
if (get_status == 0 || user_val.data[0] == 0) {
|
||||
sound->params.fo_min = 5;
|
||||
} else {
|
||||
sound->params.fo_min = (int16_t)user_val.data[0];
|
||||
}
|
||||
}
|
||||
if ((mask & 0x80) == 0) {
|
||||
if (get_status == 0 || user_val.data[1] == 0) {
|
||||
sound->params.fo_max = 0x1e;
|
||||
} else {
|
||||
sound->params.fo_max = (int16_t)user_val.data[1];
|
||||
}
|
||||
}
|
||||
if ((mask & 0x100) == 0) {
|
||||
u32 fo_curve = 0;
|
||||
if (get_status != 0) {
|
||||
fo_curve = user_val.data[2];
|
||||
}
|
||||
(sound->params).fo_curve = fo_curve;
|
||||
}
|
||||
(sound->params).fo_curve = GetFalloffCurve(sound->params.fo_curve);
|
||||
auto handle = snd_PlaySoundByNameVolPanPMPB(
|
||||
0, 0, sound->name.data, GetVolume(sound), GetPan(sound),
|
||||
(int)(sound->params).pitch_mod, (int)(sound->params).bend);
|
||||
sound->id = id;
|
||||
sound->sound_handle = handle;
|
||||
if (handle != 0) {
|
||||
if ((sound->params.mask & 0x800) != 0) {
|
||||
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
|
||||
}
|
||||
if ((sound->params.mask & 0x1000) != 0) {
|
||||
snd_SetSoundReg(sound->sound_handle, 1, sound->params.reg[1]);
|
||||
}
|
||||
if ((sound->params.mask & 0x2000) != 0) {
|
||||
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SFXUserData user_val;
|
||||
sound->params = cmd->params;
|
||||
s32 get_status =
|
||||
snd_GetSoundUserData(nullptr, nullptr, -1, sound->name.data, &user_val);
|
||||
s32 mask = (sound->params).mask;
|
||||
if ((mask & 8) == 0) {
|
||||
(sound->params).group = 0;
|
||||
}
|
||||
if ((mask & 0x40) == 0) {
|
||||
if (get_status == 0 || user_val.data[0] == 0) {
|
||||
(sound->params).fo_min = 5;
|
||||
} else {
|
||||
(sound->params).fo_min = user_val.data[0];
|
||||
}
|
||||
}
|
||||
if ((mask & 0x80) == 0) {
|
||||
if (get_status == 0 || user_val.data[1] == 0) {
|
||||
(sound->params).fo_max = 0x1e;
|
||||
} else {
|
||||
(sound->params).fo_max = user_val.data[1];
|
||||
}
|
||||
}
|
||||
if ((mask & 0x100) == 0) {
|
||||
s8 fo_curve = 0;
|
||||
if (get_status != 0) {
|
||||
fo_curve = user_val.data[2];
|
||||
}
|
||||
(sound->params).fo_curve = fo_curve;
|
||||
}
|
||||
(sound->params).fo_curve = GetFalloffCurve(sound->params.fo_curve);
|
||||
UpdateVolume(sound);
|
||||
snd_SetSoundPitchModifier(sound->sound_handle, (int)(sound->params).pitch_mod);
|
||||
if (((sound->params).mask & 4) != 0) {
|
||||
snd_SetSoundPitchBend(sound->sound_handle, (int)(sound->params).bend);
|
||||
}
|
||||
if (((sound->params).mask & 0x800) != 0) {
|
||||
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
|
||||
}
|
||||
if (((sound->params).mask & 0x1000) != 0) {
|
||||
snd_SetSoundReg(sound->sound_handle, 1, (int)(char)(sound->params).reg[1]);
|
||||
}
|
||||
if (((sound->params).mask & 0x2000) != 0) {
|
||||
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::PAUSE_SOUND: {
|
||||
const auto* cmd = (const Rpc_Player_Sound_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Pause Sound ID {}", cmd->sound_id);
|
||||
if (cmd->sound_id) {
|
||||
auto* sound = LookupSound(cmd->sound_id);
|
||||
if (sound) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching Sound {} to pause",
|
||||
sound->name.data);
|
||||
snd_PauseSound(sound->sound_handle);
|
||||
} else {
|
||||
auto* vag = FindVagStreamId(cmd->sound_id);
|
||||
if (vag) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching VAG {} to pause",
|
||||
vag->name);
|
||||
PauseVAG(vag);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::STOP_SOUND: {
|
||||
const auto* cmd = (const Rpc_Player_Sound_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] stop Sound ID {}", cmd->sound_id);
|
||||
if (cmd->sound_id) {
|
||||
auto* sound = LookupSound(cmd->sound_id);
|
||||
if (sound) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching Sound {} to stop",
|
||||
sound->name.data);
|
||||
snd_StopSound(sound->sound_handle);
|
||||
} else {
|
||||
auto* vag = FindVagStreamId(cmd->sound_id);
|
||||
if (vag) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching VAG {} to stop",
|
||||
vag->name);
|
||||
StopVagStream(vag);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::CONTINUE_SOUND: {
|
||||
const auto* cmd = (const Rpc_Player_Sound_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] continue Sound ID {}", cmd->sound_id);
|
||||
if (cmd->sound_id) {
|
||||
auto* sound = LookupSound(cmd->sound_id);
|
||||
if (sound) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching Sound {} to continue",
|
||||
sound->name.data);
|
||||
snd_ContinueSound(sound->sound_handle);
|
||||
} else {
|
||||
auto* vag = FindVagStreamId(cmd->sound_id);
|
||||
if (vag) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching VAG {} to continue",
|
||||
vag->name);
|
||||
UnPauseVAG(vag);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::SET_PARAM: {
|
||||
const auto* cmd = (const Rpc_Player_Set_Param_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] SET_PARAM Sound ID {}", cmd->sound_id);
|
||||
if (cmd->sound_id) {
|
||||
auto* sound = LookupSound(cmd->sound_id);
|
||||
if (sound) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching Sound {} to SET_PARAM",
|
||||
sound->name.data);
|
||||
auto& params = cmd->params;
|
||||
u16 mask = cmd->params.mask;
|
||||
s32 atime = cmd->auto_time;
|
||||
s32 afrom = cmd->auto_from;
|
||||
if ((mask & 1) != 0) {
|
||||
if ((mask & 0x10) == 0) {
|
||||
sound->params.volume = params.volume;
|
||||
} else {
|
||||
sound->auto_time = atime;
|
||||
sound->new_volume = params.volume;
|
||||
}
|
||||
}
|
||||
if ((mask & 0x20) != 0) {
|
||||
sound->params.trans[0] = params.trans[0];
|
||||
sound->params.trans[1] = params.trans[1];
|
||||
sound->params.trans[2] = params.trans[2];
|
||||
}
|
||||
if ((mask & 0x21) != 0) {
|
||||
UpdateVolume(sound);
|
||||
}
|
||||
if ((mask & 2) != 0) {
|
||||
auto pitch_mod = params.pitch_mod;
|
||||
sound->params.pitch_mod = pitch_mod;
|
||||
if ((mask & 0x10) == 0) {
|
||||
snd_SetSoundPitchModifier(sound->sound_handle, params.pitch_mod);
|
||||
} else {
|
||||
snd_AutoPitch(sound->sound_handle, pitch_mod, atime, afrom);
|
||||
}
|
||||
}
|
||||
if ((mask & 4) != 0) {
|
||||
auto bend = params.bend;
|
||||
sound->params.bend = bend;
|
||||
if ((mask & 0x10) == 0) {
|
||||
snd_SetSoundPitchBend(sound->sound_handle, params.bend);
|
||||
} else {
|
||||
snd_AutoPitchBend(sound->sound_handle, (int)bend, atime, afrom);
|
||||
}
|
||||
}
|
||||
if ((mask & 0x400) != 0) {
|
||||
sound->params.priority = params.priority;
|
||||
}
|
||||
if ((mask & 8) != 0) {
|
||||
sound->params.group = params.group;
|
||||
}
|
||||
if ((mask & 0x40) != 0) {
|
||||
sound->params.fo_min = params.fo_min;
|
||||
}
|
||||
if ((mask & 0x80) != 0) {
|
||||
sound->params.fo_max = params.fo_max;
|
||||
}
|
||||
if ((mask & 0x100) != 0) {
|
||||
sound->params.fo_curve = GetFalloffCurve(params.fo_curve);
|
||||
}
|
||||
if ((mask & 0x800) != 0) {
|
||||
sound->params.reg[0] = params.reg[0];
|
||||
snd_SetSoundReg(sound->sound_handle, 0, params.reg[0]);
|
||||
}
|
||||
if ((mask & 0x1000) != 0) {
|
||||
sound->params.reg[1] = params.reg[1];
|
||||
snd_SetSoundReg(sound->sound_handle, 1, params.reg[1]);
|
||||
}
|
||||
if ((mask & 0x2000) != 0) {
|
||||
sound->params.reg[2] = params.reg[2];
|
||||
snd_SetSoundReg(sound->sound_handle, 2, params.reg[2]);
|
||||
}
|
||||
} else {
|
||||
auto* vag = FindVagStreamId(cmd->sound_id);
|
||||
if (vag) {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Found matching VAG {} to SET_PARAM",
|
||||
vag->name);
|
||||
auto& params = cmd->params;
|
||||
auto mask = params.mask;
|
||||
if ((mask & 2) != 0) {
|
||||
SetVAGStreamPitch(cmd->sound_id, params.pitch_mod);
|
||||
}
|
||||
if ((mask & 0x20) != 0) {
|
||||
vag->trans[0] = params.trans[0];
|
||||
vag->trans[1] = params.trans[1];
|
||||
vag->trans[2] = params.trans[2];
|
||||
vag->updated_trans = 1;
|
||||
}
|
||||
if ((mask & 8) != 0) {
|
||||
vag->play_group = params.group;
|
||||
}
|
||||
if ((mask & 0x40) != 0) {
|
||||
vag->fo_min = (int)params.fo_min;
|
||||
}
|
||||
if ((mask & 0x80) != 0) {
|
||||
vag->fo_max = (int)params.fo_max;
|
||||
}
|
||||
if ((mask & 0x100) != 0) {
|
||||
vag->fo_curve = GetFalloffCurve(params.fo_curve);
|
||||
}
|
||||
if ((mask & 1) != 0) {
|
||||
vag->play_volume = params.volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::SET_MASTER_VOLUME: {
|
||||
const auto* cmd = (const Rpc_Player_Set_Master_Volume_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Set Master Volume to {}", cmd->volume);
|
||||
for (int i = 0; i < 17; i++) {
|
||||
if (cmd->group & (1 << i)) {
|
||||
g_anMasterVolume[i] = cmd->volume;
|
||||
snd_SetMasterVolume(i, cmd->volume);
|
||||
SetAllVagsVol(i);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::PAUSE_GROUP: {
|
||||
const auto* cmd = (const Rpc_Player_Group_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Pause groups 0b{:b}", cmd->group);
|
||||
snd_PauseAllSoundsInGroup(cmd->group);
|
||||
if (cmd->group & 4) {
|
||||
PauseVAGStreams();
|
||||
}
|
||||
if (cmd->group & 2) {
|
||||
g_bMusicPause = true;
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::STOP_GROUP: {
|
||||
const auto* cmd = (const Rpc_Player_Group_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Stop groups 0b{:b}", cmd->group);
|
||||
KillSoundsInGroup(cmd->group);
|
||||
if (cmd->group & 4) {
|
||||
ISO_VAGCommand vag_cmd;
|
||||
vag_cmd.msg_type = ISO_Hdr::MsgType::VAG_STOP; // seems unsupported by iso thread.
|
||||
vag_cmd.mbox_reply = 0;
|
||||
vag_cmd.thread_to_wake = 0;
|
||||
vag_cmd.vag_dir_entry = nullptr;
|
||||
vag_cmd.name[0] = 0;
|
||||
vag_cmd.maybe_sound_handler = 0;
|
||||
vag_cmd.id = 0;
|
||||
vag_cmd.priority_pq = 0;
|
||||
StopVagStream(&vag_cmd);
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::CONTINUE_GROUP: {
|
||||
const auto* cmd = (const Rpc_Player_Group_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Continue groups 0b{:b}", cmd->group);
|
||||
snd_ContinueAllSoundsInGroup(cmd->group);
|
||||
if (cmd->group & 4) {
|
||||
UnpauseVAGStreams();
|
||||
}
|
||||
if (cmd->group & 2) {
|
||||
g_bMusicPause = false;
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::SET_REVERB: {
|
||||
ovrld_log(LogCategory::WARN, "[RPC Player] Unimplemented set reverb.");
|
||||
} break;
|
||||
case SoundCommand::SET_EAR_TRANS: {
|
||||
const auto* cmd = (const Rpc_Player_Set_Ear_Trans_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] set ear trans");
|
||||
SetEarTrans(cmd->ear_trans0, cmd->ear_trans1, cmd->ear_trans, cmd->cam_forward,
|
||||
cmd->cam_left, cmd->cam_scale, (cmd->cam_inverted != 0));
|
||||
} break;
|
||||
case SoundCommand::SHUTDOWN: {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] Shutdown!");
|
||||
WaitSema(g_n989Semaphore);
|
||||
if (g_bSoundEnable) {
|
||||
g_bSoundEnable = false;
|
||||
snd_StopSoundSystem();
|
||||
}
|
||||
SignalSema(g_n989Semaphore);
|
||||
} break;
|
||||
case SoundCommand::SET_FPS: {
|
||||
const auto* cmd = (const Rpc_Player_Set_Fps_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] set fps {}", (int)cmd->fps);
|
||||
g_nFPS = cmd->fps;
|
||||
} break;
|
||||
case SoundCommand::CANCEL_DGO: {
|
||||
const auto* cmd = (const Rpc_Player_Cancel_Dgo_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Player] cancel dgo {}", cmd->id);
|
||||
CancelDGONoSync(cmd->id);
|
||||
} break;
|
||||
case SoundCommand::SET_MIDI_REG:
|
||||
// this is what the real overlord does - just ignore it!
|
||||
break;
|
||||
default:
|
||||
ovrld_log(LogCategory::WARN, "[RPC Player] Unsupported Player {}",
|
||||
(int)((const Rpc_Player_Base_Cmd*)m_ptr)->command);
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* RPC_Loader(unsigned int, void* msg, int size) {
|
||||
if (!g_bSoundEnable) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// const auto* cmd = (RPC_Player_Cmd*)msg;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got Loader RPC with {} cmds",
|
||||
size / kLoaderCommandStride);
|
||||
u8* m_ptr = (u8*)msg;
|
||||
const u8* end = m_ptr + size;
|
||||
|
||||
for (; m_ptr < end; m_ptr += kLoaderCommandStride) {
|
||||
switch (((const Rpc_Player_Base_Cmd*)m_ptr)->command) {
|
||||
case SoundCommand::LOAD_BANK: {
|
||||
auto* cmd = (const Rpc_Loader_Load_Bank_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got sound bank load command: {}",
|
||||
cmd->bank_name.data);
|
||||
// src = &cmd->bank_name;
|
||||
if (!LookupBank(cmd->bank_name.data)) {
|
||||
auto* info = AllocateBankName(cmd->bank_name.data, cmd->mode);
|
||||
if (info) {
|
||||
strncpyz(info->m_name1, cmd->bank_name.data, 0x10);
|
||||
info->in_use = 1;
|
||||
info->unk0 = 0;
|
||||
g_LoadingSoundBank = info;
|
||||
if (LoadSoundBankToIOP(cmd->bank_name.data, info, cmd->priority) == 0) {
|
||||
info->loaded = 1;
|
||||
} else {
|
||||
info->loaded = 0;
|
||||
info->in_use = 0;
|
||||
}
|
||||
g_LoadingSoundBank = nullptr;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::LOAD_MUSIC: {
|
||||
auto* cmd = (const Rpc_Loader_Bank_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got music load command: {}",
|
||||
cmd->bank_name.data);
|
||||
|
||||
// lock
|
||||
u32 wait_status = 1;
|
||||
while (wait_status) {
|
||||
wait_status = WaitSema(g_nMusicSemaphore);
|
||||
}
|
||||
|
||||
// set music name
|
||||
if ((cmd->bank_name).data[0] == 0) {
|
||||
g_szTargetMusicName[0] = 0;
|
||||
} else {
|
||||
strcpy(g_szTargetMusicName, cmd->bank_name.data);
|
||||
}
|
||||
|
||||
// release
|
||||
SignalSema(g_nMusicSemaphore);
|
||||
} break;
|
||||
|
||||
case SoundCommand::UNLOAD_BANK: {
|
||||
auto* cmd = (const Rpc_Loader_Bank_Cmd*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got bank load unload command: {}",
|
||||
cmd->bank_name.data);
|
||||
SoundBankInfo* ifno = LookupBank(cmd->bank_name.data);
|
||||
if (ifno) {
|
||||
auto snd_handle = ifno->snd_handle;
|
||||
ifno->snd_handle = nullptr;
|
||||
if (ifno->unk0 == 0) {
|
||||
ifno->in_use = 0;
|
||||
}
|
||||
ifno->mode = 0;
|
||||
ifno->loaded = 0;
|
||||
snd_UnloadBank(snd_handle);
|
||||
snd_ResolveBankXREFS();
|
||||
}
|
||||
} break;
|
||||
|
||||
case SoundCommand::GET_IRX_VERSION: {
|
||||
auto* cmd = (Rpc_Loader_Get_Irx_Version*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got IRX version command");
|
||||
g_nInfoEE = cmd->ee_addr;
|
||||
cmd->major = 4;
|
||||
cmd->minor = 0;
|
||||
return cmd;
|
||||
} break;
|
||||
|
||||
case SoundCommand::SET_LANGUAGE: {
|
||||
auto* cmd = (Rpc_Loader_Set_Language*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got set language command {}", cmd->lang);
|
||||
ASSERT(cmd->lang < kNumLanguages);
|
||||
g_pszLanguage = languages[cmd->lang];
|
||||
} break;
|
||||
|
||||
case SoundCommand::UNLOAD_MUSIC: {
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got unload music command");
|
||||
|
||||
// lock
|
||||
u32 wait_status = 1;
|
||||
while (wait_status) {
|
||||
wait_status = WaitSema(g_nMusicSemaphore);
|
||||
}
|
||||
|
||||
// set music name
|
||||
g_szTargetMusicName[0] = 0;
|
||||
|
||||
// release
|
||||
SignalSema(g_nMusicSemaphore);
|
||||
} break;
|
||||
|
||||
case SoundCommand::SET_STEREO_MODE: {
|
||||
auto* cmd = (Rpc_Loader_Set_Stereo_Mode*)m_ptr;
|
||||
ovrld_log(LogCategory::PLAYER_RPC, "[RPC Loader] Got set stereo command {}", cmd->mode);
|
||||
|
||||
switch (cmd->mode) {
|
||||
case 0:
|
||||
SetPlaybackMode(1);
|
||||
break;
|
||||
case 1:
|
||||
SetPlaybackMode(2);
|
||||
break;
|
||||
case 2:
|
||||
SetPlaybackMode(0);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
ovrld_log(LogCategory::WARN, "[RPC Loader] Unsupported Loader {}",
|
||||
(int)((const Rpc_Player_Base_Cmd*)m_ptr)->command);
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SetVagStreamName(ISO_VAGCommand* cmd, int len) {
|
||||
ASSERT(cmd);
|
||||
if (!cmd->music_flag && cmd->info_idx < 4) {
|
||||
if (!len) {
|
||||
g_SRPCSoundIOPInfo.stream_name[cmd->info_idx].chars[0] = 0;
|
||||
} else {
|
||||
strncpy(g_SRPCSoundIOPInfo.stream_name[cmd->info_idx].chars, cmd->name, 0x30);
|
||||
}
|
||||
} else {
|
||||
// ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jak3
|
16
game/overlord/jak3/srpc.h
Normal file
16
game/overlord/jak3/srpc.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_srpc();
|
||||
u32 Thread_Player();
|
||||
u32 Thread_Loader();
|
||||
struct ISO_VAGCommand;
|
||||
void SetVagStreamName(ISO_VAGCommand* cmd, int len);
|
||||
void* RPC_Player(unsigned int fno, void* msg, int size);
|
||||
void* RPC_Loader(unsigned int fno, void* msg, int size);
|
||||
extern const char* g_pszLanguage;
|
||||
extern u8 g_nFPS;
|
||||
|
||||
} // namespace jak3
|
995
game/overlord/jak3/ssound.cpp
Normal file
995
game/overlord/jak3/ssound.cpp
Normal file
@ -0,0 +1,995 @@
|
||||
#include "ssound.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/overlord/jak3/spustreams.h"
|
||||
#include "game/overlord/jak3/streamlist.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "game/sound/sdshim.h"
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
struct Curve {
|
||||
s32 a, b, c, d;
|
||||
};
|
||||
|
||||
constexpr int kNumSounds = 0x40;
|
||||
|
||||
using namespace iop;
|
||||
s32 g_n989Semaphore = -1;
|
||||
s32 g_EarTransSema = -1;
|
||||
bool g_bSoundEnable = true;
|
||||
u32 g_anStreamVoice[6];
|
||||
VolumePair g_aPanTable[361];
|
||||
SoundInfo gSounds[kNumSounds];
|
||||
s32 gEarTrans[6];
|
||||
s32 gCamTrans[3];
|
||||
s32 gCamForward[3];
|
||||
s32 gCamLeft[3];
|
||||
s32 gCamScale;
|
||||
Curve gCurves[0x29];
|
||||
std::array<u8, 1024> unktable;
|
||||
bool g_CameraInvert = false;
|
||||
u32 gLastTick = 0;
|
||||
|
||||
static s32 sqrt_table[256] = {
|
||||
0, 4096, 5793, 7094, 8192, 9159, 10033, 10837, 11585, 12288, 12953, 13585, 14189,
|
||||
14768, 15326, 15864, 16384, 16888, 17378, 17854, 18318, 18770, 19212, 19644, 20066, 20480,
|
||||
20886, 21283, 21674, 22058, 22435, 22806, 23170, 23530, 23884, 24232, 24576, 24915, 25249,
|
||||
25580, 25905, 26227, 26545, 26859, 27170, 27477, 27780, 28081, 28378, 28672, 28963, 29251,
|
||||
29537, 29819, 30099, 30377, 30652, 30924, 31194, 31462, 31727, 31991, 32252, 32511, 32768,
|
||||
33023, 33276, 33527, 33776, 34024, 34270, 34514, 34756, 34996, 35235, 35472, 35708, 35942,
|
||||
36175, 36406, 36636, 36864, 37091, 37316, 37540, 37763, 37985, 38205, 38424, 38642, 38858,
|
||||
39073, 39287, 39500, 39712, 39923, 40132, 40341, 40548, 40755, 40960, 41164, 41368, 41570,
|
||||
41771, 41972, 42171, 42369, 42567, 42763, 42959, 43154, 43348, 43541, 43733, 43925, 44115,
|
||||
44305, 44494, 44682, 44869, 45056, 45242, 45427, 45611, 45795, 45977, 46160, 46341, 46522,
|
||||
46702, 46881, 47059, 47237, 47415, 47591, 47767, 47942, 48117, 48291, 48465, 48637, 48809,
|
||||
48981, 49152, 49322, 49492, 49661, 49830, 49998, 50166, 50332, 50499, 50665, 50830, 50995,
|
||||
51159, 51323, 51486, 51649, 51811, 51972, 52134, 52294, 52454, 52614, 52773, 52932, 53090,
|
||||
53248, 53405, 53562, 53719, 53874, 54030, 54185, 54340, 54494, 54647, 54801, 54954, 55106,
|
||||
55258, 55410, 55561, 55712, 55862, 56012, 56162, 56311, 56459, 56608, 56756, 56903, 57051,
|
||||
57198, 57344, 57490, 57636, 57781, 57926, 58071, 58215, 58359, 58503, 58646, 58789, 58931,
|
||||
59073, 59215, 59357, 59498, 59639, 59779, 59919, 60059, 60199, 60338, 60477, 60615, 60753,
|
||||
60891, 61029, 61166, 61303, 61440, 61576, 61712, 61848, 61984, 62119, 62254, 62388, 62523,
|
||||
62657, 62790, 62924, 63057, 63190, 63323, 63455, 63587, 63719, 63850, 63982, 64113, 64243,
|
||||
64374, 64504, 64634, 64763, 64893, 65022, 65151, 65279, 65408,
|
||||
};
|
||||
|
||||
void jak3_overlord_init_globals_ssound() {
|
||||
g_bSoundEnable = true;
|
||||
g_n989Semaphore = -1;
|
||||
g_EarTransSema = -1;
|
||||
for (auto& x : g_anStreamVoice) {
|
||||
x = 0;
|
||||
}
|
||||
for (auto& x : gSounds) {
|
||||
x = {};
|
||||
}
|
||||
unktable.fill(0);
|
||||
g_CameraInvert = false;
|
||||
gLastTick = 0;
|
||||
}
|
||||
void InitSound() {
|
||||
for (auto& sound : gSounds) {
|
||||
sound.id = 0;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
do {
|
||||
unktable[j] = 0;
|
||||
unktable[j + 0x2c] = 0;
|
||||
unktable[j + 0x58] = 0;
|
||||
unktable[j + 0x84] = 0;
|
||||
unktable[j + 0xb0] = 0;
|
||||
j = j + 1;
|
||||
} while (j < 0x29);
|
||||
|
||||
SetCurve(0, 0, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(1, 0, 0, 0, 0, 0, 1, 0);
|
||||
SetCurve(2, 0, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(3, 0x1000, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(4, 0, 0x1000, 1, 0, 0, 0, 0);
|
||||
SetCurve(5, 0x800, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(6, 0x800, 0x800, 1, 0, 0, 0, 0);
|
||||
SetCurve(7, 0xfffff000, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(8, 0xfffff800, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(9, 0, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(10, 0, 0, 0, 0, 0, 0, 0);
|
||||
SetCurve(0xb, 0, 0, 1, 0, 0, 0, 0);
|
||||
SetCurve(0xc, 0, 0, 1, 0, 1, 0, 0);
|
||||
SetCurve(0xd, 0x1000, 0, 1, 0, 1, 0, 0);
|
||||
SetCurve(0xe, 0, 0x1000, 1, 0, 1, 0, 0);
|
||||
SetCurve(0xf, 0x800, 0, 1, 0, 1, 0, 0);
|
||||
SetCurve(0x10, 0x800, 0x800, 1, 0, 1, 0, 0);
|
||||
SetCurve(0x11, 0xfffff000, 0, 1, 0, 1, 0, 0);
|
||||
SetCurve(0x12, 0xfffff800, 0, 1, 0, 1, 0, 0);
|
||||
SetCurve(0x13, 0, 0, 0, 0, 1, 0, 0);
|
||||
SetCurve(0x14, 0, 0, 0, 0, 0, 1, 1);
|
||||
SetCurve(0x15, 0, 0, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x16, 0x1000, 0, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x17, 0, 0x1000, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x18, 0x800, 0, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x19, 0x800, 0x800, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x1a, 0xfffff000, 0, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x1b, 0xfffff800, 0, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x1c, 0, 0, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x1d, 0, 0, 0, 0, 0, 0, 1);
|
||||
SetCurve(0x1e, 0, 0, 1, 0, 0, 0, 1);
|
||||
SetCurve(0x1f, 0, 0, 1, 0, 1, 0, 1);
|
||||
SetCurve(0x20, 0x1000, 0, 1, 0, 1, 0, 1);
|
||||
SetCurve(0x21, 0, 0x1000, 1, 0, 1, 0, 1);
|
||||
SetCurve(0x22, 0x800, 0, 1, 0, 1, 0, 1);
|
||||
SetCurve(0x23, 0x800, 0x800, 1, 0, 1, 0, 1);
|
||||
SetCurve(0x24, 0xfffff000, 0, 1, 0, 1, 0, 1);
|
||||
SetCurve(0x25, 0xfffff800, 0, 1, 0, 1, 0, 1);
|
||||
SetCurve(0x26, 0, 0, 0, 0, 1, 0, 1);
|
||||
SetCurve(0x27, 0, 0, 1, 1, 0, 1, 0);
|
||||
SetCurve(0x28, 0, 0, 1, 1, 0, 1, 1);
|
||||
|
||||
// changed
|
||||
// snd_StartSoundSystemEx(2);
|
||||
snd_StartSoundSystem();
|
||||
|
||||
// iVar4 = 5;
|
||||
// snd_RegisterIOPMemAllocator(FUN_0000dc7c,FUN_0000de84);
|
||||
// snd_LockVoiceAllocatorEx(1,0x12345678);
|
||||
// piVar1 = g_anStreamVoice;
|
||||
// do {
|
||||
// iVar2 = snd_ExternVoiceAlloc(2,0x7f);
|
||||
// iVar4 = iVar4 + -1;
|
||||
// *piVar1 = iVar2 * 2 + ((iVar2 / 6 + (iVar2 >> 0x1f) >> 2) - (iVar2 >> 0x1f)) * -0x2f;
|
||||
// piVar1 = piVar1 + 1;
|
||||
// } while (-1 < iVar4);
|
||||
|
||||
g_anStreamVoice[0] = SD_VOICE(0, 0);
|
||||
g_anStreamVoice[1] = SD_VOICE(0, 1);
|
||||
g_anStreamVoice[2] = SD_VOICE(0, 2);
|
||||
g_anStreamVoice[3] = SD_VOICE(0, 3);
|
||||
g_anStreamVoice[4] = SD_VOICE(0, 4);
|
||||
g_anStreamVoice[5] = SD_VOICE(0, 5);
|
||||
|
||||
// snd_UnlockVoiceAllocator();
|
||||
// snd_SetMixerMode(0,0);
|
||||
// iVar4 = 0;
|
||||
// do {
|
||||
// iVar2 = iVar4 + 1;
|
||||
// snd_SetGroupVoiceRange(iVar4,6,0x2f);
|
||||
// iVar4 = iVar2;
|
||||
// } while (iVar2 < 0xe);
|
||||
// snd_SetGroupVoiceRange(2,0,5);
|
||||
|
||||
// what is this even doing.
|
||||
// sceSdGetAddr(0x1c00);
|
||||
// sceSdGetAddr(0x1d00);
|
||||
// sceSdGetAddr(0x1c01);
|
||||
// sceSdGetAddr(0x1d01);
|
||||
// CpuSuspendIntr(local_18);
|
||||
// sceSdSetAddr(0,0);
|
||||
// sceSdSetAddr(1,0);
|
||||
// sceSdSetAddr(0,0xff);
|
||||
// sceSdSetAddr(1,0xff);
|
||||
// CpuResumeIntr(local_18[0]);
|
||||
|
||||
// uVar3 = sceSdGetAddr(0x1c01);
|
||||
// snd_SRAMMarkUsed(uVar3,0x7000);
|
||||
// uVar3 = sceSdGetAddr(0x1c00);
|
||||
// snd_SRAMMarkUsed(uVar3,0x7000);
|
||||
// local_3c = 0x104;
|
||||
// local_36 = 0xa7b;
|
||||
// local_38 = 0xa7b;
|
||||
// g_nCore1ReverbMode = 4;
|
||||
// g_nCore0ReverbMode = 4;
|
||||
// local_34 = 0;
|
||||
// local_30 = 0;
|
||||
// local_40 = 0;
|
||||
// sceSdSetEffectAttr(0, &local_40);
|
||||
// local_40 = 1;
|
||||
// sceSdSetEffectAttr(1, &local_40);
|
||||
// maybe_sceSdSetCoreAttr(2, 1);
|
||||
// maybe_sceSdSetCoreAttr(3, 1);
|
||||
|
||||
// TODO: this is possibly very wrong:
|
||||
// g_aPanTable = snd_GetPanTable();
|
||||
for (int i = 0; i < 91; i++) {
|
||||
s16 opposing_front = static_cast<s16>(((i * 0x33ff) / 90) + 0xc00);
|
||||
|
||||
s16 rear_right = static_cast<s16>(((i * -0x2800) / 90) + 0x3400);
|
||||
s16 rear_left = static_cast<s16>(((i * -0xbff) / 90) + 0x3fff);
|
||||
|
||||
g_aPanTable[90 - i].left = 0x3FFF;
|
||||
g_aPanTable[180 - i].left = opposing_front;
|
||||
g_aPanTable[270 - i].left = rear_right;
|
||||
g_aPanTable[360 - i].left = rear_left;
|
||||
|
||||
g_aPanTable[i].right = opposing_front;
|
||||
g_aPanTable[90 + i].right = 0x3FFF;
|
||||
g_aPanTable[180 + i].right = rear_left;
|
||||
g_aPanTable[270 + i].right = rear_right;
|
||||
}
|
||||
|
||||
SetPlaybackMode(2);
|
||||
|
||||
SemaParam param;
|
||||
param.attr = 1;
|
||||
param.init_count = 1;
|
||||
param.max_count = 1;
|
||||
param.option = 0;
|
||||
g_nMusicSemaphore = CreateSema(¶m);
|
||||
ASSERT(g_nMusicSemaphore >= 0);
|
||||
param.attr = 1;
|
||||
param.init_count = 1;
|
||||
param.max_count = 1;
|
||||
param.option = 0;
|
||||
g_n989Semaphore = CreateSema(¶m);
|
||||
ASSERT(g_n989Semaphore >= 0);
|
||||
|
||||
param.max_count = 1;
|
||||
param.attr = 1;
|
||||
param.init_count = 1;
|
||||
param.option = 0;
|
||||
g_EarTransSema = CreateSema(¶m);
|
||||
ASSERT(g_EarTransSema >= 0);
|
||||
|
||||
// Init989Plugins();
|
||||
// InitStreamLfoHandler();
|
||||
// InitVagStreamList((List*)&g_PluginStreamsList, 4, s_plugin_00015918);
|
||||
InitVagStreamList(&g_EEStreamsList, 4, "ee");
|
||||
InitVagStreamList(&g_EEPlayList, 8, "play");
|
||||
InitVagStreamList(&g_RequestedStreamsList, 8, "streams");
|
||||
InitVagStreamList(&g_NewStreamsList, 4, "new");
|
||||
}
|
||||
|
||||
SoundInfo* LookupSound(int id) {
|
||||
if (id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto& sound : gSounds) {
|
||||
if (sound.id == id) {
|
||||
s32 handle = snd_SoundIsStillPlaying(sound.sound_handle);
|
||||
sound.sound_handle = handle;
|
||||
if (handle) {
|
||||
return &sound;
|
||||
} else {
|
||||
sound.id = 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CleanSounds() {
|
||||
for (auto& sound : gSounds) {
|
||||
if (sound.id) {
|
||||
s32 handle = snd_SoundIsStillPlaying(sound.sound_handle);
|
||||
sound.sound_handle = handle;
|
||||
if (handle == 0) {
|
||||
sound.id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KillSoundsInGroup(u32 group) {
|
||||
for (auto& sound : gSounds) {
|
||||
if (sound.id) {
|
||||
s32 handle = snd_SoundIsStillPlaying(sound.sound_handle);
|
||||
sound.sound_handle = handle;
|
||||
if (handle) {
|
||||
if (sound.params.group & group) {
|
||||
snd_StopSound(handle);
|
||||
sound.id = 0;
|
||||
}
|
||||
} else {
|
||||
sound.id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KillLeastUsefulSound() {
|
||||
int unique_sounds = 0;
|
||||
struct Entry {
|
||||
u32 id;
|
||||
u32 count;
|
||||
SoundInfo* info;
|
||||
};
|
||||
Entry entries[kNumSounds];
|
||||
Entry* best_entry = nullptr;
|
||||
|
||||
for (auto& sound : gSounds) {
|
||||
if (sound.id) {
|
||||
Entry* existing_entry = nullptr;
|
||||
u32 uid = snd_GetSoundID(sound.sound_handle);
|
||||
|
||||
// look for entry:
|
||||
for (int i = 0; i < unique_sounds; i++) {
|
||||
if (entries[i].id == uid) {
|
||||
existing_entry = &entries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if none found, create
|
||||
if (!existing_entry) {
|
||||
existing_entry = &entries[unique_sounds];
|
||||
unique_sounds++;
|
||||
existing_entry->id = uid;
|
||||
existing_entry->count = 0;
|
||||
existing_entry->info = &sound;
|
||||
}
|
||||
|
||||
// update
|
||||
existing_entry->count++;
|
||||
|
||||
// se if we're best
|
||||
if (!best_entry) {
|
||||
best_entry = existing_entry;
|
||||
} else {
|
||||
if (best_entry->count < existing_entry->count) {
|
||||
best_entry = existing_entry;
|
||||
}
|
||||
}
|
||||
|
||||
// update entry:
|
||||
|
||||
// update best:
|
||||
}
|
||||
}
|
||||
|
||||
if (best_entry) {
|
||||
snd_StopSound(best_entry->info->sound_handle);
|
||||
best_entry->info->id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SoundInfo* AllocateSound() {
|
||||
for (auto& sound : gSounds) {
|
||||
if (!sound.id) {
|
||||
return &sound;
|
||||
}
|
||||
}
|
||||
|
||||
CleanSounds();
|
||||
for (auto& sound : gSounds) {
|
||||
if (!sound.id) {
|
||||
return &sound;
|
||||
}
|
||||
}
|
||||
|
||||
KillLeastUsefulSound();
|
||||
|
||||
for (auto& sound : gSounds) {
|
||||
if (!sound.id) {
|
||||
return &sound;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 CalculateFalloffVolume(s32* trans,
|
||||
u32 vol,
|
||||
u32 fo_curve,
|
||||
u32 fo_min,
|
||||
u32 fo_max,
|
||||
u32* outa,
|
||||
u32* outb) {
|
||||
ASSERT(fo_curve < 0x29);
|
||||
// undefined4 uVar1;
|
||||
u32 uVar2;
|
||||
int iVar3;
|
||||
int iVar4;
|
||||
u32 uVar5;
|
||||
int iVar6;
|
||||
int iVar7;
|
||||
int iVar8;
|
||||
u32 uVar9;
|
||||
u32 uVar10;
|
||||
|
||||
uVar10 = 0;
|
||||
WaitSema(g_EarTransSema);
|
||||
if (outa) {
|
||||
*outa = 0;
|
||||
}
|
||||
if (outb) {
|
||||
*outb = 0;
|
||||
}
|
||||
if (unktable[fo_curve + 0x84] != 0) {
|
||||
SignalSema(g_EarTransSema);
|
||||
return vol;
|
||||
}
|
||||
if (unktable[fo_curve + 0x58] != 0) {
|
||||
trans = gEarTrans + 3;
|
||||
}
|
||||
switch (fo_curve) {
|
||||
case 9:
|
||||
case 0xb:
|
||||
case 0x1c:
|
||||
case 0x1e:
|
||||
iVar8 = gEarTrans[3] - *trans;
|
||||
iVar3 = gEarTrans[4] - trans[1];
|
||||
iVar7 = gEarTrans[5] - trans[2];
|
||||
uVar2 = 3;
|
||||
if (outa) {
|
||||
LAB_0000d094:
|
||||
*outa = uVar2;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
case 0x13:
|
||||
case 0x1d:
|
||||
case 0x26:
|
||||
iVar8 = 0;
|
||||
iVar3 = gEarTrans[1] - trans[1];
|
||||
iVar7 = 0;
|
||||
if (outa) {
|
||||
*outa = 2;
|
||||
}
|
||||
goto LAB_0000d0a4;
|
||||
default:
|
||||
iVar8 = gEarTrans[0] - *trans;
|
||||
iVar7 = gEarTrans[2] - trans[2];
|
||||
iVar3 = gEarTrans[1] - trans[1];
|
||||
if (outa) {
|
||||
uVar2 = 1;
|
||||
goto LAB_0000d094;
|
||||
}
|
||||
}
|
||||
if (iVar8 < 0) {
|
||||
iVar8 = -iVar8;
|
||||
}
|
||||
LAB_0000d0a4:
|
||||
if (iVar3 < 0) {
|
||||
iVar3 = -iVar3;
|
||||
}
|
||||
if (iVar7 < 0) {
|
||||
iVar7 = -iVar7;
|
||||
}
|
||||
fo_min = fo_min << 8;
|
||||
fo_max = fo_max << 8;
|
||||
uVar9 = 0;
|
||||
iVar6 = iVar3;
|
||||
if (iVar3 < iVar7) {
|
||||
iVar6 = iVar7;
|
||||
}
|
||||
iVar4 = fo_max;
|
||||
if (fo_max < iVar8) {
|
||||
iVar4 = iVar8;
|
||||
}
|
||||
if (iVar4 < iVar6) {
|
||||
iVar4 = iVar6;
|
||||
}
|
||||
while (0x7fff < iVar4) {
|
||||
fo_max = fo_max >> 1;
|
||||
fo_min = fo_min >> 1;
|
||||
iVar8 = iVar8 >> 1;
|
||||
iVar3 = iVar3 >> 1;
|
||||
iVar7 = iVar7 >> 1;
|
||||
uVar9 = uVar9 + 1;
|
||||
iVar4 = iVar4 >> 1;
|
||||
}
|
||||
if (gCamScale != 0x10000) {
|
||||
iVar8 = iVar8 * gCamScale >> 0x10;
|
||||
iVar3 = iVar3 * gCamScale >> 0x10;
|
||||
iVar7 = iVar7 * gCamScale >> 0x10;
|
||||
if (0x10000 < gCamScale) {
|
||||
iVar6 = iVar3;
|
||||
if (iVar3 < iVar7) {
|
||||
iVar6 = iVar7;
|
||||
}
|
||||
iVar4 = fo_max;
|
||||
if (fo_max < iVar8) {
|
||||
iVar4 = iVar8;
|
||||
}
|
||||
if (iVar4 < iVar6) {
|
||||
iVar4 = iVar6;
|
||||
}
|
||||
while (0x7fff < iVar4) {
|
||||
fo_max = fo_max >> 1;
|
||||
fo_min = fo_min >> 1;
|
||||
iVar8 = iVar8 >> 1;
|
||||
iVar3 = iVar3 >> 1;
|
||||
iVar7 = iVar7 >> 1;
|
||||
uVar9 = uVar9 + 1;
|
||||
iVar4 = iVar4 >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((outb) || (((iVar8 <= fo_max && (iVar3 <= fo_max)) && (iVar7 <= fo_max)))) {
|
||||
uVar10 = iVar8 * iVar8 + iVar3 * iVar3 + iVar7 * iVar7;
|
||||
iVar8 = 0;
|
||||
if (uVar10 != 0) {
|
||||
uVar5 = 0;
|
||||
while ((uVar10 & 0xc0000000) == 0) {
|
||||
uVar10 = uVar10 << 2;
|
||||
uVar5 = uVar5 + 1;
|
||||
}
|
||||
iVar8 = (int)(u32)sqrt_table[uVar10 >> 0x18] >> (uVar5 & 0x1f);
|
||||
}
|
||||
if (outb) {
|
||||
*outb = iVar8 << (uVar9 & 0x1f);
|
||||
}
|
||||
uVar10 = vol;
|
||||
if ((fo_min < iVar8) && (uVar10 = 0, iVar8 < fo_max)) {
|
||||
uVar10 = iVar8 - fo_min;
|
||||
uVar9 = fo_max - fo_min;
|
||||
while (0xffff < uVar10) {
|
||||
uVar10 = uVar10 >> 1;
|
||||
uVar9 = (int)uVar9 >> 1;
|
||||
}
|
||||
uVar5 = (uVar10 << 0x10) / uVar9;
|
||||
if (uVar9 == 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
uVar10 = vol;
|
||||
if (uVar5 != 0x10000) {
|
||||
uVar10 = uVar5 * uVar5 >> 0x10;
|
||||
uVar10 = gCurves[fo_curve].c * uVar5 + gCurves[fo_curve].b * uVar10 +
|
||||
gCurves[fo_curve].d * 0x10000 +
|
||||
gCurves[fo_curve].a * (uVar10 * uVar5 >> 0x10) >>
|
||||
0xc;
|
||||
if ((int)uVar10 < 0) {
|
||||
uVar10 = 0;
|
||||
} else {
|
||||
if (0x10000 < uVar10) {
|
||||
uVar10 = 0x10000;
|
||||
}
|
||||
}
|
||||
uVar10 = (int)(uVar10 * vol) >> 0x10;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((fo_curve == 0xb) && (uVar10 < 0x180)) {
|
||||
uVar10 = 0x180;
|
||||
}
|
||||
SignalSema(g_EarTransSema);
|
||||
return uVar10;
|
||||
}
|
||||
|
||||
constexpr s16 unk_table_2[2056] = {
|
||||
0xB4, 0x0, 0xB4, 0x0, 0x5A, 0x5A, 0x10E, 0x10E, 0xB4, 0x0, 0xB4, 0x0, 0x5A,
|
||||
0x5A, 0x10E, 0x10E, 0xB4, 0x0, 0xB4, 0x0, 0x5A, 0x5A, 0x10E, 0x10E, 0xB4, 0x0,
|
||||
0xB4, 0x0, 0x5A, 0x5A, 0x10E, 0x10E, 0xB4, 0x0, 0xB4, 0x0, 0x5A, 0x5A, 0x10E,
|
||||
0x10E, 0xB3, 0x1, 0xB5, 0x167, 0x5B, 0x59, 0x10D, 0x10F, 0xB3, 0x1, 0xB5, 0x167,
|
||||
0x5B, 0x59, 0x10D, 0x10F, 0xB3, 0x1, 0xB5, 0x167, 0x5B, 0x59, 0x10D, 0x10F, 0xB3,
|
||||
0x1, 0xB5, 0x167, 0x5B, 0x59, 0x10D, 0x10F, 0xB2, 0x2, 0xB6, 0x166, 0x5C, 0x58,
|
||||
0x10C, 0x110, 0xB2, 0x2, 0xB6, 0x166, 0x5C, 0x58, 0x10C, 0x110, 0xB2, 0x2, 0xB6,
|
||||
0x166, 0x5C, 0x58, 0x10C, 0x110, 0xB2, 0x2, 0xB6, 0x166, 0x5C, 0x58, 0x10C, 0x110,
|
||||
0xB2, 0x2, 0xB6, 0x166, 0x5C, 0x58, 0x10C, 0x110, 0xB1, 0x3, 0xB7, 0x165, 0x5D,
|
||||
0x57, 0x10B, 0x111, 0xB1, 0x3, 0xB7, 0x165, 0x5D, 0x57, 0x10B, 0x111, 0xB1, 0x3,
|
||||
0xB7, 0x165, 0x5D, 0x57, 0x10B, 0x111, 0xB1, 0x3, 0xB7, 0x165, 0x5D, 0x57, 0x10B,
|
||||
0x111, 0xB0, 0x4, 0xB8, 0x164, 0x5E, 0x56, 0x10A, 0x112, 0xB0, 0x4, 0xB8, 0x164,
|
||||
0x5E, 0x56, 0x10A, 0x112, 0xB0, 0x4, 0xB8, 0x164, 0x5E, 0x56, 0x10A, 0x112, 0xB0,
|
||||
0x4, 0xB8, 0x164, 0x5E, 0x56, 0x10A, 0x112, 0xB0, 0x4, 0xB8, 0x164, 0x5E, 0x56,
|
||||
0x10A, 0x112, 0xAF, 0x5, 0xB9, 0x163, 0x5F, 0x55, 0x109, 0x113, 0xAF, 0x5, 0xB9,
|
||||
0x163, 0x5F, 0x55, 0x109, 0x113, 0xAF, 0x5, 0xB9, 0x163, 0x5F, 0x55, 0x109, 0x113,
|
||||
0xAF, 0x5, 0xB9, 0x163, 0x5F, 0x55, 0x109, 0x113, 0xAE, 0x6, 0xBA, 0x162, 0x60,
|
||||
0x54, 0x108, 0x114, 0xAE, 0x6, 0xBA, 0x162, 0x60, 0x54, 0x108, 0x114, 0xAE, 0x6,
|
||||
0xBA, 0x162, 0x60, 0x54, 0x108, 0x114, 0xAE, 0x6, 0xBA, 0x162, 0x60, 0x54, 0x108,
|
||||
0x114, 0xAE, 0x6, 0xBA, 0x162, 0x60, 0x54, 0x108, 0x114, 0xAD, 0x7, 0xBB, 0x161,
|
||||
0x61, 0x53, 0x107, 0x115, 0xAD, 0x7, 0xBB, 0x161, 0x61, 0x53, 0x107, 0x115, 0xAD,
|
||||
0x7, 0xBB, 0x161, 0x61, 0x53, 0x107, 0x115, 0xAD, 0x7, 0xBB, 0x161, 0x61, 0x53,
|
||||
0x107, 0x115, 0xAC, 0x8, 0xBC, 0x160, 0x62, 0x52, 0x106, 0x116, 0xAC, 0x8, 0xBC,
|
||||
0x160, 0x62, 0x52, 0x106, 0x116, 0xAC, 0x8, 0xBC, 0x160, 0x62, 0x52, 0x106, 0x116,
|
||||
0xAC, 0x8, 0xBC, 0x160, 0x62, 0x52, 0x106, 0x116, 0xAC, 0x8, 0xBC, 0x160, 0x62,
|
||||
0x52, 0x106, 0x116, 0xAB, 0x9, 0xBD, 0x15F, 0x63, 0x51, 0x105, 0x117, 0xAB, 0x9,
|
||||
0xBD, 0x15F, 0x63, 0x51, 0x105, 0x117, 0xAB, 0x9, 0xBD, 0x15F, 0x63, 0x51, 0x105,
|
||||
0x117, 0xAB, 0x9, 0xBD, 0x15F, 0x63, 0x51, 0x105, 0x117, 0xAB, 0x9, 0xBD, 0x15F,
|
||||
0x63, 0x51, 0x105, 0x117, 0xAA, 0xA, 0xBE, 0x15E, 0x64, 0x50, 0x104, 0x118, 0xAA,
|
||||
0xA, 0xBE, 0x15E, 0x64, 0x50, 0x104, 0x118, 0xAA, 0xA, 0xBE, 0x15E, 0x64, 0x50,
|
||||
0x104, 0x118, 0xAA, 0xA, 0xBE, 0x15E, 0x64, 0x50, 0x104, 0x118, 0xA9, 0xB, 0xBF,
|
||||
0x15D, 0x65, 0x4F, 0x103, 0x119, 0xA9, 0xB, 0xBF, 0x15D, 0x65, 0x4F, 0x103, 0x119,
|
||||
0xA9, 0xB, 0xBF, 0x15D, 0x65, 0x4F, 0x103, 0x119, 0xA9, 0xB, 0xBF, 0x15D, 0x65,
|
||||
0x4F, 0x103, 0x119, 0xA9, 0xB, 0xBF, 0x15D, 0x65, 0x4F, 0x103, 0x119, 0xA8, 0xC,
|
||||
0xC0, 0x15C, 0x66, 0x4E, 0x102, 0x11A, 0xA8, 0xC, 0xC0, 0x15C, 0x66, 0x4E, 0x102,
|
||||
0x11A, 0xA8, 0xC, 0xC0, 0x15C, 0x66, 0x4E, 0x102, 0x11A, 0xA8, 0xC, 0xC0, 0x15C,
|
||||
0x66, 0x4E, 0x102, 0x11A, 0xA8, 0xC, 0xC0, 0x15C, 0x66, 0x4E, 0x102, 0x11A, 0xA7,
|
||||
0xD, 0xC1, 0x15B, 0x67, 0x4D, 0x101, 0x11B, 0xA7, 0xD, 0xC1, 0x15B, 0x67, 0x4D,
|
||||
0x101, 0x11B, 0xA7, 0xD, 0xC1, 0x15B, 0x67, 0x4D, 0x101, 0x11B, 0xA7, 0xD, 0xC1,
|
||||
0x15B, 0x67, 0x4D, 0x101, 0x11B, 0xA6, 0xE, 0xC2, 0x15A, 0x68, 0x4C, 0x100, 0x11C,
|
||||
0xA6, 0xE, 0xC2, 0x15A, 0x68, 0x4C, 0x100, 0x11C, 0xA6, 0xE, 0xC2, 0x15A, 0x68,
|
||||
0x4C, 0x100, 0x11C, 0xA6, 0xE, 0xC2, 0x15A, 0x68, 0x4C, 0x100, 0x11C, 0xA6, 0xE,
|
||||
0xC2, 0x15A, 0x68, 0x4C, 0x100, 0x11C, 0xA5, 0xF, 0xC3, 0x159, 0x69, 0x4B, 0xFF,
|
||||
0x11D, 0xA5, 0xF, 0xC3, 0x159, 0x69, 0x4B, 0xFF, 0x11D, 0xA5, 0xF, 0xC3, 0x159,
|
||||
0x69, 0x4B, 0xFF, 0x11D, 0xA5, 0xF, 0xC3, 0x159, 0x69, 0x4B, 0xFF, 0x11D, 0xA5,
|
||||
0xF, 0xC3, 0x159, 0x69, 0x4B, 0xFF, 0x11D, 0xA4, 0x10, 0xC4, 0x158, 0x6A, 0x4A,
|
||||
0xFE, 0x11E, 0xA4, 0x10, 0xC4, 0x158, 0x6A, 0x4A, 0xFE, 0x11E, 0xA4, 0x10, 0xC4,
|
||||
0x158, 0x6A, 0x4A, 0xFE, 0x11E, 0xA4, 0x10, 0xC4, 0x158, 0x6A, 0x4A, 0xFE, 0x11E,
|
||||
0xA4, 0x10, 0xC4, 0x158, 0x6A, 0x4A, 0xFE, 0x11E, 0xA3, 0x11, 0xC5, 0x157, 0x6B,
|
||||
0x49, 0xFD, 0x11F, 0xA3, 0x11, 0xC5, 0x157, 0x6B, 0x49, 0xFD, 0x11F, 0xA3, 0x11,
|
||||
0xC5, 0x157, 0x6B, 0x49, 0xFD, 0x11F, 0xA3, 0x11, 0xC5, 0x157, 0x6B, 0x49, 0xFD,
|
||||
0x11F, 0xA3, 0x11, 0xC5, 0x157, 0x6B, 0x49, 0xFD, 0x11F, 0xA2, 0x12, 0xC6, 0x156,
|
||||
0x6C, 0x48, 0xFC, 0x120, 0xA2, 0x12, 0xC6, 0x156, 0x6C, 0x48, 0xFC, 0x120, 0xA2,
|
||||
0x12, 0xC6, 0x156, 0x6C, 0x48, 0xFC, 0x120, 0xA2, 0x12, 0xC6, 0x156, 0x6C, 0x48,
|
||||
0xFC, 0x120, 0xA2, 0x12, 0xC6, 0x156, 0x6C, 0x48, 0xFC, 0x120, 0xA1, 0x13, 0xC7,
|
||||
0x155, 0x6D, 0x47, 0xFB, 0x121, 0xA1, 0x13, 0xC7, 0x155, 0x6D, 0x47, 0xFB, 0x121,
|
||||
0xA1, 0x13, 0xC7, 0x155, 0x6D, 0x47, 0xFB, 0x121, 0xA1, 0x13, 0xC7, 0x155, 0x6D,
|
||||
0x47, 0xFB, 0x121, 0xA1, 0x13, 0xC7, 0x155, 0x6D, 0x47, 0xFB, 0x121, 0xA0, 0x14,
|
||||
0xC8, 0x154, 0x6E, 0x46, 0xFA, 0x122, 0xA0, 0x14, 0xC8, 0x154, 0x6E, 0x46, 0xFA,
|
||||
0x122, 0xA0, 0x14, 0xC8, 0x154, 0x6E, 0x46, 0xFA, 0x122, 0xA0, 0x14, 0xC8, 0x154,
|
||||
0x6E, 0x46, 0xFA, 0x122, 0xA0, 0x14, 0xC8, 0x154, 0x6E, 0x46, 0xFA, 0x122, 0x9F,
|
||||
0x15, 0xC9, 0x153, 0x6F, 0x45, 0xF9, 0x123, 0x9F, 0x15, 0xC9, 0x153, 0x6F, 0x45,
|
||||
0xF9, 0x123, 0x9F, 0x15, 0xC9, 0x153, 0x6F, 0x45, 0xF9, 0x123, 0x9F, 0x15, 0xC9,
|
||||
0x153, 0x6F, 0x45, 0xF9, 0x123, 0x9F, 0x15, 0xC9, 0x153, 0x6F, 0x45, 0xF9, 0x123,
|
||||
0x9E, 0x16, 0xCA, 0x152, 0x70, 0x44, 0xF8, 0x124, 0x9E, 0x16, 0xCA, 0x152, 0x70,
|
||||
0x44, 0xF8, 0x124, 0x9E, 0x16, 0xCA, 0x152, 0x70, 0x44, 0xF8, 0x124, 0x9E, 0x16,
|
||||
0xCA, 0x152, 0x70, 0x44, 0xF8, 0x124, 0x9E, 0x16, 0xCA, 0x152, 0x70, 0x44, 0xF8,
|
||||
0x124, 0x9D, 0x17, 0xCB, 0x151, 0x71, 0x43, 0xF7, 0x125, 0x9D, 0x17, 0xCB, 0x151,
|
||||
0x71, 0x43, 0xF7, 0x125, 0x9D, 0x17, 0xCB, 0x151, 0x71, 0x43, 0xF7, 0x125, 0x9D,
|
||||
0x17, 0xCB, 0x151, 0x71, 0x43, 0xF7, 0x125, 0x9D, 0x17, 0xCB, 0x151, 0x71, 0x43,
|
||||
0xF7, 0x125, 0x9D, 0x17, 0xCB, 0x151, 0x71, 0x43, 0xF7, 0x125, 0x9C, 0x18, 0xCC,
|
||||
0x150, 0x72, 0x42, 0xF6, 0x126, 0x9C, 0x18, 0xCC, 0x150, 0x72, 0x42, 0xF6, 0x126,
|
||||
0x9C, 0x18, 0xCC, 0x150, 0x72, 0x42, 0xF6, 0x126, 0x9C, 0x18, 0xCC, 0x150, 0x72,
|
||||
0x42, 0xF6, 0x126, 0x9C, 0x18, 0xCC, 0x150, 0x72, 0x42, 0xF6, 0x126, 0x9B, 0x19,
|
||||
0xCD, 0x14F, 0x73, 0x41, 0xF5, 0x127, 0x9B, 0x19, 0xCD, 0x14F, 0x73, 0x41, 0xF5,
|
||||
0x127, 0x9B, 0x19, 0xCD, 0x14F, 0x73, 0x41, 0xF5, 0x127, 0x9B, 0x19, 0xCD, 0x14F,
|
||||
0x73, 0x41, 0xF5, 0x127, 0x9B, 0x19, 0xCD, 0x14F, 0x73, 0x41, 0xF5, 0x127, 0x9A,
|
||||
0x1A, 0xCE, 0x14E, 0x74, 0x40, 0xF4, 0x128, 0x9A, 0x1A, 0xCE, 0x14E, 0x74, 0x40,
|
||||
0xF4, 0x128, 0x9A, 0x1A, 0xCE, 0x14E, 0x74, 0x40, 0xF4, 0x128, 0x9A, 0x1A, 0xCE,
|
||||
0x14E, 0x74, 0x40, 0xF4, 0x128, 0x9A, 0x1A, 0xCE, 0x14E, 0x74, 0x40, 0xF4, 0x128,
|
||||
0x9A, 0x1A, 0xCE, 0x14E, 0x74, 0x40, 0xF4, 0x128, 0x99, 0x1B, 0xCF, 0x14D, 0x75,
|
||||
0x3F, 0xF3, 0x129, 0x99, 0x1B, 0xCF, 0x14D, 0x75, 0x3F, 0xF3, 0x129, 0x99, 0x1B,
|
||||
0xCF, 0x14D, 0x75, 0x3F, 0xF3, 0x129, 0x99, 0x1B, 0xCF, 0x14D, 0x75, 0x3F, 0xF3,
|
||||
0x129, 0x99, 0x1B, 0xCF, 0x14D, 0x75, 0x3F, 0xF3, 0x129, 0x99, 0x1B, 0xCF, 0x14D,
|
||||
0x75, 0x3F, 0xF3, 0x129, 0x98, 0x1C, 0xD0, 0x14C, 0x76, 0x3E, 0xF2, 0x12A, 0x98,
|
||||
0x1C, 0xD0, 0x14C, 0x76, 0x3E, 0xF2, 0x12A, 0x98, 0x1C, 0xD0, 0x14C, 0x76, 0x3E,
|
||||
0xF2, 0x12A, 0x98, 0x1C, 0xD0, 0x14C, 0x76, 0x3E, 0xF2, 0x12A, 0x98, 0x1C, 0xD0,
|
||||
0x14C, 0x76, 0x3E, 0xF2, 0x12A, 0x97, 0x1D, 0xD1, 0x14B, 0x77, 0x3D, 0xF1, 0x12B,
|
||||
0x97, 0x1D, 0xD1, 0x14B, 0x77, 0x3D, 0xF1, 0x12B, 0x97, 0x1D, 0xD1, 0x14B, 0x77,
|
||||
0x3D, 0xF1, 0x12B, 0x97, 0x1D, 0xD1, 0x14B, 0x77, 0x3D, 0xF1, 0x12B, 0x97, 0x1D,
|
||||
0xD1, 0x14B, 0x77, 0x3D, 0xF1, 0x12B, 0x97, 0x1D, 0xD1, 0x14B, 0x77, 0x3D, 0xF1,
|
||||
0x12B, 0x96, 0x1E, 0xD2, 0x14A, 0x78, 0x3C, 0xF0, 0x12C, 0x96, 0x1E, 0xD2, 0x14A,
|
||||
0x78, 0x3C, 0xF0, 0x12C, 0x96, 0x1E, 0xD2, 0x14A, 0x78, 0x3C, 0xF0, 0x12C, 0x96,
|
||||
0x1E, 0xD2, 0x14A, 0x78, 0x3C, 0xF0, 0x12C, 0x96, 0x1E, 0xD2, 0x14A, 0x78, 0x3C,
|
||||
0xF0, 0x12C, 0x96, 0x1E, 0xD2, 0x14A, 0x78, 0x3C, 0xF0, 0x12C, 0x95, 0x1F, 0xD3,
|
||||
0x149, 0x79, 0x3B, 0xEF, 0x12D, 0x95, 0x1F, 0xD3, 0x149, 0x79, 0x3B, 0xEF, 0x12D,
|
||||
0x95, 0x1F, 0xD3, 0x149, 0x79, 0x3B, 0xEF, 0x12D, 0x95, 0x1F, 0xD3, 0x149, 0x79,
|
||||
0x3B, 0xEF, 0x12D, 0x95, 0x1F, 0xD3, 0x149, 0x79, 0x3B, 0xEF, 0x12D, 0x95, 0x1F,
|
||||
0xD3, 0x149, 0x79, 0x3B, 0xEF, 0x12D, 0x94, 0x20, 0xD4, 0x148, 0x7A, 0x3A, 0xEE,
|
||||
0x12E, 0x94, 0x20, 0xD4, 0x148, 0x7A, 0x3A, 0xEE, 0x12E, 0x94, 0x20, 0xD4, 0x148,
|
||||
0x7A, 0x3A, 0xEE, 0x12E, 0x94, 0x20, 0xD4, 0x148, 0x7A, 0x3A, 0xEE, 0x12E, 0x94,
|
||||
0x20, 0xD4, 0x148, 0x7A, 0x3A, 0xEE, 0x12E, 0x94, 0x20, 0xD4, 0x148, 0x7A, 0x3A,
|
||||
0xEE, 0x12E, 0x94, 0x20, 0xD4, 0x148, 0x7A, 0x3A, 0xEE, 0x12E, 0x93, 0x21, 0xD5,
|
||||
0x147, 0x7B, 0x39, 0xED, 0x12F, 0x93, 0x21, 0xD5, 0x147, 0x7B, 0x39, 0xED, 0x12F,
|
||||
0x93, 0x21, 0xD5, 0x147, 0x7B, 0x39, 0xED, 0x12F, 0x93, 0x21, 0xD5, 0x147, 0x7B,
|
||||
0x39, 0xED, 0x12F, 0x93, 0x21, 0xD5, 0x147, 0x7B, 0x39, 0xED, 0x12F, 0x93, 0x21,
|
||||
0xD5, 0x147, 0x7B, 0x39, 0xED, 0x12F, 0x92, 0x22, 0xD6, 0x146, 0x7C, 0x38, 0xEC,
|
||||
0x130, 0x92, 0x22, 0xD6, 0x146, 0x7C, 0x38, 0xEC, 0x130, 0x92, 0x22, 0xD6, 0x146,
|
||||
0x7C, 0x38, 0xEC, 0x130, 0x92, 0x22, 0xD6, 0x146, 0x7C, 0x38, 0xEC, 0x130, 0x92,
|
||||
0x22, 0xD6, 0x146, 0x7C, 0x38, 0xEC, 0x130, 0x92, 0x22, 0xD6, 0x146, 0x7C, 0x38,
|
||||
0xEC, 0x130, 0x92, 0x22, 0xD6, 0x146, 0x7C, 0x38, 0xEC, 0x130, 0x91, 0x23, 0xD7,
|
||||
0x145, 0x7D, 0x37, 0xEB, 0x131, 0x91, 0x23, 0xD7, 0x145, 0x7D, 0x37, 0xEB, 0x131,
|
||||
0x91, 0x23, 0xD7, 0x145, 0x7D, 0x37, 0xEB, 0x131, 0x91, 0x23, 0xD7, 0x145, 0x7D,
|
||||
0x37, 0xEB, 0x131, 0x91, 0x23, 0xD7, 0x145, 0x7D, 0x37, 0xEB, 0x131, 0x91, 0x23,
|
||||
0xD7, 0x145, 0x7D, 0x37, 0xEB, 0x131, 0x91, 0x23, 0xD7, 0x145, 0x7D, 0x37, 0xEB,
|
||||
0x131, 0x90, 0x24, 0xD8, 0x144, 0x7E, 0x36, 0xEA, 0x132, 0x90, 0x24, 0xD8, 0x144,
|
||||
0x7E, 0x36, 0xEA, 0x132, 0x90, 0x24, 0xD8, 0x144, 0x7E, 0x36, 0xEA, 0x132, 0x90,
|
||||
0x24, 0xD8, 0x144, 0x7E, 0x36, 0xEA, 0x132, 0x90, 0x24, 0xD8, 0x144, 0x7E, 0x36,
|
||||
0xEA, 0x132, 0x90, 0x24, 0xD8, 0x144, 0x7E, 0x36, 0xEA, 0x132, 0x8F, 0x25, 0xD9,
|
||||
0x143, 0x7F, 0x35, 0xE9, 0x133, 0x8F, 0x25, 0xD9, 0x143, 0x7F, 0x35, 0xE9, 0x133,
|
||||
0x8F, 0x25, 0xD9, 0x143, 0x7F, 0x35, 0xE9, 0x133, 0x8F, 0x25, 0xD9, 0x143, 0x7F,
|
||||
0x35, 0xE9, 0x133, 0x8F, 0x25, 0xD9, 0x143, 0x7F, 0x35, 0xE9, 0x133, 0x8F, 0x25,
|
||||
0xD9, 0x143, 0x7F, 0x35, 0xE9, 0x133, 0x8F, 0x25, 0xD9, 0x143, 0x7F, 0x35, 0xE9,
|
||||
0x133, 0x8F, 0x25, 0xD9, 0x143, 0x7F, 0x35, 0xE9, 0x133, 0x8E, 0x26, 0xDA, 0x142,
|
||||
0x80, 0x34, 0xE8, 0x134, 0x8E, 0x26, 0xDA, 0x142, 0x80, 0x34, 0xE8, 0x134, 0x8E,
|
||||
0x26, 0xDA, 0x142, 0x80, 0x34, 0xE8, 0x134, 0x8E, 0x26, 0xDA, 0x142, 0x80, 0x34,
|
||||
0xE8, 0x134, 0x8E, 0x26, 0xDA, 0x142, 0x80, 0x34, 0xE8, 0x134, 0x8E, 0x26, 0xDA,
|
||||
0x142, 0x80, 0x34, 0xE8, 0x134, 0x8E, 0x26, 0xDA, 0x142, 0x80, 0x34, 0xE8, 0x134,
|
||||
0x8D, 0x27, 0xDB, 0x141, 0x81, 0x33, 0xE7, 0x135, 0x8D, 0x27, 0xDB, 0x141, 0x81,
|
||||
0x33, 0xE7, 0x135, 0x8D, 0x27, 0xDB, 0x141, 0x81, 0x33, 0xE7, 0x135, 0x8D, 0x27,
|
||||
0xDB, 0x141, 0x81, 0x33, 0xE7, 0x135, 0x8D, 0x27, 0xDB, 0x141, 0x81, 0x33, 0xE7,
|
||||
0x135, 0x8D, 0x27, 0xDB, 0x141, 0x81, 0x33, 0xE7, 0x135, 0x8D, 0x27, 0xDB, 0x141,
|
||||
0x81, 0x33, 0xE7, 0x135, 0x8C, 0x28, 0xDC, 0x140, 0x82, 0x32, 0xE6, 0x136, 0x8C,
|
||||
0x28, 0xDC, 0x140, 0x82, 0x32, 0xE6, 0x136, 0x8C, 0x28, 0xDC, 0x140, 0x82, 0x32,
|
||||
0xE6, 0x136, 0x8C, 0x28, 0xDC, 0x140, 0x82, 0x32, 0xE6, 0x136, 0x8C, 0x28, 0xDC,
|
||||
0x140, 0x82, 0x32, 0xE6, 0x136, 0x8C, 0x28, 0xDC, 0x140, 0x82, 0x32, 0xE6, 0x136,
|
||||
0x8C, 0x28, 0xDC, 0x140, 0x82, 0x32, 0xE6, 0x136, 0x8C, 0x28, 0xDC, 0x140, 0x82,
|
||||
0x32, 0xE6, 0x136, 0x8B, 0x29, 0xDD, 0x13F, 0x83, 0x31, 0xE5, 0x137, 0x8B, 0x29,
|
||||
0xDD, 0x13F, 0x83, 0x31, 0xE5, 0x137, 0x8B, 0x29, 0xDD, 0x13F, 0x83, 0x31, 0xE5,
|
||||
0x137, 0x8B, 0x29, 0xDD, 0x13F, 0x83, 0x31, 0xE5, 0x137, 0x8B, 0x29, 0xDD, 0x13F,
|
||||
0x83, 0x31, 0xE5, 0x137, 0x8B, 0x29, 0xDD, 0x13F, 0x83, 0x31, 0xE5, 0x137, 0x8B,
|
||||
0x29, 0xDD, 0x13F, 0x83, 0x31, 0xE5, 0x137, 0x8B, 0x29, 0xDD, 0x13F, 0x83, 0x31,
|
||||
0xE5, 0x137, 0x8A, 0x2A, 0xDE, 0x13E, 0x84, 0x30, 0xE4, 0x138, 0x8A, 0x2A, 0xDE,
|
||||
0x13E, 0x84, 0x30, 0xE4, 0x138, 0x8A, 0x2A, 0xDE, 0x13E, 0x84, 0x30, 0xE4, 0x138,
|
||||
0x8A, 0x2A, 0xDE, 0x13E, 0x84, 0x30, 0xE4, 0x138, 0x8A, 0x2A, 0xDE, 0x13E, 0x84,
|
||||
0x30, 0xE4, 0x138, 0x8A, 0x2A, 0xDE, 0x13E, 0x84, 0x30, 0xE4, 0x138, 0x8A, 0x2A,
|
||||
0xDE, 0x13E, 0x84, 0x30, 0xE4, 0x138, 0x8A, 0x2A, 0xDE, 0x13E, 0x84, 0x30, 0xE4,
|
||||
0x138, 0x89, 0x2B, 0xDF, 0x13D, 0x85, 0x2F, 0xE3, 0x139, 0x89, 0x2B, 0xDF, 0x13D,
|
||||
0x85, 0x2F, 0xE3, 0x139, 0x89, 0x2B, 0xDF, 0x13D, 0x85, 0x2F, 0xE3, 0x139, 0x89,
|
||||
0x2B, 0xDF, 0x13D, 0x85, 0x2F, 0xE3, 0x139, 0x89, 0x2B, 0xDF, 0x13D, 0x85, 0x2F,
|
||||
0xE3, 0x139, 0x89, 0x2B, 0xDF, 0x13D, 0x85, 0x2F, 0xE3, 0x139, 0x89, 0x2B, 0xDF,
|
||||
0x13D, 0x85, 0x2F, 0xE3, 0x139, 0x89, 0x2B, 0xDF, 0x13D, 0x85, 0x2F, 0xE3, 0x139,
|
||||
0x89, 0x2B, 0xDF, 0x13D, 0x85, 0x2F, 0xE3, 0x139, 0x88, 0x2C, 0xE0, 0x13C, 0x86,
|
||||
0x2E, 0xE2, 0x13A, 0x88, 0x2C, 0xE0, 0x13C, 0x86, 0x2E, 0xE2, 0x13A, 0x88, 0x2C,
|
||||
0xE0, 0x13C, 0x86, 0x2E, 0xE2, 0x13A, 0x88, 0x2C, 0xE0, 0x13C, 0x86, 0x2E, 0xE2,
|
||||
0x13A, 0x88, 0x2C, 0xE0, 0x13C, 0x86, 0x2E, 0xE2, 0x13A, 0x88, 0x2C, 0xE0, 0x13C,
|
||||
0x86, 0x2E, 0xE2, 0x13A, 0x88, 0x2C, 0xE0, 0x13C, 0x86, 0x2E, 0xE2, 0x13A, 0x88,
|
||||
0x2C, 0xE0, 0x13C, 0x86, 0x2E, 0xE2, 0x13A, 0x87, 0x2D, 0xE1, 0x13B, 0x87, 0x2D,
|
||||
0xE1, 0x13B,
|
||||
};
|
||||
|
||||
s32 CalculateAngle(s32* trans, u32 fo_curve, u32 param_3) {
|
||||
u32 uVar2;
|
||||
int iVar3;
|
||||
u32 uVar4;
|
||||
u32 uVar5;
|
||||
int iVar6;
|
||||
int iVar7;
|
||||
u32 uVar8;
|
||||
ASSERT(fo_curve < 0x29);
|
||||
WaitSema(g_EarTransSema);
|
||||
if (unktable[fo_curve] != 0) {
|
||||
if (unktable[fo_curve + 0x2c] == 0) {
|
||||
if (unktable[fo_curve + 0x58] != 0) {
|
||||
trans = gEarTrans + 3;
|
||||
}
|
||||
iVar6 = trans[1];
|
||||
iVar3 = gCamTrans[0] - *trans;
|
||||
iVar7 = gCamTrans[2] - trans[2];
|
||||
} else {
|
||||
iVar7 = gCamForward[0] - gCamTrans[0];
|
||||
iVar3 = gCamTrans[2] - gCamForward[2];
|
||||
iVar6 = gCamForward[1];
|
||||
}
|
||||
iVar6 = gCamTrans[1] - iVar6;
|
||||
if (((iVar3 + 0x200168U | iVar6 + 0x200168U | iVar7 + 0x200168U) & 0xffc00000) != 0) {
|
||||
if (iVar3 < 0) {
|
||||
iVar3 = iVar3 + 0x3ff;
|
||||
}
|
||||
iVar3 = iVar3 >> 10;
|
||||
if (iVar6 < 0) {
|
||||
iVar6 = iVar6 + 0x3ff;
|
||||
}
|
||||
iVar6 = iVar6 >> 10;
|
||||
if (iVar7 < 0) {
|
||||
iVar7 = iVar7 + 0x3ff;
|
||||
}
|
||||
iVar7 = iVar7 >> 10;
|
||||
}
|
||||
uVar8 = iVar3 * gCamLeft[0] + iVar6 * gCamLeft[1] + iVar7 * gCamLeft[2];
|
||||
uVar5 = uVar8;
|
||||
if ((int)uVar8 < 0) {
|
||||
uVar5 = -uVar8;
|
||||
}
|
||||
uVar2 = iVar3 * gCamForward[0] + iVar6 * gCamForward[1] + iVar7 * gCamForward[2];
|
||||
uVar4 = uVar2;
|
||||
if ((int)uVar2 < 0) {
|
||||
uVar4 = -uVar2;
|
||||
}
|
||||
if ((0x1ffff < (int)uVar5) || (0x1ffff < (int)uVar4)) {
|
||||
uVar4 = (int)uVar4 >> 8;
|
||||
uVar5 = (int)uVar5 >> 8;
|
||||
}
|
||||
if ((uVar4 != 0) || (uVar5 != 0)) {
|
||||
uVar8 = (uVar8 & 0x80000000) >> 0x1e | uVar2 >> 0x1f;
|
||||
if ((int)uVar4 < (int)uVar5) {
|
||||
if (uVar5 == 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
uVar8 = uVar8 | (int)(uVar4 << 8) / (int)uVar5 << 3 | 4;
|
||||
} else {
|
||||
if (uVar4 == 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
uVar8 = uVar8 | (int)(uVar5 << 8) / (int)uVar4 << 3;
|
||||
}
|
||||
ASSERT(uVar8 < 2056);
|
||||
iVar3 = (int)(short)unk_table_2[uVar8];
|
||||
iVar6 = iVar3;
|
||||
if (((param_3 != 0) && (iVar6 = iVar3, g_CameraInvert != 0)) && (iVar6 = 0, iVar3 != 0)) {
|
||||
iVar6 = 0x168 - iVar3;
|
||||
}
|
||||
SignalSema(g_EarTransSema);
|
||||
return iVar6;
|
||||
}
|
||||
}
|
||||
SignalSema(g_EarTransSema);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 GetVolume(SoundInfo* sound) {
|
||||
return CalculateFalloffVolume(sound->params.trans, sound->params.volume, sound->params.fo_curve,
|
||||
sound->params.fo_min, sound->params.fo_max, nullptr, nullptr);
|
||||
}
|
||||
|
||||
s32 GetPan(SoundInfo* sound) {
|
||||
return CalculateAngle(sound->params.trans, sound->params.fo_curve, 1);
|
||||
}
|
||||
|
||||
void UpdateLocation(SoundInfo* sound) {
|
||||
auto handle = snd_SoundIsStillPlaying(sound->sound_handle);
|
||||
sound->sound_handle = handle;
|
||||
if (handle == 0) {
|
||||
sound->id = 0;
|
||||
} else {
|
||||
auto vol = GetVolume(sound);
|
||||
if (vol == 0 && unktable[(int)(sound->params).fo_curve + 0xb0] == 0) {
|
||||
snd_StopSound(sound->sound_handle);
|
||||
} else {
|
||||
auto pan = GetPan(sound);
|
||||
// ovrld_log(LogCategory::WARN, "HACK: falling back to old version of setting vol/pan");
|
||||
snd_SetSoundVolPan(handle, vol, pan);
|
||||
|
||||
// FUN_00013e0c(handle,4,0,pan,0,0);
|
||||
// if ((short)(sound->params).mask < 0) {
|
||||
// FUN_00013d6c(handle,vol,0x40);
|
||||
// }
|
||||
// else {
|
||||
// snd_SetSoundVolPan(handle,vol,0xfffffffe,4);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAutoVol(SoundInfo* snd, int time) {
|
||||
bool bVar1;
|
||||
auto iVar6 = snd->auto_time;
|
||||
auto iVar4 = snd->new_volume;
|
||||
if (time < iVar6) {
|
||||
auto iVar5 = iVar4;
|
||||
if (iVar4 == -4) {
|
||||
iVar5 = 0;
|
||||
}
|
||||
auto vol = (snd->params).volume;
|
||||
int new_vol;
|
||||
if (iVar6 == 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
iVar5 = ((iVar5 - vol) * time) / iVar6;
|
||||
if (iVar5 < 0) {
|
||||
new_vol = vol + iVar5;
|
||||
bVar1 = new_vol < iVar4;
|
||||
} else {
|
||||
new_vol = vol + iVar5;
|
||||
if (iVar5 < 1) {
|
||||
new_vol = vol + 1;
|
||||
}
|
||||
bVar1 = iVar4 < new_vol;
|
||||
}
|
||||
(snd->params).volume = new_vol;
|
||||
if (bVar1) {
|
||||
(snd->params).volume = iVar4;
|
||||
}
|
||||
snd->auto_time = iVar6 - time;
|
||||
} else {
|
||||
if (iVar4 == -4) {
|
||||
snd_StopSound(snd->sound_handle);
|
||||
snd->id = 0;
|
||||
} else {
|
||||
(snd->params).volume = iVar4;
|
||||
}
|
||||
snd->auto_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateVolume(SoundInfo* sound) {
|
||||
auto handle = snd_SoundIsStillPlaying(sound->sound_handle);
|
||||
sound->sound_handle = handle;
|
||||
if (handle == 0) {
|
||||
sound->id = 0;
|
||||
} else {
|
||||
if ((s16)(sound->params).mask < 0) {
|
||||
// idk
|
||||
snd_SetSoundVolPan(handle, GetVolume(sound), -2);
|
||||
|
||||
// FUN_00013d6c(handle, GetVolume(sound), 0x40, 4);
|
||||
} else {
|
||||
snd_SetSoundVolPan(handle, GetVolume(sound), -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetEarTrans(const s32* ear_trans0,
|
||||
const s32* ear_trans1,
|
||||
const s32* cam_trans,
|
||||
const s32* cam_fwd,
|
||||
const s32* cam_left,
|
||||
s32 cam_scale,
|
||||
bool cam_inverted) {
|
||||
auto tick = snd_GetTick();
|
||||
auto time = tick - gLastTick;
|
||||
gLastTick = tick;
|
||||
WaitSema(g_EarTransSema);
|
||||
gEarTrans[0] = *ear_trans0;
|
||||
g_CameraInvert = cam_inverted;
|
||||
gEarTrans[1] = ear_trans0[1];
|
||||
gEarTrans[2] = ear_trans0[2];
|
||||
gEarTrans[3] = *ear_trans1;
|
||||
gEarTrans[4] = ear_trans1[1];
|
||||
gEarTrans[5] = ear_trans1[2];
|
||||
gCamTrans[0] = *cam_trans;
|
||||
gCamTrans[1] = cam_trans[1];
|
||||
gCamTrans[2] = cam_trans[2];
|
||||
gCamForward[0] = *cam_fwd;
|
||||
gCamForward[1] = cam_fwd[1];
|
||||
gCamForward[2] = cam_fwd[2];
|
||||
gCamLeft[0] = *cam_left;
|
||||
gCamLeft[1] = cam_left[1];
|
||||
gCamLeft[2] = cam_left[2];
|
||||
gCamScale = cam_scale;
|
||||
SignalSema(g_EarTransSema);
|
||||
|
||||
for (auto& sound : gSounds) {
|
||||
if (sound.id) {
|
||||
if (sound.auto_time) {
|
||||
UpdateAutoVol(&sound, time);
|
||||
}
|
||||
UpdateLocation(&sound);
|
||||
}
|
||||
}
|
||||
|
||||
auto* cmd = g_aVagCmds;
|
||||
s32 iVar2 = 5;
|
||||
do {
|
||||
if ((cmd->music_flag == 0) && (cmd->maybe_sound_handler != 0)) {
|
||||
if ((cmd->flags.scanned == 0) || (cmd->flags.bit8 != 0)) {
|
||||
if (cmd->flags.bit20 == 0) {
|
||||
if ((u32)cmd->play_volume < 0x11) {
|
||||
cmd->play_volume = 0;
|
||||
} else {
|
||||
cmd->play_volume = cmd->play_volume - 0x10;
|
||||
}
|
||||
SetVAGVol(cmd);
|
||||
if (cmd->play_volume != 0)
|
||||
goto LAB_0000db94;
|
||||
}
|
||||
LAB_0000db78:
|
||||
StopVagStream(cmd);
|
||||
} else {
|
||||
time = snd_SoundIsStillPlaying(cmd->id);
|
||||
if (time != 0)
|
||||
goto LAB_0000db88;
|
||||
if (cmd->flags.bit20 != 0)
|
||||
goto LAB_0000db78;
|
||||
// CpuSuspendIntr(local_28);
|
||||
cmd->flags.bit8 = 1;
|
||||
// CpuResumeIntr(local_28[0]);
|
||||
}
|
||||
} else {
|
||||
LAB_0000db88:
|
||||
SetVAGVol(cmd);
|
||||
}
|
||||
LAB_0000db94:
|
||||
iVar2 = iVar2 + -1;
|
||||
cmd = cmd + 1;
|
||||
if (iVar2 < 0) {
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void SetCurve(int param_1,
|
||||
u32 param_2,
|
||||
u32 param_3,
|
||||
uint8_t param_4,
|
||||
uint8_t param_5,
|
||||
uint8_t param_6,
|
||||
uint8_t param_7,
|
||||
uint8_t param_8) {
|
||||
gCurves[param_1].c = (param_3 - param_2) + -0x1000;
|
||||
gCurves[param_1].d = 0x1000;
|
||||
unktable[param_1 + 0xb0] = param_8;
|
||||
gCurves[param_1].b = param_2 + param_3 * -3;
|
||||
unktable[param_1] = param_4;
|
||||
unktable[param_1 + 0x2c] = param_5;
|
||||
unktable[param_1 + 0x58] = param_6;
|
||||
unktable[param_1 + 0x84] = param_7;
|
||||
gCurves[param_1].a = param_3 * 2;
|
||||
}
|
||||
|
||||
void SetPlaybackMode(s32 mode) {
|
||||
g_nPlaybackMode = mode;
|
||||
snd_SetPlayBackMode(mode);
|
||||
}
|
||||
|
||||
} // namespace jak3
|
50
game/overlord/jak3/ssound.h
Normal file
50
game/overlord/jak3/ssound.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/overlord/jak3/rpc_interface.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_ssound();
|
||||
void InitSound();
|
||||
|
||||
extern s32 g_n989Semaphore;
|
||||
extern bool g_bSoundEnable;
|
||||
|
||||
struct SoundInfo {
|
||||
SoundName name;
|
||||
s32 id;
|
||||
s32 sound_handle;
|
||||
s32 new_volume;
|
||||
s32 auto_time;
|
||||
SoundPlayParams params;
|
||||
};
|
||||
|
||||
struct VolumePair {
|
||||
s16 left;
|
||||
s16 right;
|
||||
};
|
||||
|
||||
SoundInfo* LookupSound(s32 id);
|
||||
SoundInfo* AllocateSound();
|
||||
int GetFalloffCurve(int fo_curve);
|
||||
s32 GetVolume(SoundInfo* sound);
|
||||
s32 GetPan(SoundInfo* sound);
|
||||
void UpdateVolume(SoundInfo* sound);
|
||||
void KillSoundsInGroup(u32 group);
|
||||
void SetEarTrans(const s32* ear_trans0,
|
||||
const s32* ear_trans1,
|
||||
const s32* cam_trans,
|
||||
const s32* cam_forward,
|
||||
const s32* cam_left,
|
||||
s32 cam_scale,
|
||||
bool cam_inverted);
|
||||
void SetPlaybackMode(s32 mode);
|
||||
void SetCurve(int curve_idx, u32, u32, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
|
||||
u32 CalculateFalloffVolume(s32* trans, u32 vol, u32 fo_curve, u32 fo_min, u32 fo_max, u32*, u32*);
|
||||
s32 CalculateAngle(s32* trans, u32 fo_curve, u32);
|
||||
|
||||
extern u32 g_anStreamVoice[6];
|
||||
extern VolumePair g_aPanTable[361];
|
||||
extern bool g_CameraInvert;
|
||||
} // namespace jak3
|
282
game/overlord/jak3/stream.cpp
Normal file
282
game/overlord/jak3/stream.cpp
Normal file
@ -0,0 +1,282 @@
|
||||
#include "stream.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "game/overlord/jak3/iso_api.h"
|
||||
#include "game/overlord/jak3/iso_cd.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/overlord/jak3/rpc_interface.h"
|
||||
#include "game/overlord/jak3/streamlist.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
|
||||
using namespace iop;
|
||||
|
||||
constexpr int kStrBufSize = sizeof(RPC_Str_Cmd);
|
||||
static RPC_Str_Cmd sSTRBuf;
|
||||
|
||||
constexpr int kNumPlayCmds = 4;
|
||||
constexpr int kRpcBuf2Size = sizeof(RPC_Play_Cmd) * kNumPlayCmds;
|
||||
static RPC_Play_Cmd sRPCBuf2[kNumPlayCmds];
|
||||
|
||||
constexpr int SECTOR_TABLE_SIZE = 512;
|
||||
|
||||
struct StrFileHeader {
|
||||
u32 sectors[SECTOR_TABLE_SIZE]; // start of chunk, in sectors. including this sector.
|
||||
u32 sizes[SECTOR_TABLE_SIZE]; // size of chunk, in bytes. always an integer number of sectors
|
||||
};
|
||||
|
||||
static_assert(sizeof(StrFileHeader) == 0x1000, "Sector header size");
|
||||
|
||||
struct CacheEntry {
|
||||
ISOFileDef* filedef = nullptr;
|
||||
s32 countdown = 0;
|
||||
StrFileHeader header;
|
||||
};
|
||||
|
||||
constexpr int STR_INDEX_CACHE_SIZE = 4;
|
||||
CacheEntry sCache[STR_INDEX_CACHE_SIZE];
|
||||
|
||||
void jak3_overlord_init_globals_stream() {}
|
||||
|
||||
u32 STRThread() {
|
||||
sceSifQueueData dq;
|
||||
sceSifServeData serve;
|
||||
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, RpcId::STR, RPC_STR, &sSTRBuf, kStrBufSize, nullptr, nullptr, &dq);
|
||||
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 PLAYThread() {
|
||||
sceSifQueueData dq;
|
||||
sceSifServeData serve;
|
||||
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, RpcId::PLAY, RPC_PLAY, &sRPCBuf2, kRpcBuf2Size, nullptr, nullptr, &dq);
|
||||
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* RPC_STR(unsigned int, void* msg_in, int size) {
|
||||
auto* msg = (RPC_Str_Cmd*)msg_in;
|
||||
ASSERT(size == sizeof(RPC_Str_Cmd));
|
||||
|
||||
if (msg->section < 0) {
|
||||
ovrld_log(LogCategory::STR_RPC, "RPC_STR loading full file {}", msg->basename);
|
||||
// not a stream file - treat it like a normal load
|
||||
auto* filedef = get_file_system()->Find(msg->basename);
|
||||
if (filedef) {
|
||||
msg->maxlen = LoadISOFileToEE(filedef, msg->address, msg->maxlen);
|
||||
if (msg->maxlen) {
|
||||
msg->result = 0;
|
||||
return msg;
|
||||
} else {
|
||||
ovrld_log(LogCategory::WARN, "Failed to LoadISOFileToEE in RPC_STR for {}", msg->basename);
|
||||
}
|
||||
} else {
|
||||
ovrld_log(LogCategory::WARN, "Failed to open {} for RPC STR", msg->basename);
|
||||
}
|
||||
} else {
|
||||
// this is an animation load. Convert name:
|
||||
ISOName animation_iso_name;
|
||||
file_util::ISONameFromAnimationName(animation_iso_name.data, msg->basename);
|
||||
auto* filedef = get_file_system()->FindIN(&animation_iso_name);
|
||||
ovrld_log(LogCategory::STR_RPC, "STR_RPC for {} chunk {}", msg->basename, msg->section);
|
||||
|
||||
if (filedef) {
|
||||
// found it! See if we've cached this animation's header.
|
||||
int cache_entry = 0;
|
||||
int oldest = INT32_MAX;
|
||||
int oldest_idx = -1;
|
||||
while (cache_entry < STR_INDEX_CACHE_SIZE && sCache[cache_entry].filedef != filedef) {
|
||||
sCache[cache_entry].countdown--;
|
||||
if (sCache[cache_entry].countdown < oldest) {
|
||||
oldest_idx = cache_entry;
|
||||
oldest = sCache[cache_entry].countdown;
|
||||
}
|
||||
cache_entry++;
|
||||
}
|
||||
|
||||
if (cache_entry == STR_INDEX_CACHE_SIZE) {
|
||||
// cache miss, we need to load the header to the header cache on the IOP
|
||||
ovrld_log(LogCategory::STR_RPC,
|
||||
"STR_RPC header cache miss - loading .str file header now.");
|
||||
cache_entry = oldest_idx;
|
||||
sCache[oldest_idx].filedef = filedef;
|
||||
sCache[oldest_idx].countdown = INT32_MAX - 1;
|
||||
if (!LoadISOFileToIOP(filedef, (u8*)&sCache[oldest_idx].header, sizeof(StrFileHeader))) {
|
||||
ovrld_log(LogCategory::WARN, "STR_RPC failed to load .str file header for {}",
|
||||
msg->basename);
|
||||
msg->result = 1;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
// load data, using the cached header to find the location of the chunk.
|
||||
if (!LoadISOFileChunkToEE(filedef, msg->address,
|
||||
sCache[cache_entry].header.sizes[msg->section],
|
||||
sCache[cache_entry].header.sectors[msg->section])) {
|
||||
ovrld_log(LogCategory::WARN, "STR_RPC failed to load .str file chunk {} for {}",
|
||||
msg->section, msg->basename);
|
||||
msg->result = 1;
|
||||
} else {
|
||||
// successful load!
|
||||
msg->maxlen = sCache[cache_entry].header.sizes[msg->section];
|
||||
msg->result = 0;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
msg->result = 1;
|
||||
return msg;
|
||||
}
|
||||
|
||||
void* RPC_PLAY(unsigned int, void* msg_in, int size) {
|
||||
static_assert(sizeof(RPC_Play_Cmd) == 256);
|
||||
|
||||
if (size <= 0) {
|
||||
return msg_in;
|
||||
}
|
||||
|
||||
auto* msg_array = (RPC_Play_Cmd*)msg_in;
|
||||
|
||||
for (u32 msg_idx = 0; msg_idx < size / sizeof(RPC_Play_Cmd); msg_idx++) {
|
||||
auto* msg = &msg_array[msg_idx];
|
||||
|
||||
// the operation is stashed in the "result" field of the message
|
||||
switch (msg->result) {
|
||||
case 1: {
|
||||
// remove vag streams by name
|
||||
for (int s = 0; s < 4; s++) {
|
||||
VagStreamData vsd;
|
||||
if (msg->names[s].chars[0] != 0) {
|
||||
// lg::warn("RPC PLAY remove {}", msg->names[s].chars);
|
||||
strncpy(vsd.name, msg->names[s].chars, 0x30);
|
||||
vsd.id = msg->id[s];
|
||||
WaitSema(g_EEStreamsList.sema);
|
||||
RemoveVagStreamFromList(&vsd, &g_EEStreamsList);
|
||||
SignalSema(g_EEStreamsList.sema);
|
||||
WaitSema(g_EEPlayList.sema);
|
||||
RemoveVagStreamFromList(&vsd, &g_EEPlayList);
|
||||
SignalSema(g_EEPlayList.sema);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case 2: {
|
||||
// completely redefine the set of vag streams to queue up.
|
||||
WaitSema(g_EEStreamsList.sema); // lock stream list
|
||||
EmptyVagStreamList(&g_EEStreamsList); // clear all existing streams
|
||||
|
||||
// the first stream has the highest priority.
|
||||
int priority = 9;
|
||||
for (int s = 0; s < 4; s++) {
|
||||
if (msg->names[s].chars[0] && msg->id[s]) {
|
||||
// lg::warn("RPC PLAY queue {}", msg->names[s].chars);
|
||||
|
||||
// set up list entry for this stream
|
||||
VagStreamData vsd;
|
||||
strncpy(vsd.name, msg->names[s].chars, 0x30);
|
||||
vsd.id = msg->id[s];
|
||||
vsd.art_load = msg->address & 1 << (s & 0x1f) & 0xf;
|
||||
vsd.movie_art_load = msg->address & 0x10 << (s & 0x1f) & 0xf0;
|
||||
vsd.sound_handler = 0;
|
||||
vsd.priority = priority;
|
||||
|
||||
// if we have an existing one, make sure it has the appropriate flags
|
||||
auto* existing_vag = FindThisVagStream(vsd.name, vsd.id);
|
||||
if (existing_vag) {
|
||||
existing_vag->art_flag = (u32)(vsd.art_load != 0);
|
||||
existing_vag->music_flag = 0;
|
||||
existing_vag->movie_flag = (u32)(vsd.movie_art_load != 0);
|
||||
if (vsd.art_load != 0) {
|
||||
existing_vag->flags.art = 1;
|
||||
}
|
||||
if (existing_vag->movie_flag != 0) {
|
||||
existing_vag->flags.movie = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// add to list
|
||||
InsertVagStreamInList(&vsd, &g_EEStreamsList);
|
||||
}
|
||||
|
||||
if (priority == 8) {
|
||||
priority = 2;
|
||||
} else {
|
||||
if (0 < priority) {
|
||||
priority = priority + -1;
|
||||
}
|
||||
}
|
||||
s = s + 1;
|
||||
}
|
||||
SignalSema(g_EEStreamsList.sema);
|
||||
} break;
|
||||
case 0: {
|
||||
int priority = 9;
|
||||
for (int s = 0; s < 4; s++) {
|
||||
if (msg->names[s].chars[0] && msg->id[s]) {
|
||||
// lg::warn("RPC PLAY play {}", msg->names[s].chars);
|
||||
|
||||
VagStreamData vsd;
|
||||
strncpy(vsd.name, msg->names[s].chars, 0x30);
|
||||
vsd.id = msg->id[s];
|
||||
vsd.volume2 = msg->section;
|
||||
vsd.group = msg->maxlen;
|
||||
vsd.plugin_id = 0;
|
||||
vsd.sound_handler = 0;
|
||||
vsd.maybe_volume_3 = 0;
|
||||
vsd.priority = priority;
|
||||
auto* existing_vag = FindThisVagStream(msg->names[s].chars, vsd.id);
|
||||
if (existing_vag != (ISO_VAGCommand*)0x0) {
|
||||
existing_vag->play_volume = vsd.volume2;
|
||||
existing_vag->play_group = vsd.group;
|
||||
if (existing_vag->flags.running != 0)
|
||||
goto LAB_000092a4;
|
||||
}
|
||||
WaitSema(g_EEPlayList.sema);
|
||||
auto* already_playing = FindVagStreamInList(&vsd, &g_EEPlayList);
|
||||
if (!already_playing) {
|
||||
already_playing = InsertVagStreamInList(&vsd, &g_EEPlayList);
|
||||
strncpy(already_playing->name, vsd.name, 0x30);
|
||||
already_playing->id = vsd.id;
|
||||
already_playing->priority = vsd.priority;
|
||||
already_playing->sound_handler = vsd.sound_handler;
|
||||
already_playing->plugin_id = vsd.plugin_id;
|
||||
already_playing->unk1 = 0;
|
||||
already_playing->art_load = 0;
|
||||
already_playing->movie_art_load = 0;
|
||||
}
|
||||
SignalSema(g_EEPlayList.sema);
|
||||
} else {
|
||||
// lg::warn("RPC PLAY play (NONE)");
|
||||
}
|
||||
LAB_000092a4:
|
||||
if (priority == 8) {
|
||||
priority = 2;
|
||||
} else {
|
||||
if (0 < priority) {
|
||||
priority = priority + -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return msg_in;
|
||||
}
|
||||
} // namespace jak3
|
13
game/overlord/jak3/stream.h
Normal file
13
game/overlord/jak3/stream.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_stream();
|
||||
|
||||
u32 PLAYThread();
|
||||
u32 STRThread();
|
||||
void* RPC_STR(unsigned int fno, void* msg, int size);
|
||||
void* RPC_PLAY(unsigned int fno, void* msg, int size);
|
||||
|
||||
} // namespace jak3
|
339
game/overlord/jak3/streamlist.cpp
Normal file
339
game/overlord/jak3/streamlist.cpp
Normal file
@ -0,0 +1,339 @@
|
||||
#include "streamlist.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
using namespace iop;
|
||||
List g_RequestedStreamsList;
|
||||
List g_NewStreamsList;
|
||||
List g_EEStreamsList;
|
||||
List g_EEPlayList;
|
||||
void jak3_overlord_init_globals_streamlist() {
|
||||
g_RequestedStreamsList = {};
|
||||
g_NewStreamsList = {};
|
||||
g_EEStreamsList = {};
|
||||
g_EEPlayList = {};
|
||||
}
|
||||
|
||||
void InitVagStreamList(List* list, int size, const char* name) {
|
||||
strncpy(list->name, name, 8);
|
||||
|
||||
InitList(list, size, sizeof(VagStreamData));
|
||||
|
||||
auto* iter = list->next;
|
||||
if (0 < size) {
|
||||
do {
|
||||
iter->in_use = 0;
|
||||
strncpy(iter->name, "free", 0x30);
|
||||
iter->group = 2;
|
||||
iter->id = 0;
|
||||
iter->sound_handler = 0;
|
||||
iter->priority = 0;
|
||||
iter->art_load = 0;
|
||||
iter->movie_art_load = 0;
|
||||
iter->unk2 = 0;
|
||||
iter->unk1 = 0;
|
||||
iter->volume2 = 0;
|
||||
iter->maybe_volume_3 = 0;
|
||||
iter = iter + 1;
|
||||
size = size + -1;
|
||||
} while (size != 0);
|
||||
}
|
||||
|
||||
ASSERT(list->buffer);
|
||||
}
|
||||
|
||||
VagStreamData* FindVagStreamInList(VagStreamData* stream, List* list) {
|
||||
int iVar1;
|
||||
VagStreamData* iter;
|
||||
u32 max_idx;
|
||||
u32 idx;
|
||||
u32 uVar2;
|
||||
VagStreamData* ret;
|
||||
VagStreamData* pVVar3;
|
||||
|
||||
max_idx = list->count;
|
||||
iter = list->next;
|
||||
ret = nullptr;
|
||||
idx = 0;
|
||||
if (max_idx != 0) {
|
||||
do {
|
||||
uVar2 = idx;
|
||||
pVVar3 = ret;
|
||||
if ((iter->id != stream->id) || (iVar1 = strncmp(iter->name, stream->name, 0x30),
|
||||
uVar2 = max_idx, pVVar3 = iter, iVar1 == 0)) {
|
||||
ret = pVVar3;
|
||||
idx = uVar2;
|
||||
}
|
||||
idx = idx + 1;
|
||||
iter = iter->next;
|
||||
} while (idx < max_idx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
VagStreamData* GetVagStreamInList(u32 idx, List* list) {
|
||||
VagStreamData* iter = nullptr;
|
||||
if ((idx < (u32)list->count) && (iter = list->next, idx != 0)) {
|
||||
do {
|
||||
idx = idx - 1;
|
||||
iter = iter->next;
|
||||
} while (idx != 0);
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
|
||||
void EmptyVagStreamList(List* list) {
|
||||
VagStreamData* elt;
|
||||
u32 i;
|
||||
u32 cnt;
|
||||
|
||||
cnt = list->count;
|
||||
elt = (VagStreamData*)list->buffer;
|
||||
i = 0;
|
||||
if (cnt != 0) {
|
||||
do {
|
||||
i = i + 1;
|
||||
strncpy(elt->name, "free", 0x30);
|
||||
elt->group = 2;
|
||||
elt->id = 0;
|
||||
elt->sound_handler = 0;
|
||||
elt->priority = 0;
|
||||
elt->art_load = 0;
|
||||
elt->movie_art_load = 0;
|
||||
elt->unk2 = 0;
|
||||
elt->unk1 = 0;
|
||||
elt->volume2 = 0;
|
||||
elt->maybe_volume_3 = 0;
|
||||
elt->in_use = 0;
|
||||
elt = elt + 1;
|
||||
} while (i < cnt);
|
||||
}
|
||||
list->unk_flag = 1;
|
||||
}
|
||||
|
||||
void RemoveVagStreamFromList(VagStreamData* stream, List* list) {
|
||||
VagStreamData* elt = FindVagStreamInList(stream, list);
|
||||
if (elt) {
|
||||
elt->in_use = 0;
|
||||
strncpy(elt->name, "free", 0x30);
|
||||
elt->group = 2;
|
||||
elt->id = 0;
|
||||
elt->priority = 0;
|
||||
elt->art_load = 0;
|
||||
elt->movie_art_load = 0;
|
||||
elt->unk1 = 0;
|
||||
elt->volume2 = 0;
|
||||
elt->maybe_volume_3 = 0;
|
||||
elt->sound_handler = 0;
|
||||
list->unk_flag = 1;
|
||||
elt->unk2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
VagStreamData* InsertVagStreamInList(VagStreamData* user_stream, List* list) {
|
||||
u32 uVar1;
|
||||
VagStreamData* pVVar10;
|
||||
VagStreamData* pVVar11;
|
||||
VagStreamData* pVVar12;
|
||||
|
||||
u32 count = list->count;
|
||||
VagStreamData* free_elt = nullptr;
|
||||
u32 free_elt_idx = 0;
|
||||
VagStreamData* iter = list->next;
|
||||
if (count != 0) {
|
||||
do {
|
||||
if (iter->id == 0) {
|
||||
free_elt = iter;
|
||||
free_elt_idx = count;
|
||||
}
|
||||
free_elt_idx = free_elt_idx + 1;
|
||||
iter = iter->next;
|
||||
} while (free_elt_idx < count);
|
||||
}
|
||||
if ((free_elt != (VagStreamData*)0x0) &&
|
||||
(uVar1 = 0, pVVar11 = list->next, pVVar12 = nullptr, count != 0)) {
|
||||
do {
|
||||
pVVar10 = pVVar11;
|
||||
uVar1 = uVar1 + 1;
|
||||
if (pVVar10->priority < user_stream->priority) {
|
||||
list->unk_flag = 1;
|
||||
free_elt->in_use = 1;
|
||||
strncpy(free_elt->name, user_stream->name, 0x30);
|
||||
free_elt->id = user_stream->id;
|
||||
free_elt->plugin_id = user_stream->plugin_id;
|
||||
free_elt->art_load = user_stream->art_load;
|
||||
free_elt->movie_art_load = user_stream->movie_art_load;
|
||||
free_elt->priority = user_stream->priority;
|
||||
free_elt->sound_handler = user_stream->sound_handler;
|
||||
free_elt->volume2 = user_stream->volume2;
|
||||
free_elt->maybe_volume_3 = user_stream->maybe_volume_3;
|
||||
free_elt->group = user_stream->group;
|
||||
free_elt->unk1 = 0;
|
||||
if (pVVar12 == (VagStreamData*)0x0) {
|
||||
if (free_elt == pVVar10) {
|
||||
return free_elt;
|
||||
}
|
||||
auto* prev = free_elt->next;
|
||||
auto* next = free_elt->prev;
|
||||
list->next = free_elt;
|
||||
prev->prev = next;
|
||||
pVVar11 = free_elt->prev;
|
||||
pVVar10->prev = free_elt;
|
||||
pVVar11->next = prev;
|
||||
free_elt->prev = nullptr;
|
||||
free_elt->next = pVVar10;
|
||||
return free_elt;
|
||||
}
|
||||
if (free_elt == pVVar10) {
|
||||
return free_elt;
|
||||
}
|
||||
auto* pVVar13 = free_elt->prev;
|
||||
pVVar13->next = free_elt->next;
|
||||
pVVar11 = pVVar12->next;
|
||||
free_elt->next->prev = pVVar13;
|
||||
free_elt->next = pVVar11;
|
||||
pVVar10->prev = free_elt;
|
||||
pVVar12->next = free_elt;
|
||||
free_elt->prev = pVVar12;
|
||||
return free_elt;
|
||||
}
|
||||
pVVar11 = pVVar10->next;
|
||||
pVVar12 = pVVar10;
|
||||
} while (uVar1 < count);
|
||||
}
|
||||
return free_elt;
|
||||
}
|
||||
|
||||
void MergeVagStreamLists(List* list_a, List* list_b) {
|
||||
VagStreamData* stream;
|
||||
VagStreamData* pVVar1;
|
||||
u32 uVar2;
|
||||
u32 idx;
|
||||
|
||||
idx = 0;
|
||||
uVar2 = 0;
|
||||
LAB_0000fde8:
|
||||
do {
|
||||
stream = GetVagStreamInList(idx, list_a);
|
||||
idx = idx + 1;
|
||||
if (stream != (VagStreamData*)0x0) {
|
||||
if (stream->id == 0)
|
||||
goto LAB_0000fde8;
|
||||
pVVar1 = FindVagStreamInList(stream, list_b);
|
||||
if (pVVar1 == (VagStreamData*)0x0) {
|
||||
InsertVagStreamInList(stream, list_b);
|
||||
}
|
||||
}
|
||||
uVar2 = uVar2 + 1;
|
||||
if (3 < uVar2) {
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void QueueNewStreamsFromList(List* list) {
|
||||
VagStreamData* stream;
|
||||
ISO_VAGCommand* pIVar1;
|
||||
u32 uVar2;
|
||||
u32 idx;
|
||||
|
||||
SetVagStreamsNotScanned();
|
||||
idx = 0;
|
||||
EmptyVagStreamList(&g_NewStreamsList);
|
||||
g_NewStreamsList.unk_flag = 0;
|
||||
uVar2 = 0;
|
||||
LAB_0000fe94:
|
||||
do {
|
||||
stream = GetVagStreamInList(idx, list);
|
||||
idx = idx + 1;
|
||||
if (stream == (VagStreamData*)0x0) {
|
||||
uVar2 = 4;
|
||||
} else {
|
||||
if (stream->id == 0)
|
||||
goto LAB_0000fe94;
|
||||
pIVar1 = FindThisVagStream(stream->name, stream->id);
|
||||
if (pIVar1 == (ISO_VAGCommand*)0x0) {
|
||||
pIVar1 = FindThisVagStream(stream->name, stream->id);
|
||||
if (pIVar1 == (ISO_VAGCommand*)0x0) {
|
||||
InsertVagStreamInList(stream, &g_NewStreamsList);
|
||||
}
|
||||
} else {
|
||||
pIVar1->flags.scanned = 1;
|
||||
if (pIVar1->stereo_sibling != (ISO_VAGCommand*)0x0) {
|
||||
pIVar1->stereo_sibling->flags.scanned = 1;
|
||||
}
|
||||
if (stream->priority != pIVar1->priority_pq) {
|
||||
SetNewVagCmdPri(pIVar1, stream->priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
uVar2 = uVar2 + 1;
|
||||
if (3 < uVar2) {
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void CheckPlayList(List* list) {
|
||||
int count;
|
||||
ISO_VAGCommand* cmd;
|
||||
VagStreamData* iter;
|
||||
|
||||
count = list->count;
|
||||
iter = list->next;
|
||||
joined_r0x0000ff80:
|
||||
do {
|
||||
while (true) {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
count = count + -1;
|
||||
if (iter->id != 0)
|
||||
break;
|
||||
iter = iter->next;
|
||||
}
|
||||
cmd = FindThisVagStream(iter->name, iter->id);
|
||||
} while (cmd == (ISO_VAGCommand*)0x0);
|
||||
if (cmd->flags.running == 0)
|
||||
goto code_r0x0000ffc4;
|
||||
goto LAB_00010004;
|
||||
code_r0x0000ffc4:
|
||||
if (((cmd->flags.saw_chunks1 != 0) || (cmd->flags.file_disappeared != 0)) &&
|
||||
(cmd->flags.nostart == 0)) {
|
||||
IsoPlayVagStream(cmd);
|
||||
LAB_00010004:
|
||||
RemoveVagStreamFromList(iter, list);
|
||||
}
|
||||
goto joined_r0x0000ff80;
|
||||
}
|
||||
|
||||
void StreamListThread() {
|
||||
if (g_RequestedStreamsList.pending_data == 0) {
|
||||
WaitSema(g_RequestedStreamsList.sema);
|
||||
EmptyVagStreamList(&g_RequestedStreamsList);
|
||||
g_RequestedStreamsList.unk_flag = 0;
|
||||
// WaitSema(DAT_00015dd0);
|
||||
// MergeVagStreamLists((List*)&g_PluginStreamsList, &g_RequestedStreamsList);
|
||||
// SignalSema(DAT_00015dd0);
|
||||
WaitSema(g_EEStreamsList.sema);
|
||||
MergeVagStreamLists(&g_EEStreamsList, &g_RequestedStreamsList);
|
||||
SignalSema(g_EEStreamsList.sema);
|
||||
g_RequestedStreamsList.pending_data = 1;
|
||||
SignalSema(g_RequestedStreamsList.sema);
|
||||
WaitSema(g_EEPlayList.sema);
|
||||
CheckPlayList(&g_EEPlayList);
|
||||
SignalSema(g_EEPlayList.sema);
|
||||
// WaitSema(DAT_0001e31c);
|
||||
// CheckLfoList(&g_LfoStreamsList);
|
||||
// SignalSema(DAT_0001e31c);
|
||||
}
|
||||
}
|
||||
} // namespace jak3
|
22
game/overlord/jak3/streamlist.h
Normal file
22
game/overlord/jak3/streamlist.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/overlord/jak3/list.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_streamlist();
|
||||
|
||||
struct ISO_VAGCommand;
|
||||
|
||||
extern List g_RequestedStreamsList;
|
||||
extern List g_NewStreamsList;
|
||||
extern List g_EEStreamsList;
|
||||
extern List g_EEPlayList;
|
||||
|
||||
void QueueNewStreamsFromList(List* list);
|
||||
void RemoveVagStreamFromList(VagStreamData* entry, List* list);
|
||||
void EmptyVagStreamList(List* list);
|
||||
VagStreamData* InsertVagStreamInList(VagStreamData* entry, List* list);
|
||||
VagStreamData* FindVagStreamInList(VagStreamData* entry, List* list);
|
||||
void InitVagStreamList(List* list, int size, const char* name);
|
||||
void StreamListThread();
|
||||
} // namespace jak3
|
6
game/overlord/jak3/todo.txt
Normal file
6
game/overlord/jak3/todo.txt
Normal file
@ -0,0 +1,6 @@
|
||||
- pan stuff is wrong - it's now using a table from 989snd
|
||||
- Check ssound.cpp for most of these issues, and bottom of vag.cpp
|
||||
- in some cases, we're hitting different playback modes? see comment dolby crap, we hit this when going outside freedom hq
|
||||
- changing file size
|
||||
- UpdateVolume
|
||||
- goal src update
|
1042
game/overlord/jak3/vag.cpp
Normal file
1042
game/overlord/jak3/vag.cpp
Normal file
File diff suppressed because it is too large
Load Diff
173
game/overlord/jak3/vag.h
Normal file
173
game/overlord/jak3/vag.h
Normal file
@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/overlord/jak3/isocommon.h"
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_vag();
|
||||
|
||||
extern bool g_bExtPause;
|
||||
extern bool g_bExtResume;
|
||||
|
||||
struct ISO_VAGCommand : ISO_Hdr {
|
||||
ISOFileDef* vag_file_def = nullptr; // 44 (actually INT file def?)
|
||||
VagDirEntry* vag_dir_entry = nullptr; // 48
|
||||
ISO_VAGCommand* stereo_sibling = nullptr; // 52
|
||||
|
||||
// pointer to IOP memory to DMA to SPU. Points to the data for the next new transfer.
|
||||
const u8* dma_iop_mem_ptr = nullptr; // 56
|
||||
|
||||
// the DMA channel to upload to for sceCdVoiceTrans
|
||||
int dma_chan = 0; // 60
|
||||
|
||||
// if not set, a pending dma interrupt will want to modify this command.
|
||||
int safe_to_modify_dma = 0; // 64
|
||||
|
||||
u32 current_spu_address = 0; // 68
|
||||
|
||||
char name[48]; // 72
|
||||
char overflow[16];
|
||||
|
||||
// SPU address of the next chunk to fill
|
||||
// for stereo mode, there's a 0x2000 offset between the left and right audio. This stream_sram
|
||||
// doesn't include that offset.
|
||||
u32 stream_sram; // 124
|
||||
u32 trap_sram; // 138
|
||||
|
||||
// spu voice for playback
|
||||
int voice; // 132
|
||||
int info_idx; // 136
|
||||
int maybe_sound_handler = 0; // 140
|
||||
void* lfo_callback = nullptr; // 144
|
||||
|
||||
int oog = 0; // 180
|
||||
int dolby_pan_angle = 0; // 184
|
||||
int clocka = 0; // 188
|
||||
int clockb = 0; // 192
|
||||
int clockc = 0; // 196
|
||||
int clockd = 0; // 200
|
||||
int unk_gvsp_len; // 204
|
||||
int position_for_ee; // 208
|
||||
int unk_gvsp_cntr; // 212
|
||||
|
||||
struct {
|
||||
u8 bit0 = 0; // 216
|
||||
u8 saw_chunks1 = 0; // 217
|
||||
// will start the voice, but with a pitch of 0.
|
||||
u8 paused = 0; // 218
|
||||
u8 bit3 = 0; // 219
|
||||
u8 running = 0; // 220
|
||||
u8 clocks_set = 0; // 221, set by SetVagClock if it succeeds.
|
||||
u8 file_disappeared = 0; // 222, set if SetVagClock notices that bBaseFile is gone!
|
||||
u8 scanned = 0; // 223, set shortly after internal command is created.
|
||||
u8 bit8 = 0;
|
||||
u8 stop = 0; // 225, set if this is a non-plugin stream, and was stopped by StopVagStream.
|
||||
u8 art = 0; // 226, set if this has art_flag set
|
||||
// set if we are the non-main stereo command.
|
||||
u8 stereo_secondary = 0; // 227
|
||||
u8 bit12 = 0; // 228
|
||||
u8 bit13 = 0; // 229
|
||||
u8 bit14 = 0; // 230
|
||||
u8 bit15 = 0; // 231
|
||||
u8 bit16 = 0; // 232
|
||||
u8 bit17 = 0; // 233
|
||||
// set if SPU DMA has completed for an even or odd number of chunks of non-stereo audio.
|
||||
u8 dma_complete_even_chunk_count = 0; // 234
|
||||
u8 dma_complete_odd_chunk_count = 0; // 235
|
||||
u8 bit20 = 0;
|
||||
u8 bit21 = 0;
|
||||
u8 bit22 = 0;
|
||||
u8 nostart = 0;
|
||||
u8 movie = 0;
|
||||
u8 bit25 = 0;
|
||||
} flags;
|
||||
|
||||
u32 pack_flags();
|
||||
|
||||
void set_all_flags_zero();
|
||||
int unk_gvsp_state2 = 0; // 244
|
||||
int num_isobuffered_chunks = 0; // 248
|
||||
|
||||
// if we need to do a second SPU DMA for stereo's second channel, the size of that transfer
|
||||
int xfer_size; // 252
|
||||
int vag_file_rate; // 256
|
||||
|
||||
int pitch1; // 260 pitch to use for playback, possibly overwritten
|
||||
int pitch1_file; // 264 pitch to use for playback, from the file itself (sample rate)
|
||||
int pitch_cmd; // 268 pitch mod command (?)
|
||||
int error; // 272
|
||||
int unk_spu_mem_offset; // 276
|
||||
int unk_gvsp_flag; // 280
|
||||
int play_volume = 0; // 284
|
||||
int id = 0; // 288
|
||||
int plugin_id = 0; // 292
|
||||
int priority_pq = 0; // 296
|
||||
int art_flag = 0; // 300
|
||||
int music_flag = 0; // 304
|
||||
int updated_trans = 0; // 308
|
||||
int trans[3]; // 312
|
||||
int fo_min; // 324
|
||||
int fo_max; // 328
|
||||
int fo_curve; // 332
|
||||
int play_group = 0; // 336
|
||||
int movie_flag = 0; // 340
|
||||
};
|
||||
|
||||
struct VagStreamData {
|
||||
VagStreamData* next = nullptr;
|
||||
VagStreamData* prev = nullptr;
|
||||
int in_use;
|
||||
char name[0x30];
|
||||
int id;
|
||||
int plugin_id;
|
||||
int sound_handler;
|
||||
int art_load;
|
||||
int movie_art_load;
|
||||
int priority;
|
||||
int unk2;
|
||||
int unk1;
|
||||
int volume2;
|
||||
int maybe_volume_3;
|
||||
int group;
|
||||
};
|
||||
|
||||
extern ISO_VAGCommand g_aVagCmds[6];
|
||||
extern int g_anMasterVolume[32];
|
||||
extern bool voice_key_flags[0x30];
|
||||
extern u32 voice_key_times[0x30];
|
||||
extern u32 g_nTimeOfLastVoiceKey;
|
||||
extern bool g_bRecentlyKeyedVoice;
|
||||
|
||||
int CalculateVAGPitch(int a, int b);
|
||||
void BlockUntilVoiceSafe(int, u32);
|
||||
void BlockUntilAllVoicesSafe();
|
||||
void CheckVagStreamsProgress();
|
||||
void MarkVoiceKeyedOnOff(int voice, u32 systime);
|
||||
void UnPauseVAG(ISO_VAGCommand* cmd);
|
||||
ISO_VAGCommand* FindMusicStreamName(const char* name);
|
||||
ISO_VAGCommand* SmartAllocMusicVagCommand(const ISO_VAGCommand* user_command, int flag);
|
||||
void InitVAGCmd(ISO_VAGCommand* cmd, int paused);
|
||||
int HowManyBelowThisPriority(int pri);
|
||||
ISO_VAGCommand* FindThisVagStream(const char* name, int id);
|
||||
ISO_VAGCommand* SmartAllocVagCmd(ISO_VAGCommand* user_command);
|
||||
void RemoveVagCmd(ISO_VAGCommand* cmd);
|
||||
void SetNewVagCmdPri(ISO_VAGCommand* cmd, int pri);
|
||||
void TerminateVAG(ISO_VAGCommand* cmd);
|
||||
ISO_VAGCommand* FindThisMusicStream(const char* name, int id);
|
||||
ISO_VAGCommand* FindVagStreamName(const char* name);
|
||||
int AnyVagRunning();
|
||||
void PauseVAG(ISO_VAGCommand* cmd);
|
||||
void InitVagCmds();
|
||||
void PauseVagStreams(bool music);
|
||||
void UnPauseVagStreams(bool music);
|
||||
void SetVagStreamsNoStart(int value);
|
||||
ISO_VAGCommand* FindVagStreamId(int);
|
||||
void SetVAGVol(ISO_VAGCommand* cmd);
|
||||
void SetAllVagsVol(int);
|
||||
void PauseVAGStreams();
|
||||
ISO_VAGCommand* FindNotQueuedVagCmd();
|
||||
void CalculateVAGVolumes(ISO_VAGCommand* cmd, int* l, int* r);
|
||||
void SetVagStreamsNotScanned();
|
||||
void VAG_MarkLoopEnd(uint8_t* data, int offset);
|
||||
void VAG_MarkLoopStart(uint8_t* data);
|
||||
void RestartVag(ISO_VAGCommand* cmd, int p);
|
||||
extern u32 g_nPlaybackMode;
|
||||
} // namespace jak3
|
201
game/overlord/jak3/vblank_handler.cpp
Normal file
201
game/overlord/jak3/vblank_handler.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
#include "vblank_handler.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/overlord/jak3/dma.h"
|
||||
#include "game/overlord/jak3/iso.h"
|
||||
#include "game/overlord/jak3/sbank.h"
|
||||
#include "game/overlord/jak3/srpc.h"
|
||||
#include "game/overlord/jak3/ssound.h"
|
||||
#include "game/overlord/jak3/streamlist.h"
|
||||
#include "game/overlord/jak3/vag.h"
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
namespace jak3 {
|
||||
using namespace iop;
|
||||
u32 g_nInfoEE = 0;
|
||||
SoundIOPInfo g_SRPCSoundIOPInfo;
|
||||
bool g_bVBlankInitialized = false;
|
||||
s32 g_nVBlankThreadID = -1;
|
||||
s32 g_nVBlankSemaphoreID = -1;
|
||||
bool g_bVBlankRegistered = false;
|
||||
u32 g_nIopTicks = 0;
|
||||
u32 g_nFrameNum = 0;
|
||||
void jak3_overlord_init_globals_vblank_handler() {
|
||||
g_nInfoEE = 0;
|
||||
g_SRPCSoundIOPInfo = {};
|
||||
g_bVBlankInitialized = false;
|
||||
g_nVBlankThreadID = -1;
|
||||
g_nVBlankSemaphoreID = -1;
|
||||
g_bVBlankRegistered = false;
|
||||
g_nIopTicks = 0;
|
||||
g_nFrameNum = 0;
|
||||
}
|
||||
|
||||
int VBlankHandler(void*);
|
||||
u32 VBlankThread();
|
||||
|
||||
void VBlank_Initialize() {
|
||||
ThreadParam thread_param;
|
||||
SemaParam sema_param;
|
||||
|
||||
if (g_bVBlankInitialized == 0) {
|
||||
thread_param.attr = 0x2000000;
|
||||
thread_param.stackSize = 0x800;
|
||||
thread_param.initPriority = 0x34;
|
||||
thread_param.option = 0;
|
||||
thread_param.entry = VBlankThread;
|
||||
strcpy(thread_param.name, "vblank");
|
||||
g_nVBlankThreadID = CreateThread(&thread_param);
|
||||
ASSERT(g_nVBlankThreadID >= 0);
|
||||
sema_param.max_count = 200; // hack
|
||||
sema_param.attr = 0;
|
||||
sema_param.init_count = 0;
|
||||
sema_param.option = 0;
|
||||
g_nVBlankSemaphoreID = CreateSema(&sema_param);
|
||||
ASSERT(g_nVBlankSemaphoreID >= 0);
|
||||
int ret = StartThread(g_nVBlankThreadID, 0);
|
||||
ASSERT(ret == 0);
|
||||
RegisterVblankHandler(0, 0x40, VBlankHandler, 0);
|
||||
g_bVBlankInitialized = true;
|
||||
g_bVBlankRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
int VBlankHandler(void*) {
|
||||
if ((g_bVBlankInitialized != 0) && (-1 < g_nVBlankSemaphoreID)) {
|
||||
SignalSema(g_nVBlankSemaphoreID); // was iSignalSema
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 VBlankThread() {
|
||||
// char *pcVar1;
|
||||
// int iVar2;
|
||||
// uint uVar3;
|
||||
// SoundBankInfo *pSVar4;
|
||||
// ISO_VAGCommand *cmd;
|
||||
// SoundBankInfo **ppSVar5;
|
||||
// uint *puVar6;
|
||||
// uint uVar7;
|
||||
// int iVar8;
|
||||
// int iVar9;
|
||||
// SoundIOPInfo *local_30;
|
||||
// void *local_2c;
|
||||
// undefined4 local_28;
|
||||
// undefined4 local_24;
|
||||
// undefined4 local_20 [2];
|
||||
|
||||
do {
|
||||
while ((g_bVBlankInitialized == 0 || (g_nVBlankSemaphoreID < 0))) {
|
||||
DelayThread(1000000);
|
||||
}
|
||||
WaitSema(g_nVBlankSemaphoreID);
|
||||
g_nIopTicks = g_nIopTicks + 1;
|
||||
if (g_bSoundEnable != 0) {
|
||||
CheckVagStreamsProgress();
|
||||
if ((g_nIopTicks & 1U) != 0) {
|
||||
StreamListThread();
|
||||
}
|
||||
if (g_nMusicFadeDir < 0) {
|
||||
g_nMusicFade = g_nMusicFade + -0x200;
|
||||
if (g_nMusicFade < 0) {
|
||||
g_nMusicFade = 0;
|
||||
LAB_00011f60:
|
||||
g_nMusicFadeDir = 0;
|
||||
}
|
||||
} else {
|
||||
if ((0 < g_nMusicFadeDir) &&
|
||||
(g_nMusicFade = g_nMusicFade + 0x400, 0x10000 < g_nMusicFade)) {
|
||||
g_nMusicFade = 0x10000;
|
||||
goto LAB_00011f60;
|
||||
}
|
||||
}
|
||||
if (g_nInfoEE) {
|
||||
g_nFrameNum = g_nFrameNum + 1;
|
||||
// puVar6 = g_SRPCSoundIOPInfo.stream_status;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto* cmd = &g_aVagCmds[i];
|
||||
u32 stream_status = cmd->pack_flags();
|
||||
|
||||
if ((cmd->flags.file_disappeared != 0) && (cmd->flags.paused == 0)) {
|
||||
auto uVar3 = CalculateVAGPitch(0x400, cmd->pitch_cmd);
|
||||
if (g_nFPS == 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
cmd->clockd = cmd->clockd + uVar3 / g_nFPS;
|
||||
}
|
||||
|
||||
if ((cmd->flags.saw_chunks1 == 0) && (cmd->flags.clocks_set != 0)) {
|
||||
g_SRPCSoundIOPInfo.stream_status[i] = stream_status;
|
||||
g_SRPCSoundIOPInfo.stream_id[i] = cmd->id;
|
||||
g_SRPCSoundIOPInfo.stream_position[i] = 0;
|
||||
} else {
|
||||
g_SRPCSoundIOPInfo.stream_status[i] = stream_status;
|
||||
g_SRPCSoundIOPInfo.stream_id[i] = cmd->id;
|
||||
g_SRPCSoundIOPInfo.stream_position[i] = cmd->position_for_ee;
|
||||
}
|
||||
}
|
||||
// CpuSuspendIntr(local_20);
|
||||
|
||||
// CpuResumeIntr(local_20[0]);
|
||||
g_SRPCSoundIOPInfo.iop_ticks = g_nIopTicks;
|
||||
g_SRPCSoundIOPInfo.freemem = 12345; // hack
|
||||
g_SRPCSoundIOPInfo.frame = g_nFrameNum;
|
||||
g_SRPCSoundIOPInfo.freemem2 = QueryTotalFreeMemSize();
|
||||
g_SRPCSoundIOPInfo.nocd = 0; // hack
|
||||
g_SRPCSoundIOPInfo.dirtycd = 0; // hack
|
||||
g_SRPCSoundIOPInfo.dupseg = -1;
|
||||
g_SRPCSoundIOPInfo.diskspeed[0] = 0;
|
||||
g_SRPCSoundIOPInfo.diskspeed[1] = 0;
|
||||
g_SRPCSoundIOPInfo.lastspeed = 0;
|
||||
|
||||
memset(&g_SRPCSoundIOPInfo.sound_bank0[0], 0, 8 * 16);
|
||||
|
||||
if (gBanks[0]->in_use && gBanks[0]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank0, gBanks[0]->m_name1);
|
||||
}
|
||||
if (gBanks[1]->in_use && gBanks[1]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank1, gBanks[1]->m_name1);
|
||||
}
|
||||
if (gBanks[2]->in_use && gBanks[2]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank2, gBanks[2]->m_name1);
|
||||
}
|
||||
if (gBanks[3]->in_use && gBanks[3]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank3, gBanks[3]->m_name1);
|
||||
}
|
||||
if (gBanks[4]->in_use && gBanks[4]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank4, gBanks[4]->m_name1);
|
||||
}
|
||||
if (gBanks[5]->in_use && gBanks[5]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank5, gBanks[5]->m_name1);
|
||||
}
|
||||
if (gBanks[6]->in_use && gBanks[6]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank6, gBanks[6]->m_name1);
|
||||
}
|
||||
if (gBanks[7]->in_use && gBanks[7]->loaded) {
|
||||
strcpy(g_SRPCSoundIOPInfo.sound_bank7, gBanks[7]->m_name1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 48; i++) {
|
||||
g_SRPCSoundIOPInfo.chinfo[i] = (snd_GetVoiceStatus(i) != 1) - 1;
|
||||
}
|
||||
|
||||
sceSifDmaData dma;
|
||||
dma.data = &g_SRPCSoundIOPInfo;
|
||||
dma.addr = (void*)(u64)g_nInfoEE;
|
||||
dma.size = sizeof(g_SRPCSoundIOPInfo);
|
||||
static_assert(sizeof(g_SRPCSoundIOPInfo) == 0x2d0);
|
||||
dma.mode = 0;
|
||||
/*dmaid =*/sceSifSetDma(&dma, 1);
|
||||
}
|
||||
}
|
||||
RunDeferredVoiceTrans();
|
||||
// Poll(&g_DvdDriver);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
} // namespace jak3
|
12
game/overlord/jak3/vblank_handler.h
Normal file
12
game/overlord/jak3/vblank_handler.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/overlord/jak3/rpc_interface.h"
|
||||
|
||||
namespace jak3 {
|
||||
void jak3_overlord_init_globals_vblank_handler();
|
||||
void VBlank_Initialize();
|
||||
extern u32 g_nInfoEE;
|
||||
extern SoundIOPInfo g_SRPCSoundIOPInfo;
|
||||
} // namespace jak3
|
@ -78,6 +78,8 @@
|
||||
#include "game/overlord/jak2/stream.h"
|
||||
#include "game/overlord/jak2/streamlist.h"
|
||||
#include "game/overlord/jak2/vag.h"
|
||||
#include "game/overlord/jak3/init.h"
|
||||
#include "game/overlord/jak3/overlord.h"
|
||||
#include "game/system/Deci2Server.h"
|
||||
#include "game/system/iop_thread.h"
|
||||
#include "sce/deci2.h"
|
||||
@ -269,33 +271,36 @@ void iop_runner(SystemThreadInterface& iface, GameVersion version) {
|
||||
iop::LIBRARY_register(&iop);
|
||||
Gfx::register_vsync_callback([&iop]() { iop.kernel.signal_vblank(); });
|
||||
|
||||
jak1::dma_init_globals();
|
||||
jak2::dma_init_globals();
|
||||
if (version != GameVersion::Jak3) {
|
||||
jak1::dma_init_globals();
|
||||
jak2::dma_init_globals();
|
||||
|
||||
iso_init_globals();
|
||||
jak1::iso_init_globals();
|
||||
jak2::iso_init_globals();
|
||||
iso_init_globals();
|
||||
jak1::iso_init_globals();
|
||||
jak2::iso_init_globals();
|
||||
|
||||
fake_iso_init_globals();
|
||||
jak1::fake_iso_init_globals();
|
||||
jak2::iso_cd_init_globals();
|
||||
fake_iso_init_globals();
|
||||
jak1::fake_iso_init_globals();
|
||||
jak2::iso_cd_init_globals();
|
||||
|
||||
jak1::iso_queue_init_globals();
|
||||
jak2::iso_queue_init_globals();
|
||||
jak1::iso_queue_init_globals();
|
||||
jak2::iso_queue_init_globals();
|
||||
|
||||
jak2::spusstreams_init_globals();
|
||||
jak1::ramdisk_init_globals();
|
||||
sbank_init_globals();
|
||||
jak2::spusstreams_init_globals();
|
||||
jak1::ramdisk_init_globals();
|
||||
sbank_init_globals();
|
||||
|
||||
// soundcommon
|
||||
jak1::srpc_init_globals();
|
||||
jak2::srpc_init_globals();
|
||||
srpc_init_globals();
|
||||
ssound_init_globals();
|
||||
jak2::ssound_init_globals();
|
||||
// soundcommon
|
||||
jak1::srpc_init_globals();
|
||||
jak2::srpc_init_globals();
|
||||
srpc_init_globals();
|
||||
ssound_init_globals();
|
||||
jak2::ssound_init_globals();
|
||||
|
||||
jak1::stream_init_globals();
|
||||
jak2::stream_init_globals();
|
||||
}
|
||||
|
||||
jak1::stream_init_globals();
|
||||
jak2::stream_init_globals();
|
||||
prof().end_event();
|
||||
iface.initialization_complete();
|
||||
|
||||
@ -323,9 +328,11 @@ void iop_runner(SystemThreadInterface& iface, GameVersion version) {
|
||||
jak1::start_overlord_wrapper(iop.overlord_argc, iop.overlord_argv, &complete);
|
||||
break;
|
||||
case GameVersion::Jak2:
|
||||
case GameVersion::Jak3: // TODO: jak3 using jak2's overlord.
|
||||
jak2::start_overlord_wrapper(iop.overlord_argc, iop.overlord_argv, &complete);
|
||||
break;
|
||||
case GameVersion::Jak3:
|
||||
jak3::start_overlord_wrapper(&complete);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
@ -334,7 +341,6 @@ void iop_runner(SystemThreadInterface& iface, GameVersion version) {
|
||||
{
|
||||
auto p = scoped_prof("overlord-wait-for-init");
|
||||
while (complete == false) {
|
||||
prof().root_event();
|
||||
iop.kernel.dispatch();
|
||||
}
|
||||
}
|
||||
@ -344,7 +350,7 @@ void iop_runner(SystemThreadInterface& iface, GameVersion version) {
|
||||
|
||||
// IOP Kernel loop
|
||||
while (!iface.get_want_exit() && !iop.want_exit) {
|
||||
prof().root_event();
|
||||
// prof().root_event();
|
||||
// The IOP scheduler informs us of how many microseconds are left until it has something to do.
|
||||
// So we can wait for that long or until something else needs it to wake up.
|
||||
auto wait_duration = iop.kernel.dispatch();
|
||||
|
@ -174,6 +174,10 @@ void DelayThread(u32 usec) {
|
||||
iop->kernel.DelayThread(usec);
|
||||
}
|
||||
|
||||
void YieldThread() {
|
||||
iop->kernel.YieldThread();
|
||||
}
|
||||
|
||||
int sceCdBreak() {
|
||||
return 1;
|
||||
}
|
||||
@ -199,16 +203,20 @@ s32 PollMbx(MsgPacket** recvmsg, int mbxid) {
|
||||
return iop->kernel.PollMbx((void**)recvmsg, mbxid);
|
||||
}
|
||||
|
||||
s32 ReceiveMbx(MsgPacket** recvmsg, int mbxid) {
|
||||
return iop->kernel.ReceiveMbx((void**)recvmsg, mbxid);
|
||||
}
|
||||
|
||||
s32 PeekMbx(s32 mbx) {
|
||||
return iop->kernel.PeekMbx(mbx);
|
||||
}
|
||||
|
||||
static int now = 0;
|
||||
s32 MbxSize(s32 mbx) {
|
||||
return iop->kernel.MbxSize(mbx);
|
||||
}
|
||||
|
||||
void GetSystemTime(SysClock* time) {
|
||||
time->lo = 0;
|
||||
time->hi = now;
|
||||
now += 10;
|
||||
u32 GetSystemTimeLow() {
|
||||
return iop->kernel.GetSystemTimeLow();
|
||||
}
|
||||
|
||||
void SleepThread() {
|
||||
@ -216,7 +224,7 @@ void SleepThread() {
|
||||
}
|
||||
|
||||
s32 CreateSema(SemaParam* param) {
|
||||
return iop->kernel.CreateSema(param->attr, param->option, param->max_count, param->init_count);
|
||||
return iop->kernel.CreateSema(param->attr, param->option, param->init_count, param->max_count);
|
||||
}
|
||||
|
||||
s32 WaitSema(s32 sema) {
|
||||
@ -231,6 +239,22 @@ s32 PollSema(s32 sema) {
|
||||
return iop->kernel.PollSema(sema);
|
||||
}
|
||||
|
||||
s32 CreateEventFlag(const EventFlagParam* param) {
|
||||
return iop->kernel.CreateEventFlag(param->attr, param->option, param->init_pattern);
|
||||
}
|
||||
|
||||
s32 ClearEventFlag(s32 flag, u32 pattern) {
|
||||
return iop->kernel.ClearEventFlag(flag, pattern);
|
||||
}
|
||||
|
||||
s32 WaitEventFlag(s32 flag, u32 pattern, u32 mode) {
|
||||
return iop->kernel.WaitEventFlag(flag, pattern, mode);
|
||||
}
|
||||
|
||||
s32 SetEventFlag(s32 flag, u32 pattern) {
|
||||
return iop->kernel.SetEventFlag(flag, pattern);
|
||||
}
|
||||
|
||||
s32 WakeupThread(s32 thid) {
|
||||
iop->kernel.WakeupThread(thid);
|
||||
return 0;
|
||||
|
@ -30,6 +30,10 @@
|
||||
#define SA_THFIFO 0
|
||||
#define SA_THPRI 1
|
||||
|
||||
#define EW_AND 0
|
||||
#define EW_OR 1
|
||||
#define EW_CLEAR 0x10
|
||||
|
||||
class IOP;
|
||||
|
||||
namespace iop {
|
||||
@ -56,7 +60,7 @@ struct sceCdRMode {
|
||||
};
|
||||
|
||||
struct sceSifDmaData {
|
||||
void* data;
|
||||
const void* data;
|
||||
void* addr;
|
||||
unsigned int size;
|
||||
unsigned int mode;
|
||||
@ -85,7 +89,7 @@ struct ThreadParam {
|
||||
int initPriority;
|
||||
|
||||
// added!
|
||||
char name[64];
|
||||
char name[64] = "";
|
||||
};
|
||||
|
||||
struct SemaParam {
|
||||
@ -95,6 +99,12 @@ struct SemaParam {
|
||||
int32_t max_count;
|
||||
};
|
||||
|
||||
struct EventFlagParam {
|
||||
u32 attr;
|
||||
u32 option;
|
||||
u32 init_pattern;
|
||||
};
|
||||
|
||||
// void PS2_RegisterIOP(IOP *iop);
|
||||
int QueryTotalFreeMemSize();
|
||||
void* AllocSysMemory(int type, unsigned long size, void* addr);
|
||||
@ -105,6 +115,7 @@ void CpuDisableIntr();
|
||||
void CpuEnableIntr();
|
||||
void SleepThread();
|
||||
void DelayThread(u32 usec);
|
||||
void YieldThread();
|
||||
s32 CreateThread(ThreadParam* param);
|
||||
s32 ExitThread();
|
||||
s32 StartThread(s32 thid, u32 arg);
|
||||
@ -135,16 +146,23 @@ u32 sceSifSetDma(sceSifDmaData* sdd, int len);
|
||||
|
||||
s32 SendMbx(int mbxid, void* sendmsg);
|
||||
s32 PollMbx(MsgPacket** recvmsg, int mbxid);
|
||||
s32 ReceiveMbx(MsgPacket** recvmsg, int mbxid);
|
||||
s32 PeekMbx(s32 mbx);
|
||||
s32 MbxSize(s32 mbx);
|
||||
s32 CreateMbx(MbxParam* param);
|
||||
|
||||
void GetSystemTime(SysClock* time);
|
||||
u32 GetSystemTimeLow();
|
||||
|
||||
s32 CreateSema(SemaParam* param);
|
||||
s32 WaitSema(s32 sema);
|
||||
s32 SignalSema(s32 sema);
|
||||
s32 PollSema(s32 sema);
|
||||
|
||||
s32 CreateEventFlag(const EventFlagParam* param);
|
||||
s32 ClearEventFlag(s32 flag, u32 pattern);
|
||||
s32 SetEventFlag(s32 flag, u32 pattern);
|
||||
s32 WaitEventFlag(s32 flag, u32 pattern, u32 mode);
|
||||
|
||||
s32 RegisterVblankHandler(int edge, int priority, int (*handler)(void*), void* userdata);
|
||||
|
||||
void FlushDcache();
|
||||
|
@ -15,8 +15,9 @@ BlockSoundHandler::BlockSoundHandler(SoundBank& bank,
|
||||
VoiceManager& vm,
|
||||
s32 sfx_vol,
|
||||
s32 sfx_pan,
|
||||
SndPlayParams& params)
|
||||
: m_group(sfx.VolGroup), m_sfx(sfx), m_vm(vm), m_bank(bank) {
|
||||
SndPlayParams& params,
|
||||
u32 sound_id)
|
||||
: m_group(sfx.VolGroup), m_sfx(sfx), m_vm(vm), m_bank(bank), m_sound_id(sound_id) {
|
||||
s32 vol, pan, pitch_mod, pitch_bend;
|
||||
if (sfx_vol == -1) {
|
||||
sfx_vol = sfx.Vol;
|
||||
|
@ -25,7 +25,8 @@ class BlockSoundHandler : public SoundHandler {
|
||||
VoiceManager& vm,
|
||||
s32 sfx_vol,
|
||||
s32 sfx_pan,
|
||||
SndPlayParams& params);
|
||||
SndPlayParams& params,
|
||||
u32 sound_id);
|
||||
|
||||
~BlockSoundHandler() override;
|
||||
bool Tick() override;
|
||||
@ -39,6 +40,7 @@ class BlockSoundHandler : public SoundHandler {
|
||||
void SetPMod(s32 mod) override;
|
||||
void SetRegister(u8 reg, u8 value) override { m_registers.at(reg) = value; };
|
||||
void SetPBend(s32 bend) override;
|
||||
u32 SoundID() const override { return m_sound_id; }
|
||||
|
||||
void DoGrain();
|
||||
|
||||
@ -86,5 +88,7 @@ class BlockSoundHandler : public SoundHandler {
|
||||
|
||||
s32 m_countdown{0};
|
||||
u32 m_next_grain{0};
|
||||
|
||||
u32 m_sound_id{0};
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -193,6 +193,14 @@ void Player::StopSound(u32 sound_id) {
|
||||
// m_handlers.erase(sound_id);
|
||||
}
|
||||
|
||||
u32 Player::GetSoundID(u32 sound_handle) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto handler = mHandlers.find(sound_handle);
|
||||
if (handler == mHandlers.end())
|
||||
return -1;
|
||||
return handler->second->SoundID();
|
||||
}
|
||||
|
||||
void Player::SetSoundReg(u32 sound_id, u8 reg, u8 value) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
if (mHandlers.find(sound_id) == mHandlers.end()) {
|
||||
|
@ -48,6 +48,7 @@ class Player {
|
||||
void SetMasterVolume(u32 group, s32 volume);
|
||||
void UnloadBank(BankHandle bank_handle);
|
||||
void StopSound(u32 sound_handle);
|
||||
u32 GetSoundID(u32 sound_handle);
|
||||
void SetPanTable(VolPair* pantable);
|
||||
void SetPlaybackMode(s32 mode);
|
||||
void PauseSound(s32 sound_handle);
|
||||
|
@ -18,7 +18,7 @@ std::optional<std::unique_ptr<SoundHandler>> SFXBlock::MakeHandler(VoiceManager&
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto handler = std::make_unique<BlockSoundHandler>(*this, SFX, vm, vol, pan, params);
|
||||
auto handler = std::make_unique<BlockSoundHandler>(*this, SFX, vm, vol, pan, params, sound_id);
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
@ -24,5 +24,6 @@ class SoundHandler {
|
||||
virtual void SetPMod(s32 mod) = 0;
|
||||
virtual void SetPBend(s32 /*mod*/){};
|
||||
virtual void SetRegister(u8 /*reg*/, u8 /*value*/) {}
|
||||
virtual u32 SoundID() const { return -1; }
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
std::shared_ptr<snd::Voice> voices[4];
|
||||
std::shared_ptr<snd::Voice> voices[kNVoices];
|
||||
u8 spu_memory[0x15160 * 10];
|
||||
|
||||
static sceSdTransIntrHandler trans_handler[2] = {nullptr, nullptr};
|
||||
@ -21,7 +21,7 @@ u32 sceSdGetSwitch(u32 entry) {
|
||||
}
|
||||
|
||||
snd::Voice* voice_from_entry(u32 entry) {
|
||||
u32 it = entry & 3;
|
||||
u32 it = entry % kNVoices;
|
||||
return voices[it].get();
|
||||
}
|
||||
|
||||
@ -66,7 +66,6 @@ void sceSdSetSwitch(u32 entry, u32 value) {
|
||||
void sceSdSetAddr(u32 entry, u32 value) {
|
||||
[[maybe_unused]] u32 core = entry & 1;
|
||||
[[maybe_unused]] u32 voice_id = (entry >> 1) & 0x1f;
|
||||
|
||||
auto* voice = voice_from_entry(voice_id);
|
||||
if (!voice) {
|
||||
return;
|
||||
@ -123,10 +122,10 @@ void sceSdSetTransIntrHandler(s32 channel, sceSdTransIntrHandler handler, void*
|
||||
userdata[channel] = data;
|
||||
}
|
||||
|
||||
u32 sceSdVoiceTrans(s32 channel, s32 mode, void* iop_addr, u32 spu_addr, u32 size) {
|
||||
u32 sceSdVoiceTrans(s32 channel, s32 mode, const void* iop_addr, u32 spu_addr, u32 size) {
|
||||
memcpy(&spu_memory[spu_addr], iop_addr, size);
|
||||
if (trans_handler[channel] != nullptr) {
|
||||
trans_handler[channel](channel, userdata);
|
||||
trans_handler[channel](channel, userdata[channel]);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
@ -19,7 +19,8 @@
|
||||
#define SD_S_KOFF (0x16 << 8)
|
||||
#define SD_VOICE(_core, _v) ((_core) | ((_v) << 1))
|
||||
|
||||
extern std::shared_ptr<snd::Voice> voices[4];
|
||||
constexpr int kNVoices = 8;
|
||||
extern std::shared_ptr<snd::Voice> voices[kNVoices];
|
||||
extern u8 spu_memory[0x15160 * 10];
|
||||
|
||||
using sceSdTransIntrHandler = int (*)(int, void*);
|
||||
@ -30,4 +31,4 @@ void sceSdSetSwitch(u32 entry, u32 value);
|
||||
void sceSdSetAddr(u32 entry, u32 value);
|
||||
void sceSdSetParam(u32 entry, u32 value);
|
||||
void sceSdSetTransIntrHandler(s32 channel, sceSdTransIntrHandler, void* data);
|
||||
u32 sceSdVoiceTrans(s32 channel, s32 mode, void* iop_addr, u32 spu_addr, u32 size);
|
||||
u32 sceSdVoiceTrans(s32 channel, s32 mode, const void* iop_addr, u32 spu_addr, u32 size);
|
||||
|
@ -105,6 +105,14 @@ void snd_StopSound(s32 sound_handle) {
|
||||
}
|
||||
}
|
||||
|
||||
u32 snd_GetSoundID(s32 sound_handle) {
|
||||
if (player) {
|
||||
return player->GetSoundID(sound_handle);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void snd_SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan) {
|
||||
if (player) {
|
||||
player->SetSoundVolPan(sound_handle, vol, pan);
|
||||
@ -220,6 +228,26 @@ snd::BankHandle snd_BankLoadEx(const char* filename,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool started = false;
|
||||
std::vector<u8> sbk_data;
|
||||
} // namespace
|
||||
|
||||
void snd_BankLoadFromIOPPartialEx_Start() {
|
||||
started = true;
|
||||
sbk_data.clear();
|
||||
}
|
||||
|
||||
void snd_BankLoadFromIOPPartialEx(const u8* data, u32 length, u32 spu_mem_loc, u32 spu_mem_size) {
|
||||
sbk_data.insert(sbk_data.end(), data, data + length);
|
||||
}
|
||||
void snd_BankLoadFromIOPPartialEx_Completion() {
|
||||
ASSERT(started);
|
||||
started = false;
|
||||
player->LoadBank(std::span(sbk_data));
|
||||
sbk_data.clear();
|
||||
}
|
||||
|
||||
s32 snd_GetVoiceStatus(s32 voice) {
|
||||
// hacky thincg to say that voice 0 is uses allocated
|
||||
if (voice == 0) {
|
||||
|
@ -35,6 +35,7 @@ void snd_SetPanTable(s16* table);
|
||||
void snd_SetPlayBackMode(s32 mode);
|
||||
s32 snd_SoundIsStillPlaying(s32 sound_handle);
|
||||
void snd_StopSound(s32 sound_handle);
|
||||
u32 snd_GetSoundID(s32 sound_handle);
|
||||
void snd_SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan);
|
||||
void snd_SetMasterVolume(s32 which, s32 volume);
|
||||
void snd_UnloadBank(snd::BankHandle bank_handle);
|
||||
@ -69,6 +70,11 @@ snd::BankHandle snd_BankLoadEx(const char* filepath,
|
||||
s32 data_offset,
|
||||
u32 spu_mem_loc,
|
||||
u32 spu_mem_size);
|
||||
|
||||
void snd_BankLoadFromIOPPartialEx_Start();
|
||||
void snd_BankLoadFromIOPPartialEx(const u8* data, u32 length, u32 spu_mem_loc, u32 spu_mem_size);
|
||||
void snd_BankLoadFromIOPPartialEx_Completion();
|
||||
|
||||
s32 snd_GetVoiceStatus(s32 voice);
|
||||
s32 snd_GetFreeSPUDMA();
|
||||
void snd_FreeSPUDMA(s32 channel);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/global_profiler/GlobalProfiler.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
@ -27,12 +28,27 @@ void IopThread::functionWrapper() {
|
||||
}
|
||||
}
|
||||
|
||||
IOP_Kernel::IOP_Kernel() {
|
||||
// this ugly hack
|
||||
threads.reserve(16);
|
||||
CreateThread("null-thread", nullptr, 0);
|
||||
CreateMbx();
|
||||
CreateSema(0, 0, 0, 0);
|
||||
kernel_thread = co_active();
|
||||
m_start_time = time_point_cast<microseconds>(steady_clock::now());
|
||||
}
|
||||
|
||||
/*
|
||||
** -----------------------------------------------------------------------------
|
||||
** Functions callable by threads
|
||||
** -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
u32 IOP_Kernel::GetSystemTimeLow() {
|
||||
auto delta_time = time_point_cast<microseconds>(steady_clock::now()) - m_start_time;
|
||||
return delta_time.count() * 36.864;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a new thread. Will not run the thread.
|
||||
*/
|
||||
@ -89,6 +105,12 @@ void IOP_Kernel::SleepThread() {
|
||||
leaveThread();
|
||||
}
|
||||
|
||||
void IOP_Kernel::YieldThread() {
|
||||
ASSERT(_currentThread);
|
||||
_currentThread->state = IopThread::State::Ready;
|
||||
leaveThread();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wake up a thread. Doesn't run it immediately though.
|
||||
*/
|
||||
@ -118,6 +140,105 @@ s32 IOP_Kernel::WaitSema(s32 id) {
|
||||
return KE_OK;
|
||||
}
|
||||
|
||||
s32 IOP_Kernel::ClearEventFlag(s32 id, u32 pattern) {
|
||||
auto& ef = event_flags.at(id);
|
||||
// yes, this seems backward, but the manual says this is how it works.
|
||||
ef.value &= pattern;
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool event_flag_check(u32 pattern, u32 check_pattern, u32 mode) {
|
||||
if (mode & 1) {
|
||||
// or
|
||||
return (pattern & check_pattern);
|
||||
} else {
|
||||
// and
|
||||
return (pattern & check_pattern) == check_pattern;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
s32 IOP_Kernel::WaitEventFlag(s32 flag, u32 pattern, u32 mode) {
|
||||
auto& ef = event_flags.at(flag);
|
||||
// check to see if we already match
|
||||
if (event_flag_check(ef.value, pattern, mode)) {
|
||||
if (mode & 0x10) {
|
||||
ef.value = 0;
|
||||
}
|
||||
return KE_OK;
|
||||
} else {
|
||||
if (!ef.multiple_waiters_allowed && !ef.wait_list.empty()) {
|
||||
lg::die("Multiple thread trying to wait on an event flag, but this option was not enabled.");
|
||||
}
|
||||
|
||||
auto& wait_entry = ef.wait_list.emplace_back();
|
||||
wait_entry.pattern = pattern;
|
||||
wait_entry.mode = mode;
|
||||
wait_entry.thread = _currentThread;
|
||||
|
||||
_currentThread->state = IopThread::State::Wait;
|
||||
_currentThread->waitType = IopThread::Wait::EventFlag;
|
||||
leaveThread();
|
||||
|
||||
return KE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
s32 IOP_Kernel::SetEventFlag(s32 flag, u32 pattern) {
|
||||
auto& ef = event_flags.at(flag);
|
||||
ef.value |= pattern;
|
||||
|
||||
for (auto it = ef.wait_list.begin(); it != ef.wait_list.end();) {
|
||||
if (event_flag_check(ef.value, it->pattern, it->mode)) {
|
||||
if (it->mode & 0x10) {
|
||||
ef.value = 0;
|
||||
}
|
||||
it->thread->waitType = IopThread::Wait::None;
|
||||
it->thread->state = IopThread::State::Ready;
|
||||
it = ef.wait_list.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return KE_OK;
|
||||
}
|
||||
|
||||
s32 IOP_Kernel::ReceiveMbx(void** msg, s32 id) {
|
||||
auto& box = mbxs.at(id);
|
||||
if (!box.messages.empty()) {
|
||||
auto ret = PollMbx(msg, id);
|
||||
ASSERT(ret == KE_OK);
|
||||
return KE_OK;
|
||||
}
|
||||
|
||||
ASSERT(!box.wait_thread); // don't know how to deal with this, hopefully doesn't come up.
|
||||
|
||||
box.wait_thread = _currentThread;
|
||||
_currentThread->state = IopThread::State::Wait;
|
||||
_currentThread->waitType = IopThread::Wait::Messagebox;
|
||||
leaveThread();
|
||||
|
||||
auto ret = PollMbx(msg, id);
|
||||
ASSERT(ret == KE_OK);
|
||||
return KE_OK;
|
||||
}
|
||||
|
||||
s32 IOP_Kernel::SendMbx(s32 mbx, void* value) {
|
||||
ASSERT(mbx < (s32)mbxs.size());
|
||||
auto& box = mbxs[mbx];
|
||||
box.messages.push(value);
|
||||
auto* to_run = box.wait_thread;
|
||||
|
||||
if (to_run) {
|
||||
box.wait_thread = nullptr;
|
||||
to_run->waitType = IopThread::Wait::None;
|
||||
to_run->state = IopThread::State::Ready;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 IOP_Kernel::SignalSema(s32 id) {
|
||||
auto& sema = semas.at(id);
|
||||
|
||||
@ -265,6 +386,9 @@ std::optional<time_stamp> IOP_Kernel::dispatch() {
|
||||
|
||||
// Run until all threads are idle
|
||||
IopThread* next = schedNext();
|
||||
if (next) {
|
||||
prof().root_event();
|
||||
}
|
||||
while (next != nullptr) {
|
||||
// Check vblank interrupt
|
||||
if (vblank_handler != nullptr && vblank_recieved) {
|
||||
@ -272,6 +396,7 @@ std::optional<time_stamp> IOP_Kernel::dispatch() {
|
||||
vblank_recieved = false;
|
||||
}
|
||||
// printf("[IOP Kernel] Dispatch %s (%d)\n", next->name.c_str(), next->thID);
|
||||
auto p = scoped_prof(next->name.c_str());
|
||||
runThread(next);
|
||||
updateDelay();
|
||||
processWakeups();
|
||||
|
@ -53,14 +53,10 @@ struct IopThread {
|
||||
Dormant,
|
||||
};
|
||||
|
||||
enum class Wait {
|
||||
None,
|
||||
Semaphore,
|
||||
Delay,
|
||||
};
|
||||
enum class Wait { None, Semaphore, Delay, Messagebox, EventFlag };
|
||||
|
||||
IopThread(std::string n, void (*f)(), s32 ID, u32 priority)
|
||||
: name(std::move(n)), function(f), priority(priority), thID(ID) {
|
||||
IopThread(std::string n, void (*f)(), s32 ID, u32 pri)
|
||||
: name(std::move(n)), function(f), priority(pri), thID(ID) {
|
||||
thread = co_create(0x300000, functionWrapper);
|
||||
}
|
||||
|
||||
@ -79,8 +75,12 @@ struct IopThread {
|
||||
|
||||
struct Semaphore {
|
||||
enum class attribute { fifo, prio };
|
||||
Semaphore(attribute attr, s32 option, s32 init_count, s32 max_count)
|
||||
: attr(attr), option(option), count(init_count), initCount(init_count), maxCount(max_count) {}
|
||||
Semaphore(attribute _attr, s32 _option, s32 init_count, s32 max_count)
|
||||
: attr(_attr),
|
||||
option(_option),
|
||||
count(init_count),
|
||||
initCount(init_count),
|
||||
maxCount(max_count) {}
|
||||
|
||||
attribute attr{attribute::fifo};
|
||||
u32 option{0};
|
||||
@ -91,17 +91,26 @@ struct Semaphore {
|
||||
std::list<IopThread*> wait_list;
|
||||
};
|
||||
|
||||
struct EventFlagWaiter {
|
||||
IopThread* thread = nullptr;
|
||||
u32 pattern = 0;
|
||||
u32 mode = 0;
|
||||
};
|
||||
|
||||
struct EventFlag {
|
||||
bool multiple_waiters_allowed = false;
|
||||
u32 value = 0;
|
||||
std::list<EventFlagWaiter> wait_list;
|
||||
};
|
||||
|
||||
struct Messagebox {
|
||||
std::queue<void*> messages;
|
||||
IopThread* wait_thread = nullptr;
|
||||
};
|
||||
|
||||
class IOP_Kernel {
|
||||
public:
|
||||
IOP_Kernel() {
|
||||
// this ugly hack
|
||||
threads.reserve(16);
|
||||
CreateThread("null-thread", nullptr, 0);
|
||||
CreateMbx();
|
||||
CreateSema(0, 0, 0, 0);
|
||||
kernel_thread = co_active();
|
||||
}
|
||||
|
||||
IOP_Kernel();
|
||||
s32 CreateThread(std::string n, void (*f)(), u32 priority);
|
||||
s32 ExitThread();
|
||||
void StartThread(s32 id);
|
||||
@ -109,6 +118,7 @@ class IOP_Kernel {
|
||||
void SleepThread();
|
||||
void WakeupThread(s32 id);
|
||||
void iWakeupThread(s32 id);
|
||||
void YieldThread();
|
||||
std::optional<time_stamp> dispatch();
|
||||
void set_rpc_queue(iop::sceSifQueueData* qd, u32 thread);
|
||||
void rpc_loop(iop::sceSifQueueData* qd);
|
||||
@ -137,30 +147,29 @@ class IOP_Kernel {
|
||||
*/
|
||||
s32 PollMbx(void** msg, s32 mbx) {
|
||||
ASSERT(mbx < (s32)mbxs.size());
|
||||
s32 gotSomething = mbxs[mbx].empty() ? 0 : 1;
|
||||
s32 gotSomething = mbxs[mbx].messages.empty() ? 0 : 1;
|
||||
if (gotSomething) {
|
||||
void* thing = mbxs[mbx].front();
|
||||
void* thing = mbxs[mbx].messages.front();
|
||||
|
||||
if (msg) {
|
||||
*msg = thing;
|
||||
}
|
||||
|
||||
mbxs[mbx].pop();
|
||||
mbxs[mbx].messages.pop();
|
||||
}
|
||||
|
||||
return gotSomething ? KE_OK : KE_MBOX_NOMSG;
|
||||
}
|
||||
|
||||
s32 PeekMbx(s32 mbx) { return !mbxs[mbx].empty(); }
|
||||
s32 PeekMbx(s32 mbx) { return !mbxs[mbx].messages.empty(); }
|
||||
s32 MbxSize(s32 mbx) { return mbxs[mbx].messages.size(); }
|
||||
|
||||
s32 ReceiveMbx(void** msg, s32 id);
|
||||
|
||||
/*!
|
||||
* Push something into a mbx
|
||||
*/
|
||||
s32 SendMbx(s32 mbx, void* value) {
|
||||
ASSERT(mbx < (s32)mbxs.size());
|
||||
mbxs[mbx].push(value);
|
||||
return 0;
|
||||
}
|
||||
s32 SendMbx(s32 mbx, void* value);
|
||||
|
||||
s32 CreateSema(s32 attr, s32 option, s32 init_count, s32 max_count) {
|
||||
s32 id = semas.size();
|
||||
@ -172,11 +181,27 @@ class IOP_Kernel {
|
||||
s32 SignalSema(s32 id);
|
||||
s32 PollSema(s32 id);
|
||||
|
||||
s32 CreateEventFlag(s32 attr, s32 option, u32 init_pattern) {
|
||||
ASSERT(option == 0);
|
||||
s32 id = event_flags.size();
|
||||
auto& flag = event_flags.emplace_back();
|
||||
flag.value = init_pattern;
|
||||
flag.multiple_waiters_allowed = attr == 2;
|
||||
return id;
|
||||
}
|
||||
|
||||
s32 WaitEventFlag(s32 flag, u32 pattern, u32 mode);
|
||||
s32 SetEventFlag(s32 flag, u32 pattern);
|
||||
|
||||
s32 ClearEventFlag(s32 id, u32 pattern);
|
||||
|
||||
s32 RegisterVblankHandler(int (*handler)(void*)) {
|
||||
vblank_handler = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GetSystemTimeLow();
|
||||
|
||||
void signal_vblank() { vblank_recieved = true; };
|
||||
|
||||
bool sif_busy(u32 id);
|
||||
@ -198,17 +223,20 @@ class IOP_Kernel {
|
||||
IopThread* schedNext();
|
||||
std::optional<time_stamp> nextWakeup();
|
||||
|
||||
s32 (*vblank_handler)(void*);
|
||||
s32 (*vblank_handler)(void*) = nullptr;
|
||||
std::atomic_bool vblank_recieved = false;
|
||||
|
||||
cothread_t kernel_thread;
|
||||
s32 _nextThID = 0;
|
||||
IopThread* _currentThread = nullptr;
|
||||
std::vector<IopThread> threads;
|
||||
std::vector<std::queue<void*>> mbxs;
|
||||
std::vector<Messagebox> mbxs;
|
||||
std::vector<SifRecord> sif_records;
|
||||
std::vector<Semaphore> semas;
|
||||
std::vector<EventFlag> event_flags;
|
||||
std::queue<int> wakeup_queue;
|
||||
bool mainThreadSleep = false;
|
||||
std::mutex sif_mtx, wakeup_mtx;
|
||||
|
||||
time_stamp m_start_time;
|
||||
};
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "iop_thread.h"
|
||||
|
||||
#include "common/global_profiler/GlobalProfiler.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#elif _WIN32
|
||||
@ -57,6 +59,7 @@ void* IOP::iop_alloc(int size) {
|
||||
|
||||
void IOP::wait_run_iop(
|
||||
std::chrono::time_point<std::chrono::steady_clock, std::chrono::microseconds> wakeup) {
|
||||
auto p = scoped_prof("krnlw");
|
||||
std::unique_lock<std::mutex> lk(run_cv_mutex);
|
||||
iop_run_cv.wait_until(lk, wakeup);
|
||||
}
|
||||
|
@ -905,8 +905,6 @@
|
||||
(defstate wait (scene-player)
|
||||
:virtual #t
|
||||
:enter (behavior ((arg0 symbol))
|
||||
(format 0 "scene-player: skipping scene~%")
|
||||
(set! (-> self aborted?) #t)
|
||||
(set-time! (-> self state-time))
|
||||
(if (= (-> *game-info* demo-state) 101)
|
||||
(set-setting! 'audio-language #f 0.0 5)
|
||||
@ -1158,7 +1156,6 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(not (-> self aborted?))
|
||||
)
|
||||
(set-blackout-frames (seconds 0.1))
|
||||
(suspend)
|
||||
@ -1549,7 +1546,6 @@
|
||||
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons triangle))
|
||||
#t
|
||||
)
|
||||
(none)
|
||||
)
|
||||
false-func
|
||||
)
|
||||
@ -1577,10 +1573,6 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(#when PC_PORT ;; og:preserve-this until overlord2 is done
|
||||
(backup-load-state-and-set-cmds *load-state* (-> self anim command-list))
|
||||
(restore-load-state-and-cleanup *load-state*)
|
||||
)
|
||||
(if (and (-> self wait) *target* (focus-test? *target* grabbed))
|
||||
(go-virtual release)
|
||||
)
|
||||
|
@ -492,8 +492,9 @@
|
||||
"WASSTAD4" "WASSTAD5" "WASSTAD6" "WASTOAD" "WASTURT")
|
||||
|
||||
;; Jak 3 has no MUS files
|
||||
;; (copy-mus-files "" "TWEAKVAL")
|
||||
|
||||
(defstep :in "$ISO/RES/TWEAKVAL.MUS"
|
||||
:tool 'copy
|
||||
:out '("$OUT/iso/TWEAKVAL.MUS"))
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Text
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -531,6 +532,7 @@
|
||||
"$OUT/iso/7COMMON.TXT"
|
||||
"$OUT/iso/0SUBTI2.TXT"
|
||||
"$OUT/iso/VAGDIR.AYB"
|
||||
"$OUT/iso/TWEAKVAL.MUS"
|
||||
,@(reverse *all-vis*)
|
||||
,@(reverse *all-str*)
|
||||
,@(reverse *all-sbk*)
|
||||
|
21
test/decompiler/reference/jak3/engine/scene/scene_REF.gc
generated
vendored
21
test/decompiler/reference/jak3/engine/scene/scene_REF.gc
generated
vendored
@ -1707,16 +1707,13 @@
|
||||
(the-as
|
||||
(function process-drawable symbol)
|
||||
(if (logtest? (-> self scene scene-flags) (scene-flags scf1))
|
||||
(lambda :behavior scene-player
|
||||
()
|
||||
(when (cpad-pressed? 0 triangle)
|
||||
(set! (-> self aborted?) #t)
|
||||
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons triangle))
|
||||
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons triangle))
|
||||
#t
|
||||
)
|
||||
(none)
|
||||
)
|
||||
(lambda :behavior scene-player () (when (cpad-pressed? 0 triangle)
|
||||
(set! (-> self aborted?) #t)
|
||||
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons triangle))
|
||||
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons triangle))
|
||||
#t
|
||||
)
|
||||
)
|
||||
false-func
|
||||
)
|
||||
)
|
||||
@ -2169,7 +2166,3 @@
|
||||
)
|
||||
this
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user