Initial commit of FBI rewrite.

This commit is contained in:
Steven Smith 2016-01-28 20:39:31 -08:00
parent 0212aa9eaf
commit 30221d0ac0
88 changed files with 13649 additions and 1821 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "buildtools"] [submodule "buildtools"]
path = buildtools path = buildtools
url = git://github.com/Steveice10/buildtools url = git://github.com/Steveice10/buildtools
[submodule "source/libkhax"]
path = source/libkhax
url = git://github.com/Myriachan/libkhax

View File

@ -4,13 +4,13 @@ TARGET := 3DS
LIBRARY := 0 LIBRARY := 0
ifeq ($(TARGET),3DS) ifeq ($(TARGET),3DS)
ifeq ($(strip $(DEVKITPRO)),) ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro") $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro")
endif endif
ifeq ($(strip $(DEVKITARM)),) ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM") $(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif endif
endif endif
# COMMON CONFIGURATION # # COMMON CONFIGURATION #
@ -24,25 +24,28 @@ SOURCE_DIRS := source
EXTRA_OUTPUT_FILES := EXTRA_OUTPUT_FILES :=
LIBRARY_DIRS := $(DEVKITPRO)/citrus $(DEVKITPRO)/libctru LIBRARY_DIRS := $(DEVKITPRO)/libctru
LIBRARIES := citrus ctru m LIBRARIES := citro3d ctru m
BUILD_FLAGS := -DVERSION_STRING="\"`git describe --tags --abbrev=0`\"" BUILD_FLAGS := -DLIBKHAX_AS_LIB -DVERSION_STRING="\"`git describe --tags --abbrev=0`\""
RUN_FLAGS := RUN_FLAGS :=
# 3DS CONFIGURATION # # 3DS CONFIGURATION #
TITLE := $(NAME)
DESCRIPTION := Open source CIA installer. DESCRIPTION := Open source CIA installer.
AUTHOR := Steveice10 AUTHOR := Steveice10
PRODUCT_CODE := CTR-P-CFBI PRODUCT_CODE := CTR-P-CFBI
UNIQUE_ID := 0x1930 UNIQUE_ID := 0xF8001
SYSTEM_MODE := 64MB SYSTEM_MODE := 64MB
SYSTEM_MODE_EXT := Legacy SYSTEM_MODE_EXT := Legacy
ROMFS_DIR := ICON_FLAGS :=
ROMFS_DIR := romfs
BANNER_AUDIO := meta/audio.wav BANNER_AUDIO := meta/audio.wav
BANNER_IMAGE := meta/banner.png BANNER_IMAGE := meta/banner.cgfx
ICON := meta/icon.png ICON := meta/icon.png
# INTERNAL # # INTERNAL #

View File

