Preview Branch

This commit is contained in:
Rintim 2021-01-24 09:43:05 +08:00
parent 30a1d408b5
commit 5647cd536f
154 changed files with 0 additions and 24560 deletions

View File

@ -1,41 +0,0 @@
TARGET := 3DS
NAME := FBI
BUILD_DIR := build
OUTPUT_DIR := output
INCLUDE_DIRS := include
SOURCE_DIRS := source
ROMFS_DIR := romfs
LIBRARY_DIRS += $(DEVKITPRO)/libctru $(DEVKITPRO)/portlibs/armv6k $(DEVKITPRO)/portlibs/3ds
LIBRARIES += curl mbedtls mbedx509 mbedcrypto jansson z citro3d ctru
EXTRA_OUTPUT_FILES := servefiles
BUILD_FLAGS := -Wno-format-truncation
VERSION_PARTS := 2.6.1 # $(subst ., ,$(shell git describe --tags --abbrev=0))
VERSION_MAJOR := 2#$(word 1, $(VERSION_PARTS))
VERSION_MINOR := 6#$(word 2, $(VERSION_PARTS))
VERSION_MICRO := 1#$(word 3, $(VERSION_PARTS))
CHINESE_VERSION := CV1
DESCRIPTION := 3DS开源应用管理器.
AUTHOR := Steveice10 + Theopse(汉化)
PRODUCT_CODE := CTR-P-CFBI
UNIQUE_ID := 0xF8001
ICON_FLAGS := --flags visible,ratingrequired,recordusage --cero 153 --esrb 153 --usk 153 --pegigen 153 --pegiptr 153 --pegibbfc 153 --cob 153 --grb 153 --cgsrr 153
BANNER_AUDIO := meta/audio_3ds.wav
BANNER_IMAGE := meta/banner_3ds.cgfx
ICON := meta/icon_3ds.png
LOGO := meta/logo_3ds.bcma.lz
# INTERNAL #
include buildtools/make_base

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

View File

@ -1,11 +0,0 @@
text=FF000000
nand=FF0000FF
sd=FF00FF00
gamecard=FFFF0000
dstitle=FF82004B
file=FF000000
directory=FF0000FF
enabled=FF00FF00
disabled=FF0000FF
ticketinuse=FF00FF00
ticketnotinuse=FF0000FF

Binary file not shown.

Before

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

View File

@ -1,7 +0,0 @@
# servefiles
Simple Python script for serving local files to FBI's remote installer. Requires [Python](https://www.python.org/downloads/).
**Usage**: python servefiles.py (3ds ip) (file / directory) \[host ip\] \[host port\]
- Supported file extensions: .cia, .tik, .cetk, .3dsx

View File

@ -1,11 +0,0 @@
@ECHO OFF
set /p DSIP="Enter the IP of your 3DS: "
for %%a in (%*) do (
if "%%~xa"==".cia" (
python "%~dp0servefiles.py" %DSIP% "%%~a"
)
if "%%~xa"=="" (
python "%~dp0servefiles.py" %DSIP% "%%~a"
)
)
pause

View File

@ -1,4 +0,0 @@
#!/bin/bash
read -p "Type the IP address of your 3DS: " -e input
python servefiles.py $input .

View File

@ -1,44 +0,0 @@
#!/usr/bin/env python
# coding: utf-8 -*-
import socket
import struct
import sys
import time
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
if len(sys.argv) < 3:
print('Usage: ' + sys.argv[0] + ' <target ip> <url>...')
sys.exit(1)
target_ip = sys.argv[1]
file_list_payload = ''
for url in sys.argv[2:]:
parsed = urlparse(url);
if not parsed.scheme in ('http', 'https') or parsed.netloc == '':
print(url + ': Invalid URL')
sys.exit(1)
file_list_payload += url + '\n'
file_list_payloadBytes = file_list_payload.encode('ascii')
print('URLs:')
print(file_list_payload)
try:
print('Sending URL(s) to '+ target_ip + ' on port 5000...')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target_ip, 5000))
sock.sendall(struct.pack('!L', len(file_list_payloadBytes)) + file_list_payloadBytes)
while len(sock.recv(1)) < 1:
time.sleep(0.05)
sock.close()
except Exception as e:
print('An error occurred: ' + str(e))
sys.exit(1)

View File

@ -1,141 +0,0 @@
#!/usr/bin/env python
# coding: utf-8 -*-
import os
import socket
import struct
import sys
import threading
import time
import urllib
try:
from SimpleHTTPServer import SimpleHTTPRequestHandler
from SocketServer import TCPServer
from urllib import quote
input = raw_input
except ImportError:
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from urllib.parse import quote
interactive = False
if len(sys.argv) <= 2:
# If there aren't enough variables, use interactive mode
if len(sys.argv) == 2:
if sys.argv[1].lower() in ('--help', '-help', 'help', 'h', '-h', '--h'):
print('Usage: ' + sys.argv[0] + ' <target ip> <file / directory> [host ip] [host port]')
sys.exit(1)
interactive = True
elif len(sys.argv) < 3 or len(sys.argv) > 6:
print('Usage: ' + sys.argv[0] + ' <target ip> <file / directory> [host ip] [host port]')
sys.exit(1)
accepted_extension = ('.cia', '.tik', '.cetk', '.3dsx')
hostPort = 8080 # Default value
if interactive:
target_ip = input("The IP of your 3DS: ")
target_path = input("The file you want to send (.cia, .tik, .cetk, or .3dsx): ")
hostIp = input("Host IP (or press Enter to have the script detect host IP):")
if hostIp == '':
print('Detecting host IP...')
hostIp = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
else:
hostPort = input("Host port (or press Enter to keep default, 8080):")
if hostPort == '':
hostPort = 8080 # Default
else:
# (if the script is being run using a full python path; ex: "path/to/python script_name.py foo foo..")
if sys.argv[1] == os.path.basename(__file__):
target_ip = sys.argv[2]
target_path = sys.argv[3]
if len(sys.argv) >= 5:
hostIp = sys.argv[4]
if len(sys.argv) == 6:
hostPort = int(sys.argv[5])
else:
print('Detecting host IP...')
hostIp = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
# (if the script is being run using just the script name and default executable for python scripts; ex: "script_name.py foo foo..")
else:
target_ip = sys.argv[1]
target_path = sys.argv[2]
if len(sys.argv) >= 4:
hostIp = sys.argv[3]
if len(sys.argv) == 5:
hostPort = int(sys.argv[4])
else:
print('Detecting host IP...')
hostIp = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
target_path = target_path.strip()
if not os.path.exists(target_path):
print(target_path + ': No such file or directory.')
sys.exit(1)
print('Preparing data...')
baseUrl = hostIp + ':' + str(hostPort) + '/'
if os.path.isfile(target_path):
if target_path.endswith(accepted_extension):
file_list_payload = baseUrl + quote(os.path.basename(target_path))
directory = os.path.dirname(target_path) # get file directory
else:
print('Unsupported file extension. Supported extensions are: ' + accepted_extension)
sys.exit(1)
else:
directory = target_path # it's a directory
file_list_payload = '' # init the payload before adding lines
for file in [file for file in next(os.walk(target_path))[2] if file.endswith(accepted_extension)]:
file_list_payload += baseUrl + quote(file) + '\n'
if len(file_list_payload) == 0:
print('No files to serve.')
sys.exit(1)
file_list_payloadBytes = file_list_payload.encode('ascii')
if directory and directory != '.': # doesn't need to move if it's already the current working directory
os.chdir(directory) # set working directory to the right folder to be able to serve files
print('\nURLs:')
print(file_list_payload + '\n')
class MyServer(TCPServer):
def server_bind(self):
import socket
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
print('Opening HTTP server on port ' + str(hostPort))
server = MyServer(('', hostPort), SimpleHTTPRequestHandler)
thread = threading.Thread(target=server.serve_forever)
thread.start()
try:
print('Sending URL(s) to ' + target_ip + ' on port 5000...')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target_ip, 5000))
sock.sendall(struct.pack('!L', len(file_list_payloadBytes)) + file_list_payloadBytes)
while len(sock.recv(1)) < 1:
time.sleep(0.05)
sock.close()
except Exception as e:
print('An error occurred: ' + str(e))
server.shutdown()
sys.exit(1)
print('Shutting down HTTP server...')
server.shutdown()

View File

@ -1,15 +0,0 @@
#!/usr/bin/env python
# coding: utf-8 -*-
from setuptools import setup
setup(
name="servefiles",
version="2.4.12",
scripts=["servefiles.py", "sendurls.py"],
author="Steveice10",
author_email="Steveice10@gmail.com",
description="Simple Python script for serving local files to FBI's remote installer.",
license="MIT",
url="https://github.com/Steveice10/FBI/tree/master/servefiles"
)

View File

@ -1,57 +0,0 @@
#include <malloc.h>
#include <string.h>
#include <3ds.h>
#include "clipboard.h"
#include "fs.h"
#include "stringutil.h"
static bool clipboard_has = false;
static bool clipboard_contents_only;
static FS_Archive clipboard_archive;
static char clipboard_path[FILE_PATH_MAX];
bool clipboard_has_contents() {
return clipboard_has;
}
FS_Archive clipboard_get_archive() {
return clipboard_archive;
}
char* clipboard_get_path() {
return clipboard_path;
}
bool clipboard_is_contents_only() {
return clipboard_contents_only;
}
Result clipboard_set_contents(FS_Archive archive, const char* path, bool contentsOnly) {
clipboard_clear();
Result res = 0;
if(R_SUCCEEDED(res = fs_ref_archive(archive))) {
clipboard_has = true;
clipboard_contents_only = contentsOnly;
clipboard_archive = archive;
string_copy(clipboard_path, path, FILE_PATH_MAX);
}
return res;
}
void clipboard_clear() {
if(clipboard_archive != 0) {
fs_close_archive(clipboard_archive);
}
clipboard_has = false;
clipboard_contents_only = false;
clipboard_archive = 0;
memset(clipboard_path, '\0', FILE_PATH_MAX);
}

View File

@ -1,8 +0,0 @@
#pragma once
bool clipboard_has_contents();
FS_Archive clipboard_get_archive();
char* clipboard_get_path();
bool clipboard_is_contents_only();
Result clipboard_set_contents(FS_Archive archive, const char* path, bool contentsOnly);
void clipboard_clear();

View File

@ -1,14 +0,0 @@
#pragma once
#include "data/data.h"
#include "task/task.h"
#include "ui/ui.h"
#include "clipboard.h"
#include "error.h"
#include "fs.h"
#include "http.h"
#include "linkedlist.h"
#include "screen.h"
#include "spi.h"
#include "stringutil.h"

View File

@ -1,34 +0,0 @@
#include <3ds.h>
#include "bnr.h"
#include "../stringutil.h"
static CFG_Language region_default_language[] = {
CFG_LANGUAGE_JP,
CFG_LANGUAGE_EN,
CFG_LANGUAGE_EN,
CFG_LANGUAGE_EN,
CFG_LANGUAGE_ZH,
CFG_LANGUAGE_KO,
CFG_LANGUAGE_ZH
};
u16* bnr_select_title(BNR* bnr) {
char title[0x100] = {'\0'};
CFG_Language systemLanguage;
if(R_SUCCEEDED(CFGU_GetSystemLanguage((u8*) &systemLanguage))) {
utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], sizeof(title) - 1);
}
if(string_is_empty(title)) {
CFG_Region systemRegion;
if(R_SUCCEEDED(CFGU_SecureInfoGetRegion((u8*) &systemRegion))) {
systemLanguage = region_default_language[systemRegion];
} else {
systemLanguage = CFG_LANGUAGE_JP;
}
}
return bnr->titles[systemLanguage];
}

View File

@ -1,16 +0,0 @@
#pragma once
typedef struct BNR_s {
u8 version;
bool animated;
u16 crc16[4];
u8 reserved[0x16];
u8 mainIconBitmap[0x200];
u16 mainIconPalette[0x10];
u16 titles[16][0x80];
u8 animatedFrameBitmaps[8][0x200];
u16 animatedFramePalettes[8][0x10];
u16 animationSequence[0x40];
} BNR;
u16* bnr_select_title(BNR* bnr);

View File

@ -1,55 +0,0 @@
#include <3ds.h>
#include "cia.h"
#include "smdh.h"
#include "tmd.h"
#include "../error.h"
Result cia_get_title_id(u64* titleId, u8* cia, size_t size) {
if(cia == NULL) {
return R_APP_INVALID_ARGUMENT;
}
if(size < 0x10) {
return R_APP_BAD_DATA;
}
u32 headerSize = ((*(u32*) &cia[0x00]) + 0x3F) & ~0x3F;
u32 certSize = ((*(u32*) &cia[0x08]) + 0x3F) & ~0x3F;
u32 ticketSize = ((*(u32*) &cia[0x0C]) + 0x3F) & ~0x3F;
u32 offset = headerSize + certSize + ticketSize;
if(offset >= size) {
return R_APP_BAD_DATA;
}
return tmd_get_title_id(titleId, &cia[offset], size - offset);
}
Result cia_file_get_smdh(SMDH* smdh, Handle handle) {
Result res = 0;
if(smdh != NULL) {
u32 bytesRead = 0;
u32 header[8];
if(R_SUCCEEDED(res = FSFILE_Read(handle, &bytesRead, 0, header, sizeof(header))) && bytesRead == sizeof(header)) {
u32 headerSize = (header[0] + 0x3F) & ~0x3F;
u32 certSize = (header[2] + 0x3F) & ~0x3F;
u32 ticketSize = (header[3] + 0x3F) & ~0x3F;
u32 tmdSize = (header[4] + 0x3F) & ~0x3F;
u32 metaSize = (header[5] + 0x3F) & ~0x3F;
u64 contentSize = ((header[6] | ((u64) header[7] << 32)) + 0x3F) & ~0x3F;
if(metaSize >= 0x3AC0) {
res = FSFILE_Read(handle, &bytesRead, headerSize + certSize + ticketSize + tmdSize + contentSize + 0x400, smdh, sizeof(SMDH));
} else {
res = R_APP_BAD_DATA;
}
}
} else {
res = R_APP_INVALID_ARGUMENT;
}
return res;
}

View File

@ -1,6 +0,0 @@
#pragma once
typedef struct SMDH_s SMDH;
Result cia_get_title_id(u64* titleId, u8* cia, size_t size);
Result cia_file_get_smdh(SMDH* smdh, Handle handle);

View File

@ -1,7 +0,0 @@
#pragma once
#include "bnr.h"
#include "cia.h"
#include "smdh.h"
#include "ticket.h"
#include "tmd.h"

View File

@ -1,73 +0,0 @@
#include <stdio.h>
#include <3ds.h>
#include "smdh.h"
#include "../stringutil.h"
#define SMDH_NUM_REGIONS 7
#define SMDH_ALL_REGIONS 0x7F
static const char* smdh_region_strings[SMDH_NUM_REGIONS] = {
"日本",
"美国",
"欧洲",
"澳大利亚",
"中国",
"韩国",
"台湾"
};
void smdh_region_to_string(char* out, u32 region, size_t size) {
if(out == NULL) {
return;
}
if(region == 0) {
snprintf(out, size, "未知");
} else if((region & SMDH_ALL_REGIONS) == SMDH_ALL_REGIONS) {
snprintf(out, size, "无/任意");
} else {
size_t pos = 0;
for(u32 i = 0; i < SMDH_NUM_REGIONS; i++) {
if(region & (1 << i)) {
if(pos > 0) {
pos += snprintf(out + pos, size - pos, ", ");
}
pos += snprintf(out + pos, size - pos, smdh_region_strings[i]);
}
}
}
}
static CFG_Language region_default_language[] = {
CFG_LANGUAGE_JP,
CFG_LANGUAGE_EN,
CFG_LANGUAGE_EN,
CFG_LANGUAGE_EN,
CFG_LANGUAGE_ZH,
CFG_LANGUAGE_KO,
CFG_LANGUAGE_ZH
};
SMDH_title* smdh_select_title(SMDH* smdh) {
char shortDescription[0x100] = {'\0'};
CFG_Language systemLanguage;
if(R_SUCCEEDED(CFGU_GetSystemLanguage((u8*) &systemLanguage))) {
utf16_to_utf8((uint8_t*) shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(shortDescription) - 1);
}
if(string_is_empty(shortDescription)) {
CFG_Region systemRegion;
if(R_SUCCEEDED(CFGU_SecureInfoGetRegion((u8*) &systemRegion))) {
systemLanguage = region_default_language[systemRegion];
} else {
systemLanguage = CFG_LANGUAGE_JP;
}
}
return &smdh->titles[systemLanguage];
}

View File

@ -1,29 +0,0 @@
#pragma once
typedef struct SMDH_title_s {
u16 shortDescription[0x40];
u16 longDescription[0x80];
u16 publisher[0x40];
} SMDH_title;
typedef struct SMDH_s {
char magic[0x04];
u16 version;
u16 reserved1;
SMDH_title titles[0x10];
u8 ratings[0x10];
u32 region;
u32 matchMakerId;
u64 matchMakerBitId;
u32 flags;
u16 eulaVersion;
u16 reserved;
u32 optimalBannerFrame;
u32 streetpassId;
u64 reserved2;
u8 smallIcon[0x480];
u8 largeIcon[0x1200];
} SMDH;
void smdh_region_to_string(char* out, u32 region, size_t size);
SMDH_title* smdh_select_title(SMDH* smdh);

View File

@ -1,33 +0,0 @@
#include <3ds.h>
#include "ticket.h"
#include "../core.h"
#define NUM_SIG_TYPES 6
static u32 sigSizes[NUM_SIG_TYPES] = {0x240, 0x140, 0x80, 0x240, 0x140, 0x80};
Result ticket_get_title_id(u64* titleId, u8* ticket, size_t size) {
if(ticket == NULL) {
return R_APP_INVALID_ARGUMENT;
}
if(size < 4) {
return R_APP_BAD_DATA;
}
u8 sigType = ticket[0x03];
if(sigType >= NUM_SIG_TYPES) {
return R_APP_BAD_DATA;
}
u32 offset = sigSizes[sigType] + 0x9C;
if(offset + sizeof(u64) > size) {
return R_APP_BAD_DATA;
}
if(titleId != NULL) {
*titleId = __builtin_bswap64(*(u64*) &ticket[offset]);
}
return 0;
}

View File

@ -1,3 +0,0 @@
#pragma once
Result ticket_get_title_id(u64* titleId, u8* ticket, size_t size);

View File

@ -1,89 +0,0 @@
#include <3ds.h>
#include "tmd.h"
#include "../core.h"
#define NUM_SIG_TYPES 6
static u32 sigSizes[NUM_SIG_TYPES] = {0x240, 0x140, 0x80, 0x240, 0x140, 0x80};
static Result tmd_get(u8** out, u8* tmd, size_t tmdSize, u32 pos, size_t fieldSize) {
if(tmd == NULL) {
return R_APP_INVALID_ARGUMENT;
}
if(tmdSize < 4) {
return R_APP_BAD_DATA;
}
u8 sigType = tmd[0x03];
if(sigType >= NUM_SIG_TYPES) {
return R_APP_BAD_DATA;
}
u32 offset = sigSizes[sigType] + pos;
if(offset + fieldSize > tmdSize) {
return R_APP_BAD_DATA;
}
if(out != NULL) {
*out = &tmd[offset];
}
return 0;
}
Result tmd_get_title_id(u64* titleId, u8* tmd, size_t size) {
u8* data = NULL;
Result res = tmd_get(&data, tmd, size, 0x4C, sizeof(u64));
if(R_FAILED(res)) {
return res;
}
if(titleId != NULL) {
*titleId = __builtin_bswap64(*(u64*) data);
}
return 0;
}
Result tmd_get_content_count(u16* contentCount, u8* tmd, size_t size) {
u8* data = NULL;
Result res = tmd_get(&data, tmd, size, 0x9E, sizeof(u16));
if(R_FAILED(res)) {
return res;
}
if(contentCount != NULL) {
*contentCount = __builtin_bswap16(*(u16*) data);
}
return 0;
}
Result tmd_get_content_id(u32* id, u8* tmd, size_t size, u32 num) {
u8* data = NULL;
Result res = tmd_get(&data, tmd, size, 0x9C4 + (num * 0x30), sizeof(u32));
if(R_FAILED(res)) {
return res;
}
if(id != NULL) {
*id = __builtin_bswap32(*(u32*) data);
}
return 0;
}
Result tmd_get_content_index(u16* index, u8* tmd, size_t size, u32 num) {
u8* data = NULL;
Result res = tmd_get(&data, tmd, size, 0x9C4 + (num * 0x30) + 0x4, sizeof(u16));
if(R_FAILED(res)) {
return res;
}
if(index != NULL) {
*index = __builtin_bswap16(*(u16*) data);
}
return 0;
}

View File

@ -1,6 +0,0 @@
#pragma once
Result tmd_get_title_id(u64* titleId, u8* tmd, size_t size);
Result tmd_get_content_count(u16* contentCount, u8* tmd, size_t size);
Result tmd_get_content_id(u32* id, u8* tmd, size_t size, u32 num);
Result tmd_get_content_index(u16* index, u8* tmd, size_t size, u32 num);

View File

@ -1,34 +0,0 @@
; 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

View File

@ -1,124 +0,0 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <3ds.h>
#include "error.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 error_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 遇到致命错误!";
const char* footer = "按任意键退出.";
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);
}

View File

@ -1,24 +0,0 @@
#pragma once
#define R_APP_INVALID_ARGUMENT MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_APPLICATION, 1)
#define R_APP_CANCELLED MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, 2)
#define R_APP_SKIPPED MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, 3)
#define R_APP_THREAD_CREATE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 4)
#define R_APP_PARSE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 5)
#define R_APP_BAD_DATA MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 6)
#define R_APP_HTTP_TOO_MANY_REDIRECTS MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 7)
#define R_APP_HTTP_ERROR_BASE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 8)
#define R_APP_HTTP_ERROR_END (R_APP_HTTP_ERROR_BASE + 600)
#define R_APP_CURL_INIT_FAILED R_APP_HTTP_ERROR_END
#define R_APP_CURL_ERROR_BASE (R_APP_CURL_INIT_FAILED + 1)
#define R_APP_CURL_ERROR_END (R_APP_CURL_ERROR_BASE + 100)
#define R_APP_NOT_IMPLEMENTED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, RD_NOT_IMPLEMENTED)
#define R_APP_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY)
#define R_APP_OUT_OF_RANGE MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_APPLICATION, RD_OUT_OF_RANGE)
void error_panic(const char* s, ...);

View File

