Ensure directories are created late in extract

This commit is contained in:
Jack Andersen 2017-01-16 15:23:19 -10:00
parent d66d9a24f4
commit 56b24c39f0
50 changed files with 402 additions and 177 deletions

View File

@ -67,20 +67,26 @@ namespace DataSpec
{
extern hecl::Database::DataSpecEntry SpecEntMP1;
extern hecl::Database::DataSpecEntry SpecEntMP1PC;
extern hecl::Database::DataSpecEntry SpecEntMP1ORIG;
extern hecl::Database::DataSpecEntry SpecEntMP2;
extern hecl::Database::DataSpecEntry SpecEntMP2PC;
extern hecl::Database::DataSpecEntry SpecEntMP2ORIG;
extern hecl::Database::DataSpecEntry SpecEntMP3;
extern hecl::Database::DataSpecEntry SpecEntMP3PC;
extern hecl::Database::DataSpecEntry SpecEntMP3ORIG;
}")
set(HECL_DATASPEC_PUSHES
" /* RetroCommon */
hecl::Database::DATA_SPEC_REGISTRY.reserve(6);
hecl::Database::DATA_SPEC_REGISTRY.reserve(9);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1PC);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP1ORIG);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2PC);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP2ORIG);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3PC);")
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3PC);
hecl::Database::DATA_SPEC_REGISTRY.push_back(&DataSpec::SpecEntMP3ORIG);")
add_subdirectory(hecl)
if(NOT TARGET atdna)

View File

@ -84,6 +84,7 @@ void ReadMaterialSetToBlender_1_2(hecl::BlenderConnection::PyOutStream& os,
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
if (!txtrPath.isNone())
{
txtrPath.makeDirChain(false);
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath);
}

View File