@ -2,6 +2,8 @@
FBI is an open source CIA (un)installer for the 3DS. FBI is an open source CIA (un)installer for the 3DS.
Banner credit: [OctopusRift](http://gbatemp.net/members/octopusrift.356526/), [Apache Thunder](https://gbatemp.net/members/apache-thunder.105648/)
Download: https://github.com/Steveice10/FBI/releases Download: https://github.com/Steveice10/FBI/releases
Requires [devkitARM](http://sourceforge.net/projects/devkitpro/files/devkitARM/) and [citrus](https://github.com/Steveice10/citrus/) to build. Requires [devkitARM](http://sourceforge.net/projects/devkitpro/files/devkitARM/) to build.

@ -1 +1 @@
Subproject commit 2b464d8e90ba23ae6233e517eb7ec22fb797f880 Subproject commit 45803422e8c05cc8439525f0f78df34e5afe7870

BIN
meta/banner.cgfx Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

BIN
romfs/battery0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

BIN
romfs/battery1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

BIN
romfs/battery2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

BIN
romfs/battery3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

BIN
romfs/battery4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

BIN
romfs/battery5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

BIN
romfs/battery_charging.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

BIN
romfs/bottom_screen_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

BIN
romfs/button_large.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

BIN
romfs/button_small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

BIN
romfs/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

BIN
romfs/progress_bar_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

BIN
romfs/selection_overlay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

BIN
romfs/smdh_info_box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
romfs/top_screen_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

BIN
romfs/wifi0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

BIN
romfs/wifi1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
romfs/wifi2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

BIN
romfs/wifi3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

BIN
romfs/wifi_disconnected.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

34
source/default.v.pica Normal file
View File

@ -0,0 +1,34 @@
; Uniforms
.fvec projection[4]
; Constants
.constf myconst(0.0, 1.0, -1.0, 0.1)
.constf myconst2(0.3, 0.0, 0.0, 0.0)
.alias zeros myconst.xxxx ; Vector full of zeros
.alias ones myconst.yyyy ; Vector full of ones
; Outputs
.out outpos position
.out outtc0 texcoord0
; Inputs (defined as aliases for convenience)
.alias inpos v0
.alias intex v1
.bool test
.proc main
; Force the w component of inpos to be 1.0
mov r0.xyz, inpos
mov r0.w, ones
; outpos = projectionMatrix * inpos
dp4 outpos.x, projection[0], r0
dp4 outpos.y, projection[1], r0
dp4 outpos.z, projection[2], r0
dp4 outpos.w, projection[3], r0
mov outtc0, intex
end
.end

1
source/libkhax Submodule

@ -0,0 +1 @@
Subproject commit 86f43dd4b2a701faee7d6c9b8700e638911bb50d

6167
source/lodepng.c Normal file

File diff suppressed because it is too large Load Diff

1713
source/lodepng.h Normal file

File diff suppressed because it is too large Load Diff

79
source/main.c Normal file
View File

@ -0,0 +1,79 @@
#include <malloc.h>
#include <stdlib.h>
#include <3ds.h>
#include "screen.h"
#include "util.h"
#include "libkhax/khax.h"
#include "ui/mainmenu.h"
#include "ui/section/task.h"
#include "ui/section/action/clipboard.h"
static void* soc_buffer;
void cleanup() {
clipboard_clear();
task_exit();
screen_exit();
socExit();
if(soc_buffer != NULL) {
free(soc_buffer);
soc_buffer = NULL;
}
ptmuExit();
acExit();
amExit();
cfguExit();
romfsExit();
khaxExit();
gfxExit();
}
int main(int argc, const char* argv[]) {
gfxInitDefault();
if(argc > 0) {
Result res = khaxInit();
if(R_FAILED(res)) {
util_panic("Failed to acquire service access: %08lX", res);
return 1;
}
}
romfsInit();
cfguInit();
acInit();
ptmuInit();
amInit();
AM_InitializeExternalTitleDatabase(false);
soc_buffer = memalign(0x1000, 0x100000);
if(soc_buffer != NULL) {
socInit(soc_buffer, 0x100000);
}
screen_init();
task_init();
ui_view* mainmenu = mainmenu_create();
ui_push(mainmenu);
while(aptMainLoop()) {
ui_update();
if(ui_peek() == NULL) {
break;
}
ui_draw();
}
mainmenu_destroy(mainmenu);
cleanup();
return 0;
}

View File

@ -1,551 +0,0 @@
#include <citrus/app.hpp>
#include <citrus/battery.hpp>
#include <citrus/core.hpp>
#include <citrus/err.hpp>
#include <citrus/fs.hpp>
#include <citrus/gpu.hpp>
#include <citrus/gput.hpp>
#include <citrus/hid.hpp>
#include <citrus/nor.hpp>
#include <citrus/wifi.hpp>
#include "rop.h"
#include "ui.hpp"
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <iomanip>
#include <sstream>
using namespace ctr;
typedef enum {
INSTALL_CIA,
DELETE_CIA,
DELETE_TITLE,
LAUNCH_TITLE
} Mode;
std::vector<std::string> extensions = {"cia"};
bool exit = false;
bool showNetworkPrompts = true;
u64 freeSpace = 0;
fs::MediaType destination = fs::SD;
Mode mode = INSTALL_CIA;
int prevProgress = -1;
std::string installInfo = "";
bool onProgress(u64 pos, u64 totalSize) {
u32 progress = (u32) ((pos * 100) / totalSize);
if(prevProgress != (int) progress) {
prevProgress = (int) progress;
std::stringstream details;
details << installInfo;
details << "Press B to cancel.";
uiDisplayProgress(gpu::SCREEN_TOP, "Installing", details.str(), true, progress);
}
hid::poll();
return !hid::pressed(hid::BUTTON_B);
}
void networkInstall() {
while(core::running()) {
RemoteFile file = uiAcceptRemoteFile(gpu::SCREEN_TOP, [&](std::stringstream& infoStream) {
if(hid::pressed(hid::BUTTON_A)) {
showNetworkPrompts = !showNetworkPrompts;
}
infoStream << "\n";
infoStream << "Prompts: " << (showNetworkPrompts ? "Enabled" : "Disabled") << "\n";
infoStream << "Press A to toggle prompts.";
});
if(file.fd == NULL) {
break;
}
std::stringstream confirmStream;
confirmStream << "Install the received application?" << "\n";
confirmStream << "Size: " << file.fileSize << " bytes (" << std::fixed << std::setprecision(2) << file.fileSize / 1024.0f / 1024.0f << "MB)";
if(!showNetworkPrompts || uiPrompt(gpu::SCREEN_TOP, confirmStream.str(), true)) {
app::install(destination, file.fd, file.fileSize, &onProgress);
prevProgress = -1;
if(showNetworkPrompts || err::has()) {
std::stringstream resultMsg;
resultMsg << "Install ";
if(!err::has()) {
resultMsg << "succeeded!";
} else {
err::Error error = err::get();
if(error.source == err::SOURCE_OPERATION_CANCELLED) {
resultMsg << "cancelled!";
} else {
resultMsg << "failed!" << "\n";
resultMsg << err::toString(error);
}
}
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
freeSpace = fs::freeSpace(destination);
}
}
fclose(file.fd);
}
}
void installROP() {
u32 selected = 0;
bool dirty = true;
while(core::running()) {
hid::poll();
if(hid::pressed(hid::BUTTON_B)) {
break;
}
if(hid::pressed(hid::BUTTON_A)) {
std::stringstream stream;
stream << "Install the selected ROP?" << "\n";
stream << ropNames[selected];
if(uiPrompt(gpu::SCREEN_TOP, stream.str(), true)) {
u16 userSettingsOffset = 0;
nor::read(0x20, &userSettingsOffset, 2);
if(!err::has()) {
nor::write(userSettingsOffset << 3, rops[selected], ROP_SIZE);
}
std::stringstream resultMsg;
resultMsg << "ROP installation ";
if(!err::has()) {
resultMsg << "succeeded!";
} else {
resultMsg << "failed!" << "\n";
resultMsg << err::toString(err::get());
}
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
}
dirty = true;
}
if(hid::pressed(hid::BUTTON_LEFT)) {
if(selected == 0) {
selected = ROP_COUNT - 1;
} else {
selected--;
}
dirty = true;
}
if(hid::pressed(hid::BUTTON_RIGHT)) {
if(selected >= ROP_COUNT - 1) {
selected = 0;
} else {
selected++;
}
dirty = true;
}
if(dirty) {
std::stringstream stream;
stream << "Select a ROP to install." << "\n";
stream << "< " << ropNames[selected] << " >" << "\n";
stream << "Press A to install, B to cancel.";
uiDisplayMessage(gpu::SCREEN_TOP, stream.str());
dirty = false;
}
}
}
bool installCIA(fs::MediaType destination, const std::string path, int curr, int total) {
std::string name = fs::fileName(path);
if(name.length() > 40) {
name.resize(40);
name += "...";
}
FILE* fd = fopen(path.c_str(), "r");
if(!fd) {
std::stringstream resultMsg;
resultMsg << "Install failed!" << "\n";
resultMsg << name << "\n";
resultMsg << "Could not open file." << "\n";
resultMsg << strerror(errno) << "\n";
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
return false;
}
struct stat st;
fstat(fileno(fd), &st);
u64 size = (u64) (u32) st.st_size;
std::stringstream batchInstallStream;
batchInstallStream << name << " (" << curr << "/" << total << ")" << "\n";
installInfo = batchInstallStream.str();
app::install(destination, fd, size, &onProgress);
prevProgress = -1;
installInfo = "";
if(err::has()) {
err::Error error = err::get();
if(error.module == err::MODULE_NN_AM && error.description == err::DESCRIPTION_ALREADY_EXISTS) {
std::stringstream overwriteMsg;
overwriteMsg << "Title already installed, overwrite?" << "\n";
overwriteMsg << name;
if(uiPrompt(gpu::SCREEN_TOP, overwriteMsg.str(), true)) {
uiDisplayMessage(gpu::SCREEN_TOP, "Deleting title...");
app::uninstall(app::ciaInfo(path, destination));
if(err::has()) {
std::stringstream resultMsg;
resultMsg << "Delete failed!" << "\n";
resultMsg << name << "\n";
resultMsg << err::toString(err::get());
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
return false;
}
fseek(fd, 0, SEEK_SET);
installInfo = batchInstallStream.str();
app::install(destination, fd, size, &onProgress);
prevProgress = -1;
installInfo = "";
} else {
err::set(error);
}
} else {
err::set(error);
}
}
if(err::has()) {
err::Error error = err::get();
std::stringstream resultMsg;
resultMsg << "Install ";
if(error.source == err::SOURCE_OPERATION_CANCELLED) {
resultMsg << "cancelled!" << "\n";
resultMsg << name;
} else {
resultMsg << "failed!" << "\n";
resultMsg << name << "\n";
resultMsg << err::toString(error);
}
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
return false;
}
return true;
}
bool deleteCIA(const std::string path, int curr, int total) {
std::string name = fs::fileName(path);
if(name.length() > 40) {
name.resize(40);
name += "...";
}
std::stringstream deleteStream;
deleteStream << "Deleting CIA..." << "\n";
deleteStream << name << " (" << curr << "/" << total << ")";
uiDisplayMessage(gpu::SCREEN_TOP, deleteStream.str());
if(remove(path.c_str()) != 0) {
std::stringstream resultMsg;
resultMsg << "Delete failed!" << "\n";
resultMsg << name << "\n";
resultMsg << strerror(errno);
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
return false;
}
return true;
}
bool deleteTitle(app::App app) {
uiDisplayMessage(gpu::SCREEN_TOP, "Deleting title...");
app::uninstall(app);
if(err::has()) {
std::stringstream resultMsg;
resultMsg << "Delete failed!" << "\n";
resultMsg << err::toString(err::get());
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
return false;
}
return true;
}
bool launchTitle(app::App app) {
uiDisplayMessage(gpu::SCREEN_TOP, "Launching title...");
app::launch(app);
if(err::has()) {
std::stringstream resultMsg;
resultMsg << "Launch failed!" << "\n";
resultMsg << err::toString(err::get());
uiPrompt(gpu::SCREEN_TOP, resultMsg.str(), false);
return false;
}
return true;
}
bool onLoop() {
bool launcher = core::launcher();
if(launcher && hid::pressed(hid::BUTTON_START)) {
exit = true;
return true;
}
bool breakLoop = false;
if(hid::pressed(hid::BUTTON_L)) {
if(destination == fs::SD) {
destination = fs::NAND;
} else {
destination = fs::SD;
}
freeSpace = fs::freeSpace(destination);
if(mode == DELETE_TITLE || mode == LAUNCH_TITLE) {
breakLoop = true;
}
}
if(hid::pressed(hid::BUTTON_R)) {
if(mode == INSTALL_CIA) {
mode = DELETE_CIA;
} else if(mode == DELETE_CIA) {
mode = DELETE_TITLE;
breakLoop = true;
} else if(mode == DELETE_TITLE) {
mode = LAUNCH_TITLE;
} else if(mode == LAUNCH_TITLE) {
mode = INSTALL_CIA;
breakLoop = true;
}
}
if(mode == INSTALL_CIA && hid::pressed(hid::BUTTON_Y)) {
networkInstall();
}
if(hid::pressed(hid::BUTTON_SELECT)) {
installROP();
}
std::stringstream stream;
stream << "Battery: ";
if(battery::charging()) {
stream << "Charging";
} else {
for(u32 i = 0; i < battery::level(); i++) {
stream << "|";
}
}
stream << ", WiFi: ";
if(!wifi::connected()) {
stream << "Disconnected";
} else {
for(u32 i = 0; i < wifi::strength(); i++) {
stream << "|";
}
}
stream << "\n";
stream << "Free Space: " << freeSpace << " bytes (" << std::fixed << std::setprecision(2) << freeSpace / 1024.0f / 1024.0f << "MB)" << "\n";
stream << "Destination: " << (destination == fs::NAND ? "NAND" : "SD") << ", Mode: " << (mode == INSTALL_CIA ? "Install CIA" : mode == DELETE_CIA ? "Delete CIA" : mode == DELETE_TITLE ? "Delete Title" : "Launch Title") << "\n";
stream << "L - Switch Destination, R - Switch Mode" << "\n";
if(mode == INSTALL_CIA) {
stream << "X - Install all CIAs in the current directory" << "\n";
stream << "Y - Receive an app over the network" << "\n";
} else if(mode == DELETE_CIA) {
stream << "X - Delete all CIAs in the current directory" << "\n";
}
stream << "SELECT - Install MSET ROP";
if(launcher) {
stream << "\n" << "START - Exit to launcher";
}
std::string str = stream.str();
std::stringstream titleStream;
titleStream << "FBI " << VERSION_STRING;
std::string title = titleStream.str();
u32 screenWidth;
u32 screenHeight;
gpu::getViewportWidth(&screenWidth);
gpu::getViewportHeight(&screenHeight);
gput::drawString(title, (screenWidth - gput::getStringWidth(title, 16)) / 2, (screenHeight - gput::getStringHeight(title, 16) + gput::getStringHeight(str, 8)) / 2, 16, 16);
gput::drawString(str, (screenWidth - gput::getStringWidth(str, 8)) / 2, 4, 8, 8);
return breakLoop;
}
int main(int argc, char **argv) {
if(!core::init(argc)) {
return 0;
}
uiInit();
freeSpace = fs::freeSpace(destination);
while(core::running()) {
if(mode == INSTALL_CIA || mode == DELETE_CIA) {
uiDisplayMessage(gpu::SCREEN_BOTTOM, "Loading file list...");
std::string fileTarget;
uiSelectFile(&fileTarget, "/", extensions, [&](const std::string currDirectory, bool inRoot, bool &updateList) {
if(hid::pressed(hid::BUTTON_X)) {
std::stringstream confirmMsg;
if(mode == INSTALL_CIA) {
confirmMsg << "Install ";
} else {
confirmMsg << "Delete ";
}
confirmMsg << "all CIAs in the current directory?";
if(uiPrompt(gpu::SCREEN_TOP, confirmMsg.str(), true)) {
bool failed = false;
std::vector<std::string> contents = fs::contents(currDirectory, false, extensions);
int total = contents.size();
int curr = 1;
for(std::vector<std::string>::iterator it = contents.begin(); it != contents.end(); it++) {
std::string path = *it;
if(mode == INSTALL_CIA) {
if(!installCIA(destination, path, curr, total)) {
failed = true;
break;
}
} else {
if(deleteCIA(path, curr, total)) {
updateList = true;
} else {
failed = true;
break;
}
}
curr++;
}
if(!failed) {
std::stringstream successMsg;
if(mode == INSTALL_CIA) {
successMsg << "Install ";
} else {
successMsg << "Delete ";
}
successMsg << "succeeded!";
uiPrompt(gpu::SCREEN_TOP, successMsg.str(), false);
}
freeSpace = fs::freeSpace(destination);
}
}
return onLoop();
}, [&](const std::string path, bool &updateList) {
std::stringstream confirmMsg;
if(mode == INSTALL_CIA) {
confirmMsg << "Install ";
} else {
confirmMsg << "Delete ";
}
confirmMsg << "the selected CIA?";
if(uiPrompt(gpu::SCREEN_TOP, confirmMsg.str(), true)) {
bool success = false;
if(mode == INSTALL_CIA) {
success = installCIA(destination, path, 1, 1);
} else {
success = deleteCIA(path, 1, 1);
}
if(success) {
std::stringstream successMsg;
if(mode == INSTALL_CIA) {
successMsg << "Install ";
} else {
successMsg << "Delete ";
}
successMsg << "succeeded!";
uiPrompt(gpu::SCREEN_TOP, successMsg.str(), false);
}
freeSpace = fs::freeSpace(destination);
}
return false;
});
} else if(mode == DELETE_TITLE || mode == LAUNCH_TITLE) {
uiDisplayMessage(gpu::SCREEN_BOTTOM, "Loading title list...");
app::App appTarget;
uiSelectApp(&appTarget, destination, [&](bool &updateList) {
return onLoop();
}, [&](app::App app, bool &updateList) {
if(mode == DELETE_TITLE) {
if(uiPrompt(gpu::SCREEN_TOP, "Delete the selected title?", true) && (destination != fs::NAND || uiPrompt(gpu::SCREEN_TOP, "You are about to delete a title from the NAND.\nTHIS HAS THE POTENTIAL TO BRICK YOUR 3DS!\nAre you sure you wish to continue?", true))) {
if(deleteTitle(app)) {
uiPrompt(gpu::SCREEN_TOP, "Delete succeeded!", false);
}
updateList = true;
freeSpace = fs::freeSpace(destination);
}
} else if(mode == LAUNCH_TITLE) {
if(uiPrompt(gpu::SCREEN_TOP, "Launch the selected title?", true)) {
if(launchTitle(app)) {
while(true) {
}
}
}
}
return false;
});
}
if(exit) {
break;
}
}
uiCleanup();
core::exit();
return 0;
}

View File

@ -1,500 +0,0 @@
#pragma once
#define ROP_SIZE 0x200
#define ROP_COUNT 14
const char* ropNames[ROP_COUNT] = {
"Encrypted Gateway 4.x",
"Decrypted Gateway 4.x",
"Encrypted Gateway N3DS 9.x",
"rxTools2.x 4.x",
"rxTools3.x 4.x",
"rxTools3.x 6.x",
"rxTools3.x N3DS 9.x",
"ReiNand 1.0 4.x",
"ReiNand N3DS 9.x",
"CakesFW 4.x",
"CakesFW 6.x",
"CakesFW N3DS 9.x",
"MsetForBoss 4.x",
"MsetForBoss 6.x"
};
unsigned char rops[ROP_COUNT][ROP_SIZE] = {
{
0xB9, 0xF2, 0x10, 0x00, 0xAE, 0x2B, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0x90, 0xB6, 0x10, 0x00, 0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x20, 0x00, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x38, 0x6F, 0x27, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xDC, 0xD5, 0x18, 0x00, 0x40, 0x83, 0x27, 0x00, 0x00, 0x02, 0x10, 0x00,
0xCC, 0x48, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00,
0x00, 0x00, 0x2B, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x51, 0x00, 0xCD, 0xC2, 0xE1, 0x49, 0x15, 0x00, 0x20, 0x90, 0x27, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x00, 0x90, 0x00, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00,
0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00, 0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00,
0x91, 0xFE, 0x16, 0x00, 0x00, 0x01, 0x10, 0x00, 0xBC, 0x4C, 0x14, 0x00, 0x00, 0x00, 0x2B, 0x00,
0x00, 0x90, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0xAC, 0xEF, 0x22, 0x00, 0x88, 0x5C, 0x10, 0x00,
0x00, 0x00, 0x0E, 0x00, 0x90, 0x03, 0x25, 0x00, 0xC0, 0xFA, 0x1E, 0x00, 0x91, 0xFE, 0x16, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x24, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x95, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0x5E, 0xD3,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x4D, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x68, 0x00,
0x65, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x02, 0x19, 0x02, 0x19, 0x13, 0xF8, 0x0D,
0xD6, 0x0D, 0xE6, 0xAC, 0x01, 0xFC, 0x00, 0x00, 0xC2, 0x23, 0xF8, 0x0E, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x43, 0x28, 0x01, 0x01, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xD0, 0x8C, 0x1E, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x95, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0xF0, 0xE8,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAE, 0x2B, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x38, 0x6F, 0x27, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00,
0x00, 0x00, 0x2B, 0x00, 0x8C, 0x53, 0x10, 0x00, 0x00, 0x90, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0x8C, 0x53, 0x10, 0x00, 0x28, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00,
0x07, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x4D, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x68, 0x00,
0x65, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x01, 0x34, 0x02, 0x19, 0x13, 0xC0, 0x0D,
0xEA, 0x0D, 0xE6, 0xAC, 0x01, 0xFC, 0x00, 0x00, 0xF0, 0xCD, 0x43, 0x18, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x1B, 0x7E, 0x01, 0x01, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x95, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xB1, 0x49, 0x15, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x95, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0x31, 0xF1,
},
{
0x8C, 0x3D, 0x14, 0x00, 0xAE, 0x2B, 0x27, 0x00, 0x9C, 0xF1, 0x18, 0x00, 0x90, 0xB6, 0x10, 0x00,
0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x20, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00,
0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x38, 0x6F, 0x27, 0x00, 0xAC, 0x82, 0x1B, 0x00,
0xDC, 0xD5, 0x18, 0x00, 0x40, 0x83, 0x27, 0x00, 0x00, 0x02, 0x10, 0x00, 0xCC, 0x48, 0x00, 0x00,
0x60, 0x3D, 0x14, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x04, 0x90, 0x27, 0x00, 0x00, 0x80, 0x04, 0x00,
0x88, 0x11, 0x20, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00,
0x00, 0x00, 0x2B, 0x00, 0x8C, 0x53, 0x10, 0x00, 0x00, 0x90, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x51, 0x00, 0x94, 0x53, 0xE1, 0x49, 0x15, 0x00, 0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00,
0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00, 0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00,
0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00, 0x9B, 0x44, 0x1B, 0x00, 0x00, 0x01, 0x10, 0x00,
0xBC, 0x4C, 0x14, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x90, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0xFE, 0xFE, 0xFE, 0xFE, 0xAC, 0xEF, 0x22, 0x00, 0x88, 0x5C, 0x10, 0x00, 0x00, 0x00, 0x0E, 0x00,
0x90, 0x03, 0x25, 0x00, 0xC0, 0xFA, 0x1E, 0x00, 0x91, 0xFE, 0x16, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x28, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x01, 0x00, 0x3F, 0x00,
0x00, 0x00, 0x7E, 0x9A, 0x3A, 0x00, 0x2F, 0x00, 0x64, 0x00, 0x61, 0x00, 0x9C, 0xF1, 0x18, 0x00,
0x1D, 0x00, 0x72, 0x00, 0x52, 0x00, 0x37, 0x3A, 0x00, 0x90, 0x27, 0x00, 0xAE, 0x2B, 0x33, 0x05,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x72, 0x00, 0x78, 0x00, 0x54, 0x00, 0x6F, 0x00, 0x6F, 0x00,
0x6C, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x72, 0x00, 0x78, 0x00, 0x54, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6C, 0x00,
0x73, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x01, 0xF2, 0x01, 0x19, 0x13, 0xF9, 0x0D,
0xE6, 0x0D, 0xE6, 0xAC, 0x00, 0xFE, 0x00, 0x01, 0xA2, 0x40, 0xD1, 0x12, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0xBB, 0xD8, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xD0, 0x8C, 0x1E, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x01, 0x00, 0x3F, 0x00,
0x00, 0x90, 0x00, 0x00, 0x3A, 0x00, 0x2F, 0x00, 0x64, 0x00, 0x61, 0x00, 0x9C, 0xF1, 0x18, 0x00,
0x1D, 0x00, 0x72, 0x00, 0x52, 0x00, 0x37, 0x3A, 0x00, 0x90, 0x27, 0x00, 0xAE, 0x2B, 0x2F, 0xBF,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAE, 0x2B, 0x27, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x9C, 0xF1, 0x18, 0x00,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x0C, 0x5E, 0x29, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00,
0x00, 0x00, 0x2B, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x20, 0x90, 0x27, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x00, 0x90, 0x00, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0x1A, 0x83, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0x91, 0xFE, 0x16, 0x00, 0xAC, 0xEF, 0x22, 0x00, 0xBC, 0x4C, 0x14, 0x00, 0x00, 0x00, 0x2B, 0x00,
0x00, 0x90, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0xAC, 0xEF, 0x22, 0x00, 0xAC, 0xEF, 0x22, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x60, 0x14, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x90, 0x00, 0x00,
0xF9, 0x02, 0x10, 0x00, 0x8C, 0x53, 0x10, 0x00, 0x40, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xF1, 0x8B,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x72, 0x00, 0x78, 0x00, 0x54, 0x00, 0x6F, 0x00, 0x6F, 0x00,
0x6C, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x72, 0x00, 0x78, 0x00, 0x54, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6C, 0x00,
0x73, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x65, 0x02, 0x19, 0x13, 0xE0, 0x0D,
0x11, 0x0E, 0xE6, 0xAC, 0x00, 0xFE, 0x00, 0x01, 0xA5, 0x4F, 0x4A, 0x12, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x6B, 0xC1, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xE1, 0x37,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAC, 0x94, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0x90, 0xB6, 0x10, 0x00, 0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x20, 0x00, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x0C, 0x5E, 0x29, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xDC, 0xD5, 0x18, 0x00, 0x40, 0x83, 0x27, 0x00, 0x00, 0x02, 0x10, 0x00,
0xCC, 0x48, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x00, 0x40, 0x00, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x04, 0x90, 0x27, 0x00, 0xBC, 0xCC, 0x10, 0x00, 0x44, 0x44, 0x44, 0x44,
0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x51, 0x00, 0x3F, 0xD9, 0x8C, 0x53, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00,
0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00, 0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x54, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x59, 0x53, 0x3A, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x04,
0x05, 0x00, 0x0F, 0x0A, 0x01, 0x00, 0xB3, 0x30, 0xA6, 0x30, 0xB9, 0x30, 0xB1, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x72, 0x00, 0x78, 0x00, 0x54, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6C, 0x00,
0x73, 0x00, 0x2F, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x2F, 0x00, 0x63, 0x00, 0x6F, 0x00,
0x64, 0x00, 0x65, 0x00, 0x2E, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x02, 0x3D, 0x02, 0x19, 0x13, 0xEE, 0x0D,
0x10, 0x0E, 0xE6, 0xAC, 0x00, 0xFC, 0x00, 0x00, 0x26, 0x28, 0xCE, 0x13, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x92, 0x4D, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x18,
},
{
0x49, 0x93, 0x1A, 0x00, 0x52, 0x02, 0x28, 0x00, 0x98, 0xB4, 0x19, 0x00, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0x04, 0x42, 0x2A, 0x00,
0x01, 0x00, 0x00, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0x49, 0x93, 0x1A, 0x00, 0x00, 0x70, 0x28, 0x00,
0xB8, 0x08, 0x1C, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0x04, 0xD8, 0x1C, 0x00, 0x00, 0x50, 0x00, 0x00,
0x49, 0x93, 0x1A, 0x00, 0x04, 0x70, 0x28, 0x00, 0x5C, 0xCF, 0x10, 0x00, 0x44, 0x44, 0x44, 0x44,
0x49, 0x93, 0x1A, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0xAD, 0xDE, 0xAD, 0xDE,
0x51, 0x00, 0xC9, 0x6D, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0x20, 0x70, 0x28, 0x00,
0x00, 0x00, 0x29, 0x00, 0x00, 0x04, 0x00, 0x00, 0x49, 0x93, 0x1A, 0x00, 0x00, 0x70, 0x28, 0x00,
0x8C, 0xC1, 0x1B, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xA4, 0x4B, 0x14, 0x00,
0x4C, 0x8F, 0xFF, 0xFF, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xD5, 0x73,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x4E, 0x00, 0x61, 0x00,
0x6E, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x72, 0x00, 0x78, 0x00, 0x54, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6C, 0x00,
0x73, 0x00, 0x2F, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x2F, 0x00, 0x63, 0x00, 0x6F, 0x00,
0x64, 0x00, 0x65, 0x00, 0x2E, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x02, 0x3D, 0x02, 0x19, 0x13, 0xEE, 0x0D,
0x10, 0x0E, 0xE6, 0xAC, 0x00, 0xFE, 0x00, 0x01, 0x26, 0x28, 0xCE, 0x13, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0xA3, 0x79, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF8, 0x4C, 0x14, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x4D, 0xBE, 0x11, 0x00,
0x00, 0x70, 0x29, 0x00, 0x00, 0x01, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x38, 0x38, 0x13, 0x00,
0x44, 0x44, 0x44, 0x44, 0xA4, 0x4B, 0x14, 0x00, 0x9C, 0x70, 0x29, 0xF0, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0x7B, 0xEC,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAC, 0x94, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0x90, 0xB6, 0x10, 0x00, 0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x20, 0x00, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x0C, 0x5E, 0x29, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xDC, 0xD5, 0x18, 0x00, 0x40, 0x83, 0x27, 0x00, 0x00, 0x02, 0x10, 0x00,
0xCC, 0x48, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x00, 0x48, 0x00, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x04, 0x90, 0x27, 0x00, 0xBC, 0xCC, 0x10, 0x00, 0x44, 0x44, 0x44, 0x44,
0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x51, 0x00, 0x56, 0x18, 0x8C, 0x53, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00,
0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00, 0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x54, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x59, 0x53, 0x3A, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x94, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0xBF, 0x2D,
0x05, 0x00, 0x0A, 0x03, 0x0F, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x72, 0x00, 0x78, 0x00, 0x54, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6C, 0x00,
0x73, 0x00, 0x2F, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x2F, 0x00, 0x63, 0x00, 0x6F, 0x00,
0x64, 0x00, 0x65, 0x00, 0x2E, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x01, 0x13, 0x02, 0x19, 0x13, 0xE1, 0x0D,
0xD3, 0x0D, 0xE6, 0xAC, 0x00, 0xFE, 0x00, 0x01, 0xFD, 0xA5, 0xCE, 0x12, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x2B, 0x91, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x94, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0x28, 0x31,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAE, 0x2B, 0x27, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x9C, 0xF1, 0x18, 0x00,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x0C, 0x5E, 0x29, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00,
0x00, 0x00, 0x2B, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x20, 0x90, 0x27, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x00, 0x90, 0x00, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0x1A, 0x83, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0x91, 0xFE, 0x16, 0x00, 0xAC, 0xEF, 0x22, 0x00, 0xBC, 0x4C, 0x14, 0x00, 0x00, 0x00, 0x2B, 0x00,
0x00, 0x90, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0xAC, 0xEF, 0x22, 0x00, 0xAC, 0xEF, 0x22, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x60, 0x14, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x90, 0x00, 0x00,
0xF9, 0x02, 0x10, 0x00, 0x8C, 0x53, 0x10, 0x00, 0x40, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xF1, 0x8B,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x4E, 0x00, 0x61, 0x00,
0x6E, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6E, 0x00,
0x64, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x65, 0x02, 0x19, 0x13, 0xE0, 0x0D,
0x11, 0x0E, 0xE6, 0xAC, 0x00, 0xFE, 0x00, 0x01, 0xA5, 0x4F, 0x4A, 0x12, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0xED, 0x33, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xE1, 0x37,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAC, 0x94, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0x90, 0xB6, 0x10, 0x00, 0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x20, 0x00, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x0C, 0x5E, 0x29, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xDC, 0xD5, 0x18, 0x00, 0x40, 0x83, 0x27, 0x00, 0x00, 0x02, 0x10, 0x00,
0xCC, 0x48, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x00, 0x48, 0x00, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x04, 0x90, 0x27, 0x00, 0xBC, 0xCC, 0x10, 0x00, 0x44, 0x44, 0x44, 0x44,
0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x51, 0x00, 0x56, 0x18, 0x8C, 0x53, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00,
0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00, 0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x54, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x59, 0x53, 0x3A, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x94, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0xBF, 0x2D,
0x05, 0x00, 0x0A, 0x03, 0x0F, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6E, 0x00,
0x64, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x01, 0x13, 0x02, 0x19, 0x13, 0xE1, 0x0D,
0xD3, 0x0D, 0xE6, 0xAC, 0x00, 0xFE, 0x00, 0x01, 0xFD, 0xA5, 0xCE, 0x12, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x83, 0xF7, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x94, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0x28, 0x31,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAC, 0x94, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0x90, 0xB6, 0x10, 0x00, 0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x20, 0x00, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x0C, 0x5E, 0x29, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xDC, 0xD5, 0x18, 0x00, 0x40, 0x83, 0x27, 0x00, 0x00, 0x02, 0x10, 0x00,
0xCC, 0x48, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x00, 0x40, 0x00, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x04, 0x90, 0x27, 0x00, 0xBC, 0xCC, 0x10, 0x00, 0x44, 0x44, 0x44, 0x44,
0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x51, 0x00, 0x3F, 0xD9, 0x8C, 0x53, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00,
0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00, 0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x54, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x59, 0x53, 0x3A, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xD0, 0x8C, 0x1E, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x07,
0x05, 0x00, 0x0F, 0x0A, 0x01, 0x00, 0xB3, 0x30, 0xA6, 0x30, 0xB9, 0x30, 0xB1, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2E, 0x00,
0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x47, 0x00, 0x20, 0x00, 0x62, 0x00, 0x69, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x02, 0x3D, 0x02, 0x19, 0x13, 0xEE, 0x0D,
0x10, 0x0E, 0xE6, 0xAC, 0x00, 0xFC, 0x00, 0x00, 0x26, 0x28, 0xCE, 0x13, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0xDC, 0x4C, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x18,
},
{
0x49, 0x93, 0x1A, 0x00, 0x52, 0x02, 0x28, 0x00, 0x98, 0xB4, 0x19, 0x00, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0x04, 0x42, 0x2A, 0x00,
0x01, 0x00, 0x00, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0x49, 0x93, 0x1A, 0x00, 0x00, 0x70, 0x28, 0x00,
0xB8, 0x08, 0x1C, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0x04, 0xD8, 0x1C, 0x00, 0x00, 0x50, 0x00, 0x00,
0x49, 0x93, 0x1A, 0x00, 0x04, 0x70, 0x28, 0x00, 0x5C, 0xCF, 0x10, 0x00, 0x44, 0x44, 0x44, 0x44,
0x49, 0x93, 0x1A, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0xAD, 0xDE, 0xAD, 0xDE,
0x51, 0x00, 0xC9, 0x6D, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0x20, 0x70, 0x28, 0x00,
0x00, 0x00, 0x29, 0x00, 0x00, 0x04, 0x00, 0x00, 0x49, 0x93, 0x1A, 0x00, 0x00, 0x70, 0x28, 0x00,
0x8C, 0xC1, 0x1B, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xA4, 0x4B, 0x14, 0x00,
0x4C, 0x8F, 0xFF, 0xFF, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xD0, 0x8C, 0x1E, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x5F,
0x05, 0x00, 0x0F, 0x0A, 0x01, 0x00, 0xB3, 0x30, 0xA6, 0x30, 0xB9, 0x30, 0xB1, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2E, 0x00,
0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x47, 0x00, 0x20, 0x00, 0x62, 0x00, 0x69, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x02, 0x3D, 0x02, 0x19, 0x13, 0xEE, 0x0D,
0x10, 0x0E, 0xE6, 0xAC, 0x00, 0xFC, 0x00, 0x00, 0x26, 0x28, 0xCE, 0x13, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0xDC, 0x4C, 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF8, 0x4C, 0x14, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x4D, 0xBE, 0x11, 0x00,
0x00, 0x70, 0x29, 0x00, 0x00, 0x01, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x38, 0x38, 0x13, 0x00,
0x44, 0x44, 0x44, 0x44, 0xA4, 0x4B, 0x14, 0x00, 0x9C, 0x70, 0x29, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x2B,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAC, 0x94, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0x90, 0xB6, 0x10, 0x00, 0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x20, 0x00, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x0C, 0x5E, 0x29, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xDC, 0xD5, 0x18, 0x00, 0x40, 0x83, 0x27, 0x00, 0x00, 0x02, 0x10, 0x00,
0xCC, 0x48, 0x00, 0x00, 0x60, 0x3D, 0x14, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x00, 0x48, 0x00, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x04, 0x90, 0x27, 0x00, 0xBC, 0xCC, 0x10, 0x00, 0x44, 0x44, 0x44, 0x44,
0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x51, 0x00, 0x56, 0x18, 0x8C, 0x53, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00,
0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00, 0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x54, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x59, 0x53, 0x3A, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x94, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0xBF, 0x2D,
0x05, 0xF2, 0x10, 0x01, 0x1F, 0x94, 0x27, 0x00, 0xED, 0x0D, 0xDC, 0xBA, 0x9C, 0xF1, 0x18, 0x00,
0x90, 0xB6, 0x10, 0x00, 0x00, 0xB0, 0xFA, 0x00, 0x00, 0x02, 0x0A, 0x00, 0x59, 0x00, 0x53, 0x00,
0x3A, 0x00, 0x2F, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2E, 0x00,
0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x47, 0x00, 0x20, 0x00, 0x00, 0x48, 0x00, 0x00,
0x6E, 0x00, 0x14, 0x00, 0x04, 0x90, 0x27, 0x00, 0xE5, 0x01, 0x13, 0x02, 0x19, 0x13, 0xE1, 0x0D,
0xD3, 0x0D, 0xE6, 0xAC, 0x00, 0xFE, 0x00, 0x01, 0x2C, 0x5B, 0x0E, 0x1A, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0xFC, 0xE4, 0x01, 0x00, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00,
0x20, 0x90, 0x27, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xE5, 0x04, 0x21, 0x00, 0x00, 0xDA, 0x19, 0x00,
0x00, 0x75, 0x01, 0x00, 0x86, 0xDF, 0x21, 0x00, 0x00, 0xC1, 0x1A, 0x00, 0x22, 0xDA, 0x1D, 0x00,
0x8C, 0x53, 0x10, 0x00, 0x54, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0x59, 0x53, 0x3A, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xC4, 0x4F, 0x1C, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xFE, 0xFF, 0xFF, 0xFF,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x94, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0x48, 0x43,
},
{
0xB9, 0xF2, 0x10, 0x00, 0xAE, 0x2B, 0x27, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x9C, 0xF1, 0x18, 0x00,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xB9, 0xF2, 0x10, 0x00,
0x00, 0x90, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x64, 0x6F, 0x27, 0x00,
0xAC, 0x82, 0x1B, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0x90, 0x27, 0x00,
0x00, 0x00, 0x2B, 0x00, 0xE1, 0x49, 0x15, 0x00, 0x20, 0x90, 0x27, 0x00, 0x8C, 0x53, 0x10, 0x00,
0x00, 0x90, 0x00, 0x00, 0x58, 0x39, 0x1B, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0xED, 0x0D, 0xDC, 0xBA, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
0x8C, 0x53, 0x10, 0x00, 0x74, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x4D, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x68, 0x00,
0x65, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x02, 0x47, 0x02, 0x19, 0x13, 0xEC, 0x0D,
0x25, 0x0E, 0xE6, 0xAC, 0x01, 0xFC, 0x00, 0x00, 0xCA, 0x48, 0x2E, 0x0E, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x95, 0x60, 0x01, 0x01, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB9, 0xF2, 0x10, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
0xE1, 0x49, 0x15, 0x00, 0x00, 0x94, 0x27, 0x00, 0xFC, 0x34, 0x13, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0x8C, 0x53, 0x10, 0x00, 0x9C, 0x94, 0x27, 0xF0, 0x60, 0x3D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC8, 0x59,
},
{
0x49, 0x93, 0x1A, 0x00, 0x52, 0x02, 0x28, 0x00, 0x98, 0xB4, 0x19, 0x00, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0xD8, 0x46, 0x28, 0x00,
0x01, 0x00, 0x00, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0x49, 0x93, 0x1A, 0x00, 0x00, 0x70, 0x28, 0x00,
0xB8, 0x08, 0x1C, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE,
0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0x4D, 0xBE, 0x11, 0x00, 0x20, 0x70, 0x28, 0x00,
0x00, 0x00, 0x29, 0x00, 0x00, 0x70, 0x00, 0x00, 0x49, 0x93, 0x1A, 0x00, 0x00, 0x70, 0x28, 0x00,
0x8C, 0xC1, 0x1B, 0x00, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE,
0xED, 0x0D, 0xDC, 0xBA, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xA4, 0x4B, 0x14, 0x00,
0x7C, 0x8F, 0xFF, 0xFF, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00, 0xF9, 0x02, 0x10, 0x00,
0xF9, 0x02, 0x10, 0x00, 0x8C, 0x53, 0x10, 0x00, 0x28, 0x6B, 0x03, 0x00, 0x60, 0x3D, 0x14, 0x00,
0x07, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x05, 0x00, 0x0F, 0x05, 0x1D, 0x00, 0x4D, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x68, 0x00,
0x65, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x01, 0x34, 0x02, 0x19, 0x13, 0xC0, 0x0D,
0xEA, 0x0D, 0xE6, 0xAC, 0x01, 0xFC, 0x00, 0x00, 0xF0, 0xCD, 0x43, 0x18, 0x00, 0x00, 0x00, 0x00,
0x52, 0x00, 0x1B, 0x7E, 0x01, 0x01, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4D, 0xBE, 0x11, 0x00, 0x00, 0x70, 0x29, 0x00, 0x00, 0x01, 0x00, 0x00,
0xAD, 0xDE, 0xAD, 0xDE, 0x49, 0x93, 0x1A, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x38, 0x38, 0x13, 0x00,
0xAD, 0xDE, 0xAD, 0xDE, 0xA4, 0x4B, 0x14, 0x00, 0x9C, 0x70, 0x29, 0xF0, 0x00, 0x95, 0x27, 0x00,
0x8C, 0x3D, 0x14, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x90, 0xB6, 0x10, 0x00, 0xF0, 0x93, 0x27, 0x00,
0xE8, 0x93, 0x27, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0xFB, 0x19, 0x00, 0x00, 0x00, 0x31, 0xF1,
}
};

617
source/screen.c Normal file
View File

@ -0,0 +1,617 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include <citro3d.h>
#include "screen.h"
#include "lodepng.h"
#include "util.h"
#include "default_shbin.h"
GX_TRANSFER_FORMAT gpuToGxFormat[13] = {
GX_TRANSFER_FMT_RGBA8,
GX_TRANSFER_FMT_RGB8,
GX_TRANSFER_FMT_RGB5A1,
GX_TRANSFER_FMT_RGB565,
GX_TRANSFER_FMT_RGBA4,
GX_TRANSFER_FMT_RGBA8, // Unsupported
GX_TRANSFER_FMT_RGBA8, // Unsupported
GX_TRANSFER_FMT_RGBA8, // Unsupported
GX_TRANSFER_FMT_RGBA8, // Unsupported
GX_TRANSFER_FMT_RGBA8, // Unsupported
GX_TRANSFER_FMT_RGBA8, // Unsupported
GX_TRANSFER_FMT_RGBA8, // Unsupported
GX_TRANSFER_FMT_RGBA8 // Unsupported
};
static bool c3dInitialized;
static bool shaderInitialized;
static DVLB_s* dvlb;
static shaderProgram_s program;
static C3D_RenderTarget* target_top;
static C3D_RenderTarget* target_bottom;
static C3D_Mtx projection_top;
static C3D_Mtx projection_bottom;
static struct {
bool initialized;
C3D_Tex tex;
u32 width;
u32 height;
u32 pow2Width;
u32 pow2Height;
} textures[MAX_TEXTURES];
static C3D_Tex* glyphSheets;
void screen_init() {
if(!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE)) {
util_panic("Failed to initialize the GPU.");
return;
}
c3dInitialized = true;
target_top = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
if(target_top == NULL) {
util_panic("Failed to initialize the top screen target.");
return;
}
u32 displayFlags = GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO);
C3D_RenderTargetSetClear(target_top, C3D_CLEAR_ALL, 0, 0);
C3D_RenderTargetSetOutput(target_top, GFX_TOP, GFX_LEFT, displayFlags);
target_bottom = C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
if(target_bottom == NULL) {
util_panic("Failed to initialize the bottom screen target.");
return;
}
C3D_RenderTargetSetClear(target_bottom, C3D_CLEAR_ALL, 0, 0);
C3D_RenderTargetSetOutput(target_bottom, GFX_BOTTOM, GFX_LEFT, displayFlags);
dvlb = DVLB_ParseFile((u32*) default_shbin, default_shbin_size);
if(dvlb == NULL) {
util_panic("Failed to parse shader.");
return;
}
Result progInitRes = shaderProgramInit(&program);
if(R_FAILED(progInitRes)) {
util_panic("Failed to initialize shader program: 0x%08lX", progInitRes);
return;
}
shaderInitialized = true;
Result progSetVshRes = shaderProgramSetVsh(&program, &dvlb->DVLE[0]);
if(R_FAILED(progSetVshRes)) {
util_panic("Failed to set up vertex shader: 0x%08lX", progInitRes);
return;
}
C3D_BindProgram(&program);
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
if(attrInfo == NULL) {
util_panic("Failed to retrieve attribute info.");
return;
}
AttrInfo_Init(attrInfo);
AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);
AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2);
C3D_TexEnv* env = C3D_GetTexEnv(0);
if(env == NULL) {
util_panic("Failed to retrieve combiner settings.");
return;
}
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0);
C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL);
Mtx_OrthoTilt(&projection_top, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0);
Mtx_OrthoTilt(&projection_bottom, 0.0, 320.0, 240.0, 0.0, 0.0, 1.0);
Result fontMapRes = fontEnsureMapped();
if(R_FAILED(fontMapRes)) {
util_panic("Failed to map system font: 0x%08lX", fontMapRes);
return;
}
TGLP_s* glyphInfo = fontGetGlyphInfo();
glyphSheets = calloc(1, glyphInfo->nSheets * sizeof(C3D_Tex));
for(int i = 0; i < glyphInfo->nSheets; i++) {
C3D_Tex* tex = &glyphSheets[i];
tex->data = fontGetGlyphSheetTex(i);
tex->fmt = (GPU_TEXCOLOR) glyphInfo->sheetFmt;
tex->size = glyphInfo->sheetSize;
tex->width = glyphInfo->sheetWidth;
tex->height = glyphInfo->sheetHeight;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE);
}
screen_load_texture(TEXTURE_BOTTOM_SCREEN_BG, "bottom_screen_bg.png", true);
screen_load_texture(TEXTURE_BOTTOM_SCREEN_TOP_BAR, "bottom_screen_top_bar.png", true);
screen_load_texture(TEXTURE_BOTTOM_SCREEN_TOP_BAR_SHADOW, "bottom_screen_top_bar_shadow.png", true);
screen_load_texture(TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR, "bottom_screen_bottom_bar.png", true);
screen_load_texture(TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR_SHADOW, "bottom_screen_bottom_bar_shadow.png", true);
screen_load_texture(TEXTURE_TOP_SCREEN_BG, "top_screen_bg.png", true);
screen_load_texture(TEXTURE_TOP_SCREEN_TOP_BAR, "top_screen_top_bar.png", true);
screen_load_texture(TEXTURE_TOP_SCREEN_TOP_BAR_SHADOW, "top_screen_top_bar_shadow.png", true);
screen_load_texture(TEXTURE_TOP_SCREEN_BOTTOM_BAR, "top_screen_bottom_bar.png", true);
screen_load_texture(TEXTURE_TOP_SCREEN_BOTTOM_BAR_SHADOW, "top_screen_bottom_bar_shadow.png", true);
screen_load_texture(TEXTURE_LOGO, "logo.png", true);
screen_load_texture(TEXTURE_SELECTION_OVERLAY, "selection_overlay.png", true);
screen_load_texture(TEXTURE_BUTTON_SMALL, "button_small.png", true);
screen_load_texture(TEXTURE_BUTTON_LARGE, "button_large.png", true);
screen_load_texture(TEXTURE_PROGRESS_BAR_BG, "progress_bar_bg.png", true);
screen_load_texture(TEXTURE_PROGRESS_BAR_CONTENT, "progress_bar_content.png", true);
screen_load_texture(TEXTURE_SMDH_INFO_BOX, "smdh_info_box.png", true);
screen_load_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, "smdh_info_box_shadow.png", true);
screen_load_texture(TEXTURE_BATTERY_CHARGING, "battery_charging.png", true);
screen_load_texture(TEXTURE_BATTERY_0, "battery0.png", true);
screen_load_texture(TEXTURE_BATTERY_1, "battery1.png", true);
screen_load_texture(TEXTURE_BATTERY_2, "battery2.png", true);
screen_load_texture(TEXTURE_BATTERY_3, "battery3.png", true);
screen_load_texture(TEXTURE_BATTERY_4, "battery4.png", true);
screen_load_texture(TEXTURE_BATTERY_5, "battery5.png", true);
screen_load_texture(TEXTURE_WIFI_DISCONNECTED, "wifi_disconnected.png", true);
screen_load_texture(TEXTURE_WIFI_0, "wifi0.png", true);
screen_load_texture(TEXTURE_WIFI_1, "wifi1.png", true);
screen_load_texture(TEXTURE_WIFI_2, "wifi2.png", true);
screen_load_texture(TEXTURE_WIFI_3, "wifi3.png", true);
}
void screen_exit() {
for(u32 id = 0; id < MAX_TEXTURES; id++) {
screen_unload_texture(id);
}
if(glyphSheets != NULL) {
free(glyphSheets);
glyphSheets = NULL;
}
if(shaderInitialized) {
shaderProgramFree(&program);
shaderInitialized = false;
}
if(dvlb != NULL) {
DVLB_Free(dvlb);
dvlb = NULL;
}
if(target_top != NULL) {
C3D_RenderTargetDelete(target_top);
target_top = NULL;
}
if(target_bottom != NULL) {
C3D_RenderTargetDelete(target_bottom);
target_bottom = NULL;
}
if(c3dInitialized) {
C3D_Fini();
c3dInitialized = false;
}
}
u32 next_pow_2(u32 i) {
i--;
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
i++;
return i;
}
void screen_load_texture(u32 id, const char* path, bool linearFilter) {
if(id >= MAX_TEXTURES) {
util_panic("Attempted to load path \"%s\" to invalid texture ID \"%lu\".", path, id);
return;
}
u32 realPathSize = strlen(path) + 16;
char realPath[realPathSize];
snprintf(realPath, realPathSize, "sdmc:/fbitheme/%s", path);
FILE* fd = fopen(realPath, "rb");
if(fd != NULL) {
fclose(fd);
} else {
snprintf(realPath, realPathSize, "romfs:/%s", path);
}
unsigned char* image;
unsigned width;
unsigned height;
unsigned pngErr = lodepng_decode32_file(&image, &width, &height, realPath);
if(pngErr != 0) {
util_panic("Failed to load PNG file \"%s\": %u", realPath, pngErr);
return;
}
u32 pow2Width = next_pow_2(width);
if(pow2Width < 64) {
pow2Width = 64;
}
u32 pow2Height = next_pow_2(height);
if(pow2Height < 64) {
pow2Height = 64;
}
u8* gpuTex = linearAlloc(pow2Width * pow2Height * 4);
if(gpuTex == NULL) {
util_panic("Failed to allocate temporary texture buffer for file \"%s\".", realPath);
return;
}
memset(gpuTex, 0, pow2Width * pow2Height * 4);
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
u32 imagePos = (y * width + x) * 4;
u32 gpuTexPos = (y * pow2Width + x) * 4;
gpuTex[gpuTexPos + 0] = image[imagePos + 3];
gpuTex[gpuTexPos + 1] = image[imagePos + 2];
gpuTex[gpuTexPos + 2] = image[imagePos + 1];
gpuTex[gpuTexPos + 3] = image[imagePos + 0];
}
}
textures[id].initialized = true;
textures[id].width = width;
textures[id].height = height;
textures[id].pow2Width = pow2Width;
textures[id].pow2Height = pow2Height;
if(!C3D_TexInit(&textures[id].tex, (int) pow2Width, (int) pow2Height, GPU_RGBA8)) {
util_panic("Failed to initialize texture for file \"%s\".", realPath);
return;
}
C3D_TexSetFilter(&textures[id].tex, linearFilter ? GPU_LINEAR : GPU_NEAREST, GPU_NEAREST);
Result flushRes = GSPGPU_FlushDataCache(gpuTex, pow2Width * pow2Height * 4);
if(R_FAILED(flushRes)) {
util_panic("Failed to flush texture buffer for file \"%s\": 0x%08lX", realPath, flushRes);
return;
}
Result transferRes = GX_DisplayTransfer((u32*) gpuTex, GX_BUFFER_DIM(pow2Width, pow2Height), (u32*) textures[id].tex.data, GX_BUFFER_DIM(pow2Width, pow2Height), GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO));
if(R_FAILED(transferRes)) {
util_panic("Failed to tile texture data for file \"%s\": 0x%08lX", realPath, transferRes);
return;
}
gspWaitForPPF();
free(image);
linearFree(gpuTex);
}
u32 screen_load_texture_auto(const char* path, bool linearFilter) {
int id = -1;
for(int i = TEXTURE_AUTO_START; i < MAX_TEXTURES; i++) {
if(!textures[i].initialized) {
id = i;
break;
}
}
if(id == -1) {
util_panic("Attempted to load auto texture from path \"%s\" without free textures.", path);
return 0;
}
screen_load_texture((u32) id, path, linearFilter);
return (u32) id;
}
static u32 tiled_texture_index(u32 x, u32 y, u32 w, u32 h) {
return (((y >> 3) * (w >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3));
}
void screen_load_texture_tiled(u32 id, void* tiledData, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter) {
if(id >= MAX_TEXTURES) {
util_panic("Attempted to load tiled data to invalid texture ID \"%lu\".", id);
return;
}
u32 pow2Width = next_pow_2(width);
if(pow2Width < 64) {
pow2Width = 64;
}
u32 pow2Height = next_pow_2(height);
if(pow2Height < 64) {
pow2Height = 64;
}
u32 pixelSize = size / width / height;
u8* pow2Tex = linearAlloc(pow2Width * pow2Height * pixelSize);
if(pow2Tex == NULL) {
util_panic("Failed to allocate temporary texture buffer for tiled data.");
return;
}
memset(pow2Tex, 0, pow2Width * pow2Height * pixelSize);
for(u32 x = 0; x < width; x++) {
for(u32 y = 0; y < height; y++) {
u32 tiledDataPos = tiled_texture_index(x, y, width, height) * pixelSize;
u32 pow2TexPos = (y * pow2Width + x) * pixelSize;
for(u32 i = 0; i < pixelSize; i++) {
pow2Tex[pow2TexPos + i] = ((u8*) tiledData)[tiledDataPos + i];
}
}
}
textures[id].initialized = true;
textures[id].width = width;
textures[id].height = height;
textures[id].pow2Width = pow2Width;
textures[id].pow2Height = pow2Height;
if(!C3D_TexInit(&textures[id].tex, (int) pow2Width, (int) pow2Height, format)) {
util_panic("Failed to initialize texture for tiled data.");
return;
}
C3D_TexSetFilter(&textures[id].tex, linearFilter ? GPU_LINEAR : GPU_NEAREST, GPU_NEAREST);
C3D_TexUpload(&textures[id].tex, pow2Tex);
Result flushRes = GSPGPU_FlushDataCache(pow2Tex, pow2Width * pow2Height * 4);
if(R_FAILED(flushRes)) {
util_panic("Failed to flush texture buffer for tiled data: 0x%08lX", flushRes);
return;
}
Result transferRes = GX_DisplayTransfer((u32*) pow2Tex, GX_BUFFER_DIM(pow2Width, pow2Height), (u32*) textures[id].tex.data, GX_BUFFER_DIM(pow2Width, pow2Height), GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT((u32) gpuToGxFormat[format]) | GX_TRANSFER_OUT_FORMAT((u32) gpuToGxFormat[format]) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO));
if(R_FAILED(transferRes)) {
util_panic("Failed to tile texture data for tiled data: 0x%08lX", transferRes);
return;
}
gspWaitForPPF();
linearFree(pow2Tex);
}
u32 screen_load_texture_tiled_auto(void* tiledData, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter) {
int id = -1;
for(int i = TEXTURE_AUTO_START; i < MAX_TEXTURES; i++) {
if(!textures[i].initialized) {
id = i;
break;
}
}
if(id == -1) {
util_panic("Attempted to load auto texture from tiled data without free textures.");
return 0;
}
screen_load_texture_tiled((u32) id, tiledData, size, width, height, format, linearFilter);
return (u32) id;
}
void screen_unload_texture(u32 id) {
if(id >= MAX_TEXTURES) {
util_panic("Attempted to unload invalid texture ID \"%lu\".", id);
return;
}
if(textures[id].initialized) {
C3D_TexDelete(&textures[id].tex);
textures[id].initialized = false;
textures[id].width = 0;
textures[id].height = 0;
textures[id].pow2Width = 0;
textures[id].pow2Height = 0;
}
}
void screen_get_texture_size(u32* width, u32* height, u32 id) {
if(id >= MAX_TEXTURES || !textures[id].initialized) {
util_panic("Attempted to get size of invalid texture ID \"%lu\".", id);
return;
}
if(width) {
*width = textures[id].width;
}
if(height) {
*height = textures[id].height;
}
}
void screen_begin_frame() {
if(!C3D_FrameBegin(C3D_FRAME_SYNCDRAW)) {
util_panic("Failed to begin frame.");
return;
}
}
void screen_end_frame() {
C3D_FrameEnd(0);
}
void screen_select(gfxScreen_t screen) {
if(!C3D_FrameDrawOn(screen == GFX_TOP ? target_top : target_bottom)) {
util_panic("Failed to select render target.");
return;
}
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, shaderInstanceGetUniformLocation(program.vertexShader, "projection"), screen == GFX_TOP ? &projection_top : &projection_bottom);
}
void draw_quad(float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2) {
C3D_ImmDrawBegin(GPU_TRIANGLES);
C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f);
C3D_ImmSendAttrib(tx1, ty1, 0.0f, 0.0f);
C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f);
C3D_ImmSendAttrib(tx2, ty2, 0.0f, 0.0f);
C3D_ImmSendAttrib(x2, y1, 0.5f, 0.0f);
C3D_ImmSendAttrib(tx2, ty1, 0.0f, 0.0f);
C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f);
C3D_ImmSendAttrib(tx1, ty1, 0.0f, 0.0f);
C3D_ImmSendAttrib(x1, y2, 0.5f, 0.0f);
C3D_ImmSendAttrib(tx1, ty2, 0.0f, 0.0f);
C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f);
C3D_ImmSendAttrib(tx2, ty2, 0.0f, 0.0f);
C3D_ImmDrawEnd();
}
void screen_draw_texture(u32 id, float x, float y, float width, float height) {
if(id >= MAX_TEXTURES || !textures[id].initialized) {
util_panic("Attempted to draw invalid texture ID \"%lu\".", id);
return;
}
C3D_TexBind(0, &textures[id].tex);
draw_quad(x, y, x + width, y + height, 0, 0, (float) textures[id].width / (float) textures[id].pow2Width, (float) textures[id].height / (float) textures[id].pow2Height);
}
void screen_draw_texture_crop(u32 id, float x, float y, float width, float height) {
if(id >= MAX_TEXTURES || !textures[id].initialized) {
util_panic("Attempted to draw invalid texture ID \"%lu\".", id);
return;
}
C3D_TexBind(0, &textures[id].tex);
draw_quad(x, y, x + width, y + height, 0, 0, width / (float) textures[id].pow2Width, height / (float) textures[id].pow2Height);
}
static void screen_get_string_size_internal(float* width, float* height, const void* text, float scaleX, float scaleY, bool oneLine) {
float w = 0;
float h = scaleY * fontGetInfo()->lineFeed;
float lineWidth = 0;
const uint8_t* p = (const uint8_t*) text;
uint32_t code = 0;
ssize_t units = -1;
while(*p && (units = decode_utf8(&code, p)) != -1 && code > 0) {
p += units;
if(code == '\n') {
if(*p) {
if(lineWidth > w) {
w = lineWidth;
}
lineWidth = 0;
if(oneLine) {
break;
}
h += scaleY * fontGetInfo()->lineFeed;
}
} else {
lineWidth += scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth;
}
}
if(width) {
*width = lineWidth > w ? lineWidth : w;
}
if(height) {
*height = h;
}
}
void screen_get_string_size(float* width, float* height, const char* text, float scaleX, float scaleY) {
screen_get_string_size_internal(width, height, text, scaleX, scaleY, false);
}
void screen_draw_string(const char* text, float x, float y, float scaleX, float scaleY, u32 rgba, bool baseline) {
C3D_TexEnv* env = C3D_GetTexEnv(0);
if(env == NULL) {
util_panic("Failed to retrieve combiner settings.");
return;
}
C3D_TexEnvSrc(env, C3D_RGB, GPU_CONSTANT, 0, 0);
C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_CONSTANT, 0);
C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
C3D_TexEnvColor(env, rgba);
float stringWidth;
screen_get_string_size_internal(&stringWidth, NULL, text, scaleX, scaleY, false);
float lineWidth;
screen_get_string_size_internal(&lineWidth, NULL, text, scaleX, scaleY, true);
float currX = x + (stringWidth - lineWidth) / 2;
u32 flags = GLYPH_POS_CALC_VTXCOORD | (baseline ? GLYPH_POS_AT_BASELINE : 0);
int lastSheet = -1;
const uint8_t* p = (const uint8_t*) text;
uint32_t code = 0;
ssize_t units = -1;
while(*p && (units = decode_utf8(&code, p)) != -1 && code > 0) {
p += units;
if(code == '\n') {
if(*p) {
screen_get_string_size_internal(&lineWidth, NULL, p, scaleX, scaleY, true);
currX = x + (stringWidth - lineWidth) / 2;
y += scaleY * fontGetInfo()->lineFeed;
}
} else {
fontGlyphPos_s data;
fontCalcGlyphPos(&data, fontGlyphIndexFromCodePoint(code), flags, scaleX, scaleY);
if(data.sheetIndex != lastSheet) {
lastSheet = data.sheetIndex;
C3D_TexBind(0, &glyphSheets[lastSheet]);
}
draw_quad(currX + data.vtxcoord.left, y + data.vtxcoord.top, currX + data.vtxcoord.right, y + data.vtxcoord.bottom, data.texcoord.left, data.texcoord.top, data.texcoord.right, data.texcoord.bottom);
currX += data.xAdvance;
}
}
env = C3D_GetTexEnv(0);
if(env == NULL) {
util_panic("Failed to retrieve combiner settings.");
return;
}
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0);
C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
}

