/*---------------------------------------------------------------------------* Project: Horizon File: SimpleServer.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 47228 $ *---------------------------------------------------------------------------*/ #include #include #include #include #include "demo.h" #include "SimpleServer.h" #include "dlpDemo.h" #include "Parent.h" #include "nn/dlp/CTR/dlp_ServerWithName.h" #include #include static size_t s_DlpServerWorkBufferSize; static void* s_pWorkBuffer; static void* s_pAlignedWorkBuffer; static bool s_IsDistributionDone = false; static bool s_IsNoticeNeeded = false; static nn::os::Event s_DlpServerEvent; static u16 s_Selected = 0; static nn::hid::PadStatus s_PadStatus; static nn::os::LightEvent s_AwakeEvent; // Wake events static u16 s_ClientNum; static char s_Passphraase[nn::dlp::MAX_CHILD_UDS_PASSPHRASE_LENGTH]; static nn::cfg::UserName s_UserName; nn::applet::AppletQueryReply mySleepQueryCallback(uptr arg) { NN_UNUSED_VAR(arg); DLP_DEBUG_POINT; return nn::applet::CTR::IsActive()? nn::applet::CTR::REPLY_LATER: nn::applet::CTR::REPLY_ACCEPT; } void myAwakeCallback(uptr arg) { NN_UNUSED_VAR(arg); DLP_DEBUG_POINT; s_AwakeEvent.Signal(); } // Display client status void DisplayClientState(u16 x, u16 y, nn::dlp::ClientState state, demo::RenderSystemDrawing* pRenderSystem) { switch (state) { case nn::dlp::CLIENT_STATE_WAITING_INVITE: pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "WAIT INVITE"); break; case nn::dlp::CLIENT_STATE_WAITING_ACCEPT: pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "WAIT ACCEPT"); break; case nn::dlp::CLIENT_STATE_JOINED_SESSION: pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "JOINED"); break; case nn::dlp::CLIENT_STATE_DOWNLOADING: pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "DOWNLOADING"); break; case nn::dlp::CLIENT_STATE_DOWNLOAD_COMPLETE: pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "COMPLETE"); break; default: pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "INVALID"); } } // Display a list of clients u16 DisplayClientList(u16 selected, u16 x, u16 y, demo::RenderSystemDrawing* pRenderSystem) { char buffer[14]; // Specify white in font color pRenderSystem->SetColor(WHITE_COLOR); pRenderSystem->DrawText(FONT_SIZE * (x + 3), FONT_SIZE * y, "CLIENT NAME"); pRenderSystem->DrawText(FONT_SIZE * (x + 15), FONT_SIZE * y, "STATE"); pRenderSystem->DrawText(FONT_SIZE * (x + 28), FONT_SIZE * y, "PROGRESS"); // Get the client list u16 clientNum = 0; u16 clientId[MAX_CLIENT]; nn::dlp::Server::GetConnectingClients(&clientNum, clientId, MAX_CLIENT); y += 2; for (int i = 0; i < clientNum; i++) { if (i == selected) { // Specify green in font color pRenderSystem->SetColor(GREEN_COLOR); } else { // Specify white in font color pRenderSystem->SetColor(WHITE_COLOR); } nn::dlp::NodeInfo clientInfo; nn::dlp::ClientState clientState; size_t totalNum; size_t downloadedNum; if ( nn::dlp::Server::GetClientInfo(&clientInfo, clientId[i]).IsFailure() || nn::dlp::Server::GetClientState(&clientState, &totalNum, &downloadedNum, clientId[i]).IsFailure()) { clientNum = 0; break; } pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "%02d", i + 1); wcstombs(buffer, clientInfo.userName.userName, 11); pRenderSystem->DrawText(FONT_SIZE * (x + 3), FONT_SIZE * y, "%s", buffer); DisplayClientState(x + 15, y, clientState, pRenderSystem); pRenderSystem->DrawText(FONT_SIZE * (x + 28), FONT_SIZE * y, "%5d/%5d", downloadedNum, totalNum); y++; } // Use --- to fill in blanks where there is no client for (int i = clientNum; i < MAX_CLIENT; i++) { if (i == selected) { // Specify green in font color pRenderSystem->SetColor(GREEN_COLOR); } else { // Specify white in font color pRenderSystem->SetColor(WHITE_COLOR); } pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y, "%02d", i + 1); pRenderSystem->DrawText(FONT_SIZE * (x + 3), FONT_SIZE * y, "----------"); pRenderSystem->DrawText(FONT_SIZE * (x + 15), FONT_SIZE * y, "------------"); pRenderSystem->DrawText(FONT_SIZE * (x + 28), FONT_SIZE * y, "-----------"); y++; } return y; } // Display child device program information void DisplayTitleInfo(demo::RenderSystemDrawing* pRenderSystem) { u16 x = 1; u16 y = 2; // Specify white in font color pRenderSystem->SetColor(WHITE_COLOR); // Display child device program information pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "TITLE INFO"); } /*!--------------------------------------------------------------------------* Initialization state *---------------------------------------------------------------------------*/ void DisplayInitialize(demo::RenderSystemDrawing* pRenderSystem) { u16 x = 1; u16 y = 2; pRenderSystem->SetColor(WHITE_COLOR); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "SIMPLE SERVER"); y++; pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "NOW INITIALIZING..."); } /*!--------------------------------------------------------------------------* Start state *---------------------------------------------------------------------------*/ void UpdateStart(const nn::hid::PadStatus padStatus) { if (padStatus.trigger & nn::hid::BUTTON_A) { // Start download session // Because there is a bug that it difficult to connect with 1 ch DLP_DEBUG_POINT; nn::dlp::Server::OpenSessions(true, 6); } else if (padStatus.trigger & nn::hid::BUTTON_Y) { // Initialize with the user name specified with the code std::memcpy( s_UserName.userName, L"SERVER", sizeof(L"SERVER")); nn::dlp::Server::Finalize(); nn::dlp::ServerWithName::Initialize( &s_IsNoticeNeeded, s_DlpServerEvent.GetHandle(), MAX_CLIENT, CHILD_INDEX, s_pAlignedWorkBuffer, s_DlpServerWorkBufferSize, nn::dlp::MIN_NETWORK_BLOCK_BUFFER_SIZE * 2, nn::dlp::MIN_NETWORK_BLOCK_BUFFER_NUM, &s_UserName ); // Start download session // Because there is a bug that it difficult to connect with 1 ch DLP_DEBUG_POINT; nn::dlp::Server::OpenSessions(true, 6); } } void DisplayStart(demo::RenderSystemDrawing* pRenderSystem) { u16 x = 1; u16 y = 2; pRenderSystem->SetColor(WHITE_COLOR); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "SIMPLE SERVER"); y++; pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "PRESS A TO START"); y++; if (s_IsNoticeNeeded) { y++; pRenderSystem->SetColor(RED_COLOR); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "[WARNING]"); y++; pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "SimpleServer may disconnect clients without"); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "a notice. Please update clients via network."); } } /*!--------------------------------------------------------------------------* Accept client status *---------------------------------------------------------------------------*/ void UpdateAcceptClient(u16* pSelected, const nn::hid::PadStatus padStatus) { // Get the client list u16 clientNum = 0; u16 clientId[MAX_CLIENT]; nn::dlp::Server::GetConnectingClients(&clientNum, clientId, MAX_CLIENT); // Checks whether waiting for Accept bool isWaitingAccept = false; if ((*pSelected) < clientNum) { DLP_DEBUG_POINT; // Gets client status nn::dlp::ClientState clientState; nn::dlp::Server::GetClientState(&clientState, clientId[*pSelected]); if (clientState == nn::dlp::CLIENT_STATE_WAITING_ACCEPT) { DLP_DEBUG_POINT; isWaitingAccept = true; } } if (padStatus.trigger & nn::hid::BUTTON_A) { DLP_DEBUG_POINT; // ACCEPT if (isWaitingAccept) { DLP_DEBUG_POINT; // Accept nn::dlp::Server::AcceptClient(clientId[*pSelected]); } } else if (padStatus.trigger & nn::hid::BUTTON_B) { DLP_DEBUG_POINT; // REJECT if (isWaitingAccept) { DLP_DEBUG_POINT; // Disconnect nn::dlp::Server::DisconnectClient(clientId[*pSelected]); } } else if (padStatus.trigger & nn::hid::BUTTON_X) { DLP_DEBUG_POINT; // Start distribution nn::dlp::Server::StartDistribute(); nn::dlp::Server::GetConnectingClients(&s_ClientNum, clientId, MAX_CLIENT); } else if (padStatus.trigger & nn::hid::BUTTON_UP) { DLP_DEBUG_POINT; // Move the selection (*pSelected) = ((*pSelected) - 1 + MAX_CLIENT) % MAX_CLIENT; } else if (padStatus.trigger & nn::hid::BUTTON_DOWN) { DLP_DEBUG_POINT; // Move the selection (*pSelected) = ((*pSelected) + 1) % MAX_CLIENT; } } void DisplayAcceptClient(u16 selected, demo::RenderSystemDrawing* pRenderSystem) { u16 x = 1; u16 y = 2; y = DisplayClientList(selected, x, y, pRenderSystem); y++; pRenderSystem->SetColor(WHITE_COLOR); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "PRESS A TO ACCEPT CLIENT"); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "PRESS B TO REJECT CLIENT"); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "(WAIT ACCEPT STATE CLIENT ONLY)"); y++; pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "PRESS X TO START DISTRIBUTE"); } /*!--------------------------------------------------------------------------* Distribution status *---------------------------------------------------------------------------*/ void UpdateDistribute(nn::hid::PadStatus padStatus) { if (padStatus.trigger & nn::hid::BUTTON_B) { DLP_DEBUG_POINT; nn::dlp::Server::CloseSessions(); } } void DisplayDistribute(demo::RenderSystemDrawing* pRenderSystem) { u16 x = 1; u16 y = 2; y = DisplayClientList(MAX_CLIENT + 1, x, y, pRenderSystem); pRenderSystem->SetColor(WHITE_COLOR); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "PRESS B TO CANCEL"); } /*!--------------------------------------------------------------------------* Distribution completion status *---------------------------------------------------------------------------*/ void UpdateComplete() { DLP_DEBUG_POINT; u32 random; ::std::srand(static_cast(nn::os::Tick::GetSystemCurrent() >> 1)); random = static_cast(::std::rand()); nn::nstd::TSNPrintf(s_Passphraase, sizeof(s_Passphraase), "%08x", random); DLP_DEBUG_PRINT("Passphrase : %s \n", s_Passphraase); nn::dlp::Server::RebootAllClients(s_Passphraase); } /*!--------------------------------------------------------------------------* Waiting for reboot status *---------------------------------------------------------------------------*/ void UpdateReboot() { // Get the client list u16 clientNum = 0; u16 clientId[MAX_CLIENT]; if (nn::dlp::Server::GetConnectingClients(&clientNum, clientId, MAX_CLIENT).IsSuccess()) { DLP_DEBUG_POINT; if (clientNum == 0) { DLP_DEBUG_POINT; s_IsDistributionDone = true; } } } void DisplayReboot(demo::RenderSystemDrawing* pRenderSystem) { u16 x = 1; u16 y = 2; y = DisplayClientList(MAX_CLIENT + 1, x, y, pRenderSystem); } /*!--------------------------------------------------------------------------* Error state *---------------------------------------------------------------------------*/ void UpdateError(nn::hid::PadStatus padStatus) { nn::dlp::Server::Finalize(); if (padStatus.trigger & nn::hid::BUTTON_A) { DLP_DEBUG_POINT; s_Selected = 0; nn::dlp::Server::Initialize( &s_IsNoticeNeeded, s_DlpServerEvent.GetHandle(), MAX_CLIENT, CHILD_INDEX, s_pAlignedWorkBuffer, s_DlpServerWorkBufferSize); } } void DisplayError(demo::RenderSystemDrawing* pRenderSystem) { u16 x = 1; u16 y = 2; pRenderSystem->SetColor(WHITE_COLOR); pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "+++ ERROR +++"); y++; pRenderSystem->DrawText(FONT_SIZE * x, FONT_SIZE * y++, "PRESS A TO RESTART"); } /*!--------------------------------------------------------------------------* Black background *---------------------------------------------------------------------------*/ void DisplayBlackBack(demo::RenderSystemDrawing& renderSystem) { renderSystem.SetRenderTarget(NN_GX_DISPLAY0); renderSystem.Clear(); renderSystem.SwapBuffers(); renderSystem.SetRenderTarget(NN_GX_DISPLAY1); renderSystem.Clear(); renderSystem.SwapBuffers(); } /*!--------------------------------------------------------------------------* Update/display state functions *---------------------------------------------------------------------------*/ void Update(nn::dlp::ServerState serverState, u16* pSelected, const nn::hid::PadStatus padStatus) { switch (serverState) { case nn::dlp::SERVER_STATE_INITIALIZED: UpdateStart(padStatus); break; case nn::dlp::SERVER_STATE_OPENED_SESSIONS: UpdateAcceptClient(pSelected, padStatus); break; case nn::dlp::SERVER_STATE_DISTRIBUTING: UpdateDistribute(padStatus); break; case nn::dlp::SERVER_STATE_COMPLETE_DISTRIBUTION: UpdateComplete(); break; case nn::dlp::SERVER_STATE_REBOOTING_CLIENTS: UpdateReboot(); break; case nn::dlp::SERVER_STATE_ERROR: case nn::dlp::SERVER_STATE_INVALID: UpdateError(padStatus); break; } } void Display(nn::dlp::ServerState serverState, u16 selected, demo::RenderSystemDrawing* pRenderSystem) { // Render on the upper screen pRenderSystem->SetRenderTarget(NN_GX_DISPLAY0); pRenderSystem->Clear(); switch (serverState) { case nn::dlp::SERVER_STATE_INITIALIZED: DisplayStart(pRenderSystem); break; case nn::dlp::SERVER_STATE_OPENED_SESSIONS: DisplayAcceptClient(selected, pRenderSystem); break; case nn::dlp::SERVER_STATE_DISTRIBUTING: case nn::dlp::SERVER_STATE_COMPLETE_DISTRIBUTION: DisplayDistribute(pRenderSystem); break; case nn::dlp::SERVER_STATE_REBOOTING_CLIENTS: DisplayReboot(pRenderSystem); break; case nn::dlp::SERVER_STATE_ERROR: case nn::dlp::SERVER_STATE_INVALID: DisplayError(pRenderSystem); break; } pRenderSystem->SwapBuffers(); // Render on the lower screen pRenderSystem->SetRenderTarget(NN_GX_DISPLAY1); pRenderSystem->Clear(); // DisplayTitleInfo(pRenderSystem); pRenderSystem->SwapBuffers(); } /*!--------------------------------------------------------------------------* Main function *---------------------------------------------------------------------------*/ void nnMain(void) { nn::Result result; //Set sleep callback, sleep recovery callback nn::applet::SetSleepQueryCallback(mySleepQueryCallback, 0); nn::applet::SetAwakeCallback(myAwakeCallback, 0); nn::applet::Enable(); s_AwakeEvent.Initialize(false); DLP_DEBUG_POINT; // Initialize hid result = nn::hid::Initialize(); NN_UTIL_PANIC_IF_FAILED(result); // Prepare pad nn::hid::PadReader padReader; // Allocate heap nn::fnd::ExpHeap appHeap; appHeap.Initialize(nn::os::GetDeviceMemoryAddress(), 16 * 1024 * 1024 ); // nn::os::GetDeviceMemorySize() // Prepare RenderSystem uptr heapForGx = reinterpret_cast (appHeap.Allocate(0x800000)); demo::RenderSystemDrawing renderSystem; renderSystem.Initialize(heapForGx, 0x800000); renderSystem.SetClearColor(NN_GX_DISPLAY_BOTH, 0.f, 0.f, 0.f, 0.f); renderSystem.SetFontSize(FONT_SIZE); renderSystem.SetLineWidth(LINE_WIDTH); // Initialize event used by DLP server s_DlpServerEvent.Initialize(false); // Get work buffer size necessary to initialize DLP server s_DlpServerWorkBufferSize = nn::dlp::Server::GetBufferSize(MAX_CLIENT); DLP_DEBUG_PRINT("dlpServerWorkBufferSize %d\n", s_DlpServerWorkBufferSize); // Get DLP server work buffer s_pWorkBuffer = new u8[s_DlpServerWorkBufferSize + 4096]; DLP_DEBUG_PRINT("dlpServerWorkBuffer allocated address 0x%08x\n", s_pWorkBuffer); s_pAlignedWorkBuffer = reinterpret_cast (DLP_ROUNDUP(reinterpret_cast(s_pWorkBuffer), 4096UL)); DLP_DEBUG_PRINT("dlpServerWorkBuffer aligned address 0x%08x\n", s_pAlignedWorkBuffer); renderSystem.SetRenderTarget(NN_GX_DISPLAY0); renderSystem.Clear(); DisplayInitialize(&renderSystem); renderSystem.SwapBuffers(); // Initialize DLP server nn::dlp::Server::Initialize( &s_IsNoticeNeeded, s_DlpServerEvent.GetHandle(), MAX_CLIENT, CHILD_INDEX, s_pAlignedWorkBuffer, s_DlpServerWorkBufferSize); DLP_DEBUG_POINT; nn::dlp::ServerState serverState; while (!s_IsDistributionDone) { // Get pad input padReader.ReadLatest(&s_PadStatus); nn::dlp::Server::GetState(&serverState); // Update Update(serverState, &s_Selected, s_PadStatus); // Render Display(serverState, s_Selected, &renderSystem); //Sleep support if ( nn::applet::IsExpectedToReplySleepQuery() ) { DLP_DEBUG_POINT; nn::dlp::Server::Finalize(); // Finalize before sleep. nn::applet::ReplySleepQuery(nn::applet::REPLY_ACCEPT); DLP_DEBUG_POINT; s_AwakeEvent.Wait(); DLP_DEBUG_POINT; // Processing after restart nn::gx::StartLcdDisplay(); DLP_DEBUG_POINT; } //HOME Button support if (nn::applet::IsExpectedToProcessHomeButton()) { DLP_DEBUG_POINT; nn::dlp::Server::Finalize(); nn::applet::ProcessHomeButton(); nn::applet::WaitForStarting(); DLP_DEBUG_POINT; // Recover the GPU register settings nngxUpdateState(NN_GX_STATE_ALL); nngxValidateState(NN_GX_STATE_ALL,GL_TRUE); if (nn::applet::IsExpectedToCloseApplication()) { DLP_DEBUG_POINT; break; } } //POWER Button support if ( nn::applet::IsExpectedToProcessPowerButton() ) { DLP_DEBUG_POINT; nn::dlp::Server::Finalize(); nn::applet::ProcessPowerButton(); nn::applet::WaitForStarting(); DLP_DEBUG_POINT; // Recover the GPU register settings nngxUpdateState(NN_GX_STATE_ALL); nngxValidateState(NN_GX_STATE_ALL,GL_TRUE); if ( nn::applet::IsExpectedToCloseApplication() ) { DLP_DEBUG_POINT; break; } } // Support for termination request if (nn::applet::IsExpectedToCloseApplication()) { DLP_DEBUG_POINT; DisplayBlackBack(renderSystem); break; } } nn::dlp::Server::Finalize(); DLP_DEBUG_POINT; s_DlpServerEvent.Finalize(); DLP_DEBUG_POINT; delete[] s_pWorkBuffer; DLP_DEBUG_POINT; renderSystem.Finalize(); appHeap.Free(reinterpret_cast(heapForGx)); appHeap.Finalize(); nn::hid::Finalize(); nn::applet::DisableSleep(); nn::applet::SetSleepQueryCallback(NULL, 0); nn::applet::SetAwakeCallback(NULL, 0); s_AwakeEvent.Finalize(); // Communicate with child device as the parent device if (s_IsDistributionDone) { DLP_DEBUG_POINT; // Because this is a provisional implementation, the SLEEP, HOME, and POWER Buttons do not work after this DoParent(s_ClientNum, s_Passphraase); } nn::applet::CloseApplication(); // Execution does not come here DLP_DEBUG_PRINT("EXIT.\n"); }