/*---------------------------------------------------------------------------* Project: Horizon File: FsSampleStreamingFrame.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: 46547 $ *---------------------------------------------------------------------------*/ #include #include "demo.h" #include "../Common/FsSampleCommon.h" #include "FsSampleStreaming.h" using namespace nn; namespace sample { namespace fs { namespace streaming { enum ENUM_TASK { BG_TASK_NONE, BG_TASK_SAVE, TASK_REVERT, BG_TASK_LOAD, ENUM_TASK_NUM } s_BgTask; enum ENUM_REC_STATE { REC_STATE_NONE, REC_STATE_RUNNING, REC_STATE_SUCCEEDED, REC_STATE_FAILED, ENUM_REC_STATE_NUM } s_RecState; enum ENUM_PLAY_STATE { PLAY_STATE_NONE, PLAY_STATE_RUNNING, PLAY_STATE_SUCCEEDED, PLAY_STATE_STOPPED, PLAY_STATE_FAILED, ENUM_PLAY_STATE_NUM } s_PlayState; enum ENUM_BG_STATE { STATE_BG_NONE, STATE_BG_SAVE, STATE_BG_SAVE_SUCCEEDED, STATE_BG_SAVE_FAILED, STATE_BG_LOAD, STATE_BG_LOAD_SUCCEEDED, STATE_BG_LOAD_FAILED, ENUM_BG_STATE_NUM } s_BgState; bool s_IsWorkerThreadAlive; nn::os::Event s_WorkerEvent; nn::os::Thread s_WorkerThread; int s_RecordedFrameNum = 0; int s_DroppedFrameNum = 0; int s_BgSaveData = 0; int s_LoadFileNum; int s_LoadFileIndex; bit8 s_MemoBuffer[MEMO_BUFFER_SIZE]; static nn::hid::CTR::TouchPanelStatus s_TouchPanelStatus; static nn::hid::CTR::TouchPanelStatus s_PreviousTouchPanel; GLuint s_TextureId = 0; void DrawPoint(const u16 orgX, const u16 orgY) { if (MEMO_X <= orgX && orgX < MEMO_X + MEMO_WIDTH && MEMO_Y <= orgY && orgY < MEMO_Y + MEMO_HEIGHT) { // When mapping the memo content to a texture, you must convert from a linear format to a block format. // With this sample, this conversion process is performed in advance when there is touch input, and the converted data is used internally. u16 x = orgX - MEMO_X; u16 y = orgY - MEMO_Y; u32 id = y / TEXTURE_BLOCK_SIZE * MEMO_WIDTH * TEXTURE_BLOCK_SIZE + x / TEXTURE_BLOCK_SIZE * TEXTURE_BLOCK_SIZE * TEXTURE_BLOCK_SIZE + (((x >> 0) & 1) << 0) + (((x >> 1) & 1) << 2) + (((x >> 2) & 1) << 4) + (((y >> 0) & 1) << 1) + (((y >> 1) & 1) << 3) + (((y >> 2) & 1) << 5); s_MemoBuffer[id / 8] |= 1 << (id % 8); } } void DrawLine(const nn::hid::CTR::TouchPanelStatus &now, const nn::hid::CTR::TouchPanelStatus &previous) { if (previous.touch) { s32 diffX = now.x - previous.x; s32 diffY = now.y - previous.y; s32 absDiffX = nn::math::Abs(diffX); s32 absDiffY = nn::math::Abs(diffY); if (diffX == 0 && diffY == 0) { DrawPoint(now.x, now.y); } else if (absDiffX < absDiffY) { for (int i = 0; i < absDiffY; ++i) { DrawPoint(previous.x + i * diffX / absDiffY, previous.y + i * diffY / absDiffY); } } else { for (int i = 0; i < absDiffX; ++i) { DrawPoint(previous.x + i * diffX / absDiffX, previous.y + i * diffY / absDiffX); } } } } void UpdateTexture(demo::RenderSystemDrawing &renderSystem, const bit8 *src) { const int PIXEL_NUM_PER_BLOCK = TEXTURE_BLOCK_SIZE * TEXTURE_BLOCK_SIZE; const int HORIZONTAL_BLOCK_NUM = MEMO_WIDTH / TEXTURE_BLOCK_SIZE; const int VERTICAL_BLOCK_NUM = MEMO_HEIGHT / TEXTURE_BLOCK_SIZE; u8* textureDataBuffer = new bit8[4 * MEMO_TEXTURE_WIDTH * MEMO_TEXTURE_HEIGHT]; bit8 data; for (int j = 0; j < VERTICAL_BLOCK_NUM; ++j) { u32* a = (u32*)(textureDataBuffer + (j + (MEMO_TEXTURE_HEIGHT - MEMO_HEIGHT) / TEXTURE_BLOCK_SIZE) * 4 * MEMO_TEXTURE_WIDTH / TEXTURE_BLOCK_SIZE * PIXEL_NUM_PER_BLOCK); for (int i = 0; i < HORIZONTAL_BLOCK_NUM; ++i) { for (int k = 0; k < PIXEL_NUM_PER_BLOCK; ++k) { if (k % 8 == 0) { data = *src; src++; } *a = (data & 1) ? 0 : 0xFFFFFFFF; a++; data >>= 1; } } } if (s_TextureId != 0) { NN_PANIC_IF_FALSE(renderSystem.DeleteTexture(s_TextureId)); } GLenum format = GL_RGBA_NATIVE_DMP; GLenum target = GL_TEXTURE_2D; GLenum internalFormat = GL_RGBA_NATIVE_DMP; GLenum type = GL_UNSIGNED_BYTE; GLuint textureId = 0; renderSystem.GenerateTexture(target, internalFormat, MEMO_TEXTURE_WIDTH, MEMO_TEXTURE_HEIGHT, format, type, textureDataBuffer, textureId); delete[] textureDataBuffer; if ( textureId != 0 ) { s_TextureId = textureId; } } void DrawMemo(demo::RenderSystemDrawing &renderSystem, const bit8 *src) { UpdateTexture(renderSystem, src); renderSystem.FillTexturedRectangle(s_TextureId, MEMO_X, MEMO_Y, MEMO_WIDTH, MEMO_HEIGHT, MEMO_WIDTH, MEMO_HEIGHT, 512, 256); } void ProceedDisplay0(demo::RenderSystemDrawing &renderSystem) { renderSystem.SetRenderTarget(NN_GX_DISPLAY0); renderSystem.SetColor(1.0f, 1.0f, 1.0f); renderSystem.Clear(); switch (s_RecState) { case REC_STATE_RUNNING: renderSystem.DrawText(0, LINE_HEIGHT * 1, "Recording to %d", s_LoadFileNum); break; case REC_STATE_SUCCEEDED: renderSystem.DrawText(0, LINE_HEIGHT * 1, "Recording Succeeded"); break; case REC_STATE_FAILED: renderSystem.DrawText(0, LINE_HEIGHT * 1, "Recording Failed"); break; default: break; } switch(s_PlayState) { case PLAY_STATE_RUNNING: renderSystem.DrawText(0, LINE_HEIGHT * 2, "Playing %d", s_LoadFileIndex); break; case PLAY_STATE_SUCCEEDED: renderSystem.DrawText(0, LINE_HEIGHT * 2, "Playing Succeeded"); break; case PLAY_STATE_STOPPED: renderSystem.DrawText(0, LINE_HEIGHT * 2, "Playing Stopped"); break; case PLAY_STATE_FAILED: renderSystem.DrawText(0, LINE_HEIGHT * 2, "Playing Failed"); break; default: break; } switch (s_BgState) { case STATE_BG_SAVE: renderSystem.DrawText(0, LINE_HEIGHT * 5, "Saving..."); break; case STATE_BG_SAVE_SUCCEEDED: renderSystem.DrawText(0, LINE_HEIGHT * 5, "Saving Succeeded."); break; case STATE_BG_SAVE_FAILED: renderSystem.DrawText(0, LINE_HEIGHT * 5, "Saving Failed."); break; case STATE_BG_LOAD: renderSystem.DrawText(0, LINE_HEIGHT * 5, "Loading..."); break; case STATE_BG_LOAD_SUCCEEDED: renderSystem.DrawText(0, LINE_HEIGHT * 5, "Loading Succeeded."); break; case STATE_BG_LOAD_FAILED: renderSystem.DrawText(0, LINE_HEIGHT * 5, "Loading Failed."); break; default: break; } renderSystem.DrawText(0, LINE_HEIGHT * 11, "A: %s Movie %d", (s_RecState == REC_STATE_RUNNING) ? "Stop" : "Record", s_LoadFileNum); if (s_LoadFileNum > 0) { renderSystem.DrawText(0, LINE_HEIGHT * 12, "B: %s Movie %d", (s_PlayState == PLAY_STATE_RUNNING) ? "Stop" : "Play", s_LoadFileIndex); renderSystem.DrawText(0, LINE_HEIGHT * 13, "Up & Down: Change movie No."); } renderSystem.DrawText(0, LINE_HEIGHT * 15, "L: Clear buffer"); renderSystem.DrawText(0, LINE_HEIGHT * 18, "data: %08X", s_BgSaveData); renderSystem.DrawText(0, LINE_HEIGHT * 20, "X: Save data in background"); renderSystem.DrawText(0, LINE_HEIGHT * 21, "Y: Load data in background"); renderSystem.DrawText(0, LINE_HEIGHT * 22, "Left & Right: Change data"); renderSystem.DrawText(100, LINE_HEIGHT * 25, "Recorded frame:%8d", s_RecordedFrameNum); renderSystem.DrawText(100, LINE_HEIGHT * 26, "Dropped frame: %8d", s_DroppedFrameNum); renderSystem.SwapBuffers(); } void ProceedDisplay1(demo::RenderSystemDrawing &renderSystem) { renderSystem.SetRenderTarget(NN_GX_DISPLAY1); renderSystem.Clear(); DrawMemo(renderSystem, s_MemoBuffer); renderSystem.SwapBuffers(); } void ProceedDisplay(demo::RenderSystemDrawing &renderSystem) { ProceedDisplay0(renderSystem); ProceedDisplay1(renderSystem); } void CallStartRecording() { wchar_t fileName[256]; nstd::TSNPrintf(fileName, sizeof(fileName) / sizeof(fileName[0]), L"%ls%04d.dat",MEMO_FILE_NAME, s_LoadFileNum); Result result = StartRecording(fileName); if (result.IsSuccess()) { s_RecordedFrameNum = 0; s_DroppedFrameNum = 0; s_RecState = REC_STATE_RUNNING; } else { NN_DBG_PRINT_RESULT(result); s_RecState = REC_STATE_FAILED; } } void CallStopRecording() { Result result = StopRecording(); if (result.IsSuccess()) { s_LoadFileNum++; s_RecState = REC_STATE_SUCCEEDED; } else { NN_DBG_PRINT_RESULT(result); s_RecState = REC_STATE_FAILED; } } void CallStartPlaying() { wchar_t fileName[256]; nstd::TSNPrintf(fileName, sizeof(fileName) / sizeof(fileName[0]), L"%ls%04d.dat",MEMO_FILE_NAME, s_LoadFileIndex); Result result = StartPlaying(fileName); if (result.IsSuccess()) { s_PlayState = PLAY_STATE_RUNNING; } else { NN_DBG_PRINT_RESULT(result); s_PlayState = PLAY_STATE_FAILED; } } void CallStopPlaying() { Result result = StopPlaying(); if (result.IsSuccess()) { s_PlayState = PLAY_STATE_STOPPED; } else { NN_DBG_PRINT_RESULT(result); s_PlayState = PLAY_STATE_FAILED; } } void ProceedMainTask() { if (s_PlayState == PLAY_STATE_RUNNING) { PacketData *packet; if (!LoadMemo(&packet)) { // Does nothing when there is no loaded data. } else if (packet) { std::memcpy(s_MemoBuffer, packet->GetData(), packet->GetSize()); packet->Free(); } else { NN_ERR_THROW_FATAL_ALL(FinishPlaying()); s_PlayState = PLAY_STATE_SUCCEEDED; } } if (s_TouchPanelStatus.touch) { DrawLine(s_TouchPanelStatus, s_PreviousTouchPanel); } if (s_RecState == REC_STATE_RUNNING) { ENUM_RECORDING_RESULT result = SaveMemo(s_MemoBuffer); if (result == RECORDING_RESULT_BUFFER_FULL) { s_DroppedFrameNum++; } else if (result == RECORDING_RESULT_FILE_FULL) { CallStopRecording(); } else { s_RecordedFrameNum++; } } } void WorkerThread() { while (s_IsWorkerThreadAlive) { s_WorkerEvent.Wait(); switch (s_BgTask) { case BG_TASK_SAVE: s_BgState = STATE_BG_SAVE; s_BgState = (BgSave(s_BgSaveData).IsSuccess()) ? STATE_BG_SAVE_SUCCEEDED : STATE_BG_SAVE_FAILED; break; case BG_TASK_LOAD: s_BgState = STATE_BG_LOAD; s_BgState = (BgLoad(s_BgSaveData).IsSuccess()) ? STATE_BG_LOAD_SUCCEEDED : STATE_BG_LOAD_FAILED; break; default: s_BgState = STATE_BG_NONE; break; } s_BgTask = BG_TASK_NONE; } } void ProceedHid() { static nn::hid::CTR::PadReader padReader; static nn::hid::CTR::PadStatus padStatus; static nn::hid::CTR::TouchPanelReader touchPanelReader; if (!padReader.ReadLatest(&padStatus)) { nn::dbg::detail::TPrintf("Getting pad status failed.\n"); } if (padStatus.trigger & nn::hid::BUTTON_A) { if (s_RecState != REC_STATE_RUNNING) { CallStartRecording(); } else if (s_RecState == REC_STATE_RUNNING) { CallStopRecording(); } } else if (padStatus.trigger & nn::hid::BUTTON_B) { if (s_PlayState != PLAY_STATE_RUNNING) { CallStartPlaying(); } else if (s_PlayState == PLAY_STATE_RUNNING) { CallStopPlaying(); } } else if ((padStatus.trigger & nn::hid::BUTTON_X) && s_BgTask == BG_TASK_NONE) { s_BgTask = BG_TASK_SAVE; s_WorkerEvent.Signal(); } else if ((padStatus.trigger & nn::hid::BUTTON_Y) && s_BgTask == BG_TASK_NONE) { s_BgTask = BG_TASK_LOAD; s_WorkerEvent.Signal(); } else if (padStatus.trigger & nn::hid::BUTTON_L) { std::memset(s_MemoBuffer, 0, MEMO_BUFFER_SIZE); } else if (padStatus.trigger & nn::hid::BUTTON_LEFT) { s_BgSaveData--; } else if (padStatus.trigger & nn::hid::BUTTON_RIGHT) { s_BgSaveData++; } else if (padStatus.trigger & nn::hid::BUTTON_UP && s_LoadFileNum > 0 && s_PlayState != PLAY_STATE_RUNNING) { s_LoadFileIndex = (s_LoadFileIndex + 1) % s_LoadFileNum; } else if (padStatus.trigger & nn::hid::BUTTON_DOWN && s_LoadFileNum > 0 && s_PlayState != PLAY_STATE_RUNNING) { s_LoadFileIndex = (s_LoadFileIndex - 1 + s_LoadFileNum) % s_LoadFileNum; } s_PreviousTouchPanel = s_TouchPanelStatus; if (!touchPanelReader.ReadLatest(&s_TouchPanelStatus)) { nn::dbg::detail::TPrintf("Getting touch panel status failed.\n"); } } // Initializes the save data. void FormatSaveData() { const size_t maxFiles = 8; const size_t maxDirectories = 8; const bool isDuplicateAll = true; // Format the save data region Result result = nn::fs::FormatSaveData(maxFiles, maxDirectories, isDuplicateAll); if (result.IsFailure()) { NN_LOG("FormatSaveData() failed.\n"); NN_ERR_THROW_FATAL_ALL(result); } // Mount in order to compose the data default state result = nn::fs::MountSaveData(SAVE_DATA_ARCHIVE); if (result.IsFailure()) { NN_LOG("MountSaveData() failed : Cannot use save data!\n"); NN_ERR_THROW_FATAL_ALL(result); } // Create each file nn::fs::CreateFile(DATA_FILE_NAME, sizeof(SaveData)); // Commit NN_ERR_THROW_FATAL_ALL(nn::fs::CommitSaveData(SAVE_DATA_ARCHIVE)); // Unmount nn::fs::Unmount(SAVE_DATA_ARCHIVE); } // Save data check when starting the application void InitializeSaveData() { Result result = nn::fs::MountSaveData(SAVE_DATA_ARCHIVE); if (result.IsFailure()) { NN_DBG_PRINT_RESULT(result); if ((result <= nn::fs::ResultNotFormatted()) || (result <= nn::fs::ResultBadFormat()) || (result <= nn::fs::ResultVerificationFailed())) { // Save data needs to be initialized FormatSaveData(); } else { // Unexpected errors NN_ERR_THROW_FATAL_ALL(result); } } else { nn::fs::Unmount(SAVE_DATA_ARCHIVE); } } // Expanded save data check when starting the application void InitializeExtSaveData() { Result result = nn::fs::MountExtSaveData(EXT_ARCHIVE, EXT_SAVEDATA_ID); if (result.IsFailure()) { NN_DBG_PRINT_RESULT(result); if ((result <= nn::fs::ResultNotFound()) || (result <= nn::fs::ResultNotFormatted()) || (result <= nn::fs::ResultBadFormat()) || (result <= nn::fs::ResultVerificationFailed())) { nn::fs::DeleteExtSaveData(EXT_SAVEDATA_ID); bit8 iconData[64]; NN_ERR_THROW_FATAL_ALL(nn::fs::CreateExtSaveData(EXT_SAVEDATA_ID, iconData, sizeof(iconData), 0, MEMO_FILE_NUM_MAX)); NN_ERR_THROW_FATAL_ALL(nn::fs::MountExtSaveData(EXT_ARCHIVE, EXT_SAVEDATA_ID)); } else { // Unexpected errors NN_ERR_THROW_FATAL_ALL(result); } } } } // namespace streaming using namespace streaming; void MainLoop(demo::RenderSystemDrawing &renderSystem) { ProceedHid(); ProceedMainTask(); ProceedDisplay(renderSystem); nngxWaitVSync(NN_GX_DISPLAY_BOTH); } void Initialize(demo::RenderSystemDrawing &renderSystem) { nn::fs::Initialize(); InitializeSaveData(); InitializeExtSaveData(); nn::fs::SetAnalysisLog(true); renderSystem.SetClearColor(NN_GX_DISPLAY1, 0.5f, 0.5f, 0.5f, 0.0f); s_LoadFileNum = 0; s_LoadFileIndex = 0; while (true) { wchar_t fileName[256]; nstd::TSNPrintf(fileName, sizeof(fileName) / sizeof(fileName[0]), L"%ls%04d.dat",MEMO_FILE_NAME, s_LoadFileNum); nn::fs::FileInputStream file; Result result = file.TryInitialize(fileName); if (result.IsFailure()) { break; } s_LoadFileNum++; } s_WorkerEvent.Initialize(false); s_IsWorkerThreadAlive = true; NN_ERR_THROW_FATAL_ALL(s_WorkerThread.TryStartUsingAutoStack(&WorkerThread, 4 * 1024, nn::os::Thread::GetCurrentPriority() + 1)); } void Finalize() { s_IsWorkerThreadAlive = false; s_WorkerEvent.Signal(); s_WorkerThread.Join(); } }} // namespace sample::fs