57
source/screen.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#define TOP_SCREEN_WIDTH 400
#define TOP_SCREEN_HEIGHT 240
#define BOTTOM_SCREEN_WIDTH 320
#define BOTTOM_SCREEN_HEIGHT 240
#define MAX_TEXTURES 1536 // Maximum number of 64x64 RGBA8 textures that can fit in 24MB of linear RAM.
#define TEXTURE_BOTTOM_SCREEN_BG 0
#define TEXTURE_BOTTOM_SCREEN_TOP_BAR 1
#define TEXTURE_BOTTOM_SCREEN_TOP_BAR_SHADOW 2
#define TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR 3
#define TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR_SHADOW 4
#define TEXTURE_TOP_SCREEN_BG 5
#define TEXTURE_TOP_SCREEN_TOP_BAR 6
#define TEXTURE_TOP_SCREEN_TOP_BAR_SHADOW 7
#define TEXTURE_TOP_SCREEN_BOTTOM_BAR 8
#define TEXTURE_TOP_SCREEN_BOTTOM_BAR_SHADOW 9
#define TEXTURE_LOGO 10
#define TEXTURE_SELECTION_OVERLAY 11
#define TEXTURE_BUTTON_SMALL 12
#define TEXTURE_BUTTON_LARGE 13
#define TEXTURE_PROGRESS_BAR_BG 14
#define TEXTURE_PROGRESS_BAR_CONTENT 15
#define TEXTURE_SMDH_INFO_BOX 16
#define TEXTURE_SMDH_INFO_BOX_SHADOW 17
#define TEXTURE_BATTERY_CHARGING 18
#define TEXTURE_BATTERY_0 19
#define TEXTURE_BATTERY_1 20
#define TEXTURE_BATTERY_2 21
#define TEXTURE_BATTERY_3 22
#define TEXTURE_BATTERY_4 23
#define TEXTURE_BATTERY_5 24
#define TEXTURE_WIFI_DISCONNECTED 25
#define TEXTURE_WIFI_0 26
#define TEXTURE_WIFI_1 27
#define TEXTURE_WIFI_2 28
#define TEXTURE_WIFI_3 29
#define TEXTURE_AUTO_START 30
void screen_init();
void screen_exit();
void screen_load_texture(u32 id, const char* path, bool linearFilter);
u32 screen_load_texture_auto(const char* path, bool linearFilter);
void screen_load_texture_tiled(u32 id, void* tiledData, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter);
u32 screen_load_texture_tiled_auto(void* tiledData, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter);
void screen_unload_texture(u32 id);
void screen_get_texture_size(u32* width, u32* height, u32 id);
void screen_begin_frame();
void screen_end_frame();
void screen_select(gfxScreen_t screen);
void screen_draw_texture(u32 id, float x, float y, float width, float height);
void screen_draw_texture_crop(u32 id, float x, float y, float width, float height);
void screen_get_string_size(float* width, float* height, const char* text, float scaleX, float scaleY);
void screen_draw_string(const char* text, float x, float y, float scaleX, float scaleY, u32 rgba, bool baseline);

View File

