mirror of
https://github.com/BernardoGiordano/Checkpoint
synced 2024-11-14 21:57:13 +00:00
Merge pull request #210 from FlagBrew/Screenify-3ds
Screenify the 3ds version
This commit is contained in:
commit
4d0c67a29d
29 changed files with 1362 additions and 933 deletions
68
3ds/include/CheatManagerOverlay.hpp
Normal file
68
3ds/include/CheatManagerOverlay.hpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHEATMANAGEROVERLAY_HPP
|
||||||
|
#define CHEATMANAGEROVERLAY_HPP
|
||||||
|
|
||||||
|
#include "Overlay.hpp"
|
||||||
|
#include "YesNoOverlay.hpp"
|
||||||
|
#include "cheatmanager.hpp"
|
||||||
|
#include "clickable.hpp"
|
||||||
|
#include "colors.hpp"
|
||||||
|
#include "directory.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "scrollable.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Clickable;
|
||||||
|
class Scrollable;
|
||||||
|
|
||||||
|
class CheatManagerOverlay : public Overlay {
|
||||||
|
public:
|
||||||
|
CheatManagerOverlay(Screen& screen, const std::string& mtext);
|
||||||
|
~CheatManagerOverlay(void);
|
||||||
|
void drawTop(void) const override;
|
||||||
|
void drawBottom(void) const override;
|
||||||
|
void update(touchPosition* touch) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void save(const std::string& key, Scrollable* s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool multiSelected;
|
||||||
|
std::string existingCheat;
|
||||||
|
std::string key;
|
||||||
|
const size_t MAGIC_LEN = strlen(SELECTED_MAGIC);
|
||||||
|
std::shared_ptr<Scrollable> scrollable;
|
||||||
|
size_t currentIndex;
|
||||||
|
const float scale = 0.47f;
|
||||||
|
|
||||||
|
C2D_Text multiSelectText, multiDeselectText;
|
||||||
|
C2D_TextBuf staticBuf, dynamicBuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
55
3ds/include/ErrorOverlay.hpp
Normal file
55
3ds/include/ErrorOverlay.hpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ERROROVERLAY_HPP
|
||||||
|
#define ERROROVERLAY_HPP
|
||||||
|
|
||||||
|
#include "Overlay.hpp"
|
||||||
|
#include "clickable.hpp"
|
||||||
|
#include "colors.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Clickable;
|
||||||
|
|
||||||
|
class ErrorOverlay : public Overlay {
|
||||||
|
public:
|
||||||
|
ErrorOverlay(Screen& screen, Result res, const std::string& mtext);
|
||||||
|
~ErrorOverlay(void);
|
||||||
|
void drawTop(void) const override;
|
||||||
|
void drawBottom(void) const override;
|
||||||
|
void update(touchPosition* touch) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32 posx, posy;
|
||||||
|
const float size = 0.6f;
|
||||||
|
C2D_Text text, error;
|
||||||
|
C2D_TextBuf textBuf;
|
||||||
|
std::unique_ptr<Clickable> button;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
55
3ds/include/InfoOverlay.hpp
Normal file
55
3ds/include/InfoOverlay.hpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INFOOVERLAY_HPP
|
||||||
|
#define INFOOVERLAY_HPP
|
||||||
|
|
||||||
|
#include "Overlay.hpp"
|
||||||
|
#include "clickable.hpp"
|
||||||
|
#include "colors.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Clickable;
|
||||||
|
|
||||||
|
class InfoOverlay : public Overlay {
|
||||||
|
public:
|
||||||
|
InfoOverlay(Screen& screen, const std::string& mtext);
|
||||||
|
~InfoOverlay(void);
|
||||||
|
void drawTop(void) const override;
|
||||||
|
void drawBottom(void) const override;
|
||||||
|
void update(touchPosition* touch) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32 posx, posy;
|
||||||
|
const float size = 0.6f;
|
||||||
|
C2D_Text text;
|
||||||
|
C2D_TextBuf textBuf;
|
||||||
|
std::unique_ptr<Clickable> button;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
78
3ds/include/MainScreen.hpp
Normal file
78
3ds/include/MainScreen.hpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAINSCREEN_HPP
|
||||||
|
#define MAINSCREEN_HPP
|
||||||
|
|
||||||
|
#include "CheatManagerOverlay.hpp"
|
||||||
|
#include "ErrorOverlay.hpp"
|
||||||
|
#include "InfoOverlay.hpp"
|
||||||
|
#include "Screen.hpp"
|
||||||
|
#include "YesNoOverlay.hpp"
|
||||||
|
#include "clickable.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "multiselection.hpp"
|
||||||
|
#include "scrollable.hpp"
|
||||||
|
#include "thread.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
class MainScreen : public Screen {
|
||||||
|
public:
|
||||||
|
MainScreen(void);
|
||||||
|
~MainScreen(void);
|
||||||
|
void drawTop(void) const override;
|
||||||
|
void drawBottom(void) const override;
|
||||||
|
void update(touchPosition* touch) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int selectorX(size_t i) const;
|
||||||
|
int selectorY(size_t i) const;
|
||||||
|
void drawSelector(void) const;
|
||||||
|
void handleEvents(touchPosition* touch);
|
||||||
|
void updateSelector(void);
|
||||||
|
void updateButtons(void);
|
||||||
|
std::string nameFromCell(size_t index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
HidHorizontal hid;
|
||||||
|
std::unique_ptr<Clickable> buttonBackup, buttonRestore, buttonCheats, buttonPlayCoins;
|
||||||
|
std::unique_ptr<Scrollable> directoryList;
|
||||||
|
char ver[10];
|
||||||
|
|
||||||
|
C2D_Text ins1, ins2, ins3, ins4, c2dId, c2dMediatype;
|
||||||
|
C2D_Text checkpoint, version;
|
||||||
|
// instructions text
|
||||||
|
C2D_Text top_move, top_a, top_y, top_my, top_b, bot_ts, bot_x;
|
||||||
|
C2D_TextBuf dynamicBuf, staticBuf;
|
||||||
|
|
||||||
|
const float scaleInst = 0.7f;
|
||||||
|
C2D_ImageTint checkboxTint;
|
||||||
|
int selectionTimer;
|
||||||
|
int refreshTimer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
58
3ds/include/YesNoOverlay.hpp
Normal file
58
3ds/include/YesNoOverlay.hpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef YESNOOVERLAY_HPP
|
||||||
|
#define YESNOOVERLAY_HPP
|
||||||
|
|
||||||
|
#include "Overlay.hpp"
|
||||||
|
#include "clickable.hpp"
|
||||||
|
#include "colors.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
|
#include "hid.hpp"
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Clickable;
|
||||||
|
|
||||||
|
class YesNoOverlay : public Overlay {
|
||||||
|
public:
|
||||||
|
YesNoOverlay(Screen& screen, const std::string& mtext, const std::function<void()>& callbackYes, const std::function<void()>& callbackNo);
|
||||||
|
~YesNoOverlay(void);
|
||||||
|
void drawTop(void) const override;
|
||||||
|
void drawBottom(void) const override;
|
||||||
|
void update(touchPosition* touch) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32 posx, posy;
|
||||||
|
C2D_TextBuf textBuf;
|
||||||
|
C2D_Text text;
|
||||||
|
std::unique_ptr<Clickable> buttonYes, buttonNo;
|
||||||
|
HidHorizontal hid;
|
||||||
|
std::function<void()> yesFunc, noFunc;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -27,6 +27,7 @@
|
||||||
#ifndef ARCHIVE_HPP
|
#ifndef ARCHIVE_HPP
|
||||||
#define ARCHIVE_HPP
|
#define ARCHIVE_HPP
|
||||||
|
|
||||||
|
#include "KeyboardManager.hpp"
|
||||||
#include "fsstream.hpp"
|
#include "fsstream.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
|
|
|
@ -30,20 +30,34 @@
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
#include "scrollable.hpp"
|
|
||||||
#include "thread.hpp"
|
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include <bzlib.h>
|
#include <bzlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
namespace CheatManager {
|
#define SELECTED_MAGIC "\uE071 "
|
||||||
void init(void);
|
|
||||||
void exit(void);
|
class CheatManager {
|
||||||
bool loaded(void);
|
public:
|
||||||
bool availableCodes(const std::string& key);
|
static CheatManager& getInstance(void)
|
||||||
void manageCheats(const std::string& key);
|
{
|
||||||
void save(const std::string& key, Scrollable* s);
|
static CheatManager mCheatManager;
|
||||||
}
|
return mCheatManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool areCheatsAvailable(const std::string& key);
|
||||||
|
void save(const std::string& key, const std::vector<std::string>& s);
|
||||||
|
|
||||||
|
std::shared_ptr<nlohmann::json> cheats(void) { return mCheats; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
CheatManager(void);
|
||||||
|
~CheatManager(void){};
|
||||||
|
|
||||||
|
CheatManager(CheatManager const&) = delete;
|
||||||
|
void operator=(CheatManager const&) = delete;
|
||||||
|
|
||||||
|
std::shared_ptr<nlohmann::json> mCheats;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <citro2d.h>
|
#include <citro2d.h>
|
||||||
|
|
||||||
|
inline const u32 COLOR_OVERLAY = C2D_Color32(0, 0, 0, 160);
|
||||||
|
|
||||||
#define COLOR_BG C2D_Color32(51, 51, 51, 255)
|
#define COLOR_BG C2D_Color32(51, 51, 51, 255)
|
||||||
#define COLOR_WHITE C2D_Color32(255, 255, 255, 255)
|
#define COLOR_WHITE C2D_Color32(255, 255, 255, 255)
|
||||||
#define COLOR_BLACK C2D_Color32(0, 0, 0, 255)
|
#define COLOR_BLACK C2D_Color32(0, 0, 0, 255)
|
||||||
|
|
|
@ -27,12 +27,8 @@
|
||||||
#ifndef GUI_HPP
|
#ifndef GUI_HPP
|
||||||
#define GUI_HPP
|
#define GUI_HPP
|
||||||
|
|
||||||
#include "archive.hpp"
|
|
||||||
#include "clickable.hpp"
|
|
||||||
#include "colors.hpp"
|
#include "colors.hpp"
|
||||||
#include "hid.hpp"
|
#include "main.hpp"
|
||||||
#include "multiselection.hpp"
|
|
||||||
#include "scrollable.hpp"
|
|
||||||
#include "sprites.h"
|
#include "sprites.h"
|
||||||
#include "title.hpp"
|
#include "title.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
@ -43,28 +39,17 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
inline C3D_RenderTarget* g_top;
|
||||||
|
inline C3D_RenderTarget* g_bottom;
|
||||||
|
|
||||||
|
inline C2D_SpriteSheet spritesheet;
|
||||||
|
inline C2D_Image flag;
|
||||||
|
inline C2D_Sprite checkbox, star;
|
||||||
|
|
||||||
namespace Gui {
|
namespace Gui {
|
||||||
void init(void);
|
void init(void);
|
||||||
void exit(void);
|
void exit(void);
|
||||||
|
|
||||||
bool askForConfirmation(const std::string& text);
|
|
||||||
void showError(Result res, const std::string& message);
|
|
||||||
void showInfo(const std::string& message);
|
|
||||||
void draw(void);
|
|
||||||
void drawCopy(const std::u16string& src, u32 offset, u32 size);
|
|
||||||
void frameEnd(void);
|
void frameEnd(void);
|
||||||
size_t index(void);
|
|
||||||
bool isBackupReleased(void);
|
|
||||||
bool isRestoreReleased(void);
|
|
||||||
bool isCheatReleased(void);
|
|
||||||
bool isPlayCoinsReleased(void);
|
|
||||||
std::string nameFromCell(size_t index);
|
|
||||||
void resetIndex(void);
|
|
||||||
void resetScrollableIndex(void);
|
|
||||||
size_t scrollableIndex(void);
|
|
||||||
void scrollableIndex(size_t index);
|
|
||||||
void updateButtons(void);
|
|
||||||
void updateSelector(void);
|
|
||||||
|
|
||||||
C2D_Image TWLIcon(void);
|
C2D_Image TWLIcon(void);
|
||||||
C2D_Image noIcon(void);
|
C2D_Image noIcon(void);
|
||||||
|
|
|
@ -30,15 +30,18 @@
|
||||||
#include "KeyboardManager.hpp"
|
#include "KeyboardManager.hpp"
|
||||||
#include "directory.hpp"
|
#include "directory.hpp"
|
||||||
#include "fsstream.hpp"
|
#include "fsstream.hpp"
|
||||||
#include "gui.hpp"
|
#include "multiselection.hpp"
|
||||||
|
#include "spi.hpp"
|
||||||
|
#include "title.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
#define BUFFER_SIZE 0x50000
|
#define BUFFER_SIZE 0x50000
|
||||||
|
|
||||||
namespace io {
|
namespace io {
|
||||||
void backup(size_t index);
|
std::tuple<bool, Result, std::string> backup(size_t index, size_t cellIndex);
|
||||||
void restore(size_t index);
|
std::tuple<bool, Result, std::string> restore(size_t index, size_t cellIndex, const std::string& nameFromCell);
|
||||||
|
|
||||||
Result copyDirectory(FS_Archive srcArch, FS_Archive dstArch, const std::u16string& srcPath, const std::u16string& dstPath);
|
Result copyDirectory(FS_Archive srcArch, FS_Archive dstArch, const std::u16string& srcPath, const std::u16string& dstPath);
|
||||||
void copyFile(FS_Archive srcArch, FS_Archive dstArch, const std::u16string& srcPath, const std::u16string& dstPath);
|
void copyFile(FS_Archive srcArch, FS_Archive dstArch, const std::u16string& srcPath, const std::u16string& dstPath);
|
||||||
|
|
|
@ -27,14 +27,16 @@
|
||||||
#ifndef MAIN_HPP
|
#ifndef MAIN_HPP
|
||||||
#define MAIN_HPP
|
#define MAIN_HPP
|
||||||
|
|
||||||
|
#include "Screen.hpp"
|
||||||
#include <citro2d.h>
|
#include <citro2d.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
extern bool g_bottomScrollEnabled;
|
inline std::shared_ptr<Screen> g_screen = nullptr;
|
||||||
extern C3D_RenderTarget* g_top;
|
inline bool g_bottomScrollEnabled = false;
|
||||||
extern C3D_RenderTarget* g_bottom;
|
inline float g_timer = 0;
|
||||||
extern C2D_TextBuf g_staticBuf;
|
inline std::string g_selectedCheatKey;
|
||||||
extern C2D_TextBuf g_dynamicBuf;
|
inline std::vector<std::string> g_selectedCheatCodes;
|
||||||
extern float g_timer;
|
|
||||||
|
|
||||||
void drawPulsingOutline(u32 x, u32 y, u16 w, u16 h, u8 size, u32 color);
|
void drawPulsingOutline(u32 x, u32 y, u16 w, u16 h, u8 size, u32 color);
|
||||||
void drawOutline(u32 x, u32 y, u16 w, u16 h, u8 size, u32 color);
|
void drawOutline(u32 x, u32 y, u16 w, u16 h, u8 size, u32 color);
|
||||||
|
|
|
@ -28,8 +28,9 @@
|
||||||
#define UTIL_HPP
|
#define UTIL_HPP
|
||||||
|
|
||||||
#include "archive.hpp"
|
#include "archive.hpp"
|
||||||
#include "cheatmanager.hpp"
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
#include "configuration.hpp"
|
||||||
|
#include "gui.hpp"
|
||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
#include <citro2d.h>
|
#include <citro2d.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
149
3ds/source/CheatManagerOverlay.cpp
Normal file
149
3ds/source/CheatManagerOverlay.cpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CheatManagerOverlay.hpp"
|
||||||
|
|
||||||
|
CheatManagerOverlay::CheatManagerOverlay(Screen& screen, const std::string& mkey) : Overlay(screen)
|
||||||
|
{
|
||||||
|
key = mkey;
|
||||||
|
multiSelected = false;
|
||||||
|
std::string existingCheat = "";
|
||||||
|
|
||||||
|
FILE* f = fopen(("/cheats/" + key + ".txt").c_str(), "r");
|
||||||
|
if (f != NULL) {
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
u32 size = ftell(f);
|
||||||
|
char* s = new char[size];
|
||||||
|
rewind(f);
|
||||||
|
fread(s, 1, size, f);
|
||||||
|
existingCheat = std::string(s);
|
||||||
|
delete[] s;
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
currentIndex = i;
|
||||||
|
scrollable = std::make_unique<Scrollable>(2, 2, 396, 220, 11);
|
||||||
|
auto cheats = *CheatManager::getInstance().cheats().get();
|
||||||
|
for (auto it = cheats[key].begin(); it != cheats[key].end(); ++it) {
|
||||||
|
std::string value = it.key();
|
||||||
|
if (existingCheat.find(value) != std::string::npos) {
|
||||||
|
value = SELECTED_MAGIC + value;
|
||||||
|
}
|
||||||
|
scrollable->push_back(COLOR_GREY_DARKER, COLOR_WHITE, value, i == 0);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
staticBuf = C2D_TextBufNew(48);
|
||||||
|
dynamicBuf = C2D_TextBufNew(16);
|
||||||
|
C2D_TextParse(&multiSelectText, staticBuf, "\uE003 to select all cheats");
|
||||||
|
C2D_TextParse(&multiDeselectText, staticBuf, "\uE003 to deselect all cheats");
|
||||||
|
C2D_TextOptimize(&multiSelectText);
|
||||||
|
C2D_TextOptimize(&multiDeselectText);
|
||||||
|
}
|
||||||
|
|
||||||
|
CheatManagerOverlay::~CheatManagerOverlay(void)
|
||||||
|
{
|
||||||
|
C2D_TextBufDelete(staticBuf);
|
||||||
|
C2D_TextBufDelete(dynamicBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatManagerOverlay::drawTop(void) const
|
||||||
|
{
|
||||||
|
C2D_TextBufClear(dynamicBuf);
|
||||||
|
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, COLOR_OVERLAY);
|
||||||
|
C2D_Text page;
|
||||||
|
C2D_TextParse(&page, dynamicBuf, StringUtils::format("%d/%d", scrollable->index() + 1, scrollable->size()).c_str());
|
||||||
|
C2D_TextOptimize(&page);
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, COLOR_GREY_DARK);
|
||||||
|
scrollable->draw(true);
|
||||||
|
C2D_DrawText(&page, C2D_WithColor, ceilf(396 - page.width * scale), 224, 0.5f, scale, scale, COLOR_WHITE);
|
||||||
|
C2D_DrawText(multiSelected ? &multiDeselectText : &multiSelectText, C2D_WithColor, 4, 224, 0.5f, scale, scale, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatManagerOverlay::drawBottom(void) const
|
||||||
|
{
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 320, 240, COLOR_OVERLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatManagerOverlay::update(touchPosition* touch)
|
||||||
|
{
|
||||||
|
if (hidKeysDown() & KEY_A) {
|
||||||
|
std::string cellName = scrollable->cellName(scrollable->index());
|
||||||
|
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) == 0) {
|
||||||
|
// cheat was already selected
|
||||||
|
cellName = cellName.substr(strlen(SELECTED_MAGIC), cellName.length());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cellName = SELECTED_MAGIC + cellName;
|
||||||
|
}
|
||||||
|
scrollable->c2dText(scrollable->index(), cellName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidKeysDown() & KEY_Y) {
|
||||||
|
if (multiSelected) {
|
||||||
|
for (size_t j = 0; j < scrollable->size(); j++) {
|
||||||
|
std::string cellName = scrollable->cellName(j);
|
||||||
|
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) == 0) {
|
||||||
|
cellName = cellName.substr(strlen(SELECTED_MAGIC), cellName.length());
|
||||||
|
scrollable->c2dText(j, cellName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
multiSelected = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (size_t j = 0; j < scrollable->size(); j++) {
|
||||||
|
std::string cellName = scrollable->cellName(j);
|
||||||
|
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) != 0) {
|
||||||
|
cellName = SELECTED_MAGIC + cellName;
|
||||||
|
scrollable->c2dText(j, cellName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
multiSelected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollable->updateSelection();
|
||||||
|
scrollable->selectRow(currentIndex, false);
|
||||||
|
scrollable->selectRow(scrollable->index(), true);
|
||||||
|
currentIndex = scrollable->index();
|
||||||
|
|
||||||
|
if (hidKeysDown() & KEY_B) {
|
||||||
|
g_selectedCheatKey = key;
|
||||||
|
g_selectedCheatCodes.clear();
|
||||||
|
for (size_t i = 0; i < scrollable->size(); i++) {
|
||||||
|
g_selectedCheatCodes.push_back(scrollable->cellName(i));
|
||||||
|
}
|
||||||
|
me = std::make_shared<YesNoOverlay>(
|
||||||
|
screen, "Do you want to store\nthe cheat file?",
|
||||||
|
[]() {
|
||||||
|
CheatManager::getInstance().save(g_selectedCheatKey, g_selectedCheatCodes);
|
||||||
|
g_screen->removeOverlay();
|
||||||
|
},
|
||||||
|
[]() { g_screen->removeOverlay(); });
|
||||||
|
}
|
||||||
|
}
|
69
3ds/source/ErrorOverlay.cpp
Normal file
69
3ds/source/ErrorOverlay.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ErrorOverlay.hpp"
|
||||||
|
|
||||||
|
ErrorOverlay::ErrorOverlay(Screen& screen, Result res, const std::string& mtext) : Overlay(screen)
|
||||||
|
{
|
||||||
|
textBuf = C2D_TextBufNew(128);
|
||||||
|
button = std::make_unique<Clickable>(42, 162, 236, 36, COLOR_GREY_DARKER, COLOR_WHITE, "OK", true);
|
||||||
|
button->selected(true);
|
||||||
|
std::string t = StringUtils::wrap(mtext, size, 220);
|
||||||
|
std::string e = StringUtils::format("Error: 0x%08lX", res);
|
||||||
|
C2D_TextParse(&text, textBuf, t.c_str());
|
||||||
|
C2D_TextParse(&text, textBuf, e.c_str());
|
||||||
|
C2D_TextOptimize(&text);
|
||||||
|
C2D_TextOptimize(&error);
|
||||||
|
posx = ceilf(320 - StringUtils::textWidth(text, size)) / 2;
|
||||||
|
posy = 40 + ceilf(120 - StringUtils::textHeight(t, size)) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOverlay::~ErrorOverlay(void)
|
||||||
|
{
|
||||||
|
C2D_TextBufDelete(textBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorOverlay::drawTop(void) const
|
||||||
|
{
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, COLOR_OVERLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorOverlay::drawBottom(void) const
|
||||||
|
{
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 320, 240, COLOR_OVERLAY);
|
||||||
|
C2D_DrawRectSolid(40, 40, 0.5f, 240, 160, COLOR_GREY_DARK);
|
||||||
|
C2D_DrawText(&error, C2D_WithColor, 44, 44, 0.5f, 0.5f, 0.5f, COLOR_RED);
|
||||||
|
C2D_DrawText(&text, C2D_WithColor, posx, posy, 0.5f, size, size, COLOR_WHITE);
|
||||||
|
button->draw(0.7f, COLOR_RED);
|
||||||
|
drawPulsingOutline(42, 162, 236, 36, 2, COLOR_RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorOverlay::update(touchPosition* touch)
|
||||||
|
{
|
||||||
|
if (button->released() || (hidKeysDown() & KEY_A) || (hidKeysDown() & KEY_B)) {
|
||||||
|
screen.removeOverlay();
|
||||||
|
}
|
||||||
|
}
|
65
3ds/source/InfoOverlay.cpp
Normal file
65
3ds/source/InfoOverlay.cpp
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "InfoOverlay.hpp"
|
||||||
|
|
||||||
|
InfoOverlay::InfoOverlay(Screen& screen, const std::string& mtext) : Overlay(screen)
|
||||||
|
{
|
||||||
|
textBuf = C2D_TextBufNew(64);
|
||||||
|
button = std::make_unique<Clickable>(42, 162, 236, 36, COLOR_GREY_DARK, COLOR_WHITE, "OK", true);
|
||||||
|
button->selected(true);
|
||||||
|
std::string t = StringUtils::wrap(mtext, size, 220);
|
||||||
|
C2D_TextParse(&text, textBuf, t.c_str());
|
||||||
|
C2D_TextOptimize(&text);
|
||||||
|
posx = ceilf(320 - StringUtils::textWidth(text, size)) / 2;
|
||||||
|
posy = 40 + ceilf(120 - StringUtils::textHeight(t, size)) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoOverlay::~InfoOverlay(void)
|
||||||
|
{
|
||||||
|
C2D_TextBufDelete(textBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoOverlay::drawTop(void) const
|
||||||
|
{
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, COLOR_OVERLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoOverlay::drawBottom(void) const
|
||||||
|
{
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 320, 240, COLOR_OVERLAY);
|
||||||
|
C2D_DrawRectSolid(40, 40, 0.5f, 240, 160, COLOR_GREY_DARK);
|
||||||
|
C2D_DrawText(&text, C2D_WithColor, posx, posy, 0.5f, size, size, COLOR_WHITE);
|
||||||
|
button->draw(0.7f, COLOR_BLUE);
|
||||||
|
drawPulsingOutline(42, 162, 236, 36, 2, COLOR_BLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoOverlay::update(touchPosition* touch)
|
||||||
|
{
|
||||||
|
if (button->released() || (hidKeysDown() & KEY_A) || (hidKeysDown() & KEY_B)) {
|
||||||
|
screen.removeOverlay();
|
||||||
|
}
|
||||||
|
}
|
526
3ds/source/MainScreen.cpp
Normal file
526
3ds/source/MainScreen.cpp
Normal file
|
@ -0,0 +1,526 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MainScreen.hpp"
|
||||||
|
|
||||||
|
static constexpr size_t rowlen = 4, collen = 8;
|
||||||
|
|
||||||
|
MainScreen::MainScreen(void) : hid(rowlen * collen, collen)
|
||||||
|
{
|
||||||
|
selectionTimer = 0;
|
||||||
|
refreshTimer = 0;
|
||||||
|
|
||||||
|
staticBuf = C2D_TextBufNew(256);
|
||||||
|
dynamicBuf = C2D_TextBufNew(256);
|
||||||
|
|
||||||
|
buttonBackup = std::make_unique<Clickable>(204, 102, 110, 35, COLOR_GREY_DARKER, COLOR_WHITE, "Backup \uE004", true);
|
||||||
|
buttonRestore = std::make_unique<Clickable>(204, 139, 110, 35, COLOR_GREY_DARKER, COLOR_WHITE, "Restore \uE005", true);
|
||||||
|
buttonCheats = std::make_unique<Clickable>(204, 176, 110, 36, COLOR_GREY_DARKER, COLOR_WHITE, "Cheats", true);
|
||||||
|
buttonPlayCoins = std::make_unique<Clickable>(204, 176, 110, 36, COLOR_GREY_DARKER, COLOR_WHITE, "\uE075 Coins", true);
|
||||||
|
directoryList = std::make_unique<Scrollable>(6, 102, 196, 110, 5);
|
||||||
|
buttonBackup->canChangeColorWhenSelected(true);
|
||||||
|
buttonRestore->canChangeColorWhenSelected(true);
|
||||||
|
buttonCheats->canChangeColorWhenSelected(true);
|
||||||
|
buttonPlayCoins->canChangeColorWhenSelected(true);
|
||||||
|
|
||||||
|
sprintf(ver, "v%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
|
||||||
|
|
||||||
|
C2D_TextParse(&ins1, staticBuf, "Hold SELECT to see commands. Press \uE002 for ");
|
||||||
|
C2D_TextParse(&ins2, staticBuf, "extdata");
|
||||||
|
C2D_TextParse(&ins3, staticBuf, ".");
|
||||||
|
C2D_TextParse(&ins4, staticBuf, "Press \uE073 or START to exit.");
|
||||||
|
C2D_TextParse(&version, staticBuf, ver);
|
||||||
|
C2D_TextParse(&checkpoint, staticBuf, "checkpoint");
|
||||||
|
C2D_TextParse(&c2dId, staticBuf, "ID:");
|
||||||
|
C2D_TextParse(&c2dMediatype, staticBuf, "Mediatype:");
|
||||||
|
|
||||||
|
C2D_TextParse(&top_move, staticBuf, "\uE006 to move between titles");
|
||||||
|
C2D_TextParse(&top_a, staticBuf, "\uE000 to enter target");
|
||||||
|
C2D_TextParse(&top_y, staticBuf, "\uE003 to multiselect a title");
|
||||||
|
C2D_TextParse(&top_my, staticBuf, "\uE003 hold to multiselect all titles");
|
||||||
|
C2D_TextParse(&top_b, staticBuf, "\uE001 to exit target or deselect all titles");
|
||||||
|
C2D_TextParse(&bot_ts, staticBuf, "\uE01D \uE006 to move\nbetween backups");
|
||||||
|
C2D_TextParse(&bot_x, staticBuf, "\uE002 to delete backups");
|
||||||
|
|
||||||
|
C2D_TextOptimize(&ins1);
|
||||||
|
C2D_TextOptimize(&ins2);
|
||||||
|
C2D_TextOptimize(&ins3);
|
||||||
|
C2D_TextOptimize(&ins4);
|
||||||
|
C2D_TextOptimize(&version);
|
||||||
|
C2D_TextOptimize(&checkpoint);
|
||||||
|
C2D_TextOptimize(&c2dId);
|
||||||
|
C2D_TextOptimize(&c2dMediatype);
|
||||||
|
|
||||||
|
C2D_TextOptimize(&top_move);
|
||||||
|
C2D_TextOptimize(&top_a);
|
||||||
|
C2D_TextOptimize(&top_y);
|
||||||
|
C2D_TextOptimize(&top_my);
|
||||||
|
C2D_TextOptimize(&top_b);
|
||||||
|
C2D_TextOptimize(&bot_ts);
|
||||||
|
C2D_TextOptimize(&bot_x);
|
||||||
|
|
||||||
|
C2D_PlainImageTint(&checkboxTint, C2D_Color32(88, 88, 88, 255), 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainScreen::~MainScreen(void)
|
||||||
|
{
|
||||||
|
C2D_TextBufDelete(dynamicBuf);
|
||||||
|
C2D_TextBufDelete(staticBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainScreen::drawTop(void) const
|
||||||
|
{
|
||||||
|
auto selEnt = MS::selectedEntries();
|
||||||
|
const size_t entries = hid.maxVisibleEntries();
|
||||||
|
const size_t max = hid.maxEntries(getTitleCount()) + 1;
|
||||||
|
|
||||||
|
C2D_TargetClear(g_top, COLOR_BG);
|
||||||
|
C2D_TargetClear(g_bottom, COLOR_BG);
|
||||||
|
|
||||||
|
C2D_SceneBegin(g_top);
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 19, COLOR_GREY_DARK);
|
||||||
|
C2D_DrawRectSolid(0, 221, 0.5f, 400, 19, COLOR_GREY_DARK);
|
||||||
|
|
||||||
|
C2D_Text timeText;
|
||||||
|
C2D_TextParse(&timeText, dynamicBuf, DateTime::timeStr().c_str());
|
||||||
|
C2D_TextOptimize(&timeText);
|
||||||
|
C2D_DrawText(&timeText, C2D_WithColor, 4.0f, 3.0f, 0.5f, 0.45f, 0.45f, COLOR_GREY_LIGHT);
|
||||||
|
|
||||||
|
for (size_t k = hid.page() * entries; k < hid.page() * entries + max; k++) {
|
||||||
|
C2D_DrawImageAt(icon(k), selectorX(k) + 1, selectorY(k) + 1, 0.5f, NULL, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getTitleCount() > 0) {
|
||||||
|
drawSelector();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t k = hid.page() * entries; k < hid.page() * entries + max; k++) {
|
||||||
|
if (!selEnt.empty() && std::find(selEnt.begin(), selEnt.end(), k) != selEnt.end()) {
|
||||||
|
C2D_DrawRectSolid(selectorX(k) + 31, selectorY(k) + 31, 0.5f, 16, 16, COLOR_WHITE);
|
||||||
|
C2D_SpriteSetPos(&checkbox, selectorX(k) + 27, selectorY(k) + 27);
|
||||||
|
C2D_DrawSpriteTinted(&checkbox, &checkboxTint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (favorite(k)) {
|
||||||
|
C2D_DrawRectSolid(selectorX(k) + 31, selectorY(k) + 3, 0.5f, 16, 16, COLOR_GOLD);
|
||||||
|
C2D_SpriteSetPos(&star, selectorX(k) + 27, selectorY(k) - 1);
|
||||||
|
C2D_DrawSpriteTinted(&star, &checkboxTint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float border = ceilf((400 - (ins1.width + ins2.width + ins3.width) * 0.47f) / 2);
|
||||||
|
C2D_DrawText(&ins1, C2D_WithColor, border, 223, 0.5f, 0.47f, 0.47f, COLOR_WHITE);
|
||||||
|
C2D_DrawText(
|
||||||
|
&ins2, C2D_WithColor, border + ceilf(ins1.width * 0.47f), 223, 0.5f, 0.47f, 0.47f, Archive::mode() == MODE_SAVE ? COLOR_WHITE : COLOR_RED);
|
||||||
|
C2D_DrawText(&ins3, C2D_WithColor, border + ceilf((ins1.width + ins2.width) * 0.47f), 223, 0.5f, 0.47f, 0.47f, COLOR_WHITE);
|
||||||
|
|
||||||
|
if (hidKeysHeld() & KEY_SELECT) {
|
||||||
|
const u32 inst_lh = scaleInst * fontGetInfo(NULL)->lineFeed;
|
||||||
|
const u32 inst_h = ceilf((240 - scaleInst * inst_lh * 6) / 2);
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, C2D_Color32(0, 0, 0, 190));
|
||||||
|
C2D_DrawText(&top_move, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_move, scaleInst)) / 2), inst_h, 0.9f, scaleInst, scaleInst,
|
||||||
|
COLOR_WHITE);
|
||||||
|
C2D_DrawText(&top_a, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_a, scaleInst)) / 2), inst_h + inst_lh * 1, 0.9f, scaleInst,
|
||||||
|
scaleInst, COLOR_WHITE);
|
||||||
|
C2D_DrawText(&top_b, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_b, scaleInst)) / 2), inst_h + inst_lh * 2, 0.9f, scaleInst,
|
||||||
|
scaleInst, COLOR_WHITE);
|
||||||
|
C2D_DrawText(&top_y, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_y, scaleInst)) / 2), inst_h + inst_lh * 3, 0.9f, scaleInst,
|
||||||
|
scaleInst, COLOR_WHITE);
|
||||||
|
C2D_DrawText(&top_my, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_my, scaleInst)) / 2), inst_h + inst_lh * 4, 0.9f, scaleInst,
|
||||||
|
scaleInst, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
C2D_DrawText(&version, C2D_WithColor, 400 - 4 - ceilf(0.45f * version.width), 3.0f, 0.5f, 0.45f, 0.45f, COLOR_GREY_LIGHT);
|
||||||
|
C2D_DrawImageAt(flag, 400 - 24 - ceilf(version.width * 0.45f), 0.0f, 0.5f, NULL, 1.0f, 1.0f);
|
||||||
|
C2D_DrawText(&checkpoint, C2D_WithColor, 400 - 6 - 0.45f * version.width - 0.5f * checkpoint.width - 19, 2.0f, 0.5f, 0.5f, 0.5f, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainScreen::drawBottom(void) const
|
||||||
|
{
|
||||||
|
C2D_TextBufClear(dynamicBuf);
|
||||||
|
|
||||||
|
const Mode_t mode = Archive::mode();
|
||||||
|
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 320, 19, COLOR_GREY_DARK);
|
||||||
|
C2D_DrawRectSolid(0, 221, 0.5f, 320, 19, COLOR_GREY_DARK);
|
||||||
|
if (getTitleCount() > 0) {
|
||||||
|
Title title;
|
||||||
|
getTitle(title, hid.fullIndex());
|
||||||
|
|
||||||
|
directoryList->flush();
|
||||||
|
std::vector<std::u16string> dirs = mode == MODE_SAVE ? title.saves() : title.extdata();
|
||||||
|
static std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < dirs.size(); i++) {
|
||||||
|
directoryList->push_back(COLOR_GREY_DARKER, COLOR_WHITE, convert.to_bytes(dirs.at(i)), i == directoryList->index());
|
||||||
|
}
|
||||||
|
|
||||||
|
C2D_Text shortDesc, longDesc, id, prodCode, media;
|
||||||
|
|
||||||
|
char lowid[18];
|
||||||
|
snprintf(lowid, 9, "%08X", (int)title.lowId());
|
||||||
|
|
||||||
|
C2D_TextParse(&shortDesc, dynamicBuf, title.shortDescription().c_str());
|
||||||
|
C2D_TextParse(&longDesc, dynamicBuf, title.longDescription().c_str());
|
||||||
|
C2D_TextParse(&id, dynamicBuf, lowid);
|
||||||
|
C2D_TextParse(&media, dynamicBuf, title.mediaTypeString().c_str());
|
||||||
|
|
||||||
|
C2D_TextOptimize(&shortDesc);
|
||||||
|
C2D_TextOptimize(&longDesc);
|
||||||
|
C2D_TextOptimize(&id);
|
||||||
|
C2D_TextOptimize(&media);
|
||||||
|
|
||||||
|
float longDescHeight, lowidWidth;
|
||||||
|
C2D_TextGetDimensions(&longDesc, 0.55f, 0.55f, NULL, &longDescHeight);
|
||||||
|
C2D_TextGetDimensions(&id, 0.5f, 0.5f, &lowidWidth, NULL);
|
||||||
|
|
||||||
|
C2D_DrawText(&shortDesc, C2D_WithColor, 4, 1, 0.5f, 0.6f, 0.6f, COLOR_WHITE);
|
||||||
|
C2D_DrawText(&longDesc, C2D_WithColor, 4, 27, 0.5f, 0.55f, 0.55f, COLOR_GREY_LIGHT);
|
||||||
|
|
||||||
|
C2D_DrawText(&c2dId, C2D_WithColor, 4, 31 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_GREY_LIGHT);
|
||||||
|
C2D_DrawText(&id, C2D_WithColor, 25, 31 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_WHITE);
|
||||||
|
|
||||||
|
snprintf(lowid, 18, "(%s)", title.productCode);
|
||||||
|
C2D_TextParse(&prodCode, dynamicBuf, lowid);
|
||||||
|
C2D_TextOptimize(&prodCode);
|
||||||
|
C2D_DrawText(&prodCode, C2D_WithColor, 30 + lowidWidth, 32 + longDescHeight, 0.5f, 0.42f, 0.42f, COLOR_GREY_LIGHT);
|
||||||
|
C2D_DrawText(&c2dMediatype, C2D_WithColor, 4, 47 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_GREY_LIGHT);
|
||||||
|
C2D_DrawText(&media, C2D_WithColor, 75, 47 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_WHITE);
|
||||||
|
|
||||||
|
C2D_DrawRectSolid(260, 27, 0.5f, 52, 52, COLOR_BLACK);
|
||||||
|
C2D_DrawImageAt(title.icon(), 262, 29, 0.5f, NULL, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
C2D_DrawRectSolid(4, 100, 0.5f, 312, 114, COLOR_GREY_DARK);
|
||||||
|
directoryList->draw(g_bottomScrollEnabled);
|
||||||
|
buttonBackup->draw(0.7, 0);
|
||||||
|
buttonRestore->draw(0.7, 0);
|
||||||
|
if (title.isActivityLog()) {
|
||||||
|
buttonPlayCoins->draw(0.7, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buttonCheats->draw(0.7, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
C2D_DrawText(&ins4, C2D_WithColor, ceilf((320 - ins4.width * 0.47f) / 2), 223, 0.5f, 0.47f, 0.47f, COLOR_WHITE);
|
||||||
|
|
||||||
|
if (hidKeysHeld() & KEY_SELECT) {
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 320, 240, C2D_Color32(0, 0, 0, 190));
|
||||||
|
C2D_DrawText(&bot_ts, C2D_WithColor, 16, 124, 0.5f, scaleInst, scaleInst, COLOR_WHITE);
|
||||||
|
C2D_DrawText(&bot_x, C2D_WithColor, 16, 168, 0.5f, scaleInst, scaleInst, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainScreen::update(touchPosition* touch)
|
||||||
|
{
|
||||||
|
updateSelector();
|
||||||
|
handleEvents(touch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainScreen::updateSelector(void)
|
||||||
|
{
|
||||||
|
if (!g_bottomScrollEnabled) {
|
||||||
|
if (getTitleCount() > 0) {
|
||||||
|
hid.update(getTitleCount());
|
||||||
|
directoryList->resetIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
directoryList->updateSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainScreen::handleEvents(touchPosition* touch)
|
||||||
|
{
|
||||||
|
u32 kDown = hidKeysDown();
|
||||||
|
u32 kHeld = hidKeysHeld();
|
||||||
|
// Handle pressing A
|
||||||
|
// Backup list active: Backup/Restore
|
||||||
|
// Backup list inactive: Activate backup list only if multiple
|
||||||
|
// selections are enabled
|
||||||
|
if (kDown & KEY_A) {
|
||||||
|
// If backup list is active...
|
||||||
|
if (g_bottomScrollEnabled) {
|
||||||
|
// If the "New..." entry is selected...
|
||||||
|
if (0 == directoryList->index()) {
|
||||||
|
currentOverlay = std::make_shared<YesNoOverlay>(
|
||||||
|
*this, "Backup selected title?",
|
||||||
|
[this]() {
|
||||||
|
auto result = io::backup(hid.fullIndex(), 0);
|
||||||
|
if (std::get<0>(result)) {
|
||||||
|
currentOverlay = std::make_shared<InfoOverlay>(*this, std::get<2>(result));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentOverlay = std::make_shared<ErrorOverlay>(*this, std::get<1>(result), std::get<2>(result));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[this]() { this->removeOverlay(); });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentOverlay = std::make_shared<YesNoOverlay>(
|
||||||
|
*this, "Restore selected title?",
|
||||||
|
[this]() {
|
||||||
|
size_t cellIndex = directoryList->index();
|
||||||
|
auto result = io::restore(hid.fullIndex(), cellIndex, nameFromCell(cellIndex));
|
||||||
|
if (std::get<0>(result)) {
|
||||||
|
currentOverlay = std::make_shared<InfoOverlay>(*this, std::get<2>(result));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentOverlay = std::make_shared<ErrorOverlay>(*this, std::get<1>(result), std::get<2>(result));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[this]() { this->removeOverlay(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Activate backup list only if multiple selections are not enabled
|
||||||
|
if (!MS::multipleSelectionEnabled()) {
|
||||||
|
g_bottomScrollEnabled = true;
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kDown & KEY_B) {
|
||||||
|
g_bottomScrollEnabled = false;
|
||||||
|
MS::clearSelectedEntries();
|
||||||
|
directoryList->resetIndex();
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kDown & KEY_X) {
|
||||||
|
if (g_bottomScrollEnabled) {
|
||||||
|
bool isSaveMode = Archive::mode() == MODE_SAVE;
|
||||||
|
size_t index = directoryList->index();
|
||||||
|
// avoid actions if X is pressed on "New..."
|
||||||
|
if (index > 0) {
|
||||||
|
currentOverlay = std::make_shared<YesNoOverlay>(
|
||||||
|
*this, "Delete selected backup?",
|
||||||
|
[this, isSaveMode, index]() {
|
||||||
|
Title title;
|
||||||
|
getTitle(title, hid.fullIndex());
|
||||||
|
std::u16string path = isSaveMode ? title.fullSavePath(index) : title.fullExtdataPath(index);
|
||||||
|
io::deleteBackupFolder(path);
|
||||||
|
refreshDirectories(title.id());
|
||||||
|
directoryList->setIndex(index - 1);
|
||||||
|
this->removeOverlay();
|
||||||
|
},
|
||||||
|
[this]() { this->removeOverlay(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hid.reset();
|
||||||
|
Archive::mode(Archive::mode() == MODE_SAVE ? MODE_EXTDATA : MODE_SAVE);
|
||||||
|
MS::clearSelectedEntries();
|
||||||
|
directoryList->resetIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kDown & KEY_Y) {
|
||||||
|
if (g_bottomScrollEnabled) {
|
||||||
|
directoryList->resetIndex();
|
||||||
|
g_bottomScrollEnabled = false;
|
||||||
|
}
|
||||||
|
MS::addSelectedEntry(hid.fullIndex());
|
||||||
|
updateButtons(); // Do this last
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kHeld & KEY_Y) {
|
||||||
|
selectionTimer++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
selectionTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectionTimer > 90) {
|
||||||
|
MS::clearSelectedEntries();
|
||||||
|
for (size_t i = 0, sz = getTitleCount(); i < sz; i++) {
|
||||||
|
MS::addSelectedEntry(i);
|
||||||
|
}
|
||||||
|
selectionTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kHeld & KEY_B) {
|
||||||
|
refreshTimer++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
refreshTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshTimer > 90) {
|
||||||
|
hid.reset();
|
||||||
|
MS::clearSelectedEntries();
|
||||||
|
directoryList->resetIndex();
|
||||||
|
Threads::create((ThreadFunc)Threads::titles);
|
||||||
|
refreshTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonBackup->released() || (kDown & KEY_L)) {
|
||||||
|
if (MS::multipleSelectionEnabled()) {
|
||||||
|
directoryList->resetIndex();
|
||||||
|
std::vector<size_t> list = MS::selectedEntries();
|
||||||
|
for (size_t i = 0, sz = list.size(); i < sz; i++) {
|
||||||
|
auto result = io::backup(list.at(i), directoryList->index());
|
||||||
|
if (std::get<0>(result)) {
|
||||||
|
currentOverlay = std::make_shared<InfoOverlay>(*this, std::get<2>(result));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentOverlay = std::make_shared<ErrorOverlay>(*this, std::get<1>(result), std::get<2>(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MS::clearSelectedEntries();
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
else if (g_bottomScrollEnabled) {
|
||||||
|
currentOverlay = std::make_shared<YesNoOverlay>(
|
||||||
|
*this, "Backup selected save?",
|
||||||
|
[this]() {
|
||||||
|
auto result = io::backup(hid.fullIndex(), 0);
|
||||||
|
if (std::get<0>(result)) {
|
||||||
|
currentOverlay = std::make_shared<InfoOverlay>(*this, std::get<2>(result));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentOverlay = std::make_shared<ErrorOverlay>(*this, std::get<1>(result), std::get<2>(result));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[this]() { this->removeOverlay(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonRestore->released() || (kDown & KEY_R)) {
|
||||||
|
size_t cellIndex = directoryList->index();
|
||||||
|
if (MS::multipleSelectionEnabled()) {
|
||||||
|
MS::clearSelectedEntries();
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
else if (g_bottomScrollEnabled && cellIndex > 0) {
|
||||||
|
currentOverlay = std::make_shared<YesNoOverlay>(
|
||||||
|
*this, "Restore selected save?",
|
||||||
|
[this, cellIndex]() {
|
||||||
|
auto result = io::restore(hid.fullIndex(), cellIndex, nameFromCell(cellIndex));
|
||||||
|
if (std::get<0>(result)) {
|
||||||
|
currentOverlay = std::make_shared<InfoOverlay>(*this, std::get<2>(result));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentOverlay = std::make_shared<ErrorOverlay>(*this, std::get<1>(result), std::get<2>(result));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[this]() { this->removeOverlay(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getTitleCount() > 0) {
|
||||||
|
Title title;
|
||||||
|
getTitle(title, hid.fullIndex());
|
||||||
|
if (title.isActivityLog()) {
|
||||||
|
if (buttonPlayCoins->released()) {
|
||||||
|
if (!Archive::setPlayCoins()) {
|
||||||
|
currentOverlay = std::make_shared<ErrorOverlay>(*this, -1, "Failed to set play coins.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (buttonCheats->released() && CheatManager::getInstance().cheats() != nullptr) {
|
||||||
|
if (MS::multipleSelectionEnabled()) {
|
||||||
|
MS::clearSelectedEntries();
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::string key = StringUtils::format("%016llX", title.id());
|
||||||
|
if (CheatManager::getInstance().areCheatsAvailable(key)) {
|
||||||
|
currentOverlay = std::make_shared<CheatManagerOverlay>(*this, key);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentOverlay = std::make_shared<InfoOverlay>(*this, "No available cheat codes for this title.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainScreen::selectorX(size_t i) const
|
||||||
|
{
|
||||||
|
return 50 * ((i % (rowlen * collen)) % collen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainScreen::selectorY(size_t i) const
|
||||||
|
{
|
||||||
|
return 20 + 50 * ((i % (rowlen * collen)) / collen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainScreen::drawSelector(void) const
|
||||||
|
{
|
||||||
|
static const int w = 2;
|
||||||
|
const int x = selectorX(hid.index());
|
||||||
|
const int y = selectorY(hid.index());
|
||||||
|
float highlight_multiplier = fmax(0.0, fabs(fmod(g_timer, 1.0) - 0.5) / 0.5);
|
||||||
|
u8 r = COLOR_SELECTOR & 0xFF;
|
||||||
|
u8 g = (COLOR_SELECTOR >> 8) & 0xFF;
|
||||||
|
u8 b = (COLOR_SELECTOR >> 16) & 0xFF;
|
||||||
|
u32 color = C2D_Color32(r + (255 - r) * highlight_multiplier, g + (255 - g) * highlight_multiplier, b + (255 - b) * highlight_multiplier, 255);
|
||||||
|
|
||||||
|
C2D_DrawRectSolid(x, y, 0.5f, 50, 50, C2D_Color32(255, 255, 255, 100));
|
||||||
|
C2D_DrawRectSolid(x, y, 0.5f, 50, w, color); // top
|
||||||
|
C2D_DrawRectSolid(x, y + w, 0.5f, w, 50 - 2 * w, color); // left
|
||||||
|
C2D_DrawRectSolid(x + 50 - w, y + w, 0.5f, w, 50 - 2 * w, color); // right
|
||||||
|
C2D_DrawRectSolid(x, y + 50 - w, 0.5f, 50, w, color); // bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainScreen::updateButtons(void)
|
||||||
|
{
|
||||||
|
if (MS::multipleSelectionEnabled()) {
|
||||||
|
buttonRestore->canChangeColorWhenSelected(true);
|
||||||
|
buttonRestore->canChangeColorWhenSelected(false);
|
||||||
|
buttonCheats->canChangeColorWhenSelected(false);
|
||||||
|
buttonPlayCoins->canChangeColorWhenSelected(false);
|
||||||
|
buttonBackup->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
buttonRestore->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
||||||
|
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
||||||
|
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
||||||
|
}
|
||||||
|
else if (g_bottomScrollEnabled) {
|
||||||
|
buttonBackup->canChangeColorWhenSelected(true);
|
||||||
|
buttonRestore->canChangeColorWhenSelected(true);
|
||||||
|
buttonCheats->canChangeColorWhenSelected(true);
|
||||||
|
buttonPlayCoins->canChangeColorWhenSelected(true);
|
||||||
|
buttonBackup->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
buttonRestore->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buttonBackup->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
buttonRestore->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MainScreen::nameFromCell(size_t index) const
|
||||||
|
{
|
||||||
|
return directoryList->cellName(index);
|
||||||
|
}
|
89
3ds/source/YesNoOverlay.cpp
Normal file
89
3ds/source/YesNoOverlay.cpp
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Checkpoint
|
||||||
|
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||||
|
* * Requiring preservation of specified reasonable legal notices or
|
||||||
|
* author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
* * Prohibiting misrepresentation of the origin of that material,
|
||||||
|
* or requiring that modified versions of such material be marked in
|
||||||
|
* reasonable ways as different from the original version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "YesNoOverlay.hpp"
|
||||||
|
|
||||||
|
YesNoOverlay::YesNoOverlay(
|
||||||
|
Screen& screen, const std::string& mtext, const std::function<void()>& callbackYes, const std::function<void()>& callbackNo)
|
||||||
|
: Overlay(screen), hid(2, 2)
|
||||||
|
{
|
||||||
|
textBuf = C2D_TextBufNew(64);
|
||||||
|
C2D_TextParse(&text, textBuf, mtext.c_str());
|
||||||
|
C2D_TextOptimize(&text);
|
||||||
|
|
||||||
|
yesFunc = callbackYes;
|
||||||
|
noFunc = callbackNo;
|
||||||
|
|
||||||
|
posx = ceilf(320 - text.width * 0.6) / 2;
|
||||||
|
posy = 40 + ceilf(120 - 0.6f * fontGetInfo(NULL)->lineFeed) / 2;
|
||||||
|
|
||||||
|
buttonYes = std::make_unique<Clickable>(42, 162, 116, 36, COLOR_GREY_DARK, COLOR_WHITE, "\uE000 Yes", true);
|
||||||
|
buttonNo = std::make_unique<Clickable>(162, 162, 116, 36, COLOR_GREY_DARK, COLOR_WHITE, "\uE001 No", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
YesNoOverlay::~YesNoOverlay(void)
|
||||||
|
{
|
||||||
|
C2D_TextBufDelete(textBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void YesNoOverlay::drawTop(void) const
|
||||||
|
{
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, COLOR_OVERLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void YesNoOverlay::drawBottom(void) const
|
||||||
|
{
|
||||||
|
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, COLOR_OVERLAY);
|
||||||
|
C2D_DrawRectSolid(40, 40, 0.5f, 240, 160, COLOR_GREY_DARK);
|
||||||
|
C2D_DrawText(&text, C2D_WithColor, posx, posy, 0.5f, 0.6f, 0.6f, COLOR_WHITE);
|
||||||
|
C2D_DrawRectSolid(40, 160, 0.5f, 240, 40, COLOR_GREY_LIGHT);
|
||||||
|
|
||||||
|
buttonYes->draw(0.7, 0);
|
||||||
|
buttonNo->draw(0.7, 0);
|
||||||
|
|
||||||
|
if (hid.index() == 0) {
|
||||||
|
drawPulsingOutline(42, 162, 116, 36, 2, COLOR_BLUE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drawPulsingOutline(162, 162, 116, 36, 2, COLOR_BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void YesNoOverlay::update(touchPosition* touch)
|
||||||
|
{
|
||||||
|
hid.update(2);
|
||||||
|
|
||||||
|
hid.index(buttonYes->held() ? 0 : buttonNo->held() ? 1 : hid.index());
|
||||||
|
buttonYes->selected(hid.index() == 0);
|
||||||
|
buttonNo->selected(hid.index() == 1);
|
||||||
|
|
||||||
|
if (buttonYes->released() || ((hidKeysDown() & KEY_A) && hid.index() == 0)) {
|
||||||
|
yesFunc();
|
||||||
|
}
|
||||||
|
else if (buttonNo->released() || (hidKeysDown() & KEY_B) || ((hidKeysDown() & KEY_A) && hid.index() == 1)) {
|
||||||
|
noFunc();
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,24 +26,15 @@
|
||||||
|
|
||||||
#include "cheatmanager.hpp"
|
#include "cheatmanager.hpp"
|
||||||
|
|
||||||
#define SELECTED_MAGIC "\uE071 "
|
CheatManager::CheatManager(void)
|
||||||
|
|
||||||
static bool mLoaded = false;
|
|
||||||
static nlohmann::json mCheats;
|
|
||||||
static bool multiSelected = false;
|
|
||||||
static size_t MAGIC_LEN = strlen(SELECTED_MAGIC);
|
|
||||||
|
|
||||||
void CheatManager::init(void)
|
|
||||||
{
|
{
|
||||||
Gui::updateButtons();
|
mCheats = nullptr;
|
||||||
|
|
||||||
if (io::fileExists("/3ds/Checkpoint/cheats.json")) {
|
if (io::fileExists("/3ds/Checkpoint/cheats.json")) {
|
||||||
const std::string path = "/3ds/Checkpoint/cheats.json";
|
const std::string path = "/3ds/Checkpoint/cheats.json";
|
||||||
FILE* in = fopen(path.c_str(), "rt");
|
FILE* in = fopen(path.c_str(), "rt");
|
||||||
if (in != NULL) {
|
if (in != NULL) {
|
||||||
mCheats = nlohmann::json::parse(in, nullptr, false);
|
mCheats = std::make_shared<nlohmann::json>(nlohmann::json::parse(in, nullptr, false));
|
||||||
fclose(in);
|
fclose(in);
|
||||||
mLoaded = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -61,8 +52,7 @@ void CheatManager::init(void)
|
||||||
|
|
||||||
int r = BZ2_bzBuffToBuffDecompress(d, &destLen, s, size, 0, 0);
|
int r = BZ2_bzBuffToBuffDecompress(d, &destLen, s, size, 0, 0);
|
||||||
if (r == BZ_OK) {
|
if (r == BZ_OK) {
|
||||||
mCheats = nlohmann::json::parse(d);
|
mCheats = std::make_shared<nlohmann::json>(nlohmann::json::parse(d));
|
||||||
mLoaded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] s;
|
delete[] s;
|
||||||
|
@ -70,133 +60,25 @@ void CheatManager::init(void)
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gui::updateButtons();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheatManager::exit(void) {}
|
bool CheatManager::areCheatsAvailable(const std::string& key)
|
||||||
|
|
||||||
bool CheatManager::loaded(void)
|
|
||||||
{
|
{
|
||||||
return mLoaded;
|
return mCheats->find(key) != mCheats->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheatManager::availableCodes(const std::string& key)
|
void CheatManager::save(const std::string& key, const std::vector<std::string>& s)
|
||||||
{
|
{
|
||||||
return mCheats.find(key) != mCheats.end();
|
static size_t MAGIC_LEN = strlen(SELECTED_MAGIC);
|
||||||
}
|
|
||||||
|
|
||||||
void CheatManager::manageCheats(const std::string& key)
|
|
||||||
{
|
|
||||||
std::string existingCheat = "";
|
|
||||||
FILE* f = fopen(("/cheats/" + key + ".txt").c_str(), "r");
|
|
||||||
if (f != NULL) {
|
|
||||||
fseek(f, 0, SEEK_END);
|
|
||||||
u32 size = ftell(f);
|
|
||||||
char* s = new char[size];
|
|
||||||
rewind(f);
|
|
||||||
fread(s, 1, size, f);
|
|
||||||
existingCheat = std::string(s);
|
|
||||||
delete[] s;
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t i = 0;
|
|
||||||
size_t currentIndex = i;
|
|
||||||
Scrollable* s = new Scrollable(2, 2, 396, 220, 11);
|
|
||||||
for (auto it = mCheats[key].begin(); it != mCheats[key].end(); ++it) {
|
|
||||||
std::string value = it.key();
|
|
||||||
if (existingCheat.find(value) != std::string::npos) {
|
|
||||||
value = SELECTED_MAGIC + value;
|
|
||||||
}
|
|
||||||
s->push_back(COLOR_GREY_DARKER, COLOR_WHITE, value, i == 0);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float scale = 0.47f;
|
|
||||||
C2D_Text multiSelectText, multiDeselectText;
|
|
||||||
C2D_TextParse(&multiSelectText, g_dynamicBuf, "\uE003 to select all cheats");
|
|
||||||
C2D_TextParse(&multiDeselectText, g_dynamicBuf, "\uE003 to deselect all cheats");
|
|
||||||
C2D_TextOptimize(&multiSelectText);
|
|
||||||
C2D_TextOptimize(&multiDeselectText);
|
|
||||||
|
|
||||||
while (aptMainLoop()) {
|
|
||||||
hidScanInput();
|
|
||||||
|
|
||||||
if (hidKeysDown() & KEY_B) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hidKeysDown() & KEY_A) {
|
|
||||||
std::string cellName = s->cellName(s->index());
|
|
||||||
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) == 0) {
|
|
||||||
// cheat was already selected
|
|
||||||
cellName = cellName.substr(MAGIC_LEN, cellName.length());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cellName = SELECTED_MAGIC + cellName;
|
|
||||||
}
|
|
||||||
s->c2dText(s->index(), cellName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hidKeysDown() & KEY_Y) {
|
|
||||||
if (multiSelected) {
|
|
||||||
for (size_t j = 0; j < s->size(); j++) {
|
|
||||||
std::string cellName = s->cellName(j);
|
|
||||||
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) == 0) {
|
|
||||||
cellName = cellName.substr(MAGIC_LEN, cellName.length());
|
|
||||||
s->c2dText(j, cellName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
multiSelected = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (size_t j = 0; j < s->size(); j++) {
|
|
||||||
std::string cellName = s->cellName(j);
|
|
||||||
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) != 0) {
|
|
||||||
cellName = SELECTED_MAGIC + cellName;
|
|
||||||
s->c2dText(j, cellName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
multiSelected = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s->updateSelection();
|
|
||||||
s->selectRow(currentIndex, false);
|
|
||||||
s->selectRow(s->index(), true);
|
|
||||||
currentIndex = s->index();
|
|
||||||
|
|
||||||
C2D_Text page;
|
|
||||||
C2D_TextParse(&page, g_dynamicBuf, StringUtils::format("%d/%d", s->index() + 1, s->size()).c_str());
|
|
||||||
C2D_TextOptimize(&page);
|
|
||||||
C2D_TextBufClear(g_dynamicBuf);
|
|
||||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
|
||||||
C2D_SceneBegin(g_top);
|
|
||||||
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, COLOR_GREY_DARK);
|
|
||||||
s->draw(true);
|
|
||||||
C2D_DrawText(&page, C2D_WithColor, ceilf(396 - page.width * scale), 224, 0.5f, scale, scale, COLOR_WHITE);
|
|
||||||
C2D_DrawText(multiSelected ? &multiDeselectText : &multiSelectText, C2D_WithColor, 4, 224, 0.5f, scale, scale, COLOR_WHITE);
|
|
||||||
Gui::frameEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
Gui::draw();
|
|
||||||
if (Gui::askForConfirmation("Do you want to store\nthe cheat file?")) {
|
|
||||||
save(key, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheatManager::save(const std::string& key, Scrollable* s)
|
|
||||||
{
|
|
||||||
std::string cheatFile = "";
|
std::string cheatFile = "";
|
||||||
for (size_t i = 0; i < s->size(); i++) {
|
auto cheats = *CheatManager::getInstance().cheats().get();
|
||||||
std::string cellName = s->cellName(i);
|
for (size_t i = 0; i < s.size(); i++) {
|
||||||
|
std::string cellName = s.at(i);
|
||||||
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) == 0) {
|
if (cellName.compare(0, MAGIC_LEN, SELECTED_MAGIC) == 0) {
|
||||||
cellName = cellName.substr(MAGIC_LEN, cellName.length());
|
cellName = cellName.substr(MAGIC_LEN, cellName.length());
|
||||||
cheatFile += cellName + "\n";
|
cheatFile += "[" + cellName + "]\n";
|
||||||
for (auto& it : mCheats[key][cellName]) {
|
for (auto& it : cheats[key][cellName]) {
|
||||||
cheatFile += it.get<std::string>() + "\n";
|
cheatFile += it.get<std::string>() + "\n";
|
||||||
}
|
}
|
||||||
cheatFile += "\n";
|
cheatFile += "\n";
|
||||||
|
@ -208,7 +90,4 @@ void CheatManager::save(const std::string& key, Scrollable* s)
|
||||||
fwrite(cheatFile.c_str(), 1, cheatFile.length(), f);
|
fwrite(cheatFile.c_str(), 1, cheatFile.length(), f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
Gui::showError(errno, "Failed to write cheat file\nto the sd card");
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -26,38 +26,6 @@
|
||||||
|
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
|
|
||||||
// c2d/c3d vars
|
|
||||||
C3D_RenderTarget* g_top;
|
|
||||||
C3D_RenderTarget* g_bottom;
|
|
||||||
|
|
||||||
static C2D_SpriteSheet spritesheet;
|
|
||||||
static C2D_Image flag;
|
|
||||||
static C2D_Sprite checkbox, star;
|
|
||||||
static C2D_ImageTint checkboxTint;
|
|
||||||
|
|
||||||
C2D_TextBuf g_staticBuf, g_dynamicBuf;
|
|
||||||
static C2D_Text ins1, ins2, ins3, ins4, c2dId, c2dMediatype;
|
|
||||||
static C2D_Text checkpoint, version;
|
|
||||||
// instructions text
|
|
||||||
static C2D_Text top_move, top_a, top_y, top_my, top_b, bot_ts, bot_x;
|
|
||||||
|
|
||||||
// gui elements
|
|
||||||
static Clickable* buttonBackup;
|
|
||||||
static Clickable* buttonRestore;
|
|
||||||
static Clickable* buttonCheats;
|
|
||||||
static Clickable* buttonPlayCoins;
|
|
||||||
static Scrollable* directoryList;
|
|
||||||
bool g_bottomScrollEnabled;
|
|
||||||
|
|
||||||
static const size_t rowlen = 4;
|
|
||||||
static const size_t collen = 8;
|
|
||||||
static HidHorizontal* hid;
|
|
||||||
|
|
||||||
static void drawSelector(void);
|
|
||||||
static int selectorX(size_t i);
|
|
||||||
static int selectorY(size_t i);
|
|
||||||
float g_timer = 0;
|
|
||||||
|
|
||||||
C2D_Image Gui::TWLIcon(void)
|
C2D_Image Gui::TWLIcon(void)
|
||||||
{
|
{
|
||||||
return C2D_SpriteSheetGetImage(spritesheet, sprites_twlcart_idx);
|
return C2D_SpriteSheetGetImage(spritesheet, sprites_twlcart_idx);
|
||||||
|
@ -68,170 +36,6 @@ C2D_Image Gui::noIcon(void)
|
||||||
return C2D_SpriteSheetGetImage(spritesheet, sprites_noicon_idx);
|
return C2D_SpriteSheetGetImage(spritesheet, sprites_noicon_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Gui::nameFromCell(size_t index)
|
|
||||||
{
|
|
||||||
return directoryList->cellName(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::drawCopy(const std::u16string& src, u32 offset, u32 size)
|
|
||||||
{
|
|
||||||
C2D_Text copyText, srcText;
|
|
||||||
std::string sizeString = StringUtils::sizeString(offset) + " of " + StringUtils::sizeString(size);
|
|
||||||
C2D_TextParse(&srcText, g_dynamicBuf, StringUtils::UTF16toUTF8(src).c_str());
|
|
||||||
C2D_TextParse(©Text, g_dynamicBuf, sizeString.c_str());
|
|
||||||
C2D_TextOptimize(&srcText);
|
|
||||||
C2D_TextOptimize(©Text);
|
|
||||||
const float scale = 0.6f;
|
|
||||||
const u32 size_h = scale * fontGetInfo(NULL)->lineFeed;
|
|
||||||
const u32 src_w = StringUtils::textWidth(srcText, scale);
|
|
||||||
const u32 size_w = StringUtils::textWidth(copyText, scale);
|
|
||||||
|
|
||||||
C2D_TextBufClear(g_dynamicBuf);
|
|
||||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
|
||||||
C2D_SceneBegin(g_bottom);
|
|
||||||
C2D_DrawRectSolid(40, 40, 0.5f, 240, 160, COLOR_GREY_DARK);
|
|
||||||
C2D_DrawRectSolid(40, 160, 0.5f, (float)offset / (float)size * 240, 40, COLOR_BLUE);
|
|
||||||
C2D_DrawText(&srcText, C2D_WithColor, 40 + (240 - src_w) / 2, 40 + ceilf((120 - size_h) / 2), 0.5f, scale, scale, COLOR_WHITE);
|
|
||||||
C2D_DrawText(©Text, C2D_WithColor, 40 + (240 - size_w) / 2, 160 + ceilf((40 - size_h) / 2), 0.5f, scale, scale, COLOR_WHITE);
|
|
||||||
frameEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gui::askForConfirmation(const std::string& message)
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
Clickable* buttonYes = new Clickable(42, 162, 116, 36, COLOR_GREY_DARK, COLOR_WHITE, "\uE000 Yes", true);
|
|
||||||
Clickable* buttonNo = new Clickable(162, 162, 116, 36, COLOR_GREY_DARK, COLOR_WHITE, "\uE001 No", true);
|
|
||||||
HidHorizontal* mhid = new HidHorizontal(2, 2);
|
|
||||||
C2D_Text text;
|
|
||||||
C2D_TextParse(&text, g_dynamicBuf, message.c_str());
|
|
||||||
C2D_TextOptimize(&text);
|
|
||||||
|
|
||||||
while (aptMainLoop()) {
|
|
||||||
hidScanInput();
|
|
||||||
mhid->update(2);
|
|
||||||
|
|
||||||
if (buttonYes->released() || ((hidKeysDown() & KEY_A) && mhid->index() == 0)) {
|
|
||||||
ret = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (buttonNo->released() || (hidKeysDown() & KEY_B) || ((hidKeysDown() & KEY_A) && mhid->index() == 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
mhid->index(buttonYes->held() ? 0 : buttonNo->held() ? 1 : mhid->index());
|
|
||||||
buttonYes->selected(mhid->index() == 0);
|
|
||||||
buttonNo->selected(mhid->index() == 1);
|
|
||||||
|
|
||||||
C2D_TextBufClear(g_dynamicBuf);
|
|
||||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
|
||||||
C2D_SceneBegin(g_bottom);
|
|
||||||
C2D_DrawRectSolid(40, 40, 0.5f, 240, 160, COLOR_GREY_DARK);
|
|
||||||
C2D_DrawText(&text, C2D_WithColor, ceilf(320 - text.width * 0.6) / 2, 40 + ceilf(120 - 0.6f * fontGetInfo(NULL)->lineFeed) / 2, 0.5f, 0.6f,
|
|
||||||
0.6f, COLOR_WHITE);
|
|
||||||
C2D_DrawRectSolid(40, 160, 0.5f, 240, 40, COLOR_GREY_LIGHT);
|
|
||||||
|
|
||||||
buttonYes->draw(0.7, 0);
|
|
||||||
buttonNo->draw(0.7, 0);
|
|
||||||
|
|
||||||
if (mhid->index() == 0) {
|
|
||||||
drawPulsingOutline(42, 162, 116, 36, 2, COLOR_BLUE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
drawPulsingOutline(162, 162, 116, 36, 2, COLOR_BLUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
frameEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete mhid;
|
|
||||||
delete buttonYes;
|
|
||||||
delete buttonNo;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::showInfo(const std::string& message)
|
|
||||||
{
|
|
||||||
const float size = 0.6f;
|
|
||||||
Clickable* button = new Clickable(42, 162, 236, 36, COLOR_GREY_DARK, COLOR_WHITE, "OK", true);
|
|
||||||
button->selected(true);
|
|
||||||
std::string t = StringUtils::wrap(message, size, 220);
|
|
||||||
C2D_Text text;
|
|
||||||
C2D_TextParse(&text, g_dynamicBuf, t.c_str());
|
|
||||||
C2D_TextOptimize(&text);
|
|
||||||
u32 w = StringUtils::textWidth(text, size);
|
|
||||||
u32 h = StringUtils::textHeight(t, size);
|
|
||||||
|
|
||||||
while (aptMainLoop()) {
|
|
||||||
hidScanInput();
|
|
||||||
|
|
||||||
if (button->released() || (hidKeysDown() & KEY_A) || (hidKeysDown() & KEY_B)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
C2D_TextBufClear(g_dynamicBuf);
|
|
||||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
|
||||||
C2D_SceneBegin(g_bottom);
|
|
||||||
C2D_DrawRectSolid(40, 40, 0.5f, 240, 160, COLOR_GREY_DARK);
|
|
||||||
C2D_DrawText(&text, C2D_WithColor, ceilf(320 - w) / 2, 40 + ceilf(120 - h) / 2, 0.5f, size, size, COLOR_WHITE);
|
|
||||||
button->draw(0.7f, COLOR_BLUE);
|
|
||||||
drawPulsingOutline(42, 162, 236, 36, 2, COLOR_BLUE);
|
|
||||||
frameEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete button;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::showError(Result res, const std::string& message)
|
|
||||||
{
|
|
||||||
const float size = 0.6f;
|
|
||||||
Clickable* button = new Clickable(42, 162, 236, 36, COLOR_GREY_DARKER, COLOR_WHITE, "OK", true);
|
|
||||||
button->selected(true);
|
|
||||||
std::string t = StringUtils::wrap(message, size, 220);
|
|
||||||
std::string e = StringUtils::format("Error: 0x%08lX", res);
|
|
||||||
C2D_Text text, error;
|
|
||||||
C2D_TextParse(&text, g_dynamicBuf, t.c_str());
|
|
||||||
C2D_TextParse(&error, g_dynamicBuf, e.c_str());
|
|
||||||
C2D_TextOptimize(&text);
|
|
||||||
C2D_TextOptimize(&error);
|
|
||||||
u32 w = StringUtils::textWidth(text, size);
|
|
||||||
u32 h = StringUtils::textHeight(t, size);
|
|
||||||
|
|
||||||
while (aptMainLoop()) {
|
|
||||||
hidScanInput();
|
|
||||||
|
|
||||||
if (button->released() || (hidKeysDown() & KEY_A) || (hidKeysDown() & KEY_B)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
C2D_TextBufClear(g_dynamicBuf);
|
|
||||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
|
||||||
C2D_SceneBegin(g_bottom);
|
|
||||||
C2D_DrawRectSolid(40, 40, 0.5f, 240, 160, COLOR_GREY_DARK);
|
|
||||||
C2D_DrawText(&error, C2D_WithColor, 44, 44, 0.5f, 0.5f, 0.5f, COLOR_RED);
|
|
||||||
C2D_DrawText(&text, C2D_WithColor, ceilf(320 - w) / 2, 40 + ceilf(120 - h) / 2, 0.5f, size, size, COLOR_WHITE);
|
|
||||||
button->draw(0.7f, COLOR_RED);
|
|
||||||
drawPulsingOutline(42, 162, 236, 36, 2, COLOR_RED);
|
|
||||||
frameEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete button;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::resetScrollableIndex(void)
|
|
||||||
{
|
|
||||||
directoryList->resetIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Gui::scrollableIndex(void)
|
|
||||||
{
|
|
||||||
return directoryList->index();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::scrollableIndex(size_t idx)
|
|
||||||
{
|
|
||||||
directoryList->setIndex(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::init(void)
|
void Gui::init(void)
|
||||||
{
|
{
|
||||||
gfxInitDefault();
|
gfxInitDefault();
|
||||||
|
@ -242,340 +46,22 @@ void Gui::init(void)
|
||||||
g_top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT);
|
g_top = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT);
|
||||||
g_bottom = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT);
|
g_bottom = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT);
|
||||||
|
|
||||||
g_bottomScrollEnabled = false;
|
|
||||||
hid = new HidHorizontal(rowlen * collen, collen);
|
|
||||||
|
|
||||||
buttonBackup = new Clickable(204, 102, 110, 35, COLOR_GREY_DARKER, COLOR_WHITE, "Backup \uE004", true);
|
|
||||||
buttonRestore = new Clickable(204, 139, 110, 35, COLOR_GREY_DARKER, COLOR_WHITE, "Restore \uE005", true);
|
|
||||||
buttonCheats = new Clickable(204, 176, 110, 36, COLOR_GREY_DARKER, COLOR_WHITE, "Cheats", true);
|
|
||||||
buttonPlayCoins = new Clickable(204, 176, 110, 36, COLOR_GREY_DARKER, COLOR_WHITE, "\uE075 Coins", true);
|
|
||||||
directoryList = new Scrollable(6, 102, 196, 110, 5);
|
|
||||||
buttonBackup->canChangeColorWhenSelected(true);
|
|
||||||
buttonRestore->canChangeColorWhenSelected(true);
|
|
||||||
buttonCheats->canChangeColorWhenSelected(true);
|
|
||||||
buttonPlayCoins->canChangeColorWhenSelected(true);
|
|
||||||
|
|
||||||
spritesheet = C2D_SpriteSheetLoad("romfs:/gfx/sprites.t3x");
|
spritesheet = C2D_SpriteSheetLoad("romfs:/gfx/sprites.t3x");
|
||||||
flag = C2D_SpriteSheetGetImage(spritesheet, sprites_checkpoint_idx);
|
flag = C2D_SpriteSheetGetImage(spritesheet, sprites_checkpoint_idx);
|
||||||
C2D_SpriteFromSheet(&checkbox, spritesheet, sprites_checkbox_idx);
|
C2D_SpriteFromSheet(&checkbox, spritesheet, sprites_checkbox_idx);
|
||||||
C2D_SpriteSetDepth(&checkbox, 0.5f);
|
C2D_SpriteSetDepth(&checkbox, 0.5f);
|
||||||
C2D_PlainImageTint(&checkboxTint, C2D_Color32(88, 88, 88, 255), 1.0f);
|
|
||||||
C2D_SpriteFromSheet(&star, spritesheet, sprites_star_idx);
|
C2D_SpriteFromSheet(&star, spritesheet, sprites_star_idx);
|
||||||
C2D_SpriteSetDepth(&star, 0.5f);
|
C2D_SpriteSetDepth(&star, 0.5f);
|
||||||
|
|
||||||
char ver[10];
|
|
||||||
sprintf(ver, "v%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
|
|
||||||
|
|
||||||
g_staticBuf = C2D_TextBufNew(256);
|
|
||||||
g_dynamicBuf = C2D_TextBufNew(256);
|
|
||||||
C2D_TextParse(&ins1, g_staticBuf, "Hold SELECT to see commands. Press \uE002 for ");
|
|
||||||
C2D_TextParse(&ins2, g_staticBuf, "extdata");
|
|
||||||
C2D_TextParse(&ins3, g_staticBuf, ".");
|
|
||||||
C2D_TextParse(&ins4, g_staticBuf, "Press \uE073 or START to exit.");
|
|
||||||
C2D_TextParse(&version, g_staticBuf, ver);
|
|
||||||
C2D_TextParse(&checkpoint, g_staticBuf, "checkpoint");
|
|
||||||
C2D_TextParse(&c2dId, g_staticBuf, "ID:");
|
|
||||||
C2D_TextParse(&c2dMediatype, g_staticBuf, "Mediatype:");
|
|
||||||
|
|
||||||
C2D_TextParse(&top_move, g_staticBuf, "\uE006 to move between titles");
|
|
||||||
C2D_TextParse(&top_a, g_staticBuf, "\uE000 to enter target");
|
|
||||||
C2D_TextParse(&top_y, g_staticBuf, "\uE003 to multiselect a title");
|
|
||||||
C2D_TextParse(&top_my, g_staticBuf, "\uE003 hold to multiselect all titles");
|
|
||||||
C2D_TextParse(&top_b, g_staticBuf, "\uE001 to exit target or deselect all titles");
|
|
||||||
C2D_TextParse(&bot_ts, g_staticBuf, "\uE01D \uE006 to move\nbetween backups");
|
|
||||||
C2D_TextParse(&bot_x, g_staticBuf, "\uE002 to delete backups");
|
|
||||||
|
|
||||||
C2D_TextOptimize(&ins1);
|
|
||||||
C2D_TextOptimize(&ins2);
|
|
||||||
C2D_TextOptimize(&ins3);
|
|
||||||
C2D_TextOptimize(&ins4);
|
|
||||||
C2D_TextOptimize(&version);
|
|
||||||
C2D_TextOptimize(&checkpoint);
|
|
||||||
C2D_TextOptimize(&c2dId);
|
|
||||||
C2D_TextOptimize(&c2dMediatype);
|
|
||||||
|
|
||||||
C2D_TextOptimize(&top_move);
|
|
||||||
C2D_TextOptimize(&top_a);
|
|
||||||
C2D_TextOptimize(&top_y);
|
|
||||||
C2D_TextOptimize(&top_my);
|
|
||||||
C2D_TextOptimize(&top_b);
|
|
||||||
C2D_TextOptimize(&bot_ts);
|
|
||||||
C2D_TextOptimize(&bot_x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gui::exit(void)
|
void Gui::exit(void)
|
||||||
{
|
{
|
||||||
C2D_TextBufDelete(g_dynamicBuf);
|
|
||||||
C2D_TextBufDelete(g_staticBuf);
|
|
||||||
C2D_SpriteSheetFree(spritesheet);
|
C2D_SpriteSheetFree(spritesheet);
|
||||||
delete hid;
|
|
||||||
delete directoryList;
|
|
||||||
delete buttonPlayCoins;
|
|
||||||
delete buttonCheats;
|
|
||||||
delete buttonRestore;
|
|
||||||
delete buttonBackup;
|
|
||||||
C2D_Fini();
|
C2D_Fini();
|
||||||
C3D_Fini();
|
C3D_Fini();
|
||||||
gfxExit();
|
gfxExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Gui::index(void)
|
|
||||||
{
|
|
||||||
return hid->fullIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::updateButtons(void)
|
|
||||||
{
|
|
||||||
if (MS::multipleSelectionEnabled()) {
|
|
||||||
buttonRestore->canChangeColorWhenSelected(true);
|
|
||||||
buttonRestore->canChangeColorWhenSelected(false);
|
|
||||||
buttonCheats->canChangeColorWhenSelected(false);
|
|
||||||
buttonPlayCoins->canChangeColorWhenSelected(false);
|
|
||||||
buttonBackup->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonRestore->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
|
||||||
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
|
||||||
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
|
||||||
}
|
|
||||||
else if (g_bottomScrollEnabled) {
|
|
||||||
buttonBackup->canChangeColorWhenSelected(true);
|
|
||||||
buttonRestore->canChangeColorWhenSelected(true);
|
|
||||||
buttonCheats->canChangeColorWhenSelected(true);
|
|
||||||
buttonPlayCoins->canChangeColorWhenSelected(true);
|
|
||||||
buttonBackup->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonRestore->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buttonBackup->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonRestore->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool shouldCheckCheatManager = true;
|
|
||||||
if (CheatManager::loaded() && shouldCheckCheatManager) {
|
|
||||||
buttonCheats->c2dText("Cheats");
|
|
||||||
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_WHITE);
|
|
||||||
shouldCheckCheatManager = false;
|
|
||||||
}
|
|
||||||
else if (!CheatManager::loaded()) {
|
|
||||||
buttonCheats->c2dText("Loading...");
|
|
||||||
buttonCheats->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
|
||||||
buttonPlayCoins->setColors(COLOR_GREY_DARKER, COLOR_GREY_LIGHT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::updateSelector(void)
|
|
||||||
{
|
|
||||||
if (!g_bottomScrollEnabled) {
|
|
||||||
if (getTitleCount() > 0) {
|
|
||||||
hid->update(getTitleCount());
|
|
||||||
directoryList->resetIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
directoryList->updateSelection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawSelector(void)
|
|
||||||
{
|
|
||||||
static const int w = 2;
|
|
||||||
const int x = selectorX(hid->index());
|
|
||||||
const int y = selectorY(hid->index());
|
|
||||||
float highlight_multiplier = fmax(0.0, fabs(fmod(g_timer, 1.0) - 0.5) / 0.5);
|
|
||||||
u8 r = COLOR_SELECTOR & 0xFF;
|
|
||||||
u8 g = (COLOR_SELECTOR >> 8) & 0xFF;
|
|
||||||
u8 b = (COLOR_SELECTOR >> 16) & 0xFF;
|
|
||||||
u32 color = C2D_Color32(r + (255 - r) * highlight_multiplier, g + (255 - g) * highlight_multiplier, b + (255 - b) * highlight_multiplier, 255);
|
|
||||||
|
|
||||||
C2D_DrawRectSolid(x, y, 0.5f, 50, 50, C2D_Color32(255, 255, 255, 100));
|
|
||||||
C2D_DrawRectSolid(x, y, 0.5f, 50, w, color); // g_top
|
|
||||||
C2D_DrawRectSolid(x, y + w, 0.5f, w, 50 - 2 * w, color); // left
|
|
||||||
C2D_DrawRectSolid(x + 50 - w, y + w, 0.5f, w, 50 - 2 * w, color); // right
|
|
||||||
C2D_DrawRectSolid(x, y + 50 - w, 0.5f, 50, w, color); // g_bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
static int selectorX(size_t i)
|
|
||||||
{
|
|
||||||
return 50 * ((i % (rowlen * collen)) % collen);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int selectorY(size_t i)
|
|
||||||
{
|
|
||||||
return 20 + 50 * ((i % (rowlen * collen)) / collen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::draw(void)
|
|
||||||
{
|
|
||||||
const float scaleInst = 0.7f;
|
|
||||||
auto selEnt = MS::selectedEntries();
|
|
||||||
const size_t entries = hid->maxVisibleEntries();
|
|
||||||
const size_t max = hid->maxEntries(getTitleCount()) + 1;
|
|
||||||
const Mode_t mode = Archive::mode();
|
|
||||||
|
|
||||||
C2D_TextBufClear(g_dynamicBuf);
|
|
||||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
|
||||||
C2D_TargetClear(g_top, COLOR_BG);
|
|
||||||
C2D_TargetClear(g_bottom, COLOR_BG);
|
|
||||||
|
|
||||||
C2D_SceneBegin(g_top);
|
|
||||||
C2D_DrawRectSolid(0, 0, 0.5f, 400, 19, COLOR_GREY_DARK);
|
|
||||||
C2D_DrawRectSolid(0, 221, 0.5f, 400, 19, COLOR_GREY_DARK);
|
|
||||||
|
|
||||||
C2D_Text timeText;
|
|
||||||
C2D_TextParse(&timeText, g_dynamicBuf, DateTime::timeStr().c_str());
|
|
||||||
C2D_TextOptimize(&timeText);
|
|
||||||
C2D_DrawText(&timeText, C2D_WithColor, 4.0f, 3.0f, 0.5f, 0.45f, 0.45f, COLOR_GREY_LIGHT);
|
|
||||||
|
|
||||||
for (size_t k = hid->page() * entries; k < hid->page() * entries + max; k++) {
|
|
||||||
C2D_DrawImageAt(icon(k), selectorX(k) + 1, selectorY(k) + 1, 0.5f, NULL, 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getTitleCount() > 0) {
|
|
||||||
drawSelector();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t k = hid->page() * entries; k < hid->page() * entries + max; k++) {
|
|
||||||
if (!selEnt.empty() && std::find(selEnt.begin(), selEnt.end(), k) != selEnt.end()) {
|
|
||||||
C2D_DrawRectSolid(selectorX(k) + 31, selectorY(k) + 31, 0.5f, 16, 16, COLOR_WHITE);
|
|
||||||
C2D_SpriteSetPos(&checkbox, selectorX(k) + 27, selectorY(k) + 27);
|
|
||||||
C2D_DrawSpriteTinted(&checkbox, &checkboxTint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (favorite(k)) {
|
|
||||||
C2D_DrawRectSolid(selectorX(k) + 31, selectorY(k) + 3, 0.5f, 16, 16, COLOR_GOLD);
|
|
||||||
C2D_SpriteSetPos(&star, selectorX(k) + 27, selectorY(k) - 1);
|
|
||||||
C2D_DrawSpriteTinted(&star, &checkboxTint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const float border = ceilf((400 - (ins1.width + ins2.width + ins3.width) * 0.47f) / 2);
|
|
||||||
C2D_DrawText(&ins1, C2D_WithColor, border, 223, 0.5f, 0.47f, 0.47f, COLOR_WHITE);
|
|
||||||
C2D_DrawText(
|
|
||||||
&ins2, C2D_WithColor, border + ceilf(ins1.width * 0.47f), 223, 0.5f, 0.47f, 0.47f, Archive::mode() == MODE_SAVE ? COLOR_WHITE : COLOR_RED);
|
|
||||||
C2D_DrawText(&ins3, C2D_WithColor, border + ceilf((ins1.width + ins2.width) * 0.47f), 223, 0.5f, 0.47f, 0.47f, COLOR_WHITE);
|
|
||||||
|
|
||||||
if (hidKeysHeld() & KEY_SELECT) {
|
|
||||||
const u32 inst_lh = scaleInst * fontGetInfo(NULL)->lineFeed;
|
|
||||||
const u32 inst_h = ceilf((240 - scaleInst * inst_lh * 6) / 2);
|
|
||||||
C2D_DrawRectSolid(0, 0, 0.5f, 400, 240, C2D_Color32(0, 0, 0, 190));
|
|
||||||
C2D_DrawText(&top_move, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_move, scaleInst)) / 2), inst_h, 0.9f, scaleInst, scaleInst,
|
|
||||||
COLOR_WHITE);
|
|
||||||
C2D_DrawText(&top_a, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_a, scaleInst)) / 2), inst_h + inst_lh * 1, 0.9f, scaleInst,
|
|
||||||
scaleInst, COLOR_WHITE);
|
|
||||||
C2D_DrawText(&top_b, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_b, scaleInst)) / 2), inst_h + inst_lh * 2, 0.9f, scaleInst,
|
|
||||||
scaleInst, COLOR_WHITE);
|
|
||||||
C2D_DrawText(&top_y, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_y, scaleInst)) / 2), inst_h + inst_lh * 3, 0.9f, scaleInst,
|
|
||||||
scaleInst, COLOR_WHITE);
|
|
||||||
C2D_DrawText(&top_my, C2D_WithColor, ceilf((400 - StringUtils::textWidth(top_my, scaleInst)) / 2), inst_h + inst_lh * 4, 0.9f, scaleInst,
|
|
||||||
scaleInst, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
C2D_DrawText(&version, C2D_WithColor, 400 - 4 - ceilf(0.45f * version.width), 3.0f, 0.5f, 0.45f, 0.45f, COLOR_GREY_LIGHT);
|
|
||||||
C2D_DrawImageAt(flag, 400 - 24 - ceilf(version.width * 0.45f), 0.0f, 0.5f, NULL, 1.0f, 1.0f);
|
|
||||||
C2D_DrawText(&checkpoint, C2D_WithColor, 400 - 6 - 0.45f * version.width - 0.5f * checkpoint.width - 19, 2.0f, 0.5f, 0.5f, 0.5f, COLOR_WHITE);
|
|
||||||
|
|
||||||
C2D_SceneBegin(g_bottom);
|
|
||||||
C2D_DrawRectSolid(0, 0, 0.5f, 320, 19, COLOR_GREY_DARK);
|
|
||||||
C2D_DrawRectSolid(0, 221, 0.5f, 320, 19, COLOR_GREY_DARK);
|
|
||||||
if (getTitleCount() > 0) {
|
|
||||||
Title title;
|
|
||||||
getTitle(title, hid->fullIndex());
|
|
||||||
|
|
||||||
directoryList->flush();
|
|
||||||
std::vector<std::u16string> dirs = mode == MODE_SAVE ? title.saves() : title.extdata();
|
|
||||||
static std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < dirs.size(); i++) {
|
|
||||||
directoryList->push_back(COLOR_GREY_DARKER, COLOR_WHITE, convert.to_bytes(dirs.at(i)), i == directoryList->index());
|
|
||||||
}
|
|
||||||
|
|
||||||
C2D_Text shortDesc, longDesc, id, prodCode, media;
|
|
||||||
|
|
||||||
char lowid[18];
|
|
||||||
snprintf(lowid, 9, "%08X", (int)title.lowId());
|
|
||||||
|
|
||||||
C2D_TextParse(&shortDesc, g_dynamicBuf, title.shortDescription().c_str());
|
|
||||||
C2D_TextParse(&longDesc, g_dynamicBuf, title.longDescription().c_str());
|
|
||||||
C2D_TextParse(&id, g_dynamicBuf, lowid);
|
|
||||||
C2D_TextParse(&media, g_dynamicBuf, title.mediaTypeString().c_str());
|
|
||||||
|
|
||||||
C2D_TextOptimize(&shortDesc);
|
|
||||||
C2D_TextOptimize(&longDesc);
|
|
||||||
C2D_TextOptimize(&id);
|
|
||||||
C2D_TextOptimize(&media);
|
|
||||||
|
|
||||||
float longDescHeight, lowidWidth;
|
|
||||||
C2D_TextGetDimensions(&longDesc, 0.55f, 0.55f, NULL, &longDescHeight);
|
|
||||||
C2D_TextGetDimensions(&id, 0.5f, 0.5f, &lowidWidth, NULL);
|
|
||||||
|
|
||||||
C2D_DrawText(&shortDesc, C2D_WithColor, 4, 1, 0.5f, 0.6f, 0.6f, COLOR_WHITE);
|
|
||||||
C2D_DrawText(&longDesc, C2D_WithColor, 4, 27, 0.5f, 0.55f, 0.55f, COLOR_GREY_LIGHT);
|
|
||||||
|
|
||||||
C2D_DrawText(&c2dId, C2D_WithColor, 4, 31 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_GREY_LIGHT);
|
|
||||||
C2D_DrawText(&id, C2D_WithColor, 25, 31 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_WHITE);
|
|
||||||
|
|
||||||
snprintf(lowid, 18, "(%s)", title.productCode);
|
|
||||||
C2D_TextParse(&prodCode, g_dynamicBuf, lowid);
|
|
||||||
C2D_TextOptimize(&prodCode);
|
|
||||||
C2D_DrawText(&prodCode, C2D_WithColor, 30 + lowidWidth, 32 + longDescHeight, 0.5f, 0.42f, 0.42f, COLOR_GREY_LIGHT);
|
|
||||||
C2D_DrawText(&c2dMediatype, C2D_WithColor, 4, 47 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_GREY_LIGHT);
|
|
||||||
C2D_DrawText(&media, C2D_WithColor, 75, 47 + longDescHeight, 0.5f, 0.5f, 0.5f, COLOR_WHITE);
|
|
||||||
|
|
||||||
C2D_DrawRectSolid(260, 27, 0.5f, 52, 52, COLOR_BLACK);
|
|
||||||
C2D_DrawImageAt(title.icon(), 262, 29, 0.5f, NULL, 1.0f, 1.0f);
|
|
||||||
|
|
||||||
C2D_DrawRectSolid(4, 100, 0.5f, 312, 114, COLOR_GREY_DARK);
|
|
||||||
directoryList->draw(g_bottomScrollEnabled);
|
|
||||||
buttonBackup->draw(0.7, 0);
|
|
||||||
buttonRestore->draw(0.7, 0);
|
|
||||||
if (title.isActivityLog()) {
|
|
||||||
buttonPlayCoins->draw(0.7, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buttonCheats->draw(0.7, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
C2D_DrawText(&ins4, C2D_WithColor, ceilf((320 - ins4.width * 0.47f) / 2), 223, 0.5f, 0.47f, 0.47f, COLOR_WHITE);
|
|
||||||
|
|
||||||
if (hidKeysHeld() & KEY_SELECT) {
|
|
||||||
C2D_DrawRectSolid(0, 0, 0.5f, 320, 240, C2D_Color32(0, 0, 0, 190));
|
|
||||||
C2D_DrawText(&bot_ts, C2D_WithColor, 16, 124, 0.5f, scaleInst, scaleInst, COLOR_WHITE);
|
|
||||||
C2D_DrawText(&bot_x, C2D_WithColor, 16, 168, 0.5f, scaleInst, scaleInst, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
frameEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gui::isBackupReleased(void)
|
|
||||||
{
|
|
||||||
return buttonBackup->released();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gui::isRestoreReleased(void)
|
|
||||||
{
|
|
||||||
return buttonRestore->released();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gui::isCheatReleased(void)
|
|
||||||
{
|
|
||||||
return buttonCheats->released();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Gui::isPlayCoinsReleased(void)
|
|
||||||
{
|
|
||||||
return buttonPlayCoins->released();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::resetIndex(void)
|
|
||||||
{
|
|
||||||
hid->reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gui::frameEnd(void)
|
void Gui::frameEnd(void)
|
||||||
{
|
{
|
||||||
C3D_FrameEnd(0);
|
C3D_FrameEnd(0);
|
||||||
|
|
|
@ -53,15 +53,11 @@ void io::copyFile(FS_Archive srcArch, FS_Archive dstArch, const std::u16string&
|
||||||
|
|
||||||
FSStream output(dstArch, dstPath, FS_OPEN_WRITE, input.size());
|
FSStream output(dstArch, dstPath, FS_OPEN_WRITE, input.size());
|
||||||
if (output.good()) {
|
if (output.good()) {
|
||||||
size_t slashpos = srcPath.rfind(StringUtils::UTF8toUTF16("/"));
|
|
||||||
std::u16string name = srcPath.substr(slashpos + 1, srcPath.length() - slashpos - 1);
|
|
||||||
|
|
||||||
u32 rd;
|
u32 rd;
|
||||||
u8* buf = new u8[size];
|
u8* buf = new u8[size];
|
||||||
do {
|
do {
|
||||||
rd = input.read(buf, size);
|
rd = input.read(buf, size);
|
||||||
output.write(buf, rd);
|
output.write(buf, rd);
|
||||||
Gui::drawCopy(name, input.offset(), input.size());
|
|
||||||
} while (!input.eof());
|
} while (!input.eof());
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
}
|
}
|
||||||
|
@ -147,17 +143,9 @@ Result io::deleteFolderRecursively(FS_Archive arch, const std::u16string& path)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void io::backup(size_t index)
|
std::tuple<bool, Result, std::string> io::backup(size_t index, size_t cellIndex)
|
||||||
{
|
{
|
||||||
// check if multiple selection is enabled and don't ask for confirmation if that's the case
|
|
||||||
if (!MS::multipleSelectionEnabled()) {
|
|
||||||
if (!Gui::askForConfirmation("Backup selected save?")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Mode_t mode = Archive::mode();
|
const Mode_t mode = Archive::mode();
|
||||||
const size_t cellIndex = Gui::scrollableIndex();
|
|
||||||
const bool isNewFolder = cellIndex == 0;
|
const bool isNewFolder = cellIndex == 0;
|
||||||
Result res = 0;
|
Result res = 0;
|
||||||
|
|
||||||
|
@ -199,16 +187,14 @@ void io::backup(size_t index)
|
||||||
res = FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
res = FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
Gui::showError(res, "Failed to delete the existing backup\ndirectory recursively.");
|
return std::make_tuple(false, res, "Failed to delete the existing backup\ndirectory recursively.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = io::createDirectory(Archive::sdmc(), dstPath);
|
res = io::createDirectory(Archive::sdmc(), dstPath);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
Gui::showError(res, "Failed to create destination directory.");
|
return std::make_tuple(false, res, "Failed to create destination directory.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u16string copyPath = dstPath + StringUtils::UTF8toUTF16("/");
|
std::u16string copyPath = dstPath + StringUtils::UTF8toUTF16("/");
|
||||||
|
@ -217,16 +203,14 @@ void io::backup(size_t index)
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
std::string message = mode == MODE_SAVE ? "Failed to backup save." : "Failed to backup extdata.";
|
std::string message = mode == MODE_SAVE ? "Failed to backup save." : "Failed to backup extdata.";
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
Gui::showError(res, message);
|
|
||||||
|
|
||||||
FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
||||||
return;
|
return std::make_tuple(false, res, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshDirectories(title.id());
|
refreshDirectories(title.id());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Gui::showError(res, "Failed to open save archive.");
|
return std::make_tuple(false, res, "Failed to open save archive.");
|
||||||
}
|
}
|
||||||
|
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
|
@ -259,15 +243,13 @@ void io::backup(size_t index)
|
||||||
if (!isNewFolder || io::directoryExists(Archive::sdmc(), dstPath)) {
|
if (!isNewFolder || io::directoryExists(Archive::sdmc(), dstPath)) {
|
||||||
res = FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
res = FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
Gui::showError(res, "Failed to delete the existing\nbackup directory recursively.");
|
return std::make_tuple(false, res, "Failed to delete the existing\nbackup directory recursively.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = io::createDirectory(Archive::sdmc(), dstPath);
|
res = io::createDirectory(Archive::sdmc(), dstPath);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
Gui::showError(res, "Failed to create destination directory.");
|
return std::make_tuple(false, res, "Failed to create destination directory.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u16string copyPath =
|
std::u16string copyPath =
|
||||||
|
@ -279,15 +261,12 @@ void io::backup(size_t index)
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Gui::drawCopy(
|
|
||||||
StringUtils::UTF8toUTF16(title.shortDescription().c_str()) + StringUtils::UTF8toUTF16(".sav"), sectorSize * (i + 1), saveSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
delete[] saveFile;
|
delete[] saveFile;
|
||||||
Gui::showError(res, "Failed to backup save.");
|
|
||||||
FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
||||||
return;
|
return std::make_tuple(false, res, "Failed to backup save.");
|
||||||
}
|
}
|
||||||
|
|
||||||
FSStream stream(Archive::sdmc(), copyPath, FS_OPEN_WRITE, saveSize);
|
FSStream stream(Archive::sdmc(), copyPath, FS_OPEN_WRITE, saveSize);
|
||||||
|
@ -297,9 +276,8 @@ void io::backup(size_t index)
|
||||||
else {
|
else {
|
||||||
delete[] saveFile;
|
delete[] saveFile;
|
||||||
stream.close();
|
stream.close();
|
||||||
Gui::showError(res, "Failed to backup save.");
|
|
||||||
FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, dstPath.data()));
|
||||||
return;
|
return std::make_tuple(false, res, "Failed to backup save.");
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] saveFile;
|
delete[] saveFile;
|
||||||
|
@ -307,18 +285,13 @@ void io::backup(size_t index)
|
||||||
refreshDirectories(title.id());
|
refreshDirectories(title.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
Gui::showInfo("Progress correctly saved to disk.");
|
return std::make_tuple(true, 0, "Progress correctly saved to disk.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void io::restore(size_t index)
|
std::tuple<bool, Result, std::string> io::restore(size_t index, size_t cellIndex, const std::string& nameFromCell)
|
||||||
{
|
{
|
||||||
const Mode_t mode = Archive::mode();
|
const Mode_t mode = Archive::mode();
|
||||||
const size_t cellIndex = Gui::scrollableIndex();
|
Result res = 0;
|
||||||
if (cellIndex == 0 || !Gui::askForConfirmation("Restore selected save?")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result res = 0;
|
|
||||||
|
|
||||||
Title title;
|
Title title;
|
||||||
getTitle(title, index);
|
getTitle(title, index);
|
||||||
|
@ -348,16 +321,14 @@ void io::restore(size_t index)
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
std::string message = mode == MODE_SAVE ? "Failed to restore save." : "Failed to restore extdata.";
|
std::string message = mode == MODE_SAVE ? "Failed to restore save." : "Failed to restore extdata.";
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
Gui::showError(res, message);
|
return std::make_tuple(false, res, message);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == MODE_SAVE) {
|
if (mode == MODE_SAVE) {
|
||||||
res = FSUSER_ControlArchive(archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
|
res = FSUSER_ControlArchive(archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
Gui::showError(res, "Failed to commit save data.");
|
return std::make_tuple(false, res, "Failed to commit save data.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 out;
|
u8 out;
|
||||||
|
@ -365,13 +336,12 @@ void io::restore(size_t index)
|
||||||
res = FSUSER_ControlSecureSave(SECURESAVE_ACTION_DELETE, &secureValue, 8, &out, 1);
|
res = FSUSER_ControlSecureSave(SECURESAVE_ACTION_DELETE, &secureValue, 8, &out, 1);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
Gui::showError(res, "Failed to fix secure value.");
|
return std::make_tuple(false, res, "Failed to fix secure value.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Gui::showError(res, "Failed to open save archive.");
|
return std::make_tuple(false, res, "Failed to open save archive.");
|
||||||
}
|
}
|
||||||
|
|
||||||
FSUSER_CloseArchive(archive);
|
FSUSER_CloseArchive(archive);
|
||||||
|
@ -395,8 +365,7 @@ void io::restore(size_t index)
|
||||||
|
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
delete[] saveFile;
|
delete[] saveFile;
|
||||||
Gui::showError(res, "Failed to read save file backup.");
|
return std::make_tuple(false, res, "Failed to read save file backup.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u32 i = 0; i < saveSize / pageSize; ++i) {
|
for (u32 i = 0; i < saveSize / pageSize; ++i) {
|
||||||
|
@ -404,26 +373,24 @@ void io::restore(size_t index)
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Gui::drawCopy(
|
|
||||||
StringUtils::UTF8toUTF16(title.shortDescription().c_str()) + StringUtils::UTF8toUTF16(".sav"), pageSize * (i + 1), saveSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
delete[] saveFile;
|
delete[] saveFile;
|
||||||
Gui::showError(res, "Failed to restore save.");
|
return std::make_tuple(false, res, "Failed to restore save.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] saveFile;
|
delete[] saveFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gui::showInfo(Gui::nameFromCell(cellIndex) + "\nhas been restored successfully.");
|
return std::make_tuple(true, 0, nameFromCell + "\nhas been restored successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void io::deleteBackupFolder(const std::u16string& path)
|
void io::deleteBackupFolder(const std::u16string& path)
|
||||||
{
|
{
|
||||||
Result res = FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, path.data()));
|
Result res = FSUSER_DeleteDirectoryRecursively(Archive::sdmc(), fsMakePath(PATH_UTF16, path.data()));
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
Gui::showError(res, "Failed to delete backup folder.");
|
// TODO: log this
|
||||||
|
// return std::make_tuple(false, res, "Failed to delete backup folder.");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,6 +25,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
|
#include "MainScreen.hpp"
|
||||||
#include "thread.hpp"
|
#include "thread.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
|
@ -35,166 +36,23 @@ int main()
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int selectionTimer = 0;
|
g_screen = std::make_unique<MainScreen>();
|
||||||
int refreshTimer = 0;
|
|
||||||
Threads::create((ThreadFunc)Threads::titles);
|
Threads::create((ThreadFunc)Threads::titles);
|
||||||
|
|
||||||
u32 kHeld, kDown = hidKeysDown();
|
while (aptMainLoop() && !(hidKeysDown() & KEY_START)) {
|
||||||
while (aptMainLoop() && !(kDown & KEY_START)) {
|
touchPosition touch;
|
||||||
hidScanInput();
|
hidScanInput();
|
||||||
kDown = hidKeysDown();
|
hidTouchRead(&touch);
|
||||||
kHeld = hidKeysHeld();
|
|
||||||
|
|
||||||
updateCard();
|
updateCard();
|
||||||
|
|
||||||
// Handle pressing A
|
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
||||||
// Backup list active: Backup/Restore
|
g_screen->doDrawTop();
|
||||||
// Backup list inactive: Activate backup list only if multiple
|
C2D_SceneBegin(g_bottom);
|
||||||
// selections are enabled
|
g_screen->doDrawBottom();
|
||||||
if (kDown & KEY_A) {
|
Gui::frameEnd();
|
||||||
// If backup list is active...
|
g_screen->doUpdate(&touch);
|
||||||
if (g_bottomScrollEnabled) {
|
|
||||||
// If the "New..." entry is selected...
|
|
||||||
if (0 == Gui::scrollableIndex()) {
|
|
||||||
io::backup(Gui::index());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
io::restore(Gui::index());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Activate backup list only if multiple selections are not enabled
|
|
||||||
if (!MS::multipleSelectionEnabled()) {
|
|
||||||
g_bottomScrollEnabled = true;
|
|
||||||
Gui::updateButtons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kDown & KEY_B) {
|
|
||||||
g_bottomScrollEnabled = false;
|
|
||||||
MS::clearSelectedEntries();
|
|
||||||
Gui::resetScrollableIndex();
|
|
||||||
Gui::updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kDown & KEY_X) {
|
|
||||||
if (g_bottomScrollEnabled) {
|
|
||||||
bool isSaveMode = Archive::mode() == MODE_SAVE;
|
|
||||||
size_t index = Gui::scrollableIndex();
|
|
||||||
// avoid actions if X is pressed on "New..."
|
|
||||||
if (index > 0 && Gui::askForConfirmation("Delete selected backup?")) {
|
|
||||||
Title title;
|
|
||||||
getTitle(title, Gui::index());
|
|
||||||
std::u16string path = isSaveMode ? title.fullSavePath(index) : title.fullExtdataPath(index);
|
|
||||||
io::deleteBackupFolder(path);
|
|
||||||
refreshDirectories(title.id());
|
|
||||||
Gui::scrollableIndex(index - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Gui::resetIndex();
|
|
||||||
Archive::mode(Archive::mode() == MODE_SAVE ? MODE_EXTDATA : MODE_SAVE);
|
|
||||||
MS::clearSelectedEntries();
|
|
||||||
Gui::resetScrollableIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kDown & KEY_Y) {
|
|
||||||
if (g_bottomScrollEnabled) {
|
|
||||||
Gui::resetScrollableIndex();
|
|
||||||
g_bottomScrollEnabled = false;
|
|
||||||
}
|
|
||||||
MS::addSelectedEntry(Gui::index());
|
|
||||||
Gui::updateButtons(); // Do this last
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kHeld & KEY_Y) {
|
|
||||||
selectionTimer++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
selectionTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectionTimer > 90) {
|
|
||||||
MS::clearSelectedEntries();
|
|
||||||
for (size_t i = 0, sz = getTitleCount(); i < sz; i++) {
|
|
||||||
MS::addSelectedEntry(i);
|
|
||||||
}
|
|
||||||
selectionTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kHeld & KEY_B) {
|
|
||||||
refreshTimer++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
refreshTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (refreshTimer > 90) {
|
|
||||||
Gui::resetIndex();
|
|
||||||
MS::clearSelectedEntries();
|
|
||||||
Gui::resetScrollableIndex();
|
|
||||||
Threads::create((ThreadFunc)Threads::titles);
|
|
||||||
refreshTimer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Gui::isBackupReleased() || (kDown & KEY_L)) {
|
|
||||||
if (MS::multipleSelectionEnabled()) {
|
|
||||||
Gui::resetScrollableIndex();
|
|
||||||
std::vector<size_t> list = MS::selectedEntries();
|
|
||||||
for (size_t i = 0, sz = list.size(); i < sz; i++) {
|
|
||||||
io::backup(list.at(i));
|
|
||||||
}
|
|
||||||
MS::clearSelectedEntries();
|
|
||||||
Gui::updateButtons();
|
|
||||||
}
|
|
||||||
else if (g_bottomScrollEnabled) {
|
|
||||||
io::backup(Gui::index());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Gui::isRestoreReleased() || (kDown & KEY_R)) {
|
|
||||||
if (MS::multipleSelectionEnabled()) {
|
|
||||||
MS::clearSelectedEntries();
|
|
||||||
Gui::updateButtons();
|
|
||||||
}
|
|
||||||
else if (g_bottomScrollEnabled) {
|
|
||||||
io::restore(Gui::index());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getTitleCount() > 0) {
|
|
||||||
Title title;
|
|
||||||
getTitle(title, Gui::index());
|
|
||||||
if (title.isActivityLog()) {
|
|
||||||
if (Gui::isPlayCoinsReleased()) {
|
|
||||||
if (!Archive::setPlayCoins()) {
|
|
||||||
Gui::showError(-1, "Failed to set play coins.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (Gui::isCheatReleased() && CheatManager::loaded()) {
|
|
||||||
if (MS::multipleSelectionEnabled()) {
|
|
||||||
MS::clearSelectedEntries();
|
|
||||||
Gui::updateButtons();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::string key = StringUtils::format("%016llX", title.id());
|
|
||||||
if (CheatManager::availableCodes(key)) {
|
|
||||||
CheatManager::manageCheats(key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Gui::showInfo("No available cheat codes for this title.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Gui::updateSelector();
|
|
||||||
Gui::draw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Threads::destroy();
|
Threads::destroy();
|
||||||
|
|
|
@ -106,7 +106,9 @@ bool Title::load(u64 _id, FS_MediaType _media, FS_CardType _card)
|
||||||
if (!io::directoryExists(Archive::sdmc(), mSavePath)) {
|
if (!io::directoryExists(Archive::sdmc(), mSavePath)) {
|
||||||
Result res = io::createDirectory(Archive::sdmc(), mSavePath);
|
Result res = io::createDirectory(Archive::sdmc(), mSavePath);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
Gui::showError(res, "Failed to create backup directory.");
|
loadTitle = false;
|
||||||
|
// TODO: log this
|
||||||
|
// Gui::showError(res, "Failed to create backup directory.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +118,9 @@ bool Title::load(u64 _id, FS_MediaType _media, FS_CardType _card)
|
||||||
if (!io::directoryExists(Archive::sdmc(), mExtdataPath)) {
|
if (!io::directoryExists(Archive::sdmc(), mExtdataPath)) {
|
||||||
Result res = io::createDirectory(Archive::sdmc(), mExtdataPath);
|
Result res = io::createDirectory(Archive::sdmc(), mExtdataPath);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
Gui::showError(res, "Failed to create backup directory.");
|
loadTitle = false;
|
||||||
|
// TODO: log this
|
||||||
|
// Gui::showError(res, "Failed to create backup directory.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +169,9 @@ bool Title::load(u64 _id, FS_MediaType _media, FS_CardType _card)
|
||||||
if (!io::directoryExists(Archive::sdmc(), mSavePath)) {
|
if (!io::directoryExists(Archive::sdmc(), mSavePath)) {
|
||||||
res = io::createDirectory(Archive::sdmc(), mSavePath);
|
res = io::createDirectory(Archive::sdmc(), mSavePath);
|
||||||
if (R_FAILED(res)) {
|
if (R_FAILED(res)) {
|
||||||
Gui::showError(res, "Failed to create backup directory.");
|
loadTitle = false;
|
||||||
|
// TODO: log this
|
||||||
|
// Gui::showError(res, "Failed to create backup directory.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +283,7 @@ void Title::refreshDirectories(void)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// fprintf(stderr, "Title 0x%016llX, %s\n", id(), shortDescription().c_str());
|
// fprintf(stderr, "Title 0x%016llX, %s\n", id(), shortDescription().c_str());
|
||||||
Gui::showError(savelist.error(), "Couldn't retrieve the directory list\nfor the title " + shortDescription());
|
// LOG THIS Gui::showError(savelist.error(), "Couldn't retrieve the directory list\nfor the title " + shortDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
// save backups from configuration
|
// save backups from configuration
|
||||||
|
@ -314,7 +320,7 @@ void Title::refreshDirectories(void)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// fprintf(stderr, "Title 0x%016llX, %s\n", id(), shortDescription().c_str());
|
// fprintf(stderr, "Title 0x%016llX, %s\n", id(), shortDescription().c_str());
|
||||||
Gui::showError(extlist.error(), "Couldn't retrieve the extdata list\nfor the title " + shortDescription());
|
// LOG THIS Gui::showError(extlist.error(), "Couldn't retrieve the extdata list\nfor the title " + shortDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
// extdata backups from configuration
|
// extdata backups from configuration
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
|
|
||||||
void servicesExit(void)
|
void servicesExit(void)
|
||||||
{
|
{
|
||||||
CheatManager::exit();
|
|
||||||
Gui::exit();
|
Gui::exit();
|
||||||
pxiDevExit();
|
pxiDevExit();
|
||||||
Archive::exit();
|
Archive::exit();
|
||||||
|
@ -65,7 +64,6 @@ Result servicesInit(void)
|
||||||
mkdir("sdmc:/cheats", 777);
|
mkdir("sdmc:/cheats", 777);
|
||||||
|
|
||||||
Gui::init();
|
Gui::init();
|
||||||
Threads::create((ThreadFunc)CheatManager::init);
|
|
||||||
|
|
||||||
// consoleDebugInit(debugDevice_SVC);
|
// consoleDebugInit(debugDevice_SVC);
|
||||||
// while (aptMainLoop() && !(hidKeysDown() & KEY_START)) { hidScanInput(); }
|
// while (aptMainLoop() && !(hidKeysDown() & KEY_START)) { hidScanInput(); }
|
||||||
|
|
|
@ -41,7 +41,12 @@ public:
|
||||||
Overlay(Screen& screen) : screen(screen), me(screen.currentOverlay) {}
|
Overlay(Screen& screen) : screen(screen), me(screen.currentOverlay) {}
|
||||||
virtual ~Overlay() {}
|
virtual ~Overlay() {}
|
||||||
virtual void update(touchPosition* touch) = 0;
|
virtual void update(touchPosition* touch) = 0;
|
||||||
virtual void draw() const = 0;
|
#if defined(_3DS)
|
||||||
|
virtual void drawTop() const = 0;
|
||||||
|
virtual void drawBottom() const = 0;
|
||||||
|
#elif defined(__SWITCH__)
|
||||||
|
virtual void draw() const = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Screen& screen;
|
Screen& screen;
|
||||||
|
|
|
@ -27,6 +27,26 @@
|
||||||
#include "Screen.hpp"
|
#include "Screen.hpp"
|
||||||
#include "Overlay.hpp"
|
#include "Overlay.hpp"
|
||||||
|
|
||||||
|
#if defined(_3DS)
|
||||||
|
|
||||||
|
void Screen::doDrawTop() const
|
||||||
|
{
|
||||||
|
drawTop();
|
||||||
|
if (currentOverlay) {
|
||||||
|
currentOverlay->drawTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::doDrawBottom() const
|
||||||
|
{
|
||||||
|
drawBottom();
|
||||||
|
if (currentOverlay) {
|
||||||
|
currentOverlay->drawBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__SWITCH__)
|
||||||
|
|
||||||
void Screen::doDraw() const
|
void Screen::doDraw() const
|
||||||
{
|
{
|
||||||
draw();
|
draw();
|
||||||
|
@ -35,6 +55,8 @@ void Screen::doDraw() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void Screen::doUpdate(touchPosition* touch)
|
void Screen::doUpdate(touchPosition* touch)
|
||||||
{
|
{
|
||||||
if (currentOverlay) {
|
if (currentOverlay) {
|
||||||
|
@ -43,4 +65,4 @@ void Screen::doUpdate(touchPosition* touch)
|
||||||
else {
|
else {
|
||||||
update(touch);
|
update(touch);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -40,14 +40,21 @@ class Screen {
|
||||||
friend class Overlay;
|
friend class Overlay;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Screen() {}
|
Screen(void) {}
|
||||||
virtual ~Screen() {}
|
virtual ~Screen(void) {}
|
||||||
// Call currentOverlay->update if it exists, and update if it doesn't
|
// Call currentOverlay->update if it exists, and update if it doesn't
|
||||||
virtual void doUpdate(touchPosition* touch) final;
|
virtual void doUpdate(touchPosition* touch) final;
|
||||||
virtual void update(touchPosition* touch) = 0;
|
virtual void update(touchPosition* touch) = 0;
|
||||||
// Call draw, then currentOverlay->draw if it exists
|
// Call draw, then currentOverlay->draw if it exists
|
||||||
|
#if defined(_3DS)
|
||||||
|
virtual void doDrawTop(void) const final;
|
||||||
|
virtual void doDrawBottom(void) const final;
|
||||||
|
virtual void drawTop(void) const = 0;
|
||||||
|
virtual void drawBottom(void) const = 0;
|
||||||
|
#elif defined(__SWITCH__)
|
||||||
virtual void doDraw() const final;
|
virtual void doDraw() const final;
|
||||||
virtual void draw() const = 0;
|
virtual void draw() const = 0;
|
||||||
|
#endif
|
||||||
void removeOverlay() { currentOverlay = nullptr; }
|
void removeOverlay() { currentOverlay = nullptr; }
|
||||||
void setOverlay(std::shared_ptr<Overlay>& overlay) { currentOverlay = overlay; }
|
void setOverlay(std::shared_ptr<Overlay>& overlay) { currentOverlay = overlay; }
|
||||||
|
|
||||||
|
|
|
@ -57,9 +57,6 @@ protected:
|
||||||
int selectorY(size_t i) const;
|
int selectorY(size_t i) const;
|
||||||
void updateSelector(touchPosition* touch);
|
void updateSelector(touchPosition* touch);
|
||||||
void handleEvents(touchPosition* touch);
|
void handleEvents(touchPosition* touch);
|
||||||
bool isBackupReleased(void) const;
|
|
||||||
bool isRestoreReleased(void) const;
|
|
||||||
bool isCheatReleased(void) const;
|
|
||||||
std::string nameFromCell(size_t index) const;
|
std::string nameFromCell(size_t index) const;
|
||||||
void entryType(entryType_t type);
|
void entryType(entryType_t type);
|
||||||
size_t index(entryType_t type) const;
|
size_t index(entryType_t type) const;
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include "main.hpp"
|
#include "main.hpp"
|
||||||
#include "scrollable.hpp"
|
|
||||||
#include <bzlib.h>
|
#include <bzlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
|
@ -401,7 +401,7 @@ void MainScreen::handleEvents(touchPosition* touch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle pressing/touching L
|
// Handle pressing/touching L
|
||||||
if (isBackupReleased() || (kdown & KEY_L)) {
|
if (buttonBackup->released() || (kdown & KEY_L)) {
|
||||||
if (MS::multipleSelectionEnabled()) {
|
if (MS::multipleSelectionEnabled()) {
|
||||||
resetIndex(CELLS);
|
resetIndex(CELLS);
|
||||||
std::vector<size_t> list = MS::selectedEntries();
|
std::vector<size_t> list = MS::selectedEntries();
|
||||||
|
@ -455,7 +455,7 @@ void MainScreen::handleEvents(touchPosition* touch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle pressing/touching R
|
// Handle pressing/touching R
|
||||||
if (isRestoreReleased() || (kdown & KEY_R)) {
|
if (buttonRestore->released() || (kdown & KEY_R)) {
|
||||||
if (g_backupScrollEnabled) {
|
if (g_backupScrollEnabled) {
|
||||||
if (getPKSMBridgeFlag() && this->index(CELLS) != 0) {
|
if (getPKSMBridgeFlag() && this->index(CELLS) != 0) {
|
||||||
currentOverlay = std::make_shared<YesNoOverlay>(
|
currentOverlay = std::make_shared<YesNoOverlay>(
|
||||||
|
@ -490,7 +490,7 @@ void MainScreen::handleEvents(touchPosition* touch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((isCheatReleased() || (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_RSTICK)) && CheatManager::getInstance().cheats() != nullptr) {
|
if ((buttonCheats->released() || (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_RSTICK)) && CheatManager::getInstance().cheats() != nullptr) {
|
||||||
if (MS::multipleSelectionEnabled()) {
|
if (MS::multipleSelectionEnabled()) {
|
||||||
MS::clearSelectedEntries();
|
MS::clearSelectedEntries();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
|
@ -509,21 +509,6 @@ void MainScreen::handleEvents(touchPosition* touch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainScreen::isBackupReleased(void) const
|
|
||||||
{
|
|
||||||
return buttonBackup->released();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainScreen::isRestoreReleased(void) const
|
|
||||||
{
|
|
||||||
return buttonRestore->released();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainScreen::isCheatReleased(void) const
|
|
||||||
{
|
|
||||||
return buttonCheats->released();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MainScreen::nameFromCell(size_t index) const
|
std::string MainScreen::nameFromCell(size_t index) const
|
||||||
{
|
{
|
||||||
return backupList->cellName(index);
|
return backupList->cellName(index);
|
||||||
|
|
Loading…
Reference in a new issue