@ -119,20 +119,16 @@ hecl::ProjectPath UniqueResult::uniquePath(const hecl::ProjectPath& pakPath) con
levelDir.assign(pakPath, *m_levelName);
else
levelDir = pakPath;
levelDir.makeDir();
if (m_type == Type::Area)
{
hecl::ProjectPath areaDir(levelDir, *m_areaName);
areaDir.makeDir();
return areaDir;
}
else if (m_type == Type::Layer)
{
hecl::ProjectPath areaDir(levelDir, *m_areaName);
areaDir.makeDir();
hecl::ProjectPath layerDir(areaDir, *m_layerName);
layerDir.makeDir();
return layerDir;
}
@ -228,6 +224,7 @@ void PAKRouter<BRIDGETYPE>::build(std::vector<BRIDGETYPE>& bridges, std::functio
/* Write catalog */
intptr_t curBridgeIdx = reinterpret_cast<intptr_t>(m_curBridgeIdx.get());
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].first;
pakPath.makeDirChain(true);
hecl::SystemString catalogPath = hecl::ProjectPath(pakPath, "!catalog.yaml").getAbsolutePath();
athena::io::FileWriter writer(catalogPath);
catalogWriter.finish(&writer);
@ -244,8 +241,6 @@ void PAKRouter<BRIDGETYPE>::enterPAKBridge(const BRIDGETYPE& pakBridge)
{
if (&bridge == &pakBridge)
{
pit->first.makeDir();
pit->second.makeDir();
m_pak.reset(&pakBridge.getPAK());
m_node.reset(&pakBridge.getNode());
m_curBridgeIdx.reset(reinterpret_cast<void*>(bridgeIdx));
@ -297,7 +292,6 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry,
if (singleSearch)
{
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].first;
pakPath.makeDir();
#if HECL_UCS2
hecl::SystemString entName = hecl::UTF8ToWide(getBestEntryName(*entry));
#else
@ -322,7 +316,6 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry,
{
const BRIDGETYPE& bridge = m_bridges->at(uniqueSearch->second.first);
const hecl::ProjectPath& pakPath = m_bridgePaths[uniqueSearch->second.first].first;
pakPath.makeDir();
#if HECL_UCS2
hecl::SystemString entName = hecl::UTF8ToWide(getBestEntryName(*entry));
#else
@ -369,7 +362,6 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getWorking(const EntryType* entry,
auxInfo = chWork.getAuxInfo();
}
hecl::ProjectPath sharedPath(m_sharedWorking, entName);
m_sharedWorking.makeDir();
return sharedPath.ensureAuxInfo(auxInfo);
}
@ -417,7 +409,6 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const EntryType* entry) const
if (singleSearch)
{
const hecl::ProjectPath& pakPath = m_bridgePaths[curBridgeIdx].second;
pakPath.makeDir();
return hecl::ProjectPath(pakPath, getBestEntryName(*entry));
}
}
@ -426,7 +417,6 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const EntryType* entry) const
{
const BRIDGETYPE& bridge = m_bridges->at(uniqueSearch->second.first);
const hecl::ProjectPath& pakPath = m_bridgePaths[uniqueSearch->second.first].second;
pakPath.makeDir();
if (bridge.getPAK().m_noShare)
{
return hecl::ProjectPath(pakPath, getBestEntryName(*entry));
@ -440,7 +430,6 @@ hecl::ProjectPath PAKRouter<BRIDGETYPE>::getCooked(const EntryType* entry) const
auto sharedSearch = m_sharedEntries.find(entry->id);
if (sharedSearch != m_sharedEntries.end())
{
m_sharedCooked.makeDir();
return hecl::ProjectPath(m_sharedCooked, getBestEntryName(*entry));
}
LogDNACommon.report(logvisor::Fatal, "Unable to find entry %s", entry->id.toString().c_str());
@ -551,14 +540,16 @@ bool PAKRouter<BRIDGETYPE>::extractResources(const BRIDGETYPE& pakBridge, bool f
const nod::Node* node = m_node.get();
hecl::ProjectPath working = getWorking(item, extractor);
working.makeDirChain(false);
hecl::ResourceLock resLk(working);
if (!resLk)
continue;
/* Extract first, so they start out invalid */
hecl::ProjectPath cooked = getCooked(item);
/* Extract to unmodified directory */
hecl::ProjectPath cooked = working.getCookedPath(m_dataSpec.getUnmodifiedSpec());
if (force || cooked.isNone())
{
cooked.makeDirChain(false);
PAKEntryReadStream s = item->beginReadStream(*node);
FILE* fout = hecl::Fopen(cooked.getAbsolutePath().c_str(), _S("wb"));
fwrite(s.data(), 1, s.length(), fout);

View File

@ -464,6 +464,7 @@ bool FRME::Extract(const SpecBase &dataSpec,
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
if (txtrPath.isNone())
{
txtrPath.makeDirChain(false);
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath);
}

View File

@ -43,7 +43,7 @@ void SCLY::exportToLayerDirectories(const PAK::Entry& entry, PAKRouter<PAKBridge
bool active;
hecl::ProjectPath layerPath = pakRouter.getAreaLayerWorking(entry.id, i, active);
if (layerPath.isNone())
layerPath.makeDir();
layerPath.makeDirChain(true);
if (active)
{

View File

@ -126,6 +126,7 @@ void Material::SectionPASS::constructNode(hecl::BlenderConnection::PyOutStream&
hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry);
if (txtrPath.isNone())
{
txtrPath.makeDirChain(false);
PAKEntryReadStream rs = texEntry->beginReadStream(*node);
TXTR::Extract(rs, txtrPath);
}

View File

@ -106,7 +106,7 @@ void SpecBase::doExtract(const ExtractPassInfo& info, FProgress progress)
{
/* Extract root files for repacking later */
hecl::ProjectPath outDir(m_project.getProjectWorkingPath(), _S("out"));
outDir.makeDir();
outDir.makeDirChain(true);
nod::ExtractionContext ctx = {true, info.force, nullptr};
if (!m_standalone)
@ -218,7 +218,7 @@ const hecl::Database::DataSpecEntry* SpecBase::overrideDataSpec(const hecl::Proj
{
return oldEntry;
}
return getOriginalSpec();
return &getOriginalSpec();
}
void SpecBase::doCook(const hecl::ProjectPath& path, const hecl::ProjectPath& cookedPath,
@ -264,6 +264,12 @@ void SpecBase::doCook(const hecl::ProjectPath& path, const hecl::ProjectPath& co
cookWorld(cookedPath, path, ds, fast, btok, progress);
break;
}
case hecl::BlenderConnection::BlendType::Frame:
{
hecl::BlenderConnection::DataStream ds = conn.beginData();
cookGuiFrame(cookedPath, path, ds, btok, progress);
break;
}
default: break;
}
}

View File

@ -50,7 +50,10 @@ struct SpecBase : hecl::Database::IDataSpec
hecl::BlenderToken& btok) const=0;
/* Even if PC spec is being cooked, this will return the vanilla GCN spec */
virtual const hecl::Database::DataSpecEntry* getOriginalSpec() const=0;
virtual const hecl::Database::DataSpecEntry& getOriginalSpec() const=0;
/* This will return a pseudo-spec for unmodified resources */
virtual const hecl::Database::DataSpecEntry& getUnmodifiedSpec() const=0;
/* Basic path check (game directory matching) */
virtual bool checkPathPrefix(const hecl::ProjectPath& path) const=0;
@ -77,6 +80,9 @@ struct SpecBase : hecl::Database::IDataSpec
virtual void cookWorld(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
BlendStream& ds, bool fast, hecl::BlenderToken& btok,
FCookProgress progress)=0;
virtual void cookGuiFrame(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
BlendStream& ds, hecl::BlenderToken& btok,
FCookProgress progress)=0;
virtual void cookYAML(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
athena::io::IStreamReader& fin, FCookProgress progress)=0;
virtual void cookAudioGroup(const hecl::ProjectPath& out, const hecl::ProjectPath& in,

View File

@ -36,6 +36,7 @@ namespace DataSpec
static logvisor::Module Log("urde::SpecMP1");
extern hecl::Database::DataSpecEntry SpecEntMP1;
extern hecl::Database::DataSpecEntry SpecEntMP1PC;
extern hecl::Database::DataSpecEntry SpecEntMP1ORIG;
struct SpecMP1 : SpecBase
{
@ -239,9 +240,6 @@ struct SpecMP1 : SpecBase
nod::ExtractionContext ctx = {true, force, nullptr};
m_workPath.makeDir();
const hecl::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP1);
cookPath.makeDir();
m_cookPath.makeDir();
progress(_S("Indexing PAKs"), _S(""), 2, 0.0);
m_pakRouter.build(m_paks, [&progress](float factor)
@ -314,9 +312,14 @@ struct SpecMP1 : SpecBase
return true;
}
const hecl::Database::DataSpecEntry* getOriginalSpec() const
const hecl::Database::DataSpecEntry& getOriginalSpec() const
{
return &SpecEntMP1;
return SpecEntMP1;
}
const hecl::Database::DataSpecEntry& getUnmodifiedSpec() const
{
return SpecEntMP1ORIG;
}
hecl::ProjectPath getWorking(class UniqueID32& id)
@ -605,6 +608,13 @@ struct SpecMP1 : SpecBase
DNAMP1::MLVL::Cook(out, in, world, btok);
}
void cookGuiFrame(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
BlendStream& ds, hecl::BlenderToken& btok,
FCookProgress progress)
{
ds.compileGuiFrame(out.getAbsolutePathUTF8(), 0);
}
void cookYAML(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
athena::io::IStreamReader& fin, FCookProgress progress)
{
@ -803,6 +813,13 @@ hecl::Database::DataSpecEntry SpecEntMP1PC =
}
};
hecl::Database::DataSpecEntry SpecEntMP1ORIG =
{
_S("MP1-ORIG"),
_S("Data specification for unmodified Metroid Prime resources"),
{}
};
}

View File

@ -17,6 +17,7 @@ namespace DataSpec
static logvisor::Module Log("urde::SpecMP2");
extern hecl::Database::DataSpecEntry SpecEntMP2;
extern hecl::Database::DataSpecEntry SpecEntMP2ORIG;
struct SpecMP2 : SpecBase
{
@ -212,9 +213,6 @@ struct SpecMP2 : SpecBase
nod::ExtractionContext ctx = {true, force, nullptr};
m_workPath.makeDir();
const hecl::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP2);
cookPath.makeDir();
m_cookPath.makeDir();
progress(_S("Indexing PAKs"), _S(""), 2, 0.0);
m_pakRouter.build(m_paks, [&progress](float factor)
@ -274,9 +272,14 @@ struct SpecMP2 : SpecBase
return true;
}
const hecl::Database::DataSpecEntry* getOriginalSpec() const
const hecl::Database::DataSpecEntry& getOriginalSpec() const
{
return &SpecEntMP2;
return SpecEntMP2;
}
const hecl::Database::DataSpecEntry& getUnmodifiedSpec() const
{
return SpecEntMP2ORIG;
}
hecl::ProjectPath getWorking(class UniqueID32& id)
@ -334,6 +337,12 @@ struct SpecMP2 : SpecBase
{
}
void cookGuiFrame(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
BlendStream& ds, hecl::BlenderToken& btok,
FCookProgress progress)
{
}
void cookYAML(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
athena::io::IStreamReader& fin, FCookProgress progress)
{
@ -379,4 +388,11 @@ hecl::Database::DataSpecEntry SpecEntMP2PC =
}
};
hecl::Database::DataSpecEntry SpecEntMP2ORIG =
{
_S("MP2-ORIG"),
_S("Data specification for unmodified Metroid Prime 2 resources"),
{}
};
}

View File

@ -17,6 +17,7 @@ namespace DataSpec
static logvisor::Module Log("urde::SpecMP3");
extern hecl::Database::DataSpecEntry SpecEntMP3;
extern hecl::Database::DataSpecEntry SpecEntMP3ORIG;
struct SpecMP3 : SpecBase
{
@ -340,9 +341,6 @@ struct SpecMP3 : SpecBase
if (doMP3)
{
m_workPath.makeDir();
const hecl::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP3);
cookPath.makeDir();
m_cookPath.makeDir();
progress(_S("Indexing PAKs"), _S(""), compIdx, 0.0);
m_pakRouter.build(m_paks, [&progress, &compIdx](float factor)
@ -404,9 +402,6 @@ struct SpecMP3 : SpecBase
if (doMPTFE)
{
m_feWorkPath.makeDir();
const hecl::ProjectPath& cookPath = m_project.getProjectCookedPath(SpecEntMP3);
cookPath.makeDir();
m_feCookPath.makeDir();
progress(_S("Indexing PAKs"), _S(""), compIdx, 0.0);
m_fePakRouter.build(m_fePaks, [&progress, &compIdx](float factor)
@ -465,9 +460,14 @@ struct SpecMP3 : SpecBase
return true;
}
const hecl::Database::DataSpecEntry* getOriginalSpec() const
const hecl::Database::DataSpecEntry& getOriginalSpec() const
{
return &SpecEntMP3;
return SpecEntMP3;
}
const hecl::Database::DataSpecEntry& getUnmodifiedSpec() const
{
return SpecEntMP3ORIG;
}
hecl::ProjectPath getWorking(class UniqueID64& id)
@ -520,6 +520,12 @@ struct SpecMP3 : SpecBase
{
}
void cookGuiFrame(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
BlendStream& ds, hecl::BlenderToken& btok,
FCookProgress progress)
{
}
void cookYAML(const hecl::ProjectPath& out, const hecl::ProjectPath& in,
athena::io::IStreamReader& fin, FCookProgress progress)
{
@ -561,4 +567,11 @@ hecl::Database::DataSpecEntry SpecEntMP3PC =
}
};
hecl::Database::DataSpecEntry SpecEntMP3ORIG =
{
_S("MP3-ORIG"),
_S("Data specification for unmodified Metroid Prime 3 resources"),
{}
};
}

View File

@ -17,6 +17,7 @@
#include "World/CWorldTransManager.hpp"
#include "Graphics/Shaders/CColoredQuadFilter.hpp"
#include "Graphics/Shaders/CTexturedQuadFilter.hpp"
#include "Audio/CStreamAudioManager.hpp"
#include <cstdio>
using YAMLNode = athena::io::YAMLNode;
@ -69,8 +70,11 @@ void ViewManager::BuildTestPART(urde::IObjectStore& objStore)
m_videoVoice->start();
#endif
m_newAudioPlayer.emplace(*m_voiceEngine, "Audio/frontend_1.rsf", 416480, 1973664);
m_newAudioPlayer->StartMixing();
//m_newAudioPlayer.emplace(*m_voiceEngine, "Audio/frontend_1.rsf", 416480, 1973664);
//m_newAudioPlayer->StartMixing();
// Test DSP streaming
CStreamAudioManager::Start(false, "Audio/rui_samusL.dsp|Audio/rui_samusR.dsp", 0x7f, true, 1.f, 1.f);
//m_rootView->accessContentViews().clear();
m_rootView->accessContentViews().push_back(m_particleView.get());

View File

@ -20,7 +20,7 @@ CIOWin::EMessageReturn CAudioStateWin::OnMessage(const CArchitectureMessage& msg
else if (msgType == EArchMsgType::QuitGameplay)
{
if (g_GameState->GetWorldTransitionManager()->GetTransType() == CWorldTransManager::ETransType::Disabled ||
g_Main->GetFlowState() != IMain::EFlowState::Zero)
g_Main->GetFlowState() != EFlowState::Zero)
{
CSfxManager::SetChannel(CSfxManager::ESfxChannels::Zero);
CSfxManager::KillAll(CSfxManager::ESfxChannels::One);

View File

@ -269,6 +269,8 @@ struct SDSPStream
continue;
stream.x0_active = true;
stream.x4_ownerId = ++s_HandleCounter2;
if (stream.x4_ownerId == -1)
stream.x4_ownerId = ++s_HandleCounter2;
stream.x8_stereoLeft = nullptr;
stream.xc_stereoRight = nullptr;
streamOut = &stream;
@ -294,8 +296,9 @@ struct SDSPStream
if (!x0_active || xe8_silent)
return;
float coefs[8] = {};
coefs[int(boo::AudioChannel::FrontLeft)] = (0x7f - x4d_pan) / 127.f;
coefs[int(boo::AudioChannel::FrontRight)] = x4d_pan / 127.f;
float fvol = x4c_vol / 127.f;
coefs[int(boo::AudioChannel::FrontLeft)] = ((0x7f - x4d_pan) / 127.f) * fvol;
coefs[int(boo::AudioChannel::FrontRight)] = (x4d_pan / 127.f) * fvol;
m_booVoice->setMonoChannelLevels(nullptr, coefs, true);
}
@ -406,6 +409,7 @@ struct SDSPStream
xd8_ringBytes = 0x11DC0;
xdc_ringSamples = 0x1f410;
memset(xd4_ringBuffer.get(), 0, 0x11DC0);
m_booVoice->resetSampleRate(info.x4_sampleRate);
m_booVoice->start();
}
@ -461,7 +465,7 @@ public:
x70_24_unclaimed = true;
}
static u32 FindUnlaimedStreamIdx()
static u32 FindUnclaimedStreamIdx()
{
for (int i=0 ; i<4 ; ++i)
{
@ -474,7 +478,7 @@ public:
static bool FindUnclaimedStereoPair(u32& left, u32& right)
{
u32 idx = FindUnlaimedStreamIdx();
u32 idx = FindUnclaimedStreamIdx();
for (u32 i=0 ; i<4 ; ++i)
{
@ -646,13 +650,16 @@ public:
(companionStream.x71_companionRight != selfIdx &&
companionStream.x72_companionLeft != selfIdx))
{
/* No consistent companion available */
*this = CDSPStreamManager();
return;
}
/* Companion is pending; its completion will continue */
if (companionStream.x70_26_headerReadState == 1)
return;
/* Use whichever stream is the left channel */
if (companionStream.x71_companionRight != -1)
AllocateStream(companion);
else
@ -674,10 +681,10 @@ public:
if (!file)
return false;
stream.x70_26_headerReadState = 1;
stream.m_dvdReq = file.AsyncRead(&stream.x0_header, sizeof(dspadpcm_header),
std::bind(&CDSPStreamManager::HeaderReadComplete, &stream,
std::placeholders::_1));
stream.x70_26_headerReadState = 1;
return true;
}
@ -694,7 +701,7 @@ public:
if (pipePos == std::string::npos)
{
/* Mono stream */
u32 idx = FindUnlaimedStreamIdx();
u32 idx = FindUnclaimedStreamIdx();
if (idx == -1)
return -1;

View File

@ -36,21 +36,19 @@ enum class EArchMsgType
struct IArchMsgParm
{
virtual ~IArchMsgParm() {}
virtual ~IArchMsgParm() = default;
};
struct CArchMsgParmInt32 : IArchMsgParm
{
u32 x4_parm;
CArchMsgParmInt32(u32 parm) : x4_parm(parm) {}
virtual ~CArchMsgParmInt32() {}
};
struct CArchMsgParmVoidPtr : IArchMsgParm
{
void* x4_parm1;
CArchMsgParmVoidPtr(void* parm1) : x4_parm1(parm1) {}
virtual ~CArchMsgParmVoidPtr() {}
};
struct CArchMsgParmInt32Int32VoidPtr : IArchMsgParm
@ -58,29 +56,33 @@ struct CArchMsgParmInt32Int32VoidPtr : IArchMsgParm
u32 x4_parm1;
u32 x8_parm2;
void* xc_parm3;
CArchMsgParmInt32Int32VoidPtr(u32 parm1, u32 parm2, void* parm3) : x4_parm1(parm1), x8_parm2(parm2), xc_parm3(parm3)
CArchMsgParmInt32Int32VoidPtr(u32 parm1, u32 parm2, void* parm3)
: x4_parm1(parm1), x8_parm2(parm2), xc_parm3(parm3) {}
};
struct CArchMsgParmInt32Int32IOWin : IArchMsgParm
{
}
virtual ~CArchMsgParmInt32Int32VoidPtr() {}
u32 x4_parm1;
u32 x8_parm2;
std::shared_ptr<CIOWin> xc_parm3;
CArchMsgParmInt32Int32IOWin(u32 parm1, u32 parm2, std::shared_ptr<CIOWin>&& parm3)
: x4_parm1(parm1), x8_parm2(parm2), xc_parm3(std::move(parm3)) {}
};
struct CArchMsgParmNull : IArchMsgParm
{
virtual ~CArchMsgParmNull() {}
};
struct CArchMsgParmReal32 : IArchMsgParm
{
float x4_parm;
CArchMsgParmReal32(float parm) : x4_parm(parm) {}
virtual ~CArchMsgParmReal32() {}
};
struct CArchMsgParmUserInput : IArchMsgParm
{
CFinalInput x4_parm;
CArchMsgParmUserInput(const CFinalInput& parm) : x4_parm(parm) {}
virtual ~CArchMsgParmUserInput() {}
};
struct CArchMsgParmControllerStatus : IArchMsgParm
@ -88,8 +90,6 @@ struct CArchMsgParmControllerStatus : IArchMsgParm
u16 x4_parm1;
bool x6_parm2;
CArchMsgParmControllerStatus(u16 a, bool b) : x4_parm1(a), x6_parm2(b) {}
virtual ~CArchMsgParmControllerStatus() {}
};
class CArchitectureMessage
@ -99,10 +99,9 @@ class CArchitectureMessage
std::shared_ptr<IArchMsgParm> x8_parm;
public:
CArchitectureMessage(EArchMsgTarget target, EArchMsgType type, IArchMsgParm* parm)
: x0_target(target), x4_type(type), x8_parm(parm)
{
}
CArchitectureMessage(EArchMsgTarget target, EArchMsgType type,
std::shared_ptr<IArchMsgParm>&& parm)
: x0_target(target), x4_type(type), x8_parm(std::move(parm)) {}
EArchMsgTarget GetTarget() const { return x0_target; }
EArchMsgType GetType() const { return x4_type; }
@ -118,11 +117,13 @@ class MakeMsg
public:
static CArchitectureMessage CreateQuitGameplay(EArchMsgTarget target)
{
return CArchitectureMessage(target, EArchMsgType::QuitGameplay, new CArchMsgParmNull());
return CArchitectureMessage(target, EArchMsgType::QuitGameplay,
std::make_shared<CArchMsgParmNull>());
}
static CArchitectureMessage CreateControllerStatus(EArchMsgTarget target, u16 a, bool b)
{
return CArchitectureMessage(target, EArchMsgType::ControllerStatus, new CArchMsgParmControllerStatus(a, b));
return CArchitectureMessage(target, EArchMsgType::ControllerStatus,
std::make_shared<CArchMsgParmControllerStatus>(a, b));
}
static const CArchMsgParmInt32& GetParmNewGameflowState(const CArchitectureMessage& msg)
{
@ -134,7 +135,8 @@ public:
}
static CArchitectureMessage CreateUserInput(EArchMsgTarget target, const CFinalInput& input)
{
return CArchitectureMessage(target, EArchMsgType::UserInput, new CArchMsgParmUserInput(input));
return CArchitectureMessage(target, EArchMsgType::UserInput,
std::make_shared<CArchMsgParmUserInput>(input));
}
static const CArchMsgParmReal32& GetParmTimerTick(const CArchitectureMessage& msg)
{
@ -142,20 +144,22 @@ public:
}
static CArchitectureMessage CreateTimerTick(EArchMsgTarget target, float val)
{
return CArchitectureMessage(target, EArchMsgType::TimerTick, new CArchMsgParmReal32(val));
return CArchitectureMessage(target, EArchMsgType::TimerTick,
std::make_shared<CArchMsgParmReal32>(val));
}
static const CArchMsgParmInt32Int32VoidPtr& GetParmChangeIOWinPriority(const CArchitectureMessage& msg)
{
return *msg.GetParm<CArchMsgParmInt32Int32VoidPtr>();
}
static const CArchMsgParmInt32Int32VoidPtr& GetParmCreateIOWin(const CArchitectureMessage& msg)
static const CArchMsgParmInt32Int32IOWin& GetParmCreateIOWin(const CArchitectureMessage& msg)
{
return *msg.GetParm<CArchMsgParmInt32Int32VoidPtr>();
return *msg.GetParm<CArchMsgParmInt32Int32IOWin>();
}
static CArchitectureMessage CreateCreateIOWin(EArchMsgTarget target, int pmin, int pmax, CIOWin* iowin)
static CArchitectureMessage CreateCreateIOWin(EArchMsgTarget target, int pmin, int pmax,
std::shared_ptr<CIOWin>&& iowin)
{
return CArchitectureMessage(target, EArchMsgType::CreateIOWin,
new CArchMsgParmInt32Int32VoidPtr(pmin, pmax, iowin));
std::make_shared<CArchMsgParmInt32Int32IOWin>(pmin, pmax, std::move(iowin)));
}
static const CArchMsgParmVoidPtr& GetParmDeleteIOWin(const CArchitectureMessage& msg)
{
@ -163,12 +167,14 @@ public:
}
static CArchitectureMessage CreateFrameBegin(EArchMsgTarget target, const int& a)
{
return CArchitectureMessage(target, EArchMsgType::FrameBegin, new CArchMsgParmInt32(a));
return CArchitectureMessage(target, EArchMsgType::FrameBegin,
std::make_shared<CArchMsgParmInt32>(a));
}
/* URDE Messages */
static CArchitectureMessage CreateApplicationExit(EArchMsgTarget target)
{
return CArchitectureMessage(target, EArchMsgType::ApplicationExit, new CArchMsgParmNull());
return CArchitectureMessage(target, EArchMsgType::ApplicationExit,
std::make_shared<CArchMsgParmNull>());
}
};
}