@ -1,724 +0,0 @@
#include "ui.hpp"
#include <citrus/core.hpp>
#include <citrus/err.hpp>
#include <citrus/gput.hpp>
#include <citrus/hid.hpp>
#include <arpa/inet.h>
#include <sys/dirent.h>
#include <sys/errno.h>
#include <sys/unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <stack>
using namespace ctr;
struct uiAlphabetize {
inline bool operator()(SelectableElement a, SelectableElement b) {
return strcasecmp(a.name.c_str(), b.name.c_str()) < 0;
}
};
u32 selectorTexture;
u32 selectorVbo;
void uiInit() {
gpu::createTexture(&selectorTexture);
gpu::setTextureInfo(selectorTexture, 64, 64, gpu::PIXEL_RGBA8, gpu::textureMinFilter(gpu::FILTER_NEAREST) | gpu::textureMagFilter(gpu::FILTER_NEAREST));
void* textureData;
gpu::getTextureData(selectorTexture, &textureData);
memset(textureData, 0xFF, 64 * 64 * 4);
gpu::createVbo(&selectorVbo);
gpu::setVboAttributes(selectorVbo, gpu::vboAttribute(0, 3, gpu::ATTR_FLOAT) | gpu::vboAttribute(1, 2, gpu::ATTR_FLOAT) | gpu::vboAttribute(2, 4, gpu::ATTR_FLOAT), 3);
const float vboData[] = {
0.0f, 0.0f, -0.1f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
320.0f, 0.0f, -0.1f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
320.0f, 12.0f, -0.1f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
320.0f, 12.0f, -0.1f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.0f, 12.0f, -0.1f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, -0.1f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
};
gpu::setVboData(selectorVbo, vboData, 6 * 9, gpu::PRIM_TRIANGLES);
}
void uiCleanup() {
if(selectorTexture != 0) {
gpu::freeTexture(selectorTexture);
selectorTexture = 0;
}
if(selectorVbo != 0) {
gpu::freeVbo(selectorVbo);
selectorVbo = 0;
}
}
bool uiSelect(SelectableElement* selected, std::vector<SelectableElement> elements, std::function<bool(std::vector<SelectableElement> &currElements, SelectableElement currElement, bool &elementsDirty, bool &resetCursorIfDirty)> onLoop, std::function<bool(SelectableElement select)> onSelect, bool useTopScreen, bool alphabetize, bool dpadPageScroll) {
int cursor = 0;
int scroll = 0;
u32 selectionScroll = 0;
u64 selectionScrollEndTime = 0;
u64 lastScrollTime = 0;
bool elementsDirty = false;
bool resetCursorIfDirty = true;
if(alphabetize) {
std::sort(elements.begin(), elements.end(), uiAlphabetize());
}
hid::Button leftScrollButton = dpadPageScroll ? hid::BUTTON_LEFT : hid::BUTTON_L;
hid::Button rightScrollButton = dpadPageScroll ? hid::BUTTON_RIGHT : hid::BUTTON_R;
bool canPageUp = false;
bool canPageDown = false;
while(core::running()) {
hid::poll();
if(hid::pressed(hid::BUTTON_A)) {
SelectableElement select = elements.at((u32) cursor);
if(onSelect == NULL || onSelect(select)) {
*selected = select;
return true;
}
}
if(canPageUp) {
canPageUp = !hid::released(leftScrollButton);
} else if(hid::pressed(leftScrollButton)) {
canPageUp = true;
}
if(canPageDown) {
canPageDown = !hid::released(rightScrollButton);
} else if(hid::pressed(rightScrollButton)) {
canPageDown = true;
}
if(hid::held(hid::BUTTON_DOWN) || hid::held(hid::BUTTON_UP) || (hid::held(leftScrollButton) && canPageUp) || (hid::held(rightScrollButton) && canPageDown)) {
if(lastScrollTime == 0 || core::time() - lastScrollTime >= 180) {
if(hid::held(hid::BUTTON_DOWN) && cursor < (int) elements.size() - 1) {
cursor++;
if(cursor >= scroll + 20) {
scroll++;
}
}
if(canPageDown && hid::held(rightScrollButton) && cursor < (int) elements.size() - 1) {
cursor += 20;
if(cursor >= (int) elements.size()) {
cursor = elements.size() - 1;
if(cursor < 0) {
cursor = 0;
}
}
scroll += 20;
if(scroll >= (int) elements.size() - 19) {
scroll = elements.size() - 20;
if(scroll < 0) {
scroll = 0;
}
}
}
if(hid::held(hid::BUTTON_UP) && cursor > 0) {
cursor--;
if(cursor < scroll) {
scroll--;
}
}
if(canPageUp && hid::held(leftScrollButton) && cursor > 0) {
cursor -= 20;
if(cursor < 0) {
cursor = 0;
}
scroll -= 20;
if(scroll < 0) {
scroll = 0;
}
}
selectionScroll = 0;
selectionScrollEndTime = 0;
lastScrollTime = core::time();
}
} else {
lastScrollTime = 0;
}
gpu::setViewport(gpu::SCREEN_BOTTOM, 0, 0, gpu::BOTTOM_WIDTH, gpu::BOTTOM_HEIGHT);
gput::setOrtho(0, gpu::BOTTOM_WIDTH, 0, gpu::BOTTOM_HEIGHT, -1, 1);
gpu::clear();
u32 screenWidth;
u32 screenHeight;
gpu::getViewportWidth(&screenWidth);
gpu::getViewportHeight(&screenHeight);
for(std::vector<SelectableElement>::iterator it = elements.begin() + scroll; it != elements.begin() + scroll + 20 && it != elements.end(); it++) {
SelectableElement element = *it;
int index = it - elements.begin();
u8 color = 255;
int offset = 0;
float itemHeight = gput::getStringHeight(element.name, 8) + 4;
if(index == cursor) {
color = 0;
gput::pushModelView();
gput::translate(0, (screenHeight - 1) - ((index - scroll + 1) * itemHeight), 0);
gpu::bindTexture(gpu::TEXUNIT0, selectorTexture);
gpu::drawVbo(selectorVbo);
gput::popModelView();
u32 width = (u32) gput::getStringWidth(element.name, 8);
if(width > screenWidth) {
if(selectionScroll + screenWidth >= width) {
if(selectionScrollEndTime == 0) {
selectionScrollEndTime = core::time();
} else if(core::time() - selectionScrollEndTime >= 4000) {
selectionScroll = 0;
selectionScrollEndTime = 0;
}
} else {
selectionScroll++;
}
}
offset = -selectionScroll;
}
gput::drawString(element.name, offset, (screenHeight - 1) - ((index - scroll + 1) * itemHeight) + 2, 8, 8, color, color, color);
}
gpu::flushCommands();
gpu::flushBuffer();
gpu::setViewport(gpu::SCREEN_TOP, 0, 0, gpu::TOP_WIDTH, gpu::TOP_HEIGHT);
gput::setOrtho(0, gpu::TOP_WIDTH, 0, gpu::TOP_HEIGHT, -1, 1);
if(useTopScreen) {
gpu::clear();
SelectableElement currSelected = elements.at((u32) cursor);
if(currSelected.details.size() != 0) {
std::stringstream details;
for(std::vector<std::string>::iterator it = currSelected.details.begin(); it != currSelected.details.end(); it++) {
details << *it << "\n";
}
gput::drawString(details.str(), 0, screenHeight - 1 - gput::getStringHeight(details.str(), 8), 8, 8);
}
}
bool result = onLoop != NULL && onLoop(elements, elements.at((u32) cursor), elementsDirty, resetCursorIfDirty);
if(elementsDirty) {
if(resetCursorIfDirty) {
cursor = 0;
scroll = 0;
} else if(cursor >= (int) elements.size()) {
cursor = elements.size() - 1;
if(cursor < 0) {
cursor = 0;
}
scroll = elements.size() - 20;
if(scroll < 0) {
scroll = 0;
}
}
selectionScroll = 0;
selectionScrollEndTime = 0;
if(alphabetize) {
std::sort(elements.begin(), elements.end(), uiAlphabetize());
}
elementsDirty = false;
resetCursorIfDirty = true;
}
if(useTopScreen) {
gpu::flushCommands();
gpu::flushBuffer();
}
gpu::swapBuffers(true);
if(result) {
break;
}
}
return false;
}
void uiGetDirContents(std::vector<SelectableElement> &elements, const std::string directory, std::vector<std::string> extensions) {
elements.clear();
elements.push_back({".", "."});
elements.push_back({"..", ".."});
std::vector<std::string> contents = fs::contents(directory);
for(std::vector<std::string>::iterator it = contents.begin(); it != contents.end(); it++) {
const std::string path = *it;
const std::string name = fs::fileName(path);
if(fs::directory(path)) {
elements.push_back({path, name});
} else if(fs::hasExtensions(path, extensions)) {
struct stat st;
stat(path.c_str(), &st);
std::vector<std::string> info;
std::stringstream stream;
stream << "File Size: " << ((u32) st.st_size) << " bytes (" << std::fixed << std::setprecision(2) << ((u32) st.st_size) / 1024.0f / 1024.0f << "MB)";
info.push_back(stream.str());
std::string extension = fs::extension(path);
if(extension.compare("cia") == 0) {
app::App app = app::ciaInfo(path, fs::SD);
if(!err::has()) {
std::stringstream titleId;
titleId << "0x" << std::setfill('0') << std::setw(16) << std::hex << app.titleId;
std::stringstream uniqueId;
uniqueId << "0x" << std::setfill('0') << std::setw(8) << std::hex << app.uniqueId;
std::stringstream version;
version << "0x" << std::setfill('0') << std::hex << app.version;
std::stringstream size;
size << "" << app.size << " bytes (" << std::fixed << std::setprecision(2) << app.size / 1024.0f / 1024.0f << "MB)";
info.push_back("Installed Size: " + size.str());
info.push_back("Title ID: " + titleId.str());
info.push_back("Unique ID: " + uniqueId.str());
info.push_back("Product Code: " + std::string(app.productCode));
info.push_back("Platform: " + app::platformString(app.platform));
info.push_back("Category: " + app::categoryString(app.category));
info.push_back("Version: " + version.str());
}
}
elements.push_back({path, name, info});
}
}
}
bool uiSelectFile(std::string* selectedFile, const std::string rootDirectory, std::vector<std::string> extensions, std::function<bool(const std::string currDirectory, bool inRoot, bool &updateList)> onLoop, std::function<bool(const std::string path, bool &updateList)> onSelect, bool useTopScreen, bool dpadPageScroll) {
std::stack<std::string> directoryStack;
std::string currDirectory = rootDirectory;
std::vector<SelectableElement> elements;
uiGetDirContents(elements, currDirectory, extensions);
bool updateContents = false;
bool resetCursor = true;
SelectableElement selected;
bool result = uiSelect(&selected, elements, [&](std::vector<SelectableElement> &currElements, SelectableElement currElement, bool &elementsDirty, bool &resetCursorIfDirty) {
if(onLoop != NULL && onLoop(currDirectory, directoryStack.empty(), updateContents)) {
return true;
}
if(hid::pressed(hid::BUTTON_B) && !directoryStack.empty()) {
currDirectory = directoryStack.top();
directoryStack.pop();
updateContents = true;
}
if(updateContents) {
uiGetDirContents(currElements, currDirectory, extensions);
elementsDirty = true;
resetCursorIfDirty = resetCursor;
updateContents = false;
resetCursor = true;
}
return false;
}, [&](SelectableElement select) {
if(select.name.compare(".") == 0) {
return false;
} else if(select.name.compare("..") == 0) {
if(!directoryStack.empty()) {
currDirectory = directoryStack.top();
directoryStack.pop();
updateContents = true;
}
return false;
} else if(fs::directory(select.id)) {
directoryStack.push(currDirectory);
currDirectory = select.id;
updateContents = true;
return false;
}
bool updateList = false;
bool ret = onSelect(select.id, updateList);
if(updateList) {
updateContents = true;
resetCursor = false;
}
return ret;
}, useTopScreen, true, dpadPageScroll);
if(result) {
*selectedFile = selected.id;
}
return result;
}
void uiGetApps(std::vector<SelectableElement> &elements, std::vector<app::App> apps) {
elements.clear();
for(std::vector<app::App>::iterator it = apps.begin(); it != apps.end(); it++) {
app::App app = *it;
std::stringstream titleId;
titleId << "0x" << std::setfill('0') << std::setw(16) << std::hex << app.titleId;
std::stringstream uniqueId;
uniqueId << "0x" << std::setfill('0') << std::setw(8) << std::hex << app.uniqueId;
std::stringstream version;
version << "0x" << std::setfill('0') << std::hex << app.version;
std::stringstream size;
size << "" << app.size << " bytes (" << std::fixed << std::setprecision(2) << app.size / 1024.0f / 1024.0f << "MB)";
std::vector<std::string> details;
details.push_back("Title ID: " + titleId.str());
details.push_back("Unique ID: " + uniqueId.str());
details.push_back("Product Code: " + std::string(app.productCode));
details.push_back("Platform: " + app::platformString(app.platform));
details.push_back("Category: " + app::categoryString(app.category));
details.push_back("Version: " + version.str());
details.push_back("Size: " + size.str());
elements.push_back({titleId.str(), app.productCode, details});
}
if(elements.size() == 0) {
elements.push_back({"None", "None"});
}
}
bool uiFindApp(app::App* result, std::string id, std::vector<app::App> apps) {
for(std::vector<app::App>::iterator it = apps.begin(); it != apps.end(); it++) {
app::App app = *it;
if(app.titleId == (u64) strtoll(id.c_str(), NULL, 16)) {
*result = app;
return true;
}
}
return false;
}
bool uiSelectApp(app::App* selectedApp, fs::MediaType mediaType, std::function<bool(bool &updateList)> onLoop, std::function<bool(app::App app, bool &updateList)> onSelect, bool useTopScreen, bool dpadPageScroll) {
std::vector<SelectableElement> elements;
std::vector<app::App> apps = app::list(mediaType);
uiGetApps(elements, apps);
bool updateContents = false;
SelectableElement selected;
bool result = uiSelect(&selected, elements, [&](std::vector<SelectableElement> &currElements, SelectableElement currElement, bool &elementsDirty, bool &resetCursorIfDirty) {
if(onLoop != NULL && onLoop(updateContents)) {
return true;
}
if(updateContents) {
apps = app::list(mediaType);
uiGetApps(currElements, apps);
elementsDirty = true;
resetCursorIfDirty = false;
updateContents = false;
}
return false;
}, [&](SelectableElement select) {
if(select.name.compare("None") != 0) {
app::App app;
if(uiFindApp(&app, select.id, apps)) {
bool updateList = false;
bool ret = onSelect(app, updateList);
if(updateList) {
updateContents = true;
}
return ret;
}
}
return false;
}, useTopScreen, true, dpadPageScroll);
if(result) {
app::App app;
if(uiFindApp(&app, selected.id, apps)) {
*selectedApp = app;
}
}
return result;
}
void uiDisplayMessage(gpu::Screen screen, const std::string message) {
u32 width = screen == gpu::SCREEN_TOP ? gpu::TOP_WIDTH : gpu::BOTTOM_WIDTH;
u32 height = screen == gpu::SCREEN_TOP ? gpu::TOP_HEIGHT : gpu::BOTTOM_HEIGHT;
gpu::setViewport(screen, 0, 0, width, height);
gput::setOrtho(0, width, 0, height, -1, 1);
gpu::clear();
gput::drawString(message, (width - gput::getStringWidth(message, 8)) / 2, (height - gput::getStringHeight(message, 8)) / 2, 8, 8);
gpu::flushCommands();
gpu::flushBuffer();
gpu::swapBuffers(true);
gpu::setViewport(gpu::SCREEN_TOP, 0, 0, gpu::TOP_WIDTH, gpu::TOP_HEIGHT);
gput::setOrtho(0, gpu::TOP_WIDTH, 0, gpu::TOP_HEIGHT, -1, 1);
}
bool uiPrompt(gpu::Screen screen, const std::string message, bool question) {
std::stringstream stream;
stream << message << "\n";
if(question) {
stream << "Press A to confirm, B to cancel." << "\n";
} else {
stream << "Press Start to continue." << "\n";
}
bool result = false;
std::string str = stream.str();
while(core::running()) {
hid::poll();
if(question) {
if(hid::pressed(hid::BUTTON_A)) {
result = true;
break;
}
if(hid::pressed(hid::BUTTON_B)) {
result = false;
break;
}
} else {
if(hid::pressed(hid::BUTTON_START)) {
result = true;
break;
}
}
uiDisplayMessage(screen, str);
}
hid::poll();
return result;
}
void uiDisplayProgress(gpu::Screen screen, const std::string operation, const std::string details, bool quickSwap, u32 progress) {
std::stringstream stream;
stream << operation << ": [";
u32 progressBars = progress / 4;
for(u32 i = 0; i < 25; i++) {
if(i < progressBars) {
stream << '|';
} else {
stream << ' ';
}
}
stream << "] " << std::setfill(' ') << std::setw(3) << progress << "%" << "\n";
stream << details << "\n";
std::string str = stream.str();
u32 width = screen == gpu::SCREEN_TOP ? gpu::TOP_WIDTH : gpu::BOTTOM_WIDTH;
u32 height = screen == gpu::SCREEN_TOP ? gpu::TOP_HEIGHT : gpu::BOTTOM_HEIGHT;
gpu::setViewport(screen, 0, 0, width, height);
gput::setOrtho(0, width, 0, height, -1, 1);
gpu::clear();
gput::drawString(str, (width - gput::getStringWidth(str, 8)) / 2, (height - gput::getStringHeight(str, 8)) / 2, 8, 8);
gpu::flushCommands();
gpu::flushBuffer();
gpu::swapBuffers(!quickSwap);
gpu::setViewport(gpu::SCREEN_TOP, 0, 0, gpu::TOP_WIDTH, gpu::TOP_HEIGHT);
gput::setOrtho(0, gpu::TOP_WIDTH, 0, gpu::TOP_HEIGHT, -1, 1);
}
u64 ntohll(u64 value) {
static const int num = 42;
if(*((char*) &num) == num) {
return (((uint64_t) htonl((u32) value)) << 32) + htonl((u32) (value >> 32));
} else {
return value;
}
}
int socketListen(u16 port) {
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0) {
return -1;
}
int rcvbuf = 32768;
if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0) {
}
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(port);
if(bind(fd, (struct sockaddr*) &address, sizeof(address)) != 0) {
closesocket(fd);
return -1;
}
int flags = fcntl(fd, F_GETFL);
if(flags == -1) {
closesocket(fd);
return -1;
}
if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
closesocket(fd);
return -1;
}
if(listen(fd, 10) != 0) {
closesocket(fd);
return -1;
}
return fd;
}
FILE* socketAccept(int listeningSocket) {
struct sockaddr_in addr;
socklen_t addrSize = sizeof(addr);
int afd = accept(listeningSocket, (struct sockaddr*) &addr, &addrSize);
if(afd < 0) {
return NULL;
}
int flags = fcntl(afd, F_GETFL);
if(flags == -1) {
closesocket(afd);
return NULL;
}
if(fcntl(afd, F_SETFL, flags | O_NONBLOCK) != 0) {
closesocket(afd);
return NULL;
}
return fdopen(afd, "rw");
}
RemoteFile uiAcceptRemoteFile(gpu::Screen screen, std::function<void(std::stringstream& infoStream)> onWait) {
// meh
for(u32 i = 0; i < 2; i++) {
uiDisplayMessage(screen == gpu::SCREEN_TOP ? gpu::SCREEN_BOTTOM : gpu::SCREEN_TOP, "");
}
uiDisplayMessage(screen, "Initializing socket...");
int listen = socketListen(5000);
if(listen < 0) {
std::stringstream errStream;
errStream << "Failed to initialize socket." << "\n" << strerror(errno) << "\n";
uiPrompt(screen, errStream.str(), false);
return {NULL, 0};
}
std::stringstream baseInfoStream;
baseInfoStream << "Waiting for peer to connect..." << "\n";
baseInfoStream << "IP: " << inet_ntoa({(u32) gethostid()}) << "\n";
baseInfoStream << "Press B to cancel." << "\n";
std::string baseInfo = baseInfoStream.str();
FILE* socket;
while((socket = socketAccept(listen)) == NULL) {
if(!core::running()) {
close(listen);
return {NULL, 0};
}
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINPROGRESS) {
close(listen);
std::stringstream errStream;
errStream << "Failed to accept peer." << "\n" << strerror(errno) << "\n";
uiPrompt(screen, errStream.str(), false);
return {NULL, 0};
}
hid::poll();
if(hid::pressed(hid::BUTTON_B)) {
close(listen);
return {NULL, 0};
}
std::stringstream infoStream;
infoStream << baseInfo;
onWait(infoStream);
uiDisplayMessage(screen, infoStream.str());
}
close(listen);
uiDisplayMessage(screen, "Reading info...\nPress B to cancel.");
u64 fileSize = 0;
u64 bytesRead = 0;
while(bytesRead < sizeof(fileSize)) {
if(!core::running()) {
fclose(socket);
return {NULL, 0};
}
size_t currBytesRead = fread(&fileSize + bytesRead, 1, (size_t) (sizeof(fileSize) - bytesRead), socket);
if(currBytesRead > 0) {
bytesRead += currBytesRead;
}
if(ferror(socket) && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINPROGRESS) {
fclose(socket);
std::stringstream errStream;
errStream << "Failed to read info." << "\n" << strerror(errno) << "\n";
uiPrompt(screen, errStream.str(), false);
return {NULL, 0};
}
hid::poll();
if(hid::pressed(hid::BUTTON_B)) {
fclose(socket);
return {NULL, 0};
}
}
fileSize = ntohll(fileSize);
return {socket, fileSize};
}

View File

@ -1,32 +0,0 @@
#pragma once
#include <citrus/app.hpp>
#include <citrus/fs.hpp>
#include <citrus/gpu.hpp>
#include <citrus/types.hpp>
#include <functional>
#include <string>
#include <vector>
typedef struct {
std::string id;
std::string name;
std::vector<std::string> details;
} SelectableElement;
typedef struct {
FILE* fd;
u64 fileSize;
} RemoteFile;
void uiInit();
void uiCleanup();
bool uiSelect(SelectableElement* selected, std::vector<SelectableElement> elements, std::function<bool(std::vector<SelectableElement> &currElements, SelectableElement currElement, bool &elementsDirty, bool &resetCursorIfDirty)> onLoop, std::function<bool(SelectableElement select)> onSelect, bool useTopScreen = true, bool alphabetize = true, bool dpadPageScroll = true);
bool uiSelectFile(std::string* selectedFile, const std::string rootDirectory, std::vector<std::string> extensions, std::function<bool(const std::string currDirectory, bool inRoot, bool &updateList)> onLoop, std::function<bool(const std::string path, bool &updateList)> onSelect, bool useTopScreen = true, bool dpadPageScroll = true);
bool uiSelectApp(ctr::app::App* selectedApp, ctr::fs::MediaType mediaType, std::function<bool(bool &updateList)> onLoop, std::function<bool(ctr::app::App app, bool &updateList)> onSelect, bool useTopScreen = true, bool dpadPageScroll = true);
void uiDisplayMessage(ctr::gpu::Screen screen, const std::string message);
bool uiPrompt(ctr::gpu::Screen screen, const std::string message, bool question);
void uiDisplayProgress(ctr::gpu::Screen screen, const std::string operation, const std::string details, bool quickSwap, u32 progress);
RemoteFile uiAcceptRemoteFile(ctr::gpu::Screen screen, std::function<void(std::stringstream& infoStream)> onWait = [&](std::stringstream& infoStream){});

61
source/ui/error.c Normal file
View File

@ -0,0 +1,61 @@
#include <malloc.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "error.h"
#include "prompt.h"
typedef struct {
char fullText[4096];
void* data;
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2);
} error_data;
static void error_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
error_data* errorData = (error_data*) data;
if(errorData->drawTop != NULL) {
errorData->drawTop(view, errorData->data, x1, y1, x2, y2);
}
}
static void error_onresponse(ui_view* view, void* data, bool response) {
prompt_destroy(view);
free(data);
}
void error_display_res(void* data, void (* drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...) {
error_data* errorData = (error_data*) calloc(1, sizeof(error_data));
errorData->data = data;
errorData->drawTop = drawTop;
char textBuf[1024];
va_list list;
va_start(list, text);
vsnprintf(textBuf, 1024, text, list);
va_end(list);
// TODO: Break result codes down into their parts.
snprintf(errorData->fullText, 4096, "%s\nResult code: 0x%08lX", textBuf, result);
ui_push(prompt_create("Error", errorData->fullText, 0xFF000000, false, errorData, NULL, error_draw_top, error_onresponse));
}
void error_display_errno(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), int err, const char* text, ...) {
error_data* errorData = (error_data*) calloc(1, sizeof(error_data));
errorData->data = data;
errorData->drawTop = drawTop;
char textBuf[1024];
va_list list;
va_start(list, text);
vsnprintf(textBuf, 1024, text, list);
va_end(list);
snprintf(errorData->fullText, 4096, "%s\nError: %s (%d)", textBuf, strerror(err), err);
ui_push(prompt_create("Error", errorData->fullText, 0xFF000000, false, errorData, NULL, error_draw_top, error_onresponse));
}

6
source/ui/error.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "ui.h"
void error_display_res(void* data, void (* drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...);
void error_display_errno(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), int err, const char* text, ...);

256
source/ui/list.c Normal file
View File

