Switch to subrepos

git subrepo clone https://github.com/open-ead/sead lib/sead

subrepo:
  subdir:   "lib/sead"
  merged:   "1b66e825d"
upstream:
  origin:   "https://github.com/open-ead/sead"
  branch:   "master"
  commit:   "1b66e825d"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

git subrepo clone (merge) https://github.com/open-ead/nnheaders lib/NintendoSDK

subrepo:
  subdir:   "lib/NintendoSDK"
  merged:   "9ee21399f"
upstream:
  origin:   "https://github.com/open-ead/nnheaders"
  branch:   "master"
  commit:   "9ee21399f"
git-subrepo:
  version:  "0.4.3"
  origin:   "ssh://git@github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

git subrepo clone https://github.com/open-ead/agl lib/agl

subrepo:
  subdir:   "lib/agl"
  merged:   "7c063271b"
upstream:
  origin:   "https://github.com/open-ead/agl"
  branch:   "master"
  commit:   "7c063271b"
git-subrepo:
  version:  "0.4.3"
  origin:   "ssh://git@github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"

git subrepo clone https://github.com/open-ead/EventFlow lib/EventFlow

subrepo:
  subdir:   "lib/EventFlow"
  merged:   "c35d21b34"
upstream:
  origin:   "https://github.com/open-ead/EventFlow"
  branch:   "master"
  commit:   "c35d21b34"
git-subrepo:
  version:  "0.4.3"
  origin:   "ssh://git@github.com/ingydotnet/git-subrepo"
  commit:   "2f68596"
This commit is contained in:
Léo Lam
2022-03-21 19:25:20 +01:00
parent ffcc7f659e
commit 18c60323a9
457 changed files with 52182 additions and 16 deletions
+230
View File
@@ -0,0 +1,230 @@
#include <cstdlib>
#include <basis/seadNew.h>
#include <heap/seadHeap.h>
#include <heap/seadHeapMgr.h>
namespace sead
{
namespace system
{
void* NewImpl(Heap* heap, size_t size, s32 alignment, bool abortOnFailure)
{
if (!HeapMgr::sInstancePtr)
{
SEAD_WARN("alloced[%zu] before sead system initialize", size);
return malloc(size);
}
if (!heap)
{
heap = sead::HeapMgr::sInstancePtr->getCurrentHeap();
if (!heap)
{
SEAD_ASSERT_MSG(false, "Current heap is null. Cannot alloc.");
return nullptr;
}
}
void* result = heap->tryAlloc(size, alignment);
if (!result && abortOnFailure)
{
SEAD_ASSERT_MSG(
false, "alloc failed. size: %zu, allocatable size: %zu, alignment: %d, heap: %s", size,
heap->getMaxAllocatableSize(alignment), alignment, heap->getName().cstr());
return nullptr;
}
return result;
}
void DeleteImpl(void* ptr)
{
if (!sead::HeapMgr::sInstancePtr)
{
SEAD_WARN("free[0x%p] before sead system initialize", ptr);
free(ptr);
return;
}
if (!ptr)
return;
Heap* containHeap = sead::HeapMgr::sInstancePtr->findContainHeap(ptr);
if (containHeap)
containHeap->free(ptr);
else
SEAD_ASSERT_MSG(false, "delete bad pointer [0x%p]", ptr);
}
} // namespace system
#ifdef SEAD_DEBUG
void AllocFailAssert(Heap* heap, size_t size, u32 alignment)
{
if (!heap)
{
heap = HeapMgr::instance()->getCurrentHeap();
SEAD_ASSERT_MSG(heap, "Current heap is null. Cannot alloc.");
}
SEAD_ASSERT_MSG(false,
"alloc failed. size: %zu, allocatable size: %zu, alignment: %u, heap: %s", size,
heap->getMaxAllocatableSize(alignment), alignment, heap->getName().cstr());
}
#endif
} // namespace sead
// operator new(size_t)
void* operator new(size_t size)
{
return sead::system::NewImpl(nullptr, size, 8, true);
}
void* operator new[](size_t size)
{
return sead::system::NewImpl(nullptr, size, 8, true);
}
void* operator new(size_t size, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(nullptr, size, 8, false);
}
void* operator new[](size_t size, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(nullptr, size, 8, false);
}
// operator new(size_t, s32 alignment)
void* operator new(size_t size, s32 alignment)
{
return sead::system::NewImpl(nullptr, size, alignment, true);
}
void* operator new[](size_t size, s32 alignment)
{
return sead::system::NewImpl(nullptr, size, alignment, true);
}
void* operator new(size_t size, s32 alignment, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(nullptr, size, alignment, false);
}
void* operator new[](size_t size, s32 alignment, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(nullptr, size, alignment, false);
}
// operator new(size_t, sead::Heap*, s32 alignment)
void* operator new(size_t size, sead::Heap* heap, s32 alignment)
{
return sead::system::NewImpl(heap, size, alignment, true);
}
void* operator new[](size_t size, sead::Heap* heap, s32 alignment)
{
return sead::system::NewImpl(heap, size, alignment, true);
}
void* operator new(size_t size, sead::Heap* heap, s32 alignment, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(heap, size, alignment, false);
}
void* operator new[](size_t size, sead::Heap* heap, s32 alignment, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(heap, size, alignment, false);
}
// operator new(size_t, sead::Heap*, const std::nothrow_t&)
void* operator new(size_t size, sead::Heap* heap, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(heap, size, 8, false);
}
void* operator new[](size_t size, sead::Heap* heap, const std::nothrow_t&) noexcept
{
return sead::system::NewImpl(heap, size, 8, false);
}
// operator delete(void*)
void operator delete(void* ptr) noexcept
{
sead::system::DeleteImpl(ptr);
}
void operator delete[](void* ptr) noexcept
{
sead::system::DeleteImpl(ptr);
}
void operator delete(void* ptr, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
void operator delete[](void* ptr, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
// operator delete(void*, s32)
void operator delete(void* ptr, s32)
{
sead::system::DeleteImpl(ptr);
}
void operator delete[](void* ptr, s32)
{
sead::system::DeleteImpl(ptr);
}
void operator delete(void* ptr, s32, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
void operator delete[](void* ptr, s32, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
// operator delete(void*, sead::Heap*, const std::nothrow_t&)
void operator delete(void* ptr, sead::Heap*, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
void operator delete[](void* ptr, sead::Heap*, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
// operator delete(void*, sead::Heap*, s32)
void operator delete(void* ptr, sead::Heap*, s32)
{
sead::system::DeleteImpl(ptr);
}
void operator delete[](void* ptr, sead::Heap*, s32)
{
sead::system::DeleteImpl(ptr);
}
void operator delete(void* ptr, sead::Heap*, s32, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
void operator delete[](void* ptr, sead::Heap*, s32, const std::nothrow_t&) noexcept
{
sead::system::DeleteImpl(ptr);
}
@@ -0,0 +1,59 @@
#include <array>
#include <codec/seadHashCRC16.h>
namespace sead
{
u16 HashCRC16::sTable[256];
bool HashCRC16::sInitialized = false;
void HashCRC16::initialize()
{
for (u32 i = 0; i < std::size(sTable); ++i)
{
u32 val = i;
for (int j = 0; j < 8; ++j)
val = ((val & 1) == 0) ? (val >> 1) : ((val >> 1) ^ 0xA001);
sTable[i] = val;
}
sInitialized = true;
}
u32 HashCRC16::calcHash(const void* ptr, u32 size)
{
Context ctx;
return calcHashWithContext(&ctx, ptr, size);
}
u32 HashCRC16::calcHashWithContext(Context* context, const void* ptr, u32 size)
{
if (!sInitialized)
initialize();
u32 hash = context->hash;
const u8* data = static_cast<const u8*>(ptr);
while (size--)
hash = sTable[*data++ ^ (hash & 0xFF)] ^ (hash >> 8);
context->hash = hash;
return hash;
}
u32 HashCRC16::calcStringHash(const char* str)
{
Context ctx;
return calcStringHashWithContext(&ctx, str);
}
u32 HashCRC16::calcStringHashWithContext(Context* context, const char* str)
{
if (!sInitialized)
initialize();
u32 hash = context->hash;
while (*str)
hash = sTable[*str++ ^ (hash & 0xFF)] ^ (hash >> 8);
context->hash = hash;
return hash;
}
} // namespace sead
@@ -0,0 +1,59 @@
#include <array>
#include <codec/seadHashCRC32.h>
namespace sead
{
u32 HashCRC32::sTable[256];
bool HashCRC32::sInitialized = false;
void HashCRC32::initialize()
{
for (u32 i = 0; i < std::size(sTable); ++i)
{
u32 val = i;
for (int j = 0; j < 8; ++j)
val = ((val & 1) == 0) ? (val >> 1) : ((val >> 1) ^ 0xEDB88320);
sTable[i] = val;
}
sInitialized = true;
}
u32 HashCRC32::calcHash(const void* ptr, u32 size)
{
Context ctx;
return calcHashWithContext(&ctx, ptr, size);
}
u32 HashCRC32::calcHashWithContext(Context* context, const void* ptr, u32 size)
{
if (!sInitialized)
initialize();
u32 hash = context->hash;
const u8* data = static_cast<const u8*>(ptr);
while (size--)
hash = sTable[*data++ ^ (hash & 0xFF)] ^ (hash >> 8);
context->hash = hash;
return ~hash;
}
u32 HashCRC32::calcStringHash(const char* str)
{
Context ctx;
return calcStringHashWithContext(&ctx, str);
}
u32 HashCRC32::calcStringHashWithContext(Context* context, const char* str)
{
if (!sInitialized)
initialize();
u32 hash = context->hash;
while (*str)
hash = sTable[*str++ ^ (hash & 0xFF)] ^ (hash >> 8);
context->hash = hash;
return ~hash;
}
} // namespace sead
@@ -0,0 +1,130 @@
#include <basis/seadRawPrint.h>
#include <container/seadListImpl.h>
namespace sead
{
void ListNode::insertBack_(ListNode* node)
{
SEAD_ASSERT_MSG(!node->isLinked(), "node is already linked.");
ListNode* next = mNext;
mNext = node;
node->mPrev = this;
node->mNext = next;
if (next)
next->mPrev = node;
}
void ListNode::insertFront_(ListNode* node)
{
SEAD_ASSERT_MSG(!node->isLinked(), "node is already linked.");
ListNode* prev = mPrev;
this->mPrev = node;
node->mPrev = prev;
node->mNext = this;
if (prev == NULL)
return;
prev->mNext = node;
}
void ListNode::erase_()
{
SEAD_ASSERT_MSG(isLinked(), "node is not linked.");
if (mPrev != nullptr)
mPrev->mNext = mNext;
if (mNext != nullptr)
mNext->mPrev = mPrev;
mPrev = mNext = NULL;
}
ListNode* ListImpl::popBack()
{
if (mCount < 1)
return nullptr;
ListNode* back = mStartEnd.mPrev;
back->erase_();
--mCount;
return back;
}
ListNode* ListImpl::popFront()
{
if (mCount < 1)
return nullptr;
ListNode* front = mStartEnd.mNext;
front->erase_();
--mCount;
return front;
}
ListNode* ListImpl::nth(s32 index) const
{
if (u32(mCount) <= u32(index))
{
SEAD_ASSERT_MSG(false, "index exceeded[%d/%d]", index, mCount);
return nullptr;
}
ListNode* node = mStartEnd.mNext;
for (s32 i = 0; i < index; ++i)
node = node->mNext;
return node;
}
s32 ListImpl::indexOf(const ListNode* n) const
{
ListNode* node = mStartEnd.mNext;
s32 index = 0;
while (node != &mStartEnd)
{
if (node == n)
return index;
++index;
node = node->mNext;
}
return -1;
}
void ListImpl::clear()
{
ListNode* node = mStartEnd.mNext;
while (node != &mStartEnd)
{
ListNode* next = node->mNext;
node->init_();
node = next;
}
mCount = 0;
mStartEnd.mPrev = &mStartEnd;
mStartEnd.mNext = &mStartEnd;
}
void ListImpl::swap(ListNode* n1, ListNode* n2)
{
SEAD_ASSERT(n1->mPrev && n1->mNext && n2->mPrev && n2->mNext);
if (n1 == n2)
return;
ListNode* n1_prev = n1->mPrev;
ListNode* n2_prev = n2->mPrev;
if (n2_prev != n1)
{
n1->erase_();
n2_prev->insertBack_(n1);
}
if (n1_prev != n2)
{
n2->erase_();
n1_prev->insertBack_(n2);
}
}
} // namespace sead
@@ -0,0 +1,155 @@
#include <basis/seadNew.h>
#include <basis/seadRawPrint.h>
#include <container/seadPtrArray.h>
namespace sead
{
void PtrArrayImpl::setBuffer(s32 ptrNumMax, void* buf)
{
if (ptrNumMax < 1)
{
SEAD_ASSERT_MSG(false, "ptrNumMax[%d] must be larger than zero", ptrNumMax);
return;
}
if (buf == NULL)
{
SEAD_ASSERT_MSG(false, "buf is null");
return;
}
mPtrs = static_cast<void**>(buf);
mPtrNum = 0;
mPtrNumMax = ptrNumMax;
}
void PtrArrayImpl::allocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment)
{
SEAD_ASSERT(mPtrs == nullptr);
if (ptrNumMax < 1)
{
SEAD_ASSERT_MSG(false, "ptrNumMax[%d] must be larger than zero", ptrNumMax);
return;
}
setBuffer(ptrNumMax, new (heap, alignment, std::nothrow) u8[s32(sizeof(void*)) * ptrNumMax]);
}
bool PtrArrayImpl::tryAllocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment)
{
SEAD_ASSERT(mPtrs == nullptr);
if (ptrNumMax < 1)
{
SEAD_ASSERT_MSG(false, "ptrNumMax[%d] must be larger than zero", ptrNumMax);
return false;
}
auto* buf = new (heap, alignment, std::nothrow) u8[s32(sizeof(void*)) * ptrNumMax];
if (!buf)
return false;
setBuffer(ptrNumMax, buf);
return true;
}
void PtrArrayImpl::freeBuffer()
{
if (isBufferReady())
{
delete[] mPtrs;
mPtrs = nullptr;
mPtrNum = 0;
mPtrNumMax = 0;
}
}
void PtrArrayImpl::erase(s32 pos, s32 count)
{
if (pos < 0)
{
SEAD_ASSERT_MSG(false, "illegal position[%d]", pos);
return;
}
if (count < 0)
{
SEAD_ASSERT_MSG(false, "illegal number[%d]", count);
return;
}
if (pos + count > mPtrNum)
{
SEAD_ASSERT_MSG(false, "pos[%d] + num[%d] exceed size[%d]", pos, count, mPtrNum);
return;
}
const s32 endPos = pos + count;
if (mPtrNum > endPos)
MemUtil::copyOverlap(mPtrs + pos, mPtrs + endPos, sizeof(void*) * (mPtrNum - endPos));
mPtrNum -= count;
}
// NON_MATCHING: semantically equivalent
void PtrArrayImpl::reverse()
{
for (s32 i = 0; i < mPtrNum / 2; ++i)
swap(mPtrNum - i - 1, i);
}
// FisherYates shuffle.
void PtrArrayImpl::shuffle(Random* random)
{
SEAD_ASSERT(random);
for (s32 i = mPtrNum - 1; i > 0; --i)
swap(i, random->getS32Range(0, i + 1));
}
void PtrArrayImpl::insert(s32 pos, void* ptr)
{
if (!checkInsert(pos, 1))
return;
createVacancy(pos, 1);
mPtrs[pos] = ptr;
++mPtrNum;
}
bool PtrArrayImpl::checkInsert(s32 pos, s32 num)
{
if (pos < 0)
{
SEAD_ASSERT_MSG(false, "illegal position[%d]", pos);
return false;
}
if (mPtrNum + num > mPtrNumMax)
{
SEAD_ASSERT_MSG(false, "list is full.");
return false;
}
if (mPtrNum < pos)
{
SEAD_ASSERT_MSG(false, "pos[%d] exceed size[%d]", pos, mPtrNum);
return false;
}
return true;
}
// TODO: PtrArrayImpl::insertArray
// TODO: PtrArrayImpl::sort
// TODO: PtrArrayImpl::heapSort
// TODO: PtrArrayImpl::compare
// TODO: PtrArrayImpl::uniq
// TODO: PtrArrayImpl::binarySearch
} // namespace sead
@@ -0,0 +1,201 @@
#include <basis/seadRawPrint.h>
#include <container/seadTreeNode.h>
namespace sead
{
TreeNode::TreeNode()
{
clearLinks();
}
void TreeNode::clearChildLinksRecursively_()
{
TreeNode* node = this->mChild;
while (node != NULL)
{
TreeNode* next = node->mNext;
node->clearChildLinksRecursively_();
node->clearLinks();
node = next;
}
}
void TreeNode::clearLinks()
{
mPrev = NULL;
mParent = NULL;
mNext = NULL;
mChild = NULL;
}
s32 TreeNode::countChildren() const
{
s32 count = 0;
TreeNode* node = mChild;
while (node)
{
++count;
node = node->mNext;
}
return count;
}
void TreeNode::detachAll()
{
detachSubTree();
clearChildLinksRecursively_();
clearLinks();
}
void TreeNode::detachSubTree()
{
if (mParent && mParent->mChild == this)
{
mParent->mChild = mNext;
if (mNext)
{
mNext->mPrev = mPrev;
mNext = nullptr;
}
}
else
{
if (mPrev)
mPrev->mNext = mNext;
if (mNext)
{
mNext->mPrev = mPrev;
mNext = nullptr;
}
else if (mParent)
{
mParent->mChild->mPrev = mPrev;
}
}
mPrev = nullptr;
mParent = nullptr;
}
TreeNode* TreeNode::findRoot()
{
if (!mParent)
return this;
TreeNode* p = mParent;
TreeNode* root;
do
{
root = p;
SEAD_ASSERT(p != this);
p = p->mParent;
} while (p);
return root;
}
const TreeNode* TreeNode::findRoot() const
{
if (!mParent)
return this;
TreeNode* p = mParent;
TreeNode* root;
do
{
root = p;
SEAD_ASSERT(p != this);
p = p->mParent;
} while (p);
return root;
}
void TreeNode::insertAfterSelf(TreeNode* node)
{
node->detachSubTree();
TreeNode* next = mNext;
mNext = node;
node->mPrev = this;
node->mNext = next;
if (next)
next->mPrev = node;
else if (mParent)
mParent->mChild->mPrev = node;
node->mParent = mParent;
}
void TreeNode::insertBeforeSelf(TreeNode* node)
{
node->detachSubTree();
TreeNode* prev = mPrev;
mPrev = node;
node->mPrev = prev;
node->mNext = this;
if (mParent && mParent->mChild == this)
mParent->mChild = node;
else if (prev)
prev->mNext = node;
node->mParent = mParent;
}
void TreeNode::pushBackChild(TreeNode* node)
{
node->detachSubTree();
if (mChild)
{
TreeNode* n = mChild->mPrev;
SEAD_ASSERT(n);
n->mNext = node;
node->mPrev = n;
node->mParent = n->mParent;
mChild->mPrev = node;
}
else
{
mChild = node;
node->mParent = this;
node->mPrev = node;
}
}
void TreeNode::pushBackSibling(TreeNode* node)
{
node->detachSubTree();
TreeNode* m;
if (mParent && mParent->mChild)
{
m = mParent->mChild->mPrev;
mParent->mChild->mPrev = node;
}
else
{
m = this;
while (m->mNext)
m = m->mNext;
}
m->mNext = node;
node->mPrev = m;
node->mParent = m->mParent;
}
void TreeNode::pushFrontChild(TreeNode* node)
{
node->detachSubTree();
if (mChild)
{
node->mNext = mChild;
node->mPrev = mChild->mPrev;
mChild->mPrev = node;
mChild = node;
node->mParent = this;
}
else
{
mChild = node;
node->mParent = this;
node->mPrev = node;
}
}
} // namespace sead
@@ -0,0 +1,29 @@
#include "devenv/seadAssertConfig.h"
namespace sead
{
AssertConfig::AssertEvent AssertConfig::sAssertEvent{};
IDelegate1<const char*>* AssertConfig::sFinalCallback = nullptr;
void AssertConfig::registerCallback(AssertEvent::Slot& slot)
{
sAssertEvent.connect(slot);
}
void AssertConfig::unregisterCallback(AssertEvent::Slot& slot)
{
sAssertEvent.disconnect(slot);
}
void AssertConfig::registerFinalCallback(IDelegate1<const char*>* cb)
{
sFinalCallback = cb;
}
void AssertConfig::execCallbacks(const char* assertMessage)
{
sAssertEvent.emit(assertMessage);
if (sFinalCallback)
sFinalCallback->invoke(assertMessage);
}
} // namespace sead
@@ -0,0 +1,20 @@
#include "devenv/seadGameConfig.h"
#include "filedevice/seadFileDevice.h"
#include "filedevice/seadFileDeviceMgr.h"
namespace sead
{
SEAD_SINGLETON_DISPOSER_IMPL(GameConfig)
const SafeString GameConfig::cNodeName = "sead::GameConfig";
void GameConfig::FileWriteCallback::save()
{
// FIXME
// Do not remove FileHandle. While this may appear to be useless, it has the effect of forcing
// FileHandle's vtable to be placed in this translation unit, which inhibits undesirable
// inlining.
FileHandle handle;
FileDeviceMgr::instance()->tryOpen(&handle, "dummy", FileDevice::cFileOpenFlag_WriteOnly, 0);
}
} // namespace sead
@@ -0,0 +1,6 @@
#include "devenv/seadStackTrace.h"
namespace sead
{
StackTraceBase::StackTraceBase() = default;
}
@@ -0,0 +1,377 @@
#include <cafe.h>
#include <filedevice/cafe/seadCafeFSAFileDeviceCafe.h>
#include <filedevice/seadFileDevice.h>
#include <filedevice/seadFileDeviceMgr.h>
#include <prim/seadSafeString.h>
namespace sead
{
CafeFSAFileDevice::CafeFSAFileDevice(const SafeString& name, const SafeString& devicePath)
: FileDevice(name), devicePath(devicePath.cstr()), status(FS_STATUS_OK),
openErrHandling(FS_RET_PERMISSION_ERROR | FS_RET_ACCESS_ERROR | FS_RET_NOT_FILE |
FS_RET_NOT_FOUND | FS_RET_ALREADY_OPEN),
closeErrHandling(FS_RET_NO_ERROR), readErrHandling(FS_RET_NO_ERROR), client(NULL)
{
}
bool CafeFSAFileDevice::doIsAvailable_() const
{
return true;
}
FileDevice* CafeFSAFileDevice::doOpen_(FileHandle* handle, const SafeString& path,
FileDevice::FileOpenFlag flag)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSFileHandle* fsHandle = getFileHandleInner_(handle);
char* mode;
switch (flag)
{
case FileDevice::cFileOpenFlag_ReadOnly:
mode = "r";
break;
case FileDevice::cFileOpenFlag_WriteOnly:
mode = "w";
break;
case FileDevice::cFileOpenFlag_ReadWrite:
mode = "r+";
break;
case FileDevice::cFileOpenFlag_Create:
mode = "w+";
break;
default:
mode = "r";
}
FixedSafeString<FS_MAX_ENTNAME_SIZE> fullPath;
formatPathForFSA_(&fullPath, path);
FSStatus status = FSOpenFile(client_, &block, fullPath.cstr(), mode, fsHandle, openErrHandling);
fsHandle[1] = 0;
if (this->status = status, status != FS_STATUS_OK)
{
fsHandle[0] = 0;
return NULL;
}
return this;
}
bool CafeFSAFileDevice::doClose_(FileHandle* handle)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSFileHandle* fsHandle = getFileHandleInner_(handle);
return FSCloseFile(client_, &block, *fsHandle, closeErrHandling) == FS_STATUS_OK;
}
bool CafeFSAFileDevice::doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSFileHandle* fsHandle = getFileHandleInner_(handle);
s32 status = FSReadFile(client_, &block, outBuffer, sizeof(u8), bytesToRead, *fsHandle, 0,
readErrHandling);
if (status >= 0)
{
this->status = FS_STATUS_OK;
fsHandle[1] += status;
if (bytesRead != NULL)
*bytesRead = status;
return true;
}
this->status = status;
return false;
}
bool CafeFSAFileDevice::doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer,
u32 bytesToWrite)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSFileHandle* fsHandle = getFileHandleInner_(handle);
s32 status = FSWriteFile(client_, &block, inBuffer, sizeof(const u8), bytesToWrite, *fsHandle,
0, FS_RET_STORAGE_FULL | FS_RET_FILE_TOO_BIG);
if (status >= 0)
{
this->status = FS_STATUS_OK;
fsHandle[1] += status;
if (bytesWritten != NULL)
*bytesWritten = status;
return true;
}
this->status = status;
return false;
}
bool CafeFSAFileDevice::doSeek_(FileHandle* handle, s32 offset, FileDevice::SeekOrigin origin)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSFileHandle* fsHandle = getFileHandleInner_(handle);
if (origin != FileDevice::cSeekOrigin_Begin)
{
if (origin == FileDevice::cSeekOrigin_Current)
offset += fsHandle[1];
else if (origin != FileDevice::cSeekOrigin_End)
return false;
else
{
u32 fileSize = 0;
if (!doGetFileSize_(&fileSize, handle))
return false;
offset += fileSize;
}
}
FSStatus status = FSSetPosFile(client_, &block, *fsHandle, offset, FS_RET_NO_ERROR);
if (this->status = status, status != FS_STATUS_OK)
return false;
fsHandle[1] = offset;
return true;
}
bool CafeFSAFileDevice::doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle)
{
*seekPos = getFileHandleInner_(handle)[1];
return true;
}
bool CafeFSAFileDevice::doGetFileSize_(u32* fileSize, const SafeString& path)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSStat stat;
FixedSafeString<FS_MAX_ENTNAME_SIZE> fullPath;
formatPathForFSA_(&fullPath, path);
FSStatus status = FSGetStat(client_, &block, fullPath.cstr(), &stat, FS_RET_NO_ERROR);
if (this->status = status, status != FS_STATUS_OK)
return false;
*fileSize = stat.size;
return true;
}
bool CafeFSAFileDevice::doGetFileSize_(u32* fileSize, FileHandle* handle)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSFileHandle* fsHandle = getFileHandleInner_(handle);
FSStat stat;
FSStatus status = FSGetStatFile(client_, &block, *fsHandle, &stat, FS_RET_NO_ERROR);
if (this->status = status, status != FS_STATUS_OK)
return false;
*fileSize = stat.size;
return true;
}
bool CafeFSAFileDevice::doIsExistFile_(bool* exists, const SafeString& path)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSStat stat;
FixedSafeString<FS_MAX_ENTNAME_SIZE> fullPath;
formatPathForFSA_(&fullPath, path);
FSStatus status = FSGetStat(client_, &block, fullPath.cstr(), &stat,
FS_RET_PERMISSION_ERROR | FS_RET_NOT_FOUND);
if (this->status = status, status != FS_STATUS_OK)
{
if (status != FS_STATUS_NOT_FOUND)
return false;
*exists = false;
}
else
*exists = (stat.flag & (FS_STAT_FLAG_IS_DIRECTORY | FS_STAT_FLAG_IS_QUOTA)) != 0;
return true;
}
bool CafeFSAFileDevice::doIsExistDirectory_(bool* exists, const SafeString& path)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSStat stat;
FixedSafeString<FS_MAX_ENTNAME_SIZE> fullPath;
formatPathForFSA_(&fullPath, path);
FSStatus status = FSGetStat(client_, &block, fullPath.cstr(), &stat,
FS_RET_PERMISSION_ERROR | FS_RET_NOT_FOUND);
if (this->status = status, status != FS_STATUS_OK)
{
if (status != FS_STATUS_NOT_FOUND)
return false;
*exists = false;
}
else
*exists = (stat.flag & FS_STAT_FLAG_IS_DIRECTORY) != 0;
return true;
}
FileDevice* CafeFSAFileDevice::doOpenDirectory_(DirectoryHandle* handle, const SafeString& path)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSDirHandle* fsHandle = getDirHandleInner_(handle);
FixedSafeString<FS_MAX_ENTNAME_SIZE> fullPath;
formatPathForFSA_(&fullPath, path);
FSStatus status = FSOpenDir(client_, &block, fullPath.cstr(), fsHandle,
FS_RET_PERMISSION_ERROR | FS_RET_ACCESS_ERROR | FS_RET_NOT_DIR |
FS_RET_NOT_FOUND | FS_RET_ALREADY_OPEN);
if (this->status = status, status != FS_STATUS_OK)
return NULL;
return this;
}
bool CafeFSAFileDevice::doCloseDirectory_(DirectoryHandle* handle)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSDirHandle* fsHandle = getDirHandleInner_(handle);
return (status = FSCloseDir(client_, &block, *fsHandle, FS_RET_NO_ERROR),
status == FS_STATUS_OK);
}
bool CafeFSAFileDevice::doReadDirectory_(u32* entriesRead, DirectoryHandle* handle,
DirectoryEntry* entries, u32 entriesToRead)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FSDirHandle* fsHandle = getDirHandleInner_(handle);
for (s32 i = 0; i < entriesToRead; i++)
{
FSDirEntry dirEntry;
status = FSReadDir(client_, &block, *fsHandle, &dirEntry, FS_RET_NO_ERROR);
if (status != FS_STATUS_OK)
{
if (entriesRead != NULL)
*entriesRead = i;
if (status == FS_STATUS_END)
return true;
return false;
}
SafeString name(dirEntry.name);
entries[i].name.copy(name);
entries[i].is_directory = (dirEntry.stat.flag & FS_STAT_FLAG_IS_DIRECTORY) != 0;
}
if (entriesRead != NULL)
*entriesRead = entriesToRead;
return true;
}
bool CafeFSAFileDevice::doMakeDirectory_(const SafeString& path, u32)
{
FSCmdBlock block;
FSInitCmdBlock(&block);
FSClient* client_ = getUsableFSClient_();
FixedSafeString<FS_MAX_ENTNAME_SIZE> fullPath;
formatPathForFSA_(&fullPath, path);
return (status = FSMakeDir(client_, &block, fullPath.cstr(),
FS_RET_JOURNAL_FULL | FS_RET_STORAGE_FULL | FS_RET_PERMISSION_ERROR |
FS_RET_NOT_FOUND),
status == FS_STATUS_OK);
}
s32 CafeFSAFileDevice::doGetLastRawError_() const
{
return status;
}
void CafeFSAFileDevice::doResolvePath_(BufferedSafeString* out, const SafeString& path) const
{
formatPathForFSA_(out, path);
}
void CafeFSAFileDevice::formatPathForFSA_(BufferedSafeString* out, const SafeString& path) const
{
out->format("%s/%s", devicePath, path.cstr());
}
FSClient* CafeFSAFileDevice::getUsableFSClient_() const
{
if (client == NULL)
return &FileDeviceMgr::instance()->client;
return client;
}
FSFileHandle* CafeFSAFileDevice::getFileHandleInner_(FileHandle* handle)
{
return reinterpret_cast<FSFileHandle*>(getHandleBaseHandleBuffer_(handle));
}
FSDirHandle* CafeFSAFileDevice::getDirHandleInner_(DirectoryHandle* handle)
{
return reinterpret_cast<FSDirHandle*>(getHandleBaseHandleBuffer_(handle));
}
CafeContentFileDevice::CafeContentFileDevice() : CafeFSAFileDevice("content", FS_CONTENT_DIR) {}
} // namespace sead
@@ -0,0 +1,6 @@
#include "filedevice/nin/seadNinAocFileDeviceNin.h"
namespace sead
{
NinAocFileDevice::NinAocFileDevice(const SafeString& mount) : NinFileDeviceBase("aoc", mount) {}
} // namespace sead
@@ -0,0 +1,6 @@
#include "filedevice/nin/seadNinContentFileDeviceNin.h"
namespace sead
{
NinContentFileDevice::NinContentFileDevice() : NinFileDeviceBase("content", "content") {}
} // namespace sead
@@ -0,0 +1,469 @@
#include "filedevice/nin/seadNinFileDeviceBaseNin.h"
#include "filedevice/seadPath.h"
namespace sead
{
struct NinFileDeviceBase::FileHandleInner
{
nn::fs::FileHandle mHandle;
s64 mOffset;
bool mIsWriteMode;
bool mDoNotFlushOnClose;
};
struct NinFileDeviceBase::DirectoryHandleInner
{
nn::fs::DirectoryHandle mHandle;
};
NinFileDeviceBase::NinFileDeviceBase(const SafeString& name, const SafeString& mount_point)
: FileDevice(name), mMountPoint(mount_point)
{
}
bool NinFileDeviceBase::doIsAvailable_() const
{
return true;
}
// NON_MATCHING: inverted branching for should_set_size
FileDevice* NinFileDeviceBase::doOpen_(FileHandle* handle, const SafeString& path,
FileDevice::FileOpenFlag flag)
{
static constexpr u32 sModes[4] = {
nn::fs::OpenMode_Read,
nn::fs::OpenMode_Write | nn::fs::OpenMode_Append,
nn::fs::OpenMode_ReadWrite | nn::fs::OpenMode_Append,
nn::fs::OpenMode_Write | nn::fs::OpenMode_Append,
};
const u32 mode = flag <= 3u ? sModes[s32(flag)] : u32(nn::fs::OpenMode_Read);
FixedSafeString<256> fs_path;
if (!formatPathForFS_(&fs_path, path))
{
mLastError = nn::fs::ResultUnexpected();
SEAD_WARN("invalid path. path = %s", fs_path.cstr());
return nullptr;
}
bool should_set_size = true;
if ((flag | cFileOpenFlag_ReadWrite) == cFileOpenFlag_Create)
{
bool is_file = false;
nn::fs::DirectoryEntryType type;
const auto result = nn::fs::GetEntryType(&type, fs_path.cstr());
if (result.IsSuccess())
{
is_file = type == nn::fs::DirectoryEntryType_File;
}
else if (!nn::fs::ResultPathNotFound().Includes(result))
{
SEAD_WARN("nn::fs::GetEntryType failed. module = %d desc = %d inner_value = 0x%08x "
"path = %s",
result.GetModule(), result.GetDescription(), result.GetInnerValueForDebug(),
fs_path.cstr());
mLastError = result;
return nullptr;
}
should_set_size = flag == cFileOpenFlag_Create || !is_file;
if (flag == cFileOpenFlag_Create || !is_file)
{
if (is_file)
{
mLastError = nn::fs::ResultPathAlreadyExists();
return nullptr;
}
const auto create_result = nn::fs::CreateFile(fs_path.cstr(), 0);
if (create_result.IsFailure())
{
SEAD_WARN("nn::fs::CreateFile failed. module = %d desc = %d inner_value = 0x%08x "
"path = %s",
create_result.GetModule(), create_result.GetDescription(),
create_result.GetInnerValueForDebug(), fs_path.cstr());
mLastError = create_result;
return nullptr;
}
}
}
auto* handle_inner = getFileHandleInner_(handle, true);
handle_inner->mOffset = 0;
handle_inner->mIsWriteMode = (mode >> 1) & 1;
handle_inner->mDoNotFlushOnClose = false;
const auto open_result = nn::fs::OpenFile(&handle_inner->mHandle, fs_path.cstr(), mode);
mLastError = open_result;
if (open_result.IsFailure())
{
if (!nn::fs::ResultPathNotFound().Includes(open_result))
SEAD_WARN(
"nn::fs::OpenFile failed. module = %d desc = %d inner_value = 0x%08x path = %s",
open_result.GetModule(), open_result.GetDescription(),
open_result.GetInnerValueForDebug(), fs_path.cstr());
return nullptr;
}
if (flag == cFileOpenFlag_WriteOnly && !should_set_size)
{
const auto set_result = nn::fs::SetFileSize(handle_inner->mHandle, 0);
if (set_result.IsFailure())
{
SEAD_WARN("nn::fs::SetFileSize failed. module = %d desc = %d inner_value = 0x%08x path "
"= %s",
set_result.GetModule(), set_result.GetDescription(),
set_result.GetInnerValueForDebug(), fs_path.cstr());
nn::fs::CloseFile(handle_inner->mHandle);
mLastError = set_result;
return nullptr;
}
}
return this;
}
bool NinFileDeviceBase::doClose_(FileHandle* handle)
{
const auto* inner = getFileHandleInner_(handle);
if (inner->mIsWriteMode && !inner->mDoNotFlushOnClose)
{
const auto result = nn::fs::FlushFile(inner->mHandle);
if (result.IsFailure())
{
mLastError = result;
nn::fs::CloseFile(inner->mHandle);
return false;
}
}
nn::fs::CloseFile(inner->mHandle);
mLastError = nn::ResultSuccess();
return true;
}
bool NinFileDeviceBase::doFlush_(FileHandle* handle)
{
auto* inner = getFileHandleInner_(handle);
mLastError = nn::fs::FlushFile(inner->mHandle);
if (mLastError.IsFailure())
{
inner->mDoNotFlushOnClose = true;
return false;
}
return true;
}
bool NinFileDeviceBase::doRemove_(const SafeString& path)
{
FixedSafeString<256> fs_path;
if (!formatPathForFS_(&fs_path, path))
{
mLastError = nn::fs::ResultUnexpected();
SEAD_WARN("invalid path. path = %s.", path.cstr());
return false;
}
mLastError = nn::fs::DeleteFile(fs_path.cstr());
if (mLastError.IsFailure())
{
SEAD_WARN("nn::fs::DeleteFile failed. module = %d desc = %d inner_value = 0x%08x path = %s",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug(), fs_path.cstr());
return false;
}
return true;
}
bool NinFileDeviceBase::doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead)
{
auto* inner = getFileHandleInner_(handle);
u64 out_size = 0;
mLastError = nn::fs::ReadFile(&out_size, inner->mHandle, inner->mOffset, outBuffer, bytesToRead,
nn::fs::ReadOption{});
if (mLastError.IsFailure())
{
SEAD_WARN("nn::fs::ReadFile failed. module = %d desc = %d inner_value = 0x%08x",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug());
return false;
}
inner->mOffset += out_size;
if (bytesRead)
*bytesRead = out_size;
return true;
}
bool NinFileDeviceBase::doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer,
u32 bytesToWrite)
{
auto* inner = getFileHandleInner_(handle);
mLastError = nn::fs::WriteFile(inner->mHandle, inner->mOffset, inBuffer, bytesToWrite,
nn::fs::WriteOption{});
if (mLastError.IsSuccess())
{
inner->mOffset += bytesToWrite;
if (bytesWritten)
*bytesWritten = bytesToWrite;
return true;
}
SEAD_WARN("nn::fs::WriteFile failed. module = %d desc = %d inner_value = 0x%08x",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug());
inner->mDoNotFlushOnClose = true;
return false;
}
bool NinFileDeviceBase::doSeek_(FileHandle* handle, s32 offset, FileDevice::SeekOrigin origin)
{
auto* inner = getFileHandleInner_(handle);
switch (origin)
{
case FileDevice::cSeekOrigin_Begin:
inner->mOffset = offset;
return true;
case FileDevice::cSeekOrigin_Current:
inner->mOffset += offset;
return true;
case FileDevice::cSeekOrigin_End:
{
SEAD_ASSERT(offset <= 0);
u32 file_size = 0;
if (!doGetFileSize_(&file_size, handle))
break;
inner->mOffset = file_size + offset;
return true;
}
}
return false;
}
bool NinFileDeviceBase::doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle)
{
*seekPos = getFileHandleInner_(handle)->mOffset;
return true;
}
bool NinFileDeviceBase::doGetFileSize_(u32* fileSize, const SafeString& path)
{
FileHandle handle;
if (!doOpen_(&handle, path, cFileOpenFlag_ReadOnly))
return false;
const bool ret = doGetFileSize_(fileSize, &handle);
doClose_(&handle);
return ret;
}
bool NinFileDeviceBase::doGetFileSize_(u32* fileSize, FileHandle* handle)
{
const auto* inner = getFileHandleInner_(handle);
s64 size = 0;
mLastError = nn::fs::GetFileSize(&size, inner->mHandle);
if (mLastError.IsSuccess())
{
*fileSize = size;
return true;
}
SEAD_WARN("nn::fs::GetFileSize failed. module = %d desc = %d inner_value = 0x%08x",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug());
return false;
}
bool NinFileDeviceBase::doIsExistFile_(bool* exists, const SafeString& path)
{
FixedSafeString<256> fs_path;
if (!formatPathForFS_(&fs_path, path))
{
mLastError = nn::fs::ResultUnexpected();
SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
return false;
}
nn::fs::DirectoryEntryType type;
mLastError = nn::fs::GetEntryType(&type, fs_path.cstr());
if (mLastError.IsSuccess())
{
*exists = type == nn::fs::DirectoryEntryType_File;
return true;
}
if (nn::fs::ResultPathNotFound().Includes(mLastError))
{
*exists = false;
return true;
}
SEAD_WARN("nn::fs::GetEntryType failed. module = %d desc = %d inner_value = 0x%08x path = %s",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug(), fs_path.cstr());
return false;
}
bool NinFileDeviceBase::doIsExistDirectory_(bool* exists, const SafeString& path)
{
FixedSafeString<256> fs_path;
if (!formatPathForFS_(&fs_path, path))
{
mLastError = nn::fs::ResultUnexpected();
SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
return false;
}
nn::fs::DirectoryEntryType type;
mLastError = nn::fs::GetEntryType(&type, fs_path.cstr());
if (mLastError.IsSuccess())
{
*exists = type == nn::fs::DirectoryEntryType_Directory;
return true;
}
if (nn::fs::ResultPathNotFound().Includes(mLastError))
{
*exists = false;
return true;
}
SEAD_WARN("nn::fs::GetEntryType failed. module = %d desc = %d inner_value = 0x%08x path = %s",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug(), fs_path.cstr());
return false;
}
FileDevice* NinFileDeviceBase::doOpenDirectory_(DirectoryHandle* handle, const SafeString& path)
{
auto* inner = getDirectoryHandleInner_(handle, true);
FixedSafeString<256> fs_path;
if (!formatPathForFS_(&fs_path, path))
{
mLastError = nn::fs::ResultUnexpected();
SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
return nullptr;
}
mLastError =
nn::fs::OpenDirectory(&inner->mHandle, fs_path.cstr(), nn::fs::OpenDirectoryMode_All);
if (mLastError.IsSuccess())
return this;
if (nn::fs::ResultPathNotFound().Includes(mLastError))
return nullptr;
SEAD_WARN("nn::fs::OpenDirectory failed. module = %d desc = %d inner_value = 0x%08x path = %s",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug(), fs_path.cstr());
return nullptr;
}
bool NinFileDeviceBase::doCloseDirectory_(DirectoryHandle* handle)
{
nn::fs::CloseDirectory(getDirectoryHandleInner_(handle)->mHandle);
return true;
}
bool NinFileDeviceBase::doReadDirectory_(u32* entries_read, DirectoryHandle* handle,
DirectoryEntry* entries, u32 num_entries)
{
const auto* inner = getDirectoryHandleInner_(handle);
for (u32 i = 0; i < num_entries; ++i)
{
nn::fs::DirectoryEntry entry;
s64 count = 0;
mLastError = nn::fs::ReadDirectory(&count, &entry, inner->mHandle, 1);
if (mLastError.IsFailure())
{
SEAD_WARN("nn::fs::ReadDirectory failed. module = %d desc = %d inner_value = 0x%08x",
mLastError.GetModule(), mLastError.GetDescription(),
mLastError.GetInnerValueForDebug());
return false;
}
// No more entries to read.
if (count != 1)
{
if (entries_read)
*entries_read = i;
return true;
}
entries[i].name = entry.name;
entries[i].is_directory = entry.type == nn::fs::DirectoryEntryType_Directory;
}
if (entries_read)
*entries_read = num_entries;
return true;
}
bool NinFileDeviceBase::doMakeDirectory_(const SafeString& path, u32)
{
FixedSafeString<256> fs_path;
if (!formatPathForFS_(&fs_path, path))
{
mLastError = nn::fs::ResultUnexpected();
SEAD_WARN("invalid path. path = %s.", fs_path.cstr());
return false;
}
const auto result = nn::fs::CreateDirectory(fs_path.cstr());
mLastError = result;
if (result.IsSuccess())
return true;
SEAD_WARN("nn::fs::CreateDirectory[%s] failed. module = %d desc = %d inner_value = 0x%08x",
fs_path.cstr(), result.GetModule(), result.GetDescription(),
result.GetInnerValueForDebug());
return false;
}
s32 NinFileDeviceBase::doGetLastRawError_() const
{
return mLastError.GetInnerValueForDebug();
}
void NinFileDeviceBase::doResolvePath_(BufferedSafeString* out, const SafeString& path) const
{
formatPathForFS_(out, path);
}
bool NinFileDeviceBase::formatPathForFS_(BufferedSafeString* out, const SafeString& path) const
{
out->format("%s:/%s", mMountPoint.cstr(), path.cstr());
Path::changeDelimiter(out, '/');
return true;
}
NinFileDeviceBase::FileHandleInner* NinFileDeviceBase::getFileHandleInner_(HandleBase* handle,
bool construct) const
{
auto* buffer = getHandleBaseHandleBuffer_(handle).getBufferPtr();
static_assert(sizeof(FileHandleInner) <= sizeof(HandleBuffer));
static_assert(alignof(FileHandleInner) <= alignof(HandleBase));
if (construct)
return new (buffer) FileHandleInner;
return reinterpret_cast<FileHandleInner*>(buffer);
}
NinFileDeviceBase::DirectoryHandleInner*
NinFileDeviceBase::getDirectoryHandleInner_(HandleBase* handle, bool construct) const
{
auto* buffer = getHandleBaseHandleBuffer_(handle).getBufferPtr();
static_assert(sizeof(DirectoryHandleInner) <= sizeof(HandleBuffer));
static_assert(alignof(DirectoryHandleInner) <= alignof(HandleBase));
if (construct)
return new (buffer) DirectoryHandleInner;
return reinterpret_cast<DirectoryHandleInner*>(buffer);
}
} // namespace sead
@@ -0,0 +1,25 @@
#include "filedevice/nin/seadNinHostIOFileDevice.h"
#include "devenv/seadEnvUtil.h"
#include "filedevice/seadFileDeviceMgr.h"
namespace sead
{
NinHostIOFileDevice::NinHostIOFileDevice() : NinFileDeviceBase("hostio", "hostio") {}
bool NinHostIOFileDevice::doIsAvailable_() const
{
#ifndef SEAD_DEBUG
return false;
#endif
return FileDeviceMgr::instance()->hasMountedHost();
}
bool NinHostIOFileDevice::formatPathForFS_(BufferedSafeString* out, const SafeString& path) const
{
#ifndef SEAD_DEBUG
return false;
#endif
EnvUtil::resolveEnvronmentVariable(out, path);
return out->include(":") && isAvailable();
}
} // namespace sead
@@ -0,0 +1,12 @@
#include "filedevice/nin/seadNinSDFileDeviceNin.h"
#include "filedevice/seadFileDeviceMgr.h"
namespace sead
{
NinSDFileDevice::NinSDFileDevice() : NinFileDeviceBase("sd", "sd") {}
bool NinSDFileDevice::doIsAvailable_() const
{
return FileDeviceMgr::instance()->hasMountedSd();
}
} // namespace sead
@@ -0,0 +1,21 @@
#include "filedevice/nin/seadNinSaveFileDeviceNin.h"
namespace sead
{
NinSaveFileDevice::NinSaveFileDevice(const SafeString& mount) : NinFileDeviceBase("save", mount) {}
bool NinSaveFileDevice::tryCommit()
{
const auto result = nn::fs::CommitSaveData(mMountPoint.cstr());
mLastError = result;
if (result.IsSuccess())
return true;
SEAD_WARN(
"nn::fs::CommitSaveData failed. module = %d desc = %d inner_value = 0x%08x mount_name=%s",
result.GetModule(), result.GetDescription(), result.GetInnerValueForDebug(),
mMountPoint.cstr());
return false;
}
} // namespace sead
@@ -0,0 +1,480 @@
#include "filedevice/seadArchiveFileDevice.h"
#include "basis/seadRawPrint.h"
#include "math/seadMathCalcCommon.h"
#include "prim/seadPtrUtil.h"
#include "resource/seadArchiveRes.h"
namespace sead
{
struct ArchiveFileDevice::ArchiveFileHandle
{
const u8* mFileData;
ArchiveRes::FileInfo mFileInfo;
u32 mPos;
};
ArchiveFileDevice::ArchiveFileDevice(ArchiveRes* res) : FileDevice("arc"), mArchive(res) {}
u8* ArchiveFileDevice::tryLoadWithEntryID(s32 id, FileDevice::LoadArg& arg)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return nullptr;
return doLoadWithEntryID_(id, arg);
}
FileDevice* ArchiveFileDevice::tryOpenWithEntryID(FileHandle* handle, s32 id,
FileDevice::FileOpenFlag flag, u32 div_size)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return nullptr;
setFileHandleDivSize_(handle, div_size);
FileDevice* ret = doOpenWithEntryID_(handle, id, flag);
setHandleBaseFileDevice_(handle, ret);
return ret;
}
s32 ArchiveFileDevice::tryConvertPathToEntryID(const SafeString& path)
{
return doConvertPathToEntryID_(path);
}
bool ArchiveFileDevice::setCurrentDirectory(const SafeString& dir)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
return doSetCurrentDirectory_(dir);
}
bool ArchiveFileDevice::doGetFileSize_(u32* fileSize, const SafeString& path)
{
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
if (!path.cstr())
{
SEAD_ASSERT_MSG(false, "invalid path");
return false;
}
ArchiveRes::FileInfo info{};
if (!mArchive->getFile(path, &info))
return false;
*fileSize = info.mLength;
return true;
}
bool ArchiveFileDevice::doGetFileSize_(u32* fileSize, FileHandle* handle)
{
if (!handle)
{
SEAD_ASSERT_MSG(false, "invalid handle");
return false;
}
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
*fileSize = getArchiveFileHandle_(handle)->mFileInfo.mLength;
return true;
}
ArchiveFileDevice::ArchiveFileHandle*
ArchiveFileDevice::getArchiveFileHandle_(FileHandle* handle) const
{
return reinterpret_cast<ArchiveFileHandle*>(getHandleBaseHandleBuffer_(handle).getBufferPtr());
}
ArchiveFileDevice::ArchiveFileHandle*
ArchiveFileDevice::constructArchiveFileHandle_(FileHandle* handle) const
{
return new (getHandleBaseHandleBuffer_(handle).getBufferPtr()) ArchiveFileHandle;
}
bool ArchiveFileDevice::doIsExistFile_(bool* exists, const SafeString& path)
{
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
if (!path.cstr())
{
SEAD_ASSERT_MSG(false, "invalid path");
return false;
}
*exists = mArchive->isExistFile(path);
return true;
}
bool ArchiveFileDevice::doIsExistDirectory_(bool* exists, const SafeString& path)
{
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
if (!path.cstr())
{
SEAD_ASSERT_MSG(false, "invalid path");
return false;
}
*exists = false;
return true;
}
u8* ArchiveFileDevice::doLoadWithEntryID_(s32 entry_id, LoadArg& arg)
{
if (entry_id == -1)
{
SEAD_ASSERT_MSG(false, "Invalid entry_id");
return nullptr;
}
if (arg.buffer_size_alignment % 32 != 0)
{
SEAD_ASSERT_MSG(false, "arg.buffer_size_alignment[%u] is not multipe of 32",
arg.buffer_size_alignment);
return nullptr;
}
if (arg.buffer || arg.heap)
{
FileHandle handle;
if (!tryOpenWithEntryID(&handle, entry_id, {}, arg.div_size))
return nullptr;
// Determine the buffer size.
u32 buffer_size = arg.buffer_size;
if (buffer_size == 0)
{
u32 file_size = 0;
if (!tryGetFileSize(&file_size, &handle))
return nullptr;
SEAD_ASSERT(file_size != 0);
if (arg.buffer_size_alignment)
buffer_size = Mathu::roundUp(file_size, arg.buffer_size_alignment);
else
buffer_size = Mathi::roundUpPow2(file_size, cBufferMinAlignment);
}
// Allocate the buffer if need be.
u8* buffer = arg.buffer;
bool buffer_allocated = false;
if (!buffer)
{
const s32 aligment_sign = Mathi::sign(arg.alignment);
const s32 alignment = std::max(Mathi::abs(arg.alignment), 32);
buffer = new (arg.heap, alignment * aligment_sign) u8[buffer_size];
buffer_allocated = true;
}
u32 bytes_read = 0;
if (!tryRead(&bytes_read, &handle, buffer, buffer_size) || !tryClose(&handle))
{
// Clean up the allocation on failure.
if (buffer && buffer_allocated)
delete[] buffer;
return nullptr;
}
arg.read_size = bytes_read;
arg.need_unload = buffer_allocated;
arg.roundup_size = buffer_size;
return buffer;
}
ArchiveRes::FileInfo info{};
auto* ret = mArchive->getFileFast(entry_id, &info);
if (!ret)
return nullptr;
SEAD_ASSERT(arg.alignment == 0 || PtrUtil::isAligned(ret, Mathi::abs(arg.alignment)));
if (arg.buffer_size_alignment && info.mLength % arg.buffer_size_alignment != 0)
{
SEAD_WARN("archive file size[%u] is not multipe of arg.buffer_size_alignment[%u]",
info.mLength, arg.buffer_size_alignment);
return nullptr;
}
arg.read_size = info.mLength;
arg.roundup_size = info.mLength;
arg.need_unload = false;
return const_cast<u8*>(static_cast<const u8*>(ret));
}
u8* ArchiveFileDevice::doLoad_(LoadArg& arg)
{
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return nullptr;
}
if (arg.buffer || arg.heap)
return FileDevice::doLoad_(arg);
ArchiveRes::FileInfo info{};
auto* ret = mArchive->getFile(arg.path, &info);
if (!ret)
return nullptr;
SEAD_ASSERT(arg.alignment == 0 || PtrUtil::isAligned(ret, Mathi::abs(arg.alignment)));
if (arg.buffer_size_alignment && info.mLength % arg.buffer_size_alignment != 0)
{
SEAD_WARN("archive file size[%u] is not multipe of arg.buffer_size_alignment[%u]",
info.mLength, arg.buffer_size_alignment);
return nullptr;
}
arg.read_size = info.mLength;
arg.roundup_size = info.mLength;
arg.need_unload = false;
return const_cast<u8*>(static_cast<const u8*>(ret));
}
FileDevice* ArchiveFileDevice::doOpen_(FileHandle* handle, const SafeString& path,
FileDevice::FileOpenFlag)
{
if (!handle)
{
SEAD_ASSERT_MSG(false, "invalid handle");
return nullptr;
}
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return nullptr;
}
if (!path.cstr())
{
SEAD_ASSERT_MSG(false, "invalid filename");
return nullptr;
}
auto* inner = constructArchiveFileHandle_(handle);
auto* file_data = static_cast<const u8*>(mArchive->getFile(path, &inner->mFileInfo));
if (!file_data)
return nullptr;
inner->mFileData = file_data;
inner->mPos = 0;
return this;
}
FileDevice* ArchiveFileDevice::doOpenWithEntryID_(FileHandle* handle, s32 id,
FileDevice::FileOpenFlag)
{
if (!handle)
{
SEAD_ASSERT_MSG(false, "invalid handle");
return nullptr;
}
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return nullptr;
}
auto* inner = constructArchiveFileHandle_(handle);
auto* file_data = static_cast<const u8*>(mArchive->getFileFast(id, &inner->mFileInfo));
if (!file_data)
return nullptr;
inner->mFileData = file_data;
inner->mPos = 0;
return this;
}
s32 ArchiveFileDevice::doConvertPathToEntryID_(const SafeString& path)
{
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return 0;
}
return mArchive->convertPathToEntryID(path);
}
bool ArchiveFileDevice::doClose_(FileHandle*)
{
return true;
}
bool ArchiveFileDevice::doFlush_(FileHandle*)
{
SEAD_ASSERT_MSG(false, "not supported");
return false;
}
bool ArchiveFileDevice::doRemove_(const SafeString&)
{
SEAD_ASSERT_MSG(false, "not supported");
return false;
}
bool ArchiveFileDevice::doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead)
{
ArchiveFileHandle* inner = getArchiveFileHandle_(handle);
u32 read_size;
if (inner->mPos + bytesToRead <= inner->mFileInfo.mLength)
read_size = bytesToRead;
else
read_size = inner->mFileInfo.mLength - inner->mPos;
MemUtil::copy(outBuffer, inner->mFileData + inner->mPos, read_size);
inner->mPos += read_size;
if (bytesRead)
*bytesRead = read_size;
return true;
}
bool ArchiveFileDevice::doSeek_(FileHandle* handle, s32 offset, FileDevice::SeekOrigin origin)
{
ArchiveFileHandle* inner = getArchiveFileHandle_(handle);
u32 new_position;
switch (origin)
{
case cSeekOrigin_Begin:
new_position = offset;
break;
case cSeekOrigin_Current:
new_position = inner->mPos + offset;
break;
case cSeekOrigin_End:
new_position = inner->mFileInfo.mLength + offset;
break;
default:
SEAD_ASSERT_MSG(false, "Unexpected origin");
return false;
}
if (new_position > inner->mFileInfo.mLength)
return false;
inner->mPos = new_position;
return true;
}
bool ArchiveFileDevice::doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle)
{
if (!handle)
{
SEAD_ASSERT_MSG(false, "invalid handle");
return false;
}
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
ArchiveFileHandle* inner = getArchiveFileHandle_(handle);
*seekPos = inner->mPos;
return true;
}
FileDevice* ArchiveFileDevice::doOpenDirectory_(DirectoryHandle* handle, const SafeString& path)
{
if (!handle)
{
SEAD_ASSERT_MSG(false, "invalid handle");
return nullptr;
}
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return nullptr;
}
if (!mArchive->openDirectory(&getHandleBaseHandleBuffer_(handle), path))
return nullptr;
return this;
}
bool ArchiveFileDevice::doCloseDirectory_(DirectoryHandle* handle)
{
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
return mArchive->closeDirectory(&getHandleBaseHandleBuffer_(handle));
}
bool ArchiveFileDevice::doReadDirectory_(u32* entriesRead, DirectoryHandle* handle,
DirectoryEntry* entry, u32 entriesToRead)
{
auto* archive = mArchive;
if (!archive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
auto* buffer = &getHandleBaseHandleBuffer_(handle);
SEAD_ASSERT(entry);
const u32 actual_read_count = archive->readDirectory(buffer, entry, entriesToRead);
if (entriesRead)
*entriesRead = actual_read_count;
return true;
}
bool ArchiveFileDevice::doSetCurrentDirectory_(const SafeString& path)
{
if (!mArchive)
{
SEAD_ASSERT_MSG(false, "no archive mounted");
return false;
}
if (!path.cstr())
{
SEAD_ASSERT_MSG(false, "invalid filename");
return false;
}
return mArchive->setCurrentDirectory(path);
}
bool ArchiveFileDevice::doMakeDirectory_(const SafeString&, u32)
{
return false;
}
s32 ArchiveFileDevice::doGetLastRawError_() const
{
SEAD_ASSERT_MSG(false, "not impremented");
return 0;
}
} // namespace sead
@@ -0,0 +1,859 @@
#include <basis/seadNew.h>
#include <basis/seadRawPrint.h>
#include <filedevice/seadFileDevice.h>
#include <filedevice/seadFileDeviceMgr.h>
#include <filedevice/seadPath.h>
#include <heap/seadHeapMgr.h>
#include <math/seadMathCalcCommon.h>
namespace sead
{
bool FileHandle::close()
{
if (!mOriginalDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mOriginalDevice->close(this);
}
bool FileHandle::tryClose()
{
if (!mOriginalDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mOriginalDevice->tryClose(this);
}
bool FileHandle::flush()
{
if (!mOriginalDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mOriginalDevice->flush(this);
}
bool FileHandle::tryFlush()
{
if (!mOriginalDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mOriginalDevice->tryFlush(this);
}
u32 FileHandle::read(u8* outBuffer, u32 bytesToRead)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return 0;
}
return mDevice->read(this, outBuffer, bytesToRead);
}
bool FileHandle::tryRead(u32* actual_size, u8* data, u32 size)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->tryRead(actual_size, this, data, size);
}
u32 FileHandle::write(const u8* data, u32 size)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return 0;
}
return mDevice->write(this, data, size);
}
bool FileHandle::tryWrite(u32* actual_size, const u8* data, u32 size)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->tryWrite(actual_size, this, data, size);
}
bool FileHandle::seek(s32 offset, FileDevice::SeekOrigin origin)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->seek(this, offset, origin);
}
bool FileHandle::trySeek(s32 offset, FileDevice::SeekOrigin origin)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->trySeek(this, offset, origin);
}
u32 FileHandle::getCurrentSeekPos()
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return 0;
}
return mDevice->getCurrentSeekPos(this);
}
bool FileHandle::tryGetCurrentSeekPos(u32* pos)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->tryGetCurrentSeekPos(pos, this);
}
u32 FileHandle::getFileSize()
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return 0;
}
return mDevice->getFileSize(this);
}
bool FileHandle::tryGetFileSize(u32* size)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->tryGetFileSize(size, this);
}
bool DirectoryHandle::close()
{
if (!mOriginalDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mOriginalDevice->closeDirectory(this);
}
bool DirectoryHandle::tryClose()
{
if (!mOriginalDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mOriginalDevice->tryCloseDirectory(this);
}
u32 DirectoryHandle::read(DirectoryEntry* entries, u32 count)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->readDirectory(this, entries, count);
}
bool DirectoryHandle::tryRead(u32* actual_count, DirectoryEntry* entries, u32 count)
{
if (!mDevice)
{
SEAD_ASSERT_MSG(false, "handle not opened");
return false;
}
return mDevice->tryReadDirectory(actual_count, this, entries, count);
}
FileDevice::~FileDevice()
{
if (FileDeviceMgr::instance() != NULL)
FileDeviceMgr::instance()->unmount(this);
}
void FileDevice::traceFilePath(const SafeString& path) const
{
doTracePath_(path);
}
void FileDevice::traceDirectoryPath(const SafeString& path) const
{
doTracePath_(path);
}
void FileDevice::resolveFilePath(BufferedSafeString* out, const SafeString& path) const
{
doResolvePath_(out, path);
}
void FileDevice::resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const
{
doResolvePath_(out, path);
}
bool FileDevice::isMatchDevice_(const HandleBase* handle) const
{
return handle->mDevice == this;
}
u8* FileDevice::doLoad_(LoadArg& arg)
{
if (arg.buffer && arg.buffer_size == 0)
{
SEAD_WARN("arg.buffer is specified, but arg.buffer_size is zero");
return nullptr;
}
if (arg.buffer_size_alignment % cBufferMinAlignment != 0)
{
SEAD_WARN(
"arg.buffer_size_alignment[%u] is not multipe of FileDevice::cBufferMinAlignment[%u]",
arg.buffer_size_alignment, cBufferMinAlignment);
return nullptr;
}
FileHandle handle;
if (!tryOpen(&handle, arg.path, FileDevice::cFileOpenFlag_ReadOnly, arg.div_size))
return nullptr;
u32 bytesToRead = arg.buffer_size;
if (!arg.buffer || arg.check_read_entire_file)
{
u32 fileSize = 0;
if (!tryGetFileSize(&fileSize, &handle))
return nullptr;
if (fileSize == 0)
{
SEAD_WARN("file_size is zero.[%s]", arg.path.cstr());
return nullptr;
}
if (bytesToRead != 0)
{
if (bytesToRead < fileSize)
{
SEAD_WARN("arg.buffer_size[%u] is smaller than file size[%u]", bytesToRead,
fileSize);
return nullptr;
}
if (arg.buffer_size_alignment && bytesToRead % arg.buffer_size_alignment != 0)
{
SEAD_WARN("arg.buffer_size[%u] is not multipe of arg.buffer_size_alignment[%u]",
bytesToRead, arg.buffer_size_alignment);
return nullptr;
}
}
else
{
if (arg.buffer_size_alignment)
{
bytesToRead = Mathu::roundUp(fileSize, arg.buffer_size_alignment);
}
else
{
bytesToRead = Mathi::roundUpPow2(fileSize, FileDevice::cBufferMinAlignment);
}
}
}
u8* buf = arg.buffer;
bool allocated = false;
if (buf == nullptr)
{
const s32 sign = (arg.alignment < 0) ? -1 : 1;
s32 alignment = Mathi::abs(arg.alignment);
alignment = sign * ((alignment < cBufferMinAlignment) ? cBufferMinAlignment : alignment);
Heap* heap = arg.heap;
if (!heap)
heap = HeapMgr::instance()->getCurrentHeap();
void* raw_buf = heap->tryAlloc(bytesToRead, alignment);
if (!raw_buf)
{
if (arg.assert_on_alloc_fail)
{
SEAD_ASSERT_MSG(false, "alloc size[%u] failed in heap[%s] for file[%s]",
bytesToRead, heap->getName().cstr(), arg.path.cstr());
}
return nullptr;
}
buf = new (raw_buf) u8[bytesToRead];
allocated = true;
}
u32 bytesRead = 0;
if (!tryRead(&bytesRead, &handle, buf, bytesToRead))
{
if (allocated)
delete[] buf;
return nullptr;
}
if (!tryClose(&handle))
{
if (allocated)
delete[] buf;
return nullptr;
}
arg.read_size = bytesRead;
arg.roundup_size = bytesToRead;
arg.need_unload = allocated;
return buf;
}
bool FileDevice::doSave_(FileDevice::SaveArg& arg)
{
if (!arg.buffer)
{
SEAD_ASSERT_MSG(false, "arg.buffer must be set for save file[%s]", arg.path.cstr());
return false;
}
FileHandle handle;
if (!tryOpen(&handle, arg.path, cFileOpenFlag_WriteOnly))
return false;
const bool ret =
arg.buffer_size == 0 || tryWrite(&arg.write_size, &handle, arg.buffer, arg.buffer_size);
if (!tryClose(&handle))
return false;
return ret;
}
void FileDevice::doTracePath_(const SafeString& path) const
{
SEAD_DEBUG_PRINT("[%s] %s\n", mDriveName.cstr(), path.cstr());
FixedSafeString<512> out;
doResolvePath_(&out, path);
SEAD_DEBUG_PRINT(" -> %s\n", out.cstr());
}
void FileDevice::doResolvePath_(BufferedSafeString* out, const SafeString& path) const
{
out->copy(path);
}
bool FileDevice::isAvailable() const
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
return doIsAvailable_();
}
u8* FileDevice::tryLoad(LoadArg& arg)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return NULL;
return doLoad_(arg);
}
bool FileDevice::trySave(FileDevice::SaveArg& arg)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
return doSave_(arg);
}
FileDevice* FileDevice::tryOpen(FileHandle* handle, const SafeString& path, FileOpenFlag flag,
u32 divSize)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return nullptr;
if (handle == nullptr)
{
SEAD_ASSERT_MSG(false, "handle is null");
return nullptr;
}
setFileHandleDivSize_(handle, divSize);
FileDevice* device = doOpen_(handle, path, flag);
setHandleBaseFileDevice_(handle, device);
if (device)
setHandleBaseOriginalFileDevice_(handle, this);
return device;
}
bool FileDevice::tryClose(FileHandle* handle)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == nullptr)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
bool closed = doClose_(handle);
if (closed)
{
setHandleBaseFileDevice_(handle, nullptr);
setHandleBaseOriginalFileDevice_(handle, nullptr);
}
return closed;
}
bool FileDevice::tryFlush(FileHandle* handle)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (!handle)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
return doFlush_(handle);
}
bool FileDevice::tryRemove(const SafeString& str)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
return doRemove_(str);
}
bool FileDevice::tryRead(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == nullptr)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
if (outBuffer == nullptr)
{
SEAD_ASSERT_MSG(false, "buf is null");
return false;
}
if (handle->mDivSize == 0)
{
const bool ret = doRead_(bytesRead, handle, outBuffer, bytesToRead);
SEAD_ASSERT_MSG(!bytesRead || *bytesRead <= bytesToRead, "buffer overflow");
return ret;
}
u32 totalReadSize = 0;
do
{
u32 size =
(static_cast<s32>(bytesToRead) < handle->mDivSize) ? bytesToRead : handle->mDivSize;
u32 readSize = 0;
if (!doRead_(&readSize, handle, outBuffer, size))
{
if (bytesRead != NULL)
*bytesRead = totalReadSize;
return false;
}
totalReadSize += readSize;
if (readSize < size)
break;
outBuffer += readSize;
bytesToRead -= size;
} while (bytesToRead != 0);
if (bytesRead != NULL)
*bytesRead = totalReadSize;
return true;
}
bool FileDevice::tryWrite(u32* bytesWritten, FileHandle* handle, const u8* inBuffer,
u32 bytesToWrite)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == nullptr)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (inBuffer == nullptr)
{
SEAD_ASSERT_MSG(false, "buf is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
return doWrite_(bytesWritten, handle, inBuffer, bytesToWrite);
}
bool FileDevice::trySeek(FileHandle* handle, s32 offset, FileDevice::SeekOrigin origin)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == nullptr)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
return doSeek_(handle, offset, origin);
}
bool FileDevice::tryGetCurrentSeekPos(u32* seekPos, FileHandle* handle)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == NULL)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
if (seekPos == NULL)
{
SEAD_ASSERT_MSG(false, "pos is null");
return false;
}
return doGetCurrentSeekPos_(seekPos, handle);
}
bool FileDevice::tryGetFileSize(u32* fileSize, const SafeString& path)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (fileSize == NULL)
{
SEAD_ASSERT_MSG(false, "size is null");
return false;
}
return doGetFileSize_(fileSize, path);
}
bool FileDevice::tryGetFileSize(u32* size, FileHandle* handle)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == nullptr)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (size == nullptr)
{
SEAD_ASSERT_MSG(false, "size is null");
return false;
}
return doGetFileSize_(size, handle);
}
bool FileDevice::tryIsExistFile(bool* exists, const SafeString& path)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (exists == NULL)
{
SEAD_ASSERT_MSG(false, "is_exist is null");
return false;
}
return doIsExistFile_(exists, path);
}
bool FileDevice::tryIsExistDirectory(bool* exists, const SafeString& path)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (exists == NULL)
{
SEAD_ASSERT_MSG(false, "is_exist is null");
return false;
}
return doIsExistDirectory_(exists, path);
}
FileDevice* FileDevice::tryOpenDirectory(DirectoryHandle* handle, const SafeString& path)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return NULL;
if (handle == NULL)
{
SEAD_ASSERT_MSG(false, "handle is null");
return NULL;
}
FileDevice* device = doOpenDirectory_(handle, path);
setHandleBaseFileDevice_(handle, device);
if (device != NULL)
setHandleBaseOriginalFileDevice_(handle, this);
return device;
}
bool FileDevice::tryCloseDirectory(DirectoryHandle* handle)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == NULL)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
bool closed = doCloseDirectory_(handle);
if (closed)
{
setHandleBaseFileDevice_(handle, NULL);
setHandleBaseOriginalFileDevice_(handle, NULL);
}
return closed;
}
bool FileDevice::tryReadDirectory(u32* entriesRead, DirectoryHandle* handle,
DirectoryEntry* entries, u32 entriesToRead)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
if (handle == NULL)
{
SEAD_ASSERT_MSG(false, "handle is null");
return false;
}
if (!isMatchDevice_(handle))
{
SEAD_ASSERT_MSG(false, "handle device miss match");
return false;
}
u32 readCount = 0;
bool success = doReadDirectory_(&readCount, handle, entries, entriesToRead);
if (entriesRead != NULL)
*entriesRead = readCount;
if (readCount > entriesToRead)
{
SEAD_ASSERT_MSG(false, "buffer overflow");
return false;
}
return success;
}
bool FileDevice::tryMakeDirectory(const SafeString& path, u32 _)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
return doMakeDirectory_(path, _);
}
bool FileDevice::tryMakeDirectoryWithParent(const SafeString& path, u32 x)
{
SEAD_ASSERT_MSG(mPermission, "Device permission error.");
if (!mPermission)
return false;
bool exists = false;
if (!doIsExistDirectory_(&exists, path))
return false;
if (exists)
return true;
FixedSafeString<512> dir_name;
int num_existing_parents = 1;
bool should_trim = true;
bool reached_end = !Path::getDirectoryName(&dir_name, path);
while (!reached_end)
{
exists = false;
if (!tryIsExistDirectory(&exists, dir_name))
return false;
if (exists)
{
should_trim = false;
break;
}
reached_end = !Path::getDirectoryName(&dir_name, dir_name);
++num_existing_parents;
}
if (should_trim)
dir_name.trim(0);
int num_path_components = 0;
auto counting_iterator = path.tokenBegin("/");
const auto end = path.tokenEnd("/");
for (; end != counting_iterator; ++counting_iterator)
++num_path_components;
auto it = path.tokenBegin("/");
int num_levels_to_create = num_path_components - num_existing_parents;
for (; end != it; ++it)
{
if (num_levels_to_create >= 1)
{
--num_levels_to_create;
continue;
}
FixedSafeString<128> component;
it.get(&component);
if (dir_name != "")
dir_name.append("/");
dir_name.append(component);
if (!tryMakeDirectory(dir_name, x))
return false;
}
return true;
}
s32 FileDevice::getLastRawError() const
{
return doGetLastRawError_();
}
HandleBuffer& FileDevice::getHandleBaseHandleBuffer_(HandleBase* handle) const
{
return handle->mHandleBuffer;
}
void FileDevice::setFileHandleDivSize_(FileHandle* handle, u32 divSize) const
{
handle->mDivSize = divSize;
}
void FileDevice::setHandleBaseFileDevice_(HandleBase* handle, FileDevice* device) const
{
handle->mDevice = device;
}
void FileDevice::setHandleBaseOriginalFileDevice_(HandleBase* handle, FileDevice* device) const
{
handle->mOriginalDevice = device;
}
} // namespace sead
@@ -0,0 +1,362 @@
#ifdef cafe
#include <cafe.h>
#include <nn/save.h>
#endif // cafe
#ifdef NNSDK
#include <nn/fs.h>
#endif
#include <basis/seadNew.h>
#include <basis/seadRawPrint.h>
#include <devenv/seadEnvUtil.h>
#include <filedevice/seadFileDeviceMgr.h>
#include <filedevice/seadPath.h>
#include <heap/seadHeapMgr.h>
namespace sead
{
SEAD_SINGLETON_DISPOSER_IMPL(FileDeviceMgr)
FileDeviceMgr::FileDeviceMgr()
{
if (HeapMgr::sInstancePtr == NULL)
{
SEAD_ASSERT_MSG(false, "FileDeviceMgr need HeapMgr");
return;
}
Heap* const heap = HeapMgr::instance()->findContainHeap(this);
mount_(heap);
mMainFileDevice = new (heap) MainFileDevice(heap);
mount(mMainFileDevice);
mDefaultFileDevice = mMainFileDevice;
}
FileDeviceMgr::~FileDeviceMgr()
{
if (mMainFileDevice != NULL)
{
delete mMainFileDevice;
mMainFileDevice = NULL;
}
unmount_();
}
void FileDeviceMgr::mount_([[maybe_unused]] Heap* heap)
{
#ifdef cafe
FSInit();
FSAddClient(&client, FS_RET_NO_ERROR);
FSStateChangeParams changeParams = {
.userCallback = stateChangeCallback_, .userContext = NULL, .ioMsgQueue = NULL};
FSSetStateChangeNotification(&client, &changeParams);
SAVEInit();
_17A4[0] = 0;
_1824 = 0;
#elif defined(NNSDK)
// For release builds, only content is mounted using the regular nn::fs::MountRom.
// For debug builds, content is mounted using nn::fs::MountRom or by mounting
// SEAD_NIN_CONTENT_DIR on the host computer and the host root and SD are also mounted.
#ifdef SEAD_DEBUG
const auto mount_host_result = nn::fs::MountHostRoot();
if (mount_host_result.IsFailure())
{
SEAD_WARN("nn::fs::MountHostRoot() failed. module = %d desc = %d innervalue = 0x%08x",
mount_host_result.GetModule(), mount_host_result.GetDescription(),
mount_host_result.GetInnerValueForDebug());
mMountedHost = false;
}
else
{
mMountedHost = true;
}
#endif // SEAD_DEBUG
#ifdef SEAD_DEBUG
if (nn::fs::CanMountRomForDebug())
#endif
{
u64 cache_size = 0;
const auto query_result = nn::fs::QueryMountRomCacheSize(&cache_size);
SEAD_ASSERT_MSG(query_result.IsSuccess(),
"nn::fs::QueryMountRomCacheSize() failed. module = %d desc = %d "
"innervalue = 0x%08x",
query_result.GetModule(), query_result.GetDescription(),
query_result.GetInnerValueForDebug());
SEAD_DEBUG_PRINT("FileDeviceMgr: MountRom cache size => %zd\n", cache_size);
mRomCache = new (heap) u8[cache_size];
const auto result = nn::fs::MountRom("content", mRomCache, cache_size);
SEAD_ASSERT_MSG(result.IsSuccess(),
"nn::fs::MountRom() failed. module = %d desc = %d innervalue = 0x%08x",
result.GetModule(), result.GetDescription(),
result.GetInnerValueForDebug());
}
#ifdef SEAD_DEBUG
else
{
FixedSafeString<256> content_dir;
if (EnvUtil::getEnvironmentVariable(&content_dir, "SEAD_NIN_CONTENT_DIR") == -1)
{
SEAD_WARN("SEAD_NIN_CONTENT_DIR is not set.");
}
else
{
const auto result = nn::fs::MountHost("content", content_dir.cstr());
SEAD_ASSERT_MSG(result.IsSuccess(),
"nn::fs::MountHost() failed. module = %d desc = %d innervalue = 0x%08x",
result.GetModule(), result.GetDescription(),
result.GetInnerValueForDebug());
system::Print("FileDeviceMgr: MountHost => %s\n", content_dir.cstr());
}
}
const auto sd_result = nn::fs::MountSdCardForDebug("sd");
mMountedSd = sd_result.IsSuccess();
if (sd_result.IsSuccess())
system::Print("FileDeviceMgr: mount SD card\n");
else if (nn::fs::ResultMountNameAlreadyExists().Includes(sd_result))
system::Print("FileDeviceMgr: SD card already mounted\n");
else if (nn::fs::ResultSdCardAccessFailed().Includes(sd_result))
system::Print("FileDeviceMgr: SD card is not ready\n");
#endif // SEAD_DEBUG
#else
#error "Unknown platform"
#endif
}
void FileDeviceMgr::unmount_()
{
#ifdef cafe
FSDelClient(&client, FS_RET_NO_ERROR);
SAVEShutdown();
FSShutdown();
#elif defined(NNSDK)
#ifdef SEAD_DEBUG
if (mMountedHost)
nn::fs::UnmountHostRoot();
#endif
nn::fs::Unmount("content");
if (mRomCache)
delete[] mRomCache;
#ifdef SEAD_DEBUG
if (mMountedSd)
nn::fs::Unmount("sd");
#endif
#else
#error "Unknown platform"
#endif
}
void FileDeviceMgr::traceFilePath(const SafeString& path) const
{
SEAD_DEBUG_PRINT("[FileDeviceMgr] %s\n", path.cstr());
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
if (device != NULL)
device->traceFilePath(pathNoDrive);
else
SEAD_WARN("FileDevice not found: %s", path.cstr());
}
void FileDeviceMgr::traceDirectoryPath(const SafeString& path) const
{
SEAD_DEBUG_PRINT("[FileDeviceMgr] %s\n", path.cstr());
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
if (device != NULL)
device->traceDirectoryPath(pathNoDrive);
else
SEAD_WARN("FileDevice not found: %s", path.cstr());
}
void FileDeviceMgr::resolveFilePath(BufferedSafeString* out, const SafeString& path) const
{
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
if (device != NULL)
device->resolveFilePath(out, pathNoDrive);
else
SEAD_WARN("FileDevice not found: %s", path.cstr());
}
void FileDeviceMgr::resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const
{
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
if (device != NULL)
device->resolveDirectoryPath(out, pathNoDrive);
else
SEAD_WARN("FileDevice not found: %s", path.cstr());
}
void FileDeviceMgr::mount(FileDevice* device, const SafeString& name)
{
if (!name.isEqual(SafeString::cEmptyString))
device->setDriveName(name);
mDeviceList.pushBack(device);
}
void FileDeviceMgr::unmount(FileDevice* device)
{
mDeviceList.erase(device);
if (device == mDefaultFileDevice)
mDefaultFileDevice = NULL;
}
void FileDeviceMgr::unmount(const SafeString& name)
{
auto* device = findDevice(name);
if (!device)
{
SEAD_ASSERT_MSG(false, "drive not found: %s\n", name.cstr());
return;
}
unmount(device);
}
FileDevice* FileDeviceMgr::findDeviceFromPath(const SafeString& path,
BufferedSafeString* pathNoDrive) const
{
FixedSafeString<32> driveName;
FileDevice* device;
if (!Path::getDriveName(&driveName, path))
{
device = mDefaultFileDevice;
if (!device)
{
SEAD_ASSERT_MSG(false, "drive name not found and default file device is null");
return nullptr;
}
}
else
device = findDevice(driveName);
if (!device)
return nullptr;
if (pathNoDrive != NULL)
Path::getPathExceptDrive(pathNoDrive, path);
return device;
}
FileDevice* FileDeviceMgr::findDevice(const SafeString& name) const
{
for (auto it = mDeviceList.begin(); it != mDeviceList.end(); ++it)
if ((*it)->getDriveName() == name)
return *it;
return nullptr;
}
FileDevice* FileDeviceMgr::tryOpen(FileHandle* handle, const SafeString& path,
FileDevice::FileOpenFlag flag, u32 divSize)
{
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
if (device == NULL)
return NULL;
return device->tryOpen(handle, pathNoDrive, flag, divSize);
}
FileDevice* FileDeviceMgr::tryOpenDirectory(DirectoryHandle* handle, const SafeString& path)
{
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
if (!device)
return nullptr;
if (!device->isExistDirectory(pathNoDrive))
return nullptr;
return device->tryOpenDirectory(handle, pathNoDrive);
}
u8* FileDeviceMgr::tryLoad(FileDevice::LoadArg& arg)
{
SEAD_ASSERT_MSG(arg.path != SafeString::cEmptyString, "path is null");
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(arg.path, &pathNoDrive);
if (device == NULL)
return NULL;
FileDevice::LoadArg arg2(arg);
arg2.path = pathNoDrive.cstr();
u8* data = device->tryLoad(arg2);
arg.read_size = arg2.read_size;
arg.roundup_size = arg2.roundup_size;
arg.need_unload = arg2.need_unload;
return data;
}
void FileDeviceMgr::unload(u8* data)
{
SEAD_ASSERT(data);
if (data)
delete data;
}
bool FileDeviceMgr::trySave(FileDevice::SaveArg& arg)
{
SEAD_ASSERT_MSG(arg.path != SafeString::cEmptyString, "path is null");
FixedSafeString<256> pathNoDrive;
FileDevice* device = findDeviceFromPath(arg.path, &pathNoDrive);
if (!device)
return false;
FileDevice::SaveArg arg2(arg);
arg2.path = pathNoDrive.cstr();
const bool ret = device->trySave(arg2);
arg.write_size = arg2.write_size;
return ret;
}
#ifdef NNSDK
void FileDeviceMgr::mountSaveDataForDebug(Heap*)
{
const auto result = nn::fs::MountSaveDataForDebug("save");
SEAD_ASSERT_MSG(
result.IsSuccess(),
"nn::fs::MountSaveDataForDebug() failed. module = %d desc = %d innervalue = 0x%08x",
result.GetModule(), result.GetDescription(), result.GetInnerValueForDebug());
}
void FileDeviceMgr::unmountSaveDataForDebug()
{
nn::fs::Unmount("save");
}
#endif
#ifdef cafe
void FileDeviceMgr::stateChangeCallback_(FSClient* client, FSVolumeState state, void* context)
{
FSGetLastError(client);
}
#endif // cafe
} // namespace sead
@@ -0,0 +1,52 @@
#include <filedevice/seadMainFileDevice.h>
#include <prim/seadSafeString.h>
#ifdef cafe
#include <filedevice/cafe/seadCafeFSAFileDeviceCafe.h>
#elif defined(NNSDK)
#include <filedevice/nin/seadNinContentFileDeviceNin.h>
#endif
namespace sead
{
MainFileDevice::MainFileDevice(Heap* heap) : FileDevice("main"), mFileDevice(nullptr)
{
#ifdef cafe
mFileDevice = new (heap, 4) CafeContentFileDevice();
#elif defined(NNSDK)
mFileDevice = new (heap, 8) NinContentFileDevice();
#else
#error "Unknown platform"
#endif
SEAD_ASSERT(mFileDevice);
}
MainFileDevice::~MainFileDevice()
{
if (mFileDevice == NULL)
return;
delete mFileDevice;
mFileDevice = NULL;
}
void MainFileDevice::traceFilePath(const SafeString& path) const
{
mFileDevice->traceFilePath(path);
}
void MainFileDevice::traceDirectoryPath(const SafeString& path) const
{
mFileDevice->traceDirectoryPath(path);
}
void MainFileDevice::resolveFilePath(BufferedSafeString* out, const SafeString& path) const
{
mFileDevice->resolveFilePath(out, path);
}
void MainFileDevice::resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const
{
mFileDevice->resolveDirectoryPath(out, path);
}
} // namespace sead
@@ -0,0 +1,180 @@
#include <filedevice/seadPath.h>
#include <prim/seadSafeString.h>
namespace sead
{
bool Path::getDriveName(BufferedSafeString* driveName, const SafeString& path)
{
SEAD_ASSERT_MSG(driveName, "destination buffer is null");
driveName->trim(0);
const s32 index = path.findIndex(":");
if (index == -1)
return false;
driveName->copy(path, index);
return true;
}
void Path::getPathExceptDrive(BufferedSafeString* pathNoDrive, const SafeString& path)
{
SEAD_ASSERT_MSG(pathNoDrive, "destination buffer is null");
pathNoDrive->trim(0);
s32 index = path.findIndex("://");
if (index == -1)
pathNoDrive->copyAt(0, path);
else
pathNoDrive->copyAt(0, path.getPart(index + 3));
}
namespace
{
s32 rfindCharIndex(const SafeString& path, char c)
{
const s32 length = path.calcLength();
const char* cstr = path.cstr();
for (s32 i = length; i >= 0; --i)
if (cstr[i] == c)
return i;
return -1;
}
char getLastChar(const SafeString& str)
{
return str.at(str.calcLength() - 1);
}
} // namespace
// NON_MATCHING: redundant checks for dot_index < 0 in SafeString::getPart() are optimized out
bool Path::getExt(BufferedSafeString* ext, const SafeString& path)
{
SEAD_ASSERT_MSG(ext, "destination buffer is null");
ext->trim(0);
const s32 dot_index = rfindCharIndex(path, '.');
if (dot_index < 0)
return false;
if (path.getPart(dot_index).include('/') || path.getPart(dot_index).include('\\'))
return false;
ext->copy(path.getPart(dot_index + 1));
return true;
}
bool Path::getFileName(BufferedSafeString* name, const SafeString& path)
{
SEAD_ASSERT_MSG(name, "destination buffer is null");
name->trim(0);
const s32 slash_index = rfindCharIndex(path, '/');
const s32 bslash_index = rfindCharIndex(path, '\\');
const s32 idx = slash_index > bslash_index ? slash_index : bslash_index;
name->copy(path.getPart(idx + 1));
return true;
}
bool Path::getBaseFileName(BufferedSafeString* name, const SafeString& path)
{
const s32 bslash_index = rfindCharIndex(path, '\\');
const s32 slash_index = rfindCharIndex(path, '/');
const s32 i = bslash_index > slash_index ? bslash_index : slash_index;
const s32 part_idx = i < 0 ? 0 : i + 1;
s32 dot_idx = rfindCharIndex(path, '.');
if (dot_idx < 0)
dot_idx = path.calcLength();
name->copy(path.getPart(part_idx), dot_idx - part_idx);
return true;
}
bool Path::getDirectoryName(BufferedSafeString* name, const SafeString& path)
{
SEAD_ASSERT_MSG(name, "destination buffer is null");
if (name == &path)
{
const s32 slash_index = rfindCharIndex(path, '/');
const s32 bslash_index = rfindCharIndex(path, '\\');
const s32 trim_index = slash_index > bslash_index ? slash_index : bslash_index;
if (trim_index < 1)
return false;
name->trim(trim_index);
}
else
{
name->trim(0);
const s32 slash_index = rfindCharIndex(path, '/');
const s32 bslash_index = rfindCharIndex(path, '\\');
const s32 trim_index = slash_index > bslash_index ? slash_index : bslash_index;
if (trim_index < 1)
return false;
name->copy(path, trim_index);
}
return true;
}
void Path::join(BufferedSafeString* out, const char* path1, const char* path2)
{
// Trivial case 1: path1 is empty.
if (!path1 || !path1[0])
{
out->copy(path2);
return;
}
// Trivial case 2: path2 is empty.
if (!path2 || !path2[0])
{
out->copy(path1);
return;
}
if (path2[0] == '\\' || path2[0] == '/')
{
// If path1 also ends with a slash, skip the slash in path2 to avoid getting "//".
const char last_char1 = getLastChar(path1);
if (last_char1 == '\\' || last_char1 == '/')
{
++path2;
if (!path2[0])
{
out->copy(path1);
return;
}
}
out->format("%s%s", path1, path2);
}
else
{
// If path1 already ends with a slash, do not insert "/" in the middle to avoid "//".
const char last_char1 = getLastChar(path1);
if (last_char1 == '\\' || last_char1 == '/')
out->format("%s%s", path1, path2);
else
out->format("%s/%s", path1, path2);
}
}
void Path::changeDelimiter(BufferedSafeString* out, char delimiter)
{
const s32 length = out->calcLength();
char* buffer = out->getBuffer();
for (s32 i = 0; i < length; ++i)
{
const char c = (*out)[i];
if (c == '\\' || c == '/')
buffer[i] = delimiter;
}
}
} // namespace sead
@@ -0,0 +1,16 @@
#include "framework/seadCalculateTask.h"
namespace sead
{
CalculateTask::CalculateTask(const TaskConstructArg& arg) : TaskBase(arg)
{
mCalcNode.bind(sead::Delegate<CalculateTask>{this, &CalculateTask::calc}, "CalculateTask");
}
CalculateTask::CalculateTask(const TaskConstructArg& arg, const char* name) : TaskBase(arg, name)
{
mCalcNode.bind(sead::Delegate<CalculateTask>{this, &CalculateTask::calc}, name);
}
CalculateTask::~CalculateTask() = default;
} // namespace sead
@@ -0,0 +1,38 @@
#include <framework/seadFramework.h>
#include <framework/seadMethodTreeMgr.h>
#include <framework/seadTaskMgr.h>
#include <heap/seadHeap.h>
namespace sead
{
Framework::CreateSystemTaskArg::CreateSystemTaskArg()
: hostio_parameter(NULL), infloop_detection_span()
{
}
Framework::Framework()
: mReserveReset(false), mResetParameter(NULL), mResetEvent(), mTaskMgr(NULL),
mMethodTreeMgr(NULL), mMethodTreeMgrHeap(NULL)
{
}
Framework::~Framework()
{
if (mTaskMgr != NULL)
{
mTaskMgr->finalize();
delete mTaskMgr;
mTaskMgr = NULL;
}
if (mMethodTreeMgr != NULL)
{
delete mMethodTreeMgr;
mMethodTreeMgr = NULL;
}
if (mMethodTreeMgrHeap != NULL)
mMethodTreeMgrHeap->destroy();
}
} // namespace sead
@@ -0,0 +1,101 @@
#include <framework/seadMethodTree.h>
#include <thread/seadCriticalSection.h>
namespace sead
{
void MethodTreeNode::pushBackChild(MethodTreeNode* node)
{
lock_();
node->detachSubTree();
node->mCriticalSection = mCriticalSection;
if (node->child())
{
auto* parent = node->child()->value();
if (parent)
parent->attachMutexRec_(mCriticalSection);
}
TreeNode::pushBackChild(node);
unlock_();
}
void MethodTreeNode::pushFrontChild(MethodTreeNode* node)
{
lock_();
node->detachSubTree();
node->mCriticalSection = mCriticalSection;
if (node->child())
{
auto* parent = node->child()->value();
if (parent)
parent->attachMutexRec_(mCriticalSection);
}
TreeNode::pushFrontChild(node);
unlock_();
}
void MethodTreeNode::attachMutexRec_(CriticalSection* m) const
{
const MethodTreeNode* node = this;
do
{
auto* child = node->child();
node->mCriticalSection = m;
if (child && child->value())
child->value()->attachMutexRec_(m);
} while (node->next() && (node = node->next()->value()));
}
void MethodTreeNode::detachAll()
{
CriticalSection* cs = mCriticalSection;
attachMutexRec_(NULL);
mCriticalSection = cs;
lock_();
TreeNode::detachAll();
unlock_();
mCriticalSection = NULL;
}
void MethodTreeNode::lock_()
{
if (mCriticalSection == NULL)
return;
mCriticalSection->lock();
}
void MethodTreeNode::unlock_()
{
if (mCriticalSection == NULL)
return;
mCriticalSection->unlock();
}
void MethodTreeNode::call()
{
lock_();
callRec_();
unlock_();
}
void MethodTreeNode::callRec_()
{
if (!mPauseFlag.isOn(cPause_Self))
(*mDelegateHolder.data())();
auto* node = child();
if (node && !mPauseFlag.isOn(cPause_Child))
{
while (node)
{
node->value()->callRec_();
node = node->value()->next();
}
}
}
} // namespace sead
@@ -0,0 +1,151 @@
#include "framework/seadProcessMeterBar.h"
#include "basis/seadRawPrint.h"
#include "framework/seadProcessMeter.h"
namespace sead
{
ProcessMeterBarBase::ProcessMeterBarBase(ProcessMeterBarBase::Section* sections, s32 num_sections,
const SafeString& name, const Color4f& color)
: INamable(name), mColor(color)
{
mSectionList(0).setBuffer(num_sections, sections);
_88(0) = 0;
mSectionList(1).setBuffer(num_sections, sections + num_sections);
_88(1) = 0;
}
ProcessMeterBarBase::~ProcessMeterBarBase()
{
if (mParent)
mParent->detachProcessMeterBar(this);
}
void ProcessMeterBarBase::measureBegin()
{
if (mEnabled)
measureBeginImpl_(TickTime(), mColor);
}
void ProcessMeterBarBase::measureBegin(const TickTime& start_time)
{
if (mEnabled)
measureBeginImpl_(start_time, mColor);
}
void ProcessMeterBarBase::measureBegin(const Color4f& color)
{
if (mEnabled)
measureBeginImpl_(TickTime(), color);
}
void ProcessMeterBarBase::measureBegin(const TickTime& start_time, const Color4f& color)
{
if (mEnabled)
measureBeginImpl_(start_time, color);
}
void ProcessMeterBarBase::measureEnd()
{
if (mEnabled)
measureEndImpl_(TickTime());
}
void ProcessMeterBarBase::measureEnd(const TickTime& end_time)
{
if (mEnabled)
measureEndImpl_(end_time);
}
const ProcessMeterBarBase::Section* ProcessMeterBarBase::getLastFirstBegin() const
{
return mSectionList[1 - mActiveBufferIdx].get(0);
}
TickSpan ProcessMeterBarBase::getLastTotalSpan() const
{
TickSpan total = 0;
for (s32 i = 0; i < _88[1 - mActiveBufferIdx]; ++i)
{
if (mSectionList[1 - mActiveBufferIdx].get(i)->parent == -1)
total += mSectionList[1 - mActiveBufferIdx].get(i)->span;
}
return total;
}
void ProcessMeterBarBase::onEndFrame()
{
SEAD_ASSERT(mTopSection == -1);
SEAD_ASSERT(mOverNum == 0);
mActiveBufferIdx = 1 - mActiveBufferIdx;
mTopSection = -1;
mOverNum = 0;
_88[mActiveBufferIdx] = 0;
mEnabled = mParent != nullptr;
}
void ProcessMeterBarBase::setParentProcessMeter(ProcessMeter* parent)
{
SEAD_ASSERT(mParent == nullptr || parent == nullptr);
mParent = parent;
}
void ProcessMeterBarBase::measureBeginImpl_(const TickTime& start_time, Color4f color)
{
addSection_(start_time, color, mTopSection);
}
void ProcessMeterBarBase::measureEndImpl_(const TickTime& end_time)
{
TickTime new_time = end_time;
mTicks[mActiveBufferIdx] = new_time;
if (mOverNum > 0)
{
--mOverNum;
}
else
{
TickTime t = getCurSection_(mTopSection)->time;
if (new_time.diff(t).toS64() < 0)
new_time = t;
SEAD_ASSERT_MSG(mTopSection >= 0, "Unmatching measureBegin / measureEnd.");
endSection_(mTopSection, new_time);
mTopSection = getCurSection_(mTopSection)->parent;
}
}
// NON_MATCHING: some stores are paired
void ProcessMeterBarBase::addSection_(const TickTime& time, Color4f color, s32 parent)
{
if (_88[mActiveBufferIdx] >= mSectionList[0].getSize())
{
++mOverNum;
}
else
{
Section* sec = getCurSection_(_88[mActiveBufferIdx]);
sec->time = time;
sec->span = -1;
sec->color = color;
sec->parent = parent;
mTopSection = _88[mActiveBufferIdx];
++_88[mActiveBufferIdx];
}
}
ProcessMeterBarBase::Section* ProcessMeterBarBase::getCurSection_(s32 idx)
{
SEAD_ASSERT(idx >= 0 && idx < mSectionList[0].getSize());
return mSectionList[mActiveBufferIdx].get(idx);
}
void ProcessMeterBarBase::endSection_(s32 idx, const TickTime& time)
{
SEAD_ASSERT(idx >= 0 && idx < mSectionList[0].getSize());
Section* sec = getCurSection_(idx);
SEAD_ASSERT(sec->span.toS64() == -1);
sec->span = time.diff(sec->time);
}
} // namespace sead
@@ -0,0 +1,150 @@
#include <framework/seadFramework.h>
#include <framework/seadMethodTreeMgr.h>
#include <framework/seadTaskBase.h>
#include <framework/seadTaskMgr.h>
#include <prim/seadSafeString.h>
#include <resource/seadResourceMgr.h>
#include <thread/seadDelegateThread.h>
namespace sead
{
bool TaskMgr::changeTaskState_(TaskBase* task, TaskBase::State state)
{
mCriticalSection.lock();
if (task->mState != state)
{
switch (state)
{
case TaskBase::cPrepare:
if (task->mState == TaskBase::cCreated)
{
task->mState = TaskBase::cPrepare;
appendToList_(mPrepareList, task);
if (mPrepareThread == NULL || mPrepareThread->sendMessage(1, 1))
{
mCriticalSection.unlock();
return true;
}
}
break;
case TaskBase::cPrepareDone:
task->mState = TaskBase::cPrepareDone;
task->mTaskListNode.erase();
mCriticalSection.unlock();
return true;
case TaskBase::cRunning:
task->mState = TaskBase::cRunning;
task->mTaskListNode.erase();
appendToList_(mActiveList, task);
if (ResourceMgr::instance() != NULL)
ResourceMgr::instance()->postCreate();
task->enterCommon();
mCriticalSection.unlock();
return true;
case TaskBase::cDying:
task->mState = TaskBase::cDying;
mCriticalSection.unlock();
return true;
case TaskBase::cDestroyable:
if (task->mState == TaskBase::cRunning)
{
task->mState = TaskBase::cDestroyable;
task->detachCalcImpl();
task->detachDrawImpl();
appendToList_(mDestroyableList, task);
mCriticalSection.unlock();
return true;
}
break;
case TaskBase::cDead:
task->exit();
task->mState = TaskBase::cDead;
task->mTaskListNode.erase();
mCriticalSection.unlock();
return true;
}
}
mCriticalSection.unlock();
return false;
}
void TaskMgr::destroyTaskSync(TaskBase* task)
{
if (mParentFramework->mMethodTreeMgr->mCS.tryLock())
{
doDestroyTask_(task);
mParentFramework->mMethodTreeMgr->mCS.unlock();
}
}
void TaskMgr::doDestroyTask_(TaskBase* task)
{
mCriticalSection.lock();
TreeNode* node = task->mChild;
while (node != NULL)
{
doDestroyTask_(static_cast<TTreeNode<TaskBase*>*>(node)->mData);
node = task->mChild;
}
if (changeTaskState_(task, TaskBase::cDead))
{
task->detachAll();
HeapArray heapArray(task->mHeapArray);
for (s32 i = 0; i < HeapMgr::sRootHeaps.mPtrNum; i++)
{
Heap* heap = heapArray.mHeaps[i];
if (heap != NULL)
heap->destroy();
}
}
mCriticalSection.unlock();
}
void TaskMgr::finalize()
{
if (mPrepareThread != NULL)
{
mPrepareThread->quitAndDestroySingleThread(false);
delete mPrepareThread;
mPrepareThread = NULL;
}
if (mRootTask != NULL)
{
destroyTaskSync(mRootTask);
mRootTask = NULL;
}
for (s32 i = 0; i < HeapMgr::sRootHeaps.mPtrNum; i++)
{
Heap* heap = mHeapArray.mHeaps[i];
if (heap)
{
heap->destroy();
mHeapArray.mHeaps[i] = NULL;
}
}
}
} // namespace sead
@@ -0,0 +1,392 @@
#include <cafe.h>
#include <cafe/gfd.h>
#include <filedevice/seadFileDevice.h>
#include <filedevice/seadFileDeviceMgr.h>
#include <gfx/cafe/seadPrimitiveRendererCafe.h>
#include <gfx/cafe/seadTextureCafeGX2.h>
#include <gfx/seadCamera.h>
#include <gfx/seadProjection.h>
#include <heap/seadHeap.h>
#include <math/seadMatrixCalcCommon.h>
#include <prim/seadSafeString.h>
namespace sead
{
PrimitiveRendererCafe::PrimitiveRendererCafe(Heap* heap) : mCameraMtx(), mProjectionMtx() {}
void PrimitiveRendererCafe::prepareFromBinaryImpl(Heap* heap, const void* bin_data, u32 bin_size)
{
u32 vtxHeaderSize = GFDGetVertexShaderHeaderSize(0, bin_data);
u32 vtxProgramSize = GFDGetVertexShaderProgramSize(0, bin_data);
mVertexShader = static_cast<GX2VertexShader*>(heap->alloc(vtxHeaderSize, 1));
void* vtxProgram = heap->alloc(vtxProgramSize, GX2_SHADER_ALIGNMENT);
GFDGetVertexShader(mVertexShader, vtxProgram, 0, bin_data);
GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, mVertexShader->shaderPtr, mVertexShader->shaderSize);
u32 pixHeaderSize = GFDGetPixelShaderHeaderSize(0, bin_data);
u32 pixProgramSize = GFDGetPixelShaderProgramSize(0, bin_data);
mPixelShader = static_cast<GX2PixelShader*>(heap->alloc(pixHeaderSize, 1));
void* pixProgram = heap->alloc(pixProgramSize, GX2_SHADER_ALIGNMENT);
GFDGetPixelShader(mPixelShader, pixProgram, 0, bin_data);
GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, mPixelShader->shaderPtr, mPixelShader->shaderSize);
mParamWVPOffset = GX2GetVertexUniformVarOffset(mVertexShader, "wvp");
mParamUserOffset = GX2GetVertexUniformVarOffset(mVertexShader, "user");
mParamColor0Offset = GX2GetVertexUniformVarOffset(mVertexShader, "color0");
mParamColor1Offset = GX2GetVertexUniformVarOffset(mVertexShader, "color1");
mParamRateOffset = GX2GetPixelUniformVarOffset(mPixelShader, "rate");
mParamTexLocation = GX2GetPixelSamplerVarLocation(mPixelShader, "texture0");
mAttrVertexLocation = GX2GetVertexAttribVarLocation(mVertexShader, "Vertex");
mAttrTexCoord0Location = GX2GetVertexAttribVarLocation(mVertexShader, "TexCoord0");
mAttrColorRateLocation = GX2GetVertexAttribVarLocation(mVertexShader, "ColorRate");
GX2InitAttribStream(&mAttributes[0], mAttrVertexLocation, 0, 0,
GX2_ATTRIB_FORMAT_32_32_32_FLOAT);
GX2InitAttribStream(&mAttributes[1], mAttrTexCoord0Location, 0, sizeof(f32) * 3,
GX2_ATTRIB_FORMAT_32_32_FLOAT);
GX2InitAttribStream(&mAttributes[2], mAttrColorRateLocation, 0, sizeof(f32) * 5,
GX2_ATTRIB_FORMAT_32_32_32_32_FLOAT);
mFetchShaderBufPtr = heap->alloc(GX2CalcFetchShaderSize(3), GX2_SHADER_ALIGNMENT);
GX2InitFetchShader(&mFetchShader, mFetchShaderBufPtr, 3, mAttributes);
GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, mFetchShaderBufPtr, GX2CalcFetchShaderSize(3));
{
// Quad
mQuadVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(
heap->alloc(4 * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mQuadIndexBuf = static_cast<u16*>(heap->alloc(6 * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setQuadVertex(mQuadVertexBuf, mQuadIndexBuf);
mQuadVertexBuf[0].uv.x = 0.0f;
mQuadVertexBuf[0].uv.y = 0.0f;
mQuadVertexBuf[1].uv.x = 1.0f;
mQuadVertexBuf[1].uv.y = 0.0f;
mQuadVertexBuf[2].uv.x = 0.0f;
mQuadVertexBuf[2].uv.y = 1.0f;
mQuadVertexBuf[3].uv.x = 1.0f;
mQuadVertexBuf[3].uv.y = 1.0f;
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mQuadVertexBuf,
4 * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mQuadIndexBuf, 6 * sizeof(u16));
}
{
// Box
static const u16 idx[4] = {0, 1, 3, 2};
mBoxIndexBuf = static_cast<u16*>(heap->alloc(sizeof(idx), GX2_INDEX_BUFFER_ALIGNMENT));
memcpy(mBoxIndexBuf, idx, sizeof(idx));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mBoxIndexBuf, 4 * sizeof(u16));
}
{
// Line
mLineVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(
heap->alloc(4 * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mLineIndexBuf = static_cast<u16*>(heap->alloc(6 * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setLineVertex(mLineVertexBuf, mLineIndexBuf);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mLineVertexBuf,
4 * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mLineIndexBuf, 6 * sizeof(u16));
}
{
// Cube
mCubeVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(
heap->alloc(8 * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mCubeIndexBuf =
static_cast<u16*>(heap->alloc(36 * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setCubeVertex(mCubeVertexBuf, mCubeIndexBuf);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCubeVertexBuf,
8 * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCubeIndexBuf, 36 * sizeof(u16));
}
{
// WireCube
mWireCubeVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(
heap->alloc(8 * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mWireCubeIndexBuf =
static_cast<u16*>(heap->alloc(17 * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setWireCubeVertex(mWireCubeVertexBuf, mWireCubeIndexBuf);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mWireCubeVertexBuf,
8 * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mWireCubeIndexBuf, 17 * sizeof(u16));
}
{
// SphereS
mSphereSVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(heap->alloc(
(4 * 8 + 2) * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mSphereSIndexBuf =
static_cast<u16*>(heap->alloc((4 * 8 * 6) * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setSphereVertex(mSphereSVertexBuf, mSphereSIndexBuf, 8, 4);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mSphereSVertexBuf,
(4 * 8 + 2) * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mSphereSIndexBuf,
(4 * 8 * 6) * sizeof(u16));
}
{
// SphereL
mSphereLVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(heap->alloc(
(8 * 16 + 2) * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mSphereLIndexBuf =
static_cast<u16*>(heap->alloc((8 * 16 * 6) * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setSphereVertex(mSphereLVertexBuf, mSphereLIndexBuf, 16, 8);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mSphereLVertexBuf,
(8 * 16 + 2) * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mSphereLIndexBuf,
(8 * 16 * 6) * sizeof(u16));
}
{
// DiskS
mDiskSVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(heap->alloc(
(16 + 1) * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mDiskSIndexBuf =
static_cast<u16*>(heap->alloc((8 * 6) * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setDiskVertex(mDiskSVertexBuf, mDiskSIndexBuf, 16);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mDiskSVertexBuf,
(16 + 1) * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mDiskSIndexBuf, (8 * 6) * sizeof(u16));
}
{
// DiskL
mDiskLVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(heap->alloc(
(32 + 1) * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mDiskLIndexBuf =
static_cast<u16*>(heap->alloc((16 * 6) * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setDiskVertex(mDiskLVertexBuf, mDiskLIndexBuf, 32);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mDiskLVertexBuf,
(32 + 1) * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mDiskLIndexBuf, (16 * 6) * sizeof(u16));
}
{
// CircleS
mCircleSIndexBuf =
static_cast<u16*>(heap->alloc(16 * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
for (s32 i = 0; i < 16; i++)
mCircleSIndexBuf[i] = i;
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCircleSIndexBuf, 16 * sizeof(u16));
}
{
// CircleL
mCircleLIndexBuf =
static_cast<u16*>(heap->alloc(32 * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
for (s32 i = 0; i < 32; i++)
mCircleLIndexBuf[i] = i;
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCircleLIndexBuf, 32 * sizeof(u16));
}
{
// CylinderS
mCylinderSVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(heap->alloc(
(16 * 2 + 2) * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mCylinderSIndexBuf =
static_cast<u16*>(heap->alloc((16 * 12) * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setCylinderVertex(mCylinderSVertexBuf, mCylinderSIndexBuf, 16);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCylinderSVertexBuf,
(16 * 2 + 2) * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCylinderSIndexBuf,
(16 * 12) * sizeof(u16));
}
{
// CylinderL
mCylinderLVertexBuf = static_cast<PrimitiveRendererUtil::Vertex*>(heap->alloc(
(32 * 2 + 2) * sizeof(PrimitiveRendererUtil::Vertex), GX2_VERTEX_BUFFER_ALIGNMENT));
mCylinderLIndexBuf =
static_cast<u16*>(heap->alloc((32 * 12) * sizeof(u16), GX2_INDEX_BUFFER_ALIGNMENT));
PrimitiveRendererUtil::setCylinderVertex(mCylinderLVertexBuf, mCylinderLIndexBuf, 32);
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCylinderLVertexBuf,
(32 * 2 + 2) * sizeof(PrimitiveRendererUtil::Vertex));
GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, mCylinderLIndexBuf,
(32 * 12) * sizeof(u16));
}
GX2InitSampler(&mDrawQuadSampler, GX2_TEX_CLAMP_CLAMP, GX2_TEX_XY_FILTER_BILINEAR);
}
void PrimitiveRendererCafe::prepareImpl(Heap* heap, const SafeString& path)
{
FileDevice::LoadArg arg;
arg.path = path;
arg.alignment = 0x20;
arg.heap = heap;
const void* file = sead::FileDeviceMgr::instance()->tryLoad(arg);
prepareFromBinaryImpl(heap, file, arg.read_size);
}
void PrimitiveRendererCafe::setCameraImpl(const Camera& camera)
{
ASM_MTXCopy(const_cast<f32(*)[4]>(camera.mMatrix.m), mCameraMtx.m);
}
void PrimitiveRendererCafe::setProjectionImpl(const Projection& projection)
{
ASM_MTX44Copy(const_cast<f32(*)[4]>(projection.getDeviceProjectionMatrix().m),
mProjectionMtx.m);
}
void PrimitiveRendererCafe::beginImpl()
{
Matrix44f wvp;
Matrix44CalcCommon<f32>::multiply(wvp, mProjectionMtx, mCameraMtx);
GX2SetShaders(&mFetchShader, mVertexShader, mPixelShader);
GX2SetVertexUniformReg(mParamWVPOffset, 0x10, &wvp);
GX2SetVertexUniformReg(mParamUserOffset, 0x10, &Matrix44f::ident);
GX2SetLineWidth(2.0f);
}
void PrimitiveRendererCafe::endImpl() {}
void PrimitiveRendererCafe::drawQuadImpl(const Matrix34f& model_mtx, const Color4f& colorL,
const Color4f& colorR)
{
drawTriangles_(model_mtx, colorL, colorR, mQuadVertexBuf, 4, mQuadIndexBuf, 6, NULL);
}
void PrimitiveRendererCafe::drawQuadImpl(const Matrix34f& model_mtx, const Texture& texture,
const Color4f& colorL, const Color4f& colorR,
const Vector2f& uv_src, const Vector2f& uv_size)
{
const TextureCafeGX2* texure_cafe_gx2 =
DynamicCast<const TextureCafeGX2, const Texture>(&texture);
drawTriangles_(model_mtx, colorL, colorR, mQuadVertexBuf, 4, mQuadIndexBuf, 6,
texure_cafe_gx2->mGX2Texture);
}
void PrimitiveRendererCafe::drawBoxImpl(const Matrix34f& model_mtx, const Color4f& colorL,
const Color4f& colorR)
{
drawLines_(model_mtx, colorL, colorR, mQuadVertexBuf, 4, mBoxIndexBuf, 4);
}
void PrimitiveRendererCafe::drawCubeImpl(const Matrix34f& model_mtx, const Color4f& c0,
const Color4f& c1)
{
drawTriangles_(model_mtx, c0, c1, mCubeVertexBuf, 8, mCubeIndexBuf, 36, NULL);
}
void PrimitiveRendererCafe::drawWireCubeImpl(const Matrix34f& model_mtx, const Color4f& c0,
const Color4f& c1)
{
drawLines_(model_mtx, c0, c1, mWireCubeVertexBuf, 8, mWireCubeIndexBuf, 17);
}
void PrimitiveRendererCafe::drawLineImpl(const Matrix34f& model_mtx, const Color4f& c0,
const Color4f& c1)
{
drawLines_(model_mtx, c0, c1, mLineVertexBuf, 2, mLineIndexBuf, 2);
}
void PrimitiveRendererCafe::drawSphere4x8Impl(const Matrix34f& model_mtx, const Color4f& north,
const Color4f& south)
{
drawTriangles_(model_mtx, north, south, mSphereSVertexBuf, 34, mSphereSIndexBuf, 192, NULL);
}
void PrimitiveRendererCafe::drawSphere8x16Impl(const Matrix34f& model_mtx, const Color4f& north,
const Color4f& south)
{
drawTriangles_(model_mtx, north, south, mSphereLVertexBuf, 130, mSphereLIndexBuf, 768, NULL);
}
void PrimitiveRendererCafe::drawDisk16Impl(const Matrix34f& model_mtx, const Color4f& center,
const Color4f& edge)
{
drawTriangles_(model_mtx, center, edge, mDiskSVertexBuf, 17, mDiskSIndexBuf, 48, NULL);
}
void PrimitiveRendererCafe::drawDisk32Impl(const Matrix34f& model_mtx, const Color4f& center,
const Color4f& edge)
{
drawTriangles_(model_mtx, center, edge, mDiskLVertexBuf, 33, mDiskLIndexBuf, 96, NULL);
}
void PrimitiveRendererCafe::drawCircle16Impl(const Matrix34f& model_mtx, const Color4f& edge)
{
drawLines_(model_mtx, edge, edge, mDiskSVertexBuf, 17, mCircleSIndexBuf, 16);
}
void PrimitiveRendererCafe::drawCircle32Impl(const Matrix34f& model_mtx, const Color4f& edge)
{
drawLines_(model_mtx, edge, edge, mDiskLVertexBuf, 33, mCircleLIndexBuf, 32);
}
void PrimitiveRendererCafe::drawCylinder16Impl(const Matrix34f& model_mtx, const Color4f& top,
const Color4f& btm)
{
drawTriangles_(model_mtx, top, btm, mCylinderSVertexBuf, 34, mCylinderSIndexBuf, 192, NULL);
}
void PrimitiveRendererCafe::drawCylinder32Impl(const Matrix34f& model_mtx, const Color4f& top,
const Color4f& btm)
{
drawTriangles_(model_mtx, top, btm, mCylinderLVertexBuf, 66, mCylinderLIndexBuf, 384, NULL);
}
void PrimitiveRendererCafe::drawTriangles_(const Matrix34f& model_mtx, const Color4f& c0,
const Color4f& c1, PrimitiveRendererUtil::Vertex* vtx,
u32 vtx_num, u16* idx, u32 idx_num,
const GX2Texture* tex)
{
GX2SetVertexUniformReg(mParamUserOffset, 12, &model_mtx);
GX2SetVertexUniformReg(mParamColor0Offset, 4, &c0);
GX2SetVertexUniformReg(mParamColor1Offset, 4, &c1);
if (tex != NULL)
{
GX2SetPixelUniformReg(mParamRateOffset, 4, &Vector4f::ex);
GX2SetPixelTexture(tex, mParamTexLocation);
GX2SetPixelSampler(&mDrawQuadSampler, mParamTexLocation);
}
else
GX2SetPixelUniformReg(mParamRateOffset, 4, &Vector4f::zero);
GX2SetAttribBuffer(0, vtx_num * sizeof(PrimitiveRendererUtil::Vertex),
sizeof(PrimitiveRendererUtil::Vertex), vtx);
GX2DrawIndexed(GX2_PRIMITIVE_TRIANGLES, idx_num, GX2_INDEX_FORMAT_U16, idx);
}
void PrimitiveRendererCafe::drawLines_(const Matrix34f& model_mtx, const Color4f& c0,
const Color4f& c1, PrimitiveRendererUtil::Vertex* vtx,
u32 vtx_num, u16* idx, u32 idx_num)
{
GX2SetVertexUniformReg(mParamUserOffset, 12, &model_mtx);
GX2SetVertexUniformReg(mParamColor0Offset, 4, &c0);
GX2SetVertexUniformReg(mParamColor1Offset, 4, &c1);
GX2SetPixelUniformReg(mParamRateOffset, 4, &Vector4f::zero);
GX2SetAttribBuffer(0, vtx_num * sizeof(PrimitiveRendererUtil::Vertex),
sizeof(PrimitiveRendererUtil::Vertex), vtx);
GX2DrawIndexed(GX2_PRIMITIVE_LINE_LOOP, idx_num, GX2_INDEX_FORMAT_U16, idx);
}
} // namespace sead
+14
View File
@@ -0,0 +1,14 @@
#include "gfx/seadCamera.h"
#include "basis/seadRawPrint.h"
namespace sead
{
Camera::~Camera() = default;
LookAtCamera::LookAtCamera(const Vector3f& pos, const Vector3f& at, const Vector3f& up)
: mPos(pos), mAt(at), mUp(up)
{
SEAD_ASSERT(mPos != mAt);
mUp.normalize();
}
} // namespace sead
+261
View File
@@ -0,0 +1,261 @@
#include <algorithm>
#include <cmath>
#include <gfx/seadColor.h>
#include <math/seadMathCalcCommon.h>
namespace sead
{
const Color4f Color4f::cBlack(0.0f, 0.0f, 0.0f, 1.0f);
const Color4f Color4f::cGray(0.5f, 0.5f, 0.5f, 1.0f);
const Color4f Color4f::cWhite(1.0f, 1.0f, 1.0f, 1.0f);
const Color4f Color4f::cRed(1.0f, 0.0f, 0.0f, 1.0f);
const Color4f Color4f::cGreen(0.0f, 1.0f, 0.0f, 1.0f);
const Color4f Color4f::cBlue(0.0f, 0.0f, 1.0f, 1.0f);
const Color4f Color4f::cYellow(1.0f, 1.0f, 0.0f, 1.0f);
const Color4f Color4f::cMagenta(1.0f, 0.0f, 1.0f, 1.0f);
const Color4f Color4f::cCyan(0.0f, 1.0f, 1.0f, 1.0f);
const f32 Color4f::cElementMax = 1.0f;
const f32 Color4f::cElementMin = 0.0f;
Color4f Color4f::lerp(const Color4f& color1, const Color4f& color2, f32 t)
{
t = sead::Mathf::clamp(t, cElementMin, cElementMax);
const f32 a = sead::lerp(color1.a, color2.a, t);
const f32 r = sead::lerp(color1.r, color2.r, t);
const f32 g = sead::lerp(color1.g, color2.g, t);
const f32 b = sead::lerp(color1.b, color2.b, t);
return {r, g, b, a};
}
void Color4f::setLerp(const Color4f& color1, const Color4f& color2, f32 t)
{
t = sead::Mathf::clamp(t, cElementMin, cElementMax);
a = sead::lerp(color1.a, color2.a, t);
r = sead::lerp(color1.r, color2.r, t);
g = sead::lerp(color1.g, color2.g, t);
b = sead::lerp(color1.b, color2.b, t);
}
void Color4f::setGammaCollection(const Color4f& value, f32 gamma)
{
a = value.a;
r = std::pow(value.r, gamma);
g = std::pow(value.g, gamma);
b = std::pow(value.b, gamma);
}
void Color4f::adjustOverflow()
{
r = sead::Mathf::clamp(r, cElementMin, cElementMax);
g = sead::Mathf::clamp(g, cElementMin, cElementMax);
b = sead::Mathf::clamp(b, cElementMin, cElementMax);
a = sead::Mathf::clamp(a, cElementMin, cElementMax);
}
#define SEAD_COLOR4F_OPERATORS(OP, OP2) \
Color4f& Color4f::operator OP(const Color4f& rhs) \
{ \
r OP rhs.r; \
g OP rhs.g; \
b OP rhs.b; \
a OP rhs.a; \
return *this; \
} \
Color4f& Color4f::operator OP(f32 x) \
{ \
r OP x; \
g OP x; \
b OP x; \
a OP x; \
return *this; \
} \
Color4f operator OP2(const Color4f& lhs, const Color4f& rhs) \
{ \
Color4f result = lhs; \
result OP rhs; \
return result; \
} \
Color4f operator OP2(const Color4f& lhs, f32 x) \
{ \
Color4f result = lhs; \
result OP x; \
return result; \
}
SEAD_COLOR4F_OPERATORS(+=, +)
SEAD_COLOR4F_OPERATORS(-=, -)
SEAD_COLOR4F_OPERATORS(*=, *)
SEAD_COLOR4F_OPERATORS(/=, /)
bool operator==(const Color4f& lhs, const Color4f& rhs)
{
return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b && lhs.a == rhs.a;
}
const Color4u8 Color4u8::cBlack(0, 0, 0, 255);
const Color4u8 Color4u8::cGray(128, 128, 128, 255);
const Color4u8 Color4u8::cWhite(255, 255, 255, 255);
const Color4u8 Color4u8::cRed(255, 0, 0, 255);
const Color4u8 Color4u8::cGreen(0, 255, 0, 255);
const Color4u8 Color4u8::cBlue(0, 0, 255, 255);
const Color4u8 Color4u8::cYellow(255, 255, 0, 255);
const Color4u8 Color4u8::cMagenta(255, 0, 255, 255);
const Color4u8 Color4u8::cCyan(0, 255, 255, 255);
const u8 Color4u8::cElementMax = 255;
const u8 Color4u8::cElementMin = 0;
// NON_MATCHING: but semantically equivalent (setLerp is matching after all)
Color4u8 Color4u8::lerp(const Color4u8& color1, const Color4u8& color2, f32 t)
{
Color4u8 result = color1;
result.setLerp(color1, color2, t);
return result;
}
void Color4u8::setf(f32 fr, f32 fg, f32 fb, f32 fa)
{
r = sead::Mathf::clamp(fr, 0.0f, 1.0f) * 255.0f;
g = sead::Mathf::clamp(fg, 0.0f, 1.0f) * 255.0f;
b = sead::Mathf::clamp(fb, 0.0f, 1.0f) * 255.0f;
a = sead::Mathf::clamp(fa, 0.0f, 1.0f) * 255.0f;
}
void Color4u8::setLerp(const Color4u8& color1, const Color4u8& color2, f32 t)
{
t = sead::Mathf::clamp(t, 0.0f, 1.0f);
a = sead::lerp(color1.a, color2.a, t);
r = sead::lerp(color1.r, color2.r, t);
g = sead::lerp(color1.g, color2.g, t);
b = sead::lerp(color1.b, color2.b, t);
}
void Color4u8::setGammaCollection(const Color4u8& value, f32 gamma)
{
a = value.a;
r = sead::Mathf::clamp(std::pow(f32(value.r) / 255.0f, gamma), 0.0f, 1.0f) * 255.0f;
g = sead::Mathf::clamp(std::pow(f32(value.g) / 255.0f, gamma), 0.0f, 1.0f) * 255.0f;
b = sead::Mathf::clamp(std::pow(f32(value.b) / 255.0f, gamma), 0.0f, 1.0f) * 255.0f;
}
#define SEAD_Color4u8_OPERATORS(OP, OP2) \
Color4u8 operator OP2(const Color4u8& lhs, const Color4u8& rhs) \
{ \
Color4u8 result = lhs; \
result OP rhs; \
return result; \
} \
Color4u8 operator OP2(const Color4u8& lhs, u8 x) \
{ \
Color4u8 result = lhs; \
result OP x; \
return result; \
}
SEAD_Color4u8_OPERATORS(+=, +);
SEAD_Color4u8_OPERATORS(-=, -);
SEAD_Color4u8_OPERATORS(*=, *);
SEAD_Color4u8_OPERATORS(/=, /);
SEAD_Color4u8_OPERATORS(|=, |);
SEAD_Color4u8_OPERATORS(&=, &);
Color4u8& Color4u8::operator+=(const Color4u8& rhs)
{
return apply_([&](auto m) { this->*m = std::min<u32>(0xFF, u32(this->*m) + rhs.*m); });
}
Color4u8& Color4u8::operator-=(const Color4u8& rhs)
{
return apply_([&](auto m) { this->*m = this->*m >= rhs.*m ? this->*m - rhs.*m : 0; });
}
Color4u8& Color4u8::operator*=(const Color4u8& rhs)
{
return apply_([&](auto m) { this->*m = this->*m * rhs.*m / 0xFF; });
}
Color4u8& Color4u8::operator/=(const Color4u8& rhs)
{
return apply_([&](auto m) {
if (rhs.*m)
this->*m = std::min<u32>(0xFF, 255 * u32(this->*m) / u32(rhs.*m));
else
this->*m = 255;
});
}
Color4u8& Color4u8::operator|=(const Color4u8& rhs)
{
return apply_([&](auto m) { this->*m = this->*m | rhs.*m; });
}
Color4u8& Color4u8::operator&=(const Color4u8& rhs)
{
return apply_([&](auto m) { this->*m = this->*m & rhs.*m; });
}
bool operator==(const Color4u8& lhs, const Color4u8& rhs)
{
return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b && lhs.a == rhs.a;
}
Color4u8& Color4u8::operator+=(u8 x)
{
return apply_([&](auto m) { this->*m = std::min<u32>(0xFF, x + u32(this->*m)); });
}
Color4u8& Color4u8::operator-=(u8 x)
{
return apply_([&](auto m) { this->*m = this->*m >= x ? this->*m - x : 0; });
}
// NON_MATCHING: regalloc, one harmless reordering
Color4u8& Color4u8::operator*=(float x)
{
return apply_([&](auto m) { this->*m = std::max(0.0f, this->*m * x); });
}
Color4u8& Color4u8::operator/=(float x)
{
return apply_([&](auto m) {
if (x == 0.0f)
{
this->*m = 255;
return;
}
const float q = float(this->*m) / x;
if (q < 0.0f)
this->*m = 0;
else if (q > 255.0f)
this->*m = 255;
else
this->*m = q;
});
}
Color4u8& Color4u8::operator|=(u8 x)
{
return apply_([&](auto m) { this->*m = this->*m | x; });
}
Color4u8& Color4u8::operator&=(u8 x)
{
return apply_([&](auto m) { this->*m = this->*m & x; });
}
// NON_MATCHING: see Color4u8::operator*(f32)
Color4u8 operator*(const Color4u8& lhs, f32 x)
{
Color4u8 result = lhs;
result *= x;
return result;
}
// NON_MATCHING: equivalent but affected by the same issue as Color4u8::lerp
Color4u8 operator/(const Color4u8& lhs, f32 x)
{
Color4u8 result = lhs;
result /= x;
return result;
}
} // namespace sead
@@ -0,0 +1,66 @@
#ifdef cafe
#include <gfx/cafe/seadPrimitiveRendererCafe.h>
#endif // cafe
#include <gfx/seadPrimitiveRenderer.h>
namespace sead
{
SEAD_SINGLETON_DISPOSER_IMPL(PrimitiveRenderer)
PrimitiveRenderer::PrimitiveRenderer()
: IDisposer(), mRendererImpl(NULL), mModelMtx(Matrix34f::ident)
{
}
void PrimitiveRenderer::doPrepare_(Heap* heap)
{
#ifdef cafe
mRendererImpl = new (heap) PrimitiveRendererCafe(heap);
#else
#error "Unknown platform"
#endif // cafe
}
void PrimitiveRenderer::prepareFromBinary(Heap* heap, const void* bin_data, u32 bin_size)
{
doPrepare_(heap);
mRendererImpl->prepareFromBinaryImpl(heap, bin_data, bin_size);
}
void PrimitiveRenderer::prepare(Heap* heap, const SafeString& path)
{
doPrepare_(heap);
mRendererImpl->prepareImpl(heap, path);
}
void PrimitiveRenderer::setCamera(const Camera& camera)
{
mRendererImpl->setCameraImpl(camera);
}
void PrimitiveRenderer::setProjection(const Projection& projection)
{
mRendererImpl->setProjectionImpl(projection);
}
void PrimitiveRenderer::setModelMatrix(const Matrix34f& model_mtx)
{
#ifdef cafe
ASM_MTXCopy(const_cast<f32(*)[4]>(model_mtx.m), mModelMtx.m);
#else
#error "Unknown platform"
#endif // cafe
}
void PrimitiveRenderer::begin()
{
mRendererImpl->beginImpl();
}
void PrimitiveRenderer::end()
{
mRendererImpl->endImpl();
}
} // namespace sead
@@ -0,0 +1,287 @@
#include <cmath>
#include <cstring>
#include <gfx/seadPrimitiveRendererUtil.h>
#include <prim/seadMemUtil.h>
namespace sead
{
namespace PrimitiveRendererUtil
{
void setQuadVertex(Vertex* vtx, u16* idx)
{
static const Vertex cVtx[4] = {
Vertex(Vector3f(-0.5f, 0.5f, 0.0f), Vector2f(0.0f, 1.0f), Color4f(0.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(0.5f, 0.5f, 0.0f), Vector2f(1.0f, 1.0f), Color4f(0.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(-0.5f, -0.5f, 0.0f), Vector2f(0.0f, 0.0f), Color4f(1.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(0.5f, -0.5f, 0.0f), Vector2f(1.0f, 0.0f), Color4f(1.0f, 0.0f, 0.0f, 0.0f))};
static const u16 cIdx[6] = {0, 2, 1, 1, 2, 3};
if (vtx != NULL)
MemUtil::copy(vtx, cVtx, sizeof(cVtx));
if (idx != NULL)
MemUtil::copy(idx, cIdx, sizeof(cIdx));
}
void setLineVertex(Vertex* vtx, u16* idx)
{
static const Vertex cVtx[2] = {
Vertex(Vector3f(-0.5f, 0.0f, 0.0f), Vector2f(0.0f, 0.5f), Color4f(0.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(0.5f, 0.0f, 0.0f), Vector2f(1.0f, 0.5f), Color4f(1.0f, 0.0f, 0.0f, 0.0f))};
static const u16 cIdx[2] = {0, 1};
if (vtx != NULL)
MemUtil::copy(vtx, cVtx, sizeof(cVtx));
if (idx != NULL)
MemUtil::copy(idx, cIdx, sizeof(cIdx));
}
void setCubeVertex(Vertex* vtx, u16* idx)
{
static const Vertex cVtx[8] = {
Vertex(Vector3f(-0.5f, -0.5f, -0.5f), Vector2f(0.0f, 0.0f),
Color4f(1.0f / 3.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(-0.5f, 0.5f, -0.5f), Vector2f(0.0f, 1.0f), Color4f(0.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(-0.5f, 0.5f, 0.5f), Vector2f(1.0f, 1.0f),
Color4f(1.0f / 3.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(-0.5f, -0.5f, 0.5f), Vector2f(1.0f, 0.0f),
Color4f(2.0f / 3.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(0.5f, -0.5f, 0.5f), Vector2f(0.0f, 0.0f), Color4f(1.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(0.5f, 0.5f, 0.5f), Vector2f(0.0f, 1.0f),
Color4f(2.0f / 3.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(0.5f, 0.5f, -0.5f), Vector2f(1.0f, 1.0f),
Color4f(1.0f / 3.0f, 0.0f, 0.0f, 0.0f)),
Vertex(Vector3f(0.5f, -0.5f, -0.5f), Vector2f(1.0f, 0.0f),
Color4f(2.0f / 3.0f, 0.0f, 0.0f, 0.0f))};
static const u16 cIdx[36] = {2, 1, 0, 3, 2, 0, 5, 2, 3, 4, 5, 3, 6, 5, 7, 7, 5, 4,
1, 6, 0, 6, 7, 0, 0, 7, 3, 3, 7, 4, 1, 2, 6, 2, 5, 6};
if (vtx != NULL)
MemUtil::copy(vtx, cVtx, sizeof(cVtx));
if (idx != NULL)
MemUtil::copy(idx, cIdx, sizeof(cIdx));
}
void setWireCubeVertex(Vertex* vtx, u16* idx)
{
setCubeVertex(vtx, NULL);
static const u16 cIdx[17] = {0, 1, 2, 3, 0, 7, 6, 1, 2,
5, 6, 7, 4, 5, 4, 3, 0
};
if (idx != NULL)
MemUtil::copy(idx, cIdx, sizeof(cIdx));
}
void setSphereVertex(Vertex* vtx, u16* idx, s32 x, s32 y)
{
if (vtx != NULL)
{
for (s32 i = 0; i < y; i++)
{
f32 angle_y = ((i + 1) / (y + 1.0f) - 0.5f) * M_PI;
f32 pos_y = sinf(angle_y) * 0.5f;
f32 radius = cosf(angle_y) * 0.5f;
for (s32 j = 0; j < x; j++)
{
s32 pos = i * x + j;
f32 angle_x = (M_PI * 2.0f) * j / x;
if (i % 2 == 0)
angle_x -= (M_PI * 2.0f) / x / 2;
f32 pos_x = cosf(angle_x) * radius;
f32 pos_z = sinf(angle_x) * radius;
vtx[pos].pos.x = pos_x;
vtx[pos].pos.y = pos_y;
vtx[pos].pos.z = pos_z;
vtx[pos].uv.x = pos_y + 0.5f;
vtx[pos].uv.y = static_cast<f32>(j) / x;
vtx[pos].color.r = 0.5f - pos_y;
}
}
{
s32 pos = x * y;
vtx[pos].pos.x = 0.0f;
vtx[pos].pos.y = -0.5f;
vtx[pos].pos.z = 0.0f;
vtx[pos].uv.x = 0.0f;
vtx[pos].uv.y = 0.5f;
vtx[pos].color.r = 1.0f;
}
{
s32 pos = x * y + 1;
vtx[pos].pos.x = 0.0f;
vtx[pos].pos.y = 0.5f;
vtx[pos].pos.z = 0.0f;
vtx[pos].uv.x = 1.0f;
vtx[pos].uv.y = 0.5f;
vtx[pos].color.r = 0.0f;
}
}
if (idx != NULL)
{
for (s32 i = 0; i < x; i++)
{
idx[i * 3 + 0] = x * y;
idx[i * 3 + 1] = i;
idx[i * 3 + 2] = (i + 1) % x;
}
for (s32 i = 0; i < y - 1; i++)
{
for (s32 j = 0; j < x; j++)
{
s32 offset = i % 2;
s32 pos = (i * x * 6) + j * 6 + x * 3;
idx[pos + 0] = i * x + j;
idx[pos + 1] = (i + 1) * x + ((j + offset) % x);
idx[pos + 2] = i * x + ((j + 1) % x);
idx[pos + 3] = (i + 1) * x + ((j + offset) % x);
idx[pos + 4] = (i + 1) * x + ((j + 1 + offset) % x);
idx[pos + 5] = i * x + ((j + 1) % x);
}
}
for (s32 i = 0; i < x; i++)
{
s32 posOffs = 3 * x * (y - 1) * 2 + x * 3;
idx[i * 3 + 0 + posOffs] = x * y + 1;
idx[i * 3 + 1 + posOffs] = x * (y - 1) + ((i + 1) % x);
idx[i * 3 + 2 + posOffs] = x * (y - 1) + i;
}
}
}
void setDiskVertex(Vertex* vtx, u16* idx, s32 div)
{
if (vtx != NULL)
{
for (s32 i = 0; i < div; i++)
{
f32 angle = (M_PI * 2.0f) * i / div;
vtx[i].pos.x = cosf(angle) * 0.5f;
vtx[i].pos.y = sinf(angle) * 0.5f;
vtx[i].pos.z = 0.0f;
vtx[i].uv.x = vtx[i].pos.x;
vtx[i].uv.y = 1.0f - vtx[i].pos.y;
vtx[i].color.r = 1.0f;
}
{
s32 i = div;
vtx[i].pos.x = 0.0f;
vtx[i].pos.y = 0.0f;
vtx[i].pos.z = 0.0f;
vtx[i].uv.x = 0.5f;
vtx[i].uv.y = 0.5f;
vtx[i].color.r = 0.0f;
}
}
if (idx != NULL)
for (s32 i = 0; i < div; i++)
{
idx[i * 3 + 0] = i;
idx[i * 3 + 1] = (i + 1) % div;
idx[i * 3 + 2] = div;
}
}
void setCylinderVertex(Vertex* vtx, u16* idx, s32 div)
{
if (vtx != NULL)
{
for (s32 i = 0; i < div; i++)
{
f32 angle = (M_PI * 2.0f) * i / div;
vtx[i].pos.x = cosf(angle) * 0.5f;
vtx[i].pos.z = -sinf(angle) * 0.5f;
vtx[i].pos.y = 0.5f;
vtx[i].uv.x = vtx[i].pos.x;
vtx[i].uv.y = 1.0f - vtx[i].pos.z;
vtx[i].color.r = 0.0f;
s32 pos = i + div + 1;
vtx[pos].pos.x = cosf(angle) * 0.5f;
vtx[pos].pos.z = -sinf(angle) * 0.5f;
vtx[pos].pos.y = -0.5f;
vtx[pos].uv.x = vtx[i].pos.x;
vtx[pos].uv.y = 1.0f - vtx[i].pos.z;
vtx[pos].color.r = 1.0f;
}
{
s32 pos = div;
vtx[pos].pos.x = 0.0f;
vtx[pos].pos.y = 0.5f;
vtx[pos].pos.z = 0.0f;
vtx[pos].uv.x = 0.5f;
vtx[pos].uv.y = 0.5f;
vtx[pos].color.r = 0.0f;
}
{
s32 pos = div + div + 1;
vtx[pos].pos.x = 0.0f;
vtx[pos].pos.y = -0.5f;
vtx[pos].pos.z = 0.0f;
vtx[pos].uv.x = 0.5f;
vtx[pos].uv.y = 0.5f;
vtx[pos].color.r = 1.0f;
}
}
if (idx != NULL)
{
for (s32 i = 0; i < div; i++)
{
idx[i * 3 + 0] = i;
idx[i * 3 + 1] = (i + 1) - ((i + 1) % div);
idx[i * 3 + 2] = div;
s32 posOffs = div * 3;
idx[i * 3 + 0 + posOffs] = i + (div + 1);
idx[i * 3 + 1 + posOffs] = div + (div + 1);
idx[i * 3 + 2 + posOffs] = ((i + 1) - ((i + 1) % div)) + (div + 1);
}
for (s32 i = 0; i < div; i++)
{
s32 posOffs = div * 6;
idx[i * 6 + 0 + posOffs] = i;
idx[i * 6 + 1 + posOffs] = i + (div + 1);
idx[i * 6 + 2 + posOffs] = (i + 1) - ((i + 1) % div);
idx[i * 6 + 3 + posOffs] = (i + 1) - ((i + 1) % div);
idx[i * 6 + 4 + posOffs] = i + (div + 1);
idx[i * 6 + 5 + posOffs] = ((i + 1) - ((i + 1) % div)) + (div + 1);
}
}
}
} // namespace PrimitiveRendererUtil
} // namespace sead
@@ -0,0 +1,30 @@
#include <gfx/seadProjection.h>
namespace sead
{
void Projection::updateMatrixImpl_() const
{
if (mDirty)
{
doUpdateMatrix(const_cast<Matrix44f*>(&mMatrix));
mDirty = false;
mDeviceDirty = true;
doUpdateDeviceMatrix(const_cast<Matrix44f*>(&mDeviceMatrix), mMatrix, mDevicePosture);
mDeviceDirty = false;
}
else if (mDeviceDirty)
{
doUpdateDeviceMatrix(const_cast<Matrix44f*>(&mDeviceMatrix), mMatrix, mDevicePosture);
mDeviceDirty = false;
}
}
const Matrix44f& Projection::getDeviceProjectionMatrix() const
{
updateMatrixImpl_();
return mDeviceMatrix;
}
} // namespace sead
@@ -0,0 +1,24 @@
#include <cafe.h>
#include <heap/seadArena.h>
namespace sead
{
Arena::Arena() : mStart(NULL), mSize(0) {}
Arena::~Arena() {}
u8* Arena::initialize(size_t size)
{
MEMHeapHandle handle = MEMGetBaseHeapHandle(MEM_ARENA_2);
u32 allocSize = MEMGetAllocatableSizeForExpHeap(handle);
if (size > allocSize)
size = allocSize;
mSize = size;
mStart = static_cast<u8*>((*MEMAllocFromDefaultHeapEx)(size, MEM_HEAP_DEFAULT_ALIGNMENT));
return mStart;
}
} // namespace sead
@@ -0,0 +1,61 @@
#include <heap/seadDisposer.h>
#include <heap/seadHeap.h>
#include <heap/seadHeapMgr.h>
namespace
{
const u32 cDestructedFlag = 1;
} // namespace
namespace sead
{
IDisposer::IDisposer() : IDisposer(nullptr, HeapNullOption::UseSpecifiedOrContainHeap) {}
IDisposer::IDisposer(Heap* const disposer_heap, HeapNullOption option)
{
mDisposerHeap = disposer_heap;
if (mDisposerHeap)
{
mDisposerHeap->appendDisposer_(this);
return;
}
switch (option)
{
case HeapNullOption::AlwaysUseSpecifiedHeap:
SEAD_ASSERT_MSG(false, "disposer_heap must not be nullptr");
case HeapNullOption::UseSpecifiedOrContainHeap:
if (!sead::HeapMgr::sInstancePtr)
return;
mDisposerHeap = sead::HeapMgr::sInstancePtr->findContainHeap(this);
if (mDisposerHeap)
mDisposerHeap->appendDisposer_(this);
return;
case HeapNullOption::DoNotAppendDisposerIfNoHeapSpecified:
return;
case HeapNullOption::UseSpecifiedOrCurrentHeap:
if (!sead::HeapMgr::sInstancePtr)
return;
mDisposerHeap = sead::HeapMgr::sInstancePtr->getCurrentHeap();
if (mDisposerHeap)
mDisposerHeap->appendDisposer_(this);
return;
default:
SEAD_ASSERT_MSG(false, "illegal option[%d]", int(option));
return;
}
}
IDisposer::~IDisposer()
{
if (reinterpret_cast<uintptr_t>(mDisposerHeap) != cDestructedFlag)
{
if (mDisposerHeap != NULL)
mDisposerHeap->removeDisposer_(this);
*reinterpret_cast<uintptr_t*>(&mDisposerHeap) = cDestructedFlag;
}
}
} // namespace sead
+24
View File
@@ -0,0 +1,24 @@
#include <heap/seadExpHeap.h>
namespace sead
{
bool ExpHeap::isEmpty() const
{
return this->mUseList.size() == 0;
}
bool ExpHeap::isFreeable() const
{
return true;
}
bool ExpHeap::isResizable() const
{
return true;
}
bool ExpHeap::isAdjustable() const
{
return true;
}
} // namespace sead
+35
View File
@@ -0,0 +1,35 @@
#include <heap/seadHeap.h>
#include <heap/seadHeapMgr.h>
#include <prim/seadScopedLock.h>
namespace sead
{
Heap::~Heap() = default;
void Heap::appendDisposer_(IDisposer* disposer)
{
ConditionalScopedLock<CriticalSection> lock(&mCS, isLockEnabled());
mDisposerList.pushBack(disposer);
}
void Heap::removeDisposer_(IDisposer* disposer)
{
ConditionalScopedLock<CriticalSection> lock(&mCS, isLockEnabled());
mDisposerList.erase(disposer);
}
Heap* Heap::findContainHeap_(const void* ptr)
{
if (!isInclude(ptr))
return nullptr;
for (auto it = mChildren.begin(); it != mChildren.end(); ++it)
{
if (it->isInclude(ptr))
return it->findContainHeap_(ptr);
}
return this;
}
} // namespace sead
+55
View File
@@ -0,0 +1,55 @@
#include <heap/seadHeap.h>
#include <heap/seadHeapMgr.h>
namespace sead
{
HeapMgr* HeapMgr::sInstancePtr = NULL;
HeapMgr HeapMgr::sInstance;
Arena HeapMgr::sDefaultArena;
HeapMgr::RootHeaps HeapMgr::sRootHeaps;
HeapMgr::IndependentHeaps HeapMgr::sIndependentHeaps;
CriticalSection HeapMgr::sHeapTreeLockCS;
HeapMgr::HeapMgr() : mAllocFailedCallback(NULL) {}
Heap* HeapMgr::findContainHeap(const void* ptr) const
{
Heap* containHeap;
sHeapTreeLockCS.lock();
for (Heap& heap : sRootHeaps)
{
containHeap = heap.findContainHeap_(ptr);
if (containHeap != NULL)
{
sHeapTreeLockCS.unlock();
return containHeap;
}
}
for (Heap& heap : sIndependentHeaps)
{
containHeap = heap.findContainHeap_(ptr);
if (containHeap != NULL)
{
sHeapTreeLockCS.unlock();
return containHeap;
}
}
sHeapTreeLockCS.unlock();
return NULL;
}
FindContainHeapCache::FindContainHeapCache() = default;
bool FindContainHeapCache::tryRemoveHeap(Heap* heap)
{
uintptr_t original;
if (mHeap.compareExchange(uintptr_t(heap), 0, &original))
return true;
return (original & ~1u) != uintptr_t(heap);
}
} // namespace sead
@@ -0,0 +1,260 @@
#include "hostio/seadHostIOCurve.h"
#include <cmath>
#include "math/seadMathCalcCommon.h"
#include "math/seadMathNumbers.h"
namespace sead::hostio
{
template <typename T>
constexpr CurveFunctionTable<T> makeTable_()
{
return {{
curveLinear_,
curveHermit_,
curveStep_,
curveSin_,
curveCos_,
curveSinPow2_,
curveLinear2D_,
curveHermit2D_,
curveStep2D_,
curveNonuniformSpline_,
curveHermit2DSmooth_,
}};
}
template <typename T>
constexpr CurveFunctionTableVec2<T> makeTableVec2_()
{
return {{
curveLinearVec2_,
curveHermitVec2_,
curveStepVec2_,
curveSinVec2_,
curveCosVec2_,
curveSinPow2Vec2_,
curveLinear2DVec2_,
curveHermit2DVec2_,
curveStep2DVec2_,
curveNonuniformSplineVec2_,
curveHermit2DSmoothVec2_,
}};
}
CurveFunctionTable<f32> sCurveFunctionTbl_f32 = makeTable_<f32>();
CurveFunctionTable<f64> sCurveFunctionTbl_f64 = makeTable_<f64>();
CurveFunctionTableVec2<f32> sCurveFunctionTbl_Vec2f = makeTableVec2_<f32>();
CurveFunctionTableVec2<f64> sCurveFunctionTbl_Vec2d = makeTableVec2_<f64>();
template <typename T>
static T fracPart(T x)
{
return x - T(int(x));
}
template <typename T>
T curveLinear_(f32 t, const CurveDataInfo* info, const T* f)
{
if (t < 0)
return f[0];
const auto n = info->numUse - 1;
const int i = n * t;
if (i >= n)
return f[n];
return f[i] + (fracPart(n * t) * (f[i + 1] - f[i]));
}
// NON_MATCHING: instruction ordering
template <typename T>
T curveHermit_(f32 t, const CurveDataInfo* info, const T* f)
{
if (info->numUse % 2 == 1)
return 0;
if (t < 0)
return f[0];
const auto n = (info->numUse / 2) - 1;
const int i = n * t;
const int j = 2 * i;
if (i >= n)
return f[j];
const auto x = fracPart(n * t);
const auto coeff = &f[j];
return ((2 * x * x * x) - (3 * x * x) + 1) * coeff[0] // (2t^3 - 3t^2 + 1)p0
+ ((-2 * x * x * x) + (3 * x * x)) * coeff[2] // (-2t^3 + 2t^2)p1
+ ((x * x * x) - (x * x)) * coeff[3] // (t^3 - t^2)m1
+ ((x * x * x) - (2 * x * x) + x) * f[j | 1] // (t^3 - 2t^2 + t)m0
;
}
template <typename T>
T curveStep_(f32 t, const CurveDataInfo* info, const T* f)
{
const f32 x = Mathf::clamp(t, 0.0, 1.0);
return f[int(x * (info->numUse - 1))];
}
template <typename T>
T curveSin_(f32 t_, const CurveDataInfo*, const T* f)
{
const T t = t_;
return std::sin(f[0] * t * (2 * numbers::pi_v<T>)) * f[1];
}
template <typename T>
T curveCos_(f32 t_, const CurveDataInfo*, const T* f)
{
const T t = t_;
return std::cos(f[0] * T(t) * (2 * numbers::pi_v<T>)) * f[1];
}
template <typename T>
T curveSinPow2_(f32 t_, const CurveDataInfo*, const T* f)
{
const T t = t_;
const auto y = std::sin(f[0] * t * (2 * numbers::pi_v<T>));
return y * y * f[1];
}
// NON_MATCHING: instruction reordering (which results in localized regalloc differences)
template <typename T>
T curveLinear2D_(f32 t_, const CurveDataInfo* info, const T* f)
{
const T t = t_;
if (f[0] >= t)
return f[1];
const auto n = info->numUse / 2;
if (f[2 * (n - 1)] <= t)
return f[2 * (n - 1) + 1];
for (s32 i = 0; i < n; ++i)
{
const auto j = 2 * i;
if (f[j + 2] > t)
return f[j + 1] + ((t - f[j]) / (f[j + 2] - f[j])) * (f[j + 3] - f[j + 1]);
}
return 0;
}
// NON_MATCHING: same as curveHermit_<T>
template <typename T>
T curveHermit2D_(f32 t_, const CurveDataInfo* info, const T* f)
{
const T t = t_;
const s8 n = info->numUse / 3;
if (f[0] >= t)
return f[1];
if (f[3 * (n - 1)] <= t)
return f[3 * (n - 1) + 1];
for (s32 i = 0; i < n; ++i)
{
const auto j = 3 * i;
if (f[j + 3] > t)
{
const auto x = (t - f[j]) / (f[j + 3] - f[j]);
return ((2 * x * x * x) - (3 * x * x) + 1) * f[j + 1] // (2t^3 - 3t^2 + 1)p0
+ ((-2 * x * x * x) + (3 * x * x)) * f[j + 4] // (-2t^3 + 2t^2)p1
+ ((x * x * x) - (x * x)) * f[j + 5] // (t^3 - t^2)m1
+ ((x * x * x) - (2 * x * x) + x) * f[j + 2] // (t^3 - 2t^2 + t)m0
;
}
}
return 0;
}
template <typename T>
T curveStep2D_(f32 t_, const CurveDataInfo* info, const T* f)
{
const T t = t_;
const s8 n = info->numUse / 2;
if (t <= f[0])
return f[1];
if (t >= f[2 * (n - 1)])
return f[2 * (n - 1) + 1];
for (s32 i = 0; i < n; ++i)
{
if (t < f[2 * i + 2])
return f[2 * i + 1];
}
return 0;
}
template <typename T>
T curveNonuniformSpline_(f32, const CurveDataInfo*, const T*)
{
SEAD_ASSERT_MSG(false, "You must call ICurve::interpolateToVec2 at this curve type.");
return 0;
}
template <typename T>
Vector2<T> curveLinearVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveLinear_(t, info, f)};
}
template <typename T>
Vector2<T> curveHermitVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveHermit_(t, info, f)};
}
template <typename T>
Vector2<T> curveStepVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveStep_(t, info, f)};
}
template <typename T>
Vector2<T> curveSinVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveSin_(t, info, f)};
}
template <typename T>
Vector2<T> curveCosVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveCos_(t, info, f)};
}
template <typename T>
Vector2<T> curveSinPow2Vec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveSinPow2_(t, info, f)};
}
template <typename T>
Vector2<T> curveLinear2DVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveLinear2D_(t, info, f)};
}
template <typename T>
Vector2<T> curveHermit2DVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveHermit2D_(t, info, f)};
}
template <typename T>
Vector2<T> curveStep2DVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveStep2D_(t, info, f)};
}
// curveNonuniformSplineVec2_ has an assertion
template <typename T>
Vector2<T> curveHermit2DSmoothVec2_(f32 t, const CurveDataInfo* info, const T* f)
{
return {t, curveHermit2DSmooth_(t, info, f)};
}
} // namespace sead::hostio
@@ -0,0 +1,72 @@
#include "hostio/seadHostIOEventListener.h"
#include "basis/seadRawPrint.h"
#include "hostio/seadHostIOThreadLock.h"
namespace sead::hostio
{
u32 LifeCheckable::sCurrentCreateID = 1;
LifeCheckable* LifeCheckable::sTopInstance = nullptr;
LifeCheckable* LifeCheckable::searchInstanceFromCreateID(u32 createID)
{
ThreadLock lock;
for (auto instance = sTopInstance; instance; instance = instance->mNext)
{
if (instance->getCreateID() == createID)
return instance;
}
return nullptr;
}
// NON_MATCHING: regalloc
void LifeCheckable::disposeHostIOImpl_()
{
ThreadLock lock;
if (sTopInstance == this)
{
SEAD_ASSERT(mPrev == nullptr);
if (mNext)
{
sTopInstance = mNext;
mNext->mPrev = nullptr;
}
else
{
sTopInstance = nullptr;
}
}
else
{
SEAD_ASSERT(mPrev != nullptr);
mPrev->mNext = mNext;
if (mNext)
{
SEAD_ASSERT(mNext->mPrev == this);
mNext->mPrev = mPrev;
}
}
}
void LifeCheckable::initialize_()
{
ThreadLock lock;
mCreateID = sCurrentCreateID;
// NON_MATCHING: weird increment code
sCurrentCreateID += sCurrentCreateID == 0xffffffff ? 2 : 1;
if (sTopInstance)
{
mNext = sTopInstance;
sTopInstance->mPrev = this;
}
sTopInstance = this;
}
LifeCheckable::DisposeHostIOCaller::~DisposeHostIOCaller()
{
if (!mInstance)
return;
mInstance->disposeHostIO();
mInstance = nullptr;
}
} // namespace sead::hostio
@@ -0,0 +1,76 @@
#include "hostio/seadHostIONode.h"
#include "hostio/seadHostIOReflexible.h"
#include "hostio/seadHostIOThreadLock.h"
namespace sead::hostio
{
Node::Node()
{
mTreeNode.value() = this;
}
Node::Node(Heap* heap, IDisposer::HeapNullOption heap_null_option)
: Reflexible(heap, heap_null_option)
{
mTreeNode.value() = this;
}
void Node::detachAll()
{
ThreadLock lock;
mTreeNode.detachAll();
}
void Node::detach()
{
ThreadLock lock;
mTreeNode.detachSubTree();
}
Node* Node::getParentNode() const
{
TTreeNode<Node*>* node = mTreeNode.parent();
if (node)
return node->value();
return nullptr;
}
Node* Node::getChildNode() const
{
TTreeNode<Node*>* node = mTreeNode.child();
if (node)
return node->value();
return nullptr;
}
Node* Node::getNextNode() const
{
TTreeNode<Node*>* node = mTreeNode.next();
if (node)
return node->value();
return nullptr;
}
Node* Node::getPrevNode() const
{
TTreeNode<Node*>* parent = mTreeNode.parent();
if (parent && parent->child() == &mTreeNode)
return nullptr;
TTreeNode<Node*>* node = mTreeNode.prev();
if (node)
return node->value();
return nullptr;
}
bool Node::isAppended() const
{
return mTreeNode.parent() || mTreeNode.next() || mTreeNode.prev();
}
void Node::disposeHostIOImpl_()
{
destroy();
}
} // namespace sead::hostio
@@ -0,0 +1,106 @@
#include "hostio/seadHostIOReflexible.h"
#include "basis/seadRawPrint.h"
#include "heap/seadHeap.h"
#include "heap/seadHeapMgr.h"
#include "prim/seadMemUtil.h"
namespace sead::hostio
{
namespace
{
struct ReflexibleStringCopy
{
Heap* heap;
char string_data;
};
} // namespace
Reflexible::Reflexible()
{
setNodeName("");
setNodeMeta("");
}
Reflexible::Reflexible(Heap* heap, IDisposer::HeapNullOption heap_null_option)
: NodeEventListener(heap, heap_null_option)
{
setNodeName("");
setNodeMeta("");
}
void Reflexible::setNodeName(const SafeString& name)
{
if (mName != name.cstr() && mAllocFlg.isOn(u8(AllocFlg::Name)))
safeDelete_(AllocFlg::Name);
SEAD_ASSERT_MSG(!MemUtil::isStack(name.cstr()), "%p is in stack", name.cstr());
mName = name.cstr();
}
void Reflexible::setNodeNameCopyString(const SafeString& name, Heap* heap)
{
mName = createStringBuffer_(AllocFlg::Name, name, heap);
}
void Reflexible::setNodeMeta(const SafeString& meta)
{
if (mMeta != meta.cstr() && mAllocFlg.isOn(u8(AllocFlg::Meta)))
safeDelete_(AllocFlg::Meta);
SEAD_ASSERT_MSG(!MemUtil::isStack(meta.cstr()), "%p is in stack", meta.cstr());
mMeta = meta.cstr();
}
void Reflexible::setNodeMetaCopyString(const SafeString& meta, Heap* heap)
{
mMeta = createStringBuffer_(AllocFlg::Meta, meta, heap);
}
void Reflexible::safeDelete_(Reflexible::AllocFlg flag)
{
if (!mAllocFlg.isOn(u8(flag)))
return;
const char* string = nullptr;
if (flag == AllocFlg::Name)
string = mName;
else if (flag == AllocFlg::Meta)
string = mMeta;
mAllocFlg.reset(u8(flag));
if (string)
{
auto pair = reinterpret_cast<ReflexibleStringCopy*>(uintptr_t(string) - sizeof(void*));
pair->heap->free(pair);
}
}
const char* Reflexible::createStringBuffer_(Reflexible::AllocFlg flag, const SafeString& name,
Heap* heap)
{
safeDelete_(flag);
const s32 name_len = name.calcLength();
if (!heap)
heap = HeapMgr::instance()->getCurrentHeap();
SEAD_ASSERT(heap);
auto pair = static_cast<ReflexibleStringCopy*>(heap->alloc(name_len + 1 + sizeof(Heap*)));
pair->heap = heap;
// Copy the string into the buffer and terminate it.
MemUtil::copy(&pair->string_data, name.cstr(), name_len);
(&pair->string_data)[name_len] = 0;
mAllocFlg.set(u8(flag));
return &pair->string_data;
}
void Reflexible::disposeHostIOImpl_()
{
safeDelete_(AllocFlg::Name);
safeDelete_(AllocFlg::Meta);
}
} // namespace sead::hostio
@@ -0,0 +1,35 @@
#include "hostio/seadHostIOThreadLock.h"
#include "thread/seadCriticalSection.h"
namespace sead::hostio
{
Atomic<u32> ThreadLock::sLockCnt{0u};
ThreadLock::ThreadLock()
{
lock();
}
ThreadLock::~ThreadLock()
{
unlock();
}
void ThreadLock::lock()
{
sLockCnt.increment();
getCS().lock();
}
void ThreadLock::unlock()
{
getCS().unlock();
sLockCnt.decrement();
}
CriticalSection& ThreadLock::getCS()
{
static CriticalSection sCS;
return sCS;
}
} // namespace sead::hostio
@@ -0,0 +1,50 @@
#include <limits>
#include <math/seadBoundBox.h>
namespace sead
{
template <typename T>
static BoundBox2<T> getUndefined2()
{
Vector2<T> min(std::numeric_limits<T>::max(), std::numeric_limits<T>::max());
Vector2<T> max(std::numeric_limits<T>::min(), std::numeric_limits<T>::min());
return {min, max};
}
template <typename T>
static BoundBox3<T> getUndefined3()
{
Vector3<T> min(std::numeric_limits<T>::max(), std::numeric_limits<T>::max(),
std::numeric_limits<T>::max());
Vector3<T> max(std::numeric_limits<T>::min(), std::numeric_limits<T>::min(),
std::numeric_limits<T>::min());
return {min, max};
}
template <>
const BoundBox2<s32> BoundBox2<s32>::cUndefined = getUndefined2<s32>();
template <>
const BoundBox2<u32> BoundBox2<u32>::cUndefined = getUndefined2<u32>();
template <>
const BoundBox2<s64> BoundBox2<s64>::cUndefined = getUndefined2<s64>();
template <>
const BoundBox2<u64> BoundBox2<u64>::cUndefined = getUndefined2<u64>();
template <>
const BoundBox2<f32> BoundBox2<f32>::cUndefined = getUndefined2<f32>();
template <>
const BoundBox2<f64> BoundBox2<f64>::cUndefined = getUndefined2<f64>();
template <>
const BoundBox3<s32> BoundBox3<s32>::cUndefined = getUndefined3<s32>();
template <>
const BoundBox3<u32> BoundBox3<u32>::cUndefined = getUndefined3<u32>();
template <>
const BoundBox3<s64> BoundBox3<s64>::cUndefined = getUndefined3<s64>();
template <>
const BoundBox3<u64> BoundBox3<u64>::cUndefined = getUndefined3<u64>();
template <>
const BoundBox3<f32> BoundBox3<f32>::cUndefined = getUndefined3<f32>();
template <>
const BoundBox3<f64> BoundBox3<f64>::cUndefined = getUndefined3<f64>();
} // namespace sead
@@ -0,0 +1,322 @@
#include "math/seadMathCalcCommon.h"
namespace sead
{
template <>
const MathCalcCommon<float>::SinCosSample MathCalcCommon<float>::cSinCosTbl[257] = {
{0.0, 0.024541229009628296, 1.0, -0.00030118130962364376},
{0.024541229009628296, 0.02452644519507885, 0.99969881772995, -0.0009033624664880335},
{0.049067676067352295, 0.024496888741850853, 0.9987954497337341, -0.0015049994690343738},
{0.0735645666718483, 0.02445257641375065, 0.9972904324531555, -0.0021057301200926304},
{0.0980171412229538, 0.024393534287810326, 0.9951847195625305, -0.0027051919605582952},
{0.12241067737340927, 0.02431979961693287, 0.9924795627593994, -0.003303024685010314},
{0.1467304676771164, 0.02423141337931156, 0.9891765117645264, -0.0038988676387816668},
{0.1709618866443634, 0.02412843331694603, 0.9852776527404785, -0.004492362029850483},
{0.19509032368659973, 0.02401091903448105, 0.9807852506637573, -0.00508315023034811},
{0.21910123527050018, 0.02387893944978714, 0.9757021069526672, -0.005670876707881689},
{0.24298018217086792, 0.02373257838189602, 0.9700312614440918, -0.006255187559872866},
{0.2667127549648285, 0.0235719196498394, 0.9637760519981384, -0.006835730280727148},
{0.290284663438797, 0.023397063836455345, 0.9569403529167175, -0.007412155158817768},
{0.3136817514896393, 0.02320811338722706, 0.949528157711029, -0.007984115742146969},
{0.3368898630142212, 0.023005183786153793, 0.9415440559387207, -0.00855126604437828},
{0.3598950505256653, 0.02278839610517025, 0.9329928159713745, -0.009113266132771969},
{0.3826834261417389, 0.022557880729436874, 0.9238795042037964, -0.009669777005910873},
{0.40524131059646606, 0.022313779219985008, 0.91420978307724, -0.010220462456345558},
{0.4275550842285156, 0.02205623686313629, 0.903989315032959, -0.010764991864562035},
{0.4496113359928131, 0.02178540639579296, 0.89322429895401, -0.011303036473691463},
{0.4713967442512512, 0.021501455456018448, 0.8819212913513184, -0.011834273114800453},
{0.49289819598197937, 0.021204551681876183, 0.8700869679450989, -0.012358381412923336},
{0.5141027569770813, 0.02089487574994564, 0.8577286005020142, -0.012875044718384743},
{0.5349976420402527, 0.020572613924741745, 0.8448535799980164, -0.013383952900767326},
{0.5555702447891235, 0.020237958058714867, 0.8314695954322815, -0.013884799554944038},
{0.5758081674575806, 0.01989111304283142, 0.8175848126411438, -0.014377282001078129},
{0.5956993103027344, 0.019532285630702972, 0.803207516670227, -0.01486110407859087},
{0.6152315735816956, 0.01916169375181198, 0.7883464097976685, -0.015335974283516407},
{0.6343932747840881, 0.018779559060931206, 0.7730104327201843, -0.015801606699824333},
{0.6531728506088257, 0.01838611252605915, 0.7572088241577148, -0.016257721930742264},
{0.6715589761734009, 0.01798159070312977, 0.7409511208534241, -0.01670404151082039},
{0.6895405650138855, 0.01756623573601246, 0.7242470979690552, -0.017140300944447517},
{0.7071067690849304, 0.017140300944447517, 0.7071067690849304, -0.01756623573601246},
{0.7242470979690552, 0.01670404151082039, 0.6895405650138855, -0.01798159070312977},
{0.7409511208534241, 0.016257721930742264, 0.6715589761734009, -0.01838611252605915},
{0.7572088241577148, 0.015801606699824333, 0.6531728506088257, -0.018779559060931206},
{0.7730104327201843, 0.015335974283516407, 0.6343932747840881, -0.01916169375181198},
{0.7883464097976685, 0.01486110407859087, 0.6152315735816956, -0.019532285630702972},
{0.803207516670227, 0.014377282001078129, 0.5956993103027344, -0.01989111304283142},
{0.8175848126411438, 0.013884799554944038, 0.5758081674575806, -0.020237958058714867},
{0.8314695954322815, 0.013383952900767326, 0.5555702447891235, -0.020572613924741745},
{0.8448535799980164, 0.012875044718384743, 0.5349976420402527, -0.02089487574994564},
{0.8577286005020142, 0.012358381412923336, 0.5141027569770813, -0.021204551681876183},
{0.8700869679450989, 0.011834273114800453, 0.49289819598197937, -0.021501455456018448},
{0.8819212913513184, 0.011303036473691463, 0.4713967442512512, -0.02178540639579296},
{0.89322429895401, 0.010764991864562035, 0.4496113359928131, -0.02205623686313629},
{0.903989315032959, 0.010220462456345558, 0.4275550842285156, -0.022313779219985008},
{0.91420978307724, 0.009669777005910873, 0.40524131059646606, -0.022557880729436874},
{0.9238795042037964, 0.009113266132771969, 0.3826834261417389, -0.02278839610517025},
{0.9329928159713745, 0.00855126604437828, 0.3598950505256653, -0.023005183786153793},
{0.9415440559387207, 0.007984115742146969, 0.3368898630142212, -0.02320811338722706},
{0.949528157711029, 0.007412155158817768, 0.3136817514896393, -0.023397063836455345},
{0.9569403529167175, 0.006835730280727148, 0.290284663438797, -0.0235719196498394},
{0.9637760519981384, 0.006255187559872866, 0.2667127549648285, -0.02373257838189602},
{0.9700312614440918, 0.005670876707881689, 0.24298018217086792, -0.02387893944978714},
{0.9757021069526672, 0.00508315023034811, 0.21910123527050018, -0.02401091903448105},
{0.9807852506637573, 0.004492362029850483, 0.19509032368659973, -0.02412843331694603},
{0.9852776527404785, 0.0038988676387816668, 0.1709618866443634, -0.02423141337931156},
{0.9891765117645264, 0.003303024685010314, 0.1467304676771164, -0.02431979961693287},
{0.9924795627593994, 0.0027051919605582952, 0.12241067737340927, -0.024393534287810326},
{0.9951847195625305, 0.0021057301200926304, 0.0980171412229538, -0.02445257641375065},
{0.9972904324531555, 0.0015049994690343738, 0.0735645666718483, -0.024496888741850853},
{0.9987954497337341, 0.0009033624664880335, 0.049067676067352295, -0.02452644519507885},
{0.99969881772995, 0.00030118130962364376, 0.024541229009628296, -0.024541229009628296},
{1.0, -0.00030118130962364376, 0.0, -0.024541229009628296},
{0.99969881772995, -0.0009033624664880335, -0.024541229009628296, -0.02452644519507885},
{0.9987954497337341, -0.0015049994690343738, -0.049067676067352295, -0.024496888741850853},
{0.9972904324531555, -0.0021057301200926304, -0.0735645666718483, -0.02445257641375065},
{0.9951847195625305, -0.0027051919605582952, -0.0980171412229538, -0.024393534287810326},
{0.9924795627593994, -0.003303024685010314, -0.12241067737340927, -0.02431979961693287},
{0.9891765117645264, -0.0038988676387816668, -0.1467304676771164, -0.02423141337931156},
{0.9852776527404785, -0.004492362029850483, -0.1709618866443634, -0.02412843331694603},
{0.9807852506637573, -0.00508315023034811, -0.19509032368659973, -0.02401091903448105},
{0.9757021069526672, -0.005670876707881689, -0.21910123527050018, -0.02387893944978714},
{0.9700312614440918, -0.006255187559872866, -0.24298018217086792, -0.02373257838189602},
{0.9637760519981384, -0.006835730280727148, -0.2667127549648285, -0.0235719196498394},
{0.9569403529167175, -0.007412155158817768, -0.290284663438797, -0.023397063836455345},
{0.949528157711029, -0.007984115742146969, -0.3136817514896393, -0.02320811338722706},
{0.9415440559387207, -0.00855126604437828, -0.3368898630142212, -0.023005183786153793},
{0.9329928159713745, -0.009113266132771969, -0.3598950505256653, -0.02278839610517025},
{0.9238795042037964, -0.009669777005910873, -0.3826834261417389, -0.022557880729436874},
{0.91420978307724, -0.010220462456345558, -0.40524131059646606, -0.022313779219985008},
{0.903989315032959, -0.010764991864562035, -0.4275550842285156, -0.02205623686313629},
{0.89322429895401, -0.011303036473691463, -0.4496113359928131, -0.02178540639579296},
{0.8819212913513184, -0.011834273114800453, -0.4713967442512512, -0.021501455456018448},
{0.8700869679450989, -0.012358381412923336, -0.49289819598197937, -0.021204551681876183},
{0.8577286005020142, -0.012875044718384743, -0.5141027569770813, -0.02089487574994564},
{0.8448535799980164, -0.013383952900767326, -0.5349976420402527, -0.020572613924741745},
{0.8314695954322815, -0.013884799554944038, -0.5555702447891235, -0.020237958058714867},
{0.8175848126411438, -0.014377282001078129, -0.5758081674575806, -0.01989111304283142},
{0.803207516670227, -0.01486110407859087, -0.5956993103027344, -0.019532285630702972},
{0.7883464097976685, -0.015335974283516407, -0.6152315735816956, -0.01916169375181198},
{0.7730104327201843, -0.015801606699824333, -0.6343932747840881, -0.018779559060931206},
{0.7572088241577148, -0.016257721930742264, -0.6531728506088257, -0.01838611252605915},
{0.7409511208534241, -0.01670404151082039, -0.6715589761734009, -0.01798159070312977},
{0.7242470979690552, -0.017140300944447517, -0.6895405650138855, -0.01756623573601246},
{0.7071067690849304, -0.01756623573601246, -0.7071067690849304, -0.017140300944447517},
{0.6895405650138855, -0.01798159070312977, -0.7242470979690552, -0.01670404151082039},
{0.6715589761734009, -0.01838611252605915, -0.7409511208534241, -0.016257721930742264},
{0.6531728506088257, -0.018779559060931206, -0.7572088241577148, -0.015801606699824333},
{0.6343932747840881, -0.01916169375181198, -0.7730104327201843, -0.015335974283516407},
{0.6152315735816956, -0.019532285630702972, -0.7883464097976685, -0.01486110407859087},
{0.5956993103027344, -0.01989111304283142, -0.803207516670227, -0.014377282001078129},
{0.5758081674575806, -0.020237958058714867, -0.8175848126411438, -0.013884799554944038},
{0.5555702447891235, -0.020572613924741745, -0.8314695954322815, -0.013383952900767326},
{0.5349976420402527, -0.02089487574994564, -0.8448535799980164, -0.012875044718384743},
{0.5141027569770813, -0.021204551681876183, -0.8577286005020142, -0.012358381412923336},
{0.49289819598197937, -0.021501455456018448, -0.8700869679450989, -0.011834273114800453},
{0.4713967442512512, -0.02178540639579296, -0.8819212913513184, -0.011303036473691463},
{0.4496113359928131, -0.02205623686313629, -0.89322429895401, -0.010764991864562035},
{0.4275550842285156, -0.022313779219985008, -0.903989315032959, -0.010220462456345558},
{0.40524131059646606, -0.022557880729436874, -0.91420978307724, -0.009669777005910873},
{0.3826834261417389, -0.02278839610517025, -0.9238795042037964, -0.009113266132771969},
{0.3598950505256653, -0.023005183786153793, -0.9329928159713745, -0.00855126604437828},
{0.3368898630142212, -0.02320811338722706, -0.9415440559387207, -0.007984115742146969},
{0.3136817514896393, -0.023397063836455345, -0.949528157711029, -0.007412155158817768},
{0.290284663438797, -0.0235719196498394, -0.9569403529167175, -0.006835730280727148},
{0.2667127549648285, -0.02373257838189602, -0.9637760519981384, -0.006255187559872866},
{0.24298018217086792, -0.02387893944978714, -0.9700312614440918, -0.005670876707881689},
{0.21910123527050018, -0.02401091903448105, -0.9757021069526672, -0.00508315023034811},
{0.19509032368659973, -0.02412843331694603, -0.9807852506637573, -0.004492362029850483},
{0.1709618866443634, -0.02423141337931156, -0.9852776527404785, -0.0038988676387816668},
{0.1467304676771164, -0.02431979961693287, -0.9891765117645264, -0.003303024685010314},
{0.12241067737340927, -0.024393534287810326, -0.9924795627593994, -0.0027051919605582952},
{0.0980171412229538, -0.02445257641375065, -0.9951847195625305, -0.0021057301200926304},
{0.0735645666718483, -0.024496888741850853, -0.9972904324531555, -0.0015049994690343738},
{0.049067676067352295, -0.02452644519507885, -0.9987954497337341, -0.0009033624664880335},
{0.024541229009628296, -0.024541229009628296, -0.99969881772995, -0.00030118130962364376},
{0.0, -0.024541229009628296, -1.0, 0.00030118130962364376},
{-0.024541229009628296, -0.02452644519507885, -0.99969881772995, 0.0009033624664880335},
{-0.049067676067352295, -0.024496888741850853, -0.9987954497337341, 0.0015049994690343738},
{-0.0735645666718483, -0.02445257641375065, -0.9972904324531555, 0.0021057301200926304},
{-0.0980171412229538, -0.024393534287810326, -0.9951847195625305, 0.0027051919605582952},
{-0.12241067737340927, -0.02431979961693287, -0.9924795627593994, 0.003303024685010314},
{-0.1467304676771164, -0.02423141337931156, -0.9891765117645264, 0.0038988676387816668},
{-0.1709618866443634, -0.02412843331694603, -0.9852776527404785, 0.004492362029850483},
{-0.19509032368659973, -0.02401091903448105, -0.9807852506637573, 0.00508315023034811},
{-0.21910123527050018, -0.02387893944978714, -0.9757021069526672, 0.005670876707881689},
{-0.24298018217086792, -0.02373257838189602, -0.9700312614440918, 0.006255187559872866},
{-0.2667127549648285, -0.0235719196498394, -0.9637760519981384, 0.006835730280727148},
{-0.290284663438797, -0.023397063836455345, -0.9569403529167175, 0.007412155158817768},
{-0.3136817514896393, -0.02320811338722706, -0.949528157711029, 0.007984115742146969},
{-0.3368898630142212, -0.023005183786153793, -0.9415440559387207, 0.00855126604437828},
{-0.3598950505256653, -0.02278839610517025, -0.9329928159713745, 0.009113266132771969},
{-0.3826834261417389, -0.022557880729436874, -0.9238795042037964, 0.009669777005910873},
{-0.40524131059646606, -0.022313779219985008, -0.91420978307724, 0.010220462456345558},
{-0.4275550842285156, -0.02205623686313629, -0.903989315032959, 0.010764991864562035},
{-0.4496113359928131, -0.02178540639579296, -0.89322429895401, 0.011303036473691463},
{-0.4713967442512512, -0.021501455456018448, -0.8819212913513184, 0.011834273114800453},
{-0.49289819598197937, -0.021204551681876183, -0.8700869679450989, 0.012358381412923336},
{-0.5141027569770813, -0.02089487574994564, -0.8577286005020142, 0.012875044718384743},
{-0.5349976420402527, -0.020572613924741745, -0.8448535799980164, 0.013383952900767326},
{-0.5555702447891235, -0.020237958058714867, -0.8314695954322815, 0.013884799554944038},
{-0.5758081674575806, -0.01989111304283142, -0.8175848126411438, 0.014377282001078129},
{-0.5956993103027344, -0.019532285630702972, -0.803207516670227, 0.01486110407859087},
{-0.6152315735816956, -0.01916169375181198, -0.7883464097976685, 0.015335974283516407},
{-0.6343932747840881, -0.018779559060931206, -0.7730104327201843, 0.015801606699824333},
{-0.6531728506088257, -0.01838611252605915, -0.7572088241577148, 0.016257721930742264},
{-0.6715589761734009, -0.01798159070312977, -0.7409511208534241, 0.01670404151082039},
{-0.6895405650138855, -0.01756623573601246, -0.7242470979690552, 0.017140300944447517},
{-0.7071067690849304, -0.017140300944447517, -0.7071067690849304, 0.01756623573601246},
{-0.7242470979690552, -0.01670404151082039, -0.6895405650138855, 0.01798159070312977},
{-0.7409511208534241, -0.016257721930742264, -0.6715589761734009, 0.01838611252605915},
{-0.7572088241577148, -0.015801606699824333, -0.6531728506088257, 0.018779559060931206},
{-0.7730104327201843, -0.015335974283516407, -0.6343932747840881, 0.01916169375181198},
{-0.7883464097976685, -0.01486110407859087, -0.6152315735816956, 0.019532285630702972},
{-0.803207516670227, -0.014377282001078129, -0.5956993103027344, 0.01989111304283142},
{-0.8175848126411438, -0.013884799554944038, -0.5758081674575806, 0.020237958058714867},
{-0.8314695954322815, -0.013383952900767326, -0.5555702447891235, 0.020572613924741745},
{-0.8448535799980164, -0.012875044718384743, -0.5349976420402527, 0.02089487574994564},
{-0.8577286005020142, -0.012358381412923336, -0.5141027569770813, 0.021204551681876183},
{-0.8700869679450989, -0.011834273114800453, -0.49289819598197937, 0.021501455456018448},
{-0.8819212913513184, -0.011303036473691463, -0.4713967442512512, 0.02178540639579296},
{-0.89322429895401, -0.010764991864562035, -0.4496113359928131, 0.02205623686313629},
{-0.903989315032959, -0.010220462456345558, -0.4275550842285156, 0.022313779219985008},
{-0.91420978307724, -0.009669777005910873, -0.40524131059646606, 0.022557880729436874},
{-0.9238795042037964, -0.009113266132771969, -0.3826834261417389, 0.02278839610517025},
{-0.9329928159713745, -0.00855126604437828, -0.3598950505256653, 0.023005183786153793},
{-0.9415440559387207, -0.007984115742146969, -0.3368898630142212, 0.02320811338722706},
{-0.949528157711029, -0.007412155158817768, -0.3136817514896393, 0.023397063836455345},
{-0.9569403529167175, -0.006835730280727148, -0.290284663438797, 0.0235719196498394},
{-0.9637760519981384, -0.006255187559872866, -0.2667127549648285, 0.02373257838189602},
{-0.9700312614440918, -0.005670876707881689, -0.24298018217086792, 0.02387893944978714},
{-0.9757021069526672, -0.00508315023034811, -0.21910123527050018, 0.02401091903448105},
{-0.9807852506637573, -0.004492362029850483, -0.19509032368659973, 0.02412843331694603},
{-0.9852776527404785, -0.0038988676387816668, -0.1709618866443634, 0.02423141337931156},
{-0.9891765117645264, -0.003303024685010314, -0.1467304676771164, 0.02431979961693287},
{-0.9924795627593994, -0.0027051919605582952, -0.12241067737340927, 0.024393534287810326},
{-0.9951847195625305, -0.0021057301200926304, -0.0980171412229538, 0.02445257641375065},
{-0.9972904324531555, -0.0015049994690343738, -0.0735645666718483, 0.024496888741850853},
{-0.9987954497337341, -0.0009033624664880335, -0.049067676067352295, 0.02452644519507885},
{-0.99969881772995, -0.00030118130962364376, -0.024541229009628296, 0.024541229009628296},
{-1.0, 0.00030118130962364376, 0.0, 0.024541229009628296},
{-0.99969881772995, 0.0009033624664880335, 0.024541229009628296, 0.02452644519507885},
{-0.9987954497337341, 0.0015049994690343738, 0.049067676067352295, 0.024496888741850853},
{-0.9972904324531555, 0.0021057301200926304, 0.0735645666718483, 0.02445257641375065},
{-0.9951847195625305, 0.0027051919605582952, 0.0980171412229538, 0.024393534287810326},
{-0.9924795627593994, 0.003303024685010314, 0.12241067737340927, 0.02431979961693287},
{-0.9891765117645264, 0.0038988676387816668, 0.1467304676771164, 0.02423141337931156},
{-0.9852776527404785, 0.004492362029850483, 0.1709618866443634, 0.02412843331694603},
{-0.9807852506637573, 0.00508315023034811, 0.19509032368659973, 0.02401091903448105},
{-0.9757021069526672, 0.005670876707881689, 0.21910123527050018, 0.02387893944978714},
{-0.9700312614440918, 0.006255187559872866, 0.24298018217086792, 0.02373257838189602},
{-0.9637760519981384, 0.006835730280727148, 0.2667127549648285, 0.0235719196498394},
{-0.9569403529167175, 0.007412155158817768, 0.290284663438797, 0.023397063836455345},
{-0.949528157711029, 0.007984115742146969, 0.3136817514896393, 0.02320811338722706},
{-0.9415440559387207, 0.00855126604437828, 0.3368898630142212, 0.023005183786153793},
{-0.9329928159713745, 0.009113266132771969, 0.3598950505256653, 0.02278839610517025},
{-0.9238795042037964, 0.009669777005910873, 0.3826834261417389, 0.022557880729436874},
{-0.91420978307724, 0.010220462456345558, 0.40524131059646606, 0.022313779219985008},
{-0.903989315032959, 0.010764991864562035, 0.4275550842285156, 0.02205623686313629},
{-0.89322429895401, 0.011303036473691463, 0.4496113359928131, 0.02178540639579296},
{-0.8819212913513184, 0.011834273114800453, 0.4713967442512512, 0.021501455456018448},
{-0.8700869679450989, 0.012358381412923336, 0.49289819598197937, 0.021204551681876183},
{-0.8577286005020142, 0.012875044718384743, 0.5141027569770813, 0.02089487574994564},
{-0.8448535799980164, 0.013383952900767326, 0.5349976420402527, 0.020572613924741745},
{-0.8314695954322815, 0.013884799554944038, 0.5555702447891235, 0.020237958058714867},
{-0.8175848126411438, 0.014377282001078129, 0.5758081674575806, 0.01989111304283142},
{-0.803207516670227, 0.01486110407859087, 0.5956993103027344, 0.019532285630702972},
{-0.7883464097976685, 0.015335974283516407, 0.6152315735816956, 0.01916169375181198},
{-0.7730104327201843, 0.015801606699824333, 0.6343932747840881, 0.018779559060931206},
{-0.7572088241577148, 0.016257721930742264, 0.6531728506088257, 0.01838611252605915},
{-0.7409511208534241, 0.01670404151082039, 0.6715589761734009, 0.01798159070312977},
{-0.7242470979690552, 0.017140300944447517, 0.6895405650138855, 0.01756623573601246},
{-0.7071067690849304, 0.01756623573601246, 0.7071067690849304, 0.017140300944447517},
{-0.6895405650138855, 0.01798159070312977, 0.7242470979690552, 0.01670404151082039},
{-0.6715589761734009, 0.01838611252605915, 0.7409511208534241, 0.016257721930742264},
{-0.6531728506088257, 0.018779559060931206, 0.7572088241577148, 0.015801606699824333},
{-0.6343932747840881, 0.01916169375181198, 0.7730104327201843, 0.015335974283516407},
{-0.6152315735816956, 0.019532285630702972, 0.7883464097976685, 0.01486110407859087},
{-0.5956993103027344, 0.01989111304283142, 0.803207516670227, 0.014377282001078129},
{-0.5758081674575806, 0.020237958058714867, 0.8175848126411438, 0.013884799554944038},
{-0.5555702447891235, 0.020572613924741745, 0.8314695954322815, 0.013383952900767326},
{-0.5349976420402527, 0.02089487574994564, 0.8448535799980164, 0.012875044718384743},
{-0.5141027569770813, 0.021204551681876183, 0.8577286005020142, 0.012358381412923336},
{-0.49289819598197937, 0.021501455456018448, 0.8700869679450989, 0.011834273114800453},
{-0.4713967442512512, 0.02178540639579296, 0.8819212913513184, 0.011303036473691463},
{-0.4496113359928131, 0.02205623686313629, 0.89322429895401, 0.010764991864562035},
{-0.4275550842285156, 0.022313779219985008, 0.903989315032959, 0.010220462456345558},
{-0.40524131059646606, 0.022557880729436874, 0.91420978307724, 0.009669777005910873},
{-0.3826834261417389, 0.02278839610517025, 0.9238795042037964, 0.009113266132771969},
{-0.3598950505256653, 0.023005183786153793, 0.9329928159713745, 0.00855126604437828},
{-0.3368898630142212, 0.02320811338722706, 0.9415440559387207, 0.007984115742146969},
{-0.3136817514896393, 0.023397063836455345, 0.949528157711029, 0.007412155158817768},
{-0.290284663438797, 0.0235719196498394, 0.9569403529167175, 0.006835730280727148},
{-0.2667127549648285, 0.02373257838189602, 0.9637760519981384, 0.006255187559872866},
{-0.24298018217086792, 0.02387893944978714, 0.9700312614440918, 0.005670876707881689},
{-0.21910123527050018, 0.02401091903448105, 0.9757021069526672, 0.00508315023034811},
{-0.19509032368659973, 0.02412843331694603, 0.9807852506637573, 0.004492362029850483},
{-0.1709618866443634, 0.02423141337931156, 0.9852776527404785, 0.0038988676387816668},
{-0.1467304676771164, 0.02431979961693287, 0.9891765117645264, 0.003303024685010314},
{-0.12241067737340927, 0.024393534287810326, 0.9924795627593994, 0.0027051919605582952},
{-0.0980171412229538, 0.02445257641375065, 0.9951847195625305, 0.0021057301200926304},
{-0.0735645666718483, 0.024496888741850853, 0.9972904324531555, 0.0015049994690343738},
{-0.049067676067352295, 0.02452644519507885, 0.9987954497337341, 0.0009033624664880335},
{-0.024541229009628296, 0.024541229009628296, 0.99969881772995, 0.00030118130962364376},
{0.0, 0.024541229009628296, 1.0, -0.00030118130962364376},
};
template <>
const MathCalcCommon<f32>::AtanSample MathCalcCommon<f32>::cAtanTbl[128 + 1] = {
{0x0, 5340245.0f}, {0x517c55, 5339593.0f}, {0xa2f61e, 5338290.0f},
{0xf46ad0, 5336337.0f}, {0x145d7e1, 5333734.0f}, {0x1973ac7, 5330485.0f},
{0x1e890fc, 5326591.0f}, {0x239d7fb, 5322056.0f}, {0x28b0d43, 5316880.0f},
{0x2dc2e53, 5311072.0f}, {0x32d38b3, 5304632.0f}, {0x37e29eb, 5297566.0f},
{0x3ceff89, 5289880.0f}, {0x41fb721, 5281577.0f}, {0x4704e4a, 5272666.0f},
{0x4c0c2a4, 5263152.0f}, {0x51111d4, 5253040.0f}, {0x5613984, 5242339.0f},
{0x5b13767, 5231056.0f}, {0x6010937, 5219199.0f}, {0x650acb6, 5206776.0f},
{0x6a01fae, 5193795.0f}, {0x6ef5ff1, 5180265.0f}, {0x73e6b5a, 5166196.0f},
{0x78d3fce, 5151596.0f}, {0x7dbdb3a, 5136474.0f}, {0x82a3b94, 5120843.0f},
{0x8785edf, 5104710.0f}, {0x8c64325, 5088087.0f}, {0x913e67c, 5070983.0f},
{0x9614703, 5053411.0f}, {0x9ae62e6, 5035381.0f}, {0x9fb385b, 5016903.0f},
{0xa47c5a2, 4997989.0f}, {0xa940907, 4978650.0f}, {0xae000e1, 4958900.0f},
{0xb2bab95, 4938746.0f}, {0xb77078f, 4918204.0f}, {0xbc2134b, 4897283.0f},
{0xc0ccd4e, 4875996.0f}, {0xc57342a, 4854354.0f}, {0xca1467c, 4832371.0f},
{0xceb02ef, 4810055.0f}, {0xd346836, 4787422.0f}, {0xd7d7514, 4764482.0f},
{0xdc62856, 4741246.0f}, {0xe0e80d4, 4717727.0f}, {0xe567d73, 4693937.0f},
{0xe9e1d24, 4669886.0f}, {0xee55ee2, 4645588.0f}, {0xf2c41b6, 4621054.0f},
{0xf72c4b4, 4596293.0f}, {0xfb8e6f9, 4571319.0f}, {0xffea7b0, 4546143.0f},
{0x1044060f, 4520775.0f}, {0x10890156, 4495227.0f}, {0x10cd98d1, 4469509.0f},
{0x1111cbd6, 4443632.0f}, {0x115599c6, 4417608.0f}, {0x1199020e, 4391445.0f},
{0x11dc0423, 4365155.0f}, {0x121e9f86, 4338747.0f}, {0x1260d3c1, 4312233.0f},
{0x12a2a06a, 4285619.0f}, {0x12e4051d, 4258919.0f}, {0x13250184, 4232138.0f},
{0x1365954e, 4205290.0f}, {0x13a5c038, 4178379.0f}, {0x13e58203, 4151418.0f},
{0x1424da7d, 4124413.0f}, {0x1463c97a, 4097373.0f}, {0x14a24ed7, 4070307.0f},
{0x14e06a7a, 4043223.0f}, {0x151e1c51, 4016127.0f}, {0x155b6450, 3989029.0f},
{0x15984275, 3961935.0f}, {0x15d4b6c4, 3934853.0f}, {0x1610c149, 3907789.0f},
{0x164c6216, 3880751.0f}, {0x16879945, 3853745.0f}, {0x16c266f6, 3826778.0f},
{0x16fccb50, 3799855.0f}, {0x1736c67f, 3772982.0f}, {0x177058b5, 3746167.0f},
{0x17a9822c, 3719414.0f}, {0x17e24322, 3692729.0f}, {0x181a9bdb, 3666115.0f},
{0x18528c9e, 3639581.0f}, {0x188a15bb, 3613130.0f}, {0x18c13785, 3586765.0f},
{0x18f7f252, 3560493.0f}, {0x192e467f, 3534318.0f}, {0x1964346d, 3508243.0f},
{0x1999bc80, 3482274.0f}, {0x19cedf22, 3456412.0f}, {0x1a039cbe, 3430662.0f},
{0x1a37f5c4, 3405030.0f}, {0x1a6beaaa, 3379514.0f}, {0x1a9f7be4, 3354123.0f},
{0x1ad2a9ef, 3328857.0f}, {0x1b057548, 3303718.0f}, {0x1b37de6e, 3278711.0f},
{0x1b69e5e5, 3253838.0f}, {0x1b9b8c33, 3229100.0f}, {0x1bccd1df, 3204502.0f},
{0x1bfdb775, 3180044.0f}, {0x1c2e3d81, 3155729.0f}, {0x1c5e6492, 3131558.0f},
{0x1c8e2d38, 3107535.0f}, {0x1cbd9807, 3083660.0f}, {0x1ceca593, 3059934.0f},
{0x1d1b5671, 3036361.0f}, {0x1d49ab3a, 3012941.0f}, {0x1d77a487, 2989674.0f},
{0x1da542f1, 2966562.0f}, {0x1dd28713, 2943609.0f}, {0x1dff718c, 2920811.0f},
{0x1e2c02f7, 2898173.0f}, {0x1e583bf4, 2875693.0f}, {0x1e841d21, 2853373.0f},
{0x1eafa71e, 2831215.0f}, {0x1edada8d, 2809217.0f}, {0x1f05b80e, 2787380.0f},
{0x1f304042, 2765706.0f}, {0x1f5a73cc, 2744194.0f}, {0x1f84534e, 2722845.0f},
{0x1faddf6b, 2701658.0f}, {0x1fd718c5, 2680635.0f}, {0x20000000, 2659773.0f},
};
template <>
u32 MathCalcCommon<f32>::atanIdx_(f32 t)
{
t *= 128;
s32 index = t;
f32 rest = t - index;
return cAtanTbl[index].atan_val + (u32)(cAtanTbl[index].atan_delta * rest);
}
} // namespace sead
+57
View File
@@ -0,0 +1,57 @@
#include <math/seadMatrix.h>
namespace sead
{
template <>
const Matrix22<f32> Matrix22<f32>::zero(0.0f, 0.0f, 0.0f, 0.0f);
template <>
const Matrix22<f32> Matrix22<f32>::ident(1.0f, 0.0f, 0.0f, 1.0f);
template <>
const Matrix33<f32> Matrix33<f32>::zero(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
template <>
const Matrix33<f32> Matrix33<f32>::ident(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f);
template <>
const Matrix34<f32> Matrix34<f32>::zero(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f);
template <>
const Matrix34<f32> Matrix34<f32>::ident(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f);
template <>
const Matrix44<f32> Matrix44<f32>::zero(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
template <>
const Matrix44<f32> Matrix44<f32>::ident(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
template <>
const Matrix22<f64> Matrix22<f64>::zero(0, 0, 0, 0);
template <>
const Matrix22<f64> Matrix22<f64>::ident(1, 0, 0, 1);
template <>
const Matrix33<f64> Matrix33<f64>::zero(0, 0, 0, 0, 0, 0, 0, 0, 0);
template <>
const Matrix33<f64> Matrix33<f64>::ident(1, 0, 0, 0, 1, 0, 0, 0, 1);
template <>
const Matrix34<f64> Matrix34<f64>::zero(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
template <>
const Matrix34<f64> Matrix34<f64>::ident(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0);
template <>
const Matrix44<f64> Matrix44<f64>::zero(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
template <>
const Matrix44<f64> Matrix44<f64>::ident(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
} // namespace sead
+7
View File
@@ -0,0 +1,7 @@
#include <math/seadQuat.h>
namespace sead
{
template <>
const Quatf Quat<float>::unit(0.0f, 0.0f, 0.0f, 1.0f);
} // namespace sead
+50
View File
@@ -0,0 +1,50 @@
#include <math/seadVector.h>
namespace sead
{
template <>
const Vector2<f32> Vector2<f32>::zero(0.0f, 0.0f);
template <>
const Vector2<f32> Vector2<f32>::ex(1.0f, 0.0f);
template <>
const Vector2<f32> Vector2<f32>::ey(0.0f, 1.0f);
template <>
const Vector2<f32> Vector2<f32>::ones(1.0f, 1.0f);
template <>
const Vector3<f32> Vector3<f32>::zero(0.0f, 0.0f, 0.0f);
template <>
const Vector3<f32> Vector3<f32>::ex(1.0f, 0.0f, 0.0f);
template <>
const Vector3<f32> Vector3<f32>::ey(0.0f, 1.0f, 0.0f);
template <>
const Vector3<f32> Vector3<f32>::ez(0.0f, 0.0f, 1.0f);
template <>
const Vector3<f32> Vector3<f32>::ones(1.0f, 1.0f, 1.0f);
template <>
const Vector4<f32> Vector4<f32>::zero(0.0f, 0.0f, 0.0f, 0.0f);
template <>
const Vector4<f32> Vector4<f32>::ex(1.0f, 0.0f, 0.0f, 0.0f);
template <>
const Vector4<f32> Vector4<f32>::ey(0.0f, 1.0f, 0.0f, 0.0f);
template <>
const Vector4<f32> Vector4<f32>::ez(0.0f, 0.0f, 1.0f, 0.0f);
template <>
const Vector4<f32> Vector4<f32>::ew(0.0f, 0.0f, 0.0f, 1.0f);
template <>
const Vector4<f32> Vector4<f32>::ones(1.0f, 1.0f, 1.0f, 1.0f);
} // namespace sead
+75
View File
@@ -0,0 +1,75 @@
#include <basis/seadRawPrint.h>
#include <mc/seadCoreInfo.h>
namespace sead
{
u32 CoreInfo::sNumCores = 1;
u32 CoreInfo::sPlatformCoreId[32]{};
CoreId CoreInfo::sCoreIdFromPlatformCoreIdTable[32]{};
#ifdef NNSDK
nn::os::TlsSlot CoreInfo::sCoreNumberTlsSlot{};
#endif
namespace
{
// Force a static constructor to be emitted to initialize CoreInfo.
struct CoreInfoInitializer
{
CoreInfoInitializer() { CoreInfo::configure(); }
};
// IMPORTANT: this must be located after sCoreIdFromPlatformCoreIdTable to get the correct
// initialization order.
static CoreInfoInitializer sInitializer;
} // namespace
u32 CoreIdMask::countOnBits() const
{
u32 x = mMask;
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x07070707) + ((x >> 4) & 0x07070707);
x = (x & 0x000F000F) + ((x >> 8) & 0x000F000F);
x = (x & 0x1F) + (x >> 16);
return x;
}
void CoreInfo::configure()
{
#ifdef NNSDK
sNumCores = 3;
sPlatformCoreId[0] = 0;
sPlatformCoreId[1] = 1;
sPlatformCoreId[2] = 2;
SEAD_ASSERT(nn::os::GetCurrentCoreNumber() == 0);
const auto alloc_result = nn::os::AllocateTlsSlot(&sCoreNumberTlsSlot, nullptr);
SEAD_ASSERT(alloc_result.IsSuccess());
for (u32 i = 0; i != sNumCores; ++i)
{
const u32 id = sPlatformCoreId[i];
sCoreIdFromPlatformCoreIdTable[id] = i;
}
#else
#error "Unknown platform"
#endif
}
void CoreInfo::dump()
{
system::Print("* num cores %d\n", sNumCores);
for (u32 i = 0; i < sNumCores; ++i)
{
system::Print(" [%d] : %s : PlatformCoreId=%d\n", i, i == 0 ? "Main" : "Sub ",
sPlatformCoreId[i]);
}
system::Print("all mask : %x\n", u32(getMaskAll()));
system::Print("all sub mask : %x\n", u32(getMaskSubAll()));
}
u32 CoreInfo::getPlatformMask(CoreId id)
{
return 1 << getPlatformCoreId(id);
}
} // namespace sead
+6
View File
@@ -0,0 +1,6 @@
#include "mc/seadJob.h"
namespace sead
{
Job::~Job() = default;
}
+382
View File
@@ -0,0 +1,382 @@
#include <atomic>
#include "basis/seadRawPrint.h"
#include "framework/seadProcessMeter.h"
#include "mc/seadJobQueue.h"
#include "mc/seadWorker.h"
#include "prim/seadScopedLock.h"
namespace sead
{
// NON_MATCHING
JobQueue::JobQueue()
{
mCoreEnabled.fill(0);
mNumDoneJobs = 0;
mGranularity.fill(8);
}
bool JobQueue::run(u32, u32* finished_jobs, Worker*)
{
*finished_jobs = 0;
return true;
}
void JobQueue::runAll(u32* finished_jobs)
{
const u32 size = getNumJobs();
*finished_jobs = 0;
while (true)
{
u32 finished_jobs_batch = 0;
const bool ok = run(size, &finished_jobs_batch, nullptr);
*finished_jobs += finished_jobs_batch;
if (ok)
break;
}
SEAD_ASSERT(*finished_jobs == size);
}
bool JobQueue::isAllParticipantThrough() const
{
for (auto value : mCoreEnabled.mBuffer)
if (value)
return false;
return true;
}
void JobQueue::setGranularity(CoreId core, u32 x)
{
mGranularity[core] = x ? x : 1;
}
void JobQueue::setGranularity(u32 x)
{
for (s32 i = 0; i < mGranularity.size(); ++i)
setGranularity(i, x);
}
// NON_MATCHING: CMP (AND x y), #0 gets optimized into a TST
void JobQueue::setCoreMaskAndWaitType(CoreIdMask mask, SyncType type)
{
mStatus = Status::_6;
mMask = mask;
for (u32 i = 0; i < CoreInfo::getNumCores(); ++i)
{
mCoreEnabled[i] = mask.isOn(i);
mNumDoneJobs = 0;
}
mSyncType = type;
}
void JobQueue::FINISH(CoreId core)
{
std::atomic_thread_fence(std::memory_order_seq_cst);
mCoreEnabled[core] = 0;
wait_AT_WORKER();
}
void JobQueue::wait_AT_WORKER()
{
std::atomic_thread_fence(std::memory_order_seq_cst);
switch (mSyncType)
{
case SyncType::cCore:
if (!isDone_())
mFinishEvent.wait();
break;
case SyncType::cThread:
SEAD_ASSERT_MSG(false, "*NOT YET\n");
if (!isDone_())
mFinishEvent.wait();
break;
default:
break;
}
}
void JobQueue::wait()
{
if (u32(mSyncType) >= 2)
{
if (mSyncType != SyncType::cThread)
return;
SEAD_ASSERT_MSG(false, "NOT IMPLEMENTED.\n");
}
if (!isDone_())
mFinishEvent.wait();
}
bool JobQueue::isDone_()
{
return mNumDoneJobs == getNumJobs();
}
// NON_MATCHING: stack
void PerfJobQueue::initialize(const char* name, Heap* heap)
{
mBars.allocBufferAssert(CoreInfo::getNumCores(), heap);
mInts.allocBufferAssert(CoreInfo::getNumCores(), heap);
for (s32 i = 0; i < mInts.size(); ++i)
mInts[CoreId(i)] = 0;
for (s32 i = 0; i < mBars.size(); ++i)
mBars[i].setName(CoreId(i).text());
mProcessMeterBar.setColor({1, 1, 0, 1});
mProcessMeterBar.setName(name);
}
void PerfJobQueue::finalize()
{
mInts.freeBuffer();
mBars.freeBuffer();
}
void PerfJobQueue::reset()
{
for (s32 i = 0; i < mInts.size(); ++i)
mInts[CoreId(i)] = 0;
}
// NON_MATCHING: stack
void PerfJobQueue::measureBeginDeque()
{
auto& bar = mBars[CoreInfo::getCurrentCoreId()];
static_cast<void>(mInts[CoreInfo::getCurrentCoreId()]);
bar.measureBegin(Color4f::cWhite);
}
void PerfJobQueue::measureEndDeque()
{
mBars[CoreInfo::getCurrentCoreId()].measureEnd();
}
void PerfJobQueue::measureBeginRun()
{
auto& bar = mBars[CoreInfo::getCurrentCoreId()];
auto& idx = mInts[CoreInfo::getCurrentCoreId()];
bar.measureBegin(getBarColor(idx));
idx = (idx + 1) % 9;
}
void PerfJobQueue::measureEndRun()
{
mBars[CoreInfo::getCurrentCoreId()].measureEnd();
}
// NON_MATCHING: loading sColors...
const Color4f& PerfJobQueue::getBarColor(u32 idx) const
{
static const SafeArray<Color4f, 9> sColors = {{
{0.2078431397676468, 0.8313725590705872, 0.6274510025978088, 1.0},
{0.0, 0.6666666865348816, 0.4470588266849518, 1.0},
{0.125490203499794, 0.49803921580314636, 0.3764705955982208, 1.0},
{0.7490196228027344, 0.5254902243614197, 0.1882352977991104, 1.0},
{1.0, 0.6000000238418579, 0.0, 1.0},
{1.0, 0.6980392336845398, 0.250980406999588, 1.0},
{0.6901960968971252, 0.1725490242242813, 0.29411765933036804, 1.0},
{0.0, 0.9176470637321472, 0.21568627655506134, 1.0},
{0.9607843160629272, 0.239215686917305, 0.40784314274787903, 1.0},
}};
return sColors.mBuffer[idx];
}
void PerfJobQueue::attachProcessMeter()
{
if (!ProcessMeter::instance())
return;
for (s32 i = 0; i < mBars.size(); ++i)
ProcessMeter::instance()->attachProcessMeterBar(&mBars[i]);
ProcessMeter::instance()->attachProcessMeterBar(&mProcessMeterBar);
}
void PerfJobQueue::detachProcessMeter()
{
if (!ProcessMeter::instance())
return;
for (s32 i = 0; i < mBars.size(); ++i)
ProcessMeter::instance()->detachProcessMeterBar(&mBars[i]);
ProcessMeter::instance()->detachProcessMeterBar(&mProcessMeterBar);
}
FixedSizeJQ::FixedSizeJQ()
{
_230 = true;
mStatus = Status::_0;
mNumJobs = 0;
mNumProcessedJobs = 0;
}
void FixedSizeJQ::begin() {}
// TODO: Splatoon 2 and BotW sead have a different implementation which checks _230 and the current
// core number...
bool FixedSizeJQ::run(u32 size, u32* finished_jobs, Worker* worker)
{
*finished_jobs = 0;
#ifdef SEAD_DEBUG
mPerf.measureBeginDeque();
#endif
u32 num_finished = 0;
// NON_MATCHING: Clang refuses to materialize these variables here...
bool ret = true;
s32 begin = 0;
s32 end = -1;
if (size > 0 && mNumJobs > 0)
{
if (worker)
worker->setState(Worker::State::cRunning_WaitLock);
mLock.lock();
if (worker)
worker->setState(Worker::State::cRunning_GetLock);
begin = mNumProcessedJobs;
const auto num_jobs = mNumJobs;
num_finished = std::min(num_jobs - begin, size);
mNumProcessedJobs = num_finished + begin;
mLock.unlock();
end = num_finished + begin - 1;
ret = num_finished + begin >= num_jobs;
}
#ifdef SEAD_DEBUG
mPerf.measureEndDeque();
#endif
#ifdef SEAD_DEBUG
mPerf.measureBeginRun();
#endif
if (worker)
worker->setState(Worker::State::cRunning_Run);
for (s32 i = begin; i <= end; ++i)
mJobs[i]->invoke();
if (worker)
worker->setState(Worker::State::cRunning_AfterRun);
#ifdef SEAD_DEBUG
mPerf.measureEndRun();
#endif
if (ret)
{
if (worker)
worker->setState(Worker::State::cRunning_AllJobDoneReturn);
}
else
{
if (worker)
worker->setState(Worker::State::cRunning_BeforeReturn);
}
*finished_jobs = num_finished;
return ret;
}
u32 FixedSizeJQ::getNumJobs() const
{
return mNumJobs;
}
void FixedSizeJQ::initialize(u32 size, Heap* heap)
{
#ifdef SEAD_DEBUG
mPerf.initialize(getName().cstr(), heap);
#endif
ScopedLock<JobQueueLock> lock(&mLock);
mJobs.allocBufferAssert(size, heap);
mNumJobs = 0;
mNumProcessedJobs = 0;
mStatus = Status::_1;
}
void FixedSizeJQ::finalize()
{
#ifdef SEAD_DEBUG
mPerf.finalize();
#endif
mJobs.freeBuffer();
}
bool FixedSizeJQ::enque(Job* job)
{
mStatus = Status::_3;
if (mNumJobs >= u32(mJobs.size()))
return false;
mJobs[mNumJobs++] = job;
return true;
}
bool FixedSizeJQ::enqueSafe(Job* job)
{
mStatus = Status::_3;
ScopedLock<JobQueueLock> lock(&mLock);
if (mNumJobs >= u32(mJobs.size()))
return false;
mJobs[mNumJobs++] = job;
return true;
}
Job* FixedSizeJQ::deque()
{
ScopedLock<JobQueueLock> lock(&mLock);
if (mNumProcessedJobs >= mNumJobs)
return nullptr;
return mJobs[mNumProcessedJobs++];
}
u32 FixedSizeJQ::deque(Job** jobs, u32 count)
{
ScopedLock<JobQueueLock> lock(&mLock);
u32 ret = 0;
while (mNumProcessedJobs < mNumJobs && ret < count)
{
jobs[ret] = mJobs[mNumProcessedJobs++];
++ret;
}
return ret;
}
bool FixedSizeJQ::rewind()
{
#ifdef SEAD_DEBUG
mPerf.reset();
#endif
mNumProcessedJobs = 0;
return true;
}
void FixedSizeJQ::clear()
{
mStatus = Status::_5;
#ifdef SEAD_DEBUG
mPerf.reset();
#endif
mNumJobs = 0;
mNumProcessedJobs = 0;
mSyncType = SyncType::cNoSync;
}
bool FixedSizeJQ::debug_IsAllJobDone()
{
return mNumProcessedJobs >= mNumJobs;
}
} // namespace sead
+104
View File
@@ -0,0 +1,104 @@
#include "mc/seadWorker.h"
#include "prim/seadScopedLock.h"
namespace sead
{
Worker::Worker(WorkerMgr* mgr, u32 num_jobs, s32 stack_size, s32 priority, const SafeString& name)
: Thread(name, nullptr, priority, MessageQueue::BlockType::Blocking, 0x7FFFFFFF, stack_size, 1),
mMgr(mgr)
{
mJobQueues.allocBufferAssert(num_jobs, nullptr);
mEvent.setSignal();
}
bool Worker::pushJobQueue(const char* description, JobQueue* queue, JobQueuePushType type)
{
ScopedLock<JobQueueLock> lock(&mLock);
bool ret;
if (type == JobQueuePushType::cForward)
ret = mJobQueues.pushBack(queue);
else
ret = mJobQueues.pushBackwards(queue);
queue->setDescription(description);
return ret;
}
void Worker::clearJobQQ()
{
ScopedLock<JobQueueLock> lock(&mLock);
mJobQueues.clear();
}
void Worker::calc_(MessageQueue::Element msg)
{
if (msg == cMsg_Process)
proc_();
}
void Worker::proc_()
{
++mNumRuns;
mLastRun.setNow();
mWorkerState = Worker::State::cRunning;
JobQueue* queue = getNextJQ_();
const u32 core = mCore;
while (queue)
{
mCurrentQueue = queue;
mCurrentQueueDescription = queue->getDescription();
const auto granularity = queue->getGranularity(core);
// Process the queue.
queue->resetFinishEvent();
volatile bool ok = false;
u32 total_finished_jobs = 0;
while (!ok)
{
u32 finished_jobs = 0;
ok = queue->run(granularity, &finished_jobs, this);
total_finished_jobs += finished_jobs;
}
const u32 num_done = queue->addNumDoneJobs(total_finished_jobs);
if (num_done >= queue->getNumJobs())
queue->signalFinishEvent();
mWorkerState = Worker::State::cFinished;
queue->FINISH(mCore);
mWorkerState = Worker::State::cWaitingAtWorker;
mWorkerState = Worker::State::cRunning;
queue = getNextJQ_();
}
mCurrentQueue = nullptr;
mCurrentQueueDescription = nullptr;
mWorkerState = Worker::State::cSleep;
mEvent.setSignal();
}
JobQueue* Worker::getNextJQ_()
{
ScopedLock<JobQueueLock> lock(&mLock);
return mJobQueues ? mJobQueues.popFront() : nullptr;
}
void Worker::wakeup_(MessageQueue::Element msg)
{
SEAD_ASSERT_MSG(mWorkerState.load() == Worker::State::cSleep, "invalid state[%s]",
mWorkerState.load().text());
if (mJobQueues)
{
mEvent.resetSignal();
mWorkerState = Worker::State::cWakeup;
const bool success = sendMessage(msg, MessageQueue::BlockType::NonBlocking);
SEAD_ASSERT(success);
}
}
} // namespace sead
+175
View File
@@ -0,0 +1,175 @@
#include "mc/seadWorkerMgr.h"
#include "framework/seadInfLoopChecker.h"
#include "prim/seadSafeString.h"
namespace sead
{
WorkerMgr::WorkerMgr()
: mInfLoopEventSlot{
Delegate1<WorkerMgr, const InfLoopChecker::InfLoopParam&>(this, &WorkerMgr::onInfLoop_)}
{
}
void WorkerMgr::onInfLoop_(const InfLoopChecker::InfLoopParam&)
{
TickTime time;
}
WorkerMgr::InitializeArg::InitializeArg()
{
worker_num_jobs = 0x20;
name = "WorkerMgr";
thread_stack_sizes[0] = 0x1000;
thread_priorities.fill(Thread::cDefaultPriority);
thread_stack_sizes[1] = thread_stack_sizes[2] = 0x8000;
}
static SafeString* makeWorkerName(Heap* heap, const WorkerMgr::InitializeArg& arg, u32 core)
{
FixedSafeString<128> name;
const char* core_name = "?";
#ifdef SEAD_DEBUG
core_name = CoreId(i).text();
#endif
name.format("%s/Worker%d(%s)", arg.name, core, core_name);
return new HeapSafeString(heap, name);
}
void WorkerMgr::initialize(const InitializeArg& arg)
{
if (InfLoopChecker::instance())
InfLoopChecker::instance()->getEvent().connect(mInfLoopEventSlot);
const u32 num_cores = CoreInfo::getNumCores();
auto* heap = HeapMgr::instance()->getCurrentHeap();
SEAD_ASSERT(heap);
mWorkers.allocBufferAssert(num_cores, heap);
for (u32 i = 0; i < num_cores; ++i)
{
auto* name = makeWorkerName(heap, arg, i);
auto* worker = new Worker(this, arg.worker_num_jobs, arg.thread_stack_sizes[i],
arg.thread_priorities[i], *name);
mWorkers[i] = worker;
worker->mCore = i;
if (worker->mCore)
{
worker->setAffinity(CoreIdMask(i));
worker->start();
}
}
mJobQueues.allocBufferAssert(64, nullptr);
mNumJobQueues = 0;
}
void WorkerMgr::finalize()
{
if (mWorkers.size() > 1)
{
ThreadMgr::quitAndWaitDoneMultipleThread(
reinterpret_cast<Thread**>(mWorkers.getBufferPtr() + 1), mWorkers.size() - 1, true);
}
for (u32 i = 0, n = CoreInfo::getNumCores(); i != n; ++i)
{
if (mWorkers[i])
delete mWorkers[i];
mWorkers[i] = nullptr;
}
}
void WorkerMgr::pushJobQueue(JobQueue* queue, CoreIdMask core_id_mask, SyncType sync_type,
JobQueuePushType push_type)
{
pushJobQueue("nocontext", queue, core_id_mask, sync_type, push_type);
}
void WorkerMgr::pushJobQueue(const char* context_name, JobQueue* queue, CoreIdMask core_id_mask,
SyncType sync_type, JobQueuePushType push_type)
{
SEAD_ASSERT_MSG(core_id_mask, "core_id_mask must not be 0. context_name = %s", context_name);
queue->setCoreMaskAndWaitType(core_id_mask, sync_type);
queue->begin();
for (int i = 0; i < mWorkers.size(); ++i)
{
if (core_id_mask.isOn(i))
mWorkers[i]->pushJobQueue(context_name, queue, push_type);
}
mJobQueues[mNumJobQueues] = queue;
++mNumJobQueues;
}
void WorkerMgr::run()
{
if (mProcessJobQueues)
{
for (u32 i = 0; i < mNumJobQueues; ++i)
{
mJobQueues[i]->begin();
u32 finished_jobs = 0;
mJobQueues[i]->runAll(&finished_jobs);
mJobQueues[i]->addNumDoneJobs(finished_jobs);
mJobQueues[i]->FINISH(CoreInfo::getCurrentCoreId());
for (int j = 0; j < mWorkers.size(); ++j)
mWorkers[j]->clearJobQQ();
}
}
else
{
++mNumWakeups;
mLastWakeup.setNow();
for (int i = 0; i < mWorkers.size(); ++i)
{
if (mWorkers[i]->mCore)
mWorkers[i]->wakeup_(Worker::cMsg_Process);
}
}
}
void WorkerMgr::sync()
{
if (!mProcessJobQueues)
mWorkers[0]->proc_();
for (auto it = mWorkers.begin(1), end = mWorkers.end(); it != end; ++it)
{
while (!(*it)->mEvent.wait(mWaitDuration))
continue;
}
if (!isAllWorkerSleep())
{
std::array<Worker::State, 256> states{};
u32 idx = 0;
for (int i = 0; i < mWorkers.size(); ++i)
{
states[idx] = mWorkers[i]->mWorkerState.load();
++idx;
}
for (int i = 0; i < mWorkers.size(); ++i)
SEAD_DEBUG_PRINT(" [%d] [%s] = %s\n", i, mWorkers[i]->mCore.text(), states[i].text());
SEAD_ASSERT_MSG(false, "all sleep failed\n");
}
mNumJobQueues = 0;
}
bool WorkerMgr::isAllWorkerSleep() const
{
for (int i = 0; i < mWorkers.size(); ++i)
{
if (mWorkers[i]->mWorkerState.load() != Worker::State::cSleep)
return false;
}
return true;
}
} // namespace sead
+59
View File
@@ -0,0 +1,59 @@
#include <prim/seadBitFlag.h>
namespace sead
{
int BitFlagUtil::countOnBit(u32 x)
{
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x += (x >> 8);
x += (x >> 16);
return x & 0x3f;
}
int BitFlagUtil::countRightOnBit(u32 x, int bit)
{
SEAD_ASSERT(static_cast<u32>(bit) < sizeof(u32) * 8);
const u32 mask = ((1u << bit) - 1) | (1u << bit);
return countOnBit(x & mask);
}
int BitFlagUtil::findOnBitFromRight(u32 x, int num)
{
SEAD_ASSERT(num > 0);
if (!x)
return -1;
while (--num > 0)
{
x &= x - 1;
if (!x)
return -1;
}
return countContinuousOffBitFromRight(x);
}
int BitFlagUtil::countRightOnBit64(u64 x, int bit)
{
SEAD_ASSERT(static_cast<u64>(bit) < sizeof(u64) * 8);
const u64 mask = ((1ull << bit) - 1) | (1ull << bit);
return countOnBit64(x & mask);
}
int BitFlagUtil::findOnBitFromRight64(u64 x, int num)
{
SEAD_ASSERT(num > 0);
if (!x)
return -1;
while (--num > 0)
{
x &= x - 1;
if (!x)
return -1;
}
return countContinuousOffBitFromRight64(x);
}
} // namespace sead
+60
View File
@@ -0,0 +1,60 @@
#include <prim/seadEndian.h>
namespace
{
u8 Swap8(u8 val)
{
return val;
}
u8 Null8(u8 val)
{
return val;
}
u16 Swap16(u16 val)
{
return (val << 8 | val >> 8) & 0xFFFF;
}
u16 Null16(u16 val)
{
return val;
}
u32 Swap32(u32 val)
{
return val << 24 | (val & 0xFF00) << 8 | val >> 24 | (val >> 8 & 0xFF00);
}
u32 Null32(u32 val)
{
return val;
}
u64 Swap64(u64 val)
{
// Couldn't make an implementation that matches the original assembly
// But this should be much more efficient
return val << 56 | (val & 0xFF00) << 40 | (val & 0xFF0000) << 24 | (val & 0xFF000000) << 8 |
val >> 56 | (val >> 40 & 0xFF00) | (val >> 24 & 0xFF0000) | (val >> 8 & 0xFF000000);
}
u64 Null64(u64 val)
{
return val;
}
} // anonymous namespace
namespace sead
{
const Endian::Types Endian::cHostEndian = Endian::markToEndian(0xfeff);
const Endian::ConvFuncTable Endian::cConvFuncTable = {
{&Null8, &Swap8},
{&Null16, &Swap16},
{&Null32, &Swap32},
{&Null64, &Swap64},
};
} // namespace sead
+123
View File
@@ -0,0 +1,123 @@
#include <basis/seadRawPrint.h>
#include <prim/seadEnum.h>
#include <thread/seadCriticalSection.h>
namespace
{
class EnumParseTextCriticalSection
{
public:
sead::CriticalSection* getObject()
{
static sead::CriticalSection sObject;
return &sObject;
}
};
static EnumParseTextCriticalSection sEnumParseTextCriticalSection;
class EnumInitValueArrayCriticalSection
{
public:
sead::CriticalSection* getObject()
{
static sead::CriticalSection sObject;
return &sObject;
}
};
static EnumInitValueArrayCriticalSection sEnumInitValueArrayCriticalSection;
} // namespace
namespace sead
{
CriticalSection* EnumUtil::getParseTextCS_()
{
return sEnumParseTextCriticalSection.getObject();
}
CriticalSection* EnumUtil::getInitValueArrayCS_()
{
return sEnumInitValueArrayCriticalSection.getObject();
}
void ParseFailed_([[maybe_unused]] char** text_ptr, [[maybe_unused]] int v)
{
#ifdef SEAD_DEBUG
system::Print("----------------------------------------\n");
for (int i = 0; i < v; ++i)
system::Print(" text[%d] \"%s\"\n", i, text_ptr[i]);
system::Print("----------------------------------------\n");
SEAD_ASSERT_MSG(false, "SEAD_ENUM failed to parse text. Is number of comma correct?");
#endif
}
void EnumUtil::parseText_(char** text_ptr, char* text_all, int size)
{
int index = 0;
while (*text_all)
{
skipToWordStart_(&text_all);
if (*text_all == 0)
break;
text_ptr[index] = text_all;
++index;
char* next;
skipToWordEnd_(&text_all, &next);
const char next_char = *next;
*text_all = 0;
if (next_char == '=')
{
while (!(*++next == '\0' || *next == ',' || *next == '='))
;
if (*next == '\0')
break;
}
else if (next_char == '\0')
{
break;
}
// TODO: This is missing a call to skipToWordEnd_ and ParseFailed_ for the debug/develop
// targets.
if (index >= size)
break;
text_all = ++next;
}
if (index != size)
ParseFailed_(text_ptr, index);
}
// Example:
// AoCVerAtLastPlay ,LatestAoCVerPlayed
// ^ ^ ^
// initial p | next (p_next)
// end (p_ptr)
void EnumUtil::skipToWordEnd_(char** p_ptr, char** p_next)
{
char* p = *p_ptr;
while (!(*p == '\0' || *p == ',' || *p == '='))
++p;
*p_next = p;
--p;
while ((*p == '\t' || *p == '\n' || *p == ' ') && intptr_t(p) > intptr_t(*p_ptr))
--p;
*p_ptr = p + 1;
}
void EnumUtil::skipToWordStart_(char** p_ptr)
{
char* p = *p_ptr;
while (*p == '\t' || *p == '\n' || *p == ' ' || *p == ',')
++p;
*p_ptr = p;
}
} // namespace sead
+45
View File
@@ -0,0 +1,45 @@
#include <basis/seadRawPrint.h>
#include <heap/seadHeapMgr.h>
#include <prim/seadMemUtil.h>
#include <prim/seadPtrUtil.h>
namespace sead
{
void* MemUtil::copyAlign32(void* dst, const void* src, size_t size)
{
SEAD_ASSERT_MSG(size % 32 == 0, "size %% 32 == 0 size:%zu", size);
SEAD_ASSERT_MSG(PtrUtil::isAligned(dst, 32) && PtrUtil::isAligned(src, 32),
"pointer must be 32-byte aligned. src:%p -> dst:%p", src, dst);
return copy(dst, src, size);
}
// TODO: MemUtil::isStack (in platform specific .cpp)
bool MemUtil::isHeap(const void* addr)
{
return HeapMgr::instance() && HeapMgr::isContainedInAnyHeap(addr);
}
// NON_MATCHING: Clang optimizes the if (ptr) return false; into if (ptr) return ptr;
bool MemUtil::checkFillType(const void* ptr_, size_t size)
{
const u8* ptr = static_cast<const u8*>(ptr_);
if (!ptr)
return false;
const u8 value = *ptr;
if (size == 0)
return value == 0;
size_t i = 1;
while (i < size)
{
if (value != ptr[i++])
return false;
}
return value == 0;
}
// TODO: MemUtil::dumpMemoryBinary
} // namespace sead
@@ -0,0 +1,336 @@
#include <prim/seadSafeString.h>
#include <prim/seadStringUtil.h>
namespace
{
static const char16 cEmptyStringChar16[1] = u"";
} // namespace
namespace sead
{
template <>
const char SafeStringBase<char>::cNullChar = '\0';
template <>
const char SafeStringBase<char>::cLineBreakChar = '\n';
template <>
const SafeStringBase<char> SafeStringBase<char>::cEmptyString("");
template <>
const char16 SafeStringBase<char16>::cNullChar = 0;
template <>
const char16 SafeStringBase<char16>::cLineBreakChar = static_cast<char16>('\n');
template <>
const SafeStringBase<char16> SafeStringBase<char16>::cEmptyString(cEmptyStringChar16);
template <>
SafeStringBase<char>& SafeStringBase<char>::operator=(const SafeStringBase<char>& other) = default;
template <>
SafeStringBase<char16>&
SafeStringBase<char16>::operator=(const SafeStringBase<char16>& other) = default;
template <>
BufferedSafeStringBase<char>&
BufferedSafeStringBase<char>::operator=(const SafeStringBase<char>& other)
{
copy(other);
return *this;
}
template <>
BufferedSafeStringBase<char16>&
BufferedSafeStringBase<char16>::operator=(const SafeStringBase<char16>& other)
{
copy(other);
return *this;
}
template <>
HeapSafeStringBase<char>& HeapSafeStringBase<char>::operator=(const SafeStringBase<char>& other)
{
this->copy(other);
return *this;
}
template <>
HeapSafeStringBase<char16>&
HeapSafeStringBase<char16>::operator=(const SafeStringBase<char16>& other)
{
this->copy(other);
return *this;
}
template <>
void BufferedSafeStringBase<char>::assureTerminationImpl_() const
{
auto* mutableSafeString = const_cast<BufferedSafeStringBase<char>*>(this);
mutableSafeString->getMutableStringTop_()[mBufferSize - 1] = cNullChar;
}
template <>
void BufferedSafeStringBase<char16>::assureTerminationImpl_() const
{
auto* mutableSafeString = const_cast<BufferedSafeStringBase<char16>*>(this);
mutableSafeString->getMutableStringTop_()[mBufferSize - 1] = cNullChar;
}
template <>
s32 BufferedSafeStringBase<char>::formatImpl_(char* s, s32 n, const char* formatStr, va_list args)
{
const s32 ret = StringUtil::vsnprintf(s, n, formatStr, args);
return ret < 0 ? n - 1 : ret;
}
template <>
s32 BufferedSafeStringBase<char16>::formatImpl_(char16* s, s32 n, const char16* formatStr,
va_list args)
{
const s32 ret = StringUtil::vsnw16printf(s, n, formatStr, args);
if (ret >= 0 && ret < n)
return ret;
s[n - 1] = WSafeString::cNullChar;
return n - 1;
}
template <>
s32 BufferedSafeStringBase<char>::formatV(const char* formatStr, va_list args)
{
char* mutableString = getMutableStringTop_();
return formatImpl_(mutableString, mBufferSize, formatStr, args);
}
template <>
s32 BufferedSafeStringBase<char16>::formatV(const char16* formatStr, va_list args)
{
char16* mutableString = getMutableStringTop_();
return formatImpl_(mutableString, mBufferSize, formatStr, args);
}
template <>
s32 BufferedSafeStringBase<char>::format(const char* formatStr, ...)
{
va_list args;
va_start(args, formatStr);
s32 ret = formatV(formatStr, args);
va_end(args);
return ret;
}
template <>
s32 BufferedSafeStringBase<char16>::format(const char16* formatStr, ...)
{
va_list args;
va_start(args, formatStr);
s32 ret = formatV(formatStr, args);
va_end(args);
return ret;
}
template <>
s32 BufferedSafeStringBase<char>::appendWithFormatV(const char* format, std::va_list args)
{
char* mutableString = getMutableStringTop_();
const s32 len = calcLength();
return formatImpl_(mutableString + len, mBufferSize - len, format, args) + len;
}
template <>
s32 BufferedSafeStringBase<char16>::appendWithFormatV(const char16* format, std::va_list args)
{
char16* mutableString = getMutableStringTop_();
const s32 len = calcLength();
return formatImpl_(mutableString + len, mBufferSize - len, format, args) + len;
}
template <>
s32 BufferedSafeStringBase<char>::appendWithFormat(const char* format, ...)
{
std::va_list args;
va_start(args, format);
const s32 ret = appendWithFormatV(format, args);
va_end(args);
return ret;
}
template <>
s32 BufferedSafeStringBase<char16>::appendWithFormat(const char16* format, ...)
{
std::va_list args;
va_start(args, format);
const s32 ret = appendWithFormatV(format, args);
va_end(args);
return ret;
}
// NON_MATCHING
template <typename T>
s32 replaceStringImpl_(T* dst, s32* length, s32 dst_size, const T* src, s32 src_size,
const SafeStringBase<T>& old_str, const SafeStringBase<T>& new_str,
bool* is_buffer_overflow)
{
s32 ret = 0;
*is_buffer_overflow = false;
const s32 dst_max_idx = dst_size - 1;
const T* old_cstr = old_str.cstr();
const s32 old_str_len = old_str.calcLength();
if (old_str_len == 0)
{
if (dst == src)
return 0;
*is_buffer_overflow = src_size >= dst_size;
if (src_size >= dst_size)
{
MemUtil::copy(dst, src, dst_max_idx);
dst[dst_max_idx] = SafeStringBase<T>::cNullChar;
if (length)
*length = dst_max_idx;
}
else
{
MemUtil::copy(dst, src, src_size + 1);
if (length)
*length = src_size;
}
return 0;
}
const T* new_cstr = new_str.cstr();
const s32 new_str_len = new_str.calcLength();
// Replace in-place.
if (dst == src && old_str_len < new_str_len)
{
s32 dst_final_size = 0;
s32 src_final_size = 0;
// First, terminate the string and check for buffer overflow.
while (src_final_size < src_size)
{
const s32 cmp = MemUtil::compare(&dst[src_final_size], old_cstr, old_str_len);
src_final_size += cmp == 0 ? old_str_len : 1;
dst_final_size += cmp == 0 ? new_str_len : 1;
if (dst_final_size >= dst_size)
{
*is_buffer_overflow = true;
break;
}
}
if (*is_buffer_overflow)
{
dst[dst_max_idx] = SafeStringBase<T>::cNullChar;
if (length)
*length = dst_max_idx;
}
else
{
dst[dst_final_size] = SafeStringBase<T>::cNullChar;
if (length)
*length = dst_final_size;
}
s32 dst_i = dst_final_size - 1;
s32 src_i = src_final_size - 1;
while (src_i >= 0)
{
const s32 cmp = MemUtil::compare(&dst[src_i + 1 - old_str_len], old_cstr, old_str_len);
if (cmp == 0)
{
dst_i -= new_str_len;
const s32 copy_size = std::min(new_str_len, dst_size - 2 - dst_i);
if (copy_size > 0)
{
MemUtil::copy(&dst[dst_i + 1], new_cstr, copy_size);
ret += 1;
}
src_i -= old_str_len;
}
else
{
if (dst_i < dst_max_idx)
dst[dst_i] = dst[src_i];
if (src_i < 1)
{
--src_i;
--dst_i;
break;
}
}
}
SEAD_ASSERT(dst_i == -1);
SEAD_ASSERT(src_i == -1);
}
// Simpler case.
else
{
s32 target_i = 0;
s32 buffer_i = 0;
while (target_i < src_size)
{
const s32 cmp = MemUtil::compare(&src[target_i], old_cstr, old_str_len);
// Not old_str, copy one character to the buffer.
if (cmp != 0)
{
if (buffer_i < dst_max_idx)
{
dst[buffer_i++] = src[target_i++];
continue;
}
}
// Found old_str, copy new_str to the buffer.
else
{
const s32 copy_size = std::min(new_str_len, dst_max_idx - buffer_i);
if (copy_size >= 1)
MemUtil::copy(&dst[buffer_i], new_cstr, copy_size);
ret += new_str_len == 0 || copy_size > 0;
if (copy_size >= new_str_len)
{
buffer_i += new_str_len;
target_i += old_str_len;
continue;
}
}
// Buffer overflow.
*is_buffer_overflow = true;
dst[dst_max_idx] = SafeStringBase<T>::cNullChar;
if (length)
*length = dst_max_idx;
return ret;
}
SEAD_ASSERT(buffer_i <= dst_size);
SEAD_ASSERT(target_i == src_size);
dst[buffer_i] = SafeStringBase<T>::cNullChar;
if (length)
*length = buffer_i;
}
return ret;
}
template s32 replaceStringImpl_<char>(char* buffer, s32* length, s32 buffer_size,
const char* target_buf, s32 target_len,
const SafeStringBase<char>& old_str,
const SafeStringBase<char>& new_str,
bool* is_buffer_overflow);
template s32 replaceStringImpl_<char16>(char16* buffer, s32* length, s32 buffer_size,
const char16* target_buf, s32 target_len,
const SafeStringBase<char16>& old_str,
const SafeStringBase<char16>& new_str,
bool* is_buffer_overflow);
} // namespace sead
@@ -0,0 +1,912 @@
#include "prim/seadStringBuilder.h"
#include "heap/seadHeapMgr.h"
#include "math/seadMathCalcCommon.h"
#include "prim/seadPtrUtil.h"
#include "prim/seadStringUtil.h"
namespace sead
{
template <>
StringBuilder* StringBuilder::create(s32 buffer_size, Heap* heap, s32 alignment)
{
return createImpl_(buffer_size, heap, alignment);
}
template <>
WStringBuilder* WStringBuilder::create(s32 buffer_size, Heap* heap, s32 alignment)
{
return createImpl_(buffer_size, heap, alignment);
}
template <typename T>
StringBuilderBase<T>* StringBuilderBase<T>::create(const T* str, Heap* heap, s32 alignment)
{
const s32 len = calcStrLength_(str);
auto* builder = createImpl_(len + 1, heap, alignment);
builder->copy(str, len);
return builder;
}
template StringBuilder* StringBuilder::create(const char* str, Heap* heap, s32 alignment);
template WStringBuilder* WStringBuilder::create(const char16* str, Heap* heap, s32 alignment);
template <typename T>
StringBuilderBase<T>* StringBuilderBase<T>::createImpl_(s32 buffer_size, Heap* heap, s32 alignment)
{
if (buffer_size <= 0)
{
SEAD_ASSERT_MSG(false, "buffer_size[%d] must be larger than 0", buffer_size);
return nullptr;
}
if (!heap)
heap = HeapMgr::instance()->getCurrentHeap();
if (alignment > s32(alignof(StringBuilderBase<T>)))
{
const s32 buffer_offset = Mathi::roundUpPow2(sizeof(StringBuilderBase<T>), alignment);
void* buffer = heap->alloc(buffer_offset + buffer_size * sizeof(T), alignment);
return new (buffer) StringBuilderBase<T>(
static_cast<T*>(PtrUtil::addOffset(buffer, buffer_offset)), buffer_size);
}
else
{
void* buffer = heap->alloc(buffer_size * sizeof(T) + sizeof(StringBuilderBase<T>),
alignof(StringBuilderBase<T>));
return new (buffer) StringBuilderBase<T>(
static_cast<T*>(PtrUtil::addOffset(buffer, sizeof(StringBuilderBase<T>))), buffer_size);
}
}
template <typename T>
bool StringBuilderBase<T>::endsWith(const T* suffix) const
{
const s32 sub_str_len = calcStrLength_(suffix);
if (sub_str_len == 0)
return true;
const T* strc = mBuffer;
const s32 len = calcLength();
if (len < sub_str_len)
return false;
for (s32 i = 0; i < sub_str_len; ++i)
{
if (strc[len - sub_str_len + i] != suffix[i])
return false;
}
return true;
}
template bool StringBuilder::endsWith(const char* suffix) const;
template bool WStringBuilder::endsWith(const char16* suffix) const;
template <typename T>
s32 StringBuilderBase<T>::copy(const T* src, s32 copy_length)
{
T* dst = mBuffer;
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(src, "str must not be null");
if (dst == src)
return 0;
if (copy_length == -1)
copy_length = calcStrLength_(src);
if (copy_length >= buffer_size)
{
SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Copy Size: %d)", buffer_size,
copy_length);
copy_length = buffer_size - 1;
}
if (copy_length >= 1)
{
MemUtil::copy(dst, src, copy_length * sizeof(T));
dst[copy_length] = SafeStringBase<T>::cNullChar;
}
else
{
copy_length = 0;
*dst = SafeStringBase<T>::cNullChar;
}
mLength = copy_length;
return copy_length;
}
template s32 StringBuilder::copy(const char* src, s32 copy_length);
template s32 WStringBuilder::copy(const char16* src, s32 copy_length);
template <typename T>
s32 StringBuilderBase<T>::copyAt(s32 at_, const T* src, s32 copy_length)
{
T* dst = getMutableStringTop_();
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(src, "str must not be null");
if (copy_length == -1)
copy_length = calcStrLength_(src);
s32 len = this->calcLength();
s32 at = at_;
if (at_ < 0)
{
const s32 at_new = len + at_ + 1;
if (at_new < 0)
{
SEAD_ASSERT_MSG(false, "at(%d) out of range[%d, %d]", at_, -len - 1, len);
at = 0;
goto check_buffer_overflow;
}
at = at_new;
}
if (len < at)
{
SEAD_ASSERT_MSG(false, "at(%d) out of range[%d, %d]", at_, -len - 1, len);
copy_length = 0;
return copy_length;
}
check_buffer_overflow:
if (at + copy_length >= buffer_size)
{
SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, At: %d, Copy Length: %d)",
buffer_size, at, copy_length);
copy_length = buffer_size - at - 1;
}
if (copy_length < 1)
return 0;
MemUtil::copy(dst + at, src, copy_length * sizeof(T));
if (mLength < at + copy_length)
dst[at + copy_length] = SafeStringBase<T>::cNullChar;
if (mLength < at + copy_length)
mLength = at + copy_length;
return copy_length;
}
template s32 StringBuilder::copyAt(s32 at, const char* src, s32 copy_length);
template s32 WStringBuilder::copyAt(s32 at, const char16* src, s32 copy_length);
template <typename T>
s32 StringBuilderBase<T>::cutOffCopy(const T* src, s32 copy_length)
{
T* dst = mBuffer;
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(src, "str must not be null");
if (dst == src)
return 0;
if (copy_length == -1)
copy_length = calcStrLength_(src);
if (copy_length >= buffer_size)
copy_length = buffer_size - 1;
if (copy_length >= 1)
{
MemUtil::copy(dst, src, copy_length * sizeof(T));
dst[copy_length] = SafeStringBase<T>::cNullChar;
}
else
{
copy_length = 0;
*dst = SafeStringBase<T>::cNullChar;
}
mLength = copy_length;
return copy_length;
}
template s32 StringBuilder::cutOffCopy(const char* src, s32 copy_length);
template s32 WStringBuilder::cutOffCopy(const char16* src, s32 copy_length);
template <typename T>
s32 StringBuilderBase<T>::cutOffCopyAt(s32 at_, const T* src, s32 copy_length)
{
T* dst = getMutableStringTop_();
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(src, "str must not be null");
if (copy_length == -1)
copy_length = calcStrLength_(src);
s32 len = this->calcLength();
s32 at = at_;
if (at_ < 0)
{
const s32 at_new = len + at_ + 1;
if (at_new < 0)
{
SEAD_ASSERT_MSG(false, "at(%d) out of range[%d, %d]", at_, -len - 1, len);
at = 0;
goto check_buffer_overflow;
}
at = at_new;
}
if (len < at)
{
SEAD_ASSERT_MSG(false, "at(%d) out of range[%d, %d]", at_, -len - 1, len);
copy_length = 0;
return copy_length;
}
check_buffer_overflow:
if (at + copy_length >= buffer_size)
copy_length = buffer_size - at - 1;
if (copy_length < 1)
return 0;
MemUtil::copy(dst + at, src, copy_length * sizeof(T));
if (mLength < at + copy_length)
dst[at + copy_length] = SafeStringBase<T>::cNullChar;
if (mLength < at + copy_length)
mLength = at + copy_length;
return copy_length;
}
template s32 StringBuilder::cutOffCopyAt(s32 at, const char* src, s32 copy_length);
template s32 WStringBuilder::cutOffCopyAt(s32 at, const char16* src, s32 copy_length);
template <typename T>
s32 StringBuilderBase<T>::copyAtWithTerminate(s32 at_, const T* src, s32 copy_length)
{
T* dst = getMutableStringTop_();
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(src, "str must not be null");
if (copy_length == -1)
copy_length = calcStrLength_(src);
s32 len = this->calcLength();
s32 at = at_;
if (at_ < 0)
{
const s32 at_new = len + at_ + 1;
if (at_new < 0)
{
SEAD_ASSERT_MSG(false, "at(%d) out of range[%d, %d]", at_, -len - 1, len);
at = 0;
goto check_buffer_overflow;
}
at = at_new;
}
if (len < at)
{
SEAD_ASSERT_MSG(false, "at(%d) out of range[%d, %d]", at_, -len - 1, len);
copy_length = 0;
return copy_length;
}
check_buffer_overflow:
if (at + copy_length >= buffer_size)
{
SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, At: %d, Copy Length: %d)",
buffer_size, at, copy_length);
copy_length = buffer_size - at - 1;
}
if (copy_length < 1)
return 0;
MemUtil::copy(dst + at, src, copy_length * sizeof(T));
dst[at + copy_length] = SafeStringBase<T>::cNullChar;
if (at <= mLength)
mLength = at + copy_length;
return copy_length;
}
template s32 StringBuilder::copyAtWithTerminate(s32 at, const char* src, s32 copy_length);
template s32 WStringBuilder::copyAtWithTerminate(s32 at, const char16* src, s32 copy_length);
template <typename T>
s32 StringBuilderBase<T>::format(const T* format, ...)
{
std::va_list args;
va_start(args, format);
s32 ret = formatV(format, args);
va_end(args);
return ret;
}
template s32 StringBuilder::format(const char* format, ...);
template s32 WStringBuilder::format(const char16* format, ...);
template <>
s32 StringBuilder::formatImpl_(char* s, s32 n, const char* format, va_list args)
{
const s32 ret = StringUtil::vsnprintf(s, n, format, args);
return ret < 0 ? n - 1 : ret;
}
template <>
s32 WStringBuilder::formatImpl_(char16* s, s32 n, const char16* format, va_list args)
{
const s32 ret = StringUtil::vsnw16printf(s, n, format, args);
if (ret >= 0 && ret < n)
return ret;
s[n - 1] = WSafeString::cNullChar;
return n - 1;
}
template <typename T>
s32 StringBuilderBase<T>::appendWithFormat(const T* format, ...)
{
std::va_list args;
va_start(args, format);
const s32 ret = appendWithFormatV(format, args);
va_end(args);
return ret;
}
template s32 StringBuilder::appendWithFormat(const char* format, ...);
template s32 WStringBuilder::appendWithFormat(const char16* format, ...);
template <typename T>
s32 StringBuilderBase<T>::append(const T* str, s32 append_length)
{
T* dst = getMutableStringTop_();
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(str, "str must not be null");
if (append_length == -1)
append_length = calcStrLength_(str);
const s32 at = this->calcLength();
if (at + append_length >= buffer_size)
{
SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, At: %d, Str Length: %d)",
buffer_size, at, append_length);
append_length = buffer_size - at - 1;
}
if (append_length < 1)
return 0;
MemUtil::copy(dst + at, str, append_length * sizeof(T));
dst[at + append_length] = SafeStringBase<T>::cNullChar;
mLength = at + append_length;
return append_length;
}
template s32 StringBuilder::append(const char* str, s32 append_length);
template s32 WStringBuilder::append(const char16* str, s32 append_length);
// NON_MATCHING: regalloc differences
template <typename T>
s32 appendImpl_(T* buffer_, s32* length_, const s32 buffer_size_, T c, s32 num)
{
const s32 length = *length_;
if (buffer_size_ <= num + length)
{
SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d, Num: %d)",
buffer_size_, length, num);
num = buffer_size_ - length - 1;
}
for (s32 i = 0; i < num; ++i)
buffer_[length + i] = c;
buffer_[length + num] = SafeStringBase<T>::cNullChar;
*length_ = length + num;
return num;
}
template <typename T>
s32 StringBuilderBase<T>::append(T c, s32 num)
{
if (num < 0)
{
SEAD_ASSERT_MSG(false, "append error. num < 0, num = %d", num);
return 0;
}
if (num == 0)
return 0;
return appendImpl_(mBuffer, &mLength, mBufferSize, c, num);
}
template s32 StringBuilder::append(char c, s32 n);
template s32 WStringBuilder::append(char16 c, s32 n);
template <typename T>
s32 StringBuilderBase<T>::chop(s32 chop_num)
{
s32 length = this->calcLength();
T* buffer = getMutableStringTop_();
const auto fail = [=] {
SEAD_ASSERT_MSG(false, "chop_num(%d) out of range[0, %d]", chop_num, length);
};
if (chop_num < 0)
{
fail();
return 0;
}
if (chop_num > length)
{
fail();
length = mLength;
chop_num = mLength;
}
const s32 new_length = length - chop_num;
buffer[new_length] = SafeStringBase<T>::cNullChar;
mLength = new_length;
return chop_num;
}
template s32 StringBuilder::chop(s32 chop_num);
template s32 WStringBuilder::chop(s32 chop_num);
template <typename T>
s32 StringBuilderBase<T>::chopMatchedChar(T c)
{
const s32 length = this->calcLength();
if (length < 1)
return 0;
const s32 new_length = length - 1;
if (mBuffer[new_length] == c)
{
mBuffer[new_length] = SafeStringBase<T>::cNullChar;
mLength = new_length;
return 1;
}
return 0;
}
template s32 StringBuilder::chopMatchedChar(char c);
template s32 WStringBuilder::chopMatchedChar(char16 c);
template <typename T>
s32 StringBuilderBase<T>::chopMatchedChar(const T* characters)
{
const s32 length = this->calcLength();
if (length < 1)
return 0;
T* buffer = getMutableStringTop_();
for (const T* it = characters; *it; ++it)
{
if (buffer[length - 1] == *it)
{
buffer[length - 1] = SafeStringBase<T>::cNullChar;
mLength = length - 1;
return 1;
}
}
return 0;
}
template s32 StringBuilder::chopMatchedChar(const char* characters);
template s32 WStringBuilder::chopMatchedChar(const char16* characters);
template <typename T>
s32 StringBuilderBase<T>::chopUnprintableAsciiChar()
{
const s32 length = this->calcLength();
if (length < 1)
return 0;
const s32 new_length = length - 1;
if (mBuffer[new_length] <= ' ' || mBuffer[new_length] == 0x7F)
{
mBuffer[new_length] = SafeStringBase<T>::cNullChar;
mLength = new_length;
return 1;
}
return 0;
}
template s32 StringBuilder::chopUnprintableAsciiChar();
template s32 WStringBuilder::chopUnprintableAsciiChar();
template <typename T>
s32 StringBuilderBase<T>::rstrip(const T* characters)
{
const s32 length = this->calcLength();
if (length <= 0)
return 0;
T* buffer = mBuffer;
s32 new_length = length;
const auto should_strip = [characters, buffer](s32 idx) {
for (auto it = characters; *it; ++it)
{
if (buffer[idx] == *it)
return true;
}
return false;
};
while (new_length >= 1 && should_strip(new_length - 1))
--new_length;
if (length <= new_length)
return 0;
mBuffer[new_length] = SafeStringBase<T>::cNullChar;
mLength = new_length;
return length - new_length;
}
template s32 StringBuilder::rstrip(const char* characters);
template s32 WStringBuilder::rstrip(const char16* characters);
// NON_MATCHING: equivalent, two instruction reorders
template <typename T>
s32 StringBuilderBase<T>::rstripUnprintableAsciiChars()
{
const s32 length = this->calcLength();
if (length <= 0)
return 0;
T* buffer = mBuffer;
s32 new_length = length;
while (new_length >= 1 && (buffer[new_length - 1] <= 0x20 || buffer[new_length - 1] == 0x7F))
--new_length;
if (length <= new_length)
return 0;
const s32 ret = length - new_length;
mBuffer[new_length] = SafeStringBase<T>::cNullChar;
mLength = new_length;
return ret;
}
template s32 StringBuilder::rstripUnprintableAsciiChars();
template s32 WStringBuilder::rstripUnprintableAsciiChars();
template <typename T>
s32 StringBuilderBase<T>::trim(s32 trim_length)
{
T* mutableString = getMutableStringTop_();
if (trim_length >= mBufferSize)
{
SEAD_ASSERT_MSG(false, "trim_length(%d) out of bounds. [0, %d)", trim_length, mBufferSize);
return this->calcLength();
}
if (trim_length < 0)
{
SEAD_ASSERT_MSG(false, "trim_length(%d) out of bounds. [0, %d)", trim_length, mBufferSize);
trim_length = 0;
}
mutableString[trim_length] = SafeStringBase<T>::cNullChar;
if (trim_length < mLength)
mLength = trim_length;
return trim_length;
}
template s32 StringBuilder::trim(s32 trim_length);
template s32 WStringBuilder::trim(s32 trim_length);
template <typename T>
s32 StringBuilderBase<T>::trimMatchedString(const T* str)
{
T* buffer = getMutableStringTop_();
const s32 length = this->calcLength();
const s32 trim_str_length = calcStrLength_(str);
const s32 new_length = length - trim_str_length;
if (length < trim_str_length)
return length;
T* substring = &buffer[new_length];
for (s32 i = 0; i < trim_str_length; ++i)
{
if (substring[i] != str[i])
return length;
}
buffer[new_length] = SafeStringBase<T>::cNullChar;
mLength = new_length;
return new_length;
}
template s32 StringBuilder::trimMatchedString(const char* str);
template s32 WStringBuilder::trimMatchedString(const char16* str);
template <typename T>
s32 StringBuilderBase<T>::replaceChar(T old_char, T new_char)
{
const s32 length = this->calcLength();
T* buffer = getMutableStringTop_();
s32 replaced_count = 0;
for (s32 i = 0; i < length; ++i)
{
if (buffer[i] == old_char)
{
++replaced_count;
buffer[i] = new_char;
}
}
return replaced_count;
}
template s32 StringBuilder::replaceChar(char old_char, char new_char);
template s32 WStringBuilder::replaceChar(char16 old_char, char16 new_char);
template <typename T>
s32 StringBuilderBase<T>::replaceCharList(const SafeStringBase<T>& old_chars,
const SafeStringBase<T>& new_chars)
{
T* buffer = getMutableStringTop_();
const s32 length = this->calcLength();
s32 old_chars_len = old_chars.calcLength();
const s32 new_chars_len = new_chars.calcLength();
if (old_chars_len != new_chars_len)
{
// Nintendo's code just uses the same format string for both T = char and T = char16_t,
// which is undefined behavior and produces annoying format warnings, so let's fix it...
if constexpr (std::is_same<T, char>())
{
SEAD_ASSERT_MSG(false, "old_chars(%s).length is not equal to new_chars(%s).length.",
old_chars.cstr(), new_chars.cstr());
}
else if constexpr (std::is_same<T, char16>())
{
// There is no standard format specifier for char16_t strings :/
SEAD_ASSERT_MSG(false, "old_chars(%p).length is not equal to new_chars(%p).length.",
old_chars.cstr(), new_chars.cstr());
}
if (old_chars_len > new_chars_len)
old_chars_len = new_chars_len;
}
const T* old_chars_c = old_chars.cstr();
const T* new_chars_c = new_chars.cstr();
if (length < 1)
return 0;
s32 replaced_count = 0;
for (s32 i = 0; i < length; ++i)
{
for (s32 character_idx = 0; character_idx < old_chars_len; ++character_idx)
{
if (buffer[i] == old_chars_c[character_idx])
{
++replaced_count;
buffer[i] = new_chars_c[character_idx];
break;
}
}
}
return replaced_count;
}
template s32 StringBuilder::replaceCharList(const SafeString& old_chars,
const SafeString& new_chars);
template s32 WStringBuilder::replaceCharList(const WSafeString& old_chars,
const WSafeString& new_chars);
template <typename T>
template <typename OtherType>
s32 StringBuilderBase<T>::convertFromOtherType_(const OtherType* src, s32 src_size)
{
T* dst = mBuffer;
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(src, "str must not be null");
s32 copy_size = src_size;
if (src_size == -1)
copy_size = calcStrLength_(src);
if (copy_size >= buffer_size)
{
SEAD_ASSERT_MSG(false, "str_length(%d) out of bounds. [0, %d) \n", src_size, buffer_size);
copy_size = buffer_size - 1;
}
if (copy_size <= 0)
{
copy_size = 0;
*dst = SafeStringBase<T>::cNullChar;
}
else
{
for (s32 i = 0; i < copy_size; ++i)
dst[i] = src[i];
dst[copy_size] = SafeStringBase<T>::cNullChar;
}
mLength = copy_size;
return copy_size;
}
template <typename T>
s32 StringBuilderBase<T>::convertFromMultiByteString(const char* str, s32 str_length)
{
if constexpr (std::is_same<char, T>())
return copy(str, str_length);
else
return convertFromOtherType_(str, str_length);
}
template <typename T>
s32 StringBuilderBase<T>::convertFromWideCharString(const char16* str, s32 str_length)
{
if constexpr (std::is_same<char16, T>())
return copy(str, str_length);
else
return convertFromOtherType_(str, str_length);
}
template s32 StringBuilder::convertFromMultiByteString(const char* str, s32 str_length);
template s32 StringBuilder::convertFromWideCharString(const char16* str, s32 str_length);
template s32 WStringBuilder::convertFromMultiByteString(const char* str, s32 str_length);
template s32 WStringBuilder::convertFromWideCharString(const char16* str, s32 str_length);
template <typename T>
s32 StringBuilderBase<T>::cutOffAppend(const T* str, s32 append_length)
{
T* dst = getMutableStringTop_();
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(str, "str must not be null");
if (append_length == -1)
append_length = calcStrLength_(str);
const s32 at = this->calcLength();
if (at + append_length >= buffer_size)
append_length = buffer_size - at - 1;
if (append_length < 1)
return 0;
MemUtil::copy(dst + at, str, append_length * sizeof(T));
dst[at + append_length] = SafeStringBase<T>::cNullChar;
mLength = at + append_length;
return append_length;
}
template s32 StringBuilder::cutOffAppend(const char* str, s32 append_length);
template s32 WStringBuilder::cutOffAppend(const char16* str, s32 append_length);
template <typename T>
s32 StringBuilderBase<T>::cutOffAppend(T c, s32 num)
{
if (num < 0)
{
SEAD_ASSERT_MSG(false, "append error. num < 0, num = %d", num);
return 0;
}
if (num == 0)
return 0;
const s32 buffer_size = mBufferSize;
const s32 length = mLength;
if (num + length >= buffer_size)
num = buffer_size - length - 1;
if (num <= 0)
return 0;
T* buffer = mBuffer;
for (s32 i = 0; i < num; ++i)
buffer[length + i] = c;
buffer[length + num] = SafeStringBase<T>::cNullChar;
mLength = length + num;
return num;
}
template s32 StringBuilder::cutOffAppend(char c, s32 num);
template s32 WStringBuilder::cutOffAppend(char16 c, s32 num);
// NON_MATCHING: operands to some `add` instructions are swapped
template <typename T>
s32 StringBuilderBase<T>::prepend(const T* str, s32 prepend_length)
{
T* buffer = getMutableStringTop_();
const s32 buffer_size = mBufferSize;
SEAD_ASSERT_MSG(str, "str must not be null");
if (prepend_length == -1)
prepend_length = calcStrLength_(str);
const s32 length = this->calcLength();
s32 move_length;
if (prepend_length >= buffer_size - length)
{
SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d, Prepend Length: %d)",
buffer_size, length, prepend_length);
if (prepend_length >= buffer_size)
prepend_length = buffer_size - 1;
move_length = buffer_size - 1 - prepend_length;
}
else
{
move_length = mLength;
}
void* dest = PtrUtil::addOffset(buffer, prepend_length * sizeof(T));
MemUtil::copyOverlap(dest, buffer, move_length * sizeof(T));
MemUtil::copy(buffer, str, prepend_length * sizeof(T));
buffer[move_length + prepend_length] = SafeStringBase<T>::cNullChar;
mLength = move_length + prepend_length;
return move_length + prepend_length - length;
}
template s32 StringBuilder::prepend(const char* str, s32 prepend_length);
template s32 WStringBuilder::prepend(const char16* str, s32 prepend_length);
// NON_MATCHING: same regalloc issue as append()
template <typename T>
s32 StringBuilderBase<T>::prepend(T c, s32 num)
{
if (num < 0)
{
// copy and paste error by Nintendo
SEAD_ASSERT_MSG(false, "append error. num < 0, num = %d", num);
return 0;
}
if (num == 0)
return 0;
const s32 length = mLength;
const s32 buffer_size = mBufferSize;
T* buffer = mBuffer;
s32 move_length = length;
if (buffer_size - length <= num)
{
SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d, Num: %d)",
buffer_size, length, num);
if (buffer_size <= num)
num = buffer_size - 1;
move_length = buffer_size - 1 - num;
}
MemUtil::copyOverlap(buffer + num, buffer, move_length * sizeof(T));
for (s32 i = 0; i < num; ++i)
buffer[i] = c;
buffer[num + move_length] = SafeStringBase<T>::cNullChar;
mLength = num + move_length;
return num + move_length - length;
}
template s32 StringBuilder::prepend(char c, s32 length);
template s32 WStringBuilder::prepend(char16_t c, s32 length);
} // namespace sead
@@ -0,0 +1,19 @@
#include <prim/seadStringUtil.h>
namespace sead::StringUtil
{
char16 replace(char16 c, const Buffer<const Char16Pair>& sorted_table)
{
if (sorted_table.size() == 0)
return c;
const s32 idx =
sorted_table.binarySearch(Char16Pair{c, 0}, [](const Char16Pair* p1, const Char16Pair* p2) {
return p1->before - p2->before;
});
if (idx < 0)
return c;
return sorted_table[idx].after;
}
} // namespace sead::StringUtil
@@ -0,0 +1,6 @@
#include "random/seadGlobalRandom.h"
namespace sead
{
SEAD_SINGLETON_DISPOSER_IMPL(GlobalRandom)
} // namespace sead
@@ -0,0 +1,59 @@
#include "random/seadRandom.h"
#include "time/seadTickTime.h"
namespace sead
{
void Random::init()
{
TickTime now;
init(static_cast<u32>(now.toTicks()));
}
void Random::init(u32 seed)
{
const u32 mt_constant = 0x6C078965;
mX = mt_constant * (seed ^ (seed >> 30u)) + 1;
mY = mt_constant * (mX ^ (mX >> 30u)) + 2;
mZ = mt_constant * (mY ^ (mY >> 30u)) + 3;
mW = mt_constant * (mZ ^ (mZ >> 30u)) + 4;
}
void Random::init(u32 seed_x, u32 seed_y, u32 seed_z, u32 seed_w)
{
if ((seed_x | seed_y | seed_z | seed_w) == 0)
{
SEAD_ASSERT_MSG(false, "seeds must not be all zero.");
seed_w = 0x48077044;
seed_z = 0x714ACB41;
seed_y = 0x6C078967;
seed_x = 1;
}
mX = seed_x;
mY = seed_y;
mZ = seed_z;
mW = seed_w;
}
u32 Random::getU32()
{
u32 x = mX ^ (mX << 11u);
mX = mY;
mY = mZ;
mZ = mW;
mW = mW ^ (mW >> 19u) ^ x ^ (x >> 8u);
return mW;
}
u64 Random::getU64()
{
return u64(getU32()) << 32u | getU32();
}
void Random::getContext(u32* x, u32* y, u32* z, u32* w) const
{
*x = mX;
*y = mY;
*z = mZ;
*w = mW;
}
} // namespace sead
@@ -0,0 +1,19 @@
#include <resource/seadArchiveRes.h>
namespace sead
{
s32 ArchiveRes::getLoadDataAlignment() const
{
return 0x80;
}
void ArchiveRes::doCreate_(u8* buf, u32, Heap*)
{
mEnable = prepareArchive_(buf);
}
bool ArchiveRes::isExistFileImpl_(const SafeString& path) const
{
return convertPathToEntryIDImpl_(path) != -1;
}
} // namespace sead
@@ -0,0 +1,140 @@
#include <filedevice/seadFileDeviceMgr.h>
#include <math/seadMathCalcCommon.h>
#include <prim/seadPtrUtil.h>
#include <resource/seadResource.h>
namespace sead
{
Resource::Resource() = default;
Resource::~Resource() = default;
DirectResource::DirectResource() = default;
DirectResource::~DirectResource()
{
if (mSettingFlag.isOnBit(0))
delete[] mRawData;
}
s32 DirectResource::getLoadDataAlignment() const
{
return 4;
}
void DirectResource::doCreate_(u8*, u32, Heap*) {}
void DirectResource::create(u8* buffer, u32 bufferSize, u32 allocSize, bool allocated, Heap* heap)
{
if (mRawData)
{
SEAD_ASSERT_MSG(false, "read twice");
return;
}
mRawData = buffer;
mRawSize = bufferSize;
mBufferSize = allocSize;
mSettingFlag.changeBit(0, allocated);
doCreate_(buffer, bufferSize, heap);
}
ResourceFactory::~ResourceFactory()
{
auto* mgr = ResourceMgr::instance();
if (mgr == nullptr)
return;
mgr->unregisterFactory(this);
if (mgr->getDefaultFactory() == this)
mgr->setDefaultFactory(nullptr);
}
Resource* DirectResourceFactoryBase::create(const ResourceMgr::CreateArg& createArg)
{
DirectResource* resource = newResource_(createArg.heap, createArg.alignment);
if (resource == nullptr)
{
SEAD_ASSERT_MSG(false, "resource new failed.");
return nullptr;
}
if (!PtrUtil::isAligned(createArg.buffer, resource->getLoadDataAlignment()))
{
SEAD_ASSERT_MSG(false, "buffer alignment invalid: %p, %d", createArg.buffer,
resource->getLoadDataAlignment());
delete resource;
return nullptr;
}
resource->create(createArg.buffer, createArg.file_size, createArg.buffer_size,
createArg.need_unload, createArg.heap);
return resource;
}
Resource* DirectResourceFactoryBase::tryCreate(const ResourceMgr::LoadArg& loadArg)
{
DirectResource* resource = newResource_(loadArg.instance_heap, loadArg.instance_alignment);
if (resource == nullptr)
return nullptr;
FileDevice::LoadArg fileLoadArg;
u8* data;
fileLoadArg.path = loadArg.path;
fileLoadArg.buffer = loadArg.load_data_buffer;
fileLoadArg.buffer_size = loadArg.load_data_buffer_size;
fileLoadArg.buffer_size_alignment = loadArg.load_data_buffer_alignment;
fileLoadArg.heap = loadArg.load_data_heap;
fileLoadArg.div_size = loadArg.div_size;
fileLoadArg.assert_on_alloc_fail = loadArg.assert_on_alloc_fail;
if (loadArg.load_data_alignment != 0)
fileLoadArg.alignment = loadArg.load_data_alignment;
else
fileLoadArg.alignment =
Mathi::sign(loadArg.instance_alignment) * resource->getLoadDataAlignment();
if (loadArg.device != NULL)
data = loadArg.device->tryLoad(fileLoadArg);
else
data = FileDeviceMgr::instance()->tryLoad(fileLoadArg);
if (data == NULL)
{
delete resource;
return NULL;
}
resource->create(data, fileLoadArg.read_size, fileLoadArg.roundup_size, fileLoadArg.need_unload,
loadArg.instance_heap);
return resource;
}
Resource* DirectResourceFactoryBase::tryCreateWithDecomp(const ResourceMgr::LoadArg& loadArg,
Decompressor* decompressor)
{
DirectResource* resource = newResource_(loadArg.instance_heap, loadArg.instance_alignment);
if (resource == NULL)
return NULL;
u32 outSize = 0;
u32 outAllocSize = 0;
bool outAllocated = false;
u8* data = decompressor->tryDecompFromDevice(loadArg, resource, &outSize, &outAllocSize,
&outAllocated);
if (!data)
{
delete resource;
return nullptr;
}
resource->create(data, outSize, outAllocSize, outAllocated, loadArg.instance_heap);
return resource;
}
} // namespace sead
@@ -0,0 +1,181 @@
#include <basis/seadRawPrint.h>
#include <filedevice/seadPath.h>
#include <heap/seadHeapMgr.h>
#include <resource/seadResource.h>
#include <resource/seadResourceMgr.h>
namespace sead
{
SEAD_SINGLETON_DISPOSER_IMPL(ResourceMgr)
ResourceMgr::ResourceMgr()
{
if (HeapMgr::sInstancePtr == NULL)
{
SEAD_ASSERT_MSG(false, "ResourceMgr need HeapMgr");
return;
}
mNullResourceFactory =
new (HeapMgr::sInstancePtr->findContainHeap(this)) DirectResourceFactory<DirectResource>();
mDefaultResourceFactory = mNullResourceFactory;
registerFactory(mNullResourceFactory, "");
}
ResourceMgr::~ResourceMgr()
{
if (mNullResourceFactory == NULL)
return;
delete mNullResourceFactory;
mNullResourceFactory = NULL;
}
// NON_MATCHING: tail call for factory->create
Resource* ResourceMgr::create(const ResourceMgr::CreateArg& arg)
{
if (!arg.buffer)
{
SEAD_ASSERT_MSG(false, "buffer null");
return nullptr;
}
if (arg.file_size == 0)
{
SEAD_ASSERT_MSG(false, "file_size is 0");
return nullptr;
}
if (arg.buffer_size == 0)
{
SEAD_ASSERT_MSG(false, "buffer_size is 0");
return nullptr;
}
if (arg.factory)
return arg.factory->create(arg);
auto* factory = findFactory(arg.ext);
if (factory)
return factory->create(arg);
SEAD_ASSERT_MSG(false, "factory not found: %s", arg.ext.cstr());
return nullptr;
}
void ResourceMgr::registerFactory(ResourceFactory* factory, const SafeString& name)
{
factory->setExt(name);
mFactoryList.pushBack(factory);
}
ResourceFactory* ResourceMgr::setDefaultFactory(ResourceFactory* factory)
{
ResourceFactory* const previous_default = mDefaultResourceFactory;
if (!factory)
factory = mNullResourceFactory;
mDefaultResourceFactory = factory;
registerFactory(factory, "");
return previous_default;
}
ResourceFactory* ResourceMgr::findFactory(const SafeString& name)
{
for (auto& factory : mFactoryList)
if (factory->getExt() == name)
return factory;
return mDefaultResourceFactory;
}
void ResourceMgr::registerDecompressor(Decompressor* decompressor, const SafeString& name)
{
if (!name.isEqual(SafeString::cEmptyString))
decompressor->setName(name);
mDecompList.pushBack(decompressor);
}
void ResourceMgr::unregisterFactory(ResourceFactory* factory)
{
mFactoryList.erase(factory);
}
void ResourceMgr::unregisterDecompressor(Decompressor* decompressor)
{
mDecompList.erase(decompressor);
}
Decompressor* ResourceMgr::findDecompressor(const SafeString& name)
{
for (auto& decompressor : mDecompList)
if (decompressor->getName() == name)
return decompressor;
return nullptr;
}
Resource* ResourceMgr::tryLoad(const ResourceMgr::LoadArg& arg, const SafeString& factory_name,
Decompressor* decompressor)
{
SafeString actual_factory_name;
FixedSafeString<32> ext;
if (!decompressor)
{
if (!Path::getExt(&ext, arg.path))
{
SEAD_ASSERT_MSG(false, "no file extension");
return nullptr;
}
decompressor = findDecompressor(ext);
}
if (decompressor)
actual_factory_name = factory_name;
else
actual_factory_name = ext;
auto* factory = arg.factory;
if (!factory)
{
factory = findFactory(actual_factory_name);
SEAD_ASSERT(factory);
}
if (arg.has_tried_create_with_decomp)
*arg.has_tried_create_with_decomp = decompressor;
if (decompressor)
return factory->tryCreateWithDecomp(arg, decompressor);
return factory->tryCreate(arg);
}
Resource* ResourceMgr::tryLoadWithoutDecomp(const ResourceMgr::LoadArg& arg)
{
auto* factory = arg.factory;
if (!factory)
{
FixedSafeString<32> ext;
if (Path::getExt(&ext, arg.path))
{
factory = findFactory(ext);
SEAD_ASSERT(factory);
}
else
{
factory = mDefaultResourceFactory;
}
}
return factory->tryCreate(arg);
}
void ResourceMgr::unload(Resource* res)
{
if (res)
delete res;
}
} // namespace sead
@@ -0,0 +1,447 @@
#include <filedevice/seadFileDeviceMgr.h>
#include <heap/seadHeap.h>
#include <heap/seadHeapMgr.h>
#include <math/seadMathCalcCommon.h>
#include <prim/seadBitUtil.h>
#include <prim/seadEndian.h>
#include <prim/seadPtrUtil.h>
#include <prim/seadSafeString.h>
#include <resource/seadSZSDecompressor.h>
namespace
{
#ifdef cafe
__attribute__((aligned(0x20))) s32 decodeSZSCafeAsm_(void* dst, const void* src)
{
asm("lwz r5, 0x4(r4)\n");
asm("li r11, 0x20\n");
asm("li r6, 0\n");
asm("mr r0, r5\n");
asm("addi r4, r4, 0xf\n");
asm("subi r3, r3, 1\n");
asm("cmpwi r5, 0x132\n");
asm("ble _final_decloop0\n");
asm("subi r5, r5, 0x132\n");
asm("nop\n");
asm("nop\n");
asm("nop\n");
asm("nop\n");
asm("nop\n");
asm("nop\n");
asm("nop\n");
asm("_decloop0: rlwinm. r6, r6, 0x1f, 1, 0x1f\n");
asm("bne _decloop1\n");
asm("lbzu r7, 1(r4)\n");
asm("li r6, 0x80\n");
asm("_decloop1: and. r8, r6, r7\n");
asm("lbzu r8, 1(r4)\n");
asm("beq _decloop2\n");
asm("andi. r9, r3, 0x1f\n");
asm("bne _decloop1x\n");
asm("dcbz r11, r3\n");
asm("_decloop1x: subic. r5, r5, 1\n");
asm("stbu r8, 1(r3)\n");
asm("bne _decloop0\n");
asm("b _decloop8\n");
asm("_decloop2: lbzu r9, 1(r4)\n");
asm("rlwinm. r10, r8, 0x1c, 4, 0x1f\n");
asm("bne _decloop3\n");
asm("lbzu r10, 1(r4)\n");
asm("addi r10, r10, 0x10");
asm("_decloop3: addi r10, r10, 2\n");
asm("rlwimi r9, r8, 8, 0x14, 0x17\n");
asm("subf r5, r10, r5\n");
asm("subf r8, r9, r4\n");
asm("mtspr CTR, r10\n");
asm("addi r8, r8, 1\n");
asm("_decloop4: andi. r9, r3, 0x1f\n");
asm("lbz r9, -1(r8)\n");
asm("addi r8, r8, 1\n");
asm("bne _decloop5\n");
asm("dcbz r11, r3\n");
asm("_decloop5: stbu r9, 1(r3)\n");
asm("bdnz _decloop4\n");
asm("cmpwi r5, 0\n");
asm("bgt _decloop0\n");
asm("_decloop8: addi r5, r5, 0x132\n");
asm("cmpwi r5, 0\n");
asm("ble _final_decloop8\n");
asm("_final_decloop0: rlwinm. r6, r6, 0x1f, 1, 0x1f\n");
asm("bne _final_decloop1\n");
asm("lbzu r7, 1(r4)\n");
asm("li r6, 0x80\n");
asm("_final_decloop1: and. r8, r6, r7\n");
asm("lbzu r8, 1(r4)\n");
asm("beq _final_decloop2\n");
asm("subic. r5, r5, 1\n");
asm("stbu r8, 1(r3)\n");
asm("bne _final_decloop0\n");
asm("b _final_decloop8\n");
asm("_final_decloop2: lbzu r9, 1(r4)\n");
asm("rlwinm. r10, r8, 0x1c, 4, 0x1f\n");
asm("bne _final_decloop3\n");
asm("lbzu r10, 1(r4)\n");
asm("addi r10, r10, 0x10\n");
asm("_final_decloop3: addi r10, r10, 2\n");
asm("rlwimi r9, r8, 8, 0x14, 0x17\n");
asm("subf. r5, r10, r5\n");
asm("blt _final_decloop8\n");
asm("subf r8, r9, r3\n");
asm("mtspr CTR, r10\n");
asm("addi r8, r8, 1\n");
asm("_final_decloop4: lbz r9, -1(r8)\n");
asm("addi r8, r8, 1\n");
asm("stbu r9, 1(r3)\n");
asm("bdnz _final_decloop4\n");
asm("cmpwi r5, 0\n");
asm("bgt _final_decloop0\n");
s32 register error asm("r3");
asm("_final_decloop8: mr %0, r0\n" : "=r"(error));
asm("blr");
return error;
}
#endif // cafe
} // namespace
#ifdef SWITCH
s32 decodeSZSNxAsm64_(void* dst, const void* src);
#endif
namespace sead
{
SZSDecompressor::DecompContext::DecompContext()
{
initialize(NULL);
}
SZSDecompressor::DecompContext::DecompContext(void* dst)
{
initialize(dst);
}
void SZSDecompressor::DecompContext::initialize(void* dst)
{
destp = static_cast<u8*>(dst);
destCount = 0;
forceDestCount = 0;
flagMask = 0;
flags = 0;
packHigh = 0;
step = SZSDecompressor::cStepNormal;
lzOffset = 0;
headerSize = 0x10;
}
SZSDecompressor::SZSDecompressor(u32 workSize, u8* workBuffer) : Decompressor("szs")
{
if (workBuffer == NULL)
{
mWorkSize = Mathu::roundUpPow2(workSize, FileDevice::cBufferMinAlignment);
mWorkBuffer = NULL;
}
else
{
mWorkSize = workSize;
mWorkBuffer = workBuffer;
}
}
u8* SZSDecompressor::tryDecompFromDevice(const ResourceMgr::LoadArg& loadArg, Resource* resource,
u32* outSize, u32* outAllocSize, bool* outAllocated)
{
Heap* heap = loadArg.load_data_heap;
if (heap == NULL)
heap = HeapMgr::sInstancePtr->getCurrentHeap();
FileHandle handle;
FileDevice* device;
u8* src;
if (loadArg.device != NULL)
device = loadArg.device->tryOpen(&handle, loadArg.path, FileDevice::cFileOpenFlag_ReadOnly,
loadArg.div_size);
else
device = FileDeviceMgr::instance()->tryOpen(
&handle, loadArg.path, FileDevice::cFileOpenFlag_ReadOnly, loadArg.div_size);
if (device != NULL &&
((src = mWorkBuffer, src != NULL) ||
(src = new (heap, -FileDevice::cBufferMinAlignment) u8[mWorkSize], src != NULL)))
{
u32 bytesRead = handle.read(src, mWorkSize);
if (bytesRead >= 0x10)
{
u32 decompSize = getDecompSize(src);
s32 decompAlignment = getDecompAlignment(src);
u32 allocSize = loadArg.load_data_buffer_size;
u8* dst = loadArg.load_data_buffer;
if (decompSize > allocSize && allocSize != 0)
decompSize = allocSize;
bool allocated = false;
allocSize = Mathu::roundUpPow2(decompSize, 0x20);
if (dst == NULL)
{
DirectResource* directResource = DynamicCast<DirectResource, Resource>(resource);
if (directResource != NULL)
{
s32 alignment = loadArg.load_data_alignment;
if (alignment != 0)
decompAlignment = (alignment < 0x20) ? 0x20 : alignment;
else
{
if (decompAlignment == 0)
decompAlignment = directResource->getLoadDataAlignment();
decompAlignment = ((loadArg.instance_alignment < 0) ? -1 : 1) *
((decompAlignment < 0x20) ? 0x20 : decompAlignment);
}
}
else
decompAlignment = -(((loadArg.instance_alignment < 0) ? -1 : 1) << 5);
dst = new (heap, decompAlignment) u8[allocSize];
if (dst != NULL)
allocated = true;
}
if (dst != NULL)
{
s32 error;
if (bytesRead < mWorkSize)
error = decomp(dst, allocSize, src, mWorkSize);
else
{
DecompContext context(dst);
context.forceDestCount = decompSize;
do
{
error = streamDecomp(&context, src, bytesRead);
if (error <= 0)
break;
} while ((bytesRead = handle.read(src, mWorkSize), bytesRead != 0));
}
if (!(error < 0))
{
if (mWorkBuffer == NULL)
delete[] src;
if (outSize != NULL)
*outSize = decompSize;
if (outAllocSize != NULL)
*outAllocSize = allocSize;
if (outAllocated != NULL)
*outAllocated = allocated;
return dst;
}
if (allocated)
delete[] dst;
}
}
if (mWorkBuffer == NULL)
delete[] src;
}
return NULL;
}
u32 SZSDecompressor::getDecompAlignment(const void* src)
{
return Endian::toHostU32(Endian::cBig, BitUtil::bitCastPtr<u32>(src, 8));
}
u32 SZSDecompressor::getDecompSize(const void* src)
{
return Endian::toHostU32(Endian::cBig, BitUtil::bitCastPtr<u32>(src, 4));
}
s32 SZSDecompressor::readHeader_(DecompContext* context, const u8* src, u32 srcSize)
{
s32 len = 0;
while (context->headerSize != 0)
{
context->headerSize -= 1;
if (context->headerSize == 0xF)
{
if (*src != 0x59)
return -1;
}
else if (context->headerSize == 0xE)
{
if (*src != 0x61)
return -1;
}
else if (context->headerSize == 0xD)
{
if (*src != 0x7A)
return -1;
}
else if (context->headerSize == 0xC)
{
if (*src != 0x30)
return -1;
}
else if (7 < context->headerSize)
context->destCount |= static_cast<u32>(*src) << (context->headerSize - 8) * 8;
src++;
len += 1;
if (--srcSize == 0 && context->headerSize != 0)
return len;
}
if (context->forceDestCount < 1)
return len;
if (context->destCount <= context->forceDestCount)
return len;
context->destCount = context->forceDestCount;
return len;
}
s32 SZSDecompressor::streamDecomp(DecompContext* context, const void* src, u32 srcSize)
{
const u8* _src = static_cast<const u8*>(src);
u32 n;
if (context->headerSize != 0)
{
s32 len = readHeader_(context, _src, srcSize);
if (len < 0)
return len;
srcSize -= len;
_src += len;
if (srcSize == 0)
{
if (context->headerSize == 0)
return context->destCount;
return -1;
}
}
while (context->destCount > 0)
{
if (context->step == cStepLong)
{
n = *_src + 0x12;
if (!context->doCopy(n))
return -2;
}
else if (context->step == cStepShort)
{
context->lzOffset = (((context->packHigh << 8) & 0xf00) | *_src) + 1;
n = context->packHigh >> 4;
if (n != 0)
{
n += 2;
if (!context->doCopy(n))
return -2;
}
else
context->step = cStepLong;
}
else
{
if (context->flagMask == 0)
{
context->flags = *_src++;
context->flagMask = 0x80;
if (--srcSize == 0)
break;
}
if ((context->flags & context->flagMask) == 0)
{
context->packHigh = *_src;
context->step = cStepShort;
}
else
{
*context->destp++ = *_src;
context->destCount -= 1;
}
context->flagMask >>= 1;
}
if (--srcSize == 0)
break;
_src++;
}
if (context->destCount == 0 && context->forceDestCount == 0 && 0x20 < srcSize)
return -1;
else
return context->destCount;
}
s32 SZSDecompressor::decomp(void* dst, u32 dstSize, const void* src, u32)
{
u32 magic = Endian::toHostU32(Endian::cBig, BitUtil::bitCastPtr<u32>(src));
if (magic != 0x59617A30)
return -1;
u32 decompSize = getDecompSize(src);
s32 error = -2;
if (dstSize >= decompSize)
{
#ifdef cafe
error = decodeSZSCafeAsm_(dst, src);
#elif defined(SWITCH)
error = decodeSZSNxAsm64_(dst, src);
#else
SEAD_ASSERT_MSG(false, "SZSDecompressor::decomp not implemented");
#endif // cafe
}
return error;
}
} // namespace sead
@@ -0,0 +1,326 @@
#include <container/seadBuffer.h>
#include <prim/seadPtrUtil.h>
#include <prim/seadSafeString.h>
#include <resource/seadSharcArchiveRes.h>
namespace
{
u32 calcHash32(const sead::SafeString& str, u32 key)
{
const char* str_ = str.cstr();
u32 result = 0;
// Each character must be treated as a signed value.
// The cast to s8 (not s32) is necessary to avoid unsigned conversions.
for (s32 i = 0; str_[i] != '\0'; i++)
result = result * key + s8(str_[i]);
return result;
}
#ifdef NNSDK
s32 binarySearch_(u32 hash, const sead::SharcArchiveRes::FATEntry* buffer, s32 start, s32 end,
sead::Endian::Types endian)
#else
s32 binarySearch_(u32 hash, const sead::SharcArchiveRes::FATEntry* buffer, s32 start, s32 end)
#endif
{
s32 middle;
for (;;)
{
middle = (start + end) / 2;
#ifdef NNSDK
u32 entryHash = sead::Endian::toHostU32(endian, buffer[middle].hash);
#else
u32 entryHash = buffer[middle].hash;
#endif
if (entryHash == hash)
return middle;
else if (entryHash < hash)
{
if (start == middle)
return -1;
start = middle;
}
else
{
if (end == middle)
return -1;
end = middle;
}
}
}
} // namespace
namespace sead
{
struct SharcArchiveRes::HandleInner
{
u32 x;
};
SharcArchiveRes::SharcArchiveRes()
: ArchiveRes(), mArchiveBlockHeader(NULL), mFATBlockHeader(NULL), mFNTBlock(NULL),
mDataBlock(NULL)
#ifdef cafe
,
mEndianType(Endian::cBig)
#else
,
mEndianType(Endian::cLittle)
#endif
{
}
SharcArchiveRes::~SharcArchiveRes() {}
const void* SharcArchiveRes::getFileImpl_(const SafeString& file_path, FileInfo* file_info) const
{
s32 id = convertPathToEntryIDImpl_(file_path);
if (id < 0)
return NULL;
return getFileFastImpl_(id, file_info);
}
const void* SharcArchiveRes::getFileFastImpl_(s32 entry_id, FileInfo* file_info) const
{
if (entry_id < 0 || entry_id >= mFATEntrys.size())
return NULL;
u32 start = Endian::toHostU32(mEndianType, mFATEntrys(entry_id).data_start_offset);
if (file_info != NULL)
{
u32 end = Endian::toHostU32(mEndianType, mFATEntrys(entry_id).data_end_offset);
if (start > end)
return NULL;
u32 length = end - start;
file_info->mStartOffset = start;
file_info->mLength = length;
}
return mDataBlock + start;
}
s32 SharcArchiveRes::convertPathToEntryIDImpl_(const SafeString& file_path) const
{
u32 hash = calcHash32(file_path, Endian::toHostU32(mEndianType, mFATBlockHeader->hash_key));
s32 start = 0;
s32 end = mFATEntrys.size();
#ifdef NNSDK
s32 id = binarySearch_(hash, mFATEntrys.getBufferPtr(), start, end, mEndianType);
#else
s32 id = binarySearch_(hash, mFATEntrys.getBufferPtr(), start, end);
#endif
if (id == -1)
return -1;
u32 offset = Endian::toHostU32(mEndianType, mFATEntrys(id).name_offset);
if (offset != 0)
{
id -= (offset >> 24) - 1;
while (id < end)
{
const FATEntry* entry = mFATEntrys.unsafeGet(id);
if (Endian::toHostU32(mEndianType, entry->hash) != hash)
return -1;
else
{
u32 offset_ = Endian::toHostU32(mEndianType, entry->name_offset);
if (PtrUtil::addOffset(mFNTBlock, offset_ & 0xffffff) > mDataBlock)
{
SEAD_ASSERT_MSG(false, "Invalid data start offset");
return -1;
}
if (file_path.isEqual(mFNTBlock + (offset_ & 0xffffff) * cFileNameTableAlign))
return id;
}
id++;
}
}
return id;
}
bool SharcArchiveRes::setCurrentDirectoryImpl_(const SafeString&)
{
SEAD_ASSERT_MSG(false, "Not support.");
return false;
}
bool SharcArchiveRes::openDirectoryImpl_(HandleBuffer* handle, const SafeString& path) const
{
if (path.isEmpty() || path == "/")
{
getHandleInner_(handle)->x = 0;
return true;
}
SEAD_WARN("dir_path[%s] is not allowed to open sharc directory. must be root.", path.cstr());
return false;
}
bool SharcArchiveRes::closeDirectoryImpl_(HandleBuffer*) const
{
return true;
}
u32 SharcArchiveRes::readDirectoryImpl_(HandleBuffer* handle_, DirectoryEntry* entry, u32 num) const
{
auto* handle = getHandleInner_(handle_);
u32 count = 0;
while (handle->x + count < Endian::toHostU16(mEndianType, mFATBlockHeader->file_num) &&
count < num)
{
u32 id = handle->x + count;
SEAD_ASSERT(id >= handle->x);
u32 offset = Endian::toHostU32(mEndianType, mFATEntrys(id).name_offset);
if (offset == 0)
entry[count].name.format("%08x", Endian::toHostU32(mEndianType, mFATEntrys(id).hash));
else
{
if (reinterpret_cast<const u8*>(mFNTBlock + (offset & 0xffffff)) > mDataBlock)
{
SEAD_WARN("Invalid data start offset");
entry[count].name.clear();
}
else
entry[count].name.copy(mFNTBlock + (offset & 0xffffff) * cFileNameTableAlign);
}
entry[count].is_directory = false;
count++;
}
handle->x += count;
return count;
}
bool SharcArchiveRes::prepareArchive_(const void* archive)
{
if (archive == nullptr)
{
SEAD_ASSERT_MSG(false, "archive must not be nullptr.");
return false;
}
const u8* archive_ = reinterpret_cast<const u8*>(archive);
mArchiveBlockHeader = reinterpret_cast<const ArchiveBlockHeader*>(archive_);
if (std::memcmp(mArchiveBlockHeader->signature, "SARC", 4) != 0)
{
SEAD_ASSERT_MSG(false, "Invalid ArchiveBlockHeader");
return false;
}
mEndianType = Endian::markToEndian(mArchiveBlockHeader->byte_order);
if (Endian::toHostU16(mEndianType, mArchiveBlockHeader->version) != cArchiveVersion)
{
SEAD_ASSERT_MSG(false, "unmatching version ( expect: %x, actual: %x )", cArchiveVersion,
mArchiveBlockHeader->version);
return false;
}
if (Endian::toHostU16(mEndianType, mArchiveBlockHeader->header_size) !=
sizeof(ArchiveBlockHeader))
{
SEAD_ASSERT_MSG(false, "Invalid ArchiveBlockHeader");
return false;
}
mFATBlockHeader = reinterpret_cast<const FATBlockHeader*>(
archive_ + Endian::toHostU16(mEndianType, mArchiveBlockHeader->header_size));
if (std::memcmp(mFATBlockHeader->signature, "SFAT", 4) != 0)
{
SEAD_ASSERT_MSG(false, "Invalid FATBlockHeader");
return false;
}
if (Endian::toHostU16(mEndianType, mFATBlockHeader->header_size) != sizeof(FATBlockHeader))
{
SEAD_ASSERT_MSG(false, "Invalid FATBlockHeader");
return false;
}
if (Endian::toHostU16(mEndianType, mFATBlockHeader->file_num) > cArchiveEntryMax)
{
SEAD_ASSERT_MSG(false, "Invalid FATBlockHeader");
return false;
}
mFATEntrys.setBuffer(
Endian::toHostU16(mEndianType, mFATBlockHeader->file_num),
const_cast<FATEntry*>(reinterpret_cast<const FATEntry*>(
archive_ + Endian::toHostU16(mEndianType, mArchiveBlockHeader->header_size) +
Endian::toHostU16(mEndianType, mFATBlockHeader->header_size))));
auto* fnt_header = reinterpret_cast<const FNTBlockHeader*>(
archive_ + Endian::toHostU16(mEndianType, mArchiveBlockHeader->header_size) +
Endian::toHostU16(mEndianType, mFATBlockHeader->header_size) +
Endian::toHostU16(mEndianType, mFATBlockHeader->file_num) * sizeof(FATEntry));
if (std::memcmp(fnt_header->signature, "SFNT", 4) != 0)
{
SEAD_ASSERT_MSG(false, "Invalid FNTBlockHeader");
return false;
}
if (Endian::toHostU16(mEndianType, fnt_header->header_size) != sizeof(FNTBlockHeader))
{
SEAD_ASSERT_MSG(false, "Invalid FNTBlockHeader");
return false;
}
mFNTBlock = reinterpret_cast<const char*>(fnt_header) +
Endian::toHostU16(mEndianType, fnt_header->header_size);
if (Endian::toHostU32(mEndianType, mArchiveBlockHeader->data_block_offset) <
PtrUtil::diff(mFNTBlock, mArchiveBlockHeader))
{
SEAD_ASSERT_MSG(false, "Invalid data block offset");
return false;
}
mDataBlock = archive_ + Endian::toHostU32(mEndianType, mArchiveBlockHeader->data_block_offset);
return true;
}
SharcArchiveRes::HandleInner* SharcArchiveRes::getHandleInner_(HandleBuffer* handle,
bool create_new) const
{
static_assert(sizeof(HandleInner) <= sizeof(HandleBuffer));
if (create_new)
return new (handle) HandleInner;
return reinterpret_cast<HandleInner*>(handle);
}
bool SharcArchiveRes::isExistFileImpl_(const SafeString& path) const
{
const u32 hash = calcHash32(path, Endian::toHostU32(mEndianType, mFATBlockHeader->hash_key));
const u32 size = mFATEntrys.size();
#ifdef NNSDK
const s32 id = binarySearch_(hash, mFATEntrys.getBufferPtr(), 0, size, mEndianType);
#else
const s32 id = binarySearch_(hash, mFATEntrys.getBufferPtr(), 0, size);
#endif
return id != -1;
}
} // namespace sead
@@ -0,0 +1,38 @@
#include <thread/seadCriticalSection.h>
namespace sead
{
CriticalSection::CriticalSection() : IDisposer(), mCriticalSectionInner()
{
OSInitMutex(&mCriticalSectionInner);
}
CriticalSection::CriticalSection(Heap* disposer_heap)
: IDisposer(disposer_heap, HeapNullOption::UseSpecifiedOrContainHeap)
{
OSInitMutex(&mCriticalSectionInner);
}
CriticalSection::CriticalSection(Heap* disposer_heap, HeapNullOption heap_null_option)
: IDisposer(disposer_heap, heap_null_option)
{
OSInitMutex(&mCriticalSectionInner);
}
CriticalSection::~CriticalSection() {}
void CriticalSection::lock()
{
OSLockMutex(&mCriticalSectionInner);
}
bool CriticalSection::tryLock()
{
return OSTryLockMutex(&mCriticalSectionInner);
}
void CriticalSection::unlock()
{
OSUnlockMutex(&mCriticalSectionInner);
}
} // namespace sead
@@ -0,0 +1,41 @@
#include "thread/seadCriticalSection.h"
namespace sead
{
CriticalSection::CriticalSection() : IDisposer()
{
nn::os::InitializeMutex(&mCriticalSectionInner, true, 0);
}
CriticalSection::CriticalSection(Heap* disposer_heap)
: IDisposer(disposer_heap, HeapNullOption::UseSpecifiedOrContainHeap)
{
nn::os::InitializeMutex(&mCriticalSectionInner, true, 0);
}
CriticalSection::CriticalSection(Heap* disposer_heap, HeapNullOption heap_null_option)
: IDisposer(disposer_heap, heap_null_option)
{
nn::os::InitializeMutex(&mCriticalSectionInner, true, 0);
}
CriticalSection::~CriticalSection()
{
nn::os::FinalizeMutex(&mCriticalSectionInner);
}
void CriticalSection::lock()
{
nn::os::LockMutex(&mCriticalSectionInner);
}
bool CriticalSection::tryLock()
{
return nn::os::TryLockMutex(&mCriticalSectionInner);
}
void CriticalSection::unlock()
{
nn::os::UnlockMutex(&mCriticalSectionInner);
}
} // namespace sead
@@ -0,0 +1,81 @@
#include "basis/seadRawPrint.h"
#include "thread/seadEvent.h"
namespace sead
{
Event::Event() : IDisposer() {}
Event::Event(bool manual_reset) : Event()
{
initialize(manual_reset);
}
Event::Event(Heap* disposer_heap) : Event(disposer_heap, HeapNullOption::UseSpecifiedOrContainHeap)
{
}
Event::Event(Heap* disposer_heap, bool manual_reset) : Event(disposer_heap)
{
initialize(manual_reset);
}
Event::Event(Heap* disposer_heap, IDisposer::HeapNullOption heap_null_option)
: IDisposer(disposer_heap, heap_null_option)
{
}
Event::Event(Heap* disposer_heap, IDisposer::HeapNullOption heap_null_option, bool manual_reset)
: Event(disposer_heap, heap_null_option)
{
initialize(manual_reset);
}
Event::~Event()
{
setInitialized(false);
nn::os::FinalizeLightEvent(&mEventInner);
}
void Event::initialize(bool manual_reset)
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(!mInitialized, "Event is already initialized.");
#endif
nn::os::InitializeLightEvent(&mEventInner, false,
manual_reset ? nn::os::EventClearMode_ManualClear :
nn::os::EventClearMode_AutoClear);
setInitialized(true);
}
void Event::wait()
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(mInitialized, "Event is not initialized.");
#endif
nn::os::WaitLightEvent(&mEventInner);
}
bool Event::wait(TickSpan duration)
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(mInitialized, "Event is not initialized.");
#endif
return nn::os::TimedWaitLightEvent(&mEventInner, nn::os::ConvertToTimeSpan(duration.toTicks()));
}
void Event::setSignal()
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(mInitialized, "Event is not initialized.");
#endif
nn::os::SignalLightEvent(&mEventInner);
}
void Event::resetSignal()
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(mInitialized, "Event is not initialized.");
#endif
nn::os::ClearLightEvent(&mEventInner);
}
} // namespace sead
@@ -0,0 +1,84 @@
#include "basis/seadNew.h"
#include "basis/seadRawPrint.h"
#include "thread/seadMessageQueue.h"
namespace sead
{
MessageQueue::MessageQueue() = default;
MessageQueue::~MessageQueue() = default;
void MessageQueue::allocate(s32 size, Heap* heap)
{
if (size <= 0)
{
SEAD_ASSERT_MSG(false, "MessageQueue size must not be zero");
return;
}
mBuffer = new (heap) Element[size];
nn::os::InitializeMessageQueue(&mMessageQueueInner, reinterpret_cast<u64*>(mBuffer), size);
}
void MessageQueue::free()
{
nn::os::FinalizeMessageQueue(&mMessageQueueInner);
if (mBuffer)
{
delete[] mBuffer;
mBuffer = nullptr;
}
}
bool MessageQueue::push(MessageQueue::Element message, MessageQueue::BlockType block_type)
{
if (block_type == BlockType::Blocking)
{
nn::os::SendMessageQueue(&mMessageQueueInner, message);
return true;
}
return nn::os::TrySendMessageQueue(&mMessageQueueInner, message);
}
MessageQueue::Element MessageQueue::pop(MessageQueue::BlockType block_type)
{
u64 message;
if (block_type == BlockType::Blocking)
{
nn::os::ReceiveMessageQueue(&message, &mMessageQueueInner);
return message;
}
if (nn::os::TryReceiveMessageQueue(&message, &mMessageQueueInner))
return message;
return 0;
}
MessageQueue::Element MessageQueue::peek(MessageQueue::BlockType block_type) const
{
u64 message;
if (block_type == BlockType::Blocking)
{
nn::os::PeekMessageQueue(&message, &mMessageQueueInner);
return message;
}
if (nn::os::TryPeekMessageQueue(&message, &mMessageQueueInner))
return message;
return 0;
}
bool MessageQueue::jam(MessageQueue::Element message, MessageQueue::BlockType block_type)
{
if (block_type == BlockType::Blocking)
{
nn::os::JamMessageQueue(&mMessageQueueInner, message);
return true;
}
return nn::os::TryJamMessageQueue(&mMessageQueueInner, message);
}
} // namespace sead
@@ -0,0 +1,39 @@
#include "thread/seadMutex.h"
namespace sead
{
Mutex::Mutex() : IDisposer()
{
nn::os::InitializeMutex(&mMutexInner, true, 0);
}
Mutex::Mutex(Heap* disposer_heap) : Mutex(disposer_heap, HeapNullOption::UseSpecifiedOrContainHeap)
{
}
Mutex::Mutex(Heap* disposer_heap, HeapNullOption heap_null_option)
: IDisposer(disposer_heap, heap_null_option)
{
nn::os::InitializeMutex(&mMutexInner, true, 0);
}
Mutex::~Mutex()
{
nn::os::FinalizeMutex(&mMutexInner);
}
void Mutex::lock()
{
nn::os::LockMutex(&mMutexInner);
}
bool Mutex::tryLock()
{
return nn::os::TryLockMutex(&mMutexInner);
}
void Mutex::unlock()
{
nn::os::UnlockMutex(&mMutexInner);
}
} // namespace sead
@@ -0,0 +1,86 @@
#include "basis/seadRawPrint.h"
#include "thread/seadSemaphore.h"
namespace sead
{
Semaphore::Semaphore() = default;
Semaphore::Semaphore(s32 initial_count) : Semaphore()
{
initialize(initial_count);
}
Semaphore::Semaphore(s32 initial_count, s32 max_count) : Semaphore()
{
initialize(initial_count, max_count);
}
Semaphore::Semaphore(Heap* heap) : Semaphore(heap, HeapNullOption::UseSpecifiedOrContainHeap) {}
Semaphore::Semaphore(Heap* heap, s32 initial_count) : Semaphore(heap)
{
initialize(initial_count);
}
Semaphore::Semaphore(Heap* heap, s32 initial_count, s32 max_count) : Semaphore(heap)
{
initialize(initial_count, max_count);
}
Semaphore::Semaphore(Heap* heap, IDisposer::HeapNullOption heap_null_option)
: IDisposer(heap, heap_null_option)
{
}
Semaphore::Semaphore(Heap* heap, IDisposer::HeapNullOption heap_null_option, s32 initial_count)
: Semaphore(heap, heap_null_option)
{
initialize(initial_count);
}
Semaphore::Semaphore(Heap* heap, IDisposer::HeapNullOption heap_null_option, s32 initial_count,
s32 max_count)
: Semaphore(heap, heap_null_option)
{
initialize(initial_count, max_count);
}
Semaphore::~Semaphore()
{
nn::os::FinalizeSemaphore(&mSemaphoreInner);
setInitialized(false);
}
void Semaphore::initialize(s32 initial_count, s32 max_count)
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(!mInitialized, "Semaphore is already initialized.");
#endif
nn::os::InitializeSemaphore(&mSemaphoreInner, initial_count, max_count);
setInitialized(true);
}
void Semaphore::lock()
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(mInitialized, "Semaphore is not initialized.");
#endif
nn::os::AcquireSemaphore(&mSemaphoreInner);
}
bool Semaphore::tryLock()
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(mInitialized, "Semaphore is not initialized.");
#endif
return nn::os::TryAcquireSemaphore(&mSemaphoreInner);
}
void Semaphore::unlock()
{
#ifdef SEAD_DEBUG
SEAD_ASSERT_MSG(mInitialized, "Semaphore is not initialized.");
#endif
nn::os::ReleaseSemaphore(&mSemaphoreInner);
}
} // namespace sead
@@ -0,0 +1,202 @@
#include "basis/seadNew.h"
#include "basis/seadRawPrint.h"
#include "thread/seadThread.h"
namespace sead
{
Thread::Thread(const SafeString& name, Heap* heap, s32 priority, MessageQueue::BlockType block_type,
MessageQueue::Element quit_msg, s32 stack_size, s32 message_queue_size)
: INamable(name), mStackSize(stack_size), mBlockType(block_type), mQuitMsg(quit_msg),
mPriority(priority)
{
mListNode.mData = this;
mMessageQueue.allocate(message_queue_size, heap);
mStackTop = new (heap, 0x1000) u8[stack_size];
// FIXME: ThreadType has the wrong size
mThreadInner = new (heap) nn::os::ThreadType;
const auto result =
nn::os::CreateThread(mThreadInner, ninThreadFunc_, this, mStackTop, stack_size, mPriority);
SEAD_ASSERT_MSG(result.IsSuccess(), "CreateThread failed. 0x%08x (module = %d, desc = %d) %s",
result.GetInnerValueForDebug(), result.GetModule(), result.GetDescription(),
name.cstr());
nn::os::SetThreadName(mThreadInner, name.cstr());
if (ThreadMgr::instance())
ThreadMgr::instance()->addThread_(this);
else
SEAD_ASSERT_MSG(false, "ThreadMgr not initialized");
}
Thread::~Thread()
{
if (!ThreadMgr::instance())
{
SEAD_ASSERT_MSG(false, "ThreadMgr not initialized");
return;
}
if (ThreadMgr::instance()->getMainThread() != this)
{
ThreadMgr::instance()->removeThread_(this);
if (mState != State::cQuitting && mState != State::cTerminated)
{
if (mState == State::cRunning)
{
SEAD_ASSERT_MSG(false, "Thread is running. Do quit and waitDone");
quitAndWaitDoneSingleThread(false);
}
}
else
{
SEAD_ASSERT_MSG(false, "Thread is not done. Do waitDone");
waitDone();
}
nn::os::DestroyThread(mThreadInner);
if (mThreadInner)
delete mThreadInner;
if (mStackTop)
delete[] static_cast<u8*>(mStackTop);
}
mMessageQueue.free();
}
bool Thread::start()
{
if (mState)
{
SEAD_WARN("Thread is running or done. Can not start.\n");
return false;
}
nn::os::StartThread(mThreadInner);
const u32 id = nn::os::GetThreadId(mThreadInner);
const int state = mState;
mId = id;
if (state == State::cInitialized)
mState = State::cRunning;
return true;
}
void Thread::waitDone()
{
if ((mState.value() | State::cReleased) == State::cReleased)
return;
nn::os::WaitThread(mThreadInner);
SEAD_ASSERT_MSG(mState == State::cTerminated, "Join failed?");
mState = State::cReleased;
}
void Thread::setPriority(s32 prio)
{
mPriority = prio;
if (isActive())
nn::os::ChangeThreadPriority(mThreadInner, prio);
}
s32 Thread::getPriority() const
{
return mPriority;
}
void Thread::setAffinity(const CoreIdMask& affinity)
{
mAffinity = affinity;
u64 mask = mAffinity;
const auto available_mask = nn::os::GetThreadAvailableCoreMask();
SEAD_ASSERT_MSG((~u32(available_mask) & mask) == 0, "invalid core mask. ( mask = %ld )", mask);
nn::os::SetThreadCoreMask(mThreadInner, -1, mask);
}
void Thread::yield()
{
nn::os::YieldThread();
}
void Thread::sleep(TickSpan howLong)
{
nn::os::SleepThread(nn::os::ConvertToTimeSpan(howLong.toTicks()));
}
uintptr_t Thread::getStackCheckStartAddress_() const
{
return uintptr_t(mStackTopForCheck);
}
void Thread::ninThreadFunc_(void* arg)
{
auto self = static_cast<Thread*>(arg);
ThreadMgr::instance()->mThreadPtrTLS.setValue(uintptr_t(self));
if (self->mAffinity.countOnBits() <= 1)
nn::os::SetTlsValue(CoreInfo::getCoreNumberTlsSlot(),
int(nn::os::GetCurrentCoreNumber()) + 1);
#ifdef SEAD_DEBUG
uintptr_t stack_addr;
nn::os::GetCurrentStackInfo(&stack_addr, nullptr);
self->mStackTopForCheck = reinterpret_cast<void*>(stack_addr);
self->initStackCheckWithCurrentStackPointer_();
#endif
const u32 id = nn::os::GetThreadId(self->mThreadInner);
self->mState = State::cRunning;
self->mId = id;
self->run_();
self->mState = State::cTerminated;
}
Thread::Thread(Heap* heap, nn::os::ThreadType* nn_thread, u32 thread_id)
: INamable("sead::MainThread"), mStackSize(nn_thread->thread_stack_size),
mBlockType(MessageQueue::BlockType::NonBlocking), mQuitMsg(0x7FFFFFFF), mId(thread_id),
mState(State::cRunning), mThreadInner(nn_thread),
mPriority(nn::os::GetThreadPriority(nn_thread))
{
mListNode.mData = this;
mMessageQueue.allocate(32, heap);
uintptr_t stack_addr;
size_t stack_size;
nn::os::GetCurrentStackInfo(&stack_addr, &stack_size);
mStackTopForCheck = reinterpret_cast<void*>(stack_addr);
mStackSize = s32(stack_size);
#ifdef SEAD_DEBUG
initStackCheckWithCurrentStackPointer_();
#endif
setAffinity(mAffinity);
}
ThreadMgr::ThreadMgr() = default;
u32 ThreadMgr::getCurrentThreadID_()
{
return u32(uintptr_t(nn::os::GetCurrentThread()));
}
void ThreadMgr::initMainThread_(Heap* heap)
{
nn::os::ThreadType* nn_thread = nn::os::GetCurrentThread();
nn::os::ChangeThreadPriority(nn::os::GetCurrentThread(), 16);
const u64 nn_thread_id = nn::os::GetThreadId(nn_thread);
auto thread = new (heap) MainThread(heap, nn_thread, nn_thread_id);
mMainThread = thread;
mThreadPtrTLS.setValue(uintptr_t(thread));
nn::os::SetTlsValue(CoreInfo::getCoreNumberTlsSlot(), int(nn::os::GetCurrentCoreNumber()) + 1);
}
} // namespace sead
@@ -0,0 +1,22 @@
#include "thread/seadDelegateThread.h"
#include "prim/seadDelegate.h"
namespace sead
{
DelegateThread::DelegateThread(const SafeString& name,
IDelegate2<Thread*, MessageQueue::Element>* delegate, Heap* heap,
s32 priority, MessageQueue::BlockType block_type,
MessageQueue::Element quit_msg, s32 stack_size,
s32 message_queue_size)
: Thread(name, heap, priority, block_type, quit_msg, stack_size, message_queue_size),
mDelegate(delegate)
{
}
DelegateThread::~DelegateThread() = default;
void DelegateThread::calc_(MessageQueue::Element msg)
{
mDelegate->invoke(this, msg);
}
} // namespace sead
@@ -0,0 +1,81 @@
#include "thread/seadReadWriteLock.h"
#include "prim/seadScopedLock.h"
#include "thread/seadThread.h"
namespace sead
{
ReadWriteLock::ReadWriteLock() = default;
ReadWriteLock::~ReadWriteLock() = default;
void ReadWriteLock::readLock()
{
ScopedLock<SemaphoreLock> lock(&mReadLock);
if (mNumReaders.increment() == 0)
mWriteLock.lock();
}
void ReadWriteLock::readUnlock()
{
if (mNumReaders.decrement() == 1)
mWriteLock.unlock();
}
void ReadWriteLock::writeLock()
{
Thread* current_thread = ThreadMgr::instance()->getCurrentThread();
if (mWriterThread != current_thread)
{
if (mNumWriters.increment() == 0)
mReadLock.lock();
mWriteLock.lock();
mWriterThread = current_thread;
}
++mWritingThreadCount;
}
// NON_MATCHING: see SemaphoreLock::unlock
void ReadWriteLock::writeUnlock()
{
Thread* current_thread = ThreadMgr::instance()->getCurrentThread();
if (mWriterThread != current_thread)
{
SEAD_ASSERT_MSG(false, "This thread[%p] does not have the lock.", current_thread);
return;
}
if (!current_thread || mWritingThreadCount == 0 || int(mWritingThreadCount) < 0)
{
SEAD_ASSERT_MSG(
false,
"mWritingThreadCount[%d] must not be 0 and current_thread[%p] must not be nullptr",
mWritingThreadCount, current_thread);
return;
}
--mWritingThreadCount;
if (mWritingThreadCount == 0)
{
mWriterThread = nullptr;
mWriteLock.unlock();
if (mNumWriters.decrement() == 1)
mReadLock.unlock();
}
}
ReadWriteLock::SemaphoreLock::SemaphoreLock() = default;
void ReadWriteLock::SemaphoreLock::lock()
{
if (mLockCount.increment())
mSemaphore.lock();
}
void ReadWriteLock::SemaphoreLock::unlock()
{
if (mLockCount.decrement() != 1)
mSemaphore.unlock();
}
} // namespace sead
@@ -0,0 +1,60 @@
#include "thread/seadSpinLock.h"
#include "thread/seadThread.h"
namespace sead
{
SpinLock::SpinLock() = default;
SpinLock::~SpinLock() = default;
void SpinLock::lock()
{
Thread* current_thread = ThreadMgr::instance()->getCurrentThread();
if (mOwnerThread.load() == current_thread)
{
++mCount;
return;
}
while (!mOwnerThread.compareExchange(nullptr, current_thread))
continue;
SEAD_ASSERT_MSG(mCount == 0, "mCount[%u] must be 0", mCount);
mCount = 1;
}
bool SpinLock::tryLock()
{
Thread* current_thread = ThreadMgr::instance()->getCurrentThread();
if (mOwnerThread.load() == current_thread)
{
++mCount;
return true;
}
if (!mOwnerThread.compareExchange(nullptr, current_thread))
return false;
SEAD_ASSERT_MSG(mCount == 0, "mCount[%u] must be 0", mCount);
mCount = 1;
return true;
}
void SpinLock::unlock()
{
Thread* current_thread = ThreadMgr::instance()->getCurrentThread();
if (mOwnerThread.load() != current_thread)
{
SEAD_ASSERT_MSG(false, "This thread[%p] does not have the lock.", current_thread);
return;
}
SEAD_ASSERT_MSG(mCount != 0 && current_thread != nullptr,
"mCount[%u] must not be 0 and my_thread[%p] must not be nullptr", mCount,
current_thread);
--mCount;
if (mCount == 0)
mOwnerThread.exchange(nullptr);
}
} // namespace sead
+300
View File
@@ -0,0 +1,300 @@
#include "thread/seadThread.h"
#include "basis/seadRawPrint.h"
#include "prim/seadBitUtil.h"
#include "prim/seadPtrUtil.h"
#include "prim/seadScopedLock.h"
#include "thread/seadThreadUtil.h"
namespace sead
{
const s32 Thread::cDefaultPriority = 0x10;
bool Thread::sendMessage(MessageQueue::Element msg, MessageQueue::BlockType block_type)
{
if (msg == MessageQueue::cNullElement)
{
SEAD_ASSERT_MSG(false, "Can not send cNullElement(==%ld)", MessageQueue::cNullElement);
return false;
}
if (isDone())
{
SEAD_ASSERT_MSG(false, "Thread is done. Reject message: %ld", msg);
return false;
}
if (mQuitMsg == msg)
{
SEAD_ASSERT_MSG(false, "use quit()");
return false;
}
return mMessageQueue.push(msg, block_type);
}
MessageQueue::Element Thread::recvMessage(MessageQueue::BlockType block_type)
{
if (mState == State::cQuitting)
return 0;
return mMessageQueue.pop(block_type);
}
void Thread::quit(bool is_jam)
{
if (isDone())
{
SEAD_WARN("Thread is done. Can not quit.");
return;
}
if (mState == State::cRunning)
mState = State::cQuitting;
if (is_jam)
mMessageQueue.jam(mQuitMsg, MessageQueue::BlockType::Blocking);
else
mMessageQueue.push(mQuitMsg, MessageQueue::BlockType::Blocking);
}
void Thread::quitAndWaitDoneSingleThread(bool is_jam)
{
quit(is_jam);
waitDone();
}
constexpr u32 cStackCanaryMagic = 0x5EAD5CEC;
static bool checkStackMagic(uintptr_t addr)
{
return BitUtil::bitCastPtr<u32>(reinterpret_cast<const void*>(addr)) == cStackCanaryMagic;
}
s32 Thread::calcStackUsedSizePeak() const
{
#ifdef SEAD_DEBUG
// FIXME
return 0;
#else
return 0;
#endif
}
void Thread::checkStackOverFlow(const char* source_file, s32 source_line) const
{
checkStackPointerOverFlow(source_file, source_line);
checkStackEndCorruption(source_file, source_line);
}
void Thread::checkStackEndCorruption(const char* source_file, s32 source_line) const
{
if (ThreadMgr::instance()->getMainThread() == this)
return;
const uintptr_t start = getStackCheckStartAddress_();
if (!start)
return;
SEAD_ASSERT_MSG(checkStackMagic(start),
"sead::Thread Stack End Corruption! [%s:%p]\n"
" Source File: %s\n"
" Line Number: %d\n"
" Stack Size: %d",
getName().cstr(), this,
source_file ? source_file : SafeString::cEmptyString.cstr(), source_line,
getStackSize());
}
void Thread::checkStackPointerOverFlow(const char* source_file, s32 source_line) const
{
if (!ThreadMgr::instance() || ThreadMgr::instance()->getCurrentThread() != this)
{
SEAD_WARN("sead::Thread::checkStackPointerOverFlow cannot be called from other thread.");
return;
}
const uintptr_t ptr = ThreadUtil::GetCurrentStackPointer();
const uintptr_t start = getStackCheckStartAddress_();
if (start)
{
SEAD_ASSERT_MSG(start <= ptr,
"sead::Thread Stack Pointer Overflow! [%s:%p]\n"
" Source File: %s\n"
" Line Number: %d\n"
" Stack Size: %d, Over Size: %ld",
getName().cstr(), this,
source_file ? source_file : SafeString::cEmptyString.cstr(), source_line,
getStackSize(), start - ptr);
}
else
{
ThreadMgr::instance()->getCurrentThread();
}
}
void Thread::setStackOverflowExceptionEnable(bool)
{
SEAD_WARN("This platform cannot set stack overflow exception.");
}
void Thread::run_()
{
while (true)
{
#ifdef SEAD_DEBUG
checkStackOverFlow(nullptr, 0);
#endif
const MessageQueue::Element msg = mMessageQueue.pop(mBlockType);
if (msg == mQuitMsg)
break;
calc_(msg);
}
}
// NON_MATCHING: the first loop gets unrolled and the loop counter is not negated
void Thread::initStackCheck_()
{
void* const start = reinterpret_cast<void*>(getStackCheckStartAddress_());
void* const end = PtrUtil::addOffset(mStackTopForCheck, mStackSize);
u32* addr = static_cast<u32*>(start);
if (start >= end)
return;
const size_t len = uintptr_t(end) + (-uintptr_t(start) - 1);
for (u32 i = 0; i < ((len / 4 + 1) % 8); ++i)
*addr++ = cStackCanaryMagic;
if (len >= 0x1C)
{
do
{
for (s32 i = 0; i < 8; ++i)
*addr++ = cStackCanaryMagic;
} while (addr < end);
}
}
// NON_MATCHING: see Thread::initStackCheck_
void Thread::initStackCheckWithCurrentStackPointer_()
{
void* const start = reinterpret_cast<void*>(getStackCheckStartAddress_());
void* const end = reinterpret_cast<void*>(ThreadUtil::GetCurrentStackPointer());
u32* addr = static_cast<u32*>(start);
if (start >= end)
return;
const size_t len = uintptr_t(end) + (-uintptr_t(start) - 1);
for (u32 i = 0; i < ((len / 4 + 1) % 8); ++i)
*addr++ = cStackCanaryMagic;
if (len >= 0x1C)
{
do
{
for (s32 i = 0; i < 8; ++i)
*addr++ = cStackCanaryMagic;
} while (addr < end);
}
}
SEAD_SINGLETON_DISPOSER_IMPL(ThreadMgr)
ThreadMgr::~ThreadMgr()
{
ScopedLock<CriticalSection> lock(getListCS());
for (Thread* thread : mList)
thread->quit(false);
bool all_done;
do
{
all_done = true;
for (Thread* thread : mList)
all_done &= thread->isDone();
Thread::yield();
} while (!all_done);
for (Thread* thread : mList)
thread->waitDone();
sInstance = nullptr;
}
void ThreadMgr::initialize(Heap* heap)
{
initMainThread_(heap);
SEAD_ASSERT(mMainThread);
}
void ThreadMgr::destroy()
{
destroyMainThread_();
}
void ThreadMgr::destroyMainThread_()
{
if (mMainThread)
{
delete mMainThread;
mMainThread = nullptr;
}
}
bool ThreadMgr::isMainThread() const
{
return getCurrentThread() == mMainThread;
}
void ThreadMgr::waitDoneMultipleThread(Thread* const* threads, s32 num)
{
bool all_done;
do
{
all_done = true;
for (s32 i = 0; i < num; ++i)
all_done &= threads[i]->isDone();
Thread::yield();
} while (!all_done);
for (s32 i = 0; i < num; ++i)
threads[i]->waitDone();
}
void ThreadMgr::quitAndWaitDoneMultipleThread(Thread** threads, s32 num, bool is_jam)
{
for (s32 i = 0; i < num; ++i)
threads[i]->quit(is_jam);
waitDoneMultipleThread(threads, num);
}
void ThreadMgr::checkCurrentThreadStackOverFlow(const char* source_file, s32 source_line)
{
if (!ThreadMgr::instance())
return;
if (Thread* thread = ThreadMgr::instance()->getCurrentThread())
thread->checkStackOverFlow(source_file, source_line);
}
void ThreadMgr::checkCurrentThreadStackEndCorruption(const char* source_file, s32 source_line)
{
if (!ThreadMgr::instance())
return;
if (Thread* thread = ThreadMgr::instance()->getCurrentThread())
thread->checkStackEndCorruption(source_file, source_line);
}
void ThreadMgr::checkCurrentThreadStackPointerOverFlow(const char* source_file, s32 source_line)
{
if (!ThreadMgr::instance())
return;
if (Thread* thread = ThreadMgr::instance()->getCurrentThread())
thread->checkStackPointerOverFlow(source_file, source_line);
}
} // namespace sead
@@ -0,0 +1,28 @@
#include "thread/seadThreadUtil.h"
#include "basis/seadRawPrint.h"
namespace sead
{
s32 ThreadUtil::ConvertPrioritySeadToPlatform(s32 prio)
{
SEAD_ASSERT(prio >= 0);
SEAD_ASSERT(prio < 32);
return prio;
}
s32 ThreadUtil::ConvertPriorityPlatformToSead(s32 prio)
{
SEAD_ASSERT(prio >= 0);
SEAD_ASSERT(prio < 32);
return prio;
}
// NON_MATCHING: two instructions are reordered; maybe this is inline assembly?
uintptr_t ThreadUtil::GetCurrentStackPointer()
{
volatile uintptr_t x = 0;
uintptr_t y;
x = uintptr_t(&y);
return x;
}
} // namespace sead
@@ -0,0 +1,187 @@
#include <basis/seadRawPrint.h>
#include <container/seadSafeArray.h>
#include <time/seadCalendarTime.h>
#include <time/seadDateUtil.h>
namespace sead
{
const CalendarTime::Month CalendarTime::cMonth_Jan = 1;
const CalendarTime::Month CalendarTime::cMonth_Feb = 2;
const CalendarTime::Month CalendarTime::cMonth_Mar = 3;
const CalendarTime::Month CalendarTime::cMonth_Apr = 4;
const CalendarTime::Month CalendarTime::cMonth_May = 5;
const CalendarTime::Month CalendarTime::cMonth_Jun = 6;
const CalendarTime::Month CalendarTime::cMonth_Jul = 7;
const CalendarTime::Month CalendarTime::cMonth_Aug = 8;
const CalendarTime::Month CalendarTime::cMonth_Sep = 9;
const CalendarTime::Month CalendarTime::cMonth_Oct = 10;
const CalendarTime::Month CalendarTime::cMonth_Nov = 11;
const CalendarTime::Month CalendarTime::cMonth_Dec = 12;
const CalendarTime::Year CalendarTime::cDefaultYear = 1970;
const CalendarTime::Month CalendarTime::cDefaultMonth = 1;
const CalendarTime::Day CalendarTime::cDefaultDay = 1;
const CalendarTime::Hour CalendarTime::cDefaultHour = 0;
const CalendarTime::Minute CalendarTime::cDefaultMinute = 0;
const CalendarTime::Second CalendarTime::cDefaultSecond = 0;
void CalendarTime::Year::setValue(u32 year)
{
mValue = year;
}
CalendarTime::Month::Month(u32 month)
{
setValueOneOrigin(month);
}
void CalendarTime::Month::setValueOneOrigin(u32 m)
{
SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m);
mValue = m;
}
s32 CalendarTime::Month::addSelf(u32 rhs)
{
const s32 val = (s32(rhs) + mValue + -1) % 12;
mValue = val + 1;
SEAD_ASSERT(1 <= mValue && mValue <= 12);
return val;
}
s32 CalendarTime::Month::subSelf(u32 rhs)
{
const s32 val = (mValue - s32(rhs) % 12 + 12 - 1) % 12u;
mValue = val + 1;
SEAD_ASSERT(1 <= mValue && mValue <= 12);
return val;
}
s32 CalendarTime::Month::sub(CalendarTime::Month rhs) const
{
return s32(mValue) - rhs.getValueOneOrigin();
}
SafeString CalendarTime::Month::makeStringOneOrigin(u32 m)
{
SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m);
switch (m)
{
case 1:
return "Jan";
case 2:
return "Feb";
case 3:
return "Mar";
case 4:
return "Apr";
case 5:
return "May";
case 6:
return "Jun";
case 7:
return "Jul";
case 8:
return "Aug";
case 9:
return "Sep";
case 10:
return "Oct";
case 11:
return "Nov";
case 12:
default:
return "Dec";
}
}
CalendarTime::Month CalendarTime::Month::makeFromValueOneOrigin(u32 m)
{
SEAD_ASSERT(1 <= m && m <= 12);
return Month(m);
}
void CalendarTime::Day::setValue(u32 day)
{
SEAD_ASSERT_MSG(1 <= day && day <= 31, "wrong day. correct range is [1, 31]. your param %d",
day);
mValue = day;
}
void CalendarTime::Hour::setValue(u32 hour)
{
SEAD_ASSERT_MSG(hour <= 23, "wrong hour. correct range is [0, 23]. your param %d", hour);
mValue = hour;
}
void CalendarTime::Minute::setValue(u32 minute)
{
SEAD_ASSERT_MSG(minute <= 59, "wrong minute. correct range is [0, 59]. your param %d", minute);
mValue = minute;
}
void CalendarTime::Second::setValue(u32 second)
{
SEAD_ASSERT_MSG(second <= 59, "wrong day. correct range is [0, 59]. your param %d", second);
mValue = second;
}
CalendarTime::Date::Date(const CalendarTime::Year& y, const CalendarTime::Month& m,
const CalendarTime::Day& d)
: mYear(y), mMonth(m), mDay(d)
{
mWeek = DateUtil::calcWeekDay(y, m, d);
}
CalendarTime::Time::Time(const CalendarTime::Hour& h, const CalendarTime::Minute& m,
const CalendarTime::Second& s)
: mHour(h), mMinute(m), mSecond(s)
{
}
CalendarTime::CalendarTime(const CalendarTime::Date& date, const CalendarTime::Time& time)
: mDate(date), mTime(time)
{
}
CalendarTime::CalendarTime(const CalendarTime::Year& y, const CalendarTime::Month& m,
const CalendarTime::Day& d, const CalendarTime::Hour& hour,
const CalendarTime::Minute& minute, const CalendarTime::Second& second)
: mDate(y, m, d), mTime(hour, minute, second)
{
}
void CalendarTime::setDate(const CalendarTime::Date& date)
{
mDate = date;
mDate.calcWeek();
}
u32 CalendarTime::getYearDays() const
{
const u32 m = mDate.mMonth.getValueOneOrigin();
SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m);
static const u32 sCumulativeNumberOfDays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
};
u32 num_days = mDate.mDay.getValue() + sCumulativeNumberOfDays[m - 1];
if (m >= 3)
num_days += DateUtil::isLeapYear(mDate.mYear.getValue());
return num_days;
}
void CalendarTime::Date::calcWeek()
{
mWeek = DateUtil::calcWeekDay(mYear, mMonth, mDay);
}
void CalendarTime::makeWeekDayNameLabel_(BufferedSafeString* out_str, CalendarTime::Week week)
{
static const SafeArray<const char*, 7> labels = {{"", "", "", "", "", "", ""}};
out_str->format("曜日:%s", labels[s32(week)]);
}
} // namespace sead
@@ -0,0 +1,41 @@
#include <time/seadDateSpan.h>
#include <time/seadDateUtil.h>
namespace sead
{
DateSpan::DateSpan(s64 span) : mSpan(span) {}
DateSpan::DateSpan(const CalendarSpan::Day& d, const CalendarSpan::Hour& h,
const CalendarSpan::Minute& m, const CalendarSpan::Second& s)
{
set(d, h, m, s);
}
DateSpan::DateSpan(const CalendarSpan& span)
{
set(span);
}
s64 DateSpan::set(const CalendarSpan& span)
{
return setTimeImpl_(span.getDays(), span.getHours(), span.getMinutes(), span.getSeconds());
}
s64 DateSpan::set(const CalendarSpan::Day& d, const CalendarSpan::Hour& h,
const CalendarSpan::Minute& m, const CalendarSpan::Second& s)
{
return setTimeImpl_(d.getValue(), h.getValue(), m.getValue(), s.getValue());
}
void DateSpan::getCalendarSpan(CalendarSpan* out_span) const
{
DateUtil::calcSecondToCalendarSpan(out_span, mSpan);
}
s64 DateSpan::setTimeImpl_(s32 d, s32 h, s32 m, s32 s)
{
mSpan = 86400ll * d + 3600ll * h + 60ll * m + s;
return mSpan;
}
} // namespace sead
+224
View File
@@ -0,0 +1,224 @@
#ifdef NNSDK
#include <nn/time.h>
#else
#error "Unknown platform"
#endif
#include "basis/seadRawPrint.h"
#include "time/seadDateTime.h"
#include "time/seadDateUtil.h"
namespace sead
{
namespace
{
constexpr u32 sDaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
constexpr u32 sDaysSinceJan1[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
u32 convertCalendarDateToDaysSince1970(const CalendarTime::Date& date)
{
// 0-indexed day.
const u32 d0 = date.mDay.getValue() - 1;
const u32 m = date.mMonth.getValueOneOrigin();
SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m);
const u32 y = date.mYear.getValue();
SEAD_ASSERT_MSG(y >= 1970, "wrong year. your param %d, must after 1970.", y);
const u32 days_since_jan1 = sDaysSinceJan1[m - 1];
u32 num_days = d0 + days_since_jan1;
if (m >= 3 && DateUtil::isLeapYear(date.mYear.getValue()))
num_days = d0 + days_since_jan1 + 1;
u32 num_days_since_1970 = num_days + 365 * (date.mYear.getValue() - 1970);
if (date.mYear.getValue() > 1970)
{
u32 year = 1970;
do
{
num_days_since_1970 += DateUtil::isLeapYear(year);
++year;
} while (year < date.mYear.getValue());
}
return num_days_since_1970;
}
u64 convertCalendarTimeToSeconds(const CalendarTime::Time& time)
{
return 60 * (60 * time.mHour.getValue() + time.mMinute.getValue()) + time.mSecond.getValue();
}
u64 convertCalendarDateTimeToSeconds(const CalendarTime::Date& date, const CalendarTime::Time& time)
{
const u32 y = date.mYear.getValue();
const s32 m = date.mMonth.getValueOneOrigin();
const u32 m_idx = m - 1;
SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m);
u32 num_days;
if (m == 2 && DateUtil::isLeapYear(y))
num_days = sDaysOfMonth[m_idx] + 1;
else
num_days = sDaysOfMonth[m_idx];
const u32 d = date.mDay.getValue();
SEAD_ASSERT_MSG(d <= num_days, "wrong day, correct range is [1, %d] (when year %4d month %2d)",
num_days, y, m);
const u32 days_since_1970 = convertCalendarDateToDaysSince1970(date);
return 3600 * 24 * days_since_1970 + convertCalendarTimeToSeconds(time);
}
u32 convertDaysToYears(u32* days)
{
u32 days_to_remove;
u32 i = 0;
u32 year = 1969;
do
{
++year;
days_to_remove = i;
i += DateUtil::isLeapYear(year) ? 366 : 365;
} while (i <= *days);
*days -= days_to_remove;
return year;
}
s32 convertDaysToMonth(u32* days, u32 year)
{
SEAD_ASSERT_MSG(*days <= 365, "wrong days. correct range is [0, 365]. your param %d", *days);
u32 days_to_remove;
u32 month_idx = 0;
u32 i = 0;
do
{
days_to_remove = i;
i += (month_idx == 1 && DateUtil::isLeapYear(year)) ? 29 : sDaysOfMonth[month_idx];
if (*days < i)
break;
++month_idx;
} while (month_idx < 12);
*days -= days_to_remove - 1;
return 1 + month_idx;
}
} // namespace
bool DateTime::mIsInitialized = false;
DateTime::DateTime(u64 unix_time)
{
mUnixTime = unix_time;
}
DateTime::DateTime(const CalendarTime& time)
{
setUnixTime(time);
}
DateTime::DateTime(const CalendarTime::Year& year, const CalendarTime::Month& month,
const CalendarTime::Day& day, const CalendarTime::Hour& hour,
const CalendarTime::Minute& minute, const CalendarTime::Second& second)
{
setUnixTime(year, month, day, hour, minute, second);
}
u64 DateTime::setNow()
{
#ifdef NNSDK
initializeSystemTimeModule();
nn::time::PosixTime now;
nn::time::CalendarTime ctime;
nn::time::StandardUserSystemClock::GetCurrentTime(&now);
nn::time::ToCalendarTime(&ctime, nullptr, now);
const auto year = CalendarTime::Year(ctime.year);
const auto month = CalendarTime::Month::makeFromValueOneOrigin(ctime.month);
const auto day = CalendarTime::Day(ctime.day);
const auto hour = CalendarTime::Hour(ctime.hour);
const auto minute = CalendarTime::Minute(ctime.minute);
const auto second = CalendarTime::Second(ctime.second);
setUnixTime(year, month, day, hour, minute, second);
#endif
return mUnixTime;
}
u64 DateTime::setUnixTime(const CalendarTime& time)
{
mUnixTime = convertCalendarDateTimeToSeconds(time.getDate(), time.getTime());
return mUnixTime;
}
u64 DateTime::setUnixTime(const CalendarTime::Year& year, const CalendarTime::Month& month,
const CalendarTime::Day& day, const CalendarTime::Hour& hour,
const CalendarTime::Minute& minute, const CalendarTime::Second& second)
{
CalendarTime::Date date(year, month, day);
CalendarTime::Time time(hour, minute, second);
mUnixTime = convertCalendarDateTimeToSeconds(date, time);
return mUnixTime;
}
void DateTime::getCalendarTime(CalendarTime* calendar) const
{
u32 d = mUnixTime / (3600 * 24);
const u32 y = convertDaysToYears(&d);
const u32 m = convertDaysToMonth(&d, y);
CalendarTime::Time time;
const auto reduced_time = mUnixTime % (3600 * 24);
time.mHour.setValue(reduced_time / 3600);
time.mMinute.setValue((reduced_time % 3600) / 60);
time.mSecond.setValue(reduced_time % 60);
if (calendar)
{
calendar->setDate(CalendarTime::Date(y, CalendarTime::Month::makeFromValueOneOrigin(m), d));
calendar->setTime(time);
}
}
DateSpan DateTime::diff(DateTime time) const
{
return DateSpan(mUnixTime - time.mUnixTime);
}
DateSpan DateTime::diffToNow() const
{
DateTime now(0);
now.setNow();
return now.diff(*this);
}
void DateTime::initializeSystemTimeModule()
{
if (mIsInitialized)
return;
#ifdef NNSDK
if (!nn::time::IsInitialized())
nn::time::Initialize();
#endif
mIsInitialized = true;
}
DateSpan operator-(DateTime lhs, DateTime rhs)
{
return DateSpan(lhs.getUnixTime() - rhs.getUnixTime());
}
DateTime operator-(DateTime time, DateSpan span)
{
return DateTime(time.getUnixTime() - span.getSpan());
}
DateTime operator+(DateTime time, DateSpan span)
{
return DateTime(time.getUnixTime() + span.getSpan());
}
} // namespace sead
@@ -0,0 +1,138 @@
#ifdef NNSDK
#include <nn/time.h>
#else
#error "Unknown platform"
#endif
#include "basis/seadRawPrint.h"
#include "time/seadDateTime.h"
#include "time/seadDateUtil.h"
namespace sead
{
DateTimeUtc::DateTimeUtc(u64 unix_time)
{
mUnixTime = unix_time;
}
DateTimeUtc::DateTimeUtc(const DateTime& date_time)
{
CalendarTime time;
date_time.getCalendarTime(&time);
#ifdef NNSDK
// Note: time sysmodule is not initialised here.
nn::time::CalendarTime nn_time;
nn_time.year = time.getYear();
nn_time.month = time.getMonth().getValueOneOrigin();
nn_time.day = time.getDay();
nn_time.hour = time.getHour();
nn_time.minute = time.getMinute();
nn_time.second = time.getSecond();
nn::time::PosixTime posix_time;
int dummy = 0;
nn::time::ToPosixTime(&dummy, &posix_time, 1, nn_time);
mUnixTime = posix_time.time;
#endif
}
DateTimeUtc::DateTimeUtc(const CalendarTime& time)
{
setUnixTime(time);
}
DateTimeUtc::DateTimeUtc(const CalendarTime::Year& year, const CalendarTime::Month& month,
const CalendarTime::Day& day, const CalendarTime::Hour& hour,
const CalendarTime::Minute& minute, const CalendarTime::Second& second)
{
setUnixTime(year, month, day, hour, minute, second);
}
u64 DateTimeUtc::setNow()
{
#ifdef NNSDK
DateTime::initializeSystemTimeModule();
nn::time::PosixTime now;
nn::time::StandardUserSystemClock::GetCurrentTime(&now);
mUnixTime = now.time;
#endif
return mUnixTime;
}
u64 DateTimeUtc::setUnixTime(const CalendarTime& time)
{
#ifdef NNSDK
DateTime::initializeSystemTimeModule();
nn::time::CalendarTime nn_time;
nn_time.year = time.getYear();
nn_time.month = time.getMonth().getValueOneOrigin();
nn_time.day = time.getDay();
nn_time.hour = time.getHour();
nn_time.minute = time.getMinute();
nn_time.second = time.getSecond();
mUnixTime = nn::time::ToPosixTimeFromUtc(nn_time).time;
#endif
return mUnixTime;
}
u64 DateTimeUtc::setUnixTime(const CalendarTime::Year& year, const CalendarTime::Month& month,
const CalendarTime::Day& day, const CalendarTime::Hour& hour,
const CalendarTime::Minute& minute, const CalendarTime::Second& second)
{
DateTime::initializeSystemTimeModule();
CalendarTime time(year, month, day, hour, minute, second);
#ifdef NNSDK
nn::time::CalendarTime nn_time;
nn_time.year = time.getYear();
nn_time.month = time.getMonth().getValueOneOrigin();
nn_time.day = time.getDay();
nn_time.hour = time.getHour();
nn_time.minute = time.getMinute();
nn_time.second = time.getSecond();
mUnixTime = nn::time::ToPosixTimeFromUtc(nn_time).time;
#endif
return mUnixTime;
}
void DateTimeUtc::getCalendarTime(CalendarTime* out) const
{
#ifdef NNSDK
DateTime::initializeSystemTimeModule();
const nn::time::CalendarTime ctime = nn::time::ToCalendarTimeInUtc({mUnixTime});
CalendarTime::Date date(ctime.year, CalendarTime::Month::makeFromValueOneOrigin(ctime.month),
ctime.day);
CalendarTime::Time time(ctime.hour, ctime.minute, ctime.second);
#endif
out->setDate(date);
out->setTime(time);
}
DateSpan DateTimeUtc::diff(DateTimeUtc time) const
{
return DateSpan(mUnixTime - time.mUnixTime);
}
DateSpan DateTimeUtc::diffToNow() const
{
DateTimeUtc now(0);
now.setNow();
return now.diff(*this);
}
DateSpan operator-(DateTimeUtc lhs, DateTimeUtc rhs)
{
return DateSpan(lhs.getUnixTime() - rhs.getUnixTime());
}
DateTimeUtc operator-(DateTimeUtc time, DateSpan span)
{
return DateTimeUtc(time.getUnixTime() - span.getSpan());
}
DateTimeUtc operator+(DateTimeUtc time, DateSpan span)
{
return DateTimeUtc(time.getUnixTime() + span.getSpan());
}
} // namespace sead
+196
View File
@@ -0,0 +1,196 @@
#include <prim/seadStringUtil.h>
#include <time/seadCalendarSpan.h>
#include <time/seadDateUtil.h>
namespace sead
{
namespace DateUtil
{
bool isLeapYear(u32 year)
{
#ifdef MATCHING_HACK_NX_CLANG
bool div100, div4;
return (div100 = year % 100 == 0, div4 = year % 4 == 0, !div100 & div4) | (year % 400 == 0);
#else
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
#endif
}
CalendarTime::Week calcWeekDay(const CalendarTime::Year& year, const CalendarTime::Month& month,
const CalendarTime::Day& day)
{
int y = year.getValue();
int m = month.getValueOneOrigin();
int d = day.getValue();
if (m < 3)
{
y -= 1;
m += 12;
}
d += y + (y / 4) - (y / 100) + (y / 400);
d += (26 * m + 16) / 10;
return CalendarTime::Week(d % 7);
}
void calcSecondToCalendarSpan(CalendarSpan* out_span, u64 sec)
{
if (!out_span)
return;
out_span->setDays(sec / (3600 * 24));
out_span->setHours((sec % (3600 * 24)) / 3600);
out_span->setMinutes((sec % 3600) / 60);
out_span->setSeconds(sec % 60);
}
bool parseW3CDTFSubString(bool* ok, u32* value, SafeString* str, s32* str_length,
char* out_separator, s32 parse_length, const SafeString& separators,
bool allow_null_separator, u32 value_min, u32 value_max)
{
if (*str_length < parse_length)
{
*ok = false;
return true;
}
const char c = str->at(parse_length);
if (!separators.include(c) && (!allow_null_separator || c != SafeString::cNullChar))
{
*ok = false;
return true;
}
FixedSafeString<8> buffer;
buffer.copy(*str, parse_length);
if (!StringUtil::tryParseU32(value, buffer, StringUtil::CardinalNumber::Base10) ||
*value < value_min || *value > value_max)
{
*ok = false;
return true;
}
if (c == SafeString::cNullChar)
{
*ok = true;
return true;
}
*str = str->getPart(parse_length + 1);
*str_length -= parse_length + 1;
if (out_separator)
*out_separator = c;
return false;
}
static bool parseW3CDTFStringImpl(u32* year, u32* month, u32* day, u32* hour, u32* minute,
u32* second, s32* tz_hour, s32* tz_minute,
const SafeString& string)
{
s32 len = string.calcLength();
bool ok = true;
SafeString substr = string;
char separator;
if (parseW3CDTFSubString(&ok, year, &substr, &len, &separator, 4, "-", true, 0, 0xFFFFFFFF))
return ok;
if (parseW3CDTFSubString(&ok, month, &substr, &len, &separator, 2, "-", true, 1, 12))
return ok;
if (parseW3CDTFSubString(&ok, day, &substr, &len, &separator, 2, "T", true, 1, 31))
return ok;
if (parseW3CDTFSubString(&ok, hour, &substr, &len, &separator, 2, ":", false, 0, 23))
return ok;
if (parseW3CDTFSubString(&ok, minute, &substr, &len, &separator, 2, ":+-Z", true, 0, 59))
return ok;
if (separator == ':')
{
if (parseW3CDTFSubString(&ok, second, &substr, &len, &separator, 2, ".+-Z", true, 0, 59))
return ok;
if (separator == '.')
{
if (len == 0)
return false;
auto it = substr.tokenBegin("+-Z");
++it;
auto end = substr.tokenEnd("+-Z");
// No timezone information
if (it == end)
{
*tz_hour = 0;
*tz_minute = 0;
return true;
}
separator = substr.at(it.getIndex() - 1);
substr = substr.getPart(it.getIndex());
len -= it.getIndex();
}
}
if (separator != '+' && separator != '-')
checkLength:
return len == 0;
bool done;
u32 tz_hour_abs = 0;
done = parseW3CDTFSubString(&ok, &tz_hour_abs, &substr, &len, nullptr, 2, ":", false, 0, 11);
if (ok)
{
*tz_hour = tz_hour_abs;
if (separator == '-')
*tz_hour = -tz_hour_abs;
}
if (done)
return ok;
u32 tz_minute_abs = 0;
done = parseW3CDTFSubString(&ok, &tz_minute_abs, &substr, &len, nullptr, 2, "", true, 0, 59);
if (ok)
{
*tz_minute = tz_minute_abs;
if (separator == '-')
*tz_minute = -tz_minute_abs;
if (done)
{
len -= 2;
goto checkLength;
}
}
return false;
}
bool parseW3CDTFString(CalendarTime* out_time, CalendarSpan* time_zone, const SafeString& string)
{
u32 year = 1970;
u32 month = 1;
u32 day = 1;
u32 hour = 0;
u32 minute = 0;
u32 second = 0;
s32 tz_hour = 0;
s32 tz_minute = 0;
const bool ret = parseW3CDTFStringImpl(&year, &month, &day, &hour, &minute, &second, &tz_hour,
&tz_minute, string);
if (ret)
{
out_time->setDate({year, CalendarTime::Month::makeFromValueOneOrigin(month), day});
out_time->setTime({hour, minute, second});
time_zone->setDays(0);
time_zone->setHours(tz_hour);
time_zone->setMinutes(tz_minute);
time_zone->setSeconds(0);
}
return ret;
}
} // namespace DateUtil
} // namespace sead
@@ -0,0 +1,52 @@
#include <cstdlib>
#include <limits>
#ifdef NNSDK
#include <nn/os.h>
#endif
#include <time/seadTickSpan.h>
namespace sead
{
#ifdef NNSDK
const s64 TickSpan::cFrequency = nn::os::GetSystemTickFrequency();
#else
#error "Unknown platform"
#endif
s64 TickSpan::toNanoSeconds() const
{
const s64 abs_span = std::abs(mSpan);
const s64 max = std::numeric_limits<s64>::max();
// Try to get as much precision as possible without overflowing.
if (abs_span < max / 1'000'000'000)
return 1'000'000'000 * mSpan / cFrequency;
if (abs_span < max / 1'000'000)
return 1000 * (1'000'000 * mSpan / cFrequency);
if (abs_span < max / 1000)
return 1'000'000 * (1000 * mSpan / cFrequency);
return 1'000'000'000 * (mSpan / cFrequency);
}
void TickSpan::setNanoSeconds(s64 nsec)
{
const s64 threshold = std::numeric_limits<s64>::max() / cFrequency;
const s64 abs_ns = std::abs(nsec);
if (abs_ns <= threshold)
mSpan = cFrequency * nsec / 1'000'000'000;
else if (abs_ns <= 1000 * threshold)
mSpan = cFrequency * (nsec / 1000) / 1'000'000;
else if (abs_ns <= 1'000'000 * threshold)
mSpan = cFrequency * (nsec / 1'000'000) / 1000;
else
mSpan = cFrequency * (nsec / 1'000'000'000);
}
} // namespace sead