View File

@ -72,6 +72,7 @@ bool CDvdFile::m_WorkerRun = false;
std::vector<std::shared_ptr<IDvdRequest>> CDvdFile::m_RequestQueue;
void CDvdFile::WorkerProc()
{
logvisor::RegisterThreadName("CDvdFile Thread");
while (m_WorkerRun)
{
std::unique_lock<std::mutex> lk(CDvdFile::m_WorkerMutex);
@ -87,6 +88,7 @@ void CDvdFile::WorkerProc()
concreteReq.DoRequest();
}
waitlk.unlock();
swapQueue.clear();
lk.lock();
}
if (!m_WorkerRun)

View File

@ -376,7 +376,7 @@ void CGameOptions::EnsureSettings()
SetSfxVolume(x58_sfxVol, true);
SetMusicVolume(x5c_musicVol, true);
SetSurroundMode(int(x44_soundMode), true);
SetHUDAlpha(x60_hudAlpha);
SetHelmetAlpha(x64_helmetAlpha);
SetHUDLag(x68_24_hudLag);
SetInvertYAxis(x68_25_invertY);
SetIsRumbleEnabled(x68_26_rumble);

View File

@ -19,9 +19,8 @@ bool CIOWinManager::OnIOWinMessage(const CArchitectureMessage& msg)
}
case EArchMsgType::CreateIOWin:
{
const CArchMsgParmInt32Int32VoidPtr& parm = MakeMsg::GetParmCreateIOWin(msg);
std::shared_ptr<CIOWin> iow(static_cast<CIOWin*>(parm.xc_parm3));
AddIOWin(iow, parm.x4_parm1, parm.x8_parm2);
const CArchMsgParmInt32Int32IOWin& parm = MakeMsg::GetParmCreateIOWin(msg);
AddIOWin(parm.xc_parm3, parm.x4_parm1, parm.x8_parm2);
return false;
}
case EArchMsgType::ChangeIOWinPriority:

View File

@ -9,10 +9,10 @@ namespace urde
enum class EClientFlowStates
{
Unspecified = -1,
FrontEnd = 7,
StateLoad = 8,
GameLoad = 13,
MoviePlay = 14
PreFrontEnd = 7,
FrontEnd = 8,
Game = 14,
GameExit = 15
};
class CMainFlowBase : public CIOWin

View File

@ -41,7 +41,7 @@ CIOWin::EMessageReturn CSplashScreen::OnMessage(const CArchitectureMessage& msg,
{
if (x14_which != ESplashScreen::Retro)
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 9999, 9999,
new CSplashScreen(ESplashScreen(int(x14_which) + 1))));
std::make_shared<CSplashScreen>(ESplashScreen(int(x14_which) + 1))));
return EMessageReturn::RemoveIOWinAndExit;
}
break;

View File

@ -18,9 +18,6 @@ enum class EGameplayResult
Playing
};
class IMain
{
public:
enum class EFlowState
{
Zero,
@ -32,6 +29,10 @@ public:
Six,
};
class IMain
{
public:
virtual ~IMain() = default;
virtual void RegisterResourceTweaks() {}
virtual void ResetGameState()=0;
virtual void StreamNewGameState(CBitStreamReader&, u32 idx) {}
@ -51,6 +52,7 @@ public:
virtual void ShutdownSubsystems()=0;
virtual EGameplayResult GetGameplayResult() const=0;
virtual void SetGameplayResult(EGameplayResult wl)=0;
virtual void SetFlowState(EFlowState s)=0;
virtual EFlowState GetFlowState() const=0;
};
}