@ -0,0 +1,256 @@
#include <3ds.h>
#include <malloc.h>
#include "list.h"
#include "section/task.h"
#include "../screen.h"
typedef struct {
void* data;
list_item* items;
u32* itemCount;
u32 selectedIndex;
u32 selectionScroll;
u64 nextSelectionScrollResetTime;
float scrollPos;
u32 lastScrollTouchY;
u64 nextActionTime;
void (*update)(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched);
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected);
} list_data;
static float list_get_item_screen_y(list_data* listData, u32 index) {
float y = -listData->scrollPos;
for(u32 i = 0; i < index && i < *listData->itemCount; i++) {
float stringHeight;
screen_get_string_size(NULL, &stringHeight, listData->items[i].name, 0.5f, 0.5f);
y += stringHeight;
}
return y;
}
static int list_get_item_at(list_data* listData, float screenY) {
float y = -listData->scrollPos;
for(u32 i = 0; i < *listData->itemCount; i++) {
float stringHeight;
screen_get_string_size(NULL, &stringHeight, listData->items[i].name, 0.5f, 0.5f);
if(screenY >= y && screenY < y + stringHeight) {
return (int) i;
}
y += stringHeight;
}
return -1;
}
static void list_validate_pos(list_data* listData, float by1, float by2) {
if(listData->items == NULL || listData->itemCount == NULL || *listData->itemCount <= 0 || listData->selectedIndex <= 0) {
listData->selectedIndex = 0;
listData->scrollPos = 0;
}
if(listData->items != NULL && listData->itemCount != NULL && *listData->itemCount > 0) {
if(listData->selectedIndex > *listData->itemCount - 1) {
listData->selectedIndex = *listData->itemCount - 1;
listData->scrollPos = 0;
}
float lastItemHeight;
screen_get_string_size(NULL, &lastItemHeight, listData->items[*listData->itemCount - 1].name, 0.5f, 0.5f);
float lastPageEnd = list_get_item_screen_y(listData, *listData->itemCount - 1);
if(lastPageEnd < by2 - by1 - lastItemHeight) {
listData->scrollPos -= (by2 - by1 - lastItemHeight) - lastPageEnd;
}
if(listData->scrollPos < 0) {
listData->scrollPos = 0;
}
}
}
static void list_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
svcWaitSynchronization(task_get_mutex(), U64_MAX);
list_data* listData = (list_data*) data;
bool selectedTouched = false;
if(listData->items != NULL && listData->itemCount != NULL && *listData->itemCount > 0) {
list_validate_pos(listData, by1, by2);
float itemWidth;
screen_get_string_size(&itemWidth, NULL, listData->items[listData->selectedIndex].name, 0.5f, 0.5f);
if(itemWidth > bx2 - bx1) {
if(listData->selectionScroll == 0 || listData->selectionScroll >= itemWidth - (bx2 - bx1)) {
if(listData->nextSelectionScrollResetTime == 0) {
listData->nextSelectionScrollResetTime = osGetTime() + 2000;
} else if(osGetTime() >= listData->nextSelectionScrollResetTime) {
listData->selectionScroll = listData->selectionScroll == 0 ? 1 : 0;
listData->nextSelectionScrollResetTime = 0;
}
} else {
listData->selectionScroll++;
}
} else {
listData->selectionScroll = 0;
listData->nextSelectionScrollResetTime = 0;
}
u32 lastSelectedIndex = listData->selectedIndex;
if(((hidKeysDown() & KEY_DOWN) || ((hidKeysHeld() & KEY_DOWN) && osGetTime() >= listData->nextActionTime)) && listData->selectedIndex < *listData->itemCount - 1) {
listData->selectedIndex++;
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_DOWN) ? 500 : 100);
}
if(((hidKeysDown() & KEY_UP) || ((hidKeysHeld() & KEY_UP) && osGetTime() >= listData->nextActionTime)) && listData->selectedIndex > 0) {
listData->selectedIndex--;
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_UP) ? 500 : 100);
}
if(((hidKeysDown() & KEY_RIGHT) || ((hidKeysHeld() & KEY_RIGHT) && osGetTime() >= listData->nextActionTime)) && listData->selectedIndex < *listData->itemCount - 1) {
u32 remaining = *listData->itemCount - 1 - listData->selectedIndex;
listData->selectedIndex += remaining < 13 ? remaining : 13;
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_RIGHT) ? 500 : 100);
}
if(((hidKeysDown() & KEY_LEFT) || ((hidKeysHeld() & KEY_LEFT) && osGetTime() >= listData->nextActionTime)) && listData->selectedIndex > 0) {
u32 remaining = listData->selectedIndex;
listData->selectedIndex -= remaining < 13 ? remaining : 13;
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_LEFT) ? 500 : 100);
}
if(lastSelectedIndex != listData->selectedIndex) {
listData->selectionScroll = 0;
listData->nextSelectionScrollResetTime = 0;
float itemHeight;
screen_get_string_size(NULL, &itemHeight, listData->items[listData->selectedIndex].name, 0.5f, 0.5f);
float itemY = list_get_item_screen_y(listData, listData->selectedIndex);
if(itemY + itemHeight > by2 - by1) {
listData->scrollPos -= (by2 - by1) - itemY - itemHeight;
}
if(itemY < 0) {
listData->scrollPos += itemY;
}
}
if(hidKeysDown() & KEY_TOUCH) {
touchPosition pos;
hidTouchRead(&pos);
listData->lastScrollTouchY = pos.py;
int index = list_get_item_at(listData, pos.py - by1);
if(index >= 0) {
if(listData->selectedIndex == index) {
selectedTouched = true;
} else {
listData->selectedIndex = (u32) index;
}
}
} else if(hidKeysHeld() & KEY_TOUCH) {
touchPosition pos;
hidTouchRead(&pos);
listData->scrollPos += -((int) pos.py - (int) listData->lastScrollTouchY);
listData->lastScrollTouchY = pos.py;
}
list_validate_pos(listData, by1, by2);
}
if(listData->update != NULL) {
listData->update(view, listData->data, &listData->items, &listData->itemCount, listData->items != NULL && listData->itemCount != NULL && *listData->itemCount > 0 ? &listData->items[listData->selectedIndex] : NULL, selectedTouched);
}
svcReleaseMutex(task_get_mutex());
}
static void list_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
svcWaitSynchronization(task_get_mutex(), U64_MAX);
list_data* listData = (list_data*) data;
if(listData->drawTop != NULL) {
listData->drawTop(view, listData->data, x1, y1, x2, y2, listData->items != NULL && listData->itemCount != NULL && *listData->itemCount > 0 ? &listData->items[listData->selectedIndex] : NULL);
}
svcReleaseMutex(task_get_mutex());
}
static void list_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
svcWaitSynchronization(task_get_mutex(), U64_MAX);
list_data* listData = (list_data*) data;
list_validate_pos(listData, y1, y2);
if(listData->items != NULL && listData->itemCount != NULL) {
float y = y1 - listData->scrollPos;
for(u32 i = 0; i < *listData->itemCount && y < y2; i++) {
float stringHeight;
screen_get_string_size(NULL, &stringHeight, listData->items[i].name, 0.5f, 0.5f);
if(y > y1 - stringHeight) {
float x = x1 + 2;
if(i == listData->selectedIndex) {
x -= listData->selectionScroll;
}
screen_draw_string(listData->items[i].name, x, y, 0.5f, 0.5f, listData->items[i].rgba, false);
if(i == listData->selectedIndex) {
u32 selectionOverlayWidth = 0;
u32 selectionOverlayHeight = 0;
screen_get_texture_size(&selectionOverlayWidth, &selectionOverlayHeight, TEXTURE_SELECTION_OVERLAY);
screen_draw_texture(TEXTURE_SELECTION_OVERLAY, (x1 + x2 - selectionOverlayWidth) / 2, y, selectionOverlayWidth, stringHeight);
}
}
y += stringHeight;
}
}
svcReleaseMutex(task_get_mutex());
}
ui_view* list_create(const char* name, const char* info, void* data, void (*update)(ui_view* view, void* data, list_item** contents, u32** itemCount, list_item* selected, bool selectedTouched),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected)) {
list_data* listData = (list_data*) calloc(1, sizeof(list_data));
listData->data = data;
listData->items = NULL;
listData->itemCount = NULL;
listData->selectedIndex = 0;
listData->selectionScroll = 0;
listData->nextSelectionScrollResetTime = 0;
listData->scrollPos = 0;
listData->lastScrollTouchY = 0;
listData->update = update;
listData->drawTop = drawTop;
ui_view* view = (ui_view*) calloc(1, sizeof(ui_view));
view->name = name;
view->info = info;
view->data = listData;
view->update = list_update;
view->drawTop = list_draw_top;
view->drawBottom = list_draw_bottom;
return view;
}
void list_destroy(ui_view* view) {
free(view->data);
free(view);
}
void* list_get_data(ui_view* view) {
return ((list_data*) view->data)->data;
}

16
source/ui/list.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <sys/syslimits.h>
#include "ui.h"
typedef struct {
char name[NAME_MAX];
u32 rgba;
void* data;
} list_item;
ui_view* list_create(const char* name, const char* info, void* data, void (*update)(ui_view* view, void* data, list_item** contents, u32** itemCount, list_item* selected, bool selectedTouched),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected));
void list_destroy(ui_view* view);
void* list_get_data(ui_view* view);

71
source/ui/mainmenu.c Normal file
View File

@ -0,0 +1,71 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "list.h"
#include "mainmenu.h"
#include "section/section.h"
#include "../screen.h"
#define MAINMENU_ITEM_COUNT 10
static u32 mainmenu_item_count = MAINMENU_ITEM_COUNT;
static list_item mainmenu_items[MAINMENU_ITEM_COUNT] = {
{"SD", 0xFF000000, files_open_sd},
{"CTR NAND", 0xFF000000, files_open_ctrnand},
{"TWL NAND", 0xFF000000, files_open_twlnand},
{"Titles", 0xFF000000, titles_open},
{"Pending Titles", 0xFF000000, pendingtitles_open},
{"Tickets", 0xFF000000, tickets_open},
{"Ext Save Data", 0xFF000000, extsavedata_open},
{"System Save Data", 0xFF000000, systemsavedata_open},
{"Network Install to SD", 0xFF000000, networkinstall_open_sd},
{"Network Install to NAND", 0xFF000000, networkinstall_open_nand},
};
static void mainmenu_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
u32 logoWidth;
u32 logoHeight;
screen_get_texture_size(&logoWidth, &logoHeight, TEXTURE_LOGO);
float logoX = x1 + (x2 - x1 - logoWidth) / 2;
float logoY = y1 + (y2 - y1 - logoHeight) / 2;
screen_draw_texture(TEXTURE_LOGO, logoX, logoY, logoWidth, logoHeight);
char verString[64];
snprintf(verString, 64, "Ver. %s", VERSION_STRING);
float verWidth;
float verHeight;
screen_get_string_size(&verWidth, &verHeight, verString, 0.5f, 0.5f);
float verX = x1 + (x2 - x1 - verWidth) / 2;
float verY = logoY + logoHeight + (y2 - (logoY + logoHeight) - verHeight) / 2;
screen_draw_string(verString, verX, verY, 0.5f, 0.5f, 0xFF000000, false);
}
static void mainmenu_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_START) {
ui_pop();
return;
}
if(selected != NULL && (selectedTouched || hidKeysDown() & KEY_A) && selected->data != NULL) {
((void(*)()) selected->data)();
return;
}
if(*itemCount != &mainmenu_item_count || *items != mainmenu_items) {
*itemCount = &mainmenu_item_count;
*items = mainmenu_items;
}
}
ui_view* mainmenu_create() {
return list_create("Main Menu", "A: Select, START: Exit", NULL, mainmenu_update, mainmenu_draw_top);
}
void mainmenu_destroy(ui_view* view) {
list_destroy(view);
}

6
source/ui/mainmenu.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "ui.h"
ui_view* mainmenu_create();
void mainmenu_destroy(ui_view* view);

98
source/ui/progressbar.c Normal file
View File

@ -0,0 +1,98 @@
#include <3ds.h>
#include <malloc.h>
#include "section/task.h"
#include "progressbar.h"
#include "../screen.h"
typedef struct {
bool cancellable;
void* data;
float progress;
char progressText[PROGRESS_TEXT_MAX];
void (*update)(ui_view* view, void* data, float* progress, char* progressText);
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2);
} progressbar_data;
static void progressbar_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
svcWaitSynchronization(task_get_mutex(), U64_MAX);
progressbar_data* progressBarData = (progressbar_data*) data;
if(progressBarData->update != NULL) {
progressBarData->update(view, progressBarData->data, &progressBarData->progress, progressBarData->progressText);
}
svcReleaseMutex(task_get_mutex());
}
static void progressbar_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
svcWaitSynchronization(task_get_mutex(), U64_MAX);
progressbar_data* progressBarData = (progressbar_data*) data;
if(progressBarData->drawTop != NULL) {
progressBarData->drawTop(view, progressBarData->data, x1, y1, x2, y2);
}
svcReleaseMutex(task_get_mutex());
}
static void progressbar_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
progressbar_data* progressBarData = (progressbar_data*) data;
u32 progressBarBgWidth;
u32 progressBarBgHeight;
screen_get_texture_size(&progressBarBgWidth, &progressBarBgHeight, TEXTURE_PROGRESS_BAR_BG);
float progressBarBgX = x1 + (x2 - x1 - progressBarBgWidth) / 2;
float progressBarBgY = y1 + (y2 - y1 - progressBarBgHeight) / 2;
screen_draw_texture(TEXTURE_PROGRESS_BAR_BG, progressBarBgX, progressBarBgY, progressBarBgWidth, progressBarBgHeight);
u32 progressBarContentWidth;
u32 progressBarContentHeight;
screen_get_texture_size(&progressBarContentWidth, &progressBarContentHeight, TEXTURE_PROGRESS_BAR_CONTENT);
float progressBarContentX = x1 + (x2 - x1 - progressBarContentWidth) / 2;
float progressBarContentY = y1 + (y2 - y1 - progressBarContentHeight) / 2;
screen_draw_texture_crop(TEXTURE_PROGRESS_BAR_CONTENT, progressBarContentX, progressBarContentY, progressBarContentWidth * progressBarData->progress, progressBarContentHeight);
float progressTextWidth;
float progressTextHeight;
screen_get_string_size(&progressTextWidth, &progressTextHeight, progressBarData->progressText, 0.5f, 0.5f);
float progressTextX = x1 + (x2 - x1 - progressTextWidth) / 2;
float progressTextY = progressBarBgY + progressBarBgHeight + 10;
screen_draw_string(progressBarData->progressText, progressTextX, progressTextY, 0.5f, 0.5f, 0xFF000000, false);
}
ui_view* progressbar_create(const char* name, const char* info, void* data, void (*update)(ui_view* view, void* data, float* progress, char* progressText),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2)) {
progressbar_data* progressBarData = (progressbar_data*) calloc(1, sizeof(progressbar_data));
progressBarData->data = data;
progressBarData->progress = 0;
progressBarData->update = update;
progressBarData->drawTop = drawTop;
ui_view* view = (ui_view*) calloc(1, sizeof(ui_view));
view->name = name;
view->info = info;
view->data = progressBarData;
view->update = progressbar_update;
view->drawTop = progressbar_draw_top;
view->drawBottom = progressbar_draw_bottom;
return view;
}
void progressbar_destroy(ui_view* view) {
free(view->data);
free(view);
}
void* progressbar_get_data(ui_view* view) {
return ((progressbar_data*) view->data)->data;
}
char* progressbar_get_progress_text(ui_view* view) {
return ((progressbar_data*) view->data)->progressText;
}

11
source/ui/progressbar.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "ui.h"
#define PROGRESS_TEXT_MAX 512
ui_view* progressbar_create(const char* name, const char* info, void* data, void (*update)(ui_view* view, void* data, float* progress, char* progressText),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2));
void progressbar_destroy(ui_view* view);
void* progressbar_get_data(ui_view* view);
char* progressbar_get_progress_text(ui_view* view);

186
source/ui/prompt.c Normal file
View File

@ -0,0 +1,186 @@
#include <3ds.h>
#include <malloc.h>
#include "section/task.h"
#include "prompt.h"
#include "../screen.h"
typedef struct {
const char* text;
u32 rgba;
bool option;
void* data;
void (*update)(ui_view* view, void* data);
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void (*onResponse)(ui_view* view, void* data, bool response);
} prompt_data;
static void notify_response(ui_view* view, prompt_data* promptData, bool response) {
ui_pop();
if(promptData->onResponse != NULL) {
promptData->onResponse(view, promptData->data, response);
}
}
static void prompt_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
svcWaitSynchronization(task_get_mutex(), U64_MAX);
prompt_data* promptData = (prompt_data*) data;
if(promptData->onResponse != NULL) {
if(!promptData->option && (hidKeysDown() & ~KEY_TOUCH)) {
notify_response(view, promptData, false);
svcReleaseMutex(task_get_mutex());
return;
}
if(promptData->option && (hidKeysDown() & (KEY_A | KEY_B))) {
notify_response(view, promptData, (bool) (hidKeysDown() & KEY_A));
svcReleaseMutex(task_get_mutex());
return;
}
if(hidKeysDown() & KEY_TOUCH) {
touchPosition pos;
hidTouchRead(&pos);
if(promptData->option) {
u32 buttonWidth;
u32 buttonHeight;
screen_get_texture_size(&buttonWidth, &buttonHeight, TEXTURE_BUTTON_SMALL);
float yesButtonX = bx1 + (bx2 - bx1) / 2 - 5 - buttonWidth;
float yesButtonY = by2 - 5 - buttonHeight;
if(pos.px >= yesButtonX && pos.py >= yesButtonY && pos.px < yesButtonX + buttonWidth && pos.py < yesButtonY + buttonHeight) {
notify_response(view, promptData, true);
svcReleaseMutex(task_get_mutex());
return;
}
float noButtonX = bx1 + (bx2 - bx1) / 2 + 5;
float noButtonY = by2 - 5 - buttonHeight;
if(pos.px >= noButtonX && pos.py >= noButtonY && pos.px < noButtonX + buttonWidth && pos.py < noButtonY + buttonHeight) {
notify_response(view, promptData, false);
svcReleaseMutex(task_get_mutex());
return;
}
} else {
u32 buttonWidth;
u32 buttonHeight;
screen_get_texture_size(&buttonWidth, &buttonHeight, TEXTURE_BUTTON_LARGE);
float okayButtonX = bx1 + (bx2 - bx1 - buttonWidth) / 2;
float okayButtonY = by2 - 5 - buttonHeight;
if(pos.px >= okayButtonX && pos.py >= okayButtonY && pos.px < okayButtonX + buttonWidth && pos.py < okayButtonY + buttonHeight) {
notify_response(view, promptData, false);
svcReleaseMutex(task_get_mutex());
return;
}
}
}
}
if(promptData->update != NULL) {
promptData->update(view, promptData->data);
}
svcReleaseMutex(task_get_mutex());
}
static void prompt_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
svcWaitSynchronization(task_get_mutex(), U64_MAX);
prompt_data* promptData = (prompt_data*) data;
if(promptData->drawTop != NULL) {
promptData->drawTop(view, promptData->data, x1, y1, x2, y2);
}
svcReleaseMutex(task_get_mutex());
}
static void prompt_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
prompt_data* promptData = (prompt_data*) data;
u32 buttonWidth;
u32 buttonHeight;
if(promptData->option) {
screen_get_texture_size(&buttonWidth, &buttonHeight, TEXTURE_BUTTON_SMALL);
float yesButtonX = x1 + (x2 - x1) / 2 - 5 - buttonWidth;
float yesButtonY = y2 - 5 - buttonHeight;
screen_draw_texture(TEXTURE_BUTTON_SMALL, yesButtonX, yesButtonY, buttonWidth, buttonHeight);
float noButtonX = x1 + (x2 - x1) / 2 + 5;
float noButtonY = y2 - 5 - buttonHeight;
screen_draw_texture(TEXTURE_BUTTON_SMALL, noButtonX, noButtonY, buttonWidth, buttonHeight);
static const char* yes = "Yes (A)";
static const char* no = "No (B)";
float yesWidth;
float yesHeight;
screen_get_string_size(&yesWidth, &yesHeight, yes, 0.5f, 0.5f);
screen_draw_string(yes, yesButtonX + (buttonWidth - yesWidth) / 2, yesButtonY + (buttonHeight - yesHeight) / 2, 0.5f, 0.5f, promptData->rgba, false);
float noWidth;
float noHeight;
screen_get_string_size(&noWidth, &noHeight, no, 0.5f, 0.5f);
screen_draw_string(no, noButtonX + (buttonWidth - noWidth) / 2, noButtonY + (buttonHeight - noHeight) / 2, 0.5f, 0.5f, promptData->rgba, false);
} else {
screen_get_texture_size(&buttonWidth, &buttonHeight, TEXTURE_BUTTON_LARGE);
float okayButtonX = x1 + (x2 - x1 - buttonWidth) / 2;
float okayButtonY = y2 - 5 - buttonHeight;
screen_draw_texture(TEXTURE_BUTTON_LARGE, okayButtonX, okayButtonY, buttonWidth, buttonHeight);
static const char* okay = "Okay (Any Button)";
float okayWidth;
float okayHeight;
screen_get_string_size(&okayWidth, &okayHeight, okay, 0.5f, 0.5f);
screen_draw_string(okay, okayButtonX + (buttonWidth - okayWidth) / 2, okayButtonY + (buttonHeight - okayHeight) / 2, 0.5f, 0.5f, promptData->rgba, false);
}
float textWidth;
float textHeight;
screen_get_string_size(&textWidth, &textHeight, promptData->text, 0.5f, 0.5f);
screen_draw_string(promptData->text, x1 + (x2 - x1 - textWidth) / 2, y1 + (y2 - 5 - buttonHeight - y1 - textHeight) / 2, 0.5f, 0.5f, promptData->rgba, false);
}
ui_view* prompt_create(const char* name, const char* text, u32 rgba, bool option, void* data, void (*update)(ui_view* view, void* data),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, bool response)) {
prompt_data* promptData = (prompt_data*) calloc(1, sizeof(prompt_data));
promptData->text = text;
promptData->rgba = rgba;
promptData->option = option;
promptData->data = data;
promptData->update = update;
promptData->drawTop = drawTop;
promptData->onResponse = onResponse;
ui_view* view = (ui_view*) calloc(1, sizeof(ui_view));
view->name = name;
view->info = "";
view->data = promptData;
view->update = prompt_update;
view->drawTop = prompt_draw_top;
view->drawBottom = prompt_draw_bottom;
return view;
}
void prompt_destroy(ui_view* view) {
free(view->data);
free(view);
}
void* prompt_get_data(ui_view* view) {
return ((prompt_data*) view->data)->data;
}

9
source/ui/prompt.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "ui.h"
ui_view* prompt_create(const char* name, const char* text, u32 rgba, bool option, void* data, void (*update)(ui_view* view, void* data),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, bool response));
void prompt_destroy(ui_view* view);
void* prompt_get_data(ui_view* view);

View File

@ -0,0 +1,26 @@
#pragma once
#include "../task.h"
void action_browse_ext_save_data(ext_save_data_info* info);
void action_browse_system_save_data(system_save_data_info* info);
void action_install_cias_nand(file_info* info);
void action_install_cias_sd(file_info* info);
void action_copy_contents(file_info* info);
void action_delete_contents(file_info* info);
void action_delete_dir_contents(file_info* info);
void action_delete_dir_cias(file_info* info);
void action_paste_contents(file_info* info);
void action_delete_pending_title(pending_title_info* info);
void action_delete_all_pending_titles(pending_title_info* info);
void action_delete_ticket(ticket_info* info);
void action_delete_title(title_info* info);
void action_launch_title(title_info* info);
void action_browse_title_save_data(title_info* info);
void action_import_secure_value(title_info* info);
void action_export_secure_value(title_info* info);

View File

@ -0,0 +1,10 @@
#include <3ds.h>
#include "action.h"
#include "../section.h"
void action_browse_ext_save_data(ext_save_data_info* info) {
u32 path[3] = {info->mediaType, (u32) (info->extSaveDataId & 0xFFFFFFFF), (u32) ((info->extSaveDataId >> 32) & 0xFFFFFFFF)};
FS_Archive archive = {info->shared ? ARCHIVE_SHARED_EXTDATA : ARCHIVE_EXTDATA, {PATH_BINARY, 12, path}};
files_open(archive);
}

View File

@ -0,0 +1,10 @@
#include <3ds.h>
#include "action.h"
#include "../section.h"
void action_browse_system_save_data(system_save_data_info* info) {
u32 path[2] = {MEDIATYPE_NAND, (u32) (info->systemSaveDataId & 0xFFFFFFFF)};
FS_Archive archive = {ARCHIVE_SYSTEM_SAVEDATA, {PATH_BINARY, 8, path}};
files_open(archive);
}

View File

@ -0,0 +1,10 @@
#include <3ds.h>
#include "action.h"
#include "../section.h"
void action_browse_title_save_data(title_info* info) {
u32 path[3] = {info->mediaType, (u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF)};
FS_Archive archive = {ARCHIVE_USER_SAVEDATA, {PATH_BINARY, 12, path}};
files_open(archive);
}

View File

@ -0,0 +1,57 @@
#include <sys/syslimits.h>
#include <malloc.h>
#include <string.h>
#include <3ds.h>
#include <3ds/services/fs.h>
#include "clipboard.h"
static bool clipboard_has = false;
static FS_Archive clipboard_archive;
static void* clipboard_archive_path;
static char clipboard_path[PATH_MAX];
bool clipboard_has_contents() {
return clipboard_has;
}
FS_Archive* clipboard_get_archive() {
return &clipboard_archive;
}
char* clipboard_get_path() {
return clipboard_path;
}
Result clipboard_set_contents(FS_Archive archive, const char* path) {
clipboard_clear();
clipboard_has = true;
clipboard_archive = archive;
strncpy(clipboard_path, path, PATH_MAX);
if(clipboard_archive.lowPath.size > 0) {
clipboard_archive_path = calloc(1, clipboard_archive.lowPath.size);
memcpy(clipboard_archive_path, clipboard_archive.lowPath.data, clipboard_archive.lowPath.size);
clipboard_archive.lowPath.data = clipboard_archive_path;
}
clipboard_archive.handle = 0;
return FSUSER_OpenArchive(&clipboard_archive);
}
void clipboard_clear() {
if(clipboard_archive.handle != 0) {
FSUSER_CloseArchive(&clipboard_archive);
clipboard_archive.handle = 0;
}
if(clipboard_archive_path != NULL) {
free(clipboard_archive_path);
clipboard_archive_path = NULL;
}
clipboard_has = false;
clipboard_path[0] = '\0';
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <stdbool.h>
bool clipboard_has_contents();
FS_Archive* clipboard_get_archive();
char* clipboard_get_name();
char* clipboard_get_path();
bool clipboard_is_cut();
Result clipboard_set_contents(FS_Archive archive, const char* path);
void clipboard_clear();

View File

@ -0,0 +1,22 @@
#include <3ds.h>
#include "action.h"
#include "clipboard.h"
#include "../../error.h"
#include "../../prompt.h"
static void action_copy_files_success_onresponse(ui_view* view, void* data, bool response) {
prompt_destroy(view);
}
void action_copy_contents(file_info* info) {
Result res = 0;
if(R_FAILED(res = clipboard_set_contents(*info->archive, info->path))) {
error_display_res(info, ui_draw_file_info, res, "Failed to copy contents to clipboard.");
return;
}
ui_push(prompt_create("Success", "Content copied to clipboard.", 0xFF000000, false, info, NULL, ui_draw_file_info, action_copy_files_success_onresponse));
}

View File

@ -0,0 +1,47 @@
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
static void action_delete_all_pending_titles_success_onresponse(ui_view* view, void* data, bool response) {
task_refresh_pending_titles();
prompt_destroy(view);
}
static void action_delete_all_pending_titles_update(ui_view* view, void* data, float* progress, char* progressText) {
pending_title_info* info = (pending_title_info*) data;
Result res = AM_DeleteAllPendingTitles(MEDIATYPE_NAND);
if(R_SUCCEEDED(res)) {
res = AM_DeleteAllPendingTitles(MEDIATYPE_SD);
}
if(R_FAILED(res)) {
progressbar_destroy(view);
ui_pop();
error_display_res(info, ui_draw_pending_title_info, res, "Failed to delete pending titles.");
return;
}
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Pending titles deleted.", 0xFF000000, false, info, NULL, NULL, action_delete_all_pending_titles_success_onresponse));
}
static void action_delete_all_pending_titles_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_push(progressbar_create("Deleting Pending Titles", "", data, action_delete_all_pending_titles_update, NULL));
}
}
void action_delete_all_pending_titles(pending_title_info* info) {
ui_push(prompt_create("Confirmation", "Delete all pending titles?", 0xFF000000, true, NULL, NULL, NULL, action_delete_all_pending_titles_onresponse));
}

