Initial commit of FBI rewrite.
3
.gitmodules
vendored
@ -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
|
||||||
|
27
Makefile
@ -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 #
|
||||||
|
@ -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
BIN
meta/banner.png
Before Width: | Height: | Size: 2.9 KiB |
BIN
romfs/battery0.png
Normal file
After Width: | Height: | Size: 355 B |
BIN
romfs/battery1.png
Normal file
After Width: | Height: | Size: 350 B |
BIN
romfs/battery2.png
Normal file
After Width: | Height: | Size: 352 B |
BIN
romfs/battery3.png
Normal file
After Width: | Height: | Size: 352 B |
BIN
romfs/battery4.png
Normal file
After Width: | Height: | Size: 346 B |
BIN
romfs/battery5.png
Normal file
After Width: | Height: | Size: 363 B |
BIN
romfs/battery_charging.png
Normal file
After Width: | Height: | Size: 422 B |
BIN
romfs/bottom_screen_bg.png
Normal file
After Width: | Height: | Size: 781 B |
BIN
romfs/bottom_screen_bottom_bar.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
romfs/bottom_screen_bottom_bar_shadow.png
Normal file
After Width: | Height: | Size: 414 B |
BIN
romfs/bottom_screen_top_bar.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
romfs/bottom_screen_top_bar_shadow.png
Normal file
After Width: | Height: | Size: 418 B |
BIN
romfs/button_large.png
Normal file
After Width: | Height: | Size: 327 B |
BIN
romfs/button_small.png
Normal file
After Width: | Height: | Size: 285 B |
BIN
romfs/logo.png
Normal file
After Width: | Height: | Size: 493 B |
BIN
romfs/progress_bar_bg.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
romfs/progress_bar_content.png
Normal file
After Width: | Height: | Size: 250 B |
BIN
romfs/selection_overlay.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
romfs/smdh_info_box.png
Normal file
After Width: | Height: | Size: 360 B |
BIN
romfs/smdh_info_box_shadow.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
romfs/top_screen_bg.png
Normal file
After Width: | Height: | Size: 854 B |
BIN
romfs/top_screen_bottom_bar.png
Normal file
After Width: | Height: | Size: 239 B |
BIN
romfs/top_screen_bottom_bar_shadow.png
Normal file
After Width: | Height: | Size: 420 B |
BIN
romfs/top_screen_top_bar.png
Normal file
After Width: | Height: | Size: 239 B |
BIN
romfs/top_screen_top_bar_shadow.png
Normal file
After Width: | Height: | Size: 425 B |
BIN
romfs/wifi0.png
Normal file
After Width: | Height: | Size: 235 B |
BIN
romfs/wifi1.png
Normal file
After Width: | Height: | Size: 239 B |
BIN
romfs/wifi2.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
romfs/wifi3.png
Normal file
After Width: | Height: | Size: 235 B |
BIN
romfs/wifi_disconnected.png
Normal file
After Width: | Height: | Size: 581 B |
34
source/default.v.pica
Normal 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
1713
source/lodepng.h
Normal file
79
source/main.c
Normal 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;
|
||||||
|
}
|
551
source/main.cpp
@ -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;
|
|
||||||
}
|
|
500
source/rop.h
@ -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
@ -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
@ -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);
|
724
source/ui.cpp
@ -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};
|
|
||||||
}
|
|
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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);
|
26
source/ui/section/action/action.h
Normal 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);
|
10
source/ui/section/action/browseextsavedata.c
Normal 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);
|
||||||
|
}
|
10
source/ui/section/action/browsesystemsavedata.c
Normal 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);
|
||||||
|
}
|
10
source/ui/section/action/browsetitlesavedata.c
Normal 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);
|
||||||
|
}
|
57
source/ui/section/action/clipboard.c
Normal 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';
|
||||||
|
}
|
11
source/ui/section/action/clipboard.h
Normal 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();
|
22
source/ui/section/action/copyfiles.c
Normal 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));
|
||||||
|
}
|
47
source/ui/section/action/deleteallpendingtitles.c
Normal 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));
|
||||||
|
}
|
153
source/ui/section/action/deletecontents.c
Normal 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));
|
||||||
|
}
|
43
source/ui/section/action/deletependingtitle.c
Normal 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));
|
||||||
|
}
|
43
source/ui/section/action/deleteticket.c
Normal 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));
|
||||||
|
}
|
43
source/ui/section/action/deletetitle.c
Normal 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));
|
||||||
|
}
|
75
source/ui/section/action/exportsecurevalue.c
Normal 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));
|
||||||
|
}
|
59
source/ui/section/action/importsecurevalue.c
Normal 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));
|
||||||
|
}
|
206
source/ui/section/action/installcias.c
Normal 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);
|
||||||
|
}
|
47
source/ui/section/action/launchtitle.c
Normal 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));
|
||||||
|
}
|
169
source/ui/section/action/pastefiles.c
Normal 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));
|
||||||
|
}
|
76
source/ui/section/extsavedata.c
Normal 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
@ -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);
|
||||||
|
}
|
292
source/ui/section/networkinstall.c
Normal 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);
|
||||||
|
}
|
77
source/ui/section/pendingtitles.c
Normal 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));
|
||||||
|
}
|
16
source/ui/section/section.h
Normal 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();
|
76
source/ui/section/systemsavedata.c
Normal 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
113
source/ui/section/task.h
Normal 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);
|
76
source/ui/section/tickets.c
Normal 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));
|
||||||
|
}
|
80
source/ui/section/titles.c
Normal 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
@ -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
@ -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
@ -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
@ -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);
|