@ -1,229 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <3ds.h>
#include "error.h"
#include "fs.h"
#include "linkedlist.h"
#include "stringutil.h"
bool fs_is_dir(FS_Archive archive, const char* path) {
Result res = 0;
FS_Path* fsPath = fs_make_path_utf8(path);
if(fsPath != NULL) {
Handle dirHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, archive, *fsPath))) {
FSDIR_Close(dirHandle);
}
fs_free_path_utf8(fsPath);
} else {
res = R_APP_OUT_OF_MEMORY;
}
return R_SUCCEEDED(res);
}
Result fs_ensure_dir(FS_Archive archive, const char* path) {
Result res = 0;
FS_Path* fsPath = fs_make_path_utf8(path);
if(fsPath != NULL) {
Handle dirHandle = 0;
if(R_SUCCEEDED(FSUSER_OpenDirectory(&dirHandle, archive, *fsPath))) {
FSDIR_Close(dirHandle);
} else {
FSUSER_DeleteFile(archive, *fsPath);
res = FSUSER_CreateDirectory(archive, *fsPath, 0);
}
fs_free_path_utf8(fsPath);
} else {
res = R_APP_OUT_OF_MEMORY;
}
return res;
}
FS_Path fs_make_path_binary(const void* data, u32 size) {
FS_Path path = {PATH_BINARY, size, data};
return path;
}
FS_Path* fs_make_path_utf8(const char* path) {
size_t len = strlen(path);
u16* utf16 = (u16*) calloc(len + 1, sizeof(u16));
if(utf16 == NULL) {
return NULL;
}
ssize_t utf16Len = utf8_to_utf16(utf16, (const uint8_t*) path, len);
FS_Path* fsPath = (FS_Path*) calloc(1, sizeof(FS_Path));
if(fsPath == NULL) {
free(utf16);
return NULL;
}
fsPath->type = PATH_UTF16;
fsPath->size = (utf16Len + 1) * sizeof(u16);
fsPath->data = utf16;
return fsPath;
}
void fs_free_path_utf8(FS_Path* path) {
free((void*) path->data);
free(path);
}
typedef struct {
FS_Archive archive;
u32 refs;
} archive_ref;
static linked_list opened_archives;
Result fs_open_archive(FS_Archive* archive, FS_ArchiveID id, FS_Path path) {
if(archive == NULL) {
return R_APP_INVALID_ARGUMENT;
}
Result res = 0;
FS_Archive arch = 0;
if(R_SUCCEEDED(res = FSUSER_OpenArchive(&arch, id, path))) {
if(R_SUCCEEDED(res = fs_ref_archive(arch))) {
*archive = arch;
} else {
FSUSER_CloseArchive(arch);
}
}
return res;
}
Result fs_ref_archive(FS_Archive archive) {
linked_list_iter iter;
linked_list_iterate(&opened_archives, &iter);
while(linked_list_iter_has_next(&iter)) {
archive_ref* ref = (archive_ref*) linked_list_iter_next(&iter);
if(ref->archive == archive) {
ref->refs++;
return 0;
}
}
Result res = 0;
archive_ref* ref = (archive_ref*) calloc(1, sizeof(archive_ref));
if(ref != NULL) {
ref->archive = archive;
ref->refs = 1;
linked_list_add(&opened_archives, ref);
} else {
res = R_APP_OUT_OF_MEMORY;
}
return res;
}
Result fs_close_archive(FS_Archive archive) {
linked_list_iter iter;
linked_list_iterate(&opened_archives, &iter);
while(linked_list_iter_has_next(&iter)) {
archive_ref* ref = (archive_ref*) linked_list_iter_next(&iter);
if(ref->archive == archive) {
ref->refs--;
if(ref->refs == 0) {
linked_list_iter_remove(&iter);
free(ref);
} else {
return 0;
}
}
}
return FSUSER_CloseArchive(archive);
}
static char path_3dsx[FILE_PATH_MAX] = "";
const char* fs_get_3dsx_path() {
if(strlen(path_3dsx) == 0) {
return NULL;
}
return path_3dsx;
}
void fs_set_3dsx_path(const char* path) {
if(strlen(path) >= 5 && strncmp(path, "sdmc:", 5) == 0) {
string_copy(path_3dsx, path + 5, FILE_PATH_MAX);
} else {
string_copy(path_3dsx, path, FILE_PATH_MAX);
}
}
int fs_make_3dsx_path(char* out, const char* name, size_t size) {
char filename[FILE_NAME_MAX];
string_escape_file_name(filename, name, sizeof(filename));
return snprintf(out, size, "/3ds/%s/%s.3dsx", filename, filename);
}
int fs_make_smdh_path(char* out, const char* name, size_t size) {
char filename[FILE_NAME_MAX];
string_escape_file_name(filename, name, sizeof(filename));
return snprintf(out, size, "/3ds/%s/%s.smdh", filename, filename);
}
FS_MediaType fs_get_title_destination(u64 titleId) {
u16 platform = (u16) ((titleId >> 48) & 0xFFFF);
u16 category = (u16) ((titleId >> 32) & 0xFFFF);
u8 variation = (u8) (titleId & 0xFF);
// DSiWare 3DS DSiWare, System, DLP Application System Title
return platform == 0x0003 || (platform == 0x0004 && ((category & 0x8011) != 0 || (category == 0x0000 && variation == 0x02))) ? MEDIATYPE_NAND : MEDIATYPE_SD;
}
bool fs_filter_cias(void* data, const char* name, u32 attributes) {
if(data != NULL) {
fs_filter_data* filterData = (fs_filter_data*) data;
if(filterData->parentFilter != NULL && !filterData->parentFilter(filterData->parentFilterData, name, attributes)) {
return false;
}
}
if((attributes & FS_ATTRIBUTE_DIRECTORY) != 0) {
return false;
}
size_t len = strlen(name);
return len >= 4 && strncasecmp(name + len - 4, ".cia", 4) == 0;
}
bool fs_filter_tickets(void* data, const char* name, u32 attributes) {
if(data != NULL) {
fs_filter_data* filterData = (fs_filter_data*) data;
if(filterData->parentFilter != NULL && !filterData->parentFilter(filterData->parentFilterData, name, attributes)) {
return false;
}
}
if((attributes & FS_ATTRIBUTE_DIRECTORY) != 0) {
return false;
}
size_t len = strlen(name);
return (len >= 4 && strncasecmp(name + len - 4, ".tik", 4) == 0) || (len >= 5 && strncasecmp(name + len - 5, ".cetk", 5) == 0);
}

View File

@ -1,31 +0,0 @@
#pragma once
#define FILE_NAME_MAX 256
#define FILE_PATH_MAX 512
typedef struct fs_filter_data_s {
bool (*parentFilter)(void* data, const char* name, u32 attributes);
void* parentFilterData;
} fs_filter_data;
bool fs_is_dir(FS_Archive archive, const char* path);
Result fs_ensure_dir(FS_Archive archive, const char* path);
FS_Path fs_make_path_binary(const void* data, u32 size);
FS_Path* fs_make_path_utf8(const char* path);
void fs_free_path_utf8(FS_Path* path);
Result fs_open_archive(FS_Archive* archive, FS_ArchiveID id, FS_Path path);
Result fs_ref_archive(FS_Archive archive);
Result fs_close_archive(FS_Archive archive);
const char* fs_get_3dsx_path();
void fs_set_3dsx_path(const char* path);
int fs_make_3dsx_path(char* out, const char* name, size_t size);
int fs_make_smdh_path(char* out, const char* name, size_t size);
FS_MediaType fs_get_title_destination(u64 titleId);
bool fs_filter_cias(void* data, const char* name, u32 attributes);
bool fs_filter_tickets(void* data, const char* name, u32 attributes);

View File

@ -1,502 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <3ds.h>
#include <curl/curl.h>
#include <jansson.h>
#include <zlib.h>
#include "fs.h"
#include "error.h"
#include "http.h"
#include "stringutil.h"
#define MAKE_HTTP_USER_AGENT_(major, minor, micro) ("Mozilla/5.0 (Nintendo 3DS; Mobile; rv:10.0) Gecko/20100101 FBI/" #major "." #minor "." #micro)
#define MAKE_HTTP_USER_AGENT(major, minor, micro) MAKE_HTTP_USER_AGENT_(major, minor, micro)
#define HTTP_USER_AGENT MAKE_HTTP_USER_AGENT(VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO)
#define HTTP_MAX_REDIRECTS 50
#define HTTP_TIMEOUT_SEC 15
#define HTTP_TIMEOUT_NS ((u64) HTTP_TIMEOUT_SEC * 1000000000)
struct httpc_context_s {
httpcContext httpc;
bool compressed;
z_stream inflate;
u8 buffer[32 * 1024];
u32 bufferSize;
};
typedef struct httpc_context_s* httpc_context;
static void httpc_resolve_redirect(char* oldUrl, const char* redirectTo, size_t size) {
if(size > 0) {
if(redirectTo[0] == '/') {
char* baseEnd = oldUrl;
// Find the third slash to find the end of the URL's base; e.g. https://www.example.com/
u32 slashCount = 0;
while(*baseEnd != '\0' && (baseEnd = strchr(baseEnd + 1, '/')) != NULL) {
slashCount++;
if(slashCount == 3) {
break;
}
}
// If there are less than 3 slashes, assume the base URL ends at the end of the string; e.g. https://www.example.com
if(slashCount != 3) {
baseEnd = oldUrl + strlen(oldUrl);
}
size_t baseLen = baseEnd - oldUrl;
if(baseLen < size) {
string_copy(baseEnd, redirectTo, size - baseLen);
}
} else {
string_copy(oldUrl, redirectTo, size);
}
}
}
static Result httpc_open(httpc_context* context, const char* url, bool userAgent) {
if(url == NULL) {
return R_APP_INVALID_ARGUMENT;
}
Result res = 0;
httpc_context ctx = (httpc_context) calloc(1, sizeof(struct httpc_context_s));
if(ctx != NULL) {
char currUrl[1024];
string_copy(currUrl, url, sizeof(currUrl));
bool resolved = false;
u32 redirectCount = 0;
while(R_SUCCEEDED(res) && !resolved && redirectCount < HTTP_MAX_REDIRECTS) {
if(R_SUCCEEDED(res = httpcOpenContext(&ctx->httpc, HTTPC_METHOD_GET, currUrl, 1))) {
u32 response = 0;
if(R_SUCCEEDED(res = httpcSetSSLOpt(&ctx->httpc, SSLCOPT_DisableVerify))
&& (!userAgent || R_SUCCEEDED(res = httpcAddRequestHeaderField(&ctx->httpc, "User-Agent", HTTP_USER_AGENT)))
&& R_SUCCEEDED(res = httpcAddRequestHeaderField(&ctx->httpc, "Accept-Encoding", "gzip, deflate"))
&& R_SUCCEEDED(res = httpcSetKeepAlive(&ctx->httpc, HTTPC_KEEPALIVE_ENABLED))
&& R_SUCCEEDED(res = httpcBeginRequest(&ctx->httpc))
&& R_SUCCEEDED(res = httpcGetResponseStatusCodeTimeout(&ctx->httpc, &response, HTTP_TIMEOUT_NS))) {
if(response == 301 || response == 302 || response == 303) {
redirectCount++;
char redirectTo[1024];
memset(redirectTo, '\0', sizeof(redirectTo));
if(R_SUCCEEDED(res = httpcGetResponseHeader(&ctx->httpc, "Location", redirectTo, sizeof(redirectTo)))) {
httpcCloseContext(&ctx->httpc);
httpc_resolve_redirect(currUrl, redirectTo, sizeof(currUrl));
}
} else {
resolved = true;
if(response == 200) {
char encoding[32];
if(R_SUCCEEDED(httpcGetResponseHeader(&ctx->httpc, "Content-Encoding", encoding, sizeof(encoding)))) {
bool gzip = strncmp(encoding, "gzip", sizeof(encoding)) == 0;
bool deflate = strncmp(encoding, "deflate", sizeof(encoding)) == 0;
ctx->compressed = gzip || deflate;
if(ctx->compressed) {
memset(&ctx->inflate, 0, sizeof(ctx->inflate));
if(deflate) {
inflateInit(&ctx->inflate);
} else if(gzip) {
inflateInit2(&ctx->inflate, MAX_WBITS | 16);
}
}
}
} else {
res = R_APP_HTTP_ERROR_BASE + response;
}
}
}
if(R_FAILED(res)) {
httpcCloseContext(&ctx->httpc);
}
}
}
if(R_SUCCEEDED(res) && redirectCount >= 32) {
res = R_APP_HTTP_TOO_MANY_REDIRECTS;
}
if(R_FAILED(res)) {
free(ctx);
}
} else {
res = R_APP_OUT_OF_MEMORY;
}
if(R_SUCCEEDED(res)) {
*context = ctx;
}
return res;
}
static Result httpc_close(httpc_context context) {
if(context == NULL) {
return R_APP_INVALID_ARGUMENT;
}
if(context->compressed) {
inflateEnd(&context->inflate);
}
Result res = httpcCloseContext(&context->httpc);
free(context);
return res;
}
static Result httpc_get_size(httpc_context context, u32* size) {
if(context == NULL || size == NULL) {
return R_APP_INVALID_ARGUMENT;
}
return httpcGetDownloadSizeState(&context->httpc, NULL, size);
}
static Result httpc_read(httpc_context context, u32* bytesRead, void* buffer, u32 size) {
if(context == NULL || buffer == NULL) {
return R_APP_INVALID_ARGUMENT;
}
Result res = 0;
u32 startPos = 0;
if(R_SUCCEEDED(res = httpcGetDownloadSizeState(&context->httpc, &startPos, NULL))) {
res = HTTPC_RESULTCODE_DOWNLOADPENDING;
u32 outPos = 0;
if(context->compressed) {
u32 lastPos = context->bufferSize;
while(res == HTTPC_RESULTCODE_DOWNLOADPENDING && outPos < size) {
if((context->bufferSize > 0
|| R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &context->buffer[context->bufferSize], sizeof(context->buffer) - context->bufferSize, HTTP_TIMEOUT_NS))
|| res == HTTPC_RESULTCODE_DOWNLOADPENDING)) {
Result posRes = 0;
u32 currPos = 0;
if(R_SUCCEEDED(posRes = httpcGetDownloadSizeState(&context->httpc, &currPos, NULL))) {
context->bufferSize += currPos - lastPos;
context->inflate.next_in = context->buffer;
context->inflate.next_out = buffer + outPos;
context->inflate.avail_in = context->bufferSize;
context->inflate.avail_out = size - outPos;
inflate(&context->inflate, Z_SYNC_FLUSH);
memcpy(context->buffer, context->buffer + (context->bufferSize - context->inflate.avail_in), context->inflate.avail_in);
context->bufferSize = context->inflate.avail_in;
lastPos = currPos;
outPos = size - context->inflate.avail_out;
} else {
res = posRes;
}
}
}
} else {
while(res == HTTPC_RESULTCODE_DOWNLOADPENDING && outPos < size) {
if(R_SUCCEEDED(res = httpcReceiveDataTimeout(&context->httpc, &((u8*) buffer)[outPos], size - outPos, HTTP_TIMEOUT_NS)) || res == HTTPC_RESULTCODE_DOWNLOADPENDING) {
Result posRes = 0;
u32 currPos = 0;
if(R_SUCCEEDED(posRes = httpcGetDownloadSizeState(&context->httpc, &currPos, NULL))) {
outPos = currPos - startPos;
} else {
res = posRes;
}
}
}
}
if(res == HTTPC_RESULTCODE_DOWNLOADPENDING) {
res = 0;
}
if(R_SUCCEEDED(res) && bytesRead != NULL) {
*bytesRead = outPos;
}
}
return res;
}
#define R_HTTP_TLS_VERIFY_FAILED 0xD8A0A03C
typedef struct {
u32 bufferSize;
void* userData;
Result (*callback)(void* userData, void* buffer, size_t size);
Result (*checkRunning)(void* userData);
Result (*progress)(void* userData, u64 total, u64 curr);
void* buf;
u32 pos;
Result res;
} http_curl_data;
static size_t http_curl_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) {
http_curl_data* curlData = (http_curl_data*) userdata;
size_t srcPos = 0;
size_t available = size * nmemb;
while(R_SUCCEEDED(curlData->res) && available > 0) {
size_t remaining = curlData->bufferSize - curlData->pos;
size_t copySize = available < remaining ? available : remaining;
memcpy((u8*) curlData->buf + curlData->pos, ptr + srcPos, copySize);
curlData->pos += copySize;
srcPos += copySize;
available -= copySize;
if(curlData->pos == curlData->bufferSize) {
curlData->res = curlData->callback(curlData->userData, curlData->buf, curlData->bufferSize);
curlData->pos = 0;
}
}
return R_SUCCEEDED(curlData->res) ? size * nmemb : 0;
}
int http_curl_xfer_info_callback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
http_curl_data* curlData = (http_curl_data*) clientp;
if(R_FAILED(curlData->res) || (curlData->checkRunning != NULL && R_FAILED(curlData->res = curlData->checkRunning(curlData->userData)))) {
return 1;
}
if(curlData->progress != NULL) {
curlData->progress(curlData->userData, (u64) dltotal, (u64) dlnow);
}
return 0;
}
Result http_download_callback(const char* url, u32 bufferSize, void* userData, Result (*callback)(void* userData, void* buffer, size_t size),
Result (*checkRunning)(void* userData),
Result (*progress)(void* userData, u64 total, u64 curr)) {
Result res = 0;
void* buf = malloc(bufferSize);
if(buf != NULL) {
httpc_context context = NULL;
if(R_SUCCEEDED(res = httpc_open(&context, url, true))) {
u32 dlSize = 0;
if(R_SUCCEEDED(res = httpc_get_size(context, &dlSize))) {
if(progress != NULL) {
progress(userData, dlSize, 0);
}
u32 total = 0;
u32 currSize = 0;
while(total < dlSize
&& (checkRunning == NULL || R_SUCCEEDED(res = checkRunning(userData)))
&& R_SUCCEEDED(res = httpc_read(context, &currSize, buf, bufferSize))
&& R_SUCCEEDED(res = callback(userData, buf, currSize))) {
if(progress != NULL) {
progress(userData, dlSize, total);
}
total += currSize;
}
Result closeRes = httpc_close(context);
if(R_SUCCEEDED(res)) {
res = closeRes;
}
}
} else if(res == R_HTTP_TLS_VERIFY_FAILED) {
res = 0;
CURL* curl = curl_easy_init();
if(curl != NULL) {
http_curl_data curlData = {bufferSize, userData, callback, checkRunning, progress, buf, 0, 0};
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, bufferSize);
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(curl, CURLOPT_USERAGENT, HTTP_USER_AGENT);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, (long) HTTP_TIMEOUT_SEC);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, (long) HTTP_MAX_REDIRECTS);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long) CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_curl_write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &curlData);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, http_curl_xfer_info_callback);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, (void*) &curlData);
CURLcode ret = curl_easy_perform(curl);
if(ret == CURLE_OK && curlData.pos != 0) {
curlData.res = curlData.callback(curlData.userData, curlData.buf, curlData.pos);
curlData.pos = 0;
}
res = curlData.res;
if(R_SUCCEEDED(res) && ret != CURLE_OK) {
if(ret == CURLE_HTTP_RETURNED_ERROR) {
long responseCode = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
res = R_APP_HTTP_ERROR_BASE + responseCode;
} else {
res = R_APP_CURL_ERROR_BASE + ret;
}
}
curl_easy_cleanup(curl);
} else {
res = R_APP_CURL_INIT_FAILED;
}
}
free(buf);
} else {
res = R_APP_OUT_OF_MEMORY;
}
return res;
}
typedef struct {
void* buf;
size_t size;
size_t pos;
} http_buffer_data;
static Result http_download_buffer_callback(void* userData, void* buffer, size_t size) {
http_buffer_data* data = (http_buffer_data*) userData;
size_t remaining = data->size - data->pos;
size_t copySize = size;
if(copySize > remaining) {
copySize = remaining;
}
if(copySize > 0) {
memcpy((u8*) data->buf + data->pos, buffer, copySize);
data->pos += copySize;
}
return 0;
}
Result http_download_buffer(const char* url, u32* downloadedSize, void* buf, size_t size) {
http_buffer_data data = {buf, size, 0};
Result res = http_download_callback(url, size, &data, http_download_buffer_callback, NULL, NULL);
if(R_SUCCEEDED(res)) {
*downloadedSize = data.pos;
}
return res;
}
Result http_download_json(const char* url, json_t** json, size_t maxSize) {
if(url == NULL || json == NULL) {
return R_APP_INVALID_ARGUMENT;
}
Result res = 0;
char* text = (char*) calloc(sizeof(char), maxSize);
if(text != NULL) {
u32 textSize = 0;
if(R_SUCCEEDED(res = http_download_buffer(url, &textSize, text, maxSize))) {
json_error_t error;
json_t* parsed = json_loads(text, 0, &error);
if(parsed != NULL) {
*json = parsed;
} else {
res = R_APP_PARSE_FAILED;
}
}
free(text);
} else {
res = R_APP_OUT_OF_MEMORY;
}
return res;
}
static Result FSUSER_AddSeed(u64 titleId, const void* seed) {
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = 0x087A0180;
cmdbuf[1] = (u32) (titleId & 0xFFFFFFFF);
cmdbuf[2] = (u32) (titleId >> 32);
memcpy(&cmdbuf[3], seed, 16);
Result ret = 0;
if(R_FAILED(ret = svcSendSyncRequest(*fsGetSessionHandle()))) return ret;
ret = cmdbuf[1];
return ret;
}
Result http_download_seed(u64 titleId) {
char pathBuf[64];
snprintf(pathBuf, 64, "/fbi/seed/%016llX.dat", titleId);
Result res = 0;
FS_Path* fsPath = fs_make_path_utf8(pathBuf);
if(fsPath != NULL) {
u8 seed[16];
Handle fileHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) {
u32 bytesRead = 0;
res = FSFILE_Read(fileHandle, &bytesRead, 0, seed, sizeof(seed));
FSFILE_Close(fileHandle);
}
fs_free_path_utf8(fsPath);
if(R_FAILED(res)) {
u8 region = CFG_REGION_USA;
CFGU_SecureInfoGetRegion(&region);
if(region <= CFG_REGION_TWN) {
static const char* regionStrings[] = {"JP", "US", "GB", "GB", "HK", "KR", "TW"};
char url[128];
snprintf(url, 128, "https://kagiya-ctr.cdn.nintendo.net/title/0x%016llX/ext_key?country=%s", titleId, regionStrings[region]);
u32 downloadedSize = 0;
if(R_SUCCEEDED(res = http_download_buffer(url, &downloadedSize, seed, sizeof(seed))) && downloadedSize != sizeof(seed)) {
res = R_APP_BAD_DATA;
}
} else {
res = R_APP_OUT_OF_RANGE;
}
}
if(R_SUCCEEDED(res)) {
res = FSUSER_AddSeed(titleId, seed);
}
} else {
res = R_APP_OUT_OF_MEMORY;
}
return res;
}

View File

@ -1,8 +0,0 @@
#pragma once
Result http_download_callback(const char* url, u32 bufferSize, void* userData, Result (*callback)(void* userData, void* buffer, size_t size),
Result (*checkRunning)(void* userData),
Result (*progress)(void* userData, u64 total, u64 curr));
Result http_download_buffer(const char* url, u32* downloadedSize, void* buf, size_t size);
Result http_download_json(const char* url, json_t** json, size_t maxSize);
Result http_download_seed(u64 titleId);

View File