View File

@ -0,0 +1,153 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
#include "../../../util.h"
typedef struct {
file_info* base;
u32 processed;
u32 total;
char** contents;
} delete_dir_contents_data;
static void action_delete_dir_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
ui_draw_file_info(view, ((delete_dir_contents_data*) data)->base, x1, y1, x2, y2);
}
static void action_delete_dir_contents_free_data(delete_dir_contents_data* data) {
util_free_contents(data->contents, data->total);
free(data);
}
static void action_delete_dir_contents_done_onresponse(ui_view* view, void* data, bool response) {
action_delete_dir_contents_free_data((delete_dir_contents_data*) data);
if(task_get_files_path() != NULL && !util_is_dir(task_get_files_archive(), task_get_files_path())) {
char parentPath[PATH_MAX];
util_get_parent_path(parentPath, task_get_files_path(), PATH_MAX);
strncpy(task_get_files_path(), parentPath, PATH_MAX);
}
task_refresh_files();
prompt_destroy(view);
}
static void action_delete_dir_contents_update(ui_view* view, void* data, float* progress, char* progressText) {
delete_dir_contents_data* deleteData = (delete_dir_contents_data*) data;
if(hidKeysDown() & KEY_B) {
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Failure", "Delete cancelled.", 0xFF000000, false, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_done_onresponse));
return;
}
if(deleteData->processed >= deleteData->total) {
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Contents deleted.", 0xFF000000, false, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_done_onresponse));
} else {
FS_Archive* archive = deleteData->base->archive;
char* path = deleteData->contents[deleteData->processed];
FS_Path fsPath = fsMakePath(PATH_ASCII, path);
Result res = 0;
if(util_is_dir(archive, path)) {
res = FSUSER_DeleteDirectory(*archive, fsPath);
} else {
res = FSUSER_DeleteFile(*archive, fsPath);
}
if(R_FAILED(res)) {
if(deleteData->processed >= deleteData->total - 1) {
ui_pop();
}
if(strlen(path) > 48) {
error_display_res(deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.45s...", path);
} else {
error_display_res(deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.48s", path);
}
if(deleteData->processed >= deleteData->total - 1) {
action_delete_dir_contents_free_data(deleteData);
progressbar_destroy(view);
return;
}
}
deleteData->processed++;
*progress = (float) deleteData->processed / (float) deleteData->total;
snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu", deleteData->processed, deleteData->total);
}
}
static void action_delete_dir_contents_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_view* progressView = progressbar_create("Deleting Contents", "Press B to cancel.", data, action_delete_dir_contents_update, action_delete_dir_contents_draw_top);
snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((delete_dir_contents_data*) data)->total);
ui_push(progressView);
}
}
void action_delete_contents(file_info* info) {
delete_dir_contents_data* data = (delete_dir_contents_data*) calloc(1, sizeof(delete_dir_contents_data));
data->base = info;
data->processed = 0;
Result res = 0;
if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, true, false, NULL, NULL))) {
error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list.");
free(data);
return;
}
ui_push(prompt_create("Confirmation", "Delete the selected content?", 0xFF000000, true, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_onresponse));
}
void action_delete_dir_contents(file_info* info) {
delete_dir_contents_data* data = (delete_dir_contents_data*) calloc(1, sizeof(delete_dir_contents_data));
data->base = info;
data->processed = 0;
Result res = 0;
if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, true, false, info->path, util_filter_not_path))) {
error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list.");
free(data);
return;
}
ui_push(prompt_create("Confirmation", "Delete all contents of the selected directory?", 0xFF000000, true, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_onresponse));
}
void action_delete_dir_cias(file_info* info) {
delete_dir_contents_data* data = (delete_dir_contents_data*) calloc(1, sizeof(delete_dir_contents_data));
data->base = info;
data->processed = 0;
Result res = 0;
if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, false, false, ".cia", util_filter_file_extension))) {
error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list.");
free(data);
return;
}
ui_push(prompt_create("Confirmation", "Delete all CIAs in the selected directory?", 0xFF000000, true, data, NULL, action_delete_dir_contents_draw_top, action_delete_dir_contents_onresponse));
}

View File

@ -0,0 +1,43 @@
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
static void action_delete_pending_title_success_onresponse(ui_view* view, void* data, bool response) {
task_refresh_pending_titles();
prompt_destroy(view);
}
static void action_delete_pending_title_update(ui_view* view, void* data, float* progress, char* progressText) {
pending_title_info* info = (pending_title_info*) data;
Result res = AM_DeletePendingTitle(info->mediaType, info->titleId);
if(R_FAILED(res)) {
progressbar_destroy(view);
ui_pop();
error_display_res(info, ui_draw_pending_title_info, res, "Failed to delete pending title.");
return;
}
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Pending title deleted.", 0xFF000000, false, info, NULL, ui_draw_pending_title_info, action_delete_pending_title_success_onresponse));
}
static void action_delete_pending_title_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_push(progressbar_create("Deleting Pending Title", "", data, action_delete_pending_title_update, ui_draw_pending_title_info));
}
}
void action_delete_pending_title(pending_title_info* info) {
ui_push(prompt_create("Confirmation", "Delete the selected pending title?", 0xFF000000, true, info, NULL, ui_draw_pending_title_info, action_delete_pending_title_onresponse));
}

View File

@ -0,0 +1,43 @@
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
static void action_delete_ticket_success_onresponse(ui_view* view, void* data, bool response) {
task_refresh_tickets();
prompt_destroy(view);
}
static void action_delete_ticket_update(ui_view* view, void* data, float* progress, char* progressText) {
ticket_info* info = (ticket_info*) data;
Result res = AM_DeleteTicket(info->ticketId);
if(R_FAILED(res)) {
progressbar_destroy(view);
ui_pop();
error_display_res(info, ui_draw_ticket_info, res, "Failed to delete ticket.");
return;
}
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Ticket deleted.", 0xFF000000, false, info, NULL, ui_draw_ticket_info, action_delete_ticket_success_onresponse));
}
static void action_delete_ticket_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_push(progressbar_create("Deleting Ticket", "", data, action_delete_ticket_update, ui_draw_ticket_info));
}
}
void action_delete_ticket(ticket_info* info) {
ui_push(prompt_create("Confirmation", "Delete the selected ticket?", 0xFF000000, true, info, NULL, ui_draw_ticket_info, action_delete_ticket_onresponse));
}

View File

@ -0,0 +1,43 @@
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
static void action_delete_title_success_onresponse(ui_view* view, void* data, bool response) {
task_refresh_pending_titles();
prompt_destroy(view);
}
static void action_delete_title_update(ui_view* view, void* data, float* progress, char* progressText) {
title_info* info = (title_info*) data;
Result res = AM_DeleteTitle(info->mediaType, info->titleId);
if(R_FAILED(res)) {
progressbar_destroy(view);
ui_pop();
error_display_res(info, ui_draw_title_info, res, "Failed to delete title.");
return;
}
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Title deleted.", 0xFF000000, false, info, NULL, ui_draw_title_info, action_delete_title_success_onresponse));
}
static void action_delete_title_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_push(progressbar_create("Deleting Title", "", data, action_delete_title_update, ui_draw_title_info));
}
}
void action_delete_title(title_info* info) {
ui_push(prompt_create("Confirmation", "Delete the selected title?", 0xFF000000, true, info, NULL, ui_draw_title_info, action_delete_title_onresponse));
}

View File

@ -0,0 +1,75 @@
#include <stdio.h>
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
#include "../../../util.h"
static void action_export_secure_value_end_onresponse(ui_view* view, void* data, bool response) {
prompt_destroy(view);
}
static void action_export_secure_value_update(ui_view* view, void* data, float* progress, char* progressText) {
title_info* info = (title_info*) data;
Result res = 0;
bool exists = false;
u64 value = 0;
if(R_SUCCEEDED(res = FSUSER_GetSaveDataSecureValue(&exists, &value, SECUREVALUE_SLOT_SD, (u32) ((info->titleId >> 8) & 0xFFFFF), (u8) (info->titleId & 0xFF)))) {
if(!exists) {
ui_push(prompt_create("Failure", "Secure value not set.", 0xFF000000, false, info, NULL, ui_draw_title_info, action_export_secure_value_end_onresponse));
progressbar_destroy(view);
ui_pop();
return;
}
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive))) {
if(R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/securevalue/"))) {
char pathBuf[PATH_MAX];
snprintf(pathBuf, PATH_MAX, "/fbi/securevalue/%016llX.dat", info->titleId);
Handle fileHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&fileHandle, sdmcArchive, fsMakePath(PATH_ASCII, pathBuf), FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) {
u32 bytesWritten = 0;
res = FSFILE_Write(fileHandle, &bytesWritten, 0, &value, sizeof(u64), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME);
FSFILE_Close(fileHandle);
}
}
FSUSER_CloseArchive(&sdmcArchive);
}
}
if(R_FAILED(res)) {
error_display_res(info, ui_draw_title_info, res, "Failed to export secure value.");
progressbar_destroy(view);
ui_pop();
return;
}
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Secure value exported.", 0xFF000000, false, info, NULL, ui_draw_title_info, action_export_secure_value_end_onresponse));
}
static void action_export_secure_value_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_push(progressbar_create("Exporting Secure Value", "", data, action_export_secure_value_update, ui_draw_title_info));
}
}
void action_export_secure_value(title_info* info) {
ui_push(prompt_create("Confirmation", "Export secure value for the selected title?", 0xFF000000, true, info, NULL, ui_draw_title_info, action_export_secure_value_onresponse));
}

View File

@ -0,0 +1,59 @@
#include <stdio.h>
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
static void action_import_secure_value_end_onresponse(ui_view* view, void* data, bool response) {
prompt_destroy(view);
}
static void action_import_secure_value_update(ui_view* view, void* data, float* progress, char* progressText) {
title_info* info = (title_info*) data;
char pathBuf[PATH_MAX];
snprintf(pathBuf, PATH_MAX, "/fbi/securevalue/%016llX.dat", info->titleId);
Result res = 0;
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
Handle fileHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, sdmcArchive, fsMakePath(PATH_ASCII, pathBuf), FS_OPEN_READ, 0))) {
u32 bytesRead = 0;
u64 value = 0;
if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, &value, sizeof(u64)))) {
res = FSUSER_SetSaveDataSecureValue(value, SECUREVALUE_SLOT_SD, (u32) ((info->titleId >> 8) & 0xFFFFF), (u8) (info->titleId & 0xFF));
}
FSFILE_Close(fileHandle);
}
if(R_FAILED(res)) {
error_display_res(info, ui_draw_title_info, res, "Failed to import secure value.");
progressbar_destroy(view);
ui_pop();
return;
}
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Secure value imported.", 0xFF000000, false, info, NULL, ui_draw_title_info, action_import_secure_value_end_onresponse));
}
static void action_import_secure_value_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_push(progressbar_create("Importing Secure Value", "", data, action_import_secure_value_update, ui_draw_title_info));
}
}
void action_import_secure_value(title_info* info) {
ui_push(prompt_create("Confirmation", "Import secure value for the selected title?", 0xFF000000, true, info, NULL, ui_draw_title_info, action_import_secure_value_onresponse));
}

View File

@ -0,0 +1,206 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
#include "../../../util.h"
typedef struct {
file_info* base;
FS_MediaType dest;
Handle currHandle;
bool installStarted;
u64 currProcessed;
u64 currTotal;
u32 processed;
u32 total;
char** contents;
} install_cias_data;
static Result action_install_cias_read(void* data, u32* bytesRead, void* buffer, u32 size) {
install_cias_data* installData = (install_cias_data*) data;
u32 read = 0;
Result res = FSFILE_Read(installData->currHandle, &read, installData->currProcessed, buffer, size);
if(R_SUCCEEDED(res)) {
installData->currProcessed += read;
if(bytesRead != NULL) {
*bytesRead = read;
}
}
return res;
}
static void action_install_cias_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
ui_draw_file_info(view, ((install_cias_data*) data)->base, x1, y1, x2, y2);
}
static void action_install_cias_free_data(install_cias_data* data) {
util_free_contents(data->contents, data->total);
free(data);
}
static void action_install_cias_done_onresponse(ui_view* view, void* data, bool response) {
action_install_cias_free_data((install_cias_data*) data);
task_refresh_titles();
prompt_destroy(view);
}
static void action_install_cias_update(ui_view* view, void* data, float* progress, char* progressText) {
install_cias_data* installData = (install_cias_data*) data;
bool cancelled = false;
if(hidKeysDown() & KEY_B) {
task_cancel_cia_install();
while(task_is_cia_installing()) {
svcSleepThread(1000000);
}
cancelled = true;
}
if(!task_is_cia_installing()) {
char* path = installData->contents[installData->processed];
if(installData->installStarted || cancelled) {
if(installData->currHandle != 0) {
FSFILE_Close(installData->currHandle);
installData->currHandle = 0;
}
Result res = task_get_cia_install_result();
if(R_FAILED(res)) {
if(installData->processed >= installData->total - 1) {
ui_pop();
}
if(res == CIA_INSTALL_RESULT_CANCELLED) {
ui_push(prompt_create("Failure", "Install cancelled.", 0xFF000000, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse));
} else if(res == CIA_INSTALL_RESULT_ERRNO) {
if(strlen(path) > 48) {
error_display_errno(installData->base, ui_draw_file_info, task_get_cia_install_errno(), "Failed to install CIA file.\n%.45s...", path);
} else {
error_display_errno(installData->base, ui_draw_file_info, task_get_cia_install_errno(), "Failed to install CIA file.\n%.48s", path);
}
} else if(res == CIA_INSTALL_RESULT_WRONG_SYSTEM) {
ui_push(prompt_create("Failure", "Attempted to install to wrong system.", 0xFF000000, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse));
} else {
if(strlen(path) > 48) {
error_display_res(installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.45s...", path);
} else {
error_display_res(installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.48s", path);
}
}
if(installData->processed >= installData->total - 1) {
if(res != CIA_INSTALL_RESULT_CANCELLED && res != CIA_INSTALL_RESULT_WRONG_SYSTEM) {
action_install_cias_free_data(installData);
}
progressbar_destroy(view);
return;
}
}
installData->processed++;
}
installData->installStarted = true;
if(installData->processed >= installData->total) {
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Install finished.", 0xFF000000, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse));
return;
} else {
FS_Archive* archive = installData->base->archive;
FS_Path fsPath = fsMakePath(PATH_ASCII, path);
Result res = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&installData->currHandle, *archive, fsPath, FS_OPEN_READ, 0))) {
u64 size = 0;
if(R_SUCCEEDED(res = FSFILE_GetSize(installData->currHandle, &size))) {
installData->currTotal = size;
task_request_cia_install(installData->dest, installData->currTotal, installData, action_install_cias_read);
}
}
if(R_FAILED(res)) {
if(installData->currHandle != 0) {
FSFILE_Close(installData->currHandle);
installData->currHandle = 0;
}
if(installData->processed >= installData->total - 1) {
ui_pop();
}
if(strlen(path) > 48) {
error_display_res(installData->base, ui_draw_file_info, res, "Failed to open CIA file.\n%.45s...", path);
} else {
error_display_res(installData->base, ui_draw_file_info, res, "Failed to open CIA file.\n%.48s", path);
}
if(installData->processed >= installData->total - 1) {
action_install_cias_free_data(installData);
progressbar_destroy(view);
return;
}
}
}
}
*progress = (float) ((double) installData->currProcessed / (double) installData->currTotal);
snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", installData->processed, installData->total, installData->currProcessed / 1024.0 / 1024.0, installData->currTotal / 1024.0 / 1024.0);
}
static void action_install_cias_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_view* progressView = progressbar_create("Installing CIA(s)", "Press B to cancel.", data, action_install_cias_update, action_install_cias_draw_top);
snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((install_cias_data*) data)->total);
ui_push(progressView);
}
}
static void action_install_cias(file_info* info, FS_MediaType mediaType) {
install_cias_data* data = (install_cias_data*) calloc(1, sizeof(install_cias_data));
data->base = info;
data->dest = mediaType;
data->installStarted = false;
data->currProcessed = 0;
data->currTotal = 0;
data->processed = 0;
Result res = 0;
if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, info->archive, info->path, false, false, ".cia", util_filter_file_extension))) {
error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list.");
free(data);
return;
}
ui_push(prompt_create("Confirmation", "Install the selected CIA(s)?", 0xFF000000, true, data, NULL, action_install_cias_draw_top, action_install_cias_onresponse));
}
void action_install_cias_sd(file_info* info) {
action_install_cias(info, MEDIATYPE_SD);
}
void action_install_cias_nand(file_info* info) {
action_install_cias(info, MEDIATYPE_NAND);
}

View File

@ -0,0 +1,47 @@
#include <3ds.h>
#include "action.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
static void action_launch_title_update(ui_view* view, void* data, float* progress, char* progressText) {
title_info* info = (title_info*) data;
u8 buf0[0x300];
u8 buf1[0x20];
aptOpenSession();
Result res = APT_PrepareToDoAppJump(0, info->titleId, info->mediaType);
if(R_SUCCEEDED(res)) {
res = APT_DoAppJump(0x300, 0x20, buf0, buf1);
}
aptCloseSession();
progressbar_destroy(view);
if(R_SUCCEEDED(res)) {
while(ui_peek() != NULL) {
ui_pop();
}
} else {
progressbar_destroy(view);
ui_pop();
error_display_res(info, ui_draw_title_info, res, "Failed to launch title.");
}
}
static void action_launch_title_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_push(progressbar_create("Launching Title", "", data, action_launch_title_update, ui_draw_title_info));
}
}
void action_launch_title(title_info* info) {
ui_push(prompt_create("Confirmation", "Launch the selected title?", 0xFF000000, true, info, NULL, ui_draw_title_info, action_launch_title_onresponse));
}

View File

@ -0,0 +1,169 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include <3ds/services/fs.h>
#include "action.h"
#include "clipboard.h"
#include "../../error.h"
#include "../../progressbar.h"
#include "../../prompt.h"
#include "../../../util.h"
#include "../task.h"
typedef struct {
file_info* base;
u32 processed;
u32 total;
char** contents;
} paste_files_data;
static void action_paste_files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
ui_draw_file_info(view, ((paste_files_data*) data)->base, x1, y1, x2, y2);
}
static void action_paste_files_failure_onresponse(ui_view* view, void* data, bool response) {
prompt_destroy(view);
}
static void action_paste_files_free_data(paste_files_data* data) {
util_free_contents(data->contents, data->total);
free(data);
}
static void action_paste_files_done_onresponse(ui_view* view, void* data, bool response) {
action_paste_files_free_data((paste_files_data*) data);
task_refresh_files();
prompt_destroy(view);
}
static void action_paste_files_update(ui_view* view, void* data, float* progress, char* progressText) {
paste_files_data* pasteData = (paste_files_data*) data;
if(hidKeysDown() & KEY_B) {
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Failure", "Paste cancelled.", 0xFF000000, false, data, NULL, action_paste_files_draw_top, action_paste_files_done_onresponse));
return;
}
if(pasteData->processed >= pasteData->total) {
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Contents pasted.", 0xFF000000, false, data, NULL, action_paste_files_draw_top, action_paste_files_done_onresponse));
} else {
FS_Archive* srcArchive = clipboard_get_archive();
char* srcPath = pasteData->contents[pasteData->processed];
FS_Path srcFsPath = fsMakePath(PATH_ASCII, srcPath);
char baseDstPath[PATH_MAX];
if(pasteData->base->isDirectory) {
strncpy(baseDstPath, pasteData->base->path, PATH_MAX);
} else {
util_get_parent_path(baseDstPath, pasteData->base->path, PATH_MAX);
}
FS_Archive* dstArchive = pasteData->base->archive;
char dstPath[PATH_MAX];
util_get_parent_path(dstPath, clipboard_get_path(), PATH_MAX);
snprintf(dstPath, PATH_MAX, "%s%s", baseDstPath, srcPath + strlen(dstPath));
FS_Path dstFsPath = fsMakePath(PATH_ASCII, dstPath);
Result res = 0;
if(util_is_dir(srcArchive, srcPath)) {
res = FSUSER_CreateDirectory(*dstArchive, dstFsPath, 0);
} else {
Handle srcFileHandle;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&srcFileHandle, *srcArchive, srcFsPath, FS_OPEN_READ, 0))) {
u64 size = 0;
if(R_SUCCEEDED(res = FSFILE_GetSize(srcFileHandle, &size)) && R_SUCCEEDED(res = FSUSER_CreateFile(*dstArchive, dstFsPath, 0, size))) {
Handle dstFileHandle;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&dstFileHandle, *dstArchive, dstFsPath, FS_OPEN_WRITE, 0))) {
u32 bytesRead = 0;
u32 offset = 0;
u8* buffer = (u8*) calloc(1, 1024 * 512);
while(R_SUCCEEDED(FSFILE_Read(srcFileHandle, &bytesRead, offset, buffer, 1024 * 512)) && bytesRead > 0) {
u32 bytesWritten = 0;
if(R_FAILED(res = FSFILE_Write(dstFileHandle, &bytesWritten, offset, buffer, bytesRead, 0))) {
break;
}
offset += bytesWritten;
}
free(buffer);
FSFILE_Close(dstFileHandle);
if(R_SUCCEEDED(res) && dstArchive->id == ARCHIVE_USER_SAVEDATA) {
res = FSUSER_ControlArchive(*dstArchive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
}
}
}
FSFILE_Close(srcFileHandle);
}
}
if(R_FAILED(res)) {
if(pasteData->processed >= pasteData->total - 1) {
ui_pop();
}
if(strlen(srcPath) > 48) {
error_display_res(pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.45s...", srcPath);
} else {
error_display_res(pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.48s", srcPath);
}
if(pasteData->processed >= pasteData->total - 1) {
action_paste_files_free_data(pasteData);
progressbar_destroy(view);
return;
}
}
pasteData->processed++;
*progress = (float) pasteData->processed / (float) pasteData->total;
snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu", pasteData->processed, pasteData->total);
}
}
static void action_paste_files_onresponse(ui_view* view, void* data, bool response) {
if(response) {
prompt_destroy(view);
ui_view* progressView = progressbar_create("Pasting Contents", "Press B to cancel.", data, action_paste_files_update, action_paste_files_draw_top);
snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", ((paste_files_data*) data)->total);
ui_push(progressView);
}
}
void action_paste_contents(file_info* info) {
if(!clipboard_has_contents()) {
ui_push(prompt_create("Failure", "Clipboard empty.", 0xFF000000, false, info, NULL, ui_draw_file_info, action_paste_files_failure_onresponse));
return;
}
paste_files_data* data = (paste_files_data*) calloc(1, sizeof(paste_files_data));
data->base = info;
data->processed = 0;
Result res = 0;
if(R_FAILED(res = util_populate_contents(&data->contents, &data->total, clipboard_get_archive(), clipboard_get_path(), true, true, NULL, NULL))) {
error_display_res(info, ui_draw_file_info, res, "Failed to retrieve content list.");
free(data);
return;
}
ui_push(prompt_create("Confirmation", "Paste clipboard contents to the current directory?", 0xFF000000, true, data, NULL, action_paste_files_draw_top, action_paste_files_onresponse));
}

View File

@ -0,0 +1,76 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "action/action.h"
#include "section.h"
#define EXTSAVEDATA_ACTION_COUNT 1
static u32 extsavedata_action_count = EXTSAVEDATA_ACTION_COUNT;
static list_item extsavedata_action_items[EXTSAVEDATA_ACTION_COUNT] = {
{"Browse Save Data", 0xFF000000, action_browse_ext_save_data},
};
static void extsavedata_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
ui_draw_ext_save_data_info(view, data, x1, y1, x2, y2);
}
static void extsavedata_action_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(ext_save_data_info*) = (void(*)(ext_save_data_info*)) selected->data;
list_destroy(view);
ui_pop();
action((ext_save_data_info*) data);
return;
}
if(*itemCount != &extsavedata_action_count || *items != extsavedata_action_items) {
*itemCount = &extsavedata_action_count;
*items = extsavedata_action_items;
}
}
static ui_view* extsavedata_action_create(ext_save_data_info* info) {
return list_create("Ext Save Data Action", "A: Select, B: Return", info, extsavedata_action_update, extsavedata_action_draw_top);
}
static void extsavedata_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
if(selected != NULL && selected->data != NULL) {
ui_draw_ext_save_data_info(view, selected->data, x1, y1, x2, y2);
}
}
static void extsavedata_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(hidKeysDown() & KEY_X) {
task_refresh_ext_save_data();
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
ui_push(extsavedata_action_create((ext_save_data_info*) selected->data));
}
if(*itemCount != task_get_ext_save_data_count() || *items != task_get_ext_save_data()) {
*itemCount = task_get_ext_save_data_count();
*items = task_get_ext_save_data();
}
}
void extsavedata_open() {
ui_push(list_create("Ext Save Data", "A: Select, B: Return, X: Refresh", NULL, extsavedata_update, extsavedata_draw_top));
}

