/*---------------------------------------------------------------------------* Project: Horizon File: Controller.cpp Copyright 2009 Nintendo. 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$ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include "Controller.h" #include "ConsoleRestore.h" #include "SimplePlayer.h" #include "CommonLogger.h" #include "Importer.h" #include "Updater.h" #include "NtpClient.h" namespace ConsoleRestore { namespace { typedef enum RestoreState { STARTUP, // 初期値 SERIAL_IS_NOT_IN_SD, // シリアルナンバーファイルがSDカードにないことを表示 SERIAL_IN_SD, // シリアルナンバーの情報を表示 UPDATE_IN_PROGRESS, // アップデート中 RESTORE_IN_PROGRESS, // 書き込み中 POST_RESTORE, // 書き込み後の処理 RESTORE_DONE, // 書き込み完了 REBOOTING, // 再起動を行う ERASE, // 削除処理を行う TIME_ADJUST, // 時計あわせを行う WAIT_SD_EJECT, // SDカードぬき待ち ALL_DONE, // すべて完了 FAIL // 失敗 } RestoreState; // Restore状態管理 RestoreState s_RestoreState = STARTUP; // インターネット設定を読んだかどうか bool s_ReadSettingDone = false; // インターネット設定を読んだ結果 bool s_ReadSettingSuccess = false; // APSettingの書式が無い警告サウンドを鳴らしたかどうか bool s_ExistAPSettingAnnotation = false; // APSettingの書式が違っている警告サウンドを鳴らしたかどうか bool s_APSettingAnnotation = false; // シリアルナンバーがない警告サウンドを鳴らしたかどうか bool s_SerialNumberAnnotation = false; // 失敗サウンドを鳴らしたかどうか bool s_PlayedFailSound = false; // ネットワークアップデートを開始したかどうか bool s_ExecuteFgNup = false; // FGNUPを何回リトライしたか u32 s_FgNupRetryCount = 0; } // namespace bool NeedsAcAdater() { return IsBatteryLower() && !IsAdapterConnected(); } bool CheckAndReadAPSetting(::std::vector& operationMessage) { if (!ExistsAPSetting()) { if(!s_ExistAPSettingAnnotation) { s_ExistAPSettingAnnotation = true; common::PlaySound(common::SOUND_ANNOTATION); } operationMessage.push_back(::std::string("APSetting.txt does not exist!")); return false; } // 設定ファイルからAP設定を読み込む if (!s_ReadSettingDone) { s_ReadSettingDone = true; s_ReadSettingSuccess = ReadSetting(); } if (!s_ReadSettingSuccess) { operationMessage.push_back(::std::string("Invalid APSetting.txt format!")); if(!s_APSettingAnnotation) { s_APSettingAnnotation = true; common::PlaySound(common::SOUND_ANNOTATION); } } return s_ReadSettingSuccess; } void PutAliveMessage(::std::vector& operationMessage, const char* str) { std::string message = std::string(str); static u8 i = 0; if (i < 0xff / 4) { operationMessage.push_back(message + std::string(" /")); } else if (i < 0xff * 2 / 4) { operationMessage.push_back(message + std::string(" |")); } else if (i < 0xff * 3 / 4) { operationMessage.push_back(message + std::string(" \\")); } else { operationMessage.push_back(message + std::string(" -")); } i += 4; } void ExecSyncMcuRtc() { if(!ExistsRtcSyncFinishedFile()) { ImportMcuRtc(); // 時計を無効化する nn::ptm::CTR::InvalidateSystemTime(); CreateRtcSyncFinishedFile(); } } void ControlState(::std::vector& operationMessage, bool& nextStep, bool& continueRestore) { // 状態遷移Controller switch (s_RestoreState) { // 起動時 case STARTUP: { bool error = false; bool needsUpdate = false; bool needsErase = false; // SDカードが挿入されているか? if (nn::fs::IsSdmcInserted()) { // SDカードにアップデート完了ファイルがあるか? if (ExistsUpdateCheckedFile()) { // SDカードに書き込み完了ファイルがあるか? if (!ExistsWriteFinishedFile()) { // IVSを読めるか? if (CanReadIVS()) { // 本体初期化完了ファイルがあるか? if (ExistsConsoleInitializedFile()) { // SDカードにシリアルナンバーがあるか? if (!ExistsSerialNumberFile()) { COMMON_LOGGER("Can't Read Serial Number in SD Card!!\n"); error = true; s_RestoreState = SERIAL_IS_NOT_IN_SD; } else { ::std::string serial(reinterpret_cast (ReadSerialNumber())); operationMessage.push_back(::std::string("Serial Number in SD : ") + serial); } // SDカードにIVSがあるか? if (!ExistsIVSFile()) { // 移行不能なのでFAIL COMMON_LOGGER("Can't Read IVS in SD Card!!\n"); error = true; s_RestoreState = FAIL; } else { // SDカードのIVSと本体のIVSは異なるか? if (EqualsIVSFileandIVS()) { COMMON_LOGGER("Restore data to the same Console. Initialize.\n"); // 本体初期化を行う InitializeFileSystem(); error = true; s_RestoreState = REBOOTING; } // SDカードに書き込みできるか? if (nextStep && !nn::fs::IsSdmcWritable()) { error = true; common::PlaySound(common::SOUND_ANNOTATION); COMMON_LOGGER("Can't Write SD Card!!\n"); } } } else { COMMON_LOGGER("Initialize Console\n"); // 本体初期化完了ファイルを作る CreateConsoleInitializedFile(); // 本体初期化を行う InitializeFileSystem(); error = true; s_RestoreState = REBOOTING; } } else { error = true; operationMessage.push_back(::std::string("Can't Read IVS!!")); } } else { if(CheckAndReadAPSetting(operationMessage)) { // 削除処理を行う needsErase = true; } else { error = true; } } } else { if (CheckAndReadAPSetting(operationMessage)) { // ネットワークアップデートを行う needsUpdate = true; } else { error = true; } } } else { error = true; operationMessage.push_back(::std::string("Insert SD Card!!")); } // ACアダプタが必要か? if (NeedsAcAdater()) { error = true; operationMessage.push_back(::std::string("Connect AC Adapter!!")); } // エラーが無ければ進行用メッセージ表示 if (!error) { operationMessage.push_back(::std::string("Push A or START Button")); if(needsUpdate) { operationMessage.push_back(::std::string("Network Update Mode")); } else if(needsErase) { operationMessage.push_back(::std::string("Clock Sync Mode")); } else { operationMessage.push_back(::std::string("Import Data Mode")); } } if (nextStep && !error) { if (needsUpdate) { COMMON_LOGGER("Start Network Update\n"); s_RestoreState = UPDATE_IN_PROGRESS; common::PlaySound(common::SOUND_CURSOR); } else if(needsErase) { COMMON_LOGGER("Erase Trash\n"); s_RestoreState = ERASE; common::PlaySound(common::SOUND_CURSOR); } else { COMMON_LOGGER("Start Import Data\n"); s_RestoreState = RESTORE_IN_PROGRESS; common::PlaySound(common::SOUND_CURSOR); } } } break; // シリアルナンバーがSDカードにないこと警告 case SERIAL_IS_NOT_IN_SD: { operationMessage.push_back(::std::string("Serial Number Is Not In SD Card")); operationMessage.push_back(::std::string("Push A or START Button")); operationMessage.push_back(::std::string("Import Data Mode")); if (!s_SerialNumberAnnotation) { s_SerialNumberAnnotation = true; common::PlaySound(common::SOUND_ANNOTATION); } if (nextStep) { s_RestoreState = RESTORE_IN_PROGRESS; } } break; // アップデート中 case UPDATE_IN_PROGRESS: { continueRestore = true; // ACアダプタが必要か? if (NeedsAcAdater()) { continueRestore = false; operationMessage.push_back(::std::string("Connect AC Adapter!!")); } // アップデートを行う if(!s_ExecuteFgNup) { ImportCountryLanguageData(); StartFGNetworkUpdate(); s_ExecuteFgNup = true; } // 動いていることを表示 { PutAliveMessage(operationMessage, "Updating"); } if (IsNetworkUpdateFinished()) { FinishFGNetworkUpdate(); if (GetUpdateResult().IsSuccess()) { COMMON_LOGGER("Network Update Finished.\n"); // アップデート完了ファイルを作成 CreateUpdateFinishedFile(); // リブートする s_RestoreState = REBOOTING; } else { if (s_FgNupRetryCount++ < RETRY_MAX) { // エラーのためやり直す COMMON_LOGGER_RESULT(GetUpdateResult()); COMMON_LOGGER("Network Update Failed. Retrying... %d\n", s_FgNupRetryCount); // FGNUP用のスレッドを作るとこからやり直し s_ExecuteFgNup = false; } else { s_RestoreState = FAIL; } } } } break; // 書き込み中 case RESTORE_IN_PROGRESS: { continueRestore = true; // ACアダプタが必要か? if (NeedsAcAdater()) { continueRestore = false; operationMessage.push_back(::std::string("Connect AC Adapter!!")); } // データを読み込む ImportData(); // 処理が完了した if (continueRestore && IsImportFinished()) { COMMON_LOGGER("Import NAND Data Finished.\n"); if (GetProgress() > 99) { s_RestoreState = POST_RESTORE; } else { s_RestoreState = FAIL; } } } break; // リブート中 case REBOOTING: { static bool init = true; if (init) { // ErrDispから引用 // 権限をもらえば成功するはず nn::Result result = nn::ns::CTR::InitializeForShell(); if (result.IsSuccess()) { COMMON_LOGGER("System Reboot.\n"); nn::ns::CTR::HardwareResetAsync(nn::CTR::MEMORY_ARRANGE_NORMAL); while (!nn::applet::IsExpectedToCloseApplication()) { nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(5)); } nn::ns::CTR::FinalizeForShell(); // INFO: リブートは非同期のため処理は継続 } init = false; } } break; // 書き込み後の処理 case POST_RESTORE: { operationMessage.push_back(::std::string("Post Process...")); // 書き込み完了ファイルを作成 CreateWriteFinishedFile(); s_RestoreState = RESTORE_DONE; } break; // 書き込み完了 case RESTORE_DONE: { operationMessage.push_back(::std::string("Restore Done.")); operationMessage.push_back(::std::string("Press A or START Button to Reboot")); if (nextStep) { // RTC同期を行う // RTC書き込み後できるだけ早いタイミングで再起動したいのでここで同期 ExecSyncMcuRtc(); s_RestoreState = REBOOTING; } } break; // 削除処理 case ERASE: { Cleanup(); s_RestoreState = TIME_ADJUST; } break; // 時計あわせ case TIME_ADJUST: { static bool init = true; if(init) { COMMON_LOGGER("Adjust Time\n"); AdjustTime(); init = false; } // 動いていることを表示 { PutAliveMessage(operationMessage, "Sync Clock"); } if(IsTimeAdjustFinished()) { if(IsTimeAdjustSuccessed()) { s_RestoreState = WAIT_SD_EJECT; } else { s_RestoreState = FAIL; } } } break; // すべて完了 case WAIT_SD_EJECT: { operationMessage.push_back(::std::string("ALL Done. Pull Out SD Card.")); // SDカード抜けのみで次の状態に遷移する } break; // すべて完了 case ALL_DONE: { operationMessage.push_back(::std::string("Restore Succeeded!!")); static bool init = true; if (init) { common::PlaySound(common::SOUND_OK); init = false; } } break; // 書き込み失敗 case FAIL: { operationMessage.push_back(::std::string("Failed.")); if (!s_PlayedFailSound) { common::PlaySound(common::SOUND_NG); s_PlayedFailSound = true; } } break; } } bool InProgress() { return s_RestoreState == RESTORE_IN_PROGRESS; } bool IsRestoreSucceeded() { return s_RestoreState == ALL_DONE; } bool IsRestoreFailed() { return s_RestoreState == FAIL; } void OnSdEjected() { if(s_RestoreState == WAIT_SD_EJECT || s_RestoreState == ALL_DONE) { s_RestoreState = ALL_DONE; } else { InitializeState(); } } void InitializeState() { s_RestoreState = STARTUP; InitializeFileCheck(); s_ExistAPSettingAnnotation = false; s_ReadSettingDone = false; s_ReadSettingSuccess = false; s_APSettingAnnotation = false; s_SerialNumberAnnotation = false; s_PlayedFailSound = false; s_ExecuteFgNup = false; s_FgNupRetryCount = 0; } u32 GetProgress() { if(s_RestoreState == RESTORE_IN_PROGRESS || s_RestoreState == POST_RESTORE || s_RestoreState == RESTORE_DONE) { return GetImportProgress(); } else if(s_RestoreState == UPDATE_IN_PROGRESS) { return GetUpdateProgress(); } else { return 0; } } } // namespace ConsoleRestore