View File

@ -18,10 +18,5 @@ CBeetle::CBeetle(TUniqueId uid, const std::string& name, const CEntityInfo& info
}
void CBeetle::Accept(IVisitor& visitor)
{
visitor.Visit(this);
}
}
}

View File

@ -23,8 +23,6 @@ public:
const CPatternedInfo&, CPatterned::EFlavorType,EEntranceType, const CDamageInfo &, const CDamageVulnerability&,
const zeus::CVector3f&, float, float, float, const CDamageVulnerability&, const CActorParameters&,
const rstl::optional_object<CStaticRes>);
void Accept(IVisitor& visitor);
};
}
}

25
Runtime/MP1/CCredits.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "CCredits.hpp"
namespace urde
{
namespace MP1
{
CCredits::CCredits()
: CIOWin("Credits")
{
}
CIOWin::EMessageReturn CCredits::OnMessage(const CArchitectureMessage& msg, CArchitectureQueue& queue)
{
return EMessageReturn::Normal;
}
void CCredits::Draw() const
{
}
}
}

23
Runtime/MP1/CCredits.hpp Normal file
View File

@ -0,0 +1,23 @@
#ifndef __URDE_MP1_CCREDITS_HPP__
#define __URDE_MP1_CCREDITS_HPP__
#include "CIOWin.hpp"
namespace urde
{
namespace MP1
{
class CCredits : public CIOWin
{
public:
CCredits();
EMessageReturn OnMessage(const CArchitectureMessage&, CArchitectureQueue&);
bool GetIsContinueDraw() const { return false; }
void Draw() const;
};
}
}
#endif // __URDE_MP1_CCREDITS_HPP__