217
source/ui/section/files.c Normal file
View File

@ -0,0 +1,217 @@
#include <dirent.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include <3ds/services/fs.h>
#include "action/action.h"
#include "section.h"
#include "../error.h"
#include "../../util.h"
typedef struct {
bool setup;
FS_Archive archive;
void* archivePath;
char path[PATH_MAX];
} files_data;
#define FILES_ACTION_COUNT 3
static u32 files_action_count = FILES_ACTION_COUNT;
static list_item files_action_items[FILES_ACTION_COUNT] = {
{"Delete", 0xFF000000, action_delete_contents},
{"Copy", 0xFF000000, action_copy_contents},
{"Paste", 0xFF000000, action_paste_contents},
};
#define CIA_FILES_ACTION_COUNT 5
static u32 cia_files_action_count = CIA_FILES_ACTION_COUNT;
static list_item cia_files_action_items[CIA_FILES_ACTION_COUNT] = {
{"Install CIA to SD", 0xFF000000, action_install_cias_sd},
{"Install CIA to NAND", 0xFF000000, action_install_cias_nand},
{"Delete", 0xFF000000, action_delete_contents},
{"Copy", 0xFF000000, action_copy_contents},
{"Paste", 0xFF000000, action_paste_contents},
};
#define DIRECTORIES_ACTION_COUNT 7
static u32 directories_action_count = DIRECTORIES_ACTION_COUNT;
static list_item directories_action_items[DIRECTORIES_ACTION_COUNT] = {
{"Install all CIAs to SD", 0xFF000000, action_install_cias_sd},
{"Install all CIAs to NAND", 0xFF000000, action_install_cias_nand},
{"Delete all CIAs", 0xFF000000, action_delete_dir_cias},
{"Delete all contents", 0xFF000000, action_delete_dir_contents},
{"Delete", 0xFF000000, action_delete_contents},
{"Copy", 0xFF000000, action_copy_contents},
{"Paste", 0xFF000000, action_paste_contents},
};
static void files_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
ui_draw_file_info(view, data, x1, y1, x2, y2);
}
static void files_action_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
file_info* fileInfo = (file_info*) data;
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(file_info*) = (void(*)(file_info*)) selected->data;
list_destroy(view);
ui_pop();
action(fileInfo);
return;
}
if(fileInfo->isDirectory) {
if(*itemCount != &directories_action_count || *items != directories_action_items) {
*itemCount = &directories_action_count;
*items = directories_action_items;
}
} else if(fileInfo->isCia) {
if(*itemCount != &cia_files_action_count || *items != cia_files_action_items) {
*itemCount = &cia_files_action_count;
*items = cia_files_action_items;
}
} else {
if(*itemCount != &files_action_count || *items != files_action_items) {
*itemCount = &files_action_count;
*items = files_action_items;
}
}
}
static ui_view* files_action_create(file_info* info) {
return list_create(info->isDirectory ? "Directory Action" : "File Action", "A: Select, B: Return", info, files_action_update, files_action_draw_top);
}
static void files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
if(selected != NULL && selected->data != NULL) {
ui_draw_file_info(view, selected->data, x1, y1, x2, y2);
}
}
static void files_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
files_data* filesData = (files_data*) data;
if(hidKeysDown() & KEY_B) {
if(strcmp(filesData->path, "/") == 0) {
if(filesData->archive.handle != 0) {
FSUSER_CloseArchive(&filesData->archive);
filesData->archive.handle = 0;
}
if(filesData->archivePath != NULL) {
free(filesData->archivePath);
filesData->archivePath = NULL;
}
free(data);
list_destroy(view);
ui_pop();
return;
} else if(*items != NULL && *itemCount != NULL) {
for(u32 i = 0; i < **itemCount; i++) {
char* name = (*items)[i].name;
file_info* fileInfo = (*items)[i].data;
if(fileInfo != NULL && strcmp(name, "..") == 0) {
strncpy(filesData->path, fileInfo->path, PATH_MAX);
task_refresh_files();
break;
}
}
}
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
file_info* fileInfo = (file_info*) selected->data;
if(strcmp(selected->name, ".") == 0) {
ui_push(files_action_create(fileInfo));
} else if(strcmp(selected->name, "..") == 0) {
strncpy(filesData->path, fileInfo->path, PATH_MAX);
task_refresh_files();
} else {
if(util_is_dir(&filesData->archive, fileInfo->path)) {
strncpy(filesData->path, fileInfo->path, PATH_MAX);
task_refresh_files();
} else {
ui_push(files_action_create(fileInfo));
}
}
}
if(hidKeysDown() & KEY_X) {
task_refresh_files();
}
if(!filesData->setup || task_get_files_archive() != &filesData->archive || task_get_files_path() != filesData->path) {
filesData->setup = true;
task_set_files_archive(&filesData->archive);
task_set_files_path(filesData->path);
task_refresh_files();
}
if(*itemCount != task_get_files_count() || *items != task_get_files()) {
*itemCount = task_get_files_count();
*items = task_get_files();
}
}
void files_open(FS_Archive archive) {
files_data* data = (files_data*) calloc(1, sizeof(files_data));
data->setup = false;
data->archive = archive;
snprintf(data->path, PATH_MAX, "/");
if(data->archive.lowPath.size > 0) {
data->archivePath = calloc(1, data->archive.lowPath.size);
memcpy(data->archivePath, data->archive.lowPath.data, data->archive.lowPath.size);
data->archive.lowPath.data = data->archivePath;
}
data->archive.handle = 0;
Result res = 0;
if(R_FAILED(res = FSUSER_OpenArchive(&data->archive))) {
error_display_res(NULL, NULL, res, "Failed to open file listing archive.");
if(data->archivePath != NULL) {
free(data->archivePath);
}
free(data);
return;
}
ui_push(list_create("Files", "A: Select, B: Back/Return, X: Refresh", data, files_update, files_draw_top));
}
void files_open_sd() {
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
files_open(sdmcArchive);
}
void files_open_ctrnand() {
FS_Archive ctrNandArchive = {ARCHIVE_NAND_CTR_FS, {PATH_BINARY, 0, (void*) ""}};
files_open(ctrNandArchive);
}
void files_open_twlnand() {
FS_Archive twlNandArchive = {ARCHIVE_NAND_TWL_FS, {PATH_BINARY, 0, (void*) ""}};
files_open(twlNandArchive);
}

View File

@ -0,0 +1,292 @@
#include <arpa/inet.h>
#include <sys/unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "action/action.h"
#include "section.h"
#include "../error.h"
#include "../progressbar.h"
#include "../prompt.h"
#include "../../screen.h"
#include "../../util.h"
typedef struct {
FS_MediaType dest;
int serverSocket;
int clientSocket;
bool installStarted;
u64 currProcessed;
u64 currTotal;
u32 processed;
u32 total;
} network_install_data;
static int recvwait(int sockfd, void* buf, size_t len, int flags) {
errno = 0;
int ret = 0;
size_t read = 0;
while(((ret = recv(sockfd, buf + read, len - read, flags)) >= 0 && (read += ret) < len) || errno == EAGAIN) {
errno = 0;
}
return ret < 0 ? ret : (int) read;
}
static int sendwait(int sockfd, void* buf, size_t len, int flags) {
errno = 0;
int ret = 0;
size_t written = 0;
while(((ret = send(sockfd, buf + written, len - written, flags)) >= 0 && (written += ret) < len) || errno == EAGAIN) {
errno = 0;
}
return ret < 0 ? ret : (int) read;
}
static Result networkinstall_read(void* data, u32* bytesRead, void* buffer, u32 size) {
network_install_data* networkInstallData = (network_install_data*) data;
Result res = 0;
int ret = 0;
if((ret = recvwait(networkInstallData->clientSocket, buffer, size, 0)) >= 0) {
networkInstallData->currProcessed += ret;
if(bytesRead != NULL) {
*bytesRead = (u32) ret;
}
} else {
res = -2;
}
return res;
}
static void networkinstall_done_onresponse(ui_view* view, void* data, bool response) {
task_refresh_titles();
prompt_destroy(view);
}
static void networkinstall_close_client(network_install_data* data) {
u8 ack = 0;
sendwait(data->clientSocket, &ack, sizeof(ack), 0);
close(data->clientSocket);
data->installStarted = false;
data->processed = 0;
data->total = 0;
data->currProcessed = 0;
data->currTotal = 0;
}
static void networkinstall_install_update(ui_view* view, void* data, float* progress, char* progressText) {
network_install_data* networkInstallData = (network_install_data*) data;
bool cancelled = false;
if(hidKeysDown() & KEY_B) {
task_cancel_cia_install();
while(task_is_cia_installing()) {
svcSleepThread(1000000);
}
cancelled = true;
}
if(!task_is_cia_installing()) {
if(networkInstallData->installStarted || cancelled) {
Result res = task_get_cia_install_result();
if(R_FAILED(res)) {
networkinstall_close_client(networkInstallData);
progressbar_destroy(view);
ui_pop();
if(res == CIA_INSTALL_RESULT_CANCELLED) {
ui_push(prompt_create("Failure", "Install cancelled.", 0xFF000000, false, data, NULL, NULL, networkinstall_done_onresponse));
} else if(res == CIA_INSTALL_RESULT_ERRNO) {
error_display_errno(NULL, NULL, task_get_cia_install_errno(), "Failed to install CIA file.");
} else if(res == CIA_INSTALL_RESULT_WRONG_SYSTEM) {
ui_push(prompt_create("Failure", "Attempted to install to wrong system.", 0xFF000000, false, data, NULL, NULL, networkinstall_done_onresponse));
} else {
error_display_res(NULL, NULL, res, "Failed to install CIA file.");
}
return;
}
networkInstallData->processed++;
}
networkInstallData->installStarted = true;
if(networkInstallData->processed >= networkInstallData->total) {
networkinstall_close_client(networkInstallData);
progressbar_destroy(view);
ui_pop();
ui_push(prompt_create("Success", "Install finished.", 0xFF000000, false, data, NULL, NULL, networkinstall_done_onresponse));
return;
} else {
networkInstallData->currProcessed = 0;
networkInstallData->currTotal = 0;
u8 ack = 1;
if(sendwait(networkInstallData->clientSocket, &ack, sizeof(ack), 0) < 0) {
networkinstall_close_client(networkInstallData);
progressbar_destroy(view);
ui_pop();
error_display_errno(NULL, NULL, errno, "Failed to write CIA accept notification.");
return;
}
if(recvwait(networkInstallData->clientSocket, &networkInstallData->currTotal, sizeof(networkInstallData->currTotal), 0) < 0) {
networkinstall_close_client(networkInstallData);
progressbar_destroy(view);
ui_pop();
error_display_errno(NULL, NULL, errno, "Failed to read file size.");
return;
}
networkInstallData->currTotal = __builtin_bswap64(networkInstallData->currTotal);
task_request_cia_install(networkInstallData->dest, networkInstallData->currTotal, networkInstallData, networkinstall_read);
}
}
*progress = (float) ((double) networkInstallData->currProcessed / (double) networkInstallData->currTotal);
snprintf(progressText, PROGRESS_TEXT_MAX, "%lu / %lu\n%.2f MB / %.2f MB", networkInstallData->processed, networkInstallData->total, networkInstallData->currProcessed / 1024.0 / 1024.0, networkInstallData->currTotal / 1024.0 / 1024.0);
}
static void networkinstall_confirm_onresponse(ui_view* view, void* data, bool response) {
network_install_data* networkInstallData = (network_install_data*) data;
prompt_destroy(view);
if(response) {
ui_view* progressView = progressbar_create("Installing CIA(s)", "Press B to cancel.", data, networkinstall_install_update, NULL);
snprintf(progressbar_get_progress_text(progressView), PROGRESS_TEXT_MAX, "0 / %lu", networkInstallData->total);
ui_push(progressView);
} else {
networkinstall_close_client(networkInstallData);
}
}
static void networkinstall_wait_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
network_install_data* networkInstallData = (network_install_data*) data;
if(hidKeysDown() & KEY_B) {
close(networkInstallData->serverSocket);
free(networkInstallData);
free(view);
ui_pop();
return;
}
struct sockaddr_in client;
socklen_t clientLen = sizeof(client);
int sock = accept(networkInstallData->serverSocket, (struct sockaddr*) &client, &clientLen);
if(sock >= 0) {
int bufSize = 1024 * 32;
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize));
if(recvwait(sock, &networkInstallData->total, sizeof(networkInstallData->total), 0) < 0) {
close(sock);
error_display_errno(NULL, NULL, errno, "Failed to read file count.");
return;
}
networkInstallData->total = ntohl(networkInstallData->total);
networkInstallData->clientSocket = sock;
ui_push(prompt_create("Confirmation", "Install received CIA(s)?", 0xFF000000, true, data, NULL, NULL, networkinstall_confirm_onresponse));
} else if(errno != EAGAIN) {
error_display_errno(NULL, NULL, errno, "Failed to open socket.");
}
}
static void networkinstall_wait_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
struct in_addr addr = {(in_addr_t) gethostid()};
char text[128];
snprintf(text, 128, "Waiting for connection...\nIP: %s\nPort: 5000", inet_ntoa(addr));
float textWidth;
float textHeight;
screen_get_string_size(&textWidth, &textHeight, text, 0.5f, 0.5f);
float textX = x1 + (x2 - x1 - textWidth) / 2;
float textY = y1 + (y2 - y1 - textHeight) / 2;
screen_draw_string(text, textX, textY, 0.5f, 0.5f, 0xFF000000, false);
}
void networkinstall_open(FS_MediaType dest) {
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if(sock < 0) {
error_display_errno(NULL, NULL, errno, "Failed to open server socket.");
return;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(5000);
server.sin_addr.s_addr = (in_addr_t) gethostid();
if(bind(sock, (struct sockaddr*) &server, sizeof(server)) < 0) {
close(sock);
error_display_errno(NULL, NULL, errno, "Failed to bind server socket.");
return;
}
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
if(listen(sock, 5) < 0) {
close(sock);
error_display_errno(NULL, NULL, errno, "Failed to listen on server socket.");
return;
}
network_install_data* data = (network_install_data*) calloc(1, sizeof(network_install_data));
data->dest = dest;
data->serverSocket = sock;
data->clientSocket = 0;
data->installStarted = false;
data->currProcessed = 0;
data->currTotal = 0;
data->processed = 0;
data->total = 0;
ui_view* view = (ui_view*) calloc(1, sizeof(ui_view));
view->name = "Network Install";
view->info = "B: Return";
view->data = data;
view->update = networkinstall_wait_update;
view->drawTop = NULL;
view->drawBottom = networkinstall_wait_draw_bottom;
ui_push(view);
}
void networkinstall_open_sd() {
networkinstall_open(MEDIATYPE_SD);
}
void networkinstall_open_nand() {
networkinstall_open(MEDIATYPE_NAND);
}

View File

@ -0,0 +1,77 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "action/action.h"
#include "section.h"
#define PENDINGTITLES_ACTION_COUNT 2
static u32 pending_titles_action_count = PENDINGTITLES_ACTION_COUNT;
static list_item pending_titles_action_items[PENDINGTITLES_ACTION_COUNT] = {
{"Delete Pending Title", 0xFF000000, action_delete_pending_title},
{"Delete All Pending Titles", 0xFF000000, action_delete_all_pending_titles},
};
static void pendingtitles_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
ui_draw_pending_title_info(view, data, x1, y1, x2, y2);
}
static void pendingtitles_action_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(pending_title_info*) = (void(*)(pending_title_info*)) selected->data;
list_destroy(view);
ui_pop();
action((pending_title_info*) data);
return;
}
if(*itemCount != &pending_titles_action_count || *items != pending_titles_action_items) {
*itemCount = &pending_titles_action_count;
*items = pending_titles_action_items;
}
}
static ui_view* pendingtitles_action_create(pending_title_info* info) {
return list_create("Pending Title Action", "A: Select, B: Return", info, pendingtitles_action_update, pendingtitles_action_draw_top);
}
static void pendingtitles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
if(selected != NULL && selected->data != NULL) {
ui_draw_pending_title_info(view, selected->data, x1, y1, x2, y2);
}
}
static void pendingtitles_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(hidKeysDown() & KEY_X) {
task_refresh_pending_titles();
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
ui_push(pendingtitles_action_create((pending_title_info*) selected->data));
}
if(*itemCount != task_get_pending_title_count() || *items != task_get_pending_titles()) {
*itemCount = task_get_pending_title_count();
*items = task_get_pending_titles();
}
}
void pendingtitles_open() {
ui_push(list_create("Pending Titles", "A: Select, B: Return, X: Refresh", NULL, pendingtitles_update, pendingtitles_draw_top));
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "../ui.h"
void extsavedata_open();
void files_open(FS_Archive archive);
void files_open_sd();
void files_open_ctrnand();
void files_open_twlnand();
void networkinstall_open(FS_MediaType dest);
void networkinstall_open_sd();
void networkinstall_open_nand();
void pendingtitles_open();
void systemsavedata_open();
void tickets_open();
void titles_open();

View File

@ -0,0 +1,76 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "action/action.h"
#include "section.h"
#define SYSTEMSAVEDATA_ACTION_COUNT 1
static u32 systemsavedata_action_count = SYSTEMSAVEDATA_ACTION_COUNT;
static list_item systemsavedata_action_items[SYSTEMSAVEDATA_ACTION_COUNT] = {
{"Browse Save Data", 0xFF000000, action_browse_system_save_data},
};
static void systemsavedata_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
ui_draw_system_save_data_info(view, data, x1, y1, x2, y2);
}
static void systemsavedata_action_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(system_save_data_info*) = (void(*)(system_save_data_info*)) selected->data;
list_destroy(view);
ui_pop();
action((system_save_data_info*) data);
return;
}
if(*itemCount != &systemsavedata_action_count || *items != systemsavedata_action_items) {
*itemCount = &systemsavedata_action_count;
*items = systemsavedata_action_items;
}
}
static ui_view* systemsavedata_action_create(system_save_data_info* info) {
return list_create("System Save Data Action", "A: Select, B: Return", info, systemsavedata_action_update, systemsavedata_action_draw_top);
}
static void systemsavedata_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
if(selected != NULL && selected->data != NULL) {
ui_draw_system_save_data_info(view, selected->data, x1, y1, x2, y2);
}
}
static void systemsavedata_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(hidKeysDown() & KEY_X) {
task_refresh_system_save_data();
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
ui_push(systemsavedata_action_create((system_save_data_info*) selected->data));
}
if(*itemCount != task_get_system_save_data_count() || *items != task_get_system_save_data()) {
*itemCount = task_get_system_save_data_count();
*items = task_get_system_save_data();
}
}
void systemsavedata_open() {
ui_push(list_create("System Save Data", "A: Select, B: Return, X: Refresh", NULL, systemsavedata_update, systemsavedata_draw_top));
}

1527
source/ui/section/task.c Normal file

File diff suppressed because it is too large Load Diff

113
source/ui/section/task.h Normal file
View File

@ -0,0 +1,113 @@
#pragma once
#include <sys/syslimits.h>
#include "../list.h"
#define CIA_INSTALL_RESULT_CANCELLED -1
#define CIA_INSTALL_RESULT_ERRNO -2
#define CIA_INSTALL_RESULT_WRONG_SYSTEM -3
typedef struct {
char shortDescription[0x81];
char longDescription[0x161];
char publisher[0x81];
u32 texture;
} smdh_info;
typedef struct {
u64 titleId;
u16 version;
u64 installedSizeSD;
u64 installedSizeNAND;
bool hasSmdh;
smdh_info smdhInfo;
} cia_info;
typedef struct {
FS_Archive* archive;
char name[NAME_MAX];
char path[PATH_MAX];
bool isDirectory;
u64 size;
bool isCia;
cia_info ciaInfo;
} file_info;
typedef struct {
FS_MediaType mediaType;
u64 titleId;
char productCode[0x10];
u16 version;
u64 installedSize;
bool hasSmdh;
smdh_info smdhInfo;
} title_info;
typedef struct {
FS_MediaType mediaType;
u64 titleId;
u16 version;
} pending_title_info;
typedef struct {
u64 ticketId;
} ticket_info;
typedef struct {
FS_MediaType mediaType;
u64 extSaveDataId;
bool shared;
bool hasSmdh;
smdh_info smdhInfo;
} ext_save_data_info;
typedef struct {
u64 systemSaveDataId;
} system_save_data_info;
void task_init();
void task_exit();
Handle task_get_mutex();
void task_refresh_files();
void task_refresh_titles();
void task_refresh_pending_titles();
void task_refresh_tickets();
void task_refresh_ext_save_data();
void task_refresh_system_save_data();
void task_request_cia_install(FS_MediaType dest, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size));
Result task_get_cia_install_result();
int task_get_cia_install_errno();
bool task_is_cia_installing();
void task_cancel_cia_install();
FS_Archive* task_get_files_archive();
void task_set_files_archive(FS_Archive* archive);
char* task_get_files_path();
void task_set_files_path(char* path);
list_item* task_get_files();
u32* task_get_files_count();
list_item* task_get_titles();
u32* task_get_title_count();
list_item* task_get_pending_titles();
u32* task_get_pending_title_count();
list_item* task_get_tickets();
u32* task_get_ticket_count();
list_item* task_get_ext_save_data();
u32* task_get_ext_save_data_count();
list_item* task_get_system_save_data();
u32* task_get_system_save_data_count();
void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void ui_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void ui_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void ui_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void ui_draw_pending_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void ui_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);

View File

@ -0,0 +1,76 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "action/action.h"
#include "section.h"
#define TICKETS_ACTION_COUNT 1
static u32 tickets_action_count = TICKETS_ACTION_COUNT;
static list_item tickets_action_items[TICKETS_ACTION_COUNT] = {
{"Delete Ticket", 0xFF000000, action_delete_ticket},
};
static void tickets_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
ui_draw_ticket_info(view, data, x1, y1, x2, y2);
}
static void tickets_action_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(ticket_info*) = (void(*)(ticket_info*)) selected->data;
list_destroy(view);
ui_pop();
action((ticket_info*) data);
return;
}
if(*itemCount != &tickets_action_count || *items != tickets_action_items) {
*itemCount = &tickets_action_count;
*items = tickets_action_items;
}
}
static ui_view* tickets_action_create(ticket_info* info) {
return list_create("Ticket Action", "A: Select, B: Return", info, tickets_action_update, tickets_action_draw_top);
}
static void tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
if(selected != NULL && selected->data != NULL) {
ui_draw_ticket_info(view, selected->data, x1, y1, x2, y2);
}
}
static void tickets_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(hidKeysDown() & KEY_X) {
task_refresh_tickets();
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
ui_push(tickets_action_create((ticket_info*) selected->data));
}
if(*itemCount != task_get_ticket_count() || *items != task_get_tickets()) {
*itemCount = task_get_ticket_count();
*items = task_get_tickets();
}
}
void tickets_open() {
ui_push(list_create("Tickets", "A: Select, B: Return, X: Refresh", NULL, tickets_update, tickets_draw_top));
}

View File

@ -0,0 +1,80 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "action/action.h"
#include "section.h"
#define TITLES_ACTION_COUNT 5
static u32 titles_action_count = TITLES_ACTION_COUNT;
static list_item titles_action_items[TITLES_ACTION_COUNT] = {
{"Launch Title", 0xFF000000, action_launch_title},
{"Delete Title", 0xFF000000, action_delete_title},
{"Browse Save Data", 0xFF000000, action_browse_title_save_data},
{"Import Secure Value", 0xFF000000, action_import_secure_value},
{"Export Secure Value", 0xFF000000, action_export_secure_value},
};
static void titles_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
ui_draw_title_info(view, data, x1, y1, x2, y2);
}
static void titles_action_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(title_info*) = (void(*)(title_info*)) selected->data;
list_destroy(view);
ui_pop();
action((title_info*) data);
return;
}
if(*itemCount != &titles_action_count || *items != titles_action_items) {
*itemCount = &titles_action_count;
*items = titles_action_items;
}
}
static ui_view* titles_action_create(title_info* info) {
return list_create("Title Action", "A: Select, B: Return", info, titles_action_update, titles_action_draw_top);
}
static void titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
if(selected != NULL && selected->data != NULL) {
ui_draw_title_info(view, selected->data, x1, y1, x2, y2);
}
}
static void titles_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) {
if(hidKeysDown() & KEY_B) {
list_destroy(view);
ui_pop();
return;
}
if(hidKeysDown() & KEY_X) {
task_refresh_titles();
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
ui_push(titles_action_create((title_info*) selected->data));
}
if(*itemCount != task_get_title_count() || *items != task_get_titles()) {
*itemCount = task_get_title_count();
*items = task_get_titles();
}
}
void titles_open() {
ui_push(list_create("Titles", "A: Select, B: Return, X: Refresh", NULL, titles_update, titles_draw_top));
}

