Implement most of aocManager

And fix FileDevicePrefix's userdata param type
This commit is contained in:
Léo Lam
2021-03-27 12:35:06 +01:00
parent 96157129f8
commit c3528b3911
7 changed files with 605 additions and 58 deletions
+487 -2
View File
@@ -1,13 +1,53 @@
#include "Game/DLC/aocManager.h"
#include <container/seadBuffer.h>
#include <filedevice/seadFileDeviceMgr.h>
#include <prim/seadStringBuilder.h>
#include <resource/seadSharcArchiveRes.h>
#include "KingSystem/Resource/resLoadRequest.h"
#include "KingSystem/Resource/resResourceMgrTask.h"
#include "KingSystem/Utils/InitTimeInfo.h"
#include "KingSystem/Utils/SafeDelete.h"
#ifdef NNSDK
#include <filedevice/nin/seadNinAocFileDeviceNin.h>
#include <nn/aoc.h>
#include <nn/fs.h>
#include <prim/seadStringUtil.h>
#endif
namespace uking::aoc {
namespace {
struct DungeonInfo {
int number;
sead::SafeString flag;
};
ksys::util::InitConstants sInitConstants;
DungeonInfo sDungeonInfoData[] = {
{120, "Defeat_OneHitDungeon001"},
{121, "BalladOfHeroGerudo_AppearDungeon03"},
{122, "Defeat_OneHitDungeon002"},
{123, "Defeat_OneHitDungeon003"},
{124, "BalladOfHeroZora_AppearDungeon01"},
{125, "BalladOfHeroZora_AppearDungeon02"},
{126, "BalladOfHeroZora_AppearDungeon03"},
{127, "BalladOfHeroRito_TargetHittingSuccess"},
{128, "BalladOfHeroRito_AppearDungeon03"},
{129, "BalladOfHeroRito_AppearDungeon02"},
{130, "BalladOfHeroGoron_FirstKillGolemR"},
{131, "BalladOfHeroGoron_AppearDungeon01"},
{132, "BalladOfHeroGoron_AppearDungeon03"},
{133, "BalladOfHeroGerudo_AppearDungeon02"},
{134, "BalladOfHeroGerudo_AppearDungeon01"},
{135, "Defeat_OneHitDungeon004"},
};
sead::Buffer<DungeonInfo> sDungeonInfo{sDungeonInfoData};
constexpr int NumDungeons = 16;
} // namespace
SEAD_SINGLETON_DISPOSER_IMPL(Manager)
Manager::Manager() : mGdtReinitSlot{this, &Manager::onGdtReinit} {
@@ -18,8 +58,8 @@ Manager::~Manager() {
if (auto* gdm = ksys::gdt::Manager::instance())
gdm->removeReinitCallback(mGdtReinitSlot);
mFileDevicePrefix.deregister();
mFileDevicePrefix2.deregister();
mAocMainFieldPackPrefix.deregister();
mAocPackPrefix.deregister();
mVersionFileDevPrefix.deregister();
if (mFileDevice) {
@@ -43,4 +83,449 @@ void Manager::resetFlags() {
mFlagHasAocVer3 = ksys::gdt::InvalidHandle;
}
void Manager::init(sead::Heap* heap) {
#ifdef NNSDK
std::array<int, 2> dlc_info;
if (nn::aoc::ListAddOnContent(dlc_info.data(), 0, dlc_info.size()) == dlc_info.size() &&
(dlc_info[0] == 1 || dlc_info[1] == 1)) {
size_t cache_size = 0;
nn::fs::QueryMountAddOnContentCacheSize(&cache_size, 1);
const size_t cache_size_ = cache_size;
mAocFsCache = static_cast<u8*>(heap->tryAlloc(cache_size_, sizeof(void*)));
nn::fs::MountAddOnContent("aoc", 1, mAocFsCache, cache_size);
mFileDevice = new (heap) sead::NinAocFileDevice("aoc");
sead::FileDeviceMgr::instance()->mount(mFileDevice, "aoc");
loadVersionFile();
}
#endif
}
void Manager::loadVersionFile() {
if (!mFileDevice)
return;
mVersionFileDevPrefix.registerPrefix("Aoc/0010/", mFileDevice, false);
ksys::res::LoadRequest req;
req.mRequester = "aocManager";
req._26 = false;
req.mAocFileDevice = mFileDevice;
const sead::SafeString path = "System/AocVersion.txt";
mVersionFile->file_handle.requestLoad(path, &req);
}
void Manager::loadAocMainFieldPack(ksys::OverlayArena* arena) {
if (!hasAoc3())
return;
auto* device = mFileDevice;
if (!device)
return;
ksys::res::LoadRequest req;
req.mRequester = "aocManager";
req._8 = true;
req._26 = false;
req.mAocFileDevice = device;
req.mArena = arena;
mAocMainFieldPack.requestLoad("Pack/AocMainField.pack", &req);
}
void Manager::registerAocMainFieldPack() {
mAocMainFieldPack.waitForReady();
mAocMainFieldPack.parseResource(nullptr);
if (mAocMainFieldPack.isSuccess())
mAocMainFieldPackPrefix.registerPrefix("Aoc/0010/", mAocMainFieldPack.getResource(), false);
}
void Manager::unloadAocMainFieldPack() {
mAocMainFieldPackPrefix.deregister();
mAocMainFieldPack.requestUnload();
}
sead::FileDevice* Manager::getFileDeviceForMapFile(const sead::SafeString& path) const {
if (!hasAoc2())
return nullptr;
auto* device = mFileDevice;
if (aocPackHasFile(path))
return nullptr;
if (path.startsWith("Map/MainField/"))
return device;
if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/"))
return device;
if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/"))
return device;
return nullptr;
}
bool Manager::getFileDeviceForMap(sead::FileDevice** p_file_device, ksys::res::Handle** p_handle,
const sead::SafeString& path) {
if (!hasAoc2())
return false;
auto* device = mFileDevice;
if (path.startsWith("Map/MainField/")) {
if (mAocMainFieldPack.isSuccess())
*p_handle = &mAocMainFieldPack;
else
*p_file_device = device;
return true;
}
if (aocPackHasFile(path))
return false;
if (hasAoc3() && path.startsWith("Map/MainFieldDungeon/")) {
*p_file_device = device;
return true;
}
if (isAocFile(path, "Map/", "Map/AocField/", "Map/MainFieldDungeon/", "Map/CDungeon/")) {
*p_file_device = device;
return true;
}
return false;
}
sead::FileDevice* Manager::getFileDeviceForStaticCompound(const sead::SafeString& path) const {
if (!hasAoc2())
return nullptr;
auto* device = mFileDevice;
if (aocPackHasFile(path))
return nullptr;
if (isAocFile(path, "Physics/StaticCompound/", "Physics/StaticCompound/AocField/",
"Physics/StaticCompound/MainFieldDungeon/", "Physics/StaticCompound/CDungeon/")) {
return device;
}
return nullptr;
}
sead::FileDevice* Manager::getFileDeviceForTeraMesh(const sead::SafeString& path) const {
if (!hasAoc2())
return nullptr;
if (path.startsWith("Physics/TeraMeshRigidBody/AocField/"))
return mFileDevice;
return nullptr;
}
sead::FileDevice* Manager::getFileDeviceForNavMesh(const sead::SafeString& path) const {
if (!hasAoc2())
return nullptr;
auto* device = mFileDevice;
if (aocPackHasFile(path))
return nullptr;
if (isAocFile(path, "NavMesh/", "NavMesh/AocField/", "NavMesh/MainFieldDungeon/",
"NavMesh/CDungeon/")) {
return device;
}
return nullptr;
}
sead::FileDevice* Manager::getFileDeviceForTerrain(const sead::SafeString& path) const {
if (!hasAoc2())
return nullptr;
if (path.startsWith("Terrain/A/AocField"))
return mFileDevice;
return nullptr;
}
sead::FileDevice* Manager::getFileDeviceForGame(const sead::SafeString& path) const {
if (!hasAoc2())
return nullptr;
if (path.startsWith("Game/AocField/"))
return mFileDevice;
return nullptr;
}
sead::FileDevice* Manager::getFileDeviceForUI(const sead::SafeString& path) const {
if (!hasAoc3())
return nullptr;
if (path.startsWith("UI/StaffRollDLC/"))
return mFileDevice;
return nullptr;
}
bool Manager::isAocFile(const sead::SafeString& path, const sead::SafeString& dir,
const sead::SafeString& dir_aoc_field,
const sead::SafeString& dir_main_field_dungeon,
const sead::SafeString& dir_cdungeon) const {
if (!path.startsWith(dir))
return false;
if (path.startsWith(dir_aoc_field))
return true;
if (path.startsWith(dir_main_field_dungeon)) {
const auto rel_path = path.getPart(dir_main_field_dungeon.calcLength());
if (rel_path.startsWith("RemainsElectric"))
return false;
if (rel_path.startsWith("RemainsFire"))
return false;
if (rel_path.startsWith("RemainsWater"))
return false;
if (rel_path.startsWith("RemainsWind"))
return false;
return true;
}
if (path.startsWith(dir_cdungeon)) {
const auto rel_path = path.getPart(dir_cdungeon.calcLength());
return isAocDungeon(rel_path);
}
return false;
}
static bool isAocDungeonNumber(const sead::SafeString& number) {
int num;
sead::FixedSafeString<4> buffer;
buffer.copy(number, 3);
return sead::StringUtil::tryParseS32(&num, buffer, sead::StringUtil::CardinalNumber::Base10) &&
num >= 120;
}
bool Manager::isAocDungeon(const sead::SafeString& map_name) {
if (!map_name.startsWith("Dungeon"))
return true;
const auto number = map_name.getPart(sead::SafeString("Dungeon").calcLength());
return isAocDungeonNumber(number);
}
bool Manager::isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name) {
if (isAocField(map_type))
return true;
if (map_type == "MainFieldDungeon") {
if (map_name == "RemainsElectric")
return false;
if (map_name == "RemainsFire")
return false;
if (map_name == "RemainsWater")
return false;
if (map_name == "RemainsWind")
return false;
return true;
}
if (map_type == "CDungeon")
return isAocDungeon(map_name);
return false;
}
bool Manager::isAocField(const sead::SafeString& map_type) {
return map_type == "AocField";
}
sead::FileDevice* Manager::getFileDeviceForDungeonPack(const sead::SafeString& path) const {
if (!hasAoc3())
return nullptr;
auto* device = mFileDevice;
const sead::SafeString prefix = "Pack/";
const auto prefix_len = prefix.calcLength();
if (!path.startsWith(prefix))
return nullptr;
const auto rel_path = path.getPart(prefix_len);
if (rel_path.startsWith("Dungeon") && !isAocDungeon(rel_path))
device = nullptr;
return device;
}
void Manager::registerAocPack(ksys::res::Handle* pack) {
mAocPack = pack;
if (pack) {
mAocPackPrefix.registerPrefix("Aoc/0010/", pack->getResource(), true);
} else {
mAocPackPrefix.deregister();
}
}
bool Manager::aocPackHasFile(const sead::SafeString& path) const {
if (!mAocPack)
return false;
const auto* res = sead::DynamicCast<sead::SharcArchiveRes>(mAocPack->getResource());
if (!res)
return false;
sead::FixedStringBuilder<0x81> builder;
builder.copy(path.cstr());
ksys::res::ResourceMgrTask::instance()->addSExtensionPrefix(builder);
return res->isExistFile(builder);
}
bool Manager::changeMoviePath(sead::BufferedSafeString& path) const {
if (!hasAoc3())
return false;
const sead::SafeString prefix = "Movie/";
const auto prefix_len = prefix.calcLength();
if (!path.startsWith(prefix))
return false;
const auto rel_path = path.getPart(prefix_len);
if (!rel_path.startsWith("Demo6"))
return false;
path.prepend("aoc://");
return true;
}
bool Manager::parseVersion() {
if (!mVersionFile->readVersion())
return false;
if (mVersionFile->string.isEmpty()) {
mVersion = 0;
return true;
}
{
const int dot_index = mVersionFile->string.findIndex(".");
const int minor_index = dot_index + 1;
if (dot_index <= 0 || minor_index >= mVersionFile->string.calcLength()) {
mVersionFile->string.clear();
mVersion = 0;
return true;
}
u32 major, minor;
const auto parse = [&] {
sead::FixedSafeString<16> major_str;
major_str.copy(mVersionFile->string, dot_index);
constexpr auto base = sead::StringUtil::CardinalNumber::Base10;
if (!sead::StringUtil::tryParseU32(&major, major_str, base))
return false;
const auto minor_str = mVersionFile->string.getPart(minor_index);
if (!sead::StringUtil::tryParseU32(&minor, minor_str, base))
return false;
return true;
};
if (!parse()) {
mVersionFile->string.clear();
mVersion = 0;
return true;
}
mVersion = (major << 8) + minor;
}
checkVersion();
return true;
}
// NON_MATCHING: stack and duplicated branch -- volatile variables are painful
void Manager::checkVersion() {
if (mVersion == 0)
return;
const auto fail = [this](VersionError error) {
mVersionFlags.setBit(error);
mVersion = 0;
};
if (mVersion < 0x300) {
fail(VersionError::TooOld);
} else if (mVersion >= 0x400) {
fail(VersionError::TooNew);
}
mVersionFlags.isOnBit(VersionError(VersionError::TooOld));
mVersionFlags.isOnBit(VersionError(VersionError::TooNew));
}
bool Manager::VersionFile::readVersion() {
if (!file_handle.isFlag2Set())
return true;
if (file_handle.isSuccess()) {
auto* res = sead::DynamicCast<sead::DirectResource>(file_handle.getResource());
string.copy(reinterpret_cast<const char*>(res->getRawData()), res->getRawSize());
} else {
if (!file_handle.checkLoadStatus())
return false;
string.clear();
}
file_handle.requestUnload();
return true;
}
void Manager::initGameData() {
reinitFlags();
ksys::gdt::Manager::instance()->addReinitCallback(mGdtReinitSlot);
}
static const sead::SafeString& getDungeonFlag(int number) {
for (auto it = sDungeonInfo.begin(); it != sDungeonInfo.end(); ++it) {
if (it->number == number)
return it->flag;
}
return sead::SafeString::cEmptyString;
}
void Manager::reinitFlags() {
mFlagAocVerAtLastPlay = ksys::gdt::Manager::instance()->getS32Handle("AoCVerAtLastPlay");
mFlagLatestAocVerPlayed = ksys::gdt::Manager::instance()->getS32Handle("LatestAoCVerPlayed");
mFlagHasAocVer1 =
ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer1));
mFlagHasAocVer2 =
ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer2));
mFlagHasAocVer3 =
ksys::gdt::Manager::instance()->getBoolHandle(GameDataFlag::text(GameDataFlag::HasAoCVer3));
for (int i = 0; i < mDLCPositions.size(); ++i) {
ksys::gdt::FlagHandle handle = ksys::gdt::InvalidHandle;
if (i < NumDungeons) {
if (mDLCPositions[i].flag_handle == ksys::gdt::InvalidHandle)
continue;
handle = ksys::gdt::Manager::instance()->getBoolHandle(getDungeonFlag(i + 120));
}
mDLCPositions[i].flag_handle = handle;
}
}
void Manager::onGdtReinit(ksys::gdt::Manager::ReinitEvent* event) {
reinitFlags();
}
} // namespace uking::aoc
+57 -9
View File
@@ -3,14 +3,19 @@
#include <container/seadSafeArray.h>
#include <heap/seadDisposer.h>
#include <math/seadVector.h>
#include <prim/seadEnum.h>
#include <prim/seadSafeString.h>
#include <prim/seadStorageFor.h>
#include <prim/seadTypedBitFlag.h>
#include <prim/seadBitFlag.h>
#include "KingSystem/GameData/gdtManager.h"
#include "KingSystem/Resource/resHandle.h"
#include "KingSystem/Resource/resResourceMgrTask.h"
#include "KingSystem/Utils/Types.h"
namespace ksys {
class OverlayArena;
}
namespace sead {
class FileDevice;
}
@@ -24,6 +29,8 @@ class Manager {
~Manager();
public:
void init(sead::Heap* heap);
u32 getVersion() const { return mVersion; }
/// @return Whether the Master Trials DLC is supported.
@@ -32,10 +39,44 @@ public:
/// @return Whether the Champion's Ballad DLC is supported.
bool hasAoc3() const { return mVersion >= 0x300; }
private:
enum class Flag : u8 {
void loadAocMainFieldPack(ksys::OverlayArena* arena);
void registerAocMainFieldPack();
void unloadAocMainFieldPack();
};
sead::FileDevice* getFileDeviceForMapFile(const sead::SafeString& path) const;
bool getFileDeviceForMap(sead::FileDevice** p_file_device, ksys::res::Handle** p_handle,
const sead::SafeString& path);
sead::FileDevice* getFileDeviceForStaticCompound(const sead::SafeString& path) const;
sead::FileDevice* getFileDeviceForTeraMesh(const sead::SafeString& path) const;
sead::FileDevice* getFileDeviceForNavMesh(const sead::SafeString& path) const;
sead::FileDevice* getFileDeviceForTerrain(const sead::SafeString& path) const;
sead::FileDevice* getFileDeviceForGame(const sead::SafeString& path) const;
sead::FileDevice* getFileDeviceForUI(const sead::SafeString& path) const;
bool isAocFile(const sead::SafeString& path, const sead::SafeString& dir,
const sead::SafeString& dir_aoc_field,
const sead::SafeString& dir_main_field_dungeon,
const sead::SafeString& dir_cdungeon) const;
static bool isAocDungeon(const sead::SafeString& map_name);
static bool isAocMap(const sead::SafeString& map_type, const sead::SafeString& map_name);
static bool isAocField(const sead::SafeString& map_type);
sead::FileDevice* getFileDeviceForDungeonPack(const sead::SafeString& path) const;
void registerAocPack(ksys::res::Handle* pack);
bool aocPackHasFile(const sead::SafeString& path) const;
bool changeMoviePath(sead::BufferedSafeString & path) const;
void parseAocMainFieldStaticInfo(const al::ByamlIter& iter);
// TODO: figure out what this does
bool getSomePos(sead::Vector3f* translate, u32* p_mask, u32* mask);
bool parseVersion();
void setGameDataFlags() const;
private:
SEAD_ENUM(GameDataFlag, AoCVerAtLastPlay ,LatestAoCVerPlayed ,HasAoCVer1 ,HasAoCVer2 ,HasAoCVer3)
SEAD_ENUM(VersionError, TooNew, TooOld)
struct DLCPosition {
sead::Vector3f translate = sead::Vector3f::zero;
@@ -47,12 +88,19 @@ private:
KSYS_CHECK_SIZE_NX150(DLCPosition, 0x2c);
struct VersionFile {
bool readVersion();
ksys::res::Handle file_handle;
sead::FixedSafeString<16> string;
};
KSYS_CHECK_SIZE_NX150(VersionFile, 0x78);
void loadVersionFile();
void checkVersion();
void resetFlags();
void initGameData();
void reinitFlags();
void onGdtReinit(ksys::gdt::Manager::ReinitEvent* event);
sead::FileDevice* mFileDevice{};
@@ -61,10 +109,10 @@ private:
sead::StorageFor<VersionFile, true> mVersionFile{sead::ZeroInitializeTag{}};
u32 mVersion{};
ksys::res::Handle mAocMapMainFieldPack;
ksys::res::FileDevicePrefix mFileDevicePrefix;
ksys::res::Handle* mAocArchive{};
ksys::res::FileDevicePrefix mFileDevicePrefix2;
ksys::res::Handle mAocMainFieldPack;
ksys::res::FileDevicePrefix mAocMainFieldPackPrefix;
ksys::res::Handle* mAocPack{};
ksys::res::FileDevicePrefix mAocPackPrefix;
sead::SafeArray<DLCPosition, 20> mDLCPositions;
@@ -75,7 +123,7 @@ private:
ksys::gdt::FlagHandle mFlagHasAocVer3{};
ksys::gdt::Manager::ReinitSignal::Slot mGdtReinitSlot;
sead::TypedBitFlag<Flag> mFlags{};
sead::BitFlag8 mVersionFlags{};
#ifdef NNSDK
u8* mAocFsCache{};
#endif