View File

@ -1715,7 +1715,7 @@ void CFrontEndUI::SOptionsFrontEndFrame::Draw() const
}
}
CFrontEndUI::CFrontEndUI(CArchitectureQueue& queue)
CFrontEndUI::CFrontEndUI()
: CIOWin("FrontEndUI")
{
x18_rndA = std::min(rand() * 3 / RAND_MAX, 2);
@ -1735,7 +1735,8 @@ CFrontEndUI::CFrontEndUI(CArchitectureQueue& queue)
void CFrontEndUI::StartSlideShow(CArchitectureQueue& queue)
{
xf4_curAudio->StopMixing();
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11, new CSlideShow()));
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11,
std::make_shared<CSlideShow>()));
}
std::string CFrontEndUI::GetAttractMovieFileName(int idx)

View File

@ -426,7 +426,7 @@ private:
bool PumpLoad();
public:
CFrontEndUI(CArchitectureQueue& queue);
CFrontEndUI();
void StartSlideShow(CArchitectureQueue& queue);
std::string GetAttractMovieFileName(int idx);
std::string GetNextAttractMovieFileName();

View File

@ -33,8 +33,8 @@ CMFGameLoader::CMFGameLoader() : CMFGameLoaderBase("CMFGameLoader")
{
switch (g_Main->GetFlowState())
{
case CMain::EFlowState::Five:
case CMain::EFlowState::Six:
case EFlowState::Five:
case EFlowState::Six:
{
ResId mlvlId = g_GameState->CurrentWorldAssetId();
// g_GameState->WorldTransitionManager()->

View File

@ -7,6 +7,12 @@
#include "CFrontEndUI.hpp"
#include "GameGlobalObjects.hpp"
#include "Character/CCharLayoutInfo.hpp"
#include "CSaveUI.hpp"
#include "CCredits.hpp"
#include "CPreFrontEnd.hpp"
#include "CStateSetterFlow.hpp"
#include "CNESEmulator.hpp"
#include "CQuitScreen.hpp"
namespace urde
{
@ -17,59 +23,88 @@ void CMainFlow::AdvanceGameState(CArchitectureQueue& queue)
{
switch (x14_gameState)
{
case EClientFlowStates::Unspecified:
case EClientFlowStates::Game:
CMainFlow::SetGameState(EClientFlowStates::GameExit, queue);
break;
case EClientFlowStates::PreFrontEnd:
CMainFlow::SetGameState(EClientFlowStates::FrontEnd, queue);
break;
case EClientFlowStates::FrontEnd:
CMainFlow::SetGameState(EClientFlowStates::GameLoad, queue);
CMainFlow::SetGameState(EClientFlowStates::Game, queue);
break;
case EClientFlowStates::GameLoad:
CMainFlow::SetGameState(EClientFlowStates::MoviePlay, queue);
break;
case EClientFlowStates::MoviePlay:
CMainFlow::SetGameState(EClientFlowStates::FrontEnd, queue);
case EClientFlowStates::GameExit:
{
MP1::CMain* main = static_cast<MP1::CMain*>(g_Main);
if (main->GetFlowState() != EFlowState::Zero &&
main->GetFlowState() != EFlowState::Six)
main->SetX30(true);
}
case EClientFlowStates::Unspecified:
CMainFlow::SetGameState(EClientFlowStates::PreFrontEnd, queue);
break;
}
}
void CMainFlow::SetGameState(EClientFlowStates state, CArchitectureQueue& queue)
{
x14_gameState = state;
MP1::CMain* main = static_cast<MP1::CMain*>(g_Main);
switch (state)
{
case EClientFlowStates::GameExit:
{
switch (main->GetFlowState())
{
case EFlowState::One:
case EFlowState::Two:
case EFlowState::Three:
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11,
std::make_shared<CCredits>()));
break;
case EFlowState::Four:
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11,
std::make_shared<CPlayMovie>(CPlayMovie::EWhichMovie::AfterCredits)));
break;
default: break;
}
break;
}
case EClientFlowStates::PreFrontEnd:
{
if (main->GetFlowState() == EFlowState::Zero)
return;
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11,
std::make_shared<CPreFrontEnd>()));
break;
}
case EClientFlowStates::FrontEnd:
{
if (g_Main->GetGameplayResult() == EGameplayResult::None)
std::shared_ptr<CIOWin> nextIOWin;
switch (main->GetFlowState())
{
g_Main->SetGameplayResult(EGameplayResult::Playing);
case EFlowState::Six:
nextIOWin = std::make_shared<CStateSetterFlow>();
break;
case EFlowState::One:
case EFlowState::Two:
case EFlowState::Three:
case EFlowState::Four:
case EFlowState::Five:
nextIOWin = std::make_shared<CFrontEndUI>();
break;
default: return;
}
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11, std::move(nextIOWin)));
break;
}
/* TODO: URDE handling
CResLoader& loader = g_ResFactory->GetLoader();
while (!loader.AreAllPaksLoaded())
loader.AsyncIdlePakLoading();
*/
g_Main->LoadAudio();
g_Main->RegisterResourceTweaks();
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11, new CFrontEndUI(queue)));
break;
}
case EClientFlowStates::GameLoad:
case EClientFlowStates::Game:
{
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 10, 1000, new CMFGameLoader()));
break;
}
case EClientFlowStates::MoviePlay:
{
switch (g_Main->GetGameplayResult())
{
case EGameplayResult::Win:
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11, new CPlayMovie(CPlayMovie::EWhichMovie::WinGame)));
break;
case EGameplayResult::Lose:
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11, new CPlayMovie(CPlayMovie::EWhichMovie::LoseGame)));
break;
default: break;
}
g_GameState->GameOptions().EnsureSettings();
main->SetFlowState(EFlowState::Five);
queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 10, 1000,
std::make_shared<CMFGameLoader>()));
break;
}
default: break;