@ -1,279 +0,0 @@
#include <malloc.h>
#include <stdlib.h>
#include "linkedlist.h"
void linked_list_init(linked_list* list) {
list->first = NULL;
list->last = NULL;
list->size = 0;
}
void linked_list_destroy(linked_list* list) {
linked_list_clear(list);
}
unsigned int linked_list_size(linked_list* list) {
return list->size;
}
void linked_list_clear(linked_list* list) {
linked_list_node* node = list->first;
while(node != NULL) {
linked_list_node* next = node->next;
free(node);
node = next;
}
list->first = NULL;
list->last = NULL;
list->size = 0;
}
bool linked_list_contains(linked_list* list, void* value) {
return linked_list_index_of(list, value) != -1;
}
int linked_list_index_of(linked_list* list, void* value) {
int i = 0;
linked_list_node* node = list->first;
while(node != NULL) {
if(node->value == value) {
return i;
}
i++;
node = node->next;
}
return -1;
}
static linked_list_node* linked_list_get_node(linked_list* list, unsigned int index) {
if(index >= list->size) {
return NULL;
}
linked_list_node* node = NULL;
if(index > (list->size - 1) / 2) {
node = list->last;
unsigned int pos = list->size - 1;
while(node != NULL && pos != index) {
node = node->prev;
pos--;
}
} else {
node = list->first;
unsigned int pos = 0;
while(node != NULL && pos != index) {
node = node->next;
pos++;
}
}
return node;
}
void* linked_list_get(linked_list* list, unsigned int index) {
linked_list_node* node = linked_list_get_node(list, index);
return node != NULL ? node->value : NULL;
}
bool linked_list_add(linked_list* list, void* value) {
linked_list_node* node = (linked_list_node*) calloc(1, sizeof(linked_list_node));
if(node == NULL) {
return false;
}
node->value = value;
node->next = NULL;
if(list->first == NULL || list->last == NULL) {
node->prev = NULL;
list->first = node;
list->last = node;
} else {
node->prev = list->last;
list->last->next = node;
list->last = node;
}
list->size++;
return true;
}
bool linked_list_add_at(linked_list* list, unsigned int index, void* value) {
linked_list_node* node = (linked_list_node*) calloc(1, sizeof(linked_list_node));
if(node == NULL) {
return false;
}
node->value = value;
if(index == 0) {
node->prev = NULL;
node->next = list->first;
list->first = node;
} else {
linked_list_node* prev = linked_list_get_node(list, index - 1);
if(prev == NULL) {
free(node);
return false;
}
node->prev = prev;
node->next = prev->next;
prev->next = node;
}
if(node->next != NULL) {
node->next->prev = node;
} else {
list->last = node;
}
list->size++;
return true;
}
void linked_list_add_sorted(linked_list* list, void* value, void* userData, int (*compare)(void* userData, const void* p1, const void* p2)) {
if(compare != NULL) {
unsigned int i = 0;
linked_list_node* node = list->first;
while(node != NULL) {
if(compare(userData, value, node->value) < 0) {
linked_list_add_at(list, i, value);
return;
}
i++;
node = node->next;
}
}
linked_list_add(list, value);
}
static void linked_list_remove_node(linked_list* list, linked_list_node* node) {
if(node->prev != NULL) {
node->prev->next = node->next;
}
if(node->next != NULL) {
node->next->prev = node->prev;
}
if(list->first == node) {
list->first = node->next;
}
if(list->last == node) {
list->last = node->prev;
}
list->size--;
free(node);
}
bool linked_list_remove(linked_list* list, void* value) {
bool found = false;
linked_list_node* node = list->first;
while(node != NULL) {
linked_list_node* next = node->next;
if(node->value == value) {
found = true;
linked_list_remove_node(list, node);
}
node = next;
}
return found;
}
bool linked_list_remove_at(linked_list* list, unsigned int index) {
linked_list_node* node = linked_list_get_node(list, index);
if(node == NULL) {
return false;
}
linked_list_remove_node(list, node);
return true;
}
void linked_list_sort(linked_list* list, void* userData, int (*compare)(void* userData, const void* p1, const void* p2)) {
bool swapped = true;
while(swapped) {
swapped = false;
linked_list_node* curr = list->first;
if(curr == NULL) {
return;
}
linked_list_node* next = NULL;
while((next = curr->next) != NULL) {
if(compare(userData, curr->value, next->value) > 0) {
void* temp = curr->value;
curr->value = next->value;
next->value = temp;
swapped = true;
}
curr = next;
}
}
}
void linked_list_iterate(linked_list* list, linked_list_iter* iter) {
iter->list = list;
linked_list_iter_restart(iter);
}
void linked_list_iter_restart(linked_list_iter* iter) {
if(iter->list == NULL) {
return;
}
iter->curr = NULL;
iter->next = iter->list->first;
}
bool linked_list_iter_has_next(linked_list_iter* iter) {
return iter->next != NULL && iter->next->value != NULL;
}
void* linked_list_iter_next(linked_list_iter* iter) {
linked_list_node* next = iter->next;
if(next == NULL) {
return NULL;
}
void* value = next->value;
if(value == NULL) {
return NULL;
}
iter->curr = next;
iter->next = next->next;
return value;
}
void linked_list_iter_remove(linked_list_iter* iter) {
if(iter->curr == NULL) {
return;
}
linked_list_remove_node(iter->list, iter->curr);
iter->curr = NULL;
}

View File

@ -1,43 +0,0 @@
#pragma once
#include <stdbool.h>
typedef struct linked_list_node_s {
struct linked_list_node_s* prev;
struct linked_list_node_s* next;
void* value;
} linked_list_node;
typedef struct linked_list_s {
linked_list_node* first;
linked_list_node* last;
unsigned int size;
} linked_list;
typedef struct linked_list_iter_s {
linked_list* list;
linked_list_node* curr;
linked_list_node* next;
} linked_list_iter;
void linked_list_init(linked_list* list);
void linked_list_destroy(linked_list* list);
unsigned int linked_list_size(linked_list* list);
void linked_list_clear(linked_list* list);
bool linked_list_contains(linked_list* list, void* value);
int linked_list_index_of(linked_list* list, void* value);
void* linked_list_get(linked_list* list, unsigned int index);
bool linked_list_add(linked_list* list, void* value);
bool linked_list_add_at(linked_list* list, unsigned int index, void* value);
void linked_list_add_sorted(linked_list* list, void* value, void* userData, int (*compare)(void* userData, const void* p1, const void* p2));
bool linked_list_remove(linked_list* list, void* value);
bool linked_list_remove_at(linked_list* list, unsigned int index);
void linked_list_sort(linked_list* list, void* userData, int (*compare)(void* userData, const void* p1, const void* p2));
void linked_list_iterate(linked_list* list, linked_list_iter* iter);
void linked_list_iter_restart(linked_list_iter* iter);
bool linked_list_iter_has_next(linked_list_iter* iter);
void* linked_list_iter_next(linked_list_iter* iter);
void linked_list_iter_remove(linked_list_iter* iter);

View File

