metaforce/Runtime/MP1/MP1.cpp

443 lines
14 KiB
C++
Raw Normal View History

#include "MP1.hpp"
2016-07-21 05:21:45 +00:00
#include "Graphics/Shaders/CModelShaders.hpp"
#include "Graphics/Shaders/CThermalColdFilter.hpp"
2016-08-03 21:53:03 +00:00
#include "Graphics/Shaders/CThermalHotFilter.hpp"
2016-07-29 17:38:44 +00:00
#include "Graphics/Shaders/CSpaceWarpFilter.hpp"
#include "Graphics/Shaders/CColoredQuadFilter.hpp"
#include "Graphics/Shaders/CTexturedQuadFilter.hpp"
2016-08-20 18:18:44 +00:00
#include "Graphics/Shaders/CCameraBlurFilter.hpp"
2016-08-20 04:22:13 +00:00
#include "Graphics/Shaders/CXRayBlurFilter.hpp"
2017-01-29 03:58:16 +00:00
#include "Graphics/Shaders/CTextSupportShader.hpp"
2017-04-02 03:03:37 +00:00
#include "Graphics/Shaders/CEnergyBarShader.hpp"
2017-04-07 05:35:09 +00:00
#include "Graphics/Shaders/CRadarPaintShader.hpp"
2017-04-22 06:42:32 +00:00
#include "Graphics/Shaders/CMapSurfaceShader.hpp"
2017-05-06 05:21:42 +00:00
#include "Graphics/Shaders/CPhazonSuitFilter.hpp"
2017-06-01 05:34:24 +00:00
#include "Graphics/Shaders/CScanLinesFilter.hpp"
#include "Graphics/Shaders/CRandomStaticFilter.hpp"
#include "Graphics/Shaders/CFluidPlaneShader.hpp"
2017-09-03 05:53:38 +00:00
#include "Graphics/Shaders/CAABoxShader.hpp"
#include "Graphics/Shaders/CWorldShadowShader.hpp"
2016-08-23 03:12:50 +00:00
#include "Character/CCharLayoutInfo.hpp"
2017-01-16 09:14:54 +00:00
#include "Audio/CStreamAudioManager.hpp"
2017-01-08 02:52:49 +00:00
#include "CGBASupport.hpp"
2017-01-08 03:08:02 +00:00
#include "CBasics.hpp"
2017-01-20 03:53:32 +00:00
#include "Audio/CAudioGroupSet.hpp"
2015-08-29 01:30:47 +00:00
2016-03-04 23:04:53 +00:00
namespace urde
2015-08-17 05:26:58 +00:00
{
URDE_DECL_SPECIALIZE_SHADER(CThermalColdFilter)
2016-08-03 21:53:03 +00:00
URDE_DECL_SPECIALIZE_SHADER(CThermalHotFilter)
2016-07-29 17:38:44 +00:00
URDE_DECL_SPECIALIZE_SHADER(CSpaceWarpFilter)
2016-08-20 18:18:44 +00:00
URDE_DECL_SPECIALIZE_SHADER(CCameraBlurFilter)
2016-08-20 04:22:13 +00:00
URDE_DECL_SPECIALIZE_SHADER(CXRayBlurFilter)
URDE_DECL_SPECIALIZE_SHADER(CFogVolumePlaneShader)
URDE_DECL_SPECIALIZE_SHADER(CFogVolumeFilter)
2017-04-02 03:03:37 +00:00
URDE_DECL_SPECIALIZE_SHADER(CEnergyBarShader)
2017-04-07 05:35:09 +00:00
URDE_DECL_SPECIALIZE_SHADER(CRadarPaintShader)
2017-04-22 06:42:32 +00:00
URDE_DECL_SPECIALIZE_SHADER(CMapSurfaceShader)
2017-05-06 05:21:42 +00:00
URDE_DECL_SPECIALIZE_SHADER(CPhazonSuitFilter)
2017-09-03 05:53:38 +00:00
URDE_DECL_SPECIALIZE_SHADER(CAABoxShader)
URDE_DECL_SPECIALIZE_SHADER(CWorldShadowShader)
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CColoredQuadFilter)
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CTexturedQuadFilter)
2016-09-14 05:54:09 +00:00
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CTexturedQuadFilterAlpha)
2017-01-29 03:58:16 +00:00
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CTextSupportShader)
2017-06-01 05:34:24 +00:00
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CScanLinesFilter)
URDE_DECL_SPECIALIZE_MULTI_BLEND_SHADER(CRandomStaticFilter)
2015-08-18 05:54:43 +00:00
namespace MP1
{
CGameArchitectureSupport::CGameArchitectureSupport(CMain& parent,
boo::IAudioVoiceEngine* voiceEngine,
2016-12-14 22:56:59 +00:00
amuse::IBackendVoiceAllocator& backend)
: m_parent(parent),
2016-12-29 21:38:59 +00:00
x0_audioSys(voiceEngine, backend, 0,0,0,0,0),
x30_inputGenerator(g_tweakPlayer->GetLeftLogicalThreshold(),
g_tweakPlayer->GetRightLogicalThreshold()),
2016-12-29 21:38:59 +00:00
x44_guiSys(*g_ResFactory, *g_SimplePool, CGuiSys::EUsageMode::Zero)
2016-09-14 05:54:09 +00:00
{
CMain* m = static_cast<CMain*>(g_Main);
2016-12-29 21:38:59 +00:00
g_GuiSys = &x44_guiSys;
x30_inputGenerator.startScanning();
2017-02-18 02:19:50 +00:00
g_InputGenerator = &x30_inputGenerator;
2017-01-23 07:22:17 +00:00
CAudioSys::SysSetVolume(0x7f);
CAudioSys::SetDefaultVolumeScale(0x75);
CAudioSys::SetVolumeScale(CAudioSys::GetDefaultVolumeScale());
2017-01-16 09:14:54 +00:00
CStreamAudioManager::Initialize();
2017-02-06 03:21:58 +00:00
CStreamAudioManager::SetMusicVolume(0x7f);
m->ResetGameState();
2016-09-14 05:54:09 +00:00
2017-01-29 03:58:16 +00:00
//std::shared_ptr<CIOWin> splash = std::make_shared<CSplashScreen>(CSplashScreen::ESplashScreen::Nintendo);
//x58_ioWinManager.AddIOWin(splash, 1000, 10000);
2016-09-14 05:54:09 +00:00
std::shared_ptr<CIOWin> mf = std::make_shared<CMainFlow>();
2016-12-29 21:38:59 +00:00
x58_ioWinManager.AddIOWin(mf, 0, 0);
2016-09-14 05:54:09 +00:00
std::shared_ptr<CIOWin> console = std::make_shared<CConsoleOutputWindow>(8, 5.f, 0.75f);
2016-12-29 21:38:59 +00:00
x58_ioWinManager.AddIOWin(console, 100, 0);
2016-09-14 05:54:09 +00:00
std::shared_ptr<CIOWin> audState = std::make_shared<CAudioStateWin>();
2016-12-29 21:38:59 +00:00
x58_ioWinManager.AddIOWin(audState, 100, -1);
2016-09-14 05:54:09 +00:00
std::shared_ptr<CIOWin> errWin = std::make_shared<CErrorOutputWindow>(false);
2016-12-29 21:38:59 +00:00
x58_ioWinManager.AddIOWin(errWin, 10000, 100000);
2016-09-14 05:54:09 +00:00
}
2017-01-20 03:53:32 +00:00
void CGameArchitectureSupport::UpdateTicks()
2016-09-14 05:54:09 +00:00
{
2017-02-18 02:19:50 +00:00
x4_archQueue.Push(MakeMsg::CreateFrameBegin(EArchMsgTarget::Game, x78_gameFrameCount));
2017-01-20 03:53:32 +00:00
x4_archQueue.Push(MakeMsg::CreateTimerTick(EArchMsgTarget::Game, 1.f / 60.f));
}
2016-09-14 05:54:09 +00:00
2017-01-20 03:53:32 +00:00
void CGameArchitectureSupport::Update()
{
2016-09-14 05:54:09 +00:00
g_GameState->GetWorldTransitionManager()->TouchModels();
2017-01-22 17:36:25 +00:00
x30_inputGenerator.Update(1 / 60.f, x4_archQueue);
2017-02-18 02:19:50 +00:00
x4_archQueue.Push(MakeMsg::CreateFrameEnd(EArchMsgTarget::Game, x78_gameFrameCount));
2016-12-29 21:38:59 +00:00
x58_ioWinManager.PumpMessages(x4_archQueue);
2017-01-20 03:53:32 +00:00
}
struct AudioGroupInfo
{
const char* name;
u32 id;
};
static const AudioGroupInfo StaticAudioGroups[] =
{
{"Misc_AGSC", 39},
{"MiscSamus_AGSC", 41},
{"UI_AGSC", 40},
{"Weapons_AGSC", 43},
{"ZZZ_AGSC", 65}
};
bool CGameArchitectureSupport::LoadAudio()
{
if (x88_audioLoadStatus == EAudioLoadStatus::Loaded)
return true;
for (int i=0 ; i<5 ; ++i)
{
TToken<CAudioGroupSet>& tok = x8c_pendingAudioGroups[i];
if (tok.IsLocked())
{
if (tok.IsLoaded())
{
CAudioGroupSet* set = tok.GetObj();
if (!CAudioSys::SysIsGroupSetLoaded(set->GetName()))
{
CAudioSys::SysLoadGroupSet(tok, set->GetName(), tok.GetObjectTag()->id);
CAudioSys::SysAddGroupIntoAmuse(set->GetName());
}
}
else
{
return false;
}
}
else
{
/* Lock next pending group */
tok.Lock();
return false;
}
}
CSfxManager::LoadTranslationTable(g_SimplePool, g_ResFactory->GetResourceIdByName("sound_lookup"));
x8c_pendingAudioGroups = std::vector<TToken<CAudioGroupSet>>();
x88_audioLoadStatus = EAudioLoadStatus::Loaded;
return true;
}
void CGameArchitectureSupport::PreloadAudio()
{
if (x88_audioLoadStatus != EAudioLoadStatus::Uninitialized)
return;
x8c_pendingAudioGroups.clear();
x8c_pendingAudioGroups.reserve(5);
for (int i=0 ; i<5 ; ++i)
{
const AudioGroupInfo& info = StaticAudioGroups[i];
CToken grp = g_SimplePool->GetObj(info.name);
if (i == 0) /* Lock first group in sequence */
grp.Lock();
x8c_pendingAudioGroups.push_back(std::move(grp));
}
2016-12-29 21:38:59 +00:00
2017-01-20 03:53:32 +00:00
x88_audioLoadStatus = EAudioLoadStatus::Loading;
2016-09-14 05:54:09 +00:00
}
void CGameArchitectureSupport::UnloadAudio()
{
for (int i=0 ; i<5 ; ++i)
{
const AudioGroupInfo& info = StaticAudioGroups[i];
const SObjectTag* tag = g_ResFactory->GetResourceIdByName(info.name);
const std::string& name = CAudioSys::SysGetGroupSetName(tag->id);
CAudioSys::SysRemoveGroupFromAmuse(name);
CAudioSys::SysUnloadAudioGroupSet(name);
}
x8c_pendingAudioGroups = std::vector<TToken<CAudioGroupSet>>();
x88_audioLoadStatus = EAudioLoadStatus::Uninitialized;
}
2016-09-14 05:54:09 +00:00
void CGameArchitectureSupport::Draw()
{
2016-12-29 21:38:59 +00:00
x58_ioWinManager.Draw();
2017-02-18 02:19:50 +00:00
if (m_parent.x161_24_gameFrameDrawn)
{
++x78_gameFrameCount;
m_parent.x161_24_gameFrameDrawn = false;
}
2016-09-14 05:54:09 +00:00
}
CGameArchitectureSupport::~CGameArchitectureSupport()
{
x58_ioWinManager.RemoveAllIOWins();
UnloadAudio();
CSfxManager::Shutdown();
CStreamAudioManager::Shutdown();
}
CMain::CMain(IFactory& resFactory, CSimplePool& resStore,
boo::IGraphicsDataFactory* gfxFactory,
boo::IGraphicsCommandQueue* cmdQ,
boo::ITextureR* spareTex)
2016-09-14 05:54:09 +00:00
: m_booSetter(gfxFactory, cmdQ, spareTex),
x128_globalObjects(resFactory, resStore)
2015-08-17 05:26:58 +00:00
{
2015-11-21 01:16:07 +00:00
xe4_gameplayResult = EGameplayResult::Playing;
2016-08-15 20:58:07 +00:00
g_Main = this;
2015-08-27 00:23:46 +00:00
}
2016-04-15 20:42:40 +00:00
CMain::BooSetter::BooSetter(boo::IGraphicsDataFactory* factory,
boo::IGraphicsCommandQueue* cmdQ,
boo::ITextureR* spareTex)
{
CGraphics::InitializeBoo(factory, cmdQ, spareTex);
TShader<CThermalColdFilter>::Initialize();
TShader<CThermalHotFilter>::Initialize();
TShader<CSpaceWarpFilter>::Initialize();
2016-08-20 18:18:44 +00:00
TShader<CCameraBlurFilter>::Initialize();
2016-08-20 04:22:13 +00:00
TShader<CXRayBlurFilter>::Initialize();
TShader<CFogVolumePlaneShader>::Initialize();
TShader<CFogVolumeFilter>::Initialize();
2017-04-02 03:03:37 +00:00
TShader<CEnergyBarShader>::Initialize();
2017-04-07 05:35:09 +00:00
TShader<CRadarPaintShader>::Initialize();
2017-04-22 06:42:32 +00:00
TShader<CMapSurfaceShader>::Initialize();
2017-05-06 05:21:42 +00:00
TShader<CPhazonSuitFilter>::Initialize();
2017-09-03 05:53:38 +00:00
TShader<CAABoxShader>::Initialize();
TShader<CWorldShadowShader>::Initialize();
TMultiBlendShader<CColoredQuadFilter>::Initialize();
TMultiBlendShader<CTexturedQuadFilter>::Initialize();
2016-09-14 05:54:09 +00:00
TMultiBlendShader<CTexturedQuadFilterAlpha>::Initialize();
2017-01-29 03:58:16 +00:00
TMultiBlendShader<CTextSupportShader>::Initialize();
2017-06-01 05:34:24 +00:00
TMultiBlendShader<CScanLinesFilter>::Initialize();
TMultiBlendShader<CRandomStaticFilter>::Initialize();
}
2015-08-27 00:23:46 +00:00
void CMain::RegisterResourceTweaks()
{
}
void CMain::ResetGameState()
{
2016-12-29 21:38:59 +00:00
CPersistentOptions sysOpts = g_GameState->SystemOptions();
CGameOptions gameOpts = g_GameState->GameOptions();
x128_globalObjects.ResetGameState();
g_GameState->ImportPersistentOptions(sysOpts);
g_GameState->SetGameOptions(gameOpts);
2017-01-01 06:46:52 +00:00
g_GameState->GetPlayerState()->SetIsFusionEnabled(g_GameState->SystemOptions().GetPlayerFusionSuitActive());
2015-08-27 00:23:46 +00:00
}
2016-12-14 22:56:59 +00:00
void CMain::InitializeSubsystems(const hecl::Runtime::FileStoreManager& storeMgr)
2015-08-27 00:23:46 +00:00
{
2017-01-08 03:08:02 +00:00
CBasics::Initialize();
CModelShaders::Initialize(storeMgr, CGraphics::g_BooFactory);
2016-04-15 20:42:40 +00:00
CMoviePlayer::Initialize();
CLineRenderer::Initialize();
2015-08-27 00:23:46 +00:00
CElementGen::Initialize();
CAnimData::InitializeCache();
CDecalManager::Initialize();
2017-01-08 02:52:49 +00:00
CGBASupport::Initialize();
2015-08-27 00:23:46 +00:00
}
2015-08-27 00:23:46 +00:00
void CMain::FillInAssetIDs()
{
}
2017-01-20 03:53:32 +00:00
bool CMain::LoadAudio()
2015-08-27 00:23:46 +00:00
{
2017-01-20 03:53:32 +00:00
if (x164_archSupport)
return x164_archSupport->LoadAudio();
return true;
2015-08-27 00:23:46 +00:00
}
2017-04-15 05:32:25 +00:00
void CMain::EnsureWorldPaksReady()
{
}
2017-08-13 05:26:14 +00:00
void CMain::EnsureWorldPakReady(CAssetId mlvl)
2017-04-15 05:32:25 +00:00
{
/* TODO: Schedule resource list load for World Pak containing mlvl */
}
2016-12-30 06:37:01 +00:00
void CMain::StreamNewGameState(CBitStreamReader& r, u32 idx)
{
2017-01-01 06:46:52 +00:00
bool fusionBackup = g_GameState->SystemOptions().GetPlayerFusionSuitActive();
2016-12-30 06:43:29 +00:00
x128_globalObjects.x134_gameState = std::make_unique<CGameState>(r, idx);
g_GameState = x128_globalObjects.x134_gameState.get();
2017-01-01 06:46:52 +00:00
g_GameState->SystemOptions().SetPlayerFusionSuitActive(fusionBackup);
2016-12-30 06:43:29 +00:00
g_GameState->GetPlayerState()->SetIsFusionEnabled(fusionBackup);
2016-12-30 06:37:01 +00:00
g_GameState->HintOptions().SetNextHintTime();
}
void CMain::Init(const hecl::Runtime::FileStoreManager& storeMgr,
boo::IWindow* window,
boo::IAudioVoiceEngine* voiceEngine,
amuse::IBackendVoiceAllocator& backend)
2016-04-15 20:42:40 +00:00
{
m_mainWindow = window;
2016-12-14 22:56:59 +00:00
InitializeSubsystems(storeMgr);
2016-08-15 20:58:07 +00:00
x128_globalObjects.PostInitialize();
2016-04-15 20:42:40 +00:00
x70_tweaks.RegisterTweaks();
2016-08-31 21:00:06 +00:00
x70_tweaks.RegisterResourceTweaks();
2016-04-15 20:42:40 +00:00
FillInAssetIDs();
2017-01-20 03:53:32 +00:00
x164_archSupport.reset(new CGameArchitectureSupport(*this, voiceEngine, backend));
g_archSupport = x164_archSupport.get();
x164_archSupport->PreloadAudio();
//g_TweakManager->ReadFromMemoryCard("AudioTweaks");
2017-01-21 00:20:19 +00:00
//CStreamAudioManager::Start(false, "Audio/rui_samusL.dsp|Audio/rui_samusR.dsp", 0x7f, true, 1.f, 1.f);
2016-04-15 20:42:40 +00:00
}
bool CMain::Proc()
{
2017-01-08 02:52:49 +00:00
CGBASupport::GlobalPoll();
2017-01-20 03:53:32 +00:00
x164_archSupport->UpdateTicks();
x164_archSupport->Update();
2017-01-23 07:22:17 +00:00
CSfxManager::Update(1.f / 60.f);
2017-01-16 09:14:54 +00:00
CStreamAudioManager::Update(1.f / 60.f);
2017-02-06 03:21:58 +00:00
if (x164_archSupport->GetIOWinManager().IsEmpty() || CheckReset())
{
CStreamAudioManager::StopAll();
/*
x164_archSupport.reset();
g_archSupport = x164_archSupport.get();
x164_archSupport->PreloadAudio();
*/
x160_24_finished = true;
}
2017-01-20 03:53:32 +00:00
return x160_24_finished;
2016-04-15 20:42:40 +00:00
}
2016-09-14 05:54:09 +00:00
void CMain::Draw()
{
2017-01-20 03:53:32 +00:00
x164_archSupport->Draw();
2016-09-14 05:54:09 +00:00
}
void CMain::ShutdownSubsystems()
{
CMoviePlayer::Shutdown();
CLineRenderer::Shutdown();
CDecalManager::Shutdown();
CElementGen::Shutdown();
CAnimData::FreeCache();
2017-01-22 17:36:25 +00:00
CMemoryCardSys::Shutdown();
CModelShaders::Shutdown();
2017-04-22 06:42:32 +00:00
CMappableObject::Shutdown();
}
2016-04-15 20:42:40 +00:00
void CMain::Shutdown()
{
x164_archSupport.reset();
ShutdownSubsystems();
2016-08-20 04:22:13 +00:00
TShader<CThermalColdFilter>::Shutdown();
TShader<CThermalHotFilter>::Shutdown();
TShader<CSpaceWarpFilter>::Shutdown();
2016-08-20 18:18:44 +00:00
TShader<CCameraBlurFilter>::Shutdown();
2016-08-20 04:22:13 +00:00
TShader<CXRayBlurFilter>::Shutdown();
TShader<CFogVolumePlaneShader>::Shutdown();
TShader<CFogVolumeFilter>::Shutdown();
2017-04-02 03:03:37 +00:00
TShader<CEnergyBarShader>::Shutdown();
2017-04-07 05:35:09 +00:00
TShader<CRadarPaintShader>::Shutdown();
2017-04-22 06:42:32 +00:00
TShader<CMapSurfaceShader>::Shutdown();
2017-05-06 05:21:42 +00:00
TShader<CPhazonSuitFilter>::Shutdown();
2017-09-03 05:53:38 +00:00
TShader<CAABoxShader>::Shutdown();
TShader<CWorldShadowShader>::Shutdown();
2016-08-20 04:22:13 +00:00
TMultiBlendShader<CColoredQuadFilter>::Shutdown();
TMultiBlendShader<CTexturedQuadFilter>::Shutdown();
2016-09-14 05:54:09 +00:00
TMultiBlendShader<CTexturedQuadFilterAlpha>::Shutdown();
2017-01-29 03:58:16 +00:00
TMultiBlendShader<CTextSupportShader>::Shutdown();
2017-06-01 05:34:24 +00:00
TMultiBlendShader<CScanLinesFilter>::Shutdown();
TMultiBlendShader<CRandomStaticFilter>::Shutdown();
CFluidPlaneShader::Shutdown();
2016-04-15 20:42:40 +00:00
}
boo::IWindow* CMain::GetMainWindow() const
{
return m_mainWindow;
}
2016-04-15 20:42:40 +00:00
#if MP1_USE_BOO
int CMain::appMain(boo::IApplication* app)
2015-08-27 00:23:46 +00:00
{
2016-03-04 23:04:53 +00:00
zeus::detectCPU();
2016-02-24 03:20:07 +00:00
mainWindow = app->newWindow(_S("Metroid Prime 1 Reimplementation vZygote"), 1);
mainWindow->showWindow();
2015-08-27 00:23:46 +00:00
TOneStatic<CGameGlobalObjects> globalObjs;
InitializeSubsystems();
2016-04-15 20:42:40 +00:00
globalObjs->PostInitialize();
2015-08-27 00:23:46 +00:00
x70_tweaks.RegisterTweaks();
AddWorldPaks();
g_TweakManager->ReadFromMemoryCard("AudioTweaks");
FillInAssetIDs();
2015-08-28 00:11:31 +00:00
TOneStatic<CGameArchitectureSupport> archSupport;
2016-02-20 06:45:36 +00:00
mainWindow->setCallback(archSupport.GetAllocSpace());
boo::IGraphicsCommandQueue* gfxQ = mainWindow->getCommandQueue();
2016-02-20 10:26:43 +00:00
boo::SWindowRect windowRect = mainWindow->getWindowFrame();
2016-03-30 19:16:01 +00:00
boo::ITextureR* renderTex;
boo::GraphicsDataToken data = mainWindow->getMainContextDataFactory()->commitTransaction(
[&](boo::IGraphicsDataFactory::Context& ctx) -> bool
{
renderTex = ctx.newRenderTexture(windowRect.size[0], windowRect.size[1], true, true);
return true;
});
2016-02-20 05:49:47 +00:00
float rgba[4] = { 0.2f, 0.2f, 0.2f, 1.0f};
gfxQ->setClearColor(rgba);
2015-09-17 19:50:43 +00:00
while (!xe8_b24_finished)
2015-08-17 05:26:58 +00:00
{
2015-09-17 19:50:43 +00:00
xe8_b24_finished = archSupport->Update();
2016-02-20 10:26:43 +00:00
if (archSupport->isRectDirty())
{
const boo::SWindowRect& windowRect = archSupport->getWindowRect();
gfxQ->resizeRenderTexture(renderTex,
windowRect.size[0],
windowRect.size[1]);
}
gfxQ->setRenderTarget(renderTex);
gfxQ->clearTarget();
gfxQ->resolveDisplay(renderTex);
gfxQ->execute();
mainWindow->waitForRetrace();
2015-08-17 05:26:58 +00:00
}
2015-08-27 00:23:46 +00:00
return 0;
}
2015-08-17 05:26:58 +00:00
2016-04-15 20:42:40 +00:00
#endif
}
}