View File

@ -11,6 +11,7 @@ set(MP1_SOURCES
CMFGame.hpp CMFGame.cpp
CPlayMovie.hpp CPlayMovie.cpp
CFrontEndUI.hpp CFrontEndUI.cpp
CPreFrontEnd.hpp CPreFrontEnd.cpp
CSlideShow.hpp CSlideShow.cpp
CNewIntroBoss.hpp CNewIntroBoss.cpp
CBeetle.hpp CBeetle.cpp
@ -20,6 +21,8 @@ set(MP1_SOURCES
CSaveUI.hpp CSaveUI.cpp
CMemoryCardDriver.hpp CMemoryCardDriver.cpp
CQuitScreen.hpp CQuitScreen.cpp
CCredits.hpp CCredits.cpp
CStateSetterFlow.hpp CStateSetterFlow.cpp
MP1.hpp MP1.cpp)
runtime_add_list(MP1 MP1_SOURCES)

View File

@ -16,10 +16,5 @@ CNewIntroBoss::CNewIntroBoss(TUniqueId uid, const std::string& name, const CEnti
{
}
void CNewIntroBoss::Accept(IVisitor& visitor)
{
visitor.Visit(this);
}
}
}

View File

@ -17,8 +17,6 @@ public:
const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo,
const CActorParameters& actParms, float, u32, const CDamageInfo& dInfo,
u32, u32, u32, u32);
void Accept(IVisitor& visitor);
};
}

View File

@ -16,7 +16,9 @@ public:
enum class EWhichMovie
{
WinGame,
LoseGame
LoseGame,
Two,
AfterCredits
};
private:
EWhichMovie x14_which;

View File

@ -0,0 +1,20 @@
#include "CPreFrontEnd.hpp"
namespace urde
{
namespace MP1
{
CPreFrontEnd::CPreFrontEnd()
: CIOWin("Pre front-end window")
{
}
CIOWin::EMessageReturn CPreFrontEnd::OnMessage(const CArchitectureMessage&, CArchitectureQueue&)
{
return EMessageReturn::Normal;
}
}
}

