mirror of
https://github.com/open-goal/jak-project
synced 2026-08-07 10:16:45 -04:00
d3cc739e43
This attempts to get into master whatever work was done in this PR / it's earlier PR https://github.com/open-goal/jak-project/pull/3965 I don't want this work to be lost / floating around in massive PRs. However the changes are: - switch to ntsc_v1 instead of PAL as the development target, as we have done for all other games - remove most of the copied-from-jak2/3 changes as they need to be confirmed during the decompilation process not just assumed - avoids committing any changes to `game/kernel/common` as it was not clear to me if these were changes made in jak x's kernel that were not properly broken out into it's own functions. We don't want to accidentally introduce bugs into jak1-3's kernel code. - in other words, if the change in the kernel only happens in jak x...it should likely be specific to jak x's kernel, not common. --------- Co-authored-by: VodBox <dillon@vodbox.io> Co-authored-by: yodah <greenboyyodah@gmail.com>
333 lines
11 KiB
C++
333 lines
11 KiB
C++
#include "basefile.h"
|
|
|
|
#include "common/log/log.h"
|
|
#include "common/util/Assert.h"
|
|
|
|
#include "game/overlord/jakx/overlord.h"
|
|
#include "game/overlord/jakx/pagemanager.h"
|
|
#include "game/overlord/jakx/vag.h"
|
|
|
|
namespace jakx {
|
|
void jakx_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 jakx::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, jakx::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 jakx
|