@ -1,760 +0,0 @@
#include <errno.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <3ds.h>
#include <citro3d.h>
#include "error.h"
#include "screen.h"
#include "../libs/stb_image/stb_image.h"
#include "default_shbin.h"
static bool c3d_initialized;
static bool shader_initialized;
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 C3D_Tex* glyph_sheets;
static u32 glyph_count;
static float font_scale;
static u8 base_alpha = 0xFF;
static CFNT_s* font_ttf=NULL;
static u32 color_config[MAX_COLORS] = {0xFF000000};
static struct {
bool allocated;
C3D_Tex tex;
u32 width;
u32 height;
} textures[MAX_TEXTURES];
static void FontLoad(const char* filename){
FILE* f = fopen(filename, "rb");
if (!f) return ;
CFNT_s ret;
fread(&ret, 1, sizeof(CFNT_s), f);
font_ttf=linearAlloc(ret.fileSize);
if (font_ttf)
{
memcpy(font_ttf, &ret, sizeof(CFNT_s));
fread((u8*)(font_ttf) + sizeof(CFNT_s), 1, ret.fileSize - sizeof(CFNT_s), f);
}
fclose(f);
}
static void screen_set_blend(u32 color, bool rgb, bool alpha) {
C3D_TexEnv* env = C3D_GetTexEnv(0);
if(env == NULL) {
error_panic("无法检索组合器设置.");
return;
}
C3D_TexEnvInit(env);
if(rgb) {
C3D_TexEnvSrc(env, C3D_RGB, GPU_CONSTANT, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
} else {
C3D_TexEnvSrc(env, C3D_RGB, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
}
if(alpha) {
C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_CONSTANT, GPU_PRIMARY_COLOR);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
} else {
C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR);
C3D_TexEnvFunc(env, C3D_Alpha, GPU_REPLACE);
}
C3D_TexEnvColor(env, color);
}
void screen_init() {
if(!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE * 4)) {
error_panic("无法初始化 GPU.");
return;
}
FontLoad("romfs:/zh_cn.bcfnt");
c3d_initialized = true;
u32 displayFlags = GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO);
target_top = C3D_RenderTargetCreate(TOP_SCREEN_HEIGHT, TOP_SCREEN_WIDTH, GPU_RB_RGB8, 0);
if(target_top == NULL) {
error_panic("无法初始化上屏.");
return;
}
C3D_RenderTargetSetOutput(target_top, GFX_TOP, GFX_LEFT, displayFlags);
target_bottom = C3D_RenderTargetCreate(BOTTOM_SCREEN_HEIGHT, BOTTOM_SCREEN_WIDTH, GPU_RB_RGB8, 0);
if(target_bottom == NULL) {
error_panic("无法初始化下屏.");
return;
}
C3D_RenderTargetSetOutput(target_bottom, GFX_BOTTOM, GFX_LEFT, displayFlags);
Mtx_OrthoTilt(&projection_top, 0.0, TOP_SCREEN_WIDTH, TOP_SCREEN_HEIGHT, 0.0, 0.0, 1.0, true);
Mtx_OrthoTilt(&projection_bottom, 0.0, BOTTOM_SCREEN_WIDTH, BOTTOM_SCREEN_HEIGHT, 0.0, 0.0, 1.0, true);
dvlb = DVLB_ParseFile((u32*) default_shbin, default_shbin_len);
if(dvlb == NULL) {
error_panic("无法解析着色器.");
return;
}
Result progInitRes = shaderProgramInit(&program);
if(R_FAILED(progInitRes)) {
error_panic("无法初始化着色器: 0x%08lX", progInitRes);
return;
}
shader_initialized = true;
Result progSetVshRes = shaderProgramSetVsh(&program, &dvlb->DVLE[0]);
if(R_FAILED(progSetVshRes)) {
error_panic("无法设置顶点着色器: 0x%08lX", progInitRes);
return;
}
C3D_BindProgram(&program);
C3D_AttrInfo* attrInfo = C3D_GetAttrInfo();
if(attrInfo == NULL) {
error_panic("无法检索属性信息.");
return;
}
AttrInfo_Init(attrInfo);
AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3);
AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2);
C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL);
screen_set_blend(0, false, false);
Result fontMapRes = fontEnsureMapped();
if(R_FAILED(fontMapRes)) {
error_panic("无法映射系统字体: 0x%08lX", fontMapRes);
return;
}
//TGLP_s* glyphInfo = fontGetGlyphInfo(NULL);
fontFixPointers(font_ttf);
TGLP_s* glyphInfo = font_ttf->finf.tglp;
glyph_count = glyphInfo->nSheets;
glyph_sheets = calloc(glyph_count, sizeof(C3D_Tex));
if(glyph_sheets == NULL) {
error_panic("无法分配字形纹理的数据.");
return;
}
for(int i = 0; i < glyph_count; i++) {
C3D_Tex* tex = &glyph_sheets[i];
//tex->data = fontGetGlyphSheetTex(NULL, i);
tex->data = &glyphInfo->sheetData[glyphInfo->sheetSize*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);
}
font_scale = 30.0f / glyphInfo->cellHeight; // 30 is cellHeight in J machines
}
void screen_exit() {
for(u32 id = 0; id < MAX_TEXTURES; id++) {
screen_unload_texture(id);
}
if(glyph_sheets != NULL) {
free(glyph_sheets);
glyph_sheets = NULL;
}
if(shader_initialized) {
shaderProgramFree(&program);
shader_initialized = 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(c3d_initialized) {
C3D_Fini();
c3d_initialized = false;
}
}
void screen_set_base_alpha(u8 alpha) {
base_alpha = alpha;
}
void screen_set_color(u32 id, u32 color) {
if(id >= MAX_COLORS) {
error_panic("尝试绘制具有无效颜色 ID \"%lu\" 的字符串.", id);
return;
}
color_config[id] = color;
}
static u32 screen_next_pow_2(u32 i) {
i--;
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
i++;
return i;
}
u32 screen_allocate_free_texture() {
u32 id = 0;
for(u32 i = 1; i < MAX_TEXTURES; i++) {
if(!textures[i].allocated) {
textures[i].allocated = true;
id = i;
break;
}
}
if(id == 0) {
error_panic("超出空闲纹理.");
return 0;
}
return id;
}
static void screen_prepare_texture(u32* pow2WidthOut, u32* pow2HeightOut, u32 id, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter) {
if(id >= MAX_TEXTURES) {
error_panic("尝试准备无效的纹理 ID \"%lu\".", id);
return;
}
u32 pow2Width = screen_next_pow_2(width);
if(pow2Width < 64) {
pow2Width = 64;
}
u32 pow2Height = screen_next_pow_2(height);
if(pow2Height < 64) {
pow2Height = 64;
}
if(textures[id].tex.data != NULL && (textures[id].tex.width != pow2Width || textures[id].tex.height != pow2Height || textures[id].tex.fmt != format)) {
C3D_TexDelete(&textures[id].tex);
textures[id].tex.data = NULL;
}
if(textures[id].tex.data == NULL && !C3D_TexInit(&textures[id].tex, (u16) pow2Width, (u16) pow2Height, format)) {
error_panic("无法初始化具有 ID \"%lu\" 的纹理.", id);
return;
}
C3D_TexSetFilter(&textures[id].tex, linearFilter ? GPU_LINEAR : GPU_NEAREST, GPU_NEAREST);
textures[id].allocated = true;
textures[id].width = width;
textures[id].height = height;
if(pow2WidthOut != NULL) {
*pow2WidthOut = pow2Width;
}
if(pow2HeightOut != NULL) {
*pow2HeightOut = pow2Height;
}
}
void screen_load_texture_tiled(u32 id, void* data, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter) {
u32 pow2Width = 0;
u32 pow2Height = 0;
screen_prepare_texture(&pow2Width, &pow2Height, id, width, height, format, linearFilter);
if(width != pow2Width || height != pow2Height) {
u32 pixelSize = size / width / height;
memset(textures[id].tex.data, 0, textures[id].tex.size);
for(u32 y = 0; y < height; y += 8) {
u32 dstPos = y * pow2Width * pixelSize;
u32 srcPos = y * width * pixelSize;
memcpy(&((u8*) textures[id].tex.data)[dstPos], &((u8*) data)[srcPos], width * 8 * pixelSize);
}
} else {
memcpy(textures[id].tex.data, data, textures[id].tex.size);
}
C3D_TexFlush(&textures[id].tex);
}
void screen_load_texture_untiled(u32 id, void* data, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter) {
u32 pow2Width = 0;
u32 pow2Height = 0;
screen_prepare_texture(&pow2Width, &pow2Height, id, width, height, format, linearFilter);
u32 pixelSize = size / width / height;
memset(textures[id].tex.data, 0, textures[id].tex.size);
for(u32 x = 0; x < width; x++) {
for(u32 y = 0; y < height; y++) {
u32 dstPos = ((((y >> 3) * (pow2Width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) * pixelSize;
u32 srcPos = (y * width + x) * pixelSize;
memcpy(&((u8*) textures[id].tex.data)[dstPos], &((u8*) data)[srcPos], pixelSize);
}
}
C3D_TexFlush(&textures[id].tex);
}
void screen_load_texture_path(u32 id, const char* path, bool linearFilter) {
if(id >= MAX_TEXTURES) {
error_panic("尝试加载路径 \"%s\" 到无效的纹理 ID \"%lu\".", path, id);
return;
}
FILE* fd = fopen(path, "rb");
if(fd == NULL) {
error_panic("无法加载 PNG 文件 \"%s\": %s", path, strerror(errno));
return;
}
screen_load_texture_file(id, fd, linearFilter);
fclose(fd);
}
void screen_load_texture_file(u32 id, FILE* fd, bool linearFilter) {
if(id >= MAX_TEXTURES) {
error_panic("尝试加载文件到无效的纹理 ID \"%lu\".", id);
return;
}
int width;
int height;
int depth;
u8* image = stbi_load_from_file(fd, &width, &height, &depth, STBI_rgb_alpha);
if(image == NULL) {
error_panic("尝试加载 PNG 文件到纹理 ID \"%lu\".", id);
return;
}
for(u32 x = 0; x < width; x++) {
for(u32 y = 0; y < height; y++) {
u32 pos = (y * width + x) * 4;
u8 c1 = image[pos + 0];
u8 c2 = image[pos + 1];
u8 c3 = image[pos + 2];
u8 c4 = image[pos + 3];
image[pos + 0] = c4;
image[pos + 1] = c3;
image[pos + 2] = c2;
image[pos + 3] = c1;
}
}
screen_load_texture_untiled(id, image, (u32) (width * height * 4), (u32) width, (u32) height, GPU_RGBA8, linearFilter);
free(image);
}
void screen_unload_texture(u32 id) {
if(id >= MAX_TEXTURES) {
error_panic("尝试卸载无效的纹理 ID \"%lu\".", id);
return;
}
C3D_TexDelete(&textures[id].tex);
textures[id].tex.data = NULL;
textures[id].allocated = false;
textures[id].width = 0;
textures[id].height = 0;
}
void screen_get_texture_size(u32* width, u32* height, u32 id) {
if(id >= MAX_TEXTURES) {
error_panic("尝试获取无效的纹理 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)) {
error_panic("无法启动相机.");
return;
}
}
void screen_end_frame() {
C3D_FrameEnd(0);
}
void screen_select(gfxScreen_t screen) {
C3D_RenderTarget* target = screen == GFX_TOP ? target_top : target_bottom;
C3D_RenderTargetClear(target, C3D_CLEAR_ALL, 0, 0);
if(!C3D_FrameDrawOn(target)) {
error_panic("无法选择渲染目标.");
return;
}
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, shaderInstanceGetUniformLocation(program.vertexShader, "projection"), screen == GFX_TOP ? &projection_top : &projection_bottom);
}
static void screen_draw_quad(float x1, float y1, float x2, float y2, float left, float bottom, float right, float top) {
C3D_ImmDrawBegin(GPU_TRIANGLE_STRIP);
C3D_ImmSendAttrib(x1, y2, 0.5f, 0.0f);
C3D_ImmSendAttrib(left, bottom, 0.0f, 0.0f);
C3D_ImmSendAttrib(x2, y2, 0.5f, 0.0f);
C3D_ImmSendAttrib(right, bottom, 0.0f, 0.0f);
C3D_ImmSendAttrib(x1, y1, 0.5f, 0.0f);
C3D_ImmSendAttrib(left, top, 0.0f, 0.0f);
C3D_ImmSendAttrib(x2, y1, 0.5f, 0.0f);
C3D_ImmSendAttrib(right, top, 0.0f, 0.0f);
C3D_ImmDrawEnd();
}
void screen_draw_texture(u32 id, float x, float y, float width, float height) {
if(id >= MAX_TEXTURES) {
error_panic("尝试绘制无效的纹理 ID \"%lu\".", id);
return;
}
if(textures[id].tex.data == NULL) {
return;
}
if(base_alpha != 0xFF) {
screen_set_blend(base_alpha << 24, false, true);
}
C3D_TexBind(0, &textures[id].tex);
screen_draw_quad(x, y, x + width, y + height, 0, (float) (textures[id].tex.height - textures[id].height) / (float) textures[id].tex.height, (float) textures[id].width / (float) textures[id].tex.width, 1.0f);
if(base_alpha != 0xFF) {
screen_set_blend(0, false, false);
}
}
void screen_draw_texture_crop(u32 id, float x, float y, float width, float height) {
if(id >= MAX_TEXTURES) {
error_panic("尝试绘制无效的纹理 ID \"%lu\".", id);
return;
}
if(textures[id].tex.data == NULL) {
return;
}
if(base_alpha != 0xFF) {
screen_set_blend(base_alpha << 24, false, true);
}
C3D_TexBind(0, &textures[id].tex);
screen_draw_quad(x, y, x + width, y + height, 0, (float) (textures[id].tex.height - textures[id].height) / (float) textures[id].tex.height, width / (float) textures[id].tex.width, (textures[id].tex.height - textures[id].height + height) / (float) textures[id].tex.height);
if(base_alpha != 0xFF) {
screen_set_blend(0, false, false);
}
}
float screen_get_font_height(float scaleY) {
//return scaleY * fontGetInfo(NULL)->lineFeed;
return scaleY * fontGetInfo(font_ttf)->lineFeed;
}
#define MAX_LINES 64
inline static void screen_wrap_string_finish_line(float* w, float* h, float* lw, float* lh, u32* line, u32* linePos, u32* lastAlignPos,
u32* lines, float* lineWidths, float* lineHeights,
u32 maxLines) {
if(*lw > *w) {
*w = *lw;
}
*h += *lh;
if(*line < maxLines) {
if(lines != NULL) {
lines[*line] = *linePos;
}
if(lineWidths != NULL) {
lineWidths[*line] = *lw;
}
if(lineHeights != NULL) {
lineHeights[*line] = *lh;
}
(*line)++;
}
*lw = 0;
*lh = 0;
*linePos = 0;
*lastAlignPos = 0;
}
static void screen_wrap_string(u32* lines, float* lineWidths, float* lineHeights, u32* numLines, float* totalWidth, float* totalHeight,
const char* text, u32 maxLines, float maxWidth, float scaleX, float scaleY, bool wordWrap) {
scaleX *= font_scale;
scaleY *= font_scale;
float w = 0;
float h = 0;
u32 line = 0;
float lw = 0;
float lh = 0;
u32 linePos = 0;
u32 lastAlignPos = 0;
int wordPos = -1;
float ww = 0;
const uint8_t* p = (const uint8_t*) text;
u32 code = 0;
ssize_t units = -1;
while(*p && (units = decode_utf8(&code, p)) != -1 && code > 0) {
p += units;
float charWidth = 1;
if(code == '\t') {
code = ' ';
charWidth = 4 - (linePos - lastAlignPos) % 4;
lastAlignPos = linePos;
}
//charWidth *= scaleX * fontGetCharWidthInfo(NULL, fontGlyphIndexFromCodePoint(NULL, code))->charWidth;
charWidth *= scaleX * fontGetCharWidthInfo(font_ttf, fontGlyphIndexFromCodePoint(font_ttf, code))->charWidth;
if(code == '\n' || (wordWrap && lw + charWidth >= maxWidth)) {
if(code == '\n') {
linePos++;
//lh = scaleY * fontGetInfo(NULL)->lineFeed;
lh = scaleY * fontGetInfo(font_ttf)->lineFeed;
}
u32 oldLinePos = linePos;
if(code != '\n' && wordPos != -1) {
linePos = (u32) wordPos;
lw -= ww;
}
screen_wrap_string_finish_line(&w, &h, &lw, &lh, &line, &linePos, &lastAlignPos,
lines, lineWidths, lineHeights,
maxLines);
if(code != '\n' && wordPos != -1) {
linePos = oldLinePos - wordPos;
lw = ww;
}
wordPos = -1;
ww = 0;
}
if(code == ' ') {
wordPos = -1;
ww = 0;
} else if(wordPos == -1) {
wordPos = (int) linePos;
ww = 0;
}
if(code != '\n') {
if(wordPos != -1) {
ww += charWidth;
}
lw += charWidth;
//lh = scaleY * fontGetInfo(NULL)->lineFeed;
lh = scaleY * fontGetInfo(font_ttf)->lineFeed;
linePos++;
}
}
if(linePos > 0) {
screen_wrap_string_finish_line(&w, &h, &lw, &lh, &line, &linePos, &lastAlignPos,
lines, lineWidths, lineHeights,
maxLines);
}
if(numLines != NULL) {
*numLines = line;
}
if(totalWidth != NULL) {
*totalWidth = w;
}
if(totalHeight != NULL) {
*totalHeight = h;
}
}
void screen_get_string_size(float* width, float* height, const char* text, float scaleX, float scaleY) {
screen_wrap_string(NULL, NULL, NULL, NULL, width, height, text, 0, 0, scaleX, scaleY, false);
}
void screen_get_string_size_wrap(float* width, float* height, const char* text, float scaleX, float scaleY, float wrapWidth) {
screen_wrap_string(NULL, NULL, NULL, NULL, width, height, text, 0, wrapWidth, scaleX, scaleY, true);
}
static void screen_draw_string_internal(const char* text, float x, float y, float scaleX, float scaleY, u32 colorId, bool centerLines, bool wrap, float wrapX) {
if(text == NULL) {
return;
}
if(colorId >= MAX_COLORS) {
error_panic("尝试绘制具有无效颜色 ID \"%lu\" 的字符串.", colorId);
return;
}
u32 blendColor = color_config[colorId];
if(base_alpha != 0xFF) {
float alpha1 = ((blendColor >> 24) & 0xFF) / 255.0f;
float alpha2 = base_alpha / 255.0f;
float blendedAlpha = alpha1 * alpha2;
blendColor = (((u32) (blendedAlpha * 0xFF)) << 24) | (blendColor & 0x00FFFFFF);
}
screen_set_blend(blendColor, true, true);
u32 lines[MAX_LINES];
float lineWidths[MAX_LINES];
float lineHeights[MAX_LINES];
u32 numLines = 0;
float totalWidth = 0;
float totalHeight = 0;
screen_wrap_string(lines, lineWidths, lineHeights, &numLines, &totalWidth, &totalHeight, text, MAX_LINES, wrapX - x, scaleX, scaleY, wrap);
float currX = x;
float currY = y;
u32 linePos = 0;
u32 lastAlignPos = 0;
int lastSheet = -1;
const uint8_t* p = (const uint8_t*) text;
u32 code = 0;
ssize_t units = -1;
for(u32 i = 0; i < numLines; i++) {
currX = x;
if(centerLines) {
currX += (totalWidth - lineWidths[i]) / 2;
}
while(linePos < lines[i] && *p && (units = decode_utf8(&code, p)) != -1 && code > 0) {
p += units;
if(code != '\n') {
u32 num = 1;
if(code == '\t') {
code = ' ';
num = 4 - (linePos - lastAlignPos) % 4;
lastAlignPos = linePos;
}
fontGlyphPos_s data;
//fontCalcGlyphPos(&data, NULL, fontGlyphIndexFromCodePoint(NULL, code), GLYPH_POS_CALC_VTXCOORD, scaleX * font_scale, scaleY * font_scale);
fontCalcGlyphPos(&data, font_ttf, fontGlyphIndexFromCodePoint(font_ttf, code), GLYPH_POS_CALC_VTXCOORD, scaleX * font_scale, scaleY * font_scale);
if(data.sheetIndex >= glyph_count) {
//fontCalcGlyphPos(&data, NULL, fontGlyphIndexFromCodePoint(NULL, 0xFFFD), GLYPH_POS_CALC_VTXCOORD, scaleX * font_scale, scaleY * font_scale);
fontCalcGlyphPos(&data, font_ttf, fontGlyphIndexFromCodePoint(font_ttf, 0xFFFD), GLYPH_POS_CALC_VTXCOORD, scaleX * font_scale, scaleY * font_scale);
}
if(data.sheetIndex < glyph_count && data.sheetIndex != lastSheet) {
lastSheet = data.sheetIndex;
C3D_TexBind(0, &glyph_sheets[lastSheet]);
}
for(u32 j = 0; j < num; j++) {
screen_draw_quad(currX + data.vtxcoord.left, currY + data.vtxcoord.top, currX + data.vtxcoord.right, currY + data.vtxcoord.bottom, data.texcoord.left, data.texcoord.bottom, data.texcoord.right, data.texcoord.top);
currX += data.xAdvance;
}
}
linePos++;
}
currY += lineHeights[i];
linePos = 0;
lastAlignPos = 0;
}
screen_set_blend(0, false, false);
}
void screen_draw_string(const char* text, float x, float y, float scaleX, float scaleY, u32 colorId, bool centerLines) {
screen_draw_string_internal(text, x, y, scaleX, scaleY, colorId, centerLines, false, 0);
}
void screen_draw_string_wrap(const char* text, float x, float y, float scaleX, float scaleY, u32 colorId, bool centerLines, float wrapX) {
screen_draw_string_internal(text, x, y, scaleX, scaleY, colorId, centerLines, true, wrapX);
}

View File

@ -1,36 +0,0 @@
#pragma once
typedef struct __sFILE FILE;
#define TOP_SCREEN_WIDTH 400
#define TOP_SCREEN_HEIGHT 240
#define BOTTOM_SCREEN_WIDTH 320
#define BOTTOM_SCREEN_HEIGHT 240
#define MAX_TEXTURES 1024
#define MAX_COLORS 32
#define COLOR_TEXT 0
void screen_init();
void screen_exit();
void screen_set_base_alpha(u8 alpha);
void screen_set_color(u32 id, u32 color);
u32 screen_allocate_free_texture();
void screen_load_texture_untiled(u32 id, void* data, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter);
void screen_load_texture_path(u32 id, const char* path, bool linearFilter);
void screen_load_texture_file(u32 id, FILE* fd, bool linearFilter);
void screen_load_texture_tiled(u32 id, void* data, 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);
float screen_get_font_height(float scaleY);
void screen_get_string_size(float* width, float* height, const char* text, float scaleX, float scaleY);
void screen_get_string_size_wrap(float* width, float* height, const char* text, float scaleX, float scaleY, float wrapWidth);
void screen_draw_string(const char* text, float x, float y, float scaleX, float scaleY, u32 colorId, bool centerLines);
void screen_draw_string_wrap(const char* text, float x, float y, float scaleX, float scaleY, u32 colorId, bool centerLines, float wrapX);

View File

@ -1,495 +0,0 @@
#include <malloc.h>
#include <string.h>
#include <3ds.h>
#include "error.h"
#include "spi.h"
/*
* Based on information from TWLSaveTool, by TuxSH.
* - https://github.com/TuxSH/TWLSaveTool/blob/master/source/SPI.cpp
*
* The original license is as follows:
*
* Copyright (C) 2015-2016 TuxSH
*
* TWLSaveTool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#define SPI_CMD_RDSR 5
#define SPI_CMD_WREN 6
#define SPI_CMD_RDID 0x9F
#define SPI_EEPROM_512B_CMD_WRLO 2
#define SPI_EEPROM_512B_CMD_RDLO 3
#define SPI_EEPROM_512B_CMD_WRHI 10
#define SPI_EEPROM_512B_CMD_RDHI 11
#define SPI_EEPROM_CMD_WRITE 2
#define SPI_EEPROM_CMD_READ 3
#define SPI_FLASH_CMD_READ 3
#define SPI_FLASH_CMD_PW 10
#define SPI_STAT_WIP 1
#define SPI_STAT_WEL 2
typedef enum {
CHIP_NONE = 0,
CHIP_EEPROM_512B = 1,
CHIP_EEPROM_8KB = 2,
CHIP_EEPROM_64KB = 3,
CHIP_EEPROM_128KB = 4,
CHIP_FLASH_256KB = 5,
CHIP_FLASH_512KB = 6,
CHIP_FLASH_1MB = 7,
CHIP_FLASH_8MB = 8,
CHIP_FLASH_256KB_INFRARED = 9,
CHIP_FLASH_512KB_INFRARED = 10,
CHIP_FLASH_1MB_INFRARED = 11,
CHIP_FLASH_8MB_INFRARED = 12
} SaveChip;
static Result spi_get_page_size(SaveChip chip, u32* pageSize) {
Result res = 0;
u32 size = 0;
switch(chip) {
case CHIP_EEPROM_512B:
size = 16;
break;
case CHIP_EEPROM_8KB:
size = 32;
break;
case CHIP_EEPROM_64KB:
size = 128;
break;
case CHIP_EEPROM_128KB:
case CHIP_FLASH_256KB:
case CHIP_FLASH_512KB:
case CHIP_FLASH_1MB:
case CHIP_FLASH_8MB:
case CHIP_FLASH_256KB_INFRARED:
case CHIP_FLASH_512KB_INFRARED:
case CHIP_FLASH_1MB_INFRARED:
case CHIP_FLASH_8MB_INFRARED:
size = 256;
break;
default:
res = R_APP_NOT_IMPLEMENTED;
break;
}
if(R_SUCCEEDED(res) && pageSize != NULL) {
*pageSize = size;
}
return res;
}
static Result spi_get_capacity(SaveChip chip, u32* capacity) {
Result res = 0;
u32 cap = 0;
switch(chip) {
case CHIP_EEPROM_512B:
cap = 512;
break;
case CHIP_EEPROM_8KB:
cap = 8 * 1024;
break;
case CHIP_EEPROM_64KB:
cap = 64 * 1024;
break;
case CHIP_EEPROM_128KB:
cap = 128 * 1024;
break;
case CHIP_FLASH_256KB:
case CHIP_FLASH_256KB_INFRARED:
cap = 256 * 1024;
break;
case CHIP_FLASH_512KB:
case CHIP_FLASH_512KB_INFRARED:
cap = 512 * 1024;
break;
case CHIP_FLASH_1MB:
case CHIP_FLASH_1MB_INFRARED:
cap = 1024 * 1024;
break;
case CHIP_FLASH_8MB:
case CHIP_FLASH_8MB_INFRARED:
cap = 8 * 1024 * 1024;
break;
default:
res = R_APP_NOT_IMPLEMENTED;
break;
}
if(R_SUCCEEDED(res) && capacity != NULL) {
*capacity = cap;
}
return res;
}
static Result spi_execute_command(SaveChip chip, void* cmd, u32 cmdSize, void* answer, u32 answerSize, void* data, u32 dataSize) {
if(chip == CHIP_NONE) {
return R_APP_NOT_IMPLEMENTED;
}
bool infrared = chip == CHIP_FLASH_256KB_INFRARED || chip == CHIP_FLASH_512KB_INFRARED || chip == CHIP_FLASH_1MB_INFRARED || chip == CHIP_FLASH_8MB_INFRARED;
u8 transferOp = pxiDevMakeTransferOption(BAUDRATE_4MHZ, BUSMODE_1BIT);
u64 waitOp = pxiDevMakeWaitOperation(WAIT_NONE, DEASSERT_NONE, 0);
u8 dummy = 0;
PXIDEV_SPIBuffer header = {infrared ? &dummy : NULL, infrared ? 1 : 0, infrared ? pxiDevMakeTransferOption(BAUDRATE_1MHZ, BUSMODE_1BIT) : transferOp, waitOp};
PXIDEV_SPIBuffer writeBuffer1 = {cmd, cmdSize, transferOp, waitOp};
PXIDEV_SPIBuffer readBuffer1 = {answer, answerSize, transferOp, waitOp};
PXIDEV_SPIBuffer writeBuffer2 = {data, dataSize, transferOp, waitOp};
PXIDEV_SPIBuffer readBuffer2 = {NULL, 0, transferOp, waitOp};
PXIDEV_SPIBuffer footer = {NULL, 0, transferOp, waitOp};
return PXIDEV_SPIMultiWriteRead(&header, &writeBuffer1, &readBuffer1, &writeBuffer2, &readBuffer2, &footer);
}
static Result spi_wait_write_finish(SaveChip chip) {
Result res = 0;
u8 cmd = SPI_CMD_RDSR;
u8 status = 0;
while(R_SUCCEEDED(res = spi_execute_command(chip, &cmd, sizeof(cmd), &status, sizeof(status), NULL, 0)) && (status & SPI_STAT_WIP));
return res;
}
static Result spi_read_jedec_id_status(SaveChip chip, u32* jecedId, u8* status) {
Result res = 0;
u8 cmd = SPI_CMD_RDID;
u8 idData[3] = {0};
if(R_SUCCEEDED(res = spi_wait_write_finish(chip)) && R_SUCCEEDED(res = spi_execute_command(chip, &cmd, sizeof(cmd), idData, sizeof(idData), NULL, 0))) {
cmd = SPI_CMD_RDSR;
u8 stat = 0;
if(R_SUCCEEDED(res = spi_execute_command(chip, &cmd, 1, &stat, 1, 0, 0))) {
if(jecedId != NULL) {
*jecedId = (idData[0] << 16) | (idData[1] << 8) | idData[2];
}
if(status != NULL) {
*status = stat;
}
}
}
return res;
}
static Result spi_read_data(SaveChip chip, u32* bytesRead, void* data, u32 offset, u32 size) {
Result res = 0;
u32 capacity = 0;
if(R_SUCCEEDED(res = spi_get_capacity(chip, &capacity))) {
if(size > capacity - offset) {
size = capacity - offset;
}
u32 pos = offset;
if(size > 0 && R_SUCCEEDED(res = spi_wait_write_finish(chip))) {
u8 cmd[4] = {0};
u32 cmdSize = 0;
switch(chip) {
case CHIP_EEPROM_512B:
if(pos < 0x100) {
u32 len = size > 0x100 - pos ? 0x100 - pos : size;
cmdSize = 2;
cmd[0] = SPI_EEPROM_512B_CMD_RDLO;
cmd[1] = (u8) pos;
res = spi_execute_command(chip, cmd, cmdSize, data, len, NULL, 0);
pos += len;
data = (u8*) data + len;
size = size - len;
}
if(R_SUCCEEDED(res) && pos >= 0x100 && size > 0) {
u32 len = size > 0x200 - pos ? 0x200 - pos : size;
cmdSize = 2;
cmd[0] = SPI_EEPROM_512B_CMD_RDHI;
cmd[1] = (u8) pos;
res = spi_execute_command(chip, cmd, cmdSize, data, len, NULL, 0);
pos += len;
}
break;
case CHIP_EEPROM_8KB:
case CHIP_EEPROM_64KB:
cmdSize = 3;
cmd[0] = SPI_EEPROM_CMD_READ;
cmd[1] = (u8) (pos >> 8);
cmd[2] = (u8) pos;
res = spi_execute_command(chip, cmd, cmdSize, data, size, NULL, 0);
pos += size;
break;
case CHIP_EEPROM_128KB:
cmdSize = 4;
cmd[0] = SPI_EEPROM_CMD_READ;
cmd[1] = (u8) (pos >> 16);
cmd[2] = (u8) (pos >> 8);
cmd[3] = (u8) pos;
res = spi_execute_command(chip, cmd, cmdSize, data, size, NULL, 0);
pos += size;
break;
case CHIP_FLASH_256KB:
case CHIP_FLASH_512KB:
case CHIP_FLASH_1MB:
case CHIP_FLASH_8MB:
case CHIP_FLASH_256KB_INFRARED:
case CHIP_FLASH_512KB_INFRARED:
case CHIP_FLASH_1MB_INFRARED:
case CHIP_FLASH_8MB_INFRARED:
cmdSize = 4;
cmd[0] = SPI_FLASH_CMD_READ;
cmd[1] = (u8) (pos >> 16);
cmd[2] = (u8) (pos >> 8);
cmd[3] = (u8) pos;
res = spi_execute_command(chip, cmd, cmdSize, data, size, NULL, 0);
pos += size;
break;
default:
res = R_APP_NOT_IMPLEMENTED;
break;
}
}
if(R_SUCCEEDED(res) && bytesRead != NULL) {
*bytesRead = pos - offset;
}
}
return res;
}
static Result spi_write_data(SaveChip chip, u32* bytesWritten, void* data, u32 offset, u32 size) {
Result res = 0;
u32 pageSize = 0;
u32 capacity = 0;
if(R_SUCCEEDED(res = spi_get_page_size(chip, &pageSize)) && R_SUCCEEDED(res = spi_get_capacity(chip, &capacity))) {
if(size > capacity - offset) {
size = capacity - offset;
}
u32 pos = offset;
if(size > 0 && R_SUCCEEDED(res = spi_wait_write_finish(chip))) {
while(pos < offset + size) {
u8 cmd[4] = {0};
u32 cmdSize = 0;
switch(chip) {
case CHIP_EEPROM_512B:
cmdSize = 2;
cmd[0] = (pos >= 0x100) ? (u8) SPI_EEPROM_512B_CMD_WRHI : (u8) SPI_EEPROM_512B_CMD_WRLO;
cmd[1] = (u8) pos;
break;
case CHIP_EEPROM_8KB:
case CHIP_EEPROM_64KB:
cmdSize = 3;
cmd[0] = SPI_EEPROM_CMD_WRITE;
cmd[1] = (u8) (pos >> 8);
cmd[2] = (u8) pos;
break;
case CHIP_EEPROM_128KB:
cmdSize = 4;
cmd[0] = SPI_EEPROM_CMD_WRITE;
cmd[1] = (u8) (pos >> 16);
cmd[2] = (u8) (pos >> 8);
cmd[3] = (u8) pos;
break;
case CHIP_FLASH_256KB:
case CHIP_FLASH_512KB:
case CHIP_FLASH_1MB:
case CHIP_FLASH_256KB_INFRARED:
case CHIP_FLASH_512KB_INFRARED:
case CHIP_FLASH_1MB_INFRARED:
cmdSize = 4;
cmd[0] = SPI_FLASH_CMD_PW;
cmd[1] = (u8) (pos >> 16);
cmd[2] = (u8) (pos >> 8);
cmd[3] = (u8) pos;
break;
case CHIP_FLASH_8MB:
case CHIP_FLASH_8MB_INFRARED:
default:
res = R_APP_NOT_IMPLEMENTED;
break;
}
if(R_FAILED(res)) {
break;
}
u32 pagePos = pos & ~(pageSize - 1);
u32 currSize = size - (pos - offset);
if(currSize > pageSize - (pos - pagePos)) {
currSize = pageSize - (pos - pagePos);
}
u8 ewCmd = SPI_CMD_WREN;
if(R_SUCCEEDED(res = spi_execute_command(chip, &ewCmd, sizeof(ewCmd), NULL, 0, NULL, 0))) {
if(chip != CHIP_EEPROM_512B) {
ewCmd = SPI_CMD_RDSR;
u8 status = 0;
while(R_SUCCEEDED(res = spi_execute_command(chip, &ewCmd, sizeof(ewCmd), &status, sizeof(status), NULL, 0)) && (status & ~SPI_STAT_WEL));
}
if(R_SUCCEEDED(res) && R_SUCCEEDED(res = spi_execute_command(chip, cmd, cmdSize, NULL, 0, (u8*) data + (pos - offset), currSize))) {
res = spi_wait_write_finish(chip);
}
}
if(R_FAILED(res)) {
break;
}
pos = pagePos + pageSize;
}
}
if(R_SUCCEEDED(res) && bytesWritten != NULL) {
*bytesWritten = pos - offset;
}
}
return res;
}
static Result spi_is_data_mirrored(SaveChip chip, u32 size, bool* mirrored) {
Result res = 0;
u8 original = 0;
u8 oldMirror = 0;
if(R_SUCCEEDED(res = spi_read_data(chip, NULL, &original, size - 1, sizeof(original)))
&& R_SUCCEEDED(res = spi_read_data(chip, NULL, &oldMirror, 2 * size - 1, sizeof(oldMirror)))) {
u8 modified = ~original;
u8 newMirror = 0;
if(R_SUCCEEDED(res = spi_write_data(chip, NULL, &modified, size - 1, sizeof(modified)))
&& R_SUCCEEDED(res = spi_read_data(chip, NULL, &newMirror, 2 * size - 1, sizeof(newMirror)))
&& R_SUCCEEDED(res = spi_write_data(chip, NULL, &original, size - 1, sizeof(original)))) {
if(mirrored != NULL) {
*mirrored = oldMirror != newMirror;
}
}
}
return res;
}
static Result spi_get_save_chip(SaveChip* chip, SaveChip base) {
Result res = 0;
u32 jedecId = 0;
u8 status = 0;
if(R_SUCCEEDED(res = spi_read_jedec_id_status(base, &jedecId, &status))) {
SaveChip c = CHIP_NONE;
if(jedecId == 0xFFFFFF && ((status & 0xFD) == 0xF0 || (status & 0xFD) == 0x00)) {
if((status & 0xFD) == 0xF0) {
c = CHIP_EEPROM_512B;
} else if((status & 0xFD) == 0x00) {
bool mirrored = false;
if(R_SUCCEEDED(res = spi_is_data_mirrored(CHIP_EEPROM_8KB, 8 * 1024, &mirrored))) {
if(mirrored) {
c = CHIP_EEPROM_8KB;
} else {
if(R_SUCCEEDED(res = spi_is_data_mirrored(CHIP_EEPROM_64KB, 64 * 1024, &mirrored))) {
if(mirrored) {
c = CHIP_EEPROM_64KB;
} else {
c = CHIP_EEPROM_128KB;
}
}
}
}
}
} else {
c = base < CHIP_FLASH_256KB_INFRARED ? CHIP_FLASH_256KB : CHIP_FLASH_256KB_INFRARED;
switch(jedecId) {
case 0x204012:
case 0x621600:
c += CHIP_FLASH_256KB - CHIP_FLASH_256KB;
break;
case 0x204013:
case 0x621100:
c += CHIP_FLASH_512KB - CHIP_FLASH_256KB;
break;
case 0x204014:
c += CHIP_FLASH_1MB - CHIP_FLASH_256KB;
break;
case 0x202017:
case 0x204017:
c += CHIP_FLASH_8MB - CHIP_FLASH_256KB;
break;
default:
if(base < CHIP_FLASH_256KB_INFRARED) {
res = spi_get_save_chip(&c, CHIP_FLASH_256KB_INFRARED);
} else {
res = R_APP_NOT_IMPLEMENTED;
}
break;
}
}
if(R_SUCCEEDED(res) && chip != NULL) {
*chip = c;
}
}
return res;
}
static SaveChip curr_chip = CHIP_NONE;
Result spi_init_card() {
return spi_get_save_chip(&curr_chip, CHIP_EEPROM_512B);
}
Result spi_deinit_card() {
curr_chip = CHIP_NONE;
return 0;
}
Result spi_get_save_size(u32* size) {
return spi_get_capacity(curr_chip, size);
}
Result spi_read_save(u32* bytesRead, void* data, u32 offset, u32 size) {
return spi_read_data(curr_chip, bytesRead, data, offset, size);
}
Result spi_write_save(u32* bytesWritten, void* data, u32 offset, u32 size) {
return spi_write_data(curr_chip, bytesWritten, data, offset, size);
}

View File

@ -1,7 +0,0 @@
#pragma once
Result spi_init_card();
Result spi_deinit_card();
Result spi_get_save_size(u32* size);
Result spi_read_save(u32* bytesRead, void* data, u32 offset, u32 size);
Result spi_write_save(u32* bytesWritten, void* data, u32 offset, u32 size);

View File

@ -1,104 +0,0 @@
#include <string.h>
#include <3ds.h>
#include "stringutil.h"
bool string_is_empty(const char* str) {
if(strlen(str) == 0) {
return true;
}
const char* curr = str;
while(*curr) {
if(*curr != ' ') {
return false;
}
curr++;
}
return true;
}
void string_copy(char* dst, const char* src, size_t size) {
if(size > 0) {
strncpy(dst, src, size - 1);
dst[size - 1] = '\0';
}
}
void string_get_file_name(char* out, const char* file, u32 size) {
const char* end = file + strlen(file);
const char* curr = file - 1;
while((curr = strchr(curr + 1, '.')) != NULL) {
end = curr;
}
u32 terminatorPos = end - file < size - 1 ? end - file : size - 1;
strncpy(out, file, terminatorPos);
out[terminatorPos] = '\0';
}
void string_escape_file_name(char* out, const char* file, size_t size) {
static const char reservedChars[] = {'<', '>', ':', '"', '/', '\\', '|', '?', '*'};
for(u32 i = 0; i < size; i++) {
bool reserved = false;
for(u32 j = 0; j < sizeof(reservedChars); j++) {
if(file[i] == reservedChars[j]) {
reserved = true;
break;
}
}
if(reserved) {
out[i] = '_';
} else {
out[i] = file[i];
}
if(file[i] == '\0') {
break;
}
}
}
void string_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 != path + strlen(path) - 1) {
start = end;
end = path + strlen(path);
}
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 string_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';
}

View File

@ -1,9 +0,0 @@
#pragma once
bool string_is_empty(const char* str);
void string_copy(char* dst, const char* src, size_t size);
void string_get_file_name(char* out, const char* file, u32 size);
void string_escape_file_name(char* out, const char* file, size_t size);
void string_get_path_file(char* out, const char* path, u32 size);
void string_get_parent_path(char* out, const char* path, u32 size);

View File

@ -1,152 +0,0 @@
#include <malloc.h>
#include <string.h>
#include <3ds.h>
#include "capturecam.h"
#include "task.h"
#include "../error.h"
#define EVENT_CANCEL 0
#define EVENT_RECV 1
#define EVENT_BUFFER_ERROR 2
#define EVENT_COUNT 3
static void task_capture_cam_thread(void* arg) {
capture_cam_data* data = (capture_cam_data*) arg;
Handle events[EVENT_COUNT] = {0};
events[EVENT_CANCEL] = data->cancelEvent;
Result res = 0;
u32 bufferSize = data->width * data->height * sizeof(u16);
u16* buffer = (u16*) calloc(1, bufferSize);
if(buffer != NULL) {
if(R_SUCCEEDED(res = camInit())) {
u32 cam = data->camera == CAMERA_OUTER ? SELECT_OUT1 : SELECT_IN1;
if(R_SUCCEEDED(res = CAMU_SetSize(cam, SIZE_CTR_TOP_LCD, CONTEXT_A))
&& R_SUCCEEDED(res = CAMU_SetOutputFormat(cam, OUTPUT_RGB_565, CONTEXT_A))
&& R_SUCCEEDED(res = CAMU_SetFrameRate(cam, FRAME_RATE_30))
&& R_SUCCEEDED(res = CAMU_SetNoiseFilter(cam, true))
&& R_SUCCEEDED(res = CAMU_SetAutoExposure(cam, true))
&& R_SUCCEEDED(res = CAMU_SetAutoWhiteBalance(cam, true))
&& R_SUCCEEDED(res = CAMU_Activate(cam))) {
u32 transferUnit = 0;
if(R_SUCCEEDED(res = CAMU_GetBufferErrorInterruptEvent(&events[EVENT_BUFFER_ERROR], PORT_CAM1))
&& R_SUCCEEDED(res = CAMU_SetTrimming(PORT_CAM1, true))
&& R_SUCCEEDED(res = CAMU_SetTrimmingParamsCenter(PORT_CAM1, data->width, data->height, 400, 240))
&& R_SUCCEEDED(res = CAMU_GetMaxBytes(&transferUnit, data->width, data->height))
&& R_SUCCEEDED(res = CAMU_SetTransferBytes(PORT_CAM1, transferUnit, data->width, data->height))
&& R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1))
&& R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], buffer, PORT_CAM1, bufferSize, (s16) transferUnit))
&& R_SUCCEEDED(res = CAMU_StartCapture(PORT_CAM1))) {
bool cancelRequested = false;
while(!task_is_quit_all() && !cancelRequested && R_SUCCEEDED(res)) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
s32 index = 0;
if(R_SUCCEEDED(res = svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, U64_MAX))) {
switch(index) {
case EVENT_CANCEL:
cancelRequested = true;
break;
case EVENT_RECV:
svcCloseHandle(events[EVENT_RECV]);
events[EVENT_RECV] = 0;
svcWaitSynchronization(data->mutex, U64_MAX);
memcpy(data->buffer, buffer, bufferSize);
GSPGPU_FlushDataCache(data->buffer, bufferSize);
svcReleaseMutex(data->mutex);
res = CAMU_SetReceiving(&events[EVENT_RECV], buffer, PORT_CAM1, bufferSize, (s16) transferUnit);
break;
case EVENT_BUFFER_ERROR:
svcCloseHandle(events[EVENT_RECV]);
events[EVENT_RECV] = 0;
if(R_SUCCEEDED(res = CAMU_ClearBuffer(PORT_CAM1))
&& R_SUCCEEDED(res = CAMU_SetReceiving(&events[EVENT_RECV], buffer, PORT_CAM1, bufferSize, (s16) transferUnit))) {
res = CAMU_StartCapture(PORT_CAM1);
}
break;
default:
break;
}
}
}
CAMU_StopCapture(PORT_CAM1);
bool busy = false;
while(R_SUCCEEDED(CAMU_IsBusy(&busy, PORT_CAM1)) && busy) {
svcSleepThread(1000000);
}
CAMU_ClearBuffer(PORT_CAM1);
}
CAMU_Activate(SELECT_NONE);
}
camExit();
}
free(buffer);
} else {
res = R_APP_OUT_OF_MEMORY;
}
for(int i = 0; i < EVENT_COUNT; i++) {
if(events[i] != 0) {
svcCloseHandle(events[i]);
events[i] = 0;
}
}
svcCloseHandle(data->mutex);
data->result = res;
data->finished = true;
}
Result task_capture_cam(capture_cam_data* data) {
if(data == NULL || data->buffer == NULL || data->width <= 0 || data->width > 640 || data->height <= 0 || data->height > 480) {
return R_APP_INVALID_ARGUMENT;
}
data->mutex = 0;
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY)) && R_SUCCEEDED(res = svcCreateMutex(&data->mutex, false))) {
if(threadCreate(task_capture_cam_thread, data, 0x10000, 0x1A, 0, true) == NULL) {
res = R_APP_THREAD_CREATE_FAILED;
}
}
if(R_FAILED(res)) {
data->finished = true;
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
if(data->mutex != 0) {
svcCloseHandle(data->mutex);
data->mutex = 0;
}
}
return res;
}

View File

@ -1,21 +0,0 @@
#pragma once
typedef enum capture_cam_camera_e {
CAMERA_OUTER,
CAMERA_INNER
} capture_cam_camera;
typedef struct capture_cam_data_s {
u16* buffer;
s16 width;
s16 height;
capture_cam_camera camera;
Handle mutex;
volatile bool finished;
Result result;
Handle cancelEvent;
} capture_cam_data;
Result task_capture_cam(capture_cam_data* data);

View File

@ -1,338 +0,0 @@
#include <malloc.h>
#include <string.h>
#include <3ds.h>
#include <jansson.h>
#include "dataop.h"
#include "../core.h"
static Result task_data_op_check_running(data_op_data* data) {
Result res = 0;
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
res = R_APP_CANCELLED;
} else {
bool suspended = svcWaitSynchronization(task_get_suspend_event(), 0) != 0;
if(suspended) {
if(data->suspend != NULL && R_SUCCEEDED(res)) {
res = data->suspend(data->data, data->processed);
}
}
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
if(suspended) {
if(data->restore != NULL && R_SUCCEEDED(res)) {
res = data->restore(data->data, data->processed);
}
}
}
return res;
}
static Result task_data_op_copy(data_op_data* data, u32 index) {
data->currProcessed = 0;
data->currTotal = 0;
data->bytesPerSecond = 0;
data->estimatedRemainingSeconds = 0;
Result res = 0;
bool isDir = false;
if(R_SUCCEEDED(res = data->isSrcDirectory(data->data, index, &isDir)) && isDir) {
res = data->makeDstDirectory(data->data, index);
} else {
u32 srcHandle = 0;
if(R_SUCCEEDED(res = data->openSrc(data->data, index, &srcHandle))) {
if(R_SUCCEEDED(res = data->getSrcSize(data->data, srcHandle, &data->currTotal))) {
if(data->currTotal == 0) {
if(data->copyEmpty) {
u32 dstHandle = 0;
if(R_SUCCEEDED(res = data->openDst(data->data, index, NULL, data->currTotal, &dstHandle))) {
res = data->closeDst(data->data, index, true, dstHandle);
}
} else {
res = R_APP_BAD_DATA;
}
} else {
u8* buffer = (u8*) calloc(1, data->bufferSize);
if(buffer != NULL) {
u32 dstHandle = 0;
u64 ioStartTime = 0;
u64 lastBytesPerSecondUpdate = osGetTime();
u32 bytesSinceUpdate = 0;
bool firstRun = true;
while(data->currProcessed < data->currTotal) {
if(R_FAILED(res = task_data_op_check_running(data))) {
break;
}
u32 bytesRead = 0;
if(R_FAILED(res = data->readSrc(data->data, srcHandle, &bytesRead, buffer, data->currProcessed, data->bufferSize))) {
break;
}
if(firstRun) {
firstRun = false;
if(R_FAILED(res = data->openDst(data->data, index, buffer, data->currTotal, &dstHandle))) {
break;
}
}
u32 bytesWritten = 0;
if(R_FAILED(res = data->writeDst(data->data, dstHandle, &bytesWritten, buffer, data->currProcessed, bytesRead))) {
break;
}
data->currProcessed += bytesWritten;
bytesSinceUpdate += bytesWritten;
u64 time = osGetTime();
u64 elapsed = time - lastBytesPerSecondUpdate;
if(elapsed >= 1000) {
data->bytesPerSecond = (u32) (bytesSinceUpdate / (elapsed / 1000.0f));
if(ioStartTime != 0) {
data->estimatedRemainingSeconds = (u32) ((data->currTotal - data->currProcessed) / (data->currProcessed / ((time - ioStartTime) / 1000.0f)));
} else {
data->estimatedRemainingSeconds = 0;
}
if(ioStartTime == 0 && data->currProcessed > 0) {
ioStartTime = time;
}
bytesSinceUpdate = 0;
lastBytesPerSecondUpdate = time;
}
}
if(dstHandle != 0) {
Result closeDstRes = data->closeDst(data->data, index, res == 0, dstHandle);
if(R_SUCCEEDED(res)) {
res = closeDstRes;
}
}
free(buffer);
} else {
res = R_APP_OUT_OF_MEMORY;
}
}
}
Result closeSrcRes = data->closeSrc(data->data, index, res == 0, srcHandle);
if(R_SUCCEEDED(res)) {
res = closeSrcRes;
}
}
}
return res;
}
typedef struct {
data_op_data* data;
u32 index;
u32 dstHandle;
bool firstRun;
u64 ioStartTime;
u64 lastBytesPerSecondUpdate;
u32 bytesSinceUpdate;
u64 writeOffset;
} data_op_download_data;
static Result task_data_op_download_callback(void* userData, void* buffer, size_t size) {
data_op_download_data* downloadData = (data_op_download_data*) userData;
data_op_data* data = downloadData->data;
if(downloadData->firstRun) {
downloadData->firstRun = false;
Result res = data->openDst(data->data, downloadData->index, buffer, data->currTotal, &downloadData->dstHandle);
if(R_FAILED(res)) {
return res;
}
}
u32 bytesWritten = 0;
Result res = data->writeDst(data->data, downloadData->dstHandle, &bytesWritten, buffer, downloadData->writeOffset, size);
downloadData->writeOffset += bytesWritten;
return res;
}
static Result task_data_op_download_check_running(void* userData) {
data_op_download_data* downloadData = (data_op_download_data*) userData;
return task_data_op_check_running(downloadData->data);
}
static Result task_data_op_download_progress(void* userData, u64 total, u64 curr) {
data_op_download_data* downloadData = (data_op_download_data*) userData;
data_op_data* data = downloadData->data;
downloadData->bytesSinceUpdate += curr - data->currProcessed;
data->currTotal = total;
data->currProcessed = curr;
u64 time = osGetTime();
u64 elapsed = time - downloadData->lastBytesPerSecondUpdate;
if(elapsed >= 1000) {
data->bytesPerSecond = (u32) (downloadData->bytesSinceUpdate / (elapsed / 1000.0f));
if(downloadData->ioStartTime != 0) {
data->estimatedRemainingSeconds = (u32) ((data->currTotal - data->currProcessed) / (data->currProcessed / ((time - downloadData->ioStartTime) / 1000.0f)));
} else {
data->estimatedRemainingSeconds = 0;
}
if(downloadData->ioStartTime == 0 && data->currProcessed > 0) {
downloadData->ioStartTime = time;
}
downloadData->bytesSinceUpdate = 0;
downloadData->lastBytesPerSecondUpdate = time;
}
return 0;
}
static Result task_data_op_download(data_op_data* data, u32 index) {
data->currProcessed = 0;
data->currTotal = 0;
data->bytesPerSecond = 0;
data->estimatedRemainingSeconds = 0;
Result res = 0;
char url[DOWNLOAD_URL_MAX];
if(R_SUCCEEDED(res = data->getSrcUrl(data->data, index, url, DOWNLOAD_URL_MAX))) {
data_op_download_data downloadData = {data, index, 0, true, 0, osGetTime(), 0, 0};
res = http_download_callback(url, data->bufferSize, &downloadData, task_data_op_download_callback, task_data_op_download_check_running, task_data_op_download_progress);
if(downloadData.dstHandle != 0) {
Result closeDstRes = data->closeDst(data->data, index, res == 0, downloadData.dstHandle);
if(R_SUCCEEDED(res)) {
res = closeDstRes;
}
}
}
return res;
}
static Result task_data_op_delete(data_op_data* data, u32 index) {
return data->delete(data->data, index);
}
static void task_data_op_retry_onresponse(ui_view* view, void* data, u32 response) {
((data_op_data*) data)->retryResponse = response == PROMPT_YES;
}
static void task_data_op_thread(void* arg) {
data_op_data* data = (data_op_data*) arg;
for(data->processed = 0; data->processed < data->total; data->processed++) {
Result res = 0;
if(R_SUCCEEDED(res = task_data_op_check_running(data))) {
switch(data->op) {
case DATAOP_COPY:
res = task_data_op_copy(data, data->processed);
break;
case DATAOP_DOWNLOAD:
res = task_data_op_download(data, data->processed);
break;
case DATAOP_DELETE:
res = task_data_op_delete(data, data->processed);
break;
default:
break;
}
}
data->result = res;
if(R_FAILED(res)) {
if(res == R_APP_CANCELLED) {
prompt_display_notify("失败", "已取消.", COLOR_TEXT, NULL, NULL, NULL);
break;
} else if(res != R_APP_SKIPPED) {
ui_view* errorView = NULL;
bool proceed = data->error(data->data, data->processed, res, &errorView);
if(errorView != NULL) {
svcWaitSynchronization(errorView->active, U64_MAX);
}
ui_view* retryView = prompt_display_yes_no("确认", "重试?", COLOR_TEXT, data, NULL, task_data_op_retry_onresponse);
if(retryView != NULL) {
svcWaitSynchronization(retryView->active, U64_MAX);
if(data->retryResponse) {
if(proceed) {
data->processed--;
} else {
data->processed = 0;
}
} else if(!proceed) {
break;
}
}
}
}
}
svcCloseHandle(data->cancelEvent);
data->finished = true;
aptSetSleepAllowed(true);
}
Result task_data_op(data_op_data* data) {
if(data == NULL) {
return R_APP_INVALID_ARGUMENT;
}
data->processed = 0;
data->currProcessed = 0;
data->currTotal = 0;
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) {
if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_APP_THREAD_CREATE_FAILED;
}
}
if(R_FAILED(res)) {
data->finished = true;
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
aptSetSleepAllowed(false);
return res;
}

View File

@ -1,71 +0,0 @@
#pragma once
typedef struct json_t json_t;
typedef struct ui_view_s ui_view;
#define DOWNLOAD_URL_MAX 1024
typedef enum data_op_e {
DATAOP_COPY,
DATAOP_DOWNLOAD,
DATAOP_DELETE
} data_op;
typedef struct data_op_data_s {
void* data;
data_op op;
u32 processed;
u32 total;
// Copy/Download
u64 currProcessed;
u64 currTotal;
u32 bytesPerSecond;
u32 estimatedRemainingSeconds;
u32 bufferSize;
Result (*openDst)(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle);
Result (*closeDst)(void* data, u32 index, bool succeeded, u32 handle);
Result (*writeDst)(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size);
// Copy
bool copyEmpty;
Result (*isSrcDirectory)(void* data, u32 index, bool* isDirectory);
Result (*makeDstDirectory)(void* data, u32 index);
Result (*openSrc)(void* data, u32 index, u32* handle);
Result (*closeSrc)(void* data, u32 index, bool succeeded, u32 handle);
Result (*getSrcSize)(void* data, u32 handle, u64* size);
Result (*readSrc)(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size);
// Download
Result (*getSrcUrl)(void* data, u32 index, char* url, size_t maxSize);
// Delete
Result (*delete)(void* data, u32 index);
// Suspend
Result (*suspend)(void* data, u32 index);
Result (*restore)(void* data, u32 index);
// Errors
bool (*error)(void* data, u32 index, Result res, ui_view** errorView);
// General
volatile bool finished;
Result result;
Handle cancelEvent;
// Internal
volatile bool retryResponse;
} data_op_data;
Result task_data_op(data_op_data* data);

View File

@ -1,79 +0,0 @@
#include <3ds.h>
#include "task.h"
#include "../error.h"
static bool task_quit;
static Handle task_pause_event;
static Handle task_suspend_event;
static aptHookCookie cookie;
static void task_apt_hook(APT_HookType hook, void* param) {
switch(hook) {
case APTHOOK_ONRESTORE:
svcSignalEvent(task_suspend_event);
case APTHOOK_ONWAKEUP:
svcSignalEvent(task_pause_event);
break;
case APTHOOK_ONSUSPEND:
svcClearEvent(task_suspend_event);
case APTHOOK_ONSLEEP:
svcClearEvent(task_pause_event);
break;
default:
break;
}
}
void task_init() {
task_quit = false;
Result res = 0;
if(R_FAILED(res = svcCreateEvent(&task_pause_event, RESET_STICKY))) {
error_panic("无法创建任务暂停事件: 0x%08lX", res);
return;
}
if(R_FAILED(res = svcCreateEvent(&task_suspend_event, RESET_STICKY))) {
svcCloseHandle(task_pause_event);
error_panic("无法创建任务挂起事件: 0x%08lX", res);
return;
}
svcSignalEvent(task_pause_event);
svcSignalEvent(task_suspend_event);
aptHook(&cookie, task_apt_hook, NULL);
}
void task_exit() {
task_quit = true;
aptUnhook(&cookie);
if(task_pause_event != 0) {
svcCloseHandle(task_pause_event);
task_pause_event = 0;
}
if(task_suspend_event != 0) {
svcCloseHandle(task_suspend_event);
task_suspend_event = 0;
}
}
bool task_is_quit_all() {
return task_quit;
}
Handle task_get_pause_event() {
return task_pause_event;
}
Handle task_get_suspend_event() {
return task_suspend_event;
}

View File

@ -1,10 +0,0 @@
#pragma once
void task_init();
void task_exit();
bool task_is_quit_all();
Handle task_get_pause_event();
Handle task_get_suspend_event();
#include "capturecam.h"
#include "dataop.h"

View File

@ -1,785 +0,0 @@
#include <malloc.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include <curl/curl.h>
#include "error.h"
#include "prompt.h"
#include "../error.h"
#include "../screen.h"
#include "../../fbi/resources.h"
static const char* level_to_string(Result res) {
switch(R_LEVEL(res)) {
case RL_SUCCESS:
return "Success";
case RL_INFO:
return "Info";
case RL_FATAL:
return "Fatal";
case RL_RESET:
return "Reset";
case RL_REINITIALIZE:
return "Reinitialize";
case RL_USAGE:
return "Usage";
case RL_PERMANENT:
return "Permanent";
case RL_TEMPORARY:
return "Temporary";
case RL_STATUS:
return "Status";
default:
return "<unknown>";
}
}
static const char* summary_to_string(Result res) {
switch(R_SUMMARY(res)) {
case RS_SUCCESS:
return "Success";
case RS_NOP:
return "Nop";
case RS_WOULDBLOCK:
return "Would block";
case RS_OUTOFRESOURCE:
return "Out of resource";
case RS_NOTFOUND:
return "Not found";
case RS_INVALIDSTATE:
return "Invalid state";
case RS_NOTSUPPORTED:
return "Not supported";
case RS_INVALIDARG:
return "Invalid argument";
case RS_WRONGARG:
return "Wrong argument";
case RS_CANCELED:
return "Canceled";
case RS_STATUSCHANGED:
return "Status changed";
case RS_INTERNAL:
return "Internal";
default:
return "<unknown>";
}
}
static const char* module_to_string(Result res) {
switch(R_MODULE(res)) {
case RM_COMMON:
return "Common";
case RM_KERNEL:
return "Kernel";
case RM_UTIL:
return "Util";
case RM_FILE_SERVER:
return "File server";
case RM_LOADER_SERVER:
return "Loader server";
case RM_TCB:
return "TCB";
case RM_OS:
return "OS";
case RM_DBG:
return "DBG";
case RM_DMNT:
return "DMNT";
case RM_PDN:
return "PDN";
case RM_GSP:
return "GSP";
case RM_I2C:
return "I2C";
case RM_GPIO:
return "GPIO";
case RM_DD:
return "DD";
case RM_CODEC:
return "CODEC";
case RM_SPI:
return "SPI";
case RM_PXI:
return "PXI";
case RM_FS:
return "FS";
case RM_DI:
return "DI";
case RM_HID:
return "HID";
case RM_CAM:
return "CAM";
case RM_PI:
return "PI";
case RM_PM:
return "PM";
case RM_PM_LOW:
return "PMLOW";
case RM_FSI:
return "FSI";
case RM_SRV:
return "SRV";
case RM_NDM:
return "NDM";
case RM_NWM:
return "NWM";
case RM_SOC:
return "SOC";
case RM_LDR:
return "LDR";
case RM_ACC:
return "ACC";
case RM_ROMFS:
return "RomFS";
case RM_AM:
return "AM";
case RM_HIO:
return "HIO";
case RM_UPDATER:
return "Updater";
case RM_MIC:
return "MIC";
case RM_FND:
return "FND";
case RM_MP:
return "MP";
case RM_MPWL:
return "MPWL";
case RM_AC:
return "AC";
case RM_HTTP:
return "HTTP";
case RM_DSP:
return "DSP";
case RM_SND:
return "SND";
case RM_DLP:
return "DLP";
case RM_HIO_LOW:
return "HIOLOW";
case RM_CSND:
return "CSND";
case RM_SSL:
return "SSL";
case RM_AM_LOW:
return "AMLOW";
case RM_NEX:
return "NEX";
case RM_FRIENDS:
return "Friends";
case RM_RDT:
return "RDT";
case RM_APPLET:
return "Applet";
case RM_NIM:
return "NIM";
case RM_PTM:
return "PTM";
case RM_MIDI:
return "MIDI";
case RM_MC:
return "MC";
case RM_SWC:
return "SWC";
case RM_FATFS:
return "FatFS";
case RM_NGC:
return "NGC";
case RM_CARD:
return "CARD";
case RM_CARDNOR:
return "CARDNOR";
case RM_SDMC:
return "SDMC";
case RM_BOSS:
return "BOSS";
case RM_DBM:
return "DBM";
case RM_CONFIG:
return "Config";
case RM_PS:
return "PS";
case RM_CEC:
return "CEC";
case RM_IR:
return "IR";
case RM_UDS:
return "UDS";
case RM_PL:
return "PL";
case RM_CUP:
return "CUP";
case RM_GYROSCOPE:
return "Gyroscope";
case RM_MCU:
return "MCU";
case RM_NS:
return "NS";
case RM_NEWS:
return "NEWS";
case RM_RO:
return "RO";
case RM_GD:
return "GD";
case RM_CARD_SPI:
return "CARDSPI";
case RM_EC:
return "EC";
case RM_WEB_BROWSER:
return "Web browser";
case RM_TEST:
return "TEST";
case RM_ENC:
return "ENC";
case RM_PIA:
return "PIA";
case RM_ACT:
return "ACT";
case RM_VCTL:
return "VCTL";
case RM_OLV:
return "OLV";
case RM_NEIA:
return "NEIA";
case RM_NPNS:
return "NPNS";
case RM_AVD:
return "AVD";
case RM_L2B:
return "L2B";
case RM_MVD:
return "MVD";
case RM_NFC:
return "NFC";
case RM_UART:
return "UART";
case RM_SPM:
return "SPM";
case RM_QTM:
return "QTM";
case RM_NFP:
return "NFP";
case RM_APPLICATION:
return "Application";
default:
return "<unknown>";
}
}
static const char* description_to_string(Result res) {
int module = R_MODULE(res);
int description = R_DESCRIPTION(res);
switch(module) {
case RM_KERNEL:
switch(description) {
case 2:
return "Invalid DMA buffer memory permissions";
default:
break;
}
break;
case RM_OS:
switch(description) {
case 1:
return "Out of synchronization object";
case 2:
return "Out of shared memory objects";
case 9:
return "Out of session objects";
case 10:
return "Not enough memory for allocation";
case 20:
return "Wrong permissions for unprivileged access";
case 26:
return "Session closed by remote process";
case 47:
return "Invalid command header";
case 52:
return "Max port connections exceeded";
default:
break;
}
break;
case RM_FS:
switch(description) {
case 101:
return "Archive not mounted";
case 120:
return "Doesn't exist / Failed to open";
case 141:
return "Game card not inserted";
case 171:
return "Bus: Busy / Underrun";
case 172:
return "Bus: Illegal function";
case 190:
return "Already exists / Failed to create";
case 210:
return "Partition full";
case 230:
return "Illegal operation / File in use";
case 231:
return "Resource locked";
case 250:
return "FAT operation denied";
case 265:
return "Bus: Timeout";
case 331:
return "Bus error / TWL partition invalid";
case 332:
return "Bus: Stop bit error";
case 391:
return "Hash verification failure";
case 392:
return "RSA/Hash verification failure";
case 395:
return "Invalid RomFS or save data block hash";
case 630:
return "Archive permission denied";
case 702:
return "Invalid path / Inaccessible archive";
case 705:
return "Offset out of bounds";
case 721:
return "Reached file size limit";
case 760:
return "Unsupported operation";
case 761:
return "ExeFS read size mismatch";
default:
break;
}
break;
case RM_SRV:
switch(description) {
case 5:
return "Invalid service name length";
case 6:
return "Service access denied";
case 7:
return "String size mismatch";
default:
break;
}
break;
case RM_AM:
switch(description) {
case 4:
return "Wrong installation state";
case 37:
return "Invalid NCCH";
case 39:
return "Invalid or outdated title version";
case 41:
return "Error type 1";
case 43:
return "Database does not exist";
case 44:
return "Attempted to delete system title";
case 101:
return "Error type -1";
case 102:
return "Error type -2";
case 103:
return "Error type -3";
case 104:
return "Error type -4";
case 105:
return "Error type -5";
case 106:
return "Cert signature or hash check failed";
case 107:
return "Error type -7";
case 108:
return "Error type -8";
case 109:
return "Error type -9";
case 110:
return "Error type -10";
case 111:
return "Error type -11";
case 112:
return "Error type -12";
case 113:
return "Error type -13";
case 114:
return "Error type -14";
case 393:
return "Invalid database";
default:
break;
}
break;
case RM_HTTP:
switch(description) {
case 60:
return "Failed to verify TLS certificate";
case 70:
return "Network unavailable";
case 102:
return "Wrong context handle";
case 105:
return "Request timed out";
default:
break;
}
break;
case RM_SSL:
switch(description) {
case 20:
return "Untrusted RootCA";
case 54:
return "RootCertChain handle not found";
default:
break;
}
break;
case RM_SDMC:
switch(description) {
case 1:
return "Bus: Bit23 error";
case 2:
return "Bus: RX ready error";
case 3:
return "Bus: Bit28 error";
case 4:
return "Bus: Bit27 error";
default:
break;
}
break;
case RM_MVD:
switch(description) {
case 271:
return "Invalid configuration";
default:
break;
}
break;
case RM_NFC:
switch(description) {
case 512:
return "Invalid NFC state";
default:
break;
}
break;
case RM_QTM:
switch(description) {
case 8:
return "Camera busy";
default:
break;
}
break;
case RM_APPLICATION:
switch(res) {
case R_APP_INVALID_ARGUMENT:
return "Invalid argument";
case R_APP_CANCELLED:
return "Operation cancelled";
case R_APP_SKIPPED:
return "Operation skipped";
case R_APP_THREAD_CREATE_FAILED:
return "Thread creation failed";
case R_APP_PARSE_FAILED:
return "Parse failed";
case R_APP_BAD_DATA:
return "Bad data";
case R_APP_HTTP_TOO_MANY_REDIRECTS:
return "Too many redirects";
default:
if(res >= R_APP_HTTP_ERROR_BASE && res < R_APP_HTTP_ERROR_END) {
switch(res - R_APP_HTTP_ERROR_BASE) {
case 100:
return "HTTP 100: Continue";
case 101:
return "HTTP 101: Switching Protocols";
case 102:
return "HTTP 102: Processing";
case 103:
return "HTTP 103: Early Hints";
case 200:
return "HTTP 200: OK";
case 201:
return "HTTP 201: Created";
case 202:
return "HTTP 202: Accepted";
case 203:
return "HTTP 203: Non-Authoritative Information";
case 204:
return "HTTP 204: No Content";
case 205:
return "HTTP 205: Reset Content";
case 206:
return "HTTP 206: Partial Content";
case 207:
return "HTTP 207: Multi-Status";
case 208:
return "HTTP 208: Already Reported";
case 226:
return "HTTP 226: IM Used";
case 300:
return "HTTP 300: Multiple Choices";
case 301:
return "HTTP 301: Moved Permanently";
case 302:
return "HTTP 302: Found";
case 303:
return "HTTP 303: See Other";
case 304:
return "HTTP 304: Not Modified";
case 305:
return "HTTP 305: Use Proxy";
case 306:
return "HTTP 306: Switch Proxy";
case 307:
return "HTTP 307: Temporary Redirect";
case 308:
return "HTTP 308: Permanent Redirect";
case 400:
return "HTTP 400: Bad Request";
case 401:
return "HTTP 401: Unauthorized";
case 402:
return "HTTP 402: Payment Required";
case 403:
return "HTTP 403: Forbidden";
case 404:
return "HTTP 404: Not Found";
case 405:
return "HTTP 405: Method Not Allowed";
case 406:
return "HTTP 406: Not Acceptable";
case 407:
return "HTTP 407: Proxy Authentication Required";
case 408:
return "HTTP 408: Request Timeout";
case 409:
return "HTTP 409: Conflict";
case 410:
return "HTTP 410: Gone";
case 411:
return "HTTP 411: Length Required";
case 412:
return "HTTP 412: Precondition Failed";
case 413:
return "HTTP 413: Payload Too Large";
case 414:
return "HTTP 414: URI Too Long";
case 415:
return "HTTP 415: Unsupported Media Type";
case 416:
return "HTTP 416: Range Not Satisfiable";
case 417:
return "HTTP 417: Expectation Failed";
case 418:
return "HTTP 418: I'm a teapot";
case 421:
return "HTTP 421: Misdirected Request";
case 422:
return "HTTP 422: Unprocessable Entity";
case 423:
return "HTTP 423: Locked";
case 424:
return "HTTP 424: Failed Dependency";
case 426:
return "HTTP 426: Upgrade Required";
case 428:
return "HTTP 428: Precondition Required";
case 429:
return "HTTP 429: Too Many Requests";
case 431:
return "HTTP 431: Request Header Fields Too Large";
case 451:
return "HTTP 451: Unavailable For Legal Reasons";
case 500:
return "HTTP 500: Internal Server Error";
case 501:
return "HTTP 501: Not Implemented";
case 502:
return "HTTP 502: Bad Gateway";
case 503:
return "HTTP 503: Service Unavailable";
case 504:
return "HTTP 504: Gateway Timeout";
case 505:
return "HTTP 505: HTTP Version Not Specified";
case 506:
return "HTTP 506: Variant Also Negotiates";
case 507:
return "HTTP 507: Insufficient Storage";
case 508:
return "HTTP 508: Loop Detected";
case 510:
return "HTTP 510: Not Extended";
case 511:
return "HTTP 511: Network Authentication Required";
default:
return "HTTP: Unknown Response Code";
}
} else if(res >= R_APP_CURL_ERROR_BASE && res < R_APP_CURL_ERROR_END) {
return curl_easy_strerror(res - R_APP_CURL_ERROR_BASE);
}
break;
}
default:
break;
}
switch(description) {
case RD_SUCCESS:
return "Success";
case RD_TIMEOUT:
return "Timeout";
case RD_OUT_OF_RANGE:
return "Out of range";
case RD_ALREADY_EXISTS:
return "Already exists";
case RD_CANCEL_REQUESTED:
return "Cancel requested";
case RD_NOT_FOUND:
return "Not found";
case RD_ALREADY_INITIALIZED:
return "Already initialized";
case RD_NOT_INITIALIZED:
return "Not initialized";
case RD_INVALID_HANDLE:
return "Invalid handle";
case RD_INVALID_POINTER:
return "Invalid pointer";
case RD_INVALID_ADDRESS:
return "Invalid address";
case RD_NOT_IMPLEMENTED:
return "Not implemented";
case RD_OUT_OF_MEMORY:
return "Out of memory";
case RD_MISALIGNED_SIZE:
return "Misaligned size";
case RD_MISALIGNED_ADDRESS:
return "Misaligned address";
case RD_BUSY:
return "Busy";
case RD_NO_DATA:
return "No data";
case RD_INVALID_COMBINATION:
return "Invalid combination";
case RD_INVALID_ENUM_VALUE:
return "Invalid enum value";
case RD_INVALID_SIZE:
return "Invalid size";
case RD_ALREADY_DONE:
return "Already done";
case RD_NOT_AUTHORIZED:
return "Not authorized";
case RD_TOO_LARGE:
return "Too large";
case RD_INVALID_SELECTION:
return "Invalid selection";
default:
return "<unknown>";
}
}
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, u32 response) {
free(data);
}
ui_view* error_display(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...) {
error_data* errorData = (error_data*) calloc(1, sizeof(error_data));
if(errorData == NULL) {
// No use trying to spawn another if we're out of memory.
return NULL;
}
errorData->data = data;
errorData->drawTop = drawTop;
va_list list;
va_start(list, text);
vsnprintf(errorData->fullText, 4096, text, list);
va_end(list);
return prompt_display_notify("错误", errorData->fullText, COLOR_TEXT, errorData, error_draw_top, error_onresponse);
}
ui_view* 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));
if(errorData == NULL) {
// No use trying to spawn another if we're out of memory.
return NULL;
}
errorData->data = data;
errorData->drawTop = drawTop;
char textBuf[1024];
va_list list;
va_start(list, text);
vsnprintf(textBuf, 1024, text, list);
va_end(list);
int level = R_LEVEL(result);
int summary = R_SUMMARY(result);
int module = R_MODULE(result);
int description = R_DESCRIPTION(result);
snprintf(errorData->fullText, 4096, "%s\n错误代码: 0x%08lX\n等级: %s (%d)\n摘要: %s (%d)\n模组: %s (%d)\n描述: %s (%d)", textBuf, result, level_to_string(result), level, summary_to_string(result), summary, module_to_string(result), module, description_to_string(result), description);
return prompt_display_notify("错误", errorData->fullText, COLOR_TEXT, errorData, error_draw_top, error_onresponse);
}
ui_view* 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));
if(errorData == NULL) {
// No use trying to spawn another if we're out of memory.
return NULL;
}
errorData->data = data;
errorData->drawTop = drawTop;
char textBuf[1024];
va_list list;
va_start(list, text);
vsnprintf(textBuf, 1024, text, list);
va_end(list);
if(err < 0) {
err = -err;
}
snprintf(errorData->fullText, 4096, "%s\nI/O 错误: %s (%d)", textBuf, strerror(err), err);
return prompt_display_notify("错误", errorData->fullText, COLOR_TEXT, errorData, error_draw_top, error_onresponse);
}

View File

@ -1,7 +0,0 @@
#pragma once
typedef struct ui_view_s ui_view;
ui_view* error_display(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...);
ui_view* 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, ...);
ui_view* 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, ...);

View File

@ -1,104 +0,0 @@
#include <malloc.h>
#include <stdio.h>
#include <3ds.h>
#include "error.h"
#include "info.h"
#include "ui.h"
#include "../screen.h"
#include "../../fbi/resources.h"
typedef struct {
bool bar;
void* data;
float progress;
char text[PROGRESS_TEXT_MAX];
void (*update)(ui_view* view, void* data, float* progress, char* text);
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2);
} info_data;
static void info_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
info_data* infoData = (info_data*) data;
if(infoData->update != NULL) {
infoData->update(view, infoData->data, &infoData->progress, infoData->text);
}
}
static void info_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
info_data* infoData = (info_data*) data;
if(infoData->drawTop != NULL) {
infoData->drawTop(view, infoData->data, x1, y1, x2, y2);
}
}
static void info_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
info_data* infoData = (info_data*) data;
float textWidth;
float textHeight;
screen_get_string_size(&textWidth, &textHeight, infoData->text, 0.5f, 0.5f);
float textX = x1 + (x2 - x1 - textWidth) / 2;
float textY = y1 + (y2 - y1 - textHeight) / 2;
if(infoData->bar) {
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 * infoData->progress, progressBarContentHeight);
textX = x1 + (x2 - x1 - textWidth) / 2;
textY = progressBarBgY + progressBarBgHeight + 10;
}
screen_draw_string(infoData->text, textX, textY, 0.5f, 0.5f, COLOR_TEXT, true);
}
ui_view* info_display(const char* name, const char* info, bool bar, void* data, void (*update)(ui_view* view, void* data, float* progress, char* text),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2)) {
info_data* infoData = (info_data*) calloc(1, sizeof(info_data));
if(infoData == NULL) {
error_display(NULL, NULL, "无法分配信息的数据.");
return NULL;
}
infoData->bar = bar;
infoData->data = data;
infoData->progress = 0;
snprintf(infoData->text, PROGRESS_TEXT_MAX, "请稍等...");
infoData->update = update;
infoData->drawTop = drawTop;
ui_view* view = ui_create();
view->name = name;
view->info = info;
view->data = infoData;
view->update = info_update;
view->drawTop = info_draw_top;
view->drawBottom = info_draw_bottom;
ui_push(view);
return view;
}
void info_destroy(ui_view* view) {
if(view != NULL) {
free(view->data);
ui_destroy(view);
}
}

View File

@ -1,9 +0,0 @@
#pragma once
#define PROGRESS_TEXT_MAX 512
typedef struct ui_view_s ui_view;
ui_view* info_display(const char* name, const char* info, bool bar, void* data, void (*update)(ui_view* view, void* data, float* progress, char* text),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2));
void info_destroy(ui_view* view);

View File

@ -1,81 +0,0 @@
#include <malloc.h>
#include <3ds.h>
#include "error.h"
#include "kbd.h"
#include "ui.h"
typedef struct {
const char* hint;
const char* initialText;
SwkbdType type;
u32 features;
SwkbdValidInput validation;
u32 maxSize;
char* response;
void* data;
void (*onResponse)(ui_view* view, void* data, SwkbdButton button, const char* response);
} kbd_data;
static void kbd_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
kbd_data* kbdData = (kbd_data*) data;
SwkbdState swkbd;
swkbdInit(&swkbd, kbdData->type, 2, kbdData->maxSize < 65000 ? (int) kbdData->maxSize : 65000);
swkbdSetHintText(&swkbd, kbdData->hint);
swkbdSetInitialText(&swkbd, kbdData->initialText);
swkbdSetFeatures(&swkbd, kbdData->features);
swkbdSetValidation(&swkbd, kbdData->validation, 0, 0);
SwkbdButton button = swkbdInputText(&swkbd, kbdData->response, kbdData->maxSize);
ui_pop();
if(kbdData->onResponse != NULL) {
kbdData->onResponse(view, kbdData->data, button, kbdData->response);
}
free(kbdData->response);
free(kbdData);
ui_destroy(view);
}
ui_view* kbd_display(const char* hint, const char* initialText, SwkbdType type, u32 features, SwkbdValidInput validation, u32 maxSize, void* data, void (*onResponse)(ui_view* view, void* data, SwkbdButton button, const char* response)) {
kbd_data* kbdData = (kbd_data*) calloc(1, sizeof(kbd_data));
if(kbdData == NULL) {
error_display(NULL, NULL, "无法分配键盘的数据.");
return NULL;
}
kbdData->hint = hint;
kbdData->initialText = initialText;
kbdData->type = type;
kbdData->features = features;
kbdData->validation = validation;
kbdData->maxSize = maxSize;
kbdData->response = (char*) calloc(1, maxSize);
if(kbdData->response == NULL) {
error_display(NULL, NULL, "无法分配键盘响应的缓存.");
free(kbdData);
return NULL;
}
kbdData->data = data;
kbdData->onResponse = onResponse;
ui_view* view = ui_create();
view->name = "";
view->info = "";
view->data = kbdData;
view->update = kbd_update;
view->drawTop = NULL;
view->drawBottom = NULL;
ui_push(view);
return view;
}

View File

@ -1,5 +0,0 @@
#pragma once
typedef struct ui_view_s ui_view;
ui_view* kbd_display(const char* hint, const char* initialText, SwkbdType type, u32 features, SwkbdValidInput validation, u32 maxSize, void* data, void (*onResponse)(ui_view* view, void* data, SwkbdButton button, const char* response));

View File

@ -1,306 +0,0 @@
#include <malloc.h>
#include <3ds.h>
#include "error.h"
#include "list.h"
#include "ui.h"
#include "../screen.h"
#include "../linkedlist.h"
#include "../../fbi/resources.h"
typedef struct {
void* data;
linked_list items;
u32 selectedIndex;
list_item* selectedItem;
u32 selectionScroll;
u64 nextSelectionScrollResetTime;
float scrollPos;
u32 lastScrollTouchY;
u64 nextActionTime;
void (*update)(ui_view* view, void* data, linked_list* items, 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 void list_validate(list_data* listData, float by1, float by2) {
u32 size = linked_list_size(&listData->items);
if(size == 0 || listData->selectedIndex < 0) {
listData->selectedIndex = 0;
listData->selectedItem = NULL;
listData->scrollPos = 0;
}
if(size > 0) {
if(listData->selectedIndex > size - 1) {
listData->selectedIndex = size - 1;
listData->selectedItem = NULL;
listData->scrollPos = 0;
}
float viewSize = by2 - by1;
float fontHeight = screen_get_font_height(0.5f);
bool found = false;
if(listData->selectedItem != NULL) {
u32 oldIndex = listData->selectedIndex;
int index = linked_list_index_of(&listData->items, listData->selectedItem);
if(index != -1) {
found = true;
listData->selectedIndex = (u32) index;
if(listData->selectedIndex != oldIndex) {
listData->scrollPos += (listData->selectedIndex * fontHeight) - (oldIndex * fontHeight);
}
}
}
if(!found) {
listData->selectedItem = linked_list_get(&listData->items, listData->selectedIndex);
listData->selectionScroll = 0;
listData->nextSelectionScrollResetTime = 0;
}
float itemY = listData->selectedIndex * fontHeight;
float minItemScrollPos = itemY - (viewSize - fontHeight);
if(listData->scrollPos < minItemScrollPos) {
listData->scrollPos = minItemScrollPos;
}
if(listData->scrollPos > itemY) {
listData->scrollPos = itemY;
}
float maxScrollPos = (size * fontHeight) - viewSize;
if(listData->scrollPos > maxScrollPos) {
listData->scrollPos = maxScrollPos;
}
if(listData->scrollPos < 0) {
listData->scrollPos = 0;
}
}
}
static void list_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
list_data* listData = (list_data*) data;
u32 size = linked_list_size(&listData->items);
list_validate(listData, by1, by2);
bool selectedTouched = false;
if(size > 0) {
bool scrolls = false;
if(listData->selectedItem != NULL) {
float itemWidth;
screen_get_string_size(&itemWidth, NULL, listData->selectedItem->name, 0.5f, 0.5f);
if(itemWidth > bx2 - bx1) {
scrolls = true;
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++;
}
}
}
if(!scrolls) {
listData->selectionScroll = 0;
listData->nextSelectionScrollResetTime = 0;
}
u32 lastSelectedIndex = listData->selectedIndex;
if((hidKeysDown() & KEY_DOWN) || ((hidKeysHeld() & KEY_DOWN) && osGetTime() >= listData->nextActionTime)) {
if(listData->selectedIndex < size - 1) {
listData->selectedIndex++;
} else {
listData->selectedIndex = 0;
}
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_DOWN) ? 500 : 100);
}
if((hidKeysDown() & KEY_UP) || ((hidKeysHeld() & KEY_UP) && osGetTime() >= listData->nextActionTime)) {
if(listData->selectedIndex > 0) {
listData->selectedIndex--;
} else {
listData->selectedIndex = size - 1;
}
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_UP) ? 500 : 100);
}
if((hidKeysDown() & KEY_RIGHT) || ((hidKeysHeld() & KEY_RIGHT) && osGetTime() >= listData->nextActionTime)) {
if(listData->selectedIndex < size - 1) {
u32 remaining = size - 1 - listData->selectedIndex;
listData->selectedIndex += remaining < 13 ? remaining : 13;
} else {
listData->selectedIndex = 0;
}
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_RIGHT) ? 500 : 100);
}
if((hidKeysDown() & KEY_LEFT) || ((hidKeysHeld() & KEY_LEFT) && osGetTime() >= listData->nextActionTime)) {
if(listData->selectedIndex > 0) {
u32 remaining = listData->selectedIndex;
listData->selectedIndex -= remaining < 13 ? remaining : 13;
} else {
listData->selectedIndex = size - 1;
}
listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_LEFT) ? 500 : 100);
}
if((hidKeysDown() | hidKeysHeld()) & KEY_TOUCH) {
touchPosition pos;
hidTouchRead(&pos);
if(hidKeysDown() & KEY_TOUCH) {
u32 index = (u32) ((listData->scrollPos + (pos.py - by1)) / screen_get_font_height(0.5f));
if(index >= 0) {
if(listData->selectedIndex == index) {
selectedTouched = true;
} else {
listData->selectedIndex = (u32) index;
}
}
} else if(hidKeysHeld() & KEY_TOUCH) {
listData->scrollPos += -((int) pos.py - (int) listData->lastScrollTouchY);
}
listData->lastScrollTouchY = pos.py;
}
if(listData->selectedIndex != lastSelectedIndex) {
listData->selectedItem = linked_list_get(&listData->items, listData->selectedIndex);
listData->selectionScroll = 0;
listData->nextSelectionScrollResetTime = 0;
}
list_validate(listData, by1, by2);
}
if(listData->update != NULL) {
listData->update(view, listData->data, &listData->items, listData->selectedItem, selectedTouched);
}
}
static void list_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
list_data* listData = (list_data*) data;
if(listData->drawTop != NULL) {
list_validate(listData, y1, y2);
listData->drawTop(view, listData->data, x1, y1, x2, y2, listData->selectedItem);
}
}
static void list_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
list_data* listData = (list_data*) data;
list_validate(listData, y1, y2);
float fontHeight = screen_get_font_height(0.5f);
float y = y1 - listData->scrollPos;
linked_list_iter iter;
linked_list_iterate(&listData->items, &iter);
while(linked_list_iter_has_next(&iter)) {
if(y > y2) {
break;
}
list_item* item = linked_list_iter_next(&iter);
if(y > y1 - fontHeight) {
float x = x1 + 2;
if(item == listData->selectedItem) {
x -= listData->selectionScroll;
}
screen_draw_string(item->name, x, y, 0.5f, 0.5f, item->color, true);
if(item == listData->selectedItem) {
u32 selectionOverlayWidth = 0;
screen_get_texture_size(&selectionOverlayWidth, NULL, TEXTURE_SELECTION_OVERLAY);
screen_draw_texture(TEXTURE_SELECTION_OVERLAY, (x1 + x2 - selectionOverlayWidth) / 2, y, selectionOverlayWidth, fontHeight);
}
}
y += fontHeight;
}
u32 size = linked_list_size(&listData->items);
if(size > 0) {
float totalHeight = size * fontHeight;
float viewHeight = y2 - y1;
if(totalHeight > viewHeight) {
u32 scrollBarWidth = 0;
screen_get_texture_size(&scrollBarWidth, NULL, TEXTURE_SCROLL_BAR);
float scrollBarHeight = (viewHeight / totalHeight) * viewHeight;
float scrollBarX = x2 - scrollBarWidth;
float scrollBarY = y1 + (listData->scrollPos / totalHeight) * viewHeight;
screen_draw_texture(TEXTURE_SCROLL_BAR, scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
}
}
}
ui_view* list_display(const char* name, const char* info, void* data, void (*update)(ui_view* view, void* data, linked_list* items, 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));
if(listData == NULL) {
error_display(NULL, NULL, "无法分配列表的数据.");
return NULL;
}
listData->data = data;
linked_list_init(&listData->items);
listData->selectedIndex = 0;
listData->selectedItem = NULL;
listData->selectionScroll = 0;
listData->nextSelectionScrollResetTime = 0;
listData->scrollPos = 0;
listData->lastScrollTouchY = 0;
listData->update = update;
listData->drawTop = drawTop;
ui_view* view = ui_create();
view->name = name;
view->info = info;
view->data = listData;
view->update = list_update;
view->drawTop = list_draw_top;
view->drawBottom = list_draw_bottom;
ui_push(view);
return view;
}
void list_destroy(ui_view* view) {
if(view != NULL) {
linked_list_destroy(&((list_data*) view->data)->items);
free(view->data);
ui_destroy(view);
}
}

View File

@ -1,16 +0,0 @@
#pragma once
#define LIST_ITEM_NAME_MAX 512
typedef struct linked_list_s linked_list;
typedef struct ui_view_s ui_view;
typedef struct list_item_s {
char name[LIST_ITEM_NAME_MAX];
u32 color;
void* data;
} list_item;
ui_view* list_display(const char* name, const char* info, void* data, void (*update)(ui_view* view, void* data, linked_list* items, 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);

View File

@ -1,203 +0,0 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "error.h"
#include "prompt.h"
#include "ui.h"
#include "../screen.h"
#include "../stringutil.h"
#include "../../fbi/resources.h"
typedef struct {
const char* text;
u32 color;
char options[PROMPT_OPTIONS_MAX][PROMPT_OPTION_TEXT_MAX];
u32 optionButtons[PROMPT_OPTIONS_MAX];
u32 numOptions;
void* data;
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2);
void (*onResponse)(ui_view* view, void* data, u32 response);
} prompt_data;
static void prompt_notify_response(ui_view* view, prompt_data* promptData, u32 response) {
ui_pop();
if(promptData->onResponse != NULL) {
promptData->onResponse(view, promptData->data, response);
}
free(promptData);
ui_destroy(view);
}
static void prompt_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
prompt_data* promptData = (prompt_data*) data;
u32 down = hidKeysDown();
for(u32 i = 0; i < promptData->numOptions; i++) {
if(down & (promptData->optionButtons[i] & ~KEY_TOUCH)) {
prompt_notify_response(view, promptData, i);
return;
}
}
if(hidKeysDown() & KEY_TOUCH) {
touchPosition pos;
hidTouchRead(&pos);
float buttonWidth = (bx2 - bx1 - (10 * (promptData->numOptions + 1))) / promptData->numOptions;
u32 buttonHeight;
screen_get_texture_size(NULL, &buttonHeight, TEXTURE_BUTTON);
for(u32 i = 0; i < promptData->numOptions; i++) {
float buttonX = bx1 + 10 + (buttonWidth + 10) * i;
float buttonY = by2 - 5 - buttonHeight;
if(pos.px >= buttonX && pos.py >= buttonY && pos.px < buttonX + buttonWidth && pos.py < buttonY + buttonHeight) {
prompt_notify_response(view, promptData, i);
return;
}
}
}
}
static void prompt_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
prompt_data* promptData = (prompt_data*) data;
if(promptData->drawTop != NULL) {
promptData->drawTop(view, promptData->data, x1, y1, x2, y2);
}
}
static const char* button_strings[32] = {
"A",
"B",
"SELECT",
"START",
"方向键 右",
"方向键 左",
"方向键 上",
"方向键 下",
"R",
"L",
"X",
"Y",
"",
"",
"ZL",
"ZR",
"",
"",
"",
"",
"",
"",
"",
"",
"C 摇杆 右",
"C 摇杆 左",
"C 摇杆 上",
"C 摇杆 下",
"摇杆 右",
"摇杆 左",
"摇杆 上",
"摇杆 下"
};
static void prompt_button_to_string(char* out, size_t size, u32 button) {
if(button == PROMPT_BUTTON_ANY) {
snprintf(out, size, "任意键");
return;
}
size_t pos = 0;
for(u8 bit = 0; bit < 32 && pos < size; bit++) {
if(button & (1 << bit)) {
if(pos > 0) {
pos += snprintf(out + pos, size - pos, "/");
}
pos += snprintf(out + pos, size - pos, button_strings[bit]);
}
}
}
static void prompt_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
prompt_data* promptData = (prompt_data*) data;
float buttonWidth = (x2 - x1 - (10 * (promptData->numOptions + 1))) / promptData->numOptions;
u32 buttonHeight;
screen_get_texture_size(NULL, &buttonHeight, TEXTURE_BUTTON);
char button[64];
char text[PROMPT_OPTION_TEXT_MAX + 65];
for(u32 i = 0; i < promptData->numOptions; i++) {
float buttonX = x1 + 10 + (buttonWidth + 10) * i;
float buttonY = y2 - 5 - buttonHeight;
screen_draw_texture(TEXTURE_BUTTON, buttonX, buttonY, buttonWidth, buttonHeight);
prompt_button_to_string(button, 64, promptData->optionButtons[i]);
snprintf(text, sizeof(text), "%s\n(%s)", promptData->options[i], button);
float textWidth;
float textHeight;
screen_get_string_size(&textWidth, &textHeight, text, 0.5f, 0.5f);
screen_draw_string(text, buttonX + (buttonWidth - textWidth) / 2, buttonY + (buttonHeight - textHeight) / 2, 0.5f, 0.5f, promptData->color, true);
}
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->color, true);
}
ui_view* prompt_display_multi_choice(const char* name, const char* text, u32 color, const char** options, u32* optionButtons, u32 numOptions, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, u32 response)) {
prompt_data* promptData = (prompt_data*) calloc(1, sizeof(prompt_data));
if(promptData == NULL) {
error_display(NULL, NULL, "无法分配提示的数据.");
return NULL;
}
promptData->text = text;
promptData->color = color;
for(u32 i = 0; i < numOptions && i < PROMPT_OPTIONS_MAX; i++) {
string_copy(promptData->options[i], options[i], PROMPT_OPTION_TEXT_MAX);
promptData->optionButtons[i] = optionButtons[i];
}
promptData->numOptions = numOptions;
promptData->data = data;
promptData->drawTop = drawTop;
promptData->onResponse = onResponse;
ui_view* view = ui_create();
view->name = name;
view->info = "";
view->data = promptData;
view->update = prompt_update;
view->drawTop = prompt_draw_top;
view->drawBottom = prompt_draw_bottom;
ui_push(view);
return view;
}
ui_view* prompt_display_notify(const char* name, const char* text, u32 color, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, u32 response)) {
static const char* options[1] = {""};
static u32 optionButtons[1] = {PROMPT_BUTTON_ANY};
return prompt_display_multi_choice(name, text, color, options, optionButtons, 1, data, drawTop, onResponse);
}
ui_view* prompt_display_yes_no(const char* name, const char* text, u32 color, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, u32 response)) {
static const char* options[2] = {"", ""};
static u32 optionButtons[2] = {KEY_A, KEY_B};
return prompt_display_multi_choice(name, text, color, options, optionButtons, 2, data, drawTop, onResponse);
}

View File

@ -1,20 +0,0 @@
#pragma once
typedef struct ui_view_s ui_view;
#define PROMPT_OPTIONS_MAX 4
#define PROMPT_OPTION_TEXT_MAX 64
#define PROMPT_BUTTON_ANY 0xFFFFFFFF
#define PROMPT_OK 0
#define PROMPT_YES 0
#define PROMPT_NO 1
ui_view* prompt_display_multi_choice(const char* name, const char* text, u32 color, const char** options, u32* optionButtons, u32 numOptions, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, u32 response));
ui_view* prompt_display_notify(const char* name, const char* text, u32 color, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, u32 response));
ui_view* prompt_display_yes_no(const char* name, const char* text, u32 color, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
void (*onResponse)(ui_view* view, void* data, u32 response));

View File

@ -1,435 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <3ds.h>
#include <malloc.h>
#include "ui.h"
#include "../error.h"
#include "../screen.h"
#include "../data/smdh.h"
#include "../../fbi/resources.h"
#define MAX_UI_VIEWS 16
static ui_view* ui_stack[MAX_UI_VIEWS];
static int ui_stack_top = -1;
static Handle ui_stack_mutex = 0;
static u64 ui_free_space_last_update = 0;
static char ui_free_space_buffer[128];
static u64 ui_fade_begin_time = 0;
static u8 ui_fade_alpha = 0;
void ui_init() {
if(ui_stack_mutex == 0) {
svcCreateMutex(&ui_stack_mutex, false);
}
ui_fade_begin_time = osGetTime();
}
void ui_exit() {
if(ui_stack_mutex != 0) {
svcCloseHandle(ui_stack_mutex);
ui_stack_mutex = 0;
}
}
ui_view* ui_create() {
ui_view* view = (ui_view*) calloc(1, sizeof(ui_view));
if(view == NULL) {
error_panic("无法分配 UI 的视图.");
return NULL;
}
Result res = 0;
if(R_FAILED(res = svcCreateEvent(&view->active, RESET_STICKY))) {
error_panic("无法创建视图活动事件: 0x%08lX", res);
free(view);
return NULL;
}
return view;
}
void ui_destroy(ui_view* view) {
if(view != NULL) {
svcCloseHandle(view->active);
free(view);
}
}
ui_view* ui_top() {
svcWaitSynchronization(ui_stack_mutex, U64_MAX);
ui_view* ui = NULL;
if(ui_stack_top >= 0) {
ui = ui_stack[ui_stack_top];
}
svcReleaseMutex(ui_stack_mutex);
return ui;
}
bool ui_push(ui_view* view) {
if(view == NULL) {
return false;
}
svcWaitSynchronization(ui_stack_mutex, U64_MAX);
bool space = ui_stack_top < MAX_UI_VIEWS - 1;
if(space) {
ui_stack[++ui_stack_top] = view;
svcClearEvent(view->active);
}
svcReleaseMutex(ui_stack_mutex);
return space;
}
void ui_pop() {
svcWaitSynchronization(ui_stack_mutex, U64_MAX);
if(ui_stack_top >= 0) {
svcSignalEvent(ui_stack[ui_stack_top]->active);
ui_stack[ui_stack_top--] = NULL;
}
svcReleaseMutex(ui_stack_mutex);
}
static void ui_draw_top(ui_view* ui) {
screen_select(GFX_TOP);
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_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);
screen_set_base_alpha(ui_fade_alpha);
char verText[64];
snprintf(verText, 64, "版本 %d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
float verWidth;
float verHeight;
screen_get_string_size(&verWidth, &verHeight, verText, 0.5f, 0.5f);
screen_draw_string(verText, topScreenTopBarX + 2, topScreenTopBarY + (topScreenTopBarHeight - verHeight) / 2, 0.5f, 0.5f, COLOR_TEXT, true);
time_t t = time(NULL);
//char* timeText = ctime(&t);
char otimeText[20];
struct tm * timeinfo = localtime(&t);
strftime(otimeText, 20, "%Y/%m/%d %H:%M:%S",timeinfo);
char weekInfo[7];
strftime(weekInfo, 7, "%a",timeinfo);
if (strcmp(weekInfo, "Mon") == 0) snprintf(weekInfo, 7, "周一");
if (strcmp(weekInfo, "Tue") == 0) snprintf(weekInfo, 7, "周二");
if (strcmp(weekInfo, "Wed") == 0) snprintf(weekInfo, 7, "周三");
if (strcmp(weekInfo, "Thu") == 0) snprintf(weekInfo, 7, "周四");
if (strcmp(weekInfo, "Fri") == 0) snprintf(weekInfo, 7, "周五");
if (strcmp(weekInfo, "Sat") == 0) snprintf(weekInfo, 7, "周六");
if (strcmp(weekInfo, "Sun") == 0) snprintf(weekInfo, 7, "周日");
char timeText[28];
snprintf(timeText, 28, "%s %s", otimeText, weekInfo);
timeText[27] = '\0';
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, COLOR_TEXT, true);
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);
if(osGetTime() >= ui_free_space_last_update + 1000) {
char* currBuffer = ui_free_space_buffer;
FS_ArchiveResource resource = {0};
if(R_SUCCEEDED(FSUSER_GetArchiveResource(&resource, SYSTEM_MEDIATYPE_SD)) && currBuffer < ui_free_space_buffer + sizeof(ui_free_space_buffer)) {
if(currBuffer != ui_free_space_buffer) {
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), ", ");
currBuffer += strlen(currBuffer);
}
u64 size = (u64) resource.freeClusters * (u64) resource.clusterSize;
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), "SD 卡: %.1f %s",
ui_get_display_size(size), ui_get_display_size_units(size));
currBuffer += strlen(currBuffer);
}
if(R_SUCCEEDED(FSUSER_GetArchiveResource(&resource, SYSTEM_MEDIATYPE_CTR_NAND)) && currBuffer < ui_free_space_buffer + sizeof(ui_free_space_buffer)) {
if(currBuffer != ui_free_space_buffer) {
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), ", ");
currBuffer += strlen(currBuffer);
}
u64 size = (u64) resource.freeClusters * (u64) resource.clusterSize;
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), "3DS 分区: %.1f %s",
ui_get_display_size(size), ui_get_display_size_units(size));
currBuffer += strlen(currBuffer);
}
if(R_SUCCEEDED(FSUSER_GetArchiveResource(&resource, SYSTEM_MEDIATYPE_TWL_NAND)) && currBuffer < ui_free_space_buffer + sizeof(ui_free_space_buffer)) {
if(currBuffer != ui_free_space_buffer) {
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), ", ");
currBuffer += strlen(currBuffer);
}
u64 size = (u64) resource.freeClusters * (u64) resource.clusterSize;
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), "DSi 分区: %.1f %s",
ui_get_display_size(size), ui_get_display_size_units(size));
currBuffer += strlen(currBuffer);
}
if(R_SUCCEEDED(FSUSER_GetArchiveResource(&resource, SYSTEM_MEDIATYPE_TWL_PHOTO)) && currBuffer < ui_free_space_buffer + sizeof(ui_free_space_buffer)) {
if(currBuffer != ui_free_space_buffer) {
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), ", ");
currBuffer += strlen(currBuffer);
}
u64 size = (u64) resource.freeClusters * (u64) resource.clusterSize;
snprintf(currBuffer, sizeof(ui_free_space_buffer) - (currBuffer - ui_free_space_buffer), "DSi 照片区: %.1f %s",
ui_get_display_size(size), ui_get_display_size_units(size));
currBuffer += strlen(currBuffer);
}
ui_free_space_last_update = osGetTime();
}
float freeSpaceHeight;
screen_get_string_size(NULL, &freeSpaceHeight, ui_free_space_buffer, 0.35f, 0.35f);
screen_draw_string(ui_free_space_buffer, topScreenBottomBarX + 2, topScreenBottomBarY + (topScreenBottomBarHeight - freeSpaceHeight) / 2, 0.35f, 0.35f, COLOR_TEXT, true);
screen_set_base_alpha(0xFF);
}
static void ui_draw_bottom(ui_view* ui) {
screen_select(GFX_BOTTOM);
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_draw_texture(TEXTURE_BOTTOM_SCREEN_BG, (BOTTOM_SCREEN_WIDTH - bottomScreenBgWidth) / 2, (BOTTOM_SCREEN_HEIGHT - bottomScreenBgHeight) / 2, bottomScreenBgWidth, bottomScreenBgHeight);
screen_set_base_alpha(ui_fade_alpha);
if(ui->drawBottom != NULL) {
ui->drawBottom(ui, ui->data, 0, bottomScreenTopBarHeight, BOTTOM_SCREEN_WIDTH, BOTTOM_SCREEN_HEIGHT - bottomScreenBottomBarHeight);
}
screen_set_base_alpha(0xFF);
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);
screen_set_base_alpha(ui_fade_alpha);
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, COLOR_TEXT, true);
}
if(ui->info != NULL) {
float baseInfoWidth;
screen_get_string_size(&baseInfoWidth, NULL, ui->info, 0.5f, 0.5f);
float scale = BOTTOM_SCREEN_WIDTH / (baseInfoWidth + 10);
if(scale > 1) {
scale = 1;
}
float infoWidth;
float infoHeight;
screen_get_string_size(&infoWidth, &infoHeight, ui->info, 0.5f * scale, 0.5f * scale);
screen_draw_string(ui->info, (BOTTOM_SCREEN_WIDTH - infoWidth) / 2, BOTTOM_SCREEN_HEIGHT - (bottomScreenBottomBarHeight + infoHeight) / 2, 0.5f * scale, 0.5f * scale, COLOR_TEXT, true);
}
screen_set_base_alpha(0xFF);
}
bool ui_update() {
ui_view* ui = NULL;
hidScanInput();
ui = ui_top();
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);
}
u64 time = osGetTime();
if(!envIsHomebrew() && time - ui_fade_begin_time < 500) {
ui_fade_alpha = (u8) (((time - ui_fade_begin_time) / 500.0f) * 0xFF);
} else {
ui_fade_alpha = 0xFF;
}
ui = ui_top();
if(ui != NULL) {
screen_begin_frame();
ui_draw_top(ui);
ui_draw_bottom(ui);
screen_end_frame();
}
return ui != NULL;
}
const char* ui_get_display_eta(u32 seconds) {
static char disp[12];
u8 hours = seconds / 3600;
seconds -= hours * 3600;
u8 minutes = seconds / 60;
seconds -= minutes* 60;
snprintf(disp, 12, "%02u:%02u:%02u", hours, minutes, (u8) seconds);
return disp;
}
double ui_get_display_size(u64 size) {
double s = size;
if(s >= 1024) {
s /= 1024;
}
if(s >= 1024) {
s /= 1024;
}
if(s >= 1024) {
s /= 1024;
}
return s;
}
const char* ui_get_display_size_units(u64 size) {
if(size >= 1024 * 1024 * 1024) {
return "GiB";
}
if(size >= 1024 * 1024) {
return "MiB";
}
if(size >= 1024) {
return "KiB";
}
return "B";
}

View File

@ -1,33 +0,0 @@
#pragma once
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);
Handle active;
} ui_view;
void ui_init();
void ui_exit();
ui_view* ui_create();
void ui_destroy(ui_view* view);
ui_view* ui_top();
bool ui_push(ui_view* view);
void ui_pop();
bool ui_update();
const char* ui_get_display_eta(u32 seconds);
double ui_get_display_size(u64 size);
const char* ui_get_display_size_units(u64 size);
#include "error.h"
#include "info.h"
#include "kbd.h"
#include "list.h"
#include "prompt.h"

BIN
source/fbi/.DS_Store vendored

Binary file not shown.

View File

@ -1,56 +0,0 @@
#pragma once
typedef struct ticket_info_s ticket_info;
typedef struct linked_list_s linked_list;
typedef struct list_item_s list_item;
typedef struct ui_view_s ui_view;
#define INSTALL_URLS_MAX 128
void action_browse_boss_ext_save_data(linked_list* items, list_item* selected);
void action_browse_user_ext_save_data(linked_list* items, list_item* selected);
void action_delete_ext_save_data(linked_list* items, list_item* selected);
void action_browse_system_save_data(linked_list* items, list_item* selected);
void action_delete_system_save_data(linked_list* items, list_item* selected);
void action_install_cia(linked_list* items, list_item* selected);
void action_install_cia_delete(linked_list* items, list_item* selected);
void action_install_cias(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData);
void action_install_cias_delete(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData);
void action_install_ticket(linked_list* items, list_item* selected);
void action_install_ticket_delete(linked_list* items, list_item* selected);
void action_install_tickets(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData);
void action_install_tickets_delete(linked_list* items, list_item* selected, bool (*filter)(void* data, const char* name, u32 attributes), void* filterData);
void action_delete_file(linked_list* items, list_item* selected);
void action_delete_dir(linked_list* items, list_item* selected);
void action_delete_dir_contents(linked_list* items, list_item* selected);
void action_delete_dir_cias(linked_list* items, list_item* selected);
void action_delete_dir_tickets(linked_list* items, list_item* selected);
void action_new_folder(linked_list* items, list_item* selected);
void action_paste_contents(linked_list* items, list_item* selected);
void action_rename(linked_list* items, list_item* selected);
void action_delete_pending_title(linked_list* items, list_item* selected);
void action_delete_all_pending_titles(linked_list* items, list_item* selected);
void action_delete_ticket(linked_list* items, list_item* selected);
void action_delete_tickets_unused(linked_list* items, list_item* selected);
void action_delete_title(linked_list* items, list_item* selected);
void action_delete_title_ticket(linked_list* items, list_item* selected);
void action_launch_title(linked_list* items, list_item* selected);
void action_extract_smdh(linked_list* items, list_item* selected);
void action_import_seed(linked_list* items, list_item* selected);
void action_erase_twl_save(linked_list* items, list_item* selected);
void action_export_twl_save(linked_list* items, list_item* selected);
void action_import_twl_save(linked_list* items, list_item* selected);
void action_browse_title_save_data(linked_list* items, list_item* selected);
void action_import_secure_value(linked_list* items, list_item* selected);
void action_export_secure_value(linked_list* items, list_item* selected);
void action_delete_secure_value(linked_list* items, list_item* selected);
void action_install_url(const char* confirmMessage, const char* urls, const char* paths, void* userData,
void (*finishedURL)(void* data, u32 index),
void (*finishedAll)(void* data),
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2, u32 index));

View File

@ -1,13 +0,0 @@
#include <3ds.h>
#include "action.h"
#include "../section.h"
#include "../task/uitask.h"
#include "../../core/core.h"
void action_browse_boss_ext_save_data(linked_list* items, list_item* selected) {
ext_save_data_info* info = (ext_save_data_info*) selected->data;
u32 path[3] = {info->mediaType, (u32) (info->extSaveDataId & 0xFFFFFFFF), (u32) ((info->extSaveDataId >> 32) & 0xFFFFFFFF)};
files_open(ARCHIVE_BOSS_EXTDATA, fs_make_path_binary(path, sizeof(path)));
}

View File

@ -1,13 +0,0 @@
#include <3ds.h>
#include "action.h"
#include "../section.h"
#include "../task/uitask.h"
#include "../../core/core.h"
void action_browse_system_save_data(linked_list* items, list_item* selected) {
system_save_data_info* info = (system_save_data_info*) selected->data;
u32 path[2] = {MEDIATYPE_NAND, info->systemSaveDataId};
files_open(ARCHIVE_SYSTEM_SAVEDATA, fs_make_path_binary(path, sizeof(path)));
}

View File

@ -1,13 +0,0 @@
#include <3ds.h>
#include "action.h"
#include "../section.h"
#include "../task/uitask.h"
#include "../../core/core.h"
void action_browse_title_save_data(linked_list* items, list_item* selected) {
title_info* info = (title_info*) selected->data;
u32 path[3] = {info->mediaType, (u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF)};
files_open(ARCHIVE_USER_SAVEDATA, fs_make_path_binary(path, sizeof(path)));
}

View File

@ -1,13 +0,0 @@
#include <3ds.h>
#include "action.h"
#include "../section.h"
#include "../task/uitask.h"
#include "../../core/core.h"
void action_browse_user_ext_save_data(linked_list* items, list_item* selected) {
ext_save_data_info* info = (ext_save_data_info*) selected->data;
u32 path[3] = {info->mediaType, (u32) (info->extSaveDataId & 0xFFFFFFFF), (u32) ((info->extSaveDataId >> 32) & 0xFFFFFFFF)};
files_open(info->shared ? ARCHIVE_SHARED_EXTDATA : ARCHIVE_EXTDATA, fs_make_path_binary(path, sizeof(path)));
}

View File

@ -1,268 +0,0 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "action.h"
#include "../resources.h"
#include "../task/uitask.h"
#include "../../core/core.h"
typedef struct {
linked_list* items;
list_item* targetItem;
file_info* target;
linked_list contents;
data_op_data deleteInfo;
} delete_data;
static void action_delete_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
delete_data* deleteData = (delete_data*) data;
u32 curr = deleteData->deleteInfo.processed;
if(curr < deleteData->deleteInfo.total) {
task_draw_file_info(view, ((list_item*) linked_list_get(&deleteData->contents, linked_list_size(&deleteData->contents) - curr - 1))->data, x1, y1, x2, y2);
} else {
task_draw_file_info(view, deleteData->target, x1, y1, x2, y2);
}
}
static Result action_delete_delete(void* data, u32 index) {
delete_data* deleteData = (delete_data*) data;
Result res = 0;
file_info* info = (file_info*) ((list_item*) linked_list_get(&deleteData->contents, linked_list_size(&deleteData->contents) - index - 1))->data;
FS_Path* fsPath = fs_make_path_utf8(info->path);
if(fsPath != NULL) {
if(fs_is_dir(deleteData->target->archive, info->path)) {
res = FSUSER_DeleteDirectory(deleteData->target->archive, *fsPath);
} else {
res = FSUSER_DeleteFile(deleteData->target->archive, *fsPath);
}
fs_free_path_utf8(fsPath);
} else {
res = R_APP_OUT_OF_MEMORY;
}
if(R_SUCCEEDED(res)) {
linked_list_iter iter;
linked_list_iterate(deleteData->items, &iter);
while(linked_list_iter_has_next(&iter)) {
list_item* item = (list_item*) linked_list_iter_next(&iter);
file_info* currInfo = (file_info*) item->data;
if(strncmp(currInfo->path, info->path, FILE_PATH_MAX) == 0) {
linked_list_iter_remove(&iter);
task_free_file(item);
}
}
}
return res;
}
static Result action_delete_suspend(void* data, u32 index) {
return 0;
}
static Result action_delete_restore(void* data, u32 index) {
return 0;
}
static bool action_delete_error(void* data, u32 index, Result res, ui_view** errorView) {
*errorView = error_display_res(data, action_delete_draw_top, res, "无法删除项目.");
return true;
}
static void action_delete_free_data(delete_data* data) {
task_clear_files(&data->contents);
linked_list_destroy(&data->contents);
if(data->targetItem != NULL) {
task_free_file(data->targetItem);
data->targetItem = NULL;
data->target = NULL;
}
free(data);
}
static void action_delete_update(ui_view* view, void* data, float* progress, char* text) {
delete_data* deleteData = (delete_data*) data;
if(deleteData->deleteInfo.finished) {
FSUSER_ControlArchive(deleteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
ui_pop();
info_destroy(view);
if(R_SUCCEEDED(deleteData->deleteInfo.result)) {
prompt_display_notify("成功", "已删除.", COLOR_TEXT, NULL, NULL, NULL);
}
action_delete_free_data(deleteData);
return;
}
if((hidKeysDown() & KEY_B) && !deleteData->deleteInfo.finished) {
svcSignalEvent(deleteData->deleteInfo.cancelEvent);
}
*progress = deleteData->deleteInfo.total > 0 ? (float) deleteData->deleteInfo.processed / (float) deleteData->deleteInfo.total : 0;
snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu", deleteData->deleteInfo.processed, deleteData->deleteInfo.total);
}
static void action_delete_onresponse(ui_view* view, void* data, u32 response) {
delete_data* deleteData = (delete_data*) data;
if(response == PROMPT_YES) {
Result res = task_data_op(&deleteData->deleteInfo);
if(R_SUCCEEDED(res)) {
info_display("正在删除", "按 B 取消.", true, data, action_delete_update, action_delete_draw_top);
} else {
error_display_res(NULL, NULL, res, "无法启动删除操作.");
action_delete_free_data(deleteData);
}
} else {
action_delete_free_data(deleteData);
}
}
typedef struct {
delete_data* deleteData;
const char* message;
populate_files_data popData;
} delete_loading_data;
static void action_delete_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
action_delete_draw_top(view, ((delete_loading_data*) data)->deleteData, x1, y1, x2, y2);
}
static void action_delete_loading_update(ui_view* view, void* data, float* progress, char* text) {
delete_loading_data* loadingData = (delete_loading_data*) data;
if(loadingData->popData.finished) {
ui_pop();
info_destroy(view);
if(R_SUCCEEDED(loadingData->popData.result)) {
loadingData->deleteData->deleteInfo.total = linked_list_size(&loadingData->deleteData->contents);
loadingData->deleteData->deleteInfo.processed = loadingData->deleteData->deleteInfo.total;
prompt_display_yes_no("确认", loadingData->message, COLOR_TEXT, loadingData->deleteData, action_delete_draw_top, action_delete_onresponse);
} else {
error_display_res(NULL, NULL, loadingData->popData.result, "无法填充项目列表.");
action_delete_free_data(loadingData->deleteData);
}
free(loadingData);
return;
}
if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) {
svcSignalEvent(loadingData->popData.cancelEvent);
}
snprintf(text, PROGRESS_TEXT_MAX, "正在获取项目列表...");
}
static void action_delete_internal(linked_list* items, list_item* selected, const char* message, bool recursive, bool includeBase, bool ciasOnly, bool ticketsOnly) {
delete_data* data = (delete_data*) calloc(1, sizeof(delete_data));
if(data == NULL) {
error_display(NULL, NULL, "无法分配删除的数据.");
return;
}
data->items = items;
file_info* targetInfo = (file_info*) selected->data;
Result targetCreateRes = task_create_file_item(&data->targetItem, targetInfo->archive, targetInfo->path, targetInfo->attributes, false);
if(R_FAILED(targetCreateRes)) {
error_display_res(NULL, NULL, targetCreateRes, "无法创建目标文件.");
action_delete_free_data(data);
return;
}
data->target = (file_info*) data->targetItem->data;
data->deleteInfo.data = data;
data->deleteInfo.op = DATAOP_DELETE;
data->deleteInfo.delete = action_delete_delete;
data->deleteInfo.suspend = action_delete_suspend;
data->deleteInfo.restore = action_delete_restore;
data->deleteInfo.error = action_delete_error;
data->deleteInfo.finished = false;
linked_list_init(&data->contents);
delete_loading_data* loadingData = (delete_loading_data*) calloc(1, sizeof(delete_loading_data));
if(loadingData == NULL) {
error_display(NULL, NULL, "无法分配加载的数据.");
action_delete_free_data(data);
return;
}
loadingData->deleteData = data;
loadingData->message = message;
loadingData->popData.items = &data->contents;
loadingData->popData.archive = data->target->archive;
string_copy(loadingData->popData.path, data->target->path, FILE_PATH_MAX);
loadingData->popData.recursive = recursive;
loadingData->popData.includeBase = includeBase;
loadingData->popData.meta = false;
loadingData->popData.filter = ciasOnly ? fs_filter_cias : ticketsOnly ? fs_filter_tickets : NULL;
loadingData->popData.filterData = NULL;
Result listRes = task_populate_files(&loadingData->popData);
if(R_FAILED(listRes)) {
error_display_res(NULL, NULL, listRes, "无法启动项目列表填充.");
free(loadingData);
action_delete_free_data(data);
return;
}
info_display("正在加载", "按 B 取消.", false, loadingData, action_delete_loading_update, action_delete_loading_draw_top);
}
void action_delete_file(linked_list* items, list_item* selected) {
action_delete_internal(items, selected, "删除所选文件?", false, true, false, false);
}
void action_delete_dir(linked_list* items, list_item* selected) {
action_delete_internal(items, selected, "删除当前文件夹?", true, true, false, false);
}
void action_delete_dir_contents(linked_list* items, list_item* selected) {
action_delete_internal(items, selected, "删除当前文件夹的所有项目?", true, false, false, false);
}
void action_delete_dir_cias(linked_list* items, list_item* selected) {
action_delete_internal(items, selected, "删除当前文件夹的所有安装包?", false, false, true, false);
}
void action_delete_dir_tickets(linked_list* items, list_item* selected) {
action_delete_internal(items, selected, "删除当前文件夹的所有应用引导表?", false, false, false, true);
}

View File

@ -1,61 +0,0 @@
#include <malloc.h>
#include <3ds.h>
#include "action.h"
#include "../resources.h"
#include "../task/uitask.h"
#include "../../core/core.h"
typedef struct {
linked_list* items;
list_item* selected;
} delete_ext_save_data_data;
static void action_delete_ext_save_data_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
task_draw_ext_save_data_info(view, ((delete_ext_save_data_data*) data)->selected->data, x1, y1, x2, y2);
}
static void action_delete_ext_save_data_update(ui_view* view, void* data, float* progress, char* text) {
delete_ext_save_data_data* deleteData = (delete_ext_save_data_data*) data;
ext_save_data_info* info = (ext_save_data_info*) deleteData->selected->data;
FS_ExtSaveDataInfo extInfo = {.mediaType = info->mediaType, .saveId = info->extSaveDataId};
Result res = FSUSER_DeleteExtSaveData(extInfo);
ui_pop();
info_destroy(view);
if(R_FAILED(res)) {
error_display_res(info, task_draw_ext_save_data_info, res, "无法删除追加数据.");
} else {
linked_list_remove(deleteData->items, deleteData->selected);
task_free_ext_save_data(deleteData->selected);
prompt_display_notify("成功", "已删除.", COLOR_TEXT, NULL, NULL, NULL);
}
free(data);
}
static void action_delete_ext_save_data_onresponse(ui_view* view, void* data, u32 response) {
if(response == PROMPT_YES) {
info_display("正在删除", "", false, data, action_delete_ext_save_data_update, action_delete_ext_save_data_draw_top);
} else {
free(data);
}
}
void action_delete_ext_save_data(linked_list* items, list_item* selected) {
delete_ext_save_data_data* data = (delete_ext_save_data_data*) calloc(1, sizeof(delete_ext_save_data_data));
if(data == NULL) {
error_display(NULL, NULL, "无法分配删除追加数据的数据.");
return;
}
data->items = items;
data->selected = selected;
prompt_display_yes_no("确认", "删除所选追加数据?", COLOR_TEXT, data, action_delete_ext_save_data_draw_top, action_delete_ext_save_data_onresponse);
}

View File

@ -1,229 +0,0 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "action.h"
#include "../resources.h"
#include "../task/uitask.h"
#include "../../core/core.h"
typedef struct {
linked_list* items;
list_item* selected;
linked_list contents;
bool all;
data_op_data deleteInfo;
} delete_pending_titles_data;
static void action_delete_pending_titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
u32 index = deleteData->deleteInfo.processed;
if(index < deleteData->deleteInfo.total) {
task_draw_pending_title_info(view, (pending_title_info*) ((list_item*) linked_list_get(&deleteData->contents, index))->data, x1, y1, x2, y2);
}
}
static Result action_delete_pending_titles_delete(void* data, u32 index) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
list_item* item = (list_item*) linked_list_get(&deleteData->contents, index);
pending_title_info* info = (pending_title_info*) item->data;
Result res = 0;
if(R_SUCCEEDED(res = AM_DeletePendingTitle(info->mediaType, info->titleId))) {
linked_list_iter iter;
linked_list_iterate(deleteData->items, &iter);
while(linked_list_iter_has_next(&iter)) {
list_item* currItem = (list_item*) linked_list_iter_next(&iter);
pending_title_info* currInfo = (pending_title_info*) currItem->data;
if(currInfo->titleId == info->titleId && currInfo->mediaType == info->mediaType) {
linked_list_iter_remove(&iter);
task_free_pending_title(currItem);
}
}
}
return res;
}
static Result action_delete_pending_titles_suspend(void* data, u32 index) {
return 0;
}
static Result action_delete_pending_titles_restore(void* data, u32 index) {
return 0;
}
static bool action_delete_pending_titles_error(void* data, u32 index, Result res, ui_view** errorView) {
*errorView = error_display_res(data, action_delete_pending_titles_draw_top, res, "无法删除未完成的应用.");
return true;
}
static void action_delete_pending_titles_free_data(delete_pending_titles_data* data) {
if(data->all) {
task_clear_pending_titles(&data->contents);
}
linked_list_destroy(&data->contents);
free(data);
}
static void action_delete_pending_titles_update(ui_view* view, void* data, float* progress, char* text) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
if(deleteData->deleteInfo.finished) {
ui_pop();
info_destroy(view);
if(R_SUCCEEDED(deleteData->deleteInfo.result)) {
prompt_display_notify("成功", "已删除.", COLOR_TEXT, NULL, NULL, NULL);
}
action_delete_pending_titles_free_data(deleteData);
return;
}
if((hidKeysDown() & KEY_B) && !deleteData->deleteInfo.finished) {
svcSignalEvent(deleteData->deleteInfo.cancelEvent);
}
*progress = deleteData->deleteInfo.total > 0 ? (float) deleteData->deleteInfo.processed / (float) deleteData->deleteInfo.total : 0;
snprintf(text, PROGRESS_TEXT_MAX, "%lu / %lu", deleteData->deleteInfo.processed, deleteData->deleteInfo.total);
}
static void action_delete_pending_titles_onresponse(ui_view* view, void* data, u32 response) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
if(response == PROMPT_YES) {
Result res = task_data_op(&deleteData->deleteInfo);
if(R_SUCCEEDED(res)) {
info_display("正在删除", "按 B 取消.", true, data, action_delete_pending_titles_update, action_delete_pending_titles_draw_top);
} else {
error_display_res(NULL, NULL, res, "无法启动删除操作.");
action_delete_pending_titles_free_data(deleteData);
}
} else {
action_delete_pending_titles_free_data(deleteData);
}
}
typedef struct {
delete_pending_titles_data* deleteData;
const char* message;
populate_pending_titles_data popData;
} delete_pending_titles_loading_data;
static void action_delete_pending_titles_loading_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
action_delete_pending_titles_draw_top(view, ((delete_pending_titles_loading_data*) data)->deleteData, x1, y1, x2, y2);
}
static void action_delete_pending_titles_loading_update(ui_view* view, void* data, float* progress, char* text) {
delete_pending_titles_loading_data* loadingData = (delete_pending_titles_loading_data*) data;
if(loadingData->popData.finished) {
ui_pop();
info_destroy(view);
if(R_SUCCEEDED(loadingData->popData.result)) {
loadingData->deleteData->deleteInfo.total = linked_list_size(&loadingData->deleteData->contents);
loadingData->deleteData->deleteInfo.processed = loadingData->deleteData->deleteInfo.total;
prompt_display_yes_no("确认", loadingData->message, COLOR_TEXT, loadingData->deleteData, action_delete_pending_titles_draw_top, action_delete_pending_titles_onresponse);
} else {
error_display_res(NULL, NULL, loadingData->popData.result, "无法填充未完成的应用列表.");
action_delete_pending_titles_free_data(loadingData->deleteData);
}
free(loadingData);
return;
}
if((hidKeysDown() & KEY_B) && !loadingData->popData.finished) {
svcSignalEvent(loadingData->popData.cancelEvent);
}
snprintf(text, PROGRESS_TEXT_MAX, "正在获取未完成的应用列表...");
}
void action_delete_pending_titles(linked_list* items, list_item* selected, const char* message, bool all) {
delete_pending_titles_data* data = (delete_pending_titles_data*) calloc(1, sizeof(delete_pending_titles_data));
if(data == NULL) {
error_display(NULL, NULL, "无法分配删除未完成的应用的数据.");
return;
}
data->items = items;
data->selected = selected;
data->all = all;
data->deleteInfo.data = data;
data->deleteInfo.op = DATAOP_DELETE;
data->deleteInfo.delete = action_delete_pending_titles_delete;
data->deleteInfo.suspend = action_delete_pending_titles_suspend;
data->deleteInfo.restore = action_delete_pending_titles_restore;
data->deleteInfo.error = action_delete_pending_titles_error;
data->deleteInfo.finished = true;
linked_list_init(&data->contents);
if(all) {
delete_pending_titles_loading_data* loadingData = (delete_pending_titles_loading_data*) calloc(1, sizeof(delete_pending_titles_loading_data));
if(loadingData == NULL) {
error_display(NULL, NULL, "无法分配加载的数据.");
action_delete_pending_titles_free_data(data);
return;
}
loadingData->deleteData = data;
loadingData->message = message;
loadingData->popData.items = &data->contents;
Result listRes = task_populate_pending_titles(&loadingData->popData);
if(R_FAILED(listRes)) {
error_display_res(NULL, NULL, listRes, "无法启动未完成的应用列表填充.");
free(loadingData);
action_delete_pending_titles_free_data(data);
return;
}
info_display("正在加载", "按 B 取消.", false, loadingData, action_delete_pending_titles_loading_update, action_delete_pending_titles_loading_draw_top);
} else {
linked_list_add(&data->contents, selected);
data->deleteInfo.total = 1;
data->deleteInfo.processed = data->deleteInfo.total;
prompt_display_yes_no("确认", message, COLOR_TEXT, data, action_delete_pending_titles_draw_top, action_delete_pending_titles_onresponse);
}
}
void action_delete_pending_title(linked_list* items, list_item* selected) {
action_delete_pending_titles(items, selected, "删除所选未完成的应用?", false);
}
void action_delete_all_pending_titles(linked_list* items, list_item* selected) {
action_delete_pending_titles(items, selected, "删除所有未完成的应用?", true);
}

View File

@ -1,35 +0,0 @@
#include <stdio.h>
#include <3ds.h>
#include "action.h"
#include "../resources.h"
#include "../task/uitask.h"
#include "../../core/core.h"
static void action_delete_secure_value_update(ui_view* view, void* data, float* progress, char* text) {
title_info* info = (title_info*) data;
u64 param = ((u64) SECUREVALUE_SLOT_SD << 32) | (info->titleId & 0xFFFFFFF);
u8 out = 0;
Result res = FSUSER_ControlSecureSave(SECURESAVE_ACTION_DELETE, &param, sizeof(param), &out, sizeof(out));
ui_pop();
info_destroy(view);
if(R_FAILED(res)) {
error_display_res(info, task_draw_title_info, res, "无法删除安全值.");
} else {
prompt_display_notify("成功", "已删除.", COLOR_TEXT, info, task_draw_title_info, NULL);
}
}
static void action_delete_secure_value_onresponse(ui_view* view, void* data, u32 response) {
if(response == PROMPT_YES) {
info_display("正在删除", "", false, data, action_delete_secure_value_update, task_draw_title_info);
}
}
void action_delete_secure_value(linked_list* items, list_item* selected) {
prompt_display_yes_no("确认", "删除所选应用的安全值?", COLOR_TEXT, selected->data, task_draw_title_info, action_delete_secure_value_onresponse);
}

Some files were not shown because too many files have changed in this diff Show More