View File

@ -0,0 +1,21 @@
#ifndef __URDE_MP1_CPREFRONTEND_HPP__
#define __URDE_MP1_CPREFRONTEND_HPP__
#include "CIOWin.hpp"
namespace urde
{
namespace MP1
{
class CPreFrontEnd : public CIOWin
{
public:
CPreFrontEnd();
EMessageReturn OnMessage(const CArchitectureMessage&, CArchitectureQueue&);
};
}
}
#endif // __URDE_MP1_CPREFRONTEND_HPP__

View File

@ -14,10 +14,5 @@ CSpacePirate::CSpacePirate(TUniqueId uid, const std::string& name, const CEntity
{
}
void CSpacePirate::Accept(IVisitor& visitor)
{
visitor.Visit(this);
}
}
}

View File

@ -12,8 +12,6 @@ class CSpacePirate : public CPatterned
public:
CSpacePirate(TUniqueId, const std::string&, const CEntityInfo&, const zeus::CTransform&, CModelData&&,
const CActorParameters&, const CPatternedInfo&, CInputStream&, u32);
void Accept(IVisitor& visitor);
};
}
}

View File

@ -0,0 +1,20 @@
#include "CStateSetterFlow.hpp"
namespace urde
{
namespace MP1
{
CStateSetterFlow::CStateSetterFlow()
: CIOWin("")
{
}
CIOWin::EMessageReturn CStateSetterFlow::OnMessage(const CArchitectureMessage& msg, CArchitectureQueue& queue)
{
return EMessageReturn::Normal;
}
}
}

View File

@ -0,0 +1,21 @@
#ifndef __URDE_MP1_CSTATESETTERFLOW_HPP__
#define __URDE_MP1_CSTATESETTERFLOW_HPP__
#include "CIOWin.hpp"
namespace urde
{
namespace MP1
{
class CStateSetterFlow : public CIOWin
{
public:
CStateSetterFlow();
EMessageReturn OnMessage(const CArchitectureMessage&, CArchitectureQueue&);
};
}
}
#endif // __URDE_MP1_CSTATESETTERFLOW_HPP__

View File

@ -16,11 +16,6 @@ CWarWasp::CWarWasp(TUniqueId uid, const std::string& name, const CEntityInfo& in
}
void CWarWasp::Accept(IVisitor& visitor)
{
visitor.Visit(this);
}
}
}

View File

@ -15,8 +15,6 @@ public:
const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo,
CPatterned::EFlavorType flavor, CPatterned::EColliderType, const CDamageInfo& dInfo1, const CActorParameters&,
ResId weapon, const CDamageInfo& dInfo2, ResId particle, u32 w3);
void Accept(IVisitor& visitor);
};
}
}

View File

@ -265,6 +265,9 @@ public:
void SetCardBusy(bool v) { x160_31_cardBusy = v; }
EFlowState GetFlowState() const { return x12c_flowState; }
void SetFlowState(EFlowState s) { x12c_flowState = s; }
void SetX30(bool v) { x160_30_ = v; }
};
}

View File

@ -62,13 +62,6 @@ CENTITY_TYPES = (
('CScriptDockAreaChange', 'World/CScriptDockAreaChange.hpp'),
('CScriptSpecialFunction', 'World/CScriptSpecialFunction.hpp'),
('CScriptDebris', 'World/CScriptDebris.hpp'),
Namespace('MP1'),
('CBeetle', 'MP1/CBeetle.hpp', 'MP1'),
('CWarWasp', 'MP1/CWarWasp.hpp', 'MP1'),
('CSpacePirate', 'MP1/CSpacePirate.hpp', 'MP1'),
('CNewIntroBoss', 'MP1/CNewIntroBoss.hpp', 'MP1'),
EndNamespace(),
)
def getqualified(tp):

View File

@ -1,5 +1,6 @@
#include "CPatterned.hpp"
#include "CPatternedInfo.hpp"
#include "TCastTo.hpp"
namespace urde
{
@ -23,4 +24,10 @@ CPatterned::CPatterned(EUnknown, TUniqueId uid, const std::string& name, CPatter
pInfo.xfc_stateMachineId, actorParms, pInfo.xd8_stepUpHeight, 0.8f)
{
}
void CPatterned::Accept(IVisitor& visitor)
{
visitor.Visit(this);
}
}

View File

@ -41,6 +41,7 @@ public:
CPatterned::EMovementType movement, EColliderType collider, EBodyType body,
const CActorParameters& params, bool b1);
void Accept(IVisitor& visitor);
virtual void Death(const zeus::CVector3f&, CStateManager&) {}
virtual void KnockBack(const zeus::CVector3f&, CStateManager&) {}
};

View File

@ -35,7 +35,7 @@ void CScriptStreamedMusic::TweakOverride(CStateManager& mgr)
const CWorld* wld = mgr.GetWorld();
const CGameArea* area = wld->GetAreaAlways(x4_areaId);
std::string twkName = hecl::Format("Area %8.8x MusicObject: %s",
area->GetAreaAssetId(), x10_name.c_str());
unsigned(area->GetAreaAssetId()), x10_name.c_str());
if (g_TweakManager->HasTweakValue(twkName))
{
const CTweakValue::Audio& audio = g_TweakManager->GetTweakValue(twkName)->GetAudio();

2
hecl

@ -1 +1 @@
Subproject commit 0c6a88a95af7a36cebda27f3c511ba585d195230
Subproject commit 146a96ad65a6fbfbf1d2c2f9077d6d530bf5a80d

2
nod

@ -1 +1 @@
Subproject commit 31a06ea7266abace21473488d5191cea8e6554d2
Subproject commit cf2f68f8b2f5869940426a5cdd12519538bb0c26

@ -1 +1 @@
Subproject commit e961af19ee4d69ef8e7f9cb912058b8397027973
Subproject commit 03e7124c5f576033c10ad2d0a116e4a331c0381d