217
source/ui/ui.c Normal file
View File

@ -0,0 +1,217 @@
#include <stdio.h>
#include <time.h>
#include <3ds.h>
#include "ui.h"
#include "../screen.h"
#define MAX_UI_VIEWS 16
static ui_view* ui_stack[MAX_UI_VIEWS];
static int ui_stack_top = -1;
bool ui_push(ui_view* view) {
if(view == NULL) {
return false;
}
if(ui_stack_top >= MAX_UI_VIEWS - 1) {
return false;
}
ui_stack[++ui_stack_top] = view;
return true;
}
ui_view* ui_peek() {
if(ui_stack_top == -1) {
return NULL;
}
return ui_stack[ui_stack_top];
}
ui_view* ui_pop() {
if(ui_stack_top == -1) {
return NULL;
}
ui_view* view = ui_peek();
ui_stack[ui_stack_top--] = NULL;
return view;
}
void ui_update() {
hidScanInput();
ui_view* ui = ui_peek();
if(ui != NULL && ui->update != NULL) {
u32 bottomScreenTopBarHeight = 0;
screen_get_texture_size(NULL, &bottomScreenTopBarHeight, TEXTURE_BOTTOM_SCREEN_TOP_BAR);
u32 bottomScreenBottomBarHeight = 0;
screen_get_texture_size(NULL, &bottomScreenBottomBarHeight, TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR);
ui->update(ui, ui->data, 0, bottomScreenTopBarHeight, BOTTOM_SCREEN_WIDTH, BOTTOM_SCREEN_HEIGHT - bottomScreenBottomBarHeight);
}
}
static void ui_draw_top(ui_view* ui) {
u32 topScreenBgWidth = 0;
u32 topScreenBgHeight = 0;
screen_get_texture_size(&topScreenBgWidth, &topScreenBgHeight, TEXTURE_TOP_SCREEN_BG);
u32 topScreenTopBarWidth = 0;
u32 topScreenTopBarHeight = 0;
screen_get_texture_size(&topScreenTopBarWidth, &topScreenTopBarHeight, TEXTURE_TOP_SCREEN_TOP_BAR);
u32 topScreenTopBarShadowWidth = 0;
u32 topScreenTopBarShadowHeight = 0;
screen_get_texture_size(&topScreenTopBarShadowWidth, &topScreenTopBarShadowHeight, TEXTURE_TOP_SCREEN_TOP_BAR_SHADOW);
u32 topScreenBottomBarWidth = 0;
u32 topScreenBottomBarHeight = 0;
screen_get_texture_size(&topScreenBottomBarWidth, &topScreenBottomBarHeight, TEXTURE_TOP_SCREEN_BOTTOM_BAR);
u32 topScreenBottomBarShadowWidth = 0;
u32 topScreenBottomBarShadowHeight = 0;
screen_get_texture_size(&topScreenBottomBarShadowWidth, &topScreenBottomBarShadowHeight, TEXTURE_TOP_SCREEN_BOTTOM_BAR_SHADOW);
screen_select(GFX_TOP);
screen_draw_texture(TEXTURE_TOP_SCREEN_BG, (TOP_SCREEN_WIDTH - topScreenBgWidth) / 2, (TOP_SCREEN_HEIGHT - topScreenBgHeight) / 2, topScreenBgWidth, topScreenBgHeight);
if(ui->drawTop != NULL) {
ui->drawTop(ui, ui->data, 0, topScreenTopBarHeight, TOP_SCREEN_WIDTH, TOP_SCREEN_HEIGHT - topScreenBottomBarHeight);
}
float topScreenTopBarX = (TOP_SCREEN_WIDTH - topScreenTopBarWidth) / 2;
float topScreenTopBarY = 0;
screen_draw_texture(TEXTURE_TOP_SCREEN_TOP_BAR, topScreenTopBarX, topScreenTopBarY, topScreenTopBarWidth, topScreenTopBarHeight);
screen_draw_texture(TEXTURE_TOP_SCREEN_TOP_BAR_SHADOW, topScreenTopBarX, topScreenTopBarY + topScreenTopBarHeight, topScreenTopBarShadowWidth, topScreenTopBarShadowHeight);
float topScreenBottomBarX = (TOP_SCREEN_WIDTH - topScreenBottomBarWidth) / 2;
float topScreenBottomBarY = TOP_SCREEN_HEIGHT - topScreenBottomBarHeight;
screen_draw_texture(TEXTURE_TOP_SCREEN_BOTTOM_BAR, topScreenBottomBarX, topScreenBottomBarY, topScreenBottomBarWidth, topScreenBottomBarHeight);
screen_draw_texture(TEXTURE_TOP_SCREEN_BOTTOM_BAR_SHADOW, topScreenBottomBarX, topScreenBottomBarY - topScreenBottomBarShadowHeight, topScreenBottomBarShadowWidth, topScreenBottomBarShadowHeight);
time_t t = time(NULL);
char* timeText = ctime(&t);
float timeTextWidth;
float timeTextHeight;
screen_get_string_size(&timeTextWidth, &timeTextHeight, timeText, 0.5f, 0.5f);
screen_draw_string(timeText, topScreenTopBarX + (topScreenTopBarWidth - timeTextWidth) / 2, topScreenTopBarY + (topScreenTopBarHeight - timeTextHeight) / 2, 0.5f, 0.5f, 0xFF000000, false);
u32 batteryIcon = 0;
u8 batteryChargeState = 0;
u8 batteryLevel = 0;
if(R_SUCCEEDED(PTMU_GetBatteryChargeState(&batteryChargeState)) && batteryChargeState) {
batteryIcon = TEXTURE_BATTERY_CHARGING;
} else if(R_SUCCEEDED(PTMU_GetBatteryLevel(&batteryLevel))) {
batteryIcon = TEXTURE_BATTERY_0 + batteryLevel;
} else {
batteryIcon = TEXTURE_BATTERY_0;
}
u32 batteryWidth;
u32 batteryHeight;
screen_get_texture_size(&batteryWidth, &batteryHeight, batteryIcon);
float batteryX = topScreenTopBarX + topScreenTopBarWidth - 2 - batteryWidth;
float batteryY = topScreenTopBarY + (topScreenTopBarHeight - batteryHeight) / 2;
screen_draw_texture(batteryIcon, batteryX, batteryY, batteryWidth, batteryHeight);
u32 wifiIcon = 0;
u32 wifiStatus = 0;
if(R_SUCCEEDED(ACU_GetWifiStatus(&wifiStatus)) && wifiStatus) {
wifiIcon = TEXTURE_WIFI_0 + osGetWifiStrength();
} else {
wifiIcon = TEXTURE_WIFI_DISCONNECTED;
}
u32 wifiWidth;
u32 wifiHeight;
screen_get_texture_size(&wifiWidth, &wifiHeight, wifiIcon);
float wifiX = topScreenTopBarX + topScreenTopBarWidth - 2 - batteryWidth - 4 - wifiWidth;
float wifiY = topScreenTopBarY + (topScreenTopBarHeight - wifiHeight) / 2;
screen_draw_texture(wifiIcon, wifiX, wifiY, wifiWidth, wifiHeight);
FS_ArchiveResource sd;
FSUSER_GetSdmcArchiveResource(&sd);
FS_ArchiveResource nand;
FSUSER_GetNandArchiveResource(&nand);
char buffer[64];
snprintf(buffer, 64, "SD: %.1f MiB, NAND: %.1f MiB", ((u64) sd.freeClusters * (u64) sd.clusterSize) / 1024.0 / 1024.0, ((u64) nand.freeClusters * (u64) nand.clusterSize) / 1024.0 / 1024.0);
float freeSpaceHeight;
screen_get_string_size(NULL, &freeSpaceHeight, buffer, 0.5f, 0.5f);
screen_draw_string(buffer, topScreenBottomBarX + 2, topScreenBottomBarY + (topScreenBottomBarHeight - freeSpaceHeight) / 2, 0.5f, 0.5f, 0xFF000000, false);
}
static void ui_draw_bottom(ui_view* ui) {
u32 bottomScreenBgWidth = 0;
u32 bottomScreenBgHeight = 0;
screen_get_texture_size(&bottomScreenBgWidth, &bottomScreenBgHeight, TEXTURE_BOTTOM_SCREEN_BG);
u32 bottomScreenTopBarWidth = 0;
u32 bottomScreenTopBarHeight = 0;
screen_get_texture_size(&bottomScreenTopBarWidth, &bottomScreenTopBarHeight, TEXTURE_BOTTOM_SCREEN_TOP_BAR);
u32 bottomScreenTopBarShadowWidth = 0;
u32 bottomScreenTopBarShadowHeight = 0;
screen_get_texture_size(&bottomScreenTopBarShadowWidth, &bottomScreenTopBarShadowHeight, TEXTURE_BOTTOM_SCREEN_TOP_BAR_SHADOW);
u32 bottomScreenBottomBarWidth = 0;
u32 bottomScreenBottomBarHeight = 0;
screen_get_texture_size(&bottomScreenBottomBarWidth, &bottomScreenBottomBarHeight, TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR);
u32 bottomScreenBottomBarShadowWidth = 0;
u32 bottomScreenBottomBarShadowHeight = 0;
screen_get_texture_size(&bottomScreenBottomBarShadowWidth, &bottomScreenBottomBarShadowHeight, TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR_SHADOW);
screen_select(GFX_BOTTOM);
screen_draw_texture(TEXTURE_BOTTOM_SCREEN_BG, (BOTTOM_SCREEN_WIDTH - bottomScreenBgWidth) / 2, (BOTTOM_SCREEN_HEIGHT - bottomScreenBgHeight) / 2, bottomScreenBgWidth, bottomScreenBgHeight);
if(ui->drawBottom != NULL) {
ui->drawBottom(ui, ui->data, 0, bottomScreenTopBarHeight, BOTTOM_SCREEN_WIDTH, BOTTOM_SCREEN_HEIGHT - bottomScreenBottomBarHeight);
}
float bottomScreenTopBarX = (BOTTOM_SCREEN_WIDTH - bottomScreenTopBarWidth) / 2;
float bottomScreenTopBarY = 0;
screen_draw_texture(TEXTURE_BOTTOM_SCREEN_TOP_BAR, bottomScreenTopBarX, bottomScreenTopBarY, bottomScreenTopBarWidth, bottomScreenTopBarHeight);
screen_draw_texture(TEXTURE_BOTTOM_SCREEN_TOP_BAR_SHADOW, bottomScreenTopBarX, bottomScreenTopBarY + bottomScreenTopBarHeight, bottomScreenTopBarShadowWidth, bottomScreenTopBarShadowHeight);
float bottomScreenBottomBarX = (BOTTOM_SCREEN_WIDTH - bottomScreenBottomBarWidth) / 2;
float bottomScreenBottomBarY = BOTTOM_SCREEN_HEIGHT - bottomScreenBottomBarHeight;
screen_draw_texture(TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR, bottomScreenBottomBarX, bottomScreenBottomBarY, bottomScreenBottomBarWidth, bottomScreenBottomBarHeight);
screen_draw_texture(TEXTURE_BOTTOM_SCREEN_BOTTOM_BAR_SHADOW, bottomScreenBottomBarX, bottomScreenBottomBarY - bottomScreenBottomBarShadowHeight, bottomScreenBottomBarShadowWidth, bottomScreenBottomBarShadowHeight);
if(ui->name != NULL) {
float nameWidth;
float nameHeight;
screen_get_string_size(&nameWidth, &nameHeight, ui->name, 0.5f, 0.5f);
screen_draw_string(ui->name, (BOTTOM_SCREEN_WIDTH - nameWidth) / 2, (bottomScreenTopBarHeight - nameHeight) / 2, 0.5f, 0.5f, 0xFF000000, false);
}
if(ui->info != NULL) {
float infoWidth;
float infoHeight;
screen_get_string_size(&infoWidth, &infoHeight, ui->info, 0.5f, 0.5f);
screen_draw_string(ui->info, (BOTTOM_SCREEN_WIDTH - infoWidth) / 2, BOTTOM_SCREEN_HEIGHT - (bottomScreenBottomBarHeight + infoHeight) / 2, 0.5f, 0.5f, 0xFF000000, false);
}
}
void ui_draw() {
ui_view* ui = ui_peek();
if(ui != NULL) {
screen_begin_frame();
ui_draw_top(ui);
ui_draw_bottom(ui);
screen_end_frame();
}
}

18
source/ui/ui.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
typedef struct ui_view_s {
const char* name;
const char* info;
void* data;
void (*update)(struct ui_view_s* view, void* data, float bx1, float by1, float bx2, float by2);
void (*drawTop)(struct ui_view_s* view, void* data, float x1, float y1, float x2, float y2);
void (*drawBottom)(struct ui_view_s* view, void* data, float x1, float y1, float x2, float y2);
} ui_view;
bool ui_push(ui_view* view);
ui_view* ui_peek();
ui_view* ui_pop();
void ui_update();
void ui_draw();

405
source/util.c Normal file
View File

@ -0,0 +1,405 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <3ds.h>
#include "util.h"
#include "ui/section/task.h"
extern void cleanup();
static int util_get_line_length(PrintConsole* console, const char* str) {
int lineLength = 0;
while(*str != 0) {
if(*str == '\n') {
break;
}
lineLength++;
if(lineLength >= console->consoleWidth - 1) {
break;
}
str++;
}
return lineLength;
}
static int util_get_lines(PrintConsole* console, const char* str) {
int lines = 1;
int lineLength = 0;
while(*str != 0) {
if(*str == '\n') {
lines++;
lineLength = 0;
} else {
lineLength++;
if(lineLength >= console->consoleWidth - 1) {
lines++;
lineLength = 0;
}
}
str++;
}
return lines;
}
void util_panic(const char* s, ...) {
va_list list;
va_start(list, s);
char buf[1024];
vsnprintf(buf, 1024, s, list);
va_end(list);
gspWaitForVBlank();
u16 width;
u16 height;
for(int i = 0; i < 2; i++) {
memset(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, &width, &height), 0, (size_t) (width * height * 3));
memset(gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, &width, &height), 0, (size_t) (width * height * 3));
memset(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &width, &height), 0, (size_t) (width * height * 3));
gfxSwapBuffers();
}
PrintConsole* console = consoleInit(GFX_TOP, NULL);
const char* header = "FBI has encountered a fatal error!";
const char* footer = "Press any button to exit.";
printf("\x1b[0;0H");
for(int i = 0; i < console->consoleWidth; i++) {
printf("-");
}
printf("\x1b[%d;0H", console->consoleHeight - 1);
for(int i = 0; i < console->consoleWidth; i++) {
printf("-");
}
printf("\x1b[0;%dH%s", (console->consoleWidth - util_get_line_length(console, header)) / 2, header);
printf("\x1b[%d;%dH%s", console->consoleHeight - 1, (console->consoleWidth - util_get_line_length(console, footer)) / 2, footer);
int bufRow = (console->consoleHeight - util_get_lines(console, buf)) / 2;
char* str = buf;
while(*str != 0) {
if(*str == '\n') {
bufRow++;
str++;
continue;
} else {
int lineLength = util_get_line_length(console, str);
char old = *(str + lineLength);
*(str + lineLength) = '\0';
printf("\x1b[%d;%dH%s", bufRow, (console->consoleWidth - lineLength) / 2, str);
*(str + lineLength) = old;
bufRow++;
str += lineLength;
}
}
gfxFlushBuffers();
gspWaitForVBlank();
while(aptMainLoop()) {
hidScanInput();
if(hidKeysDown() & ~KEY_TOUCH) {
break;
}
gspWaitForVBlank();
}
cleanup();
exit(1);
}
bool util_is_dir(FS_Archive* archive, const char* path) {
Handle dirHandle = 0;
if(R_SUCCEEDED(FSUSER_OpenDirectory(&dirHandle, *archive, fsMakePath(PATH_ASCII, path)))) {
FSDIR_Close(dirHandle);
return true;
}
return false;
}
static Result util_traverse_dir_internal(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
Result res = 0;
Handle handle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&handle, *archive, fsMakePath(PATH_ASCII, path)))) {
size_t pathLen = strlen(path);
char* pathBuf = (char*) calloc(1, PATH_MAX);
strncpy(pathBuf, path, PATH_MAX);
u32 entryCount = 0;
FS_DirectoryEntry entry;
u32 done = 0;
while(R_SUCCEEDED(FSDIR_Read(handle, &entryCount, 1, &entry)) && entryCount > 0) {
ssize_t units = utf16_to_utf8((uint8_t*) pathBuf + pathLen, entry.name, PATH_MAX - pathLen - 1);
if(units > 0) {
pathBuf[pathLen + units] = '\0';
if(entry.attributes & FS_ATTRIBUTE_DIRECTORY) {
if(pathLen + units < PATH_MAX - 2) {
pathBuf[pathLen + units] = '/';
pathBuf[pathLen + units + 1] = '\0';
}
}
if(dirsFirst) {
if(process != NULL && (filter == NULL || filter(data, archive, pathBuf, entry.attributes))) {
process(data, archive, pathBuf, entry.attributes);
}
}
if((entry.attributes & FS_ATTRIBUTE_DIRECTORY) && recursive) {
if(R_FAILED(res = util_traverse_dir_internal(archive, pathBuf, recursive, dirsFirst, data, filter, process))) {
break;
}
}
if(!dirsFirst) {
if(process != NULL && (filter == NULL || filter(data, archive, pathBuf, entry.attributes))) {
process(data, archive, pathBuf, entry.attributes);
}
}
}
done++;
}
free(pathBuf);
FSDIR_Close(handle);
}
return res;
}
static Result util_traverse_dir(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
if(dirsFirst && strcmp(path, "/") != 0) {
if(process != NULL && (filter == NULL || filter(data, archive, path, FS_ATTRIBUTE_DIRECTORY))) {
process(data, archive, path, FS_ATTRIBUTE_DIRECTORY);
}
}
Result res = util_traverse_dir_internal(archive, path, recursive, dirsFirst, data, filter, process);
if(!dirsFirst && strcmp(path, "/") != 0) {
if(process != NULL && (filter == NULL || filter(data, archive, path, FS_ATTRIBUTE_DIRECTORY))) {
process(data, archive, path, FS_ATTRIBUTE_DIRECTORY);
}
}
return res;
}
static Result util_traverse_file(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
Result res = 0;
Handle handle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&handle, *archive, fsMakePath(PATH_ASCII, path), FS_OPEN_READ, 0))) {
if(process != NULL && (filter == NULL || filter(data, archive, path, 0))) {
process(data, archive, path, 0);
}
FSFILE_Close(handle);
}
return res;
}
Result util_traverse_contents(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
Result res = 0;
if(util_is_dir(archive, path)) {
res = util_traverse_dir(archive, path, recursive, dirsFirst, data, filter, process);
} else {
res = util_traverse_file(archive, path, recursive, dirsFirst, data, filter, process);
}
return res;
}
bool util_filter_dirs(void* data, FS_Archive* archive, const char* path, u32 attributes) {
return (bool) (attributes & FS_ATTRIBUTE_DIRECTORY);
}
bool util_filter_files(void* data, FS_Archive* archive, const char* path, u32 attributes) {
return !(attributes & FS_ATTRIBUTE_DIRECTORY);
}
bool util_filter_hidden(void* data, FS_Archive* archive, const char* path, u32 attributes) {
return !(attributes & FS_ATTRIBUTE_HIDDEN);
}
bool util_filter_file_extension(void* data, FS_Archive* archive, const char* path, u32 attributes) {
if(data == NULL) {
return true;
}
char* extension = (char*) data;
size_t extensionLen = strlen(extension);
size_t len = strlen(path);
return util_filter_files(data, archive, path, attributes) && len >= extensionLen && strcmp(path + len - extensionLen, extension) == 0;
}
bool util_filter_not_path(void* data, FS_Archive* archive, const char* path, u32 attributes) {
if(data == NULL) {
return true;
}
return strcmp(path, (char*) data) != 0;
}
typedef struct {
u32* count;
void* data;
bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes);
} count_data;
static bool util_count_contents_filter(void* data, FS_Archive* archive, const char* path, u32 attributes) {
count_data* countData = (count_data*) data;
if(countData->filter != NULL) {
return countData->filter(countData->data, archive, path, attributes);
}
return true;
}
static void util_count_contents_process(void* data, FS_Archive* archive, const char* path, u32 attributes) {
(*((count_data*) data)->count)++;
}
Result util_count_contents(u32* out, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
if(out == NULL) {
return 0;
}
count_data countData;
countData.count = out;
countData.data = data;
countData.filter = filter;
return util_traverse_contents(archive, path, recursive, dirsFirst, &countData, util_count_contents_filter, util_count_contents_process);
}
typedef struct {
char*** contents;
u32 index;
void* data;
bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes);
} populate_data;
static bool util_populate_contents_filter(void* data, FS_Archive* archive, const char* path, u32 attributes) {
populate_data* populateData = (populate_data*) data;
if(populateData->filter != NULL) {
return populateData->filter(populateData->data, archive, path, attributes);
}
return true;
}
static void util_populate_contents_process(void* data, FS_Archive* archive, const char* path, u32 attributes) {
u32 currPathSize = strlen(path) + 1;
char* currPath = (char*) calloc(1, currPathSize);
strncpy(currPath, path, currPathSize);
populate_data* populateData = (populate_data*) data;
(*populateData->contents)[populateData->index++] = currPath;
}
Result util_populate_contents(char*** contentsOut, u32* countOut, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
if(contentsOut == NULL || countOut == NULL) {
return 0;
}
util_count_contents(countOut, archive, path, recursive, dirsFirst, data, filter);
*contentsOut = (char**) calloc(*countOut, sizeof(char*));
populate_data populateData;
populateData.contents = contentsOut;
populateData.index = 0;
populateData.data = data;
populateData.filter = filter;
Result res = util_traverse_contents(archive, path, recursive, dirsFirst, &populateData, util_populate_contents_filter, util_populate_contents_process);
if(R_FAILED(res)) {
util_free_contents(*contentsOut, *countOut);
}
return res;
}
void util_free_contents(char** contents, u32 count) {
for(u32 i = 0; i < count; i++) {
if(contents[i] != NULL) {
free(contents[i]);
}
}
free(contents);
}
void util_get_path_file(char* out, const char* path, u32 size) {
const char* start = NULL;
const char* end = NULL;
const char* curr = path - 1;
while((curr = strchr(curr + 1, '/')) != NULL) {
start = end != NULL ? end : path;
end = curr;
}
if(end - start == 0) {
strncpy(out, "/", size);
} else {
u32 terminatorPos = end - start - 1 < size - 1 ? end - start - 1 : size - 1;
strncpy(out, start + 1, terminatorPos);
out[terminatorPos] = '\0';
}
}
void util_get_parent_path(char* out, const char* path, u32 size) {
size_t pathLen = strlen(path);
const char* start = NULL;
const char* end = NULL;
const char* curr = path - 1;
while((curr = strchr(curr + 1, '/')) != NULL && (start == NULL || curr != path + pathLen - 1)) {
start = end != NULL ? end : path;
end = curr;
}
u32 terminatorPos = end - path + 1 < size - 1 ? end - path + 1 : size - 1;
strncpy(out, path, terminatorPos);
out[terminatorPos] = '\0';
}
Result util_ensure_dir(FS_Archive* archive, const char* path) {
Result res = 0;
if(!util_is_dir(archive, path)) {
FS_Path fsPath = fsMakePath(PATH_ASCII, path);
FSUSER_DeleteFile(*archive, fsPath);
res = FSUSER_CreateDirectory(*archive, fsPath, 0);
}
return res;
}

18
source/util.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
void util_panic(const char* s, ...);
bool util_is_dir(FS_Archive* archive, const char* path);
Result util_traverse_contents(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes));
bool util_filter_dirs(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_files(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_hidden(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_file_extension(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_not_path(void* data, FS_Archive* archive, const char* path, u32 attributes);
Result util_count_contents(u32* out, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes));
Result util_populate_contents(char*** contentsOut, u32* countOut, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes));
void util_free_contents(char** contents, u32 count);
void util_get_path_file(char* out, const char* path, u32 size);
void util_get_parent_path(char* out, const char* path, u32 size);
Result util_ensure_dir(FS_Archive* archive, const char* path);